Bug 1191936 - Implement RSA-PSS signing and verification r=rbarnes,smaug
authorTim Taubert <ttaubert@mozilla.com>
Tue, 13 Oct 2015 20:22:43 +0200
changeset 318134 1fba432e1cab
parent 318133 1faac7557b25
child 318135 29a076e8bc0d
push id5913
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 16:57:49 +0000
treeherdermozilla-beta@dcaf0a6fa115 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrbarnes, smaug
bugs1191936
milestone47.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1191936 - Implement RSA-PSS signing and verification r=rbarnes,smaug
config/external/nss/nss.symbols
dom/crypto/WebCryptoTask.cpp
dom/crypto/test/test_WebCrypto_RSA_PSS.html
dom/webidl/SubtleCrypto.webidl
security/manager/ssl/ScopedNSSTypes.h
--- a/config/external/nss/nss.symbols
+++ b/config/external/nss/nss.symbols
@@ -400,16 +400,17 @@ PK11_KeyGenWithTemplate
 PK11_ListCerts
 PK11_ListCertsInSlot
 PK11_ListPrivateKeysInSlot
 PK11_ListPrivKeysInSlot
 PK11_LoadPrivKey
 PK11_Logout
 PK11_LogoutAll
 PK11_MakeIDFromPubKey
+PK11_MapSignKeyType
 PK11_MechanismToAlgtag
 PK11_MergeTokens
 PK11_NeedLogin
 PK11_NeedUserInit
 PK11_ParamFromIV
 PK11_PBEKeyGen
 PK11_PrivDecrypt
 PK11_PrivDecryptPKCS1
@@ -424,21 +425,23 @@ PK11_ReadRawAttribute
 PK11_ReferenceSlot
 PK11_ResetToken
 PK11SDR_Decrypt
 PK11SDR_Encrypt
 PK11_SetPasswordFunc
 PK11_SetSlotPWValues
 PK11_Sign
 PK11_SignatureLen
+PK11_SignWithMechanism
 PK11_UnwrapPrivKey
 PK11_UnwrapSymKey
 PK11_UpdateSlotAttribute
 PK11_UserDisableSlot
 PK11_UserEnableSlot
+PK11_VerifyWithMechanism
 PK11_WrapPrivKey
 PK11_WrapSymKey
 PORT_Alloc
 PORT_Alloc_Util
 PORT_ArenaAlloc
 PORT_ArenaAlloc_Util
 PORT_ArenaGrow_Util
 PORT_ArenaMark_Util
@@ -626,19 +629,21 @@ SEC_QuickDERDecodeItem
 SEC_QuickDERDecodeItem_Util
 SEC_RegisterDefaultHttpClient
 SEC_SignData
 SEC_SignedCertificateTemplate @DATA@
 SEC_StringToOID
 SEC_UTF8StringTemplate @DATA@
 SEC_UTF8StringTemplate_Util @DATA@
 SGN_Begin
+SGN_CreateDigestInfo
 SGN_CreateDigestInfo_Util
 SGN_DecodeDigestInfo
 SGN_DestroyContext
+SGN_DestroyDigestInfo
 SGN_DestroyDigestInfo_Util
 SGN_End
 SGN_NewContext
 SGN_Update
 SSL_AuthCertificateComplete
 SSL_AuthCertificateHook
 SSL_CipherPrefGet
 SSL_CipherPrefSet
--- a/dom/crypto/WebCryptoTask.cpp
+++ b/dom/crypto/WebCryptoTask.cpp
@@ -15,16 +15,30 @@
 #include "mozilla/dom/CryptoBuffer.h"
 #include "mozilla/dom/CryptoKey.h"
 #include "mozilla/dom/KeyAlgorithmProxy.h"
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/dom/WebCryptoCommon.h"
 #include "mozilla/dom/WebCryptoTask.h"
 #include "mozilla/dom/WebCryptoThreadPool.h"
 
+// Template taken from security/nss/lib/util/templates.c
+// This (or SGN_EncodeDigestInfo) would ideally be exported
+// by NSS and until that happens we have to keep our own copy.
+const SEC_ASN1Template SGN_DigestInfoTemplate[] = {
+    { SEC_ASN1_SEQUENCE,
+      0, NULL, sizeof(SGNDigestInfo) },
+    { SEC_ASN1_INLINE,
+      offsetof(SGNDigestInfo,digestAlgorithm),
+      SEC_ASN1_GET(SECOID_AlgorithmIDTemplate) },
+    { SEC_ASN1_OCTET_STRING,
+      offsetof(SGNDigestInfo,digest) },
+    { 0, }
+};
+
 namespace mozilla {
 namespace dom {
 
 // Pre-defined identifiers for telemetry histograms
 
 enum TelemetryMethod {
   TM_ENCRYPT      = 0,
   TM_DECRYPT      = 1,
@@ -260,16 +274,51 @@ MapOIDTagToNamedCurve(SECOidTag aOIDTag,
       break;
     default:
       return false;
   }
 
   return true;
 }
 
+inline SECOidTag
+MapHashAlgorithmNameToOID(const nsString& aName)
+{
+  SECOidTag hashOID(SEC_OID_UNKNOWN);
+
+  if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
+    hashOID = SEC_OID_SHA1;
+  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
+    hashOID = SEC_OID_SHA256;
+  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
+    hashOID = SEC_OID_SHA384;
+  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
+    hashOID = SEC_OID_SHA512;
+  }
+
+  return hashOID;
+}
+
+inline CK_MECHANISM_TYPE
+MapHashAlgorithmNameToMgfMechanism(const nsString& aName) {
+  CK_MECHANISM_TYPE mech(UNKNOWN_CK_MECHANISM);
+
+  if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
+    mech = CKG_MGF1_SHA1;
+  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
+    mech = CKG_MGF1_SHA256;
+  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
+    mech = CKG_MGF1_SHA384;
+  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
+    mech = CKG_MGF1_SHA512;
+  }
+
+  return mech;
+}
+
 // Helper function to clone data from an ArrayBuffer or ArrayBufferView object
 inline bool
 CloneData(JSContext* aCx, CryptoBuffer& aDst, JS::Handle<JSObject*> aSrc)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // Try ArrayBuffer
   RootedTypedArray<ArrayBuffer> ab(aCx);
@@ -834,31 +883,25 @@ public:
       }
 
       if (params.mLabel.WasPassed()) {
         ATTEMPT_BUFFER_INIT(mLabel, params.mLabel.Value());
       }
     }
     // Otherwise mLabel remains the empty octet string, as intended
 
-    // Look up the MGF based on the KeyAlgorithm.
-    mHashMechanism = KeyAlgorithmProxy::GetMechanism(aKey.Algorithm().mRsa.mHash);
-
-    switch (mHashMechanism) {
-      case CKM_SHA_1:
-        mMgfMechanism = CKG_MGF1_SHA1; break;
-      case CKM_SHA256:
-        mMgfMechanism = CKG_MGF1_SHA256; break;
-      case CKM_SHA384:
-        mMgfMechanism = CKG_MGF1_SHA384; break;
-      case CKM_SHA512:
-        mMgfMechanism = CKG_MGF1_SHA512; break;
-      default:
-        mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
-        return;
+    KeyAlgorithm& hashAlg = aKey.Algorithm().mRsa.mHash;
+    mHashMechanism = KeyAlgorithmProxy::GetMechanism(hashAlg);
+    mMgfMechanism = MapHashAlgorithmNameToMgfMechanism(hashAlg.mName);
+
+    // Check we found appropriate mechanisms.
+    if (mHashMechanism == UNKNOWN_CK_MECHANISM ||
+        mMgfMechanism == UNKNOWN_CK_MECHANISM) {
+      mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+      return;
     }
   }
 
 private:
   CK_MECHANISM_TYPE mHashMechanism;
   CK_MECHANISM_TYPE mMgfMechanism;
   ScopedSECKEYPrivateKey mPrivKey;
   ScopedSECKEYPublicKey mPubKey;
