[openssl] master update

Richard Levitte levitte at openssl.org
Sun Sep 20 15:34:37 UTC 2020


The branch master has been updated
       via  b1415dc1820def1e9e344f9b83ad05c2a352ec56 (commit)
       via  48b62fb33aa0c5bce52b939fcd94780736491a5d (commit)
       via  ae12eac074be92e14c11a36b90e1c95eca3723f1 (commit)
       via  97bb8dff1f4915def454c702d07102924b60b254 (commit)
       via  111dc4b0f1e961afd6fc998ef443d9004356c046 (commit)
       via  b8975c68b1a7796993759db22905d0ef05f7e077 (commit)
       via  5a6d6fe66614ee5ff5976ca6e90bd156c8143553 (commit)
      from  4a71bee6cf84d0e6daa9857586ffcebf42aa1842 (commit)


- Log -----------------------------------------------------------------
commit b1415dc1820def1e9e344f9b83ad05c2a352ec56
Author: Richard Levitte <levitte at openssl.org>
Date:   Tue Sep 15 10:02:34 2020 +0200

    util/find-doc-nits: Add a regexp for C symbols and use it
    
    Our matching of C symbols here was inconsistent and could therefore
    give false negatives when the SYNOPSIS was parsed.  Now we have
    $C_symbol, which is a simple regexp that matches the common C symbol.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12873)

commit 48b62fb33aa0c5bce52b939fcd94780736491a5d
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Sep 14 11:35:07 2020 +0200

    DECODER: Some cleanups, and aligning with OSSL_ENCODER
    
    Mostly source nits, but also removing a couple of OSSL_DECODER_PARAM
    macros that are never used or even make sense.
    
    Also, some function names weren't quite consistent.  They were made a
    bit more consistent in the OSSL_ENCODER API, now we bring that back to
    OSSL_DECODER.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12873)

commit ae12eac074be92e14c11a36b90e1c95eca3723f1
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Sep 14 11:21:37 2020 +0200

    TEST: Adapt applicable tests to the changed OSSL_ENCODER_CTX_new_by_EVP_PKEY()
    
    This adds the convenience function EVP_PKEY_typenames_do_all(), which
    does the same as EVP_KEYMGMT_names_do_all(), but without having to
    expose all the internal ways to find out if the internal EVP_PKEY key
    is legacy or provider-native.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12873)

commit 97bb8dff1f4915def454c702d07102924b60b254
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Sep 14 10:42:05 2020 +0200

    ENCODER: Adapt calls to the changed OSSL_ENCODER_CTX_new_by_EVP_PKEY()
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12873)

commit 111dc4b0f1e961afd6fc998ef443d9004356c046
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Sep 14 09:31:36 2020 +0200

    ENCODER: Refactor our provider encoder implementations
    
    This only refactors them for the changed API, there's not yet a
    separate DER to PEM encoder and therefore no chaining possibility
    yet.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12873)

commit b8975c68b1a7796993759db22905d0ef05f7e077
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Sep 14 09:20:41 2020 +0200

    ENCODER: Refactor the OSSL_ENCODER API to be more like OSSL_DECODER
    
    OSSL_ENCODER was developed before OSSL_DECODER, so the idea of
    chaining and the resulting API came later.  This series of changes
    brings the same sort of API and functionality back to OSSL_ENCODER,
    making the two APIs more consistent with each other.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12873)

commit 5a6d6fe66614ee5ff5976ca6e90bd156c8143553
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Sep 14 08:29:45 2020 +0200

    ENCODER: Redefine the libcrypto <-> provider interface
    
    This is part of an effort to make OSSL_ENCODER work more like OSSL_DECODER.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12873)

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

Summary of changes:
 crypto/asn1/i2d_pr.c                               |   8 +-
 crypto/encode_decode/decoder_lib.c                 |  60 ++--
 crypto/encode_decode/decoder_meth.c                |  32 +-
 crypto/encode_decode/decoder_pkey.c                |  24 +-
 crypto/encode_decode/encoder_err.c                 |   8 +-
 crypto/encode_decode/encoder_lib.c                 | 342 +++++++++++++++++-
 crypto/encode_decode/encoder_local.h               |  52 ++-
 crypto/encode_decode/encoder_meth.c                | 122 ++++---
 crypto/encode_decode/encoder_pkey.c                | 366 +++++++++++++-------
 crypto/err/openssl.ec                              |   4 +-
 crypto/err/openssl.txt                             |   1 +
 crypto/evp/p_lib.c                                 |  36 +-
 crypto/pem/pem_local.h                             |  30 +-
 crypto/pem/pem_pk8.c                               |  43 ++-
 crypto/property/property_parse.c                   |   6 +-
 crypto/x509/x_pubkey.c                             |  19 +-
 doc/man3/EVP_PKEY_is_a.pod                         |  12 +-
 doc/man3/OSSL_DECODER_CTX.pod                      | 207 +++++++++--
 doc/man3/OSSL_DECODER_CTX_new_by_EVP_PKEY.pod      |  85 +++--
 doc/man3/OSSL_DECODER_from_bio.pod                 | 187 +---------
 doc/man3/OSSL_ENCODER.pod                          |  15 +-
 doc/man3/OSSL_ENCODER_CTX.pod                      | 177 ++++++++--
 doc/man3/OSSL_ENCODER_CTX_new_by_EVP_PKEY.pod      | 112 +++---
 doc/man3/OSSL_ENCODER_to_bio.pod                   |  24 +-
 doc/man7/provider-encoder.pod                      | 231 ++++++++-----
 include/crypto/encoder.h                           |   2 -
 include/openssl/core_dispatch.h                    |  31 +-
 include/openssl/core_names.h                       |   6 +-
 include/openssl/decoder.h                          |  25 +-
 include/openssl/encoder.h                          |  69 ++--
 include/openssl/encodererr.h                       |   5 +-
 include/openssl/evp.h                              |   3 +
 providers/baseprov.c                               |   4 +-
 providers/defltprov.c                              |   4 +-
 providers/encoders.inc                             | 174 ++--------
 .../implementations/encode_decode/encode_key2any.c | 381 ++++++++++++---------
 .../encode_decode/encode_key2text.c                | 158 +++++----
 .../implementations/include/prov/implementations.h |  89 ++---
 test/endecode_test.c                               | 256 +++++++++-----
 test/evp_pkey_provided_test.c                      |  35 +-
 util/find-doc-nits                                 |  17 +-
 util/libcrypto.num                                 |  27 +-
 util/other.syms                                    |   4 +-
 43 files changed, 2136 insertions(+), 1357 deletions(-)

diff --git a/crypto/asn1/i2d_pr.c b/crypto/asn1/i2d_pr.c
index 84513db5bf..e35781f4bd 100644
--- a/crypto/asn1/i2d_pr.c
+++ b/crypto/asn1/i2d_pr.c
@@ -31,16 +31,18 @@ int i2d_PrivateKey(const EVP_PKEY *a, unsigned char **pp)
         return ret;
     }
     if (a->keymgmt != NULL) {
-        const char *encprop = OSSL_ENCODER_PrivateKey_TO_DER_PQ;
+        /* The private key includes everything */
+        int selection =
+            OSSL_KEYMGMT_SELECT_ALL_PARAMETERS | OSSL_KEYMGMT_SELECT_KEYPAIR;
         OSSL_ENCODER_CTX *ctx =
-            OSSL_ENCODER_CTX_new_by_EVP_PKEY(a, encprop);
+            OSSL_ENCODER_CTX_new_by_EVP_PKEY(a, "DER", selection, NULL, NULL);
         BIO *out = BIO_new(BIO_s_mem());
         BUF_MEM *buf = NULL;
         int ret = -1;
 
         if (ctx != NULL
             && out != NULL
-            && OSSL_ENCODER_CTX_get_encoder(ctx) != NULL
+            && OSSL_ENCODER_CTX_get_num_encoders(ctx) != 0
             && OSSL_ENCODER_to_bio(ctx, out)
             && BIO_get_mem_ptr(out, &buf) > 0) {
             ret = buf->length;
diff --git a/crypto/encode_decode/decoder_lib.c b/crypto/encode_decode/decoder_lib.c
index 9eeff20f7c..0bc772e43b 100644
--- a/crypto/encode_decode/decoder_lib.c
+++ b/crypto/encode_decode/decoder_lib.c
@@ -159,8 +159,7 @@ int ossl_decoder_ctx_add_decoder_inst(OSSL_DECODER_CTX *ctx,
     return (sk_OSSL_DECODER_INSTANCE_push(ctx->decoder_insts, di) > 0);
 }
 
-int OSSL_DECODER_CTX_add_decoder(OSSL_DECODER_CTX *ctx,
-                                           OSSL_DECODER *decoder)
+int OSSL_DECODER_CTX_add_decoder(OSSL_DECODER_CTX *ctx, OSSL_DECODER *decoder)
 {
     OSSL_DECODER_INSTANCE *decoder_inst = NULL;
     const OSSL_PROVIDER *prov = NULL;
@@ -246,7 +245,8 @@ int OSSL_DECODER_CTX_add_extra(OSSL_DECODER_CTX *ctx,
         for (i = w_prev_start; i < w_prev_end; i++) {
             OSSL_DECODER_INSTANCE *decoder_inst =
                 sk_OSSL_DECODER_INSTANCE_value(ctx->decoder_insts, i);
-            const char *name = decoder_inst->input_type;
+            const char *input_type =
+                OSSL_DECODER_INSTANCE_get_input_type(decoder_inst);
             OSSL_DECODER *decoder = NULL;
 
             /*
@@ -256,12 +256,11 @@ int OSSL_DECODER_CTX_add_extra(OSSL_DECODER_CTX *ctx,
              * on top of this one, so we don't.
              */
             if (ctx->start_input_type != NULL
-                && strcasecmp(ctx->start_input_type,
-                              decoder_inst->input_type) != 0)
+                && strcasecmp(ctx->start_input_type, input_type) != 0)
                 continue;
 
             ERR_set_mark();
-            decoder = OSSL_DECODER_fetch(libctx, name, propq);
+            decoder = OSSL_DECODER_fetch(libctx, input_type, propq);
             ERR_pop_to_mark();
 
             if (decoder != NULL) {
@@ -308,7 +307,7 @@ int OSSL_DECODER_CTX_add_extra(OSSL_DECODER_CTX *ctx,
     return 1;
 }
 
-int OSSL_DECODER_CTX_num_decoders(OSSL_DECODER_CTX *ctx)
+int OSSL_DECODER_CTX_get_num_decoders(OSSL_DECODER_CTX *ctx)
 {
     if (ctx == NULL || ctx->decoder_insts == NULL)
         return 0;
@@ -375,6 +374,9 @@ int OSSL_DECODER_export(OSSL_DECODER_INSTANCE *decoder_inst,
                         void *reference, size_t reference_sz,
                         OSSL_CALLBACK *export_cb, void *export_cbarg)
 {
+    OSSL_DECODER *decoder = NULL;
+    void *decoderctx = NULL;
+
     if (!(ossl_assert(decoder_inst != NULL)
           && ossl_assert(reference != NULL)
           && ossl_assert(export_cb != NULL)
@@ -383,25 +385,36 @@ int OSSL_DECODER_export(OSSL_DECODER_INSTANCE *decoder_inst,
         return 0;
     }
 
-    return decoder_inst->decoder->export_object(decoder_inst->decoderctx,
-                                                reference, reference_sz,
-                                                export_cb, export_cbarg);
+    decoder = OSSL_DECODER_INSTANCE_get_decoder(decoder_inst);
+    decoderctx = OSSL_DECODER_INSTANCE_get_decoder_ctx(decoder_inst);
+    return decoder->export_object(decoderctx, reference, reference_sz,
+                                  export_cb, export_cbarg);
 }
 
-OSSL_DECODER *OSSL_DECODER_INSTANCE_decoder(OSSL_DECODER_INSTANCE *decoder_inst)
+OSSL_DECODER *
+OSSL_DECODER_INSTANCE_get_decoder(OSSL_DECODER_INSTANCE *decoder_inst)
 {
     if (decoder_inst == NULL)
         return NULL;
     return decoder_inst->decoder;
 }
 
-void *OSSL_DECODER_INSTANCE_decoder_ctx(OSSL_DECODER_INSTANCE *decoder_inst)
+void *
+OSSL_DECODER_INSTANCE_get_decoder_ctx(OSSL_DECODER_INSTANCE *decoder_inst)
 {
     if (decoder_inst == NULL)
         return NULL;
     return decoder_inst->decoderctx;
 }
 
+const char *
+OSSL_DECODER_INSTANCE_get_input_type(OSSL_DECODER_INSTANCE *decoder_inst)
+{
+    if (decoder_inst == NULL)
+        return NULL;
+    return decoder_inst->input_type;
+}
+
 static int decoder_process(const OSSL_PARAM params[], void *arg)
 {
     struct decoder_process_data_st *data = arg;
@@ -422,7 +435,7 @@ static int decoder_process(const OSSL_PARAM params[], void *arg)
         /* First iteration, where we prepare for what is to come */
 
         data->current_decoder_inst_index =
-            OSSL_DECODER_CTX_num_decoders(ctx);
+            OSSL_DECODER_CTX_get_num_decoders(ctx);
 
         bio = data->bio;
     } else {
@@ -431,7 +444,7 @@ static int decoder_process(const OSSL_PARAM params[], void *arg)
         decoder_inst =
             sk_OSSL_DECODER_INSTANCE_value(ctx->decoder_insts,
                                            data->current_decoder_inst_index);
-        decoder = OSSL_DECODER_INSTANCE_decoder(decoder_inst);
+        decoder = OSSL_DECODER_INSTANCE_get_decoder(decoder_inst);
 
         if (ctx->construct != NULL
             && ctx->construct(decoder_inst, params, ctx->construct_data)) {
@@ -473,7 +486,11 @@ static int decoder_process(const OSSL_PARAM params[], void *arg)
         OSSL_DECODER_INSTANCE *new_decoder_inst =
             sk_OSSL_DECODER_INSTANCE_value(ctx->decoder_insts, i);
         OSSL_DECODER *new_decoder =
-            OSSL_DECODER_INSTANCE_decoder(new_decoder_inst);
+            OSSL_DECODER_INSTANCE_get_decoder(new_decoder_inst);
+        void *new_decoderctx =
+            OSSL_DECODER_INSTANCE_get_decoder_ctx(new_decoder_inst);
+        const char *new_input_type =
+            OSSL_DECODER_INSTANCE_get_input_type(new_decoder_inst);
 
         /*
          * If |decoder| is NULL, it means we've just started, and the caller
@@ -481,18 +498,16 @@ static int decoder_process(const OSSL_PARAM params[], void *arg)
          * that's the case, we do this extra check.
          */
         if (decoder == NULL && ctx->start_input_type != NULL
-            && strcasecmp(ctx->start_input_type,
-                          new_decoder_inst->input_type) != 0)
+            && strcasecmp(ctx->start_input_type, new_input_type) != 0)
             continue;
 
         /*
          * If we have a previous decoder, we check that the input type
          * of the next to be used matches the type of this previous one.
-         * decoder_inst->input_type is a cache of the parameter "input-type"
-         * value for that decoder.
+         * input_type is a cache of the parameter "input-type" value for
+         * that decoder.
          */
-        if (decoder != NULL
-            && !OSSL_DECODER_is_a(decoder, new_decoder_inst->input_type))
+        if (decoder != NULL && !OSSL_DECODER_is_a(decoder, new_input_type))
             continue;
 
         /*
@@ -511,8 +526,7 @@ static int decoder_process(const OSSL_PARAM params[], void *arg)
 
         /* Recurse */
         new_data.current_decoder_inst_index = i;
-        ok = new_decoder->decode(new_decoder_inst->decoderctx,
-                                 (OSSL_CORE_BIO *)bio,
+        ok = new_decoder->decode(new_decoderctx, (OSSL_CORE_BIO *)bio,
                                  decoder_process, &new_data,
                                  ossl_pw_passphrase_callback_dec,
                                  &new_data.ctx->pwdata);
diff --git a/crypto/encode_decode/decoder_meth.c b/crypto/encode_decode/decoder_meth.c
index 41406df90f..37c6ab2b57 100644
--- a/crypto/encode_decode/decoder_meth.c
+++ b/crypto/encode_decode/decoder_meth.c
@@ -484,10 +484,8 @@ OSSL_DECODER_CTX *OSSL_DECODER_CTX_new(void)
 {
     OSSL_DECODER_CTX *ctx;
 
-    if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) == NULL) {
+    if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) == NULL)
         ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_MALLOC_FAILURE);
-        return NULL;
-    }
 
     return ctx;
 }
@@ -506,42 +504,30 @@ int OSSL_DECODER_CTX_set_params(OSSL_DECODER_CTX *ctx,
     if (ctx->decoder_insts == NULL)
         return 1;
 
-    l = (size_t)sk_OSSL_DECODER_INSTANCE_num(ctx->decoder_insts);
+    l = OSSL_DECODER_CTX_get_num_decoders(ctx);
     for (i = 0; i < l; i++) {
         OSSL_DECODER_INSTANCE *decoder_inst =
             sk_OSSL_DECODER_INSTANCE_value(ctx->decoder_insts, i);
+        OSSL_DECODER *decoder =
+            OSSL_DECODER_INSTANCE_get_decoder(decoder_inst);
+        OSSL_DECODER *decoderctx =
+            OSSL_DECODER_INSTANCE_get_decoder_ctx(decoder_inst);
 
-        if (decoder_inst->decoderctx == NULL
-            || decoder_inst->decoder->set_ctx_params == NULL)
+        if (decoderctx == NULL || decoder->set_ctx_params == NULL)
             continue;
-        if (!decoder_inst->decoder->set_ctx_params(decoder_inst->decoderctx,
-                                                   params))
+        if (!decoder->set_ctx_params(decoderctx, params))
             return 0;
     }
     return 1;
 }
 
-static void
-OSSL_DECODER_INSTANCE_free(OSSL_DECODER_INSTANCE *decoder_inst)
-{
-    if (decoder_inst != NULL) {
-        if (decoder_inst->decoder->freectx != NULL)
-            decoder_inst->decoder->freectx(decoder_inst->decoderctx);
-        decoder_inst->decoderctx = NULL;
-        OSSL_DECODER_free(decoder_inst->decoder);
-        decoder_inst->decoder = NULL;
-        OPENSSL_free(decoder_inst);
-        decoder_inst = NULL;
-    }
-}
-
 void OSSL_DECODER_CTX_free(OSSL_DECODER_CTX *ctx)
 {
     if (ctx != NULL) {
         if (ctx->cleanup != NULL)
             ctx->cleanup(ctx->construct_data);
         sk_OSSL_DECODER_INSTANCE_pop_free(ctx->decoder_insts,
-                                          OSSL_DECODER_INSTANCE_free);
+                                          ossl_decoder_instance_free);
         ossl_pw_clear_passphrase_data(&ctx->pwdata);
         OPENSSL_free(ctx);
     }
diff --git a/crypto/encode_decode/decoder_pkey.c b/crypto/encode_decode/decoder_pkey.c
index dfc7cccab1..2e07d0d7cc 100644
--- a/crypto/encode_decode/decoder_pkey.c
+++ b/crypto/encode_decode/decoder_pkey.c
@@ -62,9 +62,8 @@ static int decoder_construct_EVP_PKEY(OSSL_DECODER_INSTANCE *decoder_inst,
                                       void *construct_data)
 {
     struct decoder_EVP_PKEY_data_st *data = construct_data;
-    OSSL_DECODER *decoder =
-        OSSL_DECODER_INSTANCE_decoder(decoder_inst);
-    void *decoderctx = OSSL_DECODER_INSTANCE_decoder_ctx(decoder_inst);
+    OSSL_DECODER *decoder = OSSL_DECODER_INSTANCE_get_decoder(decoder_inst);
+    void *decoderctx = OSSL_DECODER_INSTANCE_get_decoder_ctx(decoder_inst);
     size_t i, end_i;
     /*
      * |object_ref| points to a provider reference to an object, its exact
@@ -207,7 +206,7 @@ static void collect_keymgmt(EVP_KEYMGMT *keymgmt, void *arg)
     if (!EVP_KEYMGMT_up_ref(keymgmt) /* ref++ */)
         return;
     if (sk_EVP_KEYMGMT_push(data->process_data->keymgmts, keymgmt) <= 0) {
-        EVP_KEYMGMT_free(keymgmt); /* ref-- */
+        EVP_KEYMGMT_free(keymgmt);   /* ref-- */
         return;
     }
 
@@ -301,17 +300,16 @@ int ossl_decoder_ctx_setup_for_EVP_PKEY(OSSL_DECODER_CTX *ctx,
     if (data->error_occured)
         goto err;
 
-    /* If we found no decoders to match the keymgmts, we err */
-    if (OSSL_DECODER_CTX_num_decoders(ctx) == 0)
-        goto err;
+    if (OSSL_DECODER_CTX_get_num_decoders(ctx) != 0) {
+        if (!OSSL_DECODER_CTX_set_construct(ctx, decoder_construct_EVP_PKEY)
+            || !OSSL_DECODER_CTX_set_construct_data(ctx, data->process_data)
+            || !OSSL_DECODER_CTX_set_cleanup(ctx,
+                                             decoder_clean_EVP_PKEY_construct_arg))
+            goto err;
 
-    if (!OSSL_DECODER_CTX_set_construct(ctx, decoder_construct_EVP_PKEY)
-        || !OSSL_DECODER_CTX_set_construct_data(ctx, data->process_data)
-        || !OSSL_DECODER_CTX_set_cleanup(ctx,
-                                         decoder_clean_EVP_PKEY_construct_arg))
-        goto err;
+        data->process_data = NULL; /* Avoid it being freed */
+    }
 
-    data->process_data = NULL;
     ok = 1;
  err:
     if (data != NULL) {
diff --git a/crypto/encode_decode/encoder_err.c b/crypto/encode_decode/encoder_err.c
index 5416f8390e..2c95a2a20e 100644
--- a/crypto/encode_decode/encoder_err.c
+++ b/crypto/encode_decode/encoder_err.c
@@ -14,10 +14,12 @@
 #ifndef OPENSSL_NO_ERR
 
 static const ERR_STRING_DATA OSSL_ENCODER_str_reasons[] = {
-    {ERR_PACK(ERR_LIB_OSSL_ENCODER, 0, OSSL_ENCODER_R_INCORRECT_PROPERTY_QUERY),
-     "incorrect property query"},
     {ERR_PACK(ERR_LIB_OSSL_ENCODER, 0, OSSL_ENCODER_R_ENCODER_NOT_FOUND),
-     "encoder not found"},
+    "encoder not found"},
+    {ERR_PACK(ERR_LIB_OSSL_ENCODER, 0, OSSL_ENCODER_R_INCORRECT_PROPERTY_QUERY),
+    "incorrect property query"},
+    {ERR_PACK(ERR_LIB_OSSL_ENCODER, 0, OSSL_ENCODER_R_MISSING_GET_PARAMS),
+    "missing get params"},
     {0, NULL}
 };
 
diff --git a/crypto/encode_decode/encoder_lib.c b/crypto/encode_decode/encoder_lib.c
index b083fa2d4c..179c6d3dc3 100644
--- a/crypto/encode_decode/encoder_lib.c
+++ b/crypto/encode_decode/encoder_lib.c
@@ -7,13 +7,20 @@
  * https://www.openssl.org/source/license.html
  */
 
+#include "e_os.h"                /* strcasecmp on Windows */
+#include <openssl/core_names.h>
 #include <openssl/bio.h>
 #include <openssl/encoder.h>
+#include <openssl/buffer.h>
+#include <openssl/params.h>
+#include <openssl/provider.h>
 #include "encoder_local.h"
 
+static int encoder_process(OSSL_ENCODER_CTX *ctx, BIO *out);
+
 int OSSL_ENCODER_to_bio(OSSL_ENCODER_CTX *ctx, BIO *out)
 {
-    return ctx->do_output(ctx, out);
+    return encoder_process(ctx, out);
 }
 
 #ifndef OPENSSL_NO_STDIO
@@ -41,3 +48,336 @@ int OSSL_ENCODER_to_fp(OSSL_ENCODER_CTX *ctx, FILE *fp)
     return ret;
 }
 #endif
+
+int OSSL_ENCODER_CTX_set_output_type(OSSL_ENCODER_CTX *ctx,
+                                     const char *output_type)
+{
+    if (!ossl_assert(ctx != NULL) || !ossl_assert(output_type != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    ctx->output_type = output_type;
+    return 1;
+}
+
+int OSSL_ENCODER_CTX_set_selection(OSSL_ENCODER_CTX *ctx, int selection)
+{
+    if (!ossl_assert(ctx != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    if (!ossl_assert(selection != 0)) {
+        ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+
+    ctx->selection = selection;
+    return 1;
+}
+
+static OSSL_ENCODER_INSTANCE *ossl_encoder_instance_new(OSSL_ENCODER *encoder,
+                                                        void *encoderctx)
+{
+    OSSL_ENCODER_INSTANCE *encoder_inst = NULL;
+    OSSL_PARAM params[3];
+
+    if (!ossl_assert(encoder != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    if (encoder->get_params == NULL) {
+        ERR_raise(ERR_LIB_OSSL_ENCODER,
+                  OSSL_ENCODER_R_MISSING_GET_PARAMS);
+        return 0;
+    }
+
+    if ((encoder_inst = OPENSSL_zalloc(sizeof(*encoder_inst))) == NULL) {
+        ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    /*
+     * Cache the input and output types for this encoder.  The output type
+     * is mandatory.
+     */
+    params[0] =
+        OSSL_PARAM_construct_utf8_ptr(OSSL_ENCODER_PARAM_OUTPUT_TYPE,
+                                      (char **)&encoder_inst->output_type, 0);
+    params[1] =
+        OSSL_PARAM_construct_utf8_ptr(OSSL_ENCODER_PARAM_INPUT_TYPE,
+                                      (char **)&encoder_inst->input_type, 0);
+    params[2] = OSSL_PARAM_construct_end();
+
+    if (!encoder->get_params(params)
+        || !OSSL_PARAM_modified(&params[1]))
+        goto err;
+
+    if (!OSSL_ENCODER_up_ref(encoder)) {
+        ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+
+    encoder_inst->encoder = encoder;
+    encoder_inst->encoderctx = encoderctx;
+    return encoder_inst;
+ err:
+    ossl_encoder_instance_free(encoder_inst);
+    return NULL;
+}
+
+void ossl_encoder_instance_free(OSSL_ENCODER_INSTANCE *encoder_inst)
+{
+    if (encoder_inst != NULL) {
+        if (encoder_inst->encoder != NULL)
+            encoder_inst->encoder->freectx(encoder_inst->encoderctx);
+        encoder_inst->encoderctx = NULL;
+        OSSL_ENCODER_free(encoder_inst->encoder);
+        encoder_inst->encoder = NULL;
+        OPENSSL_free(encoder_inst);
+    }
+}
+
+static int ossl_encoder_ctx_add_encoder_inst(OSSL_ENCODER_CTX *ctx,
+                                             OSSL_ENCODER_INSTANCE *ei)
+{
+    if (ctx->encoder_insts == NULL
+        && (ctx->encoder_insts =
+            sk_OSSL_ENCODER_INSTANCE_new_null()) == NULL) {
+        ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    return (sk_OSSL_ENCODER_INSTANCE_push(ctx->encoder_insts, ei) > 0);
+}
+
+int OSSL_ENCODER_CTX_add_encoder(OSSL_ENCODER_CTX *ctx, OSSL_ENCODER *encoder)
+{
+    OSSL_ENCODER_INSTANCE *encoder_inst = NULL;
+    const OSSL_PROVIDER *prov = NULL;
+    void *encoderctx = NULL;
+    void *provctx = NULL;
+
+    if (!ossl_assert(ctx != NULL) || !ossl_assert(encoder != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    prov = OSSL_ENCODER_provider(encoder);
+    provctx = OSSL_PROVIDER_get0_provider_ctx(prov);
+
+    if ((encoderctx = encoder->newctx(provctx)) == NULL
+        || (encoder_inst =
+            ossl_encoder_instance_new(encoder, encoderctx)) == NULL)
+        goto err;
+    /* Avoid double free of encoderctx on further errors */
+    encoderctx = NULL;
+
+    if (!ossl_encoder_ctx_add_encoder_inst(ctx, encoder_inst))
+        goto err;
+
+    return 1;
+ err:
+    ossl_encoder_instance_free(encoder_inst);
+    if (encoderctx != NULL)
+        encoder->freectx(encoderctx);
+    return 0;
+}
+
+int OSSL_ENCODER_CTX_add_extra(OSSL_ENCODER_CTX *ctx,
+                               OPENSSL_CTX *libctx, const char *propq)
+{
+    return 1;
+}
+
+int OSSL_ENCODER_CTX_get_num_encoders(OSSL_ENCODER_CTX *ctx)
+{
+    if (ctx == NULL || ctx->encoder_insts == NULL)
+        return 0;
+    return sk_OSSL_ENCODER_INSTANCE_num(ctx->encoder_insts);
+}
+
+int OSSL_ENCODER_CTX_set_construct(OSSL_ENCODER_CTX *ctx,
+                                   OSSL_ENCODER_CONSTRUCT *construct)
+{
+    if (!ossl_assert(ctx != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    ctx->construct = construct;
+    return 1;
+}
+
+int OSSL_ENCODER_CTX_set_construct_data(OSSL_ENCODER_CTX *ctx,
+                                        void *construct_data)
+{
+    if (!ossl_assert(ctx != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    ctx->construct_data = construct_data;
+    return 1;
+}
+
+int OSSL_ENCODER_CTX_set_cleanup(OSSL_ENCODER_CTX *ctx,
+                                 OSSL_ENCODER_CLEANUP *cleanup)
+{
+    if (!ossl_assert(ctx != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    ctx->cleanup = cleanup;
+    return 1;
+}
+
+OSSL_ENCODER *
+OSSL_ENCODER_INSTANCE_get_encoder(OSSL_ENCODER_INSTANCE *encoder_inst)
+{
+    if (encoder_inst == NULL)
+        return NULL;
+    return encoder_inst->encoder;
+}
+
+void *
+OSSL_ENCODER_INSTANCE_get_encoder_ctx(OSSL_ENCODER_INSTANCE *encoder_inst)
+{
+    if (encoder_inst == NULL)
+        return NULL;
+    return encoder_inst->encoderctx;
+}
+
+const char *
+OSSL_ENCODER_INSTANCE_get_input_type(OSSL_ENCODER_INSTANCE *encoder_inst)
+{
+    if (encoder_inst == NULL)
+        return NULL;
+    return encoder_inst->input_type;
+}
+
+const char *
+OSSL_ENCODER_INSTANCE_get_output_type(OSSL_ENCODER_INSTANCE *encoder_inst)
+{
+    if (encoder_inst == NULL)
+        return NULL;
+    return encoder_inst->output_type;
+}
+
+static int encoder_process(OSSL_ENCODER_CTX *ctx, BIO *out)
+{
+    size_t i, end;
+    void *latest_output = NULL;
+    size_t latest_output_length = 0;
+    const char *latest_output_type = NULL;
+    const char *last_input_type = NULL;
+    int ok = 0;
+
+    end = OSSL_ENCODER_CTX_get_num_encoders(ctx);
+    for (i = 0; i < end; i++) {
+        OSSL_ENCODER_INSTANCE *encoder_inst =
+            sk_OSSL_ENCODER_INSTANCE_value(ctx->encoder_insts, i);
+        OSSL_ENCODER *encoder = OSSL_ENCODER_INSTANCE_get_encoder(encoder_inst);
+        void *encoderctx = OSSL_ENCODER_INSTANCE_get_encoder_ctx(encoder_inst);
+        const char *current_input_type =
+            OSSL_ENCODER_INSTANCE_get_input_type(encoder_inst);
+        const char *current_output_type =
+            OSSL_ENCODER_INSTANCE_get_output_type(encoder_inst);
+        BIO *current_out;
+        BIO *allocated_out = NULL;
+        const void *current_data = NULL;
+        OSSL_PARAM abstract[3];
+        OSSL_PARAM *abstract_p;
+        const OSSL_PARAM *current_abstract = NULL;
+
+        if (latest_output_type == NULL) {
+            /*
+             * This is the first iteration, so we prepare the object to be
+             * encoded
+             */
+
+            current_data = ctx->construct(encoder_inst, ctx->construct_data);
+
+            /* Assume that the constructor recorded an error */
+            if (current_data == NULL)
+                goto loop_end;
+        } else {
+            /*
+             * Check that the latest output type matches the currently
+             * considered encoder
+             */
+            if (!OSSL_ENCODER_is_a(encoder, latest_output_type))
+                continue;
+
+            /*
+             * If there is a latest output type, there should be a latest output
+             */
+            if (!ossl_assert(latest_output != NULL)) {
+                ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_INTERNAL_ERROR);
+                goto loop_end;
+            }
+
+            /*
+             * Create an object abstraction from the latest output, which was
+             * stolen from the previous round.
+             */
+            abstract_p = abstract;
+            if (last_input_type != NULL)
+                *abstract_p++ =
+                    OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE,
+                                                     (char *)last_input_type, 0);
+            *abstract_p++ =
+                OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_DATA,
+                                                  latest_output,
+                                                  latest_output_length);
+            *abstract_p = OSSL_PARAM_construct_end();
+            current_abstract = abstract;
+        }
+
+        /*
+         * If the desired output type matches the output type of the currently
+         * considered encoder, we're setting up final output.  Otherwise, set
+         * up an intermediary memory output.
+         */
+        if (strcasecmp(ctx->output_type, current_output_type) == 0)
+            current_out = out;
+        else if ((current_out = allocated_out = BIO_new(BIO_s_mem())) == NULL)
+            goto loop_end;     /* Assume BIO_new() recorded an error */
+
+        ok = encoder->encode(encoderctx, (OSSL_CORE_BIO *)current_out,
+                             current_data, current_abstract, ctx->selection,
+                             ossl_pw_passphrase_callback_enc, &ctx->pwdata);
+
+        if (current_input_type != NULL)
+            last_input_type = current_input_type;
+
+        if (!ok)
+            goto loop_end;
+
+        OPENSSL_free(latest_output);
+
+        /*
+         * Steal the output from the BIO_s_mem, if we did allocate one.
+         * That'll be the data for an object abstraction in the next round.
+         */
+        if (allocated_out != NULL) {
+            BUF_MEM *buf;
+
+            BIO_get_mem_ptr(allocated_out, &buf);
+            latest_output = buf->data;
+            latest_output_length = buf->length;
+            memset(buf, 0, sizeof(*buf));
+            BIO_free(allocated_out);
+        }
+
+     loop_end:
+        if (current_data != NULL)
+            ctx->cleanup(ctx->construct_data);
+
+        if (ok)
+            break;
+    }
+
+    OPENSSL_free(latest_output);
+    return ok;
+}
diff --git a/crypto/encode_decode/encoder_local.h b/crypto/encode_decode/encoder_local.h
index e707be19ff..a57d0cd16c 100644
--- a/crypto/encode_decode/encoder_local.h
+++ b/crypto/encode_decode/encoder_local.h
@@ -29,10 +29,13 @@ struct ossl_encoder_st {
     struct ossl_endecode_base_st base;
     OSSL_FUNC_encoder_newctx_fn *newctx;
     OSSL_FUNC_encoder_freectx_fn *freectx;
+    OSSL_FUNC_encoder_get_params_fn *get_params;
+    OSSL_FUNC_encoder_gettable_params_fn *gettable_params;
     OSSL_FUNC_encoder_set_ctx_params_fn *set_ctx_params;
     OSSL_FUNC_encoder_settable_ctx_params_fn *settable_ctx_params;
-    OSSL_FUNC_encoder_encode_data_fn *encode_data;
-    OSSL_FUNC_encoder_encode_object_fn *encode_object;
+    OSSL_FUNC_encoder_encode_fn *encode;
+    OSSL_FUNC_encoder_import_object_fn *import_object;
+    OSSL_FUNC_encoder_free_object_fn *free_object;
 };
 
 struct ossl_decoder_st {
@@ -47,22 +50,43 @@ struct ossl_decoder_st {
     OSSL_FUNC_decoder_export_object_fn *export_object;
 };
 
-struct ossl_encoder_ctx_st {
-    OSSL_ENCODER *encoder;
-    void *encoderctx;
+struct ossl_encoder_instance_st {
+    OSSL_ENCODER *encoder;       /* Never NULL */
+    void *encoderctx;            /* Never NULL */
+    const char *input_type;      /* May be NULL */
+    const char *output_type;     /* Never NULL */
+};
+
+DEFINE_STACK_OF(OSSL_ENCODER_INSTANCE)
+
+void ossl_encoder_instance_free(OSSL_ENCODER_INSTANCE *encoder_inst);
 
+struct ossl_encoder_ctx_st {
+    /*
+     * The desired output type.  The encoder implementation have a gettable
+     * "output-type" parameter that this will match against.
+     */
+    const char *output_type;
+    /*
+     * Select what parts of an object will be encoded.  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;
 
-    /*-
-     * Output / encoding data, used by OSSL_ENCODER_to_{bio,fp}
-     *
-     * |object|         is the libcrypto object to handle.
-     * |do_output|      performs the actual encoding.
-     *
-     * |do_output| must have intimate knowledge of |object|.
+    /*
+     * Decoders that are components of any current decoding path.
      */
-    const void *object;
-    int (*do_output)(OSSL_ENCODER_CTX *ctx, BIO *out);
+    STACK_OF(OSSL_ENCODER_INSTANCE) *encoder_insts;
+
+    /*
+     * The constructor and destructor of an object to pass to the first
+     * encoder in a chain.
+     */
+    OSSL_ENCODER_CONSTRUCT *construct;
+    OSSL_ENCODER_CLEANUP *cleanup;
+    void *construct_data;
 
     /* For any function that needs a passphrase reader */
     struct ossl_passphrase_data_st pwdata;
diff --git a/crypto/encode_decode/encoder_meth.c b/crypto/encode_decode/encoder_meth.c
index 62342f511a..93929b5360 100644
--- a/crypto/encode_decode/encoder_meth.c
+++ b/crypto/encode_decode/encoder_meth.c
@@ -180,6 +180,16 @@ static void *encoder_from_dispatch(int id, const OSSL_ALGORITHM *algodef,
                 encoder->freectx =
                     OSSL_FUNC_encoder_freectx(fns);
             break;
+        case OSSL_FUNC_ENCODER_GET_PARAMS:
+            if (encoder->get_params == NULL)
+                encoder->get_params =
+                    OSSL_FUNC_encoder_get_params(fns);
+            break;
+        case OSSL_FUNC_ENCODER_GETTABLE_PARAMS:
+            if (encoder->gettable_params == NULL)
+                encoder->gettable_params =
+                    OSSL_FUNC_encoder_gettable_params(fns);
+            break;
         case OSSL_FUNC_ENCODER_SET_CTX_PARAMS:
             if (encoder->set_ctx_params == NULL)
                 encoder->set_ctx_params =
@@ -190,26 +200,34 @@ static void *encoder_from_dispatch(int id, const OSSL_ALGORITHM *algodef,
                 encoder->settable_ctx_params =
                     OSSL_FUNC_encoder_settable_ctx_params(fns);
             break;
-        case OSSL_FUNC_ENCODER_ENCODE_DATA:
-            if (encoder->encode_data == NULL)
-                encoder->encode_data =
-                    OSSL_FUNC_encoder_encode_data(fns);
+        case OSSL_FUNC_ENCODER_ENCODE:
+            if (encoder->encode == NULL)
+                encoder->encode = OSSL_FUNC_encoder_encode(fns);
+            break;
+        case OSSL_FUNC_ENCODER_IMPORT_OBJECT:
+            if (encoder->import_object == NULL)
+                encoder->import_object =
+                    OSSL_FUNC_encoder_import_object(fns);
             break;
-        case OSSL_FUNC_ENCODER_ENCODE_OBJECT:
-            if (encoder->encode_object == NULL)
-                encoder->encode_object =
-                    OSSL_FUNC_encoder_encode_object(fns);
+        case OSSL_FUNC_ENCODER_FREE_OBJECT:
+            if (encoder->free_object == NULL)
+                encoder->free_object =
+                    OSSL_FUNC_encoder_free_object(fns);
             break;
         }
     }
     /*
      * Try to check that the method is sensible.
      * If you have a constructor, you must have a destructor and vice versa.
-     * You must have at least one of the encoding driver functions.
+     * You must have the encoding driver functions.
      */
     if (!((encoder->newctx == NULL && encoder->freectx == NULL)
-          || (encoder->newctx != NULL && encoder->freectx != NULL))
-        || (encoder->encode_data == NULL && encoder->encode_object == NULL)) {
+          || (encoder->newctx != NULL && encoder->freectx != NULL)
+          || (encoder->import_object != NULL && encoder->free_object != NULL)
+          || (encoder->import_object == NULL && encoder->free_object == NULL))
+        || encoder->encode == NULL
+        || encoder->gettable_params == NULL
+        || encoder->get_params == NULL) {
         OSSL_ENCODER_free(encoder);
         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_INVALID_PROVIDER_FUNCTIONS);
         return NULL;
@@ -440,6 +458,24 @@ void OSSL_ENCODER_names_do_all(const OSSL_ENCODER *encoder,
     }
 }
 
+const OSSL_PARAM *
+OSSL_ENCODER_gettable_params(OSSL_ENCODER *encoder)
+{
+    if (encoder != NULL && encoder->gettable_params != NULL) {
+        void *provctx = ossl_provider_ctx(OSSL_ENCODER_provider(encoder));
+
+        return encoder->gettable_params(provctx);
+    }
+    return NULL;
+}
+
+int OSSL_ENCODER_get_params(OSSL_ENCODER *encoder, OSSL_PARAM params[])
+{
+    if (encoder != NULL && encoder->get_params != NULL)
+        return encoder->get_params(params);
+    return 0;
+}
+
 const OSSL_PARAM *OSSL_ENCODER_settable_ctx_params(OSSL_ENCODER *encoder)
 {
     if (encoder != NULL && encoder->settable_ctx_params != NULL) {
@@ -454,69 +490,51 @@ const OSSL_PARAM *OSSL_ENCODER_settable_ctx_params(OSSL_ENCODER *encoder)
  * Encoder context support
  */
 
-/*
- * |encoder| value NULL is valid, and signifies that there is no encoder.
- * This is useful to provide fallback mechanisms.
- *  Functions that want to verify if there is a encoder can do so with
- * OSSL_ENCODER_CTX_get_encoder()
- */
-OSSL_ENCODER_CTX *OSSL_ENCODER_CTX_new(OSSL_ENCODER *encoder)
+OSSL_ENCODER_CTX *OSSL_ENCODER_CTX_new(void)
 {
     OSSL_ENCODER_CTX *ctx;
 
-    if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) == NULL) {
+    if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) == NULL)
         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_MALLOC_FAILURE);
-        return NULL;
-    }
-
-    ctx->encoder = encoder;
-    if (encoder != NULL && encoder->newctx != NULL) {
-        const OSSL_PROVIDER *prov = OSSL_ENCODER_provider(encoder);
-        void *provctx = ossl_provider_ctx(prov);
-
-        if (OSSL_ENCODER_up_ref(encoder)) {
-            ctx->encoderctx = encoder->newctx(provctx);
-        } else {
-            OSSL_ENCODER_free(encoder);
-            OPENSSL_free(ctx);
-            ctx = NULL;
-        }
-    }
 
     return ctx;
 }
 
-const OSSL_ENCODER *
-OSSL_ENCODER_CTX_get_encoder(OSSL_ENCODER_CTX *ctx)
+int OSSL_ENCODER_CTX_set_params(OSSL_ENCODER_CTX *ctx,
+                                const OSSL_PARAM params[])
 {
+    size_t i;
+    size_t l;
+
     if (!ossl_assert(ctx != NULL)) {
         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
         return 0;
     }
 
-    return ctx->encoder;
-}
+    if (ctx->encoder_insts == NULL)
+        return 1;
 
+    l = OSSL_ENCODER_CTX_get_num_encoders(ctx);
+    for (i = 0; i < l; i++) {
+        OSSL_ENCODER_INSTANCE *encoder_inst =
+            sk_OSSL_ENCODER_INSTANCE_value(ctx->encoder_insts, i);
+        OSSL_ENCODER *encoder = OSSL_ENCODER_INSTANCE_get_encoder(encoder_inst);
+        void *encoderctx = OSSL_ENCODER_INSTANCE_get_encoder_ctx(encoder_inst);
 
-int OSSL_ENCODER_CTX_set_params(OSSL_ENCODER_CTX *ctx,
-                                const OSSL_PARAM params[])
-{
-    if (!ossl_assert(ctx != NULL)) {
-        ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
-        return 0;
+        if (encoderctx == NULL || encoder->set_ctx_params == NULL)
+            continue;
+        if (!encoder->set_ctx_params(encoderctx, params))
+            return 0;
     }
-
-    if (ctx->encoder != NULL && ctx->encoder->set_ctx_params != NULL)
-        return ctx->encoder->set_ctx_params(ctx->encoderctx, params);
-    return 0;
+    return 1;
 }
 
 void OSSL_ENCODER_CTX_free(OSSL_ENCODER_CTX *ctx)
 {
     if (ctx != NULL) {
-        if (ctx->encoder != NULL && ctx->encoder->freectx != NULL)
-            ctx->encoder->freectx(ctx->encoderctx);
-        OSSL_ENCODER_free(ctx->encoder);
+        sk_OSSL_ENCODER_INSTANCE_pop_free(ctx->encoder_insts,
+                                          ossl_encoder_instance_free);
+        OPENSSL_free(ctx->construct_data);
         ossl_pw_clear_passphrase_data(&ctx->pwdata);
         OPENSSL_free(ctx);
     }
diff --git a/crypto/encode_decode/encoder_pkey.c b/crypto/encode_decode/encoder_pkey.c
index 7c63a76adb..6e1a80da9f 100644
--- a/crypto/encode_decode/encoder_pkey.c
+++ b/crypto/encode_decode/encoder_pkey.c
@@ -7,6 +7,7 @@
  * https://www.openssl.org/source/license.html
  */
 
+#include "e_os.h"                /* strcasecmp on Windows */
 #include <openssl/err.h>
 #include <openssl/ui.h>
 #include <openssl/params.h>
@@ -18,6 +19,8 @@
 #include "crypto/evp.h"
 #include "encoder_local.h"
 
+DEFINE_STACK_OF(OSSL_ENCODER)
+
 int OSSL_ENCODER_CTX_set_cipher(OSSL_ENCODER_CTX *ctx,
                                 const char *cipher_name,
                                 const char *propquery)
@@ -48,28 +51,82 @@ int OSSL_ENCODER_CTX_set_passphrase_ui(OSSL_ENCODER_CTX *ctx,
     return ossl_pw_set_ui_method(&ctx->pwdata, ui_method, ui_data);
 }
 
-int OSSL_ENCODER_CTX_set_passphrase_cb(OSSL_ENCODER_CTX *ctx,
-                                       pem_password_cb *cb, void *cbarg)
+int OSSL_ENCODER_CTX_set_pem_password_cb(OSSL_ENCODER_CTX *ctx,
+                                         pem_password_cb *cb, void *cbarg)
 {
     return ossl_pw_set_pem_password_cb(&ctx->pwdata, cb, cbarg);
 }
 
+int OSSL_ENCODER_CTX_set_passphrase_cb(OSSL_ENCODER_CTX *ctx,
+                                       OSSL_PASSPHRASE_CALLBACK *cb,
+                                       void *cbarg)
+{
+    return ossl_pw_set_ossl_passphrase_cb(&ctx->pwdata, cb, cbarg);
+}
+
 /*
  * Support for OSSL_ENCODER_CTX_new_by_TYPE:
  * finding a suitable encoder
  */
 
-struct selected_encoder_st {
+struct collected_encoder_st {
+    const char *output_type;
+    STACK_OF(OSSL_ENCODER) *encoders;
+    int error_occured;
+};
+
+static void collect_encoder(OSSL_ENCODER *encoder, void *arg)
+{
+    struct collected_encoder_st *data = arg;
+    OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END };
+    const char *output_type = NULL;
+
+    if (data->error_occured)
+        return;
+
+    /*
+     * Ask for the output type.  If the encoder doesn't answer to that,
+     * we refuse it.
+     */
+    params[0] =
+        OSSL_PARAM_construct_utf8_ptr(OSSL_ENCODER_PARAM_OUTPUT_TYPE,
+                                      (char **)&output_type, 0);
+    if (!encoder->get_params(params)
+        || !OSSL_PARAM_modified(&params[0])
+        || output_type == NULL
+        || strcasecmp(output_type, data->output_type) != 0)
+        return;
+
+    data->error_occured = 1;         /* Assume the worst */
+
+    if (!OSSL_ENCODER_up_ref(encoder) /* ref++ */)
+        return;
+    if (sk_OSSL_ENCODER_push(data->encoders, encoder) <= 0) {
+        OSSL_ENCODER_free(encoder);  /* ref-- */
+        return;
+    }
+
+    data->error_occured = 0;         /* All is good now */
+}
+
+struct collected_names_st {
     STACK_OF(OPENSSL_CSTRING) *names;
-    int error;
+    unsigned int error_occured:1;
 };
 
-static void cache_encoders(const char *name, void *data)
+static void collect_name(const char *name, void *arg)
 {
-    struct selected_encoder_st *d = data;
+    struct collected_names_st *data = arg;
+
+    if (data->error_occured)
+        return;
+
+    data->error_occured = 1;         /* Assume the worst */
 
-    if (sk_OPENSSL_CSTRING_push(d->names, name) <= 0)
-        d->error = 1;
+    if (sk_OPENSSL_CSTRING_push(data->names, name) <= 0)
+        return;
+
+    data->error_occured = 0;         /* All is good now */
 }
 
 /*
@@ -78,167 +135,224 @@ static void cache_encoders(const char *name, void *data)
  * intimate knowledge of the provider side object)
  */
 
-struct encoder_write_data_st {
-    OSSL_ENCODER_CTX *ctx;
-    BIO *out;
+struct construct_data_st {
+    const EVP_PKEY *pk;
+    int selection;
+
+    OSSL_ENCODER_INSTANCE *encoder_inst;
+    const void *obj;
+    void *constructed_obj;
 };
 
-static int encoder_write_cb(const OSSL_PARAM params[], void *arg)
+static int encoder_import_cb(const OSSL_PARAM params[], void *arg)
 {
-    struct encoder_write_data_st *write_data = arg;
-    OSSL_ENCODER_CTX *ctx = write_data->ctx;
-    BIO *out = write_data->out;
-
-    return ctx->encoder->encode_data(ctx->encoderctx, params,
-                                     (OSSL_CORE_BIO *)out,
-                                     ossl_pw_passphrase_callback_enc,
-                                     &ctx->pwdata);
-}
+    struct construct_data_st *construct_data = arg;
+    OSSL_ENCODER_INSTANCE *encoder_inst = construct_data->encoder_inst;
+    OSSL_ENCODER *encoder = OSSL_ENCODER_INSTANCE_get_encoder(encoder_inst);
+    void *encoderctx = OSSL_ENCODER_INSTANCE_get_encoder_ctx(encoder_inst);
 
-/*
- * Support for OSSL_ENCODER_to_bio:
- * Perform the actual output.
- */
+    construct_data->constructed_obj =
+        encoder->import_object(encoderctx, construct_data->selection, params);
 
-static int encoder_EVP_PKEY_to_bio(OSSL_ENCODER_CTX *ctx, BIO *out)
+    return (construct_data->constructed_obj != NULL);
+}
+
+static const void *
+encoder_construct_EVP_PKEY(OSSL_ENCODER_INSTANCE *encoder_inst, void *arg)
 {
-    const EVP_PKEY *pkey = ctx->object;
-    void *keydata = pkey->keydata;
-    EVP_KEYMGMT *keymgmt = pkey->keymgmt;
+    struct construct_data_st *data = arg;
+
+    if (data->obj == NULL) {
+        OSSL_ENCODER *encoder =
+            OSSL_ENCODER_INSTANCE_get_encoder(encoder_inst);
+        const EVP_PKEY *pk = data->pk;
+        const OSSL_PROVIDER *k_prov = EVP_KEYMGMT_provider(pk->keymgmt);
+        const OSSL_PROVIDER *e_prov = OSSL_ENCODER_provider(encoder);
+
+        if (k_prov != e_prov) {
+            data->encoder_inst = encoder_inst;
+
+            if (!evp_keymgmt_export(pk->keymgmt, pk->keydata, data->selection,
+                                    &encoder_import_cb, data))
+                return NULL;
+            data->obj = data->constructed_obj;
+        } else {
+            data->obj = pk->keydata;
+        }
+    }
 
-    /*
-     * OSSL_ENCODER_CTX_new() creates a context, even when the
-     * encoder it's given is NULL.  Callers can detect the lack
-     * of encoder with OSSL_ENCODER_CTX_get_encoder() and
-     * should take precautions, possibly call a fallback instead of
-     * OSSL_ENCODER_to_bio() / OSSL_ENCODER_to_fp().  If it's
-     * come this far, we return an error.
-     */
-    if (ctx->encoder == NULL)
-        return 0;
+    return data->obj;
+}
 
-    if (ctx->encoder->encode_object == NULL
-        || (OSSL_ENCODER_provider(ctx->encoder)
-            != EVP_KEYMGMT_provider(keymgmt))) {
-        struct encoder_write_data_st write_data;
+static void encoder_destruct_EVP_PKEY(void *arg)
+{
+    struct construct_data_st *data = arg;
 
-        write_data.ctx = ctx;
-        write_data.out = out;
+    if (data->encoder_inst != NULL) {
+        OSSL_ENCODER *encoder =
+            OSSL_ENCODER_INSTANCE_get_encoder(data->encoder_inst);
 
-        return evp_keymgmt_export(keymgmt, keydata, ctx->selection,
-                                  &encoder_write_cb, &write_data);
+        encoder->free_object(data->constructed_obj);
     }
-
-    return ctx->encoder->encode_object(ctx->encoderctx, keydata,
-                                       (OSSL_CORE_BIO *)out,
-                                       ossl_pw_passphrase_callback_enc,
-                                       &ctx->pwdata);
+    data->constructed_obj = NULL;
 }
 
 /*
  * OSSL_ENCODER_CTX_new_by_EVP_PKEY() returns a ctx with no encoder if
  * it couldn't find a suitable encoder.  This allows a caller to detect if
- * a suitable encoder was found, with OSSL_ENCODER_CTX_get_encoder(),
+ * a suitable encoder was found, with OSSL_ENCODER_CTX_get_num_encoder(),
  * and to use fallback methods if the result is NULL.
  */
-OSSL_ENCODER_CTX *OSSL_ENCODER_CTX_new_by_EVP_PKEY(const EVP_PKEY *pkey,
-                                                   const char *propquery)
+static int ossl_encoder_ctx_setup_for_EVP_PKEY(OSSL_ENCODER_CTX *ctx,
+                                               const EVP_PKEY *pkey,
+                                               int selection,
+                                               OPENSSL_CTX *libctx,
+                                               const char *propquery)
 {
-    OSSL_ENCODER_CTX *ctx = NULL;
-    OSSL_ENCODER *encoder = NULL;
-    EVP_KEYMGMT *keymgmt = pkey->keymgmt;
-    int selection = OSSL_KEYMGMT_SELECT_ALL;
+    struct construct_data_st *data = NULL;
+    int ok = 0;
 
-    if (!ossl_assert(pkey != NULL && propquery != NULL)) {
+    if (!ossl_assert(ctx != NULL) || !ossl_assert(pkey != NULL)) {
         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
-        return NULL;
+        return 0;
     }
 
-    if (keymgmt != NULL) {
-        const OSSL_PROVIDER *desired_prov = EVP_KEYMGMT_provider(keymgmt);
-        OPENSSL_CTX *libctx = ossl_provider_library_context(desired_prov);
-        struct selected_encoder_st sel_data;
-        OSSL_ENCODER *first = NULL;
-        const char *name;
+    if (pkey->keymgmt != NULL) {
+        OSSL_ENCODER *found = NULL;
+        const OSSL_PROVIDER *desired_prov = EVP_KEYMGMT_provider(pkey->keymgmt);
+        struct collected_encoder_st encoder_data;
+        struct collected_names_st keymgmt_data;
         int i;
 
+        if ((data = OPENSSL_zalloc(sizeof(*data))) == NULL) {
+            ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+
         /*
-         * Select the encoder in two steps.  First, get the names of all of
-         * the encoders.  Then determine which is the best one to use.
-         * This has to be broken because it isn't possible to fetch the
-         * encoders inside EVP_KEYMGMT_names_do_all() due to locking order
-         * inversions with the store lock.
-         */
-        sel_data.error = 0;
-        sel_data.names = sk_OPENSSL_CSTRING_new_null();
-        if (sel_data.names == NULL)
-            return NULL;
-        EVP_KEYMGMT_names_do_all(keymgmt, cache_encoders, &sel_data);
-        /*
-         * Ignore memory allocation errors that are indicated in sel_data.error
-         * in case a suitable provider does get found regardless.
+         * Select the encoder in two steps.  First, collect all encoders
+         * that have the correct output type, as well as all keymgmt names.
          */
+        encoder_data.output_type = ctx->output_type;
+        encoder_data.encoders = sk_OSSL_ENCODER_new_null();
+        encoder_data.error_occured = 0;
+        keymgmt_data.names = sk_OPENSSL_CSTRING_new_null();
+        keymgmt_data.error_occured = 0;
+        if (encoder_data.encoders == NULL || keymgmt_data.names == NULL) {
+            sk_OSSL_ENCODER_free(encoder_data.encoders);
+            sk_OPENSSL_CSTRING_free(keymgmt_data.names);
+            return 0;
+        }
+        OSSL_ENCODER_do_all_provided(libctx, collect_encoder, &encoder_data);
+        EVP_KEYMGMT_names_do_all(pkey->keymgmt, collect_name, &keymgmt_data);
 
-        /*
-         * Encoders offer two functions, one that handles object data in
-         * the form of a OSSL_PARAM array, and one that directly handles a
-         * provider side object.  The latter requires that the encoder
-         * is offered by the same provider that holds that object, but is
-         * more desirable because it usually provides faster encoding.
+        /*-
+         * Now we look for the most desirable encoder for our |pkey|.
+         *
+         * Encoders offer two functions:
+         *
+         * - one ('encode') that encodes a given provider-native object that
+         *   it knows intimately, so it must be from the same provider.
+         * - one ('import_object') that imports the parameters of an object
+         *   of the same type from a different provider, which is used to
+         *   create a temporary object that 'encode' can handle.
+         *
+         * It is, of course, more desirable to be able to use 'encode'
+         * directly without having to go through an export/import maneuver,
+         * but the latter allows us to have generic encoders.
          *
-         * When looking up possible encoders, we save the first that can
-         * handle an OSSL_PARAM array in |first| and use that if nothing
-         * better turns up.
+         * Of course, if |libctx| is different from |pkey|'s library context,
+         * we're going to have to do an export/import maneuvre no matter what.
          */
-        for (i = 0; i < sk_OPENSSL_CSTRING_num(sel_data.names); i++) {
-            name = sk_OPENSSL_CSTRING_value(sel_data.names, i);
-            encoder = OSSL_ENCODER_fetch(libctx, name, propquery);
-            if (encoder != NULL) {
-                if (OSSL_ENCODER_provider(encoder) == desired_prov
-                        && encoder->encode_object != NULL) {
-                    OSSL_ENCODER_free(first);
+        for (i = 0; i < sk_OSSL_ENCODER_num(encoder_data.encoders); i++) {
+            OSSL_ENCODER *encoder =
+                sk_OSSL_ENCODER_value(encoder_data.encoders, i);
+            int j;
+
+            /* Check that any of the |keymgmt| names match */
+            for (j = 0; j < sk_OPENSSL_CSTRING_num(keymgmt_data.names); j++) {
+                const char *name =
+                    sk_OPENSSL_CSTRING_value(keymgmt_data.names, j);
+
+                if (OSSL_ENCODER_is_a(encoder, name))
                     break;
-                }
-                if (first == NULL && encoder->encode_data != NULL)
-                    first = encoder;
-                else
-                    OSSL_ENCODER_free(encoder);
-                encoder = NULL;
+            }
+
+            if (j == sk_OPENSSL_CSTRING_num(keymgmt_data.names))
+                continue;
+
+            /* We found one!  Process it */
+            if (OSSL_ENCODER_provider(encoder) == desired_prov) {
+                /*
+                 * We found one in the same provider as the keymgmt.  Choose
+                 * it and stop looking.
+                 */
+                found = encoder;
+                break;
+            }
+            if (found == NULL && encoder->import_object != NULL) {
+                /*
+                 * We found one that's good enough.  Choose it for now, but
+                 * keep looking.
+                 */
+                found = encoder;
             }
         }
-        sk_OPENSSL_CSTRING_free(sel_data.names);
-        if (encoder == NULL)
-            encoder = first;
-
-        if (encoder != NULL) {
-            OSSL_PROPERTY_LIST *check = NULL, *current_props = NULL;
-
-            check = ossl_parse_query(libctx, "type=parameters");
-            current_props =
-                ossl_parse_property(libctx, OSSL_ENCODER_properties(encoder));
-            if (ossl_property_match_count(check, current_props) > 0)
-                selection = OSSL_KEYMGMT_SELECT_ALL_PARAMETERS;
-            ossl_property_free(current_props);
-            ossl_property_free(check);
+
+        if (found != NULL) {
+            (void)OSSL_ENCODER_CTX_add_encoder(ctx, found);
         } else {
-            if (sel_data.error)
+            if (encoder_data.error_occured)
                 ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_MALLOC_FAILURE);
             else
                 ERR_raise(ERR_LIB_OSSL_ENCODER,
                           OSSL_ENCODER_R_ENCODER_NOT_FOUND);
         }
+
+        sk_OPENSSL_CSTRING_free(keymgmt_data.names);
+        sk_OSSL_ENCODER_pop_free(encoder_data.encoders, OSSL_ENCODER_free);
     }
 
-    ctx = OSSL_ENCODER_CTX_new(encoder); /* refcnt(encoder)++ */
-    OSSL_ENCODER_free(encoder);          /* refcnt(encoder)-- */
+    if (OSSL_ENCODER_CTX_get_num_encoders(ctx) != 0) {
+        if (!OSSL_ENCODER_CTX_set_construct(ctx, encoder_construct_EVP_PKEY)
+            || !OSSL_ENCODER_CTX_set_construct_data(ctx, data)
+            || !OSSL_ENCODER_CTX_set_cleanup(ctx, encoder_destruct_EVP_PKEY))
+            goto err;
 
-    if (ctx != NULL) {
-        /* Setup for OSSL_ENCODE_to_bio() */
-        ctx->selection = selection;
-        ctx->object = pkey;
-        ctx->do_output = encoder_EVP_PKEY_to_bio;
+        data->pk = pkey;
+        data->selection = selection;
+
+        data = NULL;             /* Avoid it being freed */
     }
 
-    return ctx;
+    ok = 1;
+ err:
+    if (data != NULL) {
+        OSSL_ENCODER_CTX_set_construct_data(ctx, NULL);
+        OPENSSL_free(data);
+    }
+    return ok;
 }
 
+OSSL_ENCODER_CTX *OSSL_ENCODER_CTX_new_by_EVP_PKEY(const EVP_PKEY *pkey,
+                                                   const char *output_type,
+                                                   int selection,
+                                                   OPENSSL_CTX *libctx,
+                                                   const char *propquery)
+{
+    OSSL_ENCODER_CTX *ctx = NULL;
+
+    if ((ctx = OSSL_ENCODER_CTX_new()) == NULL) {
+        ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+    if (OSSL_ENCODER_CTX_set_output_type(ctx, output_type)
+        && OSSL_ENCODER_CTX_set_selection(ctx, selection)
+        && ossl_encoder_ctx_setup_for_EVP_PKEY(ctx, pkey, selection,
+                                               libctx, propquery)
+        && OSSL_ENCODER_CTX_add_extra(ctx, libctx, propquery))
+        return ctx;
+
+    OSSL_ENCODER_CTX_free(ctx);
+    return NULL;
+}
diff --git a/crypto/err/openssl.ec b/crypto/err/openssl.ec
index 037611d759..589ff1094c 100644
--- a/crypto/err/openssl.ec
+++ b/crypto/err/openssl.ec
@@ -40,8 +40,8 @@ L OSSL_STORE    include/openssl/store.h         crypto/store/store_err.c
 L ESS           include/openssl/ess.h           crypto/ess/ess_err.c
 L PROP          include/internal/property.h     crypto/property/property_err.c
 L PROV          providers/common/include/prov/providercommon.h providers/common/provider_err.c
-L OSSL_ENCODER  include/openssl/encoder.h       crypto/encoder/encoder_err.c
-L OSSL_DECODER  include/openssl/decoder.h       crypto/encoder/decoder_err.c
+L OSSL_ENCODER  include/openssl/encoder.h       crypto/encode_decode/encoder_err.c
+L OSSL_DECODER  include/openssl/decoder.h       crypto/encode_decode/decoder_err.c
 L HTTP          include/openssl/http.h          crypto/http/http_err.c
 
 # additional header files to be scanned for function names
diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt
index f26d07835f..3c2fa4a4e7 100644
--- a/crypto/err/openssl.txt
+++ b/crypto/err/openssl.txt
@@ -2712,6 +2712,7 @@ OCSP_R_UNSUPPORTED_REQUESTORNAME_TYPE:129:unsupported requestorname type
 OSSL_DECODER_R_MISSING_GET_PARAMS:100:missing get params
 OSSL_ENCODER_R_ENCODER_NOT_FOUND:101:encoder not found
 OSSL_ENCODER_R_INCORRECT_PROPERTY_QUERY:100:incorrect property query
+OSSL_ENCODER_R_MISSING_GET_PARAMS:102:missing get params
 OSSL_STORE_R_AMBIGUOUS_CONTENT_TYPE:107:ambiguous content type
 OSSL_STORE_R_BAD_PASSWORD_READ:115:bad password read
 OSSL_STORE_R_ERROR_VERIFYING_PKCS12_MAC:113:error verifying pkcs12 mac
diff --git a/crypto/evp/p_lib.c b/crypto/evp/p_lib.c
index 5e032b4053..aae71b30e2 100644
--- a/crypto/evp/p_lib.c
+++ b/crypto/evp/p_lib.c
@@ -1040,6 +1040,22 @@ int EVP_PKEY_is_a(const EVP_PKEY *pkey, const char *name)
     return EVP_KEYMGMT_is_a(pkey->keymgmt, name);
 }
 
+void EVP_PKEY_typenames_do_all(const EVP_PKEY *pkey,
+                               void (*fn)(const char *name, void *data),
+                               void *data)
+{
+    if (!evp_pkey_is_typed(pkey))
+        return;
+
+    if (!evp_pkey_is_provided(pkey)) {
+        const char *name = OBJ_nid2sn(EVP_PKEY_id(pkey));
+
+        fn(name, data);
+        return;
+    }
+    EVP_KEYMGMT_names_do_all(pkey->keymgmt, fn, data);
+}
+
 int EVP_PKEY_can_sign(const EVP_PKEY *pkey)
 {
     if (pkey->keymgmt == NULL) {
@@ -1163,6 +1179,8 @@ static int unsup_alg(BIO *out, const EVP_PKEY *pkey, int indent,
 }
 
 static int print_pkey(const EVP_PKEY *pkey, BIO *out, int indent,
+                      int selection /* For provided encoding */,
+                      OPENSSL_CTX *libctx /* For provided encoding */,
                       const char *propquery /* For provided encoding */,
                       int (*legacy_print)(BIO *out, const EVP_PKEY *pkey,
                                           int indent, ASN1_PCTX *pctx),
@@ -1176,8 +1194,9 @@ static int print_pkey(const EVP_PKEY *pkey, BIO *out, int indent,
     if (!print_set_indent(&out, &pop_f_prefix, &saved_indent, indent))
         return 0;
 
-    ctx = OSSL_ENCODER_CTX_new_by_EVP_PKEY(pkey, propquery);
-    if (OSSL_ENCODER_CTX_get_encoder(ctx) != NULL)
+    ctx = OSSL_ENCODER_CTX_new_by_EVP_PKEY(pkey, "TEXT", selection,
+                                           libctx, propquery);
+    if (OSSL_ENCODER_CTX_get_num_encoders(ctx) != 0)
         ret = OSSL_ENCODER_to_bio(ctx, out);
     OSSL_ENCODER_CTX_free(ctx);
 
@@ -1198,7 +1217,10 @@ static int print_pkey(const EVP_PKEY *pkey, BIO *out, int indent,
 int EVP_PKEY_print_public(BIO *out, const EVP_PKEY *pkey,
                           int indent, ASN1_PCTX *pctx)
 {
-    return print_pkey(pkey, out, indent, OSSL_ENCODER_PUBKEY_TO_TEXT_PQ,
+    return print_pkey(pkey, out, indent,
+                      OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS
+                      | OSSL_KEYMGMT_SELECT_PUBLIC_KEY,
+                      NULL, NULL,
                       (pkey->ameth != NULL ? pkey->ameth->pub_print : NULL),
                       pctx);
 }
@@ -1206,7 +1228,10 @@ int EVP_PKEY_print_public(BIO *out, const EVP_PKEY *pkey,
 int EVP_PKEY_print_private(BIO *out, const EVP_PKEY *pkey,
                            int indent, ASN1_PCTX *pctx)
 {
-    return print_pkey(pkey, out, indent, OSSL_ENCODER_PrivateKey_TO_TEXT_PQ,
+    return print_pkey(pkey, out, indent,
+                      OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS
+                      | OSSL_KEYMGMT_SELECT_KEYPAIR,
+                      NULL, NULL,
                       (pkey->ameth != NULL ? pkey->ameth->priv_print : NULL),
                       pctx);
 }
@@ -1214,7 +1239,8 @@ int EVP_PKEY_print_private(BIO *out, const EVP_PKEY *pkey,
 int EVP_PKEY_print_params(BIO *out, const EVP_PKEY *pkey,
                           int indent, ASN1_PCTX *pctx)
 {
-    return print_pkey(pkey, out, indent, OSSL_ENCODER_Parameters_TO_TEXT_PQ,
+    return print_pkey(pkey, out, indent, OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
+                      NULL, NULL,
                       (pkey->ameth != NULL ? pkey->ameth->param_print : NULL),
                       pctx);
 }
diff --git a/crypto/pem/pem_local.h b/crypto/pem/pem_local.h
index 81d1718e32..39dc462e54 100644
--- a/crypto/pem/pem_local.h
+++ b/crypto/pem/pem_local.h
@@ -12,17 +12,37 @@
  * moved here.
  */
 
+#include <openssl/core_dispatch.h>
 #include <openssl/pem.h>
 #include <openssl/encoder.h>
 
+/*
+ * Selectors, named according to the ASN.1 names used throughout libcrypto.
+ *
+ * Note that these are not absolutely mandatory, they are rather a wishlist
+ * of sorts.  The provider implementations are free to make choices that
+ * make sense for them, based on these selectors.
+ * For example, the EC backend is likely to really just output the private
+ * key to a PKCS#8 structure, even thought PEM_SELECTION_PrivateKey specifies
+ * the public key as well.  This is fine, as long as the corresponding
+ * decoding operation can return an object that contains what libcrypto
+ * expects.
+ */
+# define PEM_SELECTION_PUBKEY                                           \
+    (OSSL_KEYMGMT_SELECT_ALL_PARAMETERS | OSSL_KEYMGMT_SELECT_PUBLIC_KEY)
+# define PEM_SELECTION_PrivateKey                                       \
+    (OSSL_KEYMGMT_SELECT_ALL_PARAMETERS | OSSL_KEYMGMT_SELECT_KEYPAIR)
+# define PEM_SELECTION_Parameters OSSL_KEYMGMT_SELECT_ALL_PARAMETERS
+
 /* Alternative IMPLEMENT macros for provided encoders */
 
 # define IMPLEMENT_PEM_provided_write_body_vars(type, asn1)             \
     int ret = 0;                                                        \
-    const char *pq = OSSL_ENCODER_##asn1##_TO_PEM_PQ;                   \
-    OSSL_ENCODER_CTX *ctx = OSSL_ENCODER_CTX_new_by_##type(x, pq);      \
+    OSSL_ENCODER_CTX *ctx =                                             \
+        OSSL_ENCODER_CTX_new_by_##type(x, "PEM", PEM_SELECTION_##asn1,  \
+                                       NULL, NULL);                     \
                                                                         \
-    if (ctx != NULL && OSSL_ENCODER_CTX_get_encoder(ctx) == NULL) {     \
+    if (OSSL_ENCODER_CTX_get_num_encoders(ctx) == 0) {                  \
         OSSL_ENCODER_CTX_free(ctx);                                     \
         goto legacy;                                                    \
     }
@@ -45,8 +65,8 @@
                 && !OSSL_ENCODER_CTX_set_passphrase(ctx, kstr, klen))   \
                 ret = 0;                                                \
             else if (cb != NULL                                         \
-                     && !OSSL_ENCODER_CTX_set_passphrase_cb(ctx,        \
-                                                            cb, u))     \
+                     && !OSSL_ENCODER_CTX_set_pem_password_cb(ctx,      \
+                                                              cb, u))   \
                 ret = 0;                                                \
         }                                                               \
     }                                                                   \
diff --git a/crypto/pem/pem_pk8.c b/crypto/pem/pem_pk8.c
index 84d431820b..05d6c4ae83 100644
--- a/crypto/pem/pem_pk8.c
+++ b/crypto/pem/pem_pk8.c
@@ -9,6 +9,7 @@
 
 #include <stdio.h>
 #include "internal/cryptlib.h"
+#include <openssl/core_dispatch.h>
 #include <openssl/buffer.h>
 #include <openssl/objects.h>
 #include <openssl/evp.h>
@@ -20,13 +21,15 @@
 static int do_pk8pkey(BIO *bp, const EVP_PKEY *x, int isder,
                       int nid, const EVP_CIPHER *enc,
                       const char *kstr, int klen,
-                      pem_password_cb *cb, void *u);
+                      pem_password_cb *cb, void *u,
+                      OPENSSL_CTX *libctx, const char *propq);
 
 #ifndef OPENSSL_NO_STDIO
 static int do_pk8pkey_fp(FILE *bp, const EVP_PKEY *x, int isder,
                          int nid, const EVP_CIPHER *enc,
                          const char *kstr, int klen,
-                         pem_password_cb *cb, void *u);
+                         pem_password_cb *cb, void *u,
+                         OPENSSL_CTX *libctx, const char *propq);
 #endif
 /*
  * These functions write a private key in PKCS#8 format: it is a "drop in"
@@ -39,39 +42,40 @@ int PEM_write_bio_PKCS8PrivateKey_nid(BIO *bp, const EVP_PKEY *x, int nid,
                                       const char *kstr, int klen,
                                       pem_password_cb *cb, void *u)
 {
-    return do_pk8pkey(bp, x, 0, nid, NULL, kstr, klen, cb, u);
+    return do_pk8pkey(bp, x, 0, nid, NULL, kstr, klen, cb, u, NULL, NULL);
 }
 
 int PEM_write_bio_PKCS8PrivateKey(BIO *bp, const EVP_PKEY *x, const EVP_CIPHER *enc,
                                   const char *kstr, int klen,
                                   pem_password_cb *cb, void *u)
 {
-    return do_pk8pkey(bp, x, 0, -1, enc, kstr, klen, cb, u);
+    return do_pk8pkey(bp, x, 0, -1, enc, kstr, klen, cb, u, NULL, NULL);
 }
 
 int i2d_PKCS8PrivateKey_bio(BIO *bp, const EVP_PKEY *x, const EVP_CIPHER *enc,
                             const char *kstr, int klen,
                             pem_password_cb *cb, void *u)
 {
-    return do_pk8pkey(bp, x, 1, -1, enc, kstr, klen, cb, u);
+    return do_pk8pkey(bp, x, 1, -1, enc, kstr, klen, cb, u, NULL, NULL);
 }
 
 int i2d_PKCS8PrivateKey_nid_bio(BIO *bp, const EVP_PKEY *x, int nid,
                                 const char *kstr, int klen,
                                 pem_password_cb *cb, void *u)
 {
-    return do_pk8pkey(bp, x, 1, nid, NULL, kstr, klen, cb, u);
+    return do_pk8pkey(bp, x, 1, nid, NULL, kstr, klen, cb, u, NULL, NULL);
 }
 
 static int do_pk8pkey(BIO *bp, const EVP_PKEY *x, int isder, int nid,
                       const EVP_CIPHER *enc, const char *kstr, int klen,
-                      pem_password_cb *cb, void *u)
+                      pem_password_cb *cb, void *u,
+                      OPENSSL_CTX *libctx, const char *propq)
 {
     int ret = 0;
-    const char *pq = isder
-        ? OSSL_ENCODER_PrivateKey_TO_DER_PQ
-        : OSSL_ENCODER_PrivateKey_TO_PEM_PQ;
-    OSSL_ENCODER_CTX *ctx = OSSL_ENCODER_CTX_new_by_EVP_PKEY(x, pq);
+    const char *outtype = isder ? "DER" : "PEM";
+    OSSL_ENCODER_CTX *ctx =
+        OSSL_ENCODER_CTX_new_by_EVP_PKEY(x, outtype, OSSL_KEYMGMT_SELECT_ALL,
+                                         libctx, propq);
 
     if (ctx == NULL)
         return 0;
@@ -90,7 +94,7 @@ static int do_pk8pkey(BIO *bp, const EVP_PKEY *x, int isder, int nid,
         }
     }
 
-    if (OSSL_ENCODER_CTX_get_encoder(ctx) != NULL) {
+    if (OSSL_ENCODER_CTX_get_num_encoders(ctx) != 0) {
         ret = 1;
         if (enc != NULL) {
             ret = 0;
@@ -108,7 +112,7 @@ static int do_pk8pkey(BIO *bp, const EVP_PKEY *x, int isder, int nid,
                     && !OSSL_ENCODER_CTX_set_passphrase(ctx, ukstr, klen))
                     ret = 0;
                 else if (cb != NULL
-                         && !OSSL_ENCODER_CTX_set_passphrase_cb(ctx, cb, u))
+                         && !OSSL_ENCODER_CTX_set_pem_password_cb(ctx, cb, u))
                     ret = 0;
             }
         }
@@ -199,33 +203,34 @@ int i2d_PKCS8PrivateKey_fp(FILE *fp, const EVP_PKEY *x, const EVP_CIPHER *enc,
                            const char *kstr, int klen,
                            pem_password_cb *cb, void *u)
 {
-    return do_pk8pkey_fp(fp, x, 1, -1, enc, kstr, klen, cb, u);
+    return do_pk8pkey_fp(fp, x, 1, -1, enc, kstr, klen, cb, u, NULL, NULL);
 }
 
 int i2d_PKCS8PrivateKey_nid_fp(FILE *fp, const EVP_PKEY *x, int nid,
                                const char *kstr, int klen,
                                pem_password_cb *cb, void *u)
 {
-    return do_pk8pkey_fp(fp, x, 1, nid, NULL, kstr, klen, cb, u);
+    return do_pk8pkey_fp(fp, x, 1, nid, NULL, kstr, klen, cb, u, NULL, NULL);
 }
 
 int PEM_write_PKCS8PrivateKey_nid(FILE *fp, const EVP_PKEY *x, int nid,
                                   const char *kstr, int klen,
                                   pem_password_cb *cb, void *u)
 {
-    return do_pk8pkey_fp(fp, x, 0, nid, NULL, kstr, klen, cb, u);
+    return do_pk8pkey_fp(fp, x, 0, nid, NULL, kstr, klen, cb, u, NULL, NULL);
 }
 
 int PEM_write_PKCS8PrivateKey(FILE *fp, const EVP_PKEY *x, const EVP_CIPHER *enc,
                               const char *kstr, int klen,
                               pem_password_cb *cb, void *u)
 {
-    return do_pk8pkey_fp(fp, x, 0, -1, enc, kstr, klen, cb, u);
+    return do_pk8pkey_fp(fp, x, 0, -1, enc, kstr, klen, cb, u, NULL, NULL);
 }
 
 static int do_pk8pkey_fp(FILE *fp, const EVP_PKEY *x, int isder, int nid,
                          const EVP_CIPHER *enc, const char *kstr, int klen,
-                         pem_password_cb *cb, void *u)
+                         pem_password_cb *cb, void *u,
+                         OPENSSL_CTX *libctx, const char *propq)
 {
     BIO *bp;
     int ret;
@@ -234,7 +239,7 @@ static int do_pk8pkey_fp(FILE *fp, const EVP_PKEY *x, int isder, int nid,
         PEMerr(PEM_F_DO_PK8PKEY_FP, ERR_R_BUF_LIB);
         return 0;
     }
-    ret = do_pk8pkey(bp, x, isder, nid, enc, kstr, klen, cb, u);
+    ret = do_pk8pkey(bp, x, isder, nid, enc, kstr, klen, cb, u, libctx, propq);
     BIO_free(bp);
     return ret;
 }
diff --git a/crypto/property/property_parse.c b/crypto/property/property_parse.c
index 6d6ca9b266..d53961daea 100644
--- a/crypto/property/property_parse.c
+++ b/crypto/property/property_parse.c
@@ -596,9 +596,9 @@ int ossl_property_parse_init(OPENSSL_CTX *ctx)
         "provider",     /* Name of provider (default, legacy, fips) */
         "version",      /* Version number of this provider */
         "fips",         /* FIPS validated or FIPS supporting algorithm */
-        "format",       /* output format for encoders */
-        "type",         /* output type for encoders */
-        "input",        /* input type for decoders */
+        "output",       /* Output type for encoders */
+        "input",        /* Input type for decoders */
+        "structure",    /* Structure name for encoders and decoders */
     };
     size_t i;
 
diff --git a/crypto/x509/x_pubkey.c b/crypto/x509/x_pubkey.c
index cb47f917ad..a4d3c9fa5e 100644
--- a/crypto/x509/x_pubkey.c
+++ b/crypto/x509/x_pubkey.c
@@ -23,6 +23,7 @@
 #include <openssl/rsa.h>
 #include <openssl/dsa.h>
 #include <openssl/encoder.h>
+#include "internal/provider.h"
 
 struct X509_pubkey_st {
     X509_ALGOR *algor;
@@ -96,10 +97,14 @@ int X509_PUBKEY_set(X509_PUBKEY **x, EVP_PKEY *pkey)
             goto error;
         }
     } else if (pkey->keymgmt != NULL) {
+        const OSSL_PROVIDER *pkprov = EVP_KEYMGMT_provider(pkey->keymgmt);
+        OPENSSL_CTX *libctx = ossl_provider_library_context(pkprov);
         BIO *bmem = BIO_new(BIO_s_mem());
-        const char *encprop = OSSL_ENCODER_PUBKEY_TO_DER_PQ;
+        int selection = (OSSL_KEYMGMT_SELECT_PUBLIC_KEY
+                         | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS);
         OSSL_ENCODER_CTX *ectx =
-            OSSL_ENCODER_CTX_new_by_EVP_PKEY(pkey, encprop);
+            OSSL_ENCODER_CTX_new_by_EVP_PKEY(pkey, "DER", selection,
+                                             libctx, NULL);
 
         if (OSSL_ENCODER_to_bio(ectx, bmem)) {
             const unsigned char *der = NULL;
@@ -303,15 +308,17 @@ int i2d_PUBKEY(const EVP_PKEY *a, unsigned char **pp)
         }
         X509_PUBKEY_free(xpk);
     } else if (a->keymgmt != NULL) {
-        const char *encprop = OSSL_ENCODER_PUBKEY_TO_DER_PQ;
+        const OSSL_PROVIDER *pkprov = EVP_KEYMGMT_provider(a->keymgmt);
+        OPENSSL_CTX *libctx = ossl_provider_library_context(pkprov);
+        int selection = (OSSL_KEYMGMT_SELECT_PUBLIC_KEY
+                         | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS);
         OSSL_ENCODER_CTX *ctx =
-            OSSL_ENCODER_CTX_new_by_EVP_PKEY(a, encprop);
+            OSSL_ENCODER_CTX_new_by_EVP_PKEY(a, "DER", selection, libctx, NULL);
         BIO *out = BIO_new(BIO_s_mem());
         BUF_MEM *buf = NULL;
 
-        if (ctx != NULL
+        if (OSSL_ENCODER_CTX_get_num_encoders(ctx) != 0
             && out != NULL
-            && OSSL_ENCODER_CTX_get_encoder(ctx) != NULL
             && OSSL_ENCODER_to_bio(ctx, out)
             && BIO_get_mem_ptr(out, &buf) > 0) {
             ret = buf->length;
diff --git a/doc/man3/EVP_PKEY_is_a.pod b/doc/man3/EVP_PKEY_is_a.pod
index efc72ea110..6ca64de6b3 100644
--- a/doc/man3/EVP_PKEY_is_a.pod
+++ b/doc/man3/EVP_PKEY_is_a.pod
@@ -2,7 +2,8 @@
 
 =head1 NAME
 
-EVP_PKEY_is_a, EVP_PKEY_can_sign, EVP_PKEY_get0_first_alg_name
+EVP_PKEY_is_a, EVP_PKEY_can_sign, EVP_PKEY_typenames_do_all,
+EVP_PKEY_get0_first_alg_name
 - key type and capabilities functions
 
 =head1 SYNOPSIS
@@ -11,9 +12,11 @@ EVP_PKEY_is_a, EVP_PKEY_can_sign, EVP_PKEY_get0_first_alg_name
 
  int EVP_PKEY_is_a(const EVP_PKEY *pkey, const char *name);
  int EVP_PKEY_can_sign(const EVP_PKEY *pkey);
+ void EVP_PKEY_typenames_do_all(const EVP_PKEY *pkey,
+                                void (*fn)(const char *name, void *data),
+                                void *data);
  const char *EVP_PKEY_get0_first_alg_name(const EVP_PKEY *key);
 
-
 =head1 DESCRIPTION
 
 EVP_PKEY_is_a() checks if the key type of I<pkey> is I<name>.
@@ -22,6 +25,11 @@ EVP_PKEY_can_sign() checks if the functionality for the key type of
 I<pkey> supports signing.  No other check is done, such as whether
 I<pkey> contains a private key.
 
+EVP_PKEY_typenames_do_all() traverses all names for I<pkey>'s key type, and
+calls I<fn> with each name and I<data>.  For example, an RSA B<EVP_PKEY> may
+be named both C<RSA> and C<rsaEncryption>.
+The order of the names is undefined.
+
 EVP_PKEY_get0_first_alg_name() returns the first algorithm name that is found
 for the given I<pkey>. Note that the I<pkey> may have multiple synonyms
 associated with it. In this case it is undefined which one will be returned.
diff --git a/doc/man3/OSSL_DECODER_CTX.pod b/doc/man3/OSSL_DECODER_CTX.pod
index 7dbd7550df..bb8875ea4f 100644
--- a/doc/man3/OSSL_DECODER_CTX.pod
+++ b/doc/man3/OSSL_DECODER_CTX.pod
@@ -6,8 +6,25 @@ OSSL_DECODER_CTX,
 OSSL_DECODER_CTX_new,
 OSSL_DECODER_settable_ctx_params,
 OSSL_DECODER_CTX_set_params,
-OSSL_DECODER_CTX_free
-- Encoder context routines
+OSSL_DECODER_CTX_free,
+OSSL_DECODER_CTX_set_input_type,
+OSSL_DECODER_CTX_add_decoder,
+OSSL_DECODER_CTX_add_extra,
+OSSL_DECODER_CTX_get_num_decoders,
+OSSL_DECODER_INSTANCE,
+OSSL_DECODER_CONSTRUCT,
+OSSL_DECODER_CLEANUP,
+OSSL_DECODER_CTX_set_construct,
+OSSL_DECODER_CTX_set_construct_data,
+OSSL_DECODER_CTX_set_cleanup,
+OSSL_DECODER_CTX_get_construct,
+OSSL_DECODER_CTX_get_construct_data,
+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
+- Decoder context routines
 
 =head1 SYNOPSIS
 
@@ -21,38 +38,186 @@ OSSL_DECODER_CTX_free
                                  const OSSL_PARAM params[]);
  void OSSL_DECODER_CTX_free(OSSL_DECODER_CTX *ctx);
 
+ int OSSL_DECODER_CTX_set_input_type(OSSL_DECODER_CTX *ctx,
+                                     const char *input_type);
+ 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);
+
+ typedef struct ossl_decoder_instance_st OSSL_DECODER_INSTANCE;
+ OSSL_DECODER *
+ OSSL_DECODER_INSTANCE_get_decoder(OSSL_DECODER_INSTANCE *decoder_inst);
+ 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);
+
+ typedef int OSSL_DECODER_CONSTRUCT(OSSL_DECODER_INSTANCE *decoder_inst,
+                                    const OSSL_PARAM *object,
+                                    void *construct_data);
+ typedef void OSSL_DECODER_CLEANUP(void *construct_data);
+
+ int OSSL_DECODER_CTX_set_construct(OSSL_DECODER_CTX *ctx,
+                                    OSSL_DECODER_CONSTRUCT *construct);
+ int OSSL_DECODER_CTX_set_construct_data(OSSL_DECODER_CTX *ctx,
+                                         void *construct_data);
+ int OSSL_DECODER_CTX_set_cleanup(OSSL_DECODER_CTX *ctx,
+                                  OSSL_DECODER_CLEANUP *cleanup);
+ OSSL_DECODER_CONSTRUCT *OSSL_DECODER_CTX_get_construct(OSSL_DECODER_CTX *ctx);
+ void *OSSL_DECODER_CTX_get_construct_data(OSSL_DECODER_CTX *ctx);
+ OSSL_DECODER_CLEANUP *OSSL_DECODER_CTX_get_cleanup(OSSL_DECODER_CTX *ctx);
+
+ int OSSL_DECODER_export(OSSL_DECODER_INSTANCE *decoder_inst,
+                         void *reference, size_t reference_sz,
+                         OSSL_CALLBACK *export_cb, void *export_cbarg);
+
 =head1 DESCRIPTION
 
-B<OSSL_DECODER_CTX> is a context with which B<OSSL_DECODER>
-operations are performed.  The context typically holds values, both
-internal and supplied by the application, which are useful for the
-implementations supplied by providers.
+The B<OSSL_DECODER_CTX> holds data about multiple decoders, as needed to
+figure out what the input data is and to attempt to unpack it into one of
+several possible related results.  This also includes chaining decoders, so
+the output from one can become the input for another.  This allows having
+generic format decoders such as PEM to DER, as well as more specialized
+decoders like DER to RSA.
+
+The chains may be limited by specifying an input type, which is considered a
+starting point.  This is both considered by OSSL_DECODER_CTX_add_extra(),
+which will stop adding one more decoder implementations when it has already
+added those that take the specified input type, and functions like
+L<OSSL_DECODER_from_bio(3)>, which will only start the decoding process with
+the decoder implementations that take that input type.  For example, if the
+input type is set to C<DER>, a PEM to DER decoder will be ignored.
+
+The input type can also be NULL, which means that the caller doesn't know
+what type of input they have.  In this case, OSSL_DECODER_from_bio() will
+simply try with one decoder implementation after the other, and thereby
+discover what kind of input the caller gave it.
+
+For every decoding done, even an intermediary one, a constructor provided by
+the caller is called to attempt to construct an appropriate type / structure
+that the caller knows how to handle from the current decoding result.
+The constructor is set with OSSL_DECODER_CTX_set_construct().
+
+B<OSSL_DECODER_INSTANCE> is an opaque structure that contains data about the
+decoder that was just used, and that may be useful for the constructor.
+There are some functions to extract data from this type, described further
+down.
+
+=head2 Functions
 
 OSSL_DECODER_CTX_new() creates a new empty B<OSSL_DECODER_CTX>.
 
-OSSL_DECODER_settable_ctx_params() returns an L<OSSL_PARAM(3)>
-array of parameter descriptors.
+OSSL_DECODER_settable_ctx_params() returns an L<OSSL_PARAM(3)> array of
+parameter descriptors.
 
-OSSL_DECODER_CTX_set_params() attempts to set parameters specified
-with an L<OSSL_PARAM(3)> array I<params>.  These parameters are passed
-to all decoders that have been added to the I<ctx> so far.
-Parameters that an implementation doesn't recognise should be ignored
-by it.
+OSSL_DECODER_CTX_set_params() attempts to set parameters specified with an
+L<OSSL_PARAM(3)> array I<params>.  These parameters are passed to all
+decoders that have been added to the I<ctx> so far.  Parameters that an
+implementation doesn't recognise should be ignored by it.
 
 OSSL_DECODER_CTX_free() frees the given context I<ctx>.
 
+OSSL_DECODER_CTX_add_decoder() populates the B<OSSL_DECODER_CTX> I<ctx> with
+a decoder, to be used to attempt to decode some encoded input.
+
+OSSL_DECODER_CTX_add_extra() finds decoders that generate input for already
+added decoders, and adds them as well.  This is used to build decoder
+chains.
+
+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_get_num_decoders() gets the number of decoders currently
+added to the context I<ctx>.
+
+OSSL_DECODER_CTX_set_construct() sets the constructor I<construct>.
+
+OSSL_DECODER_CTX_set_construct_data() sets the constructor data that is
+passed to the constructor every time it's called.
+
+OSSL_DECODER_CTX_set_cleanup() sets the constructor data I<cleanup>
+function.  This is called by L<OSSL_DECODER_CTX_free(3)>.
+
+OSSL_DECODER_CTX_get_construct(), OSSL_DECODER_CTX_get_construct_data() and
+OSSL_DECODER_CTX_get_cleanup() return the values that have been set by
+OSSL_DECODER_CTX_set_construct(), OSSL_DECODER_CTX_set_construct_data() and
+OSSL_DECODER_CTX_set_cleanup() respectively.
+
+OSSL_DECODER_export() is a fallback function for constructors that cannot
+use the data they get directly for diverse reasons.  It takes the same
+decode instance I<decoder_inst> that the constructor got and an object 
+I<reference>, unpacks the object which it refers to, and exports it by
+creating an L<OSSL_PARAM(3)> array that it then passes to I<export_cb>,
+along with I<export_arg>.
+
+=head2 Constructor
+
+A B<OSSL_DECODER_CONSTRUCT> gets the following arguments:
+
+=over 4
+
+=item I<decoder_inst>
+
+The B<OSSL_DECODER_INSTANCE> for the decoder from which the constructor gets
+its data.
+
+=item I<object>
+
+A provider-native object abstraction produced by the decoder.  Further
+information on the provider-native object abstraction can be found in
+L<provider-object(7)>.
+
+=item I<construct_data>
+
+The pointer that was set with OSSL_DECODE_CTX_set_construct_data().
+
+=back
+
+The constructor is expected to return 1 when the data it receives can be
+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_ctx() can be used to get the decoder
+method'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>.
+
 =head1 RETURN VALUES
 
-OSSL_DECODER_CTX_new() returns a pointer to a
-B<OSSL_DECODER_CTX>, or NULL if the context structure couldn't be
-allocated.
+OSSL_DECODER_CTX_new() returns a pointer to a B<OSSL_DECODER_CTX>, or NULL
+if the context structure couldn't be allocated.
+
+OSSL_DECODER_settable_ctx_params() returns an L<OSSL_PARAM(3)> array, or
+NULL if none is available.
+
+OSSL_DECODER_CTX_set_params() returns 1 if all recognised parameters were
+valid, or 0 if one of them was invalid or caused some other failure in the
+implementation.
+
+OSSL_DECODER_CTX_add_decoder(), OSSL_DECODER_CTX_add_extra(),
+OSSL_DECODER_CTX_set_construct(), OSSL_DECODER_CTX_set_construct_data() and
+OSSL_DECODER_CTX_set_cleanup() return 1 on success, or 0 on failure.
+
+OSSL_DECODER_CTX_get_construct(), OSSL_DECODER_CTX_get_construct_data() and
+OSSL_DECODER_CTX_get_cleanup() return the current pointers to the
+constructor, the constructor data and the cleanup functions, respectively.
+
+OSSL_DECODER_CTX_num_decoders() returns the current number of decoders.  It
+returns 0 if I<ctx> is NULL.
+
+OSSL_DECODER_export() returns 1 on success, or 0 on failure.
 
-OSSL_DECODER_settable_ctx_params() returns an L<OSSL_PARAM(3)>
-array, or NULL if none is available.
+OSSL_DECODER_INSTANCE_decoder() returns an B<OSSL_DECODER> pointer on
+success, or NULL on failure.
 
-OSSL_DECODER_CTX_set_params() returns 1 if all recognised
-parameters were valid, or 0 if one of them was invalid or caused some
-other failure in the implementation.
+OSSL_DECODER_INSTANCE_decoder_ctx() returns a provider context pointer on
+success, or NULL on failure.
 
 =head1 SEE ALSO
 
diff --git a/doc/man3/OSSL_DECODER_CTX_new_by_EVP_PKEY.pod b/doc/man3/OSSL_DECODER_CTX_new_by_EVP_PKEY.pod
index 620688e322..4190c4232b 100644
--- a/doc/man3/OSSL_DECODER_CTX_new_by_EVP_PKEY.pod
+++ b/doc/man3/OSSL_DECODER_CTX_new_by_EVP_PKEY.pod
@@ -32,63 +32,60 @@ OSSL_DECODER_CTX_set_passphrase_cb
 
 =head1 DESCRIPTION
 
-OSSL_DECODER_CTX_new_by_EVP_PKEY() is a utility function that
-creates a B<OSSL_DECODER_CTX>, finds all applicable decoder
-implementations and sets them up, so all the caller has to do next is
-call functions like OSSL_DECODE_from_bio().
-
-Internally OSSL_DECODER_CTX_new_by_EVP_PKEY() searches for all
-available L<EVP_KEYMGMT(3)> implementations, and then builds a list of all
-potential decoder implementations that may be able to process the
-encoded input into data suitable for B<EVP_PKEY>s.  All these
-implementations are implicitly fetched using I<libctx> and I<propquery>.
-
-The search of decoder implementations can be limited with
-I<input_type>, which specifies a starting input type.  This is further
-explained in L<OSSL_DECODER_CTX_set_input_type(3)>.
-
-If no suitable decoder was found, OSSL_DECODER_CTX_new_by_EVP_PKEY()
-still creates a B<OSSL_DECODER_CTX>, but with no associated
-decoder (L<OSSL_DECODER_CTX_num_decoders(3)> returns
-zero).  This helps the caller distinguish between an error when
-creating the B<OSSL_DECODER_CTX>, and the lack the decoder
-support and act accordingly.
-
-OSSL_DECODER_CTX_set_passphrase() gives the implementation a
-pass phrase to use when decrypting the encoded private key.
-Alternatively, a pass phrase callback may be specified with the
-following functions.
-
-OSSL_DECODER_CTX_set_pem_password_cb(),
-OSSL_DECODER_CTX_set_passphrase_ui() and
-OSSL_DECODER_CTX_set_passphrase_cb() set up a callback method that
-the implementation can use to prompt for a pass phrase, giving the caller
-the choice of prefered pass phrase callback form.  These are called
-indirectly, through an internal B<OSSL_PASSPHRASE_CALLBACK> function.
-
-The internal B<OSSL_PASSPHRASE_CALLBACK> function caches the pass phrase,
-to be re-used in all decodings that are performed in the same decoding run
-(for example, within one L<OSSL_DECODER_from_bio(3)> call).
+OSSL_DECODER_CTX_new_by_EVP_PKEY() is a utility function that creates a
+B<OSSL_DECODER_CTX>, finds all applicable decoder implementations and sets
+them up, so all the caller has to do next is call functions like
+OSSL_DECODE_from_bio().
+
+Internally OSSL_DECODER_CTX_new_by_EVP_PKEY() searches for all available
+L<EVP_KEYMGMT(3)> implementations, and then builds a list of all potential
+decoder implementations that may be able to process the encoded input into
+data suitable for B<EVP_PKEY>s.  All these implementations are implicitly
+fetched using I<libctx> and I<propquery>.
+
+The search of decoder implementations can be limited with I<input_type>,
+which specifies a starting input type.  This is further explained in
+L<OSSL_DECODER_CTX_set_input_type(3)>.
+
+If no suitable decoder implementation is found,
+OSSL_DECODER_CTX_new_by_EVP_PKEY() still creates a B<OSSL_DECODER_CTX>, but
+with no associated decoder (L<OSSL_DECODER_CTX_get_num_decoders(3)> returns
+zero).  This helps the caller to distinguish between an error when creating
+the B<OSSL_ENCODER_CTX> and missing encoder implementation, and allows it to
+act accordingly.
+
+OSSL_DECODER_CTX_set_passphrase() gives the implementation a pass phrase to
+use when decrypting the encoded private key. Alternatively, a pass phrase
+callback may be specified with the following functions.
+
+OSSL_DECODER_CTX_set_pem_password_cb(), OSSL_DECODER_CTX_set_passphrase_ui()
+and OSSL_DECODER_CTX_set_passphrase_cb() set up a callback method that the
+implementation can use to prompt for a pass phrase, giving the caller the
+choice of prefered pass phrase callback form.  These are called indirectly,
+through an internal B<OSSL_PASSPHRASE_CALLBACK> function.
+
+The internal B<OSSL_PASSPHRASE_CALLBACK> function caches the pass phrase, to
+be re-used in all decodings that are performed in the same decoding run (for
+example, within one L<OSSL_DECODER_from_bio(3)> call).
 
 =head1 RETURN VALUES
 
 OSSL_DECODER_CTX_new_by_EVP_PKEY() returns a pointer to a
 B<OSSL_DECODER_CTX>, or NULL if it couldn't be created.
 
-OSSL_DECODER_CTX_set_passphrase(),
-OSSL_DECODER_CTX_set_pem_password_cb(),
+OSSL_DECODER_CTX_set_passphrase(), OSSL_DECODER_CTX_set_pem_password_cb(),
 OSSL_DECODER_CTX_set_passphrase_ui() and
-OSSL_DECODER_CTX_set_passphrase_cb()
-all return 1 on success, or 0 on failure.
+OSSL_DECODER_CTX_set_passphrase_cb() all return 1 on success, or 0 on
+failure.
 
 =head1 NOTES
 
 Parts of the function names are made to match already existing OpenSSL
 names.
 
-B<EVP_PKEY> in OSSL_DECODER_CTX_new_by_EVP_PKEY() matches the type
-name, thus making for the naming pattern
-B<OSSL_DECODER_CTX_new_by_I<TYPE>>() when new types are handled.
+B<EVP_PKEY> in OSSL_DECODER_CTX_new_by_EVP_PKEY() matches the type name,
+thus making for the naming pattern B<OSSL_DECODER_CTX_new_by_I<TYPE>>() when
+new types are handled.
 
 =head1 SEE ALSO
 
diff --git a/doc/man3/OSSL_DECODER_from_bio.pod b/doc/man3/OSSL_DECODER_from_bio.pod
index 560231fe23..7de550746e 100644
--- a/doc/man3/OSSL_DECODER_from_bio.pod
+++ b/doc/man3/OSSL_DECODER_from_bio.pod
@@ -3,23 +3,7 @@
 =head1 NAME
 
 OSSL_DECODER_from_bio,
-OSSL_DECODER_from_fp,
-OSSL_DECODER_CTX_set_input_type,
-OSSL_DECODER_CTX_add_decoder,
-OSSL_DECODER_CTX_add_extra,
-OSSL_DECODER_CTX_num_decoders,
-OSSL_DECODER_INSTANCE,
-OSSL_DECODER_CONSTRUCT,
-OSSL_DECODER_CLEANUP,
-OSSL_DECODER_CTX_set_construct,
-OSSL_DECODER_CTX_set_construct_data,
-OSSL_DECODER_CTX_set_cleanup,
-OSSL_DECODER_CTX_get_construct,
-OSSL_DECODER_CTX_get_construct_data,
-OSSL_DECODER_CTX_get_cleanup,
-OSSL_DECODER_export,
-OSSL_DECODER_INSTANCE_decoder,
-OSSL_DECODER_INSTANCE_decoder_ctx
+OSSL_DECODER_from_fp
 - Routines to perform a decoding
 
 =head1 SYNOPSIS
@@ -29,36 +13,6 @@ OSSL_DECODER_INSTANCE_decoder_ctx
  int OSSL_DECODER_from_bio(OSSL_DECODER_CTX *ctx, BIO *in);
  int OSSL_DECODER_from_fp(OSSL_DECODER_CTX *ctx, FILE *fp);
 
- int OSSL_DECODER_CTX_set_input_type(OSSL_DECODER_CTX *ctx,
-                                     const char *input_type);
- 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_num_decoders(OSSL_DECODER_CTX *ctx);
-
- typedef struct ossl_decoder_instance_st OSSL_DECODER_INSTANCE;
- OSSL_DECODER *
- OSSL_DECODER_INSTANCE_decoder(OSSL_DECODER_INSTANCE *decoder_inst);
- void *OSSL_DECODER_INSTANCE_decoder_ctx(OSSL_DECODER_INSTANCE *decoder_inst);
-
- typedef int (OSSL_DECODER_CONSTRUCT)(OSSL_DECODER_INSTANCE *decoder_inst,
-                                      const OSSL_PARAM *object,
-                                      void *construct_data);
- typedef void (OSSL_DECODER_CLEANUP)(void *construct_data);
-
- int OSSL_DECODER_CTX_set_construct(OSSL_DECODER_CTX *ctx,
-                                    OSSL_DECODER_CONSTRUCT *construct);
- int OSSL_DECODER_CTX_set_construct_data(OSSL_DECODER_CTX *ctx,
-                                         void *construct_data);
- int OSSL_DECODER_CTX_set_cleanup(OSSL_DECODER_CTX *ctx,
-                                  OSSL_DECODER_CLEANUP *cleanup);
- OSSL_DECODER_CONSTRUCT *OSSL_DECODER_CTX_get_construct(OSSL_DECODER_CTX *ctx);
- void *OSSL_DECODER_CTX_get_construct_data(OSSL_DECODER_CTX *ctx);
- OSSL_DECODER_CLEANUP *OSSL_DECODER_CTX_get_cleanup(OSSL_DECODER_CTX *ctx);
-
- int OSSL_DECODER_export(OSSL_DECODER_INSTANCE *decoder_inst,
-                         void *reference, size_t reference_sz,
-                         OSSL_CALLBACK *export_cb, void *export_cbarg);
-
 Feature availability macros:
 
 =over 4
@@ -70,146 +24,17 @@ is undefined.
 
 =head1 DESCRIPTION
 
-The B<OSSL_DECODER_CTX> holds data about multiple decoders, as
-needed to figure out what the input data is and to attempt to unpack it into
-one of several possible related results.  This also includes chaining
-decoders, so the output from one can become the input for another.
-This allows having generic format decoders such as PEM to DER, as well
-as more specialized decoders like DER to RSA.
-
-The chains may be limited by specifying an input type, which is considered a
-starting point.
-This is both considered by OSSL_DECODER_CTX_add_extra(), which will
-stop adding on more decoder implementations when it has already added
-those that take the specified input type, and OSSL_DECODER_from_bio(),
-which will only start the decoding process with the decoder
-implementations that take that input type.  For example, if the input type
-is set to C<DER>, a PEM to DER decoder will be ignored.
-
-The input type can also be NULL, which means that the caller doesn't know
-what type of input they have.  In this case, OSSL_DECODER_from_bio()
-will simply try with one decoder implementation after the other, and
-thereby discover what kind of input the caller gave it.
-
-For every decoding done, even an intermediary one, a constructor
-provided by the caller is called to attempt to construct an appropriate type
-/ structure that the caller knows how to handle from the current
-decoding result.
-The constructor is set with OSSL_DECODER_CTX_set_construct().
-
-B<OSSL_DECODER_INSTANCE> is an opaque structure that contains
-data about the decoder that was just used, and that may be
-useful for the constructor.  There are some functions to extract data
-from this type, described further down.
-
-=head2 Functions
-
-OSSL_DECODER_from_bio() runs the decoding process for the
-context I<ctx>, with the input coming from the B<BIO> I<in>.  Should
-it make a difference, it's recommended to have the BIO set in binary
-mode rather than text mode.
+OSSL_DECODER_from_bio() runs the decoding process for the context I<ctx>,
+with the input coming from the B<BIO> I<in>.  Should it make a difference,
+it's recommended to have the BIO set in binary mode rather than text mode.
 
 OSSL_DECODER_from_fp() does the same thing as OSSL_DECODER_from_bio(),
 except that the input is coming from the B<FILE> I<fp>.
 
-OSSL_DECODER_CTX_add_decoder() populates the B<OSSL_DECODER_CTX>
-I<ctx> with a decoder, to be used to attempt to decode some
-encoded input.
-
-OSSL_DECODER_CTX_add_extra() finds decoders that generate
-input for already added decoders, and adds them as well.  This is
-used to build decoder chains.
-
-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_num_decoders() gets the number of
-decoders currently added to the context I<ctx>.
-
-OSSL_DECODER_CTX_set_construct() sets the constructor I<construct>.
-
-OSSL_DECODER_CTX_set_construct_data() sets the constructor data that is
-passed to the constructor every time it's called.
-
-OSSL_DECODER_CTX_set_cleanup() sets the constructor data I<cleanup>
-function.  This is called by L<OSSL_DECODER_CTX_free(3)>.
-
-OSSL_DECODER_CTX_get_construct(),
-OSSL_DECODER_CTX_get_construct_data() and
-OSSL_DECODER_CTX_get_cleanup()
-return the values that have been set by
-OSSL_DECODER_CTX_set_construct(),
-OSSL_DECODER_CTX_set_construct_data() and
-OSSL_DECODER_CTX_set_cleanup() respectively.
-
-OSSL_DECODER_export() is a fallback function for constructors that
-cannot use the data they get directly for diverse reasons.  It takes the same
-decode instance I<decoder_inst> that the constructor got and an object
-I<reference>, unpacks the object which it refers to, and exports it by creating
-an L<OSSL_PARAM(3)> array that it then passes to I<export_cb>, along with
-I<export_arg>.
-
-OSSL_DECODER_INSTANCE_decoder() can be used to get the
-decoder method from a decoder instance I<decoder_inst>.
-
-OSSL_DECODER_INSTANCE_decoder-ctx() can be used to get the
-decoder method's provider context from a decoder instance
-I<decoder_inst>.
-
-=head2 Constructor
-
-A B<OSSL_DECODER_CONSTRUCT> gets the following arguments:
-
-=over 4
-
-=item I<decoder_inst>
-
-The B<OSSL_DECODER_INSTANCE> for the decoder from which
-the constructor gets its data.
-
-=item I<object>
-
-A provider-native object abstraction produced by the decoder.  Further
-information on the provider-native object abstraction can be found in
-L<provider-object(7)>.
-
-=item I<construct_data>
-
-The pointer that was set with OSSL_DECODE_CTX_set_construct_data().
-
-=back
-
-The constructor is expected to return 1 when the data it receives can
-be constructed, otherwise 0.
-
 =head1 RETURN VALUES
 
-OSSL_DECODER_from_bio() and OSSL_DECODER_from_fp() return 1 on
-success, or 0 on failure.
-
-OSSL_DECODER_CTX_add_decoder(),
-OSSL_DECODER_CTX_add_extra(),
-OSSL_DECODER_CTX_set_construct(),
-OSSL_DECODER_CTX_set_construct_data() and
-OSSL_DECODER_CTX_set_cleanup() return 1 on success, or 0 on
-failure.
-
-OSSL_DECODER_CTX_get_construct(),
-OSSL_DECODER_CTX_get_construct_data() and
-OSSL_DECODER_CTX_get_cleanup() return the current pointers to the
-cosntructor, the constructor data and the cleanup functions, respectively.
-
-OSSL_DECODER_CTX_num_decoders() returns the current
-number of decoders.  It returns 0 if I<ctx> is NULL.
-
-OSSL_DECODER_export() returns 1 on success, or 0 on failure.
-
-OSSL_DECODER_INSTANCE_decoder() returns an
-B<OSSL_DECODER> pointer on success, or NULL on failure.
-
-OSSL_DECODER_INSTANCE_decoder_ctx() returns a provider
-context pointer on success, or NULL on failure.>
+OSSL_DECODER_from_bio() and OSSL_DECODER_from_fp() return 1 on success, or 0
+on failure.
 
 =begin comment TODO(3.0) Add examples!
 
diff --git a/doc/man3/OSSL_ENCODER.pod b/doc/man3/OSSL_ENCODER.pod
index d8998310bd..a1b7c4b76c 100644
--- a/doc/man3/OSSL_ENCODER.pod
+++ b/doc/man3/OSSL_ENCODER.pod
@@ -11,7 +11,9 @@ OSSL_ENCODER_properties,
 OSSL_ENCODER_is_a,
 OSSL_ENCODER_number,
 OSSL_ENCODER_do_all_provided,
-OSSL_ENCODER_names_do_all
+OSSL_ENCODER_names_do_all,
+OSSL_ENCODER_gettable_params,
+OSSL_ENCODER_get_params
 - Encoder method routines
 
 =head1 SYNOPSIS
@@ -34,11 +36,11 @@ OSSL_ENCODER_names_do_all
  void OSSL_ENCODER_names_do_all(const OSSL_ENCODER *encoder,
                                 void (*fn)(const char *name, void *data),
                                 void *data);
+ const OSSL_PARAM *OSSL_ENCODER_gettable_params(OSSL_ENCODER *encoder);
+ int OSSL_ENCODER_get_params(OSSL_ENCODER_CTX *ctx, const OSSL_PARAM params[]);
 
 =head1 DESCRIPTION
 
-=for comment Future development should also talk about decoding
-
 B<OSSL_ENCODER> is a method for encoders, which know how to
 encode an object of some kind to a encoded form, such as PEM,
 DER, or even human readable text.
@@ -78,6 +80,13 @@ implementations by all activated providers in the library context
 I<libctx>, and for each of the implementations, calls I<fn> with the
 implementation method and I<data> as arguments.
 
+OSSL_ENCODER_gettable_params() returns an L<OSSL_PARAM(3)>
+array of parameter descriptors.
+
+OSSL_ENCODER_get_params() attempts to get parameters specified
+with an L<OSSL_PARAM(3)> array I<params>.  Parameters that the
+implementation doesn't recognise should be ignored.
+
 =head1 NOTES
 
 OSSL_ENCODER_fetch() may be called implicitly by other fetching
diff --git a/doc/man3/OSSL_ENCODER_CTX.pod b/doc/man3/OSSL_ENCODER_CTX.pod
index bf339c6a4f..ccf847db47 100644
--- a/doc/man3/OSSL_ENCODER_CTX.pod
+++ b/doc/man3/OSSL_ENCODER_CTX.pod
@@ -4,10 +4,24 @@
 
 OSSL_ENCODER_CTX,
 OSSL_ENCODER_CTX_new,
-OSSL_ENCODER_CTX_get_encoder,
 OSSL_ENCODER_settable_ctx_params,
 OSSL_ENCODER_CTX_set_params,
-OSSL_ENCODER_CTX_free
+OSSL_ENCODER_CTX_free,
+OSSL_ENCODER_CTX_set_output_type,
+OSSL_ENCODER_CTX_set_selection,
+OSSL_ENCODER_CTX_add_encoder,
+OSSL_ENCODER_CTX_add_extra,
+OSSL_ENCODER_CTX_get_num_encoders,
+OSSL_ENCODER_INSTANCE,
+OSSL_ENCODER_INSTANCE_get_encoder,
+OSSL_ENCODER_INSTANCE_get_encoder_ctx,
+OSSL_ENCODER_INSTANCE_get_input_type,
+OSSL_ENCODER_INSTANCE_get_output_type,
+OSSL_ENCODER_CONSTRUCT,
+OSSL_ENCODER_CLEANUP,
+OSSL_ENCODER_CTX_set_construct,
+OSSL_ENCODER_CTX_set_construct_data,
+OSSL_ENCODER_CTX_set_cleanup
 - Encoder context routines
 
 =head1 SYNOPSIS
@@ -16,36 +30,66 @@ OSSL_ENCODER_CTX_free
 
  typedef struct ossl_encoder_ctx_st OSSL_ENCODER_CTX;
 
- OSSL_ENCODER_CTX *OSSL_ENCODER_CTX_new(OSSL_ENCODER *encoder);
- const OSSL_ENCODER *OSSL_ENCODER_CTX_get_encoder(OSSL_ENCODER_CTX *ctx);
+ OSSL_ENCODER_CTX *OSSL_ENCODER_CTX_new();
  const OSSL_PARAM *OSSL_ENCODER_settable_ctx_params(OSSL_ENCODER *encoder);
  int OSSL_ENCODER_CTX_set_params(OSSL_ENCODER_CTX *ctx,
                                  const OSSL_PARAM params[]);
  void OSSL_ENCODER_CTX_free(OSSL_ENCODER_CTX *ctx);
 
+ int OSSL_ENCODER_CTX_set_output_type(OSSL_ENCODER_CTX *ctx,
+                                      const char *output_type);
+ int OSSL_ENCODER_CTX_set_selection(OSSL_ENCODER_CTX *ctx, int selection);
+
+ int OSSL_ENCODER_CTX_add_encoder(OSSL_ENCODER_CTX *ctx, OSSL_ENCODER *encoder);
+ int OSSL_ENCODER_CTX_add_extra(OSSL_ENCODER_CTX *ctx,
+                                OPENSSL_CTX *libctx, const char *propq);
+ int OSSL_ENCODER_CTX_get_num_encoders(OSSL_ENCODER_CTX *ctx);
+
+ typedef struct ossl_encoder_instance_st OSSL_ENCODER_INSTANCE;
+ OSSL_ENCODER *
+ OSSL_ENCODER_INSTANCE_get_encoder(OSSL_ENCODER_INSTANCE *encoder_inst);
+ void *
+ OSSL_ENCODER_INSTANCE_get_encoder_ctx(OSSL_ENCODER_INSTANCE *encoder_inst);
+ const char *
+ OSSL_ENCODER_INSTANCE_get_input_type(OSSL_ENCODER_INSTANCE *encoder_inst);
+ const char *
+ OSSL_ENCODER_INSTANCE_get_output_type(OSSL_ENCODER_INSTANCE *encoder_inst);
+
+ typedef const void *OSSL_ENCODER_CONSTRUCT(OSSL_ENCODER_INSTANCE *encoder_inst,
+                                            void *construct_data);
+ typedef void OSSL_ENCODER_CLEANUP(void *construct_data);
+
+ int OSSL_ENCODER_CTX_set_construct(OSSL_ENCODER_CTX *ctx,
+                                    OSSL_ENCODER_CONSTRUCT *construct);
+ int OSSL_ENCODER_CTX_set_construct_data(OSSL_ENCODER_CTX *ctx,
+                                         void *construct_data);
+ int OSSL_ENCODER_CTX_set_cleanup(OSSL_ENCODER_CTX *ctx,
+                                  OSSL_ENCODER_CLEANUP *cleanup);
+
 =head1 DESCRIPTION
 
-B<OSSL_ENCODER_CTX> is a context with which B<OSSL_ENCODER>
-operations are performed.  The context typically holds values, both
-internal and supplied by the application, which are useful for the
-implementations supplied by providers.
+Encoding an input object to the desired encoding may be done with a chain of
+encoder implementations, which means that the output from one encoder may be
+the input for the next in the chain.  The B<OSSL_ENCODER_CTX> holds all the
+data about these encoders.  This allows having generic format encoders such
+as DER to PEM, as well as more specialized encoders like RSA to DER.
 
-OSSL_ENCODER_CTX_new() creates a B<OSSL_ENCODER_CTX> associated
-with the encoder I<encoder>.  NULL is a valid I<encoder>, the context will
-be created anyway, it's just not very useful.  This is intentional, to
-distinguish between errors in allocating the context or assigning it
-values on one hand, and the lack of encoder support on the other.
+The final output type must be given, and a chain of encoders must end with
+an implementation that produces that output type.
 
-=begin comment
+At the beginning of the encoding process, a contructor provided by the
+caller is called to ensure that there is an appropriate provider-side object
+to start with.
+The constructor is set with OSSL_ENCODER_CTX_set_construct().
 
-The above distinction makes it possible for other routines to sense if
-they need to report an error or fall back on other methods to
-encode.
+B<OSSL_ENCODER_INSTANCE> is an opaque structure that contains data about the
+encoder that is going to be used, and that may be useful for the
+constructor.  There are some functions to extract data from this type,
+described in L</Constructor> below.
 
-=end comment
+=head2 Functions
 
-OSSL_ENCODER_CTX_get_encoder() gets the encoder method
-currently associated with the context I<ctx>.
+OSSL_ENCODER_CTX_new() creates a B<OSSL_ENCODER_CTX>.
 
 OSSL_ENCODER_settable_ctx_params() returns an L<OSSL_PARAM(3)>
 array of parameter descriptors.
@@ -56,22 +100,93 @@ implementation doesn't recognise should be ignored.
 
 OSSL_ENCODER_CTX_free() frees the given context I<ctx>.
 
+OSSL_ENCODER_CTX_add_encoder() populates the B<OSSL_ENCODER_CTX>
+I<ctx> with a encoder, to be used to encode an input object.
+
+OSSL_ENCODER_CTX_add_extra() finds encoders that further encodes output
+from already added encoders, and adds them as well.  This is used to build
+encoder chains.
+
+OSSL_ENCODER_CTX_set_output_type() sets the ending output type.  This must
+be specified, and determines if a complete encoder chain is available.
+
+OSSL_ENCODER_CTX_num_encoders() gets the number of encoders currently added
+to the context I<ctx>.
+
+OSSL_ENCODER_CTX_set_construct() sets the constructor I<construct>.
+
+OSSL_ENCODER_CTX_set_construct_data() sets the constructor data that is
+passed to the constructor every time it's called.
+
+OSSL_ENCODER_CTX_set_cleanup() sets the constructor data I<cleanup>
+function.  This is called by L<OSSL_ENCODER_CTX_free(3)>.
+
+=head2 Constructor
+
+A B<OSSL_ENCODER_CONSTRUCT> gets the following arguments:
+
+=over 4
+
+=item I<encoder_inst>
+
+The B<OSSL_ENCODER_INSTANCE> for the encoder from which the constructor gets
+its data.
+
+=item I<construct_data>
+
+The pointer that was set with OSSL_ENCODE_CTX_set_construct_data().
+
+=back
+
+The constructor is expected to return a valid (non-NULL) pointer to a
+provider-native object that can be used as first input of an encoding chain,
+or NULL to indicate that an error has occured.
+
+These utility functions may be used by a constructor:
+
+OSSL_ENCODER_INSTANCE_encoder() can be used to get the encoder method from a
+encoder instance I<encoder_inst>.
+
+OSSL_ENCODER_INSTANCE_encoder_ctx() can be used to get the encoder method's
+provider context from a encoder instance I<encoder_inst>.
+
+OSSL_ENCODER_INSTANCE_input_type() can be used to get the input type for
+encoder method from a encoder instance I<encoder_inst>.  This may be NULL.
+
+OSSL_ENCODER_INSTANCE_output_type() can be used to get the output type for
+encoder method from a encoder instance I<encoder_inst>.  This will never be
+NULL.
+
 =head1 RETURN VALUES
 
-OSSL_ENCODER_CTX_new() returns a pointer to a
-B<OSSL_ENCODER_CTX>, or NULL if the context structure couldn't be
-allocated.
+OSSL_ENCODER_CTX_new() returns a pointer to a B<OSSL_ENCODER_CTX>, or NULL
+if the context structure couldn't be allocated.
 
-OSSL_ENCODER_CTX_get_encoder() returns a pointer to the
-encoder method associated with I<ctx>.  NULL is a valid return
-value and signifies that there is no associated encoder method.
+OSSL_ENCODER_settable_ctx_params() returns an L<OSSL_PARAM(3)> array, or
+NULL if none is available.
 
-OSSL_ENCODER_settable_ctx_params() returns an L<OSSL_PARAM(3)>
-array, or NULL if none is available.
+OSSL_ENCODER_CTX_set_params() returns 1 if all recognised parameters were
+valid, or 0 if one of them was invalid or caused some other failure in the
+implementation.
+
+OSSL_DECODER_CTX_add_decoder(), OSSL_DECODER_CTX_add_extra(),
+OSSL_DECODER_CTX_set_construct(), OSSL_DECODER_CTX_set_construct_data() and
+OSSL_DECODER_CTX_set_cleanup() return 1 on success, or 0 on failure.
+
+OSSL_DECODER_CTX_num_decoders() returns the current number of decoders.  It
+returns 0 if I<ctx> is NULL.
+
+OSSL_DECODER_INSTANCE_decoder() returns an B<OSSL_DECODER> pointer on
+success, or NULL on failure.
+
+OSSL_DECODER_INSTANCE_decoder_ctx() returns a provider context pointer on
+success, or NULL on failure.
+
+OSSL_ENCODER_INSTANCE_input_type() returns a string with the name of the
+input type, if relevant.  NULL is a valid returned value.
 
-OSSL_ENCODER_CTX_set_params() returns 1 if all recognised
-parameters were valid, or 0 if one of them was invalid or caused some
-other failure in the implementation.
+OSSL_ENCODER_INSTANCE_output_type() returns a string with the name of the
+output type.
 
 =head1 SEE ALSO
 
diff --git a/doc/man3/OSSL_ENCODER_CTX_new_by_EVP_PKEY.pod b/doc/man3/OSSL_ENCODER_CTX_new_by_EVP_PKEY.pod
index 2aa103fd14..a97208cbe3 100644
--- a/doc/man3/OSSL_ENCODER_CTX_new_by_EVP_PKEY.pod
+++ b/doc/man3/OSSL_ENCODER_CTX_new_by_EVP_PKEY.pod
@@ -5,25 +5,19 @@
 OSSL_ENCODER_CTX_new_by_EVP_PKEY,
 OSSL_ENCODER_CTX_set_cipher,
 OSSL_ENCODER_CTX_set_passphrase,
+OSSL_ENCODER_CTX_set_pem_password_cb,
 OSSL_ENCODER_CTX_set_passphrase_cb,
-OSSL_ENCODER_CTX_set_passphrase_ui,
-OSSL_ENCODER_PUBKEY_TO_PEM_PQ,
-OSSL_ENCODER_PrivateKey_TO_PEM_PQ,
-OSSL_ENCODER_Parameters_TO_PEM_PQ,
-OSSL_ENCODER_PUBKEY_TO_DER_PQ,
-OSSL_ENCODER_PrivateKey_TO_DER_PQ,
-OSSL_ENCODER_Parameters_TO_DER_PQ,
-OSSL_ENCODER_PUBKEY_TO_TEXT_PQ,
-OSSL_ENCODER_PrivateKey_TO_TEXT_PQ,
-OSSL_ENCODER_Parameters_TO_TEXT_PQ
+OSSL_ENCODER_CTX_set_passphrase_ui
 - Encoder routines to encode EVP_PKEYs
 
 =head1 SYNOPSIS
 
  #include <openssl/encoder.h>
 
- OSSL_ENCODER_CTX *OSSL_ENCODER_CTX_new_by_EVP_PKEY(const EVP_PKEY *pkey,
-                                                    const char *propquery);
+ OSSL_ENCODER_CTX *
+ OSSL_ENCODER_CTX_new_by_EVP_PKEY(const EVP_PKEY *pkey,
+                                  const char *output_type, int selection,
+                                  OPENSSL_CTX *libctx, const char *propquery);
 
  int OSSL_ENCODER_CTX_set_cipher(OSSL_ENCODER_CTX *ctx,
                                  const char *cipher_name,
@@ -31,45 +25,39 @@ OSSL_ENCODER_Parameters_TO_TEXT_PQ
  int OSSL_ENCODER_CTX_set_passphrase(OSSL_ENCODER_CTX *ctx,
                                      const unsigned char *kstr,
                                      size_t klen);
- int OSSL_ENCODER_CTX_set_passphrase_cb(OSSL_ENCODER_CTX *ctx,
-                                        pem_password_cb *cb, void *cbarg);
+ int OSSL_ENCODER_CTX_set_pem_password_cb(OSSL_ENCODER_CTX *ctx,
+                                          pem_password_cb *cb, void *cbarg);
  int OSSL_ENCODER_CTX_set_passphrase_ui(OSSL_ENCODER_CTX *ctx,
                                         const UI_METHOD *ui_method,
                                         void *ui_data);
-
- #define OSSL_ENCODER_PUBKEY_TO_PEM_PQ "format=pem,type=public"
- #define OSSL_ENCODER_PrivateKey_TO_PEM_PQ "format=pem,type=private"
- #define OSSL_ENCODER_Parameters_TO_PEM_PQ "format=pem,type=parameters"
-
- #define OSSL_ENCODER_PUBKEY_TO_DER_PQ "format=der,type=public"
- #define OSSL_ENCODER_PrivateKey_TO_DER_PQ "format=der,type=private"
- #define OSSL_ENCODER_Parameters_TO_DER_PQ "format=der,type=parameters"
-
- #define OSSL_ENCODER_PUBKEY_TO_TEXT_PQ "format=text,type=public"
- #define OSSL_ENCODER_PrivateKey_TO_TEXT_PQ "format=text,type=private"
- #define OSSL_ENCODER_Parameters_TO_TEXT_PQ "format=text,type=parameters"
+ int OSSL_ENCODER_CTX_set_passphrase_cb(OSSL_ENCODER_CTX *ctx,
+                                        OSSL_PASSPHRASE_CALLBACK *cb,
+                                        void *cbarg);
 
 =head1 DESCRIPTION
 
-OSSL_ENCODER_CTX_new_by_EVP_PKEY() creates a B<OSSL_ENCODER_CTX>
-with a suitable attached output routine for B<EVP_PKEY>s.  It will
-search for a encoder implementation that matches the algorithm of
-the B<EVP_PKEY> and the property query given with I<propquery>.  It
-will prefer to find a encoder from the same provider as the key
-data of the B<EVP_PKEY> itself, but failing that, it will choose the
-first encoder that supplies a generic encoding function.
-
-If no suitable encoder was found, OSSL_ENCODER_CTX_new_by_EVP_PKEY()
-still creates a B<OSSL_ENCODER_CTX>, but with no associated
-encoder (L<OSSL_ENCODER_CTX_get_encoder(3)> returns NULL).
-This helps the caller distinguish between an error when creating
-the B<OSSL_ENCODER_CTX>, and the lack the encoder support and
+OSSL_ENCODER_CTX_new_by_EVP_PKEY() is a utility function that creates a
+B<OSSL_ENCODER_CTX>, finds all applicable encoder implementations and sets
+them up, so almost all the caller has to do next is call functions like
+L<OSSL_ENCODER_to_bio(3)>.
+
+Internally, OSSL_ENCODER_CTX_new_by_EVP_PKEY() uses the names from the
+L<EVP_KEYMGMT(3)> implementation associated with I<pkey> to build a list of
+applicable encoder implementations that are used to process the I<pkey> into
+the encoding named by I<output_type>.  All these implementations are
+implicitly fetched using I<libctx> and I<propquery>.
+
+If no suitable encoder implementation is found,
+OSSL_ENCODER_CTX_new_by_EVP_PKEY() still creates a B<OSSL_ENCODER_CTX>, but
+with no associated encoder (L<OSSL_ENCODER_CTX_get_num_encoders(3)> returns
+zero).  This helps the caller to distinguish between an error when creating
+the B<OSSL_ENCODER_CTX> and missing encoder implementation, and allows it to
 act accordingly.
 
 OSSL_ENCODER_CTX_set_cipher() tells the implementation what cipher
 should be used to encrypt encoded keys.  The cipher is given by
 name I<cipher_name>.  The interpretation of that I<cipher_name> is
-implementation dependent.  The implementation may implement the digest
+implementation dependent.  The implementation may implement the cipher
 directly itself or by other implementations, or it may choose to fetch
 it.  If the implementation supports fetching the cipher, then it may
 use I<propquery> as properties to be queried for when fetching.
@@ -81,48 +69,30 @@ pass phrase to use when encrypting the encoded private key.
 Alternatively, a pass phrase callback may be specified with the
 following functions.
 
-OSSL_ENCODER_CTX_set_passphrase_cb() and
-OSSL_ENCODER_CTX_set_passphrase_ui() sets up a callback method that
-the implementation can use to prompt for a pass phrase.
-
-=for comment Note that the callback method is called indirectly,
+OSSL_ENCODER_CTX_set_pem_password_cb(), OSSL_ENCODER_CTX_set_passphrase_ui()
+and OSSL_ENCODER_CTX_set_passphrase_cb() sets up a callback method that the
+implementation can use to prompt for a pass phrase, giving the caller the
+choice of prefered pass phrase callback form.  These are called indirectly,
 through an internal B<OSSL_PASSPHRASE_CALLBACK> function.
 
-The macros B<OSSL_ENCODER_PUBKEY_TO_PEM_PQ>,
-B<OSSL_ENCODER_PrivateKey_TO_PEM_PQ>,
-B<OSSL_ENCODER_Parameters_TO_PEM_PQ>,
-B<OSSL_ENCODER_PUBKEY_TO_DER_PQ>,
-B<OSSL_ENCODER_PrivateKey_TO_DER_PQ>,
-B<OSSL_ENCODER_Parameters_TO_DER_PQ>,
-B<OSSL_ENCODER_PUBKEY_TO_TEXT_PQ>,
-B<OSSL_ENCODER_PrivateKey_TO_TEXT_PQ>,
-B<OSSL_ENCODER_Parameters_TO_TEXT_PQ> are convenience macros with
-property queries to encode the B<EVP_PKEY> as a public key, private
-key or parameters to B<PEM>, to B<DER>, or to text.
-
 =head1 RETURN VALUES
 
 OSSL_ENCODER_CTX_new_by_EVP_PKEY() returns a pointer to a
 B<OSSL_ENCODER_CTX>, or NULL if it couldn't be created.
 
-OSSL_ENCODER_CTX_set_cipher(),
-OSSL_ENCODER_CTX_set_passphrase(),
-OSSL_ENCODER_CTX_set_passphrase_cb(), and
-OSSL_ENCODER_CTX_set_passphrase_ui() all return 1 on success, or 0
-on failure.
+OSSL_ENCODER_CTX_set_cipher(), OSSL_ENCODER_CTX_set_passphrase(),
+OSSL_ENCODER_CTX_set_pem_password_cb(), OSSL_ENCODER_CTX_set_passphrase_ui()
+and OSSL_ENCODER_CTX_set_passphrase_cb() all return 1 on success, or 0 on
+failure.
 
 =head1 NOTES
 
-Parts of the function and macro names are made to match already
-existing OpenSSL names.
-
-B<EVP_PKEY> in OSSL_ENCODER_CTX_new_by_EVP_PKEY() matches the type
-name, thus making for the naming pattern
-B<OSSL_ENCODER_CTX_new_by_I<TYPE>>() when new types are handled.
+Parts of the function names are made to match already existing OpenSSL
+names.
 
-B<PUBKEY>, B<PrivateKey> and B<Parameters> in the macro names match
-the B<I<TYPE>> part of B<PEM_write_bio_I<TYPE>> functions as well
-as B<i2d_I<TYPE>_bio> functions.
+B<EVP_PKEY> in OSSL_ENCODER_CTX_new_by_EVP_PKEY() matches the type name,
+thus making for the naming pattern B<OSSL_ENCODER_CTX_new_by_I<TYPE>>() when
+new types are handled.
 
 =head1 SEE ALSO
 
diff --git a/doc/man3/OSSL_ENCODER_to_bio.pod b/doc/man3/OSSL_ENCODER_to_bio.pod
index ee9998b2eb..6f75f592e4 100644
--- a/doc/man3/OSSL_ENCODER_to_bio.pod
+++ b/doc/man3/OSSL_ENCODER_to_bio.pod
@@ -4,7 +4,7 @@
 
 OSSL_ENCODER_to_bio,
 OSSL_ENCODER_to_fp
-- Encoder file output routines
+- Routines to perform an encoding
 
 =head1 SYNOPSIS
 
@@ -24,10 +24,10 @@ is undefined.
 
 =head1 DESCRIPTION
 
-OSSL_ENCODER_to_bio() runs the encoding process for the
-context I<ctx>, with the output going to the B<BIO> I<out>.  The
-application is required to set up the B<BIO> properly, for example to
-have it in text or binary mode if that's appropriate.
+OSSL_ENCODER_to_bio() runs the encoding process for the context I<ctx>, with
+the output going to the B<BIO> I<out>.  The application is required to set
+up the B<BIO> properly, for example to have it in text or binary mode if
+that's appropriate.
 
 =for comment Know your encoder!
 
@@ -36,8 +36,16 @@ except that the output is going to the B<FILE> I<fp>.
 
 =head1 RETURN VALUES
 
-OSSL_ENCODER_to_bio() and OSSL_ENCODER_to_fp() return 1 on
-success, or 0 on failure.
+OSSL_ENCODER_to_bio() and OSSL_ENCODER_to_fp() return 1 on success, or 0 on
+failure.
+
+=begin comment TODO(3.0) Add examples!
+
+=head1 EXAMPLES
+
+Text, because pod2xxx doesn't like empty sections
+
+=end comment
 
 =head1 SEE ALSO
 
@@ -49,7 +57,7 @@ The functions described here were added in OpenSSL 3.0.
 
 =head1 COPYRIGHT
 
-Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+Copyright 2019-2020 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
diff --git a/doc/man7/provider-encoder.pod b/doc/man7/provider-encoder.pod
index 99787e7040..8048458b94 100644
--- a/doc/man7/provider-encoder.pod
+++ b/doc/man7/provider-encoder.pod
@@ -2,7 +2,7 @@
 
 =head1 NAME
 
-provider-encoder - The ENCODER library E<lt>-E<gt> provider functions
+provider-encoder - The OSSL_ENCODER library E<lt>-E<gt> provider functions
 
 =head1 SYNOPSIS
 
@@ -14,6 +14,10 @@ provider-encoder - The ENCODER library E<lt>-E<gt> provider functions
   * pointers in OSSL_DISPATCH arrays.
   */
 
+ /* Encoder parameter accessor and descriptor */
+ const OSSL_PARAM *OSSL_FUNC_encoder_gettable_params(void *provctx);
+ int encoder_get_params(OSSL_PARAM params[]);
+
  /* Functions to construct / destruct / manipulate the encoder context */
  void *OSSL_FUNC_encoder_newctx(void *provctx);
  void OSSL_FUNC_encoder_freectx(void *ctx);
@@ -21,29 +25,51 @@ provider-encoder - The ENCODER library E<lt>-E<gt> provider functions
  const OSSL_PARAM *OSSL_FUNC_encoder_settable_ctx_params(void *provctx)
 
  /* Functions to encode object data */
- int OSSL_FUNC_encoder_encode_data(void *ctx, const OSSL_PARAM *data,
-                                         OSSL_CORE_BIO *out,
-                                         OSSL_PASSPHRASE_CALLBACK *cb,
-                                         void *cbarg);
- int OSSL_FUNC_encoder_encode_object(void *ctx, void *obj, OSSL_CORE_BIO *out,
-                                           OSSL_PASSPHRASE_CALLBACK *cb,
-                                           void *cbarg);
+ int OSSL_FUNC_encoder_encode(void *ctx, OSSL_CORE_BIO *out,
+                              const void *obj_raw,
+                              const OSSL_PARAM obj_abstract[],
+                              int selection,
+                              OSSL_PASSPHRASE_CALLBACK *cb,
+                              void *cbarg);
+
+ /* Functions to import and free a temporary object to be encoded */
+ void *encoder_import_object(void *ctx, int selection,
+                             const OSSL_PARAM params[]);
+ void encoder_free_object(void *obj);
+
 
 =head1 DESCRIPTION
 
 I<We use the wide term "encode" in this manual.  This includes but is
 not limited to serialization.>
 
-The ENCODER is a generic method to encode any set of object data
-in L<OSSL_PARAM(3)> array form, or any provider side object into
-encoded form, and write it to the given OSSL_CORE_BIO.  If the caller wants
-to get the encoded stream to memory, it should provide a
-L<BIO_s_membuf(3)>.
+The ENCODER operation is a generic method to encode a provider-native
+object (I<obj_raw>) or an object abstraction (I<object_abstract>, see
+L<provider-object(7)>) into an encoded form, and write the result to
+the given OSSL_CORE_BIO.  If the caller wants to get the encoded
+stream to memory, it should provide a L<BIO_s_membuf(3)>.
 
-The encoder doesn't need to know more about the B<OSSL_CORE_BIO> pointer than
-being able to pass it to the appropriate BIO upcalls (see
+The encoder doesn't need to know more about the B<OSSL_CORE_BIO>
+pointer than being able to pass it to the appropriate BIO upcalls (see
 L<provider-base(7)/Core functions>).
 
+The ENCODER implementation may be part of a chain, where data is
+passed from one to the next.  For example, there may be an
+implementation to encode an object to DER (that object is assumed to
+be provider-native and thereby passed via I<obj_raw>), and another one
+that encodes DER to PEM (that one would receive the DER encoding via
+I<obj_abstract>).
+
+=begin comment
+
+Having the DER encoding passed via I<obj_abstract> may seem
+complicated.  However, there may be associated meta-data, such as the
+original data type, that need to be passed alongside it, and since
+L<provider-object(7)> already defines a way to pass such data,
+inventing another way to do it makes things even more complicated.
+
+=end comment
+
 The encoding using the L<OSSL_PARAM(3)> array form allows a
 encoder to be used for data that's been exported from another
 provider, and thereby allow them to exist independently of each
@@ -66,90 +92,91 @@ B<OSSL_FUNC_{name}>.
 For example, the "function" OSSL_FUNC_encoder_encode_data() has these:
 
  typedef int
-     (OSSL_FUNC_encoder_encode_data_fn)(void *provctx,
-                                            const OSSL_PARAM params[],
-                                            OSSL_CORE_BIO *out);
- static ossl_inline OSSL_FUNC_encoder_encode_data_fn
+     (OSSL_FUNC_encoder_encode_fn)(void *ctx, OSSL_CORE_BIO *out,
+                                   const void *obj_raw,
+                                   const OSSL_PARAM obj_abstract[],
+                                   int selection,
+                                   OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg);
+ static ossl_inline OSSL_FUNC_encoder_encode_fn
      OSSL_FUNC_encoder_encode_data(const OSSL_DISPATCH *opf);
 
 B<OSSL_DISPATCH> arrays are indexed by numbers that are provided as
 macros in L<openssl-core_dispatch.h(7)>, as follows:
 
+ OSSL_FUNC_encoder_get_params          OSSL_FUNC_ENCODER_GET_PARAMS
+ OSSL_FUNC_encoder_gettable_params     OSSL_FUNC_ENCODER_GETTABLE_PARAMS
+
  OSSL_FUNC_encoder_newctx              OSSL_FUNC_ENCODER_NEWCTX
  OSSL_FUNC_encoder_freectx             OSSL_FUNC_ENCODER_FREECTX
  OSSL_FUNC_encoder_set_ctx_params      OSSL_FUNC_ENCODER_SET_CTX_PARAMS
  OSSL_FUNC_encoder_settable_ctx_params OSSL_FUNC_ENCODER_SETTABLE_CTX_PARAMS
 
- OSSL_FUNC_encoder_encode_data      OSSL_FUNC_ENCODER_ENCODE_DATA
- OSSL_FUNC_encoder_encode_object    OSSL_FUNC_ENCODER_ENCODE_OBJECT
+ OSSL_FUNC_encoder_encode              OSSL_FUNC_ENCODER_ENCODE_DATA
+
+ OSSL_FUNC_encoder_import_object       OSSL_FUNC_ENCODER_IMPORT_OBJECT
+ OSSL_FUNC_encoder_free_object         OSSL_FUNC_ENCODER_FREE_OBJECT
 
 =head2 Names and properties
 
-The name of an implementation should match the type of object it
-handles.  For example, an implementation that encodes an RSA key
-should be named accordingly.
+The name of an implementation should match the type of object it handles.
+For example, an implementation that encodes an RSA key should be named "RSA".
+Likewise, an implementation that further encodes DER should be named "DER".
 
-To be able to specify exactly what encoding format and what type
-of data a encoder implementation is expected to handle, two
-additional properties may be given:
+Properties can be use to further specify details about an implementation:
 
 =over 4
 
-=item format
+=item output
 
-This property is used to specify what kind of output format the
-implementation produces.  Currently known formats are:
+This property is used to specify what type of output implementation
+produces.  Currently known output types are:
 
 =over 4
 
 =item text
 
-An implementation with that format property value outputs human
-readable text, making that implementation suitable for C<-text> output
-in diverse L<openssl(1)> commands.
+An implementation with that output type outputs human readable text, making
+that implementation suitable for C<-text> output in diverse L<openssl(1)>
+commands.
 
 =item pem
 
-An implementation with that format property value outputs PEM
-formatted data.
+An implementation with that output type outputs PEM formatted data.
 
 =item der
 
-An implementation with that format property value outputs DER
-formatted data.
+An implementation with that output type outputs DER formatted data.
 
 =back
 
-=item type
+=item structure
 
-With objects that have multiple purposes, this can be used to specify
-the purpose type.  The currently known use cases are asymmetric keys
-and key parameters, where the type can be one of:
+This property is used to specify the structure that is used for the encoded
+object.  An example could be C<pkcs8>, to specify explicitly that an object
+(presumably an asymmetric key pair, in this case) will be wrapped in a
+PKCS#8 structure as part of the encoding.
 
-=over 4
-
-=item private
+=back
 
-An implementation with that format property value outputs a private
-key.
+The possible values of both these properties is open ended.  A provider may
+very well specify output types and structures that libcrypto doesn't know
+anything about.
 
-=item public
+=head2 Subset selections
 
-An implementation with that format property value outputs a public
-key.
+Sometimes, an object has more than one subset of data that is interesting to
+treat separately or together.  It's possible to specify what subsets are to
+be encoded, with a set of bits I<selection> that are passed in an B<int>.
 
-=item parameters
+This set of bits depend entirely on what kind of provider-side object is
+passed.  For example, those bits are assumed to be the same as those used
+with L<provider-keymgmt(7)> (see L<provider-keymgmt(7)/Key Objects>) when
+the object is an asymmetric key.
 
-An implementation with that format property value outputs key
-parameters.
-
-=back
-
-=back
-
-The possible values of both these properties is open ended.  A
-provider may very well specify other formats that libcrypto doesn't
-know anything about.
+ENCODER implementations are free to regard the I<selection> as a set of
+hints, but must do so with care.  In the end, the output must make sense,
+and if there's a corresponding decoder, the resulting decoded object must
+match the original object that was encoded.
 
 =head2 Context functions
 
@@ -159,40 +186,76 @@ the functions.
 OSSL_FUNC_encoder_freectx() frees the given I<ctx>, if it was created by
 OSSL_FUNC_encoder_newctx().
 
-OSSL_FUNC_encoder_set_ctx_params() sets context data according to
-parameters from I<params> that it recognises.  Unrecognised parameters
-should be ignored.
+OSSL_FUNC_encoder_set_ctx_params() sets context data according to parameters
+from I<params> that it recognises.  Unrecognised parameters should be
+ignored.
 
 OSSL_FUNC_encoder_settable_ctx_params() returns a constant B<OSSL_PARAM>
 array describing the parameters that OSSL_FUNC_encoder_set_ctx_params()
 can handle.
 
-See L<OSSL_PARAM(3)> for further details on the parameters structure used
-by OSSL_FUNC_encoder_set_ctx_params() and OSSL_FUNC_encoder_settable_ctx_params().
+See L<OSSL_PARAM(3)> for further details on the parameters structure used by
+OSSL_FUNC_encoder_set_ctx_params() and OSSL_FUNC_encoder_settable_ctx_params().
+
+=head2 Import functions
+
+A provider-native object may be associated with a foreign provider, and may
+therefore be unsuitable for direct use with a given ENCODER implementation.
+Provided that the foreign provider's implementation to handle the object has
+a function to export that object in L<OSSL_PARAM(3)> array form, the ENCODER
+implementation should be able to import that array and create a suitable
+object to be passed to OSSL_FUNC_encoder_encode()'s I<obj_raw>.
+
+OSSL_FUNC_encoder_import_object() should import the subset of I<params>
+given with I<selection> to create a provider-native object that can be
+passed as I<obj_raw> to OSSL_FUNC_encoder_encode().
+
+OSSL_FUNC_encoder_free_object() should free the object that was created with
+OSSL_FUNC_encoder_import_object().
 
 =head2 Encoding functions
 
 =for comment There will be a "Decoding functions" title as well
 
-OSSL_FUNC_encoder_encode_data() should take an array of B<OSSL_PARAM>,
-I<data>, and if it contains the data necessary for the object type
-that the implementation handles, it should output the object in
-encoded form to the B<OSSL_CORE_BIO>.
+OSSL_FUNC_encoder_encode() should take an provider-native object (in
+I<obj_raw>) or an object abstraction (in I<obj_abstract>), and should output
+the object in encoded form to the B<OSSL_CORE_BIO>.  The I<selection> bits,
+if relevant, should determine in greater detail what will be output.
+The encoding functions also take an B<OSSL_PASSPHRASE_CALLBACK> function
+pointer along with a pointer to application data I<cbarg>, which should be
+used when a pass phrase prompt is needed.
 
-OSSL_FUNC_encoder_encode_object() should take a pointer to an object
-that it knows intimately, and output that object in encoded form to
-the B<OSSL_CORE_BIO>.  The caller I<must> ensure that this function is called
-with a pointer that the provider of this function is familiar with.
-It is not suitable to use with object pointers coming from other
-providers.
+=head2 Encoder parameters
 
-Both encoding functions also take an B<OSSL_PASSPHRASE_CALLBACK>
-function pointer along with a pointer to application data I<cbarg>,
-which should be used when a pass phrase prompt is needed.
+The ENCODER implementation itself has parameters that can be used to
+determine how it fits in a chain of encoders:
 
-=head2 Encoder parameters
+=over 4
+
+=item "input-type" (B<OSSL_ENCODER_PARAM_INPUT_TYPE>) <UTF8 string>
+
+This is used to specify a distinct type name for the object passed as
+I<obj_raw> to OSSL_FUNC_encoder_encode.
+
+This parameter is an optional parameter, to be used if the name of the
+implementation can be ambiguous because of aliases, and something more
+deterministic is needed.
+
+=item "output-type" (B<OSSL_ENCODER_PARAM_OUTPUT_TYPE>) <UTF8 string>
 
-Parameters currently recognised by built-in encoders are as
+This is used to specify the output type for an ENCODER implementation.
+
+This parameter is I<mandatory>.
+
+=for comment If we had functionality to get the value of a specific property
+in a set of properties, it would be possible to determine the output type
+from the C<output> property.
+
+=back
+
+=head2 Encoder operation parameters
+
+Operation parameters currently recognised by built-in encoders are as
 follows:
 
 =over 4
@@ -220,11 +283,6 @@ However, it is recommended that implementations that do not handle
 property strings return an error on receiving this parameter unless
 its value NULL or the empty string.
 
-=item "passphrase" (B<OSSL_ENCODER_PARAM_PASS>) <octet string>
-
-A pass phrase provided by the application.  When this is given, the
-built-in encoders will not attempt to use the passphrase callback.
-
 =back
 
 Parameters currently recognised by the built-in pass phrase callback:
@@ -250,8 +308,7 @@ parameters was invalid or caused an error, for which 0 is returned.
 OSSL_FUNC_encoder_settable_ctx_params() returns a pointer to an array of
 constant B<OSSL_PARAM> elements.
 
-OSSL_FUNC_encoder_encode_data() and OSSL_FUNC_encoder_encode_object()
-return 1 on success, or 0 on failure.
+OSSL_FUNC_encoder_encode() return 1 on success, or 0 on failure.
 
 =head1 SEE ALSO
 
diff --git a/include/crypto/encoder.h b/include/crypto/encoder.h
index f75a031c32..2f036456a2 100644
--- a/include/crypto/encoder.h
+++ b/include/crypto/encoder.h
@@ -11,5 +11,3 @@
 
 OSSL_ENCODER *ossl_encoder_fetch_by_number(OPENSSL_CTX *libctx, int id,
                                            const char *properties);
-OSSL_DECODER *ossl_decoder_fetch_by_number(OPENSSL_CTX *libctx, int id,
-                                           const char *properties);
diff --git a/include/openssl/core_dispatch.h b/include/openssl/core_dispatch.h
index 5c6a4f4848..63105d6629 100644
--- a/include/openssl/core_dispatch.h
+++ b/include/openssl/core_dispatch.h
@@ -752,24 +752,37 @@ OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, kem_settable_ctx_params, (void *provctx)
 /* Encoders and decoders */
 # define OSSL_FUNC_ENCODER_NEWCTX                      1
 # define OSSL_FUNC_ENCODER_FREECTX                     2
-# define OSSL_FUNC_ENCODER_SET_CTX_PARAMS              3
-# define OSSL_FUNC_ENCODER_SETTABLE_CTX_PARAMS         4
-# define OSSL_FUNC_ENCODER_ENCODE_DATA                10
-# define OSSL_FUNC_ENCODER_ENCODE_OBJECT              11
+# define OSSL_FUNC_ENCODER_GET_PARAMS                  3
+# define OSSL_FUNC_ENCODER_GETTABLE_PARAMS             4
+# define OSSL_FUNC_ENCODER_SET_CTX_PARAMS              5
+# define OSSL_FUNC_ENCODER_SETTABLE_CTX_PARAMS         6
+# define OSSL_FUNC_ENCODER_ENCODE                     10
+# define OSSL_FUNC_ENCODER_IMPORT_OBJECT              20
+# define OSSL_FUNC_ENCODER_FREE_OBJECT                21
 OSSL_CORE_MAKE_FUNC(void *, encoder_newctx, (void *provctx))
 OSSL_CORE_MAKE_FUNC(void, encoder_freectx, (void *ctx))
+OSSL_CORE_MAKE_FUNC(int, encoder_get_params, (OSSL_PARAM params[]))
+OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, encoder_gettable_params,
+                    (void *provctx))
 OSSL_CORE_MAKE_FUNC(int, encoder_set_ctx_params,
                     (void *ctx, const OSSL_PARAM params[]))
 OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, encoder_settable_ctx_params,
                     (void *provctx))
 
-OSSL_CORE_MAKE_FUNC(int, encoder_encode_data,
-                    (void *ctx, const OSSL_PARAM[], OSSL_CORE_BIO *out,
-                     OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg))
-OSSL_CORE_MAKE_FUNC(int, encoder_encode_object,
-                    (void *ctx, const void *obj, OSSL_CORE_BIO *out,
+/*
+ * TODO(3.0) investigate if this should be two functions, one that takes a
+ * raw object and one that takes an object abstraction.
+ */
+OSSL_CORE_MAKE_FUNC(int, encoder_encode,
+                    (void *ctx, OSSL_CORE_BIO *out,
+                     const void *obj_raw, const OSSL_PARAM obj_abstract[],
+                     int selection,
                      OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg))
 
+OSSL_CORE_MAKE_FUNC(void *, encoder_import_object,
+                    (void *ctx, int selection, const OSSL_PARAM params[]))
+OSSL_CORE_MAKE_FUNC(void, encoder_free_object, (void *obj))
+
 # define OSSL_FUNC_DECODER_NEWCTX                      1
 # define OSSL_FUNC_DECODER_FREECTX                     2
 # define OSSL_FUNC_DECODER_GET_PARAMS                  3
diff --git a/include/openssl/core_names.h b/include/openssl/core_names.h
index 0fc2868d5b..d17ab49700 100644
--- a/include/openssl/core_names.h
+++ b/include/openssl/core_names.h
@@ -445,14 +445,12 @@ extern "C" {
 /*
  * Encoder / decoder parameters
  */
-/* The passphrase may be passed as a utf8 string or an octet string */
 #define OSSL_ENCODER_PARAM_CIPHER       OSSL_ALG_PARAM_CIPHER
 #define OSSL_ENCODER_PARAM_PROPERTIES   OSSL_ALG_PARAM_PROPERTIES
-#define OSSL_ENCODER_PARAM_PASS         "passphrase"
+#define OSSL_ENCODER_PARAM_INPUT_TYPE   "input-type"
+#define OSSL_ENCODER_PARAM_OUTPUT_TYPE  "output-type"
 
-#define OSSL_DECODER_PARAM_CIPHER       OSSL_ALG_PARAM_CIPHER
 #define OSSL_DECODER_PARAM_PROPERTIES   OSSL_ALG_PARAM_PROPERTIES
-#define OSSL_DECODER_PARAM_PASS         "passphrase"
 #define OSSL_DECODER_PARAM_INPUT_TYPE   "input-type"
 
 /* Passphrase callback parameters */
diff --git a/include/openssl/decoder.h b/include/openssl/decoder.h
index 91dfca4a09..3da4577437 100644
--- a/include/openssl/decoder.h
+++ b/include/openssl/decoder.h
@@ -53,11 +53,9 @@ void OSSL_DECODER_CTX_free(OSSL_DECODER_CTX *ctx);
 
 /* Utilities that help set specific parameters */
 int OSSL_DECODER_CTX_set_passphrase(OSSL_DECODER_CTX *ctx,
-                                    const unsigned char *kstr,
-                                    size_t klen);
+                                    const unsigned char *kstr, size_t klen);
 int OSSL_DECODER_CTX_set_pem_password_cb(OSSL_DECODER_CTX *ctx,
-                                         pem_password_cb *cb,
-                                         void *cbarg);
+                                         pem_password_cb *cb, void *cbarg);
 int OSSL_DECODER_CTX_set_passphrase_cb(OSSL_DECODER_CTX *ctx,
                                        OSSL_PASSPHRASE_CALLBACK *cb,
                                        void *cbarg);
@@ -75,17 +73,20 @@ int OSSL_DECODER_CTX_set_input_type(OSSL_DECODER_CTX *ctx,
 int OSSL_DECODER_CTX_add_decoder(OSSL_DECODER_CTX *ctx, OSSL_DECODER *decoder);
 int OSSL_DECODER_CTX_add_extra(OSSL_DECODER_CTX *ctx,
                                OPENSSL_CTX *libctx, const char *propq);
-int OSSL_DECODER_CTX_num_decoders(OSSL_DECODER_CTX *ctx);
+int OSSL_DECODER_CTX_get_num_decoders(OSSL_DECODER_CTX *ctx);
 
 typedef struct ossl_decoder_instance_st OSSL_DECODER_INSTANCE;
 OSSL_DECODER *
-OSSL_DECODER_INSTANCE_decoder(OSSL_DECODER_INSTANCE *decoder_inst);
-void *OSSL_DECODER_INSTANCE_decoder_ctx(OSSL_DECODER_INSTANCE *decoder_inst);
-
-typedef int (OSSL_DECODER_CONSTRUCT)(OSSL_DECODER_INSTANCE *decoder_inst,
-                                     const OSSL_PARAM *params,
-                                     void *construct_data);
-typedef void (OSSL_DECODER_CLEANUP)(void *construct_data);
+OSSL_DECODER_INSTANCE_get_decoder(OSSL_DECODER_INSTANCE *decoder_inst);
+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);
+
+typedef int OSSL_DECODER_CONSTRUCT(OSSL_DECODER_INSTANCE *decoder_inst,
+                                   const OSSL_PARAM *params,
+                                   void *construct_data);
+typedef void OSSL_DECODER_CLEANUP(void *construct_data);
 
 int OSSL_DECODER_CTX_set_construct(OSSL_DECODER_CTX *ctx,
                                    OSSL_DECODER_CONSTRUCT *construct);
diff --git a/include/openssl/encoder.h b/include/openssl/encoder.h
index 10b2bc9188..6698769e24 100644
--- a/include/openssl/encoder.h
+++ b/include/openssl/encoder.h
@@ -42,26 +42,59 @@ void OSSL_ENCODER_do_all_provided(OPENSSL_CTX *libctx,
 void OSSL_ENCODER_names_do_all(const OSSL_ENCODER *encoder,
                                void (*fn)(const char *name, void *data),
                                void *data);
+const OSSL_PARAM *OSSL_ENCODER_gettable_params(OSSL_ENCODER *encoder);
+int OSSL_ENCODER_get_params(OSSL_ENCODER *encoder, OSSL_PARAM params[]);
 
 const OSSL_PARAM *OSSL_ENCODER_settable_ctx_params(OSSL_ENCODER *encoder);
-OSSL_ENCODER_CTX *OSSL_ENCODER_CTX_new(OSSL_ENCODER *encoder);
-const OSSL_ENCODER *OSSL_ENCODER_CTX_get_encoder(OSSL_ENCODER_CTX *ctx);
+OSSL_ENCODER_CTX *OSSL_ENCODER_CTX_new(void);
 int OSSL_ENCODER_CTX_set_params(OSSL_ENCODER_CTX *ctx,
                                 const OSSL_PARAM params[]);
 void OSSL_ENCODER_CTX_free(OSSL_ENCODER_CTX *ctx);
 
 /* Utilities that help set specific parameters */
-int OSSL_ENCODER_CTX_set_cipher(OSSL_ENCODER_CTX *ctx,
-                                const char *cipher_name,
-                                const char *propquery);
 int OSSL_ENCODER_CTX_set_passphrase(OSSL_ENCODER_CTX *ctx,
-                                    const unsigned char *kstr,
-                                    size_t klen);
+                                    const unsigned char *kstr, size_t klen);
+int OSSL_ENCODER_CTX_set_pem_password_cb(OSSL_ENCODER_CTX *ctx,
+                                         pem_password_cb *cb, void *cbarg);
 int OSSL_ENCODER_CTX_set_passphrase_cb(OSSL_ENCODER_CTX *ctx,
-                                       pem_password_cb *cb, void *cbarg);
+                                       OSSL_PASSPHRASE_CALLBACK *cb,
+                                       void *cbarg);
 int OSSL_ENCODER_CTX_set_passphrase_ui(OSSL_ENCODER_CTX *ctx,
                                        const UI_METHOD *ui_method,
                                        void *ui_data);
+int OSSL_ENCODER_CTX_set_cipher(OSSL_ENCODER_CTX *ctx,
+                                const char *cipher_name,
+                                const char *propquery);
+int OSSL_ENCODER_CTX_set_output_type(OSSL_ENCODER_CTX *ctx,
+                                     const char *output_type);
+int OSSL_ENCODER_CTX_set_selection(OSSL_ENCODER_CTX *ctx, int selection);
+
+/* Utilities to add encoders */
+int OSSL_ENCODER_CTX_add_encoder(OSSL_ENCODER_CTX *ctx, OSSL_ENCODER *encoder);
+int OSSL_ENCODER_CTX_add_extra(OSSL_ENCODER_CTX *ctx,
+                               OPENSSL_CTX *libctx, const char *propq);
+int OSSL_ENCODER_CTX_get_num_encoders(OSSL_ENCODER_CTX *ctx);
+
+typedef struct ossl_encoder_instance_st OSSL_ENCODER_INSTANCE;
+OSSL_ENCODER *
+OSSL_ENCODER_INSTANCE_get_encoder(OSSL_ENCODER_INSTANCE *encoder_inst);
+void *
+OSSL_ENCODER_INSTANCE_get_encoder_ctx(OSSL_ENCODER_INSTANCE *encoder_inst);
+const char *
+OSSL_ENCODER_INSTANCE_get_input_type(OSSL_ENCODER_INSTANCE *encoder_inst);
+const char *
+OSSL_ENCODER_INSTANCE_get_output_type(OSSL_ENCODER_INSTANCE *encoder_inst);
+
+typedef const void *OSSL_ENCODER_CONSTRUCT(OSSL_ENCODER_INSTANCE *encoder_inst,
+                                           void *construct_data);
+typedef void OSSL_ENCODER_CLEANUP(void *construct_data);
+
+int OSSL_ENCODER_CTX_set_construct(OSSL_ENCODER_CTX *ctx,
+                                   OSSL_ENCODER_CONSTRUCT *construct);
+int OSSL_ENCODER_CTX_set_construct_data(OSSL_ENCODER_CTX *ctx,
+                                        void *construct_data);
+int OSSL_ENCODER_CTX_set_cleanup(OSSL_ENCODER_CTX *ctx,
+                                 OSSL_ENCODER_CLEANUP *cleanup);
 
 /* Utilities to output the object to encode */
 int OSSL_ENCODER_to_bio(OSSL_ENCODER_CTX *ctx, BIO *out);
@@ -75,25 +108,11 @@ int OSSL_ENCODER_to_fp(OSSL_ENCODER_CTX *ctx, FILE *fp);
  * This is more useful than calling OSSL_ENCODER_CTX_new().
  */
 OSSL_ENCODER_CTX *OSSL_ENCODER_CTX_new_by_EVP_PKEY(const EVP_PKEY *pkey,
+                                                   const char *output_type,
+                                                   int selection,
+                                                   OPENSSL_CTX *libctx,
                                                    const char *propquery);
 
-/*
- * These macros define the last argument to pass to
- * OSSL_ENCODER_CTX_new_by_TYPE().
- */
-# define OSSL_ENCODER_PUBKEY_TO_PEM_PQ "format=pem,type=public"
-# define OSSL_ENCODER_PrivateKey_TO_PEM_PQ "format=pem,type=private"
-# define OSSL_ENCODER_Parameters_TO_PEM_PQ "format=pem,type=parameters"
-
-# define OSSL_ENCODER_PUBKEY_TO_DER_PQ "format=der,type=public"
-# define OSSL_ENCODER_PrivateKey_TO_DER_PQ "format=der,type=private"
-# define OSSL_ENCODER_Parameters_TO_DER_PQ "format=der,type=parameters"
-
-/* Corresponding macros for text output */
-# define OSSL_ENCODER_PUBKEY_TO_TEXT_PQ "format=text,type=public"
-# define OSSL_ENCODER_PrivateKey_TO_TEXT_PQ "format=text,type=private"
-# define OSSL_ENCODER_Parameters_TO_TEXT_PQ "format=text,type=parameters"
-
 # ifdef __cplusplus
 }
 # endif
diff --git a/include/openssl/encodererr.h b/include/openssl/encodererr.h
index 007070e0c0..e146d6ec92 100644
--- a/include/openssl/encodererr.h
+++ b/include/openssl/encodererr.h
@@ -30,7 +30,8 @@ int ERR_load_OSSL_ENCODER_strings(void);
 /*
  * OSSL_ENCODER reason codes.
  */
-# define OSSL_ENCODER_R_INCORRECT_PROPERTY_QUERY       100
-# define OSSL_ENCODER_R_ENCODER_NOT_FOUND           101
+# define OSSL_ENCODER_R_ENCODER_NOT_FOUND                 101
+# define OSSL_ENCODER_R_INCORRECT_PROPERTY_QUERY          100
+# define OSSL_ENCODER_R_MISSING_GET_PARAMS                102
 
 #endif
diff --git a/include/openssl/evp.h b/include/openssl/evp.h
index d3892982e7..ff3234a914 100644
--- a/include/openssl/evp.h
+++ b/include/openssl/evp.h
@@ -1189,6 +1189,9 @@ DEPRECATEDIN_3_0(int EVP_PKEY_encrypt_old(unsigned char *enc_key,
                                           const unsigned char *key,
                                           int key_len, EVP_PKEY *pub_key))
 int EVP_PKEY_is_a(const EVP_PKEY *pkey, const char *name);
+void EVP_PKEY_typenames_do_all(const EVP_PKEY *pkey,
+                               void (*fn)(const char *name, void *data),
+                               void *data);
 int EVP_PKEY_type(int type);
 int EVP_PKEY_id(const EVP_PKEY *pkey);
 int EVP_PKEY_base_id(const EVP_PKEY *pkey);
diff --git a/providers/baseprov.c b/providers/baseprov.c
index 0904e46333..aeb81e27e3 100644
--- a/providers/baseprov.c
+++ b/providers/baseprov.c
@@ -69,9 +69,9 @@ static int base_get_params(void *provctx, OSSL_PARAM params[])
 }
 
 static const OSSL_ALGORITHM base_encoder[] = {
-#define ENCODER(name, _fips, _format, _type, func_table)                    \
+#define ENCODER(name, _fips, _output, func_table)                           \
     { name,                                                                 \
-      "provider=base,fips=" _fips ",format=" _format ",type=" _type,        \
+      "provider=base,fips=" _fips ",output=" _output,                       \
       (func_table) }
 
 #include "encoders.inc"
diff --git a/providers/defltprov.c b/providers/defltprov.c
index dfb113903e..8f663affbb 100644
--- a/providers/defltprov.c
+++ b/providers/defltprov.c
@@ -418,9 +418,9 @@ static const OSSL_ALGORITHM deflt_keymgmt[] = {
 };
 
 static const OSSL_ALGORITHM deflt_encoder[] = {
-#define ENCODER(name, _fips, _format, _type, func_table)                    \
+#define ENCODER(name, _fips, _output, func_table)                           \
     { name,                                                                 \
-      "provider=default,fips=" _fips ",format=" _format ",type=" _type,     \
+      "provider=default,fips=" _fips ",output=" _output,                    \
       (func_table) }
 
 #include "encoders.inc"
diff --git a/providers/encoders.inc b/providers/encoders.inc
index 284703c6e8..fcb1bfe3ec 100644
--- a/providers/encoders.inc
+++ b/providers/encoders.inc
@@ -11,161 +11,47 @@
 # error Macro ENCODER undefined
 #endif
 
-    ENCODER("RSA", "yes", "text", "private",
-            rsa_priv_to_text_encoder_functions),
-    ENCODER("RSA", "yes", "text", "public",
-            rsa_pub_to_text_encoder_functions),
-    ENCODER("RSA", "yes", "der", "private",
-            rsa_priv_to_der_encoder_functions),
-    ENCODER("RSA", "yes", "der", "public",
-            rsa_pub_to_der_encoder_functions),
-    ENCODER("RSA", "yes", "pem", "private",
-            rsa_priv_to_pem_encoder_functions),
-    ENCODER("RSA", "yes", "pem", "public",
-            rsa_pub_to_pem_encoder_functions),
-    ENCODER("RSA-PSS", "yes", "text", "private",
-            rsa_priv_to_text_encoder_functions),
-    ENCODER("RSA-PSS", "yes", "text", "public",
-            rsa_pub_to_text_encoder_functions),
-    ENCODER("RSA-PSS", "yes", "der", "private",
-            rsa_priv_to_der_encoder_functions),
-    ENCODER("RSA-PSS", "yes", "der", "public",
-            rsa_pub_to_der_encoder_functions),
-    ENCODER("RSA-PSS", "yes", "pem", "private",
-            rsa_priv_to_pem_encoder_functions),
-    ENCODER("RSA-PSS", "yes", "pem", "public",
-            rsa_pub_to_pem_encoder_functions),
+    ENCODER("RSA", "yes", "text", rsa_to_text_encoder_functions),
+    ENCODER("RSA", "yes", "der", rsa_to_der_encoder_functions),
+    ENCODER("RSA", "yes", "pem", rsa_to_pem_encoder_functions),
+    ENCODER("RSA-PSS", "yes", "text", rsapss_to_text_encoder_functions),
+    ENCODER("RSA-PSS", "yes", "der", rsapss_to_der_encoder_functions),
+    ENCODER("RSA-PSS", "yes", "pem", rsapss_to_pem_encoder_functions),
 
 #ifndef OPENSSL_NO_DH
-    ENCODER("DH", "yes", "text", "private",
-            dh_priv_to_text_encoder_functions),
-    ENCODER("DH", "yes", "text", "public",
-            dh_pub_to_text_encoder_functions),
-    ENCODER("DH", "yes", "text", "parameters",
-            dh_param_to_text_encoder_functions),
-    ENCODER("DH", "yes", "der", "private",
-            dh_priv_to_der_encoder_functions),
-    ENCODER("DH", "yes", "der", "public",
-            dh_pub_to_der_encoder_functions),
-    ENCODER("DH", "yes", "der", "parameters",
-            dh_param_to_der_encoder_functions),
-    ENCODER("DH", "yes", "pem", "private",
-            dh_priv_to_pem_encoder_functions),
-    ENCODER("DH", "yes", "pem", "public",
-            dh_pub_to_pem_encoder_functions),
-    ENCODER("DH", "yes", "pem", "parameters",
-            dh_param_to_pem_encoder_functions),
+    ENCODER("DH", "yes", "text", dh_to_text_encoder_functions),
+    ENCODER("DH", "yes", "der", dh_to_der_encoder_functions),
+    ENCODER("DH", "yes", "pem", dh_to_pem_encoder_functions),
 
-    ENCODER("DHX", "yes", "text", "private",
-            dh_priv_to_text_encoder_functions),
-    ENCODER("DHX", "yes", "text", "public",
-            dh_pub_to_text_encoder_functions),
-    ENCODER("DHX", "yes", "text", "parameters",
-            dh_param_to_text_encoder_functions),
-    ENCODER("DHX", "yes", "der", "private",
-            dh_priv_to_der_encoder_functions),
-    ENCODER("DHX", "yes", "der", "public",
-            dh_pub_to_der_encoder_functions),
-    ENCODER("DHX", "yes", "der", "parameters",
-            dh_param_to_der_encoder_functions),
-    ENCODER("DHX", "yes", "pem", "private",
-            dh_priv_to_pem_encoder_functions),
-    ENCODER("DHX", "yes", "pem", "public",
-            dh_pub_to_pem_encoder_functions),
-    ENCODER("DHX", "yes", "pem", "parameters",
-            dh_param_to_pem_encoder_functions),
+    ENCODER("DHX", "yes", "text", dhx_to_text_encoder_functions),
+    ENCODER("DHX", "yes", "der", dhx_to_der_encoder_functions),
+    ENCODER("DHX", "yes", "pem", dhx_to_pem_encoder_functions),
 #endif
 
 #ifndef OPENSSL_NO_DSA
-    ENCODER("DSA", "yes", "text", "private",
-            dsa_priv_to_text_encoder_functions),
-    ENCODER("DSA", "yes", "text", "public",
-            dsa_pub_to_text_encoder_functions),
-    ENCODER("DSA", "yes", "text", "parameters",
-            dsa_param_to_text_encoder_functions),
-    ENCODER("DSA", "yes", "der", "private",
-            dsa_priv_to_der_encoder_functions),
-    ENCODER("DSA", "yes", "der", "public",
-            dsa_pub_to_der_encoder_functions),
-    ENCODER("DSA", "yes", "der", "parameters",
-            dsa_param_to_der_encoder_functions),
-    ENCODER("DSA", "yes", "pem", "private",
-            dsa_priv_to_pem_encoder_functions),
-    ENCODER("DSA", "yes", "pem", "public",
-            dsa_pub_to_pem_encoder_functions),
-    ENCODER("DSA", "yes", "pem", "parameters",
-            dsa_param_to_pem_encoder_functions),
+    ENCODER("DSA", "yes", "text", dsa_to_text_encoder_functions),
+    ENCODER("DSA", "yes", "der", dsa_to_der_encoder_functions),
+    ENCODER("DSA", "yes", "pem", dsa_to_pem_encoder_functions),
 #endif
 
 #ifndef OPENSSL_NO_EC
-    ENCODER("X25519", "yes", "text", "private",
-            x25519_priv_to_text_encoder_functions),
-    ENCODER("X25519", "yes", "text", "public",
-            x25519_pub_to_text_encoder_functions),
-    ENCODER("X25519", "yes", "der", "private",
-            x25519_priv_to_der_encoder_functions),
-    ENCODER("X25519", "yes", "der", "public",
-            x25519_pub_to_der_encoder_functions),
-    ENCODER("X25519", "yes", "pem", "private",
-            x25519_priv_to_pem_encoder_functions),
-    ENCODER("X25519", "yes", "pem", "public",
-            x25519_pub_to_pem_encoder_functions),
+    ENCODER("X25519", "yes", "text", x25519_to_text_encoder_functions),
+    ENCODER("X25519", "yes", "der", x25519_to_der_encoder_functions),
+    ENCODER("X25519", "yes", "pem", x25519_to_pem_encoder_functions),
 
-    ENCODER("X448", "yes", "text", "private",
-            x448_priv_to_text_encoder_functions),
-    ENCODER("X448", "yes", "text", "public",
-            x448_pub_to_text_encoder_functions),
-    ENCODER("X448", "yes", "der", "private",
-            x448_priv_to_der_encoder_functions),
-    ENCODER("X448", "yes", "der", "public",
-            x448_pub_to_der_encoder_functions),
-    ENCODER("X448", "yes", "pem", "private",
-            x448_priv_to_pem_encoder_functions),
-    ENCODER("X448", "yes", "pem", "public",
-            x448_pub_to_pem_encoder_functions),
+    ENCODER("X448", "yes", "text", x448_to_text_encoder_functions),
+    ENCODER("X448", "yes", "der", x448_to_der_encoder_functions),
+    ENCODER("X448", "yes", "pem", x448_to_pem_encoder_functions),
 
-    ENCODER("ED25519", "yes", "text", "private",
-            ed25519_priv_to_text_encoder_functions),
-    ENCODER("ED25519", "yes", "text", "public",
-            ed25519_pub_to_text_encoder_functions),
-    ENCODER("ED25519", "yes", "der", "private",
-            ed25519_priv_to_der_encoder_functions),
-    ENCODER("ED25519", "yes", "der", "public",
-            ed25519_pub_to_der_encoder_functions),
-    ENCODER("ED25519", "yes", "pem", "private",
-            ed25519_priv_to_pem_encoder_functions),
-    ENCODER("ED25519", "yes", "pem", "public",
-            ed25519_pub_to_pem_encoder_functions),
+    ENCODER("ED25519", "yes", "text", ed25519_to_text_encoder_functions),
+    ENCODER("ED25519", "yes", "der", ed25519_to_der_encoder_functions),
+    ENCODER("ED25519", "yes", "pem", ed25519_to_pem_encoder_functions),
 
-    ENCODER("ED448", "yes", "text", "private",
-            ed448_priv_to_text_encoder_functions),
-    ENCODER("ED448", "yes", "text", "public",
-            ed448_pub_to_text_encoder_functions),
-    ENCODER("ED448", "yes", "der", "private",
-            ed448_priv_to_der_encoder_functions),
-    ENCODER("ED448", "yes", "der", "public",
-            ed448_pub_to_der_encoder_functions),
-    ENCODER("ED448", "yes", "pem", "private",
-            ed448_priv_to_pem_encoder_functions),
-    ENCODER("ED448", "yes", "pem", "public",
-            ed448_pub_to_pem_encoder_functions),
+    ENCODER("ED448", "yes", "text", ed448_to_text_encoder_functions),
+    ENCODER("ED448", "yes", "der", ed448_to_der_encoder_functions),
+    ENCODER("ED448", "yes", "pem", ed448_to_pem_encoder_functions),
 
-    ENCODER("EC", "yes", "text", "private",
-            ec_priv_to_text_encoder_functions),
-    ENCODER("EC", "yes", "text", "public",
-            ec_pub_to_text_encoder_functions),
-    ENCODER("EC", "yes", "text", "parameters",
-            ec_param_to_text_encoder_functions),
-    ENCODER("EC", "yes", "der", "private",
-            ec_priv_to_der_encoder_functions),
-    ENCODER("EC", "yes", "der", "public",
-            ec_pub_to_der_encoder_functions),
-    ENCODER("EC", "yes", "der", "parameters",
-            ec_param_to_der_encoder_functions),
-    ENCODER("EC", "yes", "pem", "private",
-            ec_priv_to_pem_encoder_functions),
-    ENCODER("EC", "yes", "pem", "public",
-            ec_pub_to_pem_encoder_functions),
-    ENCODER("EC", "yes", "pem", "parameters",
-            ec_param_to_pem_encoder_functions),
+    ENCODER("EC", "yes", "text", ec_to_text_encoder_functions),
+    ENCODER("EC", "yes", "der", ec_to_der_encoder_functions),
+    ENCODER("EC", "yes", "pem", ec_to_pem_encoder_functions),
 #endif
diff --git a/providers/implementations/encode_decode/encode_key2any.c b/providers/implementations/encode_decode/encode_key2any.c
index ba240aa094..bb82c0ad4a 100644
--- a/providers/implementations/encode_decode/encode_key2any.c
+++ b/providers/implementations/encode_decode/encode_key2any.c
@@ -47,6 +47,7 @@ struct key2any_ctx_st {
     struct ossl_passphrase_data_st pwdata;
 };
 
+typedef int check_key_type_fn(const void *key, int nid);
 typedef int key_to_paramstring_fn(const void *key, int nid,
                                   void **str, int *strtype);
 typedef int key_to_der_fn(BIO *out, const void *key, int key_nid,
@@ -241,20 +242,12 @@ static int key_to_pem_pubkey_bio(BIO *out, const void *key, int key_nid,
     return ret;
 }
 
+#define der_output_type         "DER"
+#define pem_output_type         "PEM"
+
 /* ---------------------------------------------------------------------- */
 
 #ifndef OPENSSL_NO_DH
-# define dh_param_selection     OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS
-# define dh_pub_selection       (OSSL_KEYMGMT_SELECT_PUBLIC_KEY \
-                                 | dh_param_selection)
-# define dh_priv_selection      (OSSL_KEYMGMT_SELECT_KEYPAIR \
-                                 | dh_param_selection)
-
-static int dh_type_to_evp(const DH *dh)
-{
-    return DH_test_flags(dh, DH_FLAG_TYPE_DHX) ? EVP_PKEY_DHX : EVP_PKEY_DH;
-}
-
 static int prepare_dh_params(const void *dh, int nid,
                              void **pstr, int *pstrtype)
 {
@@ -333,19 +326,24 @@ static int dh_params_to_pem_bio(BIO *out, const void *key)
 {
     return PEM_write_bio_DHparams(out, key);
 }
+
+static int dh_check_key_type(const void *key, int expected_type)
+{
+    int type =
+        DH_test_flags(key, DH_FLAG_TYPE_DHX) ? EVP_PKEY_DHX : EVP_PKEY_DH;
+
+    return type == expected_type;
+}
+
+# define dh_evp_type            EVP_PKEY_DH
+# define dhx_evp_type           EVP_PKEY_DHX
+# define dh_input_type          "DH"
+# define dhx_input_type         "DHX"
 #endif
 
 /* ---------------------------------------------------------------------- */
 
 #ifndef OPENSSL_NO_DSA
-# define dsa_param_selection    OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS
-# define dsa_pub_selection      (OSSL_KEYMGMT_SELECT_PUBLIC_KEY \
-                                 | dsa_param_selection)
-# define dsa_priv_selection     (OSSL_KEYMGMT_SELECT_KEYPAIR \
-                                 | dsa_param_selection)
-
-# define dsa_type_to_evp(key) EVP_PKEY_DSA
-
 static int prepare_some_dsa_params(const void *dsa, int nid,
                                    void **pstr, int *pstrtype)
 {
@@ -449,19 +447,15 @@ static int dsa_params_to_pem_bio(BIO *out, const void *key)
 {
     return PEM_write_bio_DSAparams(out, key);
 }
+
+# define dsa_check_key_type     NULL
+# define dsa_evp_type           EVP_PKEY_DSA
+# define dsa_input_type         "DSA"
 #endif
 
 /* ---------------------------------------------------------------------- */
 
 #ifndef OPENSSL_NO_EC
-# define ec_param_selection     OSSL_KEYMGMT_SELECT_ALL_PARAMETERS
-# define ec_pub_selection       (OSSL_KEYMGMT_SELECT_PUBLIC_KEY \
-                                 | ec_param_selection)
-# define ec_priv_selection      (OSSL_KEYMGMT_SELECT_KEYPAIR \
-                                 | ec_param_selection)
-
-# define ec_type_to_evp(key) EVP_PKEY_EC
-
 static int prepare_ec_explicit_params(const void *eckey,
                                       void **pstr, int *pstrtype)
 {
@@ -550,19 +544,15 @@ static int ec_priv_to_der(const void *veckey, unsigned char **pder)
     EC_KEY_set_enc_flags(eckey, old_flags); /* restore old flags */
     return ret; /* return the length of the der encoded data */
 }
+
+# define ec_check_key_type      NULL
+# define ec_evp_type            EVP_PKEY_EC
+# define ec_input_type          "EC"
 #endif
 
 /* ---------------------------------------------------------------------- */
 
 #ifndef OPENSSL_NO_EC
-# define ecx_pub_selection      OSSL_KEYMGMT_SELECT_PUBLIC_KEY
-# define ecx_priv_selection     OSSL_KEYMGMT_SELECT_KEYPAIR
-
-# define ed25519_type_to_evp(key) EVP_PKEY_ED25519
-# define ed448_type_to_evp(key) EVP_PKEY_ED448
-# define x25519_type_to_evp(key) EVP_PKEY_X25519
-# define x448_type_to_evp(key) EVP_PKEY_X448
-
 # define prepare_ecx_params NULL
 
 static int ecx_pub_to_der(const void *vecxkey, unsigned char **pder)
@@ -609,31 +599,22 @@ static int ecx_priv_to_der(const void *vecxkey, unsigned char **pder)
     return keybloblen;
 }
 
-# define ecx_params_to_der_bio NULL
-# define ecx_params_to_pem_bio NULL
+# define ecx_params_to_der_bio  NULL
+# define ecx_params_to_pem_bio  NULL
+# define ecx_check_key_type     NULL
+
+# define ed25519_evp_type       EVP_PKEY_ED25519
+# define ed448_evp_type         EVP_PKEY_ED448
+# define x25519_evp_type        EVP_PKEY_X25519
+# define x448_evp_type          EVP_PKEY_X448
+# define ed25519_input_type     "ED25519"
+# define ed448_input_type       "ED448"
+# define x25519_input_type      "X25519"
+# define x448_input_type        "X448"
 #endif
 
 /* ---------------------------------------------------------------------- */
 
-#define rsa_param_selection     OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS
-#define rsa_pub_selection       (OSSL_KEYMGMT_SELECT_PUBLIC_KEY \
-                                 | rsa_param_selection)
-#define rsa_priv_selection      (OSSL_KEYMGMT_SELECT_KEYPAIR \
-                                 | rsa_param_selection)
-
-static int rsa_type_to_evp(const RSA *rsa)
-{
-    switch (RSA_test_flags(rsa, RSA_FLAG_TYPE_MASK)) {
-    case RSA_FLAG_TYPE_RSA:
-        return EVP_PKEY_RSA;
-    case RSA_FLAG_TYPE_RSASSAPSS:
-        return EVP_PKEY_RSA_PSS;
-    }
-
-    /* Currently unsupported RSA key type */
-    return EVP_PKEY_NONE;
-}
-
 /*
  * Helper functions to prepare RSA-PSS params for encoding.  We would
  * have simply written the whole AlgorithmIdentifier, but existing libcrypto
@@ -709,15 +690,34 @@ static int prepare_rsa_params(const void *rsa, int nid,
     return 0;
 }
 
-#define rsa_params_to_der_bio NULL
-#define rsa_params_to_pem_bio NULL
-#define rsa_priv_to_der (i2d_of_void *)i2d_RSAPrivateKey
-#define rsa_pub_to_der (i2d_of_void *)i2d_RSAPublicKey
+#define rsa_params_to_der_bio   NULL
+#define rsa_params_to_pem_bio   NULL
+#define rsa_priv_to_der         (i2d_of_void *)i2d_RSAPrivateKey
+#define rsa_pub_to_der          (i2d_of_void *)i2d_RSAPublicKey
+
+static int rsa_check_key_type(const void *rsa, int expected_type)
+{
+    switch (RSA_test_flags(rsa, RSA_FLAG_TYPE_MASK)) {
+    case RSA_FLAG_TYPE_RSA:
+        return expected_type == EVP_PKEY_RSA;
+    case RSA_FLAG_TYPE_RSASSAPSS:
+        return expected_type == EVP_PKEY_RSA_PSS;
+    }
+
+    /* Currently unsupported RSA key type */
+    return EVP_PKEY_NONE;
+}
+
+#define rsa_evp_type            EVP_PKEY_RSA
+#define rsapss_evp_type         EVP_PKEY_RSA_PSS
+#define rsa_input_type          "RSA"
+#define rsapss_input_type       "RSA-PSS"
 
 /* ---------------------------------------------------------------------- */
 
 static OSSL_FUNC_decoder_newctx_fn key2any_newctx;
 static OSSL_FUNC_decoder_freectx_fn key2any_freectx;
+static OSSL_FUNC_decoder_gettable_params_fn key2any_gettable_params;
 
 static void *key2any_newctx(void *provctx)
 {
@@ -738,6 +738,32 @@ static void key2any_freectx(void *vctx)
     OPENSSL_free(ctx);
 }
 
+static const OSSL_PARAM *key2any_gettable_params(void *provctx)
+{
+    static const OSSL_PARAM gettables[] = {
+        { OSSL_ENCODER_PARAM_OUTPUT_TYPE, OSSL_PARAM_UTF8_PTR, NULL, 0, 0 },
+        OSSL_PARAM_END,
+    };
+
+    return gettables;
+}
+
+static int key2any_get_params(OSSL_PARAM params[], const char *input_type,
+                              const char *output_type)
+{
+    OSSL_PARAM *p;
+
+    p = OSSL_PARAM_locate(params, OSSL_ENCODER_PARAM_INPUT_TYPE);
+    if (p != NULL && !OSSL_PARAM_set_utf8_ptr(p, input_type))
+        return 0;
+
+    p = OSSL_PARAM_locate(params, OSSL_ENCODER_PARAM_OUTPUT_TYPE);
+    if (p != NULL && !OSSL_PARAM_set_utf8_ptr(p, output_type))
+        return 0;
+
+    return 1;
+}
+
 static const OSSL_PARAM *key2any_settable_ctx_params(ossl_unused void *provctx)
 {
     static const OSSL_PARAM settables[] = {
@@ -777,127 +803,145 @@ static int key2any_set_ctx_params(void *vctx, const OSSL_PARAM params[])
     return 1;
 }
 
-static int key2any_encode(struct key2any_ctx_st *ctx,
+static int key2any_encode(struct key2any_ctx_st *ctx, OSSL_CORE_BIO *cout,
                           const void *key, int type,
-                          OSSL_CORE_BIO *cout, key_to_der_fn *writer,
+                          check_key_type_fn *checker,
+                          key_to_der_fn *writer,
                           OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg,
                           key_to_paramstring_fn *key2paramstring,
                           i2d_of_void *key2der)
 {
-    BIO *out = bio_new_from_core_bio(ctx->provctx, cout);
     int ret = 0;
 
-    if (out != NULL
-        && writer != NULL
-        && ossl_pw_set_ossl_passphrase_cb(&ctx->pwdata, cb, cbarg))
-        ret = writer(out, key, type, key2paramstring, key2der, ctx);
+    if (key == NULL) {
+        ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER);
+    } else if (checker == NULL || checker(key, type)) {
+        BIO *out = bio_new_from_core_bio(ctx->provctx, cout);
+
+        if (out != NULL
+            && writer != NULL
+            && ossl_pw_set_ossl_passphrase_cb(&ctx->pwdata, cb, cbarg))
+            ret = writer(out, key, type, key2paramstring, key2der, ctx);
 
-    BIO_free(out);
+        BIO_free(out);
+    } else {
+        ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT);
+    }
     return ret;
 }
 
-static int key2any_encode_params(struct key2any_ctx_st *ctx, const void *key,
+static int key2any_encode_params(struct key2any_ctx_st *ctx,
                                  OSSL_CORE_BIO *cout,
+                                 const void *key, int type,
+                                 check_key_type_fn *checker,
                                  write_bio_of_void_fn *writer)
 {
     int ret = 0;
-    BIO *out = bio_new_from_core_bio(ctx->provctx, cout);
 
-    if (out != NULL && writer != NULL)
-        ret = writer(out, key);
+    if (key == NULL) {
+        ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER);
+    } else if (checker == NULL || checker(key, type)) {
+        BIO *out = bio_new_from_core_bio(ctx->provctx, cout);
+
+        if (out != NULL && writer != NULL)
+            ret = writer(out, key);
 
-    BIO_free(out);
+        BIO_free(out);
+    } else {
+        ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT);
+    }
 
     return ret;
 }
 
-#define ALLOWED_SELECTORS \
-    (OSSL_KEYMGMT_SELECT_ALL_PARAMETERS | OSSL_KEYMGMT_SELECT_KEYPAIR)
-
-#define MAKE_ENCODER_KIND(impl, kind, type, evp_type, output)           \
-    static OSSL_FUNC_encoder_encode_data_fn                             \
-    impl##_##kind##2##output##_encode_d;                                \
-    static OSSL_FUNC_encoder_encode_object_fn                           \
-    impl##_##kind##2##output##_encode_o;                                \
-    static int                                                          \
-    impl##_##kind##2##output##_encode_d(void *vctx,                     \
-                                        const OSSL_PARAM params[],      \
-                                        OSSL_CORE_BIO *cout,            \
-                                        OSSL_PASSPHRASE_CALLBACK *cb,   \
-                                        void *cbarg)                    \
-    {                                                                   \
-        struct key2any_ctx_st *ctx = vctx;                              \
-        int selection = type##_##kind##_selection;                      \
-        void *key = ossl_prov_import_key(impl##_keymgmt_functions,      \
-                                         ctx->provctx, selection,       \
-                                         params);                       \
-        int ret;                                                        \
-                                                                        \
-        if (key == NULL)                                                \
-            return 0;                                                   \
-                                                                        \
-        ret = impl##_##kind##2##output##_encode_o(ctx, key, cout,       \
-                                                  cb, cbarg);           \
-        ossl_prov_free_key(impl##_keymgmt_functions, key);              \
-        return ret;                                                     \
-    }                                                                   \
-    static int                                                          \
-    impl##_##kind##2##output##_encode_o(void *vctx, const void *key,    \
-                                        OSSL_CORE_BIO *cout,            \
-                                        OSSL_PASSPHRASE_CALLBACK *cb,   \
-                                        void *cbarg)                    \
-    {                                                                   \
-        int selection = type##_##kind##_selection;                      \
-                                                                        \
-        if (!ossl_assert(selection != 0)                                \
-            || !ossl_assert((selection & ~ALLOWED_SELECTORS) == 0)) {   \
-            ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR);              \
-            return 0;                                                   \
-        }                                                               \
-        if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)         \
-            return key2any_encode(vctx, key, impl##_type_to_evp(key),   \
-                                  cout, key_to_##output##_pkcs8_bio,    \
-                                  cb, cbarg,                            \
-                                  prepare_##type##_params,              \
-                                  type##_priv_to_der);                  \
-        if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)          \
-            return key2any_encode(vctx, key, impl##_type_to_evp(key),   \
-                                  cout, key_to_##output##_pubkey_bio,   \
-                                  cb, cbarg,                            \
-                                  prepare_##type##_params,              \
-                                  type##_pub_to_der);                   \
-        return key2any_encode_params(vctx, key, cout,                   \
-                                     type##_params_to_##output##_bio);  \
-    }                                                                   \
-    const OSSL_DISPATCH                                                 \
-    impl##_##kind##_to_##output##_encoder_functions[] = {               \
-        { OSSL_FUNC_ENCODER_NEWCTX,                                     \
-          (void (*)(void))key2any_newctx },                             \
-        { OSSL_FUNC_ENCODER_FREECTX,                                    \
-          (void (*)(void))key2any_freectx },                            \
-        { OSSL_FUNC_ENCODER_SETTABLE_CTX_PARAMS,                        \
-          (void (*)(void))key2any_settable_ctx_params },                \
-        { OSSL_FUNC_ENCODER_SET_CTX_PARAMS,                             \
-          (void (*)(void))key2any_set_ctx_params },                     \
-        { OSSL_FUNC_ENCODER_ENCODE_DATA,                                \
-          (void (*)(void))impl##_##kind##2##output##_encode_d },        \
-        { OSSL_FUNC_ENCODER_ENCODE_OBJECT,                              \
-          (void (*)(void))impl##_##kind##2##output##_encode_o },        \
-        { 0, NULL }                                                     \
+#define MAKE_ENCODER(impl, type, evp_type, output)                          \
+    static OSSL_FUNC_encoder_get_params_fn                                  \
+    impl##2##output##_get_params;                                           \
+    static OSSL_FUNC_encoder_import_object_fn                               \
+    impl##2##output##_import_object;                                        \
+    static OSSL_FUNC_encoder_free_object_fn                                 \
+    impl##2##output##_free_object;                                          \
+    static OSSL_FUNC_encoder_encode_fn impl##2##output##_encode;            \
+                                                                            \
+    static int impl##2##output##_get_params(OSSL_PARAM params[])            \
+    {                                                                       \
+        return key2any_get_params(params, impl##_input_type,                \
+                                  output##_output_type);                    \
+    }                                                                       \
+    static void *                                                           \
+    impl##2##output##_import_object(void *vctx, int selection,              \
+                                    const OSSL_PARAM params[])              \
+    {                                                                       \
+        struct key2any_ctx_st *ctx = vctx;                                  \
+        return ossl_prov_import_key(impl##_keymgmt_functions,               \
+                                    ctx->provctx, selection, params);       \
+    }                                                                       \
+    static void impl##2##output##_free_object(void *key)                    \
+    {                                                                       \
+        ossl_prov_free_key(impl##_keymgmt_functions, key);                  \
+    }                                                                       \
+    static int                                                              \
+    impl##2##output##_encode(void *ctx, OSSL_CORE_BIO *cout,                \
+                             const void *key,                               \
+                             const OSSL_PARAM key_abstract[],               \
+                             int selection,                                 \
+                             OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)     \
+    {                                                                       \
+        /* We don't deal with abstract objects */                           \
+        if (key_abstract != NULL) {                                         \
+            ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT);         \
+            return 0;                                                       \
+        }                                                                   \
+        if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)             \
+            return key2any_encode(ctx, cout, key, impl##_evp_type,          \
+                                  type##_check_key_type,                    \
+                                  key_to_##output##_pkcs8_bio,              \
+                                  cb, cbarg,                                \
+                                  prepare_##type##_params,                  \
+                                  type##_priv_to_der);                      \
+        if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)              \
+            return key2any_encode(ctx, cout, key, impl##_evp_type,          \
+                                  type##_check_key_type,                    \
+                                  key_to_##output##_pubkey_bio,             \
+                                  cb, cbarg,                                \
+                                  prepare_##type##_params,                  \
+                                  type##_pub_to_der);                       \
+        if ((selection & OSSL_KEYMGMT_SELECT_ALL_PARAMETERS) != 0)          \
+            return key2any_encode_params(ctx, cout, key,                    \
+                                         impl##_evp_type,                   \
+                                         type##_check_key_type,             \
+                                         type##_params_to_##output##_bio);  \
+                                                                            \
+        ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT);             \
+        return 0;                                                           \
+    }                                                                       \
+    const OSSL_DISPATCH impl##_to_##output##_encoder_functions[] = {        \
+        { OSSL_FUNC_ENCODER_NEWCTX,                                         \
+          (void (*)(void))key2any_newctx },                                 \
+        { OSSL_FUNC_ENCODER_FREECTX,                                        \
+          (void (*)(void))key2any_freectx },                                \
+        { OSSL_FUNC_ENCODER_GETTABLE_PARAMS,                                \
+          (void (*)(void))key2any_gettable_params },                        \
+        { OSSL_FUNC_ENCODER_GET_PARAMS,                                     \
+          (void (*)(void))impl##2##output##_get_params },                   \
+        { OSSL_FUNC_ENCODER_SETTABLE_CTX_PARAMS,                            \
+          (void (*)(void))key2any_settable_ctx_params },                    \
+        { OSSL_FUNC_ENCODER_SET_CTX_PARAMS,                                 \
+          (void (*)(void))key2any_set_ctx_params },                         \
+        { OSSL_FUNC_ENCODER_IMPORT_OBJECT,                                  \
+          (void (*)(void))impl##2##output##_import_object },                \
+        { OSSL_FUNC_ENCODER_FREE_OBJECT,                                    \
+          (void (*)(void))impl##2##output##_free_object },                  \
+        { OSSL_FUNC_ENCODER_ENCODE,                                         \
+          (void (*)(void))impl##2##output##_encode },                       \
+        { 0, NULL }                                                         \
     }
 
-#define MAKE_ENCODER(impl, type, evp_type, output)                      \
-    MAKE_ENCODER_KIND(impl, param, type, evp_type, output);             \
-    MAKE_ENCODER_KIND(impl, pub, type, evp_type, output);               \
-    MAKE_ENCODER_KIND(impl, priv, type, evp_type, output)
-
-#define MAKE_ENCODER_NOPARAM(impl, type, evp_type, output)              \
-    MAKE_ENCODER_KIND(impl, pub, type, evp_type, output);               \
-    MAKE_ENCODER_KIND(impl, priv, type, evp_type, output)
-
 #ifndef OPENSSL_NO_DH
 MAKE_ENCODER(dh, dh, EVP_PKEY_DH, der);
 MAKE_ENCODER(dh, dh, EVP_PKEY_DH, pem);
+MAKE_ENCODER(dhx, dh, EVP_PKEY_DH, der);
+MAKE_ENCODER(dhx, dh, EVP_PKEY_DH, pem);
 #endif
 #ifndef OPENSSL_NO_DSA
 MAKE_ENCODER(dsa, dsa, EVP_PKEY_DSA, der);
@@ -906,19 +950,16 @@ MAKE_ENCODER(dsa, dsa, EVP_PKEY_DSA, pem);
 #ifndef OPENSSL_NO_EC
 MAKE_ENCODER(ec, ec, EVP_PKEY_EC, der);
 MAKE_ENCODER(ec, ec, EVP_PKEY_EC, pem);
-MAKE_ENCODER_NOPARAM(ed25519, ecx, EVP_PKEY_ED25519, der);
-MAKE_ENCODER_NOPARAM(ed25519, ecx, EVP_PKEY_ED25519, pem);
-MAKE_ENCODER_NOPARAM(ed448, ecx, EVP_PKEY_ED448, der);
-MAKE_ENCODER_NOPARAM(ed448, ecx, EVP_PKEY_ED448, pem);
-MAKE_ENCODER_NOPARAM(x25519, ecx, EVP_PKEY_X25519, der);
-MAKE_ENCODER_NOPARAM(x25519, ecx, EVP_PKEY_X25519, pem);
-MAKE_ENCODER_NOPARAM(x448, ecx, EVP_PKEY_ED448, der);
-MAKE_ENCODER_NOPARAM(x448, ecx, EVP_PKEY_ED448, pem);
+MAKE_ENCODER(ed25519, ecx, EVP_PKEY_ED25519, der);
+MAKE_ENCODER(ed25519, ecx, EVP_PKEY_ED25519, pem);
+MAKE_ENCODER(ed448, ecx, EVP_PKEY_ED448, der);
+MAKE_ENCODER(ed448, ecx, EVP_PKEY_ED448, pem);
+MAKE_ENCODER(x25519, ecx, EVP_PKEY_X25519, der);
+MAKE_ENCODER(x25519, ecx, EVP_PKEY_X25519, pem);
+MAKE_ENCODER(x448, ecx, EVP_PKEY_ED448, der);
+MAKE_ENCODER(x448, ecx, EVP_PKEY_ED448, pem);
 #endif
-/*
- * RSA-PSS does have parameters, but we don't have a separate output for them,
- * so we don't pretend we do.  Parameter handling remains internal within the
- * RSA helper functions.
- */
-MAKE_ENCODER_NOPARAM(rsa, rsa, EVP_PKEY_RSA, der);
-MAKE_ENCODER_NOPARAM(rsa, rsa, EVP_PKEY_RSA, pem);
+MAKE_ENCODER(rsa, rsa, EVP_PKEY_RSA, der);
+MAKE_ENCODER(rsa, rsa, EVP_PKEY_RSA, pem);
+MAKE_ENCODER(rsapss, rsa, EVP_PKEY_RSA, der);
+MAKE_ENCODER(rsapss, rsa, EVP_PKEY_RSA, pem);
diff --git a/providers/implementations/encode_decode/encode_key2text.c b/providers/implementations/encode_decode/encode_key2text.c
index 7f5ca2b7b7..2cf4fab95f 100644
--- a/providers/implementations/encode_decode/encode_key2text.c
+++ b/providers/implementations/encode_decode/encode_key2text.c
@@ -207,12 +207,6 @@ err:
 /* ---------------------------------------------------------------------- */
 
 #ifndef OPENSSL_NO_DH
-# define dh_param_selection     OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS
-# define dh_pub_selection       (OSSL_KEYMGMT_SELECT_PUBLIC_KEY \
-                                 | dh_param_selection)
-# define dh_priv_selection      (OSSL_KEYMGMT_SELECT_KEYPAIR \
-                                 | dh_param_selection)
-
 static int dh_to_text(BIO *out, const void *key, int selection)
 {
     const DH *dh = key;
@@ -275,17 +269,14 @@ static int dh_to_text(BIO *out, const void *key, int selection)
 
     return 1;
 }
+
+# define dh_input_type          "DH"
+# define dhx_input_type         "DHX"
 #endif
 
 /* ---------------------------------------------------------------------- */
 
 #ifndef OPENSSL_NO_DSA
-# define dsa_param_selection    OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS
-# define dsa_pub_selection      (OSSL_KEYMGMT_SELECT_PUBLIC_KEY \
-                                 | dsa_param_selection)
-# define dsa_priv_selection     (OSSL_KEYMGMT_SELECT_KEYPAIR \
-                                 | dsa_param_selection)
-
 static int dsa_to_text(BIO *out, const void *key, int selection)
 {
     const DSA *dsa = key;
@@ -348,17 +339,13 @@ static int dsa_to_text(BIO *out, const void *key, int selection)
 
     return 1;
 }
+
+# define dsa_input_type         "DSA"
 #endif
 
 /* ---------------------------------------------------------------------- */
 
 #ifndef OPENSSL_NO_EC
-# define ec_param_selection     OSSL_KEYMGMT_SELECT_ALL_PARAMETERS
-# define ec_pub_selection       (OSSL_KEYMGMT_SELECT_PUBLIC_KEY \
-                                 | ec_param_selection)
-# define ec_priv_selection      (OSSL_KEYMGMT_SELECT_KEYPAIR \
-                                 | ec_param_selection)
-
 static int ec_param_explicit_curve_to_text(BIO *out, const EC_GROUP *group,
                                            BN_CTX *ctx)
 {
@@ -552,14 +539,13 @@ err:
     OPENSSL_free(pub);
     return ret;
 }
+
+# define ec_input_type          "EC"
 #endif
 
 /* ---------------------------------------------------------------------- */
 
 #ifndef OPENSSL_NO_EC
-# define ecx_pub_selection      OSSL_KEYMGMT_SELECT_PUBLIC_KEY
-# define ecx_priv_selection     OSSL_KEYMGMT_SELECT_KEYPAIR
-
 static int ecx_to_text(BIO *out, const void *key, int selection)
 {
     const ECX_KEY *ecx = key;
@@ -624,16 +610,15 @@ static int ecx_to_text(BIO *out, const void *key, int selection)
 
     return 1;
 }
+
+# define ed25519_input_type     "ED25519"
+# define ed448_input_type       "ED448"
+# define x25519_input_type      "X25519"
+# define x448_input_type        "X448"
 #endif
 
 /* ---------------------------------------------------------------------- */
 
-#define rsa_param_selection     OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS
-#define rsa_pub_selection       (OSSL_KEYMGMT_SELECT_PUBLIC_KEY \
-                                 | rsa_param_selection)
-#define rsa_priv_selection      (OSSL_KEYMGMT_SELECT_KEYPAIR \
-                                 | rsa_param_selection)
-
 static int rsa_to_text(BIO *out, const void *key, int selection)
 {
     const RSA *rsa = key;
@@ -791,6 +776,9 @@ static int rsa_to_text(BIO *out, const void *key, int selection)
     return ret;
 }
 
+#define rsa_input_type          "RSA"
+#define rsapss_input_type       "RSA-PSS"
+
 /* ---------------------------------------------------------------------- */
 
 static void *key2text_newctx(void *provctx)
@@ -802,6 +790,31 @@ static void key2text_freectx(ossl_unused void *vctx)
 {
 }
 
+static const OSSL_PARAM *key2text_gettable_params(void *provctx)
+{
+    static const OSSL_PARAM gettables[] = {
+        { OSSL_ENCODER_PARAM_OUTPUT_TYPE, OSSL_PARAM_UTF8_PTR, NULL, 0, 0 },
+        OSSL_PARAM_END,
+    };
+
+    return gettables;
+}
+
+static int key2text_get_params(OSSL_PARAM params[], const char *input_type)
+{
+    OSSL_PARAM *p;
+
+    p = OSSL_PARAM_locate(params, OSSL_ENCODER_PARAM_INPUT_TYPE);
+    if (p != NULL && !OSSL_PARAM_set_utf8_ptr(p, input_type))
+        return 0;
+
+    p = OSSL_PARAM_locate(params, OSSL_ENCODER_PARAM_OUTPUT_TYPE);
+    if (p != NULL && !OSSL_PARAM_set_utf8_ptr(p, "TEXT"))
+        return 0;
+
+    return 1;
+}
+
 static int key2text_encode(void *vctx, const void *key, int selection,
                            OSSL_CORE_BIO *cout,
                            int (*key2text)(BIO *out, const void *key,
@@ -820,72 +833,75 @@ static int key2text_encode(void *vctx, const void *key, int selection,
     return ret;
 }
 
-#define MAKE_TEXT_ENCODER_KIND(impl, kind, type)                        \
-    static OSSL_FUNC_encoder_encode_data_fn                             \
-    impl##_##kind##2text_encode_d;                                      \
-    static OSSL_FUNC_encoder_encode_object_fn                           \
-    impl##_##kind##2text_encode_o;                                      \
-    static int                                                          \
-    impl##_##kind##2text_encode_d(void *ctx, const OSSL_PARAM params[], \
-                                  OSSL_CORE_BIO *cout,                  \
-                                  OSSL_PASSPHRASE_CALLBACK *cb,         \
-                                  void *cbarg)                          \
-    {                                                                   \
-        int selection = type##_##kind##_selection;                      \
-        void *key = ossl_prov_import_key(impl##_keymgmt_functions,      \
-                                         ctx, selection, params);       \
-        int ret;                                                        \
-                                                                        \
-        if (key == NULL)                                                \
-            return 0;                                                   \
+#define MAKE_TEXT_ENCODER(impl, type)                                   \
+    static OSSL_FUNC_encoder_get_params_fn                              \
+    impl##2text_get_params;                                             \
+    static OSSL_FUNC_encoder_import_object_fn                           \
+    impl##2text_import_object;                                          \
+    static OSSL_FUNC_encoder_free_object_fn                             \
+    impl##2text_free_object;                                            \
+    static OSSL_FUNC_encoder_encode_fn impl##2text_encode;              \
                                                                         \
-        ret = impl##_##kind##2text_encode_o(ctx, key, cout, cb, cbarg); \
+    static int impl##2text_get_params(OSSL_PARAM params[])              \
+    {                                                                   \
+        return key2text_get_params(params, impl##_input_type);           \
+    }                                                                   \
+    static void *impl##2text_import_object(void *ctx, int selection,    \
+                                           const OSSL_PARAM params[])   \
+    {                                                                   \
+        return ossl_prov_import_key(impl##_keymgmt_functions,           \
+                                    ctx, selection, params);            \
+    }                                                                   \
+    static void impl##2text_free_object(void *key)                      \
+    {                                                                   \
         ossl_prov_free_key(impl##_keymgmt_functions, key);              \
-        return ret;                                                     \
     }                                                                   \
-    static int                                                          \
-    impl##_##kind##2text_encode_o(void *vctx, const void *key,          \
-                                  OSSL_CORE_BIO *cout,                  \
+    static int impl##2text_encode(void *vctx, OSSL_CORE_BIO *cout,      \
+                                  const void *key,                      \
+                                  const OSSL_PARAM key_abstract[],      \
+                                  int selection,                        \
                                   OSSL_PASSPHRASE_CALLBACK *cb,         \
                                   void *cbarg)                          \
     {                                                                   \
-        int selection = type##_##kind##_selection;                      \
-                                                                        \
+        /* We don't deal with abstract objects */                       \
+        if (key_abstract != NULL) {                                     \
+            ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT);     \
+            return 0;                                                   \
+        }                                                               \
         return key2text_encode(vctx, key, selection, cout,              \
                                type##_to_text, cb, cbarg);              \
     }                                                                   \
-    const OSSL_DISPATCH impl##_##kind##_to_text_encoder_functions[] = { \
+    const OSSL_DISPATCH impl##_to_text_encoder_functions[] = {          \
         { OSSL_FUNC_ENCODER_NEWCTX,                                     \
           (void (*)(void))key2text_newctx },                            \
         { OSSL_FUNC_ENCODER_FREECTX,                                    \
           (void (*)(void))key2text_freectx },                           \
-        { OSSL_FUNC_ENCODER_ENCODE_DATA,                                \
-          (void (*)(void))impl##_##kind##2text_encode_d },              \
-        { OSSL_FUNC_ENCODER_ENCODE_OBJECT,                              \
-          (void (*)(void))impl##_##kind##2text_encode_o },              \
+        { OSSL_FUNC_ENCODER_GETTABLE_PARAMS,                            \
+          (void (*)(void))key2text_gettable_params },                   \
+        { OSSL_FUNC_ENCODER_GET_PARAMS,                                 \
+          (void (*)(void))impl##2text_get_params },                     \
+        { OSSL_FUNC_ENCODER_IMPORT_OBJECT,                              \
+          (void (*)(void))impl##2text_import_object },                  \
+        { OSSL_FUNC_ENCODER_FREE_OBJECT,                                \
+          (void (*)(void))impl##2text_free_object },                    \
+        { OSSL_FUNC_ENCODER_ENCODE,                                     \
+          (void (*)(void))impl##2text_encode },                         \
         { 0, NULL }                                                     \
     }
 
-#define MAKE_TEXT_ENCODER(impl, type)                                   \
-    MAKE_TEXT_ENCODER_KIND(impl, param, type);                          \
-    MAKE_TEXT_ENCODER_KIND(impl, pub, type);                            \
-    MAKE_TEXT_ENCODER_KIND(impl, priv, type)
-
-#define MAKE_TEXT_ENCODER_NOPARAM(impl, type)                           \
-    MAKE_TEXT_ENCODER_KIND(impl, pub, type);                            \
-    MAKE_TEXT_ENCODER_KIND(impl, priv, type)
-
 #ifndef OPENSSL_NO_DH
 MAKE_TEXT_ENCODER(dh, dh);
+MAKE_TEXT_ENCODER(dhx, dh);
 #endif
 #ifndef OPENSSL_NO_DSA
 MAKE_TEXT_ENCODER(dsa, dsa);
 #endif
 #ifndef OPENSSL_NO_EC
 MAKE_TEXT_ENCODER(ec, ec);
-MAKE_TEXT_ENCODER_NOPARAM(ed25519, ecx);
-MAKE_TEXT_ENCODER_NOPARAM(ed448, ecx);
-MAKE_TEXT_ENCODER_NOPARAM(x25519, ecx);
-MAKE_TEXT_ENCODER_NOPARAM(x448, ecx);
+MAKE_TEXT_ENCODER(ed25519, ecx);
+MAKE_TEXT_ENCODER(ed448, ecx);
+MAKE_TEXT_ENCODER(x25519, ecx);
+MAKE_TEXT_ENCODER(x448, ecx);
 #endif
-MAKE_TEXT_ENCODER_NOPARAM(rsa, rsa);
+MAKE_TEXT_ENCODER(rsa, rsa);
+MAKE_TEXT_ENCODER(rsapss, rsa);
diff --git a/providers/implementations/include/prov/implementations.h b/providers/implementations/include/prov/implementations.h
index 36a0d4b3d2..5c5f47f12e 100644
--- a/providers/implementations/include/prov/implementations.h
+++ b/providers/implementations/include/prov/implementations.h
@@ -308,70 +308,45 @@ extern const OSSL_DISPATCH rsa_asym_cipher_functions[];
 extern const OSSL_DISPATCH rsa_asym_kem_functions[];
 
 /* Encoders */
-extern const OSSL_DISPATCH rsa_priv_to_text_encoder_functions[];
-extern const OSSL_DISPATCH rsa_pub_to_text_encoder_functions[];
-extern const OSSL_DISPATCH rsa_priv_to_der_encoder_functions[];
-extern const OSSL_DISPATCH rsa_pub_to_der_encoder_functions[];
-extern const OSSL_DISPATCH rsa_priv_to_pem_encoder_functions[];
-extern const OSSL_DISPATCH rsa_pub_to_pem_encoder_functions[];
+extern const OSSL_DISPATCH rsa_to_der_encoder_functions[];
+extern const OSSL_DISPATCH rsa_to_pem_encoder_functions[];
+extern const OSSL_DISPATCH rsa_to_text_encoder_functions[];
 
-extern const OSSL_DISPATCH dh_priv_to_text_encoder_functions[];
-extern const OSSL_DISPATCH dh_pub_to_text_encoder_functions[];
-extern const OSSL_DISPATCH dh_param_to_text_encoder_functions[];
-extern const OSSL_DISPATCH dh_priv_to_der_encoder_functions[];
-extern const OSSL_DISPATCH dh_pub_to_der_encoder_functions[];
-extern const OSSL_DISPATCH dh_param_to_der_encoder_functions[];
-extern const OSSL_DISPATCH dh_priv_to_pem_encoder_functions[];
-extern const OSSL_DISPATCH dh_pub_to_pem_encoder_functions[];
-extern const OSSL_DISPATCH dh_param_to_pem_encoder_functions[];
+extern const OSSL_DISPATCH rsapss_to_der_encoder_functions[];
+extern const OSSL_DISPATCH rsapss_to_pem_encoder_functions[];
+extern const OSSL_DISPATCH rsapss_to_text_encoder_functions[];
 
-extern const OSSL_DISPATCH dsa_priv_to_text_encoder_functions[];
-extern const OSSL_DISPATCH dsa_pub_to_text_encoder_functions[];
-extern const OSSL_DISPATCH dsa_param_to_text_encoder_functions[];
-extern const OSSL_DISPATCH dsa_priv_to_der_encoder_functions[];
-extern const OSSL_DISPATCH dsa_pub_to_der_encoder_functions[];
-extern const OSSL_DISPATCH dsa_param_to_der_encoder_functions[];
-extern const OSSL_DISPATCH dsa_priv_to_pem_encoder_functions[];
-extern const OSSL_DISPATCH dsa_pub_to_pem_encoder_functions[];
-extern const OSSL_DISPATCH dsa_param_to_pem_encoder_functions[];
+extern const OSSL_DISPATCH dh_to_der_encoder_functions[];
+extern const OSSL_DISPATCH dh_to_pem_encoder_functions[];
+extern const OSSL_DISPATCH dh_to_text_encoder_functions[];
 
-extern const OSSL_DISPATCH x25519_priv_to_text_encoder_functions[];
-extern const OSSL_DISPATCH x25519_pub_to_text_encoder_functions[];
-extern const OSSL_DISPATCH x25519_priv_to_der_encoder_functions[];
-extern const OSSL_DISPATCH x25519_pub_to_der_encoder_functions[];
-extern const OSSL_DISPATCH x25519_priv_to_pem_encoder_functions[];
-extern const OSSL_DISPATCH x25519_pub_to_pem_encoder_functions[];
+extern const OSSL_DISPATCH dhx_to_der_encoder_functions[];
+extern const OSSL_DISPATCH dhx_to_pem_encoder_functions[];
+extern const OSSL_DISPATCH dhx_to_text_encoder_functions[];
 
-extern const OSSL_DISPATCH x448_priv_to_text_encoder_functions[];
-extern const OSSL_DISPATCH x448_pub_to_text_encoder_functions[];
-extern const OSSL_DISPATCH x448_priv_to_der_encoder_functions[];
-extern const OSSL_DISPATCH x448_pub_to_der_encoder_functions[];
-extern const OSSL_DISPATCH x448_priv_to_pem_encoder_functions[];
-extern const OSSL_DISPATCH x448_pub_to_pem_encoder_functions[];
+extern const OSSL_DISPATCH dsa_to_der_encoder_functions[];
+extern const OSSL_DISPATCH dsa_to_pem_encoder_functions[];
+extern const OSSL_DISPATCH dsa_to_text_encoder_functions[];
 
-extern const OSSL_DISPATCH ed25519_priv_to_text_encoder_functions[];
-extern const OSSL_DISPATCH ed25519_pub_to_text_encoder_functions[];
-extern const OSSL_DISPATCH ed25519_priv_to_der_encoder_functions[];
-extern const OSSL_DISPATCH ed25519_pub_to_der_encoder_functions[];
-extern const OSSL_DISPATCH ed25519_priv_to_pem_encoder_functions[];
-extern const OSSL_DISPATCH ed25519_pub_to_pem_encoder_functions[];
+extern const OSSL_DISPATCH x25519_to_der_encoder_functions[];
+extern const OSSL_DISPATCH x25519_to_pem_encoder_functions[];
+extern const OSSL_DISPATCH x25519_to_text_encoder_functions[];
 
-extern const OSSL_DISPATCH ed448_priv_to_text_encoder_functions[];
-extern const OSSL_DISPATCH ed448_pub_to_text_encoder_functions[];
-extern const OSSL_DISPATCH ed448_priv_to_der_encoder_functions[];
-extern const OSSL_DISPATCH ed448_pub_to_der_encoder_functions[];
-extern const OSSL_DISPATCH ed448_priv_to_pem_encoder_functions[];
-extern const OSSL_DISPATCH ed448_pub_to_pem_encoder_functions[];
+extern const OSSL_DISPATCH x448_to_der_encoder_functions[];
+extern const OSSL_DISPATCH x448_to_pem_encoder_functions[];
+extern const OSSL_DISPATCH x448_to_text_encoder_functions[];
 
-extern const OSSL_DISPATCH ec_priv_to_text_encoder_functions[];
-extern const OSSL_DISPATCH ec_pub_to_text_encoder_functions[];
-extern const OSSL_DISPATCH ec_param_to_text_encoder_functions[];
-extern const OSSL_DISPATCH ec_priv_to_der_encoder_functions[];
-extern const OSSL_DISPATCH ec_pub_to_der_encoder_functions[];
-extern const OSSL_DISPATCH ec_param_to_der_encoder_functions[];
-extern const OSSL_DISPATCH ec_priv_to_pem_encoder_functions[];
-extern const OSSL_DISPATCH ec_pub_to_pem_encoder_functions[];
-extern const OSSL_DISPATCH ec_param_to_pem_encoder_functions[];
+extern const OSSL_DISPATCH ed25519_to_der_encoder_functions[];
+extern const OSSL_DISPATCH ed25519_to_pem_encoder_functions[];
+extern const OSSL_DISPATCH ed25519_to_text_encoder_functions[];
+
+extern const OSSL_DISPATCH ed448_to_der_encoder_functions[];
+extern const OSSL_DISPATCH ed448_to_pem_encoder_functions[];
+extern const OSSL_DISPATCH ed448_to_text_encoder_functions[];
+
+extern const OSSL_DISPATCH ec_to_der_encoder_functions[];
+extern const OSSL_DISPATCH ec_to_pem_encoder_functions[];
+extern const OSSL_DISPATCH ec_to_text_encoder_functions[];
 
 /* Decoders */
 extern const OSSL_DISPATCH der_to_dh_decoder_functions[];
diff --git a/test/endecode_test.c b/test/endecode_test.c
index 580b7e8f35..7f37a99a6d 100644
--- a/test/endecode_test.c
+++ b/test/endecode_test.c
@@ -8,6 +8,7 @@
  */
 
 #include <string.h>
+#include <openssl/core_dispatch.h>
 #include <openssl/evp.h>
 #include <openssl/pem.h>
 #include <openssl/rsa.h>
@@ -88,11 +89,10 @@ static EVP_PKEY *make_key(const char *type, EVP_PKEY *template,
  * (TEST_strn2_eq(), for example).
  */
 
-typedef int (encoder)(void **encoded, long *encoded_len,
-                      void *object, const char *pass, const char *pcipher,
-                      const char *encoder_propq);
-typedef int (decoder)(void **object,
-                      void *encoded, long encoded_len,
+typedef int (encoder)(void **encoded, long *encoded_len, void *object,
+                      const char *output_type, int selection,
+                      const char *pass, const char *pcipher);
+typedef int (decoder)(void **object, void *encoded, long encoded_len,
                       const char *pass);
 typedef int (tester)(const void *data1, size_t data1_len,
                      const void *data2, size_t data2_len);
@@ -100,10 +100,11 @@ typedef int (checker)(const char *type, const void *data, size_t data_len);
 typedef void (dumper)(const char *label, const void *data, size_t data_len);
 
 static int test_encode_decode(const char *type, EVP_PKEY *pkey,
+                              const char *output_type, int selection,
                               const char *pass, const char *pcipher,
                               encoder *encode_cb, decoder *decode_cb,
                               tester *test_cb, checker *check_cb,
-                              dumper *dump_cb, const char *encoder_propq)
+                              dumper *dump_cb)
 {
     void *encoded = NULL;
     long encoded_len = 0;
@@ -112,12 +113,20 @@ static int test_encode_decode(const char *type, EVP_PKEY *pkey,
     long encoded2_len = 0;
     int ok = 0;
 
-    if (!encode_cb(&encoded, &encoded_len, pkey,
-                      pass, pcipher, encoder_propq)
+    /*
+     * Encode |pkey|, decode the result into |pkey2|, and finish off by
+     * encoding |pkey2| as well.  That last encoding is for checking and
+     * dumping purposes.
+     */
+    if (!encode_cb(&encoded, &encoded_len, pkey, output_type, selection,
+                   pass, pcipher)
         || !check_cb(type, encoded, encoded_len)
-        || !decode_cb((void **)&pkey2, encoded, encoded_len,
-                           pass)
-        || !TEST_int_eq(EVP_PKEY_eq(pkey, pkey2), 1))
+        || !decode_cb((void **)&pkey2, encoded, encoded_len, pass)
+        || !encode_cb(&encoded2, &encoded2_len, pkey2, output_type, selection,
+                      pass, pcipher))
+        goto end;
+
+    if (!TEST_int_eq(EVP_PKEY_eq(pkey, pkey2), 1))
         goto end;
 
     /*
@@ -126,19 +135,16 @@ static int test_encode_decode(const char *type, EVP_PKEY *pkey,
      * differ.
      */
     if ((pass == NULL && pcipher == NULL)
-        && (!encode_cb(&encoded2, &encoded2_len, pkey2,
-                          pass, pcipher, encoder_propq)
-            || !test_cb(encoded, encoded_len,
-                        encoded2, encoded2_len)))
+        && !test_cb(encoded, encoded_len, encoded2, encoded2_len))
         goto end;
 
     ok = 1;
  end:
     if (!ok) {
         if (encoded != NULL && encoded_len != 0)
-            dump_cb("encoded result", encoded, encoded_len);
+            dump_cb("|pkey| encoded", encoded, encoded_len);
         if (encoded2 != NULL && encoded2_len != 0)
-            dump_cb("re-encoded result", encoded2, encoded2_len);
+            dump_cb("|pkey2| encoded", encoded2, encoded2_len);
     }
 
     OPENSSL_free(encoded);
@@ -151,8 +157,8 @@ static int test_encode_decode(const char *type, EVP_PKEY *pkey,
 
 static int encode_EVP_PKEY_prov(void **encoded, long *encoded_len,
                                 void *object,
-                                const char *pass, const char *pcipher,
-                                const char *encoder_propq)
+                                const char *output_type, int selection,
+                                const char *pass, const char *pcipher)
 {
     EVP_PKEY *pkey = object;
     OSSL_ENCODER_CTX *ectx = NULL;
@@ -161,10 +167,12 @@ static int encode_EVP_PKEY_prov(void **encoded, long *encoded_len,
     const unsigned char *upass = (const unsigned char *)pass;
     int ok = 0;
 
-    if (!TEST_ptr(ectx = OSSL_ENCODER_CTX_new_by_EVP_PKEY(pkey, encoder_propq))
+    if (!TEST_ptr(ectx = OSSL_ENCODER_CTX_new_by_EVP_PKEY(pkey, output_type,
+                                                          selection,
+                                                          NULL, NULL))
         || (pass != NULL
             && !TEST_true(OSSL_ENCODER_CTX_set_passphrase(ectx, upass,
-                                                             strlen(pass))))
+                                                          strlen(pass))))
         || (pcipher != NULL
             && !TEST_true(OSSL_ENCODER_CTX_set_cipher(ectx, pcipher, NULL)))
         || !TEST_ptr(mem_ser = BIO_new(BIO_s_mem()))
@@ -184,8 +192,7 @@ static int encode_EVP_PKEY_prov(void **encoded, long *encoded_len,
     return ok;
 }
 
-static int decode_EVP_PKEY_prov(void **object,
-                                void *encoded, long encoded_len,
+static int decode_EVP_PKEY_prov(void **object, void *encoded, long encoded_len,
                                 const char *pass)
 {
     EVP_PKEY *pkey = NULL;
@@ -195,10 +202,9 @@ static int decode_EVP_PKEY_prov(void **object,
     int ok = 0;
 
     if (!TEST_ptr(dctx = OSSL_DECODER_CTX_new_by_EVP_PKEY(&pkey, NULL,
-                                                               NULL, NULL))
+                                                          NULL, NULL))
         || (pass != NULL
-            && !OSSL_DECODER_CTX_set_passphrase(dctx, upass,
-                                                     strlen(pass)))
+            && !OSSL_DECODER_CTX_set_passphrase(dctx, upass, strlen(pass)))
         || !TEST_ptr(mem_deser = BIO_new_mem_buf(encoded, encoded_len))
         || !TEST_true(OSSL_DECODER_from_bio(dctx, mem_deser)))
         goto end;
@@ -210,11 +216,10 @@ static int decode_EVP_PKEY_prov(void **object,
     return ok;
 }
 
-static int encode_EVP_PKEY_legacy_PEM(void **encoded,
-                                      long *encoded_len,
+static int encode_EVP_PKEY_legacy_PEM(void **encoded, long *encoded_len,
                                       void *object,
-                                      const char *pass, const char *pcipher,
-                                      ossl_unused const char *encoder_propq)
+                                      const char *output_type, int selection,
+                                      const char *pass, const char *pcipher)
 {
     EVP_PKEY *pkey = object;
     EVP_CIPHER *cipher = NULL;
@@ -252,9 +257,10 @@ static int encode_EVP_PKEY_legacy_PEM(void **encoded,
 #ifndef OPENSSL_NO_DSA
 static int encode_EVP_PKEY_MSBLOB(void **encoded, long *encoded_len,
                                   void *object,
+                                  ossl_unused const char *output_type,
+                                  ossl_unused int selection,
                                   ossl_unused const char *pass,
-                                  ossl_unused const char *pcipher,
-                                  ossl_unused const char *encoder_propq)
+                                  ossl_unused const char *pcipher)
 {
     EVP_PKEY *pkey = object;
     BIO *mem_ser = NULL;
@@ -277,11 +283,13 @@ static int encode_EVP_PKEY_MSBLOB(void **encoded, long *encoded_len,
     return ok;
 }
 
-static int encode_public_EVP_PKEY_MSBLOB(void **encoded, long *encoded_len,
+static int encode_public_EVP_PKEY_MSBLOB(void **encoded,
+                                         long *encoded_len,
                                          void *object,
+                                         ossl_unused const char *output_type,
+                                         ossl_unused int selection,
                                          ossl_unused const char *pass,
-                                         ossl_unused const char *pcipher,
-                                         ossl_unused const char *encoder_propq)
+                                         ossl_unused const char *pcipher)
 {
     EVP_PKEY *pkey = object;
     BIO *mem_ser = NULL;
@@ -314,9 +322,10 @@ static int pass_pw(char *buf, int size, int rwflag, void *userdata)
 
 static int encode_EVP_PKEY_PVK(void **encoded, long *encoded_len,
                                void *object,
+                               ossl_unused const char *output_type,
+                               ossl_unused int selection,
                                const char *pass,
-                               ossl_unused const char *pcipher,
-                               ossl_unused const char *encoder_propq)
+                               ossl_unused const char *pcipher)
 {
     EVP_PKEY *pkey = object;
     BIO *mem_ser = NULL;
@@ -357,6 +366,29 @@ static int test_mem(const void *data1, size_t data1_len,
 
 /* Test cases and their dumpers / checkers */
 
+static void collect_name(const char *name, void *arg)
+{
+    char **namelist = arg;
+    char *new_namelist;
+    size_t space;
+
+    space = strlen(name);
+    if (*namelist != NULL)
+        space += strlen(*namelist) + 2 /* for comma and space */;
+    space++; /* for terminating null byte */
+
+    new_namelist = OPENSSL_realloc(*namelist, space);
+    if (new_namelist == NULL)
+        return;
+    if (*namelist != NULL) {
+        strcat(new_namelist, ", ");
+        strcat(new_namelist, name);
+    } else {
+        strcpy(new_namelist, name);
+    }
+    *namelist = new_namelist;
+}
+
 static void dump_der(const char *label, const void *data, size_t data_len)
 {
     test_output_memory(label, data, data_len);
@@ -377,9 +409,17 @@ static int check_unprotected_PKCS8_DER(const char *type,
 
     if (TEST_ptr(p8inf)) {
         EVP_PKEY *pkey = EVP_PKCS82PKEY(p8inf);
-
-        ok = (TEST_ptr(pkey) && TEST_true(EVP_PKEY_is_a(pkey, type)));
-        EVP_PKEY_free(pkey);
+        char *namelist = NULL;
+
+        if (TEST_ptr(pkey)) {
+            if (!(ok = TEST_true(EVP_PKEY_is_a(pkey, type)))) {
+                EVP_PKEY_typenames_do_all(pkey, collect_name, &namelist);
+                if (namelist != NULL)
+                    TEST_note("%s isn't any of %s", type, namelist);
+                OPENSSL_free(namelist);
+            }
+            EVP_PKEY_free(pkey);
+        }
     }
     PKCS8_PRIV_KEY_INFO_free(p8inf);
     return ok;
@@ -387,45 +427,57 @@ static int check_unprotected_PKCS8_DER(const char *type,
 
 static int test_unprotected_via_DER(const char *type, EVP_PKEY *key)
 {
-    return test_encode_decode(type, key, NULL, NULL,
+    return test_encode_decode(type, key,
+                              "DER", OSSL_KEYMGMT_SELECT_KEYPAIR
+                              | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
+                              NULL, NULL,
                               encode_EVP_PKEY_prov, decode_EVP_PKEY_prov,
-                              test_mem, check_unprotected_PKCS8_DER, dump_der,
-                              OSSL_ENCODER_PrivateKey_TO_DER_PQ);
+                              test_mem, check_unprotected_PKCS8_DER,
+                              dump_der);
 }
 
 static int check_unprotected_PKCS8_PEM(const char *type,
                                        const void *data, size_t data_len)
 {
-    static const char pem_header[] = "-----BEGIN " PEM_STRING_PKCS8INF "-----";
+    static const char expected_pem_header[] =
+        "-----BEGIN " PEM_STRING_PKCS8INF "-----";
 
-    return TEST_strn_eq(data, pem_header, sizeof(pem_header) - 1);
+    return TEST_strn_eq(data, expected_pem_header,
+                        sizeof(expected_pem_header) - 1);
 }
 
 static int test_unprotected_via_PEM(const char *type, EVP_PKEY *key)
 {
-    return test_encode_decode(type, key, NULL, NULL,
+    return test_encode_decode(type, key,
+                              "PEM", OSSL_KEYMGMT_SELECT_KEYPAIR
+                              | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
+                              NULL, NULL,
                               encode_EVP_PKEY_prov, decode_EVP_PKEY_prov,
-                              test_text, check_unprotected_PKCS8_PEM, dump_pem,
-                              OSSL_ENCODER_PrivateKey_TO_PEM_PQ);
+                              test_text, check_unprotected_PKCS8_PEM,
+                              dump_pem);
 }
 
 static int check_unprotected_legacy_PEM(const char *type,
                                         const void *data, size_t data_len)
 {
-    static char pem_header[80];
+    static char expected_pem_header[80];
 
     return
-        TEST_int_gt(BIO_snprintf(pem_header, sizeof(pem_header),
+        TEST_int_gt(BIO_snprintf(expected_pem_header,
+                                 sizeof(expected_pem_header),
                                  "-----BEGIN %s PRIVATE KEY-----", type), 0)
-        && TEST_strn_eq(data, pem_header, strlen(pem_header));
+        && TEST_strn_eq(data, expected_pem_header, strlen(expected_pem_header));
 }
 
 static int test_unprotected_via_legacy_PEM(const char *type, EVP_PKEY *key)
 {
-    return test_encode_decode(type, key, NULL, NULL,
+    return test_encode_decode(type, key,
+                              "PEM", OSSL_KEYMGMT_SELECT_KEYPAIR
+                              | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
+                              NULL, NULL,
                               encode_EVP_PKEY_legacy_PEM, decode_EVP_PKEY_prov,
-                              test_text, check_unprotected_legacy_PEM, dump_pem,
-                              NULL);
+                              test_text, check_unprotected_legacy_PEM,
+                              dump_pem);
 }
 
 #ifndef OPENSSL_NO_DSA
@@ -441,10 +493,13 @@ static int check_MSBLOB(const char *type, const void *data, size_t data_len)
 
 static int test_unprotected_via_MSBLOB(const char *type, EVP_PKEY *key)
 {
-    return test_encode_decode(type, key, NULL, NULL,
+    return test_encode_decode(type, key,
+                              "MSBLOB", OSSL_KEYMGMT_SELECT_KEYPAIR
+                              | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
+                              NULL, NULL,
                               encode_EVP_PKEY_MSBLOB, decode_EVP_PKEY_prov,
-                              test_mem, check_MSBLOB, dump_der,
-                              NULL);
+                              test_mem, check_MSBLOB,
+                              dump_der);
 }
 
 # ifndef OPENSSL_NO_RC4
@@ -459,10 +514,13 @@ static int check_PVK(const char *type, const void *data, size_t data_len)
 
 static int test_unprotected_via_PVK(const char *type, EVP_PKEY *key)
 {
-    return test_encode_decode(type, key, NULL, NULL,
+    return test_encode_decode(type, key,
+                              "PVK", OSSL_KEYMGMT_SELECT_KEYPAIR
+                              | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
+                              NULL, NULL,
                               encode_EVP_PKEY_PVK, decode_EVP_PKEY_prov,
-                              test_mem, check_PVK, dump_der,
-                              NULL);
+                              test_mem, check_PVK,
+                              dump_der);
 }
 # endif
 #endif
@@ -483,55 +541,69 @@ static int check_protected_PKCS8_DER(const char *type,
 
 static int test_protected_via_DER(const char *type, EVP_PKEY *key)
 {
-    return test_encode_decode(type, key, pass, pass_cipher,
+    return test_encode_decode(type, key,
+                              "DER", OSSL_KEYMGMT_SELECT_KEYPAIR
+                              | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
+                              pass, pass_cipher,
                               encode_EVP_PKEY_prov, decode_EVP_PKEY_prov,
-                              test_mem, check_protected_PKCS8_DER, dump_der,
-                              OSSL_ENCODER_PrivateKey_TO_DER_PQ);
+                              test_mem, check_protected_PKCS8_DER,
+                              dump_der);
 }
 
 static int check_protected_PKCS8_PEM(const char *type,
                                      const void *data, size_t data_len)
 {
-    static const char pem_header[] = "-----BEGIN " PEM_STRING_PKCS8 "-----";
+    static const char expected_pem_header[] =
+        "-----BEGIN " PEM_STRING_PKCS8 "-----";
 
-    return TEST_strn_eq(data, pem_header, sizeof(pem_header) - 1);
+    return TEST_strn_eq(data, expected_pem_header,
+                        sizeof(expected_pem_header) - 1);
 }
 
 static int test_protected_via_PEM(const char *type, EVP_PKEY *key)
 {
-    return test_encode_decode(type, key, pass, pass_cipher,
+    return test_encode_decode(type, key,
+                              "PEM", OSSL_KEYMGMT_SELECT_KEYPAIR
+                              | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
+                              pass, pass_cipher,
                               encode_EVP_PKEY_prov, decode_EVP_PKEY_prov,
-                              test_text, check_protected_PKCS8_PEM, dump_pem,
-                              OSSL_ENCODER_PrivateKey_TO_PEM_PQ);
+                              test_text, check_protected_PKCS8_PEM,
+                              dump_pem);
 }
 
 static int check_protected_legacy_PEM(const char *type,
                                       const void *data, size_t data_len)
 {
-    static char pem_header[80];
+    static char expected_pem_header[80];
 
     return
-        TEST_int_gt(BIO_snprintf(pem_header, sizeof(pem_header),
+        TEST_int_gt(BIO_snprintf(expected_pem_header,
+                                 sizeof(expected_pem_header),
                                  "-----BEGIN %s PRIVATE KEY-----", type), 0)
-        && TEST_strn_eq(data, pem_header, strlen(pem_header))
+        && TEST_strn_eq(data, expected_pem_header, strlen(expected_pem_header))
         && TEST_ptr(strstr(data, "\nDEK-Info: "));
 }
 
 static int test_protected_via_legacy_PEM(const char *type, EVP_PKEY *key)
 {
-    return test_encode_decode(type, key, pass, pass_cipher,
+    return test_encode_decode(type, key,
+                              "PEM", OSSL_KEYMGMT_SELECT_KEYPAIR
+                              | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
+                              pass, pass_cipher,
                               encode_EVP_PKEY_legacy_PEM, decode_EVP_PKEY_prov,
-                              test_text, check_protected_legacy_PEM, dump_pem,
-                              NULL);
+                              test_text, check_protected_legacy_PEM,
+                              dump_pem);
 }
 
 #if !defined(OPENSSL_NO_DSA) && !defined(OPENSSL_NO_RC4)
 static int test_protected_via_PVK(const char *type, EVP_PKEY *key)
 {
-    return test_encode_decode(type, key, pass, NULL,
+    return test_encode_decode(type, key,
+                              "PVK", OSSL_KEYMGMT_SELECT_KEYPAIR
+                              | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
+                              pass, NULL,
                               encode_EVP_PKEY_PVK, decode_EVP_PKEY_prov,
-                              test_mem, check_PVK, dump_der,
-                              NULL);
+                              test_mem, check_PVK, dump_der);
 }
 #endif
 
@@ -547,26 +619,32 @@ static int check_public_DER(const char *type, const void *data, size_t data_len)
 
 static int test_public_via_DER(const char *type, EVP_PKEY *key)
 {
-    return test_encode_decode(type, key, NULL, NULL,
+    return test_encode_decode(type, key,
+                              "DER", OSSL_KEYMGMT_SELECT_PUBLIC_KEY
+                              | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
+                              NULL, NULL,
                               encode_EVP_PKEY_prov, decode_EVP_PKEY_prov,
-                              test_mem, check_public_DER, dump_der,
-                              OSSL_ENCODER_PUBKEY_TO_DER_PQ);
+                              test_mem, check_public_DER, dump_der);
 }
 
 static int check_public_PEM(const char *type, const void *data, size_t data_len)
 {
-    static const char pem_header[] = "-----BEGIN " PEM_STRING_PUBLIC "-----";
+    static const char expected_pem_header[] =
+        "-----BEGIN " PEM_STRING_PUBLIC "-----";
 
     return
-        TEST_strn_eq(data, pem_header, sizeof(pem_header) - 1);
+        TEST_strn_eq(data, expected_pem_header,
+                     sizeof(expected_pem_header) - 1);
 }
 
 static int test_public_via_PEM(const char *type, EVP_PKEY *key)
 {
-    return test_encode_decode(type, key, NULL, NULL,
+    return test_encode_decode(type, key,
+                              "PEM", OSSL_KEYMGMT_SELECT_PUBLIC_KEY
+                              | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
+                              NULL, NULL,
                               encode_EVP_PKEY_prov, decode_EVP_PKEY_prov,
-                              test_text, check_public_PEM, dump_pem,
-                              OSSL_ENCODER_PUBKEY_TO_PEM_PQ);
+                              test_text, check_public_PEM, dump_pem);
 }
 
 #ifndef OPENSSL_NO_DSA
@@ -583,11 +661,13 @@ static int check_public_MSBLOB(const char *type,
 
 static int test_public_via_MSBLOB(const char *type, EVP_PKEY *key)
 {
-    return test_encode_decode(type, key, NULL, NULL,
+    return test_encode_decode(type, key,
+                              "MSBLOB", OSSL_KEYMGMT_SELECT_PUBLIC_KEY
+                              | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
+                              NULL, NULL,
                               encode_public_EVP_PKEY_MSBLOB,
                               decode_EVP_PKEY_prov,
-                              test_mem, check_public_MSBLOB, dump_der,
-                              NULL);
+                              test_mem, check_public_MSBLOB, dump_der);
 }
 #endif
 
diff --git a/test/evp_pkey_provided_test.c b/test/evp_pkey_provided_test.c
index 3678dc0584..33f3915222 100644
--- a/test/evp_pkey_provided_test.c
+++ b/test/evp_pkey_provided_test.c
@@ -153,36 +153,49 @@ static int test_print_key_using_pem(const char *alg, const EVP_PKEY *pk)
 }
 
 static int test_print_key_type_using_encoder(const char *alg, int type,
-                                                const EVP_PKEY *pk)
+                                             const EVP_PKEY *pk)
 {
-    const char *pq;
+    const char *output_type;
+    int selection;
     OSSL_ENCODER_CTX *ctx = NULL;
     BIO *membio = BIO_new(BIO_s_mem());
     int ret = 0;
 
     switch (type) {
     case PRIV_TEXT:
-        pq = OSSL_ENCODER_PrivateKey_TO_TEXT_PQ;
+        output_type = "TEXT";
+        selection = OSSL_KEYMGMT_SELECT_KEYPAIR
+            | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS;
         break;
 
     case PRIV_PEM:
-        pq = OSSL_ENCODER_PrivateKey_TO_PEM_PQ;
+        output_type = "PEM";
+        selection = OSSL_KEYMGMT_SELECT_KEYPAIR
+            | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS;
         break;
 
     case PRIV_DER:
-        pq = OSSL_ENCODER_PrivateKey_TO_DER_PQ;
+        output_type = "DER";
+        selection = OSSL_KEYMGMT_SELECT_KEYPAIR
+            | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS;
         break;
 
     case PUB_TEXT:
-        pq = OSSL_ENCODER_PUBKEY_TO_TEXT_PQ;
+        output_type = "TEXT";
+        selection = OSSL_KEYMGMT_SELECT_PUBLIC_KEY
+            | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS;
         break;
 
     case PUB_PEM:
-        pq = OSSL_ENCODER_PUBKEY_TO_PEM_PQ;
+        output_type = "PEM";
+        selection = OSSL_KEYMGMT_SELECT_PUBLIC_KEY
+            | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS;
         break;
 
     case PUB_DER:
-        pq = OSSL_ENCODER_PUBKEY_TO_DER_PQ;
+        output_type = "DER";
+        selection = OSSL_KEYMGMT_SELECT_PUBLIC_KEY
+            | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS;
         break;
 
     default:
@@ -195,9 +208,11 @@ static int test_print_key_type_using_encoder(const char *alg, int type,
 
     /* Make a context, it's valid for several prints */
     TEST_note("Setting up a OSSL_ENCODER context with passphrase");
-    if (!TEST_ptr(ctx = OSSL_ENCODER_CTX_new_by_EVP_PKEY(pk, pq))
+    if (!TEST_ptr(ctx = OSSL_ENCODER_CTX_new_by_EVP_PKEY(pk, output_type,
+                                                         selection,
+                                                         NULL, NULL))
         /* Check that this operation is supported */
-        || !TEST_ptr(OSSL_ENCODER_CTX_get_encoder(ctx)))
+        || !TEST_int_ne(OSSL_ENCODER_CTX_get_num_encoders(ctx), 0))
         goto err;
 
     /* Use no cipher.  This should give us an unencrypted PEM */
diff --git a/util/find-doc-nits b/util/find-doc-nits
index ac661827dd..d614844952 100755
--- a/util/find-doc-nits
+++ b/util/find-doc-nits
@@ -104,6 +104,9 @@ my $ignored = qr/(?| ^i2d_
                  |   ^DEFINE_LHASH_OF_INTERNAL
                  )/x;
 
+# A common regexp for C symbol names
+my $C_symbol = qr/\b[[:alpha:]][_[:alnum:]]*\b/;
+
 # Collect all POD files, both internal and public, and regardless of location
 # We collect them in a hash table with each file being a key, so we can attach
 # tags to them.  For example, internal docs will have the word "internal"
@@ -367,27 +370,27 @@ sub name_synopsis {
         if ( $line =~ /env (\S*)=/ ) {
             # environment variable env NAME=...
             $sym = $1;
-        } elsif ( $line =~ /typedef.*\(\*?(\S+)\)\s*\(/ ) {
+        } elsif ( $line =~ /typedef.*\(\*?($C_symbol)\)\s*\(/ ) {
             # a callback function pointer: typedef ... (*NAME)(...
             # a callback function signature: typedef ... (NAME)(...
             $sym = $1;
-        } elsif ( $line =~ /typedef.* (\S+)\s*\(/ ) {
+        } elsif ( $line =~ /typedef.*($C_symbol)\s*\(/ ) {
             # a callback function signature: typedef ... NAME(...
             $sym = $1;
-        } elsif ( $line =~ /typedef.* (\S+);/ ) {
+        } elsif ( $line =~ /typedef.*($C_symbol);/ ) {
             # a simple typedef: typedef ... NAME;
             $is_prototype = 0;
             $sym = $1;
-        } elsif ( $line =~ /enum (\S*) \{/ ) {
+        } elsif ( $line =~ /enum ($C_symbol) \{/ ) {
             # an enumeration: enum ... {
             $sym = $1;
-        } elsif ( $line =~ /#\s*(?:define|undef) ([A-Za-z0-9_]+)/ ) {
+        } elsif ( $line =~ /#\s*(?:define|undef) ($C_symbol)/ ) {
             $is_prototype = 0;
             $sym = $1;
-        } elsif ( $line =~ /^[^\(]*?\(\*([A-Za-z0-9_]+)\s*\(/ ) {
+        } elsif ( $line =~ /^[^\(]*?\(\*($C_symbol)\s*\(/ ) {
             # a function returning a function pointer: TYPE (*NAME(args))(args)
             $sym = $1;
-        } elsif ( $line =~ /^[^\(]*?([A-Za-z0-9_]+)\s*\(/ ) {
+        } elsif ( $line =~ /^[^\(]*?($C_symbol)\s*\(/ ) {
             # a simple function declaration
             $sym = $1;
         }
diff --git a/util/libcrypto.num b/util/libcrypto.num
index 96fd637ba0..e414a6443b 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -4865,7 +4865,6 @@ OSSL_ENCODER_do_all_provided            ?	3_0_0	EXIST::FUNCTION:
 OSSL_ENCODER_names_do_all               ?	3_0_0	EXIST::FUNCTION:
 OSSL_ENCODER_settable_ctx_params        ?	3_0_0	EXIST::FUNCTION:
 OSSL_ENCODER_CTX_new                    ?	3_0_0	EXIST::FUNCTION:
-OSSL_ENCODER_CTX_get_encoder            ?	3_0_0	EXIST::FUNCTION:
 OSSL_ENCODER_CTX_set_params             ?	3_0_0	EXIST::FUNCTION:
 OSSL_ENCODER_CTX_free                   ?	3_0_0	EXIST::FUNCTION:
 OSSL_ENCODER_properties                 ?	3_0_0	EXIST::FUNCTION:
@@ -4874,7 +4873,7 @@ OSSL_ENCODER_to_fp                      ?	3_0_0	EXIST::FUNCTION:STDIO
 OSSL_ENCODER_CTX_new_by_EVP_PKEY        ?	3_0_0	EXIST::FUNCTION:
 OSSL_ENCODER_CTX_set_cipher             ?	3_0_0	EXIST::FUNCTION:
 OSSL_ENCODER_CTX_set_passphrase         ?	3_0_0	EXIST::FUNCTION:
-OSSL_ENCODER_CTX_set_passphrase_cb      ?	3_0_0	EXIST::FUNCTION:
+OSSL_ENCODER_CTX_set_pem_password_cb    ?	3_0_0	EXIST::FUNCTION:
 OSSL_ENCODER_CTX_set_passphrase_ui      ?	3_0_0	EXIST::FUNCTION:
 ERR_load_OSSL_ENCODER_strings           ?	3_0_0	EXIST::FUNCTION:
 PEM_read_X509_PUBKEY                    ?	3_0_0	EXIST::FUNCTION:STDIO
@@ -5155,11 +5154,11 @@ OSSL_DECODER_from_bio                   ?	3_0_0	EXIST::FUNCTION:
 OSSL_DECODER_from_fp                    ?	3_0_0	EXIST::FUNCTION:STDIO
 OSSL_DECODER_CTX_add_decoder            ?	3_0_0	EXIST::FUNCTION:
 OSSL_DECODER_CTX_add_extra              ?	3_0_0	EXIST::FUNCTION:
-OSSL_DECODER_CTX_num_decoders           ?	3_0_0	EXIST::FUNCTION:
+OSSL_DECODER_CTX_get_num_decoders       ?	3_0_0	EXIST::FUNCTION:
 OSSL_DECODER_CTX_set_input_type         ?	3_0_0	EXIST::FUNCTION:
 OSSL_DECODER_export                     ?	3_0_0	EXIST::FUNCTION:
-OSSL_DECODER_INSTANCE_decoder           ?	3_0_0	EXIST::FUNCTION:
-OSSL_DECODER_INSTANCE_decoder_ctx       ?	3_0_0	EXIST::FUNCTION:
+OSSL_DECODER_INSTANCE_get_decoder       ?	3_0_0	EXIST::FUNCTION:
+OSSL_DECODER_INSTANCE_get_decoder_ctx   ?	3_0_0	EXIST::FUNCTION:
 ERR_load_OSSL_DECODER_strings           ?	3_0_0	EXIST::FUNCTION:
 OSSL_DECODER_gettable_params            ?	3_0_0	EXIST::FUNCTION:
 OSSL_DECODER_get_params                 ?	3_0_0	EXIST::FUNCTION:
@@ -5296,3 +5295,21 @@ EVP_PKEY_encapsulate                    ?	3_0_0	EXIST::FUNCTION:
 EVP_PKEY_decapsulate_init               ?	3_0_0	EXIST::FUNCTION:
 EVP_PKEY_decapsulate                    ?	3_0_0	EXIST::FUNCTION:
 EVP_PKEY_CTX_set_kem_op                 ?	3_0_0	EXIST::FUNCTION:
+OSSL_ENCODER_gettable_params            ?	3_0_0	EXIST::FUNCTION:
+OSSL_ENCODER_get_params                 ?	3_0_0	EXIST::FUNCTION:
+OSSL_ENCODER_CTX_set_output_type        ?	3_0_0	EXIST::FUNCTION:
+OSSL_ENCODER_CTX_add_encoder            ?	3_0_0	EXIST::FUNCTION:
+OSSL_ENCODER_CTX_add_extra              ?	3_0_0	EXIST::FUNCTION:
+OSSL_ENCODER_CTX_get_num_encoders       ?	3_0_0	EXIST::FUNCTION:
+OSSL_ENCODER_CTX_set_selection          ?	3_0_0	EXIST::FUNCTION:
+OSSL_ENCODER_INSTANCE_get_encoder       ?	3_0_0	EXIST::FUNCTION:
+OSSL_ENCODER_INSTANCE_get_encoder_ctx   ?	3_0_0	EXIST::FUNCTION:
+OSSL_ENCODER_INSTANCE_get_input_type    ?	3_0_0	EXIST::FUNCTION:
+OSSL_ENCODER_INSTANCE_get_output_type   ?	3_0_0	EXIST::FUNCTION:
+OSSL_ENCODER_CTX_set_construct          ?	3_0_0	EXIST::FUNCTION:
+OSSL_ENCODER_CTX_set_construct_data     ?	3_0_0	EXIST::FUNCTION:
+OSSL_ENCODER_CTX_set_cleanup            ?	3_0_0	EXIST::FUNCTION:
+OSSL_DECODER_INSTANCE_get_input_type    ?	3_0_0	EXIST::FUNCTION:
+OSSL_ENCODER_CTX_set_passphrase_cb      ?	3_0_0	EXIST::FUNCTION:
+EVP_PKEY_typenames_do_all               ?	3_0_0	EXIST::FUNCTION:
+OSSL_DECODER_INSTANCE_get_input_type    ?	3_0_0	EXIST::FUNCTION:
diff --git a/util/other.syms b/util/other.syms
index 0925d726d8..6c7ec4c9ca 100644
--- a/util/other.syms
+++ b/util/other.syms
@@ -46,12 +46,14 @@ OSSL_DECODER_CTX                        datatype
 OSSL_DECODER_CONSTRUCT                  datatype
 OSSL_DECODER_CLEANUP                    datatype
 OSSL_DECODER_INSTANCE                   datatype
-OSSL_DECODER_CTX                        datatype
 OSSL_HTTP_bio_cb_t                      datatype
 OSSL_PARAM                              datatype
 OSSL_PROVIDER                           datatype
 OSSL_ENCODER                            datatype
 OSSL_ENCODER_CTX                        datatype
+OSSL_ENCODER_CONSTRUCT                  datatype
+OSSL_ENCODER_CLEANUP                    datatype
+OSSL_ENCODER_INSTANCE                   datatype
 OSSL_STORE_CTX                          datatype
 OSSL_STORE_INFO                         datatype
 OSSL_STORE_LOADER                       datatype


More information about the openssl-commits mailing list