s390/pkey: support CCA and EP11 secure ECC private keys

This patch extends the pkey kernel module to support CCA
and EP11 secure ECC (private) keys as source for deriving
ECC protected (private) keys.

There is yet another new ioctl to support this: PKEY_KBLOB2PROTK3
can handle all the old keys plus CCA and EP11 secure ECC keys.
For details see ioctl description in pkey.h.

The CPACF unit currently only supports a subset of 5
different ECC curves (P-256, P-384, P-521, ED25519, ED448) and
so only keys of this curve type can be transformed into
protected keys. However, the pkey and the cca/ep11 low level
functions do not check this but simple pass-through the key
blob to the firmware onto the crypto cards. So most likely
the failure will be a response carrying an error code
resulting in user space errno value EIO instead of EINVAL.

Deriving a protected key from an EP11 ECC secure key
requires a CEX7 in EP11 mode. Deriving a protected key from
an CCA ECC secure key requires a CEX7 in CCA mode.

Together with this new ioctl the ioctls for querying lists
of apqns (PKEY_APQNS4K and PKEY_APQNS4KT) have been extended
to support EP11 and CCA ECC secure key type and key blobs.

Together with this ioctl there comes a new struct ep11kblob_header
which is to be prepended onto the EP11 key blob. See details
in pkey.h for the fields in there. The older EP11 AES key blob
with some info stored in the (unused) session field is also
supported with this new ioctl.

Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
Reviewed-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
This commit is contained in:
Harald Freudenberger
2020-09-21 10:45:55 +02:00
committed by Vasily Gorbik
parent 32ca04bba6
commit fa6999e326
6 changed files with 821 additions and 98 deletions

View File

