Add back in legacy paths for d2i_PrivateKey/d2i_AutoPrivateKey.
[openssl.git] / crypto / asn1 / d2i_pr.c
index c657f0f3a744e30da5c4c46b10366c56b33afb58..4da5a0c9bec8205934ec31adf41848fe119c3678 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2021 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
@@ -23,9 +23,9 @@
 #include "crypto/evp.h"
 #include "internal/asn1.h"
 
-EVP_PKEY *d2i_PrivateKey_ex(int keytype, EVP_PKEY **a, const unsigned char **pp,
-                            long length, OSSL_LIB_CTX *libctx,
-                            const char *propq)
+static EVP_PKEY *
+d2i_PrivateKey_decoder(int keytype, EVP_PKEY **a, const unsigned char **pp,
+                       long length, OSSL_LIB_CTX *libctx, const char *propq)
 {
     OSSL_DECODER_CTX *dctx = NULL;
     size_t len = length;
@@ -44,6 +44,8 @@ EVP_PKEY *d2i_PrivateKey_ex(int keytype, EVP_PKEY **a, const unsigned char **pp,
         ppkey = a;
 
     for (i = 0;  i < (int)OSSL_NELEM(input_structures); ++i) {
+        const unsigned char *p = *pp;
+
         dctx = OSSL_DECODER_CTX_new_for_pkey(ppkey, "DER",
                                              input_structures[i], key_name,
                                              EVP_PKEY_KEYPAIR, libctx, propq);
@@ -56,6 +58,7 @@ EVP_PKEY *d2i_PrivateKey_ex(int keytype, EVP_PKEY **a, const unsigned char **pp,
             if (*ppkey != NULL
                 && evp_keymgmt_util_has(*ppkey, OSSL_KEYMGMT_SELECT_PRIVATE_KEY))
                 return *ppkey;
+            *pp = p;
             goto err;
         }
     }
@@ -132,12 +135,77 @@ EVP_PKEY *evp_privatekey_from_binary(int keytype, EVP_PKEY **a,
     return NULL;
 }
 
+EVP_PKEY *d2i_PrivateKey_ex(int keytype, EVP_PKEY **a, const unsigned char **pp,
+                            long length, OSSL_LIB_CTX *libctx,
+                            const char *propq)
+{
+    EVP_PKEY *ret;
+
+    ret = d2i_PrivateKey_decoder(keytype, a, pp, length, libctx, propq);
+    /* try the legacy path if the decoder failed */
+    if (ret == NULL)
+        ret = evp_privatekey_from_binary(keytype, a, pp, length, libctx, propq);
+    return ret;
+}
+
 EVP_PKEY *d2i_PrivateKey(int type, EVP_PKEY **a, const unsigned char **pp,
                          long length)
 {
     return d2i_PrivateKey_ex(type, a, pp, length, NULL, NULL);
 }
 
+static EVP_PKEY *d2i_AutoPrivateKey_legacy(EVP_PKEY **a,
+                                           const unsigned char **pp,
+                                           long length,
+                                           OSSL_LIB_CTX *libctx,
+                                           const char *propq)
+{
+    STACK_OF(ASN1_TYPE) *inkey;
+    const unsigned char *p;
+    int keytype;
+
+    p = *pp;
+    /*
+     * Dirty trick: read in the ASN1 data into a STACK_OF(ASN1_TYPE): by
+     * analyzing it we can determine the passed structure: this assumes the
+     * input is surrounded by an ASN1 SEQUENCE.
+     */
+    inkey = d2i_ASN1_SEQUENCE_ANY(NULL, &p, length);
+    p = *pp;
+    /*
+     * Since we only need to discern "traditional format" RSA and DSA keys we
+     * can just count the elements.
+     */
+    if (sk_ASN1_TYPE_num(inkey) == 6) {
+        keytype = EVP_PKEY_DSA;
+    } else if (sk_ASN1_TYPE_num(inkey) == 4) {
+        keytype = EVP_PKEY_EC;
+    } else if (sk_ASN1_TYPE_num(inkey) == 3) { /* This seems to be PKCS8, not
+                                              * traditional format */
+        PKCS8_PRIV_KEY_INFO *p8 = d2i_PKCS8_PRIV_KEY_INFO(NULL, &p, length);
+        EVP_PKEY *ret;
+
+        sk_ASN1_TYPE_pop_free(inkey, ASN1_TYPE_free);
+        if (p8 == NULL) {
+            ERR_raise(ERR_LIB_ASN1, ASN1_R_UNSUPPORTED_PUBLIC_KEY_TYPE);
+            return NULL;
+        }
+        ret = EVP_PKCS82PKEY_ex(p8, libctx, propq);
+        PKCS8_PRIV_KEY_INFO_free(p8);
+        if (ret == NULL)
+            return NULL;
+        *pp = p;
+        if (a) {
+            *a = ret;
+        }
+        return ret;
+    } else {
+        keytype = EVP_PKEY_RSA;
+    }
+    sk_ASN1_TYPE_pop_free(inkey, ASN1_TYPE_free);
+    return evp_privatekey_from_binary(keytype, a, pp, length, libctx, propq);
+}
+
 /*
  * This works like d2i_PrivateKey() except it passes the keytype as
  * EVP_PKEY_NONE, which then figures out the type during decoding.
@@ -146,7 +214,13 @@ EVP_PKEY *d2i_AutoPrivateKey_ex(EVP_PKEY **a, const unsigned char **pp,
                                 long length, OSSL_LIB_CTX *libctx,
                                 const char *propq)
 {
-    return d2i_PrivateKey_ex(EVP_PKEY_NONE, a, pp, length, libctx, propq);
+    EVP_PKEY *ret;
+
+    ret = d2i_PrivateKey_decoder(EVP_PKEY_NONE, a, pp, length, libctx, propq);
+    /* try the legacy path if the decoder failed */
+    if (ret == NULL)
+        ret = d2i_AutoPrivateKey_legacy(a, pp, length, libctx, propq);
+    return ret;
 }
 
 EVP_PKEY *d2i_AutoPrivateKey(EVP_PKEY **a, const unsigned char **pp,