mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
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:
committed by
Jan Höppner
parent
5fb30f1e6f
commit
1cdfb4946e
@@ -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
|
||||
|
||||
211
libekmfweb/cca.c
211
libekmfweb/cca.c
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -4,5 +4,6 @@ LIBEKMFWEB_1.0 {
|
||||
ekmf_print_certificates;
|
||||
ekmf_check_login_token;
|
||||
ekmf_generate_identity_key;
|
||||
ekmf_reencipher_identity_key;
|
||||
local: *;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user