tor-browser

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

CredentialsAndSecurityBackupResource.sys.mjs (4964B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
      4 
      5 import { BackupResource } from "resource:///modules/backup/BackupResource.sys.mjs";
      6 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
      7 
      8 const lazy = {};
      9 
     10 ChromeUtils.defineESModuleGetters(lazy, {
     11  BackupService: "resource:///modules/backup/BackupService.sys.mjs",
     12  OSKeyStore: "resource://gre/modules/OSKeyStore.sys.mjs",
     13 });
     14 
     15 XPCOMUtils.defineLazyServiceGetter(
     16  lazy,
     17  "nativeOSKeyStore",
     18  "@mozilla.org/security/oskeystore;1",
     19  Ci.nsIOSKeyStore
     20 );
     21 
     22 /**
     23 * Class representing files needed for logins, payment methods and form autofill within a user profile.
     24 */
     25 export class CredentialsAndSecurityBackupResource extends BackupResource {
     26  static get key() {
     27    return "credentials_and_security";
     28  }
     29 
     30  static get requiresEncryption() {
     31    return true;
     32  }
     33 
     34  async backup(
     35    stagingPath,
     36    profilePath = PathUtils.profileDir,
     37    _isEncrypting = false
     38  ) {
     39    const simpleCopyFiles = [
     40      "pkcs11.txt",
     41      "logins.json",
     42      "logins-backup.json",
     43      "autofill-profiles.json",
     44    ];
     45    await BackupResource.copyFiles(profilePath, stagingPath, simpleCopyFiles);
     46 
     47    const sqliteDatabases = ["cert9.db", "key4.db", "credentialstate.sqlite"];
     48    await BackupResource.copySqliteDatabases(
     49      profilePath,
     50      stagingPath,
     51      sqliteDatabases
     52    );
     53 
     54    return null;
     55  }
     56 
     57  async recover(_manifestEntry, recoveryPath, destProfilePath) {
     58    // Payment methods would have been encrypted via OSKeyStore, which might
     59    // have a different OSKeyStore secret than this profile (particularly if
     60    // we're on a different machine).
     61    //
     62    // BackupService created a temporary native OSKeyStore that we can use
     63    // to decrypt the payment methods using the old secret used at backup
     64    // time. We should then re-encrypt those with the current OSKeyStore.
     65    const AUTOFILL_RECORDS_PATH = PathUtils.join(
     66      recoveryPath,
     67      "autofill-profiles.json"
     68    );
     69 
     70    const files = [
     71      "pkcs11.txt",
     72      "logins.json",
     73      "logins-backup.json",
     74      "cert9.db",
     75      "key4.db",
     76      "credentialstate.sqlite",
     77    ];
     78 
     79    if (await IOUtils.exists(AUTOFILL_RECORDS_PATH)) {
     80      await this.encryptAutofillData(AUTOFILL_RECORDS_PATH);
     81      files.push("autofill-profiles.json");
     82    }
     83 
     84    await BackupResource.copyFiles(recoveryPath, destProfilePath, files);
     85 
     86    return null;
     87  }
     88 
     89  async encryptAutofillData(AUTOFILL_RECORDS_PATH) {
     90    let autofillRecords = await IOUtils.readJSON(AUTOFILL_RECORDS_PATH);
     91 
     92    for (let creditCard of autofillRecords.creditCards) {
     93      let oldEncryptedCard = creditCard["cc-number-encrypted"];
     94      if (oldEncryptedCard) {
     95        let plaintextCard;
     96        // We use the native OSKeyStore backend to decrypt the bytes with the
     97        // original secret in order to skip authentication dialogs.
     98        if (
     99          await lazy.nativeOSKeyStore.asyncSecretAvailable(
    100            lazy.BackupService.RECOVERY_OSKEYSTORE_LABEL
    101          )
    102        ) {
    103          let plaintextCardBytes =
    104            await lazy.nativeOSKeyStore.asyncDecryptBytes(
    105              lazy.BackupService.RECOVERY_OSKEYSTORE_LABEL,
    106              oldEncryptedCard
    107            );
    108          plaintextCard = String.fromCharCode.apply(String, plaintextCardBytes);
    109        } else {
    110          plaintextCard = await lazy.OSKeyStore.decrypt(
    111            oldEncryptedCard,
    112            "backup_cc"
    113          );
    114        }
    115 
    116        // We're accessing the "real" OSKeyStore for this device here, and
    117        // encrypting the card with it.
    118        let newEncryptedCard = await lazy.OSKeyStore.encrypt(plaintextCard);
    119        creditCard["cc-number-encrypted"] = newEncryptedCard;
    120      }
    121    }
    122 
    123    await IOUtils.writeJSON(AUTOFILL_RECORDS_PATH, autofillRecords);
    124  }
    125 
    126  async measure(profilePath = PathUtils.profileDir) {
    127    const securityFiles = ["cert9.db", "pkcs11.txt"];
    128    let securitySize = 0;
    129 
    130    for (let filePath of securityFiles) {
    131      let resourcePath = PathUtils.join(profilePath, filePath);
    132      let resourceSize = await BackupResource.getFileSize(resourcePath);
    133      if (Number.isInteger(resourceSize)) {
    134        securitySize += resourceSize;
    135      }
    136    }
    137 
    138    Glean.browserBackup.securityDataSize.set(securitySize);
    139 
    140    const credentialsFiles = [
    141      "key4.db",
    142      "logins.json",
    143      "logins-backup.json",
    144      "autofill-profiles.json",
    145      "credentialstate.sqlite",
    146    ];
    147    let credentialsSize = 0;
    148 
    149    for (let filePath of credentialsFiles) {
    150      let resourcePath = PathUtils.join(profilePath, filePath);
    151      let resourceSize = await BackupResource.getFileSize(resourcePath);
    152      if (Number.isInteger(resourceSize)) {
    153        credentialsSize += resourceSize;
    154      }
    155    }
    156 
    157    Glean.browserBackup.credentialsDataSize.set(credentialsSize);
    158  }
    159 }