From 9dc028d8fb058354d7580ba121233297d8a90c64 Mon Sep 17 00:00:00 2001 From: emad-salah Date: Wed, 27 May 2020 00:26:02 +0000 Subject: [PATCH] Encrypted LND Sockets --- src/sockets.js | 188 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 145 insertions(+), 43 deletions(-) diff --git a/src/sockets.js b/src/sockets.js index 476da12f..7476e780 100644 --- a/src/sockets.js +++ b/src/sockets.js @@ -1,67 +1,169 @@ +/** @prettier */ // app/sockets.js -const logger = require("winston"); -const LightningServices = require("../utils/lightningServices"); +const logger = require('winston') +const Encryption = require('../utils/encryptionStore') +const LightningServices = require('../utils/lightningServices') module.exports = ( /** @type {import('socket.io').Server} */ io ) => { - const Mediator = require("../services/gunDB/Mediator/index.js"); - + const Mediator = require('../services/gunDB/Mediator/index.js') + + // This should be used for encrypting and emitting your data + const emitEncryptedEvent = ({ eventName, data, socket }) => { + try { + if (Encryption.isNonEncrypted(eventName)) { + return socket.emit(eventName, data) + } + + const deviceId = socket.handshake.query['x-shockwallet-device-id'] + const authorized = Encryption.isAuthorizedDevice({ deviceId }) + + if (!deviceId) { + throw { + field: 'deviceId', + message: 'Please specify a device ID' + } + } + + if (!authorized) { + throw { + field: 'deviceId', + message: 'Please exchange keys with the API before using the socket' + } + } + + const encryptedMessage = Encryption.encryptMessage({ + message: data, + deviceId + }) + + return socket.emit(eventName, encryptedMessage) + } catch (err) { + logger.error( + `[SOCKET] An error has occurred while encrypting an event (${eventName}):`, + err + ) + + return socket.emit('encryption:error', err) + } + } + + const parseJSON = data => { + try { + if (typeof data === 'string') { + return JSON.parse(data) + } + + return data + } catch (err) { + return data + } + } + + const decryptEvent = ({ eventName, data, socket }) => { + try { + const deviceId = socket.handshake.query['x-shockwallet-device-id'] + if (Encryption.isNonEncrypted(eventName)) { + return data + } + + if (!data) { + return data + } + + const parsedData = parseJSON(data) + + if (!deviceId) { + throw { + field: 'deviceId', + message: 'Please specify a device ID' + } + } + + if (!Encryption.isAuthorizedDevice({ deviceId })) { + throw { + field: 'deviceId', + message: 'Please exchange keys with the API before using the socket' + } + } + + const decryptedKey = Encryption.decryptKey({ + deviceId, + message: parsedData.encryptedKey + }) + const decryptedMessage = Encryption.decryptMessage({ + message: parsedData.encryptedData, + key: decryptedKey, + iv: parsedData.iv + }) + const decryptedData = JSON.parse(decryptedMessage) + return decryptedData + } catch (err) { + logger.error( + `[SOCKET] An error has occurred while decrypting an event (${eventName}):`, + err + ) + + return socket.emit('encryption:error', err) + } + } + const onNewInvoice = socket => { - const { lightning } = LightningServices.services; - logger.warn("Subscribing to invoices socket...") - const stream = lightning.subscribeInvoices({}); - stream.on("data", data => { - logger.info("[SOCKET] New invoice data:", data); - socket.emit("invoice:new", data) + const { lightning } = LightningServices.services + logger.warn('Subscribing to invoices socket...') + const stream = lightning.subscribeInvoices({}) + stream.on('data', data => { + logger.info('[SOCKET] New invoice data:', data) + emitEncryptedEvent({ eventName: 'invoice:new', data, socket }) }) - stream.on("end", () => { - logger.info("New invoice stream ended, starting a new one...") - onNewInvoice(socket); + stream.on('end', () => { + logger.info('New invoice stream ended, starting a new one...') + onNewInvoice(socket) }) - stream.on("error", err => { - logger.error("New invoice stream error:", err); + stream.on('error', err => { + logger.error('New invoice stream error:', err) }) - stream.on("status", status => { - logger.error("New invoice stream status:", status); + stream.on('status', status => { + logger.warn('New invoice stream status:', status) if (status.code === 14) { - onNewInvoice(socket); + onNewInvoice(socket) } }) } const onNewTransaction = socket => { - const { lightning } = LightningServices.services; - const stream = lightning.subscribeTransactions({}); - logger.warn("Subscribing to transactions socket...") - stream.on("data", data => { - logger.info("[SOCKET] New transaction data:", data); - socket.emit("transaction:new", data) + const { lightning } = LightningServices.services + const stream = lightning.subscribeTransactions({}) + logger.warn('Subscribing to transactions socket...') + stream.on('data', data => { + logger.info('[SOCKET] New transaction data:', data) + emitEncryptedEvent({ eventName: 'transaction:new', data, socket }) }) - stream.on("end", () => { - logger.info("New invoice stream ended, starting a new one...") - onNewTransaction(socket); + stream.on('end', () => { + logger.info('New invoice stream ended, starting a new one...') + onNewTransaction(socket) }) - stream.on("error", err => { - logger.error("New invoice stream error:", err); + stream.on('error', err => { + logger.error('New invoice stream error:', err) }) - stream.on("status", status => { - logger.error("New invoice stream status:", status); + stream.on('status', status => { + logger.error('New invoice stream status:', status) if (status.code === 14) { - onNewTransaction(socket); + onNewTransaction(socket) } }) } - io.on("connection", socket => { + io.on('connection', socket => { logger.info(`io.onconnection`) - logger.info("socket.handshake", socket.handshake); + logger.info('socket.handshake', socket.handshake) /** printing out the client who joined */ - logger.info("New socket client connected (id=" + socket.id + ")."); + logger.info('New socket client connected (id=' + socket.id + ').') const isOneTimeUseSocket = !!socket.handshake.query.IS_GUN_AUTH const isLNDSocket = !!socket.handshake.query.IS_LND_SOCKET @@ -92,18 +194,18 @@ module.exports = ( logger.info('New socket is NOT one time use') // this is where we create the websocket connection // with the GunDB service. - Mediator.createMediator(socket); + Mediator.createMediator(socket) if (isLNDSocket) { - onNewInvoice(socket); - onNewTransaction(socket); + onNewInvoice(socket) + onNewTransaction(socket) } /** listening if client has disconnected */ - socket.on("disconnect", () => { - logger.info("client disconnected (id=" + socket.id + ")."); - }); + socket.on('disconnect', () => { + logger.info('client disconnected (id=' + socket.id + ').') + }) } }) - return io; -}; + return io +}