Commit 8cafd48f authored by eroman@chromium.org's avatar eroman@chromium.org

[webcrypto] Implement structured clone of keys (blink-side).

The format looks like this:

  subtag:byte            // The type of key
  keySpecificProperties  // Block of key specific algorithm info
  usages:uint32          // Bitfield of usages + extractability
  keyDataLength:uint32   // Block of data controlled by embedder
  keyData:byte[keyDataLength]

subtag influences how keySpecificProperties is interpreted:

[If subtag=AesKeyTag]
  keyLengthBytes:uint32  // 16, 24, or 32
  algorithmId:uint32

[If subtag=HmacKeyTag]
  keyLengthBytes:uint32
  hashId:uint32

[If subtag=RsaKeyTag]
  algorithmId:uint32
  type:uint32            // One of {PublicKeyType, PrivateKeyType}
  modulusLengthBits:uint32
  publicExponentLength:uint32
  publicExponent:byte[publicExponentLength]

[If subtag=RsaHashedKeyTag]
  <Same as for RsaKeyTag>
  hashId:uint32

Note that uint32 is encoded as a variable length number. In practice it ends up being a single byte for most of the uses above.

In this design, blink is responsible for serializing all of the key's attributes except for the actual key data which is left to the embedder.

The included tests rely on the chromium side of structured clone landing:
  https://codereview.chromium.org/196513002/

The tests cover serialization of hmac, aes and rsa public keys. I haven't added tests for serialization of rsa private keys yet, since that part is not done on the chromium side.

BUG=245025

Review URL: https://codereview.chromium.org/195543002

