Bug 842818 - Enable structured cloning for CryptoKeys across threads r=baku,keeler
authorTim Taubert <tim@timtaubert.de>
Wed, 20 Jan 2016 23:22:41 +0100
changeset 316201 9262f09b33225eaf831004be688c9b6103d3503a
parent 316200 fdded53dc92f12deeb7e2b72f07571f577418d05
child 316202 17e4c84b299dba389fdb6cbc856629d11fea55bc
push id9480
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 17:12:58 +0000
treeherdermozilla-aurora@0d6a91c76a9e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku, keeler
bugs842818
milestone48.0a1
Bug 842818 - Enable structured cloning for CryptoKeys across threads r=baku,keeler
dom/base/StructuredCloneHolder.cpp
dom/crypto/CryptoKey.cpp
dom/crypto/test/mochitest.ini
dom/crypto/test/test_WebCrypto.html
dom/crypto/test/test_WebCrypto_Workers.html
--- a/dom/base/StructuredCloneHolder.cpp
+++ b/dom/base/StructuredCloneHolder.cpp
@@ -390,20 +390,16 @@ StructuredCloneHolder::ReadFullySerializ
                                                     JSStructuredCloneReader* aReader,
                                                     uint32_t aTag)
 {
   if (aTag == SCTAG_DOM_IMAGEDATA) {
     return ReadStructuredCloneImageData(aCx, aReader);
   }
 
   if (aTag == SCTAG_DOM_WEBCRYPTO_KEY) {
-    if (!NS_IsMainThread()) {
-      return nullptr;
-    }
-
     nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
     if (!global) {
       return nullptr;
     }
 
     // Prevent the return value from being trashed by a GC during ~nsRefPtr.
     JS::Rooted<JSObject*> result(aCx);
     {
@@ -506,17 +502,16 @@ StructuredCloneHolder::WriteFullySeriali
       return WriteStructuredCloneImageData(aCx, aWriter, imageData);
     }
   }
 
   // Handle Key cloning
   {
     CryptoKey* key = nullptr;
     if (NS_SUCCEEDED(UNWRAP_OBJECT(CryptoKey, aObj, key))) {
-      MOZ_ASSERT(NS_IsMainThread());
       return JS_WriteUint32Pair(aWriter, SCTAG_DOM_WEBCRYPTO_KEY, 0) &&
              key->WriteStructuredClone(aWriter);
     }
   }
 
 #ifdef MOZ_WEBRTC
   {
     // Handle WebRTC Certificate cloning
--- a/dom/crypto/CryptoKey.cpp
+++ b/dom/crypto/CryptoKey.cpp
@@ -4,17 +4,16 @@
  * 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 "nsNSSComponent.h"
 #include "ScopedNSSTypes.h"
 #include "mozilla/dom/CryptoKey.h"
-#include "mozilla/dom/WebCryptoCommon.h"
 #include "mozilla/dom/SubtleCryptoBinding.h"
 #include "mozilla/dom/ToJSValue.h"
 
 // Templates taken from security/nss/lib/cryptohi/seckey.c
 // These would ideally be exported by NSS and until that
 // happens we have to keep our own copies.
 const SEC_ASN1Template SECKEY_DHPublicKeyTemplate[] = {
     { SEC_ASN1_INTEGER, offsetof(SECKEYPublicKey,u.dh.publicValue), },
--- a/dom/crypto/test/mochitest.ini
+++ b/dom/crypto/test/mochitest.ini
@@ -18,9 +18,10 @@ skip-if = toolkit == 'android' # bug 120
 [test_WebCrypto_HKDF.html]
 [test_WebCrypto_JWK.html]
 [test_WebCrypto_Normalize.html]
 [test_WebCrypto_PBKDF2.html]
 [test_WebCrypto_Reject_Generating_Keys_Without_Usages.html]
 [test_WebCrypto_RSA_OAEP.html]
 [test_WebCrypto_RSA_PSS.html]
 [test_WebCrypto_Structured_Cloning.html]
+[test_WebCrypto_Workers.html]
 [test_WebCrypto_Wrap_Unwrap.html]
--- a/dom/crypto/test/test_WebCrypto.html
+++ b/dom/crypto/test/test_WebCrypto.html
@@ -281,16 +281,17 @@ TestArray.addTest(
     }
 
     function doGet() {
       var req = db.transaction([dbstore], "readwrite")
                   .objectStore(dbstore)
                   .get(dbkey);
       req.onerror = error(that);
       req.onsuccess = complete(that, function(e) {
+        db.close();
         return hasKeyFields(e.target.result.val);
       });
     }
 
     crypto.subtle.importKey("raw", tv.raw, alg, false, ['encrypt'])
       .then(doIndexedDB, error(that));
   }
 );
new file mode 100644
--- /dev/null
+++ b/dom/crypto/test/test_WebCrypto_Workers.html
@@ -0,0 +1,116 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+<title>WebCrypto Test Suite</title>
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+<link rel="stylesheet" href="./test_WebCrypto.css"/>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+
+<!-- Utilities for manipulating ABVs -->
+<script src="util.js"></script>
+
+<!-- A simple wrapper around IndexedDB -->
+<script src="simpledb.js"></script>
+
+<!-- Test vectors drawn from the literature -->
+<script src="./test-vectors.js"></script>
+
+<!-- General testing framework -->
+<script src="./test-array.js"></script>
+
+<script>/*<![CDATA[*/
+"use strict";
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Send a CryptoKey to a Worker and use it to encrypt data",
+  function () {
+    var worker = new Worker(`data:text/plain,
+      onmessage = ({data: {key, data, nonce}}) => {
+        var alg = { name: "AES-GCM", iv: nonce };
+        crypto.subtle.encrypt(alg, key, data).then(postMessage);
+      };
+    `);
+
+    var data = crypto.getRandomValues(new Uint8Array(128));
+    var nonce = crypto.getRandomValues(new Uint8Array(16));
+    var alg = { name: "AES-GCM", length: 128 };
+    var that = this;
+
+    // Generate a new AES key.
+    crypto.subtle.generateKey(alg, false, ["encrypt", "decrypt"]).then(key => {
+      // Wait for ciphertext, check and decrypt.
+      worker.addEventListener("message", ({data: ciphertext}) => {
+        var alg = { name: "AES-GCM", iv: nonce };
+        crypto.subtle.decrypt(alg, key, ciphertext)
+          .then(memcmp_complete(that, data), error(that));
+      });
+
+      // Send it to the worker.
+      worker.postMessage({key, data, nonce});
+    });
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Get a CryptoKey from a Worker and encrypt/decrypt data",
+  function () {
+    var worker = new Worker(`data:text/plain,
+      var alg = { name: "AES-GCM", length: 128 };
+      crypto.subtle.generateKey(alg, false, ["encrypt", "decrypt"])
+        .then(postMessage);
+    `);
+
+    var data = crypto.getRandomValues(new Uint8Array(128));
+    var nonce = crypto.getRandomValues(new Uint8Array(16));
+    var alg = { name: "AES-GCM", iv: nonce };
+    var that = this;
+
+    // Wait for the key from the worker.
+    worker.addEventListener("message", ({data: key}) => {
+      // Encrypt some data with the key.
+      crypto.subtle.encrypt(alg, key, data).then(ciphertext => {
+        // Verify and decrypt.
+        crypto.subtle.decrypt(alg, key, ciphertext)
+          .then(memcmp_complete(that, data), error(that));
+      });
+    });
+  }
+);
+/*]]>*/</script>
+</head>
+
+<body>
+
+<div id="content">
+	<div id="head">
+		<b>Web</b>Crypto<br>
+	</div>
+
+    <div id="start" onclick="start();">RUN ALL</div>
+
+    <div id="resultDiv" class="content">
+    Summary:
+    <span class="pass"><span id="passN">0</span> passed, </span>
+    <span class="fail"><span id="failN">0</span> failed, </span>
+    <span class="pending"><span id="pendingN">0</span> pending.</span>
+    <br/>
+    <br/>
+
+    <table id="results">
+        <tr>
+            <th>Test</th>
+            <th>Result</th>
+            <th>Time</th>
+        </tr>
+    </table>
+
+    </div>
+
+    <div id="foot"></div>
+</div>
+
+</body>
+</html>