Files
s390-tools/zkey/cpacf.c
Ingo Franzki 7fffdcfe8c zkey: Remove the use of AF_ALG for calculating key verification patterns
Instead of using AF_ALG to calculate key verification patterns, transform
the key blob into a protected key and calculate the key verification
patterns with CAPCF calls.

The 'zkey-cryptsetup convert' command also calculates key verification
patterns from clear keys. Support this, too.

Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-22 16:43:03 +02:00

604 lines
15 KiB
C

/*
* zkey - Generate, re-encipher, and validate secure keys
*
* Copyright IBM Corp. 2018, 2024
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/auxv.h>
#include <openssl/crypto.h>
#include "lib/util_libc.h"
#include "cpacf.h"
#include "pkey.h"
#define MASK64(n) (1ULL << (63 - (n) % 64))
#define OFF64(n) ((n) / 64)
/*
* Checks if the specified MSA level is available.
*
* @param[in] msa the MSA level to query
*
* @returns true if available, false otherwise
*/
static bool cpacf_msa(int msa)
{
bool ret;
unsigned long hwcap, facility_list_nmemb;
u64 *facility_list = NULL, tmp;
hwcap = getauxval(AT_HWCAP);
if ((hwcap & HWCAP_S390_STFLE) == 0)
return false;
facility_list_nmemb = stfle(&tmp, 1);
if (facility_list_nmemb > UINT8_MAX)
return false;
facility_list = util_zalloc(facility_list_nmemb * sizeof(u64));
stfle(facility_list, facility_list_nmemb);
ret = (facility_list_nmemb >= (unsigned long)OFF64(msa) + 1 &&
(facility_list[OFF64(msa)] & MASK64(msa)) != 0);
free(facility_list);
return ret;
}
/*
* Checks if the KMC instruction and the specified KMC function code is
* supported.
*
* @param[in] fc the function code to query
*
* @returns true if supported, false otherwise
*/
static bool cpacf_query_kmc(int fc)
{
u64 status_word[2] = { 0 };
if (!cpacf_msa(MSA))
return false;
cpacf_kmc(CPACF_KM_QUERY, &status_word, NULL, NULL, 0, NULL);
return (status_word[OFF64(fc)] & MASK64(fc)) != 0;
}
/*
* Checks if the KM instruction and the specified KM function code is
* supported.
*
* @param[in] fc the function code to query
*
* @returns true if supported, false otherwise
*/
static bool cpacf_query_km(int fc)
{
u64 status_word[2] = { 0 };
if (!cpacf_msa(MSA))
return false;
cpacf_km(CPACF_KMC_QUERY, &status_word, NULL, NULL, 0, NULL);
return (status_word[OFF64(fc)] & MASK64(fc)) != 0;
}
/*
* Checks if the KMAC instruction and the specified KMAC function code is
* supported.
*
* @param[in] fc the function code to query
*
* @returns true if supported, false otherwise
*/
static bool cpacf_query_kmac(int fc)
{
u64 status_word[2] = { 0 };
if (!cpacf_msa(MSA))
return false;
cpacf_kmac(CPACF_KMAC_QUERY, &status_word, NULL, 0);
return (status_word[OFF64(fc)] & MASK64(fc)) != 0;
}
/*
* Checks if the PCC instruction and the specified PCC function code is
* supported.
*
* @param[in] fc the function code to query
*
* @returns true if supported, false otherwise
*/
static bool cpacf_query_pcc(int fc)
{
u64 status_word[2] = { 0 };
if (!cpacf_msa(MSA4))
return false;
cpacf_pcc(CPACF_PCC_QUERY, &status_word);
return (status_word[OFF64(fc)] & MASK64(fc)) != 0;
}
/*
* Performs a one-shot AES-ECB encryption using a clear or protected key.
*
* @param[in] key the clear or protected key
* @param[in] key_size the size of the clear or protected key
* @param[in] in the clear data to encrypt
* @param[in] out the output buffer to write the encrypted data to
* @param[in] size the size of the data to encrypt, and also the size
* of the output buffer.
* @param[in] pkey_type the type of the protected key (PKEY_KEYTYPE_nnn)
* or 0 if it is a clear key.
*
* @returns 0 on success, a negative errno in case of an error
*/
int cpacf_aes_cbc_enc(const u8 *key, size_t key_size,
const u8 *in, u8 *out, size_t size,
int pkey_type)
{
union {
struct cpacf_kmc_aes_128_param aes_128;
struct cpacf_kmc_aes_192_param aes_192;
struct cpacf_kmc_aes_256_param aes_256;
struct cpacf_kmc_enc_aes_128_param aes_128_enc;
struct cpacf_kmc_enc_aes_192_param aes_192_enc;
struct cpacf_kmc_enc_aes_256_param aes_256_enc;
} kmc_param = { 0 };
int fc, cc, rc = 0;
switch (pkey_type) {
case 0:
/* clear key */
switch (key_size) {
case 16:
fc = CPACF_KMC_AES_128;
memcpy(kmc_param.aes_128.key, key,
sizeof(kmc_param.aes_128.key));
break;
case 24:
fc = CPACF_KMC_AES_192;
memcpy(kmc_param.aes_192.key, key,
sizeof(kmc_param.aes_192.key));
break;
case 32:
fc = CPACF_KMC_AES_256;
memcpy(kmc_param.aes_256.key, key,
sizeof(kmc_param.aes_256.key));
break;
default:
rc = -EINVAL;
goto out;
}
break;
case PKEY_KEYTYPE_AES_128:
fc = CPACF_KMC_ENCRYPTED_AES_128;
if (key_size != sizeof(kmc_param.aes_128_enc.protkey)) {
rc = -EINVAL;
goto out;
}
memcpy(kmc_param.aes_128_enc.protkey, key,
sizeof(kmc_param.aes_128_enc.protkey));
break;
case PKEY_KEYTYPE_AES_192:
fc = CPACF_KMC_ENCRYPTED_AES_192;
if (key_size != sizeof(kmc_param.aes_192_enc.protkey)) {
rc = -EINVAL;
goto out;
}
memcpy(kmc_param.aes_192_enc.protkey, key,
sizeof(kmc_param.aes_192_enc.protkey));
break;
case PKEY_KEYTYPE_AES_256:
fc = CPACF_KMC_ENCRYPTED_AES_256;
if (key_size != sizeof(kmc_param.aes_256_enc.protkey)) {
rc = -EINVAL;
goto out;
}
memcpy(kmc_param.aes_256_enc.protkey, key,
sizeof(kmc_param.aes_256_enc.protkey));
break;
default:
rc = -EINVAL;
goto out;
}
if (!cpacf_query_kmc(fc)) {
rc = -ENODEV;
goto out;
}
cc = cpacf_kmc(fc, &kmc_param, out, in, size, NULL);
if (cc == 1) /* WKVP mismatch */
rc = -EAGAIN;
else if (cc != 0)
rc = -EIO;
out:
OPENSSL_cleanse(&kmc_param, sizeof(kmc_param));
return rc;
}
/*
* Performs a one-shot AES-XTS encryption using a clear or protected key.
*
* @param[in] key the clear or protected key
* @param[in] key_size the size of the clear or protected key
* @param[in] in the clear data to encrypt
* @param[in] out the output buffer to write the encrypted data to
* @param[in] size the size of the data to encrypt, and also the size
* of the output buffer.
* @param[in] pkey_type the type of the protected key (PKEY_KEYTYPE_nnn)
* or 0 if it is a clear key.
*
* @returns 0 on success, a negative errno in case of an error
*/
int cpacf_aes_xts_enc(const u8 *key, size_t key_size,
const u8 *in, u8 *out, size_t size,
int pkey_type)
{
union {
struct cpacf_pcc_xts_aes_128_param aes_xts_128;
struct cpacf_pcc_xts_aes_256_param aes_xts_256;
struct cpacf_pcc_enc_xts_aes_128_param aes_xts_enc_128;
struct cpacf_pcc_enc_xts_aes_256_param aes_xts_enc_256;
} pcc_param = { 0 };
union {
struct cpacf_km_xts_aes_128_param aes_xts_128;
struct cpacf_km_xts_aes_256_param aes_xts_256;
struct cpacf_km_enc_xts_aes_128_param aes_xts_enc_128;
struct cpacf_km_enc_xts_aes_256_param aes_xts_enc_256;
} km_param = { 0 };
int pcc_fc, km_fc, cc, rc = 0;
switch (pkey_type) {
case 0:
/* clear key */
switch (key_size) {
case 32:
pcc_fc = CPACF_PCC_XTS_AES_128;
memcpy(pcc_param.aes_xts_128.key,
key + sizeof(pcc_param.aes_xts_128.key),
sizeof(pcc_param.aes_xts_128.key));
break;
case 64:
pcc_fc = CPACF_PCC_XTS_AES_256;
memcpy(pcc_param.aes_xts_256.key,
key + sizeof(pcc_param.aes_xts_256.key),
sizeof(pcc_param.aes_xts_256.key));
break;
default:
rc = -EINVAL;
goto out;
}
break;
case PKEY_KEYTYPE_AES_128:
pcc_fc = CPACF_PCC_XTS_ENCRYPTED_AES_128;
if (key_size != 2 * sizeof(pcc_param.aes_xts_enc_128.protkey)) {
rc = -EINVAL;
goto out;
}
memcpy(pcc_param.aes_xts_enc_128.protkey,
key + sizeof(pcc_param.aes_xts_enc_128.protkey),
sizeof(pcc_param.aes_xts_enc_128.protkey));
break;
case PKEY_KEYTYPE_AES_256:
pcc_fc = CPACF_PCC_XTS_ENCRYPTED_AES_256;
if (key_size != 2 * sizeof(pcc_param.aes_xts_enc_256.protkey)) {
rc = -EINVAL;
goto out;
}
memcpy(pcc_param.aes_xts_enc_256.protkey,
key + sizeof(pcc_param.aes_xts_enc_256.protkey),
sizeof(pcc_param.aes_xts_enc_256.protkey));
break;
default:
rc = -EINVAL;
goto out;
}
if (!cpacf_query_pcc(pcc_fc)) {
rc = -ENODEV;
goto out;
}
cc = cpacf_pcc(pcc_fc, &pcc_param);
if (cc == 1) /* WKVP mismatch */
rc = -EAGAIN;
else if (cc != 0)
rc = -EIO;
if (rc != 0)
goto out;
switch (pkey_type) {
case 0:
/* clear key */
switch (key_size) {
case 32:
km_fc = CPACF_KM_XTS_AES_128;
memcpy(km_param.aes_xts_128.key, key,
sizeof(km_param.aes_xts_128.key));
memcpy(km_param.aes_xts_128.xtsparam,
pcc_param.aes_xts_128.xtsparams,
sizeof(km_param.aes_xts_128.xtsparam));
break;
case 64:
km_fc = CPACF_KM_XTS_AES_256;
memcpy(km_param.aes_xts_256.key, key,
sizeof(km_param.aes_xts_256.key));
memcpy(km_param.aes_xts_256.xtsparam,
pcc_param.aes_xts_256.xtsparams,
sizeof(km_param.aes_xts_256.xtsparam));
break;
default:
rc = -EINVAL;
goto out;
}
break;
case PKEY_KEYTYPE_AES_128:
km_fc = CPACF_KM_XTS_ENCRYPTED_AES_128;
memcpy(km_param.aes_xts_enc_128.protkey, key,
sizeof(km_param.aes_xts_enc_128.protkey));
memcpy(km_param.aes_xts_enc_128.xtsparam,
pcc_param.aes_xts_enc_128.xtsparams,
sizeof(km_param.aes_xts_enc_128.xtsparam));
break;
case PKEY_KEYTYPE_AES_256:
km_fc = CPACF_KM_XTS_ENCRYPTED_AES_256;
memcpy(km_param.aes_xts_enc_256.protkey, key,
sizeof(km_param.aes_xts_enc_256.protkey));
memcpy(km_param.aes_xts_enc_256.xtsparam,
pcc_param.aes_xts_enc_256.xtsparams,
sizeof(km_param.aes_xts_enc_256.xtsparam));
break;
default:
rc = -EINVAL;
goto out;
}
if (!cpacf_query_km(km_fc)) {
rc = -ENODEV;
goto out;
}
cc = cpacf_km(km_fc, &km_param, out, in, size, NULL);
if (cc == 1) /* WKVP mismatch */
rc = -EAGAIN;
else if (cc != 0)
rc = -EIO;
out:
OPENSSL_cleanse(&pcc_param, sizeof(pcc_param));
OPENSSL_cleanse(&km_param, sizeof(km_param));
return rc;
}
/*
* Performs a one-shot AES-XTS encryption using a Full-XTS protected key
*
* @param[in] key the protected key
* @param[in] key_size the size of the protected key
* @param[in] in the clear data to encrypt
* @param[in] out the output buffer to write the encrypted data to
* @param[in] size the size of the data to encrypt, and also the size
* of the output buffer.
* @param[in] pkey_type the type of the protected key (PKEY_KEYTYPE_nnn).
*
* @returns 0 on success, a negative errno in case of an error
*/
int cpacf_aes_xts_full_enc(const u8 *key, size_t key_size,
const u8 *in, u8 *out, size_t size,
int pkey_type)
{
union {
struct cpacf_km_xts_full_aes_128_param aes_128;
struct cpacf_km_xts_full_aes_256_param aes_256;
} km_param = { 0 };
int fc, cc, rc = 0;
switch (pkey_type) {
case PKEY_KEYTYPE_AES_XTS_128:
fc = CPACF_KM_FXTS_ENCRYPTED_AES_128;
if (key_size != sizeof(km_param.aes_128.protkey) +
sizeof(km_param.aes_128.wkvp)) {
rc = -EINVAL;
goto out;
}
memcpy(km_param.aes_128.protkey, key,
sizeof(km_param.aes_128.protkey));
km_param.aes_128.nap[0] = 0x01;
memcpy(km_param.aes_128.wkvp,
key + sizeof(km_param.aes_128.protkey),
sizeof(km_param.aes_128.wkvp));
break;
case PKEY_KEYTYPE_AES_XTS_256:
fc = CPACF_KM_FXTS_ENCRYPTED_AES_256;
if (key_size != sizeof(km_param.aes_256.protkey) +
sizeof(km_param.aes_256.wkvp)) {
rc = -EINVAL;
goto out;
}
memcpy(km_param.aes_256.protkey, key,
sizeof(km_param.aes_256.protkey));
km_param.aes_256.nap[0] = 0x01;
memcpy(km_param.aes_256.wkvp,
key + sizeof(km_param.aes_256.protkey),
sizeof(km_param.aes_256.wkvp));
break;
default:
rc = -EINVAL;
goto out;
}
if (!cpacf_query_km(fc)) {
rc = -ENODEV;
goto out;
}
cc = cpacf_km(fc, &km_param, out, in, size, NULL);
if (cc == 1) /* WKVP mismatch */
rc = -EAGAIN;
else if (cc != 0)
rc = -EIO;
out:
OPENSSL_cleanse(&km_param, sizeof(km_param));
return rc;
}
/*
* Performs a one-shot SHA-HMAC operation using a clear or protected key.
*
* @param[in] key the clear or protected key
* @param[in] key_size the size of the clear or protected key
* @param[in] in the clear data to mac
* @param[in] in_size the size of the data to mac
* @param[in] mac the output buffer to write the mac to
* @param[in] mac_size the size of the mac buffer
* @param[in] pkey_type the type of the protected key (PKEY_KEYTYPE_nnn)
* or 0 if it is a clear key.
*
* @returns 0 on success, a negative errno in case of an error
*/
int cpacf_hmac_sha(const u8 *key, size_t key_size,
const u8 *in, size_t in_size,
u8 *mac, size_t mac_size,
int pkey_type)
{
union {
struct cpacf_kmac_hmac_224_256_param hmac_256;
struct cpacf_kmac_hmac_384_512_param hmac_512;
struct cpacf_kmac_enc_hmac_224_256_param hmac_256_enc;
struct cpacf_kmac_enc_hmac_384_512_param hmac_512_enc;
} kmac_param = { 0 };
int fc, cc, rc = 0;
switch (pkey_type) {
case 0:
/* clear key */
switch (key_size) {
case 64:
fc = CPACF_KMAC_HMAC_SHA_256;
memcpy(kmac_param.hmac_256.key, key,
sizeof(kmac_param.hmac_256.key));
kmac_param.hmac_256.imbl = in_size * 8;
break;
case 128:
fc = CPACF_KMAC_HMAC_SHA_512;
memcpy(kmac_param.hmac_512.key, key,
sizeof(kmac_param.hmac_512.key));
kmac_param.hmac_512.imbl = in_size * 8;
break;
default:
rc = -EINVAL;
goto out;
}
break;
case PKEY_KEYTYPE_HMAC_512:
fc = CPACF_KMAC_HMAC_ENCRYPTED_SHA_256;
if (key_size != sizeof(kmac_param.hmac_256_enc.protkey)) {
rc = -EINVAL;
goto out;
}
memcpy(kmac_param.hmac_256_enc.protkey, key,
sizeof(kmac_param.hmac_256_enc.protkey));
kmac_param.hmac_256_enc.imbl = in_size * 8;
break;
case PKEY_KEYTYPE_HMAC_1024:
fc = CPACF_KMAC_HMAC_ENCRYPTED_SHA_512;
if (key_size != sizeof(kmac_param.hmac_512_enc.protkey)) {
rc = -EINVAL;
goto out;
}
memcpy(kmac_param.hmac_512_enc.protkey, key,
sizeof(kmac_param.hmac_512_enc.protkey));
kmac_param.hmac_512_enc.imbl = in_size * 8;
break;
default:
rc = -EINVAL;
goto out;
}
if (!cpacf_query_kmac(fc)) {
rc = -ENODEV;
goto out;
}
cc = cpacf_kmac(fc, &kmac_param, in, in_size);
if (cc == 1) /* WKVP mismatch */
rc = -EAGAIN;
else if (cc != 0)
rc = -EIO;
if (rc != 0)
goto out;
switch (pkey_type) {
case 0:
/* clear key */
switch (key_size) {
case 64:
memcpy(mac, kmac_param.hmac_256.h,
MIN(mac_size, sizeof(kmac_param.hmac_256.h)));
break;
case 128:
memcpy(mac, kmac_param.hmac_512.h,
MIN(mac_size, sizeof(kmac_param.hmac_512.h)));
break;
default:
rc = -EINVAL;
goto out;
}
break;
case PKEY_KEYTYPE_HMAC_512:
memcpy(mac, kmac_param.hmac_256_enc.h,
MIN(mac_size, sizeof(kmac_param.hmac_256_enc.h)));
break;
case PKEY_KEYTYPE_HMAC_1024:
memcpy(mac, kmac_param.hmac_512_enc.h,
MIN(mac_size, sizeof(kmac_param.hmac_512_enc.h)));
break;
default:
rc = -EINVAL;
goto out;
}
out:
OPENSSL_cleanse(&kmac_param, sizeof(kmac_param));
return rc;
}