mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Make the error message when the CCA library is missing more user friendly. Also display an URL where the CCA package can be downloaded. The CCA library is used for the REENCIPHER commands of zkey and zkey-cryptsetup and must be installed separately as part of the IBM CCA Host Libraries and Tools package. This packages is not provided by the distributors, but must be downloaded from an IBM web page and installed separately. Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com> Reviewed-by: Harald Freudenberger <freude@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
912 lines
25 KiB
C
912 lines
25 KiB
C
/*
|
|
* 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 <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"
|
|
|
|
#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
|
|
|
|
/*
|
|
* Definitions for the CCA library
|
|
*/
|
|
#define CCA_LIBRARY_NAME "libcsulcca.so"
|
|
#define CCA_WEB_PAGE "http://www.ibm.com/security/cryptocards"
|
|
|
|
#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) {
|
|
pr_verbose(verbose, "%s", dlerror());
|
|
warnx("The command requires the IBM CCA Host Libraries and "
|
|
"Tools.\nFor the supported environments and downloads, "
|
|
"see:\n%s", CCA_WEB_PAGE);
|
|
return -ELIBACC;
|
|
}
|
|
|
|
/* Get the Key Token Change function */
|
|
*dll_CSNBKTC = (t_CSNBKTC)dlsym(*lib_csulcca, "CSNBKTC");
|
|
if (*dll_CSNBKTC == NULL) {
|
|
pr_verbose(verbose, "%s", dlerror());
|
|
warnx("The command requires the IBM CCA Host Libraries and "
|
|
"Tools.\nFor the supported environments and downloads, "
|
|
"see:\n%s", CCA_WEB_PAGE);
|
|
dlclose(*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 != 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 = 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 != 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;
|
|
}
|
|
|
|
/**
|
|
* 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));
|
|
warnx("Make sure that all available CCA crypto adapters are "
|
|
"setup with the same master key");
|
|
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));
|
|
warnx("Make sure that all available CCA crypto "
|
|
"adapters are setup with the same master key");
|
|
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));
|
|
warnx("Make sure that all available CCA crypto adapters are "
|
|
"setup with the same master key");
|
|
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));
|
|
warnx("Make sure that all available CCA crypto "
|
|
"adapters are setup with the same master key");
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* 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 char *key, size_t key_size,
|
|
char *vp, size_t vp_len, bool verbose)
|
|
{
|
|
int tfmfd = -1, opfd = -1, rc = 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;
|
|
}
|
|
|
|
snprintf((char *)sa.salg_name, sizeof(sa.salg_name), "%s(paes)",
|
|
key_size > SECURE_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;
|
|
}
|
|
|
|
if (setsockopt(tfmfd, SOL_ALG, ALG_SET_KEY, key,
|
|
key_size) < 0) {
|
|
rc = -errno;
|
|
pr_verbose(verbose, "Failed to set the key");
|
|
goto out;
|
|
}
|
|
|
|
opfd = accept(tfmfd, NULL, 0);
|
|
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;
|
|
}
|