mirror of
https://github.com/fluencelabs/js-libp2p-secio
synced 2025-03-14 17:30:52 +00:00
refactor: use async await (#108)
* chore: upgrade deps * feat: first iteration of the idea * feat: iterate a layer deeper * feat: rewrite handshake * feat: rewrite propose * feat: rewrite finish * feat: rewrite exchange * feat: rewrite low-level stuff * feat: work on rewriting tests * refactor: browser tests * refactor: .aegir.js * feat: refactor benchmarks * fix: try to make it work * fix: lint * refactor: move tests * refactor: switch deps * refactor: entry file * refactor: a bit more * fix: tests * feat: inital iterables refactor * refactor: streaming * refactor: cleanup * fix: turn bufferlist into buffer * fix: use errors from interfaces * refactor: etm * fix: typo * fix: .read error * fix: satisfy output expectations * fix: it works - WARNING: using varint instead of fixed lp, tests lie * fix: use errors * refactor: benchmarks * fix: add suggestions from review Co-Authored-By: Jacob Heun <jacobheun@gmail.com> * fix: upgrade deps and use correct lp-encoder * refactor: apply changes from review * refactor: apply changes from review * refactor: apply changes from review * chore: remove old tests test: add support tests back * test: fix async benchmarks * chore: clean up deps * fix: use fixed encoding/decoding everywhere fix: exchange final nonce handshake over encryption * test: add verify inbound and outbound secio * test: verify nonces are boxed * chore: add node 12 to ci
This commit is contained in:
parent
774267f08e
commit
8ad4c15562
40
.aegir.js
40
.aegir.js
@ -1,40 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const multiaddr = require('multiaddr')
|
||||
const pull = require('pull-stream')
|
||||
const WS = require('libp2p-websockets')
|
||||
const PeerId = require('peer-id')
|
||||
|
||||
const secio = require('./src')
|
||||
|
||||
const peerNodeJSON = require('./test/fixtures/peer-node.json')
|
||||
const ma = multiaddr('/ip4/127.0.0.1/tcp/9090/ws')
|
||||
let listener
|
||||
|
||||
module.exports = {
|
||||
hooks: {
|
||||
browser: {
|
||||
pre: (done) => {
|
||||
PeerId.createFromJSON(peerNodeJSON, (err, peerId) => {
|
||||
if (err) { throw err }
|
||||
|
||||
const ws = new WS()
|
||||
|
||||
listener = ws.createListener((conn) => {
|
||||
const encryptedConn = secio.encrypt(peerId, conn, undefined, (err) => {
|
||||
if (err) { throw err }
|
||||
})
|
||||
|
||||
// echo
|
||||
pull(encryptedConn, encryptedConn)
|
||||
})
|
||||
|
||||
listener.listen(ma, done)
|
||||
})
|
||||
},
|
||||
post: (done) => {
|
||||
listener.close(done)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ stages:
|
||||
|
||||
node_js:
|
||||
- '10'
|
||||
- '12'
|
||||
|
||||
os:
|
||||
- linux
|
||||
|
39
README.md
39
README.md
@ -42,44 +42,9 @@ const secio = require('libp2p-secio')
|
||||
|
||||
## API
|
||||
|
||||
### `.tag`
|
||||
This module exposes a crypto interface, as defined in the [js-interfaces](https://github.com/libp2p/js-interfaces)
|
||||
|
||||
The current `secio` tag, usable in `multistream`.
|
||||
|
||||
### `const encryptedConnection = secio.encrypt(localPeerId, plainTextConnection [, remotePeerId] [, callback])`
|
||||
|
||||
- `localPeerId: PeerId` - A PeerId object containing the Private, Public and Id of our node.
|
||||
- `plainTextConnection: Connection` - The insecure connection to be secured.
|
||||
- `remotePeerId: PeerId` - A PeerId object containing the Public and/or Id of the node we are doing the SECIO handshake with.
|
||||
- `callback: Function` - Optional, Called if an error happens during the initialization.
|
||||
|
||||
Returns an encrypted [Connection object](https://github.com/libp2p/interface-connection) that is the upgraded `plainTextConnection` with now having every byte encrypted.
|
||||
|
||||
Both plainTextConnection and encryptedConnection are at their base, PullStreams.
|
||||
|
||||
### This module uses `pull-streams`
|
||||
|
||||
We expose a streaming interface based on `pull-streams`, rather then on the Node.js core streams implementation (aka Node.js streams). `pull-streams` offers us a better mechanism for error handling and flow control guarantees. If you would like to know more about why we did this, see the discussion at this [issue](https://github.com/ipfs/js-ipfs/issues/362).
|
||||
|
||||
You can learn more about pull-streams at:
|
||||
|
||||
- [The history of Node.js streams, nodebp April 2014](https://www.youtube.com/watch?v=g5ewQEuXjsQ)
|
||||
- [The history of streams, 2016](http://dominictarr.com/post/145135293917/history-of-streams)
|
||||
- [pull-streams, the simple streaming primitive](http://dominictarr.com/post/149248845122/pull-streams-pull-streams-are-a-very-simple)
|
||||
- [pull-streams documentation](https://pull-stream.github.io/)
|
||||
|
||||
#### Converting `pull-streams` to Node.js Streams
|
||||
|
||||
If you are a Node.js streams user, you can convert a pull-stream to a Node.js stream using the module [`pull-stream-to-stream`](https://github.com/pull-stream/pull-stream-to-stream), giving you an instance of a Node.js stream that is linked to the pull-stream. For example:
|
||||
|
||||
```js
|
||||
const pullToStream = require('pull-stream-to-stream')
|
||||
|
||||
const nodeStreamInstance = pullToStream(pullStreamInstance)
|
||||
// nodeStreamInstance is an instance of a Node.js Stream
|
||||
```
|
||||
|
||||
To learn more about this utility, visit https://pull-stream.github.io/#pull-stream-to-stream.
|
||||
[ » API Docs ](https://github.com/libp2p/js-interfaces/tree/master/src/crypto#api)
|
||||
|
||||
## Contribute
|
||||
|
||||
|
@ -1,73 +1,70 @@
|
||||
'use strict'
|
||||
|
||||
/* eslint-disable no-console */
|
||||
|
||||
const Benchmark = require('benchmark')
|
||||
const pull = require('pull-stream/pull')
|
||||
const infinite = require('pull-stream/sources/infinite')
|
||||
const take = require('pull-stream/throughs/take')
|
||||
const drain = require('pull-stream/sinks/drain')
|
||||
const Connection = require('interface-connection').Connection
|
||||
const parallel = require('async/parallel')
|
||||
const pair = require('pull-pair/duplex')
|
||||
const PeerId = require('peer-id')
|
||||
|
||||
const secio = require('../src')
|
||||
const pipe = require('it-pipe')
|
||||
const { reduce } = require('streaming-iterables')
|
||||
const DuplexPair = require('it-pair/duplex')
|
||||
|
||||
const secio = require('..')
|
||||
|
||||
const suite = new Benchmark.Suite('secio')
|
||||
let peers
|
||||
|
||||
function sendData (a, b, opts, finish) {
|
||||
async function sendData (a, b, opts) {
|
||||
opts = Object.assign({ times: 1, size: 100 }, opts)
|
||||
|
||||
pull(
|
||||
infinite(() => Buffer.allocUnsafe(opts.size)),
|
||||
take(opts.times),
|
||||
let i = opts.times
|
||||
|
||||
pipe(
|
||||
function * () {
|
||||
while (i--) {
|
||||
yield Buffer.allocUnsafe(opts.size)
|
||||
}
|
||||
},
|
||||
a
|
||||
)
|
||||
|
||||
let length = 0
|
||||
|
||||
pull(
|
||||
const res = await pipe(
|
||||
b,
|
||||
drain((data) => {
|
||||
length += data.length
|
||||
}, () => {
|
||||
if (length !== opts.times * opts.size) {
|
||||
throw new Error('Did not receive enough chunks')
|
||||
}
|
||||
finish.resolve()
|
||||
})
|
||||
reduce((acc, val) => acc + val.length, 0)
|
||||
)
|
||||
}
|
||||
|
||||
function ifErr (err) {
|
||||
if (err) {
|
||||
throw err
|
||||
if (res !== opts.times * opts.size) {
|
||||
throw new Error('Did not receive enough chunks')
|
||||
}
|
||||
}
|
||||
|
||||
suite.add('create peers for test', (deferred) => {
|
||||
parallel([
|
||||
(cb) => PeerId.createFromJSON(require('./peer-a'), cb),
|
||||
(cb) => PeerId.createFromJSON(require('./peer-b'), cb)
|
||||
], (err, _peers) => {
|
||||
if (err) { throw err }
|
||||
peers = _peers
|
||||
|
||||
suite.add('create peers for test', {
|
||||
defer: true,
|
||||
fn: async (deferred) => {
|
||||
peers = await Promise.all([
|
||||
PeerId.createFromJSON(require('./peer-a')),
|
||||
PeerId.createFromJSON(require('./peer-b'))
|
||||
])
|
||||
deferred.resolve()
|
||||
})
|
||||
}, { defer: true })
|
||||
}
|
||||
})
|
||||
suite.add('establish an encrypted channel', {
|
||||
defer: true,
|
||||
fn: async (deferred) => {
|
||||
const p = DuplexPair()
|
||||
|
||||
suite.add('establish an encrypted channel', (deferred) => {
|
||||
const p = pair()
|
||||
const peerA = peers[0]
|
||||
const peerB = peers[1]
|
||||
|
||||
const peerA = peers[0]
|
||||
const peerB = peers[1]
|
||||
const [aToB, bToA] = await Promise.all([
|
||||
secio.secureInbound(peerA, p[0], peerB),
|
||||
secio.secureOutbound(peerB, p[1], peerA)
|
||||
])
|
||||
|
||||
const aToB = secio.encrypt(peerA, new Connection(p[0]), peerB, ifErr)
|
||||
const bToA = secio.encrypt(peerB, new Connection(p[1]), peerA, ifErr)
|
||||
|
||||
sendData(aToB, bToA, {}, deferred)
|
||||
}, { defer: true })
|
||||
await sendData(aToB.conn, bToA.conn, {})
|
||||
deferred.resolve()
|
||||
}
|
||||
})
|
||||
|
||||
const cases = [
|
||||
[10, 262144],
|
||||
@ -81,23 +78,32 @@ cases.forEach((el) => {
|
||||
const times = el[0]
|
||||
const size = el[1]
|
||||
|
||||
suite.add(`send plaintext ${times} x ${size} bytes`, (deferred) => {
|
||||
const p = pair()
|
||||
suite.add(`send plaintext ${times} x ${size} bytes`, {
|
||||
defer: true,
|
||||
fn: async (deferred) => {
|
||||
const p = DuplexPair()
|
||||
await sendData(p[0], p[1], { times: times, size: size })
|
||||
deferred.resolve()
|
||||
}
|
||||
})
|
||||
|
||||
sendData(p[0], p[1], { times: times, size: size }, deferred)
|
||||
}, { defer: true })
|
||||
suite.add(`send encrypted ${times} x ${size} bytes`, {
|
||||
defer: true,
|
||||
fn: async (deferred) => {
|
||||
const p = DuplexPair()
|
||||
|
||||
suite.add(`send encrypted ${times} x ${size} bytes`, (deferred) => {
|
||||
const p = pair()
|
||||
const peerA = peers[0]
|
||||
const peerB = peers[1]
|
||||
|
||||
const peerA = peers[0]
|
||||
const peerB = peers[1]
|
||||
const [aToB, bToA] = await Promise.all([
|
||||
secio.secureInbound(peerA, p[0], peerB),
|
||||
secio.secureOutbound(peerB, p[1], peerA)
|
||||
])
|
||||
|
||||
const aToB = secio.encrypt(peerA, new Connection(p[0]), peerB, ifErr)
|
||||
const bToA = secio.encrypt(peerB, new Connection(p[1]), peerA, ifErr)
|
||||
|
||||
sendData(aToB, bToA, { times: times, size: size }, deferred)
|
||||
}, { defer: true })
|
||||
await sendData(aToB.conn, bToA.conn, { times: times, size: size })
|
||||
deferred.resolve()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
suite.on('cycle', (event) => {
|
||||
|
33
package.json
33
package.json
@ -25,31 +25,26 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"async": "^2.6.2",
|
||||
"bl": "^4.0.0",
|
||||
"debug": "^4.1.1",
|
||||
"interface-connection": "~0.3.3",
|
||||
"libp2p-crypto": "~0.16.1",
|
||||
"multiaddr": "^6.0.6",
|
||||
"multihashing-async": "~0.6.0",
|
||||
"once": "^1.4.0",
|
||||
"peer-id": "~0.12.2",
|
||||
"peer-info": "~0.15.1",
|
||||
"protons": "^1.0.1",
|
||||
"pull-defer": "~0.2.3",
|
||||
"pull-handshake": "^1.1.4",
|
||||
"pull-length-prefixed": "^1.3.2",
|
||||
"pull-stream": "^3.6.9",
|
||||
"safe-buffer": "^5.1.2"
|
||||
"it-buffer": "^0.1.1",
|
||||
"it-length-prefixed": "^3.0.0",
|
||||
"it-pair": "^1.0.0",
|
||||
"it-pb-rpc": "^0.1.4",
|
||||
"it-pipe": "^1.1.0",
|
||||
"libp2p-crypto": "~0.17.1",
|
||||
"libp2p-interfaces": "~0.1.3",
|
||||
"multiaddr": "^7.2.1",
|
||||
"multihashing-async": "~0.8.0",
|
||||
"peer-id": "~0.13.5",
|
||||
"protons": "^1.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"aegir": "^18.2.2",
|
||||
"aegir": "^20.4.1",
|
||||
"benchmark": "^2.1.4",
|
||||
"chai": "^4.2.0",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"libp2p-websockets": "~0.12.2",
|
||||
"multistream-select": "~0.14.4",
|
||||
"pull-goodbye": "~0.0.2",
|
||||
"pull-pair": "^1.1.0"
|
||||
"streaming-iterables": "^4.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0",
|
||||
|
78
src/etm.js
78
src/etm.js
@ -1,81 +1,41 @@
|
||||
'use strict'
|
||||
|
||||
const pull = require('pull-stream/pull')
|
||||
const map = require('pull-stream/throughs/map')
|
||||
const asyncMap = require('pull-stream/throughs/async-map')
|
||||
const lp = require('pull-length-prefixed')
|
||||
|
||||
const lpOpts = {
|
||||
fixed: true,
|
||||
bytes: 4
|
||||
}
|
||||
const BufferList = require('bl/BufferList')
|
||||
const { InvalidCryptoTransmissionError } = require('libp2p-interfaces/src/crypto/errors')
|
||||
|
||||
exports.createBoxStream = (cipher, mac) => {
|
||||
return pull(
|
||||
ensureBuffer(),
|
||||
asyncMap((chunk, cb) => {
|
||||
cipher.encrypt(chunk, (err, data) => {
|
||||
if (err) {
|
||||
return cb(err)
|
||||
}
|
||||
|
||||
mac.digest(data, (err, digest) => {
|
||||
if (err) {
|
||||
return cb(err)
|
||||
}
|
||||
|
||||
cb(null, Buffer.concat([data, digest]))
|
||||
})
|
||||
})
|
||||
}),
|
||||
lp.encode(lpOpts)
|
||||
)
|
||||
return async function * (source) {
|
||||
for await (const chunk of source) {
|
||||
const data = await cipher.encrypt(chunk)
|
||||
const digest = await mac.digest(data)
|
||||
yield new BufferList([data, digest])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exports.createUnboxStream = (decipher, mac) => {
|
||||
return pull(
|
||||
ensureBuffer(),
|
||||
lp.decode(lpOpts),
|
||||
asyncMap((chunk, cb) => {
|
||||
return async function * (source) {
|
||||
for await (const chunk of source) {
|
||||
const l = chunk.length
|
||||
const macSize = mac.length
|
||||
|
||||
if (l < macSize) {
|
||||
return cb(new Error(`buffer (${l}) shorter than MAC size (${macSize})`))
|
||||
throw new InvalidCryptoTransmissionError(`buffer (${l}) shorter than MAC size (${macSize})`)
|
||||
}
|
||||
|
||||
const mark = l - macSize
|
||||
const data = chunk.slice(0, mark)
|
||||
const macd = chunk.slice(mark)
|
||||
|
||||
mac.digest(data, (err, expected) => {
|
||||
if (err) {
|
||||
return cb(err)
|
||||
}
|
||||
const expected = await mac.digest(data)
|
||||
|
||||
if (!macd.equals(expected)) {
|
||||
return cb(new Error(`MAC Invalid: ${macd.toString('hex')} != ${expected.toString('hex')}`))
|
||||
}
|
||||
if (!macd.equals(expected)) {
|
||||
throw new InvalidCryptoTransmissionError(`MAC Invalid: ${macd.toString('hex')} != ${expected.toString('hex')}`)
|
||||
}
|
||||
|
||||
// all good, decrypt
|
||||
decipher.decrypt(data, (err, decrypted) => {
|
||||
if (err) {
|
||||
return cb(err)
|
||||
}
|
||||
const decrypted = await decipher.decrypt(data)
|
||||
|
||||
cb(null, decrypted)
|
||||
})
|
||||
})
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
function ensureBuffer () {
|
||||
return map((c) => {
|
||||
if (typeof c === 'string') {
|
||||
return Buffer.from(c, 'utf-8')
|
||||
yield decrypted
|
||||
}
|
||||
|
||||
return c
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,17 @@
|
||||
'use strict'
|
||||
|
||||
const protons = require('protons')
|
||||
const PeerId = require('peer-id')
|
||||
const crypto = require('libp2p-crypto')
|
||||
const parallel = require('async/parallel')
|
||||
const waterfall = require('async/waterfall')
|
||||
const debug = require('debug')
|
||||
const log = debug('libp2p:secio')
|
||||
log.error = debug('libp2p:secio:error')
|
||||
|
||||
const pbm = protons(require('./secio.proto'))
|
||||
const pbm = require('./secio.proto')
|
||||
|
||||
const support = require('../support')
|
||||
|
||||
const { UnexpectedPeerError } = require('libp2p-interfaces/src/crypto/errors')
|
||||
|
||||
// nonceSize is the size of our nonces (in bytes)
|
||||
const nonceSize = 16
|
||||
|
||||
@ -29,38 +28,30 @@ exports.createProposal = (state) => {
|
||||
return state.proposalEncoded.out
|
||||
}
|
||||
|
||||
exports.createExchange = (state, callback) => {
|
||||
crypto.keys.generateEphemeralKeyPair(state.protocols.local.curveT, (err, res) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
exports.createExchange = async (state) => {
|
||||
const res = await crypto.keys.generateEphemeralKeyPair(state.protocols.local.curveT)
|
||||
|
||||
state.ephemeralKey.local = res.key
|
||||
state.shared.generate = res.genSharedKey
|
||||
state.ephemeralKey.local = res.key
|
||||
state.shared.generate = res.genSharedKey
|
||||
|
||||
// Gather corpus to sign.
|
||||
const selectionOut = Buffer.concat([
|
||||
state.proposalEncoded.out,
|
||||
state.proposalEncoded.in,
|
||||
state.ephemeralKey.local
|
||||
])
|
||||
// Gather corpus to sign.
|
||||
const selectionOut = Buffer.concat([
|
||||
state.proposalEncoded.out,
|
||||
state.proposalEncoded.in,
|
||||
state.ephemeralKey.local
|
||||
])
|
||||
|
||||
state.key.local.sign(selectionOut, (err, sig) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
const sig = await state.key.local.sign(selectionOut)
|
||||
|
||||
state.exchange.out = {
|
||||
epubkey: state.ephemeralKey.local,
|
||||
signature: sig
|
||||
}
|
||||
state.exchange.out = {
|
||||
epubkey: state.ephemeralKey.local,
|
||||
signature: sig
|
||||
}
|
||||
|
||||
callback(null, pbm.Exchange.encode(state.exchange.out))
|
||||
})
|
||||
})
|
||||
return pbm.Exchange.encode(state.exchange.out)
|
||||
}
|
||||
|
||||
exports.identify = (state, msg, callback) => {
|
||||
exports.identify = async (state, msg) => {
|
||||
log('1.1 identify')
|
||||
|
||||
state.proposalEncoded.in = msg
|
||||
@ -69,26 +60,21 @@ exports.identify = (state, msg, callback) => {
|
||||
|
||||
state.key.remote = crypto.keys.unmarshalPublicKey(pubkey)
|
||||
|
||||
PeerId.createFromPubKey(pubkey.toString('base64'), (err, remoteId) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
const remoteId = await PeerId.createFromPubKey(pubkey.toString('base64'))
|
||||
|
||||
// If we know who we are dialing to, double check
|
||||
if (state.id.remote) {
|
||||
if (state.id.remote.toB58String() !== remoteId.toB58String()) {
|
||||
return callback(new Error('dialed to the wrong peer, Ids do not match'))
|
||||
}
|
||||
} else {
|
||||
state.id.remote = remoteId
|
||||
// If we know who we are dialing to, double check
|
||||
if (state.id.remote) {
|
||||
if (state.id.remote.toString() !== remoteId.toString()) {
|
||||
throw new UnexpectedPeerError('Dialed to the wrong peer: IDs do not match!')
|
||||
}
|
||||
} else {
|
||||
state.id.remote = remoteId
|
||||
}
|
||||
|
||||
log('1.1 identify - %s - identified remote peer as %s', state.id.local.toB58String(), state.id.remote.toB58String())
|
||||
callback()
|
||||
})
|
||||
log('1.1 identify - %s - identified remote peer as %s', state.id.local.toB58String(), state.id.remote.toB58String())
|
||||
}
|
||||
|
||||
exports.selectProtocols = (state, callback) => {
|
||||
exports.selectProtocols = async (state) => {
|
||||
log('1.2 selection')
|
||||
|
||||
const local = {
|
||||
@ -107,30 +93,26 @@ exports.selectProtocols = (state, callback) => {
|
||||
nonce: state.proposal.in.rand
|
||||
}
|
||||
|
||||
support.selectBest(local, remote, (err, selected) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
// we use the same params for both directions (must choose same curve)
|
||||
// WARNING: if they dont SelectBest the same way, this won't work...
|
||||
state.protocols.remote = {
|
||||
order: selected.order,
|
||||
curveT: selected.curveT,
|
||||
cipherT: selected.cipherT,
|
||||
hashT: selected.hashT
|
||||
}
|
||||
const selected = await support.selectBest(local, remote)
|
||||
|
||||
state.protocols.local = {
|
||||
order: selected.order,
|
||||
curveT: selected.curveT,
|
||||
cipherT: selected.cipherT,
|
||||
hashT: selected.hashT
|
||||
}
|
||||
callback()
|
||||
})
|
||||
// we use the same params for both directions (must choose same curve)
|
||||
// WARNING: if they dont SelectBest the same way, this won't work...
|
||||
state.protocols.remote = {
|
||||
order: selected.order,
|
||||
curveT: selected.curveT,
|
||||
cipherT: selected.cipherT,
|
||||
hashT: selected.hashT
|
||||
}
|
||||
|
||||
state.protocols.local = {
|
||||
order: selected.order,
|
||||
curveT: selected.curveT,
|
||||
cipherT: selected.cipherT,
|
||||
hashT: selected.hashT
|
||||
}
|
||||
}
|
||||
|
||||
exports.verify = (state, msg, callback) => {
|
||||
exports.verify = async (state, msg) => {
|
||||
log('2.1. verify')
|
||||
|
||||
state.exchange.in = pbm.Exchange.decode(msg)
|
||||
@ -142,57 +124,43 @@ exports.verify = (state, msg, callback) => {
|
||||
state.ephemeralKey.remote
|
||||
])
|
||||
|
||||
state.key.remote.verify(selectionIn, state.exchange.in.signature, (err, sigOk) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
const sigOk = await state.key.remote.verify(selectionIn, state.exchange.in.signature)
|
||||
|
||||
if (!sigOk) {
|
||||
return callback(new Error('Bad signature'))
|
||||
}
|
||||
if (!sigOk) {
|
||||
throw new Error('Bad signature')
|
||||
}
|
||||
|
||||
log('2.1. verify - signature verified')
|
||||
callback()
|
||||
})
|
||||
log('2.1. verify - signature verified')
|
||||
}
|
||||
|
||||
exports.generateKeys = (state, callback) => {
|
||||
exports.generateKeys = async (state) => {
|
||||
log('2.2. keys')
|
||||
|
||||
waterfall([
|
||||
(cb) => state.shared.generate(state.exchange.in.epubkey, cb),
|
||||
(secret, cb) => {
|
||||
state.shared.secret = secret
|
||||
const secret = await state.shared.generate(state.exchange.in.epubkey)
|
||||
|
||||
crypto.keys.keyStretcher(
|
||||
state.protocols.local.cipherT,
|
||||
state.protocols.local.hashT,
|
||||
state.shared.secret,
|
||||
cb
|
||||
)
|
||||
},
|
||||
(keys, cb) => {
|
||||
// use random nonces to decide order.
|
||||
if (state.protocols.local.order > 0) {
|
||||
state.protocols.local.keys = keys.k1
|
||||
state.protocols.remote.keys = keys.k2
|
||||
} else if (state.protocols.local.order < 0) {
|
||||
// swap
|
||||
state.protocols.local.keys = keys.k2
|
||||
state.protocols.remote.keys = keys.k1
|
||||
} else {
|
||||
// we should've bailed before state. but if not, bail here.
|
||||
return cb(new Error('you are trying to talk to yourself'))
|
||||
}
|
||||
state.shared.secret = secret
|
||||
|
||||
log('2.3. mac + cipher')
|
||||
const keys = await crypto.keys.keyStretcher(
|
||||
state.protocols.local.cipherT,
|
||||
state.protocols.local.hashT,
|
||||
state.shared.secret)
|
||||
|
||||
parallel([
|
||||
(_cb) => support.makeMacAndCipher(state.protocols.local, _cb),
|
||||
(_cb) => support.makeMacAndCipher(state.protocols.remote, _cb)
|
||||
], cb)
|
||||
}
|
||||
], callback)
|
||||
// use random nonces to decide order.
|
||||
if (state.protocols.local.order > 0) {
|
||||
state.protocols.local.keys = keys.k1
|
||||
state.protocols.remote.keys = keys.k2
|
||||
} else if (state.protocols.local.order < 0) {
|
||||
// swap
|
||||
state.protocols.local.keys = keys.k2
|
||||
state.protocols.remote.keys = keys.k1
|
||||
} else {
|
||||
// we should've bailed before state. but if not, bail here.
|
||||
throw new Error('you are trying to talk to yourself')
|
||||
}
|
||||
|
||||
log('2.3. mac + cipher')
|
||||
|
||||
await Promise.all([state.protocols.local, state.protocols.remote].map(data => support.makeMacAndCipher(data)))
|
||||
}
|
||||
|
||||
exports.verifyNonce = (state, n2) => {
|
||||
|
@ -1,35 +1,25 @@
|
||||
'use strict'
|
||||
|
||||
const debug = require('debug')
|
||||
const waterfall = require('async/waterfall')
|
||||
|
||||
const support = require('../support')
|
||||
const crypto = require('./crypto')
|
||||
|
||||
const debug = require('debug')
|
||||
const log = debug('libp2p:secio')
|
||||
log.error = debug('libp2p:secio:error')
|
||||
|
||||
// step 2. Exchange
|
||||
// -- exchange (signed) ephemeral keys. verify signatures.
|
||||
module.exports = function exchange (state, callback) {
|
||||
module.exports = async function exchange (state, wrapped) {
|
||||
log('2. exchange - start')
|
||||
|
||||
log('2. exchange - writing exchange')
|
||||
waterfall([
|
||||
(cb) => crypto.createExchange(state, cb),
|
||||
(ex, cb) => {
|
||||
support.write(state, ex)
|
||||
support.read(state.shake, cb)
|
||||
},
|
||||
(msg, cb) => {
|
||||
log('2. exchange - reading exchange')
|
||||
crypto.verify(state, msg, cb)
|
||||
},
|
||||
(cb) => crypto.generateKeys(state, cb)
|
||||
], (err) => {
|
||||
if (err) { return callback(err) }
|
||||
const ex = await crypto.createExchange(state)
|
||||
|
||||
log('2. exchange - finish')
|
||||
callback()
|
||||
})
|
||||
await wrapped.writeLP(ex)
|
||||
const msg = await wrapped.readLP()
|
||||
|
||||
log('2. exchange - reading exchange')
|
||||
await crypto.verify(state, msg.slice())
|
||||
|
||||
await crypto.generateKeys(state)
|
||||
log('2. exchange - finish')
|
||||
}
|
||||
|
@ -1,61 +1,49 @@
|
||||
'use strict'
|
||||
|
||||
const pull = require('pull-stream/pull')
|
||||
const pullError = require('pull-stream/sources/error')
|
||||
const handshake = require('pull-handshake')
|
||||
const debug = require('debug')
|
||||
|
||||
const log = debug('libp2p:secio')
|
||||
log.error = debug('libp2p:secio:error')
|
||||
|
||||
const DuplexPair = require('it-pair/duplex')
|
||||
const pipe = require('it-pipe')
|
||||
const lp = require('it-length-prefixed')
|
||||
const Wrap = require('it-pb-rpc')
|
||||
const { int32BEEncode, int32BEDecode } = lp
|
||||
const ensureBuffer = require('it-buffer')
|
||||
|
||||
const etm = require('../etm')
|
||||
const crypto = require('./crypto')
|
||||
|
||||
// step 3. Finish
|
||||
// -- send expected message to verify encryption works (send local nonce)
|
||||
module.exports = function finish (state, callback) {
|
||||
module.exports = async function finish (state, wrapped) {
|
||||
log('3. finish - start')
|
||||
|
||||
const proto = state.protocols
|
||||
const stream = state.shake.rest()
|
||||
const shake = handshake({ timeout: state.timeout }, (err) => {
|
||||
if (err) {
|
||||
throw err
|
||||
}
|
||||
})
|
||||
|
||||
pull(
|
||||
stream,
|
||||
etm.createUnboxStream(proto.remote.cipher, proto.remote.mac),
|
||||
shake,
|
||||
const [secure, user] = DuplexPair()
|
||||
const network = wrapped.unwrap()
|
||||
|
||||
pipe(
|
||||
secure, // this is FROM the user
|
||||
ensureBuffer,
|
||||
etm.createBoxStream(proto.local.cipher, proto.local.mac),
|
||||
stream
|
||||
lp.encode({ lengthEncoder: int32BEEncode }),
|
||||
network, // and gets piped INTO and FROM the network
|
||||
lp.decode({ lengthDecoder: int32BEDecode }),
|
||||
ensureBuffer,
|
||||
etm.createUnboxStream(proto.remote.cipher, proto.remote.mac),
|
||||
secure // and gets piped TO the user
|
||||
)
|
||||
|
||||
shake.handshake.write(state.proposal.in.rand)
|
||||
shake.handshake.read(state.proposal.in.rand.length, (err, nonceBack) => {
|
||||
const fail = (err) => {
|
||||
log.error(err)
|
||||
state.secure.resolve({
|
||||
source: pullError(err),
|
||||
sink (read) {
|
||||
}
|
||||
})
|
||||
callback(err)
|
||||
}
|
||||
// Exchange nonces over the encrypted stream for final verification
|
||||
const shake = Wrap(user)
|
||||
shake.write(state.proposal.in.rand)
|
||||
const nonceBack = await shake.read(state.proposal.in.rand.length)
|
||||
crypto.verifyNonce(state, nonceBack.slice())
|
||||
|
||||
if (err) return fail(err)
|
||||
log('3. finish - finish')
|
||||
|
||||
try {
|
||||
crypto.verifyNonce(state, nonceBack)
|
||||
} catch (err) {
|
||||
return fail(err)
|
||||
}
|
||||
|
||||
log('3. finish - finish')
|
||||
|
||||
// Awesome that's all folks.
|
||||
state.secure.resolve(shake.handshake.rest())
|
||||
callback()
|
||||
})
|
||||
// Awesome that's all folks.
|
||||
state.secure = shake.unwrap()
|
||||
}
|
||||
|
@ -1,31 +1,15 @@
|
||||
'use strict'
|
||||
|
||||
const series = require('async/series')
|
||||
|
||||
const propose = require('./propose')
|
||||
const exchange = require('./exchange')
|
||||
const finish = require('./finish')
|
||||
|
||||
// Performs initial communication over insecure channel to share keys, IDs,
|
||||
// and initiate communication, assigning all necessary params.
|
||||
module.exports = function handshake (state, callback) {
|
||||
series([
|
||||
(cb) => propose(state, cb),
|
||||
(cb) => exchange(state, cb),
|
||||
(cb) => finish(state, cb)
|
||||
], (err) => {
|
||||
state.cleanSecrets()
|
||||
module.exports = async function handshake (state, wrapped) {
|
||||
await propose(state, wrapped)
|
||||
await exchange(state, wrapped)
|
||||
await finish(state, wrapped)
|
||||
|
||||
if (err) {
|
||||
if (err === true) {
|
||||
err = new Error('Stream ended prematurely')
|
||||
}
|
||||
state.shake.abort(err)
|
||||
}
|
||||
|
||||
// signal when the handshake is finished so that plumbing can happen
|
||||
callback(err)
|
||||
})
|
||||
|
||||
return state.stream
|
||||
state.cleanSecrets()
|
||||
}
|
||||
|
@ -1,35 +1,28 @@
|
||||
'use strict'
|
||||
|
||||
const debug = require('debug')
|
||||
const waterfall = require('async/waterfall')
|
||||
|
||||
const support = require('../support')
|
||||
const crypto = require('./crypto')
|
||||
|
||||
const lp = require('it-length-prefixed')
|
||||
const { int32BEEncode } = lp
|
||||
const debug = require('debug')
|
||||
const log = debug('libp2p:secio')
|
||||
log.error = debug('libp2p:secio:error')
|
||||
|
||||
// step 1. Propose
|
||||
// -- propose cipher suite + send pubkeys + nonce
|
||||
module.exports = function propose (state, callback) {
|
||||
module.exports = async function propose (state, wrapped) {
|
||||
log('1. propose - start')
|
||||
|
||||
log('1. propose - writing proposal')
|
||||
support.write(state, crypto.createProposal(state))
|
||||
const prop = crypto.createProposal(state)
|
||||
log('1. propose - writing proposal', prop)
|
||||
|
||||
waterfall([
|
||||
(cb) => support.read(state.shake, cb),
|
||||
(msg, cb) => {
|
||||
log('1. propose - reading proposal', msg)
|
||||
crypto.identify(state, msg, cb)
|
||||
},
|
||||
(cb) => crypto.selectProtocols(state, cb)
|
||||
], (err) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
await wrapped.write(lp.encode.single(prop, { lengthEncoder: int32BEEncode }))
|
||||
|
||||
log('1. propose - finish')
|
||||
callback()
|
||||
})
|
||||
log('1. propose - reading proposal')
|
||||
const msg = (await wrapped.readLP()).slice()
|
||||
log('1. propose - read proposal', msg)
|
||||
|
||||
await crypto.identify(state, msg)
|
||||
await crypto.selectProtocols(state)
|
||||
|
||||
log('1. propose - finish')
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = `message Propose {
|
||||
const protons = require('protons')
|
||||
|
||||
module.exports = protons(`message Propose {
|
||||
optional bytes rand = 1;
|
||||
optional bytes pubkey = 2;
|
||||
optional string exchanges = 3;
|
||||
@ -11,4 +13,4 @@ module.exports = `message Propose {
|
||||
message Exchange {
|
||||
optional bytes epubkey = 1;
|
||||
optional bytes signature = 2;
|
||||
}`
|
||||
}`)
|
||||
|
63
src/index.js
63
src/index.js
@ -1,58 +1,33 @@
|
||||
'use strict'
|
||||
|
||||
const pull = require('pull-stream/pull')
|
||||
const Connection = require('interface-connection').Connection
|
||||
const assert = require('assert')
|
||||
const PeerInfo = require('peer-info')
|
||||
const debug = require('debug')
|
||||
const once = require('once')
|
||||
const log = debug('libp2p:secio')
|
||||
log.error = debug('libp2p:secio:error')
|
||||
|
||||
const handshake = require('./handshake')
|
||||
const State = require('./state')
|
||||
const Wrap = require('it-pb-rpc')
|
||||
const { int32BEDecode, int32BEEncode } = require('it-length-prefixed')
|
||||
|
||||
module.exports = {
|
||||
tag: '/secio/1.0.0',
|
||||
encrypt (localId, conn, remoteId, callback) {
|
||||
assert(localId, 'no local private key provided')
|
||||
assert(conn, 'no connection for the handshake provided')
|
||||
async function secure (localPeer, duplex, remotePeer) { // returns duplex
|
||||
assert(localPeer, 'no local private key provided')
|
||||
assert(duplex, 'no connection for the handshake provided')
|
||||
|
||||
if (typeof remoteId === 'function') {
|
||||
callback = remoteId
|
||||
remoteId = undefined
|
||||
}
|
||||
const state = new State(localPeer, remotePeer)
|
||||
const wrapped = Wrap(duplex, { lengthDecoder: int32BEDecode, lengthEncoder: int32BEEncode })
|
||||
await handshake(state, wrapped)
|
||||
|
||||
callback = once(callback || function (err) {
|
||||
if (err) { log.error(err) }
|
||||
})
|
||||
|
||||
const timeout = 60 * 1000 * 5
|
||||
|
||||
const state = new State(localId, remoteId, timeout, callback)
|
||||
|
||||
function finish (err) {
|
||||
if (err) { return callback(err) }
|
||||
|
||||
conn.getPeerInfo((err, peerInfo) => {
|
||||
encryptedConnection.setInnerConn(new Connection(state.secure, conn))
|
||||
|
||||
if (err) { // no peerInfo yet, means I'm the receiver
|
||||
encryptedConnection.setPeerInfo(new PeerInfo(state.id.remote))
|
||||
}
|
||||
|
||||
callback()
|
||||
})
|
||||
}
|
||||
|
||||
const encryptedConnection = new Connection(undefined, conn)
|
||||
|
||||
pull(
|
||||
conn,
|
||||
handshake(state, finish),
|
||||
conn
|
||||
)
|
||||
|
||||
return encryptedConnection
|
||||
return {
|
||||
conn: state.secure,
|
||||
remotePeer: state.id.remote
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
protocol: '/secio/1.0.0',
|
||||
|
||||
// since SECIO is symetric, we only need one function here
|
||||
secureInbound: secure,
|
||||
secureOutbound: secure
|
||||
}
|
||||
|
18
src/state.js
18
src/state.js
@ -1,28 +1,12 @@
|
||||
'use strict'
|
||||
|
||||
const handshake = require('pull-handshake')
|
||||
const deferred = require('pull-defer')
|
||||
|
||||
class State {
|
||||
constructor (localId, remoteId, timeout, callback) {
|
||||
if (typeof timeout === 'function') {
|
||||
callback = timeout
|
||||
timeout = undefined
|
||||
}
|
||||
|
||||
constructor (localId, remoteId) {
|
||||
this.setup()
|
||||
|
||||
this.id.local = localId
|
||||
// TODO use remoteId to verify PeersIdentity
|
||||
this.id.remote = remoteId
|
||||
this.key.local = localId.privKey
|
||||
this.timeout = timeout || 60 * 1000
|
||||
callback = callback || (() => {})
|
||||
|
||||
this.secure = deferred.duplex()
|
||||
this.stream = handshake({ timeout: this.timeout }, callback)
|
||||
this.shake = this.stream.handshake
|
||||
delete this.stream.handshake
|
||||
}
|
||||
|
||||
setup () {
|
||||
|
107
src/support.js
107
src/support.js
@ -1,12 +1,9 @@
|
||||
'use strict'
|
||||
|
||||
const mh = require('multihashing-async')
|
||||
const lp = require('pull-length-prefixed')
|
||||
const pull = require('pull-stream/pull')
|
||||
const values = require('pull-stream/sources/values')
|
||||
const collect = require('pull-stream/sinks/collect')
|
||||
const crypto = require('libp2p-crypto')
|
||||
const parallel = require('async/parallel')
|
||||
|
||||
const { InvalidCryptoExchangeError } = require('libp2p-interfaces/src/crypto/errors')
|
||||
|
||||
exports.exchanges = [
|
||||
'P-256',
|
||||
@ -39,97 +36,61 @@ exports.theBest = (order, p1, p2) => {
|
||||
return p1[0]
|
||||
}
|
||||
|
||||
for (let firstCandidate of first) {
|
||||
for (let secondCandidate of second) {
|
||||
for (const firstCandidate of first) {
|
||||
for (const secondCandidate of second) {
|
||||
if (firstCandidate === secondCandidate) {
|
||||
return firstCandidate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('No algorithms in common!')
|
||||
throw new InvalidCryptoExchangeError('No algorithms in common!')
|
||||
}
|
||||
|
||||
exports.makeMacAndCipher = (target, callback) => {
|
||||
parallel([
|
||||
(cb) => makeMac(target.hashT, target.keys.macKey, cb),
|
||||
(cb) => makeCipher(target.cipherT, target.keys.iv, target.keys.cipherKey, cb)
|
||||
], (err, macAndCipher) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
target.mac = macAndCipher[0]
|
||||
target.cipher = macAndCipher[1]
|
||||
callback()
|
||||
})
|
||||
exports.makeMacAndCipher = async (target) => {
|
||||
[target.mac, target.cipher] = await Promise.all([
|
||||
makeMac(target.hashT, target.keys.macKey),
|
||||
makeCipher(target.cipherT, target.keys.iv, target.keys.cipherKey)
|
||||
])
|
||||
}
|
||||
|
||||
function makeMac (hash, key, callback) {
|
||||
crypto.hmac.create(hash, key, callback)
|
||||
function makeMac (hash, key) {
|
||||
return crypto.hmac.create(hash, key)
|
||||
}
|
||||
|
||||
function makeCipher (cipherType, iv, key, callback) {
|
||||
function makeCipher (cipherType, iv, key) {
|
||||
if (cipherType === 'AES-128' || cipherType === 'AES-256') {
|
||||
return crypto.aes.create(key, iv, callback)
|
||||
return crypto.aes.create(key, iv)
|
||||
}
|
||||
|
||||
// TODO: figure out if Blowfish is needed and if so find a library for it.
|
||||
callback(new Error(`unrecognized cipher type: ${cipherType}`))
|
||||
throw new InvalidCryptoExchangeError(`unrecognized cipher type: ${cipherType}`)
|
||||
}
|
||||
|
||||
exports.selectBest = (local, remote, cb) => {
|
||||
exports.digest(Buffer.concat([
|
||||
exports.selectBest = async (local, remote) => {
|
||||
const oh1 = await exports.digest(Buffer.concat([
|
||||
remote.pubKeyBytes,
|
||||
local.nonce
|
||||
]), (err, oh1) => {
|
||||
if (err) {
|
||||
return cb(err)
|
||||
}
|
||||
]))
|
||||
const oh2 = await exports.digest(Buffer.concat([
|
||||
local.pubKeyBytes,
|
||||
remote.nonce
|
||||
]))
|
||||
|
||||
exports.digest(Buffer.concat([
|
||||
local.pubKeyBytes,
|
||||
remote.nonce
|
||||
]), (err, oh2) => {
|
||||
if (err) {
|
||||
return cb(err)
|
||||
}
|
||||
const order = Buffer.compare(oh1, oh2)
|
||||
|
||||
const order = Buffer.compare(oh1, oh2)
|
||||
if (order === 0) {
|
||||
throw new InvalidCryptoExchangeError('you are trying to talk to yourself')
|
||||
}
|
||||
|
||||
if (order === 0) {
|
||||
return cb(new Error('you are trying to talk to yourself'))
|
||||
}
|
||||
|
||||
cb(null, {
|
||||
curveT: exports.theBest(order, local.exchanges, remote.exchanges),
|
||||
cipherT: exports.theBest(order, local.ciphers, remote.ciphers),
|
||||
hashT: exports.theBest(order, local.hashes, remote.hashes),
|
||||
order
|
||||
})
|
||||
})
|
||||
})
|
||||
return {
|
||||
curveT: exports.theBest(order, local.exchanges, remote.exchanges),
|
||||
cipherT: exports.theBest(order, local.ciphers, remote.ciphers),
|
||||
hashT: exports.theBest(order, local.hashes, remote.hashes),
|
||||
order
|
||||
}
|
||||
}
|
||||
|
||||
exports.digest = (buf, cb) => {
|
||||
mh.digest(buf, 'sha2-256', buf.length, cb)
|
||||
}
|
||||
|
||||
exports.write = function write (state, msg, cb) {
|
||||
cb = cb || (() => {})
|
||||
pull(
|
||||
values([msg]),
|
||||
lp.encode({ fixed: true, bytes: 4 }),
|
||||
collect((err, res) => {
|
||||
if (err) {
|
||||
return cb(err)
|
||||
}
|
||||
state.shake.write(res[0])
|
||||
cb()
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
exports.read = function read (reader, cb) {
|
||||
lp.decodeFromReader(reader, { fixed: true, bytes: 4 }, cb)
|
||||
exports.digest = (buf) => {
|
||||
return mh.digest(buf, 'sha2-256', buf.length)
|
||||
}
|
||||
|
@ -1,68 +0,0 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const chai = require('chai')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
const expect = chai.expect
|
||||
chai.use(dirtyChai)
|
||||
const multiaddr = require('multiaddr')
|
||||
const pull = require('pull-stream')
|
||||
const pullGoodbye = require('pull-goodbye')
|
||||
const WS = require('libp2p-websockets')
|
||||
const PeerId = require('peer-id')
|
||||
const parallel = require('async/parallel')
|
||||
|
||||
const peerNodeJSON = require('./fixtures/peer-node.json')
|
||||
const peerBrowserJSON = require('./fixtures/peer-browser.json')
|
||||
|
||||
const secio = require('../src')
|
||||
|
||||
describe('secio between browser <-> nodejs through websockets', () => {
|
||||
const ma = multiaddr('/ip4/127.0.0.1/tcp/9090/ws')
|
||||
let conn
|
||||
let encryptedConn
|
||||
|
||||
before((done) => {
|
||||
parallel([
|
||||
(cb) => PeerId.createFromJSON(peerNodeJSON, cb),
|
||||
(cb) => PeerId.createFromJSON(peerBrowserJSON, cb),
|
||||
|
||||
(cb) => {
|
||||
const ws = new WS()
|
||||
conn = ws.dial(ma, cb)
|
||||
}
|
||||
], (err, res) => {
|
||||
expect(err).to.not.exist()
|
||||
|
||||
const peerIdNode = res[0]
|
||||
const peerIdBrowser = res[1]
|
||||
|
||||
encryptedConn = secio.encrypt(peerIdBrowser, conn, peerIdNode, (err) => {
|
||||
expect(err).to.not.exist()
|
||||
})
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('echo', (done) => {
|
||||
const message = 'Hello World!'
|
||||
|
||||
const s = pullGoodbye({
|
||||
source: pull.values([message]),
|
||||
sink: pull.collect((err, results) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(results).to.eql([message])
|
||||
done()
|
||||
})
|
||||
}, 'GoodBye')
|
||||
|
||||
pull(
|
||||
s,
|
||||
encryptedConn,
|
||||
// Need to convert to a string as goodbye only understands strings
|
||||
|
||||
pull.map((msg) => msg.toString()),
|
||||
s
|
||||
)
|
||||
})
|
||||
})
|
14
test/crypto.spec.js
Normal file
14
test/crypto.spec.js
Normal file
@ -0,0 +1,14 @@
|
||||
'use strict'
|
||||
|
||||
const tests = require('libp2p-interfaces/src/crypto/tests')
|
||||
const SECIO = require('..')
|
||||
|
||||
tests({
|
||||
setup () {
|
||||
// Set up your crypto if needed, then return it
|
||||
return SECIO
|
||||
},
|
||||
teardown () {
|
||||
// Clean up your crypto if needed
|
||||
}
|
||||
})
|
5
test/fixtures/peer-a.json
vendored
5
test/fixtures/peer-a.json
vendored
@ -1,5 +0,0 @@
|
||||
{
|
||||
"id": "QmeS1ou3mrjCFGoFtRx3MwrGDzqKD6xbuYJU1CKtMrtFFu",
|
||||
"privKey": "CAASqAkwggSkAgEAAoIBAQChwzYwCNIyUkzEK3sILqq9ChAKZ9eU+ribY+B/xwAwDKPfvuqHq0hjauJBMcjiQyKAWz9xEBR3WupOM7h9M8oU+/e0xJUTt/CDOrtJ0PCgUXeBLkqsApbBoXW3yomHEDHxYttKzrtoTimiP1bhrxurcpVNC4CUYD+q8gw3sRZlsrqpeYYAfU04kS0BM75W/sUT90znnHvOxFXrEdMMdenEFhZOsDyEK9ENzwhkKgOGb18MBY4kN5DoW4bVd4ItfZnNwdkQtpP/X99tMWJxO4yqpngbywZGnkfirLeuRwt/xRGFVbLOigjBpTVpcbBqe1t2Flhuf/bfWYX4FbyElA5FAgMBAAECggEAJnDTcbrG6LpyD7QdeqZMYLwBb9eZfYfPUu37LaJGwyRd1Q/zf+YOP8HonoGMMWiuzD3i56Vgl7R9NbRIxUgHX9E43jZRDuyJNUZBt5r1c8OoWIR9rj63QLBz3wc8g2Iv3CMX5cEW/ASHFE1lAiCwvJ9wJ2zyU1BEEQWQLbPhlKzw7SLhr4fee45/7pnrKZMllt5vwC9pM6lrpIkICO5gUu0OWu5wfzzlTvfmCgfTb11VqKESEPbDBMUtpJibRqegE4xvipLklJ8VV8jz7NFs9bhgCpNM74Ngt5vGHcddeqtj//86UsClEw5YgWAdRe29ZjMApWvKIkginLjZEO8eiQKBgQDoDWii0rmlgBl1/8fENUSWxYvknGmWO7eWjVqMjDvA+waWUVDpTE+eHT1QAaPofM+nFz5PG+SpB55o4rXdxDesq+DqnaRAI9WtSHdgRtjgETyqoBAiahQ0zGWmSEYHGDB+xGctTMr8GxdhZxqZjjfyptp6oXXqZkmxgcogrx+WTwKBgQCydNDmCDpeH0kSvhAPxaNx5c9WkFEFSA0OCZOx57Y+Mt0MVamRILFrUrcMz095w8BQZkjlHjSHfsRgKa/b2eOd+3BhoMLZVtxRqBdpdqq1KTAcRRG4yA2KA39rttpVzaTV5SPfdDf3tsVlBtV784W63gVpN9gNfajyyrpeffiBKwKBgDnDrLprbl8uZigjhdznza0ie9JqxTXqo6bMhS/bcLx3QIqGr3eD0YXwjWSvI9gpyZ80gAQ9U0xoYxyE4vTTdXB8UL7Wgx6cTQKXuW+z8yTD5bArrBiFA4apItyjvRrjAJ9t0KlMJnNfYxCSE+MJrg+vTU+dhbbVw552SpScQ2atAoGBAKMu3rb6XyUiRpe05MsHVuYX1vi5Dt1dfVKQv1W3JJbLvAZDbsMeuh4BjRFRoMMflQPwBEg+zpn3+WpVtFG9dL5J5gHgF0zWeLDSnFX8BS2TdELlhccKaBcEC8hbdFtxqIFO/vaeN2902hv/m8e0b1zpGNmWDyKG/a7GYpV1a3/xAoGBAJtgGANDVk6qqcWGEVk56FH1ZksvgF3SPXWaXpzbZ5KLCcV5ooRyhowylKUZBBPowMeZ46tem2xwJbraB5kDg6WiSjBsXcbN95ivb8AuoRa6gDqAszjokQUSdpY7FTgMaL046AuihrKsQSly1jrQqbQu8JBgmnnBzus3s77inL/j",
|
||||
"pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQChwzYwCNIyUkzEK3sILqq9ChAKZ9eU+ribY+B/xwAwDKPfvuqHq0hjauJBMcjiQyKAWz9xEBR3WupOM7h9M8oU+/e0xJUTt/CDOrtJ0PCgUXeBLkqsApbBoXW3yomHEDHxYttKzrtoTimiP1bhrxurcpVNC4CUYD+q8gw3sRZlsrqpeYYAfU04kS0BM75W/sUT90znnHvOxFXrEdMMdenEFhZOsDyEK9ENzwhkKgOGb18MBY4kN5DoW4bVd4ItfZnNwdkQtpP/X99tMWJxO4yqpngbywZGnkfirLeuRwt/xRGFVbLOigjBpTVpcbBqe1t2Flhuf/bfWYX4FbyElA5FAgMBAAE="
|
||||
}
|
5
test/fixtures/peer-b.json
vendored
5
test/fixtures/peer-b.json
vendored
@ -1,5 +0,0 @@
|
||||
{
|
||||
"id": "QmYWHGZ9y1Bzx59bBzn85JsJxwmpBy5bpXDWDfwMfsHsxz",
|
||||
"privKey": "CAASqQkwggSlAgEAAoIBAQDLVaPqWFA8WgK6ixuPvhTHeQfBblmEFLEmraLlIDSWbMUPva6aJ1V/hi2I5QLXNeeiig5sco+nF+RKhGnzQ9NpgHRVZ7Ze+LWq3Q4YxONdzFeNUjTvJrDSKgkubA5EKC/LI6pU33WZbjyKkomGo+Gzuqvlj4Rx1dLVXRIOjxUYcIQw3vpLQgwPpiz52eWCeoCpzn06DcsF6aNPjhlp9uJRZCRxZ4yeiwh/A0xxiQtnB4fdZuUPmia1r62+oaxrDl4hUwR7kzHYl0YGfXxAW9GT17KGtjES2yO4kAUgquelNh0hgBKZRvny9imwsObG7ntw5ZG7H62sP7UySIUJqoNRAgMBAAECggEBAKLVU25BCQg7wQGokwra2wMfPoG+IDuw4mkqFlBNKS/prSo86c2TgFmel2qQk2TLS1OUIZbha38RmAXA4qQohe5wKzmV06tcmwdY/YgCbF5aXSbUVYXLQ0Ea3r1pVUdps1SHnElZpnCXoi4Kyc2kAgSPkkdFVnhfFvc9EE/Ob8NgMkdFhlosE5WVNqm4BKQ+mqONddSz4JDbDOApPs/rRpgYm7pJKc3vkrYwniPjyQGYb5EoSbSWuu31RzIcn3Bhte3wKtfMMlpn8MMpPiYo2WJ2eVG6hlUOxhHgS93Y6czCfAgsDtD3C2JpteewuBjg8N0d6WRArKxny83J34q0qy0CgYEA6YSo5UDEq1TF8sbtSVYg6MKSX92NO5MQI/8fTjU4tEwxn/yxpGsnqUu0WGYIc2qVaZuxtcnk2CQxEilxQTbWSIxKuTt7qofEcpSjLLQ4f4chk4DpPsba+S8zSUdWdjthPHZT9IYzobylGBLfbPxyXXiYn1VuqAJfFy8iV9XqmdcCgYEA3ukROQQZCJcgsNTc5uFAKUeQvzv1iae3fGawgJmIJW3Bl8+4dSm1diqG3ZXP1WU31no2aX50PqOZjoIpbl1ggT76cnBDuu3pItR3dNJFQyMEpQOWOjO+NBWF7sRswCvlqbyjofWkzsdd0BioL7vWMjPftiusyyAFA55HRoeStxcCgYEA0tP7rKdSKKFr6inhl+GT6rGod7bOSSgYXXd7qx9v55AXCauaMqiv8TAxTdIo9RMYfHWd91OlMeNTDmOuJcO9qVhIKn5iw266VPyPac/4ZmL5VHQBobTlhC4yLomirTIlMvJeEBmNygtIPrjjUUGGe49itA/szPD/Ky5Z4lV27pcCgYAWU3mqIELxnVFk5K0LYtwuRkC1Jqg9FVNHXnGnL7l3JjsRnXh4I6lNII1JfEvIr86b6LmybzvtWi1zHI5Rw4B68XfcJmpiOpnzJxyf0r+lLci1Tlqpka0nQlCbzYim5r6l9YLeIeBT5Zv7z7xoq4OUm6V4dX9lCNv3tM6mvcVwGQKBgQC9hhjD64/VKXL8wYKZyTAOVO5xYCcqylrpI39qdzl+sS8oqmLUbXnKsGY4If9U61XdULld41BJCRlv6CsKreynm6ZN41j9YRuWWLu8STJcniV9Ef9uVl1M1zo8kfnCHMCym9LkTfJY+Ow/kYhqPukJJL6ve1CVmIuA4rnZlshjbg==",
|
||||
"pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLVaPqWFA8WgK6ixuPvhTHeQfBblmEFLEmraLlIDSWbMUPva6aJ1V/hi2I5QLXNeeiig5sco+nF+RKhGnzQ9NpgHRVZ7Ze+LWq3Q4YxONdzFeNUjTvJrDSKgkubA5EKC/LI6pU33WZbjyKkomGo+Gzuqvlj4Rx1dLVXRIOjxUYcIQw3vpLQgwPpiz52eWCeoCpzn06DcsF6aNPjhlp9uJRZCRxZ4yeiwh/A0xxiQtnB4fdZuUPmia1r62+oaxrDl4hUwR7kzHYl0YGfXxAW9GT17KGtjES2yO4kAUgquelNh0hgBKZRvny9imwsObG7ntw5ZG7H62sP7UySIUJqoNRAgMBAAE="
|
||||
}
|
5
test/fixtures/peer-browser.json
vendored
5
test/fixtures/peer-browser.json
vendored
@ -1,5 +0,0 @@
|
||||
{
|
||||
"id": "QmWDLy5Q31n9TbPsoE49NjagV8uJbNbixQ4JbyB2WcnNmv",
|
||||
"privKey": "CAASpwkwggSjAgEAAoIBAQCi3wtY+s70DLgeMKk7BhQq0Xmpkl1Z9YHaP+8ERM2slqPYSAnG1zaOP4iy+O0AAji+7/pIRGVYyY2oBmCM7Uo2jZZZHEAcJ0Ih4vIT7jwnqdOUrEkkiHxgVCq2+yxgY85LpiIDzJIme3Atc90roPfJi5wpgxM8Yvxwh/Jpr0it4VrNxWSQmGgxfrK9La5ezfFSs/MrU+aA8Qh+vMkRQHsBVyr30pzK1bD2l3kkg5hC48wClduoYHB1IGAOgx14Hsu+4qX/tpu7c3HXL9mPvjHsddqeKiHIs1u2kkXKw+myDyIJr/FVPHQxxpuOnA+GmPVQI5d+INLVzCV88CfzqKwpAgMBAAECggEATYj/HvHvWbFAaWbi+W1QZn3ofDhoZm4AzkSHZbHXc+UWxNyugtFrcFaAnirwsINePk+CB6s/z//LhwTaK9y+6q+Gto9DWeO6kOU4NxK/4mXviqRmAZVUGIuY9hkmrBB3Yf4JzWMy5Ez5PzocPSvZKkJjKkPzAVliMbQWTAedAuE1eq9b5Nb5MQJq/1a9RYFdFm0kYy7FgwQbMsoXc3Uv5s+TJGwXzhsUFH6MpNZGniUsS6wiwWPLjdGKebUrf/pD8Z7nwscSQBKzkyrHVBGfImOtNcgM+BwesN+WUFqyAJRvPv0SNVu+KwkVx3jEqAaIf5vxO62TNok+TuBxQP8HrQKBgQDai5dWEy08EZOyuM0MlU3fXhpUFczSrtxw1Ty4a4ma+jQitcaCnUdgA1keQhTpLYoSqE0NL8uykTC3FcRF2AQf0jTGZ94smbu5cJkWWh5NoIW2EnkSQwfPMjHGKjwKhltIbiAxlnbxmpLd3CTTx7eVXB5a5WgVih1fYOThoXzKTwKBgQC+yNLQ7FkxsCW3wRQ6ANk/6mQEYpuX6ap2TtxykvwCwuWbadufeRAZb52gbiNUgClqT7h0Kper6mBqhs7ubU55DVJMUTLKqWLQVkHdkQIoau5uXaYoX/1/bfc65BgHg61aUF93hJAYzv0NuEEX2UaLAXcpYfXe2Liv8AYvr9mcBwKBgDPDqpXdtvIqa6SCdzcKykYlAPF3tGsWteoX5+j6t744k07BZYKchEJuqJYtKszMV47xxEZiUso576L+Cd5NOzTaUlvIUGyaAUf8LpaHw/O5GNK2b2zu6ZOfHQEGEfCgQFDYnNGCBSxW44CfWy26eXZsOlhnTA8GBs0Ho076NBerAoGAV/5iFZBdFyjKTLVV8ebATNV7qfYdE3TndUesL4ARkeh4ZDTv4d7BiSnMxtjlnKy77Ve1mIaoi9c+/wMMYDW0EusNATwWNBjqBXMzT9D48NFZBThWUZrsXaDHfbcESjr2cohNb9+JYpfdaT2JcEl7WtOjNUgEUfMdQ7Bt+gKeWHMCgYEApoS8oknM4AtveP/w3YYSLSILbNMx2sYDNJ6HGDdg9ajjpTez4PS+BgqhcMkzMd/oVB7VD2n8As35LxppfHtSEPW1II/h3Phtd//HT8Wsu2L0BRoY/JIMLCbTwTSbtVr2+w9vWz7pg2Q2r0uScYadYNpMNF2uXC6vd6TFyCc17ic=",
|
||||
"pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCi3wtY+s70DLgeMKk7BhQq0Xmpkl1Z9YHaP+8ERM2slqPYSAnG1zaOP4iy+O0AAji+7/pIRGVYyY2oBmCM7Uo2jZZZHEAcJ0Ih4vIT7jwnqdOUrEkkiHxgVCq2+yxgY85LpiIDzJIme3Atc90roPfJi5wpgxM8Yvxwh/Jpr0it4VrNxWSQmGgxfrK9La5ezfFSs/MrU+aA8Qh+vMkRQHsBVyr30pzK1bD2l3kkg5hC48wClduoYHB1IGAOgx14Hsu+4qX/tpu7c3HXL9mPvjHsddqeKiHIs1u2kkXKw+myDyIJr/FVPHQxxpuOnA+GmPVQI5d+INLVzCV88CfzqKwpAgMBAAE="
|
||||
}
|
5
test/fixtures/peer-c.json
vendored
5
test/fixtures/peer-c.json
vendored
@ -1,5 +0,0 @@
|
||||
{
|
||||
"id": "QmYsRYcrQAftGS5U6XRsHdxiVDdGowAfCCYZLgxgL5JGpu",
|
||||
"privKey": "CAASqAkwggSkAgEAAoIBAQCoYkBQ0acKleSMT58n3IgxuCAoY4iGC8WMR5L5ZOeDGF1L8j31OwOXVP2AzxUMtBbhuqbaH6n0zXUDdCvXy+LSAE6IT+iDBMN9MhWv9XY8NBLmNJjoL6a5fWBbTmz3oFjnTLvSrmjFa7GuTFF9vFdYAf7ArFTAbwJxgofnfg+A2Vc572sh6ZFedeYWHC6lTe8f+RRAtlRnQsbvDuTRwj9s9EWT5WaKp+jGEoGHR2X8tekfHC6eDMju+Hb8325HZox1K1Tn7IAIZ0V0PfbqjdeyDtPaTWvBdTyhCrIwYIokMG3o8fn2PtyzBisStObXDUDH9ZBrypQ5wB5qJUsTZOEjAgMBAAECggEAJQ/jKibLRpgxgvSvil2PGKp3YYxu61pB9Bt47S3Zi1BckK/UFVsU8kVBuw1Zi79D3hFwUkhv5zMl00nJsKju1tw8bOJxwgzVXoQco+s6SAwKMONY2JpMbz16bP55G5QOnXKVUM0UwIdwx3WC5lMDQprF0PDdgrGsbwGVGk3z6Z8RV0CUIKsegv2njZs5y0ykSJsd1ZP4b0Kp2MButB2588K+7auR07WLTqlmZGPXBOzjgugkMrIjoEy6vhiRNqyD+V0xLxbRfHUX1JU7DTA40kSjomfB8Mv1kcGKT4rmqUhV5HARpPxW5EBitLV1nreYgl3z4gBHUzG7UERZLEmzOQKBgQDgidrTCBi6SHCjF0u8U6lFH7GlC8zc8mPJwYbJTL08vkHtM8YlTj4MhKdZJuxPNvoDD663mAwbKdUKaB33o8f3+qYVEXl3N/xfTSvcGT/fuDDkALYCXiqqXOugQ9E7CjrJ+ZlNRCCrHBuehazgqJVk2ePgroNEprt37ioejan2NQKBgQC/+iPVAQlg+l4qEvF3cD++lUsQkUnVGmm7nPpIjTqR0h/90rSWNBnyC4/N42m9naUswAQd+NieQF3z8LcKXFEjc0bb6tKlFsqxKnogT8zS5Niq9Jhqx7AKwVwZxEnF00qJ7AK5fYiyCBVtLC7m4/ZtK/rlGvZBMNvSw1R4BkGE9wKBgQCv0aIkPqHGM/Tm79Xg6I2Rz8h1WTEYDUCv+bbGJy3Cw6OxzimoioSxnqm/eGfvezf3b686mXdZSb9Ev9Q/VeK+PWkQ9DbtcExQf4gJd0f5sSuvTRM39W5pKgti2dea6F6ySpyM0PzXn1HNJhUBcWBRsP/rrhqhhgEjXr1iYUElxQKBgDvgsDZqqVs3HBGxcOEZALjX5d7YAhhWsa3Ty3i/nAoJaPMJOkwzGfiVxJeqG7wJpJAtn9sdV6scVnGUnfK8Uo5lES/csTDOyA+0OP1FBpSsziucOMHtYKoqHyjmQUBTo8ca9sqWLgGhZdV0V3iql5z7f7jfzPkOXTICvKDnCp6HAoGBAJ+xBhd/dFvH12MmPsW1m2bPDXY3Arc3xW/qUAAh7zivK5dFVHYIcVjRSiG0agyWuzz1ig1evwk9qhs+mvGmTPj4Z3mkh+lCdH1SMzi3IygX6kYnvZZcGd892vQFjNVzAxUUb8MRm5aoznzRvwzpSgdKT5lO5dXrPIsH8eK3RIY9",
|
||||
"pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCoYkBQ0acKleSMT58n3IgxuCAoY4iGC8WMR5L5ZOeDGF1L8j31OwOXVP2AzxUMtBbhuqbaH6n0zXUDdCvXy+LSAE6IT+iDBMN9MhWv9XY8NBLmNJjoL6a5fWBbTmz3oFjnTLvSrmjFa7GuTFF9vFdYAf7ArFTAbwJxgofnfg+A2Vc572sh6ZFedeYWHC6lTe8f+RRAtlRnQsbvDuTRwj9s9EWT5WaKp+jGEoGHR2X8tekfHC6eDMju+Hb8325HZox1K1Tn7IAIZ0V0PfbqjdeyDtPaTWvBdTyhCrIwYIokMG3o8fn2PtyzBisStObXDUDH9ZBrypQ5wB5qJUsTZOEjAgMBAAE="
|
||||
}
|
5
test/fixtures/peer-node.json
vendored
5
test/fixtures/peer-node.json
vendored
@ -1,5 +0,0 @@
|
||||
{
|
||||
"id": "QmY2cwDxD3B2n1Tv9YiBKZXgKkKCkcS5r2sCJMu9oBcGBq",
|
||||
"privKey": "CAASqAkwggSkAgEAAoIBAQCfBFkPbclTBzE4W4hhuteYrK4qR3eQnfbwfR1LUPdNmZKZxnTuQmL86ZCQCBzolqbJVFWb+phs2y3+0jCt/pdwSAjWIO8A9PJBcavJzYhi9JLLg8wl96xIHqHHQ8JsD8np1RLOYFQ1w2jNrXkDO0RQXb62+7xSoSyG4Y8UrhT/n65fv3aGlhqcYh1Tz4DV1bWdXzEOfg3R+KGmMcbJT4eC0x04gvxZXlTOmI2YMW+DunK/RN8fmt9YJqqu/f2bed+cuGsmwqfX/grDjVRjSxK2QECGYXjxU7WIZMrqBvlkBc7sWFyp3KHFU8bZYvP6a6K/dgoTbQAcz/UJswGpE9j3AgMBAAECggEAFZNFci53Lmi/aOIicwAi2Hg1eU8RDfIg2yhenSVzKHg5x9uBagJf4+jc0G4JXhVys8ZZhzxNChgC+ZwTNshgS7+6UzNMuliBthdyM4NLigGdGTfcrxoXqgFd+edbrcXGo49hadbwFgtZYO60iJe2ASF3CuTE/IEZxYM9IpUsDDKqZ8/fToLQk80XQuupiqwx4X6Yon/XTSLnNBSMA/hnhuwjJjw90DQQs+OER4ixbS7ENCFrMvV2omtwHh+bo2bfFhdyzSrV2wiRIyQiP6cOT5k7AsHkz2xzU7novNAW3D0bXDYhlBpTWEvn3yoKKkYuYptP9lyA2hcGedoVCR1wYQKBgQD1zWUdgnf3BfpYB0E3gItAEAgy1dvGny39XGvRtFZCWfykNKCyjOe48lXntNAjQq22Leo4U4LQWb/PWkYlO3mm2Zt1Posyx6lBbFS6h+5dCFDPbC7zvf1QD5kyAtS93Xds8gX4YWQVd0jj3ov7f3FghlTjnc6udTPD1oWiaekeqwKBgQClnTo+WHf9ZPQtZzBNht/rqEYA1p6KiedsAR1cXYMhernafLQ5FnT4czp3gZTcnEAsTfBg4mp3j+dqxmN/YpPI6hRMQlbsi7Zv3ro7FivTUHBYdWC2OHu+fDbpqras8s6nRBilKzkcaMf5WPw+fQpcr+zor4fFM01oGxfBClo+5QKBgQAI6kc1l8rUGdJnqPOzmKT0UOCLP3h2LsXTP6vlcj4CsBLavdHqR/QLoDZ/be5yqPN1/RpWqqi+99JeKe8LYKnb5F6gFQGleNpptg0oqs95bljH/SuCyaxLYBV1W+btb//p4qlWxemEYcwx/5tiJtAs6RJhIxMg/r0+6CP2rRK4ewKBgQCWNPQOd87cVCPiyiRVLG8LHaPgPsesf0cV/izTCT1VsCnAsDoFTQjqDhiJK04IiO7rQAU02iYWKr6JaUX000OWhjfCsqiEAnOFI01lKca18c7zbAI7Qx94tNBZPixQ0Cf+LRTtOTajPaWh0cN2KZKsXiNRJ2LMyKr8MRZqTylqwQKBgCb6022/mnnJQQue3NpgOnm9hSnnmm+zNU74DhQbzf5Uod+hzlpQsQnH7q935KAZj7VQGxkb0w4ctwyggoQnljCtuT+8kNLmZd5bFmx9JfY5uqzhvOueC/AKJdQ0S61NVs5YqEUrmLPRvSG5TXH3swWcHt7ofOgTsdZTKPqduj5x",
|
||||
"pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCfBFkPbclTBzE4W4hhuteYrK4qR3eQnfbwfR1LUPdNmZKZxnTuQmL86ZCQCBzolqbJVFWb+phs2y3+0jCt/pdwSAjWIO8A9PJBcavJzYhi9JLLg8wl96xIHqHHQ8JsD8np1RLOYFQ1w2jNrXkDO0RQXb62+7xSoSyG4Y8UrhT/n65fv3aGlhqcYh1Tz4DV1bWdXzEOfg3R+KGmMcbJT4eC0x04gvxZXlTOmI2YMW+DunK/RN8fmt9YJqqu/f2bed+cuGsmwqfX/grDjVRjSxK2QECGYXjxU7WIZMrqBvlkBc7sWFyp3KHFU8bZYvP6a6K/dgoTbQAcz/UJswGpE9j3AgMBAAE="
|
||||
}
|
23
test/fixtures/peer.js
vendored
Normal file
23
test/fixtures/peer.js
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
'use strict'
|
||||
|
||||
const PeerId = require('peer-id')
|
||||
|
||||
const peers = [{
|
||||
id: 'QmeS1ou3mrjCFGoFtRx3MwrGDzqKD6xbuYJU1CKtMrtFFu',
|
||||
privKey: 'CAASqAkwggSkAgEAAoIBAQChwzYwCNIyUkzEK3sILqq9ChAKZ9eU+ribY+B/xwAwDKPfvuqHq0hjauJBMcjiQyKAWz9xEBR3WupOM7h9M8oU+/e0xJUTt/CDOrtJ0PCgUXeBLkqsApbBoXW3yomHEDHxYttKzrtoTimiP1bhrxurcpVNC4CUYD+q8gw3sRZlsrqpeYYAfU04kS0BM75W/sUT90znnHvOxFXrEdMMdenEFhZOsDyEK9ENzwhkKgOGb18MBY4kN5DoW4bVd4ItfZnNwdkQtpP/X99tMWJxO4yqpngbywZGnkfirLeuRwt/xRGFVbLOigjBpTVpcbBqe1t2Flhuf/bfWYX4FbyElA5FAgMBAAECggEAJnDTcbrG6LpyD7QdeqZMYLwBb9eZfYfPUu37LaJGwyRd1Q/zf+YOP8HonoGMMWiuzD3i56Vgl7R9NbRIxUgHX9E43jZRDuyJNUZBt5r1c8OoWIR9rj63QLBz3wc8g2Iv3CMX5cEW/ASHFE1lAiCwvJ9wJ2zyU1BEEQWQLbPhlKzw7SLhr4fee45/7pnrKZMllt5vwC9pM6lrpIkICO5gUu0OWu5wfzzlTvfmCgfTb11VqKESEPbDBMUtpJibRqegE4xvipLklJ8VV8jz7NFs9bhgCpNM74Ngt5vGHcddeqtj//86UsClEw5YgWAdRe29ZjMApWvKIkginLjZEO8eiQKBgQDoDWii0rmlgBl1/8fENUSWxYvknGmWO7eWjVqMjDvA+waWUVDpTE+eHT1QAaPofM+nFz5PG+SpB55o4rXdxDesq+DqnaRAI9WtSHdgRtjgETyqoBAiahQ0zGWmSEYHGDB+xGctTMr8GxdhZxqZjjfyptp6oXXqZkmxgcogrx+WTwKBgQCydNDmCDpeH0kSvhAPxaNx5c9WkFEFSA0OCZOx57Y+Mt0MVamRILFrUrcMz095w8BQZkjlHjSHfsRgKa/b2eOd+3BhoMLZVtxRqBdpdqq1KTAcRRG4yA2KA39rttpVzaTV5SPfdDf3tsVlBtV784W63gVpN9gNfajyyrpeffiBKwKBgDnDrLprbl8uZigjhdznza0ie9JqxTXqo6bMhS/bcLx3QIqGr3eD0YXwjWSvI9gpyZ80gAQ9U0xoYxyE4vTTdXB8UL7Wgx6cTQKXuW+z8yTD5bArrBiFA4apItyjvRrjAJ9t0KlMJnNfYxCSE+MJrg+vTU+dhbbVw552SpScQ2atAoGBAKMu3rb6XyUiRpe05MsHVuYX1vi5Dt1dfVKQv1W3JJbLvAZDbsMeuh4BjRFRoMMflQPwBEg+zpn3+WpVtFG9dL5J5gHgF0zWeLDSnFX8BS2TdELlhccKaBcEC8hbdFtxqIFO/vaeN2902hv/m8e0b1zpGNmWDyKG/a7GYpV1a3/xAoGBAJtgGANDVk6qqcWGEVk56FH1ZksvgF3SPXWaXpzbZ5KLCcV5ooRyhowylKUZBBPowMeZ46tem2xwJbraB5kDg6WiSjBsXcbN95ivb8AuoRa6gDqAszjokQUSdpY7FTgMaL046AuihrKsQSly1jrQqbQu8JBgmnnBzus3s77inL/j',
|
||||
pubKey: 'CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQChwzYwCNIyUkzEK3sILqq9ChAKZ9eU+ribY+B/xwAwDKPfvuqHq0hjauJBMcjiQyKAWz9xEBR3WupOM7h9M8oU+/e0xJUTt/CDOrtJ0PCgUXeBLkqsApbBoXW3yomHEDHxYttKzrtoTimiP1bhrxurcpVNC4CUYD+q8gw3sRZlsrqpeYYAfU04kS0BM75W/sUT90znnHvOxFXrEdMMdenEFhZOsDyEK9ENzwhkKgOGb18MBY4kN5DoW4bVd4ItfZnNwdkQtpP/X99tMWJxO4yqpngbywZGnkfirLeuRwt/xRGFVbLOigjBpTVpcbBqe1t2Flhuf/bfWYX4FbyElA5FAgMBAAE='
|
||||
}, {
|
||||
id: 'QmYWHGZ9y1Bzx59bBzn85JsJxwmpBy5bpXDWDfwMfsHsxz',
|
||||
privKey: 'CAASqQkwggSlAgEAAoIBAQDLVaPqWFA8WgK6ixuPvhTHeQfBblmEFLEmraLlIDSWbMUPva6aJ1V/hi2I5QLXNeeiig5sco+nF+RKhGnzQ9NpgHRVZ7Ze+LWq3Q4YxONdzFeNUjTvJrDSKgkubA5EKC/LI6pU33WZbjyKkomGo+Gzuqvlj4Rx1dLVXRIOjxUYcIQw3vpLQgwPpiz52eWCeoCpzn06DcsF6aNPjhlp9uJRZCRxZ4yeiwh/A0xxiQtnB4fdZuUPmia1r62+oaxrDl4hUwR7kzHYl0YGfXxAW9GT17KGtjES2yO4kAUgquelNh0hgBKZRvny9imwsObG7ntw5ZG7H62sP7UySIUJqoNRAgMBAAECggEBAKLVU25BCQg7wQGokwra2wMfPoG+IDuw4mkqFlBNKS/prSo86c2TgFmel2qQk2TLS1OUIZbha38RmAXA4qQohe5wKzmV06tcmwdY/YgCbF5aXSbUVYXLQ0Ea3r1pVUdps1SHnElZpnCXoi4Kyc2kAgSPkkdFVnhfFvc9EE/Ob8NgMkdFhlosE5WVNqm4BKQ+mqONddSz4JDbDOApPs/rRpgYm7pJKc3vkrYwniPjyQGYb5EoSbSWuu31RzIcn3Bhte3wKtfMMlpn8MMpPiYo2WJ2eVG6hlUOxhHgS93Y6czCfAgsDtD3C2JpteewuBjg8N0d6WRArKxny83J34q0qy0CgYEA6YSo5UDEq1TF8sbtSVYg6MKSX92NO5MQI/8fTjU4tEwxn/yxpGsnqUu0WGYIc2qVaZuxtcnk2CQxEilxQTbWSIxKuTt7qofEcpSjLLQ4f4chk4DpPsba+S8zSUdWdjthPHZT9IYzobylGBLfbPxyXXiYn1VuqAJfFy8iV9XqmdcCgYEA3ukROQQZCJcgsNTc5uFAKUeQvzv1iae3fGawgJmIJW3Bl8+4dSm1diqG3ZXP1WU31no2aX50PqOZjoIpbl1ggT76cnBDuu3pItR3dNJFQyMEpQOWOjO+NBWF7sRswCvlqbyjofWkzsdd0BioL7vWMjPftiusyyAFA55HRoeStxcCgYEA0tP7rKdSKKFr6inhl+GT6rGod7bOSSgYXXd7qx9v55AXCauaMqiv8TAxTdIo9RMYfHWd91OlMeNTDmOuJcO9qVhIKn5iw266VPyPac/4ZmL5VHQBobTlhC4yLomirTIlMvJeEBmNygtIPrjjUUGGe49itA/szPD/Ky5Z4lV27pcCgYAWU3mqIELxnVFk5K0LYtwuRkC1Jqg9FVNHXnGnL7l3JjsRnXh4I6lNII1JfEvIr86b6LmybzvtWi1zHI5Rw4B68XfcJmpiOpnzJxyf0r+lLci1Tlqpka0nQlCbzYim5r6l9YLeIeBT5Zv7z7xoq4OUm6V4dX9lCNv3tM6mvcVwGQKBgQC9hhjD64/VKXL8wYKZyTAOVO5xYCcqylrpI39qdzl+sS8oqmLUbXnKsGY4If9U61XdULld41BJCRlv6CsKreynm6ZN41j9YRuWWLu8STJcniV9Ef9uVl1M1zo8kfnCHMCym9LkTfJY+Ow/kYhqPukJJL6ve1CVmIuA4rnZlshjbg==',
|
||||
pubKey: 'CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLVaPqWFA8WgK6ixuPvhTHeQfBblmEFLEmraLlIDSWbMUPva6aJ1V/hi2I5QLXNeeiig5sco+nF+RKhGnzQ9NpgHRVZ7Ze+LWq3Q4YxONdzFeNUjTvJrDSKgkubA5EKC/LI6pU33WZbjyKkomGo+Gzuqvlj4Rx1dLVXRIOjxUYcIQw3vpLQgwPpiz52eWCeoCpzn06DcsF6aNPjhlp9uJRZCRxZ4yeiwh/A0xxiQtnB4fdZuUPmia1r62+oaxrDl4hUwR7kzHYl0YGfXxAW9GT17KGtjES2yO4kAUgquelNh0hgBKZRvny9imwsObG7ntw5ZG7H62sP7UySIUJqoNRAgMBAAE='
|
||||
}, {
|
||||
id: 'QmYsRYcrQAftGS5U6XRsHdxiVDdGowAfCCYZLgxgL5JGpu',
|
||||
privKey: 'CAASqAkwggSkAgEAAoIBAQCoYkBQ0acKleSMT58n3IgxuCAoY4iGC8WMR5L5ZOeDGF1L8j31OwOXVP2AzxUMtBbhuqbaH6n0zXUDdCvXy+LSAE6IT+iDBMN9MhWv9XY8NBLmNJjoL6a5fWBbTmz3oFjnTLvSrmjFa7GuTFF9vFdYAf7ArFTAbwJxgofnfg+A2Vc572sh6ZFedeYWHC6lTe8f+RRAtlRnQsbvDuTRwj9s9EWT5WaKp+jGEoGHR2X8tekfHC6eDMju+Hb8325HZox1K1Tn7IAIZ0V0PfbqjdeyDtPaTWvBdTyhCrIwYIokMG3o8fn2PtyzBisStObXDUDH9ZBrypQ5wB5qJUsTZOEjAgMBAAECggEAJQ/jKibLRpgxgvSvil2PGKp3YYxu61pB9Bt47S3Zi1BckK/UFVsU8kVBuw1Zi79D3hFwUkhv5zMl00nJsKju1tw8bOJxwgzVXoQco+s6SAwKMONY2JpMbz16bP55G5QOnXKVUM0UwIdwx3WC5lMDQprF0PDdgrGsbwGVGk3z6Z8RV0CUIKsegv2njZs5y0ykSJsd1ZP4b0Kp2MButB2588K+7auR07WLTqlmZGPXBOzjgugkMrIjoEy6vhiRNqyD+V0xLxbRfHUX1JU7DTA40kSjomfB8Mv1kcGKT4rmqUhV5HARpPxW5EBitLV1nreYgl3z4gBHUzG7UERZLEmzOQKBgQDgidrTCBi6SHCjF0u8U6lFH7GlC8zc8mPJwYbJTL08vkHtM8YlTj4MhKdZJuxPNvoDD663mAwbKdUKaB33o8f3+qYVEXl3N/xfTSvcGT/fuDDkALYCXiqqXOugQ9E7CjrJ+ZlNRCCrHBuehazgqJVk2ePgroNEprt37ioejan2NQKBgQC/+iPVAQlg+l4qEvF3cD++lUsQkUnVGmm7nPpIjTqR0h/90rSWNBnyC4/N42m9naUswAQd+NieQF3z8LcKXFEjc0bb6tKlFsqxKnogT8zS5Niq9Jhqx7AKwVwZxEnF00qJ7AK5fYiyCBVtLC7m4/ZtK/rlGvZBMNvSw1R4BkGE9wKBgQCv0aIkPqHGM/Tm79Xg6I2Rz8h1WTEYDUCv+bbGJy3Cw6OxzimoioSxnqm/eGfvezf3b686mXdZSb9Ev9Q/VeK+PWkQ9DbtcExQf4gJd0f5sSuvTRM39W5pKgti2dea6F6ySpyM0PzXn1HNJhUBcWBRsP/rrhqhhgEjXr1iYUElxQKBgDvgsDZqqVs3HBGxcOEZALjX5d7YAhhWsa3Ty3i/nAoJaPMJOkwzGfiVxJeqG7wJpJAtn9sdV6scVnGUnfK8Uo5lES/csTDOyA+0OP1FBpSsziucOMHtYKoqHyjmQUBTo8ca9sqWLgGhZdV0V3iql5z7f7jfzPkOXTICvKDnCp6HAoGBAJ+xBhd/dFvH12MmPsW1m2bPDXY3Arc3xW/qUAAh7zivK5dFVHYIcVjRSiG0agyWuzz1ig1evwk9qhs+mvGmTPj4Z3mkh+lCdH1SMzi3IygX6kYnvZZcGd892vQFjNVzAxUUb8MRm5aoznzRvwzpSgdKT5lO5dXrPIsH8eK3RIY9',
|
||||
pubKey: 'CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCoYkBQ0acKleSMT58n3IgxuCAoY4iGC8WMR5L5ZOeDGF1L8j31OwOXVP2AzxUMtBbhuqbaH6n0zXUDdCvXy+LSAE6IT+iDBMN9MhWv9XY8NBLmNJjoL6a5fWBbTmz3oFjnTLvSrmjFa7GuTFF9vFdYAf7ArFTAbwJxgofnfg+A2Vc572sh6ZFedeYWHC6lTe8f+RRAtlRnQsbvDuTRwj9s9EWT5WaKp+jGEoGHR2X8tekfHC6eDMju+Hb8325HZox1K1Tn7IAIZ0V0PfbqjdeyDtPaTWvBdTyhCrIwYIokMG3o8fn2PtyzBisStObXDUDH9ZBrypQ5wB5qJUsTZOEjAgMBAAE='
|
||||
}]
|
||||
|
||||
module.exports.createPeerIdsFromFixtures = (length) => {
|
||||
return Promise.all(
|
||||
Array.from({ length }).map((_, i) => PeerId.createFromJSON(peers[i]))
|
||||
)
|
||||
}
|
@ -1,175 +1,180 @@
|
||||
/* eslint max-nested-callbacks: ["error", 8] */
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const pair = require('pull-pair/duplex')
|
||||
const chai = require('chai')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
const expect = chai.expect
|
||||
chai.use(dirtyChai)
|
||||
const PeerId = require('peer-id')
|
||||
const Connection = require('interface-connection').Connection
|
||||
const parallel = require('async/parallel')
|
||||
const series = require('async/series')
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
const ms = require('multistream-select')
|
||||
const pull = require('pull-stream')
|
||||
const Listener = ms.Listener
|
||||
const Dialer = ms.Dialer
|
||||
|
||||
const secio = require('../src')
|
||||
const duplexPair = require('it-pair/duplex')
|
||||
const Handshake = require('it-pb-rpc')
|
||||
const Secio = require('../src')
|
||||
const { createPeerIdsFromFixtures } = require('./fixtures/peer')
|
||||
const {
|
||||
createExchange,
|
||||
createProposal,
|
||||
generateKeys,
|
||||
identify,
|
||||
selectProtocols,
|
||||
verify
|
||||
} = require('../src/handshake/crypto')
|
||||
const { createBoxStream, createUnboxStream } = require('../src/etm')
|
||||
const State = require('../src/state')
|
||||
const handshake = require('../src/handshake')
|
||||
const { Propose } = require('../src/handshake/secio.proto')
|
||||
|
||||
describe('secio', () => {
|
||||
let peerA
|
||||
let peerB
|
||||
let peerC
|
||||
let remotePeer
|
||||
let localPeer
|
||||
|
||||
before((done) => {
|
||||
parallel([
|
||||
(cb) => PeerId.createFromJSON(require('./fixtures/peer-a'), cb),
|
||||
(cb) => PeerId.createFromJSON(require('./fixtures/peer-b'), cb),
|
||||
(cb) => PeerId.createFromJSON(require('./fixtures/peer-c'), cb)
|
||||
], (err, peers) => {
|
||||
expect(err).to.not.exist()
|
||||
peerA = peers[0]
|
||||
peerB = peers[1]
|
||||
peerC = peers[2]
|
||||
done()
|
||||
})
|
||||
before(async () => {
|
||||
[remotePeer, localPeer] = await createPeerIdsFromFixtures(2)
|
||||
})
|
||||
|
||||
it('exports a secio multicodec', () => {
|
||||
expect(secio.tag).to.equal('/secio/1.0.0')
|
||||
})
|
||||
it('performs a spec compliant inbound exchange', async () => {
|
||||
const [inboundConnection, outboundConnection] = duplexPair()
|
||||
await Promise.all([
|
||||
Secio.secureInbound(remotePeer, inboundConnection, null),
|
||||
(async () => {
|
||||
const wrap = Handshake(outboundConnection)
|
||||
const state = new State(localPeer, remotePeer)
|
||||
|
||||
it('upgrades a connection', (done) => {
|
||||
const p = pair()
|
||||
// Create our proposal
|
||||
const proposal = createProposal(state)
|
||||
|
||||
const aToB = secio.encrypt(peerA, new Connection(p[0]), peerB, (err) => expect(err).to.not.exist())
|
||||
const bToA = secio.encrypt(peerB, new Connection(p[1]), peerA, (err) => expect(err).to.not.exist())
|
||||
// Send our proposal
|
||||
const proposalLength = Buffer.allocUnsafe(4)
|
||||
proposalLength.writeInt32BE(proposal.length, 0)
|
||||
wrap.write(Buffer.concat([proposalLength, proposal]))
|
||||
|
||||
pull(
|
||||
pull.values([Buffer.from('hello world')]),
|
||||
aToB
|
||||
)
|
||||
// Read their proposal
|
||||
let theirProposalRaw = (await wrap.read()).slice()
|
||||
let dataLength = theirProposalRaw.readInt32BE(0)
|
||||
theirProposalRaw = theirProposalRaw.slice(4, dataLength + 4)
|
||||
const theirProposal = Propose.decode(theirProposalRaw)
|
||||
expect(theirProposal.rand).to.have.length(16)
|
||||
expect(theirProposal.pubkey).to.eql(remotePeer.pubKey.bytes)
|
||||
expect(theirProposal.exchanges).to.equal('P-256,P-384,P-521')
|
||||
expect(theirProposal.ciphers).to.equal('AES-256,AES-128')
|
||||
expect(theirProposal.hashes).to.equal('SHA256,SHA512')
|
||||
|
||||
pull(
|
||||
bToA,
|
||||
pull.collect((err, chunks) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(chunks).to.eql([Buffer.from('hello world')])
|
||||
done()
|
||||
})
|
||||
)
|
||||
})
|
||||
// Select protocols
|
||||
identify(state, theirProposalRaw)
|
||||
await selectProtocols(state)
|
||||
expect(state.protocols.local).to.include({ curveT: 'P-256', cipherT: 'AES-256', hashT: 'SHA256' })
|
||||
expect(state.protocols.remote).to.include({ curveT: 'P-256', cipherT: 'AES-256', hashT: 'SHA256' })
|
||||
|
||||
it('works over multistream-select', (done) => {
|
||||
const p = pair()
|
||||
// Create our exchange
|
||||
const exchange = await createExchange(state)
|
||||
|
||||
const listener = new Listener()
|
||||
const dialer = new Dialer()
|
||||
// Send our exchange
|
||||
const exchangeLength = Buffer.allocUnsafe(4)
|
||||
exchangeLength.writeInt32BE(exchange.length, 0)
|
||||
wrap.write(Buffer.concat([exchangeLength, exchange]))
|
||||
|
||||
series([
|
||||
(cb) => parallel([
|
||||
(cb) => listener.handle(p[0], cb),
|
||||
(cb) => dialer.handle(p[1], cb)
|
||||
], cb),
|
||||
(cb) => {
|
||||
listener.addHandler('/banana/1.0.0', (protocol, conn) => {
|
||||
const bToA = secio.encrypt(peerB, conn, peerA, (err) => expect(err).to.not.exist())
|
||||
// Read their exchange
|
||||
let theirExchangeRaw = (await wrap.read()).slice()
|
||||
dataLength = theirExchangeRaw.readInt32BE(0)
|
||||
theirExchangeRaw = theirExchangeRaw.slice(4, dataLength + 4)
|
||||
await verify(state, theirExchangeRaw)
|
||||
|
||||
pull(
|
||||
bToA,
|
||||
pull.collect((err, chunks) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(chunks).to.eql([Buffer.from('hello world')])
|
||||
done()
|
||||
})
|
||||
)
|
||||
})
|
||||
// Generate the crypto keys
|
||||
await generateKeys(state)
|
||||
|
||||
cb()
|
||||
},
|
||||
(cb) => dialer.select('/banana/1.0.0', (err, conn) => {
|
||||
expect(err).to.not.exist()
|
||||
// Create the crypto stream
|
||||
const box = createBoxStream(state.protocols.local.cipher, state.protocols.local.mac)
|
||||
const unbox = createUnboxStream(state.protocols.remote.cipher, state.protocols.remote.mac)
|
||||
|
||||
const aToB = secio.encrypt(peerA, conn, peerB, (err) => expect(err).to.not.exist())
|
||||
// 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()]))
|
||||
|
||||
pull(
|
||||
pull.values([Buffer.from('hello world')]),
|
||||
aToB
|
||||
)
|
||||
cb()
|
||||
})
|
||||
// Read our nonce from the crypto stream
|
||||
let ourNonceRaw = (await wrap.read())
|
||||
dataLength = ourNonceRaw.readInt32BE(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()
|
||||
|
||||
// Verify our nonce is correct
|
||||
expect(ourNonce.slice()).to.eql(state.proposal.out.rand)
|
||||
})()
|
||||
])
|
||||
})
|
||||
|
||||
it('establishes the connection even if the receiver does not know who is dialing', (done) => {
|
||||
const p = pair()
|
||||
it('performs a spec compliant outbound exchange', async () => {
|
||||
const [inboundConnection, outboundConnection] = duplexPair()
|
||||
await Promise.all([
|
||||
Secio.secureOutbound(localPeer, outboundConnection, remotePeer),
|
||||
(async () => {
|
||||
const wrap = Handshake(inboundConnection)
|
||||
const state = new State(remotePeer, localPeer)
|
||||
|
||||
const aToB = secio.encrypt(peerA, new Connection(p[0]), peerB, (err) => expect(err).to.not.exist())
|
||||
const bToA = secio.encrypt(peerB, new Connection(p[1]), undefined, (err) => expect(err).to.not.exist())
|
||||
// Create our proposal
|
||||
const proposal = createProposal(state)
|
||||
|
||||
pull(
|
||||
pull.values([Buffer.from('hello world')]),
|
||||
aToB
|
||||
)
|
||||
// Send our proposal
|
||||
const proposalLength = Buffer.allocUnsafe(4)
|
||||
proposalLength.writeInt32BE(proposal.length, 0)
|
||||
wrap.write(Buffer.concat([proposalLength, proposal]))
|
||||
|
||||
pull(
|
||||
bToA,
|
||||
pull.collect((err, chunks) => {
|
||||
expect(err).to.not.exist()
|
||||
// Read their proposal
|
||||
let theirProposalRaw = (await wrap.read()).slice()
|
||||
let dataLength = theirProposalRaw.readInt32BE(0)
|
||||
theirProposalRaw = theirProposalRaw.slice(4, dataLength + 4)
|
||||
const theirProposal = Propose.decode(theirProposalRaw)
|
||||
expect(theirProposal.rand).to.have.length(16)
|
||||
expect(theirProposal.pubkey).to.eql(localPeer.pubKey.bytes)
|
||||
expect(theirProposal.exchanges).to.equal('P-256,P-384,P-521')
|
||||
expect(theirProposal.ciphers).to.equal('AES-256,AES-128')
|
||||
expect(theirProposal.hashes).to.equal('SHA256,SHA512')
|
||||
|
||||
expect(chunks).to.eql([Buffer.from('hello world')])
|
||||
// Select protocols
|
||||
identify(state, theirProposalRaw)
|
||||
await selectProtocols(state)
|
||||
expect(state.protocols.local).to.include({ curveT: 'P-256', cipherT: 'AES-256', hashT: 'SHA256' })
|
||||
expect(state.protocols.remote).to.include({ curveT: 'P-256', cipherT: 'AES-256', hashT: 'SHA256' })
|
||||
|
||||
bToA.getPeerInfo((err, PeerInfo) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(PeerInfo.id.toB58String()).to.equal(peerA.toB58String())
|
||||
done()
|
||||
})
|
||||
})
|
||||
)
|
||||
})
|
||||
// Create our exchange
|
||||
const exchange = await createExchange(state)
|
||||
|
||||
it('fails if we dialed to the wrong peer', (done) => {
|
||||
const p = pair()
|
||||
let count = 0
|
||||
// Send our exchange
|
||||
const exchangeLength = Buffer.allocUnsafe(4)
|
||||
exchangeLength.writeInt32BE(exchange.length, 0)
|
||||
wrap.write(Buffer.concat([exchangeLength, exchange]))
|
||||
|
||||
function check (err) {
|
||||
expect(err).to.exist()
|
||||
if (++count === 2) { done() }
|
||||
}
|
||||
// Read their exchange
|
||||
let theirExchangeRaw = (await wrap.read()).slice()
|
||||
dataLength = theirExchangeRaw.readInt32BE(0)
|
||||
theirExchangeRaw = theirExchangeRaw.slice(4, dataLength + 4)
|
||||
await verify(state, theirExchangeRaw)
|
||||
|
||||
// we are using peerC Id on purpose to fail
|
||||
secio.encrypt(peerA, new Connection(p[0]), peerC, check)
|
||||
secio.encrypt(peerB, new Connection(p[1]), peerA, check)
|
||||
})
|
||||
// Generate the crypto keys
|
||||
await generateKeys(state)
|
||||
|
||||
it('bubbles errors from handshake failures properly', (done) => {
|
||||
const p = pair()
|
||||
const timeout = 60 * 1000 * 5
|
||||
const stateA = new State(peerA, peerC, timeout, () => { })
|
||||
const stateB = new State(peerB, peerA, timeout, () => { })
|
||||
const connA = new Connection(p[0])
|
||||
const connB = new Connection(p[1])
|
||||
// Create the crypto stream
|
||||
const box = createBoxStream(state.protocols.local.cipher, state.protocols.local.mac)
|
||||
const unbox = createUnboxStream(state.protocols.remote.cipher, state.protocols.remote.mac)
|
||||
|
||||
function finish (err) {
|
||||
expect(err).to.exist()
|
||||
done()
|
||||
}
|
||||
// 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()]))
|
||||
|
||||
pull(
|
||||
connA,
|
||||
handshake(stateA, finish),
|
||||
connA
|
||||
)
|
||||
// Read our nonce from the crypto stream
|
||||
let ourNonceRaw = (await wrap.read())
|
||||
dataLength = ourNonceRaw.readInt32BE(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()
|
||||
|
||||
pull(
|
||||
connB,
|
||||
handshake(stateB, finish),
|
||||
connB
|
||||
)
|
||||
// Verify our nonce is correct
|
||||
expect(ourNonce.slice()).to.eql(state.proposal.out.rand)
|
||||
})()
|
||||
])
|
||||
})
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user