Merge pull request #208 from shocknet/gun-decrypt-rpc

Gun decrypt rpc
This commit is contained in:
Daniel Lugo 2020-10-07 15:25:56 -04:00 committed by GitHub
commit 2d1688ba3d
4 changed files with 121 additions and 18 deletions

View file

@ -205,7 +205,7 @@ const Config = require('../config')
// TO DO: move to common repo // TO DO: move to common repo
/** /**
* @typedef {object} SimpleSocket * @typedef {object} SimpleSocket
* @prop {(eventName: string, data: Emission|EncryptedEmission) => void} emit * @prop {(eventName: string, data?: Emission|EncryptedEmission) => void} emit
* @prop {(eventName: string, handler: (data: any) => void) => void} on * @prop {(eventName: string, handler: (data: any) => void) => void} on
* @prop {{ query: { 'x-shockwallet-device-id': string }}} handshake * @prop {{ query: { 'x-shockwallet-device-id': string }}} handshake
*/ */
@ -1475,5 +1475,6 @@ module.exports = {
getUser, getUser,
mySEA, mySEA,
getMySecret, getMySecret,
freshGun freshGun,
$$__SHOCKWALLET__ENCRYPTED__
} }

View file

@ -7,11 +7,53 @@ const mapValues = require('lodash/mapValues')
const Bluebird = require('bluebird') const Bluebird = require('bluebird')
const { pubToEpub } = require('./contact-api/utils') const { pubToEpub } = require('./contact-api/utils')
const { getGun, getUser, mySEA: SEA, getMySecret } = require('./Mediator') const {
getGun,
getUser,
mySEA: SEA,
getMySecret,
$$__SHOCKWALLET__ENCRYPTED__
} = require('./Mediator')
/** /**
* @typedef {import('./contact-api/SimpleGUN').ValidDataValue} ValidDataValue * @typedef {import('./contact-api/SimpleGUN').ValidDataValue} ValidDataValue
*/ */
/**
* @param {ValidDataValue} value
* @param {string} publicKey
* @returns {Promise<ValidDataValue>}
*/
const deepDecryptIfNeeded = async (value, publicKey) => {
if (Schema.isObj(value)) {
return Bluebird.props(
mapValues(value, o => deepDecryptIfNeeded(o, publicKey))
)
}
if (
typeof value === 'string' &&
value.indexOf($$__SHOCKWALLET__ENCRYPTED__) === 0
) {
const user = getUser()
if (!user.is) {
throw new Error(Constants.ErrorCode.NOT_AUTH)
}
let sec = ''
if (user.is.pub === publicKey) {
sec = getMySecret()
} else {
sec = await SEA.secret(publicKey, user._.sea)
}
const decrypted = SEA.decrypt(value, sec)
return decrypted
}
return value
}
/** /**
* @param {ValidDataValue} value * @param {ValidDataValue} value
* @returns {Promise<ValidDataValue>} * @returns {Promise<ValidDataValue>}
@ -148,5 +190,7 @@ const set = async (rawPath, value) => {
module.exports = { module.exports = {
put, put,
set set,
deepDecryptIfNeeded,
deepEncryptIfNeeded
} }

View file

@ -2995,12 +2995,19 @@ module.exports = async (
* @prop {boolean} startFromUserGraph * @prop {boolean} startFromUserGraph
* @prop {string} path * @prop {string} path
* @prop {string=} publicKey * @prop {string=} publicKey
* @prop {string=} publicKeyForDecryption
*/ */
/** /**
* @param {HandleGunFetchParams} args0 * @param {HandleGunFetchParams} args0
* @returns {Promise<unknown>} * @returns {Promise<unknown>}
*/ */
const handleGunFetch = ({ type, startFromUserGraph, path, publicKey }) => { const handleGunFetch = ({
type,
startFromUserGraph,
path,
publicKey,
publicKeyForDecryption
}) => {
const keys = path.split('.') const keys = path.split('.')
const { tryAndWait } = require('../services/gunDB/contact-api/utils') const { tryAndWait } = require('../services/gunDB/contact-api/utils')
console.log(keys) console.log(keys)
@ -3014,76 +3021,106 @@ module.exports = async (
keys.forEach(key => (node = node.get(key))) keys.forEach(key => (node = node.get(key)))
return new Promise(res => { return new Promise(res => {
if (type === 'once') node.once(data => res(data)) const listener = async data => {
if (type === 'load') node.load(data => res(data)) if (publicKeyForDecryption) {
res(
await GunWriteRPC.deepDecryptIfNeeded(
data,
publicKeyForDecryption
)
)
} else {
res(data)
}
}
if (type === 'once') node.once(listener)
if (type === 'load') node.load(listener)
}) })
}) })
} }
/**
* Used decryption of incoming data.
*/
const PUBKEY_FOR_DECRYPT_HEADER = 'public-key-for-decryption'
ap.get('/api/gun/once/:path', async (req, res) => { ap.get('/api/gun/once/:path', async (req, res) => {
const publicKeyForDecryption = req.header(PUBKEY_FOR_DECRYPT_HEADER)
const { path } = req.params const { path } = req.params
res.status(200).json({ res.status(200).json({
data: await handleGunFetch({ data: await handleGunFetch({
path, path,
startFromUserGraph: false, startFromUserGraph: false,
type: 'once' type: 'once',
publicKeyForDecryption
}) })
}) })
}) })
ap.get('/api/gun/load/:path', async (req, res) => { ap.get('/api/gun/load/:path', async (req, res) => {
const publicKeyForDecryption = req.header(PUBKEY_FOR_DECRYPT_HEADER)
const { path } = req.params const { path } = req.params
res.status(200).json({ res.status(200).json({
data: await handleGunFetch({ data: await handleGunFetch({
path, path,
startFromUserGraph: false, startFromUserGraph: false,
type: 'load' type: 'load',
publicKeyForDecryption
}) })
}) })
}) })
ap.get('/api/gun/user/once/:path', async (req, res) => { ap.get('/api/gun/user/once/:path', async (req, res) => {
const publicKeyForDecryption = req.header(PUBKEY_FOR_DECRYPT_HEADER)
const { path } = req.params const { path } = req.params
res.status(200).json({ res.status(200).json({
data: await handleGunFetch({ data: await handleGunFetch({
path, path,
startFromUserGraph: true, startFromUserGraph: true,
type: 'once' type: 'once',
publicKeyForDecryption
}) })
}) })
}) })
ap.get('/api/gun/user/load/:path', async (req, res) => { ap.get('/api/gun/user/load/:path', async (req, res) => {
const publicKeyForDecryption = req.header(PUBKEY_FOR_DECRYPT_HEADER)
const { path } = req.params const { path } = req.params
res.status(200).json({ res.status(200).json({
data: await handleGunFetch({ data: await handleGunFetch({
path, path,
startFromUserGraph: true, startFromUserGraph: true,
type: 'load' type: 'load',
publicKeyForDecryption
}) })
}) })
}) })
ap.get('/api/gun/otheruser/:publicKey/once/:path', async (req, res) => { ap.get('/api/gun/otheruser/:publicKey/once/:path', async (req, res) => {
const publicKeyForDecryption = req.header(PUBKEY_FOR_DECRYPT_HEADER)
const { path, publicKey } = req.params const { path, publicKey } = req.params
res.status(200).json({ res.status(200).json({
data: await handleGunFetch({ data: await handleGunFetch({
path, path,
startFromUserGraph: false, startFromUserGraph: false,
type: 'once', type: 'once',
publicKey publicKey,
publicKeyForDecryption
}) })
}) })
}) })
ap.get('/api/gun/otheruser/:publicKey/load/:path', async (req, res) => { ap.get('/api/gun/otheruser/:publicKey/load/:path', async (req, res) => {
const publicKeyForDecryption = req.header(PUBKEY_FOR_DECRYPT_HEADER)
const { path, publicKey } = req.params const { path, publicKey } = req.params
res.status(200).json({ res.status(200).json({
data: await handleGunFetch({ data: await handleGunFetch({
path, path,
startFromUserGraph: false, startFromUserGraph: false,
type: 'load', type: 'load',
publicKey publicKey,
publicKeyForDecryption
}) })
}) })
}) })

