mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Normally, secure keys generated by zkey are intentionally export restricted. Export restricted keys can not be wrapped with a key encrypting key (KEK). However, keys that are generated inside a Secure Execution for Linux guest that shall also be used outside of the Secure Execution for Linux guest can only be transported to outside the Secure Execution for Linux guest by wrapping them with a KEK and unwrapping them outside of the Secure Execution for Linux guest. For that such keys must be exportable. Add an option to generate and import exportable secure keys, which then can be wrapped, and thus transported to outside of a Secure Execution for Linux guest. This applies to keys of type 'CCA-AESCIPHER' and 'EP11-AES'. Keys of type 'CCA-AESDATA' are always exportable, and can not be export restricted. For keys of type 'EP11-AES' additionally allow to set the 'wrap-with-trusted' attribute. This restricts the key so that it only can be wrapped with a trusted key encrypting key. For keys of type 'EP11-AES' to be exportable, the access control point (ACP) XCP_CPB_ALLOW_COMBINED_EXTRACT must be 'ON' on all APQNs used. This access control point is only supported on newer EP11 firmware levels. If the access control point is 'OFF' or not supported by the EP11 firmware, then the IOCTL to generate the key fails with a generic error (Input/output error - EIO). The zkey tool prints an appropriate error message in that case. Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com> Reviewed-by: Finn Callies <fcallies@linux.ibm.com> Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2413 lines
67 KiB
C
2413 lines
67 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 <dlfcn.h>
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <linux/if_alg.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#include "lib/util_base.h"
|
|
#include "lib/util_libc.h"
|
|
#include "lib/util_panic.h"
|
|
|
|
#include "pkey.h"
|
|
#include "pvsecrets.h"
|
|
#include "utils.h"
|
|
|
|
#ifndef AF_ALG
|
|
#define AF_ALG 38
|
|
#endif
|
|
#ifndef SOL_ALG
|
|
#define SOL_ALG 279
|
|
#endif
|
|
|
|
#define pr_verbose(verbose, fmt...) do { \
|
|
if (verbose) \
|
|
warnx(fmt); \
|
|
} while (0)
|
|
|
|
#define DOUBLE_KEYSIZE_FOR_XTS(keysize, xts) ((xts) ? 2 * (keysize) : (keysize))
|
|
#define HALF_KEYSIZE_FOR_XTS(keysize, xts) ((xts) ? (keysize) / 2 : (keysize))
|
|
|
|
#define MAX_CIPHER_LEN 32
|
|
|
|
#define INITIAL_APQN_ENTRIES 16
|
|
|
|
/**
|
|
* Opens the pkey device and returns its file descriptor.
|
|
*
|
|
* @param verbose if true, verbose messages are printed
|
|
*
|
|
* @returns the file descriptor or -1 to indicate an error
|
|
*/
|
|
int open_pkey_device(bool verbose)
|
|
{
|
|
int pkey_fd;
|
|
|
|
pkey_fd = open(PKEYDEVICE, O_RDWR);
|
|
if (pkey_fd < 0) {
|
|
warnx("File '%s:' %s\nEnsure that the 'pkey' kernel module "
|
|
"is loaded", PKEYDEVICE, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
pr_verbose(verbose, "Device '%s' has been opened successfully",
|
|
PKEYDEVICE);
|
|
return pkey_fd;
|
|
}
|
|
|
|
/**
|
|
* Read a secure key file and return the allocated buffer and size.
|
|
*
|
|
* @param[in] keyfile the name of the file to read
|
|
* @param[out] secure_key_size on return, the size of the secure key read
|
|
* @param[in] verbose if true, verbose messages are printed
|
|
*
|
|
* @return a buffer containing the secure key, or NULL in case of an error.
|
|
* The returned buffer must be freed by the caller.
|
|
*/
|
|
u8 *read_secure_key(const char *keyfile, size_t *secure_key_size,
|
|
bool verbose)
|
|
{
|
|
size_t count, size;
|
|
struct stat sb;
|
|
char *msg;
|
|
FILE *fp;
|
|
u8 *buf;
|
|
|
|
util_assert(keyfile != NULL, "Internal error: keyfile is NULL");
|
|
util_assert(secure_key_size != NULL,
|
|
"Internal error: secure_key_size is NULL");
|
|
|
|
if (stat(keyfile, &sb)) {
|
|
warnx("File '%s': %s", keyfile, strerror(errno));
|
|
return NULL;
|
|
}
|
|
size = sb.st_size;
|
|
|
|
if (size < MIN_SECURE_KEY_SIZE || size > 2 * MAX_SECURE_KEY_SIZE) {
|
|
warnx("File '%s' has an invalid size: %lu", keyfile, size);
|
|
return NULL;
|
|
}
|
|
|
|
fp = fopen(keyfile, "r");
|
|
if (fp == NULL) {
|
|
warnx("File '%s': %s", keyfile, strerror(errno));
|
|
return NULL;
|
|
}
|
|
|
|
buf = util_malloc(size);
|
|
count = fread(buf, 1, size, fp);
|
|
if (count != size) {
|
|
msg = ferror(fp) ? strerror(errno) : "File is too small";
|
|
warnx("File '%s': %s", keyfile, msg);
|
|
free(buf);
|
|
buf = NULL;
|
|
goto out;
|
|
}
|
|
|
|
*secure_key_size = size;
|
|
|
|
if (verbose) {
|
|
pr_verbose(verbose, "%lu bytes read from file '%s'", size,
|
|
keyfile);
|
|
util_hexdump_grp(stderr, NULL, buf, 4, size, 0);
|
|
}
|
|
out:
|
|
fclose(fp);
|
|
return buf;
|
|
}
|
|
|
|
/**
|
|
* Write a secure key file
|
|
*
|
|
* @param[in] keyfile the name of the file to write
|
|
* @param[in] secure_key a buffer containing the secure key
|
|
* @param[in] secure_key_size the size of the secure key
|
|
* @param[in] verbose if true, verbose messages are printed
|
|
*
|
|
* @returns 0 in case of success, -EIO in case of an error
|
|
*/
|
|
int write_secure_key(const char *keyfile, const u8 *secure_key,
|
|
size_t secure_key_size, bool verbose)
|
|
{
|
|
size_t count;
|
|
FILE *fp;
|
|
|
|
util_assert(keyfile != NULL, "Internal error: keyfile 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 zero");
|
|
|
|
fp = fopen(keyfile, "w");
|
|
if (fp == NULL) {
|
|
warnx("File '%s': %s", keyfile, strerror(errno));
|
|
return -EIO;
|
|
}
|
|
|
|
count = fwrite(secure_key, 1, secure_key_size, fp);
|
|
if (count != secure_key_size) {
|
|
warnx("File '%s': %s", keyfile, strerror(errno));
|
|
fclose(fp);
|
|
return -EIO;
|
|
}
|
|
|
|
if (verbose) {
|
|
pr_verbose(verbose, "%lu bytes written to file '%s'",
|
|
secure_key_size, keyfile);
|
|
util_hexdump_grp(stderr, NULL, secure_key, 4,
|
|
secure_key_size, 0);
|
|
}
|
|
fclose(fp);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Read a clear key file and return the allocated buffer and size
|
|
*
|
|
* @param[in] keyfile the name of the file to read
|
|
* @param[in] keybits the clear key size in bits. When keybits is 0, then
|
|
* the file size determines the keybits.
|
|
* @param[in] xts if true an XTS key is to be read
|
|
* @param[out] clear_key_size on return, the size of the clear key read
|
|
* @param[in] verbose if true, verbose messages are printed
|
|
*
|
|
* @return a buffer containing the clear key, or NULL in case of an error.
|
|
* The returned buffer must be freed by the caller.
|
|
*/
|
|
static u8 *read_clear_key(const char *keyfile, size_t keybits, bool xts,
|
|
size_t *clear_key_size, bool verbose)
|
|
{
|
|
size_t count, size, expected_size;
|
|
struct stat sb;
|
|
char *msg;
|
|
FILE *fp;
|
|
u8 *buf;
|
|
|
|
util_assert(keyfile != NULL, "Internal error: keyfile is NULL");
|
|
util_assert(clear_key_size != NULL,
|
|
"Internal error: clear_key_size is NULL");
|
|
|
|
if (stat(keyfile, &sb)) {
|
|
warnx("File '%s': %s", keyfile, strerror(errno));
|
|
return NULL;
|
|
}
|
|
size = sb.st_size;
|
|
|
|
if (keybits != 0) {
|
|
expected_size = DOUBLE_KEYSIZE_FOR_XTS(keybits / 8, xts);
|
|
if (size != expected_size) {
|
|
warnx("File '%s' has an invalid size, "
|
|
"%lu bytes expected", keyfile, expected_size);
|
|
return NULL;
|
|
}
|
|
} else {
|
|
keybits = HALF_KEYSIZE_FOR_XTS(size * 8, xts);
|
|
}
|
|
|
|
switch (keybits) {
|
|
case 128:
|
|
break;
|
|
case 192:
|
|
if (xts) {
|
|
warnx("File '%s' has an invalid size, "
|
|
"192 bit keys are not supported with XTS",
|
|
keyfile);
|
|
return NULL;
|
|
}
|
|
break;
|
|
case 256:
|
|
break;
|
|
default:
|
|
if (xts)
|
|
warnx("File '%s' has an invalid size, "
|
|
"32 or 64 bytes expected", keyfile);
|
|
else
|
|
warnx("File '%s' has an invalid size, 16, 24 "
|
|
"or 32 bytes expected", keyfile);
|
|
return NULL;
|
|
}
|
|
|
|
fp = fopen(keyfile, "r");
|
|
if (fp == NULL) {
|
|
warnx("File '%s': %s", keyfile, strerror(errno));
|
|
return NULL;
|
|
}
|
|
|
|
buf = util_malloc(size);
|
|
count = fread(buf, 1, size, fp);
|
|
if (count != size) {
|
|
msg = ferror(fp) ? strerror(errno) : "File is too small";
|
|
warnx("File '%s': %s", keyfile, msg);
|
|
free(buf);
|
|
buf = NULL;
|
|
goto out;
|
|
}
|
|
|
|
*clear_key_size = size;
|
|
|
|
if (verbose) {
|
|
pr_verbose(verbose, "%lu bytes read from file '%s'", size,
|
|
keyfile);
|
|
util_hexdump_grp(stderr, NULL, buf, 4, size, 0);
|
|
}
|
|
out:
|
|
fclose(fp);
|
|
return buf;
|
|
}
|
|
|
|
/**
|
|
* Returns the PKEY_KEYTYPE_xxx value for the specified key size.
|
|
*
|
|
* @param[in] keysize the key size in bits
|
|
*
|
|
* @returns the PKEY_KEYTYPE_xxx value or 0 for an unknown key size
|
|
*/
|
|
static u32 keysize_to_keytype(enum pkey_key_size keysize)
|
|
{
|
|
switch (keysize) {
|
|
case PKEY_SIZE_AES_128:
|
|
return PKEY_KEYTYPE_AES_128;
|
|
case PKEY_SIZE_AES_192:
|
|
return PKEY_KEYTYPE_AES_192;
|
|
case PKEY_SIZE_AES_256:
|
|
return PKEY_KEYTYPE_AES_256;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the PKEY_SIZE_xxx value for the specified keybits.
|
|
*
|
|
* @param[in] keybits the key size in bits
|
|
*
|
|
* @returns thePKEY_SIZE_xxx value or 0 for an unknown key size
|
|
*/
|
|
static enum pkey_key_size keybits_to_keysize(u32 keybits)
|
|
{
|
|
switch (keybits) {
|
|
case 128:
|
|
return PKEY_SIZE_AES_128;
|
|
case 192:
|
|
return PKEY_SIZE_AES_192;
|
|
case 256:
|
|
return PKEY_SIZE_AES_256;
|
|
default:
|
|
return PKEY_SIZE_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
static u32 flags_for_pkey_type(enum pkey_key_type type, bool exportable,
|
|
bool wrap_with_trusted)
|
|
{
|
|
u32 flags = 0;
|
|
|
|
if (!exportable)
|
|
return 0;
|
|
|
|
switch (type) {
|
|
case PKEY_TYPE_CCA_CIPHER:
|
|
flags = PKEY_KEYGEN_XPRT_SYM | PKEY_KEYGEN_XPRT_UASY |
|
|
PKEY_KEYGEN_XPRT_AASY | PKEY_KEYGEN_XPRT_RAW |
|
|
PKEY_KEYGEN_XPRT_CPAC | PKEY_KEYGEN_XPRT_DES |
|
|
PKEY_KEYGEN_XPRT_AES | PKEY_KEYGEN_XPRT_RSA;
|
|
break;
|
|
case PKEY_TYPE_EP11:
|
|
case PKEY_TYPE_EP11_AES:
|
|
flags = XCP_BLOB_EXTRACTABLE | XCP_BLOB_DECRYPT |
|
|
XCP_BLOB_ENCRYPT | XCP_BLOB_PROTKEY_EXTRACTABLE;
|
|
if (wrap_with_trusted)
|
|
flags |= XCP_BLOB_WRAP_W_TRUSTED;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return flags;
|
|
}
|
|
|
|
/*
|
|
* Wrapper for the PKEY_GENSECK/PKEY_GENSECK2 IOCTL to generate a secure
|
|
* key of any type by random. If the newer PKEY_GENSECK2 IOCTL is not supported
|
|
* by the pkey device, then it falls back to the older PKEY_GENSECK IOCTL
|
|
*
|
|
* @param[in] pkey_fd the pkey file descriptor
|
|
* @param[in/out] genseck info about key to generate
|
|
* @param[in] verbose if true, verbose messages are printed
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error
|
|
*/
|
|
static int pkey_genseck2(int pkey_fd, struct pkey_genseck2 *genseck2,
|
|
bool verbose)
|
|
{
|
|
struct pkey_genseck genseck;
|
|
int rc;
|
|
u32 i;
|
|
|
|
util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1");
|
|
util_assert(genseck2 != NULL, "Internal error: genseck2 is NULL");
|
|
|
|
rc = ioctl(pkey_fd, PKEY_GENSECK2, genseck2);
|
|
if (rc != 0 && errno != ENOTTY)
|
|
return -errno;
|
|
if (rc == 0)
|
|
return 0;
|
|
|
|
/* New IOCTL is not available, fall back to old one */
|
|
pr_verbose(verbose, "ioctl PKEY_GENSECK2 not supported, fall back to "
|
|
"PKEY_GENSECK");
|
|
|
|
if (genseck2->type != PKEY_TYPE_CCA_DATA) {
|
|
warnx("Key-type is not supported");
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
if (genseck2->keylen < AESDATA_KEY_SIZE)
|
|
return -EINVAL;
|
|
|
|
memset(&genseck, 0, sizeof(genseck));
|
|
|
|
genseck.keytype = keysize_to_keytype(genseck2->size);
|
|
if (genseck.keytype == 0)
|
|
return -EINVAL;
|
|
|
|
for (i = 0; i < genseck2->apqn_entries; i++) {
|
|
genseck.cardnr = genseck2->apqns[i].card;
|
|
genseck.domain = genseck2->apqns[i].domain;
|
|
|
|
rc = ioctl(pkey_fd, PKEY_GENSECK, &genseck);
|
|
if (rc != 0)
|
|
continue;
|
|
|
|
memcpy(genseck2->key, &genseck.seckey.seckey, AESDATA_KEY_SIZE);
|
|
genseck2->keylen = AESDATA_KEY_SIZE;
|
|
return 0;
|
|
}
|
|
|
|
return -errno;
|
|
}
|
|
|
|
/*
|
|
* Wrapper for the PKEY_CLR2SECK/PKEY_CLR2SECK2 IOCTL to generate a secure
|
|
* key of any type from a clear key. If the newer PKEY_CLR2SECK2 IOCTL is not
|
|
* supported by the pkey device, then it falls back to the older PKEY_CLR2SECK
|
|
* IOCTL
|
|
*
|
|
* @param[in] pkey_fd the pkey file descriptor
|
|
* @param[in/out] clr2seck2 info about key to generate
|
|
* @param[in] verbose if true, verbose messages are printed
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error
|
|
*/
|
|
static int pkey_clr2seck2(int pkey_fd, struct pkey_clr2seck2 *clr2seck2,
|
|
bool verbose)
|
|
{
|
|
struct pkey_clr2seck clr2seck;
|
|
int rc;
|
|
u32 i;
|
|
|
|
util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1");
|
|
util_assert(clr2seck2 != NULL, "Internal error: clr2seck2 is NULL");
|
|
|
|
rc = ioctl(pkey_fd, PKEY_CLR2SECK2, clr2seck2);
|
|
if (rc != 0 && errno != ENOTTY)
|
|
return -errno;
|
|
if (rc == 0)
|
|
return 0;
|
|
|
|
/* New IOCTL is not available, fall back to old one */
|
|
pr_verbose(verbose, "ioctl PKEY_CLR2SECK2 not supported, fall back to "
|
|
"PKEY_CLR2SECK");
|
|
|
|
if (clr2seck2->type != PKEY_TYPE_CCA_DATA) {
|
|
warnx("Key-type is not supported");
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
if (clr2seck2->keylen < AESDATA_KEY_SIZE)
|
|
return -EINVAL;
|
|
|
|
memset(&clr2seck, 0, sizeof(clr2seck));
|
|
clr2seck.clrkey = clr2seck2->clrkey;
|
|
|
|
clr2seck.keytype = keysize_to_keytype(clr2seck2->size);
|
|
if (clr2seck.keytype == 0)
|
|
return -EINVAL;
|
|
|
|
for (i = 0; i < clr2seck2->apqn_entries; i++) {
|
|
clr2seck.cardnr = clr2seck2->apqns[i].card;
|
|
clr2seck.domain = clr2seck2->apqns[i].domain;
|
|
|
|
rc = ioctl(pkey_fd, PKEY_CLR2SECK, &clr2seck);
|
|
if (rc != 0)
|
|
continue;
|
|
|
|
memcpy(clr2seck2->key, &clr2seck.seckey.seckey,
|
|
AESDATA_KEY_SIZE);
|
|
clr2seck2->keylen = AESDATA_KEY_SIZE;
|
|
return 0;
|
|
}
|
|
|
|
return -errno;
|
|
}
|
|
|
|
/*
|
|
* Wrapper for the PKEY_VERIFYKEY/PKEY_VERIFYKEY2 IOCTL to verify a secure
|
|
* key of any type. If the newer PKEY_VERIFYKEY2 IOCTL is not supported
|
|
* by the pkey device, then it falls back to the older PKEY_VERIFYKEY IOCTL
|
|
*
|
|
* @param[in] pkey_fd the pkey file descriptor
|
|
* @param[in/out] verifykey2 info about key to verify
|
|
* @param[in] verbose if true, verbose messages are printed
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error
|
|
*/
|
|
static int pkey_verifyseck2(int pkey_fd, struct pkey_verifykey2 *verifykey2,
|
|
bool verbose)
|
|
{
|
|
struct pkey_verifykey verifykey;
|
|
int rc;
|
|
|
|
util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1");
|
|
util_assert(verifykey2 != NULL, "Internal error: verifyseck2 is NULL");
|
|
|
|
rc = ioctl(pkey_fd, PKEY_VERIFYKEY2, verifykey2);
|
|
if (rc != 0 && errno != ENOTTY)
|
|
return -errno;
|
|
if (rc == 0)
|
|
return 0;
|
|
|
|
/* New IOCTL is not available, fall back to old one */
|
|
pr_verbose(verbose, "ioctl PKEY_VERIFYKEY2 not supported, fall back to "
|
|
"PKEY_VERIFYKEY");
|
|
|
|
if (!is_cca_aes_data_key(verifykey2->key, verifykey2->keylen))
|
|
return -ENODEV;
|
|
|
|
memset(&verifykey, 0, sizeof(verifykey));
|
|
memcpy(&verifykey.seckey, verifykey2->key, sizeof(verifykey.seckey));
|
|
|
|
/*
|
|
* Note: the old IOCTL does not support to check a specific card and
|
|
* domain. If falling back to the old IOCTL, this input is silently
|
|
* ignored, and all APQNs currently available in the system are used.
|
|
*/
|
|
rc = ioctl(pkey_fd, PKEY_VERIFYKEY, &verifykey);
|
|
if (rc != 0)
|
|
return -errno;
|
|
|
|
if ((verifykey.attributes & PKEY_VERIFY_ATTR_AES) == 0)
|
|
return -ENODEV;
|
|
|
|
verifykey2->type = PKEY_TYPE_CCA_DATA;
|
|
verifykey2->cardnr = verifykey.cardnr;
|
|
verifykey2->domain = verifykey.domain;
|
|
verifykey2->size = keybits_to_keysize(verifykey.keysize);
|
|
|
|
if (verifykey.attributes & PKEY_VERIFY_ATTR_OLD_MKVP)
|
|
verifykey2->flags = PKEY_FLAGS_MATCH_ALT_MKVP;
|
|
else
|
|
verifykey2->flags = PKEY_FLAGS_MATCH_CUR_MKVP;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Print a list of APQNs if verbose is set
|
|
*/
|
|
static void pr_verbose_apqn_list(bool verbose, struct pkey_apqn *list, u32 num)
|
|
{
|
|
u32 i;
|
|
|
|
if (!verbose)
|
|
return;
|
|
|
|
for (i = 0; i < num ; i++)
|
|
warnx(" APQN: %02x.%04x", list[i].card, list[i].domain);
|
|
}
|
|
|
|
/**
|
|
* Filter a n array list of APQNs (struct pkey_apqn) by a list of APQN strings.
|
|
*
|
|
* @param[in] apqn_list a zero terminated array of pointers to C-strings
|
|
* @param[in/out] apqns A list of APQNs as array of struct pkey_apqn to
|
|
* filter. The list is modified during filtering.
|
|
* @param[in/out] apqn_entries Number of entries in the list of APQNs. The
|
|
* number is modified during filtering.
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error
|
|
*/
|
|
static int filter_apqn_list(const char **apqn_list, struct pkey_apqn **apqns,
|
|
u32 *apqn_entries)
|
|
{
|
|
unsigned int count, i, k, card, domain;
|
|
struct pkey_apqn *list = *apqns;
|
|
bool found;
|
|
|
|
if (apqn_list == NULL)
|
|
return 0;
|
|
|
|
for (count = 0; apqn_list[count] != NULL; count++)
|
|
;
|
|
if (count == 0)
|
|
return 0;
|
|
|
|
for (i = 0; i < *apqn_entries; i++) {
|
|
found = false;
|
|
for (k = 0; apqn_list[k] != NULL; k++) {
|
|
if (sscanf(apqn_list[k], "%x.%x", &card, &domain) != 2)
|
|
return -EINVAL;
|
|
|
|
if (list[i].card == card && list[i].domain == domain) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
if (i < *apqn_entries - 1)
|
|
memmove(&list[i], &list[i+1],
|
|
(*apqn_entries - i - 1) *
|
|
sizeof(struct pkey_apqn));
|
|
(*apqn_entries)--;
|
|
i--;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Build a list of APQNs in the form accepted by the pkey IOCTLs from the
|
|
* List of APQNs as zero terminated array of pointers to C-strings that
|
|
* are usable for the CCA-AESDATA key type.
|
|
*
|
|
* @param[in] apqn_list a zero terminated array of pointers to C-strings
|
|
* @param[out] apqns A list of APQNs as array of struct pkey_apqn. The
|
|
* list must be freed by the caller using free().
|
|
* @param[out] apqn_entries Number of entries in the list of APQNs
|
|
* @param[in] verbose if true, verbose messages are printed
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error
|
|
*/
|
|
static int build_apqn_list_for_aes_data(const char **apqn_list,
|
|
struct pkey_apqn **apqns,
|
|
u32 *apqn_entries, bool verbose)
|
|
{
|
|
unsigned int card, domain, count = 0;
|
|
struct pkey_apqn *list = NULL;
|
|
u32 list_entries = 0;
|
|
int i;
|
|
|
|
pr_verbose(verbose, "Build a list of APQNs for CCA-AESDATA");
|
|
|
|
if (apqn_list != NULL)
|
|
for (count = 0; apqn_list[count] != NULL; count++)
|
|
;
|
|
|
|
if (count > 0) {
|
|
list = util_malloc(count * sizeof(struct pkey_apqn));
|
|
list_entries = count;
|
|
|
|
for (i = 0; apqn_list[i] != NULL; i++) {
|
|
if (sscanf(apqn_list[i], "%x.%x", &card, &domain) != 2)
|
|
return -EINVAL;
|
|
|
|
list[i].card = card;
|
|
list[i].domain = domain;
|
|
}
|
|
|
|
} else {
|
|
/*
|
|
* Although the new pkey IOCTLs do not support APQN entries
|
|
* with ANY indication, build an ANY-list here. If we get here,
|
|
* then the new IOCTLs are not available, and it will fall back
|
|
* to the old IOCTL which do support ANY specifications.
|
|
*/
|
|
list = util_malloc(sizeof(struct pkey_apqn));
|
|
list_entries = 1;
|
|
|
|
list[0].card = AUTOSELECT;
|
|
list[0].domain = AUTOSELECT;
|
|
}
|
|
|
|
*apqns = list;
|
|
*apqn_entries = list_entries;
|
|
|
|
pr_verbose(verbose, "%u APQNs found", list_entries);
|
|
pr_verbose_apqn_list(verbose, list, list_entries);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Build a list of APQNs in the form accepted by the pkey IOCTLs from the
|
|
* List of APQNs as zero terminated array of pointers to C-strings that
|
|
* are usable for the specified key type.
|
|
*
|
|
* @param[in] pkey_fd the pkey file descriptor
|
|
* @param[in] type the key type
|
|
* @param[in] apqn_list a zero terminated array of pointers to C-strings
|
|
* @param[out] apqns A list of APQNs as array of struct pkey_apqn. The
|
|
* list must be freed by the caller using free().
|
|
* @param[out] apqn_entries Number of entries in the list of APQNs
|
|
* @param[in] verbose if true, verbose messages are printed
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error
|
|
*/
|
|
static int build_apqn_list_for_key_type(int pkey_fd, enum pkey_key_type type,
|
|
const char **apqn_list,
|
|
struct pkey_apqn **apqns,
|
|
u32 *apqn_entries, bool verbose)
|
|
{
|
|
struct pkey_apqns4keytype apqns4keytype;
|
|
int rc;
|
|
|
|
util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1");
|
|
util_assert(apqns != NULL, "Internal error: apqns is NULL");
|
|
util_assert(apqn_entries != NULL,
|
|
"Internal error: apqn_entries is NULL");
|
|
|
|
pr_verbose(verbose, "Build a list of APQNs for key type %d", type);
|
|
|
|
memset(&apqns4keytype, 0, sizeof(apqns4keytype));
|
|
apqns4keytype.type = type;
|
|
apqns4keytype.apqn_entries = INITIAL_APQN_ENTRIES;
|
|
apqns4keytype.apqns = (struct pkey_apqn *)util_malloc(
|
|
apqns4keytype.apqn_entries * sizeof(struct pkey_apqn));
|
|
|
|
do {
|
|
rc = ioctl(pkey_fd, PKEY_APQNS4KT, &apqns4keytype);
|
|
if (rc == 0)
|
|
break;
|
|
rc = -errno;
|
|
pr_verbose(verbose, "ioctl PKEY_APQNS4KT rc: %s",
|
|
strerror(-rc));
|
|
|
|
switch (rc) {
|
|
case -ENOSPC:
|
|
free(apqns4keytype.apqns);
|
|
apqns4keytype.apqns = (struct pkey_apqn *)
|
|
util_malloc(apqns4keytype.apqn_entries *
|
|
sizeof(struct pkey_apqn));
|
|
continue;
|
|
case -ENOTTY:
|
|
/*
|
|
* New IOCTL is not available: build the list
|
|
* manually (Key type CCA-AESDATA only)
|
|
*/
|
|
free(apqns4keytype.apqns);
|
|
|
|
if (type != PKEY_TYPE_CCA_DATA)
|
|
return -ENOTSUP;
|
|
|
|
rc = build_apqn_list_for_aes_data(apqn_list, apqns,
|
|
apqn_entries,
|
|
verbose);
|
|
return rc;
|
|
case -EINVAL:
|
|
/* This is usually due to an unsupported key type */
|
|
rc = -ENOTSUP;
|
|
goto out;
|
|
default:
|
|
goto out;
|
|
}
|
|
} while (rc != 0);
|
|
|
|
if (apqns4keytype.apqn_entries == 0) {
|
|
pr_verbose(verbose, "No APQN available for key type %d", type);
|
|
rc = -ENODEV;
|
|
goto out;
|
|
}
|
|
|
|
rc = filter_apqn_list(apqn_list, &apqns4keytype.apqns,
|
|
&apqns4keytype.apqn_entries);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
if (apqns4keytype.apqn_entries == 0) {
|
|
pr_verbose(verbose, "No APQN available for key type %d", type);
|
|
rc = -ENODEV;
|
|
goto out;
|
|
}
|
|
|
|
pr_verbose(verbose, "%u APQNs found", apqns4keytype.apqn_entries);
|
|
pr_verbose_apqn_list(verbose, apqns4keytype.apqns,
|
|
apqns4keytype.apqn_entries);
|
|
|
|
out:
|
|
if (rc == 0) {
|
|
*apqns = apqns4keytype.apqns;
|
|
*apqn_entries = apqns4keytype.apqn_entries;
|
|
} else {
|
|
*apqns = NULL;
|
|
*apqn_entries = 0;
|
|
free(apqns4keytype.apqns);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Build a list of APQNs in the form accepted by the pkey IOCTLs from the
|
|
* List of APQNs as zero terminated array of pointers to C-strings that are
|
|
* usable for the specufied key.
|
|
*
|
|
* @param[in] pkey_fd the pkey file descriptor
|
|
* @param[in] key the key
|
|
* @param[in] keylen the length of the key
|
|
* @param[in] flags PKEY_FLAGS_MATCH_xxx flags
|
|
* @param[in] apqn_list a zero terminated array of pointers to C-strings
|
|
* @param[out] apqns A list of APQNs as array of struct pkey_apqn. The
|
|
* list must be freed by the caller using free().
|
|
* @param[out] apqn_entries Number of entries in the list of APQNs
|
|
* @param[in] verbose if true, verbose messages are printed
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error
|
|
*/
|
|
static int build_apqn_list_for_key(int pkey_fd, u8 *key, u32 keylen, u32 flags,
|
|
const char **apqn_list,
|
|
struct pkey_apqn **apqns,
|
|
u32 *apqn_entries, bool verbose)
|
|
{
|
|
struct pkey_apqns4key apqns4key;
|
|
int rc;
|
|
|
|
util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1");
|
|
util_assert(key != NULL, "Internal error: key is NULL");
|
|
util_assert(apqns != NULL, "Internal error: apqns is NULL");
|
|
util_assert(apqn_entries != NULL,
|
|
"Internal error: apqn_entries is NULL");
|
|
|
|
pr_verbose(verbose, "Build a list of APQNs for the key");
|
|
|
|
memset(&apqns4key, 0, sizeof(apqns4key));
|
|
apqns4key.key = key;
|
|
apqns4key.keylen = keylen;
|
|
apqns4key.flags = flags;
|
|
apqns4key.apqn_entries = INITIAL_APQN_ENTRIES;
|
|
apqns4key.apqns = (struct pkey_apqn *)util_malloc(
|
|
apqns4key.apqn_entries * sizeof(struct pkey_apqn));
|
|
|
|
do {
|
|
rc = ioctl(pkey_fd, PKEY_APQNS4K, &apqns4key);
|
|
if (rc == 0)
|
|
break;
|
|
rc = -errno;
|
|
pr_verbose(verbose, "ioctl PKEY_APQNS4K rc: %s", strerror(-rc));
|
|
|
|
switch (rc) {
|
|
case -ENOSPC:
|
|
free(apqns4key.apqns);
|
|
apqns4key.apqns = (struct pkey_apqn *)
|
|
util_malloc(apqns4key.apqn_entries *
|
|
sizeof(struct pkey_apqn));
|
|
continue;
|
|
case -ENOTTY:
|
|
/*
|
|
* New IOCTL is not available: build the list manually
|
|
* (Key type CCA-AESDATA only)
|
|
*/
|
|
free(apqns4key.apqns);
|
|
|
|
if (!is_cca_aes_data_key(key, keylen))
|
|
return -ENOTSUP;
|
|
|
|
rc = build_apqn_list_for_aes_data(apqn_list, apqns,
|
|
apqn_entries,
|
|
verbose);
|
|
return rc;
|
|
case -EINVAL:
|
|
/* This is usually due to an unsupported key type */
|
|
rc = -ENOTSUP;
|
|
goto out;
|
|
default:
|
|
goto out;
|
|
}
|
|
} while (rc != 0);
|
|
|
|
if (apqns4key.apqn_entries == 0) {
|
|
pr_verbose(verbose, "No APQN available for the key");
|
|
rc = -ENODEV;
|
|
goto out;
|
|
}
|
|
|
|
rc = filter_apqn_list(apqn_list, &apqns4key.apqns,
|
|
&apqns4key.apqn_entries);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
if (apqns4key.apqn_entries == 0) {
|
|
pr_verbose(verbose, "No APQN available for the key");
|
|
rc = -ENODEV;
|
|
goto out;
|
|
}
|
|
|
|
pr_verbose(verbose, "%u APQNs found", apqns4key.apqn_entries);
|
|
pr_verbose_apqn_list(verbose, apqns4key.apqns, apqns4key.apqn_entries);
|
|
|
|
out:
|
|
if (rc == 0) {
|
|
*apqns = apqns4key.apqns;
|
|
*apqn_entries = apqns4key.apqn_entries;
|
|
} else {
|
|
*apqns = NULL;
|
|
*apqn_entries = 0;
|
|
free(apqns4key.apqns);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Convert the key type string into the pkey enumeration
|
|
*
|
|
* @param[in] key_type the type of the key
|
|
*
|
|
* @returns the pkey key type or 0 for an u known key type
|
|
*/
|
|
static enum pkey_key_type key_type_to_pkey_type(const char *key_type)
|
|
{
|
|
if (strcasecmp(key_type, KEY_TYPE_CCA_AESDATA) == 0)
|
|
return PKEY_TYPE_CCA_DATA;
|
|
if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0)
|
|
return PKEY_TYPE_CCA_CIPHER;
|
|
if (strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0)
|
|
return PKEY_TYPE_EP11_AES;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Return the size of a key blob for a specific type
|
|
*
|
|
* @param[in] type the type of the key
|
|
*
|
|
* @returns the size of the key or 0 for an invalid key type
|
|
*/
|
|
static size_t key_size_for_type(enum pkey_key_type type)
|
|
{
|
|
switch (type) {
|
|
case PKEY_TYPE_CCA_DATA:
|
|
return AESDATA_KEY_SIZE;
|
|
case PKEY_TYPE_CCA_CIPHER:
|
|
return AESCIPHER_KEY_SIZE;
|
|
case PKEY_TYPE_EP11:
|
|
return EP11_KEY_SIZE;
|
|
case PKEY_TYPE_EP11_AES:
|
|
return EP11_AES_KEY_SIZE;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static void print_error_for_ep11_exportable(void)
|
|
{
|
|
util_print_indented("Generating an exportable key of type "
|
|
KEY_TYPE_EP11_AES " requires that access control "
|
|
"point (ACP) 'XCP_CPB_ALLOW_COMBINED_EXTRACT' is "
|
|
"'ON' on all used APQNs. This access control point "
|
|
"is only supported on newer EP11 firmware levels.",
|
|
0);
|
|
}
|
|
|
|
/**
|
|
* Generate a secure key by random
|
|
*
|
|
* @param[in] pkey_fd the pkey file descriptor
|
|
* @param[in] keyfile the file name of the secure key to generate
|
|
* @param[in] keybits the cryptographic size of the key in bits
|
|
* @param[in] xts if true an XTS key is generated
|
|
* @param[in] key_type the type of the key
|
|
* @param[in] apqns a zero terminated array of pointers to APQN-strings,
|
|
* or NULL for AUTOSELECT
|
|
* @param[in] exportable if true the key shall be exportable
|
|
* @param[in] wrap_with_trusted if true the key shall be wrapable with a
|
|
* trusted KEK only
|
|
* @param[in] verbose if true, verbose messages are printed
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error
|
|
*/
|
|
int generate_secure_key_random(int pkey_fd, const char *keyfile,
|
|
size_t keybits, bool xts, const char *key_type,
|
|
const char **apqns, bool exportable,
|
|
bool wrap_with_trusted, bool verbose)
|
|
{
|
|
struct pkey_genseck2 genseck2;
|
|
size_t secure_key_size, size;
|
|
u8 *secure_key = NULL;
|
|
int rc;
|
|
|
|
util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1");
|
|
util_assert(keyfile != NULL, "Internal error: keyfile is NULL");
|
|
util_assert(key_type != NULL, "Internal error: key_type is NULL");
|
|
|
|
if (keybits == 0)
|
|
keybits = DEFAULT_KEYBITS;
|
|
|
|
pr_verbose(verbose, "Generate secure key by random");
|
|
|
|
memset(&genseck2, 0, sizeof(genseck2));
|
|
|
|
genseck2.type = key_type_to_pkey_type(key_type);
|
|
if (genseck2.type == 0) {
|
|
warnx("Key-type not supported: %s", key_type);
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
retry:
|
|
genseck2.size = keybits_to_keysize(keybits);
|
|
if (genseck2.size == 0) {
|
|
warnx("Invalid value for '--keybits'/'-c': '%lu'", keybits);
|
|
return -EINVAL;
|
|
}
|
|
if (keybits == 192 && xts) {
|
|
warnx("Invalid value for '--keybits'|'-c' "
|
|
"for XTS: '%lu'", keybits);
|
|
return -EINVAL;
|
|
}
|
|
|
|
rc = build_apqn_list_for_key_type(pkey_fd, genseck2.type, apqns,
|
|
&genseck2.apqns,
|
|
&genseck2.apqn_entries, verbose);
|
|
if (rc != 0) {
|
|
if (rc == -ENODEV || rc == -ENOTSUP)
|
|
warnx("No APQN is available that can generate a secure "
|
|
"key of type %s", key_type);
|
|
else
|
|
warnx("Failed to build a list of APQNs that can "
|
|
"generate a secure key of type %s: %s", key_type,
|
|
strerror(-rc));
|
|
return rc;
|
|
}
|
|
|
|
size = key_size_for_type(genseck2.type);
|
|
secure_key_size = DOUBLE_KEYSIZE_FOR_XTS(size, xts);
|
|
secure_key = util_zalloc(secure_key_size);
|
|
|
|
genseck2.key = secure_key;
|
|
genseck2.keylen = size;
|
|
|
|
genseck2.keygenflags = flags_for_pkey_type(genseck2.type, exportable,
|
|
wrap_with_trusted);
|
|
|
|
rc = pkey_genseck2(pkey_fd, &genseck2, verbose);
|
|
if (rc == -EINVAL && genseck2.type == PKEY_TYPE_EP11_AES) {
|
|
/*
|
|
* Older kernels may not support gensek2 with key type
|
|
* PKEY_TYPE_EP11_AES, retry with PKEY_TYPE_EP11.
|
|
*/
|
|
pr_verbose(verbose,
|
|
"ioctl PKEY_GENSECK2 does not support "
|
|
"PKEY_TYPE_EP11_AES, fall back to PKEY_TYPE_EP11");
|
|
|
|
genseck2.type = PKEY_TYPE_EP11;
|
|
free(genseck2.apqns);
|
|
genseck2.apqns = NULL;
|
|
genseck2.apqn_entries = 0;
|
|
free(secure_key);
|
|
goto retry;
|
|
}
|
|
if (rc != 0) {
|
|
warnx("Failed to generate a secure key: %s", strerror(-rc));
|
|
if (exportable &&
|
|
strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0)
|
|
print_error_for_ep11_exportable();
|
|
goto out;
|
|
}
|
|
if (rc == 0 && genseck2.type == PKEY_TYPE_EP11) {
|
|
if (is_ep11_key_session_bound(secure_key, size)) {
|
|
warnx("The generated key is session bound. Kernel "
|
|
"support is required for such keys");
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (xts) {
|
|
free(genseck2.apqns);
|
|
genseck2.apqns = NULL;
|
|
genseck2.apqn_entries = 0;
|
|
|
|
/*
|
|
* Ensure to generate 2nd key with an APQN that has the same
|
|
* master key that is used by the 1st key.
|
|
*/
|
|
rc = build_apqn_list_for_key(pkey_fd, secure_key, size,
|
|
PKEY_FLAGS_MATCH_CUR_MKVP, apqns,
|
|
&genseck2.apqns,
|
|
&genseck2.apqn_entries, verbose);
|
|
if (rc != 0) {
|
|
if (rc == -ENODEV || rc == -ENOTSUP)
|
|
warnx("No APQN is available that can generate "
|
|
"a secure key of type %s", key_type);
|
|
else
|
|
warnx("Failed to build a list of APQNs that "
|
|
"can generate a secure key of type %s: "
|
|
"%s", key_type, strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
genseck2.key = secure_key + size;
|
|
genseck2.keylen = size;
|
|
|
|
genseck2.keygenflags = flags_for_pkey_type(genseck2.type,
|
|
exportable,
|
|
wrap_with_trusted);
|
|
|
|
rc = pkey_genseck2(pkey_fd, &genseck2, verbose);
|
|
if (rc != 0) {
|
|
warnx("Failed to generate a secure key: %s",
|
|
strerror(-rc));
|
|
if (exportable &&
|
|
strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0)
|
|
print_error_for_ep11_exportable();
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
pr_verbose(verbose, "Successfully generated a secure key");
|
|
|
|
rc = write_secure_key(keyfile, secure_key, secure_key_size, verbose);
|
|
|
|
out:
|
|
free(genseck2.apqns);
|
|
free(secure_key);
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* Generate a secure key from a clear key file
|
|
*
|
|
* @param[in] pkey_fd the pkey file descriptor
|
|
* @param[in] keyfile the file name of the secure key to generate
|
|
* @param[in] keybits the cryptographic size of the key in bits. When
|
|
* keybits is 0, then the clear key file size
|
|
* determines the keybits.
|
|
* @param[in] xts if true an XTS key is generated
|
|
* @param[in] clearkeyfile the file name of the clear key to read
|
|
* @param[in] key_type the type of the key
|
|
* @param[in] apqns a zero terminated array of pointers to APQN-strings,
|
|
* or NULL for AUTOSELECT
|
|
* @param[in] exportable if true the key shall be exportable
|
|
* @param[in] wrap_with_trusted if true the key shall be wrapable with a
|
|
* trusted KEK only
|
|
* @param[in] verbose if true, verbose messages are printed
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error
|
|
*/
|
|
int generate_secure_key_clear(int pkey_fd, const char *keyfile,
|
|
size_t keybits, bool xts,
|
|
const char *clearkeyfile, const char *key_type,
|
|
const char **apqns, bool exportable,
|
|
bool wrap_with_trusted, bool verbose)
|
|
{
|
|
struct pkey_clr2seck2 clr2seck2;
|
|
size_t secure_key_size;
|
|
size_t clear_key_size;
|
|
u8 *secure_key;
|
|
u8 *clear_key;
|
|
size_t size;
|
|
int rc;
|
|
|
|
util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1");
|
|
util_assert(keyfile != NULL, "Internal error: keyfile is NULL");
|
|
util_assert(clearkeyfile != NULL,
|
|
"Internal error: clearkeyfile is NULL");
|
|
util_assert(key_type != NULL, "Internal error: key_type is NULL");
|
|
|
|
pr_verbose(verbose, "Generate secure key from a clear key");
|
|
|
|
clear_key = read_clear_key(clearkeyfile, keybits, xts, &clear_key_size,
|
|
verbose);
|
|
if (clear_key == NULL)
|
|
return -EINVAL;
|
|
|
|
memset(&clr2seck2, 0, sizeof(clr2seck2));
|
|
|
|
memcpy(&clr2seck2.clrkey, clear_key,
|
|
HALF_KEYSIZE_FOR_XTS(clear_key_size, xts));
|
|
|
|
clr2seck2.type = key_type_to_pkey_type(key_type);
|
|
if (clr2seck2.type == 0) {
|
|
warnx("Key-type not supported: %s", key_type);
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
retry:
|
|
clr2seck2.size = keybits_to_keysize(HALF_KEYSIZE_FOR_XTS(
|
|
clear_key_size * 8, xts));
|
|
if (clr2seck2.size == 0) {
|
|
warnx("Invalid clear key size: '%lu' bytes", clear_key_size);
|
|
return -EINVAL;
|
|
}
|
|
if (keybits == 192 && xts) {
|
|
warnx("Invalid clear key size for XTS: '%lu' bytes",
|
|
clear_key_size);
|
|
return -EINVAL;
|
|
}
|
|
|
|
rc = build_apqn_list_for_key_type(pkey_fd, clr2seck2.type, apqns,
|
|
&clr2seck2.apqns,
|
|
&clr2seck2.apqn_entries, verbose);
|
|
if (rc != 0) {
|
|
if (rc == -ENODEV || rc == -ENOTSUP)
|
|
warnx("No APQN is available that can generate a secure "
|
|
"key of type %s", key_type);
|
|
else
|
|
warnx("Failed to build a list of APQNs that can "
|
|
"generate a secure key of type %s: %s", key_type,
|
|
strerror(-rc));
|
|
return rc;
|
|
}
|
|
|
|
size = key_size_for_type(clr2seck2.type);
|
|
secure_key_size = DOUBLE_KEYSIZE_FOR_XTS(size, xts);
|
|
secure_key = util_zalloc(secure_key_size);
|
|
|
|
clr2seck2.key = secure_key;
|
|
clr2seck2.keylen = size;
|
|
|
|
clr2seck2.keygenflags = flags_for_pkey_type(clr2seck2.type, exportable,
|
|
wrap_with_trusted);
|
|
|
|
rc = pkey_clr2seck2(pkey_fd, &clr2seck2, verbose);
|
|
if (rc == -EINVAL && clr2seck2.type == PKEY_TYPE_EP11_AES) {
|
|
/*
|
|
* Older kernels may not support clr2seck2 with key type
|
|
* PKEY_TYPE_EP11_AES, retry with PKEY_TYPE_EP11.
|
|
*/
|
|
pr_verbose(verbose,
|
|
"ioctl PKEY_CLR2SECK2 does not support "
|
|
"PKEY_TYPE_EP11_AES, fall back to PKEY_TYPE_EP11");
|
|
|
|
clr2seck2.type = PKEY_TYPE_EP11;
|
|
free(clr2seck2.apqns);
|
|
clr2seck2.apqns = NULL;
|
|
clr2seck2.apqn_entries = 0;
|
|
free(secure_key);
|
|
goto retry;
|
|
}
|
|
if (rc != 0) {
|
|
warnx("Failed to generate a secure key: %s", strerror(-rc));
|
|
if (exportable &&
|
|
strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0)
|
|
print_error_for_ep11_exportable();
|
|
goto out;
|
|
}
|
|
if (rc == 0 && clr2seck2.type == PKEY_TYPE_EP11) {
|
|
if (is_ep11_key_session_bound(secure_key, size)) {
|
|
warnx("The generated key is session bound. Kernel "
|
|
"support is required for such keys");
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (xts) {
|
|
free(clr2seck2.apqns);
|
|
clr2seck2.apqns = NULL;
|
|
clr2seck2.apqn_entries = 0;
|
|
|
|
memcpy(&clr2seck2.clrkey, clear_key + clear_key_size / 2,
|
|
clear_key_size / 2);
|
|
|
|
/*
|
|
* Ensure to generate 2nd key with an APQN that has the same
|
|
* master key that is used by the 1st key.
|
|
*/
|
|
rc = build_apqn_list_for_key(pkey_fd, secure_key, size,
|
|
PKEY_FLAGS_MATCH_CUR_MKVP, apqns,
|
|
&clr2seck2.apqns,
|
|
&clr2seck2.apqn_entries, verbose);
|
|
if (rc != 0) {
|
|
if (rc == -ENODEV || rc == -ENOTSUP)
|
|
warnx("No APQN is available that can generate "
|
|
"a secure key of type %s", key_type);
|
|
else
|
|
warnx("Failed to build a list of APQNs that "
|
|
"can generate a secure key of type %s: "
|
|
"%s", key_type, strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
clr2seck2.key = secure_key + size;
|
|
clr2seck2.keylen = size;
|
|
|
|
clr2seck2.keygenflags = flags_for_pkey_type(clr2seck2.type,
|
|
exportable,
|
|
wrap_with_trusted);
|
|
|
|
rc = pkey_clr2seck2(pkey_fd, &clr2seck2, verbose);
|
|
if (rc != 0) {
|
|
warnx("Failed to generate a secure key: %s",
|
|
strerror(-rc));
|
|
if (exportable &&
|
|
strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0)
|
|
print_error_for_ep11_exportable();
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
pr_verbose(verbose,
|
|
"Successfully generated a secure key from a clear key");
|
|
|
|
rc = write_secure_key(keyfile, secure_key, secure_key_size, verbose);
|
|
|
|
out:
|
|
memset(&clr2seck2, 0, sizeof(clr2seck2));
|
|
memset(clear_key, 0, clear_key_size);
|
|
free(clear_key);
|
|
free(secure_key);
|
|
free(clr2seck2.apqns);
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Validates an XTS secure key (the second part)
|
|
*
|
|
* @param[in] pkey_fd the pkey file descriptor
|
|
* @param[in] apqn the APQN to verify the key with
|
|
* @param[in] secure_key a buffer containing the secure key
|
|
* @param[in] secure_key_size the secure key size
|
|
* @param[in] part1_keysize the key size of the first key part
|
|
* @param[in] part1_flags the flags of the first key part
|
|
* @param[out] clear_key_bitsize on return , the cryptographic size of the
|
|
* clear key
|
|
* @param[in] verbose if true, verbose messages are printed
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error
|
|
*/
|
|
static int validate_secure_xts_key(int pkey_fd, struct pkey_apqn *apqn,
|
|
u8 *secure_key, size_t secure_key_size,
|
|
enum pkey_key_size part1_keysize,
|
|
u32 part1_flags, size_t *clear_key_bitsize,
|
|
bool verbose)
|
|
{
|
|
struct pkey_verifykey2 verifykey2;
|
|
int rc;
|
|
|
|
util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1");
|
|
util_assert(secure_key != NULL, "Internal error: secure_key is NULL");
|
|
util_assert(apqn != NULL, "Internal error: apqn is NULL");
|
|
|
|
memset(&verifykey2, 0, sizeof(verifykey2));
|
|
verifykey2.key = secure_key + (secure_key_size / 2);
|
|
verifykey2.keylen = secure_key_size / 2;
|
|
verifykey2.cardnr = apqn->card;
|
|
verifykey2.domain = apqn->domain;
|
|
|
|
rc = pkey_verifyseck2(pkey_fd, &verifykey2, verbose);
|
|
if (rc < 0) {
|
|
pr_verbose(verbose, "Failed to validate the 2nd part of the "
|
|
"XTS secure key on APQN %02x.%04x: %s", apqn->card,
|
|
apqn->domain, strerror(-rc));
|
|
return rc;
|
|
}
|
|
|
|
if (verifykey2.size != part1_keysize) {
|
|
pr_verbose(verbose, "XTS secure key contains 2 keys using "
|
|
"different key sizes");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (verifykey2.flags != part1_flags) {
|
|
pr_verbose(verbose, "XTS secure key contains 2 keys using "
|
|
"different master keys");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (clear_key_bitsize && verifykey2.size != PKEY_SIZE_UNKNOWN)
|
|
*clear_key_bitsize += verifykey2.size;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Validates a secure key
|
|
*
|
|
* @param[in] pkey_fd the pkey file descriptor
|
|
* @param[in] secure_key a buffer containing the secure key
|
|
* @param[in] secure_key_size the secure key size
|
|
* @param[out] clear_key_bitsize on return , the cryptographic size of the
|
|
* clear key
|
|
* @param[out] is_old_mk in return set to 1 to indicate if the secure key
|
|
* is currently enciphered by the OLD master key
|
|
* @param[in] apqns a zero terminated array of pointers to APQN-strings,
|
|
* or NULL for AUTOSELECT
|
|
* @param[in] verbose if true, verbose messages are printed
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error
|
|
*/
|
|
int validate_secure_key(int pkey_fd,
|
|
u8 *secure_key, size_t secure_key_size,
|
|
size_t *clear_key_bitsize, int *is_old_mk,
|
|
const char **apqns, bool verbose)
|
|
{
|
|
struct pkey_verifykey2 verifykey2;
|
|
struct pkey_apqn *list = NULL;
|
|
bool xts, valid, securekey;
|
|
u32 i, list_entries = 0;
|
|
u32 flags = 0;
|
|
int rc;
|
|
|
|
util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1");
|
|
util_assert(secure_key != NULL, "Internal error: secure_key is NULL");
|
|
|
|
securekey = is_secure_key(secure_key, secure_key_size);
|
|
xts = securekey ? is_xts_key(secure_key, secure_key_size) : false;
|
|
|
|
if (securekey) {
|
|
flags = PKEY_FLAGS_MATCH_CUR_MKVP;
|
|
if (is_cca_aes_data_key(secure_key, secure_key_size) ||
|
|
is_cca_aes_cipher_key(secure_key, secure_key_size))
|
|
flags |= PKEY_FLAGS_MATCH_ALT_MKVP;
|
|
|
|
rc = build_apqn_list_for_key(pkey_fd, secure_key,
|
|
HALF_KEYSIZE_FOR_XTS(
|
|
secure_key_size, xts),
|
|
flags, apqns, &list, &list_entries,
|
|
verbose);
|
|
if (rc != 0) {
|
|
pr_verbose(verbose, "Failed to build a list of APQNs "
|
|
"that can validate this secure "
|
|
"key: %s", strerror(-rc));
|
|
return rc;
|
|
}
|
|
} else {
|
|
list = util_malloc(sizeof(struct pkey_apqn));
|
|
list[0].card = 0;
|
|
list[0].domain = 0;
|
|
list_entries = 1;
|
|
}
|
|
|
|
if (is_old_mk != NULL)
|
|
*is_old_mk = securekey ? true : false;
|
|
if (clear_key_bitsize != NULL)
|
|
*clear_key_bitsize = 0;
|
|
|
|
valid = false;
|
|
for (i = 0; i < list_entries; i++) {
|
|
memset(&verifykey2, 0, sizeof(verifykey2));
|
|
verifykey2.key = secure_key;
|
|
verifykey2.keylen = HALF_KEYSIZE_FOR_XTS(secure_key_size, xts);
|
|
verifykey2.cardnr = list[i].card;
|
|
verifykey2.domain = list[i].domain;
|
|
|
|
rc = pkey_verifyseck2(pkey_fd, &verifykey2, verbose);
|
|
if (rc < 0) {
|
|
pr_verbose(verbose, "Failed to validate the secure key "
|
|
"on APQN %02x.%04x: %s", list[i].card,
|
|
list[i].domain, strerror(-rc));
|
|
continue;
|
|
}
|
|
|
|
if (xts) {
|
|
rc = validate_secure_xts_key(pkey_fd, &list[i],
|
|
secure_key,
|
|
secure_key_size,
|
|
verifykey2.size,
|
|
verifykey2.flags,
|
|
clear_key_bitsize,
|
|
verbose);
|
|
if (rc != 0)
|
|
continue;
|
|
|
|
}
|
|
|
|
valid = true;
|
|
|
|
if (clear_key_bitsize) {
|
|
if (verifykey2.size != PKEY_SIZE_UNKNOWN)
|
|
*clear_key_bitsize += verifykey2.size;
|
|
clear_key_bitsize = NULL; /* Set it only once */
|
|
}
|
|
|
|
/*
|
|
* If at least one of the APQNs have a matching current MK,
|
|
* then don't report OLD, even if some match the old MK.
|
|
*/
|
|
if (securekey && is_old_mk &&
|
|
(verifykey2.flags & PKEY_FLAGS_MATCH_CUR_MKVP))
|
|
*is_old_mk = false;
|
|
}
|
|
|
|
if (!valid)
|
|
return -ENODEV;
|
|
|
|
pr_verbose(verbose, "Secure key validation completed successfully");
|
|
|
|
if (list != NULL)
|
|
free(list);
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Generate a key verification pattern of a secure AES key by encrypting the all
|
|
* zero message with the secure key using the AF_ALG interface
|
|
*
|
|
* @param[in] key the secure key token
|
|
* @param[in] key_size the size of the secure key
|
|
* @param[in] vp buffer where the verification pattern is returned
|
|
* @param[in] vp_len the size of the buffer
|
|
* @param[in] cipher the cipher to use ('cbc([p]aes)' or 'xts([p]aes)'),
|
|
* or null to determine it from the secure key.
|
|
* @param[in] verbose if true, verbose messages are printed
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error
|
|
*/
|
|
int generate_aes_key_verification_pattern(const u8 *key, size_t key_size,
|
|
char *vp, size_t vp_len,
|
|
const char *cipher,
|
|
bool verbose)
|
|
{
|
|
int tfmfd = -1, opfd = -1, rc = 0, retry_count = 0;
|
|
char null_msg[ENC_ZERO_LEN];
|
|
char enc_zero[ENC_ZERO_LEN];
|
|
struct af_alg_iv *alg_iv;
|
|
struct cmsghdr *header;
|
|
uint32_t *type;
|
|
ssize_t len;
|
|
size_t i;
|
|
|
|
struct sockaddr_alg sa = {
|
|
.salg_family = AF_ALG,
|
|
.salg_type = "skcipher",
|
|
};
|
|
struct iovec iov = {
|
|
.iov_base = (void *)null_msg,
|
|
.iov_len = sizeof(null_msg),
|
|
};
|
|
int iv_msg_size = CMSG_SPACE(sizeof(*alg_iv) + PAES_BLOCK_SIZE);
|
|
char buffer[CMSG_SPACE(sizeof(*type)) + iv_msg_size];
|
|
struct msghdr msg = {
|
|
.msg_control = buffer,
|
|
.msg_controllen = sizeof(buffer),
|
|
.msg_iov = &iov,
|
|
.msg_iovlen = 1,
|
|
};
|
|
|
|
if (vp_len < VERIFICATION_PATTERN_LEN) {
|
|
rc = -EMSGSIZE;
|
|
goto out;
|
|
}
|
|
|
|
if (cipher != NULL) {
|
|
util_strlcpy((char *)sa.salg_name, cipher,
|
|
sizeof(sa.salg_name));
|
|
} else {
|
|
snprintf((char *)sa.salg_name, sizeof(sa.salg_name), "%s(paes)",
|
|
is_xts_key(key, key_size) ? "xts" : "cbc");
|
|
}
|
|
|
|
tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
|
|
if (tfmfd < 0) {
|
|
rc = -errno;
|
|
pr_verbose(verbose, "Failed to open an AF_ALG socket");
|
|
goto out;
|
|
}
|
|
|
|
if (bind(tfmfd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
|
|
rc = -errno;
|
|
pr_verbose(verbose, "Failed to bind the AF_ALG socket, "
|
|
"salg_name='%s' ", sa.salg_name);
|
|
goto out;
|
|
}
|
|
|
|
retry_setkey:
|
|
if (setsockopt(tfmfd, SOL_ALG, ALG_SET_KEY, key,
|
|
key_size) < 0) {
|
|
rc = -errno;
|
|
pr_verbose(verbose, "Failed to set the key: %s",
|
|
strerror(-rc));
|
|
|
|
/*
|
|
* After a master key change, it can happen that the setkey
|
|
* operation returns EINVAL or EAGAIN, although the key is
|
|
* valid. This is a temporary situation and the operation will
|
|
* succeed, once the firmware has completed some internal
|
|
* processing related with the master key change.
|
|
* Delay 1 second and retry up to 10 times.
|
|
*/
|
|
if ((rc == -EINVAL || rc == -EAGAIN) && retry_count < 10) {
|
|
pr_verbose(verbose, "Retrying after 1 second...");
|
|
retry_count++;
|
|
sleep(1);
|
|
goto retry_setkey;
|
|
}
|
|
goto out;
|
|
}
|
|
rc = 0;
|
|
|
|
opfd = accept(tfmfd, NULL, NULL);
|
|
if (opfd < 0) {
|
|
rc = -errno;
|
|
pr_verbose(verbose, "Failed to accept on the AF_ALG socket");
|
|
goto out;
|
|
}
|
|
|
|
memset(null_msg, 0, sizeof(null_msg));
|
|
memset(buffer, 0, sizeof(buffer));
|
|
|
|
header = CMSG_FIRSTHDR(&msg);
|
|
if (header == NULL) {
|
|
pr_verbose(verbose, "Failed to obtain control message header");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
header->cmsg_level = SOL_ALG;
|
|
header->cmsg_type = ALG_SET_OP;
|
|
header->cmsg_len = CMSG_LEN(sizeof(*type));
|
|
type = (void *)CMSG_DATA(header);
|
|
*type = ALG_OP_ENCRYPT;
|
|
|
|
header = CMSG_NXTHDR(&msg, header);
|
|
if (header == NULL) {
|
|
pr_verbose(verbose, "Failed to obtain control message "
|
|
"header");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
header->cmsg_level = SOL_ALG;
|
|
header->cmsg_type = ALG_SET_IV;
|
|
header->cmsg_len = iv_msg_size;
|
|
alg_iv = (void *)CMSG_DATA(header);
|
|
alg_iv->ivlen = PAES_BLOCK_SIZE;
|
|
memcpy(alg_iv->iv, null_msg, PAES_BLOCK_SIZE);
|
|
|
|
len = sendmsg(opfd, &msg, 0);
|
|
if (len != ENC_ZERO_LEN) {
|
|
pr_verbose(verbose, "Failed to send to the AF_ALG socket");
|
|
rc = -errno;
|
|
goto out;
|
|
}
|
|
|
|
len = read(opfd, enc_zero, sizeof(enc_zero));
|
|
if (len != ENC_ZERO_LEN) {
|
|
pr_verbose(verbose, "Failed to receive from the AF_ALG socket");
|
|
rc = -errno;
|
|
goto out;
|
|
}
|
|
|
|
memset(vp, 0, vp_len);
|
|
for (i = 0; i < sizeof(enc_zero); i++)
|
|
sprintf(&vp[i * 2], "%02x", enc_zero[i]);
|
|
|
|
pr_verbose(verbose, "Key verification pattern: %s", vp);
|
|
|
|
out:
|
|
if (opfd != -1)
|
|
close(opfd);
|
|
if (tfmfd != -1)
|
|
close(tfmfd);
|
|
|
|
if (rc != 0)
|
|
pr_verbose(verbose, "Failed to generate the key verification "
|
|
"pattern: %s", strerror(-rc));
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Generate a key verification pattern of a secure key by encrypting the all
|
|
* zero message with the secure key using the AF_ALG interface
|
|
*
|
|
* @param[in] key the secure key token
|
|
* @param[in] key_size the size of the secure key
|
|
* @param[in] vp buffer where the verification pattern is returned
|
|
* @param[in] vp_len the size of the buffer
|
|
* @param[in] verbose if true, verbose messages are printed
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error
|
|
*/
|
|
int generate_key_verification_pattern(const u8 *key, size_t key_size,
|
|
char *vp, size_t vp_len, bool verbose)
|
|
{
|
|
return generate_aes_key_verification_pattern(key, key_size, vp, vp_len,
|
|
NULL, verbose);
|
|
}
|
|
|
|
int get_master_key_verification_pattern(const u8 *key, size_t key_size,
|
|
u8 *mkvp, bool UNUSED(verbose))
|
|
{
|
|
struct aesdatakeytoken *datakey = (struct aesdatakeytoken *)key;
|
|
struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key;
|
|
struct ep11keytoken *ep11key = (struct ep11keytoken *)key;
|
|
struct ep11keytoken *ep11key2 =
|
|
(struct ep11keytoken *)(key + sizeof(struct ep11kblob_header));
|
|
|
|
util_assert(key != NULL, "Internal error: secure_key is NULL");
|
|
util_assert(mkvp != NULL, "Internal error: mkvp is NULL");
|
|
|
|
memset(mkvp, 0, MKVP_LENGTH);
|
|
if (is_cca_aes_data_key(key, key_size))
|
|
memcpy(mkvp, &datakey->mkvp, sizeof(datakey->mkvp));
|
|
else if (is_cca_aes_cipher_key(key, key_size))
|
|
memcpy(mkvp, &cipherkey->kvp, sizeof(cipherkey->kvp));
|
|
else if (is_ep11_aes_key(key, key_size))
|
|
memcpy(mkvp, &ep11key->wkvp, sizeof(ep11key->wkvp));
|
|
else if (is_ep11_aes_key_with_header(key, key_size))
|
|
memcpy(mkvp, &ep11key2->wkvp, sizeof(ep11key2->wkvp));
|
|
else
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Check if the specified key is a CCA AESDATA key token.
|
|
*
|
|
* @param[in] key the secure key token
|
|
* @param[in] key_size the size of the secure key
|
|
*
|
|
* @returns true if the key is an CCA AESDATA token type
|
|
*/
|
|
bool is_cca_aes_data_key(const u8 *key, size_t key_size)
|
|
{
|
|
struct tokenheader *hdr = (struct tokenheader *)key;
|
|
|
|
if (key == NULL || key_size < AESDATA_KEY_SIZE)
|
|
return false;
|
|
|
|
if (hdr->type != TOKEN_TYPE_CCA_INTERNAL)
|
|
return false;
|
|
if (hdr->version != TOKEN_VERSION_AESDATA)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Check if the specified key is a CCA AESCIPHER key token.
|
|
*
|
|
* @param[in] key the secure key token
|
|
* @param[in] key_size the size of the secure key
|
|
*
|
|
* @returns true if the key is an CCA AESCIPHER token type
|
|
*/
|
|
bool is_cca_aes_cipher_key(const u8 *key, size_t key_size)
|
|
{
|
|
struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key;
|
|
|
|
if (key == NULL || key_size < AESCIPHER_KEY_SIZE)
|
|
return false;
|
|
|
|
if (cipherkey->type != TOKEN_TYPE_CCA_INTERNAL)
|
|
return false;
|
|
if (cipherkey->version != TOKEN_VERSION_AESCIPHER)
|
|
return false;
|
|
if (cipherkey->length > key_size)
|
|
return false;
|
|
|
|
if (cipherkey->kms != 0x03) /* key wrapped by master key */
|
|
return false;
|
|
if (cipherkey->kwm != 0x02) /* key wrapped using AESKW */
|
|
return false;
|
|
if (cipherkey->pfv != 0x00 && cipherkey->pfv != 0x01) /* V0 or V1 */
|
|
return false;
|
|
if (cipherkey->adv != 0x01) /* Should have ass. data sect. version 1 */
|
|
return false;
|
|
if (cipherkey->at != 0x02) /* Algorithm: AES */
|
|
return false;
|
|
if (cipherkey->kt != 0x0001) /* Key type: CIPHER */
|
|
return false;
|
|
if (cipherkey->adl != 26) /* Ass. data section length should be 26 */
|
|
return false;
|
|
if (cipherkey->kll != 0) /* Should have no key label */
|
|
return false;
|
|
if (cipherkey->eadl != 0) /* Should have no ext associated data */
|
|
return false;
|
|
if (cipherkey->uadl != 0) /* Should have no user associated data */
|
|
return false;
|
|
if (cipherkey->kufc != 2) /* Should have 2 KUFs */
|
|
return false;
|
|
if (cipherkey->kmfc != 3) /* Should have 3 KMFs */
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Check if the specified key is a EP11 AES key token.
|
|
*
|
|
* @param[in] key the secure key token
|
|
* @param[in] key_size the size of the secure key
|
|
*
|
|
* @returns true if the key is an EP11 AES token type
|
|
*/
|
|
bool is_ep11_aes_key(const u8 *key, size_t key_size)
|
|
{
|
|
struct ep11keytoken *ep11key = (struct ep11keytoken *)key;
|
|
|
|
if (key == NULL || key_size < EP11_KEY_SIZE)
|
|
return false;
|
|
|
|
if (ep11key->head.type != TOKEN_TYPE_NON_CCA)
|
|
return false;
|
|
if (ep11key->head.hver != 0)
|
|
return false;
|
|
if (ep11key->head.version != TOKEN_VERSION_EP11_AES)
|
|
return false;
|
|
if (ep11key->head.len > key_size)
|
|
return false;
|
|
|
|
if (ep11key->version != 0x1234)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Check if the specified key is a EP11 AES key token with external header.
|
|
*
|
|
* @param[in] key the secure key token
|
|
* @param[in] key_size the size of the secure key
|
|
*
|
|
* @returns true if the key is an EP11 AES token with external header type
|
|
*/
|
|
bool is_ep11_aes_key_with_header(const u8 *key, size_t key_size)
|
|
{
|
|
struct ep11kblob_header *header = (struct ep11kblob_header *)key;
|
|
struct ep11keytoken *ep11key =
|
|
(struct ep11keytoken *)(key + sizeof(struct ep11kblob_header));
|
|
|
|
if (key == NULL || key_size < EP11_AES_KEY_SIZE)
|
|
return false;
|
|
|
|
if (header->type != TOKEN_TYPE_NON_CCA)
|
|
return false;
|
|
if (header->hver != 0)
|
|
return false;
|
|
if (header->version != TOKEN_VERSION_EP11_AES_WITH_HEADER)
|
|
return false;
|
|
if (header->len > key_size)
|
|
return false;
|
|
|
|
if (ep11key->version != 0x1234)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Check if the specified EP11 AES key is session bound.
|
|
*
|
|
* @param[in] key the secure key token
|
|
* @param[in] key_size the size of the secure key
|
|
*
|
|
* @returns true if the key is an EP11 AES token type
|
|
*/
|
|
bool is_ep11_key_session_bound(const u8 *key, size_t key_size)
|
|
{
|
|
struct ep11keytoken *ep11key;
|
|
|
|
if (is_ep11_aes_key(key, key_size)) {
|
|
ep11key = (struct ep11keytoken *)key;
|
|
return memcmp(ep11key->session + sizeof(ep11key->head),
|
|
ZERO_SESSION, sizeof(ep11key->session) -
|
|
sizeof(ep11key->head)) != 0;
|
|
} else if (is_ep11_aes_key_with_header(key, key_size)) {
|
|
ep11key = (struct ep11keytoken *)
|
|
(key + sizeof(struct ep11kblob_header));
|
|
return memcmp(ep11key->session, ZERO_SESSION,
|
|
sizeof(ep11key->session)) != 0;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if the specified key is a PVSECRET-AES key token.
|
|
*
|
|
* @param[in] key the secure key token
|
|
* @param[in] key_size the size of the secure key
|
|
*
|
|
* @returns true if the key is a PVSECRET token type
|
|
*/
|
|
bool is_pvsecret_aes_key(const u8 *key, size_t key_size)
|
|
{
|
|
struct pvsecrettoken *pvsecret = (struct pvsecrettoken *)key;
|
|
|
|
if (key == NULL || key_size < PVSECRET_KEY_SIZE)
|
|
return false;
|
|
|
|
if (pvsecret->hdr.type != TOKEN_TYPE_NON_CCA)
|
|
return false;
|
|
if (pvsecret->hdr.version != TOKEN_VERSION_PVSECRET)
|
|
return false;
|
|
|
|
switch (pvsecret->secret_type) {
|
|
case UV_SECRET_TYPE_AES_128:
|
|
case UV_SECRET_TYPE_AES_192:
|
|
case UV_SECRET_TYPE_AES_256:
|
|
case UV_SECRET_TYPE_AES_XTS_128:
|
|
case UV_SECRET_TYPE_AES_XTS_256:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if the specified key is an XTS type key
|
|
*
|
|
* @param[in] key the secure key token
|
|
* @param[in] key_size the size of the secure key
|
|
*
|
|
* @returns true if the key is an XTS key type
|
|
*/
|
|
bool is_xts_key(const u8 *key, size_t key_size)
|
|
{
|
|
struct pvsecrettoken *pvsecret = (struct pvsecrettoken *)key;
|
|
|
|
if (is_cca_aes_data_key(key, key_size)) {
|
|
if (key_size == 2 * AESDATA_KEY_SIZE &&
|
|
is_cca_aes_data_key(key + AESDATA_KEY_SIZE,
|
|
key_size - AESDATA_KEY_SIZE))
|
|
return true;
|
|
} else if (is_cca_aes_cipher_key(key, key_size)) {
|
|
if (key_size == 2 * AESCIPHER_KEY_SIZE &&
|
|
is_cca_aes_cipher_key(key + AESCIPHER_KEY_SIZE,
|
|
key_size - AESCIPHER_KEY_SIZE))
|
|
return true;
|
|
} else if (is_ep11_aes_key(key, key_size)) {
|
|
if (key_size == 2 * EP11_KEY_SIZE &&
|
|
is_ep11_aes_key(key + EP11_KEY_SIZE,
|
|
key_size - EP11_KEY_SIZE))
|
|
return true;
|
|
} else if (is_ep11_aes_key_with_header(key, key_size)) {
|
|
if (key_size == 2 * EP11_AES_KEY_SIZE &&
|
|
is_ep11_aes_key_with_header(key + EP11_AES_KEY_SIZE,
|
|
key_size - EP11_AES_KEY_SIZE))
|
|
return true;
|
|
} else if (is_pvsecret_aes_key(key, key_size)) {
|
|
switch (pvsecret->secret_type) {
|
|
case UV_SECRET_TYPE_AES_XTS_128:
|
|
case UV_SECRET_TYPE_AES_XTS_256:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Check if the specified key is a secure key type (i.e. requires a crypto card)
|
|
*
|
|
* @param[in] key the secure key token
|
|
* @param[in] key_size the size of the secure key
|
|
*
|
|
* @returns true if the key is a secure key type
|
|
*/
|
|
bool is_secure_key(const u8 *key, size_t key_size)
|
|
{
|
|
if (is_cca_aes_data_key(key, key_size))
|
|
return true;
|
|
if (is_cca_aes_cipher_key(key, key_size))
|
|
return true;
|
|
if (is_ep11_aes_key(key, key_size))
|
|
return true;
|
|
if (is_ep11_aes_key_with_header(key, key_size))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Gets the size in bits of the effective key of the specified secure key
|
|
*
|
|
* @param[in] key the secure key token
|
|
* @param[in] key_size the size of the secure key
|
|
* @param[out] bitsize On return, contains the size in bits of the key.
|
|
* If the key size can not be determined, then 0 is
|
|
* passed back as bitsize.
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error
|
|
*/
|
|
int get_key_bit_size(const u8 *key, size_t key_size, size_t *bitsize)
|
|
{
|
|
struct aesdatakeytoken *datakey = (struct aesdatakeytoken *)key;
|
|
struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key;
|
|
struct ep11keytoken *ep11key = (struct ep11keytoken *)key;
|
|
struct ep11kblob_header *hdr = (struct ep11kblob_header *)key;
|
|
struct pvsecrettoken *pvsecret = (struct pvsecrettoken *)key;
|
|
|
|
util_assert(bitsize != NULL, "Internal error: bitsize is NULL");
|
|
|
|
if (is_cca_aes_data_key(key, key_size)) {
|
|
*bitsize = datakey->bitsize;
|
|
if (key_size == 2 * AESDATA_KEY_SIZE) {
|
|
datakey = (struct aesdatakeytoken *)(key +
|
|
AESDATA_KEY_SIZE);
|
|
*bitsize += datakey->bitsize;
|
|
}
|
|
} else if (is_cca_aes_cipher_key(key, key_size)) {
|
|
if (cipherkey->pfv == 0x00) /* V0 payload */
|
|
*bitsize = cipherkey->pl - 384;
|
|
else
|
|
*bitsize = 0; /* Unknown */
|
|
if (key_size == 2 * AESCIPHER_KEY_SIZE) {
|
|
cipherkey = (struct aescipherkeytoken *)(key +
|
|
AESCIPHER_KEY_SIZE);
|
|
if (cipherkey->pfv == 0x00) /* V0 payload */
|
|
*bitsize += cipherkey->pl - 384;
|
|
}
|
|
} else if (is_ep11_aes_key(key, key_size)) {
|
|
*bitsize = ep11key->head.bitlen;
|
|
if (key_size == 2 * EP11_KEY_SIZE) {
|
|
ep11key = (struct ep11keytoken *)(key + EP11_KEY_SIZE);
|
|
*bitsize += ep11key->head.bitlen;
|
|
}
|
|
} else if (is_ep11_aes_key_with_header(key, key_size)) {
|
|
*bitsize = hdr->bitlen;
|
|
if (key_size == 2 * EP11_AES_KEY_SIZE) {
|
|
hdr = (struct ep11kblob_header *)
|
|
(key + EP11_AES_KEY_SIZE);
|
|
*bitsize += hdr->bitlen;
|
|
}
|
|
} else if (is_pvsecret_aes_key(key, key_size)) {
|
|
switch (pvsecret->secret_type) {
|
|
case UV_SECRET_TYPE_AES_128:
|
|
*bitsize = 128;
|
|
break;
|
|
case UV_SECRET_TYPE_AES_192:
|
|
*bitsize = 192;
|
|
break;
|
|
case UV_SECRET_TYPE_AES_256:
|
|
*bitsize = 256;
|
|
break;
|
|
case UV_SECRET_TYPE_AES_XTS_128:
|
|
*bitsize = 128 * 2;
|
|
break;
|
|
case UV_SECRET_TYPE_AES_XTS_256:
|
|
*bitsize = 256 * 2;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
} else {
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Returns the type of the key
|
|
*
|
|
* @param[in] key the secure key token
|
|
* @param[in] key_size the size of the secure key
|
|
*
|
|
* @returns a static string on success, NULL in case of an error
|
|
*/
|
|
const char *get_key_type(const u8 *key, size_t key_size)
|
|
{
|
|
if (is_cca_aes_data_key(key, key_size))
|
|
return KEY_TYPE_CCA_AESDATA;
|
|
if (is_cca_aes_cipher_key(key, key_size))
|
|
return KEY_TYPE_CCA_AESCIPHER;
|
|
if (is_ep11_aes_key(key, key_size))
|
|
return KEY_TYPE_EP11_AES;
|
|
if (is_ep11_aes_key_with_header(key, key_size))
|
|
return KEY_TYPE_EP11_AES;
|
|
if (is_pvsecret_aes_key(key, key_size))
|
|
return KEY_TYPE_PVSECRET_AES;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the key type is a secure key type
|
|
*
|
|
* @param[in] key_type the type of the key
|
|
*
|
|
* @returns true if the key type is a secure key type, false otherwise
|
|
*/
|
|
bool is_secure_key_type(const char *key_type)
|
|
{
|
|
if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0)
|
|
return true;
|
|
if (strcasecmp(key_type, KEY_TYPE_CCA_AESDATA) == 0)
|
|
return true;
|
|
if (strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns the minimum card level for a specific key type
|
|
*
|
|
* @param[in] key_type the type of the key
|
|
*
|
|
* @returns the minimum card level, or -1 for unknown key types
|
|
*/
|
|
int get_min_card_level_for_keytype(const char *key_type)
|
|
{
|
|
if (key_type == NULL)
|
|
return -1;
|
|
|
|
if (strcasecmp(key_type, KEY_TYPE_CCA_AESDATA) == 0)
|
|
return 3;
|
|
if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0)
|
|
return 6;
|
|
if (strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0)
|
|
return 7;
|
|
|
|
return -1;
|
|
}
|
|
|
|
const struct fw_version *get_min_fw_version_for_keytype(const char *key_type)
|
|
{
|
|
static const struct fw_version ep11_fw_version = {
|
|
.major = 0, .minor = 0, .api_ordinal = 4, };
|
|
|
|
if (key_type == NULL)
|
|
return NULL;
|
|
|
|
if (strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0)
|
|
return &ep11_fw_version;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Returns the card type required for a specific key type
|
|
*
|
|
* @param[in] key_type the type of the key
|
|
*
|
|
* @returns the card type, or CARD_TYPE_ANY for unknown key types
|
|
*/
|
|
enum card_type get_card_type_for_keytype(const char *key_type)
|
|
{
|
|
if (key_type == NULL)
|
|
return CARD_TYPE_ANY;
|
|
|
|
if (strcasecmp(key_type, KEY_TYPE_CCA_AESDATA) == 0)
|
|
return CARD_TYPE_CCA;
|
|
if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0)
|
|
return CARD_TYPE_CCA;
|
|
if (strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0)
|
|
return CARD_TYPE_EP11;
|
|
|
|
return CARD_TYPE_ANY;
|
|
}
|
|
|
|
/**
|
|
* Performs extended checks on an AES CIPHER key. It checks the key usage
|
|
* fields (KUFs) and key management fields (KMFs) of the key. The function
|
|
* returns -EINVAL and issues warning messages if a mismatch is detected.
|
|
*
|
|
* @param[in] key the secure key token
|
|
* @param[in] key_size the size of the secure key
|
|
* @param[in] exportable if true the key shall be exportable
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error
|
|
*/
|
|
int check_aes_cipher_key(const u8 *key, size_t key_size, bool exportable)
|
|
{
|
|
struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key;
|
|
bool mismatch = false;
|
|
|
|
if (!is_cca_aes_cipher_key(key, key_size)) {
|
|
warnx("The key is not of type '"KEY_TYPE_CCA_AESCIPHER"'");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if ((cipherkey->kuf1 & 0x8000) == 0) {
|
|
printf("WARNING: The secure key can not be used for "
|
|
"encryption\n");
|
|
mismatch = true;
|
|
}
|
|
if ((cipherkey->kuf1 & 0x4000) == 0) {
|
|
printf("WARNING: The secure key can not be used for "
|
|
"decryption\n");
|
|
mismatch = true;
|
|
}
|
|
if (cipherkey->kuf1 & 0x1000) {
|
|
printf("WARNING: The secure key can only be used in UDXs\n");
|
|
mismatch = true;
|
|
}
|
|
|
|
if (!exportable && (cipherkey->kmf1 & 0x8000)) {
|
|
printf("WARNING: The secure key can be exported using a "
|
|
"symmetric key\n");
|
|
mismatch = true;
|
|
}
|
|
if (!exportable && (cipherkey->kmf1 & 0x4000)) {
|
|
printf("WARNING: The secure key can be exported using an "
|
|
"unauthenticated asymmetric key\n");
|
|
mismatch = true;
|
|
}
|
|
if (!exportable && (cipherkey->kmf1 & 0x2000)) {
|
|
printf("WARNING: The secure key can be exported using an "
|
|
"authenticated asymmetric key\n");
|
|
mismatch = true;
|
|
}
|
|
if (!exportable && (cipherkey->kmf1 & 0x1000)) {
|
|
printf("WARNING: The secure key can be exported using a RAW "
|
|
"key\n");
|
|
mismatch = true;
|
|
}
|
|
if ((cipherkey->kmf1 & 0x0800) == 0) {
|
|
printf("WARNING: The secure key can not be transformed into a "
|
|
"CPACF protected key\n");
|
|
mismatch = true;
|
|
}
|
|
if (!exportable && (cipherkey->kmf1 & 0x0080) == 0) {
|
|
printf("WARNING: The secure key can be exported using a DES "
|
|
"key\n");
|
|
mismatch = true;
|
|
}
|
|
if (!exportable && (cipherkey->kmf1 & 0x0040) == 0) {
|
|
printf("WARNING: The secure key can be exported using an AES "
|
|
"key\n");
|
|
mismatch = true;
|
|
}
|
|
if (!exportable && (cipherkey->kmf1 & 0x0008) == 0) {
|
|
printf("WARNING: The secure key can be exported using an RSA "
|
|
"key\n");
|
|
mismatch = true;
|
|
}
|
|
|
|
if (cipherkey->kmf2 & 0xC000) {
|
|
printf("WARNING: The secure key is incomplete\n");
|
|
mismatch = true;
|
|
}
|
|
if (cipherkey->kmf2 & 0x0010) {
|
|
printf("WARNING: The secure key was previously encrypted with "
|
|
"an untrusted KEK\n");
|
|
mismatch = true;
|
|
}
|
|
if (cipherkey->kmf2 & 0x0008) {
|
|
printf("WARNING: The secure key was previously in a format "
|
|
"without type or usage attributes\n");
|
|
mismatch = true;
|
|
}
|
|
if (cipherkey->kmf2 & 0x0004) {
|
|
printf("WARNING: The secure key was previously encrypted with "
|
|
"a key weaker than itself\n");
|
|
mismatch = true;
|
|
}
|
|
if (cipherkey->kmf2 & 0x0002) {
|
|
printf("WARNING: The secure key was previously in a non-CCA "
|
|
"format\n");
|
|
mismatch = true;
|
|
}
|
|
if (cipherkey->kmf2 & 0x0001) {
|
|
printf("WARNING: The secure key was previously encrypted in "
|
|
"ECB mode\n");
|
|
mismatch = true;
|
|
}
|
|
|
|
if ((cipherkey->kmf3 & 0xFF00) == 0x0000 ||
|
|
(cipherkey->kmf3 & 0x00FF) == 0x0000) {
|
|
printf("WARNING: The secure key was created by an unknown "
|
|
"method\n");
|
|
mismatch = true;
|
|
}
|
|
if ((cipherkey->kmf3 & 0xFF00) == 0x0400 ||
|
|
(cipherkey->kmf3 & 0x00FF) == 0x0004) {
|
|
printf("WARNING: The secure key was created from cleartext key "
|
|
"components\n");
|
|
mismatch = true;
|
|
}
|
|
if ((cipherkey->kmf3 & 0xFF00) == 0x0500 ||
|
|
(cipherkey->kmf3 & 0x00FF) == 0x0005) {
|
|
printf("WARNING: The secure key was entered as a cleartext key "
|
|
"value\n");
|
|
mismatch = true;
|
|
}
|
|
if (!exportable && (cipherkey->kmf3 & 0x00FF) == 0x0012) {
|
|
printf("WARNING: The secure key was converted from a CCA "
|
|
"key-token that had no export control attributes\n");
|
|
mismatch = true;
|
|
}
|
|
|
|
return mismatch ? -EINVAL : 0;
|
|
}
|
|
|
|
static int reencipher_cca_secure_key(struct cca_lib *cca, u8 *secure_key,
|
|
size_t secure_key_size, const char *apqns,
|
|
u8 *mkvp, enum reencipher_method method,
|
|
bool *apqn_selected, bool verbose)
|
|
{
|
|
unsigned int flags;
|
|
int rc;
|
|
|
|
if (method == REENCIPHER_OLD_TO_CURRENT)
|
|
flags = FLAG_SEL_CCA_MATCH_OLD_MKVP;
|
|
else
|
|
flags = FLAG_SEL_CCA_MATCH_CUR_MKVP |
|
|
FLAG_SEL_CCA_NEW_MUST_BE_SET;
|
|
|
|
*apqn_selected = true;
|
|
|
|
rc = select_cca_adapter_by_mkvp(cca, mkvp, apqns, flags,
|
|
verbose);
|
|
if (rc == -ENOTSUP) {
|
|
rc = 0;
|
|
*apqn_selected = false;
|
|
}
|
|
if (rc != 0) {
|
|
pr_verbose(verbose, "No APQN found that is suitable "
|
|
"for re-enciphering this secure key");
|
|
return rc;
|
|
}
|
|
|
|
rc = key_token_change(cca, secure_key, secure_key_size,
|
|
method == REENCIPHER_OLD_TO_CURRENT ?
|
|
METHOD_OLD_TO_CURRENT :
|
|
METHOD_CURRENT_TO_NEW,
|
|
verbose);
|
|
if (rc != 0) {
|
|
pr_verbose(verbose, "Failed to re-encipher secure key: "
|
|
"%s", strerror(-rc));
|
|
return rc;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int reencipher_ep11_secure_key(struct ep11_lib *ep11, u8 *secure_key,
|
|
size_t secure_key_size, const char *apqns,
|
|
u8 *mkvp, bool *apqn_selected,
|
|
bool verbose)
|
|
{
|
|
unsigned int card, domain;
|
|
unsigned int flags;
|
|
target_t target;
|
|
int rc;
|
|
|
|
flags = FLAG_SEL_EP11_MATCH_CUR_MKVP |
|
|
FLAG_SEL_EP11_NEW_MUST_BE_SET;
|
|
|
|
*apqn_selected = true;
|
|
|
|
rc = select_ep11_apqn_by_mkvp(ep11, mkvp, apqns, flags,
|
|
&target, &card, &domain, verbose);
|
|
if (rc == -ENOTSUP) {
|
|
rc = 0;
|
|
*apqn_selected = false;
|
|
}
|
|
if (rc != 0) {
|
|
pr_verbose(verbose, "No APQN found that is suitable "
|
|
"for re-enciphering this secure key");
|
|
return rc;
|
|
}
|
|
|
|
rc = reencipher_ep11_key(ep11, target, card, domain,
|
|
secure_key, secure_key_size, verbose);
|
|
free_ep11_target_for_apqn(ep11, target);
|
|
if (rc != 0) {
|
|
pr_verbose(verbose, "Failed to re-encipher secure key: "
|
|
"%s", strerror(-rc));
|
|
return rc;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Re-enciphers a secure key
|
|
*
|
|
* @param[in] lib the external library struct
|
|
* @param[in] secure_key a buffer containing the secure key
|
|
* @param[in] secure_key_size the secure key size
|
|
* @param[in] apqns a comma separated list of APQNs. If NULL is
|
|
* specified, or an empty string, then all online
|
|
* APQNs of the matching type are subject to be used.
|
|
* @param[in] method the re-encipher method
|
|
* @param[out] apqn_selected On return: true if a specific APQN was selected.
|
|
* @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 no APQN could be found with a matching master key.
|
|
* -EIO is returned if the re-enciphering has failed.
|
|
*/
|
|
int reencipher_secure_key(struct ext_lib *lib, u8 *secure_key,
|
|
size_t secure_key_size, const char *apqns,
|
|
enum reencipher_method method, bool *apqn_selected,
|
|
bool verbose)
|
|
{
|
|
u8 mkvp[MKVP_LENGTH];
|
|
int rc;
|
|
|
|
util_assert(lib != NULL, "Internal error: lib is NULL");
|
|
util_assert(secure_key != NULL, "Internal error: secure_key is NULL");
|
|
util_assert(apqn_selected != NULL,
|
|
"Internal error: apqn_selected is NULL");
|
|
|
|
*apqn_selected = true;
|
|
|
|
rc = get_master_key_verification_pattern(secure_key, secure_key_size,
|
|
mkvp, verbose);
|
|
if (rc != 0) {
|
|
pr_verbose(verbose, "Failed to get the master key verification "
|
|
"pattern: %s", strerror(-rc));
|
|
return rc;
|
|
}
|
|
|
|
if (is_ep11_aes_key(secure_key, secure_key_size) ||
|
|
is_ep11_aes_key_with_header(secure_key, secure_key_size)) {
|
|
/* EP11 secure key: need the EP11 host library */
|
|
if (lib->ep11->lib_ep11 == NULL) {
|
|
rc = load_ep11_library(lib->ep11, verbose);
|
|
if (rc != 0)
|
|
return rc;
|
|
}
|
|
|
|
if (method == REENCIPHER_OLD_TO_CURRENT) {
|
|
util_print_indented("ERROR: An APQN of a IBM "
|
|
"cryptographic adapter in EP11 "
|
|
"coprocessor mode does not have an "
|
|
"OLD master key register. Thus, "
|
|
"you can not re-encipher a secure "
|
|
"key of type 'EP11-AES' from the "
|
|
"OLD to the CURRENT master key "
|
|
"register.\n", 0);
|
|
return -EINVAL;
|
|
}
|
|
|
|
rc = reencipher_ep11_secure_key(lib->ep11, secure_key,
|
|
secure_key_size, apqns, mkvp,
|
|
apqn_selected, verbose);
|
|
} else if (is_cca_aes_data_key(secure_key, secure_key_size) ||
|
|
is_cca_aes_cipher_key(secure_key, secure_key_size)) {
|
|
/* CCA secure key: need the CCA host library */
|
|
if (lib->cca->lib_csulcca == NULL) {
|
|
rc = load_cca_library(lib->cca, verbose);
|
|
if (rc != 0)
|
|
return rc;
|
|
}
|
|
|
|
rc = reencipher_cca_secure_key(lib->cca, secure_key,
|
|
secure_key_size, apqns, mkvp,
|
|
method, apqn_selected, verbose);
|
|
} else {
|
|
pr_verbose(verbose, "Invalid key type");
|
|
rc = -EINVAL;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|