Permalink
Browse files

intergrate new crypto into client

  • Loading branch information...
1 parent 1f95c56 commit b56f1fa8cfda5db6bf422e47db5c622eb406eb4d @zaach zaach committed Jan 4, 2013
Showing with 67 additions and 35 deletions.
  1. +30 −14 client/client.js
  2. +17 −7 client/crypto.js
  3. +1 −0 lib/api/v1/payload.js
  4. +1 −1 package.json
  5. +2 −1 test/api.v1.payload.js
  6. +3 −2 test/crypto.derive.js
  7. +13 −10 test/crypto.sign.js
View
44 client/client.js
@@ -16,8 +16,8 @@ GombotClient = function(path, options) {
this.port = url.port;
this.path = url.path || '';
- this.user = options.user;
- this.authKey = options.authKey;
+ this.user = options.user;
+ this.keys = options.keys;
};
var xhr = typeof jQuery !== 'undefined' ? jQuery.ajax : require('xhrequest');
@@ -51,6 +51,9 @@ function request(args, cb) {
processData: false,
error: function(data, res, status) {
if (cb) cb('Error: ' + data + '\nStatus: ' + status);
+ },
+ complete: function () {
+ console.log('request complete');
}
};
if (method == 'PUT' || method == 'POST') {
@@ -67,7 +70,7 @@ function authRequest(args, cb) {
// compute the authKey
GombotCrypto.sign({
email: args.email,
- key: args.key,
+ keys: args.keys,
url: url,
host: args.host,
port: args.port,
@@ -86,7 +89,7 @@ function mergeArgs(args, def) {
args.scheme = def.scheme;
args.host = args.host || def.host;
args.port = args.port || def.port;
- args.key = args.key || def.authKey;
+ args.keys = args.keys || def.keys;
args.email = args.email || def.user;
return args;
}
@@ -104,6 +107,7 @@ GombotClient.prototype = {
request(args, cb);
},
+ // create an account
account: function(args, cb) {
var self = this;
@@ -116,21 +120,24 @@ GombotClient.prototype = {
email: args.email,
password: args.pass
}, function(err, r) {
- self.authKey = r.authKey;
- self.user = args.email;
+ self.keys = r;
+ self.user = args.email;
- args.data = JSON.stringify({email: args.email, pass: r.authKey, newsletter: args.newsletter});
+ args.data = JSON.stringify({email: args.email, pass: GombotCrypto.hexToBase64(r.authKey), newsletter: args.newsletter});
// send request with authKey as the password
request(args, cb);
});
},
+ // check auth status
status: function(args, cb) {
args = mergeArgs(args, this);
args.method = 'get';
args.path = this.path + '/v1/status';
authRequest(args, cb);
},
+ // derives auth key using credentials then checks
+ // auth status using derived keys
signIn: function(args, cb) {
var self = this;
// compute the authKey
@@ -139,12 +146,12 @@ GombotClient.prototype = {
password: args.pass
}, function(err, r) {
if (err) return cb(err);
- var key = args.key = r.authKey;
+ var keys = args.keys = r;
self.status(args, function (err, r) {
if (!err && r.success) {
- self.authKey = key;
- self.user = args.email;
+ self.keys = keys;
+ self.user = args.email;
}
if (cb) cb(err, r);
});
@@ -154,16 +161,25 @@ GombotClient.prototype = {
args = mergeArgs(args, this);
args.method = 'put';
args.path = this.path + '/v1/payload';
- args.data = JSON.stringify({payload: args.payload});
-
- authRequest(args, cb);
+ GombotCrypto.encrypt(this.keys, JSON.stringify(args.payload), function (err, cipherText) {
+ if (err) return cb(err);
+ args.data = JSON.stringify({payload: cipherText});
+ authRequest(args, cb);
+ });
},
getPayload: function(args, cb) {
args = mergeArgs(args, this);
args.method = 'get';
args.path = this.path + '/v1/payload';
- authRequest(args, cb);
+ var keys = this.keys;
+ authRequest(args, function (err, data) {
+ if (err) return cb(err);
+ GombotCrypto.decrypt(keys, data.payload, function (err, plaintext) {
+ if (err) return cb(err);
+ cb(null, {success: data.success, payload: JSON.parse(plaintext), updated: data.updated});
+ });
+ });
},
getTimestamp: function(args, cb) {
args = mergeArgs(args, this);
View
24 client/crypto.js
@@ -1,8 +1,6 @@
if (typeof sjcl === 'undefined') {
var sjcl = require('./sjcl-with-cbc.js');
- // we use an HMAC tag to check message integrity, to use CBC safely
- sjcl.beware["CBC mode is dangerous because it doesn't protect message integrity."]();
//var Hawk = require('hawk');
}
@@ -11,6 +9,9 @@ if (typeof URLParse === 'undefined') {
}
var GombotCrypto = (function() {
+ // we use an HMAC tag to check message integrity, to use CBC safely
+ sjcl.beware["CBC mode is dangerous because it doesn't protect message integrity."]();
+
// the number of rounds used in PBKDF2 to generate a stretched derived
// key from a user password.
var PBKDF2_ROUNDS = 250000;
@@ -42,7 +43,14 @@ var GombotCrypto = (function() {
};
return {
+ hexToBase64: function (hex) {
+ return sjcl.codec.base64.fromBits(sjcl.codec.hex.toBits(hex));
+ },
seed: function(entropy, cb) {
+ if (typeof window !== 'undefined') sjcl.random.startCollectors();
+ sjcl.random.addEntropy(entropy, entropy.length * 6, 'server');
+ if (!sjcl.random.isReady())
+ return cb(new Error("sjcl seeded with insufficient entropy"));
setTimeout(cb, 0);
},
derive: function(args, cb) {
@@ -57,7 +65,7 @@ var GombotCrypto = (function() {
// yes, this async break is artificial for now, but is web worker
// compatible in the future.
setTimeout(function() {
- var pbkdf2 = sjc.misc.pbkdf2;
+ var pbkdf2 = sjcl.misc.pbkdf2;
var bA = sjcl.bitArray;
var str2bits = sjcl.codec.utf8String.toBits;
var secret = str2bits(""); // for the future
@@ -85,6 +93,8 @@ var GombotCrypto = (function() {
var str2bits = sjcl.codec.utf8String.toBits;
var bits2b64 = sjcl.codec.base64.fromBits;
var hex2bits = sjcl.codec.hex.toBits;
+ if (!sjcl.random.isReady())
+ return cb(new Error("sjcl.random is not ready, cannot create IV"));
var IV = sjcl.random.randomWords(16/4);
var ct = sjcl.mode.cbc.encrypt(new sjcl.cipher.aes(hex2bits(keys.aesKey)),
str2bits(plainText), IV);
@@ -138,8 +148,8 @@ var GombotCrypto = (function() {
// payload may be omitted (as for GET requests)
if (!args.payload) args.payload = '';
- if (typeof args.key !== 'string')
- throw new Error(".key is required and must be a string");
+ if (!args.keys || typeof args.keys.authKey !== 'string')
+ throw new Error(".keys.authKey is required and must be a string");
if (typeof args.email !== 'string')
throw new Error(".email is required and must be a string");
if (typeof args.date !== 'number')
@@ -160,7 +170,7 @@ var GombotCrypto = (function() {
args.method = args.method.toUpperCase();
// how about if the key is poorly formated?
- var keyBits = sjcl.codec.base64.toBits(args.key);
+ var keyBits = sjcl.codec.hex.toBits(args.keys.authKey);
var url = URLParse(args.url);
// add a port if default is in use
@@ -193,7 +203,7 @@ var GombotCrypto = (function() {
} else {
var credentials = {
id: args.email,
- key: new Buffer(args.key, 'base64'),
+ key: new Buffer(args.keys.authKey, 'hex'),
algorithm: 'hmac-sha-256'
};
View
1 lib/api/v1/payload.js
@@ -41,6 +41,7 @@ module.exports = [
function put(request) {
var id = request.session.id;
var payload = request.payload.payload;
+ console.log('storing payload', payload);
db.storePayload(id, payload, function(err) {
if (err) return request.reply(Hapi.Error.internal("Could not store payload: " + err));
request.reply({
View
2 package.json
@@ -25,7 +25,7 @@
"grunt-css": ">=0.2.x"
},
"scripts": {
- "test": "mocha -R spec -t 10000",
+ "test": "mocha -R spec -t 5000",
"start": "node scripts/run_locally.js"
}
}
View
3 test/api.v1.payload.js
@@ -27,7 +27,7 @@ function createAccount(email, pass, cb) {
pass: pass
}, function(err, r) {
if (err) cb(err);
- cb(null, client.key);
+ cb(null);
});
}
@@ -50,6 +50,7 @@ describe("/api/v1/payload", function() {
it ('should get payload', function(done) {
try {
client.getPayload({}, function(err, r) {
+ console.error('??????', r);
should.not.exist(err);
should.exist(r);
should.exist(r.updated);
View
5 test/crypto.derive.js
@@ -21,8 +21,9 @@ describe('GumbotCrypto.derive', function() {
GombotCrypto.derive({ email: 'foo', password: 'bar' }, function(err, rez) {
should.not.exist(err);
should.exist(rez);
- (rez.authKey).should.equal('LmsKnYuun7gFgw7CfxBX3CHW7oylp6bpGzo950x8ZG0=');
- (rez.cryptKey).should.equal('I0aVNap4YYwJItXT409giaxMA4K313Q+iHerYBsgtu4=');
+ (rez.authKey).should.equal('56c1b9bbc2bd675dd1a7ee67f1f5ad425a15f5e4898879981fedf0010b968452');
+ (rez.aesKey).should.equal('4940455f814509ddc808c85489dc31069e6d4e63be67c948b9e9272e3f742fda');
+ (rez.hmacKey).should.equal('e949ab6e3ddeaa91ebe5138f89eccabd40eb08161df77d1679392085b5e95dd3');
done();
});
});
View
23 test/crypto.sign.js
@@ -2,35 +2,38 @@ const
should = require('should'),
GombotCrypto = require('../client/crypto.js');
+var seed = Buffer(require("crypto").randomBytes(32), "binary").toString('base64');
+GombotCrypto.seed(seed, function () {});
+
describe('GumbotCrypto.sign', function() {
it('should throw when required parameters are missing', function(done) {
(function(){
GombotCrypto.sign();
- }).should.throw('.key is required and must be a string');
+ }).should.throw('.keys.authKey is required and must be a string');
(function(){
- GombotCrypto.sign({ key: "foo" });
+ GombotCrypto.sign({ keys: {authKey: "foo"} });
}).should.throw('.email is required and must be a string');
(function(){
- GombotCrypto.sign({ key: "foo", email: "bar" });
+ GombotCrypto.sign({ keys: {authKey: "foo"}, email: "bar" });
}).should.throw('.date is required and must be a javascript Date object or an integer representing seconds since epoch');
(function(){
GombotCrypto.sign({
- key: "foo",
+ keys: {authKey: "foo"},
email: "bar",
date: new Date(),
payload: 45 // bogus!
});
}).should.throw('.payload must be a string if supplied');
(function(){
GombotCrypto.sign({
- key: "foo",
+ keys: {authKey: "foo"},
email: "bar",
date: new Date(),
payload: 'x' });
}).should.throw('.url is required and must be a string');
(function(){
GombotCrypto.sign({
- key: "foo",
+ keys: {authKey: "foo"},
email: "bar",
date: new Date(),
payload: 'x',
@@ -39,7 +42,7 @@ describe('GumbotCrypto.sign', function() {
}).should.throw('.method must be an allowable HTTP method');
(function(){
GombotCrypto.sign({
- key: "foo",
+ keys: {authKey: "foo"},
email: "bar",
date: new Date(),
payload: 'x',
@@ -49,7 +52,7 @@ describe('GumbotCrypto.sign', function() {
}).should.throw('.nonce should be a random string');
(function(){
GombotCrypto.sign({
- key: "foo",
+ keys: {authKey: "foo"},
email: "bar",
date: new Date(),
payload: 'x',
@@ -66,7 +69,7 @@ describe('GumbotCrypto.sign', function() {
GombotCrypto.derive({ email: 'foo', password: 'bar' }, function(err, rez) {
should.not.exist(err);
GombotCrypto.sign({
- key: rez.cryptKey,
+ keys: {authKey: rez.authKey },
email: "bar",
date: new Date(1352177818454),
payload: 'x',
@@ -77,7 +80,7 @@ describe('GumbotCrypto.sign', function() {
should.not.exist(err);
should.exist(rez);
(rez.Authorization).should.be.a('string');
- (rez.Authorization).should.equal('Hawk id="bar", ts="1352177818", ext="one time only please", mac="PUiIAcoruPIarsFALtc/1lTThcYVDmzZHtvuluHHvmQ="');
+ (rez.Authorization).should.equal('Hawk id="bar", ts="1352177818", ext="one time only please", mac="6b+dMvKSWdAD0hFHR/Ik3MzkIS6mPdzRc31DGNM8dbI="');
done();
});
});

0 comments on commit b56f1fa

Please sign in to comment.