From f03a7d74e9c14a6233e45c7e35567ff95dd6aafb Mon Sep 17 00:00:00 2001 From: Guillaume Vincent Date: Fri, 18 Nov 2016 16:29:52 +0100 Subject: [PATCH] refactor code --- index.js | 130 +--- lib/lesspass.js | 1634 ++++++++++++++++++++++++++++++++++++++++++++----- src/v1.js | 121 ++++ src/v2.js | 12 +- tests/v2/api.tests.js | 3 + 5 files changed, 1615 insertions(+), 285 deletions(-) create mode 100644 src/v1.js diff --git a/index.js b/index.js index 9d05e75..78e7455 100644 --- a/index.js +++ b/index.js @@ -1,124 +1,20 @@ -var pbkdf2 = require('pbkdf2'); -var createHmac = require('create-hmac'); -var Promise = require("bluebird"); +var v1 = require('./src/v1'); var v2 = require('./src/v2'); module.exports = { - encryptLogin: _encryptLogin, - renderPassword: _renderPassword, - createFingerprint: createFingerprint, - _deriveEncryptedLogin: _deriveEncryptedLogin, - _getPasswordTemplate: _getPasswordTemplate, - _prettyPrint: _prettyPrint, - _string2charCodes: _string2charCodes, - _getCharType: _getCharType, - _getPasswordChar: _getPasswordChar, - _createHmac: _createHmac, + encryptLogin: v1.encryptLogin, + renderPassword: v1.renderPassword, + createFingerprint: v1.createFingerprint, + _deriveEncryptedLogin: v1._deriveEncryptedLogin, + _getPasswordTemplate: v1._getPasswordTemplate, + _prettyPrint: v1._prettyPrint, + _string2charCodes: v1._string2charCodes, + _getCharType: v1._getCharType, + _getPasswordChar: v1._getPasswordChar, + _createHmac: v1._createHmac, + generatePassword: v2.generatePassword, _calcEntropy: v2._calcEntropy, _getSetOfCharacters: v2._getSetOfCharacters, _renderPassword: v2._renderPassword, -}; - -function _encryptLogin(login, masterPassword, options) { - var _options = options !== undefined ? options : {}; - var iterations = _options.iterations || 8192; - var keylen = _options.keylen || 32; - - return new Promise(function (resolve, reject) { - if (!login || !masterPassword) { - reject('login and master password parameters could not be empty'); - } - pbkdf2.pbkdf2(masterPassword, login, iterations, keylen, 'sha256', function (error, key) { - if (error) { - reject('error in pbkdf2'); - } else { - resolve(key.toString('hex')); - } - }); - }) -} - -function _renderPassword(encryptedLogin, site, passwordOptions) { - return _deriveEncryptedLogin(encryptedLogin, site, passwordOptions).then(function (derivedEncryptedLogin) { - var template = passwordOptions.template || _getPasswordTemplate(passwordOptions); - return _prettyPrint(derivedEncryptedLogin, template); - }); -} - -function _createHmac(encryptedLogin, salt) { - return new Promise(function (resolve) { - resolve(createHmac('sha256', new Buffer(encryptedLogin)).update(salt).digest('hex')); - }); -} - -function _deriveEncryptedLogin(encryptedLogin, site, options) { - var _options = options !== undefined ? options : {}; - var length = _options.length || 12; - var counter = _options.counter || 1; - - var salt = site + counter.toString(); - return _createHmac(encryptedLogin, salt).then(function (derivedHash) { - return derivedHash.substring(0, length); - }); -} - -function _getPasswordTemplate(passwordTypes) { - var templates = { - lowercase: 'vc', - uppercase: 'VC', - numbers: 'n', - symbols: 's', - }; - var returnedTemplate = ''; - Object.keys(templates).forEach(function (template) { - if (passwordTypes.hasOwnProperty(template) && passwordTypes[template]) { - returnedTemplate += templates[template] - } - }); - return returnedTemplate; -} - -function _prettyPrint(hash, template) { - var password = ''; - - _string2charCodes(hash).forEach(function (charCode, index) { - var charType = _getCharType(template, index); - password += _getPasswordChar(charType, charCode); - }); - return password; -} - -function _string2charCodes(text) { - var charCodes = []; - for (var i = 0; i < text.length; i++) { - charCodes.push(text.charCodeAt(i)); - } - return charCodes; -} - -function _getCharType(template, index) { - return template[index % template.length]; -} - -function _getPasswordChar(charType, index) { - var passwordsChars = { - V: 'AEIOUY', - C: 'BCDFGHJKLMNPQRSTVWXZ', - v: 'aeiouy', - c: 'bcdfghjklmnpqrstvwxz', - A: 'AEIOUYBCDFGHJKLMNPQRSTVWXZ', - a: 'AEIOUYaeiouyBCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz', - n: '0123456789', - s: '@&%?,=[]_:-+*$#!\'^~;()/.', - x: 'AEIOUYaeiouyBCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz0123456789@&%?,=[]_:-+*$#!\'^~;()/.' - }; - var passwordChar = passwordsChars[charType]; - return passwordChar[index % passwordChar.length]; -} - -function createFingerprint(str) { - return new Promise(function (resolve) { - resolve(createHmac('sha256', new Buffer(str)).digest('hex')) - }); -} \ No newline at end of file +}; \ No newline at end of file diff --git a/lib/lesspass.js b/lib/lesspass.js index 714c2a3..38e2162 100644 --- a/lib/lesspass.js +++ b/lib/lesspass.js @@ -1,126 +1,25 @@ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.LessPass = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0) return Math.floor(n); + return Math.ceil(n); + } + + function add(a, b) { // assumes a and b are arrays with a.length >= b.length + var l_a = a.length, + l_b = b.length, + r = new Array(l_a), + carry = 0, + base = BASE, + sum, i; + for (i = 0; i < l_b; i++) { + sum = a[i] + b[i] + carry; + carry = sum >= base ? 1 : 0; + r[i] = sum - carry * base; + } + while (i < l_a) { + sum = a[i] + carry; + carry = sum === base ? 1 : 0; + r[i++] = sum - carry * base; + } + if (carry > 0) r.push(carry); + return r; + } + + function addAny(a, b) { + if (a.length >= b.length) return add(a, b); + return add(b, a); + } + + function addSmall(a, carry) { // assumes a is array, carry is number with 0 <= carry < MAX_INT + var l = a.length, + r = new Array(l), + base = BASE, + sum, i; + for (i = 0; i < l; i++) { + sum = a[i] - base + carry; + carry = Math.floor(sum / base); + r[i] = sum - carry * base; + carry += 1; + } + while (carry > 0) { + r[i++] = carry % base; + carry = Math.floor(carry / base); + } + return r; + } + + BigInteger.prototype.add = function (v) { + var value, n = parseValue(v); + if (this.sign !== n.sign) { + return this.subtract(n.negate()); + } + var a = this.value, b = n.value; + if (n.isSmall) { + return new BigInteger(addSmall(a, Math.abs(b)), this.sign); + } + return new BigInteger(addAny(a, b), this.sign); + }; + BigInteger.prototype.plus = BigInteger.prototype.add; + + SmallInteger.prototype.add = function (v) { + var n = parseValue(v); + var a = this.value; + if (a < 0 !== n.sign) { + return this.subtract(n.negate()); + } + var b = n.value; + if (n.isSmall) { + if (isPrecise(a + b)) return new SmallInteger(a + b); + b = smallToArray(Math.abs(b)); + } + return new BigInteger(addSmall(b, Math.abs(a)), a < 0); + }; + SmallInteger.prototype.plus = SmallInteger.prototype.add; + + function subtract(a, b) { // assumes a and b are arrays with a >= b + var a_l = a.length, + b_l = b.length, + r = new Array(a_l), + borrow = 0, + base = BASE, + i, difference; + for (i = 0; i < b_l; i++) { + difference = a[i] - borrow - b[i]; + if (difference < 0) { + difference += base; + borrow = 1; + } else borrow = 0; + r[i] = difference; + } + for (i = b_l; i < a_l; i++) { + difference = a[i] - borrow; + if (difference < 0) difference += base; + else { + r[i++] = difference; + break; + } + r[i] = difference; + } + for (; i < a_l; i++) { + r[i] = a[i]; + } + trim(r); + return r; + } + + function subtractAny(a, b, sign) { + var value, isSmall; + if (compareAbs(a, b) >= 0) { + value = subtract(a,b); + } else { + value = subtract(b, a); + sign = !sign; + } + value = arrayToSmall(value); + if (typeof value === "number") { + if (sign) value = -value; + return new SmallInteger(value); + } + return new BigInteger(value, sign); + } + + function subtractSmall(a, b, sign) { // assumes a is array, b is number with 0 <= b < MAX_INT + var l = a.length, + r = new Array(l), + carry = -b, + base = BASE, + i, difference; + for (i = 0; i < l; i++) { + difference = a[i] + carry; + carry = Math.floor(difference / base); + difference %= base; + r[i] = difference < 0 ? difference + base : difference; + } + r = arrayToSmall(r); + if (typeof r === "number") { + if (sign) r = -r; + return new SmallInteger(r); + } return new BigInteger(r, sign); + } + + BigInteger.prototype.subtract = function (v) { + var n = parseValue(v); + if (this.sign !== n.sign) { + return this.add(n.negate()); + } + var a = this.value, b = n.value; + if (n.isSmall) + return subtractSmall(a, Math.abs(b), this.sign); + return subtractAny(a, b, this.sign); + }; + BigInteger.prototype.minus = BigInteger.prototype.subtract; + + SmallInteger.prototype.subtract = function (v) { + var n = parseValue(v); + var a = this.value; + if (a < 0 !== n.sign) { + return this.add(n.negate()); + } + var b = n.value; + if (n.isSmall) { + return new SmallInteger(a - b); + } + return subtractSmall(b, Math.abs(a), a >= 0); + }; + SmallInteger.prototype.minus = SmallInteger.prototype.subtract; + + BigInteger.prototype.negate = function () { + return new BigInteger(this.value, !this.sign); + }; + SmallInteger.prototype.negate = function () { + var sign = this.sign; + var small = new SmallInteger(-this.value); + small.sign = !sign; + return small; + }; + + BigInteger.prototype.abs = function () { + return new BigInteger(this.value, false); + }; + SmallInteger.prototype.abs = function () { + return new SmallInteger(Math.abs(this.value)); + }; + + function multiplyLong(a, b) { + var a_l = a.length, + b_l = b.length, + l = a_l + b_l, + r = createArray(l), + base = BASE, + product, carry, i, a_i, b_j; + for (i = 0; i < a_l; ++i) { + a_i = a[i]; + for (var j = 0; j < b_l; ++j) { + b_j = b[j]; + product = a_i * b_j + r[i + j]; + carry = Math.floor(product / base); + r[i + j] = product - carry * base; + r[i + j + 1] += carry; + } + } + trim(r); + return r; + } + + function multiplySmall(a, b) { // assumes a is array, b is number with |b| < BASE + var l = a.length, + r = new Array(l), + base = BASE, + carry = 0, + product, i; + for (i = 0; i < l; i++) { + product = a[i] * b + carry; + carry = Math.floor(product / base); + r[i] = product - carry * base; + } + while (carry > 0) { + r[i++] = carry % base; + carry = Math.floor(carry / base); + } + return r; + } + + function shiftLeft(x, n) { + var r = []; + while (n-- > 0) r.push(0); + return r.concat(x); + } + + function multiplyKaratsuba(x, y) { + var n = Math.max(x.length, y.length); + + if (n <= 30) return multiplyLong(x, y); + n = Math.ceil(n / 2); + + var b = x.slice(n), + a = x.slice(0, n), + d = y.slice(n), + c = y.slice(0, n); + + var ac = multiplyKaratsuba(a, c), + bd = multiplyKaratsuba(b, d), + abcd = multiplyKaratsuba(addAny(a, b), addAny(c, d)); + + var product = addAny(addAny(ac, shiftLeft(subtract(subtract(abcd, ac), bd), n)), shiftLeft(bd, 2 * n)); + trim(product); + return product; + } + + // The following function is derived from a surface fit of a graph plotting the performance difference + // between long multiplication and karatsuba multiplication versus the lengths of the two arrays. + function useKaratsuba(l1, l2) { + return -0.012 * l1 - 0.012 * l2 + 0.000015 * l1 * l2 > 0; + } + + BigInteger.prototype.multiply = function (v) { + var value, n = parseValue(v), + a = this.value, b = n.value, + sign = this.sign !== n.sign, + abs; + if (n.isSmall) { + if (b === 0) return Integer[0]; + if (b === 1) return this; + if (b === -1) return this.negate(); + abs = Math.abs(b); + if (abs < BASE) { + return new BigInteger(multiplySmall(a, abs), sign); + } + b = smallToArray(abs); + } + if (useKaratsuba(a.length, b.length)) // Karatsuba is only faster for certain array sizes + return new BigInteger(multiplyKaratsuba(a, b), sign); + return new BigInteger(multiplyLong(a, b), sign); + }; + + BigInteger.prototype.times = BigInteger.prototype.multiply; + + function multiplySmallAndArray(a, b, sign) { // a >= 0 + if (a < BASE) { + return new BigInteger(multiplySmall(b, a), sign); + } + return new BigInteger(multiplyLong(b, smallToArray(a)), sign); + } + SmallInteger.prototype._multiplyBySmall = function (a) { + if (isPrecise(a.value * this.value)) { + return new SmallInteger(a.value * this.value); + } + return multiplySmallAndArray(Math.abs(a.value), smallToArray(Math.abs(this.value)), this.sign !== a.sign); + }; + BigInteger.prototype._multiplyBySmall = function (a) { + if (a.value === 0) return Integer[0]; + if (a.value === 1) return this; + if (a.value === -1) return this.negate(); + return multiplySmallAndArray(Math.abs(a.value), this.value, this.sign !== a.sign); + }; + SmallInteger.prototype.multiply = function (v) { + return parseValue(v)._multiplyBySmall(this); + }; + SmallInteger.prototype.times = SmallInteger.prototype.multiply; + + function square(a) { + var l = a.length, + r = createArray(l + l), + base = BASE, + product, carry, i, a_i, a_j; + for (i = 0; i < l; i++) { + a_i = a[i]; + for (var j = 0; j < l; j++) { + a_j = a[j]; + product = a_i * a_j + r[i + j]; + carry = Math.floor(product / base); + r[i + j] = product - carry * base; + r[i + j + 1] += carry; + } + } + trim(r); + return r; + } + + BigInteger.prototype.square = function () { + return new BigInteger(square(this.value), false); + }; + + SmallInteger.prototype.square = function () { + var value = this.value * this.value; + if (isPrecise(value)) return new SmallInteger(value); + return new BigInteger(square(smallToArray(Math.abs(this.value))), false); + }; + + function divMod1(a, b) { // Left over from previous version. Performs faster than divMod2 on smaller input sizes. + var a_l = a.length, + b_l = b.length, + base = BASE, + result = createArray(b.length), + divisorMostSignificantDigit = b[b_l - 1], + // normalization + lambda = Math.ceil(base / (2 * divisorMostSignificantDigit)), + remainder = multiplySmall(a, lambda), + divisor = multiplySmall(b, lambda), + quotientDigit, shift, carry, borrow, i, l, q; + if (remainder.length <= a_l) remainder.push(0); + divisor.push(0); + divisorMostSignificantDigit = divisor[b_l - 1]; + for (shift = a_l - b_l; shift >= 0; shift--) { + quotientDigit = base - 1; + if (remainder[shift + b_l] !== divisorMostSignificantDigit) { + quotientDigit = Math.floor((remainder[shift + b_l] * base + remainder[shift + b_l - 1]) / divisorMostSignificantDigit); + } + // quotientDigit <= base - 1 + carry = 0; + borrow = 0; + l = divisor.length; + for (i = 0; i < l; i++) { + carry += quotientDigit * divisor[i]; + q = Math.floor(carry / base); + borrow += remainder[shift + i] - (carry - q * base); + carry = q; + if (borrow < 0) { + remainder[shift + i] = borrow + base; + borrow = -1; + } else { + remainder[shift + i] = borrow; + borrow = 0; + } + } + while (borrow !== 0) { + quotientDigit -= 1; + carry = 0; + for (i = 0; i < l; i++) { + carry += remainder[shift + i] - base + divisor[i]; + if (carry < 0) { + remainder[shift + i] = carry + base; + carry = 0; + } else { + remainder[shift + i] = carry; + carry = 1; + } + } + borrow += carry; + } + result[shift] = quotientDigit; + } + // denormalization + remainder = divModSmall(remainder, lambda)[0]; + return [arrayToSmall(result), arrayToSmall(remainder)]; + } + + function divMod2(a, b) { // Implementation idea shamelessly stolen from Silent Matt's library http://silentmatt.com/biginteger/ + // Performs faster than divMod1 on larger input sizes. + var a_l = a.length, + b_l = b.length, + result = [], + part = [], + base = BASE, + guess, xlen, highx, highy, check; + while (a_l) { + part.unshift(a[--a_l]); + if (compareAbs(part, b) < 0) { + result.push(0); + continue; + } + xlen = part.length; + highx = part[xlen - 1] * base + part[xlen - 2]; + highy = b[b_l - 1] * base + b[b_l - 2]; + if (xlen > b_l) { + highx = (highx + 1) * base; + } + guess = Math.ceil(highx / highy); + do { + check = multiplySmall(b, guess); + if (compareAbs(check, part) <= 0) break; + guess--; + } while (guess); + result.push(guess); + part = subtract(part, check); + } + result.reverse(); + return [arrayToSmall(result), arrayToSmall(part)]; + } + + function divModSmall(value, lambda) { + var length = value.length, + quotient = createArray(length), + base = BASE, + i, q, remainder, divisor; + remainder = 0; + for (i = length - 1; i >= 0; --i) { + divisor = remainder * base + value[i]; + q = truncate(divisor / lambda); + remainder = divisor - q * lambda; + quotient[i] = q | 0; + } + return [quotient, remainder | 0]; + } + + function divModAny(self, v) { + var value, n = parseValue(v); + var a = self.value, b = n.value; + var quotient; + if (b === 0) throw new Error("Cannot divide by zero"); + if (self.isSmall) { + if (n.isSmall) { + return [new SmallInteger(truncate(a / b)), new SmallInteger(a % b)]; + } + return [Integer[0], self]; + } + if (n.isSmall) { + if (b === 1) return [self, Integer[0]]; + if (b == -1) return [self.negate(), Integer[0]]; + var abs = Math.abs(b); + if (abs < BASE) { + value = divModSmall(a, abs); + quotient = arrayToSmall(value[0]); + var remainder = value[1]; + if (self.sign) remainder = -remainder; + if (typeof quotient === "number") { + if (self.sign !== n.sign) quotient = -quotient; + return [new SmallInteger(quotient), new SmallInteger(remainder)]; + } + return [new BigInteger(quotient, self.sign !== n.sign), new SmallInteger(remainder)]; + } + b = smallToArray(abs); + } + var comparison = compareAbs(a, b); + if (comparison === -1) return [Integer[0], self]; + if (comparison === 0) return [Integer[self.sign === n.sign ? 1 : -1], Integer[0]]; + + // divMod1 is faster on smaller input sizes + if (a.length + b.length <= 200) + value = divMod1(a, b); + else value = divMod2(a, b); + + quotient = value[0]; + var qSign = self.sign !== n.sign, + mod = value[1], + mSign = self.sign; + if (typeof quotient === "number") { + if (qSign) quotient = -quotient; + quotient = new SmallInteger(quotient); + } else quotient = new BigInteger(quotient, qSign); + if (typeof mod === "number") { + if (mSign) mod = -mod; + mod = new SmallInteger(mod); + } else mod = new BigInteger(mod, mSign); + return [quotient, mod]; + } + + BigInteger.prototype.divmod = function (v) { + var result = divModAny(this, v); + return { + quotient: result[0], + remainder: result[1] + }; + }; + SmallInteger.prototype.divmod = BigInteger.prototype.divmod; + + BigInteger.prototype.divide = function (v) { + return divModAny(this, v)[0]; + }; + SmallInteger.prototype.over = SmallInteger.prototype.divide = BigInteger.prototype.over = BigInteger.prototype.divide; + + BigInteger.prototype.mod = function (v) { + return divModAny(this, v)[1]; + }; + SmallInteger.prototype.remainder = SmallInteger.prototype.mod = BigInteger.prototype.remainder = BigInteger.prototype.mod; + + BigInteger.prototype.pow = function (v) { + var n = parseValue(v), + a = this.value, + b = n.value, + value, x, y; + if (b === 0) return Integer[1]; + if (a === 0) return Integer[0]; + if (a === 1) return Integer[1]; + if (a === -1) return n.isEven() ? Integer[1] : Integer[-1]; + if (n.sign) { + return Integer[0]; + } + if (!n.isSmall) throw new Error("The exponent " + n.toString() + " is too large."); + if (this.isSmall) { + if (isPrecise(value = Math.pow(a, b))) + return new SmallInteger(truncate(value)); + } + x = this; + y = Integer[1]; + while (true) { + if (b & 1 === 1) { + y = y.times(x); + --b; + } + if (b === 0) break; + b /= 2; + x = x.square(); + } + return y; + }; + SmallInteger.prototype.pow = BigInteger.prototype.pow; + + BigInteger.prototype.modPow = function (exp, mod) { + exp = parseValue(exp); + mod = parseValue(mod); + if (mod.isZero()) throw new Error("Cannot take modPow with modulus 0"); + var r = Integer[1], + base = this.mod(mod); + while (exp.isPositive()) { + if (base.isZero()) return Integer[0]; + if (exp.isOdd()) r = r.multiply(base).mod(mod); + exp = exp.divide(2); + base = base.square().mod(mod); + } + return r; + }; + SmallInteger.prototype.modPow = BigInteger.prototype.modPow; + + function compareAbs(a, b) { + if (a.length !== b.length) { + return a.length > b.length ? 1 : -1; + } + for (var i = a.length - 1; i >= 0; i--) { + if (a[i] !== b[i]) return a[i] > b[i] ? 1 : -1; + } + return 0; + } + + BigInteger.prototype.compareAbs = function (v) { + var n = parseValue(v), + a = this.value, + b = n.value; + if (n.isSmall) return 1; + return compareAbs(a, b); + }; + SmallInteger.prototype.compareAbs = function (v) { + var n = parseValue(v), + a = Math.abs(this.value), + b = n.value; + if (n.isSmall) { + b = Math.abs(b); + return a === b ? 0 : a > b ? 1 : -1; + } + return -1; + }; + + BigInteger.prototype.compare = function (v) { + // See discussion about comparison with Infinity: + // https://github.com/peterolson/BigInteger.js/issues/61 + if (v === Infinity) { + return -1; + } + if (v === -Infinity) { + return 1; + } + + var n = parseValue(v), + a = this.value, + b = n.value; + if (this.sign !== n.sign) { + return n.sign ? 1 : -1; + } + if (n.isSmall) { + return this.sign ? -1 : 1; + } + return compareAbs(a, b) * (this.sign ? -1 : 1); + }; + BigInteger.prototype.compareTo = BigInteger.prototype.compare; + + SmallInteger.prototype.compare = function (v) { + if (v === Infinity) { + return -1; + } + if (v === -Infinity) { + return 1; + } + + var n = parseValue(v), + a = this.value, + b = n.value; + if (n.isSmall) { + return a == b ? 0 : a > b ? 1 : -1; + } + if (a < 0 !== n.sign) { + return a < 0 ? -1 : 1; + } + return a < 0 ? 1 : -1; + }; + SmallInteger.prototype.compareTo = SmallInteger.prototype.compare; + + BigInteger.prototype.equals = function (v) { + return this.compare(v) === 0; + }; + SmallInteger.prototype.eq = SmallInteger.prototype.equals = BigInteger.prototype.eq = BigInteger.prototype.equals; + + BigInteger.prototype.notEquals = function (v) { + return this.compare(v) !== 0; + }; + SmallInteger.prototype.neq = SmallInteger.prototype.notEquals = BigInteger.prototype.neq = BigInteger.prototype.notEquals; + + BigInteger.prototype.greater = function (v) { + return this.compare(v) > 0; + }; + SmallInteger.prototype.gt = SmallInteger.prototype.greater = BigInteger.prototype.gt = BigInteger.prototype.greater; + + BigInteger.prototype.lesser = function (v) { + return this.compare(v) < 0; + }; + SmallInteger.prototype.lt = SmallInteger.prototype.lesser = BigInteger.prototype.lt = BigInteger.prototype.lesser; + + BigInteger.prototype.greaterOrEquals = function (v) { + return this.compare(v) >= 0; + }; + SmallInteger.prototype.geq = SmallInteger.prototype.greaterOrEquals = BigInteger.prototype.geq = BigInteger.prototype.greaterOrEquals; + + BigInteger.prototype.lesserOrEquals = function (v) { + return this.compare(v) <= 0; + }; + SmallInteger.prototype.leq = SmallInteger.prototype.lesserOrEquals = BigInteger.prototype.leq = BigInteger.prototype.lesserOrEquals; + + BigInteger.prototype.isEven = function () { + return (this.value[0] & 1) === 0; + }; + SmallInteger.prototype.isEven = function () { + return (this.value & 1) === 0; + }; + + BigInteger.prototype.isOdd = function () { + return (this.value[0] & 1) === 1; + }; + SmallInteger.prototype.isOdd = function () { + return (this.value & 1) === 1; + }; + + BigInteger.prototype.isPositive = function () { + return !this.sign; + }; + SmallInteger.prototype.isPositive = function () { + return this.value > 0; + }; + + BigInteger.prototype.isNegative = function () { + return this.sign; + }; + SmallInteger.prototype.isNegative = function () { + return this.value < 0; + }; + + BigInteger.prototype.isUnit = function () { + return false; + }; + SmallInteger.prototype.isUnit = function () { + return Math.abs(this.value) === 1; + }; + + BigInteger.prototype.isZero = function () { + return false; + }; + SmallInteger.prototype.isZero = function () { + return this.value === 0; + }; + BigInteger.prototype.isDivisibleBy = function (v) { + var n = parseValue(v); + var value = n.value; + if (value === 0) return false; + if (value === 1) return true; + if (value === 2) return this.isEven(); + return this.mod(n).equals(Integer[0]); + }; + SmallInteger.prototype.isDivisibleBy = BigInteger.prototype.isDivisibleBy; + + function isBasicPrime(v) { + var n = v.abs(); + if (n.isUnit()) return false; + if (n.equals(2) || n.equals(3) || n.equals(5)) return true; + if (n.isEven() || n.isDivisibleBy(3) || n.isDivisibleBy(5)) return false; + if (n.lesser(25)) return true; + // we don't know if it's prime: let the other functions figure it out + } + + BigInteger.prototype.isPrime = function () { + var isPrime = isBasicPrime(this); + if (isPrime !== undefined) return isPrime; + var n = this.abs(), + nPrev = n.prev(); + var a = [2, 3, 5, 7, 11, 13, 17, 19], + b = nPrev, + d, t, i, x; + while (b.isEven()) b = b.divide(2); + for (i = 0; i < a.length; i++) { + x = bigInt(a[i]).modPow(b, n); + if (x.equals(Integer[1]) || x.equals(nPrev)) continue; + for (t = true, d = b; t && d.lesser(nPrev) ; d = d.multiply(2)) { + x = x.square().mod(n); + if (x.equals(nPrev)) t = false; + } + if (t) return false; + } + return true; + }; + SmallInteger.prototype.isPrime = BigInteger.prototype.isPrime; + + BigInteger.prototype.isProbablePrime = function (iterations) { + var isPrime = isBasicPrime(this); + if (isPrime !== undefined) return isPrime; + var n = this.abs(); + var t = iterations === undefined ? 5 : iterations; + // use the Fermat primality test + for (var i = 0; i < t; i++) { + var a = bigInt.randBetween(2, n.minus(2)); + if (!a.modPow(n.prev(), n).isUnit()) return false; // definitely composite + } + return true; // large chance of being prime + }; + SmallInteger.prototype.isProbablePrime = BigInteger.prototype.isProbablePrime; + + BigInteger.prototype.modInv = function (n) { + var t = bigInt.zero, newT = bigInt.one, r = parseValue(n), newR = this.abs(), q, lastT, lastR; + while (!newR.equals(bigInt.zero)) { + q = r.divide(newR); + lastT = t; + lastR = r; + t = newT; + r = newR; + newT = lastT.subtract(q.multiply(newT)); + newR = lastR.subtract(q.multiply(newR)); + } + if (!r.equals(1)) throw new Error(this.toString() + " and " + n.toString() + " are not co-prime"); + if (t.compare(0) === -1) { + t = t.add(n); + } + return t; + } + SmallInteger.prototype.modInv = BigInteger.prototype.modInv; + + BigInteger.prototype.next = function () { + var value = this.value; + if (this.sign) { + return subtractSmall(value, 1, this.sign); + } + return new BigInteger(addSmall(value, 1), this.sign); + }; + SmallInteger.prototype.next = function () { + var value = this.value; + if (value + 1 < MAX_INT) return new SmallInteger(value + 1); + return new BigInteger(MAX_INT_ARR, false); + }; + + BigInteger.prototype.prev = function () { + var value = this.value; + if (this.sign) { + return new BigInteger(addSmall(value, 1), true); + } + return subtractSmall(value, 1, this.sign); + }; + SmallInteger.prototype.prev = function () { + var value = this.value; + if (value - 1 > -MAX_INT) return new SmallInteger(value - 1); + return new BigInteger(MAX_INT_ARR, true); + }; + + var powersOfTwo = [1]; + while (powersOfTwo[powersOfTwo.length - 1] <= BASE) powersOfTwo.push(2 * powersOfTwo[powersOfTwo.length - 1]); + var powers2Length = powersOfTwo.length, highestPower2 = powersOfTwo[powers2Length - 1]; + + function shift_isSmall(n) { + return ((typeof n === "number" || typeof n === "string") && +Math.abs(n) <= BASE) || + (n instanceof BigInteger && n.value.length <= 1); + } + + BigInteger.prototype.shiftLeft = function (n) { + if (!shift_isSmall(n)) { + throw new Error(String(n) + " is too large for shifting."); + } + n = +n; + if (n < 0) return this.shiftRight(-n); + var result = this; + while (n >= powers2Length) { + result = result.multiply(highestPower2); + n -= powers2Length - 1; + } + return result.multiply(powersOfTwo[n]); + }; + SmallInteger.prototype.shiftLeft = BigInteger.prototype.shiftLeft; + + BigInteger.prototype.shiftRight = function (n) { + var remQuo; + if (!shift_isSmall(n)) { + throw new Error(String(n) + " is too large for shifting."); + } + n = +n; + if (n < 0) return this.shiftLeft(-n); + var result = this; + while (n >= powers2Length) { + if (result.isZero()) return result; + remQuo = divModAny(result, highestPower2); + result = remQuo[1].isNegative() ? remQuo[0].prev() : remQuo[0]; + n -= powers2Length - 1; + } + remQuo = divModAny(result, powersOfTwo[n]); + return remQuo[1].isNegative() ? remQuo[0].prev() : remQuo[0]; + }; + SmallInteger.prototype.shiftRight = BigInteger.prototype.shiftRight; + + function bitwise(x, y, fn) { + y = parseValue(y); + var xSign = x.isNegative(), ySign = y.isNegative(); + var xRem = xSign ? x.not() : x, + yRem = ySign ? y.not() : y; + var xBits = [], yBits = []; + var xStop = false, yStop = false; + while (!xStop || !yStop) { + if (xRem.isZero()) { // virtual sign extension for simulating two's complement + xStop = true; + xBits.push(xSign ? 1 : 0); + } + else if (xSign) xBits.push(xRem.isEven() ? 1 : 0); // two's complement for negative numbers + else xBits.push(xRem.isEven() ? 0 : 1); + + if (yRem.isZero()) { + yStop = true; + yBits.push(ySign ? 1 : 0); + } + else if (ySign) yBits.push(yRem.isEven() ? 1 : 0); + else yBits.push(yRem.isEven() ? 0 : 1); + + xRem = xRem.over(2); + yRem = yRem.over(2); + } + var result = []; + for (var i = 0; i < xBits.length; i++) result.push(fn(xBits[i], yBits[i])); + var sum = bigInt(result.pop()).negate().times(bigInt(2).pow(result.length)); + while (result.length) { + sum = sum.add(bigInt(result.pop()).times(bigInt(2).pow(result.length))); + } + return sum; + } + + BigInteger.prototype.not = function () { + return this.negate().prev(); + }; + SmallInteger.prototype.not = BigInteger.prototype.not; + + BigInteger.prototype.and = function (n) { + return bitwise(this, n, function (a, b) { return a & b; }); + }; + SmallInteger.prototype.and = BigInteger.prototype.and; + + BigInteger.prototype.or = function (n) { + return bitwise(this, n, function (a, b) { return a | b; }); + }; + SmallInteger.prototype.or = BigInteger.prototype.or; + + BigInteger.prototype.xor = function (n) { + return bitwise(this, n, function (a, b) { return a ^ b; }); + }; + SmallInteger.prototype.xor = BigInteger.prototype.xor; + + var LOBMASK_I = 1 << 30, LOBMASK_BI = (BASE & -BASE) * (BASE & -BASE) | LOBMASK_I; + function roughLOB(n) { // get lowestOneBit (rough) + // SmallInteger: return Min(lowestOneBit(n), 1 << 30) + // BigInteger: return Min(lowestOneBit(n), 1 << 14) [BASE=1e7] + var v = n.value, x = typeof v === "number" ? v | LOBMASK_I : v[0] + v[1] * BASE | LOBMASK_BI; + return x & -x; + } + + function max(a, b) { + a = parseValue(a); + b = parseValue(b); + return a.greater(b) ? a : b; + } + function min(a,b) { + a = parseValue(a); + b = parseValue(b); + return a.lesser(b) ? a : b; + } + function gcd(a, b) { + a = parseValue(a).abs(); + b = parseValue(b).abs(); + if (a.equals(b)) return a; + if (a.isZero()) return b; + if (b.isZero()) return a; + var c = Integer[1], d, t; + while (a.isEven() && b.isEven()) { + d = Math.min(roughLOB(a), roughLOB(b)); + a = a.divide(d); + b = b.divide(d); + c = c.multiply(d); + } + while (a.isEven()) { + a = a.divide(roughLOB(a)); + } + do { + while (b.isEven()) { + b = b.divide(roughLOB(b)); + } + if (a.greater(b)) { + t = b; b = a; a = t; + } + b = b.subtract(a); + } while (!b.isZero()); + return c.isUnit() ? a : a.multiply(c); + } + function lcm(a, b) { + a = parseValue(a).abs(); + b = parseValue(b).abs(); + return a.divide(gcd(a, b)).multiply(b); + } + function randBetween(a, b) { + a = parseValue(a); + b = parseValue(b); + var low = min(a, b), high = max(a, b); + var range = high.subtract(low); + if (range.isSmall) return low.add(Math.round(Math.random() * range)); + var length = range.value.length - 1; + var result = [], restricted = true; + for (var i = length; i >= 0; i--) { + var top = restricted ? range.value[i] : BASE; + var digit = truncate(Math.random() * top); + result.unshift(digit); + if (digit < top) restricted = false; + } + result = arrayToSmall(result); + return low.add(typeof result === "number" ? new SmallInteger(result) : new BigInteger(result, false)); + } + var parseBase = function (text, base) { + var val = Integer[0], pow = Integer[1], + length = text.length; + if (2 <= base && base <= 36) { + if (length <= LOG_MAX_INT / Math.log(base)) { + return new SmallInteger(parseInt(text, base)); + } + } + base = parseValue(base); + var digits = []; + var i; + var isNegative = text[0] === "-"; + for (i = isNegative ? 1 : 0; i < text.length; i++) { + var c = text[i].toLowerCase(), + charCode = c.charCodeAt(0); + if (48 <= charCode && charCode <= 57) digits.push(parseValue(c)); + else if (97 <= charCode && charCode <= 122) digits.push(parseValue(c.charCodeAt(0) - 87)); + else if (c === "<") { + var start = i; + do { i++; } while (text[i] !== ">"); + digits.push(parseValue(text.slice(start + 1, i))); + } + else throw new Error(c + " is not a valid character"); + } + digits.reverse(); + for (i = 0; i < digits.length; i++) { + val = val.add(digits[i].times(pow)); + pow = pow.times(base); + } + return isNegative ? val.negate() : val; + }; + + function stringify(digit) { + var v = digit.value; + if (typeof v === "number") v = [v]; + if (v.length === 1 && v[0] <= 35) { + return "0123456789abcdefghijklmnopqrstuvwxyz".charAt(v[0]); + } + return "<" + v + ">"; + } + function toBase(n, base) { + base = bigInt(base); + if (base.isZero()) { + if (n.isZero()) return "0"; + throw new Error("Cannot convert nonzero numbers to base 0."); + } + if (base.equals(-1)) { + if (n.isZero()) return "0"; + if (n.isNegative()) return new Array(1 - n).join("10"); + return "1" + new Array(+n).join("01"); + } + var minusSign = ""; + if (n.isNegative() && base.isPositive()) { + minusSign = "-"; + n = n.abs(); + } + if (base.equals(1)) { + if (n.isZero()) return "0"; + return minusSign + new Array(+n + 1).join(1); + } + var out = []; + var left = n, divmod; + while (left.isNegative() || left.compareAbs(base) >= 0) { + divmod = left.divmod(base); + left = divmod.quotient; + var digit = divmod.remainder; + if (digit.isNegative()) { + digit = base.minus(digit).abs(); + left = left.next(); + } + out.push(stringify(digit)); + } + out.push(stringify(left)); + return minusSign + out.reverse().join(""); + } + + BigInteger.prototype.toString = function (radix) { + if (radix === undefined) radix = 10; + if (radix !== 10) return toBase(this, radix); + var v = this.value, l = v.length, str = String(v[--l]), zeros = "0000000", digit; + while (--l >= 0) { + digit = String(v[l]); + str += zeros.slice(digit.length) + digit; + } + var sign = this.sign ? "-" : ""; + return sign + str; + }; + SmallInteger.prototype.toString = function (radix) { + if (radix === undefined) radix = 10; + if (radix != 10) return toBase(this, radix); + return String(this.value); + }; + + BigInteger.prototype.valueOf = function () { + return +this.toString(); + }; + BigInteger.prototype.toJSNumber = BigInteger.prototype.valueOf; + + SmallInteger.prototype.valueOf = function () { + return this.value; + }; + SmallInteger.prototype.toJSNumber = SmallInteger.prototype.valueOf; + + function parseStringValue(v) { + if (isPrecise(+v)) { + var x = +v; + if (x === truncate(x)) + return new SmallInteger(x); + throw "Invalid integer: " + v; + } + var sign = v[0] === "-"; + if (sign) v = v.slice(1); + var split = v.split(/e/i); + if (split.length > 2) throw new Error("Invalid integer: " + split.join("e")); + if (split.length === 2) { + var exp = split[1]; + if (exp[0] === "+") exp = exp.slice(1); + exp = +exp; + if (exp !== truncate(exp) || !isPrecise(exp)) throw new Error("Invalid integer: " + exp + " is not a valid exponent."); + var text = split[0]; + var decimalPlace = text.indexOf("."); + if (decimalPlace >= 0) { + exp -= text.length - decimalPlace - 1; + text = text.slice(0, decimalPlace) + text.slice(decimalPlace + 1); + } + if (exp < 0) throw new Error("Cannot include negative exponent part for integers"); + text += (new Array(exp + 1)).join("0"); + v = text; + } + var isValid = /^([0-9][0-9]*)$/.test(v); + if (!isValid) throw new Error("Invalid integer: " + v); + var r = [], max = v.length, l = LOG_BASE, min = max - l; + while (max > 0) { + r.push(+v.slice(min, max)); + min -= l; + if (min < 0) min = 0; + max -= l; + } + trim(r); + return new BigInteger(r, sign); + } + + function parseNumberValue(v) { + if (isPrecise(v)) { + if (v !== truncate(v)) throw new Error(v + " is not an integer."); + return new SmallInteger(v); + } + return parseStringValue(v.toString()); + } + + function parseValue(v) { + if (typeof v === "number") { + return parseNumberValue(v); + } + if (typeof v === "string") { + return parseStringValue(v); + } + return v; + } + // Pre-define numbers in range [-999,999] + for (var i = 0; i < 1000; i++) { + Integer[i] = new SmallInteger(i); + if (i > 0) Integer[-i] = new SmallInteger(-i); + } + // Backwards compatibility + Integer.one = Integer[1]; + Integer.zero = Integer[0]; + Integer.minusOne = Integer[-1]; + Integer.max = max; + Integer.min = min; + Integer.gcd = gcd; + Integer.lcm = lcm; + Integer.isInstance = function (x) { return x instanceof BigInteger || x instanceof SmallInteger; }; + Integer.randBetween = randBetween; + return Integer; +})(); + +// Node.js check +if (typeof module !== "undefined" && module.hasOwnProperty("exports")) { + module.exports = bigInt; +} + +},{}],4:[function(require,module,exports){ (function (process,global){ /* @preserve * The MIT License (MIT) @@ -5837,9 +6952,9 @@ module.exports = ret; },{"./es5":13}]},{},[4])(4) }); ;if (typeof window !== 'undefined' && window !== null) { window.P = window.Promise; } else if (typeof self !== 'undefined' && self !== null) { self.P = self.Promise; } }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"_process":21}],4:[function(require,module,exports){ +},{"_process":22}],5:[function(require,module,exports){ -},{}],5:[function(require,module,exports){ +},{}],6:[function(require,module,exports){ (function (global){ 'use strict'; @@ -5951,7 +7066,7 @@ exports.allocUnsafeSlow = function allocUnsafeSlow(size) { } }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"buffer":6}],6:[function(require,module,exports){ +},{"buffer":7}],7:[function(require,module,exports){ (function (global){ /*! * The buffer module from node.js, for the browser. @@ -7744,7 +8859,7 @@ function isnan (val) { } }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"base64-js":2,"ieee754":14,"isarray":17}],7:[function(require,module,exports){ +},{"base64-js":2,"ieee754":15,"isarray":18}],8:[function(require,module,exports){ (function (Buffer){ var Transform = require('stream').Transform var inherits = require('inherits') @@ -7838,7 +8953,7 @@ CipherBase.prototype._toString = function (value, enc, fin) { } }).call(this,require("buffer").Buffer) -},{"buffer":6,"inherits":15,"stream":42,"string_decoder":43}],8:[function(require,module,exports){ +},{"buffer":7,"inherits":16,"stream":43,"string_decoder":44}],9:[function(require,module,exports){ (function (Buffer){ // Copyright Joyent, Inc. and other Node contributors. // @@ -7949,7 +9064,7 @@ function objectToString(o) { } }).call(this,{"isBuffer":require("../../is-buffer/index.js")}) -},{"../../is-buffer/index.js":16}],9:[function(require,module,exports){ +},{"../../is-buffer/index.js":17}],10:[function(require,module,exports){ (function (Buffer){ 'use strict'; var inherits = require('inherits') @@ -8005,7 +9120,7 @@ module.exports = function createHash (alg) { } }).call(this,require("buffer").Buffer) -},{"./md5":11,"buffer":6,"cipher-base":7,"inherits":15,"ripemd160":33,"sha.js":35}],10:[function(require,module,exports){ +},{"./md5":12,"buffer":7,"cipher-base":8,"inherits":16,"ripemd160":34,"sha.js":36}],11:[function(require,module,exports){ (function (Buffer){ 'use strict'; var intSize = 4; @@ -8042,7 +9157,7 @@ function hash(buf, fn, hashSize, bigEndian) { } exports.hash = hash; }).call(this,require("buffer").Buffer) -},{"buffer":6}],11:[function(require,module,exports){ +},{"buffer":7}],12:[function(require,module,exports){ 'use strict'; /* * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message @@ -8199,7 +9314,7 @@ function bit_rol(num, cnt) module.exports = function md5(buf) { return helpers.hash(buf, core_md5, 16); }; -},{"./helpers":10}],12:[function(require,module,exports){ +},{"./helpers":11}],13:[function(require,module,exports){ (function (Buffer){ 'use strict'; var createHash = require('create-hash/browser'); @@ -8271,7 +9386,7 @@ module.exports = function createHmac(alg, key) { } }).call(this,require("buffer").Buffer) -},{"buffer":6,"create-hash/browser":9,"inherits":15,"stream":42}],13:[function(require,module,exports){ +},{"buffer":7,"create-hash/browser":10,"inherits":16,"stream":43}],14:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -8575,7 +9690,7 @@ function isUndefined(arg) { return arg === void 0; } -},{}],14:[function(require,module,exports){ +},{}],15:[function(require,module,exports){ exports.read = function (buffer, offset, isLE, mLen, nBytes) { var e, m var eLen = nBytes * 8 - mLen - 1 @@ -8661,7 +9776,7 @@ exports.write = function (buffer, value, offset, isLE, mLen, nBytes) { buffer[offset + i - d] |= s * 128 } -},{}],15:[function(require,module,exports){ +},{}],16:[function(require,module,exports){ if (typeof Object.create === 'function') { // implementation from standard node.js 'util' module module.exports = function inherits(ctor, superCtor) { @@ -8686,7 +9801,7 @@ if (typeof Object.create === 'function') { } } -},{}],16:[function(require,module,exports){ +},{}],17:[function(require,module,exports){ /*! * Determine if an object is a Buffer * @@ -8709,14 +9824,14 @@ function isSlowBuffer (obj) { return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0)) } -},{}],17:[function(require,module,exports){ +},{}],18:[function(require,module,exports){ var toString = {}.toString; module.exports = Array.isArray || function (arr) { return toString.call(arr) == '[object Array]'; }; -},{}],18:[function(require,module,exports){ +},{}],19:[function(require,module,exports){ (function (process,Buffer){ var createHmac = require('create-hmac') var checkParameters = require('./precondition') @@ -8788,7 +9903,7 @@ exports.pbkdf2Sync = function (password, salt, iterations, keylen, digest) { } }).call(this,require('_process'),require("buffer").Buffer) -},{"./precondition":19,"_process":21,"buffer":6,"create-hmac":12}],19:[function(require,module,exports){ +},{"./precondition":20,"_process":22,"buffer":7,"create-hmac":13}],20:[function(require,module,exports){ var MAX_ALLOC = Math.pow(2, 30) - 1 // default in iojs module.exports = function (iterations, keylen) { if (typeof iterations !== 'number') { @@ -8808,7 +9923,7 @@ module.exports = function (iterations, keylen) { } } -},{}],20:[function(require,module,exports){ +},{}],21:[function(require,module,exports){ (function (process){ 'use strict'; @@ -8855,7 +9970,7 @@ function nextTick(fn, arg1, arg2, arg3) { } }).call(this,require('_process')) -},{"_process":21}],21:[function(require,module,exports){ +},{"_process":22}],22:[function(require,module,exports){ // shim for using process in browser var process = module.exports = {}; @@ -9037,10 +10152,10 @@ process.chdir = function (dir) { }; process.umask = function() { return 0; }; -},{}],22:[function(require,module,exports){ +},{}],23:[function(require,module,exports){ module.exports = require("./lib/_stream_duplex.js") -},{"./lib/_stream_duplex.js":23}],23:[function(require,module,exports){ +},{"./lib/_stream_duplex.js":24}],24:[function(require,module,exports){ // a duplex stream is just a stream that is both readable and writable. // Since JS doesn't have multiple prototypal inheritance, this class // prototypally inherits from Readable, and then parasitically from @@ -9116,7 +10231,7 @@ function forEach(xs, f) { f(xs[i], i); } } -},{"./_stream_readable":25,"./_stream_writable":27,"core-util-is":8,"inherits":15,"process-nextick-args":20}],24:[function(require,module,exports){ +},{"./_stream_readable":26,"./_stream_writable":28,"core-util-is":9,"inherits":16,"process-nextick-args":21}],25:[function(require,module,exports){ // a passthrough stream. // basically just the most minimal sort of Transform stream. // Every written chunk gets output as-is. @@ -9143,7 +10258,7 @@ function PassThrough(options) { PassThrough.prototype._transform = function (chunk, encoding, cb) { cb(null, chunk); }; -},{"./_stream_transform":26,"core-util-is":8,"inherits":15}],25:[function(require,module,exports){ +},{"./_stream_transform":27,"core-util-is":9,"inherits":16}],26:[function(require,module,exports){ (function (process){ 'use strict'; @@ -10083,7 +11198,7 @@ function indexOf(xs, x) { return -1; } }).call(this,require('_process')) -},{"./_stream_duplex":23,"./internal/streams/BufferList":28,"_process":21,"buffer":6,"buffer-shims":5,"core-util-is":8,"events":13,"inherits":15,"isarray":17,"process-nextick-args":20,"string_decoder/":43,"util":4}],26:[function(require,module,exports){ +},{"./_stream_duplex":24,"./internal/streams/BufferList":29,"_process":22,"buffer":7,"buffer-shims":6,"core-util-is":9,"events":14,"inherits":16,"isarray":18,"process-nextick-args":21,"string_decoder/":44,"util":5}],27:[function(require,module,exports){ // a transform stream is a readable/writable stream where you do // something with the data. Sometimes it's called a "filter", // but that's not a great name for it, since that implies a thing where @@ -10264,7 +11379,7 @@ function done(stream, er) { return stream.push(null); } -},{"./_stream_duplex":23,"core-util-is":8,"inherits":15}],27:[function(require,module,exports){ +},{"./_stream_duplex":24,"core-util-is":9,"inherits":16}],28:[function(require,module,exports){ (function (process){ // A bit simpler than readable streams. // Implement an async ._write(chunk, encoding, cb), and it'll handle all @@ -10793,7 +11908,7 @@ function CorkedRequest(state) { }; } }).call(this,require('_process')) -},{"./_stream_duplex":23,"_process":21,"buffer":6,"buffer-shims":5,"core-util-is":8,"events":13,"inherits":15,"process-nextick-args":20,"util-deprecate":44}],28:[function(require,module,exports){ +},{"./_stream_duplex":24,"_process":22,"buffer":7,"buffer-shims":6,"core-util-is":9,"events":14,"inherits":16,"process-nextick-args":21,"util-deprecate":45}],29:[function(require,module,exports){ 'use strict'; var Buffer = require('buffer').Buffer; @@ -10858,10 +11973,10 @@ BufferList.prototype.concat = function (n) { } return ret; }; -},{"buffer":6,"buffer-shims":5}],29:[function(require,module,exports){ +},{"buffer":7,"buffer-shims":6}],30:[function(require,module,exports){ module.exports = require("./lib/_stream_passthrough.js") -},{"./lib/_stream_passthrough.js":24}],30:[function(require,module,exports){ +},{"./lib/_stream_passthrough.js":25}],31:[function(require,module,exports){ (function (process){ var Stream = (function (){ try { @@ -10881,13 +11996,13 @@ if (!process.browser && process.env.READABLE_STREAM === 'disable' && Stream) { } }).call(this,require('_process')) -},{"./lib/_stream_duplex.js":23,"./lib/_stream_passthrough.js":24,"./lib/_stream_readable.js":25,"./lib/_stream_transform.js":26,"./lib/_stream_writable.js":27,"_process":21}],31:[function(require,module,exports){ +},{"./lib/_stream_duplex.js":24,"./lib/_stream_passthrough.js":25,"./lib/_stream_readable.js":26,"./lib/_stream_transform.js":27,"./lib/_stream_writable.js":28,"_process":22}],32:[function(require,module,exports){ module.exports = require("./lib/_stream_transform.js") -},{"./lib/_stream_transform.js":26}],32:[function(require,module,exports){ +},{"./lib/_stream_transform.js":27}],33:[function(require,module,exports){ module.exports = require("./lib/_stream_writable.js") -},{"./lib/_stream_writable.js":27}],33:[function(require,module,exports){ +},{"./lib/_stream_writable.js":28}],34:[function(require,module,exports){ (function (Buffer){ /* CryptoJS v3.1.2 @@ -11101,7 +12216,7 @@ function ripemd160 (message) { module.exports = ripemd160 }).call(this,require("buffer").Buffer) -},{"buffer":6}],34:[function(require,module,exports){ +},{"buffer":7}],35:[function(require,module,exports){ (function (Buffer){ // prototype class for hash functions function Hash (blockSize, finalSize) { @@ -11174,7 +12289,7 @@ Hash.prototype._update = function () { module.exports = Hash }).call(this,require("buffer").Buffer) -},{"buffer":6}],35:[function(require,module,exports){ +},{"buffer":7}],36:[function(require,module,exports){ var exports = module.exports = function SHA (algorithm) { algorithm = algorithm.toLowerCase() @@ -11191,7 +12306,7 @@ exports.sha256 = require('./sha256') exports.sha384 = require('./sha384') exports.sha512 = require('./sha512') -},{"./sha":36,"./sha1":37,"./sha224":38,"./sha256":39,"./sha384":40,"./sha512":41}],36:[function(require,module,exports){ +},{"./sha":37,"./sha1":38,"./sha224":39,"./sha256":40,"./sha384":41,"./sha512":42}],37:[function(require,module,exports){ (function (Buffer){ /* * A JavaScript implementation of the Secure Hash Algorithm, SHA-0, as defined @@ -11288,7 +12403,7 @@ Sha.prototype._hash = function () { module.exports = Sha }).call(this,require("buffer").Buffer) -},{"./hash":34,"buffer":6,"inherits":15}],37:[function(require,module,exports){ +},{"./hash":35,"buffer":7,"inherits":16}],38:[function(require,module,exports){ (function (Buffer){ /* * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined @@ -11390,7 +12505,7 @@ Sha1.prototype._hash = function () { module.exports = Sha1 }).call(this,require("buffer").Buffer) -},{"./hash":34,"buffer":6,"inherits":15}],38:[function(require,module,exports){ +},{"./hash":35,"buffer":7,"inherits":16}],39:[function(require,module,exports){ (function (Buffer){ /** * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined @@ -11446,7 +12561,7 @@ Sha224.prototype._hash = function () { module.exports = Sha224 }).call(this,require("buffer").Buffer) -},{"./hash":34,"./sha256":39,"buffer":6,"inherits":15}],39:[function(require,module,exports){ +},{"./hash":35,"./sha256":40,"buffer":7,"inherits":16}],40:[function(require,module,exports){ (function (Buffer){ /** * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined @@ -11584,7 +12699,7 @@ Sha256.prototype._hash = function () { module.exports = Sha256 }).call(this,require("buffer").Buffer) -},{"./hash":34,"buffer":6,"inherits":15}],40:[function(require,module,exports){ +},{"./hash":35,"buffer":7,"inherits":16}],41:[function(require,module,exports){ (function (Buffer){ var inherits = require('inherits') var SHA512 = require('./sha512') @@ -11644,7 +12759,7 @@ Sha384.prototype._hash = function () { module.exports = Sha384 }).call(this,require("buffer").Buffer) -},{"./hash":34,"./sha512":41,"buffer":6,"inherits":15}],41:[function(require,module,exports){ +},{"./hash":35,"./sha512":42,"buffer":7,"inherits":16}],42:[function(require,module,exports){ (function (Buffer){ var inherits = require('inherits') var Hash = require('./hash') @@ -11907,7 +13022,7 @@ Sha512.prototype._hash = function () { module.exports = Sha512 }).call(this,require("buffer").Buffer) -},{"./hash":34,"buffer":6,"inherits":15}],42:[function(require,module,exports){ +},{"./hash":35,"buffer":7,"inherits":16}],43:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -12036,7 +13151,7 @@ Stream.prototype.pipe = function(dest, options) { return dest; }; -},{"events":13,"inherits":15,"readable-stream/duplex.js":22,"readable-stream/passthrough.js":29,"readable-stream/readable.js":30,"readable-stream/transform.js":31,"readable-stream/writable.js":32}],43:[function(require,module,exports){ +},{"events":14,"inherits":16,"readable-stream/duplex.js":23,"readable-stream/passthrough.js":30,"readable-stream/readable.js":31,"readable-stream/transform.js":32,"readable-stream/writable.js":33}],44:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -12259,7 +13374,7 @@ function base64DetectIncompleteChar(buffer) { this.charLength = this.charReceived ? 3 : 0; } -},{"buffer":6}],44:[function(require,module,exports){ +},{"buffer":7}],45:[function(require,module,exports){ (function (global){ /** @@ -12330,5 +13445,200 @@ function config (name) { } }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{}]},{},[1])(1) +},{}],46:[function(require,module,exports){ +(function (Buffer){ +var pbkdf2 = require('pbkdf2'); +var createHMAC = require('create-hmac'); +var Promise = require("bluebird"); + + +module.exports = { + encryptLogin: encryptLogin, + renderPassword: renderPassword, + createFingerprint: createFingerprint, + _deriveEncryptedLogin: deriveEncryptedLogin, + _getPasswordTemplate: getPasswordTemplate, + _prettyPrint: prettyPrint, + _string2charCodes: string2charCodes, + _getCharType: getCharType, + _getPasswordChar: getPasswordChar, + _createHmac: createHmac, +}; + + +function encryptLogin(login, masterPassword, options) { + var _options = options !== undefined ? options : {}; + var iterations = _options.iterations || 8192; + var keylen = _options.keylen || 32; + + return new Promise(function (resolve, reject) { + if (!login || !masterPassword) { + reject('login and master password parameters could not be empty'); + } + pbkdf2.pbkdf2(masterPassword, login, iterations, keylen, 'sha256', function (error, key) { + if (error) { + reject('error in pbkdf2'); + } else { + resolve(key.toString('hex')); + } + }); + }) +} + +function renderPassword(encryptedLogin, site, passwordOptions) { + return deriveEncryptedLogin(encryptedLogin, site, passwordOptions).then(function (derivedEncryptedLogin) { + var template = passwordOptions.template || getPasswordTemplate(passwordOptions); + return prettyPrint(derivedEncryptedLogin, template); + }); +} + +function createHmac(encryptedLogin, salt) { + return new Promise(function (resolve) { + resolve(createHMAC('sha256', new Buffer(encryptedLogin)).update(salt).digest('hex')); + }); +} + +function deriveEncryptedLogin(encryptedLogin, site, options) { + var _options = options !== undefined ? options : {}; + var length = _options.length || 12; + var counter = _options.counter || 1; + + var salt = site + counter.toString(); + return createHmac(encryptedLogin, salt).then(function (derivedHash) { + return derivedHash.substring(0, length); + }); +} + +function getPasswordTemplate(passwordTypes) { + var templates = { + lowercase: 'vc', + uppercase: 'VC', + numbers: 'n', + symbols: 's', + }; + var returnedTemplate = ''; + Object.keys(templates).forEach(function (template) { + if (passwordTypes.hasOwnProperty(template) && passwordTypes[template]) { + returnedTemplate += templates[template] + } + }); + return returnedTemplate; +} + +function prettyPrint(hash, template) { + var password = ''; + + string2charCodes(hash).forEach(function (charCode, index) { + var charType = getCharType(template, index); + password += getPasswordChar(charType, charCode); + }); + return password; +} + +function string2charCodes(text) { + var charCodes = []; + for (var i = 0; i < text.length; i++) { + charCodes.push(text.charCodeAt(i)); + } + return charCodes; +} + +function getCharType(template, index) { + return template[index % template.length]; +} + +function getPasswordChar(charType, index) { + var passwordsChars = { + V: 'AEIOUY', + C: 'BCDFGHJKLMNPQRSTVWXZ', + v: 'aeiouy', + c: 'bcdfghjklmnpqrstvwxz', + A: 'AEIOUYBCDFGHJKLMNPQRSTVWXZ', + a: 'AEIOUYaeiouyBCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz', + n: '0123456789', + s: '@&%?,=[]_:-+*$#!\'^~;()/.', + x: 'AEIOUYaeiouyBCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz0123456789@&%?,=[]_:-+*$#!\'^~;()/.' + }; + var passwordChar = passwordsChars[charType]; + return passwordChar[index % passwordChar.length]; +} + +function createFingerprint(str) { + return new Promise(function (resolve) { + resolve(createHMAC('sha256', new Buffer(str)).digest('hex')) + }); +} +}).call(this,require("buffer").Buffer) +},{"bluebird":4,"buffer":7,"create-hmac":13,"pbkdf2":19}],47:[function(require,module,exports){ +var Promise = require("bluebird"); +var pbkdf2 = require('pbkdf2'); +var bigInt = require("big-integer"); + +module.exports = { + generatePassword: generatePassword, + _calcEntropy: calcEntropy, + _getSetOfCharacters: getSetOfCharacters, + _renderPassword: renderPassword +}; + +function generatePassword(site, login, masterPassword, passwordProfile) { + return calcEntropy(site, login, masterPassword, passwordProfile).then(function (entropy) { + var setOfCharacters = getSetOfCharacters(passwordProfile); + return renderPassword(entropy, setOfCharacters, passwordProfile) + }); +} + +function calcEntropy(site, login, masterPassword, passwordProfile) { + return new Promise(function (resolve, reject) { + var salt = site + login + passwordProfile.counter.toString(16); + var iterations = passwordProfile.iterations || 100000; + var keylen = passwordProfile.keylen || 32; + var digest = passwordProfile.digest || 'sha256'; + pbkdf2.pbkdf2(masterPassword, salt, iterations, keylen, digest, function (error, key) { + if (error) { + reject('error in pbkdf2'); + } else { + resolve(key.toString('hex')); + } + }); + }); +} + +function getSetOfCharacters(passwordProfile) { + var subsetOfCharacters = { + lowercase: 'abcdefghijklmnopqrstuvwxyz', + uppercase: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', + digits: '0123456789', + symbols: '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~' + }; + + if (typeof passwordProfile === 'undefined') { + return subsetOfCharacters.lowercase + subsetOfCharacters.uppercase + subsetOfCharacters.digits; + } + + var setOfCharacters = ''; + ['lowercase', 'uppercase', 'digits', 'symbols'].forEach(function (subset) { + if (passwordProfile.hasOwnProperty(subset) && passwordProfile[subset]) { + setOfCharacters += subsetOfCharacters[subset] + } + }); + return setOfCharacters; +} + +function consumeEntropy(generatedPassword, quotient, setOfCharacters, maxLength) { + if (generatedPassword.length >= maxLength) { + return generatedPassword + } + var longDivision = quotient.divmod(setOfCharacters.length); + generatedPassword += setOfCharacters[longDivision.remainder]; + return consumeEntropy(generatedPassword, longDivision.quotient, setOfCharacters, maxLength) +} + +function renderPassword(entropy, setOfCharacters, passwordProfile) { + var _passwordProfile = passwordProfile !== undefined ? passwordProfile : {}; + var length = _passwordProfile.length || 14; + return consumeEntropy('', bigInt(entropy, 16), setOfCharacters, length); +} + +},{"big-integer":3,"bluebird":4,"pbkdf2":19}]},{},[1])(1) }); \ No newline at end of file diff --git a/src/v1.js b/src/v1.js new file mode 100644 index 0000000..1ab4259 --- /dev/null +++ b/src/v1.js @@ -0,0 +1,121 @@ +var pbkdf2 = require('pbkdf2'); +var createHMAC = require('create-hmac'); +var Promise = require("bluebird"); + + +module.exports = { + encryptLogin: encryptLogin, + renderPassword: renderPassword, + createFingerprint: createFingerprint, + _deriveEncryptedLogin: deriveEncryptedLogin, + _getPasswordTemplate: getPasswordTemplate, + _prettyPrint: prettyPrint, + _string2charCodes: string2charCodes, + _getCharType: getCharType, + _getPasswordChar: getPasswordChar, + _createHmac: createHmac, +}; + + +function encryptLogin(login, masterPassword, options) { + var _options = options !== undefined ? options : {}; + var iterations = _options.iterations || 8192; + var keylen = _options.keylen || 32; + + return new Promise(function (resolve, reject) { + if (!login || !masterPassword) { + reject('login and master password parameters could not be empty'); + } + pbkdf2.pbkdf2(masterPassword, login, iterations, keylen, 'sha256', function (error, key) { + if (error) { + reject('error in pbkdf2'); + } else { + resolve(key.toString('hex')); + } + }); + }) +} + +function renderPassword(encryptedLogin, site, passwordOptions) { + return deriveEncryptedLogin(encryptedLogin, site, passwordOptions).then(function (derivedEncryptedLogin) { + var template = passwordOptions.template || getPasswordTemplate(passwordOptions); + return prettyPrint(derivedEncryptedLogin, template); + }); +} + +function createHmac(encryptedLogin, salt) { + return new Promise(function (resolve) { + resolve(createHMAC('sha256', new Buffer(encryptedLogin)).update(salt).digest('hex')); + }); +} + +function deriveEncryptedLogin(encryptedLogin, site, options) { + var _options = options !== undefined ? options : {}; + var length = _options.length || 12; + var counter = _options.counter || 1; + + var salt = site + counter.toString(); + return createHmac(encryptedLogin, salt).then(function (derivedHash) { + return derivedHash.substring(0, length); + }); +} + +function getPasswordTemplate(passwordTypes) { + var templates = { + lowercase: 'vc', + uppercase: 'VC', + numbers: 'n', + symbols: 's', + }; + var returnedTemplate = ''; + Object.keys(templates).forEach(function (template) { + if (passwordTypes.hasOwnProperty(template) && passwordTypes[template]) { + returnedTemplate += templates[template] + } + }); + return returnedTemplate; +} + +function prettyPrint(hash, template) { + var password = ''; + + string2charCodes(hash).forEach(function (charCode, index) { + var charType = getCharType(template, index); + password += getPasswordChar(charType, charCode); + }); + return password; +} + +function string2charCodes(text) { + var charCodes = []; + for (var i = 0; i < text.length; i++) { + charCodes.push(text.charCodeAt(i)); + } + return charCodes; +} + +function getCharType(template, index) { + return template[index % template.length]; +} + +function getPasswordChar(charType, index) { + var passwordsChars = { + V: 'AEIOUY', + C: 'BCDFGHJKLMNPQRSTVWXZ', + v: 'aeiouy', + c: 'bcdfghjklmnpqrstvwxz', + A: 'AEIOUYBCDFGHJKLMNPQRSTVWXZ', + a: 'AEIOUYaeiouyBCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz', + n: '0123456789', + s: '@&%?,=[]_:-+*$#!\'^~;()/.', + x: 'AEIOUYaeiouyBCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz0123456789@&%?,=[]_:-+*$#!\'^~;()/.' + }; + var passwordChar = passwordsChars[charType]; + return passwordChar[index % passwordChar.length]; +} + +function createFingerprint(str) { + return new Promise(function (resolve) { + resolve(createHMAC('sha256', new Buffer(str)).digest('hex')) + }); +} \ No newline at end of file diff --git a/src/v2.js b/src/v2.js index 9fad7d9..72b9e84 100644 --- a/src/v2.js +++ b/src/v2.js @@ -2,7 +2,12 @@ var Promise = require("bluebird"); var pbkdf2 = require('pbkdf2'); var bigInt = require("big-integer"); -exports.generatePassword = generatePassword; +module.exports = { + generatePassword: generatePassword, + _calcEntropy: calcEntropy, + _getSetOfCharacters: getSetOfCharacters, + _renderPassword: renderPassword +}; function generatePassword(site, login, masterPassword, passwordProfile) { return calcEntropy(site, login, masterPassword, passwordProfile).then(function (entropy) { @@ -11,11 +16,6 @@ function generatePassword(site, login, masterPassword, passwordProfile) { }); } -exports._calcEntropy = calcEntropy; -exports._getSetOfCharacters = getSetOfCharacters; -exports._renderPassword = renderPassword; - - function calcEntropy(site, login, masterPassword, passwordProfile) { return new Promise(function (resolve, reject) { var salt = site + login + passwordProfile.counter.toString(16); diff --git a/tests/v2/api.tests.js b/tests/v2/api.tests.js index e8a59f1..7ac32d9 100644 --- a/tests/v2/api.tests.js +++ b/tests/v2/api.tests.js @@ -3,6 +3,7 @@ var assert = chai.assert; describe('LessPass v2', function () { describe('API', function () { it('render password', function () { + this.timeout(10000); var site = 'example.org'; var login = 'contact@example.org'; var masterPassword = 'password'; @@ -20,6 +21,7 @@ describe('LessPass v2', function () { }); }); it('render password only digit', function () { + this.timeout(10000); var site = 'example.org'; var login = 'contact@example.org'; var masterPassword = 'password'; @@ -37,6 +39,7 @@ describe('LessPass v2', function () { }); }); it('render password no number', function () { + this.timeout(10000); var site = 'example.org'; var login = 'contact@example.org'; var masterPassword = 'password';