encrypt/decrypt non-string values

This commit is contained in:
Daniel Lugo 2021-01-18 13:35:18 -04:00
parent f7639ecc8e
commit 0af1975321
4 changed files with 183 additions and 21 deletions

View file

@ -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",

View file

@ -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<any>}
*/
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(

View file

@ -120,8 +120,19 @@ export interface UserGUNNode extends GUNNode {
}
export interface ISEA {
encrypt(message: string, senderSecret: string): Promise<string>
encrypt(
message: string | number | boolean,
senderSecret: string
): Promise<string>
decrypt(encryptedMessage: string, recipientSecret: string): Promise<string>
decryptNumber(
encryptedMessage: string,
recipientSecret: string
): Promise<number>
decryptBoolean(
encryptedMessage: string,
recipientSecret: string
): Promise<boolean>
secret(
recipientOrSenderEpub: string,
recipientOrSenderUserPair: UserPair

109
src/__gun__tests__/mySea.ts Normal file
View file

@ -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<void>((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()