Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

tssrp6a

Package Overview
Dependencies
Maintainers
1
Versions
15
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

tssrp6a - npm Package Compare versions

Comparing version 2.0.0 to 3.0.0

dist/crossEnvCrypto.d.ts

8

dist/index.d.ts

@@ -1,5 +0,5 @@

export { SRPParameters } from "./parameters";
export { SRPParameters, HashFunction, PrimeGroup } from "./parameters";
export { SRPRoutines } from "./routines";
export { SRPClientSession } from "./session-client";
export { SRPServerSession } from "./session-server";
export { createVerifierAndSalt, IVerifierAndSalt, bigIntegerToWordArray, wordArrayToBigInt, generateRandomBigInt, } from "./utils";
export { SRPClientSession, SRPClientSessionStep1, SRPClientSessionStep1State, SRPClientSessionStep2, SRPClientSessionStep2State, } from "./session-client";
export { SRPServerSession, SRPServerSessionStep1, SRPServerSessionStep1State, } from "./session-server";
export { createVerifierAndSalt, IVerifierAndSalt, bigIntToArrayBuffer, arrayBufferToBigInt, generateRandomBigInt, } from "./utils";
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateRandomBigInt = exports.wordArrayToBigInt = exports.bigIntegerToWordArray = exports.createVerifierAndSalt = exports.SRPServerSession = exports.SRPClientSession = exports.SRPRoutines = exports.SRPParameters = void 0;
exports.generateRandomBigInt = exports.arrayBufferToBigInt = exports.bigIntToArrayBuffer = exports.createVerifierAndSalt = exports.SRPServerSessionStep1 = exports.SRPServerSession = exports.SRPClientSessionStep2 = exports.SRPClientSessionStep1 = exports.SRPClientSession = exports.SRPRoutines = exports.SRPParameters = void 0;
var parameters_1 = require("./parameters");

@@ -10,9 +10,12 @@ Object.defineProperty(exports, "SRPParameters", { enumerable: true, get: function () { return parameters_1.SRPParameters; } });

Object.defineProperty(exports, "SRPClientSession", { enumerable: true, get: function () { return session_client_1.SRPClientSession; } });
Object.defineProperty(exports, "SRPClientSessionStep1", { enumerable: true, get: function () { return session_client_1.SRPClientSessionStep1; } });
Object.defineProperty(exports, "SRPClientSessionStep2", { enumerable: true, get: function () { return session_client_1.SRPClientSessionStep2; } });
var session_server_1 = require("./session-server");
Object.defineProperty(exports, "SRPServerSession", { enumerable: true, get: function () { return session_server_1.SRPServerSession; } });
Object.defineProperty(exports, "SRPServerSessionStep1", { enumerable: true, get: function () { return session_server_1.SRPServerSessionStep1; } });
var utils_1 = require("./utils");
Object.defineProperty(exports, "createVerifierAndSalt", { enumerable: true, get: function () { return utils_1.createVerifierAndSalt; } });
Object.defineProperty(exports, "bigIntegerToWordArray", { enumerable: true, get: function () { return utils_1.bigIntegerToWordArray; } });
Object.defineProperty(exports, "wordArrayToBigInt", { enumerable: true, get: function () { return utils_1.wordArrayToBigInt; } });
Object.defineProperty(exports, "bigIntToArrayBuffer", { enumerable: true, get: function () { return utils_1.bigIntToArrayBuffer; } });
Object.defineProperty(exports, "arrayBufferToBigInt", { enumerable: true, get: function () { return utils_1.arrayBufferToBigInt; } });
Object.defineProperty(exports, "generateRandomBigInt", { enumerable: true, get: function () { return utils_1.generateRandomBigInt; } });
//# sourceMappingURL=index.js.map

@@ -1,32 +0,17 @@

