From bff9739e1976bd20cd90beb94ddfa9d9eb26afd6 Mon Sep 17 00:00:00 2001 From: emad-salah Date: Sun, 9 Aug 2020 00:15:57 +0100 Subject: [PATCH 1/3] Trusted keys functionality completed --- src/routes.js | 94 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 72 insertions(+), 22 deletions(-) diff --git a/src/routes.js b/src/routes.js index 0436c811..18871af2 100644 --- a/src/routes.js +++ b/src/routes.js @@ -5,6 +5,7 @@ const Axios = require('axios') const Crypto = require('crypto') +const Storage = require('node-persist') const logger = require('winston') const httpsAgent = require('https') const responseTime = require('response-time') @@ -100,7 +101,7 @@ module.exports = async ( success: true }) }) - }) + }) const checkHealth = async () => { logger.info('Getting service status...') @@ -203,7 +204,7 @@ module.exports = async ( message: sanitizeLNDError(err.message) }) } - }) + }) // Hack to check whether or not a wallet exists const walletExists = async () => { @@ -264,27 +265,30 @@ module.exports = async ( logger.error('Unknown Device') return res.status(401).json(error) } - if (!req.body.encryptionKey && !req.body.iv && !req.headers["x-shock-encryption-token"]){ + if ( + !req.body.encryptionKey && + !req.body.iv && + !req.headers['x-shock-encryption-token'] + ) { return next() } - let encryptedToken,encryptedKey,IV,data - if(req.method === 'GET' || req.method === 'DELETE'){ - if(req.headers["x-shock-encryption-token"]){ - encryptedToken = req.headers["x-shock-encryption-token"] - encryptedKey =req.headers["x-shock-encryption-key"] - IV =req.headers["x-shock-encryption-iv"] + + const { data } = req.body + let IV = req.body.iv + let encryptedKey = req.body.encryptionKey + let encryptedToken = req.body.token + if (req.method === 'GET' || req.method === 'DELETE') { + if (req.headers['x-shock-encryption-token']) { + encryptedToken = req.headers['x-shock-encryption-token'] + encryptedKey = req.headers['x-shock-encryption-key'] + IV = req.headers['x-shock-encryption-iv'] } - } else { - encryptedToken = req.body.token - encryptedKey = req.body.encryptionKey - IV = req.body.iv - data = req.body.data } const decryptedKey = Encryption.decryptKey({ deviceId, message: encryptedKey }) - if(data){ + if (data) { const decryptedMessage = Encryption.decryptMessage({ message: data, key: decryptedKey, @@ -292,7 +296,7 @@ module.exports = async ( }) req.body = JSON.parse(decryptedMessage) } - + const decryptedToken = encryptedToken ? Encryption.decryptMessage({ message: encryptedToken, @@ -300,7 +304,6 @@ module.exports = async ( iv: IV }) : null - if (decryptedToken) { req.headers.authorization = decryptedToken @@ -470,6 +473,15 @@ module.exports = async ( } }) + const validateToken = async token => { + try { + const tokenValid = await auth.validateToken(token) + return tokenValid + } catch (err) { + return err + } + } + app.post('/api/lnd/auth', async (req, res) => { try { const health = await checkHealth() @@ -486,12 +498,44 @@ module.exports = async ( } const publicKey = await GunDB.authenticate(alias, password) - if ( - walletInitialized && - health.LNDStatus.walletStatus === 'locked' && - publicKey - ) { + + if (!publicKey) { + res.status(400).json({ + field: 'alias', + errorMessage: 'Invalid alias/password combination', + success: false + }) + return false + } + + const trustedKeys = await Storage.get('trustedPKs') + const [isKeyTrusted] = trustedKeys.filter( + trustedKey => trustedKey === publicKey + ) + const walletUnlocked = health.LNDStatus.walletStatus === 'unlocked' + + if (!walletUnlocked) { await unlockWallet(password) + + if (!isKeyTrusted) { + await Storage.set('trustedPKs', [...trustedKeys, publicKey]) + } + } + + if (walletUnlocked && !isKeyTrusted) { + const { authorization = '' } = req.headers + const validatedToken = await validateToken( + authorization.replace('Bearer ', '') + ) + + if (!validatedToken) { + res.status(403).json({ + field: 'alias', + errorMessage: 'Invalid alias/password combination', + success: false + }) + return + } } // Send an event to update lightning's status @@ -651,6 +695,12 @@ module.exports = async ( GunDB.mySEA ) + const trustedPKs = await Storage.get('trustedPKs') + await Storage.setItem('trustedPKs', [ + ...(trustedPKs || []), + publicKey + ]) + walletUnlocker.initWallet( walletArgs, async (initWalletErr, initWalletResponse) => { From 52d45f697f62f73f22f9c94c873ba4706173bbc2 Mon Sep 17 00:00:00 2001 From: emad-salah Date: Tue, 11 Aug 2020 11:46:59 +0100 Subject: [PATCH 2/3] Fixed trusted keys functionality and added trusted keys .env toggle --- .env.example | 1 + src/routes.js | 22 +++++++++++++--------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/.env.example b/.env.example index 65c31d1c..1e0d824d 100644 --- a/.env.example +++ b/.env.example @@ -4,3 +4,4 @@ MS_TO_TOKEN_EXPIRATION=4500000 DISABLE_SHOCK_ENCRYPTION=false CACHE_HEADERS_MANDATORY=true SHOCK_CACHE=true +TRUSTED_KEYS=true \ No newline at end of file diff --git a/src/routes.js b/src/routes.js index 18871af2..e397b8a7 100644 --- a/src/routes.js +++ b/src/routes.js @@ -190,9 +190,10 @@ module.exports = async ( resolve(unlockResponse) }) } catch (err) { - logger.error(err) + logger.error('Unlock Error:', err) if (err.message === 'unknown service lnrpc.WalletUnlocker') { resolve({ + field: 'walletUnlocker', message: 'Wallet already unlocked' }) return @@ -478,7 +479,7 @@ module.exports = async ( const tokenValid = await auth.validateToken(token) return tokenValid } catch (err) { - return err + return false } } @@ -500,7 +501,7 @@ module.exports = async ( const publicKey = await GunDB.authenticate(alias, password) if (!publicKey) { - res.status(400).json({ + res.status(401).json({ field: 'alias', errorMessage: 'Invalid alias/password combination', success: false @@ -508,16 +509,19 @@ module.exports = async ( return false } + const trustedKeysEnabled = + process.env.TRUSTED_KEYS === 'true' || !process.env.TRUSTED_KEYS const trustedKeys = await Storage.get('trustedPKs') - const [isKeyTrusted] = trustedKeys.filter( + // Falls back to true if trusted keys is disabled in .env + const [isKeyTrusted = !trustedKeysEnabled] = (trustedKeys || []).filter( trustedKey => trustedKey === publicKey ) const walletUnlocked = health.LNDStatus.walletStatus === 'unlocked' if (!walletUnlocked) { - await unlockWallet(password) + const unlockedWallet = await unlockWallet(password) - if (!isKeyTrusted) { + if (!isKeyTrusted && unlockedWallet.field !== 'walletUnlocker') { await Storage.set('trustedPKs', [...trustedKeys, publicKey]) } } @@ -529,7 +533,7 @@ module.exports = async ( ) if (!validatedToken) { - res.status(403).json({ + res.status(401).json({ field: 'alias', errorMessage: 'Invalid alias/password combination', success: false @@ -695,9 +699,9 @@ module.exports = async ( GunDB.mySEA ) - const trustedPKs = await Storage.get('trustedPKs') + const trustedKeys = await Storage.get('trustedPKs') await Storage.setItem('trustedPKs', [ - ...(trustedPKs || []), + ...(trustedKeys || []), publicKey ]) From 14dd0227e023e19274b959d502d46a54c0533629 Mon Sep 17 00:00:00 2001 From: emad-salah Date: Tue, 11 Aug 2020 12:21:31 +0100 Subject: [PATCH 3/3] Show Unlock Error log after verifying wallet isn't unlocked already --- src/routes.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/routes.js b/src/routes.js index e397b8a7..1f3386e9 100644 --- a/src/routes.js +++ b/src/routes.js @@ -190,7 +190,6 @@ module.exports = async ( resolve(unlockResponse) }) } catch (err) { - logger.error('Unlock Error:', err) if (err.message === 'unknown service lnrpc.WalletUnlocker') { resolve({ field: 'walletUnlocker', @@ -199,6 +198,8 @@ module.exports = async ( return } + logger.error('Unlock Error:', err) + reject({ field: 'wallet', code: err.code,