Skip to content

Commit

Permalink
zkey: Support EP11 AES keys with prepended header to retain EP11 session
Browse files Browse the repository at this point in the history
The pkey kernel module supports two key blob formats for EP11 AES keys.
The first one (PKEY_TYPE_EP11) contains a 16 bytes header that overlays
the first 32 bytes of the key blob which usually contain the ID of the
EP11 session to which the key is bound. For zkey/dm-crypt that session
ID used to be all zeros. The second blob format (PKEY_TYPE_EP11_AES)
prepends the 16 bytes header to the blob, an thus does not overlay the
blob. This format can be used for key blobs that are session-bound, i.e.
have a non-zero session ID in the first 32 bytes.

Change zkey to generate EP11 keys using the new format (i.e. pkey type
PKEY_TYPE_EP11_AES), but existing key blobs using the old format can
still be used.

Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Joerg Schmidbauer <jschmidb@de.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
  • Loading branch information
ifranzki authored and steffen-eiden committed Aug 21, 2023
1 parent f46f6d3 commit 1b044b8
Show file tree
Hide file tree
Showing 8 changed files with 295 additions and 51 deletions.
48 changes: 33 additions & 15 deletions zkey/ep11.c
Expand Up @@ -365,30 +365,39 @@ int select_ep11_apqn_by_mkvp(struct ep11_lib *ep11, u8 *mkvp,
* @param[in] target the target handle to use for the re-encipher operation
* @param[in] card the card that corresponds to the target handle
* @param[in] domain the domain that corresponds to the target handle
* @param[in/out] ep11key the EP11 key token to reencipher. The re-enciphered
* secure key will be returned in this buffer.
* @param[in/out] ep11key_blob the EP11 key token to reencipher. The
* re-enciphered secure key will be returned in this
* buffer.
* @param[in] ep11key_size the size of the secure key
* @param[in] verbose if true, verbose messages are printed
*
* @returns 0 on success, a negative errno in case of errors
*/
static int ep11_adm_reencrypt(struct ep11_lib *ep11, target_t target,
unsigned int card, unsigned int domain,
struct ep11keytoken *ep11key,
u8 *ep11key_blob,
unsigned int ep11key_size, bool verbose)
{
struct ep11kblob_header *hdr = (struct ep11kblob_header *)ep11key_blob;
struct ep11keytoken *ep11key;
CK_BYTE resp[MAX_BLOBSIZE];
CK_BYTE req[MAX_BLOBSIZE];
char ep11_token_header[sizeof(ep11key->head)];
char ep11_token_header[sizeof(ep11key->head)] = { 0 };
struct XCPadmresp lrb;
struct XCPadmresp rb;
bool with_header;
size_t resp_len;
size_t blob_len;
long req_len;
CK_RV rv;
int rc;

blob_len = ep11key->head.length;
with_header = is_ep11_aes_key_with_header(ep11key_blob, ep11key_size);
ep11key = (struct ep11keytoken *)(with_header ?
ep11key_blob + sizeof(struct ep11kblob_header) :
ep11key_blob);
blob_len = with_header ? hdr->len - sizeof(struct ep11kblob_header) :
ep11key->head.len;
if (blob_len > ep11key_size) {
pr_verbose(verbose, "Blob length larger than secure key size");
return -EINVAL;
Expand All @@ -397,9 +406,14 @@ static int ep11_adm_reencrypt(struct ep11_lib *ep11, target_t target,
rb.domain = domain;
lrb.domain = domain;

/* The token header is an overlay over the (all zero) session field */
memcpy(ep11_token_header, ep11key, sizeof(ep11_token_header));
memset(ep11key->session, 0, sizeof(ep11key->session));
if (!with_header) {
/*
* The token header is an overlay over the (all zero) session
* field
*/
memcpy(ep11_token_header, ep11key, sizeof(ep11_token_header));
memset(ep11key->session, 0, sizeof(ep11key->session));
}

resp_len = sizeof(resp);
req_len = ep11->dll_xcpa_cmdblock(req, sizeof(req), XCP_ADM_REENCRYPT,
Expand Down Expand Up @@ -446,7 +460,8 @@ static int ep11_adm_reencrypt(struct ep11_lib *ep11, target_t target,
}

memcpy(ep11key, lrb.payload, blob_len);
memcpy(ep11key, ep11_token_header, sizeof(ep11_token_header));
if (!with_header)
memcpy(ep11key, ep11_token_header, sizeof(ep11_token_header));

return 0;
}
Expand All @@ -469,7 +484,6 @@ int reencipher_ep11_key(struct ep11_lib *ep11, target_t target,
unsigned int card, unsigned int domain, u8 *secure_key,
unsigned int secure_key_size, bool verbose)
{
struct ep11keytoken *ep11key = (struct ep11keytoken *)secure_key;
CK_IBM_DOMAIN_INFO dinf;
CK_ULONG dinf_len = sizeof(dinf);
CK_RV rv;
Expand All @@ -493,17 +507,21 @@ int reencipher_ep11_key(struct ep11_lib *ep11, target_t target,
return -ENODEV;
}

rc = ep11_adm_reencrypt(ep11, target, card, domain, ep11key,
rc = ep11_adm_reencrypt(ep11, target, card, domain, secure_key,
secure_key_size, verbose);
if (rc != 0)
return rc;

if (is_xts_key(secure_key, secure_key_size)) {
secure_key += EP11_KEY_SIZE;
secure_key_size -= EP11_KEY_SIZE;
ep11key = (struct ep11keytoken *)secure_key;
if (is_ep11_aes_key_with_header(secure_key, secure_key_size)) {
secure_key += EP11_AES_KEY_SIZE;
secure_key_size -= EP11_AES_KEY_SIZE;
} else {
secure_key += EP11_KEY_SIZE;
secure_key_size -= EP11_KEY_SIZE;
}

rc = ep11_adm_reencrypt(ep11, target, card, domain, ep11key,
rc = ep11_adm_reencrypt(ep11, target, card, domain, secure_key,
secure_key_size, verbose);
if (rc != 0)
return rc;
Expand Down
4 changes: 3 additions & 1 deletion zkey/keystore.c
Expand Up @@ -3398,7 +3398,9 @@ static int _keystore_perform_reencipher(struct keystore *keystore,
"CURRENT master key", name);
if (!selected &&
!is_ep11_aes_key(secure_key,
secure_key_size))
secure_key_size) &&
!is_ep11_aes_key_with_header(secure_key,
secure_key_size))
print_msg_for_cca_envvars(
"secure AES key");
}
Expand Down
76 changes: 64 additions & 12 deletions zkey/kmip/zkey-kmip.c
Expand Up @@ -5278,9 +5278,11 @@ static int _ep11_unwrap_key_rsa(struct plugin_handle *ph,
m_UnwrapKey_t dll_m_UnwrapKey;
const unsigned char *key_blob;
struct ep11keytoken *ep11key;
struct ep11kblob_header *hdr;
CK_MECHANISM mech = { 0 };
CK_BYTE csum[7] = { 0 };
CK_BBOOL ck_true = true;
int pkey_fd, rc;
CK_RV rv;

CK_ATTRIBUTE template[] = {
Expand All @@ -5306,7 +5308,8 @@ static int _ep11_unwrap_key_rsa(struct plugin_handle *ph,
pr_verbose(&ph->pd, "Wrap hashing algorithm: %d",
ph->profile->wrap_hashing_algo);

if (*unwrapped_key_len < sizeof(struct ep11keytoken)) {
if (*unwrapped_key_len < sizeof(struct ep11kblob_header) +
sizeof(struct ep11keytoken)) {
_set_error(ph, "Key buffer is too small");
return -EINVAL;
}
Expand Down Expand Up @@ -5381,19 +5384,68 @@ static int _ep11_unwrap_key_rsa(struct plugin_handle *ph,
256 * 256 * csum[csum_len - 3] +
256 * 256 * 256 * csum[csum_len - 4];

/* Setup the EP11 token header */
ep11key = (struct ep11keytoken *)unwrapped_key;
memset(&ep11key->session, 0, sizeof(ep11key->session));
ep11key->head.type = TOKEN_TYPE_NON_CCA;
ep11key->head.length = *unwrapped_key_len;
ep11key->head.version = TOKEN_VERSION_EP11_AES;
ep11key->head.keybitlen = bit_len;

pr_verbose(&ph->pd, "unwrapped bit length: %u",
ep11key->head.keybitlen);
/* Prepend and setup the EP11 token header */
hdr = (struct ep11kblob_header *)unwrapped_key;
ep11key = (struct ep11keytoken *)
(unwrapped_key + sizeof(struct ep11kblob_header));
memmove(ep11key, unwrapped_key, *unwrapped_key_len);
*unwrapped_key_len += sizeof(struct ep11kblob_header);
memset(hdr, 0, sizeof(struct ep11kblob_header));
hdr->type = TOKEN_TYPE_NON_CCA;
hdr->hver = 0;
hdr->len = *unwrapped_key_len;
hdr->version = TOKEN_VERSION_EP11_AES_WITH_HEADER;
hdr->bitlen = bit_len;

pr_verbose(&ph->pd, "unwrapped bit length: %u", hdr->bitlen);

/* return full length, blob is already zero padded */
*unwrapped_key_len = sizeof(struct ep11keytoken);
*unwrapped_key_len =
sizeof(struct ep11kblob_header) + sizeof(struct ep11keytoken);

/*
* Check if the pkey module supports keys of type
* TOKEN_VERSION_EP11_AES_WITH_HEADER, older kernels may not support
* such keys. If it does not support such keys, convert the key to
* TOKEN_VERSION_EP11_AES type, if its session field is all zero
* (i.e. the key is not session bound).
*/
pkey_fd = open_pkey_device(ph->pd.verbose);
if (pkey_fd < 0) {
_set_error(ph, "Failed to open pkey device");
return -EIO;
}

rc = validate_secure_key(pkey_fd, unwrapped_key, *unwrapped_key_len,
NULL, NULL, NULL, ph->pd.verbose);
close(pkey_fd);
if (rc == -EINVAL || rc == -ENODEV) {
pr_verbose(&ph->pd, "The pkey kernel module does not support "
"PKEY_TYPE_EP11_AES, fall back to PKEY_TYPE_EP11");

if (is_ep11_key_session_bound(unwrapped_key,
*unwrapped_key_len)) {
_set_error(ph, "The unwrapped key is session bound. "
"Kernel support is required for such keys");
return -EIO;
}

key_blob_len = hdr->len;
*unwrapped_key_len -= sizeof(struct ep11kblob_header);
memmove(unwrapped_key,
unwrapped_key + sizeof(struct ep11kblob_header),
*unwrapped_key_len);
ep11key = (struct ep11keytoken *)unwrapped_key;
memset(&ep11key->session, 0, sizeof(ep11key->session));
ep11key->head.type = TOKEN_TYPE_NON_CCA;
ep11key->head.len = key_blob_len -
sizeof(struct ep11kblob_header);
ep11key->head.version = TOKEN_VERSION_EP11_AES;
ep11key->head.bitlen = bit_len;
} else if (rc != 0) {
_set_error(ph, "Failed to validate unwrapped key");
return rc;
}

return 0;
}
Expand Down
9 changes: 8 additions & 1 deletion zkey/kms.c
Expand Up @@ -2175,7 +2175,7 @@ int generate_kms_key(struct kms_info *kms_info, const char *name,
else if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0)
key_size = AESCIPHER_KEY_SIZE;
else if (strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0)
key_size = EP11_KEY_SIZE;
key_size = EP11_AES_KEY_SIZE;
else
return -ENOTSUP;

Expand Down Expand Up @@ -2248,6 +2248,9 @@ int generate_kms_key(struct kms_info *kms_info, const char *name,
if (verbose)
util_hexdump_grp(stderr, NULL, key_blob, 4, key_blob_size, 0);

if (is_ep11_aes_key(key_blob, key_blob_size))
key_size = EP11_KEY_SIZE;

/* Save ID and label of 1st key */
rc = properties_set(key_props, xts ? PROP_NAME_KMS_XTS_KEY1_ID :
PROP_NAME_KMS_KEY_ID, key1_id);
Expand Down Expand Up @@ -3132,6 +3135,8 @@ int import_kms_key(struct kms_info *kms_info, const char *key1_id,
key_size = AESCIPHER_KEY_SIZE;
else if (is_ep11_aes_key(key_blob, key_blob_size))
key_size = EP11_KEY_SIZE;
else if (is_ep11_aes_key_with_header(key_blob, key_blob_size))
key_size = EP11_AES_KEY_SIZE;

if (key_size == 0 || key_blob_size > key_size) {
pr_verbose(verbose, "Key '%s' has an unknown or unsupported "
Expand Down Expand Up @@ -3366,6 +3371,8 @@ int refresh_kms_key(struct kms_info *kms_info, struct properties *key_props,
key_size = AESCIPHER_KEY_SIZE;
else if (is_ep11_aes_key(key_blob, key_blob_size))
key_size = EP11_KEY_SIZE;
else if (is_ep11_aes_key_with_header(key_blob, key_blob_size))
key_size = EP11_AES_KEY_SIZE;

if (key_size == 0 || key_blob_size > key_size) {
pr_verbose(verbose, "Key '%s' has an unknown or unsupported "
Expand Down

0 comments on commit 1b044b8

Please sign in to comment.