s390/pkey: add support for ecc clear key
authorHarald Freudenberger <freude@linux.ibm.com>
Sat, 1 Apr 2023 16:26:06 +0000 (18:26 +0200)
committerAlexander Gordeev <agordeev@linux.ibm.com>
Thu, 1 Jun 2023 15:10:21 +0000 (17:10 +0200)
Add support for a new 'non CCA clear key token' with these
ECC clear keys supported:

- ECC P256
- ECC P384
- ECC P521
- ECC ED25519
- ECC ED448

This makes it possible to derive a protected key from this
ECC clear key input via PKEY_KBLOB2PROTK3 ioctl. As of now
the only way to derive protected keys from these clear key
tokens is via PCKMO instruction. For AES keys an alternate
path via creating a secure key from the clear key and then
derive a protected key from the secure key exists. This
alternate path is not implemented for ECC keys as it would
require to rearrange and maybe recalculate the clear key
material for input to derive an CCA or EP11 ECC secure key.

Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
Reviewed-by: Holger Dengler <dengler@linux.ibm.com>
Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com>
arch/s390/include/asm/cpacf.h
arch/s390/include/uapi/asm/pkey.h
drivers/s390/crypto/pkey_api.c

index 646b12981f2080ce410a65f510a368e657990105..b378e2b57ad875cd9a13f823aa5ac9559cd67e95 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * CP Assist for Cryptographic Functions (CPACF)
  *
- * Copyright IBM Corp. 2003, 2017
+ * Copyright IBM Corp. 2003, 2023
  * Author(s): Thomas Spatzier
  *           Jan Glauber
  *           Harald Freudenberger (freude@de.ibm.com)
 #define CPACF_PCKMO_ENC_AES_128_KEY    0x12
 #define CPACF_PCKMO_ENC_AES_192_KEY    0x13
 #define CPACF_PCKMO_ENC_AES_256_KEY    0x14
+#define CPACF_PCKMO_ENC_ECC_P256_KEY   0x20
+#define CPACF_PCKMO_ENC_ECC_P384_KEY   0x21
+#define CPACF_PCKMO_ENC_ECC_P521_KEY   0x22
+#define CPACF_PCKMO_ENC_ECC_ED25519_KEY        0x28
+#define CPACF_PCKMO_ENC_ECC_ED448_KEY  0x29
 
 /*
  * Function codes for the PRNO (PERFORM RANDOM NUMBER OPERATION)
index 924b876f992c15e0efc4b8cf7094183987cc8bc3..f7bae1c63bd614ad5aff8a38c7babc03cf1fa6ca 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * Userspace interface to the pkey device driver
  *
- * Copyright IBM Corp. 2017, 2019
+ * Copyright IBM Corp. 2017, 2023
  *
  * Author: Harald Freudenberger <freude@de.ibm.com>
  *
 #define MINKEYBLOBSIZE SECKEYBLOBSIZE
 
 /* defines for the type field within the pkey_protkey struct */
