return pk->keydata;
/* If this key is already exported to |keymgmt|, no more to do */
+ CRYPTO_THREAD_read_lock(pk->lock);
i = evp_keymgmt_util_find_operation_cache_index(pk, keymgmt);
if (i < OSSL_NELEM(pk->operation_cache)
- && pk->operation_cache[i].keymgmt != NULL)
- return pk->operation_cache[i].keydata;
+ && pk->operation_cache[i].keymgmt != NULL) {
+ void *ret = pk->operation_cache[i].keydata;
+
+ CRYPTO_THREAD_unlock(pk->lock);
+ return ret;
+ }
+ CRYPTO_THREAD_unlock(pk->lock);
/* If the "origin" |keymgmt| doesn't support exporting, give up */
/*
return NULL;
}
+ CRYPTO_THREAD_write_lock(pk->lock);
+ /* Check to make sure some other thread didn't get there first */
+ i = evp_keymgmt_util_find_operation_cache_index(pk, keymgmt);
+ if (i < OSSL_NELEM(pk->operation_cache)
+ && pk->operation_cache[i].keymgmt != NULL) {
+ void *ret = pk->operation_cache[i].keydata;
+
+ CRYPTO_THREAD_unlock(pk->lock);
+
+ /*
+ * Another thread seemms to have already exported this so we abandon
+ * all the work we just did.
+ */
+ evp_keymgmt_freedata(keymgmt, import_data.keydata);
+
+ return ret;
+ }
+
/* Add the new export to the operation cache */
if (!evp_keymgmt_util_cache_keydata(pk, i, keymgmt, import_data.keydata)) {
evp_keymgmt_freedata(keymgmt, import_data.keydata);
return NULL;
}
+ CRYPTO_THREAD_unlock(pk->lock);
+
return import_data.keydata;
}
-void evp_keymgmt_util_clear_operation_cache(EVP_PKEY *pk)
+int evp_keymgmt_util_clear_operation_cache(EVP_PKEY *pk, int locking)
{
size_t i, end = OSSL_NELEM(pk->operation_cache);
if (pk != NULL) {
+ if (locking && pk->lock != NULL && !CRYPTO_THREAD_write_lock(pk->lock))
+ return 0;
for (i = 0; i < end && pk->operation_cache[i].keymgmt != NULL; i++) {
EVP_KEYMGMT *keymgmt = pk->operation_cache[i].keymgmt;
void *keydata = pk->operation_cache[i].keydata;
evp_keymgmt_freedata(keymgmt, keydata);
EVP_KEYMGMT_free(keymgmt);
}
+ if (locking && pk->lock != NULL)
+ CRYPTO_THREAD_unlock(pk->lock);
}
+
+ return 1;
}
size_t evp_keymgmt_util_find_operation_cache_index(EVP_PKEY *pk,
if (keydata != NULL) {
if (!EVP_KEYMGMT_up_ref(keymgmt))
return 0;
+
pk->operation_cache[index].keydata = keydata;
pk->operation_cache[index].keymgmt = keymgmt;
}
{
/* internal function; x is never NULL */
- evp_keymgmt_util_clear_operation_cache(x);
+ evp_keymgmt_util_clear_operation_cache(x, 1);
#ifndef FIPS_MODULE
evp_pkey_free_legacy(x);
#endif
* |i| remains zero, and we will clear the cache further down.
*/
if (pk->ameth->dirty_cnt(pk) == pk->dirty_cnt_copy) {
+ if (!CRYPTO_THREAD_read_lock(pk->lock))
+ goto end;
i = evp_keymgmt_util_find_operation_cache_index(pk, tmp_keymgmt);
/*
if (i < OSSL_NELEM(pk->operation_cache)
&& pk->operation_cache[i].keymgmt != NULL) {
keydata = pk->operation_cache[i].keydata;
+ CRYPTO_THREAD_unlock(pk->lock);
goto end;
}
+ CRYPTO_THREAD_unlock(pk->lock);
}
/*
keydata = NULL;
goto end;
}
- if (pk->ameth->dirty_cnt(pk) != pk->dirty_cnt_copy)
- evp_keymgmt_util_clear_operation_cache(pk);
+
+ if (!CRYPTO_THREAD_write_lock(pk->lock))
+ goto end;
+ if (pk->ameth->dirty_cnt(pk) != pk->dirty_cnt_copy
+ && !evp_keymgmt_util_clear_operation_cache(pk, 0)) {
+ CRYPTO_THREAD_unlock(pk->lock);
+ evp_keymgmt_freedata(tmp_keymgmt, keydata);
+ keydata = NULL;
+ EVP_KEYMGMT_free(tmp_keymgmt);
+ goto end;
+ }
EVP_KEYMGMT_free(tmp_keymgmt); /* refcnt-- */
/* Add the new export to the operation cache */
if (!evp_keymgmt_util_cache_keydata(pk, i, tmp_keymgmt, keydata)) {
+ CRYPTO_THREAD_unlock(pk->lock);
evp_keymgmt_freedata(tmp_keymgmt, keydata);
keydata = NULL;
goto end;
/* Synchronize the dirty count */
pk->dirty_cnt_copy = pk->ameth->dirty_cnt(pk);
+
+ CRYPTO_THREAD_unlock(pk->lock);
goto end;
}
#endif /* FIPS_MODULE */
void *evp_keymgmt_util_export_to_provider(EVP_PKEY *pk, EVP_KEYMGMT *keymgmt);
size_t evp_keymgmt_util_find_operation_cache_index(EVP_PKEY *pk,
EVP_KEYMGMT *keymgmt);
- void evp_keymgmt_util_clear_operation_cache(EVP_PKEY *pk);
- void evp_keymgmt_util_cache_keydata(EVP_PKEY *pk, size_t index,
- EVP_KEYMGMT *keymgmt, void *keydata);
+ int evp_keymgmt_util_clear_operation_cache(EVP_PKEY *pk, int locking);
+ int evp_keymgmt_util_cache_keydata(EVP_PKEY *pk, size_t index,
+ EVP_KEYMGMT *keymgmt, void *keydata);
void evp_keymgmt_util_cache_keyinfo(EVP_PKEY *pk);
void *evp_keymgmt_util_fromdata(EVP_PKEY *target, EVP_KEYMGMT *keymgmt,
int selection, const OSSL_PARAM params[]);
evp_keymgmt_util_find_operation_cache_index() finds the location if
I<keymgmt> in I<pk>'s cache of provided keys for operations. If
I<keymgmt> is NULL or couldn't be found in the cache, it finds the
-first empty slot instead if there is any.
+first empty slot instead if there is any. It should only be called while
+holding I<pk>'s lock (read or write).
evp_keymgmt_util_clear_operation_cache() can be used to explicitly
-clear the cache of operation key references.
+clear the cache of operation key references. If I<locking> is set to 1 then
+then I<pk>'s lock will be obtained while doing the clear. Otherwise it will be
+assumed that the lock has already been obtained or is not required.
evp_keymgmt_util_cache_keydata() can be used to assign a provider key
object to a specific cache slot in the given I<target>.
with a match for I<keymgmt>, the index of the first empty slot is
returned, or the maximum number of slots if there isn't an empty one.
+evp_keymgmt_util_cache_keydata() and evp_keymgmt_util_clear_operation_cache()
+return 1 on success or 0 otherwise.
+
=head1 NOTES
"Legacy key" is the term used for any key that has been assigned to an
void *evp_keymgmt_util_export_to_provider(EVP_PKEY *pk, EVP_KEYMGMT *keymgmt);
size_t evp_keymgmt_util_find_operation_cache_index(EVP_PKEY *pk,
EVP_KEYMGMT *keymgmt);
-void evp_keymgmt_util_clear_operation_cache(EVP_PKEY *pk);
+int evp_keymgmt_util_clear_operation_cache(EVP_PKEY *pk, int locking);
int evp_keymgmt_util_cache_keydata(EVP_PKEY *pk, size_t index,
EVP_KEYMGMT *keymgmt, void *keydata);
void evp_keymgmt_util_cache_keyinfo(EVP_PKEY *pk);