1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/services/crypto/modules/utils.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,577 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components; 1.9 + 1.10 +this.EXPORTED_SYMBOLS = ["CryptoUtils"]; 1.11 + 1.12 +Cu.import("resource://services-common/observers.js"); 1.13 +Cu.import("resource://services-common/utils.js"); 1.14 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.15 + 1.16 +this.CryptoUtils = { 1.17 + xor: function xor(a, b) { 1.18 + let bytes = []; 1.19 + 1.20 + if (a.length != b.length) { 1.21 + throw new Error("can't xor unequal length strings: "+a.length+" vs "+b.length); 1.22 + } 1.23 + 1.24 + for (let i = 0; i < a.length; i++) { 1.25 + bytes[i] = a.charCodeAt(i) ^ b.charCodeAt(i); 1.26 + } 1.27 + 1.28 + return String.fromCharCode.apply(String, bytes); 1.29 + }, 1.30 + 1.31 + /** 1.32 + * Generate a string of random bytes. 1.33 + */ 1.34 + generateRandomBytes: function generateRandomBytes(length) { 1.35 + let rng = Cc["@mozilla.org/security/random-generator;1"] 1.36 + .createInstance(Ci.nsIRandomGenerator); 1.37 + let bytes = rng.generateRandomBytes(length); 1.38 + return CommonUtils.byteArrayToString(bytes); 1.39 + }, 1.40 + 1.41 + /** 1.42 + * UTF8-encode a message and hash it with the given hasher. Returns a 1.43 + * string containing bytes. The hasher is reset if it's an HMAC hasher. 1.44 + */ 1.45 + digestUTF8: function digestUTF8(message, hasher) { 1.46 + let data = this._utf8Converter.convertToByteArray(message, {}); 1.47 + hasher.update(data, data.length); 1.48 + let result = hasher.finish(false); 1.49 + if (hasher instanceof Ci.nsICryptoHMAC) { 1.50 + hasher.reset(); 1.51 + } 1.52 + return result; 1.53 + }, 1.54 + 1.55 + /** 1.56 + * Treat the given message as a bytes string and hash it with the given 1.57 + * hasher. Returns a string containing bytes. The hasher is reset if it's 1.58 + * an HMAC hasher. 1.59 + */ 1.60 + digestBytes: function digestBytes(message, hasher) { 1.61 + // No UTF-8 encoding for you, sunshine. 1.62 + let bytes = [b.charCodeAt() for each (b in message)]; 1.63 + hasher.update(bytes, bytes.length); 1.64 + let result = hasher.finish(false); 1.65 + if (hasher instanceof Ci.nsICryptoHMAC) { 1.66 + hasher.reset(); 1.67 + } 1.68 + return result; 1.69 + }, 1.70 + 1.71 + /** 1.72 + * Encode the message into UTF-8 and feed the resulting bytes into the 1.73 + * given hasher. Does not return a hash. This can be called multiple times 1.74 + * with a single hasher, but eventually you must extract the result 1.75 + * yourself. 1.76 + */ 1.77 + updateUTF8: function(message, hasher) { 1.78 + let bytes = this._utf8Converter.convertToByteArray(message, {}); 1.79 + hasher.update(bytes, bytes.length); 1.80 + }, 1.81 + 1.82 + /** 1.83 + * UTF-8 encode a message and perform a SHA-1 over it. 1.84 + * 1.85 + * @param message 1.86 + * (string) Buffer to perform operation on. Should be a JS string. 1.87 + * It is possible to pass in a string representing an array 1.88 + * of bytes. But, you probably don't want to UTF-8 encode 1.89 + * such data and thus should not be using this function. 1.90 + * 1.91 + * @return string 1.92 + * Raw bytes constituting SHA-1 hash. Value is a JS string. Each 1.93 + * character is the byte value for that offset. Returned string 1.94 + * always has .length == 20. 1.95 + */ 1.96 + UTF8AndSHA1: function UTF8AndSHA1(message) { 1.97 + let hasher = Cc["@mozilla.org/security/hash;1"] 1.98 + .createInstance(Ci.nsICryptoHash); 1.99 + hasher.init(hasher.SHA1); 1.100 + 1.101 + return CryptoUtils.digestUTF8(message, hasher); 1.102 + }, 1.103 + 1.104 + sha1: function sha1(message) { 1.105 + return CommonUtils.bytesAsHex(CryptoUtils.UTF8AndSHA1(message)); 1.106 + }, 1.107 + 1.108 + sha1Base32: function sha1Base32(message) { 1.109 + return CommonUtils.encodeBase32(CryptoUtils.UTF8AndSHA1(message)); 1.110 + }, 1.111 + 1.112 + /** 1.113 + * Produce an HMAC key object from a key string. 1.114 + */ 1.115 + makeHMACKey: function makeHMACKey(str) { 1.116 + return Svc.KeyFactory.keyFromString(Ci.nsIKeyObject.HMAC, str); 1.117 + }, 1.118 + 1.119 + /** 1.120 + * Produce an HMAC hasher and initialize it with the given HMAC key. 1.121 + */ 1.122 + makeHMACHasher: function makeHMACHasher(type, key) { 1.123 + let hasher = Cc["@mozilla.org/security/hmac;1"] 1.124 + .createInstance(Ci.nsICryptoHMAC); 1.125 + hasher.init(type, key); 1.126 + return hasher; 1.127 + }, 1.128 + 1.129 + /** 1.130 + * HMAC-based Key Derivation (RFC 5869). 1.131 + */ 1.132 + hkdf: function hkdf(ikm, xts, info, len) { 1.133 + const BLOCKSIZE = 256 / 8; 1.134 + if (typeof xts === undefined) 1.135 + xts = String.fromCharCode(0, 0, 0, 0, 0, 0, 0, 0, 1.136 + 0, 0, 0, 0, 0, 0, 0, 0, 1.137 + 0, 0, 0, 0, 0, 0, 0, 0, 1.138 + 0, 0, 0, 0, 0, 0, 0, 0); 1.139 + let h = CryptoUtils.makeHMACHasher(Ci.nsICryptoHMAC.SHA256, 1.140 + CryptoUtils.makeHMACKey(xts)); 1.141 + let prk = CryptoUtils.digestBytes(ikm, h); 1.142 + return CryptoUtils.hkdfExpand(prk, info, len); 1.143 + }, 1.144 + 1.145 + /** 1.146 + * HMAC-based Key Derivation Step 2 according to RFC 5869. 1.147 + */ 1.148 + hkdfExpand: function hkdfExpand(prk, info, len) { 1.149 + const BLOCKSIZE = 256 / 8; 1.150 + let h = CryptoUtils.makeHMACHasher(Ci.nsICryptoHMAC.SHA256, 1.151 + CryptoUtils.makeHMACKey(prk)); 1.152 + let T = ""; 1.153 + let Tn = ""; 1.154 + let iterations = Math.ceil(len/BLOCKSIZE); 1.155 + for (let i = 0; i < iterations; i++) { 1.156 + Tn = CryptoUtils.digestBytes(Tn + info + String.fromCharCode(i + 1), h); 1.157 + T += Tn; 1.158 + } 1.159 + return T.slice(0, len); 1.160 + }, 1.161 + 1.162 + /** 1.163 + * PBKDF2 implementation in Javascript. 1.164 + * 1.165 + * The arguments to this function correspond to items in 1.166 + * PKCS #5, v2.0 pp. 9-10 1.167 + * 1.168 + * P: the passphrase, an octet string: e.g., "secret phrase" 1.169 + * S: the salt, an octet string: e.g., "DNXPzPpiwn" 1.170 + * c: the number of iterations, a positive integer: e.g., 4096 1.171 + * dkLen: the length in octets of the destination 1.172 + * key, a positive integer: e.g., 16 1.173 + * hmacAlg: The algorithm to use for hmac 1.174 + * hmacLen: The hmac length 1.175 + * 1.176 + * The default value of 20 for hmacLen is appropriate for SHA1. For SHA256, 1.177 + * hmacLen should be 32. 1.178 + * 1.179 + * The output is an octet string of length dkLen, which you 1.180 + * can encode as you wish. 1.181 + */ 1.182 + pbkdf2Generate : function pbkdf2Generate(P, S, c, dkLen, 1.183 + hmacAlg=Ci.nsICryptoHMAC.SHA1, hmacLen=20) { 1.184 + 1.185 + // We don't have a default in the algo itself, as NSS does. 1.186 + // Use the constant. 1.187 + if (!dkLen) { 1.188 + dkLen = SYNC_KEY_DECODED_LENGTH; 1.189 + } 1.190 + 1.191 + function F(S, c, i, h) { 1.192 + 1.193 + function XOR(a, b, isA) { 1.194 + if (a.length != b.length) { 1.195 + return false; 1.196 + } 1.197 + 1.198 + let val = []; 1.199 + for (let i = 0; i < a.length; i++) { 1.200 + if (isA) { 1.201 + val[i] = a[i] ^ b[i]; 1.202 + } else { 1.203 + val[i] = a.charCodeAt(i) ^ b.charCodeAt(i); 1.204 + } 1.205 + } 1.206 + 1.207 + return val; 1.208 + } 1.209 + 1.210 + let ret; 1.211 + let U = []; 1.212 + 1.213 + /* Encode i into 4 octets: _INT */ 1.214 + let I = []; 1.215 + I[0] = String.fromCharCode((i >> 24) & 0xff); 1.216 + I[1] = String.fromCharCode((i >> 16) & 0xff); 1.217 + I[2] = String.fromCharCode((i >> 8) & 0xff); 1.218 + I[3] = String.fromCharCode(i & 0xff); 1.219 + 1.220 + U[0] = CryptoUtils.digestBytes(S + I.join(''), h); 1.221 + for (let j = 1; j < c; j++) { 1.222 + U[j] = CryptoUtils.digestBytes(U[j - 1], h); 1.223 + } 1.224 + 1.225 + ret = U[0]; 1.226 + for (let j = 1; j < c; j++) { 1.227 + ret = CommonUtils.byteArrayToString(XOR(ret, U[j])); 1.228 + } 1.229 + 1.230 + return ret; 1.231 + } 1.232 + 1.233 + let l = Math.ceil(dkLen / hmacLen); 1.234 + let r = dkLen - ((l - 1) * hmacLen); 1.235 + 1.236 + // Reuse the key and the hasher. Remaking them 4096 times is 'spensive. 1.237 + let h = CryptoUtils.makeHMACHasher(hmacAlg, 1.238 + CryptoUtils.makeHMACKey(P)); 1.239 + 1.240 + let T = []; 1.241 + for (let i = 0; i < l;) { 1.242 + T[i] = F(S, c, ++i, h); 1.243 + } 1.244 + 1.245 + let ret = ""; 1.246 + for (let i = 0; i < l-1;) { 1.247 + ret += T[i++]; 1.248 + } 1.249 + ret += T[l - 1].substr(0, r); 1.250 + 1.251 + return ret; 1.252 + }, 1.253 + 1.254 + deriveKeyFromPassphrase: function deriveKeyFromPassphrase(passphrase, 1.255 + salt, 1.256 + keyLength, 1.257 + forceJS) { 1.258 + if (Svc.Crypto.deriveKeyFromPassphrase && !forceJS) { 1.259 + return Svc.Crypto.deriveKeyFromPassphrase(passphrase, salt, keyLength); 1.260 + } 1.261 + else { 1.262 + // Fall back to JS implementation. 1.263 + // 4096 is hardcoded in WeaveCrypto, so do so here. 1.264 + return CryptoUtils.pbkdf2Generate(passphrase, atob(salt), 4096, 1.265 + keyLength); 1.266 + } 1.267 + }, 1.268 + 1.269 + /** 1.270 + * Compute the HTTP MAC SHA-1 for an HTTP request. 1.271 + * 1.272 + * @param identifier 1.273 + * (string) MAC Key Identifier. 1.274 + * @param key 1.275 + * (string) MAC Key. 1.276 + * @param method 1.277 + * (string) HTTP request method. 1.278 + * @param URI 1.279 + * (nsIURI) HTTP request URI. 1.280 + * @param extra 1.281 + * (object) Optional extra parameters. Valid keys are: 1.282 + * nonce_bytes - How many bytes the nonce should be. This defaults 1.283 + * to 8. Note that this many bytes are Base64 encoded, so the 1.284 + * string length of the nonce will be longer than this value. 1.285 + * ts - Timestamp to use. Should only be defined for testing. 1.286 + * nonce - String nonce. Should only be defined for testing as this 1.287 + * function will generate a cryptographically secure random one 1.288 + * if not defined. 1.289 + * ext - Extra string to be included in MAC. Per the HTTP MAC spec, 1.290 + * the format is undefined and thus application specific. 1.291 + * @returns 1.292 + * (object) Contains results of operation and input arguments (for 1.293 + * symmetry). The object has the following keys: 1.294 + * 1.295 + * identifier - (string) MAC Key Identifier (from arguments). 1.296 + * key - (string) MAC Key (from arguments). 1.297 + * method - (string) HTTP request method (from arguments). 1.298 + * hostname - (string) HTTP hostname used (derived from arguments). 1.299 + * port - (string) HTTP port number used (derived from arguments). 1.300 + * mac - (string) Raw HMAC digest bytes. 1.301 + * getHeader - (function) Call to obtain the string Authorization 1.302 + * header value for this invocation. 1.303 + * nonce - (string) Nonce value used. 1.304 + * ts - (number) Integer seconds since Unix epoch that was used. 1.305 + */ 1.306 + computeHTTPMACSHA1: function computeHTTPMACSHA1(identifier, key, method, 1.307 + uri, extra) { 1.308 + let ts = (extra && extra.ts) ? extra.ts : Math.floor(Date.now() / 1000); 1.309 + let nonce_bytes = (extra && extra.nonce_bytes > 0) ? extra.nonce_bytes : 8; 1.310 + 1.311 + // We are allowed to use more than the Base64 alphabet if we want. 1.312 + let nonce = (extra && extra.nonce) 1.313 + ? extra.nonce 1.314 + : btoa(CryptoUtils.generateRandomBytes(nonce_bytes)); 1.315 + 1.316 + let host = uri.asciiHost; 1.317 + let port; 1.318 + let usedMethod = method.toUpperCase(); 1.319 + 1.320 + if (uri.port != -1) { 1.321 + port = uri.port; 1.322 + } else if (uri.scheme == "http") { 1.323 + port = "80"; 1.324 + } else if (uri.scheme == "https") { 1.325 + port = "443"; 1.326 + } else { 1.327 + throw new Error("Unsupported URI scheme: " + uri.scheme); 1.328 + } 1.329 + 1.330 + let ext = (extra && extra.ext) ? extra.ext : ""; 1.331 + 1.332 + let requestString = ts.toString(10) + "\n" + 1.333 + nonce + "\n" + 1.334 + usedMethod + "\n" + 1.335 + uri.path + "\n" + 1.336 + host + "\n" + 1.337 + port + "\n" + 1.338 + ext + "\n"; 1.339 + 1.340 + let hasher = CryptoUtils.makeHMACHasher(Ci.nsICryptoHMAC.SHA1, 1.341 + CryptoUtils.makeHMACKey(key)); 1.342 + let mac = CryptoUtils.digestBytes(requestString, hasher); 1.343 + 1.344 + function getHeader() { 1.345 + return CryptoUtils.getHTTPMACSHA1Header(this.identifier, this.ts, 1.346 + this.nonce, this.mac, this.ext); 1.347 + } 1.348 + 1.349 + return { 1.350 + identifier: identifier, 1.351 + key: key, 1.352 + method: usedMethod, 1.353 + hostname: host, 1.354 + port: port, 1.355 + mac: mac, 1.356 + nonce: nonce, 1.357 + ts: ts, 1.358 + ext: ext, 1.359 + getHeader: getHeader 1.360 + }; 1.361 + }, 1.362 + 1.363 + 1.364 + /** 1.365 + * Obtain the HTTP MAC Authorization header value from fields. 1.366 + * 1.367 + * @param identifier 1.368 + * (string) MAC key identifier. 1.369 + * @param ts 1.370 + * (number) Integer seconds since Unix epoch. 1.371 + * @param nonce 1.372 + * (string) Nonce value. 1.373 + * @param mac 1.374 + * (string) Computed HMAC digest (raw bytes). 1.375 + * @param ext 1.376 + * (optional) (string) Extra string content. 1.377 + * @returns 1.378 + * (string) Value to put in Authorization header. 1.379 + */ 1.380 + getHTTPMACSHA1Header: function getHTTPMACSHA1Header(identifier, ts, nonce, 1.381 + mac, ext) { 1.382 + let header ='MAC id="' + identifier + '", ' + 1.383 + 'ts="' + ts + '", ' + 1.384 + 'nonce="' + nonce + '", ' + 1.385 + 'mac="' + btoa(mac) + '"'; 1.386 + 1.387 + if (!ext) { 1.388 + return header; 1.389 + } 1.390 + 1.391 + return header += ', ext="' + ext +'"'; 1.392 + }, 1.393 + 1.394 + /** 1.395 + * Given an HTTP header value, strip out any attributes. 1.396 + */ 1.397 + 1.398 + stripHeaderAttributes: function(value) { 1.399 + let value = value || ""; 1.400 + let i = value.indexOf(";"); 1.401 + return value.substring(0, (i >= 0) ? i : undefined).trim().toLowerCase(); 1.402 + }, 1.403 + 1.404 + /** 1.405 + * Compute the HAWK client values (mostly the header) for an HTTP request. 1.406 + * 1.407 + * @param URI 1.408 + * (nsIURI) HTTP request URI. 1.409 + * @param method 1.410 + * (string) HTTP request method. 1.411 + * @param options 1.412 + * (object) extra parameters (all but "credentials" are optional): 1.413 + * credentials - (object, mandatory) HAWK credentials object. 1.414 + * All three keys are required: 1.415 + * id - (string) key identifier 1.416 + * key - (string) raw key bytes 1.417 + * algorithm - (string) which hash to use: "sha1" or "sha256" 1.418 + * ext - (string) application-specific data, included in MAC 1.419 + * localtimeOffsetMsec - (number) local clock offset (vs server) 1.420 + * payload - (string) payload to include in hash, containing the 1.421 + * HTTP request body. If not provided, the HAWK hash 1.422 + * will not cover the request body, and the server 1.423 + * should not check it either. This will be UTF-8 1.424 + * encoded into bytes before hashing. This function 1.425 + * cannot handle arbitrary binary data, sorry (the 1.426 + * UTF-8 encoding process will corrupt any codepoints 1.427 + * between U+0080 and U+00FF). Callers must be careful 1.428 + * to use an HTTP client function which encodes the 1.429 + * payload exactly the same way, otherwise the hash 1.430 + * will not match. 1.431 + * contentType - (string) payload Content-Type. This is included 1.432 + * (without any attributes like "charset=") in the 1.433 + * HAWK hash. It does *not* affect interpretation 1.434 + * of the "payload" property. 1.435 + * hash - (base64 string) pre-calculated payload hash. If 1.436 + * provided, "payload" is ignored. 1.437 + * ts - (number) pre-calculated timestamp, secs since epoch 1.438 + * now - (number) current time, ms-since-epoch, for tests 1.439 + * nonce - (string) pre-calculated nonce. Should only be defined 1.440 + * for testing as this function will generate a 1.441 + * cryptographically secure random one if not defined. 1.442 + * @returns 1.443 + * (object) Contains results of operation. The object has the 1.444 + * following keys: 1.445 + * field - (string) HAWK header, to use in Authorization: header 1.446 + * artifacts - (object) other generated values: 1.447 + * ts - (number) timestamp, in seconds since epoch 1.448 + * nonce - (string) 1.449 + * method - (string) 1.450 + * resource - (string) path plus querystring 1.451 + * host - (string) 1.452 + * port - (number) 1.453 + * hash - (string) payload hash (base64) 1.454 + * ext - (string) app-specific data 1.455 + * MAC - (string) request MAC (base64) 1.456 + */ 1.457 + computeHAWK: function(uri, method, options) { 1.458 + let credentials = options.credentials; 1.459 + let ts = options.ts || Math.floor(((options.now || Date.now()) + 1.460 + (options.localtimeOffsetMsec || 0)) 1.461 + / 1000); 1.462 + 1.463 + let hash_algo, hmac_algo; 1.464 + if (credentials.algorithm == "sha1") { 1.465 + hash_algo = Ci.nsICryptoHash.SHA1; 1.466 + hmac_algo = Ci.nsICryptoHMAC.SHA1; 1.467 + } else if (credentials.algorithm == "sha256") { 1.468 + hash_algo = Ci.nsICryptoHash.SHA256; 1.469 + hmac_algo = Ci.nsICryptoHMAC.SHA256; 1.470 + } else { 1.471 + throw new Error("Unsupported algorithm: " + credentials.algorithm); 1.472 + } 1.473 + 1.474 + let port; 1.475 + if (uri.port != -1) { 1.476 + port = uri.port; 1.477 + } else if (uri.scheme == "http") { 1.478 + port = 80; 1.479 + } else if (uri.scheme == "https") { 1.480 + port = 443; 1.481 + } else { 1.482 + throw new Error("Unsupported URI scheme: " + uri.scheme); 1.483 + } 1.484 + 1.485 + let artifacts = { 1.486 + ts: ts, 1.487 + nonce: options.nonce || btoa(CryptoUtils.generateRandomBytes(8)), 1.488 + method: method.toUpperCase(), 1.489 + resource: uri.path, // This includes both path and search/queryarg. 1.490 + host: uri.asciiHost.toLowerCase(), // This includes punycoding. 1.491 + port: port.toString(10), 1.492 + hash: options.hash, 1.493 + ext: options.ext, 1.494 + }; 1.495 + 1.496 + let contentType = CryptoUtils.stripHeaderAttributes(options.contentType); 1.497 + 1.498 + if (!artifacts.hash && options.hasOwnProperty("payload") 1.499 + && options.payload) { 1.500 + let hasher = Cc["@mozilla.org/security/hash;1"] 1.501 + .createInstance(Ci.nsICryptoHash); 1.502 + hasher.init(hash_algo); 1.503 + CryptoUtils.updateUTF8("hawk.1.payload\n", hasher); 1.504 + CryptoUtils.updateUTF8(contentType+"\n", hasher); 1.505 + CryptoUtils.updateUTF8(options.payload, hasher); 1.506 + CryptoUtils.updateUTF8("\n", hasher); 1.507 + let hash = hasher.finish(false); 1.508 + // HAWK specifies this .hash to use +/ (not _-) and include the 1.509 + // trailing "==" padding. 1.510 + let hash_b64 = btoa(hash); 1.511 + artifacts.hash = hash_b64; 1.512 + } 1.513 + 1.514 + let requestString = ("hawk.1.header" + "\n" + 1.515 + artifacts.ts.toString(10) + "\n" + 1.516 + artifacts.nonce + "\n" + 1.517 + artifacts.method + "\n" + 1.518 + artifacts.resource + "\n" + 1.519 + artifacts.host + "\n" + 1.520 + artifacts.port + "\n" + 1.521 + (artifacts.hash || "") + "\n"); 1.522 + if (artifacts.ext) { 1.523 + requestString += artifacts.ext.replace("\\", "\\\\").replace("\n", "\\n"); 1.524 + } 1.525 + requestString += "\n"; 1.526 + 1.527 + let hasher = CryptoUtils.makeHMACHasher(hmac_algo, 1.528 + CryptoUtils.makeHMACKey(credentials.key)); 1.529 + artifacts.mac = btoa(CryptoUtils.digestBytes(requestString, hasher)); 1.530 + // The output MAC uses "+" and "/", and padded== . 1.531 + 1.532 + function escape(attribute) { 1.533 + // This is used for "x=y" attributes inside HTTP headers. 1.534 + return attribute.replace(/\\/g, "\\\\").replace(/\"/g, '\\"'); 1.535 + } 1.536 + let header = ('Hawk id="' + credentials.id + '", ' + 1.537 + 'ts="' + artifacts.ts + '", ' + 1.538 + 'nonce="' + artifacts.nonce + '", ' + 1.539 + (artifacts.hash ? ('hash="' + artifacts.hash + '", ') : "") + 1.540 + (artifacts.ext ? ('ext="' + escape(artifacts.ext) + '", ') : "") + 1.541 + 'mac="' + artifacts.mac + '"'); 1.542 + return { 1.543 + artifacts: artifacts, 1.544 + field: header, 1.545 + }; 1.546 + }, 1.547 + 1.548 +}; 1.549 + 1.550 +XPCOMUtils.defineLazyGetter(CryptoUtils, "_utf8Converter", function() { 1.551 + let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"] 1.552 + .createInstance(Ci.nsIScriptableUnicodeConverter); 1.553 + converter.charset = "UTF-8"; 1.554 + 1.555 + return converter; 1.556 +}); 1.557 + 1.558 +let Svc = {}; 1.559 + 1.560 +XPCOMUtils.defineLazyServiceGetter(Svc, 1.561 + "KeyFactory", 1.562 + "@mozilla.org/security/keyobjectfactory;1", 1.563 + "nsIKeyObjectFactory"); 1.564 + 1.565 +Svc.__defineGetter__("Crypto", function() { 1.566 + let ns = {}; 1.567 + Cu.import("resource://services-crypto/WeaveCrypto.js", ns); 1.568 + 1.569 + let wc = new ns.WeaveCrypto(); 1.570 + delete Svc.Crypto; 1.571 + return Svc.Crypto = wc; 1.572 +}); 1.573 + 1.574 +Observers.add("xpcom-shutdown", function unloadServices() { 1.575 + Observers.remove("xpcom-shutdown", unloadServices); 1.576 + 1.577 + for (let k in Svc) { 1.578 + delete Svc[k]; 1.579 + } 1.580 +});