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