summaryrefslogtreecommitdiff
path: root/drivers/s390/crypto/pkey_ep11.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/crypto/pkey_ep11.c')
-rw-r--r--drivers/s390/crypto/pkey_ep11.c578
1 files changed, 578 insertions, 0 deletions
diff --git a/drivers/s390/crypto/pkey_ep11.c b/drivers/s390/crypto/pkey_ep11.c
new file mode 100644
index 000000000000..f42d397a9cb6
--- /dev/null
+++ b/drivers/s390/crypto/pkey_ep11.c
@@ -0,0 +1,578 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * pkey ep11 specific code
+ *
+ * Copyright IBM Corp. 2024
+ */
+
+#define KMSG_COMPONENT "pkey"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/cpufeature.h>
+
+#include "zcrypt_api.h"
+#include "zcrypt_ccamisc.h"
+#include "zcrypt_ep11misc.h"
+#include "pkey_base.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("IBM Corporation");
+MODULE_DESCRIPTION("s390 protected key EP11 handler");
+
+#if IS_MODULE(CONFIG_PKEY_EP11)
+static struct ap_device_id pkey_ep11_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_ep11_card_ids);
+#endif
+
+/*
+ * Check key blob for known and supported EP11 key.
+ */
+static bool is_ep11_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_NON_CCA:
+ switch (hdr->version) {
+ case TOKVER_EP11_AES:
+ case TOKVER_EP11_AES_WITH_HEADER:
+ case TOKVER_EP11_ECC_WITH_HEADER:
+ return true;
+ default:
+ return false;
+ }
+ default:
+ return false;
+ }
+}
+
+static bool is_ep11_keytype(enum pkey_key_type key_type)
+{
+ switch (key_type) {
+ case PKEY_TYPE_EP11:
+ case PKEY_TYPE_EP11_AES:
+ case PKEY_TYPE_EP11_ECC:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int ep11_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;
+
+ if (keylen < sizeof(struct keytoken_header) || flags == 0)
+ return -EINVAL;
+
+ zcrypt_wait_api_operational();
+
+ if (hdr->type == TOKTYPE_NON_CCA &&
+ (hdr->version == TOKVER_EP11_AES_WITH_HEADER ||
+ hdr->version == TOKVER_EP11_ECC_WITH_HEADER) &&
+ is_ep11_keyblob(key + sizeof(struct ep11kblob_header))) {
+ struct ep11keyblob *kb = (struct ep11keyblob *)
+ (key + sizeof(struct ep11kblob_header));
+ int minhwtype = 0, api = 0;
+
+ if (flags != PKEY_FLAGS_MATCH_CUR_MKVP)
+ return -EINVAL;
+ if (kb->attr & EP11_BLOB_PKEY_EXTRACTABLE) {
+ minhwtype = ZCRYPT_CEX7;
+ api = ap_is_se_guest() ? EP11_API_V6 : EP11_API_V4;
+ }
+ rc = ep11_findcard2(&_apqns, &_nr_apqns, 0xFFFF, 0xFFFF,
+ minhwtype, api, kb->wkvp);
+ if (rc)
+ goto out;
+
+ } else if (hdr->type == TOKTYPE_NON_CCA &&
+ hdr->version == TOKVER_EP11_AES &&
+ is_ep11_keyblob(key)) {
+ struct ep11keyblob *kb = (struct ep11keyblob *)key;
+ int minhwtype = 0, api = 0;
+
+ if (flags != PKEY_FLAGS_MATCH_CUR_MKVP)
+ return -EINVAL;
+ if (kb->attr & EP11_BLOB_PKEY_EXTRACTABLE) {
+ minhwtype = ZCRYPT_CEX7;
+ api = ap_is_se_guest() ? EP11_API_V6 : EP11_API_V4;
+ }
+ rc = ep11_findcard2(&_apqns, &_nr_apqns, 0xFFFF, 0xFFFF,
+ minhwtype, api, kb->wkvp);
+ 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 ep11_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_EP11 ||
+ ktype == PKEY_TYPE_EP11_AES ||
+ ktype == PKEY_TYPE_EP11_ECC) {
+ u8 *wkvp = NULL;
+ int api;
+
+ if (flags & PKEY_FLAGS_MATCH_CUR_MKVP)
+ wkvp = cur_mkvp;
+ api = ap_is_se_guest() ? EP11_API_V6 : EP11_API_V4;
+ rc = ep11_findcard2(&_apqns, &_nr_apqns, 0xFFFF, 0xFFFF,
+ ZCRYPT_CEX7, api, wkvp);
+ if (rc)
+ goto out;
+
+ } else {
+ PKEY_DBF_ERR("%s unknown/unsupported key type %d\n",
+ __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 ep11_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_NON_CCA &&
+ hdr->version == TOKVER_EP11_AES_WITH_HEADER &&
+ is_ep11_keyblob(key + sizeof(struct ep11kblob_header))) {
+ /* EP11 AES key blob with header */
+ if (ep11_check_aes_key_with_hdr(pkey_dbf_info,
+ 3, key, keylen, 1))
+ return -EINVAL;
+ } else if (hdr->type == TOKTYPE_NON_CCA &&
+ hdr->version == TOKVER_EP11_ECC_WITH_HEADER &&
+ is_ep11_keyblob(key + sizeof(struct ep11kblob_header))) {
+ /* EP11 ECC key blob with header */
+ if (ep11_check_ecc_key_with_hdr(pkey_dbf_info,
+ 3, key, keylen, 1))
+ return -EINVAL;
+ } else if (hdr->type == TOKTYPE_NON_CCA &&
+ hdr->version == TOKVER_EP11_AES &&
+ is_ep11_keyblob(key)) {
+ /* EP11 AES key blob with header in session field */
+ if (ep11_check_aes_key(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 = ep11_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_NON_CCA &&
+ hdr->version == TOKVER_EP11_AES_WITH_HEADER &&
+ is_ep11_keyblob(key + sizeof(struct ep11kblob_header))) {
+ rc = ep11_kblob2protkey(apqns[i].card, apqns[i].domain,
+ key, hdr->len, protkey,
+ protkeylen, protkeytype);
+ } else if (hdr->type == TOKTYPE_NON_CCA &&
+ hdr->version == TOKVER_EP11_ECC_WITH_HEADER &&
+ is_ep11_keyblob(key + sizeof(struct ep11kblob_header))) {
+ rc = ep11_kblob2protkey(apqns[i].card, apqns[i].domain,
+ key, hdr->len, protkey,
+ protkeylen, protkeytype);
+ } else if (hdr->type == TOKTYPE_NON_CCA &&
+ hdr->version == TOKVER_EP11_AES &&
+ is_ep11_keyblob(key)) {
+ rc = ep11_kblob2protkey(apqns[i].card, apqns[i].domain,
+ key, hdr->len, protkey,
+ protkeylen, protkeytype);
+ } else {
+ rc = -EINVAL;
+ break;
+ }
+ }
+
+out:
+ kfree(local_apqns);
+ pr_debug("rc=%d\n", rc);
+ return rc;
+}
+
+/*
+ * Generate EP11 secure key.
+ * As of now only EP11 AES secure keys are supported.
+ * keytype is one of the PKEY_KEYTYPE_* constants,
+ * subtype may be PKEY_TYPE_EP11 or PKEY_TYPE_EP11_AES
+ * or 0 (results in subtype PKEY_TYPE_EP11_AES),
+ * keybitsize is the bit size of the key (may be 0 for
+ * keytype PKEY_KEYTYPE_AES_*).
+ */
+static int ep11_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_EP11:
+ case PKEY_TYPE_EP11_AES:
+ 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 = ep11_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++) {
+ rc = ep11_genaeskey(apqns[i].card, apqns[i].domain,
+ keybitsize, flags,
+ keybuf, keybuflen, subtype);
+ }
+
+out:
+ kfree(local_apqns);
+ pr_debug("rc=%d\n", rc);
+ return rc;
+}
+
+/*
+ * Generate EP11 secure key with given clear key value.
+ * As of now only EP11 AES secure keys are supported.
+ * keytype is one of the PKEY_KEYTYPE_* constants,
+ * subtype may be PKEY_TYPE_EP11 or PKEY_TYPE_EP11_AES
+ * or 0 (assumes PKEY_TYPE_EP11_AES then).
+ * keybitsize is the bit size of the key (may be 0 for
+ * keytype PKEY_KEYTYPE_AES_*).
+ */
+static int ep11_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_EP11:
+ case PKEY_TYPE_EP11_AES:
+ 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 = ep11_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++) {
+ rc = ep11_clr2keyblob(apqns[i].card, apqns[i].domain,
+ keybitsize, flags, clrkey,
+ keybuf, keybuflen, subtype);
+ }
+
+out:
+ kfree(local_apqns);
+ pr_debug("rc=%d\n", rc);
+ return rc;
+}
+
+static int ep11_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_NON_CCA &&
+ hdr->version == TOKVER_EP11_AES) {
+ struct ep11keyblob *kb = (struct ep11keyblob *)key;
+ int api;
+
+ rc = ep11_check_aes_key(pkey_dbf_info, 3, key, keylen, 1);
+ if (rc)
+ goto out;
+ *keytype = PKEY_TYPE_EP11;
+ *keybitsize = kb->head.bitlen;
+
+ api = ap_is_se_guest() ? EP11_API_V6 : EP11_API_V4;
+ rc = ep11_findcard2(&apqns, &nr_apqns, *card, *dom,
+ ZCRYPT_CEX7, api,
+ ep11_kb_wkvp(key, keylen));
+ if (rc)
+ goto out;
+
+ *flags = PKEY_FLAGS_MATCH_CUR_MKVP;
+
+ *card = ((struct pkey_apqn *)apqns)->card;
+ *dom = ((struct pkey_apqn *)apqns)->domain;
+
+ } else if (hdr->type == TOKTYPE_NON_CCA &&
+ hdr->version == TOKVER_EP11_AES_WITH_HEADER) {
+ struct ep11kblob_header *kh = (struct ep11kblob_header *)key;
+ int api;
+
+ rc = ep11_check_aes_key_with_hdr(pkey_dbf_info,
+ 3, key, keylen, 1);
+ if (rc)
+ goto out;
+ *keytype = PKEY_TYPE_EP11_AES;
+ *keybitsize = kh->bitlen;
+
+ api = ap_is_se_guest() ? EP11_API_V6 : EP11_API_V4;
+ rc = ep11_findcard2(&apqns, &nr_apqns, *card, *dom,
+ ZCRYPT_CEX7, api,
+ ep11_kb_wkvp(key, keylen));
+ if (rc)
+ goto out;
+
+ *flags = PKEY_FLAGS_MATCH_CUR_MKVP;
+
+ *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. That is done via an intermediate step
+ * which creates an EP11 AES secure key first and then derives
+ * the protected key from this secure key.
+ */
+static int ep11_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(MAXEP11AESKEYBLOBSIZE, GFP_ATOMIC);
+ if (!tmpbuf)
+ return -ENOMEM;
+
+ /* try two times in case of failure */
+ for (i = 0, rc = -ENODEV; i < 2 && rc; i++) {
+ tmplen = MAXEP11AESKEYBLOBSIZE;
+ rc = ep11_clr2key(NULL, 0, t->keytype, PKEY_TYPE_EP11,
+ 8 * keysize, 0, t->clearkey, t->len,
+ tmpbuf, &tmplen, NULL);
+ pr_debug("ep11_clr2key()=%d\n", rc);
+ if (rc)
+ continue;
+ rc = ep11_key2protkey(NULL, 0, tmpbuf, tmplen,
+ protkey, protkeylen, protkeytype);
+ pr_debug("ep11_key2protkey()=%d\n", rc);
+ }
+
+ kfree(tmpbuf);
+ pr_debug("rc=%d\n", rc);
+ return rc;
+}
+
+static struct pkey_handler ep11_handler = {
+ .module = THIS_MODULE,
+ .name = "PKEY EP11 handler",
+ .is_supported_key = is_ep11_key,
+ .is_supported_keytype = is_ep11_keytype,
+ .key_to_protkey = ep11_key2protkey,
+ .slowpath_key_to_protkey = ep11_slowpath_key2protkey,
+ .gen_key = ep11_gen_key,
+ .clr_to_key = ep11_clr2key,
+ .verify_key = ep11_verifykey,
+ .apqns_for_key = ep11_apqns4key,
+ .apqns_for_keytype = ep11_apqns4type,
+};
+
+/*
+ * Module init
+ */
+static int __init pkey_ep11_init(void)
+{
+ /* register this module as pkey handler for all the ep11 stuff */
+ return pkey_handler_register(&ep11_handler);
+}
+
+/*
+ * Module exit
+ */
+static void __exit pkey_ep11_exit(void)
+{
+ /* unregister this module as pkey handler */
+ pkey_handler_unregister(&ep11_handler);
+}
+
+module_init(pkey_ep11_init);
+module_exit(pkey_ep11_exit);