• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1function define_tests() {
2    // May want to test prefixed implementations.
3    var subtle = self.crypto.subtle;
4
5    // pbkdf2_vectors sets up test data with the correct derivations for each
6    // test case.
7    var testData = getTestData();
8    var passwords = testData.passwords;
9    var salts = testData.salts;
10    var derivations = testData.derivations;
11
12    // What kinds of keys can be created with deriveKey? The following:
13    var derivedKeyTypes = testData.derivedKeyTypes;
14
15    return setUpBaseKeys(passwords)
16    .then(function(allKeys) {
17        // We get several kinds of base keys. Normal ones that can be used for
18        // derivation operations, ones that lack the deriveBits usage, ones
19        // that lack the deriveKeys usage, and one key that is for the wrong
20        // algorithm (not PBKDF2 in this case).
21        var baseKeys = allKeys.baseKeys;
22        var noBits = allKeys.noBits;
23        var noKey = allKeys.noKey;
24        var wrongKey = allKeys.wrongKey;
25
26        // Test each combination of password size, salt size, hash function,
27        // and number of iterations. The derivations object is structured in
28        // that way, so navigate it to run tests and compare with correct results.
29        Object.keys(derivations).forEach(function(passwordSize) {
30            Object.keys(derivations[passwordSize]).forEach(function(saltSize) {
31                Object.keys(derivations[passwordSize][saltSize]).forEach(function(hashName) {
32                    Object.keys(derivations[passwordSize][saltSize][hashName]).forEach(function(iterations) {
33                        var testName = passwordSize + " password, " + saltSize + " salt, " + hashName + ", with " + iterations + " iterations";
34
35                        // Check for correct deriveBits result
36                        subsetTest(promise_test, function(test) {
37                            return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, baseKeys[passwordSize], 256)
38                            .then(function(derivation) {
39                                assert_true(equalBuffers(derivation, derivations[passwordSize][saltSize][hashName][iterations]), "Derived correct key");
40                            }, function(err) {
41                                assert_unreached("deriveBits failed with error " + err.name + ": " + err.message);
42                            });
43                        }, testName);
44
45                        // Check for correct deriveKey results for every kind of
46                        // key that can be created by the deriveKeys operation.
47                        derivedKeyTypes.forEach(function(derivedKeyType) {
48                            var testName = "Derived key of type ";
49                            Object.keys(derivedKeyType.algorithm).forEach(function(prop) {
50                                testName += prop + ": " + derivedKeyType.algorithm[prop] + " ";
51                            });
52                            testName += " using " + passwordSize + " password, " + saltSize + " salt, " + hashName + ", with " + iterations + " iterations";
53
54                            // Test the particular key derivation.
55                            subsetTest(promise_test, function(test) {
56                                return subtle.deriveKey({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, baseKeys[passwordSize], derivedKeyType.algorithm, true, derivedKeyType.usages)
57                                .then(function(key) {
58                                    // Need to export the key to see that the correct bits were set.
59                                    return subtle.exportKey("raw", key)
60                                    .then(function(buffer) {
61                                        assert_true(equalBuffers(buffer, derivations[passwordSize][saltSize][hashName][iterations].slice(0, derivedKeyType.algorithm.length/8)), "Exported key matches correct value");
62                                    }, function(err) {
63                                        assert_unreached("Exporting derived key failed with error " + err.name + ": " + err.message);
64                                    });
65                                }, function(err) {
66                                    assert_unreached("deriveKey failed with error " + err.name + ": " + err.message);
67
68                                });
69                            }, testName);
70
71                            // Test various error conditions for deriveKey:
72
73                            // - illegal name for hash algorithm (NotSupportedError)
74                            var badHash = hashName.substring(0, 3) + hashName.substring(4);
75                            subsetTest(promise_test, function(test) {
76                                return subtle.deriveKey({name: "PBKDF2", salt: salts[saltSize], hash: badHash, iterations: parseInt(iterations)}, baseKeys[passwordSize], derivedKeyType.algorithm, true, derivedKeyType.usages)
77                                .then(function(key) {
78                                    assert_unreached("bad hash name should have thrown an NotSupportedError");
79                                }, function(err) {
80                                    assert_equals(err.name, "NotSupportedError", "deriveKey with bad hash name correctly threw NotSupportedError: " + err.message);
81                                });
82                            }, testName + " with bad hash name " + badHash);
83
84                            // - baseKey usages missing "deriveKey" (InvalidAccessError)
85                            subsetTest(promise_test, function(test) {
86                                return subtle.deriveKey({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, noKey[passwordSize], derivedKeyType.algorithm, true, derivedKeyType.usages)
87                                .then(function(key) {
88                                    assert_unreached("missing deriveKey usage should have thrown an InvalidAccessError");
89                                }, function(err) {
90                                    assert_equals(err.name, "InvalidAccessError", "deriveKey with missing deriveKey usage correctly threw InvalidAccessError: " + err.message);
91                                });
92                            }, testName + " with missing deriveKey usage");
93
94                            // - baseKey algorithm does not match PBKDF2 (InvalidAccessError)
95                            subsetTest(promise_test, function(test) {
96                                return subtle.deriveKey({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, wrongKey, derivedKeyType.algorithm, true, derivedKeyType.usages)
97                                .then(function(key) {
98                                    assert_unreached("wrong (ECDH) key should have thrown an InvalidAccessError");
99                                }, function(err) {
100                                    assert_equals(err.name, "InvalidAccessError", "deriveKey with wrong (ECDH) key correctly threw InvalidAccessError: " + err.message);
101                                });
102                            }, testName + " with wrong (ECDH) key");
103
104                        });
105
106                        // Test various error conditions for deriveBits below:
107                        // length null (OperationError)
108                        subsetTest(promise_test, function(test) {
109                            return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, baseKeys[passwordSize], null)
110                            .then(function(derivation) {
111                                assert_unreached("null length should have thrown an OperationError");
112                            }, function(err) {
113                                assert_equals(err.name, "OperationError", "deriveBits with null length correctly threw OperationError: " + err.message);
114                            });
115                        }, testName + " with null length");
116
117                        // 0 length (OperationError)
118                        subsetTest(promise_test, function(test) {
119                            return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, baseKeys[passwordSize], 0)
120                            .then(function(derivation) {
121                                assert_unreached("0 length should have thrown an OperationError");
122                            }, function(err) {
123                                assert_equals(err.name, "OperationError", "deriveBits with 0 length correctly threw OperationError: " + err.message);
124                            });
125                        }, testName + " with 0 length");
126
127                        // length not multiple of 8 (OperationError)
128                        subsetTest(promise_test, function(test) {
129                            return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, baseKeys[passwordSize], 44)
130                            .then(function(derivation) {
131                                assert_unreached("non-multiple of 8 length should have thrown an OperationError");
132                            }, function(err) {
133                                assert_equals(err.name, "OperationError", "deriveBits with non-multiple of 8 length correctly threw OperationError: " + err.message);
134                            });
135                        }, testName + " with non-multiple of 8 length");
136
137                        // - illegal name for hash algorithm (NotSupportedError)
138                        var badHash = hashName.substring(0, 3) + hashName.substring(4);
139                        subsetTest(promise_test, function(test) {
140                            return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: badHash, iterations: parseInt(iterations)}, baseKeys[passwordSize], 256)
141                            .then(function(derivation) {
142                                assert_unreached("bad hash name should have thrown an NotSupportedError");
143                            }, function(err) {
144                                assert_equals(err.name, "NotSupportedError", "deriveBits with bad hash name correctly threw NotSupportedError: " + err.message);
145                            });
146                        }, testName + " with bad hash name " + badHash);
147
148                        // - baseKey usages missing "deriveBits" (InvalidAccessError)
149                        subsetTest(promise_test, function(test) {
150                            return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, noBits[passwordSize], 256)
151                            .then(function(derivation) {
152                                assert_unreached("missing deriveBits usage should have thrown an InvalidAccessError");
153                            }, function(err) {
154                                assert_equals(err.name, "InvalidAccessError", "deriveBits with missing deriveBits usage correctly threw InvalidAccessError: " + err.message);
155                            });
156                        }, testName + " with missing deriveBits usage");
157
158                        // - baseKey algorithm does not match PBKDF2 (InvalidAccessError)
159                        subsetTest(promise_test, function(test) {
160                            return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, wrongKey, 256)
161                            .then(function(derivation) {
162                                assert_unreached("wrong (ECDH) key should have thrown an InvalidAccessError");
163                            }, function(err) {
164                                assert_equals(err.name, "InvalidAccessError", "deriveBits with wrong (ECDH) key correctly threw InvalidAccessError: " + err.message);
165                            });
166                        }, testName + " with wrong (ECDH) key");
167                    });
168
169                    // Check that 0 iterations throws proper error
170                    subsetTest(promise_test, function(test) {
171                        return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: 0}, baseKeys[passwordSize], 256)
172                        .then(function(derivation) {
173                            assert_unreached("0 iterations should have thrown an error");
174                        }, function(err) {
175                            assert_equals(err.name, "OperationError", "deriveBits with 0 iterations correctly threw OperationError: " + err.message);
176                        });
177                    }, passwordSize + " password, " + saltSize + " salt, " + hashName + ", with 0 iterations");
178
179                    derivedKeyTypes.forEach(function(derivedKeyType) {
180                        var testName = "Derived key of type ";
181                        Object.keys(derivedKeyType.algorithm).forEach(function(prop) {
182                            testName += prop + ": " + derivedKeyType.algorithm[prop] + " ";
183                        });
184                        testName += " using " + passwordSize + " password, " + saltSize + " salt, " + hashName + ", with 0 iterations";
185
186                        subsetTest(promise_test, function(test) {
187                            return subtle.deriveKey({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: 0}, baseKeys[passwordSize], derivedKeyType.algorithm, true, derivedKeyType.usages)
188                            .then(function(derivation) {
189                                assert_unreached("0 iterations should have thrown an error");
190                            }, function(err) {
191                                assert_equals(err.name, "OperationError", "derivekey with 0 iterations correctly threw OperationError: " + err.message);
192                            });
193                        }, testName);
194                    });
195                });
196
197                // - legal algorithm name but not digest one (e.g., PBKDF2) (NotSupportedError)
198                var nonDigestHash = "PBKDF2";
199                [1, 1000, 100000].forEach(function(iterations) {
200                    var testName = passwordSize + " password, " + saltSize + " salt, " + nonDigestHash + ", with " + iterations + " iterations";
201
202                    subsetTest(promise_test, function(test) {
203                        return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: nonDigestHash, iterations: parseInt(iterations)}, baseKeys[passwordSize], 256)
204                        .then(function(derivation) {
205                            assert_unreached("non-digest algorithm should have thrown an NotSupportedError");
206                        }, function(err) {
207                            assert_equals(err.name, "NotSupportedError", "deriveBits with non-digest algorithm correctly threw NotSupportedError: " + err.message);
208                        });
209                    }, testName + " with non-digest algorithm " + nonDigestHash);
210
211                    derivedKeyTypes.forEach(function(derivedKeyType) {
212                        var testName = "Derived key of type ";
213                        Object.keys(derivedKeyType.algorithm).forEach(function(prop) {
214                            testName += prop + ": " + derivedKeyType.algorithm[prop] + " ";
215                        });
216                        testName += " using " + passwordSize + " password, " + saltSize + " salt, " + nonDigestHash + ", with " + iterations + " iterations";
217
218                        subsetTest(promise_test, function(test) {
219                            return subtle.deriveKey({name: "PBKDF2", salt: salts[saltSize], hash: nonDigestHash, iterations: parseInt(iterations)}, baseKeys[passwordSize], derivedKeyType.algorithm, true, derivedKeyType.usages)
220                            .then(function(derivation) {
221                                assert_unreached("non-digest algorithm should have thrown an NotSupportedError");
222                            }, function(err) {
223                                assert_equals(err.name, "NotSupportedError", "derivekey with non-digest algorithm correctly threw NotSupportedError: " + err.message);
224                            });
225                        }, testName);
226                    });
227
228                });
229
230            });
231        });
232    });
233
234    // Deriving bits and keys requires starting with a base key, which is created
235    // by importing a password. setUpBaseKeys returns a promise that yields the
236    // necessary base keys.
237    function setUpBaseKeys(passwords) {
238        var promises = [];
239
240        var baseKeys = {};
241        var noBits = {};
242        var noKey = {};
243        var wrongKey = null;
244
245        Object.keys(passwords).forEach(function(passwordSize) {
246            var promise = subtle.importKey("raw", passwords[passwordSize], {name: "PBKDF2"}, false, ["deriveKey", "deriveBits"])
247            .then(function(baseKey) {
248                baseKeys[passwordSize] = baseKey;
249            }, function(err) {
250                baseKeys[passwordSize] = null;
251            });
252            promises.push(promise);
253
254            promise = subtle.importKey("raw", passwords[passwordSize], {name: "PBKDF2"}, false, ["deriveBits"])
255            .then(function(baseKey) {
256                noKey[passwordSize] = baseKey;
257            }, function(err) {
258                noKey[passwordSize] = null;
259            });
260            promises.push(promise);
261
262            promise = subtle.importKey("raw", passwords[passwordSize], {name: "PBKDF2"}, false, ["deriveKey"])
263            .then(function(baseKey) {
264                noBits[passwordSize] = baseKey;
265            }, function(err) {
266                noBits[passwordSize] = null;
267            });
268            promises.push(promise);
269        });
270
271        var promise = subtle.generateKey({name: "ECDH", namedCurve: "P-256"}, false, ["deriveKey", "deriveBits"])
272        .then(function(baseKey) {
273            wrongKey = baseKey.privateKey;
274        }, function(err) {
275            wrongKey = null;
276        });
277        promises.push(promise);
278
279
280        return Promise.all(promises).then(function() {
281            return {baseKeys: baseKeys, noBits: noBits, noKey: noKey, wrongKey: wrongKey};
282        });
283    }
284
285    function equalBuffers(a, b) {
286        if (a.byteLength !== b.byteLength) {
287            return false;
288        }
289
290        var aBytes = new Uint8Array(a);
291        var bBytes = new Uint8Array(b);
292
293        for (var i=0; i<a.byteLength; i++) {
294            if (aBytes[i] !== bBytes[i]) {
295                return false;
296            }
297        }
298
299        return true;
300    }
301
302}
303