Files
s390-tools/zkey/cca.c
Ingo Franzki 33fc534a81 zkey: Adjust CCA host library version detection for newer CCA versions
Newer CCA versions might report the version string with CSUACFV or CSUACFQ
with keyword STATCCA using a different indicator character after the
version information. Ignore the indication character and the remaining
data entirely. Only the version information as such is of interest.

Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-04-16 12:12:45 +02:00

1044 lines
31 KiB
C

/*
* zkey - Generate, re-encipher, and validate secure keys
*
* Copyright IBM Corp. 2019
*
* 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 <dlfcn.h>
#include <err.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
#include "lib/util_base.h"
#include "lib/util_libc.h"
#include "lib/util_panic.h"
#include "cca.h"
#include "pkey.h"
#include "utils.h"
#define pr_verbose(verbose, fmt...) do { \
if (verbose) \
warnx(fmt); \
} while (0)
/*
* Definitions for the CCA library
*/
#define CCA_LIBRARY_NAME "libcsulcca.so"
#define CCA_WEB_PAGE "http://www.ibm.com/security/cryptocards"
#define CCA_DOMAIN_ENVAR "CSU_DEFAULT_DOMAIN"
#define CCA_ADAPTER_ENVAR "CSU_DEFAULT_ADAPTER"
/**
* Prints CCA return and reason code information for certain known CCA
* error situations.
*
* @param return_code the CCA return code
* @param reason_code the CCA reason code
*/
static void print_CCA_error(int return_code, int reason_code)
{
switch (return_code) {
case 8:
switch (reason_code) {
case 48:
warnx("The secure key has a CCA master key "
"verification pattern that is not valid");
break;
case 90:
warnx("The operation has been rejected due to access "
"control checking");
break;
case 2143:
warnx("The operation has been rejected due to key "
"export restrictions of the secure key");
break;
}
break;
case 12:
switch (reason_code) {
case 764:
warnx("The CCA master key is not loaded and "
"therefore a secure key cannot be enciphered");
break;
}
break;
}
}
/**
* Returns the version, release and modification number of the used CCA library.
*
* @param[in] cca the CCA library structure
* @param[in] verbose if true, verbose messages are printed
*
* @returns 0 on success, a negative errno in case of an error
*/
static int get_cca_version(struct cca_lib *cca, bool verbose)
{
unsigned char exit_data[4] = { 0, };
unsigned char version_data[20];
long return_code, reason_code;
long version_data_length;
long exit_data_len = 0;
util_assert(cca != NULL, "Internal error: cca is NULL");
memset(version_data, 0, sizeof(version_data));
version_data_length = sizeof(version_data);
cca->dll_CSUACFV(&return_code, &reason_code,
&exit_data_len, exit_data,
&version_data_length, version_data);
pr_verbose(verbose, "CSUACFV (Cryptographic Facility Version) "
"returned: return_code: %ld, reason_code: %ld", return_code,
reason_code);
if (return_code != 0) {
print_CCA_error(return_code, reason_code);
return -EIO;
}
version_data[sizeof(version_data) - 1] = '\0';
pr_verbose(verbose, "CCA Version string: %s", version_data);
if (sscanf((char *)version_data, "%u.%u.%u", &cca->version.ver,
&cca->version.rel, &cca->version.mod) != 3) {
warnx("CCA library version is invalid: %s", version_data);
return -EINVAL;
}
return 0;
}
/**
* Loads the CCA library and provides the entry point of the CSNBKTC function.
*
* @param[out] cca on return this contains the address of the CCA
* library and certain CCA symbols. dlclose() should
* be used to free the library when no longer needed.
* @param verbose if true, verbose messages are printed
*
* @returns 0 on success, -ELIBACC in case of library load errors
*/
int load_cca_library(struct cca_lib *cca, bool verbose)
{
util_assert(cca != NULL, "Internal error: caa is NULL");
/* Load the CCA library */
cca->lib_csulcca = dlopen(CCA_LIBRARY_NAME, RTLD_GLOBAL | RTLD_NOW);
if (cca->lib_csulcca == NULL) {
pr_verbose(verbose, "%s", dlerror());
warnx("The command requires the IBM CCA Host Libraries and "
"Tools.\nFor the supported environments and downloads, "
"see:\n%s", CCA_WEB_PAGE);
return -ELIBACC;
}
/* Get the Cryptographic Facility Version function */
cca->dll_CSUACFV = (t_CSUACFV)dlsym(cca->lib_csulcca, "CSUACFV");
/* Get the Key Token Change function */
cca->dll_CSNBKTC = (t_CSNBKTC)dlsym(cca->lib_csulcca, "CSNBKTC");
/* Get the Key Token Change 2 function */
cca->dll_CSNBKTC2 = (t_CSNBKTC2)dlsym(cca->lib_csulcca, "CSNBKTC2");
/* Get the Cryptographic Facility Query function */
cca->dll_CSUACFQ = (t_CSUACFQ)dlsym(cca->lib_csulcca, "CSUACFQ");
/* Get the Cryptographic Resource Allocate function */
cca->dll_CSUACRA = (t_CSUACRA)dlsym(cca->lib_csulcca, "CSUACRA");
/* Cryptographic Resource Deallocate function */
cca->dll_CSUACRD = (t_CSUACRD)dlsym(cca->lib_csulcca, "CSUACRD");
/* Get the Key Token Build 2 function */
cca->dll_CSNBKTB2 = (t_CSNBKTB2)dlsym(cca->lib_csulcca, "CSNBKTB2");
/* Get the Key Translate 2 function */
cca->dll_CSNBKTR2 = (t_CSNBKTR2)dlsym(cca->lib_csulcca, "CSNBKTR2");
/* Get the Restrict Key Attribute function */
cca->dll_CSNBRKA = (t_CSNBRKA)dlsym(cca->lib_csulcca, "CSNBRKA");
if (cca->dll_CSUACFV == NULL ||
cca->dll_CSNBKTC == NULL ||
cca->dll_CSNBKTC2 == NULL ||
cca->dll_CSUACFQ == NULL ||
cca->dll_CSUACRA == NULL ||
cca->dll_CSUACRD == NULL ||
cca->dll_CSNBKTB2 == NULL ||
cca->dll_CSNBKTR2 == NULL ||
cca->dll_CSNBRKA == NULL) {
pr_verbose(verbose, "%s", dlerror());
warnx("The command requires the IBM CCA Host Libraries and "
"Tools.\nFor the supported environments and downloads, "
"see:\n%s", CCA_WEB_PAGE);
dlclose(cca->lib_csulcca);
cca->lib_csulcca = NULL;
return -ELIBACC;
}
pr_verbose(verbose, "CCA library '%s' has been loaded successfully",
CCA_LIBRARY_NAME);
return get_cca_version(cca, verbose);
}
/**
* Re-enciphers a secure key.
*
* @param[in] cca the CCA libraray structure
* @param[in] secure_key a buffer containing the secure key
* @param[in] secure_key_size the size of the secure key
* @param[in] method the re-enciphering method. METHOD_OLD_TO_CURRENT
* or METHOD_CURRENT_TO_NEW.
* @param[in] verbose if true, verbose messages are printed
*
* @returns 0 on success, -EIO in case of an error
*/
int key_token_change(struct cca_lib *cca,
u8 *secure_key, unsigned int secure_key_size,
char *method, bool verbose)
{
struct aescipherkeytoken *cipherkey =
(struct aescipherkeytoken *)secure_key;
long exit_data_len = 0, rule_array_count;
unsigned char rule_array[2 * 8] = { 0, };
unsigned char exit_data[4] = { 0, };
long return_code, reason_code;
long key_token_length;
util_assert(cca != NULL, "Internal error: cca is NULL");
util_assert(secure_key != NULL, "Internal error: secure_key is NULL");
util_assert(secure_key_size > 0,
"Internal error: secure_key_size is 0");
util_assert(method != NULL, "Internal error: method is NULL");
memcpy(rule_array, method, 8);
memcpy(rule_array + 8, "AES ", 8);
rule_array_count = 2;
if (is_cca_aes_data_key(secure_key, secure_key_size)) {
cca->dll_CSNBKTC(&return_code, &reason_code,
&exit_data_len, exit_data,
&rule_array_count, rule_array,
secure_key);
pr_verbose(verbose, "CSNBKTC (Key Token Change) with '%s' "
"returned: return_code: %ld, reason_code: %ld",
method, return_code, reason_code);
} else if (is_cca_aes_cipher_key(secure_key, secure_key_size)) {
key_token_length = cipherkey->length;
cca->dll_CSNBKTC2(&return_code, &reason_code,
&exit_data_len, exit_data,
&rule_array_count, rule_array,
&key_token_length,
(unsigned char *)cipherkey);
pr_verbose(verbose, "CSNBKTC2 (Key Token Change2) with '%s' "
"returned: return_code: %ld, reason_code: %ld",
method, return_code, reason_code);
pr_verbose(verbose, "key_token_length: %lu", key_token_length);
} else {
warnx("Invalid key type specified");
return -EINVAL;
}
if (return_code != 0) {
print_CCA_error(return_code, reason_code);
return -EIO;
}
if (is_xts_key(secure_key, secure_key_size)) {
if (is_cca_aes_data_key(secure_key, secure_key_size)) {
cca->dll_CSNBKTC(&return_code, &reason_code,
&exit_data_len, exit_data,
&rule_array_count, rule_array,
secure_key + AESDATA_KEY_SIZE);
pr_verbose(verbose, "CSNBKTC (Key Token Change) with "
"'%s' returned: return_code: %ld, "
"reason_code: %ld", method, return_code,
reason_code);
} else if (is_cca_aes_cipher_key(secure_key, secure_key_size)) {
cipherkey = (struct aescipherkeytoken *)(secure_key +
AESCIPHER_KEY_SIZE);
key_token_length = cipherkey->length;
cca->dll_CSNBKTC2(&return_code, &reason_code,
&exit_data_len, exit_data,
&rule_array_count, rule_array,
&key_token_length,
(unsigned char *)cipherkey);
pr_verbose(verbose, "CSNBKTC2 (Key Token Change2) with "
"'%s' returned: return_code: %ld, "
"reason_code: %ld", method, return_code,
reason_code);
pr_verbose(verbose, "key_token_length: %lu",
key_token_length);
} else {
warnx("Invalid key type specified");
return -EINVAL;
}
if (return_code != 0) {
print_CCA_error(return_code, reason_code);
return -EIO;
}
}
return 0;
}
/**
* Queries the number of adapters known by the CCA host library
*
* @param[in] cca the CCA library structure
* @param[out] adapters the number of adapters
* @param[in] verbose if true, verbose messages are printed
*
* @returns 0 on success, a negative errno in case of an error.
*/
static int get_number_of_cca_adapters(struct cca_lib *cca,
unsigned int *adapters, bool verbose)
{
long exit_data_len = 0, rule_array_count, verb_data_length = 0;
unsigned char rule_array[16 * 8] = { 0, };
unsigned char exit_data[4] = { 0, };
long return_code, reason_code;
util_assert(cca != NULL, "Internal error: cca is NULL");
util_assert(adapters != NULL, "Internal error: adapters is NULL");
memset(rule_array, 0, sizeof(rule_array));
memcpy(rule_array, "STATCRD2", 8);
rule_array_count = 1;
cca->dll_CSUACFQ(&return_code, &reason_code,
&exit_data_len, exit_data,
&rule_array_count, rule_array,
&verb_data_length, NULL);
pr_verbose(verbose, "CSUACFQ (Cryptographic Facility Query) returned: "
"return_code: %ld, reason_code: %ld", return_code,
reason_code);
if (return_code != 0) {
print_CCA_error(return_code, reason_code);
return -EIO;
}
rule_array[8] = '\0';
if (sscanf((char *)rule_array, "%u", adapters) != 1) {
pr_verbose(verbose, "Unparsable output: %s", rule_array);
return -EIO;
}
pr_verbose(verbose, "Number of CCA adapters: %u", *adapters);
return 0;
}
/**
* Allocate a specific CCA adapter.
*
* @param[in] cca the CCA library structure
* @param[in] adapter the adapter number, starting at 1. If 0 is
* specified, then the AUTOSELECT option is
* enabled.
* @param[in] verbose if true, verbose messages are printed
*
* @returns 0 on success, a negative errno in case of an error. -ENODEV is
* returned if the adapter is not available.
*/
static int allocate_cca_adapter(struct cca_lib *cca, unsigned int adapter,
bool verbose)
{
long exit_data_len = 0, rule_array_count;
unsigned char rule_array[8] = { 0, };
unsigned char exit_data[4] = { 0, };
long return_code, reason_code;
char res_name[9];
long res_name_len;
util_assert(cca != NULL, "Internal error: cca is NULL");
if (adapter > 0)
memcpy(rule_array, "DEVICE ", 8);
else
memcpy(rule_array, "DEV-ANY ", 8);
rule_array_count = 1;
sprintf(res_name, "CRP%02d", adapter);
res_name_len = strlen(res_name);
cca->dll_CSUACRA(&return_code, &reason_code,
&exit_data_len, exit_data,
&rule_array_count, rule_array,
&res_name_len, (unsigned char *)res_name);
pr_verbose(verbose, "CSUACRA (Cryptographic Resource Allocate) "
"returned: return_code: %ld, reason_code: %ld", return_code,
reason_code);
if (return_code != 0) {
print_CCA_error(return_code, reason_code);
return -ENODEV;
}
pr_verbose(verbose, "Adapter %u (%s) allocated", adapter, res_name);
return 0;
}
/**
* Deallocate a specific CCA adapter.
*
* @param[in] cca the CCA library structure
* @param[in] adapter the adapter number, starting at 1. If 0 is
* specified, then the AUTOSELECT option is
* disabled.
* @param[in] verbose if true, verbose messages are printed
*
* @returns 0 on success, a negative errno in case of an error. -ENODEV is
* returned if the adapter is not available.
*/
static int deallocate_cca_adapter(struct cca_lib *cca, unsigned int adapter,
bool verbose)
{
long exit_data_len = 0, rule_array_count;
unsigned char rule_array[8] = { 0, };
unsigned char exit_data[4] = { 0, };
long return_code, reason_code;
char res_name[9];
long res_name_len;
util_assert(cca != NULL, "Internal error: cca is NULL");
if (adapter > 0)
memcpy(rule_array, "DEVICE ", 8);
else
memcpy(rule_array, "DEV-ANY ", 8);
rule_array_count = 1;
sprintf(res_name, "CRP%02d", adapter);
res_name_len = strlen(res_name);
cca->dll_CSUACRD(&return_code, &reason_code,
&exit_data_len, exit_data,
&rule_array_count, rule_array,
&res_name_len, (unsigned char *)res_name);
pr_verbose(verbose, "CSUACRD (Cryptographic Resource Deallocate) "
"returned: return_code: %ld, reason_code: %ld", return_code,
reason_code);
if (return_code != 0) {
print_CCA_error(return_code, reason_code);
return -ENODEV;
}
pr_verbose(verbose, "Adapter %u (%s) deallocated", adapter, res_name);
return 0;
}
/**
* Queries the serial number of the current CCA adapter
*
* @param[in] cca the CCA library structure
* @param[out] serialnr the buffer where the serial number is returned
* @param[in] verbose if true, verbose messages are printed
*
* @returns 0 on success, a negative errno in case of an error.
*/
static int get_cca_adapter_serialnr(struct cca_lib *cca, char serialnr[9],
bool verbose)
{
long exit_data_len = 0, rule_array_count, verb_data_length = 0;
unsigned char rule_array[16 * 8] = { 0, };
unsigned char exit_data[4] = { 0, };
long return_code, reason_code;
util_assert(cca != NULL, "Internal error: cca is NULL");
memset(rule_array, 0, sizeof(rule_array));
memcpy(rule_array, "STATCRD2", 8);
rule_array_count = 1;
cca->dll_CSUACFQ(&return_code, &reason_code,
&exit_data_len, exit_data,
&rule_array_count, rule_array,
&verb_data_length, NULL);
pr_verbose(verbose, "CSUACFQ (Cryptographic Facility Query) returned: "
"return_code: %ld, reason_code: %ld", return_code,
reason_code);
if (return_code != 0) {
print_CCA_error(return_code, reason_code);
return -EIO;
}
memcpy(serialnr, rule_array+14*8, 8);
serialnr[8] = '\0';
pr_verbose(verbose, "Serial number of CCA adapter: %s", serialnr);
return 0;
}
/**
* Queries the firmware version of the current CCA adapter
*
* @param[in] cca the CCA library structure
* @param[out] version the struct where the version is returned
* @param[in] verbose if true, verbose messages are printed
*
* @returns 0 on success, a negative errno in case of an error.
*/
static int get_cca_adapter_version(struct cca_lib *cca,
struct cca_version *version,
bool verbose)
{
long exit_data_len = 0, rule_array_count, verb_data_length = 0;
unsigned char rule_array[6 * 8] = { 0, };
unsigned char exit_data[4] = { 0, };
long return_code, reason_code;
char version_data[9];
util_assert(cca != NULL, "Internal error: cca is NULL");
memset(rule_array, 0, sizeof(rule_array));
memcpy(rule_array, "STATCCA ", 8);
rule_array_count = 1;
cca->dll_CSUACFQ(&return_code, &reason_code,
&exit_data_len, exit_data,
&rule_array_count, rule_array,
&verb_data_length, NULL);
pr_verbose(verbose, "CSUACFQ (Cryptographic Facility Query) returned: "
"return_code: %ld, reason_code: %ld", return_code,
reason_code);
if (return_code != 0) {
print_CCA_error(return_code, reason_code);
return -EIO;
}
memcpy(version_data, rule_array+3*8, 8);
version_data[8] = '\0';
pr_verbose(verbose, "CCA firmware version string: %s", version_data);
if (sscanf((char *)version_data, "%u.%u.%u", &version->ver,
&version->rel, &version->mod) != 3) {
warnx("CCA formware version is invalid: %s", version_data);
return -EINVAL;
}
return 0;
}
/**
* Selects the specified APQN to be used for the CCA host library.
*
* @param[in] cca the CCA library structure
* @param[in] card the card number
* @param[in] domain the domain number
* @param[in] verbose if true, verbose messages are printed
*
* @returns 0 on success, a negative errno in case of an error. -ENOTSUP is
* returned when the serialnr sysfs attribute is not available,
* because the zcrypt kernel module is on an older level. -ENODEV is
* returned if the APQN is not available.
*/
int select_cca_adapter(struct cca_lib *cca, unsigned int card,
unsigned int domain, bool verbose)
{
unsigned int adapters, adapter;
char adapter_serialnr[9];
char apqn_serialnr[SERIALNR_LENGTH];
char temp[10];
int rc, found = 0;
util_assert(cca != NULL, "Internal error: cca is NULL");
pr_verbose(verbose, "Select %02x.%04x for the CCA host library", card,
domain);
rc = sysfs_get_serialnr(card, apqn_serialnr, verbose);
if (rc != 0) {
pr_verbose(verbose, "Failed to get the serial number: %s",
strerror(-rc));
return rc;
}
sprintf(temp, "%u", domain);
if (setenv(CCA_DOMAIN_ENVAR, temp, 1) != 0) {
rc = -errno;
pr_verbose(verbose, "Failed to set the %s environment variable:"
" %s", CCA_DOMAIN_ENVAR, strerror(-rc));
return rc;
}
unsetenv(CCA_ADAPTER_ENVAR);
/*
* Unload and reload the CCA host library so that it recognizes the
* changed CSU_DEFAULT_DOMAIN environment variable value.
*/
if (cca->lib_csulcca != NULL)
dlclose(cca->lib_csulcca);
memset(cca, 0, sizeof(struct cca_lib));
rc = load_cca_library(cca, verbose);
if (rc != 0)
return rc;
rc = get_number_of_cca_adapters(cca, &adapters, verbose);
if (rc != 0)
return rc;
/* Deallocate any adapter first, in case one is already allocated */
for (adapter = 1; adapter <= adapters; adapter++)
deallocate_cca_adapter(cca, adapter, false);
/* Disable the AUTOSELECT option */
rc = deallocate_cca_adapter(cca, 0, verbose);
if (rc != 0)
return rc;
for (adapter = 1; adapter <= adapters; adapter++) {
rc = allocate_cca_adapter(cca, adapter, verbose);
if (rc != 0)
return rc;
rc = get_cca_adapter_serialnr(cca, adapter_serialnr, verbose);
if (rc == 0) {
if (memcmp(apqn_serialnr, adapter_serialnr, 8) == 0) {
found = 1;
break;
}
}
rc = deallocate_cca_adapter(cca, adapter, verbose);
if (rc != 0)
return rc;
}
if (!found)
return -ENODEV;
pr_verbose(verbose, "Selected adapter %u (CRP%02d)", adapter, adapter);
return 0;
}
struct find_mkvp_info {
u8 mkvp[MKVP_LENGTH];
unsigned int flags;
bool found;
unsigned int card;
unsigned int domain;
bool verbose;
};
static int find_mkvp(unsigned int card, unsigned int domain, void *handler_data)
{
struct find_mkvp_info *info = (struct find_mkvp_info *)handler_data;
struct mk_info mk_info;
bool found = false;
int rc;
rc = sysfs_get_mkvps(card, domain, &mk_info, info->verbose);
if (rc == -ENODEV)
return 0;
if (rc != 0)
return rc;
if (info->flags & FLAG_SEL_CCA_MATCH_CUR_MKVP)
if (mk_info.cur_mk.mk_state == MK_STATE_VALID &&
MKVP_EQ(mk_info.cur_mk.mkvp, info->mkvp))
found = true;
if (info->flags & FLAG_SEL_CCA_MATCH_OLD_MKVP)
if (mk_info.old_mk.mk_state == MK_STATE_VALID &&
MKVP_EQ(mk_info.old_mk.mkvp, info->mkvp))
found = true;
if (info->flags & FLAG_SEL_CCA_NEW_MUST_BE_SET)
if (mk_info.new_mk.mk_state != MK_STATE_FULL)
found = false;
if (found) {
info->card = card;
info->domain = domain;
info->found = true;
pr_verbose(info->verbose, "%02x.%04x has the desired mkvp%s",
card, domain,
info->flags & FLAG_SEL_CCA_NEW_MUST_BE_SET ?
" and NEW MK set" : "");
return 1;
}
return 0;
}
/**
* Selects an APQN to be used for the CCA host library that has the specified
* master key verification pattern
*
* @param[in] cca the CCA library structure
* @param[in] mkvp the master key verification pattern to search for
* @param[in] apqns a comma separated list of APQNs. If NULL is specified,
* or an empty string, then all online CCA APQNs are
* checked.
* @param[in] flags Flags that control the MKVM matching and NEW register
* checking. Multiple flags can be combined.
* @param[in] verbose if true, verbose messages are printed
*
* @returns 0 on success, a negative errno in case of an error. -ENOTSUP is
* returned when the serialnr sysfs attribute is not available,
* because the zcrypt kernel module is on an older level. -ENODEV is
* returned if no APQN is available with the desired mkvp.
*/
int select_cca_adapter_by_mkvp(struct cca_lib *cca, u8 *mkvp, const char *apqns,
unsigned int flags, bool verbose)
{
struct find_mkvp_info info;
int rc;
util_assert(cca != NULL, "Internal error: cca is NULL");
util_assert(mkvp != NULL, "Internal error: mkvp is NULL");
pr_verbose(verbose, "Select mkvp %s in APQNs %s for the CCA host "
"library", printable_mkvp(CARD_TYPE_CCA, mkvp),
apqns == NULL ? "ANY" : apqns);
memcpy(info.mkvp, mkvp, sizeof(info.mkvp));
info.flags = flags;
info.found = false;
info.card = 0;
info.domain = 0;
info.verbose = verbose;
rc = handle_apqns(apqns, CARD_TYPE_CCA, find_mkvp, &info, verbose);
if (rc < 0)
return rc;
if (!info.found)
return -ENODEV;
rc = select_cca_adapter(cca, info.card, info.domain, verbose);
return rc;
}
void print_msg_for_cca_envvars(const char *key_name)
{
char *msg;
util_asprintf(&msg, "WARNING: You must set environment variables "
"%s and %s to the desired card and domain that is "
"set up with the AES master key used by this %s. "
"%s specifies the domain as decimal number. %s "
"specifies the adapter number as 'CRPnn', where "
"'nn' is the adapter number. See the CCA "
"documentation for more details.\n",
CCA_DOMAIN_ENVAR, CCA_ADAPTER_ENVAR, key_name,
CCA_DOMAIN_ENVAR, CCA_ADAPTER_ENVAR);
util_print_indented(msg, 0);
free(msg);
}
/*
* Convert a secure key of type CCA-AESDATA into a secure key of type
* CCA-AESCIPHER.
*
* @param[in] cca the CCA library structure
* @param[in] input_key the secure key to convert
* @param[in] input_key_size the size of the secure key to convert
* @param[in] output_key buffer for the converted secure key
* @param[in/out] output_key_size on input: size of the output buffer.
* on exit: size of the converted secure key
* @param[in] verbose if true, verbose messages are printed
*
* @returns 0 on success, a negative errno in case of an error.
*/
int convert_aes_data_to_cipher_key(struct cca_lib *cca,
u8 *input_key, unsigned int input_key_size,
u8 *output_key,
unsigned int *output_key_size,
bool verbose)
{
long input_token_size, output_token_size, zero = 0;
long exit_data_len = 0, rule_array_count = 0;
unsigned char *input_token, *output_token;
unsigned char rule_array[8 * 4] = { 0 };
unsigned char null_token[64] = { 0, };
long null_token_len = sizeof(null_token);
unsigned char exit_data[4] = { 0, };
struct aescipherkeytoken *cipherkey;
long return_code, reason_code;
struct cca_version version;
unsigned char buffer[800];
int rc;
util_assert(cca != NULL, "Internal error: cca is NULL");
util_assert(input_key != NULL, "Internal error: input_key is NULL");
util_assert(output_key != NULL, "Internal error: output_key is NULL");
util_assert(output_key_size != NULL,
"Internal error: output_key_size is NULL");
if (is_cca_aes_cipher_key(input_key, input_key_size)) {
warnx("Invalid key-type specified");
return -EINVAL;
}
if (*output_key_size < (is_xts_key(input_key, input_key_size) ?
2 * AESCIPHER_KEY_SIZE : AESCIPHER_KEY_SIZE))
return -EINVAL;
/*
* We need a CCA firmware version 6.3.27 or later to support
* conversion of secure keys that are exportable to CPACF protected keys
*/
rc = get_cca_adapter_version(cca, &version, verbose);
if (rc != 0)
return rc;
if (version.ver < 6 ||
(version.ver == 6 && version.rel < 3) ||
(version.ver == 6 && version.rel < 3 && version.mod < 27)) {
util_print_indented("The used CCA firmware version does not "
"support converting a secure key that can "
"be used with the PAES cipher. The "
"required CCA firmware version is 6.3.27 "
"or later. For the supported environments "
"and updates, see: " CCA_WEB_PAGE, 0);
return -ENOTSUP;
}
input_token = input_key;
input_token_size = AESDATA_KEY_SIZE;
output_token = buffer;
output_token_size = sizeof(buffer);
memset(buffer, 0, sizeof(buffer));
memcpy(rule_array, "INTERNAL", 8);
memcpy(rule_array + 8, "AES ", 8);
memcpy(rule_array + 16, "XPRTCPAC", 8);
memcpy(rule_array + 24, "CIPHER ", 8);
rule_array_count = 4;
cca->dll_CSNBKTB2(&return_code, &reason_code,
&exit_data_len, exit_data,
&rule_array_count, rule_array,
&zero, NULL, &zero, NULL,
&zero, NULL, &zero, NULL,
&zero, NULL,
&output_token_size, output_token);
pr_verbose(verbose, "CSNBKTB2 (Key Token Build2) "
"returned: return_code: %ld, reason_code: %ld", return_code,
reason_code);
if (return_code != 0) {
print_CCA_error(return_code, reason_code);
return -EIO;
}
memcpy(rule_array, "AES ", 8);
memcpy(rule_array + 8, "REFORMAT", 8);
rule_array_count = 2;
output_token_size = sizeof(buffer);
cca->dll_CSNBKTR2(&return_code, &reason_code,
&exit_data_len, exit_data,
&rule_array_count, rule_array,
&input_token_size, input_token,
&null_token_len, null_token,
&zero, NULL,
&output_token_size, output_token);
pr_verbose(verbose, "CSNBKTR2 (Key Translate2) "
"returned: return_code: %ld, reason_code: %ld", return_code,
reason_code);
if (return_code != 0) {
print_CCA_error(return_code, reason_code);
return -EIO;
}
pr_verbose(verbose, "output_token_size: %lu", output_token_size);
if (output_token_size > (long)AESCIPHER_KEY_SIZE) {
pr_verbose(verbose, "Output key token too large");
return -EINVAL;
}
/*
* Check if the converted key allows export to CPACF protected key.
* If not, then the CCA host library or firmware code level is too low.
*/
cipherkey = (struct aescipherkeytoken *)buffer;
if ((cipherkey->kmf1 & 0x0800) == 0) {
util_print_indented("The used CCA firmware version does not "
"support converting a secure key that can "
"be used with the PAES cipher. The "
"required CCA firmware version is 6.3.27 "
"or later. For the supported environments "
"and updates, see: " CCA_WEB_PAGE, 0);
return -ENOTSUP;
}
memset(output_key, 0, *output_key_size);
memcpy(output_key, buffer, output_token_size);
*output_key_size = AESCIPHER_KEY_SIZE;
if (is_xts_key(input_key, input_key_size)) {
input_token = input_key + AESDATA_KEY_SIZE;
input_token_size = AESDATA_KEY_SIZE;
output_token = buffer;
output_token_size = sizeof(buffer);
memset(buffer, 0, sizeof(buffer));
memcpy(rule_array, "INTERNAL", 8);
memcpy(rule_array + 8, "AES ", 8);
memcpy(rule_array + 16, "XPRTCPAC", 8);
memcpy(rule_array + 24, "CIPHER ", 8);
rule_array_count = 4;
cca->dll_CSNBKTB2(&return_code, &reason_code,
&exit_data_len, exit_data,
&rule_array_count, rule_array,
&zero, NULL, &zero, NULL,
&zero, NULL, &zero, NULL,
&zero, NULL,
&output_token_size, output_token);
pr_verbose(verbose, "CSNBKTB2 (Key Token Build2) "
"returned: return_code: %ld, reason_code: %ld",
return_code, reason_code);
if (return_code != 0) {
print_CCA_error(return_code, reason_code);
return -EIO;
}
memcpy(rule_array, "AES ", 8);
memcpy(rule_array + 8, "REFORMAT", 8);
rule_array_count = 2;
output_token_size = sizeof(buffer);
cca->dll_CSNBKTR2(&return_code, &reason_code,
&exit_data_len, exit_data,
&rule_array_count, rule_array,
&input_token_size, input_token,
&null_token_len, null_token,
&zero, NULL,
&output_token_size, output_token);
pr_verbose(verbose, "CSNBKTR2 (Key Translate2) "
"returned: return_code: %ld, reason_code: %ld",
return_code, reason_code);
if (return_code != 0) {
print_CCA_error(return_code, reason_code);
return -EIO;
}
pr_verbose(verbose, "output_token_size: %lu",
output_token_size);
if (output_token_size > (long)AESCIPHER_KEY_SIZE) {
pr_verbose(verbose, "Output key token too large");
return -EINVAL;
}
memcpy(output_key + AESCIPHER_KEY_SIZE, buffer,
output_token_size);
*output_key_size += AESCIPHER_KEY_SIZE;
}
return 0;
}
/*
* Restrict the exportability of an AES CIPHER key. It restricts export by means
* of NOEX-AES, NOEX-DES, NOEX-RSA, NOEX-SYM, NOEXUASY, NOEXAASY, NOEX-RAW
* keywords.
* When this function is called with an AES DATA key, it does nothing and
* returns 0. AES DATA keys can not be export restricted.
*
* @param[in] cca the CCA library structure
* @param[in] secure_key the secure key to restrict
* @param[in] secure_key_size the size of the secure key to restrict
* @param[in] verbose if true, verbose messages are printed
*
* @returns 0 on success, a negative errno in case of an error.
*/
int restrict_key_export(struct cca_lib *cca, u8 *secure_key,
unsigned int secure_key_size, bool verbose)
{
struct aescipherkeytoken *cipherkey =
(struct aescipherkeytoken *)secure_key;
long exit_data_len = 0, rule_array_count = 0;
unsigned char rule_array[8 * 8] = { 0, };
unsigned char exit_data[4] = { 0, };
long return_code, reason_code;
long token_length, zero = 0;
util_assert(cca != NULL, "Internal error: cca is NULL");
util_assert(secure_key != NULL, "Internal error: secure_key is NULL");
if (!is_cca_aes_cipher_key(secure_key, secure_key_size))
return 0;
memcpy(rule_array, "AES ", 8);
memcpy(rule_array + 8, "NOEX-AES", 8);
memcpy(rule_array + 16, "NOEX-DES", 8);
memcpy(rule_array + 24, "NOEX-RSA", 8);
memcpy(rule_array + 32, "NOEX-SYM", 8);
memcpy(rule_array + 40, "NOEXUASY", 8);
memcpy(rule_array + 48, "NOEXAASY", 8);
memcpy(rule_array + 56, "NOEX-RAW", 8);
rule_array_count = 8;
token_length = cipherkey->length;
cca->dll_CSNBRKA(&return_code, &reason_code,
&exit_data_len, exit_data,
&rule_array_count, rule_array,
&token_length, (unsigned char *)secure_key,
&zero, NULL, &zero, NULL, &zero, NULL);
pr_verbose(verbose, "CSNBRKA (Restrict Key Attribute) "
"returned: return_code: %ld, reason_code: %ld", return_code,
reason_code);
if (return_code != 0) {
print_CCA_error(return_code, reason_code);
return -EIO;
}
if (is_xts_key(secure_key, secure_key_size)) {
cipherkey = (struct aescipherkeytoken *)(secure_key +
AESCIPHER_KEY_SIZE);
token_length = cipherkey->length;
cca->dll_CSNBRKA(&return_code, &reason_code,
&exit_data_len, exit_data,
&rule_array_count, rule_array,
&token_length, (unsigned char *)cipherkey,
&zero, NULL, &zero, NULL, &zero, NULL);
pr_verbose(verbose, "CSNBRKA (Restrict Key Attribute) "
"returned: return_code: %ld, reason_code: %ld",
return_code, reason_code);
if (return_code != 0) {
print_CCA_error(return_code, reason_code);
return -EIO;
}
}
return 0;
}