tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

failures.js (11679B)


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