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 file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 * This module implements client-side key stretching for use in Firefox
7 * Accounts account creation and login.
9 * See https://github.com/mozilla/fxa-auth-server/wiki/onepw-protocol
12 import { Log } from "resource://gre/modules/Log.sys.mjs";
14 import { CryptoUtils } from "resource://services-crypto/utils.sys.mjs";
16 import { CommonUtils } from "resource://services-common/utils.sys.mjs";
18 const PROTOCOL_VERSION = "identity.mozilla.com/picl/v1/";
19 const PBKDF2_ROUNDS = 1000;
20 const STRETCHED_PW_LENGTH_BYTES = 32;
21 const HKDF_SALT = CommonUtils.hexToBytes("00");
22 const HKDF_LENGTH = 32;
24 // loglevel preference should be one of: "FATAL", "ERROR", "WARN", "INFO",
25 // "CONFIG", "DEBUG", "TRACE" or "ALL". We will be logging error messages by
27 const PREF_LOG_LEVEL = "identity.fxaccounts.loglevel";
28 let LOG_LEVEL = Log.Level.Error;
31 Services.prefs.getPrefType(PREF_LOG_LEVEL) ==
32 Ci.nsIPrefBranch.PREF_STRING &&
33 Services.prefs.getCharPref(PREF_LOG_LEVEL);
36 var log = Log.repository.getLogger("Identity.FxAccounts");
37 log.level = LOG_LEVEL;
38 log.addAppender(new Log.ConsoleAppender(new Log.BasicFormatter()));
40 export var Credentials = Object.freeze({
42 * Make constants accessible to tests
47 STRETCHED_PW_LENGTH_BYTES,
53 * KW function from https://github.com/mozilla/fxa-auth-server/wiki/onepw-protocol
55 * keyWord derivation for use as a salt.
58 * @param {String} context String for use in generating salt
60 * @return {bitArray} the salt
62 * Note that PROTOCOL_VERSION does not refer in any way to the version of the
63 * Firefox Accounts API.
66 return CommonUtils.stringToBytes(PROTOCOL_VERSION + context);
70 * KWE function from https://github.com/mozilla/fxa-auth-server/wiki/onepw-protocol
72 * keyWord extended with a name and an email.
74 * @param {String} name The name of the salt
75 * @param {String} email The email of the user.
77 * @return {bitArray} the salt combination with the namespace
79 * Note that PROTOCOL_VERSION does not refer in any way to the version of the
80 * Firefox Accounts API.
82 keyWordExtended(name, email) {
83 return CommonUtils.stringToBytes(PROTOCOL_VERSION + name + ":" + email);
86 setup(emailInput, passwordInput, options = {}) {
87 return new Promise(resolve => {
88 log.debug("setup credentials for " + emailInput);
90 let hkdfSalt = options.hkdfSalt || HKDF_SALT;
91 let hkdfLength = options.hkdfLength || HKDF_LENGTH;
92 let stretchedPWLength =
93 options.stretchedPassLength || STRETCHED_PW_LENGTH_BYTES;
94 let pbkdf2Rounds = options.pbkdf2Rounds || PBKDF2_ROUNDS;
98 let password = CommonUtils.encodeUTF8(passwordInput);
99 let salt = this.keyWordExtended("quickStretch", emailInput);
101 let runnable = async () => {
102 let start = Date.now();
103 let quickStretchedPW = await CryptoUtils.pbkdf2Generate(
110 result.quickStretchedPW = quickStretchedPW;
112 result.authPW = await CryptoUtils.hkdfLegacy(
115 this.keyWord("authPW"),
119 result.unwrapBKey = await CryptoUtils.hkdfLegacy(
122 this.keyWord("unwrapBkey"),
126 log.debug("Credentials set up after " + (Date.now() - start) + " ms");
130 Services.tm.dispatchToMainThread(runnable);
131 log.debug("Dispatched thread for credentials setup crypto work");