Pass the object type and data structure from the pem2der decoder
authorMatt Caswell <matt@openssl.org>
Sat, 13 Feb 2021 14:24:15 +0000 (14:24 +0000)
committerMatt Caswell <matt@openssl.org>
Thu, 18 Feb 2021 16:05:22 +0000 (16:05 +0000)
The pem2der decoder can infer certain information about the endoded der
data based on the PEM headers. This information should be passed to the
next decoders in the chain to ensure we end up loading the correct type of
thing.

Reviewed-by: Richard Levitte <levitte@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/14191)

crypto/store/store_result.c
providers/implementations/encode_decode/decode_pem2der.c

index b79126e1cbb782ff45c442939b8f4d53119e7a31..64b0e814b346553e09fdc43fbe84ff8473de9b60 100644 (file)
@@ -62,6 +62,7 @@
 struct extracted_param_data_st {
     int object_type;
     const char *data_type;
+    const char *data_structure;
     const char *utf8_data;
     const void *octet_data;
     size_t octet_data_size;
@@ -128,6 +129,10 @@ int ossl_store_handle_load_result(const OSSL_PARAM params[], void *arg)
                                             &helper_data.octet_data_size)
         && !OSSL_PARAM_get_utf8_string_ptr(p, &helper_data.utf8_data))
         return 0;
+    p = OSSL_PARAM_locate_const(params, OSSL_OBJECT_PARAM_DATA_STRUCTURE);
+    if (p != NULL
+        && !OSSL_PARAM_get_utf8_string_ptr(p, &helper_data.data_structure))
+        return 0;
     p = OSSL_PARAM_locate_const(params, OSSL_OBJECT_PARAM_REFERENCE);
     if (p != NULL && !OSSL_PARAM_get_octet_string_ptr(p, &helper_data.ref,
                                                       &helper_data.ref_size))
@@ -274,8 +279,9 @@ static EVP_PKEY *try_key_value(struct extracted_param_data_st *data,
     }
 
     decoderctx =
-        OSSL_DECODER_CTX_new_for_pkey(&pk, "DER", NULL, data->data_type,
-                                      selection, libctx, propq);
+        OSSL_DECODER_CTX_new_for_pkey(&pk, "DER", data->data_structure,
+                                      data->data_type, selection, libctx,
+                                      propq);
     (void)OSSL_DECODER_CTX_set_passphrase_cb(decoderctx, cb, cbarg);
 
     /* No error if this couldn't be decoded */
index 73973e13ffc54ab887e6bb250b0381b6f6674dc6..895015a56ba654ba239e3a456c8accf9440591e9 100644 (file)
@@ -17,6 +17,7 @@
 
 #include <openssl/core_dispatch.h>
 #include <openssl/core_names.h>
+#include <openssl/core_object.h>
 #include <openssl/crypto.h>
 #include <openssl/err.h>
 #include <openssl/params.h>
@@ -114,20 +115,23 @@ static int pem2der_decode(void *vctx, OSSL_CORE_BIO *cin, int selection,
                           OSSL_CALLBACK *data_cb, void *data_cbarg,
                           OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
 {
-    /* Strings to peal off the pem name */
-    static const char *pealable_pem_name_endings[] = {
+    /* Strings to peel off the pem name */
+    static struct peelablee_pem_name_endings_st {
+        const char *ending;
+        const char *data_structure;
+    } peelable_pem_name_endings[] = {
         /*
          * These entries should be in longest to shortest order to avoid
          * mixups.
          */
-        "ENCRYPTED PRIVATE KEY",
-        "PRIVATE KEY",
-        "PUBLIC KEY",
-        "PARAMETERS"
+        { "ENCRYPTED PRIVATE KEY", "pkcs8" },
+        { "PRIVATE KEY", "pkcs8" },
+        { "PUBLIC KEY", "SubjectPublicKeyInfo" },
+        { "PARAMETERS", NULL }
 
         /*
          * Libcrypto currently only supports decoding keys with provider side
-         * decoders, so we don't try to peal any other PEM name.  That's an
+         * decoders, so we don't try to peel any other PEM name.  That's an
          * exercise for when libcrypto starts to treat other types of objects
          * via providers.
          */
@@ -138,6 +142,8 @@ static int pem2der_decode(void *vctx, OSSL_CORE_BIO *cin, int selection,
     unsigned char *der = NULL;
     long der_len = 0;
     int ok = 0;
+    int objtype = OSSL_OBJECT_UNKNOWN;
+    const char *data_structure = NULL;
 
     if (read_pem(ctx->provctx, cin, &pem_name, &pem_header,
                  &der, &der_len) <= 0)
@@ -166,15 +172,15 @@ static int pem2der_decode(void *vctx, OSSL_CORE_BIO *cin, int selection,
      * no further purpose.
      */
     for (i = 0, pem_name_len = strlen(pem_name);
-         i < OSSL_NELEM(pealable_pem_name_endings);
+         i < OSSL_NELEM(peelable_pem_name_endings);
          i++) {
-        size_t peal_len = strlen(pealable_pem_name_endings[i]);
+        size_t peel_len = strlen(peelable_pem_name_endings[i].ending);
         size_t pem_name_offset;
 
-        if (peal_len <= pem_name_len) {
-            pem_name_offset = pem_name_len - peal_len;
+        if (peel_len <= pem_name_len) {
+            pem_name_offset = pem_name_len - peel_len;
             if (strcmp(pem_name + pem_name_offset,
-                       pealable_pem_name_endings[i]) == 0) {
+                       peelable_pem_name_endings[i].ending) == 0) {
 
                 do {
                     pem_name[pem_name_offset] = '\0';
@@ -185,21 +191,53 @@ static int pem2der_decode(void *vctx, OSSL_CORE_BIO *cin, int selection,
                     OPENSSL_free(pem_name);
                     pem_name = NULL;
                 }
+                /* All of these peelable endings are for EVP_PKEYs */
+                objtype = OSSL_OBJECT_PKEY;
+                if (pem_name == NULL) {
+                    data_structure = peelable_pem_name_endings[i].data_structure;
+                    if (data_structure == NULL)
+                        goto end;
+                } else {
+                    /*
+                     * If there is an algorithm name prefix then it is a
+                     * type-specific data structure
+                     */
+                    data_structure = "type-specific";
+                }
                 break;
             }
         }
     }
 
+    /* If we don't know the object type yet check if it's one we know about */
+    if (objtype == OSSL_OBJECT_UNKNOWN) {
+        if (strcmp(pem_name, PEM_STRING_X509) == 0
+                || strcmp(pem_name, PEM_STRING_X509_TRUSTED) == 0
+                || strcmp(pem_name, PEM_STRING_X509_OLD) == 0)
+            objtype = OSSL_OBJECT_CERT;
+        else if (strcmp(pem_name, PEM_STRING_X509_CRL) == 0)
+            objtype = OSSL_OBJECT_CRL;
+    }
+
     {
-        OSSL_PARAM params[3], *p = params;
+        OSSL_PARAM params[5], *p = params;
 
         if (pem_name != NULL)
             *p++ =
                 OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE,
                                                  pem_name, 0);
+
+        /* We expect this to be read only so casting away the const is ok */
+        if (data_structure != NULL)
+            *p++ =
+                OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_STRUCTURE,
+                                                 (char *)data_structure, 0);
         *p++ =
             OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_DATA,
                                               der, der_len);
+        *p++ =
+            OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, &objtype);
+
         *p = OSSL_PARAM_construct_end();
 
         ok = data_cb(params, data_cbarg);