Merge pull request #65 from isocolsky/better_secret

Better secret
This commit is contained in:
Matias Alejo Garcia 2015-02-22 12:35:55 -03:00
commit 2b939b72cc
7 changed files with 87 additions and 28 deletions

View File

@ -19,14 +19,14 @@ A Multisig HD Wallet Service, with minimun server trust.
# Create a 2-of-2 wallet (john.dat is the file where the wallet critical data will be stored, add -t for testnet) # Create a 2-of-2 wallet (john.dat is the file where the wallet critical data will be stored, add -t for testnet)
./bit create 2-2 john ./bit create 2-2 john
* Secret to share: * Secret to share:
0a18bed5-5607-4fde-a809-dc6561bc0664:L3WtafRAEHty7h2J7VCHdiyzFboAdVFnNZXMmqDGw4yiu5kW9Tp4:T JevjEwaaxW6gdAZjqgWcimL525DR8zQsAXf4cscWDa8u1qKTN5eFGSFssuSvT1WySu4YYLYMUPT
./bit status ./bit status
# Use -h or BIT_HOST to setup the base URL for your server. # Use -h or BIT_HOST to setup the base URL for your server.
# Use -f or BIT_FILE to setup the wallet data file # Use -f or BIT_FILE to setup the wallet data file
# Join the wallet from other copayer # Join the wallet from other copayer
./bit -f pete.dat join 0a18bed5-5607-4fde-a809-dc6561bc0664:L3WtafRAEHty7h2J7VCHdiyzFboAdVFnNZXMmqDGw4yiu5kW9Tp4:T ./bit -f pete.dat join JevjEwaaxW6gdAZjqgWcimL525DR8zQsAXf4cscWDa8u1qKTN5eFGSFssuSvT1WySu4YYLYMUPT
export BIT_FILE=pete.dat export BIT_FILE=pete.dat
./bit -f pete.dat status ./bit -f pete.dat status

View File

@ -307,7 +307,11 @@ API.prototype.joinWallet = function(secret, copayerName, cb) {
if (data) if (data)
return cb('Storage already contains a wallet'); return cb('Storage already contains a wallet');
var secretData = WalletUtils.fromSecret(secret); try {
var secretData = WalletUtils.fromSecret(secret);
} catch (ex) {
return cb(ex);
}
var data = self._initData(secretData.network, secretData.walletPrivKey); var data = self._initData(secretData.network, secretData.walletPrivKey);
self._doJoinWallet(secretData.walletId, secretData.walletPrivKey, data.publicKeyRing[0], copayerName, self._doJoinWallet(secretData.walletId, secretData.walletPrivKey, data.publicKeyRing[0], copayerName,
function(err, wallet) { function(err, wallet) {

View File

@ -1,3 +1,4 @@
var _ = require('lodash');
var Uuid = require('uuid'); var Uuid = require('uuid');
/* /*
@ -33,7 +34,7 @@ Notification.create = function(opts) {
var now = Date.now(); var now = Date.now();
x.createdOn = Math.floor(now / 1000); x.createdOn = Math.floor(now / 1000);
x.id = ('00000000000000' + now).slice(-14) + ('0000' + opts.ticker || 0).slice(-4); x.id = _.padLeft(now, 14, '0') + _.padLeft(opts.ticker || 0, 4, '0');
x.type = opts.type || 'general'; x.type = opts.type || 'general';
x.data = opts.data; x.data = opts.data;

View File

@ -18,7 +18,7 @@ TxProposal.create = function(opts) {
var now = Date.now(); var now = Date.now();
x.createdOn = Math.floor(now / 1000); x.createdOn = Math.floor(now / 1000);
x.id = ('00000000000000' + now).slice(-14) + Uuid.v4(); x.id = _.padLeft(now, 14, '0') + Uuid.v4();
x.creatorId = opts.creatorId; x.creatorId = opts.creatorId;
x.toAddress = opts.toAddress; x.toAddress = opts.toAddress;
x.amount = opts.amount; x.amount = opts.amount;

View File

@ -1,3 +1,5 @@
'use strict';
var _ = require('lodash'); var _ = require('lodash');
var $ = require('preconditions').singleton(); var $ = require('preconditions').singleton();
var sjcl = require('sjcl'); var sjcl = require('sjcl');
@ -7,6 +9,7 @@ var Address = Bitcore.Address;
var PrivateKey = Bitcore.PrivateKey; var PrivateKey = Bitcore.PrivateKey;
var PublicKey = Bitcore.PublicKey; var PublicKey = Bitcore.PublicKey;
var crypto = Bitcore.crypto; var crypto = Bitcore.crypto;
var encoding = Bitcore.encoding;
var HDPath = require('./hdpath'); var HDPath = require('./hdpath');
var Utils = require('./utils'); var Utils = require('./utils');
@ -32,10 +35,10 @@ WalletUtils.signMessage = function(text, privKey) {
WalletUtils.accessFromData = function(data) { WalletUtils.accessFromData = function(data) {
if (data.xPrivKey) if (data.xPrivKey)
return 'full'; return 'full';
if (data.rwPrivKey) if (data.rwPrivKey)
return 'readwrite'; return 'readwrite';
return 'readonly'; return 'readonly';
@ -84,22 +87,42 @@ WalletUtils.xPubToCopayerId = function(xpub) {
}; };
WalletUtils.toSecret = function(walletId, walletPrivKey, network) { WalletUtils.toSecret = function(walletId, walletPrivKey, network) {
return walletId + ':' + walletPrivKey.toWIF() + ':' + (network == 'testnet' ? 'T' : 'L'); var widHex = new Buffer(walletId.replace(/-/g, ''), 'hex');
var widBase58 = new encoding.Base58(widHex).toString();
return _.padRight(widBase58, 22, '0') + walletPrivKey.toWIF() + (network == 'testnet' ? 'T' : 'L');
}; };
WalletUtils.fromSecret = function(secret) { WalletUtils.fromSecret = function(secret) {
$.checkArgument(secret); $.checkArgument(secret);
var secretSplit = secret.split(':');
var walletId = secretSplit[0];
var walletPrivKey = Bitcore.PrivateKey.fromString(secretSplit[1]);
var networkChar = secretSplit[2];
function split(str, indexes) {
return { var parts = [];
walletId: walletId, indexes.push(str.length);
walletPrivKey: walletPrivKey, var i = 0;
network: networkChar == 'T' ? 'testnet' : 'livenet', while (i < indexes.length) {
parts.push(str.substring(i == 0 ? 0 : indexes[i - 1], indexes[i]));
i++;
};
return parts;
}; };
try {
var secretSplit = split(secret, [22, 74]);
var widBase58 = secretSplit[0].replace(/0/g, '');
var widHex = encoding.Base58.decode(widBase58).toString('hex');
var walletId = split(widHex, [8, 12, 16, 20]).join('-');
var walletPrivKey = Bitcore.PrivateKey.fromString(secretSplit[1]);
var networkChar = secretSplit[2];
return {
walletId: walletId,
walletPrivKey: walletPrivKey,
network: networkChar == 'T' ? 'testnet' : 'livenet',
};
} catch (ex) {
throw new Error('Invalid secret');
}
}; };

View File

@ -193,8 +193,20 @@ describe('client API ', function() {
}); });
}); });
}); });
it('should fail with a unknown secret', function(done) { it('should fail with an invalid secret', function(done) {
var oldSecret = '3f8e5acb-ceeb-4aae-134f-692d934e3b1c:L2gohj8s2fLKqVU5cQutAVGciutUxczFxLxxXHFsjzLh71ZjkFQQ:T'; // Invalid
clients[0].joinWallet('dummy', 'copayer', function(err, result) {
err.message.should.contain('Invalid secret');
// Right length, invalid char for base 58
clients[0].joinWallet('DsZbqNQQ9LrTKU8EknR7gFKyCQMPg2UUHNPZ1BzM5EbJwjRZaUNBfNtdWLluuFc0f7f7sTCkh7T', 'copayer', function(err, result) {
err.message.should.contain('Invalid secret');
done();
});
});
});
it('should fail with an unknown secret', function(done) {
// Unknown walletId
var oldSecret = '3bJKRn1HkQTpwhVaJMaJ22KwsjN24ML9uKfkSrP7iDuq91vSsTEygfGMMpo6kWLp1pXG9wZSKcT';
clients[0].joinWallet(oldSecret, 'copayer', function(err, result) { clients[0].joinWallet(oldSecret, 'copayer', function(err, result) {
err.code.should.contain('BADREQUEST'); err.code.should.contain('BADREQUEST');
done(); done();

View File

@ -1,9 +1,11 @@
'use strict'; 'use strict';
var _ = require('lodash'); var _ = require('lodash');
var Uuid = require('uuid');
var chai = require('chai'); var chai = require('chai');
var sinon = require('sinon'); var sinon = require('sinon');
var should = chai.should(); var should = chai.should();
var Bitcore = require('bitcore');
var WalletUtils = require('../lib/walletutils'); var WalletUtils = require('../lib/walletutils');
var aText = 'hola'; var aText = 'hola';
@ -16,19 +18,19 @@ var otherPubKey = '02555a2d45e309c00cc8c5090b6ec533c6880ab2d3bc970b3943def989b33
describe('WalletUtils', function() { describe('WalletUtils', function() {
describe('#hashMessage', function() { describe('#hashMessage', function() {
it('Should create a hash', function() { it('should create a hash', function() {
var res = WalletUtils.hashMessage(aText); var res = WalletUtils.hashMessage(aText);
res.toString('hex').should.equal('4102b8a140ec642feaa1c645345f714bc7132d4fd2f7f6202db8db305a96172f'); res.toString('hex').should.equal('4102b8a140ec642feaa1c645345f714bc7132d4fd2f7f6202db8db305a96172f');
}); });
}); });
describe('#signMessage', function() { describe('#signMessage', function() {
it('Should sign a message', function() { it('should sign a message', function() {
var sig = WalletUtils.signMessage(aText, aPrivKey); var sig = WalletUtils.signMessage(aText, aPrivKey);
should.exist(sig); should.exist(sig);
sig.should.equal(aSignature); sig.should.equal(aSignature);
}); });
it('Should fail to sign with wrong args', function() { it('should fail to sign with wrong args', function() {
(function() { (function() {
WalletUtils.signMessage(aText, aPubKey); WalletUtils.signMessage(aText, aPubKey);
}).should.throw('Number'); }).should.throw('Number');
@ -36,22 +38,22 @@ describe('WalletUtils', function() {
}); });
describe('#verifyMessage', function() { describe('#verifyMessage', function() {
it('Should fail to verify a malformed signature', function() { it('should fail to verify a malformed signature', function() {
var res = WalletUtils.verifyMessage(aText, 'badsignature', otherPubKey); var res = WalletUtils.verifyMessage(aText, 'badsignature', otherPubKey);
should.exist(res); should.exist(res);
res.should.equal(false); res.should.equal(false);
}); });
it('Should fail to verify a null signature', function() { it('should fail to verify a null signature', function() {
var res = WalletUtils.verifyMessage(aText, null, otherPubKey); var res = WalletUtils.verifyMessage(aText, null, otherPubKey);
should.exist(res); should.exist(res);
res.should.equal(false); res.should.equal(false);
}); });
it('Should fail to verify with wrong pubkey', function() { it('should fail to verify with wrong pubkey', function() {
var res = WalletUtils.verifyMessage(aText, aSignature, otherPubKey); var res = WalletUtils.verifyMessage(aText, aSignature, otherPubKey);
should.exist(res); should.exist(res);
res.should.equal(false); res.should.equal(false);
}); });
it('Should verify', function() { it('should verify', function() {
var res = WalletUtils.verifyMessage(aText, aSignature, aPubKey); var res = WalletUtils.verifyMessage(aText, aSignature, aPubKey);
should.exist(res); should.exist(res);
res.should.equal(true); res.should.equal(true);
@ -59,7 +61,7 @@ describe('WalletUtils', function() {
}); });
describe('#signMessage #verifyMessage round trip', function() { describe('#signMessage #verifyMessage round trip', function() {
it('Should sign and verify', function() { it('should sign and verify', function() {
var aLongerText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; var aLongerText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
var sig = WalletUtils.signMessage(aLongerText, aPrivKey); var sig = WalletUtils.signMessage(aLongerText, aPrivKey);
WalletUtils.verifyMessage(aLongerText, sig, aPubKey).should.equal(true); WalletUtils.verifyMessage(aLongerText, sig, aPubKey).should.equal(true);
@ -74,4 +76,21 @@ describe('WalletUtils', function() {
msg.should.equal('hello world'); msg.should.equal('hello world');
}); });
}); });
describe('#toSecret #fromSecret round trip', function() {
it('should create secret and parse secret', function() {
var i = 0;
while (i++ < 100) {
var walletId = Uuid.v4();
var walletPrivKey = new Bitcore.PrivateKey();
var network = 'testnet';
var secret = WalletUtils.toSecret(walletId, walletPrivKey, network);
var result = WalletUtils.fromSecret(secret);
result.walletId.should.equal(walletId);
result.walletPrivKey.toString().should.equal(walletPrivKey.toString());
result.network.should.equal(network);
};
});
});
}); });