From: Harald Freudenberger Date: Thu, 22 Aug 2024 09:32:20 +0000 (+0200) Subject: s390/pkey: Add slowpath function to CCA and EP11 handler X-Git-Url: https://git.kernel.dk/?a=commitdiff_plain;h=2fc401b94434ae97d1c9c1283fcf816c1ad9457c;p=linux-2.6-block.git s390/pkey: Add slowpath function to CCA and EP11 handler For some keys there exists an alternative but usually slower path to convert the key material into a protected key. This patch introduces a new handler function slowpath_key_to_protkey() which provides this alternate path for the CCA and EP11 handler code. With that even the knowledge about how and when this can be used within the pkey API code can be removed. So now the pkey API just tries the primary way and if that fails simple tries the alternative way. Signed-off-by: Harald Freudenberger Reviewed-by: Holger Dengler Signed-off-by: Vasily Gorbik --- diff --git a/drivers/s390/crypto/pkey_api.c b/drivers/s390/crypto/pkey_api.c index c59051ab1cfb..d6c5e5ae915b 100644 --- a/drivers/s390/crypto/pkey_api.c +++ b/drivers/s390/crypto/pkey_api.c @@ -22,102 +22,29 @@ /* * Helper functions */ - -static int key2protkey_fallback(const struct clearkeytoken *t, - u8 *protkey, u32 *protkeylen, u32 *protkeytype) -{ - size_t tmpbuflen = max_t(size_t, SECKEYBLOBSIZE, MAXEP11AESKEYBLOBSIZE); - u32 keysize, keybitsize, tmplen; - u8 *tmpbuf = NULL; - int i, rc; - - /* As of now only for AES keys a fallback is available */ - - keysize = pkey_keytype_aes_to_size(t->keytype); - if (!keysize) { - PKEY_DBF_ERR("%s unknown/unsupported keytype %u\n", - __func__, t->keytype); - return -EINVAL; - } - if (t->len != keysize) { - PKEY_DBF_ERR("%s clear key AES token: invalid key len %u\n", - __func__, t->len); - return -EINVAL; - } - keybitsize = 8 * keysize; - - /* alloc tmp key buffer */ - tmpbuf = kmalloc(tmpbuflen, GFP_ATOMIC); - if (!tmpbuf) - return -ENOMEM; - - /* try two times in case of failure */ - for (i = 0, rc = -ENODEV; i < 2 && rc; i++) { - - /* CCA secure key way */ - tmplen = tmpbuflen; - rc = pkey_handler_clr_to_key(NULL, 0, - t->keytype, PKEY_TYPE_CCA_DATA, - keybitsize, 0, - t->clearkey, t->len, - tmpbuf, &tmplen, NULL); - pr_debug("clr_to_key()=%d\n", rc); - if (rc) - goto try_via_ep11; - rc = pkey_handler_key_to_protkey(NULL, 0, - tmpbuf, tmplen, - protkey, protkeylen, - protkeytype); - pr_debug("key_to_protkey()=%d\n", rc); - if (!rc) - break; - -try_via_ep11: - /* the CCA way failed, try via EP11 */ - tmplen = tmpbuflen; - rc = pkey_handler_clr_to_key(NULL, 0, - t->keytype, PKEY_TYPE_EP11_AES, - keybitsize, 0, - t->clearkey, t->len, - tmpbuf, &tmplen, NULL); - pr_debug("clr_to_key()=%d\n", rc); - if (rc) - continue; - rc = pkey_handler_key_to_protkey(NULL, 0, - tmpbuf, tmplen, - protkey, protkeylen, - protkeytype); - pr_debug("key_to_protkey()=%d\n", rc); - } - - kfree(tmpbuf); - - return rc; -} - static int key2protkey(const struct pkey_apqn *apqns, size_t nr_apqns, const u8 *key, size_t keylen, u8 *protkey, u32 *protkeylen, u32 *protkeytype) { - struct keytoken_header *hdr = (struct keytoken_header *)key; - int i, rc; - - /* retry two times */ - for (rc = -ENODEV, i = 0; rc && i < 2; i++) { - /* First try the direct way */ - rc = pkey_handler_key_to_protkey(apqns, nr_apqns, - key, keylen, - protkey, protkeylen, - protkeytype); - /* For some clear key tokens there exists a fallback way */ - if (rc && - hdr->type == TOKTYPE_NON_CCA && - hdr->version == TOKVER_CLEAR_KEY) - rc = key2protkey_fallback((struct clearkeytoken *)key, - protkey, protkeylen, - protkeytype); + int rc; + + /* try the direct way */ + rc = pkey_handler_key_to_protkey(apqns, nr_apqns, + key, keylen, + protkey, protkeylen, + protkeytype); + + /* if this did not work, try the slowpath way */ + if (rc == -ENODEV) { + rc = pkey_handler_slowpath_key_to_protkey(apqns, nr_apqns, + key, keylen, + protkey, protkeylen, + protkeytype); + if (rc) + rc = -ENODEV; } + pr_debug("rc=%d\n", rc); return rc; } diff --git a/drivers/s390/crypto/pkey_base.c b/drivers/s390/crypto/pkey_base.c index e7abc32ca5f9..976b9ddfbe15 100644 --- a/drivers/s390/crypto/pkey_base.c +++ b/drivers/s390/crypto/pkey_base.c @@ -167,6 +167,46 @@ int pkey_handler_key_to_protkey(const struct pkey_apqn *apqns, size_t nr_apqns, } EXPORT_SYMBOL(pkey_handler_key_to_protkey); +/* + * This handler invocation is special as there may be more than + * one handler providing support for the very same key (type). + * And the handler may not respond true on is_supported_key(), + * so simple try and check return value here. + */ +int pkey_handler_slowpath_key_to_protkey(const struct pkey_apqn *apqns, + size_t nr_apqns, + const u8 *key, u32 keylen, + u8 *protkey, u32 *protkeylen, + u32 *protkeytype) +{ + const struct pkey_handler *h, *htmp[10]; + int i, n = 0, rc = -ENODEV; + + rcu_read_lock(); + list_for_each_entry_rcu(h, &handler_list, list) { + if (!try_module_get(h->module)) + continue; + if (h->slowpath_key_to_protkey && n < ARRAY_SIZE(htmp)) + htmp[n++] = h; + else + module_put(h->module); + } + rcu_read_unlock(); + + for (i = 0; i < n; i++) { + h = htmp[i]; + if (rc) + rc = h->slowpath_key_to_protkey(apqns, nr_apqns, + key, keylen, + protkey, protkeylen, + protkeytype); + module_put(h->module); + } + + return rc; +} +EXPORT_SYMBOL(pkey_handler_slowpath_key_to_protkey); + int pkey_handler_gen_key(const struct pkey_apqn *apqns, size_t nr_apqns, u32 keytype, u32 keysubtype, u32 keybitsize, u32 flags, diff --git a/drivers/s390/crypto/pkey_base.h b/drivers/s390/crypto/pkey_base.h index 7f97c6e598da..41fd69e7c66e 100644 --- a/drivers/s390/crypto/pkey_base.h +++ b/drivers/s390/crypto/pkey_base.h @@ -113,6 +113,11 @@ struct pkey_handler { int (*key_to_protkey)(const struct pkey_apqn *apqns, size_t nr_apqns, const u8 *key, u32 keylen, u8 *protkey, u32 *protkeylen, u32 *protkeytype); + int (*slowpath_key_to_protkey)(const struct pkey_apqn *apqns, + size_t nr_apqns, + const u8 *key, u32 keylen, + u8 *protkey, u32 *protkeylen, + u32 *protkeytype); int (*gen_key)(const struct pkey_apqn *apqns, size_t nr_apqns, u32 keytype, u32 keysubtype, u32 keybitsize, u32 flags, @@ -148,6 +153,11 @@ void pkey_handler_put(const struct pkey_handler *handler); int pkey_handler_key_to_protkey(const struct pkey_apqn *apqns, size_t nr_apqns, const u8 *key, u32 keylen, u8 *protkey, u32 *protkeylen, u32 *protkeytype); +int pkey_handler_slowpath_key_to_protkey(const struct pkey_apqn *apqns, + size_t nr_apqns, + const u8 *key, u32 keylen, + u8 *protkey, u32 *protkeylen, + u32 *protkeytype); int pkey_handler_gen_key(const struct pkey_apqn *apqns, size_t nr_apqns, u32 keytype, u32 keysubtype, u32 keybitsize, u32 flags, diff --git a/drivers/s390/crypto/pkey_cca.c b/drivers/s390/crypto/pkey_cca.c index ba2ae253b2ba..937051381720 100644 --- a/drivers/s390/crypto/pkey_cca.c +++ b/drivers/s390/crypto/pkey_cca.c @@ -541,17 +541,70 @@ out: 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, - .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 = 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, }; /* diff --git a/drivers/s390/crypto/pkey_ep11.c b/drivers/s390/crypto/pkey_ep11.c index 624e55195d93..f42d397a9cb6 100644 --- a/drivers/s390/crypto/pkey_ep11.c +++ b/drivers/s390/crypto/pkey_ep11.c @@ -490,17 +490,70 @@ out: 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, - .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 = 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, }; /*