Bug 998803 - Add support for RSA encryption and signing to WebCrypto API. r=bz,dkeeler
authorRichard Barnes <rbarnes@mozilla.com>
Fri, 23 May 2014 15:29:00 +0200
changeset 185135 ce017a1024b28e3f323e38a618a2a88221afdb55
parent 185134 4c7bf07794464e81c25947d8a520c01391a4fc2a
child 185136 5b98b158aee42a96533bdce11300ea995af93987
push id26844
push userryanvm@gmail.com
push dateTue, 27 May 2014 20:23:53 +0000
treeherdermozilla-central@448f2153d6d3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, dkeeler
bugs998803
milestone32.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 998803 - Add support for RSA encryption and signing to WebCrypto API. r=bz,dkeeler
dom/crypto/WebCryptoTask.cpp
dom/crypto/test/test-vectors.js
dom/crypto/test/tests.js
security/manager/ssl/src/ScopedNSSTypes.h
--- a/dom/crypto/WebCryptoTask.cpp
+++ b/dom/crypto/WebCryptoTask.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "pk11pub.h"
 #include "cryptohi.h"
+#include "secerr.h"
 #include "ScopedNSSTypes.h"
 
 #include "mozilla/dom/WebCryptoTask.h"
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/dom/Key.h"
 #include "mozilla/dom/KeyAlgorithm.h"
 #include "mozilla/dom/AesKeyAlgorithm.h"
 #include "mozilla/dom/HmacKeyAlgorithm.h"
@@ -284,16 +285,86 @@ private:
     }
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
 
     mResult.SetLength(outLen);
     return rv;
   }
 };
 
+class RsaesPkcs1Task : public ReturnArrayBufferViewTask
+{
+public:
+  RsaesPkcs1Task(JSContext* aCx, const ObjectOrString& aAlgorithm,
+                 mozilla::dom::Key& aKey, const CryptoOperationData& aData,
+                 bool aEncrypt)
+    : mPrivKey(aKey.GetPrivateKey())
+    , mPubKey(aKey.GetPublicKey())
+    , mEncrypt(aEncrypt)
+  {
+    ATTEMPT_BUFFER_INIT(mData, aData);
+
+    if (mEncrypt) {
+      if (!mPubKey) {
+        mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
+        return;
+      }
+      mStrength = SECKEY_PublicKeyStrength(mPubKey);
+
+      // Verify that the data input is not too big
+      // (as required by PKCS#1 / RFC 3447)
+      if (mData.Length() > mStrength - 11) {
+        mEarlyRv = NS_ERROR_DOM_DATA_ERR;
+        return;
+      }
+    } else {
+      if (!mPrivKey) {
+        mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
+        return;
+      }
+      mStrength = PK11_GetPrivateModulusLen(mPrivKey);
+    }
+  }
+
+private:
+  ScopedSECKEYPrivateKey mPrivKey;
+  ScopedSECKEYPublicKey mPubKey;
+  CryptoBuffer mData;
+  uint32_t mStrength;
+  bool mEncrypt;
+
+  virtual nsresult DoCrypto() MOZ_OVERRIDE
+  {
+    nsresult rv;
+
+    // Ciphertext is an integer mod the modulus, so it will be
+    // no longer than mStrength octets
+    if (!mResult.SetLength(mStrength)) {
+      return NS_ERROR_DOM_UNKNOWN_ERR;
+    }
+
+    if (mEncrypt) {
+      rv = MapSECStatus(PK11_PubEncryptPKCS1(
+              mPubKey.get(), mResult.Elements(),
+              mData.Elements(), mData.Length(),
+              nullptr));
+    } else {
+      uint32_t outLen;
+      rv = MapSECStatus(PK11_PrivDecryptPKCS1(
+              mPrivKey.get(), mResult.Elements(),
+              &outLen, mResult.Length(),
+              mData.Elements(), mData.Length()));
+      mResult.SetLength(outLen);
+    }
+
+    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
+    return NS_OK;
+  }
+};
+
 class HmacTask : public WebCryptoTask
 {
 public:
   HmacTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
            mozilla::dom::Key& aKey,
            const CryptoOperationData& aSignature,
            const CryptoOperationData& aData,
            bool aSign)
@@ -374,16 +445,132 @@ private:
                                  mResult.Elements(),
                                  mSignature.Length());
       equal = equal && (cmp == 0);
       mResultPromise->MaybeResolve(equal);
     }
   }
 };
 
