From 1e9101c404b92b1fd32e8f0308ddc20742285135 Mon Sep 17 00:00:00 2001 From: Richard Levitte Date: Wed, 5 Feb 2020 12:55:43 +0100 Subject: [PATCH] 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 (Merged from https://github.com/openssl/openssl/pull/11158) --- crypto/evp/keymgmt_lib.c | 70 ++++++++++++++++++++++++++++++++ crypto/evp/p_lib.c | 86 ++++++++++++++++++++++++++++++++++++++-- include/crypto/evp.h | 1 + 3 files changed, 153 insertions(+), 4 deletions(-) diff --git a/crypto/evp/keymgmt_lib.c b/crypto/evp/keymgmt_lib.c index dfa53b849a..6bbf28aa34 100644 --- a/crypto/evp/keymgmt_lib.c +++ b/crypto/evp/keymgmt_lib.c @@ -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); +} diff --git a/crypto/evp/p_lib.c b/crypto/evp/p_lib.c index f2761823fc..a13a98be71 100644 --- a/crypto/evp/p_lib.c +++ b/crypto/evp/p_lib.c @@ -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); } diff --git a/include/crypto/evp.h b/include/crypto/evp.h index 74d0c4b345..f9c67d2a71 100644 --- a/include/crypto/evp.h +++ b/include/crypto/evp.h @@ -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); /* -- 2.25.1