mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
zkey: Externalize secure key back-end functions
To reduce the size of the zkey.c source file, all routines that deal with secure keys are moved to a new source file pkey.c. Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com> Reviewed-by: Hendrik Brueckner <brueckner@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
committed by
Jan Höppner
parent
a090a1ffe8
commit
5872f8a21b
@@ -25,9 +25,10 @@ all: check_dep zkey
|
||||
libs = $(rootdir)/libutil/libutil.a
|
||||
|
||||
zkey.o: zkey.c pkey.h misc.h
|
||||
pkey.o: pkey.c pkey.h
|
||||
properties.o: properties.c properties.h
|
||||
|
||||
zkey: zkey.o properties.o $(libs)
|
||||
zkey: zkey.o pkey.o properties.o $(libs)
|
||||
|
||||
install: all
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR)
|
||||
|
||||
748
zkey/pkey.c
Normal file
748
zkey/pkey.c
Normal file
@@ -0,0 +1,748 @@
|
||||
/*
|
||||
* zkey - Generate, re-encipher, and validate secure keys
|
||||
*
|
||||
* Copyright IBM Corp. 2018
|
||||
*
|
||||
* 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 <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.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"
|
||||
|
||||
|
||||
#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))
|
||||
|
||||
/*
|
||||
* Definitions for the CCA library
|
||||
*/
|
||||
#define CCA_LIBRARY_NAME "libcsulcca.so"
|
||||
|
||||
#define DEFAULT_KEYBITS 256
|
||||
|
||||
/**
|
||||
* Loads the CCA library and provides the entry point of the CSNBKTC function.
|
||||
*
|
||||
* @param[out] lib_csulcca on return this contains the address of the CCA
|
||||
* library. dlclose() should be used to free this
|
||||
* when no longer needed.
|
||||
* @param[out] dll_CSNBKTC on return this contains the address of the
|
||||
* CSNBKTC function.
|
||||
* @param verbose if true, verbose messages are printed
|
||||
*
|
||||
* @returns 0 on success, -ELIBACC in case of library load errors
|
||||
*/
|
||||
int load_cca_library(void **lib_csulcca, t_CSNBKTC *dll_CSNBKTC, bool verbose)
|
||||
{
|
||||
util_assert(lib_csulcca != NULL, "Internal error: lib_csulcca is NULL");
|
||||
util_assert(dll_CSNBKTC != NULL, "Internal error: dll_CSNBKTC is NULL");
|
||||
|
||||
/* Load the CCA library */
|
||||
*lib_csulcca = dlopen(CCA_LIBRARY_NAME, RTLD_GLOBAL | RTLD_NOW);
|
||||
if (*lib_csulcca == NULL) {
|
||||
warnx("%s\nEnsure that the IBM CCA Host Libraries and "
|
||||
"Tools are installed properly", dlerror());
|
||||
return -ELIBACC;
|
||||
}
|
||||
|
||||
/* Get the Key Token Change function */
|
||||
*dll_CSNBKTC = (t_CSNBKTC)dlsym(*lib_csulcca, "CSNBKTC");
|
||||
if (*dll_CSNBKTC == NULL) {
|
||||
warnx("%s\nEnsure that the IBM CCA Host Libraries and "
|
||||
"Tools are installed properly", dlerror());
|
||||
dlclose(*lib_csulcca);
|
||||
*lib_csulcca = NULL;
|
||||
return -ELIBACC;
|
||||
}
|
||||
|
||||
pr_verbose(verbose, "CCA library '%s' has been loaded successfully",
|
||||
CCA_LIBRARY_NAME);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 != SECURE_KEY_SIZE && size != 2*SECURE_KEY_SIZE) {
|
||||
warnx("File '%s' has an invalid size, %lu or %lu bytes "
|
||||
"expected", keyfile, SECURE_KEY_SIZE,
|
||||
2 * SECURE_KEY_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 <= 0) {
|
||||
msg = feof(fp) ? "File is too small" : strerror(errno);
|
||||
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 <= 0) {
|
||||
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 = DOUBLE_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 <= 0) {
|
||||
msg = feof(fp) ? "File is too small" : strerror(errno);
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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] card the card number to use (or AUTOSELECT)
|
||||
* @param[in] domain the domain number to use (or AUTOSELECT)
|
||||
* @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, u16 card, u16 domain,
|
||||
bool verbose)
|
||||
{
|
||||
struct pkey_genseck gensec;
|
||||
size_t secure_key_size;
|
||||
u8 *secure_key;
|
||||
int rc;
|
||||
|
||||
util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1");
|
||||
util_assert(keyfile != NULL, "Internal error: keyfile is NULL");
|
||||
|
||||
if (keybits == 0)
|
||||
keybits = DEFAULT_KEYBITS;
|
||||
|
||||
secure_key_size = DOUBLE_KEYSIZE_FOR_XTS(SECURE_KEY_SIZE, xts);
|
||||
secure_key = util_malloc(secure_key_size);
|
||||
|
||||
pr_verbose(verbose, "Generate key on card %02x.%04x", card, domain);
|
||||
|
||||
gensec.cardnr = card;
|
||||
gensec.domain = domain;
|
||||
switch (keybits) {
|
||||
case 128:
|
||||
gensec.keytype = PKEY_KEYTYPE_AES_128;
|
||||
break;
|
||||
case 192:
|
||||
if (xts) {
|
||||
warnx("Invalid value for '--keybits'|'-c' "
|
||||
"for XTS: '%lu'", keybits);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
gensec.keytype = PKEY_KEYTYPE_AES_192;
|
||||
break;
|
||||
case 256:
|
||||
gensec.keytype = PKEY_KEYTYPE_AES_256;
|
||||
break;
|
||||
default:
|
||||
warnx("Invalid value for '--keybits'/'-c': '%lu'", keybits);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = ioctl(pkey_fd, PKEY_GENSECK, &gensec);
|
||||
if (rc < 0) {
|
||||
rc = -errno;
|
||||
warnx("Failed to generate a secure key: %s", strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(secure_key, &gensec.seckey, SECURE_KEY_SIZE);
|
||||
|
||||
if (xts) {
|
||||
rc = ioctl(pkey_fd, PKEY_GENSECK, &gensec);
|
||||
if (rc < 0) {
|
||||
rc = -errno;
|
||||
warnx("Failed to generate a secure key: %s",
|
||||
strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(secure_key + SECURE_KEY_SIZE, &gensec.seckey,
|
||||
SECURE_KEY_SIZE);
|
||||
}
|
||||
|
||||
pr_verbose(verbose, "Successfully generated a secure key");
|
||||
|
||||
rc = write_secure_key(keyfile, secure_key, secure_key_size, verbose);
|
||||
|
||||
out:
|
||||
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] card the card number to use (or AUTOSELECT)
|
||||
* @param[in] domain the domain number to use (or AUTOSELECT)
|
||||
* @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,
|
||||
u16 card, u16 domain,
|
||||
bool verbose)
|
||||
{
|
||||
struct pkey_clr2seck clr2sec;
|
||||
size_t secure_key_size;
|
||||
size_t clear_key_size;
|
||||
u8 *secure_key;
|
||||
u8 *clear_key;
|
||||
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");
|
||||
|
||||
secure_key_size = DOUBLE_KEYSIZE_FOR_XTS(SECURE_KEY_SIZE, xts);
|
||||
secure_key = util_malloc(secure_key_size);
|
||||
|
||||
clear_key = read_clear_key(clearkeyfile, keybits, xts, &clear_key_size,
|
||||
verbose);
|
||||
if (clear_key == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
pr_verbose(verbose, "Generate key on card %02x.%04x", card, domain);
|
||||
|
||||
clr2sec.cardnr = card;
|
||||
clr2sec.domain = domain;
|
||||
switch (HALF_KEYSIZE_FOR_XTS(clear_key_size * 8, xts)) {
|
||||
case 128:
|
||||
clr2sec.keytype = PKEY_KEYTYPE_AES_128;
|
||||
break;
|
||||
case 192:
|
||||
clr2sec.keytype = PKEY_KEYTYPE_AES_192;
|
||||
break;
|
||||
case 256:
|
||||
clr2sec.keytype = PKEY_KEYTYPE_AES_256;
|
||||
break;
|
||||
default:
|
||||
warnx("Invalid clear key size: '%lu' bytes", clear_key_size);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(&clr2sec.clrkey, clear_key,
|
||||
HALF_KEYSIZE_FOR_XTS(clear_key_size, xts));
|
||||
|
||||
rc = ioctl(pkey_fd, PKEY_CLR2SECK, &clr2sec);
|
||||
if (rc < 0) {
|
||||
rc = -errno;
|
||||
warnx("Failed to generate a secure key from a "
|
||||
"clear key: %s", strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(secure_key, &clr2sec.seckey, SECURE_KEY_SIZE);
|
||||
|
||||
if (xts) {
|
||||
memcpy(&clr2sec.clrkey, clear_key + clear_key_size / 2,
|
||||
clear_key_size / 2);
|
||||
|
||||
rc = ioctl(pkey_fd, PKEY_CLR2SECK, &clr2sec);
|
||||
if (rc < 0) {
|
||||
rc = -errno;
|
||||
warnx("Failed to generate a secure key from "
|
||||
"a clear key: %s", strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(secure_key+SECURE_KEY_SIZE, &clr2sec.seckey,
|
||||
SECURE_KEY_SIZE);
|
||||
}
|
||||
|
||||
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(&clr2sec, 0, sizeof(clr2sec));
|
||||
memset(clear_key, 0, clear_key_size);
|
||||
free(clear_key);
|
||||
free(secure_key);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-enciphers a secure key.
|
||||
*
|
||||
* @param[in] dll_CSNBKTC the address of the CCA CSNBKTC function
|
||||
* @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(t_CSNBKTC dll_CSNBKTC,
|
||||
u8 *secure_key, unsigned int secure_key_size,
|
||||
char *method, bool verbose)
|
||||
{
|
||||
long exit_data_len = 0, rule_array_count;
|
||||
unsigned char rule_array[2 * 80] = { 0, };
|
||||
unsigned char exit_data[4] = { 0, };
|
||||
long return_code, reason_code;
|
||||
|
||||
util_assert(dll_CSNBKTC != NULL, "Internal error: dll_CSNBKTC 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;
|
||||
|
||||
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);
|
||||
if (return_code != 0) {
|
||||
print_CCA_error(return_code, reason_code);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (secure_key_size == 2 * SECURE_KEY_SIZE) {
|
||||
dll_CSNBKTC(&return_code, &reason_code,
|
||||
&exit_data_len, exit_data,
|
||||
&rule_array_count, rule_array,
|
||||
secure_key + SECURE_KEY_SIZE);
|
||||
|
||||
pr_verbose(verbose, "CSNBKTC (Key Token Change) with '%s' "
|
||||
"returned: return_code: %ld, reason_code: %ld",
|
||||
method, return_code, reason_code);
|
||||
if (return_code != 0) {
|
||||
print_CCA_error(return_code, reason_code);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates an XTS secure key (the second part)
|
||||
*
|
||||
* @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[in] part1_keysize the key size of the first key part
|
||||
* @param[in] part1_attributes the attributes 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,
|
||||
u8 *secure_key, size_t secure_key_size,
|
||||
u16 part1_keysize, u32 part1_attributes,
|
||||
size_t *clear_key_bitsize, bool verbose)
|
||||
{
|
||||
struct secaeskeytoken *token = (struct secaeskeytoken *)secure_key;
|
||||
struct pkey_verifykey verifykey;
|
||||
struct secaeskeytoken *token2;
|
||||
int rc;
|
||||
|
||||
util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1");
|
||||
util_assert(secure_key != NULL, "Internal error: secure_key is NULL");
|
||||
|
||||
/* XTS uses 2 secure key tokens concatenated to each other */
|
||||
token2 = (struct secaeskeytoken *)(secure_key + SECURE_KEY_SIZE);
|
||||
|
||||
if (secure_key_size != 2 * SECURE_KEY_SIZE) {
|
||||
pr_verbose(verbose, "Size of secure key is too small: "
|
||||
"%lu expected %lu", secure_key_size,
|
||||
2 * SECURE_KEY_SIZE);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (token->bitsize != token2->bitsize) {
|
||||
pr_verbose(verbose, "XTS secure key contains 2 clear keys of "
|
||||
"different sizes");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (token->keysize != token2->keysize) {
|
||||
pr_verbose(verbose, "XTS secure key contains 2 keys of "
|
||||
"different sizes");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (memcmp(&token->mkvp, &token2->mkvp, sizeof(token->mkvp)) != 0) {
|
||||
pr_verbose(verbose, "XTS secure key contains 2 keys using "
|
||||
"different CCA master keys");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memcpy(&verifykey.seckey, token2, sizeof(verifykey.seckey));
|
||||
|
||||
rc = ioctl(pkey_fd, PKEY_VERIFYKEY, &verifykey);
|
||||
if (rc < 0) {
|
||||
rc = -errno;
|
||||
pr_verbose(verbose, "Failed to validate a secure key: %s",
|
||||
strerror(-rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
if ((verifykey.attributes & PKEY_VERIFY_ATTR_AES) == 0) {
|
||||
pr_verbose(verbose, "Secure key is not an AES key");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (verifykey.keysize != part1_keysize) {
|
||||
pr_verbose(verbose, "XTS secure key contains 2 keys using "
|
||||
"different key sizes");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (verifykey.attributes != part1_attributes) {
|
||||
pr_verbose(verbose, "XTS secure key contains 2 keys using "
|
||||
"different attributes");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (clear_key_bitsize)
|
||||
*clear_key_bitsize += verifykey.keysize;
|
||||
|
||||
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 CCA master key
|
||||
* @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,
|
||||
bool verbose)
|
||||
{
|
||||
struct secaeskeytoken *token = (struct secaeskeytoken *)secure_key;
|
||||
struct pkey_verifykey verifykey;
|
||||
int rc;
|
||||
|
||||
util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1");
|
||||
util_assert(secure_key != NULL, "Internal error: secure_key is NULL");
|
||||
|
||||
if (secure_key_size < SECURE_KEY_SIZE) {
|
||||
pr_verbose(verbose, "Size of secure key is too small: "
|
||||
"%lu expected %lu", secure_key_size,
|
||||
SECURE_KEY_SIZE);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memcpy(&verifykey.seckey, token, sizeof(verifykey.seckey));
|
||||
|
||||
rc = ioctl(pkey_fd, PKEY_VERIFYKEY, &verifykey);
|
||||
if (rc < 0) {
|
||||
rc = -errno;
|
||||
pr_verbose(verbose, "Failed to validate a secure key: %s",
|
||||
strerror(-rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
if ((verifykey.attributes & PKEY_VERIFY_ATTR_AES) == 0) {
|
||||
pr_verbose(verbose, "Secure key is not an AES key");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (clear_key_bitsize)
|
||||
*clear_key_bitsize = verifykey.keysize;
|
||||
|
||||
/* XTS uses 2 secure key tokens concatenated to each other */
|
||||
if (secure_key_size > SECURE_KEY_SIZE) {
|
||||
rc = validate_secure_xts_key(pkey_fd,
|
||||
secure_key, secure_key_size,
|
||||
verifykey.keysize,
|
||||
verifykey.attributes,
|
||||
clear_key_bitsize,
|
||||
verbose);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (is_old_mk)
|
||||
*is_old_mk = (verifykey.attributes &
|
||||
PKEY_VERIFY_ATTR_OLD_MKVP) != 0;
|
||||
|
||||
pr_verbose(verbose, "Secure key validation completed successfully");
|
||||
|
||||
return 0;
|
||||
}
|
||||
44
zkey/pkey.h
44
zkey/pkey.h
@@ -4,7 +4,7 @@
|
||||
* This header file defines the interface to the pkey kernel module.
|
||||
* It defines a set of IOCTL commands with its associated structures.
|
||||
*
|
||||
* Copyright 2017 IBM Corp.
|
||||
* Copyright IBM Corp. 2017, 2018
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -82,4 +82,44 @@ struct pkey_verifykey {
|
||||
|
||||
#define PKEY_VERIFYKEY _IOWR(PKEY_IOCTL_MAGIC, 0x07, struct pkey_verifykey)
|
||||
|
||||
#endif
|
||||
#define METHOD_OLD_TO_CURRENT "RTCMK "
|
||||
#define METHOD_CURRENT_TO_NEW "RTNMK "
|
||||
|
||||
typedef void (*t_CSNBKTC)(long *return_code,
|
||||
long *reason_code,
|
||||
long *exit_data_length,
|
||||
unsigned char *exit_data,
|
||||
long *rule_array_count,
|
||||
unsigned char *rule_array,
|
||||
unsigned char *key_identifier);
|
||||
|
||||
int load_cca_library(void **lib_csulcca, t_CSNBKTC *dll_CSNBKTC, bool verbose);
|
||||
|
||||
int open_pkey_device(bool verbose);
|
||||
|
||||
int generate_secure_key_random(int pkey_fd, const char *keyfile,
|
||||
size_t keybits, bool xts, u16 card, u16 domain,
|
||||
bool verbose);
|
||||
|
||||
int generate_secure_key_clear(int pkey_fd, const char *keyfile,
|
||||
size_t keybits, bool xts,
|
||||
const char *clearkeyfile,
|
||||
u16 card, u16 domain,
|
||||
bool verbose);
|
||||
|
||||
u8 *read_secure_key(const char *keyfile, size_t *secure_key_size,
|
||||
bool verbose);
|
||||
|
||||
int write_secure_key(const char *keyfile, const u8 *secure_key,
|
||||
size_t secure_key_size, bool verbose);
|
||||
|
||||
int validate_secure_key(int pkey_fd,
|
||||
u8 *secure_key, size_t secure_key_size,
|
||||
size_t *clear_key_bitsize, int *is_old_mk,
|
||||
bool verbose);
|
||||
|
||||
int key_token_change(t_CSNBKTC dll_CSNBKTC,
|
||||
u8 *secure_key, unsigned int secure_key_size,
|
||||
char *method, bool verbose);
|
||||
|
||||
#endif
|
||||
|
||||
581
zkey/zkey.c
581
zkey/zkey.c
@@ -188,9 +188,6 @@ static struct zkey_command zkey_commands[] = {
|
||||
#define pr_verbose(fmt...) if (g.verbose) \
|
||||
warnx(fmt)
|
||||
|
||||
#define DOUBLE_KEYSIZE_FOR_XTS(keysize, xts) (xts) ? 2 * (keysize) : (keysize)
|
||||
#define HALF_KEYSIZE_FOR_XTS(keysize, xts) (xts) ? (keysize) / 2 : (keysize)
|
||||
|
||||
static void print_usage_command(const struct zkey_command *command)
|
||||
{
|
||||
char command_str[ZKEY_COMMAND_STR_LEN];
|
||||
@@ -252,19 +249,6 @@ static void print_help(const struct zkey_command *command)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Definitions for the CCA library
|
||||
*/
|
||||
#define CCA_LIBRARY_NAME "libcsulcca.so"
|
||||
|
||||
typedef void (*t_CSNBKTC)(long *return_code,
|
||||
long *reason_code,
|
||||
long *exit_data_length,
|
||||
unsigned char *exit_data,
|
||||
long *rule_array_count,
|
||||
unsigned char *rule_array,
|
||||
unsigned char *key_identifier);
|
||||
|
||||
/*
|
||||
* Global variables for program options
|
||||
*/
|
||||
@@ -283,212 +267,6 @@ static struct zkey_globals {
|
||||
.pkey_fd = -1,
|
||||
};
|
||||
|
||||
#define DEFAULT_KEYBITS 256
|
||||
|
||||
static int load_cca_library(void)
|
||||
{
|
||||
/* Load the CCA library */
|
||||
g.lib_csulcca = dlopen(CCA_LIBRARY_NAME, RTLD_GLOBAL | RTLD_NOW);
|
||||
if (g.lib_csulcca == NULL) {
|
||||
warnx("%s\nEnsure that the IBM CCA Host Libraries and "
|
||||
"Tools are installed properly", dlerror());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Get the Key Token Change function */
|
||||
g.dll_CSNBKTC = (t_CSNBKTC)dlsym(g.lib_csulcca, "CSNBKTC");
|
||||
if (g.dll_CSNBKTC == NULL) {
|
||||
warnx("%s\nEnsure that the IBM CCA Host Libraries and "
|
||||
"Tools are installed properly", dlerror());
|
||||
dlclose(g.lib_csulcca);
|
||||
g.lib_csulcca = NULL;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
pr_verbose("CCA library '%s' has been loaded successfully",
|
||||
CCA_LIBRARY_NAME);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static int open_pkey_device(void)
|
||||
{
|
||||
g.pkey_fd = open(PKEYDEVICE, O_RDWR);
|
||||
if (g.pkey_fd < 0) {
|
||||
warnx("File '%s:' %s\nEnsure that the 'pkey' kernel module "
|
||||
"is loaded", PKEYDEVICE, strerror(errno));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
pr_verbose("Device '%s' has been opened successfully", PKEYDEVICE);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a secure key file and return the allocated buffer and size
|
||||
*/
|
||||
static u8 *read_secure_key(const char *keyfile, size_t *secure_key_size)
|
||||
{
|
||||
size_t count, size;
|
||||
struct stat sb;
|
||||
char *msg;
|
||||
FILE *fp;
|
||||
u8 *buf;
|
||||
|
||||
if (stat(keyfile, &sb)) {
|
||||
warnx("File '%s': %s", keyfile, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
size = sb.st_size;
|
||||
|
||||
if (size != SECURE_KEY_SIZE && size != 2*SECURE_KEY_SIZE) {
|
||||
warnx("File '%s' has an invalid size, %lu or %lu bytes "
|
||||
"expected", keyfile, SECURE_KEY_SIZE,
|
||||
2 * SECURE_KEY_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 <= 0) {
|
||||
msg = feof(fp) ? "File is too small" : strerror(errno);
|
||||
warnx("File '%s': %s", keyfile, msg);
|
||||
free(buf);
|
||||
buf = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*secure_key_size = size;
|
||||
|
||||
if (g.verbose) {
|
||||
pr_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
|
||||
*/
|
||||
static int write_secure_key(const char *keyfile, const u8 *secure_key,
|
||||
size_t secure_key_size)
|
||||
{
|
||||
size_t count;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(keyfile, "w");
|
||||
if (fp == NULL) {
|
||||
warnx("File '%s': %s", keyfile, strerror(errno));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
count = fwrite(secure_key, 1, secure_key_size, fp);
|
||||
if (count <= 0) {
|
||||
warnx("File '%s': %s", keyfile, strerror(errno));
|
||||
fclose(fp);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (g.verbose) {
|
||||
pr_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 EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a clear key file and return the allocated buffer and size
|
||||
*
|
||||
* When keybits is 0, then the file size determines the keybits.
|
||||
*/
|
||||
static u8 *read_clear_key(const char *keyfile, size_t keybits,
|
||||
size_t *clear_key_size)
|
||||
{
|
||||
size_t count, size, expected_size;
|
||||
struct stat sb;
|
||||
char *msg;
|
||||
FILE *fp;
|
||||
u8 *buf;
|
||||
|
||||
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, g.xts);
|
||||
if (size != expected_size) {
|
||||
warnx("File '%s' has an invalid size, "
|
||||
"%lu bytes expected", keyfile, expected_size);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
keybits = DOUBLE_KEYSIZE_FOR_XTS(size * 8, g.xts);
|
||||
}
|
||||
|
||||
switch (keybits) {
|
||||
case 128:
|
||||
break;
|
||||
case 192:
|
||||
if (g.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 (g.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 <= 0) {
|
||||
msg = feof(fp) ? "File is too small" : strerror(errno);
|
||||
warnx("File '%s': %s", keyfile, msg);
|
||||
free(buf);
|
||||
buf = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*clear_key_size = size;
|
||||
|
||||
if (g.verbose) {
|
||||
pr_verbose("%lu bytes read from file '%s'", size, keyfile);
|
||||
util_hexdump_grp(stderr, NULL, buf, 4, size, 0);
|
||||
}
|
||||
out:
|
||||
fclose(fp);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* Command handler for 'generate with clear key'
|
||||
*
|
||||
@@ -496,76 +274,15 @@ out:
|
||||
*/
|
||||
static int command_generate_clear(const char *keyfile)
|
||||
{
|
||||
struct pkey_clr2seck clr2sec;
|
||||
size_t secure_key_size;
|
||||
size_t clear_key_size;
|
||||
u8 *secure_key;
|
||||
u8 *clear_key;
|
||||
int rc;
|
||||
|
||||
secure_key_size = DOUBLE_KEYSIZE_FOR_XTS(SECURE_KEY_SIZE, g.xts);
|
||||
secure_key = util_malloc(secure_key_size);
|
||||
|
||||
clear_key = read_clear_key(g.clearkeyfile, g.keybits, &clear_key_size);
|
||||
if (clear_key == NULL)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
clr2sec.cardnr = AUTOSELECT;
|
||||
clr2sec.domain = AUTOSELECT;
|
||||
switch (HALF_KEYSIZE_FOR_XTS(clear_key_size * 8, g.xts)) {
|
||||
case 128:
|
||||
clr2sec.keytype = PKEY_KEYTYPE_AES_128;
|
||||
break;
|
||||
case 192:
|
||||
clr2sec.keytype = PKEY_KEYTYPE_AES_192;
|
||||
break;
|
||||
case 256:
|
||||
clr2sec.keytype = PKEY_KEYTYPE_AES_256;
|
||||
break;
|
||||
default:
|
||||
warnx("Invalid clear key size: '%lu' bytes", clear_key_size);
|
||||
rc = generate_secure_key_clear(g.pkey_fd, keyfile,
|
||||
g.keybits, g.xts,
|
||||
g.clearkeyfile,
|
||||
AUTOSELECT, AUTOSELECT,
|
||||
g.verbose);
|
||||
if (rc != 0)
|
||||
rc = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(&clr2sec.clrkey, clear_key,
|
||||
HALF_KEYSIZE_FOR_XTS(clear_key_size, g.xts));
|
||||
|
||||
rc = ioctl(g.pkey_fd, PKEY_CLR2SECK, &clr2sec);
|
||||
if (rc < 0) {
|
||||
warnx("Failed to generate a secure key from a "
|
||||
"clear key: %s", strerror(errno));
|
||||
rc = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(secure_key, &clr2sec.seckey, SECURE_KEY_SIZE);
|
||||
|
||||
if (g.xts) {
|
||||
memcpy(&clr2sec.clrkey, clear_key + clear_key_size / 2,
|
||||
clear_key_size / 2);
|
||||
|
||||
rc = ioctl(g.pkey_fd, PKEY_CLR2SECK, &clr2sec);
|
||||
if (rc < 0) {
|
||||
warnx("Failed to generate a secure key from "
|
||||
"a clear key: %s", strerror(errno));
|
||||
rc = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(secure_key+SECURE_KEY_SIZE, &clr2sec.seckey,
|
||||
SECURE_KEY_SIZE);
|
||||
}
|
||||
|
||||
pr_verbose("Successfully generated a secure key from a clear key");
|
||||
|
||||
rc = write_secure_key(keyfile, secure_key, secure_key_size);
|
||||
|
||||
out:
|
||||
memset(&clr2sec, 0, sizeof(clr2sec));
|
||||
memset(clear_key, 0, clear_key_size);
|
||||
free(clear_key);
|
||||
free(secure_key);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -576,69 +293,15 @@ out:
|
||||
*/
|
||||
static int command_generate_random(const char *keyfile)
|
||||
{
|
||||
struct pkey_genseck gensec;
|
||||
size_t secure_key_size;
|
||||
u8 *secure_key;
|
||||
int rc;
|
||||
|
||||
if (g.keybits == 0)
|
||||
g.keybits = DEFAULT_KEYBITS;
|
||||
|
||||
secure_key_size = DOUBLE_KEYSIZE_FOR_XTS(SECURE_KEY_SIZE, g.xts);
|
||||
secure_key = util_malloc(secure_key_size);
|
||||
|
||||
gensec.cardnr = AUTOSELECT;
|
||||
gensec.domain = AUTOSELECT;
|
||||
switch (g.keybits) {
|
||||
case 128:
|
||||
gensec.keytype = PKEY_KEYTYPE_AES_128;
|
||||
break;
|
||||
case 192:
|
||||
if (g.xts) {
|
||||
warnx("Invalid value for '--keybits'|'-c' "
|
||||
"for XTS: '%lu'", g.keybits);
|
||||
rc = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
gensec.keytype = PKEY_KEYTYPE_AES_192;
|
||||
break;
|
||||
case 256:
|
||||
gensec.keytype = PKEY_KEYTYPE_AES_256;
|
||||
break;
|
||||
default:
|
||||
warnx("Invalid value for '--keybits'/'-c': '%lu'", g.keybits);
|
||||
rc = generate_secure_key_random(g.pkey_fd, keyfile,
|
||||
g.keybits, g.xts,
|
||||
AUTOSELECT, AUTOSELECT,
|
||||
g.verbose);
|
||||
if (rc != 0)
|
||||
rc = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = ioctl(g.pkey_fd, PKEY_GENSECK, &gensec);
|
||||
if (rc < 0) {
|
||||
warnx("Failed to generate a secure key: %s", strerror(errno));
|
||||
rc = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(secure_key, &gensec.seckey, SECURE_KEY_SIZE);
|
||||
|
||||
if (g.xts) {
|
||||
rc = ioctl(g.pkey_fd, PKEY_GENSECK, &gensec);
|
||||
if (rc < 0) {
|
||||
warnx("Failed to generate a secure key: %s",
|
||||
strerror(errno));
|
||||
rc = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(secure_key + SECURE_KEY_SIZE, &gensec.seckey,
|
||||
SECURE_KEY_SIZE);
|
||||
}
|
||||
|
||||
pr_verbose("Successfully generated a secure key");
|
||||
|
||||
rc = write_secure_key(keyfile, secure_key, secure_key_size);
|
||||
|
||||
out:
|
||||
free(secure_key);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -653,182 +316,6 @@ static int command_generate(const char *keyfile)
|
||||
: command_generate_random(keyfile);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static int key_token_change(u8 *secure_key, unsigned int secure_key_size,
|
||||
char *method)
|
||||
{
|
||||
long exit_data_len = 0, rule_array_count;
|
||||
unsigned char rule_array[2 * 80] = { 0, };
|
||||
unsigned char exit_data[4] = { 0, };
|
||||
long return_code, reason_code;
|
||||
|
||||
memcpy(rule_array, method, 8);
|
||||
memcpy(rule_array + 8, "AES ", 8);
|
||||
rule_array_count = 2;
|
||||
|
||||
g.dll_CSNBKTC(&return_code, &reason_code,
|
||||
&exit_data_len, exit_data,
|
||||
&rule_array_count, rule_array,
|
||||
secure_key);
|
||||
|
||||
pr_verbose("CSNBKTC (Key Token Change) with '%s' returned: "
|
||||
"return_code: %ld, reason_code: %ld",
|
||||
method, return_code, reason_code);
|
||||
if (return_code != 0) {
|
||||
print_CCA_error(return_code, reason_code);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (secure_key_size == 2 * SECURE_KEY_SIZE) {
|
||||
g.dll_CSNBKTC(&return_code, &reason_code,
|
||||
&exit_data_len, exit_data,
|
||||
&rule_array_count, rule_array,
|
||||
secure_key + SECURE_KEY_SIZE);
|
||||
|
||||
pr_verbose("CSNBKTC (Key Token Change) with '%s' "
|
||||
"returned: return_code: %ld, reason_code: %ld",
|
||||
method, return_code, reason_code);
|
||||
if (return_code != 0) {
|
||||
print_CCA_error(return_code, reason_code);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static int validate_secure_xts_key(u8 *secure_key, size_t secure_key_size,
|
||||
u16 part1_keysize, u32 part1_attributes,
|
||||
size_t *clear_key_bitsize)
|
||||
{
|
||||
struct secaeskeytoken *token = (struct secaeskeytoken *)secure_key;
|
||||
struct pkey_verifykey verifykey;
|
||||
struct secaeskeytoken *token2;
|
||||
int rc;
|
||||
|
||||
/* XTS uses 2 secure key tokens concatenated to each other */
|
||||
token2 = (struct secaeskeytoken *)(secure_key + SECURE_KEY_SIZE);
|
||||
|
||||
if (secure_key_size != 2 * SECURE_KEY_SIZE) {
|
||||
pr_verbose("Size of secure key is too small: %lu expected %lu",
|
||||
secure_key_size, 2 * SECURE_KEY_SIZE);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (token->bitsize != token2->bitsize) {
|
||||
pr_verbose("XTS secure key contains 2 clear keys of "
|
||||
"different sizes");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (token->keysize != token2->keysize) {
|
||||
pr_verbose("XTS secure key contains 2 keys of different "
|
||||
"sizes");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (memcmp(&token->mkvp, &token2->mkvp, sizeof(token->mkvp)) != 0) {
|
||||
pr_verbose("XTS secure key contains 2 keys using different "
|
||||
"CCA master keys");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
memcpy(&verifykey.seckey, token2, sizeof(verifykey.seckey));
|
||||
|
||||
rc = ioctl(g.pkey_fd, PKEY_VERIFYKEY, &verifykey);
|
||||
if (rc < 0) {
|
||||
warnx("Failed to validate a secure key: %s", strerror(errno));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if ((verifykey.attributes & PKEY_VERIFY_ATTR_AES) == 0) {
|
||||
pr_verbose("Secure key is not an AES key");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (verifykey.keysize != part1_keysize) {
|
||||
pr_verbose("XTS secure key contains 2 keys using different "
|
||||
"key sizes");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (verifykey.attributes != part1_attributes) {
|
||||
pr_verbose("XTS secure key contains 2 keys using different "
|
||||
"attributes");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (clear_key_bitsize)
|
||||
*clear_key_bitsize += verifykey.keysize;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static int validate_secure_key(u8 *secure_key, size_t secure_key_size,
|
||||
size_t *clear_key_bitsize, int *is_old_mk)
|
||||
{
|
||||
struct secaeskeytoken *token = (struct secaeskeytoken *)secure_key;
|
||||
struct pkey_verifykey verifykey;
|
||||
int rc;
|
||||
|
||||
if (secure_key_size < SECURE_KEY_SIZE) {
|
||||
pr_verbose("Size of secure key is too small: %lu expected %lu",
|
||||
secure_key_size, SECURE_KEY_SIZE);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
memcpy(&verifykey.seckey, token, sizeof(verifykey.seckey));
|
||||
|
||||
rc = ioctl(g.pkey_fd, PKEY_VERIFYKEY, &verifykey);
|
||||
if (rc < 0) {
|
||||
warnx("Failed to validate a secure key: %s", strerror(errno));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if ((verifykey.attributes & PKEY_VERIFY_ATTR_AES) == 0) {
|
||||
pr_verbose("Secure key is not an AES key");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (clear_key_bitsize)
|
||||
*clear_key_bitsize = verifykey.keysize;
|
||||
|
||||
/* XTS uses 2 secure key tokens concatenated to each other */
|
||||
if (secure_key_size > SECURE_KEY_SIZE) {
|
||||
rc = validate_secure_xts_key(secure_key, secure_key_size,
|
||||
verifykey.keysize,
|
||||
verifykey.attributes,
|
||||
clear_key_bitsize);
|
||||
if (rc != EXIT_SUCCESS)
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (is_old_mk)
|
||||
*is_old_mk = (verifykey.attributes &
|
||||
PKEY_VERIFY_ATTR_OLD_MKVP) != 0;
|
||||
|
||||
pr_verbose("Secure key validation completed successfully");
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Command handler for 'reencipher'.
|
||||
@@ -842,14 +329,15 @@ static int command_reencipher(const char *keyfile)
|
||||
u8 *secure_key;
|
||||
|
||||
/* Read the secure key to be re-enciphered */
|
||||
secure_key = read_secure_key(keyfile, &secure_key_size);
|
||||
secure_key = read_secure_key(keyfile, &secure_key_size, g.verbose);
|
||||
if (secure_key == NULL)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
rc = validate_secure_key(secure_key, secure_key_size, NULL,
|
||||
&is_old_mk);
|
||||
if (rc != EXIT_SUCCESS) {
|
||||
rc = validate_secure_key(g.pkey_fd, secure_key, secure_key_size, NULL,
|
||||
&is_old_mk, g.verbose);
|
||||
if (rc != 0) {
|
||||
warnx("The secure key in file '%s' is not valid", keyfile);
|
||||
rc = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -884,10 +372,14 @@ static int command_reencipher(const char *keyfile)
|
||||
pr_verbose("Secure key will be re-enciphered from OLD to the "
|
||||
"CURRENT CCA master key");
|
||||
|
||||
rc = key_token_change(secure_key, secure_key_size, "RTCMK ");
|
||||
if (rc != EXIT_SUCCESS) {
|
||||
rc = key_token_change(g.dll_CSNBKTC,
|
||||
secure_key, secure_key_size,
|
||||
METHOD_OLD_TO_CURRENT,
|
||||
g.verbose);
|
||||
if (rc != 0) {
|
||||
warnx("Re-encipher from OLD to CURRENT CCA "
|
||||
"master key has failed");
|
||||
rc = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@@ -895,10 +387,13 @@ static int command_reencipher(const char *keyfile)
|
||||
pr_verbose("Secure key will be re-enciphered from CURRENT "
|
||||
"to the NEW CCA master key");
|
||||
|
||||
rc = key_token_change(secure_key, secure_key_size, "RTNMK ");
|
||||
if (rc != EXIT_SUCCESS) {
|
||||
rc = key_token_change(g.dll_CSNBKTC,
|
||||
secure_key, secure_key_size,
|
||||
METHOD_CURRENT_TO_NEW, g.verbose);
|
||||
if (rc != 0) {
|
||||
warnx("Re-encipher from CURRENT to NEW CCA "
|
||||
"master key has failed");
|
||||
rc = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@@ -907,7 +402,9 @@ static int command_reencipher(const char *keyfile)
|
||||
|
||||
/* Write the migrated secure key */
|
||||
rc = write_secure_key(g.outputfile ? g.outputfile : keyfile,
|
||||
secure_key, secure_key_size);
|
||||
secure_key, secure_key_size, g.verbose);
|
||||
if (rc != 0)
|
||||
rc = EXIT_FAILURE;
|
||||
out:
|
||||
free(secure_key);
|
||||
return rc;
|
||||
@@ -927,14 +424,15 @@ static int command_validate(const char *keyfile)
|
||||
int rc;
|
||||
|
||||
/* Read the secure key to be re-enciphered */
|
||||
secure_key = read_secure_key(keyfile, &secure_key_size);
|
||||
secure_key = read_secure_key(keyfile, &secure_key_size, g.verbose);
|
||||
if (secure_key == NULL)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
rc = validate_secure_key(secure_key, secure_key_size, &clear_key_size,
|
||||
&is_old_mk);
|
||||
if (rc != EXIT_SUCCESS) {
|
||||
rc = validate_secure_key(g.pkey_fd, secure_key, secure_key_size,
|
||||
&clear_key_size, &is_old_mk, g.verbose);
|
||||
if (rc != 0) {
|
||||
warnx("The secure key in file '%s' is not valid", keyfile);
|
||||
rc = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -1083,13 +581,14 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
if (command->need_cca_library) {
|
||||
rc = load_cca_library();
|
||||
if (rc != EXIT_SUCCESS)
|
||||
rc = load_cca_library(&g.lib_csulcca, &g.dll_CSNBKTC,
|
||||
g.verbose);
|
||||
if (rc != 0)
|
||||
goto out;
|
||||
}
|
||||
if (command->need_pkey_device) {
|
||||
rc = open_pkey_device();
|
||||
if (rc != EXIT_SUCCESS)
|
||||
g.pkey_fd = open_pkey_device(g.verbose);
|
||||
if (g.pkey_fd == -1)
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user