More implementation

This commit is contained in:
Friedel Ziegelmayer 2016-05-22 01:03:53 +02:00
parent 419d7e9dbe
commit 9d3437e8d8
6 changed files with 206 additions and 42 deletions

75
src/handshake/exchange.js Normal file
View File

@ -0,0 +1,75 @@
'use strict'
const crypto = require('libp2p-crypto')
const debug = require('debug')
const log = debug('libp2p:secio:handshake')
log.error = debug('libp2p:secio:handshake:error')
module.exports = function exchange (session) {
log('2. exchange - start')
const eResult = crypto.generateEphemeralKeyPair(session.local.curveT)
session.local.ephemeralPubKey = eResult.key
const genSharedKey = eResult.genSharedKey
// Gather corpus to sign.
const selectionOut = Buffer.concat([
proposeOutBytes,
proposeInBytes,
session.local.ephemeralPubKey
])
const exchangeOut = pbm.Exchange({
epubkey: session.local.ephemeralPubKey,
signature: session.localKey.sign(selectionOut)
})
// TODO: write exchangeOut
// TODO: read exchangeIn
const exchangeIn // = ...read
log('2.1. verify')
session.remote.ephemeralPubKey = exchangeIn.epubkey
const selectionIn = Buffer.concat([
proposeInBytes,
proposeOutBytes,
session.remote.ephemeralPubKey
])
const sigOk = session.remote.permanentPubKey.verify(selectionIn, exchangeIn.signature)
if (!sigOk) {
throw new Error('Bad signature')
}
log('2.1. verify - signature verified')
log('2.2. keys')
session.sharedSecret = genSharedKey(exchangeIn.epubkey)
const keys = crypto.keyStretcher(session.local.cipherT, session.local.hashT, session.sharedSecret)
// use random nonces to decide order.
if (order > 0) {
session.local.keys = keys.k1
session.remote.keys = keys.k2
} else if (order < 0) {
// swap
session.local.keys = keys.k2
session.remote.keys = keys.k1
} else {
// we should've bailed before this. but if not, bail here.
throw new Error('you are trying to talk to yourself')
}
log('2.2. keys - shared: %s\n\tlocal: %s\n\tremote: %s', session.sharedSecret, session.local.keys, session.remote.keys)
log('2.3. mac + cipher')
// TODO: generate mac and cipher for local and remote
log('2. exchange - finish')
}

10
src/handshake/finish.js Normal file
View File

@ -0,0 +1,10 @@
'use strict'
const debug = require('debug')
const log = debug('libp2p:secio:handshake')
log.error = debug('libp2p:secio:handshake:error')
function finish () {
log('3. finish - start')
log('3. finish - finish')
}

39
src/handshake/index.js Normal file
View File

@ -0,0 +1,39 @@
'use strict'
const crypto = require('libp2p-crypto')
const debug = require('debug')
const protobuf = require('protocol-buffers')
const path = require('path')
const fs = require('fs')
const log = debug('libp2p:secio:handshake')
log.error = debug('libp2p:secio:handshake:error')
const pbm = protobuf(fs.readFileSync(path.join(__dirname, '../secio.proto')))
const propose = require('./propose')
const exchange = require('./exchange')
const finish = require('./finish')
// HandshakeTimeout governs how long the handshake will be allowed to take place for.
// Making this number large means there could be many bogus connections waiting to
// timeout in flight. Typical handshakes take ~3RTTs, so it should be completed within
// seconds across a typical planet in the solar system.
const handshakeTimeout = 30 * 1000
// Performs initial communication over insecure channel to share
// keys, IDs, and initiate communication, assigning all necessary params.
function run (session) {
// step 1. Propose
// -- propose cipher suite + send pubkeys + nonce
propose(session)
// step 2. Exchange
// -- exchange (signed) ephemeral keys. verify signatures.
exchange(session)
// step 3. Finish
// -- send expected message to verify encryption works (send local nonce)
finish(session)
}

View File

