@@ -0,0 +1,17 @@ | |||
# editorconfig.org | |||
root = true | |||
[*] | |||
charset = utf-8 | |||
end_of_line = lf | |||
indent_size = 2 | |||
indent_style = space | |||
insert_final_newline = true | |||
trim_trailing_whitespace = true | |||
[*.md] | |||
trim_trailing_whitespace = false | |||
[*.py] | |||
indent_size = 4 |
@@ -1,31 +1,31 @@ | |||
<!doctype html> | |||
<html lang="en"> | |||
<head> | |||
<meta charset="UTF-8"> | |||
<script src="../dist/lesspass.js"></script> | |||
<meta charset="UTF-8"> | |||
<script src="../dist/lesspass.js"></script> | |||
</head> | |||
<body> | |||
<script> | |||
const site = 'example.org'; | |||
const login = 'contact@example.org'; | |||
const masterPassword = 'password'; | |||
const passwordProfile = { | |||
lowercase: true, | |||
uppercase: true, | |||
numbers: true, | |||
symbols: true, | |||
length: 16, | |||
counter: 1, | |||
version: 2 | |||
}; | |||
LessPass.generatePassword(site, login, masterPassword, passwordProfile).then(function (generatedPassword) { | |||
console.log(generatedPassword); //WHLpUL)e00[iHR+w | |||
document.body.innerHTML = document.body.innerHTML + "<br>generated password : <b>" + generatedPassword + "</b> === WHLpUL)e00[iHR+w" | |||
}); | |||
const site = 'example.org'; | |||
const login = 'contact@example.org'; | |||
const masterPassword = 'password'; | |||
const passwordProfile = { | |||
lowercase: true, | |||
uppercase: true, | |||
numbers: true, | |||
symbols: true, | |||
length: 16, | |||
counter: 1, | |||
version: 2 | |||
}; | |||
LessPass.generatePassword(site, login, masterPassword, passwordProfile).then(function(generatedPassword) { | |||
console.log(generatedPassword); //WHLpUL)e00[iHR+w | |||
document.body.innerHTML = document.body.innerHTML + "<br>generated password : <b>" + generatedPassword + "</b> === WHLpUL)e00[iHR+w" | |||
}); | |||
LessPass.createFingerprint('password').then(fingerprint => { | |||
document.body.innerHTML = document.body.innerHTML + "fingerprint : " + fingerprint | |||
}); | |||
LessPass.createFingerprint('password').then(fingerprint => { | |||
document.body.innerHTML = document.body.innerHTML + "fingerprint : " + fingerprint | |||
}); | |||
</script> | |||
</body> | |||
</html> | |||
</html> |
@@ -4,59 +4,59 @@ var pbkdf2 = require('./pbkdf2'); | |||
var objectAssign = require('object-assign'); | |||
module.exports = { | |||
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, | |||
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: generatePassword, | |||
_calcEntropy: v2._calcEntropy, | |||
_consumeEntropy: v2._consumeEntropy, | |||
_getSetOfCharacters: v2._getSetOfCharacters, | |||
_getConfiguredRules: v2._getConfiguredRules, | |||
_insertStringPseudoRandomly: v2._insertStringPseudoRandomly, | |||
_getOneCharPerRule: v2._getOneCharPerRule, | |||
_renderPassword: v2._renderPassword, | |||
generatePassword: generatePassword, | |||
_calcEntropy: v2._calcEntropy, | |||
_consumeEntropy: v2._consumeEntropy, | |||
_getSetOfCharacters: v2._getSetOfCharacters, | |||
_getConfiguredRules: v2._getConfiguredRules, | |||
_insertStringPseudoRandomly: v2._insertStringPseudoRandomly, | |||
_getOneCharPerRule: v2._getOneCharPerRule, | |||
_renderPassword: v2._renderPassword, | |||
pbkdf2: pbkdf2 | |||
pbkdf2: pbkdf2 | |||
}; | |||
var defaultPasswordProfile = { | |||
version: 2, | |||
lowercase: true, | |||
numbers: true, | |||
uppercase: true, | |||
symbols: true, | |||
keylen: 32, | |||
digest: 'sha256', | |||
length: 16, | |||
index: 1, | |||
iterations: 100000 | |||
version: 2, | |||
lowercase: true, | |||
numbers: true, | |||
uppercase: true, | |||
symbols: true, | |||
keylen: 32, | |||
digest: 'sha256', | |||
length: 16, | |||
index: 1, | |||
iterations: 100000 | |||
}; | |||
function generatePassword(site, login, masterPassword, passwordProfile) { | |||
var _passwordProfile = objectAssign({}, defaultPasswordProfile, passwordProfile); | |||
if (_passwordProfile.version === 1) { | |||
var options = { | |||
counter: _passwordProfile.counter, | |||
length: _passwordProfile.length, | |||
lowercase: _passwordProfile.lowercase, | |||
uppercase: _passwordProfile.uppercase, | |||
numbers: _passwordProfile.numbers, | |||
symbols: _passwordProfile.symbols | |||
}; | |||
return v1.encryptLogin(login, masterPassword) | |||
.then(function (encryptedLogin) { | |||
return v1.renderPassword(encryptedLogin, site, options).then(function (generatedPassword) { | |||
return generatedPassword | |||
}); | |||
}); | |||
} | |||
return v2.generatePassword(site, login, masterPassword, _passwordProfile); | |||
} | |||
var _passwordProfile = objectAssign({}, defaultPasswordProfile, passwordProfile); | |||
if (_passwordProfile.version === 1) { | |||
var options = { | |||
counter: _passwordProfile.counter, | |||
length: _passwordProfile.length, | |||
lowercase: _passwordProfile.lowercase, | |||
uppercase: _passwordProfile.uppercase, | |||
numbers: _passwordProfile.numbers, | |||
symbols: _passwordProfile.symbols | |||
}; | |||
return v1.encryptLogin(login, masterPassword) | |||
.then(function(encryptedLogin) { | |||
return v1.renderPassword(encryptedLogin, site, options).then(function(generatedPassword) { | |||
return generatedPassword | |||
}); | |||
}); | |||
} | |||
return v2.generatePassword(site, login, masterPassword, _passwordProfile); | |||
} |
@@ -3,49 +3,49 @@ var Promise = require('pinkie-promise'); | |||
function shouldUseNative() { | |||
return !!(typeof window !== 'undefined' && window.crypto && window.crypto.subtle); | |||
return !!(typeof window !== 'undefined' && window.crypto && window.crypto.subtle); | |||
} | |||
function pbkdf2Native(password, salt, iterations, keylen, digest) { | |||
var algorithms = { | |||
'sha1': 'SHA-1', | |||
'sha-1': 'SHA-1', | |||
'sha256': 'SHA-256', | |||
'sha-256': 'SHA-256', | |||
'sha512': 'SHA-512', | |||
'sha-512': 'SHA-512', | |||
}; | |||
return window.crypto.subtle.importKey('raw', new Buffer(password), 'PBKDF2', false, ['deriveKey']) | |||
.then(function (key) { | |||
var algo = { | |||
name: 'PBKDF2', | |||
salt: new Buffer(salt), | |||
iterations: iterations, | |||
hash: algorithms[digest.toLowerCase()], | |||
}; | |||
return window.crypto.subtle.deriveKey(algo, key, { | |||
name: 'AES-CTR', | |||
length: keylen * 8 | |||
}, true, ['encrypt', 'decrypt']); | |||
}) | |||
.then(function (derivedKey) { | |||
return window.crypto.subtle.exportKey('raw', derivedKey).then(function (keyArray) { | |||
return new Buffer(keyArray).toString('hex'); | |||
}); | |||
}); | |||
var algorithms = { | |||
'sha1': 'SHA-1', | |||
'sha-1': 'SHA-1', | |||
'sha256': 'SHA-256', | |||
'sha-256': 'SHA-256', | |||
'sha512': 'SHA-512', | |||
'sha-512': 'SHA-512', | |||
}; | |||
return window.crypto.subtle.importKey('raw', new Buffer(password), 'PBKDF2', false, ['deriveKey']) | |||
.then(function(key) { | |||
var algo = { | |||
name: 'PBKDF2', | |||
salt: new Buffer(salt), | |||
iterations: iterations, | |||
hash: algorithms[digest.toLowerCase()], | |||
}; | |||
return window.crypto.subtle.deriveKey(algo, key, { | |||
name: 'AES-CTR', | |||
length: keylen * 8 | |||
}, true, ['encrypt', 'decrypt']); | |||
}) | |||
.then(function(derivedKey) { | |||
return window.crypto.subtle.exportKey('raw', derivedKey).then(function(keyArray) { | |||
return new Buffer(keyArray).toString('hex'); | |||
}); | |||
}); | |||
} | |||
function pbkdf2Browserified(password, salt, iterations, keylen, digest) { | |||
return new Promise(function (resolve, reject) { | |||
pbkdf2.pbkdf2(password, salt, iterations, keylen, digest, function (error, key) { | |||
if (error) { | |||
reject('error in pbkdf2'); | |||
} else { | |||
resolve(key.toString('hex')); | |||
} | |||
}); | |||
return new Promise(function(resolve, reject) { | |||
pbkdf2.pbkdf2(password, salt, iterations, keylen, digest, function(error, key) { | |||
if (error) { | |||
reject('error in pbkdf2'); | |||
} else { | |||
resolve(key.toString('hex')); | |||
} | |||
}); | |||
}); | |||
} | |||
module.exports = shouldUseNative() ? pbkdf2Native : pbkdf2Browserified; | |||
module.exports = shouldUseNative() ? pbkdf2Native : pbkdf2Browserified; |
@@ -4,106 +4,106 @@ var Promise = require('pinkie-promise'); | |||
module.exports = { | |||
encryptLogin: encryptLogin, | |||
renderPassword: renderPassword, | |||
createFingerprint: createFingerprint, | |||
_deriveEncryptedLogin: deriveEncryptedLogin, | |||
_getPasswordTemplate: getPasswordTemplate, | |||
_prettyPrint: prettyPrint, | |||
_string2charCodes: string2charCodes, | |||
_getCharType: getCharType, | |||
_getPasswordChar: getPasswordChar, | |||
_createHmac: createHmac, | |||
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 pbkdf2(masterPassword, login, iterations, keylen, 'sha256'); | |||
var _options = options !== undefined ? options : {}; | |||
var iterations = _options.iterations || 8192; | |||
var keylen = _options.keylen || 32; | |||
return pbkdf2(masterPassword, login, iterations, keylen, 'sha256'); | |||
} | |||
function renderPassword(encryptedLogin, site, passwordOptions) { | |||
return deriveEncryptedLogin(encryptedLogin, site, passwordOptions).then(function (derivedEncryptedLogin) { | |||
var template = passwordOptions.template || getPasswordTemplate(passwordOptions); | |||
return prettyPrint(derivedEncryptedLogin, template); | |||
}); | |||
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')); | |||
}); | |||
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 _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); | |||
}); | |||
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; | |||
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 = ''; | |||
var password = ''; | |||
string2charCodes(hash).forEach(function (charCode, index) { | |||
var charType = getCharType(template, index); | |||
password += getPasswordChar(charType, charCode); | |||
}); | |||
return 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; | |||
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]; | |||
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]; | |||
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')) | |||
}); | |||
} | |||
return new Promise(function(resolve) { | |||
resolve(createHMAC('sha256', new Buffer(str)).digest('hex')) | |||
}); | |||
} |
@@ -2,83 +2,83 @@ var pbkdf2 = require('./pbkdf2'); | |||
var bigInt = require("big-integer"); | |||
module.exports = { | |||
generatePassword: generatePassword, | |||
_calcEntropy: calcEntropy, | |||
_consumeEntropy: consumeEntropy, | |||
_getSetOfCharacters: getSetOfCharacters, | |||
_getConfiguredRules: getConfiguredRules, | |||
_insertStringPseudoRandomly: insertStringPseudoRandomly, | |||
_getOneCharPerRule: getOneCharPerRule, | |||
_renderPassword: renderPassword | |||
generatePassword: generatePassword, | |||
_calcEntropy: calcEntropy, | |||
_consumeEntropy: consumeEntropy, | |||
_getSetOfCharacters: getSetOfCharacters, | |||
_getConfiguredRules: getConfiguredRules, | |||
_insertStringPseudoRandomly: insertStringPseudoRandomly, | |||
_getOneCharPerRule: getOneCharPerRule, | |||
_renderPassword: renderPassword | |||
}; | |||
function generatePassword(site, login, masterPassword, passwordProfile) { | |||
return calcEntropy(site, login, masterPassword, passwordProfile).then(function (entropy) { | |||
return renderPassword(entropy, passwordProfile); | |||
}); | |||
return calcEntropy(site, login, masterPassword, passwordProfile).then(function(entropy) { | |||
return renderPassword(entropy, passwordProfile); | |||
}); | |||
} | |||
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 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: '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~' | |||
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; | |||
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); | |||
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; | |||
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}; | |||
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]; | |||
}); | |||
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); | |||
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,29 +1,29 @@ | |||
module.exports = function (config) { | |||
var configuration = { | |||
basePath: '..', | |||
frameworks: ['mocha', 'chai'], | |||
files: [ | |||
'node_modules/big-integer/BigInteger.min.js', | |||
'dist/lesspass.js', | |||
'tests/**/*.js' | |||
], | |||
exclude: [ | |||
'tests/node.js', | |||
'tests/helper.js', | |||
'tests/karma.webcrypto.config.js', | |||
], | |||
preprocessors: {}, | |||
reporters: ['progress'], | |||
port: 9876, | |||
colors: true, | |||
logLevel: config.LOG_INFO, | |||
autoWatch: true, | |||
browsers: ['Chrome', 'Firefox'], | |||
singleRun: true, | |||
concurrency: Infinity | |||
}; | |||
if (process.env.TRAVIS) { | |||
configuration.browsers = ['PhantomJS']; | |||
} | |||
config.set(configuration) | |||
module.exports = function(config) { | |||
var configuration = { | |||
basePath: '..', | |||
frameworks: ['mocha', 'chai'], | |||
files: [ | |||
'node_modules/big-integer/BigInteger.min.js', | |||
'dist/lesspass.js', | |||
'tests/**/*.js' | |||
], | |||
exclude: [ | |||
'tests/node.js', | |||
'tests/helper.js', | |||
'tests/karma.webcrypto.config.js', | |||
], | |||
preprocessors: {}, | |||
reporters: ['progress'], | |||
port: 9876, | |||
colors: true, | |||
logLevel: config.LOG_INFO, | |||
autoWatch: true, | |||
browsers: ['Chrome', 'Firefox'], | |||
singleRun: true, | |||
concurrency: Infinity | |||
}; | |||
if (process.env.TRAVIS) { | |||
configuration.browsers = ['PhantomJS']; | |||
} | |||
config.set(configuration) | |||
}; |
@@ -5,19 +5,19 @@ const site = 'lesspass.com'; | |||
const login = 'contact@lesspass.com'; | |||
const masterPassword = 'password'; | |||
const passwordProfile = { | |||
lowercase: true, | |||
uppercase: true, | |||
numbers: true, | |||
symbols: true, | |||
length: 16, | |||
counter: 1, | |||
version: 2 | |||
lowercase: true, | |||
uppercase: true, | |||
numbers: true, | |||
symbols: true, | |||
length: 16, | |||
counter: 1, | |||
version: 2 | |||
}; | |||
LessPass.generatePassword(site, login, masterPassword, passwordProfile) | |||
.then(function (generatedPassword) { | |||
assert.equal(generatedPassword, '\\g-A1-.OHEwrXjT#'); | |||
console.log('generated password ok'); | |||
}) | |||
.catch(function (e) { | |||
console.log(e); | |||
}); | |||
.then(function(generatedPassword) { | |||
assert.equal(generatedPassword, '\\g-A1-.OHEwrXjT#'); | |||
console.log('generated password ok'); | |||
}) | |||
.catch(function(e) { | |||
console.log(e); | |||
}); |
@@ -1,13 +1,13 @@ | |||
var assert = chai.assert; | |||
describe('LessPass', function () { | |||
describe('pbkdf2', function () { | |||
it('should secret, salt, 2, 32, sha256', function () { | |||
return LessPass.pbkdf2('secret', 'salt', 2, 32, 'sha256').then(function (key) { | |||
assert.equal('f92f45f9df4c2aeabae1ed3c16f7b64660c1f8e377fa9b4699b23c2c3a29f569', key); | |||
}) | |||
}); | |||
describe('LessPass', function() { | |||
describe('pbkdf2', function() { | |||
it('should secret, salt, 2, 32, sha256', function() { | |||
return LessPass.pbkdf2('secret', 'salt', 2, 32, 'sha256').then(function(key) { | |||
assert.equal('f92f45f9df4c2aeabae1ed3c16f7b64660c1f8e377fa9b4699b23c2c3a29f569', key); | |||
}) | |||
}); | |||
}); | |||
}); | |||
@@ -1,84 +1,84 @@ | |||
var assert = chai.assert; | |||
describe('LessPass v1', function () { | |||
describe('deriveEncryptedLogin', function () { | |||
it('should createHmac', function () { | |||
var encryptedLogin = '9f505f3a95fe0485da3242cb81c9fe25c2f400d8399737655a8dad2b52778d88'; | |||
var salt = 'lesspass.com1'; | |||
return LessPass._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 LessPass._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 LessPass._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 LessPass._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 LessPass._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 LessPass._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 LessPass._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 LessPass._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'; | |||
LessPass._deriveEncryptedLogin(encryptedLogin, site).then(function (encryptedLogin) { | |||
assert.equal('be00f942fc8a', encryptedLogin); | |||
}); | |||
}); | |||
describe('LessPass v1', function() { | |||
describe('deriveEncryptedLogin', function() { | |||
it('should createHmac', function() { | |||
var encryptedLogin = '9f505f3a95fe0485da3242cb81c9fe25c2f400d8399737655a8dad2b52778d88'; | |||
var salt = 'lesspass.com1'; | |||
return LessPass._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 LessPass._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 LessPass._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 LessPass._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 LessPass._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 LessPass._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 LessPass._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 LessPass._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'; | |||
LessPass._deriveEncryptedLogin(encryptedLogin, site).then(function(encryptedLogin) { | |||
assert.equal('be00f942fc8a', encryptedLogin); | |||
}); | |||
}); | |||
}); | |||
}); | |||
@@ -1,63 +1,63 @@ | |||
var assert = chai.assert; | |||
describe('Lessass v1', function () { | |||
describe('getPasswordTemplate', function () { | |||
it('should get default template', function () { | |||
assert.equal('vcVCns', LessPass._getPasswordTemplate({ | |||
counter: 1, | |||
length: 12, | |||
lowercase: true, | |||
uppercase: true, | |||
numbers: true, | |||
symbols: true | |||
})); | |||
}); | |||
it('should get lowercase template', function () { | |||
assert.equal('vc', LessPass._getPasswordTemplate({ | |||
lowercase: true, | |||
uppercase: false, | |||
numbers: false, | |||
symbols: false | |||
})); | |||
}); | |||
it('should get uppercase template', function () { | |||
assert.equal('VC', LessPass._getPasswordTemplate({ | |||
lowercase: false, | |||
uppercase: true, | |||
numbers: false, | |||
symbols: false | |||
})); | |||
}); | |||
it('should get numbers template', function () { | |||
assert.equal('n', LessPass._getPasswordTemplate({ | |||
lowercase: false, | |||
uppercase: false, | |||
numbers: true, | |||
symbols: false | |||
})); | |||
}); | |||
it('should get symbols template', function () { | |||
assert.equal('s', LessPass._getPasswordTemplate({ | |||
lowercase: false, | |||
uppercase: false, | |||
numbers: false, | |||
symbols: true | |||
})); | |||
}); | |||
it('should concatenate template if two password settings', function () { | |||
assert.equal('vcVC', LessPass._getPasswordTemplate({ | |||
lowercase: true, | |||
uppercase: true, | |||
numbers: false, | |||
symbols: false | |||
})); | |||
assert.equal('vcns', LessPass._getPasswordTemplate({ | |||
lowercase: true, | |||
uppercase: false, | |||
numbers: true, | |||
symbols: true | |||
})); | |||
}); | |||
describe('Lessass v1', function() { | |||
describe('getPasswordTemplate', function() { | |||
it('should get default template', function() { | |||
assert.equal('vcVCns', LessPass._getPasswordTemplate({ | |||
counter: 1, | |||
length: 12, | |||
lowercase: true, | |||
uppercase: true, | |||
numbers: true, | |||
symbols: true | |||
})); | |||
}); | |||
it('should get lowercase template', function() { | |||
assert.equal('vc', LessPass._getPasswordTemplate({ | |||
lowercase: true, | |||
uppercase: false, | |||
numbers: false, | |||
symbols: false | |||
})); | |||
}); | |||
it('should get uppercase template', function() { | |||
assert.equal('VC', LessPass._getPasswordTemplate({ | |||
lowercase: false, | |||
uppercase: true, | |||
numbers: false, | |||
symbols: false | |||
})); | |||
}); | |||
it('should get numbers template', function() { | |||
assert.equal('n', LessPass._getPasswordTemplate({ | |||
lowercase: false, | |||
uppercase: false, | |||
numbers: true, | |||
symbols: false | |||
})); | |||
}); | |||
it('should get symbols template', function() { | |||
assert.equal('s', LessPass._getPasswordTemplate({ | |||
lowercase: false, | |||
uppercase: false, | |||
numbers: false, | |||
symbols: true | |||
})); | |||
}); | |||
it('should concatenate template if two password settings', function() { | |||
assert.equal('vcVC', LessPass._getPasswordTemplate({ | |||
lowercase: true, | |||
uppercase: true, | |||
numbers: false, | |||
symbols: false | |||
})); | |||
assert.equal('vcns', LessPass._getPasswordTemplate({ | |||
lowercase: true, | |||
uppercase: false, | |||
numbers: true, | |||
symbols: true | |||
})); | |||
}); | |||
}); | |||
}); | |||
@@ -1,35 +1,35 @@ | |||
var assert = chai.assert; | |||
describe('LessPass v1', function () { | |||
describe('prettyPrint', function () { | |||
it('should print different password if templates different', function () { | |||
var encryptedLogin = '78ae5892055ab59fdd54489ae30928d322841a27590b65cf875fcfdd083f7c32'; | |||
assert.notEqual(LessPass._prettyPrint(encryptedLogin, 'cv'), LessPass._prettyPrint(encryptedLogin, 'vc')); | |||
}); | |||
it('must return a string of the same length as the input', function () { | |||
var hash = 'f5785e569ab5d38b02e2248c798ac17df90f57a85f34a9d5382408c2f0d9532d'; | |||
assert.equal(hash.length, LessPass._prettyPrint(hash, 'cv').length); | |||
}); | |||
it('should return char inside a string based on modulo of the index', function () { | |||
var template = 'cv'; | |||
assert.equal('c', LessPass._getCharType(template, 0)); | |||
assert.equal('v', LessPass._getCharType(template, 1)); | |||
assert.equal('c', LessPass._getCharType(template, 10)); | |||
}); | |||
it('should convert a string into an array of char code', function () { | |||
var charCodes = LessPass._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', LessPass._getPasswordChar(typeVowel, 0)); | |||
}); | |||
it('should modulo if overflow', function () { | |||
var typeVowel = 'V'; | |||
assert.equal('E', LessPass._getPasswordChar(typeVowel, 1)); | |||
assert.equal('E', LessPass._getPasswordChar(typeVowel, 7)); | |||
}); | |||
describe('LessPass v1', function() { | |||
describe('prettyPrint', function() { | |||
it('should print different password if templates different', function() { | |||
var encryptedLogin = '78ae5892055ab59fdd54489ae30928d322841a27590b65cf875fcfdd083f7c32'; | |||
assert.notEqual(LessPass._prettyPrint(encryptedLogin, 'cv'), LessPass._prettyPrint(encryptedLogin, 'vc')); | |||
}); | |||
it('must return a string of the same length as the input', function() { | |||
var hash = 'f5785e569ab5d38b02e2248c798ac17df90f57a85f34a9d5382408c2f0d9532d'; | |||
assert.equal(hash.length, LessPass._prettyPrint(hash, 'cv').length); | |||
}); | |||
it('should return char inside a string based on modulo of the index', function() { | |||
var template = 'cv'; | |||
assert.equal('c', LessPass._getCharType(template, 0)); | |||
assert.equal('v', LessPass._getCharType(template, 1)); | |||
assert.equal('c', LessPass._getCharType(template, 10)); | |||
}); | |||
it('should convert a string into an array of char code', function() { | |||
var charCodes = LessPass._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', LessPass._getPasswordChar(typeVowel, 0)); | |||
}); | |||
it('should modulo if overflow', function() { | |||
var typeVowel = 'V'; | |||
assert.equal('E', LessPass._getPasswordChar(typeVowel, 1)); | |||
assert.equal('E', LessPass._getPasswordChar(typeVowel, 7)); | |||
}); | |||
}); | |||
}); |
@@ -1,79 +1,79 @@ | |||
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'; | |||
var passwordProfile = { | |||
lowercase: true, | |||
uppercase: true, | |||
numbers: true, | |||
symbols: true, | |||
length: 16, | |||
counter: 1, | |||
version: 2 | |||
}; | |||
return LessPass.generatePassword(site, login, masterPassword, passwordProfile).then(function (generatedPassword) { | |||
assert.equal('WHLpUL)e00[iHR+w', generatedPassword); | |||
}); | |||
}); | |||
it('render password no symbols', function () { | |||
this.timeout(10000); | |||
var site = 'example.org'; | |||
var login = 'contact@example.org'; | |||
var masterPassword = 'password'; | |||
var passwordProfile = { | |||
lowercase: true, | |||
uppercase: true, | |||
numbers: true, | |||
symbols: false, | |||
length: 14, | |||
counter: 2, | |||
version: 2 | |||
}; | |||
return LessPass.generatePassword(site, login, masterPassword, passwordProfile).then(function (generatedPassword) { | |||
assert.equal('MBAsB7b1Prt8Sl', generatedPassword); | |||
}); | |||
}); | |||
it('render password only digit', function () { | |||
this.timeout(10000); | |||
var site = 'example.org'; | |||
var login = 'contact@example.org'; | |||
var masterPassword = 'password'; | |||
var passwordProfile = { | |||
lowercase: false, | |||
uppercase: false, | |||
numbers: true, | |||
symbols: false, | |||
length: 6, | |||
counter: 3, | |||
version: 2 | |||
}; | |||
return LessPass.generatePassword(site, login, masterPassword, passwordProfile).then(function (generatedPassword) { | |||
assert.equal('117843', generatedPassword); | |||
}); | |||
}); | |||
it('render password no number', function () { | |||
this.timeout(10000); | |||
var site = 'example.org'; | |||
var login = 'contact@example.org'; | |||
var masterPassword = 'password'; | |||
var passwordProfile = { | |||
lowercase: true, | |||
uppercase: true, | |||
numbers: false, | |||
symbols: true, | |||
length: 14, | |||
counter: 1, | |||
version: 2 | |||
}; | |||
return LessPass.generatePassword(site, login, masterPassword, passwordProfile).then(function (generatedPassword) { | |||
assert.equal("sB>{qF}wN%/-fm", generatedPassword); | |||
}); | |||
}); | |||
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'; | |||
var passwordProfile = { | |||
lowercase: true, | |||
uppercase: true, | |||
numbers: true, | |||
symbols: true, | |||
length: 16, | |||
counter: 1, | |||
version: 2 | |||
}; | |||
return LessPass.generatePassword(site, login, masterPassword, passwordProfile).then(function(generatedPassword) { | |||
assert.equal('WHLpUL)e00[iHR+w', generatedPassword); | |||
}); | |||
}); | |||
it('render password no symbols', function() { | |||
this.timeout(10000); | |||
var site = 'example.org'; | |||
var login = 'contact@example.org'; | |||
var masterPassword = 'password'; | |||
var passwordProfile = { | |||
lowercase: true, | |||
uppercase: true, | |||
numbers: true, | |||
symbols: false, | |||
length: 14, | |||
counter: 2, | |||
version: 2 | |||
}; | |||
return LessPass.generatePassword(site, login, masterPassword, passwordProfile).then(function(generatedPassword) { | |||
assert.equal('MBAsB7b1Prt8Sl', generatedPassword); | |||
}); | |||
}); | |||
it('render password only digit', function() { | |||
this.timeout(10000); | |||
var site = 'example.org'; | |||
var login = 'contact@example.org'; | |||
var masterPassword = 'password'; | |||
var passwordProfile = { | |||
lowercase: false, | |||
uppercase: false, | |||
numbers: true, | |||
symbols: false, | |||
length: 6, | |||
counter: 3, | |||
version: 2 | |||
}; | |||
return LessPass.generatePassword(site, login, masterPassword, passwordProfile).then(function(generatedPassword) { | |||
assert.equal('117843', generatedPassword); | |||
}); | |||
}); | |||
it('render password no number', function() { | |||
this.timeout(10000); | |||
var site = 'example.org'; | |||
var login = 'contact@example.org'; | |||
var masterPassword = 'password'; | |||
var passwordProfile = { | |||
lowercase: true, | |||
uppercase: true, | |||
numbers: false, | |||
symbols: true, | |||
length: 14, | |||
counter: 1, | |||
version: 2 | |||
}; | |||
return LessPass.generatePassword(site, login, masterPassword, passwordProfile).then(function(generatedPassword) { | |||
assert.equal("sB>{qF}wN%/-fm", generatedPassword); | |||
}); | |||
}); | |||
}); | |||
}); | |||
@@ -1,58 +1,58 @@ | |||
var assert = chai.assert; | |||
describe('LessPass v2', function () { | |||
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 LessPass._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 LessPass._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 LessPass._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 LessPass._calcEntropy(site, login, masterPassword, passwordProfile2).then(function (entropy) { | |||
assert.equal('ddfb1136260f930c21f6d72f6eddbd40', entropy) | |||
}); | |||
}); | |||
it('consume entropy', function () { | |||
var password = LessPass._consumeEntropy('', bigInt(4 * 4 + 2), "abcd", 2); | |||
assert.equal('ca', password.value); | |||
assert.equal(1, password.entropy) | |||
}); | |||
describe('LessPass v2', function() { | |||
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 LessPass._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 LessPass._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 LessPass._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 LessPass._calcEntropy(site, login, masterPassword, passwordProfile2).then(function(entropy) { | |||
assert.equal('ddfb1136260f930c21f6d72f6eddbd40', entropy) | |||
}); | |||
}); | |||
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,70 +1,70 @@ | |||
var assert = chai.assert; | |||
describe('LessPass v2', function () { | |||
var defaultPasswordProfile = { | |||
length: 16, | |||
lowercase: true, | |||
uppercase: true, | |||
numbers: true, | |||
symbols: true | |||
describe('LessPass v2', function() { | |||
var defaultPasswordProfile = { | |||
length: 16, | |||
lowercase: true, | |||
uppercase: true, | |||
numbers: true, | |||
symbols: true | |||
}; | |||
it('render password use remainder of long division beetween entropy and set of chars length as an index', function() { | |||
var entropy = 'dc33d431bce2b01182c613382483ccdb0e2f66482cbba5e9d07dab34acc7eb1e'; | |||
assert.equal('W', LessPass._renderPassword(entropy, defaultPasswordProfile)[0]); | |||
}); | |||
it('render password use quotient as second entropy recursively', function() { | |||
var entropy = 'dc33d431bce2b01182c613382483ccdb0e2f66482cbba5e9d07dab34acc7eb1e'; | |||
assert.equal('H', LessPass._renderPassword(entropy, defaultPasswordProfile)[1]); | |||
}); | |||
it('render password has default length of 16', function() { | |||
var entropy = 'dc33d431bce2b01182c613382483ccdb0e2f66482cbba5e9d07dab34acc7eb1e'; | |||
assert.equal(16, LessPass._renderPassword(entropy, defaultPasswordProfile).length); | |||
}); | |||
it('render password can specify length', function() { | |||
var entropy = 'dc33d431bce2b01182c613382483ccdb0e2f66482cbba5e9d07dab34acc7eb1e'; | |||
var passwordProfile = { | |||
length: 20, | |||
lowercase: true, | |||
uppercase: true, | |||
numbers: true, | |||
symbols: true | |||
}; | |||
it('render password use remainder of long division beetween entropy and set of chars length as an index', function () { | |||
var entropy = 'dc33d431bce2b01182c613382483ccdb0e2f66482cbba5e9d07dab34acc7eb1e'; | |||
assert.equal('W', LessPass._renderPassword(entropy, defaultPasswordProfile)[0]); | |||
}); | |||
it('render password use quotient as second entropy recursively', function () { | |||
var entropy = 'dc33d431bce2b01182c613382483ccdb0e2f66482cbba5e9d07dab34acc7eb1e'; | |||
assert.equal('H', LessPass._renderPassword(entropy, defaultPasswordProfile)[1]); | |||
}); | |||
it('render password has default length of 16', function () { | |||
var entropy = 'dc33d431bce2b01182c613382483ccdb0e2f66482cbba5e9d07dab34acc7eb1e'; | |||
assert.equal(16, LessPass._renderPassword(entropy, defaultPasswordProfile).length); | |||
}); | |||
it('render password can specify length', function () { | |||
var entropy = 'dc33d431bce2b01182c613382483ccdb0e2f66482cbba5e9d07dab34acc7eb1e'; | |||
var passwordProfile = { | |||
length: 20, | |||
lowercase: true, | |||
uppercase: true, | |||
numbers: true, | |||
symbols: true | |||
}; | |||
assert.equal(20, LessPass._renderPassword(entropy, passwordProfile).length); | |||
}); | |||
it('include one char per set of characters', function () { | |||
var 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 () { | |||
var entropy = 'dc33d431bce2b01182c613382483ccdb0e2f66482cbba5e9d07dab34acc7eb1e'; | |||
var passwordProfile = { | |||
length: 6, | |||
lowercase: true, | |||
uppercase: true, | |||
numbers: true, | |||
symbols: true, | |||
}; | |||
var generatedPassword = LessPass._renderPassword(entropy, passwordProfile); | |||
var passwordLength = generatedPassword.length; | |||
var lowercaseOk = false; | |||
var uppercaseOk = false; | |||
var numbersOk = false; | |||
var 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) { | |||
numbersOk = true; | |||
} | |||
if ('!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'.indexOf(generatedPassword[passwordLength]) !== -1) { | |||
symbolsOk = true; | |||
} | |||
} | |||
assert.equal(6, generatedPassword.length); | |||
assert(lowercaseOk && uppercaseOk && numbersOk && symbolsOk, 'there is no at least one char in every characters set'); | |||
}); | |||
}); | |||
assert.equal(20, LessPass._renderPassword(entropy, passwordProfile).length); | |||
}); | |||
it('include one char per set of characters', function() { | |||
var 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() { | |||
var entropy = 'dc33d431bce2b01182c613382483ccdb0e2f66482cbba5e9d07dab34acc7eb1e'; | |||
var passwordProfile = { | |||
length: 6, | |||
lowercase: true, | |||
uppercase: true, | |||
numbers: true, | |||
symbols: true, | |||
}; | |||
var generatedPassword = LessPass._renderPassword(entropy, passwordProfile); | |||
var passwordLength = generatedPassword.length; | |||
var lowercaseOk = false; | |||
var uppercaseOk = false; | |||
var numbersOk = false; | |||
var 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) { | |||
numbersOk = true; | |||
} | |||
if ('!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'.indexOf(generatedPassword[passwordLength]) !== -1) { | |||
symbolsOk = true; | |||
} | |||
} | |||
assert.equal(6, generatedPassword.length); | |||
assert(lowercaseOk && uppercaseOk && numbersOk && symbolsOk, 'there is no at least one char in every characters set'); | |||
}); | |||
}); |
@@ -1,59 +1,59 @@ | |||
var assert = chai.assert; | |||
describe('LessPass v2', function () { | |||
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', 'numbers']); | |||
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 numbers', function () { | |||
var setOfCharacters = LessPass._getSetOfCharacters(['numbers']); | |||
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', 'numbers', 'symbols'], LessPass._getConfiguredRules({ | |||
lowercase: true, | |||
uppercase: true, | |||
symbols: true, | |||
numbers: true | |||
})); | |||
}); | |||
describe('LessPass v2', function() { | |||
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', 'numbers']); | |||
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 numbers', function() { | |||
var setOfCharacters = LessPass._getSetOfCharacters(['numbers']); | |||
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', 'numbers', 'symbols'], LessPass._getConfiguredRules({ | |||
lowercase: true, | |||
uppercase: true, | |||
symbols: true, | |||
numbers: true | |||
})); | |||
}); | |||
}); | |||
}); |
@@ -4,22 +4,22 @@ var webpack = require('webpack'); | |||
var production = process.env.NODE_ENV === 'production' || false; | |||
module.exports = { | |||
entry: './src/lesspass.js', | |||
output: { | |||
filename: production ? 'lesspass.min.js' : 'lesspass.js', | |||
path: path.resolve(__dirname, 'dist'), | |||
library: 'LessPass', | |||
libraryTarget: 'umd' | |||
}, | |||
module: { | |||
rules: [ | |||
{test: /\.js$/, exclude: /node_modules/, loader: "babel-loader"} | |||
] | |||
}, | |||
plugins: production ? [ | |||
new webpack.optimize.UglifyJsPlugin({ | |||
beautify: false, | |||
comments: false | |||
}) | |||
] : [] | |||
}; | |||
entry: './src/lesspass.js', | |||
output: { | |||
filename: production ? 'lesspass.min.js' : 'lesspass.js', | |||
path: path.resolve(__dirname, 'dist'), | |||
library: 'LessPass', | |||
libraryTarget: 'umd' | |||
}, | |||
module: { | |||
rules: [ | |||
{test: /\.js$/, exclude: /node_modules/, loader: "babel-loader"} | |||
] | |||
}, | |||
plugins: production ? [ | |||
new webpack.optimize.UglifyJsPlugin({ | |||
beautify: false, | |||
comments: false | |||
}) | |||
] : [] | |||
}; |