Skip to content
This repository was archived by the owner on Feb 24, 2021. It is now read-only.

Commit c3f1f34

Browse files
authored
fix: replace node buffers with uint8arrays (#124)
* 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 * chore: remove buffer api usage from tests
1 parent 328e68f commit c3f1f34

File tree

7 files changed

+76
-55
lines changed

7 files changed

+76
-55
lines changed

benchmarks/send.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ async function sendData (a, b, opts) {
2222
pipe(
2323
function * () {
2424
while (i--) {
25-
yield Buffer.allocUnsafe(opts.size)
25+
yield new Uint8Array(opts.size)
2626
}
2727
},
2828
a

package.json

+9-10
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,18 @@
3535
"it-pair": "^1.0.0",
3636
"it-pb-rpc": "^0.1.4",
3737
"it-pipe": "^1.1.0",
38-
"libp2p-crypto": "^0.17.3",
39-
"libp2p-interfaces": "^0.2.1",
40-
"multiaddr": "^7.2.1",
41-
"multihashing-async": "^0.8.0",
42-
"peer-id": "^0.13.6",
43-
"protons": "^1.0.2"
38+
"libp2p-crypto": "^0.18.0",
39+
"libp2p-interfaces": "^0.3.2",
40+
"multiaddr": "^8.0.0",
41+
"multihashing-async": "^2.0.1",
42+
"peer-id": "^0.14.0",
43+
"protons": "^2.0.0",
44+
"uint8arrays": "^1.1.0"
4445
},
4546
"devDependencies": {
46-
"aegir": "^22.0.0",
47+
"aegir": "^25.0.0",
4748
"benchmark": "^2.1.4",
48-
"chai": "^4.2.0",
49-
"dirty-chai": "^2.0.1",
50-
"streaming-iterables": "^4.1.1"
49+
"streaming-iterables": "^5.0.2"
5150
},
5251
"engines": {
5352
"node": ">=6.0.0",

src/etm.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
const BufferList = require('bl/BufferList')
44
const { InvalidCryptoTransmissionError } = require('libp2p-interfaces/src/crypto/errors')
5+
const uint8ArrayToString = require('uint8arrays/to-string')
6+
const uint8ArrayEquals = require('uint8arrays/equals')
57

68
exports.createBoxStream = (cipher, mac) => {
79
return async function * (source) {
@@ -29,8 +31,8 @@ exports.createUnboxStream = (decipher, mac) => {
2931

3032
const expected = await mac.digest(data)
3133

32-
if (!macd.equals(expected)) {
33-
throw new InvalidCryptoTransmissionError(`MAC Invalid: ${macd.toString('hex')} != ${expected.toString('hex')}`)
34+
if (!uint8ArrayEquals(macd, expected)) {
35+
throw new InvalidCryptoTransmissionError(`MAC Invalid: ${uint8ArrayToString(macd, 'base16')} != ${uint8ArrayToString(expected, 'base16')}`)
3436
}
3537

3638
const decrypted = await decipher.decrypt(data)

src/handshake/crypto.js

+8-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
'use strict'
22

3-
const { Buffer } = require('buffer')
43
const PeerId = require('peer-id')
54
const crypto = require('libp2p-crypto')
65
const debug = require('debug')
6+
const uint8ArrayConcat = require('uint8arrays/concat')
7+
const uint8ArrayEquals = require('uint8arrays/equals')
8+
const uint8ArrayToString = require('uint8arrays/to-string')
79
const log = debug('libp2p:secio')
810
log.error = debug('libp2p:secio:error')
911

@@ -36,7 +38,7 @@ exports.createExchange = async (state) => {
3638
state.shared.generate = res.genSharedKey
3739

3840
// Gather corpus to sign.
39-
const selectionOut = Buffer.concat([
41+
const selectionOut = uint8ArrayConcat([
4042
state.proposalEncoded.out,
4143
state.proposalEncoded.in,
4244
state.ephemeralKey.local
@@ -61,7 +63,7 @@ exports.identify = async (state, msg) => {
6163

6264
state.key.remote = crypto.keys.unmarshalPublicKey(pubkey)
6365

64-
const remoteId = await PeerId.createFromPubKey(pubkey.toString('base64'))
66+
const remoteId = await PeerId.createFromPubKey(uint8ArrayToString(pubkey, 'base64pad'))
6567

6668
// If we know who we are dialing to, double check
6769
if (state.id.remote) {
@@ -120,7 +122,7 @@ exports.verify = async (state, msg) => {
120122
state.exchange.in = pbm.Exchange.decode(msg)
121123
state.ephemeralKey.remote = state.exchange.in.epubkey
122124

123-
const selectionIn = Buffer.concat([
125+
const selectionIn = uint8ArrayConcat([
124126
state.proposalEncoded.in,
125127
state.proposalEncoded.out,
126128
state.ephemeralKey.remote
@@ -168,9 +170,9 @@ exports.generateKeys = async (state) => {
168170
exports.verifyNonce = (state, n2) => {
169171
const n1 = state.proposal.out.rand
170172

171-
if (n1.equals(n2)) return
173+
if (uint8ArrayEquals(n1, n2)) return
172174

173175
throw new Error(
174-
`Failed to read our encrypted nonce: ${n1.toString('hex')} != ${n2.toString('hex')}`
176+
`Failed to read our encrypted nonce: ${uint8ArrayToString(n1, 'base16')} != ${uint8ArrayToString(n2, 'base16')}`
175177
)
176178
}

src/support.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
'use strict'
22

3-
const { Buffer } = require('buffer')
43
const mh = require('multihashing-async')
54
const crypto = require('libp2p-crypto')
5+
const uint8ArrayConcat = require('uint8arrays/concat')
6+
const uint8ArrayCompare = require('uint8arrays/compare')
67

78
const { InvalidCryptoExchangeError } = require('libp2p-interfaces/src/crypto/errors')
89

@@ -69,16 +70,16 @@ function makeCipher (cipherType, iv, key) {
6970
}
7071

7172
exports.selectBest = async (local, remote) => {
72-
const oh1 = await exports.digest(Buffer.concat([
73+
const oh1 = await exports.digest(uint8ArrayConcat([
7374
remote.pubKeyBytes,
7475
local.nonce
7576
]))
76-
const oh2 = await exports.digest(Buffer.concat([
77+
const oh2 = await exports.digest(uint8ArrayConcat([
7778
local.pubKeyBytes,
7879
remote.nonce
7980
]))
8081

81-
const order = Buffer.compare(oh1, oh2)
82+
const order = uint8ArrayCompare(oh1, oh2)
8283

8384
if (order === 0) {
8485
throw new InvalidCryptoExchangeError('you are trying to talk to yourself')

test/secio.spec.js

+48-28
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
/* eslint-env mocha */
22
'use strict'
33

4-
const chai = require('chai')
5-
const dirtyChai = require('dirty-chai')
6-
const expect = chai.expect
7-
chai.use(dirtyChai)
4+
const { expect } = require('aegir/utils/chai')
85

96
const PeerId = require('peer-id')
107
const duplexPair = require('it-pair/duplex')
@@ -22,6 +19,7 @@ const {
2219
const { createBoxStream, createUnboxStream } = require('../src/etm')
2320
const State = require('../src/state')
2421
const { Propose } = require('../src/handshake/secio.proto')
22+
const uint8ArrayConcat = require('uint8arrays/concat')
2523

2624
describe('secio', () => {
2725
let remotePeer
@@ -43,13 +41,16 @@ describe('secio', () => {
4341
const proposal = createProposal(state)
4442

4543
// Send our proposal
46-
const proposalLength = Buffer.allocUnsafe(4)
47-
proposalLength.writeInt32BE(proposal.length, 0)
48-
wrap.write(Buffer.concat([proposalLength, proposal]))
44+
const proposalBuffer = new ArrayBuffer(4)
45+
const proposalLengthView = new DataView(proposalBuffer)
46+
proposalLengthView.setInt32(0, proposal.length)
47+
const proposalLength = new Uint8Array(proposalBuffer)
48+
wrap.write(uint8ArrayConcat([proposalLength, proposal]))
4949

5050
// Read their proposal
5151
let theirProposalRaw = (await wrap.read()).slice()
52-
let dataLength = theirProposalRaw.readInt32BE(0)
52+
const theirProposalRawView = new DataView(theirProposalRaw.buffer, theirProposalRaw.byteOffset, theirProposalRaw.byteLength)
53+
let dataLength = theirProposalRawView.getInt32(0)
5354
theirProposalRaw = theirProposalRaw.slice(4, dataLength + 4)
5455
const theirProposal = Propose.decode(theirProposalRaw)
5556
expect(theirProposal.rand).to.have.length(16)
@@ -68,13 +69,16 @@ describe('secio', () => {
6869
const exchange = await createExchange(state)
6970

7071
// Send our exchange
71-
const exchangeLength = Buffer.allocUnsafe(4)
72-
exchangeLength.writeInt32BE(exchange.length, 0)
73-
wrap.write(Buffer.concat([exchangeLength, exchange]))
72+
const exchangeBuffer = new ArrayBuffer(4)
73+
const exchangeLengthView = new DataView(exchangeBuffer)
74+
exchangeLengthView.setInt32(0, exchange.length)
75+
const exchangeLength = new Uint8Array(exchangeBuffer)
76+
wrap.write(uint8ArrayConcat([exchangeLength, exchange]))
7477

7578
// Read their exchange
7679
let theirExchangeRaw = (await wrap.read()).slice()
77-
dataLength = theirExchangeRaw.readInt32BE(0)
80+
const theirExchangeRawView = new DataView(theirExchangeRaw.buffer, theirExchangeRaw.byteOffset, theirExchangeRaw.byteLength)
81+
dataLength = theirExchangeRawView.getInt32(0)
7882
theirExchangeRaw = theirExchangeRaw.slice(4, dataLength + 4)
7983
await verify(state, theirExchangeRaw)
8084

@@ -88,13 +92,18 @@ describe('secio', () => {
8892
// Send back their nonce over the crypto stream
8993
const { value: nonce } = await box([state.proposal.in.rand]).next()
9094
expect(nonce.slice()).to.not.eql(state.proposal.in.rand) // The nonce should be encrypted
91-
const nonceLength = Buffer.allocUnsafe(4)
92-
nonceLength.writeInt32BE(nonce.length, 0)
93-
wrap.write(Buffer.concat([nonceLength, nonce.slice()]))
95+
96+
const nonceBuffer = new ArrayBuffer(4)
97+
const nonceView = new DataView(nonceBuffer)
98+
nonceView.setInt32(0, nonce.length)
99+
const nonceLength = new Uint8Array(nonceBuffer)
100+
wrap.write(uint8ArrayConcat([nonceLength, nonce.slice()]))
94101

95102
// Read our nonce from the crypto stream
96103
let ourNonceRaw = (await wrap.read())
97-
dataLength = ourNonceRaw.readInt32BE(0)
104+
const ourNonceRawBuffer = ourNonceRaw.slice()
105+
const ourNonceRawView = new DataView(ourNonceRawBuffer.buffer, ourNonceRawBuffer.byteOffset, ourNonceRawBuffer.byteLength)
106+
dataLength = ourNonceRawView.getInt32(0)
98107
ourNonceRaw = ourNonceRaw.shallowSlice(4, dataLength + 4) // Unbox expects a BufferList, so shallow slice here
99108
expect(ourNonceRaw.slice()).to.not.eql(state.proposal.out.rand) // The nonce should be encrypted
100109
const { value: ourNonce } = await unbox([ourNonceRaw]).next()
@@ -121,13 +130,16 @@ describe('secio', () => {
121130
const proposal = createProposal(state)
122131

123132
// Send our proposal
124-
const proposalLength = Buffer.allocUnsafe(4)
125-
proposalLength.writeInt32BE(proposal.length, 0)
126-
wrap.write(Buffer.concat([proposalLength, proposal]))
133+
const proposalBuffer = new ArrayBuffer(4)
134+
const proposalLengthView = new DataView(proposalBuffer)
135+
proposalLengthView.setInt32(0, proposal.length)
136+
const proposalLength = new Uint8Array(proposalBuffer)
137+
wrap.write(uint8ArrayConcat([proposalLength, proposal]))
127138

128139
// Read their proposal
129140
let theirProposalRaw = (await wrap.read()).slice()
130-
let dataLength = theirProposalRaw.readInt32BE(0)
141+
const theirProposalRawView = new DataView(theirProposalRaw.buffer, theirProposalRaw.byteOffset, theirProposalRaw.byteLength)
142+
let dataLength = theirProposalRawView.getInt32(0)
131143
theirProposalRaw = theirProposalRaw.slice(4, dataLength + 4)
132144
const theirProposal = Propose.decode(theirProposalRaw)
133145
expect(theirProposal.rand).to.have.length(16)
@@ -146,13 +158,16 @@ describe('secio', () => {
146158
const exchange = await createExchange(state)
147159

148160
// Send our exchange
149-
const exchangeLength = Buffer.allocUnsafe(4)
150-
exchangeLength.writeInt32BE(exchange.length, 0)
151-
wrap.write(Buffer.concat([exchangeLength, exchange]))
161+
const exchangeBuffer = new ArrayBuffer(4)
162+
const exchangeLengthView = new DataView(exchangeBuffer)
163+
exchangeLengthView.setInt32(0, exchange.length)
164+
const exchangeLength = new Uint8Array(exchangeBuffer)
165+
wrap.write(uint8ArrayConcat([exchangeLength, exchange]))
152166

153167
// Read their exchange
154168
let theirExchangeRaw = (await wrap.read()).slice()
155-
dataLength = theirExchangeRaw.readInt32BE(0)
169+
const theirExchangeRawView = new DataView(theirExchangeRaw.buffer, theirExchangeRaw.byteOffset, theirExchangeRaw.byteLength)
170+
dataLength = theirExchangeRawView.getInt32(0)
156171
theirExchangeRaw = theirExchangeRaw.slice(4, dataLength + 4)
157172
await verify(state, theirExchangeRaw)
158173

@@ -166,13 +181,18 @@ describe('secio', () => {
166181
// Send back their nonce over the crypto stream
167182
const { value: nonce } = await box([state.proposal.in.rand]).next()
168183
expect(nonce.slice()).to.not.eql(state.proposal.in.rand) // The nonce should be encrypted
169-
const nonceLength = Buffer.allocUnsafe(4)
170-
nonceLength.writeInt32BE(nonce.length, 0)
171-
wrap.write(Buffer.concat([nonceLength, nonce.slice()]))
184+
185+
const nonceBuffer = new ArrayBuffer(4)
186+
const nonceView = new DataView(nonceBuffer)
187+
nonceView.setInt32(0, nonce.length)
188+
const nonceLength = new Uint8Array(nonceBuffer)
189+
wrap.write(uint8ArrayConcat([nonceLength, nonce.slice()]))
172190

173191
// Read our nonce from the crypto stream
174192
let ourNonceRaw = (await wrap.read())
175-
dataLength = ourNonceRaw.readInt32BE(0)
193+
const ourNonceRawBuffer = ourNonceRaw.slice()
194+
const ourNonceRawView = new DataView(ourNonceRawBuffer.buffer, ourNonceRawBuffer.byteOffset, ourNonceRawBuffer.byteLength)
195+
dataLength = ourNonceRawView.getInt32(0)
176196
ourNonceRaw = ourNonceRaw.shallowSlice(4, dataLength + 4) // Unbox expects a BufferList, so shallow slice here
177197
expect(ourNonceRaw.slice()).to.not.eql(state.proposal.out.rand) // The nonce should be encrypted
178198
const { value: ourNonce } = await unbox([ourNonceRaw]).next()

test/support.spec.js

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
/* eslint-env mocha */
22
'use strict'
33

4-
const chai = require('chai')
5-
const dirtyChai = require('dirty-chai')
6-
const expect = chai.expect
7-
chai.use(dirtyChai)
4+
const { expect } = require('aegir/utils/chai')
85

96
const support = require('../src/support')
107

0 commit comments

Comments
 (0)