From f52aeabca4615e8330670f793d27d38e2bd81930 Mon Sep 17 00:00:00 2001 From: Ingo Franzki Date: Wed, 20 Nov 2019 17:30:57 +0100 Subject: [PATCH] zkey: Generalize the key re-enciphering handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Different crypto card types use different ways to re-encipher a secure key with a new master key. Generalize the handling of re-enciphering, so that the majority of the code does not have to care about the card type when dealing with it. Signed-off-by: Ingo Franzki Reviewed-by: Harald Freudenberger Signed-off-by: Jan Höppner --- zkey/keystore.c | 86 ++++++++------------- zkey/pkey.c | 166 +++++++++++++++++++++++++++++++++++++++++ zkey/pkey.h | 10 +++ zkey/zkey-cryptsetup.c | 141 +++++++++++++--------------------- zkey/zkey.c | 90 +++++++++------------- 5 files changed, 294 insertions(+), 199 deletions(-) diff --git a/zkey/keystore.c b/zkey/keystore.c index da87d8fa..732432eb 100644 --- a/zkey/keystore.c +++ b/zkey/keystore.c @@ -2696,16 +2696,8 @@ static int _keystore_perform_reencipher(struct keystore *keystore, u8 *secure_key, size_t secure_key_size, bool is_old_mk, const char *apqns) { - int rc, selected = 1; - u8 mkvp[MKVP_LENGTH]; - - rc = get_master_key_verification_pattern(secure_key, secure_key_size, - mkvp, keystore->verbose); - if (rc != 0) { - warnx("Failed to get the master key verification pattern: %s", - strerror(-rc)); - return rc; - } + bool selected; + int rc; if (!params->from_old && !params->to_new) { /* Autodetect reencipher mode */ @@ -2734,27 +2726,22 @@ static int _keystore_perform_reencipher(struct keystore *keystore, "Secure key '%s' will be re-enciphered from OLD " "to the CURRENT master key", name); - rc = select_cca_adapter_by_mkvp(lib->cca, mkvp, apqns, - FLAG_SEL_CCA_MATCH_OLD_MKVP, - keystore->verbose); - if (rc == -ENOTSUP) { - rc = 0; - selected = 0; - } + rc = reencipher_secure_key(lib, secure_key, secure_key_size, + apqns, REENCIPHER_OLD_TO_CURRENT, + &selected, keystore->verbose); if (rc != 0) { - warnx("No APQN found that is suitable for " - "re-enciphering this secure AES key"); - return rc; - } - - rc = key_token_change(lib->cca, secure_key, secure_key_size, - METHOD_OLD_TO_CURRENT, - keystore->verbose); - if (rc != 0) { - warnx("Failed to re-encipher '%s' from OLD to " - "CURRENT master key", name); - if (!selected) - print_msg_for_cca_envvars("secure AES key"); + if (rc == -ENODEV) { + warnx("No APQN found that is suitable for " + "re-enciphering this secure AES key"); + } else { + warnx("Failed to re-encipher '%s' from OLD to " + "CURRENT master key", name); + if (!selected && + !is_ep11_aes_key(secure_key, + secure_key_size)) + print_msg_for_cca_envvars( + "secure AES key"); + } return rc; } } @@ -2766,30 +2753,23 @@ static int _keystore_perform_reencipher(struct keystore *keystore, if (params->inplace == -1) params->inplace = 0; - rc = select_cca_adapter_by_mkvp(lib->cca, mkvp, apqns, - FLAG_SEL_CCA_MATCH_CUR_MKVP | - FLAG_SEL_CCA_NEW_MUST_BE_SET, - keystore->verbose); - if (rc == -ENOTSUP) { - rc = 0; - selected = 0; - } + rc = reencipher_secure_key(lib, secure_key, secure_key_size, + apqns, REENCIPHER_CURRENT_TO_NEW, + &selected, keystore->verbose); if (rc != 0) { - util_print_indented("No APQN found that is suitable " - "for re-enciphering this secure " - "AES key and has the NEW master " - "key loaded", 0); - return rc; - } - - rc = key_token_change(lib->cca, secure_key, secure_key_size, - METHOD_CURRENT_TO_NEW, - keystore->verbose); - if (rc != 0) { - warnx("Failed to re-encipher '%s' from CURRENT to " - "NEW master key", name); - if (!selected) - print_msg_for_cca_envvars("secure AES key"); + if (rc == -ENODEV) { + warnx("No APQN found that is suitable for " + "re-enciphering this secure AES key and " + "has the NEW master key loaded"); + } else { + warnx("Failed to re-encipher '%s' from CURRENT " + "to NEW master key", name); + if (!selected && + !is_ep11_aes_key(secure_key, + secure_key_size)) + print_msg_for_cca_envvars( + "secure AES key"); + } return rc; } } diff --git a/zkey/pkey.c b/zkey/pkey.c index 578a65a8..f491f9d8 100644 --- a/zkey/pkey.c +++ b/zkey/pkey.c @@ -1881,3 +1881,169 @@ int check_aes_cipher_key(const u8 *key, size_t key_size) return mismatch ? -EINVAL : 0; } + +static int reencipher_cca_secure_key(struct cca_lib *cca, u8 *secure_key, + size_t secure_key_size, const char *apqns, + u8 *mkvp, enum reencipher_method method, + bool *apqn_selected, bool verbose) +{ + unsigned int flags; + int rc; + + if (method == REENCIPHER_OLD_TO_CURRENT) + flags = FLAG_SEL_CCA_MATCH_OLD_MKVP; + else + flags = FLAG_SEL_CCA_MATCH_CUR_MKVP | + FLAG_SEL_CCA_NEW_MUST_BE_SET; + + *apqn_selected = true; + + rc = select_cca_adapter_by_mkvp(cca, mkvp, apqns, flags, + verbose); + if (rc == -ENOTSUP) { + rc = 0; + *apqn_selected = false; + } + if (rc != 0) { + pr_verbose(verbose, "No APQN found that is suitable " + "for re-enciphering this secure key"); + return rc; + } + + rc = key_token_change(cca, secure_key, secure_key_size, + method == REENCIPHER_OLD_TO_CURRENT ? + METHOD_OLD_TO_CURRENT : + METHOD_CURRENT_TO_NEW, + verbose); + if (rc != 0) { + pr_verbose(verbose, "Failed to re-encipher secure key: " + "%s", strerror(-rc)); + return rc; + } + + return 0; +} + +static int reencipher_ep11_secure_key(struct ep11_lib *ep11, u8 *secure_key, + size_t secure_key_size, const char *apqns, + u8 *mkvp, bool *apqn_selected, + bool verbose) +{ + unsigned int flags; + int card, domain; + target_t target; + int rc; + + flags = FLAG_SEL_EP11_MATCH_CUR_MKVP | + FLAG_SEL_EP11_NEW_MUST_BE_SET; + + *apqn_selected = true; + + rc = select_ep11_apqn_by_mkvp(ep11, mkvp, apqns, flags, + &target, &card, &domain, verbose); + if (rc == -ENOTSUP) { + rc = 0; + *apqn_selected = false; + } + if (rc != 0) { + pr_verbose(verbose, "No APQN found that is suitable " + "for re-enciphering this secure key"); + return rc; + } + + rc = reencipher_ep11_key(ep11, target, card, domain, + secure_key, secure_key_size, verbose); + free_ep11_target_for_apqn(ep11, target); + if (rc != 0) { + pr_verbose(verbose, "Failed to re-encipher secure key: " + "%s", strerror(-rc)); + return rc; + } + + return 0; +} + + +/** + * Re-enciphers a secure key + * + * @param[in] lib the external library struct + * @param[in] secure_key a buffer containing the secure key + * @param[in] secure_key_size the secure key size + * @param[in] apqns a comma separated list of APQNs. If NULL is + * specified, or an empty string, then all online + * APQNs of the matching type are subject to be used. + * @param[in] method the re-encipher method + * @param[out] apqn_selected On return: true if a specific APQN was selected. + * @param[in] verbose if true, verbose messages are printed + * + * @returns 0 on success, a negative errno in case of an error. + * -ENODEV is returned if no APQN could be found with a matching master key. + * -EIO is returned if the re-enciphering has failed. + */ +int reencipher_secure_key(struct ext_lib *lib, u8 *secure_key, + size_t secure_key_size, const char *apqns, + enum reencipher_method method, bool *apqn_selected, + bool verbose) +{ + u8 mkvp[MKVP_LENGTH]; + int rc; + + util_assert(lib != NULL, "Internal error: lib is NULL"); + util_assert(secure_key != NULL, "Internal error: secure_key is NULL"); + util_assert(apqn_selected != NULL, + "Internal error: apqn_selected is NULL"); + + *apqn_selected = true; + + rc = get_master_key_verification_pattern(secure_key, secure_key_size, + mkvp, verbose); + if (rc != 0) { + pr_verbose(verbose, "Failed to get the master key verification " + "pattern: %s", strerror(-rc)); + return rc; + } + + if (is_ep11_aes_key(secure_key, secure_key_size)) { + /* EP11 secure key: need the EP11 host library */ + if (lib->ep11->lib_ep11 == NULL) { + rc = load_ep11_library(lib->ep11, verbose); + if (rc != 0) + return rc; + } + + if (method == REENCIPHER_OLD_TO_CURRENT) { + util_print_indented("ERROR: An APQN of a IBM " + "cryptographic adapter in EP11 " + "coprocessor mode does not have an " + "OLD master key register. Thus, " + "you can not re-encipher a secure " + "key of type 'EP11-AES' from the " + "OLD to the CURRENT master key " + "register.\n", 0); + return -EINVAL; + } + + rc = reencipher_ep11_secure_key(lib->ep11, secure_key, + secure_key_size, apqns, mkvp, + apqn_selected, verbose); + } else if (is_cca_aes_data_key(secure_key, secure_key_size) || + is_cca_aes_cipher_key(secure_key, secure_key_size)) { + /* CCA secure key: need the CCA host library */ + if (lib->cca->lib_csulcca == NULL) { + rc = load_cca_library(lib->cca, verbose); + if (rc != 0) + return rc; + } + + rc = reencipher_cca_secure_key(lib->cca, secure_key, + secure_key_size, apqns, mkvp, + method, apqn_selected, verbose); + } else { + pr_verbose(verbose, "Invalid key type"); + rc = -EINVAL; + } + + return rc; +} + diff --git a/zkey/pkey.h b/zkey/pkey.h index 253cba24..93c48aff 100644 --- a/zkey/pkey.h +++ b/zkey/pkey.h @@ -324,4 +324,14 @@ 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); +enum reencipher_method { + REENCIPHER_OLD_TO_CURRENT = 1, + REENCIPHER_CURRENT_TO_NEW = 2, +}; + +int reencipher_secure_key(struct ext_lib *lib, u8 *secure_key, + size_t secure_key_size, const char *apqns, + enum reencipher_method method, bool *apqn_selected, + bool verbose); + #endif diff --git a/zkey/zkey-cryptsetup.c b/zkey/zkey-cryptsetup.c index e72d3c6c..24c01261 100644 --- a/zkey/zkey-cryptsetup.c +++ b/zkey/zkey-cryptsetup.c @@ -293,7 +293,7 @@ static struct zkey_cryptsetup_command zkey_cryptsetup_commands[] = { .command = COMMAND_REENCIPHER, .abbrev_len = 2, .function = command_reencipher, - .need_cca_library = 1, + /* Will load the CCA or EP11 library on demand */ .need_pkey_device = 1, .short_desc = "Re-encipher a secure volume key", .long_desc = "Re-encipher a secure volume key of a volume " @@ -1540,12 +1540,11 @@ static int reencipher_prepare(int token) struct reencipher_token reenc_tok; struct vp_token vp_tok; char *password = NULL; - u8 mkvp[MKVP_LENGTH]; size_t password_len; char *key = NULL; - int selected = 1; size_t keysize; int is_old_mk; + bool selected; char *prompt; char *msg; int rc; @@ -1623,70 +1622,48 @@ static int reencipher_prepare(int token) } } - rc = get_master_key_verification_pattern((u8 *)key, keysize, mkvp, - g.verbose); - if (rc != 0) { - warnx("Failed to get the master key verification pattern: %s", - strerror(-rc)); - goto out; - } - if (g.fromold) { - rc = select_cca_adapter_by_mkvp(&g.cca, mkvp, NULL, - FLAG_SEL_CCA_MATCH_OLD_MKVP, - g.verbose); - if (rc == -ENOTSUP) { - rc = 0; - selected = 0; - } + rc = reencipher_secure_key(&g.lib, (u8 *)key, keysize, + NULL, REENCIPHER_OLD_TO_CURRENT, + &selected, g.verbose); if (rc != 0) { - util_print_indented("No APQN found that is suitable " - "for re-enciphering the secure AES " - "volume key from the OLD to the " - "CURRENT master key.", 0); - goto out; - } - - rc = key_token_change(&g.cca, (u8 *)key, keysize, - METHOD_OLD_TO_CURRENT, g.verbose); - if (rc != 0) { - warnx("Failed to re-encipher the secure volume key of " - "device '%s'\n", g.pos_arg); - if (!selected) - print_msg_for_cca_envvars( + if (rc == -ENODEV) { + warnx("No APQN found that is suitable for " + "re-enciphering the secure AES volume " + "key from the OLD to the CURRENT master " + "key."); + } else { + warnx("Failed to re-encipher the secure volume " + "key for device '%s'\n", g.pos_arg); + if (!selected && + !is_ep11_aes_key((u8 *)key, keysize)) + print_msg_for_cca_envvars( "secure AES volume key"); - rc = -EINVAL; + rc = -EINVAL; + } goto out; } } if (g.tonew) { - rc = select_cca_adapter_by_mkvp(&g.cca, mkvp, NULL, - FLAG_SEL_CCA_MATCH_CUR_MKVP | - FLAG_SEL_CCA_NEW_MUST_BE_SET, - g.verbose); - if (rc == -ENOTSUP) { - rc = 0; - selected = 0; - } + rc = reencipher_secure_key(&g.lib, (u8 *)key, keysize, + NULL, REENCIPHER_CURRENT_TO_NEW, + &selected, g.verbose); if (rc != 0) { - util_print_indented("No APQN found that is suitable " - "for re-enciphering the secure AES " - "volume key from the CURRENT to " - "the NEW master key.", 0); - goto out; - } - - rc = key_token_change(&g.cca, (u8 *)key, keysize, - METHOD_CURRENT_TO_NEW, - g.verbose); - if (rc != 0) { - warnx("Failed to re-encipher the secure volume key of " - "device '%s'\n", g.pos_arg); - if (!selected) - print_msg_for_cca_envvars( + if (rc == -ENODEV) { + warnx("No APQN found that is suitable for " + "re-enciphering the secure AES volume " + "key from the CURRENT to the NEW master " + "key."); + } else { + warnx("Failed to re-encipher the secure volume " + "key for device '%s'\n", g.pos_arg); + if (!selected && + !is_ep11_aes_key((u8 *)key, keysize)) + print_msg_for_cca_envvars( "secure AES volume key"); - rc = -EINVAL; + rc = -EINVAL; + } goto out; } } @@ -1751,12 +1728,11 @@ static int reencipher_complete(int token) char vp[VERIFICATION_PATTERN_LEN]; struct reencipher_token tok; char *password = NULL; - u8 mkvp[MKVP_LENGTH]; size_t password_len; char *key = NULL; - int selected = 1; size_t keysize; int is_old_mk; + bool selected; char *prompt; char *msg; int rc; @@ -1804,39 +1780,24 @@ static int reencipher_complete(int token) goto out; } - rc = get_master_key_verification_pattern((u8 *)key, keysize, - mkvp, g.verbose); + rc = reencipher_secure_key(&g.lib, (u8 *)key, keysize, + NULL, REENCIPHER_OLD_TO_CURRENT, + &selected, g.verbose); if (rc != 0) { - warnx("Failed to get the master key verification " - "pattern: %s", - strerror(-rc)); - goto out; - } - - rc = select_cca_adapter_by_mkvp(&g.cca, mkvp, NULL, - FLAG_SEL_CCA_MATCH_OLD_MKVP, - g.verbose); - if (rc == -ENOTSUP) { - rc = 0; - selected = 0; - } - if (rc != 0) { - util_print_indented("No APQN found that is suitable " - "for re-enciphering the secure AES " - "volume key from the OLD to the " - "CURRENT master key.", 0); - goto out; - } - - rc = key_token_change(&g.cca, (u8 *)key, keysize, - METHOD_OLD_TO_CURRENT, g.verbose); - if (rc != 0) { - warnx("Failed to re-encipher the secure volume key for " - "device '%s'\n", g.pos_arg); - if (!selected) - print_msg_for_cca_envvars( + if (rc == -ENODEV) { + warnx("No APQN found that is suitable for " + "re-enciphering the secure AES volume " + "key from the OLD to the CURRENT master " + "key."); + } else { + warnx("Failed to re-encipher the secure volume " + "key for device '%s'\n", g.pos_arg); + if (!selected && + !is_ep11_aes_key((u8 *)key, keysize)) + print_msg_for_cca_envvars( "secure AES volume key"); - rc = -EINVAL; + rc = -EINVAL; + } goto out; } diff --git a/zkey/zkey.c b/zkey/zkey.c index d0fad71b..e6356c08 100644 --- a/zkey/zkey.c +++ b/zkey/zkey.c @@ -874,7 +874,7 @@ static struct zkey_command zkey_commands[] = { .command = COMMAND_REENCIPHER, .abbrev_len = 2, .function = command_reencipher, - .need_cca_library = 1, + /* Will load the CCA or EP11 library on demand */ .need_pkey_device = 1, .short_desc = "Re-encipher an existing secure AES key", .long_desc = "Re-encipher an existing secure AES " @@ -1205,10 +1205,9 @@ static int command_generate(void) static int command_reencipher_file(void) { size_t secure_key_size; - u8 mkvp[MKVP_LENGTH]; int rc, is_old_mk; - int selected = 1; u8 *secure_key; + bool selected; if (g.name != NULL) { warnx("Option '--name|-N' is not valid for " @@ -1254,15 +1253,6 @@ static int command_reencipher_file(void) goto out; } - rc = get_master_key_verification_pattern(secure_key, secure_key_size, - mkvp, g.verbose); - if (rc != 0) { - warnx("Failed to get the master key verification pattern: %s", - strerror(-rc)); - rc = EXIT_FAILURE; - goto out; - } - if (!g.fromold && !g.tonew) { /* Autodetect reencipher option */ if (is_old_mk) { @@ -1294,28 +1284,23 @@ static int command_reencipher_file(void) pr_verbose("Secure key will be re-enciphered from OLD to the " "CURRENT master key"); - rc = select_cca_adapter_by_mkvp(&g.cca, mkvp, NULL, - FLAG_SEL_CCA_MATCH_OLD_MKVP, - g.verbose); - if (rc == -ENOTSUP) { - rc = 0; - selected = 0; - } + rc = reencipher_secure_key(&g.lib, secure_key, secure_key_size, + NULL, REENCIPHER_OLD_TO_CURRENT, + &selected, g.verbose); if (rc != 0) { - warnx("No APQN found that is suitable for " - "re-enciphering the secure AES volume key"); - rc = EXIT_FAILURE; - goto out; - } - - rc = key_token_change(&g.cca, secure_key, secure_key_size, - METHOD_OLD_TO_CURRENT, - g.verbose); - if (rc != 0) { - warnx("Re-encipher from OLD to CURRENT " - "master key has failed\n"); - if (!selected) - print_msg_for_cca_envvars("secure AES key"); + if (rc == -ENODEV) { + warnx("No APQN found that is suitable for " + "re-enciphering the secure AES volume " + "key"); + } else { + warnx("Re-encipher from OLD to CURRENT " + "master key has failed\n"); + if (!selected && + !is_ep11_aes_key(secure_key, + secure_key_size)) + print_msg_for_cca_envvars( + "secure AES key"); + } rc = EXIT_FAILURE; goto out; } @@ -1324,30 +1309,23 @@ static int command_reencipher_file(void) pr_verbose("Secure key will be re-enciphered from CURRENT " "to the NEW master key"); - rc = select_cca_adapter_by_mkvp(&g.cca, mkvp, NULL, - FLAG_SEL_CCA_MATCH_CUR_MKVP | - FLAG_SEL_CCA_NEW_MUST_BE_SET, - g.verbose); - if (rc == -ENOTSUP) { - rc = 0; - selected = 0; - } + rc = reencipher_secure_key(&g.lib, secure_key, secure_key_size, + NULL, REENCIPHER_CURRENT_TO_NEW, + &selected, g.verbose); if (rc != 0) { - util_print_indented("No APQN found that is suitable " - "for re-enciphering this secure " - "AES key and has the NEW master " - "key loaded", 0); - rc = EXIT_FAILURE; - goto out; - } - - rc = key_token_change(&g.cca, secure_key, secure_key_size, - METHOD_CURRENT_TO_NEW, g.verbose); - if (rc != 0) { - warnx("Re-encipher from CURRENT to NEW " - "master key has failed\n"); - if (!selected) - print_msg_for_cca_envvars("secure AES key"); + if (rc == -ENODEV) { + warnx("No APQN found that is suitable for " + "re-enciphering the secure AES volume " + "key and has the NEW master key loaded"); + } else { + warnx("Re-encipher from CURRENT to NEW " + "master key has failed\n"); + if (!selected && + !is_ep11_aes_key(secure_key, + secure_key_size)) + print_msg_for_cca_envvars( + "secure AES key"); + } rc = EXIT_FAILURE; goto out; }