From aa4c32ebefacdad250b192b5ebd7560f4015f641 Mon Sep 17 00:00:00 2001
From: "Dr. Stephen Henson" <steve@openssl.org>
Date: Mon, 9 Oct 2017 15:21:11 +0100
Subject: [PATCH] Add EVP_PKEY_set1_engine() function.

Add an ENGINE to EVP_PKEY structure which can be used for cryptographic
operations: this will typically be used by an HSM key to redirect calls
to a custom EVP_PKEY_METHOD.

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

(cherry picked from commit d19b01ad79f9e2aac5c87496b5ca5f80016daeb7)
---
 crypto/evp/p_lib.c                | 26 ++++++++++++++++++++++++--
 crypto/evp/pmeth_lib.c            |  2 +-
 crypto/include/internal/evp_int.h |  1 +
 include/openssl/evp.h             |  3 +++
 4 files changed, 29 insertions(+), 3 deletions(-)

diff --git a/crypto/evp/p_lib.c b/crypto/evp/p_lib.c
index 9828620552..d7372aa129 100644
--- a/crypto/evp/p_lib.c
+++ b/crypto/evp/p_lib.c
@@ -187,9 +187,11 @@ static int pkey_set_type(EVP_PKEY *pkey, int type, const char *str, int len)
         if ((type == pkey->save_type) && pkey->ameth)
             return 1;
 #ifndef OPENSSL_NO_ENGINE
-        /* If we have an ENGINE release it */
+        /* If we have ENGINEs release them */
         ENGINE_finish(pkey->engine);
         pkey->engine = NULL;
+        ENGINE_finish(pkey->pmeth_engine);
+        pkey->pmeth_engine = NULL;
 #endif
     }
     if (str)
@@ -223,7 +225,25 @@ int EVP_PKEY_set_type_str(EVP_PKEY *pkey, const char *str, int len)
 {
     return pkey_set_type(pkey, EVP_PKEY_NONE, str, len);
 }
-
+#ifndef OPENSSL_NO_ENGINE
+int EVP_PKEY_set1_engine(EVP_PKEY *pkey, ENGINE *e)
+{
+    if (e != NULL) {
+        if (!ENGINE_init(e)) {
+            EVPerr(EVP_F_EVP_PKEY_SET1_ENGINE, ERR_R_ENGINE_LIB);
+            return 0;
+        }
+        if (ENGINE_get_pkey_meth(e, pkey->type) == NULL) {
+            ENGINE_finish(e);
+            EVPerr(EVP_F_EVP_PKEY_SET1_ENGINE, EVP_R_UNSUPPORTED_ALGORITHM);
+            return 0;
+        }
+    }
+    ENGINE_finish(pkey->pmeth_engine);
+    pkey->pmeth_engine = e;
+    return 1;
+}
+#endif
 int EVP_PKEY_assign(EVP_PKEY *pkey, int type, void *key)
 {
     if (pkey == NULL || !EVP_PKEY_set_type(pkey, type))
@@ -413,6 +433,8 @@ static void EVP_PKEY_free_it(EVP_PKEY *x)
 #ifndef OPENSSL_NO_ENGINE
     ENGINE_finish(x->engine);
     x->engine = NULL;
+    ENGINE_finish(x->pmeth_engine);
+    x->pmeth_engine = NULL;
 #endif
 }
 
diff --git a/crypto/evp/pmeth_lib.c b/crypto/evp/pmeth_lib.c
index 77e17dbd17..5e650a9db3 100644
--- a/crypto/evp/pmeth_lib.c
+++ b/crypto/evp/pmeth_lib.c
@@ -90,7 +90,7 @@ static EVP_PKEY_CTX *int_ctx_new(EVP_PKEY *pkey, ENGINE *e, int id)
     }
 #ifndef OPENSSL_NO_ENGINE
     if (e == NULL && pkey != NULL)
-        e = pkey->engine;
+        e = pkey->pmeth_engine != NULL ? pkey->pmeth_engine : pkey->engine;
     /* Try to find an ENGINE which implements this method */
     if (e) {
         if (!ENGINE_init(e)) {
diff --git a/crypto/include/internal/evp_int.h b/crypto/include/internal/evp_int.h
index c9ef58279a..6cc2f9255f 100644
--- a/crypto/include/internal/evp_int.h
+++ b/crypto/include/internal/evp_int.h
@@ -356,6 +356,7 @@ struct evp_pkey_st {
     int references;
     const EVP_PKEY_ASN1_METHOD *ameth;
     ENGINE *engine;
+    ENGINE *pmeth_engine; /* If not NULL public key ENGINE to use */
     union {
         void *ptr;
 # ifndef OPENSSL_NO_RSA
diff --git a/include/openssl/evp.h b/include/openssl/evp.h
index 01f51b7be3..6bc283c2a5 100644
--- a/include/openssl/evp.h
+++ b/include/openssl/evp.h
@@ -900,6 +900,9 @@ int EVP_PKEY_security_bits(const EVP_PKEY *pkey);
 int EVP_PKEY_size(EVP_PKEY *pkey);
 int EVP_PKEY_set_type(EVP_PKEY *pkey, int type);
 int EVP_PKEY_set_type_str(EVP_PKEY *pkey, const char *str, int len);
+# ifndef OPENSSL_NO_ENGINE
+int EVP_PKEY_set1_engine(EVP_PKEY *pkey, ENGINE *e);
+# endif
 int EVP_PKEY_assign(EVP_PKEY *pkey, int type, void *key);
 void *EVP_PKEY_get0(const EVP_PKEY *pkey);
 const unsigned char *EVP_PKEY_get0_hmac(const EVP_PKEY *pkey, size_t *len);
-- 
2.25.1