import * as CryptoJS from "crypto-js";
export declare type HashWordArray = CryptoJS.LibWordArray;
export declare type HashingAlgorithm = "SHA1" | "SHA224" | "SHA256" | "SHA384" | "SHA512" | "SHA3" | "RIPEMD160";
export declare type HashingFn = (subjects: HashWordArray[]) => HashWordArray;
export interface PrimeGroup {
N: bigint;
g: bigint;
}
export declare type HashFunction = (data: ArrayBuffer) => Promise<ArrayBuffer>;
export declare class SRPParameters {
readonly N: bigint;
readonly g: bigint;
static N: {
"256": bigint;
"512": bigint;
"768": bigint;
"1024": bigint;
"1536": bigint;
"2048": bigint;
readonly primeGroup: PrimeGroup;
readonly H: HashFunction;
static PrimeGroup: {
[key: number]: PrimeGroup;
};
static g: bigint;
static H: {
SHA1: HashingAlgorithm;
SHA224: HashingAlgorithm;
SHA256: HashingAlgorithm;
SHA384: HashingAlgorithm;
SHA512: HashingAlgorithm;
SHA3: HashingAlgorithm;
RIPEMD160: HashingAlgorithm;
[key: string]: HashFunction;
};
readonly NBits: number;
readonly H: HashingFn;
readonly HBits: number;
constructor(N?: bigint, g?: bigint, H?: HashingAlgorithm | HashingFn);
hash(...array: (bigint | string)[]): bigint;
hashPadded(...array: bigint[]): bigint;
constructor(primeGroup?: PrimeGroup, H?: HashFunction);
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SRPParameters = void 0;
const CryptoJS = require("crypto-js");
const utils_1 = require("./utils");
const crossEnvCrypto_1 = require("./crossEnvCrypto");
class SRPParameters {
constructor(N = SRPParameters.N[2048], g = SRPParameters.g, H = SRPParameters.H.SHA512) {
this.N = N;
this.g = g;
this.NBits = this.N.toString(2).length;
if (typeof H === "function") {
this.H = H;
constructor(primeGroup = SRPParameters.PrimeGroup[2048], H = SRPParameters.H.SHA512) {
this.primeGroup = primeGroup;
this.H = H;
this.NBits = this.primeGroup.N.toString(2).length;
if (!H) {
throw new Error("Hash function required");
}
else {
const hasher = CryptoJS.algo[H];
if (!hasher || !(hasher instanceof CryptoJS.lib.Hasher.init)) {
throw new Error("Unknown hash function");
}
const hasherInstance = hasher.create();
this.H = (array) => {
hasherInstance.reset();
array.forEach((hwa) => hasherInstance.update(hwa));
return hasherInstance.finalize();
};
}
// Calculate size of hash output
this.HBits = utils_1.hashBitCount(this.H);
}
hash(...array) {
return utils_1.wordArrayToBigInt(this.H(array.map(element => {
if (typeof element === "bigint") {
return utils_1.bigIntegerToWordArray(element);
}
else if (typeof element === "string") {
return utils_1.stringToWordArray(element);
}
return element;
})));
}
hashPadded(...array) {
const targetLength = Math.trunc((this.NBits + 7) / 8);
return utils_1.wordArrayToBigInt(this.H(utils_1.padHashWordArray(targetLength, array.map(utils_1.bigIntegerToWordArray))));
}
}
exports.SRPParameters = SRPParameters;
SRPParameters.N = {
"256": BigInt("125617018995153554710546479714086468244499594888726646874671447258204721048803"),
"512": BigInt("11144252439149533417835749556168991736939157778924947037200268358613863350040339017097790259154750906072491181606044774215413467851989724116331597513345603"),
"768": BigInt("1087179135105457859072065649059069760280540086975817629066444682366896187793570736574549981488868217843627094867924800342887096064844227836735667168319981288765377499806385489913341488724152562880918438701129530606139552645689583147"),
"1024": BigInt("167609434410335061345139523764350090260135525329813904557420930309800865859473551531551523800013916573891864789934747039010546328480848979516637673776605610374669426214776197828492691384519453218253702788022233205683635831626913357154941914129985489522629902540768368409482248290641036967659389658897350067939"),
"1536": BigInt("1486998185923128292816507353619409521152457662596380074614818966810244974827752411420380336514078832314731499938313197533147998565301020797040787428051479639316928015998415709101293902971072960487527411068082311763171549170528008620813391411445907584912865222076100726050255271567749213905330659264908657221124284665444825474741087704974475795505492821585749417639344967192301749033325359286273431675492866492416941152646940908101472416714421046022696100064262587"),
"2048": BigInt("21766174458617435773191008891802753781907668374255538511144643224689886235383840957210909013086056401571399717235807266581649606472148410291413364152197364477180887395655483738115072677402235101762521901569820740293149529620419333266262073471054548368736039519702486226506248861060256971802984953561121442680157668000761429988222457090413873973970171927093992114751765168063614761119615476233422096442783117971236371647333871414335895773474667308967050807005509320424799678417036867928316761272274230314067548291133582479583061439577559347101961771406173684378522703483495337037655006751328447510550299250924469288819"),
SRPParameters.PrimeGroup = {
256: {
N: BigInt("125617018995153554710546479714086468244499594888726646874671447258204721048803"),
g: BigInt(2),
},
512: {
N: BigInt("11144252439149533417835749556168991736939157778924947037200268358613863350040339017097790259154750906072491181606044774215413467851989724116331597513345603"),
g: BigInt(2),
},
768: {
N: BigInt("1087179135105457859072065649059069760280540086975817629066444682366896187793570736574549981488868217843627094867924800342887096064844227836735667168319981288765377499806385489913341488724152562880918438701129530606139552645689583147"),
g: BigInt(2),
},
1024: {
N: BigInt("167609434410335061345139523764350090260135525329813904557420930309800865859473551531551523800013916573891864789934747039010546328480848979516637673776605610374669426214776197828492691384519453218253702788022233205683635831626913357154941914129985489522629902540768368409482248290641036967659389658897350067939"),
g: BigInt(2),
},
1536: {
N: BigInt("1486998185923128292816507353619409521152457662596380074614818966810244974827752411420380336514078832314731499938313197533147998565301020797040787428051479639316928015998415709101293902971072960487527411068082311763171549170528008620813391411445907584912865222076100726050255271567749213905330659264908657221124284665444825474741087704974475795505492821585749417639344967192301749033325359286273431675492866492416941152646940908101472416714421046022696100064262587"),
g: BigInt(2),
},
2048: {
N: BigInt("21766174458617435773191008891802753781907668374255538511144643224689886235383840957210909013086056401571399717235807266581649606472148410291413364152197364477180887395655483738115072677402235101762521901569820740293149529620419333266262073471054548368736039519702486226506248861060256971802984953561121442680157668000761429988222457090413873973970171927093992114751765168063614761119615476233422096442783117971236371647333871414335895773474667308967050807005509320424799678417036867928316761272274230314067548291133582479583061439577559347101961771406173684378522703483495337037655006751328447510550299250924469288819"),
g: BigInt(2),
},
};
SRPParameters.g = BigInt("2");
SRPParameters.H = {
SHA1: "SHA1",
SHA224: "SHA224",
SHA256: "SHA256",
SHA384: "SHA384",
SHA512: "SHA512",
SHA3: "SHA3",
RIPEMD160: "RIPEMD160",
SHA1: crossEnvCrypto_1.crossEnvCrypto.hashFunctions.SHA1,
SHA256: crossEnvCrypto_1.crossEnvCrypto.hashFunctions.SHA256,
SHA384: crossEnvCrypto_1.crossEnvCrypto.hashFunctions.SHA384,
SHA512: crossEnvCrypto_1.crossEnvCrypto.hashFunctions.SHA512,
};
//# sourceMappingURL=parameters.js.map

@@ -13,7 +13,9 @@ import { SRPParameters } from "./parameters";

constructor(parameters: SRPParameters);
computeK(): bigint;
generateRandomSalt(numBytes?: number): bigint;
computeX(I: string, s: bigint, P: string): bigint;
computeXStep2(s: bigint, identityHash: bigint): bigint;
computeIdentityHash(_: string, P: string): bigint;
hash(...as: ArrayBuffer[]): Promise<ArrayBuffer>;
hashPadded(...as: ArrayBuffer[]): Promise<ArrayBuffer>;
computeK(): Promise<bigint>;
generateRandomSalt(numBytes?: number): Promise<bigint>;
computeX(I: string, s: bigint, P: string): Promise<bigint>;
computeXStep2(s: bigint, identityHash: ArrayBuffer): Promise<bigint>;
computeIdentityHash(_: string, P: string): Promise<ArrayBuffer>;
computeVerifier(x: bigint): bigint;

@@ -23,6 +25,6 @@ generatePrivateValue(): bigint;

isValidPublicValue(value: bigint): boolean;
computeU(A: bigint, B: bigint): bigint;
computeClientEvidence(_I: string, _s: bigint, A: bigint, B: bigint, S: bigint): bigint;
computeServerEvidence(A: bigint, M1: bigint, S: bigint): bigint;
computeU(A: bigint, B: bigint): Promise<bigint>;
computeClientEvidence(_I: string, _s: bigint, A: bigint, B: bigint, S: bigint): Promise<bigint>;
computeServerEvidence(A: bigint, M1: bigint, S: bigint): Promise<bigint>;
computeClientSessionKey(k: bigint, x: bigint, u: bigint, a: bigint, B: bigint): bigint;
}
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SRPRoutines = void 0;
const bigint_mod_arith_1 = require("bigint-mod-arith");
const utils_1 = require("./utils");

@@ -18,22 +26,40 @@ /**

}
hash(...as) {
return utils_1.hash(this.parameters, ...as);
}
hashPadded(...as) {
const targetLength = Math.trunc((this.parameters.NBits + 7) / 8);
return utils_1.hashPadded(this.parameters, targetLength, ...as);
}
computeK() {
return (this.parameters.hashPadded(this.parameters.N, this.parameters.g));
return __awaiter(this, void 0, void 0, function* () {
return utils_1.arrayBufferToBigInt(yield this.hashPadded(utils_1.bigIntToArrayBuffer(this.parameters.primeGroup.N), utils_1.bigIntToArrayBuffer(this.parameters.primeGroup.g)));
});
}
generateRandomSalt(numBytes) {
// Recommended salt bytes is > than Hash output bytes. We default to twice
// the bytes used by the hash
const saltBytes = numBytes || (2 * this.parameters.HBits) / 8;
return utils_1.generateRandomBigInt(saltBytes);
return __awaiter(this, void 0, void 0, function* () {
const HBits = yield utils_1.hashBitCount(this.parameters);
// Recommended salt bytes is > than Hash output bytes. We default to twice
// the bytes used by the hash
const saltBytes = numBytes || (2 * HBits) / 8;
return utils_1.generateRandomBigInt(saltBytes);
});
}
computeX(I, s, P) {
return this.parameters.hash(s, this.computeIdentityHash(I, P));
return __awaiter(this, void 0, void 0, function* () {
return utils_1.arrayBufferToBigInt(yield this.hash(utils_1.bigIntToArrayBuffer(s), yield this.computeIdentityHash(I, P)));
});
}
computeXStep2(s, identityHash) {
return this.parameters.hash(s, identityHash);
return __awaiter(this, void 0, void 0, function* () {
return utils_1.arrayBufferToBigInt(yield this.hash(utils_1.bigIntToArrayBuffer(s), identityHash));
});
}
computeIdentityHash(_, P) {
return this.parameters.hash(P);
return __awaiter(this, void 0, void 0, function* () {
return yield this.hash(utils_1.stringToArrayBuffer(P));
});
}
computeVerifier(x) {
return bigint_mod_arith_1.modPow(this.parameters.g, x, this.parameters.N);
return utils_1.modPow(this.parameters.primeGroup.g, x, this.parameters.primeGroup.N);
}

@@ -44,3 +70,3 @@ generatePrivateValue() {

do {
bi = utils_1.generateRandomBigInt(numBits / 8) % this.parameters.N;
bi = utils_1.generateRandomBigInt(numBits / 8) % this.parameters.primeGroup.N;
} while (bi === BigInt(0));

@@ -50,20 +76,27 @@ return bi;

computeClientPublicValue(a) {
return bigint_mod_arith_1.modPow(this.parameters.g, a, this.parameters.N);
return utils_1.modPow(this.parameters.primeGroup.g, a, this.parameters.primeGroup.N);
}
isValidPublicValue(value) {
return value % this.parameters.N !== BigInt(0);
return value % this.parameters.primeGroup.N !== BigInt(0);
}
computeU(A, B) {
return this.parameters.hashPadded(A, B);
return __awaiter(this, void 0, void 0, function* () {
return utils_1.arrayBufferToBigInt(yield this.hashPadded(utils_1.bigIntToArrayBuffer(A), utils_1.bigIntToArrayBuffer(B)));
});
}
computeClientEvidence(_I, _s, A, B, S) {
return (this.parameters.hash((A), (B), (S)));
return __awaiter(this, void 0, void 0, function* () {
return utils_1.arrayBufferToBigInt(yield this.hash(utils_1.bigIntToArrayBuffer(A), utils_1.bigIntToArrayBuffer(B), utils_1.bigIntToArrayBuffer(S)));
});
}
computeServerEvidence(A, M1, S) {
return (this.parameters.hash((A), (M1), (S)));
return __awaiter(this, void 0, void 0, function* () {
return utils_1.arrayBufferToBigInt(yield this.hash(utils_1.bigIntToArrayBuffer(A), utils_1.bigIntToArrayBuffer(M1), utils_1.bigIntToArrayBuffer(S)));
});
}
computeClientSessionKey(k, x, u, a, B) {
const N = this.parameters.primeGroup.N;
const exp = u * x + a;
const tmp = bigint_mod_arith_1.modPow(this.parameters.g, x, this.parameters.N) * k;
return bigint_mod_arith_1.modPow(B - tmp, exp, this.parameters.N);
const tmp = (utils_1.modPow(this.parameters.primeGroup.g, x, N) * k) % N;
return utils_1.modPow(B + N - tmp, exp, N);
}

@@ -70,0 +103,0 @@ }

@@ -5,19 +5,82 @@ import { SRPRoutines } from "./routines";

constructor(routines: SRPRoutines);
step1(userId: string, userPassword: string): SRPClientSessionStep1;
step1(
/**
* User identity
*/
userId: string,
/**
* User password (not kept in state)
*/
userPassword: string): Promise<SRPClientSessionStep1>;
}
declare class SRPClientSessionStep1 {
export declare type SRPClientSessionStep1State = {
I: string;
IH: Array<number>;
};
export declare class SRPClientSessionStep1 {
private readonly routines;
/**
* User identity
*/
private readonly I;
readonly IH: bigint;
constructor(routines: SRPRoutines, I: string, IH: bigint);
step2(salt: bigint, B: bigint): SRPClientSessionStep2;
/**
* User identity/password hash
*/
readonly IH: ArrayBuffer;
constructor(routines: SRPRoutines,
/**
* User identity
*/
I: string,
/**
* User identity/password hash
*/
IH: ArrayBuffer);
step2(
/**
* Some generated salt (see createVerifierAndSalt)
*/
salt: bigint,
/**
* Server public key "B"
*/
B: bigint): Promise<SRPClientSessionStep2>;
toJSON(): SRPClientSessionStep1State;
static fromState(routines: SRPRoutines, state: SRPClientSessionStep1State): SRPClientSessionStep1;
}
declare class SRPClientSessionStep2 {
export declare type SRPClientSessionStep2State = {
A: string;
M1: string;
S: string;
};
export declare class SRPClientSessionStep2 {
private readonly routines;
/**
* Client public value "A"
*/
readonly A: bigint;
/**
* Client evidence message "M1"
*/
readonly M1: bigint;
/**
* Shared session key "S"
*/
readonly S: bigint;
constructor(routines: SRPRoutines, A: bigint, M1: bigint, S: bigint);
step3(M2: bigint): void;
constructor(routines: SRPRoutines,
/**
* Client public value "A"
*/
A: bigint,
/**
* Client evidence message "M1"
*/
M1: bigint,
/**
* Shared session key "S"
*/
S: bigint);
step3(M2: bigint): Promise<void>;
toJSON(): SRPClientSessionStep2State;
static fromState(routines: SRPRoutines, state: SRPClientSessionStep2State): SRPClientSessionStep2;
}
export {};
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SRPClientSession = void 0;
exports.SRPClientSessionStep2 = exports.SRPClientSessionStep1 = exports.SRPClientSession = void 0;
// Variable names match the RFC (I, IH, S, b, B, salt, b, A, M1, M2...)
class SRPClientSession {

@@ -8,11 +18,21 @@ constructor(routines) {

}
step1(userId, userPassword) {
if (!userId || !userId.trim()) {
throw new Error("User identity must not be null nor empty");
}
if (!userPassword) {
throw new Error("User password must not be null");
}
const IH = this.routines.computeIdentityHash(userId, userPassword);
return new SRPClientSessionStep1(this.routines, userId, IH);
step1(
/**
* User identity
*/
userId,
/**
* User password (not kept in state)
*/
userPassword) {
return __awaiter(this, void 0, void 0, function* () {
if (!userId || !userId.trim()) {
throw new Error("User identity must not be null nor empty");
}
if (!userPassword) {
throw new Error("User password must not be null");
}
const IH = yield this.routines.computeIdentityHash(userId, userPassword);
return new SRPClientSessionStep1(this.routines, userId, IH);
});
}

@@ -22,3 +42,11 @@ }

class SRPClientSessionStep1 {
constructor(routines, I, IH) {
constructor(routines,
/**
* User identity
*/
I,
/**
* User identity/password hash
*/
IH) {
this.routines = routines;

@@ -28,21 +56,51 @@ this.I = I;

}
step2(salt, B) {
if (!salt) {
throw new Error("Salt (s) must not be null");
}
if (!B) {
throw new Error("Public server value (B) must not be null");
}
const x = this.routines.computeXStep2(salt, this.IH);
const a = this.routines.generatePrivateValue();
const A = this.routines.computeClientPublicValue(a);
const k = this.routines.computeK();
const u = this.routines.computeU(A, B);
const S = this.routines.computeClientSessionKey(k, x, u, a, B);
const M1 = this.routines.computeClientEvidence(this.I, salt, A, B, S);
return new SRPClientSessionStep2(this.routines, A, M1, S);
step2(
/**
* Some generated salt (see createVerifierAndSalt)
*/
salt,
/**
* Server public key "B"
*/
B) {
return __awaiter(this, void 0, void 0, function* () {
if (!salt) {
throw new Error("Salt (s) must not be null");
}
if (!B) {
throw new Error("Public server value (B) must not be null");
}
// TODO can we run any of these promises in parallel?
const x = yield this.routines.computeXStep2(salt, this.IH);
const a = this.routines.generatePrivateValue();
const A = this.routines.computeClientPublicValue(a);
const k = yield this.routines.computeK();
const u = yield this.routines.computeU(A, B);
const S = this.routines.computeClientSessionKey(k, x, u, a, B);
const M1 = yield this.routines.computeClientEvidence(this.I, salt, A, B, S);
return new SRPClientSessionStep2(this.routines, A, M1, S);
});
}
toJSON() {
return { I: this.I, IH: Array.from(new Uint8Array(this.IH)) };
}
static fromState(routines, state) {
return new SRPClientSessionStep1(routines, state.I, new Uint8Array(state.IH).buffer);
}
}
exports.SRPClientSessionStep1 = SRPClientSessionStep1;
class SRPClientSessionStep2 {
constructor(routines, A, M1, S) {
constructor(routines,
/**
* Client public value "A"
*/
A,
/**
* Client evidence message "M1"
*/
M1,
/**
* Shared session key "S"
*/
S) {
this.routines = routines;

@@ -54,11 +112,24 @@ this.A = A;

step3(M2) {
if (!M2) {
throw new Error("Server evidence (M2) must not be null");
}
const computedM2 = this.routines.computeServerEvidence(this.A, this.M1, this.S);
if (computedM2 !== M2) {
throw new Error("Bad server credentials");
}
return __awaiter(this, void 0, void 0, function* () {
if (!M2) {
throw new Error("Server evidence (M2) must not be null");
}
const computedM2 = yield this.routines.computeServerEvidence(this.A, this.M1, this.S);
if (computedM2 !== M2) {
throw new Error("Bad server credentials");
}
});
}
toJSON() {
return {
A: this.A.toString(16),
M1: this.M1.toString(16),
S: this.S.toString(16),
};
}
static fromState(routines, state) {
return new SRPClientSessionStep2(routines, BigInt("0x" + state.A), BigInt("0x" + state.M1), BigInt("0x" + state.S));
}
}
exports.SRPClientSessionStep2 = SRPClientSessionStep2;
//# sourceMappingURL=session-client.js.map

@@ -5,18 +5,85 @@ import { SRPRoutines } from "./routines";

constructor(routines: SRPRoutines);
step1(identifier: string, salt: bigint, verifier: bigint): SRPServerSessionStep1;
step1(
/**
* User identity
*/
identifier: string,
/**
* User salt
*/
salt: bigint,
/**
* User verifier
*/
verifier: bigint): Promise<SRPServerSessionStep1>;
}
declare class SRPServerSessionStep1 {
export declare type SRPServerSessionStep1State = {
identifier: string;
salt: string;
verifier: string;
b: string;
B: string;
};
export declare class SRPServerSessionStep1 {
readonly routines: SRPRoutines;
/**
* User identity
*/
private readonly identifier;
/**
* User salt
*/
private readonly salt;
/**
* User verifier
*/
private readonly verifier;
/**
* Server private key "b"
*/
private readonly b;
/**
* Serve public key "B"
*/
readonly B: bigint;
constructor(routines: SRPRoutines, identifier: string, salt: bigint, verifier: bigint, b: bigint, B: bigint);
constructor(routines: SRPRoutines,
/**
* User identity
*/
identifier: string,
/**
* User salt
*/
salt: bigint,
/**
* User verifier
*/
verifier: bigint,
/**
* Server private key "b"
*/
b: bigint,
/**
* Serve public key "B"
*/
B: bigint);
/**
* Compute the session key "S" without computing or checking client evidence
*/
sessionKey(A: bigint): bigint;
step2(A: bigint, M1: bigint): bigint;
sessionKey(
/**
* Client public key "A"
*/
A: bigint): Promise<bigint>;
step2(
/**
* Client public key "A"
*/
A: bigint,
/**
* Client message "M1"
*/
M1: bigint): Promise<bigint>;
toJSON(): SRPServerSessionStep1State;
static fromState(routines: SRPRoutines, state: SRPServerSessionStep1State): SRPServerSessionStep1;
}
export {};
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SRPServerSession = void 0;
const bigint_mod_arith_1 = require("bigint-mod-arith");
exports.SRPServerSessionStep1 = exports.SRPServerSession = void 0;
const utils_1 = require("./utils");
// Variable names match the RFC (I, IH, S, b, B, salt, b, A, M1, M2...)
class SRPServerSession {

@@ -9,7 +19,21 @@ constructor(routines) {

}
step1(identifier, salt, verifier) {
const b = this.routines.generatePrivateValue();
const k = this.routines.computeK();
const B = computeServerPublicValue(this.routines.parameters, k, verifier, b);
return new SRPServerSessionStep1(this.routines, identifier, salt, verifier, b, B);
step1(
/**
* User identity
*/
identifier,
/**
* User salt
*/
salt,
/**
* User verifier
*/
verifier) {
return __awaiter(this, void 0, void 0, function* () {
const b = this.routines.generatePrivateValue();
const k = yield this.routines.computeK();
const B = computeServerPublicValue(this.routines.parameters, k, verifier, b);
return new SRPServerSessionStep1(this.routines, identifier, salt, verifier, b, B);
});
}

@@ -19,3 +43,23 @@ }

class SRPServerSessionStep1 {
constructor(routines, identifier, salt, verifier, b, B) {
constructor(routines,
/**
* User identity
*/
identifier,
/**
* User salt
*/
salt,
/**
* User verifier
*/
verifier,
/**
* Server private key "b"
*/
b,
/**
* Serve public key "B"
*/
B) {
this.routines = routines;

@@ -31,32 +75,62 @@ this.identifier = identifier;

*/
sessionKey(A) {
if (A === null) {
throw new Error("Client public value (A) must not be null");
}
if (!this.routines.isValidPublicValue(A)) {
throw new Error(`Invalid Client public value (A): ${A.toString(16)}`);
}
const u = this.routines.computeU(A, this.B);
const S = computeServerSessionKey(this.routines.parameters.N, this.verifier, u, A, this.b);
return S;
sessionKey(
/**
* Client public key "A"
*/
A) {
return __awaiter(this, void 0, void 0, function* () {
if (A === null) {
throw new Error("Client public value (A) must not be null");
}
if (!this.routines.isValidPublicValue(A)) {
throw new Error(`Invalid Client public value (A): ${A.toString(16)}`);
}
const u = yield this.routines.computeU(A, this.B);
const S = computeServerSessionKey(this.routines.parameters.primeGroup.N, this.verifier, u, A, this.b);
return S;
});
}
step2(A, M1) {
if (!M1) {
throw new Error("Client evidence (M1) must not be null");
}
const S = this.sessionKey(A);
const computedM1 = this.routines.computeClientEvidence(this.identifier, this.salt, A, this.B, S);
if (computedM1 !== M1) {
throw new Error("Bad client credentials");
}
const M2 = this.routines.computeServerEvidence(A, M1, S);
return M2;
step2(
/**
* Client public key "A"
*/
A,
/**
* Client message "M1"
*/
M1) {
return __awaiter(this, void 0, void 0, function* () {
if (!M1) {
throw new Error("Client evidence (M1) must not be null");
}
const S = yield this.sessionKey(A);
const computedM1 = yield this.routines.computeClientEvidence(this.identifier, this.salt, A, this.B, S);
if (computedM1 !== M1) {
throw new Error("Bad client credentials");
}
const M2 = this.routines.computeServerEvidence(A, M1, S);
return M2;
});
}
toJSON() {
return {
identifier: this.identifier,
salt: this.salt.toString(16),
verifier: this.verifier.toString(16),
b: this.b.toString(16),
B: this.B.toString(16),
};
}
static fromState(routines, state) {
return new SRPServerSessionStep1(routines, state.identifier, BigInt("0x" + state.salt), BigInt("0x" + state.verifier), BigInt("0x" + state.b), BigInt("0x" + state.B));
}
}
exports.SRPServerSessionStep1 = SRPServerSessionStep1;
const computeServerPublicValue = (parameters, k, v, b) => {
return (bigint_mod_arith_1.modPow(parameters.g, b, parameters.N) + v * k) % parameters.N;
return ((utils_1.modPow(parameters.primeGroup.g, b, parameters.primeGroup.N) + v * k) %
parameters.primeGroup.N);
};
const computeServerSessionKey = (N, v, u, A, b) => {
return bigint_mod_arith_1.modPow(bigint_mod_arith_1.modPow(v, u, N) * A, b, N);
return utils_1.modPow(utils_1.modPow(v, u, N) * A, b, N);
};
//# sourceMappingURL=session-server.js.map

@@ -1,12 +0,13 @@

import { HashingFn, HashWordArray } from "./parameters";
import { SRPParameters } from "./parameters";
import { SRPRoutines } from "./routines";
export declare const bigIntegerToWordArray: (n: bigint) => HashWordArray;
export declare const wordArrayToBigInt: (words: HashWordArray) => bigint;
export declare const bigIntToArrayBuffer: (n: bigint) => ArrayBuffer;
export declare const arrayBufferToBigInt: (arrayBuffer: ArrayBuffer) => bigint;
/**
* Convert some string into HashWordArray.
* Convert some string into ArrayBuffer.
* @param str Any UTF8 string, like a username, email, or password
*/
export declare function stringToWordArray(str: string): HashWordArray;
export declare function stringToArrayBuffer(str: string): ArrayBuffer;
/**
* Left pad HashWordArray with zeroes.
* Left pad ArrayBuffer with zeroes.
* @param arrayBuffer - ArrayBuffer to pad
* @param targetLength Length of the target array in bytes.

@@ -16,4 +17,5 @@ * @returns Padded array or original array if targetLength is less than original

*/
export declare const padWordArray: (words: HashWordArray, targetLength: number) => HashWordArray;
export declare function padHashWordArray(targetLen: number, arrays: HashWordArray[]): HashWordArray[];
export declare const padStartArrayBuffer: (arrayBuffer: ArrayBuffer, targetLength: number) => ArrayBuffer;
export declare function hash(parameters: SRPParameters, ...arrays: ArrayBuffer[]): Promise<ArrayBuffer>;
export declare function hashPadded(parameters: SRPParameters, targetLen: number, ...arrays: ArrayBuffer[]): Promise<ArrayBuffer>;
/**

@@ -26,3 +28,3 @@ * Generates random string of ASCII characters using crypto secure random generator.

export declare function generateRandomBigInt(numBytes?: number): bigint;
export declare function createVerifier(routines: SRPRoutines, I: string, s: bigint, P: string): bigint;
export declare function createVerifier(routines: SRPRoutines, I: string, s: bigint, P: string): Promise<bigint>;
export interface IVerifierAndSalt {

@@ -32,4 +34,10 @@ v: bigint;

}
export declare function createVerifierAndSalt(routines: SRPRoutines, I: string, P: string, sBytes?: number): IVerifierAndSalt;
export declare const hashBitCount: (hasher: HashingFn) => number;
export declare function createHashWordArray(words: number[], sigBytes: number): HashWordArray;
export declare function createVerifierAndSalt(routines: SRPRoutines, I: string, P: string, sBytes?: number): Promise<IVerifierAndSalt>;
export declare const hashBitCount: (parameters: SRPParameters) => Promise<number>;
/**
* Calculates (x**pow) % mod
* @param x base
* @param pow power
* @param mod modulo
*/
export declare function modPow(x: bigint, pow: bigint, mod: bigint): bigint;
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createHashWordArray = exports.hashBitCount = exports.createVerifierAndSalt = exports.createVerifier = exports.generateRandomBigInt = exports.generateRandomString = exports.padHashWordArray = exports.padWordArray = exports.stringToWordArray = exports.wordArrayToBigInt = exports.bigIntegerToWordArray = void 0;
const CryptoJS = require("crypto-js");
const bigIntegerToWordArray = (n) => CryptoJS.enc.Hex.parse(evenLengthHex(n.toString(16)));
exports.bigIntegerToWordArray = bigIntegerToWordArray;
const wordArrayToBigInt = (words) => BigInt(`0x${CryptoJS.enc.Hex.stringify(words)}`);
exports.wordArrayToBigInt = wordArrayToBigInt;
exports.modPow = exports.hashBitCount = exports.createVerifierAndSalt = exports.createVerifier = exports.generateRandomBigInt = exports.generateRandomString = exports.hashPadded = exports.hash = exports.padStartArrayBuffer = exports.stringToArrayBuffer = exports.arrayBufferToBigInt = exports.bigIntToArrayBuffer = void 0;
const crossEnvCrypto_1 = require("./crossEnvCrypto");
const ZERO = BigInt(0);
const ONE = BigInt(1);
const TWO = BigInt(2);
const bigIntToArrayBuffer = (n) => {
const hex = n.toString(16);
const arrayBuffer = new ArrayBuffer(Math.ceil(hex.length / 2));
const u8 = new Uint8Array(arrayBuffer);
let offset = 0;
// handle toString(16) not padding
if (hex.length % 2 !== 0) {
u8[0] = parseInt(hex[0], 16);
offset = 1;
}
for (let i = 0; i < arrayBuffer.byteLength; i++) {
u8[i + offset] = parseInt(hex.slice(2 * i + offset, 2 * i + 2 + offset), 16);
}
return arrayBuffer;
};
exports.bigIntToArrayBuffer = bigIntToArrayBuffer;
const arrayBufferToBigInt = (arrayBuffer) => {
const hex = [];
// we can't use map here because map will return Uint8Array which will screw up the parsing below
new Uint8Array(arrayBuffer).forEach((i) => {
hex.push(("0" + i.toString(16)).slice(-2)); // i.toString(16) will transform 01 to 1, so we add it back on and slice takes the last two chars
});
return BigInt(`0x${hex.join("")}`);
};
exports.arrayBufferToBigInt = arrayBufferToBigInt;
/**
* Convert some string into HashWordArray.
* Convert some string into ArrayBuffer.
* @param str Any UTF8 string, like a username, email, or password
*/
function stringToWordArray(str) {
return CryptoJS.enc.Utf8.parse(str);
function stringToArrayBuffer(str) {
return new TextEncoder().encode(str).buffer;
}
exports.stringToWordArray = stringToWordArray;
exports.stringToArrayBuffer = stringToArrayBuffer;
/**
* Left pad HashWordArray with zeroes.
* Left pad ArrayBuffer with zeroes.
* @param arrayBuffer - ArrayBuffer to pad
* @param targetLength Length of the target array in bytes.

@@ -23,33 +57,28 @@ * @returns Padded array or original array if targetLength is less than original

*/
const padWordArray = (words, targetLength) => {
let result = words;
if (targetLength > words.sigBytes) {
const resultWords = new Array(ceilDiv4(targetLength)).fill(0);
result = createHashWordArray(resultWords, targetLength);
for (let dest = targetLength - words.sigBytes, src = 0; src < words.sigBytes; ++src, ++dest) {
setByte(result, dest, getByte(words, src));
}
const padStartArrayBuffer = (arrayBuffer, targetLength) => {
const u8 = new Uint8Array(arrayBuffer);
if (u8.length < targetLength) {
const tmp = new Uint8Array(targetLength);
tmp.fill(0, 0, targetLength - u8.length);
tmp.set(u8, targetLength - u8.length);
return tmp;
}
return result;
return u8;
};
exports.padWordArray = padWordArray;
// export function hash(
// parameters: SRPParameters,
// ...arrays: HashWordArray[]
// ): HashWordArray {
// return parameters.H(arrays);
// }
//
// export function hashPadded(
// parameters: SRPParameters,
// targetLen: number,
// ...arrays: HashWordArray[]
// ): HashWordArray {
// const arraysPadded = arrays.map((hwa) => padWordArray(hwa, targetLen));
// return hash(parameters, ...arraysPadded);
// }
function padHashWordArray(targetLen, arrays) {
return arrays.map((hwa) => exports.padWordArray(hwa, targetLen));
exports.padStartArrayBuffer = padStartArrayBuffer;
function hash(parameters, ...arrays) {
const length = arrays.reduce((p, c) => p + c.byteLength, 0);
const target = new Uint8Array(length);
for (let offset = 0, i = 0; i < arrays.length; i++) {
target.set(new Uint8Array(arrays[i]), offset);
offset += arrays[i].byteLength;
}
return parameters.H(target);
}
exports.padHashWordArray = padHashWordArray;
exports.hash = hash;
function hashPadded(parameters, targetLen, ...arrays) {
const arraysPadded = arrays.map((arrayBuffer) => exports.padStartArrayBuffer(arrayBuffer, targetLen));
return hash(parameters, ...arraysPadded);
}
exports.hashPadded = hashPadded;
/**

@@ -61,80 +90,73 @@ * Generates random string of ASCII characters using crypto secure random generator.

function generateRandomString(characterCount = 10) {
const randomArray = generateRandom(characterCount);
for (let i = 0; i < randomArray.sigBytes; ++i) {
let asciiChar = getByte(randomArray, i) & 0x7f;
if (asciiChar < 32) {
asciiChar |= 32;
const u8 = new Uint8Array(Math.ceil(Math.ceil(characterCount / 2))); // each byte has 2 hex digits
crossEnvCrypto_1.crossEnvCrypto.randomBytes(u8);
return u8
.reduce((str, i) => {
const hex = i.toString(16).toString();
if (hex.length === 1) {
return str + "0" + hex;
}
if (asciiChar === 0x7f) {
asciiChar = 0x7e;
}
setByte(randomArray, i, asciiChar);
}
return CryptoJS.enc.Utf8.stringify(randomArray);
return str + hex;
}, "")
.slice(0, characterCount); // so we don't go over when characterCount is odd
}
exports.generateRandomString = generateRandomString;
function generateRandomBigInt(numBytes = 16) {
return exports.wordArrayToBigInt(generateRandom(numBytes));
return exports.arrayBufferToBigInt(generateRandom(numBytes));
}
exports.generateRandomBigInt = generateRandomBigInt;
function createVerifier(routines, I, s, P) {
if (!I || !I.trim()) {
throw new Error("Identity (I) must not be null or empty.");
}
if (!s) {
throw new Error("Salt (s) must not be null.");
}
if (!P) {
throw new Error("Password (P) must not be null");
}
const x = routines.computeX(I, s, P);
return routines.computeVerifier(x);
return __awaiter(this, void 0, void 0, function* () {
if (!I || !I.trim()) {
throw new Error("Identity (I) must not be null or empty.");
}
if (!s) {
throw new Error("Salt (s) must not be null.");
}
if (!P) {
throw new Error("Password (P) must not be null");
}
const x = yield routines.computeX(I, s, P);
return routines.computeVerifier(x);
});
}
exports.createVerifier = createVerifier;
function createVerifierAndSalt(routines, I, P, sBytes) {
const s = routines.generateRandomSalt(sBytes);
return {
s,
v: createVerifier(routines, I, s, P),
};
return __awaiter(this, void 0, void 0, function* () {
const s = yield routines.generateRandomSalt(sBytes);
return {
s,
v: yield createVerifier(routines, I, s, P),
};
});
}
exports.createVerifierAndSalt = createVerifierAndSalt;
const hashBitCount = (hasher) => hasher([exports.bigIntegerToWordArray(BigInt(1))]).sigBytes << 3;
const hashBitCount = (parameters) => __awaiter(void 0, void 0, void 0, function* () { return (yield hash(parameters, exports.bigIntToArrayBuffer(BigInt(1)))).byteLength * 8; });
exports.hashBitCount = hashBitCount;
// TODO: remove when constructor is exported in @types/crypto-js
function createHashWordArray(words, sigBytes) {
const result = CryptoJS.lib.WordArray.create(words);
result.sigBytes = sigBytes;
return result;
}
exports.createHashWordArray = createHashWordArray;
function evenLengthHex(hex) {
if (hex.length % 2 === 1) {
return `0${hex}`;
}
else {
return hex;
}
}
function ceilDiv4(x) {
return (x + 3) >>> 2;
}
/**
* Return the number of bits the byte should be shifted to occupy given position in 4 bytes integer.
* @param byteNum The number of byte in big endian order. Can be only 0, 1, 2 or 3
* Calculates (x**pow) % mod
* @param x base
* @param pow power
* @param mod modulo
*/
function byteShift(byteNum) {
return (3 - byteNum) << 3;
function modPow(x, pow, mod) {
let result = ONE;
while (pow > ZERO) {
if (pow % TWO == ONE) {
result = (x * result) % mod;
pow -= ONE;
}
else {
x = (x * x) % mod;
pow /= TWO;
}
}
return result;
}
function getByte(array, idx) {
return (array.words[idx >>> 2] >>> byteShift(idx & 3)) & 0xff;
}
function setByte(array, idx, byteValue) {
array.words[idx >>> 2] &= ~(0xff << byteShift(idx & 3));
array.words[idx >>> 2] |= (byteValue & 0xff) << byteShift(idx & 3);
}
exports.modPow = modPow;
const generateRandom = (numBytes) => {
// TODO: fix type of this function in @types/crypto-js
return CryptoJS.lib.WordArray.random(numBytes);
const u8 = new Uint8Array(numBytes);
crossEnvCrypto_1.crossEnvCrypto.randomBytes(u8);
return u8.buffer;
};
//# sourceMappingURL=utils.js.map
{
"name": "tssrp6a",
"version": "2.0.0",
"version": "3.0.0",
"main": "dist/index.js",

@@ -11,8 +11,5 @@ "files": [

"private": false,
"dependencies": {
"bigint-mod-arith": "^3.0.0",
"crypto-js": "^4.0.0"
},
"devDependencies": {
"@istanbuljs/nyc-config-typescript": "^1.0.1",
"@magic-works/ttypescript-browser-like-import-transformer": "^3.0.0",
"@types/crypto-js": "3.1.43",

@@ -23,2 +20,3 @@ "@types/jsbn": "1.2.29",

"@typescript-eslint/parser": "^4.23.0",
"@zoltu/typescript-transformer-append-js-extension": "^1.0.1",
"eslint": "^7.26.0",

@@ -31,3 +29,5 @@ "nyc": "^15.1.0",

"tape": "^5.2.2",
"tape-promise": "^4.0.0",
"ts-node": "^9.1.1",
"ttypescript": "^1.5.12",
"typescript": "^4.2.4"

@@ -37,7 +37,9 @@ },

"build": "yarn tsc --build tsconfig.json",
"test": "TS_NODE_COMPILER_OPTIONS='{\"types\": [\"node\"]}' yarn tape 'test/**/*.test.*' -r ts-node/register",
"build:esm": "yarn ttsc --project tsconfig.esm.json",
"test": "yarn tape 'test/**/*.test.*' -r ts-node/register",
"test:report": "TAPE_RAW_OUTPUT=1 yarn test | yarn tap-junit -o reports -n unit",
"nyc": "nyc -e .ts",
"nyc": "nyc -e .ts -x 'src/crossEnvCrypto.ts' -x 'test/**'",
"coverage": "yarn nyc yarn test:report",
"coverage:report": "yarn nyc --reporter cobertura --reporter html --report-dir reports/unit yarn test:report",
"coverage:number": "yarn coverage | grep 'All files' | cut -d '|' -f 5 | sed 's/ //g'",
"lint_formatter": "prettier -c 'src/**' 'test/**'",

@@ -44,0 +46,0 @@ "lint": "yarn lint_formatter && yarn eslint src test",

@@ -5,3 +5,3 @@ # Midokura TSSRP6a

[![CI](https://github.com/midonet/tssrp6a/actions/workflows/main.yml/badge.svg)](https://github.com/midonet/tssrp6a/actions/workflows/main.yml)
[![badge](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/bgrosse-midokura/6d88c7cb89cc67292dc093e5d7bcede3/raw/tssrp6a-coverage-badge.json)](https://midonet.github.io/tssrp6a/)
[![badge](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/bgrosse-midokura/6d88c7cb89cc67292dc093e5d7bcede3/raw/tssrp6a-coverage-badge.json)](https://midonet.github.io/tssrp6a/coverage)

@@ -20,2 +20,4 @@ This library is a TypeScript implementation of [Secure Remote Password](http://srp.stanford.edu/) SRP6a.

You can see a real-time [demo here](https://midonet.github.io/tssrp6a/demo).
## Requirements & Dependencies

@@ -25,5 +27,6 @@

Needs [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) native support, or a polyfill which is not included.
This package has zero dependencies. It only needs [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt)
native support, or a polyfill which is not included.
The only dependencies are [crypto-js](https://www.npmjs.com/package/crypto-js) and [bigint-mod-arith](https://www.npmjs.com/package/bigint-mod-arith).
**Note**: This module makes use of [`Crypto.subtle`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/subtle) and therefore only works on HTTPS.

@@ -42,12 +45,13 @@ ## Usage

} from "tssrp6a"
const srp6aNimbusRoutines = new SRPRoutines(new SRPParameters());
const userId = "hello@world.org";
const userPassword = "password";
const { s: salt, v: verifier } = createVerifierAndSalt(
srp6aNimbusRoutines,
userId,
userPassword,
);
// store salt and verifier in a data base
(async ()=> {
const srp6aNimbusRoutines = new SRPRoutines(new SRPParameters());
const userId = "hello@world.org";
const userPassword = "password";
const { s: salt, v: verifier } = await createVerifierAndSalt(
srp6aNimbusRoutines,
userId,
userPassword,
);
// store salt and verifier in a database
})()
```

@@ -78,35 +82,37 @@

const srp6aNimbusRoutines = new SRPRoutines(new SRPParameters());
(async ()=> {
const srp6aNimbusRoutines = new SRPRoutines(new SRPParameters());
const username = "hello@world.org";
let password = "password";
const username = "hello@world.org";
let password = "password";
// Sign up
const { s: salt, v: verifier } = createVerifierAndSalt(
srp6aNimbusRoutines,
username,
password,
);
const {s: salt, v: verifier} = await createVerifierAndSalt(
srp6aNimbusRoutines,
username,
password,
);
// Sign in
const srp6aNimbusClient = new SRPClientSession(srp6aNimbusRoutines);
srp6aNimbusClient.step1(username, password);
const srp6aNimbusClient = new SRPClientSession(srp6aNimbusRoutines);
await srp6aNimbusClient.step1(username, password);
// erase password at this point, it is no longer stored
password = ""
password = ""
const server = new SRPServerSession(srp6aNimbusRoutines);
const server = new SRPServerSession(srp6aNimbusRoutines);
// server gets identifier from client, salt+verifier from db (from signup)
const B = server.step1(username, salt, verifier);
const B = await server.step1(username, salt, verifier);
// client gets challenge B from server step1 and sends prove M1 to server
const { A, M1 } = srp6aNimbusClient.step2(salt, B);
const {A, M1} = await srp6aNimbusClient.step2(salt, B);
// servers checks client prove M1 and sends server prove M2 to client
const M2 = server.step2(A, M1);
const M2 = await server.step2(A, M1);
// client ensures server identity
srp6aNimbusClient.step3(M2);
await srp6aNimbusClient.step3(M2);
})()
```
## Recomendations
## Recommendations

@@ -117,4 +123,31 @@ SRP alone only prevents a man-in-the-middle attack from _reading_ the password, but such an attack could also inject code into the browser to hijack the password.

The client can chose to exclude the identity of its computations or not. If excluded, the id cannot be changed. But this problem is better solved by an application schema that separates "identity" from "authentication", so that one identity can have multiple authentications. This allows to switch identity + password, and also to user more than one way of logging in (think "login with email+password, google, or facebook").
The client can choose to exclude the identity of its computations or not. If excluded, the id cannot be changed. But this problem is better solved by an application schema that separates "identity" from "authentication", so that one identity can have multiple authentications. This allows to switch identity + password, and also to user more than one way of logging in (think "login with email+password, google, or facebook").
## Serialization
The SRP protocol and therefore this library is stateful. Each step sets various internal state. Due to the randomness of some of this state (namely the public and private values), repeating the step methods with the same arguments is unlikely (almost definitely) to result in the same state. This proves to be an issue when using a stateless protocol such as HTTP (as opposed to websockets). The server "session" state (the server step 1 state) might not be easily kept in memory. Therefore, we provide a way to serialize and deserialize the step classes in order to restore state. [serialize.test.ts](test/serialize.test.ts) shows some examples here's an explanation of how it works:
```typescript
const serverStep1 = await new SRPServerSession(TEST_ROUTINES).step1(...); // Each step returns a class, in this case .step1 returns SRPServerSessionStep1
const serializedServerStep1 = JSON.stringify(serverStep1); // Some of the step methods (see below for which ones) have a .toJSON method that returns the internal state. JSON.stringify calls .toJSON
// you can now store serializedServerStep1 in a database or elsewhere. There are security implications, see below.
// when you are ready to restore the state, call fromState on the same step class used to serialize the data (in this case SRPServerSessionStep1) to deserialize
const deserializedServerStep1 = SRPServerSessionStep1.fromState(
TEST_ROUTINES, // first param is the routines
JSON.parse(serializedServerStep1),
);
// deserializedServerStep1 is now functionally equivilent as serverStep1 because it contains the same state
```
Supported steps/classes for serialization are:
- `SRPServerSessionStep1`
- `SRPClientSessionStep1`
- `SRPClientSessionStep2`
While the password is **never** kept directly in the state, hashes of it are. If an adversary is able to access the serialized state it will likely open you up to some kind of MITM attack and depending on the step, may allow an attacker to perform a bruteforce and/or dictionary attack to retrieve the password. **Do not expose the serialized data.** For clients, this means do not send it over the network and be careful where you store it. For servers, only send it in encrypted form to parties you trust (such as your database). If you believe state at anytime may have been exposed, it is suggested you change passwords as soon as possible.
## Notes

@@ -121,0 +154,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc