tor-browser

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

test_service_detect_upgrade.js (9087B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 * http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 const { CryptoWrapper, WBORecord } = 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 add_task(async function v4_upgrade() {
     12  enableValidationPrefs();
     13 
     14  let clients = new ServerCollection();
     15  let meta_global = new ServerWBO("global");
     16 
     17  // Tracking info/collections.
     18  let collectionsHelper = track_collections_helper();
     19  let upd = collectionsHelper.with_updated_collection;
     20  let collections = collectionsHelper.collections;
     21 
     22  let keysWBO = new ServerWBO("keys");
     23  let server = httpd_setup({
     24    // Special.
     25    "/1.1/johndoe/info/collections": collectionsHelper.handler,
     26    "/1.1/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler()),
     27    "/1.1/johndoe/storage/meta/global": upd("meta", meta_global.handler()),
     28 
     29    // Track modified times.
     30    "/1.1/johndoe/storage/clients": upd("clients", clients.handler()),
     31    "/1.1/johndoe/storage/tabs": upd("tabs", new ServerCollection().handler()),
     32 
     33    // Just so we don't get 404s in the logs.
     34    "/1.1/johndoe/storage/bookmarks": new ServerCollection().handler(),
     35    "/1.1/johndoe/storage/forms": new ServerCollection().handler(),
     36    "/1.1/johndoe/storage/history": new ServerCollection().handler(),
     37    "/1.1/johndoe/storage/passwords": new ServerCollection().handler(),
     38    "/1.1/johndoe/storage/prefs": new ServerCollection().handler(),
     39  });
     40 
     41  try {
     42    Service.status.resetSync();
     43 
     44    _("Logging in.");
     45 
     46    await configureIdentity({ username: "johndoe" }, server);
     47 
     48    await Service.login();
     49    Assert.ok(Service.isLoggedIn);
     50    await Service.verifyAndFetchSymmetricKeys();
     51    Assert.ok(await Service._remoteSetup());
     52 
     53    async function test_out_of_date() {
     54      _("Old meta/global: " + JSON.stringify(meta_global));
     55      meta_global.payload = JSON.stringify({
     56        syncID: "foooooooooooooooooooooooooo",
     57        storageVersion: STORAGE_VERSION + 1,
     58      });
     59      collections.meta = Date.now() / 1000;
     60      _("New meta/global: " + JSON.stringify(meta_global));
     61      Service.recordManager.set(Service.metaURL, meta_global);
     62      try {
     63        await Service.sync();
     64      } catch (ex) {}
     65      Assert.equal(Service.status.sync, VERSION_OUT_OF_DATE);
     66    }
     67 
     68    // See what happens when we bump the storage version.
     69    _("Syncing after server has been upgraded.");
     70    await test_out_of_date();
     71 
     72    // Same should happen after a wipe.
     73    _("Syncing after server has been upgraded and wiped.");
     74    await Service.wipeServer();
     75    await test_out_of_date();
     76 
     77    // Now's a great time to test what happens when keys get replaced.
     78    _("Syncing afresh...");
     79    Service.logout();
     80    Service.collectionKeys.clear();
     81    meta_global.payload = JSON.stringify({
     82      syncID: "foooooooooooooobbbbbbbbbbbb",
     83      storageVersion: STORAGE_VERSION,
     84    });
     85    collections.meta = Date.now() / 1000;
     86    Service.recordManager.set(Service.metaURL, meta_global);
     87    await Service.login();
     88    Assert.ok(Service.isLoggedIn);
     89    await Service.sync();
     90    Assert.ok(Service.isLoggedIn);
     91 
     92    let serverDecrypted;
     93    let serverKeys;
     94    let serverResp;
     95 
     96    async function retrieve_server_default() {
     97      serverKeys = serverResp = serverDecrypted = null;
     98 
     99      serverKeys = new CryptoWrapper("crypto", "keys");
    100      serverResp = (
    101        await serverKeys.fetch(Service.resource(Service.cryptoKeysURL))
    102      ).response;
    103      Assert.ok(serverResp.success);
    104 
    105      serverDecrypted = await serverKeys.decrypt(
    106        Service.identity.syncKeyBundle
    107      );
    108      _("Retrieved WBO:       " + JSON.stringify(serverDecrypted));
    109      _("serverKeys:          " + JSON.stringify(serverKeys));
    110 
    111      return serverDecrypted.default;
    112    }
    113 
    114    async function retrieve_and_compare_default(should_succeed) {
    115      let serverDefault = await retrieve_server_default();
    116      let localDefault = Service.collectionKeys.keyForCollection().keyPairB64;
    117 
    118      _("Retrieved keyBundle: " + JSON.stringify(serverDefault));
    119      _("Local keyBundle:     " + JSON.stringify(localDefault));
    120 
    121      if (should_succeed) {
    122        Assert.equal(
    123          JSON.stringify(serverDefault),
    124          JSON.stringify(localDefault)
    125        );
    126      } else {
    127        Assert.notEqual(
    128          JSON.stringify(serverDefault),
    129          JSON.stringify(localDefault)
    130        );
    131      }
    132    }
    133 
    134    // Uses the objects set above.
    135    async function set_server_keys(pair) {
    136      serverDecrypted.default = pair;
    137      serverKeys.cleartext = serverDecrypted;
    138      await serverKeys.encrypt(Service.identity.syncKeyBundle);
    139      await serverKeys.upload(Service.resource(Service.cryptoKeysURL));
    140    }
    141 
    142    _("Checking we have the latest keys.");
    143    await retrieve_and_compare_default(true);
    144 
    145    _("Update keys on server.");
    146    await set_server_keys([
    147      "KaaaaaaaaaaaHAtfmuRY0XEJ7LXfFuqvF7opFdBD/MY=",
    148      "aaaaaaaaaaaapxMO6TEWtLIOv9dj6kBAJdzhWDkkkis=",
    149    ]);
    150 
    151    _("Checking that we no longer have the latest keys.");
    152    await retrieve_and_compare_default(false);
    153 
    154    _("Indeed, they're what we set them to...");
    155    Assert.equal(
    156      "KaaaaaaaaaaaHAtfmuRY0XEJ7LXfFuqvF7opFdBD/MY=",
    157      (await retrieve_server_default())[0]
    158    );
    159 
    160    _("Sync. Should download changed keys automatically.");
    161    let oldClientsModified = collections.clients;
    162    let oldTabsModified = collections.tabs;
    163 
    164    await Service.login();
    165    await Service.sync();
    166    _("New key should have forced upload of data.");
    167    _("Tabs: " + oldTabsModified + " < " + collections.tabs);
    168    _("Clients: " + oldClientsModified + " < " + collections.clients);
    169    Assert.greater(collections.clients, oldClientsModified);
    170    Assert.greater(collections.tabs, oldTabsModified);
    171 
    172    _("... and keys will now match.");
    173    await retrieve_and_compare_default(true);
    174 
    175    // Clean up.
    176    await Service.startOver();
    177  } finally {
    178    for (const pref of Svc.PrefBranch.getChildList("")) {
    179      Svc.PrefBranch.clearUserPref(pref);
    180    }
    181    await promiseStopServer(server);
    182  }
    183 });
    184 
    185 add_task(async function v5_upgrade() {
    186  enableValidationPrefs();
    187 
    188  // Tracking info/collections.
    189  let collectionsHelper = track_collections_helper();
    190  let upd = collectionsHelper.with_updated_collection;
    191 
    192  let keysWBO = new ServerWBO("keys");
    193  let bulkWBO = new ServerWBO("bulk");
    194  let clients = new ServerCollection();
    195  let meta_global = new ServerWBO("global");
    196 
    197  let server = httpd_setup({
    198    // Special.
    199    "/1.1/johndoe/storage/meta/global": upd("meta", meta_global.handler()),
    200    "/1.1/johndoe/info/collections": collectionsHelper.handler,
    201    "/1.1/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler()),
    202    "/1.1/johndoe/storage/crypto/bulk": upd("crypto", bulkWBO.handler()),
    203 
    204    // Track modified times.
    205    "/1.1/johndoe/storage/clients": upd("clients", clients.handler()),
    206    "/1.1/johndoe/storage/tabs": upd("tabs", new ServerCollection().handler()),
    207  });
    208 
    209  try {
    210    Service.status.resetSync();
    211 
    212    Service.clusterURL = server.baseURI + "/";
    213 
    214    await configureIdentity({ username: "johndoe" }, server);
    215 
    216    // Test an upgrade where the contents of the server would cause us to error
    217    // -- keys decrypted with a different sync key, for example.
    218    _("Testing v4 -> v5 (or similar) upgrade.");
    219    async function update_server_keys(syncKeyBundle, wboName, collWBO) {
    220      await generateNewKeys(Service.collectionKeys);
    221      let serverKeys = Service.collectionKeys.asWBO("crypto", wboName);
    222      await serverKeys.encrypt(syncKeyBundle);
    223      let res = Service.resource(Service.storageURL + collWBO);
    224      Assert.ok((await serverKeys.upload(res)).success);
    225    }
    226 
    227    _("Bumping version.");
    228    // Bump version on the server.
    229    let m = new WBORecord("meta", "global");
    230    m.payload = {
    231      syncID: "foooooooooooooooooooooooooo",
    232      storageVersion: STORAGE_VERSION + 1,
    233    };
    234    await m.upload(Service.resource(Service.metaURL));
    235 
    236    _("New meta/global: " + JSON.stringify(meta_global));
    237 
    238    // Fill the keys with bad data.
    239    let badKeys = new BulkKeyBundle("crypto");
    240    await badKeys.generateRandom();
    241    await update_server_keys(badKeys, "keys", "crypto/keys"); // v4
    242    await update_server_keys(badKeys, "bulk", "crypto/bulk"); // v5
    243 
    244    _("Generating new keys.");
    245    await generateNewKeys(Service.collectionKeys);
    246 
    247    // Now sync and see what happens. It should be a version fail, not a crypto
    248    // fail.
    249 
    250    _("Logging in.");
    251    try {
    252      await Service.login();
    253    } catch (e) {
    254      _("Exception: " + e);
    255    }
    256    _("Status: " + Service.status);
    257    Assert.ok(!Service.isLoggedIn);
    258    Assert.equal(VERSION_OUT_OF_DATE, Service.status.sync);
    259 
    260    // Clean up.
    261    await Service.startOver();
    262  } finally {
    263    for (const pref of Svc.PrefBranch.getChildList("")) {
    264      Svc.PrefBranch.clearUserPref(pref);
    265    }
    266    await promiseStopServer(server);
    267  }
    268 });
    269 
    270 function run_test() {
    271  Log.repository.rootLogger.addAppender(new Log.DumpAppender());
    272 
    273  run_next_test();
    274 }