Bug 601645 - JavaScript API for NSS J-PAKE. r=mconnor a=blocking-beta8
authorBrian Smith <bsmith@mozilla.com>
Thu, 09 Dec 2010 18:27:48 -0800
changeset 59046 7916ff577de1f36ae45a0f944422bd1601dc7152
parent 59045 146e2854e3f5b16eae89dae61cdd247347edeac0
child 59047 006396b0b99071d92b619a0143c94e66506350d6
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmconnor, blocking-beta8
bugs601645
milestone2.0b8pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 601645 - JavaScript API for NSS J-PAKE. r=mconnor a=blocking-beta8
browser/build.mk
services/crypto/Makefile.in
services/crypto/nsISyncJPAKE.idl
services/crypto/nsSyncJPAKE.cpp
services/crypto/nsSyncJPAKE.h
toolkit/library/libxul-config.mk
toolkit/library/nsStaticXULComponents.cpp
toolkit/toolkit-tiers.mk
--- a/browser/build.mk
+++ b/browser/build.mk
@@ -45,17 +45,16 @@ ifdef MOZ_EXTENSIONS
 tier_app_dirs += extensions
 endif
 
 tier_app_dirs += $(MOZ_BRANDING_DIRECTORY)
 
 tier_app_dirs += toolkit/components/console/hudservice
 
 ifdef MOZ_SERVICES_SYNC
-tier_app_dirs += services/crypto
 tier_app_dirs += services/sync
 endif
 
 tier_app_dirs += browser
 # Never add other tier_app_dirs after browser. They won't get packaged
 # properly on mac.
 
 installer:
--- a/services/crypto/Makefile.in
+++ b/services/crypto/Makefile.in
@@ -43,18 +43,37 @@ VPATH     = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE = services-crypto
 XPIDL_MODULE = services-crypto
 
 XPIDLSRCS = \
   IWeaveCrypto.idl \
