[openssl] master update

Richard Levitte levitte at openssl.org
Wed Nov 11 10:42:14 UTC 2020


The branch master has been updated
       via  9787b5b81fd9ca41427fa7b89de4d9518e988f6a (commit)
       via  8d8fee64005d0757ba75e2b24b730cfc7b8edbef (commit)
       via  35426b2f9ca961c783ad882f1f2ede0d6fe2c232 (commit)
       via  a5cc6616454701bfcdd1694e109389c764f6f31c (commit)
       via  eb2f1b81a3e55aba2c81bd8b7cfd11309049cf58 (commit)
       via  4f2abe4378ce561c60674f3ac0642d3fb22da550 (commit)
       via  2c090c1d1b00fe49dd2911674e26c629f123c44f (commit)
       via  de5008a4076e36f7038180d60ae1521afb524d68 (commit)
       via  df65c06b59f0ccd06398c0ff3034371fdefd8e70 (commit)
       via  67c91ca23eae175a08f0f0c60be6e6957334d25e (commit)
       via  ebfdb63d96496274106b4192fda6039cbb272bae (commit)
      from  f7626d0bfab35acd89c2c6542fc6504954191a14 (commit)


- Log -----------------------------------------------------------------
commit 9787b5b81fd9ca41427fa7b89de4d9518e988f6a
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Oct 28 19:13:46 2020 +0100

    OSSL_STORE: Make sure the called OSSL_DECODER knows what to expect
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/13248)

commit 8d8fee64005d0757ba75e2b24b730cfc7b8edbef
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Oct 28 19:11:24 2020 +0100

    PEM: Have pem_read_bio_key() set the OSSL_STORE expected type
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/13248)

commit 35426b2f9ca961c783ad882f1f2ede0d6fe2c232
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Oct 28 17:35:48 2020 +0100

    Restore the legacy implementation of PEM_read_bio_DHparams()
    
    It was an overstep to have it got through OSSL_STORE just to extract a
    DH pointer from the resulting EVP_PKEY.
    
    This partially reverts 1427d33cee59d6fe54efe1b5a322a1d7c8c03c20
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/13248)

commit a5cc6616454701bfcdd1694e109389c764f6f31c
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Oct 26 16:38:40 2020 +0100

    TEST: Adapt test/endecoder_test.c
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/13248)

commit eb2f1b81a3e55aba2c81bd8b7cfd11309049cf58
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Oct 26 13:59:09 2020 +0100

    DH: Move the code to set the DH sub-type
    
    It's been moved so the type is set by d2i_DHparams() and d2i_DHxparams()
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/13248)

commit 4f2abe4378ce561c60674f3ac0642d3fb22da550
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Oct 26 13:58:30 2020 +0100

    Adapt libcrypto functionality to specify the desired input structure
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/13248)

commit 2c090c1d1b00fe49dd2911674e26c629f123c44f
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Oct 26 13:22:54 2020 +0100

    PROV: Re-implement all the keypair decoders
    
    The base functionality to implement the keypair decoders doesn't
    change much, but this results in a more massive amount of
    OSSL_DISPATCH and OSSL_ALGORITHM arrays, to support a fine grained
    selection of implementation based on what parts of the keypair
    structure (combinations of key parameters, public key and private key)
    should be expected as input, the input type ("DER", "PEM", ...) and the
    outermost input structure ("pkcs8", "SubjectPublicKeyInfo", key
    type specific structures, ...).
    
    We add support for the generic structure name "type-specific", to
    allow selecting that without knowing the exact name of that structure.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/13248)

commit de5008a4076e36f7038180d60ae1521afb524d68
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Oct 28 10:13:24 2020 +0100

    DECODER: Add tracing
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/13248)

commit df65c06b59f0ccd06398c0ff3034371fdefd8e70
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Oct 26 13:17:42 2020 +0100

    DECODER: Add input structure support for EVP_PKEY decoding
    
    OSSL_DECODER_CTX_new_by_EVP_PKEY() takes one more argument to express
    the desired outermost structure for the input.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/13248)

commit 67c91ca23eae175a08f0f0c60be6e6957334d25e
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Oct 26 13:08:54 2020 +0100

    DECODER: Add support for OSSL_FUNC_decoder_does_selection()
    
    OSSL_FUNC_decoder_does_selection() is a dispatchable decoder implementation
    function that should return 1 if the given |selection| is supported by an
    decoder implementation and 0 if not.  This can be used by libcrypto
    functionality to figure out if an encoder implementation should be
    considered or not.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/13248)

commit ebfdb63d96496274106b4192fda6039cbb272bae
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Oct 26 13:06:01 2020 +0100

    DECODER: Add support for specifying the outermost input structure
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/13248)

-----------------------------------------------------------------------

Summary of changes:
 crypto/cms/cms_ec.c                                |  17 +-
 crypto/dh/dh_ameth.c                               |   4 -
 crypto/dh/dh_asn1.c                                |   8 +-
 crypto/encode_decode/decoder_lib.c                 |  81 +++-
 crypto/encode_decode/decoder_meth.c                |   5 +
 crypto/encode_decode/decoder_pkey.c                |  81 +++-
 crypto/encode_decode/encoder_local.h               |  23 +
 crypto/pem/pem_all.c                               |  44 ++
 crypto/pem/pem_pkey.c                              |  64 +--
 crypto/store/store_result.c                        |  40 +-
 crypto/trace.c                                     |   1 +
 doc/man3/OSSL_DECODER_CTX.pod                      |  28 +-
 include/openssl/core_dispatch.h                    |   9 +-
 include/openssl/core_names.h                       |   5 +-
 include/openssl/decoder.h                          |  10 +-
 include/openssl/trace.h                            |   3 +-
 providers/baseprov.c                               |   8 +-
 providers/decoders.inc                             |  78 ++-
 providers/defltprov.c                              |   8 +-
 .../implementations/encode_decode/decode_der2key.c | 537 ++++++++++++++++++---
 .../implementations/encode_decode/decode_ms2key.c  |  18 +-
 .../implementations/encode_decode/decode_pem2der.c |   6 +-
 .../implementations/include/prov/implementations.h |  50 +-
 .../implementations/storemgmt/file_store_der2obj.c |   2 +-
 test/endecode_test.c                               |   2 +
 util/libcrypto.num                                 |   3 +
 26 files changed, 902 insertions(+), 233 deletions(-)

diff --git a/crypto/cms/cms_ec.c b/crypto/cms/cms_ec.c
index 9a602bb81d..5dac7f0683 100644
--- a/crypto/cms/cms_ec.c
+++ b/crypto/cms/cms_ec.c
@@ -24,23 +24,16 @@ static EVP_PKEY *pkey_type2param(int ptype, const void *pval,
     if (ptype == V_ASN1_SEQUENCE) {
         const ASN1_STRING *pstr = pval;
         const unsigned char *pm = pstr->data;
-        int pmlen = pstr->length;
+        size_t pmlen = (size_t)pstr->length;
         OSSL_DECODER_CTX *ctx = NULL;
-        BIO *membio = NULL;
+        int selection = OSSL_KEYMGMT_SELECT_ALL_PARAMETERS;
 
-        /* TODO(3.0): Need to be able to specify here that only params will do */
-        ctx = OSSL_DECODER_CTX_new_by_EVP_PKEY(&pkey, "DER", "EC", libctx,
-                                               propq);
+        ctx = OSSL_DECODER_CTX_new_by_EVP_PKEY(&pkey, "DER", NULL, "EC",
+                                               selection, libctx, propq);
         if (ctx == NULL)
             goto err;
 
-        membio = BIO_new_mem_buf(pm, pmlen);
-        if (membio == NULL) {
-            OSSL_DECODER_CTX_free(ctx);
-            goto err;
-        }
-        OSSL_DECODER_from_bio(ctx, membio);
-        BIO_free(membio);
+        OSSL_DECODER_from_data(ctx, &pm, &pmlen);
         OSSL_DECODER_CTX_free(ctx);
     } else if (ptype == V_ASN1_OBJECT) {
         const ASN1_OBJECT *poid = pval;
diff --git a/crypto/dh/dh_ameth.c b/crypto/dh/dh_ameth.c
index 49e65e4d6c..1efbb403cb 100644
--- a/crypto/dh/dh_ameth.c
+++ b/crypto/dh/dh_ameth.c
@@ -42,10 +42,6 @@ static DH *d2i_dhp(const EVP_PKEY *pkey, const unsigned char **pp,
     else
         dh = d2i_DHparams(NULL, pp, length);
 
-    if (dh != NULL) {
-        DH_clear_flags(dh, DH_FLAG_TYPE_MASK);
-        DH_set_flags(dh, is_dhx ? DH_FLAG_TYPE_DHX : DH_FLAG_TYPE_DH);
-    }
     return dh;
 }
 
diff --git a/crypto/dh/dh_asn1.c b/crypto/dh/dh_asn1.c
index cf5c735a6a..81899de5d6 100644
--- a/crypto/dh/dh_asn1.c
+++ b/crypto/dh/dh_asn1.c
@@ -34,7 +34,11 @@ static int dh_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
         *pval = NULL;
         return 2;
     } else if (operation == ASN1_OP_D2I_POST) {
-        ((DH *)*pval)->dirty_cnt++;
+        DH *dh = (DH *)*pval;
+
+        DH_clear_flags(dh, DH_FLAG_TYPE_MASK);
+        DH_set_flags(dh, DH_FLAG_TYPE_DH);
+        dh->dirty_cnt++;
     }
     return 1;
 }
@@ -123,6 +127,8 @@ DH *d2i_DHxparams(DH **a, const unsigned char **pp, long length)
     }
 
     OPENSSL_free(dhx);
+    DH_clear_flags(dh, DH_FLAG_TYPE_MASK);
+    DH_set_flags(dh, DH_FLAG_TYPE_DHX);
     return dh;
 }
 
diff --git a/crypto/encode_decode/decoder_lib.c b/crypto/encode_decode/decoder_lib.c
index 20350a8cd6..8e9af13bbb 100644
--- a/crypto/encode_decode/decoder_lib.c
+++ b/crypto/encode_decode/decoder_lib.c
@@ -14,6 +14,7 @@
 #include <openssl/evperr.h>
 #include <openssl/ecerr.h>
 #include <openssl/x509err.h>
+#include <openssl/trace.h>
 #include "internal/passphrase.h"
 #include "crypto/decoder.h"
 #include "encoder_local.h"
@@ -98,6 +99,21 @@ int OSSL_DECODER_from_data(OSSL_DECODER_CTX *ctx, const unsigned char **pdata,
     return ret;
 }
 
