]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
s390/pkey: Add new pkey handler module pkey-uv
authorHarald Freudenberger <freude@linux.ibm.com>
Fri, 25 Oct 2024 10:34:32 +0000 (12:34 +0200)
committerHeiko Carstens <hca@linux.ibm.com>
Tue, 29 Oct 2024 10:17:18 +0000 (11:17 +0100)
This new pkey handler module supports the conversion of
Ultravisor retrievable secrets to protected keys.
The new module pkey-uv.ko is able to retrieve and verify
protected keys backed up by the Ultravisor layer which is
only available within protected execution environment.

The module is only automatically loaded if there is the
UV CPU feature flagged as available. Additionally on module
init there is a check for protected execution environment
and for UV supporting retrievable secrets. Also if the kernel
is not running as a protected execution guest, the module
unloads itself with errno ENODEV.

The pkey UV module currently supports these Ultravisor
secrets and is able to retrieve a protected key for these
UV secret types:
  - UV_SECRET_AES_128
  - UV_SECRET_AES_192
  - UV_SECRET_AES_256
  - UV_SECRET_AES_XTS_128
  - UV_SECRET_AES_XTS_256
  - UV_SECRET_HMAC_SHA_256
  - UV_SECRET_HMAC_SHA_512
  - UV_SECRET_ECDSA_P256
  - UV_SECRET_ECDSA_P384
  - UV_SECRET_ECDSA_P521
  - UV_SECRET_ECDSA_ED25519
  - UV_SECRET_ECDSA_ED448

Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
Reviewed-by: Holger Dengler <dengler@linux.ibm.com>
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
arch/s390/configs/debug_defconfig
arch/s390/configs/defconfig
arch/s390/include/uapi/asm/pkey.h
drivers/crypto/Kconfig
drivers/s390/crypto/Makefile
drivers/s390/crypto/pkey_base.c
drivers/s390/crypto/pkey_uv.c [new file with mode: 0644]

index 9b57add02cd5c4c4ec4fb391a8fd1915a0d3ed00..1783f491c9e6831f4e1fc1ab409b70390ad07bc9 100644 (file)
@@ -801,6 +801,7 @@ CONFIG_PKEY=m
 CONFIG_PKEY_CCA=m
 CONFIG_PKEY_EP11=m
 CONFIG_PKEY_PCKMO=m
+CONFIG_PKEY_UV=m
 CONFIG_CRYPTO_PAES_S390=m
 CONFIG_CRYPTO_DEV_VIRTIO=m
 CONFIG_SYSTEM_BLACKLIST_KEYRING=y
index df4addd1834ab23c2713ccd7f8ffe488993be5a4..196745bafb2b73a3c389845338e83563b133f0b2 100644 (file)
@@ -787,6 +787,7 @@ CONFIG_PKEY=m
 CONFIG_PKEY_CCA=m
 CONFIG_PKEY_EP11=m
 CONFIG_PKEY_PCKMO=m
+CONFIG_PKEY_UV=m
 CONFIG_CRYPTO_PAES_S390=m
 CONFIG_CRYPTO_DEV_VIRTIO=m
 CONFIG_SYSTEM_BLACKLIST_KEYRING=y
index 0e266ef714ff9a4d00f76ac15bad0402494227e1..ca42e941675d4c2f42c56b85f5562049180242a1 100644 (file)
@@ -55,6 +55,7 @@ enum pkey_key_type {
        PKEY_TYPE_EP11_AES   = (__u32)6,
        PKEY_TYPE_EP11_ECC   = (__u32)7,
        PKEY_TYPE_PROTKEY    = (__u32)8,
+       PKEY_TYPE_UVSECRET   = (__u32)9,
 };
 
 /* the newer ioctls use a pkey_key_size enum for key size information */
index 08b1238bcd7b371f51bbc1cdc386c74bcf2ec203..0a9cdd31cbd9f99dc64e3d11cb9f8b3ed0586e69 100644 (file)
@@ -95,6 +95,9 @@ config PKEY
            loaded when a CEX crypto card is available.
          - A pkey EP11 kernel module (pkey-ep11.ko) which is automatically
            loaded when a CEX crypto card is available.