+class RsassaPkcs1Task : public WebCryptoTask
+{
+public:
+  RsassaPkcs1Task(JSContext* aCx, const ObjectOrString& aAlgorithm,
+                  mozilla::dom::Key& aKey,
+                  const CryptoOperationData& aSignature,
+                  const CryptoOperationData& aData,
+                  bool aSign)
+    : mOidTag(SEC_OID_UNKNOWN)
+    , mPrivKey(aKey.GetPrivateKey())
+    , mPubKey(aKey.GetPublicKey())
+    , mSign(aSign)
+    , mVerified(false)
+  {
+    ATTEMPT_BUFFER_INIT(mData, aData);
+    if (!aSign) {
+      ATTEMPT_BUFFER_INIT(mSignature, aSignature);
+    }
+
+    // Look up the SECOidTag based on the KeyAlgorithm
+    // static_cast is safe because we only get here if the algorithm name
+    // is RSASSA-PKCS1-v1_5, and that only happens if we've constructed
+    // an RsaHashedKeyAlgorithm
+    nsRefPtr<RsaHashedKeyAlgorithm> rsaAlg = static_cast<RsaHashedKeyAlgorithm*>(aKey.Algorithm());
+    nsRefPtr<KeyAlgorithm> hashAlg = rsaAlg->Hash();
+
+    switch (hashAlg->Mechanism()) {
+      case CKM_SHA_1:
+        mOidTag = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION; break;
+      case CKM_SHA224:
+        mOidTag = SEC_OID_PKCS1_SHA224_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;
+      }
+    }
+
+    // Check that we have the appropriate key
+    if ((mSign && !mPrivKey) || (!mSign && !mPubKey)) {
+      mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
+      return;
+    }
+  }
+
+private:
+  SECOidTag mOidTag;
+  ScopedSECKEYPrivateKey mPrivKey;
+  ScopedSECKEYPublicKey mPubKey;
+  CryptoBuffer mSignature;
+  CryptoBuffer mData;
+  bool mSign;
+  bool mVerified;
+
+  virtual nsresult DoCrypto() MOZ_OVERRIDE
+  {
+    nsresult rv;
+    if (mSign) {
+      ScopedSECItem signature((SECItem*) PORT_Alloc(sizeof(SECItem)));
+      ScopedSGNContext ctx(SGN_NewContext(mOidTag, mPrivKey));
+      if (!ctx) {
+        return NS_ERROR_DOM_OPERATION_ERR;
+      }
+
+      rv = MapSECStatus(SGN_Begin(ctx));
+      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
+
+      rv = MapSECStatus(SGN_Update(ctx, mData.Elements(), mData.Length()));
+      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
+
+      rv = MapSECStatus(SGN_End(ctx, signature));
+      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
+
+      mSignature.Assign(signature);
+    } else {
+      ScopedSECItem signature(mSignature.ToSECItem());
+      ScopedVFYContext ctx(VFY_CreateContext(mPubKey, signature,
+                                             mOidTag, nullptr));
+      if (!ctx) {
+        int err = PORT_GetError();
+        if (err == SEC_ERROR_BAD_SIGNATURE) {
+          mVerified = false;
+          return NS_OK;
+        }
+        return NS_ERROR_DOM_OPERATION_ERR;
+      }
+
+      rv = MapSECStatus(VFY_Begin(ctx));
+      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
+
+      rv = MapSECStatus(VFY_Update(ctx, mData.Elements(), mData.Length()));
+      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
+
+      rv = MapSECStatus(VFY_End(ctx));
+      mVerified = NS_SUCCEEDED(rv);
+    }
+
+    return NS_OK;
+  }
+
+  virtual void Resolve() MOZ_OVERRIDE
+  {
+    if (mSign) {
+      TypedArrayCreator<Uint8Array> ret(mSignature);
+      mResultPromise->MaybeResolve(ret);
+    } else {
+      mResultPromise->MaybeResolve(mVerified);
+    }
+  }
+};
+
 class SimpleDigestTask : public ReturnArrayBufferViewTask
 {
 public:
   SimpleDigestTask(JSContext* aCx,
                    const ObjectOrString& aAlgorithm,
                    const CryptoOperationData& aData)
   {
     ATTEMPT_BUFFER_INIT(mData, aData);
@@ -793,16 +980,18 @@ WebCryptoTask::EncryptDecryptTask(JSCont
       (!aEncrypt && !aKey.HasUsage(Key::DECRYPT))) {
     return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
   }
 
   if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
       algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
       algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
     return new AesTask(aCx, aAlgorithm, aKey, aData, aEncrypt);
+  } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1)) {
+    return new RsaesPkcs1Task(aCx, aAlgorithm, aKey, aData, aEncrypt);
   }
 
   return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
 }
 
 WebCryptoTask*
 WebCryptoTask::SignVerifyTask(JSContext* aCx,
                               const ObjectOrString& aAlgorithm,
@@ -820,16 +1009,18 @@ WebCryptoTask::SignVerifyTask(JSContext*
   // Ensure key is usable for this operation
   if ((aSign  && !aKey.HasUsage(Key::SIGN)) ||
       (!aSign && !aKey.HasUsage(Key::VERIFY))) {
     return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
   }
 
   if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
     return new HmacTask(aCx, aAlgorithm, aKey, aSignature, aData, aSign);
+  } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
+    return new RsassaPkcs1Task(aCx, aAlgorithm, aKey, aSignature, aData, aSign);
   }
 
   return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
 }
 
 WebCryptoTask*
 WebCryptoTask::DigestTask(JSContext* aCx,
                           const ObjectOrString& aAlgorithm,
--- a/dom/crypto/test/test-vectors.js
+++ b/dom/crypto/test/test-vectors.js
@@ -197,9 +197,117 @@ tv = {
                        "72205468616e20426c6f636b2d53697a" +
                        "65204b6579202d2048617368204b6579" +
                        "204669727374"),
     sig: util.hex2abv("60e431591ee0b67f0d8a26aacbf5b77f" +
                       "8e0bc6213728c5140546040f0ee37f54"),
     sig_fail: util.hex2abv("000000001ee0b67f0d8a26aacbf5b77f" +
                            "8e0bc6213728c5140546040f0ee37f54"),
   },
