[openssl] master update

Richard Levitte levitte at openssl.org
Thu May 14 10:23:12 UTC 2020


The branch master has been updated
       via  90ad284f4e76254f8d67686ae3a5d6c576037091 (commit)
       via  16e3588d98b313701a55ab1337b1d30ba7b08872 (commit)
       via  d49be019d257149d61b9061be83602ec51fa9812 (commit)
       via  f63f3b72949e5437a9bd6de67e9301a8c24c731e (commit)
       via  d59b7a54a5332303c42c3d097db5764dc809ecc9 (commit)
       via  106ec30bc74d5cd3086a3b959a11d73e46d7b9d8 (commit)
       via  e25761b10d48abb36a5863b087152be81ea28466 (commit)
       via  2d5536609ba92481daf42614a36bafb4e1d99293 (commit)
       via  2c6094baca6476d8b024dc7d9f461dae597ae797 (commit)
       via  ea297dca509b16190ad0a915f1324777b08ed8d8 (commit)
       via  8a758e96f2865f0aee417025630626f095bb3ae3 (commit)
       via  0ec36bf117b2c79f5663effd638410f1676c38dd (commit)
       via  36a2a551d7dd5628a3533f7f23b1f3827f9535f7 (commit)
       via  2275ff656c6d2043b40663686ec6627613d68318 (commit)
       via  967cc3f9390740f76f6ef3c91f2aeceab1902b19 (commit)
       via  15671090f46364a0e92456b32ead7b4714ae0b5e (commit)
       via  e9d6186e0507fb814310c5230293ff62310c5f9d (commit)
       via  645a541a3fdabd32cb8cbda48651f4150486189d (commit)
       via  484d1a73c70000ad07b156f04368b3922f9910b7 (commit)
      from  a87820e16bbbbb1f8a68ddaf3aa1159da886acca (commit)


- Log -----------------------------------------------------------------
commit 90ad284f4e76254f8d67686ae3a5d6c576037091
Author: Richard Levitte <levitte at openssl.org>
Date:   Tue May 12 10:27:46 2020 +0200

    PROV: make some DER AID arrays non-static, to avoid clang complaints
    
    The problem encountered is that some arrays were deemed unnecessary by
    clang, for example:
    
        providers/common/der/der_rsa.c:424:28: error: variable 'der_aid_sha224Identifier' is not needed and will not be emitted [-Werror,-Wunneeded-internal-declaration]
        static const unsigned char der_aid_sha224Identifier[] = {
                                   ^
    
    However, these arrays are used in sizeof() expressions in other parts
    of the code that's actually used, making that warning-turned-error a
    practical problem.  We solve this by making the array non-static,
    which guarantees that the arrays will be emitted, even though
    unnecessarily.  Fortunately, they are very small.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11710)

commit 16e3588d98b313701a55ab1337b1d30ba7b08872
Author: Richard Levitte <levitte at openssl.org>
Date:   Tue May 12 09:54:04 2020 +0200

    .travis.yml: never use -Werror, use --strict-warnings instead
    
    There are a few things in the OpenSSL code that are known to give
    warnings that we know are harmless.  We test our builds accordingly.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11710)

commit d49be019d257149d61b9061be83602ec51fa9812
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon May 11 18:27:04 2020 +0200

    test/recipes/15-test_rsapss.t: Add test with unrestricted signature
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11710)

commit f63f3b72949e5437a9bd6de67e9301a8c24c731e
Author: Richard Levitte <levitte at openssl.org>
Date:   Fri May 8 19:39:44 2020 +0200

    test/ssl-tests/20-cert-select.cnf.in: Re-enable RSA-PSS related tests
    
    There were a few RSA-PSS related tests that were disabled for non-default
    library contexts.  We now re-enable them.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11710)

commit d59b7a54a5332303c42c3d097db5764dc809ecc9
Author: Richard Levitte <levitte at openssl.org>
Date:   Thu May 7 09:56:52 2020 +0200

    test/evp_pkey_provided_test.c: Display first, compare after
    
    To make it easier to check the generated key manually, display it
    before comparing diverse other serializations.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11710)

commit 106ec30bc74d5cd3086a3b959a11d73e46d7b9d8
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed May 6 21:52:12 2020 +0200

    PROV & ASYM_CIPHER: Adapt the RSA asymmetric cipher code for PSS-parameters
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11710)

commit e25761b10d48abb36a5863b087152be81ea28466
Author: Richard Levitte <levitte at openssl.org>
Date:   Tue May 5 10:29:34 2020 +0200

    EVP: Refactor the RSA-PSS key generation controls for providers
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11710)

commit 2d5536609ba92481daf42614a36bafb4e1d99293
Author: Richard Levitte <levitte at openssl.org>
Date:   Sun May 3 06:02:52 2020 +0200

    PROV & SIGNATURE: Adapt the RSA signature code for PSS-parameters
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11710)

commit 2c6094baca6476d8b024dc7d9f461dae597ae797
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed May 6 21:44:58 2020 +0200

    EVP: For SIGNATURE operations, pass the propquery early
    
    Instead of passing it with signature->digest_verify_init() and
    signature->digest_sign_init(), we pass it with signature->newctx().
    This allows the digests that are indicated by RSA PSS parameters
    to have a useful propquery.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11710)

commit ea297dca509b16190ad0a915f1324777b08ed8d8
Author: Richard Levitte <levitte at openssl.org>
Date:   Sat May 2 13:39:40 2020 +0200

    PROV & SERIALIZER: Adapt the RSA serializers for PSS-parameters
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11710)

commit 8a758e96f2865f0aee417025630626f095bb3ae3
Author: Richard Levitte <levitte at openssl.org>
Date:   Sat May 2 13:31:47 2020 +0200

    PROV & KEYMGMT: Add PSS-parameter support in the RSA KEYMGMT implementation
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11710)

commit 0ec36bf117b2c79f5663effd638410f1676c38dd
Author: Richard Levitte <levitte at openssl.org>
Date:   Thu May 7 08:51:09 2020 +0200

    PROV: Refactor the RSA SIGNATURE implementation for better param control
    
    We want to catch errors in passed parameters early, which requires
    kowledge of the ongoing operation.  Fortunately, that's possible by
    re-using the EVP_PKEY_OP macros in specific init functions.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11710)

commit 36a2a551d7dd5628a3533f7f23b1f3827f9535f7
Author: Richard Levitte <levitte at openssl.org>
Date:   Sat May 2 13:25:52 2020 +0200

    PROV: Refactor the RSA DER support
    
    We separate out the NIST arc OIDs to a separate file, so it can be
    re-used, and also the DIGEST OIDs.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11710)

commit 2275ff656c6d2043b40663686ec6627613d68318
Author: Richard Levitte <levitte at openssl.org>
Date:   Sat May 2 13:33:24 2020 +0200

    DER writer: Add the possibility to abandon empty SEQUENCEs
    
    In some cases, a SEQUENCE that contains only optional fields may end
    up empty.  In some cases, this may be represented by dropping the
    SEQUENCE entirely from the encoded DER.
    
    To do this, we detect the case where WPACKET_FLAGS_ABANDON_ON_ZERO_LENGTH
    is used, and adapt accordingly.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11710)

commit 967cc3f9390740f76f6ef3c91f2aeceab1902b19
Author: Richard Levitte <levitte at openssl.org>
Date:   Sat May 2 13:14:04 2020 +0200

    RSA: Add PSS-parameter processing in EVP_PKEY_ASN1_METHOD functions
    
    The import and export functions are affected.  We also refactor them
    to assign the RSA key type more carefully.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11710)

commit 15671090f46364a0e92456b32ead7b4714ae0b5e
Author: Richard Levitte <levitte at openssl.org>
Date:   Sat May 2 12:46:55 2020 +0200

    RSA: Add a less loaded PSS-parameter structure
    
    RSA_PSS_PARAMS carries with it a lot of baggage in form of X509_ALGOR
    and ASN1_INTEGER, which we would rather avoid in our providers.
    Therefore, we create a parallell structure - RSA_PSS_PARAMS_30 - that
    contains the same information, but uses numeric identities (*) and C
    integers (**).  This makes it simpler to handle.
    
    Note that neither this structure nor its contents are passed between
    libcrypto and the providers.  Instead, the numeric identities are
    translated to and from names, which are then passed over that
    boundary.
    
    For future considerations, we might consider dropping RSA_PSS_PARAMS
    entirely.  For now, it's still reserved for EVP_PKEY_ASN1_METHOD code,
    which RSA_PSS_PARAMS_30 is (almost entirely) reserved for use in our
    providers.
    
    (*) We use NIDs in this case, because we already have them and because
    only algorithms that libcrypto knows about are permitted in PSS
    restrictions.  We could use any number series we want, as long as we
    know for sure what they represent.
    
    (**) That's for saltlen and for trailerfield, which are never expect
    to surpass the set of numbers that fit in a regular 'int'.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11710)

commit e9d6186e0507fb814310c5230293ff62310c5f9d
Author: Richard Levitte <levitte at openssl.org>
Date:   Sat May 2 12:41:39 2020 +0200

    RSA: Add rsa_schemes.c, to store scheme data and translator functions
    
    The scheme currently added is OAEP-PSSDigestAlgorithms codified.
    The translator functions translate an EVP_MD into a NID, and a NID
    into a name, to support the creation and parsing of OSSL_PARAM items.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11710)

commit 645a541a3fdabd32cb8cbda48651f4150486189d
Author: Richard Levitte <levitte at openssl.org>
Date:   Sat May 2 13:02:29 2020 +0200

    RSA: Extract much of the rsa_pkey_export_to() code to a separate function
    
    The resulting function, rsa_todata(), is designed to be usable by
    providers as well.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11710)

commit 484d1a73c70000ad07b156f04368b3922f9910b7
Author: Richard Levitte <levitte at openssl.org>
Date:   Sat May 2 11:22:23 2020 +0200

    RSA: Add RSA key types
    
    The support of restricted RSA key types (OAEP and PSS) was lacking, or
    dependent on the presence of restriction parameters.  For example,
    this means that even though an RSA-PSS key may have been generated, it
    may appear as a plain unrestricted RSA key if parameters weren't
    present (which is the case when default restriction parameters are
    used)
    
    To make it clearer what an RSA key is intended for, and avoid
    depending in an EVP_PKEY, we introduce RSA key types.  This is done by
    reserving a section of the RSA flags (4 bits, which allows a total of
    16 different types).
    
    This isn't terribly important for EVP_PKEY_ASN1_METHOD code, as that
    has access to the wrapping EVP_PKEY.  This is very important for
    provider code, which has no access to the wrapping EVP_PKEY.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/11710)

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

Summary of changes:
 .travis.yml                                        |   4 +-
 crypto/der_writer.c                                |  37 +-
 crypto/err/openssl.txt                             |   3 +
 crypto/evp/m_sigver.c                              |   9 +-
 crypto/evp/pmeth_lib.c                             |   9 +-
 crypto/evp/signature.c                             |   3 +-
 crypto/rsa/build.info                              |   2 +-
 crypto/rsa/rsa_ameth.c                             | 203 +++++++----
 crypto/rsa/rsa_backend.c                           | 191 ++++++++++
 crypto/rsa/rsa_lib.c                               | 132 ++++---
 crypto/rsa/rsa_local.h                             |  13 +-
 crypto/rsa/rsa_pss.c                               | 136 +++++++
 crypto/rsa/rsa_schemes.c                           |  86 +++++
 include/crypto/rsa.h                               |  42 +++
 include/openssl/core_names.h                       |  11 +-
 include/openssl/core_numbers.h                     |   9 +-
 include/openssl/evp.h                              |   2 +-
 include/openssl/rsa.h                              |  30 +-
 providers/common/der/DIGESTS.asn1                  |  19 +
 providers/common/der/NIST.asn1                     |   8 +
 providers/common/der/RSA.asn1                      |   2 -
 providers/common/der/build.info                    |  11 +-
 .../common/der/{der_rsa.h.in => der_digests.c.in}  |  11 +-
 .../common/der/{der_rsa.h.in => der_digests.h.in}  |   7 +-
 providers/common/der/der_rsa.c.in                  | 402 +++++++++++++++++++--
 providers/common/der/der_rsa.h.in                  |   7 +-
 providers/common/include/prov/providercommonerr.h  |   3 +
 providers/common/provider_err.c                    |   4 +
 providers/defltprov.c                              |  13 +
 providers/fips/fipsprov.c                          |   1 +
 providers/implementations/asymciphers/rsa_enc.c    |  11 +-
 .../implementations/include/prov/implementations.h |   1 +
 providers/implementations/keymgmt/rsa_kmgmt.c      | 261 +++++++++----
 providers/implementations/serializers/build.info   |   3 +
 .../implementations/serializers/serializer_local.h |   8 +
 .../implementations/serializers/serializer_rsa.c   | 145 ++++++++
 .../serializers/serializer_rsa_priv.c              |  39 +-
 .../serializers/serializer_rsa_pub.c               |  10 +-
 providers/implementations/signature/dsa.c          |  15 +-
 providers/implementations/signature/ecdsa.c        |  12 +-
 providers/implementations/signature/eddsa.c        |   4 +-
 providers/implementations/signature/rsa.c          | 376 +++++++++++++------
 test/evp_pkey_provided_test.c                      |  13 +-
 test/recipes/15-test_rsapss.t                      |  34 +-
 test/ssl-tests/20-cert-select.cnf.in               |   5 +-
 util/libcrypto.num                                 |   3 +
 46 files changed, 1935 insertions(+), 415 deletions(-)
 create mode 100644 crypto/rsa/rsa_schemes.c
 create mode 100644 providers/common/der/DIGESTS.asn1
 create mode 100644 providers/common/der/NIST.asn1
 copy providers/common/der/{der_rsa.h.in => der_digests.c.in} (62%)
 copy providers/common/der/{der_rsa.h.in => der_digests.h.in} (67%)

diff --git a/.travis.yml b/.travis.yml
index 2bc040fe28..0ac0eb5bf9 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -31,7 +31,7 @@ compiler:
 env:
     # Note: env entry here must exactly match the value in the exclude: table below that contains env:, otherwise it will not find a match.
     - CONFIG_OPTS="" DESTDIR="_install"
-    - CONFIG_OPTS="no-asm -Werror --debug no-afalgeng no-shared enable-rc5 enable-md2 -fsanitize=address" LSAN_OPTIONS="report_objects=1"
+    - CONFIG_OPTS="no-asm --debug --strict-warnings no-afalgeng no-shared enable-rc5 enable-md2 -fsanitize=address" LSAN_OPTIONS="report_objects=1"
     - CONFIG_OPTS="no-asm no-makedepend enable-buildtest-c++ --strict-warnings -D_DEFAULT_SOURCE" BUILDONLY="yes" CHECKDOCS="yes" CPPFLAGS="-ansi"
 
 jobs:
@@ -45,7 +45,7 @@ jobs:
         - os: osx
           compiler: gcc
         - os: osx
-          env: CONFIG_OPTS="no-asm -Werror --debug no-afalgeng no-shared enable-rc5 enable-md2 -fsanitize=address" LSAN_OPTIONS="report_objects=1"
+          env: CONFIG_OPTS="no-asm --debug --strict-warnings no-afalgeng no-shared enable-rc5 enable-md2 -fsanitize=address" LSAN_OPTIONS="report_objects=1"
     include:
         - os: linux
           arch: arm64
