1
0
mirror of https://github.com/danog/fast-srp.git synced 2024-11-26 20:04:49 +01:00

Setting up ESLint

This commit is contained in:
Supereg 2020-04-12 19:30:08 +02:00
parent 462b688de4
commit b91cb6fe8a
No known key found for this signature in database
GPG Key ID: 2F08948C5653D720
13 changed files with 1509 additions and 319 deletions

32
.eslintrc Normal file
View File

@ -0,0 +1,32 @@
{
"parser": "@typescript-eslint/parser",
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended" // uses the recommended rules from the @typescript-eslint/eslint-plugin
],
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module"
},
"ignorePatterns": [
"bin/",
"lib/",
"jsbn"
],
"rules": {
"quotes": ["error", "double"],
"indent": ["error", 2, { "SwitchCase": 1 }],
"linebreak-style": ["error", "unix"],
"semi": ["error", "always"],
"comma-dangle": ["error", "always-multiline"],
"dot-notation": "error",
"eqeqeq": "error",
"curly": ["error", "all"],
"brace-style": ["error"],
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/camelcase": "off"
}
}

View File

@ -23,11 +23,20 @@ jobs:
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
- name: npm install, build and test - name: Run npm ci
run: | run: npm ci
npm ci env:
npm run build --if-present CI: true
npm test - name: Run npm build
run: npm build
env:
CI: true
- name: Run npm lint
run: npm lint
env:
CI: true
- name: Run npm test
run: npm test
env: env:
CI: true CI: true

View File

@ -1,4 +1,4 @@
fast-srp fast-srp-hap
=== ===
Is a pure [NodeJS](https://nodejs.org/) implementation of the [SRP6a protocol](http://srp.stanford.edu/). Is a pure [NodeJS](https://nodejs.org/) implementation of the [SRP6a protocol](http://srp.stanford.edu/).

32
jsbn/jsbn.d.ts vendored
View File

@ -1,23 +1,23 @@
declare class BigInteger { declare class BigInteger {
constructor(number: number | string, base?: number); constructor(number: number | string, base?: number);
constructor(number: Buffer); constructor(number: Buffer);
constructor(number: unknown); constructor(number: unknown);
toString(base?: number): string; toString(base?: number): string;
toBuffer(trimOrSize?: true | number): Buffer; toBuffer(trimOrSize?: true | number): Buffer;
/** @return {number} 1 if this > a, -1 if this < a, 0 if equal */ /** @return {number} 1 if this > a, -1 if this < a, 0 if equal */
compareTo(a: BigInteger): -1 | 0 | 1; compareTo(a: BigInteger): -1 | 0 | 1;
multiply(b: BigInteger): BigInteger;
add(b: BigInteger | number): BigInteger;
subtract(b: BigInteger): BigInteger;
modPow(b: BigInteger, N: BigInteger): BigInteger;
mod(b: BigInteger): BigInteger;
bitLength(): number; multiply(b: BigInteger): BigInteger;
add(b: BigInteger | number): BigInteger;
subtract(b: BigInteger): BigInteger;
modPow(b: BigInteger, N: BigInteger): BigInteger;
mod(b: BigInteger): BigInteger;
bitLength(): number;
} }
export = BigInteger; export = BigInteger;

1113
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -5,10 +5,11 @@
"main": "lib/srp.js", "main": "lib/srp.js",
"scripts": { "scripts": {
"clean": "rm -rf lib", "clean": "rm -rf lib",
"lint": "eslint 'src/**/*.{js,ts,json}'",
"build": "npm run clean && tsc", "build": "npm run clean && tsc",
"test": "vows lib/test/test_*.js --spec",
"prepublishOnly": "npm run build", "prepublishOnly": "npm run build",
"postpublish": "npm run clean", "postpublish": "npm run clean"
"test": "vows lib/test/test_*.js --spec"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -30,6 +31,9 @@
"devDependencies": { "devDependencies": {
"@types/node": "^10.17.19", "@types/node": "^10.17.19",
"typescript": "^3.8.3", "typescript": "^3.8.3",
"vows": "^0.8.3" "vows": "^0.8.3",
"eslint": "^6.8.0",
"@typescript-eslint/parser": "^2.27.0",
"@typescript-eslint/eslint-plugin": "^2.27.0"
} }
} }

View File

@ -14,10 +14,10 @@
// since these are meant to be used internally, all values are numbers. If // since these are meant to be used internally, all values are numbers. If
// you want to add parameter sets, you'll need to convert them to bignums. // you want to add parameter sets, you'll need to convert them to bignums.
import BigInteger = require('../jsbn/jsbn'); import BigInteger = require("../jsbn/jsbn");
export function hex(s: string) { export function hex(s: string): BigInteger {
return new BigInteger(s.split(/\s|\n/).join(''), 16); return new BigInteger(s.split(/\s|\n/).join(""), 16);
} }
export interface SrpParams { export interface SrpParams {
@ -37,8 +37,8 @@ export const params = {
7BCF1885 C529F566 660E57EC 68EDBC3C 05726CC0 2FD4CBF4 976EAA9A 7BCF1885 C529F566 660E57EC 68EDBC3C 05726CC0 2FD4CBF4 976EAA9A
FD5138FE 8376435B 9FC61D2F C0EB06E3 FD5138FE 8376435B 9FC61D2F C0EB06E3
`), `),
g: hex('02'), g: hex("02"),
hash: 'sha1', hash: "sha1",
}, },
1536: { 1536: {
@ -52,8 +52,8 @@ export const params = {
F7CCB7AE 837C264A E3A9BEB8 7F8A2FE9 B8B5292E 5A021FFF 5E91479E F7CCB7AE 837C264A E3A9BEB8 7F8A2FE9 B8B5292E 5A021FFF 5E91479E
8CE7A28C 2442C6F3 15180F93 499A234D CF76E3FE D135F9BB 8CE7A28C 2442C6F3 15180F93 499A234D CF76E3FE D135F9BB
`), `),
g: hex('02'), g: hex("02"),
hash: 'sha1', hash: "sha1",
}, },
2048: { 2048: {
@ -70,8 +70,8 @@ export const params = {
94B5C803 D89F7AE4 35DE236D 525F5475 9B65E372 FCD68EF2 0FA7111F 94B5C803 D89F7AE4 35DE236D 525F5475 9B65E372 FCD68EF2 0FA7111F
9E4AFF73 9E4AFF73
`), `),
g: hex('02'), g: hex("02"),
hash: 'sha256', hash: "sha256",
}, },
3072: { 3072: {
@ -92,8 +92,8 @@ export const params = {
BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC
E0FD108E 4B82D120 A93AD2CA FFFFFFFF FFFFFFFF E0FD108E 4B82D120 A93AD2CA FFFFFFFF FFFFFFFF
`), `),
g: hex('05'), g: hex("05"),
hash: 'sha256', hash: "sha256",
}, },
hap: { hap: {
@ -114,8 +114,8 @@ export const params = {
BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC
E0FD108E 4B82D120 A93AD2CA FFFFFFFF FFFFFFFF E0FD108E 4B82D120 A93AD2CA FFFFFFFF FFFFFFFF
`), `),
g: hex('05'), g: hex("05"),
hash: 'sha512', hash: "sha512",
}, },
4096: { 4096: {
@ -141,8 +141,8 @@ export const params = {
D5B05AA9 93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34063199 D5B05AA9 93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34063199
FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
`), `),
g: hex('05'), g: hex("05"),
hash: 'sha256', hash: "sha256",
}, },
6244: { 6244: {
@ -177,8 +177,8 @@ export const params = {
387FE8D7 6E3C0468 043E8F66 3F4860EE 12BF2D5B 0B7474D6 E694F91E 387FE8D7 6E3C0468 043E8F66 3F4860EE 12BF2D5B 0B7474D6 E694F91E
6DCC4024 FFFFFFFF FFFFFFFF 6DCC4024 FFFFFFFF FFFFFFFF
`), `),
g: hex('05'), g: hex("05"),
hash: 'sha256', hash: "sha256",
}, },
8192: { 8192: {
@ -222,8 +222,8 @@ export const params = {
FC026E47 9558E447 5677E9AA 9E3050E2 765694DF C81F56E8 80B96E71 FC026E47 9558E447 5677E9AA 9E3050E2 765694DF C81F56E8 80B96E71
60C980DD 98EDD3DF FFFFFFFF FFFFFFFF 60C980DD 98EDD3DF FFFFFFFF FFFFFFFF
`), `),
g: hex('13'), g: hex("13"),
hash: 'sha256', hash: "sha256",
}, },
}; };

View File

