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