|
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/. */ |
|
4 |
|
5 "use strict"; |
|
6 |
|
7 this.EXPORTED_SYMBOLS = [ |
|
8 "BulkKeyBundle", |
|
9 "SyncKeyBundle" |
|
10 ]; |
|
11 |
|
12 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; |
|
13 |
|
14 Cu.import("resource://services-sync/constants.js"); |
|
15 Cu.import("resource://gre/modules/Log.jsm"); |
|
16 Cu.import("resource://services-sync/util.js"); |
|
17 |
|
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; |
|
35 } |
|
36 KeyBundle.prototype = { |
|
37 _encrypt: null, |
|
38 _encryptB64: null, |
|
39 _hmac: null, |
|
40 _hmacB64: null, |
|
41 _hmacObj: null, |
|
42 _sha256HMACHasher: null, |
|
43 |
|
44 equals: function equals(bundle) { |
|
45 return bundle && |
|
46 (bundle.hmacKey == this.hmacKey) && |
|
47 (bundle.encryptionKey == this.encryptionKey); |
|
48 }, |
|
49 |
|
50 /* |
|
51 * Accessors for the two keys. |
|
52 */ |
|
53 get encryptionKey() { |
|
54 return this._encrypt; |
|
55 }, |
|
56 |
|
57 set encryptionKey(value) { |
|
58 if (!value || typeof value != "string") { |
|
59 throw new Error("Encryption key can only be set to string values."); |
|
60 } |
|
61 |
|
62 if (value.length < 16) { |
|
63 throw new Error("Encryption key must be at least 128 bits long."); |
|
64 } |
|
65 |
|
66 this._encrypt = value; |
|
67 this._encryptB64 = btoa(value); |
|
68 }, |
|
69 |
|
70 get encryptionKeyB64() { |
|
71 return this._encryptB64; |
|
72 }, |
|
73 |
|
74 get hmacKey() { |
|
75 return this._hmac; |
|
76 }, |
|
77 |
|
78 set hmacKey(value) { |
|
79 if (!value || typeof value != "string") { |
|
80 throw new Error("HMAC key can only be set to string values."); |
|
81 } |
|
82 |
|
83 if (value.length < 16) { |
|
84 throw new Error("HMAC key must be at least 128 bits long."); |
|
85 } |
|
86 |
|
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 }, |
|
93 |
|
94 get hmacKeyB64() { |
|
95 return this._hmacB64; |
|
96 }, |
|
97 |
|
98 get hmacKeyObject() { |
|
99 return this._hmacObj; |
|
100 }, |
|
101 |
|
102 get sha256HMACHasher() { |
|
103 return this._sha256HMACHasher; |
|
104 }, |
|
105 |
|
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 }, |
|
114 |
|
115 }; |
|
116 |
|
117 /** |
|
118 * Represents a KeyBundle associated with a collection. |
|
119 * |
|
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); |
|
126 |
|
127 this._collection = collection; |
|
128 } |
|
129 |
|
130 BulkKeyBundle.prototype = { |
|
131 __proto__: KeyBundle.prototype, |
|
132 |
|
133 get collection() { |
|
134 return this._collection; |
|
135 }, |
|
136 |
|
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 }, |
|
145 |
|
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 } |
|
150 |
|
151 this.encryptionKey = value[0]; |
|
152 this.hmacKey = value[1]; |
|
153 }, |
|
154 |
|
155 get keyPairB64() { |
|
156 return [this.encryptionKeyB64, this.hmacKeyB64]; |
|
157 }, |
|
158 |
|
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 } |
|
164 |
|
165 this.encryptionKey = Utils.safeAtoB(value[0]); |
|
166 this.hmacKey = Utils.safeAtoB(value[1]); |
|
167 }, |
|
168 }; |
|
169 |
|
170 /** |
|
171 * Represents a key pair derived from a Sync Key via HKDF. |
|
172 * |
|
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. |
|
176 * |
|
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); |
|
183 |
|
184 this.generateFromKey(username, syncKey); |
|
185 } |
|
186 SyncKeyBundle.prototype = { |
|
187 __proto__: KeyBundle.prototype, |
|
188 |
|
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 } |
|
196 |
|
197 if (!syncKey || (typeof syncKey != "string")) { |
|
198 throw new Error("Sync Key cannot be generated from non-string key."); |
|
199 } |
|
200 |
|
201 if (!Utils.isPassphrase(syncKey)) { |
|
202 throw new Error("Provided key is not a passphrase, cannot derive Sync " + |
|
203 "Key Bundle."); |
|
204 } |
|
205 |
|
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 }, |
|
213 }; |
|
214 |