+         - A pkey UV kernel module (pkey-uv.ko) which is automatically
+           loaded when the Ultravisor feature is available within a
+           protected execution environment.
 
          Select this option if you want to enable the kernel and userspace
          API for protected key handling.
@@ -152,6 +155,24 @@ config PKEY_PCKMO
          this option unless you are sure you never need to derive protected
          keys from clear key values directly via PCKMO.
 
+config PKEY_UV
+       tristate "PKEY UV support handler"
+       depends on PKEY
+       depends on S390_UV_UAPI
+       help
+         This is the PKEY Ultravisor support handler for deriving protected
+         keys from secrets stored within the Ultravisor (UV).
+
+         This module works together with the UV device and supports the
+         retrieval of protected keys from secrets stored within the
+         UV firmware layer. This service is only available within
+         a protected execution guest and thus this module will fail upon
+         modprobe if no protected execution environment is detected.
+
+         Enable this option if you intend to run this kernel with an KVM
+         guest with protected execution and you want to use UV retrievable
+         secrets via PKEY API.
+
 config CRYPTO_PAES_S390
        tristate "PAES cipher algorithms"
        depends on S390
index c88b6e0718470842704be955ae1ba593ac5181b3..e83c6603c8587d68537d9248000d90d4aa3ce697 100644 (file)
@@ -29,6 +29,10 @@ obj-$(CONFIG_PKEY_EP11) += pkey-ep11.o
 pkey-pckmo-objs := pkey_pckmo.o
 obj-$(CONFIG_PKEY_PCKMO) += pkey-pckmo.o
 
+# pkey uv handler module
+pkey-uv-objs := pkey_uv.o
+obj-$(CONFIG_PKEY_UV) += pkey-uv.o
+
 # adjunct processor matrix
 vfio_ap-objs := vfio_ap_drv.o vfio_ap_ops.o
 obj-$(CONFIG_VFIO_AP) += vfio_ap.o
index 2fc48214336d2a0bbc0f543d461ceeb1ace8dbf2..64a376501d265933275dc7d07af6a31dfdca26c8 100644 (file)
@@ -312,6 +312,9 @@ void pkey_handler_request_modules(void)
 #endif
 #if IS_MODULE(CONFIG_PKEY_PCKMO)
                "pkey_pckmo",
+#endif
+#if IS_MODULE(CONFIG_PKEY_UV)
+               "pkey_uv",
 #endif
        };
        int i;
