js-libp2p-secio/src/index.js
2016-05-23 17:33:16 +02:00

115 lines
2.5 KiB
JavaScript

'use strict'
const duplexify = require('duplexify')
const lpstream = require('length-prefixed-stream')
const PassThrough = require('readable-stream').PassThrough
const handshake = require('./handshake')
exports.SecureSession = class SecureSession {
constructor (local, key, insecure) {
this.localKey = key
this.localPeer = local
this.sharedSecret = null
this.local = {}
this.remote = {}
this.proposal = {}
this.insecure = insecure
this.secure = null
const e = lpstream.encode()
const d = lpstream.decode()
this.insecureLp = duplexify(e, d)
e.pipe(this.insecure)
this.insecure.pipe(d)
if (!this.localPeer) {
throw new Error('no local id provided')
}
if (!this.localKey) {
throw new Error('no local private key provided')
}
// Enable when implemented in js-peer-id
// if (!this.localPeer.matchesPrivateKey(this.localKey)) {
// throw new Error('peer.ID does not match privateKey')
// }
if (!insecure) {
throw new Error('no insecure stream provided')
}
}
secureStream () {
let handshaked = false
const reader = new PassThrough()
const writer = new PassThrough()
const dp = duplexify(writer, reader)
const originalRead = reader.read.bind(reader)
const originalWrite = writer.write.bind(writer)
const doHandshake = () => {
if (handshaked) return
handshaked = true
// Restore methods to avoid overhead
reader.read = originalRead
writer.write = originalWrite
this.handshake((err) => {
if (err) return dp.emit('error', err)
// Pipe things together
writer.pipe(this.secure)
this.secure.pipe(reader)
dp.uncork()
})
}
// patch to detect first read
reader.read = (size) => {
doHandshake()
originalRead(size)
}
// patch to detect first write
writer.write = (chunk, encoding, callback) => {
doHandshake()
originalWrite(chunk, encoding, callback)
}
dp.cork()
return dp
}
handshake (cb) {
// TODO: figure out how to best handle the handshake timeout
if (this._handshakeLock) {
return cb(new Error('handshake already in progress'))
}
this._handshakeLock = true
const finish = (err) => {
this._handshakeLock = false
cb(err)
}
if (this._handshakeDone) {
return finish()
}
handshake(this, (err) => {
if (err) {
return finish(err)
}
this._handshakeDone = true
finish()
})
}
}