// SPDX-License-Identifier: GPL-2.0 /* * pkey pckmo specific code * * Copyright IBM Corp. 2024 */ #define KMSG_COMPONENT "pkey" #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt #include #include #include #include #include #include #include "zcrypt_ccamisc.h" #include "pkey_base.h" MODULE_LICENSE("GPL"); MODULE_AUTHOR("IBM Corporation"); MODULE_DESCRIPTION("s390 protected key PCKMO handler"); /* * Check key blob for known and supported here. */ static bool is_pckmo_key(const u8 *key, u32 keylen) { struct keytoken_header *hdr = (struct keytoken_header *)key; struct clearkeytoken *t = (struct clearkeytoken *)key; if (keylen < sizeof(*hdr)) return false; switch (hdr->type) { case TOKTYPE_NON_CCA: switch (hdr->version) { case TOKVER_CLEAR_KEY: if (pkey_keytype_to_size(t->keytype)) return true; return false; case TOKVER_PROTECTED_KEY: return true; default: return false; } default: return false; } } static bool is_pckmo_keytype(enum pkey_key_type keytype) { switch (keytype) { case PKEY_TYPE_PROTKEY: return true; default: return false; } } /* * Create a protected key from a clear key value via PCKMO instruction. */ static int pckmo_clr2protkey(u32 keytype, const u8 *clrkey, u32 clrkeylen, u8 *protkey, u32 *protkeylen, u32 *protkeytype) { /* mask of available pckmo subfunctions */ static cpacf_mask_t pckmo_functions; int keysize, rc = -EINVAL; u8 paramblock[160]; u32 pkeytype = 0; unsigned int fc; switch (keytype) { case PKEY_KEYTYPE_AES_128: fc = CPACF_PCKMO_ENC_AES_128_KEY; break; case PKEY_KEYTYPE_AES_192: fc = CPACF_PCKMO_ENC_AES_192_KEY; break; case PKEY_KEYTYPE_AES_256: fc = CPACF_PCKMO_ENC_AES_256_KEY; break; case PKEY_KEYTYPE_ECC_P256: pkeytype = PKEY_KEYTYPE_ECC; fc = CPACF_PCKMO_ENC_ECC_P256_KEY; break; case PKEY_KEYTYPE_ECC_P384: pkeytype = PKEY_KEYTYPE_ECC; fc = CPACF_PCKMO_ENC_ECC_P384_KEY; break; case PKEY_KEYTYPE_ECC_P521: pkeytype = PKEY_KEYTYPE_ECC; fc = CPACF_PCKMO_ENC_ECC_P521_KEY; break; case PKEY_KEYTYPE_ECC_ED25519: pkeytype = PKEY_KEYTYPE_ECC; fc = CPACF_PCKMO_ENC_ECC_ED25519_KEY; break; case PKEY_KEYTYPE_ECC_ED448: pkeytype = PKEY_KEYTYPE_ECC; fc = CPACF_PCKMO_ENC_ECC_ED448_KEY; break; case PKEY_KEYTYPE_AES_XTS_128: fc = CPACF_PCKMO_ENC_AES_XTS_128_DOUBLE_KEY; break; case PKEY_KEYTYPE_AES_XTS_256: fc = CPACF_PCKMO_ENC_AES_XTS_256_DOUBLE_KEY; break; case PKEY_KEYTYPE_HMAC_512: fc = CPACF_PCKMO_ENC_HMAC_512_KEY; break; case PKEY_KEYTYPE_HMAC_1024: fc = CPACF_PCKMO_ENC_HMAC_1024_KEY; break; default: PKEY_DBF_ERR("%s unknown/unsupported keytype %u\n", __func__, keytype); goto out; } keysize = pkey_keytype_to_size(keytype); pkeytype = pkeytype ?: keytype; if (clrkeylen && clrkeylen < keysize) { PKEY_DBF_ERR("%s clear key size too small: %u < %d\n", __func__, clrkeylen, keysize); goto out; } if (*protkeylen < keysize + AES_WK_VP_SIZE) { PKEY_DBF_ERR("%s prot key buffer size too small: %u < %d\n", __func__, *protkeylen, keysize + AES_WK_VP_SIZE); goto out; } /* Did we already check for PCKMO ? */ if (!pckmo_functions.bytes[0]) { /* no, so check now */ if (!cpacf_query(CPACF_PCKMO, &pckmo_functions)) { PKEY_DBF_ERR("%s cpacf_query() failed\n", __func__); rc = -ENODEV; goto out; } } /* check for the pckmo subfunction we need now */ if (!cpacf_test_func(&pckmo_functions, fc)) { PKEY_DBF_ERR("%s pckmo fc 0x%02x not available\n", __func__, fc); rc = -ENODEV; goto out; } /* prepare param block */ memset(paramblock, 0, sizeof(paramblock)); memcpy(paramblock, clrkey, keysize); /* call the pckmo instruction */ cpacf_pckmo(fc, paramblock); /* copy created protected key to key buffer including the wkvp block */ *protkeylen = keysize + AES_WK_VP_SIZE; memcpy(protkey, paramblock, *protkeylen); *protkeytype = pkeytype; rc = 0; out: pr_debug("rc=%d\n", rc); return rc; } /* * Verify a raw protected key blob. */ static int pckmo_verify_protkey(const u8 *protkey, u32 protkeylen, u32 protkeytype) { u8 clrkey[16] = { 0 }, tmpkeybuf[16 + AES_WK_VP_SIZE]; u32 tmpkeybuflen, tmpkeytype; int keysize, rc = -EINVAL; u8 *wkvp; /* check protkey type and size */ keysize = pkey_keytype_to_size(protkeytype); if (!keysize) { PKEY_DBF_ERR("%s unknown/unsupported keytype %u\n", __func__, protkeytype); goto out; } if (protkeylen < keysize + AES_WK_VP_SIZE) goto out; /* generate a dummy AES 128 protected key */ tmpkeybuflen = sizeof(tmpkeybuf); rc = pckmo_clr2protkey(PKEY_KEYTYPE_AES_128, clrkey, sizeof(clrkey), tmpkeybuf, &tmpkeybuflen, &tmpkeytype); if (rc) goto out; memzero_explicit(tmpkeybuf, 16); wkvp = tmpkeybuf + 16; /* compare WK VP from the temp key with that of the given prot key */ if (memcmp(wkvp, protkey + keysize, AES_WK_VP_SIZE)) { PKEY_DBF_ERR("%s protected key WK VP mismatch\n", __func__); rc = -EKEYREJECTED; goto out; } out: pr_debug("rc=%d\n", rc); return rc; } static int pckmo_key2protkey(const u8 *key, u32 keylen, u8 *protkey, u32 *protkeylen, u32 *protkeytype) { struct keytoken_header *hdr = (struct keytoken_header *)key; int rc = -EINVAL; if (keylen < sizeof(*hdr)) return -EINVAL; if (hdr->type != TOKTYPE_NON_CCA) return -EINVAL; switch (hdr->version) { case TOKVER_PROTECTED_KEY: { struct protkeytoken *t = (struct protkeytoken *)key; u32 keysize; if (keylen < sizeof(*t)) goto out; keysize = pkey_keytype_to_size(t->keytype); if (!keysize) { PKEY_DBF_ERR("%s protected key token: unknown keytype %u\n", __func__, t->keytype); goto out; } switch (t->keytype) { case PKEY_KEYTYPE_AES_128: case PKEY_KEYTYPE_AES_192: case PKEY_KEYTYPE_AES_256: if (t->len != keysize + AES_WK_VP_SIZE || keylen < sizeof(struct protaeskeytoken)) goto out; rc = pckmo_verify_protkey(t->protkey, t->len, t->keytype); if (rc) goto out; break; default: if (t->len != keysize + AES_WK_VP_SIZE || keylen < sizeof(*t) + keysize + AES_WK_VP_SIZE) goto out; break; } memcpy(protkey, t->protkey, t->len); *protkeylen = t->len; *protkeytype = t->keytype; rc = 0; break; } case TOKVER_CLEAR_KEY: { struct clearkeytoken *t = (struct clearkeytoken *)key; u32 keysize; if (keylen < sizeof(*t) || keylen < sizeof(*t) + t->len) goto out; keysize = pkey_keytype_to_size(t->keytype); if (!keysize) { PKEY_DBF_ERR("%s clear key token: unknown keytype %u\n", __func__, t->keytype); goto out; } if (t->len != keysize) { PKEY_DBF_ERR("%s clear key token: invalid key len %u\n", __func__, t->len); goto out; } rc = pckmo_clr2protkey(t->keytype, t->clearkey, t->len, protkey, protkeylen, protkeytype); break; } default: PKEY_DBF_ERR("%s unknown non-CCA token version %d\n", __func__, hdr->version); break; } out: pr_debug("rc=%d\n", rc); return rc; } /* * Generate a random protected key. */ static int pckmo_gen_protkey(u32 keytype, u32 subtype, u8 *protkey, u32 *protkeylen, u32 *protkeytype) { u8 clrkey[128]; int keysize; int rc; keysize = pkey_keytype_to_size(keytype); if (!keysize) { PKEY_DBF_ERR("%s unknown/unsupported keytype %d\n", __func__, keytype); return -EINVAL; } if (subtype != PKEY_TYPE_PROTKEY) { PKEY_DBF_ERR("%s unknown/unsupported subtype %d\n", __func__, subtype); return -EINVAL; } switch (keytype) { case PKEY_KEYTYPE_AES_128: case PKEY_KEYTYPE_AES_192: case PKEY_KEYTYPE_AES_256: case PKEY_KEYTYPE_AES_XTS_128: case PKEY_KEYTYPE_AES_XTS_256: case PKEY_KEYTYPE_HMAC_512: case PKEY_KEYTYPE_HMAC_1024: break; default: PKEY_DBF_ERR("%s unsupported keytype %d\n", __func__, keytype); return -EINVAL; } /* generate a dummy random clear key */ get_random_bytes(clrkey, keysize); /* convert it to a dummy protected key */ rc = pckmo_clr2protkey(keytype, clrkey, keysize, protkey, protkeylen, protkeytype); if (rc) goto out; /* replace the key part of the protected key with random bytes */ get_random_bytes(protkey, keysize); out: pr_debug("rc=%d\n", rc); return rc; } /* * Verify a protected key token blob. */ static int pckmo_verify_key(const u8 *key, u32 keylen) { struct keytoken_header *hdr = (struct keytoken_header *)key; int rc = -EINVAL; if (keylen < sizeof(*hdr)) return -EINVAL; if (hdr->type != TOKTYPE_NON_CCA) return -EINVAL; switch (hdr->version) { case TOKVER_PROTECTED_KEY: { struct protkeytoken *t = (struct protkeytoken *)key; u32 keysize; if (keylen < sizeof(*t)) goto out; keysize = pkey_keytype_to_size(t->keytype); if (!keysize || t->len != keysize + AES_WK_VP_SIZE) goto out; switch (t->keytype) { case PKEY_KEYTYPE_AES_128: case PKEY_KEYTYPE_AES_192: case PKEY_KEYTYPE_AES_256: if (keylen < sizeof(struct protaeskeytoken)) goto out; break; default: if (keylen < sizeof(*t) + keysize + AES_WK_VP_SIZE) goto out; break; } rc = pckmo_verify_protkey(t->protkey, t->len, t->keytype); break; } default: PKEY_DBF_ERR("%s unknown non-CCA token version %d\n", __func__, hdr->version); break; } out: pr_debug("rc=%d\n", rc); return rc; } /* * Wrapper functions used for the pkey handler struct */ static int pkey_pckmo_key2protkey(const struct pkey_apqn *_apqns, size_t _nr_apqns, const u8 *key, u32 keylen, u8 *protkey, u32 *protkeylen, u32 *keyinfo) { return pckmo_key2protkey(key, keylen, protkey, protkeylen, keyinfo); } static int pkey_pckmo_gen_key(const struct pkey_apqn *_apqns, size_t _nr_apqns, u32 keytype, u32 keysubtype, u32 _keybitsize, u32 _flags, u8 *keybuf, u32 *keybuflen, u32 *keyinfo) { return pckmo_gen_protkey(keytype, keysubtype, keybuf, keybuflen, keyinfo); } static int pkey_pckmo_verifykey(const u8 *key, u32 keylen, u16 *_card, u16 *_dom, u32 *_keytype, u32 *_keybitsize, u32 *_flags) { return pckmo_verify_key(key, keylen); } static struct pkey_handler pckmo_handler = { .module = THIS_MODULE, .name = "PKEY PCKMO handler", .is_supported_key = is_pckmo_key, .is_supported_keytype = is_pckmo_keytype, .key_to_protkey = pkey_pckmo_key2protkey, .gen_key = pkey_pckmo_gen_key, .verify_key = pkey_pckmo_verifykey, }; /* * Module init */ static int __init pkey_pckmo_init(void) { cpacf_mask_t func_mask; /* * The pckmo instruction should be available - even if we don't * actually invoke it. This instruction comes with MSA 3 which * is also the minimum level for the kmc instructions which * are able to work with protected keys. */ if (!cpacf_query(CPACF_PCKMO, &func_mask)) return -ENODEV; /* register this module as pkey handler for all the pckmo stuff */ return pkey_handler_register(&pckmo_handler); } /* * Module exit */ static void __exit pkey_pckmo_exit(void) { /* unregister this module as pkey handler */ pkey_handler_unregister(&pckmo_handler); } module_cpu_feature_match(S390_CPU_FEATURE_MSA, pkey_pckmo_init); module_exit(pkey_pckmo_exit);