feat: Encrypt oplog payload.

This commit is contained in:
Hayden Young 2024-05-23 15:24:49 +01:00
parent 90f66cfe5d
commit f272d3ee3c
4 changed files with 46 additions and 49 deletions

View File

@ -7,7 +7,6 @@
* @augments module:Databases~Database
*/
import Database from '../database.js'
import { toPayload, fromPayload } from './utils/payload.js'
const type = 'events'
@ -16,7 +15,7 @@ const type = 'events'
* @return {module:Databases.Databases-Events} A Events function.
* @memberof module:Databases
*/
const Events = () => async ({ ipfs, identity, address, name, access, directory, meta, headsStorage, entryStorage, indexStorage, referencesCount, syncAutomatically, onUpdate, encrypt }) => {
const Events = () => async ({ ipfs, identity, address, name, access, directory, meta, headsStorage, entryStorage, indexStorage, referencesCount, syncAutomatically, onUpdate }) => {
const database = await Database({ ipfs, identity, address, name, access, directory, meta, headsStorage, entryStorage, indexStorage, referencesCount, syncAutomatically, onUpdate })
const { addOperation, log } = database
@ -30,8 +29,7 @@ const Events = () => async ({ ipfs, identity, address, name, access, directory,
* @instance
*/
const add = async (value) => {
const payload = await toPayload('ADD', null, value, { encrypt })
return addOperation(payload)
return addOperation({ op: 'ADD', key: null, value })
}
/**
@ -44,9 +42,7 @@ const Events = () => async ({ ipfs, identity, address, name, access, directory,
*/
const get = async (hash) => {
const entry = await log.get(hash)
const { value } = await fromPayload(entry.payload, { encrypt })
return value
return entry.payload.value
}
/**
@ -70,8 +66,7 @@ const Events = () => async ({ ipfs, identity, address, name, access, directory,
const it = log.iterator({ gt, gte, lt, lte, amount })
for await (const event of it) {
const hash = event.hash
const payload = await fromPayload(event.payload, { encrypt })
const value = payload.value
const value = event.payload.value
yield { hash, value }
}
}

View File

@ -1,39 +0,0 @@
/**
*/
const toPayload = async (op, key, value, { encrypt }) => {
let payload = { op, key, value }
if (encrypt) {
if (encrypt.data) {
payload.value = await encrypt.data.encryptFn(value)
}
if (encrypt.op) {
payload = await encrypt.op.encryptFn(JSON.stringify(payload))
}
}
return payload
}
const fromPayload = async (payload, { encrypt }) => {
if (encrypt) {
if (encrypt.op) {
payload = JSON.parse(await encrypt.op.decryptFn(payload))
}
if (encrypt.data) {
payload.value = await encrypt.data.decryptFn(payload.value)
}
}
const { op, key, value } = payload
return { op, key, value }
}
export {
toPayload,
fromPayload
}

View File

@ -56,7 +56,7 @@ const DefaultAccessController = async () => {
* @memberof module:Log
* @instance
*/
const Log = async (identity, { logId, logHeads, access, entryStorage, headsStorage, indexStorage, sortFn } = {}) => {
const Log = async (identity, { logId, logHeads, access, entryStorage, headsStorage, indexStorage, sortFn, encryption } = {}) => {
/**
* @namespace Log
* @description The instance returned by {@link module:Log}
@ -83,6 +83,9 @@ const Log = async (identity, { logId, logHeads, access, entryStorage, headsStora
// Conflict-resolution sorting function
sortFn = NoZeroes(sortFn || LastWriteWins)
encryption = encryption || {}
const { encryptPayloadFn, decryptPayloadFn } = encryption
// Internal queues for processing appends and joins in their call-order
const appendQueue = new PQueue({ concurrency: 1 })
const joinQueue = new PQueue({ concurrency: 1 })
@ -139,6 +142,10 @@ const Log = async (identity, { logId, logHeads, access, entryStorage, headsStora
if (bytes) {
const entry = await Entry.decode(bytes)
if (decryptPayloadFn) {
entry.payload = JSON.parse(await decryptPayloadFn(entry.payload))
}
return entry
}
}
@ -172,6 +179,10 @@ const Log = async (identity, { logId, logHeads, access, entryStorage, headsStora
// (skips the heads which are covered by the next field)
const refs = await getReferences(heads_, options.referencesCount + heads_.length)
if (encryptPayloadFn) {
data = await encryptPayloadFn(JSON.stringify(data))
}
// Create the entry
const entry = await Entry.create(
identity,

View File

@ -3,6 +3,7 @@ import { rimraf } from 'rimraf'
import { copy } from 'fs-extra'
import { Log, Entry, Identities, KeyStore, MemoryStorage } from '../../src/index.js'
import testKeysPath from '../fixtures/test-keys-path.js'
import { encrypt, decrypt } from '../utils/encrypt.js'
const { create } = Entry
@ -142,5 +143,34 @@ describe('Log', function () {
strictEqual(values[1].payload, 'hello2')
strictEqual(values[2].payload, 'hello3')
})
it('encrypts a log entry when the payload is a string', async () => {
const keys = await keystore.createKey('hello1')
const privateKey = await keystore.getKey('hello1')
const publicKey = await keystore.getPublic(keys)
const encryptPayloadFn = encrypt({ publicKey })
const decryptPayloadFn = decrypt({ privateKey })
const log = await Log(testIdentity, { encryption: { encryptPayloadFn, decryptPayloadFn } })
const entry = await log.append('hello1')
const value = await log.get(entry.hash)
strictEqual(value.payload, 'hello1')
})
it('encrypts a log entry when the payload is an object', async () => {
const keys = await keystore.createKey('hello1')
const privateKey = await keystore.getKey('hello1')
const publicKey = await keystore.getPublic(keys)
const encryptPayloadFn = encrypt({ publicKey })
const decryptPayloadFn = decrypt({ privateKey })
const log = await Log(testIdentity, { encryption: { encryptPayloadFn, decryptPayloadFn } })
const entry = await log.append({ test: 'hello1' })
const value = await log.get(entry.hash)
deepStrictEqual(value.payload, { test: 'hello1' })
})
})
})