+  nsISyncJPAKE.idl \
+  $(NULL)
+
+LIBRARY_NAME   = services-crypto
+EXPORT_LIBRARY = 1
+IS_COMPONENT   = 1
+MODULE_NAME    = nsServicesCryptoModule
+LIBXUL_LIBRARY = 1
+FORCE_USE_PIC  = 1
+MOZILLA_INTERNAL_API = 1
+
+CPPSRCS = \
+  nsSyncJPAKE.cpp \
   $(NULL)
 
 libs::
 	$(PYTHON) $(topsrcdir)/config/nsinstall.py $(srcdir)/modules/* $(FINAL_TARGET)/modules/services-crypto
 
 ifdef ENABLE_TESTS
 DIRS += tests
 endif
 
 include $(topsrcdir)/config/rules.mk
+
+EXTRA_DSO_LDOPTS += \
+	$(MOZ_COMPONENT_LIBS) \
+    $(NSPR_LIBS) \
+    $(NSS_LIBS) \
+    $(NULL)
new file mode 100644
--- /dev/null
+++ b/services/crypto/nsISyncJPAKE.idl
@@ -0,0 +1,137 @@
+/* ***** 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
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Brian Smith <bsmith@mozilla.com>
+ * Philipp von Weitershausen <philipp@weitershausen.de>
+ *
+ * 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 ***** */
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(5ab02a98-5122-4b90-93cd-f259c4b42e3a)]
+interface nsISyncJPAKE : nsISupports
+{
+  /**
+   * Perform first round of the JPAKE exchange.
+   *
+   * @param aSignerID
+   *        String identifying the signer.
+   * @param aGX1
+   *        Schnorr signature value g^x1, in hex representation.
+   * @param aGV1
+   *        Schnorr signature value g^v1 (v1 is a random value), in hex
+   *        representation.
+   * @param aR1
+   *        Schnorr signature value r1 = v1 - x1 * h, in hex representation.
+   * @param aGX2
+   *        Schnorr signature value g^x2, in hex representation.
+   * @param aGV2
+   *        Schnorr signature value g^v2 (v2 is a random value), in hex
+   *        representation.
+   * @param aR2
+   *        Schnorr signature value r2 = v2 - x2 * h, in hex representation.
+   */
+  void round1(in ACString aSignerID,
+              out ACString aGX1,
+              out ACString aGV1,
+              out ACString aR1,
+              out ACString aGX2,
+              out ACString aGV2,
+              out ACString aR2);
+
+  /**
+   * Perform second round of the JPAKE exchange.
+   *
+   * @param aPeerID
+   *        String identifying the peer.
+   * @param aPIN
+   *        String containing the weak secret (PIN).
+   * @param aGX3
+   *        Schnorr signature value g^x3, in hex representation.
+   * @param aGV3
+   *        Schnorr signature value g^v3 (v3 is a random value), in hex
+   *        representation.
+   * @param aR3
+   *        Schnorr signature value r3 = v3 - x3 * h, in hex representation.
+   * @param aGX4
+   *        Schnorr signature value g^x4, in hex representation.
+   * @param aGV4
+   *        Schnorr signature value g^v4 (v4 is a random value), in hex
+   *        representation.
+   * @param aR4
+   *        Schnorr signature value r4 = v4 - x4 * h, in hex representation.
+   * @param aA
+   *        Schnorr signature value A, in hex representation.
+   * @param aGVA
+   *        Schnorr signature value g^va (va is a random value), in hex
+   *        representation.
+   * @param aRA
+   *        Schnorr signature value ra = va - xa * h, in hex representation.
+   */
+  void round2(in ACString aPeerID,
+              in ACString aPIN,
+              in ACString aGX3,
+              in ACString aGV3,
+              in ACString aR3,
+              in ACString aGX4,
+              in ACString aGV4,
+              in ACString aR4,
+              out ACString aA,
+              out ACString aGVA,
+              out ACString aRA);
+
+  /**
+   * Perform the final step of the JPAKE exchange. This will compute
+   * the key and expand the key to two keys, an AES256 encryption key
+   * and a 256 bit HMAC key. It returns a key confirmation value
+   * (SHA256d of the key) and the encryption and HMAC keys.
+   *
+   * @param aB
+   *        Schnorr signature value B, in hex representation.
+   * @param aGVB
+   *        Schnorr signature value g^vb (vb is a random value), in hex
+   *        representation.
+   * @param aRB
+   *        Schnorr signature value rb = vb - xb * h, in hex representation.
+   * @param aAES256Key
+   *        The AES 256 encryption key, in base64 representation.
+   * @param aHMAC256Key
+   *        The 256 bit HMAC key, in base64 representation.
+   */
+  void final(in ACString aB,
+             in ACString aGVB,
+             in ACString aRB,
+             in ACString aHkdfInfo,
+             out ACString aAES256Key,
+             out ACString aHMAC256Key);
+};
new file mode 100644
--- /dev/null
+++ b/services/crypto/nsSyncJPAKE.cpp
@@ -0,0 +1,476 @@
+/* ***** 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
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Brian Smith <bsmith@mozilla.com>
+ * Philipp von Weitershausen <philipp@weitershausen.de>
+ *
+ * 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 ***** */
+
+#include "nsSyncJPAKE.h"
+#include "mozilla/ModuleUtils.h"
+#include <pk11pub.h>
+#include <keyhi.h>
+#include <pkcs11.h>
+#include <nscore.h>
+#include <secmodt.h>
+#include <secport.h>
+#include <secerr.h>
+#include <nsDebug.h>
+#include <nsError.h>
+#include <base64.h>
+#include <nsString.h>
+
+static PRBool
+hex_from_2char(const unsigned char *c2, unsigned char *byteval)
+{
+  int i;
+  unsigned char offset;
+  *byteval = 0;
+  for (i=0; i<2; i++) {
+    if (c2[i] >= '0' && c2[i] <= '9') {
+      offset = c2[i] - '0';
+      *byteval |= offset << 4*(1-i);
+    } else if (c2[i] >= 'a' && c2[i] <= 'f') {
+      offset = c2[i] - 'a';
+      *byteval |= (offset + 10) << 4*(1-i);
+    } else if (c2[i] >= 'A' && c2[i] <= 'F') {
+      offset = c2[i] - 'A';
+      *byteval |= (offset + 10) << 4*(1-i);
+    } else {
+      return PR_FALSE;
+    }
+  }
+  return PR_TRUE;
+}
+
+static PRBool
+fromHex(const char * str, unsigned char * p, size_t sLen)
+{
+  size_t i;
+  if (sLen & 1)
+    return PR_FALSE;
+
+  for (i = 0; i < sLen / 2; ++i) {
+    if (!hex_from_2char((const unsigned char *) str + (2*i),
+                        (unsigned char *) p + i)) {
+      return PR_FALSE;
+    }
+  }
+  return PR_TRUE;
+}
+
+static nsresult
+fromHexString(const nsACString & str, unsigned char * p, size_t pMaxLen)
+{
+  char * strData = (char *) str.Data();
+  unsigned len = str.Length();
+  NS_ENSURE_ARG(len / 2 <= pMaxLen);
+  if (!fromHex(strData, p, len)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+  return NS_OK;
+}
+
+static PRBool
+toHexString(const unsigned char * str, unsigned len, nsACString & out)
+{
+  static const char digits[] = "0123456789ABCDEF";
+  if (!out.SetCapacity(2 * len))
+    return NS_ERROR_OUT_OF_MEMORY;
+  out.SetLength(0);
+  for (unsigned i = 0; i < len; ++i) {
+    out.Append(digits[str[i] >> 4]);
+    out.Append(digits[str[i] & 0x0f]);
+  }
+  return PR_TRUE;
+}
+
+static nsresult
+mapErrno()
+{
+  int err = PORT_GetError();
+  switch (err) {
+    case SEC_ERROR_NO_MEMORY:   return NS_ERROR_OUT_OF_MEMORY;
+    default:                    return NS_ERROR_UNEXPECTED;
+  }
+}
+
+#define NUM_ELEM(x) (sizeof(x) / sizeof (x)[0])
+
+static const char p[] = 
+  "90066455B5CFC38F9CAA4A48B4281F292C260FEEF01FD61037E56258A7795A1C"
+  "7AD46076982CE6BB956936C6AB4DCFE05E6784586940CA544B9B2140E1EB523F"
+  "009D20A7E7880E4E5BFA690F1B9004A27811CD9904AF70420EEFD6EA11EF7DA1"
+  "29F58835FF56B89FAA637BC9AC2EFAAB903402229F491D8D3485261CD068699B"
+  "6BA58A1DDBBEF6DB51E8FE34E8A78E542D7BA351C21EA8D8F1D29F5D5D159394"
+  "87E27F4416B0CA632C59EFD1B1EB66511A5A0FBF615B766C5862D0BD8A3FE7A0"
+  "E0DA0FB2FE1FCB19E8F9996A8EA0FCCDE538175238FC8B0EE6F29AF7F642773E"
+  "BE8CD5402415A01451A840476B2FCEB0E388D30D4B376C37FE401C2A2C2F941D"
+  "AD179C540C1C8CE030D460C4D983BE9AB0B20F69144C1AE13F9383EA1C08504F"
+  "B0BF321503EFE43488310DD8DC77EC5B8349B8BFE97C2C560EA878DE87C11E3D"
+  "597F1FEA742D73EEC7F37BE43949EF1A0D15C3F3E3FC0A8335617055AC91328E"
+  "C22B50FC15B941D3D1624CD88BC25F3E941FDDC6200689581BFEC416B4B2CB73";
+static const char q[] =
+  "CFA0478A54717B08CE64805B76E5B14249A77A4838469DF7F7DC987EFCCFB11D";
+static const char g[] = 
+  "5E5CBA992E0A680D885EB903AEA78E4A45A469103D448EDE3B7ACCC54D521E37"
+  "F84A4BDD5B06B0970CC2D2BBB715F7B82846F9A0C393914C792E6A923E2117AB"
+  "805276A975AADB5261D91673EA9AAFFEECBFA6183DFCB5D3B7332AA19275AFA1"
+  "F8EC0B60FB6F66CC23AE4870791D5982AAD1AA9485FD8F4A60126FEB2CF05DB8"
+  "A7F0F09B3397F3937F2E90B9E5B9C9B6EFEF642BC48351C46FB171B9BFA9EF17"
+  "A961CE96C7E7A7CC3D3D03DFAD1078BA21DA425198F07D2481622BCE45969D9C"
+  "4D6063D72AB7A0F08B2F49A7CC6AF335E08C4720E31476B67299E231F8BD90B3"
+  "9AC3AE3BE0C6B6CACEF8289A2E2873D58E51E029CAFBD55E6841489AB66B5B4B"
+  "9BA6E2F784660896AFF387D92844CCB8B69475496DE19DA2E58259B090489AC8"
+  "E62363CDF82CFD8EF2A427ABCD65750B506F56DDE3B988567A88126B914D7828"
+  "E2B63A6D7ED0747EC59E0E0A23CE7D8A74C1D2C2A7AFB6A29799620F00E11C33"
+  "787F7DED3B30E1A22D09F1FBDA1ABBBFBF25CAE05A13F812E34563F99410E73B";
+
+NS_IMETHODIMP nsSyncJPAKE::Round1(const nsACString & aSignerID,
+                                  nsACString & aGX1 NS_OUTPARAM,
+                                  nsACString & aGV1 NS_OUTPARAM,
+                                  nsACString & aR1 NS_OUTPARAM,
+                                  nsACString & aGX2 NS_OUTPARAM,
+                                  nsACString & aGV2 NS_OUTPARAM,
+                                  nsACString & aR2 NS_OUTPARAM)
+{
+  NS_ENSURE_STATE(round == JPAKENotStarted);
+  NS_ENSURE_STATE(key == NULL);
+
+  static CK_MECHANISM_TYPE mechanisms[] = {
+    CKM_NSS_JPAKE_ROUND1_SHA256,
+    CKM_NSS_JPAKE_ROUND2_SHA256,
+    CKM_NSS_JPAKE_FINAL_SHA256
+  };
+
+  PK11SlotInfo * slot = PK11_GetBestSlotMultiple(mechanisms,
+                                                 NUM_ELEM(mechanisms), NULL);
+  NS_ENSURE_STATE(slot != NULL);
+    
+  CK_BYTE pBuf[(NUM_ELEM(p) - 1) / 2];
+  CK_BYTE qBuf[(NUM_ELEM(q) - 1) / 2];
+  CK_BYTE gBuf[(NUM_ELEM(g) - 1) / 2];
+    
+  CK_KEY_TYPE keyType = CKK_NSS_JPAKE_ROUND1;
+  NS_ENSURE_STATE(fromHex(p, pBuf, (NUM_ELEM(p) - 1)));
+  NS_ENSURE_STATE(fromHex(q, qBuf, (NUM_ELEM(q) - 1)));
+  NS_ENSURE_STATE(fromHex(g, gBuf, (NUM_ELEM(g) - 1)));
+  CK_ATTRIBUTE keyTemplate[] = {
+    { CKA_NSS_JPAKE_SIGNERID, (CK_BYTE *) aSignerID.Data(),
+                              aSignerID.Length() },
+    { CKA_KEY_TYPE, &keyType, sizeof keyType },
+    { CKA_PRIME,    pBuf,     sizeof pBuf },
+    { CKA_SUBPRIME, qBuf,     sizeof qBuf },
+    { CKA_BASE,     gBuf,     sizeof gBuf }
+  };
+
+  CK_BYTE gx1Buf[NUM_ELEM(p) / 2];
+  CK_BYTE gv1Buf[NUM_ELEM(p) / 2];
+  CK_BYTE r1Buf [NUM_ELEM(p) / 2];
+  CK_BYTE gx2Buf[NUM_ELEM(p) / 2];
+  CK_BYTE gv2Buf[NUM_ELEM(p) / 2];
+  CK_BYTE r2Buf [NUM_ELEM(p) / 2];
+  CK_NSS_JPAKERound1Params rp = {
+      { gx1Buf, sizeof gx1Buf, gv1Buf, sizeof gv1Buf, r1Buf, sizeof r1Buf },
+      { gx2Buf, sizeof gx2Buf, gv2Buf, sizeof gv2Buf, r2Buf, sizeof r2Buf }
+  };
+  SECItem paramsItem;
+  paramsItem.data = (unsigned char *) &rp;
+  paramsItem.len = sizeof rp;
+  key = PK11_KeyGenWithTemplate(slot, CKM_NSS_JPAKE_ROUND1_SHA256,
+                                CKM_NSS_JPAKE_ROUND1_SHA256,
+                                &paramsItem, keyTemplate,
+                                NUM_ELEM(keyTemplate), NULL);
+  nsresult rv = key != NULL
+              ? NS_OK
+              : mapErrno();
+  if (rv == NS_OK) {
+    NS_ENSURE_TRUE(toHexString(rp.gx1.pGX, rp.gx1.ulGXLen, aGX1) &&
+                   toHexString(rp.gx1.pGV, rp.gx1.ulGVLen, aGV1) &&
+                   toHexString(rp.gx1.pR,  rp.gx1.ulRLen,  aR1)  &&
+                   toHexString(rp.gx2.pGX, rp.gx2.ulGXLen, aGX2) &&
+                   toHexString(rp.gx2.pGV, rp.gx2.ulGVLen, aGV2) &&
+                   toHexString(rp.gx2.pR,  rp.gx2.ulRLen,  aR2),
+                   NS_ERROR_OUT_OF_MEMORY);
+    round = JPAKEBeforeRound2;
+  }
+  return rv;
+}
+
+NS_IMETHODIMP nsSyncJPAKE::Round2(const nsACString & aPeerID,
+                                  const nsACString & aPIN,
+                                  const nsACString & aGX3,
+                                  const nsACString & aGV3,
+                                  const nsACString & aR3,
+                                  const nsACString & aGX4,
+                                  const nsACString & aGV4,
+                                  const nsACString & aR4,
+                                  nsACString & aA NS_OUTPARAM,
+                                  nsACString & aGVA NS_OUTPARAM,
+                                  nsACString & aRA NS_OUTPARAM)
+{
+  NS_ENSURE_STATE(round == JPAKEBeforeRound2);
+  NS_ENSURE_STATE(key != NULL);
+  NS_ENSURE_ARG(!aPeerID.IsEmpty());
+
+  /* PIN cannot be equal to zero when converted to a bignum. NSS 3.12.9 J-PAKE
+     assumes that the caller has already done this check. Future versions of 
+     NSS J-PAKE will do this check internally. See Bug 609068 Comment 4 */
+  PRBool foundNonZero = PR_FALSE;
+  for (size_t i = 0; i < aPIN.Length(); ++i) {
+    if (aPIN[i] != 0) {
+      foundNonZero = PR_TRUE;
+      break;
+    }
+  }
+  NS_ENSURE_ARG(foundNonZero);
+
+  CK_BYTE gx3Buf[NUM_ELEM(p)/2], gv3Buf[NUM_ELEM(p)/2], r3Buf [NUM_ELEM(p)/2];
+  CK_BYTE gx4Buf[NUM_ELEM(p)/2], gv4Buf[NUM_ELEM(p)/2], r4Buf [NUM_ELEM(p)/2];
+  CK_BYTE gxABuf[NUM_ELEM(p)/2], gvABuf[NUM_ELEM(p)/2], rABuf [NUM_ELEM(p)/2];
+  nsresult         rv = fromHexString(aGX3, gx3Buf, sizeof gx3Buf);
+  if (rv == NS_OK) rv = fromHexString(aGV3, gv3Buf, sizeof gv3Buf);
+  if (rv == NS_OK) rv = fromHexString(aR3,  r3Buf,  sizeof r3Buf);
+  if (rv == NS_OK) rv = fromHexString(aGX4, gx4Buf, sizeof gx4Buf);
+  if (rv == NS_OK) rv = fromHexString(aGV4, gv4Buf, sizeof gv4Buf);
+  if (rv == NS_OK) rv = fromHexString(aR4,  r4Buf,  sizeof r4Buf);
+  if (rv != NS_OK)
+    return rv;
+
+  CK_NSS_JPAKERound2Params rp;
+  rp.pSharedKey = (CK_BYTE *) aPIN.Data();
+  rp.ulSharedKeyLen = aPIN.Length();
+  rp.gx3.pGX = gx3Buf; rp.gx3.ulGXLen = aGX3.Length() / 2;
+  rp.gx3.pGV = gv3Buf; rp.gx3.ulGVLen = aGV3.Length() / 2;
+  rp.gx3.pR  = r3Buf;  rp.gx3.ulRLen  = aR3 .Length() / 2;
+  rp.gx4.pGX = gx4Buf; rp.gx4.ulGXLen = aGX4.Length() / 2;
+  rp.gx4.pGV = gv4Buf; rp.gx4.ulGVLen = aGV4.Length() / 2;
+  rp.gx4.pR  = r4Buf;  rp.gx4.ulRLen  = aR4 .Length() / 2;
+  rp.A.pGX   = gxABuf; rp.A  .ulGXLen = sizeof gxABuf;
+  rp.A.pGV   = gvABuf; rp.A  .ulGVLen = sizeof gxABuf;
+  rp.A.pR    = rABuf;  rp.A  .ulRLen  = sizeof gxABuf;
+  SECItem paramsItem;
+  paramsItem.data = (unsigned char *) &rp;
+  paramsItem.len = sizeof rp;
+  CK_KEY_TYPE keyType = CKK_NSS_JPAKE_ROUND2;
+  CK_ATTRIBUTE keyTemplate[] = {
+    { CKA_NSS_JPAKE_PEERID, (CK_BYTE *) aPeerID.Data(), aPeerID.Length(), },
+    { CKA_KEY_TYPE, &keyType, sizeof keyType }
+  };
+  PK11SymKey * newKey = PK11_DeriveWithTemplate(key,
+                                                CKM_NSS_JPAKE_ROUND2_SHA256,
+                                                &paramsItem,
+                                                CKM_NSS_JPAKE_FINAL_SHA256,
+                                                CKA_DERIVE, 0,
+                                                keyTemplate,
+                                                NUM_ELEM(keyTemplate),
+                                                PR_FALSE);
+  if (newKey != NULL) {
+    if (toHexString(rp.A.pGX, rp.A.ulGXLen, aA) &&
+        toHexString(rp.A.pGV, rp.A.ulGVLen, aGVA) &&
+        toHexString(rp.A.pR, rp.A.ulRLen, aRA)) {
+      round = JPAKEAfterRound2;
+      PK11_FreeSymKey(key);
+      key = newKey;
+      return NS_OK;
+    } else {
+      PK11_FreeSymKey(newKey);
+      rv = NS_ERROR_OUT_OF_MEMORY;
+    }
+  } else
+    rv = mapErrno();
+
+  return rv;
+}
+
+static nsresult
+setBase64(const unsigned char * data, unsigned len, nsACString & out)
+{
+  nsresult rv = NS_OK;
+  const char * base64 = BTOA_DataToAscii(data, len);
+  
+  if (base64 != NULL) {
+    size_t len = PORT_Strlen(base64);
+    if (out.SetCapacity(len)) {
+      out.SetLength(0);
+      out.Append(base64, len);
+      PORT_Free((void*) base64);
+    } else {
+      rv = NS_ERROR_OUT_OF_MEMORY;
+    }
+  } else {
+    rv = NS_ERROR_OUT_OF_MEMORY;
+  }
+  return rv;
+}
+
+static nsresult
+base64KeyValue(PK11SymKey * key, nsACString & keyString)
+{
+  nsresult rv = NS_OK;
+  if (PK11_ExtractKeyValue(key) == SECSuccess) {
+    const SECItem * value = PK11_GetKeyData(key);
+    rv = value != NULL && value->data != NULL && value->len > 0
+       ? setBase64(value->data, value->len, keyString)
+       : NS_ERROR_UNEXPECTED;
+  } else {
+    rv = mapErrno();
+  }
+  return rv;
+}
+
+static nsresult
+extractBase64KeyValue(PK11SymKey * keyBlock, CK_ULONG bitPosition,
+                      CK_MECHANISM_TYPE destMech, int keySize,
+                      nsACString & keyString)
+{
+  SECItem paramsItem;
+  paramsItem.data = (CK_BYTE *) &bitPosition;
+  paramsItem.len = sizeof bitPosition;
+  PK11SymKey * key = PK11_Derive(keyBlock, CKM_EXTRACT_KEY_FROM_KEY,
+                                 &paramsItem, destMech,
+                                 CKA_SIGN, keySize);
+  if (key == NULL)
+    return mapErrno();
+  nsresult rv = base64KeyValue(key, keyString);
+  PK11_FreeSymKey(key);
+  return rv;
+}
+
+
+NS_IMETHODIMP nsSyncJPAKE::Final(const nsACString & aB,
+                                 const nsACString & aGVB,
+                                 const nsACString & aRB,
+                                 const nsACString & aHKDFInfo,
+                                 nsACString & aAES256Key NS_OUTPARAM,
+                                 nsACString & aHMAC256Key NS_OUTPARAM)
+{
+  static const unsigned AES256_KEY_SIZE = 256 / 8;
+  static const unsigned HMAC_SHA256_KEY_SIZE = 256 / 8;
+  CK_EXTRACT_PARAMS aesBitPosition = 0;
+  CK_EXTRACT_PARAMS hmacBitPosition = aesBitPosition + (AES256_KEY_SIZE * 8);
+
+  NS_ENSURE_STATE(round == JPAKEAfterRound2);
+  NS_ENSURE_STATE(key != NULL);
+
+  CK_BYTE gxBBuf[NUM_ELEM(p)/2], gvBBuf[NUM_ELEM(p)/2], rBBuf [NUM_ELEM(p)/2];
+  nsresult         rv = fromHexString(aB,   gxBBuf, sizeof gxBBuf);
+  if (rv == NS_OK) rv = fromHexString(aGVB, gvBBuf, sizeof gvBBuf);
+  if (rv == NS_OK) rv = fromHexString(aRB,  rBBuf,  sizeof rBBuf);
+  if (rv != NS_OK)
+    return rv;
+
+  CK_NSS_JPAKEFinalParams rp;
+  rp.B.pGX = gxBBuf; rp.B.ulGXLen = aB  .Length() / 2;
+  rp.B.pGV = gvBBuf; rp.B.ulGVLen = aGVB.Length() / 2;
+  rp.B.pR  = rBBuf;  rp.B.ulRLen  = aRB .Length() / 2;
+  SECItem paramsItem;
+  paramsItem.data = (unsigned char *) &rp;
+  paramsItem.len = sizeof rp;
+  PK11SymKey * keyMaterial = PK11_Derive(key, CKM_NSS_JPAKE_FINAL_SHA256,
+                                         &paramsItem, CKM_NSS_HKDF_SHA256,
+                                         CKA_DERIVE, 0);
+  PK11SymKey * keyBlock = NULL;
+
+  if (keyMaterial == NULL)
+    rv = mapErrno();
+
+  if (rv == NS_OK) {
+    CK_NSS_HKDFParams hkdfParams;
+    hkdfParams.bExtract = CK_TRUE;
+    hkdfParams.pSalt = NULL;
+    hkdfParams.ulSaltLen = 0;
+    hkdfParams.bExpand = CK_TRUE;
+    hkdfParams.pInfo = (CK_BYTE *) aHKDFInfo.Data();
+    hkdfParams.ulInfoLen = aHKDFInfo.Length();
+    paramsItem.data = (unsigned char *) &hkdfParams;
+    paramsItem.len = sizeof hkdfParams;
+    keyBlock = PK11_Derive(keyMaterial, CKM_NSS_HKDF_SHA256,
+                           &paramsItem, CKM_EXTRACT_KEY_FROM_KEY,
+                           CKA_DERIVE, AES256_KEY_SIZE + HMAC_SHA256_KEY_SIZE);
+    if (keyBlock == NULL)
+      rv = mapErrno();
+  }
+
+  if (rv == NS_OK) {
+    rv = extractBase64KeyValue(keyBlock, aesBitPosition, CKM_AES_CBC,
+                               AES256_KEY_SIZE, aAES256Key);
+  }
+  if (rv == NS_OK) {
+    rv = extractBase64KeyValue(keyBlock, hmacBitPosition, CKM_SHA256_HMAC,
+                               HMAC_SHA256_KEY_SIZE, aHMAC256Key);
+  }
+
+  if (rv == NS_OK) {
+    SECStatus srv = PK11_ExtractKeyValue(keyMaterial);
+    NS_ENSURE_TRUE(srv == SECSuccess, NS_ERROR_UNEXPECTED); // XXX leaks
+    SECItem * keyMaterialBytes = PK11_GetKeyData(keyMaterial);
+    NS_ENSURE_TRUE(keyMaterialBytes != NULL, NS_ERROR_UNEXPECTED);
+  }
+
+  if (keyBlock != NULL)
+    PK11_FreeSymKey(keyBlock);
+  if (keyMaterial != NULL)
+    PK11_FreeSymKey(keyMaterial);
+
+  return rv;
+}
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSyncJPAKE)
+NS_DEFINE_NAMED_CID(NS_SYNCJPAKE_CID);
+
+nsSyncJPAKE::nsSyncJPAKE() : round(JPAKENotStarted), key(NULL) { }
+
+nsSyncJPAKE::~nsSyncJPAKE()
+{
+  if (key != NULL)
+    PK11_FreeSymKey(key);
+}
+
+static const mozilla::Module::CIDEntry kServicesCryptoCIDs[] = {
+  { &kNS_SYNCJPAKE_CID, false, NULL, nsSyncJPAKEConstructor },
+  { NULL }
+};
+
+static const mozilla::Module::ContractIDEntry kServicesCryptoContracts[] = {
+  { NS_SYNCJPAKE_CONTRACTID, &kNS_SYNCJPAKE_CID },
+  { NULL }
+};
+
+static const mozilla::Module kServicesCryptoModule = {
+  mozilla::Module::kVersion,
+  kServicesCryptoCIDs,
+  kServicesCryptoContracts
+};
+
+NSMODULE_DEFN(nsServicesCryptoModule) = &kServicesCryptoModule;
new file mode 100644
--- /dev/null
+++ b/services/crypto/nsSyncJPAKE.h
@@ -0,0 +1,65 @@
+/* ***** 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
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Brian Smith <bsmith@mozilla.com>
+ * Philipp von Weitershausen <philipp@weitershausen.de>
+ *
+ * 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 ***** */
+#ifndef nsSyncJPAKE_h__
+#define nsSyncJPAKE_h__
+
+#include "nsISyncJPAKE.h"
+
+#define NS_SYNCJPAKE_CONTRACTID \
+  "@mozilla.org/services-crypto/sync-jpake;1"
+
+#define NS_SYNCJPAKE_CID \
+  {0x0b9721c0, 0x1805, 0x47c3, {0x86, 0xce, 0x68, 0x13, 0x79, 0x5a, 0x78, 0x3f}}
+
+typedef struct PK11SymKeyStr PK11SymKey;
+
+class nsSyncJPAKE : public nsISyncJPAKE
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSISYNCJPAKE
+  nsSyncJPAKE();
+  virtual ~nsSyncJPAKE();
+private:
+  enum { JPAKENotStarted, JPAKEBeforeRound2, JPAKEAfterRound2 } round;
+  PK11SymKey * key;
+};
+
+NS_IMPL_ISUPPORTS1(nsSyncJPAKE, nsISyncJPAKE)
+
+#endif // nsSyncJPAKE_h__
--- a/toolkit/library/libxul-config.mk
+++ b/toolkit/library/libxul-config.mk
@@ -345,16 +345,20 @@ DEFINES += -DMOZ_SPELLCHECK
 COMPONENT_LIBS += spellchecker
 endif
 
 ifdef MOZ_ZIPWRITER
 DEFINES += -DMOZ_ZIPWRITER
 COMPONENT_LIBS += zipwriter
 endif
 
