diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..6461deec --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +*.ts diff --git a/.eslintrc.json b/.eslintrc.json index dd2485bf..252f73b1 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -88,7 +88,10 @@ // I am now convinced TODO comments closer to the relevant code are better // than GH issues. Especially when it only concerns a single function / // routine. - "no-warning-comments": "off" + "no-warning-comments": "off", + + // broken + "sort-imports": "off" }, "parser": "babel-eslint", "env": { diff --git a/.vscode/settings.json b/.vscode/settings.json index 347760c1..38a20598 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,5 +2,6 @@ "eslint.enable": true, "typescript.tsdk": "node_modules/typescript/lib", "debug.node.autoAttach": "on", - "editor.formatOnSave": true + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode" } diff --git a/package.json b/package.json index 75f523a5..6debf058 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "test:watch": "jest --no-cache --watch", "typecheck": "tsc", "lint": "eslint \"services/gunDB/**/*.js\"", - "format": "prettier --write \"./**/*.js\"" + "format": "prettier --write \"./**/*.js\"", + "test:gun": "ts-node src/__gun__tests__/*.ts && rimraf -rf GUN-TEST-*" }, "author": "", "license": "ISC", @@ -82,6 +83,8 @@ "lint-staged": "^10.2.2", "nodemon": "^1.19.3", "prettier": "^1.18.2", + "rimraf": "^3.0.2", + "ts-node": "^9.1.1", "ts-type": "^1.2.16", "typescript": "^4.0.2" }, diff --git a/services/gunDB/Mediator/index.js b/services/gunDB/Mediator/index.js index e4aba797..e7914de8 100644 --- a/services/gunDB/Mediator/index.js +++ b/services/gunDB/Mediator/index.js @@ -26,23 +26,13 @@ const SEAx = require('gun/sea') /** @type {import('../contact-api/SimpleGUN').ISEA} */ const mySEA = {} -const $$__SHOCKWALLET__MSG__ = '$$__SHOCKWALLET__MSG__' +// Avoid this: https://github.com/amark/gun/issues/804 and any other issues const $$__SHOCKWALLET__ENCRYPTED__ = '$$_SHOCKWALLET__ENCRYPTED__' +const $$__SHOCKWALLET__MSG__ = '$$__SHOCKWALLET__MSG__' +const $$__SHOCKWALLET__NUMBER__ = '$$__SHOCKWALLET__NUMBER__' +const $$__SHOCKWALLET__BOOLEAN__ = '$$__SHOCKWALLET__BOOLEAN__' mySEA.encrypt = (msg, secret) => { - if (typeof msg !== 'string') { - throw new TypeError( - 'mySEA.encrypt() -> expected msg to be an string instead got: ' + - typeof msg - ) - } - - if (msg.length === 0) { - throw new TypeError( - 'mySEA.encrypt() -> expected msg to be a populated string' - ) - } - if (typeof secret !== 'string') { throw new TypeError( `mySEA.encrypt() -> expected secret to be a an string, args: |msg| -- ${JSON.stringify( @@ -57,15 +47,35 @@ mySEA.encrypt = (msg, secret) => { ) } - // Avoid this: https://github.com/amark/gun/issues/804 and any other issues - const sanitizedMsg = $$__SHOCKWALLET__MSG__ + msg + let strToEncode = '' - return SEAx.encrypt(sanitizedMsg, secret).then(encMsg => { + if (typeof msg === 'string') { + if (msg.length === 0) { + throw new TypeError( + 'mySEA.encrypt() -> expected msg to be a populated string' + ) + } + + strToEncode = $$__SHOCKWALLET__MSG__ + msg + } else if (typeof msg === 'boolean') { + strToEncode = $$__SHOCKWALLET__BOOLEAN__ + msg + } else if (typeof msg === 'number') { + strToEncode = $$__SHOCKWALLET__NUMBER__ + msg + } else { + throw new TypeError('mySea.encrypt() -> Not a valid msg type.') + } + + return SEAx.encrypt(strToEncode, secret).then(encMsg => { return $$__SHOCKWALLET__ENCRYPTED__ + encMsg }) } -mySEA.decrypt = (encMsg, secret) => { +/** + * @param {string} encMsg + * @param {string} secret + * @returns {Promise} + */ +const decryptBase = (encMsg, secret) => { if (typeof encMsg !== 'string') { throw new TypeError( 'mySEA.encrypt() -> expected encMsg to be an string instead got: ' + @@ -104,10 +114,41 @@ mySEA.decrypt = (encMsg, secret) => { throw new TypeError('Could not decrypt') } - return decodedMsg.slice($$__SHOCKWALLET__MSG__.length) + if (decodedMsg.startsWith($$__SHOCKWALLET__MSG__)) { + return decodedMsg.slice($$__SHOCKWALLET__MSG__.length) + } else if (decodedMsg.startsWith($$__SHOCKWALLET__BOOLEAN__)) { + const dec = decodedMsg.slice($$__SHOCKWALLET__BOOLEAN__.length) + if (dec === 'true') { + return true + } else if (dec === 'false') { + return false + } + throw new Error('Could not decrypt boolean value.') + } else if (decodedMsg.startsWith($$__SHOCKWALLET__NUMBER__)) { + return Number(decodedMsg.slice($$__SHOCKWALLET__NUMBER__.length)) + } + + throw new TypeError( + `mySea.encrypt() -> Unexpected type of prefix found inside decrypted value, first 20 characters: ${decodedMsg.slice( + 0, + 20 + )}` + ) }) } +mySEA.decrypt = (encMsg, secret) => { + return decryptBase(encMsg, secret) +} + +mySEA.decryptNumber = (encMsg, secret) => { + return decryptBase(encMsg, secret) +} + +mySEA.decryptBoolean = (encMsg, secret) => { + return decryptBase(encMsg, secret) +} + mySEA.secret = async (recipientOrSenderEpub, recipientOrSenderSEA) => { if (typeof recipientOrSenderEpub !== 'string') { throw new TypeError( diff --git a/services/gunDB/contact-api/SimpleGUN.ts b/services/gunDB/contact-api/SimpleGUN.ts index 6d04fdf7..fee7b196 100644 --- a/services/gunDB/contact-api/SimpleGUN.ts +++ b/services/gunDB/contact-api/SimpleGUN.ts @@ -120,8 +120,19 @@ export interface UserGUNNode extends GUNNode { } export interface ISEA { - encrypt(message: string, senderSecret: string): Promise + encrypt( + message: string | number | boolean, + senderSecret: string + ): Promise decrypt(encryptedMessage: string, recipientSecret: string): Promise + decryptNumber( + encryptedMessage: string, + recipientSecret: string + ): Promise + decryptBoolean( + encryptedMessage: string, + recipientSecret: string + ): Promise secret( recipientOrSenderEpub: string, recipientOrSenderUserPair: UserPair diff --git a/src/__gun__tests__/mySea.ts b/src/__gun__tests__/mySea.ts new file mode 100644 index 00000000..f2f2af21 --- /dev/null +++ b/src/__gun__tests__/mySea.ts @@ -0,0 +1,109 @@ +/** + * @format + */ +import Gun from 'gun' +import uuid from 'uuid/v1' + +import { mySEA } from '../../services/gunDB/Mediator' +import { UserGUNNode } from '../../services/gunDB/contact-api/SimpleGUN' + +const setupUser = async (): Promise<[UserGUNNode]> => { + const gun = Gun({ + file: 'GUN-TEST-' + uuid() + }) + + const user = (gun.user() as unknown) as UserGUNNode + + await new Promise((res, rej) => { + user.create('testAlias-' + uuid(), 'testPass', ack => { + if (typeof ack.err === 'string') { + rej(new Error(ack.err)) + } else { + res() + } + }) + }) + + return [user] +} + +const encryptsDecryptsStrings = async () => { + const [user] = await setupUser() + + const stringMessage = 'Lorem ipsum dolor' + + const sec = await mySEA.secret(user._.sea.epub, user._.sea) + const encrypted = await mySEA.encrypt(stringMessage, sec) + const decrypted = await mySEA.decrypt(encrypted, sec) + + if (decrypted !== stringMessage) { + throw new Error() + } +} + +const encryptsDecryptsBooleans = async () => { + const [user] = await setupUser() + + const truth = true + const lie = false + + const sec = await mySEA.secret(user._.sea.epub, user._.sea) + + const encryptedTruth = await mySEA.encrypt(truth, sec) + const decryptedTruth = await mySEA.decryptBoolean(encryptedTruth, sec) + + if (decryptedTruth !== truth) { + throw new Error() + } + + const encryptedLie = await mySEA.encrypt(lie, sec) + const decryptedLie = await mySEA.decryptBoolean(encryptedLie, sec) + + if (decryptedLie !== lie) { + throw new Error( + `Expected false got: ${decryptedLie} - ${typeof decryptedLie}` + ) + } +} + +const encryptsDecryptsNumbers = async () => { + const [user] = await setupUser() + + const number = Math.random() * 999999 + + const sec = await mySEA.secret(user._.sea.epub, user._.sea) + const encrypted = await mySEA.encrypt(number, sec) + const decrypted = await mySEA.decryptNumber(encrypted, sec) + + if (decrypted !== number) { + throw new Error() + } +} + +const encryptsDecryptsZero = async () => { + const [user] = await setupUser() + + const zero = 0 + + const sec = await mySEA.secret(user._.sea.epub, user._.sea) + const encrypted = await mySEA.encrypt(zero, sec) + const decrypted = await mySEA.decryptNumber(encrypted, sec) + + if (decrypted !== zero) { + throw new Error() + } +} + +const runAllTests = async () => { + await encryptsDecryptsStrings() + await encryptsDecryptsBooleans() + await encryptsDecryptsNumbers() + await encryptsDecryptsZero() + + console.log('\n--------------------------------') + console.log('All tests ran successfully') + console.log('--------------------------------\n') + process.exit(0) +} + +runAllTests() diff --git a/src/cors.js b/src/cors.js index e3bb309e..7a0bd24b 100644 --- a/src/cors.js +++ b/src/cors.js @@ -1,8 +1,9 @@ const setAccessControlHeaders = (req, res) => { res.header("Access-Control-Allow-Origin", "*"); + res.header("Access-Control-Allow-Methods", "OPTIONS,POST,GET,PUT,DELETE") res.header( "Access-Control-Allow-Headers", - "Origin, X-Requested-With, Content-Type, Accept, Authorization" + "Origin, X-Requested-With, Content-Type, Accept, Authorization, public-key-for-decryption" ); }; diff --git a/src/routes.js b/src/routes.js index 3801a4f7..c52b7757 100644 --- a/src/routes.js +++ b/src/routes.js @@ -1124,7 +1124,7 @@ module.exports = async ( app.get('/api/lnd/listchannels', async (_, res) => { try { return res.json({ - channels: await LV2.listChannels() + channels: await LV2.listChannels({ active_only: false }) }) } catch (e) { console.log(e) diff --git a/yarn.lock b/yarn.lock index beb3a3bf..6b9685a7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1026,6 +1026,11 @@ are-we-there-yet@~1.1.2: delegates "^1.0.0" readable-stream "^2.0.6" +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -1992,6 +1997,11 @@ create-error-class@^3.0.0: dependencies: capture-stack-trace "^1.0.0" +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + cross-spawn@^5.0.1: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" @@ -2195,6 +2205,11 @@ diff-sequences@^24.9.0: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5" integrity sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew== +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + doctrine@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" @@ -4701,6 +4716,11 @@ make-dir@^2.1.0: pify "^4.0.1" semver "^5.6.0" +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + makeerror@1.0.x: version "1.0.11" resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" @@ -6058,6 +6078,13 @@ rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.3: dependencies: glob "^7.1.3" +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + rsvp@^4.8.4: version "4.8.5" resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" @@ -6409,6 +6436,14 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" +source-map-support@^0.5.17: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map-support@^0.5.6: version "0.5.13" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" @@ -6895,6 +6930,18 @@ triple-beam@^1.2.0, triple-beam@^1.3.0: resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw== +ts-node@^9.1.1: + version "9.1.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.1.1.tgz#51a9a450a3e959401bda5f004a72d54b936d376d" + integrity sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg== + dependencies: + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + source-map-support "^0.5.17" + yn "3.1.1" + ts-toolbelt@^6.6.2: version "6.7.7" resolved "https://registry.yarnpkg.com/ts-toolbelt/-/ts-toolbelt-6.7.7.tgz#4d7f34cafd519bf0f1d6da5a4b07889db6ba0184" @@ -7495,3 +7542,8 @@ yeast@0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk= + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==