diff --git a/crypto/der_writer.c b/crypto/der_writer.c
index 26fd88592d..8762787504 100644
--- a/crypto/der_writer.c
+++ b/crypto/der_writer.c
@@ -24,12 +24,24 @@ static int int_start_context(WPACKET *pkt, int tag)
 
 static int int_end_context(WPACKET *pkt, int tag)
 {
+    /*
+     * If someone set the flag WPACKET_FLAGS_ABANDON_ON_ZERO_LENGTH on this
+     * sub-packet and this sub-packet has nothing written to it, the DER length
+     * will not be written, and the total written size will be unchanged before
+     * and after WPACKET_close().  We use size1 and size2 to determine if
+     * anything was written, and only write our tag if it has.
+     *
+     */
+    size_t size1, size2;
+
     if (tag < 0)
         return 1;
     if (!ossl_assert(tag <= 30))
         return 0;
-    return WPACKET_close(pkt)
-        && WPACKET_put_bytes_u8(pkt, DER_C_CONTEXT | tag);
+    return WPACKET_get_total_written(pkt, &size1)
+        && WPACKET_close(pkt)
+        && WPACKET_get_total_written(pkt, &size2)
+        && (size1 == size2 || WPACKET_put_bytes_u8(pkt, DER_C_CONTEXT | tag));
 }
 
 int DER_w_precompiled(WPACKET *pkt, int tag,
@@ -136,7 +148,24 @@ int DER_w_begin_sequence(WPACKET *pkt, int tag)
 
 int DER_w_end_sequence(WPACKET *pkt, int tag)
 {
-    return WPACKET_close(pkt)
-        && WPACKET_put_bytes_u8(pkt, DER_F_CONSTRUCTED | DER_P_SEQUENCE)
+    /*
+     * If someone set the flag WPACKET_FLAGS_ABANDON_ON_ZERO_LENGTH on this
+     * sub-packet and this sub-packet has nothing written to it, the DER length
+     * will not be written, and the total written size will be unchanged before
+     * and after WPACKET_close().  We use size1 and size2 to determine if
+     * anything was written, and only write our tag if it has.
+     *
+     * Because we know that int_end_context() needs to do the same check,
+     * we reproduce this flag if the written length was unchanged, or we will
+     * have an erroneous context tag.
+     */
+    size_t size1, size2;
+
+    return WPACKET_get_total_written(pkt, &size1)
+        && WPACKET_close(pkt)
+        && WPACKET_get_total_written(pkt, &size2)
+        && (size1 == size2
+            ? WPACKET_set_flags(pkt, WPACKET_FLAGS_ABANDON_ON_ZERO_LENGTH)
+            : WPACKET_put_bytes_u8(pkt, DER_F_CONSTRUCTED | DER_P_SEQUENCE))
         && int_end_context(pkt, tag);
 }
diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt
index 9fa051f5c3..7bf0611ec4 100644
--- a/crypto/err/openssl.txt
+++ b/crypto/err/openssl.txt
@@ -2842,6 +2842,7 @@ PROV_R_INVALID_PADDING_MODE:168:invalid padding mode
 PROV_R_INVALID_PSS_SALTLEN:169:invalid pss saltlen
 PROV_R_INVALID_SALT_LENGTH:112:invalid salt length
 PROV_R_INVALID_SEED_LENGTH:154:invalid seed length
+PROV_R_INVALID_SIGNATURE_SIZE:179:invalid signature size
 PROV_R_INVALID_TAG:110:invalid tag
 PROV_R_INVALID_TAGLEN:118:invalid taglen
 PROV_R_INVALID_X931_DIGEST:170:invalid x931 digest
@@ -2863,6 +2864,8 @@ PROV_R_NOT_SUPPORTED:136:not supported
 PROV_R_NOT_XOF_OR_INVALID_LENGTH:113:not xof or invalid length
 PROV_R_NO_KEY_SET:114:no key set
 PROV_R_NO_PARAMETERS_SET:177:no parameters set
+PROV_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE:178:\
+	operation not supported for this keytype
 PROV_R_OUTPUT_BUFFER_TOO_SMALL:106:output buffer too small
 PROV_R_PSS_SALTLEN_TOO_SMALL:172:pss saltlen too small
 PROV_R_READ_KEY:159:read key
diff --git a/crypto/evp/m_sigver.c b/crypto/evp/m_sigver.c
index c77683a69d..44e7cab1af 100644
--- a/crypto/evp/m_sigver.c
+++ b/crypto/evp/m_sigver.c
@@ -71,6 +71,9 @@ static int do_sigver_init(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
     locpctx = ctx->pctx;
     evp_pkey_ctx_free_old_ops(locpctx);
 
+    if (props == NULL)
+        props = locpctx->propquery;
+
     /*
      * TODO when we stop falling back to legacy, this and the ERR_pop_to_mark()
      * calls can be removed.
@@ -142,7 +145,7 @@ static int do_sigver_init(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
     locpctx->operation = ver ? EVP_PKEY_OP_VERIFYCTX
                              : EVP_PKEY_OP_SIGNCTX;
     locpctx->op.sig.sigprovctx
-        = signature->newctx(ossl_provider_ctx(signature->prov));
+        = signature->newctx(ossl_provider_ctx(signature->prov), props);
     if (locpctx->op.sig.sigprovctx == NULL) {
         ERR_raise(ERR_LIB_EVP,  EVP_R_INITIALIZATION_ERROR);
         goto err;
@@ -182,14 +185,14 @@ static int do_sigver_init(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
             goto err;
         }
         ret = signature->digest_verify_init(locpctx->op.sig.sigprovctx,
-                                            mdname, props, provkey);
+                                            mdname, provkey);
     } else {
         if (signature->digest_sign_init == NULL) {
             ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
             goto err;
         }
         ret = signature->digest_sign_init(locpctx->op.sig.sigprovctx,
-                                          mdname, props, provkey);
+                                          mdname, provkey);
     }
 
     return ret ? 1 : 0;
diff --git a/crypto/evp/pmeth_lib.c b/crypto/evp/pmeth_lib.c
index 3476d83ea6..eca5178129 100644
--- a/crypto/evp/pmeth_lib.c
+++ b/crypto/evp/pmeth_lib.c
@@ -1017,6 +1017,12 @@ static int legacy_ctrl_str_to_param(EVP_PKEY_CTX *ctx, const char *name,
         name = OSSL_PKEY_PARAM_RSA_E;
     else if (strcmp(name, "rsa_keygen_primes") == 0)
         name = OSSL_PKEY_PARAM_RSA_PRIMES;
+    else if (strcmp(name, "rsa_pss_keygen_md") == 0)
+        name = OSSL_PKEY_PARAM_RSA_DIGEST;
+    else if (strcmp(name, "rsa_pss_keygen_mgf1_md") == 0)
+        name = OSSL_PKEY_PARAM_RSA_MGF1_DIGEST;
+    else if (strcmp(name, "rsa_pss_keygen_saltlen") == 0)
+        name = OSSL_PKEY_PARAM_RSA_PSS_SALTLEN;
 # ifndef OPENSSL_NO_DSA
     else if (strcmp(name, "dsa_paramgen_bits") == 0)
         name = OSSL_PKEY_PARAM_FFC_PBITS;
@@ -1066,7 +1072,8 @@ static int legacy_ctrl_str_to_param(EVP_PKEY_CTX *ctx, const char *name,
         if (!OSSL_PARAM_allocate_from_text(&params[0], settable, name, value,
                                            strlen(value), &exists)) {
             if (!exists) {
-                ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+                ERR_raise_data(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED,
+                               "name=%s,value=%s", name, value);
                 return -2;
             }
             return 0;
diff --git a/crypto/evp/signature.c b/crypto/evp/signature.c
index b7a7f79606..595a93e66e 100644
--- a/crypto/evp/signature.c
+++ b/crypto/evp/signature.c
@@ -417,7 +417,8 @@ static int evp_pkey_signature_init(EVP_PKEY_CTX *ctx, int operation)
     /* No more legacy from here down to legacy: */
 
     ctx->op.sig.signature = signature;
-    ctx->op.sig.sigprovctx = signature->newctx(ossl_provider_ctx(signature->prov));
+    ctx->op.sig.sigprovctx =
+        signature->newctx(ossl_provider_ctx(signature->prov), ctx->propquery);
     if (ctx->op.sig.sigprovctx == NULL) {
         /* The provider key can stay in the cache */
         EVPerr(0, EVP_R_INITIALIZATION_ERROR);
diff --git a/crypto/rsa/build.info b/crypto/rsa/build.info
index 970c493560..984ad775d5 100644
--- a/crypto/rsa/build.info
+++ b/crypto/rsa/build.info
@@ -3,7 +3,7 @@ LIBS=../../libcrypto
 $COMMON=rsa_ossl.c rsa_gen.c rsa_lib.c rsa_sign.c rsa_pk1.c \
         rsa_none.c rsa_oaep.c rsa_chk.c rsa_pss.c rsa_x931.c rsa_crpt.c \
         rsa_x931g.c rsa_sp800_56b_gen.c rsa_sp800_56b_check.c rsa_backend.c \
-        rsa_mp_names.c
+        rsa_mp_names.c rsa_schemes.c
 
 SOURCE[../../libcrypto]=$COMMON\
         rsa_saos.c rsa_err.c rsa_asn1.c rsa_ameth.c rsa_prn.c \
diff --git a/crypto/rsa/rsa_ameth.c b/crypto/rsa/rsa_ameth.c
index 27aa9f422d..e9eddde68e 100644
--- a/crypto/rsa/rsa_ameth.c
+++ b/crypto/rsa/rsa_ameth.c
@@ -43,7 +43,7 @@ static int rsa_param_encode(const EVP_PKEY *pkey,
 
     *pstr = NULL;
     /* If RSA it's just NULL type */
-    if (pkey->ameth->pkey_id != EVP_PKEY_RSA_PSS) {
+    if (RSA_test_flags(rsa, RSA_FLAG_TYPE_MASK) != RSA_FLAG_TYPE_RSASSAPSS) {
         *pstrtype = V_ASN1_NULL;
         return 1;
     }
@@ -196,6 +196,20 @@ static int rsa_priv_decode(EVP_PKEY *pkey, const PKCS8_PRIV_KEY_INFO *p8)
         RSA_free(rsa);
         return 0;
     }
+
+    RSA_clear_flags(rsa, RSA_FLAG_TYPE_MASK);
+    switch (pkey->ameth->pkey_id) {
+    case EVP_PKEY_RSA:
+        RSA_set_flags(rsa, RSA_FLAG_TYPE_RSA);
+        break;
+    case EVP_PKEY_RSA_PSS:
+        RSA_set_flags(rsa, RSA_FLAG_TYPE_RSASSAPSS);
+        break;
+    default:
+        /* Leave the type bits zero */
+        break;
+    }
+
     EVP_PKEY_assign(pkey, pkey->ameth->pkey_id, rsa);
     return 1;
 }
@@ -1078,18 +1092,23 @@ static size_t rsa_pkey_dirty_cnt(const EVP_PKEY *pkey)
     return pkey->pkey.rsa->dirty_cnt;
 }
 
-DEFINE_SPECIAL_STACK_OF_CONST(BIGNUM_const, BIGNUM)
-
-static int rsa_pkey_export_to(const EVP_PKEY *from, void *to_keydata,
-                              EVP_KEYMGMT *to_keymgmt, OPENSSL_CTX *libctx,
-                              const char *propq)
+/*
+ * For the moment, we trust the call path, where keys going through
+ * rsa_pkey_export_to() match a KEYMGMT for the "RSA" keytype, while
+ * keys going through rsa_pss_pkey_export_to() match a KEYMGMT for the
+ * "RSA-PSS" keytype.
+ * TODO(3.0) Investigate whether we should simply continue to trust the
+ * call path, or if we should strengthen this function by checking that
+ * |rsa_type| matches the RSA key subtype.  The latter requires ensuring
+ * that the type flag for the RSA key is properly set by other functions
+ * in this file.
+ */
+static int rsa_int_export_to(const EVP_PKEY *from, int rsa_type,
+                             void *to_keydata, EVP_KEYMGMT *to_keymgmt,
+                             OPENSSL_CTX *libctx, const char *propq)
 {
     RSA *rsa = from->pkey.rsa;
     OSSL_PARAM_BLD *tmpl = OSSL_PARAM_BLD_new();
-    const BIGNUM *n = RSA_get0_n(rsa), *e = RSA_get0_e(rsa);
-    const BIGNUM *d = RSA_get0_d(rsa);
-    STACK_OF(BIGNUM_const) *primes = NULL, *exps = NULL, *coeffs = NULL;
-    int numprimes = 0, numexps = 0, numcoeffs = 0;
     OSSL_PARAM *params = NULL;
     int selection = 0;
     int rv = 0;
@@ -1104,65 +1123,32 @@ static int rsa_pkey_export_to(const EVP_PKEY *from, void *to_keydata,
         goto err;
 
     /* Public parameters must always be present */
-    if (n == NULL || e == NULL)
+    if (RSA_get0_n(rsa) == NULL || RSA_get0_e(rsa) == NULL)
         goto err;
 
-    /* |e| and |n| are always present */
-    if (!OSSL_PARAM_BLD_push_BN(tmpl, OSSL_PKEY_PARAM_RSA_E, e))
-        goto err;
-    if (!OSSL_PARAM_BLD_push_BN(tmpl, OSSL_PKEY_PARAM_RSA_N, n))
+    if (!rsa_todata(rsa, tmpl, NULL))
         goto err;
-    selection |= OSSL_KEYMGMT_SELECT_PUBLIC_KEY;
-
-    if (d != NULL) {
-        int i;
-
-        /* Get all the primes and CRT params */
-        if ((primes = sk_BIGNUM_const_new_null()) == NULL
-            || (exps = sk_BIGNUM_const_new_null()) == NULL
-            || (coeffs = sk_BIGNUM_const_new_null()) == NULL)
-            goto err;
 
-        if (!rsa_get0_all_params(rsa, primes, exps, coeffs))
-            goto err;
+    selection |= OSSL_KEYMGMT_SELECT_PUBLIC_KEY;
+    if (RSA_get0_d(rsa) != NULL)
+        selection |= OSSL_KEYMGMT_SELECT_PRIVATE_KEY;
 
-        numprimes = sk_BIGNUM_const_num(primes);
-        numexps = sk_BIGNUM_const_num(exps);
-        numcoeffs = sk_BIGNUM_const_num(coeffs);
+    if (rsa->pss != NULL) {
+        const EVP_MD *md = NULL, *mgf1md = NULL;
+        int md_nid, mgf1md_nid, saltlen;
+        RSA_PSS_PARAMS_30 pss_params;
 
-        /*
-         * It's permisssible to have zero primes, i.e. no CRT params.
-         * Otherwise, there must be at least two, as many exponents,
-         * and one coefficient less.
-         */
-        if (numprimes != 0
-            && (numprimes < 2 || numexps < 2 || numcoeffs < 1))
+        if (!rsa_pss_get_param(rsa->pss, &md, &mgf1md, &saltlen))
             goto err;
-
-        if (!OSSL_PARAM_BLD_push_BN(tmpl, OSSL_PKEY_PARAM_RSA_D, d))
+        md_nid = EVP_MD_type(md);
+        mgf1md_nid = EVP_MD_type(mgf1md);
+        if (!rsa_pss_params_30_set_defaults(&pss_params)
+            || !rsa_pss_params_30_set_hashalg(&pss_params, md_nid)
+            || !rsa_pss_params_30_set_maskgenhashalg(&pss_params, mgf1md_nid)
+            || !rsa_pss_params_30_set_saltlen(&pss_params, saltlen)
+            || !rsa_pss_params_30_todata(&pss_params, propq, tmpl, NULL))
             goto err;
-        selection |= OSSL_KEYMGMT_SELECT_PRIVATE_KEY;
-
-        for (i = 0; i < numprimes  && rsa_mp_factor_names[i] != NULL; i++) {
-            const BIGNUM *num = sk_BIGNUM_const_value(primes, i);
-
-            if (!OSSL_PARAM_BLD_push_BN(tmpl, rsa_mp_factor_names[i], num))
-                goto err;
-        }
-
-        for (i = 0; i < numexps && rsa_mp_exp_names[i] != NULL; i++) {
-            const BIGNUM *num = sk_BIGNUM_const_value(exps, i);
-
-            if (!OSSL_PARAM_BLD_push_BN(tmpl, rsa_mp_exp_names[i], num))
-                goto err;
-        }
-
-        for (i = 0; i < numcoeffs && rsa_mp_coeff_names[i] != NULL; i++) {
-            const BIGNUM *num = sk_BIGNUM_const_value(coeffs, i);
-
-            if (!OSSL_PARAM_BLD_push_BN(tmpl, rsa_mp_coeff_names[i], num))
-                goto err;
-        }
+        selection |= OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS;
     }
 
     if ((params = OSSL_PARAM_BLD_to_param(tmpl)) == NULL)
@@ -1172,31 +1158,104 @@ static int rsa_pkey_export_to(const EVP_PKEY *from, void *to_keydata,
     rv = evp_keymgmt_import(to_keymgmt, to_keydata, selection, params);
 
  err:
-    sk_BIGNUM_const_free(primes);
-    sk_BIGNUM_const_free(exps);
-    sk_BIGNUM_const_free(coeffs);
     OSSL_PARAM_BLD_free_params(params);
     OSSL_PARAM_BLD_free(tmpl);
     return rv;
 }
 
-static int rsa_pkey_import_from(const OSSL_PARAM params[], void *vpctx)
+static int rsa_int_import_from(const OSSL_PARAM params[], void *vpctx,
+                               int rsa_type)
 {
     EVP_PKEY_CTX *pctx = vpctx;
     EVP_PKEY *pkey = EVP_PKEY_CTX_get0_pkey(pctx);
     RSA *rsa = rsa_new_with_ctx(pctx->libctx);
+    RSA_PSS_PARAMS_30 rsa_pss_params = { 0, };
+    int ok = 0;
 
     if (rsa == NULL) {
         ERR_raise(ERR_LIB_DH, ERR_R_MALLOC_FAILURE);
         return 0;
     }
 
-    if (!rsa_fromdata(rsa, params)
-        || !EVP_PKEY_assign_RSA(pkey, rsa)) {
-        RSA_free(rsa);
-        return 0;
+    RSA_clear_flags(rsa, RSA_FLAG_TYPE_MASK);
+    RSA_set_flags(rsa, rsa_type);
+
+    if (!rsa_pss_params_30_fromdata(&rsa_pss_params, params, pctx->libctx))
+        goto err;
+
+    switch (rsa_type) {
+    case RSA_FLAG_TYPE_RSA:
+        /*
+         * Were PSS parameters filled in?
+         * In that case, something's wrong
+         */
+        if (!rsa_pss_params_30_is_unrestricted(&rsa_pss_params))
+            goto err;
+        break;
+    case RSA_FLAG_TYPE_RSASSAPSS:
+        /*
+         * Were PSS parameters filled in?  In that case, create the old
+         * RSA_PSS_PARAMS structure.  Otherwise, this is an unrestricted key.
+         */
+        if (!rsa_pss_params_30_is_unrestricted(&rsa_pss_params)) {
+            /* Create the older RSA_PSS_PARAMS from RSA_PSS_PARAMS_30 data */
+            int mdnid = rsa_pss_params_30_hashalg(&rsa_pss_params);
+            int mgf1mdnid = rsa_pss_params_30_maskgenhashalg(&rsa_pss_params);
+            int saltlen = rsa_pss_params_30_saltlen(&rsa_pss_params);
+            const EVP_MD *md = EVP_get_digestbynid(mdnid);
+            const EVP_MD *mgf1md = EVP_get_digestbynid(mgf1mdnid);
+
+            if ((rsa->pss = rsa_pss_params_create(md, mgf1md, saltlen)) == NULL)
+                goto err;
+        }
+        break;
+    default:
+        /* RSA key sub-types we don't know how to handle yet */
+        goto err;
     }
-    return 1;
+
+    if (!rsa_fromdata(rsa, params))
+        goto err;
+
+    switch (rsa_type) {
+    case RSA_FLAG_TYPE_RSA:
+        ok = EVP_PKEY_assign_RSA(pkey, rsa);
+        break;
+    case RSA_FLAG_TYPE_RSASSAPSS:
+        ok = EVP_PKEY_assign(pkey, EVP_PKEY_RSA_PSS, rsa);
+        break;
+    }
+
+ err:
+    if (!ok)
+        RSA_free(rsa);
+    return ok;
+}
+
+static int rsa_pkey_export_to(const EVP_PKEY *from, void *to_keydata,
+                              EVP_KEYMGMT *to_keymgmt, OPENSSL_CTX *libctx,
+                              const char *propq)
+{
+    return rsa_int_export_to(from, RSA_FLAG_TYPE_RSA, to_keydata,
+                             to_keymgmt, libctx, propq);
+}
+
+static int rsa_pss_pkey_export_to(const EVP_PKEY *from, void *to_keydata,
+                                  EVP_KEYMGMT *to_keymgmt, OPENSSL_CTX *libctx,
+                                  const char *propq)
+{
+    return rsa_int_export_to(from, RSA_FLAG_TYPE_RSASSAPSS, to_keydata,
+                             to_keymgmt, libctx, propq);
+}
+
+static int rsa_pkey_import_from(const OSSL_PARAM params[], void *vpctx)
+{
+    return rsa_int_import_from(params, vpctx, RSA_FLAG_TYPE_RSA);
+}
+
+static int rsa_pss_pkey_import_from(const OSSL_PARAM params[], void *vpctx)
+{
+    return rsa_int_import_from(params, vpctx, RSA_FLAG_TYPE_RSASSAPSS);
 }
 
 const EVP_PKEY_ASN1_METHOD rsa_asn1_meths[2] = {
@@ -1283,6 +1342,6 @@ const EVP_PKEY_ASN1_METHOD rsa_pss_asn1_meth = {
      0, 0, 0, 0,
 
      rsa_pkey_dirty_cnt,
-     rsa_pkey_export_to,
-     rsa_pkey_import_from
+     rsa_pss_pkey_export_to,
+     rsa_pss_pkey_import_from
 };
diff --git a/crypto/rsa/rsa_backend.c b/crypto/rsa/rsa_backend.c
index 57a539c051..7497a8579c 100644
--- a/crypto/rsa/rsa_backend.c
+++ b/crypto/rsa/rsa_backend.c
@@ -7,10 +7,16 @@
  * https://www.openssl.org/source/license.html
  */
 
+#include <string.h>
 #include <openssl/core_names.h>
 #include <openssl/params.h>
+#include <openssl/evp.h>
+#include "internal/sizes.h"
+#include "internal/param_build_set.h"
 #include "crypto/rsa.h"
 
+#include "e_os.h"                /* strcasecmp for Windows() */
+
 /*
  * The intention with the "backend" source file is to offer backend support
  * for legacy backends (EVP_PKEY_ASN1_METHOD and EVP_PKEY_METHOD) and provider
@@ -97,3 +103,188 @@ int rsa_fromdata(RSA *rsa, const OSSL_PARAM params[])
     return 0;
 }
 
+DEFINE_SPECIAL_STACK_OF_CONST(BIGNUM_const, BIGNUM)
+
+int rsa_todata(RSA *rsa, OSSL_PARAM_BLD *bld, OSSL_PARAM params[])
+{
+    int ret = 0;
+    const BIGNUM *rsa_d = NULL, *rsa_n = NULL, *rsa_e = NULL;
+    STACK_OF(BIGNUM_const) *factors = sk_BIGNUM_const_new_null();
+    STACK_OF(BIGNUM_const) *exps = sk_BIGNUM_const_new_null();
+    STACK_OF(BIGNUM_const) *coeffs = sk_BIGNUM_const_new_null();
+
+    if (rsa == NULL || factors == NULL || exps == NULL || coeffs == NULL)
+        goto err;
+
+    RSA_get0_key(rsa, &rsa_n, &rsa_e, &rsa_d);
+    rsa_get0_all_params(rsa, factors, exps, coeffs);
+
+    /* Check private key data integrity */
+    if (rsa_d != NULL) {
+        int numprimes = sk_BIGNUM_const_num(factors);
+        int numexps = sk_BIGNUM_const_num(exps);
+        int numcoeffs = sk_BIGNUM_const_num(coeffs);
+
+        /*
+         * It's permisssible to have zero primes, i.e. no CRT params.
+         * Otherwise, there must be at least two, as many exponents,
+         * and one coefficient less.
+         */
+        if (numprimes != 0
+            && (numprimes < 2 || numexps < 2 || numcoeffs < 1))
+            goto err;
+    }
+
+    if (!ossl_param_build_set_bn(bld, params, OSSL_PKEY_PARAM_RSA_N, rsa_n)
+        || !ossl_param_build_set_bn(bld, params, OSSL_PKEY_PARAM_RSA_E, rsa_e)
+        || !ossl_param_build_set_bn(bld, params, OSSL_PKEY_PARAM_RSA_D, rsa_d)
+        || !ossl_param_build_set_multi_key_bn(bld, params, rsa_mp_factor_names,
+                                              factors)
+        || !ossl_param_build_set_multi_key_bn(bld, params, rsa_mp_exp_names,
+                                              exps)
+        || !ossl_param_build_set_multi_key_bn(bld, params, rsa_mp_coeff_names,
+                                              coeffs))
+        goto err;
+    ret = 1;
+ err:
+    sk_BIGNUM_const_free(factors);
+    sk_BIGNUM_const_free(exps);
+    sk_BIGNUM_const_free(coeffs);
+    return ret;
+}
+
+int rsa_pss_params_30_todata(const RSA_PSS_PARAMS_30 *pss, const char *propq,
+                             OSSL_PARAM_BLD *bld, OSSL_PARAM params[])
+{
+    if (!rsa_pss_params_30_is_unrestricted(pss)) {
+        int hashalg_nid = rsa_pss_params_30_hashalg(pss);
+        int maskgenalg_nid = rsa_pss_params_30_maskgenalg(pss);
+        int maskgenhashalg_nid = rsa_pss_params_30_maskgenhashalg(pss);
+        int saltlen = rsa_pss_params_30_saltlen(pss);
+        int default_hashalg_nid = rsa_pss_params_30_hashalg(NULL);
+        int default_maskgenalg_nid = rsa_pss_params_30_maskgenalg(NULL);
+        int default_maskgenhashalg_nid = rsa_pss_params_30_maskgenhashalg(NULL);
+        const char *mdname =
+            (hashalg_nid == default_hashalg_nid
+             ? NULL : rsa_oaeppss_nid2name(hashalg_nid));
+        const char *mgfname =
+            (maskgenalg_nid == default_maskgenalg_nid
+             ? NULL : rsa_oaeppss_nid2name(maskgenalg_nid));
+        const char *mgf1mdname =
+            (maskgenhashalg_nid == default_maskgenhashalg_nid
+             ? NULL : rsa_oaeppss_nid2name(maskgenhashalg_nid));
+        const char *key_md = OSSL_PKEY_PARAM_RSA_DIGEST;
+        const char *key_mgf = OSSL_PKEY_PARAM_RSA_MASKGENFUNC;
+        const char *key_mgf1_md = OSSL_PKEY_PARAM_RSA_MGF1_DIGEST;
+        const char *key_saltlen = OSSL_PKEY_PARAM_RSA_PSS_SALTLEN;
+
+        /*
+         * To ensure that the key isn't seen as unrestricted by the recipient,
+         * we make sure that at least one PSS-related parameter is passed, even
+         * if it has a default value; saltlen.
+         */
+        if ((mdname != NULL
+             && !ossl_param_build_set_utf8_string(bld, params, key_md, mdname))
+            || (mgfname != NULL
+                && !ossl_param_build_set_utf8_string(bld, params,
+                                                     key_mgf, mgfname))
+            || (mgf1mdname != NULL
+                && !ossl_param_build_set_utf8_string(bld, params,
+                                                     key_mgf1_md, mgf1mdname))
+            || (!ossl_param_build_set_int(bld, params, key_saltlen, saltlen)))
+            return 0;
+    }
+    return 1;
+}
+
+int rsa_pss_params_30_fromdata(RSA_PSS_PARAMS_30 *pss_params,
+                               const OSSL_PARAM params[], OPENSSL_CTX *libctx)
+{
+    const OSSL_PARAM *param_md, *param_mgf, *param_mgf1md,  *param_saltlen;
+    EVP_MD *md = NULL, *mgf1md = NULL;
+    int saltlen;
+    int ret = 0;
+
+    if (pss_params == NULL)
+        return 0;
+
+    param_md =
+        OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_DIGEST);
+    param_mgf =
+        OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_MASKGENFUNC);
+    param_mgf1md =
+        OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_MGF1_DIGEST);
+    param_saltlen =
+        OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_PSS_SALTLEN);
+
+    /*
+     * If we get any of the parameters, we know we have at least some
+     * restrictions, so we start by setting default values, and let each
+     * parameter override their specific restriction data.
+     */
+    if (param_md != NULL || param_mgf != NULL || param_mgf1md != NULL
+        || param_saltlen != NULL)
+        if (!rsa_pss_params_30_set_defaults(pss_params))
+            return 0;
+
+    if (param_mgf != NULL) {
+        int default_maskgenalg_nid = rsa_pss_params_30_maskgenalg(NULL);
+        const char *mgfname = NULL;
+
+        if (param_mgf->data_type == OSSL_PARAM_UTF8_STRING)
+            mgfname = param_mgf->data;
+        else if (!OSSL_PARAM_get_utf8_ptr(param_mgf, &mgfname))
+            return 0;
+
+        /* TODO Revisit this if / when a new MGF algorithm appears */
+        if (strcasecmp(param_mgf->data,
+                       rsa_mgf_nid2name(default_maskgenalg_nid)) != 0)
+            return 0;
+    }
+
+    /*
+     * We're only interested in the NIDs that correspond to the MDs, so the
+     * exact propquery is unimportant in the EVP_MD_fetch() calls below.
+     */
+
+    if (param_md != NULL) {
+        const char *mdname = NULL;
+
+        if (param_md->data_type == OSSL_PARAM_UTF8_STRING)
+            mdname = param_md->data;
+        else if (!OSSL_PARAM_get_utf8_ptr(param_mgf, &mdname))
+            goto err;
+
+        if ((md = EVP_MD_fetch(libctx, mdname, NULL)) == NULL
+            || !rsa_pss_params_30_set_hashalg(pss_params,
+                                              rsa_oaeppss_md2nid(md)))
+            goto err;
+    }
+
+    if (param_mgf1md != NULL) {
+        const char *mgf1mdname = NULL;
+
+        if (param_mgf1md->data_type == OSSL_PARAM_UTF8_STRING)
+            mgf1mdname = param_mgf1md->data;
+        else if (!OSSL_PARAM_get_utf8_ptr(param_mgf, &mgf1mdname))
+            goto err;
+
+        if ((mgf1md = EVP_MD_fetch(libctx, mgf1mdname, NULL)) == NULL
+            || !rsa_pss_params_30_set_maskgenhashalg(pss_params,
+                                                     rsa_oaeppss_md2nid(mgf1md)))
+            goto err;
+    }
+
+    if (param_saltlen != NULL) {
+        if (!OSSL_PARAM_get_int(param_saltlen, &saltlen)
+            || !rsa_pss_params_30_set_saltlen(pss_params, saltlen))
+            goto err;
+    }
+
+    ret = 1;
+
+ err:
+    EVP_MD_free(md);
+    EVP_MD_free(mgf1md);
+    return ret;
+}
diff --git a/crypto/rsa/rsa_lib.c b/crypto/rsa/rsa_lib.c
index 81daec4b34..e80416ed3f 100644
--- a/crypto/rsa/rsa_lib.c
+++ b/crypto/rsa/rsa_lib.c
@@ -162,7 +162,6 @@ void RSA_free(RSA *r)
     BN_clear_free(r->dmp1);
     BN_clear_free(r->dmq1);
     BN_clear_free(r->iqmp);
-    /* TODO(3.0): Support PSS in FIPS_MODULE */
 #ifndef FIPS_MODULE
     RSA_PSS_PARAMS_free(r->pss);
     sk_RSA_PRIME_INFO_pop_free(r->prime_infos, rsa_multip_info_free);
@@ -185,6 +184,11 @@ int RSA_up_ref(RSA *r)
     return i > 1 ? 1 : 0;
 }
 
+OPENSSL_CTX *rsa_get0_libctx(RSA *r)
+{
+    return r->libctx;
+}
+
 #ifndef FIPS_MODULE
 int RSA_set_ex_data(RSA *r, int idx, void *arg)
 {
@@ -637,7 +641,17 @@ const BIGNUM *RSA_get0_iqmp(const RSA *r)
 
 const RSA_PSS_PARAMS *RSA_get0_pss_params(const RSA *r)
 {
+#ifdef FIPS_MODULE
+    return NULL;
+#else
     return r->pss;
+#endif
+}
+
+/* Internal */
+RSA_PSS_PARAMS_30 *rsa_get0_pss_params_30(RSA *r)
+{
+    return &r->pss_params;
 }
 
 void RSA_clear_flags(RSA *r, int flags)
@@ -992,13 +1006,16 @@ int EVP_PKEY_CTX_get_rsa_oaep_md(EVP_PKEY_CTX *ctx, const EVP_MD **md)
     return 1;
 }
 
-int EVP_PKEY_CTX_set_rsa_mgf1_md(EVP_PKEY_CTX *ctx, const EVP_MD *md)
+static int int_set_rsa_mgf1_md(EVP_PKEY_CTX *ctx,
+                               /* For EVP_PKEY_CTX_ctrl() */
+                               int keytype, int optype, int cmd,
+                               const EVP_MD *md,
+                               /* For EVP_PKEY_CTX_set_params() */
+                               const char *mdname, const char *mdprops)
 {
-    const char *name;
+    OSSL_PARAM rsa_params[3], *p = rsa_params;
 
-    if (ctx == NULL
-            || (!EVP_PKEY_CTX_IS_ASYM_CIPHER_OP(ctx)
-                && !EVP_PKEY_CTX_IS_SIGNATURE_OP(ctx))) {
+    if (ctx == NULL || (ctx->operation & optype) == 0) {
         ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
         /* Uses the same return values as EVP_PKEY_CTX_ctrl */
         return -2;
@@ -1006,43 +1023,25 @@ int EVP_PKEY_CTX_set_rsa_mgf1_md(EVP_PKEY_CTX *ctx, const EVP_MD *md)
 
     /* If key type not RSA return error */
     if (ctx->pmeth != NULL
-            && ctx->pmeth->pkey_id != EVP_PKEY_RSA
-            && ctx->pmeth->pkey_id != EVP_PKEY_RSA_PSS)
+        && (keytype == -1
+            ? (ctx->pmeth->pkey_id != EVP_PKEY_RSA
+               && ctx->pmeth->pkey_id != EVP_PKEY_RSA_PSS)
+            : ctx->pmeth->pkey_id != keytype))
         return -1;
 
     /* TODO(3.0): Remove this eventually when no more legacy */
-    if ((EVP_PKEY_CTX_IS_ASYM_CIPHER_OP(ctx)
-                && ctx->op.ciph.ciphprovctx == NULL)
+    if (cmd != -1) {
+        if ((EVP_PKEY_CTX_IS_ASYM_CIPHER_OP(ctx)
+             && ctx->op.ciph.ciphprovctx == NULL)
             || (EVP_PKEY_CTX_IS_SIGNATURE_OP(ctx)
-                && ctx->op.sig.sigprovctx == NULL))
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA,
-                                 EVP_PKEY_OP_TYPE_SIG | EVP_PKEY_OP_TYPE_CRYPT,
-                                 EVP_PKEY_CTRL_RSA_MGF1_MD, 0, (void *)md);
-
-    name = (md == NULL) ? "" : EVP_MD_name(md);
+                && ctx->op.sig.sigprovctx == NULL)
+            || (EVP_PKEY_CTX_IS_GEN_OP(ctx)
+                && ctx->op.keymgmt.genctx == NULL))
+            return EVP_PKEY_CTX_ctrl(ctx, keytype, optype, cmd, 0, (void *)md);
 
-    return EVP_PKEY_CTX_set_rsa_mgf1_md_name(ctx, name, NULL);
-}
-
-int EVP_PKEY_CTX_set_rsa_mgf1_md_name(EVP_PKEY_CTX *ctx, const char *mdname,
-                                      const char *mdprops)
-{
-    OSSL_PARAM rsa_params[3], *p = rsa_params;
-
-    if (ctx == NULL
-            || mdname == NULL
-            || (!EVP_PKEY_CTX_IS_ASYM_CIPHER_OP(ctx)
-                && !EVP_PKEY_CTX_IS_SIGNATURE_OP(ctx))) {
-        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
+        mdname = (md == NULL) ? "" : EVP_MD_name(md);
     }
 
-    /* If key type not RSA return error */
-    if (ctx->pmeth != NULL
-            && ctx->pmeth->pkey_id != EVP_PKEY_RSA
-            && ctx->pmeth->pkey_id != EVP_PKEY_RSA_PSS)
-        return -1;
 
     *p++ = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_MGF1_DIGEST,
                                             /*
@@ -1064,6 +1063,36 @@ int EVP_PKEY_CTX_set_rsa_mgf1_md_name(EVP_PKEY_CTX *ctx, const char *mdname,
     return EVP_PKEY_CTX_set_params(ctx, rsa_params);
 }
 
+int EVP_PKEY_CTX_set_rsa_mgf1_md(EVP_PKEY_CTX *ctx, const EVP_MD *md)
+{
+    return int_set_rsa_mgf1_md(ctx, -1,
+                               EVP_PKEY_OP_TYPE_CRYPT | EVP_PKEY_OP_TYPE_SIG,
+                               EVP_PKEY_CTRL_RSA_MGF1_MD, md, NULL, NULL);
+}
+
+int EVP_PKEY_CTX_set_rsa_mgf1_md_name(EVP_PKEY_CTX *ctx, const char *mdname,
+                                      const char *mdprops)
+{
+    return int_set_rsa_mgf1_md(ctx, -1,
+                               EVP_PKEY_OP_TYPE_CRYPT | EVP_PKEY_OP_TYPE_SIG,
+                               -1, NULL, mdname, mdprops);
+}
+
+int EVP_PKEY_CTX_set_rsa_pss_keygen_mgf1_md(EVP_PKEY_CTX *ctx, const EVP_MD *md)
+{
+    return int_set_rsa_mgf1_md(ctx, EVP_PKEY_RSA_PSS,
+                               EVP_PKEY_OP_KEYGEN, EVP_PKEY_CTRL_RSA_MGF1_MD,
+                               md, NULL, NULL);
+}
+
+int EVP_PKEY_CTX_set_rsa_pss_keygen_mgf1_md_name(EVP_PKEY_CTX *ctx,
+                                                 const char *mdname)
+{
+    return int_set_rsa_mgf1_md(ctx, EVP_PKEY_RSA_PSS,
+                               EVP_PKEY_OP_TYPE_CRYPT | EVP_PKEY_OP_TYPE_SIG,
+                               -1, NULL, mdname, NULL);
+}
+
 int EVP_PKEY_CTX_get_rsa_mgf1_md_name(EVP_PKEY_CTX *ctx, char *name,
                                       size_t namelen)
 {
@@ -1202,11 +1231,12 @@ int EVP_PKEY_CTX_get0_rsa_oaep_label(EVP_PKEY_CTX *ctx, unsigned char **label)
     return (int)labellen;
 }
 
-int EVP_PKEY_CTX_set_rsa_pss_saltlen(EVP_PKEY_CTX *ctx, int saltlen)
+static int int_set_rsa_pss_saltlen(EVP_PKEY_CTX *ctx, int saltlen,
+                                   int keytype, int optype)
 {
     OSSL_PARAM pad_params[2], *p = pad_params;
 
-    if (ctx == NULL) {
+    if (ctx == NULL || (ctx->operation & optype) == 0) {
         ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
         /* Uses the same return values as EVP_PKEY_CTX_ctrl */
         return -2;
@@ -1214,14 +1244,19 @@ int EVP_PKEY_CTX_set_rsa_pss_saltlen(EVP_PKEY_CTX *ctx, int saltlen)
 
     /* If key type not RSA or RSA-PSS return error */
     if (ctx->pmeth != NULL
-            && ctx->pmeth->pkey_id != EVP_PKEY_RSA
-            && ctx->pmeth->pkey_id != EVP_PKEY_RSA_PSS)
+        && (keytype == -1
+            ? (ctx->pmeth->pkey_id != EVP_PKEY_RSA
+               && ctx->pmeth->pkey_id != EVP_PKEY_RSA_PSS)
+            : ctx->pmeth->pkey_id != keytype))
         return -1;
 
     /* TODO(3.0): Remove this eventually when no more legacy */
-    if (!EVP_PKEY_CTX_IS_SIGNATURE_OP(ctx)
-        || ctx->op.sig.sigprovctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, -1, -1, EVP_PKEY_CTRL_RSA_PSS_SALTLEN,
+    if ((EVP_PKEY_CTX_IS_SIGNATURE_OP(ctx)
+         && ctx->op.sig.sigprovctx == NULL)
+        || (EVP_PKEY_CTX_IS_GEN_OP(ctx)
+            && ctx->op.keymgmt.genctx == NULL))
+        return EVP_PKEY_CTX_ctrl(ctx, keytype, optype,
+                                 EVP_PKEY_CTRL_RSA_PSS_SALTLEN,
                                  saltlen, NULL);
 
     *p++ =
@@ -1231,6 +1266,17 @@ int EVP_PKEY_CTX_set_rsa_pss_saltlen(EVP_PKEY_CTX *ctx, int saltlen)
     return EVP_PKEY_CTX_set_params(ctx, pad_params);
 }
 
+int EVP_PKEY_CTX_set_rsa_pss_saltlen(EVP_PKEY_CTX *ctx, int saltlen)
+{
+    return int_set_rsa_pss_saltlen(ctx, saltlen, -1, EVP_PKEY_OP_TYPE_SIG);
+}
+
+int EVP_PKEY_CTX_set_rsa_pss_keygen_saltlen(EVP_PKEY_CTX *ctx, int saltlen)
+{
+    return int_set_rsa_pss_saltlen(ctx, saltlen, EVP_PKEY_RSA_PSS,
+                                   EVP_PKEY_OP_KEYGEN);
+}
+
 int EVP_PKEY_CTX_get_rsa_pss_saltlen(EVP_PKEY_CTX *ctx, int *saltlen)
 {
     OSSL_PARAM pad_params[2], *p = pad_params;
diff --git a/crypto/rsa/rsa_local.h b/crypto/rsa/rsa_local.h
index 6c4ae8611b..f94fc79cdd 100644
--- a/crypto/rsa/rsa_local.h
+++ b/crypto/rsa/rsa_local.h
@@ -12,6 +12,7 @@
 
 #include <openssl/rsa.h>
 #include "internal/refcount.h"
+#include "crypto/rsa.h"
 
 #define RSA_MAX_PRIME_NUM       5
 #define RSA_MIN_MODULUS_BITS    512
@@ -50,8 +51,18 @@ struct rsa_st {
     BIGNUM *dmp1;
     BIGNUM *dmq1;
     BIGNUM *iqmp;
-    /* If a PSS only key this contains the parameter restrictions */
+
+    /*
+     * If a PSS only key this contains the parameter restrictions.
+     * There are two structures for the same thing, used in different cases.
+     */
+    /* This is used uniquely by OpenSSL provider implementations. */
+    RSA_PSS_PARAMS_30 pss_params;
+#ifndef FIPS_MODULE
+    /* This is used uniquely by rsa_ameth.c and rsa_pmeth.c. */
     RSA_PSS_PARAMS *pss;
+#endif
+
 #ifndef FIPS_MODULE
     /* for multi-prime RSA, defined in RFC 8017 */
     STACK_OF(RSA_PRIME_INFO) *prime_infos;
diff --git a/crypto/rsa/rsa_pss.c b/crypto/rsa/rsa_pss.c
index afb558cd36..a5bcdfe1ff 100644
--- a/crypto/rsa/rsa_pss.c
+++ b/crypto/rsa/rsa_pss.c
@@ -256,6 +256,142 @@ int RSA_padding_add_PKCS1_PSS_mgf1(RSA *rsa, unsigned char *EM,
 
 }
 
+/*
+ * The defaults for PSS restrictions are defined in RFC 8017, A.2.3 RSASSA-PSS
+ * (https://tools.ietf.org/html/rfc8017#appendix-A.2.3):
+ *
+ * If the default values of the hashAlgorithm, maskGenAlgorithm, and
+ * trailerField fields of RSASSA-PSS-params are used, then the algorithm
+ * identifier will have the following value:
+ *
+ *     rSASSA-PSS-Default-Identifier    RSASSA-AlgorithmIdentifier ::= {
+ *         algorithm   id-RSASSA-PSS,
+ *         parameters  RSASSA-PSS-params : {
+ *             hashAlgorithm       sha1,
+ *             maskGenAlgorithm    mgf1SHA1,
+ *             saltLength          20,
+ *             trailerField        trailerFieldBC
+ *         }
+ *     }
+ *
+ *     RSASSA-AlgorithmIdentifier ::= AlgorithmIdentifier {
+ *         {PKCS1Algorithms}
+ *     }
+ */
+static const RSA_PSS_PARAMS_30 default_RSASSA_PSS_params = {
+    NID_sha1,                    /* default hashAlgorithm */
+    {
+        NID_mgf1,                /* default maskGenAlgorithm */
+        NID_sha1                 /* default MGF1 hash */
+    },
+    20,                          /* default saltLength */
+    1                            /* default trailerField (0xBC) */
+};
+
+int rsa_pss_params_30_set_defaults(RSA_PSS_PARAMS_30 *rsa_pss_params)
+{
+    if (rsa_pss_params == NULL)
+        return 0;
+    *rsa_pss_params = default_RSASSA_PSS_params;
+    return 1;
+}
+
+int rsa_pss_params_30_is_unrestricted(const RSA_PSS_PARAMS_30 *rsa_pss_params)
+{
+    static RSA_PSS_PARAMS_30 pss_params_cmp = { 0, };
+
+    return rsa_pss_params == NULL
+        || memcmp(rsa_pss_params, &pss_params_cmp,
+                  sizeof(*rsa_pss_params)) == 0;
+}
+
+int rsa_pss_params_30_copy(RSA_PSS_PARAMS_30 *to,
+                           const RSA_PSS_PARAMS_30 *from)
+{
+    memcpy(to, from, sizeof(*to));
+    return 1;
+}
+
+int rsa_pss_params_30_set_hashalg(RSA_PSS_PARAMS_30 *rsa_pss_params,
+                                  int hashalg_nid)
+{
+    if (rsa_pss_params == NULL)
+        return 0;
+    rsa_pss_params->hash_algorithm_nid = hashalg_nid;
+    return 1;
+}
+
+int rsa_pss_params_30_set_maskgenalg(RSA_PSS_PARAMS_30 *rsa_pss_params,
+                                     int maskgenalg_nid)
+{
+    if (rsa_pss_params == NULL)
+        return 0;
+    rsa_pss_params->mask_gen.algorithm_nid = maskgenalg_nid;
+    return 1;
+}
+
+int rsa_pss_params_30_set_maskgenhashalg(RSA_PSS_PARAMS_30 *rsa_pss_params,
+                                         int maskgenhashalg_nid)
+{
+    if (rsa_pss_params == NULL)
+        return 0;
+    rsa_pss_params->mask_gen.hash_algorithm_nid = maskgenhashalg_nid;
+    return 1;
+}
+
+int rsa_pss_params_30_set_saltlen(RSA_PSS_PARAMS_30 *rsa_pss_params,
+                                  int saltlen)
+{
+    if (rsa_pss_params == NULL)
+        return 0;
+    rsa_pss_params->salt_len = saltlen;
+    return 1;
+}
+
+int rsa_pss_params_30_set_trailerfield(RSA_PSS_PARAMS_30 *rsa_pss_params,
+                                       int trailerfield)
+{
+    if (rsa_pss_params == NULL)
+        return 0;
+    rsa_pss_params->trailer_field = trailerfield;
+    return 1;
+}
+
+int rsa_pss_params_30_hashalg(const RSA_PSS_PARAMS_30 *rsa_pss_params)
+{
+    if (rsa_pss_params == NULL)
+        return default_RSASSA_PSS_params.hash_algorithm_nid;
+    return rsa_pss_params->hash_algorithm_nid;
+}
+
+int rsa_pss_params_30_maskgenalg(const RSA_PSS_PARAMS_30 *rsa_pss_params)
+{
+    if (rsa_pss_params == NULL)
+        return default_RSASSA_PSS_params.mask_gen.algorithm_nid;
+    return rsa_pss_params->mask_gen.algorithm_nid;
+}
+
+int rsa_pss_params_30_maskgenhashalg(const RSA_PSS_PARAMS_30 *rsa_pss_params)
+{
+    if (rsa_pss_params == NULL)
+        return default_RSASSA_PSS_params.hash_algorithm_nid;
+    return rsa_pss_params->mask_gen.hash_algorithm_nid;
+}
+
+int rsa_pss_params_30_saltlen(const RSA_PSS_PARAMS_30 *rsa_pss_params)
+{
+    if (rsa_pss_params == NULL)
+        return default_RSASSA_PSS_params.salt_len;
+    return rsa_pss_params->salt_len;
+}
+
+int rsa_pss_params_30_trailerfield(const RSA_PSS_PARAMS_30 *rsa_pss_params)
+{
+    if (rsa_pss_params == NULL)
+        return default_RSASSA_PSS_params.trailer_field;
+    return rsa_pss_params->trailer_field;
+}
+
 #if defined(_MSC_VER)
 # pragma optimize("",on)
 #endif
diff --git a/crypto/rsa/rsa_schemes.c b/crypto/rsa/rsa_schemes.c
new file mode 100644
index 0000000000..7a54296a59
--- /dev/null
+++ b/crypto/rsa/rsa_schemes.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core.h>
+#include <openssl/core_names.h>
+#include <openssl/evp.h>
+#include <openssl/obj_mac.h>
+#include "internal/nelem.h"
+#include "crypto/rsa.h"
+
+static int meth2nid(const void *meth,
+                    int (*meth_is_a)(const void *meth, const char *name),
+                    const OSSL_ITEM *items, size_t items_n)
+{
+    size_t i;
+
+    if (meth != NULL)
+        for (i = 0; i < items_n; i++)
+            if (meth_is_a(meth, items[i].ptr))
+                return (int)items[i].id;
+    return NID_undef;
+}
+
+static const char *nid2name(int meth, const OSSL_ITEM *items, size_t items_n)
+{
+    size_t i;
+
+    for (i = 0; i < items_n; i++)
+        if (meth == (int)items[i].id)
+            return items[i].ptr;
+    return NULL;
+}
+
+/*
+ * The list of permitted hash functions are taken from 
+ * https://tools.ietf.org/html/rfc8017#appendix-A.2.1:
+ *
+ * OAEP-PSSDigestAlgorithms    ALGORITHM-IDENTIFIER ::= {
+ *     { OID id-sha1       PARAMETERS NULL }|
+ *     { OID id-sha224     PARAMETERS NULL }|
+ *     { OID id-sha256     PARAMETERS NULL }|
+ *     { OID id-sha384     PARAMETERS NULL }|
+ *     { OID id-sha512     PARAMETERS NULL }|
+ *     { OID id-sha512-224 PARAMETERS NULL }|
+ *     { OID id-sha512-256 PARAMETERS NULL },
+ *     ...  -- Allows for future expansion --
+ * }
+ */
+static const OSSL_ITEM oaeppss_name_nid_map[] = {
+    { NID_sha1,         OSSL_DIGEST_NAME_SHA1         },
+    { NID_sha224,       OSSL_DIGEST_NAME_SHA2_224     },
+    { NID_sha256,       OSSL_DIGEST_NAME_SHA2_256     },
+    { NID_sha384,       OSSL_DIGEST_NAME_SHA2_384     },
+    { NID_sha512,       OSSL_DIGEST_NAME_SHA2_512     },
+    { NID_sha512_224,   OSSL_DIGEST_NAME_SHA2_512_224 },
+    { NID_sha512_256,   OSSL_DIGEST_NAME_SHA2_512_256 },
+};
+
+static int md_is_a(const void *md, const char *name)
+{
+    return EVP_MD_is_a(md, name);
+}
+
+int rsa_oaeppss_md2nid(const EVP_MD *md)
+{
+    return meth2nid(md, md_is_a,
+                    oaeppss_name_nid_map, OSSL_NELEM(oaeppss_name_nid_map));
+}
+
+const char *rsa_oaeppss_nid2name(int md)
+{
+    return nid2name(md, oaeppss_name_nid_map, OSSL_NELEM(oaeppss_name_nid_map));
+}
+
+const char *rsa_mgf_nid2name(int mgf)
+{
+    if (mgf == NID_mgf1)
+        return SN_mgf1;
+    return NULL;
+}
diff --git a/include/crypto/rsa.h b/include/crypto/rsa.h
index 7ce1fdb339..6f32ec422f 100644
--- a/include/crypto/rsa.h
+++ b/include/crypto/rsa.h
@@ -13,7 +13,43 @@
 #include <openssl/core.h>
 #include <openssl/rsa.h>
 
+typedef struct rsa_pss_params_30_st {
+    int hash_algorithm_nid;
+    struct {
+        int algorithm_nid;       /* Currently always NID_mgf1 */
+        int hash_algorithm_nid;
+    } mask_gen;
+    unsigned int salt_len;
+    unsigned int trailer_field;
+} RSA_PSS_PARAMS_30;
+
+RSA_PSS_PARAMS_30 *rsa_get0_pss_params_30(RSA *r);
+int rsa_pss_params_30_set_defaults(RSA_PSS_PARAMS_30 *rsa_pss_params);
+int rsa_pss_params_30_copy(RSA_PSS_PARAMS_30 *to,
+                           const RSA_PSS_PARAMS_30 *from);
+int rsa_pss_params_30_is_unrestricted(const RSA_PSS_PARAMS_30 *rsa_pss_params);
+int rsa_pss_params_30_set_hashalg(RSA_PSS_PARAMS_30 *rsa_pss_params,
+                                  int hashalg_nid);
+int rsa_pss_params_30_set_maskgenalg(RSA_PSS_PARAMS_30 *rsa_pss_params,
+                                     int maskgenalg_nid);
+int rsa_pss_params_30_set_maskgenhashalg(RSA_PSS_PARAMS_30 *rsa_pss_params,
+                                         int maskgenhashalg_nid);
+int rsa_pss_params_30_set_saltlen(RSA_PSS_PARAMS_30 *rsa_pss_params,
+                                  int saltlen);
+int rsa_pss_params_30_set_trailerfield(RSA_PSS_PARAMS_30 *rsa_pss_params,
+                                       int trailerfield);
+int rsa_pss_params_30_hashalg(const RSA_PSS_PARAMS_30 *rsa_pss_params);
+int rsa_pss_params_30_maskgenalg(const RSA_PSS_PARAMS_30 *rsa_pss_params);
+int rsa_pss_params_30_maskgenhashalg(const RSA_PSS_PARAMS_30 *rsa_pss_params);
+int rsa_pss_params_30_saltlen(const RSA_PSS_PARAMS_30 *rsa_pss_params);
+int rsa_pss_params_30_trailerfield(const RSA_PSS_PARAMS_30 *rsa_pss_params);
+
+const char *rsa_mgf_nid2name(int mgf);
+int rsa_oaeppss_md2nid(const EVP_MD *md);
+const char *rsa_oaeppss_nid2name(int md);
+
 RSA *rsa_new_with_ctx(OPENSSL_CTX *libctx);
+OPENSSL_CTX *rsa_get0_libctx(RSA *r);
 
 int rsa_set0_all_params(RSA *r, const STACK_OF(BIGNUM) *primes,
                         const STACK_OF(BIGNUM) *exps,
@@ -21,7 +57,13 @@ int rsa_set0_all_params(RSA *r, const STACK_OF(BIGNUM) *primes,
 int rsa_get0_all_params(RSA *r, STACK_OF(BIGNUM_const) *primes,
                         STACK_OF(BIGNUM_const) *exps,
                         STACK_OF(BIGNUM_const) *coeffs);
+
+int rsa_todata(RSA *rsa, OSSL_PARAM_BLD *bld, OSSL_PARAM params[]);
 int rsa_fromdata(RSA *rsa, const OSSL_PARAM params[]);
+int rsa_pss_params_30_todata(const RSA_PSS_PARAMS_30 *pss, const char *propq,
+                             OSSL_PARAM_BLD *bld, OSSL_PARAM params[]);
+int rsa_pss_params_30_fromdata(RSA_PSS_PARAMS_30 *pss_params,
+                               const OSSL_PARAM params[], OPENSSL_CTX *libctx);
 
 int rsa_padding_check_PKCS1_type_2_TLS(OPENSSL_CTX *ctx, unsigned char *to,
                                        size_t tlen, const unsigned char *from,
diff --git a/include/openssl/core_names.h b/include/openssl/core_names.h
index 6e93738ae0..1bd122482c 100644
--- a/include/openssl/core_names.h
+++ b/include/openssl/core_names.h
@@ -182,6 +182,7 @@ extern "C" {
 #define OSSL_PKEY_PARAM_DIGEST              OSSL_ALG_PARAM_DIGEST
 #define OSSL_PKEY_PARAM_PROPERTIES          OSSL_ALG_PARAM_PROPERTIES
 #define OSSL_PKEY_PARAM_DIGEST_SIZE         "digest-size"
+#define OSSL_PKEY_PARAM_MASKGENFUNC         "mgf"
 #define OSSL_PKEY_PARAM_MGF1_DIGEST         "mgf1-digest"
 #define OSSL_PKEY_PARAM_MGF1_PROPERTIES     "mgf1-properties"
 
@@ -271,8 +272,12 @@ extern "C" {
 
 
 /* Key generation parameters */
-#define OSSL_PKEY_PARAM_RSA_BITS        OSSL_PKEY_PARAM_BITS
-#define OSSL_PKEY_PARAM_RSA_PRIMES      "primes"
+#define OSSL_PKEY_PARAM_RSA_BITS            OSSL_PKEY_PARAM_BITS
+#define OSSL_PKEY_PARAM_RSA_PRIMES          "primes"
+#define OSSL_PKEY_PARAM_RSA_DIGEST          OSSL_PKEY_PARAM_DIGEST
+#define OSSL_PKEY_PARAM_RSA_MASKGENFUNC     OSSL_PKEY_PARAM_MASKGENFUNC
+#define OSSL_PKEY_PARAM_RSA_MGF1_DIGEST     OSSL_PKEY_PARAM_MGF1_DIGEST
+#define OSSL_PKEY_PARAM_RSA_PSS_SALTLEN     "saltlen"
 
 /* Key generation parameters */
 #define OSSL_PKEY_PARAM_FFC_TYPE         "type"
@@ -310,7 +315,7 @@ extern "C" {
 #define OSSL_SIGNATURE_PARAM_PAD_MODE           OSSL_PKEY_PARAM_PAD_MODE
 #define OSSL_SIGNATURE_PARAM_DIGEST             OSSL_PKEY_PARAM_DIGEST
 #define OSSL_SIGNATURE_PARAM_PROPERTIES         OSSL_PKEY_PARAM_PROPERTIES
-#define OSSL_SIGNATURE_PARAM_PSS_SALTLEN        "pss-saltlen"
+#define OSSL_SIGNATURE_PARAM_PSS_SALTLEN        "saltlen"
 #define OSSL_SIGNATURE_PARAM_MGF1_DIGEST        OSSL_PKEY_PARAM_MGF1_DIGEST
 #define OSSL_SIGNATURE_PARAM_MGF1_PROPERTIES    \
     OSSL_PKEY_PARAM_MGF1_PROPERTIES
diff --git a/include/openssl/core_numbers.h b/include/openssl/core_numbers.h
index 6af086fc2b..3d91741601 100644
--- a/include/openssl/core_numbers.h
+++ b/include/openssl/core_numbers.h
@@ -524,7 +524,8 @@ OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, OP_keyexch_gettable_ctx_params,
 # define OSSL_FUNC_SIGNATURE_SET_CTX_MD_PARAMS      24
 # define OSSL_FUNC_SIGNATURE_SETTABLE_CTX_MD_PARAMS 25
 
-OSSL_CORE_MAKE_FUNC(void *, OP_signature_newctx, (void *provctx))
+OSSL_CORE_MAKE_FUNC(void *, OP_signature_newctx, (void *provctx,
+                                                  const char *propq))
 OSSL_CORE_MAKE_FUNC(int, OP_signature_sign_init, (void *ctx, void *provkey))
 OSSL_CORE_MAKE_FUNC(int, OP_signature_sign, (void *ctx,  unsigned char *sig,
                                              size_t *siglen, size_t sigsize,
@@ -545,8 +546,7 @@ OSSL_CORE_MAKE_FUNC(int, OP_signature_verify_recover, (void *ctx,
                                                        const unsigned char *sig,
                                                        size_t siglen))
 OSSL_CORE_MAKE_FUNC(int, OP_signature_digest_sign_init,
-                    (void *ctx, const char *mdname, const char *props,
-                     void *provkey))
+                    (void *ctx, const char *mdname, void *provkey))
 OSSL_CORE_MAKE_FUNC(int, OP_signature_digest_sign_update,
                     (void *ctx, const unsigned char *data, size_t datalen))
 OSSL_CORE_MAKE_FUNC(int, OP_signature_digest_sign_final,
@@ -556,8 +556,7 @@ OSSL_CORE_MAKE_FUNC(int, OP_signature_digest_sign,
                     (void *ctx, unsigned char *sigret, size_t *siglen,
                      size_t sigsize, const unsigned char *tbs, size_t tbslen))
 OSSL_CORE_MAKE_FUNC(int, OP_signature_digest_verify_init,
-                    (void *ctx, const char *mdname, const char *props,
-                     void *provkey))
+                    (void *ctx, const char *mdname, void *provkey))
 OSSL_CORE_MAKE_FUNC(int, OP_signature_digest_verify_update,
                     (void *ctx, const unsigned char *data, size_t datalen))
 OSSL_CORE_MAKE_FUNC(int, OP_signature_digest_verify_final,
diff --git a/include/openssl/evp.h b/include/openssl/evp.h
index 5dc29d1976..fe2e440a8b 100644
--- a/include/openssl/evp.h
+++ b/include/openssl/evp.h
@@ -437,7 +437,7 @@ typedef int (EVP_PBE_KEYGEN) (EVP_CIPHER_CTX *ctx, const char *pass,
 
 # ifndef OPENSSL_NO_RSA
 #  define EVP_PKEY_assign_RSA(pkey,rsa) EVP_PKEY_assign((pkey),EVP_PKEY_RSA,\
-                                        (rsa))
+                                                        (rsa))
 # endif
 
 # ifndef OPENSSL_NO_DSA
diff --git a/include/openssl/rsa.h b/include/openssl/rsa.h
index 2508abd81c..bf12b90088 100644
--- a/include/openssl/rsa.h
+++ b/include/openssl/rsa.h
@@ -107,6 +107,24 @@ extern "C" {
 #   define RSA_FLAG_NO_EXP_CONSTTIME RSA_FLAG_NO_CONSTTIME
 #  endif
 
+/*-
+ * New with 3.0: use part of the flags to denote exact type of RSA key,
+ * some of which are limited to specific signature and encryption schemes.
+ * These different types share the same RSA structure, but indicate the
+ * use of certain fields in that structure.
+ * Currently known are:
+ * RSA          - this is the "normal" unlimited RSA structure (typenum 0)
+ * RSASSA-PSS   - indicates that the PSS parameters are used.
+ * RSAES-OAEP   - no specific field used for the moment, but OAEP padding
+ *                is expected.  (currently unused)
+ *
+ * 4 bits allow for 16 types
+ */
+#  define RSA_FLAG_TYPE_MASK            0xF000
+#  define RSA_FLAG_TYPE_RSA             0x0000
+#  define RSA_FLAG_TYPE_RSASSAPSS       0x1000
+#  define RSA_FLAG_TYPE_RSAESOAEP       0x2000
+
 int EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX *ctx, int pad_mode);
 int EVP_PKEY_CTX_get_rsa_padding(EVP_PKEY_CTX *ctx, int *pad_mode);
 
@@ -116,6 +134,7 @@ int EVP_PKEY_CTX_get_rsa_pss_saltlen(EVP_PKEY_CTX *ctx, int *saltlen);
 int EVP_PKEY_CTX_set_rsa_keygen_bits(EVP_PKEY_CTX *ctx, int bits);
 int EVP_PKEY_CTX_set_rsa_keygen_pubexp(EVP_PKEY_CTX *ctx, BIGNUM *pubexp);
 int EVP_PKEY_CTX_set_rsa_keygen_primes(EVP_PKEY_CTX *ctx, int primes);
+int EVP_PKEY_CTX_set_rsa_pss_keygen_saltlen(EVP_PKEY_CTX *ctx, int saltlen);
 
 /* Salt length matches digest */
 #  define RSA_PSS_SALTLEN_DIGEST -1
@@ -126,20 +145,15 @@ int EVP_PKEY_CTX_set_rsa_keygen_primes(EVP_PKEY_CTX *ctx, int primes);
 /* Old compatible max salt length for sign only */
 #  define RSA_PSS_SALTLEN_MAX_SIGN    -2
 
-#  define EVP_PKEY_CTX_set_rsa_pss_keygen_saltlen(ctx, len) \
-        EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA_PSS, EVP_PKEY_OP_KEYGEN, \
-                          EVP_PKEY_CTRL_RSA_PSS_SALTLEN, len, NULL)
-
 int EVP_PKEY_CTX_set_rsa_mgf1_md(EVP_PKEY_CTX *ctx, const EVP_MD *md);
 int EVP_PKEY_CTX_set_rsa_mgf1_md_name(EVP_PKEY_CTX *ctx, const char *mdname,
                                       const char *mdprops);
 int EVP_PKEY_CTX_get_rsa_mgf1_md(EVP_PKEY_CTX *ctx, const EVP_MD **md);
 int EVP_PKEY_CTX_get_rsa_mgf1_md_name(EVP_PKEY_CTX *ctx, char *name,
                                       size_t namelen);
-
-#  define  EVP_PKEY_CTX_set_rsa_pss_keygen_mgf1_md(ctx, md) \
-        EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA_PSS, EVP_PKEY_OP_KEYGEN, \
-                          EVP_PKEY_CTRL_RSA_MGF1_MD, 0, (void *)(md))
+int EVP_PKEY_CTX_set_rsa_pss_keygen_mgf1_md(EVP_PKEY_CTX *ctx, const EVP_MD *md);
+int EVP_PKEY_CTX_set_rsa_pss_keygen_mgf1_md_name(EVP_PKEY_CTX *ctx,
+                                                 const char *mdname);
 
 int EVP_PKEY_CTX_set_rsa_oaep_md(EVP_PKEY_CTX *ctx, const EVP_MD *md);
 int EVP_PKEY_CTX_set_rsa_oaep_md_name(EVP_PKEY_CTX *ctx, const char *mdname,
diff --git a/providers/common/der/DIGESTS.asn1 b/providers/common/der/DIGESTS.asn1
new file mode 100644
index 0000000000..afed372186
--- /dev/null
+++ b/providers/common/der/DIGESTS.asn1
@@ -0,0 +1,19 @@
+-- -------------------------------------------------------------------
+-- Taken from https://csrc.nist.gov/projects/computer-security-objects-register/algorithm-registration
+
+id-sha256 OBJECT IDENTIFIER ::= { hashAlgs 1 }
+id-sha384 OBJECT IDENTIFIER ::= { hashAlgs 2 }
+id-sha512 OBJECT IDENTIFIER ::= { hashAlgs 3 }
+id-sha224 OBJECT IDENTIFIER ::= { hashAlgs 4 }
+id-sha512-224 OBJECT IDENTIFIER ::= { hashAlgs 5 }
+id-sha512-256 OBJECT IDENTIFIER ::= { hashAlgs 6 }
+id-sha3-224 OBJECT IDENTIFIER ::= { hashAlgs 7 }
+id-sha3-256 OBJECT IDENTIFIER ::= { hashAlgs 8 }
+id-sha3-384 OBJECT IDENTIFIER ::= { hashAlgs 9 }
+id-sha3-512 OBJECT IDENTIFIER ::= { hashAlgs 10 }
+id-shake128 OBJECT IDENTIFIER ::= { hashAlgs 11 }
+id-shake256 OBJECT IDENTIFIER ::= { hashAlgs 12 }
+id-shake128-len OBJECT IDENTIFIER ::= { hashAlgs 17 }
+id-shake256-len OBJECT IDENTIFIER ::= { hashAlgs 18 }
+id-KMACWithSHAKE128 OBJECT IDENTIFIER ::={hashAlgs 19}
+id-KMACWithSHAKE256 OBJECT IDENTIFIER ::={ hashAlgs 20}
diff --git a/providers/common/der/NIST.asn1 b/providers/common/der/NIST.asn1
new file mode 100644
index 0000000000..3e43848495
--- /dev/null
+++ b/providers/common/der/NIST.asn1
@@ -0,0 +1,8 @@
+-- -------------------------------------------------------------------
+-- Taken from https://csrc.nist.gov/projects/computer-security-objects-register/algorithm-registration
+
+-- Copies of common OIDs used by other ASN.1 files.
+csor OBJECT IDENTIFIER ::= { 2 16 840 1 101 3 }
+nistAlgorithms OBJECT IDENTIFIER ::= { csor nistAlgorithm(4) }
+hashAlgs OBJECT IDENTIFIER ::= { nistAlgorithms 2 }
+sigAlgs OBJECT IDENTIFIER ::= { nistAlgorithms 3 }
diff --git a/providers/common/der/RSA.asn1 b/providers/common/der/RSA.asn1
index 66511be50e..d0c54d71ef 100644
--- a/providers/common/der/RSA.asn1
+++ b/providers/common/der/RSA.asn1
@@ -80,8 +80,6 @@ id-mgf1    OBJECT IDENTIFIER ::= { pkcs-1 8 }
 -- -------------------------------------------------------------------
 -- Taken from https://csrc.nist.gov/projects/computer-security-objects-register/algorithm-registration
 
-sigAlgs OBJECT IDENTIFIER ::= { 2 16 840 1 101 3 4 3 }
-
 id-rsassa-pkcs1-v1_5-with-sha3-224 OBJECT IDENTIFIER ::= { sigAlgs 13 }
 id-rsassa-pkcs1-v1_5-with-sha3-256 OBJECT IDENTIFIER ::= { sigAlgs 14 }
 id-rsassa-pkcs1-v1_5-with-sha3-384 OBJECT IDENTIFIER ::= { sigAlgs 15 }
diff --git a/providers/common/der/build.info b/providers/common/der/build.info
index eda763ea8e..837fe73fed 100644
--- a/providers/common/der/build.info
+++ b/providers/common/der/build.info
@@ -1,4 +1,4 @@
-$FIPSABLE=der_rsa.c der_dsa.c der_ec.c
+$FIPSABLE=der_rsa.c der_dsa.c der_ec.c der_digests.c
 
 SOURCE[../../libfips.a]=$FIPSABLE
 SOURCE[../../libnonfips.a]=$FIPSABLE
@@ -6,7 +6,7 @@ SOURCE[../../libnonfips.a]=$FIPSABLE
 GENERATE[der_rsa.c]=der_rsa.c.in
 DEPEND[der_rsa.c]=oids_to_c.pm
 
-DEPEND[der_rsa.o]=../include/prov/der_rsa.h
+DEPEND[der_rsa.o]=../include/prov/der_rsa.h ../include/prov/der_digests.h
 GENERATE[../include/prov/der_rsa.h]=der_rsa.h.in
 DEPEND[../include/prov/der_rsa.h]=oids_to_c.pm
 
@@ -23,3 +23,10 @@ DEPEND[der_ec.c]=oids_to_c.pm
 DEPEND[der_ec.o]=../include/prov/der_ec.h
 GENERATE[../include/prov/der_ec.h]=der_ec.h.in
 DEPEND[../include/prov/der_ec.h]=oids_to_c.pm
+
+GENERATE[der_digests.c]=der_digests.c.in
+DEPEND[der_digests.c]=oids_to_c.pm
+
+DEPEND[der_digests.o]=../include/prov/der_digests.h
+GENERATE[../include/prov/der_digests.h]=der_digests.h.in
+DEPEND[../include/prov/der_digests.h]=oids_to_c.pm
diff --git a/providers/common/der/der_rsa.h.in b/providers/common/der/der_digests.c.in
similarity index 62%
copy from providers/common/der/der_rsa.h.in
copy to providers/common/der/der_digests.c.in
index 3f7cc0e029..433c107420 100644
--- a/providers/common/der/der_rsa.h.in
+++ b/providers/common/der/der_digests.c.in
@@ -7,15 +7,12 @@
  * https://www.openssl.org/source/license.html
  */
 
-#include "internal/der.h"
+#include "prov/der_digests.h"
 
 /* Well known OIDs precompiled */
 {-
-    $OUT = oids_to_c::process_leaves('providers/common/der/RSA.asn1',
+    $OUT = oids_to_c::process_leaves('providers/common/der/NIST.asn1',
+                                     'providers/common/der/DIGESTS.asn1',
                                      { dir => $config{sourcedir},
-                                       filter => \&oids_to_c::filter_to_H });
+                                       filter => \&oids_to_c::filter_to_C });
 -}
-
-int DER_w_algorithmIdentifier_RSA(WPACKET *pkt, int tag, RSA *rsa);
-int DER_w_algorithmIdentifier_RSA_with(WPACKET *pkt, int tag,
-                                       RSA *rsa, int mdnid);
diff --git a/providers/common/der/der_rsa.h.in b/providers/common/der/der_digests.h.in
similarity index 67%
copy from providers/common/der/der_rsa.h.in
copy to providers/common/der/der_digests.h.in
index 3f7cc0e029..91fbe1f72c 100644
--- a/providers/common/der/der_rsa.h.in
+++ b/providers/common/der/der_digests.h.in
@@ -11,11 +11,8 @@
 
 /* Well known OIDs precompiled */
 {-
-    $OUT = oids_to_c::process_leaves('providers/common/der/RSA.asn1',
+    $OUT = oids_to_c::process_leaves('providers/common/der/NIST.asn1',
+                                     'providers/common/der/DIGESTS.asn1',
                                      { dir => $config{sourcedir},
                                        filter => \&oids_to_c::filter_to_H });
 -}
-
-int DER_w_algorithmIdentifier_RSA(WPACKET *pkt, int tag, RSA *rsa);
-int DER_w_algorithmIdentifier_RSA_with(WPACKET *pkt, int tag,
-                                       RSA *rsa, int mdnid);
diff --git a/providers/common/der/der_rsa.c.in b/providers/common/der/der_rsa.c.in
index bc7c0095e9..30e945cf58 100644
--- a/providers/common/der/der_rsa.c.in
+++ b/providers/common/der/der_rsa.c.in
@@ -9,25 +9,381 @@
 
 #include <openssl/bn.h>
 #include <openssl/obj_mac.h>
+#include "internal/cryptlib.h"
 #include "prov/der_rsa.h"
+#include "prov/der_digests.h"
 
 /* Well known OIDs precompiled */
 {-
-    $OUT = oids_to_c::process_leaves('providers/common/der/RSA.asn1',
+    $OUT = oids_to_c::process_leaves('providers/common/der/NIST.asn1',
+                                     'providers/common/der/DIGESTS.asn1',
+                                     'providers/common/der/RSA.asn1',
                                      { dir => $config{sourcedir},
                                        filter => \&oids_to_c::filter_to_C });
 -}
 
+/* More complex pre-compiled sequences.  TODO(3.0) refactor? */
+/*-
+ * From https://tools.ietf.org/html/rfc8017#appendix-A.2.1
+ *
+ * OAEP-PSSDigestAlgorithms    ALGORITHM-IDENTIFIER ::= {
+ *     { OID id-sha1       PARAMETERS NULL }|
+ *     { OID id-sha224     PARAMETERS NULL }|
+ *     { OID id-sha256     PARAMETERS NULL }|
+ *     { OID id-sha384     PARAMETERS NULL }|
+ *     { OID id-sha512     PARAMETERS NULL }|
+ *     { OID id-sha512-224 PARAMETERS NULL }|
+ *     { OID id-sha512-256 PARAMETERS NULL },
+ *     ...  -- Allows for future expansion --
+ * }
+ */
+#define DER_V_NULL DER_P_NULL, 0
+#define DER_SZ_NULL 2
+
+/*
+ * The names for the hash function AlgorithmIdentifiers are borrowed and
+ * expanded from https://tools.ietf.org/html/rfc4055#section-2.1
+ *
+ * sha1Identifier  AlgorithmIdentifier  ::=  { id-sha1, NULL }
+ * sha224Identifier  AlgorithmIdentifier  ::=  { id-sha224, NULL }
+ * sha256Identifier  AlgorithmIdentifier  ::=  { id-sha256, NULL }
+ * sha384Identifier  AlgorithmIdentifier  ::=  { id-sha384, NULL }
+ * sha512Identifier  AlgorithmIdentifier  ::=  { id-sha512, NULL }
+ */
+/*
+ * NOTE: Some of the arrays aren't used other than inside sizeof(), which
+ * clang complains about (-Wno-unneeded-internal-declaration).  To get
+ * around that, we make them non-static, and declare them an extra time to
+ * avoid compilers complaining about definitions without declarations.
+ */
+#if 0                            /* Currently unused */
+#define DER_AID_V_sha1Identifier                                        \
+    DER_P_SEQUENCE|DER_F_CONSTRUCTED,                                   \
+        DER_OID_SZ_id_sha1 + DER_SZ_NULL,                               \
+        DER_OID_V_id_sha1,                                              \
+        DER_V_NULL
+extern const unsigned char der_aid_sha1Identifier[];
+const unsigned char der_aid_sha1Identifier[] = {
+    DER_AID_V_sha1Identifier
+};
+#define DER_AID_SZ_sha1Identifier sizeof(der_aid_sha1Identifier)
+#endif
+
+#define DER_AID_V_sha224Identifier                                      \
+    DER_P_SEQUENCE|DER_F_CONSTRUCTED,                                   \
+        DER_OID_SZ_id_sha224 + DER_SZ_NULL,                             \
+        DER_OID_V_id_sha224,                                            \
+        DER_V_NULL
+extern const unsigned char der_aid_sha224Identifier[];
+const unsigned char der_aid_sha224Identifier[] = {
+    DER_AID_V_sha224Identifier
+};
+#define DER_AID_SZ_sha224Identifier sizeof(der_aid_sha224Identifier)
+
+#define DER_AID_V_sha256Identifier                                      \
+    DER_P_SEQUENCE|DER_F_CONSTRUCTED,                                   \
+        DER_OID_SZ_id_sha256 + DER_SZ_NULL,                             \
+        DER_OID_V_id_sha256,                                            \
+        DER_V_NULL
+extern const unsigned char der_aid_sha256Identifier[];
+const unsigned char der_aid_sha256Identifier[] = {
+    DER_AID_V_sha256Identifier
+};
+#define DER_AID_SZ_sha256Identifier sizeof(der_aid_sha256Identifier)
+
+#define DER_AID_V_sha384Identifier                                      \
+    DER_P_SEQUENCE|DER_F_CONSTRUCTED,                                   \
+        DER_OID_SZ_id_sha384 + DER_SZ_NULL,                             \
+        DER_OID_V_id_sha384,                                            \
+        DER_V_NULL
+extern const unsigned char der_aid_sha384Identifier[];
+const unsigned char der_aid_sha384Identifier[] = {
+    DER_AID_V_sha384Identifier
+};
+#define DER_AID_SZ_sha384Identifier sizeof(der_aid_sha384Identifier)
+
+#define DER_AID_V_sha512Identifier                                      \
+    DER_P_SEQUENCE|DER_F_CONSTRUCTED,                                   \
+        DER_OID_SZ_id_sha512 + DER_SZ_NULL,                             \
+        DER_OID_V_id_sha512,                                            \
+        DER_V_NULL
+extern const unsigned char der_aid_sha512Identifier[];
+const unsigned char der_aid_sha512Identifier[] = {
+    DER_AID_V_sha512Identifier
+};
+#define DER_AID_SZ_sha512Identifier sizeof(der_aid_sha512Identifier)
+
+#define DER_AID_V_sha512_224Identifier                                  \
+    DER_P_SEQUENCE|DER_F_CONSTRUCTED,                                   \
+        DER_OID_SZ_id_sha512_224 + DER_SZ_NULL,                         \
+        DER_OID_V_id_sha512_224,                                        \
+        DER_V_NULL
+extern const unsigned char der_aid_sha512_224Identifier[];
+const unsigned char der_aid_sha512_224Identifier[] = {
+    DER_AID_V_sha512_224Identifier
+};
+#define DER_AID_SZ_sha512_224Identifier sizeof(der_aid_sha512_224Identifier)
+
+#define DER_AID_V_sha512_256Identifier                                  \
+    DER_P_SEQUENCE|DER_F_CONSTRUCTED,                                   \
+        DER_OID_SZ_id_sha512_256 + DER_SZ_NULL,                         \
+        DER_OID_V_id_sha512_256,                                        \
+        DER_V_NULL
+extern const unsigned char der_aid_sha512_256Identifier[];
+const unsigned char der_aid_sha512_256Identifier[] = {
+    DER_AID_V_sha512_256Identifier
+};
+#define DER_AID_SZ_sha512_256Identifier sizeof(der_aid_sha512_256Identifier)
+
+/*-
+ * From https://tools.ietf.org/html/rfc8017#appendix-A.2.1
+ *
+ * HashAlgorithm ::= AlgorithmIdentifier {
+ *    {OAEP-PSSDigestAlgorithms}
+ * }
+ *
+ * ...
+ *
+ * PKCS1MGFAlgorithms    ALGORITHM-IDENTIFIER ::= {
+ *     { OID id-mgf1 PARAMETERS HashAlgorithm },
+ *     ...  -- Allows for future expansion --
+ * }
+ */
+
+/*
+ * The names for the MGF1 AlgorithmIdentifiers are borrowed and expanded
+ * from https://tools.ietf.org/html/rfc4055#section-2.1
+ *
+ * mgf1SHA1Identifier  AlgorithmIdentifier  ::=
+ *                      { id-mgf1, sha1Identifier }
+ * mgf1SHA224Identifier  AlgorithmIdentifier  ::=
+ *                      { id-mgf1, sha224Identifier }
+ * mgf1SHA256Identifier  AlgorithmIdentifier  ::=
+ *                      { id-mgf1, sha256Identifier }
+ * mgf1SHA384Identifier  AlgorithmIdentifier  ::=
+ *                      { id-mgf1, sha384Identifier }
+ * mgf1SHA512Identifier  AlgorithmIdentifier  ::=
+ *                      { id-mgf1, sha512Identifier }
+ */
+#if 0                            /* Currently unused */
+#define DER_AID_V_mgf1SHA1Identifier                                    \
+    DER_P_SEQUENCE|DER_F_CONSTRUCTED,                                   \
+        DER_OID_SZ_id_mgf1 + DER_AID_SZ_sha1Identifier,                 \
+        DER_OID_V_id_mgf1,                                              \
+        DER_AID_V_sha1Identifier
+static const unsigned char der_aid_mgf1SHA1Identifier[] = {
+    DER_AID_V_mgf1SHA1Identifier
+};
+#define DER_AID_SZ_mgf1SHA1Identifier sizeof(der_aid_mgf1SHA1Identifier)
+#endif
+
+#define DER_AID_V_mgf1SHA224Identifier                          \
+    DER_P_SEQUENCE|DER_F_CONSTRUCTED,                           \
+        DER_OID_SZ_id_mgf1 + DER_AID_SZ_sha224Identifier,       \
+        DER_OID_V_id_mgf1,                                      \
+        DER_AID_V_sha224Identifier
+static const unsigned char der_aid_mgf1SHA224Identifier[] = {
+    DER_AID_V_mgf1SHA224Identifier
+};
+#define DER_AID_SZ_mgf1SHA224Identifier sizeof(der_aid_mgf1SHA224Identifier)
+
+#define DER_AID_V_mgf1SHA256Identifier                          \
+    DER_P_SEQUENCE|DER_F_CONSTRUCTED,                           \
+        DER_OID_SZ_id_mgf1 + DER_AID_SZ_sha256Identifier,       \
+        DER_OID_V_id_mgf1,                                      \
+        DER_AID_V_sha256Identifier
+static const unsigned char der_aid_mgf1SHA256Identifier[] = {
+    DER_AID_V_mgf1SHA256Identifier
+};
+#define DER_AID_SZ_mgf1SHA256Identifier sizeof(der_aid_mgf1SHA256Identifier)
+
+#define DER_AID_V_mgf1SHA384Identifier                          \
+    DER_P_SEQUENCE|DER_F_CONSTRUCTED,                           \
+        DER_OID_SZ_id_mgf1 + DER_AID_SZ_sha384Identifier,       \
+        DER_OID_V_id_mgf1,                                      \
+        DER_AID_V_sha384Identifier
+static const unsigned char der_aid_mgf1SHA384Identifier[] = {
+    DER_AID_V_mgf1SHA384Identifier
+};
+#define DER_AID_SZ_mgf1SHA384Identifier sizeof(der_aid_mgf1SHA384Identifier)
+
+#define DER_AID_V_mgf1SHA512Identifier                          \
+    DER_P_SEQUENCE|DER_F_CONSTRUCTED,                           \
+        DER_OID_SZ_id_mgf1 + DER_AID_SZ_sha512Identifier,       \
+        DER_OID_V_id_mgf1,                                      \
+        DER_AID_V_sha512Identifier
+static const unsigned char der_aid_mgf1SHA512Identifier[] = {
+    DER_AID_V_mgf1SHA512Identifier
+};
+#define DER_AID_SZ_mgf1SHA512Identifier sizeof(der_aid_mgf1SHA512Identifier)
+
+#define DER_AID_V_mgf1SHA512_224Identifier                      \
+    DER_P_SEQUENCE|DER_F_CONSTRUCTED,                           \
+        DER_OID_SZ_id_mgf1 + DER_AID_SZ_sha512_224Identifier,   \
+        DER_OID_V_id_mgf1,                                      \
+        DER_AID_V_sha512_224Identifier
+static const unsigned char der_aid_mgf1SHA512_224Identifier[] = {
+    DER_AID_V_mgf1SHA512_224Identifier
+};
+#define DER_AID_SZ_mgf1SHA512_224Identifier sizeof(der_aid_mgf1SHA512_224Identifier)
+
+#define DER_AID_V_mgf1SHA512_256Identifier                      \
+    DER_P_SEQUENCE|DER_F_CONSTRUCTED,                           \
+        DER_OID_SZ_id_mgf1 + DER_AID_SZ_sha512_256Identifier,   \
+        DER_OID_V_id_mgf1,                                      \
+        DER_AID_V_sha512_256Identifier
+static const unsigned char der_aid_mgf1SHA512_256Identifier[] = {
+    DER_AID_V_mgf1SHA512_256Identifier
+};
+#define DER_AID_SZ_mgf1SHA512_256Identifier sizeof(der_aid_mgf1SHA512_256Identifier)
+
+
+#define MGF1_SHA_CASE(bits, var)                                \
+    case NID_sha##bits:                                         \
+        var = der_aid_mgf1SHA##bits##Identifier;                \
+        var##_sz = sizeof(der_aid_mgf1SHA##bits##Identifier);   \
+        break;
+
+/*-
+ * The name is borrowed from https://tools.ietf.org/html/rfc8017#appendix-A.2.1
+ *
+ * MaskGenAlgorithm ::= AlgorithmIdentifier { {PKCS1MGFAlgorithms} }
+ */
+static int DER_w_MaskGenAlgorithm(WPACKET *pkt, int tag,
+                                  const RSA_PSS_PARAMS_30 *pss)
+{
+    if (pss != NULL && rsa_pss_params_30_maskgenalg(pss) == NID_mgf1) {
+        int maskgenhashalg_nid = rsa_pss_params_30_maskgenhashalg(pss);
+        const unsigned char *maskgenalg = NULL;
+        size_t maskgenalg_sz = 0;
+
+        switch (maskgenhashalg_nid) {
+        case NID_sha1:
+            break;
+            MGF1_SHA_CASE(224, maskgenalg);
+            MGF1_SHA_CASE(256, maskgenalg);
+            MGF1_SHA_CASE(384, maskgenalg);
+            MGF1_SHA_CASE(512, maskgenalg);
+            MGF1_SHA_CASE(512_224, maskgenalg);
+            MGF1_SHA_CASE(512_256, maskgenalg);
+        default:
+            return 0;
+        }
+
+        /* If there is none (or it was the default), we write nothing */
+        if (maskgenalg == NULL)
+            return 1;
+
+        return DER_w_precompiled(pkt, tag, maskgenalg, maskgenalg_sz);
+    }
+    return 0;
+}
+
+#define OAEP_PSS_MD_CASE(name, var)                                     \
+    case NID_##name:                                                    \
+        var = der_oid_id_##name;                                        \
+        var##_sz = sizeof(der_oid_id_##name);                           \
+        break;
+
+int DER_w_RSASSA_PSS_params(WPACKET *pkt, int tag, const RSA_PSS_PARAMS_30 *pss)
+{
+    int hashalg_nid, default_hashalg_nid;
+    int saltlen, default_saltlen;
+    int trailerfield, default_trailerfield;
+    const unsigned char *hashalg = NULL;
+    size_t hashalg_sz = 0;
+
+    /*
+     * For an unrestricted key, this function should not have been called;
+     * the caller must be in control, because unrestricted keys are permitted
+     * in some situations (when encoding the public key in a SubjectKeyInfo,
+     * for example) while not in others, and this function doesn't know the
+     * intent.  Therefore, we assert that here, the PSS parameters must show
+     * that the key is restricted.
+     */
+    if (!ossl_assert(pss != NULL && !rsa_pss_params_30_is_unrestricted(pss)))
+        return 0;
+
+    hashalg_nid = rsa_pss_params_30_hashalg(pss);
+    saltlen = rsa_pss_params_30_saltlen(pss);
+    trailerfield = rsa_pss_params_30_trailerfield(pss);
+
+    /* Getting default values */
+    default_hashalg_nid = rsa_pss_params_30_hashalg(NULL);
+    default_saltlen = rsa_pss_params_30_saltlen(NULL);
+    default_trailerfield = rsa_pss_params_30_trailerfield(NULL);
+
+    /*
+     * From https://tools.ietf.org/html/rfc8017#appendix-A.2.1:
+     *
+     * OAEP-PSSDigestAlgorithms    ALGORITHM-IDENTIFIER ::= {
+     *     { OID id-sha1       PARAMETERS NULL }|
+     *     { OID id-sha224     PARAMETERS NULL }|
+     *     { OID id-sha256     PARAMETERS NULL }|
+     *     { OID id-sha384     PARAMETERS NULL }|
+     *     { OID id-sha512     PARAMETERS NULL }|
+     *     { OID id-sha512-224 PARAMETERS NULL }|
+     *     { OID id-sha512-256 PARAMETERS NULL },
+     *     ...  -- Allows for future expansion --
+     * }
+     */
+    switch (hashalg_nid) {
+        OAEP_PSS_MD_CASE(sha1, hashalg);
+        OAEP_PSS_MD_CASE(sha224, hashalg);
+        OAEP_PSS_MD_CASE(sha256, hashalg);
+        OAEP_PSS_MD_CASE(sha384, hashalg);
+        OAEP_PSS_MD_CASE(sha512, hashalg);
+        OAEP_PSS_MD_CASE(sha512_224, hashalg);
+        OAEP_PSS_MD_CASE(sha512_256, hashalg);
+    default:
+        return 0;
+    }
+
+    return DER_w_begin_sequence(pkt, tag)
+        && (trailerfield == default_trailerfield
+            || DER_w_ulong(pkt, 3, trailerfield))
+        && (saltlen == default_saltlen || DER_w_ulong(pkt, 2, saltlen))
+        && DER_w_MaskGenAlgorithm(pkt, 1, pss)
+        && (hashalg_nid == default_hashalg_nid
+            || DER_w_precompiled(pkt, 0, hashalg, hashalg_sz))
+        && DER_w_end_sequence(pkt, tag);
+}
+
+/* Aliases so we can have a uniform RSA_CASE */
+#define der_oid_rsassaPss der_oid_id_RSASSA_PSS
+
+#define RSA_CASE(name, var)                                             \
+    var##_nid = NID_##name;                                             \
+    var##_oid = der_oid_##name;                                         \
+    var##_oid_sz = sizeof(der_oid_##name);                              \
+    break;
+
 int DER_w_algorithmIdentifier_RSA(WPACKET *pkt, int tag, RSA *rsa)
 {
+    int rsa_nid = NID_undef;
+    const unsigned char *rsa_oid = NULL;
+    size_t rsa_oid_sz = 0;
+    RSA_PSS_PARAMS_30 *pss_params = rsa_get0_pss_params_30(rsa);
+
+    switch (RSA_test_flags(rsa, RSA_FLAG_TYPE_MASK)) {
+    case RSA_FLAG_TYPE_RSA:
+        RSA_CASE(rsaEncryption, rsa);
+    case RSA_FLAG_TYPE_RSASSAPSS:
+        RSA_CASE(rsassaPss, rsa);
+    }
+
+    if (rsa_oid == NULL)
+        return 0;
+
     return DER_w_begin_sequence(pkt, tag)
-        /* No parameters (yet?) */
-        && DER_w_precompiled(pkt, -1, der_oid_rsaEncryption,
-                             sizeof(der_oid_rsaEncryption))
+        && (rsa_nid != NID_rsassaPss
+            || rsa_pss_params_30_is_unrestricted(pss_params)
+            || DER_w_RSASSA_PSS_params(pkt, -1, pss_params))
+        && DER_w_precompiled(pkt, -1, rsa_oid, rsa_oid_sz)
         && DER_w_end_sequence(pkt, tag);
 }
 
-/* Aliases so we can have a uniform MD_CASE */
+/* Aliases so we can have a uniform MD_with_RSA_CASE */
 #define der_oid_sha3_224WithRSAEncryption \
     der_oid_id_rsassa_pkcs1_v1_5_with_sha3_224
 #define der_oid_sha3_256WithRSAEncryption \
@@ -37,10 +393,10 @@ int DER_w_algorithmIdentifier_RSA(WPACKET *pkt, int tag, RSA *rsa)
 #define der_oid_sha3_512WithRSAEncryption \
     der_oid_id_rsassa_pkcs1_v1_5_with_sha3_512
 
-#define MD_CASE(name)                                                   \
+#define MD_with_RSA_CASE(name, var)                                     \
     case NID_##name:                                                    \
-        precompiled = der_oid_##name##WithRSAEncryption;                \
-        precompiled_sz = sizeof(der_oid_##name##WithRSAEncryption);     \
+        var = der_oid_##name##WithRSAEncryption;                        \
+        var##_sz = sizeof(der_oid_##name##WithRSAEncryption);           \
         break;
 
 int DER_w_algorithmIdentifier_RSA_with(WPACKET *pkt, int tag,
@@ -51,23 +407,23 @@ int DER_w_algorithmIdentifier_RSA_with(WPACKET *pkt, int tag,
 
     switch (mdnid) {
 #ifndef FIPS_MODULE
-        MD_CASE(md2);
-        MD_CASE(md5);
-        MD_CASE(md4);
-        MD_CASE(ripemd160);
+        MD_with_RSA_CASE(md2, precompiled);
+        MD_with_RSA_CASE(md5, precompiled);
+        MD_with_RSA_CASE(md4, precompiled);
+        MD_with_RSA_CASE(ripemd160, precompiled);
 /* TODO(3.0) Decide what to do about mdc2 and md5_sha1 */
 #endif
-        MD_CASE(sha1);
-        MD_CASE(sha224);
-        MD_CASE(sha256);
-        MD_CASE(sha384);
-        MD_CASE(sha512);
-        MD_CASE(sha512_224);
-        MD_CASE(sha512_256);
-        MD_CASE(sha3_224);
-        MD_CASE(sha3_256);
-        MD_CASE(sha3_384);
-        MD_CASE(sha3_512);
+        MD_with_RSA_CASE(sha1, precompiled);
+        MD_with_RSA_CASE(sha224, precompiled);
+        MD_with_RSA_CASE(sha256, precompiled);
+        MD_with_RSA_CASE(sha384, precompiled);
+        MD_with_RSA_CASE(sha512, precompiled);
+        MD_with_RSA_CASE(sha512_224, precompiled);
+        MD_with_RSA_CASE(sha512_256, precompiled);
+        MD_with_RSA_CASE(sha3_224, precompiled);
+        MD_with_RSA_CASE(sha3_256, precompiled);
+        MD_with_RSA_CASE(sha3_384, precompiled);
+        MD_with_RSA_CASE(sha3_512, precompiled);
     default:
         return 0;
     }
diff --git a/providers/common/der/der_rsa.h.in b/providers/common/der/der_rsa.h.in
index 3f7cc0e029..53f6227825 100644
--- a/providers/common/der/der_rsa.h.in
+++ b/providers/common/der/der_rsa.h.in
@@ -7,15 +7,20 @@
  * https://www.openssl.org/source/license.html
  */
 
+#include "crypto/rsa.h"
 #include "internal/der.h"
 
 /* Well known OIDs precompiled */
 {-
-    $OUT = oids_to_c::process_leaves('providers/common/der/RSA.asn1',
+    $OUT = oids_to_c::process_leaves('providers/common/der/NIST.asn1',
+                                     'providers/common/der/DIGESTS.asn1',
+                                     'providers/common/der/RSA.asn1',
                                      { dir => $config{sourcedir},
                                        filter => \&oids_to_c::filter_to_H });
 -}
 
+int DER_w_RSASSA_PSS_params(WPACKET *pkt, int tag,
+                            const RSA_PSS_PARAMS_30 *pss);
 int DER_w_algorithmIdentifier_RSA(WPACKET *pkt, int tag, RSA *rsa);
 int DER_w_algorithmIdentifier_RSA_with(WPACKET *pkt, int tag,
                                        RSA *rsa, int mdnid);
diff --git a/providers/common/include/prov/providercommonerr.h b/providers/common/include/prov/providercommonerr.h
index 5b3bcbb6a0..87bea503ab 100644
--- a/providers/common/include/prov/providercommonerr.h
+++ b/providers/common/include/prov/providercommonerr.h
@@ -10,6 +10,7 @@
 
 #ifndef OPENSSL_PROVERR_H
 # define OPENSSL_PROVERR_H
+# pragma once
 
 # include <openssl/opensslconf.h>
 # include <openssl/symhacks.h>
@@ -89,6 +90,7 @@ int ERR_load_PROV_strings(void);
 # define PROV_R_INVALID_PSS_SALTLEN                       169
 # define PROV_R_INVALID_SALT_LENGTH                       112
 # define PROV_R_INVALID_SEED_LENGTH                       154
+# define PROV_R_INVALID_SIGNATURE_SIZE                    179
 # define PROV_R_INVALID_TAG                               110
 # define PROV_R_INVALID_TAGLEN                            118
 # define PROV_R_INVALID_X931_DIGEST                       170
@@ -110,6 +112,7 @@ int ERR_load_PROV_strings(void);
 # define PROV_R_NOT_XOF_OR_INVALID_LENGTH                 113
 # define PROV_R_NO_KEY_SET                                114
 # define PROV_R_NO_PARAMETERS_SET                         177
+# define PROV_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE  178
 # define PROV_R_OUTPUT_BUFFER_TOO_SMALL                   106
 # define PROV_R_PSS_SALTLEN_TOO_SMALL                     172
 # define PROV_R_READ_KEY                                  159
diff --git a/providers/common/provider_err.c b/providers/common/provider_err.c
index 1018fa31ea..f79cdb0e0b 100644
--- a/providers/common/provider_err.c
+++ b/providers/common/provider_err.c
@@ -75,6 +75,8 @@ static const ERR_STRING_DATA PROV_str_reasons[] = {
     "invalid salt length"},
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_INVALID_SEED_LENGTH),
     "invalid seed length"},
+    {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_INVALID_SIGNATURE_SIZE),
+    "invalid signature size"},
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_INVALID_TAG), "invalid tag"},
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_INVALID_TAGLEN), "invalid taglen"},
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_INVALID_X931_DIGEST),
@@ -101,6 +103,8 @@ static const ERR_STRING_DATA PROV_str_reasons[] = {
     "not xof or invalid length"},
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_NO_KEY_SET), "no key set"},
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_NO_PARAMETERS_SET), "no parameters set"},
+    {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE),
+    "operation not supported for this keytype"},
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_OUTPUT_BUFFER_TOO_SMALL),
     "output buffer too small"},
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_PSS_SALTLEN_TOO_SMALL),
diff --git a/providers/defltprov.c b/providers/defltprov.c
index 5667825072..cedbddb80e 100644
--- a/providers/defltprov.c
+++ b/providers/defltprov.c
@@ -363,6 +363,7 @@ static const OSSL_ALGORITHM deflt_keymgmt[] = {
     { "DSA:dsaEncryption", "provider=default", dsa_keymgmt_functions },
 #endif
     { "RSA:rsaEncryption", "provider=default", rsa_keymgmt_functions },
+    { "RSA-PSS:RSASSA-PSS", "provider=default", rsapss_keymgmt_functions },
 #ifndef OPENSSL_NO_EC
     { "EC:id-ecPublicKey", "provider=default", ec_keymgmt_functions },
     { "X25519", "provider=default", x25519_keymgmt_functions },
@@ -391,6 +392,18 @@ static const OSSL_ALGORITHM deflt_serializer[] = {
       rsa_priv_pem_serializer_functions },
     { "RSA", "provider=default,fips=yes,format=pem,type=public",
       rsa_pub_pem_serializer_functions },
+    { "RSA-PSS", "provider=default,fips=yes,format=text,type=private",
+      rsa_priv_text_serializer_functions },
+    { "RSA-PSS", "provider=default,fips=yes,format=text,type=public",
+      rsa_pub_text_serializer_functions },
+    { "RSA-PSS", "provider=default,fips=yes,format=der,type=private",
+      rsa_priv_der_serializer_functions },
+    { "RSA-PSS", "provider=default,fips=yes,format=der,type=public",
+      rsa_pub_der_serializer_functions },
+    { "RSA-PSS", "provider=default,fips=yes,format=pem,type=private",
+      rsa_priv_pem_serializer_functions },
+    { "RSA-PSS", "provider=default,fips=yes,format=pem,type=public",
+      rsa_pub_pem_serializer_functions },
 
 #ifndef OPENSSL_NO_DH
     { "DH", "provider=default,fips=yes,format=text,type=private",
diff --git a/providers/fips/fipsprov.c b/providers/fips/fipsprov.c
index ac92b7885f..1d19c1b91a 100644
--- a/providers/fips/fipsprov.c
+++ b/providers/fips/fipsprov.c
@@ -477,6 +477,7 @@ static const OSSL_ALGORITHM fips_keymgmt[] = {
     { "DSA", "provider=fips,fips=yes", dsa_keymgmt_functions },
 #endif
     { "RSA:rsaEncryption", "provider=fips,fips=yes", rsa_keymgmt_functions },
+    { "RSA-PSS:RSASSA-PSS", "provider=default", rsapss_keymgmt_functions },
 #ifndef OPENSSL_NO_EC
     { "EC:id-ecPublicKey", "provider=fips,fips=yes", ec_keymgmt_functions },
     { "X25519", "provider=fips,fips=no", x25519_keymgmt_functions },
diff --git a/providers/implementations/asymciphers/rsa_enc.c b/providers/implementations/asymciphers/rsa_enc.c
index 0ca52ae743..f7e7b549f8 100644
--- a/providers/implementations/asymciphers/rsa_enc.c
+++ b/providers/implementations/asymciphers/rsa_enc.c
@@ -95,7 +95,16 @@ static int rsa_init(void *vprsactx, void *vrsa)
         return 0;
     RSA_free(prsactx->rsa);
     prsactx->rsa = vrsa;
-    prsactx->pad_mode = RSA_PKCS1_PADDING;
+
+    switch (RSA_test_flags(prsactx->rsa, RSA_FLAG_TYPE_MASK)) {
+    case RSA_FLAG_TYPE_RSA:
+        prsactx->pad_mode = RSA_PKCS1_PADDING;
+        break;
+    default:
+        ERR_raise(ERR_LIB_PROV, PROV_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+        return 0;
+    }
+
     return 1;
 }
 
diff --git a/providers/implementations/include/prov/implementations.h b/providers/implementations/include/prov/implementations.h
index 1f761e0ec4..0589a6e996 100644
--- a/providers/implementations/include/prov/implementations.h
+++ b/providers/implementations/include/prov/implementations.h
@@ -257,6 +257,7 @@ extern const OSSL_DISPATCH kdf_krb5kdf_functions[];
 extern const OSSL_DISPATCH dh_keymgmt_functions[];
 extern const OSSL_DISPATCH dsa_keymgmt_functions[];
 extern const OSSL_DISPATCH rsa_keymgmt_functions[];
+extern const OSSL_DISPATCH rsapss_keymgmt_functions[];
 extern const OSSL_DISPATCH x25519_keymgmt_functions[];
 extern const OSSL_DISPATCH x448_keymgmt_functions[];
 extern const OSSL_DISPATCH ed25519_keymgmt_functions[];
diff --git a/providers/implementations/keymgmt/rsa_kmgmt.c b/providers/implementations/keymgmt/rsa_kmgmt.c
index 181df998ad..295cdf61a4 100644
--- a/providers/implementations/keymgmt/rsa_kmgmt.c
+++ b/providers/implementations/keymgmt/rsa_kmgmt.c
@@ -26,9 +26,12 @@
 #include "internal/param_build_set.h"
 
 static OSSL_OP_keymgmt_new_fn rsa_newdata;
+static OSSL_OP_keymgmt_new_fn rsapss_newdata;
 static OSSL_OP_keymgmt_gen_init_fn rsa_gen_init;
+static OSSL_OP_keymgmt_gen_init_fn rsapss_gen_init;
 static OSSL_OP_keymgmt_gen_set_params_fn rsa_gen_set_params;
 static OSSL_OP_keymgmt_gen_settable_params_fn rsa_gen_settable_params;
+static OSSL_OP_keymgmt_gen_settable_params_fn rsapss_gen_settable_params;
 static OSSL_OP_keymgmt_gen_fn rsa_gen;
 static OSSL_OP_keymgmt_gen_cleanup_fn rsa_gen_cleanup;
 static OSSL_OP_keymgmt_free_fn rsa_freedata;
@@ -41,51 +44,53 @@ static OSSL_OP_keymgmt_import_fn rsa_import;
 static OSSL_OP_keymgmt_import_types_fn rsa_import_types;
 static OSSL_OP_keymgmt_export_fn rsa_export;
 static OSSL_OP_keymgmt_export_types_fn rsa_export_types;
+static OSSL_OP_keymgmt_query_operation_name_fn rsapss_query_operation_name;
 
 #define RSA_DEFAULT_MD "SHA256"
-#define RSA_POSSIBLE_SELECTIONS                                                \
+#define RSA_PSS_DEFAULT_MD OSSL_DIGEST_NAME_SHA1
+#define RSA_POSSIBLE_SELECTIONS                                        \
     (OSSL_KEYMGMT_SELECT_KEYPAIR | OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS)
 
 DEFINE_STACK_OF(BIGNUM)
 DEFINE_SPECIAL_STACK_OF_CONST(BIGNUM_const, BIGNUM)
 
-static int key_to_params(RSA *rsa, OSSL_PARAM_BLD *bld, OSSL_PARAM params[])
+static int pss_params_fromdata(RSA_PSS_PARAMS_30 *pss_params,
+                               const OSSL_PARAM params[], int rsa_type,
+                               OPENSSL_CTX *libctx)
 {
-    int ret = 0;
-    const BIGNUM *rsa_d = NULL, *rsa_n = NULL, *rsa_e = NULL;
-    STACK_OF(BIGNUM_const) *factors = sk_BIGNUM_const_new_null();
-    STACK_OF(BIGNUM_const) *exps = sk_BIGNUM_const_new_null();
-    STACK_OF(BIGNUM_const) *coeffs = sk_BIGNUM_const_new_null();
+    if (!rsa_pss_params_30_fromdata(pss_params, params, libctx))
+        return 0;
 
-    if (rsa == NULL || factors == NULL || exps == NULL || coeffs == NULL)
-        goto err;
+    /* If not a PSS type RSA, sending us PSS parameters is wrong */
+    if (rsa_type != RSA_FLAG_TYPE_RSASSAPSS
+        && !rsa_pss_params_30_is_unrestricted(pss_params))
+        return 0;
 
-    RSA_get0_key(rsa, &rsa_n, &rsa_e, &rsa_d);
-    rsa_get0_all_params(rsa, factors, exps, coeffs);
-
-    if (!ossl_param_build_set_bn(bld, params, OSSL_PKEY_PARAM_RSA_N, rsa_n)
-        || !ossl_param_build_set_bn(bld, params, OSSL_PKEY_PARAM_RSA_E, rsa_e)
-        || !ossl_param_build_set_bn(bld, params, OSSL_PKEY_PARAM_RSA_D, rsa_d)
-        || !ossl_param_build_set_multi_key_bn(bld, params, rsa_mp_factor_names,
-                                              factors)
-        || !ossl_param_build_set_multi_key_bn(bld, params, rsa_mp_exp_names,
-                                              exps)
-        || !ossl_param_build_set_multi_key_bn(bld, params, rsa_mp_coeff_names,
-                                              coeffs))
-        goto err;
-    ret = 1;
- err:
-    sk_BIGNUM_const_free(factors);
-    sk_BIGNUM_const_free(exps);
-    sk_BIGNUM_const_free(coeffs);
-    return ret;
+    return 1;
 }
 
 static void *rsa_newdata(void *provctx)
 {
     OPENSSL_CTX *libctx = PROV_LIBRARY_CONTEXT_OF(provctx);
+    RSA *rsa = rsa_new_with_ctx(libctx);
+
+    if (rsa != NULL) {
+        RSA_clear_flags(rsa, RSA_FLAG_TYPE_MASK);
+        RSA_set_flags(rsa, RSA_FLAG_TYPE_RSA);
+    }
+    return rsa;
+}
+
+static void *rsapss_newdata(void *provctx)
+{
+    OPENSSL_CTX *libctx = PROV_LIBRARY_CONTEXT_OF(provctx);
+    RSA *rsa = rsa_new_with_ctx(libctx);
 
-    return rsa_new_with_ctx(libctx);
+    if (rsa != NULL) {
+        RSA_clear_flags(rsa, RSA_FLAG_TYPE_MASK);
+        RSA_set_flags(rsa, RSA_FLAG_TYPE_RSASSAPSS);
+    }
+    return rsa;
 }
 
 static void rsa_freedata(void *keydata)
@@ -103,7 +108,8 @@ static int rsa_has(void *keydata, int selection)
             ok = 1;
 
         if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0)
-            ok = ok && 0;     /* This will change with PSS and OAEP */
+            /* This will change with OAEP */
+            ok = ok && (RSA_test_flags(rsa, RSA_FLAG_TYPE_RSASSAPSS) != 0);
         if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)
             ok = ok && (RSA_get0_e(rsa) != NULL);
         if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
@@ -132,13 +138,22 @@ static int rsa_match(const void *keydata1, const void *keydata2, int selection)
 static int rsa_import(void *keydata, int selection, const OSSL_PARAM params[])
 {
     RSA *rsa = keydata;
+    int rsa_type;
     int ok = 1;
 
     if (rsa == NULL)
         return 0;
 
-    /* TODO(3.0) PSS and OAEP should bring on parameters */
+    if ((selection & RSA_POSSIBLE_SELECTIONS) == 0)
+        return 0;
+
+    rsa_type = RSA_test_flags(rsa, RSA_FLAG_TYPE_MASK);
+
+    /* TODO(3.0) OAEP should bring on parameters as well */
 
+    if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0)
+        ok = ok && pss_params_fromdata(rsa_get0_pss_params_30(rsa), params,
+                                       rsa_type, rsa_get0_libctx(rsa));
     if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)
         ok = ok && rsa_fromdata(rsa, params);
 
@@ -149,6 +164,7 @@ static int rsa_export(void *keydata, int selection,
                       OSSL_CALLBACK *param_callback, void *cbarg)
 {
     RSA *rsa = keydata;
+    const RSA_PSS_PARAMS_30 *pss_params = rsa_get0_pss_params_30(rsa);
     OSSL_PARAM_BLD *tmpl;
     OSSL_PARAM *params = NULL;
     int ok = 1;
@@ -156,14 +172,17 @@ static int rsa_export(void *keydata, int selection,
     if (rsa == NULL)
         return 0;
 
-    /* TODO(3.0) PSS and OAEP should bring on parameters */
+    /* TODO(3.0) OAEP should bring on parameters */
 
     tmpl = OSSL_PARAM_BLD_new();
     if (tmpl == NULL)
         return 0;
 
+    if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0)
+        ok = ok && (rsa_pss_params_30_is_unrestricted(pss_params)
+                    || rsa_pss_params_30_todata(pss_params, NULL, tmpl, NULL));
     if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)
-        ok = ok && key_to_params(rsa, tmpl, NULL);
+        ok = ok && rsa_todata(rsa, tmpl, NULL);
 
     if (!ok
         || (params = OSSL_PARAM_BLD_to_param(tmpl)) == NULL)
@@ -265,6 +284,8 @@ static const OSSL_PARAM *rsa_export_types(int selection)
 static int rsa_get_params(void *key, OSSL_PARAM params[])
 {
     RSA *rsa = key;
+    const RSA_PSS_PARAMS_30 *pss_params = rsa_get0_pss_params_30(rsa);
+    int rsa_type = RSA_test_flags(rsa, RSA_FLAG_TYPE_MASK);
     OSSL_PARAM *p;
 
     if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS)) != NULL
@@ -277,32 +298,36 @@ static int rsa_get_params(void *key, OSSL_PARAM params[])
         && !OSSL_PARAM_set_int(p, RSA_size(rsa)))
         return 0;
 
-# if 0  /* TODO(3.0): PSS support pending */
+    /*
+     * For RSA-PSS keys, we ignore the default digest request
+     * TODO(3.0) with RSA-OAEP keys, this may need to be amended
+     */
+    if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_DEFAULT_DIGEST)) != NULL
+        && rsa_type != RSA_FLAG_TYPE_RSASSAPSS) {
+        if (!OSSL_PARAM_set_utf8_string(p, RSA_DEFAULT_MD))
+            return 0;
+    }
+
+    /*
+     * For non-RSA-PSS keys, we ignore the mandatory digest request
+     * TODO(3.0) with RSA-OAEP keys, this may need to be amended
+     */
     if ((p = OSSL_PARAM_locate(params,
                                OSSL_PKEY_PARAM_MANDATORY_DIGEST)) != NULL
-        && RSA_get0_pss_params(rsa) != NULL) {
-        const EVP_MD *md, *mgf1md;
-        int min_saltlen;
+        && rsa_type == RSA_FLAG_TYPE_RSASSAPSS) {
+        const char *mdname = RSA_PSS_DEFAULT_MD;
 
-        if (!rsa_pss_get_param(RSA_get0_pss_params(rsa),
-                               &md, &mgf1md, &min_saltlen)) {
-            ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR);
-            return 0;
+        if (!rsa_pss_params_30_is_unrestricted(pss_params)) {
+            mdname =
+                rsa_oaeppss_nid2name(rsa_pss_params_30_hashalg(pss_params));
+
+            if (mdname == NULL || !OSSL_PARAM_set_utf8_string(p, mdname))
+                return 0;
         }
-        if (!OSSL_PARAM_set_utf8_string(p, EVP_MD_name(md)))
-            return 0;
-    }
-#endif
-    if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_DEFAULT_DIGEST)) != NULL
-/* TODO(3.0): PSS support pending */
-#if 0
-            && RSA_get0_pss_params(rsa) == NULL
-#endif
-            ) {
-        if (!OSSL_PARAM_set_utf8_string(p, RSA_DEFAULT_MD))
-            return 0;
     }
