libekmfweb: Re-encipher the identity key

The identity key of the client is a secure key enciphered with the
master key of a cryptographic adapter (APQN). When the master key of
the used APQNs is changed, the identity key must be re-enciphered
under the new master key.

Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
Ingo Franzki
2020-04-23 15:41:12 +02:00
committed by Jan Höppner
parent 5fb30f1e6f
commit 1cdfb4946e
5 changed files with 355 additions and 1 deletions

View File

@@ -170,4 +170,42 @@ int ekmf_generate_identity_key(const struct ekmf_config *config,
const struct ekmf_ext_lib *ext_lib,
bool verbose);
/**
* Re-encipher the secure identity key (form field identity_secure_key in
* config) used to identify the client to EKMFWeb.
* The secure key blob is encrypted using the HSM master key. Whenever the HSM
* master key is being changed, the secure identity key must be re-enciphered.
* You can either pro-actively re-encipher a secure key once the new master key
* has been prepared (but not yet made active): to_new = true; or you can
* re-encipher a secure key when the HSM master key has already been changed:
* to_new = false. This requires that the HSM still has the old master key.
* Not all HSMs support this.
*
* For pro-active re-encipherment it is suggested to store the re-enciphered
* secure key on a separate place, until the new HSM master key has been made
* active. Specify a file name in reenc_secure_key to do so. For an in-place
* re-encipherment, set reenc_secure_key = NULL.
*
* @param config the configuration structure. Only field
* identity_secure_key must be specified, all others
* are optional.
* @param to_new If true: the identity key is re-enciphered from the
* current to the new master key.
* If false: the identity key is re-enciphered from the
* old to the current master key.
* @param reenc_secure_key if not NULL, then the re-enciphered secure key is
* stored into the filename specified here. Otherwise
* the re-enciphered secure key replaces the original
* secure identity key.
* @param ext_lib External secure key crypto library to use
* @param verbose if true, verbose messages are printed
*
* @returns a negative errno in case of an error, 0 if success.
* A -ENODEV indicates that the master keys are not loaded.
*/
int ekmf_reencipher_identity_key(const struct ekmf_config *config,
bool to_new, const char *reenc_secure_key,
const struct ekmf_ext_lib *ext_lib,
bool verbose);
#endif

View File

