// // helpers.js // // Helper functions used by several WebCryptoAPI tests // var registeredAlgorithmNames = [ "RSASSA-PKCS1-v1_5", "RSA-PSS", "RSA-OAEP", "ECDSA", "ECDH", "AES-CTR", "AES-CBC", "AES-GCM", "AES-KW", "HMAC", "SHA-1", "SHA-256", "SHA-384", "SHA-512", "HKDF-CTR", "PBKDF2" ]; // Treats an array as a set, and generates an array of all non-empty // subsets (which are themselves arrays). // // The order of members of the "subsets" is not guaranteed. function allNonemptySubsetsOf(arr) { var results = []; var firstElement; var remainingElements; for(var i=0; i 0) { allNonemptySubsetsOf(remainingElements).forEach(function(combination) { combination.push(firstElement); results.push(combination); }); } } return results; } // Create a string representation of keyGeneration parameters for // test names and labels. 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(", ") + "}"; } // Is key a CryptoKey object with correct algorithm, extractable, and usages? // Is it a secret, private, or public kind of key? function assert_goodCryptoKey(key, algorithm, extractable, usages, kind) { var correctUsages = []; var registeredAlgorithmName; registeredAlgorithmNames.forEach(function(name) { if (name.toUpperCase() === algorithm.name.toUpperCase()) { registeredAlgorithmName = name; } }); assert_equals(key.constructor, CryptoKey, "Is a CryptoKey"); assert_equals(key.type, kind, "Is a " + kind + " key"); if (key.type === "public") { extractable = true; // public keys are always extractable } assert_equals(key.extractable, extractable, "Extractability is correct"); assert_equals(key.algorithm.name, registeredAlgorithmName, "Correct algorithm name"); assert_equals(key.algorithm.length, algorithm.length, "Correct length"); if (["HMAC", "RSASSA-PKCS1-v1_5", "RSA-PSS"].includes(registeredAlgorithmName)) { assert_equals(key.algorithm.hash.name.toUpperCase(), algorithm.hash.toUpperCase(), "Correct hash function"); } // usages is expected to be provided for a key pair, but we are checking // only a single key. The publicKey and privateKey portions of a key pair // recognize only some of the usages appropriate for a key pair. if (key.type === "public") { ["encrypt", "verify", "wrapKey"].forEach(function(usage) { if (usages.includes(usage)) { correctUsages.push(usage); } }); } else if (key.type === "private") { ["decrypt", "sign", "unwrapKey", "deriveKey", "deriveBits"].forEach(function(usage) { if (usages.includes(usage)) { correctUsages.push(usage); } }); } else { correctUsages = usages; } assert_equals((typeof key.usages), "object", key.type + " key.usages is an object"); assert_not_equals(key.usages, null, key.type + " key.usages isn't null"); // The usages parameter could have repeats, but the usages // property of the result should not. var usageCount = 0; key.usages.forEach(function(usage) { usageCount += 1; assert_in_array(usage, correctUsages, "Has " + usage + " usage"); }); assert_equals(key.usages.length, usageCount, "usages property is correct"); } // The algorithm parameter is an object with a name and other // properties. Given the name, generate all valid parameters. function allAlgorithmSpecifiersFor(algorithmName) { var results = []; // RSA key generation is slow. Test a minimal set of parameters var hashes = ["SHA-1", "SHA-256"]; // EC key generation is a lot faster. Check all curves in the spec var curves = ["P-256", "P-384", "P-521"]; if (algorithmName.toUpperCase().substring(0, 3) === "AES") { // Specifier properties are name and length [128, 192, 256].forEach(function(length) { results.push({name: algorithmName, length: length}); }); } else if (algorithmName.toUpperCase() === "HMAC") { [ {name: "SHA-1", length: 160}, {name: "SHA-256", length: 256}, {name: "SHA-384", length: 384}, {name: "SHA-512", length: 512} ].forEach(function(hashAlgorithm) { results.push({name: algorithmName, hash: hashAlgorithm.name, length: hashAlgorithm.length}); }); } else if (algorithmName.toUpperCase().substring(0, 3) === "RSA") { hashes.forEach(function(hashName) { results.push({name: algorithmName, hash: hashName, modulusLength: 2048, publicExponent: new Uint8Array([1,0,1])}); }); } else if (algorithmName.toUpperCase().substring(0, 2) === "EC") { curves.forEach(function(curveName) { results.push({name: algorithmName, namedCurve: curveName}); }); } return results; } // Create every possible valid usages parameter, given legal // usages. Note that an empty usages parameter is not always valid. // // There is an optional parameter - mandatoryUsages. If provided, // it should be an array containing those usages of which one must be // included. function allValidUsages(validUsages, emptyIsValid, mandatoryUsages) { if (typeof mandatoryUsages === "undefined") { mandatoryUsages = []; } okaySubsets = []; allNonemptySubsetsOf(validUsages).forEach(function(subset) { if (mandatoryUsages.length === 0) { okaySubsets.push(subset); } else { for (var i=0; i