]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
EVP: Add evp_pkey_make_provided() and refactor around it
authorRichard Levitte <levitte@openssl.org>
Tue, 14 Jan 2020 13:11:47 +0000 (14:11 +0100)
committerRichard Levitte <levitte@openssl.org>
Sat, 18 Jan 2020 04:27:50 +0000 (05:27 +0100)
The code to ensure that an EVP_PKEY is exported to providers is
repeated all over the place, enough that copying it again has the
usual future hazards with code copying.

Instead, we refactor that code into one function,
evp_pkey_make_provided(), and make sure to use that everywhere.
It relies on the creation of EVP_PKEY_CTX to figure out facts about
the input key, should it need to.

Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/10850)

crypto/evp/evp_local.h
crypto/evp/exchange.c
crypto/evp/m_sigver.c
crypto/evp/p_lib.c
crypto/evp/pmeth_fn.c
crypto/evp/signature.c
doc/internal/man3/evp_pkey_make_provided.pod [new file with mode: 0644]

index 0feace2aa5b60f23392eb1f3c24f9a1d49494248..894fdf996ee9ddbfd089f3527272d042cf092b74 100644 (file)
@@ -281,3 +281,6 @@ void evp_names_do_all(OSSL_PROVIDER *prov, int number,
                       void (*fn)(const char *name, void *data),
                       void *data);
 int evp_cipher_cache_constants(EVP_CIPHER *cipher);
+void *evp_pkey_make_provided(EVP_PKEY *pk, OPENSSL_CTX *libctx,
+                             EVP_KEYMGMT **keymgmt, const char *propquery,
+                             int domainparams);
index 56896390e033a6dde09cb87e29a0f493474a0594..9bb241fe145e7c07a2d643cc46d06bf5362c49b7 100644 (file)
@@ -164,6 +164,8 @@ int EVP_PKEY_derive_init(EVP_PKEY_CTX *ctx)
     int ret;
     void *provkey = NULL;
     EVP_KEYEXCH *exchange = NULL;
