1function run_test(algorithmNames) {
2    var subtle = crypto.subtle; // Change to test prefixed implementations
3
4    setup({explicit_timeout: true});
5
6// These tests check that generateKey throws an error, and that
7// the error is of the right type, for a wide set of incorrect parameters.
8//
9// Error testing occurs by setting the parameter that should trigger the
10// error to an invalid value, then combining that with all valid
11// parameters that should be checked earlier by generateKey, and all
12// valid and invalid parameters that should be checked later by
13// generateKey.
14//
15// There are a lot of combinations of possible parameters for both
16// success and failure modes, resulting in a very large number of tests
17// performed.
18
19
20// Setup: define the correct behaviors that should be sought, and create
21// helper functions that generate all possible test parameters for
22// different situations.
23
24    var allTestVectors = [ // Parameters that should work for generateKey
25        {name: "AES-CTR",  resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []},
26        {name: "AES-CBC",  resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []},
27        {name: "AES-GCM",  resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []},
28        {name: "AES-KW",   resultType: CryptoKey, usages: ["wrapKey", "unwrapKey"], mandatoryUsages: []},
29        {name: "HMAC",     resultType: CryptoKey, usages: ["sign", "verify"], mandatoryUsages: []},
30        {name: "RSASSA-PKCS1-v1_5", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
31        {name: "RSA-PSS",  resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
32        {name: "RSA-OAEP", resultType: "CryptoKeyPair", usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: ["decrypt", "unwrapKey"]},
33        {name: "ECDSA",    resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
34        {name: "ECDH",     resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]},
35        {name: "Ed25519",  resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
36        {name: "Ed448",    resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
37        {name: "X25519",   resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]},
38        {name: "X448",     resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]},
39    ];
40
41    var testVectors = [];
42    if (algorithmNames && !Array.isArray(algorithmNames)) {
43        algorithmNames = [algorithmNames];
44    };
45    allTestVectors.forEach(function(vector) {
46        if (!algorithmNames || algorithmNames.includes(vector.name)) {
47            testVectors.push(vector);
48        }
49    });
50
51
52    function parameterString(algorithm, extractable, usages) {
53        if (typeof algorithm !== "object" && typeof algorithm !== "string") {
54            alert(algorithm);
55        }
56
57        var result = "(" +
58                        objectToString(algorithm) + ", " +
59                        objectToString(extractable) + ", " +
60                        objectToString(usages) +
61                     ")";
62
63        return result;
64    }
65
66    // Test that a given combination of parameters results in an error,
67    // AND that it is the correct kind of error.
68    //
69    // Expected error is either a number, tested against the error code,
70    // or a string, tested against the error name.
71    function testError(algorithm, extractable, usages, expectedError, testTag) {
72        promise_test(function(test) {
73            return crypto.subtle.generateKey(algorithm, extractable, usages)
74            .then(function(result) {
75                assert_unreached("Operation succeeded, but should not have");
76            }, function(err) {
77                if (typeof expectedError === "number") {
78                    assert_equals(err.code, expectedError, testTag + " not supported");
79                } else {
80                    assert_equals(err.name, expectedError, testTag + " not supported");
81                }
82            });
83        }, testTag + ": generateKey" + parameterString(algorithm, extractable, usages));
84    }
85
86
87    // Given an algorithm name, create several invalid parameters.
88    function badAlgorithmPropertySpecifiersFor(algorithmName) {
89        var results = [];
90
91        if (algorithmName.toUpperCase().substring(0, 3) === "AES") {
92            // Specifier properties are name and length
93            [64, 127, 129, 255, 257, 512].forEach(function(length) {
94                results.push({name: algorithmName, length: length});
95            });
96        } else if (algorithmName.toUpperCase().substring(0, 3) === "RSA") {
97            [new Uint8Array([1]), new Uint8Array([1,0,0])].forEach(function(publicExponent) {
98                results.push({name: algorithmName, hash: "SHA-256", modulusLength: 1024, publicExponent: publicExponent});
99            });
100        } else if (algorithmName.toUpperCase().substring(0, 2) === "EC") {
101            ["P-512", "Curve25519"].forEach(function(curveName) {
102                results.push({name: algorithmName, namedCurve: curveName});
103            });
104        }
105
106        return results;
107    }
108
109
110    // Don't create an exhaustive list of all invalid usages,
111    // because there would usually be nearly 2**8 of them,
112    // way too many to test. Instead, create every singleton
113    // of an illegal usage, and "poison" every valid usage
114    // with an illegal one.
115    function invalidUsages(validUsages, mandatoryUsages) {
116        var results = [];
117
118        var illegalUsages = [];
119        ["encrypt", "decrypt", "sign", "verify", "wrapKey", "unwrapKey", "deriveKey", "deriveBits"].forEach(function(usage) {
120            if (!validUsages.includes(usage)) {
121                illegalUsages.push(usage);
122            }
123        });
124
125        var goodUsageCombinations = allValidUsages(validUsages, false, mandatoryUsages);
126
127        illegalUsages.forEach(function(illegalUsage) {
128            results.push([illegalUsage]);
129            goodUsageCombinations.forEach(function(usageCombination) {
130                results.push(usageCombination.concat([illegalUsage]));
131            });
132        });
133
134        return results;
135    }
136
137
138// Now test for properly handling errors
139// - Unsupported algorithm
140// - Bad usages for algorithm
141// - Bad key lengths
142
143    // Algorithm normalization should fail with "Not supported"
144    var badAlgorithmNames = [
145        "AES",
146        {name: "AES"},
147        {name: "AES", length: 128},
148        {name: "AES-CMAC", length: 128},    // Removed after CR
149        {name: "AES-CFB", length: 128},      // Removed after CR
150        {name: "HMAC", hash: "MD5"},
151        {name: "RSA", hash: "SHA-256", modulusLength: 2048, publicExponent: new Uint8Array([1,0,1])},
152        {name: "RSA-PSS", hash: "SHA", modulusLength: 2048, publicExponent: new Uint8Array([1,0,1])},
153        {name: "EC", namedCurve: "P521"}
154    ];
155
156
157    // Algorithm normalization failures should be found first
158    // - all other parameters can be good or bad, should fail
159    //   due to NotSupportedError.
160    badAlgorithmNames.forEach(function(algorithm) {
161        allValidUsages(["decrypt", "sign", "deriveBits"], true, []) // Small search space, shouldn't matter because should fail before used
162        .forEach(function(usages) {
163            [false, true, "RED", 7].forEach(function(extractable){
164                testError(algorithm, extractable, usages, "NotSupportedError", "Bad algorithm");
165            });
166        });
167    });
168
169
170    // Algorithms normalize okay, but usages bad (though not empty).
171    // It shouldn't matter what other extractable is. Should fail
172    // due to SyntaxError
173    testVectors.forEach(function(vector) {
174        var name = vector.name;
175
176        allAlgorithmSpecifiersFor(name).forEach(function(algorithm) {
177            invalidUsages(vector.usages, vector.mandatoryUsages).forEach(function(usages) {
178                [true].forEach(function(extractable) {
179                    testError(algorithm, extractable, usages, "SyntaxError", "Bad usages");
180                });
181            });
182        });
183    });
184
185
186    // Other algorithm properties should be checked next, so try good
187    // algorithm names and usages, but bad algorithm properties next.
188    // - Special case: normally bad usage [] isn't checked until after properties,
189    //   so it's included in this test case. It should NOT cause an error.
190    testVectors.forEach(function(vector) {
191        var name = vector.name;
192        badAlgorithmPropertySpecifiersFor(name).forEach(function(algorithm) {
193            allValidUsages(vector.usages, true, vector.mandatoryUsages)
194            .forEach(function(usages) {
195                [false, true].forEach(function(extractable) {
196                    if (name.substring(0,2) === "EC") {
197                        testError(algorithm, extractable, usages, "NotSupportedError", "Bad algorithm property");
198                    } else {
199                        testError(algorithm, extractable, usages, "OperationError", "Bad algorithm property");
200                    }
201                });
202            });
203        });
204    });
205
206
207    // The last thing that should be checked is empty usages (disallowed for secret and private keys).
208    testVectors.forEach(function(vector) {
209        var name = vector.name;
210
211        allAlgorithmSpecifiersFor(name).forEach(function(algorithm) {
212            var usages = [];
213            [false, true].forEach(function(extractable) {
214                testError(algorithm, extractable, usages, "SyntaxError", "Empty usages");
215            });
216        });
217    });
218
219
220}
221