mirror of https://github.com/BTCPrivate/copay.git
peer authentication
This commit is contained in:
parent
08fa60d41c
commit
6ace16e20d
12
index.html
12
index.html
|
@ -129,7 +129,7 @@
|
|||
<div class="large-6 columns">
|
||||
<h3>Join a Wallet in Creation</h3>
|
||||
<input type="text" class="form-control" placeholder="Paste wallet secret here"
|
||||
ng-model="connectionId" required ng-minlength="32" ng-maxlength="32" autofocus>
|
||||
ng-model="connectionId" required autofocus>
|
||||
</div>
|
||||
<div class="large-3 columns">
|
||||
<button class="button primary expand round"
|
||||
|
@ -202,8 +202,8 @@
|
|||
|
||||
<ul class="no-bullet">
|
||||
<li class="panel" ng-repeat="copayer in $root.wallet.network.connectedCopayers()">
|
||||
<span ng-if="copayer === $root.wallet.getMyCopayerId()"> You ({{copayer}})</span>
|
||||
<span ng-if="copayer !== $root.wallet.getMyCopayerId()">{{copayer}}</span>
|
||||
<span ng-if="copayer === $root.wallet.getMyCopayerId()"> You </span>
|
||||
{{copayer}}
|
||||
<span>
|
||||
<i class="fi-check size-16 panel-sign right p5h br100"></i>
|
||||
</span>
|
||||
|
@ -279,9 +279,8 @@
|
|||
<tr ng-repeat="(peer, actions) in tx.peerActions">
|
||||
<td colspan="3">
|
||||
<div class="panel status">
|
||||
<span ng-if="peer == $root.wallet.network.peerId"> You
|
||||
</span>
|
||||
<span ng-if="peer != $root.wallet.network.peerId"> {{peer}}</span>
|
||||
<span ng-if="peer === $root.wallet.getMyCopayerId()"> You</span>
|
||||
{{peer}}
|
||||
|
||||
<span ng-repeat="(action, ts) in actions">
|
||||
<span ng-if="action == 'create'" title="{{ts | date:'medium'}}" class="fi-page-add size-18 right m10h"></span>
|
||||
|
@ -492,6 +491,7 @@
|
|||
<script src="lib/peerjs/peer.js"></script>
|
||||
<script src="lib/bitcore.js"></script>
|
||||
<script src="lib/crypto-js/rollups/aes.js"></script>
|
||||
<script src="lib/crypto-js/rollups/hmac-sha256.js"></script>
|
||||
<script src="lib/file-saver/FileSaver.js"></script>
|
||||
<script src="lib/socket.io.js"></script>
|
||||
<script src="js/copayBundle.js"></script>
|
||||
|
|
|
@ -60,7 +60,7 @@ angular.module('copay.transactions').controller('TransactionsController',
|
|||
return;
|
||||
}
|
||||
var p = w.txProposals.getTxProposal(ntxid);
|
||||
if (p.txp.builder.isFullySigned()) {
|
||||
if (p.builder.isFullySigned()) {
|
||||
$scope.send(ntxid);
|
||||
_updateTxs();
|
||||
$rootScope.$digest();
|
||||
|
|
|
@ -18,13 +18,22 @@ function PrivateKey(opts) {
|
|||
this.privateKeyCache = opts.privateKeyCache || {};
|
||||
};
|
||||
|
||||
PrivateKey.prototype.getId = function(prefix) {
|
||||
var buf = this.bip.extendedPublicKey;
|
||||
if (prefix) {
|
||||
buf = Buffer.concat([prefix, buf]);
|
||||
PrivateKey.prototype.getId = function() {
|
||||
if (!this.id) {
|
||||
var path = PublicKeyRing.SIGNING_BRANCH;
|
||||
var bip32 = this.bip.derive(path);
|
||||
this.id= bip32.eckey.public.toString('hex');
|
||||
}
|
||||
var hash = util.sha256(buf).toString('hex');
|
||||
return hash.substring(0, hash.length/2);
|
||||
return this.id;
|
||||
};
|
||||
|
||||
PrivateKey.prototype.getSigningKey = function() {
|
||||
if (!this.sid) {
|
||||
var path = PublicKeyRing.SIGNING_BRANCH;
|
||||
var bip32 = this.bip.derive(path);
|
||||
this.sid= bip32.eckey.private.toString('hex');
|
||||
}
|
||||
return this.sid;
|
||||
};
|
||||
|
||||
PrivateKey.fromObj = function(obj) {
|
||||
|
|
|
@ -44,9 +44,12 @@ function PublicKeyRing(opts) {
|
|||
*/
|
||||
|
||||
PublicKeyRing.Branch = function (index, isChange) {
|
||||
return 'm/'+(isChange?1:0)+'/'+index;
|
||||
// first 0 is for future use: could be copayerId.
|
||||
return 'm/0/'+(isChange?1:0)+'/'+index;
|
||||
};
|
||||
|
||||
PublicKeyRing.SIGNING_BRANCH = 'm/100/0/0';
|
||||
|
||||
PublicKeyRing.fromObj = function (data) {
|
||||
if (data instanceof PublicKeyRing) {
|
||||
throw new Error('bad data format: Did you use .toObj()?');
|
||||
|
@ -77,17 +80,20 @@ PublicKeyRing.prototype.serialize = function () {
|
|||
return JSON.stringify(this.toObj());
|
||||
};
|
||||
|
||||
PublicKeyRing.prototype.getCopayerId = function(i, prefix) {
|
||||
var buf = this.copayersBIP32[i].extendedPublicKey;
|
||||
if (prefix) {
|
||||
buf = Buffer.concat([prefix, buf]);
|
||||
PublicKeyRing.prototype.getCopayerId = function(i) {
|
||||
this.copayerIds = this.copayerIds || [];
|
||||
|
||||
if (!this.copayerIds[i]) {
|
||||
var path = PublicKeyRing.SIGNING_BRANCH;
|
||||
var bip32 = this.copayersBIP32[i].derive(path);
|
||||
this.copayerIds[i]= bip32.eckey.public.toString('hex');
|
||||
}
|
||||
var hash = util.sha256(buf).toString('hex');
|
||||
return hash.substring(0, hash.length/2);
|
||||
|
||||
return this.copayerIds[i];
|
||||
};
|
||||
|
||||
PublicKeyRing.prototype.myCopayerId = function(i, prefix) {
|
||||
return this.getCopayerId(0,prefix);
|
||||
PublicKeyRing.prototype.myCopayerId = function(i) {
|
||||
return this.getCopayerId(0);
|
||||
};
|
||||
|
||||
PublicKeyRing.prototype.registeredCopayers = function () {
|
||||
|
|
|
@ -169,9 +169,10 @@ Wallet.prototype.netStart = function() {
|
|||
|
||||
var myId = self.getMyCopayerId();
|
||||
var startOpts = {
|
||||
copayerId: myId
|
||||
copayerId: myId,
|
||||
signingKeyHex: self.privateKey.getSigningKey(),
|
||||
};
|
||||
net.start(function() {
|
||||
net.start(startOpts, function() {
|
||||
self.emit('created');
|
||||
for (var i=0; i<self.publicKeyRing.registeredCopayers(); i++) {
|
||||
var otherId = self.getCopayerId(i);
|
||||
|
@ -184,7 +185,7 @@ Wallet.prototype.netStart = function() {
|
|||
}
|
||||
self.emit('refresh');
|
||||
}
|
||||
}, startOpts);
|
||||
});
|
||||
};
|
||||
|
||||
Wallet.prototype.store = function(isSync) {
|
||||
|
|
|
@ -146,8 +146,8 @@ WalletFactory.prototype.joinCreateSession = function(copayerId, cb) {
|
|||
var privateKey = new PrivateKey({ networkName: this.networkName });
|
||||
this.log('\t### PrivateKey Initialized');
|
||||
self.network.setCopayerId(privateKey.getId());
|
||||
|
||||
self.network.start(function() {
|
||||
self.network.setSigningKey(privateKey.getSigningKey());
|
||||
self.network.start({}, function() {
|
||||
self.network.connectTo(copayerId);
|
||||
self.network.on('data', function(sender, data) {
|
||||
if (data.type ==='walletId') {
|
||||
|
|
|
@ -3,6 +3,7 @@ var imports = require('soop').imports();
|
|||
var EventEmitter= imports.EventEmitter || require('events').EventEmitter;
|
||||
var bitcore = require('bitcore');
|
||||
var util = bitcore.util;
|
||||
var Key = bitcore.Key;
|
||||
|
||||
/*
|
||||
* Emits
|
||||
|
@ -102,10 +103,10 @@ console.log('[WebRTC.js.99:arrayDiff:]',arrayDiff); //TODO
|
|||
});
|
||||
};
|
||||
|
||||
Network.prototype._sendCopayerId = function(copayerId) {
|
||||
console.log('#### SENDING COPAYERID TO TO ', copayerId);
|
||||
Network.prototype._sendHello = function(copayerId) {
|
||||
console.log('#### SENDING HELLO TO ', copayerId);
|
||||
this.send(copayerId, {
|
||||
type: 'copayerId',
|
||||
type: 'hello',
|
||||
copayerId: this.copayerId,
|
||||
});
|
||||
};
|
||||
|
@ -133,27 +134,33 @@ Network.prototype._addCopayer = function(copayerId, isInbound) {
|
|||
};
|
||||
|
||||
Network.prototype._onData = function(data, isInbound, peerId) {
|
||||
var obj;
|
||||
var sig, payload;
|
||||
try {
|
||||
obj = JSON.parse(data);
|
||||
var dataObj = JSON.parse(data);
|
||||
sig = dataObj.sig;
|
||||
payload= dataObj.payload;
|
||||
|
||||
} catch (e) {
|
||||
console.log('### ERROR ON DATA: "%s" ', data, isInbound, e);
|
||||
return;
|
||||
};
|
||||
console.log('### RECEIVED TYPE: %s FROM %s', obj.data.type, obj.sender, obj.data);
|
||||
|
||||
console.log('### RECEIVED INBOUND?:%s TYPE: %s FROM %s: sig:%s',
|
||||
isInbound, payload.type, peerId, sig, payload);
|
||||
var self=this;
|
||||
|
||||
if(obj.data.type === 'copayerId') {
|
||||
if (this.peerFromCopayer(obj.data.copayerId) === peerId) {
|
||||
console.log('#### Peer sent the right copayerId. Setting it up.'); //TODO
|
||||
this._addCopayer(obj.data.copayerId, isInbound);
|
||||
this._notifyNetworkChange( isInbound ? obj.data.copayerId : null);
|
||||
this.emit('open');
|
||||
}
|
||||
else {
|
||||
console.log('### RECEIVED WRONG COPAYER ID FROM:', peerId); //TODO
|
||||
}
|
||||
// TODO _func
|
||||
if(payload.type === 'hello') {
|
||||
var thisSig = this._sign(payload, this.copayerId);
|
||||
if (thisSig !== sig) {
|
||||
console.log('#### Peer sent WRONG hello. Closing connection.');
|
||||
return;
|
||||
}
|
||||
console.log('#### Peer sent signed hello. Setting it up.'); //TODO
|
||||
this._addCopayer(payload.copayerId, isInbound);
|
||||
this._notifyNetworkChange( isInbound ? payload.copayerId : null);
|
||||
this.emit('open');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.copayerForPeer[peerId]) {
|
||||
|
@ -161,17 +168,29 @@ Network.prototype._onData = function(data, isInbound, peerId) {
|
|||
return;
|
||||
}
|
||||
|
||||
switch(obj.data.type) {
|
||||
// check sig
|
||||
if (this.copayerForPeer[peerId]) {
|
||||
var copayerIdBuf = new Buffer(this.copayerForPeer[peerId],'hex');
|
||||
if (!bitcore.Message.verifyWithPubKey( copayerIdBuf, JSON.stringify(payload),
|
||||
new Buffer(sig,'hex'))) {
|
||||
|
||||
console.log('[WebRTC.js.152] SIGNATURE VERIFICATION FAILED!!'); //TODO
|
||||
// TODO close connection
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch(payload.type) {
|
||||
case 'copayers':
|
||||
this._addCopayer(this.copayerForPeer[peerId], false);
|
||||
this._connectToCopayers(obj.data.copayers);
|
||||
this._connectToCopayers(payload.copayers);
|
||||
this._notifyNetworkChange();
|
||||
break;
|
||||
case 'disconnect':
|
||||
this._onClose(obj.sender);
|
||||
this._onClose(peerId);
|
||||
break;
|
||||
default:
|
||||
this.emit('data', self.copayerForPeer[obj.sender], obj.data, isInbound);
|
||||
this.emit('data', self.copayerForPeer[peerId], payload, isInbound);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -193,12 +212,12 @@ Network.prototype._setupConnectionHandlers = function(dataConn, isInbound) {
|
|||
|
||||
self.connections[dataConn.peer] = dataConn;
|
||||
|
||||
console.log('### DATA CONNECTION READY: ADDING PEER: %s (inbound: %s)',
|
||||
console.log('### DATA CONNECTION READY: %s (inbound: %s) AUTHENTICATING...',
|
||||
dataConn.peer, isInbound);
|
||||
|
||||
// The outbount send its copayerID
|
||||
// The connection peer send hello (with signature)
|
||||
if(!isInbound)
|
||||
self._sendCopayerId(self.copayerForPeer[dataConn.peer]);
|
||||
self._sendHello(self.copayerForPeer[dataConn.peer]);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -272,24 +291,39 @@ Network.prototype.setCopayerId = function(copayerId) {
|
|||
if (this.started) {
|
||||
throw new Error ('network already started: can not change peerId')
|
||||
}
|
||||
console.log('[WebRTC.js.295] SETING COPAYER ID:' + copayerId); //TODO
|
||||
this.copayerId = copayerId;
|
||||
this.copayerIdBuf = new Buffer(copayerId,'hex');
|
||||
this.peerId = this.peerFromCopayer(this.copayerId);
|
||||
this._addCopayerMap(this.peerId,copayerId);
|
||||
};
|
||||
|
||||
|
||||
Network.prototype.setSigningKey = function(keyHex) {
|
||||
if (this.started || this.signingKey) {
|
||||
throw new Error ('network already started or key assigned: can not change key')
|
||||
}
|
||||
var k = new Key();
|
||||
k.private = new Buffer(keyHex,'hex');
|
||||
k.regenerateSync();
|
||||
this.signingKey = k;
|
||||
};
|
||||
|
||||
Network.prototype.peerFromCopayer = function(hex) {
|
||||
return util.sha256(new Buffer(hex,'hex')).toString('hex');
|
||||
};
|
||||
|
||||
Network.prototype.start = function(openCallback, opts) {
|
||||
Network.prototype.start = function(opts, openCallback) {
|
||||
opts = opts || {};
|
||||
var self = this;
|
||||
if (this.started) return openCallback();
|
||||
|
||||
opts.connectedPeers = opts.connectedPeers || [];
|
||||
|
||||
if (!this.copayerId)
|
||||
this.setCopayerId(opts.copayerId);
|
||||
if (!this.signingKey)
|
||||
this.setSigningKey(opts.signingKeyHex);
|
||||
|
||||
this.setCopayerId(this.copayerId || opts.copayerId);
|
||||
this.peer = new Peer(this.peerId, this.opts);
|
||||
this._setupPeerHandlers(openCallback);
|
||||
for (var i = 0; i<opts.connectedPeers.length; i++) {
|
||||
|
@ -300,15 +334,39 @@ Network.prototype.start = function(openCallback, opts) {
|
|||
console.log('[WebRTC.js.237] started TRUE'); //TODO
|
||||
};
|
||||
|
||||
Network.prototype._sendToOne = function(copayerId, data, cb) {
|
||||
|
||||
Network.prototype._sign = function(payload, copayerId) {
|
||||
var ret='';
|
||||
var str = JSON.stringify(payload);
|
||||
if (payload.type ==='hello') {
|
||||
console.log('[WebRTC.js.331] SIGNING WITH HMAC:', copayerId); //TODO
|
||||
ret = CryptoJS.enc.Base64.stringify(
|
||||
CryptoJS.HmacSHA256(
|
||||
str,
|
||||
copayerId
|
||||
));
|
||||
}
|
||||
else {
|
||||
if (!this.signingKey)
|
||||
throw new Error ('no key to sign messages :(');
|
||||
|
||||
console.log('[WebRTC.js.341] SIGNING WITH ECDSA'); //TODO
|
||||
ret = bitcore.Message.sign(
|
||||
str,
|
||||
this.signingKey
|
||||
).toString('hex');
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
Network.prototype._sendToOne = function(copayerId, payload, cb) {
|
||||
var peerId = this.peerFromCopayer(copayerId);
|
||||
if (peerId !== this.peerId) {
|
||||
var dataConn = this.connections[peerId];
|
||||
if (dataConn) {
|
||||
var str = JSON.stringify({
|
||||
sender: this.peerId,
|
||||
data: data
|
||||
sig: this._sign(payload, copayerId),
|
||||
payload: payload
|
||||
});
|
||||
dataConn.send(str);
|
||||
}
|
||||
|
@ -319,24 +377,24 @@ console.log('[WebRTC.js.255] WARN: NO CONNECTION TO:', peerId); //TODO
|
|||
if (typeof cb === 'function') cb();
|
||||
};
|
||||
|
||||
Network.prototype.send = function(copayerIds, data, cb) {
|
||||
Network.prototype.send = function(copayerIds, payload, cb) {
|
||||
var self=this;
|
||||
if (!copayerIds) {
|
||||
copayerIds = this.connectedCopayers();
|
||||
data.isBroadcast = 1;
|
||||
payload.isBroadcast = 1;
|
||||
}
|
||||
|
||||
if (Array.isArray(copayerIds)) {
|
||||
var l = copayerIds.length;
|
||||
var i = 0;
|
||||
copayerIds.forEach(function(copayerId) {
|
||||
self._sendToOne(copayerId, data, function () {
|
||||
self._sendToOne(copayerId, payload, function () {
|
||||
if (++i === l && typeof cb === 'function') cb();
|
||||
});
|
||||
});
|
||||
}
|
||||
else if (typeof copayerIds === 'string')
|
||||
self._sendToOne(copayerIds, data, cb);
|
||||
self._sendToOne(copayerIds, payload, cb);
|
||||
};
|
||||
|
||||
Network.prototype.connectTo = function(copayerId) {
|
||||
|
|
|
@ -70,7 +70,7 @@ describe('PrivateKey model', function() {
|
|||
it('should calculate .id', function () {
|
||||
var w1 = new PrivateKey(config);
|
||||
should.exist(w1.getId());
|
||||
w1.getId().length.should.equal(32);
|
||||
w1.getId().length.should.equal(66);
|
||||
});
|
||||
it('fromObj toObj roundtrip', function () {
|
||||
var w1 = new PrivateKey(config);
|
||||
|
|
|
@ -228,7 +228,7 @@ describe('PublicKeyRing model', function() {
|
|||
networkName: 'livenet',
|
||||
});
|
||||
wx.addCopayer();
|
||||
(function() { w.merge(wx, true);}).should.throw();
|
||||
(function() { w.merge(wx);}).should.throw();
|
||||
|
||||
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue