diff options
-rw-r--r-- | drivers/s390/crypto/pkey_api.c | 107 | ||||
-rw-r--r-- | drivers/s390/crypto/pkey_base.c | 40 | ||||
-rw-r--r-- | drivers/s390/crypto/pkey_base.h | 10 | ||||
-rw-r--r-- | drivers/s390/crypto/pkey_cca.c | 73 | ||||
-rw-r--r-- | drivers/s390/crypto/pkey_ep11.c | 73 |
5 files changed, 193 insertions, 110 deletions
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, }; /* |