EVP: Adapt EVP_PKEY2PKCS8() to better handle provider-native keys
authorRichard Levitte <levitte@openssl.org>
Fri, 25 Sep 2020 18:02:56 +0000 (20:02 +0200)
committerRichard Levitte <levitte@openssl.org>
Fri, 13 Nov 2020 08:20:12 +0000 (09:20 +0100)
It doesn't downgread the keys to legacy any more.  Instead, it uses
OSSL_ENCODER to encode the key to DER, and d2i_PKCS8_PRIV_KEY_INFO()
to make a PKCS8_PRIV_KEY_INFO structure from that.

Fixes #12990

Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
(Merged from https://github.com/openssl/openssl/pull/12995)

crypto/evp/evp_pkey.c

index b5a8f1cd72006a21f5f2ac86f6926d47004ab5aa..766dfb46ecbb6c20e709e19415781761ed4f6024 100644 (file)
@@ -12,6 +12,8 @@
 #include "internal/cryptlib.h"
 #include <openssl/x509.h>
 #include <openssl/rand.h>
+#include <openssl/encoder.h>
+#include "internal/provider.h"
 #include "crypto/asn1.h"
 #include "crypto/evp.h"
 #include "crypto/x509.h"
@@ -69,35 +71,65 @@ EVP_PKEY *EVP_PKCS82PKEY(const PKCS8_PRIV_KEY_INFO *p8)
 
 PKCS8_PRIV_KEY_INFO *EVP_PKEY2PKCS8(const EVP_PKEY *pkey)
 {
-    PKCS8_PRIV_KEY_INFO *p8 = PKCS8_PRIV_KEY_INFO_new();
-    if (p8  == NULL) {
-        EVPerr(EVP_F_EVP_PKEY2PKCS8, ERR_R_MALLOC_FAILURE);
-        return NULL;
-    }
+    PKCS8_PRIV_KEY_INFO *p8 = NULL;
+    OSSL_ENCODER_CTX *ctx = NULL;
+
+    /*
+     * The implementation for provider-native keys is to encode the
+     * key to a DER encoded PKCS#8 structure, then convert it to a
+     * PKCS8_PRIV_KEY_INFO with good old d2i functions.
+     */
+    if (evp_pkey_is_provided(pkey)) {
+        int selection = OSSL_KEYMGMT_SELECT_ALL;
+        const OSSL_PROVIDER *prov = EVP_KEYMGMT_provider(pkey->keymgmt);
+        OSSL_LIB_CTX *libctx = ossl_provider_libctx(prov);
+        unsigned char *der = NULL;
+        size_t derlen = 0;
+        const unsigned char *pp;
+
+        if ((ctx = OSSL_ENCODER_CTX_new_by_EVP_PKEY(pkey, selection,
+                                                    "DER", "pkcs8",
+                                                    libctx, NULL)) == NULL
+            || !OSSL_ENCODER_to_data(ctx, &der, &derlen))
+            goto error;
 
-    /* Force a key downgrade if that's possible */
-    /* TODO(3.0) Is there a better way for provider-native keys? */
-    if (EVP_PKEY_get0(pkey) == NULL)
-        goto error;
+        pp = der;
+        p8 = d2i_PKCS8_PRIV_KEY_INFO(NULL, &pp, (long)derlen);
+        OPENSSL_free(der);
+        if (p8 == NULL)
+            goto error;
+    } else {
+        p8 = PKCS8_PRIV_KEY_INFO_new();
+        if (p8  == NULL) {
+            EVPerr(EVP_F_EVP_PKEY2PKCS8, ERR_R_MALLOC_FAILURE);
+            return NULL;
+        }
 
-    if (pkey->ameth) {
-        if (pkey->ameth->priv_encode) {
-            if (!pkey->ameth->priv_encode(p8, pkey)) {
-                EVPerr(EVP_F_EVP_PKEY2PKCS8, EVP_R_PRIVATE_KEY_ENCODE_ERROR);
+        if (pkey->ameth != NULL) {
+            if (pkey->ameth->priv_encode != NULL) {
+                if (!pkey->ameth->priv_encode(p8, pkey)) {
+                    EVPerr(EVP_F_EVP_PKEY2PKCS8,
+                           EVP_R_PRIVATE_KEY_ENCODE_ERROR);
+                    goto error;
+                }
+            } else {
+                EVPerr(EVP_F_EVP_PKEY2PKCS8, EVP_R_METHOD_NOT_SUPPORTED);
                 goto error;
             }
         } else {
-            EVPerr(EVP_F_EVP_PKEY2PKCS8, EVP_R_METHOD_NOT_SUPPORTED);
+            EVPerr(EVP_F_EVP_PKEY2PKCS8,
+                   EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM);
             goto error;
         }
-    } else {
-        EVPerr(EVP_F_EVP_PKEY2PKCS8, EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM);
-        goto error;
     }
-    return p8;
+    goto end;
  error:
     PKCS8_PRIV_KEY_INFO_free(p8);
-    return NULL;
+    p8 = NULL;
+ end:
+    OSSL_ENCODER_CTX_free(ctx);
+    return p8;
+
 }
 
 /* EVP_PKEY attribute functions */