@wault-pw/srp6a-webcrypto
Version:
Pure javascript implementation of SRP-6a (RFC-5054, RFC-2945) using web-crypto
58 lines (48 loc) • 2.09 kB
text/typescript
import bigInt from "big-integer"
import {Params} from "./rfc5054"
import {BigInt2Uint8Array, BigIntFromInt, BigIntFromUint8Array, Hash} from "./util"
export class Engine {
readonly N: bigInt.BigInteger;
readonly g: bigInt.BigInteger;
readonly hash: string;
public get N_SIZE(): number {
return this.N.bitLength().toJSNumber() >> 3
}
constructor(params: Params) {
this.N = BigIntFromUint8Array(params.N)
this.g = BigIntFromInt(params.g)
this.hash = params.hash
}
async k(): Promise<bigInt.BigInteger> {
return BigIntFromUint8Array(await this.HASH(BigInt2Uint8Array(this.N), this.PAD(BigInt2Uint8Array(this.g))))
}
isModZero(a: bigInt.BigInteger, b: bigInt.BigInteger): boolean {
return a.mod(b).isZero()
}
async HASH(...inputs: (Uint8Array)[]): Promise<Uint8Array> {
return await Hash(this.hash, ...inputs)
}
// Conversion between integers and byte-strings assumes the most
// significant bytes are stored first, as per [TLS] and [SRP-RFC]. In
// the following text, if a conversion from integer to byte-string is
// implicit, the most significant byte in the resultant byte-string MUST
// be non-zero. If a conversion is explicitly specified with the
// operator PAD(), the integer will first be implicitly converted, then
// the resultant byte-string will be left-padded with zeros (if
// necessary) until its length equals the implicitly-converted length of
// N.
//
// In other words RFC5054 specifies that number should be
// left-padded with zeros to be the same length as N.
PAD(input: Uint8Array) {
if (input.length >= this.N_SIZE) {
return input
}
const diff = this.N_SIZE - input.length
return new Uint8Array([...new Uint8Array(diff), ...input])
}
async HASHED_CRED(username: string, password: string): Promise<Uint8Array> {
const credentials = new TextEncoder().encode(username + ":" + password)
return await this.HASH(credentials)
}
}