@@ -32,8 +32,7 @@ | |||||
}, | }, | ||||
"dependencies": { | "dependencies": { | ||||
"big-integer": "^1.6.22", | "big-integer": "^1.6.22", | ||||
"es6-promise": "^4.1.0", | |||||
"lodash.assign": "^4.2.0", | |||||
"lodash.merge": "^4.6.0", | |||||
"unibabel": "^2.1.4" | "unibabel": "^2.1.4" | ||||
}, | }, | ||||
"devDependencies": { | "devDependencies": { | ||||
@@ -1,5 +1,4 @@ | |||||
var crypto = require("crypto"); | var crypto = require("crypto"); | ||||
var Promise = require("es6-promise").Promise; | |||||
module.exports = function(digest, string, salt) { | module.exports = function(digest, string, salt) { | ||||
return new Promise(function(resolve) { | return new Promise(function(resolve) { | ||||
@@ -1,15 +1,157 @@ | |||||
var v1 = require("./v1"); | |||||
var v2 = require("./v2"); | |||||
var hmac = require("./hmac"); | var hmac = require("./hmac"); | ||||
var pbkdf2 = require("./pbkdf2"); | |||||
var bigInt = require("big-integer"); | |||||
var merge = require("lodash.merge"); | |||||
module.exports = { | |||||
generatePassword: function(site, login, masterPassword, options) { | |||||
if (typeof options !== "undefined" && options.version === 1) { | |||||
return v1.generatePassword(site, login, masterPassword, options); | |||||
} | |||||
return v2.generatePassword(site, login, masterPassword, options); | |||||
var defaultProfile = { | |||||
site: '', | |||||
login: '', | |||||
options: { | |||||
uppercase: true, | |||||
lowercase: true, | |||||
digits: true, | |||||
symbols: true, | |||||
length: 16, | |||||
counter: 1 | |||||
}, | }, | ||||
createFingerprint: function(str) { | |||||
return hmac("sha256", str); | |||||
crypto: { | |||||
method: 'pbkdf2', | |||||
iterations: 100000, | |||||
keylen: 32, | |||||
digest: "sha256" | |||||
} | |||||
}; | |||||
module.exports = { | |||||
generatePassword: generatePassword, | |||||
createFingerprint: createFingerprint, | |||||
isSupported: isSupported, | |||||
_calcEntropy: calcEntropy, | |||||
_consumeEntropy: consumeEntropy, | |||||
_getSetOfCharacters: getSetOfCharacters, | |||||
_getConfiguredRules: getConfiguredRules, | |||||
_insertStringPseudoRandomly: insertStringPseudoRandomly, | |||||
_getOneCharPerRule: getOneCharPerRule, | |||||
_renderPassword: renderPassword | |||||
}; | |||||
function generatePassword(profile, masterPassword) { | |||||
var _profile = merge({}, defaultProfile, profile); | |||||
return calcEntropy(_profile, masterPassword) | |||||
.then(function(entropy) { | |||||
return renderPassword(entropy, _profile.options); | |||||
}); | |||||
} | |||||
function createFingerprint(str) { | |||||
return hmac("sha256", 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'"; | |||||
}); | |||||
} catch (e) { | |||||
console.error(e); | |||||
return Promise.resolve(false); | |||||
} | } | ||||
} | |||||
function calcEntropy(profile, masterPassword) { | |||||
var salt = profile.site + profile.login + profile.options.counter.toString(16); | |||||
return pbkdf2( | |||||
masterPassword, | |||||
salt, | |||||
profile.crypto.iterations, | |||||
profile.crypto.keylen, | |||||
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,5 +1,4 @@ | |||||
const crypto = require("crypto"); | const crypto = require("crypto"); | ||||
var Promise = require("es6-promise").Promise; | |||||
module.exports = function(password, salt, iterations, keylen, digest) { | module.exports = function(password, salt, iterations, keylen, digest) { | ||||
return new Promise(function(resolve, reject) { | return new Promise(function(resolve, reject) { | ||||
@@ -1,128 +0,0 @@ | |||||
var pbkdf2 = require("./pbkdf2"); | |||||
var assign = require("lodash.assign"); | |||||
var hmac = require("./hmac"); | |||||
module.exports = { | |||||
generatePassword: generatePassword, | |||||
_renderPassword: renderPassword, | |||||
_createHmac: createHmac, | |||||
_deriveEncryptedLogin: deriveEncryptedLogin, | |||||
_getPasswordTemplate: getPasswordTemplate, | |||||
_prettyPrint: prettyPrint, | |||||
_string2charCodes: string2charCodes, | |||||
_getCharType: getCharType, | |||||
_getPasswordChar: getPasswordChar | |||||
}; | |||||
var defaultOptions = { | |||||
version: 1, | |||||
lowercase: true, | |||||
numbers: true, | |||||
uppercase: true, | |||||
symbols: true, | |||||
keylen: 32, | |||||
digest: "sha256", | |||||
length: 12, | |||||
counter: 1, | |||||
iterations: 8192 | |||||
}; | |||||
function generatePassword(site, login, masterPassword, options) { | |||||
var _options = assign({}, defaultOptions, options); | |||||
return pbkdf2( | |||||
masterPassword, | |||||
login, | |||||
_options.iterations, | |||||
_options.keylen, | |||||
"sha256" | |||||
).then(function(encryptedLogin) { | |||||
return renderPassword(encryptedLogin, site, _options).then(function( | |||||
generatedPassword | |||||
) { | |||||
return generatedPassword; | |||||
}); | |||||
}); | |||||
} | |||||
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(hmac("sha256", encryptedLogin, salt)); | |||||
}); | |||||
} | |||||
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]; | |||||
} |
@@ -1,136 +0,0 @@ | |||||
var pbkdf2 = require("./pbkdf2"); | |||||
var bigInt = require("big-integer"); | |||||
var assign = require("lodash.assign"); | |||||
module.exports = { | |||||
generatePassword: generatePassword, | |||||
_calcEntropy: calcEntropy, | |||||
_consumeEntropy: consumeEntropy, | |||||
_getSetOfCharacters: getSetOfCharacters, | |||||
_getConfiguredRules: getConfiguredRules, | |||||
_insertStringPseudoRandomly: insertStringPseudoRandomly, | |||||
_getOneCharPerRule: getOneCharPerRule, | |||||
_renderPassword: renderPassword | |||||
}; | |||||
var defaultOptions = { | |||||
version: 2, | |||||
lowercase: true, | |||||
numbers: true, | |||||
uppercase: true, | |||||
symbols: true, | |||||
keylen: 32, | |||||
digest: "sha256", | |||||
length: 16, | |||||
counter: 1, | |||||
iterations: 100000 | |||||
}; | |||||
function generatePassword(site, login, masterPassword, options) { | |||||
var _options = assign({}, defaultOptions, options); | |||||
return calcEntropy(site, login, masterPassword, _options).then(function( | |||||
entropy | |||||
) { | |||||
return renderPassword(entropy, _options); | |||||
}); | |||||
} | |||||
function calcEntropy(site, login, masterPassword, passwordProfile) { | |||||
var salt = site + login + passwordProfile.counter.toString(16); | |||||
return pbkdf2( | |||||
masterPassword, | |||||
salt, | |||||
passwordProfile.iterations, | |||||
passwordProfile.keylen, | |||||
passwordProfile.digest | |||||
); | |||||
} | |||||
var characterSubsets = { | |||||
lowercase: "abcdefghijklmnopqrstuvwxyz", | |||||
uppercase: "ABCDEFGHIJKLMNOPQRSTUVWXYZ", | |||||
numbers: "0123456789", | |||||
symbols: "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~" | |||||
}; | |||||
function getSetOfCharacters(rules) { | |||||
if (typeof rules === "undefined") { | |||||
return ( | |||||
characterSubsets.lowercase + | |||||
characterSubsets.uppercase + | |||||
characterSubsets.numbers + | |||||
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", "numbers", "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,388 +1,98 @@ | |||||
var assert = require("assert"); | var assert = require("assert"); | ||||
var LessPass = require("../src/lesspass"); | var LessPass = require("../src/lesspass"); | ||||
describe("api", function() { | |||||
describe("v1", function() { | |||||
it("generatedPassword", function() { | |||||
var site = "example.org"; | |||||
var login = "contact@example.org"; | |||||
var masterPassword = "password"; | |||||
var options = { | |||||
describe("api", () => { | |||||
it("generatePassword", () => { | |||||
const profile = { | |||||
site: "example.org", | |||||
login: "contact@example.org", | |||||
options: { | |||||
lowercase: true, | lowercase: true, | ||||
uppercase: true, | uppercase: true, | ||||
numbers: true, | |||||
symbols: true, | |||||
length: 12, | |||||
counter: 1, | |||||
version: 1 | |||||
}; | |||||
return LessPass.generatePassword( | |||||
site, | |||||
login, | |||||
masterPassword, | |||||
options | |||||
).then(function(generatedPassword) { | |||||
assert.equal("izIS5@ozYM2?", generatedPassword); | |||||
}); | |||||
}); | |||||
it("generatedPassword", function() { | |||||
var site = "lesspass.com"; | |||||
var login = "contact@lesspass.com"; | |||||
var masterPassword = "password"; | |||||
var options = { | |||||
lowercase: true, | |||||
uppercase: true, | |||||
numbers: true, | |||||
symbols: true, | |||||
length: 12, | |||||
counter: 1, | |||||
version: 1 | |||||
}; | |||||
return LessPass.generatePassword( | |||||
site, | |||||
login, | |||||
masterPassword, | |||||
options | |||||
).then(function(generatedPassword) { | |||||
assert.equal("azYS7,olOL2]", generatedPassword); | |||||
}); | |||||
}); | |||||
it("generatedPassword", function() { | |||||
var site = "lesspass.com"; | |||||
var login = "contact@lesspass.com"; | |||||
var masterPassword = "password"; | |||||
var options = { | |||||
lowercase: true, | |||||
uppercase: true, | |||||
numbers: true, | |||||
symbols: true, | |||||
length: 14, | |||||
counter: 1, | |||||
version: 1 | |||||
}; | |||||
return LessPass.generatePassword( | |||||
site, | |||||
login, | |||||
masterPassword, | |||||
options | |||||
).then(function(generatedPassword) { | |||||
assert.equal("azYS7,olOL2]iz", generatedPassword); | |||||
}); | |||||
}); | |||||
it("generatedPassword", function() { | |||||
var site = "lesspass.com"; | |||||
var login = "contact@lesspass.com"; | |||||
var masterPassword = "password"; | |||||
var options = { | |||||
lowercase: true, | |||||
uppercase: false, | |||||
numbers: false, | |||||
symbols: false, | |||||
length: 12, | |||||
counter: 1, | |||||
version: 1 | |||||
}; | |||||
return LessPass.generatePassword( | |||||
site, | |||||
login, | |||||
masterPassword, | |||||
options | |||||
).then(function(generatedPassword) { | |||||
assert.equal("azyseqololat", generatedPassword); | |||||
}); | |||||
}); | |||||
it("generatedPassword", function() { | |||||
var site = "lesspass.com"; | |||||
var login = "contact@lesspass.com"; | |||||
var masterPassword = "password"; | |||||
var options = { | |||||
lowercase: false, | |||||
uppercase: true, | |||||
numbers: true, | |||||
symbols: true, | |||||
length: 12, | |||||
counter: 1, | |||||
version: 1 | |||||
}; | |||||
return LessPass.generatePassword( | |||||
site, | |||||
login, | |||||
masterPassword, | |||||
options | |||||
).then(function(generatedPassword) { | |||||
assert.equal("AZ3[EQ7@OL2]", generatedPassword); | |||||
}); | |||||
}); | |||||
it("generatedPassword", function() { | |||||
var site = "lesspass.com"; | |||||
var login = "contact@lesspass.com"; | |||||
var masterPassword = "password"; | |||||
var options = { | |||||
lowercase: false, | |||||
uppercase: false, | |||||
numbers: true, | |||||
symbols: true, | |||||
length: 12, | |||||
counter: 1, | |||||
version: 1 | |||||
}; | |||||
return LessPass.generatePassword( | |||||
site, | |||||
login, | |||||
masterPassword, | |||||
options | |||||
).then(function(generatedPassword) { | |||||
assert.equal("4?3[7,7@7@2]", generatedPassword); | |||||
}); | |||||
}); | |||||
it("generatedPassword", function() { | |||||
var site = "lesspass.com"; | |||||
var login = "contact@lesspass.com"; | |||||
var masterPassword = "password"; | |||||
var options = { | |||||
lowercase: false, | |||||
uppercase: false, | |||||
numbers: false, | |||||
symbols: true, | |||||
length: 12, | |||||
counter: 1, | |||||
version: 1 | |||||
}; | |||||
return LessPass.generatePassword( | |||||
site, | |||||
login, | |||||
masterPassword, | |||||
options | |||||
).then(function(generatedPassword) { | |||||
assert.equal("[?=[&,:@:@[]", generatedPassword); | |||||
}); | |||||
}); | |||||
it("generatedPassword", function() { | |||||
var site = "lesspass.com"; | |||||
var login = "contact@lesspass.com"; | |||||
var masterPassword = "password"; | |||||
var options = { | |||||
lowercase: true, | |||||
uppercase: true, | |||||
numbers: true, | |||||
symbols: false, | |||||
length: 12, | |||||
counter: 1, | |||||
version: 1 | |||||
}; | |||||
return LessPass.generatePassword( | |||||
site, | |||||
login, | |||||
masterPassword, | |||||
options | |||||
).then(function(generatedPassword) { | |||||
assert.equal("azYS7uwAW8at", generatedPassword); | |||||
}); | |||||
}); | |||||
it("generatedPassword", function() { | |||||
var site = "lesspass.com"; | |||||
var login = "contact@lesspass.com"; | |||||
var masterPassword = "password"; | |||||
var options = { | |||||
lowercase: true, | |||||
uppercase: true, | |||||
numbers: false, | |||||
symbols: false, | |||||
length: 12, | |||||
counter: 1, | |||||
version: 1 | |||||
}; | |||||
return LessPass.generatePassword( | |||||
site, | |||||
login, | |||||
masterPassword, | |||||
options | |||||
).then(function(generatedPassword) { | |||||
assert.equal("azYSeqOLolAT", generatedPassword); | |||||
}); | |||||
}); | |||||
it("generatedPassword", function() { | |||||
var site = "lesspass.com"; | |||||
var login = "contact@lesspass.com"; | |||||
var masterPassword = "password"; | |||||
var options = { | |||||
lowercase: true, | |||||
uppercase: true, | |||||
numbers: true, | |||||
symbols: true, | |||||
length: 12, | |||||
counter: 2, | |||||
version: 1 | |||||
}; | |||||
return LessPass.generatePassword( | |||||
site, | |||||
login, | |||||
masterPassword, | |||||
options | |||||
).then(function(generatedPassword) { | |||||
assert.equal("obYT2=olOV9=", generatedPassword); | |||||
}); | |||||
}); | |||||
it("generatedPassword", function() { | |||||
var site = "lesspass.com"; | |||||
var login = "lesspass"; | |||||
var masterPassword = "password"; | |||||
var options = { | |||||
lowercase: true, | |||||
uppercase: true, | |||||
numbers: true, | |||||
symbols: true, | |||||
length: 12, | |||||
counter: 1, | |||||
version: 1 | |||||
}; | |||||
return LessPass.generatePassword( | |||||
site, | |||||
login, | |||||
masterPassword, | |||||
options | |||||
).then(function(generatedPassword) { | |||||
assert.equal("erOC1%imIW3,", generatedPassword); | |||||
}); | |||||
}); | |||||
it("generatedPassword", function() { | |||||
var site = "lesspass.com"; | |||||
var login = "contact@lesspass.com"; | |||||
var masterPassword = "password2"; | |||||
var options = { | |||||
lowercase: true, | |||||
uppercase: true, | |||||
numbers: true, | |||||
symbols: true, | |||||
length: 12, | |||||
counter: 1, | |||||
version: 1 | |||||
}; | |||||
return LessPass.generatePassword( | |||||
site, | |||||
login, | |||||
masterPassword, | |||||
options | |||||
).then(function(generatedPassword) { | |||||
assert.equal("uvUM5_ucUP5=", generatedPassword); | |||||
}); | |||||
}); | |||||
}); | |||||
describe("v2", function() { | |||||
it("generatedPassword", function() { | |||||
this.timeout(10000); | |||||
var site = "example.org"; | |||||
var login = "contact@example.org"; | |||||
var masterPassword = "password"; | |||||
var options = { | |||||
lowercase: true, | |||||
uppercase: true, | |||||
numbers: true, | |||||
digits: true, | |||||
symbols: true, | symbols: true, | ||||
length: 16, | length: 16, | ||||
counter: 1, | |||||
version: 2 | |||||
}; | |||||
return LessPass.generatePassword( | |||||
site, | |||||
login, | |||||
masterPassword, | |||||
options | |||||
).then(function(generatedPassword) { | |||||
counter: 1 | |||||
} | |||||
}; | |||||
const masterPassword = "password"; | |||||
return LessPass | |||||
.generatePassword(profile, masterPassword) | |||||
.then(generatedPassword => { | |||||
assert.equal("WHLpUL)e00[iHR+w", generatedPassword); | assert.equal("WHLpUL)e00[iHR+w", generatedPassword); | ||||
}); | }); | ||||
}); | |||||
it("generatePassword default options", () => { | |||||
const profile = { | |||||
site: "example.org", | |||||
login: "contact@example.org" | |||||
}; | |||||
const masterPassword = "password"; | |||||
return LessPass.generatePassword(profile, masterPassword).then(generatedPassword => { | |||||
assert.equal("WHLpUL)e00[iHR+w", generatedPassword); | |||||
}); | }); | ||||
it("generatedPassword", function() { | |||||
this.timeout(10000); | |||||
var site = "example.org"; | |||||
var login = "contact@example.org"; | |||||
var masterPassword = "password"; | |||||
var options = { | |||||
}); | |||||
it("generatedPassword different options", () => { | |||||
const profile = { | |||||
site: "example.org", | |||||
login: "contact@example.org", | |||||
options: { | |||||
lowercase: true, | lowercase: true, | ||||
uppercase: true, | uppercase: true, | ||||
numbers: true, | |||||
digits: true, | |||||
symbols: false, | symbols: false, | ||||
length: 14, | length: 14, | ||||
counter: 2, | |||||
version: 2 | |||||
}; | |||||
return LessPass.generatePassword( | |||||
site, | |||||
login, | |||||
masterPassword, | |||||
options | |||||
).then(function(generatedPassword) { | |||||
assert.equal("MBAsB7b1Prt8Sl", generatedPassword); | |||||
}); | |||||
counter: 2 | |||||
} | |||||
}; | |||||
const masterPassword = "password"; | |||||
return LessPass.generatePassword(profile, masterPassword).then(generatedPassword => { | |||||
assert.equal("MBAsB7b1Prt8Sl", generatedPassword); | |||||
assert.equal(14, generatedPassword.length); | |||||
}); | }); | ||||
it("generatedPassword", function() { | |||||
this.timeout(10000); | |||||
var site = "example.org"; | |||||
var login = "contact@example.org"; | |||||
var masterPassword = "password"; | |||||
var options = { | |||||
}); | |||||
it("generatedPassword only digits", () => { | |||||
const profile = { | |||||
site: "example.org", | |||||
login: "contact@example.org", | |||||
options: { | |||||
lowercase: false, | lowercase: false, | ||||
uppercase: false, | uppercase: false, | ||||
numbers: true, | |||||
digits: true, | |||||
symbols: false, | symbols: false, | ||||
length: 6, | length: 6, | ||||
counter: 3, | |||||
version: 2 | |||||
}; | |||||
return LessPass.generatePassword( | |||||
site, | |||||
login, | |||||
masterPassword, | |||||
options | |||||
).then(function(generatedPassword) { | |||||
assert.equal("117843", generatedPassword); | |||||
}); | |||||
counter: 3 | |||||
} | |||||
}; | |||||
const masterPassword = "password"; | |||||
return LessPass.generatePassword(profile, masterPassword).then(generatedPassword => { | |||||
assert.equal("117843", generatedPassword); | |||||
}); | }); | ||||
it("generatedPassword", function() { | |||||
this.timeout(10000); | |||||
var site = "example.org"; | |||||
var login = "contact@example.org"; | |||||
var masterPassword = "password"; | |||||
var options = { | |||||
lowercase: true, | |||||
uppercase: true, | |||||
numbers: false, | |||||
symbols: true, | |||||
length: 14, | |||||
counter: 1, | |||||
version: 2 | |||||
}; | |||||
return LessPass.generatePassword( | |||||
site, | |||||
login, | |||||
masterPassword, | |||||
options | |||||
).then(function(generatedPassword) { | |||||
assert.equal("sB>{qF}wN%/-fm", generatedPassword); | |||||
}); | |||||
}); | |||||
it("generatedPassword no digit", () => { | |||||
const profile = { | |||||
site: "example.org", | |||||
login: "contact@example.org", | |||||
options: { | |||||
digits: false | |||||
} | |||||
}; | |||||
const masterPassword = "password"; | |||||
return LessPass.generatePassword(profile, masterPassword).then(generatedPassword => { | |||||
assert.equal("s>{F}RwkN/-fmM.X", generatedPassword); | |||||
}); | }); | ||||
it("generatedPassword", function() { | |||||
this.timeout(10000); | |||||
var site = "example.org"; | |||||
var login = "contact@example.org"; | |||||
var masterPassword = "password"; | |||||
return LessPass.generatePassword( | |||||
site, | |||||
login, | |||||
masterPassword | |||||
).then(function(generatedPassword) { | |||||
assert.equal("WHLpUL)e00[iHR+w", generatedPassword); | |||||
}); | |||||
}); | |||||
it("createFingerprint", () => { | |||||
return LessPass.createFingerprint("password").then(function(fingerprint) { | |||||
assert.equal( | |||||
"e56a207acd1e6714735487c199c6f095844b7cc8e5971d86c003a7b6f36ef51e", | |||||
fingerprint | |||||
); | |||||
}); | }); | ||||
}); | }); | ||||
describe("fingerprint", function() { | |||||
it("createFingerprint", function() { | |||||
return LessPass.createFingerprint("password").then(function(fingerprint) { | |||||
assert.equal( | |||||
"e56a207acd1e6714735487c199c6f095844b7cc8e5971d86c003a7b6f36ef51e", | |||||
fingerprint | |||||
); | |||||
}); | |||||
it("isSupported", () => { | |||||
return LessPass.isSupported("password").then(function(isSupported) { | |||||
assert(isSupported); | |||||
}); | }); | ||||
}); | }); | ||||
}); | }); |
@@ -0,0 +1,89 @@ | |||||
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() { | |||||
const profile = { | |||||
site: "example.org", | |||||
login: "contact@example.org", | |||||
options: { | |||||
counter: 1 | |||||
}, | |||||
crypto: { | |||||
method: 'pbkdf2', | |||||
iterations: 100000, | |||||
keylen: 32, | |||||
digest: "sha256" | |||||
} | |||||
}; | |||||
const masterPassword = "password"; | |||||
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 = { | |||||
site: "example.org", | |||||
login: "contact@example.org", | |||||
options: { | |||||
counter: 1 | |||||
}, | |||||
crypto: { | |||||
method: 'pbkdf2', | |||||
iterations: 8192, | |||||
keylen: 16, | |||||
digest: "sha512" | |||||
} | |||||
}; | |||||
const masterPassword = "password"; | |||||
return LessPass._calcEntropy(profile, masterPassword) | |||||
.then(function(entropy) { | |||||
assert.equal("fff211c16a4e776b3574c6a5c91fd252", entropy); | |||||
}); | |||||
}); | |||||
it("calc entropy different if counter different 1", function() { | |||||
const profile = { | |||||
site: "example.org", | |||||
login: "contact@example.org", | |||||
options: { | |||||
counter: 1 | |||||
}, | |||||
crypto: { | |||||
method: 'pbkdf2', | |||||
iterations: 100000, | |||||
keylen: 32, | |||||
digest: "sha256" | |||||
} | |||||
}; | |||||
const profile2 = { | |||||
site: "example.org", | |||||
login: "contact@example.org", | |||||
options: { | |||||
counter: 2 | |||||
}, | |||||
crypto: { | |||||
method: 'pbkdf2', | |||||
iterations: 100000, | |||||
keylen: 32, | |||||
digest: "sha256" | |||||
} | |||||
}; | |||||
const promises = [ | |||||
LessPass._calcEntropy(profile, "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,47 +1,47 @@ | |||||
var assert = require("assert"); | |||||
var v2 = require("../../src/v2"); | |||||
var bigInt = require("big-integer"); | |||||
const assert = require("assert"); | |||||
const LessPass = require("../src/lesspass"); | |||||
const bigInt = require("big-integer"); | |||||
describe("LessPass v2", function() { | |||||
var defaultPasswordProfile = { | |||||
describe("LessPass LessPass", function() { | |||||
const defaultPasswordProfile = { | |||||
length: 16, | length: 16, | ||||
lowercase: true, | lowercase: true, | ||||
uppercase: true, | uppercase: true, | ||||
numbers: true, | |||||
digits: true, | |||||
symbols: true | symbols: true | ||||
}; | }; | ||||
it("render password use remainder of long division beetween entropy and set of chars length as an index", function() { | it("render password use remainder of long division beetween entropy and set of chars length as an index", function() { | ||||
var entropy = | |||||
const entropy = | |||||
"dc33d431bce2b01182c613382483ccdb0e2f66482cbba5e9d07dab34acc7eb1e"; | "dc33d431bce2b01182c613382483ccdb0e2f66482cbba5e9d07dab34acc7eb1e"; | ||||
assert.equal("W", v2._renderPassword(entropy, defaultPasswordProfile)[0]); | |||||
assert.equal("W", LessPass._renderPassword(entropy, defaultPasswordProfile)[0]); | |||||
}); | }); | ||||
it("render password use quotient as second entropy recursively", function() { | it("render password use quotient as second entropy recursively", function() { | ||||
var entropy = | |||||
const entropy = | |||||
"dc33d431bce2b01182c613382483ccdb0e2f66482cbba5e9d07dab34acc7eb1e"; | "dc33d431bce2b01182c613382483ccdb0e2f66482cbba5e9d07dab34acc7eb1e"; | ||||
assert.equal("H", v2._renderPassword(entropy, defaultPasswordProfile)[1]); | |||||
assert.equal("H", LessPass._renderPassword(entropy, defaultPasswordProfile)[1]); | |||||
}); | }); | ||||
it("render password has default length of 16", function() { | it("render password has default length of 16", function() { | ||||
var entropy = | |||||
const entropy = | |||||
"dc33d431bce2b01182c613382483ccdb0e2f66482cbba5e9d07dab34acc7eb1e"; | "dc33d431bce2b01182c613382483ccdb0e2f66482cbba5e9d07dab34acc7eb1e"; | ||||
assert.equal( | assert.equal( | ||||
16, | 16, | ||||
v2._renderPassword(entropy, defaultPasswordProfile).length | |||||
LessPass._renderPassword(entropy, defaultPasswordProfile).length | |||||
); | ); | ||||
}); | }); | ||||
it("render password can specify length", function() { | it("render password can specify length", function() { | ||||
var entropy = | |||||
const entropy = | |||||
"dc33d431bce2b01182c613382483ccdb0e2f66482cbba5e9d07dab34acc7eb1e"; | "dc33d431bce2b01182c613382483ccdb0e2f66482cbba5e9d07dab34acc7eb1e"; | ||||
var passwordProfile = { | |||||
const passwordProfile = { | |||||
length: 20, | length: 20, | ||||
lowercase: true, | lowercase: true, | ||||
uppercase: true, | uppercase: true, | ||||
numbers: true, | |||||
digits: true, | |||||
symbols: true | symbols: true | ||||
}; | }; | ||||
assert.equal(20, v2._renderPassword(entropy, passwordProfile).length); | |||||
assert.equal(20, LessPass._renderPassword(entropy, passwordProfile).length); | |||||
}); | }); | ||||
it("include one char per set of characters", function() { | it("include one char per set of characters", function() { | ||||
var password = v2._insertStringPseudoRandomly( | |||||
const password = LessPass._insertStringPseudoRandomly( | |||||
"123456", | "123456", | ||||
bigInt(7 * 6 + 2), | bigInt(7 * 6 + 2), | ||||
"uT" | "uT" | ||||
@@ -49,21 +49,21 @@ describe("LessPass v2", function() { | |||||
assert.equal("T12u3456", password); | assert.equal("T12u3456", password); | ||||
}); | }); | ||||
it("render password return at least one char in every characters set", function() { | it("render password return at least one char in every characters set", function() { | ||||
var entropy = | |||||
const entropy = | |||||
"dc33d431bce2b01182c613382483ccdb0e2f66482cbba5e9d07dab34acc7eb1e"; | "dc33d431bce2b01182c613382483ccdb0e2f66482cbba5e9d07dab34acc7eb1e"; | ||||
var passwordProfile = { | |||||
const passwordProfile = { | |||||
length: 6, | length: 6, | ||||
lowercase: true, | lowercase: true, | ||||
uppercase: true, | uppercase: true, | ||||
numbers: true, | |||||
digits: true, | |||||
symbols: true | symbols: true | ||||
}; | }; | ||||
var generatedPassword = v2._renderPassword(entropy, passwordProfile); | |||||
var passwordLength = generatedPassword.length; | |||||
var lowercaseOk = false; | |||||
var uppercaseOk = false; | |||||
var numbersOk = false; | |||||
var symbolsOk = false; | |||||
const generatedPassword = LessPass._renderPassword(entropy, passwordProfile); | |||||
let passwordLength = generatedPassword.length; | |||||
let lowercaseOk = false; | |||||
let uppercaseOk = false; | |||||
let digitsOk = false; | |||||
let symbolsOk = false; | |||||
while (passwordLength--) { | while (passwordLength--) { | ||||
if ( | if ( | ||||
"abcdefghijklmnopqrstuvwxyz".indexOf( | "abcdefghijklmnopqrstuvwxyz".indexOf( | ||||
@@ -80,7 +80,7 @@ describe("LessPass v2", function() { | |||||
uppercaseOk = true; | uppercaseOk = true; | ||||
} | } | ||||
if ("0123456789".indexOf(generatedPassword[passwordLength]) !== -1) { | if ("0123456789".indexOf(generatedPassword[passwordLength]) !== -1) { | ||||
numbersOk = true; | |||||
digitsOk = true; | |||||
} | } | ||||
if ( | if ( | ||||
"!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~".indexOf( | "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~".indexOf( | ||||
@@ -92,7 +92,7 @@ describe("LessPass v2", function() { | |||||
} | } | ||||
assert.equal(6, generatedPassword.length); | assert.equal(6, generatedPassword.length); | ||||
assert( | assert( | ||||
lowercaseOk && uppercaseOk && numbersOk && symbolsOk, | |||||
lowercaseOk && uppercaseOk && digitsOk && symbolsOk, | |||||
"there is no at least one char in every characters set" | "there is no at least one char in every characters set" | ||||
); | ); | ||||
}); | }); |
@@ -1,10 +1,10 @@ | |||||
var assert = require("assert"); | var assert = require("assert"); | ||||
var v2 = require("../../src/v2"); | |||||
var LessPass = require("../src/lesspass"); | |||||
var bigInt = require("big-integer"); | var bigInt = require("big-integer"); | ||||
describe("set of characters", function() { | describe("set of characters", function() { | ||||
it("get default set of characters", function() { | it("get default set of characters", function() { | ||||
var setOfCharacters = v2._getSetOfCharacters(); | |||||
var setOfCharacters = LessPass._getSetOfCharacters(); | |||||
assert.equal( | assert.equal( | ||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", | "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", | ||||
setOfCharacters | setOfCharacters | ||||
@@ -12,10 +12,10 @@ describe("set of characters", function() { | |||||
assert.equal(26 * 2 + 10 + 32, setOfCharacters.length); | assert.equal(26 * 2 + 10 + 32, setOfCharacters.length); | ||||
}); | }); | ||||
it("get default set of characters concat rules in order", function() { | it("get default set of characters concat rules in order", function() { | ||||
var setOfCharacters = v2._getSetOfCharacters([ | |||||
var setOfCharacters = LessPass._getSetOfCharacters([ | |||||
"lowercase", | "lowercase", | ||||
"uppercase", | "uppercase", | ||||
"numbers" | |||||
"digits" | |||||
]); | ]); | ||||
assert.equal( | assert.equal( | ||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", | "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", | ||||
@@ -24,27 +24,27 @@ describe("set of characters", function() { | |||||
assert.equal(26 * 2 + 10, setOfCharacters.length); | assert.equal(26 * 2 + 10, setOfCharacters.length); | ||||
}); | }); | ||||
it("get set of characters only lowercase", function() { | it("get set of characters only lowercase", function() { | ||||
var setOfCharacters = v2._getSetOfCharacters(["lowercase"]); | |||||
var setOfCharacters = LessPass._getSetOfCharacters(["lowercase"]); | |||||
assert.equal("abcdefghijklmnopqrstuvwxyz", setOfCharacters); | assert.equal("abcdefghijklmnopqrstuvwxyz", setOfCharacters); | ||||
assert.equal(26, setOfCharacters.length); | assert.equal(26, setOfCharacters.length); | ||||
}); | }); | ||||
it("get set of characters only uppercase", function() { | it("get set of characters only uppercase", function() { | ||||
var setOfCharacters = v2._getSetOfCharacters(["uppercase"]); | |||||
var setOfCharacters = LessPass._getSetOfCharacters(["uppercase"]); | |||||
assert.equal("ABCDEFGHIJKLMNOPQRSTUVWXYZ", setOfCharacters); | assert.equal("ABCDEFGHIJKLMNOPQRSTUVWXYZ", setOfCharacters); | ||||
assert.equal(26, setOfCharacters.length); | assert.equal(26, setOfCharacters.length); | ||||
}); | }); | ||||
it("get set of characters only numbers", function() { | |||||
var setOfCharacters = v2._getSetOfCharacters(["numbers"]); | |||||
it("get set of characters only digits", function() { | |||||
var setOfCharacters = LessPass._getSetOfCharacters(["digits"]); | |||||
assert.equal("0123456789", setOfCharacters); | assert.equal("0123456789", setOfCharacters); | ||||
assert.equal(10, setOfCharacters.length); | assert.equal(10, setOfCharacters.length); | ||||
}); | }); | ||||
it("get set of characters only symbols", function() { | it("get set of characters only symbols", function() { | ||||
var setOfCharacters = v2._getSetOfCharacters(["symbols"]); | |||||
var setOfCharacters = LessPass._getSetOfCharacters(["symbols"]); | |||||
assert.equal("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", setOfCharacters); | assert.equal("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", setOfCharacters); | ||||
assert.equal(32, setOfCharacters.length); | assert.equal(32, setOfCharacters.length); | ||||
}); | }); | ||||
it("generate one char per rules", function() { | it("generate one char per rules", function() { | ||||
var oneCharPerSetOfCharacters = v2._getOneCharPerRule(bigInt(26 * 26), [ | |||||
var oneCharPerSetOfCharacters = LessPass._getOneCharPerRule(bigInt(26 * 26), [ | |||||
"lowercase", | "lowercase", | ||||
"uppercase" | "uppercase" | ||||
]); | ]); | ||||
@@ -55,23 +55,23 @@ describe("set of characters", function() { | |||||
it("configured rules", function() { | it("configured rules", function() { | ||||
assert.deepEqual( | assert.deepEqual( | ||||
["uppercase"], | ["uppercase"], | ||||
v2._getConfiguredRules({ uppercase: true }) | |||||
LessPass._getConfiguredRules({ uppercase: true }) | |||||
); | ); | ||||
assert.deepEqual( | assert.deepEqual( | ||||
["lowercase", "uppercase"], | ["lowercase", "uppercase"], | ||||
v2._getConfiguredRules({ uppercase: true, lowercase: true }) | |||||
LessPass._getConfiguredRules({ uppercase: true, lowercase: true }) | |||||
); | ); | ||||
assert.deepEqual( | assert.deepEqual( | ||||
["lowercase"], | ["lowercase"], | ||||
v2._getConfiguredRules({ lowercase: true, symbols: false }) | |||||
LessPass._getConfiguredRules({ lowercase: true, symbols: false }) | |||||
); | ); | ||||
assert.deepEqual( | assert.deepEqual( | ||||
["lowercase", "uppercase", "numbers", "symbols"], | |||||
v2._getConfiguredRules({ | |||||
["lowercase", "uppercase", "digits", "symbols"], | |||||
LessPass._getConfiguredRules({ | |||||
lowercase: true, | lowercase: true, | ||||
uppercase: true, | uppercase: true, | ||||
symbols: true, | symbols: true, | ||||
numbers: true | |||||
digits: true | |||||
}) | }) | ||||
); | ); | ||||
}); | }); |
@@ -1,110 +0,0 @@ | |||||
var assert = require("assert"); | |||||
var v1 = require("../../src/v1"); | |||||
describe("deriveEncryptedLogin", function() { | |||||
it("should createHmac", function() { | |||||
var encryptedLogin = | |||||
"9f505f3a95fe0485da3242cb81c9fe25c2f400d8399737655a8dad2b52778d88"; | |||||
var salt = "lesspass.com1"; | |||||
return v1._createHmac(encryptedLogin, salt).then(function(hmac) { | |||||
assert.equal( | |||||
"be00f942fc8aa67d8e76fc2456862b9d66d166ebfdd3dc2f0116e278209532ed", | |||||
hmac | |||||
); | |||||
}); | |||||
}); | |||||
it("should derive encrypted login with default options 1", function() { | |||||
const encryptedLogin = | |||||
"90cff82b8847525370a8f29a59ecf45db62c719a535788ad0df58d32304e925d"; | |||||
const site = "lesspass.com"; | |||||
return v1 | |||||
._deriveEncryptedLogin(encryptedLogin, site) | |||||
.then(function(generatedPassword) { | |||||
assert.equal("ecd16aefc7e5", generatedPassword); | |||||
}); | |||||
}); | |||||
it("should derive encrypted login with default options 2", function() { | |||||
const encryptedLogin = | |||||
"90cff82b8847525370a8f29a59ecf45db62c719a535788ad0df58d32304e925d"; | |||||
const site = "lesspass.com"; | |||||
const option = { | |||||
counter: 1, | |||||
length: 12, | |||||
lowercase: true, | |||||
uppercase: true, | |||||
numbers: true, | |||||
symbols: true | |||||
}; | |||||
return v1 | |||||
._deriveEncryptedLogin(encryptedLogin, site, option) | |||||
.then(function(generatedPassword) { | |||||
assert.equal("ecd16aefc7e5", generatedPassword); | |||||
}); | |||||
}); | |||||
it("should derive encrypted login with defined length", function() { | |||||
var encryptedLogin = | |||||
"d79d8482f708122288af7b259393a58fe05840f4555cc935cdd3f062b9aa75ed"; | |||||
var site = "lesspass.com"; | |||||
var option = { | |||||
counter: 1, | |||||
length: 10 | |||||
}; | |||||
return v1 | |||||
._deriveEncryptedLogin(encryptedLogin, site, option) | |||||
.then(function(generatedPassword) { | |||||
assert.equal(10, generatedPassword.length); | |||||
}); | |||||
}); | |||||
it("should return two different passwords if site different 1", function() { | |||||
const encryptedLogin = | |||||
"f4fd3885fb70085f2285c3382e2d9adb4c2553285fc45dd896791aa5e79070a9"; | |||||
const site = "google.com"; | |||||
return v1 | |||||
._deriveEncryptedLogin(encryptedLogin, site) | |||||
.then(function(derivedEncryptedLogin) { | |||||
assert.equal("a957c3a459ec", derivedEncryptedLogin); | |||||
}); | |||||
}); | |||||
it("should return two different passwords if site different 2", function() { | |||||
const encryptedLogin = | |||||
"f4fd3885fb70085f2285c3382e2d9adb4c2553285fc45dd896791aa5e79070a9"; | |||||
const site = "facebook.com"; | |||||
return v1 | |||||
._deriveEncryptedLogin(encryptedLogin, site) | |||||
.then(function(derivedEncryptedLogin) { | |||||
assert.equal("d9f3a918c34b", derivedEncryptedLogin); | |||||
}); | |||||
}); | |||||
it("should return two different passwords if counter different 1", function() { | |||||
const encryptedLogin = | |||||
"dfba06278c9aa24d992bc2d390a53efef482788859455875f72015335d085fcd"; | |||||
const site = "lesspass.com"; | |||||
const option = { counter: 1 }; | |||||
return v1 | |||||
._deriveEncryptedLogin(encryptedLogin, site, option) | |||||
.then(function(derivedEncryptedLogins) { | |||||
assert.equal("bb2e0b34036d", derivedEncryptedLogins); | |||||
}); | |||||
}); | |||||
it("should return two different passwords if counter different 2", function() { | |||||
const encryptedLogin = | |||||
"dfba06278c9aa24d992bc2d390a53efef482788859455875f72015335d085fcd"; | |||||
const site = "lesspass.com"; | |||||
const option2 = { counter: 2 }; | |||||
return v1 | |||||
._deriveEncryptedLogin(encryptedLogin, site, option2) | |||||
.then(function(derivedEncryptedLogins) { | |||||
assert.equal("67fe8c05a248", derivedEncryptedLogins); | |||||
}); | |||||
}); | |||||
it("should derive encrypted login with sha 256", function() { | |||||
const encryptedLogin = | |||||
"9f505f3a95fe0485da3242cb81c9fe25c2f400d8399737655a8dad2b52778d88"; | |||||
const site = "lesspass.com"; | |||||
v1 | |||||
._deriveEncryptedLogin(encryptedLogin, site) | |||||
.then(function(encryptedLogin) { | |||||
assert.equal("be00f942fc8a", encryptedLogin); | |||||
}); | |||||
}); | |||||
}); |
@@ -1,82 +0,0 @@ | |||||
var assert = require("assert"); | |||||
var v1 = require("../../src/v1"); | |||||
describe("getPasswordTemplate", function() { | |||||
it("should get default template", function() { | |||||
assert.equal( | |||||
"vcVCns", | |||||
v1._getPasswordTemplate({ | |||||
counter: 1, | |||||
length: 12, | |||||
lowercase: true, | |||||
uppercase: true, | |||||
numbers: true, | |||||
symbols: true | |||||
}) | |||||
); | |||||
}); | |||||
it("should get lowercase template", function() { | |||||
assert.equal( | |||||
"vc", | |||||
v1._getPasswordTemplate({ | |||||
lowercase: true, | |||||
uppercase: false, | |||||
numbers: false, | |||||
symbols: false | |||||
}) | |||||
); | |||||
}); | |||||
it("should get uppercase template", function() { | |||||
assert.equal( | |||||
"VC", | |||||
v1._getPasswordTemplate({ | |||||
lowercase: false, | |||||
uppercase: true, | |||||
numbers: false, | |||||
symbols: false | |||||
}) | |||||
); | |||||
}); | |||||
it("should get numbers template", function() { | |||||
assert.equal( | |||||
"n", | |||||
v1._getPasswordTemplate({ | |||||
lowercase: false, | |||||
uppercase: false, | |||||
numbers: true, | |||||
symbols: false | |||||
}) | |||||
); | |||||
}); | |||||
it("should get symbols template", function() { | |||||
assert.equal( | |||||
"s", | |||||
v1._getPasswordTemplate({ | |||||
lowercase: false, | |||||
uppercase: false, | |||||
numbers: false, | |||||
symbols: true | |||||
}) | |||||
); | |||||
}); | |||||
it("should concatenate template if two password settings", function() { | |||||
assert.equal( | |||||
"vcVC", | |||||
v1._getPasswordTemplate({ | |||||
lowercase: true, | |||||
uppercase: true, | |||||
numbers: false, | |||||
symbols: false | |||||
}) | |||||
); | |||||
assert.equal( | |||||
"vcns", | |||||
v1._getPasswordTemplate({ | |||||
lowercase: true, | |||||
uppercase: false, | |||||
numbers: true, | |||||
symbols: true | |||||
}) | |||||
); | |||||
}); | |||||
}); |
@@ -1,39 +0,0 @@ | |||||
var assert = require("assert"); | |||||
var v1 = require("../../src/v1"); | |||||
describe("prettyPrint", function() { | |||||
it("should print different password if templates different", function() { | |||||
var encryptedLogin = | |||||
"78ae5892055ab59fdd54489ae30928d322841a27590b65cf875fcfdd083f7c32"; | |||||
assert.notEqual( | |||||
v1._prettyPrint(encryptedLogin, "cv"), | |||||
v1._prettyPrint(encryptedLogin, "vc") | |||||
); | |||||
}); | |||||
it("must return a string of the same length as the input", function() { | |||||
var hash = | |||||
"f5785e569ab5d38b02e2248c798ac17df90f57a85f34a9d5382408c2f0d9532d"; | |||||
assert.equal(hash.length, v1._prettyPrint(hash, "cv").length); | |||||
}); | |||||
it("should return char inside a string based on modulo of the index", function() { | |||||
var template = "cv"; | |||||
assert.equal("c", v1._getCharType(template, 0)); | |||||
assert.equal("v", v1._getCharType(template, 1)); | |||||
assert.equal("c", v1._getCharType(template, 10)); | |||||
}); | |||||
it("should convert a string into an array of char code", function() { | |||||
var charCodes = v1._string2charCodes("ab40f6ee71"); | |||||
assert.equal(97, charCodes[0]); | |||||
assert.equal(98, charCodes[1]); | |||||
assert.equal(10, charCodes.length); | |||||
}); | |||||
it("should get password char based on its type and index", function() { | |||||
var typeVowel = "V"; | |||||
assert.equal("A", v1._getPasswordChar(typeVowel, 0)); | |||||
}); | |||||
it("should modulo if overflow", function() { | |||||
var typeVowel = "V"; | |||||
assert.equal("E", v1._getPasswordChar(typeVowel, 1)); | |||||
assert.equal("E", v1._getPasswordChar(typeVowel, 7)); | |||||
}); | |||||
}); |
@@ -1,281 +0,0 @@ | |||||
var assert = require("assert"); | |||||
var v1 = require("../../src/v1"); | |||||
describe("renderPassword", function() { | |||||
it("renderPassword", function() { | |||||
var site = "lesspass.com"; | |||||
var encryptedLogin = | |||||
"63d850713d0b2f7f2c4396fe93f4ac0c6bc7485f9e7473c4b8c4a33ec12199c0"; | |||||
var passwordOptions = { | |||||
counter: 1, | |||||
length: 12, | |||||
lowercase: true, | |||||
uppercase: true, | |||||
numbers: true, | |||||
symbols: true | |||||
}; | |||||
return v1 | |||||
._renderPassword(encryptedLogin, site, passwordOptions) | |||||
.then(function(generatedPassword) { | |||||
assert.equal("azYS7,olOL2]", generatedPassword); | |||||
}); | |||||
}); | |||||
it("renderPassword with a custom template", function() { | |||||
var site = "lesspass.com"; | |||||
var encryptedLogin = | |||||
"63d850713d0b2f7f2c4396fe93f4ac0c6bc7485f9e7473c4b8c4a33ec12199c0"; | |||||
var passwordOptions = { | |||||
counter: 1, | |||||
length: 12, | |||||
lowercase: true, | |||||
uppercase: true, | |||||
numbers: true, | |||||
symbols: true, | |||||
template: "n" | |||||
}; | |||||
return v1 | |||||
._renderPassword(encryptedLogin, site, passwordOptions) | |||||
.then(function(generatedPassword) { | |||||
var i = generatedPassword.length; | |||||
while (i--) { | |||||
assert("0123456789".indexOf(generatedPassword[i]) !== -1); | |||||
} | |||||
}); | |||||
}); | |||||
it("renderPassword with a custom template too short", function() { | |||||
var site = "lesspass.com"; | |||||
var encryptedLogin = | |||||
"63d850713d0b2f7f2c4396fe93f4ac0c6bc7485f9e7473c4b8c4a33ec12199c0"; | |||||
var passwordOptions = { | |||||
counter: 1, | |||||
length: 12, | |||||
lowercase: true, | |||||
uppercase: true, | |||||
numbers: true, | |||||
symbols: true, | |||||
template: "CvcnCVsn" | |||||
}; | |||||
return v1 | |||||
._renderPassword(encryptedLogin, site, passwordOptions) | |||||
.then(function(generatedPassword) { | |||||
assert.equal("Sor4WU:8Wad5", generatedPassword); | |||||
}); | |||||
}); | |||||
it("renderPassword with a custom template too long", function() { | |||||
var site = "lesspass.com"; | |||||
var encryptedLogin = | |||||
"63d850713d0b2f7f2c4396fe93f4ac0c6bc7485f9e7473c4b8c4a33ec12199c0"; | |||||
var passwordOptions = { | |||||
counter: 1, | |||||
length: 6, | |||||
lowercase: true, | |||||
uppercase: true, | |||||
numbers: true, | |||||
symbols: true, | |||||
template: "CvcnCVsn" | |||||
}; | |||||
return v1 | |||||
._renderPassword(encryptedLogin, site, passwordOptions) | |||||
.then(function(generatedPassword) { | |||||
assert.equal("Sor4WU", generatedPassword); | |||||
}); | |||||
}); | |||||
it("renderPassword auto generated 0", function() { | |||||
var site = "lesspass.com"; | |||||
var encryptedLogin = | |||||
"63d850713d0b2f7f2c4396fe93f4ac0c6bc7485f9e7473c4b8c4a33ec12199c0"; | |||||
var passwordOptions = { | |||||
counter: 1, | |||||
length: 12, | |||||
lowercase: true, | |||||
uppercase: true, | |||||
numbers: true, | |||||
symbols: true | |||||
}; | |||||
return v1 | |||||
._renderPassword(encryptedLogin, site, passwordOptions) | |||||
.then(function(generatedPassword) { | |||||
assert.equal("azYS7,olOL2]", generatedPassword); | |||||
}); | |||||
}); | |||||
it("renderPassword auto generated 1", function() { | |||||
var site = "lesspass.com"; | |||||
var encryptedLogin = | |||||
"63d850713d0b2f7f2c4396fe93f4ac0c6bc7485f9e7473c4b8c4a33ec12199c0"; | |||||
var passwordOptions = { | |||||
counter: 1, | |||||
length: 14, | |||||
lowercase: true, | |||||
uppercase: true, | |||||
numbers: true, | |||||
symbols: true | |||||
}; | |||||
return v1 | |||||
._renderPassword(encryptedLogin, site, passwordOptions) | |||||
.then(function(generatedPassword) { | |||||
assert.equal("azYS7,olOL2]iz", generatedPassword); | |||||
}); | |||||
}); | |||||
it("renderPassword auto generated 2", function() { | |||||
var site = "lesspass.com"; | |||||
var encryptedLogin = | |||||
"63d850713d0b2f7f2c4396fe93f4ac0c6bc7485f9e7473c4b8c4a33ec12199c0"; | |||||
var passwordOptions = { | |||||
counter: 1, | |||||
length: 12, | |||||
lowercase: true, | |||||
uppercase: false, | |||||
numbers: false, | |||||
symbols: false | |||||
}; | |||||
return v1 | |||||
._renderPassword(encryptedLogin, site, passwordOptions) | |||||
.then(function(generatedPassword) { | |||||
assert.equal("azyseqololat", generatedPassword); | |||||
}); | |||||
}); | |||||
it("renderPassword auto generated 3", function() { | |||||
var site = "lesspass.com"; | |||||
var encryptedLogin = | |||||
"63d850713d0b2f7f2c4396fe93f4ac0c6bc7485f9e7473c4b8c4a33ec12199c0"; | |||||
var passwordOptions = { | |||||
counter: 1, | |||||
length: 12, | |||||
lowercase: false, | |||||
uppercase: true, | |||||
numbers: true, | |||||
symbols: true | |||||
}; | |||||
return v1 | |||||
._renderPassword(encryptedLogin, site, passwordOptions) | |||||
.then(function(generatedPassword) { | |||||
assert.equal("AZ3[EQ7@OL2]", generatedPassword); | |||||
}); | |||||
}); | |||||
it("renderPassword auto generated 4", function() { | |||||
var site = "lesspass.com"; | |||||
var encryptedLogin = | |||||
"63d850713d0b2f7f2c4396fe93f4ac0c6bc7485f9e7473c4b8c4a33ec12199c0"; | |||||
var passwordOptions = { | |||||
counter: 1, | |||||
length: 12, | |||||
lowercase: false, | |||||
uppercase: false, | |||||
numbers: true, | |||||
symbols: true | |||||
}; | |||||
return v1 | |||||
._renderPassword(encryptedLogin, site, passwordOptions) | |||||
.then(function(generatedPassword) { | |||||
assert.equal("4?3[7,7@7@2]", generatedPassword); | |||||
}); | |||||
}); | |||||
it("renderPassword auto generated 5", function() { | |||||
var site = "lesspass.com"; | |||||
var encryptedLogin = | |||||
"63d850713d0b2f7f2c4396fe93f4ac0c6bc7485f9e7473c4b8c4a33ec12199c0"; | |||||
var passwordOptions = { | |||||
counter: 1, | |||||
length: 12, | |||||
lowercase: false, | |||||
uppercase: false, | |||||
numbers: false, | |||||
symbols: true | |||||
}; | |||||
return v1 | |||||
._renderPassword(encryptedLogin, site, passwordOptions) | |||||
.then(function(generatedPassword) { | |||||
assert.equal("[?=[&,:@:@[]", generatedPassword); | |||||
}); | |||||
}); | |||||
it("renderPassword auto generated 6", function() { | |||||
var site = "lesspass.com"; | |||||
var encryptedLogin = | |||||
"63d850713d0b2f7f2c4396fe93f4ac0c6bc7485f9e7473c4b8c4a33ec12199c0"; | |||||
var passwordOptions = { | |||||
counter: 1, | |||||
length: 12, | |||||
lowercase: true, | |||||
uppercase: true, | |||||
numbers: true, | |||||
symbols: false | |||||
}; | |||||
return v1 | |||||
._renderPassword(encryptedLogin, site, passwordOptions) | |||||
.then(function(generatedPassword) { | |||||
assert.equal("azYS7uwAW8at", generatedPassword); | |||||
}); | |||||
}); | |||||
it("renderPassword auto generated 7", function() { | |||||
var site = "lesspass.com"; | |||||
var encryptedLogin = | |||||
"63d850713d0b2f7f2c4396fe93f4ac0c6bc7485f9e7473c4b8c4a33ec12199c0"; | |||||
var passwordOptions = { | |||||
counter: 1, | |||||
length: 12, | |||||
lowercase: true, | |||||
uppercase: true, | |||||
numbers: false, | |||||
symbols: false | |||||
}; | |||||
return v1 | |||||
._renderPassword(encryptedLogin, site, passwordOptions) | |||||
.then(function(generatedPassword) { | |||||
assert.equal("azYSeqOLolAT", generatedPassword); | |||||
}); | |||||
}); | |||||
it("renderPassword auto generated 8", function() { | |||||
var site = "lesspass.com"; | |||||
var encryptedLogin = | |||||
"63d850713d0b2f7f2c4396fe93f4ac0c6bc7485f9e7473c4b8c4a33ec12199c0"; | |||||
var passwordOptions = { | |||||
counter: 2, | |||||
length: 12, | |||||
lowercase: true, | |||||
uppercase: true, | |||||
numbers: true, | |||||
symbols: true | |||||
}; | |||||
return v1 | |||||
._renderPassword(encryptedLogin, site, passwordOptions) | |||||
.then(function(generatedPassword) { | |||||
assert.equal("obYT2=olOV9=", generatedPassword); | |||||
}); | |||||
}); | |||||
it("renderPassword auto generated 9", function() { | |||||
var site = "lesspass.com"; | |||||
var encryptedLogin = | |||||
"7d05ee25597dcc3ac16d082aa910e7707f75be620ed8db5bef7245e2a8579116"; | |||||
var passwordOptions = { | |||||
counter: 1, | |||||
length: 12, | |||||
lowercase: true, | |||||
uppercase: true, | |||||
numbers: true, | |||||
symbols: true | |||||
}; | |||||
return v1 | |||||
._renderPassword(encryptedLogin, site, passwordOptions) | |||||
.then(function(generatedPassword) { | |||||
assert.equal("erOC1%imIW3,", generatedPassword); | |||||
}); | |||||
}); | |||||
it("renderPassword auto generated 10", function() { | |||||
var site = "lesspass.com"; | |||||
var encryptedLogin = | |||||
"ce853092fc54fe88c281e38df97bd5826d64e6bee315dc94939cbba8930df0e4"; | |||||
var passwordOptions = { | |||||
counter: 1, | |||||
length: 12, | |||||
lowercase: true, | |||||
uppercase: true, | |||||
numbers: true, | |||||
symbols: true | |||||
}; | |||||
return v1 | |||||
._renderPassword(encryptedLogin, site, passwordOptions) | |||||
.then(function(generatedPassword) { | |||||
assert.equal("uvUM5_ucUP5=", generatedPassword); | |||||
}); | |||||
}); | |||||
}); |
@@ -1,79 +0,0 @@ | |||||
var assert = require("assert"); | |||||
var v2 = require("../../src/v2"); | |||||
var bigInt = require("big-integer"); | |||||
describe("entropy", function() { | |||||
it("calc entropy pbkdf2 with default params (100000 iterations, 32 bytes length, sha256 digest)", function() { | |||||
this.timeout(10000); | |||||
var site = "example.org"; | |||||
var login = "contact@example.org"; | |||||
var masterPassword = "password"; | |||||
var passwordProfile = { | |||||
iterations: 100000, | |||||
keylen: 32, | |||||
digest: "sha256", | |||||
counter: 1 | |||||
}; | |||||
return v2 | |||||
._calcEntropy(site, login, masterPassword, passwordProfile) | |||||
.then(function(entropy) { | |||||
assert.equal( | |||||
"dc33d431bce2b01182c613382483ccdb0e2f66482cbba5e9d07dab34acc7eb1e", | |||||
entropy | |||||
); | |||||
}); | |||||
}); | |||||
it("calc entropy with different options (8192 iterations, 16 bytes length, sha512 digest)", function() { | |||||
var site = "example.org"; | |||||
var login = "contact@example.org"; | |||||
var masterPassword = "password"; | |||||
var passwordProfile = { | |||||
iterations: 8192, | |||||
keylen: 16, | |||||
digest: "sha512", | |||||
counter: 1 | |||||
}; | |||||
return v2 | |||||
._calcEntropy(site, login, masterPassword, passwordProfile) | |||||
.then(function(entropy) { | |||||
assert.equal("fff211c16a4e776b3574c6a5c91fd252", entropy); | |||||
}); | |||||
}); | |||||
it("calc entropy different if counter different 1", function() { | |||||
var site = "example.org"; | |||||
var login = "contact@example.org"; | |||||
var masterPassword = "password"; | |||||
var passwordProfile1 = { | |||||
iterations: 1, | |||||
keylen: 16, | |||||
digest: "sha256", | |||||
counter: 1 | |||||
}; | |||||
return v2 | |||||
._calcEntropy(site, login, masterPassword, passwordProfile1) | |||||
.then(function(entropy) { | |||||
assert.equal("d3ec1e988dd0b3640c7491cd2c2a88b5", entropy); | |||||
}); | |||||
}); | |||||
it("calc entropy different if counter different 2", function() { | |||||
var site = "example.org"; | |||||
var login = "contact@example.org"; | |||||
var masterPassword = "password"; | |||||
var passwordProfile2 = { | |||||
iterations: 1, | |||||
keylen: 16, | |||||
digest: "sha256", | |||||
counter: 2 | |||||
}; | |||||
return v2 | |||||
._calcEntropy(site, login, masterPassword, passwordProfile2) | |||||
.then(function(entropy) { | |||||
assert.equal("ddfb1136260f930c21f6d72f6eddbd40", entropy); | |||||
}); | |||||
}); | |||||
it("consume entropy", function() { | |||||
var password = v2._consumeEntropy("", bigInt(4 * 4 + 2), "abcd", 2); | |||||
assert.equal("ca", password.value); | |||||
assert.equal(1, password.entropy); | |||||
}); | |||||
}); |