mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-16 05:26:07 +00:00
55e055ba7f
Move include statement for zcrypt_api.h from the codefiles to the zcrypt_ccamis.h header file. Signed-off-by: Harald Freudenberger <freude@linux.ibm.com> Suggested-by: Holger Dengler <dengler@linux.ibm.com> Reviewed-by: Holger Dengler <dengler@linux.ibm.com> Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
557 lines
13 KiB
C
557 lines
13 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* pkey pckmo 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 <asm/cpacf.h>
|
|
#include <crypto/aes.h>
|
|
#include <linux/random.h>
|
|
|
|
#include "zcrypt_ccamisc.h"
|
|
#include "pkey_base.h"
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("IBM Corporation");
|
|
MODULE_DESCRIPTION("s390 protected key PCKMO handler");
|
|
|
|
/*
|
|
* Check key blob for known and supported here.
|
|
*/
|
|
static bool is_pckmo_key(const u8 *key, u32 keylen)
|
|
{
|
|
struct keytoken_header *hdr = (struct keytoken_header *)key;
|
|
struct clearkeytoken *t = (struct clearkeytoken *)key;
|
|
|
|
if (keylen < sizeof(*hdr))
|
|
return false;
|
|
|
|
switch (hdr->type) {
|
|
case TOKTYPE_NON_CCA:
|
|
switch (hdr->version) {
|
|
case TOKVER_CLEAR_KEY:
|
|
switch (t->keytype) {
|
|
case PKEY_KEYTYPE_AES_128:
|
|
case PKEY_KEYTYPE_AES_192:
|
|
case PKEY_KEYTYPE_AES_256:
|
|
case PKEY_KEYTYPE_ECC_P256:
|
|
case PKEY_KEYTYPE_ECC_P384:
|
|
case PKEY_KEYTYPE_ECC_P521:
|
|
case PKEY_KEYTYPE_ECC_ED25519:
|
|
case PKEY_KEYTYPE_ECC_ED448:
|
|
case PKEY_KEYTYPE_AES_XTS_128:
|
|
case PKEY_KEYTYPE_AES_XTS_256:
|
|
case PKEY_KEYTYPE_HMAC_512:
|
|
case PKEY_KEYTYPE_HMAC_1024:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
case TOKVER_PROTECTED_KEY:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool is_pckmo_keytype(enum pkey_key_type keytype)
|
|
{
|
|
switch (keytype) {
|
|
case PKEY_TYPE_PROTKEY:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Create a protected key from a clear key value via PCKMO instruction.
|
|
*/
|
|
static int pckmo_clr2protkey(u32 keytype, const u8 *clrkey, u32 clrkeylen,
|
|
u8 *protkey, u32 *protkeylen, u32 *protkeytype)
|
|
{
|
|
/* mask of available pckmo subfunctions */
|
|
static cpacf_mask_t pckmo_functions;
|
|
|
|
int keysize, rc = -EINVAL;
|
|
u8 paramblock[160];
|
|
u32 pkeytype;
|
|
long fc;
|
|
|
|
switch (keytype) {
|
|
case PKEY_KEYTYPE_AES_128:
|
|
/* 16 byte key, 32 byte aes wkvp, total 48 bytes */
|
|
keysize = 16;
|
|
pkeytype = keytype;
|
|
fc = CPACF_PCKMO_ENC_AES_128_KEY;
|
|
break;
|
|
case PKEY_KEYTYPE_AES_192:
|
|
/* 24 byte key, 32 byte aes wkvp, total 56 bytes */
|
|
keysize = 24;
|
|
pkeytype = keytype;
|
|
fc = CPACF_PCKMO_ENC_AES_192_KEY;
|
|
break;
|
|
case PKEY_KEYTYPE_AES_256:
|
|
/* 32 byte key, 32 byte aes wkvp, total 64 bytes */
|
|
keysize = 32;
|
|
pkeytype = keytype;
|
|
fc = CPACF_PCKMO_ENC_AES_256_KEY;
|
|
break;
|
|
case PKEY_KEYTYPE_ECC_P256:
|
|
/* 32 byte key, 32 byte aes wkvp, total 64 bytes */
|
|
keysize = 32;
|
|
pkeytype = PKEY_KEYTYPE_ECC;
|
|
fc = CPACF_PCKMO_ENC_ECC_P256_KEY;
|
|
break;
|
|
case PKEY_KEYTYPE_ECC_P384:
|
|
/* 48 byte key, 32 byte aes wkvp, total 80 bytes */
|
|
keysize = 48;
|
|
pkeytype = PKEY_KEYTYPE_ECC;
|
|
fc = CPACF_PCKMO_ENC_ECC_P384_KEY;
|
|
break;
|
|
case PKEY_KEYTYPE_ECC_P521:
|
|
/* 80 byte key, 32 byte aes wkvp, total 112 bytes */
|
|
keysize = 80;
|
|
pkeytype = PKEY_KEYTYPE_ECC;
|
|
fc = CPACF_PCKMO_ENC_ECC_P521_KEY;
|
|
break;
|
|
case PKEY_KEYTYPE_ECC_ED25519:
|
|
/* 32 byte key, 32 byte aes wkvp, total 64 bytes */
|
|
keysize = 32;
|
|
pkeytype = PKEY_KEYTYPE_ECC;
|
|
fc = CPACF_PCKMO_ENC_ECC_ED25519_KEY;
|
|
break;
|
|
case PKEY_KEYTYPE_ECC_ED448:
|
|
/* 64 byte key, 32 byte aes wkvp, total 96 bytes */
|
|
keysize = 64;
|
|
pkeytype = PKEY_KEYTYPE_ECC;
|
|
fc = CPACF_PCKMO_ENC_ECC_ED448_KEY;
|
|
break;
|
|
case PKEY_KEYTYPE_AES_XTS_128:
|
|
/* 2x16 byte keys, 32 byte aes wkvp, total 64 bytes */
|
|
keysize = 32;
|
|
pkeytype = PKEY_KEYTYPE_AES_XTS_128;
|
|
fc = CPACF_PCKMO_ENC_AES_XTS_128_DOUBLE_KEY;
|
|
break;
|
|
case PKEY_KEYTYPE_AES_XTS_256:
|
|
/* 2x32 byte keys, 32 byte aes wkvp, total 96 bytes */
|
|
keysize = 64;
|
|
pkeytype = PKEY_KEYTYPE_AES_XTS_256;
|
|
fc = CPACF_PCKMO_ENC_AES_XTS_256_DOUBLE_KEY;
|
|
break;
|
|
case PKEY_KEYTYPE_HMAC_512:
|
|
/* 64 byte key, 32 byte aes wkvp, total 96 bytes */
|
|
keysize = 64;
|
|
pkeytype = PKEY_KEYTYPE_HMAC_512;
|
|
fc = CPACF_PCKMO_ENC_HMAC_512_KEY;
|
|
break;
|
|
case PKEY_KEYTYPE_HMAC_1024:
|
|
/* 128 byte key, 32 byte aes wkvp, total 160 bytes */
|
|
keysize = 128;
|
|
pkeytype = PKEY_KEYTYPE_HMAC_1024;
|
|
fc = CPACF_PCKMO_ENC_HMAC_1024_KEY;
|
|
break;
|
|
default:
|
|
PKEY_DBF_ERR("%s unknown/unsupported keytype %u\n",
|
|
__func__, keytype);
|
|
goto out;
|
|
}
|
|
|
|
if (clrkeylen && clrkeylen < keysize) {
|
|
PKEY_DBF_ERR("%s clear key size too small: %u < %d\n",
|
|
__func__, clrkeylen, keysize);
|
|
goto out;
|
|
}
|
|
if (*protkeylen < keysize + AES_WK_VP_SIZE) {
|
|
PKEY_DBF_ERR("%s prot key buffer size too small: %u < %d\n",
|
|
__func__, *protkeylen, keysize + AES_WK_VP_SIZE);
|
|
goto out;
|
|
}
|
|
|
|
/* Did we already check for PCKMO ? */
|
|
if (!pckmo_functions.bytes[0]) {
|
|
/* no, so check now */
|
|
if (!cpacf_query(CPACF_PCKMO, &pckmo_functions)) {
|
|
PKEY_DBF_ERR("%s cpacf_query() failed\n", __func__);
|
|
rc = -ENODEV;
|
|
goto out;
|
|
}
|
|
}
|
|
/* check for the pckmo subfunction we need now */
|
|
if (!cpacf_test_func(&pckmo_functions, fc)) {
|
|
PKEY_DBF_ERR("%s pckmo functions not available\n", __func__);
|
|
rc = -ENODEV;
|
|
goto out;
|
|
}
|
|
|
|
/* prepare param block */
|
|
memset(paramblock, 0, sizeof(paramblock));
|
|
memcpy(paramblock, clrkey, keysize);
|
|
|
|
/* call the pckmo instruction */
|
|
cpacf_pckmo(fc, paramblock);
|
|
|
|
/* copy created protected key to key buffer including the wkvp block */
|
|
*protkeylen = keysize + AES_WK_VP_SIZE;
|
|
memcpy(protkey, paramblock, *protkeylen);
|
|
*protkeytype = pkeytype;
|
|
|
|
rc = 0;
|
|
|
|
out:
|
|
pr_debug("rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* Verify a raw protected key blob.
|
|
* Currently only AES protected keys are supported.
|
|
*/
|
|
static int pckmo_verify_protkey(const u8 *protkey, u32 protkeylen,
|
|
u32 protkeytype)
|
|
{
|
|
struct {
|
|
u8 iv[AES_BLOCK_SIZE];
|
|
u8 key[MAXPROTKEYSIZE];
|
|
} param;
|
|
u8 null_msg[AES_BLOCK_SIZE];
|
|
u8 dest_buf[AES_BLOCK_SIZE];
|
|
unsigned int k, pkeylen;
|
|
unsigned long fc;
|
|
int rc = -EINVAL;
|
|
|
|
switch (protkeytype) {
|
|
case PKEY_KEYTYPE_AES_128:
|
|
pkeylen = 16 + AES_WK_VP_SIZE;
|
|
fc = CPACF_KMC_PAES_128;
|
|
break;
|
|
case PKEY_KEYTYPE_AES_192:
|
|
pkeylen = 24 + AES_WK_VP_SIZE;
|
|
fc = CPACF_KMC_PAES_192;
|
|
break;
|
|
case PKEY_KEYTYPE_AES_256:
|
|
pkeylen = 32 + AES_WK_VP_SIZE;
|
|
fc = CPACF_KMC_PAES_256;
|
|
break;
|
|
default:
|
|
PKEY_DBF_ERR("%s unknown/unsupported keytype %u\n", __func__,
|
|
protkeytype);
|
|
goto out;
|
|
}
|
|
if (protkeylen != pkeylen) {
|
|
PKEY_DBF_ERR("%s invalid protected key size %u for keytype %u\n",
|
|
__func__, protkeylen, protkeytype);
|
|
goto out;
|
|
}
|
|
|
|
memset(null_msg, 0, sizeof(null_msg));
|
|
|
|
memset(param.iv, 0, sizeof(param.iv));
|
|
memcpy(param.key, protkey, protkeylen);
|
|
|
|
k = cpacf_kmc(fc | CPACF_ENCRYPT, ¶m, null_msg, dest_buf,
|
|
sizeof(null_msg));
|
|
if (k != sizeof(null_msg)) {
|
|
PKEY_DBF_ERR("%s protected key is not valid\n", __func__);
|
|
rc = -EKEYREJECTED;
|
|
goto out;
|
|
}
|
|
|
|
rc = 0;
|
|
|
|
out:
|
|
pr_debug("rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
static int pckmo_key2protkey(const u8 *key, u32 keylen,
|
|
u8 *protkey, u32 *protkeylen, u32 *protkeytype)
|
|
{
|
|
struct keytoken_header *hdr = (struct keytoken_header *)key;
|
|
int rc = -EINVAL;
|
|
|
|
if (keylen < sizeof(*hdr))
|
|
return -EINVAL;
|
|
if (hdr->type != TOKTYPE_NON_CCA)
|
|
return -EINVAL;
|
|
|
|
switch (hdr->version) {
|
|
case TOKVER_PROTECTED_KEY: {
|
|
struct protkeytoken *t = (struct protkeytoken *)key;
|
|
|
|
if (keylen < sizeof(*t))
|
|
goto out;
|
|
switch (t->keytype) {
|
|
case PKEY_KEYTYPE_AES_128:
|
|
case PKEY_KEYTYPE_AES_192:
|
|
case PKEY_KEYTYPE_AES_256:
|
|
if (keylen != sizeof(struct protaeskeytoken))
|
|
goto out;
|
|
rc = pckmo_verify_protkey(t->protkey, t->len,
|
|
t->keytype);
|
|
if (rc)
|
|
goto out;
|
|
break;
|
|
case PKEY_KEYTYPE_AES_XTS_128:
|
|
if (t->len != 64 || keylen != sizeof(*t) + t->len)
|
|
goto out;
|
|
break;
|
|
case PKEY_KEYTYPE_AES_XTS_256:
|
|
case PKEY_KEYTYPE_HMAC_512:
|
|
if (t->len != 96 || keylen != sizeof(*t) + t->len)
|
|
goto out;
|
|
break;
|
|
case PKEY_KEYTYPE_HMAC_1024:
|
|
if (t->len != 160 || keylen != sizeof(*t) + t->len)
|
|
goto out;
|
|
break;
|
|
default:
|
|
PKEY_DBF_ERR("%s protected key token: unknown keytype %u\n",
|
|
__func__, t->keytype);
|
|
goto out;
|
|
}
|
|
memcpy(protkey, t->protkey, t->len);
|
|
*protkeylen = t->len;
|
|
*protkeytype = t->keytype;
|
|
break;
|
|
}
|
|
case TOKVER_CLEAR_KEY: {
|
|
struct clearkeytoken *t = (struct clearkeytoken *)key;
|
|
u32 keysize = 0;
|
|
|
|
if (keylen < sizeof(struct clearkeytoken) ||
|
|
keylen != sizeof(*t) + t->len)
|
|
goto out;
|
|
switch (t->keytype) {
|
|
case PKEY_KEYTYPE_AES_128:
|
|
case PKEY_KEYTYPE_AES_192:
|
|
case PKEY_KEYTYPE_AES_256:
|
|
keysize = pkey_keytype_aes_to_size(t->keytype);
|
|
break;
|
|
case PKEY_KEYTYPE_ECC_P256:
|
|
keysize = 32;
|
|
break;
|
|
case PKEY_KEYTYPE_ECC_P384:
|
|
keysize = 48;
|
|
break;
|
|
case PKEY_KEYTYPE_ECC_P521:
|
|
keysize = 80;
|
|
break;
|
|
case PKEY_KEYTYPE_ECC_ED25519:
|
|
keysize = 32;
|
|
break;
|
|
case PKEY_KEYTYPE_ECC_ED448:
|
|
keysize = 64;
|
|
break;
|
|
case PKEY_KEYTYPE_AES_XTS_128:
|
|
keysize = 32;
|
|
break;
|
|
case PKEY_KEYTYPE_AES_XTS_256:
|
|
keysize = 64;
|
|
break;
|
|
case PKEY_KEYTYPE_HMAC_512:
|
|
keysize = 64;
|
|
break;
|
|
case PKEY_KEYTYPE_HMAC_1024:
|
|
keysize = 128;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (!keysize) {
|
|
PKEY_DBF_ERR("%s clear key token: unknown keytype %u\n",
|
|
__func__, t->keytype);
|
|
goto out;
|
|
}
|
|
if (t->len != keysize) {
|
|
PKEY_DBF_ERR("%s clear key token: invalid key len %u\n",
|
|
__func__, t->len);
|
|
goto out;
|
|
}
|
|
rc = pckmo_clr2protkey(t->keytype, t->clearkey, t->len,
|
|
protkey, protkeylen, protkeytype);
|
|
break;
|
|
}
|
|
default:
|
|
PKEY_DBF_ERR("%s unknown non-CCA token version %d\n",
|
|
__func__, hdr->version);
|
|
break;
|
|
}
|
|
|
|
out:
|
|
pr_debug("rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* Generate a random protected key.
|
|
* Currently only the generation of AES protected keys
|
|
* is supported.
|
|
*/
|
|
static int pckmo_gen_protkey(u32 keytype, u32 subtype,
|
|
u8 *protkey, u32 *protkeylen, u32 *protkeytype)
|
|
{
|
|
u8 clrkey[128];
|
|
int keysize;
|
|
int rc;
|
|
|
|
switch (keytype) {
|
|
case PKEY_KEYTYPE_AES_128:
|
|
case PKEY_KEYTYPE_AES_192:
|
|
case PKEY_KEYTYPE_AES_256:
|
|
keysize = pkey_keytype_aes_to_size(keytype);
|
|
break;
|
|
case PKEY_KEYTYPE_AES_XTS_128:
|
|
keysize = 32;
|
|
break;
|
|
case PKEY_KEYTYPE_AES_XTS_256:
|
|
case PKEY_KEYTYPE_HMAC_512:
|
|
keysize = 64;
|
|
break;
|
|
case PKEY_KEYTYPE_HMAC_1024:
|
|
keysize = 128;
|
|
break;
|
|
default:
|
|
PKEY_DBF_ERR("%s unknown/unsupported keytype %d\n",
|
|
__func__, keytype);
|
|
return -EINVAL;
|
|
}
|
|
if (subtype != PKEY_TYPE_PROTKEY) {
|
|
PKEY_DBF_ERR("%s unknown/unsupported subtype %d\n",
|
|
__func__, subtype);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* generate a dummy random clear key */
|
|
get_random_bytes(clrkey, keysize);
|
|
|
|
/* convert it to a dummy protected key */
|
|
rc = pckmo_clr2protkey(keytype, clrkey, keysize,
|
|
protkey, protkeylen, protkeytype);
|
|
if (rc)
|
|
goto out;
|
|
|
|
/* replace the key part of the protected key with random bytes */
|
|
get_random_bytes(protkey, keysize);
|
|
|
|
out:
|
|
pr_debug("rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* Verify a protected key token blob.
|
|
* Currently only AES protected keys are supported.
|
|
*/
|
|
static int pckmo_verify_key(const u8 *key, u32 keylen)
|
|
{
|
|
struct keytoken_header *hdr = (struct keytoken_header *)key;
|
|
int rc = -EINVAL;
|
|
|
|
if (keylen < sizeof(*hdr))
|
|
return -EINVAL;
|
|
if (hdr->type != TOKTYPE_NON_CCA)
|
|
return -EINVAL;
|
|
|
|
switch (hdr->version) {
|
|
case TOKVER_PROTECTED_KEY: {
|
|
struct protaeskeytoken *t;
|
|
|
|
if (keylen != sizeof(struct protaeskeytoken))
|
|
goto out;
|
|
t = (struct protaeskeytoken *)key;
|
|
rc = pckmo_verify_protkey(t->protkey, t->len, t->keytype);
|
|
break;
|
|
}
|
|
default:
|
|
PKEY_DBF_ERR("%s unknown non-CCA token version %d\n",
|
|
__func__, hdr->version);
|
|
break;
|
|
}
|
|
|
|
out:
|
|
pr_debug("rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* Wrapper functions used for the pkey handler struct
|
|
*/
|
|
|
|
static int pkey_pckmo_key2protkey(const struct pkey_apqn *_apqns,
|
|
size_t _nr_apqns,
|
|
const u8 *key, u32 keylen,
|
|
u8 *protkey, u32 *protkeylen, u32 *keyinfo)
|
|
{
|
|
return pckmo_key2protkey(key, keylen,
|
|
protkey, protkeylen, keyinfo);
|
|
}
|
|
|
|
static int pkey_pckmo_gen_key(const struct pkey_apqn *_apqns, size_t _nr_apqns,
|
|
u32 keytype, u32 keysubtype,
|
|
u32 _keybitsize, u32 _flags,
|
|
u8 *keybuf, u32 *keybuflen, u32 *keyinfo)
|
|
{
|
|
return pckmo_gen_protkey(keytype, keysubtype,
|
|
keybuf, keybuflen, keyinfo);
|
|
}
|
|
|
|
static int pkey_pckmo_verifykey(const u8 *key, u32 keylen,
|
|
u16 *_card, u16 *_dom,
|
|
u32 *_keytype, u32 *_keybitsize, u32 *_flags)
|
|
{
|
|
return pckmo_verify_key(key, keylen);
|
|
}
|
|
|
|
static struct pkey_handler pckmo_handler = {
|
|
.module = THIS_MODULE,
|
|
.name = "PKEY PCKMO handler",
|
|
.is_supported_key = is_pckmo_key,
|
|
.is_supported_keytype = is_pckmo_keytype,
|
|
.key_to_protkey = pkey_pckmo_key2protkey,
|
|
.gen_key = pkey_pckmo_gen_key,
|
|
.verify_key = pkey_pckmo_verifykey,
|
|
};
|
|
|
|
/*
|
|
* Module init
|
|
*/
|
|
static int __init pkey_pckmo_init(void)
|
|
{
|
|
cpacf_mask_t func_mask;
|
|
|
|
/*
|
|
* The pckmo instruction should be available - even if we don't
|
|
* actually invoke it. This instruction comes with MSA 3 which
|
|
* is also the minimum level for the kmc instructions which
|
|
* are able to work with protected keys.
|
|
*/
|
|
if (!cpacf_query(CPACF_PCKMO, &func_mask))
|
|
return -ENODEV;
|
|
|
|
/* register this module as pkey handler for all the pckmo stuff */
|
|
return pkey_handler_register(&pckmo_handler);
|
|
}
|
|
|
|
/*
|
|
* Module exit
|
|
*/
|
|
static void __exit pkey_pckmo_exit(void)
|
|
{
|
|
/* unregister this module as pkey handler */
|
|
pkey_handler_unregister(&pckmo_handler);
|
|
}
|
|
|
|
module_cpu_feature_match(S390_CPU_FEATURE_MSA, pkey_pckmo_init);
|
|
module_exit(pkey_pckmo_exit);
|