UNPKG

datoms-protocol

Version:

An integrated implementation of hypercore protocol with both stream and state machine

109 lines (92 loc) 3.23 kB
const SH = require('simple-handshake') const crypto = require('hypercore-crypto') const varint = require('varint') const b4a = require('b4a') module.exports = class ProtocolHandshake { constructor (initiator, payload, opts, done) { this.options = opts this.ondone = done this.buffer = null this.length = 0 this.remotePayload = null this.payload = payload this.keyPair = opts.keyPair || ProtocolHandshake.keyPair() this.remotePublicKey = null this.onrecv = onrecv.bind(this) this.onsend = onsend.bind(this) this.destroyed = false const staticKeyPair = { publicKey: this.keyPair.publicKey, secretKey: this.keyPair.secretKey.subarray(0, 32) } this.noise = SH(initiator, { pattern: 'XX', onhandshake, staticKeyPair: staticKeyPair, onstatickey: onstatickey.bind(this) }) const self = this if (this.noise.waiting === false) process.nextTick(start, this) function onhandshake (state, cb) { process.nextTick(finish, self) cb(null) } } recv (data) { if (this.destroyed) return if (this.buffer) this.buffer = Buffer.concat([this.buffer, data]) else this.buffer = data while (!this.destroyed && !this.noise.finished) { if (!this.buffer || this.buffer.length < 3) return if (this.length) { if (this.buffer.length < this.length) return const message = this.buffer.slice(0, this.length) this.buffer = this.length < this.buffer.length ? this.buffer.slice(this.length) : null this.length = 0 this.noise.recv(message, this.onrecv) } else { this.length = varint.decode(this.buffer, 0) this.buffer = this.buffer.slice(varint.decode.bytes) } } } destroy (err) { if (this.destroyed) return this.destroyed = true if (!this.noise.finished) this.noise.destroy() this.ondone(err || new Error('Handshake destroyed'), null, null, null, null, null) } static keyPair (seed) { return crypto.keyPair(seed) } } function finish (self) { if (self.destroyed) return self.destroyed = true const split = { rx: Buffer.from(self.noise.split.rx), tx: Buffer.from(self.noise.split.tx) } self.ondone(null, self.remotePayload, split, self.buffer, self.remotePublicKey, self.noise.handshakeHash) } function start (self) { if (self.destroyed) return self.noise.send(self.payload, self.onsend) } function onsend (err, data) { if (err) return this.destroy(err) const buf = Buffer.allocUnsafe(varint.encodingLength(data.length) + data.length) varint.encode(data.length, buf, 0) data.copy(buf, varint.encode.bytes) this.options.send(buf) } function onrecv (err, data) { // data is reused so we need to copy it if we use it if (err) return this.destroy(err) if (data && data.length) this.remotePayload = Buffer.from(data) if (this.destroyed || this.noise.finished) return if (this.noise.waiting === false) { this.noise.send(this.payload, this.onsend) } } function onstatickey (remoteKey, done) { this.remotePublicKey = Buffer.from(remoteKey) if (this.options.onauthenticate) this.options.onauthenticate(this.remotePublicKey, done) else done(null) }