Bug 1400844, Implement handling of RSA-PSS signatures on certificates, r=mt
authorDaiki Ueno <dueno@redhat.com>
Tue, 24 Oct 2017 13:45:06 +0200
changeset 14065 84e886ea090e36c69df58a71665a97bd25c62d02
parent 14064 4698be108cbc660dfff365939da470044bd04442
child 14066 3dfd171371fa3f5e98a47cc52976db4a3c56cc56
push id2840
push user[email protected]
push dateTue, 24 Oct 2017 11:45:35 +0000
reviewersmt
bugs1400844
Bug 1400844, Implement handling of RSA-PSS signatures on certificates, r=mt This series adds high level API to sign and verify RSA-PSS signatures on certificates and utilizes them in tools.
cmd/certutil/certutil.c
cmd/lib/secutil.c
gtests/cryptohi_gtest/Makefile
gtests/cryptohi_gtest/cryptohi_gtest.gyp
gtests/cryptohi_gtest/cryptohi_unittest.cc
gtests/cryptohi_gtest/manifest.mn
gtests/manifest.mn
gtests/ssl_gtest/ssl_auth_unittest.cc
gtests/ssl_gtest/tls_agent.cc
gtests/ssl_gtest/tls_agent.h
lib/certdb/certdb.c
lib/cryptohi/cryptohi.h
lib/cryptohi/keyi.h
lib/cryptohi/seckey.c
lib/cryptohi/secsign.c
lib/cryptohi/secvfy.c
lib/nss/nss.def
nss.gyp
tests/cert/cert.sh
tests/common/init.sh
tests/ssl_gtests/ssl_gtests.sh
--- a/cmd/certutil/certutil.c
+++ b/cmd/certutil/certutil.c
@@ -189,16 +189,18 @@ CertReq(SECKEYPrivateKey *privk, SECKEYP
     CERTSubjectPublicKeyInfo *spki;
     CERTCertificateRequest *cr;
     SECItem *encoding;
     SECOidTag signAlgTag;
     SECStatus rv;
     PLArenaPool *arena;
     void *extHandle;
     SECItem signedReq = { siBuffer, NULL, 0 };
+    SECAlgorithmID signAlg;
+    SECItem *params = NULL;
 
     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
     if (!arena) {
         SECU_PrintError(progName, "out of memory");
         return SECFailure;
     }
 
     /* Create info about public key */
@@ -206,21 +208,35 @@ CertReq(SECKEYPrivateKey *privk, SECKEYP
     if (!spki) {
         PORT_FreeArena(arena, PR_FALSE);
         SECU_PrintError(progName, "unable to create subject public key");
         return SECFailure;
     }
 
     /* Change cert type to RSA-PSS, if desired. */
     if (pssCertificate) {
+        params = SEC_CreateSignatureAlgorithmParameters(arena,
+                                                        NULL,
+                                                        SEC_OID_PKCS1_RSA_PSS_SIGNATURE,
+                                                        hashAlgTag,
+                                                        NULL,
+                                                        privk);
+        if (!params) {
+            PORT_FreeArena(arena, PR_FALSE);
+            SECKEY_DestroySubjectPublicKeyInfo(spki);
+            SECU_PrintError(progName, "unable to create RSA-PSS parameters");
+            return SECFailure;
+        }
+
         spki->algorithm.parameters.data = NULL;
         rv = SECOID_SetAlgorithmID(arena, &spki->algorithm,
-                                   SEC_OID_PKCS1_RSA_PSS_SIGNATURE, 0);
+                                   SEC_OID_PKCS1_RSA_PSS_SIGNATURE, params);
         if (rv != SECSuccess) {
             PORT_FreeArena(arena, PR_FALSE);
+            SECKEY_DestroySubjectPublicKeyInfo(spki);
             SECU_PrintError(progName, "unable to set algorithm ID");
             return SECFailure;
         }
     }
 
     /* Generate certificate request */
     cr = CERT_CreateCertificateRequest(subject, spki, NULL);
     SECKEY_DestroySubjectPublicKeyInfo(spki);
@@ -251,26 +267,44 @@ CertReq(SECKEYPrivateKey *privk, SECKEYP
                                   SEC_ASN1_GET(CERT_CertificateRequestTemplate));
     CERT_DestroyCertificateRequest(cr);
     if (encoding == NULL) {
         PORT_FreeArena(arena, PR_FALSE);
         SECU_PrintError(progName, "der encoding of request failed");
         return SECFailure;
     }
 
+    PORT_Memset(&signAlg, 0, sizeof(signAlg));
+    if (pssCertificate) {
+        rv = SECOID_SetAlgorithmID(arena, &signAlg,
+                                   SEC_OID_PKCS1_RSA_PSS_SIGNATURE, params);
+        if (rv != SECSuccess) {
+            PORT_FreeArena(arena, PR_FALSE);
+            SECU_PrintError(progName, "unable to set algorithm ID");
+            return SECFailure;
+        }
+    } else {
+        signAlgTag = SEC_GetSignatureAlgorithmOidTag(keyType, hashAlgTag);
+        if (signAlgTag == SEC_OID_UNKNOWN) {
+            PORT_FreeArena(arena, PR_FALSE);
+            SECU_PrintError(progName, "unknown Key or Hash type");
+            return SECFailure;
+        }
+        rv = SECOID_SetAlgorithmID(arena, &signAlg, signAlgTag, 0);
+        if (rv != SECSuccess) {
+            PORT_FreeArena(arena, PR_FALSE);
+            SECU_PrintError(progName, "unable to set algorithm ID");
+            return SECFailure;
+        }
+    }
+
     /* Sign the request */
-    signAlgTag = SEC_GetSignatureAlgorithmOidTag(keyType, hashAlgTag);
-    if (signAlgTag == SEC_OID_UNKNOWN) {
-        PORT_FreeArena(arena, PR_FALSE);
-        SECU_PrintError(progName, "unknown Key or Hash type");
-        return SECFailure;
-    }
-
-    rv = SEC_DerSignData(arena, &signedReq, encoding->data, encoding->len,
-                         privk, signAlgTag);
+    rv = SEC_DerSignDataWithAlgorithmID(arena, &signedReq,
+                                        encoding->data, encoding->len,
+                                        privk, &signAlg);
     if (rv) {
         PORT_FreeArena(arena, PR_FALSE);
         SECU_PrintError(progName, "signing of data failed");
         return SECFailure;
     }
 
     /* Encode request in specified format */
     if (ascii) {
@@ -1860,56 +1894,129 @@ MakeV1Cert(CERTCertDBHandle *handle,
     if (issuerCert) {
         CERT_DestroyCertificate(issuerCert);
     }
 
     return (cert);
 }
 
 static SECStatus
+SetSignatureAlgorithm(PLArenaPool *arena,
+                      SECAlgorithmID *signAlg,
+                      SECAlgorithmID *spkiAlg,
+                      SECOidTag hashAlgTag,
+                      SECKEYPrivateKey *privKey,
+                      PRBool pssSign)
+{
+    SECStatus rv;
+
+    if (pssSign ||
+        SECOID_GetAlgorithmTag(spkiAlg) == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) {
+        SECItem *srcParams;
+        SECItem *params;
+
+        if (SECOID_GetAlgorithmTag(spkiAlg) == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) {
+            srcParams = &spkiAlg->parameters;
+        } else {
+            /* If the issuer's public key is RSA, the parameter field
+             * of the SPKI should be NULL, which can't be used as a
+             * basis of RSA-PSS parameters. */
+            srcParams = NULL;
+        }
+        params = SEC_CreateSignatureAlgorithmParameters(arena,
+                                                        NULL,
+                                                        SEC_OID_PKCS1_RSA_PSS_SIGNATURE,
+                                                        hashAlgTag,
+                                                        srcParams,
+                                                        privKey);
+        if (!params) {
+            SECU_PrintError(progName, "Could not create RSA-PSS parameters");
+            return SECFailure;
+        }
+        rv = SECOID_SetAlgorithmID(arena, signAlg,
+                                   SEC_OID_PKCS1_RSA_PSS_SIGNATURE,
+                                   params);
+        if (rv != SECSuccess) {
+            SECU_PrintError(progName, "Could not set signature algorithm id.");
+            return rv;
+        }
+    } else {
+        KeyType keyType = SECKEY_GetPrivateKeyType(privKey);
+        SECOidTag algID;
+
+        algID = SEC_GetSignatureAlgorithmOidTag(keyType, hashAlgTag);
+        if (algID == SEC_OID_UNKNOWN) {
+            SECU_PrintError(progName, "Unknown key or hash type for issuer.");
+            return SECFailure;
+        }
+        rv = SECOID_SetAlgorithmID(arena, signAlg, algID, 0);
+        if (rv != SECSuccess) {
+            SECU_PrintError(progName, "Could not set signature algorithm id.");
+            return rv;
+        }
+    }
+    return SECSuccess;
+}
+
+static SECStatus
 SignCert(CERTCertDBHandle *handle, CERTCertificate *cert, PRBool selfsign,
          SECOidTag hashAlgTag,
          SECKEYPrivateKey *privKey, char *issuerNickName,
-         int certVersion, void *pwarg)
+         int certVersion, PRBool pssSign, void *pwarg)
 {
     SECItem der;
     SECKEYPrivateKey *caPrivateKey = NULL;
     SECStatus rv;
     PLArenaPool *arena;
-    SECOidTag algID;
+    CERTCertificate *issuer;
     void *dummy;
 
-    if (!selfsign) {
-        CERTCertificate *issuer = PK11_FindCertFromNickname(issuerNickName, pwarg);
+    arena = cert->arena;
+
+    if (selfsign) {
+        issuer = cert;
+    } else {
+        issuer = PK11_FindCertFromNickname(issuerNickName, pwarg);
         if ((CERTCertificate *)NULL == issuer) {
             SECU_PrintError(progName, "unable to find issuer with nickname %s",
                             issuerNickName);
-            return SECFailure;
+            rv = SECFailure;
+            goto done;
         }
-
         privKey = caPrivateKey = PK11_FindKeyByAnyCert(issuer, pwarg);
-        CERT_DestroyCertificate(issuer);
         if (caPrivateKey == NULL) {
             SECU_PrintError(progName, "unable to retrieve key %s", issuerNickName);
-            return SECFailure;
+            rv = SECFailure;
+            CERT_DestroyCertificate(issuer);
+            goto done;
         }
     }
 
-    arena = cert->arena;
-
-    algID = SEC_GetSignatureAlgorithmOidTag(privKey->keyType, hashAlgTag);
-    if (algID == SEC_OID_UNKNOWN) {
-        fprintf(stderr, "Unknown key or hash type for issuer.");
+    if (pssSign &&
+        (SECKEY_GetPrivateKeyType(privKey) != rsaKey &&
+         SECKEY_GetPrivateKeyType(privKey) != rsaPssKey)) {
+        SECU_PrintError(progName, "unable to create RSA-PSS signature with key %s",
+                        issuerNickName);
         rv = SECFailure;
+        if (!selfsign) {
+            CERT_DestroyCertificate(issuer);
+        }
         goto done;
     }
 
-    rv = SECOID_SetAlgorithmID(arena, &cert->signature, algID, 0);
+    rv = SetSignatureAlgorithm(arena,
+                               &cert->signature,
+                               &issuer->subjectPublicKeyInfo.algorithm,
+                               hashAlgTag,
+                               privKey,
+                               pssSign);
+    if (!selfsign) {
+        CERT_DestroyCertificate(issuer);
+    }
     if (rv != SECSuccess) {
-        fprintf(stderr, "Could not set signature algorithm id.");
         goto done;
     }
 
     switch (certVersion) {
         case (SEC_CERTIFICATE_VERSION_1):
             /* The initial version for x509 certificates is version one
          * and this default value must be an implicit DER encoding. */
             cert->version.data = NULL;
@@ -1918,30 +2025,32 @@ SignCert(CERTCertDBHandle *handle, CERTC
         case (SEC_CERTIFICATE_VERSION_2):
         case (SEC_CERTIFICATE_VERSION_3):
         case 3: /* unspecified format (would be version 4 certificate). */
             *(cert->version.data) = certVersion;
             cert->version.len = 1;
             break;
         default:
             PORT_SetError(SEC_ERROR_INVALID_ARGS);
-            return SECFailure;
+            rv = SECFailure;
+            goto done;
     }
 
     der.len = 0;
     der.data = NULL;
     dummy = SEC_ASN1EncodeItem(arena, &der, cert,
                                SEC_ASN1_GET(CERT_CertificateTemplate));
     if (!dummy) {
         fprintf(stderr, "Could not encode certificate.\n");
         rv = SECFailure;
         goto done;
     }
 
-    rv = SEC_DerSignData(arena, &cert->derCert, der.data, der.len, privKey, algID);
+    rv = SEC_DerSignDataWithAlgorithmID(arena, &cert->derCert, der.data, der.len,
+                                        privKey, &cert->signature);
     if (rv != SECSuccess) {
         fprintf(stderr, "Could not sign encoded certificate data.\n");
         /* result allocated out of the arena, it will be freed
          * when the arena is freed */
         goto done;
     }
 done:
     if (caPrivateKey) {
@@ -1964,16 +2073,17 @@ CreateCert(
     int validityMonths,
     const char *emailAddrs,
     const char *dnsNames,
     PRBool ascii,
     PRBool selfsign,
     certutilExtnList extnList,
     const char *extGeneric,
     int certVersion,
+    PRBool pssSign,
     SECItem *certDER)
 {
     void *extHandle = NULL;
     CERTCertificate *subjectCert = NULL;
     CERTCertificateRequest *certReq = NULL;
     SECStatus rv = SECSuccess;
     CERTCertExtension **CRexts;
 
@@ -2024,17 +2134,17 @@ CreateCert(
                 fprintf(stderr, "Failed to locate private key.\n");
                 rv = SECFailure;
                 break;
             }
         }
 
         rv = SignCert(handle, subjectCert, selfsign, hashAlgTag,
                       *selfsignprivkey, issuerNickName,
-                      certVersion, pwarg);
+                      certVersion, pssSign, pwarg);
         if (rv != SECSuccess)
             break;
 
         rv = SECFailure;
         if (ascii) {
             char *asciiDER = BTOA_DataToAscii(subjectCert->derCert.data,
                                               subjectCert->derCert.len);
             if (asciiDER) {
@@ -2347,16 +2457,17 @@ enum certutilOpts {
     opt_KeyAttrFlags,
     opt_EmptyPassword,
     opt_CertVersion,
     opt_AddSubjectAltNameExt,
     opt_DumpExtensionValue,
     opt_GenericExtensions,
     opt_NewNickname,
     opt_Pss,
+    opt_PssSign,
     opt_Help
 };
 
 static const secuCommandFlag commands_init[] =
     {
       { /* cmd_AddCert             */ 'A', PR_FALSE, 0, PR_FALSE },
       { /* cmd_CreateNewCert       */ 'C', PR_FALSE, 0, PR_FALSE },
       { /* cmd_DeleteCert          */ 'D', PR_FALSE, 0, PR_FALSE },
@@ -2467,16 +2578,18 @@ static const secuCommandFlag options_ini
       { /* opt_DumpExtensionValue  */ 0, PR_TRUE, 0, PR_FALSE,
         "dump-ext-val" },
       { /* opt_GenericExtensions   */ 0, PR_TRUE, 0, PR_FALSE,
         "extGeneric" },
       { /* opt_NewNickname         */ 0, PR_TRUE, 0, PR_FALSE,
         "new-n" },
       { /* opt_Pss                 */ 0, PR_FALSE, 0, PR_FALSE,
         "pss" },
+      { /* opt_PssSign             */ 0, PR_FALSE, 0, PR_FALSE,
+        "pss-sign" },
     };
 #define NUM_OPTIONS ((sizeof options_init) / (sizeof options_init[0]))
 
 static secuCommandFlag certutil_commands[NUM_COMMANDS];
 static secuCommandFlag certutil_options[NUM_OPTIONS];
 
 static const secuCommand certutil = {
     NUM_COMMANDS,
@@ -3358,16 +3471,35 @@ certutil_main(int argc, char **argv, PRB
         if (keytype != rsaKey) {
             PR_fprintf(PR_STDERR,
                        "%s -%c: --pss only works with RSA keys.\n",
                        progName, commandToRun);
             return 255;
         }
     }
 
+    /* --pss-sign is to sign a certificate with RSA-PSS, even if the
+     * issuer's key is an RSA key.  If the key is an RSA-PSS key, the
+     * generated signature is always RSA-PSS. */
+    if (certutil.options[opt_PssSign].activated) {
+        if (!certutil.commands[cmd_CreateNewCert].activated &&
+            !certutil.commands[cmd_CreateAndAddCert].activated) {
+            PR_fprintf(PR_STDERR,
+                       "%s -%c: --pss-sign only works with -C or -S.\n",
+                       progName, commandToRun);
+            return 255;
+        }
+        if (keytype != rsaKey) {
+            PR_fprintf(PR_STDERR,
+                       "%s -%c: --pss-sign only works with RSA keys.\n",
+                       progName, commandToRun);
+            return 255;
+        }
+    }
+
     /* If we need a list of extensions convert the flags into list format */
     if (certutil.commands[cmd_CertReq].activated ||
         certutil.commands[cmd_CreateAndAddCert].activated ||
         certutil.commands[cmd_CreateNewCert].activated) {
         certutil_extns[ext_keyUsage].activated =
             certutil.options[opt_AddCmdKeyUsageExt].activated;
         if (!certutil_extns[ext_keyUsage].activated) {
             certutil_extns[ext_keyUsage].activated =
@@ -3495,16 +3627,17 @@ certutil_main(int argc, char **argv, PRB
                         certutil.options[opt_ExtendedDNSNames].arg,
                         certutil.options[opt_ASCIIForIO].activated &&
                             certutil.commands[cmd_CreateNewCert].activated,
                         certutil.options[opt_SelfSign].activated,
                         certutil_extns,
                         (certutil.options[opt_GenericExtensions].activated ? certutil.options[opt_GenericExtensions].arg
                                                                            : NULL),
                         certVersion,
+                        certutil.options[opt_PssSign].activated,
                         &certDER);
         if (rv)
             goto shutdown;
     }
 
     /*
      * Adding a cert to the database (or slot)
      */
--- a/cmd/lib/secutil.c
+++ b/cmd/lib/secutil.c
@@ -1307,25 +1307,22 @@ SECU_PrintAlgorithmID(FILE *out, SECAlgo
                 break;
             default:
                 secu_PrintPBEParams(out, &a->parameters, "Parameters", level + 1);
                 break;
         }
         return;
     }
 
-    if (algtag == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) {
-        secu_PrintRSAPSSParams(out, &a->parameters, "Parameters", level + 1);
-        return;
-    }
-
     if (a->parameters.len == 0 ||
         (a->parameters.len == 2 &&
          PORT_Memcmp(a->parameters.data, "\005\000", 2) == 0)) {
         /* No arguments or NULL argument */
+    } else if (algtag == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) {
+        secu_PrintRSAPSSParams(out, &a->parameters, "Parameters", level + 1);
     } else {
         /* Print args to algorithm */
         SECU_PrintAsHex(out, &a->parameters, "Args", level + 1);
     }
 }
 
 static void
 secu_PrintAttribute(FILE *out, SEC_PKCS7Attribute *attr, char *m, int level)
new file mode 100644
--- /dev/null
+++ b/gtests/cryptohi_gtest/Makefile
@@ -0,0 +1,43 @@
+#! gmake
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#######################################################################
+# (1) Include initial platform-independent assignments (MANDATORY).   #
+#######################################################################
+
+include manifest.mn
+
+#######################################################################
+# (2) Include "global" configuration information. (OPTIONAL)          #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/config.mk
+
+#######################################################################
+# (3) Include "component" configuration information. (OPTIONAL)       #
+#######################################################################
+
+
+#######################################################################
+# (4) Include "local" platform-dependent assignments (OPTIONAL).      #
+#######################################################################
+
+include ../common/gtest.mk
+
+#######################################################################
+# (5) Execute "global" rules. (OPTIONAL)                              #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/rules.mk
+
+#######################################################################
+# (6) Execute "component" rules. (OPTIONAL)                           #
+#######################################################################
+
+
+#######################################################################
+# (7) Execute "local" rules. (OPTIONAL).                              #
+#######################################################################
new file mode 100644
--- /dev/null
+++ b/gtests/cryptohi_gtest/cryptohi_gtest.gyp
@@ -0,0 +1,29 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+{
+  'includes': [
+    '../../coreconf/config.gypi',
+    '../common/gtest.gypi',
+  ],
+  'targets': [
+    {
+      'target_name': 'cryptohi_gtest',
+      'type': 'executable',
+      'sources': [
+        'cryptohi_unittest.cc',
+        '<(DEPTH)/gtests/common/gtests.cc'
+      ],
+      'dependencies': [
+        '<(DEPTH)/exports.gyp:nss_exports',
+        '<(DEPTH)/gtests/google_test/google_test.gyp:gtest',
+        '<(DEPTH)/lib/util/util.gyp:nssutil3',
+        '<(DEPTH)/lib/ssl/ssl.gyp:ssl3',
+        '<(DEPTH)/lib/nss/nss.gyp:nss3',
+      ]
+    }
+  ],
+  'variables': {
+    'module': 'nss'
+  }
+}
new file mode 100644
--- /dev/null
+++ b/gtests/cryptohi_gtest/cryptohi_unittest.cc
@@ -0,0 +1,369 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <string>
+
+#include "gtest/gtest.h"
+
+#include "scoped_ptrs.h"
+#include "cryptohi.h"
+#include "secitem.h"
+#include "secerr.h"
+
+namespace nss_test {
+
+class SignParamsTestF : public ::testing::Test {
+ protected:
+  ScopedPLArenaPool arena_;
+  ScopedSECKEYPrivateKey privk_;
+  ScopedSECKEYPublicKey pubk_;
+  ScopedSECKEYPrivateKey ecPrivk_;
+  ScopedSECKEYPublicKey ecPubk_;
+
+  void SetUp() {
+    arena_.reset(PORT_NewArena(2048));
+
+    SECKEYPublicKey *pubk;
+    SECKEYPrivateKey *privk = SECKEY_CreateRSAPrivateKey(1024, &pubk, NULL);
+    ASSERT_NE(nullptr, pubk);
+    pubk_.reset(pubk);
+    ASSERT_NE(nullptr, privk);
+    privk_.reset(privk);
+
+    SECKEYECParams ecParams = {siBuffer, NULL, 0};
+    SECOidData *oidData;
+    oidData = SECOID_FindOIDByTag(SEC_OID_CURVE25519);
+    ASSERT_NE(nullptr, oidData);
+    SECITEM_AllocItem(NULL, &ecParams, (2 + oidData->oid.len));
+    ecParams.data[0] = SEC_ASN1_OBJECT_ID; /* we have to prepend 0x06 */
+    ecParams.data[1] = oidData->oid.len;
+    memcpy(ecParams.data + 2, oidData->oid.data, oidData->oid.len);
+    SECKEYPublicKey *ecPubk;
+    SECKEYPrivateKey *ecPrivk =
+        SECKEY_CreateECPrivateKey(&ecParams, &ecPubk, NULL);
+    ASSERT_NE(nullptr, ecPubk);
+    ecPubk_.reset(ecPubk);
+    ASSERT_NE(nullptr, ecPrivk);
+    ecPrivk_.reset(ecPrivk);
+  }
+
+  void CreatePssParams(SECKEYRSAPSSParams *params, SECOidTag hashAlgTag) {
+    PORT_Memset(params, 0, sizeof(SECKEYRSAPSSParams));
+
+    params->hashAlg = (SECAlgorithmID *)PORT_ArenaZAlloc(
+        arena_.get(), sizeof(SECAlgorithmID));
+    ASSERT_NE(nullptr, params->hashAlg);
+    SECStatus rv =
+        SECOID_SetAlgorithmID(arena_.get(), params->hashAlg, hashAlgTag, NULL);
+    ASSERT_EQ(SECSuccess, rv);
+  }
+
+  void CreatePssParams(SECKEYRSAPSSParams *params, SECOidTag hashAlgTag,
+                       SECOidTag maskHashAlgTag) {
+    CreatePssParams(params, hashAlgTag);
+
+    SECAlgorithmID maskHashAlg;
+    PORT_Memset(&maskHashAlg, 0, sizeof(maskHashAlg));
+    SECOID_SetAlgorithmID(arena_.get(), &maskHashAlg, maskHashAlgTag, NULL);
+
+    SECItem *maskHashAlgItem =
+        SEC_ASN1EncodeItem(arena_.get(), NULL, &maskHashAlg,
+                           SEC_ASN1_GET(SECOID_AlgorithmIDTemplate));
+
+    params->maskAlg = (SECAlgorithmID *)PORT_ArenaZAlloc(
+        arena_.get(), sizeof(SECAlgorithmID));
+    ASSERT_NE(nullptr, params->maskAlg);
+
+    SECStatus rv = SECOID_SetAlgorithmID(arena_.get(), params->maskAlg,
+                                         SEC_OID_PKCS1_MGF1, maskHashAlgItem);
+    ASSERT_EQ(SECSuccess, rv);
+  }
+
+  void CreatePssParams(SECKEYRSAPSSParams *params, SECOidTag hashAlgTag,
+                       SECOidTag maskHashAlgTag, unsigned long saltLength) {
+    CreatePssParams(params, hashAlgTag, maskHashAlgTag);
+
+    SECItem *saltLengthItem =
+        SEC_ASN1EncodeInteger(arena_.get(), &params->saltLength, saltLength);
+    ASSERT_EQ(&params->saltLength, saltLengthItem);
+  }
+
+  void CheckHashAlg(SECKEYRSAPSSParams *params, SECOidTag hashAlgTag) {
+    // If hash algorithm is SHA-1, it must be omitted in the parameters
+    if (hashAlgTag == SEC_OID_SHA1) {
+      EXPECT_EQ(nullptr, params->hashAlg);
+    } else {
+      EXPECT_NE(nullptr, params->hashAlg);
+      EXPECT_EQ(hashAlgTag, SECOID_GetAlgorithmTag(params->hashAlg));
+    }
+  }
+
+  void CheckMaskAlg(SECKEYRSAPSSParams *params, SECOidTag hashAlgTag) {
+    SECStatus rv;
+
+    // If hash algorithm is SHA-1, it must be omitted in the parameters
+    if (hashAlgTag == SEC_OID_SHA1)
+      EXPECT_EQ(nullptr, params->hashAlg);
+    else {
+      EXPECT_NE(nullptr, params->maskAlg);
+      EXPECT_EQ(SEC_OID_PKCS1_MGF1, SECOID_GetAlgorithmTag(params->maskAlg));
+
+      SECAlgorithmID hashAlg;
+      rv = SEC_QuickDERDecodeItem(arena_.get(), &hashAlg,
+                                  SEC_ASN1_GET(SECOID_AlgorithmIDTemplate),
+                                  &params->maskAlg->parameters);
+      ASSERT_EQ(SECSuccess, rv);
+
+      EXPECT_EQ(hashAlgTag, SECOID_GetAlgorithmTag(&hashAlg));
+    }
+  }
+
+  void CheckSaltLength(SECKEYRSAPSSParams *params, SECOidTag hashAlg) {
+    // If the salt length parameter is missing, that means it is 20 (default)
+    if (!params->saltLength.data) {
+      return;
+    }
+
+    unsigned long value;
+    SECStatus rv = SEC_ASN1DecodeInteger(&params->saltLength, &value);
+    ASSERT_EQ(SECSuccess, rv);
+
+    // The salt length are usually the same as the hash length,
+    // except for the case where the hash length exceeds the limit
+    // set by the key length
+    switch (hashAlg) {
+      case SEC_OID_SHA1:
+        EXPECT_EQ(20UL, value);
+        break;
+      case SEC_OID_SHA224:
+        EXPECT_EQ(28UL, value);
+        break;
+      case SEC_OID_SHA256:
+        EXPECT_EQ(32UL, value);
+        break;
+      case SEC_OID_SHA384:
+        EXPECT_EQ(48UL, value);
+        break;
+      case SEC_OID_SHA512:
+        // Truncated from 64, because our private key is 1024-bit
+        EXPECT_EQ(62UL, value);
+        break;
+      default:
+        FAIL();
+    }
+  }
+};
+
+class SignParamsTest
+    : public SignParamsTestF,
+      public ::testing::WithParamInterface<std::tuple<SECOidTag, SECOidTag>> {};
+
+class SignParamsSourceTest : public SignParamsTestF,
+                             public ::testing::WithParamInterface<SECOidTag> {};
+
+TEST_P(SignParamsTest, CreateRsa) {
+  SECOidTag hashAlg = std::get<0>(GetParam());
+  SECOidTag srcHashAlg = std::get<1>(GetParam());
+
+  SECItem *srcParams;
+  if (srcHashAlg != SEC_OID_UNKNOWN) {
+    SECKEYRSAPSSParams pssParams;
+    ASSERT_NO_FATAL_FAILURE(
+        CreatePssParams(&pssParams, srcHashAlg, srcHashAlg));
+    srcParams = SEC_ASN1EncodeItem(arena_.get(), nullptr, &pssParams,
+                                   SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate));
+    ASSERT_NE(nullptr, srcParams);
+  } else {
+    srcParams = NULL;
+  }
+
+  SECItem *params = SEC_CreateSignatureAlgorithmParameters(
+      arena_.get(), nullptr, SEC_OID_PKCS1_RSA_ENCRYPTION, hashAlg, srcParams,
+      privk_.get());
+
+  // PKCS#1 RSA actually doesn't take any parameters, but if it is
+  // given, return a copy of it
+  if (srcHashAlg != SEC_OID_UNKNOWN) {
+    EXPECT_EQ(srcParams->len, params->len);
+    EXPECT_EQ(0, memcmp(params->data, srcParams->data, srcParams->len));
+  } else {
+    EXPECT_EQ(nullptr, params);
+  }
+}
+
+TEST_P(SignParamsTest, CreateRsaPss) {
+  SECOidTag hashAlg = std::get<0>(GetParam());
+  SECOidTag srcHashAlg = std::get<1>(GetParam());
+
+  SECItem *srcParams;
+  if (srcHashAlg != SEC_OID_UNKNOWN) {
+    SECKEYRSAPSSParams pssParams;
+    ASSERT_NO_FATAL_FAILURE(
+        CreatePssParams(&pssParams, srcHashAlg, srcHashAlg));
+    srcParams = SEC_ASN1EncodeItem(arena_.get(), nullptr, &pssParams,
+                                   SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate));
+    ASSERT_NE(nullptr, srcParams);
+  } else {
+    srcParams = NULL;
+  }
+
+  SECItem *params = SEC_CreateSignatureAlgorithmParameters(
+      arena_.get(), nullptr, SEC_OID_PKCS1_RSA_PSS_SIGNATURE, hashAlg,
+      srcParams, privk_.get());
+
+  if (hashAlg != SEC_OID_UNKNOWN && srcHashAlg != SEC_OID_UNKNOWN &&
+      hashAlg != srcHashAlg) {
+    EXPECT_EQ(nullptr, params);
+    return;
+  }
+
+  EXPECT_NE(nullptr, params);
+
+  SECKEYRSAPSSParams pssParams;
+  PORT_Memset(&pssParams, 0, sizeof(pssParams));
+  SECStatus rv =
+      SEC_QuickDERDecodeItem(arena_.get(), &pssParams,
+                             SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate), params);
+  ASSERT_EQ(SECSuccess, rv);
+
+  if (hashAlg == SEC_OID_UNKNOWN) {
+    if (!pssParams.hashAlg) {
+      hashAlg = SEC_OID_SHA1;
+    } else {
+      hashAlg = SECOID_GetAlgorithmTag(pssParams.hashAlg);
+    }
+
+    if (srcHashAlg == SEC_OID_UNKNOWN) {
+      // If both hashAlg and srcHashAlg is unset, NSS will decide the hash
+      // algorithm based on the key length; in this case it's SHA256
+      EXPECT_EQ(SEC_OID_SHA256, hashAlg);
+    } else {
+      EXPECT_EQ(srcHashAlg, hashAlg);
+    }
+  }
+
+  ASSERT_NO_FATAL_FAILURE(CheckHashAlg(&pssParams, hashAlg));
+  ASSERT_NO_FATAL_FAILURE(CheckMaskAlg(&pssParams, hashAlg));
+  ASSERT_NO_FATAL_FAILURE(CheckSaltLength(&pssParams, hashAlg));
+
+  // The default trailer field (1) must be omitted
+  EXPECT_EQ(nullptr, pssParams.trailerField.data);
+}
+
+TEST_P(SignParamsTest, CreateRsaPssWithECPrivateKey) {
+  SECOidTag hashAlg = std::get<0>(GetParam());
+  SECOidTag srcHashAlg = std::get<1>(GetParam());
+
+  SECItem *srcParams;
+  if (srcHashAlg != SEC_OID_UNKNOWN) {
+    SECKEYRSAPSSParams pssParams;
+    ASSERT_NO_FATAL_FAILURE(
+        CreatePssParams(&pssParams, srcHashAlg, srcHashAlg));
+    srcParams = SEC_ASN1EncodeItem(arena_.get(), nullptr, &pssParams,
+                                   SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate));
+    ASSERT_NE(nullptr, srcParams);
+  } else {
+    srcParams = NULL;
+  }
+
+  SECItem *params = SEC_CreateSignatureAlgorithmParameters(
+      arena_.get(), nullptr, SEC_OID_PKCS1_RSA_PSS_SIGNATURE, hashAlg,
+      srcParams, ecPrivk_.get());
+
+  EXPECT_EQ(nullptr, params);
+}
+
+TEST_P(SignParamsTest, CreateRsaPssWithInvalidHashAlg) {
+  SECOidTag srcHashAlg = std::get<1>(GetParam());
+
+  SECItem *srcParams;
+  if (srcHashAlg != SEC_OID_UNKNOWN) {
+    SECKEYRSAPSSParams pssParams;
+    ASSERT_NO_FATAL_FAILURE(
+        CreatePssParams(&pssParams, srcHashAlg, srcHashAlg));
+    srcParams = SEC_ASN1EncodeItem(arena_.get(), nullptr, &pssParams,
+                                   SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate));
+    ASSERT_NE(nullptr, srcParams);
+  } else {
+    srcParams = NULL;
+  }
+
+  SECItem *params = SEC_CreateSignatureAlgorithmParameters(
+      arena_.get(), nullptr, SEC_OID_PKCS1_RSA_PSS_SIGNATURE, SEC_OID_MD5,
+      srcParams, privk_.get());
+
+  EXPECT_EQ(nullptr, params);
+}
+
+TEST_P(SignParamsSourceTest, CreateRsaPssWithInvalidHashAlg) {
+  SECOidTag hashAlg = GetParam();
+
+  SECItem *srcParams;
+  SECKEYRSAPSSParams pssParams;
+  ASSERT_NO_FATAL_FAILURE(
+      CreatePssParams(&pssParams, SEC_OID_MD5, SEC_OID_MD5));
+  srcParams = SEC_ASN1EncodeItem(arena_.get(), nullptr, &pssParams,
+                                 SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate));
+  ASSERT_NE(nullptr, srcParams);
+
+  SECItem *params = SEC_CreateSignatureAlgorithmParameters(
+      arena_.get(), nullptr, SEC_OID_PKCS1_RSA_PSS_SIGNATURE, hashAlg,
+      srcParams, privk_.get());
+
+  EXPECT_EQ(nullptr, params);
+}
+
+TEST_P(SignParamsSourceTest, CreateRsaPssWithInvalidSaltLength) {
+  SECOidTag hashAlg = GetParam();
+
+  SECItem *srcParams;
+  SECKEYRSAPSSParams pssParams;
+  ASSERT_NO_FATAL_FAILURE(
+      CreatePssParams(&pssParams, SEC_OID_SHA512, SEC_OID_SHA512, 100));
+  srcParams = SEC_ASN1EncodeItem(arena_.get(), nullptr, &pssParams,
+                                 SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate));
+  ASSERT_NE(nullptr, srcParams);
+
+  SECItem *params = SEC_CreateSignatureAlgorithmParameters(
+      arena_.get(), nullptr, SEC_OID_PKCS1_RSA_PSS_SIGNATURE, hashAlg,
+      srcParams, privk_.get());
+
+  EXPECT_EQ(nullptr, params);
+}
+
+TEST_P(SignParamsSourceTest, CreateRsaPssWithHashMismatch) {
+  SECOidTag hashAlg = GetParam();
+
+  SECItem *srcParams;
+  SECKEYRSAPSSParams pssParams;
+  ASSERT_NO_FATAL_FAILURE(
+      CreatePssParams(&pssParams, SEC_OID_SHA256, SEC_OID_SHA512));
+  srcParams = SEC_ASN1EncodeItem(arena_.get(), nullptr, &pssParams,
+                                 SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate));
+  ASSERT_NE(nullptr, srcParams);
+
+  SECItem *params = SEC_CreateSignatureAlgorithmParameters(
+      arena_.get(), nullptr, SEC_OID_PKCS1_RSA_PSS_SIGNATURE, hashAlg,
+      srcParams, privk_.get());
+
+  EXPECT_EQ(nullptr, params);
+}
+
+INSTANTIATE_TEST_CASE_P(
+    SignParamsTestCases, SignParamsTest,
+    ::testing::Combine(::testing::Values(SEC_OID_UNKNOWN, SEC_OID_SHA1,
+                                         SEC_OID_SHA224, SEC_OID_SHA256,
+                                         SEC_OID_SHA384, SEC_OID_SHA512),
+                       ::testing::Values(SEC_OID_UNKNOWN, SEC_OID_SHA1,
+                                         SEC_OID_SHA224, SEC_OID_SHA256,
+                                         SEC_OID_SHA384, SEC_OID_SHA512)));
+
+INSTANTIATE_TEST_CASE_P(SignParamsSourceTestCases, SignParamsSourceTest,
+                        ::testing::Values(SEC_OID_UNKNOWN, SEC_OID_SHA1,
+                                          SEC_OID_SHA224, SEC_OID_SHA256,
+                                          SEC_OID_SHA384, SEC_OID_SHA512));
+
+}  // namespace nss_test
new file mode 100644
--- /dev/null
+++ b/gtests/cryptohi_gtest/manifest.mn
@@ -0,0 +1,22 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+CORE_DEPTH = ../..
+DEPTH      = ../..
+MODULE = nss
+
+CPPSRCS = \
+      cryptohi_unittest.cc \
+      $(NULL)
+
+INCLUDES += -I$(CORE_DEPTH)/gtests/google_test/gtest/include \
+            -I$(CORE_DEPTH)/gtests/common \
+            -I$(CORE_DEPTH)/cpputil
+
+REQUIRES = nspr gtest
+
+PROGRAM = cryptohi_gtest
+
+EXTRA_LIBS = $(DIST)/lib/$(LIB_PREFIX)gtest.$(LIB_SUFFIX) $(EXTRA_OBJS) \
+             $(DIST)/lib/$(LIB_PREFIX)gtestutil.$(LIB_SUFFIX)
--- a/gtests/manifest.mn
+++ b/gtests/manifest.mn
@@ -16,16 +16,17 @@ UTIL_SRCDIRS = \
 	$(NULL)
 endif
 
 ifneq ($(NSS_BUILD_SOFTOKEN_ONLY),1)
 ifneq ($(NSS_BUILD_UTIL_ONLY),1)
 NSS_SRCDIRS = \
 	certdb_gtest \
 	certhigh_gtest \