+int OSSL_DECODER_CTX_set_selection(OSSL_DECODER_CTX *ctx, int selection)
+{
+    if (!ossl_assert(ctx != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    /*
+     * 0 is a valid selection, and means that the caller leaves
+     * it to code to discover what the selection is.
+     */
+    ctx->selection = selection;
+    return 1;
+}
+
 int OSSL_DECODER_CTX_set_input_type(OSSL_DECODER_CTX *ctx,
                                     const char *input_type)
 {
@@ -114,11 +130,27 @@ int OSSL_DECODER_CTX_set_input_type(OSSL_DECODER_CTX *ctx,
     return 1;
 }
 
+int OSSL_DECODER_CTX_set_input_structure(OSSL_DECODER_CTX *ctx,
+                                         const char *input_structure)
+{
+    if (!ossl_assert(ctx != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    /*
+     * NULL is a valid starting input type, and means that the caller leaves
+     * it to code to discover what the starting input type is.
+     */
+    ctx->input_structure = input_structure;
+    return 1;
+}
+
 OSSL_DECODER_INSTANCE *ossl_decoder_instance_new(OSSL_DECODER *decoder,
                                                  void *decoderctx)
 {
     OSSL_DECODER_INSTANCE *decoder_inst = NULL;
-    OSSL_PARAM params[2];
+    OSSL_PARAM params[3];
 
     if (!ossl_assert(decoder != NULL)) {
         ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_PASSED_NULL_PARAMETER);
@@ -140,16 +172,22 @@ OSSL_DECODER_INSTANCE *ossl_decoder_instance_new(OSSL_DECODER *decoder,
         goto err;
     }
 
-    /* Cache the input type for this encoder */
+    /* Cache the input type for this decoder */
     params[0] =
         OSSL_PARAM_construct_utf8_ptr(OSSL_DECODER_PARAM_INPUT_TYPE,
                                       (char **)&decoder_inst->input_type, 0);
-    params[1] = OSSL_PARAM_construct_end();
+    params[1] =
+        OSSL_PARAM_construct_utf8_ptr(OSSL_DECODER_PARAM_INPUT_STRUCTURE,
+                                      (char **)&decoder_inst->input_structure,
+                                      0);
+    params[2] = OSSL_PARAM_construct_end();
 
     if (!decoder->get_params(params)
         || !OSSL_PARAM_modified(&params[0]))
         goto err;
 
+    decoder_inst->flag_input_structure_was_set =
+        OSSL_PARAM_modified(&params[1]);
     decoder_inst->decoder = decoder;
     decoder_inst->decoderctx = decoderctx;
     return decoder_inst;
@@ -171,8 +209,10 @@ void ossl_decoder_instance_free(OSSL_DECODER_INSTANCE *decoder_inst)
 }
 
 int ossl_decoder_ctx_add_decoder_inst(OSSL_DECODER_CTX *ctx,
-                                         OSSL_DECODER_INSTANCE *di)
+                                      OSSL_DECODER_INSTANCE *di)
 {
+    int ok;
+
     if (ctx->decoder_insts == NULL
         && (ctx->decoder_insts =
             sk_OSSL_DECODER_INSTANCE_new_null()) == NULL) {
@@ -180,7 +220,18 @@ int ossl_decoder_ctx_add_decoder_inst(OSSL_DECODER_CTX *ctx,
         return 0;
     }
 
-    return (sk_OSSL_DECODER_INSTANCE_push(ctx->decoder_insts, di) > 0);
+    ok = (sk_OSSL_DECODER_INSTANCE_push(ctx->decoder_insts, di) > 0);
+    if (ok) {
+        OSSL_TRACE_BEGIN(DECODER) {
+            BIO_printf(trc_out,
+                       "(ctx %p) Added decoder instance %p (decoder %p) with:\n",
+                       (void *)ctx, (void *)di, (void *)di->decoder);
+            BIO_printf(trc_out,
+                       "    input type: %s, input structure: %s\n",
+                       di->input_type, di->input_structure);
+        } OSSL_TRACE_END(DECODER);
+    }
+    return ok;
 }
 
 int OSSL_DECODER_CTX_add_decoder(OSSL_DECODER_CTX *ctx, OSSL_DECODER *decoder)
@@ -225,7 +276,7 @@ int OSSL_DECODER_CTX_add_extra(OSSL_DECODER_CTX *ctx,
      * what the existing ones want as input, and push those newly fetched
      * decoders on top of the same stack.
      * Then it does the same again, but looping over the newly fetched
-     * decoders, until there are no more encoders to be fetched, or
+     * decoders, until there are no more decoders to be fetched, or
      * when we have done this 10 times.
      *
      * we do this with sliding windows on the stack by keeping track of indexes
@@ -439,6 +490,16 @@ OSSL_DECODER_INSTANCE_get_input_type(OSSL_DECODER_INSTANCE *decoder_inst)
     return decoder_inst->input_type;
 }
 
+const char *
+OSSL_DECODER_INSTANCE_get_input_structure(OSSL_DECODER_INSTANCE *decoder_inst,
+                                          int *was_set)
+{
+    if (decoder_inst == NULL)
+        return NULL;
+    *was_set = decoder_inst->flag_input_structure_was_set;
+    return decoder_inst->input_structure;
+}
+
 static int decoder_process(const OSSL_PARAM params[], void *arg)
 {
     struct decoder_process_data_st *data = arg;
@@ -564,9 +625,17 @@ static int decoder_process(const OSSL_PARAM params[], void *arg)
         /* Recurse */
         new_data.current_decoder_inst_index = i;
         ok = new_decoder->decode(new_decoderctx, (OSSL_CORE_BIO *)bio,
+                                 new_data.ctx->selection,
                                  decoder_process, &new_data,
                                  ossl_pw_passphrase_callback_dec,
                                  &new_data.ctx->pwdata);
+
+        OSSL_TRACE_BEGIN(DECODER) {
+            BIO_printf(trc_out,
+                       "(ctx %p) Running decoder instance %p => %d\n",
+                       (void *)new_data.ctx, (void *)new_decoder_inst, ok);
+        } OSSL_TRACE_END(DECODER);
+
         if (ok)
             break;
         err = ERR_peek_last_error();
diff --git a/crypto/encode_decode/decoder_meth.c b/crypto/encode_decode/decoder_meth.c
index edbb140c44..0d389ac5a6 100644
--- a/crypto/encode_decode/decoder_meth.c
+++ b/crypto/encode_decode/decoder_meth.c
@@ -198,6 +198,11 @@ void *ossl_decoder_from_dispatch(int id, const OSSL_ALGORITHM *algodef,
                 decoder->settable_ctx_params =
                     OSSL_FUNC_decoder_settable_ctx_params(fns);
             break;
+        case OSSL_FUNC_DECODER_DOES_SELECTION:
+            if (decoder->does_selection == NULL)
+                decoder->does_selection =
+                    OSSL_FUNC_decoder_does_selection(fns);
+            break;
         case OSSL_FUNC_DECODER_DECODE:
             if (decoder->decode == NULL)
                 decoder->decode = OSSL_FUNC_decoder_decode(fns);
diff --git a/crypto/encode_decode/decoder_pkey.c b/crypto/encode_decode/decoder_pkey.c
index e9c0141804..3a765c5986 100644
--- a/crypto/encode_decode/decoder_pkey.c
+++ b/crypto/encode_decode/decoder_pkey.c
@@ -9,13 +9,16 @@
 
 #include <openssl/core_names.h>
 #include <openssl/core_object.h>
+#include <openssl/provider.h>
 #include <openssl/evp.h>
 #include <openssl/ui.h>
 #include <openssl/decoder.h>
 #include <openssl/safestack.h>
+#include <openssl/trace.h>
 #include "crypto/evp.h"
 #include "crypto/decoder.h"
 #include "encoder_local.h"
+#include "e_os.h"                /* strcasecmp on Windows */
 
 int OSSL_DECODER_CTX_set_passphrase(OSSL_DECODER_CTX *ctx,
                                     const unsigned char *kstr,
@@ -231,10 +234,40 @@ static void collect_name(const char *name, void *arg)
     data->error_occured = 0;         /* All is good now */
 }
 
+/*
+ * The input structure check is only done on the initial decoder
+ * implementations.
+ */
+static int collect_decoder_check_input_structure(OSSL_DECODER_CTX *ctx,
+                                                 OSSL_DECODER_INSTANCE *di)
+{
+    int di_is_was_set = 0;
+    const char *di_is =
+        OSSL_DECODER_INSTANCE_get_input_structure(di, &di_is_was_set);
+
+    /*
+     * If caller didn't give an input structure name, the decoder is accepted
+     * unconditionally with regards to the input structure.
+     */
+    if (ctx->input_structure == NULL)
+        return 1;
+    /*
+     * If the caller did give an input structure name, the decoder must have
+     * a matching input structure to be accepted.
+     */
+    if (di_is != NULL
+        && strcasecmp(ctx->input_structure, di_is) == 0)
+        return 1;
+    return 0;
+}
+
 static void collect_decoder(OSSL_DECODER *decoder, void *arg)
 {
     struct collected_data_st *data = arg;
     size_t i, end_i;
+    const OSSL_PROVIDER *prov = OSSL_DECODER_provider(decoder);
+    void *provctx = OSSL_PROVIDER_get0_provider_ctx(prov);
+    void *decoderctx = NULL;
 
     if (data->error_occured)
         return;
@@ -246,10 +279,30 @@ static void collect_decoder(OSSL_DECODER *decoder, void *arg)
     end_i = sk_OPENSSL_CSTRING_num(data->names);
     for (i = 0; i < end_i; i++) {
         const char *name = sk_OPENSSL_CSTRING_value(data->names, i);
+        OSSL_DECODER_INSTANCE *di = NULL;
+
+        if (OSSL_DECODER_is_a(decoder, name)
+            /*
+             * Either the caller didn't give a selection, or if they did,
+             * the decoder must tell us if it supports that selection to
+             * be accepted.  If the decoder doesn't have |does_selection|,
+             * it's seen as taking anything.
+             */
+            && (decoder->does_selection == NULL
+                || decoder->does_selection(provctx, data->ctx->selection))
+            && (decoderctx = decoder->newctx(provctx)) != NULL
+            && (di = ossl_decoder_instance_new(decoder, decoderctx)) != NULL) {
+            /* If successful so far, don't free these directly */
+            decoderctx = NULL;
+
+            if (collect_decoder_check_input_structure(data->ctx, di)
+                && ossl_decoder_ctx_add_decoder_inst(data->ctx, di))
+                di = NULL;      /* If successfully added, don't free it */
+        }
 
-        if (!OSSL_DECODER_is_a(decoder, name))
-            continue;
-        (void)OSSL_DECODER_CTX_add_decoder(data->ctx, decoder);
+        /* Free what can be freed */
+        ossl_decoder_instance_free(di);
+        decoder->freectx(decoderctx);
     }
 
     data->error_occured = 0;         /* All is good now */
@@ -325,7 +378,9 @@ int ossl_decoder_ctx_setup_for_EVP_PKEY(OSSL_DECODER_CTX *ctx,
 
 OSSL_DECODER_CTX *
 OSSL_DECODER_CTX_new_by_EVP_PKEY(EVP_PKEY **pkey,
-                                 const char *input_type, const char *keytype,
+                                 const char *input_type,
+                                 const char *input_structure,
+                                 const char *keytype, int selection,
                                  OSSL_LIB_CTX *libctx, const char *propquery)
 {
     OSSL_DECODER_CTX *ctx = NULL;
@@ -334,11 +389,27 @@ OSSL_DECODER_CTX_new_by_EVP_PKEY(EVP_PKEY **pkey,
         ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_MALLOC_FAILURE);
         return NULL;
     }
+
+    OSSL_TRACE_BEGIN(DECODER) {
+        BIO_printf(trc_out,
+                   "(ctx %p) Looking for %s decoders with selection %d\n",
+                   (void *)ctx, keytype, selection);
+        BIO_printf(trc_out, "    input type: %s, input structure: %s\n",
+                   input_type, input_structure);
+    } OSSL_TRACE_END(DECODER);
+
     if (OSSL_DECODER_CTX_set_input_type(ctx, input_type)
+        && OSSL_DECODER_CTX_set_input_structure(ctx, input_structure)
+        && OSSL_DECODER_CTX_set_selection(ctx, selection)
         && ossl_decoder_ctx_setup_for_EVP_PKEY(ctx, pkey, keytype,
                                                libctx, propquery)
-        && OSSL_DECODER_CTX_add_extra(ctx, libctx, propquery))
+        && OSSL_DECODER_CTX_add_extra(ctx, libctx, propquery)) {
+        OSSL_TRACE_BEGIN(DECODER) {
+            BIO_printf(trc_out, "(ctx %p) Got %d decoders\n",
+                       (void *)ctx, OSSL_DECODER_CTX_get_num_decoders(ctx));
+        } OSSL_TRACE_END(DECODER);
         return ctx;
+    }
 
     OSSL_DECODER_CTX_free(ctx);
     return NULL;
diff --git a/crypto/encode_decode/encoder_local.h b/crypto/encode_decode/encoder_local.h
index a57d0cd16c..18cddf50fb 100644
--- a/crypto/encode_decode/encoder_local.h
+++ b/crypto/encode_decode/encoder_local.h
@@ -46,6 +46,7 @@ struct ossl_decoder_st {
     OSSL_FUNC_decoder_gettable_params_fn *gettable_params;
     OSSL_FUNC_decoder_set_ctx_params_fn *set_ctx_params;
     OSSL_FUNC_decoder_settable_ctx_params_fn *settable_ctx_params;
+    OSSL_FUNC_decoder_does_selection_fn *does_selection;
     OSSL_FUNC_decoder_decode_fn *decode;
     OSSL_FUNC_decoder_export_object_fn *export_object;
 };
@@ -96,6 +97,9 @@ struct ossl_decoder_instance_st {
     OSSL_DECODER *decoder;       /* Never NULL */
     void *decoderctx;            /* Never NULL */
     const char *input_type;      /* Never NULL */
+    const char *input_structure; /* May be NULL */
+
+    unsigned int flag_input_structure_was_set : 1;
 };
 
 DEFINE_STACK_OF(OSSL_DECODER_INSTANCE)
@@ -108,6 +112,25 @@ struct ossl_decoder_ctx_st {
      * regardless of their respective input type.
      */
     const char *start_input_type;
+    /*
+     * The desired input structure, if that's relevant for the type of
+     * object being encoded.  It may be used for selection of the ending
+     * decoder implementations in a chain, i.e. those chosen using the
+     * expected output data type.
+     */
+    const char *input_structure;
+    /*
+     * Select what parts of an object are expected.  This may affect what
+     * decoder implementations are selected, because there are structures
+     * that look different depending on this selection; for example, EVP_PKEY
+     * objects often have different encoding structures for private keys,
+     * public keys and key parameters.
+     * This selection is bit encoded, and the bits correspond to selection
+     * bits available with the provider side operation.  For example, when
+     * encoding an EVP_PKEY, the OSSL_KEYMGMT_SELECT_ macros are used for
+     * this.
+     */
+    int selection;
 
     /*
      * Decoders that are components of any current decoding path.
diff --git a/crypto/pem/pem_all.c b/crypto/pem/pem_all.c
index bf7159a6ad..01c62d0222 100644
--- a/crypto/pem/pem_all.c
+++ b/crypto/pem/pem_all.c
@@ -179,5 +179,49 @@ EC_KEY *PEM_read_ECPrivateKey(FILE *fp, EC_KEY **eckey, pem_password_cb *cb,
 
 IMPLEMENT_PEM_write(DHparams, DH, PEM_STRING_DHPARAMS, DHparams)
 IMPLEMENT_PEM_write(DHxparams, DH, PEM_STRING_DHXPARAMS, DHxparams)
+
+/* Transparently read in PKCS#3 or X9.42 DH parameters */
+
+DH *PEM_read_bio_DHparams(BIO *bp, DH **x, pem_password_cb *cb, void *u)
+{
+    char *nm = NULL;
+    const unsigned char *p = NULL;
+    unsigned char *data = NULL;
+    long len;
+    DH *ret = NULL;
+
+    if (!PEM_bytes_read_bio(&data, &len, &nm, PEM_STRING_DHPARAMS, bp, cb, u))
+        return NULL;
+    p = data;
+
+    if (strcmp(nm, PEM_STRING_DHXPARAMS) == 0)
+        ret = d2i_DHxparams(x, &p, len);
+    else
+        ret = d2i_DHparams(x, &p, len);
+
+    if (ret == NULL)
+        PEMerr(PEM_F_PEM_READ_BIO_DHPARAMS, ERR_R_ASN1_LIB);
+    OPENSSL_free(nm);
+    OPENSSL_free(data);
+    return ret;
+}
+
+# ifndef OPENSSL_NO_STDIO
+DH *PEM_read_DHparams(FILE *fp, DH **x, pem_password_cb *cb, void *u)
+{
+    BIO *b;
+    DH *ret;
+
+    if ((b = BIO_new(BIO_s_file())) == NULL) {
+        PEMerr(PEM_F_PEM_READ_DHPARAMS, ERR_R_BUF_LIB);
+        return 0;
+    }
+    BIO_set_fp(b, fp, BIO_NOCLOSE);
+    ret = PEM_read_bio_DHparams(b, x, cb, u);
+    BIO_free(b);
+    return ret;
+}
+# endif
+
 #endif
 IMPLEMENT_PEM_provided_write(PUBKEY, EVP_PKEY, PEM_STRING_PUBLIC, PUBKEY)
diff --git a/crypto/pem/pem_pkey.c b/crypto/pem/pem_pkey.c
index 91f6f30cf6..e6c07b8fd6 100644
--- a/crypto/pem/pem_pkey.c
+++ b/crypto/pem/pem_pkey.c
@@ -67,6 +67,9 @@ static EVP_PKEY *pem_read_bio_key(BIO *bp, EVP_PKEY **x,
 # endif
 #endif
 
+    if (!OSSL_STORE_expect(ctx, expected_store_info_type))
+        goto err;
+
     while (!OSSL_STORE_eof(ctx)
            && (info = OSSL_STORE_load(ctx)) != NULL) {
         if (OSSL_STORE_INFO_get_type(info) == expected_store_info_type) {
@@ -263,64 +266,3 @@ int PEM_write_PrivateKey(FILE *fp, const EVP_PKEY *x, const EVP_CIPHER *enc,
 }
 
 #endif
-
-#ifndef OPENSSL_NO_DH
-
-/* Transparently read in PKCS#3 or X9.42 DH parameters */
-
-DH *PEM_read_bio_DHparams(BIO *bp, DH **x, pem_password_cb *cb, void *u)
-{
-    DH *ret = NULL;
-    EVP_PKEY *pkey = NULL;
-    OSSL_STORE_CTX *ctx = NULL;
-    OSSL_STORE_INFO *info = NULL;
-    UI_METHOD *ui_method = NULL;
-
-    if ((ui_method = UI_UTIL_wrap_read_pem_callback(cb, 0)) == NULL)
-        return NULL;
-
-    if ((ctx = OSSL_STORE_attach(bp, "file", NULL, NULL, ui_method, u,
-                                 NULL, NULL)) == NULL)
-        goto err;
-
-    while (!OSSL_STORE_eof(ctx) && (info = OSSL_STORE_load(ctx)) != NULL) {
-        if (OSSL_STORE_INFO_get_type(info) == OSSL_STORE_INFO_PARAMS) {
-            pkey = OSSL_STORE_INFO_get0_PARAMS(info);
-            if (EVP_PKEY_id(pkey) == EVP_PKEY_DHX
-                || EVP_PKEY_id(pkey) == EVP_PKEY_DH) {
-                ret = EVP_PKEY_get1_DH(pkey);
-                break;
-            }
-        }
-        OSSL_STORE_INFO_free(info);
-        info = NULL;
-    }
-
-    if (ret != NULL && x != NULL)
-        *x = ret;
-
- err:
-    OSSL_STORE_close(ctx);
-    UI_destroy_method(ui_method);
-    OSSL_STORE_INFO_free(info);
-    return ret;
-}
-
-# ifndef OPENSSL_NO_STDIO
-DH *PEM_read_DHparams(FILE *fp, DH **x, pem_password_cb *cb, void *u)
-{
-    BIO *b;
-    DH *ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
-        PEMerr(PEM_F_PEM_READ_DHPARAMS, ERR_R_BUF_LIB);
-        return 0;
-    }
-    BIO_set_fp(b, fp, BIO_NOCLOSE);
-    ret = PEM_read_bio_DHparams(b, x, cb, u);
-    BIO_free(b);
-    return ret;
-}
-# endif
-
-#endif
diff --git a/crypto/store/store_result.c b/crypto/store/store_result.c
index 5848761a5e..872efd56bb 100644
--- a/crypto/store/store_result.c
+++ b/crypto/store/store_result.c
@@ -253,9 +253,29 @@ static EVP_PKEY *try_key_value(struct extracted_param_data_st *data,
     OSSL_DECODER_CTX *decoderctx = NULL;
     const unsigned char *pdata = data->octet_data;
     size_t pdatalen = data->octet_data_size;
+    int selection = 0;
+
+    switch (ctx->expected_type) {
+    case 0:
+        break;
+    case OSSL_STORE_INFO_PARAMS:
+        selection = OSSL_KEYMGMT_SELECT_ALL_PARAMETERS;
+        break;
+    case OSSL_STORE_INFO_PUBKEY:
+        selection =
+            OSSL_KEYMGMT_SELECT_PUBLIC_KEY
+            | OSSL_KEYMGMT_SELECT_ALL_PARAMETERS;
+        break;
+    case OSSL_STORE_INFO_PKEY:
+        selection = OSSL_KEYMGMT_SELECT_ALL;
+        break;
+    default:
+        return NULL;
+    }
 
     decoderctx =
-        OSSL_DECODER_CTX_new_by_EVP_PKEY(&pk, "DER", NULL, libctx, propq);
+        OSSL_DECODER_CTX_new_by_EVP_PKEY(&pk, NULL, NULL, NULL, selection,
+                                         libctx, propq);
     (void)OSSL_DECODER_CTX_set_passphrase_cb(decoderctx, cb, cbarg);
 
     /* No error if this couldn't be decoded */
@@ -280,14 +300,20 @@ static EVP_PKEY *try_key_value_legacy(struct extracted_param_data_st *data,
 
     SET_ERR_MARK();
     /* Try PUBKEY first, that's a real easy target */
-    derp = der;
-    pk = d2i_PUBKEY_ex(NULL, &derp, der_len, libctx, propq);
-    if (pk != NULL)
-        *store_info_new = OSSL_STORE_INFO_new_PUBKEY;
-    RESET_ERR_MARK();
+    if (ctx->expected_type == 0
+        || ctx->expected_type == OSSL_STORE_INFO_PUBKEY) {
+        derp = der;
+        pk = d2i_PUBKEY_ex(NULL, &derp, der_len, libctx, propq);
+        if (pk != NULL)
+            *store_info_new = OSSL_STORE_INFO_new_PUBKEY;
+
+        RESET_ERR_MARK();
+    }
 
     /* Try private keys next */
-    if (pk == NULL) {
+    if (pk == NULL
+        && (ctx->expected_type == 0
+            || ctx->expected_type == OSSL_STORE_INFO_PKEY)) {
         unsigned char *new_der = NULL;
         X509_SIG *p8 = NULL;
         PKCS8_PRIV_KEY_INFO *p8info = NULL;
diff --git a/crypto/trace.c b/crypto/trace.c
index a8316c12b1..46a1800753 100644
--- a/crypto/trace.c
+++ b/crypto/trace.c
@@ -136,6 +136,7 @@ static const struct trace_category_st trace_categories[] = {
     TRACE_CATEGORY_(X509V3_POLICY),
     TRACE_CATEGORY_(BN_CTX),
     TRACE_CATEGORY_(STORE),
+    TRACE_CATEGORY_(DECODER),
 };
 
 const char *OSSL_trace_get_category_name(int num)
diff --git a/doc/man3/OSSL_DECODER_CTX.pod b/doc/man3/OSSL_DECODER_CTX.pod
index 27e0a67a96..aa5dc90893 100644
--- a/doc/man3/OSSL_DECODER_CTX.pod
+++ b/doc/man3/OSSL_DECODER_CTX.pod
@@ -7,7 +7,9 @@ OSSL_DECODER_CTX_new,
 OSSL_DECODER_settable_ctx_params,
 OSSL_DECODER_CTX_set_params,
 OSSL_DECODER_CTX_free,
+OSSL_DECODER_CTX_set_selection,
 OSSL_DECODER_CTX_set_input_type,
+OSSL_DECODER_CTX_set_input_structure,
 OSSL_DECODER_CTX_add_decoder,
 OSSL_DECODER_CTX_add_extra,
 OSSL_DECODER_CTX_get_num_decoders,
@@ -23,7 +25,8 @@ OSSL_DECODER_CTX_get_cleanup,
 OSSL_DECODER_export,
 OSSL_DECODER_INSTANCE_get_decoder,
 OSSL_DECODER_INSTANCE_get_decoder_ctx,
-OSSL_DECODER_INSTANCE_get_input_type
+OSSL_DECODER_INSTANCE_get_input_type,
+OSSL_DECODER_INSTANCE_get_input_structure
 - Decoder context routines
 
 =head1 SYNOPSIS
@@ -38,8 +41,11 @@ OSSL_DECODER_INSTANCE_get_input_type
                                  const OSSL_PARAM params[]);
  void OSSL_DECODER_CTX_free(OSSL_DECODER_CTX *ctx);
 
+ int OSSL_DECODER_CTX_set_selection(OSSL_DECODER_CTX *ctx, int selection);
  int OSSL_DECODER_CTX_set_input_type(OSSL_DECODER_CTX *ctx,
                                      const char *input_type);
+ int OSSL_DECODER_CTX_set_input_structure(OSSL_DECODER_CTX *ctx,
+                                          const char *input_structure);
  int OSSL_DECODER_CTX_add_decoder(OSSL_DECODER_CTX *ctx, OSSL_DECODER *decoder);
  int OSSL_DECODER_CTX_add_extra(OSSL_DECODER_CTX *ctx);
  int OSSL_DECODER_CTX_get_num_decoders(OSSL_DECODER_CTX *ctx);
@@ -51,6 +57,8 @@ OSSL_DECODER_INSTANCE_get_input_type
  OSSL_DECODER_INSTANCE_get_decoder_ctx(OSSL_DECODER_INSTANCE *decoder_inst);
  const char *
  OSSL_DECODER_INSTANCE_get_input_type(OSSL_DECODER_INSTANCE *decoder_inst);
+ OSSL_DECODER_INSTANCE_get_input_structure(OSSL_DECODER_INSTANCE *decoder_inst,
+                                           int *was_set);
 
  typedef int OSSL_DECODER_CONSTRUCT(OSSL_DECODER_INSTANCE *decoder_inst,
                                     const OSSL_PARAM *object,
@@ -128,6 +136,11 @@ OSSL_DECODER_CTX_set_input_type() sets the starting input type.  This limits
 the decoder chains to be considered, as explained in the general description
 above.
 
+OSSL_DECODER_CTX_set_input_structure() sets the name of the structure that
+the input is expected to have.  This may be used to determines what decoder
+implementations may be used.  NULL is a valid input structure, when it's not
+relevant, or when the decoder implementations are expected to figure it out.
+
 OSSL_DECODER_CTX_get_num_decoders() gets the number of decoders currently
 added to the context I<ctx>.
 
@@ -179,14 +192,19 @@ constructed, otherwise 0.
 
 These utility functions may be used by a constructor:
 
-OSSL_DECODER_INSTANCE_get_decoder() can be used to get the decoder method
-from a decoder instance I<decoder_inst>.
+OSSL_DECODER_INSTANCE_get_decoder() can be used to get the decoder
+implementation from a decoder instance I<decoder_inst>.
 
 OSSL_DECODER_INSTANCE_get_decoder_ctx() can be used to get the decoder
-method's provider context from a decoder instance I<decoder_inst>.
+implementation's provider context from a decoder instance I<decoder_inst>.
 
 OSSL_DECODER_INSTANCE_get_input_type() can be used to get the decoder
-method's input type from a decoder instance I<decoder_inst>.
+implementation's input type from a decoder instance I<decoder_inst>.
+
+OSSL_DECODER_INSTANCE_get_input_structure() can be used to get the input
+structure for the decoder implementation from a decoder instance
+I<decoder_inst>.
+This may be NULL.
 
 =head1 RETURN VALUES
 
diff --git a/include/openssl/core_dispatch.h b/include/openssl/core_dispatch.h
index 3b0cf3d3ed..cc8e6712ed 100644
--- a/include/openssl/core_dispatch.h
+++ b/include/openssl/core_dispatch.h
@@ -789,8 +789,9 @@ OSSL_CORE_MAKE_FUNC(void, encoder_free_object, (void *obj))
 # define OSSL_FUNC_DECODER_GETTABLE_PARAMS             4
 # define OSSL_FUNC_DECODER_SET_CTX_PARAMS              5
 # define OSSL_FUNC_DECODER_SETTABLE_CTX_PARAMS         6
-# define OSSL_FUNC_DECODER_DECODE                     10
-# define OSSL_FUNC_DECODER_EXPORT_OBJECT              11
+# define OSSL_FUNC_DECODER_DOES_SELECTION             10
+# define OSSL_FUNC_DECODER_DECODE                     11
+# define OSSL_FUNC_DECODER_EXPORT_OBJECT              20
 OSSL_CORE_MAKE_FUNC(void *, decoder_newctx, (void *provctx))
 OSSL_CORE_MAKE_FUNC(void, decoder_freectx, (void *ctx))
 OSSL_CORE_MAKE_FUNC(int, decoder_get_params, (OSSL_PARAM params[]))
@@ -801,8 +802,10 @@ OSSL_CORE_MAKE_FUNC(int, decoder_set_ctx_params,
 OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, decoder_settable_ctx_params,
                     (void *provctx))
 
+OSSL_CORE_MAKE_FUNC(int, decoder_does_selection,
+                    (void *provctx, int selection))
 OSSL_CORE_MAKE_FUNC(int, decoder_decode,
-                    (void *ctx, OSSL_CORE_BIO *in,
+                    (void *ctx, OSSL_CORE_BIO *in, int selection,
                      OSSL_CALLBACK *metadata_cb, void *metadata_cbarg,
                      OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg))
 OSSL_CORE_MAKE_FUNC(int, decoder_export_object,
diff --git a/include/openssl/core_names.h b/include/openssl/core_names.h
index 43be4ae145..11a4168cc1 100644
--- a/include/openssl/core_names.h
+++ b/include/openssl/core_names.h
@@ -457,8 +457,9 @@ extern "C" {
 #define OSSL_ENCODER_PARAM_INPUT_TYPE   "input-type"
 #define OSSL_ENCODER_PARAM_OUTPUT_TYPE  "output-type"
 
-#define OSSL_DECODER_PARAM_PROPERTIES   OSSL_ALG_PARAM_PROPERTIES
-#define OSSL_DECODER_PARAM_INPUT_TYPE   "input-type"
+#define OSSL_DECODER_PARAM_PROPERTIES       OSSL_ALG_PARAM_PROPERTIES
+#define OSSL_DECODER_PARAM_INPUT_TYPE       "input-type"
+#define OSSL_DECODER_PARAM_INPUT_STRUCTURE  "input-structure"
 
 /* Passphrase callback parameters */
 #define OSSL_PASSPHRASE_PARAM_INFO      "info"
diff --git a/include/openssl/decoder.h b/include/openssl/decoder.h
index 1c6bc8e498..3c3a9a1ea2 100644
--- a/include/openssl/decoder.h
+++ b/include/openssl/decoder.h
@@ -68,8 +68,11 @@ int OSSL_DECODER_CTX_set_passphrase_ui(OSSL_DECODER_CTX *ctx,
  * These will discover all provided methods
  */
 
+int OSSL_DECODER_CTX_set_selection(OSSL_DECODER_CTX *ctx, int selection);
 int OSSL_DECODER_CTX_set_input_type(OSSL_DECODER_CTX *ctx,
                                     const char *input_type);
+int OSSL_DECODER_CTX_set_input_structure(OSSL_DECODER_CTX *ctx,
+                                         const char *input_structure);
 int OSSL_DECODER_CTX_add_decoder(OSSL_DECODER_CTX *ctx, OSSL_DECODER *decoder);
 int OSSL_DECODER_CTX_add_extra(OSSL_DECODER_CTX *ctx,
                                OSSL_LIB_CTX *libctx, const char *propq);
@@ -82,6 +85,9 @@ void *
 OSSL_DECODER_INSTANCE_get_decoder_ctx(OSSL_DECODER_INSTANCE *decoder_inst);
 const char *
 OSSL_DECODER_INSTANCE_get_input_type(OSSL_DECODER_INSTANCE *decoder_inst);
+const char *
+OSSL_DECODER_INSTANCE_get_input_structure(OSSL_DECODER_INSTANCE *decoder_inst,
+                                          int *was_set);
 
 typedef int OSSL_DECODER_CONSTRUCT(OSSL_DECODER_INSTANCE *decoder_inst,
                                    const OSSL_PARAM *params,
@@ -115,7 +121,9 @@ int OSSL_DECODER_from_data(OSSL_DECODER_CTX *ctx, const unsigned char **pdata,
  */
 OSSL_DECODER_CTX *
 OSSL_DECODER_CTX_new_by_EVP_PKEY(EVP_PKEY **pkey,
-                                 const char *input_type, const char *keytype,
+                                 const char *input_type,
+                                 const char *input_struct,
+                                 const char *keytype, int selection,
                                  OSSL_LIB_CTX *libctx, const char *propquery);
 
 # ifdef __cplusplus
diff --git a/include/openssl/trace.h b/include/openssl/trace.h
index e1c66e00f8..d3e1e95df2 100644
--- a/include/openssl/trace.h
+++ b/include/openssl/trace.h
@@ -53,7 +53,8 @@ extern "C" {
 # define OSSL_TRACE_CATEGORY_BN_CTX             12
 # define OSSL_TRACE_CATEGORY_CMP                13
 # define OSSL_TRACE_CATEGORY_STORE              14
-# define OSSL_TRACE_CATEGORY_NUM                15
+# define OSSL_TRACE_CATEGORY_DECODER            15
+# define OSSL_TRACE_CATEGORY_NUM                16
 
 /* Returns the trace category number for the given |name| */
 int OSSL_trace_get_category_num(const char *name);
diff --git a/providers/baseprov.c b/providers/baseprov.c
index 019caf10d6..18d664aa29 100644
--- a/providers/baseprov.c
+++ b/providers/baseprov.c
@@ -80,15 +80,11 @@ static const OSSL_ALGORITHM base_encoder[] = {
 #undef ENCODER
 
 static const OSSL_ALGORITHM base_decoder[] = {
-#define DECODER(name, _fips, _input, func_table)                            \
-    { name,                                                                 \
-      "provider=base,fips=" _fips ",input=" _input,                         \
-      (func_table) }
-
+#define DECODER_PROVIDER "base"
 #include "decoders.inc"
     { NULL, NULL, NULL }
+#undef DECODER_PROVIDER
 };
-#undef DECODER
 
 static const OSSL_ALGORITHM base_store[] = {
 #define STORE(name, _fips, func_table)                           \
diff --git a/providers/decoders.inc b/providers/decoders.inc
index 4f5699418b..a9119cad79 100644
--- a/providers/decoders.inc
+++ b/providers/decoders.inc
@@ -7,36 +7,78 @@
  * https://www.openssl.org/source/license.html
  */
 
-#ifndef DECODER
-# error Macro DECODER undefined
+#ifndef DECODER_PROVIDER
+# error Macro DECODER_PROVIDER undefined
 #endif
 
+#define DECODER_STRUCTURE_type_specific_keypair         "type-specific"
+#define DECODER_STRUCTURE_type_specific_params          "type-specific"
+#define DECODER_STRUCTURE_type_specific                 "type-specific"
+#define DECODER_STRUCTURE_type_specific_no_pub          "type-specific"
+#define DECODER_STRUCTURE_PKCS8                         "pkcs8"
+#define DECODER_STRUCTURE_SubjectPublicKeyInfo          "SubjectPublicKeyInfo"
+#define DECODER_STRUCTURE_DH                            "dh"
+#define DECODER_STRUCTURE_DHX                           "dhx"
+#define DECODER_STRUCTURE_DSA                           "dsa"
+#define DECODER_STRUCTURE_EC                            "ec"
+#define DECODER_STRUCTURE_RSA                           "rsa"
+
+/* Arguments are prefixed with '_' to avoid build breaks on certain platforms */
+#define DECODER(_name, _input, _output, _fips)                          \
+    { _name,                                                            \
+      "provider=" DECODER_PROVIDER ",fips=" #_fips ",input=" #_input,   \
+      (ossl_##_input##_to_##_output##_decoder_functions) }
+#define DECODER_w_structure(_name, _input, _structure, _output, _fips)  \
+    { _name,                                                            \
+      "provider=" DECODER_PROVIDER ",fips=" #_fips ",input=" #_input    \
+      ",structure=" DECODER_STRUCTURE_##_structure,                     \
+      (ossl_##_structure##_##_input##_to_##_output##_decoder_functions) }
+
 #ifndef OPENSSL_NO_DH
-    DECODER("DH", "yes", "der", ossl_der_to_dh_decoder_functions),
-    DECODER("DHX", "yes", "der", ossl_der_to_dhx_decoder_functions),
+DECODER_w_structure("DH", der, PKCS8, dh, yes),
+DECODER_w_structure("DH", der, SubjectPublicKeyInfo, dh, yes),
+DECODER_w_structure("DH", der, type_specific_params, dh, yes),
+DECODER_w_structure("DH", der, DH, dh, yes),
+DECODER_w_structure("DHX", der, PKCS8, dhx, yes),
+DECODER_w_structure("DHX", der, SubjectPublicKeyInfo, dhx, yes),
+DECODER_w_structure("DHX", der, type_specific_params, dhx, yes),
+DECODER_w_structure("DHX", der, DHX, dhx, yes),
 #endif
 #ifndef OPENSSL_NO_DSA
-    DECODER("DSA", "yes", "der", ossl_der_to_dsa_decoder_functions),
-    DECODER("DSA", "yes", "mblob", ossl_msblob_to_dsa_decoder_functions),
+DECODER_w_structure("DSA", der, PKCS8, dsa, yes),
+DECODER_w_structure("DSA", der, SubjectPublicKeyInfo, dsa, yes),
+DECODER_w_structure("DSA", der, type_specific, dsa, yes),
+DECODER_w_structure("DSA", der, DSA, dsa, yes),
+DECODER("DSA", msblob, dsa, yes),
 # ifndef OPENSSL_NO_RC4
-    DECODER("DSA", "yes", "pvk", ossl_pvk_to_dsa_decoder_functions),
+DECODER("DSA", pvk, dsa, yes),
 # endif
 #endif
 #ifndef OPENSSL_NO_EC
-    DECODER("EC", "yes", "der", ossl_der_to_ec_decoder_functions),
-    DECODER("ED25519", "yes", "der", ossl_der_to_ed25519_decoder_functions),
-    DECODER("ED448", "yes", "der", ossl_der_to_ed448_decoder_functions),
-    DECODER("X25519", "yes", "der", ossl_der_to_x25519_decoder_functions),
-    DECODER("X448", "yes", "der", ossl_der_to_x448_decoder_functions),
+DECODER_w_structure("EC", der, PKCS8, ec, yes),
+DECODER_w_structure("EC", der, SubjectPublicKeyInfo, ec, yes),
+DECODER_w_structure("EC", der, type_specific_no_pub, ec, yes),
+DECODER_w_structure("EC", der, EC, ec, yes),
+DECODER_w_structure("ED25519", der, PKCS8, ed25519, yes),
+DECODER_w_structure("ED25519", der, SubjectPublicKeyInfo, ed25519, yes),
+DECODER_w_structure("ED448", der, PKCS8, ed448, yes),
+DECODER_w_structure("ED448", der, SubjectPublicKeyInfo, ed448, yes),
+DECODER_w_structure("X25519", der, PKCS8, x25519, yes),
+DECODER_w_structure("X25519", der, SubjectPublicKeyInfo, x25519, yes),
+DECODER_w_structure("X448", der, PKCS8, x448, yes),
+DECODER_w_structure("X448", der, SubjectPublicKeyInfo, x448, yes),
 #endif
-    DECODER("RSA", "yes", "der", ossl_der_to_rsa_decoder_functions),
-    DECODER("RSA-PSS", "yes", "der", ossl_der_to_rsapss_decoder_functions),
+DECODER_w_structure("RSA", der, PKCS8, rsa, yes),
+DECODER_w_structure("RSA", der, SubjectPublicKeyInfo, rsa, yes),
+DECODER_w_structure("RSA", der, type_specific_keypair, rsa, yes),
+DECODER_w_structure("RSA", der, RSA, rsa, yes),
+DECODER_w_structure("RSA-PSS", der, PKCS8, rsapss, yes),
+DECODER_w_structure("RSA-PSS", der, SubjectPublicKeyInfo, rsapss, yes),
 #ifndef OPENSSL_NO_DSA
-    DECODER("RSA", "yes", "mblob", ossl_msblob_to_rsa_decoder_functions),
+DECODER("RSA", msblob, rsa, yes),
 # ifndef OPENSSL_NO_RC4
-    DECODER("RSA", "yes", "pvk", ossl_pvk_to_rsa_decoder_functions),
+DECODER("RSA", pvk, rsa, yes),
 # endif
 #endif
 
-    DECODER("DER", "yes", "pem", ossl_pem_to_der_decoder_functions),
-
+DECODER("DER", pem, der, yes),
diff --git a/providers/defltprov.c b/providers/defltprov.c
index 425ec9a6c8..8ce6c92b3e 100644
--- a/providers/defltprov.c
+++ b/providers/defltprov.c
@@ -453,15 +453,11 @@ static const OSSL_ALGORITHM deflt_encoder[] = {
 #undef ENCODER
 
 static const OSSL_ALGORITHM deflt_decoder[] = {
-#define DECODER(name, _fips, _input, func_table)                            \
-    { name,                                                                 \
-      "provider=default,fips=" _fips ",input=" _input,                      \
-      (func_table) }
-
+#define DECODER_PROVIDER "default"
 #include "decoders.inc"
     { NULL, NULL, NULL }
+#undef DECODER_PROVIDER
 };
-#undef DECODER
 
 static const OSSL_ALGORITHM deflt_store[] = {
 #define STORE(name, _fips, func_table)                           \
diff --git a/providers/implementations/encode_decode/decode_der2key.c b/providers/implementations/encode_decode/decode_der2key.c
index fed4ae0720..17ed16235d 100644
--- a/providers/implementations/encode_decode/decode_der2key.c
+++ b/providers/implementations/encode_decode/decode_der2key.c
@@ -103,23 +103,38 @@ static int der_from_p8(unsigned char **new_der, long *new_der_len,
 /* ---------------------------------------------------------------------- */
 
 static OSSL_FUNC_decoder_freectx_fn der2key_freectx;
-static OSSL_FUNC_decoder_gettable_params_fn der2key_gettable_params;
-static OSSL_FUNC_decoder_get_params_fn der2key_get_params;
 static OSSL_FUNC_decoder_decode_fn der2key_decode;
 static OSSL_FUNC_decoder_export_object_fn der2key_export_object;
 
 typedef void *(extract_key_fn)(EVP_PKEY *);
 typedef void (free_key_fn)(void *);
 struct keytype_desc_st {
-    int type;                 /* EVP key type */
-    const char *name;         /* Keytype */
+    const char *keytype_name;
     const OSSL_DISPATCH *fns; /* Keymgmt (to pilfer functions from) */
 
+    /* The input structure name */
+    const char *structure_name;
+
+    /*
+     * The EVP_PKEY_xxx type macro.  Should be zero for type specific
+     * structures, non-zero when the outermost structure is PKCS#8 or
+     * SubjectPublicKeyInfo.  This determines which of the function
+     * pointers below will be used.
+     */
+    int evp_type;
+
+    /* The selection mask for OSSL_FUNC_decoder_does_selection() */
+    int selection_mask;
+
+    /* For type specific decoders, we use the corresponding d2i */
+    d2i_of_void *d2i_private_key;
+    d2i_of_void *d2i_public_key;
+    d2i_of_void *d2i_key_params;
     /*
-     * These must be the correct EVP_PKEY_get1_{TYPE}() and {TYPE}_free()
-     * function for the key.
+     * For PKCS#8 decoders, we use EVP_PKEY extractors, EVP_PKEY_get1_{TYPE}()
      */
     extract_key_fn *extract_key;
+    /* {type}_free() */
     free_key_fn *free_key;
 };
 
@@ -150,28 +165,74 @@ static void der2key_freectx(void *vctx)
     OPENSSL_free(ctx);
 }
 
-static const OSSL_PARAM *der2key_gettable_params(void *provctx)
+static const OSSL_PARAM *
+der2key_gettable_params(void *provctx, const struct keytype_desc_st *desc)
 {
     static const OSSL_PARAM gettables[] = {
         { OSSL_DECODER_PARAM_INPUT_TYPE, OSSL_PARAM_UTF8_PTR, NULL, 0, 0 },
         OSSL_PARAM_END,
     };
+    static const OSSL_PARAM gettables_w_structure[] = {
+        { OSSL_DECODER_PARAM_INPUT_TYPE, OSSL_PARAM_UTF8_PTR, NULL, 0, 0 },
+        { OSSL_DECODER_PARAM_INPUT_STRUCTURE, OSSL_PARAM_UTF8_PTR, NULL, 0, 0 },
+        OSSL_PARAM_END,
+    };
 
-    return gettables;
+    return desc->structure_name != NULL ? gettables_w_structure :  gettables;
 }
 
-static int der2key_get_params(OSSL_PARAM params[])
+static int der2key_get_params(OSSL_PARAM params[],
+                              const struct keytype_desc_st *desc)
 {
     OSSL_PARAM *p;
 
     p = OSSL_PARAM_locate(params, OSSL_DECODER_PARAM_INPUT_TYPE);
     if (p != NULL && !OSSL_PARAM_set_utf8_ptr(p, "DER"))
         return 0;
+    if (desc->structure_name != NULL) {
+        p = OSSL_PARAM_locate(params, OSSL_DECODER_PARAM_INPUT_STRUCTURE);
+        if (p != NULL && !OSSL_PARAM_set_utf8_ptr(p, desc->structure_name))
+            return 0;
+    }
 
     return 1;
 }
 
-static int der2key_decode(void *vctx, OSSL_CORE_BIO *cin,
+static int der2key_check_selection(int selection,
+                                   const struct keytype_desc_st *desc)
+{
+    /*
+     * The selections are kinda sorta "levels", i.e. each selection given
+     * here is assumed to include those following.
+     */
+    int checks[] = {
+        OSSL_KEYMGMT_SELECT_PRIVATE_KEY,
+        OSSL_KEYMGMT_SELECT_PUBLIC_KEY,
+        OSSL_KEYMGMT_SELECT_ALL_PARAMETERS
+    };
+    size_t i;
+
+    /* The decoder implementations made here support guessing */
+    if (selection == 0)
+        return 1;
+
+    for (i = 0; i < OSSL_NELEM(checks); i++) {
+        int check1 = (selection & checks[i]) != 0;
+        int check2 = (desc->selection_mask & checks[i]) != 0;
+
+        /*
+         * If the caller asked for the currently checked bit(s), return
+         * whether the decoder description says it's supported.
+         */
+        if (check1)
+            return check2;
+    }
+
+    /* This should be dead code, but just to be safe... */
+    return 0;
+}
+
+static int der2key_decode(void *vctx, OSSL_CORE_BIO *cin, int selection,
                           OSSL_CALLBACK *data_cb, void *data_cbarg,
                           OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
 {
@@ -184,63 +245,110 @@ static int der2key_decode(void *vctx, OSSL_CORE_BIO *cin,
     long new_der_len;
     EVP_PKEY *pkey = NULL;
     void *key = NULL;
+    int orig_selection = selection;
     int ok = 0;
 
-    SET_ERR_MARK();
-    if (!read_der(ctx->provctx, cin, &der, &der_len))
-        goto err;
-
     /*
-     * Opportunistic attempt to decrypt.  If it doesn't work, we try to
-     * decode our input unencrypted.
+     * The caller is allowed to specify 0 as a selection mark, to have the
+     * structure and key type guessed.  For type-specific structures, this
+     * is not recommended, as some structures are very similar.
+     * Note that 0 isn't the same as OSSL_KEYMGMT_SELECT_ALL, as the latter
+     * signifies a private key structure, where everything else is assumed
+     * to be present as well.
      */
-    if (der_from_p8(&new_der, &new_der_len, der, der_len, pw_cb, pw_cbarg)) {
-        OPENSSL_free(der);
-        der = new_der;
-        der_len = new_der_len;
+    if (selection == 0)
+        selection = ctx->desc->selection_mask;
+    if ((selection & ctx->desc->selection_mask) == 0) {
+        ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
     }
-    RESET_ERR_MARK();
 
-    derp = der;
-    pkey = d2i_PrivateKey_ex(ctx->desc->type, NULL, &derp, der_len,
-                             libctx, NULL);
-    if (pkey == NULL) {
-        RESET_ERR_MARK();
-        derp = der;
-        pkey = d2i_PUBKEY_ex(NULL, &derp, der_len, libctx, NULL);
-    }
+    SET_ERR_MARK();
+    if (!read_der(ctx->provctx, cin, &der, &der_len))
+        goto end;
 
-    if (pkey == NULL) {
-        RESET_ERR_MARK();
+    if (ctx->desc->extract_key == NULL) {
+        /*
+         * There's no EVP_PKEY extractor, so we use the type specific
+         * functions.
+         */
         derp = der;
-        pkey = d2i_KeyParams(ctx->desc->type, NULL, &derp, der_len);
-    }
- err:
-    /*
-     * Prune low-level ASN.1 parse errors from error queue, assuming that
-     * this is called by decoder_process() in a loop trying several formats.
-     */
-    CLEAR_ERR_MARK();
-
-    if (pkey != NULL) {
+        if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+            key = ctx->desc->d2i_private_key(NULL, &derp, der_len);
+            if (key == NULL && orig_selection != 0)
+                goto end;
+        }
+        if (key == NULL
+            && (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+            key = ctx->desc->d2i_public_key(NULL, &derp, der_len);
+            if (key == NULL && orig_selection != 0)
+                goto end;
+        }
+        if (key == NULL
+            && (selection & OSSL_KEYMGMT_SELECT_ALL_PARAMETERS) != 0) {
+            key = ctx->desc->d2i_key_params(NULL, &derp, der_len);
+        }
+    } else {
         /*
-         * Tear out the low-level key pointer from the pkey,
-         * but only if it matches the expected key type.
-         *
-         * TODO(3.0): The check should be done with EVP_PKEY_is_a(), but
-         * as long as we still have #legacy internal keys, it's safer to
-         * use the type numbers inside the provider.
+         * There is a EVP_PKEY extractor, so we use the more generic
+         * EVP_PKEY functions, since they know how to unpack PKCS#8 and
+         * SubjectPublicKeyInfo.
          */
-        if (EVP_PKEY_id(pkey) == ctx->desc->type)
-            key = ctx->desc->extract_key(pkey);
 
         /*
-         * ctx->desc->extract_key() is expected to have incremented |key|'s
-         * reference count, so it should be safe to free |pkey| now.
+         * Opportunistic attempt to decrypt.  If it doesn't work, we try
+         * to decode our input unencrypted.
          */
-        EVP_PKEY_free(pkey);
+        if (der_from_p8(&new_der, &new_der_len, der, der_len,
+                        pw_cb, pw_cbarg)) {
+            OPENSSL_free(der);
+            der = new_der;
+            der_len = new_der_len;
+        }
+        RESET_ERR_MARK();
+
+        if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+            derp = der;
+            pkey = d2i_PrivateKey_ex(ctx->desc->evp_type, NULL, &derp, der_len,
+                                     libctx, NULL);
+        }
+
+        if (pkey == NULL
+            && (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+            RESET_ERR_MARK();
+            derp = der;
+            pkey = d2i_PUBKEY_ex(NULL, &derp, der_len, libctx, NULL);
+        }
+
+        if (pkey != NULL) {
+            /*
+             * Tear out the low-level key pointer from the pkey,
+             * but only if it matches the expected key type.
+             *
+             * TODO: The check should be done with EVP_PKEY_is_a(), but
+             * as long as we still have #legacy internal keys, it's safer
+             * to use the type numbers inside the provider.
+             */
+            if (EVP_PKEY_id(pkey) == ctx->desc->evp_type)
+                key = ctx->desc->extract_key(pkey);
+
+            /*
+             * ctx->desc->extract_key() is expected to have incremented
+             * |key|'s reference count, so it should be safe to free |pkey|
+             * now.
+             */
+            EVP_PKEY_free(pkey);
+        }
     }
 
+ end:
+    /*
+     * Prune low-level ASN.1 parse errors from error queue, assuming
+     * that this is called by decoder_process() in a loop trying several
+     * formats.
+     */
+    CLEAR_ERR_MARK();
+
     OPENSSL_free(der);
 
     if (key != NULL) {
@@ -251,7 +359,8 @@ static int der2key_decode(void *vctx, OSSL_CORE_BIO *cin,
             OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, &object_type);
         params[1] =
             OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE,
-                                             (char *)ctx->desc->name, 0);
+                                             (char *)ctx->desc->keytype_name,
+                                             0);
         /* The address of the key becomes the octet string */
         params[2] =
             OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_REFERENCE,
@@ -284,26 +393,279 @@ static int der2key_export_object(void *vctx,
     return 0;
 }
 
-#define IMPLEMENT_NEWCTX(KEYTYPEstr, KEYTYPE, keytype, extract, free)   \
-    static const struct keytype_desc_st keytype##_desc =                \
-        { EVP_PKEY_##KEYTYPE, KEYTYPEstr,                               \
-          ossl_##keytype##_keymgmt_functions,                           \
-          (extract_key_fn *)extract,                                    \
-          (free_key_fn *)free };                                        \
-    static OSSL_FUNC_decoder_newctx_fn der2##keytype##_newctx;          \
-    static void *der2##keytype##_newctx(void *provctx)                  \
+/* ---------------------------------------------------------------------- */
+
+#ifndef OPENSSL_NO_DH
+# define dh_evp_type                    EVP_PKEY_DH
+# define dh_evp_extract                 (extract_key_fn *)EVP_PKEY_get1_DH
+# define dh_d2i_private_key             NULL
+# define dh_d2i_public_key              NULL
+# define dh_d2i_key_params              (d2i_of_void *)d2i_DHparams
+# define dh_free                        (free_key_fn *)DH_free
+
+# define dhx_evp_type                   EVP_PKEY_DHX
+# define dhx_evp_extract                (extract_key_fn *)EVP_PKEY_get1_DH
+# define dhx_d2i_private_key            NULL
+# define dhx_d2i_public_key             NULL
+# define dhx_d2i_key_params             (d2i_of_void *)d2i_DHxparams
+# define dhx_free                       (free_key_fn *)DH_free
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+#ifndef OPENSSL_NO_DSA
+# define dsa_evp_type                   EVP_PKEY_DSA
+# define dsa_evp_extract                (extract_key_fn *)EVP_PKEY_get1_DSA
+# define dsa_d2i_private_key            (d2i_of_void *)d2i_DSAPrivateKey
+# define dsa_d2i_public_key             (d2i_of_void *)d2i_DSAPublicKey
+# define dsa_d2i_key_params             (d2i_of_void *)d2i_DSAparams
+# define dsa_free                       (free_key_fn *)DSA_free
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+#ifndef OPENSSL_NO_EC
+# define ec_evp_type                    EVP_PKEY_EC
+# define ec_evp_extract                 (extract_key_fn *)EVP_PKEY_get1_EC_KEY
+# define ec_d2i_private_key             (d2i_of_void *)d2i_ECPrivateKey
+# define ec_d2i_public_key              NULL
+# define ec_d2i_key_params              (d2i_of_void *)d2i_ECParameters
+# define ec_free                        (free_key_fn *)EC_KEY_free
+
+/*
+ * ED25519, ED448, X25519, X448 only implement PKCS#8 and SubjectPublicKeyInfo,
+ * so no d2i functions to be had.
+ */
+# define ed25519_evp_type               EVP_PKEY_ED25519
+# define ed25519_evp_extract            (extract_key_fn *)evp_pkey_get1_ED25519
+# define ed25519_d2i_private_key        NULL
+# define ed25519_d2i_public_key         NULL
+# define ed25519_d2i_key_params         NULL
+# define ed25519_free                   (free_key_fn *)ecx_key_free
+
+# define ed448_evp_type                 EVP_PKEY_ED448
+# define ed448_evp_extract              (extract_key_fn *)evp_pkey_get1_ED448
+# define ed448_d2i_private_key          NULL
+# define ed448_d2i_public_key           NULL
+# define ed448_d2i_key_params           NULL
+# define ed448_free                     (free_key_fn *)ecx_key_free
+
+# define x25519_evp_type                EVP_PKEY_X25519
+# define x25519_evp_extract             (extract_key_fn *)evp_pkey_get1_X25519
+# define x25519_d2i_private_key         NULL
+# define x25519_d2i_public_key          NULL
+# define x25519_d2i_key_params          NULL
+# define x25519_free                    (free_key_fn *)ecx_key_free
+
+# define x448_evp_type                  EVP_PKEY_X448
+# define x448_evp_extract               (extract_key_fn *)evp_pkey_get1_X448
+# define x448_d2i_private_key           NULL
+# define x448_d2i_public_key            NULL
+# define x448_d2i_key_params            NULL
+# define x448_free                      (free_key_fn *)ecx_key_free
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+#define rsa_evp_type                    EVP_PKEY_RSA
+#define rsa_evp_extract                 (extract_key_fn *)EVP_PKEY_get1_RSA
+#define rsa_d2i_private_key             (d2i_of_void *)d2i_RSAPrivateKey
+#define rsa_d2i_public_key              (d2i_of_void *)d2i_RSAPublicKey
+#define rsa_d2i_key_params              NULL
+#define rsa_free                        (free_key_fn *)RSA_free
+
+#define rsapss_evp_type                 EVP_PKEY_RSA_PSS
+#define rsapss_evp_extract              (extract_key_fn *)EVP_PKEY_get1_RSA
+#define rsapss_d2i_private_key          (d2i_of_void *)d2i_RSAPrivateKey
+#define rsapss_d2i_public_key           (d2i_of_void *)d2i_RSAPublicKey
+#define rsapss_d2i_key_params           NULL
+#define rsapss_free                     (free_key_fn *)RSA_free
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * The DO_ macros help define the selection mask and the method functions
+ * for each kind of object we want to decode.
+ */
+#define DO_type_specific_keypair(keytype)               \
+    "type-specific", 0,                                 \
+        ( OSSL_KEYMGMT_SELECT_KEYPAIR ),                \
+        keytype##_d2i_private_key,                      \
+        keytype##_d2i_public_key,                       \
+        NULL,                                           \
+        NULL,                                           \
+        keytype##_free
+
+#define DO_type_specific_pub(keytype)                   \
+    "type-specific", 0,                                 \
+        ( OSSL_KEYMGMT_SELECT_PUBLIC_KEY ),             \
+        NULL,                                           \
+        keytype##_d2i_public_key,                       \
+        NULL,                                           \
+        NULL,                                           \
+        keytype##_free
+
+#define DO_type_specific_priv(keytype)                  \
+    "type-specific", 0,                                 \
+        ( OSSL_KEYMGMT_SELECT_PRIVATE_KEY ),            \
+        keytype##_d2i_private_key,                      \
+        NULL,                                           \
+        NULL,                                           \
+        NULL,                                           \
+        keytype##_free
+
+#define DO_type_specific_params(keytype)                \
+    "type-specific", 0,                                 \
+        ( OSSL_KEYMGMT_SELECT_ALL_PARAMETERS ),         \
+        NULL,                                           \
+        NULL,                                           \
+        keytype##_d2i_key_params,                       \
+        NULL,                                           \
+        keytype##_free
+
+#define DO_type_specific(keytype)                       \
+    "type-specific", 0,                                 \
+        ( OSSL_KEYMGMT_SELECT_ALL ),                    \
+        keytype##_d2i_private_key,                      \
+        keytype##_d2i_public_key,                       \
+        keytype##_d2i_key_params,                       \
+        NULL,                                           \
+        keytype##_free
+
+#define DO_type_specific_no_pub(keytype)                \
+    "type-specific", 0,                                 \
+        ( OSSL_KEYMGMT_SELECT_PRIVATE_KEY               \
+          | OSSL_KEYMGMT_SELECT_ALL_PARAMETERS ),       \
+        keytype##_d2i_private_key,                      \
+        NULL,                                           \
+        keytype##_d2i_key_params,                       \
+        NULL,                                           \
+        keytype##_free
+
+#define DO_PKCS8(keytype)                               \
+    "pkcs8", keytype##_evp_type,                        \
+        ( OSSL_KEYMGMT_SELECT_PRIVATE_KEY ),            \
+        NULL,                                           \
+        NULL,                                           \
+        NULL,                                           \
+        keytype##_evp_extract,                          \
+        keytype##_free
+
+#define DO_SubjectPublicKeyInfo(keytype)                \
+    "SubjectPublicKeyInfo", keytype##_evp_type,         \
+        ( OSSL_KEYMGMT_SELECT_PUBLIC_KEY ),             \
+        NULL,                                           \
+        NULL,                                           \
+        NULL,                                           \
+        keytype##_evp_extract,                          \
+        keytype##_free
+
+#define DO_DH(keytype)                                  \
+    "DH", 0,                                            \
+        ( OSSL_KEYMGMT_SELECT_ALL_PARAMETERS ),         \
+        NULL,                                           \
+        NULL,                                           \
+        keytype##_d2i_key_params,                       \
+        NULL,                                           \
+        keytype##_free
+
+#define DO_DHX(keytype)                                 \
+    "DHX", 0,                                           \
+        ( OSSL_KEYMGMT_SELECT_ALL_PARAMETERS ),         \
+        NULL,                                           \
+        NULL,                                           \
+        keytype##_d2i_key_params,                       \
+        NULL,                                           \
+        keytype##_free
+
+#define DO_DSA(keytype)                                 \
+    "DSA", 0,                                           \
+        ( OSSL_KEYMGMT_SELECT_ALL ),                    \
+        keytype##_d2i_private_key,                      \
+        keytype##_d2i_public_key,                       \
+        keytype##_d2i_key_params,                       \
+        NULL,                                           \
+        keytype##_free
+
+#define DO_EC(keytype)                                  \
+    "EC", 0,                                            \
+        ( OSSL_KEYMGMT_SELECT_PRIVATE_KEY               \
+          | OSSL_KEYMGMT_SELECT_ALL_PARAMETERS ),       \
+        keytype##_d2i_private_key,                      \
+        NULL,                                           \
+        keytype##_d2i_key_params,                       \
+        NULL,                                           \
+        keytype##_free
+
+#define DO_RSA(keytype)                                 \
+    "RSA", 0,                                           \
+        ( OSSL_KEYMGMT_SELECT_KEYPAIR ),                \
+        keytype##_d2i_private_key,                      \
+        keytype##_d2i_public_key,                       \
+        NULL,                                           \
+        NULL,                                           \
+        keytype##_free
+
+/*
+ * MAKE_DECODER is the single driver for creating OSSL_DISPATCH tables.
+ * It takes the following arguments:
+ *
+ * keytype_name The implementation key type as a string.
+ * keytype      The implementation key type.  This must correspond exactly
+ *              to our existing keymgmt keytype names...  in other words,
+ *              there must exist an ossl_##keytype##_keymgmt_functions.
+ * type         The type name for the set of functions that implement the
+ *              decoder for the key type.  This isn't necessarily the same
+ *              as keytype.  For example, the key types ed25519, ed448,
+ *              x25519 and x448 are all handled by the same functions with
+ *              the common type name ecx.
+ * kind         The kind of support to implement.  This translates into
+ *              the DO_##kind macros above, to populate the keytype_desc_st
+ *              structure.
+ */
+#define MAKE_DECODER(keytype_name, keytype, type, kind)                 \
+    static const struct keytype_desc_st kind##_##keytype##_desc =       \
+        { keytype_name, ossl_##keytype##_keymgmt_functions,             \
+          DO_##kind(keytype) };                                         \
+                                                                        \
+    static OSSL_FUNC_decoder_newctx_fn kind##_der2##keytype##_newctx;   \
+    static OSSL_FUNC_decoder_gettable_params_fn                         \
+    kind##_der2##keytype##_gettable_params;                             \
+    static OSSL_FUNC_decoder_get_params_fn                              \
+    kind##_der2##keytype##_get_params;                                  \
+                                                                        \
+    static void *kind##_der2##keytype##_newctx(void *provctx)           \
+    {                                                                   \
+        return der2key_newctx(provctx, &kind##_##keytype##_desc);       \
+    }                                                                   \
+    static const OSSL_PARAM *                                           \
+    kind##_der2##keytype##_gettable_params(void *provctx)               \
+    {                                                                   \
+        return                                                          \
+            der2key_gettable_params(provctx, &kind##_##keytype##_desc); \
+    }                                                                   \
+    static int kind##_der2##keytype##_get_params(OSSL_PARAM params[])   \
+    {                                                                   \
+        return der2key_get_params(params, &kind##_##keytype##_desc);    \
+    }                                                                   \
+    static int kind##_der2##keytype##_does_selection(void *provctx,     \
+                                                     int selection)     \
     {                                                                   \
-        return der2key_newctx(provctx, &keytype##_desc);                \
+        return der2key_check_selection(selection,                       \
+                                       &kind##_##keytype##_desc);       \
     }                                                                   \
-    const OSSL_DISPATCH ossl_der_to_##keytype##_decoder_functions[] = { \
+    const OSSL_DISPATCH                                                 \
+    ossl_##kind##_der_to_##keytype##_decoder_functions[] = {            \
         { OSSL_FUNC_DECODER_NEWCTX,                                     \
-          (void (*)(void))der2##keytype##_newctx },                     \
+          (void (*)(void))kind##_der2##keytype##_newctx },              \
         { OSSL_FUNC_DECODER_FREECTX,                                    \
           (void (*)(void))der2key_freectx },                            \
         { OSSL_FUNC_DECODER_GETTABLE_PARAMS,                            \
-          (void (*)(void))der2key_gettable_params },                    \
+          (void (*)(void))kind##_der2##keytype##_gettable_params },     \
         { OSSL_FUNC_DECODER_GET_PARAMS,                                 \
-          (void (*)(void))der2key_get_params },                         \
+          (void (*)(void))kind##_der2##keytype##_get_params },          \
+        { OSSL_FUNC_DECODER_DOES_SELECTION,                             \
+          (void (*)(void))kind##_der2##keytype##_does_selection },      \
         { OSSL_FUNC_DECODER_DECODE,                                     \
           (void (*)(void))der2key_decode },                             \
         { OSSL_FUNC_DECODER_EXPORT_OBJECT,                              \
@@ -312,21 +674,38 @@ static int der2key_export_object(void *vctx,
     }
 
 #ifndef OPENSSL_NO_DH
-IMPLEMENT_NEWCTX("DH", DH, dh, EVP_PKEY_get1_DH, DH_free);
-IMPLEMENT_NEWCTX("DHX", DHX, dhx, EVP_PKEY_get1_DH, DH_free);
+MAKE_DECODER("DH", dh, dh, PKCS8);
+MAKE_DECODER("DH", dh, dh, SubjectPublicKeyInfo);
+MAKE_DECODER("DH", dh, dh, type_specific_params);
+MAKE_DECODER("DH", dh, dh, DH);
+MAKE_DECODER("DHX", dhx, dhx, PKCS8);
+MAKE_DECODER("DHX", dhx, dhx, SubjectPublicKeyInfo);
+MAKE_DECODER("DHX", dhx, dhx, type_specific_params);
+MAKE_DECODER("DHX", dhx, dhx, DHX);
 #endif
 #ifndef OPENSSL_NO_DSA
-IMPLEMENT_NEWCTX("DSA", DSA, dsa, EVP_PKEY_get1_DSA, DSA_free);
+MAKE_DECODER("DSA", dsa, dsa, PKCS8);
+MAKE_DECODER("DSA", dsa, dsa, SubjectPublicKeyInfo);
+MAKE_DECODER("DSA", dsa, dsa, type_specific);
+MAKE_DECODER("DSA", dsa, dsa, DSA);
 #endif
 #ifndef OPENSSL_NO_EC
-IMPLEMENT_NEWCTX("EC", EC, ec, EVP_PKEY_get1_EC_KEY, EC_KEY_free);
-IMPLEMENT_NEWCTX("X25519", X25519, x25519,
-                 evp_pkey_get1_X25519, ecx_key_free);
-IMPLEMENT_NEWCTX("X448", X448, x448,
-                 evp_pkey_get1_X448, ecx_key_free);
-IMPLEMENT_NEWCTX("ED25519", ED25519, ed25519,
-                 evp_pkey_get1_ED25519, ecx_key_free);
-IMPLEMENT_NEWCTX("ED448", ED448, ed448, evp_pkey_get1_ED448, ecx_key_free);
+MAKE_DECODER("EC", ec, ec, PKCS8);
+MAKE_DECODER("EC", ec, ec, SubjectPublicKeyInfo);
+MAKE_DECODER("EC", ec, ec, type_specific_no_pub);
+MAKE_DECODER("EC", ec, ec, EC);
+MAKE_DECODER("X25519", x25519, ecx, PKCS8);
+MAKE_DECODER("X25519", x25519, ecx, SubjectPublicKeyInfo);
+MAKE_DECODER("X448", x448, ecx, PKCS8);
+MAKE_DECODER("X448", x448, ecx, SubjectPublicKeyInfo);
+MAKE_DECODER("ED25519", ed25519, ecx, PKCS8);
+MAKE_DECODER("ED25519", ed25519, ecx, SubjectPublicKeyInfo);
+MAKE_DECODER("ED448", ed448, ecx, PKCS8);
+MAKE_DECODER("ED448", ed448, ecx, SubjectPublicKeyInfo);
 #endif
-IMPLEMENT_NEWCTX("RSA", RSA, rsa, EVP_PKEY_get1_RSA, RSA_free);
-IMPLEMENT_NEWCTX("RSA-PSS", RSA_PSS, rsapss, EVP_PKEY_get1_RSA, RSA_free);
+MAKE_DECODER("RSA", rsa, rsa, PKCS8);
+MAKE_DECODER("RSA", rsa, rsa, SubjectPublicKeyInfo);
+MAKE_DECODER("RSA", rsa, rsa, type_specific_keypair);
+MAKE_DECODER("RSA", rsa, rsa, RSA);
+MAKE_DECODER("RSA-PSS", rsapss, rsapss, PKCS8);
+MAKE_DECODER("RSA-PSS", rsapss, rsapss, SubjectPublicKeyInfo);
diff --git a/providers/implementations/encode_decode/decode_ms2key.c b/providers/implementations/encode_decode/decode_ms2key.c
index deb2cc2d5b..573f9c9a56 100644
--- a/providers/implementations/encode_decode/decode_ms2key.c
+++ b/providers/implementations/encode_decode/decode_ms2key.c
@@ -188,27 +188,37 @@ static int ms2key_post(struct ms2key_ctx_st *ctx, EVP_PKEY *pkey,
     return ok;
 }
 
-static int msblob2key_decode(void *vctx, OSSL_CORE_BIO *cin,
+static int msblob2key_decode(void *vctx, OSSL_CORE_BIO *cin, int selection,
                              OSSL_CALLBACK *data_cb, void *data_cbarg,
                              OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
 {
     struct ms2key_ctx_st *ctx = vctx;
     int ispub = -1;
     EVP_PKEY *pkey = read_msblob(ctx->provctx, cin, &ispub);
-    int ok = ms2key_post(ctx, pkey, data_cb, data_cbarg);
+    int ok = 0;
+
+    if (selection == 0
+        || (ispub
+            ? (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0
+            : (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0))
+        ok = ms2key_post(ctx, pkey, data_cb, data_cbarg);
 
     EVP_PKEY_free(pkey);
     return ok;
 }
 
 #ifndef OPENSSL_NO_RC4
-static int pvk2key_decode(void *vctx, OSSL_CORE_BIO *cin,
+static int pvk2key_decode(void *vctx, OSSL_CORE_BIO *cin, int selection,
                           OSSL_CALLBACK *data_cb, void *data_cbarg,
                           OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
 {
     struct ms2key_ctx_st *ctx = vctx;
     EVP_PKEY *pkey = read_pvk(ctx->provctx, cin, pw_cb, pw_cbarg);
-    int ok = ms2key_post(ctx, pkey, data_cb, data_cbarg);
+    int ok = 0;
+
+    if (selection == 0
+        || (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+        ok = ms2key_post(ctx, pkey, data_cb, data_cbarg);
 
     EVP_PKEY_free(pkey);
     return ok;
diff --git a/providers/implementations/encode_decode/decode_pem2der.c b/providers/implementations/encode_decode/decode_pem2der.c
index 9ddc0ae3bb..6c8b108290 100644
--- a/providers/implementations/encode_decode/decode_pem2der.c
+++ b/providers/implementations/encode_decode/decode_pem2der.c
@@ -106,7 +106,11 @@ static int pem2der_pass_helper(char *buf, int num, int w, void *data)
     return (int)plen;
 }
 
-static int pem2der_decode(void *vctx, OSSL_CORE_BIO *cin,
+/*
+ * The selection parameter in pem2der_decode() is not used by this function
+ * because it's not relevant just to decode PEM to DER.
+ */
+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)
 {
diff --git a/providers/implementations/include/prov/implementations.h b/providers/implementations/include/prov/implementations.h
index fd6b721364..23b0f51291 100644
--- a/providers/implementations/include/prov/implementations.h
+++ b/providers/implementations/include/prov/implementations.h
@@ -362,20 +362,50 @@ extern const OSSL_DISPATCH ossl_ec_to_pem_encoder_functions[];
 extern const OSSL_DISPATCH ossl_ec_to_text_encoder_functions[];
 
 /* Decoders */
-extern const OSSL_DISPATCH ossl_der_to_dh_decoder_functions[];
-extern const OSSL_DISPATCH ossl_der_to_dhx_decoder_functions[];
-extern const OSSL_DISPATCH ossl_der_to_dsa_decoder_functions[];
+extern const OSSL_DISPATCH ossl_PKCS8_der_to_dh_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_dh_decoder_functions[];
+extern const OSSL_DISPATCH ossl_type_specific_params_der_to_dh_decoder_functions[];
+extern const OSSL_DISPATCH ossl_DH_der_to_dh_decoder_functions[];
+
+extern const OSSL_DISPATCH ossl_PKCS8_der_to_dhx_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_dhx_decoder_functions[];
+extern const OSSL_DISPATCH ossl_type_specific_params_der_to_dhx_decoder_functions[];
+extern const OSSL_DISPATCH ossl_DHX_der_to_dhx_decoder_functions[];
+
+extern const OSSL_DISPATCH ossl_PKCS8_der_to_dsa_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_dsa_decoder_functions[];
+extern const OSSL_DISPATCH ossl_type_specific_der_to_dsa_decoder_functions[];
+extern const OSSL_DISPATCH ossl_DSA_der_to_dsa_decoder_functions[];
 extern const OSSL_DISPATCH ossl_msblob_to_dsa_decoder_functions[];
 extern const OSSL_DISPATCH ossl_pvk_to_dsa_decoder_functions[];
-extern const OSSL_DISPATCH ossl_der_to_ec_decoder_functions[];
-extern const OSSL_DISPATCH ossl_der_to_x25519_decoder_functions[];
-extern const OSSL_DISPATCH ossl_der_to_x448_decoder_functions[];
-extern const OSSL_DISPATCH ossl_der_to_ed25519_decoder_functions[];
-extern const OSSL_DISPATCH ossl_der_to_ed448_decoder_functions[];
-extern const OSSL_DISPATCH ossl_der_to_rsa_decoder_functions[];
-extern const OSSL_DISPATCH ossl_der_to_rsapss_decoder_functions[];
+
+extern const OSSL_DISPATCH ossl_PKCS8_der_to_ec_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_ec_decoder_functions[];
+extern const OSSL_DISPATCH ossl_type_specific_no_pub_der_to_ec_decoder_functions[];
+extern const OSSL_DISPATCH ossl_EC_der_to_ec_decoder_functions[];
+
+extern const OSSL_DISPATCH ossl_PKCS8_der_to_x25519_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_x25519_decoder_functions[];
+
+extern const OSSL_DISPATCH ossl_PKCS8_der_to_x448_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_x448_decoder_functions[];
+
+extern const OSSL_DISPATCH ossl_PKCS8_der_to_ed25519_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_ed25519_decoder_functions[];
+
+extern const OSSL_DISPATCH ossl_PKCS8_der_to_ed448_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_ed448_decoder_functions[];
+
+extern const OSSL_DISPATCH ossl_PKCS8_der_to_rsa_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_rsa_decoder_functions[];
+extern const OSSL_DISPATCH ossl_type_specific_keypair_der_to_rsa_decoder_functions[];
+extern const OSSL_DISPATCH ossl_RSA_der_to_rsa_decoder_functions[];
 extern const OSSL_DISPATCH ossl_msblob_to_rsa_decoder_functions[];
 extern const OSSL_DISPATCH ossl_pvk_to_rsa_decoder_functions[];
+
+extern const OSSL_DISPATCH ossl_PKCS8_der_to_rsapss_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_rsapss_decoder_functions[];
+
 extern const OSSL_DISPATCH ossl_pem_to_der_decoder_functions[];
 
 extern const OSSL_DISPATCH ossl_file_store_functions[];
diff --git a/providers/implementations/storemgmt/file_store_der2obj.c b/providers/implementations/storemgmt/file_store_der2obj.c
index 8c9168b125..74a18eb70b 100644
--- a/providers/implementations/storemgmt/file_store_der2obj.c
+++ b/providers/implementations/storemgmt/file_store_der2obj.c
@@ -77,7 +77,7 @@ static int der2obj_get_params(OSSL_PARAM params[])
     return 1;
 }
 
-static int der2obj_decode(void *provctx, OSSL_CORE_BIO *cin,
+static int der2obj_decode(void *provctx, OSSL_CORE_BIO *cin, int selection,
                           OSSL_CALLBACK *data_cb, void *data_cbarg,
                           OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
 {
diff --git a/test/endecode_test.c b/test/endecode_test.c
index 7a901e9793..57ba6774e4 100644
--- a/test/endecode_test.c
+++ b/test/endecode_test.c
@@ -235,7 +235,9 @@ static int decode_EVP_PKEY_prov(void **object, void *encoded, long encoded_len,
 
         if (!TEST_ptr(dctx = OSSL_DECODER_CTX_new_by_EVP_PKEY(&testpkey,
                                                               testtype,
+                                                              NULL,
                                                               keytype,
+                                                              selection,
                                                               NULL, NULL))
             || (pass != NULL
                 && !OSSL_DECODER_CTX_set_passphrase(dctx, upass, strlen(pass)))
diff --git a/util/libcrypto.num b/util/libcrypto.num
index 9437e30e85..40e1fcb43a 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -5285,3 +5285,6 @@ EVP_PKEY_CTX_get0_libctx                ?	3_0_0	EXIST::FUNCTION:
 EVP_PKEY_CTX_get0_propq                 ?	3_0_0	EXIST::FUNCTION:
 EVP_PKEY_set1_encoded_public_key        ?	3_0_0	EXIST::FUNCTION:
 EVP_PKEY_get1_encoded_public_key        ?	3_0_0	EXIST::FUNCTION:
+OSSL_DECODER_CTX_set_selection          ?	3_0_0	EXIST::FUNCTION:
+OSSL_DECODER_CTX_set_input_structure    ?	3_0_0	EXIST::FUNCTION:
+OSSL_DECODER_INSTANCE_get_input_structure ?	3_0_0	EXIST::FUNCTION:


More information about the openssl-commits mailing list