hypercore 11.0.47 → 11.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -425,10 +425,6 @@ In contrast to `core.key` this key does not allow you to verify the data but can
425
425
 
426
426
  Populated after `ready` has been emitted. Will be `null` before the event.
427
427
 
428
- #### `core.encryptionKey`
429
-
430
- Buffer containing the optional block encryption key of this core. Will be `null` unless block encryption is enabled.
431
-
432
428
  #### `core.length`
433
429
 
434
430
  How many blocks of data are available on this core.
package/index.js CHANGED
@@ -3,15 +3,16 @@ const isOptions = require('is-options')
3
3
  const crypto = require('hypercore-crypto')
4
4
  const CoreStorage = require('hypercore-storage')
5
5
  const c = require('compact-encoding')
6
+ const sodium = require('sodium-universal')
6
7
  const b4a = require('b4a')
7
8
  const NoiseSecretStream = require('@hyperswarm/secret-stream')
9
+ const HypercoreEncryption = require('hypercore-encryption')
8
10
  const Protomux = require('protomux')
9
11
  const id = require('hypercore-id-encoding')
10
12
  const safetyCatch = require('safety-catch')
11
13
  const unslab = require('unslab')
12
14
 
13
15
  const Core = require('./lib/core')
14
- const BlockEncryption = require('./lib/block-encryption')
15
16
  const Info = require('./lib/info')
16
17
  const Download = require('./lib/download')
17
18
  const caps = require('./lib/caps')
@@ -149,7 +150,7 @@ class Hypercore extends EventEmitter {
149
150
  }
150
151
 
151
152
  static blockEncryptionKey (key, encryptionKey) {
152
- return BlockEncryption.blockEncryptionKey(key, encryptionKey)
153
+ return HypercoreEncryption.blockEncryptionKey(key, encryptionKey)
153
154
  }
154
155
 
155
156
  static getProtocolMuxer (stream) {
@@ -233,10 +234,24 @@ class Hypercore extends EventEmitter {
233
234
  return s
234
235
  }
235
236
 
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) {
237
243
  if (!this.opened) await this.opening
238
- if (this.core.unencrypted) return
239
- this.encryption = encryptionKey ? new BlockEncryption(encryptionKey, this.key, { compat: this.core.compat, ...opts }) : null
244
+
245
+ if (encryption === null) {
246
+ this.encryption = encryption
247
+ return
248
+ }
249
+
250
+ if (!HypercoreEncryption.isHypercoreEncryption(encryption)) {
251
+ throw new Error('Expected hypercore encryption provider')
252
+ }
253
+
254
+ this.encryption = encryption
240
255
  if (!this.core.encryption) this.core.encryption = this.encryption
241
256
  }
242
257
 
@@ -290,7 +305,7 @@ class Hypercore extends EventEmitter {
290
305
  if (this.state !== null) this.state.removeSession(this)
291
306
 
292
307
  this.closed = true
293
- this.emit('close', this.core.hasSession() === false)
308
+ this.emit('close')
294
309
  throw err
295
310
  }
296
311
 
