1
2function run_test() {
3  setup({explicit_done: true});
4
5  var subtle = self.crypto.subtle; // Change to test prefixed implementations
6
7  // When are all these tests really done? When all the promises they use have resolved.
8  var all_promises = [];
9
10  // Source file [algorithm_name]_vectors.js provides the getTestVectors method
11  // for the algorithm that drives these tests.
12  var testVectors = getTestVectors();
13
14  // Test verification first, because signing tests rely on that working
15  testVectors.forEach(function(vector) {
16      var promise = importVectorKeys(vector, ["verify"], ["sign"])
17      .then(function(vectors) {
18          var algorithm = {name: vector.algorithmName};
19          promise_test(function(test) {
20              var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, vector.data)
21              .then(function(is_verified) {
22                  assert_true(is_verified, "Signature verified");
23              }, function(err) {
24                  assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'");
25              });
26
27              return operation;
28          }, vector.name + " verification");
29
30      }, function(err) {
31          // We need a failed test if the importVectorKey operation fails, so
32          // we know we never tested verification.
33          promise_test(function(test) {
34              assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
35          }, "importVectorKeys step: " + vector.name + " verification");
36      });
37
38      all_promises.push(promise);
39  });
40
41  // Test verification with an altered buffer after call
42  testVectors.forEach(function(vector) {
43      var promise = importVectorKeys(vector, ["verify"], ["sign"])
44      .then(function(vectors) {
45          var algorithm = {name: vector.algorithmName};
46          promise_test(function(test) {
47              var signature = copyBuffer(vector.signature);
48              var operation = subtle.verify(algorithm, vector.publicKey, signature, vector.data)
49              .then(function(is_verified) {
50                  assert_true(is_verified, "Signature verified");
51              }, function(err) {
52                  assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'");
53              });
54
55              signature[0] = 255 - signature[0];
56              return operation;
57          }, vector.name + " verification with altered signature after call");
58      }, function(err) {
59          promise_test(function(test) {
60              assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
61          }, "importVectorKeys step: " + vector.name + " verification with altered signature after call");
62      });
63
64      all_promises.push(promise);
65  });
66
67  // Check for successful verification even if data is altered after call.
68  testVectors.forEach(function(vector) {
69      var promise = importVectorKeys(vector, ["verify"], ["sign"])
70      .then(function(vectors) {
71          var algorithm = {name: vector.algorithmName};
72          promise_test(function(test) {
73              var data = copyBuffer(vector.data);
74              var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, data)
75              .then(function(is_verified) {
76                  assert_true(is_verified, "Signature verified");
77              }, function(err) {
78                  assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'");
79              });
80
81              data[0] = 255 - data[0];
82              return operation;
83          }, vector.name + " with altered data after call");
84      }, function(err) {
85          promise_test(function(test) {
86              assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
87          }, "importVectorKeys step: " + vector.name + " with altered data after call");
88      });
89
90      all_promises.push(promise);
91  });
92
93  // Check for failures due to using privateKey to verify.
94  testVectors.forEach(function(vector) {
95      var promise = importVectorKeys(vector, ["verify"], ["sign"])
96      .then(function(vectors) {
97          var algorithm = {name: vector.algorithmName};
98          promise_test(function(test) {
99              return subtle.verify(algorithm, vector.privateKey, vector.signature, vector.data)
100              .then(function(data) {
101                  assert_unreached("Should have thrown error for using privateKey to verify in " + vector.name + ": " + err.message + "'");
102              }, function(err) {
103                  assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'");
104              });
105          }, vector.name + " using privateKey to verify");
106
107      }, function(err) {
108          promise_test(function(test) {
109              assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
110          }, "importVectorKeys step: " + vector.name + " using privateKey to verify");
111      });
112
113      all_promises.push(promise);
114  });
115
116  // Check for failures due to using publicKey to sign.
117  testVectors.forEach(function(vector) {
118      var promise = importVectorKeys(vector, ["verify"], ["sign"])
119      .then(function(vectors) {
120          var algorithm = {name: vector.algorithmName};
121          promise_test(function(test) {
122              return subtle.sign(algorithm, vector.publicKey, vector.data)
123              .then(function(signature) {
124                  assert_unreached("Should have thrown error for using publicKey to sign in " + vector.name + ": " + err.message + "'");
125              }, function(err) {
126                  assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'");
127              });
128          }, vector.name + " using publicKey to sign");
129      }, function(err) {
130          promise_test(function(test) {
131              assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
132          }, "importVectorKeys step: " + vector.name + " using publicKey to sign");
133      });
134
135      all_promises.push(promise);
136  });
137
138  // Check for failures due to no "verify" usage.
139  testVectors.forEach(function(originalVector) {
140      var vector = Object.assign({}, originalVector);
141
142      var promise = importVectorKeys(vector, [], ["sign"])
143      .then(function(vectors) {
144          var algorithm = {name: vector.algorithmName};
145          promise_test(function(test) {
146              return subtle.verify(algorithm, vector.publicKey, vector.signature, vector.data)
147              .then(function(data) {
148                  assert_unreached("Should have thrown error for no verify usage in " + vector.name + ": " + err.message + "'");
149              }, function(err) {
150                  assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'");
151              });
152          }, vector.name + " no verify usage");
153      }, function(err) {
154          promise_test(function(test) {
155              assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
156          }, "importVectorKeys step: " + vector.name + " no verify usage");
157      });
158
159      all_promises.push(promise);
160  });
161
162  // Check for successful signing and verification.
163  testVectors.forEach(function(vector) {
164      var promise = importVectorKeys(vector, ["verify"], ["sign"])
165      .then(function(vectors) {
166          var algorithm = {name: vector.algorithmName};
167          promise_test(function(test) {
168              return subtle.sign(algorithm, vector.privateKey, vector.data)
169              .then(function(signature) {
170                  // Can we verify the signature?
171                  return subtle.verify(algorithm, vector.publicKey, signature, vector.data)
172                  .then(function(is_verified) {
173                      assert_true(is_verified, "Round trip verification works");
174                      return signature;
175                  }, function(err) {
176                      assert_unreached("verify error for test " + vector.name + ": " + err.message + "'");
177                  });
178              }, function(err) {
179                  assert_unreached("sign error for test " + vector.name + ": '" + err.message + "'");
180              });
181          }, vector.name + " round trip");
182
183      }, function(err) {
184          // We need a failed test if the importVectorKey operation fails, so
185          // we know we never tested signing or verifying
186          promise_test(function(test) {
187              assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
188          }, "importVectorKeys step: " + vector.name + " round trip");
189      });
190
191      all_promises.push(promise);
192  });
193
194  // Test signing with the wrong algorithm
195  testVectors.forEach(function(vector) {
196      // Want to get the key for the wrong algorithm
197      var promise = subtle.generateKey({name: "HMAC", hash: "SHA-1"}, false, ["sign", "verify"])
198      .then(function(wrongKey) {
199          var algorithm = {name: vector.algorithmName};
200          return importVectorKeys(vector, ["verify"], ["sign"])
201          .then(function(vectors) {
202              promise_test(function(test) {
203                  var operation = subtle.sign(algorithm, wrongKey, vector.data)
204                  .then(function(signature) {
205                      assert_unreached("Signing should not have succeeded for " + vector.name);
206                  }, function(err) {
207                      assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'");
208                  });
209
210                  return operation;
211              }, vector.name + " signing with wrong algorithm name");
212
213          }, function(err) {
214              // We need a failed test if the importVectorKey operation fails, so
215              // we know we never tested verification.
216              promise_test(function(test) {
217                  assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
218              }, "importVectorKeys step: " + vector.name + " signing with wrong algorithm name");
219          });
220      }, function(err) {
221          promise_test(function(test) {
222              assert_unreached("Generate wrong key for test " + vector.name + " failed: '" + err.message + "'");
223          }, "generate wrong key step: " + vector.name + " signing with wrong algorithm name");
224      });
225
226      all_promises.push(promise);
227  });
228
229  // Test verification with the wrong algorithm
230  testVectors.forEach(function(vector) {
231      // Want to get the key for the wrong algorithm
232      var promise = subtle.generateKey({name: "HMAC", hash: "SHA-1"}, false, ["sign", "verify"])
233      .then(function(wrongKey) {
234          return importVectorKeys(vector, ["verify"], ["sign"])
235          .then(function(vectors) {
236              var algorithm = {name: vector.algorithmName};
237              promise_test(function(test) {
238                  var operation = subtle.verify(algorithm, wrongKey, vector.signature, vector.data)
239                  .then(function(signature) {
240                      assert_unreached("Verifying should not have succeeded for " + vector.name);
241                  }, function(err) {
242                      assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'");
243                  });
244
245                  return operation;
246              }, vector.name + " verifying with wrong algorithm name");
247
248          }, function(err) {
249              // We need a failed test if the importVectorKey operation fails, so
250              // we know we never tested verification.
251              promise_test(function(test) {
252                  assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
253              }, "importVectorKeys step: " + vector.name + " verifying with wrong algorithm name");
254          });
255      }, function(err) {
256          promise_test(function(test) {
257              assert_unreached("Generate wrong key for test " + vector.name + " failed: '" + err.message + "'");
258          }, "generate wrong key step: " + vector.name + " verifying with wrong algorithm name");
259      });
260
261      all_promises.push(promise);
262  });
263
264  // Test verification fails with wrong signature
265  testVectors.forEach(function(vector) {
266      var promise = importVectorKeys(vector, ["verify"], ["sign"])
267      .then(function(vectors) {
268          var algorithm = {name: vector.algorithmName};
269          var signature = copyBuffer(vector.signature);
270          signature[0] = 255 - signature[0];
271          promise_test(function(test) {
272              var operation = subtle.verify(algorithm, vector.publicKey, signature, vector.data)
273              .then(function(is_verified) {
274                  assert_false(is_verified, "Signature NOT verified");
275              }, function(err) {
276                  assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'");
277              });
278
279              return operation;
280          }, vector.name + " verification failure due to altered signature");
281
282      }, function(err) {
283          // We need a failed test if the importVectorKey operation fails, so
284          // we know we never tested verification.
285          promise_test(function(test) {
286              assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
287          }, "importVectorKeys step: " + vector.name + " verification failure due to altered signature");
288      });
289
290      all_promises.push(promise);
291  });
292
293  // Test verification fails with short (odd length) signature
294  testVectors.forEach(function(vector) {
295      var promise = importVectorKeys(vector, ["verify"], ["sign"])
296      .then(function(vectors) {
297          var algorithm = {name: vector.algorithmName};
298          var signature = vector.signature.slice(1); // Skip the first byte
299          promise_test(function(test) {
300              var operation = subtle.verify(algorithm, vector.publicKey, signature, vector.data)
301              .then(function(is_verified) {
302                  assert_false(is_verified, "Signature NOT verified");
303              }, function(err) {
304                  assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'");
305              });
306
307              return operation;
308          }, vector.name + " verification failure due to shortened signature");
309
310      }, function(err) {
311          // We need a failed test if the importVectorKey operation fails, so
312          // we know we never tested verification.
313          promise_test(function(test) {
314              assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
315          }, "importVectorKeys step: " + vector.name + " verification failure due to shortened signature");
316      });
317
318      all_promises.push(promise);
319  });
320
321  // Test verification fails with wrong data
322  testVectors.forEach(function(vector) {
323      var promise = importVectorKeys(vector, ["verify"], ["sign"])
324      .then(function(vectors) {
325          var algorithm = {name: vector.algorithmName};
326          var data = copyBuffer(vector.data);
327          data[0] = 255 - data[0];
328          promise_test(function(test) {
329              var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, data)
330              .then(function(is_verified) {
331                  assert_false(is_verified, "Signature NOT verified");
332              }, function(err) {
333                  assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'");
334              });
335
336              return operation;
337          }, vector.name + " verification failure due to altered data");
338
339      }, function(err) {
340          // We need a failed test if the importVectorKey operation fails, so
341          // we know we never tested verification.
342          promise_test(function(test) {
343              assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
344          }, "importVectorKeys step: " + vector.name + " verification failure due to altered data");
345      });
346
347      all_promises.push(promise);
348  });
349
350
351  promise_test(function() {
352      return Promise.all(all_promises)
353          .then(function() {done();})
354          .catch(function() {done();})
355  }, "setup");
356
357  // Test that generated keys are valid for signing and verifying.
358  testVectors.forEach(function(vector) {
359      var algorithm = {name: vector.algorithmName};
360      promise_test(async() => {
361          let key = await subtle.generateKey(algorithm, false, ["sign", "verify"]);
362          let signature = await subtle.sign(algorithm, key.privateKey, vector.data);
363          let isVerified = await subtle.verify(algorithm, key.publicKey, signature, vector.data);
364          assert_true(isVerified, "Verificaton failed.");
365      }, "Sign and verify using generated " + vector.algorithmName + " keys.");
366  });
367
368
369  // A test vector has all needed fields for signing and verifying, EXCEPT that the
370  // key field may be null. This function replaces that null with the Correct
371  // CryptoKey object.
372  //
373  // Returns a Promise that yields an updated vector on success.
374  function importVectorKeys(vector, publicKeyUsages, privateKeyUsages) {
375      var publicPromise, privatePromise;
376
377      if (vector.publicKey !== null) {
378          publicPromise = new Promise(function(resolve, reject) {
379              resolve(vector);
380          });
381      } else {
382          publicPromise = subtle.importKey(vector.publicKeyFormat, vector.publicKeyBuffer, {name: vector.algorithmName}, false, publicKeyUsages)
383          .then(function(key) {
384              vector.publicKey = key;
385              return vector;
386          });        // Returns a copy of the sourceBuffer it is sent.
387      }
388
389      if (vector.privateKey !== null) {
390          privatePromise = new Promise(function(resolve, reject) {
391              resolve(vector);
392          });
393      } else {
394          privatePromise = subtle.importKey(vector.privateKeyFormat, vector.privateKeyBuffer, {name: vector.algorithmName}, false, privateKeyUsages)
395          .then(function(key) {
396              vector.privateKey = key;
397              return vector;
398          });
399      }
400
401      return Promise.all([publicPromise, privatePromise]);
402  }
403
404  // Returns a copy of the sourceBuffer it is sent.
405  function copyBuffer(sourceBuffer) {
406      var source = new Uint8Array(sourceBuffer);
407      var copy = new Uint8Array(sourceBuffer.byteLength)
408
409      for (var i=0; i<source.byteLength; i++) {
410          copy[i] = source[i];
411      }
412
413      return copy;
414  }
415
416  function equalBuffers(a, b) {
417      if (a.byteLength !== b.byteLength) {
418          return false;
419      }
420
421      var aBytes = new Uint8Array(a);
422      var bBytes = new Uint8Array(b);
423
424      for (var i=0; i<a.byteLength; i++) {
425          if (aBytes[i] !== bBytes[i]) {
426              return false;
427          }
428      }
429
430      return true;
431  }
432
433  return;
434}
435