Deprecate RSA Encryption

This commit is contained in:
emad-salah 2021-10-15 09:24:49 +01:00
parent 485ec10465
commit 7b1e7ae97a
3 changed files with 11 additions and 350 deletions

View file

@ -20,7 +20,6 @@ const path = require('path')
const getListPage = require('../utils/paginate')
const auth = require('../services/auth/auth')
const FS = require('../utils/fs')
const Encryption = require('../utils/encryptionStore')
const ECC = require('../utils/ECC')
const LightningServices = require('../utils/lightningServices')
const lndErrorManager = require('../utils/lightningServices/errors')
@ -214,98 +213,13 @@ module.exports = async (
next()
})
app.use((req, res, next) => {
const legacyDeviceId = req.headers['x-shockwallet-device-id']
const deviceId = req.headers['encryption-device-id']
try {
if (
nonEncryptedRoutes.includes(req.path) ||
process.env.DISABLE_SHOCK_ENCRYPTION === 'true' ||
(deviceId && !legacyDeviceId)
) {
return next()
}
if (!legacyDeviceId) {
const error = {
field: 'deviceId',
message: 'Please specify a device ID'
}
logger.error('Please specify a device ID')
return res.status(401).json(error)
}
if (!Encryption.isAuthorizedDevice({ deviceId: legacyDeviceId })) {
const error = {
field: 'deviceId',
message: 'Please specify a device ID'
}
logger.error('Unknown Device')
return res.status(401).json(error)
}
if (
!req.body.encryptionKey &&
!req.body.iv &&
!req.headers['x-shock-encryption-token']
) {
return next()
}
let reqData = null
let IV = null
let encryptedKey = null
let encryptedToken = null
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 || req.body.encryptedKey
IV = req.body.iv
reqData = req.body.data || req.body.encryptedData
}
const decryptedKey = Encryption.decryptKey({
deviceId: legacyDeviceId,
message: encryptedKey
})
if (reqData) {
const decryptedMessage = Encryption.decryptMessage({
message: reqData,
key: decryptedKey,
iv: IV
})
req.body = JSON.parse(decryptedMessage)
}
const decryptedToken = encryptedToken
? Encryption.decryptMessage({
message: encryptedToken,
key: decryptedKey,
iv: IV
})
: null
if (decryptedToken) {
req.headers.authorization = decryptedToken
}
return next()
} catch (err) {
logger.error(err)
return res.status(401).json(err)
}
})
app.use(async (req, res, next) => {
const legacyDeviceId = req.headers['x-shockwallet-device-id']
const deviceId = req.headers['encryption-device-id']
try {
if (
nonEncryptedRoutes.includes(req.path) ||
process.env.DISABLE_SHOCK_ENCRYPTION === 'true' ||
(legacyDeviceId && !deviceId)
!deviceId
) {
return next()
}
@ -499,44 +413,6 @@ module.exports = async (
res.json({ msg: 'OK' })
})
app.post('/api/security/exchangeKeys', async (req, res) => {
try {
const { publicKey, deviceId } = req.body
if (!publicKey) {
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
})
logger.info(authorizedDevice)
return res.json(authorizedDevice)
} catch (err) {
logger.error(err)
return res.status(401).json({
field: 'unknown',
message: err
})
}
})
app.post('/api/encryption/exchange', async (req, res) => {
try {
const { publicKey, deviceId } = req.body

View file

@ -1,5 +1,3 @@
const { generateRandomString } = require('../utils/encryptionStore')
/**
* @prettier
*/
@ -22,7 +20,6 @@ const server = program => {
const ECC = require('../utils/ECC')
const LightningServices = require('../utils/lightningServices')
const Encryption = require('../utils/encryptionStore')
const app = Express()
const compression = require('compression')
@ -124,7 +121,6 @@ const server = program => {
* @param {(() => void)} next
*/
const modifyResponseBody = (req, res, next) => {
const legacyDeviceId = req.headers['x-shockwallet-device-id']
const deviceId = req.headers['encryption-device-id']
const oldSend = res.send
@ -136,38 +132,6 @@ const server = program => {
return
}
if (legacyDeviceId) {
res.send = (...args) => {
if (args[0] && args[0].encryptedData && args[0].encryptionKey) {
logger.warn('Response loop detected!')
oldSend.apply(res, args)
return
}
const { cached, hash } = cacheCheck({ req, res, args, send: oldSend })
if (cached) {
return
}
// arguments[0] (or `data`) contains the response body
const authorized = Encryption.isAuthorizedDevice({
deviceId: legacyDeviceId
})
const encryptedMessage = authorized
? Encryption.encryptMessage({
message: args[0] ? args[0] : {},
deviceId: legacyDeviceId,
metadata: {
hash
}
})
: args[0]
args[0] = JSON.stringify(encryptedMessage)
oldSend.apply(res, args)
}
}
if (deviceId) {
res.send = (...args) => {
if (args[0] && args[0].ciphertext && args[0].iv) {
@ -288,7 +252,7 @@ const server = program => {
return randomField
}
const newValue = await Encryption.generateRandomString(length)
const newValue = await ECC.generateRandomString(length)
await Storage.setItem(fieldName, newValue)
return newValue
}
@ -441,13 +405,15 @@ const server = program => {
}
if (process.env.ALLOW_UNLOCKED_LND === 'true') {
const codes = await Storage.valuesWithKeyMatch(/^UnlockedAccessSecrets\//u)
const codes = await Storage.valuesWithKeyMatch(
/^UnlockedAccessSecrets\//u
)
if (codes.length === 0) {
const code = generateRandomString(12)
const code = ECC.generateRandomString(12)
await Storage.setItem(`UnlockedAccessSecrets/${code}`, false)
logger.info("the access code is:"+code)
logger.info('the access code is:' + code)
} else if (codes.length === 1 || codes[0] === false) {
logger.info("the access code is:"+codes[0])
logger.info('the access code is:' + codes[0])
}
}
serverInstance.listen(serverPort, serverHost)

View file

@ -1,181 +0,0 @@
/**
* @prettier
*/
const Crypto = require('crypto')
const { Buffer } = require('buffer')
const logger = require('../config/log')
const APIKeyPair = new Map()
const authorizedDevices = new Map()
const nonEncryptedEvents = [
'ping',
'disconnect',
'IS_GUN_AUTH',
'SET_LAST_SEEN_APP'
]
const Encryption = {
/**
* @param {string} event
* @returns {boolean}
*/
isNonEncrypted: event => nonEncryptedEvents.includes(event),
/**
* @param {{ deviceId: string , message: string }} arg0
*/
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(
{
key: devicePublicKey,
padding: Crypto.constants.RSA_PKCS1_PADDING
},
data
)
return encryptedData.toString('base64')
},
/**
* @param {{ deviceId: string , message: string }} arg0
*/
decryptKey: ({ deviceId, message }) => {
if (!authorizedDevices.has(deviceId)) {
throw { field: 'deviceId', message: 'Unknown Device ID' }
}
const data = Buffer.from(message, 'base64')
const encryptedData = Crypto.privateDecrypt(
{
key: APIKeyPair.get(deviceId).privateKey,
padding: Crypto.constants.RSA_PKCS1_PADDING
},
data
)
return encryptedData.toString()
},
/**
* @param {{ deviceId: string , message: any , metadata?: any}} arg0
*/
encryptMessage: ({ deviceId, message, metadata = {} }) => {
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')
const encryptedMessage = {
encryptedData,
encryptedKey,
iv: iv.toString('hex'),
metadata
}
return encryptedMessage
},
/**
* @param {{ message: string , key: string , iv: string }} arg0
*/
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()
return decryptedData.toString()
},
/**
* @param {{ deviceId: string }} arg0
*/
isAuthorizedDevice: ({ deviceId }) => {
if (authorizedDevices.has(deviceId)) {
return true
}
return false
},
/**
* @param {{ deviceId: string , publicKey: string }} arg0
*/
authorizeDevice: ({ deviceId, publicKey }) =>
new Promise((resolve, reject) => {
authorizedDevices.set(deviceId, publicKey)
Crypto.generateKeyPair(
'rsa',
{
modulusLength: 2048,
privateKeyEncoding: {
type: 'pkcs1',
format: 'pem'
},
publicKeyEncoding: {
type: 'pkcs1',
format: 'pem'
}
},
(err, publicKey, privateKey) => {
if (err) {
// @ts-ignore
logger.error(err)
reject(err)
return
}
const exportedKey = {
publicKey,
privateKey
}
APIKeyPair.set(deviceId, exportedKey)
resolve({
success: true,
APIPublicKey: exportedKey.publicKey
})
}
)
}),
/**
* @param {{ deviceId: string }} arg0
*/
unAuthorizeDevice: ({ deviceId }) => {
authorizedDevices.delete(deviceId)
},
generateRandomString: (length = 16) =>
new Promise((resolve, reject) => {
Crypto.randomBytes(length, (err, buffer) => {
if (err) {
reject(err)
return
}
const token = buffer.toString('hex')
resolve(token)
})
})
}
module.exports = Encryption