From 0ff4dd912f94246c3c42c1f8d9e084c513991969 Mon Sep 17 00:00:00 2001 From: achingbrain Date: Mon, 10 Aug 2020 10:18:37 +0100 Subject: [PATCH 1/2] fix: replace node buffers with uint8arrays All use of node buffers has been replaced with uint8arrays All deps have been updated to ones that use uint8arrays BREAKING CHANGES: - The dependencies this module has have uint8arrays as properties instead of node buffers --- benchmarks/send.js | 2 +- package.json | 19 +++++++------- src/etm.js | 6 +++-- src/handshake/crypto.js | 14 ++++++----- src/support.js | 9 ++++--- test/secio.spec.js | 56 +++++++++++++++++++++++++---------------- test/support.spec.js | 5 +--- 7 files changed, 62 insertions(+), 49 deletions(-) diff --git a/benchmarks/send.js b/benchmarks/send.js index 04e649a..0aa8da3 100644 --- a/benchmarks/send.js +++ b/benchmarks/send.js @@ -22,7 +22,7 @@ async function sendData (a, b, opts) { pipe( function * () { while (i--) { - yield Buffer.allocUnsafe(opts.size) + yield new Uint8Array(opts.size) } }, a diff --git a/package.json b/package.json index 9507e23..da44021 100644 --- a/package.json +++ b/package.json @@ -35,19 +35,18 @@ "it-pair": "^1.0.0", "it-pb-rpc": "^0.1.4", "it-pipe": "^1.1.0", - "libp2p-crypto": "^0.17.3", - "libp2p-interfaces": "^0.2.1", - "multiaddr": "^7.2.1", - "multihashing-async": "^0.8.0", - "peer-id": "^0.13.6", - "protons": "^1.0.2" + "libp2p-crypto": "^0.18.0", + "libp2p-interfaces": "^0.3.2", + "multiaddr": "^8.0.0", + "multihashing-async": "^2.0.1", + "peer-id": "^0.14.0", + "protons": "^2.0.0", + "uint8arrays": "^1.1.0" }, "devDependencies": { - "aegir": "^22.0.0", + "aegir": "^25.0.0", "benchmark": "^2.1.4", - "chai": "^4.2.0", - "dirty-chai": "^2.0.1", - "streaming-iterables": "^4.1.1" + "streaming-iterables": "^5.0.2" }, "engines": { "node": ">=6.0.0", diff --git a/src/etm.js b/src/etm.js index e51dbc6..fa6cc5f 100644 --- a/src/etm.js +++ b/src/etm.js @@ -2,6 +2,8 @@ const BufferList = require('bl/BufferList') const { InvalidCryptoTransmissionError } = require('libp2p-interfaces/src/crypto/errors') +const uint8ArrayToString = require('uint8arrays/to-string') +const uint8ArrayEquals = require('uint8arrays/equals') exports.createBoxStream = (cipher, mac) => { return async function * (source) { @@ -29,8 +31,8 @@ exports.createUnboxStream = (decipher, mac) => { const expected = await mac.digest(data) - if (!macd.equals(expected)) { - throw new InvalidCryptoTransmissionError(`MAC Invalid: ${macd.toString('hex')} != ${expected.toString('hex')}`) + if (!uint8ArrayEquals(macd, expected)) { + throw new InvalidCryptoTransmissionError(`MAC Invalid: ${uint8ArrayToString(macd, 'base16')} != ${uint8ArrayToString(expected, 'base16')}`) } const decrypted = await decipher.decrypt(data) diff --git a/src/handshake/crypto.js b/src/handshake/crypto.js index 3b16bb6..1cdc5ec 100644 --- a/src/handshake/crypto.js +++ b/src/handshake/crypto.js @@ -1,9 +1,11 @@ 'use strict' -const { Buffer } = require('buffer') const PeerId = require('peer-id') const crypto = require('libp2p-crypto') const debug = require('debug') +const uint8ArrayConcat = require('uint8arrays/concat') +const uint8ArrayEquals = require('uint8arrays/equals') +const uint8ArrayToString = require('uint8arrays/to-string') const log = debug('libp2p:secio') log.error = debug('libp2p:secio:error') @@ -36,7 +38,7 @@ exports.createExchange = async (state) => { state.shared.generate = res.genSharedKey // Gather corpus to sign. - const selectionOut = Buffer.concat([ + const selectionOut = uint8ArrayConcat([ state.proposalEncoded.out, state.proposalEncoded.in, state.ephemeralKey.local @@ -61,7 +63,7 @@ exports.identify = async (state, msg) => { state.key.remote = crypto.keys.unmarshalPublicKey(pubkey) - const remoteId = await PeerId.createFromPubKey(pubkey.toString('base64')) + const remoteId = await PeerId.createFromPubKey(uint8ArrayToString(pubkey, 'base64pad')) // If we know who we are dialing to, double check if (state.id.remote) { @@ -120,7 +122,7 @@ exports.verify = async (state, msg) => { state.exchange.in = pbm.Exchange.decode(msg) state.ephemeralKey.remote = state.exchange.in.epubkey - const selectionIn = Buffer.concat([ + const selectionIn = uint8ArrayConcat([ state.proposalEncoded.in, state.proposalEncoded.out, state.ephemeralKey.remote @@ -168,9 +170,9 @@ exports.generateKeys = async (state) => { exports.verifyNonce = (state, n2) => { const n1 = state.proposal.out.rand - if (n1.equals(n2)) return + if (uint8ArrayEquals(n1, n2)) return throw new Error( - `Failed to read our encrypted nonce: ${n1.toString('hex')} != ${n2.toString('hex')}` + `Failed to read our encrypted nonce: ${uint8ArrayToString(n1, 'base16')} != ${uint8ArrayToString(n2, 'base16')}` ) } diff --git a/src/support.js b/src/support.js index 5b1398b..eff68f6 100644 --- a/src/support.js +++ b/src/support.js @@ -1,8 +1,9 @@ 'use strict' -const { Buffer } = require('buffer') const mh = require('multihashing-async') const crypto = require('libp2p-crypto') +const uint8ArrayConcat = require('uint8arrays/concat') +const uint8ArrayCompare = require('uint8arrays/compare') const { InvalidCryptoExchangeError } = require('libp2p-interfaces/src/crypto/errors') @@ -69,16 +70,16 @@ function makeCipher (cipherType, iv, key) { } exports.selectBest = async (local, remote) => { - const oh1 = await exports.digest(Buffer.concat([ + const oh1 = await exports.digest(uint8ArrayConcat([ remote.pubKeyBytes, local.nonce ])) - const oh2 = await exports.digest(Buffer.concat([ + const oh2 = await exports.digest(uint8ArrayConcat([ local.pubKeyBytes, remote.nonce ])) - const order = Buffer.compare(oh1, oh2) + const order = uint8ArrayCompare(oh1, oh2) if (order === 0) { throw new InvalidCryptoExchangeError('you are trying to talk to yourself') diff --git a/test/secio.spec.js b/test/secio.spec.js index 4b2e252..4804d8b 100644 --- a/test/secio.spec.js +++ b/test/secio.spec.js @@ -1,10 +1,7 @@ /* eslint-env mocha */ 'use strict' -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) +const { expect } = require('aegir/utils/chai') const PeerId = require('peer-id') const duplexPair = require('it-pair/duplex') @@ -22,6 +19,7 @@ const { const { createBoxStream, createUnboxStream } = require('../src/etm') const State = require('../src/state') const { Propose } = require('../src/handshake/secio.proto') +const uint8ArrayConcat = require('uint8arrays/concat') describe('secio', () => { let remotePeer @@ -43,9 +41,11 @@ describe('secio', () => { const proposal = createProposal(state) // Send our proposal - const proposalLength = Buffer.allocUnsafe(4) - proposalLength.writeInt32BE(proposal.length, 0) - wrap.write(Buffer.concat([proposalLength, proposal])) + const proposalBuffer = new ArrayBuffer(4) + const proposalLengthView = new DataView(proposalBuffer) + proposalLengthView.setInt32(0, proposal.length) + const proposalLength = new Uint8Array(proposalBuffer) + wrap.write(uint8ArrayConcat([proposalLength, proposal])) // Read their proposal let theirProposalRaw = (await wrap.read()).slice() @@ -68,9 +68,11 @@ describe('secio', () => { const exchange = await createExchange(state) // Send our exchange - const exchangeLength = Buffer.allocUnsafe(4) - exchangeLength.writeInt32BE(exchange.length, 0) - wrap.write(Buffer.concat([exchangeLength, exchange])) + const exchangeBuffer = new ArrayBuffer(4) + const exchangeLengthView = new DataView(exchangeBuffer) + exchangeLengthView.setInt32(0, exchange.length) + const exchangeLength = new Uint8Array(exchangeBuffer) + wrap.write(uint8ArrayConcat([exchangeLength, exchange])) // Read their exchange let theirExchangeRaw = (await wrap.read()).slice() @@ -88,9 +90,12 @@ describe('secio', () => { // Send back their nonce over the crypto stream const { value: nonce } = await box([state.proposal.in.rand]).next() expect(nonce.slice()).to.not.eql(state.proposal.in.rand) // The nonce should be encrypted - const nonceLength = Buffer.allocUnsafe(4) - nonceLength.writeInt32BE(nonce.length, 0) - wrap.write(Buffer.concat([nonceLength, nonce.slice()])) + + const nonceBuffer = new ArrayBuffer(4) + const nonceView = new DataView(nonceBuffer) + nonceView.setInt32(0, nonce.length) + const nonceLength = new Uint8Array(nonceBuffer) + wrap.write(uint8ArrayConcat([nonceLength, nonce.slice()])) // Read our nonce from the crypto stream let ourNonceRaw = (await wrap.read()) @@ -121,9 +126,11 @@ describe('secio', () => { const proposal = createProposal(state) // Send our proposal - const proposalLength = Buffer.allocUnsafe(4) - proposalLength.writeInt32BE(proposal.length, 0) - wrap.write(Buffer.concat([proposalLength, proposal])) + const proposalBuffer = new ArrayBuffer(4) + const proposalLengthView = new DataView(proposalBuffer) + proposalLengthView.setInt32(0, proposal.length) + const proposalLength = new Uint8Array(proposalBuffer) + wrap.write(uint8ArrayConcat([proposalLength, proposal])) // Read their proposal let theirProposalRaw = (await wrap.read()).slice() @@ -146,9 +153,11 @@ describe('secio', () => { const exchange = await createExchange(state) // Send our exchange - const exchangeLength = Buffer.allocUnsafe(4) - exchangeLength.writeInt32BE(exchange.length, 0) - wrap.write(Buffer.concat([exchangeLength, exchange])) + const exchangeBuffer = new ArrayBuffer(4) + const exchangeLengthView = new DataView(exchangeBuffer) + exchangeLengthView.setInt32(0, exchange.length) + const exchangeLength = new Uint8Array(exchangeBuffer) + wrap.write(uint8ArrayConcat([exchangeLength, exchange])) // Read their exchange let theirExchangeRaw = (await wrap.read()).slice() @@ -166,9 +175,12 @@ describe('secio', () => { // Send back their nonce over the crypto stream const { value: nonce } = await box([state.proposal.in.rand]).next() expect(nonce.slice()).to.not.eql(state.proposal.in.rand) // The nonce should be encrypted - const nonceLength = Buffer.allocUnsafe(4) - nonceLength.writeInt32BE(nonce.length, 0) - wrap.write(Buffer.concat([nonceLength, nonce.slice()])) + + const nonceBuffer = new ArrayBuffer(4) + const nonceView = new DataView(nonceBuffer) + nonceView.setInt32(0, nonce.length) + const nonceLength = new Uint8Array(nonceBuffer) + wrap.write(uint8ArrayConcat([nonceLength, nonce.slice()])) // Read our nonce from the crypto stream let ourNonceRaw = (await wrap.read()) diff --git a/test/support.spec.js b/test/support.spec.js index d3d0a88..6083f33 100644 --- a/test/support.spec.js +++ b/test/support.spec.js @@ -1,10 +1,7 @@ /* eslint-env mocha */ 'use strict' -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) +const { expect } = require('aegir/utils/chai') const support = require('../src/support') From 950d3db98f402eb2a4b6df48d8829757ee9793cd Mon Sep 17 00:00:00 2001 From: achingbrain Date: Mon, 10 Aug 2020 10:25:52 +0100 Subject: [PATCH 2/2] chore: remove buffer api usage from tests --- test/secio.spec.js | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/test/secio.spec.js b/test/secio.spec.js index 4804d8b..dc5ca52 100644 --- a/test/secio.spec.js +++ b/test/secio.spec.js @@ -49,7 +49,8 @@ describe('secio', () => { // Read their proposal let theirProposalRaw = (await wrap.read()).slice() - let dataLength = theirProposalRaw.readInt32BE(0) + const theirProposalRawView = new DataView(theirProposalRaw.buffer, theirProposalRaw.byteOffset, theirProposalRaw.byteLength) + let dataLength = theirProposalRawView.getInt32(0) theirProposalRaw = theirProposalRaw.slice(4, dataLength + 4) const theirProposal = Propose.decode(theirProposalRaw) expect(theirProposal.rand).to.have.length(16) @@ -76,7 +77,8 @@ describe('secio', () => { // Read their exchange let theirExchangeRaw = (await wrap.read()).slice() - dataLength = theirExchangeRaw.readInt32BE(0) + const theirExchangeRawView = new DataView(theirExchangeRaw.buffer, theirExchangeRaw.byteOffset, theirExchangeRaw.byteLength) + dataLength = theirExchangeRawView.getInt32(0) theirExchangeRaw = theirExchangeRaw.slice(4, dataLength + 4) await verify(state, theirExchangeRaw) @@ -99,7 +101,9 @@ describe('secio', () => { // Read our nonce from the crypto stream let ourNonceRaw = (await wrap.read()) - dataLength = ourNonceRaw.readInt32BE(0) + const ourNonceRawBuffer = ourNonceRaw.slice() + const ourNonceRawView = new DataView(ourNonceRawBuffer.buffer, ourNonceRawBuffer.byteOffset, ourNonceRawBuffer.byteLength) + dataLength = ourNonceRawView.getInt32(0) ourNonceRaw = ourNonceRaw.shallowSlice(4, dataLength + 4) // Unbox expects a BufferList, so shallow slice here expect(ourNonceRaw.slice()).to.not.eql(state.proposal.out.rand) // The nonce should be encrypted const { value: ourNonce } = await unbox([ourNonceRaw]).next() @@ -134,7 +138,8 @@ describe('secio', () => { // Read their proposal let theirProposalRaw = (await wrap.read()).slice() - let dataLength = theirProposalRaw.readInt32BE(0) + const theirProposalRawView = new DataView(theirProposalRaw.buffer, theirProposalRaw.byteOffset, theirProposalRaw.byteLength) + let dataLength = theirProposalRawView.getInt32(0) theirProposalRaw = theirProposalRaw.slice(4, dataLength + 4) const theirProposal = Propose.decode(theirProposalRaw) expect(theirProposal.rand).to.have.length(16) @@ -161,7 +166,8 @@ describe('secio', () => { // Read their exchange let theirExchangeRaw = (await wrap.read()).slice() - dataLength = theirExchangeRaw.readInt32BE(0) + const theirExchangeRawView = new DataView(theirExchangeRaw.buffer, theirExchangeRaw.byteOffset, theirExchangeRaw.byteLength) + dataLength = theirExchangeRawView.getInt32(0) theirExchangeRaw = theirExchangeRaw.slice(4, dataLength + 4) await verify(state, theirExchangeRaw) @@ -184,7 +190,9 @@ describe('secio', () => { // Read our nonce from the crypto stream let ourNonceRaw = (await wrap.read()) - dataLength = ourNonceRaw.readInt32BE(0) + const ourNonceRawBuffer = ourNonceRaw.slice() + const ourNonceRawView = new DataView(ourNonceRawBuffer.buffer, ourNonceRawBuffer.byteOffset, ourNonceRawBuffer.byteLength) + dataLength = ourNonceRawView.getInt32(0) ourNonceRaw = ourNonceRaw.shallowSlice(4, dataLength + 4) // Unbox expects a BufferList, so shallow slice here expect(ourNonceRaw.slice()).to.not.eql(state.proposal.out.rand) // The nonce should be encrypted const { value: ourNonce } = await unbox([ourNonceRaw]).next()