From 4d598ade86f23b5425d3c7de57ce119361a168b4 Mon Sep 17 00:00:00 2001 From: Ingo Franzki Date: Wed, 13 Aug 2025 09:27:57 +0200 Subject: [PATCH] zkey: Add support for generating and importing exportable secure keys Normally, secure keys generated by zkey are intentionally export restricted. Export restricted keys can not be wrapped with a key encrypting key (KEK). However, keys that are generated inside a Secure Execution for Linux guest that shall also be used outside of the Secure Execution for Linux guest can only be transported to outside the Secure Execution for Linux guest by wrapping them with a KEK and unwrapping them outside of the Secure Execution for Linux guest. For that such keys must be exportable. Add an option to generate and import exportable secure keys, which then can be wrapped, and thus transported to outside of a Secure Execution for Linux guest. This applies to keys of type 'CCA-AESCIPHER' and 'EP11-AES'. Keys of type 'CCA-AESDATA' are always exportable, and can not be export restricted. For keys of type 'EP11-AES' additionally allow to set the 'wrap-with-trusted' attribute. This restricts the key so that it only can be wrapped with a trusted key encrypting key. For keys of type 'EP11-AES' to be exportable, the access control point (ACP) XCP_CPB_ALLOW_COMBINED_EXTRACT must be 'ON' on all APQNs used. This access control point is only supported on newer EP11 firmware levels. If the access control point is 'OFF' or not supported by the EP11 firmware, then the IOCTL to generate the key fails with a generic error (Input/output error - EIO). The zkey tool prints an appropriate error message in that case. Signed-off-by: Ingo Franzki Reviewed-by: Finn Callies Signed-off-by: Steffen Eiden --- zkey/ekmfweb/zkey-ekmfweb.c | 2 +- zkey/ep11.h | 6 ++ zkey/keystore.c | 61 +++++++++++------- zkey/keystore.h | 10 +-- zkey/pkey.c | 96 ++++++++++++++++++++++++---- zkey/pkey.h | 8 ++- zkey/pvsecrets.c | 2 +- zkey/zkey.1 | 43 +++++++++++++ zkey/zkey.c | 121 ++++++++++++++++++++++++++++++++---- 9 files changed, 294 insertions(+), 55 deletions(-) diff --git a/zkey/ekmfweb/zkey-ekmfweb.c b/zkey/ekmfweb/zkey-ekmfweb.c index d474e449..365e9a64 100644 --- a/zkey/ekmfweb/zkey-ekmfweb.c +++ b/zkey/ekmfweb/zkey-ekmfweb.c @@ -3937,7 +3937,7 @@ static int _restrict_key(struct plugin_handle *ph, unsigned char *key_blob, return rc; } - rc = check_aes_cipher_key(key_blob, key_blob_length); + rc = check_aes_cipher_key(key_blob, key_blob_length, false); if (rc != 0) { warnx("The secure key retrieved from EKMF Web might not be " "secure"); diff --git a/zkey/ep11.h b/zkey/ep11.h index 6ad5bedc..173bd6fd 100644 --- a/zkey/ep11.h +++ b/zkey/ep11.h @@ -121,6 +121,12 @@ typedef struct { #define CKR_OK 0x00000000 +#define XCP_BLOB_EXTRACTABLE 0x00000001 +#define XCP_BLOB_DECRYPT 0x00000400 +#define XCP_BLOB_ENCRYPT 0x00000800 +#define XCP_BLOB_WRAP_W_TRUSTED 0x00040000 +#define XCP_BLOB_PROTKEY_EXTRACTABLE 0x00200000 + typedef int (*m_init_t) (void); typedef int (*m_add_module_t) (XCP_Module_t module, target_t *target); typedef int (*m_rm_module_t) (XCP_Module_t module, target_t target); diff --git a/zkey/keystore.c b/zkey/keystore.c index 58f27df2..c9dc7b68 100644 --- a/zkey/keystore.c +++ b/zkey/keystore.c @@ -1983,6 +1983,9 @@ out: * @param[in] gen_passphrase if true, generate a (dummy) passphrase for LUKS2 * @param[in] passphrase_file the file name of a file containing a passphrase * for LUKS2 (optional, can be NULL) + * @param[in] exportable if true the key shall be exportable + * @param[in] wrap_with_trusted if true the key shall be wrapable with a + * trusted KEK only * @param[in] pkey_fd the file descriptor of /dev/pkey * * @returns 0 for success or a negative errno in case of an error @@ -1993,7 +1996,8 @@ int keystore_generate_key(struct keystore *keystore, const char *name, size_t sector_size, size_t keybits, bool xts, const char *clear_key_file, const char *volume_type, const char *key_type, bool gen_passphrase, - const char *passphrase_file, int pkey_fd) + const char *passphrase_file, bool exportable, + bool wrap_with_trusted, int pkey_fd) { struct key_filenames file_names = { 0 }; struct properties *key_props = NULL; @@ -2043,6 +2047,7 @@ int keystore_generate_key(struct keystore *keystore, const char *name, file_names.skey_filename, keybits, xts, key_type, (const char **)apqn_list, + exportable, wrap_with_trusted, keystore->verbose); else rc = generate_secure_key_clear(pkey_fd, @@ -2050,6 +2055,7 @@ int keystore_generate_key(struct keystore *keystore, const char *name, keybits, xts, clear_key_file, key_type, (const char **)apqn_list, + exportable, wrap_with_trusted, keystore->verbose); if (rc != 0) goto out_free_props; @@ -2286,6 +2292,7 @@ out_free_key_filenames: * @param[in] gen_passphrase if true, generate a (dummy) passphrase for LUKS2 * @param[in] passphrase_file the file name of a file containing a passphrase * for LUKS2 (optional, can be NULL) + * @param[in] exportable if true the key shall be exportable * @param[in] lib the external library struct * * @returns 0 for success or a negative errno in case of an error @@ -2295,7 +2302,8 @@ int keystore_import(struct keystore *keystore, unsigned char *secure_key, const char *description, const char *volumes, const char *apqns, bool noapqncheck, size_t sector_size, const char *volume_type, bool gen_passphrase, - const char *passphrase_file, struct ext_lib *lib) + const char *passphrase_file, bool exportable, + struct ext_lib *lib) { struct key_filenames file_names = { 0 }; struct properties *key_props = NULL; @@ -2375,17 +2383,22 @@ int keystore_import(struct keystore *keystore, unsigned char *secure_key, goto out_free_props; } - rc = restrict_key_export(lib->cca, secure_key, secure_key_size, - keystore->verbose); - if (rc != 0) { - warnx("Failed to export-restrict the imported secure " - "key: %s", strerror(-rc)); - if (!selected) - print_msg_for_cca_envvars("secure AES key"); - goto out_free_props; + if (!exportable) { + rc = restrict_key_export(lib->cca, secure_key, + secure_key_size, + keystore->verbose); + if (rc != 0) { + warnx("Failed to export-restrict the imported " + "secure key: %s", strerror(-rc)); + if (!selected) + print_msg_for_cca_envvars("secure AES " + "key"); + goto out_free_props; + } } - rc = check_aes_cipher_key(secure_key, secure_key_size); + rc = check_aes_cipher_key(secure_key, secure_key_size, + exportable); if (rc != 0) { warnx("The secure key to import might not be secure"); printf("%s: Do you want to import it anyway [y/N]? ", @@ -2455,6 +2468,7 @@ out_free_key_filenames: * @param[in] gen_passphrase if true, generate a (dummy) passphrase for LUKS2 * @param[in] passphrase_file the file name of a file containing a passphrase * for LUKS2 (optional, can be NULL) + * @param[in] exportable if true the key shall be exportable * @param[in] lib the external library struct * * @returns 0 for success or a negative errno in case of an error @@ -2464,7 +2478,7 @@ int keystore_import_key(struct keystore *keystore, const char *name, const char *apqns, bool noapqncheck, size_t sector_size, const char *import_file, const char *volume_type, bool gen_passphrase, const char *passphrase_file, - struct ext_lib *lib) + bool exportable, struct ext_lib *lib) { size_t secure_key_size; u8 *secure_key; @@ -2480,7 +2494,7 @@ int keystore_import_key(struct keystore *keystore, const char *name, rc = keystore_import(keystore, secure_key, secure_key_size, name, description, volumes, apqns, noapqncheck, sector_size, volume_type, gen_passphrase, - passphrase_file, lib); + passphrase_file, exportable, lib); if (secure_key != NULL) free(secure_key); @@ -4797,6 +4811,7 @@ int keystore_crypttab(struct keystore *keystore, const char *volume_filter, * @param[in] key_type the type of the key to convert it to * @param[in] noapqncheck if true, the specified APQN(s) are not checked for * existence and type. + * @param[in] exportable if true the key shall be exportable * @param[in] pkey_fd the file descriptor of /dev/pkey * @param[in] lib the external library struct * @@ -4804,7 +4819,7 @@ int keystore_crypttab(struct keystore *keystore, const char *volume_filter, */ int keystore_convert_key(struct keystore *keystore, const char *name, const char *key_type, bool noapqncheck, bool quiet, - int pkey_fd, struct ext_lib *lib) + bool exportable, int pkey_fd, struct ext_lib *lib) { struct key_filenames file_names = { 0 }; u8 output_key[2 * MAX_SECURE_KEY_SIZE]; @@ -4941,14 +4956,16 @@ int keystore_convert_key(struct keystore *keystore, const char *name, goto out; } - rc = restrict_key_export(lib->cca, output_key, output_key_size, - keystore->verbose); - if (rc != 0) { - warnx("Export restricting the converted secure key '%s' has " - "failed", name); - if (!selected) - print_msg_for_cca_envvars("secure AES key"); - goto out; + if (!exportable) { + rc = restrict_key_export(lib->cca, output_key, output_key_size, + keystore->verbose); + if (rc != 0) { + warnx("Export restricting the converted secure key " + "'%s' has failed", name); + if (!selected) + print_msg_for_cca_envvars("secure AES key"); + goto out; + } } rc = properties_set2(properties, PROP_NAME_KEY_TYPE, key_type, true); diff --git a/zkey/keystore.h b/zkey/keystore.h index b4cae9aa..510c4a51 100644 --- a/zkey/keystore.h +++ b/zkey/keystore.h @@ -55,7 +55,8 @@ int keystore_generate_key(struct keystore *keystore, const char *name, size_t sector_size, size_t keybits, bool xts, const char *clear_key_file, const char *volume_type, const char *key_type, bool gen_passphrase, - const char *passphrase_file, int pkey_fd); + const char *passphrase_file, bool exportable, + bool wrap_with_trusted, int pkey_fd); int keystore_generate_key_kms(struct keystore *keystore, const char *name, const char *description, const char *volumes, @@ -70,14 +71,15 @@ int keystore_import(struct keystore *keystore, unsigned char *secure_key, const char *description, const char *volumes, const char *apqns, bool noapqncheck, size_t sector_size, const char *volume_type, bool gen_passphrase, - const char *passphrase_file, struct ext_lib *lib); + const char *passphrase_file, bool exportable, + struct ext_lib *lib); int keystore_import_key(struct keystore *keystore, const char *name, const char *description, const char *volumes, const char *apqns, bool noapqncheck, size_t sector_size, const char *import_file, const char *volume_type, bool gen_passphrase, const char *passphrase_file, - struct ext_lib *lib); + bool exportable, struct ext_lib *lib); int keystore_change_key(struct keystore *keystore, const char *name, const char *description, const char *volumes, @@ -126,7 +128,7 @@ int keystore_crypttab(struct keystore *keystore, const char *volume_filter, int keystore_convert_key(struct keystore *keystore, const char *name, const char *key_type, bool noapqncheck, bool quiet, - int pkey_fd, struct ext_lib *lib); + bool exportable, int pkey_fd, struct ext_lib *lib); int keystore_kms_keys_set_property(struct keystore *keystore, const char *key_type, diff --git a/zkey/pkey.c b/zkey/pkey.c index 95ed7c98..5343b071 100644 --- a/zkey/pkey.c +++ b/zkey/pkey.c @@ -313,6 +313,35 @@ static enum pkey_key_size keybits_to_keysize(u32 keybits) } } +static u32 flags_for_pkey_type(enum pkey_key_type type, bool exportable, + bool wrap_with_trusted) +{ + u32 flags = 0; + + if (!exportable) + return 0; + + switch (type) { + case PKEY_TYPE_CCA_CIPHER: + flags = PKEY_KEYGEN_XPRT_SYM | PKEY_KEYGEN_XPRT_UASY | + PKEY_KEYGEN_XPRT_AASY | PKEY_KEYGEN_XPRT_RAW | + PKEY_KEYGEN_XPRT_CPAC | PKEY_KEYGEN_XPRT_DES | + PKEY_KEYGEN_XPRT_AES | PKEY_KEYGEN_XPRT_RSA; + break; + case PKEY_TYPE_EP11: + case PKEY_TYPE_EP11_AES: + flags = XCP_BLOB_EXTRACTABLE | XCP_BLOB_DECRYPT | + XCP_BLOB_ENCRYPT | XCP_BLOB_PROTKEY_EXTRACTABLE; + if (wrap_with_trusted) + flags |= XCP_BLOB_WRAP_W_TRUSTED; + break; + default: + break; + } + + return flags; +} + /* * Wrapper for the PKEY_GENSECK/PKEY_GENSECK2 IOCTL to generate a secure * key of any type by random. If the newer PKEY_GENSECK2 IOCTL is not supported @@ -887,6 +916,16 @@ static size_t key_size_for_type(enum pkey_key_type type) } } +static void print_error_for_ep11_exportable(void) +{ + util_print_indented("Generating an exportable key of type " + KEY_TYPE_EP11_AES " requires that access control " + "point (ACP) 'XCP_CPB_ALLOW_COMBINED_EXTRACT' is " + "'ON' on all used APQNs. This access control point " + "is only supported on newer EP11 firmware levels.", + 0); +} + /** * Generate a secure key by random * @@ -897,13 +936,17 @@ static size_t key_size_for_type(enum pkey_key_type type) * @param[in] key_type the type of the key * @param[in] apqns a zero terminated array of pointers to APQN-strings, * or NULL for AUTOSELECT + * @param[in] exportable if true the key shall be exportable + * @param[in] wrap_with_trusted if true the key shall be wrapable with a + * trusted KEK only * @param[in] verbose if true, verbose messages are printed * * @returns 0 on success, a negative errno in case of an error */ int generate_secure_key_random(int pkey_fd, const char *keyfile, size_t keybits, bool xts, const char *key_type, - const char **apqns, bool verbose) + const char **apqns, bool exportable, + bool wrap_with_trusted, bool verbose) { struct pkey_genseck2 genseck2; size_t secure_key_size, size; @@ -960,6 +1003,9 @@ retry: genseck2.key = secure_key; genseck2.keylen = size; + genseck2.keygenflags = flags_for_pkey_type(genseck2.type, exportable, + wrap_with_trusted); + rc = pkey_genseck2(pkey_fd, &genseck2, verbose); if (rc == -EINVAL && genseck2.type == PKEY_TYPE_EP11_AES) { /* @@ -979,6 +1025,9 @@ retry: } if (rc != 0) { warnx("Failed to generate a secure key: %s", strerror(-rc)); + if (exportable && + strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0) + print_error_for_ep11_exportable(); goto out; } if (rc == 0 && genseck2.type == PKEY_TYPE_EP11) { @@ -1016,10 +1065,17 @@ retry: genseck2.key = secure_key + size; genseck2.keylen = size; + genseck2.keygenflags = flags_for_pkey_type(genseck2.type, + exportable, + wrap_with_trusted); + rc = pkey_genseck2(pkey_fd, &genseck2, verbose); if (rc != 0) { warnx("Failed to generate a secure key: %s", strerror(-rc)); + if (exportable && + strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0) + print_error_for_ep11_exportable(); goto out; } } @@ -1048,6 +1104,9 @@ out: * @param[in] key_type the type of the key * @param[in] apqns a zero terminated array of pointers to APQN-strings, * or NULL for AUTOSELECT + * @param[in] exportable if true the key shall be exportable + * @param[in] wrap_with_trusted if true the key shall be wrapable with a + * trusted KEK only * @param[in] verbose if true, verbose messages are printed * * @returns 0 on success, a negative errno in case of an error @@ -1055,7 +1114,8 @@ out: int generate_secure_key_clear(int pkey_fd, const char *keyfile, size_t keybits, bool xts, const char *clearkeyfile, const char *key_type, - const char **apqns, bool verbose) + const char **apqns, bool exportable, + bool wrap_with_trusted, bool verbose) { struct pkey_clr2seck2 clr2seck2; size_t secure_key_size; @@ -1123,6 +1183,9 @@ retry: clr2seck2.key = secure_key; clr2seck2.keylen = size; + clr2seck2.keygenflags = flags_for_pkey_type(clr2seck2.type, exportable, + wrap_with_trusted); + rc = pkey_clr2seck2(pkey_fd, &clr2seck2, verbose); if (rc == -EINVAL && clr2seck2.type == PKEY_TYPE_EP11_AES) { /* @@ -1142,6 +1205,9 @@ retry: } if (rc != 0) { warnx("Failed to generate a secure key: %s", strerror(-rc)); + if (exportable && + strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0) + print_error_for_ep11_exportable(); goto out; } if (rc == 0 && clr2seck2.type == PKEY_TYPE_EP11) { @@ -1182,10 +1248,17 @@ retry: clr2seck2.key = secure_key + size; clr2seck2.keylen = size; + clr2seck2.keygenflags = flags_for_pkey_type(clr2seck2.type, + exportable, + wrap_with_trusted); + rc = pkey_clr2seck2(pkey_fd, &clr2seck2, verbose); if (rc != 0) { warnx("Failed to generate a secure key: %s", strerror(-rc)); + if (exportable && + strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0) + print_error_for_ep11_exportable(); goto out; } } @@ -2044,10 +2117,11 @@ enum card_type get_card_type_for_keytype(const char *key_type) * * @param[in] key the secure key token * @param[in] key_size the size of the secure key + * @param[in] exportable if true the key shall be exportable * * @returns 0 on success, a negative errno in case of an error */ -int check_aes_cipher_key(const u8 *key, size_t key_size) +int check_aes_cipher_key(const u8 *key, size_t key_size, bool exportable) { struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key; bool mismatch = false; @@ -2072,22 +2146,22 @@ int check_aes_cipher_key(const u8 *key, size_t key_size) mismatch = true; } - if (cipherkey->kmf1 & 0x8000) { + if (!exportable && (cipherkey->kmf1 & 0x8000)) { printf("WARNING: The secure key can be exported using a " "symmetric key\n"); mismatch = true; } - if (cipherkey->kmf1 & 0x4000) { + if (!exportable && (cipherkey->kmf1 & 0x4000)) { printf("WARNING: The secure key can be exported using an " "unauthenticated asymmetric key\n"); mismatch = true; } - if (cipherkey->kmf1 & 0x2000) { + if (!exportable && (cipherkey->kmf1 & 0x2000)) { printf("WARNING: The secure key can be exported using an " "authenticated asymmetric key\n"); mismatch = true; } - if (cipherkey->kmf1 & 0x1000) { + if (!exportable && (cipherkey->kmf1 & 0x1000)) { printf("WARNING: The secure key can be exported using a RAW " "key\n"); mismatch = true; @@ -2097,17 +2171,17 @@ int check_aes_cipher_key(const u8 *key, size_t key_size) "CPACF protected key\n"); mismatch = true; } - if ((cipherkey->kmf1 & 0x0080) == 0) { + if (!exportable && (cipherkey->kmf1 & 0x0080) == 0) { printf("WARNING: The secure key can be exported using a DES " "key\n"); mismatch = true; } - if ((cipherkey->kmf1 & 0x0040) == 0) { + if (!exportable && (cipherkey->kmf1 & 0x0040) == 0) { printf("WARNING: The secure key can be exported using an AES " "key\n"); mismatch = true; } - if ((cipherkey->kmf1 & 0x0008) == 0) { + if (!exportable && (cipherkey->kmf1 & 0x0008) == 0) { printf("WARNING: The secure key can be exported using an RSA " "key\n"); mismatch = true; @@ -2161,7 +2235,7 @@ int check_aes_cipher_key(const u8 *key, size_t key_size) "value\n"); mismatch = true; } - if ((cipherkey->kmf3 & 0x00FF) == 0x0012) { + if (!exportable && (cipherkey->kmf3 & 0x00FF) == 0x0012) { printf("WARNING: The secure key was converted from a CCA " "key-token that had no export control attributes\n"); mismatch = true; diff --git a/zkey/pkey.h b/zkey/pkey.h index 71061e8d..a13e08a2 100644 --- a/zkey/pkey.h +++ b/zkey/pkey.h @@ -330,12 +330,14 @@ int open_pkey_device(bool verbose); int generate_secure_key_random(int pkey_fd, const char *keyfile, size_t keybits, bool xts, const char *key_type, - const char **apqns, bool verbose); + const char **apqns, bool exportable, + bool wrap_with_trusted, bool verbose); int generate_secure_key_clear(int pkey_fd, const char *keyfile, size_t keybits, bool xts, const char *clearkeyfile, const char *key_type, - const char **apqns, bool verbose); + const char **apqns, bool exportable, + bool wrap_with_trusted, bool verbose); u8 *read_secure_key(const char *keyfile, size_t *secure_key_size, bool verbose); @@ -373,7 +375,7 @@ bool is_secure_key_type(const char *key_type); int get_min_card_level_for_keytype(const char *key_type); const struct fw_version *get_min_fw_version_for_keytype(const char *key_type); enum card_type get_card_type_for_keytype(const char *key_type); -int check_aes_cipher_key(const u8 *key, size_t key_size); +int check_aes_cipher_key(const u8 *key, size_t key_size, bool exportable); enum reencipher_method { REENCIPHER_OLD_TO_CURRENT = 1, diff --git a/zkey/pvsecrets.c b/zkey/pvsecrets.c index a4b3a5a8..2c07f666 100644 --- a/zkey/pvsecrets.c +++ b/zkey/pvsecrets.c @@ -674,7 +674,7 @@ int pvsecrets_import(struct keystore *keystore, int uv_fd, rc = keystore_import(keystore, (unsigned char *)&build_blob_data.token, sizeof(build_blob_data.token), name, description, volumes, NULL, false, sector_size, volume_type, - gen_passphrase, passphrase_file, NULL); + gen_passphrase, passphrase_file, false, NULL); return rc; } diff --git a/zkey/zkey.1 b/zkey/zkey.1 index b44eadf1..a8f60c2e 100644 --- a/zkey/zkey.1 +++ b/zkey/zkey.1 @@ -100,6 +100,8 @@ key repository. .IR clear\-key\-file ] .RB [ \-\-key\-type | \-K .IR type ] +.RB [ \-\-exportable ] +.RB [ \-\-wrap\-with\-trusted ] .RB [ \-\-verbose | \-V ] . .PP @@ -129,6 +131,8 @@ key repository. .RB [ \-\-gen\-dummy\-passphrase ] .RB [ \-\-set\-dummy\-passphrase .IR passphrase\-file ] +.RB [ \-\-exportable ] +.RB [ \-\-wrap\-with\-trusted ] .RB [ KMS-plugin\ specific\ options ] .RB [ \-\-verbose | \-V ] .PP @@ -183,6 +187,29 @@ Secure keys of type \fBCCA\-AESCIPHER\fP require an IBM cryptographic adapter in CCA coprocessor mode of version 6 or later, e.g. a CEX6C. Secure keys of type \fBEP11\-AES\fP require an IBM cryptographic adapter in EP11 coprocessor mode of version 7 or later, e.g. a CEX7P. +.PP +By default, secure keys of type \fBCCA\-AESCIPHER\fP and \fBEP11\-AES\fP are +export restricted when generated by zkey. Keys of type \fBCCA\-AESDATA\fP can +not be export restricted. Export restricted means that the secure keys can not +be wrapped by a key encrypting key (KEK), and thus can not escape the +protection of the IBM cryptographic adapter (HSM). If exportability is required +for a secure key of type \fBCCA\-AESCIPHER\fP or \fBEP11\-AES\fP, specify +option \fB\-\-exportable\fP. This makes the generated secure keys exportable. +Additionally, for keys of type \fBEP11\-AES\fP option +\fB\-\-wrap\-with\-trusted\fP can be specified to allow wrapping only with a +trusted KEK. When the secure key repository is bound to a key management system +plugin (KMS plugin), options \fB\-\-exportable\fP and +\fB\-\-wrap\-with\-trusted\fP are not allowed, except the \fB\-\-local\fP option +is specified. + +.PP +.B Note: +For keys of type \fBEP11\-AES\fP to be exportable, the access control point +(ACP) \fBXCP_CPB_ALLOW_COMBINED_EXTRACT\fP must be \fBON\fP on all APQNs used. +This access control point is only supported on newer EP11 firmware levels. If +the access control point is \fBOFF\fP or not supported by the EP11 firmware, +key generation fails with a generic error (Input/output error). The \fBzkey\fP +tool prints an appropriate error message in that case. . .SS "Validating secure AES keys" . @@ -391,6 +418,7 @@ EP11\-AES to be installed. For the supported environments and downloads, see: .RB [ \-\-gen\-dummy\-passphrase ] .RB [ \-\-set\-dummy\-passphrase .IR passphrase\-file ] +.RB [ \-\-exportable ] .RB [ \-\-verbose | \-V ] . .PP @@ -416,6 +444,13 @@ The \fBimport\fP command requires the CCA host library (libcsulcca.so) to be installed when secure keys of type \fBCCA\-AESCIPHER\fP are imported. For the supported environments and downloads, see: \fIhttp://www.ibm.com/security/cryptocards\fP +.PP +By default, secure keys of type \fBCCA\-AESCIPHER\fP are changed to be export +restricted during the import operation. Export restricted means that the secure +keys can not be wrapped by a key encrypting key (KEK), and thus can not escape +the protection of the IBM cryptographic adapter (HSM). If exportability of an +imported key is required, specify option \fB\-\-exportable\fP. The import +operation will then not change the exportability of the secure key. . .SS "Export AES secure keys from the secure key repository" . @@ -798,6 +833,7 @@ same way as with \fBcryptsetup\fP. .IR type .RB [ \-\-no\-apqn\-check ] .RB [ \-\-force | \-F ] +.RB [ \-\-exportable ] .RB [ \-\-verbose | \-V ] . .PP @@ -844,6 +880,13 @@ The \fBconvert\fP command requires the CCA host library (libcsulcca.so) to be installed. The required CCA IBM cryptographic adapter firmware version is 6.3.27 or later. For the supported environments and downloads, see: \fIhttp://www.ibm.com/security/cryptocards\fP +.PP +By default, secure keys are changed to be export restricted during the convert +operation. Export restricted means that the secure keys can not be wrapped by a +key encrypting key (KEK), and thus can not escape the protection of the IBM +cryptographic adapter (HSM). If exportability of a converted key is required, +specify option \fB\-\-exportable\fP. The convert operation will then not change +the exportability of the secure key. . . .SH COMMANDS FOR KEY MANAGEMENT SYSTEM INTEGRATION diff --git a/zkey/zkey.c b/zkey/zkey.c index 943b9f16..c81670cc 100644 --- a/zkey/zkey.c +++ b/zkey/zkey.c @@ -83,6 +83,8 @@ static struct zkey_globals { bool gen_passphrase; char *passphrase_file; bool remove_passphrase; + bool exportable; + bool wrap_with_trusted; bool kms_bound; bool run; bool batch_mode; @@ -173,6 +175,8 @@ static struct zkey_globals { #define OPT_GEN_DUMMY_PASSPHRASE 265 #define OPT_SET_DUMMY_PASSPHRASE 266 #define OPT_REMOVE_DUMMY_PASSPHRASE 267 +#define OPT_EXPORTABLE 268 +#define OPT_WRAP_WITH_TRUSTED 269 /* * Configuration of command line options @@ -313,6 +317,28 @@ static struct util_opt opt_vec[] = { .flags = UTIL_OPT_FLAG_NOSHORT, .command = COMMAND_GENERATE, }, + { + .option = {"exportable", 0, NULL, OPT_EXPORTABLE}, + .desc = "Generate a secure AES key that is not export " + "restricted. Exportable keys can be exported by means " + "of wrapping them with a key encryption key (KEK). By " + "default keys of type " KEY_TYPE_CCA_AESCIPHER " and " + KEY_TYPE_EP11_AES " are export restricted, and thus can " + "not be wrapped by a KEK. Keys of type " + KEY_TYPE_CCA_AESDATA " are always exportable, and can " + "not be export restricted.", + .command = COMMAND_GENERATE, + .flags = UTIL_OPT_FLAG_NOSHORT, + }, + { + .option = {"wrap-with-trusted", 0, NULL, OPT_WRAP_WITH_TRUSTED}, + .desc = "Generate a secure AES key that can only be wrapped " + "with a trusted key encryption key (KEK). This option " + "is only valid for keys of type " KEY_TYPE_EP11_AES + " and only if option '--exportable' is also specified.", + .command = COMMAND_GENERATE, + .flags = UTIL_OPT_FLAG_NOSHORT, + }, /***********************************************************/ { .flags = UTIL_OPT_FLAG_SECTION, @@ -496,6 +522,19 @@ static struct util_opt opt_vec[] = { .flags = UTIL_OPT_FLAG_NOSHORT, .command = COMMAND_IMPORT, }, + { + .option = {"exportable", 0, NULL, OPT_EXPORTABLE}, + .desc = "Allow to import a secure AES key that is not export " + "restricted. Exportable keys can be exported by means " + "of wrapping them with a key encryption key (KEK). By " + "default keys of type " KEY_TYPE_CCA_AESCIPHER " and " + KEY_TYPE_EP11_AES " are export restricted, and thus can " + "not be wrapped by a KEK. Keys of type " + KEY_TYPE_CCA_AESDATA " are always exportable, and can " + "not be export restricted.", + .command = COMMAND_IMPORT, + .flags = UTIL_OPT_FLAG_NOSHORT, + }, /***********************************************************/ { .flags = UTIL_OPT_FLAG_SECTION, @@ -969,6 +1008,14 @@ static struct util_opt opt_vec[] = { "key", .command = COMMAND_CONVERT, }, + { + .option = {"exportable", 0, NULL, OPT_EXPORTABLE}, + .desc = "Convert to an secure AES key that is not export " + "restricted. Exportable keys can be exported by means " + "of wrapping them with a key encryption key (KEK).", + .command = COMMAND_CONVERT, + .flags = UTIL_OPT_FLAG_NOSHORT, + }, /***********************************************************/ { .flags = UTIL_OPT_FLAG_SECTION, @@ -1891,7 +1938,8 @@ static int command_generate_clear(void) rc = generate_secure_key_clear(g.pkey_fd, g.pos_arg, g.keybits, g.xts, g.clearkeyfile, g.key_type, - NULL, g.verbose); + NULL, g.exportable, g.wrap_with_trusted, + g.verbose); return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; } @@ -1907,7 +1955,8 @@ static int command_generate_random(void) rc = generate_secure_key_random(g.pkey_fd, g.pos_arg, g.keybits, g.xts, g.key_type, - NULL, g.verbose); + NULL, g.exportable, g.wrap_with_trusted, + g.verbose); return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; } @@ -1948,6 +1997,17 @@ static int command_generate_repository(void) return EXIT_FAILURE; } + if (g.exportable) { + warnx("Option '--exportable' is not supported for " + "generating a key in a KMS-bound repository"); + return EXIT_FAILURE; + } + if (g.wrap_with_trusted) { + warnx("Option '--wrap-with-trusted' is not supported " + "for generating a key in a KMS-bound repository"); + return EXIT_FAILURE; + } + rc = perform_kms_login(&g.kms_info, g.verbose); if (rc != 0) rc = EXIT_FAILURE; @@ -1966,11 +2026,25 @@ static int command_generate_repository(void) if (g.key_type == NULL) g.key_type = KEY_TYPE_CCA_AESDATA; + if (g.wrap_with_trusted) { + if (!g.exportable) { + warnx("Option '--wrap-with-trusted' is only valid " + "together with the '--exportable' option"); + return EXIT_FAILURE; + } + if (strcasecmp(g.key_type, KEY_TYPE_EP11_AES) != 0) { + warnx("Option '--wrap-with-trusted' is only valid for " + "keys of type '%s'", KEY_TYPE_EP11_AES); + return EXIT_FAILURE; + } + } + rc = keystore_generate_key(g.keystore, g.name, g.description, g.volumes, g.apqns, g.noapqncheck, g.sector_size, g.keybits, g.xts, g.clearkeyfile, g.volume_type, g.key_type, g.gen_passphrase, - g.passphrase_file, g.pkey_fd); + g.passphrase_file, g.exportable, + g.wrap_with_trusted, g.pkey_fd); out: return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; @@ -2007,6 +2081,18 @@ static int command_generate(void) "pvsecret import' instead", g.key_type); return -EXIT_FAILURE; } + if (g.wrap_with_trusted) { + if (!g.exportable) { + warnx("Option '--wrap-with-trusted' is only valid " + "together with the '--exportable' option"); + return EXIT_FAILURE; + } + if (strcasecmp(g.key_type, KEY_TYPE_EP11_AES) != 0) { + warnx("Option '--wrap-with-trusted' is only valid for " + "keys of type '%s'", KEY_TYPE_EP11_AES); + return EXIT_FAILURE; + } + } if (g.pos_arg != NULL) { if (g.volumes != NULL) { warnx("Option '--volumes|-l' is not valid for " @@ -2463,7 +2549,7 @@ static int command_import(void) rc = keystore_import_key(g.keystore, g.name, g.description, g.volumes, g.apqns, g.noapqncheck, g.sector_size, g.pos_arg, g.volume_type, g.gen_passphrase, - g.passphrase_file, &g.lib); + g.passphrase_file, g.exportable, &g.lib); return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; } @@ -2816,14 +2902,17 @@ static int command_convert_file(void) goto out; } - rc = restrict_key_export(&g.cca, output_key, output_key_size, - g.verbose); - if (rc != 0) { - warnx("Export restricting the converted secure key has failed"); - if (!selected) - print_msg_for_cca_envvars("secure AES key"); - rc = EXIT_FAILURE; - goto out; + if (!g.exportable) { + rc = restrict_key_export(&g.cca, output_key, output_key_size, + g.verbose); + if (rc != 0) { + warnx("Export restricting the converted secure key " + "has failed"); + if (!selected) + print_msg_for_cca_envvars("secure AES key"); + rc = EXIT_FAILURE; + goto out; + } } pr_verbose("Secure key was converted successfully"); @@ -2853,7 +2942,7 @@ static int command_convert_repository(void) } rc = keystore_convert_key(g.keystore, g.name, g.key_type, g.noapqncheck, - g.force, g.pkey_fd, &g.lib); + g.force, g.exportable, g.pkey_fd, &g.lib); return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; } @@ -3519,6 +3608,12 @@ int main(int argc, char *argv[]) case OPT_REMOVE_DUMMY_PASSPHRASE: g.remove_passphrase = 1; break; + case OPT_EXPORTABLE: + g.exportable = 1; + break; + case OPT_WRAP_WITH_TRUSTED: + g.wrap_with_trusted = 1; + break; case 'A': g.all = 1; break;