failures.js (11679B)
1 function 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-OCB", resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []}, 29 {name: "ChaCha20-Poly1305", resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []}, 30 {name: "AES-KW", resultType: CryptoKey, usages: ["wrapKey", "unwrapKey"], mandatoryUsages: []}, 31 {name: "HMAC", resultType: CryptoKey, usages: ["sign", "verify"], mandatoryUsages: []}, 32 {name: "RSASSA-PKCS1-v1_5", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, 33 {name: "RSA-PSS", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, 34 {name: "RSA-OAEP", resultType: "CryptoKeyPair", usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: ["decrypt", "unwrapKey"]}, 35 {name: "ECDSA", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, 36 {name: "ECDH", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]}, 37 {name: "Ed25519", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, 38 {name: "Ed448", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, 39 {name: "ML-DSA-44", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, 40 {name: "ML-DSA-65", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, 41 {name: "ML-DSA-87", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]}, 42 {name: "ML-KEM-512", resultType: "CryptoKeyPair", usages: ["decapsulateBits", "decapsulateKey", "encapsulateBits", "encapsulateKey"], mandatoryUsages: ["decapsulateBits", "decapsulateKey"]}, 43 {name: "ML-KEM-768", resultType: "CryptoKeyPair", usages: ["decapsulateBits", "decapsulateKey", "encapsulateBits", "encapsulateKey"], mandatoryUsages: ["decapsulateBits", "decapsulateKey"]}, 44 {name: "ML-KEM-1024", resultType: "CryptoKeyPair", usages: ["decapsulateBits", "decapsulateKey", "encapsulateBits", "encapsulateKey"], mandatoryUsages: ["decapsulateBits", "decapsulateKey"]}, 45 {name: "X25519", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]}, 46 {name: "X448", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]}, 47 {name: "KMAC128", resultType: CryptoKey, usages: ["sign", "verify"], mandatoryUsages: []}, 48 {name: "KMAC256", resultType: CryptoKey, usages: ["sign", "verify"], mandatoryUsages: []}, 49 ]; 50 51 var testVectors = []; 52 if (algorithmNames && !Array.isArray(algorithmNames)) { 53 algorithmNames = [algorithmNames]; 54 }; 55 allTestVectors.forEach(function(vector) { 56 if (!algorithmNames || algorithmNames.includes(vector.name)) { 57 testVectors.push(vector); 58 } 59 }); 60 61 62 function parameterString(algorithm, extractable, usages) { 63 if (typeof algorithm !== "object" && typeof algorithm !== "string") { 64 alert(algorithm); 65 } 66 67 var result = "(" + 68 objectToString(algorithm) + ", " + 69 objectToString(extractable) + ", " + 70 objectToString(usages) + 71 ")"; 72 73 return result; 74 } 75 76 // Test that a given combination of parameters results in an error, 77 // AND that it is the correct kind of error. 78 // 79 // Expected error is either a number, tested against the error code, 80 // or a string, tested against the error name. 81 function testError(algorithm, extractable, usages, expectedError, testTag) { 82 promise_test(function(test) { 83 return crypto.subtle.generateKey(algorithm, extractable, usages) 84 .then(function(result) { 85 assert_unreached("Operation succeeded, but should not have"); 86 }, function(err) { 87 if (typeof expectedError === "number") { 88 assert_equals(err.code, expectedError, testTag + " not supported"); 89 } else { 90 assert_equals(err.name, expectedError, testTag + " not supported"); 91 } 92 }); 93 }, testTag + ": generateKey" + parameterString(algorithm, extractable, usages)); 94 } 95 96 97 // Given an algorithm name, create several invalid parameters. 98 function badAlgorithmPropertySpecifiersFor(algorithmName) { 99 var results = []; 100 101 if (algorithmName.toUpperCase().substring(0, 3) === "AES") { 102 // Specifier properties are name and length 103 [64, 127, 129, 255, 257, 512].forEach(function(length) { 104 results.push({name: algorithmName, length: length}); 105 }); 106 } else if (algorithmName.toUpperCase().substring(0, 3) === "RSA") { 107 [new Uint8Array([1]), new Uint8Array([1,0,0])].forEach(function(publicExponent) { 108 results.push({name: algorithmName, hash: "SHA-256", modulusLength: 1024, publicExponent: publicExponent}); 109 }); 110 } else if (algorithmName.toUpperCase().substring(0, 2) === "EC") { 111 ["P-512", "Curve25519"].forEach(function(curveName) { 112 results.push({name: algorithmName, namedCurve: curveName}); 113 }); 114 } 115 116 return results; 117 } 118 119 120 // Don't create an exhaustive list of all invalid usages, 121 // because there would usually be nearly 2**8 of them, 122 // way too many to test. Instead, create every singleton 123 // of an illegal usage, and "poison" every valid usage 124 // with an illegal one. 125 function invalidUsages(validUsages, mandatoryUsages) { 126 var results = []; 127 128 var illegalUsages = []; 129 ["encrypt", "decrypt", "sign", "verify", "wrapKey", "unwrapKey", "deriveKey", "deriveBits"].forEach(function(usage) { 130 if (!validUsages.includes(usage)) { 131 illegalUsages.push(usage); 132 } 133 }); 134 135 var goodUsageCombinations = allValidUsages(validUsages, false, mandatoryUsages); 136 137 illegalUsages.forEach(function(illegalUsage) { 138 results.push([illegalUsage]); 139 goodUsageCombinations.forEach(function(usageCombination) { 140 results.push(usageCombination.concat([illegalUsage])); 141 }); 142 }); 143 144 return results; 145 } 146 147 148 // Now test for properly handling errors 149 // - Unsupported algorithm 150 // - Bad usages for algorithm 151 // - Bad key lengths 152 153 // Algorithm normalization should fail with "Not supported" 154 var badAlgorithmNames = [ 155 "AES", 156 {name: "AES"}, 157 {name: "AES", length: 128}, 158 {name: "AES-CMAC", length: 128}, // Removed after CR 159 {name: "AES-CFB", length: 128}, // Removed after CR 160 {name: "HMAC", hash: "MD5"}, 161 {name: "RSA", hash: "SHA-256", modulusLength: 2048, publicExponent: new Uint8Array([1,0,1])}, 162 {name: "RSA-PSS", hash: "SHA", modulusLength: 2048, publicExponent: new Uint8Array([1,0,1])}, 163 {name: "EC", namedCurve: "P521"} 164 ]; 165 166 167 // Algorithm normalization failures should be found first 168 // - all other parameters can be good or bad, should fail 169 // due to NotSupportedError. 170 badAlgorithmNames.forEach(function(algorithm) { 171 allValidUsages(["decrypt", "sign", "deriveBits"], true, []) // Small search space, shouldn't matter because should fail before used 172 .forEach(function(usages) { 173 [false, true, "RED", 7].forEach(function(extractable){ 174 testError(algorithm, extractable, usages, "NotSupportedError", "Bad algorithm"); 175 }); 176 }); 177 }); 178 179 // Empty algorithm should fail with TypeError 180 allValidUsages(["decrypt", "sign", "deriveBits"], true, []) // Small search space, shouldn't matter because should fail before used 181 .forEach(function(usages) { 182 [false, true, "RED", 7].forEach(function(extractable){ 183 testError({}, extractable, usages, "TypeError", "Empty algorithm"); 184 }); 185 }); 186 187 188 // Algorithms normalize okay, but usages bad (though not empty). 189 // It shouldn't matter what other extractable is. Should fail 190 // due to SyntaxError 191 testVectors.forEach(function(vector) { 192 var name = vector.name; 193 194 allAlgorithmSpecifiersFor(name).forEach(function(algorithm) { 195 invalidUsages(vector.usages, vector.mandatoryUsages).forEach(function(usages) { 196 [true].forEach(function(extractable) { 197 testError(algorithm, extractable, usages, "SyntaxError", "Bad usages"); 198 }); 199 }); 200 }); 201 }); 202 203 204 // Other algorithm properties should be checked next, so try good 205 // algorithm names and usages, but bad algorithm properties next. 206 // - Special case: normally bad usage [] isn't checked until after properties, 207 // so it's included in this test case. It should NOT cause an error. 208 testVectors.forEach(function(vector) { 209 var name = vector.name; 210 badAlgorithmPropertySpecifiersFor(name).forEach(function(algorithm) { 211 allValidUsages(vector.usages, true, vector.mandatoryUsages) 212 .forEach(function(usages) { 213 [false, true].forEach(function(extractable) { 214 if (name.substring(0,2) === "EC") { 215 testError(algorithm, extractable, usages, "NotSupportedError", "Bad algorithm property"); 216 } else { 217 testError(algorithm, extractable, usages, "OperationError", "Bad algorithm property"); 218 } 219 }); 220 }); 221 }); 222 }); 223 224 225 // The last thing that should be checked is empty usages (disallowed for secret and private keys). 226 testVectors.forEach(function(vector) { 227 var name = vector.name; 228 229 allAlgorithmSpecifiersFor(name).forEach(function(algorithm) { 230 var usages = []; 231 [false, true].forEach(function(extractable) { 232 testError(algorithm, extractable, usages, "SyntaxError", "Empty usages"); 233 }); 234 }); 235 }); 236 237 238 }