629 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			629 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0
 | |
| /*
 | |
|  *  pkey cca specific code
 | |
|  *
 | |
|  *  Copyright IBM Corp. 2024
 | |
|  */
 | |
| 
 | |
| #define KMSG_COMPONENT "pkey"
 | |
| #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
 | |
| 
 | |
| #include <linux/init.h>
 | |
| #include <linux/module.h>
 | |
| #include <linux/cpufeature.h>
 | |
| 
 | |
| #include "zcrypt_ccamisc.h"
 | |
| #include "pkey_base.h"
 | |
| 
 | |
| MODULE_LICENSE("GPL");
 | |
| MODULE_AUTHOR("IBM Corporation");
 | |
| MODULE_DESCRIPTION("s390 protected key CCA handler");
 | |
| 
 | |
| #if IS_MODULE(CONFIG_PKEY_CCA)
 | |
| static struct ap_device_id pkey_cca_card_ids[] = {
 | |
| 	{ .dev_type = AP_DEVICE_TYPE_CEX4 },
 | |
| 	{ .dev_type = AP_DEVICE_TYPE_CEX5 },
 | |
| 	{ .dev_type = AP_DEVICE_TYPE_CEX6 },
 | |
| 	{ .dev_type = AP_DEVICE_TYPE_CEX7 },
 | |
| 	{ .dev_type = AP_DEVICE_TYPE_CEX8 },
 | |
| 	{ /* end of list */ },
 | |
| };
 | |
| MODULE_DEVICE_TABLE(ap, pkey_cca_card_ids);
 | |
| #endif
 | |
| 
 | |
| /*
 | |
|  * Check key blob for known and supported CCA key.
 | |
|  */
 | |
| static bool is_cca_key(const u8 *key, u32 keylen)
 | |
