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