+ifdef MOZ_SERVICES_SYNC
+COMPONENT_LIBS += services-crypto
+endif
+
 ifdef MOZ_DEBUG
 ifdef ENABLE_TESTS
 COMPONENT_LIBS += gkdebug
 endif
 endif
 
 ifdef MOZ_APP_COMPONENT_LIBS
 COMPONENT_LIBS += $(MOZ_APP_COMPONENT_LIBS)
--- a/toolkit/library/nsStaticXULComponents.cpp
+++ b/toolkit/library/nsStaticXULComponents.cpp
@@ -228,16 +228,22 @@
 #endif
 
 #if defined(BUILD_CTYPES)
 #define JSCTYPES_MODULE MODULE(jsctypes)
 #else
 #define JSCTYPES_MODULE
 #endif
 
+#ifdef MOZ_SERVICES_SYNC
+#define SERVICES_CRYPTO_MODULE MODULE(nsServicesCryptoModule)
+#else
+#define SERVICES_CRYPTO_MODULE
+#endif
+
 #if defined(MOZ_APP_COMPONENT_INCLUDE)
 #include MOZ_APP_COMPONENT_INCLUDE
 #else
 #define APP_COMPONENT_MODULES
 #endif
 
 #define XUL_MODULES                          \
     MODULE(nsUConvModule)                    \
