Cu.import("resource://services-sync/base_records/keys.js");
Cu.import("resource://services-sync/service.js");

function run_test() {
  initTestLogging("Debug");

  // Test fixtures:

  // A private key with a unicode passphrase generated with Sync 1.4
  // and the corresponding public key.
  let pubkeyUri = "http://localhost:8080/1.0/foo/storage/keys/pubkey";
  let privkeyUri = "http://localhost:8080/1.0/foo/storage/keys/privkey";

  // a bunch of currency symbols
  let passphrase = "moneyislike$\u20ac\u00a5\u5143";
  let lowbyteonly = "moneyislike$\u00ac\u00a5\u0043";

  let sync14privkey = {
    salt: "Huk0JwJoJBXeKNioTZBQZA==",
    iv: "bHPlRmSFfkUe5IcfjnoNTA==",
    keyData: "MxAragjVoBL7erecOAaEgq/qSyLgVBFTHdEXpnP1tlEseobFsM+4z5Xhnxbows5+aL1nu50BNwr4Aao9A6Gky/Eeoo4vmWPGlQTaJgX4DDI3IrW6aXkoNzzJqvdYF2EbttnMz/x6JcXhX3KOmBgXl4bz2AgOwwyH1KCYTYhQ8PLB/BKh8vSR7gycwAqOC7OgaH1iAs4TMqy53DIlYGOdQKMxgsbQoRNGEIycBboMc35/e8PROVrp1LIUHniGtPj13J2BEi7sPGFswZbxOSnS+Zu1B8X1HVf3xlpKLGPsrD5ZWrtX/6hZS0tKR2M+ec1vIRYTGevAmICp+f6vdlp27bT9nAlSFwwHKodysNj3SyNQ2Dj6JuBkqSrjk9nV/73/8af8TF6NXZkCH9dRuI+gKrbzK1u6ikrg3ayQRzChLhqt4UvvkufMs2Pu8dIaZBgo1XSQiE+spMdsj/KEinyFeD3AnWi2WwesMSFrs0zWLHxHMgVJw0L6XXpTWPohf/uZ+O5fjULFZoyJ/Jr2AB67shm1wxkIaNIPACg9tjYstF2oyA6LsLUt1ctEXhoj/GjvnZqPXl9GGR1ElKbncKBOl9hxV4l1SEu70w/IVcluDhBIT3Fu1jX96TL+4UeRf3Qev84aY+7xSX2+OnWAFTMb6BsOcJmQXNh6PQ59eiv+AfwlxPedrWnP69+m/JfT5YvvzcEF6eiQKswOLid+O0aiHvqlhH/yxqHh5PjeGnNIWwtHO56d4tdfQsPGGF+dWGPIIw5hrG+GbdgA6nleIrZIEmxkyhVFikqSJLeFxB11+pF7Vk1wANgYPoxAh5T3CiCkX+k0KiJodqt+DThmmcgevdW3yOilmOPRkeYSlkfH9wFt+LnrJ5/LAglyOmudwc+amT6LYMUEp++NzPwk1Lx18hwXe7CQDvFaqvGB/XLQIPXqbCSdNVdRc3yADk5cA4caiOG37i2Qb/q6laJ3kjawSvSI43nkk4akGd1Gt533L1Ip1r70Tm1iV4nyeAO84CPgRPnDLR1KHxRNJZejVE4ouSIR/94veHRUDEFPrsae4JJlXVG1urmyPHXDdrWmquMkyjzbs+YB8JT1eFtT2Wqu9W8NZyMP0R57btNuXrw1qZm5QK5MmrCfqjGzx2dlv0Zito6CtQePlVjMq+gdprqB71Pl98WzvRNXZtK0uxVZjMJSLPqI1Zc9ju/lHgZgarsIFlKTGPcoqGnlUex2n8JBn9/sdiit1YLIFUMsrd7myyC6KaBFCoaFqOyFQjqD1phPBIM8fn5qp8vdsOwBHxIwfGet5UUa9jtrhu0f8ZT1FgYF2Add9bHxh8mlzsrr0dBMYC6/87hfHkcj08hRZgfG4glDlgzjVHGa4Z3KoyhdBmbFH8GRHp6BxjwdJNn9eUkT3bbelphU/wsIplwMDWbAT2fdphiwCYxH87sAiBR44PsQqOnHuzC36IE7Hes+t4f6iUzHF0yDX7txB0b069dHXMW9crXIAGzKMPcBaMn9ojeX6Y5rRfMLCN8nMQa3Bd1xU5/bgpxbsSnOgybIuHPWxMHksbQlj5x+PsLFQLYwAF5/PLjHqrDPcnybh6cJYjMI46vYJXY+A20L14YjRbqsDkO5nlJxBmV2n4s8RPpmLGo="
  };
  let sync14pubkey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyuTplSewMZI+iEuW2tQsEI06ySCht2c7oS5GHQkmVjFW3EWZwBtV+XFhq4T/XM/bhVKHYEQ8h74V14i3zFG/1+g8oQprhls59LMX0W6Q4YZqJQhw4egoS74ZkEQWSCZpQQlpCPfO339OdSUAEHl6Gn63CO06aBrsSWZm4Ue6PtoBdUMjl9Wf6ig0DKAlBxTB3XOXeGzsgnWOMcjyPloSY+LH7gf7LT+79FDCvAX0K85Qyii2kN2ScEzAgGMrGFlK8f7CBBNnjR9RKiOZT0+EzX5DgH8FfBBbbZDgGYcU0bKS262XrmnSbMzhgEl2/bH2w4xRnNdRaE0Dk+Ggi37/SwIDAQAB";

  // A symmetric key that was wrapped with the above public key
  let symkeyData = "Vyrj8pzynsZ6+9LzLJx+myDogLMOboaBMrOi1I3Xa9tO/x5mkoGs7owQbsK1eTbfW3WUAsTpGaozzSmOR27QbVFxksSS38OZsVkT5SAmkwQ/MDPB4A6kJ7fedyTru8o/2O/XGMEQM3Qp5ApE/DJkITWPAx1U3K1f21vV7Mo4dkKkgHCNjsTMplbcA77+EyRLBaQtMaBuJhUi91RLSwKzqpVU0MdmWxgjhv+7+hd76idlaxNDMsAk1n13rYI6Sv6aQ1jqTBBsD7YEa1sShJadB9NxOnl2sugPuEqqnQN6Emb8R2Ok59NzoY+Zhk4n3QkoDeWgJSa96sQbzVa3M88ZVg==";
  let symkey = {keyring:{}};
  symkey.keyring[pubkeyUri] = {
    wrapped: symkeyData,
    hmac: "f0f5f4e4ee08dfed196455496e67af97fcf80cbc425ce8e753591023d3194635"
  };

  // Hook these keys into the Weave API.
  let privkey = new PrivKey(privkeyUri);
  privkey.salt = sync14privkey.salt;
  privkey.iv = sync14privkey.iv;
  privkey.keyData = sync14privkey.keyData;
  privkey.publicKeyUri = pubkeyUri;
  PrivKeys.set(privkeyUri, privkey);

  let pubkey = new PubKey(pubkeyUri);
  pubkey.keyData = sync14pubkey;
  pubkey.privateKeyUri = privkeyUri;
  PubKeys.defaultKeyUri = pubkeyUri;
  PubKeys.set(pubkeyUri, pubkey);

  Weave.Service.username = "foo";
  Weave.Service.clusterURL = "http://localhost:8080/";

  // Set up an engine whose bulk key we need to reupload.
  function SteamEngine() {
    Weave.SyncEngine.call(this, "Steam");
  }
  SteamEngine.prototype = Weave.SyncEngine.prototype;
  Weave.Engines.register(SteamEngine);

  // Set up an engine whose bulk key won't have the right HMAC, so we
  // need to wipe it.
  function PetrolEngine() {
    Weave.SyncEngine.call(this, "Petrol");
  }
  PetrolEngine.prototype = Weave.SyncEngine.prototype;
  Weave.Engines.register(PetrolEngine);
  let petrol_symkey = Weave.Utils.deepCopy(symkey);
  petrol_symkey.keyring[pubkeyUri].hmac = "definitely-not-the-right-HMAC";

  // Set up the server
  let server_privkey = new ServerWBO('privkey');
  let server_steam_key = new ServerWBO('steam', symkey);
  let server_petrol_key = new ServerWBO('petrol', petrol_symkey);
  let server_petrol_coll = new ServerCollection({
    'obj': new ServerWBO('obj', {somedata: "that's going", toget: "wiped"})
  });

  do_test_pending();
  let server = httpd_setup({
    // Need these to make Weave.Service.wipeRemote() etc. happy
    "/1.0/foo/storage/meta/global": new ServerWBO('global', {}).handler(),
    "/1.0/foo/storage/crypto/clients": new ServerWBO('clients', {}).handler(),
    "/1.0/foo/storage/clients": new ServerCollection().handler(),

    // Records to reupload
    "/1.0/foo/storage/keys/privkey": server_privkey.handler(),
    "/1.0/foo/storage/crypto/steam": server_steam_key.handler(),

    // Records that are going to be wiped
    "/1.0/foo/storage/crypto/petrol": server_petrol_key.handler(),
    "/1.0/foo/storage/petrol": server_petrol_coll.handler()
  });

  try {
    _("The original key can be decoded with both the low byte only and the full unicode passphrase.");
    do_check_true(Weave.Svc.Crypto.verifyPassphrase(sync14privkey.keyData,
                                                    passphrase,
                                                    sync14privkey.salt,
                                                    sync14privkey.iv));
    do_check_true(Weave.Svc.Crypto.verifyPassphrase(sync14privkey.keyData,
                                                    lowbyteonly,
                                                    sync14privkey.salt,
                                                    sync14privkey.iv));

    _("An obviously different passphrase will not work.");
    Weave.Service.passphrase = "something completely different";
    do_check_false(Weave.Service._verifyPassphrase());
    do_check_eq(server_privkey.payload, undefined);

    _("The right unicode passphrase will work, even though the key was generated with a low-byte only passphrase.");
    Weave.Service.passphrase = passphrase;
    do_check_true(Weave.Service._verifyPassphrase());

    _("The _needUpdatedKeys flag is set.");
    do_check_true(Weave.Service._needUpdatedKeys);

    _("We can now call updateKeysToUTF8Passphrase to trigger an upload of a new privkey.");
    Weave.Service._updateKeysToUTF8Passphrase();
    do_check_true(!!server_privkey.payload);
    do_check_eq(server_privkey.data.keyData, privkey.keyData);

    _("The new key can't be decoded with the raw passphrase but the UTF8 encoded one.");
    do_check_false(Weave.Svc.Crypto.verifyPassphrase(
      server_privkey.data.keyData, passphrase,
      server_privkey.data.salt, server_privkey.data.iv)
    );

    do_check_true(Weave.Svc.Crypto.verifyPassphrase(
      server_privkey.data.keyData, Weave.Utils.encodeUTF8(passphrase),
      server_privkey.data.salt, server_privkey.data.iv)
    );

    _("The 'steam' bulk key has been reuploaded (though only HMAC changes).");
    let server_wrapped_key = server_steam_key.data.keyring[pubkeyUri];
    do_check_eq(server_wrapped_key.wrapped, symkeyData);
    let hmacKey = Weave.Svc.KeyFactory.keyFromString(
        Ci.nsIKeyObject.HMAC, Weave.Utils.encodeUTF8(passphrase));
    let hmac = Weave.Utils.sha256HMAC(symkeyData, hmacKey);
    do_check_eq(server_wrapped_key.hmac, hmac);

    _("The 'petrol' bulk key had an incorrect HMAC to begin with, so it and all the data from that engine has been wiped.");
    do_check_eq(server_petrol_key.payload, undefined);
    do_check_eq(server_petrol_coll.wbos.obj.payload, undefined);

    _("The _needUpdatedKeys flag is no longer set.");
    do_check_false(Weave.Service._needUpdatedKeys);

  } finally {
    if (server)
      server.stop(do_test_finished);
    Weave.Svc.Prefs.resetBranch("");
  }
}
