[WebCrypto] Make sure all CryptoKey classes are structured clonable
authorjiewen_tan@apple.com <jiewen_tan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 17 Mar 2017 21:18:42 +0000 (21:18 +0000)
committerjiewen_tan@apple.com <jiewen_tan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 17 Mar 2017 21:18:42 +0000 (21:18 +0000)
https://bugs.webkit.org/show_bug.cgi?id=169232
<rdar://problem/31106660>

Reviewed by Brent Fulgham.

Source/WebCore:

This patch does the following few things: 1) It makes CryptoKeyEC and CryptoKeyRaw
structured clonable; 2) It makes CryptoKeyAES and CryptoKeyHMAC to use a move importer
during deserialization; 3) It adds some tests to make sure CryptoKeyAES, CryptoKeyHMAC
and CryptoKeyRSA won't lose their internal data during serialization/deserialization.

Tests: crypto/workers/subtle/ec-postMessage-worker.html
       crypto/workers/subtle/raw-postMessage-worker.html

* bindings/js/SerializedScriptValue.cpp:
(WebCore::CloneSerializer::write):
(WebCore::CloneDeserializer::readHMACKey):
(WebCore::CloneDeserializer::readAESKey):
(WebCore::CloneDeserializer::readECKey):
(WebCore::CloneDeserializer::readRawKey):
(WebCore::CloneDeserializer::readCryptoKey):
* crypto/keys/CryptoKeyEC.cpp:
(WebCore::CryptoKeyEC::namedCurveString):
(WebCore::CryptoKeyEC::isValidECAlgorithm):
* crypto/keys/CryptoKeyEC.h:

LayoutTests:

* crypto/workers/subtle/aes-postMessage-worker-expected.txt:
* crypto/workers/subtle/aes-postMessage-worker.html:
* crypto/workers/subtle/ec-postMessage-worker-expected.txt: Added.
* crypto/workers/subtle/ec-postMessage-worker.html: Added.
* crypto/workers/subtle/hmac-postMessage-worker-expected.txt:
* crypto/workers/subtle/hmac-postMessage-worker.html:
* crypto/workers/subtle/raw-postMessage-worker-expected.txt: Added.
* crypto/workers/subtle/raw-postMessage-worker.html: Added.
* crypto/workers/subtle/resources/ec-postMessage-worker.js: Added.
* crypto/workers/subtle/resources/raw-postMessage-worker.js: Added.
* crypto/workers/subtle/resources/rsa-postMessage-worker.js:
* crypto/workers/subtle/rsa-postMessage-worker-expected.txt:
* crypto/workers/subtle/rsa-postMessage-worker.html:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@214122 268f45cc-cd09-0410-ab3c-d52691b4dbfc

18 files changed:
LayoutTests/ChangeLog
LayoutTests/crypto/workers/subtle/aes-postMessage-worker-expected.txt
LayoutTests/crypto/workers/subtle/aes-postMessage-worker.html
LayoutTests/crypto/workers/subtle/ec-postMessage-worker-expected.txt [new file with mode: 0644]
LayoutTests/crypto/workers/subtle/ec-postMessage-worker.html [new file with mode: 0644]
LayoutTests/crypto/workers/subtle/hmac-postMessage-worker-expected.txt
LayoutTests/crypto/workers/subtle/hmac-postMessage-worker.html
LayoutTests/crypto/workers/subtle/raw-postMessage-worker-expected.txt [new file with mode: 0644]
LayoutTests/crypto/workers/subtle/raw-postMessage-worker.html [new file with mode: 0644]
LayoutTests/crypto/workers/subtle/resources/ec-postMessage-worker.js [new file with mode: 0644]
LayoutTests/crypto/workers/subtle/resources/raw-postMessage-worker.js [new file with mode: 0644]
LayoutTests/crypto/workers/subtle/resources/rsa-postMessage-worker.js
LayoutTests/crypto/workers/subtle/rsa-postMessage-worker-expected.txt
LayoutTests/crypto/workers/subtle/rsa-postMessage-worker.html
Source/WebCore/ChangeLog
Source/WebCore/bindings/js/SerializedScriptValue.cpp
Source/WebCore/crypto/keys/CryptoKeyEC.cpp
Source/WebCore/crypto/keys/CryptoKeyEC.h

index d805876..f35c1c8 100644 (file)
@@ -1,3 +1,25 @@
+2017-03-17  Jiewen Tan  <jiewen_tan@apple.com>
+
+        [WebCrypto] Make sure all CryptoKey classes are structured clonable
+        https://bugs.webkit.org/show_bug.cgi?id=169232
+        <rdar://problem/31106660>
+
+        Reviewed by Brent Fulgham.
+
+        * crypto/workers/subtle/aes-postMessage-worker-expected.txt:
+        * crypto/workers/subtle/aes-postMessage-worker.html:
+        * crypto/workers/subtle/ec-postMessage-worker-expected.txt: Added.
+        * crypto/workers/subtle/ec-postMessage-worker.html: Added.
+        * crypto/workers/subtle/hmac-postMessage-worker-expected.txt:
+        * crypto/workers/subtle/hmac-postMessage-worker.html:
+        * crypto/workers/subtle/raw-postMessage-worker-expected.txt: Added.
+        * crypto/workers/subtle/raw-postMessage-worker.html: Added.
+        * crypto/workers/subtle/resources/ec-postMessage-worker.js: Added.
+        * crypto/workers/subtle/resources/raw-postMessage-worker.js: Added.
+        * crypto/workers/subtle/resources/rsa-postMessage-worker.js:
+        * crypto/workers/subtle/rsa-postMessage-worker-expected.txt:
+        * crypto/workers/subtle/rsa-postMessage-worker.html:
+
 2017-03-17  Zalan Bujtas  <zalan@apple.com>
 
         Fix the flow thread state on the descendants of out of flow positioned replaced elements.
index 48de8ac..191b7ad 100644 (file)
@@ -9,6 +9,7 @@ PASS key.extractable is true
 PASS key.algorithm.name is 'AES-CBC'
 PASS key.algorithm.length is 128
 PASS key.usages is ['decrypt', 'encrypt']
+PASS bytesToASCIIString(exportedKey) is rawKeyAscii
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 9aecc3d..acec054 100644 (file)
 description("Test sending aes crypto keys via postMessage to a worker.");
 
 jsTestIsAsync = true;
+var rawKeyAscii = "16 bytes of key!";
+var rawKey = asciiToUint8Array(rawKeyAscii);
 
