mirror of https://github.com/BTCPrivate/copay.git
Merge pull request #1275 from matiaspando/feature/secret
Added secret number feature
This commit is contained in:
commit
bd5b7a22b0
|
@ -43,6 +43,7 @@ function Wallet(opts) {
|
||||||
' and tried to create a Wallet with network ' + this.getNetworkName());
|
' and tried to create a Wallet with network ' + this.getNetworkName());
|
||||||
|
|
||||||
this.id = opts.id || Wallet.getRandomId();
|
this.id = opts.id || Wallet.getRandomId();
|
||||||
|
this.secretNumber = opts.secretNumber || Wallet.getRandomNumber();
|
||||||
this.lock = new WalletLock(this.storage, this.id, opts.lockTimeOutMin);
|
this.lock = new WalletLock(this.storage, this.id, opts.lockTimeOutMin);
|
||||||
this.name = opts.name;
|
this.name = opts.name;
|
||||||
|
|
||||||
|
@ -50,6 +51,7 @@ function Wallet(opts) {
|
||||||
this.publicKeyRing.walletId = this.id;
|
this.publicKeyRing.walletId = this.id;
|
||||||
this.txProposals.walletId = this.id;
|
this.txProposals.walletId = this.id;
|
||||||
this.network.maxPeers = this.totalCopayers;
|
this.network.maxPeers = this.totalCopayers;
|
||||||
|
this.network.secretNumber = this.secretNumber;
|
||||||
this.registeredPeerIds = [];
|
this.registeredPeerIds = [];
|
||||||
this.addressBook = opts.addressBook || {};
|
this.addressBook = opts.addressBook || {};
|
||||||
this.publicKey = this.privateKey.publicHex;
|
this.publicKey = this.privateKey.publicHex;
|
||||||
|
@ -78,6 +80,11 @@ Wallet.getRandomId = function() {
|
||||||
return r;
|
return r;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Wallet.getRandomNumber = function() {
|
||||||
|
var r = bitcore.SecureRandom.getPseudoRandomBuffer(5).toString('hex');
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
|
||||||
Wallet.prototype.seedCopayer = function(pubKey) {
|
Wallet.prototype.seedCopayer = function(pubKey) {
|
||||||
this.seededCopayerId = pubKey;
|
this.seededCopayerId = pubKey;
|
||||||
};
|
};
|
||||||
|
@ -408,18 +415,28 @@ Wallet.prototype.getMyCopayerIdPriv = function() {
|
||||||
return this.privateKey.getIdPriv(); //copayer idpriv is hex of a private key
|
return this.privateKey.getIdPriv(); //copayer idpriv is hex of a private key
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Wallet.prototype.getSecretNumber = function() {
|
||||||
|
if (this.secretNumber) return this.secretNumber;
|
||||||
|
this.secretNumber = Wallet.getRandomNumber();
|
||||||
|
return this.secretNumber;
|
||||||
|
};
|
||||||
|
|
||||||
Wallet.prototype.getSecret = function() {
|
Wallet.prototype.getSecret = function() {
|
||||||
var pubkeybuf = new Buffer(this.getMyCopayerId(), 'hex');
|
var buf = new Buffer(this.getMyCopayerId() + this.getSecretNumber(), 'hex');
|
||||||
var str = Base58Check.encode(pubkeybuf);
|
var str = Base58Check.encode(buf);
|
||||||
return str;
|
return str;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Wallet.decodeSecret = function(secretB) {
|
Wallet.decodeSecret = function(secretB) {
|
||||||
var secret = Base58Check.decode(secretB);
|
var secret = Base58Check.decode(secretB);
|
||||||
var pubKeyBuf = secret.slice(0, 33);
|
var pubKeyBuf = secret.slice(0, 33);
|
||||||
|
var secretNumber = secret.slice(33, 38);
|
||||||
return {
|
return {
|
||||||
pubKey: pubKeyBuf.toString('hex')
|
pubKey: pubKeyBuf.toString('hex'),
|
||||||
|
secretNumber : secretNumber.toString('hex')
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -445,6 +462,7 @@ Wallet.prototype.netStart = function(callback) {
|
||||||
privkey: myIdPriv,
|
privkey: myIdPriv,
|
||||||
maxPeers: self.totalCopayers,
|
maxPeers: self.totalCopayers,
|
||||||
lastTimestamp: this.lastTimestamp,
|
lastTimestamp: this.lastTimestamp,
|
||||||
|
secretNumber: self.secretNumber,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.publicKeyRing.isComplete()) {
|
if (this.publicKeyRing.isComplete()) {
|
||||||
|
|
|
@ -233,7 +233,8 @@ WalletFactory.prototype.joinCreateSession = function(secret, nickname, passphras
|
||||||
var opts = {
|
var opts = {
|
||||||
copayerId: privateKey.getId(),
|
copayerId: privateKey.getId(),
|
||||||
privkey: privateKey.getIdPriv(),
|
privkey: privateKey.getIdPriv(),
|
||||||
key: privateKey.getIdKey()
|
key: privateKey.getIdKey(),
|
||||||
|
secretNumber : s.secretNumber,
|
||||||
};
|
};
|
||||||
self.network.cleanUp();
|
self.network.cleanUp();
|
||||||
|
|
||||||
|
@ -248,7 +249,7 @@ WalletFactory.prototype.joinCreateSession = function(secret, nickname, passphras
|
||||||
});
|
});
|
||||||
|
|
||||||
self.network.start(opts, function() {
|
self.network.start(opts, function() {
|
||||||
self.network.greet(s.pubKey);
|
self.network.greet(s.pubKey,opts.secretNumber);
|
||||||
self.network.on('data', function(sender, data) {
|
self.network.on('data', function(sender, data) {
|
||||||
if (data.type === 'walletId') {
|
if (data.type === 'walletId') {
|
||||||
if (data.networkName !== self.networkName) {
|
if (data.networkName !== self.networkName) {
|
||||||
|
|
|
@ -17,6 +17,7 @@ function Network(opts) {
|
||||||
this.host = opts.host || 'localhost';
|
this.host = opts.host || 'localhost';
|
||||||
this.port = opts.port || 3001;
|
this.port = opts.port || 3001;
|
||||||
this.schema = opts.schema || 'https';
|
this.schema = opts.schema || 'https';
|
||||||
|
this.secretNumber = opts.secretNumber;
|
||||||
this.cleanUp();
|
this.cleanUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,11 +74,12 @@ Network.prototype.connectedCopayers = function() {
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
Network.prototype._sendHello = function(copayerId) {
|
Network.prototype._sendHello = function(copayerId,secretNumber) {
|
||||||
|
|
||||||
this.send(copayerId, {
|
this.send(copayerId, {
|
||||||
type: 'hello',
|
type: 'hello',
|
||||||
copayerId: this.copayerId,
|
copayerId: this.copayerId,
|
||||||
|
secretNumber : secretNumber
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -193,6 +195,12 @@ Network.prototype._onMessage = function(enc) {
|
||||||
var self = this;
|
var self = this;
|
||||||
switch (payload.type) {
|
switch (payload.type) {
|
||||||
case 'hello':
|
case 'hello':
|
||||||
|
|
||||||
|
if (typeof payload.secretNumber === 'undefined' || payload.secretNumber !== this.secretNumber)
|
||||||
|
{
|
||||||
|
this._deletePeer(enc.pubkey, 'incorrect secret number');
|
||||||
|
return;
|
||||||
|
}
|
||||||
// if we locked allowed copayers, check if it belongs
|
// if we locked allowed copayers, check if it belongs
|
||||||
if (this.allowedCopayerIds && !this.allowedCopayerIds[payload.copayerId]) {
|
if (this.allowedCopayerIds && !this.allowedCopayerIds[payload.copayerId]) {
|
||||||
this._deletePeer(sender);
|
this._deletePeer(sender);
|
||||||
|
@ -240,8 +248,8 @@ Network.prototype._onError = function(err) {
|
||||||
this.criticalError = err.message;
|
this.criticalError = err.message;
|
||||||
};
|
};
|
||||||
|
|
||||||
Network.prototype.greet = function(copayerId) {
|
Network.prototype.greet = function(copayerId,secretNumber) {
|
||||||
this._sendHello(copayerId);
|
this._sendHello(copayerId,secretNumber);
|
||||||
var peerId = this.peerFromCopayer(copayerId);
|
var peerId = this.peerFromCopayer(copayerId);
|
||||||
this._addCopayerMap(peerId, copayerId);
|
this._addCopayerMap(peerId, copayerId);
|
||||||
};
|
};
|
||||||
|
@ -350,6 +358,7 @@ Network.prototype.send = function(dest, payload, cb) {
|
||||||
if (to == this.copayerId)
|
if (to == this.copayerId)
|
||||||
continue;
|
continue;
|
||||||
log.debug('SEND to: ' + to, this.copayerId, payload);
|
log.debug('SEND to: ' + to, this.copayerId, payload);
|
||||||
|
|
||||||
var message = this.encode(to, payload);
|
var message = this.encode(to, payload);
|
||||||
this.socket.emit('message', message);
|
this.socket.emit('message', message);
|
||||||
}
|
}
|
||||||
|
|
|
@ -337,22 +337,29 @@ describe('Wallet model', function() {
|
||||||
it('#getSecret decodeSecret', function() {
|
it('#getSecret decodeSecret', function() {
|
||||||
var w = cachedCreateW2();
|
var w = cachedCreateW2();
|
||||||
var id = w.getMyCopayerId();
|
var id = w.getMyCopayerId();
|
||||||
|
var secretNumber = w.getSecretNumber();
|
||||||
|
|
||||||
var sb = w.getSecret();
|
var sb = w.getSecret();
|
||||||
should.exist(sb);
|
should.exist(sb);
|
||||||
|
|
||||||
var s = Wallet.decodeSecret(sb);
|
var s = Wallet.decodeSecret(sb);
|
||||||
s.pubKey.should.equal(id);
|
s.pubKey.should.equal(id);
|
||||||
|
s.secretNumber.should.equal(secretNumber);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('decodeSecret check', function() {
|
it('decodeSecret check', function() {
|
||||||
(function() {
|
(function() {
|
||||||
Wallet.decodeSecret('4fp61K187CsYmjoRQC5iAdC5eGmbCRsAAXfwEwetSQgHvZs27eWKaLaNHRoKM');
|
Wallet.decodeSecret('4fp61K187CsYmjoRQC5iAdC5eGmbCRsAAXfwEwetSQgHvZs27eWKaLaNHRoKM');
|
||||||
}).should.not.
|
}).should.not.
|
||||||
throw();
|
throw();
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
Wallet.decodeSecret('4fp61K187CsYmjoRQC5iAdC5eGmbCRsAAXfwEwetSQgHvZs27eWKaLaNHRoK');
|
Wallet.decodeSecret('4fp61K187CsYmjoRQC5iAdC5eGmbCRsAAXfwEwetSQgHvZs27eWKaLaNHRoK');
|
||||||
}).should.
|
}).should.
|
||||||
throw();
|
throw();
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
Wallet.decodeSecret('12345');
|
Wallet.decodeSecret('12345');
|
||||||
}).should.
|
}).should.
|
||||||
|
|
|
@ -27,10 +27,12 @@ describe('Network / Async', function() {
|
||||||
privkey: pk || '31701118abde096d166607115ed00ce74a2231f68f43144406c863f5ebf06c32',
|
privkey: pk || '31701118abde096d166607115ed00ce74a2231f68f43144406c863f5ebf06c32',
|
||||||
lastTimestamp: 1,
|
lastTimestamp: 1,
|
||||||
};
|
};
|
||||||
|
n.secretNumber = 'mySecret';
|
||||||
n.start(opts);
|
n.start(opts);
|
||||||
return n;
|
return n;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
it('should create an instance', function() {
|
it('should create an instance', function() {
|
||||||
var n = createN();
|
var n = createN();
|
||||||
should.exist(n);
|
should.exist(n);
|
||||||
|
@ -129,7 +131,8 @@ describe('Network / Async', function() {
|
||||||
|
|
||||||
var message = {
|
var message = {
|
||||||
type: 'hello',
|
type: 'hello',
|
||||||
copayerId: cid1
|
copayerId: cid1,
|
||||||
|
secretNumber : 'mySecret'
|
||||||
};
|
};
|
||||||
var enc = n1.encode(cid2, message);
|
var enc = n1.encode(cid2, message);
|
||||||
n2._onMessage(enc);
|
n2._onMessage(enc);
|
||||||
|
@ -141,7 +144,8 @@ describe('Network / Async', function() {
|
||||||
|
|
||||||
var message = {
|
var message = {
|
||||||
type: 'hello',
|
type: 'hello',
|
||||||
copayerId: cid3 // MITM
|
copayerId: cid3, // MITM
|
||||||
|
secretNumber : 'mySecret'
|
||||||
};
|
};
|
||||||
|
|
||||||
var enc = n.encode(cid2, message);
|
var enc = n.encode(cid2, message);
|
||||||
|
@ -160,7 +164,8 @@ describe('Network / Async', function() {
|
||||||
|
|
||||||
var message = {
|
var message = {
|
||||||
type: 'hello',
|
type: 'hello',
|
||||||
copayerId: cid1
|
copayerId: cid1,
|
||||||
|
secretNumber : 'mySecret'
|
||||||
};
|
};
|
||||||
var nonce = new Buffer('0000000000000001', 'hex');
|
var nonce = new Buffer('0000000000000001', 'hex');
|
||||||
var enc = n1.encode(cid2, message, nonce);
|
var enc = n1.encode(cid2, message, nonce);
|
||||||
|
@ -176,7 +181,8 @@ describe('Network / Async', function() {
|
||||||
|
|
||||||
var message = {
|
var message = {
|
||||||
type: 'hello',
|
type: 'hello',
|
||||||
copayerId: cid1
|
copayerId: cid1,
|
||||||
|
secretNumber : 'mySecret'
|
||||||
};
|
};
|
||||||
n2.networkNonces = {};
|
n2.networkNonces = {};
|
||||||
n2.networkNonces[cid1] = new Buffer('5000000000000001', 'hex');
|
n2.networkNonces[cid1] = new Buffer('5000000000000001', 'hex');
|
||||||
|
@ -195,7 +201,8 @@ describe('Network / Async', function() {
|
||||||
|
|
||||||
var message = {
|
var message = {
|
||||||
type: 'hello',
|
type: 'hello',
|
||||||
copayerId: cid1
|
copayerId: cid1,
|
||||||
|
secretNumber : 'mySecret'
|
||||||
};
|
};
|
||||||
n2.networkNonces = {};
|
n2.networkNonces = {};
|
||||||
n2.networkNonces[cid1] = new Buffer('0000000000000002', 'hex');
|
n2.networkNonces[cid1] = new Buffer('0000000000000002', 'hex');
|
||||||
|
@ -205,6 +212,40 @@ describe('Network / Async', function() {
|
||||||
n2._deletePeer.calledOnce.should.equal(true);
|
n2._deletePeer.calledOnce.should.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should accept join with a correct secret number', function() {
|
||||||
|
var n1 = createN(pk1);
|
||||||
|
var n2 = createN(pk2);
|
||||||
|
n2._deletePeer = sinon.spy();
|
||||||
|
|
||||||
|
var message = {
|
||||||
|
type: 'hello',
|
||||||
|
copayerId: cid1,
|
||||||
|
secretNumber : 'mySecret'
|
||||||
|
};
|
||||||
|
|
||||||
|
var enc = n1.encode(cid2, message);
|
||||||
|
n2._onMessage(enc);
|
||||||
|
n2._deletePeer.calledOnce.should.equal(false);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reject join with a incorrect secret number', function() {
|
||||||
|
var n1 = createN(pk1);
|
||||||
|
var n2 = createN(pk2);
|
||||||
|
n2._deletePeer = sinon.spy();
|
||||||
|
|
||||||
|
var message = {
|
||||||
|
type: 'hello',
|
||||||
|
copayerId: cid1,
|
||||||
|
secretNumber : 'otherSecret'
|
||||||
|
};
|
||||||
|
|
||||||
|
var enc = n1.encode(cid2, message);
|
||||||
|
n2._onMessage(enc);
|
||||||
|
n2._deletePeer.calledOnce.should.equal(true);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#setHexNonce', function() {
|
describe('#setHexNonce', function() {
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
<h1 class="text-primary line-sidebar-b">Waiting copayers</h1>
|
<h1 class="text-primary line-sidebar-b">Waiting copayers</h1>
|
||||||
<h3>Share this secret with your other copayers</h3>
|
<h3>Share this secret with your other copayers</h3>
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
<qrcode size="250" data="{{$root.wallet.getSecret()}}"></qrcode>
|
<qrcode size="350" data="{{$root.wallet.getSecret()}}"></qrcode>
|
||||||
<div class="secret text-gray size-14">
|
<div class="secret text-gray size-14">
|
||||||
{{$root.wallet.getSecret()}}
|
{{$root.wallet.getSecret()}}
|
||||||
<span class="btn-copy" clip-copy="$root.wallet.getSecret()"></span>
|
<span class="btn-copy" clip-copy="$root.wallet.getSecret()"></span>
|
||||||
|
|
Loading…
Reference in New Issue