tor-browser

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

getPublicKey.tentative.https.any.js (8857B)


      1 // META: title=WebCryptoAPI: getPublicKey() method
      2 // META: timeout=long
      3 // META: script=util/helpers.js
      4 
      5 "use strict";
      6 
      7 const algorithms = [
      8    {
      9        name: "ECDH",
     10        generateKeyParams: { name: "ECDH", namedCurve: "P-256" },
     11        usages: ["deriveKey", "deriveBits"],
     12        publicKeyUsages: []
     13    },
     14    {
     15        name: "ECDSA",
     16        generateKeyParams: { name: "ECDSA", namedCurve: "P-256" },
     17        usages: ["sign", "verify"],
     18        publicKeyUsages: ["verify"]
     19    },
     20    {
     21        name: "Ed25519",
     22        generateKeyParams: { name: "Ed25519" },
     23        usages: ["sign", "verify"],
     24        publicKeyUsages: ["verify"]
     25    },
     26    {
     27        name: "RSA-OAEP",
     28        generateKeyParams: {
     29            name: "RSA-OAEP",
     30            modulusLength: 2048,
     31            publicExponent: new Uint8Array([1, 0, 1]),
     32            hash: "SHA-256"
     33        },
     34        usages: ["encrypt", "decrypt"],
     35        publicKeyUsages: ["encrypt"]
     36    },
     37    {
     38        name: "RSA-PSS",
     39        generateKeyParams: {
     40            name: "RSA-PSS",
     41            modulusLength: 2048,
     42            publicExponent: new Uint8Array([1, 0, 1]),
     43            hash: "SHA-256"
     44        },
     45        usages: ["sign", "verify"],
     46        publicKeyUsages: ["verify"]
     47    },
     48    {
     49        name: "RSASSA-PKCS1-v1_5",
     50        generateKeyParams: {
     51            name: "RSASSA-PKCS1-v1_5",
     52            modulusLength: 2048,
     53            publicExponent: new Uint8Array([1, 0, 1]),
     54            hash: "SHA-256"
     55        },
     56        usages: ["sign", "verify"],
     57        publicKeyUsages: ["verify"]
     58    },
     59    {
     60        name: "X25519",
     61        generateKeyParams: { name: "X25519" },
     62        usages: ["deriveKey", "deriveBits"],
     63        publicKeyUsages: []
     64    }
     65 ];
     66 
     67 // Test basic functionality for supported algorithms
     68 algorithms.forEach(function(algorithm) {
     69    promise_test(async function(t) {
     70        // Generate a key pair
     71        const keyPair = await crypto.subtle.generateKey(
     72            algorithm.generateKeyParams,
     73            false, // extractable
     74            algorithm.usages
     75        );
     76 
     77        assert_true(keyPair.privateKey instanceof CryptoKey, "Generated private key");
     78        assert_equals(keyPair.privateKey.type, "private", "Private key type");
     79 
     80        // Test getPublicKey with valid usages
     81        const publicKey = await crypto.subtle.getPublicKey(
     82            keyPair.privateKey,
     83            algorithm.publicKeyUsages
     84        );
     85 
     86        // Verify the returned public key
     87        assert_true(publicKey instanceof CryptoKey, "getPublicKey returns a CryptoKey");
     88        assert_equals(publicKey.type, "public", "Returned key is public");
     89        assert_equals(publicKey.algorithm.name, algorithm.name, "Algorithm name matches");
     90        assert_true(publicKey.extractable, "Public key is extractable");
     91 
     92        // Verify usages
     93        assert_equals(publicKey.usages.length, algorithm.publicKeyUsages.length, "Usage count matches");
     94        algorithm.publicKeyUsages.forEach(function(usage) {
     95            assert_true(publicKey.usages.includes(usage), `Has ${usage} usage`);
     96        });
     97 
     98        // Verify that the derived public key matches the original public key
     99        // by comparing their exported forms
    100        const originalExported = await crypto.subtle.exportKey("spki", keyPair.publicKey);
    101        const derivedExported = await crypto.subtle.exportKey("spki", publicKey);
    102 
    103        assert_array_equals(
    104            new Uint8Array(originalExported),
    105            new Uint8Array(derivedExported),
    106            "Exported public keys match"
    107        );
    108 
    109    }, `getPublicKey works for ${algorithm.name}`);
    110 });
    111 
    112 // Test functional equivalence - ensure derived public key works for crypto operations
    113 promise_test(async function(t) {
    114    const keyPair = await crypto.subtle.generateKey(
    115        { name: "ECDSA", namedCurve: "P-256" },
    116        false,
    117        ["sign", "verify"]
    118    );
    119 
    120    const derivedPublicKey = await crypto.subtle.getPublicKey(
    121        keyPair.privateKey,
    122        ["verify"]
    123    );
    124 
    125    // Create test data
    126    const data = new TextEncoder().encode("test message");
    127 
    128    // Sign with private key
    129    const signature = await crypto.subtle.sign(
    130        { name: "ECDSA", hash: "SHA-256" },
    131        keyPair.privateKey,
    132        data
    133    );
    134 
    135    // Verify with both original and derived public keys
    136    const verifyOriginal = await crypto.subtle.verify(
    137        { name: "ECDSA", hash: "SHA-256" },
    138        keyPair.publicKey,
    139        signature,
    140        data
    141    );
    142 
    143    const verifyDerived = await crypto.subtle.verify(
    144        { name: "ECDSA", hash: "SHA-256" },
    145        derivedPublicKey,
    146        signature,
    147        data
    148    );
    149 
    150    assert_true(verifyOriginal, "Original public key verifies signature");
    151    assert_true(verifyDerived, "Derived public key verifies signature");
    152 
    153 }, "Derived public key is functionally equivalent to original public key");
    154 
    155 // Test with empty usages array
    156 algorithms.forEach(function(algorithm) {
    157    promise_test(async function(t) {
    158        // Skip X25519 if not supported
    159        if (algorithm.name === "X25519") {
    160            try {
    161                await crypto.subtle.generateKey(algorithm.generateKeyParams, false, algorithm.usages);
    162            } catch (e) {
    163                if (e.name === "NotSupportedError") {
    164                    return;
    165                }
    166                throw e;
    167            }
    168        }
    169 
    170        const keyPair = await crypto.subtle.generateKey(
    171            algorithm.generateKeyParams,
    172            false,
    173            algorithm.usages
    174        );
    175 
    176        // Test with empty usages array
    177        const publicKey = await crypto.subtle.getPublicKey(
    178            keyPair.privateKey,
    179            []
    180        );
    181 
    182        assert_true(publicKey instanceof CryptoKey, "getPublicKey returns a CryptoKey");
    183        assert_equals(publicKey.type, "public", "Returned key is public");
    184        assert_equals(publicKey.usages.length, 0, "Public key has no usages");
    185 
    186    }, `getPublicKey works with empty usages for ${algorithm.name}`);
    187 });
    188 
    189 // Test error cases
    190 
    191 // Test with non-private key (should throw InvalidAccessError)
    192 promise_test(async function(t) {
    193    const keyPair = await crypto.subtle.generateKey(
    194        { name: "ECDSA", namedCurve: "P-256" },
    195        false,
    196        ["sign", "verify"]
    197    );
    198 
    199    await promise_rejects_dom(t, "InvalidAccessError",
    200        crypto.subtle.getPublicKey(keyPair.publicKey, ["verify"]),
    201        "getPublicKey should reject when called with a public key"
    202    );
    203 }, "getPublicKey rejects with InvalidAccessError when given a public key");
    204 
    205 // Test with symmetric keys (should throw NotSupportedError for non-private keys)
    206 promise_test(async function(t) {
    207    const aesKey = await crypto.subtle.generateKey(
    208        { name: "AES-GCM", length: 256 },
    209        false,
    210        ["encrypt", "decrypt"]
    211    );
    212 
    213    await promise_rejects_dom(t, "NotSupportedError",
    214        crypto.subtle.getPublicKey(aesKey, []),
    215        "getPublicKey should reject AES-GCM keys"
    216    );
    217 }, "getPublicKey rejects with NotSupportedError for AES-GCM symmetric keys");
    218 
    219 promise_test(async function(t) {
    220    const hmacKey = await crypto.subtle.generateKey(
    221        { name: "HMAC", hash: "SHA-256" },
    222        false,
    223        ["sign", "verify"]
    224    );
    225 
    226    await promise_rejects_dom(t, "NotSupportedError",
    227        crypto.subtle.getPublicKey(hmacKey, []),
    228        "getPublicKey should reject HMAC keys"
    229    );
    230 }, "getPublicKey rejects with NotSupportedError for HMAC symmetric keys");
    231 
    232 // Test with invalid usages for the algorithm
    233 promise_test(async function(t) {
    234    const keyPair = await crypto.subtle.generateKey(
    235        { name: "ECDSA", namedCurve: "P-256" },
    236        false,
    237        ["sign", "verify"]
    238    );
    239 
    240    // Try to use "encrypt" usage with ECDSA (not supported for ECDSA public keys)
    241    await promise_rejects_dom(t, "SyntaxError",
    242        crypto.subtle.getPublicKey(keyPair.privateKey, ["encrypt"]),
    243        "getPublicKey should reject invalid usages for the algorithm"
    244    );
    245 }, "getPublicKey rejects with SyntaxError for invalid usages");
    246 
    247 // Test with mixed valid and invalid usages
    248 promise_test(async function(t) {
    249    const keyPair = await crypto.subtle.generateKey(
    250        { name: "ECDSA", namedCurve: "P-256" },
    251        false,
    252        ["sign", "verify"]
    253    );
    254 
    255    // Mix valid ("verify") and invalid ("encrypt") usages
    256    await promise_rejects_dom(t, "SyntaxError",
    257        crypto.subtle.getPublicKey(keyPair.privateKey, ["verify", "encrypt"]),
    258        "getPublicKey should reject when any usage is invalid"
    259    );
    260 }, "getPublicKey rejects with SyntaxError when any usage is invalid for the algorithm");
    261 
    262 // Test that the method exists
    263 test(function() {
    264    assert_true("getPublicKey" in crypto.subtle, "getPublicKey method exists on SubtleCrypto");
    265    assert_equals(typeof crypto.subtle.getPublicKey, "function", "getPublicKey is a function");
    266 }, "getPublicKey method is available");