View file

@ -1,7 +1,10 @@
/** @prettier */ /**
* @format
*/
// @ts-check // @ts-check
const logger = require('winston') const logger = require('winston')
const Encryption = require('../utils/encryptionStore') const Encryption = require('../utils/encryptionStore')
const LightningServices = require('../utils/lightningServices') const LightningServices = require('../utils/lightningServices')
const { const {
@ -9,7 +12,16 @@ const {
getUser, getUser,
isAuthenticated isAuthenticated
} = require('../services/gunDB/Mediator') } = require('../services/gunDB/Mediator')
const { deepDecryptIfNeeded } = require('../services/gunDB/rpc')
/**
* @typedef {import('../services/gunDB/Mediator').SimpleSocket} SimpleSocket
* @typedef {import('../services/gunDB/contact-api/SimpleGUN').ValidDataValue} ValidDataValue
*/
/**
* @param {SimpleSocket} socket
* @param {string} subID
*/
const onPing = (socket, subID) => { const onPing = (socket, subID) => {
logger.warn('Subscribing to pings socket...' + subID) logger.warn('Subscribing to pings socket...' + subID)
@ -296,7 +308,7 @@ module.exports = (
return return
} }
const { $shock } = socket.handshake.query const { $shock, publicKeyForDecryption } = socket.handshake.query
const [root, path, method] = $shock.split('::') const [root, path, method] = $shock.split('::')
@ -316,12 +328,21 @@ module.exports = (
} }
/** /**
* @param {unknown} data * @param {ValidDataValue} data
* @param {string} key * @param {string} key
*/ */
const listener = (data, key) => { const listener = async (data, key) => {
try { try {
if (publicKeyForDecryption) {
const decData = await deepDecryptIfNeeded(
data,
publicKeyForDecryption
)
socket.emit('$shock', decData, key)
} else {
socket.emit('$shock', data, key) socket.emit('$shock', data, key)
}
} catch (err) { } catch (err) {
logger.error( logger.error(
`Error for gun rpc socket, query ${$shock} -> ${err.message}` `Error for gun rpc socket, query ${$shock} -> ${err.message}`