diff --git a/src/routes.js b/src/routes.js index 143492ab..b7021538 100644 --- a/src/routes.js +++ b/src/routes.js @@ -5,9 +5,10 @@ */ "use strict"; -const Http = require("axios"); +const Axios = require("axios"); const Crypto = require("crypto"); const logger = require("winston"); +const httpsAgent = require("https"); const responseTime = require("response-time"); const getListPage = require("../utils/paginate"); const auth = require("../services/auth/auth"); @@ -19,12 +20,18 @@ const { unprotectedRoutes } = require("../utils/protectedRoutes"); const DEFAULT_MAX_NUM_ROUTES_TO_QUERY = 10; // module.exports = (app) => { -module.exports = ( +module.exports = async ( app, config, mySocketsEvents, - { serverPort } + { serverPort, CA, CA_KEY, usetls } ) => { + const Http = Axios.create({ + httpsAgent: new httpsAgent.Agent({ + ca: await FS.readFile(CA) + }) + }) + const sanitizeLNDError = (message = "") => message.toLowerCase().includes("unknown") ? message @@ -81,7 +88,7 @@ module.exports = ( const serviceStatus = await getAvailableService(); const LNDStatus = serviceStatus; try { - const APIHealth = await Http.get(`http://localhost:${serverPort}/ping`); + const APIHealth = await Http.get(`${usetls ? 'https' : 'http'}://localhost:${serverPort}/ping`); const APIStatus = { message: APIHealth.data, responseTime: APIHealth.headers["x-response-time"], @@ -92,6 +99,7 @@ module.exports = ( APIStatus }; } catch (err) { + console.error(err); const APIStatus = { message: err.response.data, responseTime: err.response.headers["x-response-time"], @@ -259,6 +267,10 @@ module.exports = ( res.json({ msg: "OK" }); }); + app.post("/api/security/exchangeKeys", (req, res) => { + + }) + app.get("/api/lnd/wallet/status", async (req, res) => { try { const walletStatus = await walletExists(); diff --git a/src/server.js b/src/server.js index b52f0210..3743f2cc 100644 --- a/src/server.js +++ b/src/server.js @@ -125,12 +125,15 @@ const server = program => { .send({ status: 500, errorMessage: "internal error" }); }); + const CA = LightningServices.servicesConfig.lndCertPath + const CA_KEY = CA.replace("cert", "key") + const createServer = async () => { try { - if (program.usetls) { + if (LightningServices.servicesConfig.lndCertPath && program.usetls) { const [key, cert] = await Promise.all([ - FS.readFile(program.usetls + "/key.pem"), - FS.readFile(program.usetls + "/cert.pem") + FS.readFile(CA_KEY), + FS.readFile(CA) ]); const httpsServer = Https.createServer({ key, cert }, app); @@ -141,7 +144,10 @@ const server = program => { return httpServer; } catch (err) { logger.error(err.message); - throw err; + logger.error("An error has occurred while finding an LND cert to use to open an HTTPS server") + logger.warn("Falling back to opening an HTTP server...") + const httpServer = Http.Server(app); + return httpServer } }; @@ -164,7 +170,10 @@ const server = program => { Sockets, { serverHost: module.serverHost, - serverPort: module.serverPort + serverPort: module.serverPort, + usetls: program.usetls, + CA, + CA_KEY } ); diff --git a/utils/encryptionStore.js b/utils/encryptionStore.js new file mode 100644 index 00000000..3e5c75f4 --- /dev/null +++ b/utils/encryptionStore.js @@ -0,0 +1,61 @@ +/** + * @prettier + */ +const Crypto = require('crypto') +const { Buffer } = require('buffer') +const APIKeyPair = new Map() +const authorizedDevices = new Map() + +module.exports = { + encrypt: ({ deviceId, message }) => { + if (!authorizedDevices.has(deviceId)) { + throw { field: 'deviceId', message: 'Unknown Device ID' } + } + + const devicePublicKey = authorizedDevices.get(deviceId) + const data = Buffer.from(message) + const encryptedData = Crypto.publicEncrypt(devicePublicKey, data) + return encryptedData.toString('base64') + }, + decrypt: ({ deviceId, message }) => { + if (!authorizedDevices.has(deviceId)) { + throw { field: 'deviceId', message: 'Unknown Device ID' } + } + + const data = Buffer.from(message, 'base64') + const encryptedData = Crypto.privateDecrypt(APIKeyPair.private, data) + return encryptedData.toString('base64') + }, + authorizeDevice: ({ deviceId, publicKey }) => + new Promise((resolve, reject) => { + if (authorizedDevices.has(deviceId)) { + const error = { success: false, message: 'Device already exists' } + reject(error) + return error + } + + authorizedDevices.set(deviceId, publicKey) + + Crypto.generateKeyPair( + 'rsa', + { + modulusLength: 4096 + }, + (err, publicKey, privateKey) => { + if (err) { + reject({ field: 'APIKeyPair', errorMessage: err }) + return err + } + + APIKeyPair.set(deviceId, { + publicKey, + privateKey + }) + resolve({ success: true }) + } + ) + }), + unAuthorizeDevice: ({ deviceId }) => { + authorizedDevices.delete(deviceId) + } +}