failures.js 10 KB

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