@ -1,15 +1,24 @@
import crypto from 'crypto'; import crypto from "crypto";
import assert from 'assert'; import assert from "assert";
import BigInteger = require('../jsbn/jsbn'); import BigInteger = require("../jsbn/jsbn");
import {SrpParams, params as srpParams} from './params'; import { SrpParams, params as srpParams } from "./params";
export { SrpParams } from './params'; export { SrpParams } from "./params";
const zero = new BigInteger(0, 10); const zero = new BigInteger(0, 10);
function assert_(val: any, msg: string) { function assert_<V>(val: V, msg: string): void{
if (!val) if (!val) {
throw new Error(msg || 'assertion'); throw new Error(msg || "assertion");
}
}
function assertIsBuffer(arg: Buffer, argname = "arg"): void {
assert_(Buffer.isBuffer(arg), `Type error: ${argname} must be a buffer`);
}
function assertIsBigInteger(arg: BigInteger, argname?: string): void {
assert_(arg.constructor.name === "BigInteger", `Type error: ${argname} must be a BigInteger`);
} }
/** /**
@ -22,30 +31,21 @@ function assert_(val: any, msg: string) {
* @param {number} len Length of the resulting Buffer * @param {number} len Length of the resulting Buffer
* @return {Buffer} * @return {Buffer}
*/ */
function padTo(n: Buffer, len: number) { function padTo(n: Buffer, len: number): Buffer {
assertIsBuffer(n, 'n'); assertIsBuffer(n, "n");
const padding = len - n.length; const padding = len - n.length;
assert_(padding > -1, 'Negative padding. Very uncomfortable.'); assert_(padding > -1, "Negative padding. Very uncomfortable.");
const result = Buffer.alloc(len); const result = Buffer.alloc(len);
result.fill(0, 0, padding); result.fill(0, 0, padding);
n.copy(result, padding); n.copy(result, padding);
assert.equal(result.length, len); assert.strictEqual(result.length, len);
return result; return result;
} }
function padToN(number: BigInteger, params: SrpParams) { function padToN(number: BigInteger, params: SrpParams): Buffer {
assertIsBigInteger(number); assertIsBigInteger(number);
const n = number.toString(16).length % 2 != 0 ? '0' + number.toString(16) : number.toString(16); const n = number.toString(16).length % 2 !== 0 ? "0" + number.toString(16) : number.toString(16);
return padTo(Buffer.from(n, 'hex'), params.N_length_bits / 8); return padTo(Buffer.from(n, "hex"), params.N_length_bits / 8);
}
function assertIsBuffer(arg: Buffer, argname: string, ___?: any) {
argname = argname || 'arg';
assert_(Buffer.isBuffer(arg), `Type error: ${argname} must be a buffer`);
}
function assertIsBigInteger(arg: BigInteger, argname?: string) {
assert_(arg.constructor.name === 'BigInteger', `Type error: ${argname} must be a BigInteger`);
} }
/** /**
@ -60,12 +60,12 @@ function assertIsBigInteger(arg: BigInteger, argname?: string) {
* @param {Buffer} P User password * @param {Buffer} P User password
* @return {BigInteger} User secret * @return {BigInteger} User secret
*/ */
function getx(params: SrpParams, salt: Buffer, I: Buffer, P: Buffer) { function getx(params: SrpParams, salt: Buffer, I: Buffer, P: Buffer): BigInteger {
assertIsBuffer(salt, 'salt (salt)'); assertIsBuffer(salt, "salt (salt)");
assertIsBuffer(I, 'identity (I)'); assertIsBuffer(I, "identity (I)");
assertIsBuffer(P, 'password (P)'); assertIsBuffer(P, "password (P)");
const hashIP = crypto.createHash(params.hash) const hashIP = crypto.createHash(params.hash)
.update(Buffer.concat([I, Buffer.from(':'), P])) .update(Buffer.concat([I, Buffer.from(":"), P]))
.digest(); .digest();
const hashX: Buffer = crypto.createHash(params.hash) const hashX: Buffer = crypto.createHash(params.hash)
.update(salt) .update(salt)
@ -97,13 +97,18 @@ export class SRP {
* @return {Buffer} * @return {Buffer}
*/ */
public static computeVerifier(params: SrpParams, salt: Buffer, I: Buffer, P: Buffer): Buffer { public static computeVerifier(params: SrpParams, salt: Buffer, I: Buffer, P: Buffer): Buffer {
assertIsBuffer(salt, 'salt (salt)'); assertIsBuffer(salt, "salt (salt)");
assertIsBuffer(I, 'identity (I)'); assertIsBuffer(I, "identity (I)");
assertIsBuffer(P, 'password (P)'); assertIsBuffer(P, "password (P)");
// eslint-disable-next-line @typescript-eslint/camelcase
const v_num = params.g.modPow(getx(params, salt, I, P), params.N); const v_num = params.g.modPow(getx(params, salt, I, P), params.N);
return v_num.toBuffer(params.N_length_bits / 8); return v_num.toBuffer(params.N_length_bits / 8);
} }
public static genKey(callback: GenKeyCallback): void;
public static genKey(bytes: number, callback: GenKeyCallback): void;
public static genKey(bytes?: number): Promise<Buffer>;
/** /**
* Generate a random key. * Generate a random key.
* *
@ -111,20 +116,21 @@ export class SRP {
* @param {function} [callback] If not provided a Promise is returned * @param {function} [callback] If not provided a Promise is returned
* @return {Promise|void} * @return {Promise|void}
*/ */
public static genKey(callback: GenKeyCallback): void;
public static genKey(bytes: number, callback: GenKeyCallback): void;
public static genKey(bytes?: number): Promise<Buffer>;
public static genKey(bytes: number | GenKeyCallback = 32, callback?: GenKeyCallback): Promise<Buffer> | void { public static genKey(bytes: number | GenKeyCallback = 32, callback?: GenKeyCallback): Promise<Buffer> | void {
// bytes is optional // bytes is optional
if (typeof bytes !== 'number') { if (typeof bytes !== "number") {
callback = bytes as unknown as GenKeyCallback; callback = bytes as unknown as GenKeyCallback;
bytes = 32; bytes = 32;
} }
if (!callback) return new Promise((rs, rj) => SRP.genKey(bytes as number, (err, data) => err ? rj(err) : rs(data!))); if (!callback) {
return new Promise((rs, rj) => SRP.genKey(bytes as number, (err, data) => err ? rj(err) : rs(data!)));
}
crypto.randomBytes(bytes, (err, buf) => { crypto.randomBytes(bytes, (err, buf) => {
if (err) return callback!(err, null); if (err) {
return callback!(err, null);
}
return callback!(null, buf); return callback!(null, buf);
}); });
} }
@ -137,7 +143,7 @@ export class SRP {
* @param {object} params Group parameters, with .N, .g, .hash * @param {object} params Group parameters, with .N, .g, .hash
* @return {BigInteger} * @return {BigInteger}
*/ */
function getk(params: SrpParams) { function getk(params: SrpParams): BigInteger {
const k_buf = crypto const k_buf = crypto
.createHash(params.hash) .createHash(params.hash)
.update(padToN(params.N, params)) .update(padToN(params.N, params))
@ -161,7 +167,7 @@ function getk(params: SrpParams) {
* @param {BigInteger} b Server secret exponent * @param {BigInteger} b Server secret exponent
* @return {Buffer} B - The server public message * @return {Buffer} B - The server public message
*/ */
function getB(params: SrpParams, k: BigInteger, v: BigInteger, b: BigInteger) { function getB(params: SrpParams, k: BigInteger, v: BigInteger, b: BigInteger): Buffer {
assertIsBigInteger(v); assertIsBigInteger(v);
assertIsBigInteger(k); assertIsBigInteger(k);
assertIsBigInteger(b); assertIsBigInteger(b);
@ -178,12 +184,12 @@ function getB(params: SrpParams, k: BigInteger, v: BigInteger, b: BigInteger) {
* *
* @param {object} params Group parameters, with .N, .g, .hash * @param {object} params Group parameters, with .N, .g, .hash
* @param {BigInteger} a_num Client secret exponent * @param {BigInteger} a_num Client secret exponent
* @return {BigInteger} A - The client public message * @return {Buffer} A - The client public message
*/ */
function getA(params: SrpParams, a_num: BigInteger) { function getA(params: SrpParams, a_num: BigInteger): Buffer {
assertIsBigInteger(a_num); assertIsBigInteger(a_num);
if (Math.ceil(a_num.toString(16).length / 2) < 32) { if (Math.ceil(a_num.toString(16).length / 2) < 32) {
console.warn('getA: client key length %d is less than the recommended 256 bits', a_num.bitLength()); console.warn("getA: client key length %d is less than the recommended 256 bits", a_num.bitLength());
} }
return params.g.modPow(a_num, params.N).toBuffer(params.N_length_bits / 8); return params.g.modPow(a_num, params.N).toBuffer(params.N_length_bits / 8);
} }
@ -200,9 +206,9 @@ function getA(params: SrpParams, a_num: BigInteger) {
* @param {Buffer} B Server ephemeral public key * @param {Buffer} B Server ephemeral public key
* @return {BigInteger} u - Shared scrambling parameter * @return {BigInteger} u - Shared scrambling parameter
*/ */
function getu(params: SrpParams, A: Buffer, B: Buffer) { function getu(params: SrpParams, A: Buffer, B: Buffer): BigInteger {
assertIsBuffer(A, 'A'); assertIsBuffer(A, "A");
assertIsBuffer(B, 'B'); assertIsBuffer(B, "B");
const u_buf = crypto.createHash(params.hash) const u_buf = crypto.createHash(params.hash)
.update(padTo(A, params.N_length_bits / 8)) .update(padTo(A, params.N_length_bits / 8))
.update(padTo(B, params.N_length_bits / 8)) .update(padTo(B, params.N_length_bits / 8))
@ -213,22 +219,23 @@ function getu(params: SrpParams, A: Buffer, B: Buffer) {
/** /**
* The TLS premaster secret as calculated by the client * The TLS premaster secret as calculated by the client
* *
* @param {object} params Group parameters, with .N, .g, .hash * @param {SrpParams} params Group parameters, with .N, .g, .hash
* @param {Buffer} salt Salt (read from server) * @param {BigInteger} k_num
* @param {Buffer} I User identity (read from user) * @param {BigInteger} x_num
* @param {Buffer} P User password (read from user) * @param {BigInteger} a_num
* @param {BigInteger} a Ephemeral private key (generated for session) * @param {BigInteger} B_num
* @param {BigInteger} B Server ephemeral public key (read from server) * @param {BigInteger} u_num
* @return {Buffer} * @return {Buffer}
*/ */
function client_getS(params: SrpParams, k_num: BigInteger, x_num: BigInteger, a_num: BigInteger, B_num: BigInteger, u_num: BigInteger) { function client_getS(params: SrpParams, k_num: BigInteger, x_num: BigInteger, a_num: BigInteger, B_num: BigInteger, u_num: BigInteger): Buffer {
assertIsBigInteger(k_num); assertIsBigInteger(k_num);
assertIsBigInteger(x_num); assertIsBigInteger(x_num);
assertIsBigInteger(a_num); assertIsBigInteger(a_num);
assertIsBigInteger(B_num); assertIsBigInteger(B_num);
assertIsBigInteger(u_num); assertIsBigInteger(u_num);
if ((zero.compareTo(B_num) >= 0) || (params.N.compareTo(B_num) <= 0)) if ((zero.compareTo(B_num) >= 0) || (params.N.compareTo(B_num) <= 0)) {
throw new Error('invalid server-supplied "B", must be 1..N-1'); throw new Error("invalid server-supplied \"B\", must be 1..N-1");
}
const S_num = B_num.subtract(k_num.multiply(params.g.modPow(x_num, params.N))) const S_num = B_num.subtract(k_num.multiply(params.g.modPow(x_num, params.N)))
.modPow(a_num.add(u_num.multiply(x_num)), params.N) .modPow(a_num.add(u_num.multiply(x_num)), params.N)
@ -246,13 +253,14 @@ function client_getS(params: SrpParams, k_num: BigInteger, x_num: BigInteger, a_
* @param {BigInteger} u_num {@see getu} * @param {BigInteger} u_num {@see getu}
* @return {Buffer} * @return {Buffer}
*/ */
function server_getS(params: SrpParams, v_num: BigInteger, A_num: BigInteger, b_num: BigInteger, u_num: BigInteger) { function server_getS(params: SrpParams, v_num: BigInteger, A_num: BigInteger, b_num: BigInteger, u_num: BigInteger): Buffer {
assertIsBigInteger(v_num); assertIsBigInteger(v_num);
assertIsBigInteger(A_num); assertIsBigInteger(A_num);
assertIsBigInteger(b_num); assertIsBigInteger(b_num);
assertIsBigInteger(u_num); assertIsBigInteger(u_num);
if ((zero.compareTo(A_num) >= 0) || (params.N.compareTo(A_num) <= 0)) if ((zero.compareTo(A_num) >= 0) || (params.N.compareTo(A_num) <= 0)) {
throw new Error('invalid client-supplied "A", must be 1..N-1'); throw new Error("invalid client-supplied \"A\", must be 1..N-1");
}
const S_num = A_num.multiply(v_num.modPow(u_num, params.N)) const S_num = A_num.multiply(v_num.modPow(u_num, params.N))
.modPow(b_num, params.N) .modPow(b_num, params.N)
@ -267,13 +275,13 @@ function server_getS(params: SrpParams, v_num: BigInteger, A_num: BigInteger, b_
* @param {Buffer} S_buf Session key * @param {Buffer} S_buf Session key
* @return {Buffer} * @return {Buffer}
*/ */
function getK(params: SrpParams, S_buf: Buffer) { function getK(params: SrpParams, S_buf: Buffer): Buffer {
assertIsBuffer(S_buf, 'S'); assertIsBuffer(S_buf, "S");
if (params.hash === 'sha1') { if (params.hash === "sha1") {
// use t_mgf1 interleave for short sha1 hashes // use t_mgf1 interleave for short sha1 hashes
return Buffer.concat([ return Buffer.concat([
crypto.createHash(params.hash).update(S_buf).update(Buffer.from([0,0,0,0])).digest(), crypto.createHash(params.hash).update(S_buf).update(Buffer.from([0,0,0,0])).digest(),
crypto.createHash(params.hash).update(S_buf).update(Buffer.from([0,0,0,1])).digest() crypto.createHash(params.hash).update(S_buf).update(Buffer.from([0,0,0,1])).digest(),
]); ]);
} else { } else {
// use hash as-is otherwise // use hash as-is otherwise
@ -292,19 +300,20 @@ function getK(params: SrpParams, S_buf: Buffer) {
*/ */
function getM1(params: SrpParams, u_buf: Buffer, s_buf: Buffer, A_buf: Buffer, B_buf: Buffer, K_buf: Buffer): Buffer function getM1(params: SrpParams, u_buf: Buffer, s_buf: Buffer, A_buf: Buffer, B_buf: Buffer, K_buf: Buffer): Buffer
function getM1(params: SrpParams, A_buf: Buffer, B_buf: Buffer, K_buf: Buffer): Buffer function getM1(params: SrpParams, A_buf: Buffer, B_buf: Buffer, K_buf: Buffer): Buffer
function getM1(params: SrpParams, u_buf: Buffer, s_buf: Buffer, A_buf: Buffer, B_buf?: Buffer, K_buf?: Buffer) { function getM1(params: SrpParams, u_buf: Buffer, s_buf: Buffer, A_buf: Buffer, B_buf?: Buffer, K_buf?: Buffer): Buffer {
if (arguments.length > 4) { if (arguments.length > 4) {
assertIsBuffer(u_buf, 'identity (I)'); assertIsBuffer(u_buf, "identity (I)");
assertIsBuffer(s_buf, 'salt (s)') assertIsBuffer(s_buf, "salt (s)");
assertIsBuffer(A_buf, 'client public key (A)'); assertIsBuffer(A_buf, "client public key (A)");
assertIsBuffer(B_buf!, 'server public key (B)'); assertIsBuffer(B_buf!, "server public key (B)");
assertIsBuffer(K_buf!, 'session key (K)'); assertIsBuffer(K_buf!, "session key (K)");
const hN = crypto.createHash(params.hash).update(params.N.toBuffer(true)).digest(); const hN = crypto.createHash(params.hash).update(params.N.toBuffer(true)).digest();
const hG = crypto.createHash(params.hash).update(params.g.toBuffer(true)).digest(); const hG = crypto.createHash(params.hash).update(params.g.toBuffer(true)).digest();
for (let i = 0; i < hN.length; i++) for (let i = 0; i < hN.length; i++) {
hN[i] ^= hG[i]; hN[i] ^= hG[i];
}
const hU = crypto.createHash(params.hash).update(u_buf).digest(); const hU = crypto.createHash(params.hash).update(u_buf).digest();
@ -315,9 +324,9 @@ function getM1(params: SrpParams, u_buf: Buffer, s_buf: Buffer, A_buf: Buffer, B
} else { } else {
[A_buf, B_buf, s_buf] = [u_buf, s_buf, A_buf]; [A_buf, B_buf, s_buf] = [u_buf, s_buf, A_buf];
assertIsBuffer(A_buf, 'A'); assertIsBuffer(A_buf, "A");
assertIsBuffer(B_buf, 'B'); assertIsBuffer(B_buf, "B");
assertIsBuffer(s_buf, 'S'); assertIsBuffer(s_buf, "S");
return crypto.createHash(params.hash) return crypto.createHash(params.hash)
.update(A_buf).update(B_buf).update(s_buf) .update(A_buf).update(B_buf).update(s_buf)
@ -325,20 +334,20 @@ function getM1(params: SrpParams, u_buf: Buffer, s_buf: Buffer, A_buf: Buffer, B
} }
} }
function getM2(params: SrpParams, A_buf: Buffer, M1_buf: Buffer, K_buf: Buffer) { function getM2(params: SrpParams, A_buf: Buffer, M1_buf: Buffer, K_buf: Buffer): Buffer {
assertIsBuffer(A_buf, 'A'); assertIsBuffer(A_buf, "A");
assertIsBuffer(M1_buf, 'M1'); assertIsBuffer(M1_buf, "M1");
assertIsBuffer(K_buf, 'K'); assertIsBuffer(K_buf, "K");
return crypto.createHash(params.hash) return crypto.createHash(params.hash)
.update(A_buf).update(M1_buf).update(K_buf) .update(A_buf).update(M1_buf).update(K_buf)
.digest(); .digest();
} }
function equal(buf1: Buffer, buf2: Buffer) { function equal(buf1: Buffer, buf2: Buffer): boolean {
// constant-time comparison. A drop in the ocean compared to our // constant-time comparison. A drop in the ocean compared to our
// non-constant-time modexp operations, but still good practice. // non-constant-time modexp operations, but still good practice.
return buf1.toString('hex') === buf2.toString('hex'); return buf1.toString("hex") === buf2.toString("hex");
} }
export class SrpClient { export class SrpClient {
@ -376,12 +385,13 @@ export class SrpClient {
* @param {Buffer} identity_buf Identity/username * @param {Buffer} identity_buf Identity/username
* @param {Buffer} password_buf Password * @param {Buffer} password_buf Password
* @param {Buffer} secret1_buf Client private key {@see genKey} * @param {Buffer} secret1_buf Client private key {@see genKey}
* @param {boolean} hap
*/ */
constructor(params: SrpParams, salt_buf: Buffer, identity_buf: Buffer, password_buf: Buffer, secret1_buf: Buffer, hap = true) { constructor(params: SrpParams, salt_buf: Buffer, identity_buf: Buffer, password_buf: Buffer, secret1_buf: Buffer, hap = true) {
assertIsBuffer(salt_buf, 'salt (s)'); assertIsBuffer(salt_buf, "salt (s)");
assertIsBuffer(identity_buf, 'identity (I)'); assertIsBuffer(identity_buf, "identity (I)");
assertIsBuffer(password_buf, 'password (P)'); assertIsBuffer(password_buf, "password (P)");
assertIsBuffer(secret1_buf, 'secret1'); assertIsBuffer(secret1_buf, "secret1");
this._params = params; this._params = params;
this._k = getk(params); this._k = getk(params);
@ -401,7 +411,7 @@ export class SrpClient {
* *
* @return {Buffer} * @return {Buffer}
*/ */
computeA() { computeA(): Buffer {
return this._A; return this._A;
} }
@ -410,7 +420,7 @@ export class SrpClient {
* *
* @param {Buffer} B_buf The server's public key * @param {Buffer} B_buf The server's public key
*/ */
setB(B_buf: Buffer) { setB(B_buf: Buffer): void {
const u_num = getu(this._params, this._A, B_buf); const u_num = getu(this._params, this._A, B_buf);
const S_buf_x = client_getS(this._params, this._k, this._x, this._a, new BigInteger(B_buf), u_num); const S_buf_x = client_getS(this._params, this._k, this._x, this._a, new BigInteger(B_buf), u_num);
@ -432,9 +442,10 @@ export class SrpClient {
* *
* @return {Buffer} * @return {Buffer}
*/ */
computeM1() { computeM1(): Buffer {
if (this._M1 === undefined) if (this._M1 === undefined) {
throw new Error('incomplete protocol'); throw new Error("incomplete protocol");
}
return this._M1; return this._M1;
} }
@ -444,9 +455,10 @@ export class SrpClient {
* *
* @param M2 The server's M2 value * @param M2 The server's M2 value
*/ */
checkM2(M2: Buffer) { checkM2(M2: Buffer): void {
if (!equal(this._M2!, M2)) if (!equal(this._M2!, M2)) {
throw new Error('server is not authentic'); throw new Error("server is not authentic");
}
} }
/** /**
@ -454,9 +466,10 @@ export class SrpClient {
* *
* @return {Buffer} * @return {Buffer}
*/ */
computeK() { computeK(): Buffer {
if (this._K === undefined) if (this._K === undefined) {
throw new Error('incomplete protocol'); throw new Error("incomplete protocol");
}
return this._K; return this._K;
} }
} }
@ -519,10 +532,10 @@ export class SrpServer {
this._k = getk(params); this._k = getk(params);
if (arguments.length > 3) { if (arguments.length > 3) {
assertIsBuffer(salt_buf as Buffer, 'salt (salt)'); assertIsBuffer(salt_buf as Buffer, "salt (salt)");
assertIsBuffer(identity_buf!, 'identity (I)'); assertIsBuffer(identity_buf!, "identity (I)");
assertIsBuffer(password_buf!, 'password (P)'); assertIsBuffer(password_buf!, "password (P)");
assertIsBuffer(secret2_buf!, 'secret2'); assertIsBuffer(secret2_buf!, "secret2");
this._b = new BigInteger(secret2_buf!); this._b = new BigInteger(secret2_buf!);
this._v = new BigInteger(SRP.computeVerifier(params, salt_buf as Buffer, identity_buf!, password_buf!)); this._v = new BigInteger(SRP.computeVerifier(params, salt_buf as Buffer, identity_buf!, password_buf!));
@ -531,34 +544,40 @@ export class SrpServer {
this._s = salt_buf as Buffer; this._s = salt_buf as Buffer;
} else if (salt_buf instanceof Buffer) { } else if (salt_buf instanceof Buffer) {
const verifier_buf = salt_buf; const verifier_buf = salt_buf;
// noinspection JSUnusedAssignment
[secret2_buf, salt_buf, identity_buf, password_buf] = [identity_buf, undefined, undefined, undefined]; [secret2_buf, salt_buf, identity_buf, password_buf] = [identity_buf, undefined, undefined, undefined];
assertIsBuffer(verifier_buf, 'verifier (v)'); assertIsBuffer(verifier_buf, "verifier (v)");
assertIsBuffer(secret2_buf!, 'secret2'); assertIsBuffer(secret2_buf!, "secret2");
this._b = new BigInteger(secret2_buf!); this._b = new BigInteger(secret2_buf!);
this._v = new BigInteger(verifier_buf); this._v = new BigInteger(verifier_buf);
} else { } else {
const identity = salt_buf as Identity; const identity = salt_buf as Identity;
// noinspection JSUnusedAssignment
[secret2_buf, salt_buf, identity_buf, password_buf] = [identity_buf, undefined, undefined, undefined]; [secret2_buf, salt_buf, identity_buf, password_buf] = [identity_buf, undefined, undefined, undefined];
// noinspection SuspiciousTypeOfGuard // noinspection SuspiciousTypeOfGuard
assert(identity.username instanceof Buffer || typeof identity.username === 'string', 'identity.username (I) must be a string or Buffer'); assert(identity.username instanceof Buffer || typeof identity.username === "string", "identity.username (I) must be a string or Buffer");
assertIsBuffer(identity.salt, 'identity.salt (s)'); assertIsBuffer(identity.salt, "identity.salt (s)");
assert('password' in identity || 'verifier' in identity, 'identity requires a password or verifier'); assert("password" in identity || "verifier" in identity, "identity requires a password or verifier");
if ('verifier' in identity) assertIsBuffer(identity.verifier, 'identity.verifier (v)'); if ("verifier" in identity) {
else assert(identity.password instanceof Buffer || typeof identity.password === 'string', 'identity.password (p) must be a string or Buffer'); assertIsBuffer(identity.verifier, "identity.verifier (v)");
assertIsBuffer(secret2_buf!, 'secret2'); } else {
// noinspection SuspiciousTypeOfGuard
assert(identity.password instanceof Buffer || typeof identity.password === "string", "identity.password (p) must be a string or Buffer");
}
assertIsBuffer(secret2_buf!, "secret2");
const username = typeof identity.username === 'string' ? Buffer.from(identity.username) : identity.username; const username = typeof identity.username === "string" ? Buffer.from(identity.username) : identity.username;
this._b = new BigInteger(secret2_buf!); this._b = new BigInteger(secret2_buf!);
if ('verifier' in identity) { if ("verifier" in identity) {
this._v = new BigInteger(identity.verifier); this._v = new BigInteger(identity.verifier);
} else { } else {
this._v = new BigInteger(SRP.computeVerifier( this._v = new BigInteger(SRP.computeVerifier(
params, identity.salt, username, params, identity.salt, username,
typeof identity.password === 'string' ? Buffer.from(identity.password) : identity.password typeof identity.password === "string" ? Buffer.from(identity.password) : identity.password,
)); ));
} }
@ -574,7 +593,7 @@ export class SrpServer {
* *
* @return {Buffer} * @return {Buffer}
*/ */
computeB() { computeB(): Buffer {
return this._B; return this._B;
} }
@ -583,7 +602,7 @@ export class SrpServer {
* *
* @param {Buffer} A The client's public key * @param {Buffer} A The client's public key
*/ */
setA(A: Buffer) { setA(A: Buffer): void {
const u_num = getu(this._params, A, this._B); const u_num = getu(this._params, A, this._B);
const S_buf = server_getS(this._params, this._v, new BigInteger(A), this._b, u_num); const S_buf = server_getS(this._params, this._v, new BigInteger(A), this._b, u_num);
@ -604,11 +623,13 @@ export class SrpServer {
* *
* @param {Buffer} M1 The client's M1 value * @param {Buffer} M1 The client's M1 value
*/ */
checkM1(M1: Buffer) { checkM1(M1: Buffer): void {
if (this._M1 === undefined) if (this._M1 === undefined) {
throw new Error('incomplete protocol'); throw new Error("incomplete protocol");
if (!equal(this._M1, M1)) }
throw new Error('client did not use the same password'); if (!equal(this._M1, M1)) {
throw new Error("client did not use the same password");
}
} }
/** /**
@ -616,9 +637,10 @@ export class SrpServer {
* *
* @return {Buffer} * @return {Buffer}
*/ */
computeK() { computeK(): Buffer {
if (this._K === undefined) if (this._K === undefined) {
throw new Error('incomplete protocol'); throw new Error("incomplete protocol");
}
return this._K; return this._K;
} }
@ -628,9 +650,11 @@ export class SrpServer {
* *
* @return {Buffer} * @return {Buffer}
*/ */
computeM2() { computeM2(): Buffer {
if (this._M2 === undefined) if (this._M2 === undefined) {
throw new Error('incomplete protocol'); throw new Error("incomplete protocol");
}
return this._M2; return this._M2;
} }
} }

View File

@ -1,4 +1,4 @@
import {hex} from '../params'; import { hex } from "../params";
// Modulus (N), as specified by the 3072-bit group of RFC 5054 // Modulus (N), as specified by the 3072-bit group of RFC 5054
export const N = hex(` export const N = hex(`
@ -17,13 +17,13 @@ export const N = hex(`
`); `);
// Generator (g), as specified by the 3072-bit group of RFC 5054 // Generator (g), as specified by the 3072-bit group of RFC 5054
export const g = hex('05'); export const g = hex("05");
// Username (I), as an ASCII string without quotes // Username (I), as an ASCII string without quotes
export const I = 'alice'; export const I = "alice";
// Password (p), as an ASCII string without quotes // Password (p), as an ASCII string without quotes
export const p = 'password123'; export const p = "password123";
// A private (a) // A private (a)
export const a = hex(` export const a = hex(`

View File

@ -1,8 +1,9 @@
/* eslint-disable @typescript-eslint/ban-ts-ignore */
// @ts-ignore // @ts-ignore
import vows from 'vows'; import vows from "vows";
import assert from 'assert'; import assert from "assert";
import {SRP, SrpClient, SrpParams, SrpServer} from "../srp"; import { SRP, SrpClient, SrpParams, SrpServer } from "../srp";
import BigInteger = require('../../jsbn/jsbn'); import BigInteger = require("../../jsbn/jsbn");
interface Input { interface Input {
/** Identity */ /** Identity */
@ -32,13 +33,14 @@ interface ExpectedOutput {
M1: Buffer; M1: Buffer;
} }
import {N, g, I, p, a, A, b, B, s, v, u, S, K} from './hap_test_data'; // eslint-disable-next-line @typescript-eslint/no-unused-vars
import {N, g, I, p, a, A, b, B, s, v, u, S, K} from "./hap_test_data";
const params = SRP.params.hap; const params = SRP.params.hap;
const inputs: Input = { const inputs: Input = {
I: Buffer.from(I, 'ascii'), I: Buffer.from(I, "ascii"),
P: Buffer.from(p, 'ascii'), P: Buffer.from(p, "ascii"),
salt: s.toBuffer(true), salt: s.toBuffer(true),
// a and b are usually random. For testing, we force them to specific values. // a and b are usually random. For testing, we force them to specific values.
a: a.toBuffer(true), a: a.toBuffer(true),
@ -47,9 +49,9 @@ const inputs: Input = {
const expected: ExpectedOutput = { const expected: ExpectedOutput = {
// 'k' encodes the group (N and g), used in SRP-6a // 'k' encodes the group (N and g), used in SRP-6a
k: Buffer.from('a9c2e2559bf0ebb53f0cbbf62282906bede7f2182f00678211fbd5bde5b285033a4993503b87397f9be5ec02080fedbc0835587ad039060879b8621e8c3659e0', 'hex'), k: Buffer.from("a9c2e2559bf0ebb53f0cbbf62282906bede7f2182f00678211fbd5bde5b285033a4993503b87397f9be5ec02080fedbc0835587ad039060879b8621e8c3659e0", "hex"),
// 'x' is derived from the salt and password // 'x' is derived from the salt and password
x: Buffer.from('b149ecb0946b0b206d77e73d95deb7c41bd12e86a5e2eea3893d5416591a002ff94bfea384dc0e1c550f7ed4d5a9d2ad1f1526f01c56b5c10577730cc4a4d709', 'hex'), x: Buffer.from("b149ecb0946b0b206d77e73d95deb7c41bd12e86a5e2eea3893d5416591a002ff94bfea384dc0e1c550f7ed4d5a9d2ad1f1526f01c56b5c10577730cc4a4d709", "hex"),
// 'v' is the SRP verifier // 'v' is the SRP verifier
v: v.toBuffer(true), v: v.toBuffer(true),
// 'B' is the server's public message // 'B' is the server's public message
@ -63,21 +65,21 @@ const expected: ExpectedOutput = {
// 'K' is the shared derived key // 'K' is the shared derived key
K: K.toBuffer(true), K: K.toBuffer(true),
// 'M1' is the client's proof that it knows the shared key // 'M1' is the client's proof that it knows the shared key
M1: Buffer.from('5f7c14ab57ed0e94fd1d78c6b4dd09ed7e340b7e05d419a9fd760f6b35e523d1310777a1ae1d2826f596f3a85116cc457c7c964d4f44ded5559da818c88b617f', 'hex'), M1: Buffer.from("5f7c14ab57ed0e94fd1d78c6b4dd09ed7e340b7e05d419a9fd760f6b35e523d1310777a1ae1d2826f596f3a85116cc457c7c964d4f44ded5559da818c88b617f", "hex"),
}; };
function hexequal(a: Buffer, b: Buffer, msg?: string) { function hexequal(a: Buffer, b: Buffer, msg?: string): void {
assert.equal(a.length, b.length, msg); assert.strictEqual(a.length, b.length, msg);
assert.equal(a.toString('hex'), b.toString('hex'), msg); assert.strictEqual(a.toString("hex"), b.toString("hex"), msg);
} }
function numequal(a: BigInteger, b: BigInteger, msg?: string) { function numequal(a: BigInteger, b: BigInteger, msg?: string): void {
assert(a.compareTo(b) === 0, msg); assert(a.compareTo(b) === 0, msg);
} }
function checkVectors(params: SrpParams, inputs: Input, expected: ExpectedOutput, useVerifier = true) { function checkVectors(params: SrpParams, inputs: Input, expected: ExpectedOutput, useVerifier = true): void {
hexequal(inputs.I, Buffer.from('616c696365', 'hex'), 'I'); hexequal(inputs.I, Buffer.from("616c696365", "hex"), "I");
hexequal(SRP.computeVerifier(params, inputs.salt, inputs.I, inputs.P), expected.v, 'v'); hexequal(SRP.computeVerifier(params, inputs.salt, inputs.I, inputs.P), expected.v, "v");
const client = new SrpClient(params, inputs.salt, inputs.I, inputs.P, inputs.a, true); const client = new SrpClient(params, inputs.salt, inputs.I, inputs.P, inputs.a, true);
const server = useVerifier ? const server = useVerifier ?
@ -85,9 +87,9 @@ function checkVectors(params: SrpParams, inputs: Input, expected: ExpectedOutput
new SrpServer(params, inputs.salt, inputs.I, inputs.P, inputs.b); new SrpServer(params, inputs.salt, inputs.I, inputs.P, inputs.b);
// @ts-ignore // @ts-ignore
numequal(client._k, new BigInteger(expected.k.toString('hex'), 16), 'k'); numequal(client._k, new BigInteger(expected.k.toString("hex"), 16), "k");
// @ts-ignore // @ts-ignore
numequal(client._x, new BigInteger(expected.x.toString('hex'), 16), 'x'); numequal(client._x, new BigInteger(expected.x.toString("hex"), 16), "x");
hexequal(client.computeA(), expected.A); hexequal(client.computeA(), expected.A);
hexequal(server.computeB(), expected.B); hexequal(server.computeB(), expected.B);
@ -99,7 +101,7 @@ function checkVectors(params: SrpParams, inputs: Input, expected: ExpectedOutput
client.setB(expected.B); client.setB(expected.B);
// @ts-ignore // @ts-ignore
numequal(client._u, new BigInteger(expected.u.toString('hex'), 16)); numequal(client._u, new BigInteger(expected.u.toString("hex"), 16));
// @ts-ignore // @ts-ignore
hexequal(client._S, expected.S); hexequal(client._S, expected.S);
hexequal(client.computeM1(), expected.M1); hexequal(client.computeM1(), expected.M1);
@ -107,15 +109,15 @@ function checkVectors(params: SrpParams, inputs: Input, expected: ExpectedOutput
server.setA(expected.A); server.setA(expected.A);
// @ts-ignore // @ts-ignore
numequal(server._u, new BigInteger(expected.u.toString('hex'), 16)); numequal(server._u, new BigInteger(expected.u.toString("hex"), 16));
// @ts-ignore // @ts-ignore
hexequal(server._S, expected.S); hexequal(server._S, expected.S);
assert.throws(() => server.checkM1(Buffer.from('notM1')), /client did not use the same password/); assert.throws(() => server.checkM1(Buffer.from("notM1")), /client did not use the same password/);
server.checkM1(expected.M1); // happy, not throwy server.checkM1(expected.M1); // happy, not throwy
hexequal(server.computeK(), expected.K); hexequal(server.computeK(), expected.K);
} }
vows.describe('HomeKit vectors').addBatch({ vows.describe("HomeKit vectors").addBatch({
'with verifier': () => checkVectors(params, inputs, expected), "with verifier": () => checkVectors(params, inputs, expected),
'with password': () => checkVectors(params, inputs, expected, false), "with password": () => checkVectors(params, inputs, expected, false),
}).export(module); }).export(module);

View File

@ -1,8 +1,9 @@
/* eslint-disable @typescript-eslint/ban-ts-ignore */
// @ts-ignore // @ts-ignore
import vows from 'vows'; import vows from "vows";
import assert from 'assert'; import assert from "assert";
import {SRP, SrpClient, SrpParams, SrpServer} from '../srp'; import { SRP, SrpClient, SrpParams, SrpServer } from "../srp";
import BigInteger = require('../../jsbn/jsbn'); import BigInteger = require("../../jsbn/jsbn");
/* /*
* Vectors from https://wiki.mozilla.org/Identity/AttachedServices/KeyServerProtocol * Vectors from https://wiki.mozilla.org/Identity/AttachedServices/KeyServerProtocol
@ -13,8 +14,8 @@ import BigInteger = require('../../jsbn/jsbn');
* Note that P is the HKDF-stretched key, computed elsewhere. * Note that P is the HKDF-stretched key, computed elsewhere.
*/ */
function hex(s: string) { function hex(s: string): Buffer {
return Buffer.from(s.split(/\s/).join(''), 'hex'); return Buffer.from(s.split(/\s/).join(""), "hex");
} }
const params = SRP.params[2048]; const params = SRP.params[2048];
@ -53,9 +54,9 @@ interface ExpectedOutput {
*/ */
const inputs_1: Input = { const inputs_1: Input = {
I: Buffer.from('andré@example.org', 'utf8'), I: Buffer.from("andré@example.org", "utf8"),
P: hex('00f9b71800ab5337 d51177d8fbc682a3 653fa6dae5b87628 eeec43a18af59a9d'), P: hex("00f9b71800ab5337 d51177d8fbc682a3 653fa6dae5b87628 eeec43a18af59a9d"),
salt: hex('00f1000000000000000000000000000000000000000000000000000000000179'), salt: hex("00f1000000000000000000000000000000000000000000000000000000000179"),
// a and b are usually random. For testing, we force them to specific values. // a and b are usually random. For testing, we force them to specific values.
a: hex(` a: hex(`
00f2000000000000 0000000000000000 0000000000000000 0000000000000000 00f2000000000000 0000000000000000 0000000000000000 0000000000000000
@ -81,10 +82,10 @@ const inputs_1: Input = {
const expected_1: ExpectedOutput = { const expected_1: ExpectedOutput = {
// 'k' encodes the group (N and g), used in SRP-6a // 'k' encodes the group (N and g), used in SRP-6a
k: hex('05b9e8ef059c6b32 ea59fc1d322d37f0 4aa30bae5aa9003b 8321e21ddb04e300'), k: hex("05b9e8ef059c6b32 ea59fc1d322d37f0 4aa30bae5aa9003b 8321e21ddb04e300"),
// 'x' is derived from the salt and password // 'x' is derived from the salt and password
// 'v' is the SRP verifier // 'v' is the SRP verifier
x: hex('b5200337cc3f3f92 6cdddae0b2d31029 c069936a844aff58 779a545be89d0abe'), x: hex("b5200337cc3f3f92 6cdddae0b2d31029 c069936a844aff58 779a545be89d0abe"),
v: hex(` v: hex(`
00173ffa0263e63c cfd6791b8ee2a40f 048ec94cd95aa8a3 125726f9805e0c82 00173ffa0263e63c cfd6791b8ee2a40f 048ec94cd95aa8a3 125726f9805e0c82
83c658dc0b607fbb 25db68e68e93f265 8483049c68af7e82 14c49fde2712a775 83c658dc0b607fbb 25db68e68e93f265 8483049c68af7e82 14c49fde2712a775
@ -118,7 +119,7 @@ const expected_1: ExpectedOutput = {
7374d73e79be2c39 75632de562c59647 0bb27bad79c3e2fc ddf194e1666cb9fc 7374d73e79be2c39 75632de562c59647 0bb27bad79c3e2fc ddf194e1666cb9fc
`), `),
// 'u' combines the two public messages // 'u' combines the two public messages
u: hex('b284aa1064e87751 50da6b5e2147b47c a7df505bed94a6f4 bb2ad873332ad732'), u: hex("b284aa1064e87751 50da6b5e2147b47c a7df505bed94a6f4 bb2ad873332ad732"),
// 'S' is the shared secret // 'S' is the shared secret
S: hex(` S: hex(`
0092aaf0f527906a a5e8601f5d707907 a03137e1b601e04b 5a1deb02a981f4be 0092aaf0f527906a a5e8601f5d707907 a03137e1b601e04b 5a1deb02a981f4be
@ -131,15 +132,15 @@ const expected_1: ExpectedOutput = {
6001497c27f362ed bafb25e0f045bfdf 9fb02db9c908f103 40a639fe84c31b27 6001497c27f362ed bafb25e0f045bfdf 9fb02db9c908f103 40a639fe84c31b27
`), `),
// 'K' is the shared derived key // 'K' is the shared derived key
K: hex('e68fd0112bfa31dc ffc8e9c96a1cbadb 4c3145978ff35c73 e5bf8d30bbc7499a'), K: hex("e68fd0112bfa31dc ffc8e9c96a1cbadb 4c3145978ff35c73 e5bf8d30bbc7499a"),
// 'M1' is the client's proof that it knows the shared key // 'M1' is the client's proof that it knows the shared key
M1: hex('27949ec1e0f16256 33436865edb037e2 3eb6bf5cb91873f2 a2729373c2039008') M1: hex("27949ec1e0f16256 33436865edb037e2 3eb6bf5cb91873f2 a2729373c2039008"),
}; };
/* inputs_2/expected_2 have leading 0x00 bytes in 'x' and 'u' */ /* inputs_2/expected_2 have leading 0x00 bytes in 'x' and 'u' */
const inputs_2: Input = { const inputs_2: Input = {
I: inputs_1.I, P: inputs_1.P, I: inputs_1.I, P: inputs_1.P,
salt: hex('00f1000000000000000000000000000000000000000000000000000000000021'), salt: hex("00f1000000000000000000000000000000000000000000000000000000000021"),
a: hex(` a: hex(`
00f2000000000000 0000000000000000 0000000000000000 0000000000000000 00f2000000000000 0000000000000000 0000000000000000 0000000000000000
0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000
@ -159,11 +160,11 @@ const inputs_2: Input = {
0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000
0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000
0000000000000000 0000000000000000 0000000000000000 0000000000000001 0000000000000000 0000000000000000 0000000000000000 0000000000000001
`) `),
}; };
const expected_2: ExpectedOutput = { const expected_2: ExpectedOutput = {
k: expected_1.k, k: expected_1.k,
x: hex('009b2740fb49284d 69cab7c916d449ee d7dcabf41332b8b8 d6928f529bd1a94e'), x: hex("009b2740fb49284d 69cab7c916d449ee d7dcabf41332b8b8 d6928f529bd1a94e"),
v: hex(` v: hex(`
1cd8b856685672ee 7a5895d897121234 6c17c3472f2696e4 8cdeec5533c06693 1cd8b856685672ee 7a5895d897121234 6c17c3472f2696e4 8cdeec5533c06693
179bc24802b762bc c1e1f8fc8abe607a f2f44aac9172e7dd 0c0110e45cf3b700 179bc24802b762bc c1e1f8fc8abe607a f2f44aac9172e7dd 0c0110e45cf3b700
@ -197,7 +198,7 @@ const expected_2: ExpectedOutput = {
cdcb555a6b16e844 5b03e09776eba841 7576dac458afbbd5 2902dfb0282bed79 cdcb555a6b16e844 5b03e09776eba841 7576dac458afbbd5 2902dfb0282bed79
`), `),
// 'u' combines the two public messages // 'u' combines the two public messages
u: hex('000e4039be3989ad 088dc17d8ade899a 6409e7e57b3e8518 cee1cbc77e1de243'), u: hex("000e4039be3989ad 088dc17d8ade899a 6409e7e57b3e8518 cee1cbc77e1de243"),
// 'S' is the shared secret // 'S' is the shared secret
S: hex(` S: hex(`
5c7f591d134d19f9 fcedc2b4e3eecd3d 5deadfe7dd42bd59 b1c960516c65ab61 5c7f591d134d19f9 fcedc2b4e3eecd3d 5deadfe7dd42bd59 b1c960516c65ab61
@ -210,15 +211,15 @@ const expected_2: ExpectedOutput = {
e75f0058f718ec14 f9bbeb29ff966e00 ddfdd2d38a1c7a68 ac455a57b972d528 e75f0058f718ec14 f9bbeb29ff966e00 ddfdd2d38a1c7a68 ac455a57b972d528
`), `),
// 'K' is the shared derived key // 'K' is the shared derived key
K: hex('b637ede0b7a31c46 b2567e855eb8a7f7 a994937deee76479 62afbe35d6929709'), K: hex("b637ede0b7a31c46 b2567e855eb8a7f7 a994937deee76479 62afbe35d6929709"),
// 'M1' is the client's proof that it knows the shared key // 'M1' is the client's proof that it knows the shared key
M1: hex('67c83797eb1a3987 e2d48d287e3bd772 d25db2b3cd86ea22 c8cf3ae932a1e45b') M1: hex("67c83797eb1a3987 e2d48d287e3bd772 d25db2b3cd86ea22 c8cf3ae932a1e45b"),
}; };
/* inputs_3/expected_3 have leading 0x00 bytes in 'x' and 'K' */ /* inputs_3/expected_3 have leading 0x00 bytes in 'x' and 'K' */
const inputs_3: Input = { const inputs_3: Input = {
I: inputs_2.I, P: inputs_2.P, I: inputs_2.I, P: inputs_2.P,
salt: hex('00f1000000000000000000000000000000000000000000000000000000000021'), salt: hex("00f1000000000000000000000000000000000000000000000000000000000021"),
a: hex(` a: hex(`
00f2000000000000 0000000000000000 0000000000000000 0000000000000000 00f2000000000000 0000000000000000 0000000000000000 0000000000000000
0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000
@ -229,7 +230,7 @@ const inputs_3: Input = {
0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000
0000000000000000 0000000000000000 0000000000000000 00000000000001a0 0000000000000000 0000000000000000 0000000000000000 00000000000001a0
`), `),
b: inputs_2.b b: inputs_2.b,
}; };
const expected_3: ExpectedOutput = { const expected_3: ExpectedOutput = {
k: expected_2.k, k: expected_2.k,
@ -246,7 +247,7 @@ const expected_3: ExpectedOutput = {
6c7011f3c979eaab 469f06465a5b7239 afaee535aedc5bd2 1a220546e0e6b70b 6c7011f3c979eaab 469f06465a5b7239 afaee535aedc5bd2 1a220546e0e6b70b
5b6f54db3fea46d5 7ebc7fe46156d793 c59e6290d3cf9bc2 4316528da34f4640 5b6f54db3fea46d5 7ebc7fe46156d793 c59e6290d3cf9bc2 4316528da34f4640
`), `),
u: hex('865d0efca6cf17d6 f489e129231f1a48 b20c83ec6581d11f 3a2fa48ea93cd305'), u: hex("865d0efca6cf17d6 f489e129231f1a48 b20c83ec6581d11f 3a2fa48ea93cd305"),
S: hex(` S: hex(`
0ae26456e1a0dec1 ce162fb2e5bc7300 3c285e17c0b44f03 7ebbc57f8020ceae 0ae26456e1a0dec1 ce162fb2e5bc7300 3c285e17c0b44f03 7ebbc57f8020ceae
5d10a9e6e44eab2a 6915b582ab5f6e7d 16002ce05e524015 e9bc7c56d5131da4 5d10a9e6e44eab2a 6915b582ab5f6e7d 16002ce05e524015 e9bc7c56d5131da4
@ -257,14 +258,14 @@ const expected_3: ExpectedOutput = {
249f7734081aa42d 58dd54f8f725b245 175cf7d102e1086c eba4cfe7e49a2d27 249f7734081aa42d 58dd54f8f725b245 175cf7d102e1086c eba4cfe7e49a2d27
ffd6aef7549d402f bfcea78b4f3398ac 9ab1ee199f70acb6 4d2a17e159ff500d ffd6aef7549d402f bfcea78b4f3398ac 9ab1ee199f70acb6 4d2a17e159ff500d
`), `),
K: hex('00217598a4008956 4b17196bd43422d6 03a0a88a545b61b3 98c42c9cbcc1d1b3'), K: hex("00217598a4008956 4b17196bd43422d6 03a0a88a545b61b3 98c42c9cbcc1d1b3"),
M1: hex('96d815ecece1dff4 254cd77517b37b97 65e741c1a57169ab af538e867444ec7f') M1: hex("96d815ecece1dff4 254cd77517b37b97 65e741c1a57169ab af538e867444ec7f"),
}; };
/* inputs_4/expected_4 have leading 0x00 bytes in 'x' and 'M1' */ /* inputs_4/expected_4 have leading 0x00 bytes in 'x' and 'M1' */
const inputs_4: Input = { const inputs_4: Input = {
I: inputs_2.I, P: inputs_2.P, I: inputs_2.I, P: inputs_2.P,
salt: hex('00f1000000000000000000000000000000000000000000000000000000000021'), salt: hex("00f1000000000000000000000000000000000000000000000000000000000021"),
a: hex(` a: hex(`
00f2000000000000 0000000000000000 0000000000000000 0000000000000000 00f2000000000000 0000000000000000 0000000000000000 0000000000000000
0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000
@ -275,7 +276,7 @@ const inputs_4: Input = {
0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000
0000000000000000 0000000000000000 0000000000000000 0000000000000190 0000000000000000 0000000000000000 0000000000000000 0000000000000190
`), `),
b: inputs_2.b b: inputs_2.b,
}; };
const expected_4: ExpectedOutput = { const expected_4: ExpectedOutput = {
k: expected_2.k, k: expected_2.k,
@ -292,7 +293,7 @@ const expected_4: ExpectedOutput = {
c690f9478d5b331d c00eef68670edbf3 629fd1a6c85267d2 cbb90f1670e7ba09 c690f9478d5b331d c00eef68670edbf3 629fd1a6c85267d2 cbb90f1670e7ba09
cf2b5a9b00be8e11 f33e47a1c1f04eca f35bccb61af1116e 4d0f9d475017bad2 cf2b5a9b00be8e11 f33e47a1c1f04eca f35bccb61af1116e 4d0f9d475017bad2
`), `),
u: hex('d0913eb75b61e15a 87756ffa04d4f967 e492bd0b330a2b11 fe8976aada2bb1ee'), u: hex("d0913eb75b61e15a 87756ffa04d4f967 e492bd0b330a2b11 fe8976aada2bb1ee"),
S: hex(` S: hex(`
7ba3ce4a3d236b95 3c2d0fee42195c85 081664a44f55b82d a3abf66ac68bdbd7 7ba3ce4a3d236b95 3c2d0fee42195c85 081664a44f55b82d a3abf66ac68bdbd7
ad82d5ad95090782 5241fb706de8fc58 0a29e4579fbbedf3 0bec0138b3f76e06 ad82d5ad95090782 5241fb706de8fc58 0a29e4579fbbedf3 0bec0138b3f76e06
@ -303,30 +304,30 @@ const expected_4: ExpectedOutput = {
d4a9bf0242bfe703 26fc19b68c90e83b 59b5cc21886ab602 f8bfa16fb50c3147 d4a9bf0242bfe703 26fc19b68c90e83b 59b5cc21886ab602 f8bfa16fb50c3147
9aad5e31698abf67 863b7ca6b6ac25a7 09a24d8f94c80bbf 691e38c81beb3c72 9aad5e31698abf67 863b7ca6b6ac25a7 09a24d8f94c80bbf 691e38c81beb3c72
`), `),
K: hex('bd2a167a93b8496e 68c7e24b37956924 672eb8249d25c281 13984912d5cf27a6'), K: hex("bd2a167a93b8496e 68c7e24b37956924 672eb8249d25c281 13984912d5cf27a6"),
M1: hex('00cef66a047d506c bf941c236218e583 5343534ae08cf0cd 0fb7980bed242e05') M1: hex("00cef66a047d506c bf941c236218e583 5343534ae08cf0cd 0fb7980bed242e05"),
}; };
function hexequal(a: Buffer, b: Buffer, msg?: string) { function hexequal(a: Buffer, b: Buffer, msg?: string): void {
assert.equal(a.length, b.length, msg); assert.strictEqual(a.length, b.length, msg);
assert.equal(a.toString('hex'), b.toString('hex'), msg); assert.strictEqual(a.toString("hex"), b.toString("hex"), msg);
} }
function numequal(a: BigInteger, b: BigInteger, msg?: string) { function numequal(a: BigInteger, b: BigInteger, msg?: string): void {
assert(a.compareTo(b) === 0, msg); assert(a.compareTo(b) === 0, msg);
} }
function checkVectors(params: SrpParams, inputs: Input, expected: ExpectedOutput) { function checkVectors(params: SrpParams, inputs: Input, expected: ExpectedOutput): void {
hexequal(inputs.I, Buffer.from('616e6472c3a9406578616d706c652e6f7267', 'hex'), 'I'); hexequal(inputs.I, Buffer.from("616e6472c3a9406578616d706c652e6f7267", "hex"), "I");
hexequal(SRP.computeVerifier(params, inputs.salt, inputs.I, inputs.P), expected.v, 'v'); hexequal(SRP.computeVerifier(params, inputs.salt, inputs.I, inputs.P), expected.v, "v");
const client = new SrpClient(params, inputs.salt, inputs.I, inputs.P, inputs.a, false); const client = new SrpClient(params, inputs.salt, inputs.I, inputs.P, inputs.a, false);
const server = new SrpServer(params, expected.v, inputs.b); const server = new SrpServer(params, expected.v, inputs.b);
// @ts-ignore // @ts-ignore
numequal(client._k, new BigInteger(expected.k.toString('hex'), 16), 'k'); numequal(client._k, new BigInteger(expected.k.toString("hex"), 16), "k");
// @ts-ignore // @ts-ignore
numequal(client._x, new BigInteger(expected.x.toString('hex'), 16), 'x'); numequal(client._x, new BigInteger(expected.x.toString("hex"), 16), "x");
hexequal(client.computeA(), expected.A); hexequal(client.computeA(), expected.A);
hexequal(server.computeB(), expected.B); hexequal(server.computeB(), expected.B);
@ -338,7 +339,7 @@ function checkVectors(params: SrpParams, inputs: Input, expected: ExpectedOutput
client.setB(expected.B); client.setB(expected.B);
// @ts-ignore // @ts-ignore
numequal(client._u, new BigInteger(expected.u.toString('hex'), 16)); numequal(client._u, new BigInteger(expected.u.toString("hex"), 16));
// @ts-ignore // @ts-ignore
hexequal(client._S, expected.S); hexequal(client._S, expected.S);
hexequal(client.computeM1(), expected.M1); hexequal(client.computeM1(), expected.M1);
@ -346,17 +347,17 @@ function checkVectors(params: SrpParams, inputs: Input, expected: ExpectedOutput
server.setA(expected.A); server.setA(expected.A);
// @ts-ignore // @ts-ignore
numequal(server._u, new BigInteger(expected.u.toString('hex'), 16)); numequal(server._u, new BigInteger(expected.u.toString("hex"), 16));
// @ts-ignore // @ts-ignore
hexequal(server._S, expected.S); hexequal(server._S, expected.S);
assert.throws(() => server.checkM1(Buffer.from('notM1')), /client did not use the same password/); assert.throws(() => server.checkM1(Buffer.from("notM1")), /client did not use the same password/);
server.checkM1(expected.M1); // happy, not throwy server.checkM1(expected.M1); // happy, not throwy
hexequal(server.computeK(), expected.K); hexequal(server.computeK(), expected.K);
} }
vows.describe('picl vectors').addBatch({ vows.describe("picl vectors").addBatch({
'vectors 1': () => checkVectors(params, inputs_1, expected_1), "vectors 1": () => checkVectors(params, inputs_1, expected_1),
'vectors 2': () => checkVectors(params, inputs_2, expected_2), "vectors 2": () => checkVectors(params, inputs_2, expected_2),
'vectors 3': () => checkVectors(params, inputs_3, expected_3), "vectors 3": () => checkVectors(params, inputs_3, expected_3),
'vectors 4': () => checkVectors(params, inputs_4, expected_4), "vectors 4": () => checkVectors(params, inputs_4, expected_4),
}).export(module); }).export(module);

View File

@ -1,8 +1,9 @@
/* eslint-disable @typescript-eslint/ban-ts-ignore */
// @ts-ignore // @ts-ignore
import vows from 'vows'; import vows from "vows";
import assert from 'assert'; import assert from "assert";
import {SRP, SrpClient, SrpServer} from '../srp'; import { SRP, SrpClient, SrpServer } from "../srp";
import BigInteger = require('../../jsbn/jsbn'); import BigInteger = require("../../jsbn/jsbn");
const params = SRP.params[1024]; const params = SRP.params[1024];
@ -10,15 +11,15 @@ const params = SRP.params[1024];
* http://tools.ietf.org/html/rfc5054#appendix-B * http://tools.ietf.org/html/rfc5054#appendix-B
*/ */
function hex(h: string) { function hex(h: string): string {
return h.split(/\s/).join(''); return h.split(/\s/).join("");
} }
const I = Buffer.from('alice'); const I = Buffer.from("alice");
const P = Buffer.from('password123'); const P = Buffer.from("password123");
const s = Buffer.from('beb25379d1a8581eb5a727673a2441ee', 'hex'); const s = Buffer.from("beb25379d1a8581eb5a727673a2441ee", "hex");
const k_expected = '7556aa045aef2cdd07abaf0f665c3e818913186f'; const k_expected = "7556aa045aef2cdd07abaf0f665c3e818913186f";
const x_expected = '94b7555aabe9127cc58ccf4993db6cf84d16c124'; const x_expected = "94b7555aabe9127cc58ccf4993db6cf84d16c124";
const v_expected = hex(` const v_expected = hex(`
7e273de8 696ffc4f 4e337d05 b4b375be b0dde156 9e8fa00a 9886d812 7e273de8 696ffc4f 4e337d05 b4b375be b0dde156 9e8fa00a 9886d812
9bada1f1 822223ca 1a605b53 0e379ba4 729fdc59 f105b478 7e5186f5 9bada1f1 822223ca 1a605b53 0e379ba4 729fdc59 f105b478 7e5186f5
@ -26,8 +27,8 @@ const v_expected = hex(`
ea53d15c 1aff87b2 b9da6e04 e058ad51 cc72bfc9 033b564e 26480d78 ea53d15c 1aff87b2 b9da6e04 e058ad51 cc72bfc9 033b564e 26480d78
e955a5e2 9e7ab245 db2be315 e2099afb e955a5e2 9e7ab245 db2be315 e2099afb
`); `);
const a = Buffer.from('60975527035cf2ad1989806f0407210bc81edc04e2762a56afd529ddda2d4393', 'hex'); const a = Buffer.from("60975527035cf2ad1989806f0407210bc81edc04e2762a56afd529ddda2d4393", "hex");
const b = Buffer.from('e487cb59d31ac550471e81f00f6928e01dda08e974a004f49e61f5d105284d20', 'hex'); const b = Buffer.from("e487cb59d31ac550471e81f00f6928e01dda08e974a004f49e61f5d105284d20", "hex");
const A_expected = hex(` const A_expected = hex(`
61d5e490 f6f1b795 47b0704c 436f523d d0e560f0 c64115bb 72557ec4 61d5e490 f6f1b795 47b0704c 436f523d d0e560f0 c64115bb 72557ec4
4352e890 3211c046 92272d8b 2d1a5358 a2cf1b6e 0bfcf99f 921530ec 4352e890 3211c046 92272d8b 2d1a5358 a2cf1b6e 0bfcf99f 921530ec
@ -42,7 +43,7 @@ const B_expected = hex(`
37089e6f 9c6059f3 88838e7a 00030b33 1eb76840 910440b1 b27aaeae 37089e6f 9c6059f3 88838e7a 00030b33 1eb76840 910440b1 b27aaeae
eb4012b7 d7665238 a8e3fb00 4b117b58 eb4012b7 d7665238 a8e3fb00 4b117b58
`); `);
const u_expected = 'ce38b9593487da98554ed47d70a7ae5f462ef019'; const u_expected = "ce38b9593487da98554ed47d70a7ae5f462ef019";
const S_expected = hex(` const S_expected = hex(`
b0dc82ba bcf30674 ae450c02 87745e79 90a3381f 63b387aa f271a10d b0dc82ba bcf30674 ae450c02 87745e79 90a3381f 63b387aa f271a10d
233861e3 59b48220 f7c4693c 9ae12b0a 6f67809f 0876e2d0 13800d6c 233861e3 59b48220 f7c4693c 9ae12b0a 6f67809f 0876e2d0 13800d6c
@ -51,61 +52,61 @@ const S_expected = hex(`
c346d7e4 74b29ede 8a469ffe ca686e5a c346d7e4 74b29ede 8a469ffe ca686e5a
`); `);
function asHex(num: number | BigInteger) { function asHex(num: number | BigInteger): string {
return num.toString(16); return num.toString(16);
} }
vows.describe('RFC 5054').addBatch({ vows.describe("RFC 5054").addBatch({
'Test vectors': { "Test vectors": {
topic() { topic(): Buffer {
return SRP.computeVerifier(params, s, I, P); return SRP.computeVerifier(params, s, I, P);
}, },
'x'() { "x"(): void {
const client = new SrpClient(params, s, I, P, a, false); const client = new SrpClient(params, s, I, P, a, false);
// @ts-ignore // @ts-ignore
assert.equal(asHex(client._x), x_expected); assert.strictEqual(asHex(client._x), x_expected);
}, },
'V'(v: Buffer) { "V"(v: Buffer): void {
assert.equal(v.toString('hex'), v_expected); assert.strictEqual(v.toString("hex"), v_expected);
}, },
'k'() { "k"(): void {
const client = new SrpClient(params, s, I, P, a, false); const client = new SrpClient(params, s, I, P, a, false);
// @ts-ignore // @ts-ignore
assert.equal(asHex(client._k), k_expected); assert.strictEqual(asHex(client._k), k_expected);
}, },
'A'() { "A"(): void {
const client = new SrpClient(params, s, I, P, a, false); const client = new SrpClient(params, s, I, P, a, false);
assert.equal(client.computeA().toString('hex'), A_expected); assert.strictEqual(client.computeA().toString("hex"), A_expected);
}, },
'B'(v: Buffer) { "B"(v: Buffer): void {
const server = new SrpServer(params, v, b); const server = new SrpServer(params, v, b);
assert.equal(server.computeB().toString('hex'), B_expected); assert.strictEqual(server.computeB().toString("hex"), B_expected);
}, },
'u'() { "u"(): void {
const client = new SrpClient(params, s, I, P, a, false); const client = new SrpClient(params, s, I, P, a, false);
client.setB(Buffer.from(B_expected, 'hex')); client.setB(Buffer.from(B_expected, "hex"));
// @ts-ignore // @ts-ignore
assert.equal(asHex(client._u), u_expected); assert.strictEqual(asHex(client._u), u_expected);
}, },
'S client'() { "S client"(): void {
const client = new SrpClient(params, s, I, P, a, false); const client = new SrpClient(params, s, I, P, a, false);
client.setB(Buffer.from(B_expected, 'hex')); client.setB(Buffer.from(B_expected, "hex"));
// @ts-ignore // @ts-ignore
assert.equal(client._S.toString('hex'), S_expected); assert.strictEqual(client._S.toString("hex"), S_expected);
}, },
'S server'(v: Buffer) { "S server"(v: Buffer): void {
const server = new SrpServer(params, v, b); const server = new SrpServer(params, v, b);
server.setA(Buffer.from(A_expected, 'hex')); server.setA(Buffer.from(A_expected, "hex"));
// @ts-ignore // @ts-ignore
assert.equal(server._S.toString('hex'), S_expected); assert.strictEqual(server._S.toString("hex"), S_expected);
}, },
}, },
}).export(module); }).export(module);

View File

@ -1,15 +1,16 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore // @ts-ignore
import vows from 'vows'; import vows from "vows";
import assert from 'assert'; import assert from "assert";
import {SRP, SrpClient, SrpServer} from '../srp'; import { SRP, SrpClient, SrpServer } from "../srp";
const params = SRP.params[4096]; const params = SRP.params[4096];
const salt = Buffer.from('salty'); const salt = Buffer.from("salty");
const identity = Buffer.from('alice'); const identity = Buffer.from("alice");
const password = Buffer.from('password123'); const password = Buffer.from("password123");
assert(params, 'missing parameters'); assert(params, "missing parameters");
let client: SrpClient, server: SrpServer; let client: SrpClient, server: SrpServer;
let a: Buffer, A: Buffer; let a: Buffer, A: Buffer;
@ -17,20 +18,23 @@ let b: Buffer, B: Buffer;
let verifier: Buffer; let verifier: Buffer;
// let S_client, S_server; // let S_client, S_server;
vows.describe('srp.js').addBatch({ vows.describe("srp.js").addBatch({
'create Verifier'() { "create Verifier"() {
verifier = SRP.computeVerifier(params, salt, identity, password); verifier = SRP.computeVerifier(params, salt, identity, password);
assert.equal(verifier.toString('hex'), 'f0e47f50f5dead8db8d93a279e3b62d6ff50854b31fbd3474a886bef916261717e84dd4fb8b4d27feaa5146db7b1cbbc274fdf96a132b5029c2cd72527427a9b9809d5a4d018252928b4fc343bc17ce63c1859d5806f5466014fc361002d8890aeb4d6316ff37331fc2761be0144c91cdd8e00ed0138c0ce51534d1b9a9ba629d7be34d2742dd4097daabc9ecb7aaad89e53c342b038f1d2adae1f2410b7884a3e9a124c357e421bccd4524467e1922660e0a4460c5f7c38c0877b65f6e32f28296282a93fc11bbabb7bb69bf1b3f9391991d8a86dd05e15000b7e38ba38a536bb0bf59c808ec25e791b8944719488b8087df8bfd7ff20822997a53f6c86f3d45d004476d6303301376bb25a9f94b552cce5ed40de5dd7da8027d754fa5f66738c7e3fc4ef3e20d625df62cbe6e7adfc21e47880d8a6ada37e60370fd4d8fc82672a90c29f2e72f35652649d68348de6f36d0e435c8bd42dd00155d35d501becc0661b43e04cdb2da84ce92b8bf49935d73d75efcbd1176d7bbccc3cc4d4b5fefcc02d478614ee1681d2ff3c711a61a7686eb852ae06fb8227be21fb8802719b1271ba1c02b13bbf0a2c2e459d9bedcc8d1269f6a785cb4563aa791b38fb038269f63f58f47e9051499549789269cc7b8ec7026fc34ba73289c4af829d5a532e723967ce9b6c023ef0fd0cfe37f51f10f19463b6534159a09ddd2f51f3b30033'); assert.strictEqual(verifier.toString("hex"), "f0e47f50f5dead8db8d93a279e3b62d6ff50854b31fbd3474a886bef916261717e84dd4fb8b4d27feaa5146db7b1cbbc274fdf96a132b5029c2cd72527427a9b9809d5a4d018252928b4fc343bc17ce63c1859d5806f5466014fc361002d8890aeb4d6316ff37331fc2761be0144c91cdd8e00ed0138c0ce51534d1b9a9ba629d7be34d2742dd4097daabc9ecb7aaad89e53c342b038f1d2adae1f2410b7884a3e9a124c357e421bccd4524467e1922660e0a4460c5f7c38c0877b65f6e32f28296282a93fc11bbabb7bb69bf1b3f9391991d8a86dd05e15000b7e38ba38a536bb0bf59c808ec25e791b8944719488b8087df8bfd7ff20822997a53f6c86f3d45d004476d6303301376bb25a9f94b552cce5ed40de5dd7da8027d754fa5f66738c7e3fc4ef3e20d625df62cbe6e7adfc21e47880d8a6ada37e60370fd4d8fc82672a90c29f2e72f35652649d68348de6f36d0e435c8bd42dd00155d35d501becc0661b43e04cdb2da84ce92b8bf49935d73d75efcbd1176d7bbccc3cc4d4b5fefcc02d478614ee1681d2ff3c711a61a7686eb852ae06fb8227be21fb8802719b1271ba1c02b13bbf0a2c2e459d9bedcc8d1269f6a785cb4563aa791b38fb038269f63f58f47e9051499549789269cc7b8ec7026fc34ba73289c4af829d5a532e723967ce9b6c023ef0fd0cfe37f51f10f19463b6534159a09ddd2f51f3b30033");
}, },
'create a and b': { "create a and b": {
topic(this: any) {(async () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any
a = await SRP.genKey(64); topic(this: any): void {
b = await SRP.genKey(32); (async (): Promise<void> => {
this.callback(); a = await SRP.genKey(64);
})()}, b = await SRP.genKey(32);
this.callback();
})();
},
'use a and b'() { "use a and b"(): void {
client = new SrpClient(params, salt, identity, password, a, false); client = new SrpClient(params, salt, identity, password, a, false);
// client produces A // client produces A
@ -61,51 +65,51 @@ vows.describe('srp.js').addBatch({
// client and server agree on K // client and server agree on K
const client_K = client.computeK(); const client_K = client.computeK();
const server_K = server.computeK(); const server_K = server.computeK();
assert.equal(client_K.toString('hex'), server_K.toString('hex')); assert.strictEqual(client_K.toString("hex"), server_K.toString("hex"));
// server is authentic // server is authentic
assert.doesNotThrow(() => client.checkM2(serverM2), 'M2 didn\'t check'); assert.doesNotThrow(() => client.checkM2(serverM2), "M2 didn't check");
}, },
'server rejects wrong M1'() { "server rejects wrong M1"(): void {
const bad_client = new SrpClient(params, salt, identity, Buffer.from('bad'), a, false); const bad_client = new SrpClient(params, salt, identity, Buffer.from("bad"), a, false);
const server2 = new SrpServer(params, verifier, b); const server2 = new SrpServer(params, verifier, b);
bad_client.setB(server2.computeB()); bad_client.setB(server2.computeB());
assert.throws(() => server.checkM1(bad_client.computeM1()), /client did not use the same password/); assert.throws(() => server.checkM1(bad_client.computeM1()), /client did not use the same password/);
}, },
'server rejects bad A'() { "server rejects bad A"(): void {
// client's "A" must be 1..N-1 . Reject 0 and N and N+1. We should // client's "A" must be 1..N-1 . Reject 0 and N and N+1. We should
// reject 2*N too, but our Buffer-length checks reject it before the // reject 2*N too, but our Buffer-length checks reject it before the
// number itself is examined. // number itself is examined.
var server2 = new SrpServer(params, verifier, b); const server2 = new SrpServer(params, verifier, b);
var Azero = Buffer.alloc(params.N_length_bits / 8); const Azero = Buffer.alloc(params.N_length_bits / 8);
Azero.fill(0); Azero.fill(0);
//! var AN = params.N.toBuffer(); //! var AN = params.N.toBuffer();
//! var AN1 = params.N.add(1).toBuffer(); //! var AN1 = params.N.add(1).toBuffer();
var AN = Buffer.from(params.N.toString(16), 'hex'); const AN = Buffer.from(params.N.toString(16), "hex");
var AN1 = Buffer.from(params.N.add(1).toString(16), 'hex'); const AN1 = Buffer.from(params.N.add(1).toString(16), "hex");
assert.throws(() => server2.setA(Azero), /invalid client-supplied "A"/); assert.throws(() => server2.setA(Azero), /invalid client-supplied "A"/);
assert.throws(() => server2.setA(AN), /invalid client-supplied "A"/); assert.throws(() => server2.setA(AN), /invalid client-supplied "A"/);
assert.throws(() => server2.setA(AN1), /invalid client-supplied "A"/); assert.throws(() => server2.setA(AN1), /invalid client-supplied "A"/);
}, },
'client rejects bad B'() { "client rejects bad B"(): void {
// server's "B" must be 1..N-1 . Reject 0 and N and N+1 // server's "B" must be 1..N-1 . Reject 0 and N and N+1
var client2 = new SrpClient(params, salt, identity, password, a, false); const client2 = new SrpClient(params, salt, identity, password, a, false);
var Bzero = Buffer.alloc(params.N_length_bits / 8); const Bzero = Buffer.alloc(params.N_length_bits / 8);
Bzero.fill(0, 0, params.N_length_bits / 8); Bzero.fill(0, 0, params.N_length_bits / 8);
//! var BN = params.N.toBuffer(); //! var BN = params.N.toBuffer();
//! var BN1 = params.N.add(1).toBuffer(); //! var BN1 = params.N.add(1).toBuffer();
var BN = Buffer.from(params.N.toString(16), 'hex'); const BN = Buffer.from(params.N.toString(16), "hex");
var BN1 = Buffer.from(params.N.add(1).toString(16), 'hex'); const BN1 = Buffer.from(params.N.add(1).toString(16), "hex");
assert.throws(() => client2.setB(Bzero), /invalid server-supplied "B"/); assert.throws(() => client2.setB(Bzero), /invalid server-supplied "B"/);
assert.throws(() => client2.setB(BN), /invalid server-supplied "B"/); assert.throws(() => client2.setB(BN), /invalid server-supplied "B"/);
assert.throws(() => client2.setB(BN1), /invalid server-supplied "B"/); assert.throws(() => client2.setB(BN1), /invalid server-supplied "B"/);
}, },
'client rejects bad M2'() { "client rejects bad M2"(): void {
client = new SrpClient(params, salt, identity, password, a, false); client = new SrpClient(params, salt, identity, password, a, false);
// client produces A // client produces A
@ -130,15 +134,15 @@ vows.describe('srp.js').addBatch({
server.checkM1(client.computeM1()); server.checkM1(client.computeM1());
let serverM2 = server.computeM2(); let serverM2 = server.computeM2();
// we tamper with the server's M2 // we tamper with the server's M2
serverM2 = Buffer.from('a'); serverM2 = Buffer.from("a");
// client and server agree on K // client and server agree on K
var client_K = client.computeK(); const client_K = client.computeK();
var server_K = server.computeK(); const server_K = server.computeK();
assert.equal(client_K.toString('hex'), server_K.toString('hex')); assert.strictEqual(client_K.toString("hex"), server_K.toString("hex"));
// server is NOT authentic // server is NOT authentic
assert.throws(() => client.checkM2(serverM2), 'M2 didn\'t check'); assert.throws(() => client.checkM2(serverM2), "M2 didn't check");
}, },
}, },
}).export(module); }).export(module);