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

Restructured the project a bit

This commit is contained in:
Supereg 2020-04-12 19:01:18 +02:00
parent d8e2938064
commit 462b688de4
No known key found for this signature in database
GPG Key ID: 2F08948C5653D720
15 changed files with 194 additions and 185 deletions

2
.gitignore vendored
View File

@ -1,2 +1,2 @@
node_modules node_modules
dist lib

View File

@ -9,7 +9,7 @@ Creating the Verifier
--- ---
```ts ```ts
import * as srp6a from 'fast-srp-hap'; import { SRP } from 'fast-srp-hap';
/** /**
* Computes the verifier of a user. Only needed to add the user to the auth system. * Computes the verifier of a user. Only needed to add the user to the auth system.
@ -19,12 +19,12 @@ import * as srp6a from 'fast-srp-hap';
* @return {Promise<{salt: Buffer, verifier: Buffer}>} * @return {Promise<{salt: Buffer, verifier: Buffer}>}
*/ */
async function srp6a_create_user(I: string, P: string) { async function srp6a_create_user(I: string, P: string) {
const salt = await srp6a.genKey(32); const salt = await SRP.genKey(32);
return { return {
// The salt is required for authenticating the user later // The salt is required for authenticating the user later
salt, salt,
verifier: srp6a.computeVerifier(srp6a.params[4096], salt, Buffer.from(I), Buffer.from(P)), verifier: SRP.computeVerifier(SRP.params[4096], salt, Buffer.from(I), Buffer.from(P)),
}; };
} }
@ -38,7 +38,7 @@ Server
--- ---
```ts ```ts
import {Server, genKey, params} from 'fast-srp-hap'; import {SRP, SrpServer} from 'fast-srp-hap';
(async () => { (async () => {
// Get the user details from somewhere // Get the user details from somewhere
@ -46,7 +46,7 @@ import {Server, genKey, params} from 'fast-srp-hap';
username: 'username', // Or a Buffer username: 'username', // Or a Buffer
// If we have the plaintext password // If we have the plaintext password
salt: await genKey(32), salt: await SRP.genKey(32),
password: 'password', // Or a Buffer password: 'password', // Or a Buffer
// If we have a saved verifier // If we have a saved verifier
@ -55,9 +55,9 @@ import {Server, genKey, params} from 'fast-srp-hap';
}; };
// Generate a secret key // Generate a secret key
const secret = await genKey(32); const secret = await SRP.genKey(32);
const server = new Server(params[3076], user, secret); // For Apple SRP use params.hap const server = new SrpServer(SRP.params[3076], user, secret); // For Apple SRP use params.hap
// ... // ...
})(); })();

View File

14
package-lock.json generated
View File