+
+  // RSA test vectors, Example 1.3
+  // <ftp://ftp.rsa.com/pub/rsalabs/tmp/pkcs1v15crypt-vectors.txt>
+  rsaes: {
+    pkcs8: util.hex2abv(
+      "30820276020100300d06092a864886f70d0101010500048202603082025c0201" +
+      "0002818100a8b3b284af8eb50b387034a860f146c4919f318763cd6c5598c8ae" +
+      "4811a1e0abc4c7e0b082d693a5e7fced675cf4668512772c0cbc64a742c6c630" +
+      "f533c8cc72f62ae833c40bf25842e984bb78bdbf97c0107d55bdb662f5c4e0fa" +
+      "b9845cb5148ef7392dd3aaff93ae1e6b667bb3d4247616d4f5ba10d4cfd226de" +
+      "88d39f16fb020301000102818053339cfdb79fc8466a655c7316aca85c55fd8f" +
+      "6dd898fdaf119517ef4f52e8fd8e258df93fee180fa0e4ab29693cd83b152a55" +
+      "3d4ac4d1812b8b9fa5af0e7f55fe7304df41570926f3311f15c4d65a732c4831" +
+      "16ee3d3d2d0af3549ad9bf7cbfb78ad884f84d5beb04724dc7369b31def37d0c" +
+      "f539e9cfcdd3de653729ead5d1024100cc8853d1d54da630fac004f471f281c7" +
+      "b8982d8224a490edbeb33d3e3d5cc93c4765703d1dd791642f1f116a0dd852be" +
+      "2419b2af72bfe9a030e860b0288b5d77024100d32737e7267ffe1341b2d5c0d1" +
+      "50a81b586fb3132bed2f8d5262864a9cb9f30af38be448598d413a172efb802c" +
+      "21acf1c11c520c2f26a471dcad212eac7ca39d02410095297b0f95a2fa67d007" +
+      "07d609dfd4fc05c89dafc2ef6d6ea55bec771ea333734d9251e79082ecda866e" +
+      "fef13c459e1a631386b7e354c899f5f112ca85d7158302400e12bf1718e9cef5" +
+      "599ba1c3882fe8046a90874eefce8f2ccc20e4f2741fb0a33a3848aec9c9305f" +
+      "becbd2d76819967d4671acc6431e4037968db37878e695c102407fbf3360826f" +
+      "c125dceac9bf8d38b6cad8ccc8b4f867bfea02dcbf5df008258ee9902ea07e28" +
+      "7770df660328c81e906184b6aa2239868775204d098fc846c669"
+    ),
+    spki: util.hex2abv(
+      "30819f300d06092a864886f70d010101050003818d0030818902818100a8b3b2" +
+      "84af8eb50b387034a860f146c4919f318763cd6c5598c8ae4811a1e0abc4c7e0" +
+      "b082d693a5e7fced675cf4668512772c0cbc64a742c6c630f533c8cc72f62ae8" +
+      "33c40bf25842e984bb78bdbf97c0107d55bdb662f5c4e0fab9845cb5148ef739" +
+      "2dd3aaff93ae1e6b667bb3d4247616d4f5ba10d4cfd226de88d39f16fb020301" +
+      "0001"
+    ),
+    data: util.hex2abv(
+      "d94ae0832e6445ce42331cb06d531a82b1db4baad30f746dc916df24d4e3c245" +
+      "1fff59a6423eb0e1d02d4fe646cf699dfd818c6e97b051"
+    ),
+    result: util.hex2abv(
+      "709c7d2d4598c96065b6588da2f89fa87f062d7241ef6595898f637ada57eae9" +
+      "0173f0fb4bf6a91ebd96506907c853dacf208494be94d313a04185d474a90741" +
+      "2effc3e024d07e4d09aa245fbcb130219bfa5de02d4f7e2ec9e62e8ad32dee5f" +
+      "f4d8e4cfecbc5033a1c2c61c5233ae16192a481d0075bfc7ce028212cd27bebe"
+    ),
+  },
+
+  // RSA test vectors, Example 1.3 (sig256 generated with PyCrypto)
+  // <ftp://ftp.rsa.com/pub/rsalabs/tmp/pkcs1v15sign-vectors.txt>
+  rsassa: {
+    pkcs8: util.hex2abv(
+      "30820275020100300d06092a864886f70d01010105000482025f3082025b0201" +
+      "0002818100a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1" +
+      "e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ce" +
+      "abfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e" +
+      "6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb22" +
+      "49bd9a2137020301000102818033a5042a90b27d4f5451ca9bbbd0b44771a101" +
+      "af884340aef9885f2a4bbe92e894a724ac3c568c8f97853ad07c0266c8c6a3ca" +
+      "0929f1e8f11231884429fc4d9ae55fee896a10ce707c3ed7e734e44727a39574" +
+      "501a532683109c2abacaba283c31b4bd2f53c3ee37e352cee34f9e503bd80c06" +
+      "22ad79c6dcee883547c6a3b325024100e7e8942720a877517273a356053ea2a1" +
+      "bc0c94aa72d55c6e86296b2dfc967948c0a72cbccca7eacb35706e09a1df55a1" +
+      "535bd9b3cc34160b3b6dcd3eda8e6443024100b69dca1cf7d4d7ec81e75b90fc" +
+      "ca874abcde123fd2700180aa90479b6e48de8d67ed24f9f19d85ba275874f542" +
+      "cd20dc723e6963364a1f9425452b269a6799fd024028fa13938655be1f8a159c" +
+      "baca5a72ea190c30089e19cd274a556f36c4f6e19f554b34c077790427bbdd8d" +
+      "d3ede2448328f385d81b30e8e43b2fffa02786197902401a8b38f398fa712049" +
+      "898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd48be455e" +
+      "aeb6e1678255827580a8e4e8e14151d1510a82a3f2e729024027156aba4126d2" +
+      "4a81f3a528cbfb27f56886f840a9f6e86e17a44b94fe9319584b8e22fdde1e5a" +
+      "2e3bd8aa5ba8d8584194eb2190acf832b847f13a3d24a79f4d"
+    ),
+    spki: util.hex2abv(
+      "30819f300d06092a864886f70d010101050003818d0030818902818100a56e4a" +
+      "0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c510" +
+      "56ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd95" +
+      "08096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2" +
+      "d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137020301" +
+      "0001"
+    ),
+    data: util.hex2abv(
+      "a4b159941761c40c6a82f2b80d1b94f5aa2654fd17e12d588864679b54cd04ef" +
+      "8bd03012be8dc37f4b83af7963faff0dfa225477437c48017ff2be8191cf3955" +
+      "fc07356eab3f322f7f620e21d254e5db4324279fe067e0910e2e81ca2cab31c7" +
+      "45e67a54058eb50d993cdb9ed0b4d029c06d21a94ca661c3ce27fae1d6cb20f4" +
+      "564d66ce4767583d0e5f060215b59017be85ea848939127bd8c9c4d47b51056c" +
+      "031cf336f17c9980f3b8f5b9b6878e8b797aa43b882684333e17893fe9caa6aa" +
+      "299f7ed1a18ee2c54864b7b2b99b72618fb02574d139ef50f019c9eef4169713" +
+      "38e7d470"
+    ),
+    sig1: util.hex2abv(
+      "0b1f2e5180e5c7b4b5e672929f664c4896e50c35134b6de4d5a934252a3a245f" +
+      "f48340920e1034b7d5a5b524eb0e1cf12befef49b27b732d2c19e1c43217d6e1" +
+      "417381111a1d36de6375cf455b3c9812639dbc27600c751994fb61799ecf7da6" +
+      "bcf51540afd0174db4033188556675b1d763360af46feeca5b60f882829ee7b2"
+    ),
+    sig256: util.hex2abv(
+      "558af496a9900ec497a51723a0bf1be167a3fdd0e40c95764575bcc93d35d415" +
+      "94aef08cd8d339272387339fe5faa5635a1c4ad6c9b622f8c38edce6b26d9b76" +
+      "e3fec5b567e5b996624c4aeef74191c4349e5ac9e29b848c54bcfa538fec58d5" +
+      "9368253f0ff9a7ba0637918dd16b2c95f8c73ad7484482ba4387655f2f7d4b00"
+    ),
+    sig_fail: util.hex2abv(
+      "8000000080e5c7b4b5e672929f664c4896e50c35134b6de4d5a934252a3a245f" +
+      "f48340920e1034b7d5a5b524eb0e1cf12befef49b27b732d2c19e1c43217d6e1" +
+      "417381111a1d36de6375cf455b3c9812639dbc27600c751994fb61799ecf7da6" +
+      "bcf51540afd0174db4033188556675b1d763360af46feeca5b60f882829ee7b2"
+    ),
+  },
 }
--- a/dom/crypto/test/tests.js
+++ b/dom/crypto/test/tests.js
@@ -585,8 +585,186 @@ TestArray.addTest(
       .then(doVerify)
       .then(
         error(that),
         complete(that, function(x) { return true; })
       );
   }
 );
 
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "RSAES-PKCS#1 encrypt/decrypt round-trip",
+  function () {
+    var that = this;
+    var privKey, pubKey;
+    var alg = {name:"RSAES-PKCS1-v1_5"};
+
+    var privKey, pubKey, data, ct, pt;
+    function setPriv(x) { privKey = x; }
+    function setPub(x) { pubKey = x; }
+    function doEncrypt() {
+      return crypto.subtle.encrypt(alg.name, pubKey, tv.rsaes.data);
+    }
+    function doDecrypt(x) {
+      return crypto.subtle.decrypt(alg.name, privKey, x);
+    }
+
+    function fail() { error(that); }
+
+    Promise.all([
+      crypto.subtle.importKey("pkcs8", tv.rsaes.pkcs8, alg, false, ['decrypt'])
+          .then(setPriv, error(that)),
+      crypto.subtle.importKey("spki", tv.rsaes.spki, alg, false, ['encrypt'])
+          .then(setPub, error(that))
+    ]).then(doEncrypt, error(that))
+      .then(doDecrypt, error(that))
+      .then(
+        memcmp_complete(that, tv.rsaes.data),
+        error(that)
+      );
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "RSAES-PKCS#1 decryption known answer",
+  function () {
+    var that = this;
+    var alg = {name:"RSAES-PKCS1-v1_5"};
+
+    function doDecrypt(x) {
+      return crypto.subtle.decrypt(alg.name, x, tv.rsaes.result);
+    }
+    function fail() { error(that); }
+
+    crypto.subtle.importKey("pkcs8", tv.rsaes.pkcs8, alg, false, ['decrypt'])
+      .then( doDecrypt, fail )
+      .then( memcmp_complete(that, tv.rsaes.data), fail );
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "RSASSA/SHA-1 signature",
+  function () {
+    var that = this;
+    var alg = { name: "RSASSA-PKCS1-v1_5", hash: "SHA-1" };
+
+    function doSign(x) {
+      console.log("sign");
+      console.log(x);
+      return crypto.subtle.sign(alg.name, x, tv.rsassa.data);
+    }
+    function fail() { console.log("fail"); error(that); }
+
+    crypto.subtle.importKey("pkcs8", tv.rsassa.pkcs8, alg, false, ['sign'])
+      .then( doSign, fail )
+      .then( memcmp_complete(that, tv.rsassa.sig1), fail );
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "RSASSA verification (SHA-1)",
+  function () {
+    var that = this;
+    var alg = { name: "RSASSA-PKCS1-v1_5", hash: "SHA-1" };
+
+    function doVerify(x) {
+      return crypto.subtle.verify(alg.name, x, tv.rsassa.sig1, tv.rsassa.data);
+    }
+    function fail(x) { error(that); }
+
+    crypto.subtle.importKey("spki", tv.rsassa.spki, alg, false, ['verify'])
+      .then( doVerify, fail )
+      .then(
+        complete(that, function(x) { return x; }),
+        fail
+      );
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "RSASSA verification (SHA-1), failing verification",
+  function () {
+    var that = this;
+    var alg = { name: "RSASSA-PKCS1-v1_5", hash: "SHA-1" };
+
+    function doVerify(x) {
+      return crypto.subtle.verify(alg.name, x, tv.rsassa.sig_fail, tv.rsassa.data);
+    }
+    function fail(x) { error(that); }
+
+    crypto.subtle.importKey("spki", tv.rsassa.spki, alg, false, ['verify'])
+      .then( doVerify, fail )
+      .then(
+        complete(that, function(x) { return !x; }),
+        fail
+      );
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "RSASSA/SHA-256 signature",
+  function () {
+    var that = this;
+    var alg = { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" };
+
+    function doSign(x) {
+      return crypto.subtle.sign(alg.name, x, tv.rsassa.data);
+    }
+    function fail(x) { console.log(x); error(that); }
+
+    crypto.subtle.importKey("pkcs8", tv.rsassa.pkcs8, alg, false, ['sign'])
+      .then( doSign, fail )
+      .then( memcmp_complete(that, tv.rsassa.sig256), fail );
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "RSASSA verification (SHA-256)",
+  function () {
+    var that = this;
+    var alg = { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" };
+
+    function doVerify(x) {
+      return crypto.subtle.verify(alg.name, x, tv.rsassa.sig256, tv.rsassa.data);
+    }
+    function fail(x) { error(that); }
+
+    crypto.subtle.importKey("spki", tv.rsassa.spki, alg, false, ['verify'])
+      .then( doVerify, fail )
+      .then(
+        complete(that, function(x) { return x; }),
+        fail
+      );
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "RSASSA verification (SHA-256), failing verification",
+  function () {
+    var that = this;
+    var alg = { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" };
+    var use = ['sign', 'verify'];
+
+    function doVerify(x) {
+      console.log("verifying")
+      return crypto.subtle.verify(alg.name, x, tv.rsassa.sig_fail, tv.rsassa.data);
+    }
+    function fail(x) { console.log("failing"); error(that)(x); }
+
+    console.log("running")
+    crypto.subtle.importKey("spki", tv.rsassa.spki, alg, false, ['verify'])
+      .then( doVerify, fail )
+      .then(
+        complete(that, function(x) { return !x; }),
+        fail
+      );
+  }
+);
+
+
--- a/security/manager/ssl/src/ScopedNSSTypes.h
+++ b/security/manager/ssl/src/ScopedNSSTypes.h
@@ -11,16 +11,17 @@
 #include "mozilla/mozalloc_oom.h"
 #include "mozilla/Scoped.h"
 #include "nsError.h"
 #include "nsDebug.h"
 #include "prio.h"
 #include "cert.h"
 #include "cms.h"
 #include "keyhi.h"
+#include "cryptohi.h"
 #include "pk11pub.h"
 #include "sechash.h"
 #include "secpkcs7.h"
 #include "prerror.h"
 #include "ocsp.h"
 
 namespace mozilla {
 
@@ -115,21 +116,37 @@ MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLAT
 
 namespace psm {
 
 inline void
 PK11_DestroyContext_true(PK11Context * ctx) {
   PK11_DestroyContext(ctx, true);
 }
 
+inline void
+SGN_DestroyContext_true(SGNContext* ctx) {
+  SGN_DestroyContext(ctx, true);
+}
+
+inline void
+VFY_DestroyContext_true(VFYContext * ctx) {
+  VFY_DestroyContext(ctx, true);
+}
+
 } // 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(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.
  *
  * Typical usage, for digesting a buffer in memory:
  *