@@ -1033,165 +1076,192 @@ class AsymmetricSignVerifyTask : public 
 public:
   AsymmetricSignVerifyTask(JSContext* aCx,
                            const ObjectOrString& aAlgorithm,
                            CryptoKey& aKey,
                            const CryptoOperationData& aSignature,
                            const CryptoOperationData& aData,
                            bool aSign)
     : mOidTag(SEC_OID_UNKNOWN)
+    , mHashMechanism(UNKNOWN_CK_MECHANISM)
+    , mMgfMechanism(UNKNOWN_CK_MECHANISM)
     , mPrivKey(aKey.GetPrivateKey())
     , mPubKey(aKey.GetPublicKey())
+    , mSaltLength(0)
     , mSign(aSign)
     , mVerified(false)
-    , mEcdsa(false)
+    , mAlgorithm(Algorithm::UNKNOWN)
   {
     ATTEMPT_BUFFER_INIT(mData, aData);
     if (!aSign) {
       ATTEMPT_BUFFER_INIT(mSignature, aSignature);
     }
 
     nsString algName;
+    nsString hashAlgName;
     mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
     if (NS_FAILED(mEarlyRv)) {
       return;
     }
 
-    // Look up the SECOidTag
     if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
-      mEcdsa = false;
+      mAlgorithm = Algorithm::RSA_PKCS1;
       Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSASSA_PKCS1);
       CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSASSA_PKCS1);
-
-      // For RSA, the hash name comes from the key algorithm
-      nsString hashName = aKey.Algorithm().mRsa.mHash.mName;
-      switch (MapAlgorithmNameToMechanism(hashName)) {
-        case CKM_SHA_1:
-          mOidTag = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION; break;
-        case CKM_SHA256:
-          mOidTag = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION; break;
-        case CKM_SHA384:
-          mOidTag = SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION; break;
-        case CKM_SHA512:
-          mOidTag = SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION; break;
-        default:
-          mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
-          return;
+      hashAlgName = aKey.Algorithm().mRsa.mHash.mName;
+    } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
+      mAlgorithm = Algorithm::RSA_PSS;
+      Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSA_PSS);
+      CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSA_PSS);
+
+      KeyAlgorithm& hashAlg = aKey.Algorithm().mRsa.mHash;
+      hashAlgName = hashAlg.mName;
+      mHashMechanism = KeyAlgorithmProxy::GetMechanism(hashAlg);
+      mMgfMechanism = MapHashAlgorithmNameToMgfMechanism(hashAlgName);
+
+      // Check we found appropriate mechanisms.
+      if (mHashMechanism == UNKNOWN_CK_MECHANISM ||
+          mMgfMechanism == UNKNOWN_CK_MECHANISM) {
+        mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+        return;
       }
+
+      RootedDictionary<RsaPssParams> params(aCx);
+      mEarlyRv = Coerce(aCx, params, aAlgorithm);
+      if (NS_FAILED(mEarlyRv)) {
+        mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
+        return;
+      }
+
+      mSaltLength = params.mSaltLength;
     } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
-      mEcdsa = true;
+      mAlgorithm = Algorithm::ECDSA;
       Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_ECDSA);
       CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_ECDSA);
 
       // For ECDSA, the hash name comes from the algorithm parameter
       RootedDictionary<EcdsaParams> params(aCx);
       mEarlyRv = Coerce(aCx, params, aAlgorithm);
       if (NS_FAILED(mEarlyRv)) {
         mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
         return;
       }
 
-      nsString hashName;
-      mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
+      mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashAlgName);
       if (NS_FAILED(mEarlyRv)) {
         mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
         return;
       }