-#define PKEY_KEYTYPE_AES_128                 1
-#define PKEY_KEYTYPE_AES_192                 2
-#define PKEY_KEYTYPE_AES_256                 3
-#define PKEY_KEYTYPE_ECC                     4
+#define PKEY_KEYTYPE_AES_128           1
+#define PKEY_KEYTYPE_AES_192           2
+#define PKEY_KEYTYPE_AES_256           3
+#define PKEY_KEYTYPE_ECC               4
+#define PKEY_KEYTYPE_ECC_P256          5
+#define PKEY_KEYTYPE_ECC_P384          6
+#define PKEY_KEYTYPE_ECC_P521          7
+#define PKEY_KEYTYPE_ECC_ED25519       8
+#define PKEY_KEYTYPE_ECC_ED448         9
 
 /* the newer ioctls use a pkey_key_type enum for type information */
 enum pkey_key_type {
index 9d2af01204ea75700c2dfffac57db6cf50013fd6..e58bfd2253231d93203d8fe09b2a926bc7d85cd9 100644 (file)
@@ -2,7 +2,8 @@
 /*
  *  pkey device driver
  *
- *  Copyright IBM Corp. 2017,2019
+ *  Copyright IBM Corp. 2017, 2023
+ *
  *  Author(s): Harald Freudenberger
  */
 
@@ -32,6 +33,7 @@ MODULE_AUTHOR("IBM Corporation");
 MODULE_DESCRIPTION("s390 protected key interface");
 
 #define KEYBLOBBUFSIZE 8192    /* key buffer size used for internal processing */
+#define MINKEYBLOBBUFSIZE (sizeof(struct keytoken_header))
 #define PROTKEYBLOBBUFSIZE 256 /* protected key buffer size used internal */
 #define MAXAPQNSINLIST 64      /* max 64 apqns within a apqn list */
 #define AES_WK_VP_SIZE 32      /* Size of WK VP block appended to a prot key */
@@ -72,16 +74,31 @@ struct protaeskeytoken {
 } __packed;
 
 /* inside view of a clear key token (type 0x00 version 0x02) */
-struct clearaeskeytoken {
-       u8  type;        /* 0x00 for PAES specific key tokens */
+struct clearkeytoken {
+       u8  type;       /* 0x00 for PAES specific key tokens */
        u8  res0[3];
-       u8  version;     /* 0x02 for clear AES key token */
+       u8  version;    /* 0x02 for clear key token */
        u8  res1[3];
-       u32 keytype;     /* key type, one of the PKEY_KEYTYPE values */
-       u32 len;         /* bytes actually stored in clearkey[] */
+       u32 keytype;    /* key type, one of the PKEY_KEYTYPE_* values */
+       u32 len;        /* bytes actually stored in clearkey[] */
        u8  clearkey[]; /* clear key value */
 } __packed;
 
+/* helper function which translates the PKEY_KEYTYPE_AES_* to their keysize */
+static inline u32 pkey_keytype_aes_to_size(u32 keytype)
+{
+       switch (keytype) {
+       case PKEY_KEYTYPE_AES_128:
+               return 16;
+       case PKEY_KEYTYPE_AES_192:
+               return 24;
+       case PKEY_KEYTYPE_AES_256:
+               return 32;
+       default:
+               return 0;
+       }
+}
+
 /*
  * Create a protected key from a clear key value via PCKMO instruction.
  */
@@ -91,23 +108,60 @@ static int pkey_clr2protkey(u32 keytype, const u8 *clrkey,
        /* mask of available pckmo subfunctions */
        static cpacf_mask_t pckmo_functions;
 
-       u8 paramblock[64];
+       u8 paramblock[112];
+       u32 pkeytype;
        int keysize;
        long fc;
 
        switch (keytype) {
        case PKEY_KEYTYPE_AES_128:
+               /* 16 byte key, 32 byte aes wkvp, total 48 bytes */
                keysize = 16;
+               pkeytype = keytype;
                fc = CPACF_PCKMO_ENC_AES_128_KEY;
                break;
        case PKEY_KEYTYPE_AES_192:
+               /* 24 byte key, 32 byte aes wkvp, total 56 bytes */
                keysize = 24;
+               pkeytype = keytype;
                fc = CPACF_PCKMO_ENC_AES_192_KEY;
                break;
        case PKEY_KEYTYPE_AES_256:
+               /* 32 byte key, 32 byte aes wkvp, total 64 bytes */
                keysize = 32;
+               pkeytype = keytype;
                fc = CPACF_PCKMO_ENC_AES_256_KEY;
                break;
+       case PKEY_KEYTYPE_ECC_P256:
+               /* 32 byte key, 32 byte aes wkvp, total 64 bytes */
+               keysize = 32;
+               pkeytype = PKEY_KEYTYPE_ECC;
+               fc = CPACF_PCKMO_ENC_ECC_P256_KEY;
+               break;
+       case PKEY_KEYTYPE_ECC_P384:
+               /* 48 byte key, 32 byte aes wkvp, total 80 bytes */
+               keysize = 48;
+               pkeytype = PKEY_KEYTYPE_ECC;
+               fc = CPACF_PCKMO_ENC_ECC_P384_KEY;
+               break;
+       case PKEY_KEYTYPE_ECC_P521:
+               /* 80 byte key, 32 byte aes wkvp, total 112 bytes */
+               keysize = 80;
+               pkeytype = PKEY_KEYTYPE_ECC;
+               fc = CPACF_PCKMO_ENC_ECC_P521_KEY;
+               break;
+       case PKEY_KEYTYPE_ECC_ED25519:
+               /* 32 byte key, 32 byte aes wkvp, total 64 bytes */
+               keysize = 32;
+               pkeytype = PKEY_KEYTYPE_ECC;
+               fc = CPACF_PCKMO_ENC_ECC_ED25519_KEY;
+               break;
+       case PKEY_KEYTYPE_ECC_ED448:
+               /* 64 byte key, 32 byte aes wkvp, total 96 bytes */
+               keysize = 64;
+               pkeytype = PKEY_KEYTYPE_ECC;
+               fc = CPACF_PCKMO_ENC_ECC_ED448_KEY;
+               break;
        default:
                DEBUG_ERR("%s unknown/unsupported keytype %u\n",
                          __func__, keytype);
@@ -142,7 +196,7 @@ static int pkey_clr2protkey(u32 keytype, const u8 *clrkey,
        /* copy created protected key to key buffer including the wkvp block */
        *protkeylen = keysize + AES_WK_VP_SIZE;
        memcpy(protkey, paramblock, *protkeylen);
-       *protkeytype = keytype;
+       *protkeytype = pkeytype;
 
        return 0;
 }
@@ -319,17 +373,8 @@ static int pkey_genprotkey(u32 keytype, u8 *protkey,
        int keysize;
        int rc;
 
-       switch (keytype) {
-       case PKEY_KEYTYPE_AES_128:
-               keysize = 16;
-               break;
-       case PKEY_KEYTYPE_AES_192:
-               keysize = 24;
-               break;
-       case PKEY_KEYTYPE_AES_256:
-               keysize = 32;
-               break;
-       default:
+       keysize = pkey_keytype_aes_to_size(keytype);
+       if (!keysize) {
                DEBUG_ERR("%s unknown/unsupported keytype %d\n", __func__,
                          keytype);
                return -EINVAL;
@@ -404,6 +449,111 @@ static int pkey_verifyprotkey(const u8 *protkey, u32 protkeylen,
        return 0;
 }
 
+/* Helper for pkey_nonccatok2pkey, handles aes clear key token */
+static int nonccatokaes2pkey(const struct clearkeytoken *t,
+                            u8 *protkey, u32 *protkeylen, u32 *protkeytype)
+{
+       size_t tmpbuflen = max_t(size_t, SECKEYBLOBSIZE, MAXEP11AESKEYBLOBSIZE);
+       u8 *tmpbuf = NULL;
+       u32 keysize;
+       int rc;
+
+       keysize = pkey_keytype_aes_to_size(t->keytype);
+       if (!keysize) {
+               DEBUG_ERR("%s unknown/unsupported keytype %u\n",
+                         __func__, t->keytype);
+               return -EINVAL;
+       }
+       if (t->len != keysize) {
+               DEBUG_ERR("%s non clear key aes token: invalid key len %u\n",
+                         __func__, t->len);
+               return -EINVAL;
+       }
+
+       /* try direct way with the PCKMO instruction */
+       rc = pkey_clr2protkey(t->keytype, t->clearkey,
+                             protkey, protkeylen, protkeytype);
+       if (!rc)
+               goto out;
+
+       /* PCKMO failed, so try the CCA secure key way */
+       tmpbuf = kmalloc(tmpbuflen, GFP_ATOMIC);
+       if (!tmpbuf)
+               return -ENOMEM;
+       zcrypt_wait_api_operational();
+       rc = cca_clr2seckey(0xFFFF, 0xFFFF, t->keytype, t->clearkey, tmpbuf);
+       if (rc)
+               goto try_via_ep11;
+       rc = pkey_skey2pkey(tmpbuf,
+                           protkey, protkeylen, protkeytype);
+       if (!rc)
+               goto out;
+
+try_via_ep11:
+       /* if the CCA way also failed, let's try via EP11 */
+       rc = pkey_clr2ep11key(t->clearkey, t->len,
+                             tmpbuf, &tmpbuflen);
+       if (rc)
+               goto failure;
+       rc = pkey_ep11key2pkey(tmpbuf,
+                              protkey, protkeylen, protkeytype);
+       if (!rc)
+               goto out;
+
+failure:
+       DEBUG_ERR("%s unable to build protected key from clear", __func__);
+
+out:
+       kfree(tmpbuf);
+       return rc;
+}
+
+/* Helper for pkey_nonccatok2pkey, handles ecc clear key token */
+static int nonccatokecc2pkey(const struct clearkeytoken *t,
+                            u8 *protkey, u32 *protkeylen, u32 *protkeytype)
+{
+       u32 keylen;
+       int rc;
+
+       switch (t->keytype) {
+       case PKEY_KEYTYPE_ECC_P256:
+               keylen = 32;
+               break;
+       case PKEY_KEYTYPE_ECC_P384:
+               keylen = 48;
+               break;
+       case PKEY_KEYTYPE_ECC_P521:
+               keylen = 80;
+               break;
+       case PKEY_KEYTYPE_ECC_ED25519:
+               keylen = 32;
+               break;
+       case PKEY_KEYTYPE_ECC_ED448:
+               keylen = 64;
+               break;
+       default:
+               DEBUG_ERR("%s unknown/unsupported keytype %u\n",
+                         __func__, t->keytype);
+               return -EINVAL;
+       }
+
+       if (t->len != keylen) {
+               DEBUG_ERR("%s non clear key ecc token: invalid key len %u\n",
+                         __func__, t->len);
+               return -EINVAL;
+       }
+
+       /* only one path possible: via PCKMO instruction */
+       rc = pkey_clr2protkey(t->keytype, t->clearkey,
+                             protkey, protkeylen, protkeytype);
+       if (rc) {
+               DEBUG_ERR("%s unable to build protected key from clear",
+                         __func__);
+       }
+
+       return rc;
+}
+
 /*
  * Transform a non-CCA key token into a protected key
  */
@@ -411,7 +561,6 @@ static int pkey_nonccatok2pkey(const u8 *key, u32 keylen,
                               u8 *protkey, u32 *protkeylen, u32 *protkeytype)
 {
        struct keytoken_header *hdr = (struct keytoken_header *)key;
-       u8 *tmpbuf = NULL;
        int rc = -EINVAL;
 
        switch (hdr->version) {
@@ -430,54 +579,31 @@ static int pkey_nonccatok2pkey(const u8 *key, u32 keylen,
                break;
        }
        case TOKVER_CLEAR_KEY: {
-               struct clearaeskeytoken *t;
-               struct pkey_clrkey ckey;
-               union u_tmpbuf {
-                       u8 skey[SECKEYBLOBSIZE];
-                       u8 ep11key[MAXEP11AESKEYBLOBSIZE];
-               };
-               size_t tmpbuflen = sizeof(union u_tmpbuf);
-
-               if (keylen < sizeof(struct clearaeskeytoken))
-                       goto out;
-               t = (struct clearaeskeytoken *)key;
-               if (keylen != sizeof(*t) + t->len)
-                       goto out;
-               if ((t->keytype == PKEY_KEYTYPE_AES_128 && t->len == 16) ||
-                   (t->keytype == PKEY_KEYTYPE_AES_192 && t->len == 24) ||
-                   (t->keytype == PKEY_KEYTYPE_AES_256 && t->len == 32))
-                       memcpy(ckey.clrkey, t->clearkey, t->len);
-               else
-                       goto out;
-               /* alloc temp key buffer space */
-               tmpbuf = kmalloc(tmpbuflen, GFP_ATOMIC);
-               if (!tmpbuf) {
-                       rc = -ENOMEM;
+               struct clearkeytoken *t = (struct clearkeytoken *)key;
+
+               if (keylen < sizeof(struct clearkeytoken) ||
+                   keylen != sizeof(*t) + t->len)
                        goto out;
-               }
-               /* try direct way with the PCKMO instruction */
-               rc = pkey_clr2protkey(t->keytype, ckey.clrkey,
-                                     protkey, protkeylen, protkeytype);
-               if (rc == 0)
+               switch (t->keytype) {
+               case PKEY_KEYTYPE_AES_128:
+               case PKEY_KEYTYPE_AES_192:
+               case PKEY_KEYTYPE_AES_256:
+                       rc = nonccatokaes2pkey(t, protkey,
+                                              protkeylen, protkeytype);
                        break;
-               /* PCKMO failed, so try the CCA secure key way */
-               zcrypt_wait_api_operational();
-               rc = cca_clr2seckey(0xFFFF, 0xFFFF, t->keytype,
-                                   ckey.clrkey, tmpbuf);
-               if (rc == 0)
-                       rc = pkey_skey2pkey(tmpbuf,
-                                           protkey, protkeylen, protkeytype);
-               if (rc == 0)
+               case PKEY_KEYTYPE_ECC_P256:
+               case PKEY_KEYTYPE_ECC_P384:
+               case PKEY_KEYTYPE_ECC_P521:
+               case PKEY_KEYTYPE_ECC_ED25519:
+               case PKEY_KEYTYPE_ECC_ED448:
+                       rc = nonccatokecc2pkey(t, protkey,
+                                              protkeylen, protkeytype);
                        break;
-               /* if the CCA way also failed, let's try via EP11 */
-               rc = pkey_clr2ep11key(ckey.clrkey, t->len,
-                                     tmpbuf, &tmpbuflen);
-               if (rc == 0)
-                       rc = pkey_ep11key2pkey(tmpbuf,
-                                              protkey, protkeylen, protkeytype);
-               /* now we should really have an protected key */
-               DEBUG_ERR("%s unable to build protected key from clear",
-                         __func__);
+               default:
+                       DEBUG_ERR("%s unknown/unsupported non cca clear key type %u\n",
+                                 __func__, t->keytype);
+                       return -EINVAL;
+               }
                break;
        }
        case TOKVER_EP11_AES: {
@@ -500,11 +626,9 @@ static int pkey_nonccatok2pkey(const u8 *key, u32 keylen,
        default:
                DEBUG_ERR("%s unknown/unsupported non-CCA token version %d\n",
                          __func__, hdr->version);
-               rc = -EINVAL;
        }
 
 out:
-       kfree(tmpbuf);
        return rc;
 }
 
@@ -1149,7 +1273,7 @@ static int pkey_keyblob2pkey3(const struct pkey_apqn *apqns, size_t nr_apqns,
 
 static void *_copy_key_from_user(void __user *ukey, size_t keylen)
 {
-       if (!ukey || keylen < MINKEYBLOBSIZE || keylen > KEYBLOBBUFSIZE)
+       if (!ukey || keylen < MINKEYBLOBBUFSIZE || keylen > KEYBLOBBUFSIZE)
                return ERR_PTR(-EINVAL);
 
        return memdup_user(ukey, keylen);