Async Crypto Endeavour (+benchmarks!) (#19)

* feat: add benchmarks and integrate with new libp2p-crypto

* start removing node-forge

* less forge

* cleanup and fixes

* benchmarked and tested

* remove unused .aegir

* Replace lib multihashing with multihashing-async

* cleanup package.json

* Using string instead of buffer for proto file

* chore: fix benchmarks

* docs(readme): update README URLs based on HTTP redirects

* ready for next aegir

* update aegir

* ready

* chore: update deps
This commit is contained in:
Friedel Ziegelmayer 2016-11-03 10:23:33 +01:00 committed by David Dias
parent 4f1911f817
commit a1167c0084
18 changed files with 443 additions and 292 deletions

View File

@ -1,16 +0,0 @@
'use strict'
const path = require('path')
module.exports = {
webpack: {
resolve: {
alias: {
'node-forge': path.resolve(
path.dirname(require.resolve('libp2p-crypto')),
'../vendor/forge.bundle.js'
)
}
}
}
}

1
.gitignore vendored
View File

@ -31,5 +31,4 @@ build
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules
lib
dist

View File

@ -1,9 +1,15 @@
sudo: false
language: node_js
node_js:
- 4
- 5
- stable
matrix:
include:
- node_js: 4
env: CXX=g++-4.8
- node_js: 6
env:
- SAUCE=true
- CXX=g++-4.8
- node_js: stable
env: CXX=g++-4.8
# Make sure we have new NPM.
before_install:
@ -14,7 +20,6 @@ script:
- npm test
- npm run coverage
before_script:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
@ -22,13 +27,10 @@ before_script:
after_success:
- npm run coverage-publish
env:
- CXX=g++-4.8
addons:
firefox: 'latest'
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
- g++-4.8

View File

@ -8,6 +8,10 @@
[![Travis CI](https://travis-ci.org/libp2p/js-libp2p-secio.svg?branch=master)](https://travis-ci.org/libp2p/js-libp2p-secio)
[![Circle CI](https://circleci.com/gh/libp2p/js-libp2p-secio.svg?style=svg)](https://circleci.com/gh/libp2p/js-libp2p-secio)
[![Dependency Status](https://david-dm.org/libp2p/js-libp2p-secio.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-secio) [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/feross/standard)
![](https://img.shields.io/badge/npm-%3E%3D3.0.0-orange.svg?style=flat-square)
![](https://img.shields.io/badge/Node.js-%3E%3D4.0.0-orange.svg?style=flat-square)
[![Sauce Test Status](https://saucelabs.com/browser-matrix/libp2p-js-secio.svg)](https://saucelabs.com/u/libp2p-js-secio)
> Secio implementation in JavaScript

87
benchmarks/send.js Normal file
View File

@ -0,0 +1,87 @@
'use strict'
const Benchmark = require('benchmark')
const pull = require('pull-stream')
const pair = require('pull-pair/duplex')
const PeerId = require('peer-id')
const crypto = require('libp2p-crypto')
const secio = require('../src')
const suite = new Benchmark.Suite('secio')
const ids = []
suite.add('createKey', function (d) {
crypto.generateKeyPair('RSA', 2048, (err, key) => {
if (err) {
throw err
}
PeerId.createFromPrivKey(key.bytes, (err, id) => {
if (err) {
throw err
}
ids.push(id)
d.resolve()
})
})
}, {
defer: true
})
.add('send', function (deferred) {
const p = pair()
createSession(p[0], (err, local) => {
if (err) {
throw err
}
createSession(p[1], (err, remote) => {
if (err) {
throw err
}
sendMessages(local, remote)
})
})
function sendMessages (local, remote) {
pull(
pull.infinite(),
pull.take(100),
pull.map((val) => Buffer(val.toString())),
local
)
pull(
remote,
pull.take(100),
pull.collect((err, chunks) => {
if (err) throw err
if (chunks.length !== 100) throw new Error('Did not receive enough chunks')
deferred.resolve()
})
)
}
}, {
defer: true
})
.on('cycle', (event) => {
console.log(String(event.target))
})
// run async
.run({
async: true
})
function createSession (insecure, cb) {
crypto.generateKeyPair('RSA', 2048, (err, key) => {
if (err) {
return cb(err)
}
PeerId.createFromPrivKey(key.bytes, (err, id) => {
if (err) {
return cb(err)
}
cb(null, secio.encrypt(id, key, insecure))
})
})
}

View File

@ -2,8 +2,7 @@
"name": "libp2p-secio",
"version": "0.5.0",
"description": "Secio implementation in JavaScript",
"main": "lib/index.js",
"jsnext:main": "src/index.js",
"main": "src/index.js",
"scripts": {
"lint": "aegir-lint",
"build": "aegir-build",
@ -14,7 +13,8 @@
"release-minor": "aegir-release --type minor",
"release-major": "aegir-release --type major",
"coverage": "aegir-coverage",
"coverage-publish": "aegir-coverage publish"
"coverage-publish": "aegir-coverage publish",
"bench": "node benchmarks/send.js"
},
"keywords": [
"IPFS",
@ -22,37 +22,36 @@
"crypto",
"rsa"
],
"author": "Friedel Ziegelmayer <dignifiedqurie@gmail.com>",
"author": "Friedel Ziegelmayer <dignifiedquire@gmail.com>",
"license": "MIT",
"dependencies": {
"async": "^2.1.2",
"debug": "^2.2.0",
"interface-connection": "^0.2.1",
"libp2p-crypto": "^0.6.1",
"multihashing": "^0.2.1",
"node-forge": "^0.6.42",
"peer-id": "^0.7.0",
"interface-connection": "^0.3.0",
"libp2p-crypto": "^0.7.0",
"multihashing-async": "^0.2.0",
"peer-id": "^0.8.0",
"protocol-buffers": "^3.1.6",
"pull-defer": "^0.2.2",
"pull-handshake": "^1.1.4",
"pull-length-prefixed": "^1.2.0",
"pull-stream": "^3.4.3",
"pull-through": "^1.0.18",
"run-series": "^1.1.4"
"pull-stream": "^3.5.0"
},
"devDependencies": {
"aegir": "^8.0.0",
"aegir": "^9.0.1",
"benchmark": "^2.1.2",
"chai": "^3.5.0",
"multistream-select": "^0.11.0",
"multistream-select": "^0.11.1",
"pre-commit": "^1.1.3",
"pull-pair": "^1.1.0",
"run-parallel": "^1.1.6"
"pull-pushable": "^2.0.1"
},
"pre-commit": [
"lint",
"test"
],
"engines": {
"node": "^4.0.0"
"node": ">=4.0.0"
},
"repository": {
"type": "git",
@ -68,4 +67,4 @@
"Richard Littauer <richard.littauer@gmail.com>",
"greenkeeperio-bot <support@greenkeeper.io>"
]
}
}

View File

@ -1,73 +1,67 @@
'use strict'
const through = require('pull-through')
const pull = require('pull-stream')
const lp = require('pull-length-prefixed')
const toForgeBuffer = require('./support').toForgeBuffer
const lpOpts = {
fixed: true,
bytes: 4
}
exports.createBoxStream = (cipher, mac) => {
const pt = through(function (chunk) {
cipher.update(toForgeBuffer(chunk))
if (cipher.output.length() > 0) {
const data = new Buffer(cipher.output.getBytes(), 'binary')
mac.update(data.toString('binary'))
const macBuffer = new Buffer(mac.digest().getBytes(), 'binary')
this.queue(Buffer.concat([data, macBuffer]))
// reset hmac
mac.start(null, null)
}
})
return pull(
pt,
pull.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)
)
}
exports.createUnboxStream = (decipher, mac) => {
const pt = through(function (chunk) {
const l = chunk.length
const macSize = mac.getMac().length()
if (l < macSize) {
return this.emit('error', new Error(`buffer (${l}) shorter than MAC size (${macSize})`))
}
const mark = l - macSize
const data = chunk.slice(0, mark)
const macd = chunk.slice(mark)
// Clear out any previous data
mac.start(null, null)
mac.update(data.toString('binary'))
const expected = new Buffer(mac.getMac().getBytes(), 'binary')
// reset hmac
mac.start(null, null)
if (!macd.equals(expected)) {
return this.emit('error', new Error(`MAC Invalid: ${macd.toString('hex')} != ${expected.toString('hex')}`))
}
// all good, decrypt
decipher.update(toForgeBuffer(data))
if (decipher.output.length() > 0) {
const data = new Buffer(decipher.output.getBytes(), 'binary')
this.queue(data)
}
})
return pull(
lp.decode(lpOpts),
pt
pull.asyncMap((chunk, cb) => {
const l = chunk.length
const macSize = mac.length
if (l < macSize) {
return cb(new Error(`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)
}
if (!macd.equals(expected)) {
return cb(new Error(`MAC Invalid: ${macd.toString('hex')} != ${expected.toString('hex')}`))
}
// all good, decrypt
decipher.decrypt(data, (err, decrypted) => {
if (err) {
return cb(err)
}
cb(null, decrypted)
})
})
})
)
}

View File

@ -1,15 +1,15 @@
'use strict'
const protobuf = require('protocol-buffers')
const path = require('path')
const fs = require('fs')
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 = protobuf(fs.readFileSync(path.join(__dirname, 'secio.proto')))
const pbm = protobuf(require('./secio.proto'))
const support = require('../support')
@ -29,27 +29,38 @@ exports.createProposal = (state) => {
return state.proposalEncoded.out
}
exports.createExchange = (state) => {
const res = crypto.generateEphemeralKeyPair(state.protocols.local.curveT)
state.ephemeralKey.local = res.key
state.shared.generate = res.genSharedKey
exports.createExchange = (state, callback) => {
crypto.generateEphemeralKeyPair(state.protocols.local.curveT, (err, res) => {
if (err) {
return callback(err)
}
// Gather corpus to sign.
const selectionOut = Buffer.concat([
state.proposalEncoded.out,
state.proposalEncoded.in,
state.ephemeralKey.local
])
state.ephemeralKey.local = res.key
state.shared.generate = res.genSharedKey
state.exchange.out = {
epubkey: state.ephemeralKey.local,
signature: new Buffer(state.key.local.sign(selectionOut), 'binary')
}
// Gather corpus to sign.
const selectionOut = Buffer.concat([
state.proposalEncoded.out,
state.proposalEncoded.in,
state.ephemeralKey.local
])
return pbm.Exchange.encode(state.exchange.out)
state.key.local.sign(selectionOut, (err, sig) => {
if (err) {
return callback(err)
}
state.exchange.out = {
epubkey: state.ephemeralKey.local,
signature: sig
}
callback(null, pbm.Exchange.encode(state.exchange.out))
})
})
}
exports.identify = (state, msg) => {
exports.identify = (state, msg, callback) => {
log('1.1 identify')
state.proposalEncoded.in = msg
@ -57,12 +68,19 @@ exports.identify = (state, msg) => {
const pubkey = state.proposal.in.pubkey
state.key.remote = crypto.unmarshalPublicKey(pubkey)
state.id.remote = PeerId.createFromPubKey(pubkey.toString('base64'))
PeerId.createFromPubKey(pubkey.toString('base64'), (err, remoteId) => {
if (err) {
return callback(err)
}
log('1.1 identify - %s - identified remote peer as %s', state.id.local.toB58String(), state.id.remote.toB58String())
state.id.remote = remoteId
log('1.1 identify - %s - identified remote peer as %s', state.id.local.toB58String(), state.id.remote.toB58String())
callback()
})
}
exports.selectProtocols = (state) => {
exports.selectProtocols = (state, callback) => {
log('1.2 selection')
const local = {
@ -81,25 +99,30 @@ exports.selectProtocols = (state) => {
nonce: state.proposal.in.rand
}
let selected = support.selectBest(local, remote)
// 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
}
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
}
state.protocols.local = {
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
}
callback()
})
}
exports.verify = (state, msg) => {
exports.verify = (state, msg, callback) => {
log('2.1. verify')
state.exchange.in = pbm.Exchange.decode(msg)
@ -111,43 +134,57 @@ exports.verify = (state, msg) => {
state.ephemeralKey.remote
])
const sigOk = state.key.remote.verify(selectionIn, state.exchange.in.signature)
state.key.remote.verify(selectionIn, state.exchange.in.signature, (err, sigOk) => {
if (err) {
return callback(err)
}
if (!sigOk) {
throw new Error('Bad signature')
}
if (!sigOk) {
return callback(new Error('Bad signature'))
}
log('2.1. verify - signature verified')
log('2.1. verify - signature verified')
callback()
})
}
exports.generateKeys = (state) => {
exports.generateKeys = (state, callback) => {
log('2.2. keys')
state.shared.secret = state.shared.generate(state.exchange.in.epubkey)
waterfall([
(cb) => state.shared.generate(state.exchange.in.epubkey, cb),
(secret, cb) => {
state.shared.secret = secret
const keys = crypto.keyStretcher(
state.protocols.local.cipherT,
state.protocols.local.hashT,
state.shared.secret
)
crypto.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'))
}
// 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')
log('2.3. mac + cipher')
support.makeMacAndCipher(state.protocols.local)
support.makeMacAndCipher(state.protocols.remote)
parallel([
(cb) => support.makeMacAndCipher(state.protocols.local, cb),
(cb) => support.makeMacAndCipher(state.protocols.remote, cb)
], cb)
}
], callback)
}
exports.verifyNonce = (state, n2) => {

View File

@ -1,6 +1,7 @@
'use strict'
const debug = require('debug')
const waterfall = require('async/waterfall')
const support = require('../support')
const crypto = require('./crypto')
@ -14,21 +15,22 @@ module.exports = function exchange (state, cb) {
log('2. exchange - start')
log('2. exchange - writing exchange')
support.write(state, crypto.createExchange(state))
support.read(state.shake, (err, msg) => {
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 cb(err)
}
log('2. exchange - reading exchange')
try {
crypto.verify(state, msg)
crypto.generateKeys(state)
} catch (err) {
return cb(err)
}
log('2. exchange - finish')
cb()
})

View File

@ -17,7 +17,11 @@ module.exports = function finish (state, cb) {
const proto = state.protocols
const stream = state.shake.rest()
const shake = handshake({timeout: state.timeout})
const shake = handshake({timeout: state.timeout}, (err) => {
if (err) {
throw err
}
})
pull(
stream,

View File

@ -1,6 +1,6 @@
'use strict'
const series = require('run-series')
const series = require('async/series')
const propose = require('./propose')
const exchange = require('./exchange')

View File

@ -1,6 +1,7 @@
'use strict'
const debug = require('debug')
const waterfall = require('async/waterfall')
const support = require('../support')
const crypto = require('./crypto')
@ -15,22 +16,20 @@ module.exports = function propose (state, cb) {
log('1. propose - writing proposal')
support.write(state, crypto.createProposal(state))
support.read(state.shake, (err, msg) => {
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 cb(err)
}
log('1. propose - reading proposal', msg)
try {
crypto.identify(state, msg)
crypto.selectProtocols(state)
} catch (err) {
return cb(err)
}
log('1. propose - finish')
cb()
})
}

View File

@ -1,12 +0,0 @@
message Propose {
optional bytes rand = 1;
optional bytes pubkey = 2;
optional string exchanges = 3;
optional string ciphers = 4;
optional string hashes = 5;
}
message Exchange {
optional bytes epubkey = 1;
optional bytes signature = 2;
}

View File

@ -0,0 +1,14 @@
'use strict'
module.exports = `message Propose {
optional bytes rand = 1;
optional bytes pubkey = 2;
optional string exchanges = 3;
optional string ciphers = 4;
optional string hashes = 5;
}
message Exchange {
optional bytes epubkey = 1;
optional bytes signature = 2;
}`

View File

@ -8,7 +8,7 @@ const State = require('./state')
module.exports = {
tag: '/secio/1.0.0',
encrypt (local, key, insecure) {
encrypt (local, key, insecure, callback) {
if (!local) {
throw new Error('no local id provided')
}
@ -21,7 +21,13 @@ module.exports = {
throw new Error('no insecure stream provided')
}
const state = new State(local, key)
if (!callback) {
callback = (err) => {
throw err
}
}
const state = new State(local, key, callback)
pull(
insecure,

View File

@ -5,6 +5,11 @@ const deferred = require('pull-defer')
class State {
constructor (id, key, timeout, cb) {
if (typeof timeout === 'function') {
cb = timeout
timeout = undefined
}
this.setup()
this.id.local = id
this.key.local = key

View File

@ -1,9 +1,10 @@
'use strict'
const mh = require('multihashing')
const forge = require('node-forge')
const mh = require('multihashing-async')
const lp = require('pull-length-prefixed')
const pull = require('pull-stream')
const crypto = require('libp2p-crypto')
const parallel = require('async/parallel')
exports.exchanges = [
'P-256',
@ -47,78 +48,73 @@ exports.theBest = (order, p1, p2) => {
throw new Error('No algorithms in common!')
}
exports.makeMacAndCipher = (target) => {
target.mac = makeMac(target.hashT, target.keys.macKey)
target.cipher = makeCipher(target.cipherT, target.keys.iv, target.keys.cipherKey)
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()
})
}
const hashMap = {
SHA1: 'sha1',
SHA256: 'sha256',
// workaround for https://github.com/digitalbazaar/forge/issues/401
SHA512: forge.md.sha512.create()
function makeMac (hash, key, callback) {
crypto.hmac.create(hash, key, callback)
}
const toForgeBuffer = exports.toForgeBuffer = (buf) => (
forge.util.createBuffer(buf.toString('binary'))
)
function makeMac (hashType, key) {
const hash = hashMap[hashType]
if (!hash) {
throw new Error(`unsupported hash type: ${hashType}`)
}
const mac = forge.hmac.create()
mac.start(hash, toForgeBuffer(key))
return mac
}
function makeCipher (cipherType, iv, key) {
function makeCipher (cipherType, iv, key, callback) {
if (cipherType === 'AES-128' || cipherType === 'AES-256') {
// aes in counter (CTR) mode because that is what
// is used in go (cipher.NewCTR)
const cipher = forge.cipher.createCipher('AES-CTR', toForgeBuffer(key))
cipher.start({iv: toForgeBuffer(iv)})
return cipher
return crypto.aes.create(key, iv, callback)
}
// TODO: Blowfish is not supported in node-forge, figure out if
// it's needed and if so find a library for it.
throw new Error(`unrecognized cipher type: ${cipherType}`)
// TODO: figure out if Blowfish is needed and if so find a library for it.
callback(new Error(`unrecognized cipher type: ${cipherType}`))
}
exports.randomBytes = (nonceSize) => {
return new Buffer(forge.random.getBytesSync(nonceSize), 'binary')
return Buffer.from(crypto.webcrypto.getRandomValues(new Uint8Array(nonceSize)))
}
exports.selectBest = (local, remote) => {
const oh1 = exports.digest(Buffer.concat([
exports.selectBest = (local, remote, cb) => {
exports.digest(Buffer.concat([
remote.pubKeyBytes,
local.nonce
]))
const oh2 = exports.digest(Buffer.concat([
local.pubKeyBytes,
remote.nonce
]))
const order = Buffer.compare(oh1, oh2)
]), (err, oh1) => {
if (err) {
return cb(err)
}
if (order === 0) {
throw new Error('you are trying to talk to yourself')
}
exports.digest(Buffer.concat([
local.pubKeyBytes,
remote.nonce
]), (err, oh2) => {
if (err) {
return cb(err)
}
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
}
const order = Buffer.compare(oh1, oh2)
if (order === 0) {
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
})
})
})
}
exports.digest = (buf) => {
return mh.digest(buf, 'sha2-256', buf.length)
exports.digest = (buf, cb) => {
mh.digest(buf, 'sha2-256', buf.length, cb)
}
exports.write = function write (state, msg, cb) {

View File

@ -1,3 +1,4 @@
/* eslint max-nested-callbacks: ["error", 8] */
/* eslint-env mocha */
'use strict'
@ -5,8 +6,8 @@ const pair = require('pull-pair/duplex')
const expect = require('chai').expect
const PeerId = require('peer-id')
const crypto = require('libp2p-crypto')
const parallel = require('run-parallel')
const series = require('run-series')
const parallel = require('async/parallel')
const series = require('async/series')
const ms = require('multistream-select')
const pull = require('pull-stream')
const Listener = ms.Listener
@ -21,23 +22,31 @@ describe('libp2p-secio', () => {
it('upgrades a connection', (done) => {
const p = pair()
createSession(p[0], (err, local) => {
if (err) {
return done(err)
}
const local = createSession(p[0])
const remote = createSession(p[1])
createSession(p[1], (err, remote) => {
if (err) {
return done(err)
}
pull(
pull.values(['hello world']),
local
)
pull(
pull.values([new Buffer('hello world')]),
local
)
pull(
remote,
pull.collect((err, chunks) => {
expect(err).to.not.exist
expect(chunks).to.be.eql([new Buffer('hello world')])
done()
pull(
remote,
pull.collect((err, chunks) => {
expect(err).to.not.exist
expect(chunks).to.be.eql([new Buffer('hello world')])
done()
})
)
})
)
})
})
it('works over multistream', (done) => {
@ -45,8 +54,7 @@ describe('libp2p-secio', () => {
const listener = new Listener()
const dialer = new Dialer()
let local
let remote
series([
(cb) => parallel([
(cb) => listener.handle(p[0], cb),
@ -54,35 +62,58 @@ describe('libp2p-secio', () => {
], cb),
(cb) => {
listener.addHandler('/banana/1.0.0', (conn) => {
local = createSession(conn)
pull(
local,
pull.collect((err, chunks) => {
expect(err).to.not.exist
expect(chunks).to.be.eql([new Buffer('hello world')])
done()
})
)
createSession(conn, (err, local) => {
if (err) {
return done(err)
}
pull(
local,
pull.collect((err, chunks) => {
expect(err).to.not.exist
expect(chunks).to.be.eql([new Buffer('hello world')])
done()
})
)
})
})
cb()
},
(cb) => dialer.select('/banana/1.0.0', (err, conn) => {
remote = createSession(conn)
pull(
pull.values(['hello world']),
remote
)
cb(err)
if (err) {
return cb(err)
}
createSession(conn, (err, remote) => {
if (err) {
return cb(err)
}
pull(
pull.values([new Buffer('hello world')]),
remote
)
cb()
})
})
], (err) => {
if (err) throw err
if (err) {
throw err
}
})
})
})
function createSession (insecure) {
const key = crypto.generateKeyPair('RSA', 2048)
const id = PeerId.createFromPrivKey(key.bytes)
function createSession (insecure, cb) {
crypto.generateKeyPair('RSA', 2048, (err, key) => {
if (err) {
return cb(err)
}
return secio.encrypt(id, key, insecure)
key.public.hash((err, digest) => {
if (err) {
return cb(err)
}
cb(null, secio.encrypt(new PeerId(digest, key), key, insecure))
})
})
}