EVP: Add support for copying provided EVP_PKEYs
authorRichard Levitte <levitte@openssl.org>
Wed, 5 Feb 2020 15:30:21 +0000 (16:30 +0100)
committerRichard Levitte <levitte@openssl.org>
Mon, 2 Mar 2020 02:27:03 +0000 (03:27 +0100)
This adds evp_keymgmt_util_copy() and affects EVP_PKEY_copy_parameters()

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 6bbf28aa34d50ab12f2fbd0eed5aabd2778dadfc..68ccdbb8ee90d21b5f661be1cce7178945341589 100644 (file)
@@ -293,3 +293,60 @@ int evp_keymgmt_util_match(EVP_PKEY *pk1, EVP_PKEY *pk2, int selection)
 
     return evp_keymgmt_match(keymgmt1, keydata1, keydata2, selection);
 }
+
+int evp_keymgmt_util_copy(EVP_PKEY *to, EVP_PKEY *from, int selection)
+{
+    /* Save copies of pointers we want to play with without affecting |to| */
+    EVP_KEYMGMT *to_keymgmt = to->keymgmt;
+    void *to_keydata = to->keydata, *alloc_keydata = NULL;
+
+    /* An unassigned key can't be copied */
+    if (from == NULL || from->keymgmt == NULL)
+        return 0;
+
+    /* If |from| doesn't support copying, we fail */
+    if (from->keymgmt->copy == NULL)
+        return 0;
+
+    /* If |to| doesn't have a provider side "origin" yet, create one */
+    if (to_keymgmt == NULL) {
+        to_keydata = alloc_keydata = evp_keymgmt_newdata(from->keymgmt);
+        if (to_keydata == NULL)
+            return 0;
+        to_keymgmt = from->keymgmt;
+    }
+
+    if (to_keymgmt == from->keymgmt) {
+        /* |to| and |from| have the same keymgmt, just copy and be done */
+        if (!evp_keymgmt_copy(to_keymgmt, to_keydata, from->keydata,
+                              selection))
+            return 0;
+    } else if (match_type(to_keymgmt, from->keymgmt)) {
+        struct import_data_st import_data;
+
+        import_data.keymgmt = to_keymgmt;
+        import_data.keydata = to_keydata;
+        import_data.selection = selection;
+
+        if (!evp_keymgmt_export(from->keymgmt, from->keydata, selection,
+                                &try_import, &import_data)) {
+            evp_keymgmt_freedata(to_keymgmt, alloc_keydata);
+            return 0;
+        }
+    } else {
+        ERR_raise(ERR_LIB_EVP, EVP_R_DIFFERENT_KEY_TYPES);
+        return 0;
+    }
+
+    if (to->keymgmt == NULL
+        && !EVP_KEYMGMT_up_ref(to_keymgmt)) {
+        evp_keymgmt_freedata(to_keymgmt, alloc_keydata);
+        return 0;
+    }
+    evp_keymgmt_util_clear_operation_cache(to);
+    to->keymgmt = to_keymgmt;
+    to->keydata = to_keydata;
+    evp_keymgmt_util_cache_keyinfo(to);
+
+    return 1;
+}
index a13a98be711d06bbfa133bf6036341cec6ea3cf8..586ffaf0417f14c69b131843380be11ec644e830 100644 (file)
@@ -86,12 +86,25 @@ int EVP_PKEY_save_parameters(EVP_PKEY *pkey, int mode)
 
 int EVP_PKEY_copy_parameters(EVP_PKEY *to, const EVP_PKEY *from)
 {
-    if (to->type == EVP_PKEY_NONE) {
-        if (EVP_PKEY_set_type(to, from->type) == 0)
-            return 0;
-    } else if (to->type != from->type) {
-        EVPerr(EVP_F_EVP_PKEY_COPY_PARAMETERS, EVP_R_DIFFERENT_KEY_TYPES);
-        goto err;
+    /*
+     * TODO: clean up legacy stuff from this function when legacy support
+     * is gone.
+     */
+
+    /*
+     * Only check that type match this early when both keys are legacy.
+     * If either of them is provided, we let evp_keymgmt_util_copy()
+     * do this check, after having exported either of them that isn't
+     * provided.
+     */
+    if (to->keymgmt == NULL && from->keymgmt == NULL) {
+        if (to->type == EVP_PKEY_NONE) {
+            if (EVP_PKEY_set_type(to, from->type) == 0)
+                return 0;
+        } else if (to->type != from->type) {
+            EVPerr(EVP_F_EVP_PKEY_COPY_PARAMETERS, EVP_R_DIFFERENT_KEY_TYPES);
+            goto err;
+        }
     }
 
     if (EVP_PKEY_missing_parameters(from)) {
@@ -106,7 +119,56 @@ int EVP_PKEY_copy_parameters(EVP_PKEY *to, const EVP_PKEY *from)
         return 0;
     }
 
-    if (from->ameth && from->ameth->param_copy)
+    /*
+     * If |from| is provided, we upgrade |to| to be provided as well.
+     * This drops the legacy key from |to|.
+     * evp_pkey_upgrade_to_provider() checks if |to| is already provided,
+     * we don't need to do that here.
+     *
+     * TODO(3.0) We should investigate if that's too aggressive and make
+     * this scenario unsupported instead.
+     */
+    if (from->keymgmt != NULL) {
+        EVP_KEYMGMT *tmp_keymgmt = from->keymgmt;
+
+        /*
+         * The returned pointer is known to be cached, so we don't have to
+         * save it.  However, if it's NULL, something went wrong and we can't
+         * copy.
+         */
+        if (evp_pkey_upgrade_to_provider(to, NULL,
+                                         &tmp_keymgmt, NULL) == NULL) {
+            ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR);
+            return 0;
+        }
+    }
+
+    /* For purely provided keys, we just call the keymgmt utility */
+    if (to->keymgmt != NULL && from->keymgmt != NULL)
+        return evp_keymgmt_util_copy(to, (EVP_PKEY *)from,
+                                     OSSL_KEYMGMT_SELECT_ALL_PARAMETERS);
+
+    /*
+     * If |to| is provided, we know that |from| is legacy at this point.
+     * Try exporting |from| to |to|'s keymgmt, then use evp_keymgmt_copy()
+     * to copy the appropriate data to |to|'s keydata.
+     */
+    if (to->keymgmt != NULL) {
+        EVP_KEYMGMT *to_keymgmt = to->keymgmt;
+        void *from_keydata =
+            evp_pkey_export_to_provider((EVP_PKEY *)from, NULL, &to_keymgmt,
+                                        NULL);
+
+        if (from_keydata == NULL) {
+            ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR);
+            return 0;
+        }
+        return evp_keymgmt_copy(to->keymgmt, to->keydata, from_keydata,
+                                OSSL_KEYMGMT_SELECT_ALL_PARAMETERS);
+    }
+
+    /* Both keys are legacy */
+    if (from->ameth != NULL && from->ameth->param_copy != NULL)
         return from->ameth->param_copy(to, from);
  err:
     return 0;
@@ -953,7 +1015,7 @@ int EVP_PKEY_up_ref(EVP_PKEY *pkey)
 static void evp_pkey_free_legacy(EVP_PKEY *x)
 {
     if (x->ameth != NULL) {
-        if (x->ameth->pkey_free)
+        if (x->ameth->pkey_free != NULL)
             x->ameth->pkey_free(x);
         x->pkey.ptr = NULL;
         x->ameth = NULL;
index 21f3f16053da82155268f105aa62e90d12a04f29..7da0258279815c3127aef437697921892bc4ea1b 100644 (file)
@@ -621,6 +621,7 @@ 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);
+int evp_keymgmt_util_copy(EVP_PKEY *to, EVP_PKEY *from, int selection);
 
 
 /*