tor-browser

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

chacha20_poly1305.tentative.https.any.js (11325B)


      1 // META: title=WebCryptoAPI: encrypt()/decrypt() ChaCha20-Poly1305
      2 // META: timeout=long
      3 
      4 var subtle = crypto.subtle; // Change to test prefixed implementations
      5 
      6 var sourceData = {
      7  empty: new Uint8Array(0),
      8  short: new Uint8Array([
      9    21, 110, 234, 124, 193, 76, 86, 203, 148, 219, 3, 10, 74, 157, 149, 255,
     10  ]),
     11  medium: new Uint8Array([
     12    182, 200, 249, 223, 100, 140, 208, 136, 183, 15, 56, 231, 65, 151, 177, 140,
     13    184, 30, 30, 67, 80, 213, 11, 204, 184, 251, 90, 115, 121, 200, 123, 178,
     14    227, 214, 237, 84, 97, 237, 30, 159, 54, 243, 64, 163, 150, 42, 68, 107,
     15    129, 91, 121, 75, 75, 212, 58, 68, 3, 80, 32, 119, 178, 37, 108, 200, 7,
     16    131, 127, 58, 172, 209, 24, 235, 75, 156, 43, 174, 184, 151, 6, 134, 37,
     17    171, 172, 161, 147,
     18  ]),
     19  long: new Uint8Array(
     20    Array(65)
     21      .fill(0)
     22      .map((_, i) => i % 256)
     23  ),
     24 };
     25 
     26 var additionalData = {
     27  empty: new Uint8Array(0),
     28  short: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]),
     29  medium: new Uint8Array([
     30    9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
     31  ]),
     32 };
     33 
     34 var iv = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); // 96-bit IV (only valid size for ChaCha20-Poly1305)
     35 
     36 var keyBytes = new Uint8Array([
     37  52, 138, 105, 103, 105, 3, 94, 254, 59, 241, 159, 138, 189, 254, 153, 191,
     38  228, 172, 165, 239, 117, 172, 19, 206, 219, 9, 205, 138, 45, 87, 166, 89,
     39 ]);
     40 
     41 var encryptedData = {
     42  empty_ad: {
     43    empty: new Uint8Array([
     44      202, 31, 160, 169, 169, 221, 162, 53, 252, 126, 127, 237, 158, 98, 86, 41,
     45    ]),
     46    short: new Uint8Array([
     47      226, 238, 222, 234, 202, 96, 116, 202, 205, 199, 126, 40, 167, 93, 65,
     48      172, 87, 45, 146, 68, 245, 236, 87, 162, 200, 89, 228, 51, 2, 249, 103,
     49      255,
     50    ]),
     51    medium: new Uint8Array([
     52      65, 72, 205, 73, 111, 160, 242, 137, 238, 19, 69, 197, 172, 87, 101, 223,
     53      97, 245, 255, 57, 226, 43, 36, 125, 205, 218, 211, 252, 102, 45, 234, 179,
     54      23, 71, 87, 156, 145, 16, 106, 98, 75, 140, 67, 249, 230, 124, 37, 127,
     55      192, 145, 40, 129, 203, 31, 15, 182, 15, 226, 211, 111, 132, 132, 114,
     56      204, 73, 36, 245, 199, 212, 187, 239, 33, 46, 203, 124, 134, 76, 21, 242,
     57      233, 119, 179, 216, 193, 210, 145, 43, 48, 105, 153, 28, 62, 49, 175, 154,
     58      142, 222, 68, 219, 229, 66,
     59    ]),
     60    long: new Uint8Array([
     61      247, 129, 54, 149, 15, 41, 36, 6, 81, 21, 119, 41, 225, 205, 218, 92, 201,
     62      250, 243, 105, 166, 235, 57, 166, 109, 56, 147, 148, 3, 248, 143, 30, 212,
     63      176, 152, 235, 212, 216, 82, 218, 85, 86, 41, 113, 92, 123, 79, 59, 113,
     64      251, 99, 249, 180, 254, 3, 197, 52, 139, 201, 35, 10, 156, 32, 59, 14,
     65      225, 117, 48, 100, 126, 58, 112, 157, 195, 18, 185, 178, 97, 215, 233,
     66      113,
     67    ]),
     68  },
     69  short_ad: {
     70    empty: new Uint8Array([
     71      0, 176, 175, 37, 156, 39, 141, 93, 198, 224, 223, 214, 239, 212, 50, 215,
     72    ]),
     73    short: new Uint8Array([
     74      226, 238, 222, 234, 202, 96, 116, 202, 205, 199, 126, 40, 167, 93, 65,
     75      172, 40, 45, 199, 67, 109, 80, 99, 203, 246, 137, 82, 206, 183, 23, 175,
     76      176,
     77    ]),
     78    medium: new Uint8Array([
     79      65, 72, 205, 73, 111, 160, 242, 137, 238, 19, 69, 197, 172, 87, 101, 223,
     80      97, 245, 255, 57, 226, 43, 36, 125, 205, 218, 211, 252, 102, 45, 234, 179,
     81      23, 71, 87, 156, 145, 16, 106, 98, 75, 140, 67, 249, 230, 124, 37, 127,
     82      192, 145, 40, 129, 203, 31, 15, 182, 15, 226, 211, 111, 132, 132, 114,
     83      204, 73, 36, 245, 199, 212, 187, 239, 33, 46, 203, 124, 134, 76, 21, 242,
     84      233, 119, 179, 216, 193, 210, 199, 121, 7, 153, 245, 137, 122, 100, 72,
     85      102, 113, 169, 244, 218, 111, 105,
     86    ]),
     87    long: new Uint8Array([
     88      247, 129, 54, 149, 15, 41, 36, 6, 81, 21, 119, 41, 225, 205, 218, 92, 201,
     89      250, 243, 105, 166, 235, 57, 166, 109, 56, 147, 148, 3, 248, 143, 30, 212,
     90      176, 152, 235, 212, 216, 82, 218, 85, 86, 41, 113, 92, 123, 79, 59, 113,
     91      251, 99, 249, 180, 254, 3, 197, 52, 139, 201, 35, 10, 156, 32, 59, 14,
     92      137, 0, 55, 193, 166, 74, 124, 251, 91, 36, 191, 112, 236, 204, 35, 102,
     93    ]),
     94  },
     95  medium_ad: {
     96    empty: new Uint8Array([
     97      163, 122, 139, 166, 205, 190, 132, 236, 17, 19, 31, 4, 16, 47, 13, 242,
     98    ]),
     99    short: new Uint8Array([
    100      226, 238, 222, 234, 202, 96, 116, 202, 205, 199, 126, 40, 167, 93, 65,
    101      172, 18, 242, 253, 220, 52, 138, 67, 162, 201, 38, 125, 119, 44, 53, 147,
    102      119,
    103    ]),
    104    medium: new Uint8Array([
    105      65, 72, 205, 73, 111, 160, 242, 137, 238, 19, 69, 197, 172, 87, 101, 223,
    106      97, 245, 255, 57, 226, 43, 36, 125, 205, 218, 211, 252, 102, 45, 234, 179,
    107      23, 71, 87, 156, 145, 16, 106, 98, 75, 140, 67, 249, 230, 124, 37, 127,
    108      192, 145, 40, 129, 203, 31, 15, 182, 15, 226, 211, 111, 132, 132, 114,
    109      204, 73, 36, 245, 199, 212, 187, 239, 33, 46, 203, 124, 134, 76, 21, 242,
    110      233, 119, 179, 216, 193, 210, 161, 250, 192, 51, 193, 133, 185, 55, 148,
    111      21, 194, 168, 212, 203, 252, 184,
    112    ]),
    113    long: new Uint8Array([
    114      247, 129, 54, 149, 15, 41, 36, 6, 81, 21, 119, 41, 225, 205, 218, 92, 201,
    115      250, 243, 105, 166, 235, 57, 166, 109, 56, 147, 148, 3, 248, 143, 30, 212,
    116      176, 152, 235, 212, 216, 82, 218, 85, 86, 41, 113, 92, 123, 79, 59, 113,
    117      251, 99, 249, 180, 254, 3, 197, 52, 139, 201, 35, 10, 156, 32, 59, 14, 88,
    118      20, 159, 185, 145, 230, 1, 242, 106, 255, 55, 7, 60, 85, 179, 184,
    119    ]),
    120  },
    121 };
    122 
    123 function equalBuffers(a, b) {
    124  if (a.byteLength !== b.byteLength) {
    125    return false;
    126  }
    127  var aBytes = new Uint8Array(a);
    128  var bBytes = new Uint8Array(b);
    129  for (var i = 0; i < a.byteLength; i++) {
    130    if (aBytes[i] !== bBytes[i]) {
    131      return false;
    132    }
    133  }
    134  return true;
    135 }
    136 
    137 // Test ChaCha20-Poly1305 encryption/decryption
    138 var algorithmName = 'ChaCha20-Poly1305';
    139 
    140 // Test with different additional data combinations
    141 Object.keys(additionalData).forEach(function (adName) {
    142  var ad = additionalData[adName];
    143 
    144  Object.keys(sourceData).forEach(function (dataName) {
    145    var plaintext = sourceData[dataName];
    146    var ciphertext = encryptedData[adName + '_ad'][dataName];
    147 
    148    promise_test(function (test) {
    149      var operation = subtle
    150        .importKey('raw-secret', keyBytes, { name: algorithmName }, false, [
    151          'encrypt',
    152          'decrypt',
    153        ])
    154        .then(
    155          function (key) {
    156            return subtle.encrypt(
    157              { name: algorithmName, iv: iv, additionalData: ad },
    158              key,
    159              plaintext
    160            );
    161          },
    162          function (err) {
    163            assert_unreached(
    164              'importKey failed unexpectedly: ' + err.toString()
    165            );
    166          }
    167        )
    168        .then(
    169          function (result) {
    170            assert_true(
    171              equalBuffers(result, ciphertext),
    172              'Encrypted data should match expected result'
    173            );
    174            return result;
    175          },
    176          function (err) {
    177            assert_unreached('encrypt failed unexpectedly: ' + err.toString());
    178          }
    179        );
    180 
    181      return operation;
    182    }, algorithmName +
    183      ' encryption with ' +
    184      adName +
    185      ' additional data, ' +
    186      dataName +
    187      ' data');
    188 
    189    promise_test(function (test) {
    190      var operation = subtle
    191        .importKey('raw-secret', keyBytes, { name: algorithmName }, false, [
    192          'encrypt',
    193          'decrypt',
    194        ])
    195        .then(
    196          function (key) {
    197            return subtle.decrypt(
    198              { name: algorithmName, iv: iv, additionalData: ad },
    199              key,
    200              ciphertext
    201            );
    202          },
    203          function (err) {
    204            assert_unreached(
    205              'importKey failed unexpectedly: ' + err.toString()
    206            );
    207          }
    208        )
    209        .then(
    210          function (result) {
    211            assert_true(
    212              equalBuffers(result, plaintext),
    213              'Decrypted data should match original plaintext'
    214            );
    215          },
    216          function (err) {
    217            assert_unreached('decrypt failed unexpectedly: ' + err.toString());
    218          }
    219        );
    220 
    221      return operation;
    222    }, algorithmName +
    223      ' decryption with ' +
    224      adName +
    225      ' additional data, ' +
    226      dataName +
    227      ' data');
    228  });
    229 });
    230 
    231 // Test round-trip encryption/decryption without fixed vectors
    232 promise_test(function (test) {
    233  var key;
    234  var plaintext = sourceData.medium;
    235  var ad = additionalData.short;
    236 
    237  return subtle
    238    .generateKey({ name: algorithmName }, false, ['encrypt', 'decrypt'])
    239    .then(function (result) {
    240      key = result;
    241      return subtle.encrypt(
    242        { name: algorithmName, iv: iv, additionalData: ad },
    243        key,
    244        plaintext
    245      );
    246    })
    247    .then(function (encrypted) {
    248      return subtle.decrypt(
    249        { name: algorithmName, iv: iv, additionalData: ad },
    250        key,
    251        encrypted
    252      );
    253    })
    254    .then(function (decrypted) {
    255      assert_true(
    256        equalBuffers(decrypted, plaintext),
    257        'Round-trip encryption/decryption should return original plaintext'
    258      );
    259    });
    260 }, algorithmName + ' round-trip encrypt/decrypt');
    261 
    262 // Test decryption with wrong additional data should fail
    263 promise_test(function (test) {
    264  var key;
    265  var plaintext = sourceData.short;
    266  var correctAd = additionalData.short;
    267  var wrongAd = additionalData.medium;
    268 
    269  return subtle
    270    .generateKey({ name: algorithmName }, false, ['encrypt', 'decrypt'])
    271    .then(function (result) {
    272      key = result;
    273      return subtle.encrypt(
    274        { name: algorithmName, iv: iv, additionalData: correctAd },
    275        key,
    276        plaintext
    277      );
    278    })
    279    .then(function (encrypted) {
    280      return promise_rejects_dom(
    281        test,
    282        'OperationError',
    283        subtle.decrypt(
    284          { name: algorithmName, iv: iv, additionalData: wrongAd },
    285          key,
    286          encrypted
    287        )
    288      );
    289    });
    290 }, algorithmName + ' decryption with wrong additional data should fail');
    291 
    292 // Test decryption with wrong IV should fail
    293 promise_test(function (test) {
    294  var key;
    295  var correctIv = iv;
    296  var wrongIv = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); // Different 96-bit IV
    297  var plaintext = sourceData.short;
    298  var ad = additionalData.empty;
    299 
    300  return subtle
    301    .generateKey({ name: algorithmName }, false, ['encrypt', 'decrypt'])
    302    .then(function (result) {
    303      key = result;
    304      return subtle.encrypt(
    305        { name: algorithmName, iv: correctIv, additionalData: ad },
    306        key,
    307        plaintext
    308      );
    309    })
    310    .then(function (encrypted) {
    311      return promise_rejects_dom(
    312        test,
    313        'OperationError',
    314        subtle.decrypt(
    315          { name: algorithmName, iv: wrongIv, additionalData: ad },
    316          key,
    317          encrypted
    318        )
    319      );
    320    });
    321 }, algorithmName + ' decryption with wrong IV should fail');
    322 
    323 // Test invalid IV size should fail
    324 promise_test(function (test) {
    325  var invalidIv = new Uint8Array(16); // 128-bit IV is invalid for ChaCha20-Poly1305
    326 
    327  return subtle
    328    .generateKey({ name: algorithmName }, false, ['encrypt'])
    329    .then(function (key) {
    330      return promise_rejects_dom(
    331        test,
    332        'OperationError',
    333        subtle.encrypt(
    334          { name: algorithmName, iv: invalidIv },
    335          key,
    336          sourceData.short
    337        )
    338      );
    339    });
    340 }, algorithmName + ' encryption with invalid IV size should fail');