+	cryptohi_gtest \
 	der_gtest \
 	pk11_gtest \
 	softoken_gtest \
 	ssl_gtest \
 	nss_bogo_shim \
 	$(NULL)
 endif
 endif
--- a/gtests/ssl_gtest/ssl_auth_unittest.cc
+++ b/gtests/ssl_gtest/ssl_auth_unittest.cc
@@ -24,17 +24,35 @@ namespace nss_test {
 
 TEST_P(TlsConnectGeneric, ServerAuthBigRsa) {
   Reset(TlsAgent::kRsa2048);
   Connect();
   CheckKeys();
 }
 
 TEST_P(TlsConnectGeneric, ServerAuthRsaChain) {
-  Reset(TlsAgent::kServerRsaChain);
+  Reset("rsa_chain");
+  Connect();
+  CheckKeys();
+  size_t chain_length;
+  EXPECT_TRUE(client_->GetPeerChainLength(&chain_length));
+  EXPECT_EQ(2UL, chain_length);
+}
+
+TEST_P(TlsConnectGeneric, ServerAuthRsaPssChain) {
+  Reset("rsa_pss_chain");
+  Connect();
+  CheckKeys();
+  size_t chain_length;
+  EXPECT_TRUE(client_->GetPeerChainLength(&chain_length));
+  EXPECT_EQ(2UL, chain_length);
+}
+
+TEST_P(TlsConnectGeneric, ServerAuthRsaCARsaPssChain) {
+  Reset("rsa_ca_rsa_pss_chain");
   Connect();
   CheckKeys();
   size_t chain_length;
   EXPECT_TRUE(client_->GetPeerChainLength(&chain_length));
   EXPECT_EQ(2UL, chain_length);
 }
 
 TEST_P(TlsConnectGeneric, ClientAuth) {
--- a/gtests/ssl_gtest/tls_agent.cc
+++ b/gtests/ssl_gtest/tls_agent.cc
@@ -31,17 +31,16 @@ namespace nss_test {
 const char* TlsAgent::states[] = {"INIT", "CONNECTING", "CONNECTED", "ERROR"};
 
 const std::string TlsAgent::kClient = "client";    // both sign and encrypt
 const std::string TlsAgent::kRsa2048 = "rsa2048";  // bigger
 const std::string TlsAgent::kServerRsa = "rsa";    // both sign and encrypt
 const std::string TlsAgent::kServerRsaSign = "rsa_sign";
 const std::string TlsAgent::kServerRsaPss = "rsa_pss";
 const std::string TlsAgent::kServerRsaDecrypt = "rsa_decrypt";
-const std::string TlsAgent::kServerRsaChain = "rsa_chain";
 const std::string TlsAgent::kServerEcdsa256 = "ecdsa256";
 const std::string TlsAgent::kServerEcdsa384 = "ecdsa384";
 const std::string TlsAgent::kServerEcdsa521 = "ecdsa521";
 const std::string TlsAgent::kServerEcdhRsa = "ecdh_rsa";
 const std::string TlsAgent::kServerEcdhEcdsa = "ecdh_ecdsa";
 const std::string TlsAgent::kServerDsa = "dsa";
 
 TlsAgent::TlsAgent(const std::string& name, Role role,
--- a/gtests/ssl_gtest/tls_agent.h
+++ b/gtests/ssl_gtest/tls_agent.h
@@ -61,17 +61,16 @@ class TlsAgent : public PollTarget {
   enum State { STATE_INIT, STATE_CONNECTING, STATE_CONNECTED, STATE_ERROR };
 
   static const std::string kClient;     // the client key is sign only
   static const std::string kRsa2048;    // bigger sign and encrypt for either
   static const std::string kServerRsa;  // both sign and encrypt
   static const std::string kServerRsaSign;
   static const std::string kServerRsaPss;
   static const std::string kServerRsaDecrypt;
-  static const std::string kServerRsaChain;  // A cert that requires a chain.
   static const std::string kServerEcdsa256;
   static const std::string kServerEcdsa384;
   static const std::string kServerEcdsa521;
   static const std::string kServerEcdhEcdsa;
   static const std::string kServerEcdhRsa;
   static const std::string kServerDsa;
 
   TlsAgent(const std::string& name, Role role, SSLProtocolVariant variant);
--- a/lib/certdb/certdb.c
+++ b/lib/certdb/certdb.c
@@ -1187,16 +1187,17 @@ CERT_CheckKeyUsage(CERTCertificate *cert
         KeyType keyType = CERT_GetCertKeyType(&cert->subjectPublicKeyInfo);
         /* turn off the special bit */
         requiredUsage &= (~KU_KEY_AGREEMENT_OR_ENCIPHERMENT);
 
         switch (keyType) {
             case rsaKey:
                 requiredUsage |= KU_KEY_ENCIPHERMENT;
                 break;
+            case rsaPssKey:
             case dsaKey:
                 requiredUsage |= KU_DIGITAL_SIGNATURE;
                 break;
             case dhKey:
                 requiredUsage |= KU_KEY_AGREEMENT;
                 break;
             case ecKey:
                 /* Accept either signature or agreement. */
--- a/lib/cryptohi/cryptohi.h
+++ b/lib/cryptohi/cryptohi.h
@@ -55,16 +55,24 @@ extern SECItem *DSAU_DecodeDerSigToLen(c
 /*
 ** Create a new signature context used for signing a data stream.
 **      "alg" the signature algorithm to use (e.g. SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION)
 **	"privKey" the private key to use
 */
 extern SGNContext *SGN_NewContext(SECOidTag alg, SECKEYPrivateKey *privKey);
 
 /*
+** Create a new signature context from an algorithmID.
+**      "alg" the signature algorithm to use
+**	"privKey" the private key to use
+*/
+extern SGNContext *SGN_NewContextWithAlgorithmID(SECAlgorithmID *alg,
+                                                 SECKEYPrivateKey *privKey);
+
+/*
 ** Destroy a signature-context object
 **	"cx" the object
 **	"freeit" if PR_TRUE then free the object as well as its sub-objects
 */
 extern void SGN_DestroyContext(SGNContext *cx, PRBool freeit);
 
 /*
 ** Reset the signing context "cx" to its initial state, preparing it for
@@ -101,16 +109,31 @@ extern SECStatus SGN_End(SGNContext *cx,
 **	"algid" the signature/hash algorithm to sign with
 **		(must be compatible with the key type).
 */
 extern SECStatus SEC_SignData(SECItem *result,
                               const unsigned char *buf, int len,
                               SECKEYPrivateKey *pk, SECOidTag algid);
 
 /*
+** Sign a single block of data using private key encryption and given
+** signature/hash algorithm with parameters from an algorithmID.
+**	"result" the final signature data (memory is allocated)
+**	"buf" the input data to sign
+**	"len" the amount of data to sign
+**	"pk" the private key to encrypt with
+**	"algid" the signature/hash algorithm to sign with
+**		(must be compatible with the key type).
+*/
+extern SECStatus SEC_SignDataWithAlgorithmID(SECItem *result,
+                                             const unsigned char *buf, int len,
+                                             SECKEYPrivateKey *pk,
+                                             SECAlgorithmID *algid);
+
+/*
 ** Sign a pre-digested block of data using private key encryption, encoding
 **  The given signature/hash algorithm.
 **	"result" the final signature data (memory is allocated)
 **	"digest" the digest to sign
 **	"privKey" the private key to encrypt with
 **	"algtag" The algorithm tag to encode (need for RSA only)
 */
 extern SECStatus SGN_Digest(SECKEYPrivateKey *privKey,
@@ -127,30 +150,68 @@ extern SECStatus SGN_Digest(SECKEYPrivat
 ** 	"len" the amount of data to sign
 ** 	"pk" the private key to encrypt with
 */
 extern SECStatus SEC_DerSignData(PLArenaPool *arena, SECItem *result,
                                  const unsigned char *buf, int len,
                                  SECKEYPrivateKey *pk, SECOidTag algid);
 
 /*
+** DER sign a single block of data using private key encryption and
+** the given signature/hash algorithm with parameters from an
+** algorithmID. This routine first computes a digital signature using
+** SEC_SignData, then wraps it with an CERTSignedData and then der
+** encodes the result.
+**	"arena" is the memory arena to use to allocate data from
+** 	"result" the final der encoded data (memory is allocated)
+** 	"buf" the input data to sign
+** 	"len" the amount of data to sign
+** 	"pk" the private key to encrypt with
+**	"algid" the signature/hash algorithm to sign with
+**		(must be compatible with the key type).
+*/
+extern SECStatus SEC_DerSignDataWithAlgorithmID(PLArenaPool *arena,
+                                                SECItem *result,
+                                                const unsigned char *buf,
+                                                int len,
+                                                SECKEYPrivateKey *pk,
+                                                SECAlgorithmID *algid);
+
+/*
 ** Destroy a signed-data object.
 **	"sd" the object
 **	"freeit" if PR_TRUE then free the object as well as its sub-objects
 */
 extern void SEC_DestroySignedData(CERTSignedData *sd, PRBool freeit);
 
 /*
 ** Get the signature algorithm tag number for the given key type and hash
 ** algorithm tag. Returns SEC_OID_UNKNOWN if key type and hash algorithm
 ** do not match or are not supported.
 */
 extern SECOidTag SEC_GetSignatureAlgorithmOidTag(KeyType keyType,
                                                  SECOidTag hashAlgTag);
 
+/*
+** Create algorithm parameters for signing. Return a new item
+** allocated from arena, or NULL on failure.
+**	"arena" is the memory arena to use to allocate data from
+**	"result" the encoded parameters (memory is allocated)
+**	"signAlgTag" is the signing algorithm
+**	"hashAlgTag" is the preferred hash algorithm
+**	"params" is the default parameters
+**	"key" is the private key
+*/
+extern SECItem *SEC_CreateSignatureAlgorithmParameters(PLArenaPool *arena,
+                                                       SECItem *result,
+                                                       SECOidTag signAlgTag,
+                                                       SECOidTag hashAlgTag,
+                                                       const SECItem *params,
+                                                       const SECKEYPrivateKey *key);
+
 /****************************************/
 /*
 ** Signature verification operations
 */
 
 /*
 ** Create a signature verification context. This version is deprecated,
 **  This function is deprecated. Use VFY_CreateContextDirect or
--- a/lib/cryptohi/keyi.h
+++ b/lib/cryptohi/keyi.h
@@ -12,11 +12,14 @@ SEC_BEGIN_PROTOS
 KeyType seckey_GetKeyType(SECOidTag pubKeyOid);
 
 /* extract the 'encryption' (could be signing) and hash oids from and
  * algorithm, key and parameters (parameters is the parameters field
  * of a algorithm ID structure (SECAlgorithmID)*/
 SECStatus sec_DecodeSigAlg(const SECKEYPublicKey *key, SECOidTag sigAlg,
                            const SECItem *param, SECOidTag *encalg, SECOidTag *hashalg);
 
+SECStatus sec_RSAPSSParamsToMechanism(CK_RSA_PKCS_PSS_PARAMS *mech,
+                                      const SECKEYRSAPSSParams *params);
+
 SEC_END_PROTOS
 
 #endif /* _KEYHI_H_ */
--- a/lib/cryptohi/seckey.c
+++ b/lib/cryptohi/seckey.c
@@ -1043,16 +1043,17 @@ SECKEY_PublicKeyStrengthInBits(const SEC
 unsigned
 SECKEY_SignatureLen(const SECKEYPublicKey *pubk)
 {
     unsigned char b0;
     unsigned size;
 
     switch (pubk->keyType) {
         case rsaKey:
+        case rsaPssKey:
             b0 = pubk->u.rsa.modulus.data[0];
             return b0 ? pubk->u.rsa.modulus.len : pubk->u.rsa.modulus.len - 1;
         case dsaKey:
             return pubk->u.dsa.params.subPrime.len * 2;
         case ecKey:
             /* Get the base point order length in bits and adjust */
             size = SECKEY_ECParamsToBasePointOrderLen(
                 &pubk->u.ec.DEREncodedParams);
@@ -1969,8 +1970,97 @@ SECKEY_GetECCOid(const SECKEYECParams *p
         return 0;
     oid.len = params->len - 2;
     oid.data = params->data + 2;
     if ((oidData = SECOID_FindOID(&oid)) == NULL)
         return 0;
 
     return oidData->offset;
 }
+
+static CK_MECHANISM_TYPE
+sec_GetHashMechanismByOidTag(SECOidTag tag)
+{
+    switch (tag) {
+        case SEC_OID_SHA512:
+            return CKM_SHA512;
+        case SEC_OID_SHA384:
+            return CKM_SHA384;
+        case SEC_OID_SHA256:
+            return CKM_SHA256;
+        default:
+            PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+        /* fallthrough */
+        case SEC_OID_SHA1:
+            break;
+    }
+    return CKM_SHA_1;
+}
+
+static CK_RSA_PKCS_MGF_TYPE
+sec_GetMgfTypeByOidTag(SECOidTag tag)
+{
+    switch (tag) {
+        case SEC_OID_SHA512:
+            return CKG_MGF1_SHA512;
+        case SEC_OID_SHA384:
+            return CKG_MGF1_SHA384;
+        case SEC_OID_SHA256:
+            return CKG_MGF1_SHA256;
+        default:
+            PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+        /* fallthrough */
+        case SEC_OID_SHA1:
+            break;
+    }
+    return CKG_MGF1_SHA1;
+}
+
+SECStatus
+sec_RSAPSSParamsToMechanism(CK_RSA_PKCS_PSS_PARAMS *mech,
+                            const SECKEYRSAPSSParams *params)
+{
+    SECStatus rv = SECSuccess;
+    SECOidTag hashAlgTag;
+    unsigned long saltLength;
+
+    PORT_Memset(mech, 0, sizeof(CK_RSA_PKCS_PSS_PARAMS));
+
+    if (params->hashAlg) {
+        hashAlgTag = SECOID_GetAlgorithmTag(params->hashAlg);
+    } else {
+        hashAlgTag = SEC_OID_SHA1; /* default, SHA-1 */
+    }
+    mech->hashAlg = sec_GetHashMechanismByOidTag(hashAlgTag);
+
+    if (params->maskAlg) {
+        SECAlgorithmID maskHashAlg;
+        SECOidTag maskHashAlgTag;
+        PORTCheapArenaPool tmpArena;
+
+        if (SECOID_GetAlgorithmTag(params->maskAlg) != SEC_OID_PKCS1_MGF1) {
+            /* only MGF1 is known to PKCS#11 */
+            PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+            return SECFailure;
+        }
+
+        PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);
+        rv = SEC_QuickDERDecodeItem(&tmpArena.arena, &maskHashAlg,
+                                    SEC_ASN1_GET(SECOID_AlgorithmIDTemplate),
+                                    &params->maskAlg->parameters);
+        PORT_DestroyCheapArena(&tmpArena);
+        if (rv != SECSuccess) {
+            return rv;
+        }
+        maskHashAlgTag = SECOID_GetAlgorithmTag(&maskHashAlg);
+        mech->mgf = sec_GetMgfTypeByOidTag(maskHashAlgTag);
+    } else {
+        mech->mgf = CKG_MGF1_SHA1; /* default, MGF1 with SHA-1 */
+    }
+
+    rv = SEC_ASN1DecodeInteger((SECItem *)&params->saltLength, &saltLength);
+    if (rv != SECSuccess) {
+        return rv;
+    }
+    mech->sLen = saltLength;
+
+    return rv;
+}
--- a/lib/cryptohi/secsign.c
+++ b/lib/cryptohi/secsign.c
@@ -17,57 +17,73 @@
 #include "keyi.h"
 
 struct SGNContextStr {
     SECOidTag signalg;
     SECOidTag hashalg;
     void *hashcx;
     const SECHashObject *hashobj;
     SECKEYPrivateKey *key;
+    SECItem *params;
 };
 
-SGNContext *
-SGN_NewContext(SECOidTag alg, SECKEYPrivateKey *key)
+static SGNContext *
+sgn_NewContext(SECOidTag alg, SECItem *params, SECKEYPrivateKey *key)
 {
     SGNContext *cx;
     SECOidTag hashalg, signalg;
     KeyType keyType;
     SECStatus rv;
 
     /* OK, map a PKCS #7 hash and encrypt algorithm into
      * a standard hashing algorithm. Why did we pass in the whole
      * PKCS #7 algTag if we were just going to change here you might
      * ask. Well the answer is for some cards we may have to do the
      * hashing on card. It may not support CKM_RSA_PKCS sign algorithm,
      * it may just support CKM_SHA1_RSA_PKCS and/or CKM_MD5_RSA_PKCS.
      */
     /* we have a private key, not a public key, so don't pass it in */
-    rv = sec_DecodeSigAlg(NULL, alg, NULL, &signalg, &hashalg);
+    rv = sec_DecodeSigAlg(NULL, alg, params, &signalg, &hashalg);
     if (rv != SECSuccess) {
         PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
         return 0;
     }
     keyType = seckey_GetKeyType(signalg);
 
     /* verify our key type */
     if (key->keyType != keyType &&
-        !((key->keyType == dsaKey) && (keyType == fortezzaKey))) {
+        !((key->keyType == dsaKey) && (keyType == fortezzaKey)) &&
+        !((key->keyType == rsaKey) && (keyType == rsaPssKey))) {
         PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
         return 0;
     }
 
     cx = (SGNContext *)PORT_ZAlloc(sizeof(SGNContext));
     if (cx) {
         cx->hashalg = hashalg;
         cx->signalg = signalg;
         cx->key = key;
+        cx->params = params;
     }
     return cx;
 }
 
+SGNContext *
+SGN_NewContext(SECOidTag alg, SECKEYPrivateKey *key)
+{
+    return sgn_NewContext(alg, NULL, key);
+}
+
+SGNContext *
+SGN_NewContextWithAlgorithmID(SECAlgorithmID *alg, SECKEYPrivateKey *key)
+{
+    SECOidTag tag = SECOID_GetAlgorithmTag(alg);
+    return sgn_NewContext(tag, &alg->parameters, key);
+}
+
 void
 SGN_DestroyContext(SGNContext *cx, PRBool freeit)
 {
     if (cx) {
         if (cx->hashcx != NULL) {
             (*cx->hashobj->destroy)(cx->hashcx, PR_TRUE);
             cx->hashcx = NULL;
         }
@@ -143,25 +159,27 @@ SGN_End(SGNContext *cx, SECItem *result)
     SECStatus rv;
     SECItem digder, sigitem;
     PLArenaPool *arena = 0;
     SECKEYPrivateKey *privKey = cx->key;
     SGNDigestInfo *di = 0;
 
     result->data = 0;
     digder.data = 0;
+    sigitem.data = 0;
 
     /* Finish up digest function */
     if (cx->hashcx == NULL) {
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
         return SECFailure;
     }
     (*cx->hashobj->end)(cx->hashcx, digest, &part1, sizeof(digest));
 
-    if (privKey->keyType == rsaKey) {
+    if (privKey->keyType == rsaKey &&
+        cx->signalg != SEC_OID_PKCS1_RSA_PSS_SIGNATURE) {
 
         arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
         if (!arena) {
             rv = SECFailure;
             goto loser;
         }
 
         /* Construct digest info */
@@ -195,57 +213,92 @@ SGN_End(SGNContext *cx, SECItem *result)
     sigitem.len = signatureLen;
     sigitem.data = (unsigned char *)PORT_Alloc(signatureLen);
 
     if (sigitem.data == NULL) {
         rv = SECFailure;
         goto loser;
     }
 
-    rv = PK11_Sign(privKey, &sigitem, &digder);
-    if (rv != SECSuccess) {
-        PORT_Free(sigitem.data);
-        sigitem.data = NULL;
-        goto loser;
+    if (cx->signalg == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) {
+        CK_RSA_PKCS_PSS_PARAMS mech;
+        SECItem mechItem = { siBuffer, (unsigned char *)&mech, sizeof(mech) };
+
+        PORT_Memset(&mech, 0, sizeof(mech));
+
+        if (cx->params && cx->params->data) {
+            SECKEYRSAPSSParams params;
+
+            arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+            if (!arena) {
+                rv = SECFailure;
+                goto loser;
+            }
+
+            PORT_Memset(&params, 0, sizeof(params));
+            rv = SEC_QuickDERDecodeItem(arena, &params,
+                                        SECKEY_RSAPSSParamsTemplate,
+                                        cx->params);
+            if (rv != SECSuccess) {
+                goto loser;
+            }
+            rv = sec_RSAPSSParamsToMechanism(&mech, &params);
+            if (rv != SECSuccess) {
+                goto loser;
+            }
+        } else {
+            mech.hashAlg = CKM_SHA_1;
+            mech.mgf = CKG_MGF1_SHA1;
+            mech.sLen = digder.len;
+        }
+        rv = PK11_SignWithMechanism(privKey, CKM_RSA_PKCS_PSS, &mechItem,
+                                    &sigitem, &digder);
+        if (rv != SECSuccess) {
+            goto loser;
+        }
+    } else {
+        rv = PK11_Sign(privKey, &sigitem, &digder);
+        if (rv != SECSuccess) {
+            goto loser;
+        }
     }
 
     if ((cx->signalg == SEC_OID_ANSIX9_DSA_SIGNATURE) ||
         (cx->signalg == SEC_OID_ANSIX962_EC_PUBLIC_KEY)) {
         /* DSAU_EncodeDerSigWithLen works for DSA and ECDSA */
         rv = DSAU_EncodeDerSigWithLen(result, &sigitem, sigitem.len);
-        PORT_Free(sigitem.data);
         if (rv != SECSuccess)
             goto loser;
+        SECITEM_FreeItem(&sigitem, PR_FALSE);
     } else {
         result->len = sigitem.len;
         result->data = sigitem.data;
     }
 
 loser:
+    if (rv != SECSuccess) {
+        SECITEM_FreeItem(&sigitem, PR_FALSE);
+    }
     SGN_DestroyDigestInfo(di);
     if (arena != NULL) {
         PORT_FreeArena(arena, PR_FALSE);
     }
     return rv;
 }
 
 /************************************************************************/
 
-/*
-** Sign a block of data returning in result a bunch of bytes that are the
-** signature. Returns zero on success, an error code on failure.
-*/
-SECStatus
-SEC_SignData(SECItem *res, const unsigned char *buf, int len,
-             SECKEYPrivateKey *pk, SECOidTag algid)
+static SECStatus
+sec_SignData(SECItem *res, const unsigned char *buf, int len,
+             SECKEYPrivateKey *pk, SECOidTag algid, SECItem *params)
 {
     SECStatus rv;
     SGNContext *sgn;
 
-    sgn = SGN_NewContext(algid, pk);
+    sgn = sgn_NewContext(algid, params, pk);
 
     if (sgn == NULL)
         return SECFailure;
 
     rv = SGN_Begin(sgn);
     if (rv != SECSuccess)
         goto loser;
 
@@ -255,16 +308,35 @@ SEC_SignData(SECItem *res, const unsigne
 
     rv = SGN_End(sgn, res);
 
 loser:
     SGN_DestroyContext(sgn, PR_TRUE);
     return rv;
 }
 
+/*
+** Sign a block of data returning in result a bunch of bytes that are the
+** signature. Returns zero on success, an error code on failure.
+*/
+SECStatus
+SEC_SignData(SECItem *res, const unsigned char *buf, int len,
+             SECKEYPrivateKey *pk, SECOidTag algid)
+{
+    return sec_SignData(res, buf, len, pk, algid, NULL);
+}
+
+SECStatus
+SEC_SignDataWithAlgorithmID(SECItem *res, const unsigned char *buf, int len,
+                            SECKEYPrivateKey *pk, SECAlgorithmID *algid)
+{
+    SECOidTag tag = SECOID_GetAlgorithmTag(algid);
+    return sec_SignData(res, buf, len, pk, tag, &algid->parameters);
+}
+
 /************************************************************************/
 
 DERTemplate CERTSignedDataTemplate[] =
     {
       { DER_SEQUENCE,
         0, NULL, sizeof(CERTSignedData) },
       { DER_ANY,
         offsetof(CERTSignedData, data) },
@@ -289,20 +361,20 @@ const SEC_ASN1Template CERT_SignedDataTe
         SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
       { SEC_ASN1_BIT_STRING,
         offsetof(CERTSignedData, signature) },
       { 0 }
     };
 
 SEC_ASN1_CHOOSER_IMPLEMENT(CERT_SignedDataTemplate)
 
-SECStatus
-SEC_DerSignData(PLArenaPool *arena, SECItem *result,
+static SECStatus
+sec_DerSignData(PLArenaPool *arena, SECItem *result,
                 const unsigned char *buf, int len, SECKEYPrivateKey *pk,
-                SECOidTag algID)
+                SECOidTag algID, SECItem *params)
 {
     SECItem it;
     CERTSignedData sd;
     SECStatus rv;
 
     it.data = 0;
 
     /* XXX We should probably have some asserts here to make sure the key type
@@ -334,40 +406,58 @@ SEC_DerSignData(PLArenaPool *arena, SECI
                 break;
             default:
                 PORT_SetError(SEC_ERROR_INVALID_KEY);
                 return SECFailure;
         }
     }
 
     /* Sign input buffer */
-    rv = SEC_SignData(&it, buf, len, pk, algID);
+    rv = sec_SignData(&it, buf, len, pk, algID, params);
     if (rv)
         goto loser;
 
     /* Fill out SignedData object */
     PORT_Memset(&sd, 0, sizeof(sd));
     sd.data.data = (unsigned char *)buf;
     sd.data.len = len;
     sd.signature.data = it.data;
     sd.signature.len = it.len << 3; /* convert to bit string */
-    rv = SECOID_SetAlgorithmID(arena, &sd.signatureAlgorithm, algID, 0);
+    rv = SECOID_SetAlgorithmID(arena, &sd.signatureAlgorithm, algID, params);
     if (rv)
         goto loser;
 
     /* DER encode the signed data object */
     rv = DER_Encode(arena, result, CERTSignedDataTemplate, &sd);
 /* FALL THROUGH */
 
 loser:
     PORT_Free(it.data);
     return rv;
 }
 
 SECStatus
+SEC_DerSignData(PLArenaPool *arena, SECItem *result,
+                const unsigned char *buf, int len, SECKEYPrivateKey *pk,
+                SECOidTag algID)
+{
+    return sec_DerSignData(arena, result, buf, len, pk, algID, NULL);
+}
+
+SECStatus
+SEC_DerSignDataWithAlgorithmID(PLArenaPool *arena, SECItem *result,
+                               const unsigned char *buf, int len,
+                               SECKEYPrivateKey *pk,
+                               SECAlgorithmID *algID)
+{
+    SECOidTag tag = SECOID_GetAlgorithmTag(algID);
+    return sec_DerSignData(arena, result, buf, len, pk, tag, &algID->parameters);
+}
+
+SECStatus
 SGN_Digest(SECKEYPrivateKey *privKey,
            SECOidTag algtag, SECItem *result, SECItem *digest)
 {
     int modulusLen;
     SECStatus rv;
     SECItem digder;
     PLArenaPool *arena = 0;
     SGNDigestInfo *di = 0;
@@ -504,8 +594,231 @@ SEC_GetSignatureAlgorithmOidTag(KeyType 
                 default:
                     break;
             }
         default:
             break;
     }
     return sigTag;
 }
+
+static SECItem *
+sec_CreateRSAPSSParameters(PLArenaPool *arena,
+                           SECItem *result,
+                           SECOidTag hashAlgTag,
+                           const SECItem *params,
+                           const SECKEYPrivateKey *key)
+{
+    SECKEYRSAPSSParams pssParams;
+    int modBytes, hashLength;
+    unsigned long saltLength;
+    SECStatus rv;
+
+    if (key->keyType != rsaKey && key->keyType != rsaPssKey) {
+        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+        return NULL;
+    }
+
+    PORT_Memset(&pssParams, 0, sizeof(pssParams));
+
+    if (params && params->data) {
+        /* The parameters field should either be empty or contain
+         * valid RSA-PSS parameters */
+        PORT_Assert(!(params->len == 2 &&
+                      params->data[0] == SEC_ASN1_NULL &&
+                      params->data[1] == 0));
+        rv = SEC_QuickDERDecodeItem(arena, &pssParams,
+                                    SECKEY_RSAPSSParamsTemplate,
+                                    params);
+        if (rv != SECSuccess) {
+            return NULL;
+        }
+    }
+
+    if (pssParams.trailerField.data) {
+        unsigned long trailerField;
+
+        rv = SEC_ASN1DecodeInteger((SECItem *)&pssParams.trailerField,
+                                   &trailerField);
+        if (rv != SECSuccess) {
+            return NULL;
+        }
+        if (trailerField != 1) {
+            PORT_SetError(SEC_ERROR_INVALID_ARGS);
+            return NULL;
+        }
+    }
+
+    modBytes = PK11_GetPrivateModulusLen((SECKEYPrivateKey *)key);
+
+    /* Determine the hash algorithm to use, based on hashAlgTag and
+     * pssParams.hashAlg; there are four cases */
+    if (hashAlgTag != SEC_OID_UNKNOWN) {
+        if (pssParams.hashAlg) {
+            if (SECOID_GetAlgorithmTag(pssParams.hashAlg) != hashAlgTag) {
+                PORT_SetError(SEC_ERROR_INVALID_ARGS);
+                return NULL;
+            }
+        }
+    } else if (hashAlgTag == SEC_OID_UNKNOWN) {
+        if (pssParams.hashAlg) {
+            hashAlgTag = SECOID_GetAlgorithmTag(pssParams.hashAlg);
+        } else {
+            /* Find a suitable hash algorithm based on the NIST recommendation */
+            if (modBytes <= 384) { /* 128, in NIST 800-57, Part 1 */
+                hashAlgTag = SEC_OID_SHA256;
+            } else if (modBytes <= 960) { /* 192, NIST 800-57, Part 1 */
+                hashAlgTag = SEC_OID_SHA384;
+            } else {
+                hashAlgTag = SEC_OID_SHA512;
+            }
+        }
+    }
+
+    if (hashAlgTag != SEC_OID_SHA1 && hashAlgTag != SEC_OID_SHA224 &&
+        hashAlgTag != SEC_OID_SHA256 && hashAlgTag != SEC_OID_SHA384 &&
+        hashAlgTag != SEC_OID_SHA512) {
+        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+        return NULL;
+    }
+
+    /* Now that the hash algorithm is decided, check if it matches the
+     * existing parameters if any */
+    if (pssParams.maskAlg) {
+        SECAlgorithmID maskHashAlg;
+
+        if (SECOID_GetAlgorithmTag(pssParams.maskAlg) != SEC_OID_PKCS1_MGF1) {
+            PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+            return NULL;
+        }
+
+        if (pssParams.maskAlg->parameters.data == NULL) {
+            PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+            return NULL;
+        }
+
+        PORT_Memset(&maskHashAlg, 0, sizeof(maskHashAlg));
+        rv = SEC_QuickDERDecodeItem(arena, &maskHashAlg,
+                                    SEC_ASN1_GET(SECOID_AlgorithmIDTemplate),
+                                    &pssParams.maskAlg->parameters);
+        if (rv != SECSuccess) {
+            return NULL;
+        }
+
+        /* Following the recommendation in RFC 4055, assume the hash
+         * algorithm identical to pssParam.hashAlg */
+        if (SECOID_GetAlgorithmTag(&maskHashAlg) != hashAlgTag) {
+            PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+            return NULL;
+        }
+    }
+
+    hashLength = HASH_ResultLenByOidTag(hashAlgTag);
+
+    if (pssParams.saltLength.data) {
+        rv = SEC_ASN1DecodeInteger((SECItem *)&pssParams.saltLength,
+                                   &saltLength);
+        if (rv != SECSuccess) {
+            return NULL;
+        }
+
+        /* The specified salt length is too long */
+        if (saltLength > modBytes - hashLength - 2) {
+            PORT_SetError(SEC_ERROR_INVALID_ARGS);
+            return NULL;
+        }
+    }
+
+    /* Fill in the parameters */
+    if (pssParams.hashAlg) {
+        if (hashAlgTag == SEC_OID_SHA1) {
+            /* Omit hashAlg if the the algorithm is SHA-1 (default) */
+            pssParams.hashAlg = NULL;
+        }
+    } else {
+        if (hashAlgTag != SEC_OID_SHA1) {
+            pssParams.hashAlg = PORT_ArenaZAlloc(arena, sizeof(SECAlgorithmID));
+            if (!pssParams.hashAlg) {
+                return NULL;
+            }
+            rv = SECOID_SetAlgorithmID(arena, pssParams.hashAlg, hashAlgTag,
+                                       NULL);
+            if (rv != SECSuccess) {
+                return NULL;
+            }
+        }
+    }
+
+    if (pssParams.maskAlg) {
+        if (hashAlgTag == SEC_OID_SHA1) {
+            /* Omit maskAlg if the the algorithm is SHA-1 (default) */
+            pssParams.maskAlg = NULL;
+        }
+    } else {
+        if (hashAlgTag != SEC_OID_SHA1) {
+            SECItem *hashAlgItem;
+
+            PORT_Assert(pssParams.hashAlg != NULL);
+
+            hashAlgItem = SEC_ASN1EncodeItem(arena, NULL, pssParams.hashAlg,
+                                             SEC_ASN1_GET(SECOID_AlgorithmIDTemplate));
+            if (!hashAlgItem) {
+                return NULL;
+            }
+            pssParams.maskAlg = PORT_ArenaZAlloc(arena, sizeof(SECAlgorithmID));
+            if (!pssParams.maskAlg) {
+                return NULL;
+            }
+            rv = SECOID_SetAlgorithmID(arena, pssParams.maskAlg,
+                                       SEC_OID_PKCS1_MGF1, hashAlgItem);
+            if (rv != SECSuccess) {
+                return NULL;
+            }
+        }
+    }
+
+    if (pssParams.saltLength.data) {
+        if (saltLength == 20) {
+            /* Omit the salt length if it is the default */
+            pssParams.saltLength.data = NULL;
+        }
+    } else {
+        /* Find a suitable length from the hash algorithm and modulus bits */
+        saltLength = PR_MIN(hashLength, modBytes - hashLength - 2);
+
+        if (saltLength != 20 &&
+            !SEC_ASN1EncodeInteger(arena, &pssParams.saltLength, saltLength)) {
+            return NULL;
+        }
+    }
+
+    if (pssParams.trailerField.data) {
+        /* Omit trailerField if the value is 1 (default) */
+        pssParams.trailerField.data = NULL;
+    }
+
+    return SEC_ASN1EncodeItem(arena, result,
+                              &pssParams, SECKEY_RSAPSSParamsTemplate);
+}
+
+SECItem *
+SEC_CreateSignatureAlgorithmParameters(PLArenaPool *arena,
+                                       SECItem *result,
+                                       SECOidTag signAlgTag,
+                                       SECOidTag hashAlgTag,
+                                       const SECItem *params,
+                                       const SECKEYPrivateKey *key)
+{
+    switch (signAlgTag) {
+        case SEC_OID_PKCS1_RSA_PSS_SIGNATURE:
+            return sec_CreateRSAPSSParameters(arena, result,
+                                              hashAlgTag, params, key);
+
+        default:
+            if (params == NULL)
+                return NULL;
+            if (result == NULL)
+                result = SECITEM_AllocItem(arena, NULL, 0);
+            if (SECITEM_CopyItem(arena, result, params) != SECSuccess)
+                return NULL;
+            return result;
+    }
+}
--- a/lib/cryptohi/secvfy.c
+++ b/lib/cryptohi/secvfy.c
@@ -131,28 +131,31 @@ struct VFYContextStr {
      */
     union {
         unsigned char buffer[1];
 
         /* the full DSA signature... 40 bytes */
         unsigned char dsasig[DSA_MAX_SIGNATURE_LEN];
         /* the full ECDSA signature */
         unsigned char ecdsasig[2 * MAX_ECKEY_LEN];
+        /* the full RSA signature, only used in RSA-PSS */
+        unsigned char rsasig[(RSA_MAX_MODULUS_BITS + 7) / 8];
     } u;
     unsigned int pkcs1RSADigestInfoLen;
     /* the encoded DigestInfo from a RSA PKCS#1 signature */
     unsigned char *pkcs1RSADigestInfo;
     void *wincx;
     void *hashcx;
     const SECHashObject *hashobj;
     SECOidTag encAlg;    /* enc alg */
     PRBool hasSignature; /* true if the signature was provided in the
                           * VFY_CreateContext call.  If false, the
                           * signature must be provided with a
                           * VFY_EndWithSignature call. */
+    SECItem *params;
 };
 
 static SECStatus
 verifyPKCS1DigestInfo(const VFYContext *cx, const SECItem *digest)
 {
     SECItem pkcs1DigestInfo;
     pkcs1DigestInfo.data = cx->pkcs1RSADigestInfo;
     pkcs1DigestInfo.len = cx->pkcs1RSADigestInfoLen;
@@ -245,18 +248,47 @@ sec_DecodeSigAlg(const SECKEYPublicKey *
             *hashalg = SEC_OID_MD5;
             break;
         case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION:
         case SEC_OID_ISO_SHA_WITH_RSA_SIGNATURE:
         case SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE:
             *hashalg = SEC_OID_SHA1;
             break;
         case SEC_OID_PKCS1_RSA_ENCRYPTION:
+            *hashalg = SEC_OID_UNKNOWN; /* get it from the RSA signature */
+            break;
         case SEC_OID_PKCS1_RSA_PSS_SIGNATURE:
-            *hashalg = SEC_OID_UNKNOWN; /* get it from the RSA signature */
+            if (param && param->data) {
+                SECKEYRSAPSSParams pssParam;
+                arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+                if (arena == NULL) {
+                    return SECFailure;
+                }
+                PORT_Memset(&pssParam, 0, sizeof pssParam);
+                rv = SEC_QuickDERDecodeItem(arena, &pssParam,
+                                            SECKEY_RSAPSSParamsTemplate,
+                                            param);
+                if (rv != SECSuccess) {
+                    PORT_FreeArena(arena, PR_FALSE);
+                    return rv;
+                }
+                if (pssParam.hashAlg) {
+                    *hashalg = SECOID_GetAlgorithmTag(pssParam.hashAlg);
+                } else {
+                    *hashalg = SEC_OID_SHA1; /* default, SHA-1 */
+                }
+                PORT_FreeArena(arena, PR_FALSE);
+                /* only accept hash algorithms */
+                if (HASH_GetHashTypeByOidTag(*hashalg) == HASH_AlgNULL) {
+                    /* error set by HASH_GetHashTypeByOidTag */
+                    return SECFailure;
+                }
+            } else {
+                *hashalg = SEC_OID_SHA1; /* default, SHA-1 */
+            }
             break;
 
         case SEC_OID_ANSIX962_ECDSA_SHA224_SIGNATURE:
         case SEC_OID_PKCS1_SHA224_WITH_RSA_ENCRYPTION:
         case SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA224_DIGEST:
             *hashalg = SEC_OID_SHA224;
             break;
         case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE:
@@ -429,16 +461,30 @@ vfy_CreateContext(const SECKEYPublicKey 
         switch (type) {
             case rsaKey:
                 rv = recoverPKCS1DigestInfo(hashAlg, &cx->hashAlg,
                                             &cx->pkcs1RSADigestInfo,
                                             &cx->pkcs1RSADigestInfoLen,
                                             cx->key,
                                             sig, wincx);
                 break;
+            case rsaPssKey:
+                sigLen = SECKEY_SignatureLen(key);
+                if (sigLen == 0) {
+                    /* error set by SECKEY_SignatureLen */
+                    rv = SECFailure;
+                    break;
+                }
+                if (sig->len != sigLen) {
+                    PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
+                    rv = SECFailure;
+                    break;
+                }
+                PORT_Memcpy(cx->u.buffer, sig->data, sigLen);
+                break;
             case dsaKey:
             case ecKey:
                 sigLen = SECKEY_SignatureLen(key);
                 if (sigLen == 0) {
                     /* error set by SECKEY_SignatureLen */
                     rv = SECFailure;
                     break;
                 }
@@ -491,40 +537,50 @@ VFY_CreateContextDirect(const SECKEYPubl
 {
     return vfy_CreateContext(key, sig, encAlg, hashAlg, hash, wincx);
 }
 
 VFYContext *
 VFY_CreateContextWithAlgorithmID(const SECKEYPublicKey *key, const SECItem *sig,
                                  const SECAlgorithmID *sigAlgorithm, SECOidTag *hash, void *wincx)
 {
+    VFYContext *cx;
     SECOidTag encAlg, hashAlg;
     SECStatus rv = sec_DecodeSigAlg(key,
                                     SECOID_GetAlgorithmTag((SECAlgorithmID *)sigAlgorithm),
                                     &sigAlgorithm->parameters, &encAlg, &hashAlg);
     if (rv != SECSuccess) {
         return NULL;
     }
-    return vfy_CreateContext(key, sig, encAlg, hashAlg, hash, wincx);
+
+    cx = vfy_CreateContext(key, sig, encAlg, hashAlg, hash, wincx);
+    if (sigAlgorithm->parameters.data) {
+        cx->params = SECITEM_DupItem(&sigAlgorithm->parameters);
+    }
+
+    return cx;
 }
 
 void
 VFY_DestroyContext(VFYContext *cx, PRBool freeit)
 {
     if (cx) {
         if (cx->hashcx != NULL) {
             (*cx->hashobj->destroy)(cx->hashcx, PR_TRUE);
             cx->hashcx = NULL;
         }
         if (cx->key) {
             SECKEY_DestroyPublicKey(cx->key);
         }
         if (cx->pkcs1RSADigestInfo) {
             PORT_Free(cx->pkcs1RSADigestInfo);
         }
+        if (cx->params) {
+            SECITEM_FreeItem(cx->params, PR_TRUE);
+        }
         if (freeit) {
             PORT_ZFree(cx, sizeof(VFYContext));
         }
     }
 }
 
 SECStatus
 VFY_Begin(VFYContext *cx)
@@ -557,17 +613,17 @@ VFY_Update(VFYContext *cx, const unsigne
     return SECSuccess;
 }
 
 SECStatus
 VFY_EndWithSignature(VFYContext *cx, SECItem *sig)
 {
     unsigned char final[HASH_LENGTH_MAX];
     unsigned part;
-    SECItem hash, dsasig; /* dsasig is also used for ECDSA */
+    SECItem hash, rsasig, dsasig; /* dsasig is also used for ECDSA */
     SECStatus rv;
 
     if ((cx->hasSignature == PR_FALSE) && (sig == NULL)) {
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
         return SECFailure;
     }
 
     if (cx->hashcx == NULL) {
@@ -593,35 +649,80 @@ VFY_EndWithSignature(VFYContext *cx, SEC
             }
             hash.data = final;
             hash.len = part;
             if (PK11_Verify(cx->key, &dsasig, &hash, cx->wincx) != SECSuccess) {
                 PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
                 return SECFailure;
             }
             break;
-        case rsaKey: {
-            SECItem digest;
-            digest.data = final;
-            digest.len = part;
-            if (sig) {
-                SECOidTag hashid;
-                PORT_Assert(cx->hashAlg != SEC_OID_UNKNOWN);
-                rv = recoverPKCS1DigestInfo(cx->hashAlg, &hashid,
-                                            &cx->pkcs1RSADigestInfo,
-                                            &cx->pkcs1RSADigestInfoLen,
-                                            cx->key,
-                                            sig, cx->wincx);
-                PORT_Assert(cx->hashAlg == hashid);
+        case rsaKey:
+            if (cx->encAlg == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) {
+                CK_RSA_PKCS_PSS_PARAMS mech;
+                SECItem mechItem = { siBuffer, (unsigned char *)&mech, sizeof(mech) };
+                SECKEYRSAPSSParams params;
+                PLArenaPool *arena;
+
+                arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+                if (arena == NULL) {
+                    return SECFailure;
+                }
+
+                PORT_Memset(&params, 0, sizeof(params));
+                rv = SEC_QuickDERDecodeItem(arena, &params,
+                                            SECKEY_RSAPSSParamsTemplate,
+                                            cx->params);
+                if (rv != SECSuccess) {
+                    PORT_FreeArena(arena, PR_FALSE);
+                    return SECFailure;
+                }
+                rv = sec_RSAPSSParamsToMechanism(&mech, &params);
+                PORT_FreeArena(arena, PR_FALSE);
                 if (rv != SECSuccess) {
                     return SECFailure;
                 }
+                rsasig.data = cx->u.buffer;
+                rsasig.len = SECKEY_SignatureLen(cx->key);
+                if (rsasig.len == 0) {
+                    return SECFailure;
+                }
+                if (sig) {
+                    if (sig->len != rsasig.len) {
+                        PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
+                        return SECFailure;
+                    }
+                    PORT_Memcpy(rsasig.data, sig->data, rsasig.len);
+                }
+                hash.data = final;
+                hash.len = part;
+                if (PK11_VerifyWithMechanism(cx->key, CKM_RSA_PKCS_PSS, &mechItem,
+                                             &rsasig, &hash, cx->wincx) != SECSuccess) {
+                    PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
+                    return SECFailure;
+                }
+            } else {
+                SECItem digest;
+                digest.data = final;
+                digest.len = part;
+                if (sig) {
+                    SECOidTag hashid;
+                    PORT_Assert(cx->hashAlg != SEC_OID_UNKNOWN);
+                    rv = recoverPKCS1DigestInfo(cx->hashAlg, &hashid,
+                                                &cx->pkcs1RSADigestInfo,
+                                                &cx->pkcs1RSADigestInfoLen,
+                                                cx->key,
+                                                sig, cx->wincx);
+                    PORT_Assert(cx->hashAlg == hashid);
+                    if (rv != SECSuccess) {
+                        return SECFailure;
+                    }
+                }
+                return verifyPKCS1DigestInfo(cx, &digest);
             }
-            return verifyPKCS1DigestInfo(cx, &digest);
-        }
+            break;
         default:
             PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
             return SECFailure; /* shouldn't happen */
     }
     return SECSuccess;
 }
 
 SECStatus
@@ -717,24 +818,27 @@ VFY_VerifyDigestWithAlgorithmID(const SE
         return SECFailure;
     }
     return vfy_VerifyDigest(digest, key, sig, encAlg, hashAlg, wincx);
 }
 
 static SECStatus
 vfy_VerifyData(const unsigned char *buf, int len, const SECKEYPublicKey *key,
                const SECItem *sig, SECOidTag encAlg, SECOidTag hashAlg,
-               SECOidTag *hash, void *wincx)
+               const SECItem *params, SECOidTag *hash, void *wincx)
 {
     SECStatus rv;
     VFYContext *cx;
 
     cx = vfy_CreateContext(key, sig, encAlg, hashAlg, hash, wincx);
     if (cx == NULL)
         return SECFailure;
+    if (params) {
+        cx->params = SECITEM_DupItem(params);
+    }
 
     rv = VFY_Begin(cx);
     if (rv == SECSuccess) {
         rv = VFY_Update(cx, (unsigned char *)buf, len);
         if (rv == SECSuccess)
             rv = VFY_End(cx);
     }
 
@@ -743,39 +847,40 @@ vfy_VerifyData(const unsigned char *buf,
 }
 
 SECStatus
 VFY_VerifyDataDirect(const unsigned char *buf, int len,
                      const SECKEYPublicKey *key, const SECItem *sig,
                      SECOidTag encAlg, SECOidTag hashAlg,
                      SECOidTag *hash, void *wincx)
 {
-    return vfy_VerifyData(buf, len, key, sig, encAlg, hashAlg, hash, wincx);
+    return vfy_VerifyData(buf, len, key, sig, encAlg, hashAlg, NULL, hash, wincx);
 }
 
 SECStatus
 VFY_VerifyData(const unsigned char *buf, int len, const SECKEYPublicKey *key,
                const SECItem *sig, SECOidTag algid, void *wincx)
 {
     SECOidTag encAlg, hashAlg;
     SECStatus rv = sec_DecodeSigAlg(key, algid, NULL, &encAlg, &hashAlg);
     if (rv != SECSuccess) {
         return rv;
     }
-    return vfy_VerifyData(buf, len, key, sig, encAlg, hashAlg, NULL, wincx);
+    return vfy_VerifyData(buf, len, key, sig, encAlg, hashAlg, NULL, NULL, wincx);
 }
 
 SECStatus
 VFY_VerifyDataWithAlgorithmID(const unsigned char *buf, int len,
                               const SECKEYPublicKey *key,
                               const SECItem *sig,
                               const SECAlgorithmID *sigAlgorithm,
                               SECOidTag *hash, void *wincx)
 {
     SECOidTag encAlg, hashAlg;
     SECOidTag sigAlg = SECOID_GetAlgorithmTag((SECAlgorithmID *)sigAlgorithm);
     SECStatus rv = sec_DecodeSigAlg(key, sigAlg,
                                     &sigAlgorithm->parameters, &encAlg, &hashAlg);
     if (rv != SECSuccess) {
         return rv;
     }
-    return vfy_VerifyData(buf, len, key, sig, encAlg, hashAlg, hash, wincx);
+    return vfy_VerifyData(buf, len, key, sig, encAlg, hashAlg,
+                          &sigAlgorithm->parameters, hash, wincx);
 }
--- a/lib/nss/nss.def
+++ b/lib/nss/nss.def
@@ -1118,8 +1118,17 @@ PK11_GetTokenURI;
 ;+NSS_3.33 { 	# NSS 3.33 release
 ;+    global:
 CERT_FindCertByIssuerAndSNCX;
 CERT_FindCertByNicknameOrEmailAddrCX;
 CERT_FindCertByNicknameOrEmailAddrForUsageCX;
 ;+    local:
 ;+       *;
 ;+};
+;+NSS_3.34 { 	# NSS 3.34 release
+;+    global:
+SGN_NewContextWithAlgorithmID;
+SEC_SignDataWithAlgorithmID;
+SEC_DerSignDataWithAlgorithmID;
+SEC_CreateSignatureAlgorithmParameters;
+;+    local:
+;+       *;
+;+};
--- a/nss.gyp
+++ b/nss.gyp
@@ -158,16 +158,17 @@
             'cmd/tests/tests.gyp:encodeinttest',
             'cmd/tests/tests.gyp:nonspr10',
             'cmd/tests/tests.gyp:remtest',
             'cmd/tests/tests.gyp:secmodtest',
             'cmd/tstclnt/tstclnt.gyp:tstclnt',
             'cmd/vfychain/vfychain.gyp:vfychain',
             'cmd/vfyserv/vfyserv.gyp:vfyserv',
             'gtests/certhigh_gtest/certhigh_gtest.gyp:certhigh_gtest',
+            'gtests/cryptohi_gtest/cryptohi_gtest.gyp:cryptohi_gtest',
             'gtests/der_gtest/der_gtest.gyp:der_gtest',
             'gtests/certdb_gtest/certdb_gtest.gyp:certdb_gtest',
             'gtests/freebl_gtest/freebl_gtest.gyp:prng_gtest',
             'gtests/freebl_gtest/freebl_gtest.gyp:blake2b_gtest',
             'gtests/pk11_gtest/pk11_gtest.gyp:pk11_gtest',
             'gtests/softoken_gtest/softoken_gtest.gyp:softoken_gtest',
             'gtests/ssl_gtest/ssl_gtest.gyp:ssl_gtest',
             'gtests/util_gtest/util_gtest.gyp:util_gtest',
--- a/tests/cert/cert.sh
+++ b/tests/cert/cert.sh
@@ -505,17 +505,21 @@ cert_all_CA()
 	cert_dsa_CA $CLIENT_CADIR chain-1-clientCA-dsa "-c clientCA-dsa" "u,u,u" ${D_CLIENT_CA} "6"
 	ALL_CU_SUBJECT="CN=NSS Chain2 Client Test CA (DSA), O=BOGUS NSS, L=Santa Clara, ST=California, C=US"
 	cert_dsa_CA $CLIENT_CADIR chain-2-clientCA-dsa "-c chain-1-clientCA-dsa" "u,u,u" ${D_CLIENT_CA} "7"
 
 	rm $CLIENT_CADIR/dsaroot.cert $SERVER_CADIR/dsaroot.cert
 #	dsaroot.cert in $CLIENT_CADIR and in $SERVER_CADIR is one of the last 
 #	in the chain
 
-
+#
+#       Create RSA-PSS version of TestCA
+	ALL_CU_SUBJECT="CN=NSS Test CA (RSA-PSS), O=BOGUS NSS, L=Mountain View, ST=California, C=US"
+	cert_rsa_pss_CA $CADIR TestCA-rsa-pss -x "CTu,CTu,CTu" ${D_CA} "1" SHA256
+	rm $CLIENT_CADIR/rsapssroot.cert $SERVER_CADIR/rsapssroot.cert
 
 
 #
 #       Create EC version of TestCA
 	CA_CURVE="secp521r1"
 	ALL_CU_SUBJECT="CN=NSS Test CA (ECC), O=BOGUS NSS, L=Mountain View, ST=California, C=US"
 	cert_ec_CA $CADIR TestCA-ec -x "CTu,CTu,CTu" ${D_CA} "1" ${CA_CURVE}
 #
@@ -622,41 +626,41 @@ CERTSCRIPT
 
 
 
 
 
 ################################ cert_dsa_CA #############################
 # local shell function to build the Temp. Certificate Authority (CA)
 # used for testing purposes, creating  a CA Certificate and a root cert
-# This is the ECC version of cert_CA.
+# This is the DSA version of cert_CA.
 ##########################################################################
 cert_dsa_CA()
 {
   CUR_CADIR=$1
   NICKNAME=$2
   SIGNER=$3
   TRUSTARG=$4
   DOMAIN=$5
   CERTSERIAL=$6
 
-  echo "$SCRIPTNAME: Creating an DSA CA Certificate $NICKNAME =========================="
+  echo "$SCRIPTNAME: Creating a DSA CA Certificate $NICKNAME =========================="
 
   if [ ! -d "${CUR_CADIR}" ]; then
       mkdir -p "${CUR_CADIR}"
   fi
   cd ${CUR_CADIR}
   pwd
 
   LPROFILE=.
   if [ -n "${MULTIACCESS_DBM}" ]; then
 	LPROFILE="multiaccess:${DOMAIN}"
   fi
 
-  ################# Creating an DSA CA Cert ###############################
+  ################# Creating a DSA CA Cert ###############################
   #
   CU_ACTION="Creating DSA CA Cert $NICKNAME "
   CU_SUBJECT=$ALL_CU_SUBJECT
   certu -S -n $NICKNAME -k dsa -t $TRUSTARG -v 600 $SIGNER \
     -d ${LPROFILE} -1 -2 -5 -f ${R_PWFILE} -z ${R_NOISE_FILE} \
     -m $CERTSERIAL 2>&1 <<CERTSCRIPT
 5
 6
@@ -685,16 +689,89 @@ CERTSCRIPT
       Exit 7 "Fatal - failed to export dsa root cert"
   fi
   cp dsaroot.cert ${NICKNAME}.ca.cert
 }
 
 
 
 
+
+################################ cert_rsa_pss_CA #############################
+# local shell function to build the Temp. Certificate Authority (CA)
+# used for testing purposes, creating  a CA Certificate and a root cert
+# This is the RSA-PSS version of cert_CA.
+##########################################################################
+cert_rsa_pss_CA()
+{
+  CUR_CADIR=$1
+  NICKNAME=$2
+  SIGNER=$3
+  TRUSTARG=$4
+  DOMAIN=$5
+  CERTSERIAL=$6
+  HASHALG=$7
+
+  echo "$SCRIPTNAME: Creating an RSA-PSS CA Certificate $NICKNAME =========================="
+
+  if [ ! -d "${CUR_CADIR}" ]; then
+      mkdir -p "${CUR_CADIR}"
+  fi
+  cd ${CUR_CADIR}
+  pwd
+
+  LPROFILE=.
+  if [ -n "${MULTIACCESS_DBM}" ]; then
+	LPROFILE="multiaccess:${DOMAIN}"
+  fi
+
+  HASHOPT=
+  if [ -n "$HASHALG" ]; then
+        HASHOPT="-Z $HASHALG"
+  fi
+
+  ################# Creating an RSA-PSS CA Cert ###############################
+  #
+  CU_ACTION="Creating RSA-PSS CA Cert $NICKNAME "
+  CU_SUBJECT=$ALL_CU_SUBJECT
+  certu -S -n $NICKNAME -k rsa --pss $HASHOPT -t $TRUSTARG -v 600 $SIGNER \
+    -d ${LPROFILE} -1 -2 -5 -f ${R_PWFILE} -z ${R_NOISE_FILE} \
+    -m $CERTSERIAL 2>&1 <<CERTSCRIPT
+5
+6
+9
+n
+y
+-1
+n
+5
+6
+7
+9
+n
+CERTSCRIPT
+
+  if [ "$RET" -ne 0 ]; then
+      echo "return value is $RET"
+      Exit 6 "Fatal - failed to create RSA-PSS CA cert"
+  fi
+
+  ################# Exporting RSA-PSS Root Cert ###############################
+  #
+  CU_ACTION="Exporting RSA-PSS Root Cert"
+  certu -L -n  $NICKNAME -r -d ${LPROFILE} -o rsapssroot.cert
+  if [ "$RET" -ne 0 ]; then
+      Exit 7 "Fatal - failed to export RSA-PSS root cert"
+  fi
+  cp rsapssroot.cert ${NICKNAME}.ca.cert
+}
+
+
+
+
 ################################ cert_ec_CA ##############################
 # local shell function to build the Temp. Certificate Authority (CA)
 # used for testing purposes, creating  a CA Certificate and a root cert
 # This is the ECC version of cert_CA.
 ##########################################################################
 cert_ec_CA()
 {
   CUR_CADIR=$1
@@ -1968,16 +2045,273 @@ cert_test_implicit_db_init()
 {
   echo "$SCRIPTNAME: test implicit database init"
 
   CU_ACTION="Add cert with trust flags to db with implicit init"
   mkdir ${IMPLICIT_INIT_DIR}
   certu -A -n ca -t 'C,C,C' -d ${P_R_IMPLICIT_INIT_DIR} -i "${SERVER_CADIR}/serverCA.ca.cert"
 }
 
+check_sign_algo()
+{
+  certu -L -n "$CERTNAME" -d "${PROFILEDIR}" -f "${R_PWFILE}" | \
+      sed -n '/^ *Data:/,/^$/{
+/^        Signature Algorithm/,/^ *Salt Length/s/^        //p
+}' > ${TMP}/signalgo.txt
+
+  diff ${TMP}/signalgo.exp ${TMP}/signalgo.txt
+  RET=$?
+  if [ "$RET" -ne 0 ]; then
+      CERTFAILED=$RET
+      html_failed "${CU_ACTION} ($RET) "
+      cert_log "ERROR: ${CU_ACTION} failed $RET"
+  else
+      html_passed "${CU_ACTION}"
+  fi
+}
+
+cert_test_rsapss()
+{
+  TEMPFILES="$TEMPFILES ${TMP}/signalgo.exp ${TMP}/signalgo.txt"
+
+  cert_init_cert "${RSAPSSDIR}" "RSA-PSS Test Cert" 1000 "${D_RSAPSS}"
+
+  CU_ACTION="Initialize Cert DB"
+  certu -N -d "${PROFILEDIR}" -f "${R_PWFILE}" 2>&1
+
+  CU_ACTION="Import RSA CA Cert"
+  certu -A -n "TestCA" -t "C,," -d "${PROFILEDIR}" -f "${R_PWFILE}" \
+        -i "${R_CADIR}/TestCA.ca.cert" 2>&1
+
+  CU_ACTION="Import RSA-PSS CA Cert"
+  certu -A -n "TestCA-rsa-pss" -t "C,," -d "${PROFILEDIR}" -f "${R_PWFILE}" \
+        -i "${R_CADIR}/TestCA-rsa-pss.ca.cert" 2>&1
+
+  CU_ACTION="Verify RSA-PSS CA Cert"
+  certu -V -u L -e -n "TestCA-rsa-pss" -d "${PROFILEDIR}" -f "${R_PWFILE}"
+
+  # Subject certificate: RSA
+  # Issuer certificate: RSA
+  # Signature: RSA-PSS (explicit, with --pss-sign)
+  CERTNAME="TestUser-rsa-pss1"
+
+  CU_ACTION="Generate Cert Request for $CERTNAME"
+  CU_SUBJECT="CN=$CERTNAME, E=${CERTNAME}@bogus.com, O=BOGUS NSS, L=Mountain View, ST=California, C=US"
+  certu -R -d "${PROFILEDIR}" -f "${R_PWFILE}" -z "${R_NOISE_FILE}" -o req  2>&1
+
+  CU_ACTION="Sign ${CERTNAME}'s Request"
+  certu -C -c "TestCA" --pss-sign -m 200 -v 60 -d "${P_R_CADIR}" \
+        -i req -o "${CERTNAME}.cert" -f "${R_PWFILE}" "$1" 2>&1
+
+  CU_ACTION="Import $CERTNAME's Cert"
+  certu -A -n "$CERTNAME" -t ",," -d "${PROFILEDIR}" -f "${R_PWFILE}" \
+        -i "${CERTNAME}.cert" 2>&1
+
+  CU_ACTION="Verify $CERTNAME's Cert"
+  certu -V -u V -e -n "$CERTNAME" -d "${PROFILEDIR}" -f "${R_PWFILE}"
+  cat > ${TMP}/signalgo.exp <<EOF
+Signature Algorithm: PKCS #1 RSA-PSS Signature
+    Parameters:
+        Hash algorithm: SHA-256
+        Mask algorithm: PKCS #1 MGF1 Mask Generation Function
+        Mask hash algorithm: SHA-256
+        Salt Length: 32 (0x20)
+EOF
+  check_sign_algo
+
+  # Subject certificate: RSA
+  # Issuer certificate: RSA
+  # Signature: RSA-PSS (explict, with --pss-sign -Z SHA512)
+  CERTNAME="TestUser-rsa-pss2"
+
+  CU_ACTION="Generate Cert Request for $CERTNAME"
+  CU_SUBJECT="CN=$CERTNAME, E=${CERTNAME}@bogus.com, O=BOGUS NSS, L=Mountain View, ST=California, C=US"
+  certu -R -d "${PROFILEDIR}" -f "${R_PWFILE}" -z "${R_NOISE_FILE}" -o req  2>&1
+
+  CU_ACTION="Sign ${CERTNAME}'s Request"
+  certu -C -c "TestCA" --pss-sign -Z SHA512 -m 201 -v 60 -d "${P_R_CADIR}" \
+        -i req -o "${CERTNAME}.cert" -f "${R_PWFILE}" "$1" 2>&1
+
+  CU_ACTION="Import $CERTNAME's Cert"
+  certu -A -n "$CERTNAME" -t ",," -d "${PROFILEDIR}" -f "${R_PWFILE}" \
+        -i "${CERTNAME}.cert" 2>&1
+
+  CU_ACTION="Verify $CERTNAME's Cert"
+  certu -V -u V -e -n "$CERTNAME" -d "${PROFILEDIR}" -f "${R_PWFILE}"
+  cat > ${TMP}/signalgo.exp <<EOF
+Signature Algorithm: PKCS #1 RSA-PSS Signature
+    Parameters:
+        Hash algorithm: SHA-512
+        Mask algorithm: PKCS #1 MGF1 Mask Generation Function
+        Mask hash algorithm: SHA-512
+        Salt Length: 64 (0x40)
+EOF
+  check_sign_algo
+
+  # Subject certificate: RSA
+  # Issuer certificate: RSA-PSS
+  # Signature: RSA-PSS
+  CERTNAME="TestUser-rsa-pss3"
+
+  CU_ACTION="Generate Cert Request for $CERTNAME"
+  CU_SUBJECT="CN=$CERTNAME, E=${CERTNAME}@bogus.com, O=BOGUS NSS, L=Mountain View, ST=California, C=US"
+  certu -R -d "${PROFILEDIR}" -f "${R_PWFILE}" -z "${R_NOISE_FILE}" -o req  2>&1
+
+  CU_ACTION="Sign ${CERTNAME}'s Request"
+  certu -C -c "TestCA-rsa-pss" -m 202 -v 60 -d "${P_R_CADIR}" \
+        -i req -o "${CERTNAME}.cert" -f "${R_PWFILE}" "$1" 2>&1
+
+  CU_ACTION="Import $CERTNAME's Cert"
+  certu -A -n "$CERTNAME" -t ",," -d "${PROFILEDIR}" -f "${R_PWFILE}" \
+        -i "${CERTNAME}.cert" 2>&1
+
+  CU_ACTION="Verify $CERTNAME's Cert"
+  certu -V -u V -e -n "$CERTNAME" -d "${PROFILEDIR}" -f "${R_PWFILE}"
+  cat > ${TMP}/signalgo.exp <<EOF
+Signature Algorithm: PKCS #1 RSA-PSS Signature
+    Parameters:
+        Hash algorithm: SHA-256
+        Mask algorithm: PKCS #1 MGF1 Mask Generation Function
+        Mask hash algorithm: SHA-256
+        Salt Length: 32 (0x20)
+EOF
+  check_sign_algo
+
+  # Subject certificate: RSA-PSS
+  # Issuer certificate: RSA
+  # Signature: RSA-PSS (explicit, with --pss-sign)
+  CERTNAME="TestUser-rsa-pss4"
+
+  CU_ACTION="Generate Cert Request for $CERTNAME"
+  CU_SUBJECT="CN=$CERTNAME, E=${CERTNAME}@bogus.com, O=BOGUS NSS, L=Mountain View, ST=California, C=US"
+  certu -R -d "${PROFILEDIR}" -f "${R_PWFILE}" -z "${R_NOISE_FILE}" --pss -o req  2>&1
+
+  CU_ACTION="Sign ${CERTNAME}'s Request"
+  certu -C -c "TestCA" --pss-sign -m 203 -v 60 -d "${P_R_CADIR}" \
+        -i req -o "${CERTNAME}.cert" -f "${R_PWFILE}" "$1" 2>&1
+
+  CU_ACTION="Import $CERTNAME's Cert"
+  certu -A -n "$CERTNAME" -t ",," -d "${PROFILEDIR}" -f "${R_PWFILE}" \
+        -i "${CERTNAME}.cert" 2>&1
+
+  CU_ACTION="Verify $CERTNAME's Cert"
+  certu -V -u V -e -n "$CERTNAME" -d "${PROFILEDIR}" -f "${R_PWFILE}"
+  cat > ${TMP}/signalgo.exp <<EOF
+Signature Algorithm: PKCS #1 RSA-PSS Signature
+    Parameters:
+        Hash algorithm: SHA-256
+        Mask algorithm: PKCS #1 MGF1 Mask Generation Function
+        Mask hash algorithm: SHA-256
+        Salt Length: 32 (0x20)
+EOF
+  check_sign_algo
+
+  # Subject certificate: RSA-PSS
+  # Issuer certificate: RSA-PSS
+  # Signature: RSA-PSS (explicit, with --pss-sign)
+  CERTNAME="TestUser-rsa-pss5"
+
+  CU_ACTION="Generate Cert Request for $CERTNAME"
+  CU_SUBJECT="CN=$CERTNAME, E=${CERTNAME}@bogus.com, O=BOGUS NSS, L=Mountain View, ST=California, C=US"
+  certu -R -d "${PROFILEDIR}" -f "${R_PWFILE}" -z "${R_NOISE_FILE}" --pss -o req  2>&1
+
+  CU_ACTION="Sign ${CERTNAME}'s Request"
+  certu -C -c "TestCA-rsa-pss" --pss-sign -m 204 -v 60 -d "${P_R_CADIR}" \
+        -i req -o "${CERTNAME}.cert" -f "${R_PWFILE}" "$1" 2>&1
+
+  CU_ACTION="Import $CERTNAME's Cert"
+  certu -A -n "$CERTNAME" -t ",," -d "${PROFILEDIR}" -f "${R_PWFILE}" \
+        -i "${CERTNAME}.cert" 2>&1
+
+  CU_ACTION="Verify $CERTNAME's Cert"
+  certu -V -u V -e -n "$CERTNAME" -d "${PROFILEDIR}" -f "${R_PWFILE}"
+  cat > ${TMP}/signalgo.exp <<EOF
+Signature Algorithm: PKCS #1 RSA-PSS Signature
+    Parameters:
+        Hash algorithm: SHA-256
+        Mask algorithm: PKCS #1 MGF1 Mask Generation Function
+        Mask hash algorithm: SHA-256
+        Salt Length: 32 (0x20)
+EOF
+  check_sign_algo
+
+  # Subject certificate: RSA-PSS
+  # Issuer certificate: RSA-PSS
+  # Signature: RSA-PSS (implicit, without --pss-sign)
+  CERTNAME="TestUser-rsa-pss6"
+
+  CU_ACTION="Generate Cert Request for $CERTNAME"
+  CU_SUBJECT="CN=$CERTNAME, E=${CERTNAME}@bogus.com, O=BOGUS NSS, L=Mountain View, ST=California, C=US"
+  certu -R -d "${PROFILEDIR}" -f "${R_PWFILE}" -z "${R_NOISE_FILE}" --pss -o req  2>&1
+
+  CU_ACTION="Sign ${CERTNAME}'s Request"
+  certu -C -c "TestCA-rsa-pss" -m 205 -v 60 -d "${P_R_CADIR}" \
+        -i req -o "${CERTNAME}.cert" -f "${R_PWFILE}" "$1" 2>&1
+
+  CU_ACTION="Import $CERTNAME's Cert"
+  certu -A -n "$CERTNAME" -t ",," -d "${PROFILEDIR}" -f "${R_PWFILE}" \
+        -i "${CERTNAME}.cert" 2>&1
+
+  CU_ACTION="Verify $CERTNAME's Cert"
+  certu -V -u V -e -n "$CERTNAME" -d "${PROFILEDIR}" -f "${R_PWFILE}"
+  cat > ${TMP}/signalgo.exp <<EOF
+Signature Algorithm: PKCS #1 RSA-PSS Signature
+    Parameters:
+        Hash algorithm: SHA-256
+        Mask algorithm: PKCS #1 MGF1 Mask Generation Function
+        Mask hash algorithm: SHA-256
+        Salt Length: 32 (0x20)
+EOF
+  check_sign_algo
+
+  # Subject certificate: RSA-PSS
+  # Issuer certificate: RSA-PSS
+  # Signature: RSA-PSS (with conflicting hash algorithm)
+  CERTNAME="TestUser-rsa-pss7"
+
+  CU_ACTION="Generate Cert Request for $CERTNAME"
+  CU_SUBJECT="CN=$CERTNAME, E=${CERTNAME}@bogus.com, O=BOGUS NSS, L=Mountain View, ST=California, C=US"
+  certu -R -d "${PROFILEDIR}" -f "${R_PWFILE}" -z "${R_NOISE_FILE}" --pss -o req  2>&1
+
+  CU_ACTION="Sign ${CERTNAME}'s Request"
+  RETEXPECTED=255
+  certu -C -c "TestCA-rsa-pss" --pss-sign -Z SHA512 -m 206 -v 60 -d "${P_R_CADIR}" \
+        -i req -o "${CERTNAME}.cert" -f "${R_PWFILE}" "$1" 2>&1
+  RETEXPECTED=0
+
+  # Subject certificate: RSA-PSS
+  # Issuer certificate: RSA-PSS
+  # Signature: RSA-PSS (with compatible hash algorithm)
+  CERTNAME="TestUser-rsa-pss8"
+
+  CU_ACTION="Generate Cert Request for $CERTNAME"
+  CU_SUBJECT="CN=$CERTNAME, E=${CERTNAME}@bogus.com, O=BOGUS NSS, L=Mountain View, ST=California, C=US"
+  certu -R -d "${PROFILEDIR}" -f "${R_PWFILE}" -z "${R_NOISE_FILE}" --pss -o req  2>&1
+
+  CU_ACTION="Sign ${CERTNAME}'s Request"
+  certu -C -c "TestCA-rsa-pss" --pss-sign -Z SHA256 -m 207 -v 60 -d "${P_R_CADIR}" \
+        -i req -o "${CERTNAME}.cert" -f "${R_PWFILE}" "$1" 2>&1
+
+  CU_ACTION="Import $CERTNAME's Cert"
+  certu -A -n "$CERTNAME" -t ",," -d "${PROFILEDIR}" -f "${R_PWFILE}" \
+        -i "${CERTNAME}.cert" 2>&1
+
+  CU_ACTION="Verify $CERTNAME's Cert"
+  certu -V -u V -e -n "$CERTNAME" -d "${PROFILEDIR}" -f "${R_PWFILE}"
+  cat > ${TMP}/signalgo.exp <<EOF
+Signature Algorithm: PKCS #1 RSA-PSS Signature
+    Parameters:
+        Hash algorithm: SHA-256
+        Mask algorithm: PKCS #1 MGF1 Mask Generation Function
+        Mask hash algorithm: SHA-256
+        Salt Length: 32 (0x20)
+EOF
+  check_sign_algo
+}
+
 ############################## cert_cleanup ############################
 # local shell function to finish this script (no exit since it might be
 # sourced)
 ########################################################################
 cert_cleanup()
 {
   cert_log "$SCRIPTNAME: finished $SCRIPTNAME"
   html "</TABLE><BR>"
@@ -2004,16 +2338,17 @@ fi
 #   cert_rsa_exponent
 # fi
 cert_eccurves
 cert_extensions
 cert_san_and_generic_extensions
 cert_test_password
 cert_test_distrust
 cert_test_ocspresp
+cert_test_rsapss
 
 if [ -z "$NSS_TEST_DISABLE_CRL" ] ; then
     cert_crl_ssl
 else
     echo "$SCRIPTNAME: Skipping CRL Tests"
 fi
 
 if [ -n "$DO_DIST_ST" -a "$DO_DIST_ST" = "TRUE" ] ; then
--- a/tests/common/init.sh
+++ b/tests/common/init.sh
@@ -58,16 +58,17 @@ if [ -z "${INIT_SOURCED}" -o "${INIT_SOU
         ALICEDIR=${HOSTDIR}/alicedir
         BOBDIR=${HOSTDIR}/bobdir
         DAVEDIR=${HOSTDIR}/dave
         EVEDIR=${HOSTDIR}/eve
         FIPSDIR=${HOSTDIR}/fips
         DBPASSDIR=${HOSTDIR}/dbpass
         ECCURVES_DIR=${HOSTDIR}/eccurves
         DISTRUSTDIR=${HOSTDIR}/distrust
+        RSAPSSDIR=${HOSTDIR}/rsapss
 
         SERVER_CADIR=${HOSTDIR}/serverCA
         CLIENT_CADIR=${HOSTDIR}/clientCA
         EXT_SERVERDIR=${HOSTDIR}/ext_server
         EXT_CLIENTDIR=${HOSTDIR}/ext_client
         IMPLICIT_INIT_DIR=${HOSTDIR}/implicit_init
 
         IOPR_CADIR=${HOSTDIR}/CA_iopr
@@ -535,16 +536,17 @@ if [ -z "${INIT_SOURCED}" -o "${INIT_SOU
     D_FIPS="FIPS.$version"
     D_DBPASS="DBPASS.$version"
     D_ECCURVES="ECCURVES.$version"
     D_EXT_SERVER="ExtendedServer.$version"
     D_EXT_CLIENT="ExtendedClient.$version"
     D_IMPLICIT_INIT="ImplicitInit.$version"
     D_CERT_EXTENSTIONS="CertExtensions.$version"
     D_DISTRUST="Distrust.$version"
+    D_RSAPSS="RSAPSS.$version"
 
     # we need relative pathnames of these files abd directories, since our
     # tools can't handle the unix style absolut pathnames on cygnus
 
     R_CADIR=../CA
     R_SERVERDIR=../server
     R_CLIENTDIR=../client
     R_IOPR_CADIR=../CA_iopr
--- a/tests/ssl_gtests/ssl_gtests.sh
+++ b/tests/ssl_gtests/ssl_gtests.sh
@@ -36,26 +36,30 @@ certscript() {
 }
 
 # $1: name
 # $2: type
 # $3+: usages: sign or kex
 make_cert() {
   name=$1
   type=$2
+  unset type_args trust sign
   case $type in
     dsa) type_args='-g 1024' ;;
     rsa) type_args='-g 1024' ;;
     rsa2048) type_args='-g 2048';type=rsa ;;
     rsapss) type_args='-g 1024 --pss';type=rsa ;;
     p256) type_args='-q nistp256';type=ec ;;
     p384) type_args='-q secp384r1';type=ec ;;
     p521) type_args='-q secp521r1';type=ec ;;
     rsa_ca) type_args='-g 1024';trust='CT,CT,CT';ca=y;type=rsa ;;
     rsa_chain) type_args='-g 1024';sign='-c rsa_ca';type=rsa;;
+    rsapss_ca) type_args='-g 1024 --pss';trust='CT,CT,CT';ca=y;type=rsa ;;
+    rsapss_chain) type_args='-g 1024';sign='-c rsa_pss_ca';type=rsa;;
+    rsa_ca_rsapss_chain) type_args='-g 1024 --pss-sign';sign='-c rsa_ca';type=rsa;;
     ecdh_rsa) type_args='-q nistp256';sign='-c rsa_ca';type=ec ;;
   esac
   shift 2
   counter=$(($counter + 1))
   certscript $@ | ${BINDIR}/certutil -S \
     -z ${R_NOISE_FILE} -d "${PROFILEDIR}" \
     -n $name -s "CN=$name" -t ${trust:-,,} ${sign:--x} -m $counter \
     -w -2 -v 120 -k $type $type_args -Z SHA256 -1 -2
@@ -82,16 +86,19 @@ ssl_gtest_certs() {
   make_cert rsa_pss rsapss sign
   make_cert rsa_decrypt rsa kex
   make_cert ecdsa256 p256 sign
   make_cert ecdsa384 p384 sign
   make_cert ecdsa521 p521 sign
   make_cert ecdh_ecdsa p256 kex
   make_cert rsa_ca rsa_ca ca
   make_cert rsa_chain rsa_chain sign
+  make_cert rsa_pss_ca rsapss_ca ca
+  make_cert rsa_pss_chain rsapss_chain sign
+  make_cert rsa_ca_rsa_pss_chain rsa_ca_rsapss_chain sign
   make_cert ecdh_rsa ecdh_rsa kex
   make_cert dsa dsa sign
 }
 
 ############################## ssl_gtest_init ##########################
 # local shell function to initialize this script
 ########################################################################
 ssl_gtest_init()