-
-      CK_MECHANISM_TYPE hashMechanism = MapAlgorithmNameToMechanism(hashName);
-      if (hashMechanism == UNKNOWN_CK_MECHANISM) {
-        mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
-        return;
-      }
-
-      switch (hashMechanism) {
-        case CKM_SHA_1:
-          mOidTag = SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE; break;
-        case CKM_SHA256:
-          mOidTag = SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE; break;
-        case CKM_SHA384:
-          mOidTag = SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE; break;
-        case CKM_SHA512:
-          mOidTag = SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE; break;
-        default:
-          mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
-          return;
-      }
     } else {
       // This shouldn't happen; CreateSignVerifyTask shouldn't create
       // one of these unless it's for the above algorithms.
       MOZ_ASSERT(false);
     }
 
+    // Must have a valid algorithm by now.
+    MOZ_ASSERT(mAlgorithm != Algorithm::UNKNOWN);
+
+    // Determine hash algorithm to use.
+    mOidTag = MapHashAlgorithmNameToOID(hashAlgName);
+    if (mOidTag == SEC_OID_UNKNOWN) {
+      mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+      return;
+    }
+
     // Check that we have the appropriate key
     if ((mSign && !mPrivKey) || (!mSign && !mPubKey)) {
       mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
       return;
     }
   }
 
 private:
   SECOidTag mOidTag;
+  CK_MECHANISM_TYPE mHashMechanism;
+  CK_MECHANISM_TYPE mMgfMechanism;
   ScopedSECKEYPrivateKey mPrivKey;
   ScopedSECKEYPublicKey mPubKey;
   CryptoBuffer mSignature;
   CryptoBuffer mData;
+  uint32_t mSaltLength;
   bool mSign;
   bool mVerified;
