@ 261107.7
current
// These tests check that generateKey throws an error, and that
// the error is of the right type, for a wide set of incorrect parameters.
//
// Error testing occurs by setting the parameter that should trigger the
// error to an invalid value, then combining that with all valid
// parameters that should be checked earlier by generateKey, and all
// valid and invalid parameters that should be checked later by
// generateKey.
//
// There are a lot of combinations of possible parameters for both
// success and failure modes, resulting in a very large number of tests
// performed.


// Setup: define the correct behaviors that should be sought, and create
// helper functions that generate all possible test parameters for
// different situations.

function parameterString(algorithm, extractable, usages) {
    if (typeof algorithm !== "object" && typeof algorithm !== "string") {
        alert(algorithm);
    }

    var result = "(" +
                    objectToString(algorithm) + ", " +
                    objectToString(extractable) + ", " +
                    objectToString(usages) +
                 ")";

    return result;
}

// Test that a given combination of parameters results in an error,
// AND that it is the correct kind of error.
//
// Expected error is either a number, tested against the error code,
// or a string, tested against the error name.
function testError(algorithm, extractable, usages, expectedError, testTag) {
    promise_test(function(test) {
        return crypto.subtle.generateKey(algorithm, extractable, usages)
        .then(function(result) {
            assert_unreached("Operation succeeded, but should not have");
        }, function(err) {
            if (typeof expectedError === "number") {
                assert_equals(err.code, expectedError, testTag + " not supported");
            } else {
                assert_equals(err.name, expectedError, testTag + " not supported");
            }
        });
    }, testTag + ": generateKey" + parameterString(algorithm, extractable, usages));
}


// Algorithm normalization happens before generateKey looks at any other
// argument, so these cases are independent of the algorithm under test and
// only need to run once for the whole suite.
function run_bad_algorithm_test() {
    // Algorithm normalization should fail with "Not supported"
    var badAlgorithmNames = [
        "AES",
        {name: "AES"},
        {name: "AES", length: 128},
        {name: "AES-CMAC", length: 128},    // Removed after CR
        {name: "AES-CFB", length: 128},      // Removed after CR
        {name: "HMAC", hash: "MD5"},
        {name: "RSA", hash: "SHA-256", modulusLength: 2048, publicExponent: new Uint8Array([1,0,1])},
        {name: "RSA-PSS", hash: "SHA", modulusLength: 2048, publicExponent: new Uint8Array([1,0,1])},
        {name: "EC", namedCurve: "P521"}
    ];


    // Algorithm normalization failures should be found first
    // - all other parameters can be good or bad, should fail
    //   due to NotSupportedError.
    badAlgorithmNames.forEach(function(algorithm) {
        allValidUsages(["decrypt", "sign", "deriveBits"], true, []) // Small search space, shouldn't matter because should fail before used
        .forEach(function(usages) {
            [false, true, "RED", 7].forEach(function(extractable){
                testError(algorithm, extractable, usages, "NotSupportedError", "Bad algorithm");
            });
        });
    });

    // Empty algorithm should fail with TypeError
    allValidUsages(["decrypt", "sign", "deriveBits"], true, []) // Small search space, shouldn't matter because should fail before used
        .forEach(function(usages) {
            [false, true, "RED", 7].forEach(function(extractable){
                testError({}, extractable, usages, "TypeError", "Empty algorithm");
            });
        });
}


