tor-browser

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

encap_decap_bits.tentative.https.any.js (5828B)


      1 // META: title=WebCryptoAPI: ML-KEM encapsulateBits() and decapsulateBits() tests
      2 // META: script=ml_kem_vectors.js
      3 // META: script=../util/helpers.js
      4 // META: timeout=long
      5 
      6 function define_bits_tests() {
      7  var subtle = self.crypto.subtle;
      8  var variants = ['ML-KEM-512', 'ML-KEM-768', 'ML-KEM-1024'];
      9 
     10  variants.forEach(function (algorithmName) {
     11    // Test encapsulateBits operation
     12    promise_test(async function (test) {
     13      // Generate a key pair for testing
     14      var keyPair = await subtle.generateKey({ name: algorithmName }, false, [
     15        'encapsulateBits',
     16        'decapsulateBits',
     17      ]);
     18 
     19      // Test encapsulateBits
     20      var encapsulatedBits = await subtle.encapsulateBits(
     21        { name: algorithmName },
     22        keyPair.publicKey
     23      );
     24 
     25      assert_true(
     26        encapsulatedBits instanceof Object,
     27        'encapsulateBits should return an object'
     28      );
     29      assert_true(
     30        encapsulatedBits.hasOwnProperty('sharedKey'),
     31        'Result should have sharedKey property'
     32      );
     33      assert_true(
     34        encapsulatedBits.hasOwnProperty('ciphertext'),
     35        'Result should have ciphertext property'
     36      );
     37      assert_true(
     38        encapsulatedBits.sharedKey instanceof ArrayBuffer,
     39        'sharedKey should be ArrayBuffer'
     40      );
     41      assert_true(
     42        encapsulatedBits.ciphertext instanceof ArrayBuffer,
     43        'ciphertext should be ArrayBuffer'
     44      );
     45 
     46      // Verify sharedKey length (should be 32 bytes for all ML-KEM variants)
     47      assert_equals(
     48        encapsulatedBits.sharedKey.byteLength,
     49        32,
     50        'Shared key should be 32 bytes'
     51      );
     52 
     53      // Verify ciphertext length based on algorithm variant
     54      var expectedCiphertextLength;
     55      switch (algorithmName) {
     56        case 'ML-KEM-512':
     57          expectedCiphertextLength = 768;
     58          break;
     59        case 'ML-KEM-768':
     60          expectedCiphertextLength = 1088;
     61          break;
     62        case 'ML-KEM-1024':
     63          expectedCiphertextLength = 1568;
     64          break;
     65      }
     66      assert_equals(
     67        encapsulatedBits.ciphertext.byteLength,
     68        expectedCiphertextLength,
     69        'Ciphertext should be ' +
     70          expectedCiphertextLength +
     71          ' bytes for ' +
     72          algorithmName
     73      );
     74    }, algorithmName + ' encapsulateBits basic functionality');
     75 
     76    // Test decapsulateBits operation
     77    promise_test(async function (test) {
     78      // Generate a key pair for testing
     79      var keyPair = await subtle.generateKey({ name: algorithmName }, false, [
     80        'encapsulateBits',
     81        'decapsulateBits',
     82      ]);
     83 
     84      // First encapsulate to get ciphertext
     85      var encapsulatedBits = await subtle.encapsulateBits(
     86        { name: algorithmName },
     87        keyPair.publicKey
     88      );
     89 
     90      // Then decapsulate using the private key
     91      var decapsulatedBits = await subtle.decapsulateBits(
     92        { name: algorithmName },
     93        keyPair.privateKey,
     94        encapsulatedBits.ciphertext
     95      );
     96 
     97      assert_true(
     98        decapsulatedBits instanceof ArrayBuffer,
     99        'decapsulateBits should return ArrayBuffer'
    100      );
    101      assert_equals(
    102        decapsulatedBits.byteLength,
    103        32,
    104        'Decapsulated bits should be 32 bytes'
    105      );
    106 
    107      // The decapsulated shared secret should match the original
    108      assert_true(
    109        equalBuffers(decapsulatedBits, encapsulatedBits.sharedKey),
    110        'Decapsulated shared secret should match original'
    111      );
    112    }, algorithmName + ' decapsulateBits basic functionality');
    113 
    114    // Test round-trip compatibility
    115    promise_test(async function (test) {
    116      var keyPair = await subtle.generateKey({ name: algorithmName }, false, [
    117        'encapsulateBits',
    118        'decapsulateBits',
    119      ]);
    120 
    121      var encapsulatedBits = await subtle.encapsulateBits(
    122        { name: algorithmName },
    123        keyPair.publicKey
    124      );
    125 
    126      var decapsulatedBits = await subtle.decapsulateBits(
    127        { name: algorithmName },
    128        keyPair.privateKey,
    129        encapsulatedBits.ciphertext
    130      );
    131 
    132      assert_true(
    133        equalBuffers(encapsulatedBits.sharedKey, decapsulatedBits),
    134        'Encapsulated and decapsulated shared secrets should match'
    135      );
    136    }, algorithmName +
    137      ' encapsulateBits/decapsulateBits round-trip compatibility');
    138 
    139    // Test vector-based decapsulation
    140    promise_test(async function (test) {
    141      var vectors = ml_kem_vectors[algorithmName];
    142 
    143      // Import the private key from the vector's privateSeed
    144      var privateKey = await subtle.importKey(
    145        'raw-seed',
    146        vectors.privateSeed,
    147        { name: algorithmName },
    148        false,
    149        ['decapsulateBits']
    150      );
    151 
    152      // Decapsulate the sample ciphertext from the vectors
    153      var decapsulatedBits = await subtle.decapsulateBits(
    154        { name: algorithmName },
    155        privateKey,
    156        vectors.sampleCiphertext
    157      );
    158 
    159      assert_true(
    160        decapsulatedBits instanceof ArrayBuffer,
    161        'decapsulateBits should return ArrayBuffer'
    162      );
    163      assert_equals(
    164        decapsulatedBits.byteLength,
    165        32,
    166        'Decapsulated bits should be 32 bytes'
    167      );
    168 
    169      // The decapsulated shared secret should match the expected value from vectors
    170      assert_true(
    171        equalBuffers(decapsulatedBits, vectors.expectedSharedSecret),
    172        "Decapsulated shared secret should match vector's expectedSharedSecret"
    173      );
    174    }, algorithmName + ' vector-based sampleCiphertext decapsulation');
    175  });
    176 }
    177 
    178 // Helper function to compare two ArrayBuffers
    179 function equalBuffers(a, b) {
    180  if (a.byteLength !== b.byteLength) {
    181    return false;
    182  }
    183  var aBytes = new Uint8Array(a);
    184  var bBytes = new Uint8Array(b);
    185  for (var i = 0; i < a.byteLength; i++) {
    186    if (aBytes[i] !== bBytes[i]) {
    187      return false;
    188    }
    189  }
    190  return true;
    191 }
    192 
    193 define_bits_tests();