-  bool mEcdsa;
+
+  // The signature algorithm to use.
+  enum class Algorithm: uint8_t {ECDSA, RSA_PKCS1, RSA_PSS, UNKNOWN};
+  Algorithm mAlgorithm;
 
   virtual nsresult DoCrypto() override
   {
-    nsresult rv;
-    if (mSign) {
-      ScopedSECItem signature(::SECITEM_AllocItem(nullptr, nullptr, 0));
-      if (!signature.get()) {
+    SECStatus rv;
+    ScopedSECItem hash(::SECITEM_AllocItem(nullptr, nullptr,
+                                           HASH_ResultLenByOidTag(mOidTag)));
+    if (!hash) {
+      return NS_ERROR_DOM_OPERATION_ERR;
+    }
+
+    // Compute digest over given data.
+    rv = PK11_HashBuf(mOidTag, hash->data, mData.Elements(), mData.Length());
+    NS_ENSURE_SUCCESS(MapSECStatus(rv), NS_ERROR_DOM_OPERATION_ERR);
+
+    // Wrap hash in a digest info template (RSA-PKCS1 only).
+    if (mAlgorithm == Algorithm::RSA_PKCS1) {
+      ScopedSGNDigestInfo di(SGN_CreateDigestInfo(mOidTag, hash->data, hash->len));
+      if (!di) {
+        return NS_ERROR_DOM_OPERATION_ERR;
+      }
+
+      // Reuse |hash|.
+      SECITEM_FreeItem(hash, false);
+      if (!SEC_ASN1EncodeItem(nullptr, hash, di, SGN_DigestInfoTemplate)) {
         return NS_ERROR_DOM_OPERATION_ERR;
       }
-
-      rv = MapSECStatus(SEC_SignData(signature, mData.Elements(),
-                                     mData.Length(), mPrivKey, mOidTag));
-
-      if (mEcdsa) {
-        // DER-decode the signature
-        int signatureLength = PK11_SignatureLen(mPrivKey);
-        ScopedSECItem rawSignature(DSAU_DecodeDerSigToLen(signature.get(),
-                                                          signatureLength));
-        if (!rawSignature.get()) {
-          return NS_ERROR_DOM_OPERATION_ERR;
-        }
-
-        ATTEMPT_BUFFER_ASSIGN(mSignature, rawSignature);
-      } else {
-        ATTEMPT_BUFFER_ASSIGN(mSignature, signature);
-      }
-
+    }
+
+    SECItem* params = nullptr;
+    CK_MECHANISM_TYPE mech = PK11_MapSignKeyType((mSign ? mPrivKey->keyType :
+                                                          mPubKey->keyType));
+
+    CK_RSA_PKCS_PSS_PARAMS rsaPssParams;
+    SECItem rsaPssParamsItem = { siBuffer, };
+
+    // Set up parameters for RSA-PSS.
+    if (mAlgorithm == Algorithm::RSA_PSS) {
+      rsaPssParams.hashAlg = mHashMechanism;
+      rsaPssParams.mgf = mMgfMechanism;
+      rsaPssParams.sLen = mSaltLength;
+
+      rsaPssParamsItem.data = (unsigned char*)&rsaPssParams;
+      rsaPssParamsItem.len = sizeof(rsaPssParams);
+      params = &rsaPssParamsItem;
+
+      mech = CKM_RSA_PKCS_PSS;
+    }
+
+    // Allocate SECItem to hold the signature.
+    uint32_t len = mSign ? PK11_SignatureLen(mPrivKey) : 0;
+    ScopedSECItem sig(::SECITEM_AllocItem(nullptr, nullptr, len));
+    if (!sig) {
+      return NS_ERROR_DOM_OPERATION_ERR;
+    }
+
+    if (mSign) {
+      // Sign the hash.
+      rv = PK11_SignWithMechanism(mPrivKey, mech, params, sig, hash);
+      NS_ENSURE_SUCCESS(MapSECStatus(rv), NS_ERROR_DOM_OPERATION_ERR);
+      ATTEMPT_BUFFER_ASSIGN(mSignature, sig);
     } else {
-      ScopedSECItem signature(::SECITEM_AllocItem(nullptr, nullptr, 0));
-      if (!signature.get()) {
-        return NS_ERROR_DOM_UNKNOWN_ERR;
+      // Copy the given signature to the SECItem.
+      if (!mSignature.ToSECItem(nullptr, sig)) {
+        return NS_ERROR_DOM_OPERATION_ERR;
       }
 
-      if (mEcdsa) {
-        // DER-encode the signature
-        ScopedSECItem rawSignature(::SECITEM_AllocItem(nullptr, nullptr, 0));
-        if (!rawSignature || !mSignature.ToSECItem(nullptr, rawSignature)) {
-          return NS_ERROR_DOM_UNKNOWN_ERR;
-        }
-
-        rv = MapSECStatus(DSAU_EncodeDerSigWithLen(signature, rawSignature,
-                                                   rawSignature->len));
-        NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
-      } else if (!mSignature.ToSECItem(nullptr, signature)) {
-        return NS_ERROR_DOM_UNKNOWN_ERR;
-      }
-
-      rv = MapSECStatus(VFY_VerifyData(mData.Elements(), mData.Length(),
-                                       mPubKey, signature, mOidTag, nullptr));
-      mVerified = NS_SUCCEEDED(rv);
+      // Verify the signature.
+      rv = PK11_VerifyWithMechanism(mPubKey, mech, params, sig, hash, nullptr);
+      mVerified = NS_SUCCEEDED(MapSECStatus(rv));
     }
 
     return NS_OK;
   }
 
   virtual void Resolve() override
   {
     if (mSign) {
@@ -1216,32 +1286,29 @@ public:
     mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
     if (NS_FAILED(mEarlyRv)) {
       mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
       return;
     }
 
     TelemetryAlgorithm telemetryAlg;
     if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA1))   {
-      mOidTag = SEC_OID_SHA1;
       telemetryAlg = TA_SHA_1;
     } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
-      mOidTag = SEC_OID_SHA256;
       telemetryAlg = TA_SHA_224;
     } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
-      mOidTag = SEC_OID_SHA384;
       telemetryAlg = TA_SHA_256;
     } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
-      mOidTag = SEC_OID_SHA512;
       telemetryAlg = TA_SHA_384;
     } else {
       mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
       return;
     }
     Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, telemetryAlg);
