diff options
Diffstat (limited to 'drivers/s390/crypto/pkey_uv.c')
-rw-r--r-- | drivers/s390/crypto/pkey_uv.c | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/drivers/s390/crypto/pkey_uv.c b/drivers/s390/crypto/pkey_uv.c new file mode 100644 index 000000000000..805817b14354 --- /dev/null +++ b/drivers/s390/crypto/pkey_uv.c @@ -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); |