-    return key_to_params(rsa, NULL, params);
+    return (rsa_type != RSA_FLAG_TYPE_RSASSAPSS
+            || rsa_pss_params_30_todata(pss_params, NULL, NULL, params))
+        && rsa_todata(rsa, NULL, params);
 }
 
 static const OSSL_PARAM rsa_params[] = {
@@ -343,10 +368,15 @@ static int rsa_validate(void *keydata, int selection)
 struct rsa_gen_ctx {
     OPENSSL_CTX *libctx;
 
+    int rsa_type;
+
     size_t nbits;
     BIGNUM *pub_exp;
     size_t primes;
 
+    /* For PSS */
+    RSA_PSS_PARAMS_30 pss_params;
+
     /* For generation callback */
     OSSL_CALLBACK *cb;
     void *cbarg;
@@ -363,7 +393,7 @@ static int rsa_gencb(int p, int n, BN_GENCB *cb)
     return gctx->cb(params, gctx->cbarg);
 }
 
-static void *rsa_gen_init(void *provctx, int selection)
+static void *gen_init(void *provctx, int selection, int rsa_type)
 {
     OPENSSL_CTX *libctx = PROV_LIBRARY_CONTEXT_OF(provctx);
     struct rsa_gen_ctx *gctx = NULL;
@@ -382,10 +412,26 @@ static void *rsa_gen_init(void *provctx, int selection)
             gctx->nbits = 2048;
             gctx->primes = RSA_DEFAULT_PRIME_NUM;
         }
+        gctx->rsa_type = rsa_type;
     }
     return gctx;
 }
 
+static void *rsa_gen_init(void *provctx, int selection)
+{
+    return gen_init(provctx, selection, RSA_FLAG_TYPE_RSA);
+}
+
+static void *rsapss_gen_init(void *provctx, int selection)
+{
+    return gen_init(provctx, selection, RSA_FLAG_TYPE_RSASSAPSS);
+}
+
+/*
+ * This function is common for all RSA sub-types, to detect possible
+ * misuse, such as PSS parameters being passed when a plain RSA key
+ * is generated.
+ */
 static int rsa_gen_set_params(void *genctx, const OSSL_PARAM params[])
 {
     struct rsa_gen_ctx *gctx = genctx;
@@ -400,15 +446,44 @@ static int rsa_gen_set_params(void *genctx, const OSSL_PARAM params[])
     if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_E)) != NULL
         && !OSSL_PARAM_get_BN(p, &gctx->pub_exp))
         return 0;
+    /* Only attempt to get PSS parameters when generating an RSA-PSS key */
+    if (gctx->rsa_type == RSA_FLAG_TYPE_RSASSAPSS
+        && !pss_params_fromdata(&gctx->pss_params, params, gctx->rsa_type,
+                                gctx->libctx))
+        return 0;
     return 1;
 }
 
+#define rsa_gen_basic                                           \
+    OSSL_PARAM_size_t(OSSL_PKEY_PARAM_RSA_BITS, NULL),          \
+    OSSL_PARAM_size_t(OSSL_PKEY_PARAM_RSA_PRIMES, NULL),        \
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_E, NULL, 0)
+
+/*
+ * The following must be kept in sync with rsa_pss_params_30_fromdata()
+ * in crypto/rsa/rsa_backend.c
+ */
+#define rsa_gen_pss                                                     \
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_DIGEST, NULL, 0),        \
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_MASKGENFUNC, NULL, 0),   \
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_MGF1_DIGEST, NULL, 0),   \
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_RSA_PSS_SALTLEN, NULL)
+
 static const OSSL_PARAM *rsa_gen_settable_params(void *provctx)
 {
     static OSSL_PARAM settable[] = {
-        OSSL_PARAM_size_t(OSSL_PKEY_PARAM_RSA_BITS, NULL),
-        OSSL_PARAM_size_t(OSSL_PKEY_PARAM_RSA_PRIMES, NULL),
-        OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_E, NULL, 0),
+        rsa_gen_basic,
+        OSSL_PARAM_END
+    };
+
+    return settable;
+}
+
+static const OSSL_PARAM *rsapss_gen_settable_params(void *provctx)
+{
+    static OSSL_PARAM settable[] = {
+        rsa_gen_basic,
+        rsa_gen_pss,
         OSSL_PARAM_END
     };
 
@@ -418,11 +493,28 @@ static const OSSL_PARAM *rsa_gen_settable_params(void *provctx)
 static void *rsa_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg)
 {
     struct rsa_gen_ctx *gctx = genctx;
-    RSA *rsa = NULL;
+    RSA *rsa = NULL, *rsa_tmp = NULL;
     BN_GENCB *gencb = NULL;
 
+    switch (gctx->rsa_type) {
+    case RSA_FLAG_TYPE_RSA:
+        /* For plain RSA keys, PSS parameters must not be set */
+        if (!rsa_pss_params_30_is_unrestricted(&gctx->pss_params))
+            goto err;
+        break;
+    case RSA_FLAG_TYPE_RSASSAPSS:
+        /*
+         * For plain RSA-PSS keys, PSS parameters may be set but don't have
+         * to, so not check.
+         */
+        break;
+    default:
+        /* Unsupported RSA key sub-type... */
+        return NULL;
+    }
+
     if (gctx == NULL
-        || (rsa = rsa_new_with_ctx(gctx->libctx)) == NULL)
+        || (rsa_tmp = rsa_new_with_ctx(gctx->libctx)) == NULL)
         return NULL;
 
     gctx->cb = osslcb;
@@ -431,14 +523,23 @@ static void *rsa_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg)
     if (gencb != NULL)
         BN_GENCB_set(gencb, rsa_gencb, genctx);
 
-    if (!RSA_generate_multi_prime_key(rsa, (int)gctx->nbits, (int)gctx->primes,
-                                      gctx->pub_exp, gencb)) {
-        RSA_free(rsa);
-        rsa = NULL;
-    }
+    if (!RSA_generate_multi_prime_key(rsa_tmp,
+                                      (int)gctx->nbits, (int)gctx->primes,
+                                      gctx->pub_exp, gencb))
+        goto err;
 
-    BN_GENCB_free(gencb);
+    if (!rsa_pss_params_30_copy(rsa_get0_pss_params_30(rsa_tmp),
+                                &gctx->pss_params))
+        goto err;
 
+    RSA_clear_flags(rsa_tmp, RSA_FLAG_TYPE_MASK);
+    RSA_set_flags(rsa_tmp, gctx->rsa_type);
+
+    rsa = rsa_tmp;
+    rsa_tmp = NULL;
+ err:
+    BN_GENCB_free(gencb);
+    RSA_free(rsa_tmp);
     return rsa;
 }
 
@@ -453,6 +554,12 @@ static void rsa_gen_cleanup(void *genctx)
     OPENSSL_free(gctx);
 }
 
+/* For any RSA key, we use the "RSA" algorithms regardless of sub-type. */
+static const char *rsapss_query_operation_name(int operation_id)
+{
+    return "RSA";
+}
+
 const OSSL_DISPATCH rsa_keymgmt_functions[] = {
     { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))rsa_newdata },
     { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))rsa_gen_init },
@@ -474,3 +581,25 @@ const OSSL_DISPATCH rsa_keymgmt_functions[] = {
     { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))rsa_export_types },
     { 0, NULL }
 };
+
+const OSSL_DISPATCH rsapss_keymgmt_functions[] = {
+    { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))rsapss_newdata },
+    { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))rsapss_gen_init },
+    { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))rsa_gen_set_params },
+    { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS,
+      (void (*)(void))rsapss_gen_settable_params },
+    { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))rsa_gen },
+    { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))rsa_gen_cleanup },
+    { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))rsa_freedata },
+    { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))rsa_get_params },
+    { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))rsa_gettable_params },
+    { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))rsa_has },
+    { OSSL_FUNC_KEYMGMT_VALIDATE, (void (*)(void))rsa_validate },
+    { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))rsa_import },
+    { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))rsa_import_types },
+    { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))rsa_export },
+    { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))rsa_export_types },
+    { OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME,
+      (void (*)(void))rsapss_query_operation_name },
+    { 0, NULL }
+};
diff --git a/providers/implementations/serializers/build.info b/providers/implementations/serializers/build.info
index 097bdcac1c..ffafbe38e5 100644
--- a/providers/implementations/serializers/build.info
+++ b/providers/implementations/serializers/build.info
@@ -10,7 +10,10 @@ $ECX_GOAL=../../libimplementations.a
 $EC_GOAL=../../libimplementations.a
 
 SOURCE[$SERIALIZER_GOAL]=serializer_common.c
+
 SOURCE[$RSA_GOAL]=serializer_rsa.c serializer_rsa_priv.c serializer_rsa_pub.c
+DEPEND[serializer_rsa.o]=../../common/include/prov/der_rsa.h
+
 IF[{- !$disabled{"dh"} || !$disabled{"dsa"} -}]
   SOURCE[$FFC_GOAL]=serializer_ffc_params.c
 ENDIF
diff --git a/providers/implementations/serializers/serializer_local.h b/providers/implementations/serializers/serializer_local.h
index 39fb3ab1e7..f4aee6fc23 100644
--- a/providers/implementations/serializers/serializer_local.h
+++ b/providers/implementations/serializers/serializer_local.h
@@ -82,6 +82,14 @@ int ossl_prov_prepare_all_dsa_params(const void *dsa, int nid,
 int ossl_prov_dsa_pub_to_der(const void *dsa, unsigned char **pder);
 int ossl_prov_dsa_priv_to_der(const void *dsa, unsigned char **pder);
 
+/*
+ * ossl_prov_prepare_rsa_params() is designed to work with the ossl_prov_write_
+ * functions, hence 'void *rsa' rather than 'RSA *rsa'.
+ */
+int ossl_prov_prepare_rsa_params(const void *rsa, int nid,
+                                 void **pstr, int *pstrtype);
+int ossl_prov_rsa_type_to_evp(const RSA *rsa);
+
 int ossl_prov_print_labeled_bignum(BIO *out, const char *label,
                                    const BIGNUM *bn);
 int ossl_prov_print_labeled_buf(BIO *out, const char *label,
diff --git a/providers/implementations/serializers/serializer_rsa.c b/providers/implementations/serializers/serializer_rsa.c
index 7578fec7c2..564210ede2 100644
--- a/providers/implementations/serializers/serializer_rsa.c
+++ b/providers/implementations/serializers/serializer_rsa.c
@@ -13,8 +13,10 @@
  */
 #include "internal/deprecated.h"
 
+#include "internal/packet.h"
 #include "crypto/rsa.h"           /* rsa_get0_all_params() */
 #include "prov/bio.h"             /* ossl_prov_bio_printf() */
+#include "prov/der_rsa.h"         /* DER_w_RSASSA_PSS_params() */
 #include "prov/implementations.h" /* rsa_keymgmt_functions */
 #include "serializer_local.h"
 
@@ -43,6 +45,7 @@ int ossl_prov_print_rsa(BIO *out, RSA *rsa, int priv)
     STACK_OF(BIGNUM_const) *factors = sk_BIGNUM_const_new_null();
     STACK_OF(BIGNUM_const) *exps = sk_BIGNUM_const_new_null();
     STACK_OF(BIGNUM_const) *coeffs = sk_BIGNUM_const_new_null();
+    RSA_PSS_PARAMS_30 *pss_params = rsa_get0_pss_params_30(rsa);
     int ret = 0;
 
     if (rsa == NULL || factors == NULL || exps == NULL || coeffs == NULL)
@@ -109,6 +112,61 @@ int ossl_prov_print_rsa(BIO *out, RSA *rsa, int priv)
                 goto err;
         }
     }
+
+    switch (RSA_test_flags(rsa, RSA_FLAG_TYPE_MASK)) {
+    case RSA_FLAG_TYPE_RSA:
+        if (!rsa_pss_params_30_is_unrestricted(pss_params)) {
+            if (ossl_prov_bio_printf(out, "(INVALID PSS PARAMETERS)\n") <= 0)
+                goto err;
+        }
+        break;
+    case RSA_FLAG_TYPE_RSASSAPSS:
+        if (rsa_pss_params_30_is_unrestricted(pss_params)) {
+            if (ossl_prov_bio_printf(out,
+                                     "No PSS parameter restrictions\n") <= 0)
+                goto err;
+        } else {
+            int hashalg_nid = rsa_pss_params_30_hashalg(pss_params);
+            int maskgenalg_nid = rsa_pss_params_30_maskgenalg(pss_params);
+            int maskgenhashalg_nid =
+                rsa_pss_params_30_maskgenhashalg(pss_params);
+            int saltlen = rsa_pss_params_30_saltlen(pss_params);
+            int trailerfield = rsa_pss_params_30_trailerfield(pss_params);
+
+            if (ossl_prov_bio_printf(out, "PSS parameter restrictions:\n") <= 0)
+                goto err;
+            if (ossl_prov_bio_printf(out, "  Hash Algorithm: %s%s\n",
+                                     rsa_oaeppss_nid2name(hashalg_nid),
+                                     (hashalg_nid == NID_sha1
+                                      ? " (default)" : "")) <= 0)
+                goto err;
+            if (ossl_prov_bio_printf(out, "  Mask Algorithm: %s with %s%s\n",
+                                     rsa_mgf_nid2name(maskgenalg_nid),
+                                     rsa_oaeppss_nid2name(maskgenhashalg_nid),
+                                     (maskgenalg_nid == NID_mgf1
+                                      && maskgenhashalg_nid == NID_sha1
+                                      ? " (default)" : "")) <= 0)
+                goto err;
+            if (ossl_prov_bio_printf(out, "  Minimum Salt Length: %d%s\n",
+                                     saltlen,
+                                     (saltlen == 20 ? " (default)" : "")) <= 0)
+                goto err;
+            /*
+             * TODO(3.0) Should we show the ASN.1 trailerField value, or
+             * the actual trailerfield byte (i.e. 0xBC for 1)?
+             * crypto/rsa/rsa_ameth.c isn't very clear on that, as it
+             * does display 0xBC when the default applies, but the ASN.1
+             * trailerField value otherwise...
+             */
+            if (ossl_prov_bio_printf(out, "  Trailer Field: 0x%x%s\n",
+                                     trailerfield,
+                                     (trailerfield == 1 ? " (default)" : ""))
+                <= 0)
+                goto err;
+        }
+        break;
+    }
+
     ret = 1;
  err:
     sk_BIGNUM_const_free(factors);
