Use hmac instead of a second pbkdf
This commit is contained in:
parent
df52a0ef95
commit
49fbf2c849
4 changed files with 51 additions and 28 deletions
|
|
@ -4,6 +4,8 @@ var buffers = require('buffer');
|
||||||
var querystring = require('querystring');
|
var querystring = require('querystring');
|
||||||
var Identity = require('../models/Identity');
|
var Identity = require('../models/Identity');
|
||||||
|
|
||||||
|
var SEPARATOR = '|';
|
||||||
|
|
||||||
function InsightStorage(config) {
|
function InsightStorage(config) {
|
||||||
this.type = 'DB';
|
this.type = 'DB';
|
||||||
this.storeUrl = config.url || 'https://test-insight.bitpay.com:443/api/email';
|
this.storeUrl = config.url || 'https://test-insight.bitpay.com:443/api/email';
|
||||||
|
|
@ -15,6 +17,7 @@ InsightStorage.prototype.init = function () {};
|
||||||
InsightStorage.prototype.setCredentials = function(email, password, opts) {
|
InsightStorage.prototype.setCredentials = function(email, password, opts) {
|
||||||
this.email = email;
|
this.email = email;
|
||||||
this.password = password;
|
this.password = password;
|
||||||
|
this._cachedKey = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
InsightStorage.prototype.createItem = function(name, value, callback) {
|
InsightStorage.prototype.createItem = function(name, value, callback) {
|
||||||
|
|
@ -35,24 +38,23 @@ function mayBeOldPassword(password) {
|
||||||
}
|
}
|
||||||
|
|
||||||
InsightStorage.prototype.getItem = function(name, callback) {
|
InsightStorage.prototype.getItem = function(name, callback) {
|
||||||
var key = cryptoUtil.kdf(this.password + this.email);
|
var passphrase = this.getPassphrase();
|
||||||
var secret = this.makeSecret(key);
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
this._makeGetRequest(secret, name, function(err, body) {
|
this._makeGetRequest(passphrase, name, function(err, body) {
|
||||||
if (err && err.indexOf('PNOTFOUND') !== -1 && mayBeOldPassword(self.password)) {
|
if (err && err.indexOf('PNOTFOUND') !== -1 && mayBeOldPassword(self.password)) {
|
||||||
return self._brokenGetItem(key, name, callback);
|
return self._brokenGetItem(name, callback);
|
||||||
}
|
}
|
||||||
return callback(err, body);
|
return callback(err, body);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
InsightStorage.prototype.makeSecret = function(key) {
|
InsightStorage.prototype.getPassphrase = function() {
|
||||||
return cryptoUtil.kdf(key + this.password);
|
return cryptoUtil.hmac(this.getKey(), this.password);
|
||||||
};
|
};
|
||||||
|
|
||||||
InsightStorage.prototype._makeGetRequest = function(secret, key, callback) {
|
InsightStorage.prototype._makeGetRequest = function(passphrase, key, callback) {
|
||||||
var authHeader = new Buffer(this.email + ':' + secret).toString('base64');
|
var authHeader = new buffers.Buffer(this.email + ':' + passphrase).toString('base64');
|
||||||
var retrieveUrl = this.storeUrl + '/retrieve';
|
var retrieveUrl = this.storeUrl + '/retrieve';
|
||||||
this.request.get({
|
this.request.get({
|
||||||
url: retrieveUrl + '?' + querystring.encode({key: key}),
|
url: retrieveUrl + '?' + querystring.encode({key: key}),
|
||||||
|
|
@ -73,12 +75,12 @@ InsightStorage.prototype._makeGetRequest = function(secret, key, callback) {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
InsightStorage.prototype._brokenGetItem = function(key, name, callback) {
|
InsightStorage.prototype._brokenGetItem = function(name, callback) {
|
||||||
var secret = this._makeBrokenSecret(key);
|
var passphrase = this._makeBrokenSecret();
|
||||||
var self = this;
|
var self = this;
|
||||||
this._makeGetRequest(secret, name, function(err, body) {
|
this._makeGetRequest(passphrase, name, function(err, body) {
|
||||||
if (!err) {
|
if (!err) {
|
||||||
return self._changePassword(function(err) {
|
return self._changePassphrase(function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
@ -89,22 +91,29 @@ InsightStorage.prototype._brokenGetItem = function(key, name, callback) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
InsightStorage.prototype._makeBrokenSecret = function(key) {
|
InsightStorage.prototype.getKey = function() {
|
||||||
|
if (!this._cachedKey) {
|
||||||
|
this._cachedKey = cryptoUtil.kdf(this.password + SEPARATOR + this.email);
|
||||||
|
}
|
||||||
|
return this._cachedKey;
|
||||||
|
};
|
||||||
|
|
||||||
|
InsightStorage.prototype._makeBrokenSecret = function() {
|
||||||
|
var key = cryptoUtil.kdf(this.password + this.email);
|
||||||
return cryptoUtil.kdf(key, this.password);
|
return cryptoUtil.kdf(key, this.password);
|
||||||
};
|
};
|
||||||
|
|
||||||
InsightStorage.prototype._changePassword = function(callback) {
|
InsightStorage.prototype._changePassphrase = function(callback) {
|
||||||
var key = cryptoUtil.kdf(this.password + this.email);
|
var passphrase = this._makeBrokenSecret();
|
||||||
var secret = this._makeBrokenSecret(key);
|
var newPassphrase = this.getPassphrase();
|
||||||
var newSecret = this.makeSecret(key);
|
var authHeader = new buffers.Buffer(this.email + ':' + passphrase).toString('base64');
|
||||||
|
|
||||||
var url = this.storeUrl + '/change_passphrase';
|
var url = this.storeUrl + '/change_passphrase';
|
||||||
this.request.post({
|
this.request.post({
|
||||||
url: url,
|
url: url,
|
||||||
|
headers: {'Authorization': authHeader},
|
||||||
body: querystring.encode({
|
body: querystring.encode({
|
||||||
email: this.email,
|
newPassphrase: newPassphrase
|
||||||
secret: secret,
|
|
||||||
newSecret: newSecret
|
|
||||||
})
|
})
|
||||||
}, function(err, response, body) {
|
}, function(err, response, body) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|
@ -121,15 +130,14 @@ InsightStorage.prototype._changePassword = function(callback) {
|
||||||
};
|
};
|
||||||
|
|
||||||
InsightStorage.prototype.setItem = function(name, value, callback) {
|
InsightStorage.prototype.setItem = function(name, value, callback) {
|
||||||
var key = cryptoUtil.kdf(this.password + this.email);
|
var passphrase = this.getPassphrase();
|
||||||
var secret = this.makeSecret(key);
|
var authHeader = new buffers.Buffer(this.email + ':' + passphrase).toString('base64');
|
||||||
var registerUrl = this.storeUrl + '/register';
|
var registerUrl = this.storeUrl + '/register';
|
||||||
this.request.post({
|
this.request.post({
|
||||||
url: registerUrl,
|
url: registerUrl,
|
||||||
|
headers: {'Authorization': authHeader},
|
||||||
body: querystring.encode({
|
body: querystring.encode({
|
||||||
key: name,
|
key: name,
|
||||||
email: this.email,
|
|
||||||
secret: secret,
|
|
||||||
record: value
|
record: value
|
||||||
})
|
})
|
||||||
}, function(err, response, body) {
|
}, function(err, response, body) {
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,17 @@ module.exports = {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} key
|
||||||
|
* @param {string} data
|
||||||
|
* @return {string} base64 encoded hmac
|
||||||
|
*/
|
||||||
|
hmac: function(key, data) {
|
||||||
|
return sjcl.codec.base64.fromBits(
|
||||||
|
new sjcl.misc.hmac(key, sjcl.hash.sha256).encrypt(data)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} password
|
* @param {string} password
|
||||||
* @param {string} salt - base64 encoded, defaults to 'mjuBtGybi/4='
|
* @param {string} salt - base64 encoded, defaults to 'mjuBtGybi/4='
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,7 @@ describe('insight storage plugin', function() {
|
||||||
|
|
||||||
var oldSecret = 'rFA+F/N+ZvKXp717zBdfCKYQ5v9Fjry0W6tautj5etIH'
|
var oldSecret = 'rFA+F/N+ZvKXp717zBdfCKYQ5v9Fjry0W6tautj5etIH'
|
||||||
+ 'KLQliZBEYXA7AXjTJ9K3DglzGWJKost3QJUCMbhM/A=='
|
+ 'KLQliZBEYXA7AXjTJ9K3DglzGWJKost3QJUCMbhM/A=='
|
||||||
var newSecret = '2YTcmtkmC/WSVrfmjAEyOmDnmntVQ8A9wS/q1DbD6rkc'
|
var newSecret = '+72pwnQ/ukrXVXZ/L4vFeiykwn522uVz0J6p81TGXvI=';
|
||||||
+ 'si8LrIvS2Ru85Feb5Bvk2ziQbEN6PzlL1mIyQ3a+Vw=='
|
|
||||||
|
|
||||||
var setupStorageCredentials = function() {
|
var setupStorageCredentials = function() {
|
||||||
storage.setCredentials(email, password);
|
storage.setCredentials(email, password);
|
||||||
|
|
@ -122,8 +121,10 @@ describe('insight storage plugin', function() {
|
||||||
var url = requestMock.post.firstCall.args[0].url;
|
var url = requestMock.post.firstCall.args[0].url;
|
||||||
var args = querystring.decode(receivedArgs);
|
var args = querystring.decode(receivedArgs);
|
||||||
assert(url.indexOf('change_passphrase') !== -1);
|
assert(url.indexOf('change_passphrase') !== -1);
|
||||||
assert(args.secret === oldSecret);
|
assert(requestMock.post.firstCall.args[0].headers.Authorization
|
||||||
assert(args.newSecret === newSecret);
|
===
|
||||||
|
new Buffer(email + ':' + oldSecret).toString('base64'));
|
||||||
|
assert(args.newPassphrase === newSecret);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,9 @@ describe('crypto utils', function() {
|
||||||
});
|
});
|
||||||
it('should generate a passphrase from weird chars', function() {
|
it('should generate a passphrase from weird chars', function() {
|
||||||
var phrase = cryptoUtils.kdf('Pwd123!@#$%^&*(){}[]\|/?.>,<=+-_`~åéþ䲤þçæ¶');
|
var phrase = cryptoUtils.kdf('Pwd123!@#$%^&*(){}[]\|/?.>,<=+-_`~åéþ䲤þçæ¶');
|
||||||
|
var expected = 'CZwb5KdikvZHVsEoZUdJckAy+yyzGnd++XhyqxJXbc30'
|
||||||
|
+ 'pEoO+WqHgqBbdf0gn2wiyWZv3zymB+7L75Xnz3uSlg==';
|
||||||
|
phrase.should.equal(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue