From: Richard Levitte Date: Wed, 5 Feb 2020 15:30:21 +0000 (+0100) Subject: EVP: Add support for copying provided EVP_PKEYs X-Git-Tag: openssl-3.0.0-alpha1~347 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=ff3b59e1705a40f7cfc6df8d788d08b0a525aa58;p=oweals%2Fopenssl.git EVP: Add support for copying provided EVP_PKEYs This adds evp_keymgmt_util_copy() and affects EVP_PKEY_copy_parameters() Reviewed-by: Shane Lontis (Merged from https://github.com/openssl/openssl/pull/11158) --- diff --git a/crypto/evp/keymgmt_lib.c b/crypto/evp/keymgmt_lib.c index 6bbf28aa34..68ccdbb8ee 100644 --- a/crypto/evp/keymgmt_lib.c +++ b/crypto/evp/keymgmt_lib.c @@ -293,3 +293,60 @@ int evp_keymgmt_util_match(EVP_PKEY *pk1, EVP_PKEY *pk2, int selection) return evp_keymgmt_match(keymgmt1, keydata1, keydata2, selection); } + +int evp_keymgmt_util_copy(EVP_PKEY *to, EVP_PKEY *from, int selection) +{ + /* Save copies of pointers we want to play with without affecting |to| */ + EVP_KEYMGMT *to_keymgmt = to->keymgmt; + void *to_keydata = to->keydata, *alloc_keydata = NULL; + + /* An unassigned key can't be copied */ + if (from == NULL || from->keymgmt == NULL) + return 0; + + /* If |from| doesn't support copying, we fail */ + if (from->keymgmt->copy == NULL) + return 0; + + /* If |to| doesn't have a provider side "origin" yet, create one */ + if (to_keymgmt == NULL) { + to_keydata = alloc_keydata = evp_keymgmt_newdata(from->keymgmt); + if (to_keydata == NULL) + return 0; + to_keymgmt = from->keymgmt; + } + + if (to_keymgmt == from->keymgmt) { + /* |to| and |from| have the same keymgmt, just copy and be done */ + if (!evp_keymgmt_copy(to_keymgmt, to_keydata, from->keydata, + selection)) + return 0; + } else if (match_type(to_keymgmt, from->keymgmt)) { + struct import_data_st import_data; + + import_data.keymgmt = to_keymgmt; + import_data.keydata = to_keydata; + import_data.selection = selection; + + if (!evp_keymgmt_export(from->keymgmt, from->keydata, selection, + &try_import, &import_data)) { + evp_keymgmt_freedata(to_keymgmt, alloc_keydata); + return 0; + } + } else { + ERR_raise(ERR_LIB_EVP, EVP_R_DIFFERENT_KEY_TYPES); + return 0; + } + + if (to->keymgmt == NULL + && !EVP_KEYMGMT_up_ref(to_keymgmt)) { + evp_keymgmt_freedata(to_keymgmt, alloc_keydata); + return 0; + } + evp_keymgmt_util_clear_operation_cache(to); + to->keymgmt = to_keymgmt; + to->keydata = to_keydata; + evp_keymgmt_util_cache_keyinfo(to); + + return 1; +} diff --git a/crypto/evp/p_lib.c b/crypto/evp/p_lib.c index a13a98be71..586ffaf041 100644 --- a/crypto/evp/p_lib.c +++ b/crypto/evp/p_lib.c @@ -86,12 +86,25 @@ int EVP_PKEY_save_parameters(EVP_PKEY *pkey, int mode) int EVP_PKEY_copy_parameters(EVP_PKEY *to, const EVP_PKEY *from) { - if (to->type == EVP_PKEY_NONE) { - if (EVP_PKEY_set_type(to, from->type) == 0) - return 0; - } else if (to->type != from->type) { - EVPerr(EVP_F_EVP_PKEY_COPY_PARAMETERS, EVP_R_DIFFERENT_KEY_TYPES); - goto err; + /* + * TODO: clean up legacy stuff from this function when legacy support + * is gone. + */ + + /* + * Only check that type match this early when both keys are legacy. + * If either of them is provided, we let evp_keymgmt_util_copy() + * do this check, after having exported either of them that isn't + * provided. + */ + if (to->keymgmt == NULL && from->keymgmt == NULL) { + if (to->type == EVP_PKEY_NONE) { + if (EVP_PKEY_set_type(to, from->type) == 0) + return 0; + } else if (to->type != from->type) { + EVPerr(EVP_F_EVP_PKEY_COPY_PARAMETERS, EVP_R_DIFFERENT_KEY_TYPES); + goto err; + } } if (EVP_PKEY_missing_parameters(from)) { @@ -106,7 +119,56 @@ int EVP_PKEY_copy_parameters(EVP_PKEY *to, const EVP_PKEY *from) return 0; } - if (from->ameth && from->ameth->param_copy) + /* + * If |from| is provided, we upgrade |to| to be provided as well. + * This drops the legacy key from |to|. + * evp_pkey_upgrade_to_provider() checks if |to| is already provided, + * we don't need to do that here. + * + * TODO(3.0) We should investigate if that's too aggressive and make + * this scenario unsupported instead. + */ + if (from->keymgmt != NULL) { + EVP_KEYMGMT *tmp_keymgmt = from->keymgmt; + + /* + * The returned pointer is known to be cached, so we don't have to + * save it. However, if it's NULL, something went wrong and we can't + * copy. + */ + if (evp_pkey_upgrade_to_provider(to, NULL, + &tmp_keymgmt, NULL) == NULL) { + ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR); + return 0; + } + } + + /* For purely provided keys, we just call the keymgmt utility */ + if (to->keymgmt != NULL && from->keymgmt != NULL) + return evp_keymgmt_util_copy(to, (EVP_PKEY *)from, + OSSL_KEYMGMT_SELECT_ALL_PARAMETERS); + + /* + * If |to| is provided, we know that |from| is legacy at this point. + * Try exporting |from| to |to|'s keymgmt, then use evp_keymgmt_copy() + * to copy the appropriate data to |to|'s keydata. + */ + if (to->keymgmt != NULL) { + EVP_KEYMGMT *to_keymgmt = to->keymgmt; + void *from_keydata = + evp_pkey_export_to_provider((EVP_PKEY *)from, NULL, &to_keymgmt, + NULL); + + if (from_keydata == NULL) { + ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR); + return 0; + } + return evp_keymgmt_copy(to->keymgmt, to->keydata, from_keydata, + OSSL_KEYMGMT_SELECT_ALL_PARAMETERS); + } + + /* Both keys are legacy */ + if (from->ameth != NULL && from->ameth->param_copy != NULL) return from->ameth->param_copy(to, from); err: return 0; @@ -953,7 +1015,7 @@ int EVP_PKEY_up_ref(EVP_PKEY *pkey) static void evp_pkey_free_legacy(EVP_PKEY *x) { if (x->ameth != NULL) { - if (x->ameth->pkey_free) + if (x->ameth->pkey_free != NULL) x->ameth->pkey_free(x); x->pkey.ptr = NULL; x->ameth = NULL; diff --git a/include/crypto/evp.h b/include/crypto/evp.h index 21f3f16053..7da0258279 100644 --- a/include/crypto/evp.h +++ b/include/crypto/evp.h @@ -621,6 +621,7 @@ void *evp_keymgmt_util_fromdata(EVP_PKEY *target, EVP_KEYMGMT *keymgmt, int selection, const OSSL_PARAM params[]); int evp_keymgmt_util_has(EVP_PKEY *pk, int selection); int evp_keymgmt_util_match(EVP_PKEY *pk1, EVP_PKEY *pk2, int selection); +int evp_keymgmt_util_copy(EVP_PKEY *to, EVP_PKEY *from, int selection); /*