End to end encryption completed through HTTP!

This commit is contained in:
emad-salah 2020-01-20 15:19:31 +01:00
parent 2a604edb9e
commit 4126fd503a
6 changed files with 389 additions and 184 deletions

View file

@ -2,5 +2,5 @@
"requirePragma": true,
"semi": false,
"singleQuote": true,
"endOfLine": "lf"
"endOfLine": "auto"
}

View file

@ -10,14 +10,17 @@ const Crypto = require("crypto");
const logger = require("winston");
const httpsAgent = require("https");
const responseTime = require("response-time");
const uuid = require("uuid/v4");
const getListPage = require("../utils/paginate");
const auth = require("../services/auth/auth");
const FS = require("../utils/fs");
const Encryption = require("../utils/encryptionStore");
const LightningServices = require("../utils/lightningServices");
const GunDB = require("../services/gunDB/Mediator");
const { unprotectedRoutes } = require("../utils/protectedRoutes");
const { unprotectedRoutes, nonEncryptedRoutes } = require("../utils/protectedRoutes");
const DEFAULT_MAX_NUM_ROUTES_TO_QUERY = 10;
const SESSION_ID = uuid();
// module.exports = (app) => {
module.exports = async (
@ -116,7 +119,7 @@ module.exports = async (
const health = await checkHealth();
if (health.LNDStatus.success) {
if (err) {
res.send({
res.json({
errorMessage: sanitizeLNDError(err.message)
});
} else {
@ -124,7 +127,7 @@ module.exports = async (
}
} else {
res.status(500);
res.send({ errorMessage: "LND is down" });
res.json({ errorMessage: "LND is down" });
}
};
@ -192,6 +195,53 @@ module.exports = async (
}
};
app.use((req, res, next) => {
res.setHeader("x-session-id", SESSION_ID)
next()
})
app.use((req, res, next) => {
const deviceId = req.headers["x-shockwallet-device-id"];
try {
if (nonEncryptedRoutes.includes(req.path)) {
return next();
}
if (!deviceId) {
const error = {
field: "deviceId",
message: "Please specify a device ID"
};
console.error(error)
return res.status(401).json(error);
}
if (!Encryption.isAuthorizedDevice({ deviceId })) {
const error = {
field: "deviceId",
message: "Please specify a device ID"
};
console.error("Unknown Device", error)
return res.status(401).json(error);
}
console.log("Body:", req.body)
console.log("Decrypt params:", { deviceId, message: req.body.encryptionKey })
const decryptedKey = Encryption.decryptKey({ deviceId, message: req.body.encryptionKey });
console.log("decryptedKey", decryptedKey)
const decryptedMessage = Encryption.decryptMessage({ message: req.body.data, key: decryptedKey, iv: req.body.iv })
req.body = JSON.parse(decryptedMessage);
return next();
} catch (err) {
console.error(err);
return res
.status(401)
.json(
err
);
}
})
app.use(async (req, res, next) => {
try {
console.log("Route:", req.path)
@ -247,7 +297,7 @@ module.exports = async (
*/
app.get("/health", async (req, res) => {
const health = await checkHealth();
res.send(health);
res.json(health);
});
/**
@ -255,11 +305,11 @@ module.exports = async (
*/
app.get("/healthz", async (req, res) => {
const health = await checkHealth();
res.send(health);
res.json(health);
});
app.get("/ping", (req, res) => {
res.send("OK");
res.json({ message: "OK" });
});
app.post("/api/mobile/error", (req, res) => {
@ -267,8 +317,36 @@ module.exports = async (
res.json({ msg: "OK" });
});
app.post("/api/security/exchangeKeys", (req, res) => {
app.post("/api/security/exchangeKeys", async (req, res) => {
try {
const { publicKey, deviceId } = req.body;
if (!publicKey || publicKey.length < 600) {
return res.status(400).json({
field: 'publicKey',
message: "Please provide a valid public key"
})
}
if (!deviceId ||
!/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/iu
.test(deviceId)) {
return res.status(400).json({
field: 'deviceId',
message: "Please provide a valid device ID"
})
}
const authorizedDevice = await Encryption.authorizeDevice({ deviceId, publicKey })
console.log(authorizedDevice)
return res.status(200).json(authorizedDevice)
} catch (err) {
console.error(err)
return res.status(401).json({
field: 'unknown',
message: err
})
}
})
app.get("/api/lnd/wallet/status", async (req, res) => {
@ -292,6 +370,7 @@ module.exports = async (
app.post("/api/lnd/auth", async (req, res) => {
try {
console.log("/api/lnd/auth Body:", req.body)
const health = await checkHealth();
const walletInitialized = await walletExists();
// If we're connected to lnd, unlock the wallet using the password supplied
@ -337,7 +416,7 @@ module.exports = async (
}
res.status(500);
res.send({
res.json({
field: "health",
errorMessage: sanitizeLNDError(health.LNDStatus.message),
success: false
@ -346,7 +425,7 @@ module.exports = async (
} catch (err) {
logger.debug("Unlock Error:", err);
res.status(400);
res.send({ field: "user", errorMessage: sanitizeLNDError(err.message), success: false });
res.json({ field: "user", errorMessage: sanitizeLNDError(err.message), success: false });
return err;
}
});
@ -368,10 +447,10 @@ module.exports = async (
const health = await checkHealth();
if (health.LNDStatus.success) {
res.status(400);
res.send({ field: "WalletUnlocker", errorMessage: unlockErr.message });
res.json({ field: "WalletUnlocker", errorMessage: unlockErr.message });
} else {
res.status(500);
res.send({ errorMessage: "LND is down" });
res.json({ errorMessage: "LND is down" });
}
} else {
await recreateLnServices();
@ -435,12 +514,12 @@ module.exports = async (
const message = genSeedErr.details;
return res
.status(400)
.send({ field: "GenSeed", errorMessage: message, success: false });
.json({ field: "GenSeed", errorMessage: message, success: false });
}
return res
.status(500)
.send({ field: "health", errorMessage: "LND is down", success: false });
.json({ field: "health", errorMessage: "LND is down", success: false });
}
logger.debug("GenSeed:", genSeedResponse);
@ -613,13 +692,13 @@ module.exports = async (
logger.error("GetInfo Error:", err);
const health = await checkHealth();
if (health.LNDStatus.success) {
res.status(400).send({
res.status(400).json({
field: "getInfo",
errorMessage: sanitizeLNDError(err.message)
});
} else {
res.status(500);
res.send({ errorMessage: "LND is down" });
res.json({ errorMessage: "LND is down" });
}
}
logger.info("GetInfo:", response);
@ -644,13 +723,13 @@ module.exports = async (
const health = await checkHealth();
if (health.LNDStatus.success) {
res.status(400);
res.send({
res.json({
field: "getNodeInfo",
errorMessage: sanitizeLNDError(err.message)
});
} else {
res.status(500);
res.send({ errorMessage: "LND is down" });
res.json({ errorMessage: "LND is down" });
}
}
logger.debug("GetNodeInfo:", response);
@ -666,13 +745,13 @@ module.exports = async (
logger.debug("GetNetworkInfo Error:", err);
const health = await checkHealth();
if (health.LNDStatus.success) {
res.status(400).send({
res.status(400).json({
field: "getNodeInfo",
errorMessage: sanitizeLNDError(err.message)
});
} else {
res.status(500);
res.send({ errorMessage: "LND is down" });
res.json({ errorMessage: "LND is down" });
}
}
logger.debug("GetNetworkInfo:", response);
@ -688,13 +767,13 @@ module.exports = async (
logger.debug("ListPeers Error:", err);
const health = await checkHealth();
if (health.LNDStatus.success) {
res.status(400).send({
res.status(400).json({
field: "listPeers",
errorMessage: sanitizeLNDError(err.message)
});
} else {
res.status(500);
res.send({ errorMessage: "LND is down" });
res.json({ errorMessage: "LND is down" });
}
}
logger.debug("ListPeers:", response);
@ -710,13 +789,13 @@ module.exports = async (
logger.debug("NewAddress Error:", err);
const health = await checkHealth();
if (health.LNDStatus.success) {
res.status(400).send({
res.status(400).json({
field: "newAddress",
errorMessage: sanitizeLNDError(err.message)
});
} else {
res.status(500);
res.send({ errorMessage: "LND is down" });
res.json({ errorMessage: "LND is down" });
}
}
logger.debug("NewAddress:", response);
@ -731,13 +810,13 @@ module.exports = async (
const health = await checkHealth();
if (health.LNDStatus.success) {
res.status(403);
return res.send({
return res.json({
field: "limituser",
errorMessage: "User limited"
});
}
res.status(500);
res.send({ errorMessage: "LND is down" });
res.json({ errorMessage: "LND is down" });
}
const connectRequest = {
addr: { pubkey: req.body.pubkey, host: req.body.host },
@ -747,7 +826,7 @@ module.exports = async (
lightning.connectPeer(connectRequest, (err, response) => {
if (err) {
logger.debug("ConnectPeer Error:", err);
res.status(500).send({ field: "connectPeer", errorMessage: sanitizeLNDError(err.message) });
res.status(500).json({ field: "connectPeer", errorMessage: sanitizeLNDError(err.message) });
} else {
logger.debug("ConnectPeer:", response);
res.json(response);
@ -762,20 +841,20 @@ module.exports = async (
const health = await checkHealth();
if (health.LNDStatus.success) {
res.status(403);
return res.send({
return res.json({
field: "limituser",
errorMessage: "User limited"
});
}
res.status(500);
res.send({ errorMessage: "LND is down" });
res.json({ errorMessage: "LND is down" });
}
const disconnectRequest = { pub_key: req.body.pubkey };
logger.debug("DisconnectPeer Request:", disconnectRequest);
lightning.disconnectPeer(disconnectRequest, (err, response) => {
if (err) {
logger.debug("DisconnectPeer Error:", err);
res.status(400).send({ field: "disconnectPeer", errorMessage: sanitizeLNDError(err.message) });
res.status(400).json({ field: "disconnectPeer", errorMessage: sanitizeLNDError(err.message) });
} else {
logger.debug("DisconnectPeer:", response);
res.json(response);
@ -791,13 +870,13 @@ module.exports = async (
logger.debug("ListChannels Error:", err);
const health = await checkHealth();
if (health.LNDStatus.success) {
res.status(400).send({
res.status(400).json({
field: "listChannels",
errorMessage: sanitizeLNDError(err.message)
});
} else {
res.status(500);
res.send({ errorMessage: "LND is down" });
res.json({ errorMessage: "LND is down" });
}
}
logger.debug("ListChannels:", response);
@ -813,13 +892,13 @@ module.exports = async (
logger.debug("PendingChannels Error:", err);
const health = await checkHealth();
if (health.LNDStatus.success) {
res.status(400).send({
res.status(400).json({
field: "pendingChannels",
errorMessage: sanitizeLNDError(err.message)
});
} else {
res.status(500);
res.send({ errorMessage: "LND is down" });
res.json({ errorMessage: "LND is down" });
}
}
logger.debug("PendingChannels:", response);
@ -906,10 +985,10 @@ module.exports = async (
logger.debug("ListInvoices Error:", err);
const health = await checkHealth();
if (health.LNDStatus.success) {
res.status(400).send({ errorMessage: sanitizeLNDError(err.message), success: false });
res.status(400).json({ errorMessage: sanitizeLNDError(err.message), success: false });
} else {
res.status(500);
res.send({ errorMessage: health.LNDStatus.message, success: false });
res.json({ errorMessage: health.LNDStatus.message, success: false });
}
} else {
// logger.debug("ListInvoices:", response);
@ -932,13 +1011,13 @@ module.exports = async (
logger.debug("ForwardingHistory Error:", err);
const health = await checkHealth();
if (health.LNDStatus.success) {
res.status(400).send({
res.status(400).json({
field: "forwardingHistory",
errorMessage: sanitizeLNDError(err.message)
});
} else {
res.status(500);
res.send({ errorMessage: "LND is down" });
res.json({ errorMessage: "LND is down" });
}
}
logger.debug("ForwardingHistory:", response);
@ -954,13 +1033,13 @@ module.exports = async (
logger.debug("WalletBalance Error:", err);
const health = await checkHealth();
if (health.LNDStatus.success) {
res.status(400).send({
res.status(400).json({
field: "walletBalance",
errorMessage: sanitizeLNDError(err.message)
});
} else {
res.status(500);
res.send({ errorMessage: "LND is down" });
res.json({ errorMessage: "LND is down" });
}
}
logger.debug("WalletBalance:", response);
@ -976,13 +1055,13 @@ module.exports = async (
if (err) {
logger.debug("WalletBalance Error:", err);
if (health.LNDStatus.success) {
res.status(400).send({
res.status(400).json({
field: "walletBalance",
errorMessage: sanitizeLNDError(err.message)
});
} else {
res.status(500);
res.send(health.LNDStatus);
res.json(health.LNDStatus);
}
return err;
}
@ -991,13 +1070,13 @@ module.exports = async (
if (err) {
logger.debug("ChannelBalance Error:", err);
if (health.LNDStatus.success) {
res.status(400).send({
res.status(400).json({
field: "channelBalance",
errorMessage: sanitizeLNDError(err.message)
});
} else {
res.status(500);
res.send(health.LNDStatus);
res.json(health.LNDStatus);
}
return err;
}
@ -1020,11 +1099,11 @@ module.exports = async (
logger.debug("DecodePayReq Error:", err);
const health = await checkHealth();
if (health.LNDStatus.success) {
res.status(500).send({
res.status(500).json({
errorMessage: sanitizeLNDError(err.message)
});
} else {
res.status(500).send({ errorMessage: "LND is down" });
res.status(500).json({ errorMessage: "LND is down" });
}
} else {
logger.info("DecodePayReq:", paymentRequest);
@ -1042,13 +1121,13 @@ module.exports = async (
logger.debug("ChannelBalance Error:", err);
const health = await checkHealth();
if (health.LNDStatus.success) {
res.status(400).send({
res.status(400).json({
field: "channelBalance",
errorMessage: sanitizeLNDError(err.message)
});
} else {
res.status(500);
res.send({ errorMessage: "LND is down" });
res.json({ errorMessage: "LND is down" });
}
}
logger.debug("ChannelBalance:", response);
@ -1065,7 +1144,7 @@ module.exports = async (
res.sendStatus(403);
} else {
res.status(500);
res.send({ errorMessage: "LND is down" });
res.json({ errorMessage: "LND is down" });
}
return;
}
@ -1089,10 +1168,10 @@ module.exports = async (
logger.info("OpenChannelRequest Error:", err);
const health = await checkHealth();
if (health.LNDStatus.success && !res.headersSent) {
res.status(500).send({ field: "openChannelRequest", errorMessage: sanitizeLNDError(err.message) });
res.status(500).json({ field: "openChannelRequest", errorMessage: sanitizeLNDError(err.message) });
} else if (!res.headersSent) {
res.status(500);
res.send({ errorMessage: "LND is down" });
res.json({ errorMessage: "LND is down" });
}
});
openedChannel.write(openChannelRequest)
@ -1108,7 +1187,7 @@ module.exports = async (
res.sendStatus(403);
} else {
res.status(500);
res.send({ errorMessage: "LND is down" });
res.json({ errorMessage: "LND is down" });
}
}
const { channelPoint, outputIndex } = req.body;
@ -1136,13 +1215,13 @@ module.exports = async (
if (!res.headersSent) {
if (health.LNDStatus.success) {
logger.debug("CloseChannelRequest Error:", err);
res.status(400).send({
res.status(400).json({
field: "closeChannel",
errorMessage: sanitizeLNDError(err.message)
});
} else {
res.status(500);
res.send({ errorMessage: "LND is down" });
res.json({ errorMessage: "LND is down" });
}
}
});
@ -1157,7 +1236,7 @@ module.exports = async (
res.sendStatus(403);
} else {
res.status(500);
res.send({ errorMessage: "LND is down" });
res.json({ errorMessage: "LND is down" });
}
}
const paymentRequest = { payment_request: req.body.payreq };
@ -1189,12 +1268,12 @@ module.exports = async (
logger.error("SendPayment Error:", err);
const health = await checkHealth();
if (health.LNDStatus.success) {
res.status(500).send({
res.status(500).json({
errorMessage: sanitizeLNDError(err.message)
});
} else {
res.status(500);
res.send({ errorMessage: "LND is down" });
res.json({ errorMessage: "LND is down" });
}
});
@ -1210,7 +1289,7 @@ module.exports = async (
res.sendStatus(403);
} else {
res.status(500);
res.send({ errorMessage: "LND is down" });
res.json({ errorMessage: "LND is down" });
}
return false;
}
@ -1226,13 +1305,13 @@ module.exports = async (
logger.debug("AddInvoice Error:", err);
const health = await checkHealth();
if (health.LNDStatus.success) {
res.status(400).send({
res.status(400).json({
field: "addInvoice",
errorMessage: sanitizeLNDError(err.message)
});
} else {
res.status(500);
res.send({ errorMessage: "LND is down" });
res.json({ errorMessage: "LND is down" });
}
return err;
}
@ -1250,7 +1329,7 @@ module.exports = async (
res.sendStatus(403);
} else {
res.status(500);
res.send({ errorMessage: "LND is down" });
res.json({ errorMessage: "LND is down" });
}
}
lightning.signMessage(
@ -1260,10 +1339,10 @@ module.exports = async (
logger.debug("SignMessage Error:", err);
const health = await checkHealth();
if (health.LNDStatus.success) {
res.status(400).send({ field: "signMessage", errorMessage: sanitizeLNDError(err.message) });
res.status(400).json({ field: "signMessage", errorMessage: sanitizeLNDError(err.message) });
} else {
res.status(500);
res.send({ errorMessage: "LND is down" });
res.json({ errorMessage: "LND is down" });
}
}
logger.debug("SignMessage:", response);
@ -1282,10 +1361,10 @@ module.exports = async (
logger.debug("VerifyMessage Error:", err);
const health = await checkHealth();
if (health.LNDStatus.success) {
res.status(400).send({ field: "verifyMessage", errorMessage: sanitizeLNDError(err.message) });
res.status(400).json({ field: "verifyMessage", errorMessage: sanitizeLNDError(err.message) });
} else {
res.status(500);
res.send({ errorMessage: "LND is down" });
res.json({ errorMessage: "LND is down" });
}
}
logger.debug("VerifyMessage:", response);
@ -1303,7 +1382,7 @@ module.exports = async (
res.sendStatus(403);
} else {
res.status(500);
res.send({ errorMessage: "LND is down" });
res.json({ errorMessage: "LND is down" });
}
}
const sendCoinsRequest = { addr: req.body.addr, amount: req.body.amount };
@ -1313,13 +1392,13 @@ module.exports = async (
logger.debug("SendCoins Error:", err);
const health = await checkHealth();
if (health.LNDStatus.success) {
res.status(400).send({
res.status(400).json({
field: "sendCoins",
errorMessage: sanitizeLNDError(err.message)
});
} else {
res.status(500);
res.send({ errorMessage: "LND is down" });
res.json({ errorMessage: "LND is down" });
}
}
logger.debug("SendCoins:", response);
@ -1339,10 +1418,10 @@ module.exports = async (
logger.debug("QueryRoute Error:", err);
const health = await checkHealth();
if (health.LNDStatus.success) {
res.status(400).send({ field: "queryRoute", errorMessage: sanitizeLNDError(err.message) });
res.status(400).json({ field: "queryRoute", errorMessage: sanitizeLNDError(err.message) });
} else {
res.status(500);
res.send({ errorMessage: "LND is down" });
res.json({ errorMessage: "LND is down" });
}
}
logger.debug("QueryRoute:", response);
@ -1365,12 +1444,12 @@ module.exports = async (
if (err) {
const health = await checkHealth();
if (health.LNDStatus.success) {
res.status(400).send({
res.status(400).json({
error: err.message
});
} else {
res.status(500);
res.send({ errorMessage: "LND is down" });
res.json({ errorMessage: "LND is down" });
}
} else {
logger.debug("EstimateFee:", fee);

View file

@ -1,78 +1,119 @@
"use strict";
/**
* @prettier
*/
/**
* Module dependencies.
*/
const server = program => {
const Https = require("https");
const Http = require("http");
const Express = require("express");
const LightningServices = require("../utils/lightningServices");
const app = Express();
const FS = require("../utils/fs");
const bodyParser = require("body-parser");
const session = require("express-session");
const methodOverride = require("method-override");
const { unprotectedRoutes, sensitiveRoutes } = require("../utils/protectedRoutes");
const Https = require('https')
const Http = require('http')
const Express = require('express')
const LightningServices = require('../utils/lightningServices')
const Encryption = require('../utils/encryptionStore')
const app = Express()
const FS = require('../utils/fs')
const bodyParser = require('body-parser')
const session = require('express-session')
const methodOverride = require('method-override')
const {
unprotectedRoutes,
sensitiveRoutes,
nonEncryptedRoutes
} = require('../utils/protectedRoutes')
// load app default configuration data
const defaults = require("../config/defaults")(program.mainnet);
const defaults = require('../config/defaults')(program.mainnet)
// define useful global variables ======================================
module.useTLS = program.usetls;
module.serverPort = program.serverport || defaults.serverPort;
module.httpsPort = module.serverPort;
module.serverHost = program.serverhost || defaults.serverHost;
module.useTLS = program.usetls
module.serverPort = program.serverport || defaults.serverPort
module.httpsPort = module.serverPort
module.serverHost = program.serverhost || defaults.serverHost
// setup winston logging ==========
const logger = require("../config/log")(
const logger = require('../config/log')(
program.logfile || defaults.logfile,
program.loglevel || defaults.loglevel
);
)
// utilities functions =================
require("../utils/server-utils")(module);
require('../utils/server-utils')(module)
logger.info("Mainnet Mode:", !!program.mainnet);
logger.info('Mainnet Mode:', !!program.mainnet)
const modifyResponseBody = (req, res, next) => {
const deviceId = req.headers['x-shockwallet-device-id']
const oldSend = res.send
if (!nonEncryptedRoutes.includes(req.path)) {
res.send = (...args) => {
if (args[0] && args[0].encryptedData && args[0].encryptionKey) {
console.log('Response loop detected', req.path, args[0])
oldSend.apply(res, args)
} else {
// arguments[0] (or `data`) contains the response body
const authorized = Encryption.isAuthorizedDevice({ deviceId })
const encryptedMessage = authorized
? Encryption.encryptMessage({
message: args[0],
deviceId
})
: args[0]
args[0] = JSON.stringify(encryptedMessage)
oldSend.apply(res, args)
}
}
}
next()
}
const wait = seconds =>
new Promise(resolve => {
const timer = setTimeout(() => resolve(timer), seconds * 1000);
});
const timer = setTimeout(() => resolve(timer), seconds * 1000)
})
// eslint-disable-next-line consistent-return
const startServer = async () => {
try {
LightningServices.setDefaults(program);
await LightningServices.init();
LightningServices.setDefaults(program)
await LightningServices.init()
// init lnd module =================
const lnd = require("../services/lnd/lnd")(LightningServices.services.lightning);
const auth = require("../services/auth/auth");
const lnd = require('../services/lnd/lnd')(
LightningServices.services.lightning
)
const auth = require('../services/auth/auth')
app.use(async (req, res, next) => {
console.log("Route:", req.path)
console.log('Route:', req.path)
if (unprotectedRoutes[req.method][req.path]) {
next();
next()
} else {
try {
const response = await auth.validateToken(
req.headers.authorization.replace("Bearer ", "")
);
req.headers.authorization.replace('Bearer ', '')
)
if (response.valid) {
next();
next()
} else {
res.status(401).json({ field: "authorization", errorMessage: "The authorization token you've supplied is invalid" });
res.status(401).json({
field: 'authorization',
errorMessage:
"The authorization token you've supplied is invalid"
})
}
} catch (err) {
logger.error(
!req.headers.authorization
? "Please add an Authorization header"
!req.headers.authorization
? 'Please add an Authorization header'
: err
);
res.status(401).json({ field: "authorization", errorMessage: "Please log in" });
)
res
.status(401)
.json({ field: 'authorization', errorMessage: 'Please log in' })
}
}
});
})
app.use((req, res, next) => {
if (sensitiveRoutes[req.method][req.path]) {
@ -84,7 +125,7 @@ const server = program => {
path: req.path,
sessionId: req.sessionId
})
);
)
} else {
console.log(
JSON.stringify({
@ -96,10 +137,10 @@ const server = program => {
query: req.query,
sessionId: req.sessionId
})
);
)
}
next();
});
next()
})
app.use(
session({
secret: defaults.sessionSecret,
@ -108,25 +149,23 @@ const server = program => {
rolling: true,
saveUninitialized: true
})
);
app.use(bodyParser.urlencoded({ extended: "true" }));
app.use(bodyParser.json());
app.use(bodyParser.json({ type: "application/vnd.api+json" }));
app.use(methodOverride());
)
app.use(bodyParser.urlencoded({ extended: 'true' }))
app.use(bodyParser.json())
app.use(bodyParser.json({ type: 'application/vnd.api+json' }))
app.use(methodOverride())
// WARNING
// error handler middleware, KEEP 4 parameters as express detects the
// arity of the function to treat it as a err handling middleware
// eslint-disable-next-line no-unused-vars
app.use((err, _, res, __) => {
// Do logging and user-friendly error message display
logger.error(err);
res
.status(500)
.send({ status: 500, errorMessage: "internal error" });
});
logger.error(err)
res.status(500).send({ status: 500, errorMessage: 'internal error' })
})
const CA = LightningServices.servicesConfig.lndCertPath
const CA_KEY = CA.replace("cert", "key")
const CA_KEY = CA.replace('cert', 'key')
const createServer = async () => {
try {
@ -134,62 +173,60 @@ const server = program => {
const [key, cert] = await Promise.all([
FS.readFile(CA_KEY),
FS.readFile(CA)
]);
const httpsServer = Https.createServer({ key, cert }, app);
])
const httpsServer = Https.createServer({ key, cert }, app)
return httpsServer;
return httpsServer
}
const httpServer = Http.Server(app);
return httpServer;
const httpServer = Http.Server(app)
return httpServer
} catch (err) {
logger.error(err.message);
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);
logger.error(err.message)
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
}
};
}
const serverInstance = await createServer();
const serverInstance = await createServer()
const io = require("socket.io")(serverInstance);
const io = require('socket.io')(serverInstance)
const Sockets = require("./sockets")(
const Sockets = require('./sockets')(
io,
lnd,
program.user,
program.pwd,
program.limituser,
program.limitpwd
);
)
require("./routes")(
app,
defaults,
Sockets,
{
serverHost: module.serverHost,
serverPort: module.serverPort,
usetls: program.usetls,
CA,
CA_KEY
}
);
require('./routes')(app, defaults, Sockets, {
serverHost: module.serverHost,
serverPort: module.serverPort,
usetls: program.usetls,
CA,
CA_KEY
})
// enable CORS headers
app.use(require("./cors"));
app.use(require('./cors'))
// app.use(bodyParser.json({limit: '100000mb'}));
app.use(bodyParser.json({ limit: "50mb" }));
app.use(bodyParser.urlencoded({ limit: "50mb", extended: true }));
app.use(bodyParser.json({ limit: '50mb' }))
app.use(bodyParser.urlencoded({ limit: '50mb', extended: true }))
app.use(modifyResponseBody)
serverInstance.listen(module.serverPort, module.serverhost);
serverInstance.listen(module.serverPort, module.serverhost)
logger.info(
"App listening on " + module.serverHost + " port " + module.serverPort
);
'App listening on ' + module.serverHost + ' port ' + module.serverPort
)
module.server = serverInstance;
module.server = serverInstance
// const localtunnel = require('localtunnel');
//
@ -198,15 +235,15 @@ const server = program => {
// console.log('t', t.url);
// });
} catch (err) {
logger.info(err);
logger.info("Restarting server in 30 seconds...");
await wait(30);
startServer();
return false;
logger.info(err)
logger.info('Restarting server in 30 seconds...')
await wait(30)
startServer()
return false
}
};
}
startServer();
};
startServer()
}
module.exports = server;
module.exports = server

View file

@ -6,52 +6,130 @@ const { Buffer } = require('buffer')
const APIKeyPair = new Map()
const authorizedDevices = new Map()
module.exports = {
encrypt: ({ deviceId, message }) => {
const Encryption = {
encryptKey: ({ 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)
const encryptedData = Crypto.publicEncrypt(
{
key: devicePublicKey,
padding: Crypto.constants.RSA_PKCS1_PADDING
},
data
)
return encryptedData.toString('base64')
},
decrypt: ({ deviceId, message }) => {
decryptKey: ({ 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')
const encryptedData = Crypto.privateDecrypt(
{
key: APIKeyPair.get(deviceId).privateKey,
padding: Crypto.constants.RSA_PKCS1_PADDING
},
data
)
console.log('Decrypted Data:', encryptedData)
return encryptedData.toString()
},
encryptMessage: ({ deviceId, message }) => {
const parsedMessage =
typeof message === 'object' ? JSON.stringify(message) : message
const data = Buffer.from(parsedMessage)
const key = Crypto.randomBytes(32)
const iv = Crypto.randomBytes(16)
const encryptedKey = Encryption.encryptKey({
deviceId,
message: key.toString('hex')
})
const cipher = Crypto.createCipheriv('aes-256-cbc', key, iv)
const encryptedCipher = cipher.update(data)
const encryptedBuffer = Buffer.concat([
Buffer.from(encryptedCipher),
Buffer.from(cipher.final())
])
const encryptedData = encryptedBuffer.toString('base64')
return { encryptedData, encryptedKey, iv: iv.toString('hex') }
},
decryptMessage: ({ message, key, iv }) => {
const data = Buffer.from(message, 'base64')
const cipher = Crypto.createDecipheriv(
'aes-256-cbc',
Buffer.from(key, 'hex'),
Buffer.from(iv, 'hex')
)
const decryptedCipher = cipher.update(data)
const decryptedBuffer = Buffer.concat([
Buffer.from(decryptedCipher),
Buffer.from(cipher.final())
])
const decryptedData = decryptedBuffer.toString()
console.log('Decrypted Data:', decryptedData)
return decryptedData.toString()
},
isAuthorizedDevice: ({ deviceId }) => {
console.log(
'deviceId',
deviceId,
Object.fromEntries(authorizedDevices.entries()),
authorizedDevices.has(deviceId)
)
if (authorizedDevices.has(deviceId)) {
return true
}
return false
},
authorizeDevice: ({ deviceId, publicKey }) =>
new Promise((resolve, reject) => {
if (authorizedDevices.has(deviceId)) {
const error = { success: false, message: 'Device already exists' }
reject(error)
return error
const devicePublicKey = APIKeyPair.get(deviceId).publicKey
const deviceExists = {
success: true,
APIPublicKey: devicePublicKey
}
resolve(deviceExists)
return deviceExists
}
authorizedDevices.set(deviceId, publicKey)
Crypto.generateKeyPair(
'rsa',
{
modulusLength: 4096
modulusLength: 4096,
privateKeyEncoding: {
type: 'pkcs1',
format: 'pem'
},
publicKeyEncoding: {
type: 'pkcs1',
format: 'pem'
}
},
(err, publicKey, privateKey) => {
if (err) {
reject({ field: 'APIKeyPair', errorMessage: err })
console.error(err)
reject(err)
return err
}
APIKeyPair.set(deviceId, {
const exportedKey = {
publicKey,
privateKey
}
APIKeyPair.set(deviceId, exportedKey)
resolve({
success: true,
APIPublicKey: exportedKey.publicKey
})
resolve({ success: true })
}
)
}),
@ -59,3 +137,5 @@ module.exports = {
authorizedDevices.delete(deviceId)
}
}
module.exports = Encryption

View file

@ -13,7 +13,8 @@ module.exports = {
"/api/lnd/connect": true,
"/api/lnd/wallet": true,
"/api/lnd/wallet/existing": true,
"/api/lnd/auth": true
"/api/lnd/auth": true,
"/api/security/exchangeKeys": true
},
PUT: {},
DELETE: {}
@ -26,5 +27,6 @@ module.exports = {
},
PUT: {},
DELETE: {}
}
},
nonEncryptedRoutes: ['/api/security/exchangeKeys', '/healthz', '/ping', '/api/lnd/wallet/status']
}

View file

@ -873,7 +873,7 @@ ascli@~1:
colour "~0.7.1"
optjs "~3.2.2"
asn1@~0.2.3:
asn1@^0.2.4, asn1@~0.2.3:
version "0.2.4"
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
@ -4341,6 +4341,13 @@ node-pre-gyp@^0.13.0:
semver "^5.3.0"
tar "^4"
node-rsa@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/node-rsa/-/node-rsa-1.0.7.tgz#85b7a6d6fa8ee624be6402a6b41be49272d58055"
integrity sha512-idwRXma6scFufZmbaKkHpJoLL93yynRefP6yur13wZ5i9FR35ex451KCoF2OORDeJanyRVahmjjiwmUlCnTqJA==
dependencies:
asn1 "^0.2.4"
nodemon@^1.19.3:
version "1.19.3"
resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.19.3.tgz#db71b3e62aef2a8e1283a9fa00164237356102c0"