function run_test(algorithmNames) {
    var testVectors = getGenerateKeyTestVectors(algorithmNames);


    // Given an algorithm name, create several invalid parameters.
    function badAlgorithmPropertySpecifiersFor(algorithmName) {
        var results = [];

        if (algorithmName.toUpperCase().substring(0, 3) === "AES") {
            // Specifier properties are name and length
            [64, 127, 129, 255, 257, 512, 128 + 2**32, 128 - 2**32].forEach(function(length) {
                results.push({name: algorithmName, length: length});
            });
        } else if (algorithmName.toUpperCase() === "HMAC") {
            [128 + 2**32, 128 - 2**32].forEach(function(length) {
                results.push({name: algorithmName, hash: "SHA-256", length: length});
            });
        } else if (algorithmName.toUpperCase().substring(0, 3) === "RSA") {
            [new Uint8Array([1]), new Uint8Array([1,0,0])].forEach(function(publicExponent) {
                results.push({name: algorithmName, hash: "SHA-256", modulusLength: 1024, publicExponent: publicExponent});
            });
            results.push({name: algorithmName, hash: "SHA-256", modulusLength: 1024 + 2 ** 32, publicExponent: new Uint8Array([1,0,1])});
            results.push({name: algorithmName, hash: "SHA-256", modulusLength: 1024 - 2 ** 32, publicExponent: new Uint8Array([1,0,1])});
        } else if (algorithmName.toUpperCase().substring(0, 2) === "EC") {
            ["P-512", "Curve25519"].forEach(function(curveName) {
                results.push({name: algorithmName, namedCurve: curveName});
            });
        }

        return results;
    }


    // Don't create an exhaustive list of all invalid usages because
    // there would be too many to test. Instead, create every singleton
    // of an illegal usage, and "poison" every valid usage
    // with an illegal one.
    function invalidUsages(validUsages, mandatoryUsages) {
        var results = [];

        var illegalUsages = [];
        allKeyUsages.forEach(function(usage) {
            if (!validUsages.includes(usage)) {
                illegalUsages.push(usage);
            }
        });

        var goodUsageCombinations = allValidUsages(validUsages, false, mandatoryUsages);

        illegalUsages.forEach(function(illegalUsage) {
            results.push([illegalUsage]);
            goodUsageCombinations.forEach(function(usageCombination) {
                results.push(usageCombination.concat([illegalUsage]));
            });
        });

        return results;
    }


// Now test for properly handling errors
// - Bad usages for algorithm
// - Bad key lengths

    // Algorithms normalize okay, but usages bad (though not empty).
    // It shouldn't matter what other extractable is. Should fail
    // due to SyntaxError
    testVectors.forEach(function(vector) {
        var name = vector.name;

        allAlgorithmSpecifiersFor(name).forEach(function(algorithm) {
            invalidUsages(vector.usages, vector.mandatoryUsages).forEach(function(usages) {
                [true].forEach(function(extractable) {
                    testError(algorithm, extractable, usages, "SyntaxError", "Bad usages");
                });
            });
        });
    });


    // Other algorithm properties should be checked next, so try good
    // algorithm names and usages, but bad algorithm properties next.
    // - Special case: normally bad usage [] isn't checked until after properties,
    //   so it's included in this test case. It should NOT cause an error.
    testVectors.forEach(function(vector) {
        var name = vector.name;
        badAlgorithmPropertySpecifiersFor(name).forEach(function(algorithm) {
            allValidUsages(vector.usages, true, vector.mandatoryUsages)
            .forEach(function(usages) {
                [false, true].forEach(function(extractable) {
                    if (name.substring(0,2) === "EC") {
                        testError(algorithm, extractable, usages, "NotSupportedError", "Bad algorithm property");
                    } else if (name.substring(0,3) === "RSA" && (algorithm.modulusLength < 0 || algorithm.modulusLength > 2**32) ||
                        (name.substring(0,3) === "AES" || name === "HMAC") && (algorithm.length < 0 || algorithm.length > 2**32)) {
                        testError(algorithm, extractable, usages, "TypeError", "Bad algorithm property");
                    } else {
                        testError(algorithm, extractable, usages, "OperationError", "Bad algorithm property");
                    }
                });
            });
        });
    });


    // The last thing that should be checked is empty usages (disallowed for secret and private keys).
    testVectors.forEach(function(vector) {
        var name = vector.name;

        allAlgorithmSpecifiersFor(name).forEach(function(algorithm) {
            var usages = [];
            [false, true].forEach(function(extractable) {
                testError(algorithm, extractable, usages, "SyntaxError", "Empty usages");
            });
        });
    });


}