@@ -1,124 +1,20 @@ | |||||
var pbkdf2 = require('pbkdf2'); | |||||
var createHmac = require('create-hmac'); | |||||
var Promise = require("bluebird"); | |||||
var v1 = require('./src/v1'); | |||||
var v2 = require('./src/v2'); | var v2 = require('./src/v2'); | ||||
module.exports = { | module.exports = { | ||||
encryptLogin: _encryptLogin, | |||||
renderPassword: _renderPassword, | |||||
createFingerprint: createFingerprint, | |||||
_deriveEncryptedLogin: _deriveEncryptedLogin, | |||||
_getPasswordTemplate: _getPasswordTemplate, | |||||
_prettyPrint: _prettyPrint, | |||||
_string2charCodes: _string2charCodes, | |||||
_getCharType: _getCharType, | |||||
_getPasswordChar: _getPasswordChar, | |||||
_createHmac: _createHmac, | |||||
encryptLogin: v1.encryptLogin, | |||||
renderPassword: v1.renderPassword, | |||||
createFingerprint: v1.createFingerprint, | |||||
_deriveEncryptedLogin: v1._deriveEncryptedLogin, | |||||
_getPasswordTemplate: v1._getPasswordTemplate, | |||||
_prettyPrint: v1._prettyPrint, | |||||
_string2charCodes: v1._string2charCodes, | |||||
_getCharType: v1._getCharType, | |||||
_getPasswordChar: v1._getPasswordChar, | |||||
_createHmac: v1._createHmac, | |||||
generatePassword: v2.generatePassword, | generatePassword: v2.generatePassword, | ||||
_calcEntropy: v2._calcEntropy, | _calcEntropy: v2._calcEntropy, | ||||
_getSetOfCharacters: v2._getSetOfCharacters, | _getSetOfCharacters: v2._getSetOfCharacters, | ||||
_renderPassword: v2._renderPassword, | _renderPassword: v2._renderPassword, | ||||
}; | |||||
function _encryptLogin(login, masterPassword, options) { | |||||
var _options = options !== undefined ? options : {}; | |||||
var iterations = _options.iterations || 8192; | |||||
var keylen = _options.keylen || 32; | |||||
return new Promise(function (resolve, reject) { | |||||
if (!login || !masterPassword) { | |||||
reject('login and master password parameters could not be empty'); | |||||
} | |||||
pbkdf2.pbkdf2(masterPassword, login, iterations, keylen, 'sha256', function (error, key) { | |||||
if (error) { | |||||
reject('error in pbkdf2'); | |||||
} else { | |||||
resolve(key.toString('hex')); | |||||
} | |||||
}); | |||||
}) | |||||
} | |||||
function _renderPassword(encryptedLogin, site, passwordOptions) { | |||||
return _deriveEncryptedLogin(encryptedLogin, site, passwordOptions).then(function (derivedEncryptedLogin) { | |||||
var template = passwordOptions.template || _getPasswordTemplate(passwordOptions); | |||||
return _prettyPrint(derivedEncryptedLogin, template); | |||||
}); | |||||
} | |||||
function _createHmac(encryptedLogin, salt) { | |||||
return new Promise(function (resolve) { | |||||
resolve(createHmac('sha256', new Buffer(encryptedLogin)).update(salt).digest('hex')); | |||||
}); | |||||
} | |||||
function _deriveEncryptedLogin(encryptedLogin, site, options) { | |||||
var _options = options !== undefined ? options : {}; | |||||
var length = _options.length || 12; | |||||
var counter = _options.counter || 1; | |||||
var salt = site + counter.toString(); | |||||
return _createHmac(encryptedLogin, salt).then(function (derivedHash) { | |||||
return derivedHash.substring(0, length); | |||||
}); | |||||
} | |||||
function _getPasswordTemplate(passwordTypes) { | |||||
var templates = { | |||||
lowercase: 'vc', | |||||
uppercase: 'VC', | |||||
numbers: 'n', | |||||
symbols: 's', | |||||
}; | |||||
var returnedTemplate = ''; | |||||
Object.keys(templates).forEach(function (template) { | |||||
if (passwordTypes.hasOwnProperty(template) && passwordTypes[template]) { | |||||
returnedTemplate += templates[template] | |||||
} | |||||
}); | |||||
return returnedTemplate; | |||||
} | |||||
function _prettyPrint(hash, template) { | |||||
var password = ''; | |||||
_string2charCodes(hash).forEach(function (charCode, index) { | |||||
var charType = _getCharType(template, index); | |||||
password += _getPasswordChar(charType, charCode); | |||||
}); | |||||
return password; | |||||
} | |||||
function _string2charCodes(text) { | |||||
var charCodes = []; | |||||
for (var i = 0; i < text.length; i++) { | |||||
charCodes.push(text.charCodeAt(i)); | |||||
} | |||||
return charCodes; | |||||
} | |||||
function _getCharType(template, index) { | |||||
return template[index % template.length]; | |||||
} | |||||
function _getPasswordChar(charType, index) { | |||||
var passwordsChars = { | |||||
V: 'AEIOUY', | |||||
C: 'BCDFGHJKLMNPQRSTVWXZ', | |||||
v: 'aeiouy', | |||||
c: 'bcdfghjklmnpqrstvwxz', | |||||
A: 'AEIOUYBCDFGHJKLMNPQRSTVWXZ', | |||||
a: 'AEIOUYaeiouyBCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz', | |||||
n: '0123456789', | |||||
s: '@&%?,=[]_:-+*$#!\'^~;()/.', | |||||
x: 'AEIOUYaeiouyBCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz0123456789@&%?,=[]_:-+*$#!\'^~;()/.' | |||||
}; | |||||
var passwordChar = passwordsChars[charType]; | |||||
return passwordChar[index % passwordChar.length]; | |||||
} | |||||
function createFingerprint(str) { | |||||
return new Promise(function (resolve) { | |||||
resolve(createHmac('sha256', new Buffer(str)).digest('hex')) | |||||
}); | |||||
} | |||||
}; |
@@ -0,0 +1,121 @@ | |||||
var pbkdf2 = require('pbkdf2'); | |||||
var createHMAC = require('create-hmac'); | |||||
var Promise = require("bluebird"); | |||||
module.exports = { | |||||
encryptLogin: encryptLogin, | |||||
renderPassword: renderPassword, | |||||
createFingerprint: createFingerprint, | |||||
_deriveEncryptedLogin: deriveEncryptedLogin, | |||||
_getPasswordTemplate: getPasswordTemplate, | |||||
_prettyPrint: prettyPrint, | |||||
_string2charCodes: string2charCodes, | |||||
_getCharType: getCharType, | |||||
_getPasswordChar: getPasswordChar, | |||||
_createHmac: createHmac, | |||||
}; | |||||
function encryptLogin(login, masterPassword, options) { | |||||
var _options = options !== undefined ? options : {}; | |||||
var iterations = _options.iterations || 8192; | |||||
var keylen = _options.keylen || 32; | |||||
return new Promise(function (resolve, reject) { | |||||
if (!login || !masterPassword) { | |||||
reject('login and master password parameters could not be empty'); | |||||
} | |||||
pbkdf2.pbkdf2(masterPassword, login, iterations, keylen, 'sha256', function (error, key) { | |||||
if (error) { | |||||
reject('error in pbkdf2'); | |||||
} else { | |||||
resolve(key.toString('hex')); | |||||
} | |||||
}); | |||||
}) | |||||
} | |||||
function renderPassword(encryptedLogin, site, passwordOptions) { | |||||
return deriveEncryptedLogin(encryptedLogin, site, passwordOptions).then(function (derivedEncryptedLogin) { | |||||
var template = passwordOptions.template || getPasswordTemplate(passwordOptions); | |||||
return prettyPrint(derivedEncryptedLogin, template); | |||||
}); | |||||
} | |||||
function createHmac(encryptedLogin, salt) { | |||||
return new Promise(function (resolve) { | |||||
resolve(createHMAC('sha256', new Buffer(encryptedLogin)).update(salt).digest('hex')); | |||||
}); | |||||
} | |||||
function deriveEncryptedLogin(encryptedLogin, site, options) { | |||||
var _options = options !== undefined ? options : {}; | |||||
var length = _options.length || 12; | |||||
var counter = _options.counter || 1; | |||||
var salt = site + counter.toString(); | |||||
return createHmac(encryptedLogin, salt).then(function (derivedHash) { | |||||
return derivedHash.substring(0, length); | |||||
}); | |||||
} | |||||
function getPasswordTemplate(passwordTypes) { | |||||
var templates = { | |||||
lowercase: 'vc', | |||||
uppercase: 'VC', | |||||
numbers: 'n', | |||||
symbols: 's', | |||||
}; | |||||
var returnedTemplate = ''; | |||||
Object.keys(templates).forEach(function (template) { | |||||
if (passwordTypes.hasOwnProperty(template) && passwordTypes[template]) { | |||||
returnedTemplate += templates[template] | |||||
} | |||||
}); | |||||
return returnedTemplate; | |||||
} | |||||
function prettyPrint(hash, template) { | |||||
var password = ''; | |||||
string2charCodes(hash).forEach(function (charCode, index) { | |||||
var charType = getCharType(template, index); | |||||
password += getPasswordChar(charType, charCode); | |||||
}); | |||||
return password; | |||||
} | |||||
function string2charCodes(text) { | |||||
var charCodes = []; | |||||
for (var i = 0; i < text.length; i++) { | |||||
charCodes.push(text.charCodeAt(i)); | |||||
} | |||||
return charCodes; | |||||
} | |||||
function getCharType(template, index) { | |||||
return template[index % template.length]; | |||||
} | |||||
function getPasswordChar(charType, index) { | |||||
var passwordsChars = { | |||||
V: 'AEIOUY', | |||||
C: 'BCDFGHJKLMNPQRSTVWXZ', | |||||
v: 'aeiouy', | |||||
c: 'bcdfghjklmnpqrstvwxz', | |||||
A: 'AEIOUYBCDFGHJKLMNPQRSTVWXZ', | |||||
a: 'AEIOUYaeiouyBCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz', | |||||
n: '0123456789', | |||||
s: '@&%?,=[]_:-+*$#!\'^~;()/.', | |||||
x: 'AEIOUYaeiouyBCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz0123456789@&%?,=[]_:-+*$#!\'^~;()/.' | |||||
}; | |||||
var passwordChar = passwordsChars[charType]; | |||||
return passwordChar[index % passwordChar.length]; | |||||
} | |||||
function createFingerprint(str) { | |||||
return new Promise(function (resolve) { | |||||
resolve(createHMAC('sha256', new Buffer(str)).digest('hex')) | |||||
}); | |||||
} |
@@ -2,7 +2,12 @@ var Promise = require("bluebird"); | |||||
var pbkdf2 = require('pbkdf2'); | var pbkdf2 = require('pbkdf2'); | ||||
var bigInt = require("big-integer"); | var bigInt = require("big-integer"); | ||||
exports.generatePassword = generatePassword; | |||||
module.exports = { | |||||
generatePassword: generatePassword, | |||||
_calcEntropy: calcEntropy, | |||||
_getSetOfCharacters: getSetOfCharacters, | |||||
_renderPassword: renderPassword | |||||
}; | |||||
function generatePassword(site, login, masterPassword, passwordProfile) { | function generatePassword(site, login, masterPassword, passwordProfile) { | ||||
return calcEntropy(site, login, masterPassword, passwordProfile).then(function (entropy) { | return calcEntropy(site, login, masterPassword, passwordProfile).then(function (entropy) { | ||||
@@ -11,11 +16,6 @@ function generatePassword(site, login, masterPassword, passwordProfile) { | |||||
}); | }); | ||||
} | } | ||||
exports._calcEntropy = calcEntropy; | |||||
exports._getSetOfCharacters = getSetOfCharacters; | |||||
exports._renderPassword = renderPassword; | |||||
function calcEntropy(site, login, masterPassword, passwordProfile) { | function calcEntropy(site, login, masterPassword, passwordProfile) { | ||||
return new Promise(function (resolve, reject) { | return new Promise(function (resolve, reject) { | ||||
var salt = site + login + passwordProfile.counter.toString(16); | var salt = site + login + passwordProfile.counter.toString(16); | ||||
@@ -3,6 +3,7 @@ var assert = chai.assert; | |||||
describe('LessPass v2', function () { | describe('LessPass v2', function () { | ||||
describe('API', function () { | describe('API', function () { | ||||
it('render password', function () { | it('render password', function () { | ||||
this.timeout(10000); | |||||
var site = 'example.org'; | var site = 'example.org'; | ||||
var login = 'contact@example.org'; | var login = 'contact@example.org'; | ||||
var masterPassword = 'password'; | var masterPassword = 'password'; | ||||
@@ -20,6 +21,7 @@ describe('LessPass v2', function () { | |||||
}); | }); | ||||
}); | }); | ||||
it('render password only digit', function () { | it('render password only digit', function () { | ||||
this.timeout(10000); | |||||
var site = 'example.org'; | var site = 'example.org'; | ||||
var login = 'contact@example.org'; | var login = 'contact@example.org'; | ||||
var masterPassword = 'password'; | var masterPassword = 'password'; | ||||
@@ -37,6 +39,7 @@ describe('LessPass v2', function () { | |||||
}); | }); | ||||
}); | }); | ||||
it('render password no number', function () { | it('render password no number', function () { | ||||
this.timeout(10000); | |||||
var site = 'example.org'; | var site = 'example.org'; | ||||
var login = 'contact@example.org'; | var login = 'contact@example.org'; | ||||
var masterPassword = 'password'; | var masterPassword = 'password'; | ||||