Bug 617868 - Use the RFC5869-compliant HKDF in the SyncKeyBundle. r=mconnor
authorPhilipp von Weitershausen <philipp@weitershausen.de>
Thu, 09 Dec 2010 12:14:17 -0800
changeset 59041 11a11df115386b8dc1c7516c57a582a2be17badb
parent 59040 9b506c98d19d94047c99905ed8e2d828e561bc53
child 59042 2fa36f8b633c9da29ea58ba1a02f7bcecccd2920
push id1
push usershaver@mozilla.com
push dateTue, 04 Jan 2011 17:58:04 +0000
reviewersmconnor
bugs617868
Bug 617868 - Use the RFC5869-compliant HKDF in the SyncKeyBundle. r=mconnor This requires new bulk keys and constitutes a storage version bump.
services/sync/modules/base_records/crypto.js
services/sync/modules/constants.js
services/sync/tests/unit/test_records_crypto_generateEntry.js
--- a/services/sync/modules/base_records/crypto.js
+++ b/services/sync/modules/base_records/crypto.js
@@ -535,34 +535,26 @@ SyncKeyBundle.prototype = {
       this.generateEntry();
     return this._hmacObj;
   },
   
   /*
    * If we've got a string, hash it into keys and store them.
    */
   generateEntry: function generateEntry() {
-    let m = this.keyStr;
-    if (m) {
-      // Decode into a 16-byte string before we go any further.
-      m = Utils.decodeKeyBase32(m);
-      
-      // Reuse the hasher.
-      let h = Utils.makeHMACHasher();
-      
-      // First key.
-      let u = this.username; 
-      let k1 = Utils.makeHMACKey("" + HMAC_INPUT + u + "\x01");
-      let enc = Utils.sha256HMACBytes(m, k1, h);
-      
-      // Second key: depends on the output of the first run.
-      let k2 = Utils.makeHMACKey(enc + HMAC_INPUT + u + "\x02");
-      let hmac = Utils.sha256HMACBytes(m, k2, h);
-      
-      // Save them.
-      this._encrypt = btoa(enc);
-      
-      // Individual sets: cheaper than calling parent setter.
-      this._hmac = hmac;
-      this._hmacObj = Utils.makeHMACKey(hmac);
-    }
+    let syncKey = this.keyStr;
+    if (!syncKey)
+      return;
+
+    // Expand the base32 Sync Key to an AES 256 and 256 bit HMAC key.
+    let prk = Utils.decodeKeyBase32(syncKey);
+    let info = HMAC_INPUT + this.username;
+    let okm = Utils.hkdfExpand(prk, info, 32 * 2);
+    let enc = okm.slice(0, 32);
+    let hmac = okm.slice(32, 64);
+
+    // Save them.
+    this._encrypt = btoa(enc);      
+    // Individual sets: cheaper than calling parent setter.
+    this._hmac = hmac;
+    this._hmacObj = Utils.makeHMACKey(hmac);
   }
 };
--- a/services/sync/modules/constants.js
+++ b/services/sync/modules/constants.js
@@ -40,17 +40,17 @@ let EXPORTED_SYMBOLS = [((this[key] = va
 
 WEAVE_CHANNEL:                         "@xpi_type@",
 WEAVE_VERSION:                         "@weave_version@",
 WEAVE_ID:                              "@weave_id@",
 
 // Version of the data format this client supports. The data format describes
 // how records are packaged; this is separate from the Server API version and
 // the per-engine cleartext formats.
-STORAGE_VERSION:                       4,
+STORAGE_VERSION:                       5,
 
 UPDATED_DEV_URL:                       "https://services.mozilla.com/sync/updated/?version=@weave_version@&channel=@xpi_type@",
 UPDATED_REL_URL:                       "http://www.mozilla.com/firefox/sync/updated.html",
 
 PREFS_BRANCH:                          "services.sync.",
 
 // Host "key" to access Weave Identity in the password manager
 PWDMGR_HOST:                           "chrome://weave",
--- a/services/sync/tests/unit/test_records_crypto_generateEntry.js
+++ b/services/sync/tests/unit/test_records_crypto_generateEntry.js
@@ -1,24 +1,24 @@
 let atob = Cu.import("resource://services-sync/util.js").atob;
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/base_records/crypto.js");
 
 /**
- * Testing the SHA256-HMAC key derivation process against st3fan's implementation
- * in Firefox Home.
+ * Testing the SHA256-HMAC key derivation process against test vectors
+ * verified with the Firefox Home implementation.
  */
 function run_test() {
   
   // Test the production of keys from a sync key.
   let bundle = new SyncKeyBundle(PWDMGR_PASSPHRASE_REALM, "st3fan", "q7ynpwq7vsc9m34hankbyi3s3i");
   
   // These should be compared to the results from Home, as they once were.
-  let e = "3fe2d3743fe03d4f460ce2405ec189e68dfd7e42c97d50fab9bda3761263cc87";
-  let h = "bf05f720423d297e8fd55faee7cdeaf32aa15cfb6e56115268c9c326b999795a";
+  let e = "14b8c09fa84e92729ee695160af6e0385f8f6215a25d14906e1747bdaa2de426";
+  let h = "370e3566245d79fe602a3adb5137e42439cd2a571235197e0469d7d541b07875";
   
   // The encryption key is stored as base64 for handing off to WeaveCrypto.
   let realE = Utils.bytesAsHex(atob(bundle.encryptionKey));
   let realH = Utils.bytesAsHex(bundle.hmacKey);
   
   _("Real E: " + realE);
   _("Real H: " + realH);
   do_check_eq(realH, h);