EVP: Add support for comparing provided EVP_PKEYs
authorRichard Levitte <levitte@openssl.org>
Wed, 5 Feb 2020 11:55:43 +0000 (12:55 +0100)
committerRichard Levitte <levitte@openssl.org>
Mon, 2 Mar 2020 02:27:03 +0000 (03:27 +0100)
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)

crypto/evp/keymgmt_lib.c
crypto/evp/p_lib.c
include/crypto/evp.h

index dfa53b849aa6c3561663aa8f713c9c3cea90c03b..6bbf28aa34d50ab12f2fbd0eed5aabd2778dadfc 100644 (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);
+}
index f2761823fcf49039b7efa10857c2c6ae01ee7d3e..a13a98be711d06bbfa133bf6036341cec6ea3cf8 100644 (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);
     }
 
index 74d0c4b345fa06090b5a5b9d39064572488be3ea..f9c67d2a718902932cd516db59932b846db4154d 100644 (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);
 
 
 /*