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

Commit 41ca19b

Browse files
committed
test: add verify inbound and outbound secio
1 parent 840c217 commit 41ca19b

File tree

5 files changed

+205
-5
lines changed

5 files changed

+205
-5
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"it-buffer": "^0.1.1",
3131
"it-length-prefixed": "^3.0.0",
3232
"it-pair": "^1.0.0",
33-
"it-pb-rpc": "jacobheun/it-pb-rpc#master",
33+
"it-pb-rpc": "^0.1.4",
3434
"it-pipe": "^1.1.0",
3535
"libp2p-crypto": "~0.17.1",
3636
"libp2p-interfaces": "~0.1.3",

src/handshake/crypto.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
'use strict'
22

3-
const protons = require('protons')
43
const PeerId = require('peer-id')
54
const crypto = require('libp2p-crypto')
65
const debug = require('debug')
76
const log = debug('libp2p:secio')
87
log.error = debug('libp2p:secio:error')
98

10-
const pbm = protons(require('./secio.proto'))
9+
const pbm = require('./secio.proto')
1110

1211
const support = require('../support')
1312

src/handshake/secio.proto.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
'use strict'
22

3-
module.exports = `message Propose {
3+
const protons = require('protons')
4+
5+
module.exports = protons(`message Propose {
46
optional bytes rand = 1;
57
optional bytes pubkey = 2;
68
optional string exchanges = 3;
@@ -11,4 +13,4 @@ module.exports = `message Propose {
1113
message Exchange {
1214
optional bytes epubkey = 1;
1315
optional bytes signature = 2;
14-
}`
16+
}`)

test/fixtures/peer.js

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/secio.spec.js

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/* eslint-env mocha */
2+
'use strict'
3+
4+
const chai = require('chai')
5+
const dirtyChai = require('dirty-chai')
6+
const expect = chai.expect
7+
chai.use(dirtyChai)
8+
9+
const duplexPair = require('it-pair/duplex')
10+
const Handshake = require('it-pb-rpc')
11+
const Secio = require('../src')
12+
const { createPeerIdsFromFixtures } = require('./fixtures/peer')
13+
const {
14+
createExchange,
15+
createProposal,
16+
generateKeys,
17+
identify,
18+
selectProtocols,
19+
verify
20+
} = require('../src/handshake/crypto')
21+
const { createBoxStream, createUnboxStream } = require('../src/etm')
22+
const State = require('../src/state')
23+
const { Propose } = require('../src/handshake/secio.proto')
24+
25+
describe('secio', () => {
26+
let remotePeer
27+
let localPeer
28+
29+
before(async () => {
30+
[remotePeer, localPeer] = await createPeerIdsFromFixtures(2)
31+
})
32+
33+
it('performs a spec compliant inbound exchange', async () => {
34+
const [inboundConnection, outboundConnection] = duplexPair()
35+
await Promise.all([
36+
Secio.secureInbound(remotePeer, inboundConnection, null),
37+
(async () => {
38+
const wrap = Handshake(outboundConnection)
39+
const state = new State(localPeer, remotePeer)
40+
41+
// Create our proposal
42+
const proposal = createProposal(state)
43+
44+
// Send our proposal
45+
const proposalLength = Buffer.allocUnsafe(4)
46+
proposalLength.writeInt32BE(proposal.length, 0)
47+
wrap.write(Buffer.concat([proposalLength, proposal]))
48+
49+
// Read their proposal
50+
let theirProposalRaw = (await wrap.read()).slice()
51+
let dataLength = theirProposalRaw.readInt32BE(0)
52+
theirProposalRaw = theirProposalRaw.slice(4, dataLength + 4)
53+
const theirProposal = Propose.decode(theirProposalRaw)
54+
expect(theirProposal.rand).to.have.length(16)
55+
expect(theirProposal.pubkey).to.eql(remotePeer.pubKey.bytes)
56+
expect(theirProposal.exchanges).to.equal('P-256,P-384,P-521')
57+
expect(theirProposal.ciphers).to.equal('AES-256,AES-128')
58+
expect(theirProposal.hashes).to.equal('SHA256,SHA512')
59+
60+
// Select protocols
61+
identify(state, theirProposalRaw)
62+
await selectProtocols(state)
63+
expect(state.protocols.local).to.include({ curveT: 'P-256', cipherT: 'AES-256', hashT: 'SHA256' })
64+
expect(state.protocols.remote).to.include({ curveT: 'P-256', cipherT: 'AES-256', hashT: 'SHA256' })
65+
66+
// Create our exchange
67+
const exchange = await createExchange(state)
68+
69+
// Send our exchange
70+
const exchangeLength = Buffer.allocUnsafe(4)
71+
exchangeLength.writeInt32BE(exchange.length, 0)
72+
wrap.write(Buffer.concat([exchangeLength, exchange]))
73+
74+
// Read their exchange
75+
let theirExchangeRaw = (await wrap.read()).slice()
76+
dataLength = theirExchangeRaw.readInt32BE(0)
77+
theirExchangeRaw = theirExchangeRaw.slice(4, dataLength + 4)
78+
await verify(state, theirExchangeRaw)
79+
80+
// Generate the crypto keys
81+
await generateKeys(state)
82+
83+
// Create the crypto stream
84+
const box = createBoxStream(state.protocols.local.cipher, state.protocols.local.mac)
85+
const unbox = createUnboxStream(state.protocols.remote.cipher, state.protocols.remote.mac)
86+
87+
// Send back their nonce over the crypto stream
88+
const { value: nonce } = await box([state.proposal.in.rand]).next()
89+
const nonceLength = Buffer.allocUnsafe(4)
90+
nonceLength.writeInt32BE(nonce.length, 0)
91+
wrap.write(Buffer.concat([nonceLength, nonce.slice()]))
92+
93+
// Read our nonce from the crypto stream
94+
let ourNonceRaw = (await wrap.read())
95+
dataLength = ourNonceRaw.readInt32BE(0)
96+
ourNonceRaw = ourNonceRaw.shallowSlice(4, dataLength + 4) // Unbox expects a BufferList, so shallow slice here
97+
const { value: ourNonce } = await unbox([ourNonceRaw]).next()
98+
99+
// Verify our nonce is correct
100+
expect(ourNonce.slice()).to.eql(state.proposal.out.rand)
101+
})()
102+
])
103+
})
104+
105+
it('performs a spec compliant outbound exchange', async () => {
106+
const [inboundConnection, outboundConnection] = duplexPair()
107+
await Promise.all([
108+
Secio.secureOutbound(localPeer, outboundConnection, remotePeer),
109+
(async () => {
110+
const wrap = Handshake(inboundConnection)
111+
const state = new State(remotePeer, localPeer)
112+
113+
// Create our proposal
114+
const proposal = createProposal(state)
115+
116+
// Send our proposal
117+
const proposalLength = Buffer.allocUnsafe(4)
118+
proposalLength.writeInt32BE(proposal.length, 0)
119+
wrap.write(Buffer.concat([proposalLength, proposal]))
120+
121+
// Read their proposal
122+
let theirProposalRaw = (await wrap.read()).slice()
123+
let dataLength = theirProposalRaw.readInt32BE(0)
124+
theirProposalRaw = theirProposalRaw.slice(4, dataLength + 4)
125+
const theirProposal = Propose.decode(theirProposalRaw)
126+
expect(theirProposal.rand).to.have.length(16)
127+
expect(theirProposal.pubkey).to.eql(localPeer.pubKey.bytes)
128+
expect(theirProposal.exchanges).to.equal('P-256,P-384,P-521')
129+
expect(theirProposal.ciphers).to.equal('AES-256,AES-128')
130+
expect(theirProposal.hashes).to.equal('SHA256,SHA512')
131+
132+
// Select protocols
133+
identify(state, theirProposalRaw)
134+
await selectProtocols(state)
135+
expect(state.protocols.local).to.include({ curveT: 'P-256', cipherT: 'AES-256', hashT: 'SHA256' })
136+
expect(state.protocols.remote).to.include({ curveT: 'P-256', cipherT: 'AES-256', hashT: 'SHA256' })
137+
138+
// Create our exchange
139+
const exchange = await createExchange(state)
140+
141+
// Send our exchange
142+
const exchangeLength = Buffer.allocUnsafe(4)
143+
exchangeLength.writeInt32BE(exchange.length, 0)
144+
wrap.write(Buffer.concat([exchangeLength, exchange]))
145+
146+
// Read their exchange
147+
let theirExchangeRaw = (await wrap.read()).slice()
148+
dataLength = theirExchangeRaw.readInt32BE(0)
149+
theirExchangeRaw = theirExchangeRaw.slice(4, dataLength + 4)
150+
await verify(state, theirExchangeRaw)
151+
152+
// Generate the crypto keys
153+
await generateKeys(state)
154+
155+
// Create the crypto stream
156+
const box = createBoxStream(state.protocols.local.cipher, state.protocols.local.mac)
157+
const unbox = createUnboxStream(state.protocols.remote.cipher, state.protocols.remote.mac)
158+
159+
// Send back their nonce over the crypto stream
160+
const { value: nonce } = await box([state.proposal.in.rand]).next()
161+
const nonceLength = Buffer.allocUnsafe(4)
162+
nonceLength.writeInt32BE(nonce.length, 0)
163+
wrap.write(Buffer.concat([nonceLength, nonce.slice()]))
164+
165+
// Read our nonce from the crypto stream
166+
let ourNonceRaw = (await wrap.read())
167+
dataLength = ourNonceRaw.readInt32BE(0)
168+
ourNonceRaw = ourNonceRaw.shallowSlice(4, dataLength + 4) // Unbox expects a BufferList, so shallow slice here
169+
const { value: ourNonce } = await unbox([ourNonceRaw]).next()
170+
171+
// Verify our nonce is correct
172+
expect(ourNonce.slice()).to.eql(state.proposal.out.rand)
173+
})()
174+
])
175+
})
176+
})

0 commit comments

Comments
 (0)