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/. */
7 this.EXPORTED_SYMBOLS = [
12 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
14 Cu.import("resource://services-sync/constants.js");
15 Cu.import("resource://gre/modules/Log.jsm");
16 Cu.import("resource://services-sync/util.js");
19 * Represents a pair of keys.
21 * Each key stored in a key bundle is 256 bits. One key is used for symmetric
22 * encryption. The other is used for HMAC.
24 * A KeyBundle by itself is just an anonymous pair of keys. Other types
25 * deriving from this one add semantics, such as associated collections or
26 * generating a key bundle via HKDF from another key.
28 function KeyBundle() {
30 this._encryptB64 = null;
34 this._sha256HMACHasher = null;
36 KeyBundle.prototype = {
42 _sha256HMACHasher: null,
44 equals: function equals(bundle) {
46 (bundle.hmacKey == this.hmacKey) &&
47 (bundle.encryptionKey == this.encryptionKey);
51 * Accessors for the two keys.
57 set encryptionKey(value) {
58 if (!value || typeof value != "string") {
59 throw new Error("Encryption key can only be set to string values.");
62 if (value.length < 16) {
63 throw new Error("Encryption key must be at least 128 bits long.");
66 this._encrypt = value;
67 this._encryptB64 = btoa(value);
70 get encryptionKeyB64() {
71 return this._encryptB64;
79 if (!value || typeof value != "string") {
80 throw new Error("HMAC key can only be set to string values.");
83 if (value.length < 16) {
84 throw new Error("HMAC key must be at least 128 bits long.");
88 this._hmacB64 = btoa(value);
89 this._hmacObj = value ? Utils.makeHMACKey(value) : null;
90 this._sha256HMACHasher = value ? Utils.makeHMACHasher(
91 Ci.nsICryptoHMAC.SHA256, this._hmacObj) : null;
102 get sha256HMACHasher() {
103 return this._sha256HMACHasher;
107 * Populate this key pair with 2 new, randomly generated keys.
109 generateRandom: function generateRandom() {
110 let generatedHMAC = Svc.Crypto.generateRandomKey();
111 let generatedEncr = Svc.Crypto.generateRandomKey();
112 this.keyPairB64 = [generatedEncr, generatedHMAC];
118 * Represents a KeyBundle associated with a collection.
120 * This is just a KeyBundle with a collection attached.
122 this.BulkKeyBundle = function BulkKeyBundle(collection) {
123 let log = Log.repository.getLogger("Sync.BulkKeyBundle");
124 log.info("BulkKeyBundle being created for " + collection);
125 KeyBundle.call(this);
127 this._collection = collection;
130 BulkKeyBundle.prototype = {
131 __proto__: KeyBundle.prototype,
134 return this._collection;
138 * Obtain the key pair in this key bundle.
140 * The returned keys are represented as raw byte strings.
143 return [this.encryptionKey, this.hmacKey];
147 if (!Array.isArray(value) || value.length != 2) {
148 throw new Error("BulkKeyBundle.keyPair value must be array of 2 keys.");
151 this.encryptionKey = value[0];
152 this.hmacKey = value[1];
156 return [this.encryptionKeyB64, this.hmacKeyB64];
159 set keyPairB64(value) {
160 if (!Array.isArray(value) || value.length != 2) {
161 throw new Error("BulkKeyBundle.keyPairB64 value must be an array of 2 " +
165 this.encryptionKey = Utils.safeAtoB(value[0]);
166 this.hmacKey = Utils.safeAtoB(value[1]);
171 * Represents a key pair derived from a Sync Key via HKDF.
173 * Instances of this type should be considered immutable. You create an
174 * instance by specifying the username and 26 character "friendly" Base32
175 * encoded Sync Key. The Sync Key is derived at instance creation time.
177 * If the username or Sync Key is invalid, an Error will be thrown.
179 this.SyncKeyBundle = function SyncKeyBundle(username, syncKey) {
180 let log = Log.repository.getLogger("Sync.SyncKeyBundle");
181 log.info("SyncKeyBundle being created.");
182 KeyBundle.call(this);
184 this.generateFromKey(username, syncKey);
186 SyncKeyBundle.prototype = {
187 __proto__: KeyBundle.prototype,
190 * If we've got a string, hash it into keys and store them.
192 generateFromKey: function generateFromKey(username, syncKey) {
193 if (!username || (typeof username != "string")) {
194 throw new Error("Sync Key cannot be generated from non-string username.");
197 if (!syncKey || (typeof syncKey != "string")) {
198 throw new Error("Sync Key cannot be generated from non-string key.");
201 if (!Utils.isPassphrase(syncKey)) {
202 throw new Error("Provided key is not a passphrase, cannot derive Sync " +
206 // Expand the base32 Sync Key to an AES 256 and 256 bit HMAC key.
207 let prk = Utils.decodeKeyBase32(syncKey);
208 let info = HMAC_INPUT + username;
209 let okm = Utils.hkdfExpand(prk, info, 32 * 2);
210 this.encryptionKey = okm.slice(0, 32);
211 this.hmacKey = okm.slice(32, 64);