feat: Basic asymmetric encryption using an OrbitDB identity.

This commit is contained in:
Hayden Young 2024-05-01 15:44:57 +01:00
parent bd1eb71e44
commit ed2c5e7d8c
5 changed files with 1309 additions and 13 deletions

1278
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -20,6 +20,7 @@
"dependencies": {
"@ipld/dag-cbor": "^9.0.6",
"@libp2p/crypto": "^3.0.2",
"eth-crypto": "^2.6.0",
"it-pipe": "^3.0.1",
"level": "^8.0.0",
"lru": "^3.1.0",

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, encryptFn, decryptFn } = {}) => {
/**
* @namespace Log
* @description The instance returned by {@link module:Log}
@ -82,6 +82,7 @@ const Log = async (identity, { logId, logHeads, access, entryStorage, headsStora
const _heads = await Heads({ storage: headsStorage, heads: logHeads })
// Conflict-resolution sorting function
sortFn = NoZeroes(sortFn || LastWriteWins)
// Internal queues for processing appends and joins in their call-order
const appendQueue = new PQueue({ concurrency: 1 })
const joinQueue = new PQueue({ concurrency: 1 })
@ -137,6 +138,11 @@ const Log = async (identity, { logId, logHeads, access, entryStorage, headsStora
const bytes = await _entries.get(hash)
if (bytes) {
const entry = await Entry.decode(bytes)
if (decryptFn) {
entry.payload = await decryptFn(entry.payload)
}
return entry
}
}
@ -169,6 +175,11 @@ const Log = async (identity, { logId, logHeads, access, entryStorage, headsStora
// Get references (pointers) to multiple entries in the past
// (skips the heads which are covered by the next field)
const refs = await getReferences(heads_, options.referencesCount + heads_.length)
if (encryptFn) {
data = await encryptFn(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,14 @@ describe('Log', function () {
strictEqual(values[1].payload, 'hello2')
strictEqual(values[2].payload, 'hello3')
})
it('encrypts the value of an entry in the log', async () => {
const encryptFn = encrypt({ identity: testIdentity })
const decryptFn = decrypt({ identities, identity: testIdentity })
const log = await Log(testIdentity, { encryptFn, decryptFn })
const entry = await log.append('hello1')
const value = await log.get(entry.hash)
strictEqual(value.payload, 'hello1')
})
})
})

20
test/utils/encrypt.js Normal file
View File

@ -0,0 +1,20 @@
import EthCrypto from 'eth-crypto'
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
const encrypt = ({ identity }) => async (value) => {
const encryptedObj = await EthCrypto.encryptWithPublicKey(identity.publicKey, value)
return EthCrypto.cipher.stringify(encryptedObj)
}
const decrypt = ({ identities, identity }) => async (value) => {
const privateKey = await identities.keystore.getKey(identity.id)
const privateKeyStr = uint8ArrayToString(privateKey.marshal(), 'base16')
const encryptedObj = EthCrypto.cipher.parse(value)
return await EthCrypto.decryptWithPrivateKey(privateKeyStr, encryptedObj)
}
export {
encrypt,
decrypt
}