| {
 | |
| 	struct keytoken_header *hdr = (struct keytoken_header *)key;
 | |
| 
 | |
| 	if (keylen < sizeof(*hdr))
 | |
| 		return false;
 | |
| 
 | |
| 	switch (hdr->type) {
 | |
| 	case TOKTYPE_CCA_INTERNAL:
 | |
| 		switch (hdr->version) {
 | |
| 		case TOKVER_CCA_AES:
 | |
| 		case TOKVER_CCA_VLSC:
 | |
| 			return true;
 | |
| 		default:
 | |
| 			return false;
 | |
| 		}
 | |
| 	case TOKTYPE_CCA_INTERNAL_PKA:
 | |
| 		return true;
 | |
| 	default:
 | |
| 		return false;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static bool is_cca_keytype(enum pkey_key_type key_type)
 | |
| {
 | |
| 	switch (key_type) {
 | |
| 	case PKEY_TYPE_CCA_DATA:
 | |
| 	case PKEY_TYPE_CCA_CIPHER:
 | |
| 	case PKEY_TYPE_CCA_ECC:
 | |
| 		return true;
 | |
| 	default:
 | |
| 		return false;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int cca_apqns4key(const u8 *key, u32 keylen, u32 flags,
 | |
| 			 struct pkey_apqn *apqns, size_t *nr_apqns)
 | |
| {
 | |
| 	struct keytoken_header *hdr = (struct keytoken_header *)key;
 | |
| 	u32 _nr_apqns, *_apqns = NULL;
 | |
| 	int rc;
 | |
| 
 | |
| 	if (!flags)
 | |
| 		flags = PKEY_FLAGS_MATCH_CUR_MKVP | PKEY_FLAGS_MATCH_ALT_MKVP;
 | |
| 
 | |
| 	if (keylen < sizeof(struct keytoken_header))
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	zcrypt_wait_api_operational();
 | |
| 
 | |
| 	if (hdr->type == TOKTYPE_CCA_INTERNAL) {
 | |
| 		u64 cur_mkvp = 0, old_mkvp = 0;
 | |
| 		int minhwtype = ZCRYPT_CEX3C;
 | |
| 
 | |
| 		if (hdr->version == TOKVER_CCA_AES) {
 | |
| 			struct secaeskeytoken *t = (struct secaeskeytoken *)key;
 | |
| 
 | |
| 			if (flags & PKEY_FLAGS_MATCH_CUR_MKVP)
 | |
| 				cur_mkvp = t->mkvp;
 | |
| 			if (flags & PKEY_FLAGS_MATCH_ALT_MKVP)
 | |
| 				old_mkvp = t->mkvp;
 | |
| 		} else if (hdr->version == TOKVER_CCA_VLSC) {
 | |
| 			struct cipherkeytoken *t = (struct cipherkeytoken *)key;
 | |
| 
 | |
| 			minhwtype = ZCRYPT_CEX6;
 | |
| 			if (flags & PKEY_FLAGS_MATCH_CUR_MKVP)
 | |
| 				cur_mkvp = t->mkvp0;
 | |
| 			if (flags & PKEY_FLAGS_MATCH_ALT_MKVP)
 | |
| 				old_mkvp = t->mkvp0;
 | |
| 		} else {
 | |
| 			/* unknown CCA internal token type */
 | |
| 			return -EINVAL;
 | |
| 		}
 | |
| 		rc = cca_findcard2(&_apqns, &_nr_apqns, 0xFFFF, 0xFFFF,
 | |
| 				   minhwtype, AES_MK_SET,
 | |
| 				   cur_mkvp, old_mkvp, 1);
 | |
| 		if (rc)
 | |
| 			goto out;
 | |
| 
 | |
| 	} else if (hdr->type == TOKTYPE_CCA_INTERNAL_PKA) {
 | |
| 		struct eccprivkeytoken *t = (struct eccprivkeytoken *)key;
 | |
| 		u64 cur_mkvp = 0, old_mkvp = 0;
 | |
| 
 | |
| 		if (t->secid == 0x20) {
 | |
| 			if (flags & PKEY_FLAGS_MATCH_CUR_MKVP)
 | |
| 				cur_mkvp = t->mkvp;
 | |
| 			if (flags & PKEY_FLAGS_MATCH_ALT_MKVP)
 | |
| 				old_mkvp = t->mkvp;
 | |
| 		} else {
 | |
| 			/* unknown CCA internal 2 token type */
 | |
| 			return -EINVAL;
 | |
| 		}
 | |
| 		rc = cca_findcard2(&_apqns, &_nr_apqns, 0xFFFF, 0xFFFF,
 | |
| 				   ZCRYPT_CEX7, APKA_MK_SET,
 | |
| 				   cur_mkvp, old_mkvp, 1);
 | |
| 		if (rc)
 | |
| 			goto out;
 | |
| 
 | |
| 	} else {
 | |
| 		PKEY_DBF_ERR("%s unknown/unsupported blob type %d version %d\n",
 | |
| 			     __func__, hdr->type, hdr->version);
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	if (apqns) {
 | |
| 		if (*nr_apqns < _nr_apqns)
 | |
| 			rc = -ENOSPC;
 | |
| 		else
 | |
| 			memcpy(apqns, _apqns, _nr_apqns * sizeof(u32));
 | |
| 	}
 | |
| 	*nr_apqns = _nr_apqns;
 | |
| 
 | |
| out:
 | |
| 	kfree(_apqns);
 | |
| 	pr_debug("rc=%d\n", rc);
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| static int cca_apqns4type(enum pkey_key_type ktype,
 | |
| 			  u8 cur_mkvp[32], u8 alt_mkvp[32], u32 flags,
 | |
| 			  struct pkey_apqn *apqns, size_t *nr_apqns)
 | |
| {
 | |
| 	u32 _nr_apqns, *_apqns = NULL;
 | |
| 	int rc;
 | |
| 
 | |
| 	zcrypt_wait_api_operational();
 | |
| 
 | |
| 	if (ktype == PKEY_TYPE_CCA_DATA || ktype == PKEY_TYPE_CCA_CIPHER) {
 | |
| 		u64 cur_mkvp = 0, old_mkvp = 0;
 | |
| 		int minhwtype = ZCRYPT_CEX3C;
 | |
| 
 | |
| 		if (flags & PKEY_FLAGS_MATCH_CUR_MKVP)
 | |
| 			cur_mkvp = *((u64 *)cur_mkvp);
 | |
| 		if (flags & PKEY_FLAGS_MATCH_ALT_MKVP)
 | |
| 			old_mkvp = *((u64 *)alt_mkvp);
 | |
| 		if (ktype == PKEY_TYPE_CCA_CIPHER)
 | |
| 			minhwtype = ZCRYPT_CEX6;
 | |
| 		rc = cca_findcard2(&_apqns, &_nr_apqns, 0xFFFF, 0xFFFF,
 | |
| 				   minhwtype, AES_MK_SET,
 | |
| 				   cur_mkvp, old_mkvp, 1);
 | |
| 		if (rc)
 | |
| 			goto out;
 | |
| 
 | |
| 	} else if (ktype == PKEY_TYPE_CCA_ECC) {
 | |
| 		u64 cur_mkvp = 0, old_mkvp = 0;
 | |
| 
 | |
| 		if (flags & PKEY_FLAGS_MATCH_CUR_MKVP)
 | |
| 			cur_mkvp = *((u64 *)cur_mkvp);
 | |
| 		if (flags & PKEY_FLAGS_MATCH_ALT_MKVP)
 | |
| 			old_mkvp = *((u64 *)alt_mkvp);
 | |
| 		rc = cca_findcard2(&_apqns, &_nr_apqns, 0xFFFF, 0xFFFF,
 | |
| 				   ZCRYPT_CEX7, APKA_MK_SET,
 | |
| 				   cur_mkvp, old_mkvp, 1);
 | |
| 		if (rc)
 | |
| 			goto out;
 | |
| 
 | |
| 	} else {
 | |
| 		PKEY_DBF_ERR("%s unknown/unsupported key type %d",
 | |
| 			     __func__, (int)ktype);
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	if (apqns) {
 | |
| 		if (*nr_apqns < _nr_apqns)
 | |
| 			rc = -ENOSPC;
 | |
| 		else
 | |
| 			memcpy(apqns, _apqns, _nr_apqns * sizeof(u32));
 | |
| 	}
 | |
| 	*nr_apqns = _nr_apqns;
 | |
| 
 | |
| out:
 | |
| 	kfree(_apqns);
 | |
| 	pr_debug("rc=%d\n", rc);
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| static int cca_key2protkey(const struct pkey_apqn *apqns, size_t nr_apqns,
 | |
| 			   const u8 *key, u32 keylen,
 | |
| 			   u8 *protkey, u32 *protkeylen, u32 *protkeytype)
 | |
| {
 | |
| 	struct keytoken_header *hdr = (struct keytoken_header *)key;
 | |
| 	struct pkey_apqn *local_apqns = NULL;
 | |
| 	int i, rc;
 | |
| 
 | |
| 	if (keylen < sizeof(*hdr))
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	if (hdr->type == TOKTYPE_CCA_INTERNAL &&
 | |
| 	    hdr->version == TOKVER_CCA_AES) {
 | |
| 		/* CCA AES data key */
 | |
| 		if (keylen < sizeof(struct secaeskeytoken))
 | |
| 			return -EINVAL;
 | |
| 		if (cca_check_secaeskeytoken(pkey_dbf_info, 3, key, 0))
 | |
| 			return -EINVAL;
 | |
| 	} else if (hdr->type == TOKTYPE_CCA_INTERNAL &&
 | |
| 		   hdr->version == TOKVER_CCA_VLSC) {
 | |
| 		/* CCA AES cipher key */
 | |
| 		if (keylen < hdr->len)
 | |
| 			return -EINVAL;
 | |
| 		if (cca_check_secaescipherkey(pkey_dbf_info,
 | |
| 					      3, key, 0, 1))
 | |
| 			return -EINVAL;
 | |
| 	} else if (hdr->type == TOKTYPE_CCA_INTERNAL_PKA) {
 | |
| 		/* CCA ECC (private) key */
 | |
| 		if (keylen < sizeof(struct eccprivkeytoken))
 | |
| 			return -EINVAL;
 | |
| 		if (cca_check_sececckeytoken(pkey_dbf_info, 3, key, keylen, 1))
 | |
| 			return -EINVAL;
 | |
| 	} else {
 | |
| 		PKEY_DBF_ERR("%s unknown/unsupported blob type %d version %d\n",
 | |
| 			     __func__, hdr->type, hdr->version);
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	zcrypt_wait_api_operational();
 | |
| 
 | |
| 	if (!apqns || (nr_apqns == 1 &&
 | |
| 		       apqns[0].card == 0xFFFF && apqns[0].domain == 0xFFFF)) {
 | |
| 		nr_apqns = MAXAPQNSINLIST;
 | |
| 		local_apqns = kmalloc_array(nr_apqns, sizeof(struct pkey_apqn),
 | |
| 					    GFP_KERNEL);
 | |
| 		if (!local_apqns)
 | |
| 			return -ENOMEM;
 | |
| 		rc = cca_apqns4key(key, keylen, 0, local_apqns, &nr_apqns);
 | |
| 		if (rc)
 | |
| 			goto out;
 | |
| 		apqns = local_apqns;
 | |
| 	}
 | |
| 
 | |
| 	for (rc = -ENODEV, i = 0; rc && i < nr_apqns; i++) {
 | |
| 		if (hdr->type == TOKTYPE_CCA_INTERNAL &&
 | |
| 		    hdr->version == TOKVER_CCA_AES) {
 | |
| 			rc = cca_sec2protkey(apqns[i].card, apqns[i].domain,
 | |
| 					     key, protkey,
 | |
| 					     protkeylen, protkeytype);
 | |
| 		} else if (hdr->type == TOKTYPE_CCA_INTERNAL &&
 | |
| 			   hdr->version == TOKVER_CCA_VLSC) {
 | |
| 			rc = cca_cipher2protkey(apqns[i].card, apqns[i].domain,
 | |
| 						key, protkey,
 | |
| 						protkeylen, protkeytype);
 | |
| 		} else if (hdr->type == TOKTYPE_CCA_INTERNAL_PKA) {
 | |
| 			rc = cca_ecc2protkey(apqns[i].card, apqns[i].domain,
 | |
| 					     key, protkey,
 | |
| 					     protkeylen, protkeytype);
 | |
| 		} else {
 | |
| 			rc = -EINVAL;
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| out:
 | |
| 	kfree(local_apqns);
 | |
| 	pr_debug("rc=%d\n", rc);
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Generate CCA secure key.
 | |
|  * As of now only CCA AES Data or Cipher secure keys are
 | |
|  * supported.
 | |
|  * keytype is one of the PKEY_KEYTYPE_* constants,
 | |
|  * subtype may be 0 or PKEY_TYPE_CCA_DATA or PKEY_TYPE_CCA_CIPHER,
 | |
|  * keybitsize is the bit size of the key (may be 0 for
 | |
|  * keytype PKEY_KEYTYPE_AES_*).
 | |
|  */
 | |
| static int cca_gen_key(const struct pkey_apqn *apqns, size_t nr_apqns,
 | |
| 		       u32 keytype, u32 subtype,
 | |
| 		       u32 keybitsize, u32 flags,
 | |
| 		       u8 *keybuf, u32 *keybuflen, u32 *_keyinfo)
 | |
| {
 | |
| 	struct pkey_apqn *local_apqns = NULL;
 | |
| 	int i, len, rc;
 | |
| 
 | |
| 	/* check keytype, subtype, keybitsize */
 | |
| 	switch (keytype) {
 | |
| 	case PKEY_KEYTYPE_AES_128:
 | |
| 	case PKEY_KEYTYPE_AES_192:
 | |
| 	case PKEY_KEYTYPE_AES_256:
 | |
| 		len = pkey_keytype_aes_to_size(keytype);
 | |
| 		if (keybitsize && keybitsize != 8 * len) {
 | |
| 			PKEY_DBF_ERR("%s unknown/unsupported keybitsize %d\n",
 | |
| 				     __func__, keybitsize);
 | |
| 			return -EINVAL;
 | |
| 		}
 | |
| 		keybitsize = 8 * len;
 | |
| 		switch (subtype) {
 | |
| 		case PKEY_TYPE_CCA_DATA:
 | |
| 		case PKEY_TYPE_CCA_CIPHER:
 | |
| 			break;
 | |
| 		default:
 | |
| 			PKEY_DBF_ERR("%s unknown/unsupported subtype %d\n",
 | |
| 				     __func__, subtype);
 | |
| 			return -EINVAL;
 | |
| 		}
 | |
| 		break;
 | |
| 	default:
 | |
| 		PKEY_DBF_ERR("%s unknown/unsupported keytype %d\n",
 | |
| 			     __func__, keytype);
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	zcrypt_wait_api_operational();
 | |
| 
 | |
| 	if (!apqns || (nr_apqns == 1 &&
 | |
| 		       apqns[0].card == 0xFFFF && apqns[0].domain == 0xFFFF)) {
 | |
| 		nr_apqns = MAXAPQNSINLIST;
 | |
| 		local_apqns = kmalloc_array(nr_apqns, sizeof(struct pkey_apqn),
 | |
| 					    GFP_KERNEL);
 | |
| 		if (!local_apqns)
 | |
| 			return -ENOMEM;
 | |
| 		rc = cca_apqns4type(subtype, NULL, NULL, 0,
 | |
| 				    local_apqns, &nr_apqns);
 | |
| 		if (rc)
 | |
| 			goto out;
 | |
| 		apqns = local_apqns;
 | |
| 	}
 | |
| 
 | |
| 	for (rc = -ENODEV, i = 0; rc && i < nr_apqns; i++) {
 | |
| 		if (subtype == PKEY_TYPE_CCA_CIPHER) {
 | |
| 			rc = cca_gencipherkey(apqns[i].card, apqns[i].domain,
 | |
| 					      keybitsize, flags,
 | |
| 					      keybuf, keybuflen);
 | |
| 		} else {
 | |
| 			/* PKEY_TYPE_CCA_DATA */
 | |
| 			rc = cca_genseckey(apqns[i].card, apqns[i].domain,
 | |
| 					   keybitsize, keybuf);
 | |
| 			*keybuflen = (rc ? 0 : SECKEYBLOBSIZE);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| out:
 | |
| 	kfree(local_apqns);
 | |
| 	pr_debug("rc=%d\n", rc);
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Generate CCA secure key with given clear key value.
 | |
|  * As of now only CCA AES Data or Cipher secure keys are
 | |
|  * supported.
 | |
|  * keytype is one of the PKEY_KEYTYPE_* constants,
 | |
|  * subtype may be 0 or PKEY_TYPE_CCA_DATA or PKEY_TYPE_CCA_CIPHER,
 | |
|  * keybitsize is the bit size of the key (may be 0 for
 | |
|  * keytype PKEY_KEYTYPE_AES_*).
 | |
|  */
 | |
| static int cca_clr2key(const struct pkey_apqn *apqns, size_t nr_apqns,
 | |
| 		       u32 keytype, u32 subtype,
 | |
| 		       u32 keybitsize, u32 flags,
 | |
| 		       const u8 *clrkey, u32 clrkeylen,
 | |
| 		       u8 *keybuf, u32 *keybuflen, u32 *_keyinfo)
 | |
| {
 | |
| 	struct pkey_apqn *local_apqns = NULL;
 | |
| 	int i, len, rc;
 | |
| 
 | |
| 	/* check keytype, subtype, clrkeylen, keybitsize */
 | |
| 	switch (keytype) {
 | |
| 	case PKEY_KEYTYPE_AES_128:
 | |
| 	case PKEY_KEYTYPE_AES_192:
 | |
| 	case PKEY_KEYTYPE_AES_256:
 | |
| 		len = pkey_keytype_aes_to_size(keytype);
 | |
| 		if (keybitsize && keybitsize != 8 * len) {
 | |
| 			PKEY_DBF_ERR("%s unknown/unsupported keybitsize %d\n",
 | |
| 				     __func__, keybitsize);
 | |
| 			return -EINVAL;
 | |
| 		}
 | |
| 		keybitsize = 8 * len;
 | |
| 		if (clrkeylen != len) {
 | |
| 			PKEY_DBF_ERR("%s invalid clear key len %d != %d\n",
 | |
| 				     __func__, clrkeylen, len);
 | |
| 			return -EINVAL;
 | |
| 		}
 | |
| 		switch (subtype) {
 | |
| 		case PKEY_TYPE_CCA_DATA:
 | |
| 		case PKEY_TYPE_CCA_CIPHER:
 | |
| 			break;
 | |
| 		default:
 | |
| 			PKEY_DBF_ERR("%s unknown/unsupported subtype %d\n",
 | |
| 				     __func__, subtype);
 | |
| 			return -EINVAL;
 | |
| 		}
 | |
| 		break;
 | |
| 	default:
 | |
| 		PKEY_DBF_ERR("%s unknown/unsupported keytype %d\n",
 | |
| 			     __func__, keytype);
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	zcrypt_wait_api_operational();
 | |
| 
 | |
| 	if (!apqns || (nr_apqns == 1 &&
 | |
| 		       apqns[0].card == 0xFFFF && apqns[0].domain == 0xFFFF)) {
 | |
| 		nr_apqns = MAXAPQNSINLIST;
 | |
| 		local_apqns = kmalloc_array(nr_apqns, sizeof(struct pkey_apqn),
 | |
| 					    GFP_KERNEL);
 | |
| 		if (!local_apqns)
 | |
| 			return -ENOMEM;
 | |
| 		rc = cca_apqns4type(subtype, NULL, NULL, 0,
 | |
| 				    local_apqns, &nr_apqns);
 | |
| 		if (rc)
 | |
| 			goto out;
 | |
| 		apqns = local_apqns;
 | |
| 	}
 | |
| 
 | |
| 	for (rc = -ENODEV, i = 0; rc && i < nr_apqns; i++) {
 | |
| 		if (subtype == PKEY_TYPE_CCA_CIPHER) {
 | |
| 			rc = cca_clr2cipherkey(apqns[i].card, apqns[i].domain,
 | |
| 					       keybitsize, flags, clrkey,
 | |
| 					       keybuf, keybuflen);
 | |
| 		} else {
 | |
| 			/* PKEY_TYPE_CCA_DATA */
 | |
| 			rc = cca_clr2seckey(apqns[i].card, apqns[i].domain,
 | |
| 					    keybitsize, clrkey, keybuf);
 | |
| 			*keybuflen = (rc ? 0 : SECKEYBLOBSIZE);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| out:
 | |
| 	kfree(local_apqns);
 | |
| 	pr_debug("rc=%d\n", rc);
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| static int cca_verifykey(const u8 *key, u32 keylen,
 | |
| 			 u16 *card, u16 *dom,
 | |
| 			 u32 *keytype, u32 *keybitsize, u32 *flags)
 | |
| {
 | |
| 	struct keytoken_header *hdr = (struct keytoken_header *)key;
 | |
| 	u32 nr_apqns, *apqns = NULL;
 | |
| 	int rc;
 | |
| 
 | |
| 	if (keylen < sizeof(*hdr))
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	zcrypt_wait_api_operational();
 | |
| 
 | |
| 	if (hdr->type == TOKTYPE_CCA_INTERNAL &&
 | |
| 	    hdr->version == TOKVER_CCA_AES) {
 | |
| 		struct secaeskeytoken *t = (struct secaeskeytoken *)key;
 | |
| 
 | |
| 		rc = cca_check_secaeskeytoken(pkey_dbf_info, 3, key, 0);
 | |
| 		if (rc)
 | |
| 			goto out;
 | |
| 		*keytype = PKEY_TYPE_CCA_DATA;
 | |
| 		*keybitsize = t->bitsize;
 | |
| 		rc = cca_findcard2(&apqns, &nr_apqns, *card, *dom,
 | |
| 				   ZCRYPT_CEX3C, AES_MK_SET,
 | |
| 				   t->mkvp, 0, 1);
 | |
| 		if (!rc)
 | |
| 			*flags = PKEY_FLAGS_MATCH_CUR_MKVP;
 | |
| 		if (rc == -ENODEV) {
 | |
| 			rc = cca_findcard2(&apqns, &nr_apqns, *card, *dom,
 | |
| 					   ZCRYPT_CEX3C, AES_MK_SET,
 | |
| 					   0, t->mkvp, 1);
 | |
| 			if (!rc)
 | |
| 				*flags = PKEY_FLAGS_MATCH_ALT_MKVP;
 | |
| 		}
 | |
| 		if (rc)
 | |
| 			goto out;
 | |
| 
 | |
| 		*card = ((struct pkey_apqn *)apqns)->card;
 | |
| 		*dom = ((struct pkey_apqn *)apqns)->domain;
 | |
| 
 | |
| 	} else if (hdr->type == TOKTYPE_CCA_INTERNAL &&
 | |
| 		   hdr->version == TOKVER_CCA_VLSC) {
 | |
| 		struct cipherkeytoken *t = (struct cipherkeytoken *)key;
 | |
| 
 | |
| 		rc = cca_check_secaescipherkey(pkey_dbf_info, 3, key, 0, 1);
 | |
| 		if (rc)
 | |
| 			goto out;
 | |
| 		*keytype = PKEY_TYPE_CCA_CIPHER;
 | |
| 		*keybitsize = PKEY_SIZE_UNKNOWN;
 | |
| 		if (!t->plfver && t->wpllen == 512)
 | |
| 			*keybitsize = PKEY_SIZE_AES_128;
 | |
| 		else if (!t->plfver && t->wpllen == 576)
 | |
| 			*keybitsize = PKEY_SIZE_AES_192;
 | |
| 		else if (!t->plfver && t->wpllen == 640)
 | |
| 			*keybitsize = PKEY_SIZE_AES_256;
 | |
| 		rc = cca_findcard2(&apqns, &nr_apqns, *card, *dom,
 | |
| 				   ZCRYPT_CEX6, AES_MK_SET,
 | |
| 				   t->mkvp0, 0, 1);
 | |
| 		if (!rc)
 | |
| 			*flags = PKEY_FLAGS_MATCH_CUR_MKVP;
 | |
| 		if (rc == -ENODEV) {
 | |
| 			rc = cca_findcard2(&apqns, &nr_apqns, *card, *dom,
 | |
| 					   ZCRYPT_CEX6, AES_MK_SET,
 | |
| 					   0, t->mkvp0, 1);
 | |
| 			if (!rc)
 | |
| 				*flags = PKEY_FLAGS_MATCH_ALT_MKVP;
 | |
| 		}
 | |
| 		if (rc)
 | |
| 			goto out;
 | |
| 
 | |
| 		*card = ((struct pkey_apqn *)apqns)->card;
 | |
| 		*dom = ((struct pkey_apqn *)apqns)->domain;
 | |
| 
 | |
| 	} else {
 | |
| 		/* unknown/unsupported key blob */
 | |
| 		rc = -EINVAL;
 | |
| 	}
 | |
| 
 | |
| out:
 | |
| 	kfree(apqns);
 | |
| 	pr_debug("rc=%d\n", rc);
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * This function provides an alternate but usually slow way
 | |
|  * to convert a 'clear key token' with AES key material into
 | |
|  * a protected key. This is done via an intermediate step
 | |
|  * which creates a CCA AES DATA secure key first and then
 | |
|  * derives the protected key from this secure key.
 | |
|  */
 | |
| static int cca_slowpath_key2protkey(const struct pkey_apqn *apqns,
 | |
| 				    size_t nr_apqns,
 | |
| 				    const u8 *key, u32 keylen,
 | |
| 				    u8 *protkey, u32 *protkeylen,
 | |
| 				    u32 *protkeytype)
 | |
| {
 | |
| 	const struct keytoken_header *hdr = (const struct keytoken_header *)key;
 | |
| 	const struct clearkeytoken *t = (const struct clearkeytoken *)key;
 | |
| 	u32 tmplen, keysize = 0;
 | |
| 	u8 *tmpbuf;
 | |
| 	int i, rc;
 | |
| 
 | |
| 	if (keylen < sizeof(*hdr))
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	if (hdr->type == TOKTYPE_NON_CCA &&
 | |
| 	    hdr->version == TOKVER_CLEAR_KEY)
 | |
| 		keysize = pkey_keytype_aes_to_size(t->keytype);
 | |
| 	if (!keysize || t->len != keysize)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	/* alloc tmp key buffer */
 | |
| 	tmpbuf = kmalloc(SECKEYBLOBSIZE, GFP_ATOMIC);
 | |
| 	if (!tmpbuf)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	/* try two times in case of failure */
 | |
| 	for (i = 0, rc = -ENODEV; i < 2 && rc; i++) {
 | |
| 		tmplen = SECKEYBLOBSIZE;
 | |
| 		rc = cca_clr2key(NULL, 0, t->keytype, PKEY_TYPE_CCA_DATA,
 | |
| 				 8 * keysize, 0, t->clearkey, t->len,
 | |
| 				 tmpbuf, &tmplen, NULL);
 | |
| 		pr_debug("cca_clr2key()=%d\n", rc);
 | |
| 		if (rc)
 | |
| 			continue;
 | |
| 		rc = cca_key2protkey(NULL, 0, tmpbuf, tmplen,
 | |
| 				     protkey, protkeylen, protkeytype);
 | |
| 		pr_debug("cca_key2protkey()=%d\n", rc);
 | |
| 	}
 | |
| 
 | |
| 	kfree(tmpbuf);
 | |
| 	pr_debug("rc=%d\n", rc);
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| static struct pkey_handler cca_handler = {
 | |
| 	.module			 = THIS_MODULE,
 | |
| 	.name			 = "PKEY CCA handler",
 | |
| 	.is_supported_key	 = is_cca_key,
 | |
| 	.is_supported_keytype	 = is_cca_keytype,
 | |
| 	.key_to_protkey		 = cca_key2protkey,
 | |
| 	.slowpath_key_to_protkey = cca_slowpath_key2protkey,
 | |
| 	.gen_key		 = cca_gen_key,
 | |
| 	.clr_to_key		 = cca_clr2key,
 | |
| 	.verify_key		 = cca_verifykey,
 | |
| 	.apqns_for_key		 = cca_apqns4key,
 | |
| 	.apqns_for_keytype	 = cca_apqns4type,
 | |
| };
 | |
| 
 | |
| /*
 | |
|  * Module init
 | |
|  */
 | |
| static int __init pkey_cca_init(void)
 | |
| {
 | |
| 	/* register this module as pkey handler for all the cca stuff */
 | |
| 	return pkey_handler_register(&cca_handler);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Module exit
 | |
|  */
 | |
| static void __exit pkey_cca_exit(void)
 | |
| {
 | |
| 	/* unregister this module as pkey handler */
 | |
| 	pkey_handler_unregister(&cca_handler);
 | |
| }
 | |
| 
 | |
| module_init(pkey_cca_init);
 | |
| module_exit(pkey_cca_exit);
 |