// SPDX-License-Identifier: GPL-2.0 /* * pkey cca specific code * * Copyright IBM Corp. 2024 */ #define KMSG_COMPONENT "pkey" #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt #include #include #include #include "zcrypt_ccamisc.h" #include "pkey_base.h" MODULE_LICENSE("GPL"); MODULE_AUTHOR("IBM Corporation"); MODULE_DESCRIPTION("s390 protected key CCA handler"); #if IS_MODULE(CONFIG_PKEY_CCA) static struct ap_device_id pkey_cca_card_ids[] = { { .dev_type = AP_DEVICE_TYPE_CEX4 }, { .dev_type = AP_DEVICE_TYPE_CEX5 }, { .dev_type = AP_DEVICE_TYPE_CEX6 }, { .dev_type = AP_DEVICE_TYPE_CEX7 }, { .dev_type = AP_DEVICE_TYPE_CEX8 }, { /* end of list */ }, }; MODULE_DEVICE_TABLE(ap, pkey_cca_card_ids); #endif /* * Check key blob for known and supported CCA key. */ static bool is_cca_key(const u8 *key, u32 keylen) { struct keytoken_header *hdr = (struct keytoken_header *)key; if (keylen < sizeof(*hdr)) return false; switch (hdr->type) { case TOKTYPE_CCA_INTERNAL: switch (hdr->version) { case TOKVER_CCA_AES: case TOKVER_CCA_VLSC: return true; default: return false; } case TOKTYPE_CCA_INTERNAL_PKA: return true; default: return false; } } static bool is_cca_keytype(enum pkey_key_type key_type) { switch (key_type) { case PKEY_TYPE_CCA_DATA: case PKEY_TYPE_CCA_CIPHER: case PKEY_TYPE_CCA_ECC: return true; default: return false; } } static int cca_apqns4key(const u8 *key, u32 keylen, u32 flags, struct pkey_apqn *apqns, size_t *nr_apqns) { struct keytoken_header *hdr = (struct keytoken_header *)key; u32 _nr_apqns, *_apqns = NULL; int rc; if (!flags) flags = PKEY_FLAGS_MATCH_CUR_MKVP | PKEY_FLAGS_MATCH_ALT_MKVP; if (keylen < sizeof(struct keytoken_header)) return -EINVAL; zcrypt_wait_api_operational(); if (hdr->type == TOKTYPE_CCA_INTERNAL) { u64 cur_mkvp = 0, old_mkvp = 0; int minhwtype = ZCRYPT_CEX3C; if (hdr->version == TOKVER_CCA_AES) { struct secaeskeytoken *t = (struct secaeskeytoken *)key; if (flags & PKEY_FLAGS_MATCH_CUR_MKVP) cur_mkvp = t->mkvp; if (flags & PKEY_FLAGS_MATCH_ALT_MKVP) old_mkvp = t->mkvp; } else if (hdr->version == TOKVER_CCA_VLSC) { struct cipherkeytoken *t = (struct cipherkeytoken *)key; minhwtype = ZCRYPT_CEX6; if (flags & PKEY_FLAGS_MATCH_CUR_MKVP) cur_mkvp = t->mkvp0; if (flags & PKEY_FLAGS_MATCH_ALT_MKVP) old_mkvp = t->mkvp0; } else { /* unknown CCA internal token type */ return -EINVAL; } rc = cca_findcard2(&_apqns, &_nr_apqns, 0xFFFF, 0xFFFF, minhwtype, AES_MK_SET, cur_mkvp, old_mkvp, 1); if (rc) goto out; } else if (hdr->type == TOKTYPE_CCA_INTERNAL_PKA) { struct eccprivkeytoken *t = (struct eccprivkeytoken *)key; u64 cur_mkvp = 0, old_mkvp = 0; if (t->secid == 0x20) { if (flags & PKEY_FLAGS_MATCH_CUR_MKVP) cur_mkvp = t->mkvp; if (flags & PKEY_FLAGS_MATCH_ALT_MKVP) old_mkvp = t->mkvp; } else { /* unknown CCA internal 2 token type */ return -EINVAL; } rc = cca_findcard2(&_apqns, &_nr_apqns, 0xFFFF, 0xFFFF, ZCRYPT_CEX7, APKA_MK_SET, cur_mkvp, old_mkvp, 1); if (rc) goto out; } else { PKEY_DBF_ERR("%s unknown/unsupported blob type %d version %d\n", __func__, hdr->type, hdr->version); return -EINVAL; } if (apqns) { if (*nr_apqns < _nr_apqns) rc = -ENOSPC; else memcpy(apqns, _apqns, _nr_apqns * sizeof(u32)); } *nr_apqns = _nr_apqns; out: kfree(_apqns); pr_debug("rc=%d\n", rc); return rc; } static int cca_apqns4type(enum pkey_key_type ktype, u8 cur_mkvp[32], u8 alt_mkvp[32], u32 flags, struct pkey_apqn *apqns, size_t *nr_apqns) { u32 _nr_apqns, *_apqns = NULL; int rc; zcrypt_wait_api_operational(); if (ktype == PKEY_TYPE_CCA_DATA || ktype == PKEY_TYPE_CCA_CIPHER) { u64 cur_mkvp = 0, old_mkvp = 0; int minhwtype = ZCRYPT_CEX3C; if (flags & PKEY_FLAGS_MATCH_CUR_MKVP) cur_mkvp = *((u64 *)cur_mkvp); if (flags & PKEY_FLAGS_MATCH_ALT_MKVP) old_mkvp = *((u64 *)alt_mkvp); if (ktype == PKEY_TYPE_CCA_CIPHER) minhwtype = ZCRYPT_CEX6; rc = cca_findcard2(&_apqns, &_nr_apqns, 0xFFFF, 0xFFFF, minhwtype, AES_MK_SET, cur_mkvp, old_mkvp, 1); if (rc) goto out; } else if (ktype == PKEY_TYPE_CCA_ECC) { u64 cur_mkvp = 0, old_mkvp = 0; if (flags & PKEY_FLAGS_MATCH_CUR_MKVP) cur_mkvp = *((u64 *)cur_mkvp); if (flags & PKEY_FLAGS_MATCH_ALT_MKVP) old_mkvp = *((u64 *)alt_mkvp); rc = cca_findcard2(&_apqns, &_nr_apqns, 0xFFFF, 0xFFFF, ZCRYPT_CEX7, APKA_MK_SET, cur_mkvp, old_mkvp, 1); if (rc) goto out; } else { PKEY_DBF_ERR("%s unknown/unsupported key type %d", __func__, (int)ktype); return -EINVAL; } if (apqns) { if (*nr_apqns < _nr_apqns) rc = -ENOSPC; else memcpy(apqns, _apqns, _nr_apqns * sizeof(u32)); } *nr_apqns = _nr_apqns; out: kfree(_apqns); pr_debug("rc=%d\n", rc); return rc; } static int cca_key2protkey(const struct pkey_apqn *apqns, size_t nr_apqns, const u8 *key, u32 keylen, u8 *protkey, u32 *protkeylen, u32 *protkeytype) { struct keytoken_header *hdr = (struct keytoken_header *)key; struct pkey_apqn *local_apqns = NULL; int i, rc; if (keylen < sizeof(*hdr)) return -EINVAL; if (hdr->type == TOKTYPE_CCA_INTERNAL && hdr->version == TOKVER_CCA_AES) { /* CCA AES data key */ if (keylen < sizeof(struct secaeskeytoken)) return -EINVAL; if (cca_check_secaeskeytoken(pkey_dbf_info, 3, key, 0)) return -EINVAL; } else if (hdr->type == TOKTYPE_CCA_INTERNAL && hdr->version == TOKVER_CCA_VLSC) { /* CCA AES cipher key */ if (keylen < hdr->len) return -EINVAL; if (cca_check_secaescipherkey(pkey_dbf_info, 3, key, 0, 1)) return -EINVAL; } else if (hdr->type == TOKTYPE_CCA_INTERNAL_PKA) { /* CCA ECC (private) key */ if (keylen < sizeof(struct eccprivkeytoken)) return -EINVAL; if (cca_check_sececckeytoken(pkey_dbf_info, 3, key, keylen, 1)) return -EINVAL; } else { PKEY_DBF_ERR("%s unknown/unsupported blob type %d version %d\n", __func__, hdr->type, hdr->version); return -EINVAL; } zcrypt_wait_api_operational(); if (!apqns || (nr_apqns == 1 && apqns[0].card == 0xFFFF && apqns[0].domain == 0xFFFF)) { nr_apqns = MAXAPQNSINLIST; local_apqns = kmalloc_array(nr_apqns, sizeof(struct pkey_apqn), GFP_KERNEL); if (!local_apqns) return -ENOMEM; rc = cca_apqns4key(key, keylen, 0, local_apqns, &nr_apqns); if (rc) goto out; apqns = local_apqns; } for (rc = -ENODEV, i = 0; rc && i < nr_apqns; i++) { if (hdr->type == TOKTYPE_CCA_INTERNAL && hdr->version == TOKVER_CCA_AES) { rc = cca_sec2protkey(apqns[i].card, apqns[i].domain, key, protkey, protkeylen, protkeytype); } else if (hdr->type == TOKTYPE_CCA_INTERNAL && hdr->version == TOKVER_CCA_VLSC) { rc = cca_cipher2protkey(apqns[i].card, apqns[i].domain, key, protkey, protkeylen, protkeytype); } else if (hdr->type == TOKTYPE_CCA_INTERNAL_PKA) { rc = cca_ecc2protkey(apqns[i].card, apqns[i].domain, key, protkey, protkeylen, protkeytype); } else { rc = -EINVAL; break; } } out: kfree(local_apqns); pr_debug("rc=%d\n", rc); return rc; } /* * Generate CCA secure key. * As of now only CCA AES Data or Cipher secure keys are * supported. * keytype is one of the PKEY_KEYTYPE_* constants, * subtype may be 0 or PKEY_TYPE_CCA_DATA or PKEY_TYPE_CCA_CIPHER, * keybitsize is the bit size of the key (may be 0 for * keytype PKEY_KEYTYPE_AES_*). */ static int cca_gen_key(const struct pkey_apqn *apqns, size_t nr_apqns, u32 keytype, u32 subtype, u32 keybitsize, u32 flags, u8 *keybuf, u32 *keybuflen, u32 *_keyinfo) { struct pkey_apqn *local_apqns = NULL; int i, len, rc; /* check keytype, subtype, keybitsize */ switch (keytype) { case PKEY_KEYTYPE_AES_128: case PKEY_KEYTYPE_AES_192: case PKEY_KEYTYPE_AES_256: len = pkey_keytype_aes_to_size(keytype); if (keybitsize && keybitsize != 8 * len) { PKEY_DBF_ERR("%s unknown/unsupported keybitsize %d\n", __func__, keybitsize); return -EINVAL; } keybitsize = 8 * len; switch (subtype) { case PKEY_TYPE_CCA_DATA: case PKEY_TYPE_CCA_CIPHER: break; default: PKEY_DBF_ERR("%s unknown/unsupported subtype %d\n", __func__, subtype); return -EINVAL; } break; default: PKEY_DBF_ERR("%s unknown/unsupported keytype %d\n", __func__, keytype); return -EINVAL; } zcrypt_wait_api_operational(); if (!apqns || (nr_apqns == 1 && apqns[0].card == 0xFFFF && apqns[0].domain == 0xFFFF)) { nr_apqns = MAXAPQNSINLIST; local_apqns = kmalloc_array(nr_apqns, sizeof(struct pkey_apqn), GFP_KERNEL); if (!local_apqns) return -ENOMEM; rc = cca_apqns4type(subtype, NULL, NULL, 0, local_apqns, &nr_apqns); if (rc) goto out; apqns = local_apqns; } for (rc = -ENODEV, i = 0; rc && i < nr_apqns; i++) { if (subtype == PKEY_TYPE_CCA_CIPHER) { rc = cca_gencipherkey(apqns[i].card, apqns[i].domain, keybitsize, flags, keybuf, keybuflen); } else { /* PKEY_TYPE_CCA_DATA */ rc = cca_genseckey(apqns[i].card, apqns[i].domain, keybitsize, keybuf); *keybuflen = (rc ? 0 : SECKEYBLOBSIZE); } } out: kfree(local_apqns); pr_debug("rc=%d\n", rc); return rc; } /* * Generate CCA secure key with given clear key value. * As of now only CCA AES Data or Cipher secure keys are * supported. * keytype is one of the PKEY_KEYTYPE_* constants, * subtype may be 0 or PKEY_TYPE_CCA_DATA or PKEY_TYPE_CCA_CIPHER, * keybitsize is the bit size of the key (may be 0 for * keytype PKEY_KEYTYPE_AES_*). */ static int cca_clr2key(const struct pkey_apqn *apqns, size_t nr_apqns, u32 keytype, u32 subtype, u32 keybitsize, u32 flags, const u8 *clrkey, u32 clrkeylen, u8 *keybuf, u32 *keybuflen, u32 *_keyinfo) { struct pkey_apqn *local_apqns = NULL; int i, len, rc; /* check keytype, subtype, clrkeylen, keybitsize */ switch (keytype) { case PKEY_KEYTYPE_AES_128: case PKEY_KEYTYPE_AES_192: case PKEY_KEYTYPE_AES_256: len = pkey_keytype_aes_to_size(keytype); if (keybitsize && keybitsize != 8 * len) { PKEY_DBF_ERR("%s unknown/unsupported keybitsize %d\n", __func__, keybitsize); return -EINVAL; } keybitsize = 8 * len; if (clrkeylen != len) { PKEY_DBF_ERR("%s invalid clear key len %d != %d\n", __func__, clrkeylen, len); return -EINVAL; } switch (subtype) { case PKEY_TYPE_CCA_DATA: case PKEY_TYPE_CCA_CIPHER: break; default: PKEY_DBF_ERR("%s unknown/unsupported subtype %d\n", __func__, subtype); return -EINVAL; } break; default: PKEY_DBF_ERR("%s unknown/unsupported keytype %d\n", __func__, keytype); return -EINVAL; } zcrypt_wait_api_operational(); if (!apqns || (nr_apqns == 1 && apqns[0].card == 0xFFFF && apqns[0].domain == 0xFFFF)) { nr_apqns = MAXAPQNSINLIST; local_apqns = kmalloc_array(nr_apqns, sizeof(struct pkey_apqn), GFP_KERNEL); if (!local_apqns) return -ENOMEM; rc = cca_apqns4type(subtype, NULL, NULL, 0, local_apqns, &nr_apqns); if (rc) goto out; apqns = local_apqns; } for (rc = -ENODEV, i = 0; rc && i < nr_apqns; i++) { if (subtype == PKEY_TYPE_CCA_CIPHER) { rc = cca_clr2cipherkey(apqns[i].card, apqns[i].domain, keybitsize, flags, clrkey, keybuf, keybuflen); } else { /* PKEY_TYPE_CCA_DATA */ rc = cca_clr2seckey(apqns[i].card, apqns[i].domain, keybitsize, clrkey, keybuf); *keybuflen = (rc ? 0 : SECKEYBLOBSIZE); } } out: kfree(local_apqns); pr_debug("rc=%d\n", rc); return rc; } static int cca_verifykey(const u8 *key, u32 keylen, u16 *card, u16 *dom, u32 *keytype, u32 *keybitsize, u32 *flags) { struct keytoken_header *hdr = (struct keytoken_header *)key; u32 nr_apqns, *apqns = NULL; int rc; if (keylen < sizeof(*hdr)) return -EINVAL; zcrypt_wait_api_operational(); if (hdr->type == TOKTYPE_CCA_INTERNAL && hdr->version == TOKVER_CCA_AES) { struct secaeskeytoken *t = (struct secaeskeytoken *)key; rc = cca_check_secaeskeytoken(pkey_dbf_info, 3, key, 0); if (rc) goto out; *keytype = PKEY_TYPE_CCA_DATA; *keybitsize = t->bitsize; rc = cca_findcard2(&apqns, &nr_apqns, *card, *dom, ZCRYPT_CEX3C, AES_MK_SET, t->mkvp, 0, 1); if (!rc) *flags = PKEY_FLAGS_MATCH_CUR_MKVP; if (rc == -ENODEV) { rc = cca_findcard2(&apqns, &nr_apqns, *card, *dom, ZCRYPT_CEX3C, AES_MK_SET, 0, t->mkvp, 1); if (!rc) *flags = PKEY_FLAGS_MATCH_ALT_MKVP; } if (rc) goto out; *card = ((struct pkey_apqn *)apqns)->card; *dom = ((struct pkey_apqn *)apqns)->domain; } else if (hdr->type == TOKTYPE_CCA_INTERNAL && hdr->version == TOKVER_CCA_VLSC) { struct cipherkeytoken *t = (struct cipherkeytoken *)key; rc = cca_check_secaescipherkey(pkey_dbf_info, 3, key, 0, 1); if (rc) goto out; *keytype = PKEY_TYPE_CCA_CIPHER; *keybitsize = PKEY_SIZE_UNKNOWN; if (!t->plfver && t->wpllen == 512) *keybitsize = PKEY_SIZE_AES_128; else if (!t->plfver && t->wpllen == 576) *keybitsize = PKEY_SIZE_AES_192; else if (!t->plfver && t->wpllen == 640) *keybitsize = PKEY_SIZE_AES_256; rc = cca_findcard2(&apqns, &nr_apqns, *card, *dom, ZCRYPT_CEX6, AES_MK_SET, t->mkvp0, 0, 1); if (!rc) *flags = PKEY_FLAGS_MATCH_CUR_MKVP; if (rc == -ENODEV) { rc = cca_findcard2(&apqns, &nr_apqns, *card, *dom, ZCRYPT_CEX6, AES_MK_SET, 0, t->mkvp0, 1); if (!rc) *flags = PKEY_FLAGS_MATCH_ALT_MKVP; } if (rc) goto out; *card = ((struct pkey_apqn *)apqns)->card; *dom = ((struct pkey_apqn *)apqns)->domain; } else { /* unknown/unsupported key blob */ rc = -EINVAL; } out: kfree(apqns); pr_debug("rc=%d\n", rc); return rc; } /* * This function provides an alternate but usually slow way * to convert a 'clear key token' with AES key material into * a protected key. This is done via an intermediate step * which creates a CCA AES DATA secure key first and then * derives the protected key from this secure key. */ static int cca_slowpath_key2protkey(const struct pkey_apqn *apqns, size_t nr_apqns, const u8 *key, u32 keylen, u8 *protkey, u32 *protkeylen, u32 *protkeytype) { const struct keytoken_header *hdr = (const struct keytoken_header *)key; const struct clearkeytoken *t = (const struct clearkeytoken *)key; u32 tmplen, keysize = 0; u8 *tmpbuf; int i, rc; if (keylen < sizeof(*hdr)) return -EINVAL; if (hdr->type == TOKTYPE_NON_CCA && hdr->version == TOKVER_CLEAR_KEY) keysize = pkey_keytype_aes_to_size(t->keytype); if (!keysize || t->len != keysize) return -EINVAL; /* alloc tmp key buffer */ tmpbuf = kmalloc(SECKEYBLOBSIZE, GFP_ATOMIC); if (!tmpbuf) return -ENOMEM; /* try two times in case of failure */ for (i = 0, rc = -ENODEV; i < 2 && rc; i++) { tmplen = SECKEYBLOBSIZE; rc = cca_clr2key(NULL, 0, t->keytype, PKEY_TYPE_CCA_DATA, 8 * keysize, 0, t->clearkey, t->len, tmpbuf, &tmplen, NULL); pr_debug("cca_clr2key()=%d\n", rc); if (rc) continue; rc = cca_key2protkey(NULL, 0, tmpbuf, tmplen, protkey, protkeylen, protkeytype); pr_debug("cca_key2protkey()=%d\n", rc); } kfree(tmpbuf); pr_debug("rc=%d\n", rc); return rc; } static struct pkey_handler cca_handler = { .module = THIS_MODULE, .name = "PKEY CCA handler", .is_supported_key = is_cca_key, .is_supported_keytype = is_cca_keytype, .key_to_protkey = cca_key2protkey, .slowpath_key_to_protkey = cca_slowpath_key2protkey, .gen_key = cca_gen_key, .clr_to_key = cca_clr2key, .verify_key = cca_verifykey, .apqns_for_key = cca_apqns4key, .apqns_for_keytype = cca_apqns4type, }; /* * Module init */ static int __init pkey_cca_init(void) { /* register this module as pkey handler for all the cca stuff */ return pkey_handler_register(&cca_handler); } /* * Module exit */ static void __exit pkey_cca_exit(void) { /* unregister this module as pkey handler */ pkey_handler_unregister(&cca_handler); } module_init(pkey_cca_init); module_exit(pkey_cca_exit);