tor-browser

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

successes.js (9554B)


      1 function run_test(algorithmNames, slowTest) {
      2    var subtle = crypto.subtle; // Change to test prefixed implementations
      3 
      4    setup({explicit_timeout: true});
      5 
      6 // These tests check that generateKey successfully creates keys
      7 // when provided any of a wide set of correct parameters
      8 // and that they can be exported afterwards.
      9 //
     10 // There are a lot of combinations of possible parameters,
     11 // resulting in a very large number of tests
     12 // performed.
     13 
     14 
     15 // Setup: define the correct behaviors that should be sought, and create
     16 // helper functions that generate all possible test parameters for
     17 // different situations.
     18 
     19    var allTestVectors = [ // Parameters that should work for generateKey
     20        {name: "AES-CTR",  resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []},
     21        {name: "AES-CBC",  resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []},
     22        {name: "AES-GCM",  resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []},
     23        {name: "AES-OCB",  resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []},
     24        {name: "ChaCha20-Poly1305",  resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []},
     25        {name: "AES-KW",   resultType: CryptoKey, usages: ["wrapKey", "unwrapKey"], mandatoryUsages: []},
     26        {name: "HMAC",     resultType: CryptoKey, usages: ["sign", "verify"], mandatoryUsages: []},
     27        {name: "RSASSA-PKCS1-v1_5", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
     28        {name: "RSA-PSS",  resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
     29        {name: "RSA-OAEP", resultType: "CryptoKeyPair", usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: ["decrypt", "unwrapKey"]},
     30        {name: "ECDSA",    resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
     31        {name: "ECDH",     resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]},
     32        {name: "Ed25519",  resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
     33        {name: "Ed448",    resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
     34        {name: "ML-DSA-44", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
     35        {name: "ML-DSA-65", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
     36        {name: "ML-DSA-87", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
     37        {name: "ML-KEM-512", resultType: "CryptoKeyPair", usages: ["decapsulateBits", "decapsulateKey", "encapsulateBits", "encapsulateKey"], mandatoryUsages: ["decapsulateBits", "decapsulateKey"]},
     38        {name: "ML-KEM-768", resultType: "CryptoKeyPair", usages: ["decapsulateBits", "decapsulateKey", "encapsulateBits", "encapsulateKey"], mandatoryUsages: ["decapsulateBits", "decapsulateKey"]},
     39        {name: "ML-KEM-1024", resultType: "CryptoKeyPair", usages: ["decapsulateBits", "decapsulateKey", "encapsulateBits", "encapsulateKey"], mandatoryUsages: ["decapsulateBits", "decapsulateKey"]},
     40        {name: "X25519",   resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]},
     41        {name: "X448",     resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]},
     42        {name: "KMAC128",  resultType: CryptoKey, usages: ["sign", "verify"], mandatoryUsages: []},
     43        {name: "KMAC256",  resultType: CryptoKey, usages: ["sign", "verify"], mandatoryUsages: []},
     44    ];
     45 
     46    var testVectors = [];
     47    if (algorithmNames && !Array.isArray(algorithmNames)) {
     48        algorithmNames = [algorithmNames];
     49    };
     50    allTestVectors.forEach(function(vector) {
     51        if (!algorithmNames || algorithmNames.includes(vector.name)) {
     52            testVectors.push(vector);
     53        }
     54    });
     55 
     56    function parameterString(algorithm, extractable, usages) {
     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 is successful
     67    function testSuccess(algorithm, extractable, usages, resultType, testTag) {
     68        // algorithm, extractable, and usages are the generateKey parameters
     69        // resultType is the expected result, either the CryptoKey object or "CryptoKeyPair"
     70        // testTag is a string to prepend to the test name.
     71 
     72        promise_test(function(test) {
     73            return subtle.generateKey(algorithm, extractable, usages)
     74            .then(function(result) {
     75                if (resultType === "CryptoKeyPair") {
     76                    assert_goodCryptoKey(result.privateKey, algorithm, extractable, usages, "private");
     77                    assert_goodCryptoKey(result.publicKey, algorithm, true, usages, "public");
     78                } else {
     79                    assert_goodCryptoKey(result, algorithm, extractable, usages, "secret");
     80                }
     81                return result;
     82            }, function(err) {
     83                assert_unreached("generateKey threw an unexpected error: " + err.toString());
     84            })
     85            .then(async function (result) {
     86                // TODO: remove this block to enable ML-KEM JWK when its definition is done in IETF JOSE WG
     87                if (result.publicKey?.algorithm.name.startsWith('ML-KEM')) {
     88                    const promises = [
     89                        subtle.exportKey('spki', result.publicKey),
     90                        extractable ? subtle.exportKey('pkcs8', result.privateKey) : undefined,
     91                        subtle.exportKey('raw-public', result.publicKey),
     92                    ];
     93                    if (extractable)
     94                        promises.push(subtle.exportKey('raw-seed', result.privateKey));
     95                } else if (resultType === "CryptoKeyPair") {
     96                    const promises = [
     97                        subtle.exportKey('jwk', result.publicKey),
     98                        extractable ? subtle.exportKey('jwk', result.privateKey) : undefined,
     99                        subtle.exportKey('spki', result.publicKey),
    100                        extractable ? subtle.exportKey('pkcs8', result.privateKey) : undefined,
    101                    ];
    102 
    103                    switch (result.publicKey.algorithm.name.substring(0, 2)) {
    104                        case 'ML':
    105                            promises.push(subtle.exportKey('raw-public', result.publicKey));
    106                            if (extractable)
    107                                promises.push(subtle.exportKey('raw-seed', result.privateKey));
    108                            break;
    109                        case 'SL':
    110                            promises.push(subtle.exportKey('raw-public', result.publicKey));
    111                            if (extractable)
    112                                promises.push(subtle.exportKey('raw-private', result.privateKey));
    113                            break;
    114                        case 'EC':
    115                        case 'Ed':
    116                        case 'X2':
    117                        case 'X4':
    118                            promises.push(subtle.exportKey('raw', result.publicKey));
    119                            break;
    120                        case 'RS':
    121                            break;
    122                        default:
    123                            throw new Error('not implemented');
    124                    }
    125 
    126                    const [jwkPub, jwkPriv] = await Promise.all(promises);
    127 
    128                    if (extractable) {
    129                        // Test that the JWK public key is a superset of the JWK private key.
    130                        for (const [prop, value] of Object.entries(jwkPub)) {
    131                            if (prop !== 'key_ops') {
    132                                assert_equals(value, jwkPriv[prop], `Property ${prop} is equal in public and private JWK`);
    133                            }
    134                        }
    135                    }
    136                } else {
    137                    if (extractable) {
    138                        await Promise.all([
    139                            subtle.exportKey(/cha|ocb|kmac/i.test(result.algorithm.name) ? 'raw-secret' : 'raw', result),
    140                            subtle.exportKey('jwk', result),
    141                        ]);
    142                    }
    143                }
    144            }, function(err) {
    145                assert_unreached("exportKey threw an unexpected error: " + err.toString());
    146            })
    147        }, testTag + ": generateKey" + parameterString(algorithm, extractable, usages));
    148    }
    149 
    150    // Test all valid sets of parameters for successful
    151    // key generation.
    152    testVectors.forEach(function(vector) {
    153        allNameVariants(vector.name, slowTest).forEach(function(name) {
    154            allAlgorithmSpecifiersFor(name).forEach(function(algorithm) {
    155                allValidUsages(vector.usages, false, vector.mandatoryUsages).forEach(function(usages) {
    156                    [false, true].forEach(function(extractable) {
    157                        subsetTest(testSuccess, algorithm, extractable, usages, vector.resultType, "Success");
    158                    });
    159                });
    160            });
    161        });
    162    });
    163 
    164 }