@@ -172,6 +172,49 @@ int cca_check_secaescipherkey(debug_info_t *dbg, int dbflvl,
}
EXPORT_SYMBOL(cca_check_secaescipherkey);
/*
* Simple check if the token is a valid CCA secure ECC private
* key token. Returns 0 on success or errno value on failure.
*/
int cca_check_sececckeytoken(debug_info_t *dbg, int dbflvl,
const u8 *token, size_t keysize,
int checkcpacfexport)
{
struct eccprivkeytoken *t = (struct eccprivkeytoken *) token;
#define DBF(...) debug_sprintf_event(dbg, dbflvl, ##__VA_ARGS__)
if (t->type != TOKTYPE_CCA_INTERNAL_PKA) {
if (dbg)
DBF("%s token check failed, type 0x%02x != 0x%02x\n",
__func__, (int) t->type, TOKTYPE_CCA_INTERNAL_PKA);
return -EINVAL;
}
if (t->len > keysize) {
if (dbg)
DBF("%s token check failed, len %d > keysize %zu\n",
__func__, (int) t->len, keysize);
return -EINVAL;
}
if (t->secid != 0x20) {
if (dbg)
DBF("%s token check failed, secid 0x%02x != 0x20\n",
__func__, (int) t->secid);
return -EINVAL;
}
if (checkcpacfexport && !(t->kutc & 0x01)) {
if (dbg)
DBF("%s token check failed, XPRTCPAC bit is 0\n",
__func__);
return -EINVAL;
}
#undef DBF
return 0;
}
EXPORT_SYMBOL(cca_check_sececckeytoken);
/*
* Allocate consecutive memory for request CPRB, request param
* block, reply CPRB and reply param block and fill in values
@@ -1297,6 +1340,156 @@ out:
}
EXPORT_SYMBOL(cca_cipher2protkey);
/*
* Derive protected key from CCA ECC secure private key.
*/
int cca_ecc2protkey(u16 cardnr, u16 domain, const u8 *key,
u8 *protkey, u32 *protkeylen, u32 *protkeytype)
{
int rc;
u8 *mem, *ptr;
struct CPRBX *preqcblk, *prepcblk;
struct ica_xcRB xcrb;
struct aureqparm {
u8 subfunc_code[2];
u16 rule_array_len;
u8 rule_array[8];
struct {
u16 len;
u16 tk_blob_len;
u16 tk_blob_tag;
u8 tk_blob[66];
} vud;
struct {
u16 len;
u16 cca_key_token_len;
u16 cca_key_token_flags;
u8 cca_key_token[0];
} kb;
} __packed * preqparm;
struct aurepparm {
u8 subfunc_code[2];
u16 rule_array_len;
struct {
u16 len;
u16 sublen;
u16 tag;
struct cpacfkeyblock {
u8 version; /* version of this struct */
u8 flags[2];
u8 algo;
u8 form;
u8 pad1[3];
u16 keylen;
u8 key[0]; /* the key (keylen bytes) */
u16 keyattrlen;
u8 keyattr[32];
u8 pad2[1];
u8 vptype;
u8 vp[32]; /* verification pattern */
} ckb;
} vud;
struct {
u16 len;
} kb;
} __packed * prepparm;
int keylen = ((struct eccprivkeytoken *)key)->len;
/* get already prepared memory for 2 cprbs with param block each */
rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, &preqcblk, &prepcblk);
if (rc)
return rc;
/* fill request cprb struct */
preqcblk->domain = domain;
/* fill request cprb param block with AU request */
preqparm = (struct aureqparm __force *) preqcblk->req_parmb;
memcpy(preqparm->subfunc_code, "AU", 2);
preqparm->rule_array_len =
sizeof(preqparm->rule_array_len)
+ sizeof(preqparm->rule_array);
memcpy(preqparm->rule_array, "EXPT-SK ", 8);
/* vud, tk blob */
preqparm->vud.len = sizeof(preqparm->vud);
preqparm->vud.tk_blob_len = sizeof(preqparm->vud.tk_blob)
+ 2 * sizeof(uint16_t);
preqparm->vud.tk_blob_tag = 0x00C2;
/* kb, cca token */
preqparm->kb.len = keylen + 3 * sizeof(uint16_t);
preqparm->kb.cca_key_token_len = keylen + 2 * sizeof(uint16_t);
memcpy(preqparm->kb.cca_key_token, key, keylen);
/* now fill length of param block into cprb */
preqcblk->req_parml = sizeof(struct aureqparm) + keylen;
/* fill xcrb struct */
prep_xcrb(&xcrb, cardnr, preqcblk, prepcblk);
/* forward xcrb with request CPRB and reply CPRB to zcrypt dd */
rc = zcrypt_send_cprb(&xcrb);
if (rc) {
DEBUG_ERR(
"%s zcrypt_send_cprb (cardnr=%d domain=%d) failed, rc=%d\n",
__func__, (int) cardnr, (int) domain, rc);
goto out;
}
/* check response returncode and reasoncode */
if (prepcblk->ccp_rtcode != 0) {
DEBUG_ERR(
"%s unwrap secure key failure, card response %d/%d\n",
__func__,
(int) prepcblk->ccp_rtcode,
(int) prepcblk->ccp_rscode);
rc = -EIO;
goto out;
}
if (prepcblk->ccp_rscode != 0) {
DEBUG_WARN(
"%s unwrap secure key warning, card response %d/%d\n",
__func__,
(int) prepcblk->ccp_rtcode,
(int) prepcblk->ccp_rscode);
}
/* process response cprb param block */
ptr = ((u8 *) prepcblk) + sizeof(struct CPRBX);
prepcblk->rpl_parmb = (u8 __user *) ptr;
prepparm = (struct aurepparm *) ptr;
/* check the returned keyblock */
if (prepparm->vud.ckb.version != 0x02) {
DEBUG_ERR("%s reply param keyblock version mismatch 0x%02x != 0x02\n",
__func__, (int) prepparm->vud.ckb.version);
rc = -EIO;
goto out;
}
if (prepparm->vud.ckb.algo != 0x81) {
DEBUG_ERR(
"%s reply param keyblock algo mismatch 0x%02x != 0x81\n",
__func__, (int) prepparm->vud.ckb.algo);
rc = -EIO;
goto out;
}
/* copy the translated protected key */
if (prepparm->vud.ckb.keylen > *protkeylen) {
DEBUG_ERR("%s prot keylen mismatch %d > buffersize %u\n",
__func__, prepparm->vud.ckb.keylen, *protkeylen);
rc = -EIO;
goto out;
}
memcpy(protkey, prepparm->vud.ckb.key, prepparm->vud.ckb.keylen);
*protkeylen = prepparm->vud.ckb.keylen;
if (protkeytype)
*protkeytype = PKEY_KEYTYPE_ECC;
out:
free_cprbmem(mem, PARMBSIZE, 0);
return rc;
}
EXPORT_SYMBOL(cca_ecc2protkey);
/*
* query cryptographic facility from CCA adapter
*/