+    mOidTag = MapHashAlgorithmNameToOID(algName);
   }
 
 private:
   SECOidTag mOidTag;
   CryptoBuffer mData;
 
   virtual nsresult DoCrypto() override
   {
@@ -3190,16 +3257,17 @@ WebCryptoTask::CreateSignVerifyTask(JSCo
   nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
   if (NS_FAILED(rv)) {
     return new FailureTask(rv);
   }
 
   if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
     return new HmacTask(aCx, aAlgorithm, aKey, aSignature, aData, aSign);
   } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
+             algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS) ||
              algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
     return new AsymmetricSignVerifyTask(aCx, aAlgorithm, aKey, aSignature,
                                         aData, aSign);
   }
 
   return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
 }
 
--- a/dom/crypto/test/test_WebCrypto_RSA_PSS.html
+++ b/dom/crypto/test/test_WebCrypto_RSA_PSS.html
@@ -36,16 +36,48 @@ TestArray.addTest(
       modulusLength: 1024,
       publicExponent: new Uint8Array([0x01, 0x00, 0x01])
     };
 
     crypto.subtle.generateKey(alg, false, ["sign", "verify"])
       .then(complete(that), error(that));
   }
 );
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "RSA-PSS key generation and sign/verify round-trip (SHA-256, 2048-bit)",
+  function () {
+    var that = this;
+    var alg = {
+      name: "RSA-PSS",
+      hash: "SHA-256",
+      modulusLength: 2048,
+      publicExponent: new Uint8Array([0x01, 0x00, 0x01])
+    };
+
+    var privKey, pubKey;
+    var data = crypto.getRandomValues(new Uint8Array(128));
+    function setKey(x) { pubKey = x.publicKey; privKey = x.privateKey; }
+    function doSign() {
+      var alg = {name: "RSA-PSS", saltLength: 32};
+      return crypto.subtle.sign(alg, privKey, data);
+    }
+    function doVerify(x) {
+      var alg = {name: "RSA-PSS", saltLength: 32};
+      return crypto.subtle.verify(alg, pubKey, x, data);
+    }
+
+    crypto.subtle.generateKey(alg, false, ["sign", "verify"])
+      .then(setKey, error(that))
+      .then(doSign, error(that))
+      .then(doVerify, error(that))
+      .then(complete(that, x => x), error(that))
+  }
+);
 /*]]>*/</script>
 </head>
 
 <body>
 
 <div id="content">
 	<div id="head">
 		<b>Web</b>Crypto<br>
--- a/dom/webidl/SubtleCrypto.webidl
+++ b/dom/webidl/SubtleCrypto.webidl
@@ -61,16 +61,20 @@ dictionary RsaHashedKeyGenParams : Algor
   required BigInteger publicExponent;
   required AlgorithmIdentifier hash;
 };
 
 dictionary RsaOaepParams : Algorithm {
   BufferSource label;
 };
 
+dictionary RsaPssParams : Algorithm {
+  [EnforceRange] required unsigned long saltLength;
+};
+
 dictionary DhKeyGenParams : Algorithm {
   required BigInteger prime;
   required BigInteger generator;
 };
 
 dictionary EcKeyGenParams : Algorithm {
   required NamedCurve namedCurve;
 };
--- a/security/manager/ssl/ScopedNSSTypes.h
+++ b/security/manager/ssl/ScopedNSSTypes.h
@@ -126,16 +126,19 @@ VFY_DestroyContext_true(VFYContext * ctx
 } // namespace mozilla::psm
 
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPK11Context,
                                           PK11Context,
                                           mozilla::psm::PK11_DestroyContext_true)
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSGNContext,
                                           SGNContext,
                                           mozilla::psm::SGN_DestroyContext_true)
+MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSGNDigestInfo,
+                                          SGNDigestInfo,
+                                          SGN_DestroyDigestInfo)
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedVFYContext,
                                           VFYContext,
                                           mozilla::psm::VFY_DestroyContext_true)
 
 /** A more convenient way of dealing with digests calculated into
  *  stack-allocated buffers. NSS must be initialized on the main thread before
  *  use, and the caller must ensure NSS isn't shut down, typically by
  *  subclassing nsNSSShutDownObject, while Digest is in use.