blob: 805817b1435401d8c05ea2968a22f7bcdabd59c4 [file] [log] [blame]
// 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);