mirror of
https://github.com/fluencelabs/js-libp2p-secio
synced 2025-05-12 06:17:12 +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'
|
'use strict'
|
||||||
|
|
||||||
const forge = require('node-forge')
|
const forge = require('node-forge')
|
||||||
const crypto = require('libp2p-crypto')
|
|
||||||
const debug = require('debug')
|
const debug = require('debug')
|
||||||
const protobuf = require('protocol-buffers')
|
const protobuf = require('protocol-buffers')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const PeerId = require('peer-id')
|
const PeerId = require('peer-id')
|
||||||
|
const mh = require('multihashing')
|
||||||
|
|
||||||
const log = debug('libp2p:secio:handshake')
|
const log = debug('libp2p:secio:handshake')
|
||||||
log.error = debug('libp2p:secio:handshake:error')
|
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)
|
// nonceSize is the size of our nonces (in bytes)
|
||||||
const nonceSize = 16
|
const nonceSize = 16
|
||||||
|
|
||||||
// Performs initial communication over insecure channel to share
|
const pbm = protobuf(fs.readFileSync(path.join(__dirname, '../secio.proto')))
|
||||||
// keys, IDs, and initiate communication, assigning all necessary params.
|
const support = require('./support')
|
||||||
function run () {
|
|
||||||
|
|
||||||
// step 1. Propose
|
module.exports = function propose (session) {
|
||||||
// -- 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) {
|
|
||||||
log('1. propose - start')
|
log('1. propose - start')
|
||||||
|
|
||||||
const nonceOut = forge.random.getBytesSync(nonceSize)
|
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)
|
// we use the same params for both directions (must choose same curve)
|
||||||
// WARNING: if they dont SelectBest the same way, this won't work...
|
// WARNING: if they dont SelectBest the same way, this won't work...
|
||||||
session.remote.curveT = session.local.curveT
|
session.remote.curveT = session.local.curveT
|
||||||
session.remote.cipherT = session.local.cipherT
|
session.remote.cipherT = session.local.cipherT
|
||||||
session.remote.hashT = session.local.hashT
|
session.remote.hashT = session.local.hashT
|
||||||
|
|
||||||
log('1. propose - finish')
|
log('1. propose - finish')
|
||||||
}
|
}
|
||||||
@ -124,13 +99,3 @@ function selectBest (local, remote) {
|
|||||||
hashT: support.theBest(order, local.hashes, remote.hashes)
|
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)
|
// Determines which algorithm to use. Note: f(a, b) = f(b, a)
|
||||||
exports.theBest = (order, p1, p2) => {
|
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