Bug 1368560 part 2 - Move Svc.Crypto to Weave.Crypto. r?markh draft
authorEdouard Oger <eoger@fastmail.com>
Mon, 29 May 2017 13:24:01 -0400
changeset 587953 4623bc1e6cb2b680ac146b5c93a0b5d0b6a591e5
parent 587167 30e71e2eb7be081d68a62f371efa5e4ad5ef59dd
child 631417 d55745227d93b3901326f5530848dd54d8103052
push id61863
push userbmo:eoger@fastmail.com
push dateThu, 01 Jun 2017 23:11:10 +0000
reviewersmarkh
bugs1368560
milestone55.0a1
Bug 1368560 part 2 - Move Svc.Crypto to Weave.Crypto. r?markh MozReview-Commit-ID: 74IFsVjZSgz
services/sync/modules-testing/fakeservices.js
services/sync/modules/keys.js
services/sync/modules/main.js
services/sync/modules/record.js
services/sync/modules/service.js
services/sync/modules/util.js
services/sync/tests/unit/test_bookmark_order.js
services/sync/tests/unit/test_corrupt_keys.js
services/sync/tests/unit/test_keys.js
services/sync/tests/unit/test_syncengine_sync.js
toolkit/components/extensions/ExtensionStorageSync.jsm
--- a/services/sync/modules-testing/fakeservices.js
+++ b/services/sync/modules-testing/fakeservices.js
@@ -8,16 +8,17 @@ this.EXPORTED_SYMBOLS = [
   "FakeCryptoService",
   "FakeFilesystemService",
   "FakeGUIDService",
   "fakeSHA256HMAC",
 ];
 
 var {utils: Cu} = Components;
 
