2019-08-16 17:30:03 +02:00
|
|
|
'use strict'
|
|
|
|
|
|
|
|
const debug = require('debug')
|
|
|
|
const Errors = require('./errors')
|
|
|
|
const xsalsa20 = require('xsalsa20')
|
|
|
|
const KEY_LENGTH = require('./key-generator').KEY_LENGTH
|
2020-08-24 11:58:02 +01:00
|
|
|
const uint8ArrayFromString = require('uint8arrays/from-string')
|
|
|
|
const uint8ArrayToString = require('uint8arrays/to-string')
|
2019-08-16 17:30:03 +02:00
|
|
|
|
|
|
|
const log = debug('libp2p:pnet')
|
|
|
|
log.trace = debug('libp2p:pnet:trace')
|
2019-11-04 14:05:58 +01:00
|
|
|
log.error = debug('libp2p:pnet:err')
|
2019-08-16 17:30:03 +02:00
|
|
|
|
|
|
|
/**
|
2019-11-04 14:05:58 +01:00
|
|
|
* Creates a stream iterable to encrypt messages in a private network
|
2019-08-16 17:30:03 +02:00
|
|
|
*
|
2020-08-24 11:58:02 +01:00
|
|
|
* @param {Uint8Array} nonce The nonce to use in encryption
|
|
|
|
* @param {Uint8Array} psk The private shared key to use in encryption
|
2019-11-04 14:05:58 +01:00
|
|
|
* @returns {*} a through iterable
|
2019-08-16 17:30:03 +02:00
|
|
|
*/
|
|
|
|
module.exports.createBoxStream = (nonce, psk) => {
|
|
|
|
const xor = xsalsa20(nonce, psk)
|
2019-11-04 14:05:58 +01:00
|
|
|
return (source) => (async function * () {
|
|
|
|
for await (const chunk of source) {
|
2020-08-24 11:58:02 +01:00
|
|
|
yield Uint8Array.from(xor.update(chunk.slice()))
|
2019-11-04 14:05:58 +01:00
|
|
|
}
|
|
|
|
})()
|
2019-08-16 17:30:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-11-04 14:05:58 +01:00
|
|
|
* Creates a stream iterable to decrypt messages in a private network
|
2019-08-16 17:30:03 +02:00
|
|
|
*
|
2020-08-24 11:58:02 +01:00
|
|
|
* @param {Uint8Array} nonce The nonce of the remote peer
|
|
|
|
* @param {Uint8Array} psk The private shared key to use in decryption
|
2019-11-04 14:05:58 +01:00
|
|
|
* @returns {*} a through iterable
|
2019-08-16 17:30:03 +02:00
|
|
|
*/
|
2019-11-04 14:05:58 +01:00
|
|
|
module.exports.createUnboxStream = (nonce, psk) => {
|
|
|
|
return (source) => (async function * () {
|
|
|
|
const xor = xsalsa20(nonce, psk)
|
|
|
|
log.trace('Decryption enabled')
|
2019-08-16 17:30:03 +02:00
|
|
|
|
2019-11-04 14:05:58 +01:00
|
|
|
for await (const chunk of source) {
|
2020-08-24 11:58:02 +01:00
|
|
|
yield Uint8Array.from(xor.update(chunk.slice()))
|
2019-11-04 14:05:58 +01:00
|
|
|
}
|
|
|
|
})()
|
2019-08-16 17:30:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-08-24 11:58:02 +01:00
|
|
|
* Decode the version 1 psk from the given Uint8Array
|
2019-08-16 17:30:03 +02:00
|
|
|
*
|
2020-08-24 11:58:02 +01:00
|
|
|
* @param {Uint8Array} pskBuffer
|
2019-08-16 17:30:03 +02:00
|
|
|
* @throws {INVALID_PSK}
|
|
|
|
* @returns {Object} The PSK metadata (tag, codecName, psk)
|
|
|
|
*/
|
|
|
|
module.exports.decodeV1PSK = (pskBuffer) => {
|
|
|
|
try {
|
|
|
|
// This should pull from multibase/multicodec to allow for
|
|
|
|
// more encoding flexibility. Ideally we'd consume the codecs
|
|
|
|
// from the buffer line by line to evaluate the next line
|
2019-11-04 14:05:58 +01:00
|
|
|
// programmatically instead of making assumptions about the
|
2019-08-16 17:30:03 +02:00
|
|
|
// encodings of each line.
|
2020-08-24 11:58:02 +01:00
|
|
|
const metadata = uint8ArrayToString(pskBuffer).split(/(?:\r\n|\r|\n)/g)
|
2019-08-16 17:30:03 +02:00
|
|
|
const pskTag = metadata.shift()
|
|
|
|
const codec = metadata.shift()
|
2020-08-24 11:58:02 +01:00
|
|
|
const psk = uint8ArrayFromString(metadata.shift(), 'base16')
|
2019-08-16 17:30:03 +02:00
|
|
|
|
|
|
|
if (psk.byteLength !== KEY_LENGTH) {
|
|
|
|
throw new Error(Errors.INVALID_PSK)
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
tag: pskTag,
|
|
|
|
codecName: codec,
|
|
|
|
psk: psk
|
|
|
|
}
|
|
|
|
} catch (err) {
|
2019-11-04 14:05:58 +01:00
|
|
|
log.error(err)
|
2019-08-16 17:30:03 +02:00
|
|
|
throw new Error(Errors.INVALID_PSK)
|
|
|
|
}
|
|
|
|
}
|