aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crypto/evp/evp_fetch.c2
-rw-r--r--crypto/evp/evp_local.h4
-rw-r--r--crypto/evp/exchange.c12
-rw-r--r--crypto/evp/keymgmt_lib.c145
-rw-r--r--crypto/evp/p_lib.c106
-rw-r--r--doc/internal/man3/evp_keymgmt_util_export_to_provider.pod16
-rw-r--r--doc/internal/man3/evp_pkey_make_provided.pod8
-rw-r--r--include/crypto/evp.h4
-rw-r--r--test/keymgmt_internal_test.c2
9 files changed, 209 insertions, 90 deletions
diff --git a/crypto/evp/evp_fetch.c b/crypto/evp/evp_fetch.c
index 5bf825d..da7f33e 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 ca1239d..9b4ab29 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 901081d..142a820 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 812bdcb..cb30405 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 98e0704..2ffddf5 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
diff --git a/doc/internal/man3/evp_keymgmt_util_export_to_provider.pod b/doc/internal/man3/evp_keymgmt_util_export_to_provider.pod
index 38e7133..2c8b7b2 100644
--- a/doc/internal/man3/evp_keymgmt_util_export_to_provider.pod
+++ b/doc/internal/man3/evp_keymgmt_util_export_to_provider.pod
@@ -21,17 +21,15 @@ evp_keymgmt_util_fromdata
=head1 DESCRIPTION
-evp_keymgmt_util_export_to_provider() exports the key material from
-the given key I<pk> to a provider via a B<EVP_KEYMGMT> interface, if
-this hasn't already been done.
+evp_keymgmt_util_export_to_provider() exports cached key material
+(provider side key material) from the given key I<pk> to a provider
+via a B<EVP_KEYMGMT> interface, if this hasn't already been done.
It maintains a cache of provider key references in I<pk> to keep track
-of all such exports.
+of all provider side keys.
-If I<pk> has an assigned legacy key, a check is done to see if any of
-its key material has changed since last export, i.e. the legacy key's
-is_dirty() method returns 1.
-If it has, the cache of already exported keys is cleared, and a new
-export is made with the new key material.
+To export a legacy key, use L<evp_pkey_make_provided(3)> instead, as
+this function deals purely with provider side keys and will not care
+to look at any legacy key.
evp_keymgmt_util_clear_pkey_cache() can be used to explicitly clear
the cache of provider key references.
diff --git a/doc/internal/man3/evp_pkey_make_provided.pod b/doc/internal/man3/evp_pkey_make_provided.pod
index 12cbe0c..3eb17e7 100644
--- a/doc/internal/man3/evp_pkey_make_provided.pod
+++ b/doc/internal/man3/evp_pkey_make_provided.pod
@@ -24,6 +24,14 @@ fetch an B<EVP_KEYMGMT> implicitly, using I<propquery> as property query string.
As output from this function, I<*keymgmt> will be assigned the B<EVP_KEYMGMT>
that was used, if the export was successful, otherwise it will be assigned NULL.
+If I<pk> has an assigned legacy key, a check is done to see if any of
+its key material has changed since last export, by comparing the
+result of the legacy key's dirty_cnt() method with a copy of that
+result from last time evp_pkey_make_provided() was run with this
+B<EVP_PKEY>.
+If it has, the cache of already exported keys is cleared, and a new
+export is made with the new legacy key material.
+
=head1 RETURN VALUES
evp_pkey_make_provided() returns the provider key data that was exported if
diff --git a/include/crypto/evp.h b/include/crypto/evp.h
index 0f5e86b..1724a12 100644
--- a/include/crypto/evp.h
+++ b/include/crypto/evp.h
@@ -574,11 +574,15 @@ void openssl_add_all_ciphers_int(void);
void openssl_add_all_digests_int(void);
void evp_cleanup_int(void);
void evp_app_cleanup_int(void);
+void *evp_pkey_make_provided(EVP_PKEY *pk, OPENSSL_CTX *libctx,
+ EVP_KEYMGMT **keymgmt, const char *propquery);
/*
* KEYMGMT utility functions
*/
void *evp_keymgmt_util_export_to_provider(EVP_PKEY *pk, EVP_KEYMGMT *keymgmt);
+size_t evp_keymgmt_util_find_pkey_cache_index(EVP_PKEY *pk,
+ EVP_KEYMGMT *keymgmt);
void evp_keymgmt_util_clear_pkey_cache(EVP_PKEY *pk);
void evp_keymgmt_util_cache_pkey(EVP_PKEY *pk, size_t index,
EVP_KEYMGMT *keymgmt, void *keydata);
diff --git a/test/keymgmt_internal_test.c b/test/keymgmt_internal_test.c
index ad2e1de..77a4afa 100644
--- a/test/keymgmt_internal_test.c
+++ b/test/keymgmt_internal_test.c
@@ -207,7 +207,7 @@ static int test_pass_rsa(FIXTURE *fixture)
|| !TEST_ptr_ne(km1, km2))
goto err;
- if (!TEST_ptr(evp_keymgmt_util_export_to_provider(pk, km1))
+ if (!TEST_ptr(evp_pkey_make_provided(pk, NULL, &km1, NULL))
|| !TEST_ptr(provkey = evp_keymgmt_util_export_to_provider(pk, km2)))
goto err;