Browse Source

follow recommendation for pbkdf2

pull/44/head
Guillaume Vincent 9 years ago
parent
commit
bbb654e7e4
4 changed files with 134 additions and 39 deletions
  1. +11
    -12
      app/components/password-generator.vue
  2. +22
    -0
      app/lesspass.js
  3. +1
    -0
      package.json
  4. +100
    -27
      tests/lesspass.tests.js

+ 11
- 12
app/components/password-generator.vue View File

@@ -78,8 +78,7 @@
</template>

<script lang="babel">
import crypto from 'crypto';
import lesspass from '../lesspass';
import Lesspass from '../lesspass';

import Clipboard from 'clipboard';

@@ -87,6 +86,7 @@
export default {
data: function () {
return {
lesspass: new Lesspass(),
email: '',
password: '',
site: '',
@@ -98,18 +98,17 @@
},
computed: {
generatedPassword: function () {
var generatedPassword;
if (this.email && this.password && this.site) {
var masterPassword = crypto.pbkdf2Sync(this.email, this.password, 10, 64, 'sha256').toString('hex');

generatedPassword = lesspass.create_password(masterPassword, {
site_name: this.site,
password_length: this.length,
password_types: this.passwordTypes,
counter: this.counter
});
var entry = {
site: this.site,
password: {
length: this.length,
settings: this.passwordTypes,
counter: this.counter
}
};
return this.lesspass.createPassword(this.email, this.password, entry);
}
return generatedPassword;
}
}
}


+ 22
- 0
app/lesspass.js View File

@@ -1,6 +1,28 @@
import crypto from 'crypto';
export default class lesspass {
createPassword(email, password, entry) {
if (!this.masterPassword || this.email != email || this.password != password) {
this.email = email;
this.password = password;
this.masterPassword = lesspass._createMasterPassword(email, password);
}
var siteInformation = {
site_name: entry.site,
password_length: entry.password.length,
password_types: entry.password.settings,
counter: entry.password.counter
};
return lesspass.create_password(this.masterPassword, siteInformation);
}
static _createMasterPassword(email, password) {
var iterations = 8192;
var keylen = 32;
return crypto.pbkdf2Sync(password, email, iterations, keylen, 'sha256').toString('hex');
}
static create_password(masterPassword, siteInformation) {
var hash = this._create_hash(masterPassword, siteInformation);
var template = this._getTemplate(siteInformation.password_types);


+ 1
- 0
package.json View File

@@ -55,6 +55,7 @@
"nodemon": "latest",
"npm-run-all": "latest",
"rimraf": "latest",
"sinon": "^1.17.2",
"vue": "^1.0.12",
"vue-hot-reload-api": "^1.2.2",
"vueify": "^8.0.0",


+ 100
- 27
tests/lesspass.tests.js View File

@@ -1,7 +1,9 @@
import assert from 'assert';
import lesspass from '../app/lesspass';
import Lesspass from '../app/lesspass';

describe('lesspass', ()=> {
import sinon from 'sinon';

describe('LessPass', ()=> {
describe('public api', ()=> {
it('should create password', function () {
var master_password = "password";
@@ -11,14 +13,85 @@ describe('lesspass', ()=> {
'password_types': ['strong'],
'counter': 1
};
assert.equal('Vexu8[Syce4&', lesspass.create_password(master_password, site_information));
assert.equal('Vexu8[Syce4&', Lesspass.create_password(master_password, site_information));
});
it('should create password new API', function () {
var email = "test@lesspass.com";
var password = "password";
var entry = {
site: 'facebook',
password: {
length: 14,
settings: ['lowercase', 'uppercase', 'numbers', 'symbols'],
counter: 1
}
};
var lesspass = new Lesspass();
assert.equal('evYZ1_inUQ2[eb', lesspass.createPassword(email, password, entry));
});
});
describe('master password', () => {
it('should pbkdf2 email with password with 8192 iterations and use SHA 256 pseudo random function', ()=> {
var email = 'test@lesspass.com';
var password = "password";
assert.equal(
"90cff82b8847525370a8f29a59ecf45db62c719a535788ad0df58d32304e925d",
Lesspass._createMasterPassword(email, password)
);
});
it('should be length of 64', ()=> {
var email = 'test@lesspass.com';
var password = "password";
assert.equal(64, Lesspass._createMasterPassword(email, password).length);
});
});
describe('master password spy', () => {
var lesspass, createMasterPasswordStub, defaultEntry;
before(()=> {
lesspass = new Lesspass();
defaultEntry = {
site: 'facebook',
password: {
length: 14,
settings: ['lowercase', 'uppercase', 'numbers', 'symbols'],
counter: 1
}
};
});
beforeEach(() => {
createMasterPasswordStub = sinon.stub(Lesspass, '_createMasterPassword', () => 'masterpassword');
});
it('should call createMasterPassword only once', ()=> {
var email = "test@lesspass.com";
var password = "password";
lesspass.createPassword(email, password, defaultEntry);
lesspass.createPassword(email, password, defaultEntry);
assert.ok(createMasterPasswordStub.calledOnce);
});

it('should call createMasterPassword twice if email change', ()=> {
var password = "password";
lesspass.createPassword("admin@lesspass.com", password, defaultEntry);
lesspass.createPassword("test@lesspass.com", password, defaultEntry);
assert.ok(createMasterPasswordStub.calledTwice);
});

it('should call createMasterPassword twice if password change', ()=> {
var email = "test@lesspass.com";
lesspass.createPassword(email, "password1", defaultEntry);
lesspass.createPassword(email, "password2", defaultEntry);
assert.ok(createMasterPasswordStub.calledTwice);
});

afterEach(() => {
Lesspass._createMasterPassword.restore();
})
});
describe('hash', ()=> {
it('should have default length of 12', ()=> {
var master_password = "password";
var site_information = {'site_name': 'facebook'};
assert.equal(12, lesspass._create_hash(master_password, site_information).length);
assert.equal(12, Lesspass._create_hash(master_password, site_information).length);
});
it('should be able to create hash with defined length', ()=> {
var master_password = "password";
@@ -26,15 +99,15 @@ describe('lesspass', ()=> {
'site_name': 'facebook',
'password_length': 10
};
assert.equal(10, lesspass._create_hash(master_password, site_information).length);
assert.equal(10, Lesspass._create_hash(master_password, site_information).length);
});
it('should return two different passwords if site different', ()=> {
var master_password = "password";
var site_information = {'site_name': 'facebook'};
var site_information2 = {'site_name': 'google'};
assert.notEqual(
lesspass._create_hash(master_password, site_information),
lesspass._create_hash(master_password, site_information2)
Lesspass._create_hash(master_password, site_information),
Lesspass._create_hash(master_password, site_information2)
);
});
it('should return two different passwords if counter different', ()=> {
@@ -42,61 +115,61 @@ describe('lesspass', ()=> {
var old_site_information = {'site_name': 'facebook'};
var site_information = {'site_name': 'facebook', 'counter': 2};
assert.notEqual(
lesspass._create_hash(master_password, site_information),
lesspass._create_hash(master_password, old_site_information)
Lesspass._create_hash(master_password, site_information),
Lesspass._create_hash(master_password, old_site_information)
);
});
});
describe('password templates', ()=> {
it('should get default template from password type', ()=> {
assert.equal('Cvcvns', lesspass._getTemplate());
assert.equal('Cvcvns', Lesspass._getTemplate());
});
it('should get template from password type', ()=> {
assert.equal('vc', lesspass._getTemplate(['lowercase']));
assert.equal('VC', lesspass._getTemplate(['uppercase']));
assert.equal('n', lesspass._getTemplate(['numbers']));
assert.equal('s', lesspass._getTemplate(['symbols']));
assert.equal('vc', Lesspass._getTemplate(['lowercase']));
assert.equal('VC', Lesspass._getTemplate(['uppercase']));
assert.equal('n', Lesspass._getTemplate(['numbers']));
assert.equal('s', Lesspass._getTemplate(['symbols']));
});
it('should concatenate template if two password password_types', ()=> {
assert.equal('vcVC', lesspass._getTemplate(['lowercase', 'uppercase']));
assert.equal('vcns', lesspass._getTemplate(['lowercase', 'numbers', 'symbols']));
assert.equal('vcVC', Lesspass._getTemplate(['lowercase', 'uppercase']));
assert.equal('vcns', Lesspass._getTemplate(['lowercase', 'numbers', 'symbols']));
});
it('should not care about order of type in password password_types', ()=> {
assert.equal(
lesspass._getTemplate(['uppercase', 'lowercase']),
lesspass._getTemplate(['lowercase', 'uppercase'])
Lesspass._getTemplate(['uppercase', 'lowercase']),
Lesspass._getTemplate(['lowercase', 'uppercase'])
);
});
it('should return char inside template 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));
assert.equal('c', Lesspass._getCharType(template, 0));
assert.equal('v', Lesspass._getCharType(template, 1));
assert.equal('c', Lesspass._getCharType(template, 10));
});
});
describe('crypto', ()=> {
it('should convert a string into a char code table', ()=> {
var charCodes = lesspass._string2charCodes('ab40f6ee71');
var charCodes = Lesspass._string2charCodes('ab40f6ee71');
assert.equal(97, charCodes[0]);
assert.equal(98, charCodes[1]);
assert.equal(10, charCodes.length);
});
it('should return password size same size of hash given', function () {
var hash = 'Y2Vi2a112A';
assert.equal(10, lesspass._encode(hash, 'cv').length);
assert.equal(10, Lesspass._encode(hash, 'cv').length);
});
it('should return different values if templates are different', function () {
var hash = 'a';
assert.notEqual(lesspass._encode(hash, 'cv'), lesspass._encode(hash, 'vc'));
assert.notEqual(Lesspass._encode(hash, 'cv'), Lesspass._encode(hash, 'vc'));
});
it('should get password char based on its type and index', function () {
var typeVowel = 'V';
assert.equal('A', lesspass._getPasswordChar(typeVowel, 0));
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));
assert.equal('E', Lesspass._getPasswordChar(typeVowel, 1));
assert.equal('E', Lesspass._getPasswordChar(typeVowel, 7));
});
});
});

Loading…
Cancel
Save