git-svn-id: svn://svn.chromium.org/blink/trunk@169633 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent 685f6782
<!DOCTYPE html>
<html>
<head>
<script src="../resources/js-test.js"></script>
<script src="resources/common.js"></script>
</head>
<body>
<p id="description"></p>
<div id="console"></div>
<script>
description("Tests structured cloning of AES keys");
jsTestIsAsync = true;
// Tests the 48 permutations of keys generated by:
// kPossibleAlgorithms x kPossibleExtractable x kPossibleKeyUsages x kPossibleKeyData
//
// For practical reasons these tests are not exhaustive.
var k128BitData = "30112233445566778899aabbccddeeff"
var k192BitData = "800102030405060708090a0b0c0d0e0f1011121314151617";
var k256BitData = "00112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0f";
var kPossibleAlgorithms = ['AES-CBC', 'AES-GCM'];
var kPossibleExtractable = [true, false];
var kPossibleKeyUsages = [[], ['encrypt'], ['decrypt', 'wrapKey'], ['encrypt', 'wrapKey', 'unwrapKey']];
var kPossibleKeyData = [k128BitData, k192BitData, k256BitData];
function runTest(algorithmName, extractable, keyUsages, keyData)
{
var importData = hexStringToUint8Array(keyData);
var importAlgorithm = { name: algorithmName };
var results = {};
return crypto.subtle.importKey('raw', importData, importAlgorithm, extractable, keyUsages).then(function(importedKey) {
results.importedKey = importedKey;
importedKey.extraProperty = 'hi';
return cloneKey(importedKey);
}).then(function(clonedKey) {
results.clonedKey = clonedKey;
if (extractable)
return crypto.subtle.exportKey('raw', clonedKey);
return null;
}).then(function(clonedKeyData) {
importedKey = results.importedKey;
clonedKey = results.clonedKey;
shouldEvaluateAs("importedKey.extraProperty", "hi");
shouldEvaluateAs("importedKey.type", "secret");
shouldEvaluateAs("importedKey.extractable", extractable);
shouldEvaluateAs("importedKey.algorithm.name", algorithmName);
shouldEvaluateAs("importedKey.algorithm.length", importData.length * 8);
shouldEvaluateAs("importedKey.usages.join(',')", keyUsages.join(","));
shouldBeTrue("importedKey != clonedKey");
shouldBeUndefined("clonedKey.extraProperty");
shouldEvaluateAs("clonedKey.type", "secret");
shouldEvaluateAs("clonedKey.extractable", extractable);
shouldEvaluateAs("clonedKey.algorithm.name", algorithmName);
shouldEvaluateAs("clonedKey.algorithm.length", importData.length * 8);
shouldEvaluateAs("clonedKey.usages.join(',')", keyUsages.join(","));
logSerializedKey(importedKey);
if (extractable)
bytesShouldMatchHexString("Cloned key exported data", keyData, clonedKeyData);
debug("");
});
}
var lastPromise = Promise.resolve(null);
kPossibleAlgorithms.forEach(function(algorithmName) {
kPossibleExtractable.forEach(function(extractable) {
kPossibleKeyUsages.forEach(function(keyUsages) {
kPossibleKeyData.forEach(function(keyData) {
lastPromise = lastPromise.then(runTest.bind(null, algorithmName, extractable, keyUsages, keyData));
});
});
});
});
lastPromise.then(finishJSTest, failAndFinishJSTest);
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<script src="../resources/js-test.js"></script>
<script src="resources/common.js"></script>
</head>
<body>
<p id="description"></p>
<div id="console"></div>
<script>
description("Tests structured cloning of HMAC keys");
jsTestIsAsync = true;
// Tests the 48 permutations of keys generated by:
// kPossibleHashAlgorithms x kPossibleExtractable x kPossibleKeyUsages x kPossibleKeyData
//
// For practical reasons these tests are not exhaustive.
var k128BitData = "30112233445566778899aabbccddeeff"
var k256BitData = "00112233445546778899aabbccddeeff000102030405060708090a0b0c0d0e0f";
var kPossibleHashAlgorithms = ['SHA-1', 'SHA-256', 'SHA-512'];
var kPossibleExtractable = [true, false];
var kPossibleKeyUsages = [[], ['sign'], ['verify'], ['sign', 'verify']];
var kPossibleKeyData = [
k128BitData,
k256BitData
];
function runTest(hashName, extractable, keyUsages, keyData)
{
var importData = hexStringToUint8Array(keyData);
var importAlgorithm = { name: 'HMAC', hash: {name: hashName } };
var results = {};
return crypto.subtle.importKey('raw', importData, importAlgorithm, extractable, keyUsages).then(function(importedKey) {
results.importedKey = importedKey;
importedKey.extraProperty = 'hi';
return cloneKey(importedKey);
}).then(function(clonedKey) {
results.clonedKey = clonedKey;
if (extractable)
return crypto.subtle.exportKey('raw', clonedKey);
return null;
}).then(function(clonedKeyData) {
importedKey = results.importedKey;
clonedKey = results.clonedKey;
shouldEvaluateAs("importedKey.extraProperty", "hi");
shouldEvaluateAs("importedKey.type", "secret");
shouldEvaluateAs("importedKey.extractable", extractable);
shouldEvaluateAs("importedKey.algorithm.name", "HMAC");
shouldEvaluateAs("importedKey.algorithm.length", importData.length * 8);
shouldEvaluateAs("importedKey.algorithm.hash.name", hashName);
shouldEvaluateAs("importedKey.usages.join(',')", keyUsages.join(","));
shouldBeTrue("importedKey != clonedKey");
shouldBeUndefined("clonedKey.extraProperty");
shouldEvaluateAs("clonedKey.type", "secret");
shouldEvaluateAs("clonedKey.extractable", extractable);
shouldEvaluateAs("clonedKey.algorithm.name", "HMAC");
shouldEvaluateAs("clonedKey.algorithm.length", importData.length * 8);
shouldEvaluateAs("clonedKey.algorithm.hash.name", hashName);
shouldEvaluateAs("clonedKey.usages.join(',')", keyUsages.join(","));
logSerializedKey(importedKey);
if (extractable)
bytesShouldMatchHexString("Cloned key exported data", keyData, clonedKeyData);
debug("");
});
}
var lastPromise = Promise.resolve(null);
kPossibleHashAlgorithms.forEach(function(hashName) {
kPossibleExtractable.forEach(function(extractable) {
kPossibleKeyUsages.forEach(function(keyUsages) {
kPossibleKeyData.forEach(function(keyData) {
lastPromise = lastPromise.then(runTest.bind(null, hashName, extractable, keyUsages, keyData));
});
});
});
});
lastPromise.then(finishJSTest, failAndFinishJSTest);
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<script src="../resources/js-test.js"></script>
<script src="resources/common.js"></script>
</head>
<body>
<p id="description"></p>
<div id="console"></div>
<script>
description("Tests structured cloning of RSA keys (with a hash)");
jsTestIsAsync = true;
// Tests the 48 permutations of keys generated by:
// kPossibleAlgorithms x kPossibleExtractable x kPossibleKeyUsages x kPossibleKeyData x kPossibleHashAlgorithms
//
// For practical reasons these tests are not exhaustive.
var kPossibleAlgorithms = ['RSASSA-PKCS1-v1_5'];
var kPossibleExtractable = [true, false];
var kPossibleKeyUsages = [[], ['sign'], ['verify'], ['sign', 'verify']];
var kPossibleHashAlgorithms = ['SHA-1', 'SHA-256', 'SHA-512'];
var kPossibleKeyData = [
{
modululusLengthBits: 1024,
publicExponent: "010001",
spkiData: "30819f300d06092a864886f70d010101050003818d0030818902818100b2" +
"89c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d" +
"96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09" +
"eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66" +
"d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f01" +
"7de4232a306a410203010001"
},
{
modululusLengthBits: 2048,
publicExponent: "010001",
spkiData: "30820122300d06092a864886f70d01010105000382010f003082010a0282" +
"010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7" +
"c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba9" +
"67062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c5" +
"30b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e" +
"53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7b" +
"d9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533" +
"e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f650" +
"59ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043" +
"dda68881c790f1517671fb7d198fca5ba97bef0203010001"
}
];
function runTest(algorithmName, hashName, extractable, keyUsages, keyData)
{
var importData = hexStringToUint8Array(keyData.spkiData);
var importAlgorithm = { name: algorithmName, hash: {name: hashName} };
var results = {};
return crypto.subtle.importKey('spki', importData, importAlgorithm, extractable, keyUsages).then(function(importedKey) {
results.importedKey = importedKey;
importedKey.extraProperty = 'hi';
return cloneKey(importedKey);
}).then(function(clonedKey) {
results.clonedKey = clonedKey;
if (extractable)
return crypto.subtle.exportKey('spki', clonedKey);
return null;
}).then(function(clonedKeyData) {
importedKey = results.importedKey;
clonedKey = results.clonedKey;
shouldEvaluateAs("importedKey.extraProperty", "hi");
shouldEvaluateAs("importedKey.type", "public");
shouldEvaluateAs("importedKey.extractable", extractable);
shouldEvaluateAs("importedKey.algorithm.name", algorithmName);
shouldEvaluateAs("importedKey.algorithm.modulusLength", keyData.modululusLengthBits);
bytesShouldMatchHexString("importedKey.algorithm.publicExponent", keyData.publicExponent, importedKey.algorithm.publicExponent);
shouldEvaluateAs("importedKey.algorithm.hash.name", hashName);
shouldEvaluateAs("importedKey.usages.join(',')", keyUsages.join(","));
shouldBeTrue("importedKey != clonedKey");
shouldBeUndefined("clonedKey.extraProperty");
shouldEvaluateAs("clonedKey.type", "public");
shouldEvaluateAs("clonedKey.extractable", extractable);
shouldEvaluateAs("clonedKey.algorithm.name", algorithmName);
shouldEvaluateAs("clonedKey.algorithm.modulusLength", keyData.modululusLengthBits);
bytesShouldMatchHexString("clonedKey.algorithm.publicExponent", keyData.publicExponent, clonedKey.algorithm.publicExponent);
shouldEvaluateAs("clonedKey.algorithm.hash.name", hashName);
shouldEvaluateAs("clonedKey.usages.join(',')", keyUsages.join(","));
logSerializedKey(importedKey);
if (extractable)
bytesShouldMatchHexString("Cloned key exported data", keyData.spkiData, clonedKeyData);
debug("");
});
}
var lastPromise = Promise.resolve(null);
kPossibleAlgorithms.forEach(function(algorithmName) {
kPossibleExtractable.forEach(function(extractable) {
kPossibleKeyUsages.forEach(function(keyUsages) {
kPossibleKeyData.forEach(function(keyData) {
kPossibleHashAlgorithms.forEach(function(hashName) {
lastPromise = lastPromise.then(runTest.bind(null, algorithmName, hashName, extractable, keyUsages, keyData));
});
});
});
});
});
lastPromise.then(finishJSTest, failAndFinishJSTest);
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<script src="../resources/js-test.js"></script>
<script src="resources/common.js"></script>
</head>
<body>
<p id="description"></p>
<div id="console"></div>
<script>
description("Tests structured cloning of RSA keys (without a hash)");
jsTestIsAsync = true;
// Tests the 16 permutations of keys generated by:
// kPossibleAlgorithms x kPossibleExtractable x kPossibleKeyUsages x kPossibleKeyData
//
// For practical reasons these tests are not exhaustive.
var kPossibleAlgorithms = ['RSAES-PKCS1-v1_5'];
var kPossibleExtractable = [true, false];
var kPossibleKeyUsages = [[], ['encrypt'], ['decrypt', 'wrapKey'], ['encrypt', 'wrapKey', 'unwrapKey']];
var kPossibleKeyData = [
{
modululusLengthBits: 1024,
publicExponent: "010001",
spkiData: "30819f300d06092a864886f70d010101050003818d0030818902818100b2" +
"89c62ecc3ddf64154817203439eaa0dc07a65954429a7b6098a77235673d" +
"96df1f06bd3c1ae73990867199e678bf95b3728fcd4686136e6ee9dd4c09" +
"eb490eb7cb953c388668b759263f61d6a7dfcabf27b5c9d6972455b12b66" +
"d483843286d6b871f35f912a773c97c1932255fcee05ce7b8af213879f01" +
"7de4232a306a410203010001"
},
{
modululusLengthBits: 2048,
publicExponent: "010001",
spkiData: "30820122300d06092a864886f70d01010105000382010f003082010a0282" +
"010100b4c8b631194aef0154b1479ab7a534b60ca878981108680f0ae6b7" +
"c88cb6010f6dbf9f665646208410575cb923b26bf874a24b4cd801c9bba9" +
"67062ae506cdcf307add4380d0d93077a4c1f0fc06d498dc729f811335c5" +
"30b90fe9bf9f3979ccec050a48e8923045b19368e1e89ea4157538e8059e" +
"53320f47c155f1741310a93ed153a7f3af2d46c388b95d82518527a02b7b" +
"d9ab7edc4bcb737677f679c5c6de5e1ebed3a29d6b99b8eace2d59ceb533" +
"e001cf39c5671495d51d3ee34406ea4fdb0c626dee68da256b8a12f9f650" +
"59ccc85a2190ce1385152d62785e00cae702e77c4c597b86a6268adbf043" +
"dda68881c790f1517671fb7d198fca5ba97bef0203010001"
}
];
function runTest(algorithmName, extractable, keyUsages, keyData)
{
var importData = hexStringToUint8Array(keyData.spkiData);
var importAlgorithm = { name: algorithmName };
var results = {};
return crypto.subtle.importKey('spki', importData, importAlgorithm, extractable, keyUsages).then(function(importedKey) {
results.importedKey = importedKey;
importedKey.extraProperty = 'hi';
return cloneKey(importedKey);
}).then(function(clonedKey) {
results.clonedKey = clonedKey;
if (extractable)
return crypto.subtle.exportKey('spki', clonedKey);
return null;
}).then(function(clonedKeyData) {
importedKey = results.importedKey;
clonedKey = results.clonedKey;
shouldEvaluateAs("importedKey.extraProperty", "hi");
shouldEvaluateAs("importedKey.type", "public");
shouldEvaluateAs("importedKey.extractable", extractable);
shouldEvaluateAs("importedKey.algorithm.name", algorithmName);
shouldEvaluateAs("importedKey.algorithm.modulusLength", keyData.modululusLengthBits);
bytesShouldMatchHexString("importedKey.algorithm.publicExponent", keyData.publicExponent, importedKey.algorithm.publicExponent);
shouldBeUndefined("importedKey.algorithm.hash");
shouldEvaluateAs("importedKey.usages.join(',')", keyUsages.join(","));
shouldBeTrue("importedKey != clonedKey");
shouldBeUndefined("clonedKey.extraProperty");
shouldEvaluateAs("clonedKey.type", "public");
shouldEvaluateAs("clonedKey.extractable", extractable);
shouldEvaluateAs("clonedKey.algorithm.name", algorithmName);
shouldEvaluateAs("clonedKey.algorithm.modulusLength", keyData.modululusLengthBits);
bytesShouldMatchHexString("clonedKey.algorithm.publicExponent", keyData.publicExponent, clonedKey.algorithm.publicExponent);
shouldBeUndefined("clonedKey.algorithm.hash");
shouldEvaluateAs("clonedKey.usages.join(',')", keyUsages.join(","));
logSerializedKey(importedKey);
if (extractable)
bytesShouldMatchHexString("Cloned key exported data", keyData.spkiData, clonedKeyData);
debug("");
});
}
var lastPromise = Promise.resolve(null);
kPossibleAlgorithms.forEach(function(algorithmName) {
kPossibleExtractable.forEach(function(extractable) {
kPossibleKeyUsages.forEach(function(keyUsages) {
kPossibleKeyData.forEach(function(keyData) {
lastPromise = lastPromise.then(runTest.bind(null, algorithmName, extractable, keyUsages, keyData));
});
});
});
});
lastPromise.then(finishJSTest, failAndFinishJSTest);
</script>
</body>
</html>
......@@ -158,3 +158,39 @@ function shouldAcceptPromise(code)
addTask(promise.then(acceptCallback, rejectCallback));
}
// Returns a Promise for the cloned key.
function cloneKey(key)
{
// Sending an object through a MessagePort implicitly clones it.
// Use a single MessageChannel so requests complete in FIFO order.
var self = cloneKey;
if (!self.channel) {
self.channel = new MessageChannel();
self.callbacks = [];
self.channel.port1.addEventListener('message', function(e) {
var callback = self.callbacks.shift();
callback(e.data);
}, false);
self.channel.port1.start();
}
return new Promise(function(resolve, reject) {
self.callbacks.push(resolve);
self.channel.port2.postMessage(key);
});
}
// Logging the serialized format ensures that if it changes it will break tests.
function logSerializedKey(o)
{
if (internals)
debug("Serialized key bytes: " + bytesToHexString(internals.serializeObject(o)));
}
function shouldEvaluateAs(actual, expectedValue)
{
if (typeof expectedValue == "string")
return shouldBeEqualToString(actual, expectedValue);
return shouldEvaluateTo(actual, expectedValue);
}
var kSerializedScriptValueVersion = 4;
var kSerializedScriptValueVersion = 5;
function forVersion(version, values) {
var versionTag = 0xff;
......
......@@ -60,7 +60,8 @@ public:
// Version 2: Added StringUCharTag for UChar v8 strings.
// Version 3: Switched to using uuids as blob data identifiers.
// Version 4: Extended File serialization to be complete.
static const uint32_t wireFormatVersion = 4;
// Version 5: Added CryptoKeyTag for Key objects.
static const uint32_t wireFormatVersion = 5;
~SerializedScriptValue();
......
......@@ -171,7 +171,7 @@ private:
AlgorithmNameToIdMap m_algorithmNameToId;
// Algorithm ID to information.
AlgorithmInfo m_algorithms[blink::NumberOfWebCryptoAlgorithmId];
AlgorithmInfo m_algorithms[blink::WebCryptoAlgorithmIdLast + 1];
};
AlgorithmRegistry& AlgorithmRegistry::instance()
......
......@@ -163,6 +163,27 @@ const WebCryptoRsaHashedKeyGenParams* WebCryptoAlgorithm::rsaHashedKeyGenParams(
return 0;
}
bool WebCryptoAlgorithm::isHash(WebCryptoAlgorithmId id)
{
switch (id) {
case WebCryptoAlgorithmIdSha1:
case WebCryptoAlgorithmIdSha256:
case WebCryptoAlgorithmIdSha384:
case WebCryptoAlgorithmIdSha512:
return true;
case WebCryptoAlgorithmIdAesCbc:
case WebCryptoAlgorithmIdHmac:
case WebCryptoAlgorithmIdRsaSsaPkcs1v1_5:
case WebCryptoAlgorithmIdRsaEsPkcs1v1_5:
case WebCryptoAlgorithmIdAesGcm:
case WebCryptoAlgorithmIdRsaOaep:
case WebCryptoAlgorithmIdAesCtr:
case WebCryptoAlgorithmIdAesKw:
break;
}
return false;
}
void WebCryptoAlgorithm::assign(const WebCryptoAlgorithm& other)
{
m_private = other.m_private;
......
......@@ -36,6 +36,12 @@
namespace blink {
// FIXME: Remove the need for this.
WebCryptoAlgorithm createHash(WebCryptoAlgorithmId hash)
{
return WebCryptoAlgorithm::adoptParamsAndCreate(hash, 0);
}
class WebCryptoKeyAlgorithmPrivate : public ThreadSafeRefCounted<WebCryptoKeyAlgorithmPrivate> {
public:
WebCryptoKeyAlgorithmPrivate(WebCryptoAlgorithmId id, PassOwnPtr<WebCryptoKeyAlgorithmParams> params)
......@@ -58,6 +64,36 @@ WebCryptoKeyAlgorithm WebCryptoKeyAlgorithm::adoptParamsAndCreate(WebCryptoAlgor
return WebCryptoKeyAlgorithm(id, adoptPtr(params));
}
WebCryptoKeyAlgorithm WebCryptoKeyAlgorithm::createAes(WebCryptoAlgorithmId id, unsigned short keyLengthBits)
{
// FIXME: Verify that id is an AES algorithm.
// FIXME: Move this somewhere more general.
if (keyLengthBits != 128 && keyLengthBits != 192 && keyLengthBits != 256)
return WebCryptoKeyAlgorithm();
return WebCryptoKeyAlgorithm(id, adoptPtr(new WebCryptoAesKeyAlgorithmParams(keyLengthBits)));
}
WebCryptoKeyAlgorithm WebCryptoKeyAlgorithm::createHmac(WebCryptoAlgorithmId hash, unsigned keyLengthBits)
{
if (!WebCryptoAlgorithm::isHash(hash))
return WebCryptoKeyAlgorithm();
return WebCryptoKeyAlgorithm(WebCryptoAlgorithmIdHmac, adoptPtr(new WebCryptoHmacKeyAlgorithmParams(createHash(hash), keyLengthBits)));
}
WebCryptoKeyAlgorithm WebCryptoKeyAlgorithm::createRsa(WebCryptoAlgorithmId id, unsigned modulusLengthBits, const unsigned char* publicExponent, unsigned publicExponentSize)
{
// FIXME: Verify that id is an RSA algorithm without a hash
return WebCryptoKeyAlgorithm(id, adoptPtr(new WebCryptoRsaKeyAlgorithmParams(modulusLengthBits, publicExponent, publicExponentSize)));
}
WebCryptoKeyAlgorithm WebCryptoKeyAlgorithm::createRsaHashed(WebCryptoAlgorithmId id, unsigned modulusLengthBits, const unsigned char* publicExponent, unsigned publicExponentSize, WebCryptoAlgorithmId hash)
{
// FIXME: Verify that id is an RSA algorithm which expects a hash
if (!WebCryptoAlgorithm::isHash(hash))
return WebCryptoKeyAlgorithm();
return WebCryptoKeyAlgorithm(id, adoptPtr(new WebCryptoRsaHashedKeyAlgorithmParams(modulusLengthBits, publicExponent, publicExponentSize, createHash(hash))));
}
bool WebCryptoKeyAlgorithm::isNull() const
{
return m_private.isNull();
......
......@@ -35,6 +35,7 @@
#include "WebCryptoAlgorithm.h"
#include "WebCryptoKey.h"
#include "WebPrivatePtr.h"
#include "WebVector.h"
namespace WebCore { class CryptoResult; }
......@@ -182,6 +183,54 @@ public:
// and is not part of the WebCrypto standard.
virtual bool digestSynchronous(const WebCryptoAlgorithmId algorithmId, const unsigned char* data, unsigned dataSize, WebArrayBuffer& result) { return false; }
// -----------------------
// Structured clone
// -----------------------
//
// deserializeKeyForClone() and serializeKeyForClone() are used for
// implementing structured cloning of WebCryptoKey.
//
// Blink is responsible for saving and restoring all of the attributes of
// WebCryptoKey EXCEPT for the actual key data:
//
// In other words, Blink takes care of serializing:
// * Key usages
// * Key extractability
// * Key algorithm
// * Key type (public, private, secret)
//
// The embedder is responsible for saving the key data itself.
//
// Visibility of the serialized key data:
//
// The serialized key data will NOT be visible to web pages. So if the
// serialized format were to include key bytes as plain text, this wouldn't
// make it available to web pages.
//
// Longevity of the key data:
//
// The serialized key data is intended to be long lived (years) and MUST
// be using a stable format. For instance a key might be persisted to
// IndexedDB and should be able to be deserialized correctly in the
// future.
//
// Error handling and asynchronous completion:
//
// Serialization/deserialization must complete synchronously, and will
// block the JavaScript thread.
//
// The only reasons to fail serialization/deserialization are:
// * Key serialization not yet implemented
// * The bytes to deserialize were corrupted
// Creates a new key given key data which was written using
// serializeKeyForClone(). Returns true on success.
virtual bool deserializeKeyForClone(const WebCryptoKeyAlgorithm&, WebCryptoKeyType, bool extractable, WebCryptoKeyUsageMask, const unsigned char* keyData, unsigned keyDataSize, WebCryptoKey&) { return false; }
// Writes the key data into the given WebVector.
// Returns true on success.
virtual bool serializeKeyForClone(const WebCryptoKey&, WebVector<unsigned char>&) { return false; }
protected:
virtual ~WebCrypto() { }
};
......
......@@ -54,7 +54,7 @@ enum WebCryptoAlgorithmId {
WebCryptoAlgorithmIdAesCtr,
WebCryptoAlgorithmIdAesKw,
#if INSIDE_BLINK
NumberOfWebCryptoAlgorithmId,
WebCryptoAlgorithmIdLast = WebCryptoAlgorithmIdAesKw,
#endif
};
......@@ -131,6 +131,9 @@ public:
BLINK_PLATFORM_EXPORT const WebCryptoRsaHashedImportParams* rsaHashedImportParams() const;
BLINK_PLATFORM_EXPORT const WebCryptoRsaHashedKeyGenParams* rsaHashedKeyGenParams() const;
// Returns true if the provided algorithm ID is for a hash (in other words, SHA-*)
BLINK_PLATFORM_EXPORT static bool isHash(WebCryptoAlgorithmId);
private:
BLINK_PLATFORM_EXPORT void assign(const WebCryptoAlgorithm& other);
BLINK_PLATFORM_EXPORT void reset();
......
......@@ -86,11 +86,6 @@ class WebCryptoKeyHandle;
//
// If WebCryptoKey "isNull()" then it is invalid to call any of the other
// methods on it (other than destruction, assignment, or isNull()).
//
// FIXME: Define the interface to use for structured clone.
// Cloning across a process boundary will need serialization,
// however cloning for in-process workers could just share the same
// (threadsafe) handle.
class WebCryptoKey {
public:
~WebCryptoKey() { reset(); }
......
......@@ -56,8 +56,14 @@ public:
BLINK_PLATFORM_EXPORT WebCryptoKeyAlgorithm(WebCryptoAlgorithmId, PassOwnPtr<WebCryptoKeyAlgorithmParams>);
#endif
// FIXME: Delete this in favor of the create*() functions.
BLINK_PLATFORM_EXPORT static WebCryptoKeyAlgorithm adoptParamsAndCreate(WebCryptoAlgorithmId, WebCryptoKeyAlgorithmParams*);
BLINK_PLATFORM_EXPORT static WebCryptoKeyAlgorithm createAes(WebCryptoAlgorithmId, unsigned short keyLengthBits);
BLINK_PLATFORM_EXPORT static WebCryptoKeyAlgorithm createHmac(WebCryptoAlgorithmId hash, unsigned keyLengthBits);
BLINK_PLATFORM_EXPORT static WebCryptoKeyAlgorithm createRsa(WebCryptoAlgorithmId, unsigned modulusLengthBits, const unsigned char* publicExponent, unsigned publicExponentSize);
BLINK_PLATFORM_EXPORT static WebCryptoKeyAlgorithm createRsaHashed(WebCryptoAlgorithmId, unsigned modulusLengthBits, const unsigned char* publicExponent, unsigned publicExponentSize, WebCryptoAlgorithmId hash);
~WebCryptoKeyAlgorithm() { reset(); }
WebCryptoKeyAlgorithm(const WebCryptoKeyAlgorithm& other) { assign(other); }
......
......@@ -38,7 +38,7 @@ namespace blink {
// Embedders may serialize this as out-of-band metadata along with
// collections of serialized data so that version skew can be detected
// before deserializing individual values.
const unsigned kSerializedScriptValueVersion = 4;
const unsigned kSerializedScriptValueVersion = 5;
} // namespace blink
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment