add AuthMessage and fix browser tests
This commit is contained in:
parent
cb88dd185a
commit
e44b2480aa
|
@ -44,6 +44,7 @@ requireWhenAccessed('EncodedData', './util/EncodedData');
|
||||||
requireWhenAccessed('VersionedData', './util/VersionedData');
|
requireWhenAccessed('VersionedData', './util/VersionedData');
|
||||||
requireWhenAccessed('BinaryParser', './util/BinaryParser');
|
requireWhenAccessed('BinaryParser', './util/BinaryParser');
|
||||||
requireWhenAccessed('Address', './lib/Address');
|
requireWhenAccessed('Address', './lib/Address');
|
||||||
|
requireWhenAccessed('AuthMessage', './lib/AuthMessage');
|
||||||
requireWhenAccessed('HierarchicalKey', './lib/HierarchicalKey');
|
requireWhenAccessed('HierarchicalKey', './lib/HierarchicalKey');
|
||||||
Object.defineProperty(module.exports, 'BIP32', {
|
Object.defineProperty(module.exports, 'BIP32', {
|
||||||
get: function() {
|
get: function() {
|
||||||
|
|
|
@ -13,6 +13,7 @@ var puts = function(error, stdout, stderr) {
|
||||||
var modules = [
|
var modules = [
|
||||||
'lib/Address',
|
'lib/Address',
|
||||||
'lib/Armory',
|
'lib/Armory',
|
||||||
|
'lib/AuthMessage',
|
||||||
'lib/Base58',
|
'lib/Base58',
|
||||||
'lib/HierarchicalKey',
|
'lib/HierarchicalKey',
|
||||||
'lib/BIP39',
|
'lib/BIP39',
|
||||||
|
@ -141,6 +142,9 @@ var createTestData = function() {
|
||||||
tb.require('./test/testdata', {
|
tb.require('./test/testdata', {
|
||||||
expose: 'testdata'
|
expose: 'testdata'
|
||||||
});
|
});
|
||||||
|
tb.require('sinon', {
|
||||||
|
expose: 'sinon'
|
||||||
|
});
|
||||||
tb.transform('brfs');
|
tb.transform('brfs');
|
||||||
|
|
||||||
return tb.bundle();
|
return tb.bundle();
|
||||||
|
|
File diff suppressed because one or more lines are too long
4594
browser/testdata.js
4594
browser/testdata.js
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,148 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Message = require('./Message');
|
||||||
|
var ECIES = require('./ECIES');
|
||||||
|
|
||||||
|
/* Encrypted, authenticated messages to be shared between copayers */
|
||||||
|
var AuthMessage = function() {
|
||||||
|
};
|
||||||
|
|
||||||
|
AuthMessage.encode = function(topubkey, fromkey, payload, opts) {
|
||||||
|
var version1 = new Buffer([1]); //peers will reject messges containing not-understood version1
|
||||||
|
//i.e., increment version1 to prevent communications with old clients
|
||||||
|
var version2 = new Buffer([0]); //peers will not reject messages containing not-understood version2
|
||||||
|
//i.e., increment version2 to allow communication with old clients, but signal new clients
|
||||||
|
|
||||||
|
if (opts && opts.nonce && Buffer.isBuffer(opts.nonce) && opts.nonce.length == 8) {
|
||||||
|
var nonce = opts.nonce;
|
||||||
|
} else {
|
||||||
|
var nonce = new Buffer(8);
|
||||||
|
nonce.fill(0); //nonce is a big endian 8 byte number
|
||||||
|
}
|
||||||
|
|
||||||
|
var toencrypt = Buffer.concat([version1, version2, nonce, payload]);
|
||||||
|
var toencrypthexbuf = new Buffer(toencrypt.toString('hex')); //due to bug in sjcl/bitcore, must use hex string
|
||||||
|
var encrypted = AuthMessage._encrypt(topubkey, toencrypthexbuf);
|
||||||
|
var sig = AuthMessage._sign(fromkey, encrypted);
|
||||||
|
var encoded = {
|
||||||
|
pubkey: fromkey.public.toString('hex'),
|
||||||
|
sig: sig.toString('hex'),
|
||||||
|
encrypted: encrypted.toString('hex')
|
||||||
|
};
|
||||||
|
return encoded;
|
||||||
|
};
|
||||||
|
|
||||||
|
AuthMessage.decode = function(key, encoded, opts) {
|
||||||
|
if (opts && opts.prevnonce && Buffer.isBuffer(opts.prevnonce) && opts.prevnonce.length == 8) {
|
||||||
|
var prevnonce = opts.prevnonce;
|
||||||
|
} else {
|
||||||
|
var prevnonce = new Buffer(8);
|
||||||
|
prevnonce.fill(0); //nonce is a big endian 8 byte number
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var frompubkey = new Buffer(encoded.pubkey, 'hex');
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('Error decoding public key: ' + e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var sig = new Buffer(encoded.sig, 'hex');
|
||||||
|
var encrypted = new Buffer(encoded.encrypted, 'hex');
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('Error decoding data: ' + e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var v = AuthMessage._verify(frompubkey, sig, encrypted);
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('Error verifying signature: ' + e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!v) {
|
||||||
|
throw new Error('Invalid signature');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var decryptedhexbuf = AuthMessage._decrypt(key.private, encrypted);
|
||||||
|
var decrypted = new Buffer(decryptedhexbuf.toString(), 'hex'); //workaround for bug in bitcore/sjcl
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('Cannot decrypt data: ' + e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var version1 = decrypted[0];
|
||||||
|
var version2 = decrypted[1];
|
||||||
|
var nonce = decrypted.slice(2, 10);
|
||||||
|
var payload = decrypted.slice(10);
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('Cannot parse decrypted data: ' + e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payload.length === 0) {
|
||||||
|
throw new Error('No data present');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version1 !== 1) {
|
||||||
|
throw new Error('Invalid version number');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version2 !== 0) {
|
||||||
|
//put special version2 handling code here, if ever needed
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!AuthMessage._noncegt(nonce, prevnonce) && prevnonce.toString('hex') !== '0000000000000000') {
|
||||||
|
throw new Error('Nonce not equal to zero and not greater than the previous nonce');
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoded = {
|
||||||
|
version1: version1,
|
||||||
|
version2: version2,
|
||||||
|
nonce: nonce,
|
||||||
|
payload: payload
|
||||||
|
};
|
||||||
|
|
||||||
|
return decoded;
|
||||||
|
};
|
||||||
|
|
||||||
|
//return true if nonce > prevnonce; false otherwise
|
||||||
|
AuthMessage._noncegt = function(nonce, prevnonce) {
|
||||||
|
var noncep1 = nonce.slice(0, 4).readUInt32BE(0);
|
||||||
|
var prevnoncep1 = prevnonce.slice(0, 4).readUInt32BE(0);
|
||||||
|
|
||||||
|
if (noncep1 > prevnoncep1)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (noncep1 < prevnoncep1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var noncep2 = nonce.slice(4, 8).readUInt32BE(0);
|
||||||
|
var prevnoncep2 = prevnonce.slice(4, 8).readUInt32BE(0);
|
||||||
|
|
||||||
|
if (noncep2 > prevnoncep2)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
AuthMessage._encrypt = function(topubkey, payload, r, iv) {
|
||||||
|
var encrypted = ECIES.encrypt(topubkey, payload, r, iv);
|
||||||
|
return encrypted;
|
||||||
|
};
|
||||||
|
|
||||||
|
AuthMessage._decrypt = function(privkey, encrypted) {
|
||||||
|
var decrypted = ECIES.decrypt(privkey, encrypted);
|
||||||
|
return decrypted;
|
||||||
|
};
|
||||||
|
|
||||||
|
AuthMessage._sign = function(key, payload) {
|
||||||
|
var sig = Message.sign(payload, key);
|
||||||
|
return sig;
|
||||||
|
};
|
||||||
|
|
||||||
|
AuthMessage._verify = function(pubkey, signature, payload) {
|
||||||
|
var v = Message.verifyWithPubKey(pubkey, payload, signature);
|
||||||
|
return v;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = AuthMessage;
|
|
@ -17,6 +17,7 @@
|
||||||
<script src="adapter.js"></script>
|
<script src="adapter.js"></script>
|
||||||
|
|
||||||
<script src="test.Address.js"></script>
|
<script src="test.Address.js"></script>
|
||||||
|
<script src="test.AuthMessage.js"></script>
|
||||||
<script src="test.Base58.js"></script>
|
<script src="test.Base58.js"></script>
|
||||||
<script src="test.basic.js"></script>
|
<script src="test.basic.js"></script>
|
||||||
<script src="test.Bignum.js"></script>
|
<script src="test.Bignum.js"></script>
|
||||||
|
|
|
@ -0,0 +1,160 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var chai = chai || require('chai');
|
||||||
|
var should = chai.should();
|
||||||
|
var sinon = require('sinon');
|
||||||
|
var bitcore = bitcore || require('../bitcore');
|
||||||
|
var AuthMessage = bitcore.AuthMessage;
|
||||||
|
var Key = bitcore.Key;
|
||||||
|
var util = bitcore.util;
|
||||||
|
|
||||||
|
describe('AuthMessage model', function() {
|
||||||
|
var key = new Key();
|
||||||
|
key.private = util.sha256(new Buffer('test'));
|
||||||
|
key.regenerateSync();
|
||||||
|
|
||||||
|
var key2 = new Key();
|
||||||
|
key2.private = util.sha256(new Buffer('test 2'));
|
||||||
|
key2.regenerateSync();
|
||||||
|
|
||||||
|
describe('#encode', function() {
|
||||||
|
|
||||||
|
it('should encode a message', function() {
|
||||||
|
var message = new Buffer('message');
|
||||||
|
var encoded = AuthMessage.encode(key2.public, key, message);
|
||||||
|
should.exist(encoded.pubkey);
|
||||||
|
should.exist(encoded.sig);
|
||||||
|
should.exist(encoded.encrypted);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#decode', function() {
|
||||||
|
|
||||||
|
it('should decode an encoded message', function() {
|
||||||
|
var message = new Buffer('message');
|
||||||
|
var messagehex = message.toString('hex');
|
||||||
|
var encoded = AuthMessage.encode(key2.public, key, message);
|
||||||
|
|
||||||
|
var decoded = AuthMessage.decode(key2, encoded);
|
||||||
|
var payload = decoded.payload;
|
||||||
|
payload.toString('hex').should.equal(messagehex);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should decode an encoded message with proper prevnonce', function() {
|
||||||
|
var message = new Buffer('message');
|
||||||
|
var messagehex = message.toString('hex');
|
||||||
|
var nonce = new Buffer([0, 0, 0, 0, 0, 0, 0, 2]);
|
||||||
|
var opts = {nonce: nonce};
|
||||||
|
var encoded = AuthMessage.encode(key2.public, key, message, opts);
|
||||||
|
|
||||||
|
var prevnonce = new Buffer([0, 0, 0, 0, 0, 0, 0, 1]);
|
||||||
|
opts = {prevnonce: prevnonce};
|
||||||
|
var decoded = AuthMessage.decode(key2, encoded, opts);
|
||||||
|
var payload = decoded.payload;
|
||||||
|
payload.toString('hex').should.equal(messagehex);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should decode an encoded message with proper prevnonce - for first part', function() {
|
||||||
|
var message = new Buffer('message');
|
||||||
|
var messagehex = message.toString('hex');
|
||||||
|
var nonce = new Buffer([0, 0, 0, 2, 0, 0, 0, 0]);
|
||||||
|
var opts = {nonce: nonce};
|
||||||
|
var encoded = AuthMessage.encode(key2.public, key, message, opts);
|
||||||
|
|
||||||
|
var prevnonce = new Buffer([0, 0, 0, 1, 0, 0, 0, 0]);
|
||||||
|
opts = {prevnonce: prevnonce};
|
||||||
|
var decoded = AuthMessage.decode(key2, encoded, opts);
|
||||||
|
var payload = decoded.payload;
|
||||||
|
payload.toString('hex').should.equal(messagehex);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail if prevnonce is too high', function() {
|
||||||
|
var message = new Buffer('message');
|
||||||
|
var messagehex = message.toString('hex');
|
||||||
|
var nonce = new Buffer([0, 0, 0, 0, 0, 0, 0, 1]);
|
||||||
|
var opts = {nonce: nonce};
|
||||||
|
var encoded = AuthMessage.encode(key2.public, key, message, opts);
|
||||||
|
|
||||||
|
var prevnonce = new Buffer([0, 0, 0, 0, 0, 0, 0, 1]);
|
||||||
|
opts = {prevnonce: prevnonce};
|
||||||
|
(function() {AuthMessage.decode(key2, encoded, opts)}).should.throw('Nonce not equal to zero and not greater than the previous nonce');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail if prevnonce is too high - for first part', function() {
|
||||||
|
var message = new Buffer('message');
|
||||||
|
var messagehex = message.toString('hex');
|
||||||
|
var nonce = new Buffer([0, 0, 0, 1, 0, 0, 0, 0]);
|
||||||
|
var opts = {nonce: nonce};
|
||||||
|
var encoded = AuthMessage.encode(key2.public, key, message, opts);
|
||||||
|
|
||||||
|
var prevnonce = new Buffer([0, 0, 0, 1, 0, 0, 0, 0]);
|
||||||
|
opts = {prevnonce: prevnonce};
|
||||||
|
(function() {AuthMessage.decode(key2, encoded, opts)}).should.throw('Nonce not equal to zero and not greater than the previous nonce');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail if the version number is incorrect', function() {
|
||||||
|
var payload = new Buffer('message');
|
||||||
|
var fromkey = key;
|
||||||
|
var topubkey = key2.public;
|
||||||
|
var version1 = new Buffer([2]);
|
||||||
|
var version2 = new Buffer([0]);
|
||||||
|
var nonce = new Buffer([0, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
|
var toencrypt = Buffer.concat([version1, version2, nonce, payload]);
|
||||||
|
var toencrypt_workaround = new Buffer(toencrypt.toString('hex'));
|
||||||
|
var encrypted = AuthMessage._encrypt(topubkey, toencrypt_workaround);
|
||||||
|
var sig = AuthMessage._sign(fromkey, encrypted);
|
||||||
|
var encoded = {
|
||||||
|
pubkey: fromkey.public.toString('hex'),
|
||||||
|
sig: sig.toString('hex'),
|
||||||
|
encrypted: encrypted.toString('hex')
|
||||||
|
};
|
||||||
|
|
||||||
|
(function() {AuthMessage.decode(key2, encoded);}).should.throw('Invalid version number');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#_encrypt', function() {
|
||||||
|
|
||||||
|
it('should encrypt data', function() {
|
||||||
|
var payload = new Buffer('payload');
|
||||||
|
var encrypted = AuthMessage._encrypt(key.public, payload);
|
||||||
|
encrypted.length.should.equal(129);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#_decrypt', function() {
|
||||||
|
var payload = new Buffer('payload');
|
||||||
|
var payloadhex = payload.toString('hex');
|
||||||
|
|
||||||
|
it('should decrypt encrypted data', function() {
|
||||||
|
var encrypted = AuthMessage._encrypt(key.public, payload);
|
||||||
|
var decrypted = AuthMessage._decrypt(key.private, encrypted);
|
||||||
|
decrypted.toString('hex').should.equal(payloadhex);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#_sign', function() {
|
||||||
|
|
||||||
|
it('should sign data', function() {
|
||||||
|
var payload = new Buffer('payload');
|
||||||
|
var sig = AuthMessage._sign(key, payload);
|
||||||
|
sig.length.should.be.greaterThan(60);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#_verify', function() {
|
||||||
|
var payload = new Buffer('payload');
|
||||||
|
var sig = AuthMessage._sign(key, payload);
|
||||||
|
|
||||||
|
it('should verify signed data', function() {
|
||||||
|
AuthMessage._verify(key.public, sig, payload).should.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -147,7 +147,8 @@ describe('BIP39', function() {
|
||||||
|
|
||||||
//do not run these slow tests on TRAVIS which often fails
|
//do not run these slow tests on TRAVIS which often fails
|
||||||
var vectors = bip39_vectors['english'];
|
var vectors = bip39_vectors['english'];
|
||||||
if (!process.env.TRAVIS && !process.env.CI) {
|
var process = process || null;
|
||||||
|
if (!process || (!process.env.TRAVIS && !process.env.CI)) {
|
||||||
for (var v = 0 ; v < vectors.length ; v++) {
|
for (var v = 0 ; v < vectors.length ; v++) {
|
||||||
(function(v){
|
(function(v){
|
||||||
it('should pass test vector ' + v, function() {
|
it('should pass test vector ' + v, function() {
|
||||||
|
|
Loading…
Reference in New Issue