js-libp2p-secio/src/support.js
2016-12-13 14:01:33 +01:00

140 lines
2.9 KiB
JavaScript

'use strict'
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',
'P-384',
'P-521'
]
exports.ciphers = [
'AES-256',
'AES-128'
]
exports.hashes = [
'SHA256',
'SHA512'
]
// 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!')
}
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()
})
}
function makeMac (hash, key, callback) {
crypto.hmac.create(hash, key, callback)
}
function makeCipher (cipherType, iv, key, callback) {
if (cipherType === 'AES-128' || cipherType === 'AES-256') {
return crypto.aes.create(key, iv, callback)
}
// 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 Buffer.from(crypto.webcrypto.getRandomValues(new Uint8Array(nonceSize)))
}
exports.selectBest = (local, remote, cb) => {
exports.digest(Buffer.concat([
remote.pubKeyBytes,
local.nonce
]), (err, oh1) => {
if (err) {
return cb(err)
}
exports.digest(Buffer.concat([
local.pubKeyBytes,
remote.nonce
]), (err, oh2) => {
if (err) {
return cb(err)
}
const order = Buffer.compare(oh1, oh2)
if (order === 0) {
return 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, cb) => {
mh.digest(buf, 'sha2-256', buf.length, cb)
}
exports.write = function write (state, msg, cb) {
cb = cb || (() => {})
pull(
pull.values([
msg
]),
lp.encode({fixed: true, bytes: 4}),
pull.collect((err, res) => {
if (err) {
return cb(err)
}
state.shake.write(res[0])
cb()
})
)
}
exports.read = function read (reader, cb) {
lp.decodeFromReader(reader, {fixed: true, bytes: 4}, cb)
}