diff options
Diffstat (limited to 'crypto/evp/p_lib.c')
-rw-r--r-- | crypto/evp/p_lib.c | 106 |
1 files changed, 103 insertions, 3 deletions
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 |