tor-browser

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

test_crypto_encrypt.js (6512B)


      1 // Test PushCrypto.encrypt()
      2 "use strict";
      3 
      4 const { PushCrypto } = ChromeUtils.importESModule(
      5  "resource://gre/modules/PushCrypto.sys.mjs"
      6 );
      7 
      8 let from64 = v => {
      9  // allow whitespace in the strings.
     10  let stripped = v.replace(/ |\t|\r|\n/g, "");
     11  return new Uint8Array(
     12    ChromeUtils.base64URLDecode(stripped, { padding: "reject" })
     13  );
     14 };
     15 
     16 let to64 = v => ChromeUtils.base64URLEncode(v, { pad: false });
     17 
     18 // A helper function to take a public key (as a buffer containing a 65-byte
     19 // buffer of uncompressed EC points) and a private key (32byte buffer) and
     20 // return 2 crypto keys.
     21 async function importKeyPair(publicKeyBuffer, privateKeyBuffer) {
     22  let jwk = {
     23    kty: "EC",
     24    crv: "P-256",
     25    x: to64(publicKeyBuffer.slice(1, 33)),
     26    y: to64(publicKeyBuffer.slice(33, 65)),
     27    ext: true,
     28  };
     29  let publicKey = await crypto.subtle.importKey(
     30    "jwk",
     31    jwk,
     32    { name: "ECDH", namedCurve: "P-256" },
     33    true,
     34    []
     35  );
     36  jwk.d = to64(privateKeyBuffer);
     37  let privateKey = await crypto.subtle.importKey(
     38    "jwk",
     39    jwk,
     40    { name: "ECDH", namedCurve: "P-256" },
     41    true,
     42    ["deriveBits"]
     43  );
     44  return { publicKey, privateKey };
     45 }
     46 
     47 // The example from draft-ietf-webpush-encryption-09.
     48 add_task(async function static_aes128gcm() {
     49  let fixture = {
     50    ciphertext:
     51      from64(`DGv6ra1nlYgDCS1FRnbzlwAAEABBBP4z9KsN6nGRTbVYI_c7VJSPQTBtkgcy27ml
     52                        mlMoZIIgDll6e3vCYLocInmYWAmS6TlzAC8wEqKK6PBru3jl7A_yl95bQpu6cVPT
     53                        pK4Mqgkf1CXztLVBSt2Ks3oZwbuwXPXLWyouBWLVWGNWQexSgSxsj_Qulcy4a-fN`),
     54    plaintext: new TextEncoder().encode(
     55      "When I grow up, I want to be a watermelon"
     56    ),
     57    authSecret: from64("BTBZMqHH6r4Tts7J_aSIgg"),
     58    receiver: {
     59      private: from64("q1dXpw3UpT5VOmu_cf_v6ih07Aems3njxI-JWgLcM94"),
     60      public: from64(`BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcx
     61                      aOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4`),
     62    },
     63    sender: {
     64      private: from64("yfWPiYE-n46HLnH0KqZOF1fJJU3MYrct3AELtAQ-oRw"),
     65      public: from64(`BP4z9KsN6nGRTbVYI_c7VJSPQTBtkgcy27mlmlMoZIIg
     66                      Dll6e3vCYLocInmYWAmS6TlzAC8wEqKK6PBru3jl7A8`),
     67    },
     68    salt: from64("DGv6ra1nlYgDCS1FRnbzlw"),
     69  };
     70 
     71  let options = {
     72    senderKeyPair: await importKeyPair(
     73      fixture.sender.public,
     74      fixture.sender.private
     75    ),
     76    salt: fixture.salt,
     77  };
     78 
     79  let { ciphertext, encoding } = await PushCrypto.encrypt(
     80    fixture.plaintext,
     81    fixture.receiver.public,
     82    fixture.authSecret,
     83    options
     84  );
     85 
     86  Assert.deepEqual(ciphertext, fixture.ciphertext);
     87  Assert.equal(encoding, "aes128gcm");
     88 
     89  // and for fun, decrypt it and check the plaintext.
     90  let recvKeyPair = await importKeyPair(
     91    fixture.receiver.public,
     92    fixture.receiver.private
     93  );
     94  let jwk = await crypto.subtle.exportKey("jwk", recvKeyPair.privateKey);
     95  let plaintext = await PushCrypto.decrypt(
     96    jwk,
     97    fixture.receiver.public,
     98    fixture.authSecret,
     99    { encoding: "aes128gcm" },
    100    ciphertext
    101  );
    102  Assert.deepEqual(plaintext, fixture.plaintext);
    103 });
    104 
    105 // This is how we expect real code to interact with .encrypt.
    106 add_task(async function aes128gcm_simple() {
    107  let [recvPublicKey, recvPrivateKey] = await PushCrypto.generateKeys();
    108 
    109  let message = new TextEncoder().encode("Fast for good.");
    110  let authSecret = crypto.getRandomValues(new Uint8Array(16));
    111  let { ciphertext, encoding } = await PushCrypto.encrypt(
    112    message,
    113    recvPublicKey,
    114    authSecret
    115  );
    116  Assert.equal(encoding, "aes128gcm");
    117  // and decrypt it.
    118  let plaintext = await PushCrypto.decrypt(
    119    recvPrivateKey,
    120    recvPublicKey,
    121    authSecret,
    122    { encoding },
    123    ciphertext
    124  );
    125  deepEqual(message, plaintext);
    126 });
    127 
    128 // Variable record size tests
    129 add_task(async function aes128gcm_rs() {
    130  let [recvPublicKey, recvPrivateKey] = await PushCrypto.generateKeys();
    131 
    132  for (let rs of [-1, 0, 1, 17]) {
    133    let payload = "x".repeat(1024);
    134    info(`testing expected encoder failure with rs=${rs}`);
    135    let message = new TextEncoder().encode(payload);
    136    let authSecret = crypto.getRandomValues(new Uint8Array(16));
    137    await Assert.rejects(
    138      PushCrypto.encrypt(message, recvPublicKey, authSecret, { rs }),
    139      /recordsize is too small/
    140    );
    141  }
    142  for (let rs of [-1, 0, 1, 17]) {
    143    let payload = "x".repeat(1024);
    144    info(`testing expected decoder failure with rs=${rs}`);
    145    let message = new TextEncoder().encode(payload);
    146    let authSecret = crypto.getRandomValues(new Uint8Array(16));
    147    const { ciphertext, encoding } = await PushCrypto.encrypt(
    148      message,
    149      recvPublicKey,
    150      authSecret
    151    );
    152    // Given it doesn't make sense to encrypt with invalid param,
    153    // we just overwrite the header with invalid value
    154    new DataView(ciphertext.buffer).setUint32(16, rs);
    155    await Assert.rejects(
    156      PushCrypto.decrypt(
    157        recvPrivateKey,
    158        recvPublicKey,
    159        authSecret,
    160        { encoding },
    161        ciphertext
    162      ),
    163      /Record sizes smaller than 18 are invalid/
    164    );
    165  }
    166  for (let rs of [18, 50, 1024, 4096, 16384]) {
    167    info(`testing expected success with rs=${rs}`);
    168    let payload = "x".repeat(rs * 3);
    169    let message = new TextEncoder().encode(payload);
    170    let authSecret = crypto.getRandomValues(new Uint8Array(16));
    171    let { ciphertext, encoding } = await PushCrypto.encrypt(
    172      message,
    173      recvPublicKey,
    174      authSecret,
    175      { rs }
    176    );
    177    Assert.equal(encoding, "aes128gcm");
    178    // and decrypt it.
    179    let plaintext = await PushCrypto.decrypt(
    180      recvPrivateKey,
    181      recvPublicKey,
    182      authSecret,
    183      { encoding },
    184      ciphertext
    185    );
    186    deepEqual(message, plaintext);
    187  }
    188 });
    189 
    190 // And try and hit some edge-cases.
    191 add_task(async function aes128gcm_edgecases() {
    192  let [recvPublicKey, recvPrivateKey] = await PushCrypto.generateKeys();
    193 
    194  for (let size of [
    195    0,
    196    4096 - 16,
    197    4096 - 16 - 1,
    198    4096 - 16 + 1,
    199    4095,
    200    4096,
    201    4097,
    202    10240,
    203  ]) {
    204    info(`testing encryption of ${size} byte payload`);
    205    let message = new TextEncoder().encode("x".repeat(size));
    206    let authSecret = crypto.getRandomValues(new Uint8Array(16));
    207    let { ciphertext, encoding } = await PushCrypto.encrypt(
    208      message,
    209      recvPublicKey,
    210      authSecret
    211    );
    212    Assert.equal(encoding, "aes128gcm");
    213    // and decrypt it.
    214    let plaintext = await PushCrypto.decrypt(
    215      recvPrivateKey,
    216      recvPublicKey,
    217      authSecret,
    218      { encoding },
    219      ciphertext
    220    );
    221    deepEqual(message, plaintext);
    222  }
    223 });