diff --git a/docs/Configuration.md b/docs/Configuration.md index 9667a79d..14eb08e6 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -59,7 +59,6 @@ Note that certain chain options affect the format and indexing of the chain data - `selfish`: Enable "selfish" mode (no relaying of txes or blocks) (default: false). - `compact`: Enable compact block relay (default: true). - `bip37`: Enable serving of bip37 merkleblocks (default: false). -- `bip151`: Enable bip151 peer-to-peer encryption (default: false). - `listen`: Accept incoming connections (default: true). - `max-outbound`: Max number of outbound connections (default: 8). - `max-inbound`: Max number of inbound connections (default: 30). @@ -68,10 +67,6 @@ Note that certain chain options affect the format and indexing of the chain data - `port`: Port to listen on (default: 8333). - `public-host`: Public host to advertise on network. - `public-port`: Public port to advertise on network. -- `bip150`: Enable bip150 peer auth (default: false). -- `identity-key`: BIP150 identity key (32 byte hex string). -- `auth-peers`: Path to `authorized-peers` file for BIP150. -- `known-peers`: Path to `known-peers` file for BIP150. - `nodes`: List of target nodes to connect to (comma-separated). ## Miner Options diff --git a/etc/authorized-peers b/etc/authorized-peers deleted file mode 100644 index 0039fff5..00000000 --- a/etc/authorized-peers +++ /dev/null @@ -1 +0,0 @@ -02b9f7499c4166e76d3a64326bbbe92dc02f0e07dc184f94995da61683c311cb0f diff --git a/etc/known-peers b/etc/known-peers deleted file mode 100644 index 695f51b9..00000000 --- a/etc/known-peers +++ /dev/null @@ -1 +0,0 @@ -node.bcoin.io,52.39.113.206 02b9f7499c4166e76d3a64326bbbe92dc02f0e07dc184f94995da61683c311cb0f diff --git a/etc/sample.conf b/etc/sample.conf index 090239a8..32eb00ef 100644 --- a/etc/sample.conf +++ b/etc/sample.conf @@ -60,7 +60,6 @@ persistent-mempool: false selfish: false compact: true bip37: false -bip151: true listen: true max-outbound: 8 max-inbound: 30 @@ -81,10 +80,6 @@ host: :: # public-host: 1.2.3.4 # public-port: 8444 -# BIP151 AuthDB and Identity Key -bip150: false -identity-key: 74b4147957813b62cc8987f2b711ddb31f8cb46dcbf71502033da66053c8780a - # Always try to connect to these nodes. # nodes: 127.0.0.1,127.0.0.2 diff --git a/lib/net/bip150.js b/lib/net/bip150.js deleted file mode 100644 index 89011d0f..00000000 --- a/lib/net/bip150.js +++ /dev/null @@ -1,816 +0,0 @@ -/*! - * bip150.js - peer auth. - * Copyright (c) 2016-2017, Christopher Jeffrey (MIT License). - * https://github.com/bcoin-org/bcoin - * Resources: - * https://github.com/bitcoin/bips/blob/master/bip-0150.mediawiki - */ - -'use strict'; - -const assert = require('assert'); -const path = require('path'); -const EventEmitter = require('events'); -const bio = require('bufio'); -const fs = require('bfile'); -const dns = require('bdns'); -const IP = require('binet'); -const Logger = require('blgr'); -const {base58} = require('bstring'); -const ccmp = require('bcrypto/lib/ccmp'); -const hash160 = require('bcrypto/lib/hash160'); -const hash256 = require('bcrypto/lib/hash256'); -const random = require('bcrypto/lib/random'); -const secp256k1 = require('bcrypto/lib/secp256k1'); -const consensus = require('../protocol/consensus'); -const packets = require('./packets'); -const common = require('./common'); - -/** - * BIP150 - * Represents a BIP150 input/output stream. - * @alias module:net.BIP150 - * @extends EventEmitter - * @property {BIP151} bip151 - * @property {BIP151Stream} input - * @property {BIP151Stream} output - * @property {String} hostname - * @property {Boolean} outbound - * @property {AuthDB} db - * @property {Buffer} privateKey - * @property {Buffer} publicKey - * @property {Buffer} peerIdentity - * @property {Boolean} challengeReceived - * @property {Boolean} replyReceived - * @property {Boolean} proposeReceived - * @property {Boolean} challengeSent - * @property {Boolean} auth - * @property {Boolean} completed - */ - -class BIP150 extends EventEmitter { - /** - * Create a BIP150 input/output stream. - * @constructor - * @param {BIP151} bip151 - * @param {String} host - * @param {Boolean} outbound - * @param {AuthDB} db - * @param {Buffer} key - Identity key. - */ - - constructor(bip151, host, outbound, db, key) { - super(); - - assert(bip151, 'BIP150 requires BIP151.'); - assert(typeof host === 'string', 'Hostname required.'); - assert(typeof outbound === 'boolean', 'Outbound flag required.'); - assert(db instanceof AuthDB, 'Auth DB required.'); - assert(Buffer.isBuffer(key), 'Identity key required.'); - - this.bip151 = bip151; - this.input = bip151.input; - this.output = bip151.output; - this.hostname = host; - this.outbound = outbound; - this.db = db; - this.privateKey = key; - this.publicKey = secp256k1.publicKeyCreate(key, true); - - this.peerIdentity = null; - this.challengeReceived = false; - this.replyReceived = false; - this.proposeReceived = false; - this.challengeSent = false; - this.auth = false; - this.completed = false; - this.job = null; - this.timeout = null; - this.onAuth = null; - - this.init(); - } - - /** - * Initialize BIP150. - * @private - */ - - init() { - if (this.outbound) - this.peerIdentity = this.db.getKnown(this.hostname); - } - - /** - * Test whether the state should be - * considered authed. This differs - * for inbound vs. outbound. - * @returns {Boolean} - */ - - isAuthed() { - if (this.outbound) - return this.challengeSent && this.challengeReceived; - return this.challengeReceived && this.replyReceived; - } - - /** - * Handle a received challenge hash. - * Returns an authreply signature. - * @param {Buffer} hash - * @returns {Buffer} - * @throws on auth failure - */ - - challenge(hash) { - const type = this.outbound ? 'r' : 'i'; - - assert(this.bip151.handshake, 'No BIP151 handshake before challenge.'); - assert(!this.challengeReceived, 'Peer challenged twice.'); - this.challengeReceived = true; - - if (hash.equals(consensus.ZERO_HASH)) - throw new Error('Auth failure.'); - - const msg = this.hash(this.input.sid, type, this.publicKey); - - if (!ccmp(hash, msg)) - return common.ZERO_SIG; - - if (this.isAuthed()) { - this.auth = true; - this.emit('auth'); - } - - // authreply - return secp256k1.sign(msg, this.privateKey); - } - - /** - * Handle a received reply signature. - * Returns an authpropose hash. - * @param {Buffer} sig - * @returns {Buffer} - * @throws on auth failure - */ - - reply(sig) { - const type = this.outbound ? 'i' : 'r'; - - assert(this.challengeSent, 'Unsolicited reply.'); - assert(!this.replyReceived, 'Peer replied twice.'); - this.replyReceived = true; - - if (sig.equals(common.ZERO_SIG)) - throw new Error('Auth failure.'); - - if (!this.peerIdentity) - return random.randomBytes(32); - - const msg = this.hash(this.output.sid, type, this.peerIdentity); - const result = secp256k1.verify(msg, sig, this.peerIdentity); - - if (!result) - return random.randomBytes(32); - - if (this.isAuthed()) { - this.auth = true; - this.emit('auth'); - return null; - } - - assert(this.outbound, 'No challenge received before reply on inbound.'); - - // authpropose - return this.hash(this.input.sid, 'p', this.publicKey); - } - - /** - * Handle a received propose hash. - * Returns an authchallenge hash. - * @param {Buffer} hash - * @returns {Buffer} - */ - - propose(hash) { - assert(!this.outbound, 'Outbound peer tried to propose.'); - assert(!this.challengeSent, 'Unsolicited propose.'); - assert(!this.proposeReceived, 'Peer proposed twice.'); - this.proposeReceived = true; - - const match = this.findAuthorized(hash); - - if (!match) - return consensus.ZERO_HASH; - - this.peerIdentity = match; - - // Add them in case we ever connect to them. - this.db.addKnown(this.hostname, this.peerIdentity); - - this.challengeSent = true; - - // authchallenge - return this.hash(this.output.sid, 'r', this.peerIdentity); - } - - /** - * Create initial authchallenge hash - * for the peer. The peer's identity - * key must be known. - * @returns {AuthChallengePacket} - */ - - toChallenge() { - assert(this.bip151.handshake, 'No BIP151 handshake before challenge.'); - assert(this.outbound, 'Cannot challenge an inbound connection.'); - assert(this.peerIdentity, 'Cannot challenge without a peer identity.'); - - const msg = this.hash(this.output.sid, 'i', this.peerIdentity); - - assert(!this.challengeSent, 'Cannot initiate challenge twice.'); - this.challengeSent = true; - - return new packets.AuthChallengePacket(msg); - } - - /** - * Derive new cipher keys based on - * BIP150 data. This differs from - * the regular key derivation of BIP151. - * @param {Buffer} sid - Sesson ID - * @param {Buffer} key - `k1` or `k2` - * @param {Buffer} req - Requesting Identity Key - * @param {Buffer} res - Response Identity Key - * @returns {Buffer} - */ - - rekey(sid, key, req, res) { - const seed = Buffer.allocUnsafe(130); - sid.copy(seed, 0); - key.copy(seed, 32); - req.copy(seed, 64); - res.copy(seed, 97); - return hash256.digest(seed); - } - - /** - * Rekey the BIP151 input stream - * using BIP150-style derivation. - */ - - rekeyInput() { - const stream = this.input; - const req = this.peerIdentity; - const res = this.publicKey; - const k1 = this.rekey(stream.sid, stream.k1, req, res); - const k2 = this.rekey(stream.sid, stream.k2, req, res); - stream.rekey(k1, k2); - } - - /** - * Rekey the BIP151 output stream - * using BIP150-style derivation. - */ - - rekeyOutput() { - const stream = this.output; - const req = this.publicKey; - const res = this.peerIdentity; - const k1 = this.rekey(stream.sid, stream.k1, req, res); - const k2 = this.rekey(stream.sid, stream.k2, req, res); - stream.rekey(k1, k2); - } - - /** - * Create a hash using the session ID. - * @param {Buffer} sid - * @param {String} ch - * @param {Buffer} key - * @returns {Buffer} - */ - - hash(sid, ch, key) { - const data = Buffer.allocUnsafe(66); - sid.copy(data, 0); - data[32] = ch.charCodeAt(0); - key.copy(data, 33); - return hash256.digest(data); - } - - /** - * Find an authorized peer in the Auth - * DB based on a proposal hash. Note - * that the hash to find is specific - * to the state of BIP151. This results - * in an O(n) search. - * @param {Buffer} hash - * @returns {Buffer|null} - */ - - findAuthorized(hash) { - // Scary O(n) stuff. - for (const key of this.db.authorized) { - const msg = this.hash(this.output.sid, 'p', key); - - // XXX Do we really need a constant - // time compare here? Do it just to - // be safe I guess. - if (ccmp(msg, hash)) - return key; - } - - return null; - } - - /** - * Destroy the BIP150 stream and - * any current running wait job. - */ - - destroy() { - if (!this.job) - return; - - this.reject(new Error('BIP150 stream was destroyed.')); - } - - /** - * Cleanup wait job. - * @private - * @returns {Job} - */ - - cleanup() { - const job = this.job; - - assert(!this.completed, 'Already completed.'); - assert(job, 'No completion job.'); - - this.completed = true; - this.job = null; - - if (this.timeout != null) { - clearTimeout(this.timeout); - this.timeout = null; - } - - if (this.onAuth) { - this.removeListener('auth', this.onAuth); - this.onAuth = null; - } - - return job; - } - - /** - * Resolve the current wait job. - * @private - * @param {Object} result - */ - - resolve(result) { - const job = this.cleanup(); - job.resolve(result); - } - - /** - * Reject the current wait job. - * @private - * @param {Error} err - */ - - reject(err) { - const job = this.cleanup(); - job.reject(err); - } - - /** - * Wait for handshake to complete. - * @param {Number} timeout - * @returns {Promise} - */ - - wait(timeout) { - return new Promise((resolve, reject) => { - this._wait(timeout, resolve, reject); - }); - } - - /** - * Wait for handshake to complete. - * @private - * @param {Number} timeout - * @param {Function} resolve - * @param {Function} reject - */ - - _wait(timeout, resolve, reject) { - assert(!this.auth, 'Cannot wait for init after handshake.'); - - this.job = { resolve, reject }; - - if (this.outbound && !this.peerIdentity) { - this.reject(new Error(`No identity for ${this.hostname}.`)); - return; - } - - this.timeout = setTimeout(() => { - this.reject(new Error('BIP150 handshake timed out.')); - }, timeout); - - this.onAuth = this.resolve.bind(this); - this.once('auth', this.onAuth); - } - - /** - * Serialize the peer's identity - * key as a BIP150 "address". - * @returns {Base58String} - */ - - getAddress() { - assert(this.peerIdentity, 'Cannot serialize address.'); - return BIP150.address(this.peerIdentity); - } - - /** - * Serialize an identity key as a - * BIP150 "address". - * @returns {Base58String} - */ - - static address(key) { - const bw = bio.write(27); - bw.writeU8(0x0f); - bw.writeU16BE(0xff01); - bw.writeBytes(hash160.digest(key)); - bw.writeChecksum(hash256.digest); - return base58.encode(bw.render()); - } -} - -/** - * AuthDB - * @alias module:net.AuthDB - */ - -class AuthDB { - /** - * Create an auth DB. - * @constructor - */ - - constructor(options) { - this.logger = Logger.global; - this.resolve = dns.lookup; - this.prefix = null; - this.dnsKnown = []; - - this.known = new Map(); - this.authorized = []; - - this.init(options); - } - - /** - * Initialize authdb with options. - * @param {Object} options - */ - - init(options) { - if (!options) - return; - - if (options.logger != null) { - assert(typeof options.logger === 'object'); - this.logger = options.logger.context('authdb'); - } - - if (options.resolve != null) { - assert(typeof options.resolve === 'function'); - this.resolve = options.resolve; - } - - if (options.knownPeers != null) { - assert(typeof options.knownPeers === 'object'); - this.setKnown(options.knownPeers); - } - - if (options.authPeers != null) { - assert(Array.isArray(options.authPeers)); - this.setAuthorized(options.authPeers); - } - - if (options.prefix != null) { - assert(typeof options.prefix === 'string'); - this.prefix = options.prefix; - } - } - - /** - * Open auth database (lookup known peers). - * @method - * @returns {Promise} - */ - - async open() { - await this.readKnown(); - await this.readAuth(); - await this.lookup(); - } - - /** - * Close auth database. - * @method - * @returns {Promise} - */ - - async close() { - ; - } - - /** - * Add a known peer. - * @param {String} host - Peer Hostname - * @param {Buffer} key - Identity Key - */ - - addKnown(host, key) { - assert(typeof host === 'string', - 'Known host must be a string.'); - - assert(Buffer.isBuffer(key) && key.length === 33, - 'Invalid public key for known peer.'); - - const addr = IP.fromHostname(host); - - if (addr.type === IP.types.DNS) { - // Defer this for resolution. - this.dnsKnown.push([addr, key]); - return; - } - - this.known.set(host, key); - } - - /** - * Add an authorized peer. - * @param {Buffer} key - Identity Key - */ - - addAuthorized(key) { - assert(Buffer.isBuffer(key) && key.length === 33, - 'Invalid public key for authorized peer.'); - this.authorized.push(key); - } - - /** - * Initialize known peers with a host->key map. - * @param {Object} map - */ - - setKnown(map) { - this.known.clear(); - - for (const host of Object.keys(map)) { - const key = map[host]; - this.addKnown(host, key); - } - } - - /** - * Initialize authorized peers with a list of keys. - * @param {Buffer[]} keys - */ - - setAuthorized(keys) { - this.authorized.length = 0; - - for (const key of keys) - this.addAuthorized(key); - } - - /** - * Get a known peer key by hostname. - * @param {String} hostname - * @returns {Buffer|null} - */ - - getKnown(hostname) { - const known = this.known.get(hostname); - - if (known) - return known; - - const addr = IP.fromHostname(hostname); - - return this.known.get(addr.host); - } - - /** - * Lookup known peers. - * @method - * @returns {Promise} - */ - - async lookup() { - const jobs = []; - - for (const [addr, key] of this.dnsKnown) - jobs.push(this.populate(addr, key)); - - await Promise.all(jobs); - } - - /** - * Populate known peers with hosts. - * @method - * @private - * @param {Object} addr - * @param {Buffer} key - * @returns {Promise} - */ - - async populate(addr, key) { - assert(addr.type === IP.types.DNS, 'Resolved host passed.'); - - this.logger.info('Resolving authorized hosts from: %s.', addr.host); - - let hosts; - try { - hosts = await this.resolve(addr.host); - } catch (e) { - this.logger.error(e); - return; - } - - for (let host of hosts) { - if (addr.port !== 0) - host = IP.toHostname(host, addr.port); - - this.known.set(host, key); - } - } - - /** - * Parse known peers. - * @param {String} text - * @returns {Object} - */ - - async readKnown() { - if (fs.unsupported) - return; - - if (!this.prefix) - return; - - const file = path.join(this.prefix, 'known-peers'); - - let text; - try { - text = await fs.readFile(file, 'utf8'); - } catch (e) { - if (e.code === 'ENOENT') - return; - throw e; - } - - this.parseKnown(text); - } - - /** - * Parse known peers. - * @param {String} text - * @returns {Object} - */ - - parseKnown(text) { - assert(typeof text === 'string'); - - if (text.charCodeAt(0) === 0xfeff) - text = text.substring(1); - - text = text.replace(/\r\n/g, '\n'); - text = text.replace(/\r/g, '\n'); - - let num = 0; - - for (const chunk of text.split('\n')) { - const line = chunk.trim(); - - num += 1; - - if (line.length === 0) - continue; - - if (line[0] === '#') - continue; - - const parts = line.split(/\s+/); - - if (parts.length < 2) - throw new Error(`No key present on line ${num}: "${line}".`); - - const hosts = parts[0].split(','); - - let host, addr; - if (hosts.length >= 2) { - host = hosts[0]; - addr = hosts[1]; - } else { - host = null; - addr = hosts[0]; - } - - const key = Buffer.from(parts[1], 'hex'); - - if (key.length !== 33) - throw new Error(`Invalid key on line ${num}: "${parts[1]}".`); - - if (host && host.length > 0) - this.addKnown(host, key); - - if (addr.length === 0) - continue; - - this.addKnown(addr, key); - } - } - - /** - * Parse known peers. - * @param {String} text - * @returns {Object} - */ - - async readAuth() { - if (fs.unsupported) - return; - - if (!this.prefix) - return; - - const file = path.join(this.prefix, 'authorized-peers'); - - let text; - try { - text = await fs.readFile(file, 'utf8'); - } catch (e) { - if (e.code === 'ENOENT') - return; - throw e; - } - - this.parseAuth(text); - } - - /** - * Parse authorized peers. - * @param {String} text - * @returns {Buffer[]} keys - */ - - parseAuth(text) { - assert(typeof text === 'string'); - - if (text.charCodeAt(0) === 0xfeff) - text = text.substring(1); - - text = text.replace(/\r\n/g, '\n'); - text = text.replace(/\r/g, '\n'); - - let num = 0; - - for (const chunk of text.split('\n')) { - const line = chunk.trim(); - - num += 1; - - if (line.length === 0) - continue; - - if (line[0] === '#') - continue; - - const key = Buffer.from(line, 'hex'); - - if (key.length !== 33) - throw new Error(`Invalid key on line ${num}: "${line}".`); - - this.addAuthorized(key); - } - } -} - -/* - * Expose - */ - -exports = BIP150; - -exports.BIP150 = BIP150; -exports.AuthDB = AuthDB; - -module.exports = exports; diff --git a/lib/net/bip151.js b/lib/net/bip151.js deleted file mode 100644 index 0a393193..00000000 --- a/lib/net/bip151.js +++ /dev/null @@ -1,761 +0,0 @@ -/*! - * bip151.js - peer-to-peer communication encryption. - * Copyright (c) 2016-2017, Christopher Jeffrey (MIT License). - * https://github.com/bcoin-org/bcoin - * Resources: - * https://github.com/bitcoin/bips/blob/master/bip-0151.mediawiki - * https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.chacha20poly1305 - * https://github.com/openssh/openssh-portable/blob/master/cipher-chachapoly.c - * https://github.com/openssh/openssh-portable/blob/master/cipher.c - * https://github.com/openssh/openssh-portable/blob/master/packet.c - */ - -'use strict'; - -const assert = require('assert'); -const EventEmitter = require('events'); -const {format} = require('util'); -const bio = require('bufio'); -const util = require('../utils/util'); -const hash256 = require('bcrypto/lib/hash256'); -const sha256 = require('bcrypto/lib/sha256'); -const ChaCha20 = require('bcrypto/lib/chacha20'); -const Poly1305 = require('bcrypto/lib/poly1305'); -const AEAD = require('bcrypto/lib/aead'); -const hkdf = require('bcrypto/lib/hkdf'); -const secp256k1 = require('bcrypto/lib/secp256k1'); -const packets = require('./packets'); -const common = require('./common'); -const {encoding} = bio; -const {EncinitPacket, EncackPacket} = packets; - -/* - * Constants - */ - -const HKDF_SALT = Buffer.from('bitcoinecdh', 'ascii'); -const INFO_KEY1 = Buffer.from('BitcoinK1', 'ascii'); -const INFO_KEY2 = Buffer.from('BitcoinK2', 'ascii'); -const INFO_SID = Buffer.from('BitcoinSessionID', 'ascii'); -const HIGH_WATERMARK = 1024 * (1 << 20); - -/** - * BIP151 Stream - * Represents a BIP151 input or output stream. - * @alias module:net.BIP151Stream - * @property {Buffer} publicKey - * @property {Buffer} privateKey - * @property {Number} cipher - * @property {Buffer} k1 - * @property {Buffer} k2 - * @property {Buffer} sid - * @property {ChaCha20} chacha - * @property {AEAD} aead - * @property {Buffer} tag - * @property {Number} seq - * @property {Number} processed - * @property {Number} lastKey - */ - -class BIP151Stream { - /** - * Create a BIP151 input or output stream. - * @constructor - * @param {Number} cipher - */ - - constructor(cipher) { - this.cipher = BIP151.ciphers.CHACHAPOLY; - this.privateKey = secp256k1.generatePrivateKey(); - this.publicKey = null; - this.k1 = null; - this.k2 = null; - this.sid = null; - - if (cipher != null) { - assert(cipher === BIP151.ciphers.CHACHAPOLY, 'Unknown cipher type.'); - this.cipher = cipher; - } - - this.chacha = new ChaCha20(); - this.aead = new AEAD(); - this.tag = null; - this.seq = 0; - this.iv = Buffer.allocUnsafe(8); - this.iv.fill(0); - - this.processed = 0; - this.lastRekey = 0; - } - - /** - * Initialize the stream with peer's public key. - * Computes ecdh secret and chacha keys. - * @param {Buffer} publicKey - */ - - init(publicKey) { - assert(Buffer.isBuffer(publicKey)); - - this.publicKey = publicKey; - - const secret = secp256k1.ecdh(this.publicKey, this.privateKey).slice(1); - - const bw = bio.pool(33); - - bw.writeBytes(secret); - bw.writeU8(this.cipher); - - const data = bw.render(); - const prk = hkdf.extract(sha256, data, HKDF_SALT); - - this.k1 = hkdf.expand(sha256, prk, INFO_KEY1, 32); - this.k2 = hkdf.expand(sha256, prk, INFO_KEY2, 32); - this.sid = hkdf.expand(sha256, prk, INFO_SID, 32); - - this.seq = 0; - - this.update(); - - this.chacha.init(this.k1, this.iv); - this.aead.init(this.k2, this.iv); - - this.lastRekey = util.now(); - } - - /** - * Add buffer size to `processed`, - * check whether we need to rekey. - * @param {Buffer} packet - * @returns {Boolean} - */ - - shouldRekey(packet) { - const now = util.now(); - - this.processed += packet.length; - - if (now >= this.lastRekey + 10 - || this.processed >= HIGH_WATERMARK) { - this.lastRekey = now; - this.processed = 0; - return true; - } - - return false; - } - - /** - * Generate new chacha keys with `key = HASH256(sid | key)`. - * This will reinitialize the state of both ciphers. - */ - - rekey(k1, k2) { - assert(this.sid, 'Cannot rekey before initialization.'); - - if (!k1) { - this.k1 = hash256.root(this.sid, this.k1); - this.k2 = hash256.root(this.sid, this.k2); - } else { - this.k1 = k1; - this.k2 = k2; - } - - assert(this.k1); - assert(this.k2); - - // All state is reinitialized - // aside from the sequence number. - this.chacha.init(this.k1, this.iv); - this.aead.init(this.k2, this.iv); - } - - /** - * Increment packet sequence number and update IVs - * (note, sequence number overflows after 2^64-1). - * The IV will be updated without reinitializing - * cipher state. - */ - - sequence() { - // Wrap sequence number a la openssh. - if (++this.seq === 0x100000000) - this.seq = 0; - - this.update(); - - // State of the ciphers is - // unaltered aside from the iv. - this.chacha.init(null, this.iv); - this.aead.init(null, this.iv); - } - - /** - * Render the IV necessary for cipher streams. - * @returns {Buffer} - */ - - update() { - this.iv.writeUInt32LE(this.seq, 0, true); - return this.iv; - } - - /** - * Get public key tied to private key - * (not the same as BIP151Stream#publicKey). - * @returns {Buffer} - */ - - getPublicKey() { - return secp256k1.publicKeyCreate(this.privateKey, true); - } - - /** - * Encrypt a payload size with k1. - * @param {Buffer} data - * @returns {Buffer} - */ - - encryptSize(data) { - return this.chacha.encrypt(data.slice(0, 4)); - } - - /** - * Decrypt payload size with k1. - * @param {Buffer} data - * @returns {Number} - */ - - decryptSize(data) { - this.chacha.encrypt(data); - return data.readUInt32LE(0, true); - } - - /** - * Encrypt payload with AEAD (update cipher and mac). - * @param {Buffer} data - * @returns {Buffer} data - */ - - encrypt(data) { - return this.aead.encrypt(data); - } - - /** - * Decrypt payload with AEAD (update cipher only). - * @param {Buffer} data - * @returns {Buffer} data - */ - - decrypt(data) { - return this.aead.chacha20.encrypt(data); - } - - /** - * Authenticate payload with AEAD (update mac only). - * @param {Buffer} data - * @returns {Buffer} data - */ - - auth(data) { - return this.aead.auth(data); - } - - /** - * Finalize AEAD and compute MAC. - * @returns {Buffer} - */ - - final() { - this.tag = this.aead.final(); - return this.tag; - } - - /** - * Verify tag against mac in constant time. - * @param {Buffer} tag - * @returns {Boolean} - */ - - verify(tag) { - return Poly1305.verify(this.tag, tag); - } -} - -/** - * BIP151 - * Represents a BIP151 input and output stream. - * Holds state for peer communication. - * @alias module:net.BIP151 - * @extends EventEmitter - * @property {BIP151Stream} input - * @property {BIP151Stream} output - * @property {Boolean} initReceived - * @property {Boolean} ackReceived - * @property {Boolean} initSent - * @property {Boolean} ackSent - * @property {Object} timeout - * @property {Job} job - * @property {Boolean} completed - * @property {Boolean} handshake - */ - -class BIP151 extends EventEmitter { - /** - * Create a BIP151 input and output stream. - * @constructor - * @param {Number} cipher - */ - - constructor(cipher) { - super(); - - this.input = new BIP151Stream(cipher); - this.output = new BIP151Stream(cipher); - - this.initReceived = false; - this.ackReceived = false; - this.initSent = false; - this.ackSent = false; - this.completed = false; - this.handshake = false; - - this.pending = []; - this.total = 0; - this.waiting = 4; - this.hasSize = false; - - this.timeout = null; - this.job = null; - this.onShake = null; - - this.bip150 = null; - } - - /** - * Emit an error. - * @param {...String} msg - */ - - error() { - const msg = format.apply(null, arguments); - this.emit('error', new Error(msg)); - } - - /** - * Test whether handshake has completed. - * @returns {Boolean} - */ - - isReady() { - return this.initSent - && this.ackReceived - && this.initReceived - && this.ackSent; - } - - /** - * Render an `encinit` packet. Contains the - * input public key and cipher number. - * @returns {Buffer} - */ - - toEncinit() { - assert(!this.initSent, 'Cannot init twice.'); - this.initSent = true; - return new EncinitPacket(this.input.getPublicKey(), this.input.cipher); - } - - /** - * Render `encack` packet. Contains the - * output stream public key. - * @returns {Buffer} - */ - - toEncack() { - assert(this.output.sid, 'Cannot ack before init.'); - assert(!this.ackSent, 'Cannot ack twice.'); - this.ackSent = true; - - if (this.isReady()) { - assert(!this.completed, 'No encack after timeout.'); - this.handshake = true; - this.emit('handshake'); - } - - return new EncackPacket(this.output.getPublicKey()); - } - - /** - * Render `encack` packet with an all - * zero public key, notifying of a rekey - * for the output stream. - * @returns {Buffer} - */ - - toRekey() { - assert(this.handshake, 'Cannot rekey before handshake.'); - return new EncackPacket(common.ZERO_KEY); - } - - /** - * Handle `encinit` from remote peer. - * @param {Buffer} - */ - - encinit(publicKey, cipher) { - assert(cipher === this.output.cipher, 'Cipher mismatch.'); - assert(!this.initReceived, 'Already initialized.'); - assert(!this.completed, 'No encinit after timeout.'); - this.initReceived = true; - this.output.init(publicKey); - } - - /** - * Handle `encack` from remote peer. - * @param {Buffer} data - */ - - encack(publicKey) { - assert(this.initSent, 'Unsolicited ACK.'); - - if (publicKey.equals(common.ZERO_KEY)) { - assert(this.handshake, 'No initialization before rekey.'); - - if (this.bip150 && this.bip150.auth) { - this.bip150.rekeyInput(); - return; - } - - this.input.rekey(); - - return; - } - - assert(!this.ackReceived, 'Already ACKed.'); - assert(!this.completed, 'No encack after timeout.'); - this.ackReceived = true; - - this.input.init(publicKey); - - if (this.isReady()) { - this.handshake = true; - this.emit('handshake'); - } - } - - /** - * Cleanup handshake job. - * @returns {Job} - */ - - cleanup() { - const job = this.job; - - assert(!this.completed, 'Already completed.'); - assert(job, 'No completion job.'); - - this.completed = true; - this.job = null; - - if (this.timeout != null) { - clearTimeout(this.timeout); - this.timeout = null; - } - - if (this.onShake) { - this.removeListener('handshake', this.onShake); - this.onShake = null; - } - - return job; - } - - /** - * Complete the timeout for handshake. - * @param {Object} result - */ - - resolve(result) { - const job = this.cleanup(); - job.resolve(result); - } - - /** - * Complete the timeout for handshake with error. - * @param {Error} err - */ - - reject(err) { - const job = this.cleanup(); - job.reject(err); - } - - /** - * Set a timeout and wait for handshake to complete. - * @param {Number} timeout - Timeout in ms. - * @returns {Promise} - */ - - wait(timeout) { - return new Promise((resolve, reject) => { - this._wait(timeout, resolve, reject); - }); - } - - /** - * Set a timeout and wait for handshake to complete. - * @private - * @param {Number} timeout - * @param {Function} resolve - * @param {Function} reject - */ - - _wait(timeout, resolve, reject) { - assert(!this.handshake, 'Cannot wait for init after handshake.'); - - this.job = { resolve, reject }; - - this.timeout = setTimeout(() => { - this.reject(new Error('BIP151 handshake timed out.')); - }, timeout); - - this.onShake = this.resolve.bind(this); - this.once('handshake', this.onShake); - } - - /** - * Destroy BIP151 state and streams. - */ - - destroy() { - if (!this.job) - return; - - this.reject(new Error('BIP151 stream was destroyed.')); - } - - /** - * Add buffer size to `processed`, - * check whether we need to rekey. - * @param {Buffer} packet - */ - - maybeRekey(packet) { - if (!this.output.shouldRekey(packet)) - return; - - this.emit('rekey'); - - if (this.bip150 && this.bip150.auth) { - this.bip150.rekeyOutput(); - return; - } - - this.output.rekey(); - } - - /** - * Calculate packet size. - * @param {String} cmd - * @param {Buffer} body - * @returns {Number} - */ - - packetSize(cmd, body) { - let size = 0; - size += 4; - size += encoding.sizeVarString(cmd, 'ascii'); - size += 4; - size += body.length; - size += 16; - return size; - } - - /** - * Frame plaintext payload for the output stream. - * @param {String} cmd - * @param {Buffer} body - * @returns {Buffer} Ciphertext payload - */ - - packet(cmd, body) { - const size = this.packetSize(cmd, body); - const bw = bio.write(size); - const payloadSize = size - 20; - - bw.writeU32(payloadSize); - bw.writeVarString(cmd, 'ascii'); - bw.writeU32(body.length); - bw.writeBytes(body); - bw.seek(16); - - const msg = bw.render(); - const payload = msg.slice(4, 4 + payloadSize); - - this.maybeRekey(msg); - - this.output.encryptSize(msg); - this.output.encrypt(payload); - this.output.final().copy(msg, 4 + payloadSize); - this.output.sequence(); - - return msg; - } - - /** - * Feed ciphertext payload chunk - * to the input stream. Potentially - * emits a `packet` event. - * @param {Buffer} data - */ - - feed(data) { - this.total += data.length; - this.pending.push(data); - - while (this.total >= this.waiting) { - const chunk = this.read(this.waiting); - this.parse(chunk); - } - } - - /** - * Read and consume a number of bytes - * from the buffered stream. - * @param {Number} size - * @returns {Buffer} - */ - - read(size) { - assert(this.total >= size, 'Reading too much.'); - - if (size === 0) - return Buffer.alloc(0); - - const pending = this.pending[0]; - - if (pending.length > size) { - const chunk = pending.slice(0, size); - this.pending[0] = pending.slice(size); - this.total -= chunk.length; - return chunk; - } - - if (pending.length === size) { - const chunk = this.pending.shift(); - this.total -= chunk.length; - return chunk; - } - - const chunk = Buffer.allocUnsafe(size); - let off = 0; - - while (off < chunk.length) { - const pending = this.pending[0]; - const len = pending.copy(chunk, off); - if (len === pending.length) - this.pending.shift(); - else - this.pending[0] = pending.slice(len); - off += len; - } - - assert.strictEqual(off, chunk.length); - - this.total -= chunk.length; - - return chunk; - } - - /** - * Parse a ciphertext payload chunk. - * Potentially emits a `packet` event. - * @param {Buffer} data - */ - - parse(data) { - if (!this.hasSize) { - const size = this.input.decryptSize(data); - - assert(this.waiting === 4); - assert(data.length === 4); - - // Allow 3 batched packets of max message size (12mb). - // Not technically standard, but this protects us - // from buffering tons of data due to either an - // potential dos'er or a cipher state mismatch. - // Note that 6 is the minimum size: - // varint-cmdlen(1) str-cmd(1) u32-size(4) payload(0) - if (size < 6 || size > BIP151.MAX_MESSAGE) { - this.error('Bad packet size: %d.', size); - return; - } - - this.hasSize = true; - this.waiting = size + 16; - - return; - } - - const payload = data.slice(0, this.waiting - 16); - const tag = data.slice(this.waiting - 16, this.waiting); - - this.hasSize = false; - this.waiting = 4; - - // Authenticate payload before decrypting. - // This ensures the cipher state isn't altered - // if the payload integrity has been compromised. - this.input.auth(payload); - this.input.final(); - - if (!this.input.verify(tag)) { - this.input.sequence(); - this.error('Bad tag: %s.', tag.toString('hex')); - return; - } - - this.input.decrypt(payload); - this.input.sequence(); - - const br = bio.read(payload); - - while (br.left()) { - let cmd, body; - - try { - cmd = br.readVarString('ascii'); - body = br.readBytes(br.readU32()); - } catch (e) { - this.emit('error', e); - return; - } - - this.emit('packet', cmd, body); - } - } -} - -/** - * Cipher list. - * @enum {Number} - */ - -BIP151.ciphers = { - CHACHAPOLY: 0 -}; - -/** - * Max message size. - * @const {Number} - * @default - */ - -BIP151.MAX_MESSAGE = 12 * 1000 * 1000; - -/* - * Expose - */ - -module.exports = BIP151; diff --git a/lib/net/index.js b/lib/net/index.js index 14be16e2..26ad5710 100644 --- a/lib/net/index.js +++ b/lib/net/index.js @@ -10,8 +10,6 @@ * @module net */ -exports.BIP150 = require('./bip150'); -exports.BIP151 = require('./bip151'); exports.bip152 = require('./bip152'); exports.common = require('./common'); exports.Framer = require('./framer'); diff --git a/lib/net/packets.js b/lib/net/packets.js index d0190981..535b398d 100644 --- a/lib/net/packets.js +++ b/lib/net/packets.js @@ -60,15 +60,10 @@ exports.types = { CMPCTBLOCK: 23, GETBLOCKTXN: 24, BLOCKTXN: 25, - ENCINIT: 26, - ENCACK: 27, - AUTHCHALLENGE: 28, - AUTHREPLY: 29, - AUTHPROPOSE: 30, - UNKNOWN: 31, + UNKNOWN: 26, // Internal - INTERNAL: 32, - DATA: 33 + INTERNAL: 27, + DATA: 28 }; /** @@ -104,11 +99,6 @@ exports.typesByVal = [ 'CMPCTBLOCK', 'GETBLOCKTXN', 'BLOCKTXN', - 'ENCINIT', - 'ENCACK', - 'AUTHCHALLENGE', - 'AUTHREPLY', - 'AUTHPROPOSE', 'UNKNOWN', // Internal 'INTERNAL', @@ -2640,486 +2630,6 @@ class BlockTxnPacket extends Packet { } } -/** - * Encinit Packet - * @extends Packet - * @property {Buffer} publicKey - * @property {Number} cipher - */ - -class EncinitPacket extends Packet { - /** - * Create an `encinit` packet. - * @constructor - * @param {Buffer|null} publicKey - * @param {Number|null} cipher - */ - - constructor(publicKey, cipher) { - super(); - - this.cmd = 'encinit'; - this.type = exports.types.ENCINIT; - - this.publicKey = publicKey || common.ZERO_KEY; - this.cipher = cipher || 0; - } - - /** - * Get serialization size. - * @returns {Number} - */ - - getSize() { - return 34; - } - - /** - * Serialize encinit packet to writer. - * @param {BufferWriter} bw - */ - - toWriter(bw) { - bw.writeBytes(this.publicKey); - bw.writeU8(this.cipher); - return bw; - } - - /** - * Serialize encinit packet. - * @returns {Buffer} - */ - - toRaw() { - return this.toWriter(bio.write(34)).render(); - } - - /** - * Inject properties from buffer reader. - * @private - * @param {BufferReader} br - */ - - fromReader(br) { - this.publicKey = br.readBytes(33); - this.cipher = br.readU8(); - return this; - } - - /** - * Inject properties from serialized data. - * @private - * @param {Buffer} data - */ - - fromRaw(data) { - return this.fromReader(bio.read(data)); - } - - /** - * Instantiate getblocks packet from buffer reader. - * @param {BufferReader} br - * @returns {EncinitPacket} - */ - - static fromReader(br) { - return new this().fromReader(br); - } - - /** - * Instantiate getblocks packet from serialized data. - * @param {Buffer} data - * @param {String?} enc - * @returns {EncinitPacket} - */ - - static fromRaw(data, enc) { - if (typeof data === 'string') - data = Buffer.from(data, enc); - return new this().fromRaw(data); - } -} - -/** - * Encack Packet - * @extends Packet - * @property {Buffer} publicKey - */ - -class EncackPacket extends Packet { - /** - * Create a `encack` packet. - * @constructor - * @param {Buffer?} publicKey - */ - - constructor(publicKey) { - super(); - - this.cmd = 'encack'; - this.type = exports.types.ENCACK; - - this.publicKey = publicKey || common.ZERO_KEY; - } - - /** - * Get serialization size. - * @returns {Number} - */ - - getSize() { - return 33; - } - - /** - * Serialize encack packet to writer. - * @param {BufferWriter} bw - */ - - toWriter(bw) { - bw.writeBytes(this.publicKey); - return bw; - } - - /** - * Serialize encack packet. - * @returns {Buffer} - */ - - toRaw() { - return this.publicKey; - } - - /** - * Inject properties from buffer reader. - * @private - * @param {BufferReader} br - */ - - fromReader(br) { - this.publicKey = br.readBytes(33); - return this; - } - - /** - * Inject properties from serialized data. - * @private - * @param {Buffer} data - */ - - fromRaw(data) { - return this.fromReader(bio.read(data)); - } - - /** - * Instantiate encack packet from buffer reader. - * @param {BufferReader} br - * @returns {EncackPacket} - */ - - static fromReader(br) { - return new this().fromReader(br); - } - - /** - * Instantiate encack packet from serialized data. - * @param {Buffer} data - * @param {String?} enc - * @returns {EncackPacket} - */ - - static fromRaw(data, enc) { - if (typeof data === 'string') - data = Buffer.from(data, enc); - return new this().fromRaw(data); - } -} - -/** - * AuthChallenge Packet - * @extends Packet - * @property {Buffer} hash - */ - -class AuthChallengePacket extends Packet { - /** - * Create an `authchallenge` packet. - * @constructor - * @param {Buffer?} hash - */ - - constructor(hash) { - super(); - - this.cmd = 'authchallenge'; - this.type = exports.types.AUTHCHALLENGE; - - this.hash = hash || consensus.ZERO_HASH; - } - - /** - * Get serialization size. - * @returns {Number} - */ - - getSize() { - return 32; - } - - /** - * Serialize authchallenge packet to writer. - * @param {BufferWriter} bw - */ - - toWriter(bw) { - bw.writeBytes(this.hash); - return bw; - } - - /** - * Serialize authchallenge packet. - * @returns {Buffer} - */ - - toRaw() { - return this.hash; - } - - /** - * Inject properties from buffer reader. - * @private - * @param {BufferReader} br - */ - - fromReader(br) { - this.hash = br.readHash(); - return this; - } - - /** - * Inject properties from serialized data. - * @private - * @param {Buffer} data - */ - - fromRaw(data) { - return this.fromReader(bio.read(data)); - } - - /** - * Instantiate authchallenge packet from buffer reader. - * @param {BufferReader} br - * @returns {AuthChallengePacket} - */ - - static fromReader(br) { - return new this().fromReader(br); - } - - /** - * Instantiate authchallenge packet from serialized data. - * @param {Buffer} data - * @param {String?} enc - * @returns {AuthChallengePacket} - */ - - static fromRaw(data, enc) { - if (typeof data === 'string') - data = Buffer.from(data, enc); - return new this().fromRaw(data); - } -} - -/** - * AuthReply Packet - * @extends Packet - * @property {Buffer} signature - */ - -class AuthReplyPacket extends Packet { - /** - * Create a `authreply` packet. - * @constructor - * @param {Buffer?} signature - */ - - constructor(signature) { - super(); - - this.cmd = 'authreply'; - this.type = exports.types.AUTHREPLY; - - this.signature = signature || common.ZERO_SIG; - } - - /** - * Get serialization size. - * @returns {Number} - */ - - getSize() { - return 64; - } - - /** - * Serialize authreply packet to writer. - * @param {BufferWriter} bw - */ - - toWriter(bw) { - bw.writeBytes(this.signature); - return bw; - } - - /** - * Serialize authreply packet. - * @returns {Buffer} - */ - - toRaw() { - return this.signature; - } - - /** - * Inject properties from buffer reader. - * @private - * @param {BufferReader} br - */ - - fromReader(br) { - this.signature = br.readBytes(64); - return this; - } - - /** - * Inject properties from serialized data. - * @private - * @param {Buffer} data - */ - - fromRaw(data) { - return this.fromReader(bio.read(data)); - } - - /** - * Instantiate authreply packet from buffer reader. - * @param {BufferReader} br - * @returns {AuthReplyPacket} - */ - - static fromReader(br) { - return new this().fromReader(br); - } - - /** - * Instantiate authreply packet from serialized data. - * @param {Buffer} data - * @param {String?} enc - * @returns {AuthReplyPacket} - */ - - static fromRaw(data, enc) { - if (typeof data === 'string') - data = Buffer.from(data, enc); - return new this().fromRaw(data); - } -} - -/** - * AuthPropose Packet - * @extends Packet - * @property {Hash} hash - */ - -class AuthProposePacket extends Packet { - /** - * Create a `authpropose` packet. - * @constructor - * @param {Hash?} hash - */ - - constructor(hash) { - super(); - - this.cmd = 'authpropose'; - this.type = exports.types.AUTHPROPOSE; - - this.hash = hash || consensus.ZERO_HASH; - } - - /** - * Get serialization size. - * @returns {Number} - */ - - getSize() { - return 32; - } - - /** - * Serialize authpropose packet to writer. - * @param {BufferWriter} bw - */ - - toWriter(bw) { - bw.writeBytes(this.hash); - return bw; - } - - /** - * Serialize authpropose packet. - * @returns {Buffer} - */ - - toRaw() { - return this.hash; - } - - /** - * Inject properties from buffer reader. - * @private - * @param {BufferReader} br - */ - - fromReader(br) { - this.hash = br.readHash(); - return this; - } - - /** - * Inject properties from serialized data. - * @private - * @param {Buffer} data - */ - - fromRaw(data) { - return this.fromReader(bio.read(data)); - } - - /** - * Instantiate authpropose packet from buffer reader. - * @param {BufferReader} br - * @returns {AuthProposePacket} - */ - - static fromReader(br) { - return new this().fromReader(br); - } - - /** - * Instantiate authpropose packet from serialized data. - * @param {Buffer} data - * @param {String?} enc - * @returns {AuthProposePacket} - */ - - static fromRaw(data, enc) { - if (typeof data === 'string') - data = Buffer.from(data, enc); - return new this().fromRaw(data); - } -} - /** * Unknown Packet * @extends Packet @@ -3259,16 +2769,6 @@ exports.fromRaw = function fromRaw(cmd, data) { return GetBlockTxnPacket.fromRaw(data); case 'blocktxn': return BlockTxnPacket.fromRaw(data); - case 'encinit': - return EncinitPacket.fromRaw(data); - case 'encack': - return EncackPacket.fromRaw(data); - case 'authchallenge': - return AuthChallengePacket.fromRaw(data); - case 'authreply': - return AuthReplyPacket.fromRaw(data); - case 'authpropose': - return AuthProposePacket.fromRaw(data); default: return UnknownPacket.fromRaw(cmd, data); } @@ -3305,9 +2805,4 @@ exports.SendCmpctPacket = SendCmpctPacket; exports.CmpctBlockPacket = CmpctBlockPacket; exports.GetBlockTxnPacket = GetBlockTxnPacket; exports.BlockTxnPacket = BlockTxnPacket; -exports.EncinitPacket = EncinitPacket; -exports.EncackPacket = EncackPacket; -exports.AuthChallengePacket = AuthChallengePacket; -exports.AuthReplyPacket = AuthReplyPacket; -exports.AuthProposePacket = AuthProposePacket; exports.UnknownPacket = UnknownPacket; diff --git a/lib/net/peer.js b/lib/net/peer.js index 804ef397..ece37414 100644 --- a/lib/net/peer.js +++ b/lib/net/peer.js @@ -22,8 +22,6 @@ const packets = require('./packets'); const consensus = require('../protocol/consensus'); const common = require('./common'); const InvItem = require('../primitives/invitem'); -const BIP151 = require('./bip151'); -const BIP150 = require('./bip150'); const BIP152 = require('./bip152'); const Block = require('../primitives/block'); const TX = require('../primitives/tx'); @@ -114,8 +112,6 @@ class Peer extends EventEmitter { this.hashContinue = null; this.spvFilter = null; this.feeRate = -1; - this.bip151 = null; - this.bip150 = null; this.compactMode = -1; this.compactWitness = false; this.merkleBlock = null; @@ -228,8 +224,6 @@ class Peer extends EventEmitter { */ framePacket(cmd, payload, checksum) { - if (this.bip151 && this.bip151.handshake) - return this.bip151.packet(cmd, payload); return this.framer.packet(cmd, payload, checksum); } @@ -239,68 +233,9 @@ class Peer extends EventEmitter { */ feedParser(data) { - if (this.bip151 && this.bip151.handshake) - return this.bip151.feed(data); return this.parser.feed(data); } - /** - * Set BIP151 cipher type. - * @param {Number} cipher - */ - - setCipher(cipher) { - assert(!this.bip151, 'BIP151 already set.'); - assert(this.socket, 'Peer must be initialized with a socket.'); - assert(!this.opened, 'Cannot set cipher after open.'); - - this.bip151 = new BIP151(cipher); - - this.bip151.on('error', (err) => { - this.error(err); - this.destroy(); - }); - - this.bip151.on('rekey', () => { - if (this.destroyed) - return; - - this.logger.debug('Rekeying with peer (%s).', this.hostname()); - this.send(this.bip151.toRekey()); - }); - - this.bip151.on('packet', (cmd, body) => { - let payload = null; - try { - payload = this.parser.parsePayload(cmd, body); - } catch (e) { - this.parser.error(e); - return; - } - this.parser.emit('packet', payload); - }); - } - - /** - * Set BIP150 auth. - * @param {AuthDB} db - * @param {Buffer} key - */ - - setAuth(db, key) { - const bip151 = this.bip151; - const hostname = this.hostname(); - const outbound = this.outbound; - - assert(this.bip151, 'BIP151 not set.'); - assert(!this.bip150, 'BIP150 already set.'); - assert(this.socket, 'Peer must be initialized with a socket.'); - assert(!this.opened, 'Cannot set auth after open.'); - - this.bip150 = new BIP150(bip151, hostname, outbound, db, key); - this.bip151.bip150 = this.bip150; - } - /** * Bind to socket. * @param {net.Socket} socket @@ -437,8 +372,6 @@ class Peer extends EventEmitter { // Connect to peer. await this.initConnect(); await this.initStall(); - await this.initBIP151(); - await this.initBIP150(); await this.initVersion(); await this.finalize(); @@ -510,83 +443,6 @@ class Peer extends EventEmitter { return Promise.resolve(); } - /** - * Handle `connect` event (called immediately - * if a socket was passed into peer). - * @method - * @private - * @returns {Promise} - */ - - async initBIP151() { - assert(!this.destroyed); - - // Send encinit. Wait for handshake to complete. - if (!this.bip151) - return; - - assert(!this.bip151.completed); - - this.logger.info('Attempting BIP151 handshake (%s).', this.hostname()); - - this.send(this.bip151.toEncinit()); - - try { - await this.bip151.wait(3000); - } catch (err) { - this.error(err); - } - - if (this.destroyed) - throw new Error('Peer was destroyed during BIP151 handshake.'); - - assert(this.bip151.completed); - - if (this.bip151.handshake) { - this.logger.info('BIP151 handshake complete (%s).', this.hostname()); - this.logger.info('Connection is encrypted (%s).', this.hostname()); - } - } - - /** - * Handle post bip151-handshake. - * @method - * @private - * @returns {Promise} - */ - - async initBIP150() { - assert(!this.destroyed); - - if (!this.bip150) - return; - - assert(this.bip151); - assert(!this.bip150.completed); - - if (!this.bip151.handshake) - throw new Error('BIP151 handshake was not completed for BIP150.'); - - this.logger.info('Attempting BIP150 handshake (%s).', this.hostname()); - - if (this.bip150.outbound) { - if (!this.bip150.peerIdentity) - throw new Error('No known identity for peer.'); - this.send(this.bip150.toChallenge()); - } - - await this.bip150.wait(3000); - - assert(!this.destroyed); - assert(this.bip150.completed); - - if (this.bip150.auth) { - this.logger.info('BIP150 handshake complete (%s).', this.hostname()); - this.logger.info('Peer is authed (%s): %s.', - this.hostname(), this.bip150.getAddress()); - } - } - /** * Handle post handshake. * @method @@ -981,12 +837,6 @@ class Peer extends EventEmitter { this.socket.destroy(); this.socket = null; - if (this.bip151) - this.bip151.destroy(); - - if (this.bip150) - this.bip150.destroy(); - if (this.pingTimer != null) { clearInterval(this.pingTimer); this.pingTimer = null; @@ -1298,7 +1148,7 @@ class Peer extends EventEmitter { if (this.responseMap.size >= common.MAX_REQUEST) { this.destroy(); - return; + return null; } } @@ -1335,13 +1185,13 @@ class Peer extends EventEmitter { wait(type, timeout) { return new Promise((resolve, reject) => { - if (this.destroyed) { + const entry = this.request(type); + + if (!entry) { reject(new Error('Peer is destroyed (request).')); return; } - const entry = this.request(type); - entry.setTimeout(timeout); entry.addJob(resolve, reject); }); @@ -1486,11 +1336,6 @@ class Peer extends EventEmitter { // is handled at a low level. They // must be handled immediately. switch (packet.type) { - case packetTypes.ENCINIT: - case packetTypes.ENCACK: - case packetTypes.AUTHCHALLENGE: - case packetTypes.AUTHREPLY: - case packetTypes.AUTHPROPOSE: case packetTypes.PONG: { try { this.socket.pause(); @@ -1527,23 +1372,6 @@ class Peer extends EventEmitter { if (this.destroyed) throw new Error('Destroyed peer sent a packet.'); - if (this.bip151 - && this.bip151.job - && !this.bip151.completed - && packet.type !== packetTypes.ENCINIT - && packet.type !== packetTypes.ENCACK) { - this.bip151.reject(new Error('Message before BIP151 handshake.')); - } - - if (this.bip150 - && this.bip150.job - && !this.bip150.completed - && packet.type !== packetTypes.AUTHCHALLENGE - && packet.type !== packetTypes.AUTHREPLY - && packet.type !== packetTypes.AUTHPROPOSE) { - this.bip150.reject(new Error('Message before BIP150 auth.')); - } - const entry = this.fulfill(packet); switch (packet.type) { @@ -1577,21 +1405,6 @@ class Peer extends EventEmitter { case packetTypes.SENDCMPCT: await this.handleSendCmpct(packet); break; - case packetTypes.ENCINIT: - await this.handleEncinit(packet); - break; - case packetTypes.ENCACK: - await this.handleEncack(packet); - break; - case packetTypes.AUTHCHALLENGE: - await this.handleAuthChallenge(packet); - break; - case packetTypes.AUTHREPLY: - await this.handleAuthReply(packet); - break; - case packetTypes.AUTHPROPOSE: - await this.handleAuthPropose(packet); - break; } if (this.onPacket) @@ -1858,85 +1671,6 @@ class Peer extends EventEmitter { this.compactWitness = packet.version === 2; } - /** - * Handle `encinit` packet. - * @method - * @private - * @param {EncinitPacket} packet - */ - - async handleEncinit(packet) { - if (!this.bip151) - return; - - this.bip151.encinit(packet.publicKey, packet.cipher); - - this.send(this.bip151.toEncack()); - } - - /** - * Handle `encack` packet. - * @method - * @private - * @param {EncackPacket} packet - */ - - async handleEncack(packet) { - if (!this.bip151) - return; - - this.bip151.encack(packet.publicKey); - } - - /** - * Handle `authchallenge` packet. - * @method - * @private - * @param {AuthChallengePacket} packet - */ - - async handleAuthChallenge(packet) { - if (!this.bip150) - return; - - const sig = this.bip150.challenge(packet.hash); - - this.send(new packets.AuthReplyPacket(sig)); - } - - /** - * Handle `authreply` packet. - * @method - * @private - * @param {AuthReplyPacket} packet - */ - - async handleAuthReply(packet) { - if (!this.bip150) - return; - - const hash = this.bip150.reply(packet.signature); - - if (hash) - this.send(new packets.AuthProposePacket(hash)); - } - - /** - * Handle `authpropose` packet. - * @method - * @private - * @param {AuthProposePacket} packet - */ - - async handleAuthPropose(packet) { - if (!this.bip150) - return; - - const hash = this.bip150.propose(packet.hash); - - this.send(new packets.AuthChallengePacket(hash)); - } - /** * Send `getheaders` to peer. Note that unlike * `getblocks`, `getheaders` can have a null locator. diff --git a/lib/net/pool.js b/lib/net/pool.js index 33e54d1c..74c8d670 100644 --- a/lib/net/pool.js +++ b/lib/net/pool.js @@ -17,13 +17,10 @@ const UPNP = require('bupnp'); const socks = require('bsocks'); const List = require('blst'); const {BloomFilter, RollingFilter} = require('bfilter'); -const secp256k1 = require('bcrypto/lib/secp256k1'); const util = require('../utils/util'); const common = require('./common'); const chainCommon = require('../blockchain/common'); const Address = require('../primitives/address'); -const BIP150 = require('./bip150'); -const BIP151 = require('./bip151'); const BIP152 = require('./bip152'); const Network = require('../protocol/network'); const Peer = require('./peer'); @@ -82,7 +79,6 @@ class Pool extends EventEmitter { this.headerTip = null; this.peers = new PeerList(); - this.authdb = new BIP150.AuthDB(this.options); this.hosts = new HostList(this.options); this.id = 0; @@ -182,12 +178,6 @@ class Pool extends EventEmitter { this.logger.info('Pool loaded (maxpeers=%d).', this.options.maxOutbound); - if (this.options.bip150) { - const key = secp256k1.publicKeyCreate(this.options.identityKey, true); - this.logger.info('Identity public key: %s.', key.toString('hex')); - this.logger.info('Identity address: %s.', BIP150.address(key)); - } - this.resetChain(); } @@ -257,7 +247,6 @@ class Pool extends EventEmitter { return; await this.hosts.open(); - await this.authdb.open(); await this.discoverGateway(); await this.discoverExternal(); @@ -324,7 +313,6 @@ class Pool extends EventEmitter { this.stopTimer(); - await this.authdb.close(); await this.hosts.close(); await this.unlisten(); @@ -1063,18 +1051,10 @@ class Pool extends EventEmitter { */ createOutbound(addr) { - const cipher = BIP151.ciphers.CHACHAPOLY; - const identity = this.options.identityKey; const peer = Peer.fromOutbound(this.options, addr); this.hosts.markAttempt(addr.hostname); - if (this.options.bip151) - peer.setCipher(cipher); - - if (this.options.bip150) - peer.setAuth(this.authdb, identity); - this.bindPeer(peer); this.logger.debug('Connecting to %s.', peer.hostname()); @@ -1092,16 +1072,8 @@ class Pool extends EventEmitter { */ createInbound(socket) { - const cipher = BIP151.ciphers.CHACHAPOLY; - const identity = this.options.identityKey; const peer = Peer.fromInbound(this.options, socket); - if (this.options.bip151) - peer.setCipher(cipher); - - if (this.options.bip150) - peer.setAuth(this.authdb, identity); - this.bindPeer(peer); peer.tryOpen(); @@ -1255,21 +1227,6 @@ class Pool extends EventEmitter { case packetTypes.BLOCKTXN: await this.handleBlockTxn(peer, packet); break; - case packetTypes.ENCINIT: - await this.handleEncinit(peer, packet); - break; - case packetTypes.ENCACK: - await this.handleEncack(peer, packet); - break; - case packetTypes.AUTHCHALLENGE: - await this.handleAuthChallenge(peer, packet); - break; - case packetTypes.AUTHREPLY: - await this.handleAuthReply(peer, packet); - break; - case packetTypes.AUTHPROPOSE: - await this.handleAuthPropose(peer, packet); - break; case packetTypes.UNKNOWN: await this.handleUnknown(peer, packet); break; @@ -1593,6 +1550,7 @@ class Pool extends EventEmitter { const blocks = []; const txs = []; + let unknown = -1; for (const item of items) { @@ -2915,66 +2873,6 @@ class Pool extends EventEmitter { await this.addBlock(peer, block.toBlock(), flags); } - /** - * Handle `encinit` packet. - * @method - * @private - * @param {Peer} peer - * @param {EncinitPacket} packet - */ - - async handleEncinit(peer, packet) { - ; - } - - /** - * Handle `encack` packet. - * @method - * @private - * @param {Peer} peer - * @param {EncackPacket} packet - */ - - async handleEncack(peer, packet) { - ; - } - - /** - * Handle `authchallenge` packet. - * @method - * @private - * @param {Peer} peer - * @param {AuthChallengePacket} packet - */ - - async handleAuthChallenge(peer, packet) { - ; - } - - /** - * Handle `authreply` packet. - * @method - * @private - * @param {Peer} peer - * @param {AuthReplyPacket} packet - */ - - async handleAuthReply(peer, packet) { - ; - } - - /** - * Handle `authpropose` packet. - * @method - * @private - * @param {Peer} peer - * @param {AuthProposePacket} packet - */ - - async handleAuthPropose(peer, packet) { - ; - } - /** * Handle `unknown` packet. * @method @@ -3636,11 +3534,6 @@ class PoolOptions { this.selfish = false; this.version = common.PROTOCOL_VERSION; this.agent = common.USER_AGENT; - this.bip151 = false; - this.bip150 = false; - this.authPeers = []; - this.knownPeers = {}; - this.identityKey = secp256k1.generatePrivateKey(); this.banScore = common.BAN_SCORE; this.banTime = common.BAN_TIME; this.feeRate = -1; @@ -3808,36 +3701,6 @@ class PoolOptions { this.agent = options.agent; } - if (options.bip151 != null) { - assert(typeof options.bip151 === 'boolean'); - this.bip151 = options.bip151; - } - - if (options.bip150 != null) { - assert(typeof options.bip150 === 'boolean'); - assert(!options.bip150 || this.bip151, - 'Cannot enable bip150 without bip151.'); - - if (options.knownPeers) { - assert(typeof options.knownPeers === 'object'); - assert(!Array.isArray(options.knownPeers)); - this.knownPeers = options.knownPeers; - } - - if (options.authPeers) { - assert(Array.isArray(options.authPeers)); - this.authPeers = options.authPeers; - } - - if (options.identityKey) { - assert(Buffer.isBuffer(options.identityKey), - 'Identity key must be a buffer.'); - assert(secp256k1.privateKeyVerify(options.identityKey), - 'Invalid identity key.'); - this.identityKey = options.identityKey; - } - } - if (options.banScore != null) { assert(typeof this.options.banScore === 'number'); this.banScore = this.options.banScore; diff --git a/lib/node/fullnode.js b/lib/node/fullnode.js index 0420b383..2794d398 100644 --- a/lib/node/fullnode.js +++ b/lib/node/fullnode.js @@ -93,9 +93,6 @@ class FullNode extends Node { selfish: this.config.bool('selfish'), compact: this.config.bool('compact'), bip37: this.config.bool('bip37'), - bip151: this.config.bool('bip151'), - bip150: this.config.bool('bip150'), - identityKey: this.config.buf('identity-key'), maxOutbound: this.config.uint('max-outbound'), maxInbound: this.config.uint('max-inbound'), createSocket: this.config.func('create-socket'), diff --git a/lib/node/spvnode.js b/lib/node/spvnode.js index b0b0e319..db13fea6 100644 --- a/lib/node/spvnode.js +++ b/lib/node/spvnode.js @@ -68,9 +68,6 @@ class SPVNode extends Node { seeds: this.config.array('seeds'), nodes: this.config.array('nodes'), only: this.config.array('only'), - bip151: this.config.bool('bip151'), - bip150: this.config.bool('bip150'), - identityKey: this.config.buf('identity-key'), maxOutbound: this.config.uint('max-outbound'), createSocket: this.config.func('create-socket'), memory: this.config.bool('memory'), diff --git a/test/bip150-test.js b/test/bip150-test.js deleted file mode 100644 index 7f745f62..00000000 --- a/test/bip150-test.js +++ /dev/null @@ -1,238 +0,0 @@ -/* eslint-env mocha */ -/* eslint prefer-arrow-callback: "off" */ - -'use strict'; - -const assert = require('./util/assert'); -const secp256k1 = require('bcrypto/lib/secp256k1'); -const BIP150 = require('../lib/net/bip150'); -const BIP151 = require('../lib/net/bip151'); - -const db = new BIP150.AuthDB(); -const ck = secp256k1.generatePrivateKey(); -const sk = secp256k1.generatePrivateKey(); - -db.addAuthorized(secp256k1.publicKeyCreate(ck, true)); -db.addKnown('127.0.0.2', secp256k1.publicKeyCreate(sk, true)); - -const client = new BIP151(); -const server = new BIP151(); - -client.bip150 = new BIP150(client, '127.0.0.2', true, db, ck); -server.bip150 = new BIP150(server, '127.0.0.1', false, db, sk); - -function payload() { - return Buffer.from('deadbeef', 'hex'); -} - -describe('BIP150', function() { - it('should do encinit', () => { - const init = server.toEncinit(); - client.encinit(init.publicKey, init.cipher); - - const init2 = client.toEncinit(); - server.encinit(init2.publicKey, init2.cipher); - - assert(!client.handshake); - assert(!server.handshake); - }); - - it('should do encack', () => { - client.encack(server.toEncack().publicKey); - server.encack(client.toEncack().publicKey); - assert(client.handshake); - assert(server.handshake); - }); - - it('should have completed ECDH handshake', () => { - assert(client.isReady()); - assert(server.isReady()); - assert(client.handshake); - assert(server.handshake); - }); - - it('should do BIP150 handshake', () => { - const challenge = client.bip150.toChallenge(); - const reply = server.bip150.challenge(challenge.hash); - const propose = client.bip150.reply(reply); - const challenge2 = server.bip150.propose(propose); - const reply2 = client.bip150.challenge(challenge2); - const result = server.bip150.reply(reply2); - - assert(!result); - assert(client.bip150.auth); - assert(server.bip150.auth); - }); - - it('should encrypt payload from client to server', () => { - const packet = client.packet('fake', payload()); - - let emitted = false; - server.once('packet', (cmd, body) => { - emitted = true; - assert.strictEqual(cmd, 'fake'); - assert.bufferEqual(body, payload()); - }); - - server.feed(packet); - - assert(emitted); - }); - - it('should encrypt payload from server to client', () => { - const packet = server.packet('fake', payload()); - - let emitted = false; - client.once('packet', (cmd, body) => { - emitted = true; - assert.strictEqual(cmd, 'fake'); - assert.bufferEqual(body, payload()); - }); - - client.feed(packet); - - assert(emitted); - }); - - it('should encrypt payload from client to server (2)', () => { - const packet = client.packet('fake', payload()); - - let emitted = false; - server.once('packet', (cmd, body) => { - emitted = true; - assert.strictEqual(cmd, 'fake'); - assert.bufferEqual(body, payload()); - }); - - server.feed(packet); - - assert(emitted); - }); - - it('should encrypt payload from server to client (2)', () => { - const packet = server.packet('fake', payload()); - - let emitted = false; - client.once('packet', (cmd, body) => { - emitted = true; - assert.strictEqual(cmd, 'fake'); - assert.bufferEqual(body, payload()); - }); - - client.feed(packet); - - assert(emitted); - }); - - it('client should rekey', () => { - const bytes = client.output.processed; - let rekeyed = false; - - client.once('rekey', () => { - rekeyed = true; - const packet = client.packet('encack', client.toRekey().toRaw()); - let emitted = false; - server.once('packet', (cmd, body) => { - emitted = true; - assert.strictEqual(cmd, 'encack'); - server.encack(body); - }); - server.feed(packet); - assert(emitted); - }); - - // Force a rekey after 1gb processed. - client.maybeRekey({ length: 1024 * (1 << 20) }); - - assert(rekeyed); - - // 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', () => { - const packet = client.packet('fake', payload()); - - let emitted = false; - server.once('packet', (cmd, body) => { - emitted = true; - assert.strictEqual(cmd, 'fake'); - assert.bufferEqual(body, payload()); - }); - - server.feed(packet); - - assert(emitted); - }); - - it('should encrypt payload from server to client after rekey', () => { - const packet = server.packet('fake', payload()); - - let emitted = false; - client.once('packet', (cmd, body) => { - emitted = true; - assert.strictEqual(cmd, 'fake'); - assert.bufferEqual(body, payload()); - }); - - client.feed(packet); - - assert(emitted); - }); - - it('should encrypt payload from client to server after rekey (2)', () => { - const packet = client.packet('fake', payload()); - - let emitted = false; - server.once('packet', (cmd, body) => { - emitted = true; - assert.strictEqual(cmd, 'fake'); - assert.bufferEqual(body, payload()); - }); - - server.feed(packet); - - assert(emitted); - }); - - it('should encrypt payload from server to client after rekey (2)', () => { - const packet = server.packet('fake', payload()); - - let emitted = false; - client.once('packet', (cmd, body) => { - emitted = true; - assert.strictEqual(cmd, 'fake'); - assert.bufferEqual(body, payload()); - }); - - client.feed(packet); - - assert(emitted); - }); - - it('should encrypt payloads both ways asynchronously', () => { - const spacket = server.packet('fake', payload()); - const cpacket = client.packet('fake', payload()); - - let cemitted = false; - client.once('packet', (cmd, body) => { - cemitted = true; - assert.strictEqual(cmd, 'fake'); - assert.bufferEqual(body, payload()); - }); - - let semitted = false; - server.once('packet', (cmd, body) => { - semitted = true; - assert.strictEqual(cmd, 'fake'); - assert.bufferEqual(body, payload()); - }); - - client.feed(spacket); - server.feed(cpacket); - - assert(cemitted); - assert(semitted); - }); -}); diff --git a/test/bip151-test.js b/test/bip151-test.js deleted file mode 100644 index 42e7e86e..00000000 --- a/test/bip151-test.js +++ /dev/null @@ -1,213 +0,0 @@ -/* eslint-env mocha */ -/* eslint prefer-arrow-callback: "off" */ - -'use strict'; - -const assert = require('./util/assert'); -const BIP151 = require('../lib/net/bip151'); - -const client = new BIP151(); -const server = new BIP151(); - -function payload() { - return Buffer.from('deadbeef', 'hex'); -} - -describe('BIP151', function() { - it('should do encinit', () => { - let init = server.toEncinit(); - client.encinit(init.publicKey, init.cipher); - - init = client.toEncinit(); - server.encinit(init.publicKey, init.cipher); - - assert(!client.handshake); - assert(!server.handshake); - }); - - it('should do encack', () => { - client.encack(server.toEncack().publicKey); - server.encack(client.toEncack().publicKey); - assert(client.handshake); - assert(server.handshake); - }); - - it('should have completed ECDH handshake', () => { - assert(client.isReady()); - assert(server.isReady()); - assert(client.handshake); - assert(server.handshake); - }); - - it('should encrypt payload from client to server', () => { - const packet = client.packet('fake', payload()); - - let emitted = false; - server.once('packet', (cmd, body) => { - emitted = true; - assert.strictEqual(cmd, 'fake'); - assert.bufferEqual(body, payload()); - }); - - server.feed(packet); - - assert(emitted); - }); - - it('should encrypt payload from server to client', () => { - const packet = server.packet('fake', payload()); - - let emitted = false; - client.once('packet', (cmd, body) => { - emitted = true; - assert.strictEqual(cmd, 'fake'); - assert.bufferEqual(body, payload()); - }); - - client.feed(packet); - - assert(emitted); - }); - - it('should encrypt payload from client to server (2)', () => { - const packet = client.packet('fake', payload()); - - let emitted = false; - server.once('packet', (cmd, body) => { - emitted = true; - assert.strictEqual(cmd, 'fake'); - assert.bufferEqual(body, payload()); - }); - - server.feed(packet); - - assert(emitted); - }); - - it('should encrypt payload from server to client (2)', () => { - const packet = server.packet('fake', payload()); - - let emitted = false; - client.once('packet', (cmd, body) => { - emitted = true; - assert.strictEqual(cmd, 'fake'); - assert.bufferEqual(body, payload()); - }); - - client.feed(packet); - - assert(emitted); - }); - - it('client should rekey', () => { - const bytes = client.output.processed; - let rekeyed = false; - - client.once('rekey', () => { - rekeyed = true; - const packet = client.packet('encack', client.toRekey().toRaw()); - let emitted = false; - server.once('packet', (cmd, body) => { - emitted = true; - assert.strictEqual(cmd, 'encack'); - server.encack(body); - }); - server.feed(packet); - assert(emitted); - }); - - // Force a rekey after 1gb processed. - client.maybeRekey({ length: 1024 * (1 << 20) }); - - assert(rekeyed); - - // 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', () => { - const packet = client.packet('fake', payload()); - - let emitted = false; - server.once('packet', (cmd, body) => { - emitted = true; - assert.strictEqual(cmd, 'fake'); - assert.bufferEqual(body, payload()); - }); - - server.feed(packet); - - assert(emitted); - }); - - it('should encrypt payload from server to client after rekey', () => { - const packet = server.packet('fake', payload()); - - let emitted = false; - client.once('packet', (cmd, body) => { - emitted = true; - assert.strictEqual(cmd, 'fake'); - assert.bufferEqual(body, payload()); - }); - - client.feed(packet); - - assert(emitted); - }); - - it('should encrypt payload from client to server after rekey (2)', () => { - const packet = client.packet('fake', payload()); - - let emitted = false; - server.once('packet', (cmd, body) => { - emitted = true; - assert.strictEqual(cmd, 'fake'); - assert.bufferEqual(body, payload()); - }); - - server.feed(packet); - - assert(emitted); - }); - - it('should encrypt payload from server to client after rekey (2)', () => { - const packet = server.packet('fake', payload()); - - let emitted = false; - client.once('packet', (cmd, body) => { - emitted = true; - assert.strictEqual(cmd, 'fake'); - assert.bufferEqual(body, payload()); - }); - - client.feed(packet); - - assert(emitted); - }); - - it('should encrypt payloads both ways asynchronously', () => { - const spacket = server.packet('fake', payload()); - const cpacket = client.packet('fake', payload()); - - let cemitted = false; - client.once('packet', (cmd, body) => { - cemitted = true; - assert.strictEqual(cmd, 'fake'); - assert.bufferEqual(body, payload()); - }); - - let semitted = false; - server.once('packet', (cmd, body) => { - semitted = true; - assert.strictEqual(cmd, 'fake'); - assert.bufferEqual(body, payload()); - }); - - client.feed(spacket); - server.feed(cpacket); - - assert(cemitted); - assert(semitted); - }); -});