Skip to content

Commit e922402

Browse files
authoredMar 26, 2025··
Use async hypercore-encryption module (#676)
* use async hypercore-block-encryption module * standard fix * update encryption module api * instantiate legacy provider as required * clearer naming * massage args * wait for encryption ready * method to dynamically set encryption * kill encryption key prop * setEncryption only accepts encryption provider * use hypercore-encryption dep * fix typo * use encryption module helper
1 parent b89d070 commit e922402

File tree

6 files changed

+95
-108
lines changed

6 files changed

+95
-108
lines changed
 

‎README.md

-4
Original file line numberDiff line numberDiff line change
@@ -425,10 +425,6 @@ In contrast to `core.key` this key does not allow you to verify the data but can
425425

426426
Populated after `ready` has been emitted. Will be `null` before the event.
427427

428-
#### `core.encryptionKey`
429-
430-
Buffer containing the optional block encryption key of this core. Will be `null` unless block encryption is enabled.
431-
432428
#### `core.length`
433429

434430
How many blocks of data are available on this core.

‎index.js

+53-13
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@ const isOptions = require('is-options')
33
const crypto = require('hypercore-crypto')
44
const CoreStorage = require('hypercore-storage')
55
const c = require('compact-encoding')
6+
const sodium = require('sodium-universal')
67
const b4a = require('b4a')
78
const NoiseSecretStream = require('@hyperswarm/secret-stream')
9+
const HypercoreEncryption = require('hypercore-encryption')
810
const Protomux = require('protomux')
911
const id = require('hypercore-id-encoding')
1012
const safetyCatch = require('safety-catch')
1113
const unslab = require('unslab')
1214

1315
const Core = require('./lib/core')
14-
const BlockEncryption = require('./lib/block-encryption')
1516
const Info = require('./lib/info')
1617
const Download = require('./lib/download')
1718
const caps = require('./lib/caps')
@@ -149,7 +150,7 @@ class Hypercore extends EventEmitter {
149150
}
150151

151152
static blockEncryptionKey (key, encryptionKey) {
152-
return BlockEncryption.blockEncryptionKey(key, encryptionKey)
153+
return HypercoreEncryption.blockEncryptionKey(key, encryptionKey)
153154
}
154155

155156
static getProtocolMuxer (stream) {
@@ -233,10 +234,25 @@ class Hypercore extends EventEmitter {
233234
return s
234235
}
235236

236-
async setEncryptionKey (encryptionKey, opts) {
237+
setEncryptionKey (encryptionKey, opts) {
238+
const encryption = this._getLegacyEncryption(encryptionKey, !!(opts && opts.block))
239+
return this.setEncryption(encryption, opts)
240+
}
241+
242+
async setEncryption (encryption, opts) {
237243
if (!this.opened) await this.opening
238244
if (this.core.unencrypted) return
239-
this.encryption = encryptionKey ? new BlockEncryption(encryptionKey, this.key, { compat: this.core.compat, ...opts }) : null
245+
246+
if (encryption === null) {
247+
this.encryption = encryption
248+
return
249+
}
250+
251+
if (!HypercoreEncryption.isHypercoreEncryption(encryption)) {
252+
throw new Error('Expected hypercore encryption provider')
253+
}
254+
255+
this.encryption = encryption
240256
if (!this.core.encryption) this.core.encryption = this.encryption
241257
}
242258

@@ -315,7 +331,12 @@ class Hypercore extends EventEmitter {
315331

316332
if (!this.core.encryption && !this.core.unencrypted) {
317333
const e = getEncryptionOption(opts)
318-
if (e) this.core.encryption = new BlockEncryption(e.key, this.key, { compat: this.core.compat, ...e })
334+
335+
if (HypercoreEncryption.isHypercoreEncryption(e)) {
336+
this.core.encryption = e
337+
} else if (e) {
338+
this.core.encryption = this._getLegacyEncryption(e.key, e.block)
339+
}
319340
}
320341

321342
const parent = opts.parent || null
@@ -576,10 +597,6 @@ class Hypercore extends EventEmitter {
576597
return this.opened === false ? [] : this.core.replicator.peers
577598
}
578599

579-
get encryptionKey () {
580-
return this.encryption && this.encryption.key
581-
}
582-
583600
get padding () {
584601
return this.encryption === null ? 0 : this.encryption.padding
585602
}
@@ -758,7 +775,7 @@ class Hypercore extends EventEmitter {
758775

759776
if (this.encryption.compat !== this.core.compat) this._updateEncryption()
760777
if (this.core.unencrypted) this.encryption = null
761-
else this.encryption.decrypt(index, block)
778+
else await this.encryption.decrypt(index, block)
762779
}
763780

764781
return this._decode(encoding, block)
@@ -907,6 +924,7 @@ class Hypercore extends EventEmitter {
907924
blocks = Array.isArray(blocks) ? blocks : [blocks]
908925

909926
const preappend = this.core.unencrypted ? null : (this.encryption && this._preappend)
927+
if (preappend) await this.encryption.ready()
910928

911929
const buffers = this.encodeBatch !== null ? this.encodeBatch(blocks) : new Array(blocks.length)
912930

@@ -1037,9 +1055,22 @@ class Hypercore extends EventEmitter {
10371055

10381056
_updateEncryption () {
10391057
const e = this.encryption
1040-
this.encryption = new BlockEncryption(e.key, this.key, { compat: this.core.compat, block: b4a.equals(e.blockKey, e.key) })
1058+
if (HypercoreEncryption.isHypercoreEncryption(e)) return
1059+
1060+
this.encryption = this._getLegacyEncryption(e.key, e.block)
1061+
10411062
if (e === this.core.encryption) this.core.encryption = this.encryption
10421063
}
1064+
1065+
_getLegacyEncryption (encryptionKey, block) {
1066+
if (!encryptionKey) return null
1067+
1068+
const blockKey = block
1069+
? encryptionKey
1070+
: getLegacyBlockKey(this.key, encryptionKey, this.core.compat)
1071+
1072+
return HypercoreEncryption.createLegacyProvider(encryptionKey, blockKey)
1073+
}
10431074
}
10441075

10451076
module.exports = Hypercore
@@ -1052,14 +1083,14 @@ function toHex (buf) {
10521083
return buf && b4a.toString(buf, 'hex')
10531084
}
10541085

1055-
function preappend (blocks) {
1086+
async function preappend (blocks) {
10561087
const offset = this.state.length
10571088
const fork = this.state.encryptionFork
10581089

10591090
if (this.encryption.compat !== this.core.compat) this._updateEncryption()
10601091

10611092
for (let i = 0; i < blocks.length; i++) {
1062-
this.encryption.encrypt(offset + i, blocks[i], fork)
1093+
await this.encryption.encrypt(offset + i, blocks[i], fork)
10631094
}
10641095
}
10651096

@@ -1126,3 +1157,12 @@ function getEncryptionOption (opts) {
11261157
if (!opts.encryption) return null
11271158
return b4a.isBuffer(opts.encryption) ? { key: opts.encryption } : opts.encryption
11281159
}
1160+
1161+
function getLegacyBlockKey (hypercoreKey, encryptionKey, compat) {
1162+
const key = b4a.alloc(HypercoreEncryption.KEYBYTES)
1163+
1164+
if (compat) sodium.crypto_generichash_batch(key, [encryptionKey], hypercoreKey)
1165+
else sodium.crypto_generichash_batch(key, [caps.LEGACY_BLOCK_ENCRYPTION, hypercoreKey, encryptionKey])
1166+
1167+
return key
1168+
}

‎lib/block-encryption.js

-80
This file was deleted.

‎lib/caps.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ const [
1212
REPLICATE_RESPONDER,
1313
MANIFEST,
1414
DEFAULT_NAMESPACE,
15-
BLOCK_ENCRYPTION
15+
LEGACY_BLOCK_ENCRYPTION
1616
] = crypto.namespace('hypercore', 6)
1717

1818
exports.MANIFEST = MANIFEST
1919
exports.DEFAULT_NAMESPACE = DEFAULT_NAMESPACE
20-
exports.BLOCK_ENCRYPTION = BLOCK_ENCRYPTION
20+
exports.LEGACY_BLOCK_ENCRYPTION = LEGACY_BLOCK_ENCRYPTION
2121

2222
exports.replicate = function (isInitiator, key, handshakeHash) {
2323
const out = b4a.allocUnsafe(32)

‎package.json

+1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"fast-fifo": "^1.3.0",
5050
"flat-tree": "^1.9.0",
5151
"hypercore-crypto": "^3.2.1",
52+
"hypercore-encryption": "^1.0.0",
5253
"hypercore-errors": "^1.2.0",
5354
"hypercore-id-encoding": "^1.2.0",
5455
"hypercore-storage": "^1.0.0",

‎test/encryption.js

+39-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const test = require('brittle')
22
const b4a = require('b4a')
3+
const HypercoreEncryption = require('hypercore-encryption')
34
const Hypercore = require('..')
45
const { create, createStorage, replicate } = require('./helpers')
56

@@ -8,7 +9,7 @@ const encryptionKey = b4a.alloc(32, 'hello world')
89
test('encrypted append and get', async function (t) {
910
const a = await create(t, { encryption: { key: encryptionKey } })
1011

11-
t.alike(a.encryptionKey, encryptionKey)
12+
t.ok(a.encryption)
1213

1314
await a.append(['hello'])
1415

@@ -151,8 +152,8 @@ test('encrypted session on unencrypted core', async function (t) {
151152

152153
const s = a.session({ encryption: { key: encryptionKey }, debug: 'debug' })
153154

154-
t.alike(s.encryptionKey, encryptionKey)
155-
t.unlike(s.encryptionKey, a.encryptionKey)
155+
t.ok(s.encryption)
156+
t.absent(a.encryption)
156157

157158
await s.append(['hello'])
158159

@@ -200,7 +201,7 @@ test('encrypted core from existing unencrypted core', async function (t) {
200201
const b = new Hypercore({ core: a.core, encryption: { key: encryptionKey } })
201202

202203
t.alike(b.key, a.key)
203-
t.alike(b.encryptionKey, encryptionKey)
204+
t.alike(b.encryption, a.core.encryption)
204205

205206
await b.append(['hello'])
206207

@@ -221,9 +222,9 @@ test('from session sessions pass encryption', async function (t) {
221222
await b.ready()
222223
await c.ready()
223224

224-
t.absent(a.encryptionKey)
225-
t.ok(b.encryptionKey)
226-
t.ok(c.encryptionKey)
225+
t.absent(a.encryption)
226+
t.ok(b.encryption)
227+
t.ok(c.encryption)
227228

228229
await c.close()
229230
await b.close()
@@ -237,12 +238,41 @@ test('session keeps encryption', async function (t) {
237238
const b = a.session({ encryption: { key: encryptionKey } })
238239
await b.ready()
239240

240-
t.alike(b.encryptionKey, encryptionKey)
241-
242241
await b.close()
243242
await a.close()
244243
})
245244

245+
test('block encryption module', async function (t) {
246+
const blindingKey = b4a.alloc(32, 0)
247+
248+
const encryption = new HypercoreEncryption(blindingKey, getKey, { id: 1 })
249+
250+
await encryption.ready()
251+
252+
const core = await create(t, null, { encryption })
253+
await core.ready()
254+
255+
await core.append('0')
256+
257+
await encryption.load(2)
258+
259+
await core.append('1')
260+
await core.append('2')
261+
262+
t.alike(await core.get(0), b4a.from('0'))
263+
t.alike(await core.get(1), b4a.from('1'))
264+
t.alike(await core.get(2), b4a.from('2'))
265+
266+
async function getKey (id) {
267+
await Promise.resolve()
268+
return {
269+
version: 1,
270+
key: b4a.alloc(32, id),
271+
padding: 16
272+
}
273+
}
274+
})
275+
246276
function getBlock (core, index) {
247277
const batch = core.core.storage.read()
248278
const b = batch.getBlock(index)

0 commit comments

Comments
 (0)
Please sign in to comment.