bip151: fix rekeying. replicate openssh more.
This commit is contained in:
parent
4af5273c0e
commit
f37946abb7
@ -75,35 +75,45 @@ BIP151Stream.prototype.init = function init(publicKey) {
|
|||||||
this.aead.init(this.k2, this.iv());
|
this.aead.init(this.k2, this.iv());
|
||||||
this.aead.aad(this.sid);
|
this.aead.aad(this.sid);
|
||||||
|
|
||||||
this.lastRekey = utils.ms();
|
this.lastRekey = utils.now();
|
||||||
};
|
};
|
||||||
|
|
||||||
BIP151Stream.prototype.maybeRekey = function maybeRekey(data) {
|
BIP151Stream.prototype.maybeRekey = function maybeRekey(data) {
|
||||||
|
var now = utils.now();
|
||||||
|
|
||||||
this.processed += data.length;
|
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.processed -= this.highWaterMark;
|
||||||
this.rekey();
|
|
||||||
this.emit('rekey');
|
this.emit('rekey');
|
||||||
|
this.rekey();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
BIP151Stream.prototype.rekey = function rekey() {
|
BIP151Stream.prototype.rekey = function rekey() {
|
||||||
assert(this.prk, 'Cannot rekey before initialization.');
|
assert(this.prk, 'Cannot rekey before initialization.');
|
||||||
|
|
||||||
|
// All state is reinitialized
|
||||||
|
// aside from the sequence number.
|
||||||
this.k1 = utils.hash256(this.k1);
|
this.k1 = utils.hash256(this.k1);
|
||||||
this.k2 = utils.hash256(this.k2);
|
this.k2 = utils.hash256(this.k2);
|
||||||
|
|
||||||
this.seq = 0;
|
|
||||||
|
|
||||||
this.chacha.init(this.k1, this.iv());
|
this.chacha.init(this.k1, this.iv());
|
||||||
this.aead.init(this.k2, this.iv());
|
this.aead.init(this.k2, this.iv());
|
||||||
this.aead.aad(this.sid);
|
this.aead.aad(this.sid);
|
||||||
|
|
||||||
this.lastRekey = utils.ms();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
BIP151Stream.prototype.sequence = function sequence() {
|
BIP151Stream.prototype.sequence = function sequence() {
|
||||||
this.seq++;
|
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.chacha.init(null, this.iv());
|
||||||
this.aead.init(null, this.iv());
|
this.aead.init(null, this.iv());
|
||||||
this.aead.aad(this.sid);
|
this.aead.aad(this.sid);
|
||||||
@ -152,8 +162,6 @@ BIP151Stream.prototype.verify = function verify(tag) {
|
|||||||
BIP151Stream.prototype.feed = function feed(data) {
|
BIP151Stream.prototype.feed = function feed(data) {
|
||||||
var chunk, payload, tag, p, cmd, body;
|
var chunk, payload, tag, p, cmd, body;
|
||||||
|
|
||||||
this.maybeRekey(data);
|
|
||||||
|
|
||||||
while (data) {
|
while (data) {
|
||||||
if (!this.hasHeader) {
|
if (!this.hasHeader) {
|
||||||
this.pendingHeaderTotal += data.length;
|
this.pendingHeaderTotal += data.length;
|
||||||
@ -246,13 +254,13 @@ BIP151Stream.prototype.packet = function packet(cmd, body) {
|
|||||||
|
|
||||||
packet = new Buffer(4 + payload.length + 16);
|
packet = new Buffer(4 + payload.length + 16);
|
||||||
|
|
||||||
|
this.maybeRekey(packet);
|
||||||
|
|
||||||
this.encryptSize(payload.length).copy(packet, 0);
|
this.encryptSize(payload.length).copy(packet, 0);
|
||||||
this.encrypt(payload).copy(packet, 4);
|
this.encrypt(payload).copy(packet, 4);
|
||||||
this.finish().copy(packet, 4 + payload.length);
|
this.finish().copy(packet, 4 + payload.length);
|
||||||
this.sequence();
|
this.sequence();
|
||||||
|
|
||||||
this.maybeRekey(packet);
|
|
||||||
|
|
||||||
return packet;
|
return packet;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -263,7 +271,7 @@ function BIP151(cipher) {
|
|||||||
EventEmitter.call(this);
|
EventEmitter.call(this);
|
||||||
|
|
||||||
this.input = new BIP151Stream(cipher);
|
this.input = new BIP151Stream(cipher);
|
||||||
this.output = null;
|
this.output = new BIP151Stream(cipher);
|
||||||
|
|
||||||
this.initReceived = false;
|
this.initReceived = false;
|
||||||
this.ackReceived = false;
|
this.ackReceived = false;
|
||||||
@ -282,7 +290,7 @@ utils.inherits(BIP151, EventEmitter);
|
|||||||
BIP151.prototype._init = function _init() {
|
BIP151.prototype._init = function _init() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
this.input.on('rekey', function() {
|
this.output.on('rekey', function() {
|
||||||
self.emit('rekey');
|
self.emit('rekey');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -320,7 +328,7 @@ BIP151.prototype.encack = function encack(data) {
|
|||||||
|
|
||||||
if (utils.equal(publicKey, constants.ZERO_KEY)) {
|
if (utils.equal(publicKey, constants.ZERO_KEY)) {
|
||||||
assert(this.handshake, 'No initialization before rekey.');
|
assert(this.handshake, 'No initialization before rekey.');
|
||||||
this.output.rekey();
|
this.input.rekey();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,10 +346,11 @@ BIP151.prototype.encack = function encack(data) {
|
|||||||
BIP151.prototype.encinit = function encinit(data) {
|
BIP151.prototype.encinit = function encinit(data) {
|
||||||
var p = bcoin.reader(data);
|
var p = bcoin.reader(data);
|
||||||
var publicKey = p.readBytes(33);
|
var publicKey = p.readBytes(33);
|
||||||
|
var cipher = p.readU8();
|
||||||
|
|
||||||
assert(!this.initReceived, 'Already initialized.');
|
assert(!this.initReceived, 'Already initialized.');
|
||||||
|
assert(cipher === this.output.cipher, 'Cipher mismatch.');
|
||||||
|
|
||||||
this.output = new BIP151Stream(p.readU8());
|
|
||||||
this.output.init(publicKey);
|
this.output.init(publicKey);
|
||||||
|
|
||||||
this.initReceived = true;
|
this.initReceived = true;
|
||||||
|
|||||||
@ -241,6 +241,7 @@ Peer.prototype._init = function init() {
|
|||||||
self._error(err, true);
|
self._error(err, true);
|
||||||
});
|
});
|
||||||
this.bip151.on('rekey', function() {
|
this.bip151.on('rekey', function() {
|
||||||
|
self.logger.debug('Rekeying with peer (%s).', self.hostname);
|
||||||
self.write(self.framer.encack(self.bip151.toRekey()));
|
self.write(self.framer.encack(self.bip151.toRekey()));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -85,7 +85,7 @@ describe('BIP151', function() {
|
|||||||
|
|
||||||
it('client should rekey', function() {
|
it('client should rekey', function() {
|
||||||
var rekeyed = false;
|
var rekeyed = false;
|
||||||
var bytes = client.input.processed;
|
var bytes = client.output.processed;
|
||||||
|
|
||||||
client.once('rekey', function() {
|
client.once('rekey', function() {
|
||||||
rekeyed = true;
|
rekeyed = true;
|
||||||
@ -101,15 +101,13 @@ describe('BIP151', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Force a rekey after 1gb processed.
|
// 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
|
// Reset so as not to mess up
|
||||||
// the symmetry of client and server.
|
// the symmetry of client and server.
|
||||||
client.input.processed = bytes + 33 + 31;
|
client.output.processed = bytes + 33 + 31;
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should encrypt payload from client to server after rekey', function() {
|
it('should encrypt payload from client to server after rekey', function() {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user