Bug 1296767 part 1 - Remove J-PAKE from Sync. r?markh draft
authorEdouard Oger <eoger@fastmail.com>
Tue, 10 Jan 2017 13:47:24 -0500
changeset 467361 5c5b1abda02c2f3468dc03222ac2621b9ac54b75
parent 467360 39cce2d50c54e4cc156bdf69604e7d50863cee6e
child 467362 be5e97383698de0eb0d5fbba66510681e13a344e
push id43155
push userbmo:eoger@fastmail.com
push dateFri, 27 Jan 2017 18:31:15 +0000
reviewersmarkh
bugs1296767
milestone54.0a1
Bug 1296767 part 1 - Remove J-PAKE from Sync. r?markh MozReview-Commit-ID: iD4OJv436i
services/crypto/component/moz.build
services/crypto/component/nsISyncJPAKE.idl
services/crypto/component/nsSyncJPAKE.cpp
services/crypto/component/nsSyncJPAKE.h
services/crypto/component/tests/unit/test_jpake.js
services/crypto/component/tests/unit/xpcshell.ini
services/sync/modules/constants.js
services/sync/modules/jpakeclient.js
services/sync/modules/main.js
services/sync/modules/policies.js
services/sync/modules/status.js
services/sync/moz.build
services/sync/services-sync.js
services/sync/tests/unit/test_jpakeclient.js
services/sync/tests/unit/test_load_modules.js
services/sync/tests/unit/test_sendcredentials_controller.js
services/sync/tests/unit/xpcshell.ini
tools/lint/eslint/modules.json
--- a/services/crypto/component/moz.build
+++ b/services/crypto/component/moz.build
@@ -1,21 +1,17 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
-
 XPIDL_SOURCES += [
     'nsIIdentityCryptoService.idl',
-    'nsISyncJPAKE.idl',
 ]
 
 XPIDL_MODULE = 'services-crypto-component'
 
 SOURCES += [
     'IdentityCryptoService.cpp',
-    'nsSyncJPAKE.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
deleted file mode 100644
--- a/services/crypto/component/nsISyncJPAKE.idl
+++ /dev/null
@@ -1,103 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#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);
-};
deleted file mode 100644
--- a/services/crypto/component/nsSyncJPAKE.cpp
+++ /dev/null
@@ -1,484 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsSyncJPAKE.h"
-
-#include "base64.h"
-#include "keyhi.h"
-#include "mozilla/ModuleUtils.h"
-#include "mozilla/Move.h"
-#include "nsDebug.h"
-#include "nsError.h"
-#include "nsString.h"
-#include "nscore.h"
-#include "pk11pub.h"
-#include "pkcs11.h"
-#include "secerr.h"
-#include "secmodt.h"
-#include "secport.h"
-
-using mozilla::fallible;
-
-static bool
-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 false;
-    }
-  }
-  return true;
-}
-
-static bool
-fromHex(const char * str, unsigned char * p, size_t sLen)
-{
-  size_t i;
-  if (sLen & 1)
-    return false;
-
-  for (i = 0; i < sLen / 2; ++i) {
-    if (!hex_from_2char((const unsigned char *) str + (2*i),
-                        (unsigned char *) p + i)) {
-      return false;
-    }
-  }
-  return 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 bool
-toHexString(const unsigned char * str, unsigned len, nsACString & out)
-{
-  static const char digits[] = "0123456789ABCDEF";
-  if (!out.SetCapacity(2 * len, fallible))
-    return false;
-  out.SetLength(0);
-  for (unsigned i = 0; i < len; ++i) {
-    out.Append(digits[str[i] >> 4]);
-    out.Append(digits[str[i] & 0x0f]);
-  }
-  return 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,
-                                  nsACString & aGV1,
-                                  nsACString & aR1,
-                                  nsACString & aGX2,
-                                  nsACString & aGV2,
-                                  nsACString & aR2)
-{
-  nsNSSShutDownPreventionLock locker;
-  if (isAlreadyShutDown()) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  NS_ENSURE_STATE(round == JPAKENotStarted);
-  NS_ENSURE_STATE(key == nullptr);
-
-  static CK_MECHANISM_TYPE mechanisms[] = {
-    CKM_NSS_JPAKE_ROUND1_SHA256,
-    CKM_NSS_JPAKE_ROUND2_SHA256,
-    CKM_NSS_JPAKE_FINAL_SHA256
-  };
-
-  UniquePK11SlotInfo slot(PK11_GetBestSlotMultiple(mechanisms,
-                                                   NUM_ELEM(mechanisms),
-                                                   nullptr));
-  NS_ENSURE_STATE(slot != nullptr);
-    
-  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 = UniquePK11SymKey(
-    PK11_KeyGenWithTemplate(slot.get(), CKM_NSS_JPAKE_ROUND1_SHA256,
-                            CKM_NSS_JPAKE_ROUND1_SHA256, &paramsItem,
-                            keyTemplate, NUM_ELEM(keyTemplate), nullptr));
-  nsresult rv = key != nullptr
-              ? 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,
-                                  nsACString & aGVA,
-                                  nsACString & aRA)
-{
-  nsNSSShutDownPreventionLock locker;
-  if (isAlreadyShutDown()) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  NS_ENSURE_STATE(round == JPAKEBeforeRound2);
-  NS_ENSURE_STATE(key != nullptr);
-  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 */
-  bool foundNonZero = false;
-  for (size_t i = 0; i < aPIN.Length(); ++i) {
-    if (aPIN[i] != 0) {
-      foundNonZero = 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;
-
-  // Bug 629090: NSS 3.12.9 J-PAKE fails to check that gx^4 != 1, so check here.
-  bool gx4Good = false;
-  for (unsigned i = 0; i < rp.gx4.ulGXLen; ++i) {
-    if (rp.gx4.pGX[i] > 1 || (rp.gx4.pGX[i] != 0 && i < rp.gx4.ulGXLen - 1)) {
-      gx4Good = true;
-      break;
-    }
-  }
-  NS_ENSURE_ARG(gx4Good);
-
-  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 }
-  };
-  UniquePK11SymKey newKey(PK11_DeriveWithTemplate(key.get(),
-                                                  CKM_NSS_JPAKE_ROUND2_SHA256,
-                                                  &paramsItem,
-                                                  CKM_NSS_JPAKE_FINAL_SHA256,
-                                                  CKA_DERIVE, 0,
-                                                  keyTemplate,
-                                                  NUM_ELEM(keyTemplate),
-                                                  false));
-  if (newKey != nullptr) {
-    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;
-      key = Move(newKey);
-      return NS_OK;
-    } else {
-      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 != nullptr) {
-    size_t len = PORT_Strlen(base64);
-    if (out.SetCapacity(len, fallible)) {
-      out.SetLength(0);
-      out.Append(base64, len);
-    } else {
-      rv = NS_ERROR_OUT_OF_MEMORY;
-    }
-    PORT_Free((void*) base64);
-  } 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 != nullptr && value->data != nullptr && value->len > 0
-       ? setBase64(value->data, value->len, keyString)
-       : NS_ERROR_UNEXPECTED;
-  } else {
-    rv = mapErrno();
-  }
-  return rv;
-}
-
-static nsresult
-extractBase64KeyValue(UniquePK11SymKey & 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.get(), CKM_EXTRACT_KEY_FROM_KEY,
-                                 &paramsItem, destMech,
-                                 CKA_SIGN, keySize);
-  if (key == nullptr)
-    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,
-                                 nsACString & aHMAC256Key)
-{
-  nsNSSShutDownPreventionLock locker;
-  if (isAlreadyShutDown()) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  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 != nullptr);
-
-  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;
-  UniquePK11SymKey keyMaterial(PK11_Derive(key.get(), CKM_NSS_JPAKE_FINAL_SHA256,
-                                           &paramsItem, CKM_NSS_HKDF_SHA256,
-                                           CKA_DERIVE, 0));
-  UniquePK11SymKey keyBlock;
-
-  if (keyMaterial == nullptr)
-    rv = mapErrno();
-
-  if (rv == NS_OK) {
-    CK_NSS_HKDFParams hkdfParams;
-    hkdfParams.bExtract = CK_TRUE;
-    hkdfParams.pSalt = nullptr;
-    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 = UniquePK11SymKey(
-      PK11_Derive(keyMaterial.get(), CKM_NSS_HKDF_SHA256, &paramsItem,
-                  CKM_EXTRACT_KEY_FROM_KEY, CKA_DERIVE,
-                  AES256_KEY_SIZE + HMAC_SHA256_KEY_SIZE));
-    if (keyBlock == nullptr)
-      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.get());
-    NS_ENSURE_TRUE(srv == SECSuccess, NS_ERROR_UNEXPECTED);
-    SECItem * keyMaterialBytes = PK11_GetKeyData(keyMaterial.get());
-    NS_ENSURE_TRUE(keyMaterialBytes != nullptr, NS_ERROR_UNEXPECTED);
-  }
-
-  return rv;
-}
-
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsSyncJPAKE)
-NS_DEFINE_NAMED_CID(NS_SYNCJPAKE_CID);
-
-nsSyncJPAKE::nsSyncJPAKE() : round(JPAKENotStarted), key(nullptr) { }
-
-nsSyncJPAKE::~nsSyncJPAKE()
-{
-  nsNSSShutDownPreventionLock locker;
-  if (isAlreadyShutDown()) {
-    return;
-  }
-  destructorSafeDestroyNSSReference();
-  shutdown(ShutdownCalledFrom::Object);
-}
-
-void
-nsSyncJPAKE::virtualDestroyNSSReference()
-{
-  destructorSafeDestroyNSSReference();
-}
-
-void
-nsSyncJPAKE::destructorSafeDestroyNSSReference()
-{
-  key = nullptr;
-}
-
-static const mozilla::Module::CIDEntry kServicesCryptoCIDs[] = {
-  { &kNS_SYNCJPAKE_CID, false, nullptr, nsSyncJPAKEConstructor },
-  { nullptr }
-};
-
-static const mozilla::Module::ContractIDEntry kServicesCryptoContracts[] = {
-  { NS_SYNCJPAKE_CONTRACTID, &kNS_SYNCJPAKE_CID },
-  { nullptr }
-};
-
-static const mozilla::Module kServicesCryptoModule = {
-  mozilla::Module::kVersion,
-  kServicesCryptoCIDs,
-  kServicesCryptoContracts
-};
-
-NSMODULE_DEFN(nsServicesCryptoModule) = &kServicesCryptoModule;
deleted file mode 100644
--- a/services/crypto/component/nsSyncJPAKE.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#ifndef nsSyncJPAKE_h__
-#define nsSyncJPAKE_h__
-
-#include "ScopedNSSTypes.h"
-#include "nsISyncJPAKE.h"
-#include "nsNSSShutDown.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}}
-
-using namespace mozilla;
-
-class nsSyncJPAKE : public nsISyncJPAKE
-                  , public nsNSSShutDownObject
-{
-public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSISYNCJPAKE
-  nsSyncJPAKE();
-protected:
-  virtual ~nsSyncJPAKE();
-private:
-  virtual void virtualDestroyNSSReference() override;
-  void destructorSafeDestroyNSSReference();
-
-  enum { JPAKENotStarted, JPAKEBeforeRound2, JPAKEAfterRound2 } round;
-  UniquePK11SymKey key;
-};
-
-NS_IMPL_ISUPPORTS(nsSyncJPAKE, nsISyncJPAKE)
-
-#endif // nsSyncJPAKE_h__
deleted file mode 100644
--- a/services/crypto/component/tests/unit/test_jpake.js
+++ /dev/null
@@ -1,289 +0,0 @@
-var Cc = Components.classes;
-var Ci = Components.interfaces;
-
-// Ensure PSM is initialized.
-Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
-
-function do_check_throws(func) {
-  let have_error = false;
-  try {
-    func();
-  } catch (ex) {
-    dump("Was expecting an exception. Caught: " + ex + "\n");
-    have_error = true;
-  }
-  do_check_true(have_error);
-}
-
-function test_success() {
-  let a = Cc["@mozilla.org/services-crypto/sync-jpake;1"]
-            .createInstance(Ci.nsISyncJPAKE);
-  let b = Cc["@mozilla.org/services-crypto/sync-jpake;1"]
-            .createInstance(Ci.nsISyncJPAKE);
-
-  let a_gx1 = {};
-  let a_gv1 = {};
-  let a_r1 = {};
-  let a_gx2 = {};
-  let a_gv2 = {};
-  let a_r2 = {};
-
-  let b_gx1 = {};
-  let b_gv1 = {};
-  let b_r1 = {};
-  let b_gx2 = {};
-  let b_gv2 = {};
-  let b_r2 = {};
-
-  a.round1("alice", a_gx1, a_gv1, a_r1, a_gx2, a_gv2, a_r2);
-  b.round1("bob", b_gx1, b_gv1, b_r1, b_gx2, b_gv2, b_r2);
-
-  let a_A = {};
-  let a_gva = {};
-  let a_ra = {};
-
-  let b_A = {};
-  let b_gva = {};
-  let b_ra = {};
-
-  a.round2("bob", "sekrit", b_gx1.value, b_gv1.value, b_r1.value,
-           b_gx2.value, b_gv2.value, b_r2.value, a_A, a_gva, a_ra);
-  b.round2("alice", "sekrit", a_gx1.value, a_gv1.value, a_r1.value,
-           a_gx2.value, a_gv2.value, a_r2.value, b_A, b_gva, b_ra);
-
-  let a_aes = {};
-  let a_hmac = {};
-  let b_aes = {};
-  let b_hmac = {};
-
-  a.final(b_A.value, b_gva.value, b_ra.value, "ohai", a_aes, a_hmac);
-  b.final(a_A.value, a_gva.value, a_ra.value, "ohai", b_aes, b_hmac);
-
-  do_check_eq(a_aes.value, b_aes.value);
-  do_check_eq(a_hmac.value, b_hmac.value);
-}
-
-function test_failure(modlen) {
-  let a = Cc["@mozilla.org/services-crypto/sync-jpake;1"]
-            .createInstance(Ci.nsISyncJPAKE);
-  let b = Cc["@mozilla.org/services-crypto/sync-jpake;1"]
-            .createInstance(Ci.nsISyncJPAKE);
-
-  let a_gx1 = {};
-  let a_gv1 = {};
-  let a_r1 = {};
-  let a_gx2 = {};
-  let a_gv2 = {};
-  let a_r2 = {};
-
-  let b_gx1 = {};
-  let b_gv1 = {};
-  let b_r1 = {};
-  let b_gx2 = {};
-  let b_gv2 = {};
-  let b_r2 = {};
-
-  a.round1("alice", a_gx1, a_gv1, a_r1, a_gx2, a_gv2, a_r2);
-  b.round1("bob", b_gx1, b_gv1, b_r1, b_gx2, b_gv2, b_r2);
-
-  let a_A = {};
-  let a_gva = {};
-  let a_ra = {};
-
-  let b_A = {};
-  let b_gva = {};
-  let b_ra = {};
-
-  // Note how the PINs are different (secret vs. sekrit)
-  a.round2("bob", "secret", b_gx1.value, b_gv1.value, b_r1.value,
-           b_gx2.value, b_gv2.value, b_r2.value, a_A, a_gva, a_ra);
-  b.round2("alice", "sekrit", a_gx1.value, a_gv1.value, a_r1.value,
-           a_gx2.value, a_gv2.value, a_r2.value, b_A, b_gva, b_ra);
-
-  let a_aes = {};
-  let a_hmac = {};
-  let b_aes = {};
-  let b_hmac = {};
-
-  a.final(b_A.value, b_gva.value, b_ra.value, "ohai", a_aes, a_hmac);
-  b.final(a_A.value, a_gva.value, a_ra.value, "ohai", b_aes, b_hmac);
-
-  do_check_neq(a_aes.value, b_aes.value);
-  do_check_neq(a_hmac.value, b_hmac.value);
-}
-
-function test_same_signerids() {
-  let a = Cc["@mozilla.org/services-crypto/sync-jpake;1"]
-            .createInstance(Ci.nsISyncJPAKE);
-  let b = Cc["@mozilla.org/services-crypto/sync-jpake;1"]
-            .createInstance(Ci.nsISyncJPAKE);
-
-  let gx1 = {};
-  let gv1 = {};
-  let r1 = {};
-  let gx2 = {};
-  let gv2 = {};
-  let r2 = {};
-
-  a.round1("alice", {}, {}, {}, {}, {}, {});
-  b.round1("alice", gx1, gv1, r1, gx2, gv2, r2);
-  do_check_throws(function() {
-    a.round2("alice", "sekrit", gx1.value, gv1.value, r1.value,
-             gx2.value, gv2.value, r2.value, {}, {}, {});
-  });
-}
-
-function test_bad_zkp() {
-  let a = Cc["@mozilla.org/services-crypto/sync-jpake;1"]
-            .createInstance(Ci.nsISyncJPAKE);
-  let b = Cc["@mozilla.org/services-crypto/sync-jpake;1"]
-            .createInstance(Ci.nsISyncJPAKE);
-
-  let gx1 = {};
-  let gv1 = {};
-  let r1 = {};
-  let gx2 = {};
-  let gv2 = {};
-  let r2 = {};
-
-  a.round1("alice", {}, {}, {}, {}, {}, {});
-  b.round1("bob", gx1, gv1, r1, gx2, gv2, r2);
-  do_check_throws(function() {
-    a.round2("invalid", "sekrit", gx1.value, gv1.value, r1.value,
-             gx2.value, gv2.value, r2.value, {}, {}, {});
-  });
-}
-
-function test_x4_zero() {
-  // The PKCS#11 API for J-PAKE does not allow us to choose any of the nonces.
-  // In order to test the defence against x4 (mod p) == 1, we had to generate
-  // our own signed nonces using a the FreeBL JPAKE_Sign function directly.
-  // To verify the signatures are accurate, pass the given value of R as the
-  // "testRandom" parameter to FreeBL's JPAKE_Sign, along with the given values
-  // for X and GX, using signerID "alice". Then verify that each GV returned
-  // from JPAKE_Sign matches the value specified here.
-  let test = function(badGX, badX_GV, badX_R) {
-    let a = Cc["@mozilla.org/services-crypto/sync-jpake;1"]
-            .createInstance(Ci.nsISyncJPAKE);
-    let b = Cc["@mozilla.org/services-crypto/sync-jpake;1"]
-            .createInstance(Ci.nsISyncJPAKE);
-
-    let a_gx1 = {};
-    let a_gv1 = {};
-    let a_r1 = {};
-    let a_gx2 = {};
-    let a_gv2 = {};
-    let a_r2 = {};
-
-    let b_gx1 = {};
-    let b_gv1 = {};
-    let b_r1 = {};
-    let b_gx2 = {};
-    let b_gv2 = {};
-    let b_r2 = {};
-
-    a.round1("alice", a_gx1, a_gv1, a_r1, a_gx2, a_gv2, a_r2);
-    b.round1("bob", b_gx1, b_gv1, b_r1, b_gx2, b_gv2, b_r2);
-
-    // Replace the g^x2 generated by A with the given illegal value.
-    a_gx2.value = badGX;
-    a_gv2.value = badX_GV;
-    a_r2.value = badX_R;
-
-    let b_A = {};
-    let b_gva = {};
-    let b_ra = {};
-
-    do_check_throws(function() {
-        b.round2("alice", "secret", a_gx1.value, a_gv1.value, a_r1.value,
-                    a_gx2.value, a_gv2.value, a_r2.value, b_A, b_gva, b_ra);
-    });
-  };
-
-  // g^x is NIST 3072's p + 1, (p + 1) mod p == 1, x == 0
-  test("90066455B5CFC38F9CAA4A48B4281F292C260FEEF01FD61037E56258A7795A1C"
-         + "7AD46076982CE6BB956936C6AB4DCFE05E6784586940CA544B9B2140E1EB523F"
-         + "009D20A7E7880E4E5BFA690F1B9004A27811CD9904AF70420EEFD6EA11EF7DA1"
-         + "29F58835FF56B89FAA637BC9AC2EFAAB903402229F491D8D3485261CD068699B"
-         + "6BA58A1DDBBEF6DB51E8FE34E8A78E542D7BA351C21EA8D8F1D29F5D5D159394"
-         + "87E27F4416B0CA632C59EFD1B1EB66511A5A0FBF615B766C5862D0BD8A3FE7A0"
-         + "E0DA0FB2FE1FCB19E8F9996A8EA0FCCDE538175238FC8B0EE6F29AF7F642773E"
-         + "BE8CD5402415A01451A840476B2FCEB0E388D30D4B376C37FE401C2A2C2F941D"
-         + "AD179C540C1C8CE030D460C4D983BE9AB0B20F69144C1AE13F9383EA1C08504F"
-         + "B0BF321503EFE43488310DD8DC77EC5B8349B8BFE97C2C560EA878DE87C11E3D"
-         + "597F1FEA742D73EEC7F37BE43949EF1A0D15C3F3E3FC0A8335617055AC91328E"
-         + "C22B50FC15B941D3D1624CD88BC25F3E941FDDC6200689581BFEC416B4B2CB74",
-       "5386107A0DD4A96ECF8D9BCF864BDE23AAEF13351F5550D777A32C1FEC165ED67AE51"
-         + "66C3876AABC1FED1A0993754F3AEE256530F529548F8FE010BC0D070175569845"
-         + "CF009AD24BC897A9CA1F18E1A9CE421DD54FD93AB528BC2594B47791713165276"
-         + "7B76903190C3DCD2076FEC1E61FFFC32D1B07273B06EA2889E66FCBFD41FE8984"
-         + "5FCE36056B09D1F20E58BB6BAA07A32796F11998BEF0AB3D387E2FB4FE3073FEB"
-         + "634BA91709010A70DA29C06F8F92D638C4F158680EAFEB5E0E323BD7DACB671C0"
-         + "BA3EDEEAB5CAA243CABAB28E7205AC9A0AAEAFE132635DAC7FE001C19F880A96E"
-         + "395C42536D694F81B4F44DC66D7D6FBE933C56ABF585837291D8751C18EB1F3FB"
-         + "620582E6A7B795D699E38C270863A289583CB9D07651E6BA3B82BC656B49BD09B"
-         + "6B8C27F370120C7CB89D0829BE51D56356EA836012E9204FF4D1CA8B1B7F9C768"
-         + "4BB2B0F226FD4042EEBAD931FDBD4F81F8425B305752F5E37FFA2B73BB5A034EC"
-         + "7EEF5AAC92EA212897E3A2B8961D2147710ECCE127B942AB2",
-       "05CC4DF005FE006C11111624E14806E4A904A4D1D6A53E795AC7867A960CD4FD");
-
-  // x == 0 implies g^x == 1
-  test("01",
-       "488759644532FA7C53E5239F2A365D4B9189582BDD2967A1852FE56568382B65"
-         + "C66BDFCD9B581EAEF4BB497CAF1290ECDFA47A1D1658DC5DC9248D9A4135"
-         + "DC70B6A8497CDF117236841FA18500DC696A92EEF5000ABE68E9C75B37BC"
-         + "6A722126BE728163AA90A6B03D5585994D3403557EEF08E819C72D143BBC"
-         + "CDF74559645066CB3607E1B0430365356389FC8FB3D66FD2B6E2E834EC23"
-         + "0B0234956752D07F983C918488C8E5A124B062D50B44C5E6FB36BCB03E39"
-         + "0385B17CF8062B6688371E6AF5915C2B1AAA31C9294943CC6DC1B994FC09"
-         + "49CA31828B83F3D6DFB081B26045DFD9F10092588B63F1D6E68881A06522"
-         + "5A417CA9555B036DE89D349AC794A43EB28FE320F9A321F06A9364C88B54"
-         + "99EEF4816375B119824ACC9AA56D1340B6A49D05F855DE699B351012028C"
-         + "CA43001F708CC61E71CA3849935BEEBABC0D268CD41B8D2B8DCA705FDFF8"
-         + "1DAA772DA96EDEA0B291FD5C0C1B8EFE5318D37EBC1BFF53A9DDEC4171A6"
-         + "479E341438970058E25C8F2BCDA6166C8BF1B065C174",
-       "8B2BACE575179D762F6F2FFDBFF00B497C07766AB3EED9961447CF6F43D06A97");
-}
-
-function test_invalid_input_round2() {
-  let a = Cc["@mozilla.org/services-crypto/sync-jpake;1"]
-            .createInstance(Ci.nsISyncJPAKE);
-
-  a.round1("alice", {}, {}, {}, {}, {}, {});
-  do_check_throws(function() {
-    a.round2("invalid", "sekrit", "some", "real", "garbage",
-             "even", "more", "garbage", {}, {}, {});
-  });
-}
-
-function test_invalid_input_final() {
-  let a = Cc["@mozilla.org/services-crypto/sync-jpake;1"]
-            .createInstance(Ci.nsISyncJPAKE);
-  let b = Cc["@mozilla.org/services-crypto/sync-jpake;1"]
-            .createInstance(Ci.nsISyncJPAKE);
-
-  let gx1 = {};
-  let gv1 = {};
-  let r1 = {};
-  let gx2 = {};
-  let gv2 = {};
-  let r2 = {};
-
-  a.round1("alice", {}, {}, {}, {}, {}, {});
-  b.round1("bob", gx1, gv1, r1, gx2, gv2, r2);
-  a.round2("bob", "sekrit", gx1.value, gv1.value, r1.value,
-           gx2.value, gv2.value, r2.value, {}, {}, {});
-  do_check_throws(function() {
-    a.final("some", "garbage", "alright", "foobar-info", {}, {});
-  });
-}
-
-function run_test() {
-  test_x4_zero();
-  test_success();
-  test_failure();
-  test_same_signerids();
-  test_bad_zkp();
-  test_invalid_input_round2();
-  test_invalid_input_final();
-}
deleted file mode 100644
--- a/services/crypto/component/tests/unit/xpcshell.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[DEFAULT]
-head = 
-firefox-appdir = browser
-
-[test_jpake.js]
--- a/services/sync/modules/constants.js
+++ b/services/sync/modules/constants.js
@@ -119,17 +119,16 @@ MASTER_PASSWORD_LOCKED:                "
 
 // success states
 LOGIN_SUCCEEDED:                       "success.login",
 SYNC_SUCCEEDED:                        "success.sync",
 ENGINE_SUCCEEDED:                      "success.engine",
 
 // login failure status codes:
 LOGIN_FAILED_NO_USERNAME:              "error.login.reason.no_username",
