Bumping manifests a=b2g-bump
[gecko.git] / services / sync / modules / keys.js
blobbf909bdc20bbe058c9c1177d2bb3320d1a13180b
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/. */
5 "use strict";
7 this.EXPORTED_SYMBOLS = [
8   "BulkKeyBundle",
9   "SyncKeyBundle"
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");
18 /**
19  * Represents a pair of keys.
20  *
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.
23  *
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.
27  */
28 function KeyBundle() {
29   this._encrypt = null;
30   this._encryptB64 = null;
31   this._hmac = null;
32   this._hmacB64 = null;
33   this._hmacObj = null;
34   this._sha256HMACHasher = null;
36 KeyBundle.prototype = {
37   _encrypt: null,
38   _encryptB64: null,
39   _hmac: null,
40   _hmacB64: null,
41   _hmacObj: null,
42   _sha256HMACHasher: null,
44   equals: function equals(bundle) {
45     return bundle &&
46            (bundle.hmacKey == this.hmacKey) &&
47            (bundle.encryptionKey == this.encryptionKey);
48   },
50   /*
51    * Accessors for the two keys.
52    */
53   get encryptionKey() {
54     return this._encrypt;
55   },
57   set encryptionKey(value) {
58     if (!value || typeof value != "string") {
59       throw new Error("Encryption key can only be set to string values.");
60     }
62     if (value.length < 16) {
63       throw new Error("Encryption key must be at least 128 bits long.");
64     }
66     this._encrypt = value;
67     this._encryptB64 = btoa(value);
68   },
70   get encryptionKeyB64() {
71     return this._encryptB64;
72   },
74   get hmacKey() {
75     return this._hmac;
76   },
78   set hmacKey(value) {
79     if (!value || typeof value != "string") {
80       throw new Error("HMAC key can only be set to string values.");
81     }
83     if (value.length < 16) {
84       throw new Error("HMAC key must be at least 128 bits long.");
85     }
87     this._hmac = value;
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;
92   },
94   get hmacKeyB64() {
95     return this._hmacB64;
96   },
98   get hmacKeyObject() {
99     return this._hmacObj;
100   },
102   get sha256HMACHasher() {
103     return this._sha256HMACHasher;
104   },
106   /**
107    * Populate this key pair with 2 new, randomly generated keys.
108    */
109   generateRandom: function generateRandom() {
110     let generatedHMAC = Svc.Crypto.generateRandomKey();
111     let generatedEncr = Svc.Crypto.generateRandomKey();
112     this.keyPairB64 = [generatedEncr, generatedHMAC];
113   },
118  * Represents a KeyBundle associated with a collection.
120  * This is just a KeyBundle with a collection attached.
121  */
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,
133   get collection() {
134     return this._collection;
135   },
137   /**
138    * Obtain the key pair in this key bundle.
139    *
140    * The returned keys are represented as raw byte strings.
141    */
142   get keyPair() {
143     return [this.encryptionKey, this.hmacKey];
144   },
146   set keyPair(value) {
147     if (!Array.isArray(value) || value.length != 2) {
148       throw new Error("BulkKeyBundle.keyPair value must be array of 2 keys.");
149     }
151     this.encryptionKey = value[0];
152     this.hmacKey       = value[1];
153   },
155   get keyPairB64() {
156     return [this.encryptionKeyB64, this.hmacKeyB64];
157   },
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 " +
162                       "keys.");
163     }
165     this.encryptionKey  = Utils.safeAtoB(value[0]);
166     this.hmacKey        = Utils.safeAtoB(value[1]);
167   },
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.
178  */
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,
189   /*
190    * If we've got a string, hash it into keys and store them.
191    */
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.");
195     }
197     if (!syncKey || (typeof syncKey != "string")) {
198       throw new Error("Sync Key cannot be generated from non-string key.");
199     }
201     if (!Utils.isPassphrase(syncKey)) {
202       throw new Error("Provided key is not a passphrase, cannot derive Sync " +
203                       "Key Bundle.");
204     }
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);
212   },