285 lines
6.2 KiB
C
285 lines
6.2 KiB
C
// 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);
|