I’m back from the EU MozCamp in Prague and we all had a great time! Check out the slides from my talks: Labs Overview and Weave in Depth.
A few people at the MozCamp were interested in Weave’s use of cryptography to protect the user’s data and privacy. Although the specs for the Weave server are available, it may take someone new a while to wrap their head around the whole scheme. I’m going to attempt explaining what crypto operations we do and why we do it in this blog post.
First, let’s get some basic definitions out of the way. Symmetric cryptography means you have one key that can perform both encryption and decryption, and they are complementary operations. For Weave, we use AES with a 256 bit key, and we use it in a mode that requires an ‘initialization vector’ for every decryption. Asymmetric cryptography means there’s a pair of keys (usually called ‘public’ and ‘private’ keys). A piece of text “encrypted” by one key can only be “decrypted” by the other key. Here, we use RSA with a 2048 bit private key.
So, when a user first signs up for Weave using the wizard on their computer, we generate a (random) pair of public and private keys. Next, we use the user’s passphrase to create a symmetric key. This is done using a pretty standard algorithm known as PBKDF2 (short for “Password Key Derivation Function”). The PBKDF2 algorithm requires a ‘salt’ value which is also stored on the server. Now that we have a symmetric key, we use it to encrypt the user’s private key and upload it along with the public key to the server. Note that the passphrase is never sent to the server, so if the user’s password ever gets compromised all the attacker can get is their encrypted private key, which really isn’t of much use (especially given that the key is 2048 bits long).
Whenever a particular “engine” is to be synchronized (an engine could be Tabs, Bookmarks, History etc.) we generate a random symmetric key for that engine. This key is then encrypted using the user’s public key (now, one can only retrieve the original symmetric key with the corresponding private key) and uploaded as being associated with a particular engine. All entries (the ‘ciphertext’ property in a “Weave Basic Object”) in that engine are encrypted with the symmetric key that was generated for it.
To make things clear, let’s enumerate the steps we would take to decrypt a single tab object for user ‘foo’:
- Find the user’s cluster by making a GET request to https://services.mozilla.com/user/1/foo/node/weave. It returns https://sj-weave06.services.mozilla.com/.
- Fetch the user’s encrypted private key and public key from https://sj-weave06.services.mozilla.com/0.5/foo/storage/keys/privkey and https://sj-weave06.services.mozilla.com/0.5/foo/storage/keys/pubkey respectively. The user’s password is required to access these JSON objects.
- Ask the user for their passphrase and generate a 256 bit symmetric key from it using PBKDF2 and the ‘salt’ found in the privkey object.
- Use the generated symmetric key and the initialization vector found in the ‘iv’ property of the privkey object to decrypt the user’s private key.
- Fetch the user’s encrypted tab objects from https://sj-weave06.services.mozilla.com/0.5/foo/storage/tabs/?full=1.
- Fetch the corresponding symmetric key (the URL is also listed in the “encryption” property of every WBO), in this case https://sj-weave06.services.mozilla.com/0.5/foo/storage/crypto/tabs.
- Decrypt the symmetric key with the user’s private key.
- Use the decrypted symmetric key to decrypt any WBO from the tabs collection with the initialization vector found in the ‘bulkIV’ property of the tabs symmetric key WBO.
- Profit.
A word about the formats in which the keys are actually stored in. All values are Base64. For symmetric keys, the key is stored as-is. For asymmetric keys, I wish we used a standard format like PKCS#12, but we don’t. It’s still ASN.1 though, in some format NSS exports private keys in. You need to do a bit of ASN.1 parsing to figure out the values you’re interested in.
Fortunately, I’ve already figured out most of the details for you – check out my Javascript or PHP implementations of the crypto elements required to decrypt Weave Basic Objects.
Finally, a quick note about why we do all this. Sharing is now reasonably easy, if you want to share your bookmarks with someone, you just need to encrypt the corresponding symmetric key with their public key and they’re good to go. Also, each WBO has it’s own ‘encryption’ property so this can be as granular as needed. Secondly, the passphrase is never stored anywhere (except possibly on the user’s computer) so the server never sees anything other than encrypted blobs of Base64′ed text. Along with making HTTPS mandatory, we think this is a pretty secure way of protecting the user’s data.
If you have other encryption schemes that might fit into Weave’s use cases please let us know! (We’ve already been looking at interesting developments in this area such as Tahoe). I’d also love to hear from you if you have any questions on our current cryptography scheme. We’re constantly trying to improve the security and efficiency of our system so these details are only valid until we change our scheme
Now, go write that third-party Weave client, you have no excuse not to!
Have you considered supporting a model in which the user’s private key does *not* get stored on the server, but instead gets sneakernetted between the user’s systems? You could still encrypt it with a symmetric key generated from a passphrase, as an added security measure against key compromise, but by not storing it on the server, you avoid the possibility of a passphrase dictionary attack by someone who doesn’t have the key.
I realize that this model doesn’t prove quite as easy to use for the average user, but for the technically inclined user willing to go to a little extra trouble for security, it seems useful to support.
Massive bonus points if you support integrating this with the user’s existing GPG key; you could use that to encrypt the Weave private key.
By the way, thank you for your in-depth explanation of Weave’s crypto. I’ve seen overviews before, but you provide several useful details I didn’t know yet.
I wonder if you would mind explaining one other thing about Weave that I’ve wondered strongly about:
How does Weave interact with “clear recent history”? If I want to wipe out my last X hours of browsing history, cookies, etc, then how does Weave deal with that? Does it preserve any information? Will other clients properly forget this information as well?
I assume that Weave does a sensible job if I use Private Browsing mode, and it doesn’t save any information from a Private Browsing session. I’d just like to know what happens if I browse normally and clear recent history afterward.
Thanks.
I installed Weave recently, and have been really pleased with it.
But IMO Weave really needs to find a way of only needing one password. Use the hash of one as the other one, split the given password into two halves – whatever. But making people supply both a password and a passphrase is weird and counter-intuitive, and it more than doubles the chance of people forgetting something important.
Gerv
@Anonymous: We have considered providing support for custom user keys, though the focus right now is on a stable and reliable 1.0 release. As for how Weave interacts with “clear recent history”, your history might get synced but the entries will quickly be deleted on the next cycle (the action of clearing recent history would trigger a sync).
@Gerv: We’re definitely thinking about deriving the password from the passphrase. thunder has been working on a spec: it’s available at https://wiki.mozilla.org/Labs/Weave/WEP/100 (Comments welcome!)
I may have misunderstood but does that mean if you share your symmetric key (e.g. for bookmarks) with someone, you can’t revoke it later without regenerating the symmetric key for that component?
Pingback: Smörgåsbord » Weave: Browser bookmark & password syncing The Right Way
Pingback: Mozilla’s Weave and cryptography. « My Blog
Pingback: Le blog de Xavier … » Blog Archive » Quoi de neuf cette semaine …
I suggest that you or one of your peers create one or more illustrative pics explaining the above article more comprehensively. I know quite something about crypto, but many other uses do not. The illustration could be similar to a UML sequence diagram (http://en.wikipedia.org/wiki/Sequence_diagram) and show which steps happen on the client vs. server side. Who en-/decrypts what, where and when?
Pingback: Xentience (Internal) Blog » Ars examines Chrome and Firefox bookmark sync protocols
I’m trying to write a plugin for DolplinHD (a browser for android) to support Firefox 4 Sync (version 3) but am struggling with the crypto in Java. I could not find my way around the C++ -source of Firefox for Android (have the code here).
Somehow I never end up with a valid RSA-key in step 4.
my step 3 is:
private static SecretKeySpec passwordToSymmetricKey(final char[] password, byte[] salt) throws Exception {
// http://stackoverflow.com/questions/992019/java-256bit-aes-encryption
SecretKeyFactory f = SecretKeyFactory.getInstance(“PBKDF2WithHmacSHA1″);
KeySpec ks = new PBEKeySpec(password, salt, 1024, 128);
SecretKey s = f.generateSecret(ks);
SecretKeySpec k = new SecretKeySpec(s.getEncoded(), “AES”);
return k;
}
my step 4 is:
SecretKeySpec symmetricKey = passwordToSymmetricKey(passphrase.toCharArray(), salt);
Cipher cipher = Cipher.getInstance(“AES/CBC/NoPadding”);
IvParameterSpec ips = new IvParameterSpec(iv);
cipher.init(Cipher.UNWRAP_MODE, symmetricKey, ips);
Key decryptedPrivKey = cipher.unwrap(privKey, “RSA”, Cipher.PRIVATE_KEY);
the problem:
“invalid key format” (trying to parse the key as PKCS8=ASN.1)
Pingback: Delicious Bookmarks for December 13th from 18:47 to 21:50 « Lâmôlabs
Pingback: Why should you encrypt user data? « Binary Sunrise