mirror of https://github.com/openssl/openssl.git
Rethink the EVP_PKEY cache of provider side keys
The role of this cache was two-fold:
1. It was a cache of key copies exported to providers with which an
operation was initiated.
2. If the EVP_PKEY didn't have a legacy key, item 0 of the cache was
the corresponding provider side origin, while the rest was the
actual cache.
This dual role for item 0 made the code a bit confusing, so we now
make a separate keymgmt / keydata pair outside of that cache, which is
the provider side "origin" key.
A hard rule is that an EVP_PKEY cannot hold a legacy "origin" and a
provider side "origin" at the same time.
Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
(Merged from https://github.com/openssl/openssl/pull/11148)
This commit is contained in:
parent
4911964763
commit
3c6ed9555c
|
|
@ -30,7 +30,7 @@ int i2d_PrivateKey(const EVP_PKEY *a, unsigned char **pp)
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
if (a->pkeys[0].keymgmt != NULL) {
|
if (a->keymgmt != NULL) {
|
||||||
const char *serprop = OSSL_SERIALIZER_PrivateKey_TO_DER_PQ;
|
const char *serprop = OSSL_SERIALIZER_PrivateKey_TO_DER_PQ;
|
||||||
OSSL_SERIALIZER_CTX *ctx =
|
OSSL_SERIALIZER_CTX *ctx =
|
||||||
OSSL_SERIALIZER_CTX_new_by_EVP_PKEY(a, serprop);
|
OSSL_SERIALIZER_CTX_new_by_EVP_PKEY(a, serprop);
|
||||||
|
|
|
||||||
|
|
@ -200,9 +200,12 @@ int EVP_PKEY_derive_init(EVP_PKEY_CTX *ctx)
|
||||||
if (ctx->engine != NULL || ctx->keytype == NULL)
|
if (ctx->engine != NULL || ctx->keytype == NULL)
|
||||||
goto legacy;
|
goto legacy;
|
||||||
|
|
||||||
/* Ensure that the key is provided. If not, go legacy */
|
/*
|
||||||
|
* Ensure that the key is provided, either natively, or as a cached export.
|
||||||
|
* If not, go legacy
|
||||||
|
*/
|
||||||
tmp_keymgmt = ctx->keymgmt;
|
tmp_keymgmt = ctx->keymgmt;
|
||||||
provkey = evp_pkey_make_provided(ctx->pkey, ctx->libctx,
|
provkey = evp_pkey_export_to_provider(ctx->pkey, ctx->libctx,
|
||||||
&tmp_keymgmt, ctx->propquery);
|
&tmp_keymgmt, ctx->propquery);
|
||||||
if (provkey == NULL)
|
if (provkey == NULL)
|
||||||
goto legacy;
|
goto legacy;
|
||||||
|
|
@ -309,7 +312,7 @@ int EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *ctx, EVP_PKEY *peer)
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
provkey = evp_pkey_make_provided(peer, ctx->libctx, &ctx->keymgmt,
|
provkey = evp_pkey_export_to_provider(peer, ctx->libctx, &ctx->keymgmt,
|
||||||
ctx->propquery);
|
ctx->propquery);
|
||||||
/*
|
/*
|
||||||
* If making the key provided wasn't possible, legacy may be able to pick
|
* If making the key provided wasn't possible, legacy may be able to pick
|
||||||
|
|
|
||||||
|
|
@ -47,31 +47,48 @@ void *evp_keymgmt_util_export_to_provider(EVP_PKEY *pk, EVP_KEYMGMT *keymgmt)
|
||||||
{
|
{
|
||||||
void *keydata = NULL;
|
void *keydata = NULL;
|
||||||
struct import_data_st import_data;
|
struct import_data_st import_data;
|
||||||
size_t i, j;
|
size_t i = 0;
|
||||||
|
|
||||||
/* Export to where? */
|
/* Export to where? */
|
||||||
if (keymgmt == NULL)
|
if (keymgmt == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* If we have an unassigned key, give up */
|
/* If we have an unassigned key, give up */
|
||||||
if (pk->pkeys[0].keymgmt == NULL)
|
if (pk->keymgmt == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* If |keymgmt| matches the "origin" |keymgmt|, no more to do */
|
||||||
|
if (pk->keymgmt == keymgmt)
|
||||||
|
return pk->keydata;
|
||||||
|
|
||||||
|
/* If this key is already exported to |keymgmt|, no more to do */
|
||||||
|
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;
|
||||||
|
|
||||||
|
/* If the "origin" |keymgmt| doesn't support exporting, give up */
|
||||||
|
/*
|
||||||
|
* TODO(3.0) consider an evp_keymgmt_export() return value that indicates
|
||||||
|
* that the method is unsupported.
|
||||||
|
*/
|
||||||
|
if (pk->keymgmt->export == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Check that we have found an empty slot in the export cache */
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
if (!ossl_assert(i < OSSL_NELEM(pk->operation_cache)))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/*
|
|
||||||
* See if we have exported to this provider already.
|
|
||||||
* If we have, return immediately.
|
|
||||||
*/
|
|
||||||
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
|
* Make sure that the type of the keymgmt to export to matches the type
|
||||||
* of already cached keymgmt
|
* of the "origin"
|
||||||
*/
|
*/
|
||||||
if (!ossl_assert(match_type(pk->pkeys[0].keymgmt, keymgmt)))
|
if (!ossl_assert(match_type(pk->keymgmt, keymgmt)))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* Create space to import data into */
|
/* Create space to import data into */
|
||||||
|
|
@ -89,93 +106,76 @@ void *evp_keymgmt_util_export_to_provider(EVP_PKEY *pk, EVP_KEYMGMT *keymgmt)
|
||||||
import_data.keymgmt = keymgmt;
|
import_data.keymgmt = keymgmt;
|
||||||
import_data.selection = OSSL_KEYMGMT_SELECT_ALL;
|
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;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TODO(3.0) consider an evp_keymgmt_export() return value that
|
* The export function calls the callback (try_import), which does the
|
||||||
* indicates that the method is unsupported.
|
* import for us. If successful, we're done.
|
||||||
*/
|
*/
|
||||||
if (exp_keymgmt->export == NULL)
|
if (!evp_keymgmt_export(pk->keymgmt, pk->keydata, OSSL_KEYMGMT_SELECT_ALL,
|
||||||
continue;
|
&try_import, &import_data)) {
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 */
|
/* If there was an error, bail out */
|
||||||
evp_keymgmt_freedata(keymgmt, keydata);
|
evp_keymgmt_freedata(keymgmt, keydata);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* Add the new export to the operation cache */
|
||||||
* TODO(3.0) Right now, we assume we have ample space. We will
|
if (!evp_keymgmt_util_cache_keydata(pk, i, keymgmt, keydata)) {
|
||||||
* have to think about a cache aging scheme, though, if |i| indexes
|
evp_keymgmt_freedata(keymgmt, keydata);
|
||||||
* outside the array.
|
|
||||||
*/
|
|
||||||
if (!ossl_assert(i < OSSL_NELEM(pk->pkeys)))
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
evp_keymgmt_util_cache_pkey(pk, i, keymgmt, keydata);
|
|
||||||
|
|
||||||
return keydata;
|
return keydata;
|
||||||
}
|
}
|
||||||
|
|
||||||
void evp_keymgmt_util_clear_pkey_cache(EVP_PKEY *pk)
|
void evp_keymgmt_util_clear_operation_cache(EVP_PKEY *pk)
|
||||||
{
|
{
|
||||||
size_t i, end = OSSL_NELEM(pk->pkeys);
|
size_t i, end = OSSL_NELEM(pk->operation_cache);
|
||||||
|
|
||||||
if (pk != NULL) {
|
if (pk != NULL) {
|
||||||
for (i = 0; i < end && pk->pkeys[i].keymgmt != NULL; i++) {
|
for (i = 0; i < end && pk->operation_cache[i].keymgmt != NULL; i++) {
|
||||||
EVP_KEYMGMT *keymgmt = pk->pkeys[i].keymgmt;
|
EVP_KEYMGMT *keymgmt = pk->operation_cache[i].keymgmt;
|
||||||
void *keydata = pk->pkeys[i].keydata;
|
void *keydata = pk->operation_cache[i].keydata;
|
||||||
|
|
||||||
pk->pkeys[i].keymgmt = NULL;
|
pk->operation_cache[i].keymgmt = NULL;
|
||||||
pk->pkeys[i].keydata = NULL;
|
pk->operation_cache[i].keydata = NULL;
|
||||||
evp_keymgmt_freedata(keymgmt, keydata);
|
evp_keymgmt_freedata(keymgmt, keydata);
|
||||||
EVP_KEYMGMT_free(keymgmt);
|
EVP_KEYMGMT_free(keymgmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
pk->cache.size = 0;
|
|
||||||
pk->cache.bits = 0;
|
|
||||||
pk->cache.security_bits = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t evp_keymgmt_util_find_pkey_cache_index(EVP_PKEY *pk,
|
size_t evp_keymgmt_util_find_operation_cache_index(EVP_PKEY *pk,
|
||||||
EVP_KEYMGMT *keymgmt)
|
EVP_KEYMGMT *keymgmt)
|
||||||
{
|
{
|
||||||
size_t i, end = OSSL_NELEM(pk->pkeys);
|
size_t i, end = OSSL_NELEM(pk->operation_cache);
|
||||||
|
|
||||||
for (i = 0; i < end && pk->pkeys[i].keymgmt != NULL; i++) {
|
for (i = 0; i < end && pk->operation_cache[i].keymgmt != NULL; i++) {
|
||||||
if (keymgmt == pk->pkeys[i].keymgmt)
|
if (keymgmt == pk->operation_cache[i].keymgmt)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
void evp_keymgmt_util_cache_pkey(EVP_PKEY *pk, size_t index,
|
int evp_keymgmt_util_cache_keydata(EVP_PKEY *pk, size_t index,
|
||||||
EVP_KEYMGMT *keymgmt, void *keydata)
|
EVP_KEYMGMT *keymgmt, void *keydata)
|
||||||
{
|
{
|
||||||
if (keydata != NULL) {
|
if (keydata != NULL) {
|
||||||
EVP_KEYMGMT_up_ref(keymgmt);
|
if (!EVP_KEYMGMT_up_ref(keymgmt))
|
||||||
pk->pkeys[index].keydata = keydata;
|
return 0;
|
||||||
pk->pkeys[index].keymgmt = keymgmt;
|
pk->operation_cache[index].keydata = keydata;
|
||||||
|
pk->operation_cache[index].keymgmt = keymgmt;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void evp_keymgmt_util_cache_keyinfo(EVP_PKEY *pk)
|
||||||
|
{
|
||||||
/*
|
/*
|
||||||
* Cache information about the key object. Only needed for the
|
* Cache information about the provider "origin" key.
|
||||||
* "original" provider side key.
|
|
||||||
*
|
*
|
||||||
* This services functions like EVP_PKEY_size, EVP_PKEY_bits, etc
|
* This services functions like EVP_PKEY_size, EVP_PKEY_bits, etc
|
||||||
*/
|
*/
|
||||||
if (index == 0) {
|
if (pk->keymgmt != NULL) {
|
||||||
int bits = 0;
|
int bits = 0;
|
||||||
int security_bits = 0;
|
int security_bits = 0;
|
||||||
int size = 0;
|
int size = 0;
|
||||||
|
|
@ -184,17 +184,15 @@ void evp_keymgmt_util_cache_pkey(EVP_PKEY *pk, size_t index,
|
||||||
params[0] = OSSL_PARAM_construct_int(OSSL_PKEY_PARAM_BITS, &bits);
|
params[0] = OSSL_PARAM_construct_int(OSSL_PKEY_PARAM_BITS, &bits);
|
||||||
params[1] = OSSL_PARAM_construct_int(OSSL_PKEY_PARAM_SECURITY_BITS,
|
params[1] = OSSL_PARAM_construct_int(OSSL_PKEY_PARAM_SECURITY_BITS,
|
||||||
&security_bits);
|
&security_bits);
|
||||||
params[2] = OSSL_PARAM_construct_int(OSSL_PKEY_PARAM_MAX_SIZE,
|
params[2] = OSSL_PARAM_construct_int(OSSL_PKEY_PARAM_MAX_SIZE, &size);
|
||||||
&size);
|
|
||||||
params[3] = OSSL_PARAM_construct_end();
|
params[3] = OSSL_PARAM_construct_end();
|
||||||
if (evp_keymgmt_get_params(keymgmt, keydata, params)) {
|
if (evp_keymgmt_get_params(pk->keymgmt, pk->keydata, params)) {
|
||||||
pk->cache.size = size;
|
pk->cache.size = size;
|
||||||
pk->cache.bits = bits;
|
pk->cache.bits = bits;
|
||||||
pk->cache.security_bits = security_bits;
|
pk->cache.security_bits = security_bits;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void *evp_keymgmt_util_fromdata(EVP_PKEY *target, EVP_KEYMGMT *keymgmt,
|
void *evp_keymgmt_util_fromdata(EVP_PKEY *target, EVP_KEYMGMT *keymgmt,
|
||||||
int selection, const OSSL_PARAM params[])
|
int selection, const OSSL_PARAM params[])
|
||||||
|
|
@ -202,14 +200,16 @@ void *evp_keymgmt_util_fromdata(EVP_PKEY *target, EVP_KEYMGMT *keymgmt,
|
||||||
void *keydata = evp_keymgmt_newdata(keymgmt);
|
void *keydata = evp_keymgmt_newdata(keymgmt);
|
||||||
|
|
||||||
if (keydata != NULL) {
|
if (keydata != NULL) {
|
||||||
if (!evp_keymgmt_import(keymgmt, keydata, selection, params)) {
|
if (!evp_keymgmt_import(keymgmt, keydata, selection, params)
|
||||||
|
|| !EVP_KEYMGMT_up_ref(keymgmt)) {
|
||||||
evp_keymgmt_freedata(keymgmt, keydata);
|
evp_keymgmt_freedata(keymgmt, keydata);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
evp_keymgmt_util_clear_operation_cache(target);
|
||||||
evp_keymgmt_util_clear_pkey_cache(target);
|
target->keymgmt = keymgmt;
|
||||||
evp_keymgmt_util_cache_pkey(target, 0, keymgmt, keydata);
|
target->keydata = keydata;
|
||||||
|
evp_keymgmt_util_cache_keyinfo(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
return keydata;
|
return keydata;
|
||||||
|
|
|
||||||
|
|
@ -64,9 +64,12 @@ static int do_sigver_init(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
|
||||||
if (locpctx->keytype == NULL)
|
if (locpctx->keytype == NULL)
|
||||||
goto legacy;
|
goto legacy;
|
||||||
|
|
||||||
/* Ensure that the key is provided. If not, go legacy */
|
/*
|
||||||
|
* Ensure that the key is provided, either natively, or as a cached export.
|
||||||
|
* If not, go legacy
|
||||||
|
*/
|
||||||
tmp_keymgmt = locpctx->keymgmt;
|
tmp_keymgmt = locpctx->keymgmt;
|
||||||
provkey = evp_pkey_make_provided(locpctx->pkey, locpctx->libctx,
|
provkey = evp_pkey_export_to_provider(locpctx->pkey, locpctx->libctx,
|
||||||
&tmp_keymgmt, locpctx->propquery);
|
&tmp_keymgmt, locpctx->propquery);
|
||||||
if (provkey == NULL)
|
if (provkey == NULL)
|
||||||
goto legacy;
|
goto legacy;
|
||||||
|
|
|
||||||
|
|
@ -713,7 +713,7 @@ int EVP_PKEY_print_params(BIO *out, const EVP_PKEY *pkey,
|
||||||
static int legacy_asn1_ctrl_to_param(EVP_PKEY *pkey, int op,
|
static int legacy_asn1_ctrl_to_param(EVP_PKEY *pkey, int op,
|
||||||
int arg1, void *arg2)
|
int arg1, void *arg2)
|
||||||
{
|
{
|
||||||
if (pkey->pkeys[0].keymgmt == NULL)
|
if (pkey->keymgmt == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case ASN1_PKEY_CTRL_DEFAULT_MD_NID:
|
case ASN1_PKEY_CTRL_DEFAULT_MD_NID:
|
||||||
|
|
@ -768,9 +768,7 @@ int EVP_PKEY_get_default_digest_name(EVP_PKEY *pkey,
|
||||||
mdmandatory,
|
mdmandatory,
|
||||||
sizeof(mdmandatory));
|
sizeof(mdmandatory));
|
||||||
params[2] = OSSL_PARAM_construct_end();
|
params[2] = OSSL_PARAM_construct_end();
|
||||||
if (!evp_keymgmt_get_params(pkey->pkeys[0].keymgmt,
|
if (!evp_keymgmt_get_params(pkey->keymgmt, pkey->keydata, params))
|
||||||
pkey->pkeys[0].keydata,
|
|
||||||
params))
|
|
||||||
return 0;
|
return 0;
|
||||||
if (mdmandatory[0] != '\0') {
|
if (mdmandatory[0] != '\0') {
|
||||||
OPENSSL_strlcpy(mdname, mdmandatory, mdname_sz);
|
OPENSSL_strlcpy(mdname, mdmandatory, mdname_sz);
|
||||||
|
|
@ -872,12 +870,18 @@ static void evp_pkey_free_it(EVP_PKEY *x)
|
||||||
{
|
{
|
||||||
/* internal function; x is never NULL */
|
/* internal function; x is never NULL */
|
||||||
|
|
||||||
evp_keymgmt_util_clear_pkey_cache(x);
|
evp_keymgmt_util_clear_operation_cache(x);
|
||||||
|
|
||||||
if (x->ameth && x->ameth->pkey_free) {
|
if (x->ameth && x->ameth->pkey_free) {
|
||||||
x->ameth->pkey_free(x);
|
x->ameth->pkey_free(x);
|
||||||
x->pkey.ptr = NULL;
|
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)
|
#if !defined(OPENSSL_NO_ENGINE) && !defined(FIPS_MODE)
|
||||||
ENGINE_finish(x->engine);
|
ENGINE_finish(x->engine);
|
||||||
x->engine = NULL;
|
x->engine = NULL;
|
||||||
|
|
@ -917,8 +921,9 @@ int EVP_PKEY_size(const EVP_PKEY *pkey)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *evp_pkey_make_provided(EVP_PKEY *pk, OPENSSL_CTX *libctx,
|
void *evp_pkey_export_to_provider(EVP_PKEY *pk, OPENSSL_CTX *libctx,
|
||||||
EVP_KEYMGMT **keymgmt, const char *propquery)
|
EVP_KEYMGMT **keymgmt,
|
||||||
|
const char *propquery)
|
||||||
{
|
{
|
||||||
EVP_KEYMGMT *allocated_keymgmt = NULL;
|
EVP_KEYMGMT *allocated_keymgmt = NULL;
|
||||||
EVP_KEYMGMT *tmp_keymgmt = NULL;
|
EVP_KEYMGMT *tmp_keymgmt = NULL;
|
||||||
|
|
@ -927,59 +932,22 @@ void *evp_pkey_make_provided(EVP_PKEY *pk, OPENSSL_CTX *libctx,
|
||||||
if (pk == NULL)
|
if (pk == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
#ifndef FIPS_MODE
|
||||||
|
if (pk->pkey.ptr != NULL) {
|
||||||
|
/*
|
||||||
|
* If the legacy key doesn't have an dirty counter or export function,
|
||||||
|
* give up
|
||||||
|
*/
|
||||||
|
if (pk->ameth->dirty_cnt == NULL || pk->ameth->export_to == NULL)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (keymgmt != NULL) {
|
if (keymgmt != NULL) {
|
||||||
tmp_keymgmt = *keymgmt;
|
tmp_keymgmt = *keymgmt;
|
||||||
*keymgmt = NULL;
|
*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 no keymgmt was given or found, get a default keymgmt */
|
||||||
if (tmp_keymgmt == NULL) {
|
if (tmp_keymgmt == NULL) {
|
||||||
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_pkey(libctx, pk, propquery);
|
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_pkey(libctx, pk, propquery);
|
||||||
|
|
@ -990,35 +958,41 @@ void *evp_pkey_make_provided(EVP_PKEY *pk, OPENSSL_CTX *libctx,
|
||||||
EVP_PKEY_CTX_free(ctx);
|
EVP_PKEY_CTX_free(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If there's still no keymgmt to be had, give up */
|
||||||
if (tmp_keymgmt == NULL)
|
if (tmp_keymgmt == NULL)
|
||||||
goto end;
|
goto end;
|
||||||
|
|
||||||
#ifndef FIPS_MODE
|
#ifndef FIPS_MODE
|
||||||
if (pk->pkey.ptr != NULL) {
|
if (pk->pkey.ptr != NULL) {
|
||||||
size_t i;
|
size_t i = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Find our keymgmt in the cache. If it's present, it means that
|
* If the legacy "origin" hasn't changed since last time, we try
|
||||||
* export has already been done. We take token copies of the
|
* to find our keymgmt in the operation cache. If it has changed,
|
||||||
* cached pointers, to have token success values to return.
|
* |i| remains zero, and we will clear the cache further down.
|
||||||
*
|
|
||||||
* 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 (pk->ameth->dirty_cnt(pk) == pk->dirty_cnt_copy) {
|
||||||
if (!ossl_assert(i < OSSL_NELEM(pk->pkeys)))
|
i = evp_keymgmt_util_find_operation_cache_index(pk, tmp_keymgmt);
|
||||||
goto end;
|
|
||||||
if (pk->pkeys[i].keymgmt != NULL) {
|
/*
|
||||||
keydata = pk->pkeys[i].keydata;
|
* If |tmp_keymgmt| is present in the operation cache, it means
|
||||||
|
* that export doesn't need to be redone. In that case, we take
|
||||||
|
* token copies of the cached pointers, to have token success
|
||||||
|
* values to return.
|
||||||
|
*/
|
||||||
|
if (i < OSSL_NELEM(pk->operation_cache)
|
||||||
|
&& pk->operation_cache[i].keymgmt != NULL) {
|
||||||
|
keydata = pk->operation_cache[i].keydata;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we still don't have a keymgmt at this point, or the legacy
|
* TODO(3.0) Right now, we assume we have ample space. We will have
|
||||||
* key doesn't have an export function, just bail out.
|
* to think about a cache aging scheme, though, if |i| indexes outside
|
||||||
|
* the array.
|
||||||
*/
|
*/
|
||||||
if (pk->ameth->export_to == NULL)
|
if (!ossl_assert(i < OSSL_NELEM(pk->operation_cache)))
|
||||||
goto end;
|
goto end;
|
||||||
|
|
||||||
/* Make sure that the keymgmt key type matches the legacy NID */
|
/* Make sure that the keymgmt key type matches the legacy NID */
|
||||||
|
|
@ -1034,7 +1008,27 @@ void *evp_pkey_make_provided(EVP_PKEY *pk, OPENSSL_CTX *libctx,
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
evp_keymgmt_util_cache_pkey(pk, i, tmp_keymgmt, keydata);
|
/*
|
||||||
|
* If the dirty counter changed since last time, then clear the
|
||||||
|
* operation cache. In that case, we know that |i| is zero. Just
|
||||||
|
* in case this is a re-export, we increment then decrement the
|
||||||
|
* keymgmt reference counter.
|
||||||
|
*/
|
||||||
|
if (!EVP_KEYMGMT_up_ref(tmp_keymgmt)) { /* refcnt++ */
|
||||||
|
evp_keymgmt_freedata(tmp_keymgmt, keydata);
|
||||||
|
keydata = NULL;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
if (pk->ameth->dirty_cnt(pk) != pk->dirty_cnt_copy)
|
||||||
|
evp_keymgmt_util_clear_operation_cache(pk);
|
||||||
|
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)) {
|
||||||
|
evp_keymgmt_freedata(tmp_keymgmt, keydata);
|
||||||
|
keydata = NULL;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
/* Synchronize the dirty count */
|
/* Synchronize the dirty count */
|
||||||
pk->dirty_cnt_copy = pk->ameth->dirty_cnt(pk);
|
pk->dirty_cnt_copy = pk->ameth->dirty_cnt(pk);
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,8 @@ int EVP_PKEY_public_check(EVP_PKEY_CTX *ctx)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
keymgmt = pkey->pkeys[0].keymgmt;
|
keymgmt = pkey->keymgmt;
|
||||||
key = pkey->pkeys[0].keydata;
|
key = pkey->keydata;
|
||||||
|
|
||||||
if (key != NULL && keymgmt != NULL)
|
if (key != NULL && keymgmt != NULL)
|
||||||
return evp_keymgmt_validate(keymgmt, key,
|
return evp_keymgmt_validate(keymgmt, key,
|
||||||
|
|
@ -61,8 +61,8 @@ int EVP_PKEY_param_check(EVP_PKEY_CTX *ctx)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
keymgmt = pkey->pkeys[0].keymgmt;
|
keymgmt = pkey->keymgmt;
|
||||||
key = pkey->pkeys[0].keydata;
|
key = pkey->keydata;
|
||||||
|
|
||||||
if (key != NULL && keymgmt != NULL)
|
if (key != NULL && keymgmt != NULL)
|
||||||
return evp_keymgmt_validate(keymgmt, key,
|
return evp_keymgmt_validate(keymgmt, key,
|
||||||
|
|
@ -94,8 +94,8 @@ int EVP_PKEY_private_check(EVP_PKEY_CTX *ctx)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
keymgmt = pkey->pkeys[0].keymgmt;
|
keymgmt = pkey->keymgmt;
|
||||||
key = pkey->pkeys[0].keydata;
|
key = pkey->keydata;
|
||||||
|
|
||||||
if (key != NULL && keymgmt != NULL)
|
if (key != NULL && keymgmt != NULL)
|
||||||
return evp_keymgmt_validate(keymgmt, key,
|
return evp_keymgmt_validate(keymgmt, key,
|
||||||
|
|
@ -115,8 +115,8 @@ int EVP_PKEY_pairwise_check(EVP_PKEY_CTX *ctx)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
keymgmt = pkey->pkeys[0].keymgmt;
|
keymgmt = pkey->keymgmt;
|
||||||
key = pkey->pkeys[0].keydata;
|
key = pkey->keydata;
|
||||||
|
|
||||||
if (key != NULL && keymgmt != NULL)
|
if (key != NULL && keymgmt != NULL)
|
||||||
return evp_keymgmt_validate(keymgmt, key, OSSL_KEYMGMT_SELECT_KEYPAIR);
|
return evp_keymgmt_validate(keymgmt, key, OSSL_KEYMGMT_SELECT_KEYPAIR);
|
||||||
|
|
@ -135,8 +135,8 @@ int EVP_PKEY_check(EVP_PKEY_CTX *ctx)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
keymgmt = pkey->pkeys[0].keymgmt;
|
keymgmt = pkey->keymgmt;
|
||||||
key = pkey->pkeys[0].keydata;
|
key = pkey->keydata;
|
||||||
|
|
||||||
if (key != NULL && keymgmt != NULL)
|
if (key != NULL && keymgmt != NULL)
|
||||||
return evp_keymgmt_validate(keymgmt, key, OSSL_KEYMGMT_SELECT_ALL);
|
return evp_keymgmt_validate(keymgmt, key, OSSL_KEYMGMT_SELECT_ALL);
|
||||||
|
|
|
||||||
|
|
@ -41,9 +41,12 @@ static int evp_pkey_asym_cipher_init(EVP_PKEY_CTX *ctx, int operation)
|
||||||
if (ctx->keytype == NULL || ctx->engine != NULL)
|
if (ctx->keytype == NULL || ctx->engine != NULL)
|
||||||
goto legacy;
|
goto legacy;
|
||||||
|
|
||||||
/* Ensure that the key is provided. If not, go legacy */
|
/*
|
||||||
|
* Ensure that the key is provided, either natively, or as a cached export.
|
||||||
|
* If not, go legacy
|
||||||
|
*/
|
||||||
tmp_keymgmt = ctx->keymgmt;
|
tmp_keymgmt = ctx->keymgmt;
|
||||||
provkey = evp_pkey_make_provided(ctx->pkey, ctx->libctx,
|
provkey = evp_pkey_export_to_provider(ctx->pkey, ctx->libctx,
|
||||||
&tmp_keymgmt, ctx->propquery);
|
&tmp_keymgmt, ctx->propquery);
|
||||||
if (provkey == NULL)
|
if (provkey == NULL)
|
||||||
goto legacy;
|
goto legacy;
|
||||||
|
|
|
||||||
|
|
@ -160,8 +160,7 @@ static EVP_PKEY_CTX *int_ctx_new(OPENSSL_CTX *libctx,
|
||||||
/* If we have an engine, something went wrong somewhere... */
|
/* If we have an engine, something went wrong somewhere... */
|
||||||
if (!ossl_assert(e == NULL))
|
if (!ossl_assert(e == NULL))
|
||||||
return NULL;
|
return NULL;
|
||||||
name = evp_first_name(pkey->pkeys[0].keymgmt->prov,
|
name = evp_first_name(pkey->keymgmt->prov, pkey->keymgmt->name_id);
|
||||||
pkey->pkeys[0].keymgmt->name_id);
|
|
||||||
/*
|
/*
|
||||||
* TODO: I wonder if the EVP_PKEY should have the name and propquery
|
* TODO: I wonder if the EVP_PKEY should have the name and propquery
|
||||||
* that were used when building it.... /RL
|
* that were used when building it.... /RL
|
||||||
|
|
|
||||||
|
|
@ -342,9 +342,12 @@ static int evp_pkey_signature_init(EVP_PKEY_CTX *ctx, int operation)
|
||||||
if (ctx->keytype == NULL)
|
if (ctx->keytype == NULL)
|
||||||
goto legacy;
|
goto legacy;
|
||||||
|
|
||||||
/* Ensure that the key is provided. If not, go legacy */
|
/*
|
||||||
|
* Ensure that the key is provided, either natively, or as a cached export.
|
||||||
|
* If not, go legacy
|
||||||
|
*/
|
||||||
tmp_keymgmt = ctx->keymgmt;
|
tmp_keymgmt = ctx->keymgmt;
|
||||||
provkey = evp_pkey_make_provided(ctx->pkey, ctx->libctx,
|
provkey = evp_pkey_export_to_provider(ctx->pkey, ctx->libctx,
|
||||||
&tmp_keymgmt, ctx->propquery);
|
&tmp_keymgmt, ctx->propquery);
|
||||||
if (provkey == NULL)
|
if (provkey == NULL)
|
||||||
goto legacy;
|
goto legacy;
|
||||||
|
|
|
||||||
|
|
@ -267,8 +267,8 @@ static int serializer_write_cb(const OSSL_PARAM params[], void *arg)
|
||||||
static int serializer_EVP_PKEY_to_bio(OSSL_SERIALIZER_CTX *ctx, BIO *out)
|
static int serializer_EVP_PKEY_to_bio(OSSL_SERIALIZER_CTX *ctx, BIO *out)
|
||||||
{
|
{
|
||||||
const EVP_PKEY *pkey = ctx->object;
|
const EVP_PKEY *pkey = ctx->object;
|
||||||
void *keydata = pkey->pkeys[0].keydata;
|
void *keydata = pkey->keydata;
|
||||||
EVP_KEYMGMT *keymgmt = pkey->pkeys[0].keymgmt;
|
EVP_KEYMGMT *keymgmt = pkey->keymgmt;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* OSSL_SERIALIZER_CTX_new() creates a context, even when the
|
* OSSL_SERIALIZER_CTX_new() creates a context, even when the
|
||||||
|
|
@ -306,7 +306,7 @@ OSSL_SERIALIZER_CTX *OSSL_SERIALIZER_CTX_new_by_EVP_PKEY(const EVP_PKEY *pkey,
|
||||||
{
|
{
|
||||||
OSSL_SERIALIZER_CTX *ctx = NULL;
|
OSSL_SERIALIZER_CTX *ctx = NULL;
|
||||||
OSSL_SERIALIZER *ser = NULL;
|
OSSL_SERIALIZER *ser = NULL;
|
||||||
EVP_KEYMGMT *keymgmt = pkey->pkeys[0].keymgmt;
|
EVP_KEYMGMT *keymgmt = pkey->keymgmt;
|
||||||
int selection = OSSL_KEYMGMT_SELECT_ALL;
|
int selection = OSSL_KEYMGMT_SELECT_ALL;
|
||||||
|
|
||||||
if (!ossl_assert(pkey != NULL && propquery != NULL)) {
|
if (!ossl_assert(pkey != NULL && propquery != NULL)) {
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ int X509_PUBKEY_set(X509_PUBKEY **x, EVP_PKEY *pkey)
|
||||||
X509err(X509_F_X509_PUBKEY_SET, X509_R_METHOD_NOT_SUPPORTED);
|
X509err(X509_F_X509_PUBKEY_SET, X509_R_METHOD_NOT_SUPPORTED);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
} else if (pkey->pkeys[0].keymgmt != NULL) {
|
} else if (pkey->keymgmt != NULL) {
|
||||||
BIO *bmem = BIO_new(BIO_s_mem());
|
BIO *bmem = BIO_new(BIO_s_mem());
|
||||||
const char *serprop = OSSL_SERIALIZER_PUBKEY_TO_DER_PQ;
|
const char *serprop = OSSL_SERIALIZER_PUBKEY_TO_DER_PQ;
|
||||||
OSSL_SERIALIZER_CTX *sctx =
|
OSSL_SERIALIZER_CTX *sctx =
|
||||||
|
|
@ -270,7 +270,7 @@ int i2d_PUBKEY(const EVP_PKEY *a, unsigned char **pp)
|
||||||
xpk->pkey = NULL;
|
xpk->pkey = NULL;
|
||||||
}
|
}
|
||||||
X509_PUBKEY_free(xpk);
|
X509_PUBKEY_free(xpk);
|
||||||
} else if (a->pkeys[0].keymgmt != NULL) {
|
} else if (a->keymgmt != NULL) {
|
||||||
const char *serprop = OSSL_SERIALIZER_PUBKEY_TO_DER_PQ;
|
const char *serprop = OSSL_SERIALIZER_PUBKEY_TO_DER_PQ;
|
||||||
OSSL_SERIALIZER_CTX *ctx =
|
OSSL_SERIALIZER_CTX *ctx =
|
||||||
OSSL_SERIALIZER_CTX_new_by_EVP_PKEY(a, serprop);
|
OSSL_SERIALIZER_CTX_new_by_EVP_PKEY(a, serprop);
|
||||||
|
|
|
||||||
|
|
@ -504,6 +504,11 @@ struct evp_pkey_st {
|
||||||
/* == Legacy attributes == */
|
/* == Legacy attributes == */
|
||||||
int type;
|
int type;
|
||||||
int save_type;
|
int save_type;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Legacy key "origin" is composed of a pointer to an EVP_PKEY_ASN1_METHOD,
|
||||||
|
* a pointer to a low level key and possibly a pointer to an engine.
|
||||||
|
*/
|
||||||
const EVP_PKEY_ASN1_METHOD *ameth;
|
const EVP_PKEY_ASN1_METHOD *ameth;
|
||||||
ENGINE *engine;
|
ENGINE *engine;
|
||||||
ENGINE *pmeth_engine; /* If not NULL public key ENGINE to use */
|
ENGINE *pmeth_engine; /* If not NULL public key ENGINE to use */
|
||||||
|
|
@ -531,20 +536,41 @@ struct evp_pkey_st {
|
||||||
int save_parameters;
|
int save_parameters;
|
||||||
|
|
||||||
/* == Provider attributes == */
|
/* == Provider attributes == */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* To support transparent export/import between providers that support
|
* Provider keydata "origin" is composed of a pointer to an EVP_KEYMGMT
|
||||||
* the methods for it, and still not having to do the export/import
|
* and a pointer to the provider side key data. This is never used at
|
||||||
* every time a key object is changed, we maintain a cache of imported
|
* the same time as the legacy key data above.
|
||||||
* key objects, indexed by keymgmt address. pkeys[0] is *always* the
|
*/
|
||||||
* "original" data unless we have a legacy key attached.
|
EVP_KEYMGMT *keymgmt;
|
||||||
|
void *keydata;
|
||||||
|
/*
|
||||||
|
* If any libcrypto code does anything that may modify the keydata
|
||||||
|
* contents, this dirty counter must be incremented.
|
||||||
|
*/
|
||||||
|
size_t dirty_cnt;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* To support transparent execution of operation in backends other
|
||||||
|
* than the "origin" key, we support transparent export/import to
|
||||||
|
* those providers, and maintain a cache of the imported keydata,
|
||||||
|
* so we don't need to redo the export/import every time we perform
|
||||||
|
* the same operation in that same provider.
|
||||||
|
* This requires that the "origin" backend (whether it's a legacy or a
|
||||||
|
* provider "origin") implements exports, and that the target provider
|
||||||
|
* has an EVP_KEYMGMT that implements import.
|
||||||
|
*
|
||||||
|
* The cache limit is set at 10 different providers using the same
|
||||||
|
* "origin". It's probably over the top, but is preferable to too
|
||||||
|
* few.
|
||||||
*/
|
*/
|
||||||
struct {
|
struct {
|
||||||
EVP_KEYMGMT *keymgmt;
|
EVP_KEYMGMT *keymgmt;
|
||||||
void *keydata;
|
void *keydata;
|
||||||
} pkeys[10];
|
} operation_cache[10];
|
||||||
/*
|
/*
|
||||||
* If there is a legacy key assigned to this structure, we keep
|
* We keep a copy of that "origin"'s dirty count, so we know if the
|
||||||
* a copy of that key's dirty count.
|
* operation cache needs flushing.
|
||||||
*/
|
*/
|
||||||
size_t dirty_cnt_copy;
|
size_t dirty_cnt_copy;
|
||||||
|
|
||||||
|
|
@ -574,18 +600,20 @@ void openssl_add_all_ciphers_int(void);
|
||||||
void openssl_add_all_digests_int(void);
|
void openssl_add_all_digests_int(void);
|
||||||
void evp_cleanup_int(void);
|
void evp_cleanup_int(void);
|
||||||
void evp_app_cleanup_int(void);
|
void evp_app_cleanup_int(void);
|
||||||
void *evp_pkey_make_provided(EVP_PKEY *pk, OPENSSL_CTX *libctx,
|
void *evp_pkey_export_to_provider(EVP_PKEY *pk, OPENSSL_CTX *libctx,
|
||||||
EVP_KEYMGMT **keymgmt, const char *propquery);
|
EVP_KEYMGMT **keymgmt,
|
||||||
|
const char *propquery);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* KEYMGMT utility functions
|
* KEYMGMT utility functions
|
||||||
*/
|
*/
|
||||||
void *evp_keymgmt_util_export_to_provider(EVP_PKEY *pk, EVP_KEYMGMT *keymgmt);
|
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,
|
size_t evp_keymgmt_util_find_operation_cache_index(EVP_PKEY *pk,
|
||||||
EVP_KEYMGMT *keymgmt);
|
EVP_KEYMGMT *keymgmt);
|
||||||
void evp_keymgmt_util_clear_pkey_cache(EVP_PKEY *pk);
|
void evp_keymgmt_util_clear_operation_cache(EVP_PKEY *pk);
|
||||||
void evp_keymgmt_util_cache_pkey(EVP_PKEY *pk, size_t index,
|
int evp_keymgmt_util_cache_keydata(EVP_PKEY *pk, size_t index,
|
||||||
EVP_KEYMGMT *keymgmt, void *keydata);
|
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,
|
void *evp_keymgmt_util_fromdata(EVP_PKEY *target, EVP_KEYMGMT *keymgmt,
|
||||||
int selection, const OSSL_PARAM params[]);
|
int selection, const OSSL_PARAM params[]);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue