diff --git a/lib/bcoin/bip151.js b/lib/bcoin/bip151.js index 91a81675..2482dc99 100644 --- a/lib/bcoin/bip151.js +++ b/lib/bcoin/bip151.js @@ -75,35 +75,45 @@ BIP151Stream.prototype.init = function init(publicKey) { this.aead.init(this.k2, this.iv()); this.aead.aad(this.sid); - this.lastRekey = utils.ms(); + this.lastRekey = utils.now(); }; BIP151Stream.prototype.maybeRekey = function maybeRekey(data) { + var now = utils.now(); + this.processed += data.length; - if (this.processed >= this.highWaterMark) { + + if (now > this.lastRekey + 10 + || this.processed >= this.highWaterMark) { + this.lastRekey = utils.now(); this.processed -= this.highWaterMark; - this.rekey(); this.emit('rekey'); + this.rekey(); } }; BIP151Stream.prototype.rekey = function rekey() { assert(this.prk, 'Cannot rekey before initialization.'); + // All state is reinitialized + // aside from the sequence number. this.k1 = utils.hash256(this.k1); this.k2 = utils.hash256(this.k2); - this.seq = 0; - this.chacha.init(this.k1, this.iv()); this.aead.init(this.k2, this.iv()); this.aead.aad(this.sid); - - this.lastRekey = utils.ms(); }; BIP151Stream.prototype.sequence = function sequence() { this.seq++; + + // Wrap sequence number a la openssh. + if (this.seq === 0xffffffff) + this.seq = 0; + + // State of the ciphers is + // unaltered aside from the iv. this.chacha.init(null, this.iv()); this.aead.init(null, this.iv()); this.aead.aad(this.sid); @@ -152,8 +162,6 @@ BIP151Stream.prototype.verify = function verify(tag) { BIP151Stream.prototype.feed = function feed(data) { var chunk, payload, tag, p, cmd, body; - this.maybeRekey(data); - while (data) { if (!this.hasHeader) { this.pendingHeaderTotal += data.length; @@ -246,13 +254,13 @@ BIP151Stream.prototype.packet = function packet(cmd, body) { packet = new Buffer(4 + payload.length + 16); + this.maybeRekey(packet); + this.encryptSize(payload.length).copy(packet, 0); this.encrypt(payload).copy(packet, 4); this.finish().copy(packet, 4 + payload.length); this.sequence(); - this.maybeRekey(packet); - return packet; }; @@ -263,7 +271,7 @@ function BIP151(cipher) { EventEmitter.call(this); this.input = new BIP151Stream(cipher); - this.output = null; + this.output = new BIP151Stream(cipher); this.initReceived = false; this.ackReceived = false; @@ -282,7 +290,7 @@ utils.inherits(BIP151, EventEmitter); BIP151.prototype._init = function _init() { var self = this; - this.input.on('rekey', function() { + this.output.on('rekey', function() { self.emit('rekey'); }); @@ -320,7 +328,7 @@ BIP151.prototype.encack = function encack(data) { if (utils.equal(publicKey, constants.ZERO_KEY)) { assert(this.handshake, 'No initialization before rekey.'); - this.output.rekey(); + this.input.rekey(); return; } @@ -338,10 +346,11 @@ BIP151.prototype.encack = function encack(data) { BIP151.prototype.encinit = function encinit(data) { var p = bcoin.reader(data); var publicKey = p.readBytes(33); + var cipher = p.readU8(); assert(!this.initReceived, 'Already initialized.'); + assert(cipher === this.output.cipher, 'Cipher mismatch.'); - this.output = new BIP151Stream(p.readU8()); this.output.init(publicKey); this.initReceived = true; diff --git a/lib/bcoin/peer.js b/lib/bcoin/peer.js index 6fe75b2f..a2e36b05 100644 --- a/lib/bcoin/peer.js +++ b/lib/bcoin/peer.js @@ -241,6 +241,7 @@ Peer.prototype._init = function init() { self._error(err, true); }); this.bip151.on('rekey', function() { + self.logger.debug('Rekeying with peer (%s).', self.hostname); self.write(self.framer.encack(self.bip151.toRekey())); }); } diff --git a/test/bip151-test.js b/test/bip151-test.js index 4eae0392..93d4b260 100644 --- a/test/bip151-test.js +++ b/test/bip151-test.js @@ -85,7 +85,7 @@ describe('BIP151', function() { it('client should rekey', function() { var rekeyed = false; - var bytes = client.input.processed; + var bytes = client.output.processed; client.once('rekey', function() { rekeyed = true; @@ -101,15 +101,13 @@ describe('BIP151', function() { }); // Force a rekey after 1gb processed. - client.input.maybeRekey({ length: 1024 * (1 << 20) }); + client.output.maybeRekey({ length: 1024 * (1 << 20) }); - utils.nextTick(function() { - assert(rekeyed); + assert(rekeyed); - // Reset so as not to mess up - // the symmetry of client and server. - client.input.processed = bytes + 33 + 31; - }); + // Reset so as not to mess up + // the symmetry of client and server. + client.output.processed = bytes + 33 + 31; }); it('should encrypt payload from client to server after rekey', function() {