• 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 hmac_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(vector) {
18            promise_test(function(test) {
19                var operation = subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, vector.signature, vector.plaintext)
20                .then(function(is_verified) {
21                    assert_true(is_verified, "Signature verified");
22                }, function(err) {
23                    assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'");
24                });
25
26                return operation;
27            }, vector.name + " verification");
28
29        }, function(err) {
30            // We need a failed test if the importVectorKey operation fails, so
31            // we know we never tested verification.
32            promise_test(function(test) {
33                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
34            }, "importVectorKeys step: " + vector.name + " verification");
35        });
36
37        all_promises.push(promise);
38    });
39
40    // Test verification with an altered buffer after call
41    testVectors.forEach(function(vector) {
42        var promise = importVectorKeys(vector, ["verify", "sign"])
43        .then(function(vector) {
44            promise_test(function(test) {
45                var signature = copyBuffer(vector.signature);
46                var operation = subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, signature, vector.plaintext)
47                .then(function(is_verified) {
48                    assert_true(is_verified, "Signature is not verified");
49                }, function(err) {
50                    assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'");
51                });
52
53                signature[0] = 255 - signature[0];
54                return operation;
55            }, vector.name + " verification with altered signature after call");
56        }, function(err) {
57            promise_test(function(test) {
58                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
59            }, "importVectorKeys step: " + vector.name + " verification with altered signature after call");
60        });
61
62        all_promises.push(promise);
63    });
64
65    // Check for successful verification even if plaintext is altered after call.
66    testVectors.forEach(function(vector) {
67        var promise = importVectorKeys(vector, ["verify", "sign"])
68        .then(function(vector) {
69            promise_test(function(test) {
70                var plaintext = copyBuffer(vector.plaintext);
71                var operation = subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, vector.signature, plaintext)
72                .then(function(is_verified) {
73                    assert_true(is_verified, "Signature verified");
74                }, function(err) {
75                    assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'");
76                });
77
78                plaintext[0] = 255 - plaintext[0];
79                return operation;
80            }, vector.name + " with altered plaintext after call");
81        }, function(err) {
82            promise_test(function(test) {
83                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
84            }, "importVectorKeys step: " + vector.name + " with altered plaintext");
85        });
86
87        all_promises.push(promise);
88    });
89
90    // Check for failures due to no "verify" usage.
91    testVectors.forEach(function(originalVector) {
92        var vector = Object.assign({}, originalVector);
93
94        var promise = importVectorKeys(vector, ["sign"])
95        .then(function(vector) {
96            promise_test(function(test) {
97                return subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, vector.signature, vector.plaintext)
98                .then(function(plaintext) {
99                    assert_unreached("Should have thrown error for no verify usage in " + vector.name + ": " + err.message + "'");
100                }, function(err) {
101                    assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'");
102                });
103            }, vector.name + " no verify usage");
104        }, function(err) {
105            promise_test(function(test) {
106                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
107            }, "importVectorKeys step: " + vector.name + " no verify usage");
108        });
109
110        all_promises.push(promise);
111    });
112
113    // Check for successful signing and verification.
114    testVectors.forEach(function(vector) {
115        var promise = importVectorKeys(vector, ["verify", "sign"])
116        .then(function(vectors) {
117            promise_test(function(test) {
118                return subtle.sign({name: "HMAC", hash: vector.hash}, vector.key, vector.plaintext)
119                .then(function(signature) {
120                    // Can we get the verify the new signature?
121                    return subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, signature, vector.plaintext)
122                    .then(function(is_verified) {
123                        assert_true(is_verified, "Round trip verifies");
124                        return signature;
125                    }, function(err) {
126                        assert_unreached("verify error for test " + vector.name + ": " + err.message + "'");
127                    });
128                });
129            }, vector.name + " round trip");
130
131        }, function(err) {
132            // We need a failed test if the importVectorKey operation fails, so
133            // we know we never tested signing or verifying
134            promise_test(function(test) {
135                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
136            }, "importVectorKeys step: " + vector.name + " round trip");
137        });
138
139        all_promises.push(promise);
140    });
141
142    // Test signing with the wrong algorithm
143    testVectors.forEach(function(vector) {
144        // Want to get the key for the wrong algorithm
145        var promise = subtle.generateKey({name: "ECDSA", namedCurve: "P-256", hash: "SHA-256"}, false, ["sign", "verify"])
146        .then(function(wrongKey) {
147            return importVectorKeys(vector, ["verify", "sign"])
148            .then(function(vectors) {
149                promise_test(function(test) {
150                    var operation = subtle.sign({name: "HMAC", hash: vector.hash}, wrongKey.privateKey, vector.plaintext)
151                    .then(function(signature) {
152                        assert_unreached("Signing should not have succeeded for " + vector.name);
153                    }, function(err) {
154                        assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'");
155                    });
156
157                    return operation;
158                }, vector.name + " signing with wrong algorithm name");
159
160            }, function(err) {
161                // We need a failed test if the importVectorKey operation fails, so
162                // we know we never tested verification.
163                promise_test(function(test) {
164                    assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
165                }, "importVectorKeys step: " + vector.name + " signing with wrong algorithm name");
166            });
167        }, function(err) {
168            promise_test(function(test) {
169                assert_unreached("Generate wrong key for test " + vector.name + " failed: '" + err.message + "'");
170            }, "generate wrong key step: " + vector.name + " signing with wrong algorithm name");
171        });
172
173        all_promises.push(promise);
174    });
175
176    // Test verification with the wrong algorithm
177    testVectors.forEach(function(vector) {
178        // Want to get the key for the wrong algorithm
179        var promise = subtle.generateKey({name: "ECDSA", namedCurve: "P-256", hash: "SHA-256"}, false, ["sign", "verify"])
180        .then(function(wrongKey) {
181            return importVectorKeys(vector, ["verify", "sign"])
182            .then(function(vector) {
183                promise_test(function(test) {
184                    var operation = subtle.verify({name: "HMAC", hash: vector.hash}, wrongKey.publicKey, vector.signature, vector.plaintext)
185                    .then(function(signature) {
186                        assert_unreached("Verifying should not have succeeded for " + vector.name);
187                    }, function(err) {
188                        assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'");
189                    });
190
191                    return operation;
192                }, vector.name + " verifying with wrong algorithm name");
193
194            }, function(err) {
195                // We need a failed test if the importVectorKey operation fails, so
196                // we know we never tested verification.
197                promise_test(function(test) {
198                    assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
199                }, "importVectorKeys step: " + vector.name + " verifying with wrong algorithm name");
200            });
201        }, function(err) {
202            promise_test(function(test) {
203                assert_unreached("Generate wrong key for test " + vector.name + " failed: '" + err.message + "'");
204            }, "generate wrong key step: " + vector.name + " verifying with wrong algorithm name");
205        });
206
207        all_promises.push(promise);
208    });
209
210    // Verification should fail if the plaintext is changed
211    testVectors.forEach(function(vector) {
212        var promise = importVectorKeys(vector, ["verify", "sign"])
213        .then(function(vector) {
214            var plaintext = copyBuffer(vector.plaintext);
215            plaintext[0] = 255 - plaintext[0];
216            promise_test(function(test) {
217                var operation = subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, vector.signature, plaintext)
218                .then(function(is_verified) {
219                    assert_false(is_verified, "Signature is NOT verified");
220                }, function(err) {
221                    assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'");
222                });
223
224                return operation;
225            }, vector.name + " verification failure due to wrong plaintext");
226
227        }, function(err) {
228            // We need a failed test if the importVectorKey operation fails, so
229            // we know we never tested verification.
230            promise_test(function(test) {
231                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
232            }, "importVectorKeys step: " + vector.name + " verification failure due to wrong plaintext");
233        });
234
235        all_promises.push(promise);
236    });
237
238    // Verification should fail if the signature is changed
239    testVectors.forEach(function(vector) {
240        var promise = importVectorKeys(vector, ["verify", "sign"])
241        .then(function(vector) {
242            var signature = copyBuffer(vector.signature);
243            signature[0] = 255 - signature[0];
244            promise_test(function(test) {
245                var operation = subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, signature, vector.plaintext)
246                .then(function(is_verified) {
247                    assert_false(is_verified, "Signature is NOT verified");
248                }, function(err) {
249                    assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'");
250                });
251
252                return operation;
253            }, vector.name + " verification failure due to wrong signature");
254
255        }, function(err) {
256            // We need a failed test if the importVectorKey operation fails, so
257            // we know we never tested verification.
258            promise_test(function(test) {
259                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
260            }, "importVectorKeys step: " + vector.name + " verification failure due to wrong signature");
261        });
262
263        all_promises.push(promise);
264    });
265
266    // Verification should fail if the signature is wrong length
267    testVectors.forEach(function(vector) {
268        var promise = importVectorKeys(vector, ["verify", "sign"])
269        .then(function(vector) {
270            var signature = vector.signature.slice(1); // Drop first byte
271            promise_test(function(test) {
272                var operation = subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, signature, vector.plaintext)
273                .then(function(is_verified) {
274                    assert_false(is_verified, "Signature is 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 short 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 short signature");
288        });
289
290        all_promises.push(promise);
291    });
292
293
294
295    promise_test(function() {
296        return Promise.all(all_promises)
297            .then(function() {done();})
298            .catch(function() {done();})
299    }, "setup");
300
301    // A test vector has all needed fields for signing and verifying, EXCEPT that the
302    // key field may be null. This function replaces that null with the Correct
303    // CryptoKey object.
304    //
305    // Returns a Promise that yields an updated vector on success.
306    function importVectorKeys(vector, keyUsages) {
307        if (vector.key !== null) {
308            return new Promise(function(resolve, reject) {
309                resolve(vector);
310            });
311        } else {
312            return subtle.importKey("raw", vector.keyBuffer, {name: "HMAC", hash: vector.hash}, false, keyUsages)
313            .then(function(key) {
314                vector.key = key;
315                return vector;
316            });
317        }
318    }
319
320    // Returns a copy of the sourceBuffer it is sent.
321    function copyBuffer(sourceBuffer) {
322        var source = new Uint8Array(sourceBuffer);
323        var copy = new Uint8Array(sourceBuffer.byteLength)
324
325        for (var i=0; i<source.byteLength; i++) {
326            copy[i] = source[i];
327        }
328
329        return copy;
330    }
331
332    function equalBuffers(a, b) {
333        if (a.byteLength !== b.byteLength) {
334            return false;
335        }
336
337        var aBytes = new Uint8Array(a);
338        var bBytes = new Uint8Array(b);
339
340        for (var i=0; i<a.byteLength; i++) {
341            if (aBytes[i] !== bBytes[i]) {
342                return false;
343            }
344        }
345
346        return true;
347    }
348
349    return;
350}
351