mirror of
https://github.com/fluencelabs/js-libp2p-secio
synced 2025-03-15 01:40:51 +00:00
test: check if we dialed to the right peer
This commit is contained in:
parent
ee1f64c98e
commit
74f6fd8ab3
@ -15,17 +15,18 @@ module.exports = {
|
||||
hooks: {
|
||||
browser: {
|
||||
pre: (done) => {
|
||||
PeerId.createFromJSON(peerNodeJSON, (err, id) => {
|
||||
PeerId.createFromJSON(peerNodeJSON, (err, peerId) => {
|
||||
if (err) { throw err }
|
||||
|
||||
const ws = new WS()
|
||||
|
||||
listener = ws.createListener((conn) => {
|
||||
const encrypted = secio.encrypt(id, id._privKey, conn, (err) => {
|
||||
const encryptedConn = secio.encrypt(peerId, conn, undefined, (err) => {
|
||||
if (err) { throw err }
|
||||
})
|
||||
|
||||
pull(encrypted, encrypted)
|
||||
// echo
|
||||
pull(encryptedConn, encryptedConn)
|
||||
})
|
||||
|
||||
listener.listen(ma, done)
|
||||
|
12
README.md
12
README.md
@ -43,12 +43,12 @@ const secio = require('libp2p-secio')
|
||||
|
||||
The current `secio` tag, usable in `multistream`.
|
||||
|
||||
### `const encryptedConnection = secio.encrypt(id, key, plainTextConnection [, callback])`
|
||||
### `const encryptedConnection = secio.encrypt(localPeerId, plainTextConnection [, remotePeerId] [, callback])`
|
||||
|
||||
- `id: PeerId` - The id of the node we are doing the SECIO handshake with.
|
||||
- `key: RSAPrivateKey` - The private key of our node node.
|
||||
- `insecure: PullStream` - The insecure connection.
|
||||
- `callback: Function` - Called if an error happens during the initialization.
|
||||
- `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 encripted.
|
||||
|
||||
@ -78,8 +78,6 @@ const nodeStreamInstance = pullToStream(pullStreamInstance)
|
||||
|
||||
To learn more about this utility, visit https://pull-stream.github.io/#pull-stream-to-stream.
|
||||
|
||||
|
||||
|
||||
## Contribute
|
||||
|
||||
Feel free to join in. All welcome. Open an [issue](https://github.com/libp2p/js-libp2p-secio/issues)!
|
||||
|
@ -53,8 +53,8 @@ suite.add('establish an encrypted channel', (deferred) => {
|
||||
const peerA = peers[0]
|
||||
const peerB = peers[1]
|
||||
|
||||
const aToB = secio.encrypt(peerB, peerA.privKey, p[0], (err) => { throw err })
|
||||
const bToA = secio.encrypt(peerA, peerB.privKey, p[1], (err) => { throw err })
|
||||
const aToB = secio.encrypt(peerA, p[0], peerB, (err) => { throw err })
|
||||
const bToA = secio.encrypt(peerB, p[1], peerA, (err) => { throw err })
|
||||
|
||||
sendData(aToB, bToA, {}, deferred)
|
||||
}, { defer: true })
|
||||
@ -83,8 +83,8 @@ cases.forEach((el) => {
|
||||
const peerA = peers[0]
|
||||
const peerB = peers[1]
|
||||
|
||||
const aToB = secio.encrypt(peerB, peerA.privKey, p[0], (err) => { throw err })
|
||||
const bToA = secio.encrypt(peerA, peerB.privKey, p[1], (err) => { throw err })
|
||||
const aToB = secio.encrypt(peerA, p[0], peerB, (err) => { throw err })
|
||||
const bToA = secio.encrypt(peerB, p[1], peerA, (err) => { throw err })
|
||||
|
||||
sendData(aToB, bToA, { times: times, size: size }, deferred)
|
||||
}, { defer: true })
|
||||
|
@ -31,6 +31,7 @@
|
||||
"libp2p-crypto": "~0.11.0",
|
||||
"multihashing-async": "~0.4.7",
|
||||
"peer-id": "~0.10.4",
|
||||
"peer-info": "^0.11.4",
|
||||
"protons": "^1.0.1",
|
||||
"pull-defer": "^0.2.2",
|
||||
"pull-handshake": "^1.1.4",
|
||||
|
@ -68,12 +68,20 @@ exports.identify = (state, msg, callback) => {
|
||||
const pubkey = state.proposal.in.pubkey
|
||||
|
||||
state.key.remote = crypto.keys.unmarshalPublicKey(pubkey)
|
||||
|
||||
PeerId.createFromPubKey(pubkey.toString('base64'), (err, remoteId) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
state.id.remote = remoteId
|
||||
// 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
|
||||
}
|
||||
|
||||
log('1.1 identify - %s - identified remote peer as %s', state.id.local.toB58String(), state.id.remote.toB58String())
|
||||
callback()
|
||||
|
@ -27,9 +27,7 @@ module.exports = function exchange (state, cb) {
|
||||
},
|
||||
(cb) => crypto.generateKeys(state, cb)
|
||||
], (err) => {
|
||||
if (err) {
|
||||
return cb(err)
|
||||
}
|
||||
if (err) { return cb(err) }
|
||||
|
||||
log('2. exchange - finish')
|
||||
cb()
|
||||
|
@ -6,9 +6,9 @@ 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) {
|
||||
// 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),
|
||||
@ -22,6 +22,9 @@ module.exports = function handshake (state) {
|
||||
}
|
||||
state.shake.abort(err)
|
||||
}
|
||||
|
||||
// signal when the handshake is finished so that plumbing can happen
|
||||
callback()
|
||||
})
|
||||
|
||||
return state.stream
|
||||
|
54
src/index.js
54
src/index.js
@ -2,41 +2,57 @@
|
||||
|
||||
const pull = require('pull-stream')
|
||||
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')
|
||||
|
||||
module.exports = {
|
||||
tag: '/secio/1.0.0',
|
||||
encrypt (local, key, insecure, callback) {
|
||||
if (!local) {
|
||||
throw new Error('no local id provided')
|
||||
encrypt (localId, conn, remoteId, callback) {
|
||||
assert(localId, 'no local private key provided')
|
||||
assert(conn, 'no connection for the handshake provided')
|
||||
|
||||
if (typeof remoteId === 'function') {
|
||||
callback = remoteId
|
||||
remoteId = undefined
|
||||
}
|
||||
|
||||
if (!key) {
|
||||
throw new Error('no local private key provided')
|
||||
}
|
||||
callback = once(callback || function (err) {
|
||||
if (err) { log.error(err) }
|
||||
})
|
||||
|
||||
if (!insecure) {
|
||||
throw new Error('no insecure stream provided')
|
||||
}
|
||||
const timeout = 60 * 1000 * 5
|
||||
|
||||
if (!callback) {
|
||||
callback = (err) => {
|
||||
if (err) {
|
||||
console.error(err)
|
||||
const state = new State(localId, remoteId, timeout, callback)
|
||||
|
||||
function finish (err) {
|
||||
if (err) { return callback(err) }
|
||||
|
||||
conn.getPeerInfo((err, peerInfo) => {
|
||||
encryptedConnection.setInnerConn(state.secure)
|
||||
|
||||
if (err) { // no peerInfo yet, means I'm the receiver
|
||||
encryptedConnection.setPeerInfo(new PeerInfo(state.id.remote))
|
||||
}
|
||||
}
|
||||
|
||||
callback()
|
||||
})
|
||||
}
|
||||
|
||||
const state = new State(local, key, 60 * 1000 * 5, callback)
|
||||
const encryptedConnection = new Connection(undefined, conn)
|
||||
|
||||
pull(
|
||||
insecure,
|
||||
handshake(state),
|
||||
insecure
|
||||
conn,
|
||||
handshake(state, finish),
|
||||
conn
|
||||
)
|
||||
|
||||
return new Connection(state.secure, insecure)
|
||||
return encryptedConnection
|
||||
}
|
||||
}
|
||||
|
57
src/state.js
57
src/state.js
@ -4,37 +4,31 @@ const handshake = require('pull-handshake')
|
||||
const deferred = require('pull-defer')
|
||||
|
||||
class State {
|
||||
constructor (id, key, timeout, cb) {
|
||||
constructor (localId, remoteId, timeout, callback) {
|
||||
if (typeof timeout === 'function') {
|
||||
cb = timeout
|
||||
callback = timeout
|
||||
timeout = undefined
|
||||
}
|
||||
|
||||
this.setup()
|
||||
this.id.local = id
|
||||
this.key.local = key
|
||||
|
||||
this.id.local = localId
|
||||
// TODO use remoteId to verify PeersIdentity
|
||||
this.id.remote = remoteId
|
||||
this.key.local = localId.privKey
|
||||
this.timeout = timeout || 60 * 1000
|
||||
cb = cb || (() => {})
|
||||
callback = callback || (() => {})
|
||||
|
||||
this.secure = deferred.duplex()
|
||||
this.stream = handshake({timeout: this.timeout}, cb)
|
||||
this.stream = handshake({ timeout: this.timeout }, callback)
|
||||
this.shake = this.stream.handshake
|
||||
delete this.stream.handshake
|
||||
}
|
||||
|
||||
setup () {
|
||||
this.id = {
|
||||
local: null,
|
||||
remote: null
|
||||
}
|
||||
|
||||
this.key = {
|
||||
local: null,
|
||||
remote: null
|
||||
}
|
||||
|
||||
this.id = { local: null, remote: null }
|
||||
this.key = { local: null, remote: null }
|
||||
this.shake = null
|
||||
|
||||
this.cleanSecrets()
|
||||
}
|
||||
|
||||
@ -42,30 +36,11 @@ class State {
|
||||
cleanSecrets () {
|
||||
this.shared = {}
|
||||
|
||||
this.ephemeralKey = {
|
||||
local: null,
|
||||
remote: null
|
||||
}
|
||||
|
||||
this.proposal = {
|
||||
in: null,
|
||||
out: null
|
||||
}
|
||||
|
||||
this.proposalEncoded = {
|
||||
in: null,
|
||||
out: null
|
||||
}
|
||||
|
||||
this.protocols = {
|
||||
local: null,
|
||||
remote: null
|
||||
}
|
||||
|
||||
this.exchange = {
|
||||
in: null,
|
||||
out: null
|
||||
}
|
||||
this.ephemeralKey = { local: null, remote: null }
|
||||
this.proposal = { in: null, out: null }
|
||||
this.proposalEncoded = { in: null, out: null }
|
||||
this.protocols = { local: null, remote: null }
|
||||
this.exchange = { in: null, out: null }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,9 +116,7 @@ exports.digest = (buf, cb) => {
|
||||
exports.write = function write (state, msg, cb) {
|
||||
cb = cb || (() => {})
|
||||
pull(
|
||||
pull.values([
|
||||
msg
|
||||
]),
|
||||
pull.values([msg]),
|
||||
lp.encode({fixed: true, bytes: 4}),
|
||||
pull.collect((err, res) => {
|
||||
if (err) {
|
||||
|
@ -12,7 +12,9 @@ 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', () => {
|
||||
@ -22,17 +24,22 @@ describe('secio between browser <-> nodejs through websockets', () => {
|
||||
|
||||
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) => {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
expect(err).to.not.exist()
|
||||
|
||||
encryptedConn = secio.encrypt(res[0], res[0]._privKey, conn)
|
||||
const peerIdNode = res[0]
|
||||
const peerIdBrowser = res[1]
|
||||
|
||||
encryptedConn = secio.encrypt(peerIdBrowser, conn, peerIdNode, (err) => {
|
||||
expect(err).to.not.exist()
|
||||
})
|
||||
done()
|
||||
})
|
||||
})
|
||||
@ -44,7 +51,7 @@ describe('secio between browser <-> nodejs through websockets', () => {
|
||||
source: pull.values([message]),
|
||||
sink: pull.collect((err, results) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(results).to.be.eql([message])
|
||||
expect(results).to.eql([message])
|
||||
done()
|
||||
})
|
||||
}, 'GoodBye')
|
||||
|
@ -8,7 +8,7 @@ const dirtyChai = require('dirty-chai')
|
||||
const expect = chai.expect
|
||||
chai.use(dirtyChai)
|
||||
const PeerId = require('peer-id')
|
||||
const crypto = require('libp2p-crypto')
|
||||
const Connection = require('interface-connection').Connection
|
||||
const parallel = require('async/parallel')
|
||||
const series = require('async/series')
|
||||
const Buffer = require('safe-buffer').Buffer
|
||||
@ -45,8 +45,8 @@ describe('secio', () => {
|
||||
it('upgrades a connection', (done) => {
|
||||
const p = pair()
|
||||
|
||||
const aToB = secio.encrypt(peerB, peerA.privKey, p[0], (err) => expect(err).to.not.exist())
|
||||
const bToA = secio.encrypt(peerA, peerB.privKey, p[1], (err) => expect(err).to.not.exist())
|
||||
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())
|
||||
|
||||
pull(
|
||||
pull.values([Buffer.from('hello world')]),
|
||||
@ -76,7 +76,7 @@ describe('secio', () => {
|
||||
], cb),
|
||||
(cb) => {
|
||||
listener.addHandler('/banana/1.0.0', (protocol, conn) => {
|
||||
const bToA = secio.encrypt(peerA, peerB.privKey, conn, (err) => expect(err).to.not.exist())
|
||||
const bToA = secio.encrypt(peerB, conn, peerA, (err) => expect(err).to.not.exist())
|
||||
|
||||
pull(
|
||||
bToA,
|
||||
@ -93,7 +93,7 @@ describe('secio', () => {
|
||||
(cb) => dialer.select('/banana/1.0.0', (err, conn) => {
|
||||
expect(err).to.not.exist()
|
||||
|
||||
const aToB = secio.encrypt(peerB, peerA.privKey, conn, (err) => expect(err).to.not.exist())
|
||||
const aToB = secio.encrypt(peerA, conn, peerB, (err) => expect(err).to.not.exist())
|
||||
|
||||
pull(
|
||||
pull.values([Buffer.from('hello world')]),
|
||||
@ -104,6 +104,44 @@ describe('secio', () => {
|
||||
])
|
||||
})
|
||||
|
||||
it.skip('fails if we dialed to the wrong peer', (done) => {
|
||||
it('establishes the connection even if the receiver does not know who is dialing', (done) => {
|
||||
const p = pair()
|
||||
|
||||
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())
|
||||
|
||||
pull(
|
||||
pull.values([Buffer.from('hello world')]),
|
||||
aToB
|
||||
)
|
||||
|
||||
pull(
|
||||
bToA,
|
||||
pull.collect((err, chunks) => {
|
||||
expect(err).to.not.exist()
|
||||
|
||||
expect(chunks).to.eql([Buffer.from('hello world')])
|
||||
|
||||
bToA.getPeerInfo((err, PeerInfo) => {
|
||||
expect(err).to.not.exist()
|
||||
expect(PeerInfo.id.toB58String()).to.equal(peerA.toB58String())
|
||||
done()
|
||||
})
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it('fails if we dialed to the wrong peer', (done) => {
|
||||
const p = pair()
|
||||
let count = 0
|
||||
|
||||
function check (err) {
|
||||
expect(err).to.exist()
|
||||
if (++count === 2) { done() }
|
||||
}
|
||||
|
||||
// we are using peerC Id on purpose to fail
|
||||
secio.encrypt(peerA, p[0], peerC, check)
|
||||
secio.encrypt(peerB, p[1], peerA, check)
|
||||
})
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user