@@ -313,9 +328,14 @@ class Hypercore extends EventEmitter {
313
328
 
314
329
  if (this.keyPair === null) this.keyPair = opts.keyPair || this.core.header.keyPair
315
330
 
316
- if (!this.core.encryption && !this.core.unencrypted) {
331
+ if (!this.core.encryption) {
317
332
  const e = getEncryptionOption(opts)
318
- if (e) this.core.encryption = new BlockEncryption(e.key, this.key, { compat: this.core.compat, ...e })
333
+
334
+ if (HypercoreEncryption.isHypercoreEncryption(e)) {
335
+ this.core.encryption = e
336
+ } else if (e) {
337
+ this.core.encryption = this._getLegacyEncryption(e.key, e.block)
338
+ }
319
339
  }
320
340
 
321
341
  const parent = opts.parent || null
@@ -476,14 +496,14 @@ class Hypercore extends EventEmitter {
476
496
  if (this.core.hasSession()) {
477
497
  // emit "fake" close as this is a session
478
498
  this.closed = true
479
- this.emit('close', false)
499
+ this.emit('close')
480
500
  return
481
501
  }
482
502
 
483
503
  if (this.core.autoClose) await this.core.close()
484
504
 
485
505
  this.closed = true
486
- this.emit('close', true)
506
+ this.emit('close')
487
507
  }
488
508
 
489
509
  async commit (session, opts) {
@@ -576,10 +596,6 @@ class Hypercore extends EventEmitter {
576
596
  return this.opened === false ? [] : this.core.replicator.peers
577
597
  }
578
598
 
579
- get encryptionKey () {
580
- return this.encryption && this.encryption.key
581
- }
582
-
583
599
  get padding () {
584
600
  return this.encryption === null ? 0 : this.encryption.padding
585
601
  }
@@ -757,8 +773,8 @@ class Hypercore extends EventEmitter {
757
773
  block = b4a.from(block)
758
774
 
759
775
  if (this.encryption.compat !== this.core.compat) this._updateEncryption()
760
- if (this.core.unencrypted) this.encryption = null
761
- else this.encryption.decrypt(index, block)
776
+
777
+ await this.encryption.decrypt(index, block)
762
778
  }
763
779
 
764
780
  return this._decode(encoding, block)
@@ -906,7 +922,8 @@ class Hypercore extends EventEmitter {
906
922
 
907
923
  blocks = Array.isArray(blocks) ? blocks : [blocks]
908
924
 
909
- const preappend = this.core.unencrypted ? null : (this.encryption && this._preappend)
925
+ const preappend = this.encryption && this._preappend
926
+ if (preappend) await this.encryption.ready()
910
927
 
911
928
  const buffers = this.encodeBatch !== null ? this.encodeBatch(blocks) : new Array(blocks.length)
912
929
 
@@ -1037,9 +1054,22 @@ class Hypercore extends EventEmitter {
1037
1054
 
1038
1055
  _updateEncryption () {
1039
1056
  const e = this.encryption
1040
- this.encryption = new BlockEncryption(e.key, this.key, { compat: this.core.compat, block: b4a.equals(e.blockKey, e.key) })
1057
+ if (HypercoreEncryption.isHypercoreEncryption(e)) return
1058
+
1059
+ this.encryption = this._getLegacyEncryption(e.key, e.block)
1060
+
1041
1061
  if (e === this.core.encryption) this.core.encryption = this.encryption
1042
1062
  }
1063
+
1064
+ _getLegacyEncryption (encryptionKey, block) {
1065
+ if (!encryptionKey) return null
1066
+
1067
+ const blockKey = block
1068
+ ? encryptionKey
1069
+ : getLegacyBlockKey(this.key, encryptionKey, this.core.compat)
1070
+
1071
+ return HypercoreEncryption.createLegacyProvider(blockKey)
1072
+ }
1043
1073
  }
1044
1074
 
1045
1075
  module.exports = Hypercore
@@ -1052,14 +1082,14 @@ function toHex (buf) {
1052
1082
  return buf && b4a.toString(buf, 'hex')
1053
1083
  }
1054
1084
 
1055
- function preappend (blocks) {
1085
+ async function preappend (blocks) {
1056
1086
  const offset = this.state.length
1057
1087
  const fork = this.state.encryptionFork
1058
1088
 
1059
1089
  if (this.encryption.compat !== this.core.compat) this._updateEncryption()
1060
1090
 
1061
1091
  for (let i = 0; i < blocks.length; i++) {
1062
- this.encryption.encrypt(offset + i, blocks[i], fork)
1092
+ await this.encryption.encrypt(offset + i, blocks[i], fork)
1063
1093
  }
1064
1094
  }
1065
1095
 
@@ -1126,3 +1156,12 @@ function getEncryptionOption (opts) {
1126
1156
  if (!opts.encryption) return null
1127
1157
  return b4a.isBuffer(opts.encryption) ? { key: opts.encryption } : opts.encryption
1128
1158
  }
1159
+
1160
+ function getLegacyBlockKey (hypercoreKey, encryptionKey, compat) {
1161
+ const key = b4a.alloc(HypercoreEncryption.KEYBYTES)
1162
+
1163
+ if (compat) sodium.crypto_generichash_batch(key, [encryptionKey], hypercoreKey)
1164
+ else sodium.crypto_generichash_batch(key, [caps.LEGACY_BLOCK_ENCRYPTION, hypercoreKey, encryptionKey])
1165
+
1166
+ return key
1167
+ }
package/lib/caps.js CHANGED
@@ -12,12 +12,12 @@ const [
12
12
  REPLICATE_RESPONDER,
13
13
  MANIFEST,
14
14
  DEFAULT_NAMESPACE,
15
- BLOCK_ENCRYPTION
15
+ LEGACY_BLOCK_ENCRYPTION
16
16
  ] = crypto.namespace('hypercore', 6)
17
17
 
18
18
  exports.MANIFEST = MANIFEST
19
19
  exports.DEFAULT_NAMESPACE = DEFAULT_NAMESPACE
20
- exports.BLOCK_ENCRYPTION = BLOCK_ENCRYPTION
20
+ exports.LEGACY_BLOCK_ENCRYPTION = LEGACY_BLOCK_ENCRYPTION
21
21
 
22
22
  exports.replicate = function (isInitiator, key, handshakeHash) {
23
23
  const out = b4a.allocUnsafe(32)
package/lib/core.js CHANGED
@@ -39,7 +39,6 @@ module.exports = class Core {
39
39
  this.verifier = null
40
40
  this.truncating = 0
41
41
  this.updating = false
42
- this.unencrypted = false
43
42
  this.skipBitfield = null
44
43
  this.globalCache = opts.globalCache || null
45
44
  this.autoClose = opts.autoClose !== false
@@ -271,7 +270,6 @@ module.exports = class Core {
271
270
  // to unslab
272
271
  if (header.manifest) {
273
272
  header.manifest = Verifier.createManifest(header.manifest)
274
- this.unencrypted = header.manifest.unencrypted
275
273
  }
276
274
 
277
275
  const verifier = header.manifest ? new Verifier(header.key, header.manifest, { crypto, legacy }) : null
@@ -326,7 +324,6 @@ module.exports = class Core {
326
324
  if (verifier.prologue) this.state.prologue = Object.assign({}, verifier.prologue)
327
325
 
328
326
  this.manifest = this.header.manifest = manifest
329
- this.unencrypted = this.manifest.unencrypted
330
327
 
331
328
  tx.setAuth({
332
329
  key: this.header.key,
package/lib/messages.js CHANGED
@@ -6,6 +6,10 @@ const unslab = require('unslab')
6
6
 
7
7
  const EMPTY = b4a.alloc(0)
8
8
 
9
+ const MANIFEST_PATCH = 0b00000001
10
+ const MANIFEST_PROLOGUE = 0b00000010
11
+ const MANIFEST_LINKED = 0b00000100
12
+
9
13
  const hashes = {
10
14
  preencode (state, m) {
11
15
  state.end++ // small uint
@@ -137,7 +141,7 @@ const manifestv0 = {
137
141
  hash: c.fixed32.decode(state),
138
142
  length: 0
139
143
  },
140
- unencrypted: false
144
+ linked: []
141
145
  }
142
146
  }
143
147
 
@@ -149,7 +153,7 @@ const manifestv0 = {
149
153
  quorum: 1,
150
154
  signers: [signer.decode(state)],
151
155
  prologue: null,
152
- unencrypted: false
156
+ linked: []
153
157
  }
154
158
  }
155
159
 
@@ -162,11 +166,13 @@ const manifestv0 = {
162
166
  quorum: c.uint.decode(state),
163
167
  signers: signerArray.decode(state),
164
168
  prologue: null,
165
- unencrypted: false
169
+ linked: []
166
170
  }
167
171
  }
168
172
  }
169
173
 
174
+ const fixed32Array = c.array(c.fixed32)
175
+
170
176
  const manifest = exports.manifest = {
171
177
  preencode (state, m) {
172
178
  state.end++ // version
@@ -178,37 +184,53 @@ const manifest = exports.manifest = {
178
184
  c.uint.preencode(state, m.quorum)
179
185
  signerArray.preencode(state, m.signers)
180
186
  if (m.prologue) prologue.preencode(state, m.prologue)
187
+
188
+ if (m.linked) {
189
+ fixed32Array.preencode(state, m.linked)
190
+ }
181
191
  },
182
192
  encode (state, m) {
183
193
  c.uint.encode(state, m.version)
184
194
  if (m.version === 0) return manifestv0.encode(state, m)
185
195
 
186
- c.uint.encode(state, (m.allowPatch ? 1 : 0) | (m.prologue ? 2 : 0) | (m.unencrypted ? 4 : 0))
196
+ let flags = 0
197
+ if (m.allowPatch) flags |= MANIFEST_PATCH
198
+ if (m.prologue) flags |= MANIFEST_PROLOGUE
199
+ if (m.linked) flags |= MANIFEST_LINKED
200
+
201
+ c.uint.encode(state, flags)
187
202
  hashes.encode(state, m.hash)
188
203
 
189
204
  c.uint.encode(state, m.quorum)
190
205
  signerArray.encode(state, m.signers)
191
206
  if (m.prologue) prologue.encode(state, m.prologue)
207
+
208
+ if (m.linked) {
209
+ fixed32Array.encode(state, m.linked)
210
+ }
192
211
  },
193
212
  decode (state) {
194
- const v = c.uint.decode(state)
195
- if (v === 0) return manifestv0.decode(state)
196
- if (v !== 1) throw new Error('Unknown version: ' + v)
213
+ const version = c.uint.decode(state)
214
+ if (version === 0) return manifestv0.decode(state)
215
+ if (version > 2) throw new Error('Unknown version: ' + version)
197
216
 
198
217
  const flags = c.uint.decode(state)
199
218
  const hash = hashes.decode(state)
200
219
  const quorum = c.uint.decode(state)
201
220
  const signers = signerArray.decode(state)
202
- const unencrypted = (flags & 4) !== 0
221
+
222
+ const hasPatch = (flags & MANIFEST_PATCH) !== 0
223
+ const hasPrologue = (flags & MANIFEST_PROLOGUE) !== 0
224
+ const hasLinked = (flags & MANIFEST_LINKED) !== 0
203
225
 
204
226
  return {
205
- version: 1,
227
+ version,
206
228
  hash,
207
- allowPatch: (flags & 1) !== 0,
229
+ allowPatch: hasPatch,
208
230
  quorum,
209
231
  signers,
210
- prologue: (flags & 2) === 0 ? null : prologue.decode(state),
211
- unencrypted
232
+ prologue: hasPrologue ? prologue.decode(state) : null,
233
+ linked: hasLinked ? fixed32Array.decode(state) : null
212
234
  }
213
235
  }
214
236
  }
package/lib/replicator.js CHANGED
@@ -1520,6 +1520,7 @@ module.exports = class Replicator {
1520
1520
  }
1521
1521
 
1522
1522
  this._downloadingTimer = setTimeout(setDownloadingLater, this._notDownloadingLinger, this, downloading)
1523
+ if (this._downloadingTimer.unref) this._downloadingTimer.unref()
1523
1524
  }
1524
1525
 
1525
1526
  setDownloadingNow (downloading) {
@@ -36,6 +36,7 @@ module.exports = class SessionState {
36
36
  this.signature = treeInfo.signature || null
37
37
 
38
38
  this.snapshotCompatLength = this.isSnapshot() ? Math.min(this.length, this.core.state.length) : -1
39
+ this.lastTruncation = null
39
40
 
40
41
  this.active = 0
41
42
 
@@ -230,10 +231,13 @@ module.exports = class SessionState {
230
231
  this._activeTx = null
231
232
 
232
233
  try {
233
- return await tx.flush()
234
+ if (!(await tx.flush())) return false
234
235
  } finally {
235
236
  this._clearActiveBatch()
236
237
  }
238
+
239
+ this.lastTruncation = null
240
+ return true
237
241
  }
238
242
 
239
243
  _precommit () {
@@ -246,6 +250,7 @@ module.exports = class SessionState {
246
250
  try {
247
251
  const bitfield = this._pendingBitfield
248
252
  this._pendingBitfield = null
253
+ this.lastTruncation = null
249
254
  await this.parent._oncommit(this, bitfield)
250
255
  } finally {
251
256
  this.commiting = false
@@ -576,6 +581,8 @@ module.exports = class SessionState {
576
581
  ontruncate (tree, to, from, flushed) {
577
582
  const bitfield = { start: to, length: from - to, drop: true }
578
583
 
584
+ this.lastTruncation = { from, to }
585
+
579
586
  if (!flushed) this._updateBitfield(bitfield)
580
587
  else if (this.isDefault()) this.core.ontruncate(tree, bitfield)
581
588
 
package/lib/verifier.js CHANGED
@@ -129,7 +129,7 @@ module.exports = class Verifier {
129
129
  }
130
130
 
131
131
  verify (batch, signature) {
132
- if (this.version !== 1) {
132
+ if (this.version === 0) {
133
133
  return this._verifyCompat(batch, signature)
134
134
  }
135
135
 
@@ -182,7 +182,8 @@ module.exports = class Verifier {
182
182
  namespace: caps.DEFAULT_NAMESPACE,
183
183
  publicKey
184
184
  }],
185
- prologue: null
185
+ prologue: null,
186
+ linked: null
186
187
  }
187
188
  }
188
189
 
@@ -195,13 +196,13 @@ module.exports = class Verifier {
195
196
  if (!inp) return null
196
197
 
197
198
  const manifest = {
198
- version: typeof inp.version === 'number' ? inp.version : 1,
199
+ version: getManifestVersion(inp), // only v2 if linked are present
199
200
  hash: 'blake2b',
200
201
  allowPatch: !!inp.allowPatch,
201
202
  quorum: defaultQuorum(inp),
202
203
  signers: inp.signers ? inp.signers.map(parseSigner) : [],
203
204
  prologue: null,
204
- unencrypted: !!inp.unencrypted
205
+ linked: null
205
206
  }
206
207
 
207
208
  if (inp.hash && inp.hash !== 'blake2b') throw BAD_ARGUMENT('Only Blake2b hashes are supported')
@@ -215,6 +216,18 @@ module.exports = class Verifier {
215
216
  manifest.prologue.hash = unslab(manifest.prologue.hash)
216
217
  }
217
218
 
219
+ if (inp.linked && inp.linked.length) {
220
+ if (manifest.version < 2) throw BAD_ARGUMENT('Invalid field')
221
+
222
+ for (const key of inp.linked) {
223
+ if (!(b4a.isBuffer(key) && key.byteLength === 32)) {
224
+ throw BAD_ARGUMENT('Invalid key')
225
+ }
226
+ }
227
+
228
+ manifest.linked = inp.linked
229
+ }
230
+
218
231
  return manifest
219
232
  }
220
233
 
@@ -311,3 +324,9 @@ function proofToVersion1 (proof) {
311
324
  patch: proof.patch ? proof.patch.length : 0
312
325
  }
313
326
  }
327
+
328
+ function getManifestVersion (inp) {
329
+ if (typeof inp.version === 'number') return inp.version
330
+ if (inp.linked && inp.linked.length) return 2
331
+ return 1
332
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypercore",
3
- "version": "11.0.47",
3
+ "version": "11.1.0",
4
4
  "description": "Hypercore is a secure, distributed append-only log",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -49,6 +49,7 @@
49
49
  "fast-fifo": "^1.3.0",
50
50
  "flat-tree": "^1.9.0",
51
51
  "hypercore-crypto": "^3.2.1",
52
+ "hypercore-encryption": "^1.0.0",
52
53
  "hypercore-errors": "^1.2.0",
53
54
  "hypercore-id-encoding": "^1.2.0",
54
55
  "hypercore-storage": "^1.0.0",
@@ -1,80 +0,0 @@
1
- const sodium = require('sodium-universal')
2
- const c = require('compact-encoding')
3
- const b4a = require('b4a')
4
- const { BLOCK_ENCRYPTION } = require('./caps')
5
-
6
- const nonce = b4a.alloc(sodium.crypto_stream_NONCEBYTES)
7
-
8
- module.exports = class BlockEncryption {
9
- constructor (encryptionKey, hypercoreKey, { block = false, compat = true } = {}) {
10
- const subKeys = b4a.alloc(2 * sodium.crypto_stream_KEYBYTES)
11
-
12
- this.key = encryptionKey
13
- this.blockKey = block ? encryptionKey : subKeys.subarray(0, sodium.crypto_stream_KEYBYTES)
14
- this.blindingKey = subKeys.subarray(sodium.crypto_stream_KEYBYTES)
15
- this.padding = 8
16
- this.compat = compat
17
-
18
- if (!block) {
19
- if (compat) sodium.crypto_generichash_batch(this.blockKey, [encryptionKey], hypercoreKey)
20
- else sodium.crypto_generichash_batch(this.blockKey, [BLOCK_ENCRYPTION, hypercoreKey, encryptionKey])
21
- }
22
-
23
- sodium.crypto_generichash(this.blindingKey, this.blockKey)
24
- }
25
-
26
- static blockEncryptionKey (hypercoreKey, encryptionKey) {
27
- const blockKey = b4a.alloc(sodium.crypto_stream_KEYBYTES)
28
- sodium.crypto_generichash_batch(blockKey, [BLOCK_ENCRYPTION, hypercoreKey, encryptionKey])
29
- return blockKey
30
- }
31
-
32
- encrypt (index, block, fork) {
33
- const padding = block.subarray(0, this.padding)
34
- block = block.subarray(this.padding)
35
-
36
- c.uint64.encode({ start: 0, end: 8, buffer: padding }, fork)
37
- c.uint64.encode({ start: 0, end: 8, buffer: nonce }, index)
38
-
39
- // Zero out any previous padding.
40
- nonce.fill(0, 8, 8 + padding.byteLength)
41
-
42
- // Blind the fork ID, possibly risking reusing the nonce on a reorg of the
43
- // Hypercore. This is fine as the blinding is best-effort and the latest
44
- // fork ID shared on replication anyway.
45
- sodium.crypto_stream_xor(
46
- padding,
47
- padding,
48
- nonce,
49
- this.blindingKey
50
- )
51
-
52
- nonce.set(padding, 8)
53
-
54
- // The combination of a (blinded) fork ID and a block index is unique for a
55
- // given Hypercore and is therefore a valid nonce for encrypting the block.
56
- sodium.crypto_stream_xor(
57
- block,
58
- block,
59
- nonce,
60
- this.blockKey
61
- )
62
- }
63
-
64
- decrypt (index, block) {
65
- const padding = block.subarray(0, this.padding)
66
- block = block.subarray(this.padding)
67
-
68
- c.uint64.encode({ start: 0, end: 8, buffer: nonce }, index)
69
-
70
- nonce.set(padding, 8)
71
-
72
- // Decrypt the block using the blinded fork ID.
73
- sodium.crypto_stream_xor(
74
- block,
75
- block,
76
- nonce,
77
- this.blockKey
78
- )
79
- }
80
- }