diff --git a/.gitignore b/.gitignore index a9f4ed5..1ca9571 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -lib -node_modules \ No newline at end of file +node_modules/ +npm-debug.log diff --git a/.npmignore b/.npmignore deleted file mode 100644 index 6bd575e..0000000 --- a/.npmignore +++ /dev/null @@ -1,2 +0,0 @@ -.travis.yml -tests \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 92a1e92..10d8531 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,3 @@ language: node_js node_js: - - 4 \ No newline at end of file + - 6 \ No newline at end of file diff --git a/index.js b/index.js index da82a42..bdfe301 100644 --- a/index.js +++ b/index.js @@ -9,6 +9,7 @@ module.exports = { _string2charCodes, _getCharType, _getPasswordChar, + _createHmac }; function _encryptLogin(login, masterPassword) { @@ -29,15 +30,23 @@ function _encryptLogin(login, masterPassword) { } function _renderPassword(encryptedLogin, site, passwordOptions) { - const derivedEncryptedLogin = this._deriveEncryptedLogin(encryptedLogin, site, passwordOptions); - const template = this._getPasswordTemplate(passwordOptions); - return this._prettyPrint(derivedEncryptedLogin, template); + return _deriveEncryptedLogin(encryptedLogin, site, passwordOptions).then(function (derivedEncryptedLogin) { + const template = _getPasswordTemplate(passwordOptions); + return _prettyPrint(derivedEncryptedLogin, template); + }); +} + +function _createHmac(encryptedLogin, salt) { + return new Promise(resolve => { + resolve(crypto.createHmac('sha256', encryptedLogin).update(salt).digest('hex')); + }); } function _deriveEncryptedLogin(encryptedLogin, site, passwordOptions = {length: 12, counter: 1}) { const salt = site + passwordOptions.counter.toString(); - const derivedHash = crypto.createHmac('sha256', encryptedLogin).update(salt).digest('hex'); - return derivedHash.substring(0, passwordOptions.length); + return _createHmac(encryptedLogin, salt).then(derivedHash => { + return derivedHash.substring(0, passwordOptions.length); + }); } function _getPasswordTemplate(passwordTypes) { @@ -59,9 +68,9 @@ function _getPasswordTemplate(passwordTypes) { function _prettyPrint(hash, template) { let password = ''; - this._string2charCodes(hash).forEach((charCode, index) => { - const charType = this._getCharType(template, index); - password += this._getPasswordChar(charType, charCode); + _string2charCodes(hash).forEach((charCode, index) => { + const charType = _getCharType(template, index); + password += _getPasswordChar(charType, charCode); }); return password; } diff --git a/package.json b/package.json index bc17313..aba9245 100644 --- a/package.json +++ b/package.json @@ -1,19 +1,21 @@ { "name": "lesspass", - "version": "4.0.4", + "version": "5.0.0", "author": "Guillaume Vincent ", - "description": "LessPass javascript module to generate idempotent passwords", + "description": "LessPass node module used to generate LessPass passwords", "main": "lib/index.js", "jsnext:main": "index.js", "repository": "lesspass/core", - "homepage": "https://github.com/lesspass/core#readme", - "bugs": "https://github.com/lesspass/core/issues", "scripts": { - "test-node": "cd tests && babel-node --presets es2015 node.js && cd ..", + "test:node": "npm run build && cd tests && babel-node --presets es2015 node.js && cd ..", "test": "ava --require babel-core/register", "build": "rimraf lib && babel index.js -d lib", "prepublish": "npm test && npm run build" }, + "files": [ + "lib/", + "index.js" + ], "keywords": [ "password", "crypto", diff --git a/readme.md b/readme.md index 93c270a..aab81fd 100644 --- a/readme.md +++ b/readme.md @@ -4,7 +4,7 @@ ## Requirements - - node v4.6.x + - node LTS v6 ## Install @@ -12,12 +12,12 @@ ## Usage - import LessPass from 'lesspass'; - - const site = 'lesspass.com'; - const login = 'contact@lesspass.com'; - const masterPassword = 'password'; - const options = { + var LessPass = require('lesspass'); + + var site = 'lesspass.com'; + var login = 'contact@lesspass.com'; + var masterPassword = 'password'; + var options = { counter: 1, length: 12, lowercase: true, @@ -25,11 +25,12 @@ numbers: true, symbols: true }; - LessPass.encryptLogin(login, masterPassword) - .then(encryptedLogin => { - var generatedPassword = LessPass.renderPassword(encryptedLogin, site, options); - console.log(generatedPassword); //azYS7,olOL2] + + LessPass.encryptLogin(login, masterPassword).then(encryptedLogin => { + LessPass.renderPassword(encryptedLogin, site, options).then(generatedPassword => { + console.log(generatedPassword); //azYS7,olOL2] }); + }); see [tests/api.tests.js](tests/api.tests.js) for more examples diff --git a/tests/api.tests.js b/tests/api.tests.js index 654724d..d753b81 100644 --- a/tests/api.tests.js +++ b/tests/api.tests.js @@ -1,36 +1,20 @@ import test from 'ava'; import lesspass from '../index'; -test('encrypt login with pbkdf2 8192 iterations and sha256', t => { - return lesspass.encryptLogin('test@example.org', 'password').then(encryptedLogin => { +test('should use pbkdf2 with 8192 iterations and sha256', t=> { + lesspass.encryptLogin('test@example.org', 'password').then(function (encryptedLogin) { t.is('d8af5f918db6b65b1db3d3984e5a400e39e1dbb19462220e4431de283809f472', encryptedLogin); - }) -}); - -test('encrypt login with utf8 parameter', t => { - return lesspass.encryptLogin('test@example.org', '♥ LessPass ♥').then(encryptedLogin => { - t.is('063092c809334979f505df88ed37845d298c01f7e8a03cbd661edbc084c650ca', encryptedLogin); - }) + }); }); -test('render password', t => { - const site = 'lesspass.com'; - const encryptedLogin = '63d850713d0b2f7f2c4396fe93f4ac0c6bc7485f9e7473c4b8c4a33ec12199c0'; - const passwordOptions = { - counter: 1, - length: 12, - lowercase: true, - uppercase: true, - numbers: true, - symbols: true - }; - t.is('azYS7,olOL2]', lesspass.renderPassword(encryptedLogin, site, passwordOptions)); +test('should allow utf8 parameter', t=> { + lesspass.encryptLogin('test@example.org', '♥ LessPass ♥').then(function (encryptedLogin) { + t.is('997fe81d3d0db236e039c75efdb487f17a902fdf94f9dacaa9884329c85d9651', encryptedLogin); + }); }); - - test('auto generated encrypt login tests', t => { - const promises = []; - const passwords = [ + var promises = []; + var passwords = [ { login: 'contact@lesspass.com', masterPassword: 'password', @@ -88,20 +72,34 @@ test('auto generated encrypt login tests', t => { } ]; - for (const entry of passwords) { + for (var entry of passwords) { promises.push(lesspass.encryptLogin(entry.login, entry.masterPassword)); } - t.plan(passwords.length); return Promise.all(promises).then(values => { for (let i = 0; i < values.length; i++) { t.is(passwords[i].encryptedLogin, values[i]); } }); }); - -test('auto generated derive encrypted login tests', t => { - const passwords = [ +test('render password', t => { + var site = 'lesspass.com'; + var encryptedLogin = '63d850713d0b2f7f2c4396fe93f4ac0c6bc7485f9e7473c4b8c4a33ec12199c0'; + var passwordOptions = { + counter: 1, + length: 12, + lowercase: true, + uppercase: true, + numbers: true, + symbols: true + }; + lesspass.renderPassword(encryptedLogin, site, passwordOptions).then(function (generatedPassword) { + t.is('azYS7,olOL2]', generatedPassword); + }) +}); +test('auto generated render password tests', t => { + var promises = []; + var passwords = [ { encryptedLogin: '63d850713d0b2f7f2c4396fe93f4ac0c6bc7485f9e7473c4b8c4a33ec12199c0', site: 'lesspass.com', @@ -225,18 +223,22 @@ test('auto generated derive encrypted login tests', t => { } ]; - - t.plan(passwords.length); - for (let i = 0; i < passwords.length; i++) { - let password = passwords[i]; - let passwordOption = { - counter: password.counter, - length: password.length, - lowercase: password.lowercase, - uppercase: password.uppercase, - numbers: password.numbers, - symbols: password.symbols, + for (var entry of passwords) { + var passwordOption = { + counter: entry.counter, + length: entry.length, + lowercase: entry.lowercase, + uppercase: entry.uppercase, + numbers: entry.numbers, + symbols: entry.symbols, }; - t.is(password.generatedPassword, lesspass.renderPassword(password.encryptedLogin, password.site, passwordOption)); + promises.push(lesspass.renderPassword(entry.encryptedLogin, entry.site, passwordOption)); } + + return Promise.all(promises).then(values => { + for (let i = 0; i < values.length; i++) { + t.is(passwords[i].generatedPassword, values[i]); + } + }); }); + diff --git a/tests/deriveEncryptedLogin.tests.js b/tests/deriveEncryptedLogin.tests.js index 79ad944..002954c 100644 --- a/tests/deriveEncryptedLogin.tests.js +++ b/tests/deriveEncryptedLogin.tests.js @@ -1,12 +1,13 @@ import test from 'ava'; import lesspass from '../index'; -test('should derive encrypted login with default length', t => { - const encryptedLogin = '9f505f3a95fe0485da3242cb81c9fe25c2f400d8399737655a8dad2b52778d88'; - const site = 'lesspass.com'; - t.is(12, lesspass._deriveEncryptedLogin(encryptedLogin, site).length); +test('should createHmac', t => { + var encryptedLogin = '9f505f3a95fe0485da3242cb81c9fe25c2f400d8399737655a8dad2b52778d88'; + var salt = 'lesspass.com1'; + return lesspass._createHmac(encryptedLogin, salt).then(hmac => { + t.is('be00f942fc8aa67d8e76fc2456862b9d66d166ebfdd3dc2f0116e278209532ed', hmac); + }); }); - test('should derive encrypted login with default options', t => { const encryptedLogin = '90cff82b8847525370a8f29a59ecf45db62c719a535788ad0df58d32304e925d'; const site = 'lesspass.com'; @@ -18,30 +19,32 @@ test('should derive encrypted login with default options', t => { numbers: true, symbols: true, }; - t.is( - lesspass._deriveEncryptedLogin(encryptedLogin, site), - lesspass._deriveEncryptedLogin(encryptedLogin, site, option) - ); + var p1 = lesspass._deriveEncryptedLogin(encryptedLogin, site); + var p2 = lesspass._deriveEncryptedLogin(encryptedLogin, site, option); + Promise.all([p1, p2]).then(generatedPasswords => { + t.is(generatedPasswords[0], generatedPasswords[1]) + }); }); - test('should derive encrypted login with defined length', t => { - const encryptedLogin = 'd79d8482f708122288af7b259393a58fe05840f4555cc935cdd3f062b9aa75ed'; - const site = 'lesspass.com'; - const option = { + var encryptedLogin = 'd79d8482f708122288af7b259393a58fe05840f4555cc935cdd3f062b9aa75ed'; + var site = 'lesspass.com'; + var option = { counter: 1, length: 10, }; - t.is(10, lesspass._deriveEncryptedLogin(encryptedLogin, site, option).length); + return lesspass._deriveEncryptedLogin(encryptedLogin, site, option).then(function (generatedPassword) { + t.is(10, generatedPassword.length); + }) }); - test('should return two different passwords if site different', t => { const encryptedLogin = 'f4fd3885fb70085f2285c3382e2d9adb4c2553285fc45dd896791aa5e79070a9'; const site = 'google.com'; const site2 = 'facebook.com'; - t.not( - lesspass._deriveEncryptedLogin(encryptedLogin, site), - lesspass._deriveEncryptedLogin(encryptedLogin, site2) - ); + var p1 = lesspass._deriveEncryptedLogin(encryptedLogin, site); + var p2 = lesspass._deriveEncryptedLogin(encryptedLogin, site2); + Promise.all([p1, p2]).then(derivedEncryptedLogins => { + t.not(derivedEncryptedLogins[0], derivedEncryptedLogins[1]) + }); }); test('should return two different passwords if counter different', t => { @@ -49,14 +52,17 @@ test('should return two different passwords if counter different', t => { const site = 'lesspass.com'; const option = {counter: 1}; const option2 = {counter: 2}; - t.not( - lesspass._deriveEncryptedLogin(encryptedLogin, site, option), - lesspass._deriveEncryptedLogin(encryptedLogin, site, option2) - ); + var p1 = lesspass._deriveEncryptedLogin(encryptedLogin, site, option); + var p2 = lesspass._deriveEncryptedLogin(encryptedLogin, site, option2); + Promise.all([p1, p2]).then(derivedEncryptedLogins => { + t.not(derivedEncryptedLogins[0], derivedEncryptedLogins[1]) + }); }); test('should derive encrypted login with sha 256', t => { const encryptedLogin = '9f505f3a95fe0485da3242cb81c9fe25c2f400d8399737655a8dad2b52778d88'; const site = 'lesspass.com'; - t.is('be00f942fc8a', lesspass._deriveEncryptedLogin(encryptedLogin, site)); + return lesspass._deriveEncryptedLogin(encryptedLogin, site).then(function (encryptedLogin) { + t.is('be00f942fc8a', encryptedLogin); + }); }); diff --git a/tests/node.js b/tests/node.js index e5b15a1..a148b28 100644 --- a/tests/node.js +++ b/tests/node.js @@ -1,4 +1,4 @@ -var lesspass = require('../lib/index'); +var LessPass = require('../lib/index'); var assert = require('assert'); var site = 'lesspass.com'; @@ -13,13 +13,13 @@ var options = { symbols: true }; -var generatedPassword; -lesspass.encryptLogin(login, masterPassword) - .then(function (encryptedLogin) { - generatedPassword = lesspass.renderPassword(encryptedLogin, site, options); - assert.equal(generatedPassword, 'azYS7,olOL2]'); - console.log('generated password ok'); +LessPass.encryptLogin(login, masterPassword) + .then(encryptedLogin => { + LessPass.renderPassword(encryptedLogin, site, options).then(generatedPassword => { + assert.equal(generatedPassword, 'azYS7,olOL2]'); + console.log('generated password ok'); + }); }) .catch(e => { console.log(e); - }); + }); \ No newline at end of file