This is my first Node module, as well as the first time using Promises in Javascript. It is a client for the KeePass plugin "KeePassHTTP" to expose passwords securely, which I am planning on using to pass credentials to gulp.
All feedback welcome!
// Code based on https://github.com/belaviyo/keepass-macpass-helper
// Keepass HTTP protocol documentation - https://github.com/pfn/keepasshttp
const sjcl = require('./sjcl').sjcl; // not using npm module because it doesn't have support for cbc
const nconf = require('nconf');
const rp = require('request-promise-native');
let port = null;
let key = null;
let id = null;
function iv(len = 16) {
let iv = [];
for (let i = 0; i < len; i++) {
iv.push(String.fromCharCode(Math.floor(Math.random() * 256)));
}
return new Buffer(iv.join(''), 'binary').toString('base64');
}
function encrypt(data, iv) {
const enc = sjcl.mode.cbc.encrypt(
new sjcl.cipher.aes(sjcl.codec.base64.toBits(key)),
sjcl.codec.utf8String.toBits(data),
sjcl.codec.base64.toBits(iv)
);
return sjcl.codec.base64.fromBits(enc);
}
function decrypt(data, iv) {
const dec = sjcl.mode.cbc.decrypt(
new sjcl.cipher.aes(sjcl.codec.base64.toBits(key)),
sjcl.codec.base64.toBits(data),
sjcl.codec.base64.toBits(iv)
);
return sjcl.codec.utf8String.fromBits(dec);
}
function verify(request) {
const nonce = iv();
request['Nonce'] = nonce;
request['Verifier'] = encrypt(nonce, nonce);
if (id) {
request['Id'] = id;
}
return request;
}
function post(request) {
return rp({
method: 'POST',
uri: `http://localhost:${port}`,
body: request,
json: true
});
}
function init() {
return new Promise(function(resolve) {
nconf.file({file: '.keepass'});
nconf.defaults({
port: 19455
});
port = nconf.get('port');
key = nconf.get('key');
id = nconf.get('id');
if (!key) {
key = iv(32);
nconf.set('key', key);
nconf.save();
}
resolve();
});
}
function test() {
return new Promise((resolve, reject) => {
let request = {
RequestType: 'test-associate',
TriggerUnlock: false,
};
request = verify(request);
post(request)
.then(response => {
if (response && response['Success']) {
resolve(response);
} else {
reject(response);
}
})
.catch(response => reject(response));
});
}
function associate() {
return new Promise((resolve, reject) => {
let request = {
RequestType: 'associate',
Key: key
};
request = verify(request);
post(request)
.then(response => {
if (response && response['Success']) {
id = response['Id'];
nconf.set('id', id);
nconf.save();
resolve(response);
} else {
reject(response);
}
})
.catch(response => reject(response));
});
}
function logins(url) {
return new Promise((resolve, reject) => {
let request = {
RequestType: 'get-logins',
TriggerUnlock: 'false',
SortSelection: 'false',
};
request = verify(request);
const iv = request['Nonce'];
request['Url'] = encrypt(url, iv);
post(request)
.then(response => {
if (response && response['Entries']) {
let nonce = response.Nonce;
response.Entries = response.Entries.map(entry => {
return Object.assign(entry, {
Login: decrypt(entry.Login, nonce),
Name: decrypt(entry.Name, nonce),
Password: decrypt(entry.Password, nonce)
})
});
resolve(response);
} else {
reject(response);
}
})
.catch(response => reject(response));
});
}
// itl: init -> test -> logins
function itl(url) {
return new Promise((resolve, reject) => {
init()
.then(() => test())
.then(() => logins(url))
.then(response => resolve(response))
.catch(() => {
associate()
.then(() => logins(url))
.then(response => resolve(response))
.catch(response => reject(response));
});
});
}
module.exports = {
init,
test,
associate,
logins,
itl,
};
Example Usage:
const keepass = require('./keepass/keepass.js');
keepass.itl('www.example.com')
.then(result => console.log('Success', result))
.catch(result => console.log('Error', result))