[openssl] master update

Richard Levitte levitte at openssl.org
Fri Jul 24 15:00:10 UTC 2020


The branch master has been updated
       via  a57fc73063bee3fb787e583f5778433ef29d58eb (commit)
       via  e2ac846eff6856136d67c46751b2b8ca16a5b575 (commit)
       via  436623f89f01a40c12327a67af0885a6219338b4 (commit)
       via  3ecbea6a0999cdd7caaac2871e1d198294dc494a (commit)
       via  38b14f474722ac2ace20d3b63b933b9b9cd3bbe1 (commit)
       via  7524b7b748d5989f015bc4b9651be92dbcb375fd (commit)
       via  45396db0e3bfd796e89669baf3a3ecc9602d36d5 (commit)
       via  5a23d78c9b141e31ab9b7d551b2125b124a75e49 (commit)
       via  dcfacbbfe9b3f8fa13eeb17a8fa4c89edefc8389 (commit)
       via  1017b8e4a161682c909a98ebf3f7a21b38d6c677 (commit)
       via  853ca12813dee0ec7ac75cfe5f1c9685ffb2d420 (commit)
       via  072a9fde7d67a621ebd2c8d1ba22ab6e17da5a88 (commit)
       via  c3e4c1f325e6fc829a5b00a19a6019249cac781a (commit)
       via  5dacb38ccefd45d832c9710b4dd0121fdcbdac72 (commit)
       via  af836c22cede6bd89c0b35c13d17e95f1854c5d0 (commit)
      from  6725682d77510bf6d499957897d7be124d603f40 (commit)


- Log -----------------------------------------------------------------
commit a57fc73063bee3fb787e583f5778433ef29d58eb
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Jul 20 10:50:04 2020 +0200

    EVP: Fix key type check logic in evp_pkey_cmp_any()
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12410)

commit e2ac846eff6856136d67c46751b2b8ca16a5b575
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Jul 20 10:47:59 2020 +0200

    TEST: Update the serialization/deserialization test with legacy PEM encryption
    
    This adds legacy PEM variants of already existing tests.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12410)

commit 436623f89f01a40c12327a67af0885a6219338b4
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Jul 20 10:46:49 2020 +0200

    PROV: Update the PEM to DER deserializer to handle encrypted legacy PEM
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12410)

commit 3ecbea6a0999cdd7caaac2871e1d198294dc494a
Author: Richard Levitte <levitte at openssl.org>
Date:   Fri Jul 10 15:28:05 2020 +0200

    TEST: Update the serialization/deserialization test with encryption
    
    This adds variants of already existing tests, but where the object
    is encrypted / decrypted along the way as well.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12410)

commit 38b14f474722ac2ace20d3b63b933b9b9cd3bbe1
Author: Richard Levitte <levitte at openssl.org>
Date:   Fri Jul 10 15:25:15 2020 +0200

    PROV: Update the DER to RSA deserializer to handle encrypted PKCS#8
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12410)

commit 7524b7b748d5989f015bc4b9651be92dbcb375fd
Author: Richard Levitte <levitte at openssl.org>
Date:   Fri Jul 10 15:13:55 2020 +0200

    DESERIALIZER: Implement decryption of password protected objects
    
    This implements these functions:
    
    OSSL_DESERIALIZER_CTX_set_cipher()
    OSSL_DESERIALIZER_CTX_set_passphrase()
    OSSL_DESERIALIZER_CTX_set_passphrase_ui()
    OSSL_DESERIALIZER_CTX_set_passphrase_cb()
    
    To be able to deal with multiple deserializers trying to work on the
    same byte array and wanting to decrypt it while doing so, the
    deserializer caches the passphrase.  This cache is cleared at the end
    of OSSL_DESERIALIZER_from_bio().
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12410)

commit 45396db0e3bfd796e89669baf3a3ecc9602d36d5
Author: Richard Levitte <levitte at openssl.org>
Date:   Fri Jul 10 15:08:29 2020 +0200

    SERIALIZER: No enc argument for OSSL_SERIALIZER_CTX_set_passphrase_cb()
    
    Serialization will only encrypt, so there's no point telling
    OSSL_SERIALIZER_CTX_set_passphrase_cb() that's going to happen.
    
    We fix the declaration of OSSL_DESERIALIZER_CTX_set_passphrase_cb()
    the same way.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12410)

commit 5a23d78c9b141e31ab9b7d551b2125b124a75e49
Author: Richard Levitte <levitte at openssl.org>
Date:   Thu Jul 9 19:10:39 2020 +0200

    TEST: Add new serializer and deserializer test
    
    This test revolves around a central function that will first serialize
    an EVP_PKEY, then deserialize the result into a new EVP_PKEY and
    compare the two.
    
    The following tests are currently implemented:
    
    1.  EVP_PKEY (RSA) -> DER, then DER -> EVP_PKEY (RSA).
    2.  EVP_PKEY (RSA) -> PEM, then PEM -> EVP_PKEY (RSA).
        This one exercises deserializer chains, as we know that there is a
        PEM -> DER and a DER -> EVP_PKEY (RSA) deserializer, but no direct
        PEM -> EVP_PKEY (RSA) deserializer.
    
    Additionally, a small fix in test_fail_string_common(), as strcmp()
    could run past a buffer if one of the strings isn't terminated with
    a null byte within the given length.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12410)

commit dcfacbbfe9b3f8fa13eeb17a8fa4c89edefc8389
Author: Richard Levitte <levitte at openssl.org>
Date:   Thu Jul 9 19:09:40 2020 +0200

    PROV: Implement PEM to DER deserializer
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12410)

commit 1017b8e4a161682c909a98ebf3f7a21b38d6c677
Author: Richard Levitte <levitte at openssl.org>
Date:   Thu Jul 9 19:07:12 2020 +0200

    PROV: Implement DER to RSA deserializer
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12410)

commit 853ca12813dee0ec7ac75cfe5f1c9685ffb2d420
Author: Richard Levitte <levitte at openssl.org>
Date:   Thu Jul 9 18:55:44 2020 +0200

    CORE: Add upcalls for BIO_gets() and BIO_puts()
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12410)

commit 072a9fde7d67a621ebd2c8d1ba22ab6e17da5a88
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Jul 8 23:19:13 2020 +0200

    SERIALIZER: Add functions to deserialize into an EVP_PKEY
    
    EVP_PKEY is the fundamental type for provider side code, so we
    implement specific support for it, in form of a special context
    constructor.
    
    This constructor looks up and collects all available KEYMGMT
    implementations, and then uses those names to collect deserializer
    implementations, as described in the previous commit.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12410)

commit c3e4c1f325e6fc829a5b00a19a6019249cac781a
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Jul 8 23:04:08 2020 +0200

    DESERIALIZER: Add foundation for deserializers
    
    This adds a method OSSL_DESERIALIZER, a deserializer context and basic
    support to use a set of serializers to get a desired type of data, as
    well as deserializer chains.
    
    The idea is that the caller can call OSSL_DESERIALIZER_CTX_add_serializer()
    to set up the set of desired results, and to add possible chains, call
    OSSL_DESERIALIZER_CTX_add_extra().  All these deserializers are pushed
    on an internal stack.
    
    The actual deserialization is then performed using functions like
    OSSL_DESERIALIZER_from_bio().  When performing deserialization, the
    inernal stack is walked backwards, keeping track of the deserialized
    data and its type along the way, until the data kan be processed into
    the desired type of data.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12410)

commit 5dacb38ccefd45d832c9710b4dd0121fdcbdac72
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Jul 8 22:21:18 2020 +0200

    KEYMGMT: Add key loading function OSSL_FUNC_keymgmt_load()
    
    This function is used to create a keydata for a key that libcrypto
    only has a reference to.
    
    This introduces provider references, the contents which only the
    provider know how to interpret.  Outside of the provider, this is just
    an array of bytes.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12410)

commit af836c22cede6bd89c0b35c13d17e95f1854c5d0
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Jul 8 22:09:32 2020 +0200

    EVP KEYMGMT utils: Make a few more utility functions available
    
    This makes the following functions available for libcrypto code:
    
    evp_keymgmt_util_try_import()  - callback function
    evp_keymgmt_util_assign_pkey() - assigns keymgmt and keydata to an EVP_PKEY
    evp_keymgmt_util_make_pkey()   - creates an EVP_PKEY from keymgmt and keydata
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12410)

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

Summary of changes:
 crypto/err/err.c                                   |   1 +
 crypto/err/openssl.ec                              |   1 +
 crypto/err/openssl.txt                             |   1 +
 crypto/evp/evp_local.h                             |   2 +
 crypto/evp/keymgmt_lib.c                           |  78 +--
 crypto/evp/keymgmt_meth.c                          |  16 +-
 crypto/evp/p_lib.c                                 |  21 +-
 crypto/pem/pem_local.h                             |   2 +-
 crypto/pem/pem_pk8.c                               |   3 +-
 crypto/property/property_parse.c                   |   1 +
 crypto/provider_core.c                             |   2 +
 crypto/serializer/build.info                       |  10 +-
 crypto/serializer/deserializer_err.c               |  31 ++
 crypto/serializer/deserializer_lib.c               | 438 ++++++++++++++++
 crypto/serializer/deserializer_meth.c              | 548 +++++++++++++++++++++
 crypto/serializer/deserializer_pkey.c              | 361 ++++++++++++++
 crypto/serializer/serdes_pass.c                    | 159 ++++++
 crypto/serializer/serializer_local.h               |  85 +++-
 crypto/serializer/serializer_meth.c                |  36 +-
 crypto/serializer/serializer_pkey.c                | 112 +----
 doc/man3/OSSL_DESERIALIZER.pod                     | 146 ++++++
 doc/man3/OSSL_DESERIALIZER_CTX.pod                 |  74 +++
 doc/man3/OSSL_DESERIALIZER_CTX_new_by_EVP_PKEY.pod | 117 +++++
 doc/man3/OSSL_DESERIALIZER_from_bio.pod            | 253 ++++++++++
 doc/man3/OSSL_SERIALIZER_CTX_new_by_EVP_PKEY.pod   |   2 +-
 doc/man7/provider-keymgmt.pod                      |  23 +-
 include/crypto/evp.h                               |  18 +
 include/crypto/serializer.h                        |   2 +
 include/internal/cryptlib.h                        |   9 +-
 include/openssl/core_dispatch.h                    |  40 +-
 include/openssl/core_names.h                       |  10 +-
 include/openssl/deserializer.h                     | 120 +++++
 include/openssl/deserializererr.h                  |  35 ++
 include/openssl/err.h                              |   3 +-
 include/openssl/serializer.h                       |   2 +-
 include/openssl/types.h                            |   2 +
 providers/common/bio_prov.c                        |  32 +-
 providers/common/include/prov/bio.h                |   2 +
 providers/defltprov.c                              |  13 +
 .../implementations/include/prov/implementations.h |   3 +
 providers/implementations/keymgmt/rsa_kmgmt.c      |  17 +
 providers/implementations/serializers/build.info   |   3 +-
 .../serializers/deserialize_common.c               |  91 ++++
 .../serializers/deserialize_der2rsa.c              | 231 +++++++++
 .../serializers/deserialize_pem2der.c              | 202 ++++++++
 .../serializers/serializer_common.c                |  10 +
 .../implementations/serializers/serializer_local.h |  13 +
 .../implementations/serializers/serializer_rsa.c   |   5 +
 test/build.info                                    |   5 +
 test/recipes/04-test_serializer_deserializer.t     |  15 +
 test/serdes_test.c                                 | 378 ++++++++++++++
 test/testutil/format_output.c                      |   2 +-
 util/libcrypto.num                                 |  31 ++
 util/missingcrypto.txt                             |   1 +
 util/other.syms                                    |   6 +
 55 files changed, 3623 insertions(+), 201 deletions(-)
 create mode 100644 crypto/serializer/deserializer_err.c
 create mode 100644 crypto/serializer/deserializer_lib.c
 create mode 100644 crypto/serializer/deserializer_meth.c
 create mode 100644 crypto/serializer/deserializer_pkey.c
 create mode 100644 crypto/serializer/serdes_pass.c
 create mode 100644 doc/man3/OSSL_DESERIALIZER.pod
 create mode 100644 doc/man3/OSSL_DESERIALIZER_CTX.pod
 create mode 100644 doc/man3/OSSL_DESERIALIZER_CTX_new_by_EVP_PKEY.pod
 create mode 100644 doc/man3/OSSL_DESERIALIZER_from_bio.pod
 create mode 100644 include/openssl/deserializer.h
 create mode 100644 include/openssl/deserializererr.h
 create mode 100644 providers/implementations/serializers/deserialize_common.c
 create mode 100644 providers/implementations/serializers/deserialize_der2rsa.c
 create mode 100644 providers/implementations/serializers/deserialize_pem2der.c
 create mode 100644 test/recipes/04-test_serializer_deserializer.t
 create mode 100644 test/serdes_test.c

diff --git a/crypto/err/err.c b/crypto/err/err.c
index 26cf2b0b9d..e2d70d7a58 100644
--- a/crypto/err/err.c
+++ b/crypto/err/err.c
@@ -76,6 +76,7 @@ static ERR_STRING_DATA ERR_str_libraries[] = {
     {ERR_PACK(ERR_LIB_ESS, 0, 0), "ESS routines"},
     {ERR_PACK(ERR_LIB_PROV, 0, 0), "Provider routines"},
     {ERR_PACK(ERR_LIB_OSSL_SERIALIZER, 0, 0), "SERIALIZER routines"},
+    {ERR_PACK(ERR_LIB_OSSL_DESERIALIZER, 0, 0), "DESERIALIZER routines"},
     {ERR_PACK(ERR_LIB_HTTP, 0, 0), "HTTP routines"},
     {0, NULL},
 };
diff --git a/crypto/err/openssl.ec b/crypto/err/openssl.ec
index 1ec7bb1162..f1bed12795 100644
--- a/crypto/err/openssl.ec
+++ b/crypto/err/openssl.ec
@@ -41,6 +41,7 @@ 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_SERIALIZER include/openssl/serializer.h  crypto/serializer/serializer_err.c
+L OSSL_DESERIALIZER include/openssl/deserializer.h  crypto/serializer/deserializer_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 fcc4fb5c1c..e5ed28bce1 100644
--- a/crypto/err/openssl.txt
+++ b/crypto/err/openssl.txt
@@ -2693,6 +2693,7 @@ OCSP_R_STATUS_TOO_OLD:127:status too old
 OCSP_R_UNKNOWN_MESSAGE_DIGEST:119:unknown message digest
 OCSP_R_UNKNOWN_NID:120:unknown nid
 OCSP_R_UNSUPPORTED_REQUESTORNAME_TYPE:129:unsupported requestorname type
+OSSL_DESERIALIZER_R_MISSING_GET_PARAMS:100:missing get params
 OSSL_SERIALIZER_R_INCORRECT_PROPERTY_QUERY:100:incorrect property query
 OSSL_SERIALIZER_R_SERIALIZER_NOT_FOUND:101:serializer not found
 OSSL_STORE_R_AMBIGUOUS_CONTENT_TYPE:107:ambiguous content type
diff --git a/crypto/evp/evp_local.h b/crypto/evp/evp_local.h
index 4aae702d6f..99c53484a6 100644
--- a/crypto/evp/evp_local.h
+++ b/crypto/evp/evp_local.h
@@ -122,6 +122,8 @@ struct evp_keymgmt_st {
     OSSL_FUNC_keymgmt_gen_fn *gen;
     OSSL_FUNC_keymgmt_gen_cleanup_fn *gen_cleanup;
 
+    OSSL_FUNC_keymgmt_load_fn *load;
+
     /* Key object checking */
     OSSL_FUNC_keymgmt_query_operation_name_fn *query_operation_name;
     OSSL_FUNC_keymgmt_has_fn *has;
diff --git a/crypto/evp/keymgmt_lib.c b/crypto/evp/keymgmt_lib.c
index 68ed74b23a..5ef4115f47 100644
--- a/crypto/evp/keymgmt_lib.c
+++ b/crypto/evp/keymgmt_lib.c
@@ -28,16 +28,9 @@ static int match_type(const EVP_KEYMGMT *keymgmt1, const EVP_KEYMGMT *keymgmt2)
     return EVP_KEYMGMT_is_a(keymgmt1, name2);
 }
 
-struct import_data_st {
-    EVP_KEYMGMT *keymgmt;
-    void *keydata;
-
-    int selection;
-};
-
-static int try_import(const OSSL_PARAM params[], void *arg)
+int evp_keymgmt_util_try_import(const OSSL_PARAM params[], void *arg)
 {
-    struct import_data_st *data = arg;
+    struct evp_keymgmt_util_try_import_data_st *data = arg;
 
     /* Just in time creation of keydata */
     if (data->keydata == NULL
@@ -57,9 +50,36 @@ static int try_import(const OSSL_PARAM params[], void *arg)
                               params);
 }
 
+int evp_keymgmt_util_assign_pkey(EVP_PKEY *pkey, EVP_KEYMGMT *keymgmt,
+                                 void *keydata)
+{
+    if (pkey == NULL || keymgmt == NULL || keydata == NULL
+        || !EVP_PKEY_set_type_by_keymgmt(pkey, keymgmt)) {
+        ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+    pkey->keydata = keydata;
+    evp_keymgmt_util_cache_keyinfo(pkey);
+    return 1;
+}
+
+EVP_PKEY *evp_keymgmt_util_make_pkey(EVP_KEYMGMT *keymgmt, void *keydata)
+{
+    EVP_PKEY *pkey = NULL;
+
+    if (keymgmt == NULL
+        || keydata == NULL
+        || (pkey = EVP_PKEY_new()) == NULL
+        || !evp_keymgmt_util_assign_pkey(pkey, keymgmt, keydata)) {
+        EVP_PKEY_free(pkey);
+        return NULL;
+    }
+    return pkey;
+}
+
 void *evp_keymgmt_util_export_to_provider(EVP_PKEY *pk, EVP_KEYMGMT *keymgmt)
 {
-    struct import_data_st import_data;
+    struct evp_keymgmt_util_try_import_data_st import_data;
     size_t i = 0;
 
     /* Export to where? */
@@ -111,16 +131,16 @@ void *evp_keymgmt_util_export_to_provider(EVP_PKEY *pk, EVP_KEYMGMT *keymgmt)
      */
 
     /* Setup for the export callback */
-    import_data.keydata = NULL;  /* try_import will create it */
+    import_data.keydata = NULL;  /* evp_keymgmt_util_try_import will create it */
     import_data.keymgmt = keymgmt;
     import_data.selection = OSSL_KEYMGMT_SELECT_ALL;
 
     /*
-     * The export function calls the callback (try_import), which does the
-     * import for us.  If successful, we're done.
+     * The export function calls the callback (evp_keymgmt_util_try_import),
+     * which does the import for us.  If successful, we're done.
      */
     if (!evp_keymgmt_export(pk->keymgmt, pk->keydata, OSSL_KEYMGMT_SELECT_ALL,
-                            &try_import, &import_data)) {
+                            &evp_keymgmt_util_try_import, &import_data)) {
         /* If there was an error, bail out */
         evp_keymgmt_freedata(keymgmt, import_data.keydata);
         return NULL;
@@ -210,15 +230,10 @@ void *evp_keymgmt_util_fromdata(EVP_PKEY *target, EVP_KEYMGMT *keymgmt,
 
     if ((keydata = evp_keymgmt_newdata(keymgmt)) == NULL
         || !evp_keymgmt_import(keymgmt, keydata, selection, params)
-        || !EVP_PKEY_set_type_by_keymgmt(target, keymgmt)) {
+        || !evp_keymgmt_util_assign_pkey(target, keymgmt, keydata)) {
         evp_keymgmt_freedata(keymgmt, keydata);
         keydata = NULL;
     }
-    if (keydata != NULL) {
-        target->keydata = keydata;
-        evp_keymgmt_util_cache_keyinfo(target);
-    }
-
     return keydata;
 }
 
@@ -371,21 +386,21 @@ int evp_keymgmt_util_copy(EVP_PKEY *to, EVP_PKEY *from, int selection)
                               selection))
             return 0;
     } else if (match_type(to_keymgmt, from->keymgmt)) {
-        struct import_data_st import_data;
+        struct evp_keymgmt_util_try_import_data_st import_data;
 
         import_data.keymgmt = to_keymgmt;
         import_data.keydata = to_keydata;
         import_data.selection = selection;
 
         if (!evp_keymgmt_export(from->keymgmt, from->keydata, selection,
-                                &try_import, &import_data)) {
+                                &evp_keymgmt_util_try_import, &import_data)) {
             evp_keymgmt_freedata(to_keymgmt, alloc_keydata);
             return 0;
         }
 
         /*
-         * In case to_keydata was previously unallocated, try_import()
-         * may have created it for us.
+         * In case to_keydata was previously unallocated,
+         * evp_keymgmt_util_try_import() may have created it for us.
          */
         if (to_keydata == NULL)
             to_keydata = alloc_keydata = import_data.keydata;
@@ -394,6 +409,15 @@ int evp_keymgmt_util_copy(EVP_PKEY *to, EVP_PKEY *from, int selection)
         return 0;
     }
 
+    /*
+     * We only need to set the |to| type when its |keymgmt| isn't set.
+     * We can then just set its |keydata| to what we have, which might
+     * be exactly what it had when entering this function.
+     * This is a bit different from using evp_keymgmt_util_assign_pkey(),
+     * which isn't as careful with |to|'s original |keymgmt|, since it's
+     * meant to forcibly reassign an EVP_PKEY no matter what, which is
+     * why we don't use that one here.
+     */
     if (to->keymgmt == NULL
         && !EVP_PKEY_set_type_by_keymgmt(to, to_keymgmt)) {
         evp_keymgmt_freedata(to_keymgmt, alloc_keydata);
@@ -411,14 +435,10 @@ void *evp_keymgmt_util_gen(EVP_PKEY *target, EVP_KEYMGMT *keymgmt,
     void *keydata = NULL;
 
     if ((keydata = evp_keymgmt_gen(keymgmt, genctx, cb, cbarg)) == NULL
-        || !EVP_PKEY_set_type_by_keymgmt(target, keymgmt)) {
+        || !evp_keymgmt_util_assign_pkey(target, keymgmt, keydata)) {
         evp_keymgmt_freedata(keymgmt, keydata);
         keydata = NULL;
     }
-    if (keydata != NULL) {
-        target->keydata = keydata;
-        evp_keymgmt_util_cache_keyinfo(target);
-    }
 
     return keydata;
 }
diff --git a/crypto/evp/keymgmt_meth.c b/crypto/evp/keymgmt_meth.c
index 7847b98380..47067dd6c7 100644
--- a/crypto/evp/keymgmt_meth.c
+++ b/crypto/evp/keymgmt_meth.c
@@ -89,6 +89,10 @@ static void *keymgmt_from_dispatch(int name_id,
             if (keymgmt->free == NULL)
                 keymgmt->free = OSSL_FUNC_keymgmt_free(fns);
             break;
+        case OSSL_FUNC_KEYMGMT_LOAD:
+            if (keymgmt->load == NULL)
+                keymgmt->load = OSSL_FUNC_keymgmt_load(fns);
+            break;
         case OSSL_FUNC_KEYMGMT_GET_PARAMS:
             if (keymgmt->get_params == NULL) {
                 getparamfncnt++;
@@ -171,7 +175,9 @@ static void *keymgmt_from_dispatch(int name_id,
      * export if you can't import or export.
      */
     if (keymgmt->free == NULL
-        || (keymgmt->new == NULL && keymgmt->gen == NULL)
+        || (keymgmt->new == NULL
+            && keymgmt->gen == NULL
+            && keymgmt->load == NULL)
         || keymgmt->has == NULL
         || (getparamfncnt != 0 && getparamfncnt != 2)
         || (setparamfncnt != 0 && setparamfncnt != 2)
@@ -345,6 +351,14 @@ void evp_keymgmt_gen_cleanup(const EVP_KEYMGMT *keymgmt, void *genctx)
         keymgmt->gen_cleanup(genctx);
 }
 
+void *evp_keymgmt_load(const EVP_KEYMGMT *keymgmt,
+                       const void *objref, size_t objref_sz)
+{
+    if (keymgmt->load != NULL)
+        return keymgmt->load(objref, objref_sz);
+    return NULL;
+}
+
 int evp_keymgmt_get_params(const EVP_KEYMGMT *keymgmt, void *keydata,
                            OSSL_PARAM params[])
 {
diff --git a/crypto/evp/p_lib.c b/crypto/evp/p_lib.c
index aa11608688..65a767b4d0 100644
--- a/crypto/evp/p_lib.c
+++ b/crypto/evp/p_lib.c
@@ -219,23 +219,22 @@ static int evp_pkey_cmp_any(const EVP_PKEY *a, const EVP_PKEY *b,
     void *keydata1 = NULL, *keydata2 = NULL, *tmp_keydata = NULL;
 
     /* If none of them are provided, this function shouldn't have been called */
-    if (!ossl_assert(a->keymgmt != NULL || b->keymgmt != NULL))
+    if (!ossl_assert(evp_pkey_is_provided(a) || evp_pkey_is_provided(b)))
         return -2;
 
     /* For purely provided keys, we just call the keymgmt utility */
-    if (a->keymgmt != NULL && b->keymgmt != NULL)
+    if (evp_pkey_is_provided(a) && evp_pkey_is_provided(b))
         return evp_keymgmt_util_match((EVP_PKEY *)a, (EVP_PKEY *)b, selection);
 
     /*
      * At this point, one of them is provided, the other not.  This allows
      * us to compare types using legacy NIDs.
      */
-    if ((a->type != EVP_PKEY_NONE
-         && (b->keymgmt == NULL
-             || !EVP_KEYMGMT_is_a(b->keymgmt, OBJ_nid2sn(a->type))))
-        || (b->type != EVP_PKEY_NONE
-            && (a->keymgmt == NULL
-                || !EVP_KEYMGMT_is_a(a->keymgmt, OBJ_nid2sn(b->type)))))
+    if (evp_pkey_is_legacy(a)
+        && !EVP_KEYMGMT_is_a(b->keymgmt, OBJ_nid2sn(a->type)))
+        return -1;               /* not the same key type */
+    if (evp_pkey_is_legacy(b)
+        && !EVP_KEYMGMT_is_a(a->keymgmt, OBJ_nid2sn(b->type)))
         return -1;               /* not the same key type */
 
     /*
@@ -1803,15 +1802,13 @@ int evp_pkey_downgrade(EVP_PKEY *pk)
      * of the key data, typically the private bits.  In this case, we restore
      * the provider side internal "origin" and leave it at that.
      */
-    if (!ossl_assert(EVP_PKEY_set_type_by_keymgmt(pk, keymgmt))) {
+    if (!ossl_assert(evp_keymgmt_util_assign_pkey(pk, keymgmt, keydata))) {
         /* This should not be impossible */
         ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR);
         return 0;
     }
-    /* EVP_PKEY_set_type_by_keymgmt() increased the refcount... */
+    /* evp_keymgmt_util_assign_pkey() increased the refcount... */
     EVP_KEYMGMT_free(keymgmt);
-    pk->keydata = keydata;
-    evp_keymgmt_util_cache_keyinfo(pk);
     return 0;     /* No downgrade, but at least the key is restored */
 }
 #endif  /* FIPS_MODULE */
diff --git a/crypto/pem/pem_local.h b/crypto/pem/pem_local.h
index 3b501abde7..9563925f73 100644
--- a/crypto/pem/pem_local.h
+++ b/crypto/pem/pem_local.h
@@ -45,7 +45,7 @@
                 && !OSSL_SERIALIZER_CTX_set_passphrase(ctx, kstr, klen)) \
                 ret = 0;                                                \
             else if (cb != NULL                                         \
-                     && !OSSL_SERIALIZER_CTX_set_passphrase_cb(ctx, 1,  \
+                     && !OSSL_SERIALIZER_CTX_set_passphrase_cb(ctx,     \
                                                                cb, u))  \
                 ret = 0;                                                \
         }                                                               \
diff --git a/crypto/pem/pem_pk8.c b/crypto/pem/pem_pk8.c
index 8dbcb65bf7..12a25b7a82 100644
--- a/crypto/pem/pem_pk8.c
+++ b/crypto/pem/pem_pk8.c
@@ -109,8 +109,7 @@ static int do_pk8pkey(BIO *bp, const EVP_PKEY *x, int isder, int nid,
                     && !OSSL_SERIALIZER_CTX_set_passphrase(ctx, ukstr, klen))
                     ret = 0;
                 else if (cb != NULL
-                         && !OSSL_SERIALIZER_CTX_set_passphrase_cb(ctx, 1,
-                                                                   cb, u))
+                         && !OSSL_SERIALIZER_CTX_set_passphrase_cb(ctx, cb, u))
                     ret = 0;
             }
         }
diff --git a/crypto/property/property_parse.c b/crypto/property/property_parse.c
index 41a5a059c5..91b830c2e5 100644
--- a/crypto/property/property_parse.c
+++ b/crypto/property/property_parse.c
@@ -598,6 +598,7 @@ int ossl_property_parse_init(OPENSSL_CTX *ctx)
         "fips",         /* FIPS validated or FIPS supporting algorithm */
         "format",       /* output format for serializers */
         "type",         /* output type for serializers */
+        "input",        /* input type for deserializers */
     };
     size_t i;
 
diff --git a/crypto/provider_core.c b/crypto/provider_core.c
index b6586f904e..79c330383c 100644
--- a/crypto/provider_core.c
+++ b/crypto/provider_core.c
@@ -1061,6 +1061,8 @@ static const OSSL_DISPATCH core_dispatch_[] = {
     { OSSL_FUNC_BIO_NEW_MEMBUF, (void (*)(void))BIO_new_mem_buf },
     { OSSL_FUNC_BIO_READ_EX, (void (*)(void))BIO_read_ex },
     { OSSL_FUNC_BIO_WRITE_EX, (void (*)(void))BIO_write_ex },
+    { OSSL_FUNC_BIO_GETS, (void (*)(void))BIO_gets },
+    { OSSL_FUNC_BIO_PUTS, (void (*)(void))BIO_puts },
     { OSSL_FUNC_BIO_FREE, (void (*)(void))BIO_free },
     { OSSL_FUNC_BIO_VPRINTF, (void (*)(void))BIO_vprintf },
     { OSSL_FUNC_BIO_VSNPRINTF, (void (*)(void))BIO_vsnprintf },
diff --git a/crypto/serializer/build.info b/crypto/serializer/build.info
index 551319ed59..11f8889b6b 100644
--- a/crypto/serializer/build.info
+++ b/crypto/serializer/build.info
@@ -1,2 +1,8 @@
-SOURCE[../../libcrypto]=serializer_meth.c serializer_lib.c serializer_pkey.c \
-        serializer_err.c
+SOURCE[../../libcrypto]=serdes_pass.c
+
+SOURCE[../../libcrypto]=serializer_meth.c serializer_lib.c serializer_pkey.c
+SOURCE[../../libcrypto]=deserializer_meth.c deserializer_lib.c \
+        deserializer_pkey.c
+
+SOURCE[../../libcrypto]=serializer_err.c
+SOURCE[../../libcrypto]=deserializer_err.c
diff --git a/crypto/serializer/deserializer_err.c b/crypto/serializer/deserializer_err.c
new file mode 100644
index 0000000000..2cc245996f
--- /dev/null
+++ b/crypto/serializer/deserializer_err.c
@@ -0,0 +1,31 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-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
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/err.h>
+#include <openssl/deserializererr.h>
+
+#ifndef OPENSSL_NO_ERR
+
+static const ERR_STRING_DATA OSSL_DESERIALIZER_str_reasons[] = {
+    {ERR_PACK(ERR_LIB_OSSL_DESERIALIZER, 0, OSSL_DESERIALIZER_R_MISSING_GET_PARAMS),
+    "missing get params"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_OSSL_DESERIALIZER_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+    if (ERR_reason_error_string(OSSL_DESERIALIZER_str_reasons[0].error) == NULL)
+        ERR_load_strings_const(OSSL_DESERIALIZER_str_reasons);
+#endif
+    return 1;
+}
diff --git a/crypto/serializer/deserializer_lib.c b/crypto/serializer/deserializer_lib.c
new file mode 100644
index 0000000000..2fbb7782cf
--- /dev/null
+++ b/crypto/serializer/deserializer_lib.c
@@ -0,0 +1,438 @@
+/*
+ * Copyright 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
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core_names.h>
+#include <openssl/bio.h>
+#include <openssl/params.h>
+#include <openssl/provider.h>
+#include "serializer_local.h"
+#include "e_os.h"
+
+struct deser_process_data_st {
+    OSSL_DESERIALIZER_CTX *ctx;
+
+    /* Current BIO */
+    BIO *bio;
+
+    /* Index of the current deserializer instance to be processed */
+    size_t current_deser_inst_index;
+};
+
+static int deser_process(const OSSL_PARAM params[], void *arg);
+
+int OSSL_DESERIALIZER_from_bio(OSSL_DESERIALIZER_CTX *ctx, BIO *in)
+{
+    struct deser_process_data_st data;
+    int ok = 0;
+
+    memset(&data, 0, sizeof(data));
+    data.ctx = ctx;
+    data.bio = in;
+
+    ok = deser_process(NULL, &data);
+
+    /* Clear any cached passphrase */
+    OPENSSL_clear_free(ctx->cached_passphrase, ctx->cached_passphrase_len);
+    ctx->cached_passphrase = NULL;
+    ctx->cached_passphrase_len = 0;
+    return ok;
+}
+
+#ifndef OPENSSL_NO_STDIO
+static BIO *bio_from_file(FILE *fp)
+{
+    BIO *b;
+
+    if ((b = BIO_new(BIO_s_file())) == NULL) {
+        ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_BIO_LIB);
+        return NULL;
+    }
+    BIO_set_fp(b, fp, BIO_NOCLOSE);
+    return b;
+}
+
+int OSSL_DESERIALIZER_from_fp(OSSL_DESERIALIZER_CTX *ctx, FILE *fp)
+{
+    BIO *b = bio_from_file(fp);
+    int ret = 0;
+
+    if (b != NULL)
+        ret = OSSL_DESERIALIZER_from_bio(ctx, b);
+
+    BIO_free(b);
+    return ret;
+}
+#endif
+
+int OSSL_DESERIALIZER_CTX_set_input_type(OSSL_DESERIALIZER_CTX *ctx,
+                                         const char *input_type)
+{
+    if (!ossl_assert(ctx != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    /*
+     * NULL is a valid starting input type, and means that the caller leaves
+     * it to code to discover what the starting input type is.
+     */
+    ctx->start_input_type = input_type;
+    return 1;
+}
+
+int OSSL_DESERIALIZER_CTX_add_deserializer(OSSL_DESERIALIZER_CTX *ctx,
+                                           OSSL_DESERIALIZER *deser)
+{
+    OSSL_DESERIALIZER_INSTANCE *deser_inst = NULL;
+    const OSSL_PROVIDER *prov = NULL;
+    OSSL_PARAM params[2];
+    void *provctx = NULL;
+
+    if (!ossl_assert(ctx != NULL) || !ossl_assert(deser != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    if (deser->get_params == NULL) {
+        ERR_raise(ERR_LIB_OSSL_DESERIALIZER,
+                  OSSL_DESERIALIZER_R_MISSING_GET_PARAMS);
+        return 0;
+    }
+
+    if (ctx->deser_insts == NULL
+        && (ctx->deser_insts =
+            sk_OSSL_DESERIALIZER_INSTANCE_new_null()) == NULL) {
+        ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    if ((deser_inst = OPENSSL_zalloc(sizeof(*deser_inst))) == NULL) {
+        ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    if (!OSSL_DESERIALIZER_up_ref(deser)) {
+        ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    deser_inst->deser = deser;
+
+    prov = OSSL_DESERIALIZER_provider(deser_inst->deser);
+    provctx = OSSL_PROVIDER_get0_provider_ctx(prov);
+
+    /* Cache the input type for this serializer */
+    params[0] =
+        OSSL_PARAM_construct_utf8_ptr(OSSL_DESERIALIZER_PARAM_INPUT_TYPE,
+                                      (char **)&deser_inst->input_type, 0);
+    params[1] = OSSL_PARAM_construct_end();
+
+    if (!deser_inst->deser->get_params(params)
+        || !OSSL_PARAM_modified(&params[0]))
+        goto err;
+
+    if ((deser_inst->deserctx = deser_inst->deser->newctx(provctx))
+        == NULL)
+        goto err;
+
+    if (sk_OSSL_DESERIALIZER_INSTANCE_push(ctx->deser_insts, deser_inst) <= 0)
+        goto err;
+
+    return 1;
+ err:
+    if (deser_inst != NULL) {
+        if (deser_inst->deser != NULL)
+            deser_inst->deser->freectx(deser_inst->deserctx);
+        OSSL_DESERIALIZER_free(deser_inst->deser);
+        OPENSSL_free(deser_inst);
+    }
+    return 0;
+}
+
+int OSSL_DESERIALIZER_CTX_add_extra(OSSL_DESERIALIZER_CTX *ctx,
+                                    OPENSSL_CTX *libctx, const char *propq)
+{
+    /*
+     * This function goes through existing deserializer methods in
+     * |ctx->deser_insts|, and tries to fetch new deserializers that produce
+     * what the existing ones want as input, and push those newly fetched
+     * deserializers on top of the same stack.
+     * Then it does the same again, but looping over the newly fetched
+     * deserializers, until there are no more serializers to be fetched, or
+     * when we have done this 10 times.
+     *
+     * we do this with sliding windows on the stack by keeping track of indexes
+     * and of the end.
+     *
+     * +----------------+
+     * |   DER to RSA   | <--- w_prev_start
+     * +----------------+
+     * |   DER to DSA   |
+     * +----------------+
+     * |   DER to DH    |
+     * +----------------+
+     * |   PEM to DER   | <--- w_prev_end, w_new_start
+     * +----------------+
+     *                    <--- w_new_end
+     */
+    size_t w_prev_start, w_prev_end; /* "previous" deserializers */
+    size_t w_new_start, w_new_end;   /* "new" deserializers */
+    size_t count = 0; /* Calculates how many were added in each iteration */
+    size_t depth = 0; /* Counts the number of iterations */
+
+    if (!ossl_assert(ctx != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    /*
+     * If there is no stack of OSSL_DESERIALIZER_INSTANCE, we have nothing
+     * more to add.  That's fine.
+     */
+    if (ctx->deser_insts == NULL)
+        return 1;
+
+    w_prev_start = 0;
+    w_prev_end = sk_OSSL_DESERIALIZER_INSTANCE_num(ctx->deser_insts);
+    do {
+        size_t i;
+
+        w_new_start = w_new_end = w_prev_end;
+
+        for (i = w_prev_start; i < w_prev_end; i++) {
+            OSSL_DESERIALIZER_INSTANCE *deser_inst =
+                sk_OSSL_DESERIALIZER_INSTANCE_value(ctx->deser_insts, i);
+            const char *name = deser_inst->input_type;
+            OSSL_DESERIALIZER *deser = NULL;
+
+            /*
+             * If the caller has specified what the initial input should be,
+             * and the deserializer implementation we're looking at has that
+             * input type, there's no point adding on more implementations
+             * on top of this one, so we don't.
+             */
+            if (ctx->start_input_type != NULL
+                && strcasecmp(ctx->start_input_type,
+                              deser_inst->input_type) != 0)
+                continue;
+
+            ERR_set_mark();
+            deser = OSSL_DESERIALIZER_fetch(libctx, name, propq);
+            ERR_pop_to_mark();
+
+            if (deser != NULL) {
+                size_t j;
+
+                /*
+                 * Check that we don't already have this deserializer in our
+                 * stack We only need to check among the newly added ones.
+                 */
+                for (j = w_new_start; j < w_new_end; j++) {
+                    OSSL_DESERIALIZER_INSTANCE *check_inst =
+                        sk_OSSL_DESERIALIZER_INSTANCE_value(ctx->deser_insts, j);
+
+                    if (deser == check_inst->deser) {
+                        /* We found it, so drop the new fetch */
+                        OSSL_DESERIALIZER_free(deser);
+                        deser = NULL;
+                        break;
+                    }
+                }
+            }
+
+            if (deser == NULL)
+                continue;
+
+            /*
+             * Apart from keeping w_new_end up to date, We don't care about
+             * errors here.  If it doesn't collect, then it doesn't...
+             */
+            if (OSSL_DESERIALIZER_CTX_add_deserializer(ctx, deser)) /* ref++ */
+                w_new_end++;
+            OSSL_DESERIALIZER_free(deser); /* ref-- */
+        }
+        /* How many were added in this iteration */
+        count = w_new_end - w_new_start;
+
+        /* Slide the "previous deserializer" windows */
+        w_prev_start = w_new_start;
+        w_prev_end = w_new_end;
+
+        depth++;
+    } while (count != 0 && depth <= 10);
+
+    return 1;
+}
+
+int OSSL_DESERIALIZER_CTX_num_deserializers(OSSL_DESERIALIZER_CTX *ctx)
+{
+    if (ctx == NULL || ctx->deser_insts == NULL)
+        return 0;
+    return sk_OSSL_DESERIALIZER_INSTANCE_num(ctx->deser_insts);
+}
+
+int OSSL_DESERIALIZER_CTX_set_finalizer(OSSL_DESERIALIZER_CTX *ctx,
+                                        OSSL_DESERIALIZER_FINALIZER *finalizer,
+                                        OSSL_DESERIALIZER_CLEANER *cleaner,
+                                        void *finalize_arg)
+{
+    if (!ossl_assert(ctx != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    ctx->finalizer = finalizer;
+    ctx->cleaner = cleaner;
+    ctx->finalize_arg = finalize_arg;
+    return 1;
+}
+
+int OSSL_DESERIALIZER_export(OSSL_DESERIALIZER_INSTANCE *deser_inst,
+                             void *reference, size_t reference_sz,
+                             OSSL_CALLBACK *export_cb, void *export_cbarg)
+{
+    if (!(ossl_assert(deser_inst != NULL)
+          && ossl_assert(reference != NULL)
+          && ossl_assert(export_cb != NULL)
+          && ossl_assert(export_cbarg != NULL))) {
+        ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    return deser_inst->deser->export_object(deser_inst->deserctx,
+                                            reference, reference_sz,
+                                            export_cb, export_cbarg);
+}
+
+OSSL_DESERIALIZER *OSSL_DESERIALIZER_INSTANCE_deserializer
+    (OSSL_DESERIALIZER_INSTANCE *deser_inst)
+{
+    if (deser_inst == NULL)
+        return NULL;
+    return deser_inst->deser;
+}
+
+void *OSSL_DESERIALIZER_INSTANCE_deserializer_ctx
+    (OSSL_DESERIALIZER_INSTANCE *deser_inst)
+{
+    if (deser_inst == NULL)
+        return NULL;
+    return deser_inst->deserctx;
+}
+
+static int deser_process(const OSSL_PARAM params[], void *arg)
+{
+    struct deser_process_data_st *data = arg;
+    OSSL_DESERIALIZER_CTX *ctx = data->ctx;
+    OSSL_DESERIALIZER_INSTANCE *deser_inst = NULL;
+    OSSL_DESERIALIZER *deser = NULL;
+    BIO *bio = data->bio;
+    long loc;
+    size_t i;
+    int ok = 0;
+    /* For recursions */
+    struct deser_process_data_st new_data;
+
+    memset(&new_data, 0, sizeof(new_data));
+    new_data.ctx = data->ctx;
+
+    if (params == NULL) {
+        /* First iteration, where we prepare for what is to come */
+
+        data->current_deser_inst_index =
+            OSSL_DESERIALIZER_CTX_num_deserializers(ctx);
+
+        bio = data->bio;
+    } else {
+        const OSSL_PARAM *p;
+
+        deser_inst =
+            sk_OSSL_DESERIALIZER_INSTANCE_value(ctx->deser_insts,
+                                                data->current_deser_inst_index);
+        deser = OSSL_DESERIALIZER_INSTANCE_deserializer(deser_inst);
+
+        if (ctx->finalizer(deser_inst, params, ctx->finalize_arg)) {
+            ok = 1;
+            goto end;
+        }
+
+        /* The finalizer didn't return success */
+
+        /*
+         * so we try to use the object we got and feed it to any next
+         * deserializer that will take it.  Object references are not
+         * allowed for this.
+         * If this data isn't present, deserialization has failed.
+         */
+
+        p = OSSL_PARAM_locate_const(params, OSSL_DESERIALIZER_PARAM_DATA);
+        if (p == NULL || p->data_type != OSSL_PARAM_OCTET_STRING)
+            goto end;
+        new_data.bio = BIO_new_mem_buf(p->data, (int)p->data_size);
+        if (new_data.bio == NULL)
+            goto end;
+        bio = new_data.bio;
+    }
+
+    /*
+     * If we have no more deserializers to look through at this point,
+     * we failed
+     */
+    if (data->current_deser_inst_index == 0)
+        goto end;
+
+    if ((loc = BIO_tell(bio)) < 0) {
+        ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_BIO_LIB);
+        goto end;
+    }
+
+    for (i = data->current_deser_inst_index; i-- > 0;) {
+        OSSL_DESERIALIZER_INSTANCE *new_deser_inst =
+            sk_OSSL_DESERIALIZER_INSTANCE_value(ctx->deser_insts, i);
+        OSSL_DESERIALIZER *new_deser =
+            OSSL_DESERIALIZER_INSTANCE_deserializer(new_deser_inst);
+
+        /*
+         * If |deser| is NULL, it means we've just started, and the caller
+         * may have specified what it expects the initial input to be.  If
+         * that's the case, we do this extra check.
+         */
+        if (deser == NULL && ctx->start_input_type != NULL
+            && strcasecmp(ctx->start_input_type, deser_inst->input_type) != 0)
+            continue;
+
+        /*
+         * If we have a previous deserializer, we check that the input type
+         * of the next to be used matches the type of this previous one.
+         * deser_inst->input_type is a cache of the parameter "input-type"
+         * value for that deserializer.
+         */
+        if (deser != NULL
+            && !OSSL_DESERIALIZER_is_a(deser, new_deser_inst->input_type))
+            continue;
+
+        if (loc == 0) {
+            if (BIO_reset(bio) <= 0)
+                goto end;
+        } else {
+            if (BIO_seek(bio, loc) <= 0)
+                goto end;
+        }
+
+        /* Recurse */
+        new_data.current_deser_inst_index = i;
+        ok = new_deser->deserialize(new_deser_inst->deserctx,
+                                    (OSSL_CORE_BIO *)bio,
+                                    deser_process, &new_data,
+                                    NULL /* ossl_deserializer_passphrase_in_cb */,
+                                    new_data.ctx);
+        if (ok)
+            break;
+    }
+
+ end:
+    BIO_free(new_data.bio);
+    return ok;
+}
diff --git a/crypto/serializer/deserializer_meth.c b/crypto/serializer/deserializer_meth.c
new file mode 100644
index 0000000000..54500716ec
--- /dev/null
+++ b/crypto/serializer/deserializer_meth.c
@@ -0,0 +1,548 @@
+/*
+ * Copyright 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
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/deserializer.h>
+#include <openssl/ui.h>
+#include "internal/core.h"
+#include "internal/namemap.h"
+#include "internal/property.h"
+#include "internal/provider.h"
+#include "crypto/serializer.h"
+#include "serializer_local.h"
+
+static void OSSL_DESERIALIZER_INSTANCE_free(OSSL_DESERIALIZER_INSTANCE *instance);
+
+/*
+ * Deserializer can have multiple names, separated with colons in a name string
+ */
+#define NAME_SEPARATOR ':'
+
+/* Simple method structure constructor and destructor */
+static OSSL_DESERIALIZER *ossl_deserializer_new(void)
+{
+    OSSL_DESERIALIZER *deser = NULL;
+
+    if ((deser = OPENSSL_zalloc(sizeof(*deser))) == NULL
+        || (deser->base.lock = CRYPTO_THREAD_lock_new()) == NULL) {
+        OSSL_DESERIALIZER_free(deser);
+        ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    deser->base.refcnt = 1;
+
+    return deser;
+}
+
+int OSSL_DESERIALIZER_up_ref(OSSL_DESERIALIZER *deser)
+{
+    int ref = 0;
+
+    CRYPTO_UP_REF(&deser->base.refcnt, &ref, deser->base.lock);
+    return 1;
+}
+
+void OSSL_DESERIALIZER_free(OSSL_DESERIALIZER *deser)
+{
+    int ref = 0;
+
+    if (deser == NULL)
+        return;
+
+    CRYPTO_DOWN_REF(&deser->base.refcnt, &ref, deser->base.lock);
+    if (ref > 0)
+        return;
+    ossl_provider_free(deser->base.prov);
+    CRYPTO_THREAD_lock_free(deser->base.lock);
+    OPENSSL_free(deser);
+}
+
+/* Permanent deserializer method store, constructor and destructor */
+static void deserializer_store_free(void *vstore)
+{
+    ossl_method_store_free(vstore);
+}
+
+static void *deserializer_store_new(OPENSSL_CTX *ctx)
+{
+    return ossl_method_store_new(ctx);
+}
+
+
+static const OPENSSL_CTX_METHOD deserializer_store_method = {
+    deserializer_store_new,
+    deserializer_store_free,
+};
+
+/* Data to be passed through ossl_method_construct() */
+struct deserializer_data_st {
+    OPENSSL_CTX *libctx;
+    OSSL_METHOD_CONSTRUCT_METHOD *mcm;
+    int id;                      /* For get_deserializer_from_store() */
+    const char *names;           /* For get_deserializer_from_store() */
+    const char *propquery;       /* For get_deserializer_from_store() */
+};
+
+/*
+ * Generic routines to fetch / create DESERIALIZER methods with
+ * ossl_method_construct()
+ */
+
+/* Temporary deserializer method store, constructor and destructor */
+static void *alloc_tmp_deserializer_store(OPENSSL_CTX *ctx)
+{
+    return ossl_method_store_new(ctx);
+}
+
+ static void dealloc_tmp_deserializer_store(void *store)
+{
+    if (store != NULL)
+        ossl_method_store_free(store);
+}
+
+/* Get the permanent deserializer store */
+static OSSL_METHOD_STORE *get_deserializer_store(OPENSSL_CTX *libctx)
+{
+    return openssl_ctx_get_data(libctx, OPENSSL_CTX_DESERIALIZER_STORE_INDEX,
+                                &deserializer_store_method);
+}
+
+/* Get deserializer methods from a store, or put one in */
+static void *get_deserializer_from_store(OPENSSL_CTX *libctx, void *store,
+                                         void *data)
+{
+    struct deserializer_data_st *methdata = data;
+    void *method = NULL;
+    int id;
+
+    if ((id = methdata->id) == 0) {
+        OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx);
+
+        id = ossl_namemap_name2num(namemap, methdata->names);
+    }
+
+    if (store == NULL
+        && (store = get_deserializer_store(libctx)) == NULL)
+        return NULL;
+
+    if (!ossl_method_store_fetch(store, id, methdata->propquery, &method))
+        return NULL;
+    return method;
+}
+
+static int put_deserializer_in_store(OPENSSL_CTX *libctx, void *store,
+                                     void *method, const OSSL_PROVIDER *prov,
+                                     int operation_id, const char *names,
+                                     const char *propdef, void *unused)
+{
+    OSSL_NAMEMAP *namemap;
+    int id;
+
+    if ((namemap = ossl_namemap_stored(libctx)) == NULL
+        || (id = ossl_namemap_name2num(namemap, names)) == 0)
+        return 0;
+
+    if (store == NULL && (store = get_deserializer_store(libctx)) == NULL)
+        return 0;
+
+    return ossl_method_store_add(store, prov, id, propdef, method,
+                                 (int (*)(void *))OSSL_DESERIALIZER_up_ref,
+                                 (void (*)(void *))OSSL_DESERIALIZER_free);
+}
+
+/* Create and populate a deserializer method */
+static void *deserializer_from_dispatch(int id, const OSSL_ALGORITHM *algodef,
+                                        OSSL_PROVIDER *prov)
+{
+    OSSL_DESERIALIZER *deser = NULL;
+    const OSSL_DISPATCH *fns = algodef->implementation;
+
+    if ((deser = ossl_deserializer_new()) == NULL)
+        return NULL;
+    deser->base.id = id;
+    deser->base.propdef = algodef->property_definition;
+
+    for (; fns->function_id != 0; fns++) {
+        switch (fns->function_id) {
+        case OSSL_FUNC_DESERIALIZER_NEWCTX:
+            if (deser->newctx == NULL)
+                deser->newctx = OSSL_FUNC_deserializer_newctx(fns);
+            break;
+        case OSSL_FUNC_DESERIALIZER_FREECTX:
+            if (deser->freectx == NULL)
+                deser->freectx = OSSL_FUNC_deserializer_freectx(fns);
+            break;
+        case OSSL_FUNC_DESERIALIZER_GET_PARAMS:
+            if (deser->get_params == NULL)
+                deser->get_params =
+                    OSSL_FUNC_deserializer_get_params(fns);
+            break;
+        case OSSL_FUNC_DESERIALIZER_GETTABLE_PARAMS:
+            if (deser->gettable_params == NULL)
+                deser->gettable_params =
+                    OSSL_FUNC_deserializer_gettable_params(fns);
+            break;
+        case OSSL_FUNC_DESERIALIZER_SET_CTX_PARAMS:
+            if (deser->set_ctx_params == NULL)
+                deser->set_ctx_params =
+                    OSSL_FUNC_deserializer_set_ctx_params(fns);
+            break;
+        case OSSL_FUNC_DESERIALIZER_SETTABLE_CTX_PARAMS:
+            if (deser->settable_ctx_params == NULL)
+                deser->settable_ctx_params =
+                    OSSL_FUNC_deserializer_settable_ctx_params(fns);
+            break;
+        case OSSL_FUNC_DESERIALIZER_DESERIALIZE:
+            if (deser->deserialize == NULL)
+                deser->deserialize = OSSL_FUNC_deserializer_deserialize(fns);
+            break;
+        case OSSL_FUNC_DESERIALIZER_EXPORT_OBJECT:
+            if (deser->export_object == NULL)
+                deser->export_object = OSSL_FUNC_deserializer_export_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 serializing driver functions.
+     */
+    if (!((deser->newctx == NULL && deser->freectx == NULL)
+          || (deser->newctx != NULL && deser->freectx != NULL))
+        || (deser->deserialize == NULL && deser->export_object == NULL)) {
+        OSSL_DESERIALIZER_free(deser);
+        ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_INVALID_PROVIDER_FUNCTIONS);
+        return NULL;
+    }
+
+    if (prov != NULL && !ossl_provider_up_ref(prov)) {
+        OSSL_DESERIALIZER_free(deser);
+        return NULL;
+    }
+
+    deser->base.prov = prov;
+    return deser;
+}
+
+
+/*
+ * The core fetching functionality passes the names of the implementation.
+ * This function is responsible to getting an identity number for them,
+ * then call deserializer_from_dispatch() with that identity number.
+ */
+static void *construct_deserializer(const OSSL_ALGORITHM *algodef,
+                                    OSSL_PROVIDER *prov, void *unused)
+{
+    /*
+     * This function is only called if get_deserializer_from_store() returned
+     * NULL, so it's safe to say that of all the spots to create a new
+     * namemap entry, this is it.  Should the name already exist there, we
+     * know that ossl_namemap_add() will return its corresponding number.
+     */
+    OPENSSL_CTX *libctx = ossl_provider_library_context(prov);
+    OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx);
+    const char *names = algodef->algorithm_names;
+    int id = ossl_namemap_add_names(namemap, 0, names, NAME_SEPARATOR);
+    void *method = NULL;
+
+    if (id != 0)
+        method = deserializer_from_dispatch(id, algodef, prov);
+
+    return method;
+}
+
+/* Intermediary function to avoid ugly casts, used below */
+static void destruct_deserializer(void *method, void *data)
+{
+    OSSL_DESERIALIZER_free(method);
+}
+
+static int up_ref_deserializer(void *method)
+{
+    return OSSL_DESERIALIZER_up_ref(method);
+}
+
+static void free_deserializer(void *method)
+{
+    OSSL_DESERIALIZER_free(method);
+}
+
+/* Fetching support.  Can fetch by numeric identity or by name */
+static OSSL_DESERIALIZER *inner_ossl_deserializer_fetch(OPENSSL_CTX *libctx,
+                                                        int id,
+                                                        const char *name,
+                                                        const char *properties)
+{
+    OSSL_METHOD_STORE *store = get_deserializer_store(libctx);
+    OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx);
+    void *method = NULL;
+
+    if (store == NULL || namemap == NULL)
+        return NULL;
+
+    /*
+     * If we have been passed neither a name_id or a name, we have an
+     * internal programming error.
+     */
+    if (!ossl_assert(id != 0 || name != NULL))
+        return NULL;
+
+    if (id == 0)
+        id = ossl_namemap_name2num(namemap, name);
+
+    if (id == 0
+        || !ossl_method_store_cache_get(store, id, properties, &method)) {
+        OSSL_METHOD_CONSTRUCT_METHOD mcm = {
+            alloc_tmp_deserializer_store,
+            dealloc_tmp_deserializer_store,
+            get_deserializer_from_store,
+            put_deserializer_in_store,
+            construct_deserializer,
+            destruct_deserializer
+        };
+        struct deserializer_data_st mcmdata;
+
+        mcmdata.libctx = libctx;
+        mcmdata.mcm = &mcm;
+        mcmdata.id = id;
+        mcmdata.names = name;
+        mcmdata.propquery = properties;
+        if ((method = ossl_method_construct(libctx, OSSL_OP_DESERIALIZER,
+                                            0 /* !force_cache */,
+                                            &mcm, &mcmdata)) != NULL) {
+            /*
+             * If construction did create a method for us, we know that
+             * there is a correct name_id and meth_id, since those have
+             * already been calculated in get_deserializer_from_store() and
+             * put_deserializer_in_store() above.
+             */
+            if (id == 0)
+                id = ossl_namemap_name2num(namemap, name);
+            ossl_method_store_cache_set(store, id, properties, method,
+                                        up_ref_deserializer, free_deserializer);
+        }
+    }
+
+    return method;
+}
+
+OSSL_DESERIALIZER *OSSL_DESERIALIZER_fetch(OPENSSL_CTX *libctx,
+                                           const char *name,
+                                           const char *properties)
+{
+    return inner_ossl_deserializer_fetch(libctx, 0, name, properties);
+}
+
+OSSL_DESERIALIZER *ossl_deserializer_fetch_by_number(OPENSSL_CTX *libctx,
+                                                     int id,
+                                                     const char *properties)
+{
+    return inner_ossl_deserializer_fetch(libctx, id, NULL, properties);
+}
+
+/*
+ * Library of basic method functions
+ */
+
+const OSSL_PROVIDER *OSSL_DESERIALIZER_provider(const OSSL_DESERIALIZER *deser)
+{
+    if (!ossl_assert(deser != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    return deser->base.prov;
+}
+
+const char *OSSL_DESERIALIZER_properties(const OSSL_DESERIALIZER *deser)
+{
+    if (!ossl_assert(deser != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    return deser->base.propdef;
+}
+
+int OSSL_DESERIALIZER_number(const OSSL_DESERIALIZER *deser)
+{
+    if (!ossl_assert(deser != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    return deser->base.id;
+}
+
+int OSSL_DESERIALIZER_is_a(const OSSL_DESERIALIZER *deser, const char *name)
+{
+    if (deser->base.prov != NULL) {
+        OPENSSL_CTX *libctx = ossl_provider_library_context(deser->base.prov);
+        OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx);
+
+        return ossl_namemap_name2num(namemap, name) == deser->base.id;
+    }
+    return 0;
+}
+
+struct deserializer_do_all_data_st {
+    void (*user_fn)(void *method, void *arg);
+    void *user_arg;
+};
+
+static void deserializer_do_one(OSSL_PROVIDER *provider,
+                                const OSSL_ALGORITHM *algodef,
+                                int no_store, void *vdata)
+{
+    struct deserializer_do_all_data_st *data = vdata;
+    OPENSSL_CTX *libctx = ossl_provider_library_context(provider);
+    OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx);
+    const char *names = algodef->algorithm_names;
+    int id = ossl_namemap_add_names(namemap, 0, names, NAME_SEPARATOR);
+    void *method = NULL;
+
+    if (id != 0)
+        method =
+            deserializer_from_dispatch(id, algodef, provider);
+
+    if (method != NULL) {
+        data->user_fn(method, data->user_arg);
+        OSSL_DESERIALIZER_free(method);
+    }
+}
+
+void OSSL_DESERIALIZER_do_all_provided(OPENSSL_CTX *libctx,
+                                       void (*fn)(OSSL_DESERIALIZER *deser,
+                                                  void *arg),
+                                       void *arg)
+{
+    struct deserializer_do_all_data_st data;
+
+    data.user_fn = (void (*)(void *, void *))fn;
+    data.user_arg = arg;
+    ossl_algorithm_do_all(libctx, OSSL_OP_DESERIALIZER, NULL,
+                          NULL, deserializer_do_one, NULL,
+                          &data);
+}
+
+void OSSL_DESERIALIZER_names_do_all(const OSSL_DESERIALIZER *deser,
+                                    void (*fn)(const char *name, void *data),
+                                    void *data)
+{
+    if (deser == NULL)
+        return;
+
+    if (deser->base.prov != NULL) {
+        OPENSSL_CTX *libctx = ossl_provider_library_context(deser->base.prov);
+        OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx);
+
+        ossl_namemap_doall_names(namemap, deser->base.id, fn, data);
+    }
+}
+
+const OSSL_PARAM *
+OSSL_DESERIALIZER_gettable_params(OSSL_DESERIALIZER *deser)
+{
+    if (deser != NULL && deser->gettable_params != NULL)
+        return deser->gettable_params();
+    return NULL;
+}
+
+int OSSL_DESERIALIZER_get_params(OSSL_DESERIALIZER *deser, OSSL_PARAM params[])
+{
+    if (deser != NULL && deser->get_params != NULL)
+        return deser->get_params(params);
+    return 0;
+}
+
+const OSSL_PARAM *
+OSSL_DESERIALIZER_settable_ctx_params(OSSL_DESERIALIZER *deser)
+{
+    if (deser != NULL && deser->settable_ctx_params != NULL)
+        return deser->settable_ctx_params();
+    return NULL;
+}
+
+/*
+ * Deserializer context support
+ */
+
+/*
+ * |ser| value NULL is valid, and signifies that there is no deserializer.
+ * This is useful to provide fallback mechanisms.
+ *  Functions that want to verify if there is a deserializer can do so with
+ * OSSL_DESERIALIZER_CTX_get_deserializer()
+ */
+OSSL_DESERIALIZER_CTX *OSSL_DESERIALIZER_CTX_new(void)
+{
+    OSSL_DESERIALIZER_CTX *ctx;
+
+    if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) == NULL) {
+        ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    return ctx;
+}
+
+int OSSL_DESERIALIZER_CTX_set_params(OSSL_DESERIALIZER_CTX *ctx,
+                                     const OSSL_PARAM params[])
+{
+    size_t i;
+    size_t l;
+
+    if (!ossl_assert(ctx != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    if (ctx->deser_insts == NULL)
+        return 1;
+
+    l = (size_t)sk_OSSL_DESERIALIZER_INSTANCE_num(ctx->deser_insts);
+    for (i = 0; i < l; i++) {
+        OSSL_DESERIALIZER_INSTANCE *deser_inst =
+            sk_OSSL_DESERIALIZER_INSTANCE_value(ctx->deser_insts, i);
+
+        if (deser_inst->deserctx == NULL
+            || deser_inst->deser->set_ctx_params == NULL)
+            continue;
+        if (!deser_inst->deser->set_ctx_params(deser_inst->deserctx, params))
+            return 0;
+    }
+    return 1;
+}
+
+static void
+OSSL_DESERIALIZER_INSTANCE_free(OSSL_DESERIALIZER_INSTANCE *deser_inst)
+{
+    if (deser_inst != NULL) {
+        if (deser_inst->deser->freectx != NULL)
+            deser_inst->deser->freectx(deser_inst->deserctx);
+        deser_inst->deserctx = NULL;
+        OSSL_DESERIALIZER_free(deser_inst->deser);
+        deser_inst->deser = NULL;
+        OPENSSL_free(deser_inst);
+        deser_inst = NULL;
+    }
+}
+
+void OSSL_DESERIALIZER_CTX_free(OSSL_DESERIALIZER_CTX *ctx)
+{
+    if (ctx != NULL) {
+        if (ctx->cleaner != NULL)
+            ctx->cleaner(ctx->finalize_arg);
+        sk_OSSL_DESERIALIZER_INSTANCE_pop_free(ctx->deser_insts,
+                                               OSSL_DESERIALIZER_INSTANCE_free);
+        UI_destroy_method(ctx->allocated_ui_method);
+        OPENSSL_free(ctx);
+    }
+}
diff --git a/crypto/serializer/deserializer_pkey.c b/crypto/serializer/deserializer_pkey.c
new file mode 100644
index 0000000000..0fafdf31aa
--- /dev/null
+++ b/crypto/serializer/deserializer_pkey.c
@@ -0,0 +1,361 @@
+/*
+ * Copyright 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
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core_names.h>
+#include <openssl/evp.h>
+#include <openssl/ui.h>
+#include <openssl/deserializer.h>
+#include <openssl/core_names.h>
+#include <openssl/safestack.h>
+#include "crypto/evp.h"
+#include "serializer_local.h"
+
+int OSSL_DESERIALIZER_CTX_set_cipher(OSSL_DESERIALIZER_CTX *ctx,
+                                     const char *cipher_name,
+                                     const char *propquery)
+{
+    OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END };
+
+    params[0] =
+        OSSL_PARAM_construct_utf8_string(OSSL_DESERIALIZER_PARAM_CIPHER,
+                                         (void *)cipher_name, 0);
+    params[1] =
+        OSSL_PARAM_construct_utf8_string(OSSL_DESERIALIZER_PARAM_PROPERTIES,
+                                         (void *)propquery, 0);
+
+    return OSSL_DESERIALIZER_CTX_set_params(ctx, params);
+}
+
+int OSSL_DESERIALIZER_CTX_set_passphrase(OSSL_DESERIALIZER_CTX *ctx,
+                                         const unsigned char *kstr,
+                                         size_t klen)
+{
+    OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END };
+
+    params[0] = OSSL_PARAM_construct_octet_string(OSSL_DESERIALIZER_PARAM_PASS,
+                                                  (void *)kstr, klen);
+
+    return OSSL_DESERIALIZER_CTX_set_params(ctx, params);
+}
+
+static void deserializer_ctx_reset_passphrase_ui(OSSL_DESERIALIZER_CTX *ctx)
+{
+    UI_destroy_method(ctx->allocated_ui_method);
+    ctx->allocated_ui_method = NULL;
+    ctx->ui_method = NULL;
+    ctx->ui_data = NULL;
+}
+
+int OSSL_DESERIALIZER_CTX_set_passphrase_ui(OSSL_DESERIALIZER_CTX *ctx,
+                                            const UI_METHOD *ui_method,
+                                            void *ui_data)
+{
+    if (!ossl_assert(ctx != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    deserializer_ctx_reset_passphrase_ui(ctx);
+    ctx->ui_method = ui_method;
+    ctx->ui_data = ui_data;
+    return 1;
+}
+
+int OSSL_DESERIALIZER_CTX_set_passphrase_cb(OSSL_DESERIALIZER_CTX *ctx,
+                                            pem_password_cb *cb, void *cbarg)
+{
+    if (!ossl_assert(ctx != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    deserializer_ctx_reset_passphrase_ui(ctx);
+    if (cb == NULL)
+        return 1;
+    ctx->ui_method =
+        ctx->allocated_ui_method = UI_UTIL_wrap_read_pem_callback(cb, 0);
+    ctx->ui_data = cbarg;
+
+    return ctx->ui_method != NULL;
+}
+
+/*
+ * Support for OSSL_DESERIALIZER_CTX_new_by_EVP_PKEY:
+ * Handle an object reference
+ */
+
+DEFINE_STACK_OF(EVP_KEYMGMT)
+
+struct deser_EVP_PKEY_data_st {
+    char *object_type;           /* recorded object data type, may be NULL */
+    void **object;               /* Where the result should end up */
+    STACK_OF(EVP_KEYMGMT) *keymgmts; /* The EVP_KEYMGMTs we handle */
+};
+
+static int deser_finalize_EVP_PKEY(OSSL_DESERIALIZER_INSTANCE *deser_inst,
+                                   const OSSL_PARAM *params,
+                                   void *finalize_arg)
+{
+    struct deser_EVP_PKEY_data_st *data = finalize_arg;
+    OSSL_DESERIALIZER *deser =
+        OSSL_DESERIALIZER_INSTANCE_deserializer(deser_inst);
+    void *deserctx = OSSL_DESERIALIZER_INSTANCE_deserializer_ctx(deser_inst);
+    size_t i, end_i;
+    /*
+     * |object_ref| points to a provider reference to an object, its exact
+     * contents entirely opaque to us, but may be passed to any provider
+     * function that expects this (such as OSSL_FUNC_keymgmt_load().
+     *
+     * This pointer is considered volatile, i.e. whatever it points at
+     * is assumed to be freed as soon as this function returns.
+     */
+    void *object_ref = NULL;
+    size_t object_ref_sz = 0;
+    const OSSL_PARAM *p;
+
+    p = OSSL_PARAM_locate_const(params, OSSL_DESERIALIZER_PARAM_DATA_TYPE);
+    if (p != NULL) {
+        char *object_type = NULL;
+
+        if (!OSSL_PARAM_get_utf8_string(p, &object_type, 0))
+            return 0;
+        OPENSSL_free(data->object_type);
+        data->object_type = object_type;
+    }
+
+    /*
+     * For stuff that should end up in an EVP_PKEY, we only accept an object
+     * reference for the moment.  This enforces that the key data itself
+     * remains with the provider.
+     */
+    p = OSSL_PARAM_locate_const(params, OSSL_DESERIALIZER_PARAM_REFERENCE);
+    if (p == NULL || p->data_type != OSSL_PARAM_OCTET_STRING)
+        return 0;
+    object_ref = p->data;
+    object_ref_sz = p->data_size;
+
+    /* We may have reached one of the goals, let's find out! */
+    end_i = sk_EVP_KEYMGMT_num(data->keymgmts);
+    for (i = 0; end_i; i++) {
+        EVP_KEYMGMT *keymgmt = sk_EVP_KEYMGMT_value(data->keymgmts, i);
+
+        /*
+         * There are two ways to find a matching KEYMGMT:
+         *
+         * 1.  If the object data type (recorded in |data->object_type|)
+         *     is defined, by checking it using EVP_KEYMGMT_is_a().
+         * 2.  If the object data type is NOT defined, by comparing the
+         *     EVP_KEYMGMT and OSSL_DESERIALIZER method numbers.  Since
+         *     EVP_KEYMGMT and OSSL_DESERIALIZE operate with the same
+         *     namemap, we know that the method numbers must match.
+         *
+         * This allows individual deserializers to specify variants of keys,
+         * such as a DER to RSA deserializer finding a RSA-PSS key, without
+         * having to deserialize the exact same DER blob into the exact same
+         * internal structure twice.  This is, of course, entirely at the
+         * discretion of the deserializer implementations.
+         */
+        if (data->object_type != NULL
+            ? EVP_KEYMGMT_is_a(keymgmt, data->object_type)
+            : EVP_KEYMGMT_number(keymgmt) == OSSL_DESERIALIZER_number(deser)) {
+            EVP_PKEY *pkey = NULL;
+            void *keydata = NULL;
+            const OSSL_PROVIDER *keymgmt_prov =
+                EVP_KEYMGMT_provider(keymgmt);
+            const OSSL_PROVIDER *deser_prov =
+                OSSL_DESERIALIZER_provider(deser);
+
+            /*
+             * If the EVP_KEYMGMT and the OSSL_DDESERIALIZER are from the
+             * same provider, we assume that the KEYMGMT has a key loading
+             * function that can handle the provider reference we hold.
+             *
+             * Otherwise, we export from the deserializer and import the
+             * result in the keymgmt.
+             */
+            if (keymgmt_prov == deser_prov) {
+                keydata = evp_keymgmt_load(keymgmt, object_ref, object_ref_sz);
+            } else {
+                struct evp_keymgmt_util_try_import_data_st import_data;
+
+                import_data.keymgmt = keymgmt;
+                import_data.keydata = NULL;
+                import_data.selection = OSSL_KEYMGMT_SELECT_ALL_PARAMETERS;
+
+                /*
+                 * No need to check for errors here, the value of
+                 * |import_data.keydata| is as much an indicator.
+                 */
+                (void)deser->export_object(deserctx, object_ref, object_ref_sz,
+                                           &evp_keymgmt_util_try_import,
+                                           &import_data);
+                keydata = import_data.keydata;
+                import_data.keydata = NULL;
+            }
+
+            if (keydata != NULL
+                && (pkey =
+                    evp_keymgmt_util_make_pkey(keymgmt, keydata)) == NULL)
+                evp_keymgmt_freedata(keymgmt, keydata);
+
+            *data->object = pkey;
+
+            break;
+        }
+    }
+    /*
+     * We successfully looked through, |*ctx->object| determines if we
+     * actually found something.
+     */
+    return (*data->object != NULL);
+}
+
+static void deser_clean_EVP_PKEY(void *finalize_arg)
+{
+    struct deser_EVP_PKEY_data_st *data = finalize_arg;
+
+    sk_EVP_KEYMGMT_pop_free(data->keymgmts, EVP_KEYMGMT_free);
+    OPENSSL_free(data->object_type);
+    OPENSSL_free(data);
+}
+
+DEFINE_STACK_OF_CSTRING()
+
+struct collected_data_st {
+    struct deser_EVP_PKEY_data_st *process_data;
+    STACK_OF(OPENSSL_CSTRING) *names;
+
+    unsigned int error_occured:1;
+};
+
+static void collect_keymgmt(EVP_KEYMGMT *keymgmt, void *arg)
+{
+    struct collected_data_st *data = arg;
+
+    if (data->error_occured)
+        return;
+
+    data->error_occured = 1;         /* Assume the worst */
+
+    if (!EVP_KEYMGMT_up_ref(keymgmt) /* ref++ */)
+        return;
+    if (sk_EVP_KEYMGMT_push(data->process_data->keymgmts, keymgmt) <= 0) {
+        EVP_KEYMGMT_free(keymgmt); /* ref-- */
+        return;
+    }
+
+    data->error_occured = 0;         /* All is good now */
+}
+
+static void collect_name(const char *name, void *arg)
+{
+    struct collected_data_st *data = arg;
+
+    if (data->error_occured)
+        return;
+
+    data->error_occured = 1;         /* Assume the worst */
+
+    if (sk_OPENSSL_CSTRING_push(data->names, name) <= 0)
+        return;
+
+    data->error_occured = 0;         /* All is good now */
+}
+
+OSSL_DESERIALIZER_CTX *
+OSSL_DESERIALIZER_CTX_new_by_EVP_PKEY(EVP_PKEY **pkey,
+                                      const char *input_type,
+                                      OPENSSL_CTX *libctx,
+                                      const char *propquery)
+{
+    OSSL_DESERIALIZER_CTX *ctx = NULL;
+    struct collected_data_st *data = NULL;
+    size_t i, end_i;
+
+    if ((ctx = OSSL_DESERIALIZER_CTX_new()) == NULL
+        || (data = OPENSSL_zalloc(sizeof(*data))) == NULL
+        || (data->process_data =
+            OPENSSL_zalloc(sizeof(*data->process_data))) == NULL
+        || (data->process_data->keymgmts
+            = sk_EVP_KEYMGMT_new_null()) == NULL
+        || (data->names = sk_OPENSSL_CSTRING_new_null()) == NULL) {
+        ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    data->process_data->object = (void **)pkey;
+    OSSL_DESERIALIZER_CTX_set_input_type(ctx, input_type);
+
+    /* First, find all keymgmts to form goals */
+    EVP_KEYMGMT_do_all_provided(libctx, collect_keymgmt, data);
+
+    if (data->error_occured)
+        goto err;
+
+    /*
+     * Then, use the names of those keymgmts to find the first set of
+     * derializers.
+     */
+    ERR_set_mark();
+    end_i = sk_EVP_KEYMGMT_num(data->process_data->keymgmts);
+    for (i = 0; i < end_i; i++) {
+        EVP_KEYMGMT *keymgmt =
+            sk_EVP_KEYMGMT_value(data->process_data->keymgmts, i);
+        size_t j;
+        OSSL_DESERIALIZER *deser = NULL;
+
+        EVP_KEYMGMT_names_do_all(keymgmt, collect_name, data);
+
+        for (j = sk_OPENSSL_CSTRING_num(data->names);
+             j-- > 0 && deser == NULL;) {
+            const char *name = sk_OPENSSL_CSTRING_pop(data->names);
+
+            ERR_set_mark();
+            deser = OSSL_DESERIALIZER_fetch(libctx, name, propquery);
+            ERR_pop_to_mark();
+        }
+
+        /*
+         * The names in |data->names| aren't allocated for the stack,
+         * so we can simply clear it and let it be re-used.
+         */
+        sk_OPENSSL_CSTRING_zero(data->names);
+
+        /*
+         * If we found a matching serializer, try to add it to the context.
+         */
+        if (deser != NULL) {
+            (void)OSSL_DESERIALIZER_CTX_add_deserializer(ctx, deser);
+            OSSL_DESERIALIZER_free(deser);
+        }
+    }
+    /* If we found no deserializers to match the keymgmts, we err */
+    if (OSSL_DESERIALIZER_CTX_num_deserializers(ctx) == 0) {
+        ERR_clear_last_mark();
+        goto err;
+    }
+    ERR_pop_to_mark();
+
+    /* Finally, collect extra deserializers based on what we already have */
+    (void)OSSL_DESERIALIZER_CTX_add_extra(ctx, libctx, propquery);
+
+    if (!OSSL_DESERIALIZER_CTX_set_finalizer(ctx, deser_finalize_EVP_PKEY,
+                                             deser_clean_EVP_PKEY,
+                                             data->process_data))
+        goto err;
+
+    data->process_data = NULL;
+ err:
+    if (data->process_data != NULL)
+        sk_EVP_KEYMGMT_pop_free(data->process_data->keymgmts,
+                                EVP_KEYMGMT_free);
+    OPENSSL_free(data->process_data);
+    sk_OPENSSL_CSTRING_free(data->names);
+    OPENSSL_free(data);
+    return ctx;
+}
diff --git a/crypto/serializer/serdes_pass.c b/crypto/serializer/serdes_pass.c
new file mode 100644
index 0000000000..8a33af5e9a
--- /dev/null
+++ b/crypto/serializer/serdes_pass.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright 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
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/err.h>
+#include <openssl/ui.h>
+#include <openssl/core_names.h>
+#include "internal/cryptlib.h"
+#include "serializer_local.h"
+
+/* Passphrase callbacks for any who need it */
+
+/*
+ * First, define the generic passphrase function that supports both
+ * outgoing (with passphrase verify) and incoming (without passphrase
+ * verify) passphrase reading.
+ */
+static int do_passphrase(char *pass, size_t pass_size, size_t *pass_len,
+                         const OSSL_PARAM params[], void *arg, int verify,
+                         const UI_METHOD *ui_method, void *ui_data, int errlib)
+{
+    const OSSL_PARAM *p;
+    const char *prompt_info = NULL;
+    char *prompt = NULL, *vpass = NULL;
+    int prompt_idx = -1, verify_idx = -1;
+    UI *ui = NULL;
+    int ret = 0;
+
+    if (!ossl_assert(pass != NULL && pass_size != 0 && pass_len != NULL)) {
+        ERR_raise(errlib, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    if ((p = OSSL_PARAM_locate_const(params,
+                                     OSSL_PASSPHRASE_PARAM_INFO)) != NULL) {
+        if (p->data_type != OSSL_PARAM_UTF8_STRING)
+            return 0;
+        prompt_info = p->data;
+    }
+
+    if ((ui = UI_new()) == NULL) {
+        ERR_raise(errlib, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    UI_set_method(ui, ui_method);
+    UI_add_user_data(ui, ui_data);
+
+    /* Get an application constructed prompt */
+    prompt = UI_construct_prompt(ui, "pass phrase", prompt_info);
+   if (prompt == NULL) {
+        ERR_raise(errlib, ERR_R_MALLOC_FAILURE);
+        goto end;
+    }
+
+    prompt_idx = UI_add_input_string(ui, prompt,
+                                     UI_INPUT_FLAG_DEFAULT_PWD,
+                                     pass, 0, pass_size - 1) - 1;
+    if (prompt_idx < 0) {
+        ERR_raise(errlib, ERR_R_UI_LIB);
+        goto end;
+    }
+
+    if (verify) {
+        /* Get a buffer for verification prompt */
+        vpass = OPENSSL_zalloc(pass_size);
+        if (vpass == NULL) {
+            ERR_raise(errlib, ERR_R_MALLOC_FAILURE);
+            goto end;
+        }
+        verify_idx = UI_add_verify_string(ui, prompt,
+                                          UI_INPUT_FLAG_DEFAULT_PWD,
+                                          vpass, 0, pass_size - 1,
+                                          pass) - 1;
+        if (verify_idx < 0) {
+            ERR_raise(errlib, ERR_R_UI_LIB);
+            goto end;
+        }
+    }
+
+    switch (UI_process(ui)) {
+    case -2:
+        ERR_raise(errlib, ERR_R_INTERRUPTED_OR_CANCELLED);
+        break;
+    case -1:
+        ERR_raise(errlib, ERR_R_UI_LIB);
+        break;
+    default:
+        *pass_len = (size_t)UI_get_result_length(ui, prompt_idx);
+        ret = 1;
+        break;
+    }
+
+ end:
+    OPENSSL_free(vpass);
+    OPENSSL_free(prompt);
+    UI_free(ui);
+    return ret;
+}
+
+/*
+ * Serializers typically want to get an outgoing passphrase, while
+ * deserializers typically want to get en incoming passphrase.
+ */
+int ossl_serializer_passphrase_out_cb(char *pass, size_t pass_size,
+                                      size_t *pass_len,
+                                      const OSSL_PARAM params[], void *arg)
+{
+    OSSL_SERIALIZER_CTX *ctx = arg;
+
+    if (!ossl_assert(ctx != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    return do_passphrase(pass, pass_size, pass_len, params, arg, 1,
+                         ctx->ui_method, ctx->ui_data,
+                         ERR_LIB_OSSL_SERIALIZER);
+}
+
+int ossl_deserializer_passphrase_in_cb(char *pass, size_t pass_size,
+                                       size_t *pass_len,
+                                       const OSSL_PARAM params[], void *arg)
+{
+    OSSL_DESERIALIZER_CTX *ctx = arg;
+
+    if (!ossl_assert(ctx != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    if (ctx->cached_passphrase != NULL) {
+        size_t len = ctx->cached_passphrase_len;
+
+        if (len > pass_size)
+            len = pass_size;
+        memcpy(pass, ctx->cached_passphrase, len);
+        *pass_len = len;
+        return 1;
+    } else {
+        if ((ctx->cached_passphrase = OPENSSL_zalloc(pass_size)) == NULL) {
+            ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+    }
+    if (do_passphrase(pass, pass_size, pass_len, params, arg, 0,
+                      ctx->ui_method, ctx->ui_data,
+                      ERR_LIB_OSSL_DESERIALIZER)) {
+        memcpy(ctx->cached_passphrase, pass, *pass_len);
+        ctx->cached_passphrase_len = *pass_len;
+        return 1;
+    }
+    return 0;
+}
diff --git a/crypto/serializer/serializer_local.h b/crypto/serializer/serializer_local.h
index 970c7c5585..acf600c285 100644
--- a/crypto/serializer/serializer_local.h
+++ b/crypto/serializer/serializer_local.h
@@ -9,17 +9,23 @@
 
 #include <openssl/core_dispatch.h>
 #include <openssl/types.h>
+#include <openssl/safestack.h>
+#include <openssl/serializer.h>
+#include <openssl/deserializer.h>
 #include "internal/cryptlib.h"
 #include "internal/refcount.h"
 
-struct ossl_serializer_st {
+struct ossl_serdes_base_st {
     OSSL_PROVIDER *prov;
     int id;
     const char *propdef;
 
     CRYPTO_REF_COUNT refcnt;
     CRYPTO_RWLOCK *lock;
+};
 
+struct ossl_serializer_st {
+    struct ossl_serdes_base_st base;
     OSSL_FUNC_serializer_newctx_fn *newctx;
     OSSL_FUNC_serializer_freectx_fn *freectx;
     OSSL_FUNC_serializer_set_ctx_params_fn *set_ctx_params;
@@ -28,15 +34,31 @@ struct ossl_serializer_st {
     OSSL_FUNC_serializer_serialize_object_fn *serialize_object;
 };
 
+struct ossl_deserializer_st {
+    struct ossl_serdes_base_st base;
+    OSSL_FUNC_deserializer_newctx_fn *newctx;
+    OSSL_FUNC_deserializer_freectx_fn *freectx;
+    OSSL_FUNC_deserializer_get_params_fn *get_params;
+    OSSL_FUNC_deserializer_gettable_params_fn *gettable_params;
+    OSSL_FUNC_deserializer_set_ctx_params_fn *set_ctx_params;
+    OSSL_FUNC_deserializer_settable_ctx_params_fn *settable_ctx_params;
+    OSSL_FUNC_deserializer_deserialize_fn *deserialize;
+    OSSL_FUNC_deserializer_export_object_fn *export_object;
+};
+
 struct ossl_serializer_ctx_st {
     OSSL_SERIALIZER *ser;
     void *serctx;
 
     int selection;
 
-    /*
-     * |object| is the libcrypto object to handle.
-     * |do_output| must have intimate knowledge of this object.
+    /*-
+     * Output / serializing data, used by OSSL_SERIALIZER_to_{bio,fp}
+     *
+     * |object|         is the libcrypto object to handle.
+     * |do_output|      performs the actual serialization.
+     *
+     * |do_output| must have intimate knowledge of |object|.
      */
     const void *object;
     int (*do_output)(OSSL_SERIALIZER_CTX *ctx, BIO *out);
@@ -50,3 +72,58 @@ struct ossl_serializer_ctx_st {
      */
     UI_METHOD *allocated_ui_method;
 };
+
+struct ossl_deserializer_instance_st {
+    OSSL_DESERIALIZER *deser;    /* Never NULL */
+    void *deserctx;              /* Never NULL */
+    const char *input_type;      /* Never NULL */
+};
+
+DEFINE_STACK_OF(OSSL_DESERIALIZER_INSTANCE)
+
+struct ossl_deserializer_ctx_st {
+    /*
+     * The caller may know the input type of the data they pass.  If not,
+     * this will remain NULL and the deserializing functionality will start
+     * with trying to deserialize with any desserializer in |deser_insts|,
+     * regardless of their respective input type.
+     */
+    const char *start_input_type;
+
+    /*
+     * Deserializers that are components of any current deserialization path.
+     */
+    STACK_OF(OSSL_DESERIALIZER_INSTANCE) *deser_insts;
+
+    /*
+     * The finalizer of a deserialization, and its caller argument.
+     */
+    OSSL_DESERIALIZER_FINALIZER *finalizer;
+    OSSL_DESERIALIZER_CLEANER *cleaner;
+    void *finalize_arg;
+
+    /* For any function that needs a passphrase reader */
+    const UI_METHOD *ui_method;
+    void *ui_data;
+    /*
+     * if caller used OSSL_SERIALIZER_CTX_set_passphrase_cb(), we need
+     * intermediary storage.
+     */
+    UI_METHOD *allocated_ui_method;
+    /*
+     * Because the same input may pass through more than one deserializer,
+     * we cache any passphrase passed to us.  The desrializing processor
+     * must clear this at the end of a run.
+     */
+    unsigned char *cached_passphrase;
+    size_t cached_passphrase_len;
+};
+
+/* Passphrase callbacks, found in serdes_pass.c */
+
+/*
+ * Serializers typically want to get an outgoing passphrase, while
+ * deserializers typically want to get en incoming passphrase.
+ */
+OSSL_PASSPHRASE_CALLBACK ossl_serializer_passphrase_out_cb;
+OSSL_PASSPHRASE_CALLBACK ossl_deserializer_passphrase_in_cb;
diff --git a/crypto/serializer/serializer_meth.c b/crypto/serializer/serializer_meth.c
index d7c98891e4..c2ff1c0dca 100644
--- a/crypto/serializer/serializer_meth.c
+++ b/crypto/serializer/serializer_meth.c
@@ -29,13 +29,13 @@ static OSSL_SERIALIZER *ossl_serializer_new(void)
     OSSL_SERIALIZER *ser = NULL;
 
     if ((ser = OPENSSL_zalloc(sizeof(*ser))) == NULL
-        || (ser->lock = CRYPTO_THREAD_lock_new()) == NULL) {
+        || (ser->base.lock = CRYPTO_THREAD_lock_new()) == NULL) {
         OSSL_SERIALIZER_free(ser);
         ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_MALLOC_FAILURE);
         return NULL;
     }
 
-    ser->refcnt = 1;
+    ser->base.refcnt = 1;
 
     return ser;
 }
@@ -44,7 +44,7 @@ int OSSL_SERIALIZER_up_ref(OSSL_SERIALIZER *ser)
 {
     int ref = 0;
 
-    CRYPTO_UP_REF(&ser->refcnt, &ref, ser->lock);
+    CRYPTO_UP_REF(&ser->base.refcnt, &ref, ser->base.lock);
     return 1;
 }
 
@@ -55,11 +55,11 @@ void OSSL_SERIALIZER_free(OSSL_SERIALIZER *ser)
     if (ser == NULL)
         return;
 
-    CRYPTO_DOWN_REF(&ser->refcnt, &ref, ser->lock);
+    CRYPTO_DOWN_REF(&ser->base.refcnt, &ref, ser->base.lock);
     if (ref > 0)
         return;
-    ossl_provider_free(ser->prov);
-    CRYPTO_THREAD_lock_free(ser->lock);
+    ossl_provider_free(ser->base.prov);
+    CRYPTO_THREAD_lock_free(ser->base.lock);
     OPENSSL_free(ser);
 }
 
@@ -165,8 +165,8 @@ static void *serializer_from_dispatch(int id, const OSSL_ALGORITHM *algodef,
 
     if ((ser = ossl_serializer_new()) == NULL)
         return NULL;
-    ser->id = id;
-    ser->propdef = algodef->property_definition;
+    ser->base.id = id;
+    ser->base.propdef = algodef->property_definition;
 
     for (; fns->function_id != 0; fns++) {
         switch (fns->function_id) {
@@ -220,7 +220,7 @@ static void *serializer_from_dispatch(int id, const OSSL_ALGORITHM *algodef,
         return NULL;
     }
 
-    ser->prov = prov;
+    ser->base.prov = prov;
     return ser;
 }
 
@@ -348,7 +348,7 @@ const OSSL_PROVIDER *OSSL_SERIALIZER_provider(const OSSL_SERIALIZER *ser)
         return 0;
     }
 
-    return ser->prov;
+    return ser->base.prov;
 }
 
 const char *OSSL_SERIALIZER_properties(const OSSL_SERIALIZER *ser)
@@ -358,7 +358,7 @@ const char *OSSL_SERIALIZER_properties(const OSSL_SERIALIZER *ser)
         return 0;
     }
 
-    return ser->propdef;
+    return ser->base.propdef;
 }
 
 int OSSL_SERIALIZER_number(const OSSL_SERIALIZER *ser)
@@ -368,16 +368,16 @@ int OSSL_SERIALIZER_number(const OSSL_SERIALIZER *ser)
         return 0;
     }
 
-    return ser->id;
+    return ser->base.id;
 }
 
 int OSSL_SERIALIZER_is_a(const OSSL_SERIALIZER *ser, const char *name)
 {
-    if (ser->prov != NULL) {
-        OPENSSL_CTX *libctx = ossl_provider_library_context(ser->prov);
+    if (ser->base.prov != NULL) {
+        OPENSSL_CTX *libctx = ossl_provider_library_context(ser->base.prov);
         OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx);
 
-        return ossl_namemap_name2num(namemap, name) == ser->id;
+        return ossl_namemap_name2num(namemap, name) == ser->base.id;
     }
     return 0;
 }
@@ -433,11 +433,11 @@ void OSSL_SERIALIZER_names_do_all(const OSSL_SERIALIZER *ser,
     if (ser == NULL)
         return;
 
-    if (ser->prov != NULL) {
-        OPENSSL_CTX *libctx = ossl_provider_library_context(ser->prov);
+    if (ser->base.prov != NULL) {
+        OPENSSL_CTX *libctx = ossl_provider_library_context(ser->base.prov);
         OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx);
 
-        ossl_namemap_doall_names(namemap, ser->id, fn, data);
+        ossl_namemap_doall_names(namemap, ser->base.id, fn, data);
     }
 }
 
diff --git a/crypto/serializer/serializer_pkey.c b/crypto/serializer/serializer_pkey.c
index 1e7fc3eafb..6e24ed73f0 100644
--- a/crypto/serializer/serializer_pkey.c
+++ b/crypto/serializer/serializer_pkey.c
@@ -71,7 +71,7 @@ int OSSL_SERIALIZER_CTX_set_passphrase_ui(OSSL_SERIALIZER_CTX *ctx,
     return 1;
 }
 
-int OSSL_SERIALIZER_CTX_set_passphrase_cb(OSSL_SERIALIZER_CTX *ctx, int enc,
+int OSSL_SERIALIZER_CTX_set_passphrase_cb(OSSL_SERIALIZER_CTX *ctx,
                                           pem_password_cb *cb, void *cbarg)
 {
     if (!ossl_assert(ctx != NULL)) {
@@ -83,7 +83,7 @@ int OSSL_SERIALIZER_CTX_set_passphrase_cb(OSSL_SERIALIZER_CTX *ctx, int enc,
     if (cb == NULL)
         return 1;
     ctx->ui_method =
-        ctx->allocated_ui_method = UI_UTIL_wrap_read_pem_callback(cb, enc);
+        ctx->allocated_ui_method = UI_UTIL_wrap_read_pem_callback(cb, 1);
     ctx->ui_data = cbarg;
 
     return ctx->ui_method != NULL;
@@ -107,110 +107,6 @@ static void cache_serializers(const char *name, void *data)
         d->error = 1;
 }
 
-/*
- * Support for OSSL_SERIALIZER_CTX_new_by_TYPE and OSSL_SERIALIZER_to_bio:
- * Passphrase callbacks
- */
-
-/*
- * First, we define the generic passphrase function that supports both
- * outgoing (with passphrase verify) and incoming (without passphrase verify)
- * passphrase reading.
- */
-static int serializer_passphrase(char *pass, size_t pass_size,
-                                 size_t *pass_len, int verify,
-                                 const OSSL_PARAM params[], void *arg)
-{
-    OSSL_SERIALIZER_CTX *ctx = arg;
-    const OSSL_PARAM *p;
-    const char *prompt_info = NULL;
-    char *prompt = NULL, *vpass = NULL;
-    int prompt_idx = -1, verify_idx = -1;
-    UI *ui = NULL;
-    int ret = 0;
-
-    if (!ossl_assert(ctx != NULL && pass != NULL
-                    && pass_size != 0 && pass_len != NULL)) {
-        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_PASSED_NULL_PARAMETER);
-        return 0;
-    }
-
-    if ((p = OSSL_PARAM_locate_const(params,
-                                     OSSL_PASSPHRASE_PARAM_INFO)) != NULL) {
-        if (p->data_type != OSSL_PARAM_UTF8_STRING)
-            return 0;
-        prompt_info = p->data;
-    }
-
-    if ((ui = UI_new()) == NULL) {
-        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_MALLOC_FAILURE);
-        return 0;
-    }
-
-    UI_set_method(ui, ctx->ui_method);
-    UI_add_user_data(ui, ctx->ui_data);
-
-    /* Get an application constructed prompt */
-    prompt = UI_construct_prompt(ui, "pass phrase", prompt_info);
-   if (prompt == NULL) {
-        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_MALLOC_FAILURE);
-        goto end;
-    }
-
-    prompt_idx = UI_add_input_string(ui, prompt,
-                                     UI_INPUT_FLAG_DEFAULT_PWD,
-                                     pass, 0, pass_size - 1) - 1;
-    if (prompt_idx < 0) {
-        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_UI_LIB);
-        goto end;
-    }
-
-    if (verify) {
-        /* Get a buffer for verification prompt */
-        vpass = OPENSSL_zalloc(pass_size);
-        if (vpass == NULL) {
-            ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_MALLOC_FAILURE);
-            goto end;
-        }
-        verify_idx = UI_add_verify_string(ui, prompt,
-                                          UI_INPUT_FLAG_DEFAULT_PWD,
-                                          vpass, 0, pass_size - 1,
-                                          pass) - 1;
-        if (verify_idx < 0) {
-            ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_UI_LIB);
-            goto end;
-        }
-    }
-
-    switch (UI_process(ui)) {
-    case -2:
-        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_INTERRUPTED_OR_CANCELLED);
-        break;
-    case -1:
-        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_UI_LIB);
-        break;
-    default:
-        *pass_len = (size_t)UI_get_result_length(ui, prompt_idx);
-        ret = 1;
-        break;
-    }
-
- end:
-    OPENSSL_free(vpass);
-    OPENSSL_free(prompt);
-    UI_free(ui);
-    return ret;
-}
-
-/* Ensure correct function definition for outgoing passphrase reader */
-static OSSL_PASSPHRASE_CALLBACK serializer_passphrase_out_cb;
-static int serializer_passphrase_out_cb(char *pass, size_t pass_size,
-                                        size_t *pass_len,
-                                        const OSSL_PARAM params[], void *arg)
-{
-    return serializer_passphrase(pass, pass_size, pass_len, 1, params, arg);
-}
-
 /*
  * Support for OSSL_SERIALIZER_to_bio:
  * writing callback for the OSSL_PARAM (the implementation doesn't have
@@ -229,7 +125,7 @@ static int serializer_write_cb(const OSSL_PARAM params[], void *arg)
     BIO *out = write_data->out;
 
     return ctx->ser->serialize_data(ctx->serctx, params, (OSSL_CORE_BIO *)out,
-                                    serializer_passphrase_out_cb, ctx);
+                                    ossl_serializer_passphrase_out_cb, ctx);
 }
 
 /*
@@ -266,7 +162,7 @@ static int serializer_EVP_PKEY_to_bio(OSSL_SERIALIZER_CTX *ctx, BIO *out)
 
     return ctx->ser->serialize_object(ctx->serctx, keydata,
                                       (OSSL_CORE_BIO *)out,
-                                      serializer_passphrase_out_cb, ctx);
+                                      ossl_serializer_passphrase_out_cb, ctx);
 }
 
 /*
diff --git a/doc/man3/OSSL_DESERIALIZER.pod b/doc/man3/OSSL_DESERIALIZER.pod
new file mode 100644
index 0000000000..5562a8122b
--- /dev/null
+++ b/doc/man3/OSSL_DESERIALIZER.pod
@@ -0,0 +1,146 @@
+=pod
+
+=head1 NAME
+
+OSSL_DESERIALIZER,
+OSSL_DESERIALIZER_fetch,
+OSSL_DESERIALIZER_up_ref,
+OSSL_DESERIALIZER_free,
+OSSL_DESERIALIZER_provider,
+OSSL_DESERIALIZER_properties,
+OSSL_DESERIALIZER_is_a,
+OSSL_DESERIALIZER_number,
+OSSL_DESERIALIZER_do_all_provided,
+OSSL_DESERIALIZER_names_do_all,
+OSSL_DESERIALIZER_gettable_params,
+OSSL_DESERIALIZER_get_params
+- Deserializer method routines
+
+=head1 SYNOPSIS
+
+ #include <openssl/deserializer.h>
+
+ typedef struct ossl_deserializer_st OSSL_DESERIALIZER;
+
+ OSSL_DESERIALIZER *OSSL_DESERIALIZER_fetch(OPENSSL_CTX *ctx, const char *name,
+                                            const char *properties);
+ int OSSL_DESERIALIZER_up_ref(OSSL_DESERIALIZER *deserializer);
+ void OSSL_DESERIALIZER_free(OSSL_DESERIALIZER *deserializer);
+ const OSSL_PROVIDER *OSSL_DESERIALIZER_provider(const OSSL_DESERIALIZER
+                                                 *deserializer);
+ const char *OSSL_DESERIALIZER_properties(const OSSL_DESERIALIZER *deser);
+ int OSSL_DESERIALIZER_is_a(const OSSL_DESERIALIZER *deserializer,
+                            const char *name);
+ int OSSL_DESERIALIZER_number(const OSSL_DESERIALIZER *deserializer);
+ void OSSL_DESERIALIZER_do_all_provided(OPENSSL_CTX *libctx,
+                                        void (*fn)(OSSL_DESERIALIZER *deserializer,
+                                                   void *arg),
+                                        void *arg);
+ void OSSL_DESERIALIZER_names_do_all(const OSSL_DESERIALIZER *deserializer,
+                                     void (*fn)(const char *name, void *data),
+                                     void *data);
+ const OSSL_PARAM *OSSL_DESERIALIZER_gettable_params(OSSL_DESERIALIZER *deser);
+ int OSSL_DESERIALIZER_get_params(OSSL_DESERIALIZER_CTX *ctx,
+                                  const OSSL_PARAM params[]);
+
+=head1 DESCRIPTION
+
+B<OSSL_DESERIALIZER> is a method for deserializers, which know how to
+deserialize serialized data into an object of some type that the rest
+of OpenSSL knows how to handle.
+
+OSSL_DESERIALIZER_fetch() looks for an algorithm within the provider that
+has been loaded into the B<OPENSSL_CTX> given by I<ctx>, having the
+name given by I<name> and the properties given by I<properties>.
+The I<name> determines what type of object the fetched deserializer
+method is expected to be able to deserialize, and the properties are
+used to determine the expected output type.
+For known properties and the values they may have, please have a look
+in L<provider-serializer(7)/Names and properties>.
+
+OSSL_DESERIALIZER_up_ref() increments the reference count for the given
+I<deserializer>.
+
+OSSL_DESERIALIZER_free() decrements the reference count for the given
+I<deserializer>, and when the count reaches zero, frees it.
+
+OSSL_DESERIALIZER_provider() returns the provider of the given
+I<deserializer>.
+
+OSSL_DESERIALIZER_properties() returns the property definition associated
+with the given I<deserializer>.
+
+OSSL_DESERIALIZER_is_a() checks if I<deserializer> is an implementation
+of an algorithm that's identifiable with I<name>.
+
+OSSL_DESERIALIZER_number() returns the internal dynamic number assigned
+to the given I<deserializer>.
+
+OSSL_DESERIALIZER_names_do_all() traverses all names for the given
+I<deserializer>, and calls I<fn> with each name and I<data>.
+
+OSSL_DESERIALIZER_do_all_provided() traverses all serializer
+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_DESERIALIZER_gettable_params() returns an L<OSSL_PARAM(3)>
+array of parameter descriptors.
+
+OSSL_DESERIALIZER_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 RETURN VALUES
+
+OSSL_DESERIALIZER_fetch() returns a pointer to an OSSL_DESERIALIZER object,
+or NULL on error.
+
+OSSL_DESERIALIZER_up_ref() returns 1 on success, or 0 on error.
+
+OSSL_DESERIALIZER_free() doesn't return any value.
+
+OSSL_DESERIALIZER_provider() returns a pointer to a provider object, or
+NULL on error.
+
+OSSL_DESERIALIZER_properties() returns a pointer to a property
+definition string, or NULL on error.
+
+OSSL_DESERIALIZER_is_a() returns 1 if I<deserializer> was identifiable,
+otherwise 0.
+
+OSSL_DESERIALIZER_number() returns an integer.
+
+=head1 NOTES
+
+OSSL_DESERIALIZER_fetch() may be called implicitly by other fetching
+functions, using the same library context and properties.
+Any other API that uses keys will typically do this.
+
+=begin comment TODO(3.0) Add examples!
+
+=head1 EXAMPLES
+
+Text, because pod2xxx doesn't like empty sections
+
+=end comment
+
+=head1 SEE ALSO
+
+L<provider(7)>, L<OSSL_DESERIALIZER_CTX(3)>, L<OSSL_DESERIALIZER_from_bio(3)>,
+L<OSSL_DESERIALIZER_CTX_new_by_EVP_PKEY(3)>, L<OPENSSL_CTX(3)>
+
+=head1 HISTORY
+
+The functions described here were added in OpenSSL 3.0.
+
+=head1 COPYRIGHT
+
+Copyright 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
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
diff --git a/doc/man3/OSSL_DESERIALIZER_CTX.pod b/doc/man3/OSSL_DESERIALIZER_CTX.pod
new file mode 100644
index 0000000000..413584f8dc
--- /dev/null
+++ b/doc/man3/OSSL_DESERIALIZER_CTX.pod
@@ -0,0 +1,74 @@
+=pod
+
+=head1 NAME
+
+OSSL_DESERIALIZER_CTX,
+OSSL_DESERIALIZER_CTX_new,
+OSSL_DESERIALIZER_settable_ctx_params,
+OSSL_DESERIALIZER_CTX_set_params,
+OSSL_DESERIALIZER_CTX_free
+- Serializer context routines
+
+=head1 SYNOPSIS
+
+ #include <openssl/deserializer.h>
+
+ typedef struct ossl_deserializer_ctx_st OSSL_DESERIALIZER_CTX;
+
+ OSSL_DESERIALIZER_CTX *OSSL_DESERIALIZER_CTX_new(OPENSSL_CTX *libctx);
+ const OSSL_PARAM *OSSL_DESERIALIZER_settable_ctx_params(OSSL_DESERIALIZER *deser);
+ int OSSL_DESERIALIZER_CTX_set_params(OSSL_DESERIALIZER_CTX *ctx,
+                                      const OSSL_PARAM params[]);
+ void OSSL_DESERIALIZER_CTX_free(OSSL_DESERIALIZER_CTX *ctx);
+
+=head1 DESCRIPTION
+
+B<OSSL_DESERIALIZER_CTX> is a context with which B<OSSL_DESERIALIZER>
+operations are performed.  The context typically holds values, both
+internal and supplied by the application, which are useful for the
+implementations supplied by providers.
+
+OSSL_DESERIALIZER_CTX_new() creates a new empty B<OSSL_DESERIALIZER_CTX>.
+
+OSSL_DESERIALIZER_settable_ctx_params() returns an L<OSSL_PARAM(3)>
+array of parameter descriptors.
+
+OSSL_DESERIALIZER_CTX_set_params() attempts to set parameters specified
+with an L<OSSL_PARAM(3)> array I<params>.  These parameters are passed
+to all deserializers that have been added to the I<ctx> so far.
+Parameters that an implementation doesn't recognise should be ignored
+by it.
+
+OSSL_DESERIALIZER_CTX_free() frees the given context I<ctx>.
+
+=head1 RETURN VALUES
+
+OSSL_DESERIALIZER_CTX_new() returns a pointer to a
+B<OSSL_DESERIALIZER_CTX>, or NULL if the context structure couldn't be
+allocated.
+
+OSSL_DESERIALIZER_settable_ctx_params() returns an L<OSSL_PARAM(3)>
+array, or NULL if none is available.
+
+OSSL_DESERIALIZER_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.
+
+=head1 SEE ALSO
+
+L<provider(7)>, L<OSSL_DESERIALIZER(3)>, L<OSSL_DESERIALIZER_from_bio(3)>
+
+=head1 HISTORY
+
+The functions described here were added in OpenSSL 3.0.
+
+=head1 COPYRIGHT
+
+Copyright 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
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
diff --git a/doc/man3/OSSL_DESERIALIZER_CTX_new_by_EVP_PKEY.pod b/doc/man3/OSSL_DESERIALIZER_CTX_new_by_EVP_PKEY.pod
new file mode 100644
index 0000000000..9ed4e5992e
--- /dev/null
+++ b/doc/man3/OSSL_DESERIALIZER_CTX_new_by_EVP_PKEY.pod
@@ -0,0 +1,117 @@
+=pod
+
+=head1 NAME
+
+OSSL_DESERIALIZER_CTX_new_by_EVP_PKEY,
+OSSL_DESERIALIZER_CTX_set_cipher,
+OSSL_DESERIALIZER_CTX_set_passphrase,
+OSSL_DESERIALIZER_CTX_set_passphrase_cb,
+OSSL_DESERIALIZER_CTX_set_passphrase_ui
+- Deserializer routines to deserialize EVP_PKEYs
+
+=head1 SYNOPSIS
+
+ #include <openssl/deserializer.h>
+
+ OSSL_DESERIALIZER_CTX *
+ OSSL_DESERIALIZER_CTX_new_by_EVP_PKEY(const EVP_PKEY *pkey,
+                                       const char *input_type,
+                                       OPENSSL_CTX *libctx,
+                                       const char *propquery);
+
+ int OSSL_DESERIALIZER_CTX_set_cipher(OSSL_DESERIALIZER_CTX *ctx,
+                                      const char *cipher_name,
+                                      const char *propquery);
+ int OSSL_DESERIALIZER_CTX_set_passphrase(OSSL_DESERIALIZER_CTX *ctx,
+                                          const unsigned char *kstr,
+                                          size_t klen);
+ int OSSL_DESERIALIZER_CTX_set_passphrase_cb(OSSL_DESERIALIZER_CTX *ctx,
+                                             pem_password_cb *cb, void *cbarg);
+ int OSSL_DESERIALIZER_CTX_set_passphrase_ui(OSSL_DESERIALIZER_CTX *ctx,
+                                             const UI_METHOD *ui_method,
+                                             void *ui_data);
+
+=head1 DESCRIPTION
+
+OSSL_DESERIALIZER_CTX_new_by_EVP_PKEY() is a utility function that
+creates a B<OSSL_DESERIALIZER_CTX>, finds all applicable deserializer
+implementations and sets them up, so all the caller has to do next is
+call functions like OSSL_DESERIALIZE_from_bio().
+
+Internally OSSL_DESERIALIZER_CTX_new_by_EVP_PKEY() searches for all
+available L<EVP_KEYMGMT(3)> implementations, and then builds a list of all
+potential deserializer implementations that may be able to process the
+serialized input into data suitable for B<EVP_PKEY>s.  All these
+implementations are implicitly fetched using I<libctx> and I<propquery>.
+
+The search of deserializer implementations can be limited with
+I<input_type>, which specifies a starting input type.  This is further
+explained in L<OSSL_DESERIALIZER_CTX_set_input_type(3)>.
+
+If no suitable deserializer was found, OSSL_DESERIALIZER_CTX_new_by_EVP_PKEY()
+still creates a B<OSSL_DESERIALIZER_CTX>, but with no associated
+deserializer (L<OSSL_DESERIALIZER_CTX_num_deserializers(3)> returns
+zero).  This helps the caller distinguish between an error when
+creating the B<OSSL_DESERIALIZER_CTX>, and the lack the deserializer
+support and act accordingly.
+
+OSSL_DESERIALIZER_CTX_set_cipher() tells the implementation what cipher
+should be used to decrypt serialized 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 cipher
+directly itself, 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.  I<cipher_name> may also
+be NULL, which will result in failure if the serialized input is an
+encrypted key.
+
+OSSL_DESERIALIZER_CTX_set_passphrase() gives the implementation a
+pass phrase to use when decrypting the serialized private key.
+Alternatively, a pass phrase callback may be specified with the
+following functions.
+
+OSSL_DESERIALIZER_CTX_set_passphrase_cb() and
+OSSL_DESERIALIZER_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,
+through an internal B<OSSL_PASSPHRASE_CALLBACK> function.
+
+=head1 RETURN VALUES
+
+OSSL_DESERIALIZER_CTX_new_by_EVP_PKEY() returns a pointer to a
+B<OSSL_DESERIALIZER_CTX>, or NULL if it couldn't be created.
+
+OSSL_DESERIALIZER_CTX_set_cipher(),
+OSSL_DESERIALIZER_CTX_set_passphrase(),
+OSSL_DESERIALIZER_CTX_set_passphrase_cb(), and
+OSSL_DESERIALIZER_CTX_set_passphrase_ui() 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_DESERIALIZER_CTX_new_by_EVP_PKEY() matches the type
+name, thus making for the naming pattern
+B<OSSL_DESERIALIZER_CTX_new_by_I<TYPE>>() when new types are handled.
+
+=head1 SEE ALSO
+
+L<provider(7)>, L<OSSL_DESERIALIZER(3)>, L<OSSL_DESERIALIZER_CTX(3)>
+
+=head1 HISTORY
+
+The functions described here were added in OpenSSL 3.0.
+
+=head1 COPYRIGHT
+
+Copyright 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
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
diff --git a/doc/man3/OSSL_DESERIALIZER_from_bio.pod b/doc/man3/OSSL_DESERIALIZER_from_bio.pod
new file mode 100644
index 0000000000..8c372a6cf6
--- /dev/null
+++ b/doc/man3/OSSL_DESERIALIZER_from_bio.pod
@@ -0,0 +1,253 @@
+=pod
+
+=head1 NAME
+
+OSSL_DESERIALIZER_from_bio,
+OSSL_DESERIALIZER_from_fp,
+OSSL_DESERIALIZER_CTX_set_input_type,
+OSSL_DESERIALIZER_CTX_add_deserializer,
+OSSL_DESERIALIZER_CTX_add_extra,
+OSSL_DESERIALIZER_CTX_num_deserializers,
+OSSL_DESERIALIZER_INSTANCE,
+OSSL_DESERIALIZER_FINALIZER,
+OSSL_DESERIALIZER_CLEANER,
+OSSL_DESERIALIZER_CTX_set_finalizer,
+OSSL_DESERIALIZER_export,
+OSSL_DESERIALIZER_INSTANCE_deserializer,
+OSSL_DESERIALIZER_INSTANCE_deserializer_ctx
+- Routines to perform a deserialization
+
+=head1 SYNOPSIS
+
+ #include <openssl/deserializer.h>
+
+ int OSSL_DESERIALIZER_from_bio(OSSL_DESERIALIZER_CTX *ctx, BIO *in);
+ int OSSL_DESERIALIZER_from_fp(OSSL_DESERIALIZER_CTX *ctx, FILE *fp);
+
+ int OSSL_DESERIALIZER_CTX_set_input_type(OSSL_DESERIALIZER_CTX *ctx,
+                                          const char *input_type);
+ int OSSL_DESERIALIZER_CTX_add_deserializer(OSSL_DESERIALIZER_CTX *ctx,
+                                            OSSL_DESERIALIZER *deser);
+ int OSSL_DESERIALIZER_CTX_add_extra(OSSL_DESERIALIZER_CTX *ctx);
+ int OSSL_DESERIALIZER_CTX_num_deserializers(OSSL_DESERIALIZER_CTX *ctx);
+
+ typedef struct ossl_deserializer_instance_st OSSL_DESERIALIZER_INSTANCE;
+ typedef int (OSSL_DESERIALIZER_FINALIZER)
+     (OSSL_DESERIALIZER_INSTANCE *deser_inst,
+      const OSSL_PARAM *params, void *finalize_arg);
+ typedef void (OSSL_DESERIALIZER_CLEANER)(void *finalize_arg);
+
+ int OSSL_DESERIALIZER_CTX_set_finalizer(OSSL_DESERIALIZER_CTX *ctx,
+                                         OSSL_DESRIALIZER_FINALIZER *finalizer,
+                                         OSSL_DESERIALIZER_CLEANER *cleaner,
+                                         void *finalize_arg);
+
+ int OSSL_DESERIALIZER_export(OSSL_DESERIALIZER_INSTANCE *deser_inst,
+                              void *reference, size_t reference_sz,
+                              OSSL_CALLBACK *export_cb, void *export_cbarg);
+
+ OSSL_DESERIALIZER *OSSL_DESERIALIZER_INSTANCE_deserializer
+     (OSSL_DESERIALIZER_INSTANCE *deser_inst);
+ void *OSSL_DESERIALIZER_INSTANCE_deserializer_ctx
+     (OSSL_DESERIALIZER_INSTANCE *deser_inst);
+
+Feature availability macros:
+
+=over 4
+
+=item OSSL_DESERIALIZER_from_fp() is only available when B<OPENSSL_NO_STDIO>
+is undefined.
+
+=back
+
+=head1 DESCRIPTION
+
+The B<OSSL_DESERIALIZER_CTX> holds data about multiple deserializers, 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
+deserializers, so the output from one can become the input for another.
+This allows having generic format deserializers such as PEM to DER, as well
+as more specialized deserializers 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_DESERIALIZER_CTX_add_extra(), which will
+stop adding on more deserializer implementations when it has already added
+those that take the specified input type, and OSSL_DESERIALIZER_from_bio(),
+which will only start the deserializing process with the deserializer
+implementations that take that input type.  For example, if the input type
+is set to C<DER>, a PEM to DER deserializer 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_DESERIALIZER_from_bio()
+will simply try with one deserializer implementation after the other, and
+thereby discover what kind of input the caller gave it.
+
+For every deserialization done, even intermediary, a I<finalizer>
+provided by the caller is used to attempt to "finalize" the current
+deserialization output, which is always a provider side object of some
+sort, by "wrapping" it into some appropriate type or structure that
+the caller knows how to handle.  Exactly what this "wrapping" consists
+of is entirely at the discretion of the I<finalizer>.
+
+B<OSSL_DESERIALIZER_INSTANCE> is an opaque structure that contains
+data about the deserializer that was just used, and that may be
+useful for the I<finalizer>.  There are some functions to extract data
+from this type, described further down.
+
+=head2 Functions
+
+OSSL_DESERIALIZER_from_bio() runs the deserialization process for the
+context I<ctx>, with the input coming from the B<BIO> I<in>.  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 deserializer!
+
+OSSL_DESERIALIZER_from_fp() does the same thing as OSSL_DESERIALIZER_from_bio(),
+except that the input is coming from the B<FILE> I<fp>.
+
+OSSL_DESERIALIZER_CTX_add_deserializer() populates the B<OSSL_DESERIALIZER_CTX>
+I<ctx> with a deserializer, to be used to attempt to deserialize some
+serialized input.
+
+OSSL_DESERIALIZER_CTX_add_extra() finds deserializers that generate
+input for already added deserializers, and adds them as well.  This is
+used to build deserializer chains.
+
+OSSL_DESERIALIZER_CTX_set_input_type() sets the starting input type.  This
+limits the deserializer chains to be considered, as explained in the general
+description above.
+
+OSSL_DESERIALIZER_CTX_num_deserializers() gets the number of
+deserializers currently added to the context I<ctx>.
+
+OSSL_DESERIALIZER_CTX_set_finalizer() sets the I<finalizer> function
+together with the caller argument for the finalizer, I<finalize_arg>,
+as well as I<cleaner>, the function to clean up I<finalize_arg> when
+the deserialization has concluded.
+
+OSSL_DESERIALIZER_export() is a fallback function for I<finalizers>
+that can't use the data they get directly for diverse reasons.  It
+takes the same deserialize instance I<deser_inst> that the
+I<finalizer> got and an object I<reference>, unpacks the object that
+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_DESERIALIZER_INSTANCE_deserializer() can be used to get the
+deserializer method from a deserializer instance I<deser_inst>.
+
+OSSL_DESERIALIZER_INSTANCE_deserializer-ctx() can be used to get the
+deserializer method's provider context from a deserializer instance
+I<deser_inst>.
+
+=head2 Finalizer
+
+The I<finalizer> gets the following arguments:
+
+=over 4
+
+=item I<deser_inst>
+
+The B<OSSL_DESERIALIZER_INSTANCE> for the deserializer from which
+I<finalizer> gets its data.
+
+=item I<params>
+
+The data produced by the deserializer, further described below.
+
+=item I<finalize_arg>
+
+The pointer that was set with OSSL_DESERIALIZE_CTX_set_finalizer() as
+I<finalize_arg>.
+
+=back
+
+The I<finalizer> is expected to return 1 when the data it receives can
+be "finalized", otherwise 0.
+
+The globally known parameters that I<finalize> can get in I<params>
+are:
+
+=over 4
+
+=item "data-type" (B<OSSL_DESERIALIZER_PARAM_DATA_TYPE>) <UTF8 string>
+
+This is a detected content type that some deserializers may provide.
+For example, PEM input sometimes has a type specified in its header,
+and some deserializers may add that information as this parameter.
+This is an optional parameter, but may be useful for extra checks in
+the I<finalizer>.
+
+=item "data" (B<OSSL_DESERIALIZER_PARAM_DATA>) <octet string>
+
+The deserialized data itself, as an octet string.  This is produced by
+deserializers when it's possible to pass an object in this form.  Most
+often, this is simply meant to be passed to the next deserializer in a
+chain, but could be considered final data as well, at the discretion
+of the I<finalizer>.
+
+=item "reference" (B<OSSL_DESERIALIZER_PARAM_DATA>) <octet string>
+
+The deserialized data itself, as a reference to an object.  The
+reference itself is an octet string, and can be passed to other
+operations and functions within the same provider as the one that
+provides I<deser>.
+
+=back
+
+At least one of "data" or "reference" must be present, and it's
+possible that both can be.  A I<finalizer> should choose to use the
+"reference" parameter if possible, otherwise the "data" parameter.
+
+If it's not possible to use the "reference" parameter, but that's
+still what a I<finalizer> wants to do, it is possible to use
+OSSL_DESERIALIZER_export() as a fallback.
+
+=head1 RETURN VALUES
+
+OSSL_DESERIALIZER_from_bio() and OSSL_DESERIALIZER_from_fp() return 1 on
+success, or 0 on failure.
+
+OSSL_DESERIALIZER_CTX_add_deserializer(),
+OSSL_DESERIALIZER_CTX_add_extra(), and
+OSSL_DESERIALIZER_CTX_set_finalizer() return 1 on success, or 0 on
+failure.
+
+OSSL_DESERIALIZER_CTX_num_deserializers() returns the current
+number of deserializers.  It returns 0 if I<ctx> is NULL.
+
+OSSL_DESERIALIZER_export() returns 1 on success, or 0 on failure.
+
+OSSL_DESERIALIZER_INSTANCE_deserializer() returns an
+B<OSSL_DESERIALIZER> pointer on success, or NULL on failure.
+
+OSSL_DESERIALIZER_INSTANCE_deserializer_ctx() returns a provider
+context pointer on success, or NULL on failure.>
+
+=begin comment TODO(3.0) Add examples!
+
+=head1 EXAMPLES
+
+Text, because pod2xxx doesn't like empty sections
+
+=end comment
+
+=head1 SEE ALSO
+
+L<provider(7)>, L<OSSL_DESERIALIZER_CTX(3)>
+
+=head1 HISTORY
+
+The functions described here were added in OpenSSL 3.0.
+
+=head1 COPYRIGHT
+
+Copyright 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
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
diff --git a/doc/man3/OSSL_SERIALIZER_CTX_new_by_EVP_PKEY.pod b/doc/man3/OSSL_SERIALIZER_CTX_new_by_EVP_PKEY.pod
index 43dddbce02..5797ab1caa 100644
--- a/doc/man3/OSSL_SERIALIZER_CTX_new_by_EVP_PKEY.pod
+++ b/doc/man3/OSSL_SERIALIZER_CTX_new_by_EVP_PKEY.pod
@@ -31,7 +31,7 @@ OSSL_SERIALIZER_Parameters_TO_TEXT_PQ
  int OSSL_SERIALIZER_CTX_set_passphrase(OSSL_SERIALIZER_CTX *ctx,
                                         const unsigned char *kstr,
                                         size_t klen);
- int OSSL_SERIALIZER_CTX_set_passphrase_cb(OSSL_SERIALIZER_CTX *ctx, int enc,
+ int OSSL_SERIALIZER_CTX_set_passphrase_cb(OSSL_SERIALIZER_CTX *ctx,
                                            pem_password_cb *cb, void *cbarg);
  int OSSL_SERIALIZER_CTX_set_passphrase_ui(OSSL_SERIALIZER_CTX *ctx,
                                            const UI_METHOD *ui_method,
diff --git a/doc/man7/provider-keymgmt.pod b/doc/man7/provider-keymgmt.pod
index aa2be41acc..43743798ac 100644
--- a/doc/man7/provider-keymgmt.pod
+++ b/doc/man7/provider-keymgmt.pod
@@ -18,6 +18,7 @@ provider-keymgmt - The KEYMGMT library E<lt>-E<gt> provider functions
  void *OSSL_FUNC_keymgmt_new(void *provctx);
  void OSSL_FUNC_keymgmt_free(void *keydata);
 
+ /* Generation, a more complex constructor */
  void *OSSL_FUNC_keymgmt_gen_init(void *provctx, int selection);
  int OSSL_FUNC_keymgmt_gen_set_template(void *genctx, void *template);
  int OSSL_FUNC_keymgmt_gen_set_params(void *genctx, const OSSL_PARAM params[]);
@@ -25,6 +26,9 @@ provider-keymgmt - The KEYMGMT library E<lt>-E<gt> provider functions
  void *OSSL_FUNC_keymgmt_gen(void *genctx, OSSL_CALLBACK *cb, void *cbarg);
  void OSSL_FUNC_keymgmt_gen_cleanup(void *genctx);
 
+ /* Key loading by object reference, also a constructor */
+ void *OSSL_FUNC_keymgmt_load(const void *reference, size_t *reference_sz);
+
  /* Key object information */
  int OSSL_FUNC_keymgmt_get_params(void *keydata, OSSL_PARAM params[]);
  const OSSL_PARAM *OSSL_FUNC_keymgmt_gettable_params(void);
@@ -94,6 +98,8 @@ macros in L<openssl-core_dispatch.h(7)>, as follows:
  OSSL_FUNC_keymgmt_gen                  OSSL_FUNC_KEYMGMT_GEN
  OSSL_FUNC_keymgmt_gen_cleanup          OSSL_FUNC_KEYMGMT_GEN_CLEANUP
 
+ OSSL_FUNC_keymgmt_load                 OSSL_FUNC_KEYMGMT_LOAD
+
  OSSL_FUNC_keymgmt_get_params           OSSL_FUNC_KEYMGMT_GET_PARAMS
  OSSL_FUNC_keymgmt_gettable_params      OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS
  OSSL_FUNC_keymgmt_set_params           OSSL_FUNC_KEYMGMT_SET_PARAMS
@@ -209,8 +215,8 @@ OSSL_FUNC_keymgmt_free() should free the passed I<keydata>.
 
 OSSL_FUNC_keymgmt_gen_init(), OSSL_FUNC_keymgmt_gen_set_template(),
 OSSL_FUNC_keymgmt_gen_set_params(), OSSL_FUNC_keymgmt_gen_settable_params(),
-OSSL_FUNC_keymgmt_gen() and OSSL_FUNC_keymgmt_gen_cleanup() work together as a more
-elaborate context based key object constructor.
+OSSL_FUNC_keymgmt_gen() and OSSL_FUNC_keymgmt_gen_cleanup() work together as a
+more elaborate context based key object constructor.
 
 OSSL_FUNC_keymgmt_gen_init() should create the key object generation context
 and initialize it with I<selections>, which will determine what kind
@@ -238,10 +244,15 @@ progresses.
 OSSL_FUNC_keymgmt_gen_cleanup() should clean up and free the key object
 generation context I<genctx>
 
-At least one of OSSL_FUNC_keymgmt_new() and OSSL_FUNC_keymgmt_gen() are mandatory,
-as well as OSSL_FUNC_keymgmt_free().  Additionally, if OSSL_FUNC_keymgmt_gen() is
-present, OSSL_FUNC_keymgmt_gen_init() and OSSL_FUNC_keymgmt_gen_cleanup() must be
-present as well.
+OSSL_FUNC_keymgmt_load() creates a provider side key object based on a
+I<reference> object with a size of I<reference_sz> bytes, that only the
+provider knows how to interpret, but that may come from other operations.
+Outside the provider, this reference is simply an array of bytes.
+
+At least one of OSSL_FUNC_keymgmt_new(), OSSL_FUNC_keymgmt_gen() and
+OSSL_FUNC_keymgmt_load() are mandatory, as well as OSSL_FUNC_keymgmt_free().
+Additionally, if OSSL_FUNC_keymgmt_gen() is present, OSSL_FUNC_keymgmt_gen_init()
+and OSSL_FUNC_keymgmt_gen_cleanup() must be present as well.
 
 =head2 Key Object Information Functions
 
diff --git a/include/crypto/evp.h b/include/crypto/evp.h
index ec2fc1c805..2e85b56266 100644
--- a/include/crypto/evp.h
+++ b/include/crypto/evp.h
@@ -657,6 +657,21 @@ void evp_pkey_free_legacy(EVP_PKEY *x);
 /*
  * KEYMGMT utility functions
  */
+
+/*
+ * Key import structure and helper function, to be used as an export callback
+ */
+struct evp_keymgmt_util_try_import_data_st {
+    EVP_KEYMGMT *keymgmt;
+    void *keydata;
+
+    int selection;
+};
+int evp_keymgmt_util_try_import(const OSSL_PARAM params[], void *arg);
+int evp_keymgmt_util_assign_pkey(EVP_PKEY *pkey, EVP_KEYMGMT *keymgmt,
+                                 void *keydata);
+EVP_PKEY *evp_keymgmt_util_make_pkey(EVP_KEYMGMT *keymgmt, void *keydata);
+
 void *evp_keymgmt_util_export_to_provider(EVP_PKEY *pk, EVP_KEYMGMT *keymgmt);
 size_t evp_keymgmt_util_find_operation_cache_index(EVP_PKEY *pk,
                                                    EVP_KEYMGMT *keymgmt);
@@ -698,6 +713,9 @@ void *evp_keymgmt_gen(const EVP_KEYMGMT *keymgmt, void *genctx,
                       OSSL_CALLBACK *cb, void *cbarg);
 void evp_keymgmt_gen_cleanup(const EVP_KEYMGMT *keymgmt, void *genctx);
 
+void *evp_keymgmt_load(const EVP_KEYMGMT *keymgmt,
+                       const void *objref, size_t objref_sz);
+
 int evp_keymgmt_has(const EVP_KEYMGMT *keymgmt, void *keyddata, int selection);
 int evp_keymgmt_validate(const EVP_KEYMGMT *keymgmt, void *keydata,
                          int selection);
diff --git a/include/crypto/serializer.h b/include/crypto/serializer.h
index c40788f78b..df4953994f 100644
--- a/include/crypto/serializer.h
+++ b/include/crypto/serializer.h
@@ -11,3 +11,5 @@
 
 OSSL_SERIALIZER *ossl_serializer_fetch_by_number(OPENSSL_CTX *libctx, int id,
                                                  const char *properties);
+OSSL_DESERIALIZER *ossl_deserializer_fetch_by_number(OPENSSL_CTX *libctx, int id,
+                                                     const char *properties);
diff --git a/include/internal/cryptlib.h b/include/internal/cryptlib.h
index fba1d5643f..d0dd6fe2b5 100644
--- a/include/internal/cryptlib.h
+++ b/include/internal/cryptlib.h
@@ -156,10 +156,11 @@ typedef struct ossl_ex_data_global_st {
 # define OPENSSL_CTX_THREAD_EVENT_HANDLER_INDEX     8
 # define OPENSSL_CTX_FIPS_PROV_INDEX                9
 # define OPENSSL_CTX_SERIALIZER_STORE_INDEX        10
-# define OPENSSL_CTX_SELF_TEST_CB_INDEX            11
-# define OPENSSL_CTX_BIO_PROV_INDEX                12
-# define OPENSSL_CTX_GLOBAL_PROPERTIES             13
-# define OPENSSL_CTX_MAX_INDEXES                   14
+# define OPENSSL_CTX_DESERIALIZER_STORE_INDEX      11
+# define OPENSSL_CTX_SELF_TEST_CB_INDEX            12
+# define OPENSSL_CTX_BIO_PROV_INDEX                13
+# define OPENSSL_CTX_GLOBAL_PROPERTIES             14
+# define OPENSSL_CTX_MAX_INDEXES                   15
 
 typedef struct openssl_ctx_method {
     void *(*new_func)(OPENSSL_CTX *ctx);
diff --git a/include/openssl/core_dispatch.h b/include/openssl/core_dispatch.h
index 0feb38b417..c3f6c88f46 100644
--- a/include/openssl/core_dispatch.h
+++ b/include/openssl/core_dispatch.h
@@ -135,6 +135,9 @@ OSSL_CORE_MAKE_FUNC(void,
 #define OSSL_FUNC_BIO_FREE                    44
 #define OSSL_FUNC_BIO_VPRINTF                 45
 #define OSSL_FUNC_BIO_VSNPRINTF               46
+#define OSSL_FUNC_BIO_PUTS                    47
+#define OSSL_FUNC_BIO_GETS                    48
+
 
 OSSL_CORE_MAKE_FUNC(OSSL_CORE_BIO *, BIO_new_file, (const char *filename,
                                                     const char *mode))
@@ -143,6 +146,8 @@ OSSL_CORE_MAKE_FUNC(int, BIO_read_ex, (OSSL_CORE_BIO *bio, void *data,
                                        size_t data_len, size_t *bytes_read))
 OSSL_CORE_MAKE_FUNC(int, BIO_write_ex, (OSSL_CORE_BIO *bio, const void *data,
                                         size_t data_len, size_t *written))
+OSSL_CORE_MAKE_FUNC(int, BIO_gets, (OSSL_CORE_BIO *bio, char *buf, int size))
+OSSL_CORE_MAKE_FUNC(int, BIO_puts, (OSSL_CORE_BIO *bio, const char *str))
 OSSL_CORE_MAKE_FUNC(int, BIO_free, (OSSL_CORE_BIO *bio))
 OSSL_CORE_MAKE_FUNC(int, BIO_vprintf, (OSSL_CORE_BIO *bio, const char *format,
                                        va_list args))
@@ -185,8 +190,9 @@ OSSL_CORE_MAKE_FUNC(int, provider_get_capabilities, (void *provctx,
 # define OSSL_OP_ASYM_CIPHER                        13
 /* New section for non-EVP operations */
 # define OSSL_OP_SERIALIZER                         20
+# define OSSL_OP_DESERIALIZER                       21
 /* Highest known operation number */
-# define OSSL_OP__HIGHEST                           20
+# define OSSL_OP__HIGHEST                           21
 
 /* Digests */
 
@@ -477,6 +483,11 @@ OSSL_CORE_MAKE_FUNC(void *, keymgmt_gen,
                     (void *genctx, OSSL_CALLBACK *cb, void *cbarg))
 OSSL_CORE_MAKE_FUNC(void, keymgmt_gen_cleanup, (void *genctx))
 
+/* Key loading by object reference */
+# define OSSL_FUNC_KEYMGMT_LOAD                        8
+OSSL_CORE_MAKE_FUNC(void *, keymgmt_load,
+                    (const void *reference, size_t reference_sz))
+
 /* Basic key object destruction */
 # define OSSL_FUNC_KEYMGMT_FREE                       10
 OSSL_CORE_MAKE_FUNC(void, keymgmt_free, (void *keydata))
@@ -689,7 +700,7 @@ OSSL_CORE_MAKE_FUNC(int, asym_cipher_set_ctx_params,
 OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, asym_cipher_settable_ctx_params,
                     (void))
 
-/* Serializers */
+/* Serializers and deserializers */
 # define OSSL_FUNC_SERIALIZER_NEWCTX                1
 # define OSSL_FUNC_SERIALIZER_FREECTX               2
 # define OSSL_FUNC_SERIALIZER_SET_CTX_PARAMS        3
@@ -710,6 +721,31 @@ OSSL_CORE_MAKE_FUNC(int, serializer_serialize_object,
                     (void *ctx, void *obj, OSSL_CORE_BIO *out,
                      OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg))
 
+# define OSSL_FUNC_DESERIALIZER_NEWCTX              1
+# define OSSL_FUNC_DESERIALIZER_FREECTX             2
+# define OSSL_FUNC_DESERIALIZER_GET_PARAMS          3
+# define OSSL_FUNC_DESERIALIZER_GETTABLE_PARAMS     4
+# define OSSL_FUNC_DESERIALIZER_SET_CTX_PARAMS      5
+# define OSSL_FUNC_DESERIALIZER_SETTABLE_CTX_PARAMS 6
+# define OSSL_FUNC_DESERIALIZER_DESERIALIZE        10
+# define OSSL_FUNC_DESERIALIZER_EXPORT_OBJECT      11
+OSSL_CORE_MAKE_FUNC(void *, deserializer_newctx, (void *provctx))
+OSSL_CORE_MAKE_FUNC(void, deserializer_freectx, (void *ctx))
+OSSL_CORE_MAKE_FUNC(int, deserializer_get_params, (OSSL_PARAM params[]))
+OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, deserializer_gettable_params, (void))
+OSSL_CORE_MAKE_FUNC(int, deserializer_set_ctx_params,
+                    (void *ctx, const OSSL_PARAM params[]))
+OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, deserializer_settable_ctx_params,
+                    (void))
+
+OSSL_CORE_MAKE_FUNC(int, deserializer_deserialize,
+                    (void *ctx, OSSL_CORE_BIO *in,
+                     OSSL_CALLBACK *metadata_cb, void *metadata_cbarg,
+                     OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg))
+OSSL_CORE_MAKE_FUNC(int, deserializer_export_object,
+                    (void *ctx, const void *objref, size_t objref_sz,
+                     OSSL_CALLBACK *export_cb, void *export_cbarg))
+
 # ifdef __cplusplus
 }
 # endif
diff --git a/include/openssl/core_names.h b/include/openssl/core_names.h
index 702ee6a6ed..9ce4115a89 100644
--- a/include/openssl/core_names.h
+++ b/include/openssl/core_names.h
@@ -397,13 +397,21 @@ extern "C" {
 #define OSSL_ASYM_CIPHER_PARAM_TLS_NEGOTIATED_VERSION   "tls-negotiated-version"
 
 /*
- * Serializer parameters
+ * Serializer / deserializer parameters
  */
 /* The passphrase may be passed as a utf8 string or an octet string */
 #define OSSL_SERIALIZER_PARAM_CIPHER            OSSL_ALG_PARAM_CIPHER
 #define OSSL_SERIALIZER_PARAM_PROPERTIES        OSSL_ALG_PARAM_PROPERTIES
 #define OSSL_SERIALIZER_PARAM_PASS              "passphrase"
 
+#define OSSL_DESERIALIZER_PARAM_CIPHER          OSSL_ALG_PARAM_CIPHER
+#define OSSL_DESERIALIZER_PARAM_PROPERTIES      OSSL_ALG_PARAM_PROPERTIES
+#define OSSL_DESERIALIZER_PARAM_PASS            "passphrase"
+#define OSSL_DESERIALIZER_PARAM_INPUT_TYPE      "input-type"
+#define OSSL_DESERIALIZER_PARAM_DATA_TYPE       "data-type"
+#define OSSL_DESERIALIZER_PARAM_DATA            "data"
+#define OSSL_DESERIALIZER_PARAM_REFERENCE       "reference"
+
 /* Passphrase callback parameters */
 #define OSSL_PASSPHRASE_PARAM_INFO              "info"
 
diff --git a/include/openssl/deserializer.h b/include/openssl/deserializer.h
new file mode 100644
index 0000000000..d54e47915d
--- /dev/null
+++ b/include/openssl/deserializer.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright 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
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef OPENSSL_DESERIALIZER_H
+# define OPENSSL_DESERIALIZER_H
+# pragma once
+
+# include <openssl/opensslconf.h>
+
+# ifndef OPENSSL_NO_STDIO
+#  include <stdio.h>
+# endif
+# include <stdarg.h>
+# include <stddef.h>
+# include <openssl/deserializererr.h>
+# include <openssl/types.h>
+# include <openssl/core.h>
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+OSSL_DESERIALIZER *OSSL_DESERIALIZER_fetch(OPENSSL_CTX *libctx,
+                                           const char *name,
+                                           const char *properties);
+int OSSL_DESERIALIZER_up_ref(OSSL_DESERIALIZER *ser);
+void OSSL_DESERIALIZER_free(OSSL_DESERIALIZER *ser);
+
+const OSSL_PROVIDER *OSSL_DESERIALIZER_provider(const OSSL_DESERIALIZER *ser);
+const char *OSSL_DESERIALIZER_properties(const OSSL_DESERIALIZER *ser);
+int OSSL_DESERIALIZER_number(const OSSL_DESERIALIZER *ser);
+int OSSL_DESERIALIZER_is_a(const OSSL_DESERIALIZER *ser,
+                           const char *name);
+
+void OSSL_DESERIALIZER_do_all_provided(OPENSSL_CTX *libctx,
+                                       void (*fn)(OSSL_DESERIALIZER *ser,
+                                                  void *arg),
+                                       void *arg);
+void OSSL_DESERIALIZER_names_do_all(const OSSL_DESERIALIZER *ser,
+                                    void (*fn)(const char *name, void *data),
+                                    void *data);
+const OSSL_PARAM *OSSL_DESERIALIZER_gettable_params(OSSL_DESERIALIZER *deser);
+int OSSL_DESERIALIZER_get_params(OSSL_DESERIALIZER *deser, OSSL_PARAM params[]);
+
+const OSSL_PARAM *OSSL_DESERIALIZER_settable_ctx_params(OSSL_DESERIALIZER *ser);
+OSSL_DESERIALIZER_CTX *OSSL_DESERIALIZER_CTX_new(void);
+int OSSL_DESERIALIZER_CTX_set_params(OSSL_DESERIALIZER_CTX *ctx,
+                                     const OSSL_PARAM params[]);
+void OSSL_DESERIALIZER_CTX_free(OSSL_DESERIALIZER_CTX *ctx);
+
+/* Utilities that help set specific parameters */
+int OSSL_DESERIALIZER_CTX_set_cipher(OSSL_DESERIALIZER_CTX *ctx,
+                                     const char *cipher_name,
+                                     const char *propquery);
+int OSSL_DESERIALIZER_CTX_set_passphrase(OSSL_DESERIALIZER_CTX *ctx,
+                                         const unsigned char *kstr,
+                                         size_t klen);
+int OSSL_DESERIALIZER_CTX_set_passphrase_cb(OSSL_DESERIALIZER_CTX *ctx,
+                                            pem_password_cb *cb, void *cbarg);
+int OSSL_DESERIALIZER_CTX_set_passphrase_ui(OSSL_DESERIALIZER_CTX *ctx,
+                                            const UI_METHOD *ui_method,
+                                            void *ui_data);
+
+/*
+ * Utilities to read the object to deserialize, with the result sent to cb.
+ * These will discover all provided methods
+ */
+
+int OSSL_DESERIALIZER_CTX_set_input_type(OSSL_DESERIALIZER_CTX *ctx,
+                                         const char *input_type);
+int OSSL_DESERIALIZER_CTX_add_deserializer(OSSL_DESERIALIZER_CTX *ctx,
+                                           OSSL_DESERIALIZER *deser);
+int OSSL_DESERIALIZER_CTX_add_extra(OSSL_DESERIALIZER_CTX *ctx,
+                                    OPENSSL_CTX *libctx, const char *propq);
+int OSSL_DESERIALIZER_CTX_num_deserializers(OSSL_DESERIALIZER_CTX *ctx);
+
+typedef struct ossl_deserializer_instance_st OSSL_DESERIALIZER_INSTANCE;
+typedef int (OSSL_DESERIALIZER_FINALIZER)
+    (OSSL_DESERIALIZER_INSTANCE *deser_inst,
+     const OSSL_PARAM *params, void *finalize_arg);
+typedef void (OSSL_DESERIALIZER_CLEANER)(void *finalize_arg);
+
+int OSSL_DESERIALIZER_CTX_set_finalizer(OSSL_DESERIALIZER_CTX *ctx,
+                                        OSSL_DESERIALIZER_FINALIZER *finalizer,
+                                        OSSL_DESERIALIZER_CLEANER *cleaner,
+                                        void *finalize_arg);
+
+int OSSL_DESERIALIZER_export(OSSL_DESERIALIZER_INSTANCE *deser_inst,
+                             void *reference, size_t reference_sz,
+                             OSSL_CALLBACK *export_cb, void *export_cbarg);
+
+OSSL_DESERIALIZER *OSSL_DESERIALIZER_INSTANCE_deserializer
+    (OSSL_DESERIALIZER_INSTANCE *deser_inst);
+void *OSSL_DESERIALIZER_INSTANCE_deserializer_ctx
+    (OSSL_DESERIALIZER_INSTANCE *deser_inst);
+
+int OSSL_DESERIALIZER_from_bio(OSSL_DESERIALIZER_CTX *ctx, BIO *in);
+#ifndef OPENSSL_NO_STDIO
+int OSSL_DESERIALIZER_from_fp(OSSL_DESERIALIZER_CTX *ctx, FILE *in);
+#endif
+
+/*
+ * Create the OSSL_DESERIALIZER_CTX with an associated type.  This will perform
+ * an implicit OSSL_DESERIALIZER_fetch(), suitable for the object of that type.
+ */
+OSSL_DESERIALIZER_CTX *
+OSSL_DESERIALIZER_CTX_new_by_EVP_PKEY(EVP_PKEY **pkey, const char *input_type,
+                                      OPENSSL_CTX *libctx,
+                                      const char *propquery);
+
+# ifdef __cplusplus
+}
+# endif
+#endif
diff --git a/include/openssl/deserializererr.h b/include/openssl/deserializererr.h
new file mode 100644
index 0000000000..1c6573afb6
--- /dev/null
+++ b/include/openssl/deserializererr.h
@@ -0,0 +1,35 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-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
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef OPENSSL_OSSL_DESERIALIZERERR_H
+# define OPENSSL_OSSL_DESERIALIZERERR_H
+# pragma once
+
+# include <openssl/opensslconf.h>
+# include <openssl/symhacks.h>
+
+
+# ifdef  __cplusplus
+extern "C"
+# endif
+int ERR_load_OSSL_DESERIALIZER_strings(void);
+
+/*
+ * OSSL_DESERIALIZER function codes.
+ */
+# ifndef OPENSSL_NO_DEPRECATED_3_0
+# endif
+
+/*
+ * OSSL_DESERIALIZER reason codes.
+ */
+# define OSSL_DESERIALIZER_R_MISSING_GET_PARAMS           100
+
+#endif
diff --git a/include/openssl/err.h b/include/openssl/err.h
index a40d231ea0..fd3b93aa47 100644
--- a/include/openssl/err.h
+++ b/include/openssl/err.h
@@ -114,7 +114,8 @@ struct err_state_st {
 # define ERR_LIB_PROV            57
 # define ERR_LIB_CMP             58
 # define ERR_LIB_OSSL_SERIALIZER 59
-# define ERR_LIB_HTTP            60
+# define ERR_LIB_OSSL_DESERIALIZER 60
+# define ERR_LIB_HTTP            61
 
 # define ERR_LIB_USER            128
 
diff --git a/include/openssl/serializer.h b/include/openssl/serializer.h
index 8b24840786..50c85d617f 100644
--- a/include/openssl/serializer.h
+++ b/include/openssl/serializer.h
@@ -61,7 +61,7 @@ int OSSL_SERIALIZER_CTX_set_cipher(OSSL_SERIALIZER_CTX *ctx,
 int OSSL_SERIALIZER_CTX_set_passphrase(OSSL_SERIALIZER_CTX *ctx,
                                        const unsigned char *kstr,
                                        size_t klen);
-int OSSL_SERIALIZER_CTX_set_passphrase_cb(OSSL_SERIALIZER_CTX *ctx, int enc,
+int OSSL_SERIALIZER_CTX_set_passphrase_cb(OSSL_SERIALIZER_CTX *ctx,
                                           pem_password_cb *cb, void *cbarg);
 int OSSL_SERIALIZER_CTX_set_passphrase_ui(OSSL_SERIALIZER_CTX *ctx,
                                           const UI_METHOD *ui_method,
diff --git a/include/openssl/types.h b/include/openssl/types.h
index d43950a809..496f42a101 100644
--- a/include/openssl/types.h
+++ b/include/openssl/types.h
@@ -214,6 +214,8 @@ typedef int pem_password_cb (char *buf, int size, int rwflag, void *userdata);
 
 typedef struct ossl_serializer_st OSSL_SERIALIZER;
 typedef struct ossl_serializer_ctx_st OSSL_SERIALIZER_CTX;
+typedef struct ossl_deserializer_st OSSL_DESERIALIZER;
+typedef struct ossl_deserializer_ctx_st OSSL_DESERIALIZER_CTX;
 
 typedef struct ossl_self_test_st OSSL_SELF_TEST;
 
diff --git a/providers/common/bio_prov.c b/providers/common/bio_prov.c
index c193658c58..fc1f8b2b26 100644
--- a/providers/common/bio_prov.c
+++ b/providers/common/bio_prov.c
@@ -16,6 +16,8 @@ static OSSL_FUNC_BIO_new_file_fn *c_bio_new_file = NULL;
 static OSSL_FUNC_BIO_new_membuf_fn *c_bio_new_membuf = NULL;
 static OSSL_FUNC_BIO_read_ex_fn *c_bio_read_ex = NULL;
 static OSSL_FUNC_BIO_write_ex_fn *c_bio_write_ex = NULL;
+static OSSL_FUNC_BIO_gets_fn *c_bio_gets = NULL;
+static OSSL_FUNC_BIO_puts_fn *c_bio_puts = NULL;
 static OSSL_FUNC_BIO_free_fn *c_bio_free = NULL;
 static OSSL_FUNC_BIO_vprintf_fn *c_bio_vprintf = NULL;
 
@@ -39,6 +41,14 @@ int ossl_prov_bio_from_dispatch(const OSSL_DISPATCH *fns)
             if (c_bio_write_ex == NULL)
                 c_bio_write_ex = OSSL_FUNC_BIO_write_ex(fns);
             break;
+        case OSSL_FUNC_BIO_GETS:
+            if (c_bio_gets == NULL)
+                c_bio_gets = OSSL_FUNC_BIO_gets(fns);
+            break;
+        case OSSL_FUNC_BIO_PUTS:
+            if (c_bio_puts == NULL)
+                c_bio_puts = OSSL_FUNC_BIO_puts(fns);
+            break;
         case OSSL_FUNC_BIO_FREE:
             if (c_bio_free == NULL)
                 c_bio_free = OSSL_FUNC_BIO_free(fns);
@@ -83,6 +93,20 @@ int ossl_prov_bio_write_ex(OSSL_CORE_BIO *bio, const void *data, size_t data_len
     return c_bio_write_ex(bio, data, data_len, written);
 }
 
+int ossl_prov_bio_gets(OSSL_CORE_BIO *bio, char *buf, int size)
+{
+    if (c_bio_gets == NULL)
+        return -1;
+    return c_bio_gets(bio, buf, size);
+}
+
+int ossl_prov_bio_puts(OSSL_CORE_BIO *bio, const char *str)
+{
+    if (c_bio_puts == NULL)
+        return -1;
+    return c_bio_puts(bio, str);
+}
+
 int ossl_prov_bio_free(OSSL_CORE_BIO *bio)
 {
     if (c_bio_free == NULL)
@@ -134,16 +158,12 @@ static long bio_core_ctrl(BIO *bio, int cmd, long num, void *ptr)
 
 static int bio_core_gets(BIO *bio, char *buf, int size)
 {
-    /* We don't support this */
-    assert(0);
-    return -1;
+    return ossl_prov_bio_gets(BIO_get_data(bio), buf, size);
 }
 
 static int bio_core_puts(BIO *bio, const char *str)
 {
-    /* We don't support this */
-    assert(0);
-    return -1;
+    return ossl_prov_bio_puts(BIO_get_data(bio), str);
 }
 
 static int bio_core_new(BIO *bio)
diff --git a/providers/common/include/prov/bio.h b/providers/common/include/prov/bio.h
index c63f6b5da5..3cef89ce18 100644
--- a/providers/common/include/prov/bio.h
+++ b/providers/common/include/prov/bio.h
@@ -20,6 +20,8 @@ int ossl_prov_bio_read_ex(OSSL_CORE_BIO *bio, void *data, size_t data_len,
                           size_t *bytes_read);
 int ossl_prov_bio_write_ex(OSSL_CORE_BIO *bio, const void *data, size_t data_len,
                            size_t *written);
+int ossl_prov_bio_gets(OSSL_CORE_BIO *bio, char *buf, int size);
+int ossl_prov_bio_puts(OSSL_CORE_BIO *bio, const char *str);
 int ossl_prov_bio_free(OSSL_CORE_BIO *bio);
 int ossl_prov_bio_vprintf(OSSL_CORE_BIO *bio, const char *format, va_list ap);
 int ossl_prov_bio_printf(OSSL_CORE_BIO *bio, const char *format, ...);
diff --git a/providers/defltprov.c b/providers/defltprov.c
index d404585afd..7ab006ae83 100644
--- a/providers/defltprov.c
+++ b/providers/defltprov.c
@@ -530,6 +530,17 @@ static const OSSL_ALGORITHM deflt_serializer[] = {
     { "EC", "provider=default,fips=yes,format=pem,type=parameters",
       ec_param_pem_serializer_functions },
 #endif
+
+    { NULL, NULL, NULL }
+};
+
+static const OSSL_ALGORITHM deflt_deserializer[] = {
+    { "RSA", "provider=default,fips=yes,input=der",
+      der_to_rsa_deserializer_functions },
+
+    { "DER", "provider=default,fips=yes,input=pem",
+      pem_to_der_deserializer_functions },
+
     { NULL, NULL, NULL }
 };
 
@@ -559,6 +570,8 @@ static const OSSL_ALGORITHM *deflt_query(void *provctx, int operation_id,
         return deflt_asym_cipher;
     case OSSL_OP_SERIALIZER:
         return deflt_serializer;
+    case OSSL_OP_DESERIALIZER:
+        return deflt_deserializer;
     }
     return NULL;
 }
diff --git a/providers/implementations/include/prov/implementations.h b/providers/implementations/include/prov/implementations.h
index 0b32f3727c..4890f11969 100644
--- a/providers/implementations/include/prov/implementations.h
+++ b/providers/implementations/include/prov/implementations.h
@@ -358,3 +358,6 @@ extern const OSSL_DISPATCH ec_param_der_serializer_functions[];
 extern const OSSL_DISPATCH ec_priv_pem_serializer_functions[];
 extern const OSSL_DISPATCH ec_pub_pem_serializer_functions[];
 extern const OSSL_DISPATCH ec_param_pem_serializer_functions[];
+
+extern const OSSL_DISPATCH der_to_rsa_deserializer_functions[];
+extern const OSSL_DISPATCH pem_to_der_deserializer_functions[];
diff --git a/providers/implementations/keymgmt/rsa_kmgmt.c b/providers/implementations/keymgmt/rsa_kmgmt.c
index 5c6b52efaf..3231c020c9 100644
--- a/providers/implementations/keymgmt/rsa_kmgmt.c
+++ b/providers/implementations/keymgmt/rsa_kmgmt.c
@@ -23,6 +23,7 @@
 #include "prov/providercommon.h"
 #include "prov/provider_ctx.h"
 #include "crypto/rsa.h"
+#include "crypto/cryptlib.h"
 #include "internal/param_build_set.h"
 
 static OSSL_FUNC_keymgmt_new_fn rsa_newdata;
@@ -34,6 +35,7 @@ static OSSL_FUNC_keymgmt_gen_settable_params_fn rsa_gen_settable_params;
 static OSSL_FUNC_keymgmt_gen_settable_params_fn rsapss_gen_settable_params;
 static OSSL_FUNC_keymgmt_gen_fn rsa_gen;
 static OSSL_FUNC_keymgmt_gen_cleanup_fn rsa_gen_cleanup;
+static OSSL_FUNC_keymgmt_load_fn rsa_load;
 static OSSL_FUNC_keymgmt_free_fn rsa_freedata;
 static OSSL_FUNC_keymgmt_get_params_fn rsa_get_params;
 static OSSL_FUNC_keymgmt_gettable_params_fn rsa_gettable_params;
@@ -575,6 +577,20 @@ static void rsa_gen_cleanup(void *genctx)
     OPENSSL_free(gctx);
 }
 
+void *rsa_load(const void *reference, size_t reference_sz)
+{
+    RSA *rsa = NULL;
+
+    if (reference_sz == sizeof(rsa)) {
+        /* The contents of the reference is the address to our object */
+        rsa = *(RSA **)reference;
+        /* We grabbed, so we detach it */
+        *(RSA **)reference = NULL;
+        return rsa;
+    }
+    return NULL;
+}
+
 /* For any RSA key, we use the "RSA" algorithms regardless of sub-type. */
 static const char *rsapss_query_operation_name(int operation_id)
 {
@@ -590,6 +606,7 @@ const OSSL_DISPATCH rsa_keymgmt_functions[] = {
       (void (*)(void))rsa_gen_settable_params },
     { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))rsa_gen },
     { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))rsa_gen_cleanup },
+    { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))rsa_load },
     { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))rsa_freedata },
     { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))rsa_get_params },
     { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))rsa_gettable_params },
diff --git a/providers/implementations/serializers/build.info b/providers/implementations/serializers/build.info
index ffafbe38e5..bcfe9d4d4b 100644
--- a/providers/implementations/serializers/build.info
+++ b/providers/implementations/serializers/build.info
@@ -9,8 +9,9 @@ $DSA_GOAL=../../libimplementations.a
 $ECX_GOAL=../../libimplementations.a
 $EC_GOAL=../../libimplementations.a
 
-SOURCE[$SERIALIZER_GOAL]=serializer_common.c
+SOURCE[$SERIALIZER_GOAL]=serializer_common.c deserialize_common.c
 
+SOURCE[$RSA_GOAL]=deserialize_der2rsa.c deserialize_pem2der.c
 SOURCE[$RSA_GOAL]=serializer_rsa.c serializer_rsa_priv.c serializer_rsa_pub.c
 DEPEND[serializer_rsa.o]=../../common/include/prov/der_rsa.h
 
diff --git a/providers/implementations/serializers/deserialize_common.c b/providers/implementations/serializers/deserialize_common.c
new file mode 100644
index 0000000000..449d57b0a3
--- /dev/null
+++ b/providers/implementations/serializers/deserialize_common.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright 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
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/bio.h>
+#include <openssl/err.h>
+#include <openssl/buffer.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs12.h>
+#include "internal/cryptlib.h"
+#include "crypto/asn1.h"
+#include "prov/bio.h"               /* ossl_prov_bio_printf() */
+#include "prov/providercommonerr.h" /* PROV_R_READ_KEY */
+#include "serializer_local.h"
+
+int ossl_prov_read_der(PROV_CTX *provctx, OSSL_CORE_BIO *cin,
+                       unsigned char **data, long *len)
+{
+    BUF_MEM *mem = NULL;
+    BIO *in = bio_new_from_core_bio(provctx, cin);
+    int ok = (asn1_d2i_read_bio(in, &mem) >= 0);
+
+    if (ok) {
+        *data = (unsigned char *)mem->data;
+        *len = (long)mem->length;
+        OPENSSL_free(mem);
+    }
+    BIO_free(in);
+    return ok;
+}
+
+int ossl_prov_read_pem(PROV_CTX *provctx, OSSL_CORE_BIO *cin,
+                       char **pem_name, char **pem_header,
+                       unsigned char **data, long *len)
+{
+    BIO *in = bio_new_from_core_bio(provctx, cin);
+    int ok = (PEM_read_bio(in, pem_name, pem_header, data, len) > 0);
+
+    BIO_free(in);
+    return ok;
+}
+
+int ossl_prov_der_from_p8(unsigned char **new_der, long *new_der_len,
+                          unsigned char *input_der, long input_der_len,
+                          struct pkcs8_encrypt_ctx_st *ctx)
+{
+    const unsigned char *derp;
+    X509_SIG *p8 = NULL;
+    int ok = 0;
+
+    if (!ossl_assert(new_der != NULL && *new_der == NULL)
+        || !ossl_assert(new_der_len != NULL))
+        return 0;
+
+    if (ctx->cipher == NULL)
+        return 0;
+
+    derp = input_der;
+    if ((p8 = d2i_X509_SIG(NULL, &derp, input_der_len)) != NULL) {
+        char pbuf[PEM_BUFSIZE];
+        const void *pstr = ctx->cipher_pass;
+        size_t plen = ctx->cipher_pass_length;
+
+        if (pstr == NULL) {
+            pstr = pbuf;
+            if (!ctx->cb(pbuf, sizeof(pbuf), &plen, NULL, ctx->cbarg)) {
+                ERR_raise(ERR_LIB_PROV, PROV_R_READ_KEY);
+                pstr = NULL;
+            }
+        }
+
+        if (pstr != NULL) {
+            const X509_ALGOR *alg = NULL;
+            const ASN1_OCTET_STRING *oct = NULL;
+            int len = 0;
+
+            X509_SIG_get0(p8, &alg, &oct);
+            if (PKCS12_pbe_crypt(alg, pstr, plen, oct->data, oct->length,
+                                 new_der, &len, 0) != NULL)
+                ok = 1;
+            *new_der_len = len;
+        }
+    }
+    X509_SIG_free(p8);
+    return ok;
+}
diff --git a/providers/implementations/serializers/deserialize_der2rsa.c b/providers/implementations/serializers/deserialize_der2rsa.c
new file mode 100644
index 0000000000..6854c7efcb
--- /dev/null
+++ b/providers/implementations/serializers/deserialize_der2rsa.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright 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
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * RSA low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/crypto.h>
+#include <openssl/params.h>
+#include <openssl/x509.h>
+#include "prov/bio.h"
+#include "prov/implementations.h"
+#include "serializer_local.h"
+
+static OSSL_FUNC_deserializer_newctx_fn der2rsa_newctx;
+static OSSL_FUNC_deserializer_freectx_fn der2rsa_freectx;
+static OSSL_FUNC_deserializer_gettable_params_fn der2rsa_gettable_params;
+static OSSL_FUNC_deserializer_get_params_fn der2rsa_get_params;
+static OSSL_FUNC_deserializer_settable_ctx_params_fn der2rsa_settable_ctx_params;
+static OSSL_FUNC_deserializer_set_ctx_params_fn der2rsa_set_ctx_params;
+static OSSL_FUNC_deserializer_deserialize_fn der2rsa_deserialize;
+static OSSL_FUNC_deserializer_export_object_fn der2rsa_export_object;
+
+/*
+ * Context used for DER to RSA key deserialization.
+ */
+struct der2rsa_ctx_st {
+    PROV_CTX *provctx;
+
+    struct pkcs8_encrypt_ctx_st sc;
+};
+
+static void *der2rsa_newctx(void *provctx)
+{
+    struct der2rsa_ctx_st *ctx = OPENSSL_zalloc(sizeof(*ctx));
+
+    if (ctx != NULL) {
+        ctx->provctx = provctx;
+        /* -1 is the "whatever" indicator, i.e. the PKCS8 library default PBE */
+        ctx->sc.pbe_nid = -1;
+    }
+    return ctx;
+}
+
+static void der2rsa_freectx(void *vctx)
+{
+    struct der2rsa_ctx_st *ctx = vctx;
+
+    EVP_CIPHER_free(ctx->sc.cipher);
+    OPENSSL_clear_free(ctx->sc.cipher_pass, ctx->sc.cipher_pass_length);
+    OPENSSL_free(ctx);
+}
+
+static const OSSL_PARAM *der2rsa_gettable_params(void)
+{
+    static const OSSL_PARAM gettables[] = {
+        { OSSL_DESERIALIZER_PARAM_INPUT_TYPE, OSSL_PARAM_UTF8_PTR, NULL, 0, 0 },
+        OSSL_PARAM_END,
+    };
+
+    return gettables;
+}
+
+static int der2rsa_get_params(OSSL_PARAM params[])
+{
+    OSSL_PARAM *p;
+
+    p = OSSL_PARAM_locate(params, OSSL_DESERIALIZER_PARAM_INPUT_TYPE);
+    if (p != NULL && !OSSL_PARAM_set_utf8_ptr(p, "DER"))
+        return 0;
+
+    return 1;
+}
+
+
+static const OSSL_PARAM *der2rsa_settable_ctx_params(void)
+{
+    static const OSSL_PARAM settables[] = {
+        OSSL_PARAM_utf8_string(OSSL_DESERIALIZER_PARAM_CIPHER, NULL, 0),
+        OSSL_PARAM_utf8_string(OSSL_DESERIALIZER_PARAM_PROPERTIES, NULL, 0),
+        OSSL_PARAM_octet_string(OSSL_DESERIALIZER_PARAM_PASS, NULL, 0),
+        OSSL_PARAM_END,
+    };
+
+    return settables;
+}
+
+static int der2rsa_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+    struct der2rsa_ctx_st *ctx = vctx;
+    OPENSSL_CTX *libctx = PROV_CTX_get0_library_context(ctx->provctx);
+    const OSSL_PARAM *p;
+
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_DESERIALIZER_PARAM_CIPHER))
+        != NULL) {
+        const OSSL_PARAM *propsp =
+            OSSL_PARAM_locate_const(params, OSSL_DESERIALIZER_PARAM_PROPERTIES);
+        const char *props = NULL;
+
+        if (p->data_type != OSSL_PARAM_UTF8_STRING)
+            return 0;
+        if (propsp != NULL && propsp->data_type != OSSL_PARAM_UTF8_STRING)
+            return 0;
+        props = (propsp != NULL ? propsp->data : NULL);
+
+        EVP_CIPHER_free(ctx->sc.cipher);
+        ctx->sc.cipher = NULL;
+        ctx->sc.cipher_intent = p->data != NULL;
+        if (p->data != NULL
+            && ((ctx->sc.cipher = EVP_CIPHER_fetch(libctx, p->data, props))
+                == NULL))
+            return 0;
+    }
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_DESERIALIZER_PARAM_PASS))
+        != NULL) {
+        OPENSSL_clear_free(ctx->sc.cipher_pass, ctx->sc.cipher_pass_length);
+        ctx->sc.cipher_pass = NULL;
+        if (!OSSL_PARAM_get_octet_string(p, &ctx->sc.cipher_pass, 0,
+                                         &ctx->sc.cipher_pass_length))
+            return 0;
+    }
+    return 1;
+}
+
+static int der2rsa_deserialize(void *vctx, OSSL_CORE_BIO *cin,
+                               OSSL_CALLBACK *data_cb, void *data_cbarg,
+                               OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
+{
+    struct der2rsa_ctx_st *ctx = vctx;
+    void *libctx = PROV_LIBRARY_CONTEXT_OF(ctx->provctx);
+    RSA *rsa = NULL;
+    unsigned char *der = NULL;
+    const unsigned char *derp;
+    long der_len = 0;
+    unsigned char *new_der = NULL;
+    long new_der_len;
+    EVP_PKEY *pkey = NULL;
+    int ok = 0;
+
+    ctx->sc.cb = pw_cb;
+    ctx->sc.cbarg = pw_cbarg;
+
+    if (!ossl_prov_read_der(ctx->provctx, cin, &der, &der_len))
+        return 0;
+
+    /*
+     * Opportunistic attempt to decrypt.  If it doesn't work, we try to
+     * decode our input unencrypted.
+     */
+    if (ctx->sc.cipher_intent
+        && ossl_prov_der_from_p8(&new_der, &new_der_len, der, der_len,
+                                 &ctx->sc)) {
+        OPENSSL_free(der);
+        der = new_der;
+        der_len = new_der_len;
+    }
+
+    derp = der;
+    if ((pkey = d2i_PrivateKey_ex(EVP_PKEY_RSA, NULL, &derp, der_len,
+                                  libctx, NULL)) != NULL) {
+        /* Tear out the RSA pointer from the pkey */
+        rsa = EVP_PKEY_get1_RSA(pkey);
+        EVP_PKEY_free(pkey);
+    }
+
+    OPENSSL_free(der);
+
+    if (rsa != NULL) {
+        OSSL_PARAM params[3];
+
+        params[0] =
+            OSSL_PARAM_construct_utf8_string(OSSL_DESERIALIZER_PARAM_DATA_TYPE,
+                                             "RSA", 0);
+        /* The address of the key becomes the octet string */
+        params[1] =
+            OSSL_PARAM_construct_octet_string(OSSL_DESERIALIZER_PARAM_REFERENCE,
+                                              &rsa, sizeof(rsa));
+        params[2] = OSSL_PARAM_construct_end();
+
+        ok = data_cb(params, data_cbarg);
+    }
+    RSA_free(rsa);
+
+    return ok;
+}
+
+static int der2rsa_export_object(void *vctx,
+                                 const void *reference, size_t reference_sz,
+                                 OSSL_CALLBACK *export_cb, void *export_cbarg)
+{
+    OSSL_FUNC_keymgmt_export_fn *rsa_export =
+        ossl_prov_get_keymgmt_rsa_export();
+    void *keydata;
+
+    if (reference_sz == sizeof(keydata) && rsa_export != NULL) {
+        /* The contents of the reference is the address to our object */
+        keydata = *(RSA **)reference;
+
+        return rsa_export(keydata, OSSL_KEYMGMT_SELECT_ALL,
+                          export_cb, export_cbarg);
+    }
+    return 0;
+}
+
+const OSSL_DISPATCH der_to_rsa_deserializer_functions[] = {
+    { OSSL_FUNC_DESERIALIZER_NEWCTX, (void (*)(void))der2rsa_newctx },
+    { OSSL_FUNC_DESERIALIZER_FREECTX, (void (*)(void))der2rsa_freectx },
+    { OSSL_FUNC_DESERIALIZER_GETTABLE_PARAMS,
+      (void (*)(void))der2rsa_gettable_params },
+    { OSSL_FUNC_DESERIALIZER_GET_PARAMS,
+      (void (*)(void))der2rsa_get_params },
+    { OSSL_FUNC_DESERIALIZER_SETTABLE_CTX_PARAMS,
+      (void (*)(void))der2rsa_settable_ctx_params },
+    { OSSL_FUNC_DESERIALIZER_SET_CTX_PARAMS,
+      (void (*)(void))der2rsa_set_ctx_params },
+    { OSSL_FUNC_DESERIALIZER_DESERIALIZE,
+      (void (*)(void))der2rsa_deserialize },
+    { OSSL_FUNC_DESERIALIZER_EXPORT_OBJECT,
+      (void (*)(void))der2rsa_export_object },
+    { 0, NULL }
+};
diff --git a/providers/implementations/serializers/deserialize_pem2der.c b/providers/implementations/serializers/deserialize_pem2der.c
new file mode 100644
index 0000000000..490f041703
--- /dev/null
+++ b/providers/implementations/serializers/deserialize_pem2der.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright 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
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * RSA low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <string.h>
+
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+#include <openssl/params.h>
+#include <openssl/pem.h>
+#include "prov/bio.h"
+#include "prov/bio.h"
+#include "prov/providercommonerr.h"
+#include "serializer_local.h"
+
+static OSSL_FUNC_deserializer_newctx_fn pem2der_newctx;
+static OSSL_FUNC_deserializer_freectx_fn pem2der_freectx;
+static OSSL_FUNC_deserializer_gettable_params_fn pem2der_gettable_params;
+static OSSL_FUNC_deserializer_get_params_fn pem2der_get_params;
+static OSSL_FUNC_deserializer_deserialize_fn pem2der_deserialize;
+
+/*
+ * Context used for PEM to DER deserialization.
+ */
+struct pem2der_ctx_st {
+    PROV_CTX *provctx;
+
+    /* Set to 1 if intending to encrypt/decrypt, otherwise 0 */
+    int cipher_intent;
+
+    EVP_CIPHER *cipher;
+
+    /* Passphrase that was passed by the caller */
+    void *cipher_pass;
+    size_t cipher_pass_length;
+
+    /* This callback is only used if |cipher_pass| is NULL */
+    OSSL_PASSPHRASE_CALLBACK *cb;
+    void *cbarg;
+};
+
+static void *pem2der_newctx(void *provctx)
+{
+    struct pem2der_ctx_st *ctx = OPENSSL_zalloc(sizeof(*ctx));
+
+    if (ctx != NULL)
+        ctx->provctx = provctx;
+    return ctx;
+}
+
+static void pem2der_freectx(void *vctx)
+{
+    struct pem2der_ctx_st *ctx = vctx;
+
+    EVP_CIPHER_free(ctx->cipher);
+    OPENSSL_clear_free(ctx->cipher_pass, ctx->cipher_pass_length);
+    OPENSSL_free(ctx);
+}
+
+static const OSSL_PARAM *pem2der_gettable_params(void)
+{
+    static const OSSL_PARAM gettables[] = {
+        { OSSL_DESERIALIZER_PARAM_INPUT_TYPE, OSSL_PARAM_UTF8_PTR, NULL, 0, 0 },
+        OSSL_PARAM_END,
+    };
+
+    return gettables;
+}
+
+static int pem2der_get_params(OSSL_PARAM params[])
+{
+    OSSL_PARAM *p;
+
+    p = OSSL_PARAM_locate(params, OSSL_DESERIALIZER_PARAM_INPUT_TYPE);
+    if (p != NULL && !OSSL_PARAM_set_utf8_ptr(p, "PEM"))
+        return 0;
+
+    return 1;
+}
+
+static const OSSL_PARAM *pem2der_settable_ctx_params(void)
+{
+    static const OSSL_PARAM settables[] = {
+        OSSL_PARAM_octet_string(OSSL_DESERIALIZER_PARAM_PASS, NULL, 0),
+        OSSL_PARAM_END,
+    };
+
+    return settables;
+}
+
+static int pem2der_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+    struct pem2der_ctx_st *ctx = vctx;
+    const OSSL_PARAM *p;
+
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_DESERIALIZER_PARAM_PASS))
+        != NULL) {
+        OPENSSL_clear_free(ctx->cipher_pass, ctx->cipher_pass_length);
+        ctx->cipher_pass = NULL;
+        if (!OSSL_PARAM_get_octet_string(p, &ctx->cipher_pass, 0,
+                                         &ctx->cipher_pass_length))
+            return 0;
+    }
+    return 1;
+}
+
+/* pem_password_cb compatible function */
+static int pem2der_pass_helper(char *buf, int num, int w, void *data)
+{
+    struct pem2der_ctx_st *ctx = data;
+    size_t plen;
+
+    if (ctx->cipher_pass != NULL) {
+        if (ctx->cipher_pass_length < (size_t)num - 1) {
+            strncpy(buf, ctx->cipher_pass, ctx->cipher_pass_length);
+            buf[ctx->cipher_pass_length] = '\0';
+        } else {
+            OPENSSL_strlcpy(buf, ctx->cipher_pass, num);
+        }
+    } else if (ctx->cb == NULL
+               || !ctx->cb(buf, num, &plen, NULL, ctx->cbarg)) {
+        return -1;
+    }
+    return (int)ctx->cipher_pass_length;
+}
+
+static int pem2der_deserialize(void *vctx, OSSL_CORE_BIO *cin,
+                               OSSL_CALLBACK *data_cb, void *data_cbarg,
+                               OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
+{
+    struct pem2der_ctx_st *ctx = vctx;
+    char *pem_name = NULL, *pem_header = NULL;
+    unsigned char *der = NULL;
+    long der_len = 0;
+    int ok = 0;
+
+    if (ossl_prov_read_pem(ctx->provctx, cin, &pem_name, &pem_header,
+                           &der, &der_len) <= 0)
+        return 0;
+
+    /*
+     * 10 is the number of characters in "Proc-Type:", which
+     * PEM_get_EVP_CIPHER_INFO() requires to be present.
+     * If the PEM header has less characters than that, it's
+     * not worth spending cycles on it.
+     */
+    if (strlen(pem_header) > 10) {
+        EVP_CIPHER_INFO cipher;
+
+        if (!PEM_get_EVP_CIPHER_INFO(pem_header, &cipher)
+            || !PEM_do_header(&cipher, der, &der_len, pem2der_pass_helper, ctx))
+            goto end;
+    }
+
+    {
+        OSSL_PARAM params[3];
+
+        params[0] =
+            OSSL_PARAM_construct_utf8_string(OSSL_DESERIALIZER_PARAM_DATA_TYPE,
+                                             pem_name, 0);
+        params[1] =
+            OSSL_PARAM_construct_octet_string(OSSL_DESERIALIZER_PARAM_DATA,
+                                              der, der_len);
+        params[2] = OSSL_PARAM_construct_end();
+
+        ok = data_cb(params, data_cbarg);
+    }
+
+ end:
+    OPENSSL_free(pem_name);
+    OPENSSL_free(pem_header);
+    OPENSSL_free(der);
+    return ok;
+}
+
+const OSSL_DISPATCH pem_to_der_deserializer_functions[] = {
+    { OSSL_FUNC_DESERIALIZER_NEWCTX, (void (*)(void))pem2der_newctx },
+    { OSSL_FUNC_DESERIALIZER_FREECTX, (void (*)(void))pem2der_freectx },
+    { OSSL_FUNC_DESERIALIZER_GETTABLE_PARAMS,
+      (void (*)(void))pem2der_gettable_params },
+    { OSSL_FUNC_DESERIALIZER_GET_PARAMS,
+      (void (*)(void))pem2der_get_params },
+    { OSSL_FUNC_DESERIALIZER_SETTABLE_CTX_PARAMS,
+      (void (*)(void))pem2der_settable_ctx_params },
+    { OSSL_FUNC_DESERIALIZER_SET_CTX_PARAMS,
+      (void (*)(void))pem2der_set_ctx_params },
+    { OSSL_FUNC_DESERIALIZER_DESERIALIZE, (void (*)(void))pem2der_deserialize },
+    { 0, NULL }
+};
diff --git a/providers/implementations/serializers/serializer_common.c b/providers/implementations/serializers/serializer_common.c
index 7bf0ce941a..58d7a27e60 100644
--- a/providers/implementations/serializers/serializer_common.c
+++ b/providers/implementations/serializers/serializer_common.c
@@ -144,6 +144,16 @@ OSSL_FUNC_keymgmt_import_fn *ossl_prov_get_keymgmt_import(const OSSL_DISPATCH *f
     return NULL;
 }
 
+OSSL_FUNC_keymgmt_export_fn *ossl_prov_get_keymgmt_export(const OSSL_DISPATCH *fns)
+{
+    /* Pilfer the keymgmt dispatch table */
+    for (; fns->function_id != 0; fns++)
+        if (fns->function_id == OSSL_FUNC_KEYMGMT_EXPORT)
+            return OSSL_FUNC_keymgmt_export(fns);
+
+    return NULL;
+}
+
 # ifdef SIXTY_FOUR_BIT_LONG
 #  define BN_FMTu "%lu"
 #  define BN_FMTx "%lx"
diff --git a/providers/implementations/serializers/serializer_local.h b/providers/implementations/serializers/serializer_local.h
index 5378bf1c84..a94418bb2a 100644
--- a/providers/implementations/serializers/serializer_local.h
+++ b/providers/implementations/serializers/serializer_local.h
@@ -35,10 +35,12 @@ struct pkcs8_encrypt_ctx_st {
 OSSL_FUNC_keymgmt_new_fn *ossl_prov_get_keymgmt_new(const OSSL_DISPATCH *fns);
 OSSL_FUNC_keymgmt_free_fn *ossl_prov_get_keymgmt_free(const OSSL_DISPATCH *fns);
 OSSL_FUNC_keymgmt_import_fn *ossl_prov_get_keymgmt_import(const OSSL_DISPATCH *fns);
+OSSL_FUNC_keymgmt_export_fn *ossl_prov_get_keymgmt_export(const OSSL_DISPATCH *fns);
 
 OSSL_FUNC_keymgmt_new_fn *ossl_prov_get_keymgmt_rsa_new(void);
 OSSL_FUNC_keymgmt_free_fn *ossl_prov_get_keymgmt_rsa_free(void);
 OSSL_FUNC_keymgmt_import_fn *ossl_prov_get_keymgmt_rsa_import(void);
+OSSL_FUNC_keymgmt_export_fn *ossl_prov_get_keymgmt_rsa_export(void);
 OSSL_FUNC_keymgmt_new_fn *ossl_prov_get_keymgmt_dh_new(void);
 OSSL_FUNC_keymgmt_free_fn *ossl_prov_get_keymgmt_dh_free(void);
 OSSL_FUNC_keymgmt_import_fn *ossl_prov_get_keymgmt_dh_import(void);
@@ -157,3 +159,14 @@ int ossl_prov_write_pub_pem_from_obj(BIO *out, const void *obj, int obj_nid,
                                                 int *strtype),
                                      int (*k2d)(const void *obj,
                                                 unsigned char **pder));
+
+int ossl_prov_read_der(PROV_CTX *provctx, OSSL_CORE_BIO *cin,
+                       unsigned char **data, long *len);
+int ossl_prov_read_pem(PROV_CTX *provctx, OSSL_CORE_BIO *cin,
+                       char **pem_name, char **pem_header,
+                       unsigned char **data, long *len);
+
+int ossl_prov_der_from_p8(unsigned char **new_der, long *new_der_len,
+                          unsigned char *input_der, long input_der_len,
+                          struct pkcs8_encrypt_ctx_st *ctx);
+
diff --git a/providers/implementations/serializers/serializer_rsa.c b/providers/implementations/serializers/serializer_rsa.c
index e936a67212..d0cea458d1 100644
--- a/providers/implementations/serializers/serializer_rsa.c
+++ b/providers/implementations/serializers/serializer_rsa.c
@@ -37,6 +37,11 @@ OSSL_FUNC_keymgmt_import_fn *ossl_prov_get_keymgmt_rsa_import(void)
     return ossl_prov_get_keymgmt_import(rsa_keymgmt_functions);
 }
 
+OSSL_FUNC_keymgmt_export_fn *ossl_prov_get_keymgmt_rsa_export(void)
+{
+    return ossl_prov_get_keymgmt_export(rsa_keymgmt_functions);
+}
+
 int ossl_prov_print_rsa(BIO *out, RSA *rsa, int priv)
 {
     const char *modulus_label;
diff --git a/test/build.info b/test/build.info
index a49d9c6d6e..d15ee75814 100644
--- a/test/build.info
+++ b/test/build.info
@@ -777,6 +777,11 @@ IF[{- !$disabled{tests} -}]
   INCLUDE[hexstr_test]=.. ../include ../apps/include
   DEPEND[hexstr_test]=../libcrypto.a libtestutil.a
 
+  PROGRAMS{noinst}=serdes_test
+  SOURCE[serdes_test]=serdes_test.c
+  INCLUDE[serdes_test]=.. ../include ../apps/include
+  DEPEND[serdes_test]=../libcrypto.a libtestutil.a
+
   PROGRAMS{noinst}=namemap_internal_test
   SOURCE[namemap_internal_test]=namemap_internal_test.c
   INCLUDE[namemap_internal_test]=.. ../include ../apps/include
diff --git a/test/recipes/04-test_serializer_deserializer.t b/test/recipes/04-test_serializer_deserializer.t
new file mode 100644
index 0000000000..8da6ffb09f
--- /dev/null
+++ b/test/recipes/04-test_serializer_deserializer.t
@@ -0,0 +1,15 @@
+#! /usr/bin/env perl
+# Copyright 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
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+use strict;
+use warnings;
+
+use OpenSSL::Test::Simple;
+use OpenSSL::Test;
+
+simple_test("test_serializer_deserializer", "serdes_test");
diff --git a/test/serdes_test.c b/test/serdes_test.c
new file mode 100644
index 0000000000..b4f3d0b5c8
--- /dev/null
+++ b/test/serdes_test.c
@@ -0,0 +1,378 @@
+/*
+ * Copyright 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
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <string.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+#include <openssl/x509.h>
+#include <openssl/serializer.h>
+#include <openssl/deserializer.h>
+
+#include "internal/cryptlib.h"   /* ossl_assert */
+
+#include "testutil.h"
+
+/*
+ * TODO(3.0) Modify PEM_write_bio_PrivateKey_traditional() to handle
+ * provider side EVP_PKEYs (which don't necessarily have an ameth)
+ *
+ * In the mean time, we use separate "downgraded" EVP_PKEYs to test
+ * serializing/deserializing with "traditional" keys.
+ */
+
+static EVP_PKEY *key_RSA = NULL;
+static EVP_PKEY *legacy_key_RSA = NULL;
+
+static EVP_PKEY *make_RSA(const char *rsa_type, int make_legacy)
+{
+    EVP_PKEY *pkey = NULL;
+    EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, rsa_type, NULL);
+
+    /*
+     * No real need to check the errors other than for the cascade
+     * effect.  |pkey| will imply remain NULL if something goes wrong.
+     */
+    (void)(ctx != NULL
+           && EVP_PKEY_keygen_init(ctx) > 0
+           && EVP_PKEY_keygen(ctx, &pkey) > 0);
+    EVP_PKEY_CTX_free(ctx);
+    if (make_legacy && EVP_PKEY_get0(pkey) == NULL) {
+        EVP_PKEY_free(pkey);
+        pkey = NULL;
+    }
+
+    return pkey;
+}
+
+/* Main test driver */
+
+typedef int (serializer)(void **serialized, long *serialized_len,
+                         void *object,
+                         const char *pass, const char *pcipher,
+                         const char *ser_propq);
+typedef int (deserializer)(void **object,
+                           void *serialized, long serialized_len,
+                           const char *pass, const char *pcipher);
+typedef int (checker)(int type, const void *data, size_t data_len);
+typedef void (dumper)(const char *label, const void *data, size_t data_len);
+
+static int test_serialize_deserialize(EVP_PKEY *pkey,
+                                      const char *pass, const char *pcipher,
+                                      serializer *serialize_cb,
+                                      deserializer *deserialize_cb,
+                                      checker *check_cb, dumper *dump_cb,
+                                      const char *ser_propq, int make_legacy)
+{
+    void *serialized = NULL;
+    long serialized_len = 0;
+    EVP_PKEY *pkey2 = NULL;
+    void *serialized2 = NULL;
+    long serialized2_len = 0;
+    int ok = 0;
+
+    if (!serialize_cb(&serialized, &serialized_len, pkey,
+                      pass, pcipher, ser_propq)
+        || !check_cb(EVP_PKEY_base_id(pkey), serialized, serialized_len)
+        || !deserialize_cb((void **)&pkey2, serialized, serialized_len,
+                           pass, pcipher)
+        || !TEST_int_eq(EVP_PKEY_eq(pkey, pkey2), 1))
+        goto end;
+
+    /*
+     * TODO(3.0) Remove this when PEM_write_bio_PrivateKey_traditional()
+     * handles provider side keys.
+     */
+    if (make_legacy
+        && !TEST_ptr(EVP_PKEY_get0(pkey2)))
+        goto end;
+
+    /*
+     * Double check the serialization, but only for unprotected keys,
+     * as protected keys have a random component, which makes the output
+     * differ.
+     */
+    if ((pass == NULL && pcipher == NULL)
+        && (!serialize_cb(&serialized2, &serialized2_len, pkey2,
+                          pass, pcipher, ser_propq)
+            || !TEST_mem_eq(serialized, serialized_len,
+                            serialized2, serialized2_len)))
+        goto end;
+
+    ok = 1;
+ end:
+    if (!ok)
+        dump_cb("serialized result", serialized, serialized_len);
+
+    OPENSSL_free(serialized);
+    OPENSSL_free(serialized2);
+    EVP_PKEY_free(pkey2);
+    return ok;
+}
+
+/* Serializing and desserializing methods */
+
+static int serialize_EVP_PKEY_prov(void **serialized, long *serialized_len,
+                                   void *object,
+                                   const char *pass, const char *pcipher,
+                                   const char *ser_propq)
+{
+    EVP_PKEY *pkey = object;
+    OSSL_SERIALIZER_CTX *sctx = NULL;
+    BIO *mem_ser = NULL;
+    BUF_MEM *mem_buf = NULL;
+    const unsigned char *upass = (const unsigned char *)pass;
+    int ok = 0;
+
+    if (!TEST_ptr(sctx = OSSL_SERIALIZER_CTX_new_by_EVP_PKEY(pkey, ser_propq))
+        || (pass != NULL
+            && !TEST_true(OSSL_SERIALIZER_CTX_set_passphrase(sctx, upass,
+                                                             strlen(pass))))
+        || (pcipher != NULL
+            && !TEST_true(OSSL_SERIALIZER_CTX_set_cipher(sctx, pcipher, NULL)))
+        || !TEST_ptr(mem_ser = BIO_new(BIO_s_mem()))
+        || !TEST_true(OSSL_SERIALIZER_to_bio(sctx, mem_ser))
+        || !TEST_true(BIO_get_mem_ptr(mem_ser, &mem_buf) > 0)
+        || !TEST_ptr(*serialized = mem_buf->data)
+        || !TEST_long_gt(*serialized_len = mem_buf->length, 0))
+        goto end;
+
+    /* Detach the serialized output */
+    mem_buf->data = NULL;
+    mem_buf->length = 0;
+    ok = 1;
+ end:
+    BIO_free(mem_ser);
+    OSSL_SERIALIZER_CTX_free(sctx);
+    return ok;
+}
+
+static int deserialize_EVP_PKEY_prov(void **object,
+                                     void *serialized, long serialized_len,
+                                     const char *pass, const char *pcipher)
+{
+    EVP_PKEY *pkey = NULL;
+    OSSL_DESERIALIZER_CTX *dctx = NULL;
+    BIO *mem_deser = NULL;
+    const unsigned char *upass = (const unsigned char *)pass;
+    int ok = 0;
+
+    if (!TEST_ptr(dctx = OSSL_DESERIALIZER_CTX_new_by_EVP_PKEY(&pkey, NULL,
+                                                               NULL, NULL))
+        || (pass != NULL
+            && !OSSL_DESERIALIZER_CTX_set_passphrase(dctx, upass,
+                                                     strlen(pass)))
+        || (pcipher != NULL
+            && !OSSL_DESERIALIZER_CTX_set_cipher(dctx, pcipher, NULL))
+        || !TEST_ptr(mem_deser = BIO_new_mem_buf(serialized, serialized_len))
+        || !TEST_true(OSSL_DESERIALIZER_from_bio(dctx, mem_deser)))
+        goto end;
+    ok = 1;
+    *object = pkey;
+ end:
+    BIO_free(mem_deser);
+    OSSL_DESERIALIZER_CTX_free(dctx);
+    return ok;
+}
+
+static int serialize_EVP_PKEY_legacy_PEM(void **serialized,
+                                         long *serialized_len,
+                                         void *object,
+                                         const char *pass, const char *pcipher,
+                                         ossl_unused const char *ser_propq)
+{
+    EVP_PKEY *pkey = object;
+    EVP_CIPHER *cipher = NULL;
+    BIO *mem_ser = NULL;
+    BUF_MEM *mem_buf = NULL;
+    const unsigned char *upass = (const unsigned char *)pass;
+    size_t passlen = 0;
+    int ok = 0;
+
+    if (pcipher != NULL && pass != NULL) {
+        passlen = strlen(pass);
+        if (!TEST_ptr(cipher = EVP_CIPHER_fetch(NULL, pcipher, NULL)))
+            goto end;
+    }
+    if (!TEST_ptr(mem_ser = BIO_new(BIO_s_mem()))
+        || !TEST_true(PEM_write_bio_PrivateKey_traditional(mem_ser, pkey,
+                                                           cipher,
+                                                           upass, passlen,
+                                                           NULL, NULL))
+        || !TEST_true(BIO_get_mem_ptr(mem_ser, &mem_buf) > 0)
+        || !TEST_ptr(*serialized = mem_buf->data)
+        || !TEST_long_gt(*serialized_len = mem_buf->length, 0))
+        goto end;
+
+    /* Detach the serialized output */
+    mem_buf->data = NULL;
+    mem_buf->length = 0;
+    ok = 1;
+ end:
+    BIO_free(mem_ser);
+    EVP_CIPHER_free(cipher);
+    return ok;
+}
+
+/* Test cases and their dumpers / checkers */
+
+static void dump_der(const char *label, const void *data, size_t data_len)
+{
+    test_output_memory(label, data, data_len);
+}
+
+static void dump_pem(const char *label, const void *data, size_t data_len)
+{
+    test_output_string(label, data, data_len - 1);
+}
+
+static int check_unprotected_PKCS8_DER(int type,
+                                       const void *data, size_t data_len)
+{
+    const unsigned char *datap = data;
+    PKCS8_PRIV_KEY_INFO *p8inf =
+        d2i_PKCS8_PRIV_KEY_INFO(NULL, &datap, data_len);
+    int ok = 0;
+
+    if (TEST_ptr(p8inf)) {
+        EVP_PKEY *pkey = EVP_PKCS82PKEY(p8inf);
+
+        ok = (TEST_ptr(pkey) && TEST_true(EVP_PKEY_is_a(pkey, "RSA")));
+        EVP_PKEY_free(pkey);
+    }
+    PKCS8_PRIV_KEY_INFO_free(p8inf);
+    return ok;
+}
+
+static int test_unprotected_RSA_via_DER(void)
+{
+    return test_serialize_deserialize(key_RSA, NULL, NULL,
+                                      serialize_EVP_PKEY_prov,
+                                      deserialize_EVP_PKEY_prov,
+                                      check_unprotected_PKCS8_DER, dump_der,
+                                      OSSL_SERIALIZER_PrivateKey_TO_DER_PQ,
+                                      0);
+}
+
+static int check_unprotected_PKCS8_PEM(int type,
+                                       const void *data, size_t data_len)
+{
+    static const char pem_header[] = "-----BEGIN " PEM_STRING_PKCS8INF "-----";
+
+    return TEST_strn_eq(data, pem_header, sizeof(pem_header) - 1);
+}
+
+static int test_unprotected_RSA_via_PEM(void)
+{
+    return test_serialize_deserialize(key_RSA, NULL, NULL,
+                                      serialize_EVP_PKEY_prov,
+                                      deserialize_EVP_PKEY_prov,
+                                      check_unprotected_PKCS8_PEM, dump_pem,
+                                      OSSL_SERIALIZER_PrivateKey_TO_PEM_PQ,
+                                      0);
+}
+
+static int check_unprotected_legacy_PEM(int type,
+                                        const void *data, size_t data_len)
+{
+    static const char pem_header[] = "-----BEGIN " PEM_STRING_RSA "-----";
+
+    return TEST_strn_eq(data, pem_header, sizeof(pem_header) - 1);
+}
+
+static int test_unprotected_RSA_via_legacy_PEM(void)
+{
+    return test_serialize_deserialize(legacy_key_RSA, NULL, NULL,
+                                      serialize_EVP_PKEY_legacy_PEM,
+                                      deserialize_EVP_PKEY_prov,
+                                      check_unprotected_legacy_PEM, dump_pem,
+                                      NULL, 1);
+}
+
+static const char *pass_cipher = "AES-256-CBC";
+static const char *pass = "the holy handgrenade of antioch";
+
+static int check_protected_PKCS8_DER(int type,
+                                     const void *data, size_t data_len)
+{
+    const unsigned char *datap = data;
+    X509_SIG *p8 = d2i_X509_SIG(NULL, &datap, data_len);
+    int ok = TEST_ptr(p8);
+
+    X509_SIG_free(p8);
+    return ok;
+}
+
+static int test_protected_RSA_via_DER(void)
+{
+    return test_serialize_deserialize(key_RSA, pass, pass_cipher,
+                                      serialize_EVP_PKEY_prov,
+                                      deserialize_EVP_PKEY_prov,
+                                      check_protected_PKCS8_DER, dump_der,
+                                      OSSL_SERIALIZER_PrivateKey_TO_DER_PQ,
+                                      0);
+}
+
+static int check_protected_PKCS8_PEM(int type,
+                                     const void *data, size_t data_len)
+{
+    static const char pem_header[] = "-----BEGIN " PEM_STRING_PKCS8 "-----";
+
+    return TEST_strn_eq(data, pem_header, sizeof(pem_header) - 1);
+}
+
+static int test_protected_RSA_via_PEM(void)
+{
+    return test_serialize_deserialize(key_RSA, pass, pass_cipher,
+                                      serialize_EVP_PKEY_prov,
+                                      deserialize_EVP_PKEY_prov,
+                                      check_protected_PKCS8_PEM, dump_pem,
+                                      OSSL_SERIALIZER_PrivateKey_TO_PEM_PQ,
+                                      0);
+}
+
+static int check_protected_legacy_PEM(int type,
+                                      const void *data, size_t data_len)
+{
+    static const char pem_header[] = "-----BEGIN " PEM_STRING_RSA "-----";
+
+    return
+        TEST_strn_eq(data, pem_header, sizeof(pem_header) - 1)
+        && TEST_ptr(strstr(data, "\nDEK-Info: "));
+}
+
+static int test_protected_RSA_via_legacy_PEM(void)
+{
+    return test_serialize_deserialize(legacy_key_RSA, pass, pass_cipher,
+                                      serialize_EVP_PKEY_legacy_PEM,
+                                      deserialize_EVP_PKEY_prov,
+                                      check_protected_legacy_PEM, dump_pem,
+                                      NULL, 1);
+}
+
+int setup_tests(void)
+{
+    TEST_info("Generating keys...");
+    if (!TEST_ptr(key_RSA = make_RSA("RSA", 0))
+        || !TEST_ptr(legacy_key_RSA = make_RSA("RSA", 1))) {
+        EVP_PKEY_free(key_RSA);
+        EVP_PKEY_free(legacy_key_RSA);
+        return 0;
+    }
+    TEST_info("Generating key... done");
+
+    ADD_TEST(test_unprotected_RSA_via_DER);
+    ADD_TEST(test_unprotected_RSA_via_PEM);
+    ADD_TEST(test_unprotected_RSA_via_legacy_PEM);
+    ADD_TEST(test_protected_RSA_via_DER);
+    ADD_TEST(test_protected_RSA_via_PEM);
+    ADD_TEST(test_protected_RSA_via_legacy_PEM);
+
+    return 1;
+}
diff --git a/test/testutil/format_output.c b/test/testutil/format_output.c
index 069a6a03a5..e2ee98cfd8 100644
--- a/test/testutil/format_output.c
+++ b/test/testutil/format_output.c
@@ -65,7 +65,7 @@ static void test_fail_string_common(const char *prefix, const char *file,
         goto fin;
     }
 
-    if (l1 != l2 || strcmp(m1, m2) != 0)
+    if (l1 != l2 || strncmp(m1, m2, l1) != 0)
         test_diff_header(left, right);
 
     while (l1 > 0 || l2 > 0) {
diff --git a/util/libcrypto.num b/util/libcrypto.num
index 5aff5d5c44..a4642f1973 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -5149,3 +5149,34 @@ X509_STORE_load_store_with_libctx       ?	3_0_0	EXIST::FUNCTION:
 X509_STORE_load_locations_with_libctx   ?	3_0_0	EXIST::FUNCTION:
 X509_STORE_set_default_paths_with_libctx ?	3_0_0	EXIST::FUNCTION:
 OSSL_STORE_open_with_libctx             ?	3_0_0	EXIST::FUNCTION:
+OSSL_DESERIALIZER_fetch                 ?	3_0_0	EXIST::FUNCTION:
+OSSL_DESERIALIZER_up_ref                ?	3_0_0	EXIST::FUNCTION:
+OSSL_DESERIALIZER_free                  ?	3_0_0	EXIST::FUNCTION:
+OSSL_DESERIALIZER_provider              ?	3_0_0	EXIST::FUNCTION:
+OSSL_DESERIALIZER_properties            ?	3_0_0	EXIST::FUNCTION:
+OSSL_DESERIALIZER_number                ?	3_0_0	EXIST::FUNCTION:
+OSSL_DESERIALIZER_is_a                  ?	3_0_0	EXIST::FUNCTION:
+OSSL_DESERIALIZER_do_all_provided       ?	3_0_0	EXIST::FUNCTION:
+OSSL_DESERIALIZER_names_do_all          ?	3_0_0	EXIST::FUNCTION:
+OSSL_DESERIALIZER_settable_ctx_params   ?	3_0_0	EXIST::FUNCTION:
+OSSL_DESERIALIZER_CTX_new               ?	3_0_0	EXIST::FUNCTION:
+OSSL_DESERIALIZER_CTX_set_params        ?	3_0_0	EXIST::FUNCTION:
+OSSL_DESERIALIZER_CTX_free              ?	3_0_0	EXIST::FUNCTION:
+OSSL_DESERIALIZER_CTX_set_cipher        ?	3_0_0	EXIST::FUNCTION:
+OSSL_DESERIALIZER_CTX_set_passphrase    ?	3_0_0	EXIST::FUNCTION:
+OSSL_DESERIALIZER_CTX_set_passphrase_cb ?	3_0_0	EXIST::FUNCTION:
+OSSL_DESERIALIZER_CTX_set_passphrase_ui ?	3_0_0	EXIST::FUNCTION:
+OSSL_DESERIALIZER_from_bio              ?	3_0_0	EXIST::FUNCTION:
+OSSL_DESERIALIZER_from_fp               ?	3_0_0	EXIST::FUNCTION:STDIO
+OSSL_DESERIALIZER_CTX_add_deserializer  ?	3_0_0	EXIST::FUNCTION:
+OSSL_DESERIALIZER_CTX_add_extra         ?	3_0_0	EXIST::FUNCTION:
+OSSL_DESERIALIZER_CTX_num_deserializers ?	3_0_0	EXIST::FUNCTION:
+OSSL_DESERIALIZER_CTX_set_finalizer     ?	3_0_0	EXIST::FUNCTION:
+OSSL_DESERIALIZER_CTX_set_input_type    ?	3_0_0	EXIST::FUNCTION:
+OSSL_DESERIALIZER_export                ?	3_0_0	EXIST::FUNCTION:
+OSSL_DESERIALIZER_INSTANCE_deserializer ?	3_0_0	EXIST::FUNCTION:
+OSSL_DESERIALIZER_INSTANCE_deserializer_ctx ?	3_0_0	EXIST::FUNCTION:
+ERR_load_OSSL_DESERIALIZER_strings      ?	3_0_0	EXIST::FUNCTION:
+OSSL_DESERIALIZER_gettable_params       ?	3_0_0	EXIST::FUNCTION:
+OSSL_DESERIALIZER_get_params            ?	3_0_0	EXIST::FUNCTION:
+OSSL_DESERIALIZER_CTX_new_by_EVP_PKEY   ?	3_0_0	EXIST::FUNCTION:
diff --git a/util/missingcrypto.txt b/util/missingcrypto.txt
index 0034a711d1..0e5bb35878 100644
--- a/util/missingcrypto.txt
+++ b/util/missingcrypto.txt
@@ -636,6 +636,7 @@ ERR_load_KDF_strings(3)
 ERR_load_OBJ_strings(3)
 ERR_load_OCSP_strings(3)
 ERR_load_OSSL_SERIALIZER_strings(3)
+ERR_load_OSSL_DESERIALIZER_strings(3)
 ERR_load_OSSL_STORE_strings(3)
 ERR_load_PEM_strings(3)
 ERR_load_PKCS12_strings(3)
diff --git a/util/other.syms b/util/other.syms
index 54a2b71abb..a623ff5e77 100644
--- a/util/other.syms
+++ b/util/other.syms
@@ -41,6 +41,12 @@ GEN_SESSION_CB                          datatype
 OPENSSL_Applink                         external
 OPENSSL_CTX                             datatype
 NAMING_AUTHORITY                        datatype
+OSSL_DESERIALIZER                       datatype
+OSSL_DESERIALIZER_CTX                   datatype
+OSSL_DESERIALIZER_FINALIZER             datatype
+OSSL_DESERIALIZER_CLEANER               datatype
+OSSL_DESERIALIZER_INSTANCE              datatype
+OSSL_DESERIALIZER_CTX                   datatype
 OSSL_HTTP_bio_cb_t                      datatype
 OSSL_PARAM                              datatype
 OSSL_PROVIDER                           datatype


More information about the openssl-commits mailing list