tor-browser

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

test_records_crypto.js (6171B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 * http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 const { CollectionKeyManager, CryptoWrapper } = ChromeUtils.importESModule(
      5  "resource://services-sync/record.sys.mjs"
      6 );
      7 const { Service } = ChromeUtils.importESModule(
      8  "resource://services-sync/service.sys.mjs"
      9 );
     10 
     11 var cryptoWrap;
     12 
     13 function crypted_resource_handler(metadata, response) {
     14  let obj = {
     15    id: "resource",
     16    modified: cryptoWrap.modified,
     17    payload: JSON.stringify(cryptoWrap.payload),
     18  };
     19  return httpd_basic_auth_handler(JSON.stringify(obj), metadata, response);
     20 }
     21 
     22 function prepareCryptoWrap(collection, id) {
     23  let w = new CryptoWrapper();
     24  w.cleartext.stuff = "my payload here";
     25  w.collection = collection;
     26  w.id = id;
     27  return w;
     28 }
     29 
     30 add_task(async function test_records_crypto() {
     31  let server;
     32 
     33  await configureIdentity({ username: "john@example.com" });
     34  let keyBundle = Service.identity.syncKeyBundle;
     35 
     36  try {
     37    let log = Log.repository.getLogger("Test");
     38    Log.repository.rootLogger.addAppender(new Log.DumpAppender());
     39 
     40    log.info("Setting up server and authenticator");
     41 
     42    server = httpd_setup({ "/steam/resource": crypted_resource_handler });
     43 
     44    log.info("Creating a record");
     45 
     46    cryptoWrap = prepareCryptoWrap("steam", "resource");
     47 
     48    log.info("cryptoWrap: " + cryptoWrap.toString());
     49 
     50    log.info("Encrypting a record");
     51 
     52    await cryptoWrap.encrypt(keyBundle);
     53    log.info("Ciphertext is " + cryptoWrap.ciphertext);
     54    Assert.notEqual(cryptoWrap.ciphertext, null);
     55 
     56    let firstIV = cryptoWrap.IV;
     57 
     58    log.info("Decrypting the record");
     59 
     60    let payload = await cryptoWrap.decrypt(keyBundle);
     61    Assert.equal(payload.stuff, "my payload here");
     62    Assert.notEqual(payload, cryptoWrap.payload); // wrap.data.payload is the encrypted one
     63 
     64    log.info("Make sure multiple decrypts cause failures");
     65    let error = "";
     66    try {
     67      payload = await cryptoWrap.decrypt(keyBundle);
     68    } catch (ex) {
     69      error = ex;
     70    }
     71    Assert.equal(error.message, "No ciphertext: nothing to decrypt?");
     72 
     73    log.info("Re-encrypting the record with alternate payload");
     74 
     75    cryptoWrap.cleartext.stuff = "another payload";
     76    await cryptoWrap.encrypt(keyBundle);
     77    let secondIV = cryptoWrap.IV;
     78    payload = await cryptoWrap.decrypt(keyBundle);
     79    Assert.equal(payload.stuff, "another payload");
     80 
     81    log.info("Make sure multiple encrypts use different IVs");
     82    Assert.notEqual(firstIV, secondIV);
     83 
     84    log.info(await "Make sure differing ids cause failures");
     85    await cryptoWrap.encrypt(keyBundle);
     86    cryptoWrap.data.id = "other";
     87    error = "";
     88    try {
     89      await cryptoWrap.decrypt(keyBundle);
     90    } catch (ex) {
     91      error = ex;
     92    }
     93    Assert.equal(error.message, "Record id mismatch: resource != other");
     94 
     95    log.info("Make sure wrong hmacs cause failures");
     96    await cryptoWrap.encrypt(keyBundle);
     97    cryptoWrap.hmac = "foo";
     98    error = "";
     99    try {
    100      await cryptoWrap.decrypt(keyBundle);
    101    } catch (ex) {
    102      error = ex;
    103    }
    104    Assert.equal(
    105      error.message.substr(0, 42),
    106      "Record SHA256 HMAC mismatch: should be foo"
    107    );
    108 
    109    // Checking per-collection keys and default key handling.
    110 
    111    await generateNewKeys(Service.collectionKeys);
    112    let bookmarkItem = prepareCryptoWrap("bookmarks", "foo");
    113    await bookmarkItem.encrypt(
    114      Service.collectionKeys.keyForCollection("bookmarks")
    115    );
    116    log.info("Ciphertext is " + bookmarkItem.ciphertext);
    117    Assert.notEqual(bookmarkItem.ciphertext, null);
    118    log.info("Decrypting the record explicitly with the default key.");
    119    Assert.equal(
    120      (await bookmarkItem.decrypt(Service.collectionKeys._default)).stuff,
    121      "my payload here"
    122    );
    123 
    124    // Per-collection keys.
    125    // Generate a key for "bookmarks".
    126    await generateNewKeys(Service.collectionKeys, ["bookmarks"]);
    127    bookmarkItem = prepareCryptoWrap("bookmarks", "foo");
    128    Assert.equal(bookmarkItem.collection, "bookmarks");
    129 
    130    // Encrypt. This'll use the "bookmarks" encryption key, because we have a
    131    // special key for it. The same key will need to be used for decryption.
    132    await bookmarkItem.encrypt(
    133      Service.collectionKeys.keyForCollection("bookmarks")
    134    );
    135    Assert.notEqual(bookmarkItem.ciphertext, null);
    136 
    137    // Attempt to use the default key, because this is a collision that could
    138    // conceivably occur in the real world. Decryption will error, because
    139    // it's not the bookmarks key.
    140    let err;
    141    try {
    142      await bookmarkItem.decrypt(Service.collectionKeys._default);
    143    } catch (ex) {
    144      err = ex;
    145    }
    146    Assert.equal("Record SHA256 HMAC mismatch", err.message.substr(0, 27));
    147 
    148    // Explicitly check that it's using the bookmarks key.
    149    // This should succeed.
    150    Assert.equal(
    151      (
    152        await bookmarkItem.decrypt(
    153          Service.collectionKeys.keyForCollection("bookmarks")
    154        )
    155      ).stuff,
    156      "my payload here"
    157    );
    158 
    159    Assert.ok(Service.collectionKeys.hasKeysFor(["bookmarks"]));
    160 
    161    // Add a key for some new collection and verify that it isn't the
    162    // default key.
    163    Assert.ok(!Service.collectionKeys.hasKeysFor(["forms"]));
    164    Assert.ok(!Service.collectionKeys.hasKeysFor(["bookmarks", "forms"]));
    165    let oldFormsKey = Service.collectionKeys.keyForCollection("forms");
    166    Assert.equal(oldFormsKey, Service.collectionKeys._default);
    167    let newKeys = await Service.collectionKeys.ensureKeysFor(["forms"]);
    168    Assert.ok(newKeys.hasKeysFor(["forms"]));
    169    Assert.ok(newKeys.hasKeysFor(["bookmarks", "forms"]));
    170    let newFormsKey = newKeys.keyForCollection("forms");
    171    Assert.notEqual(newFormsKey, oldFormsKey);
    172 
    173    // Verify that this doesn't overwrite keys
    174    let regetKeys = await newKeys.ensureKeysFor(["forms"]);
    175    Assert.equal(regetKeys.keyForCollection("forms"), newFormsKey);
    176 
    177    const emptyKeys = new CollectionKeyManager();
    178    payload = {
    179      default: Service.collectionKeys._default.keyPairB64,
    180      collections: {},
    181    };
    182    // Verify that not passing `modified` doesn't throw
    183    emptyKeys.setContents(payload, null);
    184 
    185    log.info("Done!");
    186  } finally {
    187    await promiseStopServer(server);
    188  }
    189 });