EVP: Add support for comparing provided EVP_PKEYs

This adds evp_keymgmt_util_match() and affects EVP_PKEY_cmp() and
EVP_PKEY_cmp_parameters().

The word 'match' was used for the new routines because many associate
'cmp' with comparison functions that allows sorting, i.e. return -1, 0
or 1 depending on the order in which the two compared elements should
be sorted.  EVP_PKEY_cmp() and EVP_PKEY_cmp_parameters() don't quite
do that.

Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
(Merged from https://github.com/openssl/openssl/pull/11158)
This commit is contained in:
Richard Levitte 2020-02-05 12:55:43 +01:00
parent bee5d6cd3f
commit 1e9101c404
3 changed files with 153 additions and 4 deletions

View File

@ -223,3 +223,73 @@ int evp_keymgmt_util_has(EVP_PKEY *pk, int selection)
return evp_keymgmt_has(pk->keymgmt, pk->keydata, selection);
}
/*
* evp_keymgmt_util_match() doesn't just look at the provider side "origin",
* but also in the operation cache to see if there's any common keymgmt that
* supplies OP_keymgmt_match.
*
* evp_keymgmt_util_match() adheres to the return values that EVP_PKEY_cmp()
* and EVP_PKEY_cmp_parameters() return, i.e.:
*
* 1 same key
* 0 not same key
* -1 not same key type
* -2 unsupported operation
*/
int evp_keymgmt_util_match(EVP_PKEY *pk1, EVP_PKEY *pk2, int selection)
{
EVP_KEYMGMT *keymgmt1 = NULL, *keymgmt2 = NULL;
void *keydata1 = NULL, *keydata2 = NULL;
if (pk1 == NULL || pk2 == NULL) {
if (pk1 == NULL && pk2 == NULL)
return 1;
return 0;
}
keymgmt1 = pk1->keymgmt;
keydata1 = pk1->keydata;
keymgmt2 = pk2->keymgmt;
keydata2 = pk2->keydata;
if (keymgmt1 != keymgmt2) {
void *tmp_keydata = NULL;
/* Complex case, where the keymgmt differ */
if (keymgmt1 != NULL
&& keymgmt2 != NULL
&& !match_type(keymgmt1, keymgmt2)) {
ERR_raise(ERR_LIB_EVP, EVP_R_DIFFERENT_KEY_TYPES);
return -1; /* Not the same type */
}
/*
* The key types are determined to match, so we try cross export,
* but only to keymgmt's that supply a matching function.
*/
if (keymgmt2 != NULL
&& keymgmt2->match != NULL) {
tmp_keydata = evp_keymgmt_util_export_to_provider(pk1, keymgmt2);
if (tmp_keydata != NULL) {
keymgmt1 = keymgmt2;
keydata1 = tmp_keydata;
}
}
if (tmp_keydata == NULL
&& keymgmt1 != NULL
&& keymgmt1->match != NULL) {
tmp_keydata = evp_keymgmt_util_export_to_provider(pk2, keymgmt1);
if (tmp_keydata != NULL) {
keymgmt2 = keymgmt1;
keydata2 = tmp_keydata;
}
}
}
/* If we still don't have matching keymgmt implementations, we give up */
if (keymgmt1 != keymgmt2)
return -2;
return evp_keymgmt_match(keymgmt1, keydata1, keydata2, selection);
}

View File

@ -124,30 +124,108 @@ int EVP_PKEY_missing_parameters(const EVP_PKEY *pkey)
return 0;
}
/*
* This function is called for any mixture of keys except pure legacy pair.
* TODO When legacy keys are gone, we replace a call to this functions with
* a call to evp_keymgmt_util_match().
*/
static int evp_pkey_cmp_any(const EVP_PKEY *a, const EVP_PKEY *b,
int selection)
{
EVP_KEYMGMT *keymgmt1 = NULL, *keymgmt2 = NULL;
void *keydata1 = NULL, *keydata2 = NULL, *tmp_keydata = NULL;
/* If none of them are provided, this function shouldn't have been called */
if (!ossl_assert(a->keymgmt != NULL || b->keymgmt != NULL))
return -2;
/* For purely provided keys, we just call the keymgmt utility */
if (a->keymgmt != NULL && b->keymgmt != NULL)
return evp_keymgmt_util_match((EVP_PKEY *)a, (EVP_PKEY *)b, selection);
/*
* Here, we know that we have a mixture of legacy and provided keys.
* Try cross export and compare the resulting key data.
*/
keymgmt1 = a->keymgmt;
keydata1 = a->keydata;
keymgmt2 = b->keymgmt;
keydata2 = b->keydata;
if ((keymgmt1 == NULL
&& !EVP_KEYMGMT_is_a(keymgmt2, OBJ_nid2sn(a->type)))
|| (keymgmt2 == NULL
&& !EVP_KEYMGMT_is_a(keymgmt1, OBJ_nid2sn(b->type))))
return -1; /* not the same key type */
if (keymgmt2 != NULL && keymgmt2->match != NULL) {
tmp_keydata =
evp_pkey_export_to_provider((EVP_PKEY *)a, NULL, &keymgmt2, NULL);
if (tmp_keydata != NULL) {
keymgmt1 = keymgmt2;
keydata1 = tmp_keydata;
}
}
if (tmp_keydata == NULL && keymgmt1 != NULL && keymgmt1->match != NULL) {
tmp_keydata =
evp_pkey_export_to_provider((EVP_PKEY *)b, NULL, &keymgmt1, NULL);
if (tmp_keydata != NULL) {
keymgmt2 = keymgmt1;
keydata2 = tmp_keydata;
}
}
/* If we still don't have matching keymgmt implementations, we give up */
if (keymgmt1 != keymgmt2)
return -2;
return evp_keymgmt_match(keymgmt1, keydata1, keydata2, selection);
}
int EVP_PKEY_cmp_parameters(const EVP_PKEY *a, const EVP_PKEY *b)
{
/*
* TODO: clean up legacy stuff from this function when legacy support
* is gone.
*/
if (a->keymgmt != NULL || b->keymgmt != NULL)
return evp_pkey_cmp_any(a, b, OSSL_KEYMGMT_SELECT_ALL_PARAMETERS);
/* All legacy keys */
if (a->type != b->type)
return -1;
if (a->ameth && a->ameth->param_cmp)
if (a->ameth != NULL && a->ameth->param_cmp != NULL)
return a->ameth->param_cmp(a, b);
return -2;
}
int EVP_PKEY_cmp(const EVP_PKEY *a, const EVP_PKEY *b)
{
/*
* TODO: clean up legacy stuff from this function when legacy support
* is gone.
*/
if (a->keymgmt != NULL || b->keymgmt != NULL)
return evp_pkey_cmp_any(a, b,
OSSL_KEYMGMT_SELECT_ALL_PARAMETERS
| OSSL_KEYMGMT_SELECT_PUBLIC_KEY);
/* All legacy keys */
if (a->type != b->type)
return -1;
if (a->ameth) {
if (a->ameth != NULL) {
int ret;
/* Compare parameters if the algorithm has them */
if (a->ameth->param_cmp) {
if (a->ameth->param_cmp != NULL) {
ret = a->ameth->param_cmp(a, b);
if (ret <= 0)
return ret;
}
if (a->ameth->pub_cmp)
if (a->ameth->pub_cmp != NULL)
return a->ameth->pub_cmp(a, b);
}

View File

@ -620,6 +620,7 @@ 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[]);
int evp_keymgmt_util_has(EVP_PKEY *pk, int selection);
int evp_keymgmt_util_match(EVP_PKEY *pk1, EVP_PKEY *pk2, int selection);
/*