s390/pkey: Add slowpath function to CCA and EP11 handler
authorHarald Freudenberger <freude@linux.ibm.com>
Thu, 22 Aug 2024 09:32:20 +0000 (11:32 +0200)
committerVasily Gorbik <gor@linux.ibm.com>
Thu, 29 Aug 2024 20:56:34 +0000 (22:56 +0200)
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 <freude@linux.ibm.com>
Reviewed-by: Holger Dengler <dengler@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
drivers/s390/crypto/pkey_api.c
drivers/s390/crypto/pkey_base.c
drivers/s390/crypto/pkey_base.h
drivers/s390/crypto/pkey_cca.c
drivers/s390/crypto/pkey_ep11.c

index c59051ab1cfbfbef51864b415c59980af8f783a1..d6c5e5ae915bdec528f19c60cbbebfdbd5de83e1 100644 (file)
 /*
  * 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;
 }
 
index e7abc32ca5f9bec867279c1e1978462ca2eeea05..976b9ddfbe15273f61a39c496339907eb998f6fd 100644 (file)
@@ -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,
index 7f97c6e598dad135c7799a12c217f1c47ef24e08..41fd69e7c66e6a5a34e48f0b67e8e3267849baa1 100644 (file)
@@ -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,
index ba2ae253b2ba1a7613c2bc5a5617ec7dc94c576c..9370513817209e6a9db149d5bb59f53942726041 100644 (file)
@@ -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,
 };
 
 /*
index 624e55195d93c6ad5c1c61923d8b7c24b2aae743..f42d397a9cb66423dfd14ba3fb8094043ab22628 100644 (file)
@@ -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,
 };
 
 /*