From 0be7efc956340a20ecf6a5d2e71278dc66f03f05 Mon Sep 17 00:00:00 2001 From: Ingo Franzki Date: Tue, 19 Nov 2019 14:04:17 +0100 Subject: [PATCH] zkey: Add support for re-enciphering EP11 secure keys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-encipher EP11 secure keys using the EP11 host library. Signed-off-by: Ingo Franzki Reviewed-by: Harald Freudenberger Signed-off-by: Jan Höppner --- zkey/ep11.c | 173 +++++++++++++++++++++++++++++++++++++++++++++++++++- zkey/ep11.h | 59 ++++++++++++++++++ 2 files changed, 231 insertions(+), 1 deletion(-) diff --git a/zkey/ep11.c b/zkey/ep11.c index e6ef1957..22f81f7e 100644 --- a/zkey/ep11.c +++ b/zkey/ep11.c @@ -126,9 +126,25 @@ int load_ep11_library(struct ep11_lib *ep11, bool verbose) ep11->dll_m_get_xcp_info = (m_get_xcp_info_t)dlsym(ep11->lib_ep11, "m_get_xcp_info"); + ep11->dll_m_admin = (m_admin_t)dlsym(ep11->lib_ep11, "m_admin"); + ep11->dll_xcpa_cmdblock = (xcpa_cmdblock_t)dlsym(ep11->lib_ep11, + "xcpa_cmdblock"); + if (ep11->dll_xcpa_cmdblock == NULL) + ep11->dll_xcpa_cmdblock = (xcpa_cmdblock_t)dlsym(ep11->lib_ep11, + "ep11a_cmdblock"); + ep11->dll_xcpa_internal_rv = (xcpa_internal_rv_t)dlsym(ep11->lib_ep11, + "xcpa_internal_rv"); + if (ep11->dll_xcpa_internal_rv == NULL) + ep11->dll_xcpa_internal_rv = + (xcpa_internal_rv_t)dlsym(ep11->lib_ep11, + "ep11a_internal_rv"); + /* dll_m_add_module and dll_m_rm_module may be NULL for V1 EP11 lib */ if (ep11->dll_m_init == NULL || - ep11->dll_m_get_xcp_info == NULL) { + ep11->dll_m_get_xcp_info == NULL || + ep11->dll_m_admin == NULL || + ep11->dll_xcpa_cmdblock == NULL || + ep11->dll_xcpa_internal_rv == NULL) { pr_verbose(verbose, "%s", dlerror()); warnx("The command requires the IBM Z Enterprise PKCS #11 " "(EP11) Support Program (EP11 host library).\n" @@ -337,3 +353,158 @@ int select_ep11_apqn_by_mkvp(struct ep11_lib *ep11, u8 *mkvp, return 0; } + +/** + * Performs an EP11 administrative request to Re-encrypt a single EP11 secure + * key with a new EP11 master key (wrapping key). + * + * @param[in] ep11 the EP11 library structure + * @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] 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, int card, + int domain, struct ep11keytoken *ep11key, + unsigned int ep11key_size, bool verbose) +{ + CK_BYTE resp[MAX_BLOBSIZE]; + CK_BYTE req[MAX_BLOBSIZE]; + char ep11_token_header[sizeof(ep11key->head)]; + struct XCPadmresp lrb; + struct XCPadmresp rb; + size_t resp_len; + size_t blob_len; + long req_len; + CK_RV rv; + int rc; + + blob_len = ep11key->head.length; + if (blob_len > ep11key_size) { + pr_verbose(verbose, "Blob length larger than secure key size"); + return -EINVAL; + } + + 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)); + + resp_len = sizeof(resp); + req_len = ep11->dll_xcpa_cmdblock(req, sizeof(req), XCP_ADM_REENCRYPT, + &rb, NULL, (unsigned char *)ep11key, + blob_len); + if (req_len < 0) { + pr_verbose(verbose, "Failed to build XCP command block"); + return -EIO; + } + + rv = ep11->dll_m_admin(resp, &resp_len, NULL, 0, req, req_len, NULL, 0, + target); + if (rv != CKR_OK || resp_len == 0) { + pr_verbose(verbose, "Command XCP_ADM_REENCRYPT failed. " + "rc = 0x%lx, resp_len = %ld", rv, resp_len); + return -EIO; + } + + rc = ep11->dll_xcpa_internal_rv(resp, resp_len, &lrb, &rv); + if (rc != 0) { + pr_verbose(verbose, "Failed to parse response. rc = %d", rc); + return -EIO; + } + + if (rv != CKR_OK) { + pr_verbose(verbose, "Failed to re-encrypt the EP11 secure key. " + "rc = 0x%lx", rv); + switch (rv) { + case CKR_IBM_WKID_MISMATCH: + warnx("The EP11 secure key is currently encrypted " + "under a different master that does not match " + "the master key in the CURRENT master key " + "register of APQN %02X.%04X", card, domain); + break; + } + return -EIO; + } + + if (blob_len != lrb.pllen) { + pr_verbose(verbose, "Re-encrypted EP11 secure key size has " + "changed: org-len: %lu, new-len: %lu", blob_len, + lrb.pllen); + return -EIO; + } + + memcpy(ep11key, lrb.payload, blob_len); + memcpy(ep11key, ep11_token_header, sizeof(ep11_token_header)); + + return 0; +} + +/** + * Re-encipher an EP11 secure key with a new EP11 master key (wrapping key). + * + * @param[in] ep11 the EP11 library structure + * @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] secure_key the EP11 key token to reencipher. The re-enciphered + * secure key will be returned in this buffer. + * @param[in] secure_key_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 + */ +int reencipher_ep11_key(struct ep11_lib *ep11, target_t target, int card, + 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; + int rc; + + util_assert(ep11 != NULL, "Internal error: ep11 is NULL"); + util_assert(secure_key != NULL, "Internal error: secure_key is NULL"); + + rv = ep11->dll_m_get_xcp_info(&dinf, &dinf_len, CK_IBM_XCPQ_DOMAIN, 0, + target); + if (rv != CKR_OK) { + pr_verbose(verbose, "Failed to query domain information for " + "%02X.%04X: m_get_xcp_info rc: 0x%lx", card, domain, + rv); + return -EIO; + } + + if ((dinf.flags & CK_IBM_DOM_COMMITTED_NWK) == 0) { + warnx("The NEW master key register of APQN %02X.%04X is not " + "in COMMITTED state", card, domain); + return -ENODEV; + } + + rc = ep11_adm_reencrypt(ep11, target, card, domain, ep11key, + 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; + + rc = ep11_adm_reencrypt(ep11, target, card, domain, ep11key, + secure_key_size, verbose); + if (rc != 0) + return rc; + } + + return 0; +} + diff --git a/zkey/ep11.h b/zkey/ep11.h index 212c9f9f..3a0c2b59 100644 --- a/zkey/ep11.h +++ b/zkey/ep11.h @@ -68,7 +68,48 @@ typedef enum { #define XCPTGTMASK_SET_DOM(mask, domain) \ mask[((domain)/8)] |= (1 << (7-(domain)%8)) +#define XCP_SERIALNR_CHARS 8 +#define XCP_ADMCTR_BYTES ((size_t) (128/8)) +#define XCP_KEYCSUM_BYTES (256/8) + +#define XCP_ADM_REENCRYPT 25 /* transform blobs to next WK */ + +#define MAX_BLOBSIZE 8192 + +#define CKR_VENDOR_DEFINED 0x80000000 +#define CKR_IBM_WKID_MISMATCH CKR_VENDOR_DEFINED + 0x10001 + +typedef struct XCPadmresp { + uint32_t fn; + uint32_t domain; + uint32_t domainInst; + + /* module ID || module instance */ + unsigned char module[XCP_SERIALNR_CHARS + XCP_SERIALNR_CHARS]; + unsigned char modNr[XCP_SERIALNR_CHARS]; + unsigned char modInst[XCP_SERIALNR_CHARS]; + + unsigned char tctr[XCP_ADMCTR_BYTES]; /* transaction counter */ + + CK_RV rv; + uint32_t reason; + + const unsigned char *payload; + size_t pllen; +} *XCPadmresp_t; + +typedef struct CK_IBM_DOMAIN_INFO { + CK_ULONG domain; + CK_BYTE wk[XCP_KEYCSUM_BYTES]; + CK_BYTE nextwk[XCP_KEYCSUM_BYTES]; + CK_ULONG flags; + CK_BYTE mode[8]; +} CK_IBM_DOMAIN_INFO; + +#define CK_IBM_DOM_COMMITTED_NWK 8 + #define CK_IBM_XCPHQ_VERSION 0xff000001 +#define CK_IBM_XCPQ_DOMAIN 3 #define MAX_APQN 256 @@ -86,6 +127,17 @@ typedef int (*m_rm_module_t) (XCP_Module_t module, target_t target); typedef CK_RV (*m_get_xcp_info_t)(CK_VOID_PTR pinfo, CK_ULONG_PTR infbytes, unsigned int query, unsigned int subquery, target_t target); +typedef unsigned long int (*m_admin_t)(unsigned char *resp1, size_t *r1len, + unsigned char *resp2, size_t *r2len, + const unsigned char *cmd, size_t clen, + const unsigned char *sigs, size_t slen, + target_t target); +typedef long (*xcpa_cmdblock_t)(unsigned char *blk, size_t blen, + unsigned int fn, const struct XCPadmresp *minf, + const unsigned char *tctr, + const unsigned char *payload, size_t plen); +typedef long (*xcpa_internal_rv_t)(const unsigned char *rsp, size_t rlen, + struct XCPadmresp *rspblk, CK_RV *rv); struct ep11_version { unsigned int minor; @@ -98,6 +150,9 @@ struct ep11_lib { m_add_module_t dll_m_add_module; m_rm_module_t dll_m_rm_module; m_get_xcp_info_t dll_m_get_xcp_info; + m_admin_t dll_m_admin; + xcpa_cmdblock_t dll_xcpa_cmdblock; + xcpa_internal_rv_t dll_xcpa_internal_rv; struct ep11_version version; }; @@ -116,4 +171,8 @@ int select_ep11_apqn_by_mkvp(struct ep11_lib *ep11, u8 *mkvp, target_t *target, int *card, int *domain, bool verbose); +int reencipher_ep11_key(struct ep11_lib *ep11, target_t target, int card, + int domain, u8 *secure_key, + unsigned int secure_key_size, bool verbose); + #endif