@@ -51,6 +51,45 @@ struct cca_rsa_key_pair_value_struct {
#define CCA_PRIME_CURVE 0x00
#define CCA_BRAINPOOL_CURVE 0x01
struct cca_token_header {
uint8_t token_identifier;
uint8_t token_version1; /* Used for PKA key tokens */
uint16_t token_length;
uint8_t token_version2; /* Used for symmetric key tokens */
uint8_t reserved[3];
} __packed;
/* Key token identifiers */
#define CCA_TOKEN_ID_NULL 0x00
#define CCA_TOKEN_ID_INTERNAL_SYMMETRIC 0x01
#define CCA_TOKEN_ID_EXTERNAL_SYMMETRIC 0x02
#define CCA_TOKEN_ID_EXTERNAL_PKA 0x1e
#define CCA_TOKEN_ID_INTERNAL_PKA 0x1f
/* Key token versions */
#define CCA_TOKEN_VERS1_V0 0x00
#define CCA_TOKEN_VERS2_DES_V0 0x00
#define CCA_TOKEN_VERS2_DES_V1 0x01
#define CCA_TOKEN_VERS2_AES_DATA 0x04
#define CCA_TOKEN_VERS2_AES_CIPHER 0x05
struct cca_section_header {
uint8_t section_identifier;
uint8_t section_version;
uint16_t section_length;
} __packed;
#define CCA_SECTION_ID_RSA_ME_1024_PRIV 0x02
#define CCA_SECTION_ID_RSA_PUBL 0x04
#define CCA_SECTION_ID_RSA_CRT_2048_PRIV 0x05
#define CCA_SECTION_ID_RSA_ME_1024_OPK_PRIV 0x06
#define CCA_SECTION_ID_RSA_CRT_4096_OPK_PRIV 0x08
#define CCA_SECTION_ID_RSA_ME_4096_PRIV 0x09
#define CCA_SECTION_ID_ECC_PRIV 0x20
#define CCA_SECTION_ID_ECC_PUBL 0x21
#define CCA_SECTION_ID_RSA_ME_1024_EOPK_PRIV 0x30
#define CCA_SECTION_ID_RSA_CRT_4096_EOPK_PRIV 0x31
/**
* Gets the CCA library function entry points from the library handle
*/
@@ -62,8 +101,10 @@ static int _cca_get_library_functions(const struct ekmf_cca_lib *cca_lib,
cca->dll_CSNDPKB = (CSNDPKB_t)dlsym(cca_lib->cca_lib, "CSNDPKB");
cca->dll_CSNDPKG = (CSNDPKG_t)dlsym(cca_lib->cca_lib, "CSNDPKG");
cca->dll_CSNDKTC = (CSNDKTC_t)dlsym(cca_lib->cca_lib, "CSNDKTC");
if (cca->dll_CSNDPKB == NULL || cca->dll_CSNDPKG == NULL)
if (cca->dll_CSNDPKB == NULL || cca->dll_CSNDPKG == NULL ||
cca->dll_CSNDKTC == NULL)
return -EIO;
return 0;
@@ -310,3 +351,171 @@ int cca_generate_rsa_key_pair(const struct ekmf_cca_lib *cca_lib,
return 0;
}
/**
* Finds a specific section of a CCA internal PKA key token.
*/
static const void *_cca_get_pka_section(const unsigned char *key_token,
size_t key_token_length,
unsigned int section_id, bool verbose)
{
const struct cca_section_header *section_hdr;
const struct cca_token_header *token_hdr;
size_t ofs;
if (key_token == NULL)
return NULL;
if (key_token_length < sizeof(struct cca_token_header)) {
pr_verbose(verbose, "key token length too small");
return NULL;
}
token_hdr = (struct cca_token_header *)key_token;
if (token_hdr->token_length > key_token_length) {
pr_verbose(verbose, "key token length too small");
return NULL;
}
if (token_hdr->token_identifier != CCA_TOKEN_ID_INTERNAL_PKA) {
pr_verbose(verbose, "not an internal PKA token");
return NULL;
}
if (token_hdr->token_version1 != CCA_TOKEN_VERS1_V0) {
pr_verbose(verbose, "invalid token version");
return NULL;
}
ofs = sizeof(struct cca_token_header);
section_hdr = (struct cca_section_header *)&key_token[ofs];
while (section_hdr->section_identifier != section_id) {
ofs += section_hdr->section_length;
if (ofs >= token_hdr->token_length) {
pr_verbose(verbose, "section %u not found", section_id);
return NULL;
}
section_hdr = (struct cca_section_header *)&key_token[ofs];
}
if (ofs + section_hdr->section_length > token_hdr->token_length) {
pr_verbose(verbose, "section exceed the token length");
return NULL;
}
return section_hdr;
}
/**
* Queries the PKEY type of the key token.
*
* @param key_token the key token containing an CCA ECC key
* @param key_token_length the size of the key token
* @param pkey_type On return: the PKEY type of the key token
*
* @returns a negative errno in case of an error, 0 if success.
*/
int cca_get_key_type(const unsigned char *key_token, size_t key_token_length,
int *pkey_type)
{
if (key_token == NULL || pkey_type == NULL)
return -EINVAL;
if (_cca_get_pka_section(key_token, key_token_length,
CCA_SECTION_ID_ECC_PUBL, false) != NULL)
*pkey_type = EVP_PKEY_EC;
else if (_cca_get_pka_section(key_token, key_token_length,
CCA_SECTION_ID_RSA_PUBL, false) != NULL)
*pkey_type = EVP_PKEY_RSA;
else
return -EINVAL;
return 0;
}
/**
* Re-enciphers a key token with a new CCA master key.
*
* @param cca_lib the CCA library structure
* @param key_token the key token containing an CCA ECC or RSA key.
* The re-enciphered key token is returned in the same
* buffer. The size of the re-enciphered key token
* remains the same.
* @param key_token_length the size of the key token
* @param to_new If true: the key token is re-enciphered from the
* current to the new master key.
* If false: the key token is re-enciphered from the
* old to the current master key.
* @param verbose if true, verbose messages are printed
*
* @returns a negative errno in case of an error, 0 if success.
* -ENODEV is returned if the master keys are not loaded.
*/
int cca_reencipher_key(const struct ekmf_cca_lib *cca_lib,
const unsigned char *key_token, size_t key_token_length,
bool to_new, bool verbose)
{
long return_code, reason_code, rule_array_count, exit_data_len = 0;
unsigned char rule_array[2 * CCA_KEYWORD_SIZE] = { 0, };
unsigned char *exit_data = NULL;
struct cca_lib cca;
long token_length;
int rc, type;
if (cca_lib == NULL || key_token == NULL)
return -EINVAL;
rc = _cca_get_library_functions(cca_lib, &cca);
if (rc != 0) {
pr_verbose(verbose, "Failed to get CCA functions from library");
return rc;
}
rc = cca_get_key_type(key_token, key_token_length, &type);
if (rc != 0) {
pr_verbose(verbose, "Failed to determine the key token type");
return rc;
}
rule_array_count = 2;
switch (type) {
case EVP_PKEY_EC:
memcpy(rule_array, "ECC ", CCA_KEYWORD_SIZE);
break;
case EVP_PKEY_RSA:
case EVP_PKEY_RSA_PSS:
memcpy(rule_array, "RSA ", CCA_KEYWORD_SIZE);
break;
default:
pr_verbose(verbose, "Invalid key token type: %d", type);
return -EINVAL;
}
if (to_new)
memcpy(rule_array + CCA_KEYWORD_SIZE, "RTNMK ",
CCA_KEYWORD_SIZE);
else
memcpy(rule_array + CCA_KEYWORD_SIZE, "RTCMK ",
CCA_KEYWORD_SIZE);
token_length = key_token_length;
cca.dll_CSNDKTC(&return_code, &reason_code,
&exit_data_len, exit_data,
&rule_array_count, rule_array,
&token_length, (unsigned char *)key_token);
if (return_code != 0) {
pr_verbose(verbose, "CCA CSNDKTC (PKA KEY TOKEN CHANGE) failed:"
" return_code: %ld reason_code: %ld", return_code,
reason_code);
if (return_code == 12 && reason_code == 764) {
pr_verbose(verbose, "The master keys are not loaded");
return -ENODEV;
}
return -EIO;
}
return 0;
}

View File

@@ -13,6 +13,8 @@
#include <stddef.h>
#include <stdbool.h>
#include <openssl/evp.h>
#include "ekmfweb/ekmfweb.h"
/* CCA PKA Key Generate function */
@@ -53,9 +55,20 @@ typedef void (*CSNDPKB_t)(long *return_code,
unsigned char *reserved_5,
long *token_length, unsigned char *token);
/* CCA PKA Key Token Change function */
typedef void (*CSNDKTC_t)(long *return_code,
long *reason_code,
long *exit_data_length,
unsigned char *exit_data,
long *rule_array_count,
unsigned char *rule_array,
long *key_identifier_length,
unsigned char *key_identifier);
struct cca_lib {
CSNDPKB_t dll_CSNDPKB;
CSNDPKG_t dll_CSNDPKG;
CSNDKTC_t dll_CSNDKTC;
};
#define CCA_MAX_PKA_KEY_TOKEN_SIZE 3500
@@ -69,4 +82,11 @@ int cca_generate_rsa_key_pair(const struct ekmf_cca_lib *cca_lib,
unsigned char *key_token,
size_t *key_token_length, bool verbose);
int cca_get_key_type(const unsigned char *key_token, size_t key_token_length,
int *pkey_type);
int cca_reencipher_key(const struct ekmf_cca_lib *cca_lib,
const unsigned char *key_token, size_t key_token_length,
bool to_new, bool verbose);
#endif

View File

@@ -1209,6 +1209,92 @@ int ekmf_generate_identity_key(const struct ekmf_config *config,
return 0;
}
/**
* Re-encipher the secure identity key (form field identity_secure_key in
* config) used to identify the client to EKMFWeb.
* The secure key blob is encrypted using the HSM master key. Whenever the HSM
* master key is being changed, the secure identity key must be re-enciphered.
* You can either pro-actively re-encipher a secure key once the new master key
* has been prepared (but not yet made active): to_new = true; or you can
* re-encipher a secure key when the HSM master key has already been changed:
* to_new = false. This requires that the HSM still has the old master key.
* Not all HSMs support this.
*
* For pro-active re-encipherment it is suggested to store the re-enciphered
* secure key on a separate place, until the new HSM master key has been made
* active. Specify a file name in reenc_secure_key to do so. For an in-place
* re-encipherment, set reenc_secure_key = NULL.
*
* @param config the configuration structure. Only field
* identity_secure_key must be specified, all others
* are optional.
* @param to_new If true: the identity key is re-enciphered from the
* current to the new master key.
* If false: the identity key is re-enciphered from the
* old to the current master key.
* @param reenc_secure_key if not NULL, then the re-enciphered secure key is
* stored into the filename specified here. Otherwise
* the re-enciphered secure key replaces the original
* secure identity key.
* @param ext_lib External secure key crypto library to use
* @param verbose if true, verbose messages are printed
*
* @returns a negative errno in case of an error, 0 if success.
* A -ENODEV indicates that the master keys are not loaded.
*/
int ekmf_reencipher_identity_key(const struct ekmf_config *config,
bool to_new, const char *reenc_secure_key,
const struct ekmf_ext_lib *ext_lib,
bool verbose)
{
unsigned char key_blob[MAX_KEY_BLOB_SIZE];
size_t key_blob_size = sizeof(key_blob);
const char *out_file;
int rc;
if (config == NULL || ext_lib == NULL)
return -EINVAL;
if (config->identity_secure_key == NULL)
return -EINVAL;
rc = read_key_blob(config->identity_secure_key, key_blob,
&key_blob_size);
if (rc != 0) {
pr_verbose(verbose, "Failed to read identity key from file "
"'%s': %s", config->identity_secure_key,
strerror(-rc));
return rc;
}
switch (ext_lib->type) {
case EKMF_EXT_LIB_CCA:
rc = cca_reencipher_key(ext_lib->cca, key_blob, key_blob_size,
to_new, verbose);
break;
default:
pr_verbose(verbose, "Invalid ext lib type: %d", ext_lib->type);
return -EINVAL;
}
if (rc != 0) {
pr_verbose(verbose, "Failed to re-encipher the secure identity "
"key from file '%s': %s",
config->identity_secure_key, strerror(-rc));
return rc;
}
out_file = reenc_secure_key != NULL ? reenc_secure_key :
config->identity_secure_key;
rc = write_key_blob(out_file, key_blob, key_blob_size);
if (rc != 0) {
pr_verbose(verbose, "Failed to write identity key to file "
"'%s': %s", out_file, strerror(-rc));
return rc;
}
return 0;
}
/**
* Library constructor
*/

View File

@@ -4,5 +4,6 @@ LIBEKMFWEB_1.0 {
ekmf_print_certificates;
ekmf_check_login_token;
ekmf_generate_identity_key;
ekmf_reencipher_identity_key;
local: *;
};