diff --git a/drivers/s390/crypto/pkey_uv.c b/drivers/s390/crypto/pkey_uv.c
new file mode 100644 (file)
index 0000000..805817b
--- /dev/null
@@ -0,0 +1,284 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  pkey uv specific code
+ *
+ *  Copyright IBM Corp. 2024
+ */
+
+#define KMSG_COMPONENT "pkey"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/cpufeature.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <asm/uv.h>
+
+#include "zcrypt_ccamisc.h"
+#include "pkey_base.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("IBM Corporation");
+MODULE_DESCRIPTION("s390 protected key UV handler");
+
+/*
+ * UV secret token struct and defines.
+ */
+
+#define TOKVER_UV_SECRET 0x09
+
+struct uvsecrettoken {
+       u8  type;               /* 0x00 = TOKTYPE_NON_CCA */
+       u8  res0[3];
+       u8  version;            /* 0x09 = TOKVER_UV_SECRET */
+       u8  res1[3];
+       u16 secret_type;        /* one of enum uv_secret_types from uv.h */
+       u16 secret_len;         /* length in bytes of the secret */
+       u8  secret_id[UV_SECRET_ID_LEN]; /* the secret id for this secret */
+} __packed;
+
+/*
+ * Check key blob for known and supported UV key.
+ */
+static bool is_uv_key(const u8 *key, u32 keylen)
+{
+       struct uvsecrettoken *t = (struct uvsecrettoken *)key;
+
+       if (keylen < sizeof(*t))
+               return false;
+
+       switch (t->type) {
+       case TOKTYPE_NON_CCA:
+               switch (t->version) {
+               case TOKVER_UV_SECRET:
+                       switch (t->secret_type) {
+                       case UV_SECRET_AES_128:
+                       case UV_SECRET_AES_192:
+                       case UV_SECRET_AES_256:
+                       case UV_SECRET_AES_XTS_128:
+                       case UV_SECRET_AES_XTS_256:
+                       case UV_SECRET_HMAC_SHA_256:
+                       case UV_SECRET_HMAC_SHA_512:
+                       case UV_SECRET_ECDSA_P256:
+                       case UV_SECRET_ECDSA_P384:
+                       case UV_SECRET_ECDSA_P521:
+                       case UV_SECRET_ECDSA_ED25519:
+                       case UV_SECRET_ECDSA_ED448:
+                               return true;
+                       default:
+                               return false;
+                       }
+               default:
+                       return false;
+               }
+       default:
+               return false;
+       }
+}
+
+static bool is_uv_keytype(enum pkey_key_type keytype)
+{
+       switch (keytype) {
+       case PKEY_TYPE_UVSECRET:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static int retrieve_secret(const u8 secret_id[UV_SECRET_ID_LEN],
+                          u16 *secret_type, u8 *buf, u32 *buflen)
+{
+       struct uv_secret_list_item_hdr secret_meta_data;
+       int rc;
+
+       rc = uv_get_secret_metadata(secret_id, &secret_meta_data);
+       if (rc)
+               return rc;
+
+       if (*buflen < secret_meta_data.length)
+               return -EINVAL;
+
+       rc = uv_retrieve_secret(secret_meta_data.index,
+                               buf, secret_meta_data.length);
+       if (rc)
+               return rc;
+
+       *secret_type = secret_meta_data.type;
+       *buflen = secret_meta_data.length;
+
+       return 0;
+}
+
+static int uv_get_size_and_type(u16 secret_type, u32 *pkeysize, u32 *pkeytype)
+{
+       int rc = 0;
+
+       switch (secret_type) {
+       case UV_SECRET_AES_128:
+               *pkeysize = 16 + AES_WK_VP_SIZE;
+               *pkeytype = PKEY_KEYTYPE_AES_128;
+               break;
+       case UV_SECRET_AES_192:
+               *pkeysize = 24 + AES_WK_VP_SIZE;
+               *pkeytype = PKEY_KEYTYPE_AES_192;
+               break;
+       case UV_SECRET_AES_256:
+               *pkeysize = 32 + AES_WK_VP_SIZE;
+               *pkeytype = PKEY_KEYTYPE_AES_256;
+               break;
+       case UV_SECRET_AES_XTS_128:
+               *pkeysize = 16 + 16 + AES_WK_VP_SIZE;
+               *pkeytype = PKEY_KEYTYPE_AES_XTS_128;
+               break;
+       case UV_SECRET_AES_XTS_256:
+               *pkeysize = 32 + 32 + AES_WK_VP_SIZE;
+               *pkeytype = PKEY_KEYTYPE_AES_XTS_256;
+               break;
+       case UV_SECRET_HMAC_SHA_256:
+               *pkeysize = 64 + AES_WK_VP_SIZE;
+               *pkeytype = PKEY_KEYTYPE_HMAC_512;
+               break;
+       case UV_SECRET_HMAC_SHA_512:
+               *pkeysize = 128 + AES_WK_VP_SIZE;
+               *pkeytype = PKEY_KEYTYPE_HMAC_1024;
+               break;
+       case UV_SECRET_ECDSA_P256:
+               *pkeysize = 32 + AES_WK_VP_SIZE;
+               *pkeytype = PKEY_KEYTYPE_ECC_P256;
+               break;
+       case UV_SECRET_ECDSA_P384:
+               *pkeysize = 48 + AES_WK_VP_SIZE;
+               *pkeytype = PKEY_KEYTYPE_ECC_P384;
+               break;
+       case UV_SECRET_ECDSA_P521:
+               *pkeysize = 80 + AES_WK_VP_SIZE;
+               *pkeytype = PKEY_KEYTYPE_ECC_P521;
+               break;
+       case UV_SECRET_ECDSA_ED25519:
+               *pkeysize = 32 + AES_WK_VP_SIZE;
+               *pkeytype = PKEY_KEYTYPE_ECC_ED25519;
+               break;
+       case UV_SECRET_ECDSA_ED448:
+               *pkeysize = 64 + AES_WK_VP_SIZE;
+               *pkeytype = PKEY_KEYTYPE_ECC_ED448;
+               break;
+       default:
+               rc = -EINVAL;
+       }
+
+       return rc;
+}
+
+static int uv_key2protkey(const struct pkey_apqn *_apqns __always_unused,
+                         size_t _nr_apqns __always_unused,
+                         const u8 *key, u32 keylen,
+                         u8 *protkey, u32 *protkeylen, u32 *keyinfo)
+{
+       struct uvsecrettoken *t = (struct uvsecrettoken *)key;
+       u32 pkeysize, pkeytype;
+       u16 secret_type;
+       int rc;
+
+       rc = uv_get_size_and_type(t->secret_type, &pkeysize, &pkeytype);
+       if (rc)
+               goto out;
+
+       if (*protkeylen < pkeysize) {
+               PKEY_DBF_ERR("%s prot key buffer size too small: %u < %u\n",
+                            __func__, *protkeylen, pkeysize);
+               rc = -EINVAL;
+               goto out;
+       }
+
+       rc = retrieve_secret(t->secret_id, &secret_type, protkey, protkeylen);
+       if (rc) {
+               PKEY_DBF_ERR("%s retrieve_secret() failed with %d\n",
+                            __func__, rc);
+               goto out;
+       }
+       if (secret_type != t->secret_type) {
+               PKEY_DBF_ERR("%s retrieved secret type %u != expected type %u\n",
+                            __func__, secret_type, t->secret_type);
+               rc = -EINVAL;
+               goto out;
+       }
+
+       if (keyinfo)
+               *keyinfo = pkeytype;
+
+out:
+       pr_debug("rc=%d\n", rc);
+       return rc;
+}
+
+static int uv_verifykey(const u8 *key, u32 keylen,
+                       u16 *_card __always_unused,
+                       u16 *_dom __always_unused,
+                       u32 *keytype, u32 *keybitsize, u32 *flags)
+{
+       struct uvsecrettoken *t = (struct uvsecrettoken *)key;
+       struct uv_secret_list_item_hdr secret_meta_data;
+       u32 pkeysize, pkeytype, bitsize;
+       int rc;
+
+       rc = uv_get_size_and_type(t->secret_type, &pkeysize, &pkeytype);
+       if (rc)
+               goto out;
+
+       rc = uv_get_secret_metadata(t->secret_id, &secret_meta_data);
+       if (rc)
+               goto out;
+
+       if (secret_meta_data.type != t->secret_type) {
+               rc = -EINVAL;
+               goto out;
+       }
+
+       /* set keytype; keybitsize and flags are not supported */
+       if (keytype)
+               *keytype = PKEY_TYPE_UVSECRET;
+       if (keybitsize) {
+               bitsize = 8 * pkey_keytype_to_size(pkeytype);
+               *keybitsize = bitsize ?: PKEY_SIZE_UNKNOWN;
+       }
+       if (flags)
+               *flags = pkeytype;
+
+out:
+       pr_debug("rc=%d\n", rc);
+       return rc;
+}
+
+static struct pkey_handler uv_handler = {
+       .module                  = THIS_MODULE,
+       .name                    = "PKEY UV handler",
+       .is_supported_key        = is_uv_key,
+       .is_supported_keytype    = is_uv_keytype,
+       .key_to_protkey          = uv_key2protkey,
+       .verify_key              = uv_verifykey,
+};
+
+/*
+ * Module init
+ */
+static int __init pkey_uv_init(void)
+{
+       if (!is_prot_virt_guest())
+               return -ENODEV;
+
+       if (!test_bit_inv(BIT_UVC_CMD_RETR_SECRET, uv_info.inst_calls_list))
+               return -ENODEV;
+
+       return pkey_handler_register(&uv_handler);
+}
+
+/*
+ * Module exit
+ */
+static void __exit pkey_uv_exit(void)
+{
+       pkey_handler_unregister(&uv_handler);
+}
+
+module_cpu_feature_match(S390_CPU_FEATURE_UV, pkey_uv_init);
+module_exit(pkey_uv_exit);