Files
s390-tools/zkey/cca.c
Ingo Franzki e24629b977 zkey-kmip: Configure APQNs
The KMIP plugin supports CCA and EP11 APQNs, but only it can only be
configured with one type. Once configured with one type of APQN, it accepts
only APQNs of the same type.

It supports key types matching to the APQn type it is configured with.

Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2021-07-07 13:53:06 +02:00

1045 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;
char date[20];
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.%uz%s", &cca->version.ver,
&cca->version.rel, &cca->version.mod, date) != 4) {
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.%uz", &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;
}