• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1
2function run_test() {
3    var subtle = self.crypto.subtle; // Change to test prefixed implementations
4
5    // When are all these tests really done? When all the promises they use have resolved.
6    var all_promises = [];
7
8    // Source file aes_XXX_vectors.js provides the getTestVectors method
9    // for the AES-XXX algorithm that drives these tests.
10    var vectors = getTestVectors();
11    var passingVectors = vectors.passing;
12    var failingVectors = vectors.failing;
13    var decryptionFailingVectors = vectors.decryptionFailing;
14
15    // Check for successful encryption.
16    passingVectors.forEach(function(vector) {
17        var promise = importVectorKey(vector, ["encrypt", "decrypt"])
18        .then(function(vector) {
19            promise_test(function(test) {
20                return subtle.encrypt(vector.algorithm, vector.key, vector.plaintext)
21                .then(function(result) {
22                    assert_true(equalBuffers(result, vector.result), "Should return expected result");
23                }, function(err) {
24                    assert_unreached("encrypt error for test " + vector.name + ": " + err.message);
25                });
26            }, vector.name);
27        }, function(err) {
28            // We need a failed test if the importVectorKey operation fails, so
29            // we know we never tested encryption
30            promise_test(function(test) {
31                assert_unreached("importKey failed for " + vector.name);
32            }, "importKey step: " + vector.name);
33        });
34
35        all_promises.push(promise);
36    });
37
38    // Check for successful encryption even if the buffer is changed after calling encrypt.
39    passingVectors.forEach(function(vector) {
40        var plaintext = copyBuffer(vector.plaintext);
41        var promise = importVectorKey(vector, ["encrypt", "decrypt"])
42        .then(function(vector) {
43            promise_test(function(test) {
44                var operation = subtle.encrypt(vector.algorithm, vector.key, plaintext)
45                .then(function(result) {
46                    assert_true(equalBuffers(result, vector.result), "Should return expected result");
47                }, function(err) {
48                    assert_unreached("encrypt error for test " + vector.name + ": " + err.message);
49                });
50                plaintext[0] = 255 - plaintext[0];
51                return operation;
52            }, vector.name + " with altered plaintext");
53        }, function(err) {
54            // We need a failed test if the importVectorKey operation fails, so
55            // we know we never tested encryption
56            promise_test(function(test) {
57                assert_unreached("importKey failed for " + vector.name);
58            }, "importKey step: " + vector.name + " with altered plaintext");
59        });
60
61        all_promises.push(promise);
62    });
63
64    // Check for successful decryption.
65    passingVectors.forEach(function(vector) {
66        var promise = importVectorKey(vector, ["encrypt", "decrypt"])
67        .then(function(vector) {
68            promise_test(function(test) {
69                return subtle.decrypt(vector.algorithm, vector.key, vector.result)
70                .then(function(result) {
71                    assert_true(equalBuffers(result, vector.plaintext), "Should return expected result");
72                }, function(err) {
73                    assert_unreached("decrypt error for test " + vector.name + ": " + err.message);
74                });
75            }, vector.name + " decryption");
76        }, function(err) {
77            // We need a failed test if the importVectorKey operation fails, so
78            // we know we never tested encryption
79            promise_test(function(test) {
80                assert_unreached("importKey failed for " + vector.name);
81            }, "importKey step for decryption: " + vector.name);
82        });
83
84        all_promises.push(promise);
85    });
86
87    // Check for successful decryption even if ciphertext is altered.
88    passingVectors.forEach(function(vector) {
89        var ciphertext = copyBuffer(vector.result);
90        var promise = importVectorKey(vector, ["encrypt", "decrypt"])
91        .then(function(vector) {
92            promise_test(function(test) {
93                var operation = subtle.decrypt(vector.algorithm, vector.key, ciphertext)
94                .then(function(result) {
95                    assert_true(equalBuffers(result, vector.plaintext), "Should return expected result");
96                }, function(err) {
97                    assert_unreached("decrypt error for test " + vector.name + ": " + err.message);
98                });
99                ciphertext[0] = 255 - ciphertext[0];
100                return operation;
101            }, vector.name + " decryption with altered ciphertext");
102        }, function(err) {
103            // We need a failed test if the importVectorKey operation fails, so
104            // we know we never tested encryption
105            promise_test(function(test) {
106                assert_unreached("importKey failed for " + vector.name);
107            }, "importKey step for decryption: " + vector.name + " with altered ciphertext");
108        });
109
110        all_promises.push(promise);
111    });
112
113    // Everything that succeeded should fail if no "encrypt" usage.
114    passingVectors.forEach(function(vector) {
115        // Don't want to overwrite key being used for success tests!
116        var badVector = Object.assign({}, vector);
117        badVector.key = null;
118
119        var promise = importVectorKey(badVector, ["decrypt"])
120        .then(function(vector) {
121            promise_test(function(test) {
122                return subtle.encrypt(vector.algorithm, vector.key, vector.plaintext)
123                .then(function(result) {
124                    assert_unreached("should have thrown exception for test " + vector.name);
125                }, function(err) {
126                    assert_equals(err.name, "InvalidAccessError", "Should throw an InvalidAccessError instead of " + err.message)
127                });
128            }, vector.name + " without encrypt usage");
129        }, function(err) {
130            // We need a failed test if the importVectorKey operation fails, so
131            // we know we never tested encryption
132            promise_test(function(test) {
133                assert_unreached("importKey failed for " + vector.name);
134            }, "importKey step: " + vector.name + " without encrypt usage");
135        });
136
137        all_promises.push(promise);
138    });
139
140    // Encryption should fail if algorithm of key doesn't match algorithm of function call.
141    passingVectors.forEach(function(vector) {
142        var algorithm = Object.assign({}, vector.algorithm);
143        if (algorithm.name === "AES-CBC") {
144            algorithm.name = "AES-CTR";
145            algorithm.counter = new Uint8Array(16);
146            algorithm.length = 64;
147        } else {
148            algorithm.name = "AES-CBC";
149            algorithm.iv = new Uint8Array(16); // Need syntactically valid parameter to get to error being checked.
150        }
151
152        var promise = importVectorKey(vector, ["encrypt", "decrypt"])
153        .then(function(vector) {
154            promise_test(function(test) {
155                return subtle.encrypt(algorithm, vector.key, vector.plaintext)
156                .then(function(result) {
157                    assert_unreached("encrypt succeeded despite mismatch " + vector.name + ": " + err.message);
158                }, function(err) {
159                    assert_equals(err.name, "InvalidAccessError", "Mismatch should cause InvalidAccessError instead of " + err.message);
160                });
161            }, vector.name + " with mismatched key and algorithm");
162        }, function(err) {
163            // We need a failed test if the importVectorKey operation fails, so
164            // we know we never tested encryption
165            promise_test(function(test) {
166                assert_unreached("importKey failed for " + vector.name);
167            }, "importKey step: " + vector.name + " with mismatched key and algorithm");
168        });
169
170        all_promises.push(promise);
171    });
172
173    // Everything that succeeded decrypting should fail if no "decrypt" usage.
174    passingVectors.forEach(function(vector) {
175        // Don't want to overwrite key being used for success tests!
176        var badVector = Object.assign({}, vector);
177        badVector.key = null;
178
179        var promise = importVectorKey(badVector, ["encrypt"])
180        .then(function(vector) {
181            promise_test(function(test) {
182                return subtle.decrypt(vector.algorithm, vector.key, vector.result)
183                .then(function(result) {
184                    assert_unreached("should have thrown exception for test " + vector.name);
185                }, function(err) {
186                    assert_equals(err.name, "InvalidAccessError", "Should throw an InvalidAccessError instead of " + err.message)
187                });
188            }, vector.name + " without decrypt usage");
189        }, function(err) {
190            // We need a failed test if the importVectorKey operation fails, so
191            // we know we never tested encryption
192            promise_test(function(test) {
193                assert_unreached("importKey failed for " + vector.name);
194            }, "importKey step: " + vector.name + " without decrypt usage");
195        });
196
197        all_promises.push(promise);
198    });
199
200    // Check for OperationError due to data lengths.
201    failingVectors.forEach(function(vector) {
202        var promise = importVectorKey(vector, ["encrypt", "decrypt"])
203        .then(function(vector) {
204            promise_test(function(test) {
205                return subtle.encrypt(vector.algorithm, vector.key, vector.plaintext)
206                .then(function(result) {
207                    assert_unreached("should have thrown exception for test " + vector.name);
208                }, function(err) {
209                    assert_equals(err.name, "OperationError", "Should throw an OperationError instead of " + err.message)
210                });
211            }, vector.name);
212        }, function(err) {
213            // We need a failed test if the importVectorKey operation fails, so
214            // we know we never tested encryption
215            promise_test(function(test) {
216                assert_unreached("importKey failed for " + vector.name);
217            }, "importKey step: " + vector.name);
218        });
219
220        all_promises.push(promise);
221    });
222
223    // Check for OperationError due to data lengths for decryption, too.
224    failingVectors.forEach(function(vector) {
225        var promise = importVectorKey(vector, ["encrypt", "decrypt"])
226        .then(function(vector) {
227            promise_test(function(test) {
228                return subtle.decrypt(vector.algorithm, vector.key, vector.result)
229                .then(function(result) {
230                    assert_unreached("should have thrown exception for test " + vector.name);
231                }, function(err) {
232                    assert_equals(err.name, "OperationError", "Should throw an OperationError instead of " + err.message)
233                });
234            }, vector.name + " decryption");
235        }, function(err) {
236            // We need a failed test if the importVectorKey operation fails, so
237            // we know we never tested encryption
238            promise_test(function(test) {
239                assert_unreached("importKey failed for " + vector.name);
240            }, "importKey step: decryption " + vector.name);
241        });
242
243        all_promises.push(promise);
244    });
245
246    // Check for decryption failing for algorithm-specific reasons (such as bad
247    // padding for AES-CBC).
248    decryptionFailingVectors.forEach(function(vector) {
249        var promise = importVectorKey(vector, ["encrypt", "decrypt"])
250        .then(function(vector) {
251            promise_test(function(test) {
252                return subtle.decrypt(vector.algorithm, vector.key, vector.result)
253                .then(function(result) {
254                    assert_unreached("should have thrown exception for test " + vector.name);
255                }, function(err) {
256                    assert_equals(err.name, "OperationError", "Should throw an OperationError instead of " + err.message)
257                });
258            }, vector.name);
259        }, function(err) {
260            // We need a failed test if the importVectorKey operation fails, so
261            // we know we never tested encryption
262            promise_test(function(test) {
263                assert_unreached("importKey failed for " + vector.name);
264            }, "importKey step: decryption " + vector.name);
265        });
266
267        all_promises.push(promise);
268    });
269
270    promise_test(function() {
271        return Promise.all(all_promises)
272            .then(function() {done();})
273            .catch(function() {done();})
274    }, "setup");
275
276    // A test vector has all needed fields for encryption, EXCEPT that the
277    // key field may be null. This function replaces that null with the Correct
278    // CryptoKey object.
279    //
280    // Returns a Promise that yields an updated vector on success.
281    function importVectorKey(vector, usages) {
282        if (vector.key !== null) {
283            return new Promise(function(resolve, reject) {
284                resolve(vector);
285            });
286        } else {
287            return subtle.importKey("raw", vector.keyBuffer, {name: vector.algorithm.name}, false, usages)
288            .then(function(key) {
289                vector.key = key;
290                return vector;
291            });
292        }
293    }
294
295    // Returns a copy of the sourceBuffer it is sent.
296    function copyBuffer(sourceBuffer) {
297        var source = new Uint8Array(sourceBuffer);
298        var copy = new Uint8Array(sourceBuffer.byteLength)
299
300        for (var i=0; i<source.byteLength; i++) {
301            copy[i] = source[i];
302        }
303
304        return copy;
305    }
306
307    function equalBuffers(a, b) {
308        if (a.byteLength !== b.byteLength) {
309            return false;
310        }
311
312        var aBytes = new Uint8Array(a);
313        var bBytes = new Uint8Array(b);
314
315        for (var i=0; i<a.byteLength; i++) {
316            if (aBytes[i] !== bBytes[i]) {
317                return false;
318            }
319        }
320
321        return true;
322    }
323
324    return;
325}
326