diff --git a/.gitignore b/.gitignore index 0c0aa04..1118d6c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,24 @@ -/node_modules/ -/npm-debug.log +# dependencies +/node_modules +/.pnp +.pnp.js + +yarn.lock +package-lock.json +pnpm-lock.yaml + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +node_modules + +size-plugin.json +build diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..e4f08c3 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,10 @@ +{ + "tabWidth": 4, + "useTabs": true, + "singleQuote": true, + "jsxSingleQuote": true, + "semi": false, + "bracketSpacing": true, + "jsxBracketSameLine": false, + "arrowParens": "avoid" +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..9ecefa8 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode" +} \ No newline at end of file diff --git a/browser/sha256.js b/browser/sha256.js deleted file mode 100644 index 34cb349..0000000 --- a/browser/sha256.js +++ /dev/null @@ -1,34 +0,0 @@ -'use strict' - -const arrayBufferToHex = require('array-buffer-to-hex') -const encodeUtf8 = require('encode-utf8') -const hexToArrayBuffer = require('hex-to-array-buffer') -const rawSha256 = require('crypto-digest-sync/sha256') - -const SRPInteger = require('../lib/srp-integer') - -function concat (buffers) { - const length = buffers.reduce((mem, item) => mem + item.byteLength, 0) - const combined = new Uint8Array(length) - - buffers.reduce((offset, item) => { - combined.set(new Uint8Array(item), offset) - return offset + item.byteLength - }, 0) - - return combined.buffer -} - -module.exports = function sha256 (...args) { - const buffer = concat(args.map((arg) => { - if (arg instanceof SRPInteger) { - return hexToArrayBuffer(arg.toHex()) - } else if (typeof arg === 'string') { - return encodeUtf8(arg) - } else { - throw new TypeError('Expected string or SRPInteger') - } - })) - - return SRPInteger.fromHex(arrayBufferToHex(rawSha256(buffer))) -} diff --git a/client.d.ts b/client.d.ts deleted file mode 100644 index c5194b0..0000000 --- a/client.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -export interface Ephemeral { - public: string - secret: string -} - -export interface Session { - key: string - proof: string -} - -export function generateSalt(): string -export function derivePrivateKey(salt: string, username: string, password: string): string -export function deriveVerifier(privateKey: string): string -export function generateEphemeral(): Ephemeral -export function deriveSession(clientSecretEphemeral: string, serverPublicEphemeral: string, salt: string, username: string, privateKey: string): Session -export function verifySession(clientPublicEphemeral: string, clientSession: Session, serverSessionProof: string): void diff --git a/client.js b/client.js deleted file mode 100644 index 80d8013..0000000 --- a/client.js +++ /dev/null @@ -1,123 +0,0 @@ -'use strict' - -const params = require('./lib/params') -const SRPInteger = require('./lib/srp-integer') - -exports.generateSalt = function () { - // s User's salt - const s = SRPInteger.randomInteger(params.hashOutputBytes) - - return s.toHex() -} - -exports.derivePrivateKey = function (salt, username, password) { - // H() One-way hash function - const { H } = params - - // s User's salt - // I Username - // p Cleartext Password - const s = SRPInteger.fromHex(salt) - const I = String(username) - const p = String(password) - - // x = H(s, H(I | ':' | p)) (s is chosen randomly) - const x = H(s, H(`${I}:${p}`)) - - return x.toHex() -} - -exports.deriveVerifier = function (privateKey) { - // N A large safe prime (N = 2q+1, where q is prime) - // g A generator modulo N - const { N, g } = params - - // x Private key (derived from p and s) - const x = SRPInteger.fromHex(privateKey) - - // v = g^x (computes password verifier) - const v = g.modPow(x, N) - - return v.toHex() -} - -exports.generateEphemeral = function () { - // N A large safe prime (N = 2q+1, where q is prime) - // g A generator modulo N - const { N, g } = params - - // A = g^a (a = random number) - const a = SRPInteger.randomInteger(params.hashOutputBytes) - const A = g.modPow(a, N) - - return { - secret: a.toHex(), - public: A.toHex() - } -} - -exports.deriveSession = function (clientSecretEphemeral, serverPublicEphemeral, salt, username, privateKey) { - // N A large safe prime (N = 2q+1, where q is prime) - // g A generator modulo N - // k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6) - // H() One-way hash function - const { N, g, k, H } = params - - // a Secret ephemeral values - // B Public ephemeral values - // s User's salt - // I Username - // x Private key (derived from p and s) - const a = SRPInteger.fromHex(clientSecretEphemeral) - const B = SRPInteger.fromHex(serverPublicEphemeral) - const s = SRPInteger.fromHex(salt) - const I = String(username) - const x = SRPInteger.fromHex(privateKey) - - // A = g^a (a = random number) - const A = g.modPow(a, N) - - // B % N > 0 - if (B.mod(N).equals(SRPInteger.ZERO)) { - // fixme: .code, .statusCode, etc. - throw new Error('The server sent an invalid public ephemeral') - } - - // u = H(A, B) - const u = H(A, B) - - // S = (B - kg^x) ^ (a + ux) - const S = B.subtract(k.multiply(g.modPow(x, N))).modPow(a.add(u.multiply(x)), N) - - // K = H(S) - const K = H(S) - - // M = H(H(N) xor H(g), H(I), s, A, B, K) - const M = H(H(N).xor(H(g)), H(I), s, A, B, K) - - return { - key: K.toHex(), - proof: M.toHex() - } -} - -exports.verifySession = function (clientPublicEphemeral, clientSession, serverSessionProof) { - // H() One-way hash function - const { H } = params - - // A Public ephemeral values - // M Proof of K - // K Shared, strong session key - const A = SRPInteger.fromHex(clientPublicEphemeral) - const M = SRPInteger.fromHex(clientSession.proof) - const K = SRPInteger.fromHex(clientSession.key) - - // H(A, M, K) - const expected = H(A, M, K) - const actual = SRPInteger.fromHex(serverSessionProof) - - if (!actual.equals(expected)) { - // fixme: .code, .statusCode, etc. - throw new Error('Server provided session proof is invalid') - } -} diff --git a/es5/client.d.ts b/es5/client.d.ts new file mode 100644 index 0000000..fe584f0 --- /dev/null +++ b/es5/client.d.ts @@ -0,0 +1,10 @@ +import { Session } from '.'; +export declare const generateSalt: () => string; +export declare const derivePrivateKey: (salt: string, username: string, password: string) => string; +export declare const deriveVerifier: (privateKey: string) => string; +export declare const generateEphemeral: () => { + secret: string; + public: string; +}; +export declare const deriveSession: (clientSecretEphemeral: string, serverPublicEphemeral: string, salt: string, username: string, privateKey: string) => Session; +export declare const verifySession: (clientPublicEphemeral: string, clientSession: Session, serverSessionProof: string) => void; diff --git a/es5/client.js b/es5/client.js new file mode 100644 index 0000000..5d4859c --- /dev/null +++ b/es5/client.js @@ -0,0 +1,130 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.verifySession = exports.deriveSession = exports.generateEphemeral = exports.deriveVerifier = exports.derivePrivateKey = exports.generateSalt = void 0; +var params = __importStar(require("./params")); +var srp_integer_1 = __importDefault(require("./srp-integer")); +var generateSalt = function () { + // s User's salt + var s = srp_integer_1.default.randomInteger(params.hashOutputBytes); + return s.toHex(); +}; +exports.generateSalt = generateSalt; +var derivePrivateKey = function (salt, username, password) { + // H() One-way hash function + var H = params.H; + // s User's salt + // I Username + // p Cleartext Password + var s = srp_integer_1.default.fromHex(salt); + var I = String(username); + var p = String(password); + // x = H(s, H(I | p)) (s is chosen randomly) + /** Editor's note + * Error happening here on SRPInteger.fromHex: 'Expected string to be an even number of characters' when calling hexToArrayBuffer + */ + var x = H(s, H(srp_integer_1.default.fromHex(I + ":" + p))); + return x.toHex(); +}; +exports.derivePrivateKey = derivePrivateKey; +var deriveVerifier = function (privateKey) { + // N A large safe prime (N = 2q+1, where q is prime) + // g A generator modulo N + var N = params.N, g = params.g; + // x Private key (derived from p and s) + var x = srp_integer_1.default.fromHex(privateKey); + // v = g^x (computes password verifier) + var v = g.modPow(x, N); + return v.toHex(); +}; +exports.deriveVerifier = deriveVerifier; +var generateEphemeral = function () { + // N A large safe prime (N = 2q+1, where q is prime) + // g A generator modulo N + var N = params.N, g = params.g; + // A = g^a (a = random number) + var a = srp_integer_1.default.randomInteger(params.hashOutputBytes); + var A = g.modPow(a, N); + return { + secret: a.toHex(), + public: A.toHex(), + }; +}; +exports.generateEphemeral = generateEphemeral; +var deriveSession = function (clientSecretEphemeral, serverPublicEphemeral, salt, username, privateKey) { + // N A large safe prime (N = 2q+1, where q is prime) + // g A generator modulo N + // k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6) + // H() One-way hash function + var N = params.N, g = params.g, k = params.k, H = params.H; + // a Secret ephemeral values + // B Public ephemeral values + // s User's salt + // I Username + // x Private key (derived from p and s) + var a = srp_integer_1.default.fromHex(clientSecretEphemeral); + var B = srp_integer_1.default.fromHex(serverPublicEphemeral); + var s = srp_integer_1.default.fromHex(salt); + var I = String(username); + var x = srp_integer_1.default.fromHex(privateKey); + // A = g^a (a = random number) + var A = g.modPow(a, N); + // B % N > 0 + if (B.mod(N).equals(srp_integer_1.default.ZERO)) { + // fixme: .code, .statusCode, etc. + throw new Error('The server sent an invalid public ephemeral'); + } + // u = H(A, B) + var u = H(A, B); + // S = (B - kg^x) ^ (a + ux) + var S = B.subtract(k.multiply(g.modPow(x, N))).modPow(a.add(u.multiply(x)), N); + // K = H(S) + var K = H(S); + // M = H(H(N) xor H(g), H(I), s, A, B, K) + var M = H(H(N).xor(H(g)), H(srp_integer_1.default.fromHex(I)), s, A, B, K); + return { + key: K.toHex(), + proof: M.toHex(), + }; +}; +exports.deriveSession = deriveSession; +var verifySession = function (clientPublicEphemeral, clientSession, serverSessionProof) { + // H() One-way hash function + var H = params.H; + // A Public ephemeral values + // M Proof of K + // K Shared, strong session key + var A = srp_integer_1.default.fromHex(clientPublicEphemeral); + var M = srp_integer_1.default.fromHex(clientSession.proof); + var K = srp_integer_1.default.fromHex(clientSession.key); + // H(A, M, K) + var expected = H(A, M, K); + var actual = srp_integer_1.default.fromHex(serverSessionProof); + if (!actual.equals(expected)) { + // fixme: .code, .statusCode, etc. + throw new Error('Server provided session proof is invalid'); + } +}; +exports.verifySession = verifySession; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2NsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQ0EsK0NBQWtDO0FBQ2xDLDhEQUFzQztBQUUvQixJQUFNLFlBQVksR0FBRztJQUMzQixtQkFBbUI7SUFDbkIsSUFBTSxDQUFDLEdBQUcscUJBQVUsQ0FBQyxhQUFhLENBQUMsTUFBTSxDQUFDLGVBQWUsQ0FBQyxDQUFBO0lBRTFELE9BQU8sQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFBO0FBQ2pCLENBQUMsQ0FBQTtBQUxZLFFBQUEsWUFBWSxnQkFLeEI7QUFFTSxJQUFNLGdCQUFnQixHQUFHLFVBQy9CLElBQVksRUFDWixRQUFnQixFQUNoQixRQUFnQjtJQUVoQiw2QkFBNkI7SUFDckIsSUFBQSxDQUFDLEdBQUssTUFBTSxFQUFYLENBQVc7SUFFcEIsbUJBQW1CO0lBQ25CLGdCQUFnQjtJQUNoQiwwQkFBMEI7SUFDMUIsSUFBTSxDQUFDLEdBQUcscUJBQVUsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUE7SUFDbEMsSUFBTSxDQUFDLEdBQUcsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFBO0lBQzFCLElBQU0sQ0FBQyxHQUFHLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQTtJQUUxQiw2Q0FBNkM7SUFDN0M7O09BRUc7SUFDSCxJQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxxQkFBVSxDQUFDLE9BQU8sQ0FBSSxDQUFDLFNBQUksQ0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFBO0lBRWxELE9BQU8sQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFBO0FBQ2pCLENBQUMsQ0FBQTtBQXRCWSxRQUFBLGdCQUFnQixvQkFzQjVCO0FBRU0sSUFBTSxjQUFjLEdBQUcsVUFBQyxVQUFrQjtJQUNoRCx1REFBdUQ7SUFDdkQsNEJBQTRCO0lBQ3BCLElBQUEsQ0FB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 \ No newline at end of file diff --git a/es5/index.d.ts b/es5/index.d.ts new file mode 100644 index 0000000..b1b2516 --- /dev/null +++ b/es5/index.d.ts @@ -0,0 +1,6 @@ +export * as Client from './client'; +export * as Server from './server'; +export declare type Session = { + key: string; + proof: string; +}; diff --git a/es5/index.js b/es5/index.js new file mode 100644 index 0000000..aca1c4d --- /dev/null +++ b/es5/index.js @@ -0,0 +1,25 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Server = exports.Client = void 0; +exports.Client = __importStar(require("./client")); +exports.Server = __importStar(require("./server")); +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLG1EQUFrQztBQUNsQyxtREFBa0MifQ== \ No newline at end of file diff --git a/es5/params.d.ts b/es5/params.d.ts new file mode 100644 index 0000000..6e9b541 --- /dev/null +++ b/es5/params.d.ts @@ -0,0 +1,6 @@ +import SRPInteger from './srp-integer'; +export declare const N: SRPInteger; +export declare const g: SRPInteger; +export declare const k: SRPInteger; +export declare const H: (...args: SRPInteger[]) => SRPInteger; +export declare const hashOutputBytes: number; diff --git a/es5/params.js b/es5/params.js new file mode 100644 index 0000000..f506a08 --- /dev/null +++ b/es5/params.js @@ -0,0 +1,25 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.hashOutputBytes = exports.H = exports.k = exports.g = exports.N = void 0; +var sha256_1 = __importDefault(require("./sha256")); +var srp_integer_1 = __importDefault(require("./srp-integer")); +var _2048bitGroup = "\nAC6BDB41 324A9A9B F166DE5E 1389582F AF72B665 1987EE07 FC319294\n3DB56050 A37329CB B4A099ED 8193E075 7767A13D D52312AB 4B03310D\nCD7F48A9 DA04FD50 E8083969 EDB767B0 CF609517 9A163AB3 661A05FB\nD5FAAAE8 2918A996 2F0B93B8 55F97993 EC975EEA A80D740A DBF4FF74\n7359D041 D5C33EA7 1D281E44 6B14773B CA97B43A 23FB8016 76BD207A\n436C6481 F1D2B907 8717461A 5B9D32E6 88F87748 544523B5 24B0D57D\n5EA77A27 75D2ECFA 032CFBDB F52FB378 61602790 04E57AE6 AF874E73\n03CE5329 9CCC041C 7BC308D8 2A5698F3 A8D0C382 71AE35F8 E9DBFBB6\n94B5C803 D89F7AE4 35DE236D 525F5475 9B65E372 FCD68EF2 0FA7111F\n9E4AFF73\n".trim(); +var input = { + largeSafePrime: _2048bitGroup, + generatorModulo: '02', + hashFunction: 'sha256', + hashOutputBytes: 256 / 8, +}; +// N A large safe prime (N = 2q+1, where q is prime) +// g A generator modulo N +// k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6) +// H() One-way hash function +exports.N = srp_integer_1.default.fromHex(input.largeSafePrime.replace(/\s+/g, '')); +exports.g = srp_integer_1.default.fromHex(input.generatorModulo.replace(/\s+/g, '')); +exports.k = sha256_1.default(exports.N, exports.g); +exports.H = sha256_1.default; +exports.hashOutputBytes = input.hashOutputBytes; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGFyYW1zLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3BhcmFtcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSxvREFBNkI7QUFDN0IsOERBQXNDO0FBRXRDLElBQU0sYUFBYSxHQUFHLDhrQkFXckIsQ0FBQyxJQUFJLEVBQUUsQ0FBQTtBQUVSLElBQU0sS0FBSyxHQUFHO0lBQ2IsY0FBYyxFQUFFLGFBQWE7SUFDN0IsZUFBZSxFQUFFLElBQUk7SUFDckIsWUFBWSxFQUFFLFFBQVE7SUFDdEIsZUFBZSxFQUFFLEdBQUcsR0FBRyxDQUFDO0NBQ3hCLENBQUE7QUFFRCx1REFBdUQ7QUFDdkQsNEJBQTRCO0FBQzVCLDRFQUE0RTtBQUM1RSw2QkFBNkI7QUFDaEIsUUFBQSxDQUFDLEdBQUcscUJBQVUsQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLGNBQWMsQ0FBQyxPQUFPLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUE7QUFDaEUsUUFBQSxDQUFDLEdBQUcscUJBQVUsQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLGVBQWUsQ0FBQyxPQUFPLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUE7QUFDakUsUUFBQSxDQUFDLEdBQUcsZ0JBQU0sQ0FBQyxTQUFDLEVBQUUsU0FBQyxDQUFDLENBQUE7QUFDaEIsUUFBQSxDQUFDLEdBQUcsZ0JBQU0sQ0FBQTtBQUVWLFFBQUEsZUFBZSxHQUFHLEtBQUssQ0FBQyxlQUFlLENBQUEifQ== \ No newline at end of file diff --git a/es5/server.d.ts b/es5/server.d.ts new file mode 100644 index 0000000..90cf671 --- /dev/null +++ b/es5/server.d.ts @@ -0,0 +1,6 @@ +import { Session } from '.'; +export declare const generateEphemeral: (verifier: string) => { + secret: string; + public: string; +}; +export declare const deriveSession: (serverSecretEphemeral: string, clientPublicEphemeral: string, salt: string, username: string, verifier: string, clientSessionProof: string) => Session; diff --git a/es5/server.js b/es5/server.js new file mode 100644 index 0000000..c127675 --- /dev/null +++ b/es5/server.js @@ -0,0 +1,90 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.deriveSession = exports.generateEphemeral = void 0; +var params = __importStar(require("./params")); +var srp_integer_1 = __importDefault(require("./srp-integer")); +var generateEphemeral = function (verifier) { + // N A large safe prime (N = 2q+1, where q is prime) + // g A generator modulo N + // k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6) + var N = params.N, g = params.g, k = params.k; + // v Password verifier + var v = srp_integer_1.default.fromHex(verifier); + // B = kv + g^b (b = random number) + var b = srp_integer_1.default.randomInteger(params.hashOutputBytes); + var B = k.multiply(v).add(g.modPow(b, N)).mod(N); + return { + secret: b.toHex(), + public: B.toHex(), + }; +}; +exports.generateEphemeral = generateEphemeral; +var deriveSession = function (serverSecretEphemeral, clientPublicEphemeral, salt, username, verifier, clientSessionProof) { + // N A large safe prime (N = 2q+1, where q is prime) + // g A generator modulo N + // k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6) + // H() One-way hash function + var N = params.N, g = params.g, k = params.k, H = params.H; + // b Secret ephemeral values + // A Public ephemeral values + // s User's salt + // p Cleartext Password + // I Username + // v Password verifier + var b = srp_integer_1.default.fromHex(serverSecretEphemeral); + var A = srp_integer_1.default.fromHex(clientPublicEphemeral); + var s = srp_integer_1.default.fromHex(salt); + var I = String(username); + var v = srp_integer_1.default.fromHex(verifier); + // B = kv + g^b (b = random number) + var B = k.multiply(v).add(g.modPow(b, N)).mod(N); + // A % N > 0 + if (A.mod(N).equals(srp_integer_1.default.ZERO)) { + // fixme: .code, .statusCode, etc. + throw new Error('The client sent an invalid public ephemeral'); + } + // u = H(A, B) + var u = H(A, B); + // S = (Av^u) ^ b (computes session key) + var S = A.multiply(v.modPow(u, N)).modPow(b, N); + // K = H(S) + var K = H(S); + // M = H(H(N) xor H(g), H(I), s, A, B, K) + var M = H(H(N).xor(H(g)), H(srp_integer_1.default.fromHex(I)), s, A, B, K); + var expected = M; + var actual = srp_integer_1.default.fromHex(clientSessionProof); + if (!actual.equals(expected)) { + // fixme: .code, .statusCode, etc. + throw new Error('Client provided session proof is invalid'); + } + // P = H(A, M, K) + var P = H(A, M, K); + return { + key: K.toHex(), + proof: P.toHex(), + }; +}; +exports.deriveSession = deriveSession; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3NlcnZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQ0EsK0NBQWtDO0FBQ2xDLDhEQUFzQztBQUUvQixJQUFNLGlCQUFpQixHQUFHLFVBQUMsUUFBZ0I7SUFDakQsdURBQXVEO0lBQ3ZELDRCQUE0QjtJQUM1Qiw0RUFBNEU7SUFDcEUsSUFBQSxDQUFDLEdBQVcsTUFBTSxFQUFqQixFQUFFLENBQUMsR0FBUSxNQUFNLEVBQWQsRUFBRSxDQUFDLEdBQUssTUFBTSxFQUFYLENBQVc7SUFFMUIseUJBQXlCO0lBQ3pCLElBQU0sQ0FBQyxHQUFHLHFCQUFVLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFBO0lBRXRDLCtDQUErQztJQUMvQyxJQUFNLENBQUMsR0FBRyxxQkFBVSxDQUFDLGFBQWEsQ0FBQyxNQUFNLENBQUMsZUFBZSxDQUFDLENBQUE7SUFDMUQsSUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUE7SUFFbEQsT0FBTztRQUNOLE1BQU0sRUFBRSxDQUFDLENBQUMsS0FBSyxFQUFFO1FBQ2pCLE1BQU0sRUFBRSxDQUFDLENBQUMsS0FBSyxFQUFFO0tBQ2pCLENBQUE7QUFDRixDQUFDLENBQUE7QUFqQlksUUFBQSxpQkFBaUIscUJBaUI3QjtBQUVNLElBQU0sYUFBYSxHQUFHLFVBQzVCLHFCQUE2QixFQUM3QixxQkFBNkIsRUFDN0IsSUFBWSxFQUNaLFFBQWdCLEVBQ2hCLFFBQWdCLEVBQ2hCLGtCQUEwQjtJQUUxQix1REFBdUQ7SUFDdkQsNEJBQTRCO0lBQzVCLDRFQUE0RTtJQUM1RSw2QkFBNkI7SUFDckIsSUFBQSxDQUFDLEdB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 \ No newline at end of file diff --git a/es5/sha256.d.ts b/es5/sha256.d.ts new file mode 100644 index 0000000..01f4b89 --- /dev/null +++ b/es5/sha256.d.ts @@ -0,0 +1,3 @@ +import SRPInteger from './srp-integer'; +declare const sha256: (...args: SRPInteger[]) => SRPInteger; +export default sha256; diff --git a/es5/sha256.js b/es5/sha256.js new file mode 100644 index 0000000..3b0131b --- /dev/null +++ b/es5/sha256.js @@ -0,0 +1,39 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var array_buffer_to_hex_1 = __importDefault(require("array-buffer-to-hex")); +var encode_utf8_1 = __importDefault(require("encode-utf8")); +var hex_to_array_buffer_1 = __importDefault(require("hex-to-array-buffer")); +var sha256_1 = __importDefault(require("crypto-digest-sync/sha256")); +var srp_integer_1 = __importDefault(require("./srp-integer")); +function concat(buffers) { + var length = buffers.reduce(function (mem, item) { return mem + item.byteLength; }, 0); + var combined = new Uint8Array(length); + buffers.reduce(function (offset, item) { + combined.set(new Uint8Array(item), offset); + return offset + item.byteLength; + }, 0); + return combined.buffer; +} +var sha256 = function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + var buffer = concat(args.map(function (arg) { + if (arg instanceof srp_integer_1.default) { + return hex_to_array_buffer_1.default(arg.toHex()); + } + else if (typeof arg === 'string') { + return encode_utf8_1.default(arg); + } + else { + throw new TypeError('Expected string or SRPInteger'); + } + })); + return srp_integer_1.default.fromHex(array_buffer_to_hex_1.default(sha256_1.default(buffer))); +}; +exports.default = sha256; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2hhMjU2LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3NoYTI1Ni50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUFBLDRFQUFrRDtBQUNsRCw0REFBb0M7QUFDcEMsNEVBQWtEO0FBQ2xELHFFQUFpRDtBQUNqRCw4REFBc0M7QUFFdEMsU0FBUyxNQUFNLENBQUMsT0FBc0I7SUFDckMsSUFBTSxNQUFNLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQyxVQUFDLEdBQUcsRUFBRSxJQUFJLElBQUssT0FBQSxHQUFHLEdBQUcsSUFBSSxDQUFDLFVBQVUsRUFBckIsQ0FBcUIsRUFBRSxDQUFDLENBQUMsQ0FBQTtJQUN0RSxJQUFNLFFBQVEsR0FBRyxJQUFJLFVBQVUsQ0FBQyxNQUFNLENBQUMsQ0FBQTtJQUV2QyxPQUFPLENBQUMsTUFBTSxDQUFDLFVBQUMsTUFBTSxFQUFFLElBQUk7UUFDM0IsUUFBUSxDQUFDLEdBQUcsQ0FBQyxJQUFJLFVBQVUsQ0FBQyxJQUFJLENBQUMsRUFBRSxNQUFNLENBQUMsQ0FBQTtRQUMxQyxPQUFPLE1BQU0sR0FBRyxJQUFJLENBQUMsVUFBVSxDQUFBO0lBQ2hDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQTtJQUVMLE9BQU8sUUFBUSxDQUFDLE1BQU0sQ0FBQTtBQUN2QixDQUFDO0FBRUQsSUFBTSxNQUFNLEdBQUc7SUFBQyxjQUFxQjtTQUFyQixVQUFxQixFQUFyQixxQkFBcUIsRUFBckIsSUFBcUI7UUFBckIseUJBQXFCOztJQUNwQyxJQUFNLE1BQU0sR0FBRyxNQUFNLENBQ3BCLElBQUksQ0FBQyxHQUFHLENBQUMsVUFBQyxHQUFlO1FBQ3hCLElBQUksR0FBRyxZQUFZLHFCQUFVLEVBQUU7WUFDOUIsT0FBTyw2QkFBZ0IsQ0FBQyxHQUFHLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQTtTQUNwQzthQUFNLElBQUksT0FBTyxHQUFHLEtBQUssUUFBUSxFQUFFO1lBQ25DLE9BQU8scUJBQVUsQ0FBQyxHQUFHLENBQUMsQ0FBQTtTQUN0QjthQUFNO1lBQ04sTUFBTSxJQUFJLFNBQVMsQ0FBQywrQkFBK0IsQ0FBQyxDQUFBO1NBQ3BEO0lBQ0YsQ0FBQyxDQUFDLENBQ0YsQ0FBQTtJQUVELE9BQU8scUJBQVUsQ0FBQyxPQUFPLENBQUMsNkJBQWdCLENBQUMsZ0JBQVMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUE7QUFDL0QsQ0FBQyxDQUFBO0FBRUQsa0JBQWUsTUFBTSxDQUFBIn0= \ No newline at end of file diff --git a/es5/srp-integer.d.ts b/es5/srp-integer.d.ts new file mode 100644 index 0000000..e85ad5a --- /dev/null +++ b/es5/srp-integer.d.ts @@ -0,0 +1,21 @@ +import { BigInteger } from 'jsbn'; +declare const kBigInteger: unique symbol; +declare const kHexLength: unique symbol; +declare class SRPInteger { + [kBigInteger]: BigInteger; + [kHexLength]: number | null; + static fromHex: (input: string) => SRPInteger; + static randomInteger: (bytes: number) => SRPInteger; + static ZERO: SRPInteger; + constructor(bigInteger: BigInteger, hexLength: number | null); + add(val: SRPInteger): SRPInteger; + equals(val: SRPInteger): boolean; + multiply(val: SRPInteger): SRPInteger; + modPow(exponent: SRPInteger, m: SRPInteger): SRPInteger; + mod(m: SRPInteger): SRPInteger; + subtract(val: SRPInteger): SRPInteger; + xor(val: SRPInteger): SRPInteger; + inspect(): string; + toHex(): string; +} +export default SRPInteger; diff --git a/es5/srp-integer.js b/es5/srp-integer.js new file mode 100644 index 0000000..0cf1b04 --- /dev/null +++ b/es5/srp-integer.js @@ -0,0 +1,60 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var crypto_random_hex_1 = __importDefault(require("crypto-random-hex")); +var jsbn_1 = require("jsbn"); +var padStart = function (str, maxLength, fillString) { + return str.padStart(maxLength, fillString); +}; +var kBigInteger = Symbol('big-integer'); +var kHexLength = Symbol('hex-length'); +var SRPInteger = /** @class */ (function () { + function SRPInteger(bigInteger, hexLength) { + this[kBigInteger] = bigInteger; + this[kHexLength] = hexLength; + } + SRPInteger.prototype.add = function (val) { + return new SRPInteger(this[kBigInteger].add(val[kBigInteger]), null); + }; + SRPInteger.prototype.equals = function (val) { + return this[kBigInteger].equals(val[kBigInteger]); + }; + SRPInteger.prototype.multiply = function (val) { + return new SRPInteger(this[kBigInteger].multiply(val[kBigInteger]), null); + }; + SRPInteger.prototype.modPow = function (exponent, m) { + return new SRPInteger(this[kBigInteger].modPow(exponent[kBigInteger], m[kBigInteger]), m[kHexLength]); + }; + SRPInteger.prototype.mod = function (m) { + return new SRPInteger(this[kBigInteger].mod(m[kBigInteger]), m[kHexLength]); + }; + SRPInteger.prototype.subtract = function (val) { + return new SRPInteger(this[kBigInteger].subtract(val[kBigInteger]), this[kHexLength]); + }; + SRPInteger.prototype.xor = function (val) { + return new SRPInteger(this[kBigInteger].xor(val[kBigInteger]), this[kHexLength]); + }; + SRPInteger.prototype.inspect = function () { + var hex = this[kBigInteger].toString(16); + return " 16 ? '...' : '') + ">"; + }; + SRPInteger.prototype.toHex = function () { + var _a; + if (this[kHexLength] === null) { + throw new Error('This SRPInteger has no specified length'); + } + return padStart(this[kBigInteger].toString(16), (_a = this[kHexLength]) !== null && _a !== void 0 ? _a : 0, '0'); + }; + return SRPInteger; +}()); +SRPInteger.fromHex = function (input) { + return new SRPInteger(new jsbn_1.BigInteger(input, 16), input.length); +}; +SRPInteger.randomInteger = function (bytes) { + return SRPInteger.fromHex(crypto_random_hex_1.default(bytes)); +}; +SRPInteger.ZERO = new SRPInteger(new jsbn_1.BigInteger('0'), null); +exports.default = SRPInteger; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3JwLWludGVnZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvc3JwLWludGVnZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSx3RUFBeUM7QUFDekMsNkJBQWlDO0FBRWpDLElBQU0sUUFBUSxHQUFHLFVBQUMsR0FBVyxFQUFFLFNBQWlCLEVBQUUsVUFBa0I7SUFDbkUsT0FBQSxHQUFHLENBQUMsUUFBUSxDQUFDLFNBQVMsRUFBRSxVQUFVLENBQUM7QUFBbkMsQ0FBbUMsQ0FBQTtBQUVwQyxJQUFNLFdBQVcsR0FBRyxNQUFNLENBQUMsYUFBYSxDQUFDLENBQUE7QUFDekMsSUFBTSxVQUFVLEdBQUcsTUFBTSxDQUFDLFlBQVksQ0FBQyxDQUFBO0FBRXZDO0lBT0Msb0JBQVksVUFBc0IsRUFBRSxTQUF3QjtRQUMzRCxJQUFJLENBQUMsV0FBVyxDQUFDLEdBQUcsVUFBVSxDQUFBO1FBQzlCLElBQUksQ0FBQyxVQUFVLENBQUMsR0FBRyxTQUFTLENBQUE7SUFDN0IsQ0FBQztJQUVELHdCQUFHLEdBQUgsVUFBSSxHQUFlO1FBQ2xCLE9BQU8sSUFBSSxVQUFVLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsV0FBVyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQTtJQUNyRSxDQUFDO0lBRUQsMkJBQU0sR0FBTixVQUFPLEdBQWU7UUFDckIsT0FBTyxJQUFJLENBQUMsV0FBVyxDQUFDLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxXQUFXLENBQUMsQ0FBQyxDQUFBO0lBQ2xELENBQUM7SUFFRCw2QkFBUSxHQUFSLFVBQVMsR0FBZTtRQUN2QixPQUFPLElBQUksVUFBVSxDQUNwQixJQUFJLENBQUMsV0FBVyxDQUFDLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxXQUFXLENBQUMsQ0FBQyxFQUM1QyxJQUFJLENBQ0osQ0FBQTtJQUNGLENBQUM7SUFFRCwyQkFBTSxHQUFOLFVB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 \ No newline at end of file diff --git a/es6/client.d.ts b/es6/client.d.ts new file mode 100644 index 0000000..fe584f0 --- /dev/null +++ b/es6/client.d.ts @@ -0,0 +1,10 @@ +import { Session } from '.'; +export declare const generateSalt: () => string; +export declare const derivePrivateKey: (salt: string, username: string, password: string) => string; +export declare const deriveVerifier: (privateKey: string) => string; +export declare const generateEphemeral: () => { + secret: string; + public: string; +}; +export declare const deriveSession: (clientSecretEphemeral: string, serverPublicEphemeral: string, salt: string, username: string, privateKey: string) => Session; +export declare const verifySession: (clientPublicEphemeral: string, clientSession: Session, serverSessionProof: string) => void; diff --git a/es6/client.js b/es6/client.js new file mode 100644 index 0000000..e363650 --- /dev/null +++ b/es6/client.js @@ -0,0 +1,99 @@ +import * as params from './params'; +import SRPInteger from './srp-integer'; +export const generateSalt = () => { + // s User's salt + const s = SRPInteger.randomInteger(params.hashOutputBytes); + return s.toHex(); +}; +export const derivePrivateKey = (salt, username, password) => { + // H() One-way hash function + const { H } = params; + // s User's salt + // I Username + // p Cleartext Password + const s = SRPInteger.fromHex(salt); + const I = String(username); + const p = String(password); + // x = H(s, H(I | p)) (s is chosen randomly) + /** Editor's note + * Error happening here on SRPInteger.fromHex: 'Expected string to be an even number of characters' when calling hexToArrayBuffer + */ + const x = H(s, H(SRPInteger.fromHex(`${I}:${p}`))); + return x.toHex(); +}; +export const deriveVerifier = (privateKey) => { + // N A large safe prime (N = 2q+1, where q is prime) + // g A generator modulo N + const { N, g } = params; + // x Private key (derived from p and s) + const x = SRPInteger.fromHex(privateKey); + // v = g^x (computes password verifier) + const v = g.modPow(x, N); + return v.toHex(); +}; +export const generateEphemeral = () => { + // N A large safe prime (N = 2q+1, where q is prime) + // g A generator modulo N + const { N, g } = params; + // A = g^a (a = random number) + const a = SRPInteger.randomInteger(params.hashOutputBytes); + const A = g.modPow(a, N); + return { + secret: a.toHex(), + public: A.toHex(), + }; +}; +export const deriveSession = (clientSecretEphemeral, serverPublicEphemeral, salt, username, privateKey) => { + // N A large safe prime (N = 2q+1, where q is prime) + // g A generator modulo N + // k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6) + // H() One-way hash function + const { N, g, k, H } = params; + // a Secret ephemeral values + // B Public ephemeral values + // s User's salt + // I Username + // x Private key (derived from p and s) + const a = SRPInteger.fromHex(clientSecretEphemeral); + const B = SRPInteger.fromHex(serverPublicEphemeral); + const s = SRPInteger.fromHex(salt); + const I = String(username); + const x = SRPInteger.fromHex(privateKey); + // A = g^a (a = random number) + const A = g.modPow(a, N); + // B % N > 0 + if (B.mod(N).equals(SRPInteger.ZERO)) { + // fixme: .code, .statusCode, etc. + throw new Error('The server sent an invalid public ephemeral'); + } + // u = H(A, B) + const u = H(A, B); + // S = (B - kg^x) ^ (a + ux) + const S = B.subtract(k.multiply(g.modPow(x, N))).modPow(a.add(u.multiply(x)), N); + // K = H(S) + const K = H(S); + // M = H(H(N) xor H(g), H(I), s, A, B, K) + const M = H(H(N).xor(H(g)), H(SRPInteger.fromHex(I)), s, A, B, K); + return { + key: K.toHex(), + proof: M.toHex(), + }; +}; +export const verifySession = (clientPublicEphemeral, clientSession, serverSessionProof) => { + // H() One-way hash function + const { H } = params; + // A Public ephemeral values + // M Proof of K + // K Shared, strong session key + const A = SRPInteger.fromHex(clientPublicEphemeral); + const M = SRPInteger.fromHex(clientSession.proof); + const K = SRPInteger.fromHex(clientSession.key); + // H(A, M, K) + const expected = H(A, M, K); + const actual = SRPInteger.fromHex(serverSessionProof); + if (!actual.equals(expected)) { + // fixme: .code, .statusCode, etc. + throw new Error('Server provided session proof is invalid'); + } +}; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2NsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEtBQUssTUFBTSxNQUFNLFVBQVUsQ0FBQTtBQUNsQyxPQUFPLFVBQVUsTUFBTSxlQUFlLENBQUE7QUFFdEMsTUFBTSxDQUFDLE1BQU0sWUFBWSxHQUFHLEdBQUcsRUFBRTtJQUNoQyxtQkFBbUI7SUFDbkIsTUFBTSxDQUFDLEdBQUcsVUFBVSxDQUFDLGFBQWEsQ0FBQyxNQUFNLENBQUMsZUFBZSxDQUFDLENBQUE7SUFFMUQsT0FBTyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUE7QUFDakIsQ0FBQyxDQUFBO0FBRUQsTUFBTSxDQUFDLE1BQU0sZ0JBQWdCLEdBQUcsQ0FDL0IsSUFBWSxFQUNaLFFBQWdCLEVBQ2hCLFFBQWdCLEVBQ2YsRUFBRTtJQUNILDZCQUE2QjtJQUM3QixNQUFNLEVBQUUsQ0FBQyxFQUFFLEdBQUcsTUFBTSxDQUFBO0lBRXBCLG1CQUFtQjtJQUNuQixnQkFBZ0I7SUFDaEIsMEJBQTBCO0lBQzFCLE1BQU0sQ0FBQyxHQUFHLFVBQVUsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUE7SUFDbEMsTUFBTSxDQUFDLEdBQUcsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFBO0lBQzFCLE1BQU0sQ0FBQyxHQUFHLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQTtJQUUxQiw2Q0FBNkM7SUFDN0M7O09BRUc7SUFDSCxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFBO0lBRWxELE9BQU8sQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFBO0FBQ2pCLENBQUMsQ0FBQTtBQUVELE1BQU0sQ0FBQyxNQUFNLGNBQWMsR0FBRyxDQUFDLFVBQWtCLEVBQUUsRUFBRTtJ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 \ No newline at end of file diff --git a/es6/index.d.ts b/es6/index.d.ts new file mode 100644 index 0000000..b1b2516 --- /dev/null +++ b/es6/index.d.ts @@ -0,0 +1,6 @@ +export * as Client from './client'; +export * as Server from './server'; +export declare type Session = { + key: string; + proof: string; +}; diff --git a/es6/index.js b/es6/index.js new file mode 100644 index 0000000..4d251cd --- /dev/null +++ b/es6/index.js @@ -0,0 +1,5 @@ +import * as Client_1 from './client'; +export { Client_1 as Client }; +import * as Server_1 from './server'; +export { Server_1 as Server }; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjBCQUF3QixVQUFVO3FCQUF0QixNQUFNOzBCQUNNLFVBQVU7cUJBQXRCLE1BQU0ifQ== \ No newline at end of file diff --git a/es6/params.d.ts b/es6/params.d.ts new file mode 100644 index 0000000..6e9b541 --- /dev/null +++ b/es6/params.d.ts @@ -0,0 +1,6 @@ +import SRPInteger from './srp-integer'; +export declare const N: SRPInteger; +export declare const g: SRPInteger; +export declare const k: SRPInteger; +export declare const H: (...args: SRPInteger[]) => SRPInteger; +export declare const hashOutputBytes: number; diff --git a/es6/params.js b/es6/params.js new file mode 100644 index 0000000..72e7f3b --- /dev/null +++ b/es6/params.js @@ -0,0 +1,30 @@ +import sha256 from './sha256'; +import SRPInteger from './srp-integer'; +const _2048bitGroup = ` +AC6BDB41 324A9A9B F166DE5E 1389582F AF72B665 1987EE07 FC319294 +3DB56050 A37329CB B4A099ED 8193E075 7767A13D D52312AB 4B03310D +CD7F48A9 DA04FD50 E8083969 EDB767B0 CF609517 9A163AB3 661A05FB +D5FAAAE8 2918A996 2F0B93B8 55F97993 EC975EEA A80D740A DBF4FF74 +7359D041 D5C33EA7 1D281E44 6B14773B CA97B43A 23FB8016 76BD207A +436C6481 F1D2B907 8717461A 5B9D32E6 88F87748 544523B5 24B0D57D +5EA77A27 75D2ECFA 032CFBDB F52FB378 61602790 04E57AE6 AF874E73 +03CE5329 9CCC041C 7BC308D8 2A5698F3 A8D0C382 71AE35F8 E9DBFBB6 +94B5C803 D89F7AE4 35DE236D 525F5475 9B65E372 FCD68EF2 0FA7111F +9E4AFF73 +`.trim(); +const input = { + largeSafePrime: _2048bitGroup, + generatorModulo: '02', + hashFunction: 'sha256', + hashOutputBytes: 256 / 8, +}; +// N A large safe prime (N = 2q+1, where q is prime) +// g A generator modulo N +// k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6) +// H() One-way hash function +export const N = SRPInteger.fromHex(input.largeSafePrime.replace(/\s+/g, '')); +export const g = SRPInteger.fromHex(input.generatorModulo.replace(/\s+/g, '')); +export const k = sha256(N, g); +export const H = sha256; +export const hashOutputBytes = input.hashOutputBytes; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGFyYW1zLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3BhcmFtcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLE1BQU0sTUFBTSxVQUFVLENBQUE7QUFDN0IsT0FBTyxVQUFVLE1BQU0sZUFBZSxDQUFBO0FBRXRDLE1BQU0sYUFBYSxHQUFHOzs7Ozs7Ozs7OztDQVdyQixDQUFDLElBQUksRUFBRSxDQUFBO0FBRVIsTUFBTSxLQUFLLEdBQUc7SUFDYixjQUFjLEVBQUUsYUFBYTtJQUM3QixlQUFlLEVBQUUsSUFBSTtJQUNyQixZQUFZLEVBQUUsUUFBUTtJQUN0QixlQUFlLEVBQUUsR0FBRyxHQUFHLENBQUM7Q0FDeEIsQ0FBQTtBQUVELHVEQUF1RDtBQUN2RCw0QkFBNEI7QUFDNUIsNEVBQTRFO0FBQzVFLDZCQUE2QjtBQUM3QixNQUFNLENBQUMsTUFBTSxDQUFDLEdBQUcsVUFBVSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsY0FBYyxDQUFDLE9BQU8sQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQTtBQUM3RSxNQUFNLENBQUMsTUFBTSxDQUFDLEdBQUcsVUFBVSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsZUFBZSxDQUFDLE9BQU8sQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQTtBQUM5RSxNQUFNLENBQUMsTUFBTSxDQUFDLEdBQUcsTUFBTSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQTtBQUM3QixNQUFNLENBQUMsTUFBTSxDQUFDLEdBQUcsTUFBTSxDQUFBO0FBRXZCLE1BQU0sQ0FBQyxNQUFNLGVBQWUsR0FBRyxLQUFLLENBQUMsZUFBZSxDQUFBIn0= \ No newline at end of file diff --git a/es6/server.d.ts b/es6/server.d.ts new file mode 100644 index 0000000..90cf671 --- /dev/null +++ b/es6/server.d.ts @@ -0,0 +1,6 @@ +import { Session } from '.'; +export declare const generateEphemeral: (verifier: string) => { + secret: string; + public: string; +}; +export declare const deriveSession: (serverSecretEphemeral: string, clientPublicEphemeral: string, salt: string, username: string, verifier: string, clientSessionProof: string) => Session; diff --git a/es6/server.js b/es6/server.js new file mode 100644 index 0000000..d50c21e --- /dev/null +++ b/es6/server.js @@ -0,0 +1,63 @@ +import * as params from './params'; +import SRPInteger from './srp-integer'; +export const generateEphemeral = (verifier) => { + // N A large safe prime (N = 2q+1, where q is prime) + // g A generator modulo N + // k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6) + const { N, g, k } = params; + // v Password verifier + const v = SRPInteger.fromHex(verifier); + // B = kv + g^b (b = random number) + const b = SRPInteger.randomInteger(params.hashOutputBytes); + const B = k.multiply(v).add(g.modPow(b, N)).mod(N); + return { + secret: b.toHex(), + public: B.toHex(), + }; +}; +export const deriveSession = (serverSecretEphemeral, clientPublicEphemeral, salt, username, verifier, clientSessionProof) => { + // N A large safe prime (N = 2q+1, where q is prime) + // g A generator modulo N + // k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6) + // H() One-way hash function + const { N, g, k, H } = params; + // b Secret ephemeral values + // A Public ephemeral values + // s User's salt + // p Cleartext Password + // I Username + // v Password verifier + const b = SRPInteger.fromHex(serverSecretEphemeral); + const A = SRPInteger.fromHex(clientPublicEphemeral); + const s = SRPInteger.fromHex(salt); + const I = String(username); + const v = SRPInteger.fromHex(verifier); + // B = kv + g^b (b = random number) + const B = k.multiply(v).add(g.modPow(b, N)).mod(N); + // A % N > 0 + if (A.mod(N).equals(SRPInteger.ZERO)) { + // fixme: .code, .statusCode, etc. + throw new Error('The client sent an invalid public ephemeral'); + } + // u = H(A, B) + const u = H(A, B); + // S = (Av^u) ^ b (computes session key) + const S = A.multiply(v.modPow(u, N)).modPow(b, N); + // K = H(S) + const K = H(S); + // M = H(H(N) xor H(g), H(I), s, A, B, K) + const M = H(H(N).xor(H(g)), H(SRPInteger.fromHex(I)), s, A, B, K); + const expected = M; + const actual = SRPInteger.fromHex(clientSessionProof); + if (!actual.equals(expected)) { + // fixme: .code, .statusCode, etc. + throw new Error('Client provided session proof is invalid'); + } + // P = H(A, M, K) + const P = H(A, M, K); + return { + key: K.toHex(), + proof: P.toHex(), + }; +}; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3NlcnZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEtBQUssTUFBTSxNQUFNLFVBQVUsQ0FBQTtBQUNsQyxPQUFPLFVBQVUsTUFBTSxlQUFlLENBQUE7QUFFdEMsTUFBTSxDQUFDLE1BQU0saUJBQWlCLEdBQUcsQ0FBQyxRQUFnQixFQUFFLEVBQUU7SUFDckQsdURBQXVEO0lBQ3ZELDRCQUE0QjtJQUM1Qiw0RUFBNEU7SUFDNUUsTUFBTSxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLEdBQUcsTUFBTSxDQUFBO0lBRTFCLHlCQUF5QjtJQUN6QixNQUFNLENBQUMsR0FBRyxVQUFVLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFBO0lBRXRDLCtDQUErQztJQUMvQyxNQUFNLENBQUMsR0FBRyxVQUFVLENBQUMsYUFBYSxDQUFDLE1BQU0sQ0FBQyxlQUFlLENBQUMsQ0FBQTtJQUMxRCxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQTtJQUVsRCxPQUFPO1FBQ04sTUFBTSxFQUFFLENBQUMsQ0FBQyxLQUFLLEVBQUU7UUFDakIsTUFBTSxFQUFFLENBQUMsQ0FBQyxLQUFLLEVBQUU7S0FDakIsQ0FBQTtBQUNGLENBQUMsQ0FBQTtBQUVELE1BQU0sQ0FBQyxNQUFNLGFBQWEsR0FBRyxDQUM1QixxQkFBNkIsRUFDN0IscUJBQTZCLEVBQzdCLElBQVksRUFDWixRQUFnQixFQUNoQixRQUFnQixFQUNoQixrQkFBMEIsRUFDaEIsRUFBRTtJQUNaLHVEQUF1RDtJQUN2RCw0QkFBNEI7SUFDNUIsNEVBQTRFO0lBQzVFLDZCQUE2QjtJQUM3QixNQUFNLEVBQUUs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 \ No newline at end of file diff --git a/es6/sha256.d.ts b/es6/sha256.d.ts new file mode 100644 index 0000000..01f4b89 --- /dev/null +++ b/es6/sha256.d.ts @@ -0,0 +1,3 @@ +import SRPInteger from './srp-integer'; +declare const sha256: (...args: SRPInteger[]) => SRPInteger; +export default sha256; diff --git a/es6/sha256.js b/es6/sha256.js new file mode 100644 index 0000000..9f1f2a2 --- /dev/null +++ b/es6/sha256.js @@ -0,0 +1,30 @@ +import arrayBufferToHex from 'array-buffer-to-hex'; +import encodeUtf8 from 'encode-utf8'; +import hexToArrayBuffer from 'hex-to-array-buffer'; +import rawSha256 from 'crypto-digest-sync/sha256'; +import SRPInteger from './srp-integer'; +function concat(buffers) { + const length = buffers.reduce((mem, item) => mem + item.byteLength, 0); + const combined = new Uint8Array(length); + buffers.reduce((offset, item) => { + combined.set(new Uint8Array(item), offset); + return offset + item.byteLength; + }, 0); + return combined.buffer; +} +const sha256 = (...args) => { + const buffer = concat(args.map((arg) => { + if (arg instanceof SRPInteger) { + return hexToArrayBuffer(arg.toHex()); + } + else if (typeof arg === 'string') { + return encodeUtf8(arg); + } + else { + throw new TypeError('Expected string or SRPInteger'); + } + })); + return SRPInteger.fromHex(arrayBufferToHex(rawSha256(buffer))); +}; +export default sha256; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2hhMjU2LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3NoYTI1Ni50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLGdCQUFnQixNQUFNLHFCQUFxQixDQUFBO0FBQ2xELE9BQU8sVUFBVSxNQUFNLGFBQWEsQ0FBQTtBQUNwQyxPQUFPLGdCQUFnQixNQUFNLHFCQUFxQixDQUFBO0FBQ2xELE9BQU8sU0FBUyxNQUFNLDJCQUEyQixDQUFBO0FBQ2pELE9BQU8sVUFBVSxNQUFNLGVBQWUsQ0FBQTtBQUV0QyxTQUFTLE1BQU0sQ0FBQyxPQUFzQjtJQUNyQyxNQUFNLE1BQU0sR0FBRyxPQUFPLENBQUMsTUFBTSxDQUFDLENBQUMsR0FBRyxFQUFFLElBQUksRUFBRSxFQUFFLENBQUMsR0FBRyxHQUFHLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQyxDQUFDLENBQUE7SUFDdEUsTUFBTSxRQUFRLEdBQUcsSUFBSSxVQUFVLENBQUMsTUFBTSxDQUFDLENBQUE7SUFFdkMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDLE1BQU0sRUFBRSxJQUFJLEVBQUUsRUFBRTtRQUMvQixRQUFRLENBQUMsR0FBRyxDQUFDLElBQUksVUFBVSxDQUFDLElBQUksQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFBO1FBQzFDLE9BQU8sTUFBTSxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUE7SUFDaEMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFBO0lBRUwsT0FBTyxRQUFRLENBQUMsTUFBTSxDQUFBO0FBQ3ZCLENBQUM7QUFFRCxNQUFNLE1BQU0sR0FBRyxDQUFDLEdBQUcsSUFBa0IsRUFBRSxFQUFFO0lBQ3hDLE1BQU0sTUFBTSxHQUFHLE1BQU0sQ0FDcEIsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQWUsRUFBRSxFQUFFO1FBQzVCLElBQUksR0FBRyxZQUFZLFVBQVUsRUFBRTtZQUM5QixPQUFPLGdCQUFnQixDQUFDLEdBQUcsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFBO1NBQ3BDO2FBQU0sSUFBSSxPQUFPLEdBQUcsS0FBSyxRQUFRLEVBQUU7WUFDbkMsT0FBTyxVQUFVLENBQUMsR0FBRyxDQUFDLENBQUE7U0FDdEI7YUFBTTtZQUNOLE1BQU0sSUFBSSxTQUFTLENBQUMsK0JBQStCLENBQUMsQ0FBQTtTQUNwRDtJQUNGLENBQUMsQ0FBQyxDQUNGLENBQUE7SUFFRCxPQUFPLFVBQVUsQ0FBQyxPQUFPLENBQUMsZ0JBQWdCLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQTtBQUMvRCxDQUFDLENBQUE7QUFFRCxlQUFlLE1BQU0sQ0FBQSJ9 \ No newline at end of file diff --git a/es6/srp-integer.d.ts b/es6/srp-integer.d.ts new file mode 100644 index 0000000..e85ad5a --- /dev/null +++ b/es6/srp-integer.d.ts @@ -0,0 +1,21 @@ +import { BigInteger } from 'jsbn'; +declare const kBigInteger: unique symbol; +declare const kHexLength: unique symbol; +declare class SRPInteger { + [kBigInteger]: BigInteger; + [kHexLength]: number | null; + static fromHex: (input: string) => SRPInteger; + static randomInteger: (bytes: number) => SRPInteger; + static ZERO: SRPInteger; + constructor(bigInteger: BigInteger, hexLength: number | null); + add(val: SRPInteger): SRPInteger; + equals(val: SRPInteger): boolean; + multiply(val: SRPInteger): SRPInteger; + modPow(exponent: SRPInteger, m: SRPInteger): SRPInteger; + mod(m: SRPInteger): SRPInteger; + subtract(val: SRPInteger): SRPInteger; + xor(val: SRPInteger): SRPInteger; + inspect(): string; + toHex(): string; +} +export default SRPInteger; diff --git a/es6/srp-integer.js b/es6/srp-integer.js new file mode 100644 index 0000000..75d6a12 --- /dev/null +++ b/es6/srp-integer.js @@ -0,0 +1,52 @@ +import randomHex from 'crypto-random-hex'; +import { BigInteger } from 'jsbn'; +const padStart = (str, maxLength, fillString) => str.padStart(maxLength, fillString); +const kBigInteger = Symbol('big-integer'); +const kHexLength = Symbol('hex-length'); +class SRPInteger { + constructor(bigInteger, hexLength) { + this[kBigInteger] = bigInteger; + this[kHexLength] = hexLength; + } + add(val) { + return new SRPInteger(this[kBigInteger].add(val[kBigInteger]), null); + } + equals(val) { + return this[kBigInteger].equals(val[kBigInteger]); + } + multiply(val) { + return new SRPInteger(this[kBigInteger].multiply(val[kBigInteger]), null); + } + modPow(exponent, m) { + return new SRPInteger(this[kBigInteger].modPow(exponent[kBigInteger], m[kBigInteger]), m[kHexLength]); + } + mod(m) { + return new SRPInteger(this[kBigInteger].mod(m[kBigInteger]), m[kHexLength]); + } + subtract(val) { + return new SRPInteger(this[kBigInteger].subtract(val[kBigInteger]), this[kHexLength]); + } + xor(val) { + return new SRPInteger(this[kBigInteger].xor(val[kBigInteger]), this[kHexLength]); + } + inspect() { + const hex = this[kBigInteger].toString(16); + return ` 16 ? '...' : ''}>`; + } + toHex() { + var _a; + if (this[kHexLength] === null) { + throw new Error('This SRPInteger has no specified length'); + } + return padStart(this[kBigInteger].toString(16), (_a = this[kHexLength]) !== null && _a !== void 0 ? _a : 0, '0'); + } +} +SRPInteger.fromHex = function (input) { + return new SRPInteger(new BigInteger(input, 16), input.length); +}; +SRPInteger.randomInteger = function (bytes) { + return SRPInteger.fromHex(randomHex(bytes)); +}; +SRPInteger.ZERO = new SRPInteger(new BigInteger('0'), null); +export default SRPInteger; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3JwLWludGVnZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvc3JwLWludGVnZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxTQUFTLE1BQU0sbUJBQW1CLENBQUE7QUFDekMsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLE1BQU0sQ0FBQTtBQUVqQyxNQUFNLFFBQVEsR0FBRyxDQUFDLEdBQVcsRUFBRSxTQUFpQixFQUFFLFVBQWtCLEVBQUUsRUFBRSxDQUN2RSxHQUFHLENBQUMsUUFBUSxDQUFDLFNBQVMsRUFBRSxVQUFVLENBQUMsQ0FBQTtBQUVwQyxNQUFNLFdBQVcsR0FBRyxNQUFNLENBQUMsYUFBYSxDQUFDLENBQUE7QUFDekMsTUFBTSxVQUFVLEdBQUcsTUFBTSxDQUFDLFlBQVksQ0FBQyxDQUFBO0FBRXZDLE1BQU0sVUFBVTtJQU9mLFlBQVksVUFBc0IsRUFBRSxTQUF3QjtRQUMzRCxJQUFJLENBQUMsV0FBVyxDQUFDLEdBQUcsVUFBVSxDQUFBO1FBQzlCLElBQUksQ0FBQyxVQUFVLENBQUMsR0FBRyxTQUFTLENBQUE7SUFDN0IsQ0FBQztJQUVELEdBQUcsQ0FBQyxHQUFlO1FBQ2xCLE9BQU8sSUFBSSxVQUFVLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsV0FBVyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQTtJQUNyRSxDQUFDO0lBRUQsTUFBTSxDQUFDLEdBQWU7UUFDckIsT0FBTyxJQUFJLENBQUMsV0FBVyxDQUFDLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxXQUFXLENBQUMsQ0FBQyxDQUFBO0lBQ2xELENBQUM7SUFFRCxRQUFRLENBQUMsR0FBZTtRQUN2QixPQUFPLElBQUksVUFBVSxDQUNwQixJQUFJLENBQUMsV0FBVyxDQUFDLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxXQUFXLENBQUMsQ0FBQyxFQUM1QyxJQUFJLENBQ0osQ0FBQTtJQUNGLENBQUM7SUFFRCxNQUFNLENBQUMsUUFBb0IsRUFBRSxDQUFhO1FBQ3pDLE9BQU8sSUFBSSxVQUFVLENBQ3BCLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLFdBQVcsQ0FBQyxFQUFFLENBQUMsQ0FBQyxXQUFXLENBQUMsQ0FBQyxFQUMvRCxDQUFDLENBQUMsVUFBVSxDQUFDLENBQ2IsQ0FBQTtJQUNGLENBQUM7SUFFRCxHQUFHLENBQUMsQ0FBYTtRQUNoQixPQUFPLElBQUksVUFBVSxDQUNwQixJQUFJLENBQUMsV0FBVyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxXQUFXLENBQUMsQ0FBQyxFQUNyQyxDQUFDLENBQUMsVUFBVSxDQUFDLENBQ2IsQ0FBQTtJQUNGLENBQUM7SUFFRCxRQUFRLENBQUMsR0FBZTtRQUN2QixPQUFPLElBQUksVUFBVSxDQUNwQixJQUFJLENBQUMsV0FBVyxDQUFDLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxXQUFXLENBQUMsQ0FBQyxFQUM1QyxJQUFJLENBQUMsVUFBVSxDQUFDLENBQ2hCLENBQUE7SUFDRixDQUFDO0lBRUQsR0FBRyxDQUFDLEdBQWU7UUFDbEIsT0FBTyxJQUFJLFVBQVUsQ0FDcEIsSUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsV0FBVyxDQUFDLENBQUMsRUFDdkMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUNoQixDQUFBO0lBQ0YsQ0FBQztJQUVELE9BQU87UUFDTixNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQyxDQUFBO1FBRTFDLE9BQU8sZUFBZSxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsR0FBRyxHQUFHLENBQUMsTUFBTSxHQUFHLEVBQUUsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLEdBQUcsQ0FBQTtJQUN6RSxDQUFDO0lBRUQsS0FBSzs7UUFDSixJQUFJLElBQUksQ0FBQyxVQUFVLENBQUMsS0FBSyxJQUFJLEVBQUU7WUFDOUIsTUFBTSxJQUFJLEtBQUssQ0FBQyx5Q0FBeUMsQ0FBQyxDQUFBO1NBQzFEO1FBRUQsT0FBTyxRQUFRLENBQ2QsSUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsRUFDOUIsTUFBQSxJQUFJLENBQUMsVUFBVSxDQUFDLG1DQUFJLENBQUMsRUFDckIsR0FBRyxDQUNILENBQUE7SUFDRixDQUFDO0NBQ0Q7QUFFRCxVQUFVLENBQUMsT0FBTyxHQUFHLFVBQVUsS0FBYTtJQUMzQyxPQUFPLElBQUksVUFBVSxDQUFDLElBQUksVUFBVSxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsRUFBRSxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUE7QUFDL0QsQ0FBQyxDQUFBO0FBRUQsVUFBVSxDQUFDLGFBQWEsR0FBRyxVQUFVLEtBQWE7SUFDakQsT0FBTyxVQUFVLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFBO0FBQzVDLENBQUMsQ0FBQTtBQUVELFVBQVUsQ0FBQyxJQUFJLEdBQUcsSUFBSSxVQUFVLENBQUMsSUFBSSxVQUFVLENBQUMsR0FBRyxDQUFDLEVBQUUsSUFBSSxDQUFDLENBQUE7QUFFM0QsZUFBZSxVQUFVLENBQUEifQ== \ No newline at end of file diff --git a/lib/params.js b/lib/params.js deleted file mode 100644 index f21b262..0000000 --- a/lib/params.js +++ /dev/null @@ -1,33 +0,0 @@ -'use strict' - -const sha256 = require('./sha256') -const SRPInteger = require('./srp-integer') - -const input = { - largeSafePrime: ` - AC6BDB41 324A9A9B F166DE5E 1389582F AF72B665 1987EE07 FC319294 - 3DB56050 A37329CB B4A099ED 8193E075 7767A13D D52312AB 4B03310D - CD7F48A9 DA04FD50 E8083969 EDB767B0 CF609517 9A163AB3 661A05FB - D5FAAAE8 2918A996 2F0B93B8 55F97993 EC975EEA A80D740A DBF4FF74 - 7359D041 D5C33EA7 1D281E44 6B14773B CA97B43A 23FB8016 76BD207A - 436C6481 F1D2B907 8717461A 5B9D32E6 88F87748 544523B5 24B0D57D - 5EA77A27 75D2ECFA 032CFBDB F52FB378 61602790 04E57AE6 AF874E73 - 03CE5329 9CCC041C 7BC308D8 2A5698F3 A8D0C382 71AE35F8 E9DBFBB6 - 94B5C803 D89F7AE4 35DE236D 525F5475 9B65E372 FCD68EF2 0FA7111F - 9E4AFF73 - `, - generatorModulo: '02', - hashFunction: 'sha256', - hashOutputBytes: (256 / 8) -} - -// N A large safe prime (N = 2q+1, where q is prime) -// g A generator modulo N -// k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6) -// H() One-way hash function -exports.N = SRPInteger.fromHex(input.largeSafePrime.replace(/\s+/g, '')) -exports.g = SRPInteger.fromHex(input.generatorModulo.replace(/\s+/g, '')) -exports.k = sha256(exports.N, exports.g) -exports.H = sha256 - -exports.hashOutputBytes = input.hashOutputBytes diff --git a/lib/sha256.js b/lib/sha256.js deleted file mode 100644 index 229e884..0000000 --- a/lib/sha256.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict' - -const crypto = require('crypto') - -const SRPInteger = require('./srp-integer') - -module.exports = function sha256 (...args) { - const h = crypto.createHash('sha256') - - for (const arg of args) { - if (arg instanceof SRPInteger) { - h.update(Buffer.from(arg.toHex(), 'hex')) - } else if (typeof arg === 'string') { - h.update(arg) - } else { - throw new TypeError('Expected string or SRPInteger') - } - } - - return SRPInteger.fromHex(h.digest('hex')) -} diff --git a/lib/srp-integer.js b/lib/srp-integer.js deleted file mode 100644 index 9cc67c9..0000000 --- a/lib/srp-integer.js +++ /dev/null @@ -1,69 +0,0 @@ -'use strict' - -const padStart = require('pad-start') -const randomHex = require('crypto-random-hex') -const { BigInteger } = require('jsbn') - -const kBigInteger = Symbol('big-integer') -const kHexLength = Symbol('hex-length') - -class SRPInteger { - constructor (bigInteger, hexLength) { - this[kBigInteger] = bigInteger - this[kHexLength] = hexLength - } - - add (val) { - return new SRPInteger(this[kBigInteger].add(val[kBigInteger]), null) - } - - equals (val) { - return this[kBigInteger].equals(val[kBigInteger]) - } - - multiply (val) { - return new SRPInteger(this[kBigInteger].multiply(val[kBigInteger]), null) - } - - modPow (exponent, m) { - return new SRPInteger(this[kBigInteger].modPow(exponent[kBigInteger], m[kBigInteger]), m[kHexLength]) - } - - mod (m) { - return new SRPInteger(this[kBigInteger].mod(m[kBigInteger]), m[kHexLength]) - } - - subtract (val) { - return new SRPInteger(this[kBigInteger].subtract(val[kBigInteger]), this[kHexLength]) - } - - xor (val) { - return new SRPInteger(this[kBigInteger].xor(val[kBigInteger]), this[kHexLength]) - } - - inspect () { - const hex = this[kBigInteger].toString(16) - - return ` 16 ? '...' : ''}>` - } - - toHex () { - if (this[kHexLength] === null) { - throw new Error('This SRPInteger has no specified length') - } - - return padStart(this[kBigInteger].toString(16), this[kHexLength], '0') - } -} - -SRPInteger.fromHex = function (input) { - return new SRPInteger(new BigInteger(input, 16), input.length) -} - -SRPInteger.randomInteger = function (bytes) { - return SRPInteger.fromHex(randomHex(bytes)) -} - -SRPInteger.ZERO = new SRPInteger(new BigInteger('0'), null) - -module.exports = SRPInteger diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index e589327..0000000 --- a/package-lock.json +++ /dev/null @@ -1,1755 +0,0 @@ -{ - "name": "secure-remote-password", - "version": "0.3.1", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "acorn": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", - "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", - "dev": true - }, - "acorn-jsx": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz", - "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==", - "dev": true - }, - "ajv": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", - "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.0.tgz", - "integrity": "sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw==", - "dev": true - }, - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "array-buffer-to-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-to-hex/-/array-buffer-to-hex-1.0.0.tgz", - "integrity": "sha512-arycdkxgK1cj6s03GDb96tlCxOl1n3kg9M2OHseUc6Pqyqp+lgfceFPmG507eI5V+oxOSEnlOw/dFc7LXBXF4Q==" - }, - "array-includes": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", - "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.7.0" - } - }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - }, - "dependencies": { - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "brace-expansion": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", - "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "caller-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", - "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", - "dev": true, - "requires": { - "callsites": "^0.2.0" - } - }, - "callsites": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", - "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - } - } - }, - "chardet": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", - "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", - "dev": true - }, - "circular-json": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", - "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", - "dev": true - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "dev": true, - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", - "dev": true - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "commander": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", - "dev": true - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "crypto-digest-sync": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/crypto-digest-sync/-/crypto-digest-sync-1.0.0.tgz", - "integrity": "sha512-UQBOB5z+HF4iA8shKQ3PPwhCmdFAihwcytD1Qh4uiz78x04cZZmKtZ1F1VyAjkrA8uEZqXt2tMXfj3dJHtcbng==" - }, - "crypto-random-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-hex/-/crypto-random-hex-1.0.0.tgz", - "integrity": "sha512-1DuZQ03El13TRgfrqbbjW40Gvi4OKInny/Wxqj23/JMXe214C/3Tlz92bKXWDW3NZT5RjXUGdYW4qiIOUPf+cA==", - "requires": { - "array-buffer-to-hex": "^1.0.0" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - }, - "dependencies": { - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - } - } - }, - "debug-log": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/debug-log/-/debug-log-1.0.1.tgz", - "integrity": "sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8=", - "dev": true - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "deglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/deglob/-/deglob-2.1.1.tgz", - "integrity": "sha512-2kjwuGGonL7gWE1XU4Fv79+vVzpoQCl0V+boMwWtOQJV2AGDabCwez++nB1Nli/8BabAfZQ/UuHPlp6AymKdWw==", - "dev": true, - "requires": { - "find-root": "^1.0.0", - "glob": "^7.0.5", - "ignore": "^3.0.9", - "pkg-config": "^1.1.0", - "run-parallel": "^1.1.2", - "uniq": "^1.0.1" - }, - "dependencies": { - "ignore": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", - "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", - "dev": true - } - } - }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "encode-utf8": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/encode-utf8/-/encode-utf8-1.0.1.tgz", - "integrity": "sha512-F9nrr3tYt0xVSFS8JEwBLcIg8SEX6p5wPPghnMGMgaYkUuyG7S0CiLlViHBK7PJwlWvM3xlT4wFh46mTBpuaPw==" - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", - "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.0", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-keys": "^1.0.12" - } - }, - "es-to-primitive": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", - "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "eslint": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.4.0.tgz", - "integrity": "sha512-UIpL91XGex3qtL6qwyCQJar2j3osKxK9e3ano3OcGEIRM4oWIpCkDg9x95AXEC2wMs7PnxzOkPZ2gq+tsMS9yg==", - "dev": true, - "requires": { - "ajv": "^6.5.0", - "babel-code-frame": "^6.26.0", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", - "debug": "^3.1.0", - "doctrine": "^2.1.0", - "eslint-scope": "^4.0.0", - "eslint-utils": "^1.3.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^4.0.0", - "esquery": "^1.0.1", - "esutils": "^2.0.2", - "file-entry-cache": "^2.0.0", - "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", - "globals": "^11.7.0", - "ignore": "^4.0.2", - "imurmurhash": "^0.1.4", - "inquirer": "^5.2.0", - "is-resolvable": "^1.1.0", - "js-yaml": "^3.11.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.5", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", - "pluralize": "^7.0.0", - "progress": "^2.0.0", - "regexpp": "^2.0.0", - "require-uncached": "^1.0.3", - "semver": "^5.5.0", - "strip-ansi": "^4.0.0", - "strip-json-comments": "^2.0.1", - "table": "^4.0.3", - "text-table": "^0.2.0" - } - }, - "eslint-config-standard": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-12.0.0.tgz", - "integrity": "sha512-COUz8FnXhqFitYj4DTqHzidjIL/t4mumGZto5c7DrBpvWoie+Sn3P4sLEzUGeYhRElWuFEf8K1S1EfvD1vixCQ==", - "dev": true - }, - "eslint-config-standard-jsx": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-6.0.2.tgz", - "integrity": "sha512-D+YWAoXw+2GIdbMBRAzWwr1ZtvnSf4n4yL0gKGg7ShUOGXkSOLerI17K4F6LdQMJPNMoWYqepzQD/fKY+tXNSg==", - "dev": true - }, - "eslint-import-resolver-node": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz", - "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==", - "dev": true, - "requires": { - "debug": "^2.6.9", - "resolve": "^1.5.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "eslint-module-utils": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.3.0.tgz", - "integrity": "sha512-lmDJgeOOjk8hObTysjqH7wyMi+nsHwwvfBykwfhjR1LNdd7C2uFJBvx4OpWYpXOw4df1yE1cDEVd1yLHitk34w==", - "dev": true, - "requires": { - "debug": "^2.6.8", - "pkg-dir": "^2.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "eslint-plugin-es": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-1.4.0.tgz", - "integrity": "sha512-XfFmgFdIUDgvaRAlaXUkxrRg5JSADoRC8IkKLc/cISeR3yHVMefFHQZpcyXXEUUPHfy5DwviBcrfqlyqEwlQVw==", - "dev": true, - "requires": { - "eslint-utils": "^1.3.0", - "regexpp": "^2.0.1" - } - }, - "eslint-plugin-import": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.14.0.tgz", - "integrity": "sha512-FpuRtniD/AY6sXByma2Wr0TXvXJ4nA/2/04VPlfpmUDPOpOY264x+ILiwnrk/k4RINgDAyFZByxqPUbSQ5YE7g==", - "dev": true, - "requires": { - "contains-path": "^0.1.0", - "debug": "^2.6.8", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.1", - "eslint-module-utils": "^2.2.0", - "has": "^1.0.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.3", - "read-pkg-up": "^2.0.0", - "resolve": "^1.6.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" - } - } - } - }, - "eslint-plugin-node": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-7.0.1.tgz", - "integrity": "sha512-lfVw3TEqThwq0j2Ba/Ckn2ABdwmL5dkOgAux1rvOk6CO7A6yGyPI2+zIxN6FyNkp1X1X/BSvKOceD6mBWSj4Yw==", - "dev": true, - "requires": { - "eslint-plugin-es": "^1.3.1", - "eslint-utils": "^1.3.1", - "ignore": "^4.0.2", - "minimatch": "^3.0.4", - "resolve": "^1.8.1", - "semver": "^5.5.0" - } - }, - "eslint-plugin-promise": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.0.1.tgz", - "integrity": "sha512-Si16O0+Hqz1gDHsys6RtFRrW7cCTB6P7p3OJmKp3Y3dxpQE2qwOA7d3xnV+0mBmrPoi0RBnxlCKvqu70te6wjg==", - "dev": true - }, - "eslint-plugin-react": { - "version": "7.11.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.11.1.tgz", - "integrity": "sha512-cVVyMadRyW7qsIUh3FHp3u6QHNhOgVrLQYdQEB1bPWBsgbNCHdFAeNMquBMCcZJu59eNthX053L70l7gRt4SCw==", - "dev": true, - "requires": { - "array-includes": "^3.0.3", - "doctrine": "^2.1.0", - "has": "^1.0.3", - "jsx-ast-utils": "^2.0.1", - "prop-types": "^15.6.2" - } - }, - "eslint-plugin-standard": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.0.0.tgz", - "integrity": "sha512-OwxJkR6TQiYMmt1EsNRMe5qG3GsbjlcOhbGUBY4LtavF9DsLaTcoR+j2Tdjqi23oUwKNUqX7qcn5fPStafMdlA==", - "dev": true - }, - "eslint-scope": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.2.tgz", - "integrity": "sha512-5q1+B/ogmHl8+paxtOKx38Z8LtWkVGuNt3+GQNErqwLl6ViNp/gdJGMCjZNxZ8j/VYjDNZ2Fo+eQc1TAVPIzbg==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", - "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==", - "dev": true - }, - "eslint-visitor-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", - "dev": true - }, - "espree": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-4.1.0.tgz", - "integrity": "sha512-I5BycZW6FCVIub93TeVY1s7vjhP9CY6cXCznIRfiig7nRviKZYdRnj/sHEWC6A7WE9RDWOFq9+7OsWSYz8qv2w==", - "dev": true, - "requires": { - "acorn": "^6.0.2", - "acorn-jsx": "^5.0.0", - "eslint-visitor-keys": "^1.0.0" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", - "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", - "dev": true, - "requires": { - "estraverse": "^4.0.0" - } - }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "dev": true, - "requires": { - "estraverse": "^4.1.0" - } - }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true - }, - "external-editor": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", - "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", - "dev": true, - "requires": { - "chardet": "^0.4.0", - "iconv-lite": "^0.4.17", - "tmp": "^0.0.33" - } - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-entry-cache": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", - "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", - "dev": true, - "requires": { - "flat-cache": "^1.2.1", - "object-assign": "^4.0.1" - } - }, - "find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", - "dev": true - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "flat-cache": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", - "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", - "dev": true, - "requires": { - "circular-json": "^0.3.1", - "graceful-fs": "^4.1.2", - "rimraf": "~2.6.2", - "write": "^0.2.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "get-stdin": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", - "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", - "dev": true - }, - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "globals": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.11.0.tgz", - "integrity": "sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw==", - "dev": true - }, - "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true - }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", - "dev": true - }, - "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", - "dev": true - }, - "hex-to-array-buffer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hex-to-array-buffer/-/hex-to-array-buffer-1.1.0.tgz", - "integrity": "sha512-vvl3IM8FfT1uOnHtEqyjkDK9Luqz6MQrH82qIvVnjyXxRhkeaEZyRRPiBgf2yym3nweRVEfayxt/1SoTXZYd4Q==" - }, - "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", - "dev": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "inquirer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", - "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", - "dev": true, - "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^2.1.0", - "figures": "^2.0.0", - "lodash": "^4.3.0", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^5.5.2", - "string-width": "^2.1.0", - "strip-ansi": "^4.0.0", - "through": "^2.3.6" - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", - "dev": true - }, - "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", - "dev": true - }, - "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "dev": true, - "requires": { - "has": "^1.0.1" - } - }, - "is-resolvable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", - "dev": true - }, - "is-symbol": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", - "dev": true, - "requires": { - "has-symbols": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true - }, - "js-yaml": { - "version": "3.12.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.2.tgz", - "integrity": "sha512-QHn/Lh/7HhZ/Twc7vJYQTkjuCa0kaCcDcjK5Zlk2rvnUpy7DxMJ23+Jc2dcyvltwQVg1nygAVlB2oRDFHoRS5Q==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha1-sBMHyym2GKHtJux56RH4A8TaAEA=" - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "jsx-ast-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz", - "integrity": "sha1-6AGxs5mF4g//yHtA43SAgOLcrH8=", - "dev": true, - "requires": { - "array-includes": "^3.0.3" - } - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", - "dev": true - }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "mocha": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", - "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", - "dev": true, - "requires": { - "browser-stdout": "1.3.1", - "commander": "2.15.1", - "debug": "3.1.0", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "glob": "7.1.2", - "growl": "1.10.5", - "he": "1.1.1", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "supports-color": "5.4.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "object-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz", - "integrity": "sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "pad-start": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pad-start/-/pad-start-1.0.2.tgz", - "integrity": "sha1-I+W6s+lkRrYoFs/28VCXXwQNGxQ=" - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "^1.2.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true - }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "requires": { - "pify": "^2.0.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "pkg-conf": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", - "integrity": "sha1-ISZRTKbyq/69FoWW3xi6V4Z/AFg=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "load-json-file": "^4.0.0" - }, - "dependencies": { - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "pkg-config": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pkg-config/-/pkg-config-1.1.1.tgz", - "integrity": "sha1-VX7yLXPaPIg3EHdmxS6tq94pj+Q=", - "dev": true, - "requires": { - "debug-log": "^1.0.0", - "find-root": "^1.0.0", - "xtend": "^4.0.1" - } - }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "requires": { - "find-up": "^2.1.0" - } - }, - "pluralize": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", - "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", - "dev": true - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", - "dev": true, - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.8.1" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "react-is": { - "version": "16.8.4", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.4.tgz", - "integrity": "sha512-PVadd+WaUDOAciICm/J1waJaSvgq+4rHE/K70j0PFqKhkTBsPv/82UGQJNXAngz1fOQLLxI6z1sEDmJDQhCTAA==", - "dev": true - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - } - }, - "regexpp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", - "dev": true - }, - "require-uncached": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", - "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", - "dev": true, - "requires": { - "caller-path": "^0.1.0", - "resolve-from": "^1.0.0" - } - }, - "resolve": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", - "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - }, - "resolve-from": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", - "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", - "dev": true - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "dev": true, - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "run-async": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", - "dev": true, - "requires": { - "is-promise": "^2.1.0" - } - }, - "run-parallel": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", - "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", - "dev": true - }, - "rxjs": { - "version": "5.5.12", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz", - "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==", - "dev": true, - "requires": { - "symbol-observable": "1.0.1" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "semver": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", - "dev": true - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "slice-ansi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", - "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0" - } - }, - "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz", - "integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==", - "dev": true - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "standard": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/standard/-/standard-12.0.1.tgz", - "integrity": "sha512-UqdHjh87OG2gUrNCSM4QRLF5n9h3TFPwrCNyVlkqu31Hej0L/rc8hzKqVvkb2W3x0WMq7PzZdkLfEcBhVOR6lg==", - "dev": true, - "requires": { - "eslint": "~5.4.0", - "eslint-config-standard": "12.0.0", - "eslint-config-standard-jsx": "6.0.2", - "eslint-plugin-import": "~2.14.0", - "eslint-plugin-node": "~7.0.1", - "eslint-plugin-promise": "~4.0.0", - "eslint-plugin-react": "~7.11.1", - "eslint-plugin-standard": "~4.0.0", - "standard-engine": "~9.0.0" - } - }, - "standard-engine": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-9.0.0.tgz", - "integrity": "sha512-ZfNfCWZ2Xq67VNvKMPiVMKHnMdvxYzvZkf1AH8/cw2NLDBm5LRsxMqvEJpsjLI/dUosZ3Z1d6JlHDp5rAvvk2w==", - "dev": true, - "requires": { - "deglob": "^2.1.0", - "get-stdin": "^6.0.0", - "minimist": "^1.1.0", - "pkg-conf": "^2.0.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - } - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "symbol-observable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", - "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", - "dev": true - }, - "table": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/table/-/table-4.0.3.tgz", - "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==", - "dev": true, - "requires": { - "ajv": "^6.0.1", - "ajv-keywords": "^3.0.0", - "chalk": "^2.1.0", - "lodash": "^4.17.4", - "slice-ansi": "1.0.0", - "string-width": "^2.1.1" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "uniq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", - "dev": true - }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "write": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", - "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - } - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true - } - } -} diff --git a/package.json b/package.json index c013b8c..2bdfefe 100644 --- a/package.json +++ b/package.json @@ -1,25 +1,32 @@ { - "name": "secure-remote-password", - "version": "0.3.1", - "license": "MIT", - "repository": "LinusU/secure-remote-password", - "scripts": { - "test": "standard && mocha" - }, - "browser": { - "./lib/sha256.js": "./browser/sha256.js" - }, - "dependencies": { - "array-buffer-to-hex": "^1.0.0", - "crypto-digest-sync": "^1.0.0", - "crypto-random-hex": "^1.0.0", - "encode-utf8": "^1.0.1", - "hex-to-array-buffer": "^1.1.0", - "jsbn": "^1.1.0", - "pad-start": "^1.0.2" - }, - "devDependencies": { - "mocha": "^5.2.0", - "standard": "^12.0.1" - } + "name": "secure-remote-password", + "version": "1.0.0", + "license": "MIT", + "main": "es5/index.js", + "module": "es6/index.js", + "source": "src/index.ts", + "scripts": { + "es5": "tsc -p tsconfig.json --target es6 --outDir es6 --module es6 --watch", + "es6": "tsc -p tsconfig.json --target es5 --outDir es5 --module commonjs --watch", + "tsc": "concurrently \"npm run es5\" \"npm run es6\"", + "watch": "concurrently \"npm run es5\" \"npm run es6\"" + }, + "dependencies": { + "@types/node": "^15.6.0", + "array-buffer-to-hex": "^1.0.0", + "crypto-digest-sync": "^1.0.0", + "crypto-random-hex": "^1.0.0", + "encode-utf8": "^1.0.3", + "fast-sha256": "^1.3.0", + "hex-to-array-buffer": "^1.1.0", + "jsbn": "^1.1.0", + "pad-start": "^1.0.2" + }, + "devDependencies": { + "@types/jsbn": "^1.2.29", + "concurrently": "^6.1.0", + "mocha": "^8.4.0", + "standard": "^16.0.3", + "typescript": "^4.2.4" + } } diff --git a/server.d.ts b/server.d.ts deleted file mode 100644 index 78ae3a5..0000000 --- a/server.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -export interface Ephemeral { - public: string - secret: string -} - -export interface Session { - key: string - proof: string -} - -export function generateEphemeral(verifier: string): Ephemeral -export function deriveSession(serverSecretEphemeral: string, clientPublicEphemeral: string, salt: string, username: string, verifier: string, clientSessionProof: string): Session diff --git a/server.js b/server.js deleted file mode 100644 index 9674064..0000000 --- a/server.js +++ /dev/null @@ -1,80 +0,0 @@ -'use strict' - -const params = require('./lib/params') -const SRPInteger = require('./lib/srp-integer') - -exports.generateEphemeral = function (verifier) { - // N A large safe prime (N = 2q+1, where q is prime) - // g A generator modulo N - // k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6) - const { N, g, k } = params - - // v Password verifier - const v = SRPInteger.fromHex(verifier) - - // B = kv + g^b (b = random number) - const b = SRPInteger.randomInteger(params.hashOutputBytes) - const B = k.multiply(v).add(g.modPow(b, N)).mod(N) - - return { - secret: b.toHex(), - public: B.toHex() - } -} - -exports.deriveSession = function (serverSecretEphemeral, clientPublicEphemeral, salt, username, verifier, clientSessionProof) { - // N A large safe prime (N = 2q+1, where q is prime) - // g A generator modulo N - // k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6) - // H() One-way hash function - const { N, g, k, H } = params - - // b Secret ephemeral values - // A Public ephemeral values - // s User's salt - // p Cleartext Password - // I Username - // v Password verifier - const b = SRPInteger.fromHex(serverSecretEphemeral) - const A = SRPInteger.fromHex(clientPublicEphemeral) - const s = SRPInteger.fromHex(salt) - const I = String(username) - const v = SRPInteger.fromHex(verifier) - - // B = kv + g^b (b = random number) - const B = k.multiply(v).add(g.modPow(b, N)).mod(N) - - // A % N > 0 - if (A.mod(N).equals(SRPInteger.ZERO)) { - // fixme: .code, .statusCode, etc. - throw new Error('The client sent an invalid public ephemeral') - } - - // u = H(A, B) - const u = H(A, B) - - // S = (Av^u) ^ b (computes session key) - const S = A.multiply(v.modPow(u, N)).modPow(b, N) - - // K = H(S) - const K = H(S) - - // M = H(H(N) xor H(g), H(I), s, A, B, K) - const M = H(H(N).xor(H(g)), H(I), s, A, B, K) - - const expected = M - const actual = SRPInteger.fromHex(clientSessionProof) - - if (!actual.equals(expected)) { - // fixme: .code, .statusCode, etc. - throw new Error('Client provided session proof is invalid') - } - - // P = H(A, M, K) - const P = H(A, M, K) - - return { - key: K.toHex(), - proof: P.toHex() - } -} diff --git a/src/client.ts b/src/client.ts new file mode 100644 index 0000000..b52c794 --- /dev/null +++ b/src/client.ts @@ -0,0 +1,142 @@ +import { Session } from '.' +import * as params from './params' +import SRPInteger from './srp-integer' + +export const generateSalt = () => { + // s User's salt + const s = SRPInteger.randomInteger(params.hashOutputBytes) + + return s.toHex() +} + +export const derivePrivateKey = ( + salt: string, + username: string, + password: string +) => { + // H() One-way hash function + const { H } = params + + // s User's salt + // I Username + // p Cleartext Password + const s = SRPInteger.fromHex(salt) + const I = String(username) + const p = String(password) + + // x = H(s, H(I | p)) (s is chosen randomly) + /** Editor's note + * Error happening here on SRPInteger.fromHex: 'Expected string to be an even number of characters' when calling hexToArrayBuffer + */ + const x = H(s, H(SRPInteger.fromHex(`${I}:${p}`))) + + return x.toHex() +} + +export const deriveVerifier = (privateKey: string) => { + // N A large safe prime (N = 2q+1, where q is prime) + // g A generator modulo N + const { N, g } = params + + // x Private key (derived from p and s) + const x = SRPInteger.fromHex(privateKey) + + // v = g^x (computes password verifier) + const v = g.modPow(x, N) + + return v.toHex() +} + +export const generateEphemeral = () => { + // N A large safe prime (N = 2q+1, where q is prime) + // g A generator modulo N + const { N, g } = params + + // A = g^a (a = random number) + const a = SRPInteger.randomInteger(params.hashOutputBytes) + const A = g.modPow(a, N) + + return { + secret: a.toHex(), + public: A.toHex(), + } +} + +export const deriveSession = ( + clientSecretEphemeral: string, + serverPublicEphemeral: string, + salt: string, + username: string, + privateKey: string +): Session => { + // N A large safe prime (N = 2q+1, where q is prime) + // g A generator modulo N + // k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6) + // H() One-way hash function + const { N, g, k, H } = params + + // a Secret ephemeral values + // B Public ephemeral values + // s User's salt + // I Username + // x Private key (derived from p and s) + const a = SRPInteger.fromHex(clientSecretEphemeral) + const B = SRPInteger.fromHex(serverPublicEphemeral) + const s = SRPInteger.fromHex(salt) + const I = String(username) + const x = SRPInteger.fromHex(privateKey) + + // A = g^a (a = random number) + const A = g.modPow(a, N) + + // B % N > 0 + if (B.mod(N).equals(SRPInteger.ZERO)) { + // fixme: .code, .statusCode, etc. + throw new Error('The server sent an invalid public ephemeral') + } + + // u = H(A, B) + const u = H(A, B) + + // S = (B - kg^x) ^ (a + ux) + const S = B.subtract(k.multiply(g.modPow(x, N))).modPow( + a.add(u.multiply(x)), + N + ) + + // K = H(S) + const K = H(S) + + // M = H(H(N) xor H(g), H(I), s, A, B, K) + const M = H(H(N).xor(H(g)), H(SRPInteger.fromHex(I)), s, A, B, K) + + return { + key: K.toHex(), + proof: M.toHex(), + } +} + +export const verifySession = ( + clientPublicEphemeral: string, + clientSession: Session, + serverSessionProof: string +) => { + // H() One-way hash function + const { H } = params + + // A Public ephemeral values + // M Proof of K + // K Shared, strong session key + const A = SRPInteger.fromHex(clientPublicEphemeral) + const M = SRPInteger.fromHex(clientSession.proof) + const K = SRPInteger.fromHex(clientSession.key) + + // H(A, M, K) + const expected = H(A, M, K) + const actual = SRPInteger.fromHex(serverSessionProof) + + if (!actual.equals(expected)) { + // fixme: .code, .statusCode, etc. + throw new Error('Server provided session proof is invalid') + } +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..026fde7 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,7 @@ +export * as Client from './client' +export * as Server from './server' + +export type Session = { + key: string + proof: string +} diff --git a/src/params.ts b/src/params.ts new file mode 100644 index 0000000..c45688a --- /dev/null +++ b/src/params.ts @@ -0,0 +1,33 @@ +import sha256 from './sha256' +import SRPInteger from './srp-integer' + +const _2048bitGroup = ` +AC6BDB41 324A9A9B F166DE5E 1389582F AF72B665 1987EE07 FC319294 +3DB56050 A37329CB B4A099ED 8193E075 7767A13D D52312AB 4B03310D +CD7F48A9 DA04FD50 E8083969 EDB767B0 CF609517 9A163AB3 661A05FB +D5FAAAE8 2918A996 2F0B93B8 55F97993 EC975EEA A80D740A DBF4FF74 +7359D041 D5C33EA7 1D281E44 6B14773B CA97B43A 23FB8016 76BD207A +436C6481 F1D2B907 8717461A 5B9D32E6 88F87748 544523B5 24B0D57D +5EA77A27 75D2ECFA 032CFBDB F52FB378 61602790 04E57AE6 AF874E73 +03CE5329 9CCC041C 7BC308D8 2A5698F3 A8D0C382 71AE35F8 E9DBFBB6 +94B5C803 D89F7AE4 35DE236D 525F5475 9B65E372 FCD68EF2 0FA7111F +9E4AFF73 +`.trim() + +const input = { + largeSafePrime: _2048bitGroup, + generatorModulo: '02', + hashFunction: 'sha256', + hashOutputBytes: 256 / 8, +} + +// N A large safe prime (N = 2q+1, where q is prime) +// g A generator modulo N +// k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6) +// H() One-way hash function +export const N = SRPInteger.fromHex(input.largeSafePrime.replace(/\s+/g, '')) +export const g = SRPInteger.fromHex(input.generatorModulo.replace(/\s+/g, '')) +export const k = sha256(N, g) +export const H = sha256 + +export const hashOutputBytes = input.hashOutputBytes diff --git a/src/server.ts b/src/server.ts new file mode 100644 index 0000000..ee124fa --- /dev/null +++ b/src/server.ts @@ -0,0 +1,86 @@ +import { Session } from '.' +import * as params from './params' +import SRPInteger from './srp-integer' + +export const generateEphemeral = (verifier: string) => { + // N A large safe prime (N = 2q+1, where q is prime) + // g A generator modulo N + // k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6) + const { N, g, k } = params + + // v Password verifier + const v = SRPInteger.fromHex(verifier) + + // B = kv + g^b (b = random number) + const b = SRPInteger.randomInteger(params.hashOutputBytes) + const B = k.multiply(v).add(g.modPow(b, N)).mod(N) + + return { + secret: b.toHex(), + public: B.toHex(), + } +} + +export const deriveSession = ( + serverSecretEphemeral: string, + clientPublicEphemeral: string, + salt: string, + username: string, + verifier: string, + clientSessionProof: string +): Session => { + // N A large safe prime (N = 2q+1, where q is prime) + // g A generator modulo N + // k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6) + // H() One-way hash function + const { N, g, k, H } = params + + // b Secret ephemeral values + // A Public ephemeral values + // s User's salt + // p Cleartext Password + // I Username + // v Password verifier + const b = SRPInteger.fromHex(serverSecretEphemeral) + const A = SRPInteger.fromHex(clientPublicEphemeral) + const s = SRPInteger.fromHex(salt) + const I = String(username) + const v = SRPInteger.fromHex(verifier) + + // B = kv + g^b (b = random number) + const B = k.multiply(v).add(g.modPow(b, N)).mod(N) + + // A % N > 0 + if (A.mod(N).equals(SRPInteger.ZERO)) { + // fixme: .code, .statusCode, etc. + throw new Error('The client sent an invalid public ephemeral') + } + + // u = H(A, B) + const u = H(A, B) + + // S = (Av^u) ^ b (computes session key) + const S = A.multiply(v.modPow(u, N)).modPow(b, N) + + // K = H(S) + const K = H(S) + + // M = H(H(N) xor H(g), H(I), s, A, B, K) + const M = H(H(N).xor(H(g)), H(SRPInteger.fromHex(I)), s, A, B, K) + + const expected = M + const actual = SRPInteger.fromHex(clientSessionProof) + + if (!actual.equals(expected)) { + // fixme: .code, .statusCode, etc. + throw new Error('Client provided session proof is invalid') + } + + // P = H(A, M, K) + const P = H(A, M, K) + + return { + key: K.toHex(), + proof: P.toHex(), + } +} diff --git a/src/sha256.ts b/src/sha256.ts new file mode 100644 index 0000000..ebb8826 --- /dev/null +++ b/src/sha256.ts @@ -0,0 +1,35 @@ +import arrayBufferToHex from 'array-buffer-to-hex' +import encodeUtf8 from 'encode-utf8' +import hexToArrayBuffer from 'hex-to-array-buffer' +import rawSha256 from 'crypto-digest-sync/sha256' +import SRPInteger from './srp-integer' + +function concat(buffers: ArrayBuffer[]) { + const length = buffers.reduce((mem, item) => mem + item.byteLength, 0) + const combined = new Uint8Array(length) + + buffers.reduce((offset, item) => { + combined.set(new Uint8Array(item), offset) + return offset + item.byteLength + }, 0) + + return combined.buffer +} + +const sha256 = (...args: SRPInteger[]) => { + const buffer = concat( + args.map((arg: SRPInteger) => { + if (arg instanceof SRPInteger) { + return hexToArrayBuffer(arg.toHex()) + } else if (typeof arg === 'string') { + return encodeUtf8(arg) + } else { + throw new TypeError('Expected string or SRPInteger') + } + }) + ) + + return SRPInteger.fromHex(arrayBufferToHex(rawSha256(buffer))) +} + +export default sha256 diff --git a/src/srp-integer.ts b/src/srp-integer.ts new file mode 100644 index 0000000..cca6dbc --- /dev/null +++ b/src/srp-integer.ts @@ -0,0 +1,94 @@ +import randomHex from 'crypto-random-hex' +import { BigInteger } from 'jsbn' + +const padStart = (str: string, maxLength: number, fillString: string) => + str.padStart(maxLength, fillString) + +const kBigInteger = Symbol('big-integer') +const kHexLength = Symbol('hex-length') + +class SRPInteger { + [kBigInteger]: BigInteger; + [kHexLength]: number | null + static fromHex: (input: string) => SRPInteger + static randomInteger: (bytes: number) => SRPInteger + static ZERO: SRPInteger + + constructor(bigInteger: BigInteger, hexLength: number | null) { + this[kBigInteger] = bigInteger + this[kHexLength] = hexLength + } + + add(val: SRPInteger) { + return new SRPInteger(this[kBigInteger].add(val[kBigInteger]), null) + } + + equals(val: SRPInteger) { + return this[kBigInteger].equals(val[kBigInteger]) + } + + multiply(val: SRPInteger) { + return new SRPInteger( + this[kBigInteger].multiply(val[kBigInteger]), + null + ) + } + + modPow(exponent: SRPInteger, m: SRPInteger) { + return new SRPInteger( + this[kBigInteger].modPow(exponent[kBigInteger], m[kBigInteger]), + m[kHexLength] + ) + } + + mod(m: SRPInteger) { + return new SRPInteger( + this[kBigInteger].mod(m[kBigInteger]), + m[kHexLength] + ) + } + + subtract(val: SRPInteger) { + return new SRPInteger( + this[kBigInteger].subtract(val[kBigInteger]), + this[kHexLength] + ) + } + + xor(val: SRPInteger) { + return new SRPInteger( + this[kBigInteger].xor(val[kBigInteger]), + this[kHexLength] + ) + } + + inspect() { + const hex = this[kBigInteger].toString(16) + + return ` 16 ? '...' : ''}>` + } + + toHex() { + if (this[kHexLength] === null) { + throw new Error('This SRPInteger has no specified length') + } + + return padStart( + this[kBigInteger].toString(16), + this[kHexLength] ?? 0, + '0' + ) + } +} + +SRPInteger.fromHex = function (input: string) { + return new SRPInteger(new BigInteger(input, 16), input.length) +} + +SRPInteger.randomInteger = function (bytes: number) { + return SRPInteger.fromHex(randomHex(bytes)) +} + +SRPInteger.ZERO = new SRPInteger(new BigInteger('0'), null) + +export default SRPInteger diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..d2cc837 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,65 @@ +// Original https://github.com/bitjson/typescript-starter/blob/master/tsconfig.json +{ + "compilerOptions": { + "rootDir": "src", + "target": "ESNext", + "outDir": "lib", + "module": "commonjs", + "moduleResolution": "node", + /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + "incremental": false, + "declaration": true, + "inlineSourceMap": true, + /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + "esModuleInterop": true, + /* Include modules imported with .json extension. */ + "resolveJsonModule": true, + + /* Enable all strict type-checking options. */ + "strict": true, + + /* Strict Type-Checking Options */ + // "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, + // "strictNullChecks": true /* Enable strict null checks. */, + // "strictFunctionTypes": true /* Enable strict checking of function types. */, + // "strictPropertyInitialization": true /* Enable strict checking of property initialization in classes. */, + // "noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */, + // "alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */, + + /* Additional Checks */ + /* Report errors on unused locals. */ + "noUnusedLocals": true, + /* Report errors on unused parameters. */ + "noUnusedParameters": true, + /* Report error when not all code paths in function return a value. */ + "noImplicitReturns": true, + /* Report errors for fallthrough cases in switch statement. */ + "noFallthroughCasesInSwitch": true, + /* Add undefined to a type when accessed using an index. */ + "noUncheckedIndexedAccess": true, + /* Ensure that casing is correct in imports. */ + "forceConsistentCasingInFileNames": true, + + /* Debugging Options */ + /* Report module resolution log messages. */ + "traceResolution": false, + /* Print names of generated files part of the compilation. */ + "listEmittedFiles": false, + /* Print names of files part of the compilation. */ + "listFiles": false, + /* Stylize errors and messages using color and context. */ + "pretty": true, + /* Experimental Options */ + // "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */, + // "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */, + + "lib": ["ESNext"], + // "types": ["node"], + "typeRoots": ["node_modules/@types", "src/types"], + + "skipLibCheck": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules"], + "compileOnSave": false +}