| // 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); |