-crypto.subtle.importKey("raw", asciiToUint8Array("16 bytes of key!"), {name: "aes-cbc", length: 128}, true, ["encrypt", "decrypt"]).then(function(localKey) {
+crypto.subtle.importKey("raw", rawKey, {name: "aes-cbc", length: 128}, true, ["encrypt", "decrypt"]).then(function(localKey) {
     var worker = new Worker("resources/aes-postMessage-worker.js");
     worker.onmessage = function(evt) {
         if (!evt.data.result) {
@@ -25,8 +27,15 @@ crypto.subtle.importKey("raw", asciiToUint8Array("16 bytes of key!"), {name: "ae
             shouldBe("key.algorithm.name", "'AES-CBC'");
             shouldBe("key.algorithm.length", "128");
             shouldBe("key.usages", "['decrypt', 'encrypt']");
+
+            crypto.subtle.exportKey("raw", key).then(function(result) {
+                exportedKey = result;
+
+                shouldBe("bytesToASCIIString(exportedKey)", "rawKeyAscii");
+
+                finishJSTest();
+            });
         }
-        finishJSTest();
     }
     worker.postMessage(localKey);
 });
diff --git a/LayoutTests/crypto/workers/subtle/ec-postMessage-worker-expected.txt b/LayoutTests/crypto/workers/subtle/ec-postMessage-worker-expected.txt
new file mode 100644 (file)
index 0000000..75bc79e
--- /dev/null
@@ -0,0 +1,26 @@
+Test sending ec crypto keys via postMessage to a worker.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS All checks passed in worker for private key
+PASS privateKey.type is 'private'
+PASS privateKey.extractable is true
+PASS privateKey.algorithm.name is 'ECDH'
+PASS privateKey.algorithm.namedCurve is 'P-256'
+PASS privateKey.usages is ['deriveBits']
+PASS exportedKey.x is privateKeyJSON.x
+PASS exportedKey.y is privateKeyJSON.y
+PASS exportedKey.d is privateKeyJSON.d
+PASS All checks passed in worker for public key
+PASS publicKey.type is 'public'
+PASS publicKey.extractable is true
+PASS publicKey.algorithm.name is 'ECDH'
+PASS publicKey.algorithm.namedCurve is 'P-256'
+PASS publicKey.usages is [ ]
+PASS exportedKey.x is publicKeyJSON.x
+PASS exportedKey.y is publicKeyJSON.y
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/crypto/workers/subtle/ec-postMessage-worker.html b/LayoutTests/crypto/workers/subtle/ec-postMessage-worker.html
new file mode 100644 (file)
index 0000000..4a16666
--- /dev/null
@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<script src="../../../resources/js-test-pre.js"></script>
+<script src="../../resources/common.js"></script>
+</head>
+<body>
+<script>
+
+description("Test sending ec crypto keys via postMessage to a worker.");
+
+jsTestIsAsync = true;
+
+var publicKeyJSON = {
+    kty: "EC",
+    use: "enc",
+    ext: true,
+    crv: "P-256",
+    x: "1FSVWieTvikFkG1NOyhkUCaMbdQhxwH6aCu4Ez-sRtA",
+    y: "9jmNTLqM4cjBhdAnHcNI9YQV3O8LFmo-EdZWk8ntAaI",
+};
+
+var privateKeyJSON = {
+    kty: "EC",
+    ext: true,
+    key_ops: ["deriveBits", "deriveKey"],
+    crv: "P-256",
+    x: "1FSVWieTvikFkG1NOyhkUCaMbdQhxwH6aCu4Ez-sRtA",
+    y: "9jmNTLqM4cjBhdAnHcNI9YQV3O8LFmo-EdZWk8ntAaI",
+    d: "ppxBSov3N8_AUcisAuvmLV4yE8e_L_BLE8bZb9Z1Xjg",
+};
+
+var count = 0;
+var worker = new Worker("resources/ec-postMessage-worker.js");
+worker.onmessage = function(evt) {
+    if (!evt.data.result) {
+        testFailed("Check failed in worker: " + evt.data.message);
+        finishJSTest();
+    } else {
+        if (publicKey = evt.data.publicKey) {
+            testPassed("All checks passed in worker for public key");
+            shouldBe("publicKey.type", "'public'");
+            shouldBe("publicKey.extractable", "true");
+            shouldBe("publicKey.algorithm.name", "'ECDH'");
+            shouldBe("publicKey.algorithm.namedCurve", "'P-256'");
+            shouldBe("publicKey.usages", "[ ]");
+
+            crypto.subtle.exportKey("jwk", publicKey).then(function(result) {
+                count = count + 1;
+                exportedKey = result;
+
+                shouldBe("exportedKey.x", "publicKeyJSON.x");
+                shouldBe("exportedKey.y", "publicKeyJSON.y");
+
+                if (count == 2)
+                    finishJSTest();
+            });
+        } else if (privateKey = evt.data.privateKey) {
+            testPassed("All checks passed in worker for private key");
+            shouldBe("privateKey.type", "'private'");
+            shouldBe("privateKey.extractable", "true");
+            shouldBe("privateKey.algorithm.name", "'ECDH'");
+            shouldBe("privateKey.algorithm.namedCurve", "'P-256'");
+            shouldBe("privateKey.usages", "['deriveBits']");
+
+            crypto.subtle.exportKey("jwk", privateKey).then(function(result) {
+                count = count + 1;
+                exportedKey = result;
+
+                shouldBe("exportedKey.x", "privateKeyJSON.x");
+                shouldBe("exportedKey.y", "privateKeyJSON.y");
+                shouldBe("exportedKey.d", "privateKeyJSON.d");
+
+                if (count == 2)
+                    finishJSTest();
+            });
+        }
+    }
+}
+
+crypto.subtle.importKey("jwk", publicKeyJSON, { name:"ECDH", namedCurve:"P-256" }, true, [ ]).then(function(localPublicKey) {
+    worker.postMessage({ publicKey: localPublicKey });
+});
+crypto.subtle.importKey("jwk", privateKeyJSON, { name:"ECDH", namedCurve:"P-256" }, true, ['deriveBits']).then(function(localPrivateKey) {
+    worker.postMessage({ privateKey: localPrivateKey });
+});
+</script>
+<script src="../../../resources/js-test-post.js"></script>
+</body>
+</html>
index e237ae8..0ec0827 100644 (file)
@@ -10,6 +10,7 @@ PASS key.algorithm.name is 'HMAC'
 PASS key.algorithm.length is 128
 PASS key.algorithm.hash.name is 'SHA-1'
 PASS key.usages is ["sign", "verify"]
+PASS bytesToASCIIString(exportedKey) is rawKeyAscii
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 0ee1171..57136aa 100644 (file)
 description("Test sending hmac crypto keys via postMessage to a worker.");
 
 jsTestIsAsync = true;
+var rawKeyAscii = "16 bytes of key!";
+var rawKey = asciiToUint8Array(rawKeyAscii);
 
-crypto.subtle.importKey("raw", asciiToUint8Array("16 bytes of key!"), {name: 'hmac', hash: {name: 'sha-1'}}, true, ['sign', 'verify']).then(function(localKey) {
+crypto.subtle.importKey("raw", rawKey, {name: 'hmac', hash: {name: 'sha-1'}}, true, ['sign', 'verify']).then(function(localKey) {
     var worker = new Worker("resources/hmac-postMessage-worker.js");
     worker.onmessage = function(evt) {
         if (!evt.data.result) {
@@ -26,8 +28,15 @@ crypto.subtle.importKey("raw", asciiToUint8Array("16 bytes of key!"), {name: 'hm
             shouldBe("key.algorithm.length", "128");
             shouldBe("key.algorithm.hash.name", "'SHA-1'");
             shouldBe("key.usages", '["sign", "verify"]');
+
+            crypto.subtle.exportKey("raw", key).then(function(result) {
+                exportedKey = result;
+
+                shouldBe("bytesToASCIIString(exportedKey)", "rawKeyAscii");
+
+                finishJSTest();
+            });
         }
-        finishJSTest();
     }
     worker.postMessage(localKey);
 });
diff --git a/LayoutTests/crypto/workers/subtle/raw-postMessage-worker-expected.txt b/LayoutTests/crypto/workers/subtle/raw-postMessage-worker-expected.txt
new file mode 100644 (file)
index 0000000..9158d63
--- /dev/null
@@ -0,0 +1,14 @@
+Test sending raw crypto keys via postMessage to a worker.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS All checks passed in worker
+PASS key.type is 'secret'
+PASS key.extractable is false
+PASS key.algorithm.name is 'PBKDF2'
+PASS key.usages is ['deriveBits', 'deriveKey']
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/crypto/workers/subtle/raw-postMessage-worker.html b/LayoutTests/crypto/workers/subtle/raw-postMessage-worker.html
new file mode 100644 (file)
index 0000000..a1ae6cf
--- /dev/null
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<script src="../../../resources/js-test-pre.js"></script>
+<script src="../../resources/common.js"></script>
+</head>
+<body>
+<script>
+
+description("Test sending raw crypto keys via postMessage to a worker.");
+
+jsTestIsAsync = true;
+
+crypto.subtle.importKey("raw", asciiToUint8Array("16 bytes of key!"), "PBKDF2", false, ["deriveBits", "deriveKey"]).then(function(localKey) {
+    var worker = new Worker("resources/raw-postMessage-worker.js");
+    worker.onmessage = function(evt) {
+        if (!evt.data.result) {
+            testFailed("Check failed in worker: " + evt.data.message);
+        } else {
+            testPassed("All checks passed in worker");
+            key = evt.data.key;
+            shouldBe("key.type", "'secret'");
+            shouldBe("key.extractable", "false");
+            shouldBe("key.algorithm.name", "'PBKDF2'");
+            shouldBe("key.usages", "['deriveBits', 'deriveKey']");
+        }
+        finishJSTest();
+    }
+    worker.postMessage(localKey);
+});
+</script>
+<script src="../../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/crypto/workers/subtle/resources/ec-postMessage-worker.js b/LayoutTests/crypto/workers/subtle/resources/ec-postMessage-worker.js
new file mode 100644 (file)
index 0000000..0d4c0f3
--- /dev/null
@@ -0,0 +1,34 @@
+importScripts("../../../resources/common.js");
+
+onmessage = function(evt)
+{
+    if (publicKey = evt.data.publicKey) {
+        if (publicKey.type != 'public')
+            postMessage({ result:false, message:'publicKey.type should be "public"' });
+        else if (!publicKey.extractable)
+            postMessage({ result:false, message:'publicKey.extractable should be true' });
+        else if (publicKey.algorithm.name != "ECDH")
+            postMessage({ result:false, message:'publicKey.algorithm.name should be "ECDH"' });
+        else if (publicKey.algorithm.namedCurve != "P-256")
+            postMessage({ result:false, message:'publicKey.algorithm.namedCurve should be P-256' });
+        else if (publicKey.usages.toString() != "")
+            postMessage({ result:false, message:'publicKey.usages should be [ ]' });
+        else
+            postMessage({ result:true, publicKey:publicKey });
+    } else if (privateKey = evt.data.privateKey) {
+        if (privateKey.type != 'private')
+            postMessage({ result:false, message:'privateKey.type should be "private"' });
+        else if (!privateKey.extractable)
+            postMessage({ result:false, message:'privateKey.extractable should be true' });
+        else if (privateKey.algorithm.name != "ECDH")
+            postMessage({ result:false, message:'publicKey.algorithm.name should be "ECDH"' });
+        else if (privateKey.algorithm.namedCurve != "P-256")
+            postMessage({ result:false, message:'publicKey.algorithm.namedCurve should be P-256' });
+        else if (privateKey.usages.toString() != "deriveBits")
+            postMessage({ result:false, message:'privateKey.usages should be ["deriveBits"]' });
+        else
+            postMessage({ result:true, privateKey:privateKey });
+    } else {
+        postMessage({ result:false, message:'key is ' + key });
+    }
+}
diff --git a/LayoutTests/crypto/workers/subtle/resources/raw-postMessage-worker.js b/LayoutTests/crypto/workers/subtle/resources/raw-postMessage-worker.js
new file mode 100644 (file)
index 0000000..0818c41
--- /dev/null
@@ -0,0 +1,16 @@
+onmessage = function(evt)
+{
+    var key = evt.data;
+    if (!key)
+        postMessage({ result:false, message:'key is ' + key });
+    if (key.type != 'secret')
+        postMessage({ result:false, message:'key.type should be "secret"' });
+    else if (key.extractable)
+        postMessage({ result:false, message:'key.extractable should be false' });
+    else if (key.algorithm.name != "PBKDF2")
+        postMessage({ result:false, message:'key.algorithm.name should be "PBKDF2"' });
+    else if (key.usages.toString() != "deriveBits,deriveKey")
+        postMessage({ result:false, message:'key.usages should be ["deriveBits", "deriveKey"]' });
+    else
+        postMessage({ result:true, key:key });
+}
index 21c87c2..3a0ef83 100644 (file)
@@ -22,8 +22,8 @@ onmessage = function(evt)
     } else if (privateKey = evt.data.privateKey) {
         if (privateKey.type != 'private')
             postMessage({ result:false, message:'privateKey.type should be "private"' });
-        else if (privateKey.extractable)
-            postMessage({ result:false, message:'privateKey.extractable should be false' });
+        else if (!privateKey.extractable)
+            postMessage({ result:false, message:'privateKey.extractable should be true' });
         else if (privateKey.algorithm.name != "RSAES-PKCS1-v1_5")
             postMessage({ result:false, message:'privateKey.algorithm.name should be "RSAES-PKCS1-v1_5"' });
         else if (privateKey.algorithm.modulusLength != 2048)
index 6bd3d29..0c39790 100644 (file)
@@ -5,12 +5,20 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 PASS All checks passed in worker for private key
 PASS privateKey.type is 'private'
-PASS privateKey.extractable is false
+PASS privateKey.extractable is true
 PASS privateKey.algorithm.name is 'RSAES-PKCS1-v1_5'
 PASS privateKey.algorithm.modulusLength is 2048
 PASS bytesToHexString(privateKey.algorithm.publicExponent) is '010001'
 PASS privateKey.algorithm.hash is undefined.
 PASS privateKey.usages is ['decrypt']
+PASS exportedKey.n is privateKeyJSON.n
+PASS exportedKey.e is privateKeyJSON.e
+PASS exportedKey.d is privateKeyJSON.d
+PASS exportedKey.p is privateKeyJSON.p
+PASS exportedKey.q is privateKeyJSON.q
+PASS exportedKey.dp is privateKeyJSON.dp
+PASS exportedKey.dq is privateKeyJSON.dq
+PASS exportedKey.qi is privateKeyJSON.qi
 PASS All checks passed in worker for public key
 PASS publicKey.type is 'public'
 PASS publicKey.extractable is true
@@ -19,6 +27,8 @@ PASS publicKey.algorithm.modulusLength is 2048
 PASS bytesToHexString(publicKey.algorithm.publicExponent) is '010001'
 PASS publicKey.algorithm.hash is undefined.
 PASS publicKey.usages is ['encrypt']
+PASS exportedKey.n is publicKeyJSON.n
+PASS exportedKey.e is publicKeyJSON.e
 PASS successfullyParsed is true
 
 TEST COMPLETE
index bfe9f38..4f551ea 100644 (file)
@@ -55,27 +55,51 @@ worker.onmessage = function(evt) {
             shouldBe("bytesToHexString(publicKey.algorithm.publicExponent)", "'010001'");
             shouldBeUndefined("publicKey.algorithm.hash");
             shouldBe("publicKey.usages", "['encrypt']");
+
+            crypto.subtle.exportKey("jwk", publicKey).then(function(result) {
+                count = count + 1;
+                exportedKey = result;
+
+                shouldBe("exportedKey.n", "publicKeyJSON.n");
+                shouldBe("exportedKey.e", "publicKeyJSON.e");
+
+                if (count == 2)
+                    finishJSTest();
+            });
         } else if (privateKey = evt.data.privateKey) {
             testPassed("All checks passed in worker for private key");
             shouldBe("privateKey.type", "'private'");
-            shouldBe("privateKey.extractable", "false");
+            shouldBe("privateKey.extractable", "true");
             shouldBe("privateKey.algorithm.name", "'RSAES-PKCS1-v1_5'");
             shouldBe("privateKey.algorithm.modulusLength", "2048");
             shouldBe("bytesToHexString(privateKey.algorithm.publicExponent)", "'010001'");
             shouldBeUndefined("privateKey.algorithm.hash");
             shouldBe("privateKey.usages", "['decrypt']");
+
+            crypto.subtle.exportKey("jwk", privateKey).then(function(result) {
+                count = count + 1;
+                exportedKey = result;
+
+                shouldBe("exportedKey.n", "privateKeyJSON.n");
+                shouldBe("exportedKey.e", "privateKeyJSON.e");
+                shouldBe("exportedKey.d", "privateKeyJSON.d");
+                shouldBe("exportedKey.p", "privateKeyJSON.p");
+                shouldBe("exportedKey.q", "privateKeyJSON.q");
+                shouldBe("exportedKey.dp", "privateKeyJSON.dp");
+                shouldBe("exportedKey.dq", "privateKeyJSON.dq");
+                shouldBe("exportedKey.qi", "privateKeyJSON.qi");
+
+                if (count == 2)
+                    finishJSTest();
+            });
         }
-        count = count + 1;
     }
-
-    if (count == 2)
-        finishJSTest();
 }
 
 crypto.subtle.importKey("jwk", publicKeyJSON, algorithmKeyGen, true, ['encrypt']).then(function(localPublicKey) {
     worker.postMessage({ publicKey: localPublicKey });
 });
-crypto.subtle.importKey("jwk", privateKeyJSON, algorithmKeyGen, false, ['decrypt']).then(function(localPrivateKey) {
+crypto.subtle.importKey("jwk", privateKeyJSON, algorithmKeyGen, true, ['decrypt']).then(function(localPrivateKey) {
     worker.postMessage({ privateKey: localPrivateKey });
 });
 </script>
index f9cb96a..e0788d4 100644 (file)
@@ -1,3 +1,31 @@
+2017-03-17  Jiewen Tan  <jiewen_tan@apple.com>
+
+        [WebCrypto] Make sure all CryptoKey classes are structured clonable
+        https://bugs.webkit.org/show_bug.cgi?id=169232
+        <rdar://problem/31106660>
+
+        Reviewed by Brent Fulgham.
+
+        This patch does the following few things: 1) It makes CryptoKeyEC and CryptoKeyRaw
+        structured clonable; 2) It makes CryptoKeyAES and CryptoKeyHMAC to use a move importer
+        during deserialization; 3) It adds some tests to make sure CryptoKeyAES, CryptoKeyHMAC
+        and CryptoKeyRSA won't lose their internal data during serialization/deserialization.
+
+        Tests: crypto/workers/subtle/ec-postMessage-worker.html
+               crypto/workers/subtle/raw-postMessage-worker.html
+
+        * bindings/js/SerializedScriptValue.cpp:
+        (WebCore::CloneSerializer::write):
+        (WebCore::CloneDeserializer::readHMACKey):
+        (WebCore::CloneDeserializer::readAESKey):
+        (WebCore::CloneDeserializer::readECKey):
+        (WebCore::CloneDeserializer::readRawKey):
+        (WebCore::CloneDeserializer::readCryptoKey):
+        * crypto/keys/CryptoKeyEC.cpp:
+        (WebCore::CryptoKeyEC::namedCurveString):
+        (WebCore::CryptoKeyEC::isValidECAlgorithm):
+        * crypto/keys/CryptoKeyEC.h:
+
 2017-03-17  Michael Saboff  <msaboff@apple.com>
 
         Use USE_INTERNAL_SDK to compute ENABLE_FAST_JIT_PERMISSIONS instead of HAVE_INTERNAL_SDK
index 1d5dcca..92a7322 100644 (file)
 #include "CryptoKeyAES.h"
 #include "CryptoKeyDataOctetSequence.h"
 #include "CryptoKeyDataRSAComponents.h"
+#include "CryptoKeyEC.h"
 #include "CryptoKeyHMAC.h"
 #include "CryptoKeyRSA.h"
+#include "CryptoKeyRaw.h"
 #include "File.h"
 #include "FileList.h"
 #include "IDBValue.h"
@@ -191,9 +193,11 @@ const uint32_t currentKeyFormatVersion = 1;
 enum class CryptoKeyClassSubtag {
     HMAC = 0,
     AES = 1,
-    RSA = 2
+    RSA = 2,
+    EC = 3,
+    Raw = 4,
 };
-const uint8_t cryptoKeyClassSubtagMaximumValue = 2;
+const uint8_t cryptoKeyClassSubtagMaximumValue = 4;
 
 enum class CryptoKeyAsymmetricTypeSubtag {
     Public = 0,
@@ -383,6 +387,12 @@ static const unsigned StringDataIs8BitFlag = 0x80000000;
  *
  * PrimeInfo :-
  *    <factorSize:uint32_t> <factor:byte{factorSize}> <crtExponentSize:uint32_t> <crtExponent:byte{crtExponentSize}> <crtCoefficientSize:uint32_t> <crtCoefficient:byte{crtCoefficientSize}>
+ *
+ * CryptoKeyEC :-
+ *    CryptoAlgorithmIdentifierTag <namedCurve:StringData> CryptoKeyAsymmetricTypeSubtag <keySize:uint32_t> <keyData:byte{keySize}>
+ *
+ * CryptoKeyRaw :-
+ *    CryptoAlgorithmIdentifierTag <keySize:uint32_t> <keyData:byte{keySize}>
  */
 
 using DeserializationResult = std::pair<JSC::JSValue, SerializationReturnCode>;
@@ -1209,12 +1219,33 @@ private:
             write(downcast<CryptoKeyAES>(*key).key());
             break;
         case CryptoKeyClass::EC:
-            // A dummy implementation for now.
-            // FIXME: https://bugs.webkit.org/show_bug.cgi?id=169232
+            write(CryptoKeyClassSubtag::EC);
+            write(key->algorithmIdentifier());
+            write(downcast<CryptoKeyEC>(*key).namedCurveString());
+            switch (key->type()) {
+            case CryptoKey::Type::Public: {
+                write(CryptoKeyAsymmetricTypeSubtag::Public);
+                auto result = downcast<CryptoKeyEC>(*key).exportRaw();
+                ASSERT(!result.hasException());
+                write(result.releaseReturnValue());
+                break;
+            }
+            case CryptoKey::Type::Private: {
+                write(CryptoKeyAsymmetricTypeSubtag::Private);
+                // Use the standard complied method is not very efficient, but simple/reliable.
+                auto result = downcast<CryptoKeyEC>(*key).exportPkcs8();
+                ASSERT(!result.hasException());
+                write(result.releaseReturnValue());
+                break;
+            }
+            default:
+                ASSERT_NOT_REACHED();
+            }
             break;
         case CryptoKeyClass::Raw:
-            // A dummy implementation for now.
-            // FIXME: https://bugs.webkit.org/show_bug.cgi?id=169232
+            write(CryptoKeyClassSubtag::Raw);
+            write(key->algorithmIdentifier());
+            write(downcast<CryptoKeyRaw>(*key).key());
             break;
         case CryptoKeyClass::RSA:
             write(CryptoKeyClassSubtag::RSA);
@@ -2031,7 +2062,7 @@ private:
         CryptoAlgorithmIdentifier hash;
         if (!read(hash))
             return false;
-        result = CryptoKeyHMAC::create(keyData, hash, extractable, usages);
+        result = CryptoKeyHMAC::importRaw(0, hash, WTFMove(keyData), extractable, usages);
         return true;
     }
 
@@ -2045,7 +2076,7 @@ private:
         Vector<uint8_t> keyData;
         if (!read(keyData))
             return false;
-        result = CryptoKeyAES::create(algorithm, keyData, extractable, usages);
+        result = CryptoKeyAES::importRaw(algorithm, WTFMove(keyData), extractable, usages);
         return true;
     }
 
@@ -2127,6 +2158,47 @@ private:
         return true;
     }
 
+    bool readECKey(bool extractable, CryptoKeyUsageBitmap usages, RefPtr<CryptoKey>& result)
+    {
+        CryptoAlgorithmIdentifier algorithm;
+        if (!read(algorithm))
+            return false;
+        if (!CryptoKeyEC::isValidECAlgorithm(algorithm))
+            return false;
+        CachedStringRef curve;
+        if (!readStringData(curve))
+            return false;
+        CryptoKeyAsymmetricTypeSubtag type;
+        if (!read(type))
+            return false;
+        Vector<uint8_t> keyData;
+        if (!read(keyData))
+            return false;
+
+        switch (type) {
+        case CryptoKeyAsymmetricTypeSubtag::Public:
+            result = CryptoKeyEC::importRaw(algorithm, curve->string(), WTFMove(keyData), extractable, usages);
+            break;
+        case CryptoKeyAsymmetricTypeSubtag::Private:
+            result = CryptoKeyEC::importPkcs8(algorithm, curve->string(), WTFMove(keyData), extractable, usages);
+            break;
+        }
+
+        return true;
+    }
+
+    bool readRawKey(CryptoKeyUsageBitmap usages, RefPtr<CryptoKey>& result)
+    {
+        CryptoAlgorithmIdentifier algorithm;
+        if (!read(algorithm))
+            return false;
+        Vector<uint8_t> keyData;
+        if (!read(keyData))
+            return false;
+        result = CryptoKeyRaw::create(algorithm, WTFMove(keyData), usages);
+        return true;
+    }
+
     bool readCryptoKey(JSValue& cryptoKey)
     {
         uint32_t keyFormatVersion;
@@ -2191,6 +2263,14 @@ private:
             if (!readRSAKey(extractable, usages, result))
                 return false;
             break;
+        case CryptoKeyClassSubtag::EC:
+            if (!readECKey(extractable, usages, result))
+                return false;
+            break;
+        case CryptoKeyClassSubtag::Raw:
+            if (!readRawKey(usages, result))
+                return false;
+            break;
         }
         cryptoKey = getJSValue(result.get());
         return true;
index d8de61e..db2eba7 100644 (file)
@@ -173,6 +173,24 @@ ExceptionOr<Vector<uint8_t>> CryptoKeyEC::exportPkcs8() const
     return platformExportPkcs8();
 }
 
+String CryptoKeyEC::namedCurveString() const
+{
+    switch (m_curve) {
+    case NamedCurve::P256:
+        return String(P256);
+    case NamedCurve::P384:
+        return String(P384);
+    }
+
+    ASSERT_NOT_REACHED();
+    return emptyString();
+}
+
+bool CryptoKeyEC::isValidECAlgorithm(CryptoAlgorithmIdentifier algorithm)
+{
+    return algorithm == CryptoAlgorithmIdentifier::ECDSA || algorithm == CryptoAlgorithmIdentifier::ECDH;
+}
+
 std::unique_ptr<KeyAlgorithm> CryptoKeyEC::buildAlgorithm() const
 {
     String name = CryptoAlgorithmRegistry::singleton().name(algorithmIdentifier());
index 028c387..f18d414 100644 (file)
@@ -89,7 +89,9 @@ public:
 
     size_t keySizeInBits() const;
     NamedCurve namedCurve() const { return m_curve; }
+    String namedCurveString() const;
     PlatformECKey platformKey() { return m_platformKey; }
+    static bool isValidECAlgorithm(CryptoAlgorithmIdentifier);
 
 private:
     CryptoKeyEC(CryptoAlgorithmIdentifier, NamedCurve, CryptoKeyType, PlatformECKey, bool extractable, CryptoKeyUsageBitmap);