DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (1047d440caa5)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * This module implements client-side key stretching for use in Firefox
 * Accounts account creation and login.
 *
 * See https://github.com/mozilla/fxa-auth-server/wiki/onepw-protocol
 */

"use strict";

var EXPORTED_SYMBOLS = ["Credentials"];

const { Log } = ChromeUtils.import("resource://gre/modules/Log.jsm");
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { CryptoUtils } = ChromeUtils.import(
  "resource://services-crypto/utils.js"
);
const { CommonUtils } = ChromeUtils.import(
  "resource://services-common/utils.js"
);

const PROTOCOL_VERSION = "identity.mozilla.com/picl/v1/";
const PBKDF2_ROUNDS = 1000;
const STRETCHED_PW_LENGTH_BYTES = 32;
const HKDF_SALT = CommonUtils.hexToBytes("00");
const HKDF_LENGTH = 32;

// loglevel preference should be one of: "FATAL", "ERROR", "WARN", "INFO",
// "CONFIG", "DEBUG", "TRACE" or "ALL". We will be logging error messages by
// default.
const PREF_LOG_LEVEL = "identity.fxaccounts.loglevel";
try {
  this.LOG_LEVEL =
    Services.prefs.getPrefType(PREF_LOG_LEVEL) ==
      Ci.nsIPrefBranch.PREF_STRING &&
    Services.prefs.getCharPref(PREF_LOG_LEVEL);
} catch (e) {
  this.LOG_LEVEL = Log.Level.Error;
}

var log = Log.repository.getLogger("Identity.FxAccounts");
log.level = LOG_LEVEL;
log.addAppender(new Log.ConsoleAppender(new Log.BasicFormatter()));

var Credentials = Object.freeze({
  /**
   * Make constants accessible to tests
   */
  constants: {
    PROTOCOL_VERSION,
    PBKDF2_ROUNDS,
    STRETCHED_PW_LENGTH_BYTES,
    HKDF_SALT,
    HKDF_LENGTH,
  },

  /**
   * KW function from https://github.com/mozilla/fxa-auth-server/wiki/onepw-protocol
   *
   * keyWord derivation for use as a salt.
   *
   *
   *   @param {String} context  String for use in generating salt
   *
   *   @return {bitArray} the salt
   *
   * Note that PROTOCOL_VERSION does not refer in any way to the version of the
   * Firefox Accounts API.
   */
  keyWord(context) {
    return CommonUtils.stringToBytes(PROTOCOL_VERSION + context);
  },

  /**
   * KWE function from https://github.com/mozilla/fxa-auth-server/wiki/onepw-protocol
   *
   * keyWord extended with a name and an email.
   *
   *   @param {String} name The name of the salt
   *   @param {String} email The email of the user.
   *
   *   @return {bitArray} the salt combination with the namespace
   *
   * Note that PROTOCOL_VERSION does not refer in any way to the version of the
   * Firefox Accounts API.
   */
  keyWordExtended(name, email) {
    return CommonUtils.stringToBytes(PROTOCOL_VERSION + name + ":" + email);
  },

  setup(emailInput, passwordInput, options = {}) {
    return new Promise(resolve => {
      log.debug("setup credentials for " + emailInput);

      let hkdfSalt = options.hkdfSalt || HKDF_SALT;
      let hkdfLength = options.hkdfLength || HKDF_LENGTH;
      let stretchedPWLength =
        options.stretchedPassLength || STRETCHED_PW_LENGTH_BYTES;
      let pbkdf2Rounds = options.pbkdf2Rounds || PBKDF2_ROUNDS;

      let result = {};

      let password = CommonUtils.encodeUTF8(passwordInput);
      let salt = this.keyWordExtended("quickStretch", emailInput);

      let runnable = async () => {
        let start = Date.now();
        let quickStretchedPW = await CryptoUtils.pbkdf2Generate(
          password,
          salt,
          pbkdf2Rounds,
          stretchedPWLength
        );

        result.quickStretchedPW = quickStretchedPW;

        result.authPW = await CryptoUtils.hkdfLegacy(
          quickStretchedPW,
          hkdfSalt,
          this.keyWord("authPW"),
          hkdfLength
        );

        result.unwrapBKey = await CryptoUtils.hkdfLegacy(
          quickStretchedPW,
          hkdfSalt,
          this.keyWord("unwrapBkey"),
          hkdfLength
        );

        log.debug("Credentials set up after " + (Date.now() - start) + " ms");
        resolve(result);
      };

      Services.tm.dispatchToMainThread(runnable);
      log.debug("Dispatched thread for credentials setup crypto work");
    });
  },
});