|
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 const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components; |
|
6 |
|
7 this.EXPORTED_SYMBOLS = ["CryptoUtils"]; |
|
8 |
|
9 Cu.import("resource://services-common/observers.js"); |
|
10 Cu.import("resource://services-common/utils.js"); |
|
11 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
12 |
|
13 this.CryptoUtils = { |
|
14 xor: function xor(a, b) { |
|
15 let bytes = []; |
|
16 |
|
17 if (a.length != b.length) { |
|
18 throw new Error("can't xor unequal length strings: "+a.length+" vs "+b.length); |
|
19 } |
|
20 |
|
21 for (let i = 0; i < a.length; i++) { |
|
22 bytes[i] = a.charCodeAt(i) ^ b.charCodeAt(i); |
|
23 } |
|
24 |
|
25 return String.fromCharCode.apply(String, bytes); |
|
26 }, |
|
27 |
|
28 /** |
|
29 * Generate a string of random bytes. |
|
30 */ |
|
31 generateRandomBytes: function generateRandomBytes(length) { |
|
32 let rng = Cc["@mozilla.org/security/random-generator;1"] |
|
33 .createInstance(Ci.nsIRandomGenerator); |
|
34 let bytes = rng.generateRandomBytes(length); |
|
35 return CommonUtils.byteArrayToString(bytes); |
|
36 }, |
|
37 |
|
38 /** |
|
39 * UTF8-encode a message and hash it with the given hasher. Returns a |
|
40 * string containing bytes. The hasher is reset if it's an HMAC hasher. |
|
41 */ |
|
42 digestUTF8: function digestUTF8(message, hasher) { |
|
43 let data = this._utf8Converter.convertToByteArray(message, {}); |
|
44 hasher.update(data, data.length); |
|
45 let result = hasher.finish(false); |
|
46 if (hasher instanceof Ci.nsICryptoHMAC) { |
|
47 hasher.reset(); |
|
48 } |
|
49 return result; |
|
50 }, |
|
51 |
|
52 /** |
|
53 * Treat the given message as a bytes string and hash it with the given |
|
54 * hasher. Returns a string containing bytes. The hasher is reset if it's |
|
55 * an HMAC hasher. |
|
56 */ |
|
57 digestBytes: function digestBytes(message, hasher) { |
|
58 // No UTF-8 encoding for you, sunshine. |
|
59 let bytes = [b.charCodeAt() for each (b in message)]; |
|
60 hasher.update(bytes, bytes.length); |
|
61 let result = hasher.finish(false); |
|
62 if (hasher instanceof Ci.nsICryptoHMAC) { |
|
63 hasher.reset(); |
|
64 } |
|
65 return result; |
|
66 }, |
|
67 |
|
68 /** |
|
69 * Encode the message into UTF-8 and feed the resulting bytes into the |
|
70 * given hasher. Does not return a hash. This can be called multiple times |
|
71 * with a single hasher, but eventually you must extract the result |
|
72 * yourself. |
|
73 */ |
|
74 updateUTF8: function(message, hasher) { |
|
75 let bytes = this._utf8Converter.convertToByteArray(message, {}); |
|
76 hasher.update(bytes, bytes.length); |
|
77 }, |
|
78 |
|
79 /** |
|
80 * UTF-8 encode a message and perform a SHA-1 over it. |
|
81 * |
|
82 * @param message |
|
83 * (string) Buffer to perform operation on. Should be a JS string. |
|
84 * It is possible to pass in a string representing an array |
|
85 * of bytes. But, you probably don't want to UTF-8 encode |
|
86 * such data and thus should not be using this function. |
|
87 * |
|
88 * @return string |
|
89 * Raw bytes constituting SHA-1 hash. Value is a JS string. Each |
|
90 * character is the byte value for that offset. Returned string |
|
91 * always has .length == 20. |
|
92 */ |
|
93 UTF8AndSHA1: function UTF8AndSHA1(message) { |
|
94 let hasher = Cc["@mozilla.org/security/hash;1"] |
|
95 .createInstance(Ci.nsICryptoHash); |
|
96 hasher.init(hasher.SHA1); |
|
97 |
|
98 return CryptoUtils.digestUTF8(message, hasher); |
|
99 }, |
|
100 |
|
101 sha1: function sha1(message) { |
|
102 return CommonUtils.bytesAsHex(CryptoUtils.UTF8AndSHA1(message)); |
|
103 }, |
|
104 |
|
105 sha1Base32: function sha1Base32(message) { |
|
106 return CommonUtils.encodeBase32(CryptoUtils.UTF8AndSHA1(message)); |
|
107 }, |
|
108 |
|
109 /** |
|
110 * Produce an HMAC key object from a key string. |
|
111 */ |
|
112 makeHMACKey: function makeHMACKey(str) { |
|
113 return Svc.KeyFactory.keyFromString(Ci.nsIKeyObject.HMAC, str); |
|
114 }, |
|
115 |
|
116 /** |
|
117 * Produce an HMAC hasher and initialize it with the given HMAC key. |
|
118 */ |
|
119 makeHMACHasher: function makeHMACHasher(type, key) { |
|
120 let hasher = Cc["@mozilla.org/security/hmac;1"] |
|
121 .createInstance(Ci.nsICryptoHMAC); |
|
122 hasher.init(type, key); |
|
123 return hasher; |
|
124 }, |
|
125 |
|
126 /** |
|
127 * HMAC-based Key Derivation (RFC 5869). |
|
128 */ |
|
129 hkdf: function hkdf(ikm, xts, info, len) { |
|
130 const BLOCKSIZE = 256 / 8; |
|
131 if (typeof xts === undefined) |
|
132 xts = String.fromCharCode(0, 0, 0, 0, 0, 0, 0, 0, |
|
133 0, 0, 0, 0, 0, 0, 0, 0, |
|
134 0, 0, 0, 0, 0, 0, 0, 0, |
|
135 0, 0, 0, 0, 0, 0, 0, 0); |
|
136 let h = CryptoUtils.makeHMACHasher(Ci.nsICryptoHMAC.SHA256, |
|
137 CryptoUtils.makeHMACKey(xts)); |
|
138 let prk = CryptoUtils.digestBytes(ikm, h); |
|
139 return CryptoUtils.hkdfExpand(prk, info, len); |
|
140 }, |
|
141 |
|
142 /** |
|
143 * HMAC-based Key Derivation Step 2 according to RFC 5869. |
|
144 */ |
|
145 hkdfExpand: function hkdfExpand(prk, info, len) { |
|
146 const BLOCKSIZE = 256 / 8; |
|
147 let h = CryptoUtils.makeHMACHasher(Ci.nsICryptoHMAC.SHA256, |
|
148 CryptoUtils.makeHMACKey(prk)); |
|
149 let T = ""; |
|
150 let Tn = ""; |
|
151 let iterations = Math.ceil(len/BLOCKSIZE); |
|
152 for (let i = 0; i < iterations; i++) { |
|
153 Tn = CryptoUtils.digestBytes(Tn + info + String.fromCharCode(i + 1), h); |
|
154 T += Tn; |
|
155 } |
|
156 return T.slice(0, len); |
|
157 }, |
|
158 |
|
159 /** |
|
160 * PBKDF2 implementation in Javascript. |
|
161 * |
|
162 * The arguments to this function correspond to items in |
|
163 * PKCS #5, v2.0 pp. 9-10 |
|
164 * |
|
165 * P: the passphrase, an octet string: e.g., "secret phrase" |
|
166 * S: the salt, an octet string: e.g., "DNXPzPpiwn" |
|
167 * c: the number of iterations, a positive integer: e.g., 4096 |
|
168 * dkLen: the length in octets of the destination |
|
169 * key, a positive integer: e.g., 16 |
|
170 * hmacAlg: The algorithm to use for hmac |
|
171 * hmacLen: The hmac length |
|
172 * |
|
173 * The default value of 20 for hmacLen is appropriate for SHA1. For SHA256, |
|
174 * hmacLen should be 32. |
|
175 * |
|
176 * The output is an octet string of length dkLen, which you |
|
177 * can encode as you wish. |
|
178 */ |
|
179 pbkdf2Generate : function pbkdf2Generate(P, S, c, dkLen, |
|
180 hmacAlg=Ci.nsICryptoHMAC.SHA1, hmacLen=20) { |
|
181 |
|
182 // We don't have a default in the algo itself, as NSS does. |
|
183 // Use the constant. |
|
184 if (!dkLen) { |
|
185 dkLen = SYNC_KEY_DECODED_LENGTH; |
|
186 } |
|
187 |
|
188 function F(S, c, i, h) { |
|
189 |
|
190 function XOR(a, b, isA) { |
|
191 if (a.length != b.length) { |
|
192 return false; |
|
193 } |
|
194 |
|
195 let val = []; |
|
196 for (let i = 0; i < a.length; i++) { |
|
197 if (isA) { |
|
198 val[i] = a[i] ^ b[i]; |
|
199 } else { |
|
200 val[i] = a.charCodeAt(i) ^ b.charCodeAt(i); |
|
201 } |
|
202 } |
|
203 |
|
204 return val; |
|
205 } |
|
206 |
|
207 let ret; |
|
208 let U = []; |
|
209 |
|
210 /* Encode i into 4 octets: _INT */ |
|
211 let I = []; |
|
212 I[0] = String.fromCharCode((i >> 24) & 0xff); |
|
213 I[1] = String.fromCharCode((i >> 16) & 0xff); |
|
214 I[2] = String.fromCharCode((i >> 8) & 0xff); |
|
215 I[3] = String.fromCharCode(i & 0xff); |
|
216 |
|
217 U[0] = CryptoUtils.digestBytes(S + I.join(''), h); |
|
218 for (let j = 1; j < c; j++) { |
|
219 U[j] = CryptoUtils.digestBytes(U[j - 1], h); |
|
220 } |
|
221 |
|
222 ret = U[0]; |
|
223 for (let j = 1; j < c; j++) { |
|
224 ret = CommonUtils.byteArrayToString(XOR(ret, U[j])); |
|
225 } |
|
226 |
|
227 return ret; |
|
228 } |
|
229 |
|
230 let l = Math.ceil(dkLen / hmacLen); |
|
231 let r = dkLen - ((l - 1) * hmacLen); |
|
232 |
|
233 // Reuse the key and the hasher. Remaking them 4096 times is 'spensive. |
|
234 let h = CryptoUtils.makeHMACHasher(hmacAlg, |
|
235 CryptoUtils.makeHMACKey(P)); |
|
236 |
|
237 let T = []; |
|
238 for (let i = 0; i < l;) { |
|
239 T[i] = F(S, c, ++i, h); |
|
240 } |
|
241 |
|
242 let ret = ""; |
|
243 for (let i = 0; i < l-1;) { |
|
244 ret += T[i++]; |
|
245 } |
|
246 ret += T[l - 1].substr(0, r); |
|
247 |
|
248 return ret; |
|
249 }, |
|
250 |
|
251 deriveKeyFromPassphrase: function deriveKeyFromPassphrase(passphrase, |
|
252 salt, |
|
253 keyLength, |
|
254 forceJS) { |
|
255 if (Svc.Crypto.deriveKeyFromPassphrase && !forceJS) { |
|
256 return Svc.Crypto.deriveKeyFromPassphrase(passphrase, salt, keyLength); |
|
257 } |
|
258 else { |
|
259 // Fall back to JS implementation. |
|
260 // 4096 is hardcoded in WeaveCrypto, so do so here. |
|
261 return CryptoUtils.pbkdf2Generate(passphrase, atob(salt), 4096, |
|
262 keyLength); |
|
263 } |
|
264 }, |
|
265 |
|
266 /** |
|
267 * Compute the HTTP MAC SHA-1 for an HTTP request. |
|
268 * |
|
269 * @param identifier |
|
270 * (string) MAC Key Identifier. |
|
271 * @param key |
|
272 * (string) MAC Key. |
|
273 * @param method |
|
274 * (string) HTTP request method. |
|
275 * @param URI |
|
276 * (nsIURI) HTTP request URI. |
|
277 * @param extra |
|
278 * (object) Optional extra parameters. Valid keys are: |
|
279 * nonce_bytes - How many bytes the nonce should be. This defaults |
|
280 * to 8. Note that this many bytes are Base64 encoded, so the |
|
281 * string length of the nonce will be longer than this value. |
|
282 * ts - Timestamp to use. Should only be defined for testing. |
|
283 * nonce - String nonce. Should only be defined for testing as this |
|
284 * function will generate a cryptographically secure random one |
|
285 * if not defined. |
|
286 * ext - Extra string to be included in MAC. Per the HTTP MAC spec, |
|
287 * the format is undefined and thus application specific. |
|
288 * @returns |
|
289 * (object) Contains results of operation and input arguments (for |
|
290 * symmetry). The object has the following keys: |
|
291 * |
|
292 * identifier - (string) MAC Key Identifier (from arguments). |
|
293 * key - (string) MAC Key (from arguments). |
|
294 * method - (string) HTTP request method (from arguments). |
|
295 * hostname - (string) HTTP hostname used (derived from arguments). |
|
296 * port - (string) HTTP port number used (derived from arguments). |
|
297 * mac - (string) Raw HMAC digest bytes. |
|
298 * getHeader - (function) Call to obtain the string Authorization |
|
299 * header value for this invocation. |
|
300 * nonce - (string) Nonce value used. |
|
301 * ts - (number) Integer seconds since Unix epoch that was used. |
|
302 */ |
|
303 computeHTTPMACSHA1: function computeHTTPMACSHA1(identifier, key, method, |
|
304 uri, extra) { |
|
305 let ts = (extra && extra.ts) ? extra.ts : Math.floor(Date.now() / 1000); |
|
306 let nonce_bytes = (extra && extra.nonce_bytes > 0) ? extra.nonce_bytes : 8; |
|
307 |
|
308 // We are allowed to use more than the Base64 alphabet if we want. |
|
309 let nonce = (extra && extra.nonce) |
|
310 ? extra.nonce |
|
311 : btoa(CryptoUtils.generateRandomBytes(nonce_bytes)); |
|
312 |
|
313 let host = uri.asciiHost; |
|
314 let port; |
|
315 let usedMethod = method.toUpperCase(); |
|
316 |
|
317 if (uri.port != -1) { |
|
318 port = uri.port; |
|
319 } else if (uri.scheme == "http") { |
|
320 port = "80"; |
|
321 } else if (uri.scheme == "https") { |
|
322 port = "443"; |
|
323 } else { |
|
324 throw new Error("Unsupported URI scheme: " + uri.scheme); |
|
325 } |
|
326 |
|
327 let ext = (extra && extra.ext) ? extra.ext : ""; |
|
328 |
|
329 let requestString = ts.toString(10) + "\n" + |
|
330 nonce + "\n" + |
|
331 usedMethod + "\n" + |
|
332 uri.path + "\n" + |
|
333 host + "\n" + |
|
334 port + "\n" + |
|
335 ext + "\n"; |
|
336 |
|
337 let hasher = CryptoUtils.makeHMACHasher(Ci.nsICryptoHMAC.SHA1, |
|
338 CryptoUtils.makeHMACKey(key)); |
|
339 let mac = CryptoUtils.digestBytes(requestString, hasher); |
|
340 |
|
341 function getHeader() { |
|
342 return CryptoUtils.getHTTPMACSHA1Header(this.identifier, this.ts, |
|
343 this.nonce, this.mac, this.ext); |
|
344 } |
|
345 |
|
346 return { |
|
347 identifier: identifier, |
|
348 key: key, |
|
349 method: usedMethod, |
|
350 hostname: host, |
|
351 port: port, |
|
352 mac: mac, |
|
353 nonce: nonce, |
|
354 ts: ts, |
|
355 ext: ext, |
|
356 getHeader: getHeader |
|
357 }; |
|
358 }, |
|
359 |
|
360 |
|
361 /** |
|
362 * Obtain the HTTP MAC Authorization header value from fields. |
|
363 * |
|
364 * @param identifier |
|
365 * (string) MAC key identifier. |
|
366 * @param ts |
|
367 * (number) Integer seconds since Unix epoch. |
|
368 * @param nonce |
|
369 * (string) Nonce value. |
|
370 * @param mac |
|
371 * (string) Computed HMAC digest (raw bytes). |
|
372 * @param ext |
|
373 * (optional) (string) Extra string content. |
|
374 * @returns |
|
375 * (string) Value to put in Authorization header. |
|
376 */ |
|
377 getHTTPMACSHA1Header: function getHTTPMACSHA1Header(identifier, ts, nonce, |
|
378 mac, ext) { |
|
379 let header ='MAC id="' + identifier + '", ' + |
|
380 'ts="' + ts + '", ' + |
|
381 'nonce="' + nonce + '", ' + |
|
382 'mac="' + btoa(mac) + '"'; |
|
383 |
|
384 if (!ext) { |
|
385 return header; |
|
386 } |
|
387 |
|
388 return header += ', ext="' + ext +'"'; |
|
389 }, |
|
390 |
|
391 /** |
|
392 * Given an HTTP header value, strip out any attributes. |
|
393 */ |
|
394 |
|
395 stripHeaderAttributes: function(value) { |
|
396 let value = value || ""; |
|
397 let i = value.indexOf(";"); |
|
398 return value.substring(0, (i >= 0) ? i : undefined).trim().toLowerCase(); |
|
399 }, |
|
400 |
|
401 /** |
|
402 * Compute the HAWK client values (mostly the header) for an HTTP request. |
|
403 * |
|
404 * @param URI |
|
405 * (nsIURI) HTTP request URI. |
|
406 * @param method |
|
407 * (string) HTTP request method. |
|
408 * @param options |
|
409 * (object) extra parameters (all but "credentials" are optional): |
|
410 * credentials - (object, mandatory) HAWK credentials object. |
|
411 * All three keys are required: |
|
412 * id - (string) key identifier |
|
413 * key - (string) raw key bytes |
|
414 * algorithm - (string) which hash to use: "sha1" or "sha256" |
|
415 * ext - (string) application-specific data, included in MAC |
|
416 * localtimeOffsetMsec - (number) local clock offset (vs server) |
|
417 * payload - (string) payload to include in hash, containing the |
|
418 * HTTP request body. If not provided, the HAWK hash |
|
419 * will not cover the request body, and the server |
|
420 * should not check it either. This will be UTF-8 |
|
421 * encoded into bytes before hashing. This function |
|
422 * cannot handle arbitrary binary data, sorry (the |
|
423 * UTF-8 encoding process will corrupt any codepoints |
|
424 * between U+0080 and U+00FF). Callers must be careful |
|
425 * to use an HTTP client function which encodes the |
|
426 * payload exactly the same way, otherwise the hash |
|
427 * will not match. |
|
428 * contentType - (string) payload Content-Type. This is included |
|
429 * (without any attributes like "charset=") in the |
|
430 * HAWK hash. It does *not* affect interpretation |
|
431 * of the "payload" property. |
|
432 * hash - (base64 string) pre-calculated payload hash. If |
|
433 * provided, "payload" is ignored. |
|
434 * ts - (number) pre-calculated timestamp, secs since epoch |
|
435 * now - (number) current time, ms-since-epoch, for tests |
|
436 * nonce - (string) pre-calculated nonce. Should only be defined |
|
437 * for testing as this function will generate a |
|
438 * cryptographically secure random one if not defined. |
|
439 * @returns |
|
440 * (object) Contains results of operation. The object has the |
|
441 * following keys: |
|
442 * field - (string) HAWK header, to use in Authorization: header |
|
443 * artifacts - (object) other generated values: |
|
444 * ts - (number) timestamp, in seconds since epoch |
|
445 * nonce - (string) |
|
446 * method - (string) |
|
447 * resource - (string) path plus querystring |
|
448 * host - (string) |
|
449 * port - (number) |
|
450 * hash - (string) payload hash (base64) |
|
451 * ext - (string) app-specific data |
|
452 * MAC - (string) request MAC (base64) |
|
453 */ |
|
454 computeHAWK: function(uri, method, options) { |
|
455 let credentials = options.credentials; |
|
456 let ts = options.ts || Math.floor(((options.now || Date.now()) + |
|
457 (options.localtimeOffsetMsec || 0)) |
|
458 / 1000); |
|
459 |
|
460 let hash_algo, hmac_algo; |
|
461 if (credentials.algorithm == "sha1") { |
|
462 hash_algo = Ci.nsICryptoHash.SHA1; |
|
463 hmac_algo = Ci.nsICryptoHMAC.SHA1; |
|
464 } else if (credentials.algorithm == "sha256") { |
|
465 hash_algo = Ci.nsICryptoHash.SHA256; |
|
466 hmac_algo = Ci.nsICryptoHMAC.SHA256; |
|
467 } else { |
|
468 throw new Error("Unsupported algorithm: " + credentials.algorithm); |
|
469 } |
|
470 |
|
471 let port; |
|
472 if (uri.port != -1) { |
|
473 port = uri.port; |
|
474 } else if (uri.scheme == "http") { |
|
475 port = 80; |
|
476 } else if (uri.scheme == "https") { |
|
477 port = 443; |
|
478 } else { |
|
479 throw new Error("Unsupported URI scheme: " + uri.scheme); |
|
480 } |
|
481 |
|
482 let artifacts = { |
|
483 ts: ts, |
|
484 nonce: options.nonce || btoa(CryptoUtils.generateRandomBytes(8)), |
|
485 method: method.toUpperCase(), |
|
486 resource: uri.path, // This includes both path and search/queryarg. |
|
487 host: uri.asciiHost.toLowerCase(), // This includes punycoding. |
|
488 port: port.toString(10), |
|
489 hash: options.hash, |
|
490 ext: options.ext, |
|
491 }; |
|
492 |
|
493 let contentType = CryptoUtils.stripHeaderAttributes(options.contentType); |
|
494 |
|
495 if (!artifacts.hash && options.hasOwnProperty("payload") |
|
496 && options.payload) { |
|
497 let hasher = Cc["@mozilla.org/security/hash;1"] |
|
498 .createInstance(Ci.nsICryptoHash); |
|
499 hasher.init(hash_algo); |
|
500 CryptoUtils.updateUTF8("hawk.1.payload\n", hasher); |
|
501 CryptoUtils.updateUTF8(contentType+"\n", hasher); |
|
502 CryptoUtils.updateUTF8(options.payload, hasher); |
|
503 CryptoUtils.updateUTF8("\n", hasher); |
|
504 let hash = hasher.finish(false); |
|
505 // HAWK specifies this .hash to use +/ (not _-) and include the |
|
506 // trailing "==" padding. |
|
507 let hash_b64 = btoa(hash); |
|
508 artifacts.hash = hash_b64; |
|
509 } |
|
510 |
|
511 let requestString = ("hawk.1.header" + "\n" + |
|
512 artifacts.ts.toString(10) + "\n" + |
|
513 artifacts.nonce + "\n" + |
|
514 artifacts.method + "\n" + |
|
515 artifacts.resource + "\n" + |
|
516 artifacts.host + "\n" + |
|
517 artifacts.port + "\n" + |
|
518 (artifacts.hash || "") + "\n"); |
|
519 if (artifacts.ext) { |
|
520 requestString += artifacts.ext.replace("\\", "\\\\").replace("\n", "\\n"); |
|
521 } |
|
522 requestString += "\n"; |
|
523 |
|
524 let hasher = CryptoUtils.makeHMACHasher(hmac_algo, |
|
525 CryptoUtils.makeHMACKey(credentials.key)); |
|
526 artifacts.mac = btoa(CryptoUtils.digestBytes(requestString, hasher)); |
|
527 // The output MAC uses "+" and "/", and padded== . |
|
528 |
|
529 function escape(attribute) { |
|
530 // This is used for "x=y" attributes inside HTTP headers. |
|
531 return attribute.replace(/\\/g, "\\\\").replace(/\"/g, '\\"'); |
|
532 } |
|
533 let header = ('Hawk id="' + credentials.id + '", ' + |
|
534 'ts="' + artifacts.ts + '", ' + |
|
535 'nonce="' + artifacts.nonce + '", ' + |
|
536 (artifacts.hash ? ('hash="' + artifacts.hash + '", ') : "") + |
|
537 (artifacts.ext ? ('ext="' + escape(artifacts.ext) + '", ') : "") + |
|
538 'mac="' + artifacts.mac + '"'); |
|
539 return { |
|
540 artifacts: artifacts, |
|
541 field: header, |
|
542 }; |
|
543 }, |
|
544 |
|
545 }; |
|
546 |
|
547 XPCOMUtils.defineLazyGetter(CryptoUtils, "_utf8Converter", function() { |
|
548 let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"] |
|
549 .createInstance(Ci.nsIScriptableUnicodeConverter); |
|
550 converter.charset = "UTF-8"; |
|
551 |
|
552 return converter; |
|
553 }); |
|
554 |
|
555 let Svc = {}; |
|
556 |
|
557 XPCOMUtils.defineLazyServiceGetter(Svc, |
|
558 "KeyFactory", |
|
559 "@mozilla.org/security/keyobjectfactory;1", |
|
560 "nsIKeyObjectFactory"); |
|
561 |
|
562 Svc.__defineGetter__("Crypto", function() { |
|
563 let ns = {}; |
|
564 Cu.import("resource://services-crypto/WeaveCrypto.js", ns); |
|
565 |
|
566 let wc = new ns.WeaveCrypto(); |
|
567 delete Svc.Crypto; |
|
568 return Svc.Crypto = wc; |
|
569 }); |
|
570 |
|
571 Observers.add("xpcom-shutdown", function unloadServices() { |
|
572 Observers.remove("xpcom-shutdown", unloadServices); |
|
573 |
|
574 for (let k in Svc) { |
|
575 delete Svc[k]; |
|
576 } |
|
577 }); |