+    EVP_KEYMGMT *tmp_keymgmt = NULL;
+    const char *supported_exch = NULL;
 
     if (ctx == NULL) {
         EVPerr(0, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
@@ -176,33 +178,36 @@ int EVP_PKEY_derive_init(EVP_PKEY_CTX *ctx)
     if (ctx->engine != NULL || ctx->keytype == NULL)
         goto legacy;
 
-    if (ctx->keymgmt == NULL)
-        ctx->keymgmt =
-            EVP_KEYMGMT_fetch(ctx->libctx, ctx->keytype, ctx->propquery);
-    if (ctx->keymgmt != NULL) {
-        const char *supported_exch = NULL;
+    /* Ensure that the key is provided.  If not, go legacy */
+    tmp_keymgmt = ctx->keymgmt;
+    provkey = evp_pkey_make_provided(ctx->pkey, ctx->libctx,
+                                     &tmp_keymgmt, ctx->propquery, 0);
+    if (provkey == NULL)
+        goto legacy;
+    if (!EVP_KEYMGMT_up_ref(tmp_keymgmt)) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
+        goto err;
+    }
+    EVP_KEYMGMT_free(ctx->keymgmt);
+    ctx->keymgmt = tmp_keymgmt;
 
-        if (ctx->keymgmt->query_operation_name != NULL)
-            supported_exch =
-                ctx->keymgmt->query_operation_name(OSSL_OP_KEYEXCH);
+    if (ctx->keymgmt->query_operation_name != NULL)
+        supported_exch = ctx->keymgmt->query_operation_name(OSSL_OP_KEYEXCH);
 
-        /*
-         * If we didn't get a supported exch, assume there is one with the
-         * same name as the key type.
-         */
-        if (supported_exch == NULL)
-            supported_exch = ctx->keytype;
+    /*
+     * If we didn't get a supported exch, assume there is one with the
+     * same name as the key type.
+     */
+    if (supported_exch == NULL)
+        supported_exch = ctx->keytype;
 
-        /*
-         * Because we cleared out old ops, we shouldn't need to worry about
-         * checking if exchange is already there.
-         */
-        exchange =
-            EVP_KEYEXCH_fetch(ctx->libctx, supported_exch, ctx->propquery);
-    }
+    /*
+     * Because we cleared out old ops, we shouldn't need to worry about
+     * checking if exchange is already there.
+     */
+    exchange = EVP_KEYEXCH_fetch(ctx->libctx, supported_exch, ctx->propquery);
 
-    if (ctx->keymgmt == NULL
-        || exchange == NULL
+    if (exchange == NULL
         || (EVP_KEYMGMT_provider(ctx->keymgmt)
             != EVP_KEYEXCH_provider(exchange))) {
         /*
@@ -217,13 +222,6 @@ int EVP_PKEY_derive_init(EVP_PKEY_CTX *ctx)
 
 
     ctx->op.kex.exchange = exchange;
-
-    if (ctx->pkey != NULL) {
-        provkey = evp_keymgmt_export_to_provider(ctx->pkey, ctx->keymgmt, 0);
-        /* If export failed, legacy may be able to pick it up */
-        if (provkey == NULL)
-            goto legacy;
-    }
     ctx->op.kex.exchprovctx = exchange->newctx(ossl_provider_ctx(exchange->prov));
     if (ctx->op.kex.exchprovctx == NULL) {
         /* The provider key can stay in the cache */
index dbfa01b3ed20138ef0e2b83eb22bf19e390a26c2..20c0a1ad597c70b1fb6d37908c89dbba1f5ecef3 100644 (file)
@@ -31,6 +31,8 @@ static int do_sigver_init(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
 {
     EVP_PKEY_CTX *locpctx = NULL;
     EVP_SIGNATURE *signature = NULL;
+    EVP_KEYMGMT *tmp_keymgmt = NULL;
+    const char *supported_sig = NULL;
     void *provkey = NULL;
     int ret;
 
@@ -71,33 +73,38 @@ static int do_sigver_init(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
         }
     }
 
-    if (locpctx->keymgmt == NULL)
-        locpctx->keymgmt = EVP_KEYMGMT_fetch(locpctx->libctx, locpctx->keytype,
-                                             locpctx->propquery);
-    if (locpctx->keymgmt != NULL) {
-        const char *supported_sig = NULL;
+    /* Ensure that the key is provided.  If not, go legacy */
+    tmp_keymgmt = locpctx->keymgmt;
+    provkey = evp_pkey_make_provided(locpctx->pkey, locpctx->libctx,
+                                     &tmp_keymgmt, locpctx->propquery, 0);
+    if (provkey == NULL)
+        goto legacy;
+    if (!EVP_KEYMGMT_up_ref(tmp_keymgmt)) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
+        goto err;
+    }
+    EVP_KEYMGMT_free(locpctx->keymgmt);
+    locpctx->keymgmt = tmp_keymgmt;
 
-        if (locpctx->keymgmt->query_operation_name != NULL)
-            supported_sig =
-                locpctx->keymgmt->query_operation_name(OSSL_OP_SIGNATURE);
+    if (locpctx->keymgmt->query_operation_name != NULL)
+        supported_sig =
+            locpctx->keymgmt->query_operation_name(OSSL_OP_SIGNATURE);
 
-        /*
-         * If we didn't get a supported sig, assume there is one with the
-         * same name as the key type.
-         */
-        if (supported_sig == NULL)
-            supported_sig = locpctx->keytype;
+    /*
+     * If we didn't get a supported sig, assume there is one with the
+     * same name as the key type.
+     */
+    if (supported_sig == NULL)
+        supported_sig = locpctx->keytype;
 
-        /*
-         * Because we cleared out old ops, we shouldn't need to worry about
-         * checking if signature is already there.
-         */
-        signature = EVP_SIGNATURE_fetch(locpctx->libctx, supported_sig,
-                                        locpctx->propquery);
-    }
+    /*
+     * Because we cleared out old ops, we shouldn't need to worry about
+     * checking if signature is already there.
+     */
+    signature = EVP_SIGNATURE_fetch(locpctx->libctx, supported_sig,
+                                    locpctx->propquery);
 
-    if (locpctx->keymgmt == NULL
-        || signature == NULL
+    if (signature == NULL
         || (EVP_KEYMGMT_provider(locpctx->keymgmt)
             != EVP_SIGNATURE_provider(signature))) {
         /*
@@ -113,16 +120,8 @@ static int do_sigver_init(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
     /* No more legacy from here down to legacy: */
 
     locpctx->op.sig.signature = signature;
-
-    provkey =
-        evp_keymgmt_export_to_provider(locpctx->pkey, locpctx->keymgmt, 0);
-    /* If export failed, legacy may be able to pick it up */
-    if (provkey == NULL)
-        goto legacy;
-
     locpctx->operation = ver ? EVP_PKEY_OP_VERIFYCTX
                              : EVP_PKEY_OP_SIGNCTX;
-
     locpctx->op.sig.sigprovctx
         = signature->newctx(ossl_provider_ctx(signature->prov));
     if (locpctx->op.sig.sigprovctx == NULL) {
index 2e0890cac51abe31e9369d7301aaf8cde80d7971..016bcf93c2eb186f6a7c07e05304c437bf3c0550 100644 (file)
@@ -27,6 +27,7 @@
 #include "crypto/asn1.h"
 #include "crypto/evp.h"
 #include "internal/provider.h"
+#include "evp_local.h"
 
 static void evp_pkey_free_it(EVP_PKEY *key);
 
@@ -827,3 +828,47 @@ int EVP_PKEY_size(const EVP_PKEY *pkey)
     }
     return 0;
 }
+
+void *evp_pkey_make_provided(EVP_PKEY *pk, OPENSSL_CTX *libctx,
+                             EVP_KEYMGMT **keymgmt, const char *propquery,
+                             int domainparams)
+{
+    EVP_KEYMGMT *allocated_keymgmt = NULL;
+    EVP_KEYMGMT *tmp_keymgmt = NULL;
+    void *provdata = NULL;
+
+    if (pk == NULL)
+        return NULL;
+
+    if (keymgmt != NULL) {
+        tmp_keymgmt = *keymgmt;
+        *keymgmt = NULL;
+    }
+
+    if (tmp_keymgmt == NULL) {
+        EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_pkey(libctx, pk);
+
+        if (ctx != NULL && ctx->keytype != NULL)
+            tmp_keymgmt = allocated_keymgmt =
+                EVP_KEYMGMT_fetch(ctx->libctx, ctx->keytype, propquery);
+        EVP_PKEY_CTX_free(ctx);
+    }
+
+    if (tmp_keymgmt != NULL)
+        provdata =
+            evp_keymgmt_export_to_provider(pk, tmp_keymgmt, domainparams);
+
+    /*
+     * If nothing was exported, |tmp_keymgmt| might point at a freed
+     * EVP_KEYMGMT, so we clear it to be safe.  It shouldn't be useful for
+     * the caller either way in that case.
+     */
+    if (provdata == NULL)
+        tmp_keymgmt = NULL;
+
+    if (keymgmt != NULL)
+        *keymgmt = tmp_keymgmt;
+
+    EVP_KEYMGMT_free(allocated_keymgmt);
+    return provdata;
+}
index aa226fcc7b0797d0b1558e8b871b569e7cfae6b3..19bc56c654d818137fd8344f22e8177fd38a2562 100644 (file)
@@ -21,6 +21,8 @@ static int evp_pkey_asym_cipher_init(EVP_PKEY_CTX *ctx, int operation)
     int ret = 0;
     void *provkey = NULL;
     EVP_ASYM_CIPHER *cipher = NULL;
+    EVP_KEYMGMT *tmp_keymgmt = NULL;
+    const char *supported_ciph = NULL;
 
     if (ctx == NULL) {
         EVPerr(0, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
@@ -33,33 +35,35 @@ static int evp_pkey_asym_cipher_init(EVP_PKEY_CTX *ctx, int operation)
     if (ctx->keytype == NULL || ctx->engine != NULL)
         goto legacy;
 
-    if (ctx->keymgmt == NULL)
-        ctx->keymgmt =
-            EVP_KEYMGMT_fetch(ctx->libctx, ctx->keytype, ctx->propquery);
-    if (ctx->keymgmt != NULL) {
-        const char *supported_ciph = NULL;
-
-        if (ctx->keymgmt->query_operation_name != NULL)
-            supported_ciph =
-                ctx->keymgmt->query_operation_name(OSSL_OP_ASYM_CIPHER);
-
-        /*
-         * If we didn't get a supported ciph, assume there is one with the
-         * same name as the key type.
-         */
-        if (supported_ciph == NULL)
-            supported_ciph = ctx->keytype;
-
-        /*
-         * Because we cleared out old ops, we shouldn't need to worry about
-         * checking if cipher is already there.
-         */
-        cipher =
-            EVP_ASYM_CIPHER_fetch(ctx->libctx, supported_ciph, ctx->propquery);
-    }
-
-    if (ctx->keymgmt == NULL
-        || cipher == NULL
+    /* Ensure that the key is provided.  If not, go legacy */
+    tmp_keymgmt = ctx->keymgmt;
+    provkey = evp_pkey_make_provided(ctx->pkey, ctx->libctx,
+                                     &tmp_keymgmt, ctx->propquery, 0);
+    if (provkey == NULL)
+        goto legacy;
+    EVP_KEYMGMT_up_ref(tmp_keymgmt);
+    EVP_KEYMGMT_free(ctx->keymgmt);
+    ctx->keymgmt = tmp_keymgmt;
+
+    if (ctx->keymgmt->query_operation_name != NULL)
+        supported_ciph =
+            ctx->keymgmt->query_operation_name(OSSL_OP_ASYM_CIPHER);
+
+    /*
+     * If we didn't get a supported ciph, assume there is one with the
+     * same name as the key type.
+     */
+    if (supported_ciph == NULL)
+        supported_ciph = ctx->keytype;
+
+    /*
+     * Because we cleared out old ops, we shouldn't need to worry about
+     * checking if cipher is already there.
+     */
+    cipher =
+        EVP_ASYM_CIPHER_fetch(ctx->libctx, supported_ciph, ctx->propquery);
+
+    if (cipher == NULL
         || (EVP_KEYMGMT_provider(ctx->keymgmt)
             != EVP_ASYM_CIPHER_provider(cipher))) {
         /*
@@ -73,13 +77,6 @@ static int evp_pkey_asym_cipher_init(EVP_PKEY_CTX *ctx, int operation)
     }
 
     ctx->op.ciph.cipher = cipher;
-
-    if (ctx->pkey != NULL) {
-        provkey = evp_keymgmt_export_to_provider(ctx->pkey, ctx->keymgmt, 0);
-        /* If export failed, legacy may be able to pick it up */
-        if (provkey == NULL)
-            goto legacy;
-    }
     ctx->op.ciph.ciphprovctx = cipher->newctx(ossl_provider_ctx(cipher->prov));
     if (ctx->op.ciph.ciphprovctx == NULL) {
         /* The provider key can stay in the cache */
index 1cef4649ed49bf5c7c61a62f48009c928bb3aed8..0adf59be2a444ad6cc5ede32e30292d7a56c79e5 100644 (file)
@@ -322,6 +322,8 @@ static int evp_pkey_signature_init(EVP_PKEY_CTX *ctx, int operation)
     int ret = 0;
     void *provkey = NULL;
     EVP_SIGNATURE *signature = NULL;
+    EVP_KEYMGMT *tmp_keymgmt = NULL;
+    const char *supported_sig = NULL;
 
     if (ctx == NULL) {
         EVPerr(0, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
@@ -334,33 +336,37 @@ static int evp_pkey_signature_init(EVP_PKEY_CTX *ctx, int operation)
     if (ctx->keytype == NULL)
         goto legacy;
 
-    if (ctx->keymgmt == NULL)
-        ctx->keymgmt =
-            EVP_KEYMGMT_fetch(ctx->libctx, ctx->keytype, ctx->propquery);
-    if (ctx->keymgmt != NULL) {
-        const char *supported_sig = NULL;
-
-        if (ctx->keymgmt->query_operation_name != NULL)
-            supported_sig =
-                ctx->keymgmt->query_operation_name(OSSL_OP_SIGNATURE);
-
-        /*
-         * If we didn't get a supported sig, assume there is one with the
-         * same name as the key type.
-         */
-        if (supported_sig == NULL)
-            supported_sig = ctx->keytype;
-
-        /*
-         * Because we cleared out old ops, we shouldn't need to worry about
-         * checking if signature is already there.
-         */
-        signature =
-            EVP_SIGNATURE_fetch(ctx->libctx, supported_sig, ctx->propquery);
+    /* Ensure that the key is provided.  If not, go legacy */
+    tmp_keymgmt = ctx->keymgmt;
+    provkey = evp_pkey_make_provided(ctx->pkey, ctx->libctx,
+                                     &tmp_keymgmt, ctx->propquery, 0);
+    if (provkey == NULL)
+        goto legacy;
+    if (!EVP_KEYMGMT_up_ref(tmp_keymgmt)) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
+        goto err;
     }
-
-    if (ctx->keymgmt == NULL
-        || signature == NULL
+    EVP_KEYMGMT_free(ctx->keymgmt);
+    ctx->keymgmt = tmp_keymgmt;
+
+    if (ctx->keymgmt->query_operation_name != NULL)
+        supported_sig = ctx->keymgmt->query_operation_name(OSSL_OP_SIGNATURE);
+
+    /*
+     * If we didn't get a supported sig, assume there is one with the
+     * same name as the key type.
+     */
+    if (supported_sig == NULL)
+        supported_sig = ctx->keytype;
+
+    /*
+     * Because we cleared out old ops, we shouldn't need to worry about
+     * checking if signature is already there.
+     */
+    signature =
+        EVP_SIGNATURE_fetch(ctx->libctx, supported_sig, ctx->propquery);
+
+    if (signature == NULL
         || (EVP_KEYMGMT_provider(ctx->keymgmt)
             != EVP_SIGNATURE_provider(signature))) {
         /*
@@ -374,14 +380,6 @@ static int evp_pkey_signature_init(EVP_PKEY_CTX *ctx, int operation)
     }
 
     ctx->op.sig.signature = signature;
-
-    if (ctx->pkey != NULL) {
-        provkey =
-            evp_keymgmt_export_to_provider(ctx->pkey, ctx->keymgmt, 0);
-        /* If export failed, legacy may be able to pick it up */
-        if (provkey == NULL)
-            goto legacy;
-    }
     ctx->op.sig.sigprovctx = signature->newctx(ossl_provider_ctx(signature->prov));
     if (ctx->op.sig.sigprovctx == NULL) {
         /* The provider key can stay in the cache */
diff --git a/doc/internal/man3/evp_pkey_make_provided.pod b/doc/internal/man3/evp_pkey_make_provided.pod
new file mode 100644 (file)
index 0000000..e5dde78
--- /dev/null
@@ -0,0 +1,58 @@
+=pod
+
+=head1 NAME
+
+evp_pkey_make_provided - internal EVP_PKEY support functions for providers
+
+=head1 SYNOPSIS
+
+ /* Only for EVP source */
+ #include "evp_local.h"
+
+ void *evp_pkey_make_provided(EVP_PKEY *pk, OPENSSL_CTX *libctx,
+                              EVP_KEYMGMT **keymgmt, const char *propquery,
+                              int domainparams);
+
+=head1 DESCRIPTION
+
+evp_pkey_make_provided() ensures that the B<EVP_PKEY> I<pk> is provided within
+the library context I<libctx> (NULL means the default context).  I<keymgmt>
+may point at a reference to a B<EVP_KEYMGMT>, and works as an input/output
+parameter.
+As input to this function, it can be used to specify a B<EVP_KEYMGMT> to be
+used for exporting.  If not (I<*keymgmt> is NULL), then this function will
+fetch an B<EVP_KEYMGMT> implicitly, using I<propquery> as property query string.
+As output from this function, I<*keymgmt> will be assigned the B<EVP_KEYMGMT>
+that was used, if the export was successful, otherwise it will be assigned NULL.
+I<domainparams> decides if I<pk> should be considered domain parameters or the
+actual key.
+
+=head1 RETURN VALUES
+
+evp_pkey_make_provided() returns the provider key data that was exported if
+I<pk> was successfully provided.  Otherwise, NULL is returned.
+
+=head1 NOTES
+
+Some functions calling evp_pkey_make_provided() may have received a const
+key, and may therefore have to cast the key to non-const form to call this
+function.  Since B<EVP_PKEY> is always dynamically allocated, this is OK.
+
+=head1 SEE ALSO
+
+L<OPENSSL_CTX(3)>, L<EVP_KEYMGMT(3)>
+
+=head1 HISTORY
+
+The functions described here were all added in OpenSSL 3.0.
+
+=head1 COPYRIGHT
+
+Copyright 2020 The OpenSSL Project Authors. All Rights Reserved.
+
+Licensed under the Apache License 2.0 (the "License").  You may not use
+this file except in compliance with the License.  You can obtain a copy
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut