[openssl] master update

Richard Levitte levitte at openssl.org
Mon Aug 24 08:05:05 UTC 2020


The branch master has been updated
       via  a8b7ea8268e23a033750eb77380ef9a4f9a5aa27 (commit)
       via  34b80d0622924d112b145fd36bfaad18616f2546 (commit)
       via  4fd397821139723fd4e51a03e92df33e9a9fadcc (commit)
       via  ab00ddb55907317d6cece552d12ddf3263c01043 (commit)
       via  c4fc564d48456be622509886d3ea5618fce2a02e (commit)
       via  8704b6bf32ccc24570b57fbdfef685fbd3572635 (commit)
       via  faa64bca9f59b4390aed93dca75e53c3cacddc4a (commit)
       via  a517edec0385280e31e2dc2912301501e6b0c4a3 (commit)
       via  14c8a3d118e3ec5d0179d45c7f227d29a52f7697 (commit)
      from  bc8c3e1cd8691e6c8e6fe208377ee0d0e408af73 (commit)


- Log -----------------------------------------------------------------
commit a8b7ea8268e23a033750eb77380ef9a4f9a5aa27
Author: Richard Levitte <levitte at openssl.org>
Date:   Thu Jul 23 16:12:10 2020 +0200

    STORE: Change all error recording to use ERR_raise() / ERR_raise_data()
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12512)

commit 34b80d0622924d112b145fd36bfaad18616f2546
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Jul 22 22:55:00 2020 +0200

    STORE: Modify to support loading with provider based loaders
    
    This adds the needed code to make the OSSL_STORE API functions handle
    provided STORE implementations.
    
    This also modifies OSSL_STORE_attach() for have the URI, the
    library context and the properties in the same order as
    OSSL_STORE_open_with_libctx().
    
    The most notable change, though, is how this creates a division of
    labor between libcrypto and any storemgmt implementation that wants to
    pass X.509, X.509 CRL, etc structures back to libcrypto.  Since those
    structures aren't directly supported in the libcrypto <-> provider
    interface (asymmetric keys being the only exception so far), we resort
    to a libcrypto object callback that can handle passed data in DER form
    and does its part of figuring out what the DER content actually is.
    
    This also adds the internal x509_crl_set0_libctx(), which works just
    like x509_set0_libctx(), but for X509_CRL.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12512)

commit 4fd397821139723fd4e51a03e92df33e9a9fadcc
Author: Richard Levitte <levitte at openssl.org>
Date:   Sun Aug 2 14:29:33 2020 +0200

    DECODER: Add function to set an OSSL_PASSPHRASE_CALLBACK type callback
    
    This makes it possible to use OSSL_DECODER in functions that are passed
    a OSSL_PASSPHRASE_CALLBACK already.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12512)

commit ab00ddb55907317d6cece552d12ddf3263c01043
Author: Richard Levitte <levitte at openssl.org>
Date:   Tue Jul 28 22:00:09 2020 +0200

    OSSL_PARAM: Add string pointer getters
    
    When some function receives an OSSL_PARAM array to pilfer for data,
    and there is a string of some sort, and all the code needs is to get
    the pointer to the data, rather than a copy, there is currently no
    other way than to use |param->data| directly.  This is of course a
    valid method, but lacks any safety check (is |param->data_type|
    correct, for example?).
    
    OSSL_PARAM_get_utf8_string_ptr() and OSSL_PARAM_get_octet_string_ptr()
    helps the programmer with such things, by setting the argument pointer
    to |param->data|.
    Additionally, the handle the data types OSSL_PARAM_UTF8_PTR and
    OSSL_PARAM_OCTET_PTR as well.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12512)

commit c4fc564d48456be622509886d3ea5618fce2a02e
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Jul 22 22:54:54 2020 +0200

    STORE: Add the base functions to support provider based loaders
    
    This includes fixing a bug that could only be discovered when no
    loaders were registered.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12512)

commit 8704b6bf32ccc24570b57fbdfef685fbd3572635
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Jul 22 22:54:42 2020 +0200

    STORE for providers: define libcrypto <-> provider interface
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12512)

commit faa64bca9f59b4390aed93dca75e53c3cacddc4a
Author: Richard Levitte <levitte at openssl.org>
Date:   Sun Jul 26 09:21:29 2020 +0200

    STORE: Add missing function OSSL_STORE_LOADER_set_open_with_libctx()
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12512)

commit a517edec0385280e31e2dc2912301501e6b0c4a3
Author: Richard Levitte <levitte at openssl.org>
Date:   Sun Aug 2 12:14:19 2020 +0200

    CORE: Generalise internal pass phrase prompter
    
    The pass phrase prompter that's part of OSSL_ENCODER and OSSL_DECODER
    is really a passphrase callback bridge between the diverse forms of
    prompters that exist within OpenSSL: pem_password_cb, ui_method and
    OSSL_PASSPHRASE_CALLBACK.
    
    This can be generalised, to be re-used by other parts of OpenSSL, and
    to thereby allow the users to specify whatever form of pass phrase
    callback they need, while being able to pass that on to other APIs
    that are called internally, in the form that those APIs demand.
    
    Additionally, we throw in the possibility to cache pass phrases during
    a "session" (we leave it to each API to define what a "session" is).
    This is useful for any API that implements discovery and therefore may
    need to get the same password more than once, such as OSSL_DECODER and
    OSSL_STORE.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12512)

commit 14c8a3d118e3ec5d0179d45c7f227d29a52f7697
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Jul 22 15:34:25 2020 +0200

    CORE: Define provider-native abstract objects
    
    This is placed as CORE because the core of libcrypto is the authority
    for what is possible to do and what's required to make these abstract
    objects work.
    
    In essence, an abstract object is an OSSL_PARAM array with well
    defined parameter keys and values:
    
    -   an object type, which is a number indicating what kind of
        libcrypto structure the object in question can be used with.  The
        currently possible numbers are defined in <openssl/core_object.h>.
    -   an object data type, which is a string that indicates more closely
        what the contents of the object are.
    -   the object data, an octet string.  The exact encoding used depends
        on the context in which it's used.  For example, the decoder
        sub-system accepts any encoding, as long as there is a decoder
        implementation that takes that as input.  If central code is to
        handle the data directly, DER encoding is assumed. (*)
    -   an object reference, also an octet string.  This octet string is
        not the object contents, just a mere reference to a provider-native
        object. (**)
    -   an object description, which is a human readable text string that
        can be displayed if some software desires to do so.
    
    The intent is that certain provider-native operations (called X
    here) are able to return any sort of object that belong with other
    operations, or an object that has no provider support otherwise.
    
    (*) A future extension might be to be able to specify encoding.
    
    (**) The possible mechanisms for dealing with object references are:
    
    -   An object loading function in the target operation.  The exact
        target operation is determined by the object type (for example,
        OSSL_OBJECT_PKEY implies that the target operation is a KEYMGMT)
        and the implementation to be fetched by its object data type (for
        an OSSL_OBJECT_PKEY, that's the KEYMGMT keytype to be fetched).
        This loading function is only useful for this if the implementations
        that are involved (X and KEYMGMT, for example) are from the same
        provider.
    
    -   An object exporter function in the operation X implementation.
        That exporter function can be used to export the object data in
        OSSL_PARAM form that can be imported by a target operation's
        import function.  This can be used when it's not possible to fetch
        the target operation implementation from the same provider.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12512)

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

Summary of changes:
 crypto/build.info                                  |   2 +-
 crypto/encode_decode/build.info                    |   5 +-
 crypto/encode_decode/decoder_lib.c                 |  15 +-
 crypto/encode_decode/decoder_meth.c                |   4 +-
 crypto/encode_decode/decoder_pkey.c                |  75 +--
 crypto/encode_decode/encoder_local.h               |  44 +-
 crypto/encode_decode/encoder_meth.c                |   2 +-
 crypto/encode_decode/encoder_pkey.c                |  38 +-
 crypto/encode_decode/endecode_pass.c               | 162 ------
 crypto/params.c                                    |  26 +
 crypto/passphrase.c                                | 324 +++++++++++
 crypto/store/build.info                            |   4 +-
 crypto/store/store_init.c                          |   2 +-
 crypto/store/store_lib.c                           | 498 +++++++++++++----
 crypto/store/store_local.h                         |  76 +++
 crypto/store/store_meth.c                          | 425 +++++++++++++++
 crypto/store/store_register.c                      |  80 ++-
 crypto/store/store_result.c                        | 594 +++++++++++++++++++++
 crypto/x509/x_crl.c                                |   9 +
 doc/man3/OSSL_DECODER_CTX_new_by_EVP_PKEY.pod      |  25 +-
 doc/man3/OSSL_DECODER_from_bio.pod                 |  47 +-
 doc/man3/OSSL_PARAM_int.pod                        |  16 +
 doc/man3/OSSL_STORE_LOADER.pod                     | 238 ++++++---
 doc/man3/OSSL_STORE_open.pod                       |  21 +-
 doc/man7/provider-object.pod                       | 189 +++++++
 doc/man7/provider-storemgmt.pod                    | 178 ++++++
 include/crypto/x509.h                              |   4 +
 include/internal/cryptlib.h                        |   3 +-
 include/internal/passphrase.h                      | 120 +++++
 include/openssl/core_dispatch.h                    |  43 +-
 include/openssl/core_names.h                       |  48 +-
 include/openssl/core_object.h                      |  40 ++
 include/openssl/decoder.h                          |   3 +
 include/openssl/err.h                              |   1 +
 include/openssl/params.h                           |   4 +
 include/openssl/store.h                            |  85 ++-
 .../implementations/encode_decode/decode_common.c  |  39 +-
 .../implementations/encode_decode/decode_der2key.c |  14 +-
 .../implementations/encode_decode/decode_ms2key.c  |  14 +-
 .../implementations/encode_decode/decode_pem2der.c |   4 +-
 util/libcrypto.num                                 |  12 +
 util/other.syms                                    |   1 +
 42 files changed, 2854 insertions(+), 680 deletions(-)
 delete mode 100644 crypto/encode_decode/endecode_pass.c
 create mode 100644 crypto/passphrase.c
 create mode 100644 crypto/store/store_meth.c
 create mode 100644 crypto/store/store_result.c
 create mode 100644 doc/man7/provider-object.pod
 create mode 100644 doc/man7/provider-storemgmt.pod
 create mode 100644 include/internal/passphrase.h
 create mode 100644 include/openssl/core_object.h

diff --git a/crypto/build.info b/crypto/build.info
index 07e3dd526f..9e10145d3c 100644
--- a/crypto/build.info
+++ b/crypto/build.info
@@ -71,7 +71,7 @@ $UTIL_COMMON=\
         cryptlib.c params.c params_from_text.c bsearch.c ex_data.c o_str.c \
         ctype.c threads_pthread.c threads_win.c threads_none.c initthread.c \
         context.c sparse_array.c asn1_dsa.c packet.c param_build.c $CPUIDASM \
-        param_build_set.c der_writer.c
+        param_build_set.c der_writer.c passphrase.c
 $UTIL_DEFINE=$CPUIDDEF
 
 SOURCE[../libcrypto]=$UTIL_COMMON \
diff --git a/crypto/encode_decode/build.info b/crypto/encode_decode/build.info
index 4686c4a19d..e2cd846673 100644
--- a/crypto/encode_decode/build.info
+++ b/crypto/encode_decode/build.info
@@ -1,8 +1,5 @@
-SOURCE[../../libcrypto]=endecode_pass.c
-
 SOURCE[../../libcrypto]=encoder_meth.c encoder_lib.c encoder_pkey.c
-SOURCE[../../libcrypto]=decoder_meth.c decoder_lib.c \
-        decoder_pkey.c
+SOURCE[../../libcrypto]=decoder_meth.c decoder_lib.c decoder_pkey.c
 
 SOURCE[../../libcrypto]=encoder_err.c
 SOURCE[../../libcrypto]=decoder_err.c
diff --git a/crypto/encode_decode/decoder_lib.c b/crypto/encode_decode/decoder_lib.c
index 21b4703084..5d18ef1eff 100644
--- a/crypto/encode_decode/decoder_lib.c
+++ b/crypto/encode_decode/decoder_lib.c
@@ -11,6 +11,7 @@
 #include <openssl/bio.h>
 #include <openssl/params.h>
 #include <openssl/provider.h>
+#include "internal/passphrase.h"
 #include "encoder_local.h"
 #include "e_os.h"
 
@@ -35,13 +36,14 @@ int OSSL_DECODER_from_bio(OSSL_DECODER_CTX *ctx, BIO *in)
     data.ctx = ctx;
     data.bio = in;
 
+    /* Enable passphrase caching */
+    (void)ossl_pw_enable_passphrase_caching(&ctx->pwdata);
+
     ok = decoder_process(NULL, &data);
 
     /* Clear any internally cached passphrase */
-    if (!ctx->flag_user_passphrase) {
-        OSSL_DECODER_CTX_set_passphrase(ctx, NULL, 0);
-        ctx->flag_user_passphrase = 0;
-    }
+    (void)ossl_pw_clear_passphrase_cache(&ctx->pwdata);
+
     return ok;
 }
 
@@ -407,7 +409,7 @@ static int decoder_process(const OSSL_PARAM params[], void *arg)
          * If this data isn't present, decoding has failed.
          */
 
-        p = OSSL_PARAM_locate_const(params, OSSL_DECODER_PARAM_DATA);
+        p = OSSL_PARAM_locate_const(params, OSSL_OBJECT_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);
@@ -472,7 +474,8 @@ static int decoder_process(const OSSL_PARAM params[], void *arg)
         new_data.current_deser_inst_index = i;
         ok = new_deser->decode(new_deser_inst->deserctx, (OSSL_CORE_BIO *)bio,
                                decoder_process, &new_data,
-                               ctx->passphrase_cb, new_data.ctx);
+                               ossl_pw_passphrase_callback_dec,
+                               &new_data.ctx->pwdata);
         if (ok)
             break;
     }
diff --git a/crypto/encode_decode/decoder_meth.c b/crypto/encode_decode/decoder_meth.c
index 2259c6348a..409bb1aa54 100644
--- a/crypto/encode_decode/decoder_meth.c
+++ b/crypto/encode_decode/decoder_meth.c
@@ -492,7 +492,6 @@ OSSL_DECODER_CTX *OSSL_DECODER_CTX_new(void)
         return NULL;
     }
 
-    ctx->passphrase_cb = ossl_decoder_passphrase_in_cb;
     return ctx;
 }
 
@@ -545,8 +544,7 @@ void OSSL_DECODER_CTX_free(OSSL_DECODER_CTX *ctx)
             ctx->cleanup(ctx->construct_data);
         sk_OSSL_DECODER_INSTANCE_pop_free(ctx->decoder_insts,
                                           OSSL_DECODER_INSTANCE_free);
-        OSSL_DECODER_CTX_set_passphrase_ui(ctx, NULL, NULL);
-        OSSL_DECODER_CTX_set_passphrase(ctx, NULL, 0);
+        ossl_pw_clear_passphrase_data(&ctx->pwdata);
         OPENSSL_free(ctx);
     }
 }
diff --git a/crypto/encode_decode/decoder_pkey.c b/crypto/encode_decode/decoder_pkey.c
index 7ab38f228f..0bb548abce 100644
--- a/crypto/encode_decode/decoder_pkey.c
+++ b/crypto/encode_decode/decoder_pkey.c
@@ -8,6 +8,7 @@
  */
 
 #include <openssl/core_names.h>
+#include <openssl/core_object.h>
 #include <openssl/evp.h>
 #include <openssl/ui.h>
 #include <openssl/decoder.h>
@@ -19,79 +20,27 @@ int OSSL_DECODER_CTX_set_passphrase(OSSL_DECODER_CTX *ctx,
                                     const unsigned char *kstr,
                                     size_t klen)
 {
-    if (!ossl_assert(ctx != NULL)) {
-        ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_PASSED_NULL_PARAMETER);
-        return 0;
-    }
-
-    OPENSSL_clear_free(ctx->cached_passphrase, ctx->cached_passphrase_len);
-    ctx->cached_passphrase = NULL;
-    ctx->cached_passphrase_len = 0;
-    if (kstr != NULL) {
-        if (klen == 0) {
-            ctx->cached_passphrase = OPENSSL_zalloc(1);
-            ctx->cached_passphrase_len = 0;
-        } else {
-            ctx->cached_passphrase = OPENSSL_memdup(kstr, klen);
-            ctx->cached_passphrase_len = klen;
-        }
-        if (ctx->cached_passphrase == NULL) {
-            ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_MALLOC_FAILURE);
-            return 0;
-        }
-    }
-    ctx->flag_user_passphrase = 1;
-    return 1;
-}
-
-static void decoder_ctx_reset_passphrase_ui(OSSL_DECODER_CTX *ctx)
-{
-    UI_destroy_method(ctx->allocated_ui_method);
-    ctx->allocated_ui_method = NULL;
-    ctx->ui_method = NULL;
-    ctx->ui_data = NULL;
+    return ossl_pw_set_passphrase(&ctx->pwdata, kstr, klen);
 }
 
 int OSSL_DECODER_CTX_set_passphrase_ui(OSSL_DECODER_CTX *ctx,
                                        const UI_METHOD *ui_method,
                                        void *ui_data)
 {
-    if (!ossl_assert(ctx != NULL)) {
-        ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_PASSED_NULL_PARAMETER);
-        return 0;
-    }
-
-    decoder_ctx_reset_passphrase_ui(ctx);
-    ctx->ui_method = ui_method;
-    ctx->ui_data = ui_data;
-    return 1;
+    return ossl_pw_set_ui_method(&ctx->pwdata, ui_method, ui_data);
 }
 
 int OSSL_DECODER_CTX_set_pem_password_cb(OSSL_DECODER_CTX *ctx,
                                          pem_password_cb *cb, void *cbarg)
 {
-    UI_METHOD *ui_method = NULL;
-
-    if (!ossl_assert(ctx != NULL)) {
-        ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_PASSED_NULL_PARAMETER);
-        return 0;
-    }
-
-    /*
-     * If |cb| is NULL, it means the caller wants to reset previous
-     * password callback info.  Otherwise, we only set the new data
-     * if a new UI_METHOD could be created for this sort of callback.
-     */
-    if (cb == NULL
-        || (ui_method = UI_UTIL_wrap_read_pem_callback(cb, 0)) != NULL) {
-        decoder_ctx_reset_passphrase_ui(ctx);
-        ctx->ui_method = ctx->allocated_ui_method = ui_method;
-        ctx->ui_data = cbarg;
-        ctx->passphrase_cb = ossl_decoder_passphrase_in_cb;
-        return 1;
-    }
+    return ossl_pw_set_pem_password_cb(&ctx->pwdata, cb, cbarg);
+}
 
-    return 0;
+int OSSL_DECODER_CTX_set_passphrase_cb(OSSL_DECODER_CTX *ctx,
+                                       OSSL_PASSPHRASE_CALLBACK *cb,
+                                       void *cbarg)
+{
+    return ossl_pw_set_ossl_passphrase_cb(&ctx->pwdata, cb, cbarg);
 }
 
 /*
@@ -128,7 +77,7 @@ static int decoder_construct_EVP_PKEY(OSSL_DECODER_INSTANCE *decoder_inst,
     size_t object_ref_sz = 0;
     const OSSL_PARAM *p;
 
-    p = OSSL_PARAM_locate_const(params, OSSL_DECODER_PARAM_DATA_TYPE);
+    p = OSSL_PARAM_locate_const(params, OSSL_OBJECT_PARAM_DATA_TYPE);
     if (p != NULL) {
         char *object_type = NULL;
 
@@ -143,7 +92,7 @@ static int decoder_construct_EVP_PKEY(OSSL_DECODER_INSTANCE *decoder_inst,
      * reference for the moment.  This enforces that the key data itself
      * remains with the provider.
      */
-    p = OSSL_PARAM_locate_const(params, OSSL_DECODER_PARAM_REFERENCE);
+    p = OSSL_PARAM_locate_const(params, OSSL_OBJECT_PARAM_REFERENCE);
     if (p == NULL || p->data_type != OSSL_PARAM_OCTET_STRING)
         return 0;
     object_ref = p->data;
diff --git a/crypto/encode_decode/encoder_local.h b/crypto/encode_decode/encoder_local.h
index 34931d4e43..749b2688e4 100644
--- a/crypto/encode_decode/encoder_local.h
+++ b/crypto/encode_decode/encoder_local.h
@@ -13,6 +13,7 @@
 #include <openssl/encoder.h>
 #include <openssl/decoder.h>
 #include "internal/cryptlib.h"
+#include "internal/passphrase.h"
 #include "internal/refcount.h"
 
 struct ossl_serdes_base_st {
@@ -64,13 +65,7 @@ struct ossl_encoder_ctx_st {
     int (*do_output)(OSSL_ENCODER_CTX *ctx, BIO *out);
 
     /* For any function that needs a passphrase reader */
-    const UI_METHOD *ui_method;
-    void *ui_data;
-    /*
-     * if caller used OSSL_ENCODER_CTX_set_passphrase_cb(), we need
-     * intermediary storage.
-     */
-    UI_METHOD *allocated_ui_method;
+    struct ossl_passphrase_data_st pwdata;
 };
 
 struct ossl_decoder_instance_st {
@@ -103,38 +98,5 @@ struct ossl_decoder_ctx_st {
     void *construct_data;
 
     /* For any function that needs a passphrase reader */
-    OSSL_PASSPHRASE_CALLBACK *passphrase_cb;
-    const UI_METHOD *ui_method;
-    void *ui_data;
-    /*
-     * if caller used OSSL_ENCODER_CTX_set_pem_password_cb(), we need
-     * intermediary storage.
-     */
-    UI_METHOD *allocated_ui_method;
-    /*
-     * Because the same input may pass through more than one decoder,
-     * 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;
-
-    /*
-     * Flag section.  Keep these together
-     */
-
-    /*
-     * The passphrase was passed to us by the user.  In that case, it
-     * should only be freed when freeing this context.
-     */
-    unsigned int flag_user_passphrase:1;
+    struct ossl_passphrase_data_st pwdata;
 };
-
-/* Passphrase callbacks, found in serdes_pass.c */
-
-/*
- * Encoders typically want to get an outgoing passphrase, while
- * decoders typically want to get en incoming passphrase.
- */
-OSSL_PASSPHRASE_CALLBACK ossl_encoder_passphrase_out_cb;
-OSSL_PASSPHRASE_CALLBACK ossl_decoder_passphrase_in_cb;
diff --git a/crypto/encode_decode/encoder_meth.c b/crypto/encode_decode/encoder_meth.c
index ef3c24433e..81bc5c47b9 100644
--- a/crypto/encode_decode/encoder_meth.c
+++ b/crypto/encode_decode/encoder_meth.c
@@ -517,7 +517,7 @@ void OSSL_ENCODER_CTX_free(OSSL_ENCODER_CTX *ctx)
         if (ctx->encoder != NULL && ctx->encoder->freectx != NULL)
             ctx->encoder->freectx(ctx->serctx);
         OSSL_ENCODER_free(ctx->encoder);
-        UI_destroy_method(ctx->allocated_ui_method);
+        ossl_pw_clear_passphrase_data(&ctx->pwdata);
         OPENSSL_free(ctx);
     }
 }
diff --git a/crypto/encode_decode/encoder_pkey.c b/crypto/encode_decode/encoder_pkey.c
index dc776c023e..6664f589b4 100644
--- a/crypto/encode_decode/encoder_pkey.c
+++ b/crypto/encode_decode/encoder_pkey.c
@@ -48,45 +48,17 @@ int OSSL_ENCODER_CTX_set_passphrase(OSSL_ENCODER_CTX *ctx,
     return OSSL_ENCODER_CTX_set_params(ctx, params);
 }
 
-static void encoder_ctx_reset_passphrase_ui(OSSL_ENCODER_CTX *ctx)
-{
-    UI_destroy_method(ctx->allocated_ui_method);
-    ctx->allocated_ui_method = NULL;
-    ctx->ui_method = NULL;
-    ctx->ui_data = NULL;
-}
-
 int OSSL_ENCODER_CTX_set_passphrase_ui(OSSL_ENCODER_CTX *ctx,
                                        const UI_METHOD *ui_method,
                                        void *ui_data)
 {
-    if (!ossl_assert(ctx != NULL)) {
-        ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
-        return 0;
-    }
-
-    encoder_ctx_reset_passphrase_ui(ctx);
-    ctx->ui_method = ui_method;
-    ctx->ui_data = ui_data;
-    return 1;
+    return ossl_pw_set_ui_method(&ctx->pwdata, ui_method, ui_data);
 }
 
 int OSSL_ENCODER_CTX_set_passphrase_cb(OSSL_ENCODER_CTX *ctx,
                                        pem_password_cb *cb, void *cbarg)
 {
-    if (!ossl_assert(ctx != NULL)) {
-        ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
-        return 0;
-    }
-
-    encoder_ctx_reset_passphrase_ui(ctx);
-    if (cb == NULL)
-        return 1;
-    ctx->ui_method =
-        ctx->allocated_ui_method = UI_UTIL_wrap_read_pem_callback(cb, 1);
-    ctx->ui_data = cbarg;
-
-    return ctx->ui_method != NULL;
+    return ossl_pw_set_pem_password_cb(&ctx->pwdata, cb, cbarg);
 }
 
 /*
@@ -125,7 +97,8 @@ static int encoder_write_cb(const OSSL_PARAM params[], void *arg)
     BIO *out = write_data->out;
 
     return ctx->encoder->encode_data(ctx->serctx, params, (OSSL_CORE_BIO *)out,
-                                     ossl_encoder_passphrase_out_cb, ctx);
+                                     ossl_pw_passphrase_callback_enc,
+                                     &ctx->pwdata);
 }
 
 /*
@@ -164,7 +137,8 @@ static int encoder_EVP_PKEY_to_bio(OSSL_ENCODER_CTX *ctx, BIO *out)
 
     return ctx->encoder->encode_object(ctx->serctx, keydata,
                                        (OSSL_CORE_BIO *)out,
-                                       ossl_encoder_passphrase_out_cb, ctx);
+                                       ossl_pw_passphrase_callback_enc,
+                                       &ctx->pwdata);
 }
 
 /*
diff --git a/crypto/encode_decode/endecode_pass.c b/crypto/encode_decode/endecode_pass.c
deleted file mode 100644
index 113f5eeb84..0000000000
--- a/crypto/encode_decode/endecode_pass.c
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * 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 "encoder_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;
-    }
-
-    if (ui_method != NULL) {
-        UI_set_method(ui, ui_method);
-        if (ui_data != NULL)
-            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;
-}
-
-/*
- * Encoders typically want to get an outgoing passphrase, while
- * decoders typically want to get en incoming passphrase.
- */
-int ossl_encoder_passphrase_out_cb(char *pass, size_t pass_size,
-                                   size_t *pass_len,
-                                   const OSSL_PARAM params[], void *arg)
-{
-    OSSL_ENCODER_CTX *ctx = arg;
-
-    if (!ossl_assert(ctx != NULL)) {
-        ERR_raise(ERR_LIB_OSSL_ENCODER, 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_ENCODER);
-}
-
-int ossl_decoder_passphrase_in_cb(char *pass, size_t pass_size,
-                                  size_t *pass_len,
-                                  const OSSL_PARAM params[], void *arg)
-{
-    OSSL_DECODER_CTX *ctx = arg;
-
-    if (!ossl_assert(ctx != NULL)) {
-        ERR_raise(ERR_LIB_OSSL_DECODER, 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_DECODER, 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_DECODER)) {
-        memcpy(ctx->cached_passphrase, pass, *pass_len);
-        ctx->cached_passphrase_len = *pass_len;
-        return 1;
-    }
-    return 0;
-}
diff --git a/crypto/params.c b/crypto/params.c
index 67ca4f0c83..4f7e25e0ca 100644
--- a/crypto/params.c
+++ b/crypto/params.c
@@ -969,3 +969,29 @@ OSSL_PARAM OSSL_PARAM_construct_end(void)
 
     return end;
 }
+
+static int get_string_ptr_internal(const OSSL_PARAM *p, const void **val,
+                                   size_t *used_len, unsigned int type)
+{
+    if (val == NULL || p == NULL || p->data_type != type)
+        return 0;
+    if (used_len != NULL)
+        *used_len = p->data_size;
+    *val = p->data;
+    return 1;
+}
+
+int OSSL_PARAM_get_utf8_string_ptr(const OSSL_PARAM *p, const char **val)
+{
+    return OSSL_PARAM_get_utf8_ptr(p, val)
+        || get_string_ptr_internal(p, (const void **)val, NULL,
+                                   OSSL_PARAM_UTF8_STRING);
+}
+
+int OSSL_PARAM_get_octet_string_ptr(const OSSL_PARAM *p, const void **val,
+                                    size_t *used_len)
+{
+    return OSSL_PARAM_get_octet_ptr(p, val, used_len)
+        || get_string_ptr_internal(p, val, used_len, OSSL_PARAM_OCTET_STRING);
+}
+
diff --git a/crypto/passphrase.c b/crypto/passphrase.c
new file mode 100644
index 0000000000..ac352697db
--- /dev/null
+++ b/crypto/passphrase.c
@@ -0,0 +1,324 @@
+/*
+ * 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 "internal/passphrase.h"
+
+void ossl_pw_clear_passphrase_data(struct ossl_passphrase_data_st *data)
+{
+    if (data != NULL) {
+        if (data->type == is_expl_passphrase)
+            OPENSSL_clear_free(data->_.expl_passphrase.passphrase_copy,
+                               data->_.expl_passphrase.passphrase_len);
+        ossl_pw_clear_passphrase_cache(data);
+        memset(data, 0, sizeof(*data));
+    }
+}
+
+void ossl_pw_clear_passphrase_cache(struct ossl_passphrase_data_st *data)
+{
+    OPENSSL_clear_free(data->cached_passphrase, data->cached_passphrase_len);
+    data->cached_passphrase = NULL;
+}
+
+int ossl_pw_set_passphrase(struct ossl_passphrase_data_st *data,
+                           const unsigned char *passphrase,
+                           size_t passphrase_len)
+{
+    if (!ossl_assert(data != NULL && passphrase != NULL)) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    ossl_pw_clear_passphrase_data(data);
+    data->type = is_expl_passphrase;
+    data->_.expl_passphrase.passphrase_copy =
+        OPENSSL_memdup(passphrase, passphrase_len);
+    if (data->_.expl_passphrase.passphrase_copy == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    data->_.expl_passphrase.passphrase_len = passphrase_len;
+    return 1;
+}
+
+int ossl_pw_set_pem_password_cb(struct ossl_passphrase_data_st *data,
+                                pem_password_cb *cb, void *cbarg)
+{
+    if (!ossl_assert(data != NULL && cb != NULL)) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    ossl_pw_clear_passphrase_data(data);
+    data->type = is_pem_password;
+    data->_.pem_password.password_cb = cb;
+    data->_.pem_password.password_cbarg = cbarg;
+    return 1;
+}
+
+int ossl_pw_set_ossl_passphrase_cb(struct ossl_passphrase_data_st *data,
+                                   OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    if (!ossl_assert(data != NULL && cb != NULL)) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    ossl_pw_clear_passphrase_data(data);
+    data->type = is_ossl_passphrase;
+    data->_.ossl_passphrase.passphrase_cb = cb;
+    data->_.ossl_passphrase.passphrase_cbarg = cbarg;
+    return 1;
+}
+
+int ossl_pw_set_ui_method(struct ossl_passphrase_data_st *data,
+                          const UI_METHOD *ui_method, void *ui_data)
+{
+    if (!ossl_assert(data != NULL && ui_method != NULL)) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    ossl_pw_clear_passphrase_data(data);
+    data->type = is_ui_method;
+    data->_.ui_method.ui_method = ui_method;
+    data->_.ui_method.ui_method_data = ui_data;
+    return 1;
+}
+
+int ossl_pw_enable_passphrase_caching(struct ossl_passphrase_data_st *data)
+{
+    data->flag_cache_passphrase = 1;
+    return 1;
+}
+
+int ossl_pw_disable_passphrase_caching(struct ossl_passphrase_data_st *data)
+{
+    data->flag_cache_passphrase = 0;
+    return 1;
+}
+
+
+/*-
+ * UI_METHOD processor.  It differs from UI_UTIL_read_pw() like this:
+ *
+ * 1.  It constructs a prompt on its own, based on |prompt_info|.
+ * 2.  It allocates a buffer for verification on its own.
+ * 3.  It raises errors.
+ * 4.  It reports back the length of the prompted pass phrase.
+ */
+static int do_ui_passphrase(char *pass, size_t pass_size, size_t *pass_len,
+                            const char *prompt_info, int verify,
+                            const UI_METHOD *ui_method, void *ui_data)
+{
+    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(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    if ((ui = UI_new()) == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    if (ui_method != NULL) {
+        UI_set_method(ui, ui_method);
+        if (ui_data != NULL)
+            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(ERR_LIB_CRYPTO, 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_CRYPTO, 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_CRYPTO, 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_CRYPTO, ERR_R_UI_LIB);
+            goto end;
+        }
+    }
+
+    switch (UI_process(ui)) {
+    case -2:
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERRUPTED_OR_CANCELLED);
+        break;
+    case -1:
+        ERR_raise(ERR_LIB_CRYPTO, 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;
+}
+
+/* Central pw prompting dispatcher */
+int ossl_pw_get_passphrase(char *pass, size_t pass_size, size_t *pass_len,
+                           const OSSL_PARAM params[], int verify,
+                           struct ossl_passphrase_data_st *data)
+{
+    const char *source = NULL;
+    size_t source_len = 0;
+    const char *prompt_info = NULL;
+    const UI_METHOD *ui_method = NULL;
+    UI_METHOD *allocated_ui_method = NULL;
+    void *ui_data = NULL;
+    const OSSL_PARAM *p = NULL;
+    int ret;
+
+    /* Handle explicit and cached passphrases */
+
+    if (data->type == is_expl_passphrase) {
+        source = data->_.expl_passphrase.passphrase_copy;
+        source_len = data->_.expl_passphrase.passphrase_len;
+    } else if (data->flag_cache_passphrase && data->cached_passphrase != NULL) {
+        source = data->cached_passphrase;
+        source_len = data->cached_passphrase_len;
+    }
+
+    if (source != NULL) {
+        if (source_len > pass_size)
+            source_len = pass_size;
+        memcpy(pass, source, source_len);
+        *pass_len = source_len;
+        return 1;
+    }
+
+    /* Handle the is_ossl_passphrase case...  that's pretty direct */
+
+    if (data->type == is_ossl_passphrase) {
+        OSSL_PASSPHRASE_CALLBACK *cb = data->_.ossl_passphrase.passphrase_cb;
+        void *cbarg = data->_.ossl_passphrase.passphrase_cbarg;
+
+        ret = cb(pass, pass_size, pass_len, params, cbarg);
+        goto do_cache;
+    }
+
+    /* Handle the is_pem_password and is_ui_method cases */
+
+    if ((p = OSSL_PARAM_locate_const(params,
+                                     OSSL_PASSPHRASE_PARAM_INFO)) != NULL) {
+        if (p->data_type != OSSL_PARAM_UTF8_STRING) {
+            ERR_raise_data(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT,
+                           "Prompt info data type incorrect");
+            return 0;
+        }
+        prompt_info = p->data;
+    }
+
+    if (data->type == is_pem_password) {
+        /* We use a UI wrapper for PEM */
+        pem_password_cb *cb = data->_.pem_password.password_cb;
+
+        ui_method = allocated_ui_method =
+            UI_UTIL_wrap_read_pem_callback(cb, verify);
+        ui_data = data->_.pem_password.password_cbarg;
+
+        if (ui_method == NULL) {
+            ERR_raise(ERR_LIB_CRYPTO, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+    } else if (data->type == is_ui_method) {
+        ui_method = data->_.ui_method.ui_method;
+        ui_data = data->_.ui_method.ui_method_data;
+    }
+
+    if (ui_method == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+
+    ret = do_ui_passphrase(pass, pass_size, pass_len, prompt_info, verify,
+                           ui_method, ui_data);
+
+    UI_destroy_method(allocated_ui_method);
+
+ do_cache:
+    if (ret && data->flag_cache_passphrase) {
+        if (*pass_len > data->cached_passphrase_len) {
+            void *new_cache =
+                OPENSSL_clear_realloc(data->cached_passphrase,
+                                      data->cached_passphrase_len,
+                                      *pass_len + 1);
+
+            if (new_cache == NULL) {
+                OPENSSL_cleanse(pass, *pass_len);
+                ERR_raise(ERR_LIB_CRYPTO, ERR_R_MALLOC_FAILURE);
+                return 0;
+            }
+            data->cached_passphrase = new_cache;
+        }
+        memcpy(data->cached_passphrase, pass, *pass_len);
+        data->cached_passphrase[*pass_len] = '\0';
+        data->cached_passphrase_len = *pass_len;
+    }
+
+    return ret;
+}
+
+int ossl_pw_pem_password(char *buf, int size, int rwflag, void *userdata)
+{
+    size_t password_len = 0;
+    OSSL_PARAM params[] = {
+        OSSL_PARAM_utf8_string(OSSL_PASSPHRASE_PARAM_INFO, NULL, 0),
+        OSSL_PARAM_END
+    };
+
+    params[0].data = "PEM";
+    if (ossl_pw_get_passphrase(buf, (size_t)size, &password_len, params,
+                               rwflag, userdata))
+        return (int)password_len;
+    return -1;
+}
+
+int ossl_pw_passphrase_callback_enc(char *pass, size_t pass_size,
+                                    size_t *pass_len,
+                                    const OSSL_PARAM params[], void *arg)
+{
+    return ossl_pw_get_passphrase(pass, pass_size, pass_len, params, 1, arg);
+}
+
+int ossl_pw_passphrase_callback_dec(char *pass, size_t pass_size,
+                                    size_t *pass_len,
+                                    const OSSL_PARAM params[], void *arg)
+{
+    return ossl_pw_get_passphrase(pass, pass_size, pass_len, params, 0, arg);
+}
diff --git a/crypto/store/build.info b/crypto/store/build.info
index 7d882f313e..33b59f0fae 100644
--- a/crypto/store/build.info
+++ b/crypto/store/build.info
@@ -1,4 +1,4 @@
 LIBS=../../libcrypto
 SOURCE[../../libcrypto]=\
-        store_err.c store_init.c store_lib.c store_register.c store_strings.c \
-        loader_file.c
+        store_err.c store_lib.c store_result.c store_strings.c store_meth.c \
+        store_init.c store_register.c loader_file.c
diff --git a/crypto/store/store_init.c b/crypto/store/store_init.c
index e1b953fbf8..b87730736d 100644
--- a/crypto/store/store_init.c
+++ b/crypto/store/store_init.c
@@ -21,7 +21,7 @@ DEFINE_RUN_ONCE_STATIC(do_store_init)
 int ossl_store_init_once(void)
 {
     if (!RUN_ONCE(&store_init, do_store_init)) {
-        OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_INIT_ONCE, ERR_R_MALLOC_FAILURE);
+        ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_MALLOC_FAILURE);
         return 0;
     }
     return 1;
diff --git a/crypto/store/store_lib.c b/crypto/store/store_lib.c
index 2ff92b7984..d0fdb38cd8 100644
--- a/crypto/store/store_lib.c
+++ b/crypto/store/store_lib.c
@@ -7,39 +7,39 @@
  * https://www.openssl.org/source/license.html
  */
 
-#include "e_os.h"
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
+
+#include "e_os.h"
+
 #include <openssl/crypto.h>
 #include <openssl/err.h>
 #include <openssl/trace.h>
+#include <openssl/core_names.h>
+#include <openssl/provider.h>
+#include <openssl/param_build.h>
 #include <openssl/store.h>
 #include "internal/thread_once.h"
+#include "internal/cryptlib.h"
+#include "internal/provider.h"
 #include "crypto/store.h"
 #include "store_local.h"
 
-struct ossl_store_ctx_st {
-    const OSSL_STORE_LOADER *loader;
-    OSSL_STORE_LOADER_CTX *loader_ctx;
-    const UI_METHOD *ui_method;
-    void *ui_data;
-    OSSL_STORE_post_process_info_fn post_process;
-    void *post_process_data;
-    int expected_type;
+static int ossl_store_close_it(OSSL_STORE_CTX *ctx);
 
-    /* 0 before the first STORE_load(), 1 otherwise */
-    int loading;
-};
-
-OSSL_STORE_CTX *OSSL_STORE_open_with_libctx(
-    const char *uri, OPENSSL_CTX *libctx, const char *propq,
-    const UI_METHOD *ui_method, void *ui_data,
-    OSSL_STORE_post_process_info_fn post_process, void *post_process_data)
+OSSL_STORE_CTX *
+OSSL_STORE_open_with_libctx(const char *uri,
+                            OPENSSL_CTX *libctx, const char *propq,
+                            const UI_METHOD *ui_method, void *ui_data,
+                            OSSL_STORE_post_process_info_fn post_process,
+                            void *post_process_data)
 {
     const OSSL_STORE_LOADER *loader = NULL;
+    OSSL_STORE_LOADER *fetched_loader = NULL;
     OSSL_STORE_LOADER_CTX *loader_ctx = NULL;
     OSSL_STORE_CTX *ctx = NULL;
+    char *propq_copy = NULL;
     char scheme_copy[256], *p, *schemes[2];
     size_t schemes_n = 0;
     size_t i;
@@ -70,32 +70,76 @@ OSSL_STORE_CTX *OSSL_STORE_open_with_libctx(
 
     ERR_set_mark();
 
-    /* Try each scheme until we find one that could open the URI */
+    /*
+     * Try each scheme until we find one that could open the URI.
+     *
+     * For each scheme, we look for the engine implementation first, and
+     * failing that, we then try to fetch a provided implementation.
+     * This is consistent with how we handle legacy / engine implementations
+     * elsewhere.
+     */
     for (i = 0; loader_ctx == NULL && i < schemes_n; i++) {
         OSSL_TRACE1(STORE, "Looking up scheme %s\n", schemes[i]);
         if ((loader = ossl_store_get0_loader_int(schemes[i])) != NULL) {
-            OSSL_TRACE1(STORE, "Found loader for scheme %s\n", schemes[i]);
             if (loader->open_with_libctx != NULL)
                 loader_ctx = loader->open_with_libctx(loader, uri, libctx, propq,
                                                       ui_method, ui_data);
             else
                 loader_ctx = loader->open(loader, uri, ui_method, ui_data);
-            OSSL_TRACE2(STORE, "Opened %s => %p\n", uri, (void *)loader_ctx);
+        }
+        if (loader == NULL
+            && (fetched_loader =
+                OSSL_STORE_LOADER_fetch(schemes[i], libctx, propq)) != NULL) {
+            const OSSL_PROVIDER *provider =
+                OSSL_STORE_LOADER_provider(fetched_loader);
+            void *provctx = OSSL_PROVIDER_get0_provider_ctx(provider);
+
+            loader_ctx = fetched_loader->p_open(provctx, uri);
+            if (loader_ctx == NULL) {
+                OSSL_STORE_LOADER_free(fetched_loader);
+                fetched_loader = NULL;
+            } else if (propq != NULL) {
+                OSSL_PARAM params[] = {
+                    OSSL_PARAM_utf8_string(OSSL_STORE_PARAM_PROPERTIES,
+                                           NULL, 0),
+                    OSSL_PARAM_END
+                };
+
+                params[0].data = (void *)propq;
+                if (!fetched_loader->p_set_ctx_params(loader_ctx, params)) {
+                    (void)fetched_loader->p_close(loader_ctx);
+                    OSSL_STORE_LOADER_free(fetched_loader);
+                    fetched_loader = NULL;
+                }
+            }
+            loader = fetched_loader;
         }
     }
 
+    if (loader != NULL)
+        OSSL_TRACE1(STORE, "Found loader for scheme %s\n", schemes[i]);
+
     if (loader_ctx == NULL)
         goto err;
 
-    if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) == NULL) {
-        OSSL_STOREerr(0, ERR_R_MALLOC_FAILURE);
+    OSSL_TRACE2(STORE, "Opened %s => %p\n", uri, (void *)loader_ctx);
+
+    if ((propq != NULL && (propq_copy = OPENSSL_strdup(propq)) == NULL)
+        || (ctx = OPENSSL_zalloc(sizeof(*ctx))) == NULL) {
+        ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_MALLOC_FAILURE);
         goto err;
     }
 
+    if ((ui_method != NULL
+         && !ossl_pw_set_ui_method(&ctx->pwdata, ui_method, ui_data))
+        || !ossl_pw_enable_passphrase_caching(&ctx->pwdata)) {
+        ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_CRYPTO_LIB);
+        goto err;
+    }
+    ctx->properties = propq_copy;
+    ctx->fetched_loader = fetched_loader;
     ctx->loader = loader;
     ctx->loader_ctx = loader_ctx;
-    ctx->ui_method = ui_method;
-    ctx->ui_data = ui_data;
     ctx->post_process = post_process;
     ctx->post_process_data = post_process_data;
 
@@ -111,13 +155,25 @@ OSSL_STORE_CTX *OSSL_STORE_open_with_libctx(
  err:
     ERR_clear_last_mark();
     if (loader_ctx != NULL) {
+        /*
+         * Temporary structure so OSSL_STORE_close() can work even when
+         * |ctx| couldn't be allocated properly
+         */
+        OSSL_STORE_CTX tmpctx = { NULL, };
+
+        tmpctx.fetched_loader = fetched_loader;
+        tmpctx.loader = loader;
+        tmpctx.loader_ctx = loader_ctx;
+
         /*
          * We ignore a returned error because we will return NULL anyway in
          * this case, so if something goes wrong when closing, that'll simply
          * just add another entry on the error stack.
          */
-        (void)loader->close(loader_ctx);
+        (void)ossl_store_close_it(&tmpctx);
     }
+    OSSL_STORE_LOADER_free(fetched_loader);
+    OPENSSL_free(propq_copy);
     return NULL;
 }
 
@@ -144,39 +200,148 @@ int OSSL_STORE_ctrl(OSSL_STORE_CTX *ctx, int cmd, ...)
 
 int OSSL_STORE_vctrl(OSSL_STORE_CTX *ctx, int cmd, va_list args)
 {
-    if (ctx->loader->ctrl != NULL)
+    if (ctx->fetched_loader != NULL) {
+        if (ctx->fetched_loader->p_set_ctx_params != NULL) {
+            OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END };
+
+            switch (cmd) {
+            case OSSL_STORE_C_USE_SECMEM:
+                {
+                    int on = *(va_arg(args, int *));
+
+                    params[0] = OSSL_PARAM_construct_int("use_secmem", &on);
+                }
+                break;
+            default:
+                break;
+            }
+
+            return ctx->fetched_loader->p_set_ctx_params(ctx->loader_ctx,
+                                                         params);
+        }
+    } else if (ctx->loader->ctrl != NULL) {
         return ctx->loader->ctrl(ctx->loader_ctx, cmd, args);
-    return 0;
+    }
+
+    /*
+     * If the fetched loader doesn't have a set_ctx_params or a ctrl, it's as
+     * if there was one that ignored our params, which usually returns 1.
+     */
+    return 1;
 }
 
 int OSSL_STORE_expect(OSSL_STORE_CTX *ctx, int expected_type)
 {
+    int ret = 1;
+
     if (ctx->loading) {
-        OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_EXPECT,
-                      OSSL_STORE_R_LOADING_STARTED);
+        ERR_raise(ERR_LIB_OSSL_STORE, OSSL_STORE_R_LOADING_STARTED);
         return 0;
     }
 
     ctx->expected_type = expected_type;
-    if (ctx->loader->expect != NULL)
-        return ctx->loader->expect(ctx->loader_ctx, expected_type);
-    return 1;
+    if (ctx->fetched_loader != NULL
+        && ctx->fetched_loader->p_set_ctx_params != NULL) {
+        OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END };
+
+        params[0] =
+            OSSL_PARAM_construct_int(OSSL_STORE_PARAM_EXPECT, &expected_type);
+        ret = ctx->fetched_loader->p_set_ctx_params(ctx->loader_ctx, params);
+    }
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+    if (ctx->fetched_loader == NULL
+        && ctx->loader->expect != NULL) {
+        ret = ctx->loader->expect(ctx->loader_ctx, expected_type);
+    }
+#endif
+    return ret;
 }
 
 int OSSL_STORE_find(OSSL_STORE_CTX *ctx, const OSSL_STORE_SEARCH *search)
 {
+    int ret = 1;
+
     if (ctx->loading) {
-        OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_FIND,
-                      OSSL_STORE_R_LOADING_STARTED);
+        ERR_raise(ERR_LIB_OSSL_STORE, OSSL_STORE_R_LOADING_STARTED);
         return 0;
     }
-    if (ctx->loader->find == NULL) {
-        OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_FIND,
-                      OSSL_STORE_R_UNSUPPORTED_OPERATION);
-        return 0;
+
+    if (ctx->fetched_loader != NULL) {
+        OSSL_PARAM_BLD *bld;
+        OSSL_PARAM *params;
+        /* OSSL_STORE_SEARCH_BY_NAME, OSSL_STORE_SEARCH_BY_ISSUER_SERIAL*/
+        void *name_der = NULL;
+        int name_der_sz;
+        /* OSSL_STORE_SEARCH_BY_ISSUER_SERIAL */
+        BIGNUM *number = NULL;
+
+        if (ctx->fetched_loader->p_set_ctx_params == NULL) {
+            ERR_raise(ERR_LIB_OSSL_STORE, OSSL_STORE_R_UNSUPPORTED_OPERATION);
+            return 0;
+        }
+
+        if ((bld = OSSL_PARAM_BLD_new()) == NULL) {
+            ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+
+        ret = 0;                 /* Assume the worst */
+
+        switch (search->search_type) {
+        case OSSL_STORE_SEARCH_BY_NAME:
+            if ((name_der_sz = i2d_X509_NAME(search->name,
+                                             (unsigned char **)&name_der)) > 0
+                && OSSL_PARAM_BLD_push_octet_string(bld,
+                                                    OSSL_STORE_PARAM_SUBJECT,
+                                                    name_der, name_der_sz))
+                ret = 1;
+            break;
+        case OSSL_STORE_SEARCH_BY_ISSUER_SERIAL:
+            if ((name_der_sz = i2d_X509_NAME(search->name,
+                                             (unsigned char **)&name_der)) > 0
+                && (number = ASN1_INTEGER_to_BN(search->serial, NULL)) != NULL
+                && OSSL_PARAM_BLD_push_octet_string(bld,
+                                                    OSSL_STORE_PARAM_ISSUER,
+                                                    name_der, name_der_sz)
+                && OSSL_PARAM_BLD_push_BN(bld, OSSL_STORE_PARAM_SERIAL,
+                                          number))
+                ret = 1;
+            break;
+        case OSSL_STORE_SEARCH_BY_KEY_FINGERPRINT:
+            if (OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_STORE_PARAM_DIGEST,
+                                                EVP_MD_name(search->digest), 0)
+                && OSSL_PARAM_BLD_push_octet_string(bld,
+                                                    OSSL_STORE_PARAM_FINGERPRINT,
+                                                    search->string,
+                                                    search->stringlength))
+                ret = 1;
+            break;
+        case OSSL_STORE_SEARCH_BY_ALIAS:
+            if (OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_STORE_PARAM_ALIAS,
+                                                (char *)search->string,
+                                                search->stringlength))
+                ret = 1;
+            break;
+        }
+        if (ret) {
+            params = OSSL_PARAM_BLD_to_param(bld);
+            ret = ctx->fetched_loader->p_set_ctx_params(ctx->loader_ctx,
+                                                        params);
+            OSSL_PARAM_BLD_free_params(params);
+        }
+        OSSL_PARAM_BLD_free(bld);
+        OPENSSL_free(name_der);
+        BN_free(number);
+    } else {
+        /* legacy loader section */
+        if (ctx->loader->find == NULL) {
+            ERR_raise(ERR_LIB_OSSL_STORE, OSSL_STORE_R_UNSUPPORTED_OPERATION);
+            return 0;
+        }
+        ret = ctx->loader->find(ctx->loader_ctx, search);
     }
 
-    return ctx->loader->find(ctx->loader_ctx, search);
+    return ret;
 }
 
 OSSL_STORE_INFO *OSSL_STORE_load(OSSL_STORE_CTX *ctx)
@@ -188,8 +353,40 @@ OSSL_STORE_INFO *OSSL_STORE_load(OSSL_STORE_CTX *ctx)
     if (OSSL_STORE_eof(ctx))
         return NULL;
 
-    OSSL_TRACE(STORE, "Loading next object\n");
-    v = ctx->loader->load(ctx->loader_ctx, ctx->ui_method, ctx->ui_data);
+    if (ctx->loader != NULL)
+        OSSL_TRACE(STORE, "Loading next object\n");
+
+    if (ctx->cached_info != NULL
+        && sk_OSSL_STORE_INFO_num(ctx->cached_info) == 0) {
+        sk_OSSL_STORE_INFO_free(ctx->cached_info);
+        ctx->cached_info = NULL;
+    }
+
+    if (ctx->cached_info != NULL) {
+        v = sk_OSSL_STORE_INFO_shift(ctx->cached_info);
+    } else {
+        if (ctx->fetched_loader != NULL) {
+            struct ossl_load_result_data_st load_data;
+
+            load_data.v = NULL;
+            load_data.ctx = ctx;
+
+            if (!ctx->fetched_loader->p_load(ctx->loader_ctx,
+                                             ossl_store_handle_load_result,
+                                             &load_data,
+                                             ossl_pw_passphrase_callback_dec,
+                                             &ctx->pwdata)) {
+                if (!OSSL_STORE_eof(ctx))
+                    ctx->error_flag = 1;
+                return NULL;
+            }
+            v = load_data.v;
+        }
+        if (ctx->fetched_loader == NULL)
+            v = ctx->loader->load(ctx->loader_ctx,
+                                  ctx->pwdata._.ui_method.ui_method,
+                                  ctx->pwdata._.ui_method.ui_method_data);
+    }
 
     if (ctx->post_process != NULL && v != NULL) {
         v = ctx->post_process(v, ctx->post_process_data);
@@ -206,13 +403,6 @@ OSSL_STORE_INFO *OSSL_STORE_load(OSSL_STORE_CTX *ctx)
         int returned_type = OSSL_STORE_INFO_get_type(v);
 
         if (returned_type != OSSL_STORE_INFO_NAME && returned_type != 0) {
-            /*
-             * Soft assert here so those who want to harsly weed out faulty
-             * loaders can do so using a debugging version of libcrypto.
-             */
-            if (ctx->loader->expect != NULL)
-                assert(ctx->expected_type == returned_type);
-
             if (ctx->expected_type != returned_type) {
                 OSSL_STORE_INFO_free(v);
                 goto again;
@@ -220,6 +410,7 @@ OSSL_STORE_INFO *OSSL_STORE_load(OSSL_STORE_CTX *ctx)
         }
     }
 
+    ossl_pw_clear_passphrase_cache(&ctx->pwdata);
     if (v != NULL)
         OSSL_TRACE1(STORE, "Got a %s\n",
                     OSSL_STORE_INFO_type_string(OSSL_STORE_INFO_get_type(v)));
@@ -229,25 +420,51 @@ OSSL_STORE_INFO *OSSL_STORE_load(OSSL_STORE_CTX *ctx)
 
 int OSSL_STORE_error(OSSL_STORE_CTX *ctx)
 {
-    return ctx->loader->error(ctx->loader_ctx);
+    int ret = 1;
+
+    if (ctx->fetched_loader != NULL)
+        ret = ctx->error_flag;
+    if (ctx->fetched_loader == NULL)
+        ret = ctx->loader->error(ctx->loader_ctx);
+    return ret;
 }
 
 int OSSL_STORE_eof(OSSL_STORE_CTX *ctx)
 {
-    return ctx->loader->eof(ctx->loader_ctx);
+    int ret = 1;
+
+    if (ctx->fetched_loader != NULL)
+        ret = ctx->loader->p_eof(ctx->loader_ctx);
+    if (ctx->fetched_loader == NULL)
+        ret = ctx->loader->eof(ctx->loader_ctx);
+    return ret;
 }
 
-int OSSL_STORE_close(OSSL_STORE_CTX *ctx)
+static int ossl_store_close_it(OSSL_STORE_CTX *ctx)
 {
-    int loader_ret;
+    int ret = 0;
 
     if (ctx == NULL)
         return 1;
     OSSL_TRACE1(STORE, "Closing %p\n", (void *)ctx->loader_ctx);
-    loader_ret = ctx->loader->close(ctx->loader_ctx);
+
+    if (ctx->fetched_loader != NULL)
+        ret = ctx->loader->p_close(ctx->loader_ctx);
+    if (ctx->fetched_loader == NULL)
+        ret = ctx->loader->close(ctx->loader_ctx);
+
+    sk_OSSL_STORE_INFO_pop_free(ctx->cached_info, OSSL_STORE_INFO_free);
+    OSSL_STORE_LOADER_free(ctx->fetched_loader);
+    OPENSSL_free(ctx->properties);
+    return ret;
+}
+
+int OSSL_STORE_close(OSSL_STORE_CTX *ctx)
+{
+    int ret = ossl_store_close_it(ctx);
 
     OPENSSL_free(ctx);
-    return loader_ret;
+    return ret;
 }
 
 /*
@@ -274,8 +491,7 @@ OSSL_STORE_INFO *OSSL_STORE_INFO_new_NAME(char *name)
     OSSL_STORE_INFO *info = store_info_new(OSSL_STORE_INFO_NAME, NULL);
 
     if (info == NULL) {
-        OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_INFO_NEW_NAME,
-                      ERR_R_MALLOC_FAILURE);
+        ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_MALLOC_FAILURE);
         return NULL;
     }
 
@@ -288,8 +504,7 @@ OSSL_STORE_INFO *OSSL_STORE_INFO_new_NAME(char *name)
 int OSSL_STORE_INFO_set0_NAME_description(OSSL_STORE_INFO *info, char *desc)
 {
     if (info->type != OSSL_STORE_INFO_NAME) {
-        OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_INFO_SET0_NAME_DESCRIPTION,
-                      ERR_R_PASSED_INVALID_ARGUMENT);
+        ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_PASSED_INVALID_ARGUMENT);
         return 0;
     }
 
@@ -302,8 +517,7 @@ OSSL_STORE_INFO *OSSL_STORE_INFO_new_PARAMS(EVP_PKEY *params)
     OSSL_STORE_INFO *info = store_info_new(OSSL_STORE_INFO_PARAMS, params);
 
     if (info == NULL)
-        OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_INFO_NEW_PARAMS,
-                      ERR_R_MALLOC_FAILURE);
+        ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_MALLOC_FAILURE);
     return info;
 }
 
@@ -321,8 +535,7 @@ OSSL_STORE_INFO *OSSL_STORE_INFO_new_PKEY(EVP_PKEY *pkey)
     OSSL_STORE_INFO *info = store_info_new(OSSL_STORE_INFO_PKEY, pkey);
 
     if (info == NULL)
-        OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_INFO_NEW_PKEY,
-                      ERR_R_MALLOC_FAILURE);
+        ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_MALLOC_FAILURE);
     return info;
 }
 
@@ -331,8 +544,7 @@ OSSL_STORE_INFO *OSSL_STORE_INFO_new_CERT(X509 *x509)
     OSSL_STORE_INFO *info = store_info_new(OSSL_STORE_INFO_CERT, x509);
 
     if (info == NULL)
-        OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_INFO_NEW_CERT,
-                      ERR_R_MALLOC_FAILURE);
+        ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_MALLOC_FAILURE);
     return info;
 }
 
@@ -341,8 +553,7 @@ OSSL_STORE_INFO *OSSL_STORE_INFO_new_CRL(X509_CRL *crl)
     OSSL_STORE_INFO *info = store_info_new(OSSL_STORE_INFO_CRL, crl);
 
     if (info == NULL)
-        OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_INFO_NEW_CRL,
-                      ERR_R_MALLOC_FAILURE);
+        ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_MALLOC_FAILURE);
     return info;
 }
 
@@ -367,12 +578,10 @@ char *OSSL_STORE_INFO_get1_NAME(const OSSL_STORE_INFO *info)
         char *ret = OPENSSL_strdup(info->_.name.name);
 
         if (ret == NULL)
-            OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_INFO_GET1_NAME,
-                          ERR_R_MALLOC_FAILURE);
+            ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_MALLOC_FAILURE);
         return ret;
     }
-    OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_INFO_GET1_NAME,
-                  OSSL_STORE_R_NOT_A_NAME);
+    ERR_raise(ERR_LIB_OSSL_STORE, OSSL_STORE_R_NOT_A_NAME);
     return NULL;
 }
 
@@ -390,12 +599,10 @@ char *OSSL_STORE_INFO_get1_NAME_description(const OSSL_STORE_INFO *info)
                                    ? info->_.name.desc : "");
 
         if (ret == NULL)
-            OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_INFO_GET1_NAME_DESCRIPTION,
-                     ERR_R_MALLOC_FAILURE);
+            ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_MALLOC_FAILURE);
         return ret;
     }
-    OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_INFO_GET1_NAME_DESCRIPTION,
-                  OSSL_STORE_R_NOT_A_NAME);
+    ERR_raise(ERR_LIB_OSSL_STORE, OSSL_STORE_R_NOT_A_NAME);
     return NULL;
 }
 
@@ -412,8 +619,7 @@ EVP_PKEY *OSSL_STORE_INFO_get1_PARAMS(const OSSL_STORE_INFO *info)
         EVP_PKEY_up_ref(info->_.params);
         return info->_.params;
     }
-    OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_INFO_GET1_PARAMS,
-                  OSSL_STORE_R_NOT_PARAMETERS);
+    ERR_raise(ERR_LIB_OSSL_STORE, OSSL_STORE_R_NOT_PARAMETERS);
     return NULL;
 }
 
@@ -447,8 +653,7 @@ EVP_PKEY *OSSL_STORE_INFO_get1_PKEY(const OSSL_STORE_INFO *info)
         EVP_PKEY_up_ref(info->_.pkey);
         return info->_.pkey;
     }
-    OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_INFO_GET1_PKEY,
-                  OSSL_STORE_R_NOT_A_PRIVATE_KEY);
+    ERR_raise(ERR_LIB_OSSL_STORE, OSSL_STORE_R_NOT_A_PRIVATE_KEY);
     return NULL;
 }
 
@@ -465,8 +670,7 @@ X509 *OSSL_STORE_INFO_get1_CERT(const OSSL_STORE_INFO *info)
         X509_up_ref(info->_.x509);
         return info->_.x509;
     }
-    OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_INFO_GET1_CERT,
-                  OSSL_STORE_R_NOT_A_CERTIFICATE);
+    ERR_raise(ERR_LIB_OSSL_STORE, OSSL_STORE_R_NOT_A_CERTIFICATE);
     return NULL;
 }
 
@@ -483,8 +687,7 @@ X509_CRL *OSSL_STORE_INFO_get1_CRL(const OSSL_STORE_INFO *info)
         X509_CRL_up_ref(info->_.crl);
         return info->_.crl;
     }
-    OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_INFO_GET1_CRL,
-                  OSSL_STORE_R_NOT_A_CRL);
+    ERR_raise(ERR_LIB_OSSL_STORE, OSSL_STORE_R_NOT_A_CRL);
     return NULL;
 }
 
@@ -525,12 +728,53 @@ void OSSL_STORE_INFO_free(OSSL_STORE_INFO *info)
 
 int OSSL_STORE_supports_search(OSSL_STORE_CTX *ctx, int search_type)
 {
-    OSSL_STORE_SEARCH tmp_search;
+    int ret = 0;
+
+    if (ctx->fetched_loader != NULL) {
+        void *provctx =
+            ossl_provider_ctx(OSSL_STORE_LOADER_provider(ctx->fetched_loader));
+        const OSSL_PARAM *params;
+        const OSSL_PARAM *p_subject = NULL;
+        const OSSL_PARAM *p_issuer = NULL;
+        const OSSL_PARAM *p_serial = NULL;
+        const OSSL_PARAM *p_fingerprint = NULL;
+        const OSSL_PARAM *p_alias = NULL;
+
+        if (ctx->fetched_loader->p_settable_ctx_params == NULL)
+            return 0;
+
+        params = ctx->fetched_loader->p_settable_ctx_params(provctx);
+        p_subject = OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_SUBJECT);
+        p_issuer = OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_ISSUER);
+        p_serial = OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_SERIAL);
+        p_fingerprint =
+            OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_FINGERPRINT);
+        p_alias = OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_ALIAS);
+
+        switch (search_type) {
+        case OSSL_STORE_SEARCH_BY_NAME:
+            ret = (p_subject != NULL);
+            break;
+        case OSSL_STORE_SEARCH_BY_ISSUER_SERIAL:
+            ret = (p_issuer != NULL && p_serial != NULL);
+            break;
+        case OSSL_STORE_SEARCH_BY_KEY_FINGERPRINT:
+            ret = (p_fingerprint != NULL);
+            break;
+        case OSSL_STORE_SEARCH_BY_ALIAS:
+            ret = (p_alias != NULL);
+            break;
+        }
+    }
+    if (ctx->fetched_loader == NULL) {
+        OSSL_STORE_SEARCH tmp_search;
 
-    if (ctx->loader->find == NULL)
-        return 0;
-    tmp_search.search_type = search_type;
-    return ctx->loader->find(NULL, &tmp_search);
+        if (ctx->loader->find == NULL)
+            return 0;
+        tmp_search.search_type = search_type;
+        ret = ctx->loader->find(NULL, &tmp_search);
+    }
+    return ret;
 }
 
 /* Search term constructors */
@@ -539,8 +783,7 @@ OSSL_STORE_SEARCH *OSSL_STORE_SEARCH_by_name(X509_NAME *name)
     OSSL_STORE_SEARCH *search = OPENSSL_zalloc(sizeof(*search));
 
     if (search == NULL) {
-        OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_SEARCH_BY_NAME,
-                      ERR_R_MALLOC_FAILURE);
+        ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_MALLOC_FAILURE);
         return NULL;
     }
 
@@ -555,8 +798,7 @@ OSSL_STORE_SEARCH *OSSL_STORE_SEARCH_by_issuer_serial(X509_NAME *name,
     OSSL_STORE_SEARCH *search = OPENSSL_zalloc(sizeof(*search));
 
     if (search == NULL) {
-        OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_SEARCH_BY_ISSUER_SERIAL,
-                      ERR_R_MALLOC_FAILURE);
+        ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_MALLOC_FAILURE);
         return NULL;
     }
 
@@ -573,20 +815,16 @@ OSSL_STORE_SEARCH *OSSL_STORE_SEARCH_by_key_fingerprint(const EVP_MD *digest,
     OSSL_STORE_SEARCH *search = OPENSSL_zalloc(sizeof(*search));
 
     if (search == NULL) {
-        OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_SEARCH_BY_KEY_FINGERPRINT,
-                      ERR_R_MALLOC_FAILURE);
+        ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_MALLOC_FAILURE);
         return NULL;
     }
 
     if (digest != NULL && len != (size_t)EVP_MD_size(digest)) {
-        char buf1[20], buf2[20];
-
-        BIO_snprintf(buf1, sizeof(buf1), "%d", EVP_MD_size(digest));
-        BIO_snprintf(buf2, sizeof(buf2), "%zu", len);
-        OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_SEARCH_BY_KEY_FINGERPRINT,
-                      OSSL_STORE_R_FINGERPRINT_SIZE_DOES_NOT_MATCH_DIGEST);
-        ERR_add_error_data(5, EVP_MD_name(digest), " size is ", buf1,
-                           ", fingerprint size is ", buf2);
+        ERR_raise_data(ERR_LIB_OSSL_STORE,
+                       OSSL_STORE_R_FINGERPRINT_SIZE_DOES_NOT_MATCH_DIGEST,
+                       "%s size is %d, fingerprint size is %zu",
+                       EVP_MD_name(digest), EVP_MD_size(digest), len);
+        return NULL;
     }
 
     search->search_type = OSSL_STORE_SEARCH_BY_KEY_FINGERPRINT;
@@ -601,8 +839,7 @@ OSSL_STORE_SEARCH *OSSL_STORE_SEARCH_by_alias(const char *alias)
     OSSL_STORE_SEARCH *search = OPENSSL_zalloc(sizeof(*search));
 
     if (search == NULL) {
-        OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_SEARCH_BY_ALIAS,
-                      ERR_R_MALLOC_FAILURE);
+        ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_MALLOC_FAILURE);
         return NULL;
     }
 
@@ -659,8 +896,7 @@ OSSL_STORE_INFO *ossl_store_info_new_EMBEDDED(const char *new_pem_name,
     OSSL_STORE_INFO *info = store_info_new(OSSL_STORE_INFO_EMBEDDED, NULL);
 
     if (info == NULL) {
-        OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_INFO_NEW_EMBEDDED,
-                      ERR_R_MALLOC_FAILURE);
+        ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_MALLOC_FAILURE);
         return NULL;
     }
 
@@ -669,8 +905,7 @@ OSSL_STORE_INFO *ossl_store_info_new_EMBEDDED(const char *new_pem_name,
         new_pem_name == NULL ? NULL : OPENSSL_strdup(new_pem_name);
 
     if (new_pem_name != NULL && info->_.embedded.pem_name == NULL) {
-        OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_INFO_NEW_EMBEDDED,
-                      ERR_R_MALLOC_FAILURE);
+        ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_MALLOC_FAILURE);
         OSSL_STORE_INFO_free(info);
         info = NULL;
     }
@@ -698,25 +933,58 @@ OSSL_STORE_CTX *OSSL_STORE_attach(BIO *bp, const char *scheme,
                                   OSSL_STORE_post_process_info_fn post_process,
                                   void *post_process_data)
 {
-    OSSL_STORE_CTX *ctx = NULL;
     const OSSL_STORE_LOADER *loader = NULL;
+    OSSL_STORE_LOADER *fetched_loader = NULL;
     OSSL_STORE_LOADER_CTX *loader_ctx = NULL;
+    OSSL_STORE_CTX *ctx = NULL;
 
-    if ((loader =
-         ossl_store_get0_loader_int(scheme != NULL ? scheme : "file")) == NULL
-        || (loader_ctx = loader->attach(loader, bp, libctx, propq,
-                                        ui_method, ui_data)) == NULL)
+    if (scheme == NULL)
+        scheme = "file";
+
+    OSSL_TRACE1(STORE, "Looking up scheme %s\n", scheme);
+    if ((loader = ossl_store_get0_loader_int(scheme)) != NULL)
+        loader_ctx = loader->attach(loader, bp, libctx, propq,
+                                    ui_method, ui_data);
+    if (loader == NULL
+        && (fetched_loader =
+            OSSL_STORE_LOADER_fetch(scheme, libctx, propq)) != NULL) {
+        const OSSL_PROVIDER *provider =
+            OSSL_STORE_LOADER_provider(fetched_loader);
+        void *provctx = OSSL_PROVIDER_get0_provider_ctx(provider);
+
+        if ((loader_ctx =
+             fetched_loader->p_attach(provctx, (OSSL_CORE_BIO *)bp)) == NULL) {
+            OSSL_STORE_LOADER_free(fetched_loader);
+            fetched_loader = NULL;
+        } else if (propq != NULL) {
+            OSSL_PARAM params[] = {
+                OSSL_PARAM_utf8_string(OSSL_STORE_PARAM_PROPERTIES,
+                                       NULL, 0),
+                OSSL_PARAM_END
+            };
+
+            params[0].data = (void *)propq;
+            if (!fetched_loader->p_set_ctx_params(loader_ctx, params)) {
+                (void)fetched_loader->p_close(loader_ctx);
+                OSSL_STORE_LOADER_free(fetched_loader);
+                fetched_loader = NULL;
+            }
+        }
+        loader = fetched_loader;
+    }
+
+    if (loader_ctx == NULL)
         return NULL;
 
     if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) == NULL) {
-        OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_ATTACH, ERR_R_MALLOC_FAILURE);
+        ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_MALLOC_FAILURE);
         return NULL;
     }
 
+    (void)ossl_pw_set_ui_method(&ctx->pwdata, ui_method, ui_data);
+    ctx->fetched_loader = fetched_loader;
     ctx->loader = loader;
     ctx->loader_ctx = loader_ctx;
-    ctx->ui_method = ui_method;
-    ctx->ui_data = ui_data;
     ctx->post_process = post_process;
     ctx->post_process_data = post_process_data;
 
diff --git a/crypto/store/store_local.h b/crypto/store/store_local.h
index 47cba87f86..619e547aae 100644
--- a/crypto/store/store_local.h
+++ b/crypto/store/store_local.h
@@ -7,13 +7,16 @@
  * https://www.openssl.org/source/license.html
  */
 
+#include <openssl/core_dispatch.h>
 #include "internal/thread_once.h"
+#include "internal/refcount.h"
 #include <openssl/dsa.h>
 #include <openssl/engine.h>
 #include <openssl/evp.h>
 #include <openssl/lhash.h>
 #include <openssl/x509.h>
 #include <openssl/store.h>
+#include "internal/passphrase.h"
 
 /*-
  *  OSSL_STORE_INFO stuff
@@ -100,6 +103,7 @@ OSSL_STORE_LOADER *ossl_store_unregister_loader_int(const char *scheme);
 
 /* loader stuff */
 struct ossl_store_loader_st {
+    /* Legacy stuff */
     const char *scheme;
     ENGINE *engine;
     OSSL_STORE_open_fn open;
@@ -112,12 +116,58 @@ struct ossl_store_loader_st {
     OSSL_STORE_error_fn error;
     OSSL_STORE_close_fn close;
     OSSL_STORE_open_with_libctx_fn open_with_libctx;
+
+    /* Provider stuff */
+    OSSL_PROVIDER *prov;
+    int scheme_id;
+    const char *propdef;
+
+    CRYPTO_REF_COUNT refcnt;
+    CRYPTO_RWLOCK *lock;
+
+    OSSL_FUNC_store_open_fn *p_open;
+    OSSL_FUNC_store_attach_fn *p_attach;
+    OSSL_FUNC_store_settable_ctx_params_fn *p_settable_ctx_params;
+    OSSL_FUNC_store_set_ctx_params_fn *p_set_ctx_params;
+    OSSL_FUNC_store_load_fn *p_load;
+    OSSL_FUNC_store_eof_fn *p_eof;
+    OSSL_FUNC_store_close_fn *p_close;
+    OSSL_FUNC_store_export_object_fn *p_export_object;
 };
 DEFINE_LHASH_OF(OSSL_STORE_LOADER);
 
 const OSSL_STORE_LOADER *ossl_store_get0_loader_int(const char *scheme);
 void ossl_store_destroy_loaders_int(void);
 
+/*-
+ *  OSSL_STORE_CTX stuff
+ *  ---------------------
+ */
+
+struct ossl_store_ctx_st {
+    const OSSL_STORE_LOADER *loader; /* legacy */
+    OSSL_STORE_LOADER *fetched_loader;
+    OSSL_STORE_LOADER_CTX *loader_ctx;
+    OSSL_STORE_post_process_info_fn post_process;
+    void *post_process_data;
+    int expected_type;
+
+    char *properties;
+
+    /* 0 before the first STORE_load(), 1 otherwise */
+    int loading;
+    /* 1 on load error, only valid for fetched loaders */
+    int error_flag;
+
+    /*
+     * Cache of stuff, to be able to return the contents of a PKCS#12
+     * blob, one object at a time.
+     */
+    STACK_OF(OSSL_STORE_INFO) *cached_info;
+
+    struct ossl_passphrase_data_st pwdata;
+};
+
 /*-
  *  OSSL_STORE init stuff
  *  ---------------------
@@ -125,3 +175,29 @@ void ossl_store_destroy_loaders_int(void);
 
 int ossl_store_init_once(void);
 int ossl_store_file_loader_init(void);
+
+/*-
+ *  'file' scheme stuff
+ *  -------------------
+ */
+
+OSSL_STORE_LOADER_CTX *ossl_store_file_attach_pem_bio_int(BIO *bp);
+int ossl_store_file_detach_pem_bio_int(OSSL_STORE_LOADER_CTX *ctx);
+
+/*-
+ * Provider stuff
+ * -------------------
+ */
+OSSL_STORE_LOADER *ossl_store_loader_fetch(OPENSSL_CTX *libctx,
+                                           const char *scheme,
+                                           const char *properties);
+OSSL_STORE_LOADER *ossl_store_loader_fetch_by_number(OPENSSL_CTX *libctx,
+                                                     int scheme_id,
+                                                     const char *properties);
+
+/* Standard function to handle the result from OSSL_FUNC_store_load() */
+struct ossl_load_result_data_st {
+    OSSL_STORE_INFO *v;          /* To be filled in */
+    OSSL_STORE_CTX *ctx;
+};
+OSSL_CALLBACK ossl_store_handle_load_result;
diff --git a/crypto/store/store_meth.c b/crypto/store/store_meth.c
new file mode 100644
index 0000000000..6af5ec14f2
--- /dev/null
+++ b/crypto/store/store_meth.c
@@ -0,0 +1,425 @@
+/*
+ * 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/store.h>
+#include <openssl/crypto.h>
+#include "internal/core.h"
+#include "internal/namemap.h"
+#include "internal/property.h"
+#include "internal/provider.h"
+#include "store_local.h"
+
+int OSSL_STORE_LOADER_up_ref(OSSL_STORE_LOADER *loader)
+{
+    int ref = 0;
+
+    if (loader->prov != NULL)
+        CRYPTO_UP_REF(&loader->refcnt, &ref, loader->lock);
+    return 1;
+}
+
+void OSSL_STORE_LOADER_free(OSSL_STORE_LOADER *loader)
+{
+    if (loader != NULL && loader->prov != NULL) {
+        int i;
+
+        CRYPTO_DOWN_REF(&loader->refcnt, &i, loader->lock);
+        if (i > 0)
+            return;
+        ossl_provider_free(loader->prov);
+        CRYPTO_THREAD_lock_free(loader->lock);
+    }
+    OPENSSL_free(loader);
+}
+
+/*
+ * OSSL_STORE_LOADER_new() expects the scheme as a constant string,
+ * which we currently don't have, so we need an alternative allocator.
+ */
+static OSSL_STORE_LOADER *new_loader(OSSL_PROVIDER *prov)
+{
+    OSSL_STORE_LOADER *loader;
+
+    if ((loader = OPENSSL_zalloc(sizeof(*loader))) == NULL
+        || (loader->lock = CRYPTO_THREAD_lock_new()) == NULL) {
+        OPENSSL_free(loader);
+        return NULL;
+    }
+    loader->prov = prov;
+    ossl_provider_up_ref(prov);
+    loader->refcnt = 1;
+
+    return loader;
+}
+
+static int up_ref_loader(void *method)
+{
+    return OSSL_STORE_LOADER_up_ref(method);
+}
+
+static void free_loader(void *method)
+{
+    OSSL_STORE_LOADER_free(method);
+}
+
+/* Permanent loader method store, constructor and destructor */
+static void loader_store_free(void *vstore)
+{
+    ossl_method_store_free(vstore);
+}
+
+static void *loader_store_new(OPENSSL_CTX *ctx)
+{
+    return ossl_method_store_new(ctx);
+}
+
+
+static const OPENSSL_CTX_METHOD loader_store_method = {
+    loader_store_new,
+    loader_store_free,
+};
+
+/* Data to be passed through ossl_method_construct() */
+struct loader_data_st {
+    OPENSSL_CTX *libctx;
+    OSSL_METHOD_CONSTRUCT_METHOD *mcm;
+    int scheme_id;               /* For get_loader_from_store() */
+    const char *scheme;          /* For get_loader_from_store() */
+    const char *propquery;       /* For get_loader_from_store() */
+};
+
+/*
+ * Generic routines to fetch / create OSSL_STORE methods with
+ * ossl_method_construct()
+ */
+
+/* Temporary loader method store, constructor and destructor */
+static void *alloc_tmp_loader_store(OPENSSL_CTX *ctx)
+{
+    return ossl_method_store_new(ctx);
+}
+
+ static void dealloc_tmp_loader_store(void *store)
+{
+    if (store != NULL)
+        ossl_method_store_free(store);
+}
+
+/* Get the permanent loader store */
+static OSSL_METHOD_STORE *get_loader_store(OPENSSL_CTX *libctx)
+{
+    return openssl_ctx_get_data(libctx, OPENSSL_CTX_STORE_LOADER_STORE_INDEX,
+                                &loader_store_method);
+}
+
+/* Get loader methods from a store, or put one in */
+static void *get_loader_from_store(OPENSSL_CTX *libctx, void *store,
+                                   void *data)
+{
+    struct loader_data_st *methdata = data;
+    void *method = NULL;
+    int id;
+
+    if ((id = methdata->scheme_id) == 0) {
+        OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx);
+
+        id = ossl_namemap_name2num(namemap, methdata->scheme);
+    }
+
+    if (store == NULL
+        && (store = get_loader_store(libctx)) == NULL)
+        return NULL;
+
+    if (!ossl_method_store_fetch(store, id, methdata->propquery, &method))
+        return NULL;
+    return method;
+}
+
+static int put_loader_in_store(OPENSSL_CTX *libctx, void *store,
+                               void *method, const OSSL_PROVIDER *prov,
+                               int operation_id, const char *scheme,
+                               const char *propdef, void *unused)
+{
+    OSSL_NAMEMAP *namemap;
+    int id;
+
+    if ((namemap = ossl_namemap_stored(libctx)) == NULL
+        || (id = ossl_namemap_name2num(namemap, scheme)) == 0)
+        return 0;
+
+    if (store == NULL && (store = get_loader_store(libctx)) == NULL)
+        return 0;
+
+    return ossl_method_store_add(store, prov, id, propdef, method,
+                                 up_ref_loader, free_loader);
+}
+
+static void *loader_from_dispatch(int scheme_id, const OSSL_ALGORITHM *algodef,
+                                  OSSL_PROVIDER *prov)
+{
+    OSSL_STORE_LOADER *loader = NULL;
+    const OSSL_DISPATCH *fns = algodef->implementation;
+
+    if ((loader = new_loader(prov)) == NULL)
+        return NULL;
+    loader->scheme_id = scheme_id;
+    loader->propdef = algodef->property_definition;
+
+    for (; fns->function_id != 0; fns++) {
+        switch (fns->function_id) {
+        case OSSL_FUNC_STORE_OPEN:
+            if (loader->p_open == NULL)
+                loader->p_open = OSSL_FUNC_store_open(fns);
+            break;
+        case OSSL_FUNC_STORE_ATTACH:
+            if (loader->p_attach == NULL)
+                loader->p_attach = OSSL_FUNC_store_attach(fns);
+            break;
+        case OSSL_FUNC_STORE_SETTABLE_CTX_PARAMS:
+            if (loader->p_settable_ctx_params == NULL)
+                loader->p_settable_ctx_params =
+                    OSSL_FUNC_store_settable_ctx_params(fns);
+            break;
+        case OSSL_FUNC_STORE_SET_CTX_PARAMS:
+            if (loader->p_set_ctx_params == NULL)
+                loader->p_set_ctx_params = OSSL_FUNC_store_set_ctx_params(fns);
+            break;
+        case OSSL_FUNC_STORE_LOAD:
+            if (loader->p_load == NULL)
+                loader->p_load = OSSL_FUNC_store_load(fns);
+            break;
+        case OSSL_FUNC_STORE_EOF:
+            if (loader->p_eof == NULL)
+                loader->p_eof = OSSL_FUNC_store_eof(fns);
+            break;
+        case OSSL_FUNC_STORE_CLOSE:
+            if (loader->p_close == NULL)
+                loader->p_close = OSSL_FUNC_store_close(fns);
+            break;
+        case OSSL_FUNC_STORE_EXPORT_OBJECT:
+            if (loader->p_export_object == NULL)
+                loader->p_export_object = OSSL_FUNC_store_export_object(fns);
+            break;
+        }
+    }
+
+    if ((loader->p_open == NULL && loader->p_attach == NULL)
+        || loader->p_load == NULL
+        || loader->p_eof == NULL
+        || loader->p_close == NULL) {
+        /* Only set_ctx_params is optionaal */
+        OSSL_STORE_LOADER_free(loader);
+        ERR_raise(ERR_LIB_OSSL_STORE, OSSL_STORE_R_LOADER_INCOMPLETE);
+        return NULL;
+    }
+    return loader;
+}
+
+/*
+ * The core fetching functionality passes the scheme of the implementation.
+ * This function is responsible to getting an identity number for them,
+ * then call loader_from_dispatch() with that identity number.
+ */
+static void *construct_loader(const OSSL_ALGORITHM *algodef,
+                              OSSL_PROVIDER *prov, void *unused)
+{
+    /*
+     * This function is only called if get_loader_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 scheme 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 *scheme = algodef->algorithm_names;
+    int id = ossl_namemap_add_name(namemap, 0, scheme);
+    void *method = NULL;
+
+    if (id != 0)
+        method = loader_from_dispatch(id, algodef, prov);
+
+    return method;
+}
+
+/* Intermediary function to avoid ugly casts, used below */
+static void destruct_loader(void *method, void *data)
+{
+    OSSL_STORE_LOADER_free(method);
+}
+
+/* Fetching support.  Can fetch by numeric identity or by scheme */
+static OSSL_STORE_LOADER *inner_loader_fetch(OPENSSL_CTX *libctx,
+                                             int id, const char *scheme,
+                                             const char *properties)
+{
+    OSSL_METHOD_STORE *store = get_loader_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 scheme_id or a scheme, we have an
+     * internal programming error.
+     */
+    if (!ossl_assert(id != 0 || scheme != NULL))
+        return NULL;
+
+    if (id == 0)
+        id = ossl_namemap_name2num(namemap, scheme);
+
+    if (id == 0
+        || !ossl_method_store_cache_get(store, id, properties, &method)) {
+        OSSL_METHOD_CONSTRUCT_METHOD mcm = {
+            alloc_tmp_loader_store,
+            dealloc_tmp_loader_store,
+            get_loader_from_store,
+            put_loader_in_store,
+            construct_loader,
+            destruct_loader
+        };
+        struct loader_data_st mcmdata;
+
+        mcmdata.libctx = libctx;
+        mcmdata.mcm = &mcm;
+        mcmdata.scheme_id = id;
+        mcmdata.scheme = scheme;
+        mcmdata.propquery = properties;
+        if ((method = ossl_method_construct(libctx, OSSL_OP_STORE,
+                                            0 /* !force_cache */,
+                                            &mcm, &mcmdata)) != NULL) {
+            /*
+             * If construction did create a method for us, we know that there
+             * is a correct scheme_id, since those have already been calculated
+             * in get_loader_from_store() and put_loader_in_store() above.
+             */
+            if (id == 0)
+                id = ossl_namemap_name2num(namemap, scheme);
+            ossl_method_store_cache_set(store, id, properties, method,
+                                        up_ref_loader, free_loader);
+        }
+    }
+
+    return method;
+}
+
+OSSL_STORE_LOADER *OSSL_STORE_LOADER_fetch(const char *scheme,
+                                           OPENSSL_CTX *libctx,
+                                           const char *properties)
+{
+    return inner_loader_fetch(libctx, 0, scheme, properties);
+}
+
+OSSL_STORE_LOADER *ossl_store_loader_fetch_by_number(OPENSSL_CTX *libctx,
+                                                     int scheme_id,
+                                                     const char *properties)
+{
+    return inner_loader_fetch(libctx, scheme_id, NULL, properties);
+}
+
+/*
+ * Library of basic method functions
+ */
+
+const OSSL_PROVIDER *OSSL_STORE_LOADER_provider(const OSSL_STORE_LOADER *loader)
+{
+    if (!ossl_assert(loader != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    return loader->prov;
+}
+
+const char *OSSL_STORE_LOADER_properties(const OSSL_STORE_LOADER *loader)
+{
+    if (!ossl_assert(loader != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    return loader->propdef;
+}
+
+int OSSL_STORE_LOADER_number(const OSSL_STORE_LOADER *loader)
+{
+    if (!ossl_assert(loader != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    return loader->scheme_id;
+}
+
+int OSSL_STORE_LOADER_is_a(const OSSL_STORE_LOADER *loader, const char *name)
+{
+    if (loader->prov != NULL) {
+        OPENSSL_CTX *libctx = ossl_provider_library_context(loader->prov);
+        OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx);
+
+        return ossl_namemap_name2num(namemap, name) == loader->scheme_id;
+    }
+    return 0;
+}
+
+struct loader_do_all_data_st {
+    void (*user_fn)(void *method, void *arg);
+    void *user_arg;
+};
+
+static void loader_do_one(OSSL_PROVIDER *provider,
+                          const OSSL_ALGORITHM *algodef,
+                          int no_store, void *vdata)
+{
+    struct loader_do_all_data_st *data = vdata;
+    OPENSSL_CTX *libctx = ossl_provider_library_context(provider);
+    OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx);
+    const char *name = algodef->algorithm_names;
+    int id = ossl_namemap_add_name(namemap, 0, name);
+    void *method = NULL;
+
+    if (id != 0)
+        method =
+            loader_from_dispatch(id, algodef, provider);
+
+    if (method != NULL) {
+        data->user_fn(method, data->user_arg);
+        OSSL_STORE_LOADER_free(method);
+    }
+}
+
+void OSSL_STORE_LOADER_do_all_provided(OPENSSL_CTX *libctx,
+                                       void (*fn)(OSSL_STORE_LOADER *loader,
+                                                  void *arg),
+                                       void *arg)
+{
+    struct loader_do_all_data_st data;
+
+    data.user_fn = (void (*)(void *, void *))fn;
+    data.user_arg = arg;
+    ossl_algorithm_do_all(libctx, OSSL_OP_STORE, NULL,
+                          NULL, loader_do_one, NULL,
+                          &data);
+}
+
+void OSSL_STORE_LOADER_names_do_all(const OSSL_STORE_LOADER *loader,
+                                    void (*fn)(const char *name, void *data),
+                                    void *data)
+{
+    if (loader == NULL)
+        return;
+
+    if (loader->prov != NULL) {
+        OPENSSL_CTX *libctx = ossl_provider_library_context(loader->prov);
+        OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx);
+
+        ossl_namemap_doall_names(namemap, loader->scheme_id, fn, data);
+    }
+}
diff --git a/crypto/store/store_register.c b/crypto/store/store_register.c
index 4fbf459afa..7bc233ca9d 100644
--- a/crypto/store/store_register.c
+++ b/crypto/store/store_register.c
@@ -39,13 +39,12 @@ OSSL_STORE_LOADER *OSSL_STORE_LOADER_new(ENGINE *e, const char *scheme)
      * later on.
      */
     if (scheme == NULL) {
-        OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_LOADER_NEW,
-                      OSSL_STORE_R_INVALID_SCHEME);
+        ERR_raise(ERR_LIB_OSSL_STORE, OSSL_STORE_R_INVALID_SCHEME);
         return NULL;
     }
 
     if ((res = OPENSSL_zalloc(sizeof(*res))) == NULL) {
-        OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_LOADER_NEW, ERR_R_MALLOC_FAILURE);
+        ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_MALLOC_FAILURE);
         return NULL;
     }
 
@@ -71,6 +70,14 @@ int OSSL_STORE_LOADER_set_open(OSSL_STORE_LOADER *loader,
     return 1;
 }
 
+int OSSL_STORE_LOADER_set_open_with_libctx
+    (OSSL_STORE_LOADER *loader,
+     OSSL_STORE_open_with_libctx_fn open_with_libctx_function)
+{
+    loader->open_with_libctx = open_with_libctx_function;
+    return 1;
+}
+
 int OSSL_STORE_LOADER_set_attach(OSSL_STORE_LOADER *loader,
                                  OSSL_STORE_attach_fn attach_function)
 {
@@ -127,11 +134,6 @@ int OSSL_STORE_LOADER_set_close(OSSL_STORE_LOADER *loader,
     return 1;
 }
 
-void OSSL_STORE_LOADER_free(OSSL_STORE_LOADER *loader)
-{
-    OPENSSL_free(loader);
-}
-
 /*
  *  Functions for registering OSSL_STORE_LOADERs
  */
@@ -149,6 +151,14 @@ static int store_loader_cmp(const OSSL_STORE_LOADER *a,
 }
 
 static LHASH_OF(OSSL_STORE_LOADER) *loader_register = NULL;
+static int ossl_store_register_init(void)
+{
+    if (loader_register == NULL) {
+        loader_register = lh_OSSL_STORE_LOADER_new(store_loader_hash,
+                                                   store_loader_cmp);
+    }
+    return loader_register != NULL;
+}
 
 int ossl_store_register_loader_int(OSSL_STORE_LOADER *loader)
 {
@@ -168,33 +178,25 @@ int ossl_store_register_loader_int(OSSL_STORE_LOADER *loader)
                    || strchr("+-.", *scheme) != NULL))
             scheme++;
     if (*scheme != '\0') {
-        OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_REGISTER_LOADER_INT,
-                      OSSL_STORE_R_INVALID_SCHEME);
-        ERR_add_error_data(2, "scheme=", loader->scheme);
+        ERR_raise_data(ERR_LIB_OSSL_STORE, OSSL_STORE_R_INVALID_SCHEME,
+                       "scheme=%s", loader->scheme);
         return 0;
     }
 
     /* Check that functions we absolutely require are present */
     if (loader->open == NULL || loader->load == NULL || loader->eof == NULL
         || loader->error == NULL || loader->close == NULL) {
-        OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_REGISTER_LOADER_INT,
-                      OSSL_STORE_R_LOADER_INCOMPLETE);
+        ERR_raise(ERR_LIB_OSSL_STORE, OSSL_STORE_R_LOADER_INCOMPLETE);
         return 0;
     }
 
     if (!RUN_ONCE(&registry_init, do_registry_init)) {
-        OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_REGISTER_LOADER_INT,
-                      ERR_R_MALLOC_FAILURE);
+        ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_MALLOC_FAILURE);
         return 0;
     }
     CRYPTO_THREAD_write_lock(registry_lock);
 
-    if (loader_register == NULL) {
-        loader_register = lh_OSSL_STORE_LOADER_new(store_loader_hash,
-                                                   store_loader_cmp);
-    }
-
-    if (loader_register != NULL
+    if (ossl_store_register_init()
         && (lh_OSSL_STORE_LOADER_insert(loader_register, loader) != NULL
             || lh_OSSL_STORE_LOADER_error(loader_register) == 0))
         ok = 1;
@@ -226,19 +228,17 @@ const OSSL_STORE_LOADER *ossl_store_get0_loader_int(const char *scheme)
         return NULL;
 
     if (!RUN_ONCE(&registry_init, do_registry_init)) {
-        OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_GET0_LOADER_INT,
-                      ERR_R_MALLOC_FAILURE);
+        ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_MALLOC_FAILURE);
         return NULL;
     }
     CRYPTO_THREAD_write_lock(registry_lock);
 
-    loader = lh_OSSL_STORE_LOADER_retrieve(loader_register, &template);
-
-    if (loader == NULL) {
-        OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_GET0_LOADER_INT,
-                      OSSL_STORE_R_UNREGISTERED_SCHEME);
-        ERR_add_error_data(2, "scheme=", scheme);
-    }
+    if (!ossl_store_register_init())
+        ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_INTERNAL_ERROR);
+    else if ((loader = lh_OSSL_STORE_LOADER_retrieve(loader_register,
+                                                     &template)) == NULL)
+        ERR_raise_data(ERR_LIB_OSSL_STORE, OSSL_STORE_R_UNREGISTERED_SCHEME,
+                       "scheme=%s", scheme);
 
     CRYPTO_THREAD_unlock(registry_lock);
 
@@ -257,19 +257,17 @@ OSSL_STORE_LOADER *ossl_store_unregister_loader_int(const char *scheme)
     template.close = NULL;
 
     if (!RUN_ONCE(&registry_init, do_registry_init)) {
-        OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_UNREGISTER_LOADER_INT,
-                      ERR_R_MALLOC_FAILURE);
+        ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_MALLOC_FAILURE);
         return NULL;
     }
     CRYPTO_THREAD_write_lock(registry_lock);
 
-    loader = lh_OSSL_STORE_LOADER_delete(loader_register, &template);
-
-    if (loader == NULL) {
-        OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_UNREGISTER_LOADER_INT,
-                      OSSL_STORE_R_UNREGISTERED_SCHEME);
-        ERR_add_error_data(2, "scheme=", scheme);
-    }
+    if (!ossl_store_register_init())
+        ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_INTERNAL_ERROR);
+    else if ((loader = lh_OSSL_STORE_LOADER_delete(loader_register,
+                                                   &template)) == NULL)
+        ERR_raise_data(ERR_LIB_OSSL_STORE, OSSL_STORE_R_UNREGISTERED_SCHEME,
+                       "scheme=%s", scheme);
 
     CRYPTO_THREAD_unlock(registry_lock);
 
@@ -284,7 +282,6 @@ OSSL_STORE_LOADER *OSSL_STORE_unregister_loader(const char *scheme)
 
 void ossl_store_destroy_loaders_int(void)
 {
-    assert(lh_OSSL_STORE_LOADER_num_items(loader_register) == 0);
     lh_OSSL_STORE_LOADER_free(loader_register);
     loader_register = NULL;
     CRYPTO_THREAD_lock_free(registry_lock);
@@ -300,6 +297,7 @@ int OSSL_STORE_do_all_loaders(void (*do_function) (const OSSL_STORE_LOADER
                                                    *loader, void *do_arg),
                               void *do_arg)
 {
-    lh_OSSL_STORE_LOADER_doall_void(loader_register, do_function, do_arg);
+    if (ossl_store_register_init())
+        lh_OSSL_STORE_LOADER_doall_void(loader_register, do_function, do_arg);
     return 1;
 }
diff --git a/crypto/store/store_result.c b/crypto/store/store_result.c
new file mode 100644
index 0000000000..74aeaf543b
--- /dev/null
+++ b/crypto/store/store_result.c
@@ -0,0 +1,594 @@
+/*
+ * 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 "e_os.h"
+#include <string.h>
+
+#include <openssl/core.h>
+#include <openssl/core_names.h>
+#include <openssl/core_object.h>
+#include <openssl/err.h>
+#include <openssl/pkcs12.h>
+#include <openssl/provider.h>
+#include <openssl/decoder.h>
+#include <openssl/store.h>
+#include "internal/provider.h"
+#include "internal/passphrase.h"
+#include "crypto/evp.h"
+#include "crypto/x509.h"
+#include "store_local.h"
+
+#ifndef OSSL_OBJECT_PKCS12
+/*
+ * The object abstraction doesn't know PKCS#12, but we want to indicate
+ * it anyway, so we create our own.  Since the public macros use positive
+ * numbers, negative ones should be fine.  They must never slip out from
+ * this translation unit anyway.
+ */
+# define OSSL_OBJECT_PKCS12 -1
+#endif
+
+/*
+ * ossl_store_handle_load_result() is initially written to be a companion
+ * to our 'file:' scheme provider implementation, but has been made generic
+ * to serve others as well.
+ *
+ * This result handler takes any object abstraction (see provider-object(7))
+ * and does the best it can with it.  If the object is passed by value (not
+ * by reference), the contents are currently expected to be DER encoded.
+ * If an object type is specified, that will be respected; otherwise, this
+ * handler will guess the contents, by trying the following in order:
+ *
+ * 1.  Decode it into an EVP_PKEY, using OSSL_DECODER.
+ * 2.  Decode it into an X.509 certificate, using d2i_X509 / d2i_X509_AUX.
+ * 3.  Decode it into an X.509 CRL, using d2i_X509_CRL.
+ * 4.  Decode it into a PKCS#12 structure, using d2i_PKCS12 (*).
+ *
+ * For the 'file:' scheme implementation, this is division of labor.  Since
+ * the libcrypto <-> provider interface currently doesn't support certain
+ * structures as first class objects, they must be unpacked from DER here
+ * rather than in the provider.  The current exception is asymmetric keys,
+ * which can reside within the provider boundary, most of all thanks to
+ * OSSL_FUNC_keymgmt_load(), which allows loading the key material by
+ * reference.
+ */
+
+DEFINE_STACK_OF(X509)
+
+struct extracted_param_data_st {
+    int object_type;
+    const char *data_type;
+    const char *utf8_data;
+    const void *octet_data;
+    size_t octet_data_size;
+    const void *ref;
+    size_t ref_size;
+    const char *desc;
+};
+
+static int try_name(struct extracted_param_data_st *, OSSL_STORE_INFO **);
+static int try_key(struct extracted_param_data_st *, OSSL_STORE_INFO **,
+                   OSSL_STORE_CTX *, const OSSL_PROVIDER *,
+                   OPENSSL_CTX *, const char *);
+static int try_cert(struct extracted_param_data_st *, OSSL_STORE_INFO **,
+                    OPENSSL_CTX *, const char *);
+static int try_crl(struct extracted_param_data_st *, OSSL_STORE_INFO **,
+                   OPENSSL_CTX *, const char *);
+static int try_pkcs12(struct extracted_param_data_st *, OSSL_STORE_INFO **,
+                      OSSL_STORE_CTX *, OPENSSL_CTX *, const char *);
+
+int ossl_store_handle_load_result(const OSSL_PARAM params[], void *arg)
+{
+    struct ossl_load_result_data_st *cbdata = arg;
+    OSSL_STORE_INFO **v = &cbdata->v;
+    OSSL_STORE_CTX *ctx = cbdata->ctx;
+    const OSSL_PROVIDER *provider =
+        OSSL_STORE_LOADER_provider(ctx->fetched_loader);
+    OPENSSL_CTX *libctx = ossl_provider_library_context(provider);
+    const char *propq = ctx->properties;
+    const OSSL_PARAM *p;
+    struct extracted_param_data_st helper_data;
+
+    memset(&helper_data, 0, sizeof(helper_data));
+    helper_data.object_type = OSSL_OBJECT_UNKNOWN;
+
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_OBJECT_PARAM_TYPE)) != NULL
+        && !OSSL_PARAM_get_int(p, &helper_data.object_type))
+        return 0;
+    p = OSSL_PARAM_locate_const(params, OSSL_OBJECT_PARAM_DATA_TYPE);
+    if (p != NULL
+        && !OSSL_PARAM_get_utf8_string_ptr(p, &helper_data.data_type))
+        return 0;
+    p = OSSL_PARAM_locate_const(params, OSSL_OBJECT_PARAM_DATA);
+    if (p != NULL
+        && !OSSL_PARAM_get_octet_string_ptr(p, &helper_data.octet_data,
+                                            &helper_data.octet_data_size)
+        && !OSSL_PARAM_get_utf8_string_ptr(p, &helper_data.utf8_data))
+        return 0;
+    p = OSSL_PARAM_locate_const(params, OSSL_OBJECT_PARAM_REFERENCE);
+    if (p != NULL && !OSSL_PARAM_get_octet_string_ptr(p, &helper_data.ref,
+                                                      &helper_data.ref_size))
+        return 0;
+    p = OSSL_PARAM_locate_const(params, OSSL_OBJECT_PARAM_DESC);
+    if (p != NULL && !OSSL_PARAM_get_utf8_string_ptr(p, &helper_data.desc))
+        return 0;
+
+    /*
+     * The helper functions return 0 on actual errors, otherwise 1, even if
+     * they didn't fill out |*v|.
+     */
+    if (!try_name(&helper_data, v)
+        || !try_key(&helper_data, v, ctx, provider, libctx, propq)
+        || !try_cert(&helper_data, v, libctx, propq)
+        || !try_crl(&helper_data, v, libctx, propq)
+        || !try_pkcs12(&helper_data, v, ctx, libctx, propq))
+        return 0;
+
+    return (*v != NULL);
+}
+
+static int try_name(struct extracted_param_data_st *data, OSSL_STORE_INFO **v)
+{
+    if (data->object_type == OSSL_OBJECT_NAME) {
+        char *newname = NULL, *newdesc = NULL;
+
+        if (data->utf8_data == NULL)
+            return 0;
+        if ((newname = OPENSSL_strdup(data->utf8_data)) == NULL
+            || (data->desc != NULL
+                && (newdesc = OPENSSL_strdup(data->desc)) == NULL)
+            || (*v = OSSL_STORE_INFO_new_NAME(newname)) == NULL) {
+            OPENSSL_free(newname);
+            OPENSSL_free(newdesc);
+            return 0;
+        }
+        OSSL_STORE_INFO_set0_NAME_description(*v, newdesc);
+    }
+    return 1;
+}
+
+/*
+ * For the rest of the object types, the provider code may not know what
+ * type of data it gave us, so we may need to figure that out on our own.
+ * Therefore, we do check for OSSL_OBJECT_UNKNOWN everywhere below, and
+ * only return 0 on error if the object type is known.
+ */
+
+static EVP_PKEY *try_key_ref(struct extracted_param_data_st *data,
+                             OSSL_STORE_CTX *ctx,
+                             const OSSL_PROVIDER *provider,
+                             OPENSSL_CTX *libctx, const char *propq)
+{
+    EVP_PKEY *pk = NULL;
+    EVP_KEYMGMT *keymgmt = NULL;
+    void *keydata = NULL;
+
+    /* If we have an object reference, we must have a data type */
+    if (data->data_type == NULL)
+        return 0;
+
+    keymgmt = EVP_KEYMGMT_fetch(libctx, data->data_type, propq);
+    if (keymgmt != NULL) {
+        /*
+         * There are two possible cases
+         *
+         * 1.  The keymgmt is from the same provider as the loader,
+         *     so we can use evp_keymgmt_load()
+         * 2.  The keymgmt is from another provider, then we must
+         *     do the export/import dance.
+         */
+        if (EVP_KEYMGMT_provider(keymgmt) == provider) {
+            keydata = evp_keymgmt_load(keymgmt, data->ref, data->ref_size);
+        } else {
+            struct evp_keymgmt_util_try_import_data_st import_data;
+            OSSL_FUNC_store_export_object_fn *export_object =
+                ctx->fetched_loader->p_export_object;
+
+            import_data.keymgmt = keymgmt;
+            import_data.keydata = NULL;
+            import_data.selection = OSSL_KEYMGMT_SELECT_ALL;
+
+            if (export_object != NULL) {
+                /*
+                 * No need to check for errors here, the value of
+                 * |import_data.keydata| is as much an indicator.
+                 */
+                (void)export_object(ctx->loader_ctx,
+                                    data->ref, data->ref_size,
+                                    &evp_keymgmt_util_try_import,
+                                    &import_data);
+            }
+
+            keydata = import_data.keydata;
+        }
+    }
+    if (keydata != NULL)
+        pk = evp_keymgmt_util_make_pkey(keymgmt, keydata);
+    EVP_KEYMGMT_free(keymgmt);
+
+    return pk;
+}
+
+static EVP_PKEY *try_key_value(struct extracted_param_data_st *data,
+                               OSSL_STORE_CTX *ctx,
+                               OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg,
+                               OPENSSL_CTX *libctx, const char *propq)
+{
+    EVP_PKEY *pk = NULL;
+    OSSL_DECODER_CTX *decoderctx = NULL;
+    BIO *membio =
+        BIO_new_mem_buf(data->octet_data, (int)data->octet_data_size);
+
+    if (membio == NULL)
+        return 0;
+
+    decoderctx = OSSL_DECODER_CTX_new_by_EVP_PKEY(&pk, "DER", libctx, propq);
+    (void)OSSL_DECODER_CTX_set_passphrase_cb(decoderctx, cb, cbarg);
+
+    /* No error if this couldn't be decoded */
+    (void)OSSL_DECODER_from_bio(decoderctx, membio);
+
+    OSSL_DECODER_CTX_free(decoderctx);
+    BIO_free(membio);
+
+    return pk;
+}
+
+typedef OSSL_STORE_INFO *store_info_new_fn(EVP_PKEY *);
+
+static EVP_PKEY *try_key_value_legacy(struct extracted_param_data_st *data,
+                                      store_info_new_fn **store_info_new,
+                                      OSSL_STORE_CTX *ctx,
+                                      OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg,
+                                      OPENSSL_CTX *libctx, const char *propq)
+{
+    EVP_PKEY *pk = NULL;
+    const unsigned char *der = data->octet_data, *derp;
+    long der_len = (long)data->octet_data_size;
+
+    /* Try PUBKEY first, that's a real easy target */
+    derp = der;
+    pk = d2i_PUBKEY_ex(NULL, &derp, der_len, libctx, propq);
+    if (pk != NULL)
+        *store_info_new = OSSL_STORE_INFO_new_PUBKEY;
+
+    /* Try private keys next */
+    if (pk == NULL) {
+        unsigned char *new_der = NULL;
+        X509_SIG *p8 = NULL;
+        PKCS8_PRIV_KEY_INFO *p8info = NULL;
+
+        /* See if it's an encrypted PKCS#8 and decrypt it */
+        derp = der;
+        if ((p8 = d2i_X509_SIG(NULL, &derp, der_len)) != NULL) {
+            char pbuf[PEM_BUFSIZE];
+            size_t plen = 0;
+
+            if (!cb(pbuf, sizeof(pbuf), &plen, NULL, cbarg)) {
+                ERR_raise(ERR_LIB_OSSL_STORE,
+                          OSSL_STORE_R_BAD_PASSWORD_READ);
+            } else {
+                const X509_ALGOR *alg = NULL;
+                const ASN1_OCTET_STRING *oct = NULL;
+                int len = 0;
+
+                X509_SIG_get0(p8, &alg, &oct);
+
+                /*
+                 * No need to check the returned value, |new_der|
+                 * will be NULL on error anyway.
+                 */
+                PKCS12_pbe_crypt(alg, pbuf, plen,
+                                 oct->data, oct->length,
+                                 &new_der, &len, 0);
+                der_len = len;
+                der = new_der;
+            }
+            X509_SIG_free(p8);
+        }
+
+        /*
+         * If the encrypted PKCS#8 couldn't be decrypted,
+         * |der| is NULL
+         */
+        if (der != NULL) {
+            /* Try to unpack an unencrypted PKCS#8, that's easy */
+            derp = der;
+            p8info = d2i_PKCS8_PRIV_KEY_INFO(NULL, &derp, der_len);
+            if (p8info != NULL) {
+                pk = evp_pkcs82pkey_int(p8info, libctx, propq);
+                PKCS8_PRIV_KEY_INFO_free(p8info);
+            }
+
+            /*
+             * It wasn't PKCS#8, so we must try the hard way.
+             * However, we can cheat a little bit, because we know
+             * what's not yet fully supported in out decoders.
+             * TODO(3.0) Eliminate these when we have decoder support.
+             */
+            if (pk == NULL) {
+                derp = der;
+                pk = d2i_PrivateKey_ex(EVP_PKEY_SM2, NULL,
+                                       &derp, der_len,
+                                       libctx, NULL);
+            }
+        }
+
+        if (pk != NULL)
+            *store_info_new = OSSL_STORE_INFO_new_PKEY;
+
+        OPENSSL_free(new_der);
+        der = data->octet_data;
+        der_len = (long)data->octet_data_size;
+    }
+
+    /*
+     * Last, we try parameters.  We cheat the same way we do for
+     * private keys above.
+     * TODO(3.0) Eliminate these when we have decoder support.
+     */
+    if (pk == NULL) {
+        derp = der;
+        pk = d2i_KeyParams(EVP_PKEY_SM2, NULL, &derp, der_len);
+        if (pk != NULL)
+            *store_info_new = OSSL_STORE_INFO_new_PARAMS;
+    }
+
+    return pk;
+}
+
+static int try_key(struct extracted_param_data_st *data, OSSL_STORE_INFO **v,
+                   OSSL_STORE_CTX *ctx, const OSSL_PROVIDER *provider,
+                   OPENSSL_CTX *libctx, const char *propq)
+{
+    store_info_new_fn *store_info_new = NULL;
+
+    if (data->object_type == OSSL_OBJECT_UNKNOWN
+        || data->object_type == OSSL_OBJECT_PKEY) {
+        EVP_PKEY *pk = NULL;
+
+        /* Prefer key by reference than key by value */
+        if (data->object_type == OSSL_OBJECT_PKEY && data->ref != NULL) {
+            pk = try_key_ref(data, ctx, provider, libctx, propq);
+
+            /*
+             * If for some reason we couldn't get a key, it's an error.
+             * It indicates that while decoders could make a key reference,
+             * the keymgmt somehow couldn't handle it, or doesn't have a
+             * OSSL_FUNC_keymgmt_load function.
+             */
+            if (pk == NULL)
+                return 0;
+        } else if (data->octet_data != NULL) {
+            OSSL_PASSPHRASE_CALLBACK *cb = ossl_pw_passphrase_callback_dec;
+            void *cbarg = &ctx->pwdata;
+
+            pk = try_key_value(data, ctx, cb, cbarg, libctx, propq);
+
+            /*
+             * Desperate last maneuver, in case the decoders don't support
+             * the data we have, then we try on our own to at least get a
+             * legacy key.
+             * This is the same as der2key_decode() does, but in a limited
+             * way and within the walls of libcrypto.
+             *
+             * TODO Remove this when #legacy keys are gone
+             */
+            if (pk == NULL)
+                pk = try_key_value_legacy(data, &store_info_new, ctx,
+                                          cb, cbarg, libctx, propq);
+        }
+
+        if (pk != NULL) {
+            data->object_type = OSSL_OBJECT_PKEY;
+
+            if (store_info_new == NULL) {
+                /*
+                 * We determined the object type for OSSL_STORE_INFO, which
+                 * makes an explicit difference between an EVP_PKEY with just
+                 * (domain) parameters and an EVP_PKEY with actual key
+                 * material.
+                 * The logic is that an EVP_PKEY with actual key material
+                 * always has the public half.
+                 */
+                if (evp_keymgmt_util_has(pk, OSSL_KEYMGMT_SELECT_PRIVATE_KEY))
+                    store_info_new = OSSL_STORE_INFO_new_PKEY;
+                else if (evp_keymgmt_util_has(pk,
+                                              OSSL_KEYMGMT_SELECT_PUBLIC_KEY))
+                    store_info_new = OSSL_STORE_INFO_new_PUBKEY;
+                else
+                    store_info_new = OSSL_STORE_INFO_new_PARAMS;
+            }
+            *v = store_info_new(pk);
+        }
+
+        if (*v == NULL)
+            EVP_PKEY_free(pk);
+    }
+
+    return 1;
+}
+
+static int try_cert(struct extracted_param_data_st *data, OSSL_STORE_INFO **v,
+                    OPENSSL_CTX *libctx, const char *propq)
+{
+    if (data->object_type == OSSL_OBJECT_UNKNOWN
+        || data->object_type == OSSL_OBJECT_CERT) {
+        X509 *cert;
+
+        /*
+         * In most cases, we can try to interpret the serialized
+         * data as a trusted cert (X509 + X509_AUX) and fall back
+         * to reading it as a normal cert (just X509), but if
+         * |data_type| (the PEM name) specifically declares it as a
+         * trusted cert, then no fallback should be engaged.
+         * |ignore_trusted| tells if the fallback can be used (1)
+         * or not (0).
+         */
+        int ignore_trusted = 1;
+
+        /* If we have a data type, it should be a PEM name */
+        if (data->data_type != NULL
+            && (strcasecmp(data->data_type, PEM_STRING_X509_TRUSTED) == 0))
+            ignore_trusted = 0;
+
+        cert = d2i_X509_AUX(NULL, (const unsigned char **)&data->octet_data,
+                            data->octet_data_size);
+        if (cert == NULL && ignore_trusted)
+            cert = d2i_X509(NULL, (const unsigned char **)&data->octet_data,
+                            data->octet_data_size);
+
+        if (cert != NULL)
+            /* We determined the object type */
+            data->object_type = OSSL_OBJECT_CERT;
+
+        if (cert != NULL && !x509_set0_libctx(cert, libctx, propq)) {
+            X509_free(cert);
+            cert = NULL;
+        }
+
+        if (cert != NULL)
+            *v = OSSL_STORE_INFO_new_CERT(cert);
+        if (*v == NULL)
+            X509_free(cert);
+    }
+
+    return 1;
+}
+
+static int try_crl(struct extracted_param_data_st *data, OSSL_STORE_INFO **v,
+                   OPENSSL_CTX *libctx, const char *propq)
+{
+    if (data->object_type == OSSL_OBJECT_UNKNOWN
+        || data->object_type == OSSL_OBJECT_CRL) {
+        X509_CRL *crl;
+
+        crl = d2i_X509_CRL(NULL, (const unsigned char **)&data->octet_data,
+                           data->octet_data_size);
+        if (crl != NULL)
+            /* We determined the object type */
+            data->object_type = OSSL_OBJECT_CRL;
+
+        if (crl != NULL && !x509_crl_set0_libctx(crl, libctx, propq)) {
+            X509_CRL_free(crl);
+            crl = NULL;
+        }
+
+        if (crl != NULL)
+            *v = OSSL_STORE_INFO_new_CRL(crl);
+        if (*v == NULL)
+            X509_CRL_free(crl);
+    }
+
+    return 1;
+}
+
+static int try_pkcs12(struct extracted_param_data_st *data, OSSL_STORE_INFO **v,
+                      OSSL_STORE_CTX *ctx,
+                      OPENSSL_CTX *libctx, const char *propq)
+{
+    /* There is no specific object type for PKCS12 */
+    if (data->object_type == OSSL_OBJECT_UNKNOWN) {
+        /* Initial parsing */
+        PKCS12 *p12;
+
+        if ((p12 = d2i_PKCS12(NULL, (const unsigned char **)&data->octet_data,
+                              data->octet_data_size)) != NULL) {
+            char *pass = NULL;
+            char tpass[PEM_BUFSIZE];
+            size_t tpass_len;
+            EVP_PKEY *pkey = NULL;
+            X509 *cert = NULL;
+            STACK_OF(X509) *chain = NULL;
+
+            data->object_type = OSSL_OBJECT_PKCS12;
+
+            if (PKCS12_verify_mac(p12, "", 0)
+                || PKCS12_verify_mac(p12, NULL, 0)) {
+                pass = "";
+            } else {
+                static char prompt_info[] = "PKCS12 import pass phrase";
+                OSSL_PARAM pw_params[] = {
+                    OSSL_PARAM_utf8_string(OSSL_PASSPHRASE_PARAM_INFO,
+                                           prompt_info,
+                                           sizeof(prompt_info) - 1),
+                    OSSL_PARAM_END
+                };
+
+                if (!ossl_pw_get_passphrase(tpass, sizeof(tpass), &tpass_len,
+                                            pw_params, 0, &ctx->pwdata)) {
+                    ERR_raise(ERR_LIB_OSSL_STORE,
+                              OSSL_STORE_R_PASSPHRASE_CALLBACK_ERROR);
+                    goto p12_end;
+                }
+                pass = tpass;
+                if (!PKCS12_verify_mac(p12, pass, strlen(pass))) {
+                    ERR_raise(ERR_LIB_OSSL_STORE,
+                              OSSL_STORE_R_ERROR_VERIFYING_PKCS12_MAC);
+                    goto p12_end;
+                }
+            }
+
+            if (PKCS12_parse(p12, pass, &pkey, &cert, &chain)) {
+                STACK_OF(OSSL_STORE_INFO) *infos = NULL;
+                OSSL_STORE_INFO *osi_pkey = NULL;
+                OSSL_STORE_INFO *osi_cert = NULL;
+                OSSL_STORE_INFO *osi_ca = NULL;
+                int ok = 1;
+
+                if ((infos = sk_OSSL_STORE_INFO_new_null()) != NULL) {
+                    if (pkey != NULL) {
+                        if ((osi_pkey = OSSL_STORE_INFO_new_PKEY(pkey)) != NULL
+                            /* clearing pkey here avoids case distinctions */
+                            && (pkey = NULL) == NULL
+                            && sk_OSSL_STORE_INFO_push(infos, osi_pkey) != 0)
+                            osi_pkey = NULL;
+                        else
+                            ok = 0;
+                    }
+                    if (ok && cert != NULL) {
+                        if ((osi_cert = OSSL_STORE_INFO_new_CERT(cert)) != NULL
+                            /* clearing cert here avoids case distinctions */
+                            && (cert = NULL) == NULL
+                            && sk_OSSL_STORE_INFO_push(infos, osi_cert) != 0)
+                            osi_cert = NULL;
+                        else
+                            ok = 0;
+                    }
+                    while (ok && sk_X509_num(chain) > 0) {
+                        X509 *ca = sk_X509_value(chain, 0);
+
+                        if ((osi_ca = OSSL_STORE_INFO_new_CERT(ca)) != NULL
+                            && sk_X509_shift(chain) != NULL
+                            && sk_OSSL_STORE_INFO_push(infos, osi_ca) != 0)
+                            osi_ca = NULL;
+                        else
+                            ok = 0;
+                    }
+                }
+                EVP_PKEY_free(pkey);
+                X509_free(cert);
+                sk_X509_pop_free(chain, X509_free);
+                OSSL_STORE_INFO_free(osi_pkey);
+                OSSL_STORE_INFO_free(osi_cert);
+                OSSL_STORE_INFO_free(osi_ca);
+                if (!ok) {
+                    sk_OSSL_STORE_INFO_pop_free(infos, OSSL_STORE_INFO_free);
+                    infos = NULL;
+                }
+                ctx->cached_info = infos;
+            }
+        }
+     p12_end:
+        PKCS12_free(p12);
+        *v = sk_OSSL_STORE_INFO_shift(ctx->cached_info);
+    }
+
+    return 1;
+}
diff --git a/crypto/x509/x_crl.c b/crypto/x509/x_crl.c
index 1690dd8963..44f374aed3 100644
--- a/crypto/x509/x_crl.c
+++ b/crypto/x509/x_crl.c
@@ -494,3 +494,12 @@ void *X509_CRL_get_meth_data(X509_CRL *crl)
 {
     return crl->meth_data;
 }
+
+int x509_crl_set0_libctx(X509_CRL *x, OPENSSL_CTX *libctx, const char *propq)
+{
+    if (x != NULL) {
+        x->libctx = libctx;
+        x->propq = propq;
+    }
+    return 1;
+}
diff --git a/doc/man3/OSSL_DECODER_CTX_new_by_EVP_PKEY.pod b/doc/man3/OSSL_DECODER_CTX_new_by_EVP_PKEY.pod
index 4486e6b001..620688e322 100644
--- a/doc/man3/OSSL_DECODER_CTX_new_by_EVP_PKEY.pod
+++ b/doc/man3/OSSL_DECODER_CTX_new_by_EVP_PKEY.pod
@@ -5,7 +5,8 @@
 OSSL_DECODER_CTX_new_by_EVP_PKEY,
 OSSL_DECODER_CTX_set_passphrase,
 OSSL_DECODER_CTX_set_pem_password_cb,
-OSSL_DECODER_CTX_set_passphrase_ui
+OSSL_DECODER_CTX_set_passphrase_ui,
+OSSL_DECODER_CTX_set_passphrase_cb
 - Decoder routines to decode EVP_PKEYs
 
 =head1 SYNOPSIS
@@ -25,6 +26,9 @@ OSSL_DECODER_CTX_set_passphrase_ui
  int OSSL_DECODER_CTX_set_passphrase_ui(OSSL_DECODER_CTX *ctx,
                                         const UI_METHOD *ui_method,
                                         void *ui_data);
+ int OSSL_DECODER_CTX_set_passphrase_cb(OSSL_DECODER_CTX *ctx,
+                                        OSSL_PASSPHRASE_CALLBACK *cb,
+                                        void *cbarg);
 
 =head1 DESCRIPTION
 
@@ -55,29 +59,26 @@ pass phrase to use when decrypting the encoded private key.
 Alternatively, a pass phrase callback may be specified with the
 following functions.
 
-OSSL_DECODER_CTX_set_pem_password_cb() and
-OSSL_DECODER_CTX_set_passphrase_ui() set up a callback method that
+OSSL_DECODER_CTX_set_pem_password_cb(),
+OSSL_DECODER_CTX_set_passphrase_ui() and
+OSSL_DECODER_CTX_set_passphrase_cb() set up a callback method that
 the implementation can use to prompt for a pass phrase, giving the caller
 the choice of prefered pass phrase callback form.  These are called
 indirectly, through an internal B<OSSL_PASSPHRASE_CALLBACK> function.
 
-The internal B<OSSL_PASSPHRASE_CALLBACK> function caches the pass phrase, to
-be re-used in all decodings that are performed in the same
-decoding run
+The internal B<OSSL_PASSPHRASE_CALLBACK> function caches the pass phrase,
+to be re-used in all decodings that are performed in the same decoding run
 (for example, within one L<OSSL_DECODER_from_bio(3)> call).
 
-=for comment the name OSSL_DECODER_CTX_set_pem_password_cb() leaves
-open the future possibility of having a function where the caller can set a
-B<OSSL_PASSPHRASE_CALLBACK> method as another option.
-
 =head1 RETURN VALUES
 
 OSSL_DECODER_CTX_new_by_EVP_PKEY() returns a pointer to a
 B<OSSL_DECODER_CTX>, or NULL if it couldn't be created.
 
 OSSL_DECODER_CTX_set_passphrase(),
-OSSL_DECODER_CTX_set_pem_password_cb() and
-OSSL_DECODER_CTX_set_passphrase_ui()
+OSSL_DECODER_CTX_set_pem_password_cb(),
+OSSL_DECODER_CTX_set_passphrase_ui() and
+OSSL_DECODER_CTX_set_passphrase_cb()
 all return 1 on success, or 0 on failure.
 
 =head1 NOTES
diff --git a/doc/man3/OSSL_DECODER_from_bio.pod b/doc/man3/OSSL_DECODER_from_bio.pod
index 1beb74d5ff..560231fe23 100644
--- a/doc/man3/OSSL_DECODER_from_bio.pod
+++ b/doc/man3/OSSL_DECODER_from_bio.pod
@@ -41,7 +41,7 @@ OSSL_DECODER_INSTANCE_decoder_ctx
  void *OSSL_DECODER_INSTANCE_decoder_ctx(OSSL_DECODER_INSTANCE *decoder_inst);
 
  typedef int (OSSL_DECODER_CONSTRUCT)(OSSL_DECODER_INSTANCE *decoder_inst,
-                                      const OSSL_PARAM *params,
+                                      const OSSL_PARAM *object,
                                       void *construct_data);
  typedef void (OSSL_DECODER_CLEANUP)(void *construct_data);
 
@@ -168,9 +168,11 @@ A B<OSSL_DECODER_CONSTRUCT> gets the following arguments:
 The B<OSSL_DECODER_INSTANCE> for the decoder from which
 the constructor gets its data.
 
-=item I<params>
+=item I<object>
 
-The data produced by the decoder, further described below.
+A provider-native object abstraction produced by the decoder.  Further
+information on the provider-native object abstraction can be found in
+L<provider-object(7)>.
 
 =item I<construct_data>
 
@@ -181,45 +183,6 @@ The pointer that was set with OSSL_DECODE_CTX_set_construct_data().
 The constructor is expected to return 1 when the data it receives can
 be constructed, otherwise 0.
 
-The globally known parameters that the constructor can get in I<params>
-are:
-
-=over 4
-
-=item "data-type" (B<OSSL_DECODER_PARAM_DATA_TYPE>) <UTF8 string>
-
-This is a detected content type that some decoders may provide.
-For example, PEM input sometimes has a type specified in its header,
-and some decoders may add that information as this parameter.
-This is an optional parameter, but may be useful for extra checks in
-the constructor.
-
-=item "data" (B<OSSL_DECODER_PARAM_DATA>) <octet string>
-
-The decoded data itself, as an octet string.  This is produced by
-decoders when it's possible to pass an object in this form.  Most
-often, this is simply meant to be passed to the next decoder in a
-chain, but could be considered final data as well, at the discretion
-of the constructor.
-
-=item "reference" (B<OSSL_DECODER_PARAM_DATA>) <octet string>
-
-The decoded 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<decoder>.
-
-=back
-
-At least one of "data" or "reference" must be present, and it's
-possible that both can be.  A constructor should choose to use the
-"reference" parameter if possible, otherwise it should use the "data"
-parameter.
-
-If it's not possible to use the "reference" parameter, but that's
-still what a constructor wants to do, it is possible to use
-OSSL_DECODER_export() as a fallback.
-
 =head1 RETURN VALUES
 
 OSSL_DECODER_from_bio() and OSSL_DECODER_from_fp() return 1 on
diff --git a/doc/man3/OSSL_PARAM_int.pod b/doc/man3/OSSL_PARAM_int.pod
index 6712a07327..691bc3b340 100644
--- a/doc/man3/OSSL_PARAM_int.pod
+++ b/doc/man3/OSSL_PARAM_int.pod
@@ -24,6 +24,7 @@ OSSL_PARAM_get_time_t, OSSL_PARAM_get_uint, OSSL_PARAM_get_uint32,
 OSSL_PARAM_get_uint64, OSSL_PARAM_get_ulong, OSSL_PARAM_get_BN,
 OSSL_PARAM_get_utf8_string, OSSL_PARAM_get_octet_string,
 OSSL_PARAM_get_utf8_ptr, OSSL_PARAM_get_octet_ptr,
+OSSL_PARAM_get_utf8_string_ptr, OSSL_PARAM_get_octet_string_ptr,
 OSSL_PARAM_set_double, OSSL_PARAM_set_int, OSSL_PARAM_set_int32,
 OSSL_PARAM_set_int64, OSSL_PARAM_set_long, OSSL_PARAM_set_size_t,
 OSSL_PARAM_set_time_t, OSSL_PARAM_set_uint, OSSL_PARAM_set_uint32,
@@ -96,6 +97,10 @@ OSSL_PARAM_UNMODIFIED, OSSL_PARAM_modified, OSSL_PARAM_set_all_unmodified
  int OSSL_PARAM_set_octet_ptr(OSSL_PARAM *p, const void *val,
                               size_t used_len);
 
+ int OSSL_PARAM_get_utf8_string_ptr(const OSSL_PARAM *p, const char **val);
+ int OSSL_PARAM_get_octet_string_ptr(const OSSL_PARAM *p, const void **val,
+                                     size_t *used_len);
+
  int OSSL_PARAM_modified(const OSSL_PARAM *param);
  void OSSL_PARAM_set_all_unmodified(OSSL_PARAM *params);
 
@@ -264,6 +269,17 @@ OSSL_PARAM_set_octet_ptr() sets the OCTET string pointer in the parameter
 referenced by B<p> to the values B<val>.
 The length of the OCTET string is provided by B<used_len>.
 
+OSSL_PARAM_get_utf8_string_ptr() retrieves the pointer to a UTF8 string from
+the parameter pointed to by B<p>, and stores that pointer in B<*val>.
+This is different from OSSL_PARAM_get_utf8_string(), which copies the
+string.
+
+OSSL_PARAM_get_octet_string_ptr() retrieves the pointer to a octet string
+from the parameter pointed to by B<p>, and stores that pointer in B<*val>,
+along with the string's length in B<*used_len>.
+This is different from OSSL_PARAM_get_octet_string(), which copies the
+string.
+
 The OSSL_PARAM_UNMODIFIED macro is used to detect if a parameter was set.  On
 creation, via either the macros or construct calls, the I<return_size> field
 is set to this.  If the parameter is set using the calls defined herein, the
diff --git a/doc/man3/OSSL_STORE_LOADER.pod b/doc/man3/OSSL_STORE_LOADER.pod
index 64b15c18c8..4e25f79f9e 100644
--- a/doc/man3/OSSL_STORE_LOADER.pod
+++ b/doc/man3/OSSL_STORE_LOADER.pod
@@ -2,16 +2,26 @@
 
 =head1 NAME
 
-OSSL_STORE_LOADER, OSSL_STORE_LOADER_CTX, OSSL_STORE_LOADER_new,
+OSSL_STORE_LOADER,
+OSSL_STORE_LOADER_fetch,
+OSSL_STORE_LOADER_up_ref,
+OSSL_STORE_LOADER_free,
+OSSL_STORE_LOADER_provider,
+OSSL_STORE_LOADER_properties,
+OSSL_STORE_LOADER_is_a,
+OSSL_STORE_LOADER_number,
+OSSL_STORE_LOADER_do_all_provided,
+OSSL_STORE_LOADER_names_do_all,
+OSSL_STORE_LOADER_CTX, OSSL_STORE_LOADER_new,
 OSSL_STORE_LOADER_get0_engine, OSSL_STORE_LOADER_get0_scheme,
-OSSL_STORE_LOADER_set_open, OSSL_STORE_LOADER_set_attach,
-OSSL_STORE_LOADER_set_ctrl, OSSL_STORE_LOADER_set_expect,
-OSSL_STORE_LOADER_set_find, OSSL_STORE_LOADER_set_load,
-OSSL_STORE_LOADER_set_eof, OSSL_STORE_LOADER_set_error,
-OSSL_STORE_LOADER_set_close,
-OSSL_STORE_LOADER_free, OSSL_STORE_register_loader,
-OSSL_STORE_unregister_loader,
-OSSL_STORE_open_fn, OSSL_STORE_attach_fn, OSSL_STORE_ctrl_fn,
+OSSL_STORE_LOADER_set_open, OSSL_STORE_LOADER_set_open_with_libctx,
+OSSL_STORE_LOADER_set_attach, OSSL_STORE_LOADER_set_ctrl,
+OSSL_STORE_LOADER_set_expect, OSSL_STORE_LOADER_set_find,
+OSSL_STORE_LOADER_set_load, OSSL_STORE_LOADER_set_eof,
+OSSL_STORE_LOADER_set_error, OSSL_STORE_LOADER_set_close,
+OSSL_STORE_register_loader, OSSL_STORE_unregister_loader,
+OSSL_STORE_open_fn, OSSL_STORE_open_with_libctx_fn,
+OSSL_STORE_attach_fn, OSSL_STORE_ctrl_fn,
 OSSL_STORE_expect_fn, OSSL_STORE_find_fn,
 OSSL_STORE_load_fn, OSSL_STORE_eof_fn, OSSL_STORE_error_fn,
 OSSL_STORE_close_fn - Types and functions to manipulate, register and
@@ -23,6 +33,28 @@ unregister STORE loaders for different URI schemes
 
  typedef struct ossl_store_loader_st OSSL_STORE_LOADER;
 
+ OSSL_STORE_LOADER *OSSL_STORE_LOADER_fetch(const char *scheme,
+                                            OPENSSL_CTX *libctx,
+                                            const char *properties);
+ int OSSL_STORE_LOADER_up_ref(OSSL_STORE_LOADER *loader);
+ void OSSL_STORE_LOADER_free(OSSL_STORE_LOADER *loader);
+ const OSSL_PROVIDER *OSSL_STORE_LOADER_provider(const OSSL_STORE_LOADER *
+                                                 loader);
+ const char *OSSL_STORE_LOADER_properties(const OSSL_STORE_LOADER *loader);
+ int OSSL_STORE_LOADER_number(const OSSL_STORE_LOADER *loader);
+ int OSSL_STORE_LOADER_is_a(const OSSL_STORE_LOADER *loader,
+                            const char *scheme);
+ void OSSL_STORE_LOADER_do_all_provided(OPENSSL_CTX *libctx,
+                                        void (*fn)(OSSL_STORE_LOADER *loader,
+                                                   void *arg),
+                                        void *arg);
+ void OSSL_STORE_LOADER_names_do_all(const OSSL_STORE_LOADER *loader,
+                                     void (*fn)(const char *name, void *data),
+                                     void *data);
+
+Legacy functions, still present to support B<OSSL_STORE_LOADER>s provided
+by B<ENGINE>:
+
  OSSL_STORE_LOADER *OSSL_STORE_LOADER_new(ENGINE *e, const char *scheme);
  const ENGINE *OSSL_STORE_LOADER_get0_engine(const OSSL_STORE_LOADER
                                              *store_loader);
@@ -36,6 +68,11 @@ unregister STORE loaders for different URI schemes
      const char *uri, const UI_METHOD *ui_method, void *ui_data);
  int OSSL_STORE_LOADER_set_open(OSSL_STORE_LOADER *store_loader,
                                 OSSL_STORE_open_fn store_open_function);
+ typedef OSSL_STORE_LOADER_CTX *(*OSSL_STORE_open_with_libctx_fn)(
+     const char *uri, const UI_METHOD *ui_method, void *ui_data);
+ int OSSL_STORE_LOADER_set_open_with_libctx
+     (OSSL_STORE_LOADER *store_loader,
+      OSSL_STORE_open_with_libctx_fn store_open_with_libctx_function);
  typedef OSSL_STORE_LOADER_CTX *(*OSSL_STORE_attach_fn)
      (const OSSL_STORE_LOADER *loader, BIO *bio,
       OPENSSL_CTX *libctx, const char *propq,
@@ -74,52 +111,90 @@ unregister STORE loaders for different URI schemes
 
 =head1 DESCRIPTION
 
-These functions help applications and engines to create loaders for
-schemes they support.
+B<OSSL_STORE_LOADER> is a method for OSSL_STORE loaders, which implement
+OSSL_STORE_open(), OSSL_STORE_open_with_libctx(), OSSL_STORE_load(),
+OSSL_STORE_eof(), OSSL_STORE_error() and OSSL_STORE_close() for specific
+storage schemes.
+
+OSSL_STORE_LOADER_fetch() looks for an implementation for a storage
+I<scheme> within the providers that has been loaded into the B<OPENSSL_CTX>
+given by I<ctx>, and with the properties given by I<properties>.
+
+OSSL_STORE_LOADER_up_ref() increments the reference count for the given
+I<loader>.
+
+OSSL_STORE_LOADER_free() decrements the reference count for the given
+I<loader>, and when the count reaches zero, frees it.
+
+OSSL_STORE_LOADER_provider() returns the provider of the given
+I<loader>.
 
-=head2 Types
+OSSL_STORE_LOADER_properties() returns the property definition associated
+with the given I<loader>.
 
-B<OSSL_STORE_LOADER> is the type to hold a loader.
-It contains a scheme and the functions needed to implement
-OSSL_STORE_open(), OSSL_STORE_load(), OSSL_STORE_eof(), OSSL_STORE_error() and
-OSSL_STORE_close() for this scheme.
+OSSL_STORE_LOADER_is_a() checks if I<loader> is an implementation
+of an algorithm that's identifiable with I<scheme>.
+
+OSSL_STORE_LOADER_number() returns the internal dynamic number assigned
+to the given I<loader>.
+
+OSSL_STORE_LOADER_do_all_provided() traverses all store 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_STORE_LOADER_names_do_all() traverses all names for the given
+I<loader>, and calls I<fn> with each name and I<data>.
+
+=head2 Legacy Types and Functions
+
+These functions help applications and engines to create loaders for
+schemes they support.  These are all discouraged in favour of provider
+implementations, see L<provider-storemgmt(7)>.
 
 B<OSSL_STORE_LOADER_CTX> is a type template, to be defined by each loader
-using B<struct ossl_store_loader_ctx_st { ... }>.
+using C<struct ossl_store_loader_ctx_st { ... }>.
 
-B<OSSL_STORE_open_fn>, B<OSSL_STORE_ctrl_fn>, B<OSSL_STORE_expect_fn>,
-B<OSSL_STORE_find_fn>, B<OSSL_STORE_load_fn>, B<OSSL_STORE_eof_fn>,
-and B<OSSL_STORE_close_fn>
+B<OSSL_STORE_open_fn>, B<OSSL_STORE_open_with_libctx_fn>,
+B<OSSL_STORE_ctrl_fn>, B<OSSL_STORE_expect_fn>, B<OSSL_STORE_find_fn>,
+B<OSSL_STORE_load_fn>, B<OSSL_STORE_eof_fn>, and B<OSSL_STORE_close_fn>
 are the function pointer types used within a STORE loader.
 The functions pointed at define the functionality of the given loader.
 
 =over 4
 
-=item B<OSSL_STORE_open_fn>
+=item B<OSSL_STORE_open_fn> and B<OSSL_STORE_open_with_libctx_fn>
 
-This function takes a URI and is expected to interpret it in the best
-manner possible according to the scheme the loader implements, it also
-takes a B<UI_METHOD> and associated data, to be used any time
-something needs to be prompted for.
+B<OSSL_STORE_open_with_libctx_fn> takes a URI and is expected to
+interpret it in the best manner possible according to the scheme the
+loader implements.  It also takes a B<UI_METHOD> and associated data,
+to be used any time something needs to be prompted for, as well as a
+library context I<libctx> with an associated property query I<propq>,
+to be used when fetching necessary algorithms to perform the loads.
 Furthermore, this function is expected to initialize what needs to be
-initialized, to create a private data store (B<OSSL_STORE_LOADER_CTX>, see
-above), and to return it.
+initialized, to create a private data store (B<OSSL_STORE_LOADER_CTX>,
+see above), and to return it.
 If something goes wrong, this function is expected to return NULL.
 
-=item B<OSSL_STORE_open_fn>
+B<OSSL_STORE_open_fn> does the same thing as
+B<OSSL_STORE_open_with_libctx_fn> but uses NULL for the library
+context I<libctx> and property query I<propq>.
 
-This function takes a B<BIO>, otherwise works like B<OSSL_STORE_open_fn>.
+=item B<OSSL_STORE_attach_fn>
+
+This function takes a B<BIO>, otherwise works like
+B<OSSL_STORE_open_with_libctx_fn>.
 
 =item B<OSSL_STORE_ctrl_fn>
 
 This function takes a B<OSSL_STORE_LOADER_CTX> pointer, a command number
-B<cmd> and a B<va_list> B<args> and is used to manipulate loader
+I<cmd> and a B<va_list> I<args> and is used to manipulate loader
 specific parameters.
 
 =begin comment
 
 Globally known command numbers are documented in L<OSSL_STORE_ctrl(3)>,
-along with what B<args> are expected with each of them.
+along with what I<args> are expected with each of them.
 
 =end comment
 
@@ -132,9 +207,9 @@ This function is expected to return 1 on success, 0 on error.
 =item B<OSSL_STORE_expect_fn>
 
 This function takes a B<OSSL_STORE_LOADER_CTX> pointer and a B<OSSL_STORE_INFO>
-identity B<expected>, and is used to tell the loader what object type is
+identity I<expected>, and is used to tell the loader what object type is
 expected.
-B<expected> may be zero to signify that no specific object type is expected.
+I<expected> may be zero to signify that no specific object type is expected.
 
 This function is expected to return 1 on success, 0 on error.
 
@@ -144,10 +219,10 @@ This function takes a B<OSSL_STORE_LOADER_CTX> pointer and a
 B<OSSL_STORE_SEARCH> search criterion, and is used to tell the loader what
 to search for.
 
-When called with the loader context being B<NULL>, this function is expected
+When called with the loader context being NULL, this function is expected
 to return 1 if the loader supports the criterion, otherwise 0.
 
-When called with the loader context being something other than B<NULL>, this
+When called with the loader context being something other than NULL, this
 function is expected to return 1 on success, 0 on error.
 
 =item B<OSSL_STORE_load_fn>
@@ -186,78 +261,105 @@ It returns 1 on success and 0 on error.
 
 =back
 
-=head2 Functions
-
 OSSL_STORE_LOADER_new() creates a new B<OSSL_STORE_LOADER>.
-It takes an B<ENGINE> B<e> and a string B<scheme>.
-B<scheme> must I<always> be set.
-Both B<e> and B<scheme> are used as is and must therefore be alive as
+It takes an B<ENGINE> I<e> and a string I<scheme>.
+I<scheme> must I<always> be set.
+Both I<e> and I<scheme> are used as is and must therefore be alive as
 long as the created loader is.
 
-OSSL_STORE_LOADER_get0_engine() returns the engine of the B<store_loader>.
-OSSL_STORE_LOADER_get0_scheme() returns the scheme of the B<store_loader>.
+OSSL_STORE_LOADER_get0_engine() returns the engine of the I<store_loader>.
+OSSL_STORE_LOADER_get0_scheme() returns the scheme of the I<store_loader>.
 
 OSSL_STORE_LOADER_set_open() sets the opener function for the
-B<store_loader>.
+I<store_loader>.
+
+OSSL_STORE_LOADER_set_open_with_libctx() sets the opener with library context
+function for the I<store_loader>.
 
 OSSL_STORE_LOADER_set_attach() sets the attacher function for the
-B<store_loader>.
+I<store_loader>.
 
 OSSL_STORE_LOADER_set_ctrl() sets the control function for the
-B<store_loader>.
+I<store_loader>.
 
 OSSL_STORE_LOADER_set_expect() sets the expect function for the
-B<store_loader>.
+I<store_loader>.
 
 OSSL_STORE_LOADER_set_load() sets the loader function for the
-B<store_loader>.
+I<store_loader>.
 
 OSSL_STORE_LOADER_set_eof() sets the end of file checker function for the
-B<store_loader>.
+I<store_loader>.
 
 OSSL_STORE_LOADER_set_close() sets the closing function for the
-B<store_loader>.
+I<store_loader>.
 
-OSSL_STORE_LOADER_free() frees the given B<store_loader>.
+OSSL_STORE_LOADER_free() frees the given I<store_loader>.
 
-OSSL_STORE_register_loader() register the given B<store_loader> and thereby
-makes it available for use with OSSL_STORE_open(), OSSL_STORE_load(),
-OSSL_STORE_eof() and OSSL_STORE_close().
+OSSL_STORE_register_loader() register the given I<store_loader> and
+thereby makes it available for use with OSSL_STORE_open(),
+OSSL_STORE_open_with_libctx(), OSSL_STORE_load(), OSSL_STORE_eof()
+and OSSL_STORE_close().
 
 OSSL_STORE_unregister_loader() unregister the store loader for the given
-B<scheme>.
+I<scheme>.
 
-=head1 NOTES
+=head1 RETURN VALUES
 
-The B<file:> scheme has built in support.
+OSSL_STORE_LOADER_fetch() returns a pointer to an OSSL_STORE_LOADER object,
+or NULL on error.
 
-=head1 RETURN VALUES
+OSSL_STORE_LOADER_up_ref() returns 1 on success, or 0 on error.
+
+OSSL_STORE_LOADER_free() doesn't return any value.
+
+OSSL_STORE_LOADER_provider() returns a pointer to a provider object, or
+NULL on error.
 
-The functions with the types B<OSSL_STORE_open_fn>, B<OSSL_STORE_ctrl_fn>,
-B<OSSL_STORE_expect_fn>,
-B<OSSL_STORE_load_fn>, B<OSSL_STORE_eof_fn> and B<OSSL_STORE_close_fn> have the
-same return values as OSSL_STORE_open(), OSSL_STORE_ctrl(), OSSL_STORE_expect(),
+OSSL_STORE_LOADER_properties() returns a pointer to a property
+definition string, or NULL on error.
+
+OSSL_STORE_LOADER_is_a() returns 1 if I<loader> was identifiable,
+otherwise 0.
+
+OSSL_STORE_LOADER_number() returns an integer.
+
+The functions with the types B<OSSL_STORE_open_fn>,
+B<OSSL_STORE_open_with_libctx_fn>, B<OSSL_STORE_ctrl_fn>,
+B<OSSL_STORE_expect_fn>, B<OSSL_STORE_load_fn>, B<OSSL_STORE_eof_fn>
+and B<OSSL_STORE_close_fn> have the same return values as OSSL_STORE_open(),
+OSSL_STORE_open_with_libctx(), OSSL_STORE_ctrl(), OSSL_STORE_expect(),
 OSSL_STORE_load(), OSSL_STORE_eof() and OSSL_STORE_close(), respectively.
 
 OSSL_STORE_LOADER_new() returns a pointer to a B<OSSL_STORE_LOADER> on success,
-or B<NULL> on failure.
+or NULL on failure.
 
-OSSL_STORE_LOADER_set_open(), OSSL_STORE_LOADER_set_ctrl(),
-OSSL_STORE_LOADER_set_load(), OSSL_STORE_LOADER_set_eof() and
-OSSL_STORE_LOADER_set_close() return 1 on success, or 0 on failure.
+OSSL_STORE_LOADER_set_open(), OSSL_STORE_LOADER_set_open_with_libctx(),
+OSSL_STORE_LOADER_set_ctrl(), OSSL_STORE_LOADER_set_load(),
+OSSL_STORE_LOADER_set_eof() and OSSL_STORE_LOADER_set_close() return 1
+on success, or 0 on failure.
 
 OSSL_STORE_register_loader() returns 1 on success, or 0 on failure.
 
 OSSL_STORE_unregister_loader() returns the unregistered loader on success,
-or B<NULL> on failure.
+or NULL on failure.
 
 =head1 SEE ALSO
 
-L<ossl_store(7)>, L<OSSL_STORE_open(3)>
+L<ossl_store(7)>, L<OSSL_STORE_open(3)>, L<OPENSSL_CTX(3)>,
+L<provider-storemgmt(7)>
 
 =head1 HISTORY
 
-OSSL_STORE_LOADER(), OSSL_STORE_LOADER_CTX(), OSSL_STORE_LOADER_new(),
+OSSL_STORE_LOADER_fetch(), OSSL_STORE_LOADER_up_ref(),
+OSSL_STORE_LOADER_free(), OSSL_STORE_LOADER_provider(),
+OSSL_STORE_LOADER_properties(), OSSL_STORE_LOADER_is_a(),
+OSSL_STORE_LOADER_number(), OSSL_STORE_LOADER_do_all_provided() and
+OSSL_STORE_LOADER_names_do_all() were added in OpenSSL 3.0.
+
+OSSL_STORE_open_with_libctx_fn() was added in OpenSSL 3.0.
+
+B<OSSL_STORE_LOADER>, B<OSSL_STORE_LOADER_CTX>, OSSL_STORE_LOADER_new(),
 OSSL_STORE_LOADER_set0_scheme(), OSSL_STORE_LOADER_set_open(),
 OSSL_STORE_LOADER_set_ctrl(), OSSL_STORE_LOADER_set_load(),
 OSSL_STORE_LOADER_set_eof(), OSSL_STORE_LOADER_set_close(),
diff --git a/doc/man3/OSSL_STORE_open.pod b/doc/man3/OSSL_STORE_open.pod
index ef1a934194..0f7bf9c0d3 100644
--- a/doc/man3/OSSL_STORE_open.pod
+++ b/doc/man3/OSSL_STORE_open.pod
@@ -21,11 +21,12 @@ OSSL_STORE_error, OSSL_STORE_close
                                  void *ui_data,
                                  OSSL_STORE_post_process_info_fn post_process,
                                  void *post_process_data);
- OSSL_STORE_CTX *OSSL_STORE_open_with_libctx
-     (const char *uri, OPENSSL_CTX *libctx, const char *propq,
-      const UI_METHOD *ui_method, void *ui_data,
-      OSSL_STORE_post_process_info_fn post_process, void *post_process_data);
-
+ OSSL_STORE_CTX *
+ OSSL_STORE_open_with_libctx(const char *uri,
+                             OPENSSL_CTX *libctx, const char *propq,
+                             const UI_METHOD *ui_method, void *ui_data,
+                             OSSL_STORE_post_process_info_fn post_process,
+                             void *post_process_data);
  int OSSL_STORE_ctrl(OSSL_STORE_CTX *ctx, int cmd, ... /* args */);
  OSSL_STORE_INFO *OSSL_STORE_load(OSSL_STORE_CTX *ctx);
  int OSSL_STORE_eof(OSSL_STORE_CTX *ctx);
@@ -57,7 +58,7 @@ together.
 OSSL_STORE_open_with_libctx() takes a uri or path I<uri>, password UI method
 I<ui_method> with associated data I<ui_data>, and post processing
 callback I<post_process> with associated data I<post_process_data>,
-a library context I<libctx> with an associated property query <propq>,
+a library context I<libctx> with an associated property query I<propq>,
 and opens a channel to the data located at the URI and returns a
 B<OSSL_STORE_CTX> with all necessary internal information.
 The given I<ui_method> and I<ui_data> will be reused by all
@@ -71,7 +72,7 @@ the next object, until I<post_process> returns something other than
 NULL, or the end of data is reached as indicated by OSSL_STORE_eof().
 
 OSSL_STORE_open() is similar to OSSL_STORE_open_with_libctx() but uses NULL for
-the library context I<libctx> and property query <propq>.
+the library context I<libctx> and property query I<propq>.
 
 OSSL_STORE_ctrl() takes a B<OSSL_STORE_CTX>, and command number I<cmd> and
 more arguments not specified here.
@@ -87,7 +88,7 @@ There are also global controls available:
 
 Controls if the loader should attempt to use secure memory for any
 allocated B<OSSL_STORE_INFO> and its contents.
-This control expects one argument, a pointer to an B<int> that is expected to
+This control expects one argument, a pointer to an I<int> that is expected to
 have the value 1 (yes) or 0 (no).
 Any other value is an error.
 
@@ -161,13 +162,15 @@ L<passphrase-encoding(7)>
 
 OSSL_STORE_open_with_libctx() was added in OpenSSL 3.0.
 
-OSSL_STORE_CTX(), OSSL_STORE_post_process_info_fn(), OSSL_STORE_open(),
+B<OSSL_STORE_CTX>, OSSL_STORE_post_process_info_fn(), OSSL_STORE_open(),
 OSSL_STORE_ctrl(), OSSL_STORE_load(), OSSL_STORE_eof() and OSSL_STORE_close()
 were added in OpenSSL 1.1.1.
 
 Handling of NULL I<ctx> argument for OSSL_STORE_close()
 was introduced in OpenSSL 1.1.1h.
 
+OSSL_STORE_open_with_libctx() was added in OpenSSL 3.0.
+
 =head1 COPYRIGHT
 
 Copyright 2016-2020 The OpenSSL Project Authors. All Rights Reserved.
diff --git a/doc/man7/provider-object.pod b/doc/man7/provider-object.pod
new file mode 100644
index 0000000000..63332b4f9b
--- /dev/null
+++ b/doc/man7/provider-object.pod
@@ -0,0 +1,189 @@
+=pod
+
+=head1 NAME
+
+provider-object - A specification for a provider-native object abstraction
+
+=head1 SYNOPSIS
+
+=for openssl multiple includes
+
+ #include <openssl/core_object.h>
+ #include <openssl/core_names.h>
+
+=head1 DESCRIPTION
+
+The provider-native object abstraction is a set of L<OSSL_PARAM(3)> keys and
+values that can be used to pass provider-native objects to OpenSSL library
+code or between different provider operation implementations with the help
+of OpenSSL library code.
+
+The intention is that certain provider-native operations can pass any sort
+of object that belong with other operations, or with OpenSSL library code.
+
+An object may be passed in the following manners:
+
+=over 4
+
+=item 1.
+
+I<By value>
+
+This means that the I<object data> is passed as an octet string or an UTF8
+string, which can be handled in diverse ways by other provided implementations.
+The encoding of the object depends on the context it's used in; for example,
+L<OSSL_DECODER(3)> allows multiple encodings, depending on existing decoders.
+If central OpenSSL library functionality is to handle the data directly, it
+B<must> be encoded in DER for all object types except for B<OSSL_OBJECT_NAME>
+(see L</Parameter reference> below), where it's assumed to a plain UTF8 string.
+
+=for comment A future extension might be to be able to specify encoding as a
+separate parameter.
+
+=item 2.
+
+I<By reference>
+
+This means that the I<object data> isn't passed directly, an I<object
+reference> is passed instead.  It's an octet string that only the correct
+provider understands correctly.
+
+=back
+
+Objects I<by value> can be used by anything that handles DER encoded
+objects.
+
+Objects I<by reference> need a higher level of cooperation from the
+implementation where the object originated (let's call it X) and its target
+implementation (let's call it Y):
+
+=over 4
+
+=item 1.
+
+I<An object loading function in the target implementation>
+
+The target implementation (Y) may have a function that can take an I<object
+reference>.  This can only be used if the target implementation is from the
+same provider as the one originating the object abstraction in question (X).
+
+The exact target implementation to use is determined from the I<object type>
+and possibly the I<object data type>.
+For example, when the OpenSSL library receives an object abstraction with the
+I<object type> B<OSSL_OBJECT_PKEY>, it will fetch a L<provider-keymgmt(7)>
+using the I<object data type> as its key type (the second argument in
+L<EVP_KEYMGMT_fetch(3)>).
+
+=item 2.
+
+I<An object exporter in the originating implementation>
+
+The originating implementation (X) may have an exporter function.  This
+exporter function can be used to export the object in L<OSSL_PARAM(3)> form,
+that can then be imported by the target implementation's imported function.
+
+This can be used when it's not possible to fetch the target implementation
+(Y) from the same provider.
+
+=back
+
+=head2 Parameter reference
+
+A provider-native object abstraction is an L<OSSL_PARAM(3)> with a selection
+of the following parameters:
+
+=over 4
+
+=item "data" (B<OSSL_OBJECT_PARAM_DATA>) <octet string> or <utf8 string>
+
+The object data I<passed by value>.
+
+=item "reference" (B<OSSL_OBJECT_PARAM_REFERENCE>) <octet string>
+
+The object data I<passed by reference>.
+
+=item "type" (B<OSSL_OBJECT_PARAM_TYPE>) <integer>
+
+The I<object type>, a number that may have any of the following values (all
+defined in F<< <openssl/core_object.h> >>):
+
+=over 4
+
+=item B<OSSL_OBJECT_NAME>
+
+The object data may only be I<passed by value>, and should be a UTF8
+string.
+
+This is useful for L<provider-storemgmt(7)> when a URI load results in new
+URIs.
+
+=item B<OSSL_OBJECT_PKEY>
+
+The object data is suitable as provider-native B<EVP_PKEY> key data.  The
+object data may be I<passed by value> or I<passed by reference>.
+
+=item B<OSSL_OBJECT_CERT>
+
+The object data is suitable as B<X509> data.  The object data for this
+object type can only be I<passed by value>, and should be an octet string.
+
+Since there's no provider-native X.509 object, OpenSSL libraries that
+receive this object abstraction are expected to convert the data to a
+B<X509> object with d2i_X509().
+
+=item B<OSSL_OBJECT_CRL>
+
+The object data is suitable as B<X509_CRL> data.  The object data can
+only be I<passed by value>, and should be an octet string.
+
+Since there's no provider-native X.509 CRL object, OpenSSL libraries that
+receive this object abstraction are expected to convert the data to a
+B<X509_CRL> object with d2i_X509_CRL().
+
+=back
+
+=item "data-type" (B<OSSL_OBJECT_PARAM_DATA_TYPE>) <utf8 string>
+
+The specific type of the object content.  Legitimate values depend on the
+object type; if it is B<OSSL_OBJECT_PKEY>, the data type is expected to be a
+key type suitable for fetching a L<provider-keymgmt(7)> that can handle the
+data.
+
+=for comment For objects with an unknown object type (OSSL_OBJECT_PARAM_TYPE
+is either missing or has the value OSSL_OBJECT_UNKNOWN), libcrypto
+interprets the object data type as the input type for a decoder.
+
+=item "desc" (B<OSSL_OBJECT_PARAM_DESC>) <utf8 string>
+
+A human readable text that describes extra details on the object.
+
+=back
+
+When a provider-native object abtraction is used, it I<must> contain object
+data in at least one form (object data I<passed by value>, i.e. the "data"
+item, or object data I<passed by reference>, i.e. the "reference" item).
+Both may be present at once, in which case the OpenSSL library code that
+receives this will use the most optimal variant.
+
+For objects with the object type B<OSSL_OBJECT_NAME>, that object type
+I<must> be given.
+
+=head1 SEE ALSO
+
+L<provider(7)>, L<OSSL_DECODER(3)>
+
+=head1 HISTORY
+
+The concept of providers and everything surrounding them was
+introduced 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/man7/provider-storemgmt.pod b/doc/man7/provider-storemgmt.pod
new file mode 100644
index 0000000000..2e5bddc5ec
--- /dev/null
+++ b/doc/man7/provider-storemgmt.pod
@@ -0,0 +1,178 @@
+=pod
+
+=head1 NAME
+
+provider-storemgmt - The OSSL_STORE library E<lt>-E<gt> provider functions
+
+=head1 SYNOPSIS
+
+ #include <openssl/core_dispatch.h>
+
+ /*
+  * None of these are actual functions, but are displayed like this for
+  * the function signatures for functions that are offered as function
+  * pointers in OSSL_DISPATCH arrays.
+  */
+
+ void *OSSL_FUNC_store_open(void *provctx, const char *uri);
+ void *OSSL_FUNC_store_attach(void *provctx, OSSL_CORE_BIO *bio);
+ const OSSL_PARAM *store_settable_ctx_params(void *provctx);
+ int OSSL_FUNC_store_set_ctx_params(void *loaderctx, const OSSL_PARAM[]);
+ int OSSL_FUNC_store_load(void *loaderctx,
+                          OSSL_CALLBACK *object_cb, void *object_cbarg,
+                          OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg);
+ int OSSL_FUNC_store_eof(void *loaderctx);
+ int OSSL_FUNC_store_close(void *loaderctx);
+
+ int OSSL_FUNC_store_export_object
+     (void *loaderctx, const void *objref, size_t objref_sz,
+      OSSL_CALLBACK *export_cb, void *export_cbarg);
+
+=head1 DESCRIPTION
+
+The STORE operation is the provider side of the L<ossl_store(7)> API.
+
+The primary responsibility of the STORE operation is to load all sorts
+of objects from a container indicated by URI.  These objects are given
+to the OpenSSL library in provider-native object abstraction form (see
+L<provider-object(7)>).  The OpenSSL library is then responsible for
+passing on that abstraction to suitable provided functions.
+
+Examples of functions that the OpenSSL library can pass the abstraction to
+include OSSL_FUNC_keymgmt_load() (L<provider-keymgmt(7)>),
+OSSL_FUNC_store_export_object() (which exports the object in parameterized
+form).
+
+All "functions" mentioned here are passed as function pointers between
+F<libcrypto> and the provider in B<OSSL_DISPATCH> arrays via
+B<OSSL_ALGORITHM> arrays that are returned by the provider's
+provider_query_operation() function
+(see L<provider-base(7)/Provider Functions>).
+
+All these "functions" have a corresponding function type definition named
+B<OSSL_{name}_fn>, and a helper function to retrieve the function pointer
+from a B<OSSL_DISPATCH> element named B<OSSL_get_{name}>.
+For example, the "function" OSSL_FUNC_store_load() has these:
+
+ typedef void *(OSSL_OSSL_FUNC_store_load_fn)(void *provctx,
+                                              const OSSL_PARAM params[]);
+ static ossl_inline OSSL_OSSL_FUNC_store_load_fn
+     OSSL_OSSL_FUNC_store_load(const OSSL_DISPATCH *opf);
+
+B<OSSL_DISPATCH> arrays are indexed by numbers that are provided as macros
+in L<openssl-core_dispatch.h(7)>, as follows:
+
+ OSSL_FUNC_store_open                 OSSL_FUNC_STORE_OPEN
+ OSSL_FUNC_store_attach               OSSL_FUNC_STORE_ATTACH
+ OSSL_FUNC_store_settable_ctx_params  OSSL_FUNC_STORE_SETTABLE_CTX_PARAMS
+ OSSL_FUNC_store_set_ctx_params       OSSL_FUNC_STORE_SET_CTX_PARAMS
+ OSSL_FUNC_store_load                 OSSL_FUNC_STORE_LOAD
+ OSSL_FUNC_store_eof                  OSSL_FUNC_STORE_EOF
+ OSSL_FUNC_store_close                OSSL_FUNC_STORE_CLOSE
+ OSSL_FUNC_store_export_object        OSSL_FUNC_STORE_EXPORT_OBJECT
+
+=head2 Functions
+
+OSSL_FUNC_store_open() should create a provider side context with data based
+on the input I<uri>.  The implementation is entirely responsible for the
+interpretation of the URI.
+
+OSSL_FUNC_store_attach() should create a provider side context with the core
+B<BIO> I<bio> attached.  This is an alternative to using a URI to find storage,
+supporting L<OSSL_STORE_attach(3)>.
+
+OSSL_FUNC_store_settable_ctx_params() should return a constant array of
+descriptor B<OSSL_PARAM>, for parameters that OSSL_FUNC_store_set_ctx_params()
+can handle.
+
+OSSL_FUNC_store_set_ctx_params() should set additional parameters, such as what
+kind of data to expect, search criteria, and so on.  More on those below, in
+L</Load Parameters>.  Whether unrecognised parameters are an error or simply
+ignored is at the implementation's discretion.
+
+OSSL_FUNC_store_load() loads the next object from the URI opened by
+OSSL_FUNC_store_open(), creates an object abstraction for it (see
+L<provider-object(7)>), and calls I<object_cb> with it as well as
+I<object_cbarg>.  I<object_cb> will then interpret the object abstraction
+and do what it can to wrap it or decode it into an OpenSSL structure.  In
+case a passphrase needs to be prompted to unlock an object, I<pw_cb> should
+be called.
+
+OSSL_FUNC_store_eof() indicates if the end of the set of objects from the
+URI has been reached.  When that happens, there's no point trying to do any
+further loading.
+
+OSSL_FUNC_store_close() frees the provider side context I<ctx>.
+
+=head2 Load Parameters
+
+=over 4
+
+=item "expect" (B<OSSL_STORE_PARAM_EXPECT>) <integer>
+
+Is a hint of what type of data the OpenSSL library expects to get.
+This is only useful for optimization, as the library will check that the
+object types match the expectation too.
+
+The number that can be given through this parameter is found in
+F<< <openssl/store.h> >>, with the macros having names starting with
+C<OSSL_STORE_INFO_>.  These are further described in
+L<OSSL_STORE_INFO(3)/SUPPORTED OBJECTS>.
+
+=item "subject" (B<OSSL_STORE_PARAM_SUBJECT>) <octet string>
+
+Indicates that the caller wants to search for an object with the given
+subject associated.  This can be used to select specific certificates
+by subject.
+
+The contents of the octet string is expected to be in DER form.
+
+=item "issuer" (B<OSSL_STORE_PARAM_ISSUER>) <octet string>
+
+Indicates that the caller wants to search for an object with the given
+issuer associated.  This can be used to select specific certificates
+by issuer.
+
+The contents of the octet string is expected to be in DER form.
+
+=item "serial" (B<OSSL_STORE_PARAM_SERIAL>) <integer>
+
+Indicates that the caller wants to search for an object with the given
+serial number associated.
+
+=item "digest" (B<OSSL_STORE_PARAM_DIGEST>) <utf8 string>
+
+=item "fingerprint" (B<OSSL_STORE_PARAM_FINGERPRINT>) <octet string>
+
+Indicates that the caller wants to search for an object with the given
+fingerprint, computed with the given digest.
+
+=item "alias" (B<OSSL_STORE_PARAM_ALIAS>) <utf8 string>
+
+Indicates that the caller wants to search for an object with the given
+alias (some call it a "friendly name").
+
+=back
+
+Several of these search criteria may be combined.  For example, to
+search for a certificate by issuer+serial, both the "issuer" and the
+"serial" parameters will be given.
+
+=head1 SEE ALSO
+
+L<provider(7)>
+
+=head1 HISTORY
+
+The STORE interface was introduced 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/include/crypto/x509.h b/include/crypto/x509.h
index a3c05f15b8..bd8f9ba52d 100644
--- a/include/crypto/x509.h
+++ b/include/crypto/x509.h
@@ -114,6 +114,9 @@ struct X509_crl_st {
     const X509_CRL_METHOD *meth;
     void *meth_data;
     CRYPTO_RWLOCK *lock;
+
+    OPENSSL_CTX *libctx;
+    const char *propq;
 };
 
 struct x509_revoked_st {
@@ -302,6 +305,7 @@ int x509_set1_time(ASN1_TIME **ptm, const ASN1_TIME *tm);
 int x509_print_ex_brief(BIO *bio, X509 *cert, unsigned long neg_cflags);
 int x509v3_cache_extensions(X509 *x);
 int x509_set0_libctx(X509 *x, OPENSSL_CTX *libctx, const char *propq);
+int x509_crl_set0_libctx(X509_CRL *x, OPENSSL_CTX *libctx, const char *propq);
 void x509_init_sig_info(X509 *x);
 int asn1_item_digest_with_libctx(const ASN1_ITEM *it, const EVP_MD *type,
                                  void *data, unsigned char *md,
diff --git a/include/internal/cryptlib.h b/include/internal/cryptlib.h
index 3075bd70c0..1df0c5df34 100644
--- a/include/internal/cryptlib.h
+++ b/include/internal/cryptlib.h
@@ -160,7 +160,8 @@ typedef struct ossl_ex_data_global_st {
 # 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
+# define OPENSSL_CTX_STORE_LOADER_STORE_INDEX      15
+# define OPENSSL_CTX_MAX_INDEXES                   16
 
 typedef struct openssl_ctx_method {
     void *(*new_func)(OPENSSL_CTX *ctx);
diff --git a/include/internal/passphrase.h b/include/internal/passphrase.h
new file mode 100644
index 0000000000..9077907d52
--- /dev/null
+++ b/include/internal/passphrase.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 OSSL_INTERNAL_PASSPHRASE_H
+# define OSSL_INTERNAL_PASSPHRASE_H
+
+/*
+ * This is a passphrase reader bridge with bells and whistles.
+ *
+ * On one hand, an API may wish to offer all sorts of passphrase callback
+ * possibilities to users, or may have to do so for historical reasons.
+ * On the other hand, that same API may have demands from other interfaces,
+ * notably from the libcrypto <-> provider interface, which uses
+ * OSSL_PASSPHRASE_CALLBACK consistently.
+ *
+ * The structure and functions below are the fundaments for bridging one
+ * passphrase callback form to another.
+ *
+ * In addition, extra features are included (this may be a growing list):
+ *
+ * -   password caching.  This is to be used by APIs where it's likely
+ *     that the same passphrase may be asked for more than once, but the
+ *     user shouldn't get prompted more than once.  For example, this is
+ *     useful for OSSL_DECODER, which may have to use a passphrase while
+ *     trying to find out what input it has.
+ */
+
+/*
+ * Structure to hold whatever the calling user may specify.  This structure
+ * is intended to be integrated into API specific structures or to be used
+ * as a local on-stack variable type.  Therefore, no functions to allocate
+ * or freed it on the heap is offered.
+ */
+struct ossl_passphrase_data_st {
+    enum {
+        is_expl_passphrase = 1, /* Explicit passphrase given by user */
+        is_pem_password,        /* pem_password_cb given by user */
+        is_ossl_passphrase,     /* OSSL_PASSPHRASE_CALLBACK given by user */
+        is_ui_method            /* UI_METHOD given by user */
+    } type;
+    union {
+        struct {
+            char *passphrase_copy;
+            size_t passphrase_len;
+        } expl_passphrase;
+
+        struct {
+            pem_password_cb *password_cb;
+            void *password_cbarg;
+        } pem_password;
+
+        struct {
+            OSSL_PASSPHRASE_CALLBACK *passphrase_cb;
+            void *passphrase_cbarg;
+        } ossl_passphrase;
+
+        struct {
+            const UI_METHOD *ui_method;
+            void *ui_method_data;
+        } ui_method;
+    } _;
+
+    /*-
+     * Flags section
+     */
+
+    /* Set to indicate that caching should be done */
+    unsigned int flag_cache_passphrase:1;
+
+    /*-
+     * Misc section: caches and other
+     */
+
+    char *cached_passphrase;
+    size_t cached_passphrase_len;
+};
+
+/* Structure manipulation */
+
+void ossl_pw_clear_passphrase_data(struct ossl_passphrase_data_st *data);
+void ossl_pw_clear_passphrase_cache(struct ossl_passphrase_data_st *data);
+
+int ossl_pw_set_passphrase(struct ossl_passphrase_data_st *data,
+                           const unsigned char *passphrase,
+                           size_t passphrase_len);
+int ossl_pw_set_pem_password_cb(struct ossl_passphrase_data_st *data,
+                                pem_password_cb *cb, void *cbarg);
+int ossl_pw_set_ossl_passphrase_cb(struct ossl_passphrase_data_st *data,
+                                   OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg);
+int ossl_pw_set_ui_method(struct ossl_passphrase_data_st *data,
+                          const UI_METHOD *ui_method, void *ui_data);
+
+int ossl_pw_enable_passphrase_caching(struct ossl_passphrase_data_st *data);
+int ossl_pw_disable_passphrase_caching(struct ossl_passphrase_data_st *data);
+
+/* Central function for direct calls */
+
+int ossl_pw_get_passphrase(char *pass, size_t pass_size, size_t *pass_len,
+                           const OSSL_PARAM params[], int verify,
+                           struct ossl_passphrase_data_st *data);
+
+/* Callback functions */
+
+/*
+ * All of these callback expect that the callback argument is a
+ * struct ossl_passphrase_data_st
+ */
+
+pem_password_cb ossl_pw_pem_password;
+/* One callback for encoding (verification prompt) and one for decoding */
+OSSL_PASSPHRASE_CALLBACK ossl_pw_passphrase_callback_enc;
+OSSL_PASSPHRASE_CALLBACK ossl_pw_passphrase_callback_dec;
+
+#endif
diff --git a/include/openssl/core_dispatch.h b/include/openssl/core_dispatch.h
index ee589eae6b..b21fe559f7 100644
--- a/include/openssl/core_dispatch.h
+++ b/include/openssl/core_dispatch.h
@@ -191,10 +191,11 @@ OSSL_CORE_MAKE_FUNC(int, provider_self_test, (void *provctx))
 # define OSSL_OP_SIGNATURE                          12
 # define OSSL_OP_ASYM_CIPHER                        13
 /* New section for non-EVP operations */
-# define OSSL_OP_ENCODER                         20
-# define OSSL_OP_DECODER                       21
+# define OSSL_OP_ENCODER                            20
+# define OSSL_OP_DECODER                            21
+# define OSSL_OP_STORE                              22
 /* Highest known operation number */
-# define OSSL_OP__HIGHEST                           21
+# define OSSL_OP__HIGHEST                           22
 
 /* Digests */
 
@@ -760,6 +761,42 @@ OSSL_CORE_MAKE_FUNC(int, decoder_export_object,
                     (void *ctx, const void *objref, size_t objref_sz,
                      OSSL_CALLBACK *export_cb, void *export_cbarg))
 
+/*-
+ * Store
+ *
+ * Objects are scanned by using the 'open', 'load', 'eof' and 'close'
+ * functions, which implement an OSSL_STORE loader.
+ *
+ * store_load() works in a way that's very similar to the decoders, in
+ * that they pass an abstract object through a callback, either as a DER
+ * octet string or as an object reference, which libcrypto will have to
+ * deal with.
+ */
+
+#define OSSL_FUNC_STORE_OPEN                        1
+#define OSSL_FUNC_STORE_ATTACH                      2
+#define OSSL_FUNC_STORE_SETTABLE_CTX_PARAMS         3
+#define OSSL_FUNC_STORE_SET_CTX_PARAMS              4
+#define OSSL_FUNC_STORE_LOAD                        5
+#define OSSL_FUNC_STORE_EOF                         6
+#define OSSL_FUNC_STORE_CLOSE                       7
+#define OSSL_FUNC_STORE_EXPORT_OBJECT               8
+OSSL_CORE_MAKE_FUNC(void *, store_open, (void *provctx, const char *uri))
+OSSL_CORE_MAKE_FUNC(void *, store_attach, (void *provctx, OSSL_CORE_BIO *in))
+OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, store_settable_ctx_params,
+                    (void *provctx))
+OSSL_CORE_MAKE_FUNC(int, store_set_ctx_params,
+                    (void *loaderctx, const OSSL_PARAM params[]))
+OSSL_CORE_MAKE_FUNC(int, store_load,
+                    (void *loaderctx,
+                     OSSL_CALLBACK *object_cb, void *object_cbarg,
+                     OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg))
+OSSL_CORE_MAKE_FUNC(int, store_eof, (void *loaderctx))
+OSSL_CORE_MAKE_FUNC(int, store_close, (void *loaderctx))
+OSSL_CORE_MAKE_FUNC(int, store_export_object,
+                    (void *loaderctx, 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 bf6dd2e96f..4ca794fd50 100644
--- a/include/openssl/core_names.h
+++ b/include/openssl/core_names.h
@@ -30,6 +30,23 @@ extern "C" {
 #define OSSL_PROV_PARAM_SELF_TEST_TYPE   "st-type"  /* utf8_string */
 #define OSSL_PROV_PARAM_SELF_TEST_DESC   "st-desc"  /* utf8_string */
 
+/*-
+ * Provider-native object abstractions
+ *
+ * These are used when a provider wants to pass object data or an object
+ * reference back to libcrypto.  This is only useful for provider functions
+ * that take a callback to which an OSSL_PARAM array with these parameters
+ * can be passed.
+ *
+ * This set of parameter names is explained in detail in provider-object(7)
+ * (doc/man7/provider-object.pod)
+ */
+#define OSSL_OBJECT_PARAM_TYPE       "type"      /* INTEGER */
+#define OSSL_OBJECT_PARAM_DATA_TYPE  "data-type" /* UTF8_STRING */
+#define OSSL_OBJECT_PARAM_REFERENCE  "reference" /* OCTET_STRING */
+#define OSSL_OBJECT_PARAM_DATA       "data" /* OCTET_STRING or UTF8_STRING */
+#define OSSL_OBJECT_PARAM_DESC       "desc"      /* UTF8_STRING */
+
 /*
  * Algorithm parameters
  * If "engine" or "properties" are specified, they should always be paired
@@ -433,9 +450,6 @@ extern "C" {
 #define OSSL_DECODER_PARAM_PROPERTIES   OSSL_ALG_PARAM_PROPERTIES
 #define OSSL_DECODER_PARAM_PASS         "passphrase"
 #define OSSL_DECODER_PARAM_INPUT_TYPE   "input-type"
-#define OSSL_DECODER_PARAM_DATA_TYPE    "data-type"
-#define OSSL_DECODER_PARAM_DATA         "data"
-#define OSSL_DECODER_PARAM_REFERENCE    "reference"
 
 /* Passphrase callback parameters */
 #define OSSL_PASSPHRASE_PARAM_INFO      "info"
@@ -470,6 +484,34 @@ extern "C" {
 #define OSSL_CAPABILITY_TLS_GROUP_MIN_DTLS          "tls-min-dtls"
 #define OSSL_CAPABILITY_TLS_GROUP_MAX_DTLS          "tls-max-dtls"
 
+/*-
+ * storemgmt parameters
+ */
+
+/*
+ * Used by storemgmt_ctx_set_params():
+ *
+ * - OSSL_STORE_PARAM_EXPECT is an INTEGER, and the value is any of the
+ *   OSSL_STORE_INFO numbers.  This is used to set the expected type of
+ *   object loaded.
+ *
+ * - OSSL_STORE_PARAM_SUBJECT, OSSL_STORE_PARAM_ISSUER,
+ *   OSSL_STORE_PARAM_SERIAL, OSSL_STORE_PARAM_FINGERPRINT,
+ *   OSSL_STORE_PARAM_DIGEST, OSSL_STORE_PARAM_ALIAS
+ *   are used as search criteria.
+ *   (OSSL_STORE_PARAM_DIGEST is used with OSSL_STORE_PARAM_FINGERPRINT)
+ */
+#define OSSL_STORE_PARAM_EXPECT     "expect"       /* INTEGER */
+#define OSSL_STORE_PARAM_SUBJECT    "subject" /* DER blob => OCTET_STRING */
+#define OSSL_STORE_PARAM_ISSUER     "name" /* DER blob => OCTET_STRING */
+#define OSSL_STORE_PARAM_SERIAL     "serial"       /* INTEGER */
+#define OSSL_STORE_PARAM_DIGEST     "digest"       /* UTF8_STRING */
+#define OSSL_STORE_PARAM_FINGERPRINT "fingerprint" /* OCTET_STRING */
+#define OSSL_STORE_PARAM_ALIAS      "alias"        /* UTF8_STRING */
+
+/* You may want to pass properties for the provider implementation to use */
+#define OSSL_STORE_PARAM_PROPERTIES "properties"   /* utf8_string */
+
 # ifdef __cplusplus
 }
 # endif
diff --git a/include/openssl/core_object.h b/include/openssl/core_object.h
new file mode 100644
index 0000000000..395279d7bc
--- /dev/null
+++ b/include/openssl/core_object.h
@@ -0,0 +1,40 @@
+/*
+ * 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_CORE_OBJECT_H
+# define OPENSSL_CORE_OBJECT_H
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+/*-
+ * Known object types
+ *
+ * These numbers are used as values for the OSSL_PARAM parameter
+ * OSSL_OBJECT_PARAM_TYPE.
+ *
+ * For most of these types, there's a corresponding libcrypto object type.
+ * The corresponding type is indicated with a comment after the number.
+ */
+# define OSSL_OBJECT_UNKNOWN            0
+# define OSSL_OBJECT_NAME               1 /* char * */
+# define OSSL_OBJECT_PKEY               2 /* EVP_PKEY * */
+# define OSSL_OBJECT_CERT               3 /* X509 * */
+# define OSSL_OBJECT_CRL                4 /* X509_CRL * */
+
+/*
+ * The rest of the associated OSSL_PARAM elements is described in core_names.h
+ */
+
+# ifdef __cplusplus
+}
+# endif
+
+#endif
diff --git a/include/openssl/decoder.h b/include/openssl/decoder.h
index cb5a404b89..91dfca4a09 100644
--- a/include/openssl/decoder.h
+++ b/include/openssl/decoder.h
@@ -58,6 +58,9 @@ int OSSL_DECODER_CTX_set_passphrase(OSSL_DECODER_CTX *ctx,
 int OSSL_DECODER_CTX_set_pem_password_cb(OSSL_DECODER_CTX *ctx,
                                          pem_password_cb *cb,
                                          void *cbarg);
+int OSSL_DECODER_CTX_set_passphrase_cb(OSSL_DECODER_CTX *ctx,
+                                       OSSL_PASSPHRASE_CALLBACK *cb,
+                                       void *cbarg);
 int OSSL_DECODER_CTX_set_passphrase_ui(OSSL_DECODER_CTX *ctx,
                                        const UI_METHOD *ui_method,
                                        void *ui_data);
diff --git a/include/openssl/err.h b/include/openssl/err.h
index 4dd69c6a3f..497436d2c5 100644
--- a/include/openssl/err.h
+++ b/include/openssl/err.h
@@ -295,6 +295,7 @@ static ossl_inline int ERR_FATAL_ERROR(unsigned long errcode)
 # define ERR_R_DSA_LIB   ERR_LIB_DSA/* 10 */
 # define ERR_R_X509_LIB  ERR_LIB_X509/* 11 */
 # define ERR_R_ASN1_LIB  ERR_LIB_ASN1/* 13 */
+# define ERR_R_CRYPTO_LIB ERR_LIB_CRYPTO/* 15 */
 # define ERR_R_EC_LIB    ERR_LIB_EC/* 16 */
 # define ERR_R_BIO_LIB   ERR_LIB_BIO/* 32 */
 # define ERR_R_PKCS7_LIB ERR_LIB_PKCS7/* 33 */
diff --git a/include/openssl/params.h b/include/openssl/params.h
index 44fc1a6a38..6ed7ecbb24 100644
--- a/include/openssl/params.h
+++ b/include/openssl/params.h
@@ -142,6 +142,10 @@ int OSSL_PARAM_get_octet_ptr(const OSSL_PARAM *p, const void **val,
 int OSSL_PARAM_set_octet_ptr(OSSL_PARAM *p, const void *val,
                              size_t used_len);
 
+int OSSL_PARAM_get_utf8_string_ptr(const OSSL_PARAM *p, const char **val);
+int OSSL_PARAM_get_octet_string_ptr(const OSSL_PARAM *p, const void **val,
+                                    size_t *used_len);
+
 int OSSL_PARAM_modified(const OSSL_PARAM *p);
 void OSSL_PARAM_set_all_unmodified(OSSL_PARAM *p);
 
diff --git a/include/openssl/store.h b/include/openssl/store.h
index b75c79868c..9a2b423371 100644
--- a/include/openssl/store.h
+++ b/include/openssl/store.h
@@ -52,15 +52,16 @@ typedef OSSL_STORE_INFO *(*OSSL_STORE_post_process_info_fn)(OSSL_STORE_INFO *,
  * Returns a context reference which represents the channel to communicate
  * through.
  */
-OSSL_STORE_CTX *OSSL_STORE_open(const char *uri, const UI_METHOD *ui_method,
-                                void *ui_data,
-                                OSSL_STORE_post_process_info_fn post_process,
-                                void *post_process_data);
-
-OSSL_STORE_CTX *OSSL_STORE_open_with_libctx
-    (const char *uri, OPENSSL_CTX *libctx, const char *propq,
-     const UI_METHOD *ui_method, void *ui_data,
-     OSSL_STORE_post_process_info_fn post_process, void *post_process_data);
+OSSL_STORE_CTX *
+OSSL_STORE_open(const char *uri, const UI_METHOD *ui_method, void *ui_data,
+                OSSL_STORE_post_process_info_fn post_process,
+                void *post_process_data);
+OSSL_STORE_CTX *
+OSSL_STORE_open_with_libctx(const char *uri,
+                            OPENSSL_CTX *libctx, const char *propq,
+                            const UI_METHOD *ui_method, void *ui_data,
+                            OSSL_STORE_post_process_info_fn post_process,
+                            void *post_process_data);
 
 /*
  * Control / fine tune the OSSL_STORE channel.  |cmd| determines what is to be
@@ -234,6 +235,32 @@ int OSSL_STORE_expect(OSSL_STORE_CTX *ctx, int expected_type);
 int OSSL_STORE_find(OSSL_STORE_CTX *ctx, const OSSL_STORE_SEARCH *search);
 
 
+/*-
+ *  Function to fetch a loader and extract data from it
+ *  ---------------------------------------------------
+ */
+
+typedef struct ossl_store_loader_st OSSL_STORE_LOADER;
+
+OSSL_STORE_LOADER *OSSL_STORE_LOADER_fetch(const char *scheme,
+                                           OPENSSL_CTX *libctx,
+                                           const char *properties);
+int OSSL_STORE_LOADER_up_ref(OSSL_STORE_LOADER *loader);
+void OSSL_STORE_LOADER_free(OSSL_STORE_LOADER *loader);
+const OSSL_PROVIDER *OSSL_STORE_LOADER_provider(const OSSL_STORE_LOADER *
+                                                loader);
+const char *OSSL_STORE_LOADER_properties(const OSSL_STORE_LOADER *loader);
+int OSSL_STORE_LOADER_number(const OSSL_STORE_LOADER *loader);
+int OSSL_STORE_LOADER_is_a(const OSSL_STORE_LOADER *loader,
+                           const char *scheme);
+void OSSL_STORE_LOADER_do_all_provided(OPENSSL_CTX *libctx,
+                                       void (*fn)(OSSL_STORE_LOADER *loader,
+                                                  void *arg),
+                                       void *arg);
+void OSSL_STORE_LOADER_names_do_all(const OSSL_STORE_LOADER *loader,
+                                    void (*fn)(const char *name, void *data),
+                                    void *data);
+
 /*-
  *  Function to register a loader for the given URI scheme.
  *  -------------------------------------------------------
@@ -242,10 +269,6 @@ int OSSL_STORE_find(OSSL_STORE_CTX *ctx, const OSSL_STORE_SEARCH *search);
  *  scheme.
  */
 
-typedef struct ossl_store_loader_st OSSL_STORE_LOADER;
-OSSL_STORE_LOADER *OSSL_STORE_LOADER_new(ENGINE *e, const char *scheme);
-const ENGINE *OSSL_STORE_LOADER_get0_engine(const OSSL_STORE_LOADER *loader);
-const char *OSSL_STORE_LOADER_get0_scheme(const OSSL_STORE_LOADER *loader);
 /* struct ossl_store_loader_ctx_st is defined differently by each loader */
 typedef struct ossl_store_loader_ctx_st OSSL_STORE_LOADER_CTX;
 typedef OSSL_STORE_LOADER_CTX *(*OSSL_STORE_open_fn)
@@ -256,41 +279,47 @@ typedef OSSL_STORE_LOADER_CTX *(*OSSL_STORE_open_with_libctx_fn)
      const char *uri, OPENSSL_CTX *libctx, const char *propq,
      const UI_METHOD *ui_method, void *ui_data);
 
-int OSSL_STORE_LOADER_set_open(OSSL_STORE_LOADER *loader,
-                               OSSL_STORE_open_fn open_function);
 typedef OSSL_STORE_LOADER_CTX *(*OSSL_STORE_attach_fn)
     (const OSSL_STORE_LOADER *loader, BIO *bio,
      OPENSSL_CTX *libctx, const char *propq,
      const UI_METHOD *ui_method, void *ui_data);
+typedef int (*OSSL_STORE_ctrl_fn)
+    (OSSL_STORE_LOADER_CTX *ctx, int cmd, va_list args);
+typedef int (*OSSL_STORE_expect_fn)
+    (OSSL_STORE_LOADER_CTX *ctx, int expected);
+typedef int (*OSSL_STORE_find_fn)
+    (OSSL_STORE_LOADER_CTX *ctx, const OSSL_STORE_SEARCH *criteria);
+typedef OSSL_STORE_INFO *(*OSSL_STORE_load_fn)
+    (OSSL_STORE_LOADER_CTX *ctx, const UI_METHOD *ui_method, void *ui_data);
+typedef int (*OSSL_STORE_eof_fn)(OSSL_STORE_LOADER_CTX *ctx);
+typedef int (*OSSL_STORE_error_fn)(OSSL_STORE_LOADER_CTX *ctx);
+typedef int (*OSSL_STORE_close_fn)(OSSL_STORE_LOADER_CTX *ctx);
+
+OSSL_STORE_LOADER *OSSL_STORE_LOADER_new(ENGINE *e, const char *scheme);
+int OSSL_STORE_LOADER_set_open(OSSL_STORE_LOADER *loader,
+                               OSSL_STORE_open_fn open_function);
+int OSSL_STORE_LOADER_set_open_with_libctx
+    (OSSL_STORE_LOADER *loader,
+     OSSL_STORE_open_with_libctx_fn open_with_libctx_function);
 int OSSL_STORE_LOADER_set_attach(OSSL_STORE_LOADER *loader,
                                  OSSL_STORE_attach_fn attach_function);
-typedef int (*OSSL_STORE_ctrl_fn)(OSSL_STORE_LOADER_CTX *ctx, int cmd,
-                                  va_list args);
 int OSSL_STORE_LOADER_set_ctrl(OSSL_STORE_LOADER *loader,
                                OSSL_STORE_ctrl_fn ctrl_function);
-typedef int (*OSSL_STORE_expect_fn)(OSSL_STORE_LOADER_CTX *ctx, int expected);
 int OSSL_STORE_LOADER_set_expect(OSSL_STORE_LOADER *loader,
                                  OSSL_STORE_expect_fn expect_function);
-typedef int (*OSSL_STORE_find_fn)(OSSL_STORE_LOADER_CTX *ctx,
-                                  const OSSL_STORE_SEARCH *criteria);
 int OSSL_STORE_LOADER_set_find(OSSL_STORE_LOADER *loader,
                                OSSL_STORE_find_fn find_function);
-typedef OSSL_STORE_INFO *(*OSSL_STORE_load_fn)(OSSL_STORE_LOADER_CTX *ctx,
-                                               const UI_METHOD *ui_method,
-                                               void *ui_data);
 int OSSL_STORE_LOADER_set_load(OSSL_STORE_LOADER *loader,
                                OSSL_STORE_load_fn load_function);
-
-typedef int (*OSSL_STORE_eof_fn)(OSSL_STORE_LOADER_CTX *ctx);
 int OSSL_STORE_LOADER_set_eof(OSSL_STORE_LOADER *loader,
                               OSSL_STORE_eof_fn eof_function);
-typedef int (*OSSL_STORE_error_fn)(OSSL_STORE_LOADER_CTX *ctx);
 int OSSL_STORE_LOADER_set_error(OSSL_STORE_LOADER *loader,
                                 OSSL_STORE_error_fn error_function);
-typedef int (*OSSL_STORE_close_fn)(OSSL_STORE_LOADER_CTX *ctx);
 int OSSL_STORE_LOADER_set_close(OSSL_STORE_LOADER *loader,
                                 OSSL_STORE_close_fn close_function);
-void OSSL_STORE_LOADER_free(OSSL_STORE_LOADER *loader);
+
+const ENGINE *OSSL_STORE_LOADER_get0_engine(const OSSL_STORE_LOADER *loader);
+const char * OSSL_STORE_LOADER_get0_scheme(const OSSL_STORE_LOADER *loader);
 
 int OSSL_STORE_register_loader(OSSL_STORE_LOADER *loader);
 OSSL_STORE_LOADER *OSSL_STORE_unregister_loader(const char *scheme);
diff --git a/providers/implementations/encode_decode/decode_common.c b/providers/implementations/encode_decode/decode_common.c
index 1b5eea4c8d..2277c150c1 100644
--- a/providers/implementations/encode_decode/decode_common.c
+++ b/providers/implementations/encode_decode/decode_common.c
@@ -16,6 +16,7 @@
 #include "internal/pem.h"        /* For internal PVK and "blob" functions */
 #include "internal/cryptlib.h"
 #include "crypto/asn1.h"
+#include "internal/passphrase.h"
 #include "prov/bio.h"               /* ossl_prov_bio_printf() */
 #include "prov/providercommonerr.h" /* PROV_R_READ_KEY */
 #include "encoder_local.h"
@@ -58,44 +59,22 @@ EVP_PKEY *ossl_prov_read_msblob(PROV_CTX *provctx, OSSL_CORE_BIO *cin,
     return pkey;
 }
 
-struct pwdata_st {
-    OSSL_PASSPHRASE_CALLBACK *pw_cb;
-    void *pw_cbarg;
-};
-
-pem_password_cb pw_pem_password_to_ossl_passhrase;
-int pw_pem_password_to_ossl_passhrase(char *buf, int size, int rwflag,
-                                      void *userdata)
-{
-    struct pwdata_st *data = userdata;
-    size_t pw_len = 0;
-    static char prompt_info[] = "pass phrase";
-    OSSL_PARAM params[] = {
-        OSSL_PARAM_utf8_string(OSSL_PASSPHRASE_PARAM_INFO, prompt_info,
-                               sizeof(prompt_info) - 1),
-        OSSL_PARAM_END
-    };
-    int ok = data->pw_cb(buf, (size_t)size, &pw_len, params, data->pw_cbarg);
-
-    if (ok)
-        return (int)pw_len;
-    else
-        return -1;
-}
-
 # ifndef OPENSSL_NO_RC4
 EVP_PKEY *ossl_prov_read_pvk(PROV_CTX *provctx, OSSL_CORE_BIO *cin,
                              OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
 {
-    BIO *in = bio_new_from_core_bio(provctx, cin);
+    BIO *in = NULL;
     EVP_PKEY *pkey = NULL;
-    struct pwdata_st pwdata;
+    struct ossl_passphrase_data_st pwdata;
 
-    pwdata.pw_cb = pw_cb;
-    pwdata.pw_cbarg = pw_cbarg;
-    pkey = b2i_PVK_bio(in, pw_pem_password_to_ossl_passhrase, &pwdata);
+    memset(&pwdata, 0, sizeof(pwdata));
+    if (!ossl_pw_set_ossl_passphrase_cb(&pwdata, pw_cb, pw_cbarg))
+        return NULL;
 
+    in = bio_new_from_core_bio(provctx, cin);
+    pkey = b2i_PVK_bio(in, ossl_pw_pem_password, &pwdata);
     BIO_free(in);
+
     return pkey;
 }
 # endif
diff --git a/providers/implementations/encode_decode/decode_der2key.c b/providers/implementations/encode_decode/decode_der2key.c
index 6af1c0927d..b8b268217d 100644
--- a/providers/implementations/encode_decode/decode_der2key.c
+++ b/providers/implementations/encode_decode/decode_der2key.c
@@ -15,6 +15,7 @@
 
 #include <openssl/core_dispatch.h>
 #include <openssl/core_names.h>
+#include <openssl/core_object.h>
 #include <openssl/crypto.h>
 #include <openssl/params.h>
 #include <openssl/x509.h>
@@ -157,16 +158,19 @@ static int der2key_decode(void *vctx, OSSL_CORE_BIO *cin,
     OPENSSL_free(der);
 
     if (key != NULL) {
-        OSSL_PARAM params[3];
+        OSSL_PARAM params[4];
+        int object_type = OSSL_OBJECT_PKEY;
 
         params[0] =
-            OSSL_PARAM_construct_utf8_string(OSSL_DECODER_PARAM_DATA_TYPE,
+            OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, &object_type);
+        params[1] =
+            OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE,
                                              (char *)ctx->desc->name, 0);
         /* The address of the key becomes the octet string */
-        params[1] =
-            OSSL_PARAM_construct_octet_string(OSSL_DECODER_PARAM_REFERENCE,
+        params[2] =
+            OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_REFERENCE,
                                               &key, sizeof(key));
-        params[2] = OSSL_PARAM_construct_end();
+        params[3] = OSSL_PARAM_construct_end();
 
         ok = data_cb(params, data_cbarg);
     }
diff --git a/providers/implementations/encode_decode/decode_ms2key.c b/providers/implementations/encode_decode/decode_ms2key.c
index 453e8188b3..d8aa813ced 100644
--- a/providers/implementations/encode_decode/decode_ms2key.c
+++ b/providers/implementations/encode_decode/decode_ms2key.c
@@ -15,6 +15,7 @@
 
 #include <openssl/core_dispatch.h>
 #include <openssl/core_names.h>
+#include <openssl/core_object.h>
 #include <openssl/crypto.h>
 #include <openssl/params.h>
 #include <openssl/x509.h>
@@ -131,16 +132,19 @@ static int ms2key_post(struct ms2key_ctx_st *ctx, EVP_PKEY *pkey,
     }
 
     if (key != NULL) {
-        OSSL_PARAM params[3];
+        OSSL_PARAM params[4];
+        int object_type = OSSL_OBJECT_PKEY;
 
         params[0] =
-            OSSL_PARAM_construct_utf8_string(OSSL_DECODER_PARAM_DATA_TYPE,
+            OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, &object_type);
+        params[1] =
+            OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE,
                                              (char *)ctx->desc->name, 0);
         /* The address of the key becomes the octet string */
-        params[1] =
-            OSSL_PARAM_construct_octet_string(OSSL_DECODER_PARAM_REFERENCE,
+        params[2] =
+            OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_REFERENCE,
                                               &key, sizeof(key));
-        params[2] = OSSL_PARAM_construct_end();
+        params[3] = OSSL_PARAM_construct_end();
 
         ok = data_cb(params, data_cbarg);
     }
diff --git a/providers/implementations/encode_decode/decode_pem2der.c b/providers/implementations/encode_decode/decode_pem2der.c
index 7ba1cbe3d3..cbee397982 100644
--- a/providers/implementations/encode_decode/decode_pem2der.c
+++ b/providers/implementations/encode_decode/decode_pem2der.c
@@ -130,10 +130,10 @@ static int pem2der_decode(void *vctx, OSSL_CORE_BIO *cin,
         OSSL_PARAM params[3];
 
         params[0] =
-            OSSL_PARAM_construct_utf8_string(OSSL_DECODER_PARAM_DATA_TYPE,
+            OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE,
                                              pem_name, 0);
         params[1] =
-            OSSL_PARAM_construct_octet_string(OSSL_DECODER_PARAM_DATA,
+            OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_DATA,
                                               der, der_len);
         params[2] = OSSL_PARAM_construct_end();
 
diff --git a/util/libcrypto.num b/util/libcrypto.num
index 549ab0a7c7..99790a1b74 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -5272,3 +5272,15 @@ PEM_read_bio_PUBKEY_ex                  ?	3_0_0	EXIST::FUNCTION:
 PEM_read_PUBKEY_ex                      ?	3_0_0	EXIST::FUNCTION:STDIO
 PEM_read_bio_Parameters_ex              ?	3_0_0	EXIST::FUNCTION:
 EC_GROUP_new_from_params                ?	3_0_0	EXIST::FUNCTION:EC
+OSSL_STORE_LOADER_set_open_with_libctx  ?	3_0_0	EXIST::FUNCTION:
+OSSL_STORE_LOADER_fetch                 ?	3_0_0	EXIST::FUNCTION:
+OSSL_STORE_LOADER_up_ref                ?	3_0_0	EXIST::FUNCTION:
+OSSL_STORE_LOADER_provider              ?	3_0_0	EXIST::FUNCTION:
+OSSL_STORE_LOADER_properties            ?	3_0_0	EXIST::FUNCTION:
+OSSL_STORE_LOADER_number                ?	3_0_0	EXIST::FUNCTION:
+OSSL_STORE_LOADER_is_a                  ?	3_0_0	EXIST::FUNCTION:
+OSSL_STORE_LOADER_do_all_provided       ?	3_0_0	EXIST::FUNCTION:
+OSSL_STORE_LOADER_names_do_all          ?	3_0_0	EXIST::FUNCTION:
+OSSL_PARAM_get_utf8_string_ptr          ?	3_0_0	EXIST::FUNCTION:
+OSSL_PARAM_get_octet_string_ptr         ?	3_0_0	EXIST::FUNCTION:
+OSSL_DECODER_CTX_set_passphrase_cb      ?	3_0_0	EXIST::FUNCTION:
diff --git a/util/other.syms b/util/other.syms
index 97f71906da..964e09f0bd 100644
--- a/util/other.syms
+++ b/util/other.syms
@@ -66,6 +66,7 @@ OSSL_STORE_eof_fn                       datatype
 OSSL_STORE_error_fn                     datatype
 OSSL_STORE_load_fn                      datatype
 OSSL_STORE_open_fn                      datatype
+OSSL_STORE_open_with_libctx_fn          datatype
 OSSL_STORE_post_process_info_fn         datatype
 OSSL_trace_cb                           datatype
 PROFESSION_INFO                         datatype


More information about the openssl-commits mailing list