diff options
Diffstat (limited to 'crypto/evp')
-rw-r--r-- | crypto/evp/evp_fetch.c | 2 | ||||
-rw-r--r-- | crypto/evp/evp_local.h | 4 | ||||
-rw-r--r-- | crypto/evp/exchange.c | 12 | ||||
-rw-r--r-- | crypto/evp/keymgmt_lib.c | 145 | ||||
-rw-r--r-- | crypto/evp/p_lib.c | 106 |
5 files changed, 189 insertions, 80 deletions
diff --git a/crypto/evp/evp_fetch.c b/crypto/evp/evp_fetch.c index 5bf825d62b..da7f33e95e 100644 --- a/crypto/evp/evp_fetch.c +++ b/crypto/evp/evp_fetch.c @@ -373,7 +373,7 @@ void evp_generic_do_all(OPENSSL_CTX *libctx, int operation_id, ossl_algorithm_do_all(libctx, operation_id, NULL, do_one, &data); } -const char *evp_first_name(OSSL_PROVIDER *prov, int name_id) +const char *evp_first_name(const OSSL_PROVIDER *prov, int name_id) { OPENSSL_CTX *libctx = ossl_provider_library_context(prov); OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx); diff --git a/crypto/evp/evp_local.h b/crypto/evp/evp_local.h index ca1239dfdd..9b4ab29fda 100644 --- a/crypto/evp/evp_local.h +++ b/crypto/evp/evp_local.h @@ -267,12 +267,10 @@ OSSL_PARAM *evp_pkey_to_param(EVP_PKEY *pkey, size_t *sz); void evp_pkey_ctx_free_old_ops(EVP_PKEY_CTX *ctx); /* OSSL_PROVIDER * is only used to get the library context */ -const char *evp_first_name(OSSL_PROVIDER *prov, int name_id); +const char *evp_first_name(const OSSL_PROVIDER *prov, int name_id); int evp_is_a(OSSL_PROVIDER *prov, int number, const char *legacy_name, const char *name); void evp_names_do_all(OSSL_PROVIDER *prov, int number, void (*fn)(const char *name, void *data), void *data); int evp_cipher_cache_constants(EVP_CIPHER *cipher); -void *evp_pkey_make_provided(EVP_PKEY *pk, OPENSSL_CTX *libctx, - EVP_KEYMGMT **keymgmt, const char *propquery); diff --git a/crypto/evp/exchange.c b/crypto/evp/exchange.c index 901081d062..142a820651 100644 --- a/crypto/evp/exchange.c +++ b/crypto/evp/exchange.c @@ -309,8 +309,12 @@ int EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *ctx, EVP_PKEY *peer) return -2; } - provkey = evp_keymgmt_util_export_to_provider(peer, ctx->keymgmt); - /* If export failed, legacy may be able to pick it up */ + provkey = evp_pkey_make_provided(peer, ctx->libctx, &ctx->keymgmt, + ctx->propquery); + /* + * If making the key provided wasn't possible, legacy may be able to pick + * it up + */ if (provkey == NULL) goto legacy; return ctx->op.kex.exchange->set_peer(ctx->op.kex.exchprovctx, provkey); @@ -319,6 +323,10 @@ int EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *ctx, EVP_PKEY *peer) #ifdef FIPS_MODE return ret; #else + /* + * TODO(3.0) investigate the case where the operation is deemed legacy, + * but the given peer key is provider only. + */ if (ctx->pmeth == NULL || !(ctx->pmeth->derive != NULL || ctx->pmeth->encrypt != NULL diff --git a/crypto/evp/keymgmt_lib.c b/crypto/evp/keymgmt_lib.c index 812bdcb560..cb30405166 100644 --- a/crypto/evp/keymgmt_lib.c +++ b/crypto/evp/keymgmt_lib.c @@ -16,6 +16,18 @@ #include "internal/provider.h" #include "evp_local.h" +/* + * match_type() checks if two EVP_KEYMGMT are matching key types. This + * function assumes that the caller has made all the necessary NULL checks. + */ +static int match_type(const EVP_KEYMGMT *keymgmt1, const EVP_KEYMGMT *keymgmt2) +{ + const OSSL_PROVIDER *prov2 = EVP_KEYMGMT_provider(keymgmt2); + const char *name2 = evp_first_name(prov2, EVP_KEYMGMT_number(keymgmt2)); + + return EVP_KEYMGMT_is_a(keymgmt1, name2); +} + struct import_data_st { EVP_KEYMGMT *keymgmt; void *keydata; @@ -34,92 +46,72 @@ static int try_import(const OSSL_PARAM params[], void *arg) void *evp_keymgmt_util_export_to_provider(EVP_PKEY *pk, EVP_KEYMGMT *keymgmt) { void *keydata = NULL; + struct import_data_st import_data; size_t i, j; - /* - * If there is an underlying legacy key and it has changed, invalidate - * the cache of provider keys. - */ - if (pk->pkey.ptr != NULL) { - /* - * If there is no dirty counter, this key can't be used with - * providers. - */ - if (pk->ameth->dirty_cnt == NULL) - return NULL; + /* Export to where? */ + if (keymgmt == NULL) + return NULL; - if (pk->ameth->dirty_cnt(pk) != pk->dirty_cnt_copy) - evp_keymgmt_util_clear_pkey_cache(pk); - } + /* If we have an unassigned key, give up */ + if (pk->pkeys[0].keymgmt == NULL) + return NULL; /* * See if we have exported to this provider already. * If we have, return immediately. */ - for (i = 0; - i < OSSL_NELEM(pk->pkeys) && pk->pkeys[i].keymgmt != NULL; - i++) { - if (keymgmt == pk->pkeys[i].keymgmt) - return pk->pkeys[i].keydata; - } + i = evp_keymgmt_util_find_pkey_cache_index(pk, keymgmt); + + /* If we're already exported to the given keymgmt, no more to do */ + if (keymgmt == pk->pkeys[i].keymgmt) + return pk->pkeys[i].keydata; + /* + * Make sure that the type of the keymgmt to export to matches the type + * of already cached keymgmt + */ + if (!ossl_assert(match_type(pk->pkeys[0].keymgmt, keymgmt))) + return NULL; + + /* Create space to import data into */ if ((keydata = evp_keymgmt_newdata(keymgmt)) == NULL) return NULL; - if (pk->pkey.ptr != NULL) { - /* There is a legacy key, try to export that one to the provider */ + /* + * We look at the already cached provider keys, and import from the + * first that supports it (i.e. use its export function), and export + * the imported data to the new provider. + */ + + /* Setup for the export callback */ + import_data.keydata = keydata; + import_data.keymgmt = keymgmt; + import_data.selection = OSSL_KEYMGMT_SELECT_ALL; + + for (j = 0; j < i && pk->pkeys[j].keymgmt != NULL; j++) { + EVP_KEYMGMT *exp_keymgmt = pk->pkeys[j].keymgmt; + void *exp_keydata = pk->pkeys[j].keydata; /* - * If the legacy key doesn't have an export function or the export - * function fails, give up + * TODO(3.0) consider an evp_keymgmt_export() return value that + * indicates that the method is unsupported. */ - if (pk->ameth->export_to == NULL - || !pk->ameth->export_to(pk, keydata, keymgmt)) { - evp_keymgmt_freedata(keymgmt, keydata); - return NULL; - } + if (exp_keymgmt->export == NULL) + continue; - /* Synchronize the dirty count */ - pk->dirty_cnt_copy = pk->ameth->dirty_cnt(pk); - } else { /* - * Here, there is no legacy key, so we look at the already cached - * provider keys, and import from the first that supports it - * (i.e. use its export function), and export the imported data to - * the new provider. + * The export function calls the callback (try_import), which does + * the import for us. If successful, we're done. */ + if (evp_keymgmt_export(exp_keymgmt, exp_keydata, + OSSL_KEYMGMT_SELECT_ALL, + &try_import, &import_data)) + break; - /* Setup for the export callback */ - struct import_data_st import_data; - - import_data.keydata = keydata; - import_data.keymgmt = keymgmt; - import_data.selection = OSSL_KEYMGMT_SELECT_ALL; - - for (j = 0; j < i && pk->pkeys[j].keymgmt != NULL; j++) { - EVP_KEYMGMT *exp_keymgmt = pk->pkeys[i].keymgmt; - void *exp_keydata = pk->pkeys[i].keydata; - - /* - * TODO(3.0) consider an evp_keymgmt_export() return value that - * indicates that the method is unsupported. - */ - if (exp_keymgmt->export == NULL) - continue; - - /* - * The export function calls the callback (try_import), which - * does the import for us. If successful, we're done. - */ - if (evp_keymgmt_export(exp_keymgmt, exp_keydata, - OSSL_KEYMGMT_SELECT_ALL, - &try_import, &import_data)) - break; - - /* If there was an error, bail out */ - evp_keymgmt_freedata(keymgmt, keydata); - return NULL; - } + /* If there was an error, bail out */ + evp_keymgmt_freedata(keymgmt, keydata); + return NULL; } /* @@ -137,12 +129,10 @@ void *evp_keymgmt_util_export_to_provider(EVP_PKEY *pk, EVP_KEYMGMT *keymgmt) void evp_keymgmt_util_clear_pkey_cache(EVP_PKEY *pk) { - size_t i; + size_t i, end = OSSL_NELEM(pk->pkeys); if (pk != NULL) { - for (i = 0; - i < OSSL_NELEM(pk->pkeys) && pk->pkeys[i].keymgmt != NULL; - i++) { + for (i = 0; i < end && pk->pkeys[i].keymgmt != NULL; i++) { EVP_KEYMGMT *keymgmt = pk->pkeys[i].keymgmt; void *keydata = pk->pkeys[i].keydata; @@ -158,6 +148,19 @@ void evp_keymgmt_util_clear_pkey_cache(EVP_PKEY *pk) } } +size_t evp_keymgmt_util_find_pkey_cache_index(EVP_PKEY *pk, + EVP_KEYMGMT *keymgmt) +{ + size_t i, end = OSSL_NELEM(pk->pkeys); + + for (i = 0; i < end && pk->pkeys[i].keymgmt != NULL; i++) { + if (keymgmt == pk->pkeys[i].keymgmt) + break; + } + + return i; +} + void evp_keymgmt_util_cache_pkey(EVP_PKEY *pk, size_t index, EVP_KEYMGMT *keymgmt, void *keydata) { diff --git a/crypto/evp/p_lib.c b/crypto/evp/p_lib.c index 98e0704347..2ffddf5d0a 100644 --- a/crypto/evp/p_lib.c +++ b/crypto/evp/p_lib.c @@ -932,6 +932,55 @@ void *evp_pkey_make_provided(EVP_PKEY *pk, OPENSSL_CTX *libctx, *keymgmt = NULL; } +#ifndef FIPS_MODE + /* + * If there is an underlying legacy key and it has changed, invalidate + * the cache of provider keys. + */ + if (pk->pkey.ptr != NULL) { + EVP_KEYMGMT *legacy_keymgmt = NULL; + + /* + * If there is no dirty counter, this key can't be used with + * providers. + */ + if (pk->ameth->dirty_cnt == NULL) + goto end; + + /* + * If no keymgmt was given by the caller, we set it to the first + * that's cached, to become the keymgmt to re-export to if needed, + * or to have a token keymgmt to return on success. Further checks + * are done further down. + * + * We need to carefully save the pointer somewhere other than in + * tmp_keymgmt, so the EVP_KEYMGMT_up_ref() below doesn't mistakenly + * increment the reference counter of a keymgmt given by the caller. + */ + if (tmp_keymgmt == NULL) + legacy_keymgmt = pk->pkeys[0].keymgmt; + + /* + * If the dirty counter changed since last time, we make sure to + * hold on to the keymgmt we just got (if we got one), then clear + * the cache. + */ + if (pk->ameth->dirty_cnt(pk) != pk->dirty_cnt_copy) { + if (legacy_keymgmt != NULL && !EVP_KEYMGMT_up_ref(legacy_keymgmt)) + goto end; + evp_keymgmt_util_clear_pkey_cache(pk); + } + + /* + * |legacy_keymgmt| was only given a value if |tmp_keymgmt| is + * NULL. + */ + if (legacy_keymgmt != NULL) + tmp_keymgmt = legacy_keymgmt; + } +#endif + + /* If no keymgmt was given or found, get a default keymgmt */ if (tmp_keymgmt == NULL) { EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_pkey(libctx, pk, propquery); @@ -941,10 +990,61 @@ void *evp_pkey_make_provided(EVP_PKEY *pk, OPENSSL_CTX *libctx, EVP_PKEY_CTX_free(ctx); } - if (tmp_keymgmt != NULL) - keydata = - evp_keymgmt_util_export_to_provider(pk, tmp_keymgmt); + if (tmp_keymgmt == NULL) + goto end; +#ifndef FIPS_MODE + if (pk->pkey.ptr != NULL) { + size_t i; + + /* + * Find our keymgmt in the cache. If it's present, it means that + * export has already been done. We take token copies of the + * cached pointers, to have token success values to return. + * + * TODO(3.0) Right now, we assume we have ample space. We will + * have to think about a cache aging scheme, though, if |i| indexes + * outside the array. + */ + i = evp_keymgmt_util_find_pkey_cache_index(pk, tmp_keymgmt); + if (!ossl_assert(i < OSSL_NELEM(pk->pkeys))) + goto end; + if (pk->pkeys[i].keymgmt != NULL) { + keydata = pk->pkeys[i].keydata; + goto end; + } + + /* + * If we still don't have a keymgmt at this point, or the legacy + * key doesn't have an export function, just bail out. + */ + if (pk->ameth->export_to == 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_freedata(tmp_keymgmt, keydata); + keydata = NULL; + goto end; + } + + evp_keymgmt_util_cache_pkey(pk, i, tmp_keymgmt, keydata); + + /* Synchronize the dirty count */ + pk->dirty_cnt_copy = pk->ameth->dirty_cnt(pk); + goto end; + } +#endif /* FIPS_MODE */ + + keydata = evp_keymgmt_util_export_to_provider(pk, tmp_keymgmt); + + end: /* * If nothing was exported, |tmp_keymgmt| might point at a freed * EVP_KEYMGMT, so we clear it to be safe. It shouldn't be useful for |