@@ -116,3 +174,90 @@ int ossl_prov_print_rsa(BIO *out, RSA *rsa, int priv)
     sk_BIGNUM_const_free(coeffs);
     return ret;
 }
+
+/*
+ * Helper functions to prepare RSA-PSS params for serialization.  We would
+ * have simply written the whole AlgorithmIdentifier, but existing libcrypto
+ * functionality doesn't allow that.
+ */
+
+int ossl_prov_prepare_rsa_params(const void *rsa, int nid,
+                                 void **pstr, int *pstrtype)
+{
+    const RSA_PSS_PARAMS_30 *pss = rsa_get0_pss_params_30((RSA *)rsa);
+
+    *pstr = NULL;
+
+    switch (RSA_test_flags(rsa, RSA_FLAG_TYPE_MASK)) {
+    case RSA_FLAG_TYPE_RSA:
+        /* If plain RSA, the parameters shall be NULL */
+        *pstrtype = V_ASN1_NULL;
+        return 1;
+    case RSA_FLAG_TYPE_RSASSAPSS:
+        if (rsa_pss_params_30_is_unrestricted(pss)) {
+            *pstrtype = V_ASN1_UNDEF;
+        } else {
+            ASN1_STRING *astr = NULL;
+            WPACKET pkt;
+            unsigned char *str = NULL;
+            size_t str_sz = 0;
+            int i;
+
+            for (i = 0; i < 2; i++) {
+                switch (i) {
+                case 0:
+                    if (!WPACKET_init_null_der(&pkt))
+                        goto err;
+                    break;
+                case 1:
+                    if ((str = OPENSSL_malloc(str_sz)) == NULL
+                        || !WPACKET_init_der(&pkt, str, str_sz)) {
+                        goto err;
+                    }
+                    break;
+                }
+                if (!DER_w_RSASSA_PSS_params(&pkt, -1, pss)
+                    || !WPACKET_finish(&pkt))
+                    goto err;
+                WPACKET_get_total_written(&pkt, &str_sz);
+                WPACKET_cleanup(&pkt);
+
+                /*
+                 * If no PSS parameters are going to be written, there's no
+                 * point going for another iteration.
+                 * This saves us from getting |str| allocated just to have it
+                 * immediately de-allocated.
+                 */
+                if (str_sz == 0)
+                    break;
+            }
+
+            if ((astr = ASN1_STRING_new()) == NULL)
+                goto err;
+            *pstrtype = V_ASN1_SEQUENCE;
+            ASN1_STRING_set0(astr, str, (int)str_sz);
+            *pstr = astr;
+
+            return 1;
+         err:
+            OPENSSL_free(str);
+            return 0;
+        }
+    }
+
+    /* Currently unsupported RSA key type */
+    return 0;
+}
+
+int ossl_prov_rsa_type_to_evp(const RSA *rsa)
+{
+    switch (RSA_test_flags(rsa, RSA_FLAG_TYPE_MASK)) {
+    case RSA_FLAG_TYPE_RSA:
+        return EVP_PKEY_RSA;
+    case RSA_FLAG_TYPE_RSASSAPSS:
+        return EVP_PKEY_RSA_PSS;
+    }
+
+    /* Currently unsupported RSA key type */
+    return EVP_PKEY_NONE;
+}
diff --git a/providers/implementations/serializers/serializer_rsa_priv.c b/providers/implementations/serializers/serializer_rsa_priv.c
index 8196473a04..8c68f5de34 100644
--- a/providers/implementations/serializers/serializer_rsa_priv.c
+++ b/providers/implementations/serializers/serializer_rsa_priv.c
@@ -21,6 +21,7 @@
 #include <openssl/types.h>
 #include <openssl/params.h>
 #include <openssl/safestack.h>
+#include "crypto/rsa.h"
 #include "prov/bio.h"
 #include "prov/implementations.h"
 #include "prov/providercommonerr.h"
@@ -49,34 +50,6 @@ struct rsa_priv_ctx_st {
     struct pkcs8_encrypt_ctx_st sc;
 };
 
