test: check if we dialed to the right peer

This commit is contained in:
David Dias 2018-01-07 13:58:30 +00:00
parent ee1f64c98e
commit 74f6fd8ab3
12 changed files with 138 additions and 95 deletions

View File

@ -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)

View File

@ -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)!

View File

@ -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 })

View File

@ -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",

View File

@ -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()

View File

@ -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()

View File

@ -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

View File

@ -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
}
}

View File

@ -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 }
}
}

View File

@ -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) {

View File

@ -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')

View File

@ -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)
})
})