+Cu.import("resource://services-sync/main.js");
 Cu.import("resource://services-sync/record.js");
 Cu.import("resource://services-sync/util.js");
 
 var btoa = Cu.import("resource://gre/modules/Log.jsm").btoa;
 
 this.FakeFilesystemService = function FakeFilesystemService(contents) {
   this.fakeContents = contents;
   let self = this;
@@ -90,18 +91,18 @@ this.FakeGUIDService = function FakeGUID
 
 /*
  * Mock implementation of WeaveCrypto. It does not encrypt or
  * decrypt, merely returning the input verbatim.
  */
 this.FakeCryptoService = function FakeCryptoService() {
   this.counter = 0;
 
-  delete Svc.Crypto;  // get rid of the getter first
-  Svc.Crypto = this;
+  delete Weave.Crypto;  // get rid of the getter first
+  Weave.Crypto = this;
 
   CryptoWrapper.prototype.ciphertextHMAC = function ciphertextHMAC(keyBundle) {
     return fakeSHA256HMAC(this.ciphertext);
   };
 }
 FakeCryptoService.prototype = {
 
   encrypt: function encrypt(clearText, symmetricKey, iv) {
--- a/services/sync/modules/keys.js
+++ b/services/sync/modules/keys.js
@@ -8,16 +8,17 @@ this.EXPORTED_SYMBOLS = [
   "BulkKeyBundle",
   "SyncKeyBundle"
 ];
 
 var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://gre/modules/Log.jsm");
+Cu.import("resource://services-sync/main.js");
 Cu.import("resource://services-sync/util.js");
 
 /**
  * Represents a pair of keys.
  *
  * Each key stored in a key bundle is 256 bits. One key is used for symmetric
  * encryption. The other is used for HMAC.
  *
@@ -102,18 +103,18 @@ KeyBundle.prototype = {
   get sha256HMACHasher() {
     return this._sha256HMACHasher;
   },
 
   /**
    * Populate this key pair with 2 new, randomly generated keys.
    */
   generateRandom: function generateRandom() {
-    let generatedHMAC = Svc.Crypto.generateRandomKey();
-    let generatedEncr = Svc.Crypto.generateRandomKey();
+    let generatedHMAC = Weave.Crypto.generateRandomKey();
+    let generatedEncr = Weave.Crypto.generateRandomKey();
     this.keyPairB64 = [generatedEncr, generatedHMAC];
   },
 
 };
 
 /**
  * Represents a KeyBundle associated with a collection.
  *
--- a/services/sync/modules/main.js
+++ b/services/sync/modules/main.js
@@ -1,29 +1,38 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 this.EXPORTED_SYMBOLS = ["Weave"];
 
+const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
 this.Weave = {};
-Components.utils.import("resource://services-sync/constants.js", Weave);
+Cu.import("resource://services-sync/constants.js", Weave);
 var lazies = {
   "service.js":           ["Service"],
   "status.js":            ["Status"],
   "util.js":              ["Utils", "Svc"]
 };
 
 function lazyImport(module, dest, props) {
   function getter(prop) {
     return function() {
       let ns = {};
-      Components.utils.import(module, ns);
+      Cu.import(module, ns);
       delete dest[prop];
       return dest[prop] = ns[prop];
     };
   }
   props.forEach(function(prop) { dest.__defineGetter__(prop, getter(prop)); });
 }
 
 for (let mod in lazies) {
   lazyImport("resource://services-sync/" + mod, Weave, lazies[mod]);
 }
+
+XPCOMUtils.defineLazyGetter(Weave, "Crypto", function() {
+  let { WeaveCrypto } = Cu.import("resource://services-crypto/WeaveCrypto.js", {});
+  return new WeaveCrypto();
+});
--- a/services/sync/modules/record.js
+++ b/services/sync/modules/record.js
@@ -16,16 +16,17 @@ var Cr = Components.results;
 var Cu = Components.utils;
 
 const CRYPTO_COLLECTION = "crypto";
 const KEYS_WBO = "keys";
 
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/keys.js");
+Cu.import("resource://services-sync/main.js");
 Cu.import("resource://services-sync/resource.js");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://services-common/async.js");
 
 this.WBORecord = function WBORecord(collection, id) {
   this.data = {};
   this.payload = {};
   this.collection = collection;      // Optional.
@@ -134,19 +135,19 @@ CryptoWrapper.prototype = {
    *
    * Optional key bundle overrides the collection key lookup.
    */
   encrypt: function encrypt(keyBundle) {
     if (!keyBundle) {
       throw new Error("A key bundle must be supplied to encrypt.");
     }
 
-    this.IV = Svc.Crypto.generateRandomIV();
-    this.ciphertext = Svc.Crypto.encrypt(JSON.stringify(this.cleartext),
-                                         keyBundle.encryptionKeyB64, this.IV);
+    this.IV = Weave.Crypto.generateRandomIV();
+    this.ciphertext = Weave.Crypto.encrypt(JSON.stringify(this.cleartext),
+                                           keyBundle.encryptionKeyB64, this.IV);
     this.hmac = this.ciphertextHMAC(keyBundle);
     this.cleartext = null;
   },
 
   // Optional key bundle.
   decrypt: function decrypt(keyBundle) {
     if (!this.ciphertext) {
       throw "No ciphertext: nothing to decrypt?";
@@ -159,18 +160,18 @@ CryptoWrapper.prototype = {
     // Authenticate the encrypted blob with the expected HMAC
     let computedHMAC = this.ciphertextHMAC(keyBundle);
 
     if (computedHMAC != this.hmac) {
       Utils.throwHMACMismatch(this.hmac, computedHMAC);
     }
 
     // Handle invalid data here. Elsewhere we assume that cleartext is an object.
-    let cleartext = Svc.Crypto.decrypt(this.ciphertext,
-                                       keyBundle.encryptionKeyB64, this.IV);
+    let cleartext = Weave.Crypto.decrypt(this.ciphertext,
+                                         keyBundle.encryptionKeyB64, this.IV);
     let json_result = JSON.parse(cleartext);
 
     if (json_result && (json_result instanceof Object)) {
       this.cleartext = json_result;
       this.ciphertext = null;
     } else {
       throw "Decryption failed: result is <" + json_result + ">, not an object.";
     }
--- a/services/sync/modules/service.js
+++ b/services/sync/modules/service.js
@@ -20,16 +20,17 @@ const KEYS_WBO = "keys";
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://services-common/async.js");
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/engines/clients.js");
+Cu.import("resource://services-sync/main.js");
 Cu.import("resource://services-sync/policies.js");
 Cu.import("resource://services-sync/record.js");
 Cu.import("resource://services-sync/resource.js");
 Cu.import("resource://services-sync/rest.js");
 Cu.import("resource://services-sync/stages/enginesync.js");
 Cu.import("resource://services-sync/stages/declined.js");
 Cu.import("resource://services-sync/status.js");
 Cu.import("resource://services-sync/telemetry.js");
@@ -148,17 +149,17 @@ Sync11Service.prototype = {
     this.metaURL = this.storageURL + "meta/global";
     this.cryptoKeysURL = this.storageURL + CRYPTO_COLLECTION + "/" + KEYS_WBO;
   },
 
   _checkCrypto: function _checkCrypto() {
     let ok = false;
 
     try {
-      let iv = Svc.Crypto.generateRandomIV();
+      let iv = Weave.Crypto.generateRandomIV();
       if (iv.length == 24)
         ok = true;
 
     } catch (e) {
       this._log.debug("Crypto check failed: " + e);
     }
 
     return ok;
--- a/services/sync/modules/util.js
+++ b/services/sync/modules/util.js
@@ -636,21 +636,12 @@ XPCOMUtils.defineLazyGetter(Utils, "_utf
 
 /*
  * Commonly-used services
  */
 this.Svc = {};
 Svc.Prefs = new Preferences(PREFS_BRANCH);
 Svc.Obs = Observers;
 
-Svc.__defineGetter__("Crypto", function() {
-  let cryptoSvc;
-  let ns = {};
-  Cu.import("resource://services-crypto/WeaveCrypto.js", ns);
-  cryptoSvc = new ns.WeaveCrypto();
-  delete Svc.Crypto;
-  return Svc.Crypto = cryptoSvc;
-});
-
 Svc.Obs.add("xpcom-shutdown", function() {
   for (let name in Svc)
     delete Svc[name];
 });
--- a/services/sync/tests/unit/test_bookmark_order.js
+++ b/services/sync/tests/unit/test_bookmark_order.js
@@ -1,14 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 _("Making sure after processing incoming bookmarks, they show up in the right order");
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://services-sync/engines/bookmarks.js");
+Cu.import("resource://services-sync/main.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://testing-common/services/sync/utils.js");
 
 function run_test() {
   Svc.Prefs.set("log.logger.engine.bookmarks", "Trace");
   initTestLogging("Trace");
   Log.repository.getLogger("Sqlite").level = Log.Level.Info;
@@ -38,18 +39,18 @@ function serverForFoo(engine) {
       },
     },
     crypto: {
       keys: encryptPayload({
         id: "keys",
         // Generate a fake default key bundle to avoid resetting the client
         // before the first sync.
         default: [
-          Svc.Crypto.generateRandomKey(),
-          Svc.Crypto.generateRandomKey(),
+          Weave.Crypto.generateRandomKey(),
+          Weave.Crypto.generateRandomKey(),
         ],
       }),
     },
     [engine.name]: {},
   });
 }
 
 async function resolveConflict(engine, collection, timestamp, buildTree,
--- a/services/sync/tests/unit/test_corrupt_keys.js
+++ b/services/sync/tests/unit/test_corrupt_keys.js
@@ -1,14 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/engines.js");
+Cu.import("resource://services-sync/main.js");
 Cu.import("resource://services-sync/engines/tabs.js");
 Cu.import("resource://services-sync/engines/history.js");
 Cu.import("resource://services-sync/record.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/status.js");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://testing-common/services/sync/utils.js");
 
@@ -40,18 +41,18 @@ add_task(async function test_locally_cha
     await configureIdentity({ username: "johndoe" }, server);
     // We aren't doing a .login yet, so fudge the cluster URL.
     Service.clusterURL = Service.identity._token.endpoint;
 
     Service.engineManager.register(HistoryEngine);
     Service.engineManager.unregister("addons");
 
     function corrupt_local_keys() {
-      Service.collectionKeys._default.keyPair = [Svc.Crypto.generateRandomKey(),
-                                                 Svc.Crypto.generateRandomKey()];
+      Service.collectionKeys._default.keyPair = [Weave.Crypto.generateRandomKey(),
+                                                 Weave.Crypto.generateRandomKey()];
     }
 
     _("Setting meta.");
 
     // Bump version on the server.
     let m = new WBORecord("meta", "global");
     m.payload = {"syncID": "foooooooooooooooooooooooooo",
                  "storageVersion": STORAGE_VERSION};
--- a/services/sync/tests/unit/test_keys.js
+++ b/services/sync/tests/unit/test_keys.js
@@ -1,13 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/keys.js");
+Cu.import("resource://services-sync/main.js");
 Cu.import("resource://services-sync/record.js");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://services-sync/browserid_identity.js");
 Cu.import("resource://testing-common/services/sync/utils.js");
 
 var collectionKeys = new CollectionKeyManager();
 
 function sha256HMAC(message, key) {
@@ -178,20 +179,20 @@ add_task(async function test_ensureLogge
   /*
    * Build a test version of storage/crypto/keys.
    * Encrypt it with the sync key.
    * Pass it into the CollectionKeyManager.
    */
 
   log.info("Building storage keys...");
   let storage_keys = new CryptoWrapper("crypto", "keys");
-  let default_key64 = Svc.Crypto.generateRandomKey();
-  let default_hmac64 = Svc.Crypto.generateRandomKey();
-  let bookmarks_key64 = Svc.Crypto.generateRandomKey();
-  let bookmarks_hmac64 = Svc.Crypto.generateRandomKey();
+  let default_key64 = Weave.Crypto.generateRandomKey();
+  let default_hmac64 = Weave.Crypto.generateRandomKey();
+  let bookmarks_key64 = Weave.Crypto.generateRandomKey();
+  let bookmarks_hmac64 = Weave.Crypto.generateRandomKey();
 
   storage_keys.cleartext = {
     "default": [default_key64, default_hmac64],
     "collections": {"bookmarks": [bookmarks_key64, bookmarks_hmac64]},
   };
   storage_keys.modified = Date.now() / 1000;
   storage_keys.id = "keys";
 
--- a/services/sync/tests/unit/test_syncengine_sync.js
+++ b/services/sync/tests/unit/test_syncengine_sync.js
@@ -1,13 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/engines.js");
+Cu.import("resource://services-sync/main.js");
 Cu.import("resource://services-sync/policies.js");
 Cu.import("resource://services-sync/record.js");
 Cu.import("resource://services-sync/resource.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://testing-common/services/sync/rotaryengine.js");
 Cu.import("resource://testing-common/services/sync/utils.js");
 
@@ -1082,18 +1083,18 @@ add_task(async function test_processInco
   collection._wbos.nojson2 = new ServerWBO("nojson2", "This is invalid JSON");
   collection._wbos.scotsman = new ServerWBO(
       "scotsman", encryptPayload({id: "scotsman",
                                   denomination: "Flying Scotsman"}));
   collection._wbos.nodecrypt = new ServerWBO("nodecrypt", "Decrypt this!");
   collection._wbos.nodecrypt2 = new ServerWBO("nodecrypt2", "Decrypt this!");
 
   // Patch the fake crypto service to throw on the record above.
-  Svc.Crypto._decrypt = Svc.Crypto.decrypt;
-  Svc.Crypto.decrypt = function(ciphertext) {
+  Weave.Crypto._decrypt = Weave.Crypto.decrypt;
+  Weave.Crypto.decrypt = function(ciphertext) {
     if (ciphertext == "Decrypt this!") {
       throw "Derp! Cipher finalized failed. Im ur crypto destroyin ur recordz.";
     }
     return this._decrypt.apply(this, arguments);
   };
 
   // Some broken records also exist locally.
   let engine = makeRotaryEngine();
--- a/toolkit/components/extensions/ExtensionStorageSync.jsm
+++ b/toolkit/components/extensions/ExtensionStorageSync.jsm
@@ -68,25 +68,28 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "Log",
                                   "resource://gre/modules/Log.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Observers",
                                   "resource://services-common/observers.js");
 XPCOMUtils.defineLazyModuleGetter(this, "Services",
                                   "resource://gre/modules/Services.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Sqlite",
                                   "resource://gre/modules/Sqlite.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Svc",
-                                  "resource://services-sync/util.js");
 XPCOMUtils.defineLazyModuleGetter(this, "Utils",
                                   "resource://services-sync/util.js");
 XPCOMUtils.defineLazyPreferenceGetter(this, "prefPermitsStorageSync",
                                       STORAGE_SYNC_ENABLED_PREF, true);
 XPCOMUtils.defineLazyPreferenceGetter(this, "prefStorageSyncServerURL",
                                       STORAGE_SYNC_SERVER_URL_PREF,
                                       KINTO_DEFAULT_SERVER_URL);
+XPCOMUtils.defineLazyGetter(this, "WeaveCrypto", function() {
+  let {WeaveCrypto} = Cu.import("resource://services-crypto/WeaveCrypto.js", {});
+  return new WeaveCrypto();
+});
+
 const {
   runSafeSyncWithoutClone,
 } = ExtensionUtils;
 
 // Map of Extensions to Set<Contexts> to track contexts that are still
 // "live" and use storage.sync.
 const extensionContexts = new Map();
 // Borrow logger from Sync.
@@ -175,19 +178,19 @@ class EncryptionRemoteTransformer {
     if (record.ciphertext) {
       throw new Error("Attempt to reencrypt??");
     }
     let id = await this.getEncodedRecordId(record);
     if (!id) {
       throw new Error("Record ID is missing or invalid");
     }
 
-    let IV = Svc.Crypto.generateRandomIV();
-    let ciphertext = Svc.Crypto.encrypt(JSON.stringify(record),
-                                        keyBundle.encryptionKeyB64, IV);
+    let IV = WeaveCrypto.generateRandomIV();
+    let ciphertext = WeaveCrypto.encrypt(JSON.stringify(record),
+                                         keyBundle.encryptionKeyB64, IV);
     let hmac = ciphertextHMAC(keyBundle, id, IV, ciphertext);
     const encryptedResult = {ciphertext, IV, hmac, id};
 
     // Copy over the _status field, so that we handle concurrency
     // headers (If-Match, If-None-Match) correctly.
     // DON'T copy over "deleted" status, because then we'd leak
     // plaintext deletes.
     encryptedResult._status = record._status == "deleted" ? "updated" : record._status;
@@ -210,18 +213,18 @@ class EncryptionRemoteTransformer {
     // Authenticate the encrypted blob with the expected HMAC
     let computedHMAC = ciphertextHMAC(keyBundle, record.id, record.IV, record.ciphertext);
 
     if (computedHMAC != record.hmac) {
       Utils.throwHMACMismatch(record.hmac, computedHMAC);
     }
 
     // Handle invalid data here. Elsewhere we assume that cleartext is an object.
-    let cleartext = Svc.Crypto.decrypt(record.ciphertext,
-                                       keyBundle.encryptionKeyB64, record.IV);
+    let cleartext = WeaveCrypto.decrypt(record.ciphertext,
+                                        keyBundle.encryptionKeyB64, record.IV);
     let jsonResult = JSON.parse(cleartext);
     if (!jsonResult || typeof jsonResult !== "object") {
       throw new Error("Decryption failed: result is <" + jsonResult + ">, not an object.");
     }
 
     if (record.hasOwnProperty("last_modified")) {
       jsonResult.last_modified = record.last_modified;
     }