-/* Helper functions to prepare RSA-PSS params for serialization */
-
-static int prepare_rsa_params(const void *rsa, int nid,
-                              void **pstr, int *pstrtype)
-{
-    const RSA_PSS_PARAMS *pss = RSA_get0_pss_params(rsa);
-    *pstr = NULL;
-
-    /* If RSA it's just NULL type */
-    if (nid != EVP_PKEY_RSA_PSS) {
-        *pstrtype = V_ASN1_NULL;
-        return 1;
-    }
-    /* If no PSS parameters we omit parameters entirely */
-    if (pss == NULL) {
-        *pstrtype = V_ASN1_UNDEF;
-        return 1;
-    }
-    /* Encode PSS parameters */
-    if (ASN1_item_pack((void *)pss, ASN1_ITEM_rptr(RSA_PSS_PARAMS),
-                       (ASN1_STRING **)pstr)
-        == NULL)
-        return 0;
-
-    *pstrtype = V_ASN1_SEQUENCE;
-    return 1;
-}
-
 /* Private key : context */
 static void *rsa_priv_newctx(void *provctx)
 {
@@ -176,8 +149,9 @@ static int rsa_priv_der(void *vctx, void *rsa, BIO *out,
     ctx->sc.cb = cb;
     ctx->sc.cbarg = cbarg;
 
-    ret = ossl_prov_write_priv_der_from_obj(out, rsa, EVP_PKEY_RSA,
-                                            prepare_rsa_params,
+    ret = ossl_prov_write_priv_der_from_obj(out, rsa,
+                                            ossl_prov_rsa_type_to_evp(rsa),
+                                            ossl_prov_prepare_rsa_params,
                                             (i2d_of_void *)i2d_RSAPrivateKey,
                                             &ctx->sc);
 
@@ -215,8 +189,9 @@ static int rsa_pem_priv(void *vctx, void *rsa, BIO *out,
     ctx->sc.cb = cb;
     ctx->sc.cbarg = cbarg;
 
-    ret = ossl_prov_write_priv_pem_from_obj(out, rsa, EVP_PKEY_RSA,
-                                            prepare_rsa_params,
+    ret = ossl_prov_write_priv_pem_from_obj(out, rsa,
+                                            ossl_prov_rsa_type_to_evp(rsa),
+                                            ossl_prov_prepare_rsa_params,
                                             (i2d_of_void *)i2d_RSAPrivateKey,
                                             &ctx->sc);
 
diff --git a/providers/implementations/serializers/serializer_rsa_pub.c b/providers/implementations/serializers/serializer_rsa_pub.c
index bcae074480..28df00877e 100644
--- a/providers/implementations/serializers/serializer_rsa_pub.c
+++ b/providers/implementations/serializers/serializer_rsa_pub.c
@@ -72,7 +72,10 @@ static int rsa_pub_der_data(void *ctx, const OSSL_PARAM params[], BIO *out,
 static int rsa_pub_der(void *ctx, void *rsa, BIO *out,
                        OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
 {
-    return i2d_RSA_PUBKEY_bio(out, rsa);
+    return ossl_prov_write_pub_der_from_obj(out, rsa,
+                                            ossl_prov_rsa_type_to_evp(rsa),
+                                            ossl_prov_prepare_rsa_params,
+                                            (i2d_of_void *)i2d_RSAPublicKey);
 }
 
 /* Public key : PEM */
@@ -100,7 +103,10 @@ static int rsa_pub_pem_data(void *ctx, const OSSL_PARAM params[], BIO *out,
 static int rsa_pub_pem(void *ctx, void *rsa, BIO *out,
                        OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
 {
-    return PEM_write_bio_RSA_PUBKEY(out, rsa);
+    return ossl_prov_write_pub_pem_from_obj(out, rsa,
+                                            ossl_prov_rsa_type_to_evp(rsa),
+                                            ossl_prov_prepare_rsa_params,
+                                            (i2d_of_void *)i2d_RSAPublicKey);
 }
 
 static int rsa_pub_print_data(void *ctx, const OSSL_PARAM params[], BIO *out,
diff --git a/providers/implementations/signature/dsa.c b/providers/implementations/signature/dsa.c
index f0662d1ee8..bfab22488f 100644
--- a/providers/implementations/signature/dsa.c
+++ b/providers/implementations/signature/dsa.c
@@ -63,6 +63,7 @@ static OSSL_OP_signature_settable_ctx_md_params_fn dsa_settable_ctx_md_params;
 
 typedef struct {
     OPENSSL_CTX *libctx;
+    char *propq;
     DSA *dsa;
 
     /*
@@ -131,7 +132,7 @@ static int dsa_get_md_nid(const EVP_MD *md)
     return mdnid;
 }
 
-static void *dsa_newctx(void *provctx)
+static void *dsa_newctx(void *provctx, const char *propq)
 {
     PROV_DSA_CTX *pdsactx = OPENSSL_zalloc(sizeof(PROV_DSA_CTX));
 
@@ -140,12 +141,20 @@ static void *dsa_newctx(void *provctx)
 
     pdsactx->libctx = PROV_LIBRARY_CONTEXT_OF(provctx);
     pdsactx->flag_allow_md = 1;
+    if (propq != NULL && (pdsactx->propq = OPENSSL_strdup(propq)) == NULL) {
+        OPENSSL_free(pdsactx);
+        pdsactx = NULL;
+        ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
+    }
     return pdsactx;
 }
 
 static int dsa_setup_md(PROV_DSA_CTX *ctx,
                         const char *mdname, const char *mdprops)
 {
+    if (mdprops == NULL)
+        mdprops = ctx->propq;
+
     if (mdname != NULL) {
         EVP_MD *md = EVP_MD_fetch(ctx->libctx, mdname, mdprops);
         int md_nid = dsa_get_md_nid(md);
@@ -234,7 +243,7 @@ static int dsa_verify(void *vpdsactx, const unsigned char *sig, size_t siglen,
 }
 
 static int dsa_digest_signverify_init(void *vpdsactx, const char *mdname,
-                                      const char *props, void *vdsa)
+                                      void *vdsa)
 {
     PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx;
 
@@ -242,7 +251,7 @@ static int dsa_digest_signverify_init(void *vpdsactx, const char *mdname,
     if (!dsa_signature_init(vpdsactx, vdsa))
         return 0;
 
-    if (!dsa_setup_md(pdsactx, mdname, props))
+    if (!dsa_setup_md(pdsactx, mdname, NULL))
         return 0;
 
     pdsactx->mdctx = EVP_MD_CTX_new();
diff --git a/providers/implementations/signature/ecdsa.c b/providers/implementations/signature/ecdsa.c
index e05830f500..267950d537 100644
--- a/providers/implementations/signature/ecdsa.c
+++ b/providers/implementations/signature/ecdsa.c
@@ -60,6 +60,7 @@ static OSSL_OP_signature_settable_ctx_md_params_fn ecdsa_settable_ctx_md_params;
 
 typedef struct {
     OPENSSL_CTX *libctx;
+    char *propq;
     EC_KEY *ec;
     char mdname[OSSL_MAX_NAME_SIZE];
 
@@ -90,7 +91,7 @@ typedef struct {
     BIGNUM *r;
 } PROV_ECDSA_CTX;
 
-static void *ecdsa_newctx(void *provctx)
+static void *ecdsa_newctx(void *provctx, const char *propq)
 {
     PROV_ECDSA_CTX *ctx = OPENSSL_zalloc(sizeof(PROV_ECDSA_CTX));
 
@@ -98,6 +99,11 @@ static void *ecdsa_newctx(void *provctx)
         return NULL;
 
     ctx->libctx = PROV_LIBRARY_CONTEXT_OF(provctx);
+    if (propq != NULL && (ctx->propq = OPENSSL_strdup(propq)) == NULL) {
+        OPENSSL_free(ctx);
+        ctx = NULL;
+        ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
+    }
     return ctx;
 }
 
@@ -203,7 +209,7 @@ static void free_md(PROV_ECDSA_CTX *ctx)
 }
 
 static int ecdsa_digest_signverify_init(void *vctx, const char *mdname,
-                                        const char *props, void *ec)
+                                        void *ec)
 {
     PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx;
     int md_nid = NID_undef;
@@ -214,7 +220,7 @@ static int ecdsa_digest_signverify_init(void *vctx, const char *mdname,
     if (!ecdsa_signature_init(vctx, ec))
         return 0;
 
-    ctx->md = EVP_MD_fetch(ctx->libctx, mdname, props);
+    ctx->md = EVP_MD_fetch(ctx->libctx, mdname, ctx->propq);
     if ((md_nid = get_md_nid(ctx->md)) == NID_undef)
         goto error;
 
diff --git a/providers/implementations/signature/eddsa.c b/providers/implementations/signature/eddsa.c
index 1a7bf94702..4ecc5266e2 100644
--- a/providers/implementations/signature/eddsa.c
+++ b/providers/implementations/signature/eddsa.c
@@ -36,7 +36,7 @@ typedef struct {
     ECX_KEY *key;
 } PROV_EDDSA_CTX;
 
-static void *eddsa_newctx(void *provctx)
+static void *eddsa_newctx(void *provctx, const char *propq_unused)
 {
     PROV_EDDSA_CTX *peddsactx = OPENSSL_zalloc(sizeof(PROV_EDDSA_CTX));
 
@@ -51,7 +51,7 @@ static void *eddsa_newctx(void *provctx)
 }
 
 static int eddsa_digest_signverify_init(void *vpeddsactx, const char *mdname,
-                                        const char *props, void *vedkey)
+                                        void *vedkey)
 {
     PROV_EDDSA_CTX *peddsactx = (PROV_EDDSA_CTX *)vpeddsactx;
     ECX_KEY *edkey = (ECX_KEY *)vedkey;
diff --git a/providers/implementations/signature/rsa.c b/providers/implementations/signature/rsa.c
index 0670447480..4dc3a89878 100644
--- a/providers/implementations/signature/rsa.c
+++ b/providers/implementations/signature/rsa.c
@@ -31,16 +31,16 @@
 #include "prov/der_rsa.h"
 
 static OSSL_OP_signature_newctx_fn rsa_newctx;
-static OSSL_OP_signature_sign_init_fn rsa_signature_init;
-static OSSL_OP_signature_verify_init_fn rsa_signature_init;
-static OSSL_OP_signature_verify_recover_init_fn rsa_signature_init;
+static OSSL_OP_signature_sign_init_fn rsa_sign_init;
+static OSSL_OP_signature_verify_init_fn rsa_verify_init;
+static OSSL_OP_signature_verify_recover_init_fn rsa_verify_recover_init;
 static OSSL_OP_signature_sign_fn rsa_sign;
 static OSSL_OP_signature_verify_fn rsa_verify;
 static OSSL_OP_signature_verify_recover_fn rsa_verify_recover;
-static OSSL_OP_signature_digest_sign_init_fn rsa_digest_signverify_init;
+static OSSL_OP_signature_digest_sign_init_fn rsa_digest_sign_init;
 static OSSL_OP_signature_digest_sign_update_fn rsa_digest_signverify_update;
 static OSSL_OP_signature_digest_sign_final_fn rsa_digest_sign_final;
-static OSSL_OP_signature_digest_verify_init_fn rsa_digest_signverify_init;
+static OSSL_OP_signature_digest_verify_init_fn rsa_digest_verify_init;
 static OSSL_OP_signature_digest_verify_update_fn rsa_digest_signverify_update;
 static OSSL_OP_signature_digest_verify_final_fn rsa_digest_verify_final;
 static OSSL_OP_signature_freectx_fn rsa_freectx;
@@ -73,7 +73,9 @@ static OSSL_ITEM padding_item[] = {
 
 typedef struct {
     OPENSSL_CTX *libctx;
+    char *propq;
     RSA *rsa;
+    int operation;
 
     /*
      * Flag to determine if the hash function can be changed (1) or not (0)
@@ -155,9 +157,6 @@ static int rsa_get_md_nid(const EVP_MD *md)
         }
     }
 
-    if (mdnid == NID_undef)
-        ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST);
-
  end:
     return mdnid;
 }
@@ -179,44 +178,51 @@ static int rsa_check_padding(int mdnid, int padding)
     return 1;
 }
 
-static void *rsa_newctx(void *provctx)
+static int rsa_check_parameters(EVP_MD *md, PROV_RSA_CTX *prsactx)
 {
-    PROV_RSA_CTX *prsactx = OPENSSL_zalloc(sizeof(PROV_RSA_CTX));
+    if (prsactx->pad_mode == RSA_PKCS1_PSS_PADDING) {
+        int max_saltlen;
+
+        /* See if minimum salt length exceeds maximum possible */
+        max_saltlen = RSA_size(prsactx->rsa) - EVP_MD_size(md);
+        if ((RSA_bits(prsactx->rsa) & 0x7) == 1)
+            max_saltlen--;
+        if (prsactx->min_saltlen > max_saltlen) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SALT_LENGTH);
+            return 0;
+        }
+    }
+    return 1;
+}
 
-    if (prsactx == NULL)
+static void *rsa_newctx(void *provctx, const char *propq)
+{
+    PROV_RSA_CTX *prsactx = NULL;
+    char *propq_copy = NULL;
+
+    if ((prsactx = OPENSSL_zalloc(sizeof(PROV_RSA_CTX))) == NULL
+        || (propq != NULL
+            && (propq_copy = OPENSSL_strdup(propq)) == NULL)) {
+        OPENSSL_free(prsactx);
+        ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
         return NULL;
+    }
 
     prsactx->libctx = PROV_LIBRARY_CONTEXT_OF(provctx);
     prsactx->flag_allow_md = 1;
+    prsactx->propq = propq_copy;
     return prsactx;
 }
 
 /* True if PSS parameters are restricted */
 #define rsa_pss_restricted(prsactx) (prsactx->min_saltlen != -1)
 
-static int rsa_signature_init(void *vprsactx, void *vrsa)
-{
-    PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
-
-    if (prsactx == NULL || vrsa == NULL || !RSA_up_ref(vrsa))
-        return 0;
-
-    RSA_free(prsactx->rsa);
-    prsactx->rsa = vrsa;
-    if (RSA_get0_pss_params(prsactx->rsa) != NULL)
-        prsactx->pad_mode = RSA_PKCS1_PSS_PADDING;
-    else
-        prsactx->pad_mode = RSA_PKCS1_PADDING;
-    /* Maximum for sign, auto for verify */
-    prsactx->saltlen = RSA_PSS_SALTLEN_AUTO;
-    prsactx->min_saltlen = -1;
-
-    return 1;
-}
-
 static int rsa_setup_md(PROV_RSA_CTX *ctx, const char *mdname,
                         const char *mdprops)
 {
+    if (mdprops == NULL)
+        mdprops = ctx->propq;
+
     if (mdname != NULL) {
         EVP_MD *md = EVP_MD_fetch(ctx->libctx, mdname, mdprops);
         int md_nid = rsa_get_md_nid(md);
@@ -224,7 +230,14 @@ static int rsa_setup_md(PROV_RSA_CTX *ctx, const char *mdname,
 
         if (md == NULL
             || md_nid == NID_undef
-            || !rsa_check_padding(md_nid, ctx->pad_mode)) {
+            || !rsa_check_padding(md_nid, ctx->pad_mode)
+            || !rsa_check_parameters(md, ctx)) {
+            if (md == NULL)
+                ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST,
+                               "%s could not be fetched", mdname);
+            if (md_nid == NID_undef)
+                ERR_raise_data(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED,
+                               "digest=%s", mdname);
             EVP_MD_free(md);
             return 0;
         }
@@ -258,18 +271,90 @@ static int rsa_setup_md(PROV_RSA_CTX *ctx, const char *mdname,
 }
 
 static int rsa_setup_mgf1_md(PROV_RSA_CTX *ctx, const char *mdname,
-                             const char *props)
+                             const char *mdprops)
 {
+    if (mdprops == NULL)
+        mdprops = ctx->propq;
+
     if (ctx->mgf1_mdname[0] != '\0')
         EVP_MD_free(ctx->mgf1_md);
 
-    if ((ctx->mgf1_md = EVP_MD_fetch(ctx->libctx, mdname, props)) == NULL)
+    if ((ctx->mgf1_md = EVP_MD_fetch(ctx->libctx, mdname, mdprops)) == NULL) {
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST,
+                       "%s could not be fetched", mdname);
         return 0;
+    }
     OPENSSL_strlcpy(ctx->mgf1_mdname, mdname, sizeof(ctx->mgf1_mdname));
 
     return 1;
 }
 
+static int rsa_signature_init(void *vprsactx, void *vrsa, int operation)
+{
+    PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+
+    if (prsactx == NULL || vrsa == NULL || !RSA_up_ref(vrsa))
+        return 0;
+
+    RSA_free(prsactx->rsa);
+    prsactx->rsa = vrsa;
+    prsactx->operation = operation;
+
+    /* Maximum for sign, auto for verify */
+    prsactx->saltlen = RSA_PSS_SALTLEN_AUTO;
+    prsactx->min_saltlen = -1;
+
+    switch (RSA_test_flags(prsactx->rsa, RSA_FLAG_TYPE_MASK)) {
+    case RSA_FLAG_TYPE_RSA:
+        prsactx->pad_mode = RSA_PKCS1_PADDING;
+        break;
+    case RSA_FLAG_TYPE_RSASSAPSS:
+        prsactx->pad_mode = RSA_PKCS1_PSS_PADDING;
+
+        {
+            const RSA_PSS_PARAMS_30 *pss =
+                rsa_get0_pss_params_30(prsactx->rsa);
+
+            if (!rsa_pss_params_30_is_unrestricted(pss)) {
+                int md_nid = rsa_pss_params_30_hashalg(pss);
+                int mgf1md_nid = rsa_pss_params_30_maskgenhashalg(pss);
+                int min_saltlen = rsa_pss_params_30_saltlen(pss);
+                const char *mdname, *mgf1mdname;
+
+                mdname = rsa_oaeppss_nid2name(md_nid);
+                mgf1mdname = rsa_oaeppss_nid2name(mgf1md_nid);
+                prsactx->min_saltlen = min_saltlen;
+
+                if (mdname == NULL) {
+                    ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST,
+                                   "PSS restrictions lack hash algorithm");
+                    return 0;
+                }
+                if (mgf1mdname == NULL) {
+                    ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST,
+                                   "PSS restrictions lack MGF1 hash algorithm");
+                    return 0;
+                }
+
+                strncpy(prsactx->mdname, mdname, sizeof(prsactx->mdname));
+                strncpy(prsactx->mgf1_mdname, mgf1mdname,
+                        sizeof(prsactx->mgf1_mdname));
+                prsactx->saltlen = min_saltlen;
+
+                return rsa_setup_md(prsactx, mdname, prsactx->propq)
+                    && rsa_setup_mgf1_md(prsactx, mgf1mdname, prsactx->propq);
+            }
+        }
+
+        break;
+    default:
+        ERR_raise(ERR_LIB_RSA, PROV_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+        return 0;
+    }
+
+    return 1;
+}
+
 static int setup_tbuf(PROV_RSA_CTX *ctx)
 {
     if (ctx->tbuf != NULL)
@@ -289,10 +374,16 @@ static void clean_tbuf(PROV_RSA_CTX *ctx)
 
 static void free_tbuf(PROV_RSA_CTX *ctx)
 {
-    OPENSSL_clear_free(ctx->tbuf, RSA_size(ctx->rsa));
+    clean_tbuf(ctx);
+    OPENSSL_free(ctx->tbuf);
     ctx->tbuf = NULL;
 }
 
+static int rsa_sign_init(void *vprsactx, void *vrsa)
+{
+    return rsa_signature_init(vprsactx, vrsa, EVP_PKEY_OP_SIGN);
+}
+
 static int rsa_sign(void *vprsactx, unsigned char *sig, size_t *siglen,
                     size_t sigsize, const unsigned char *tbs, size_t tbslen)
 {
@@ -306,8 +397,11 @@ static int rsa_sign(void *vprsactx, unsigned char *sig, size_t *siglen,
         return 1;
     }
 
-    if (sigsize < (size_t)rsasize)
+    if (sigsize < rsasize) {
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_SIGNATURE_SIZE,
+                       "is %zu, should be at least %zu", sigsize, rsasize);
         return 0;
+    }
 
     if (mdsize != 0) {
         if (tbslen != mdsize) {
@@ -338,7 +432,9 @@ static int rsa_sign(void *vprsactx, unsigned char *sig, size_t *siglen,
         switch (prsactx->pad_mode) {
         case RSA_X931_PADDING:
             if ((size_t)RSA_size(prsactx->rsa) < tbslen + 1) {
-                ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SIZE_TOO_SMALL);
+                ERR_raise_data(ERR_LIB_PROV, PROV_R_KEY_SIZE_TOO_SMALL,
+                               "RSA key size = %d, expected minimum = %d",
+                               RSA_size(prsactx->rsa), tbslen + 1);
                 return 0;
             }
             if (!setup_tbuf(prsactx)) {
@@ -372,14 +468,24 @@ static int rsa_sign(void *vprsactx, unsigned char *sig, size_t *siglen,
                 switch (prsactx->saltlen) {
                 case RSA_PSS_SALTLEN_DIGEST:
                     if (prsactx->min_saltlen > EVP_MD_size(prsactx->md)) {
-                        ERR_raise(ERR_LIB_PROV, PROV_R_PSS_SALTLEN_TOO_SMALL);
+                        ERR_raise_data(ERR_LIB_PROV,
+                                       PROV_R_PSS_SALTLEN_TOO_SMALL,
+                                       "minimum salt length set to %d, "
+                                       "but the digest only gives %d",
+                                       prsactx->min_saltlen,
+                                       EVP_MD_size(prsactx->md));
                         return 0;
                     }
                     /* FALLTHRU */
                 default:
                     if (prsactx->saltlen >= 0
                         && prsactx->saltlen < prsactx->min_saltlen) {
-                        ERR_raise(ERR_LIB_PROV, PROV_R_PSS_SALTLEN_TOO_SMALL);
+                        ERR_raise_data(ERR_LIB_PROV,
+                                       PROV_R_PSS_SALTLEN_TOO_SMALL,
+                                       "minimum salt length set to %d, but the"
+                                       "actual salt length is only set to %d",
+                                       prsactx->min_saltlen,
+                                       prsactx->saltlen);
                         return 0;
                     }
                     break;
@@ -421,6 +527,11 @@ static int rsa_sign(void *vprsactx, unsigned char *sig, size_t *siglen,
     return 1;
 }
 
+static int rsa_verify_recover_init(void *vprsactx, void *vrsa)
+{
+    return rsa_signature_init(vprsactx, vrsa, EVP_PKEY_OP_VERIFYRECOVER);
+}
+
 static int rsa_verify_recover(void *vprsactx,
                               unsigned char *rout,
                               size_t *routlen,
@@ -461,7 +572,9 @@ static int rsa_verify_recover(void *vprsactx,
 
             *routlen = ret;
             if (routsize < (size_t)ret) {
-                ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+                ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
+                               "buffer size is %d, should be %d",
+                               routsize, ret);
                 return 0;
             }
             memcpy(rout, prsactx->tbuf, ret);
@@ -498,6 +611,11 @@ static int rsa_verify_recover(void *vprsactx,
     return 1;
 }
 
+static int rsa_verify_init(void *vprsactx, void *vrsa)
+{
+    return rsa_signature_init(vprsactx, vrsa, EVP_PKEY_OP_VERIFY);
+}
+
 static int rsa_verify(void *vprsactx, const unsigned char *sig, size_t siglen,
                       const unsigned char *tbs, size_t tbslen)
 {
@@ -522,29 +640,6 @@ static int rsa_verify(void *vprsactx, const unsigned char *sig, size_t siglen,
                 int ret;
                 size_t mdsize;
 
-                /* Check PSS restrictions */
-                if (rsa_pss_restricted(prsactx)) {
-                    switch (prsactx->saltlen) {
-                    case RSA_PSS_SALTLEN_AUTO:
-                        ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_PSS_SALTLEN);
-                        return 0;
-                    case RSA_PSS_SALTLEN_DIGEST:
-                        if (prsactx->min_saltlen > EVP_MD_size(prsactx->md)) {
-                            ERR_raise(ERR_LIB_PROV,
-                                      PROV_R_PSS_SALTLEN_TOO_SMALL);
-                            return 0;
-                        }
-                        /* FALLTHRU */
-                    default:
-                        if (prsactx->saltlen >= 0
-                            && prsactx->saltlen < prsactx->min_saltlen) {
-                            ERR_raise(ERR_LIB_PROV, PROV_R_PSS_SALTLEN_TOO_SMALL);
-                            return 0;
-                        }
-                        break;
-                    }
-                }
-
                 /*
                  * We need to check this for the RSA_verify_PKCS1_PSS_mgf1()
                  * call
@@ -598,18 +693,20 @@ static int rsa_verify(void *vprsactx, const unsigned char *sig, size_t siglen,
 }
 
 static int rsa_digest_signverify_init(void *vprsactx, const char *mdname,
-                                      const char *props, void *vrsa)
+                                      void *vrsa, int operation)
 {
     PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
 
     prsactx->flag_allow_md = 0;
-    if (!rsa_signature_init(vprsactx, vrsa)
-        || !rsa_setup_md(prsactx, mdname, props))
+    if (!rsa_signature_init(vprsactx, vrsa, operation)
+        || !rsa_setup_md(prsactx, mdname, NULL)) /* TODO RL */
         return 0;
 
     prsactx->mdctx = EVP_MD_CTX_new();
-    if (prsactx->mdctx == NULL)
+    if (prsactx->mdctx == NULL) {
+        ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
         goto error;
+    }
 
     if (!EVP_DigestInit_ex(prsactx->mdctx, prsactx->md, NULL))
         goto error;
@@ -624,8 +721,9 @@ static int rsa_digest_signverify_init(void *vprsactx, const char *mdname,
     return 0;
 }
 
-int rsa_digest_signverify_update(void *vprsactx, const unsigned char *data,
-                                 size_t datalen)
+static int rsa_digest_signverify_update(void *vprsactx,
+                                        const unsigned char *data,
+                                        size_t datalen)
 {
     PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
 
@@ -635,8 +733,15 @@ int rsa_digest_signverify_update(void *vprsactx, const unsigned char *data,
     return EVP_DigestUpdate(prsactx->mdctx, data, datalen);
 }
 
-int rsa_digest_sign_final(void *vprsactx, unsigned char *sig, size_t *siglen,
-                          size_t sigsize)
+static int rsa_digest_sign_init(void *vprsactx, const char *mdname,
+                                void *vrsa)
+{
+    return rsa_digest_signverify_init(vprsactx, mdname, vrsa,
+                                      EVP_PKEY_OP_SIGN);
+}
+
+static int rsa_digest_sign_final(void *vprsactx, unsigned char *sig,
+                                 size_t *siglen, size_t sigsize)
 {
     PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
     unsigned char digest[EVP_MAX_MD_SIZE];
@@ -663,6 +768,12 @@ int rsa_digest_sign_final(void *vprsactx, unsigned char *sig, size_t *siglen,
     return rsa_sign(vprsactx, sig, siglen, sigsize, digest, (size_t)dlen);
 }
 
+static int rsa_digest_verify_init(void *vprsactx, const char *mdname,
+                                  void *vrsa)
+{
+    return rsa_digest_signverify_init(vprsactx, mdname, vrsa,
+                                      EVP_PKEY_OP_VERIFY);
+}
 
 int rsa_digest_verify_final(void *vprsactx, const unsigned char *sig,
                             size_t siglen)
@@ -697,6 +808,7 @@ static void rsa_freectx(void *vprsactx)
     EVP_MD_CTX_free(prsactx->mdctx);
     EVP_MD_free(prsactx->md);
     EVP_MD_free(prsactx->mgf1_md);
+    OPENSSL_free(prsactx->propq);
     free_tbuf(prsactx);
 
     OPENSSL_clear_free(prsactx, sizeof(prsactx));
@@ -708,8 +820,10 @@ static void *rsa_dupctx(void *vprsactx)
     PROV_RSA_CTX *dstctx;
 
     dstctx = OPENSSL_zalloc(sizeof(*srcctx));
-    if (dstctx == NULL)
+    if (dstctx == NULL) {
+        ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
         return NULL;
+    }
 
     *dstctx = *srcctx;
     dstctx->rsa = NULL;
@@ -860,11 +974,13 @@ static int rsa_set_ctx_params(void *vprsactx, const OSSL_PARAM params[])
 
         if (!OSSL_PARAM_get_utf8_string(p, &pmdname, sizeof(mdname)))
             return 0;
-        if (propsp != NULL
-            && !OSSL_PARAM_get_utf8_string(propsp, &pmdprops, sizeof(mdprops)))
+
+        if (propsp == NULL)
+            pmdprops = NULL;
+        else if (!OSSL_PARAM_get_utf8_string(propsp,
+                                             &pmdprops, sizeof(mdprops)))
             return 0;
 
-        /* TODO(3.0) PSS check needs more work */
         if (rsa_pss_restricted(prsactx)) {
             /* TODO(3.0) figure out what to do for prsactx->md == NULL */
             if (prsactx->md == NULL || EVP_MD_is_a(prsactx->md, mdname))
@@ -874,13 +990,14 @@ static int rsa_set_ctx_params(void *vprsactx, const OSSL_PARAM params[])
         }
 
         /* non-PSS code follows */
-        if (!rsa_setup_md(prsactx, mdname, mdprops))
+        if (!rsa_setup_md(prsactx, mdname, pmdprops))
             return 0;
     }
 
     p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_PAD_MODE);
     if (p != NULL) {
         int pad_mode = 0;
+        const char *err_extra_text = NULL;
 
         switch (p->data_type) {
         case OSSL_PARAM_INTEGER: /* Support for legacy pad mode number */
@@ -912,31 +1029,49 @@ static int rsa_set_ctx_params(void *vprsactx, const OSSL_PARAM params[])
              * OAEP padding is for asymmetric cipher only so is not compatible
              * with signature use.
              */
-            ERR_raise_data(ERR_LIB_PROV,
-                           PROV_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE,
-                           "OAEP padding not allowed for signing / verifying");
-            return 0;
+            err_extra_text = "OAEP padding not allowed for signing / verifying";
+            goto bad_pad;
         case RSA_PKCS1_PSS_PADDING:
-            if (prsactx->mdname[0] == '\0')
-                rsa_setup_md(prsactx, "SHA1", "");
-            goto cont;
+            if ((prsactx->operation
+                 & (EVP_PKEY_OP_SIGN | EVP_PKEY_OP_VERIFY)) == 0) {
+                err_extra_text =
+                    "PSS padding only allowed for sign and verify operations";
+                goto bad_pad;
+            }
+            if (prsactx->md == NULL
+                && !rsa_setup_md(prsactx, OSSL_DIGEST_NAME_SHA1, NULL)) {
+                return 0;
+            }
+            break;
         case RSA_PKCS1_PADDING:
+            err_extra_text = "PKCS#1 padding not allowed with RSA-PSS";
+            goto cont;
         case RSA_SSLV23_PADDING:
+            err_extra_text = "SSLv3 padding not allowed with RSA-PSS";
+            goto cont;
         case RSA_NO_PADDING:
+            err_extra_text = "No padding not allowed with RSA-PSS";
+            goto cont;
         case RSA_X931_PADDING:
-            if (RSA_get0_pss_params(prsactx->rsa) != NULL) {
-                ERR_raise_data(ERR_LIB_PROV,
-                               PROV_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE,
-                               "X.931 padding not allowed with RSA-PSS");
-                return 0;
-            }
+            err_extra_text = "X.931 padding not allowed with RSA-PSS";
         cont:
-            if (!rsa_check_padding(prsactx->mdnid, pad_mode))
-                return 0;
-            break;
+            if (RSA_test_flags(prsactx->rsa,
+                               RSA_FLAG_TYPE_MASK) == RSA_FLAG_TYPE_RSA)
+                break;
+            /* FALLTHRU */
         default:
+        bad_pad:
+            if (err_extra_text == NULL)
+                ERR_raise(ERR_LIB_PROV,
+                          PROV_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE);
+            else
+                ERR_raise_data(ERR_LIB_PROV,
+                               PROV_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE,
+                               err_extra_text);
             return 0;
         }
+        if (!rsa_check_padding(prsactx->mdnid, pad_mode))
+            return 0;
         prsactx->pad_mode = pad_mode;
     }
 
@@ -980,6 +1115,37 @@ static int rsa_set_ctx_params(void *vprsactx, const OSSL_PARAM params[])
             return 0;
         }
 
+        if (rsa_pss_restricted(prsactx)) {
+            switch (prsactx->saltlen) {
+            case RSA_PSS_SALTLEN_AUTO:
+                if (prsactx->operation == EVP_PKEY_OP_VERIFY) {
+                    ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_PSS_SALTLEN);
+                    return 0;
+                }
+                break;
+            case RSA_PSS_SALTLEN_DIGEST:
+                if (prsactx->min_saltlen > EVP_MD_size(prsactx->md)) {
+                    ERR_raise_data(ERR_LIB_PROV,
+                                   PROV_R_PSS_SALTLEN_TOO_SMALL,
+                                   "Should be more than %d, but would be "
+                                   "set to match digest size (%d)",
+                                   prsactx->min_saltlen,
+                                   EVP_MD_size(prsactx->md));
+                    return 0;
+                }
+                /* FALLTHRU */
+            default:
+                if (saltlen >= 0 && saltlen < prsactx->min_saltlen) {
+                    ERR_raise_data(ERR_LIB_PROV,
+                                   PROV_R_PSS_SALTLEN_TOO_SMALL,
+                                   "Should be more than %d, "
+                                   "but would be set to %d",
+                                   prsactx->min_saltlen, saltlen);
+                    return 0;
+                }
+            }
+        }
+
         prsactx->saltlen = saltlen;
     }
 
@@ -993,8 +1159,11 @@ static int rsa_set_ctx_params(void *vprsactx, const OSSL_PARAM params[])
 
         if (!OSSL_PARAM_get_utf8_string(p, &pmdname, sizeof(mdname)))
             return 0;
-        if (propsp != NULL
-            && !OSSL_PARAM_get_utf8_string(propsp, &pmdprops, sizeof(mdprops)))
+
+        if (propsp == NULL)
+            pmdprops = NULL;
+        else if (!OSSL_PARAM_get_utf8_string(propsp,
+                                             &pmdprops, sizeof(mdprops)))
             return 0;
 
         if (prsactx->pad_mode != RSA_PKCS1_PSS_PADDING) {
@@ -1002,9 +1171,8 @@ static int rsa_set_ctx_params(void *vprsactx, const OSSL_PARAM params[])
             return  0;
         }
 
-        /* TODO(3.0) PSS check needs more work */
         if (rsa_pss_restricted(prsactx)) {
-            /* TODO(3.0) figure out what to do for prsactx->md == NULL */
+            /* TODO(3.0) figure out what to do for prsactx->mgf1_md == NULL */
             if (prsactx->mgf1_md == NULL
                 || EVP_MD_is_a(prsactx->mgf1_md, mdname))
                 return 1;
@@ -1013,7 +1181,7 @@ static int rsa_set_ctx_params(void *vprsactx, const OSSL_PARAM params[])
         }
 
         /* non-PSS code follows */
-        if (!rsa_setup_mgf1_md(prsactx, mdname, mdprops))
+        if (!rsa_setup_mgf1_md(prsactx, mdname, pmdprops))
             return 0;
     }
 
@@ -1083,20 +1251,22 @@ static const OSSL_PARAM *rsa_settable_ctx_md_params(void *vprsactx)
 
 const OSSL_DISPATCH rsa_signature_functions[] = {
     { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))rsa_newctx },
-    { OSSL_FUNC_SIGNATURE_SIGN_INIT, (void (*)(void))rsa_signature_init },
+    { OSSL_FUNC_SIGNATURE_SIGN_INIT, (void (*)(void))rsa_sign_init },
     { OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))rsa_sign },
-    { OSSL_FUNC_SIGNATURE_VERIFY_INIT, (void (*)(void))rsa_signature_init },
+    { OSSL_FUNC_SIGNATURE_VERIFY_INIT, (void (*)(void))rsa_verify_init },
     { OSSL_FUNC_SIGNATURE_VERIFY, (void (*)(void))rsa_verify },
-    { OSSL_FUNC_SIGNATURE_VERIFY_RECOVER_INIT, (void (*)(void))rsa_signature_init },
-    { OSSL_FUNC_SIGNATURE_VERIFY_RECOVER, (void (*)(void))rsa_verify_recover },
+    { OSSL_FUNC_SIGNATURE_VERIFY_RECOVER_INIT,
+      (void (*)(void))rsa_verify_recover_init },
+    { OSSL_FUNC_SIGNATURE_VERIFY_RECOVER,
+      (void (*)(void))rsa_verify_recover },
     { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT,
-      (void (*)(void))rsa_digest_signverify_init },
+      (void (*)(void))rsa_digest_sign_init },
     { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_UPDATE,
       (void (*)(void))rsa_digest_signverify_update },
     { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_FINAL,
       (void (*)(void))rsa_digest_sign_final },
     { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT,
-      (void (*)(void))rsa_digest_signverify_init },
+      (void (*)(void))rsa_digest_verify_init },
     { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_UPDATE,
       (void (*)(void))rsa_digest_signverify_update },
     { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_FINAL,
diff --git a/test/evp_pkey_provided_test.c b/test/evp_pkey_provided_test.c
index 2c07ed0282..c5ef7241db 100644
--- a/test/evp_pkey_provided_test.c
+++ b/test/evp_pkey_provided_test.c
@@ -130,7 +130,12 @@ static int test_print_key_using_pem(const char *alg, const EVP_PKEY *pk)
     if (!TEST_ptr(membio))
         goto err;
 
-    if (!TEST_true(EVP_PKEY_print_private(membio, pk, 0, NULL))
+    if (/* Output Encrypted private key in PEM form */
+        !TEST_true(PEM_write_bio_PrivateKey(bio_out, pk, EVP_aes_256_cbc(),
+                                            (unsigned char *)"pass", 4,
+                                            NULL, NULL))
+        /* Private key in text form */
+        || !TEST_true(EVP_PKEY_print_private(membio, pk, 0, NULL))
         || !TEST_true(compare_with_file(alg, PRIV_TEXT, membio))
         /* Public key in PEM form */
         || !TEST_true(PEM_write_bio_PUBKEY(membio, pk))
@@ -138,11 +143,7 @@ static int test_print_key_using_pem(const char *alg, const EVP_PKEY *pk)
         /* Unencrypted private key in PEM form */
         || !TEST_true(PEM_write_bio_PrivateKey(membio, pk,
                                                NULL, NULL, 0, NULL, NULL))
-        || !TEST_true(compare_with_file(alg, PRIV_PEM, membio))
-        /* Encrypted private key in PEM form */
-        || !TEST_true(PEM_write_bio_PrivateKey(bio_out, pk, EVP_aes_256_cbc(),
-                                               (unsigned char *)"pass", 4,
-                                               NULL, NULL)))
+        || !TEST_true(compare_with_file(alg, PRIV_PEM, membio)))
         goto err;
 
     ret = 1;
diff --git a/test/recipes/15-test_rsapss.t b/test/recipes/15-test_rsapss.t
index 0d7e7bf2e3..5c8340259f 100644
--- a/test/recipes/15-test_rsapss.t
+++ b/test/recipes/15-test_rsapss.t
@@ -16,14 +16,22 @@ use OpenSSL::Test::Utils;
 
 setup("test_rsapss");
 
-plan tests => 5;
+plan tests => 7;
 
 #using test/testrsa.pem which happens to be a 512 bit RSA
 ok(run(app(['openssl', 'dgst', '-sign', srctop_file('test', 'testrsa.pem'), '-sha1',
-            '-sigopt', 'rsa_padding_mode:pss', '-sigopt', 'rsa_pss_saltlen:max',
-            '-sigopt', 'rsa_mgf1_md:sha512', '-out', 'testrsapss.sig',
+            '-sigopt', 'rsa_padding_mode:pss',
+            '-sigopt', 'rsa_pss_saltlen:max',
+            '-sigopt', 'rsa_mgf1_md:sha512',
+            '-out', 'testrsapss-restricted.sig',
             srctop_file('test', 'testrsa.pem')])),
-   "openssl dgst -sign");
+   "openssl dgst -sign [plain RSA key, PSS padding mode, PSS restrictions]");
+
+ok(run(app(['openssl', 'dgst', '-sign', srctop_file('test', 'testrsa.pem'), '-sha1',
+            '-sigopt', 'rsa_padding_mode:pss',
+            '-out', 'testrsapss-unrestricted.sig',
+            srctop_file('test', 'testrsa.pem')])),
+   "openssl dgst -sign [plain RSA key, PSS padding mode, no PSS restrictions]");
 
 with({ exit_checker => sub { return shift == 1; } },
      sub { ok(run(app(['openssl', 'dgst', '-sign', srctop_file('test', 'testrsa.pem'), '-sha512',
@@ -41,8 +49,18 @@ with({ exit_checker => sub { return shift == 1; } },
               "openssl dgst -prverify, expect to fail gracefully");
          });
 
-ok(run(app(['openssl', 'dgst', '-prverify', srctop_file('test', 'testrsa.pem'), '-sha1',
-            '-sigopt', 'rsa_padding_mode:pss', '-sigopt', 'rsa_pss_saltlen:max',
-            '-sigopt', 'rsa_mgf1_md:sha512', '-signature', 'testrsapss.sig',
+ok(run(app(['openssl', 'dgst', '-prverify', srctop_file('test', 'testrsa.pem'),
+            '-sha1',
+            '-sigopt', 'rsa_padding_mode:pss',
+            '-sigopt', 'rsa_pss_saltlen:max',
+            '-sigopt', 'rsa_mgf1_md:sha512',
+            '-signature', 'testrsapss-restricted.sig',
+            srctop_file('test', 'testrsa.pem')])),
+   "openssl dgst -prverify [plain RSA key, PSS padding mode, PSS restrictions]");
+
+ok(run(app(['openssl', 'dgst', '-prverify', srctop_file('test', 'testrsa.pem'),
+            '-sha1',
+            '-sigopt', 'rsa_padding_mode:pss',
+            '-signature', 'testrsapss-unrestricted.sig',
             srctop_file('test', 'testrsa.pem')])),
-   "openssl dgst -prverify");
+   "openssl dgst -prverify [plain RSA key, PSS padding mode, no PSS restrictions]");
diff --git a/test/ssl-tests/20-cert-select.cnf.in b/test/ssl-tests/20-cert-select.cnf.in
index fd3f09d7fb..79325e71c1 100644
--- a/test/ssl-tests/20-cert-select.cnf.in
+++ b/test/ssl-tests/20-cert-select.cnf.in
@@ -599,10 +599,7 @@ my @tests_tls_1_1 = (
 );
 
 push @tests, @tests_non_fips unless $fips_mode;
-
-#TODO(3.0): Re-enable these PSS tests in a $no_deflt_libctx build once we have
-#           support for it
-push @tests, @tests_pss unless $no_deflt_libctx;
+push @tests, @tests_pss;
 push @tests, @tests_tls_1_1 unless disabled("tls1_1") || $no_deflt_libctx;
 
 my $server_tls_1_3;
diff --git a/util/libcrypto.num b/util/libcrypto.num
index ec6acaefd4..104e065bbd 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -5089,3 +5089,6 @@ EVP_PKEY_new_raw_private_key_with_libctx ?	3_0_0	EXIST::FUNCTION:
 EVP_PKEY_new_raw_public_key_with_libctx ?	3_0_0	EXIST::FUNCTION:
 OSSL_STORE_attach                       ?	3_0_0	EXIST::FUNCTION:
 OSSL_STORE_LOADER_set_attach            ?	3_0_0	EXIST::FUNCTION:
+EVP_PKEY_CTX_set_rsa_pss_keygen_saltlen ?	3_0_0	EXIST::FUNCTION:RSA
+EVP_PKEY_CTX_set_rsa_pss_keygen_mgf1_md ?	3_0_0	EXIST::FUNCTION:RSA
+EVP_PKEY_CTX_set_rsa_pss_keygen_mgf1_md_name ?	3_0_0	EXIST::FUNCTION:RSA


More information about the openssl-commits mailing list