@@ -283,16 +289,17 @@
     SYSTEMPREF_MODULES                       \
     SPELLCHECK_MODULE                        \
     LAYOUT_DEBUG_MODULE                      \
     UNIXPROXY_MODULE                         \
     OSXPROXY_MODULE                          \
     WINDOWSPROXY_MODULE                      \
     JSCTYPES_MODULE                          \
     MODULE(jsperf)                           \
+    SERVICES_CRYPTO_MODULE                   \
     APP_COMPONENT_MODULES                    \
     /* end of list */
 
 #define MODULE(_name) \
   NSMODULE_DECL(_name);
 
 XUL_MODULES
 
--- a/toolkit/toolkit-tiers.mk
+++ b/toolkit/toolkit-tiers.mk
@@ -230,16 +230,20 @@ tier_platform_dirs	+= security/manager
 else
 tier_platform_dirs	+= security/manager/boot/public security/manager/ssl/public
 endif
 
 ifdef MOZ_PREF_EXTENSIONS
 tier_platform_dirs += extensions/pref
 endif
 
+ifdef MOZ_SERVICES_SYNC
+tier_platform_dirs += services/crypto
+endif
+
 # JavaXPCOM JNI code is compiled into libXUL
 ifdef MOZ_JAVAXPCOM
 tier_platform_dirs += extensions/java/xpcom/src
 endif
 
 ifndef BUILD_STATIC_LIBS
 ifneq (,$(MOZ_ENABLE_GTK2))
 tier_platform_dirs += embedding/browser/gtk