tor-browser

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

test_BackupService_enable_disable_encryption.js (10648B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 https://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 const TEST_PASSWORD = "This is some test password.";
      7 const TEST_PASSWORD_ALT = "mozilla.org";
      8 
      9 /**
     10 * Creates a backup with this backup service and checks whether it can be
     11 * decrypted with the given password. This ensures that encryption is
     12 * enabled/disabled as needed, and that the backup service will actually use
     13 * the given password if applicable.
     14 *
     15 * @param {BackupService} bs
     16 *   The BackupService to use.
     17 * @param {string} profilePath
     18 *   The path to the current profile.
     19 * @param {string?} password
     20 *   The expected password, or null if it should not be encrypted.
     21 * @returns {boolean}
     22 *   True if the password matched, false if something went wrong.
     23 */
     24 async function backupServiceUsesPassword(bs, profilePath, password = null) {
     25  try {
     26    const backup = await bs.createBackup({ profilePath });
     27    let dest = await IOUtils.createUniqueFile(
     28      PathUtils.parent(backup.archivePath),
     29      "extracted-" + PathUtils.filename(backup.archivePath)
     30    );
     31 
     32    let { isEncrypted } = await bs.sampleArchive(backup.archivePath);
     33    let shouldBeEncrypted = password != null;
     34    Assert.equal(
     35      isEncrypted,
     36      shouldBeEncrypted,
     37      `Archive is ${shouldBeEncrypted ? "" : "not "}encrypted`
     38    );
     39 
     40    // This should throw if the password is incorrect.
     41    await bs.extractCompressedSnapshotFromArchive(
     42      backup.archivePath,
     43      dest,
     44      password
     45    );
     46 
     47    await IOUtils.remove(backup.archivePath);
     48    await IOUtils.remove(dest);
     49    return true;
     50  } catch (e) {
     51    console.error(e);
     52    return false;
     53  }
     54 }
     55 
     56 add_setup(async () => {
     57  // Much of this setup is copied from toolkit/profile/xpcshell/head.js. It is
     58  // needed in order to put the xpcshell test environment into the state where
     59  // it thinks its profile is the one pointed at by
     60  // nsIToolkitProfileService.currentProfile.
     61  let gProfD = do_get_profile();
     62  let gDataHome = gProfD.clone();
     63  gDataHome.append("data");
     64  gDataHome.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
     65  let gDataHomeLocal = gProfD.clone();
     66  gDataHomeLocal.append("local");
     67  gDataHomeLocal.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
     68 
     69  let xreDirProvider = Cc["@mozilla.org/xre/directory-provider;1"].getService(
     70    Ci.nsIXREDirProvider
     71  );
     72  xreDirProvider.setUserDataDirectory(gDataHome, false);
     73  xreDirProvider.setUserDataDirectory(gDataHomeLocal, true);
     74 
     75  let profileSvc = Cc["@mozilla.org/toolkit/profile-service;1"].getService(
     76    Ci.nsIToolkitProfileService
     77  );
     78 
     79  let createdProfile = {};
     80  let didCreate = profileSvc.selectStartupProfile(
     81    ["xpcshell"],
     82    false,
     83    AppConstants.UPDATE_CHANNEL,
     84    "",
     85    {},
     86    {},
     87    createdProfile
     88  );
     89  Assert.ok(didCreate, "Created a testing profile and set it to current.");
     90  Assert.equal(
     91    profileSvc.currentProfile,
     92    createdProfile.value,
     93    "Profile set to current"
     94  );
     95 });
     96 
     97 /**
     98 * Tests that if encryption is disabled that only BackupResource's with
     99 * requiresEncryption set to `false` will have `backup()` called on them.
    100 */
    101 add_task(async function test_disabled_encryption() {
    102  let sandbox = sinon.createSandbox();
    103 
    104  let bs = new BackupService({
    105    FakeBackupResource1,
    106    FakeBackupResource2,
    107    FakeBackupResource3,
    108  });
    109 
    110  Assert.ok(
    111    !bs.state.encryptionEnabled,
    112    "State should indicate that encryption is disabled."
    113  );
    114 
    115  let testProfilePath = await IOUtils.createUniqueDirectory(
    116    PathUtils.tempDir,
    117    "testDisabledEncryption"
    118  );
    119  let encState = await bs.loadEncryptionState(testProfilePath);
    120  Assert.ok(!encState, "Should not find an ArchiveEncryptionState.");
    121 
    122  // Override FakeBackupResource2 so that it requires encryption.
    123  sandbox.stub(FakeBackupResource2, "requiresEncryption").get(() => {
    124    return true;
    125  });
    126  Assert.ok(
    127    FakeBackupResource2.requiresEncryption,
    128    "FakeBackupResource2 requires encryption."
    129  );
    130 
    131  // This is how these FakeBackupResource's are defined in head.js
    132  Assert.ok(
    133    !FakeBackupResource1.requiresEncryption,
    134    "FakeBackupResource1 does not require encryption."
    135  );
    136  Assert.ok(
    137    !FakeBackupResource3.requiresEncryption,
    138    "FakeBackupResource3 does not require encryption."
    139  );
    140 
    141  let resourceWithoutEncryptionStubs = [
    142    sandbox.stub(FakeBackupResource1.prototype, "backup").resolves(null),
    143    sandbox.stub(FakeBackupResource3.prototype, "backup").resolves(null),
    144  ];
    145 
    146  let resourceWithEncryptionStub = sandbox
    147    .stub(FakeBackupResource2.prototype, "backup")
    148    .resolves(null);
    149 
    150  await bs.createBackup({ profilePath: testProfilePath });
    151  Assert.ok(
    152    resourceWithEncryptionStub.notCalled,
    153    "FakeBackupResource2.backup should not have been called"
    154  );
    155 
    156  for (let resourceWithoutEncryptionStub of resourceWithoutEncryptionStubs) {
    157    Assert.ok(
    158      resourceWithoutEncryptionStub.calledOnce,
    159      "backup called on resource that didn't require encryption"
    160    );
    161  }
    162 
    163  await IOUtils.remove(testProfilePath, { recursive: true });
    164  sandbox.restore();
    165 });
    166 
    167 /**
    168 * Tests that if encryption is enabled from a non-enabled state, that an
    169 * ArchiveEncryptionState is created, and state is written to the profile
    170 * directory. Also tests that this allows BackupResource's with
    171 * requiresEncryption set to `true` to have `backup()` called on them.
    172 */
    173 add_task(async function test_enable_encryption() {
    174  let sandbox = sinon.createSandbox();
    175 
    176  let bs = new BackupService({
    177    FakeBackupResource1,
    178    FakeBackupResource2,
    179    FakeBackupResource3,
    180  });
    181 
    182  Assert.ok(
    183    !bs.state.encryptionEnabled,
    184    "State should initially indicate that encryption is disabled."
    185  );
    186 
    187  let testProfilePath = await IOUtils.createUniqueDirectory(
    188    PathUtils.tempDir,
    189    "testEnableEncryption"
    190  );
    191  let encState = await bs.loadEncryptionState(testProfilePath);
    192  Assert.ok(!encState, "Should not find an ArchiveEncryptionState.");
    193 
    194  // Now enable encryption.
    195  let stateUpdatePromise = new Promise(resolve => {
    196    bs.addEventListener("BackupService:StateUpdate", resolve, { once: true });
    197  });
    198  await bs.enableEncryption(TEST_PASSWORD, testProfilePath);
    199  await stateUpdatePromise;
    200  Assert.ok(
    201    bs.state.encryptionEnabled,
    202    "State should indicate that encryption is enabled."
    203  );
    204 
    205  Assert.ok(
    206    await IOUtils.exists(
    207      PathUtils.join(
    208        testProfilePath,
    209        BackupService.PROFILE_FOLDER_NAME,
    210        BackupService.ARCHIVE_ENCRYPTION_STATE_FILE
    211      )
    212    ),
    213    "Encryption state file should exist."
    214  );
    215 
    216  // Override FakeBackupResource2 so that it requires encryption.
    217  sandbox.stub(FakeBackupResource2, "requiresEncryption").get(() => {
    218    return true;
    219  });
    220  Assert.ok(
    221    FakeBackupResource2.requiresEncryption,
    222    "FakeBackupResource2 requires encryption."
    223  );
    224 
    225  // This is how these FakeBackupResource's are defined in head.js
    226  Assert.ok(
    227    !FakeBackupResource1.requiresEncryption,
    228    "FakeBackupResource1 does not require encryption."
    229  );
    230  Assert.ok(
    231    !FakeBackupResource3.requiresEncryption,
    232    "FakeBackupResource3 does not require encryption."
    233  );
    234 
    235  let allResourceBackupStubs = [
    236    sandbox.stub(FakeBackupResource1.prototype, "backup").resolves(null),
    237    sandbox.stub(FakeBackupResource3.prototype, "backup").resolves(null),
    238    sandbox.stub(FakeBackupResource2.prototype, "backup").resolves(null),
    239  ];
    240 
    241  await bs.createBackup({
    242    profilePath: testProfilePath,
    243  });
    244 
    245  for (let resourceBackupStub of allResourceBackupStubs) {
    246    Assert.ok(resourceBackupStub.calledOnce, "backup called on resource");
    247  }
    248 
    249  Assert.ok(
    250    await IOUtils.exists(
    251      PathUtils.join(
    252        testProfilePath,
    253        BackupService.PROFILE_FOLDER_NAME,
    254        BackupService.ARCHIVE_ENCRYPTION_STATE_FILE
    255      )
    256    ),
    257    "Encryption state file should still exist."
    258  );
    259 
    260  await IOUtils.remove(testProfilePath, { recursive: true });
    261  sandbox.restore();
    262 });
    263 
    264 /**
    265 * Tests that enabling encryption while it is already enabled changes the
    266 * password.
    267 */
    268 add_task(async function test_change_encryption_password() {
    269  let bs = new BackupService();
    270 
    271  let testProfilePath = await IOUtils.createUniqueDirectory(
    272    PathUtils.tempDir,
    273    "testAlreadyEnabledEncryption"
    274  );
    275 
    276  // Enable encryption.
    277  await bs.enableEncryption(TEST_PASSWORD, testProfilePath);
    278 
    279  let encState = await bs.loadEncryptionState(testProfilePath);
    280  Assert.ok(encState, "ArchiveEncryptionState is available.");
    281  Assert.ok(
    282    await backupServiceUsesPassword(bs, testProfilePath, TEST_PASSWORD),
    283    "BackupService is using TEST_PASSWORD"
    284  );
    285 
    286  // Change the password.
    287  await bs.enableEncryption(TEST_PASSWORD_ALT, testProfilePath);
    288  encState = await bs.loadEncryptionState(testProfilePath);
    289  Assert.ok(encState, "ArchiveEncryptionState is still available.");
    290  Assert.ok(
    291    await backupServiceUsesPassword(bs, testProfilePath, TEST_PASSWORD_ALT),
    292    "BackupService is using TEST_PASSWORD_ALT"
    293  );
    294 
    295  await IOUtils.remove(testProfilePath, { recursive: true });
    296 });
    297 
    298 /**
    299 * Tests that if encryption is enabled that it can be disabled.
    300 */
    301 add_task(async function test_disabling_encryption() {
    302  let bs = new BackupService();
    303 
    304  let testProfilePath = await IOUtils.createUniqueDirectory(
    305    PathUtils.tempDir,
    306    "testDisableEncryption"
    307  );
    308 
    309  // Enable encryption.
    310  await bs.enableEncryption(TEST_PASSWORD, testProfilePath);
    311 
    312  let encState = await bs.loadEncryptionState(testProfilePath);
    313  Assert.ok(encState, "ArchiveEncryptionState is available.");
    314  Assert.ok(
    315    bs.state.encryptionEnabled,
    316    "State should indicate that encryption is enabled."
    317  );
    318 
    319  Assert.ok(
    320    await IOUtils.exists(
    321      PathUtils.join(
    322        testProfilePath,
    323        BackupService.PROFILE_FOLDER_NAME,
    324        BackupService.ARCHIVE_ENCRYPTION_STATE_FILE
    325      )
    326    ),
    327    "Encryption state file should exist."
    328  );
    329 
    330  // Now disable encryption.
    331  let stateUpdatePromise = new Promise(resolve => {
    332    bs.addEventListener("BackupService:StateUpdate", resolve, { once: true });
    333  });
    334  await bs.disableEncryption(testProfilePath);
    335  await stateUpdatePromise;
    336  Assert.ok(
    337    !bs.state.encryptionEnabled,
    338    "State should indicate that encryption is now disabled."
    339  );
    340 
    341  Assert.ok(
    342    !(await IOUtils.exists(
    343      PathUtils.join(
    344        testProfilePath,
    345        BackupService.PROFILE_FOLDER_NAME,
    346        BackupService.ARCHIVE_ENCRYPTION_STATE_FILE
    347      )
    348    )),
    349    "Encryption state file should have been removed."
    350  );
    351  Assert.ok(
    352    await backupServiceUsesPassword(bs, testProfilePath, null),
    353    "BackupService should not use encryption."
    354  );
    355 
    356  await IOUtils.remove(testProfilePath, { recursive: true });
    357 });