@@ -1039,14 +1039,31 @@ var bigInt = (function (undefined) { | |||
} | |||
var parseBase = function (text, base) { | |||
var length = text.length; | |||
var i; | |||
var absBase = Math.abs(base); | |||
for(var i = 0; i < length; i++) { | |||
var c = text[i].toLowerCase(); | |||
if(c === "-") continue; | |||
if(/[a-z0-9]/.test(c)) { | |||
if(/[0-9]/.test(c) && +c >= absBase) { | |||
if(c === "1" && absBase === 1) continue; | |||
throw new Error(c + " is not a valid digit in base " + base + "."); | |||
} else if(c.charCodeAt(0) - 87 >= absBase) { | |||
throw new Error(c + " is not a valid digit in base " + base + "."); | |||
} | |||
} | |||
} | |||
if (2 <= base && base <= 36) { | |||
if (length <= LOG_MAX_INT / Math.log(base)) { | |||
var result = parseInt(text, base); | |||
if(isNaN(result)) { | |||
throw new Error(c + " is not a valid digit in base " + 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(), | |||
@@ -1127,11 +1144,13 @@ var bigInt = (function (undefined) { | |||
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.toJSON = SmallInteger.prototype.toJSON = function() { return this.toString(); } | |||
BigInteger.prototype.valueOf = function () { | |||
return +this.toString(); | |||
@@ -1235,6 +1254,112 @@ if ( typeof define === "function" && define.amd ) { | |||
} | |||
},{}],2:[function(require,module,exports){ | |||
var consumeEntropy = require('./entropy').consumeEntropy; | |||
var _characterSubsets = { | |||
lowercase: "abcdefghijklmnopqrstuvwxyz", | |||
uppercase: "ABCDEFGHIJKLMNOPQRSTUVWXYZ", | |||
digits: "0123456789", | |||
symbols: "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~" | |||
}; | |||
function getSetOfCharacters(rules) { | |||
if (typeof(rules) === "undefined") { | |||
return ( | |||
_characterSubsets.lowercase + | |||
_characterSubsets.uppercase + | |||
_characterSubsets.digits + | |||
_characterSubsets.symbols | |||
); | |||
} | |||
var setOfChars = ""; | |||
rules.forEach(function(rule) { | |||
setOfChars += _characterSubsets[rule]; | |||
}); | |||
return setOfChars; | |||
} | |||
function getOneCharPerRule(entropy, rules) { | |||
var oneCharPerRules = ""; | |||
rules.forEach(function(rule) { | |||
var password = consumeEntropy("", entropy, _characterSubsets[rule], 1); | |||
oneCharPerRules += password.value; | |||
entropy = password.entropy; | |||
}); | |||
return {value: oneCharPerRules, entropy: entropy}; | |||
} | |||
function getRules(options) { | |||
return ["lowercase", "uppercase", "digits", "symbols"].filter(function(rule) { | |||
return options[rule]; | |||
}); | |||
} | |||
function insertStringPseudoRandomly(generatedPassword, entropy, string) { | |||
for (var i = 0; i < string.length; i++) { | |||
var longDivision = entropy.divmod(generatedPassword.length); | |||
generatedPassword = | |||
generatedPassword.slice(0, longDivision.remainder) + | |||
string[i] + | |||
generatedPassword.slice(longDivision.remainder); | |||
entropy = longDivision.quotient; | |||
} | |||
return generatedPassword; | |||
} | |||
module.exports = { | |||
getSetOfCharacters: getSetOfCharacters, | |||
getOneCharPerRule: getOneCharPerRule, | |||
insertStringPseudoRandomly: insertStringPseudoRandomly, | |||
getRules: getRules, | |||
characterSubsets: _characterSubsets | |||
}; | |||
},{"./entropy":3}],3:[function(require,module,exports){ | |||
function _consumeEntropy(generatedPassword, | |||
quotient, | |||
setOfCharacters, | |||
maxLength) { | |||
if (generatedPassword.length >= maxLength) { | |||
return {value: generatedPassword, entropy: quotient}; | |||
} | |||
var longDivision = quotient.divmod(setOfCharacters.length); | |||
generatedPassword += setOfCharacters[longDivision.remainder]; | |||
return _consumeEntropy( | |||
generatedPassword, | |||
longDivision.quotient, | |||
setOfCharacters, | |||
maxLength | |||
); | |||
} | |||
module.exports = { | |||
consumeEntropy: _consumeEntropy | |||
}; | |||
},{}],4:[function(require,module,exports){ | |||
var bigInt = require("big-integer"); | |||
var chars = require("./chars"); | |||
var consumeEntropy = require("./entropy").consumeEntropy; | |||
module.exports = function renderPassword(entropy, options) { | |||
var rules = chars.getRules(options); | |||
var setOfCharacters = chars.getSetOfCharacters(rules); | |||
var password = consumeEntropy( | |||
"", | |||
bigInt(entropy, 16), | |||
setOfCharacters, | |||
options.length - rules.length | |||
); | |||
var charactersToAdd = chars.getOneCharPerRule(password.entropy, rules); | |||
return chars.insertStringPseudoRandomly( | |||
password.value, | |||
charactersToAdd.entropy, | |||
charactersToAdd.value | |||
); | |||
}; | |||
},{"./chars":2,"./entropy":3,"big-integer":1}],5:[function(require,module,exports){ | |||
(function (global){ | |||
/** | |||
* lodash (Custom Build) <https://lodash.com/> | |||
@@ -3445,7 +3570,7 @@ function stubFalse() { | |||
module.exports = merge; | |||
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |||
},{}],3:[function(require,module,exports){ | |||
},{}],6:[function(require,module,exports){ | |||
(function () { | |||
'use strict'; | |||
@@ -3551,7 +3676,7 @@ window.Unibabel = { | |||
}()); | |||
},{}],4:[function(require,module,exports){ | |||
},{}],7:[function(require,module,exports){ | |||
(function () { | |||
'use strict'; | |||
@@ -3599,7 +3724,7 @@ window.Unibabel.bufferToHex = bufferToHex; | |||
}()); | |||
},{}],5:[function(require,module,exports){ | |||
},{}],8:[function(require,module,exports){ | |||
require("unibabel"); | |||
require("unibabel/unibabel.hex"); | |||
@@ -3632,11 +3757,11 @@ module.exports = function(digest, string, salt) { | |||
}); | |||
}; | |||
},{"unibabel":3,"unibabel/unibabel.hex":4}],6:[function(require,module,exports){ | |||
},{"unibabel":6,"unibabel/unibabel.hex":7}],9:[function(require,module,exports){ | |||
var hmac = require("./hmac"); | |||
var pbkdf2 = require("./pbkdf2"); | |||
var bigInt = require("big-integer"); | |||
var merge = require("lodash.merge"); | |||
var renderPassword = require("lesspass-render-password"); | |||
var defaultProfile = { | |||
site: '', | |||
@@ -3661,18 +3786,12 @@ module.exports = { | |||
generatePassword: generatePassword, | |||
createFingerprint: createFingerprint, | |||
isSupported: isSupported, | |||
_calcEntropy: calcEntropy, | |||
_consumeEntropy: consumeEntropy, | |||
_getSetOfCharacters: getSetOfCharacters, | |||
_getConfiguredRules: getConfiguredRules, | |||
_insertStringPseudoRandomly: insertStringPseudoRandomly, | |||
_getOneCharPerRule: getOneCharPerRule, | |||
_renderPassword: renderPassword | |||
_calcEntropy: _calcEntropy | |||
}; | |||
function generatePassword(profile, masterPassword) { | |||
var _profile = merge({}, defaultProfile, profile); | |||
return calcEntropy(_profile, masterPassword) | |||
return _calcEntropy(_profile, masterPassword) | |||
.then(function(entropy) { | |||
return renderPassword(entropy, _profile.options); | |||
}); | |||
@@ -3687,7 +3806,7 @@ function isSupported() { | |||
var simpleProfile = merge({}, defaultProfile, {crypto: {iterations: 1}}); | |||
return generatePassword(simpleProfile, 'LessPass') | |||
.then(function(generatedPassword) { | |||
return generatedPassword == "n'LTsjPA#3E$e*2'"; | |||
return generatedPassword === "n'LTsjPA#3E$e*2'"; | |||
}); | |||
} catch (e) { | |||
console.error(e); | |||
@@ -3695,7 +3814,7 @@ function isSupported() { | |||
} | |||
} | |||
function calcEntropy(profile, masterPassword) { | |||
function _calcEntropy(profile, masterPassword) { | |||
var salt = profile.site + profile.login + profile.options.counter.toString(16); | |||
return pbkdf2( | |||
masterPassword, | |||
@@ -3706,92 +3825,7 @@ function calcEntropy(profile, masterPassword) { | |||
); | |||
} | |||
var characterSubsets = { | |||
lowercase: "abcdefghijklmnopqrstuvwxyz", | |||
uppercase: "ABCDEFGHIJKLMNOPQRSTUVWXYZ", | |||
digits: "0123456789", | |||
symbols: "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~" | |||
}; | |||
function getSetOfCharacters(rules) { | |||
if (typeof rules === "undefined") { | |||
return ( | |||
characterSubsets.lowercase + | |||
characterSubsets.uppercase + | |||
characterSubsets.digits + | |||
characterSubsets.symbols | |||
); | |||
} | |||
var setOfChars = ""; | |||
rules.forEach(function(rule) { | |||
setOfChars += characterSubsets[rule]; | |||
}); | |||
return setOfChars; | |||
} | |||
function consumeEntropy(generatedPassword, | |||
quotient, | |||
setOfCharacters, | |||
maxLength) { | |||
if (generatedPassword.length >= maxLength) { | |||
return {value: generatedPassword, entropy: quotient}; | |||
} | |||
var longDivision = quotient.divmod(setOfCharacters.length); | |||
generatedPassword += setOfCharacters[longDivision.remainder]; | |||
return consumeEntropy( | |||
generatedPassword, | |||
longDivision.quotient, | |||
setOfCharacters, | |||
maxLength | |||
); | |||
} | |||
function insertStringPseudoRandomly(generatedPassword, entropy, string) { | |||
for (var i = 0; i < string.length; i++) { | |||
var longDivision = entropy.divmod(generatedPassword.length); | |||
generatedPassword = | |||
generatedPassword.slice(0, longDivision.remainder) + | |||
string[i] + | |||
generatedPassword.slice(longDivision.remainder); | |||
entropy = longDivision.quotient; | |||
} | |||
return generatedPassword; | |||
} | |||
function getOneCharPerRule(entropy, rules) { | |||
var oneCharPerRules = ""; | |||
rules.forEach(function(rule) { | |||
var password = consumeEntropy("", entropy, characterSubsets[rule], 1); | |||
oneCharPerRules += password.value; | |||
entropy = password.entropy; | |||
}); | |||
return {value: oneCharPerRules, entropy: entropy}; | |||
} | |||
function getConfiguredRules(passwordProfile) { | |||
return ["lowercase", "uppercase", "digits", "symbols"].filter(function(rule) { | |||
return passwordProfile[rule]; | |||
}); | |||
} | |||
function renderPassword(entropy, passwordProfile) { | |||
var rules = getConfiguredRules(passwordProfile); | |||
var setOfCharacters = getSetOfCharacters(rules); | |||
var password = consumeEntropy( | |||
"", | |||
bigInt(entropy, 16), | |||
setOfCharacters, | |||
passwordProfile.length - rules.length | |||
); | |||
var charactersToAdd = getOneCharPerRule(password.entropy, rules); | |||
return insertStringPseudoRandomly( | |||
password.value, | |||
charactersToAdd.entropy, | |||
charactersToAdd.value | |||
); | |||
} | |||
},{"./hmac":5,"./pbkdf2":7,"big-integer":1,"lodash.merge":2}],7:[function(require,module,exports){ | |||
},{"./hmac":8,"./pbkdf2":10,"lesspass-render-password":4,"lodash.merge":5}],10:[function(require,module,exports){ | |||
require("unibabel"); | |||
require("unibabel/unibabel.hex"); | |||
@@ -3835,5 +3869,5 @@ module.exports = function(password, salt, iterations, keylen, digest) { | |||
}); | |||
}; | |||
},{"unibabel":3,"unibabel/unibabel.hex":4}]},{},[6])(6) | |||
},{"unibabel":6,"unibabel/unibabel.hex":7}]},{},[9])(9) | |||
}); |
@@ -1,6 +1,6 @@ | |||
{ | |||
"name": "lesspass", | |||
"version": "8.0.0", | |||
"version": "8.0.1", | |||
"description": "LessPass node module used to generate LessPass passwords", | |||
"keywords": [ | |||
"crypto", | |||
@@ -22,7 +22,7 @@ | |||
"jsnext:main": "src/lesspass.js", | |||
"repository": "lesspass/core", | |||
"scripts": { | |||
"precommit": "npm test && lint-staged", | |||
"precommit": "npm run test:unit && lint-staged", | |||
"clean": "rm -rf dist && mkdir dist && npm prune", | |||
"build": "npm run clean && browserify --standalone LessPass src/lesspass.js > dist/lesspass.js && npm run minify", | |||
"minify": "uglifyjs --output dist/lesspass.min.js --compress --mangle -- dist/lesspass.js", | |||
@@ -31,20 +31,20 @@ | |||
"test:browser": "npm run build && karma start test/karma.conf.js" | |||
}, | |||
"dependencies": { | |||
"big-integer": "^1.6.22", | |||
"lesspass-render-password": "^0.1.0", | |||
"lodash.merge": "^4.6.0", | |||
"unibabel": "^2.1.4" | |||
"unibabel": "2.1.4" | |||
}, | |||
"devDependencies": { | |||
"browserify": "^14.3.0", | |||
"husky": "^0.13.3", | |||
"husky": "^0.14.3", | |||
"karma": "^1.6.0", | |||
"karma-browserify": "^5.1.1", | |||
"karma-chrome-launcher": "^2.0.0", | |||
"karma-firefox-launcher": "^1.0.1", | |||
"karma-mocha": "^1.3.0", | |||
"lint-staged": "^3.4.1", | |||
"mocha": "^3.3.0", | |||
"lint-staged": "^4.3.0", | |||
"mocha": "^4.0.1", | |||
"prettier": "^1.2.2", | |||
"uglify-js": "^3.0.1" | |||
}, | |||
@@ -1,11 +1,11 @@ | |||
var hmac = require("./hmac"); | |||
var pbkdf2 = require("./pbkdf2"); | |||
var bigInt = require("big-integer"); | |||
var merge = require("lodash.merge"); | |||
var renderPassword = require("lesspass-render-password"); | |||
var defaultProfile = { | |||
site: '', | |||
login: '', | |||
site: "", | |||
login: "", | |||
options: { | |||
uppercase: true, | |||
lowercase: true, | |||
@@ -15,7 +15,7 @@ var defaultProfile = { | |||
counter: 1 | |||
}, | |||
crypto: { | |||
method: 'pbkdf2', | |||
method: "pbkdf2", | |||
iterations: 100000, | |||
keylen: 32, | |||
digest: "sha256" | |||
@@ -26,21 +26,14 @@ module.exports = { | |||
generatePassword: generatePassword, | |||
createFingerprint: createFingerprint, | |||
isSupported: isSupported, | |||
_calcEntropy: calcEntropy, | |||
_consumeEntropy: consumeEntropy, | |||
_getSetOfCharacters: getSetOfCharacters, | |||
_getConfiguredRules: getConfiguredRules, | |||
_insertStringPseudoRandomly: insertStringPseudoRandomly, | |||
_getOneCharPerRule: getOneCharPerRule, | |||
_renderPassword: renderPassword | |||
_calcEntropy: _calcEntropy | |||
}; | |||
function generatePassword(profile, masterPassword) { | |||
var _profile = merge({}, defaultProfile, profile); | |||
return calcEntropy(_profile, masterPassword) | |||
.then(function(entropy) { | |||
return renderPassword(entropy, _profile.options); | |||
}); | |||
return _calcEntropy(_profile, masterPassword).then(function(entropy) { | |||
return renderPassword(entropy, _profile.options); | |||
}); | |||
} | |||
function createFingerprint(str) { | |||
@@ -49,19 +42,23 @@ function createFingerprint(str) { | |||
function isSupported() { | |||
try { | |||
var simpleProfile = merge({}, defaultProfile, {crypto: {iterations: 1}}); | |||
return generatePassword(simpleProfile, 'LessPass') | |||
.then(function(generatedPassword) { | |||
return generatedPassword == "n'LTsjPA#3E$e*2'"; | |||
}); | |||
var simpleProfile = merge({}, defaultProfile, { | |||
crypto: { iterations: 1 } | |||
}); | |||
return generatePassword(simpleProfile, "LessPass").then(function( | |||
generatedPassword | |||
) { | |||
return generatedPassword === "n'LTsjPA#3E$e*2'"; | |||
}); | |||
} catch (e) { | |||
console.error(e); | |||
return Promise.resolve(false); | |||
} | |||
} | |||
function calcEntropy(profile, masterPassword) { | |||
var salt = profile.site + profile.login + profile.options.counter.toString(16); | |||
function _calcEntropy(profile, masterPassword) { | |||
var salt = | |||
profile.site + profile.login + profile.options.counter.toString(16); | |||
return pbkdf2( | |||
masterPassword, | |||
salt, | |||
@@ -70,88 +67,3 @@ function calcEntropy(profile, masterPassword) { | |||
profile.crypto.digest | |||
); | |||
} | |||
var characterSubsets = { | |||
lowercase: "abcdefghijklmnopqrstuvwxyz", | |||
uppercase: "ABCDEFGHIJKLMNOPQRSTUVWXYZ", | |||
digits: "0123456789", | |||
symbols: "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~" | |||
}; | |||
function getSetOfCharacters(rules) { | |||
if (typeof rules === "undefined") { | |||
return ( | |||
characterSubsets.lowercase + | |||
characterSubsets.uppercase + | |||
characterSubsets.digits + | |||
characterSubsets.symbols | |||
); | |||
} | |||
var setOfChars = ""; | |||
rules.forEach(function(rule) { | |||
setOfChars += characterSubsets[rule]; | |||
}); | |||
return setOfChars; | |||
} | |||
function consumeEntropy(generatedPassword, | |||
quotient, | |||
setOfCharacters, | |||
maxLength) { | |||
if (generatedPassword.length >= maxLength) { | |||
return {value: generatedPassword, entropy: quotient}; | |||
} | |||
var longDivision = quotient.divmod(setOfCharacters.length); | |||
generatedPassword += setOfCharacters[longDivision.remainder]; | |||
return consumeEntropy( | |||
generatedPassword, | |||
longDivision.quotient, | |||
setOfCharacters, | |||
maxLength | |||
); | |||
} | |||
function insertStringPseudoRandomly(generatedPassword, entropy, string) { | |||
for (var i = 0; i < string.length; i++) { | |||
var longDivision = entropy.divmod(generatedPassword.length); | |||
generatedPassword = | |||
generatedPassword.slice(0, longDivision.remainder) + | |||
string[i] + | |||
generatedPassword.slice(longDivision.remainder); | |||
entropy = longDivision.quotient; | |||
} | |||
return generatedPassword; | |||
} | |||
function getOneCharPerRule(entropy, rules) { | |||
var oneCharPerRules = ""; | |||
rules.forEach(function(rule) { | |||
var password = consumeEntropy("", entropy, characterSubsets[rule], 1); | |||
oneCharPerRules += password.value; | |||
entropy = password.entropy; | |||
}); | |||
return {value: oneCharPerRules, entropy: entropy}; | |||
} | |||
function getConfiguredRules(passwordProfile) { | |||
return ["lowercase", "uppercase", "digits", "symbols"].filter(function(rule) { | |||
return passwordProfile[rule]; | |||
}); | |||
} | |||
function renderPassword(entropy, passwordProfile) { | |||
var rules = getConfiguredRules(passwordProfile); | |||
var setOfCharacters = getSetOfCharacters(rules); | |||
var password = consumeEntropy( | |||
"", | |||
bigInt(entropy, 16), | |||
setOfCharacters, | |||
passwordProfile.length - rules.length | |||
); | |||
var charactersToAdd = getOneCharPerRule(password.entropy, rules); | |||
return insertStringPseudoRandomly( | |||
password.value, | |||
charactersToAdd.entropy, | |||
charactersToAdd.value | |||
); | |||
} |
@@ -1,6 +1,5 @@ | |||
var assert = require("assert"); | |||
var LessPass = require("../src/lesspass"); | |||
var bigInt = require("big-integer"); | |||
describe("entropy", function() { | |||
it("calc entropy pbkdf2 with default params (100000 iterations, 32 bytes length, sha256 digest)", function() { | |||
@@ -11,20 +10,21 @@ describe("entropy", function() { | |||
counter: 1 | |||
}, | |||
crypto: { | |||
method: 'pbkdf2', | |||
method: "pbkdf2", | |||
iterations: 100000, | |||
keylen: 32, | |||
digest: "sha256" | |||
} | |||
}; | |||
const masterPassword = "password"; | |||
return LessPass._calcEntropy(profile, masterPassword) | |||
.then(function(entropy) { | |||
assert.equal( | |||
"dc33d431bce2b01182c613382483ccdb0e2f66482cbba5e9d07dab34acc7eb1e", | |||
entropy | |||
); | |||
}); | |||
return LessPass._calcEntropy(profile, masterPassword).then(function( | |||
entropy | |||
) { | |||
assert.equal( | |||
"dc33d431bce2b01182c613382483ccdb0e2f66482cbba5e9d07dab34acc7eb1e", | |||
entropy | |||
); | |||
}); | |||
}); | |||
it("calc entropy with different options (8192 iterations, 16 bytes length, sha512 digest)", function() { | |||
const profile = { | |||
@@ -34,17 +34,18 @@ describe("entropy", function() { | |||
counter: 1 | |||
}, | |||
crypto: { | |||
method: 'pbkdf2', | |||
method: "pbkdf2", | |||
iterations: 8192, | |||
keylen: 16, | |||
digest: "sha512" | |||
} | |||
}; | |||
const masterPassword = "password"; | |||
return LessPass._calcEntropy(profile, masterPassword) | |||
.then(function(entropy) { | |||
assert.equal("fff211c16a4e776b3574c6a5c91fd252", entropy); | |||
}); | |||
return LessPass._calcEntropy(profile, masterPassword).then(function( | |||
entropy | |||
) { | |||
assert.equal("fff211c16a4e776b3574c6a5c91fd252", entropy); | |||
}); | |||
}); | |||
it("calc entropy different if counter different 1", function() { | |||
const profile = { | |||
@@ -54,7 +55,7 @@ describe("entropy", function() { | |||
counter: 1 | |||
}, | |||
crypto: { | |||
method: 'pbkdf2', | |||
method: "pbkdf2", | |||
iterations: 100000, | |||
keylen: 32, | |||
digest: "sha256" | |||
@@ -67,7 +68,7 @@ describe("entropy", function() { | |||
counter: 2 | |||
}, | |||
crypto: { | |||
method: 'pbkdf2', | |||
method: "pbkdf2", | |||
iterations: 100000, | |||
keylen: 32, | |||
digest: "sha256" | |||
@@ -75,15 +76,10 @@ describe("entropy", function() { | |||
}; | |||
const promises = [ | |||
LessPass._calcEntropy(profile, "password"), | |||
LessPass._calcEntropy(profile2, "password"), | |||
LessPass._calcEntropy(profile2, "password") | |||
]; | |||
Promise.all(promises).then(values => { | |||
assert.notEqual(values[0], values[1]); | |||
}); | |||
}); | |||
it("consume entropy", function() { | |||
var password = LessPass._consumeEntropy("", bigInt(4 * 4 + 2), "abcd", 2); | |||
assert.equal("ca", password.value); | |||
assert.equal(1, password.entropy); | |||
}); | |||
}); |
@@ -1,99 +0,0 @@ | |||
const assert = require("assert"); | |||
const LessPass = require("../src/lesspass"); | |||
const bigInt = require("big-integer"); | |||
describe("LessPass LessPass", function() { | |||
const defaultPasswordProfile = { | |||
length: 16, | |||
lowercase: true, | |||
uppercase: true, | |||
digits: true, | |||
symbols: true | |||
}; | |||
it("render password use remainder of long division beetween entropy and set of chars length as an index", function() { | |||
const entropy = | |||
"dc33d431bce2b01182c613382483ccdb0e2f66482cbba5e9d07dab34acc7eb1e"; | |||
assert.equal("W", LessPass._renderPassword(entropy, defaultPasswordProfile)[0]); | |||
}); | |||
it("render password use quotient as second entropy recursively", function() { | |||
const entropy = | |||
"dc33d431bce2b01182c613382483ccdb0e2f66482cbba5e9d07dab34acc7eb1e"; | |||
assert.equal("H", LessPass._renderPassword(entropy, defaultPasswordProfile)[1]); | |||
}); | |||
it("render password has default length of 16", function() { | |||
const entropy = | |||
"dc33d431bce2b01182c613382483ccdb0e2f66482cbba5e9d07dab34acc7eb1e"; | |||
assert.equal( | |||
16, | |||
LessPass._renderPassword(entropy, defaultPasswordProfile).length | |||
); | |||
}); | |||
it("render password can specify length", function() { | |||
const entropy = | |||
"dc33d431bce2b01182c613382483ccdb0e2f66482cbba5e9d07dab34acc7eb1e"; | |||
const passwordProfile = { | |||
length: 20, | |||
lowercase: true, | |||
uppercase: true, | |||
digits: true, | |||
symbols: true | |||
}; | |||
assert.equal(20, LessPass._renderPassword(entropy, passwordProfile).length); | |||
}); | |||
it("include one char per set of characters", function() { | |||
const password = LessPass._insertStringPseudoRandomly( | |||
"123456", | |||
bigInt(7 * 6 + 2), | |||
"uT" | |||
); | |||
assert.equal("T12u3456", password); | |||
}); | |||
it("render password return at least one char in every characters set", function() { | |||
const entropy = | |||
"dc33d431bce2b01182c613382483ccdb0e2f66482cbba5e9d07dab34acc7eb1e"; | |||
const passwordProfile = { | |||
length: 6, | |||
lowercase: true, | |||
uppercase: true, | |||
digits: true, | |||
symbols: true | |||
}; | |||
const generatedPassword = LessPass._renderPassword(entropy, passwordProfile); | |||
let passwordLength = generatedPassword.length; | |||
let lowercaseOk = false; | |||
let uppercaseOk = false; | |||
let digitsOk = false; | |||
let symbolsOk = false; | |||
while (passwordLength--) { | |||
if ( | |||
"abcdefghijklmnopqrstuvwxyz".indexOf( | |||
generatedPassword[passwordLength] | |||
) !== -1 | |||
) { | |||
lowercaseOk = true; | |||
} | |||
if ( | |||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ".indexOf( | |||
generatedPassword[passwordLength] | |||
) !== -1 | |||
) { | |||
uppercaseOk = true; | |||
} | |||
if ("0123456789".indexOf(generatedPassword[passwordLength]) !== -1) { | |||
digitsOk = true; | |||
} | |||
if ( | |||
"!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~".indexOf( | |||
generatedPassword[passwordLength] | |||
) !== -1 | |||
) { | |||
symbolsOk = true; | |||
} | |||
} | |||
assert.equal(6, generatedPassword.length); | |||
assert( | |||
lowercaseOk && uppercaseOk && digitsOk && symbolsOk, | |||
"there is no at least one char in every characters set" | |||
); | |||
}); | |||
}); |
@@ -1,78 +0,0 @@ | |||
var assert = require("assert"); | |||
var LessPass = require("../src/lesspass"); | |||
var bigInt = require("big-integer"); | |||
describe("set of characters", function() { | |||
it("get default set of characters", function() { | |||
var setOfCharacters = LessPass._getSetOfCharacters(); | |||
assert.equal( | |||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", | |||
setOfCharacters | |||
); | |||
assert.equal(26 * 2 + 10 + 32, setOfCharacters.length); | |||
}); | |||
it("get default set of characters concat rules in order", function() { | |||
var setOfCharacters = LessPass._getSetOfCharacters([ | |||
"lowercase", | |||
"uppercase", | |||
"digits" | |||
]); | |||
assert.equal( | |||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", | |||
setOfCharacters | |||
); | |||
assert.equal(26 * 2 + 10, setOfCharacters.length); | |||
}); | |||
it("get set of characters only lowercase", function() { | |||
var setOfCharacters = LessPass._getSetOfCharacters(["lowercase"]); | |||
assert.equal("abcdefghijklmnopqrstuvwxyz", setOfCharacters); | |||
assert.equal(26, setOfCharacters.length); | |||
}); | |||
it("get set of characters only uppercase", function() { | |||
var setOfCharacters = LessPass._getSetOfCharacters(["uppercase"]); | |||
assert.equal("ABCDEFGHIJKLMNOPQRSTUVWXYZ", setOfCharacters); | |||
assert.equal(26, setOfCharacters.length); | |||
}); | |||
it("get set of characters only digits", function() { | |||
var setOfCharacters = LessPass._getSetOfCharacters(["digits"]); | |||
assert.equal("0123456789", setOfCharacters); | |||
assert.equal(10, setOfCharacters.length); | |||
}); | |||
it("get set of characters only symbols", function() { | |||
var setOfCharacters = LessPass._getSetOfCharacters(["symbols"]); | |||
assert.equal("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", setOfCharacters); | |||
assert.equal(32, setOfCharacters.length); | |||
}); | |||
it("generate one char per rules", function() { | |||
var oneCharPerSetOfCharacters = LessPass._getOneCharPerRule(bigInt(26 * 26), [ | |||
"lowercase", | |||
"uppercase" | |||
]); | |||
assert.equal("aA", oneCharPerSetOfCharacters.value); | |||
assert.equal(2, oneCharPerSetOfCharacters.value.length); | |||
assert.equal(1, oneCharPerSetOfCharacters.entropy); | |||
}); | |||
it("configured rules", function() { | |||
assert.deepEqual( | |||
["uppercase"], | |||
LessPass._getConfiguredRules({ uppercase: true }) | |||
); | |||
assert.deepEqual( | |||
["lowercase", "uppercase"], | |||
LessPass._getConfiguredRules({ uppercase: true, lowercase: true }) | |||
); | |||
assert.deepEqual( | |||
["lowercase"], | |||
LessPass._getConfiguredRules({ lowercase: true, symbols: false }) | |||
); | |||
assert.deepEqual( | |||
["lowercase", "uppercase", "digits", "symbols"], | |||
LessPass._getConfiguredRules({ | |||
lowercase: true, | |||
uppercase: true, | |||
symbols: true, | |||
digits: true | |||
}) | |||
); | |||
}); | |||
}); |