From badf51c869d687f934e817f3bb4653acec0088ca Mon Sep 17 00:00:00 2001 From: Richard Levitte Date: Thu, 20 Feb 2020 22:55:41 +0100 Subject: [PATCH] EVP: Add evp_pkey_upgrade_to_provider(), for EVP_PKEY upgrades This function "upgrades" a key from a legacy key container to a provider side key container. Reviewed-by: Shane Lontis (Merged from https://github.com/openssl/openssl/pull/11148) --- crypto/evp/p_lib.c | 129 ++++++++++++++++++++++++++++++++--- include/crypto/evp.h | 3 + test/keymgmt_internal_test.c | 3 +- 3 files changed, 124 insertions(+), 11 deletions(-) diff --git a/crypto/evp/p_lib.c b/crypto/evp/p_lib.c index 574527aa5c..621d99d171 100644 --- a/crypto/evp/p_lib.c +++ b/crypto/evp/p_lib.c @@ -866,28 +866,40 @@ int EVP_PKEY_up_ref(EVP_PKEY *pkey) return ((i > 1) ? 1 : 0); } +#ifndef FIPS_MODE +static void evp_pkey_free_legacy(EVP_PKEY *x) +{ + if (x->ameth != NULL) { + if (x->ameth->pkey_free) + x->ameth->pkey_free(x); + x->pkey.ptr = NULL; + x->ameth = NULL; + } +# ifndef OPENSSL_NO_ENGINE + ENGINE_finish(x->engine); + x->engine = NULL; + ENGINE_finish(x->pmeth_engine); + x->pmeth_engine = NULL; +# endif + x->type = x->save_type = EVP_PKEY_NONE; +} +#endif /* FIPS_MODE */ + static void evp_pkey_free_it(EVP_PKEY *x) { /* internal function; x is never NULL */ evp_keymgmt_util_clear_operation_cache(x); +#ifndef FIPS_MODE + evp_pkey_free_legacy(x); +#endif - if (x->ameth && x->ameth->pkey_free) { - x->ameth->pkey_free(x); - x->pkey.ptr = NULL; - } if (x->keymgmt != NULL) { evp_keymgmt_freedata(x->keymgmt, x->keydata); EVP_KEYMGMT_free(x->keymgmt); x->keymgmt = NULL; x->keydata = NULL; } -#if !defined(OPENSSL_NO_ENGINE) && !defined(FIPS_MODE) - ENGINE_finish(x->engine); - x->engine = NULL; - ENGINE_finish(x->pmeth_engine); - x->pmeth_engine = NULL; -#endif } void EVP_PKEY_free(EVP_PKEY *x) @@ -1053,3 +1065,100 @@ void *evp_pkey_export_to_provider(EVP_PKEY *pk, OPENSSL_CTX *libctx, EVP_KEYMGMT_free(allocated_keymgmt); return keydata; } + +#ifndef FIPS_MODE +/* + * This differs from exporting in that it releases the legacy key and assigns + * the export keymgmt and keydata to the "origin" provider side key instead + * of the operation cache. + */ +void *evp_pkey_upgrade_to_provider(EVP_PKEY *pk, OPENSSL_CTX *libctx, + EVP_KEYMGMT **keymgmt, + const char *propquery) +{ + EVP_KEYMGMT *allocated_keymgmt = NULL; + EVP_KEYMGMT *tmp_keymgmt = NULL; + void *keydata = NULL; + + if (pk == NULL) + return NULL; + + /* + * If this key is already "upgraded", this function shouldn't have been + * called. + */ + if (!ossl_assert(pk->keymgmt == NULL)) + return NULL; + + if (keymgmt != NULL) { + tmp_keymgmt = *keymgmt; + *keymgmt = NULL; + } + + /* If the key isn't a legacy one, bail out, but with proper values */ + if (pk->pkey.ptr == NULL) { + tmp_keymgmt = pk->keymgmt; + keydata = pk->keydata; + } else { + /* If the legacy key doesn't have an export function, give up */ + if (pk->ameth->export_to == NULL) + return NULL; + + /* If no keymgmt was given, get a default keymgmt */ + if (tmp_keymgmt == NULL) { + EVP_PKEY_CTX *ctx = + EVP_PKEY_CTX_new_from_pkey(libctx, pk, propquery); + + if (ctx != NULL && ctx->keytype != NULL) + tmp_keymgmt = allocated_keymgmt = + EVP_KEYMGMT_fetch(ctx->libctx, ctx->keytype, propquery); + EVP_PKEY_CTX_free(ctx); + } + + /* If we still don't have a keymgmt, give up */ + if (tmp_keymgmt == NULL) + goto end; + + /* Make sure that the keymgmt key type matches the legacy NID */ + if (!ossl_assert(EVP_KEYMGMT_is_a(tmp_keymgmt, OBJ_nid2sn(pk->type)))) + goto end; + + if ((keydata = evp_keymgmt_newdata(tmp_keymgmt)) == NULL) + goto end; + + if (!pk->ameth->export_to(pk, keydata, tmp_keymgmt) + || !EVP_KEYMGMT_up_ref(tmp_keymgmt)) { + evp_keymgmt_freedata(tmp_keymgmt, keydata); + keydata = NULL; + goto end; + } + + /* + * Clear the operation cache, all the legacy data, as well as the + * dirty counters + */ + evp_pkey_free_legacy(pk); + pk->dirty_cnt_copy = 0; + + evp_keymgmt_util_clear_operation_cache(pk); + pk->keymgmt = tmp_keymgmt; + pk->keydata = keydata; + evp_keymgmt_util_cache_keyinfo(pk); + } + + end: + /* + * If nothing was upgraded, |tmp_keymgmt| might point at a freed + * EVP_KEYMGMT, so we clear it to be safe. It shouldn't be useful for + * the caller either way in that case. + */ + if (keydata == NULL) + tmp_keymgmt = NULL; + + if (keymgmt != NULL) + *keymgmt = tmp_keymgmt; + + EVP_KEYMGMT_free(allocated_keymgmt); + return keydata; +} +#endif /* FIPS_MODE */ diff --git a/include/crypto/evp.h b/include/crypto/evp.h index aed2ccf31d..ddba4083e9 100644 --- a/include/crypto/evp.h +++ b/include/crypto/evp.h @@ -603,6 +603,9 @@ void evp_app_cleanup_int(void); void *evp_pkey_export_to_provider(EVP_PKEY *pk, OPENSSL_CTX *libctx, EVP_KEYMGMT **keymgmt, const char *propquery); +void *evp_pkey_upgrade_to_provider(EVP_PKEY *pk, OPENSSL_CTX *libctx, + EVP_KEYMGMT **keymgmt, + const char *propquery); /* * KEYMGMT utility functions diff --git a/test/keymgmt_internal_test.c b/test/keymgmt_internal_test.c index 77a4afa490..5ef238ccf1 100644 --- a/test/keymgmt_internal_test.c +++ b/test/keymgmt_internal_test.c @@ -207,7 +207,8 @@ static int test_pass_rsa(FIXTURE *fixture) || !TEST_ptr_ne(km1, km2)) goto err; - if (!TEST_ptr(evp_pkey_make_provided(pk, NULL, &km1, NULL)) + if (!TEST_ptr(evp_pkey_export_to_provider(pk, NULL, &km1, NULL)) + || !TEST_ptr(evp_pkey_upgrade_to_provider(pk, NULL, &km1, NULL)) || !TEST_ptr(provkey = evp_keymgmt_util_export_to_provider(pk, km2))) goto err; -- 2.34.1