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