1function run_test(algorithmNames) { 2 var subtle = crypto.subtle; // Change to test prefixed implementations 3 4 setup({explicit_timeout: true}); 5 6// These tests check that generateKey throws an error, and that 7// the error is of the right type, for a wide set of incorrect parameters. 8// 9// Error testing occurs by setting the parameter that should trigger the 10// error to an invalid value, then combining that with all valid 11// parameters that should be checked earlier by generateKey, and all 12// valid and invalid parameters that should be checked later by 13// generateKey. 14// 15// There are a lot of combinations of possible parameters for both 16// success and failure modes, resulting in a very large number of tests 17// performed. 18 19 20// Setup: define the correct behaviors that should be sought, and create 21// helper functions that generate all possible test parameters for 22// different situations. 23 24 var allTestVectors = [ // Parameters that should work for generateKey 25 {name: "AES-CTR", resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []}, 26 {name: "AES-CBC", resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []}, 27 {name: "AES-GCM", resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []}, 28 {name: "AES-KW", resultType: CryptoKey, usages: ["wrapKey", "unwrapKey"], mandatoryUsages: []}, 29 {name: "HMAC", resultType: CryptoKey, usages: ["sign", "verify"], mandatoryUsages: []}, 30 {name: "RSASSA-PKCS1-v1_5", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, 31 {name: "RSA-PSS", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, 32 {name: "RSA-OAEP", resultType: "CryptoKeyPair", usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: ["decrypt", "unwrapKey"]}, 33 {name: "ECDSA", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, 34 {name: "ECDH", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]}, 35 {name: "Ed25519", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, 36 {name: "Ed448", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, 37 {name: "X25519", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]}, 38 {name: "X448", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]}, 39 ]; 40 41 var testVectors = []; 42 if (algorithmNames && !Array.isArray(algorithmNames)) { 43 algorithmNames = [algorithmNames]; 44 }; 45 allTestVectors.forEach(function(vector) { 46 if (!algorithmNames || algorithmNames.includes(vector.name)) { 47 testVectors.push(vector); 48 } 49 }); 50 51 52 function parameterString(algorithm, extractable, usages) { 53 if (typeof algorithm !== "object" && typeof algorithm !== "string") { 54 alert(algorithm); 55 } 56 57 var result = "(" + 58 objectToString(algorithm) + ", " + 59 objectToString(extractable) + ", " + 60 objectToString(usages) + 61 ")"; 62 63 return result; 64 } 65 66 // Test that a given combination of parameters results in an error, 67 // AND that it is the correct kind of error. 68 // 69 // Expected error is either a number, tested against the error code, 70 // or a string, tested against the error name. 71 function testError(algorithm, extractable, usages, expectedError, testTag) { 72 promise_test(function(test) { 73 return crypto.subtle.generateKey(algorithm, extractable, usages) 74 .then(function(result) { 75 assert_unreached("Operation succeeded, but should not have"); 76 }, function(err) { 77 if (typeof expectedError === "number") { 78 assert_equals(err.code, expectedError, testTag + " not supported"); 79 } else { 80 assert_equals(err.name, expectedError, testTag + " not supported"); 81 } 82 }); 83 }, testTag + ": generateKey" + parameterString(algorithm, extractable, usages)); 84 } 85 86 87 // Given an algorithm name, create several invalid parameters. 88 function badAlgorithmPropertySpecifiersFor(algorithmName) { 89 var results = []; 90 91 if (algorithmName.toUpperCase().substring(0, 3) === "AES") { 92 // Specifier properties are name and length 93 [64, 127, 129, 255, 257, 512].forEach(function(length) { 94 results.push({name: algorithmName, length: length}); 95 }); 96 } else if (algorithmName.toUpperCase().substring(0, 3) === "RSA") { 97 [new Uint8Array([1]), new Uint8Array([1,0,0])].forEach(function(publicExponent) { 98 results.push({name: algorithmName, hash: "SHA-256", modulusLength: 1024, publicExponent: publicExponent}); 99 }); 100 } else if (algorithmName.toUpperCase().substring(0, 2) === "EC") { 101 ["P-512", "Curve25519"].forEach(function(curveName) { 102 results.push({name: algorithmName, namedCurve: curveName}); 103 }); 104 } 105 106 return results; 107 } 108 109 110 // Don't create an exhaustive list of all invalid usages, 111 // because there would usually be nearly 2**8 of them, 112 // way too many to test. Instead, create every singleton 113 // of an illegal usage, and "poison" every valid usage 114 // with an illegal one. 115 function invalidUsages(validUsages, mandatoryUsages) { 116 var results = []; 117 118 var illegalUsages = []; 119 ["encrypt", "decrypt", "sign", "verify", "wrapKey", "unwrapKey", "deriveKey", "deriveBits"].forEach(function(usage) { 120 if (!validUsages.includes(usage)) { 121 illegalUsages.push(usage); 122 } 123 }); 124 125 var goodUsageCombinations = allValidUsages(validUsages, false, mandatoryUsages); 126 127 illegalUsages.forEach(function(illegalUsage) { 128 results.push([illegalUsage]); 129 goodUsageCombinations.forEach(function(usageCombination) { 130 results.push(usageCombination.concat([illegalUsage])); 131 }); 132 }); 133 134 return results; 135 } 136 137 138// Now test for properly handling errors 139// - Unsupported algorithm 140// - Bad usages for algorithm 141// - Bad key lengths 142 143 // Algorithm normalization should fail with "Not supported" 144 var badAlgorithmNames = [ 145 "AES", 146 {name: "AES"}, 147 {name: "AES", length: 128}, 148 {name: "AES-CMAC", length: 128}, // Removed after CR 149 {name: "AES-CFB", length: 128}, // Removed after CR 150 {name: "HMAC", hash: "MD5"}, 151 {name: "RSA", hash: "SHA-256", modulusLength: 2048, publicExponent: new Uint8Array([1,0,1])}, 152 {name: "RSA-PSS", hash: "SHA", modulusLength: 2048, publicExponent: new Uint8Array([1,0,1])}, 153 {name: "EC", namedCurve: "P521"} 154 ]; 155 156 157 // Algorithm normalization failures should be found first 158 // - all other parameters can be good or bad, should fail 159 // due to NotSupportedError. 160 badAlgorithmNames.forEach(function(algorithm) { 161 allValidUsages(["decrypt", "sign", "deriveBits"], true, []) // Small search space, shouldn't matter because should fail before used 162 .forEach(function(usages) { 163 [false, true, "RED", 7].forEach(function(extractable){ 164 testError(algorithm, extractable, usages, "NotSupportedError", "Bad algorithm"); 165 }); 166 }); 167 }); 168 169 170 // Algorithms normalize okay, but usages bad (though not empty). 171 // It shouldn't matter what other extractable is. Should fail 172 // due to SyntaxError 173 testVectors.forEach(function(vector) { 174 var name = vector.name; 175 176 allAlgorithmSpecifiersFor(name).forEach(function(algorithm) { 177 invalidUsages(vector.usages, vector.mandatoryUsages).forEach(function(usages) { 178 [true].forEach(function(extractable) { 179 testError(algorithm, extractable, usages, "SyntaxError", "Bad usages"); 180 }); 181 }); 182 }); 183 }); 184 185 186 // Other algorithm properties should be checked next, so try good 187 // algorithm names and usages, but bad algorithm properties next. 188 // - Special case: normally bad usage [] isn't checked until after properties, 189 // so it's included in this test case. It should NOT cause an error. 190 testVectors.forEach(function(vector) { 191 var name = vector.name; 192 badAlgorithmPropertySpecifiersFor(name).forEach(function(algorithm) { 193 allValidUsages(vector.usages, true, vector.mandatoryUsages) 194 .forEach(function(usages) { 195 [false, true].forEach(function(extractable) { 196 if (name.substring(0,2) === "EC") { 197 testError(algorithm, extractable, usages, "NotSupportedError", "Bad algorithm property"); 198 } else { 199 testError(algorithm, extractable, usages, "OperationError", "Bad algorithm property"); 200 } 201 }); 202 }); 203 }); 204 }); 205 206 207 // The last thing that should be checked is empty usages (disallowed for secret and private keys). 208 testVectors.forEach(function(vector) { 209 var name = vector.name; 210 211 allAlgorithmSpecifiersFor(name).forEach(function(algorithm) { 212 var usages = []; 213 [false, true].forEach(function(extractable) { 214 testError(algorithm, extractable, usages, "SyntaxError", "Empty usages"); 215 }); 216 }); 217 }); 218 219 220} 221