bsrp
Version:
Secure Remote Password Protocol (SRP-6a) Implementation
110 lines (87 loc) • 2.42 kB
text/typescript
import { BigInteger } from "jsbn";
import * as utils from "./utils";
import * as constants from "./constants";
const {
generateRandomBytes,
hash,
toBigInteger,
pad,
calculateM,
calculateX,
} = utils;
const { generator, prime } = constants;
export { constants };
export { toBigInteger };
export { utils };
export interface APair {
ephemeralA: BigInteger;
publicA: BigInteger;
}
export const generateAPair = (): APair => {
const ephemeralA = toBigInteger(generateRandomBytes(32));
const publicA = generator.modPow(ephemeralA, prime);
return {
ephemeralA: ephemeralA,
publicA: publicA,
};
};
export interface ProcessedChallenge {
message: BigInteger;
sessionKey: BigInteger;
}
export const processChallenge = async (
identity: string,
password: string,
salt: BigInteger,
ephemeralA: BigInteger,
publicA: BigInteger,
publicB: BigInteger
): Promise<null | ProcessedChallenge> => {
const width = Math.floor((prime.bitLength() + 7) / 8);
const paddedGenerator = pad(generator, width);
const paddedPublicA = pad(publicA, width);
const paddedPublicB = pad(publicB, width);
// RFC 5054 u
const scrambler = toBigInteger(await hash(paddedPublicA, paddedPublicB));
const x = await calculateX(salt, identity, password);
// RFC 5054 k
const multiplier = toBigInteger(await hash(prime, paddedGenerator));
// SRP-6a safety checks
if (publicB.mod(prime).equals(BigInteger.ZERO)) {
return null;
}
if (scrambler.equals(BigInteger.ZERO)) {
return null;
}
// Premaster secret, S = t1: (B - k*(generator^x)) ^ t2: (a + u*x)
const t1 = publicB.subtract(multiplier.multiply(generator.modPow(x, prime)));
const t2 = ephemeralA.add(scrambler.multiply(x));
// Calculate shared session key
const S = t1.modPow(t2, prime);
const sessionKey = await hash(S);
const message = await calculateM(
generator,
prime,
identity,
salt,
publicA,
publicB,
sessionKey
);
return {
message: toBigInteger(message),
sessionKey: toBigInteger(sessionKey),
};
};
export const verifySession = async (
publicA: BigInteger,
message: BigInteger,
sessionKey: BigInteger,
serverHAMK: BigInteger
): Promise<BigInteger | null> => {
const clientHAMK = await hash(publicA, message, sessionKey);
if (!toBigInteger(clientHAMK).equals(serverHAMK)) {
return null;
}
return toBigInteger(clientHAMK);
};