-LOGIN_FAILED_NO_PASSWORD:              "error.login.reason.no_password2",
 LOGIN_FAILED_NO_PASSPHRASE:            "error.login.reason.no_recoverykey",
 LOGIN_FAILED_NETWORK_ERROR:            "error.login.reason.network",
 LOGIN_FAILED_SERVER_ERROR:             "error.login.reason.server",
 LOGIN_FAILED_INVALID_PASSPHRASE:       "error.login.reason.recoverykey",
 LOGIN_FAILED_LOGIN_REJECTED:           "error.login.reason.account",
 
 // sync failure status codes
 METARECORD_DOWNLOAD_FAIL:              "error.sync.reason.metarecord_download_fail",
@@ -150,28 +149,16 @@ ENGINE_UPLOAD_FAIL:                    "
 ENGINE_DOWNLOAD_FAIL:                  "error.engine.reason.record_download_fail",
 ENGINE_UNKNOWN_FAIL:                   "error.engine.reason.unknown_fail",
 ENGINE_APPLY_FAIL:                     "error.engine.reason.apply_fail",
 ENGINE_METARECORD_DOWNLOAD_FAIL:       "error.engine.reason.metarecord_download_fail",
 ENGINE_METARECORD_UPLOAD_FAIL:         "error.engine.reason.metarecord_upload_fail",
 // an upload failure where the batch was interrupted with a 412
 ENGINE_BATCH_INTERRUPTED:              "error.engine.reason.batch_interrupted",
 
-JPAKE_ERROR_CHANNEL:                   "jpake.error.channel",
-JPAKE_ERROR_NETWORK:                   "jpake.error.network",
-JPAKE_ERROR_SERVER:                    "jpake.error.server",
-JPAKE_ERROR_TIMEOUT:                   "jpake.error.timeout",
-JPAKE_ERROR_INTERNAL:                  "jpake.error.internal",
-JPAKE_ERROR_INVALID:                   "jpake.error.invalid",
-JPAKE_ERROR_NODATA:                    "jpake.error.nodata",
-JPAKE_ERROR_KEYMISMATCH:               "jpake.error.keymismatch",
-JPAKE_ERROR_WRONGMESSAGE:              "jpake.error.wrongmessage",
-JPAKE_ERROR_USERABORT:                 "jpake.error.userabort",
-JPAKE_ERROR_DELAYUNSUPPORTED:          "jpake.error.delayunsupported",
-
 // info types for Service.getStorageInfo
 INFO_COLLECTIONS:                      "collections",
 INFO_COLLECTION_USAGE:                 "collection_usage",
 INFO_COLLECTION_COUNTS:                "collection_counts",
 INFO_QUOTA:                            "quota",
 
 // Ways that a sync can be disabled (messages only to be printed in debug log)
 kSyncMasterPasswordLocked:             "User elected to leave Master Password locked",
deleted file mode 100644
--- a/services/sync/modules/jpakeclient.js
+++ /dev/null
@@ -1,773 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-this.EXPORTED_SYMBOLS = ["JPAKEClient", "SendCredentialsController"];
-
-var {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
-
-Cu.import("resource://gre/modules/Log.jsm");
-Cu.import("resource://services-common/rest.js");
-Cu.import("resource://services-sync/constants.js");
-Cu.import("resource://services-sync/util.js");
-
-const REQUEST_TIMEOUT         = 60; // 1 minute
-const KEYEXCHANGE_VERSION     = 3;
-
-const JPAKE_SIGNERID_SENDER   = "sender";
-const JPAKE_SIGNERID_RECEIVER = "receiver";
-const JPAKE_LENGTH_SECRET     = 8;
-const JPAKE_LENGTH_CLIENTID   = 256;
-const JPAKE_VERIFY_VALUE      = "0123456789ABCDEF";
-
-
-/**
- * Client to exchange encrypted data using the J-PAKE algorithm.
- * The exchange between two clients of this type looks like this:
- *
- *
- *  Mobile                        Server                        Desktop
- *  ===================================================================
- *                                   |
- *  retrieve channel <---------------|
- *  generate random secret           |
- *  show PIN = secret + channel      |                 ask user for PIN
- *  upload Mobile's message 1 ------>|
- *                                   |----> retrieve Mobile's message 1
- *                                   |<----- upload Desktop's message 1
- *  retrieve Desktop's message 1 <---|
- *  upload Mobile's message 2 ------>|
- *                                   |----> retrieve Mobile's message 2
- *                                   |                      compute key
- *                                   |<----- upload Desktop's message 2
- *  retrieve Desktop's message 2 <---|
- *  compute key                      |
- *  encrypt known value ------------>|
- *                                   |-------> retrieve encrypted value
- *                                   | verify against local known value
- *
- *   At this point Desktop knows whether the PIN was entered correctly.
- *   If it wasn't, Desktop deletes the session. If it was, the account
- *   setup can proceed. If Desktop doesn't yet have an account set up,
- *   it will keep the channel open and let the user connect to or
- *   create an account.
- *
- *                                   |              encrypt credentials
- *                                   |<------------- upload credentials
- *  retrieve credentials <-----------|
- *  verify HMAC                      |
- *  decrypt credentials              |
- *  delete session ----------------->|
- *  start syncing                    |
- *
- *
- * Create a client object like so:
- *
- *   let client = new JPAKEClient(controller);
- *
- * The 'controller' object must implement the following methods:
- *
- *   displayPIN(pin) -- Called when a PIN has been generated and is ready to
- *     be displayed to the user. Only called on the client where the pairing
- *     was initiated with 'receiveNoPIN()'.
- *
- *   onPairingStart() -- Called when the pairing has started and messages are
- *     being sent back and forth over the channel. Only called on the client
- *     where the pairing was initiated with 'receiveNoPIN()'.
- *
- *   onPaired() -- Called when the device pairing has been established and
- *     we're ready to send the credentials over. To do that, the controller
- *     must call 'sendAndComplete()' while the channel is active.
- *
- *   onComplete(data) -- Called after transfer has been completed. On
- *     the sending side this is called with no parameter and as soon as the
- *     data has been uploaded. This does not mean the receiving side has
- *     actually retrieved them yet.
- *
- *   onAbort(error) -- Called whenever an error is encountered. All errors lead
- *     to an abort and the process has to be started again on both sides.
- *
- * To start the data transfer on the receiving side, call
- *
- *   client.receiveNoPIN();
- *
- * This will allocate a new channel on the server, generate a PIN, have it
- * displayed and then do the transfer once the protocol has been completed
- * with the sending side.
- *
- * To initiate the transfer from the sending side, call
- *
- *   client.pairWithPIN(pin, true);
- *
- * Once the pairing has been established, the controller's 'onPaired()' method
- * will be called. To then transmit the data, call
- *
- *   client.sendAndComplete(data);
- *
- * To abort the process, call
- *
- *   client.abort();
- *
- * Note that after completion or abort, the 'client' instance may not be reused.
- * You will have to create a new one in case you'd like to restart the process.
- */
-this.JPAKEClient = function JPAKEClient(controller) {
-  this.controller = controller;
-
-  this._log = Log.repository.getLogger("Sync.JPAKEClient");
-  this._log.level = Log.Level[Svc.Prefs.get(
-    "log.logger.service.jpakeclient", "Debug")];
-
-  this._serverURL = Svc.Prefs.get("jpake.serverURL");
-  this._pollInterval = Svc.Prefs.get("jpake.pollInterval");
-  this._maxTries = Svc.Prefs.get("jpake.maxTries");
-  if (this._serverURL.slice(-1) != "/") {
-    this._serverURL += "/";
-  }
-
-  this._jpake = Cc["@mozilla.org/services-crypto/sync-jpake;1"]
-                  .createInstance(Ci.nsISyncJPAKE);
-
-  this._setClientID();
-}
-JPAKEClient.prototype = {
-
-  _chain: Async.chain,
-
-  /*
-   * Public API
-   */
-
-  /**
-   * Initiate pairing and receive data without providing a PIN. The PIN will
-   * be generated and passed on to the controller to be displayed to the user.
-   *
-   * This is typically called on mobile devices where typing is tedious.
-   */
-  receiveNoPIN: function receiveNoPIN() {
-    this._my_signerid = JPAKE_SIGNERID_RECEIVER;
-    this._their_signerid = JPAKE_SIGNERID_SENDER;
-
-    this._secret = this._createSecret();
-
-    // Allow a large number of tries first while we wait for the PIN
-    // to be entered on the other device.
-    this._maxTries = Svc.Prefs.get("jpake.firstMsgMaxTries");
-    this._chain(this._getChannel,
-                this._computeStepOne,
-                this._putStep,
-                this._getStep,
-                function(callback) {
-                  // We fetched the first response from the other client.
-                  // Notify controller of the pairing starting.
-                  Utils.nextTick(this.controller.onPairingStart,
-                                 this.controller);
-
-                  // Now we can switch back to the smaller timeout.
-                  this._maxTries = Svc.Prefs.get("jpake.maxTries");
-                  callback();
-                },
-                this._computeStepTwo,
-                this._putStep,
-                this._getStep,
-                this._computeFinal,
-                this._computeKeyVerification,
-                this._putStep,
-                function(callback) {
-                  // Allow longer time-out for the last message.
-                  this._maxTries = Svc.Prefs.get("jpake.lastMsgMaxTries");
-                  callback();
-                },
-                this._getStep,
-                this._decryptData,
-                this._complete)();
-  },
-
-  /**
-   * Initiate pairing based on the PIN entered by the user.
-   *
-   * This is typically called on desktop devices where typing is easier than
-   * on mobile.
-   *
-   * @param pin
-   *        12 character string (in human-friendly base32) containing the PIN
-   *        entered by the user.
-   * @param expectDelay
-   *        Flag that indicates that a significant delay between the pairing
-   *        and the sending should be expected. v2 and earlier of the protocol
-   *        did not allow for this and the pairing to a v2 or earlier client
-   *        will be aborted if this flag is 'true'.
-   */
-  pairWithPIN: function pairWithPIN(pin, expectDelay) {
-    this._my_signerid = JPAKE_SIGNERID_SENDER;
-    this._their_signerid = JPAKE_SIGNERID_RECEIVER;
-
-    this._channel = pin.slice(JPAKE_LENGTH_SECRET);
-    this._channelURL = this._serverURL + this._channel;
-    this._secret = pin.slice(0, JPAKE_LENGTH_SECRET);
-
-    this._chain(this._computeStepOne,
-                this._getStep,
-                function(callback) {
-                  // Ensure that the other client can deal with a delay for
-                  // the last message if that's requested by the caller.
-                  if (!expectDelay) {
-                    return callback();
-                  }
-                  if (!this._incoming.version || this._incoming.version < 3) {
-                    return this.abort(JPAKE_ERROR_DELAYUNSUPPORTED);
-                  }
-                  return callback();
-                },
-                this._putStep,
-                this._computeStepTwo,
-                this._getStep,
-                this._putStep,
-                this._computeFinal,
-                this._getStep,
-                this._verifyPairing)();
-  },
-
-  /**
-   * Send data after a successful pairing.
-   *
-   * @param obj
-   *        Object containing the data to send. It will be serialized as JSON.
-   */
-  sendAndComplete: function sendAndComplete(obj) {
-    if (!this._paired || this._finished) {
-      this._log.error("Can't send data, no active pairing!");
-      throw "No active pairing!";
-    }
-    this._data = JSON.stringify(obj);
-    this._chain(this._encryptData,
-                this._putStep,
-                this._complete)();
-  },
-
-  /**
-   * Abort the current pairing. The channel on the server will be deleted
-   * if the abort wasn't due to a network or server error. The controller's
-   * 'onAbort()' method is notified in all cases.
-   *
-   * @param error [optional]
-   *        Error constant indicating the reason for the abort. Defaults to
-   *        user abort.
-   */
-  abort: function abort(error) {
-    this._log.debug("Aborting...");
-    this._finished = true;
-    let self = this;
-
-    // Default to "user aborted".
-    if (!error) {
-      error = JPAKE_ERROR_USERABORT;
-    }
-
-    if (error == JPAKE_ERROR_CHANNEL ||
-        error == JPAKE_ERROR_NETWORK ||
-        error == JPAKE_ERROR_NODATA) {
-      Utils.nextTick(function() { this.controller.onAbort(error); }, this);
-    } else {
-      this._reportFailure(error, function() { self.controller.onAbort(error); });
-    }
-  },
-
-  /*
-   * Utilities
-   */
-
-  _setClientID: function _setClientID() {
-    let rng = Cc["@mozilla.org/security/random-generator;1"]
-                .createInstance(Ci.nsIRandomGenerator);
-    let bytes = rng.generateRandomBytes(JPAKE_LENGTH_CLIENTID / 2);
-    this._clientID = bytes.map(byte => ("0" + byte.toString(16)).slice(-2)).join("");
-  },
-
-  _createSecret: function _createSecret() {
-    // 0-9a-z without 1,l,o,0
-    const key = "23456789abcdefghijkmnpqrstuvwxyz";
-    let rng = Cc["@mozilla.org/security/random-generator;1"]
-                .createInstance(Ci.nsIRandomGenerator);
-    let bytes = rng.generateRandomBytes(JPAKE_LENGTH_SECRET);
-    return bytes.map(byte => key[Math.floor(byte * key.length / 256)]).join("");
-  },
-
-  _newRequest: function _newRequest(uri) {
-    let request = new RESTRequest(uri);
-    request.setHeader("X-KeyExchange-Id", this._clientID);
-    request.timeout = REQUEST_TIMEOUT;
-    return request;
-  },
-
-  /*
-   * Steps of J-PAKE procedure
-   */
-
-  _getChannel: function _getChannel(callback) {
-    this._log.trace("Requesting channel.");
-    let request = this._newRequest(this._serverURL + "new_channel");
-    request.get(Utils.bind2(this, function handleChannel(error) {
-      if (this._finished) {
-        return;
-      }
-
-      if (error) {
-        this._log.error("Error acquiring channel ID. " + error);
-        this.abort(JPAKE_ERROR_CHANNEL);
-        return;
-      }
-      if (request.response.status != 200) {
-        this._log.error("Error acquiring channel ID. Server responded with HTTP "
-                        + request.response.status);
-        this.abort(JPAKE_ERROR_CHANNEL);
-        return;
-      }
-
-      try {
-        this._channel = JSON.parse(request.response.body);
-      } catch (ex) {
-        this._log.error("Server responded with invalid JSON.");
-        this.abort(JPAKE_ERROR_CHANNEL);
-        return;
-      }
-      this._log.debug("Using channel " + this._channel);
-      this._channelURL = this._serverURL + this._channel;
-
-      // Don't block on UI code.
-      let pin = this._secret + this._channel;
-      Utils.nextTick(function() { this.controller.displayPIN(pin); }, this);
-      callback();
-    }));
-  },
-
-  // Generic handler for uploading data.
-  _putStep: function _putStep(callback) {
-    this._log.trace("Uploading message " + this._outgoing.type);
-    let request = this._newRequest(this._channelURL);
-    if (this._their_etag) {
-      request.setHeader("If-Match", this._their_etag);
-    } else {
-      request.setHeader("If-None-Match", "*");
-    }
-    request.put(this._outgoing, Utils.bind2(this, function(error) {
-      if (this._finished) {
-        return;
-      }
-
-      if (error) {
-        this._log.error("Error uploading data. " + error);
-        this.abort(JPAKE_ERROR_NETWORK);
-        return;
-      }
-      if (request.response.status != 200) {
-        this._log.error("Could not upload data. Server responded with HTTP "
-                        + request.response.status);
-        this.abort(JPAKE_ERROR_SERVER);
-        return;
-      }
-      // There's no point in returning early here since the next step will
-      // always be a GET so let's pause for twice the poll interval.
-      this._my_etag = request.response.headers["etag"];
-      Utils.namedTimer(function() { callback(); }, this._pollInterval * 2,
-                       this, "_pollTimer");
-    }));
-  },
-
-  // Generic handler for polling for and retrieving data.
-  _pollTries: 0,
-  _getStep: function _getStep(callback) {
-    this._log.trace("Retrieving next message.");
-    let request = this._newRequest(this._channelURL);
-    if (this._my_etag) {
-      request.setHeader("If-None-Match", this._my_etag);
-    }
-
-    request.get(Utils.bind2(this, function(error) {
-      if (this._finished) {
-        return;
-      }
-
-      if (error) {
-        this._log.error("Error fetching data. " + error);
-        this.abort(JPAKE_ERROR_NETWORK);
-        return;
-      }
-
-      if (request.response.status == 304) {
-        this._log.trace("Channel hasn't been updated yet. Will try again later.");
-        if (this._pollTries >= this._maxTries) {
-          this._log.error("Tried for " + this._pollTries + " times, aborting.");
-          this.abort(JPAKE_ERROR_TIMEOUT);
-          return;
-        }
-        this._pollTries += 1;
-        Utils.namedTimer(function() { this._getStep(callback); },
-                         this._pollInterval, this, "_pollTimer");
-        return;
-      }
-      this._pollTries = 0;
-
-      if (request.response.status == 404) {
-        this._log.error("No data found in the channel.");
-        this.abort(JPAKE_ERROR_NODATA);
-        return;
-      }
-      if (request.response.status != 200) {
-        this._log.error("Could not retrieve data. Server responded with HTTP "
-                        + request.response.status);
-        this.abort(JPAKE_ERROR_SERVER);
-        return;
-      }
-
-      this._their_etag = request.response.headers["etag"];
-      if (!this._their_etag) {
-        this._log.error("Server did not supply ETag for message: "
-                        + request.response.body);
-        this.abort(JPAKE_ERROR_SERVER);
-        return;
-      }
-
-      try {
-        this._incoming = JSON.parse(request.response.body);
-      } catch (ex) {
-        this._log.error("Server responded with invalid JSON.");
-        this.abort(JPAKE_ERROR_INVALID);
-        return;
-      }
-      this._log.trace("Fetched message " + this._incoming.type);
-      callback();
-    }));
-  },
-
-  _reportFailure: function _reportFailure(reason, callback) {
-    this._log.debug("Reporting failure to server.");
-    let request = this._newRequest(this._serverURL + "report");
-    request.setHeader("X-KeyExchange-Cid", this._channel);
-    request.setHeader("X-KeyExchange-Log", reason);
-    request.post("", Utils.bind2(this, function(error) {
-      if (error) {
-        this._log.warn("Report failed: " + error);
-      } else if (request.response.status != 200) {
-        this._log.warn("Report failed. Server responded with HTTP "
-                       + request.response.status);
-      }
-
-      // Do not block on errors, we're done or aborted by now anyway.
-      callback();
-    }));
-  },
-
-  _computeStepOne: function _computeStepOne(callback) {
-    this._log.trace("Computing round 1.");
-    let gx1 = {};
-    let gv1 = {};
-    let r1 = {};
-    let gx2 = {};
-    let gv2 = {};
-    let r2 = {};
-    try {
-      this._jpake.round1(this._my_signerid, gx1, gv1, r1, gx2, gv2, r2);
-    } catch (ex) {
-      this._log.error("JPAKE round 1 threw: " + ex);
-      this.abort(JPAKE_ERROR_INTERNAL);
-      return;
-    }
-    let one = {gx1: gx1.value,
-               gx2: gx2.value,
-               zkp_x1: {gr: gv1.value, b: r1.value, id: this._my_signerid},
-               zkp_x2: {gr: gv2.value, b: r2.value, id: this._my_signerid}};
-    this._outgoing = {type: this._my_signerid + "1",
-                      version: KEYEXCHANGE_VERSION,
-                      payload: one};
-    this._log.trace("Generated message " + this._outgoing.type);
-    callback();
-  },
-
-  _computeStepTwo: function _computeStepTwo(callback) {
-    this._log.trace("Computing round 2.");
-    if (this._incoming.type != this._their_signerid + "1") {
-      this._log.error("Invalid round 1 message: "
-                      + JSON.stringify(this._incoming));
-      this.abort(JPAKE_ERROR_WRONGMESSAGE);
-      return;
-    }
-
-    let step1 = this._incoming.payload;
-    if (!step1 || !step1.zkp_x1 || step1.zkp_x1.id != this._their_signerid
-        || !step1.zkp_x2 || step1.zkp_x2.id != this._their_signerid) {
-      this._log.error("Invalid round 1 payload: " + JSON.stringify(step1));
-      this.abort(JPAKE_ERROR_WRONGMESSAGE);
-      return;
-    }
-
-    let A = {};
-    let gvA = {};
-    let rA = {};
-
-    try {
-      this._jpake.round2(this._their_signerid, this._secret,
-                         step1.gx1, step1.zkp_x1.gr, step1.zkp_x1.b,
-                         step1.gx2, step1.zkp_x2.gr, step1.zkp_x2.b,
-                         A, gvA, rA);
-    } catch (ex) {
-      this._log.error("JPAKE round 2 threw: " + ex);
-      this.abort(JPAKE_ERROR_INTERNAL);
-      return;
-    }
-    let two = {A: A.value,
-               zkp_A: {gr: gvA.value, b: rA.value, id: this._my_signerid}};
-    this._outgoing = {type: this._my_signerid + "2",
-                      version: KEYEXCHANGE_VERSION,
-                      payload: two};
-    this._log.trace("Generated message " + this._outgoing.type);
-    callback();
-  },
-
-  _computeFinal: function _computeFinal(callback) {
-    if (this._incoming.type != this._their_signerid + "2") {
-      this._log.error("Invalid round 2 message: "
-                      + JSON.stringify(this._incoming));
-      this.abort(JPAKE_ERROR_WRONGMESSAGE);
-      return;
-    }
-
-    let step2 = this._incoming.payload;
-    if (!step2 || !step2.zkp_A || step2.zkp_A.id != this._their_signerid) {
-      this._log.error("Invalid round 2 payload: " + JSON.stringify(step1));
-      this.abort(JPAKE_ERROR_WRONGMESSAGE);
-      return;
-    }
-
-    let aes256Key = {};
-    let hmac256Key = {};
-
-    try {
-      this._jpake.final(step2.A, step2.zkp_A.gr, step2.zkp_A.b, HMAC_INPUT,
-                        aes256Key, hmac256Key);
-    } catch (ex) {
-      this._log.error("JPAKE final round threw: " + ex);
-      this.abort(JPAKE_ERROR_INTERNAL);
-      return;
-    }
-
-    this._crypto_key = aes256Key.value;
-    let hmac_key = Utils.makeHMACKey(Utils.safeAtoB(hmac256Key.value));
-    this._hmac_hasher = Utils.makeHMACHasher(Ci.nsICryptoHMAC.SHA256, hmac_key);
-
-    callback();
-  },
-
-  _computeKeyVerification: function _computeKeyVerification(callback) {
-    this._log.trace("Encrypting key verification value.");
-    let iv, ciphertext;
-    try {
-      iv = Svc.Crypto.generateRandomIV();
-      ciphertext = Svc.Crypto.encrypt(JPAKE_VERIFY_VALUE,
-                                      this._crypto_key, iv);
-    } catch (ex) {
-      this._log.error("Failed to encrypt key verification value.");
-      this.abort(JPAKE_ERROR_INTERNAL);
-      return;
-    }
-    this._outgoing = {type: this._my_signerid + "3",
-                      version: KEYEXCHANGE_VERSION,
-                      payload: {ciphertext, IV: iv}};
-    this._log.trace("Generated message " + this._outgoing.type);
-    callback();
-  },
-
-  _verifyPairing: function _verifyPairing(callback) {
-    this._log.trace("Verifying their key.");
-    if (this._incoming.type != this._their_signerid + "3") {
-      this._log.error("Invalid round 3 data: " +
-                      JSON.stringify(this._incoming));
-      this.abort(JPAKE_ERROR_WRONGMESSAGE);
-      return;
-    }
-    let step3 = this._incoming.payload;
-    let ciphertext;
-    try {
-      ciphertext = Svc.Crypto.encrypt(JPAKE_VERIFY_VALUE,
-                                      this._crypto_key, step3.IV);
-      if (ciphertext != step3.ciphertext) {
-        throw "Key mismatch!";
-      }
-    } catch (ex) {
-      this._log.error("Keys don't match!");
-      this.abort(JPAKE_ERROR_KEYMISMATCH);
-      return;
-    }
-
-    this._log.debug("Verified pairing!");
-    this._paired = true;
-    Utils.nextTick(function() { this.controller.onPaired(); }, this);
-    callback();
-  },
-
-  _encryptData: function _encryptData(callback) {
-    this._log.trace("Encrypting data.");
-    let iv, ciphertext, hmac;
-    try {
-      iv = Svc.Crypto.generateRandomIV();
-      ciphertext = Svc.Crypto.encrypt(this._data, this._crypto_key, iv);
-      hmac = Utils.bytesAsHex(Utils.digestUTF8(ciphertext, this._hmac_hasher));
-    } catch (ex) {
-      this._log.error("Failed to encrypt data.");
-      this.abort(JPAKE_ERROR_INTERNAL);
-      return;
-    }
-    this._outgoing = {type: this._my_signerid + "3",
-                      version: KEYEXCHANGE_VERSION,
-                      payload: {ciphertext, IV: iv, hmac}};
-    this._log.trace("Generated message " + this._outgoing.type);
-    callback();
-  },
-
-  _decryptData: function _decryptData(callback) {
-    this._log.trace("Verifying their key.");
-    if (this._incoming.type != this._their_signerid + "3") {
-      this._log.error("Invalid round 3 data: "
-                      + JSON.stringify(this._incoming));
-      this.abort(JPAKE_ERROR_WRONGMESSAGE);
-      return;
-    }
-    let step3 = this._incoming.payload;
-    try {
-      let hmac = Utils.bytesAsHex(
-        Utils.digestUTF8(step3.ciphertext, this._hmac_hasher));
-      if (hmac != step3.hmac) {
-        throw "HMAC validation failed!";
-      }
-    } catch (ex) {
-      this._log.error("HMAC validation failed.");
-      this.abort(JPAKE_ERROR_KEYMISMATCH);
-      return;
-    }
-
-    this._log.trace("Decrypting data.");
-    let cleartext;
-    try {
-      cleartext = Svc.Crypto.decrypt(step3.ciphertext, this._crypto_key,
-                                     step3.IV);
-    } catch (ex) {
-      this._log.error("Failed to decrypt data.");
-      this.abort(JPAKE_ERROR_INTERNAL);
-      return;
-    }
-
-    try {
-      this._newData = JSON.parse(cleartext);
-    } catch (ex) {
-      this._log.error("Invalid data data: " + JSON.stringify(cleartext));
-      this.abort(JPAKE_ERROR_INVALID);
-      return;
-    }
-
-    this._log.trace("Decrypted data.");
-    callback();
-  },
-
-  _complete: function _complete() {
-    this._log.debug("Exchange completed.");
-    this._finished = true;
-    Utils.nextTick(function() { this.controller.onComplete(this._newData); },
-                   this);
-  }
-
-};
-
-
-/**
- * Send credentials over an active J-PAKE channel.
- *
- * This object is designed to take over as the JPAKEClient controller,
- * presumably replacing one that is UI-based which would either cause
- * DOM objects to leak or the JPAKEClient to be GC'ed when the DOM
- * context disappears. This object stays alive for the duration of the
- * transfer by being strong-ref'ed as an nsIObserver.
- *
- * Credentials are sent after the first sync has been completed
- * (successfully or not.)
- *
- * Usage:
- *
- *   jpakeclient.controller = new SendCredentialsController(jpakeclient,
- *                                                          service);
- *
- */
-this.SendCredentialsController =
- function SendCredentialsController(jpakeclient, service) {
-  this._log = Log.repository.getLogger("Sync.SendCredentialsController");
-  this._log.level = Log.Level[Svc.Prefs.get("log.logger.service.main")];
-
-  this._log.trace("Loading.");
-  this.jpakeclient = jpakeclient;
-  this.service = service;
-
-  // Register ourselves as observers the first Sync finishing (either
-  // successfully or unsuccessfully, we don't care) or for removing
-  // this device's sync configuration, in case that happens while we
-  // haven't finished the first sync yet.
-  Services.obs.addObserver(this, "weave:service:sync:finish", false);
-  Services.obs.addObserver(this, "weave:service:sync:error", false);
-  Services.obs.addObserver(this, "weave:service:start-over", false);
-}
-SendCredentialsController.prototype = {
-
-  unload: function unload() {
-    this._log.trace("Unloading.");
-    try {
-      Services.obs.removeObserver(this, "weave:service:sync:finish");
-      Services.obs.removeObserver(this, "weave:service:sync:error");
-      Services.obs.removeObserver(this, "weave:service:start-over");
-    } catch (ex) {
-      // Ignore.
-    }
-  },
-
-  observe: function observe(subject, topic, data) {
-    switch (topic) {
-      case "weave:service:sync:finish":
-      case "weave:service:sync:error":
-        Utils.nextTick(this.sendCredentials, this);
-        break;
-      case "weave:service:start-over":
-        // This will call onAbort which will call unload().
-        this.jpakeclient.abort();
-        break;
-    }
-  },
-
-  sendCredentials: function sendCredentials() {
-    this._log.trace("Sending credentials.");
-    let credentials = {account:   this.service.identity.account,
-                       password:  this.service.identity.basicPassword,
-                       synckey:   this.service.identity.syncKey,
-                       serverURL: this.service.serverURL};
-    this.jpakeclient.sendAndComplete(credentials);
-  },
-
-  // JPAKEClient controller API
-
-  onComplete: function onComplete() {
-    this._log.debug("Exchange was completed successfully!");
-    this.unload();
-
-    // Schedule a Sync for soonish to fetch the data uploaded by the
-    // device with which we just paired.
-    this.service.scheduler.scheduleNextSync(this.service.scheduler.activeInterval);
-  },
-
-  onAbort: function onAbort(error) {
-    // It doesn't really matter why we aborted, but the channel is closed
-    // for sure, so we won't be able to do anything with it.
-    this._log.debug("Exchange was aborted with error: " + error);
-    this.unload();
-  },
-
-  // Irrelevant methods for this controller:
-  displayPIN: function displayPIN() {},
-  onPairingStart: function onPairingStart() {},
-  onPaired: function onPaired() {},
-};
--- a/services/sync/modules/main.js
+++ b/services/sync/modules/main.js
@@ -2,17 +2,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 this.EXPORTED_SYMBOLS = ["Weave"];
 
 this.Weave = {};
 Components.utils.import("resource://services-sync/constants.js", Weave);
 var lazies = {
-  "jpakeclient.js":       ["JPAKEClient", "SendCredentialsController"],
   "service.js":           ["Service"],
   "status.js":            ["Status"],
   "util.js":              ["Utils", "Svc"]
 };
 
 function lazyImport(module, dest, props) {
   function getter(prop) {
     return function() {
--- a/services/sync/modules/policies.js
+++ b/services/sync/modules/policies.js
@@ -31,17 +31,16 @@ function getThrottledIntervalPreference(
 this.SyncScheduler = function SyncScheduler(service) {
   this.service = service;
   this.init();
 }
 SyncScheduler.prototype = {
   _log: Log.repository.getLogger("Sync.SyncScheduler"),
 
   _fatalLoginStatus: [LOGIN_FAILED_NO_USERNAME,
-                      LOGIN_FAILED_NO_PASSWORD,
                       LOGIN_FAILED_NO_PASSPHRASE,
                       LOGIN_FAILED_INVALID_PASSPHRASE,
                       LOGIN_FAILED_LOGIN_REJECTED],
 
   /**
    * The nsITimer object that schedules the next sync. See scheduleNextSync().
    */
   syncTimer: null,
--- a/services/sync/modules/status.js
+++ b/services/sync/modules/status.js
@@ -47,17 +47,16 @@ this.Status = {
     return this._login;
   },
 
   set login(code) {
     this._log.debug("Status.login: " + this._login + " => " + code);
     this._login = code;
 
     if (code == LOGIN_FAILED_NO_USERNAME ||
-        code == LOGIN_FAILED_NO_PASSWORD ||
         code == LOGIN_FAILED_NO_PASSPHRASE) {
       this.service = CLIENT_NOT_CONFIGURED;
     } else if (code != LOGIN_SUCCEEDED) {
       this.service = LOGIN_FAILED;
     } else {
       this.service = STATUS_OK;
     }
   },
--- a/services/sync/moz.build
+++ b/services/sync/moz.build
@@ -20,17 +20,16 @@ EXTRA_JS_MODULES['services-sync'] += [
     'modules/addonsreconciler.js',
     'modules/addonutils.js',
     'modules/bookmark_validator.js',
     'modules/browserid_identity.js',
     'modules/collection_validator.js',
     'modules/engines.js',
     'modules/FxaMigrator.jsm',
     'modules/identity.js',
-    'modules/jpakeclient.js',
     'modules/keys.js',
     'modules/main.js',
     'modules/policies.js',
     'modules/record.js',
     'modules/resource.js',
     'modules/rest.js',
     'modules/service.js',
     'modules/status.js',
--- a/services/sync/services-sync.js
+++ b/services/sync/services-sync.js
@@ -27,22 +27,16 @@ pref("services.sync.errorhandler.network
 pref("services.sync.engine.addons", true);
 pref("services.sync.engine.bookmarks", true);
 pref("services.sync.engine.history", true);
 pref("services.sync.engine.passwords", true);
 pref("services.sync.engine.prefs", true);
 pref("services.sync.engine.tabs", true);
 pref("services.sync.engine.tabs.filteredUrls", "^(about:.*|resource:.*|chrome:.*|wyciwyg:.*|file:.*|blob:.*)$");
 
-pref("services.sync.jpake.serverURL", "https://setup.services.mozilla.com/");
-pref("services.sync.jpake.pollInterval", 1000);
-pref("services.sync.jpake.firstMsgMaxTries", 300); // 5 minutes
-pref("services.sync.jpake.lastMsgMaxTries", 300);  // 5 minutes
-pref("services.sync.jpake.maxTries", 10);
-
 // If true, add-on sync ignores changes to the user-enabled flag. This
 // allows people to have the same set of add-ons installed across all
 // profiles while maintaining different enabled states.
 pref("services.sync.addons.ignoreUserEnabledChanges", false);
 
 // Comma-delimited list of hostnames to trust for add-on install.
 pref("services.sync.addons.trustedSourceHostnames", "addons.mozilla.org");
 
@@ -54,17 +48,16 @@ pref("services.sync.log.appender.file.lo
 pref("services.sync.log.appender.file.maxErrorAge", 864000); // 10 days
 pref("services.sync.log.rootLogger", "Debug");
 pref("services.sync.log.logger.addonutils", "Debug");
 pref("services.sync.log.logger.declined", "Debug");
 pref("services.sync.log.logger.service.main", "Debug");
 pref("services.sync.log.logger.status", "Debug");
 pref("services.sync.log.logger.authenticator", "Debug");
 pref("services.sync.log.logger.network.resources", "Debug");
-pref("services.sync.log.logger.service.jpakeclient", "Debug");
 pref("services.sync.log.logger.engine.bookmarks", "Debug");
 pref("services.sync.log.logger.engine.clients", "Debug");
 pref("services.sync.log.logger.engine.forms", "Debug");
 pref("services.sync.log.logger.engine.history", "Debug");
 pref("services.sync.log.logger.engine.passwords", "Debug");
 pref("services.sync.log.logger.engine.prefs", "Debug");
 pref("services.sync.log.logger.engine.tabs", "Debug");
 pref("services.sync.log.logger.engine.addons", "Debug");
deleted file mode 100644
--- a/services/sync/tests/unit/test_jpakeclient.js
+++ /dev/null
@@ -1,562 +0,0 @@
-Cu.import("resource://gre/modules/Log.jsm");
-Cu.import("resource://services-sync/identity.js");
-Cu.import("resource://services-sync/jpakeclient.js");
-Cu.import("resource://services-sync/constants.js");
-Cu.import("resource://services-sync/util.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
-
-const JPAKE_LENGTH_SECRET     = 8;
-const JPAKE_LENGTH_CLIENTID   = 256;
-const KEYEXCHANGE_VERSION     = 3;
-
-/*
- * Simple server.
- */
-
-const SERVER_MAX_GETS = 6;
-
-function check_headers(request) {
-  let stack = Components.stack.caller;
-
-  // There shouldn't be any Basic auth
-  do_check_false(request.hasHeader("Authorization"), stack);
-
-  // Ensure key exchange ID is set and the right length
-  do_check_true(request.hasHeader("X-KeyExchange-Id"), stack);
-  do_check_eq(request.getHeader("X-KeyExchange-Id").length,
-              JPAKE_LENGTH_CLIENTID, stack);
-}
-
-function new_channel() {
-  // Create a new channel and register it with the server.
-  let cid = Math.floor(Math.random() * 10000);
-  while (channels[cid]) {
-    cid = Math.floor(Math.random() * 10000);
-  }
-  let channel = channels[cid] = new ServerChannel();
-  server.registerPathHandler("/" + cid, channel.handler());
-  return cid;
-}
-
-var server;
-var channels = {};  // Map channel -> ServerChannel object
-function server_new_channel(request, response) {
-  check_headers(request);
-  let cid = new_channel();
-  let body = JSON.stringify("" + cid);
-  response.setStatusLine(request.httpVersion, 200, "OK");
-  response.bodyOutputStream.write(body, body.length);
-}
-
-var error_report;
-function server_report(request, response) {
-  check_headers(request);
-
-  if (request.hasHeader("X-KeyExchange-Log")) {
-    error_report = request.getHeader("X-KeyExchange-Log");
-  }
-
-  if (request.hasHeader("X-KeyExchange-Cid")) {
-    let cid = request.getHeader("X-KeyExchange-Cid");
-    let channel = channels[cid];
-    if (channel) {
-      channel.clear();
-    }
-  }
-
-  response.setStatusLine(request.httpVersion, 200, "OK");
-}
-
-// Hook for test code.
-var hooks = {};
-function initHooks() {
-  hooks.onGET = function onGET(request) {};
-}
-initHooks();
-
-function ServerChannel() {
-  this.data = "";
-  this.etag = "";
-  this.getCount = 0;
-}
-ServerChannel.prototype = {
-
-  GET: function GET(request, response) {
-    if (!this.data) {
-      response.setStatusLine(request.httpVersion, 404, "Not Found");
-      return;
-    }
-
-    if (request.hasHeader("If-None-Match")) {
-      let etag = request.getHeader("If-None-Match");
-      if (etag == this.etag) {
-        response.setStatusLine(request.httpVersion, 304, "Not Modified");
-        hooks.onGET(request);
-        return;
-      }
-    }
-    response.setHeader("ETag", this.etag);
-    response.setStatusLine(request.httpVersion, 200, "OK");
-    response.bodyOutputStream.write(this.data, this.data.length);
-
-    // Automatically clear the channel after 6 successful GETs.
-    this.getCount += 1;
-    if (this.getCount == SERVER_MAX_GETS) {
-      this.clear();
-    }
-    hooks.onGET(request);
-  },
-
-  PUT: function PUT(request, response) {
-    if (this.data) {
-      do_check_true(request.hasHeader("If-Match"));
-      let etag = request.getHeader("If-Match");
-      if (etag != this.etag) {
-        response.setHeader("ETag", this.etag);
-        response.setStatusLine(request.httpVersion, 412, "Precondition Failed");
-        return;
-      }
-    } else {
-      do_check_true(request.hasHeader("If-None-Match"));
-      do_check_eq(request.getHeader("If-None-Match"), "*");
-    }
-
-    this.data = readBytesFromInputStream(request.bodyInputStream);
-    this.etag = '"' + Utils.sha1(this.data) + '"';
-    response.setHeader("ETag", this.etag);
-    response.setStatusLine(request.httpVersion, 200, "OK");
-  },
-
-  clear: function clear() {
-    delete this.data;
-  },
-
-  handler: function handler() {
-    let self = this;
-    return function(request, response) {
-      check_headers(request);
-      let method = self[request.method];
-      return method.apply(self, arguments);
-    };
-  }
-
-};
-
-
-/**
- * Controller that throws for everything.
- */
-var BaseController = {
-  displayPIN: function displayPIN() {
-    do_throw("displayPIN() shouldn't have been called!");
-  },
-  onPairingStart: function onPairingStart() {
-    do_throw("onPairingStart shouldn't have been called!");
-  },
-  onAbort: function onAbort(error) {
-    do_throw("Shouldn't have aborted with " + error + "!");
-  },
-  onPaired: function onPaired() {
-    do_throw("onPaired() shouldn't have been called!");
-  },
-  onComplete: function onComplete(data) {
-    do_throw("Shouldn't have completed with " + data + "!");
-  }
-};
-
-
-const DATA = {"msg": "eggstreamly sekrit"};
-const POLLINTERVAL = 50;
-
-function run_test() {
-  server = httpd_setup({"/new_channel": server_new_channel,
-                        "/report":      server_report});
-  Svc.Prefs.set("jpake.serverURL", server.baseURI + "/");
-  Svc.Prefs.set("jpake.pollInterval", POLLINTERVAL);
-  Svc.Prefs.set("jpake.maxTries", 2);
-  Svc.Prefs.set("jpake.firstMsgMaxTries", 5);
-  Svc.Prefs.set("jpake.lastMsgMaxTries", 5);
-  // Ensure clean up
-  Svc.Obs.add("profile-before-change", function() {
-    Svc.Prefs.resetBranch("");
-  });
-
-  // Ensure PSM is initialized.
-  Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
-
-  // Simulate Sync setup with credentials in place. We want to make
-  // sure the J-PAKE requests don't include those data.
-  ensureLegacyIdentityManager();
-  setBasicCredentials("johndoe", "ilovejane");
-
-  initTestLogging("Trace");
-  Log.repository.getLogger("Sync.JPAKEClient").level = Log.Level.Trace;
-  Log.repository.getLogger("Common.RESTRequest").level =
-    Log.Level.Trace;
-  run_next_test();
-}
-
-
-add_test(function test_success_receiveNoPIN() {
-  _("Test a successful exchange started by receiveNoPIN().");
-
-  let snd = new JPAKEClient({
-    __proto__: BaseController,
-    onPaired: function onPaired() {
-      _("Pairing successful, sending final payload.");
-      do_check_true(pairingStartCalledOnReceiver);
-      Utils.nextTick(function() { snd.sendAndComplete(DATA); });
-    },
-    onComplete: function onComplete() {}
-  });
-
-  let pairingStartCalledOnReceiver = false;
-  let rec = new JPAKEClient({
-    __proto__: BaseController,
-    displayPIN: function displayPIN(pin) {
-      _("Received PIN " + pin + ". Entering it in the other computer...");
-      this.cid = pin.slice(JPAKE_LENGTH_SECRET);
-      Utils.nextTick(function() { snd.pairWithPIN(pin, false); });
-    },
-    onPairingStart: function onPairingStart() {
-      pairingStartCalledOnReceiver = true;
-    },
-    onComplete: function onComplete(data) {
-      do_check_true(Utils.deepEquals(DATA, data));
-      // Ensure channel was cleared, no error report.
-      do_check_eq(channels[this.cid].data, undefined);
-      do_check_eq(error_report, undefined);
-      run_next_test();
-    }
-  });
-  rec.receiveNoPIN();
-});
-
-
-add_test(function test_firstMsgMaxTries_timeout() {
-  _("Test abort when sender doesn't upload anything.");
-
-  let rec = new JPAKEClient({
-    __proto__: BaseController,
-    displayPIN: function displayPIN(pin) {
-      _("Received PIN " + pin + ". Doing nothing...");
-      this.cid = pin.slice(JPAKE_LENGTH_SECRET);
-    },
-    onAbort: function onAbort(error) {
-      do_check_eq(error, JPAKE_ERROR_TIMEOUT);
-      // Ensure channel was cleared, error report was sent.
-      do_check_eq(channels[this.cid].data, undefined);
-      do_check_eq(error_report, JPAKE_ERROR_TIMEOUT);
-      error_report = undefined;
-      run_next_test();
-    }
-  });
-  rec.receiveNoPIN();
-});
-
-
-add_test(function test_firstMsgMaxTries() {
-  _("Test that receiver can wait longer for the first message.");
-
-  let snd = new JPAKEClient({
-    __proto__: BaseController,
-    onPaired: function onPaired() {
-      _("Pairing successful, sending final payload.");
-      Utils.nextTick(function() { snd.sendAndComplete(DATA); });
-    },
-    onComplete: function onComplete() {}
-  });
-
-  let rec = new JPAKEClient({
-    __proto__: BaseController,
-    displayPIN: function displayPIN(pin) {
-      // For the purpose of the tests, the poll interval is 50ms and
-      // we're polling up to 5 times for the first exchange (as
-      // opposed to 2 times for most of the other exchanges). So let's
-      // pretend it took 150ms to enter the PIN on the sender, which should
-      // require 3 polls.
-      // Rather than using an imprecise timer, we hook into the channel's
-      // GET handler to know how long to wait.
-      _("Received PIN " + pin + ". Waiting for three polls before entering it into sender...");
-      this.cid = pin.slice(JPAKE_LENGTH_SECRET);
-      let count = 0;
-      hooks.onGET = function onGET(request) {
-        if (++count == 3) {
-          _("Third GET. Triggering pair.");
-          Utils.nextTick(function() { snd.pairWithPIN(pin, false); });
-        }
-      };
-    },
-    onPairingStart: function onPairingStart(pin) {},
-    onComplete: function onComplete(data) {
-      do_check_true(Utils.deepEquals(DATA, data));
-      // Ensure channel was cleared, no error report.
-      do_check_eq(channels[this.cid].data, undefined);
-      do_check_eq(error_report, undefined);
-
-      // Clean up.
-      initHooks();
-      run_next_test();
-    }
-  });
-  rec.receiveNoPIN();
-});
-
-
-add_test(function test_lastMsgMaxTries() {
-  _("Test that receiver can wait longer for the last message.");
-
- let snd = new JPAKEClient({
-    __proto__: BaseController,
-    onPaired: function onPaired() {
-      // For the purpose of the tests, the poll interval is 50ms and
-      // we're polling up to 5 times for the last exchange (as opposed
-      // to 2 times for other exchanges). So let's pretend it took
-      // 150ms to come up with the final payload, which should require
-      // 3 polls.
-      // Rather than using an imprecise timer, we hook into the channel's
-      // GET handler to know how long to wait.
-      let count = 0;
-      hooks.onGET = function onGET(request) {
-        if (++count == 3) {
-          _("Third GET. Triggering send.");
-          Utils.nextTick(function() { snd.sendAndComplete(DATA); });
-        }
-      };
-    },
-    onComplete: function onComplete() {}
-  });
-
-  let rec = new JPAKEClient({
-    __proto__: BaseController,
-    displayPIN: function displayPIN(pin) {
-      _("Received PIN " + pin + ". Entering it in the other computer...");
-      this.cid = pin.slice(JPAKE_LENGTH_SECRET);
-      Utils.nextTick(function() { snd.pairWithPIN(pin, false); });
-    },
-    onPairingStart: function onPairingStart(pin) {},
-    onComplete: function onComplete(data) {
-      do_check_true(Utils.deepEquals(DATA, data));
-      // Ensure channel was cleared, no error report.
-      do_check_eq(channels[this.cid].data, undefined);
-      do_check_eq(error_report, undefined);
-
-      // Clean up.
-      initHooks();
-      run_next_test();
-    }
-  });
-
-  rec.receiveNoPIN();
-});
-
-
-add_test(function test_wrongPIN() {
-  _("Test abort when PINs don't match.");
-
-  let snd = new JPAKEClient({
-    __proto__: BaseController,
-    onAbort: function onAbort(error) {
-      do_check_eq(error, JPAKE_ERROR_KEYMISMATCH);
-      do_check_eq(error_report, JPAKE_ERROR_KEYMISMATCH);
-      error_report = undefined;
-    }
-  });
-
-  let pairingStartCalledOnReceiver = false;
-  let rec = new JPAKEClient({
-    __proto__: BaseController,
-    displayPIN: function displayPIN(pin) {
-      this.cid = pin.slice(JPAKE_LENGTH_SECRET);
-      let secret = pin.slice(0, JPAKE_LENGTH_SECRET);
-      secret = Array.prototype.slice.call(secret).reverse().join("");
-      let new_pin = secret + this.cid;
-      _("Received PIN " + pin + ", but I'm entering " + new_pin);
-
-      Utils.nextTick(function() { snd.pairWithPIN(new_pin, false); });
-    },
-    onPairingStart: function onPairingStart() {
-      pairingStartCalledOnReceiver = true;
-    },
-    onAbort: function onAbort(error) {
-      do_check_true(pairingStartCalledOnReceiver);
-      do_check_eq(error, JPAKE_ERROR_NODATA);
-      // Ensure channel was cleared.
-      do_check_eq(channels[this.cid].data, undefined);
-      run_next_test();
-    }
-  });
-  rec.receiveNoPIN();
-});
-
-
-add_test(function test_abort_receiver() {
-  _("Test user abort on receiving side.");
-
-  let rec = new JPAKEClient({
-    __proto__: BaseController,
-    onAbort: function onAbort(error) {
-      // Manual abort = userabort.
-      do_check_eq(error, JPAKE_ERROR_USERABORT);
-      // Ensure channel was cleared.
-      do_check_eq(channels[this.cid].data, undefined);
-      do_check_eq(error_report, JPAKE_ERROR_USERABORT);
-      error_report = undefined;
-      run_next_test();
-    },
-    displayPIN: function displayPIN(pin) {
-      this.cid = pin.slice(JPAKE_LENGTH_SECRET);
-      Utils.nextTick(function() { rec.abort(); });
-    }
-  });
-  rec.receiveNoPIN();
-});
-
-
-add_test(function test_abort_sender() {
-  _("Test user abort on sending side.");
-
-  let snd = new JPAKEClient({
-    __proto__: BaseController,
-    onAbort: function onAbort(error) {
-      // Manual abort == userabort.
-      do_check_eq(error, JPAKE_ERROR_USERABORT);
-      do_check_eq(error_report, JPAKE_ERROR_USERABORT);
-      error_report = undefined;
-    }
-  });
-
-  let rec = new JPAKEClient({
-    __proto__: BaseController,
-    onAbort: function onAbort(error) {
-      do_check_eq(error, JPAKE_ERROR_NODATA);
-      // Ensure channel was cleared, no error report.
-      do_check_eq(channels[this.cid].data, undefined);
-      do_check_eq(error_report, undefined);
-      initHooks();
-      run_next_test();
-    },
-    displayPIN: function displayPIN(pin) {
-      _("Received PIN " + pin + ". Entering it in the other computer...");
-      this.cid = pin.slice(JPAKE_LENGTH_SECRET);
-      Utils.nextTick(function() { snd.pairWithPIN(pin, false); });
-
-      // Abort after the first poll.
-      let count = 0;
-      hooks.onGET = function onGET(request) {
-        if (++count >= 1) {
-          _("First GET. Aborting.");
-          Utils.nextTick(function() { snd.abort(); });
-        }
-      };
-    },
-    onPairingStart: function onPairingStart(pin) {}
-  });
-  rec.receiveNoPIN();
-});
-
-
-add_test(function test_wrongmessage() {
-  let cid = new_channel();
-  let channel = channels[cid];
-  channel.data = JSON.stringify({type: "receiver2",
-                                 version: KEYEXCHANGE_VERSION,
-                                 payload: {}});
-  channel.etag = '"fake-etag"';
-  let snd = new JPAKEClient({
-    __proto__: BaseController,
-    onComplete: function onComplete(data) {
-      do_throw("onComplete shouldn't be called.");
-    },
-    onAbort: function onAbort(error) {
-      do_check_eq(error, JPAKE_ERROR_WRONGMESSAGE);
-      run_next_test();
-    }
-  });
-  snd.pairWithPIN("01234567" + cid, false);
-});
-
-
-add_test(function test_error_channel() {
-  let serverURL = Svc.Prefs.get("jpake.serverURL");
-  Svc.Prefs.set("jpake.serverURL", "http://localhost:12345/");
-
-  let rec = new JPAKEClient({
-    __proto__: BaseController,
-    onAbort: function onAbort(error) {
-      do_check_eq(error, JPAKE_ERROR_CHANNEL);
-      Svc.Prefs.set("jpake.serverURL", serverURL);
-      run_next_test();
-    },
-    onPairingStart: function onPairingStart(pin) {},
-    displayPIN: function displayPIN(pin) {}
-  });
-  rec.receiveNoPIN();
-});
-
-
-add_test(function test_error_network() {
-  let serverURL = Svc.Prefs.get("jpake.serverURL");
-  Svc.Prefs.set("jpake.serverURL", "http://localhost:12345/");
-
-  let snd = new JPAKEClient({
-    __proto__: BaseController,
-    onAbort: function onAbort(error) {
-      do_check_eq(error, JPAKE_ERROR_NETWORK);
-      Svc.Prefs.set("jpake.serverURL", serverURL);
-      run_next_test();
-    }
-  });
-  snd.pairWithPIN("0123456789ab", false);
-});
-
-
-add_test(function test_error_server_noETag() {
-  let cid = new_channel();
-  let channel = channels[cid];
-  channel.data = JSON.stringify({type: "receiver1",
-                                 version: KEYEXCHANGE_VERSION,
-                                 payload: {}});
-  // This naughty server doesn't supply ETag (well, it supplies empty one).
-  channel.etag = "";
-  let snd = new JPAKEClient({
-    __proto__: BaseController,
-    onAbort: function onAbort(error) {
-      do_check_eq(error, JPAKE_ERROR_SERVER);
-      run_next_test();
-    }
-  });
-  snd.pairWithPIN("01234567" + cid, false);
-});
-
-
-add_test(function test_error_delayNotSupported() {
-  let cid = new_channel();
-  let channel = channels[cid];
-  channel.data = JSON.stringify({type: "receiver1",
-                                 version: 2,
-                                 payload: {}});
-  channel.etag = '"fake-etag"';
-  let snd = new JPAKEClient({
-    __proto__: BaseController,
-    onAbort: function onAbort(error) {
-      do_check_eq(error, JPAKE_ERROR_DELAYUNSUPPORTED);
-      run_next_test();
-    }
-  });
-  snd.pairWithPIN("01234567" + cid, true);
-});
-
-
-add_test(function test_sendAndComplete_notPaired() {
-  let snd = new JPAKEClient({__proto__: BaseController});
-  do_check_throws(function() {
-    snd.sendAndComplete(DATA);
-  });
-  run_next_test();
-});
-
-
-add_test(function tearDown() {
-  server.stop(run_next_test);
-});
--- a/services/sync/tests/unit/test_load_modules.js
+++ b/services/sync/tests/unit/test_load_modules.js
@@ -12,17 +12,16 @@ const modules = [
   "engines/extension-storage.js",
   "engines/forms.js",
   "engines/history.js",
   "engines/passwords.js",
   "engines/prefs.js",
   "engines/tabs.js",
   "engines.js",
   "identity.js",
-  "jpakeclient.js",
   "keys.js",
   "main.js",
   "policies.js",
   "record.js",
   "resource.js",
   "rest.js",
   "service.js",
   "stages/cluster.js",
deleted file mode 100644
--- a/services/sync/tests/unit/test_sendcredentials_controller.js
+++ /dev/null
@@ -1,102 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-Cu.import("resource://services-sync/constants.js");
-Cu.import("resource://services-sync/jpakeclient.js");
-Cu.import("resource://services-sync/service.js");
-Cu.import("resource://services-sync/util.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
-
-function run_test() {
-  ensureLegacyIdentityManager();
-  setBasicCredentials("johndoe", "ilovejane", Utils.generatePassphrase());
-  Service.serverURL  = "http://weave.server/";
-
-  initTestLogging("Trace");
-  Log.repository.getLogger("Sync.SendCredentialsController").level = Log.Level.Trace;
-  Log.repository.getLogger("Sync.SyncScheduler").level = Log.Level.Trace;
-  run_next_test();
-}
-
-function make_sendCredentials_test(topic) {
-  return function test_sendCredentials() {
-    _("Test sending credentials on " + topic + " observer notification.");
-
-    let sendAndCompleteCalled = false;
-    let jpakeclient = {
-      sendAndComplete: function sendAndComplete(data) {
-        // Verify that the controller unregisters itself as an observer
-        // when the exchange is complete by faking another notification.
-        do_check_false(sendAndCompleteCalled);
-        sendAndCompleteCalled = true;
-
-        // Verify it sends the correct data.
-        do_check_eq(data.account, Service.identity.account);
-        do_check_eq(data.password, Service.identity.basicPassword);
-        do_check_eq(data.synckey, Service.identity.syncKey);
-        do_check_eq(data.serverURL, Service.serverURL);
-
-        this.controller.onComplete();
-        // Verify it schedules a sync for the expected interval.
-        let expectedInterval = Service.scheduler.activeInterval;
-        do_check_true(Service.scheduler.nextSync - Date.now() <= expectedInterval);
-
-        // Signal the end of another sync. We shouldn't be registered anymore,
-        // so we shouldn't re-enter this method (cf sendAndCompleteCalled above)
-        Svc.Obs.notify(topic);
-
-        Service.scheduler.setDefaults();
-        Utils.nextTick(run_next_test);
-      }
-    };
-    jpakeclient.controller = new SendCredentialsController(jpakeclient, Service);
-    Svc.Obs.notify(topic);
-  };
-}
-
-add_test(make_sendCredentials_test("weave:service:sync:finish"));
-add_test(make_sendCredentials_test("weave:service:sync:error"));
-
-
-add_test(function test_abort() {
-  _("Test aborting the J-PAKE exchange.");
-
-  let jpakeclient = {
-    sendAndComplete: function sendAndComplete() {
-      do_throw("Shouldn't get here!");
-    }
-  };
-  jpakeclient.controller = new SendCredentialsController(jpakeclient, Service);
-
-  // Verify that the controller unregisters itself when the exchange
-  // was aborted.
-  jpakeclient.controller.onAbort(JPAKE_ERROR_USERABORT);
-  Svc.Obs.notify("weave:service:sync:finish");
-  Utils.nextTick(run_next_test);
-});
-
-
-add_test(function test_startOver() {
-  _("Test wiping local Sync config aborts transaction.");
-
-  let abortCalled = false;
-  let jpakeclient = {
-    abort: function abort() {
-      abortCalled = true;
-      this.controller.onAbort(JPAKE_ERROR_USERABORT);
-    },
-    sendAndComplete: function sendAndComplete() {
-      do_throw("Shouldn't get here!");
-    }
-  };
-  jpakeclient.controller = new SendCredentialsController(jpakeclient, Service);
-
-  Svc.Obs.notify("weave:service:start-over");
-  do_check_true(abortCalled);
-
-  // Ensure that the controller no longer does anything if a sync
-  // finishes now or -- more likely -- errors out.
-  Svc.Obs.notify("weave:service:sync:error");
-
-  Utils.nextTick(run_next_test);
-});
--- a/services/sync/tests/unit/xpcshell.ini
+++ b/services/sync/tests/unit/xpcshell.ini
@@ -36,20 +36,16 @@ support-files =
 [test_utils_notify.js]
 [test_utils_passphrase.js]
 
 # We have a number of other libraries that are pretty much standalone.
 [test_addon_utils.js]
 run-sequentially = Restarts server, can't change pref.
 tags = addons
 [test_httpd_sync_server.js]
-[test_jpakeclient.js]
-# Bug 618233: this test produces random failures on Windows 7.
-# Bug 676978: test hangs on Android (see also testing/xpcshell/xpcshell.ini)
-skip-if = os == "win" || os == "android"
 
 # HTTP layers.
 [test_resource.js]
 [test_resource_async.js]
 [test_resource_header.js]
 [test_resource_ua.js]
 [test_syncstoragerequest.js]
 
@@ -117,17 +113,16 @@ skip-if = os == "android"
 [test_errorhandler_sync_checkServerError.js]
 # Bug 676978: test hangs on Android (see also testing/xpcshell/xpcshell.ini)
 skip-if = os == "android"
 [test_errorhandler_eol.js]
 [test_hmac_error.js]
 [test_interval_triggers.js]
 [test_node_reassignment.js]
 [test_score_triggers.js]
-[test_sendcredentials_controller.js]
 [test_status.js]
 [test_status_checkSetup.js]
 [test_syncscheduler.js]
 [test_upgrade_old_sync_key.js]
 
 # Firefox Accounts specific tests
 [test_fxa_startOver.js]
 [test_fxa_service_cluster.js]
--- a/tools/lint/eslint/modules.json
+++ b/tools/lint/eslint/modules.json
@@ -105,17 +105,16 @@
   "history.jsm": ["HistoryEntry", "DumpHistory"],
   "Http.jsm": ["httpRequest", "percentEncode"],
   "httpd.js": ["HTTP_400", "HTTP_401", "HTTP_402", "HTTP_403", "HTTP_404", "HTTP_405", "HTTP_406", "HTTP_407", "HTTP_408", "HTTP_409", "HTTP_410", "HTTP_411", "HTTP_412", "HTTP_413", "HTTP_414", "HTTP_415", "HTTP_417", "HTTP_500", "HTTP_501", "HTTP_502", "HTTP_503", "HTTP_504", "HTTP_505", "HttpError", "HttpServer"],
   "identity.js": ["IdentityManager"],
   "import_module.jsm": ["MODULE_IMPORTED", "MODULE_URI", "SUBMODULE_IMPORTED", "same_scope", "SUBMODULE_IMPORTED_TO_SCOPE"],
   "import_sub_module.jsm": ["SUBMODULE_IMPORTED", "test_obj"],
   "InlineSpellChecker.jsm": ["InlineSpellChecker", "SpellCheckHelper"],
   "JNI.jsm": ["JNI", "android_log"],
-  "jpakeclient.js": ["JPAKEClient", "SendCredentialsController"],
   "Jsbeautify.jsm": ["jsBeautify"],
   "jsdebugger.jsm": ["addDebuggerToGlobal"],
   "json2.js": ["JSON"],
   "keys.js": ["BulkKeyBundle", "SyncKeyBundle"],
   "KeyValueParser.jsm": ["parseKeyValuePairsFromLines", "parseKeyValuePairs", "parseKeyValuePairsFromFile"],
   "kinto-http-client.js": ["KintoHttpClient"],
   "kinto-offline-client.js": ["Kinto"],
   "kinto-storage-adapter.js": ["FirefoxAdapter"],