import { BigInteger } from "jsbn";
import { APair, generateAPair, processChallenge, verifySession } from "./index";
import crypto from "crypto";

Object.defineProperty(global, "crypto", {
  value: {
    getRandomValues: (arr: Uint8Array): Uint8Array =>
      new Uint8Array(crypto.randomBytes(arr.length)),
  },
});

test("should generate distinct ephemeral A pairs on subsequent calls", () => {
  const firstAPair: APair = generateAPair();
  const secondAPair: APair = generateAPair();

  expect(firstAPair.ephemeralA.equals(secondAPair.ephemeralA)).toBeFalsy();
  expect(firstAPair.publicA.equals(secondAPair.publicA)).toBeFalsy();
});

/*
The following test against data generated by our backend
secure remote password (SRP) library found at
common.capitalrx.com/python/capitalrx_srp/lib/capitalrx_srp/server.py
*/

describe("should process the challenge and ", () => {
  const mockIdentity = "test";
  const mockPassword = "#yoloswag";
  const mockSalt = new BigInteger(
    "82309018978721799953398513360460893179767695404873354411100056397421052845665"
  );

  const mockPublicB = new BigInteger(
    `
    2168633872370722745670231052464515298895652280466586654940561279418192885446
    5569815697827449086706130654556981842259346317901572319578127690722060828257
    3518553531735879868022446812124213772126832091842123960193172642571089048102
    1733289687022334152421632949742565001244773176944756872042436192195957381164
    3090658931531623031352514968292009353084810965494415056390874498442382477900
    5882353129578106468292270918741874924193460980087907520441982126932668030685
    0090969045828991711574270619272362170264839084534189611581461513287496008879
    4917135704491740168013752189651036694080408335025658762869099665070041969775
    98773447`
  );

  const mockEphemeralA = new BigInteger(
    `
    4511537769169629456932317311882933072389518195547222946095076177846929590195
    1371663079183837225894296915421325322153782291026063288897303615301028637536
    4456708100501196659474191428313993281737569455237451851801736494448257151040
    4365649028849103887131660619405196304102087967082694857865126560813264823405
    2896370620643251731159847597082454921230612553999232050453350061287212947114
    3468309214437185337349903617563711682323708858174186538383127890364117859261
    0235971430683027990812743687636354509374067537368960261011404677986671649957
    1557511465995246567919633908251275514634723736321964939491245041138953850276
    87951603`
  );

  const mockPublicA = new BigInteger(
    `
    7723050516275197268596595028736303071422024349483662336212921812048310033512
    6572244050991069989463201236543019478742255154255342629510064426510389492901
    5314632288491039186285275840080928643682068266242876368549903623900851264362
    7570310166999141108388337926675047366656403001872884373870424306058994437632
    6182236026078347522175520387250862728453632132151431118882482079388402701874
    1521703855998269888412158021986972606977074552546346835095695193183456199335
    2853623091631714197790513583278068216773290743046592802328988338075991944028
    1512937113784396196375280239379337167801926867954676026661710324701047040250
    3411593`
  );

  const mockM = new BigInteger(
    "47331452883524291432813240675074597019503107827225066688099958715824381672937"
  );
  const mockSessionKey = new BigInteger(
    "56698353699494108654378460060782330526832508847563378289382973830724156337149"
  );

  const mockServerHAMK = new BigInteger(
    "112019802657940577633183024678428984798515249411730363524477901544479122837941"
  );

  test("respond with the expected message M to the server", async () => {
    const processedChallenge = await processChallenge(
      mockIdentity,
      mockPassword,
      mockSalt,
      mockEphemeralA,
      mockPublicA,
      mockPublicB
    );

    const { message, sessionKey } = processedChallenge;

    expect(message.equals(mockM)).toBeTruthy();
    expect(sessionKey.equals(mockSessionKey)).toBeTruthy();

    // Mutual authentication
    const clientHAMK = await verifySession(
      mockPublicA,
      message,
      sessionKey,
      mockServerHAMK
    );
    // verifySession already performs this check and will return null if verification fails, but alas do it here
    expect(clientHAMK.equals(mockServerHAMK)).toBeTruthy();
  });

  test("fail (SRP6a safety check) since B % prime = 0", async () => {
    // Causes the safety to fail
    const mockZeroPublicB = new BigInteger("0");

    const processedChallenge = await processChallenge(
      mockIdentity,
      mockPassword,
      mockSalt,
      mockEphemeralA,
      mockPublicA,
      mockZeroPublicB
    );
    // Safety failed
    expect(processedChallenge).toBeNull();
  });

  test("fail to mutually authenticate the server", async () => {
    // Causes the mutual auth to fail
    const mockWrongServerHAMK = new BigInteger(
      "482758924758974325893457983478954"
    );

    const processedChallenge = await processChallenge(
      mockIdentity,
      mockPassword,
      mockSalt,
      mockEphemeralA,
      mockPublicA,
      mockPublicB
    );

    const { message, sessionKey } = processedChallenge;

    expect(message.equals(mockM)).toBeTruthy();
    expect(sessionKey.equals(mockSessionKey)).toBeTruthy();

    // Mutual authentication
    const clientHAMK = await verifySession(
      mockPublicA,
      message,
      sessionKey,
      mockWrongServerHAMK
    );
    // Authentication of server failed
    expect(clientHAMK).toBeNull();
  });
});
