mirror of
https://github.com/fluencelabs/js-libp2p-secio
synced 2025-05-07 20:32:14 +00:00
More implementation
This commit is contained in:
parent
419d7e9dbe
commit
9d3437e8d8
75
src/handshake/exchange.js
Normal file
75
src/handshake/exchange.js
Normal 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
10
src/handshake/finish.js
Normal 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
39
src/handshake/index.js
Normal 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)
|
||||
}
|
@ -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')
|
||||
}
|
@ -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
54
test/support.spec.js
Normal 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/
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
Loading…
x
Reference in New Issue
Block a user