@ -1,48 +1,23 @@
'use strict'
const forge = require('node-forge')
const crypto = require('libp2p-crypto')
const debug = require('debug')
const protobuf = require('protocol-buffers')
const path = require('path')
const fs = require('fs')
const PeerId = require('peer-id')
const mh = require('multihashing')
const log = debug('libp2p:secio:handshake')
log.error = debug('libp2p:secio:handshake:error')
const pbm = protobuf(fs.readFileSync(path.join(__dirname, '../secio.proto')))
const support = require('./support')
// HandshakeTimeout governs how long the handshake will be allowed to take place for.
// Making this number large means there could be many bogus connections waiting to
// timeout in flight. Typical handshakes take ~3RTTs, so it should be completed within
// seconds across a typical planet in the solar system.
const HandshakeTimeout = 30 * 1000
// nonceSize is the size of our nonces (in bytes)
const nonceSize = 16
// Performs initial communication over insecure channel to share
// keys, IDs, and initiate communication, assigning all necessary params.
function run () {
const pbm = protobuf(fs.readFileSync(path.join(__dirname, '../secio.proto')))
const support = require('./support')
// step 1. Propose
// -- propose cipher suite + send pubkeys + nonce
propose()
// step 2. Exchange
// -- exchange (signed) ephemeral keys. verify signatures.
exchange()
// step 3. Finish
// -- send expected message to verify encryption works (send local nonce)
finish()
}
function propose (session) {
module.exports = function propose (session) {
log('1. propose - start')
const nonceOut = forge.random.getBytesSync(nonceSize)
@ -81,9 +56,9 @@ function propose (session) {
// we use the same params for both directions (must choose same curve)
// WARNING: if they dont SelectBest the same way, this won't work...
session.remote.curveT = session.local.curveT
session.remote.cipherT = session.local.cipherT
session.remote.hashT = session.local.hashT
session.remote.curveT = session.local.curveT
session.remote.cipherT = session.local.cipherT
session.remote.hashT = session.local.hashT
log('1. propose - finish')
}
@ -124,13 +99,3 @@ function selectBest (local, remote) {
hashT: support.theBest(order, local.hashes, remote.hashes)
}
}
function exchange () {
log('2. exchnage - start')
log('2. exchange - finish')
}
function finish () {
log('3. finish - start')
log('3. finish - finish')
}

View File

@ -19,5 +19,26 @@ exports.hashes = [
// Determines which algorithm to use. Note: f(a, b) = f(b, a)
exports.theBest = (order, p1, p2) => {
let first
let second
if (order < 0) {
first = p2
second = p1
} else if (order > 0) {
first = p1
second = p2
} else {
return p1[0]
}
for (let firstCandidate of first) {
for (let secondCandidate of second) {
if (firstCandidate === secondCandidate) {
return firstCandidate
}
}
}
throw new Error('No algorithms in common!')
}

54
test/support.spec.js Normal file
View File

@ -0,0 +1,54 @@
/* eslint-env mocha */
'use strict'
const expect = require('chai').expect
const support = require('../src/support')
describe('support', () => {
describe('theBest', () => {
it('returns the first matching element, preferring p1', () => {
const order = 1
const p1 = ['hello', 'world']
const p2 = ['world', 'hello']
expect(
support.theBest(order, p1, p2)
).to.be.eql(
'hello'
)
})
it('returns the first matching element, preferring p2', () => {
const order = -1
const p1 = ['hello', 'world']
const p2 = ['world', 'hello']
expect(
support.theBest(order, p1, p2)
).to.be.eql(
'world'
)
})
it('returns the first element if the same', () => {
const order = 0
const p1 = ['hello', 'world']
const p2 = p1
expect(
support.theBest(order, p1, p2)
).to.be.eql(
'hello'
)
})
it('throws if no matching element was found', () => {
expect(
() => support.theBest(1, ['hello'], ['world'])
).to.throw(
/No algorithms in common/
)
})
})
})