Bug 603388 - Merge fx-sync to mozilla-central. a=blockers
authorPhilipp von Weitershausen <philipp@weitershausen.de>
Fri, 15 Oct 2010 11:46:02 +0200
changeset 55850 7b6f71ee1ab408efb0085704ab80ca9ce1ee527a
parent 55843 e139cc2d6f0eb9b0796638fdfd7f7867a3b99a45 (current diff)
parent 55849 156f6e2674f2a39cda49891079ef7ef38cd170b7 (diff)
child 55851 19cb42fa45543537ed916ab79d326b58e8a8d412
push idunknown
push userunknown
push dateunknown
reviewersblockers
bugs603388
milestone2.0b8pre
Bug 603388 - Merge fx-sync to mozilla-central. a=blockers
services/crypto/WeaveCrypto.js
services/crypto/modules/WeaveCrypto.js
services/sync/Weave.js
services/sync/modules/engines/history.js
services/sync/modules/service.js
services/sync/modules/util.js
services/sync/tests/unit/test_crypto_crypt.js
services/sync/tests/unit/test_crypto_keypair.js
services/sync/tests/unit/test_crypto_random.js
services/sync/tests/unit/test_crypto_rewrap.js
services/sync/tests/unit/test_crypto_verify.js
rename from services/crypto/WeaveCrypto.js
rename to services/crypto/modules/WeaveCrypto.js
--- a/services/crypto/WeaveCrypto.js
+++ b/services/crypto/modules/WeaveCrypto.js
@@ -29,36 +29,31 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+const EXPORTED_SYMBOLS = ["WeaveCrypto"];
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-try {
-  Components.utils.import("resource://gre/modules/Services.jsm");
-  Components.utils.import("resource://gre/modules/ctypes.jsm");
-}
-catch(ex) {}
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/ctypes.jsm");
 
 function WeaveCrypto() {
     this.init();
 }
 
 WeaveCrypto.prototype = {
-    classDescription: "WeaveCrypto",
-    contractID: "@labs.mozilla.com/Weave/Crypto;2",
-    classID: Components.ID("{7fa20841-c90e-4432-a1a1-ba3b20cb6b37}"),
     QueryInterface: XPCOMUtils.generateQI([Ci.IWeaveCrypto]),
 
     prefBranch : null,
     debug      : true,  // services.sync.log.cryptoDebug
     nss        : null,
     nss_t      : null,
 
     observer : {
@@ -71,16 +66,22 @@ WeaveCrypto.prototype = {
             let self = this._self;
             self.log("Observed " + topic + " topic.");
             if (topic == "nsPref:changed") {
                 self.debug = self.prefBranch.getBoolPref("cryptoDebug");
             }
         }
     },
 
+    // This is its own method so that it can be overridden.
+    // (Components.Exception isn't thread-safe for instance)
+    makeException : function makeException(message, result) {
+        return Components.Exception(message, result);
+    },
+
     init : function() {
         try {
             // Preferences. Add observer so we get notified of changes.
             this.prefBranch = Services.prefs.getBranch("services.sync.log.");
             this.prefBranch.QueryInterface(Ci.nsIPrefBranch2);
             this.prefBranch.addObserver("cryptoDebug", this.observer, false);
             this.observer._self = this;
             this.debug = this.prefBranch.getBoolPref("cryptoDebug");
@@ -124,17 +125,17 @@ WeaveCrypto.prototype = {
             nssfile.append("libnss3.so");
             break;
           case "Android":
             // Android uses a $GREDIR/lib/ subdir.
             nssfile.append("lib");
             nssfile.append("libnss3.so");
             break;
           default:
-            throw Components.Exception("unsupported platform: " + os, Cr.NS_ERROR_UNEXPECTED);
+            throw this.makeException("unsupported platform: " + os, Cr.NS_ERROR_UNEXPECTED);
         }
         this.log("Using NSS library " + nssfile.path);
 
         // XXX really want to be able to pass specific dlopen flags here.
         let nsslib = ctypes.open(nssfile.path);
 
         this.log("Initializing NSS types and function declarations...");
 
@@ -524,52 +525,52 @@ WeaveCrypto.prototype = {
         let keyItem = this.makeSECItem(symmetricKey, true);
         let ivItem  = this.makeSECItem(iv, true);
 
         // Determine which (padded) PKCS#11 mechanism to use.
         // EG: AES_128_CBC --> CKM_AES_CBC --> CKM_AES_CBC_PAD
         let mechanism = this.nss.PK11_AlgtagToMechanism(this.algorithm);
         mechanism = this.nss.PK11_GetPadMechanism(mechanism);
         if (mechanism == this.nss.CKM_INVALID_MECHANISM)
-            throw Components.Exception("invalid algorithm (can't pad)", Cr.NS_ERROR_FAILURE);
+            throw this.makeException("invalid algorithm (can't pad)", Cr.NS_ERROR_FAILURE);
 
         let ctx, symKey, slot, ivParam;
         try {
             ivParam = this.nss.PK11_ParamFromIV(mechanism, ivItem.address());
             if (ivParam.isNull())
-                throw Components.Exception("can't convert IV to param", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("can't convert IV to param", Cr.NS_ERROR_FAILURE);
 
             slot = this.nss.PK11_GetInternalKeySlot();
             if (slot.isNull())
-                throw Components.Exception("can't get internal key slot", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("can't get internal key slot", Cr.NS_ERROR_FAILURE);
 
             symKey = this.nss.PK11_ImportSymKey(slot, mechanism, this.nss.PK11_OriginUnwrap, operation, keyItem.address(), null);
             if (symKey.isNull())
-                throw Components.Exception("symkey import failed", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("symkey import failed", Cr.NS_ERROR_FAILURE);
 
             ctx = this.nss.PK11_CreateContextBySymKey(mechanism, operation, symKey, ivParam);
             if (ctx.isNull())
-                throw Components.Exception("couldn't create context for symkey", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("couldn't create context for symkey", Cr.NS_ERROR_FAILURE);
 
             let maxOutputSize = output.length;
             let tmpOutputSize = new ctypes.int(); // Note 1: NSS uses a signed int here...
 
             if (this.nss.PK11_CipherOp(ctx, output, tmpOutputSize.address(), maxOutputSize, input, input.length))
-                throw Components.Exception("cipher operation failed", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("cipher operation failed", Cr.NS_ERROR_FAILURE);
 
             let actualOutputSize = tmpOutputSize.value;
             let finalOutput = output.addressOfElement(actualOutputSize);
             maxOutputSize -= actualOutputSize;
 
             // PK11_DigestFinal sure sounds like the last step for *hashing*, but it
             // just seems to be an odd name -- NSS uses this to finish the current
             // cipher operation. You'd think it would be called PK11_CipherOpFinal...
             let tmpOutputSize2 = new ctypes.unsigned_int(); // Note 2: ...but an unsigned here!
             if (this.nss.PK11_DigestFinal(ctx, finalOutput, tmpOutputSize2.address(), maxOutputSize))
-                throw Components.Exception("cipher finalize failed", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("cipher finalize failed", Cr.NS_ERROR_FAILURE);
 
             actualOutputSize += tmpOutputSize2.value;
             let newOutput = ctypes.cast(output, ctypes.unsigned_char.array(actualOutputSize));
             return newOutput;
         } catch (e) {
             this.log("_commonCrypt: failed: " + e);
             throw e;
         } finally {
@@ -598,37 +599,37 @@ WeaveCrypto.prototype = {
             pubKey  = new this.nss_t.SECKEYPublicKey.ptr();
 
             let rsaParams = new this.nss_t.PK11RSAGenParams();
             rsaParams.keySizeInBits = this.keypairBits; // 1024, 2048, etc.
             rsaParams.pe = 65537;                       // public exponent.
 
             slot = this.nss.PK11_GetInternalSlot();
             if (slot.isNull())
-                throw Components.Exception("couldn't get internal slot", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("couldn't get internal slot", Cr.NS_ERROR_FAILURE);
 
             // Generate the keypair.
             privKey = this.nss.PK11_GenerateKeyPairWithFlags(slot,
                                                              this.nss.CKM_RSA_PKCS_KEY_PAIR_GEN,
                                                              rsaParams.address(),
                                                              pubKey.address(),
                                                              attrFlags, null);
             if (privKey.isNull())
-                throw Components.Exception("keypair generation failed", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("keypair generation failed", Cr.NS_ERROR_FAILURE);
             
             let s = this.nss.PK11_SetPrivateKeyNickname(privKey, "Weave User PrivKey");
             if (s)
-                throw Components.Exception("key nickname failed", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("key nickname failed", Cr.NS_ERROR_FAILURE);
 
             let wrappedPrivateKey = this._wrapPrivateKey(privKey, passphrase, salt, iv);
             out_wrappedPrivateKey.value = wrappedPrivateKey; // outparam
 
             let derKey = this.nss.SECKEY_EncodeDERSubjectPublicKeyInfo(pubKey);
             if (derKey.isNull())
-              throw Components.Exception("SECKEY_EncodeDERSubjectPublicKeyInfo failed", Cr.NS_ERROR_FAILURE);
+              throw this.makeException("SECKEY_EncodeDERSubjectPublicKeyInfo failed", Cr.NS_ERROR_FAILURE);
 
             let encodedPublicKey = this.encodeBase64(derKey.contents.data, derKey.contents.len);
             out_encodedPublicKey.value = encodedPublicKey; // outparam
         } catch (e) {
             this.log("generateKeypair: failed: " + e);
             throw e;
         } finally {
             if (pubKey && !pubKey.isNull())
@@ -658,37 +659,37 @@ WeaveCrypto.prototype = {
             break;
 
           case Ci.IWeaveCrypto.AES_256_CBC:
             keygenMech = this.nss.CKM_AES_KEY_GEN;
             keySize = 32;
             break;
 
           default:
-            throw Components.Exception("unknown algorithm", Cr.NS_ERROR_FAILURE);
+            throw this.makeException("unknown algorithm", Cr.NS_ERROR_FAILURE);
         }
 
         let slot, randKey, keydata;
         try {
             slot = this.nss.PK11_GetInternalSlot();
             if (slot.isNull())
-                throw Components.Exception("couldn't get internal slot", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("couldn't get internal slot", Cr.NS_ERROR_FAILURE);
 
             randKey = this.nss.PK11_KeyGen(slot, keygenMech, null, keySize, null);
             if (randKey.isNull())
-                throw Components.Exception("PK11_KeyGen failed.", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("PK11_KeyGen failed.", Cr.NS_ERROR_FAILURE);
 
             // Slightly odd API, this call just prepares the key value for
             // extraction, we get the actual bits from the call to PK11_GetKeyData().
             if (this.nss.PK11_ExtractKeyValue(randKey))
-                throw Components.Exception("PK11_ExtractKeyValue failed.", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("PK11_ExtractKeyValue failed.", Cr.NS_ERROR_FAILURE);
 
             keydata = this.nss.PK11_GetKeyData(randKey);
             if (keydata.isNull())
-                throw Components.Exception("PK11_GetKeyData failed.", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("PK11_GetKeyData failed.", Cr.NS_ERROR_FAILURE);
 
             return this.encodeBase64(keydata.contents.data, keydata.contents.len);
         } catch (e) {
             this.log("generateRandomKey: failed: " + e);
             throw e;
         } finally {
             if (randKey && !randKey.isNull())
                 this.nss.PK11_FreeSymKey(randKey);
@@ -709,17 +710,17 @@ WeaveCrypto.prototype = {
 
 
     generateRandomBytes : function(byteCount) {
         this.log("generateRandomBytes() called");
 
         // Temporary buffer to hold the generated data.
         let scratch = new ctypes.ArrayType(ctypes.unsigned_char, byteCount)();
         if (this.nss.PK11_GenerateRandom(scratch, byteCount))
-            throw Components.Exception("PK11_GenrateRandom failed", Cr.NS_ERROR_FAILURE);
+            throw this.makeException("PK11_GenrateRandom failed", Cr.NS_ERROR_FAILURE);
 
         return this.encodeBase64(scratch.address(), scratch.length);
     },
 
 
     wrapSymmetricKey : function(symmetricKey, encodedPublicKey) {
         this.log("wrapSymmetricKey() called");
 
@@ -732,46 +733,46 @@ WeaveCrypto.prototype = {
         let keyData = new ctypes.ArrayType(ctypes.unsigned_char, 4096)();
         let wrappedKey = new this.nss_t.SECItem(this.nss.SIBUFFER, keyData, keyData.length);
 
         // Step 2. Put the symmetric key bits into a P11 key object.
         let slot, symKey, pubKeyInfo, pubKey;
         try {
             slot = this.nss.PK11_GetInternalSlot();
             if (slot.isNull())
-                throw Components.Exception("couldn't get internal slot", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("couldn't get internal slot", Cr.NS_ERROR_FAILURE);
 
             // ImportSymKey wants a mechanism, from which it derives the key type.
             let keyMech = this.nss.PK11_AlgtagToMechanism(this.algorithm);
 
             // This imports a key with the usage set for encryption, but that doesn't
             // really matter because we're just going to wrap it up and not use it.
             symKey = this.nss.PK11_ImportSymKey(slot, keyMech, this.nss.PK11_OriginUnwrap, this.nss.CKA_ENCRYPT, symKeyData.address(), null);
             if (symKey.isNull())
-                throw Components.Exception("symkey import failed", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("symkey import failed", Cr.NS_ERROR_FAILURE);
 
             // Step 3. Put the public key bits into a P11 key object.
 
             // Can't just do this directly, it's expecting a minimal ASN1 blob
             // pubKey = SECKEY_ImportDERPublicKey(&pubKeyData, CKK_RSA);
             pubKeyInfo = this.nss.SECKEY_DecodeDERSubjectPublicKeyInfo(pubKeyData.address());
             if (pubKeyInfo.isNull())
-                throw Components.Exception("SECKEY_DecodeDERSubjectPublicKeyInfo failed", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("SECKEY_DecodeDERSubjectPublicKeyInfo failed", Cr.NS_ERROR_FAILURE);
 
             pubKey = this.nss.SECKEY_ExtractPublicKey(pubKeyInfo);
             if (pubKey.isNull())
-                throw Components.Exception("SECKEY_ExtractPublicKey failed", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("SECKEY_ExtractPublicKey failed", Cr.NS_ERROR_FAILURE);
 
             // Step 4. Wrap the symmetric key with the public key.
 
             let wrapMech = this.nss.PK11_AlgtagToMechanism(this.nss.SEC_OID_PKCS1_RSA_ENCRYPTION);
 
             let s = this.nss.PK11_PubWrapSymKey(wrapMech, pubKey, symKey, wrappedKey.address());
             if (s)
-                throw Components.Exception("PK11_PubWrapSymKey failed", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("PK11_PubWrapSymKey failed", Cr.NS_ERROR_FAILURE);
 
             // Step 5. Base64 encode the wrapped key, cleanup, and return to caller.
             return this.encodeBase64(wrappedKey.data, wrappedKey.len);
         } catch (e) {
             this.log("wrapSymmetricKey: failed: " + e);
             throw e;
         } finally {
             if (pubKey && !pubKey.isNull())
@@ -801,26 +802,26 @@ WeaveCrypto.prototype = {
             // Step 2. Convert the passphrase to a symmetric key and get the IV in the proper form.
             pbeKey = this._deriveKeyFromPassphrase(passphrase, salt);
             let ivItem = this.makeSECItem(iv, true);
 
             // AES_128_CBC --> CKM_AES_CBC --> CKM_AES_CBC_PAD
             let wrapMech = this.nss.PK11_AlgtagToMechanism(this.algorithm);
             wrapMech = this.nss.PK11_GetPadMechanism(wrapMech);
             if (wrapMech == this.nss.CKM_INVALID_MECHANISM)
-                throw Components.Exception("unwrapSymKey: unknown key mech", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("unwrapSymKey: unknown key mech", Cr.NS_ERROR_FAILURE);
 
             ivParam = this.nss.PK11_ParamFromIV(wrapMech, ivItem.address());
             if (ivParam.isNull())
-                throw Components.Exception("unwrapSymKey: PK11_ParamFromIV failed", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("unwrapSymKey: PK11_ParamFromIV failed", Cr.NS_ERROR_FAILURE);
 
             // Step 3. Unwrap the private key with the key from the passphrase.
             slot = this.nss.PK11_GetInternalSlot();
             if (slot.isNull())
-                throw Components.Exception("couldn't get internal slot", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("couldn't get internal slot", Cr.NS_ERROR_FAILURE);
 
             // Normally, one wants to associate a private key with a public key.
             // P11_UnwrapPrivKey() passes its keyID arg to PK11_MakeIDFromPubKey(),
             // which hashes the public key to create an ID (or, for small inputs,
             // assumes it's already hashed and does nothing).
             // We don't really care about this, because our unwrapped private key will
             // just live long enough to unwrap the bulk data key. So, we'll just jam in
             // a random value... We have an IV handy, so that will suffice.
@@ -831,34 +832,34 @@ WeaveCrypto.prototype = {
                                                   null,   // label
                                                   keyID,
                                                   false, // isPerm (token object)
                                                   true,  // isSensitive
                                                   this.nss.CKK_RSA,
                                                   privKeyUsage.addressOfElement(0), privKeyUsageLength,
                                                   null);  // wincx
             if (privKey.isNull())
-                throw Components.Exception("PK11_UnwrapPrivKey failed", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("PK11_UnwrapPrivKey failed", Cr.NS_ERROR_FAILURE);
 
             // Step 4. Unwrap the symmetric key with the user's private key.
 
             // XXX also have PK11_PubUnwrapSymKeyWithFlags() if more control is needed.
             // (last arg is keySize, 0 seems to work)
             symKey = this.nss.PK11_PubUnwrapSymKey(privKey, wrappedSymKey.address(), wrapMech,
                                                    this.nss.CKA_DECRYPT, 0);
             if (symKey.isNull())
-                throw Components.Exception("PK11_PubUnwrapSymKey failed", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("PK11_PubUnwrapSymKey failed", Cr.NS_ERROR_FAILURE);
 
             // Step 5. Base64 encode the unwrapped key, cleanup, and return to caller.
             if (this.nss.PK11_ExtractKeyValue(symKey))
-                throw Components.Exception("PK11_ExtractKeyValue failed.", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("PK11_ExtractKeyValue failed.", Cr.NS_ERROR_FAILURE);
 
             symKeyData = this.nss.PK11_GetKeyData(symKey);
             if (symKeyData.isNull())
-                throw Components.Exception("PK11_GetKeyData failed.", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("PK11_GetKeyData failed.", Cr.NS_ERROR_FAILURE);
 
             return this.encodeBase64(symKeyData.contents.data, symKeyData.contents.len);
         } catch (e) {
             this.log("unwrapSymmetricKey: failed: " + e);
             throw e;
         } finally {
             if (privKey && !privKey.isNull())
                 this.nss.SECKEY_DestroyPrivateKey(privKey);
@@ -888,40 +889,40 @@ WeaveCrypto.prototype = {
             // Step 2. Convert the passphrase to a symmetric key and get the IV in the proper form.
             let pbeKey = this._deriveKeyFromPassphrase(oldPassphrase, salt);
             let ivItem = this.makeSECItem(iv, true);
 
             // AES_128_CBC --> CKM_AES_CBC --> CKM_AES_CBC_PAD
             let wrapMech = this.nss.PK11_AlgtagToMechanism(this.algorithm);
             wrapMech = this.nss.PK11_GetPadMechanism(wrapMech);
             if (wrapMech == this.nss.CKM_INVALID_MECHANISM)
-                throw Components.Exception("rewrapSymKey: unknown key mech", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("rewrapSymKey: unknown key mech", Cr.NS_ERROR_FAILURE);
 
             ivParam = this.nss.PK11_ParamFromIV(wrapMech, ivItem.address());
             if (ivParam.isNull())
-                throw Components.Exception("rewrapSymKey: PK11_ParamFromIV failed", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("rewrapSymKey: PK11_ParamFromIV failed", Cr.NS_ERROR_FAILURE);
 
             // Step 3. Unwrap the private key with the key from the passphrase.
             slot = this.nss.PK11_GetInternalSlot();
             if (slot.isNull())
-                throw Components.Exception("couldn't get internal slot", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("couldn't get internal slot", Cr.NS_ERROR_FAILURE);
 
             let keyID = ivItem.address();
 
             privKey = this.nss.PK11_UnwrapPrivKey(slot,
                                                   pbeKey, wrapMech, ivParam, wrappedPrivKey.address(),
                                                   null,   // label
                                                   keyID,
                                                   false, // isPerm (token object)
                                                   true,  // isSensitive
                                                   this.nss.CKK_RSA,
                                                   privKeyUsage.addressOfElement(0), privKeyUsageLength,
                                                   null);  // wincx
             if (privKey.isNull())
-                throw Components.Exception("PK11_UnwrapPrivKey failed", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("PK11_UnwrapPrivKey failed", Cr.NS_ERROR_FAILURE);
 
             // Step 4. Rewrap the private key with the new passphrase.
             return this._wrapPrivateKey(privKey, newPassphrase, salt, iv);
         } catch (e) {
             this.log("rewrapPrivateKey: failed: " + e);
             throw e;
         } finally {
             if (privKey && !privKey.isNull())
@@ -950,26 +951,26 @@ WeaveCrypto.prototype = {
             // Step 2. Convert the passphrase to a symmetric key and get the IV in the proper form.
             pbeKey = this._deriveKeyFromPassphrase(passphrase, salt);
             let ivItem = this.makeSECItem(iv, true);
 
             // AES_128_CBC --> CKM_AES_CBC --> CKM_AES_CBC_PAD
             let wrapMech = this.nss.PK11_AlgtagToMechanism(this.algorithm);
             wrapMech = this.nss.PK11_GetPadMechanism(wrapMech);
             if (wrapMech == this.nss.CKM_INVALID_MECHANISM)
-                throw Components.Exception("rewrapSymKey: unknown key mech", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("rewrapSymKey: unknown key mech", Cr.NS_ERROR_FAILURE);
 
             ivParam = this.nss.PK11_ParamFromIV(wrapMech, ivItem.address());
             if (ivParam.isNull())
-                throw Components.Exception("rewrapSymKey: PK11_ParamFromIV failed", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("rewrapSymKey: PK11_ParamFromIV failed", Cr.NS_ERROR_FAILURE);
 
             // Step 3. Unwrap the private key with the key from the passphrase.
             slot = this.nss.PK11_GetInternalSlot();
             if (slot.isNull())
-                throw Components.Exception("couldn't get internal slot", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("couldn't get internal slot", Cr.NS_ERROR_FAILURE);
 
             let keyID = ivItem.address();
 
             privKey = this.nss.PK11_UnwrapPrivKey(slot,
                                                   pbeKey, wrapMech, ivParam, wrappedPrivKey.address(),
                                                   null,   // label
                                                   keyID,
                                                   false, // isPerm (token object)
@@ -999,17 +1000,17 @@ WeaveCrypto.prototype = {
     //
 
 
     // Compress a JS string (2-byte chars) into a normal C string (1-byte chars)
     // EG, for "ABC",  0x0041, 0x0042, 0x0043 --> 0x41, 0x42, 0x43
     byteCompress : function (jsString, charArray) {
         let intArray = ctypes.cast(charArray, ctypes.uint8_t.array(charArray.length));
         for (let i = 0; i < jsString.length; i++)
-            intArray[i] = jsString.charCodeAt(i);
+            intArray[i] = jsString.charCodeAt(i) % 256; // convert to bytes
     },
 
     // Expand a normal C string (1-byte chars) into a JS string (2-byte chars)
     // EG, for "ABC",  0x41, 0x42, 0x43 --> 0x0041, 0x0042, 0x0043
     byteExpand : function (charArray) {
         let expanded = "";
         let len = charArray.length;
         let intData = ctypes.cast(charArray, ctypes.uint8_t.array(len));
@@ -1053,25 +1054,25 @@ WeaveCrypto.prototype = {
         let keyLength  = 0;    // Callee will pick.
         let iterations = 4096; // PKCS#5 recommends at least 1000.
 
         let algid, slot, symKey;
         try {
             algid = this.nss.PK11_CreatePBEV2AlgorithmID(pbeAlg, cipherAlg, prfAlg,
                                                         keyLength, iterations, saltItem.address());
             if (algid.isNull())
-                throw Components.Exception("PK11_CreatePBEV2AlgorithmID failed", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("PK11_CreatePBEV2AlgorithmID failed", Cr.NS_ERROR_FAILURE);
 
             slot = this.nss.PK11_GetInternalSlot();
             if (slot.isNull())
-                throw Components.Exception("couldn't get internal slot", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("couldn't get internal slot", Cr.NS_ERROR_FAILURE);
 
             symKey = this.nss.PK11_PBEKeyGen(slot, algid, passItem.address(), false, null);
             if (symKey.isNull())
-                throw Components.Exception("PK11_PBEKeyGen failed", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("PK11_PBEKeyGen failed", Cr.NS_ERROR_FAILURE);
         } catch (e) {
             this.log("_deriveKeyFromPassphrase: failed: " + e);
             throw e;
         } finally {
             if (algid && !algid.isNull())
                 this.nss.SECOID_DestroyAlgorithmID(algid, true);
             if (slot && !slot.isNull())
                 this.nss.PK11_FreeSlot(slot);
@@ -1089,48 +1090,38 @@ WeaveCrypto.prototype = {
             pbeKey = this._deriveKeyFromPassphrase(passphrase, salt);
 
             let ivItem = this.makeSECItem(iv, true);
 
             // AES_128_CBC --> CKM_AES_CBC --> CKM_AES_CBC_PAD
             let wrapMech = this.nss.PK11_AlgtagToMechanism(this.algorithm);
             wrapMech = this.nss.PK11_GetPadMechanism(wrapMech);
             if (wrapMech == this.nss.CKM_INVALID_MECHANISM)
-                throw Components.Exception("wrapPrivKey: unknown key mech", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("wrapPrivKey: unknown key mech", Cr.NS_ERROR_FAILURE);
 
             let ivParam = this.nss.PK11_ParamFromIV(wrapMech, ivItem.address());
             if (ivParam.isNull())
-                throw Components.Exception("wrapPrivKey: PK11_ParamFromIV failed", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("wrapPrivKey: PK11_ParamFromIV failed", Cr.NS_ERROR_FAILURE);
 
             // Use a buffer to hold the wrapped key. NSS says about 1200 bytes for
             // a 2048-bit RSA key, so a 4096 byte buffer should be plenty.
             let keyData = new ctypes.ArrayType(ctypes.unsigned_char, 4096)();
             wrappedKey = new this.nss_t.SECItem(this.nss.SIBUFFER, keyData, keyData.length);
 
             let s = this.nss.PK11_WrapPrivKey(privKey.contents.pkcs11Slot,
                                               pbeKey, privKey,
                                               wrapMech, ivParam,
                                               wrappedKey.address(), null);
             if (s)
-                throw Components.Exception("wrapPrivKey: PK11_WrapPrivKey failed", Cr.NS_ERROR_FAILURE);
+                throw this.makeException("wrapPrivKey: PK11_WrapPrivKey failed", Cr.NS_ERROR_FAILURE);
 
             return this.encodeBase64(wrappedKey.data, wrappedKey.len);
         } catch (e) {
             this.log("_wrapPrivateKey: failed: " + e);
             throw e;
         } finally {
             if (ivParam && !ivParam.isNull())
                 this.nss.SECITEM_FreeItem(ivParam, true);
             if (pbeKey && !pbeKey.isNull())
                 this.nss.PK11_FreeSymKey(pbeKey);
         }
     }
 };
-
-// Gecko <2.0
-let component = typeof Services == "undefined" || typeof ctypes == "undefined" ? [] : [WeaveCrypto];
-function NSGetModule (compMgr, fileSpec) {
-    return XPCOMUtils.generateModule(component);
-}
-
-// Gecko >=2.0
-if (typeof XPCOMUtils.generateNSGetFactory == "function")
-    const NSGetFactory = XPCOMUtils.generateNSGetFactory([WeaveCrypto]);
new file mode 100644
--- /dev/null
+++ b/services/crypto/modules/threaded.js
@@ -0,0 +1,150 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Firefox Sync.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Philipp von Weitershausen <philipp@weitershausen>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+const EXPORTED_SYMBOLS = ["ThreadedCrypto"];
+
+const Cu = Components.utils;
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://services-sync/ext/Sync.js");
+Cu.import("resource://services-crypto/WeaveCrypto.js");
+
+/*
+ * Execute a function in a thread.
+ */
+function Runner(func, thisObj, returnval, error) {
+  this.func = func;
+  this.thisObj = thisObj;
+  this.returnval = returnval;
+  this.error = error;
+}
+Runner.prototype = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIRunnable]),
+
+  run: function run() {
+    let ex = this.error;
+    if (ex) {
+      this.func.throw(ex);
+    } else {
+      this.func.call(this.thisObj, this.returnval);
+    }
+  }
+};
+
+/*
+ * Execute a function in a thread and notify a callback on another thread
+ * afterward.
+ */
+function CallbackRunner(func, thisObj, args, callback, cbThread) {
+  this.func = func;
+  this.thisObj = thisObj;
+  this.args = args;
+  this.callback = callback;
+  this.cbThread = cbThread;
+}
+CallbackRunner.prototype = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIRunnable]),
+
+  run: function run() {
+    let returnval, error;
+    try {
+      returnval = this.func.apply(this.thisObj, this.args);
+    } catch(ex) {
+      error = ex;
+    }
+    this.cbThread.dispatch(new Runner(this.callback, this.thisObj,
+                                      returnval, error),
+                           Ci.nsIThread.DISPATCH_NORMAL);
+  }
+};
+
+/*
+ * Implementation of IWeaveCrypto that defers method calls to another thread
+ * but keeps the synchronous API. (Don't ask...)
+ */
+function ThreadedCrypto() {
+  this.backgroundThread = Services.tm.newThread(0);
+  this.crypto = new WeaveCrypto();
+
+  // Components.Exception isn't thread-safe.
+  this.crypto.makeException = function makeException(message, result) {
+    return result;
+  };
+
+  // Make sure to kill the thread before XPCOM shuts down.
+  Services.obs.addObserver(this, "profile-before-change", true);
+}
+ThreadedCrypto.deferToThread = function deferToThread(methodname) {
+  return function threadMethod() {
+    // Dispatch method call to background thread.
+    let args = Array.slice(arguments);
+    return Sync(function(callback) {
+      let runner = new CallbackRunner(this.crypto[methodname], this.crypto,
+                                      args, callback, Services.tm.mainThread);
+      this.backgroundThread.dispatch(runner, Ci.nsIThread.DISPATCH_NORMAL);
+    }, this)();
+  };
+};
+ThreadedCrypto.prototype = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.IWeaveCrypto,
+                                         Ci.nsISupportsWeakReference]),
+
+  observe: function observe() {
+    this.backgroundThread.shutdown();
+  },
+
+  get algorithm() this.crypto.algorithm,
+  set algorithm(value) this.crypto.algorithm = value,
+
+  get keypairBits() this.crypto.keypairBits,
+  set keypairBits(value) this.crypto.keypairBits = value,
+
+  encrypt: ThreadedCrypto.deferToThread("encrypt"),
+  decrypt: ThreadedCrypto.deferToThread("decrypt"),
+  generateKeypair: ThreadedCrypto.deferToThread("generateKeypair"),
+  generateRandomKey: ThreadedCrypto.deferToThread("generateRandomKey"),
+  generateRandomIV: ThreadedCrypto.deferToThread("generateRandomIV"),
+  generateRandomBytes: ThreadedCrypto.deferToThread("generateRandomBytes"),
+  wrapSymmetricKey: ThreadedCrypto.deferToThread("wrapSymmetricKey"),
+  unwrapSymmetricKey: ThreadedCrypto.deferToThread("unwrapSymmetricKey"),
+  rewrapPrivateKey: ThreadedCrypto.deferToThread("rewrapPrivateKey"),
+  verifyPassphrase: ThreadedCrypto.deferToThread("verifyPassphrase")
+};
new file mode 100644
--- /dev/null
+++ b/services/crypto/tests/unit/head_helpers.js
@@ -0,0 +1,68 @@
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+try {
+  // In the context of xpcshell tests, there won't be a default AppInfo
+  Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);
+}
+catch(ex) {
+
+do_get_profile();
+
+// Make sure to provide the right OS so crypto loads the right binaries
+let OS = "XPCShell";
+if ("@mozilla.org/windows-registry-key;1" in Cc)
+  OS = "WINNT";
+else if ("nsILocalFileMac" in Ci)
+  OS = "Darwin";
+else
+  OS = "Linux";
+
+let XULAppInfo = {
+  vendor: "Mozilla",
+  name: "XPCShell",
+  ID: "{3e3ba16c-1675-4e88-b9c8-afef81b3d2ef}",
+  version: "1",
+  appBuildID: "20100621",
+  platformVersion: "",
+  platformBuildID: "20100621",
+  inSafeMode: false,
+  logConsoleErrors: true,
+  OS: OS,
+  XPCOMABI: "noarch-spidermonkey",
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIXULAppInfo, Ci.nsIXULRuntime])
+};
+
+let XULAppInfoFactory = {
+  createInstance: function (outer, iid) {
+    if (outer != null)
+      throw Cr.NS_ERROR_NO_AGGREGATION;
+    return XULAppInfo.QueryInterface(iid);
+  }
+};
+
+let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+registrar.registerFactory(Components.ID("{fbfae60b-64a4-44ef-a911-08ceb70b9f31}"),
+                          "XULAppInfo", "@mozilla.org/xre/app-info;1",
+                          XULAppInfoFactory);
+
+}
+
+// Provide resource://services-crypto if it isn't already available
+let weaveService = Cc["@mozilla.org/weave/service;1"].getService();
+weaveService.wrappedJSObject.addResourceAlias();
+
+/**
+ * Print some debug message to the console. All arguments will be printed,
+ * separated by spaces.
+ *
+ * @param [arg0, arg1, arg2, ...]
+ *        Any number of arguments to print out
+ * @usage _("Hello World") -> prints "Hello World"
+ * @usage _(1, 2, 3) -> prints "1 2 3"
+ */
+let _ = function(some, debug, text, to) print(Array.slice(arguments).join(" "));
rename from services/sync/tests/unit/test_crypto_crypt.js
rename to services/crypto/tests/unit/test_crypto_crypt.js
--- a/services/sync/tests/unit/test_crypto_crypt.js
+++ b/services/crypto/tests/unit/test_crypto_crypt.js
@@ -1,13 +1,19 @@
-Cu.import("resource://services-sync/util.js");
+let cryptoSvc;
+try {
+  Components.utils.import("resource://services-crypto/threaded.js");
+  cryptoSvc = new ThreadedCrypto();
+} catch (ex) {
+  // Fallback to binary WeaveCrypto
+  cryptoSvc = Cc["@labs.mozilla.com/Weave/Crypto;1"]
+                .getService(Ci.IWeaveCrypto);
+}
 
 function run_test() {
-  let cryptoSvc = Svc.Crypto;
-
   // First, do a normal run with expected usage... Generate a random key and
   // iv, encrypt and decrypt a string.
   var iv = cryptoSvc.generateRandomIV();
   do_check_eq(iv.length, 24);
 
   var key = cryptoSvc.generateRandomKey();
   do_check_eq(key.length, 44);
 
@@ -114,17 +120,17 @@ function run_test() {
   cryptoSvc.algorithm = Ci.IWeaveCrypto.AES_128_CBC;
   key = "St1tFCor7vQEJNug/465dQ==";
   iv  = "oLjkfrLIOnK2bDRvW4kXYA==";
   mySecret = "does thunder read testcases?";
   cipherText = cryptoSvc.encrypt(mySecret, key, iv);
   do_check_eq(cipherText, "T6fik9Ros+DB2ablH9zZ8FWZ0xm/szSwJjIHZu7sjPs=");
 
   var badkey    = "badkeybadkeybadkeybadk==";
-  var badiv     = "badivbadivbadivbadivbad==";
+  var badiv     = "badivbadivbadivbadivbad=";
   var badcipher = "crapinputcrapinputcrapinputcrapinputcrapinp=";
   var failure;
 
   try {
     failure = false;
     clearText = cryptoSvc.decrypt(cipherText, badkey, iv);
   } catch (e) {
     failure = true;
rename from services/sync/tests/unit/test_crypto_keypair.js
rename to services/crypto/tests/unit/test_crypto_keypair.js
--- a/services/sync/tests/unit/test_crypto_keypair.js
+++ b/services/crypto/tests/unit/test_crypto_keypair.js
@@ -1,13 +1,19 @@
-Cu.import("resource://services-sync/util.js");
+let cryptoSvc;
+try {
+  Components.utils.import("resource://services-crypto/threaded.js");
+  cryptoSvc = new ThreadedCrypto();
+} catch (ex) {
+  // Fallback to binary WeaveCrypto
+  cryptoSvc = Cc["@labs.mozilla.com/Weave/Crypto;1"]
+                .getService(Ci.IWeaveCrypto);
+}
 
 function run_test() {
-  let cryptoSvc = Svc.Crypto;
-
   var salt = cryptoSvc.generateRandomBytes(16);
   do_check_eq(salt.length, 24);
 
   var iv = cryptoSvc.generateRandomIV();
   do_check_eq(iv.length, 24);
 
   var symKey = cryptoSvc.generateRandomKey();
   do_check_eq(symKey.length, 44);
rename from services/sync/tests/unit/test_crypto_random.js
rename to services/crypto/tests/unit/test_crypto_random.js
--- a/services/sync/tests/unit/test_crypto_random.js
+++ b/services/crypto/tests/unit/test_crypto_random.js
@@ -1,13 +1,19 @@
-Cu.import("resource://services-sync/util.js");
+let cryptoSvc;
+try {
+  Components.utils.import("resource://services-crypto/threaded.js");
+  cryptoSvc = new ThreadedCrypto();
+} catch (ex) {
+  // Fallback to binary WeaveCrypto
+  cryptoSvc = Cc["@labs.mozilla.com/Weave/Crypto;1"]
+                .getService(Ci.IWeaveCrypto);
+}
 
 function run_test() {
-  let cryptoSvc = Svc.Crypto;
-
   // Test salt generation.
   var salt;
 
   salt = cryptoSvc.generateRandomBytes(0);
   do_check_eq(salt.length, 0);
   salt = cryptoSvc.generateRandomBytes(1);
   do_check_eq(salt.length, 4);
   salt = cryptoSvc.generateRandomBytes(2);
rename from services/sync/tests/unit/test_crypto_rewrap.js
rename to services/crypto/tests/unit/test_crypto_rewrap.js
--- a/services/sync/tests/unit/test_crypto_rewrap.js
+++ b/services/crypto/tests/unit/test_crypto_rewrap.js
@@ -1,13 +1,19 @@
-Cu.import("resource://services-sync/util.js");
+let cryptoSvc;
+try {
+  Components.utils.import("resource://services-crypto/threaded.js");
+  cryptoSvc = new ThreadedCrypto();
+} catch (ex) {
+  // Fallback to binary WeaveCrypto
+  cryptoSvc = Cc["@labs.mozilla.com/Weave/Crypto;1"]
+                .getService(Ci.IWeaveCrypto);
+}
 
 function run_test() {
-  let cryptoSvc = Svc.Crypto;
-
   var salt = cryptoSvc.generateRandomBytes(16);
   var iv = cryptoSvc.generateRandomIV();
   var symKey = cryptoSvc.generateRandomKey();
 
   // Tests with a 2048 bit key (the default)
   do_check_eq(cryptoSvc.keypairBits, 2048)
   var pubOut = {};
   var privOut = {};
rename from services/sync/tests/unit/test_crypto_verify.js
rename to services/crypto/tests/unit/test_crypto_verify.js
--- a/services/sync/tests/unit/test_crypto_verify.js
+++ b/services/crypto/tests/unit/test_crypto_verify.js
@@ -1,13 +1,19 @@
-Cu.import("resource://services-sync/util.js");
+let cryptoSvc;
+try {
+  Components.utils.import("resource://services-crypto/threaded.js");
+  cryptoSvc = new ThreadedCrypto();
+} catch (ex) {
+  // Fallback to binary WeaveCrypto
+  cryptoSvc = Cc["@labs.mozilla.com/Weave/Crypto;1"]
+                .getService(Ci.IWeaveCrypto);
+}
 
 function run_test() {
-  let cryptoSvc = Svc.Crypto;
-
   var salt = cryptoSvc.generateRandomBytes(16);
   var iv = cryptoSvc.generateRandomIV();
 
   // Tests with a 2048 bit key (the default)
   do_check_eq(cryptoSvc.keypairBits, 2048)
   var privOut = {};
   cryptoSvc.generateKeypair("passphrase", salt, iv, {}, privOut);
   var privKey = privOut.value;
--- a/services/sync/Weave.js
+++ b/services/sync/Weave.js
@@ -75,22 +75,26 @@ WeaveService.prototype = {
 
   addResourceAlias: function() {
     let ioService = Cc["@mozilla.org/network/io-service;1"]
                     .getService(Ci.nsIIOService);
     let resProt = ioService.getProtocolHandler("resource")
                   .QueryInterface(Ci.nsIResProtocolHandler);
 
     // Only create alias if resource://services-sync doesn't already exist.
-    if (resProt.hasSubstitution("services-sync"))
-      return;
-
-    let uri = ioService.newURI("resource:///modules/services-sync/",
-                               null, null);
-    resProt.setSubstitution("services-sync", uri);
+    if (!resProt.hasSubstitution("services-sync")) {
+      let uri = ioService.newURI("resource:///modules/services-sync/",
+                                 null, null);
+      resProt.setSubstitution("services-sync", uri);
+    }
+    if (!resProt.hasSubstitution("services-crypto")) {
+      let uri = ioService.newURI("resource:///modules/services-crypto/",
+                                 null, null);
+      resProt.setSubstitution("services-crypto", uri);
+    }
   }
 };
 
 function AboutWeaveLog() {}
 AboutWeaveLog.prototype = {
   classID: Components.ID("{d28f8a0b-95da-48f4-b712-caf37097be41}"),
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule,
--- a/services/sync/modules/engines/clients.js
+++ b/services/sync/modules/engines/clients.js
@@ -124,17 +124,17 @@ ClientEngine.prototype = {
 
   get localName() {
     let localName = Svc.Prefs.get("client.name", "");
     if (localName != "")
       return localName;
 
     // Generate a client name if we don't have a useful one yet
     let user = Svc.Env.get("USER") || Svc.Env.get("USERNAME") ||
-               Svc.Prefs.get("username");
+               Svc.Prefs.get("account") || Svc.Prefs.get("username");
     let brand = new StringBundle("chrome://branding/locale/brand.properties");
     let app = brand.get("brandShortName");
 
     let system = Svc.SysInfo.get("device") ||
                  Cc["@mozilla.org/network/protocol;1?name=http"]
                    .getService(Ci.nsIHttpProtocolHandler).oscpu;
 
     return this.localName = Str.sync.get("client.name2", [user, app, system]);
--- a/services/sync/modules/engines/history.js
+++ b/services/sync/modules/engines/history.js
@@ -43,22 +43,29 @@ const Cu = Components.utils;
 const GUID_ANNO = "weave/guid";
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/stores.js");
 Cu.import("resource://services-sync/trackers.js");
 Cu.import("resource://services-sync/type_records/history.js");
 Cu.import("resource://services-sync/util.js");
+Cu.import("resource://services-sync/log4moz.js");
 
 // Create some helper functions to handle GUIDs
 function setGUID(uri, guid) {
   if (arguments.length == 1)
     guid = Utils.makeGUID();
-  Utils.anno(uri, GUID_ANNO, guid, "WITH_HISTORY");
+
+  try {
+    Utils.anno(uri, GUID_ANNO, guid, "WITH_HISTORY");
+  } catch (ex) {
+    let log = Log4Moz.repository.getLogger("Engine.History");
+    log.warn("Couldn't annotate URI " + uri + ": " + ex);
+  }
   return guid;
 }
 function GUIDForUri(uri, create) {
   try {
     // Use the existing GUID if it exists
     return Utils.anno(uri, GUID_ANNO);
   }
   catch (ex) {
--- a/services/sync/modules/service.js
+++ b/services/sync/modules/service.js
@@ -352,32 +352,36 @@ WeaveSvc.prototype = {
     let capp = new Log4Moz.ConsoleAppender(formatter);
     capp.level = Log4Moz.Level[Svc.Prefs.get("log.appender.console")];
     root.addAppender(capp);
 
     let dapp = new Log4Moz.DumpAppender(formatter);
     dapp.level = Log4Moz.Level[Svc.Prefs.get("log.appender.dump")];
     root.addAppender(dapp);
 
-    let verbose = Svc.Directory.get("ProfD", Ci.nsIFile);
-    verbose.QueryInterface(Ci.nsILocalFile);
-    verbose.append("weave");
-    verbose.append("logs");
-    verbose.append("verbose-log.txt");
-    if (!verbose.exists())
-      verbose.create(verbose.NORMAL_FILE_TYPE, PERMS_FILE);
-
-    let maxSize = 65536; // 64 * 1024 (64KB)
-    this._debugApp = new Log4Moz.RotatingFileAppender(verbose, formatter, maxSize);
-    this._debugApp.level = Log4Moz.Level[Svc.Prefs.get("log.appender.debugLog")];
-    root.addAppender(this._debugApp);
+    let enabled = Svc.Prefs.get("log.appender.debugLog.enabled", false);
+    if (enabled) {
+      let verbose = Svc.Directory.get("ProfD", Ci.nsIFile);
+      verbose.QueryInterface(Ci.nsILocalFile);
+      verbose.append("weave");
+      verbose.append("logs");
+      verbose.append("verbose-log.txt");
+      if (!verbose.exists())
+        verbose.create(verbose.NORMAL_FILE_TYPE, PERMS_FILE);
+  
+      let maxSize = 65536; // 64 * 1024 (64KB)
+      this._debugApp = new Log4Moz.RotatingFileAppender(verbose, formatter, maxSize);
+      this._debugApp.level = Log4Moz.Level[Svc.Prefs.get("log.appender.debugLog")];
+      root.addAppender(this._debugApp);
+    }
   },
 
   clearLogs: function WeaveSvc_clearLogs() {
-    this._debugApp.clear();
+    if (this._debugApp)
+      this._debugApp.clear();
   },
 
   /**
    * Register the built-in engines for certain applications
    */
   _registerEngines: function WeaveSvc__registerEngines() {
     let engines = [];
     // Applications can provide this preference (comma-separated list)
--- a/services/sync/modules/util.js
+++ b/services/sync/modules/util.js
@@ -992,21 +992,16 @@ let FakeSvc = {
     }
   },
   // A fake service only used for testing
   "@labs.mozilla.com/Fake/Thing;1": {
     isFake: true
   }
 };
 
-// Use the binary WeaveCrypto (;1) if the js-ctypes version (;2) fails to load
-// by adding an alias on FakeSvc from ;2 to ;1
-Utils.lazySvc(FakeSvc, "@labs.mozilla.com/Weave/Crypto;2",
-              "@labs.mozilla.com/Weave/Crypto;1", "IWeaveCrypto");
-
 /*
  * Commonly-used services
  */
 let Svc = {};
 Svc.Prefs = new Preferences(PREFS_BRANCH);
 Svc.DefaultPrefs = new Preferences({branch: PREFS_BRANCH, defaultBranch: true});
 Svc.Obs = Observers;
 
@@ -1014,17 +1009,16 @@ this.__defineGetter__("_sessionCID", fun
   //sets session CID based on browser type
   let appInfo = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);
   return appInfo.ID == SEAMONKEY_ID ? "@mozilla.org/suite/sessionstore;1"
                                     : "@mozilla.org/browser/sessionstore;1";
 });
 [["Annos", "@mozilla.org/browser/annotation-service;1", "nsIAnnotationService"],
  ["AppInfo", "@mozilla.org/xre/app-info;1", "nsIXULAppInfo"],
  ["Bookmark", "@mozilla.org/browser/nav-bookmarks-service;1", "nsINavBookmarksService"],
- ["Crypto", "@labs.mozilla.com/Weave/Crypto;2", "IWeaveCrypto"],
  ["Directory", "@mozilla.org/file/directory_service;1", "nsIProperties"],
  ["Env", "@mozilla.org/process/environment;1", "nsIEnvironment"],
  ["Favicon", "@mozilla.org/browser/favicon-service;1", "nsIFaviconService"],
  ["Form", "@mozilla.org/satchel/form-history;1", "nsIFormHistory2"],
  ["History", "@mozilla.org/browser/nav-history-service;1", "nsPIPlacesDatabase"],
  ["Idle", "@mozilla.org/widget/idleservice;1", "nsIIdleService"],
  ["IO", "@mozilla.org/network/io-service;1", "nsIIOService"],
  ["KeyFactory", "@mozilla.org/security/keyobjectfactory;1", "nsIKeyObjectFactory"],
@@ -1036,16 +1030,31 @@ this.__defineGetter__("_sessionCID", fun
  ["Script", "@mozilla.org/moz/jssubscript-loader;1", "mozIJSSubScriptLoader"],
  ["SysInfo", "@mozilla.org/system-info;1", "nsIPropertyBag2"],
  ["Version", "@mozilla.org/xpcom/version-comparator;1", "nsIVersionComparator"],
  ["WinMediator", "@mozilla.org/appshell/window-mediator;1", "nsIWindowMediator"],
  ["WinWatcher", "@mozilla.org/embedcomp/window-watcher;1", "nsIWindowWatcher"],
  ["Session", this._sessionCID, "nsISessionStore"],
 ].forEach(function(lazy) Utils.lazySvc(Svc, lazy[0], lazy[1], lazy[2]));
 
+Svc.__defineGetter__("Crypto", function() {
+  let cryptoSvc;
+  try {
+    let ns = {};
+    Cu.import("resource://services-crypto/threaded.js", ns);
+    cryptoSvc = new ns.ThreadedCrypto();
+  } catch (ex) {
+    // Fallback to binary WeaveCrypto
+    cryptoSvc = Cc["@labs.mozilla.com/Weave/Crypto;1"].
+                getService(Ci.IWeaveCrypto);
+  }
+  delete Svc.Crypto;
+  return Svc.Crypto = cryptoSvc;
+});
+
 let Str = {};
 ["errors", "sync"]
   .forEach(function(lazy) Utils.lazy2(Str, lazy, Utils.lazyStrings(lazy)));
 
 Svc.Obs.add("xpcom-shutdown", function () {
   for (let name in Svc)
     delete Svc[name];
 });
--- a/services/sync/tests/unit/test_history_store.js
+++ b/services/sync/tests/unit/test_history_store.js
@@ -108,16 +108,24 @@ function run_test() {
                               type: Ci.nsINavHistoryService.TRANSITION_TYPED}]});
     });
     do_check_eq([id for (id in store.getAllIDs())].length, 2);
     queryres = queryHistoryVisits(tburi);
     do_check_eq(queryres.length, 1);
     do_check_eq(queryres[0].time, TIMESTAMP3);
     do_check_eq(queryres[0].title, "The bird is the word!");
 
+    _("Make sure we handle invalid URLs in places databases gracefully.");
+    let query = "INSERT INTO moz_places "
+      + "(url, title, rev_host, visit_count, last_visit_date) "
+      + "VALUES ('invalid-uri', 'Invalid URI', '.', 1, " + TIMESTAMP3 + ")";
+    let stmt = Utils.createStatement(Svc.History.DBConnection, query);
+    let result = Utils.queryAsync(stmt);    
+    do_check_eq([id for (id in store.getAllIDs())].length, 3);
+
     _("Remove a record from the store.");
     store.remove({id: fxguid});
     do_check_false(store.itemExists(fxguid));
     queryres = queryHistoryVisits(fxuri);
     do_check_eq(queryres.length, 0);
 
     _("Make sure wipe works.");
     store.wipe();