@ -1,13 +1,13 @@
{ {
"name": "fast-srp-hap", "name": "fast-srp-hap",
"version": "1.1.0", "version": "2.0.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
"@types/node": { "@types/node": {
"version": "12.12.3", "version": "10.17.19",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.3.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.19.tgz",
"integrity": "sha512-opgSsy+cEF9N8MgaVPnWVtdJ3o4mV2aMHvDq7thkQUFt0EuOHJon4rQpJfhjmNHB+ikl0Cd6WhWIErOyQ+f7tw==", "integrity": "sha512-46/xThm3zvvc9t9/7M3AaLEqtOpqlYYYcCZbpYVAQHG20+oMZBkae/VMrn4BTi6AJ8cpack0mEXhGiKmDNbLrQ==",
"dev": true "dev": true
}, },
"balanced-match": { "balanced-match": {
@ -105,9 +105,9 @@
"dev": true "dev": true
}, },
"typescript": { "typescript": {
"version": "3.6.4", "version": "3.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.4.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz",
"integrity": "sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==", "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==",
"dev": true "dev": true
}, },
"vows": { "vows": {

View File

@ -2,26 +2,34 @@
"name": "fast-srp-hap", "name": "fast-srp-hap",
"description": "Secure Remote Password (SRP)", "description": "Secure Remote Password (SRP)",
"version": "1.1.0", "version": "1.1.0",
"main": "dist/index.js", "main": "lib/srp.js",
"scripts": { "scripts": {
"prepare": "rm -rf dist && tsc", "clean": "rm -rf lib",
"test": "vows dist/test/test_*.js --spec" "build": "npm run clean && tsc",
"prepublishOnly": "npm run build",
"postpublish": "npm run clean",
"test": "vows lib/test/test_*.js --spec"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/zarmack/fast-srp" "url": "https://github.com/homebridge/fast-srp-hap"
}, },
"author": "Zarmack Tanen", "author": "Zarmack Tanen",
"licence": "MIT", "license": "MIT",
"engines": {
"node": ">=10.17.0"
},
"files": [ "files": [
"jsbn",
"lib", "lib",
"dist/index.*", "!lib/test",
"dist/lib" "README.md",
"LICENSE"
], ],
"dependencies": {}, "dependencies": {},
"devDependencies": { "devDependencies": {
"@types/node": "^12.12.3", "@types/node": "^10.17.19",
"typescript": "^3.6.4", "typescript": "^3.8.3",
"vows": "^0.8.3" "vows": "^0.8.3"
} }
} }

View File

@ -1,8 +0,0 @@
export * from './lib/srp';
export {
default as params,
SrpParams,
} from './lib/params';
export default module.exports;

View File

@ -14,7 +14,7 @@
// 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('../../lib/jsbn'); import BigInteger = require('../jsbn/jsbn');
export function hex(s: string) { export function hex(s: string) {
return new BigInteger(s.split(/\s|\n/).join(''), 16); return new BigInteger(s.split(/\s|\n/).join(''), 16);
@ -27,9 +27,7 @@ export interface SrpParams {
hash: string; hash: string;
} }
const params: { export const params = {
[n: string]: SrpParams;
} = {
1024: { 1024: {
N_length_bits: 1024, N_length_bits: 1024,
N: hex(` N: hex(`

View File

@ -1,10 +1,11 @@
import * as crypto from 'crypto'; import crypto from 'crypto';
import * as assert from 'assert'; import assert from 'assert';
import BigInteger = require('../../lib/jsbn'); import BigInteger = require('../jsbn/jsbn');
import {SrpParams, params as srpParams} from './params';
import {SrpParams} from './params'; export { SrpParams } from './params';
var zero = new BigInteger(0, 10); const zero = new BigInteger(0, 10);
function assert_(val: any, msg: string) { function assert_(val: any, msg: string) {
if (!val) if (!val)
@ -34,7 +35,7 @@ function padTo(n: Buffer, len: number) {
function padToN(number: BigInteger, params: SrpParams) { function padToN(number: BigInteger, params: SrpParams) {
assertIsBigInteger(number); assertIsBigInteger(number);
var 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);
} }
@ -66,35 +67,68 @@ function getx(params: SrpParams, salt: Buffer, I: Buffer, P: Buffer) {
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 = crypto.createHash(params.hash) const hashX: Buffer = crypto.createHash(params.hash)
.update(salt) .update(salt)
.update(hashIP) .update(hashIP)
.digest(); .digest();
return new BigInteger(hashX); return new BigInteger(hashX);
} }
/** export type GenKeyCallback = (err: Error | null, data: Buffer | null) => void;
* The verifier is calculated as described in Section 3 of [SRP-RFC].
* We give the algorithm here for convenience. export class SRP {
*
* The verifier (v) is computed based on the salt (s), user name (I), public static readonly params = srpParams;
* password (P), and group parameters (N, g).
* /**
* x = H(s | H(I | ":" | P)) * The verifier is calculated as described in Section 3 of [SRP-RFC].
* v = g^x % N * We give the algorithm here for convenience.
* *
* @param {object} params Group parameters, with .N, .g, .hash * The verifier (v) is computed based on the salt (s), user name (I),
* @param {Buffer} salt * password (P), and group parameters (N, g).
* @param {Buffer} I User identity *
* @param {Buffer} P User password * x = H(s | H(I | ":" | P))
* @return {Buffer} * v = g^x % N
*/ *
export function computeVerifier(params: SrpParams, salt: Buffer, I: Buffer, P: Buffer) { * @param {object} params Group parameters, with .N, .g, .hash
assertIsBuffer(salt, 'salt (salt)'); * @param {Buffer} salt
assertIsBuffer(I, 'identity (I)'); * @param {Buffer} I User identity
assertIsBuffer(P, 'password (P)'); * @param {Buffer} P User password
var v_num = params.g.modPow(getx(params, salt, I, P), params.N); * @return {Buffer}
return v_num.toBuffer(params.N_length_bits / 8); */
public static computeVerifier(params: SrpParams, salt: Buffer, I: Buffer, P: Buffer): Buffer {
assertIsBuffer(salt, 'salt (salt)');
assertIsBuffer(I, 'identity (I)');
assertIsBuffer(P, 'password (P)');
const v_num = params.g.modPow(getx(params, salt, I, P), params.N);
return v_num.toBuffer(params.N_length_bits / 8);
}
/**
* Generate a random key.
*
* @param {number} [bytes=32] Length of key
* @param {function} [callback] If not provided a Promise is returned
* @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 {
// bytes is optional
if (typeof bytes !== 'number') {
callback = bytes as unknown as GenKeyCallback;
bytes = 32;
}
if (!callback) return new Promise((rs, rj) => SRP.genKey(bytes as number, (err, data) => err ? rj(err) : rs(data!)));
crypto.randomBytes(bytes, (err, buf) => {
if (err) return callback!(err, null);
return callback!(null, buf);
});
}
} }
/** /**
@ -113,32 +147,6 @@ function getk(params: SrpParams) {
return new BigInteger(k_buf); return new BigInteger(k_buf);
} }
/**
* Generate a random key.
*
* @param {number} [bytes=32] Length of key
* @param {function} [callback] If not provided a Promise is returned
* @return {Promise|void}
*/
type GenKeyCallback = (err: Error | null, data: Buffer | null) => void;
export function genKey(callback: GenKeyCallback): void
export function genKey(bytes: number, callback: GenKeyCallback): void
export function genKey(bytes?: number): Promise<Buffer>
export function genKey(bytes: number | GenKeyCallback = 32, callback?: GenKeyCallback): Promise<Buffer> | void {
// bytes is optional
if (typeof bytes !== 'number') {
callback = bytes as unknown as GenKeyCallback;
bytes = 32;
}
if (!callback) return new Promise((rs, rj) => genKey(bytes as number, (err, data) => err ? rj(err) : rs(data!)));
crypto.randomBytes(bytes, (err, buf) => {
if (err) return callback!(err, null);
return callback!(null, buf);
});
}
/** /**
* The server key exchange message also contains the server's public * The server key exchange message also contains the server's public
* value (B). The server calculates this value as B = k*v + g^b % N, * value (B). The server calculates this value as B = k*v + g^b % N,
@ -147,7 +155,8 @@ export function genKey(bytes: number | GenKeyCallback = 32, callback?: GenKeyCal
* *
* Note: as the tests imply, the entire expression is mod N. * Note: as the tests imply, the entire expression is mod N.
* *
* @param {object} params Group parameters, with .N, .g, .hash * @param {SrpParams} params Group parameters, with .N, .g, .hash
* @param {BigInteger} k
* @param {BigInteger} v Verifier (stored) * @param {BigInteger} v Verifier (stored)
* @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
@ -168,7 +177,7 @@ function getB(params: SrpParams, k: BigInteger, v: BigInteger, b: BigInteger) {
* Note: for this implementation, we take that to mean 256/8 bytes. * Note: for this implementation, we take that to mean 256/8 bytes.
* *
* @param {object} params Group parameters, with .N, .g, .hash * @param {object} params Group parameters, with .N, .g, .hash
* @param {BigInteger} a Client secret exponent * @param {BigInteger} a_num Client secret exponent
* @return {BigInteger} A - The client public message * @return {BigInteger} A - The client public message
*/ */
function getA(params: SrpParams, a_num: BigInteger) { function getA(params: SrpParams, a_num: BigInteger) {
@ -231,10 +240,10 @@ function client_getS(params: SrpParams, k_num: BigInteger, x_num: BigInteger, a_
* The TLS premastersecret as calculated by the server * The TLS premastersecret as calculated by the server
* *
* @param {BigInteger} params Group parameters, with .N, .g, .hash * @param {BigInteger} params Group parameters, with .N, .g, .hash
* @param {BigInteger} v Verifier (stored on server) * @param {BigInteger} v_num Verifier (stored on server)
* @param {BigInteger} A Ephemeral client public key (read from client) * @param {BigInteger} A_num Ephemeral client public key (read from client)
* @param {BigInteger} b Server ephemeral private key (generated for session) * @param {BigInteger} b_num Server ephemeral private key (generated for session)
* @param {BigInteger} u {@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) {
@ -255,7 +264,7 @@ function server_getS(params: SrpParams, v_num: BigInteger, A_num: BigInteger, b_
* Compute the shared session key K from S * Compute the shared session key K from S
* *
* @param {object} params Group parameters, with .N, .g, .hash * @param {object} params Group parameters, with .N, .g, .hash
* @param {Buffer} S 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) {
@ -273,7 +282,7 @@ function getK(params: SrpParams, S_buf: Buffer) {
} }
/** /**
* *
* @param params SRP params * @param params SRP params
* @param u_buf User identity * @param u_buf User identity
* @param s_buf User salt * @param s_buf User salt
@ -291,13 +300,13 @@ function getM1(params: SrpParams, u_buf: Buffer, s_buf: Buffer, A_buf: Buffer, B
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)');
var hN = crypto.createHash(params.hash).update(params.N.toBuffer(true)).digest(); const hN = crypto.createHash(params.hash).update(params.N.toBuffer(true)).digest();
var hG = crypto.createHash(params.hash).update(params.g.toBuffer(true)).digest(); const hG = crypto.createHash(params.hash).update(params.g.toBuffer(true)).digest();
for (var i = 0; i < hN.length; i++) for (let i = 0; i < hN.length; i++)
hN[i] ^= hG[i]; hN[i] ^= hG[i];
var hU = crypto.createHash(params.hash).update(u_buf).digest(); const hU = crypto.createHash(params.hash).update(u_buf).digest();
return crypto.createHash(params.hash) return crypto.createHash(params.hash)
.update(hN).update(hU).update(s_buf) .update(hN).update(hU).update(s_buf)
@ -332,19 +341,19 @@ function equal(buf1: Buffer, buf2: Buffer) {
return buf1.toString('hex') === buf2.toString('hex'); return buf1.toString('hex') === buf2.toString('hex');
} }
export class Client { export class SrpClient {
private _params: SrpParams; private readonly _params: SrpParams;
private _k: BigInteger; private readonly _k: BigInteger;
private _x: BigInteger; private readonly _x: BigInteger;
/** Client private key */ /** Client private key */
private _a: BigInteger; private readonly _a: BigInteger;
/** Client public key */ /** Client public key */
private _A: Buffer; private readonly _A: Buffer;
/** User identity */ /** User identity */
private _I?: Buffer; private readonly _I?: Buffer;
/** User salt */ /** User salt */
private _s?: Buffer; private readonly _s?: Buffer;
/** Session key */ /** Session key */
private _K?: Buffer; private _K?: Buffer;
@ -386,7 +395,7 @@ export class Client {
this._A = getA(params, this._a); this._A = getA(params, this._a);
} }
/** /**
* Returns the client's public key (A). * Returns the client's public key (A).
* *
@ -395,7 +404,7 @@ export class Client {
computeA() { computeA() {
return this._A; return this._A;
} }
/** /**
* Sets the server's public key (B). * Sets the server's public key (B).
* *
@ -462,21 +471,21 @@ export type VerifierIdentity = BaseIdentity & {verifier: Buffer};
export type Identity = PasswordIdentity | VerifierIdentity; export type Identity = PasswordIdentity | VerifierIdentity;
export class Server { export class SrpServer {
private _params: SrpParams; private readonly _params: SrpParams;
/** Multiplier parameter (H(N, g)) */ /** Multiplier parameter (H(N, g)) */
private _k: BigInteger; private readonly _k: BigInteger;
/** Server private key */ /** Server private key */
private _b: BigInteger; private readonly _b: BigInteger;
/** Server public key */ /** Server public key */
private _B: Buffer; private readonly _B: Buffer;
/** Verifier */ /** Verifier */
private _v: BigInteger; private readonly _v: BigInteger;
/** User identity */ /** User identity */
private _I?: Buffer; private readonly _I?: Buffer;
/** User salt */ /** User salt */
private _s?: Buffer; private readonly _s?: Buffer;
/** Session key */ /** Session key */
_K?: Buffer; _K?: Buffer;
@ -500,7 +509,7 @@ export class Server {
* @param {Buffer} salt_buf User salt (from server) * @param {Buffer} salt_buf User salt (from server)
* @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} secret2_buf Client private key {@see genKey}
*/ */
constructor(params: SrpParams, salt_buf: Buffer, identity_buf: Buffer, password_buf: Buffer, secret2_buf: Buffer) constructor(params: SrpParams, salt_buf: Buffer, identity_buf: Buffer, password_buf: Buffer, secret2_buf: Buffer)
constructor(params: SrpParams, verifier_buf: Buffer, secret2_buf: Buffer) constructor(params: SrpParams, verifier_buf: Buffer, secret2_buf: Buffer)
@ -516,7 +525,7 @@ export class Server {
assertIsBuffer(secret2_buf!, 'secret2'); assertIsBuffer(secret2_buf!, 'secret2');
this._b = new BigInteger(secret2_buf!); this._b = new BigInteger(secret2_buf!);
this._v = new BigInteger(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!));
this._I = identity_buf; this._I = identity_buf;
this._s = salt_buf as Buffer; this._s = salt_buf as Buffer;
@ -533,6 +542,7 @@ export class Server {
const identity = salt_buf as Identity; const identity = salt_buf as Identity;
[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
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');
@ -546,7 +556,7 @@ export class Server {
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(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
)); ));

View File

@ -1,5 +1,4 @@
import {hex} from '../lib/params'; import {hex} from '../params';
import BigInteger = require('../../lib/jsbn');
// 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(`

View File

@ -1,10 +1,8 @@
// @ts-ignore // @ts-ignore
import * as vows from 'vows'; import vows from 'vows';
import * as assert from 'assert'; import assert from 'assert';
import * as srp from '..'; import {SRP, SrpClient, SrpParams, SrpServer} from "../srp";
import BigInteger = require('../../lib/jsbn'); import BigInteger = require('../../jsbn/jsbn');
delete exports.__esModule;
interface Input { interface Input {
/** Identity */ /** Identity */
@ -36,7 +34,7 @@ interface ExpectedOutput {
import {N, g, I, p, a, A, b, B, s, v, u, S, K} from './hap_test_data'; 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'),
@ -77,14 +75,14 @@ function numequal(a: BigInteger, b: BigInteger, msg?: string) {
assert(a.compareTo(b) === 0, msg); assert(a.compareTo(b) === 0, msg);
} }
function checkVectors(params: srp.SrpParams, inputs: Input, expected: ExpectedOutput, useVerifier = true) { function checkVectors(params: SrpParams, inputs: Input, expected: ExpectedOutput, useVerifier = true) {
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 srp.Client(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 ?
new srp.Server(params, {username: inputs.I, salt: inputs.salt, verifier: expected.v}, inputs.b) : new SrpServer(params, {username: inputs.I, salt: inputs.salt, verifier: expected.v}, inputs.b) :
new srp.Server(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');

View File

@ -1,10 +1,8 @@
// @ts-ignore // @ts-ignore
import * as vows from 'vows'; import vows from 'vows';
import * as assert from 'assert'; import assert from 'assert';
import * as srp from '..'; import {SRP, SrpClient, SrpParams, SrpServer} from '../srp';
import BigInteger = require('../../lib/jsbn'); import BigInteger = require('../../jsbn/jsbn');
delete exports.__esModule;
/* /*
* Vectors from https://wiki.mozilla.org/Identity/AttachedServices/KeyServerProtocol * Vectors from https://wiki.mozilla.org/Identity/AttachedServices/KeyServerProtocol
@ -19,7 +17,7 @@ function hex(s: string) {
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];
interface Input { interface Input {
/** Identity */ /** Identity */
@ -318,12 +316,12 @@ function numequal(a: BigInteger, b: BigInteger, msg?: string) {
assert(a.compareTo(b) === 0, msg); assert(a.compareTo(b) === 0, msg);
} }
function checkVectors(params: srp.SrpParams, inputs: Input, expected: ExpectedOutput) { function checkVectors(params: SrpParams, inputs: Input, expected: ExpectedOutput) {
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 srp.Client(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 srp.Server(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');

View File

@ -1,12 +1,10 @@
// @ts-ignore // @ts-ignore
import * as vows from 'vows'; import vows from 'vows';
import * as assert from 'assert'; import assert from 'assert';
import * as srp from '..'; import {SRP, SrpClient, SrpServer} from '../srp';
import BigInteger = require('../../lib/jsbn'); import BigInteger = require('../../jsbn/jsbn');
delete exports.__esModule; const params = SRP.params[1024];
const params = srp.params[1024];
/* /*
* http://tools.ietf.org/html/rfc5054#appendix-B * http://tools.ietf.org/html/rfc5054#appendix-B
@ -60,11 +58,11 @@ function asHex(num: number | BigInteger) {
vows.describe('RFC 5054').addBatch({ vows.describe('RFC 5054').addBatch({
'Test vectors': { 'Test vectors': {
topic() { topic() {
return srp.computeVerifier(params, s, I, P); return SRP.computeVerifier(params, s, I, P);
}, },
'x'() { 'x'() {
const client = new srp.Client(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.equal(asHex(client._x), x_expected);
}, },
@ -74,37 +72,37 @@ vows.describe('RFC 5054').addBatch({
}, },
'k'() { 'k'() {
const client = new srp.Client(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.equal(asHex(client._k), k_expected);
}, },
'A'() { 'A'() {
const client = new srp.Client(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.equal(client.computeA().toString('hex'), A_expected);
}, },
'B'(v: Buffer) { 'B'(v: Buffer) {
const server = new srp.Server(params, v, b); const server = new SrpServer(params, v, b);
assert.equal(server.computeB().toString('hex'), B_expected); assert.equal(server.computeB().toString('hex'), B_expected);
}, },
'u'() { 'u'() {
const client = new srp.Client(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.equal(asHex(client._u), u_expected);
}, },
'S client'() { 'S client'() {
const client = new srp.Client(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.equal(client._S.toString('hex'), S_expected);
}, },
'S server'(v: Buffer) { 'S server'(v: Buffer) {
const server = new srp.Server(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.equal(server._S.toString('hex'), S_expected);

View File

@ -1,11 +1,9 @@
// @ts-ignore // @ts-ignore
import * as vows from 'vows'; import vows from 'vows';
import * as assert from 'assert'; import assert from 'assert';
import * as srp from '..'; import {SRP, SrpClient, SrpServer} from '../srp';
delete exports.__esModule; 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');
@ -13,7 +11,7 @@ const password = Buffer.from('password123');
assert(params, 'missing parameters'); assert(params, 'missing parameters');
let client: srp.Client, server: srp.Server; let client: SrpClient, server: SrpServer;
let a: Buffer, A: Buffer; let a: Buffer, A: Buffer;
let b: Buffer, B: Buffer; let b: Buffer, B: Buffer;
let verifier: Buffer; let verifier: Buffer;
@ -21,25 +19,25 @@ let verifier: Buffer;
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.equal(verifier.toString('hex'), 'f0e47f50f5dead8db8d93a279e3b62d6ff50854b31fbd3474a886bef916261717e84dd4fb8b4d27feaa5146db7b1cbbc274fdf96a132b5029c2cd72527427a9b9809d5a4d018252928b4fc343bc17ce63c1859d5806f5466014fc361002d8890aeb4d6316ff37331fc2761be0144c91cdd8e00ed0138c0ce51534d1b9a9ba629d7be34d2742dd4097daabc9ecb7aaad89e53c342b038f1d2adae1f2410b7884a3e9a124c357e421bccd4524467e1922660e0a4460c5f7c38c0877b65f6e32f28296282a93fc11bbabb7bb69bf1b3f9391991d8a86dd05e15000b7e38ba38a536bb0bf59c808ec25e791b8944719488b8087df8bfd7ff20822997a53f6c86f3d45d004476d6303301376bb25a9f94b552cce5ed40de5dd7da8027d754fa5f66738c7e3fc4ef3e20d625df62cbe6e7adfc21e47880d8a6ada37e60370fd4d8fc82672a90c29f2e72f35652649d68348de6f36d0e435c8bd42dd00155d35d501becc0661b43e04cdb2da84ce92b8bf49935d73d75efcbd1176d7bbccc3cc4d4b5fefcc02d478614ee1681d2ff3c711a61a7686eb852ae06fb8227be21fb8802719b1271ba1c02b13bbf0a2c2e459d9bedcc8d1269f6a785cb4563aa791b38fb038269f63f58f47e9051499549789269cc7b8ec7026fc34ba73289c4af829d5a532e723967ce9b6c023ef0fd0cfe37f51f10f19463b6534159a09ddd2f51f3b30033');
}, },
'create a and b': { 'create a and b': {
topic(this: any) {(async () => { topic(this: any) {(async () => {
a = await srp.genKey(64); a = await SRP.genKey(64);
b = await srp.genKey(32); b = await SRP.genKey(32);
this.callback(); this.callback();
})()}, })()},
'use a and b'() { 'use a and b'() {
client = new srp.Client(params, salt, identity, password, a, false); client = new SrpClient(params, salt, identity, password, a, false);
// client produces A // client produces A
A = client.computeA(); A = client.computeA();
// create server // create server
server = new srp.Server(params, verifier, b); server = new SrpServer(params, verifier, b);
// server produces B // server produces B
B = server.computeB(); B = server.computeB();
@ -70,8 +68,8 @@ vows.describe('srp.js').addBatch({
}, },
'server rejects wrong M1'() { 'server rejects wrong M1'() {
const bad_client = new srp.Client(params, salt, identity, Buffer.from('bad'), a, false); const bad_client = new SrpClient(params, salt, identity, Buffer.from('bad'), a, false);
const server2 = new srp.Server(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/);
}, },
@ -81,7 +79,7 @@ vows.describe('srp.js').addBatch({
// 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 srp.Server(params, verifier, b); var server2 = new SrpServer(params, verifier, b);
var Azero = Buffer.alloc(params.N_length_bits / 8); var Azero = Buffer.alloc(params.N_length_bits / 8);
Azero.fill(0); Azero.fill(0);
//! var AN = params.N.toBuffer(); //! var AN = params.N.toBuffer();
@ -95,7 +93,7 @@ vows.describe('srp.js').addBatch({
'client rejects bad B'() { 'client rejects bad B'() {
// 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 srp.Client(params, salt, identity, password, a, false); var client2 = new SrpClient(params, salt, identity, password, a, false);
var Bzero = Buffer.alloc(params.N_length_bits / 8); var 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();
@ -108,13 +106,13 @@ vows.describe('srp.js').addBatch({
}, },
'client rejects bad M2'() { 'client rejects bad M2'() {
client = new srp.Client(params, salt, identity, password, a, false); client = new SrpClient(params, salt, identity, password, a, false);
// client produces A // client produces A
A = client.computeA(); A = client.computeA();
// create server // create server
server = new srp.Server(params, verifier, b); server = new SrpServer(params, verifier, b);
// server produces B // server produces B
B = server.computeB(); B = server.computeB();

View File

@ -1,12 +1,22 @@
{ {
"compilerOptions": { "compilerOptions": {
"strict": true, "target": "ES2018",
"module": "commonjs",
"lib": [
"es2015",
"es2016",
"es2017",
"es2018"
],
"declaration": true, "declaration": true,
"declarationMap": true,
"sourceMap": true, "sourceMap": true,
"outDir": "lib",
"rootDir": "src", "rootDir": "src",
"outDir": "dist" "strict": true,
"esModuleInterop": true
}, },
"include": [ "include": [
"src/**/*" "src/"
] ]
} }