bsrp
Version:
Secure Remote Password Protocol (SRP-6a) Implementation
156 lines (134 loc) • 5.53 kB
text/typescript
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();
});
});