blob: 835d59f4fbc563394d7c2fee6552409120ac3326 [file] [log] [blame]
// 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:
if (pkey_keytype_to_size(t->keytype))
return true;
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 = 0;
unsigned int fc;
switch (keytype) {
case PKEY_KEYTYPE_AES_128:
fc = CPACF_PCKMO_ENC_AES_128_KEY;
break;
case PKEY_KEYTYPE_AES_192:
fc = CPACF_PCKMO_ENC_AES_192_KEY;
break;
case PKEY_KEYTYPE_AES_256:
fc = CPACF_PCKMO_ENC_AES_256_KEY;
break;
case PKEY_KEYTYPE_ECC_P256:
pkeytype = PKEY_KEYTYPE_ECC;
fc = CPACF_PCKMO_ENC_ECC_P256_KEY;
break;
case PKEY_KEYTYPE_ECC_P384:
pkeytype = PKEY_KEYTYPE_ECC;
fc = CPACF_PCKMO_ENC_ECC_P384_KEY;
break;
case PKEY_KEYTYPE_ECC_P521:
pkeytype = PKEY_KEYTYPE_ECC;
fc = CPACF_PCKMO_ENC_ECC_P521_KEY;
break;
case PKEY_KEYTYPE_ECC_ED25519:
pkeytype = PKEY_KEYTYPE_ECC;
fc = CPACF_PCKMO_ENC_ECC_ED25519_KEY;
break;
case PKEY_KEYTYPE_ECC_ED448:
pkeytype = PKEY_KEYTYPE_ECC;
fc = CPACF_PCKMO_ENC_ECC_ED448_KEY;
break;
case PKEY_KEYTYPE_AES_XTS_128:
fc = CPACF_PCKMO_ENC_AES_XTS_128_DOUBLE_KEY;
break;
case PKEY_KEYTYPE_AES_XTS_256:
fc = CPACF_PCKMO_ENC_AES_XTS_256_DOUBLE_KEY;
break;
case PKEY_KEYTYPE_HMAC_512:
fc = CPACF_PCKMO_ENC_HMAC_512_KEY;
break;
case 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;
}
keysize = pkey_keytype_to_size(keytype);
pkeytype = pkeytype ?: keytype;
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 fc 0x%02x not available\n",
__func__, fc);
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.
*/
static int pckmo_verify_protkey(const u8 *protkey, u32 protkeylen,
u32 protkeytype)
{
u8 clrkey[16] = { 0 }, tmpkeybuf[16 + AES_WK_VP_SIZE];
u32 tmpkeybuflen, tmpkeytype;
int keysize, rc = -EINVAL;
u8 *wkvp;
/* check protkey type and size */
keysize = pkey_keytype_to_size(protkeytype);
if (!keysize) {
PKEY_DBF_ERR("%s unknown/unsupported keytype %u\n", __func__,
protkeytype);
goto out;
}
if (protkeylen < keysize + AES_WK_VP_SIZE)
goto out;
/* generate a dummy AES 128 protected key */
tmpkeybuflen = sizeof(tmpkeybuf);
rc = pckmo_clr2protkey(PKEY_KEYTYPE_AES_128,
clrkey, sizeof(clrkey),
tmpkeybuf, &tmpkeybuflen, &tmpkeytype);
if (rc)
goto out;
memzero_explicit(tmpkeybuf, 16);
wkvp = tmpkeybuf + 16;
/* compare WK VP from the temp key with that of the given prot key */
if (memcmp(wkvp, protkey + keysize, AES_WK_VP_SIZE)) {
PKEY_DBF_ERR("%s protected key WK VP mismatch\n", __func__);
rc = -EKEYREJECTED;
goto out;
}
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;
u32 keysize;
if (keylen < sizeof(*t))
goto out;
keysize = pkey_keytype_to_size(t->keytype);
if (!keysize) {
PKEY_DBF_ERR("%s protected key token: unknown keytype %u\n",
__func__, t->keytype);
goto out;
}
switch (t->keytype) {
case PKEY_KEYTYPE_AES_128:
case PKEY_KEYTYPE_AES_192:
case PKEY_KEYTYPE_AES_256:
if (t->len != keysize + AES_WK_VP_SIZE ||
keylen < sizeof(struct protaeskeytoken))
goto out;
rc = pckmo_verify_protkey(t->protkey, t->len,
t->keytype);
if (rc)
goto out;
break;
default:
if (t->len != keysize + AES_WK_VP_SIZE ||
keylen < sizeof(*t) + keysize + AES_WK_VP_SIZE)
goto out;
break;
}
memcpy(protkey, t->protkey, t->len);
*protkeylen = t->len;
*protkeytype = t->keytype;
rc = 0;
break;
}
case TOKVER_CLEAR_KEY: {
struct clearkeytoken *t = (struct clearkeytoken *)key;
u32 keysize;
if (keylen < sizeof(*t) ||
keylen < sizeof(*t) + t->len)
goto out;
keysize = pkey_keytype_to_size(t->keytype);
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.
*/
static int pckmo_gen_protkey(u32 keytype, u32 subtype,
u8 *protkey, u32 *protkeylen, u32 *protkeytype)
{
u8 clrkey[128];
int keysize;
int rc;
keysize = pkey_keytype_to_size(keytype);
if (!keysize) {
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;
}
switch (keytype) {
case PKEY_KEYTYPE_AES_128:
case PKEY_KEYTYPE_AES_192:
case PKEY_KEYTYPE_AES_256:
case PKEY_KEYTYPE_AES_XTS_128:
case PKEY_KEYTYPE_AES_XTS_256:
case PKEY_KEYTYPE_HMAC_512:
case PKEY_KEYTYPE_HMAC_1024:
break;
default:
PKEY_DBF_ERR("%s unsupported keytype %d\n",
__func__, keytype);
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.
*/
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 protkeytoken *t = (struct protkeytoken *)key;
u32 keysize;
if (keylen < sizeof(*t))
goto out;
keysize = pkey_keytype_to_size(t->keytype);
if (!keysize || t->len != keysize + AES_WK_VP_SIZE)
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;
break;
default:
if (keylen < sizeof(*t) + keysize + AES_WK_VP_SIZE)
goto out;
break;
}
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);