Index: third_party/WebKit/LayoutTests/external/wpt/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.js |
diff --git a/third_party/WebKit/LayoutTests/external/wpt/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.js b/third_party/WebKit/LayoutTests/external/wpt/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.js |
deleted file mode 100644 |
index d584c25f81e517051adff7ffc4c469d2bee1fe3e..0000000000000000000000000000000000000000 |
--- a/third_party/WebKit/LayoutTests/external/wpt/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.js |
+++ /dev/null |
@@ -1,516 +0,0 @@ |
-// Tests for wrapKey and unwrapKey round tripping |
- |
-function run_test() { |
- var subtle = self.crypto.subtle; |
- |
- var wrappers = []; // Things we wrap (and upwrap) keys with |
- var keys = []; // Things to wrap and unwrap |
- var ecdhPeerKey; // ECDH peer public key needed for non-extractable ECDH key comparison |
- |
- // Generate all the keys needed, then iterate over all combinations |
- // to test wrapping and unwrapping. |
- Promise.all([generateWrappingKeys(), generateKeysToWrap(), generateEcdhPeerKey()]) |
- .then(function(results) { |
- var promises = []; |
- wrappers.forEach(function(wrapper) { |
- keys.forEach(function(key) { |
- promises.push(testWrapping(wrapper, key)); |
- }) |
- }); |
- return Promise.all(promises); |
- }, function(err) { |
- promise_test(function(test) { |
- assert_unreached("A key failed to generate: " + err.name + ": " + err.message) |
- }, "Could not run all tests") |
- }) |
- .then(function() { |
- done(); |
- }, function(err) { |
- promise_test(function(test) { |
- assert_unreached("A test failed to run: " + err.name + ": " + err.message) |
- }, "Could not run all tests") |
- }); |
- |
- |
- function generateWrappingKeys() { |
- // There are five algorithms that can be used for wrapKey/unwrapKey. |
- // Generate one key with typical parameters for each kind. |
- // |
- // Note: we don't need cryptographically strong parameters for things |
- // like IV - just any legal value will do. |
- var parameters = [ |
- { |
- name: "RSA-OAEP", |
- generateParameters: {name: "RSA-OAEP", modulusLength: 4096, publicExponent: new Uint8Array([1,0,1]), hash: "SHA-256"}, |
- wrapParameters: {name: "RSA-OAEP", label: new Uint8Array(8)} |
- }, |
- { |
- name: "AES-CTR", |
- generateParameters: {name: "AES-CTR", length: 128}, |
- wrapParameters: {name: "AES-CTR", counter: new Uint8Array(16), length: 64} |
- }, |
- { |
- name: "AES-CBC", |
- generateParameters: {name: "AES-CBC", length: 128}, |
- wrapParameters: {name: "AES-CBC", iv: new Uint8Array(16)} |
- }, |
- { |
- name: "AES-GCM", |
- generateParameters: {name: "AES-GCM", length: 128}, |
- wrapParameters: {name: "AES-GCM", iv: new Uint8Array(16), additionalData: new Uint8Array(16), tagLength: 64} |
- }, |
- { |
- name: "AES-KW", |
- generateParameters: {name: "AES-KW", length: 128}, |
- wrapParameters: {name: "AES-KW"} |
- } |
- ]; |
- |
- return Promise.all(parameters.map(function(params) { |
- return subtle.generateKey(params.generateParameters, true, ["wrapKey", "unwrapKey"]) |
- .then(function(key) { |
- var wrapper; |
- if (params.name === "RSA-OAEP") { // we have a key pair, not just a key |
- wrapper = {wrappingKey: key.publicKey, unwrappingKey: key.privateKey, parameters: params}; |
- } else { |
- wrapper = {wrappingKey: key, unwrappingKey: key, parameters: params}; |
- } |
- wrappers.push(wrapper); |
- return true; |
- }) |
- })); |
- } |
- |
- |
- function generateKeysToWrap() { |
- var parameters = [ |
- {algorithm: {name: "RSASSA-PKCS1-v1_5", modulusLength: 1024, publicExponent: new Uint8Array([1,0,1]), hash: "SHA-256"}, privateUsages: ["sign"], publicUsages: ["verify"]}, |
- {algorithm: {name: "RSA-PSS", modulusLength: 1024, publicExponent: new Uint8Array([1,0,1]), hash: "SHA-256"}, privateUsages: ["sign"], publicUsages: ["verify"]}, |
- {algorithm: {name: "RSA-OAEP", modulusLength: 1024, publicExponent: new Uint8Array([1,0,1]), hash: "SHA-256"}, privateUsages: ["decrypt"], publicUsages: ["encrypt"]}, |
- {algorithm: {name: "ECDSA", namedCurve: "P-256"}, privateUsages: ["sign"], publicUsages: ["verify"]}, |
- {algorithm: {name: "ECDH", namedCurve: "P-256"}, privateUsages: ["deriveBits"], publicUsages: []}, |
- {algorithm: {name: "AES-CTR", length: 128}, usages: ["encrypt", "decrypt"]}, |
- {algorithm: {name: "AES-CBC", length: 128}, usages: ["encrypt", "decrypt"]}, |
- {algorithm: {name: "AES-GCM", length: 128}, usages: ["encrypt", "decrypt"]}, |
- {algorithm: {name: "AES-KW", length: 128}, usages: ["wrapKey", "unwrapKey"]}, |
- {algorithm: {name: "HMAC", length: 128, hash: "SHA-256"}, usages: ["sign", "verify"]} |
- ]; |
- |
- return Promise.all(parameters.map(function(params) { |
- var usages; |
- if ("usages" in params) { |
- usages = params.usages; |
- } else { |
- usages = params.publicUsages.concat(params.privateUsages); |
- } |
- |
- return subtle.generateKey(params.algorithm, true, usages) |
- .then(function(result) { |
- if (result.constructor === CryptoKey) { |
- keys.push({name: params.algorithm.name, algorithm: params.algorithm, usages: params.usages, key: result}); |
- } else { |
- keys.push({name: params.algorithm.name + " public key", algorithm: params.algorithm, usages: params.publicUsages, key: result.publicKey}); |
- keys.push({name: params.algorithm.name + " private key", algorithm: params.algorithm, usages: params.privateUsages, key: result.privateKey}); |
- } |
- return true; |
- }); |
- })); |
- } |
- |
- function generateEcdhPeerKey() { |
- return subtle.generateKey({name: "ECDH", namedCurve: "P-256"},true,["deriveBits"]) |
- .then(function(result){ |
- ecdhPeerKey = result.publicKey; |
- }); |
- } |
- |
- // Can we successfully "round-trip" (wrap, then unwrap, a key)? |
- function testWrapping(wrapper, toWrap) { |
- var formats; |
- |
- if (toWrap.name.includes("private")) { |
- formats = ["pkcs8", "jwk"]; |
- } else if (toWrap.name.includes("public")) { |
- formats = ["spki", "jwk"] |
- } else { |
- formats = ["raw", "jwk"] |
- } |
- |
- return Promise.all(formats.map(function(fmt) { |
- var originalExport; |
- return subtle.exportKey(fmt, toWrap.key).then(function(exportedKey) { |
- originalExport = exportedKey; |
- if (wrappingIsPossible(originalExport, wrapper.parameters.name)) { |
- promise_test(function(test) { |
- return subtle.wrapKey(fmt, toWrap.key, wrapper.wrappingKey, wrapper.parameters.wrapParameters) |
- .then(function(wrappedResult) { |
- return subtle.unwrapKey(fmt, wrappedResult, wrapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, true, toWrap.usages); |
- }).then(function(unwrappedResult) { |
- assert_true(unwrappedResult.extractable, "Unwrapped result is extractable"); |
- return subtle.exportKey(fmt, unwrappedResult) |
- }).then(function(roundTripExport) { |
- assert_true(equalExport(originalExport, roundTripExport), "Post-wrap export matches original export"); |
- }, function(err) { |
- assert_unreached("Round trip for extractable key threw an error - " + err.name + ': "' + err.message + '"'); |
- }); |
- }, "Can wrap and unwrap " + toWrap.name + " keys using " + fmt + " and " + wrapper.parameters.name); |
- |
- if (canCompareNonExtractableKeys(toWrap.key)) { |
- promise_test(function(test){ |
- return subtle.wrapKey(fmt, toWrap.key, wrapper.wrappingKey, wrapper.parameters.wrapParameters) |
- .then(function(wrappedResult) { |
- return subtle.unwrapKey(fmt, wrappedResult, wrapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, false, toWrap.usages); |
- }).then(function(unwrappedResult){ |
- assert_false(unwrappedResult.extractable, "Unwrapped result is non-extractable"); |
- return equalKeys(toWrap.key, unwrappedResult); |
- }).then(function(result){ |
- assert_true(result, "Unwrapped key matches original"); |
- }).catch(function(err){ |
- assert_unreached("Round trip for key unwrapped non-extractable threw an error - " + err.name + ': "' + err.message + '"'); |
- }); |
- }, "Can wrap and unwrap " + toWrap.name + " keys as non-extractable using " + fmt + " and " + wrapper.parameters.name); |
- |
- if (fmt === "jwk") { |
- promise_test(function(test){ |
- var wrappedKey; |
- return wrapAsNonExtractableJwk(toWrap.key,wrapper).then(function(wrappedResult){ |
- wrappedKey = wrappedResult; |
- return subtle.unwrapKey("jwk", wrappedKey, wrapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, false, toWrap.usages); |
- }).then(function(unwrappedResult){ |
- assert_false(unwrappedResult.extractable, "Unwrapped key is non-extractable"); |
- return equalKeys(toWrap.key,unwrappedResult); |
- }).then(function(result){ |
- assert_true(result, "Unwrapped key matches original"); |
- }).catch(function(err){ |
- assert_unreached("Round trip for non-extractable key threw an error - " + err.name + ': "' + err.message + '"'); |
- }).then(function(){ |
- return subtle.unwrapKey("jwk", wrappedKey, wrapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, true, toWrap.usages); |
- }).then(function(unwrappedResult){ |
- assert_unreached("Unwrapping a non-extractable JWK as extractable should fail"); |
- }).catch(function(err){ |
- assert_equals(err.name, "DataError", "Unwrapping a non-extractable JWK as extractable fails with DataError"); |
- }); |
- }, "Can unwrap " + toWrap.name + " non-extractable keys using jwk and " + wrapper.parameters.name); |
- } |
- } |
- } |
- }); |
- })); |
- } |
- |
- // Implement key wrapping by hand to wrap a key as non-extractable JWK |
- function wrapAsNonExtractableJwk(key, wrapper){ |
- var wrappingKey = wrapper.wrappingKey, |
- encryptKey; |
- |
- return subtle.exportKey("jwk",wrappingKey) |
- .then(function(jwkWrappingKey){ |
- // Update the key generation parameters to work as key import parameters |
- var params = Object.create(wrapper.parameters.generateParameters); |
- if(params.name === "AES-KW") { |
- params.name = "AES-CBC"; |
- jwkWrappingKey.alg = "A"+params.length+"CBC"; |
- } else if (params.name === "RSA-OAEP") { |
- params.modulusLength = undefined; |
- params.publicExponent = undefined; |
- } |
- jwkWrappingKey.key_ops = ["encrypt"]; |
- return subtle.importKey("jwk", jwkWrappingKey, params, true, ["encrypt"]); |
- }).then(function(importedWrappingKey){ |
- encryptKey = importedWrappingKey; |
- return subtle.exportKey("jwk",key); |
- }).then(function(exportedKey){ |
- exportedKey.ext = false; |
- var jwk = JSON.stringify(exportedKey) |
- if (wrappingKey.algorithm.name === "AES-KW") { |
- return aeskw(encryptKey, str2ab(jwk.slice(0,-1) + " ".repeat(jwk.length%8 ? 8-jwk.length%8 : 0) + "}")); |
- } else { |
- return subtle.encrypt(wrapper.parameters.wrapParameters,encryptKey,str2ab(jwk)); |
- } |
- }); |
- } |
- |
- |
- // RSA-OAEP can only wrap relatively small payloads. AES-KW can only |
- // wrap payloads a multiple of 8 bytes long. |
- // |
- // Note that JWK payloads will be converted to ArrayBuffer for wrapping, |
- // and should automatically be padded if needed for AES-KW. |
- function wrappingIsPossible(exportedKey, algorithmName) { |
- if ("byteLength" in exportedKey && algorithmName === "AES-KW") { |
- return exportedKey.byteLength % 8 === 0; |
- } |
- |
- if ("byteLength" in exportedKey && algorithmName === "RSA-OAEP") { |
- // RSA-OAEP can only encrypt payloads with lengths shorter |
- // than modulusLength - 2*hashLength - 1 bytes long. For |
- // a 4096 bit modulus and SHA-256, that comes to |
- // 4096/8 - 2*(256/8) - 1 = 512 - 2*32 - 1 = 447 bytes. |
- return exportedKey.byteLength <= 446; |
- } |
- |
- if ("kty" in exportedKey && algorithmName === "RSA-OAEP") { |
- return JSON.stringify(exportedKey).length <= 478; |
- } |
- |
- return true; |
- } |
- |
- |
- // Helper methods follow: |
- |
- // Are two exported keys equal |
- function equalExport(originalExport, roundTripExport) { |
- if ("byteLength" in originalExport) { |
- return equalBuffers(originalExport, roundTripExport); |
- } else { |
- return equalJwk(originalExport, roundTripExport); |
- } |
- } |
- |
- // Are two array buffers the same? |
- function equalBuffers(a, b) { |
- if (a.byteLength !== b.byteLength) { |
- return false; |
- } |
- |
- var aBytes = new Uint8Array(a); |
- var bBytes = new Uint8Array(b); |
- |
- for (var i=0; i<a.byteLength; i++) { |
- if (aBytes[i] !== bBytes[i]) { |
- return false; |
- } |
- } |
- |
- return true; |
- } |
- |
- // Are two Jwk objects "the same"? That is, does the object returned include |
- // matching values for each property that was expected? It's okay if the |
- // returned object has extra methods; they aren't checked. |
- function equalJwk(expected, got) { |
- var fields = Object.keys(expected); |
- var fieldName; |
- |
- for(var i=0; i<fields.length; i++) { |
- fieldName = fields[i]; |
- if (!(fieldName in got)) { |
- return false; |
- } |
- if (objectToString(expected[fieldName]) !== objectToString(got[fieldName])) { |
- return false; |
- } |
- } |
- |
- return true; |
- } |
- |
- // Character representation of any object we may use as a parameter. |
- function objectToString(obj) { |
- var keyValuePairs = []; |
- |
- if (Array.isArray(obj)) { |
- return "[" + obj.map(function(elem){return objectToString(elem);}).join(", ") + "]"; |
- } else if (typeof obj === "object") { |
- Object.keys(obj).sort().forEach(function(keyName) { |
- keyValuePairs.push(keyName + ": " + objectToString(obj[keyName])); |
- }); |
- return "{" + keyValuePairs.join(", ") + "}"; |
- } else if (typeof obj === "undefined") { |
- return "undefined"; |
- } else { |
- return obj.toString(); |
- } |
- |
- var keyValuePairs = []; |
- |
- Object.keys(obj).sort().forEach(function(keyName) { |
- var value = obj[keyName]; |
- if (typeof value === "object") { |
- value = objectToString(value); |
- } else if (typeof value === "array") { |
- value = "[" + value.map(function(elem){return objectToString(elem);}).join(", ") + "]"; |
- } else { |
- value = value.toString(); |
- } |
- |
- keyValuePairs.push(keyName + ": " + value); |
- }); |
- |
- return "{" + keyValuePairs.join(", ") + "}"; |
- } |
- |
- // Can we compare key values by using them |
- function canCompareNonExtractableKeys(key){ |
- if (key.usages.indexOf("decrypt") !== -1) { |
- return true; |
- } |
- if (key.usages.indexOf("sign") !== -1) { |
- return true; |
- } |
- if (key.usages.indexOf("wrapKey") !== -1) { |
- return true; |
- } |
- if (key.usages.indexOf("deriveBits") !== -1) { |
- return true; |
- } |
- return false; |
- } |
- |
- // Compare two keys by using them (works for non-extractable keys) |
- function equalKeys(expected, got){ |
- if ( expected.algorithm.name !== got.algorithm.name ) { |
- return Promise.resolve(false); |
- } |
- |
- var cryptParams, signParams, wrapParams, deriveParams; |
- switch(expected.algorithm.name){ |
- case "AES-CTR" : |
- cryptParams = {name: "AES-CTR", counter: new Uint8Array(16), length: 64}; |
- break; |
- case "AES-CBC" : |
- cryptParams = {name: "AES-CBC", iv: new Uint8Array(16) }; |
- break; |
- case "AES-GCM" : |
- cryptParams = {name: "AES-GCM", iv: new Uint8Array(16) }; |
- break; |
- case "RSA-OAEP" : |
- cryptParams = {name: "RSA-OAEP", label: new Uint8Array(8) }; |
- break; |
- case "RSASSA-PKCS1-v1_5" : |
- signParams = {name: "RSASSA-PKCS1-v1_5"}; |
- break; |
- case "RSA-PSS" : |
- signParams = {name: "RSA-PSS", saltLength: 32 }; |
- break; |
- case "ECDSA" : |
- signParams = {name: "ECDSA", hash: "SHA-256"}; |
- break; |
- case "HMAC" : |
- signParams = {name: "HMAC"}; |
- break; |
- case "AES-KW" : |
- wrapParams = {name: "AES-KW"}; |
- break; |
- case "ECDH" : |
- deriveParams = {name: "ECDH", public: ecdhPeerKey}; |
- break; |
- default: |
- throw new Error("Unsupported algorithm for key comparison"); |
- } |
- |
- if (cryptParams) { |
- return subtle.exportKey("jwk",expected) |
- .then(function(jwkExpectedKey){ |
- if (expected.algorithm.name === "RSA-OAEP") { |
- ["d","p","q","dp","dq","qi","oth"].forEach(function(field){ delete jwkExpectedKey[field]; }); |
- } |
- jwkExpectedKey.key_ops = ["encrypt"]; |
- return subtle.importKey("jwk", jwkExpectedKey, expected.algorithm, true, ["encrypt"]); |
- }).then(function(expectedEncryptKey){ |
- return subtle.encrypt(cryptParams, expectedEncryptKey, new Uint8Array(32)); |
- }).then(function(encryptedData){ |
- return subtle.decrypt(cryptParams, got, encryptedData); |
- }).then(function(decryptedData){ |
- var result = new Uint8Array(decryptedData); |
- return !result.some(x => x); |
- }); |
- } else if (signParams) { |
- var verifyKey; |
- return subtle.exportKey("jwk",expected) |
- .then(function(jwkExpectedKey){ |
- if (expected.algorithm.name === "RSA-PSS" || expected.algorithm.name === "RSASSA-PKCS1-v1_5") { |
- ["d","p","q","dp","dq","qi","oth"].forEach(function(field){ delete jwkExpectedKey[field]; }); |
- } |
- if (expected.algorithm.name === "ECDSA") { |
- delete jwkExpectedKey["d"]; |
- } |
- jwkExpectedKey.key_ops = ["verify"]; |
- return subtle.importKey("jwk", jwkExpectedKey, expected.algorithm, true, ["verify"]); |
- }).then(function(expectedVerifyKey){ |
- verifyKey = expectedVerifyKey; |
- return subtle.sign(signParams, got, new Uint8Array(32)); |
- }).then(function(signature){ |
- return subtle.verify(signParams, verifyKey, signature, new Uint8Array(32)); |
- }); |
- } else if (wrapParams) { |
- var aKeyToWrap, wrappedWithExpected; |
- return subtle.importKey("raw", new Uint8Array(16), "AES-CBC", true, ["encrypt"]) |
- .then(function(key){ |
- aKeyToWrap = key; |
- return subtle.wrapKey("raw", aKeyToWrap, expected, wrapParams); |
- }).then(function(wrapResult){ |
- wrappedWithExpected = Array.from((new Uint8Array(wrapResult)).values()); |
- return subtle.wrapKey("raw", aKeyToWrap, got, wrapParams); |
- }).then(function(wrapResult){ |
- var wrappedWithGot = Array.from((new Uint8Array(wrapResult)).values()); |
- return wrappedWithGot.every((x,i) => x === wrappedWithExpected[i]); |
- }); |
- } else { |
- var expectedDerivedBits; |
- return subtle.deriveBits(deriveParams, expected, 128) |
- .then(function(result){ |
- expectedDerivedBits = Array.from((new Uint8Array(result)).values()); |
- return subtle.deriveBits(deriveParams, got, 128); |
- }).then(function(result){ |
- var gotDerivedBits = Array.from((new Uint8Array(result)).values()); |
- return gotDerivedBits.every((x,i) => x === expectedDerivedBits[i]); |
- }); |
- } |
- } |
- |
- // Raw AES encryption |
- function aes( k, p ) { |
- return subtle.encrypt({name: "AES-CBC", iv: new Uint8Array(16) }, k, p).then(function(ciphertext){return ciphertext.slice(0,16);}); |
- } |
- |
- // AES Key Wrap |
- function aeskw(key, data) { |
- if (data.byteLength % 8 !== 0) { |
- throw new Error("AES Key Wrap data must be a multiple of 8 bytes in length"); |
- } |
- |
- var A = Uint8Array.from([0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0, 0, 0, 0, 0, 0, 0, 0]), |
- Av = new DataView(A.buffer), |
- R = [], |
- n = data.byteLength / 8; |
- |
- for(var i = 0; i<data.byteLength; i+=8) { |
- R.push(new Uint8Array(data.slice(i,i+8))); |
- } |
- |
- function aeskw_step(j, i, final, B) { |
- A.set(new Uint8Array(B.slice(0,8))); |
- Av.setUint32(4,Av.getUint32(4) ^ (n*j+i+1)); |
- R[i] = new Uint8Array(B.slice(8,16)); |
- if (final) { |
- R.unshift(A.slice(0,8)); |
- var result = new Uint8Array(R.length * 8); |
- R.forEach(function(Ri,i){ result.set(Ri, i*8); }); |
- return result; |
- } else { |
- A.set(R[(i+1)%n],8); |
- return aes(key,A); |
- } |
- } |
- |
- var p = new Promise(function(resolve){ |
- A.set(R[0],8); |
- resolve(aes(key,A)); |
- }); |
- |
- for(var j=0;j<6;++j) { |
- for(var i=0;i<n;++i) { |
- p = p.then(aeskw_step.bind(undefined, j, i,j===5 && i===(n-1))); |
- } |
- } |
- |
- return p; |
- } |
- |
- function str2ab(str) { return Uint8Array.from( str.split(''), function(s){return s.charCodeAt(0)} ); } |
- function ab2str(ab) { return String.fromCharCode.apply(null, new Uint8Array(ab)); } |
- |
- |
-} |