hypercore 11.2.0 → 11.4.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
@@ -68,6 +68,8 @@ You can also set valueEncoding to any [compact-encoding](https://github.com/comp
68
68
 
69
69
  valueEncodings will be applied to individual blocks, even if you append batches. If you want to control encoding at the batch-level, you can use the `encodeBatch` option, which is a function that takes a batch and returns a binary-encoded batch. If you provide a custom valueEncoding, it will not be applied prior to `encodeBatch`.
70
70
 
71
+ The user may provide a custom encryption module as `opts.encryption`, which should satisfy the [HypercoreEncryption](https://github.com/holepunchto/hypercore-encryption) interface.
72
+
71
73
  #### `const { length, byteLength } = await core.append(block)`
72
74
 
73
75
  Append a block of data (or an array of blocks) to the core.
package/index.js CHANGED
@@ -3,10 +3,8 @@ 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')
7
6
  const b4a = require('b4a')
8
7
  const NoiseSecretStream = require('@hyperswarm/secret-stream')
9
- const HypercoreEncryption = require('hypercore-encryption')
10
8
  const Protomux = require('protomux')
11
9
  const id = require('hypercore-id-encoding')
12
10
  const safetyCatch = require('safety-catch')
@@ -15,6 +13,7 @@ const unslab = require('unslab')
15
13
  const Core = require('./lib/core')
16
14
  const Info = require('./lib/info')
17
15
  const Download = require('./lib/download')
16
+ const DefaultEncryption = require('./lib/default-encryption')
18
17
  const caps = require('./lib/caps')
19
18
  const { manifestHash, createManifest } = require('./lib/verifier')
20
19
  const { ReadStream, WriteStream, ByteStream } = require('./lib/streams')
@@ -140,6 +139,8 @@ class Hypercore extends EventEmitter {
140
139
 
141
140
  static MAX_SUGGESTED_BLOCK_SIZE = MAX_SUGGESTED_BLOCK_SIZE
142
141
 
142
+ static DefaultEncryption = DefaultEncryption
143
+
143
144
  static key (manifest, { compat, version, namespace } = {}) {
144
145
  if (b4a.isBuffer(manifest)) manifest = { version, signers: [{ publicKey: manifest, namespace }] }
145
146
  return compat ? manifest.signers[0].publicKey : manifestHash(createManifest(manifest))
@@ -150,7 +151,7 @@ class Hypercore extends EventEmitter {
150
151
  }
151
152
 
152
153
  static blockEncryptionKey (key, encryptionKey) {
153
- return HypercoreEncryption.blockEncryptionKey(key, encryptionKey)
154
+ return DefaultEncryption.blockEncryptionKey(key, encryptionKey)
154
155
  }
155
156
 
156
157
  static getProtocolMuxer (stream) {
@@ -235,7 +236,7 @@ class Hypercore extends EventEmitter {
235
236
  }
236
237
 
237
238
  setEncryptionKey (encryptionKey, opts) {
238
- const encryption = this._getLegacyEncryption(encryptionKey, !!(opts && opts.block))
239
+ const encryption = this._getEncryptionProvider(encryptionKey, !!(opts && opts.block))
239
240
  return this.setEncryption(encryption, opts)
240
241
  }
241
242
 
@@ -247,8 +248,8 @@ class Hypercore extends EventEmitter {
247
248
  return
248
249
  }
249
250
 
250
- if (!HypercoreEncryption.isHypercoreEncryption(encryption)) {
251
- throw new Error('Expected hypercore encryption provider')
251
+ if (!isEncryptionProvider(encryption)) {
252
+ throw new Error('Provider does not satisfy HypercoreEncryption interface')
252
253
  }
253
254
 
254
255
  this.encryption = encryption
@@ -328,13 +329,12 @@ class Hypercore extends EventEmitter {
328
329
 
329
330
  if (this.keyPair === null) this.keyPair = opts.keyPair || this.core.header.keyPair
330
331
 
331
- if (!this.core.encryption) {
332
- const e = getEncryptionOption(opts)
333
-
334
- if (HypercoreEncryption.isHypercoreEncryption(e)) {
332
+ const e = getEncryptionOption(opts)
333
+ if (!this.core.encryption && e) {
334
+ if (isEncryptionProvider(e)) {
335
335
  this.core.encryption = e
336
- } else if (e) {
337
- this.core.encryption = this._getLegacyEncryption(e.key, e.block)
336
+ } else {
337
+ this.core.encryption = this._getEncryptionProvider(e.key, e.block)
338
338
  }
339
339
  }
340
340
 
@@ -592,12 +592,16 @@ class Hypercore extends EventEmitter {
592
592
  return this.state.fork
593
593
  }
594
594
 
595
- get peers () {
596
- return this.opened === false ? [] : this.core.replicator.peers
595
+ get padding () {
596
+ if (this.encryption && this.key && this.manifest) {
597
+ return this.encryption.padding(this.core, this.length)
598
+ }
599
+
600
+ return 0
597
601
  }
598
602
 
599
- get padding () {
600
- return this.encryption === null ? 0 : this.encryption.padding
603
+ get peers () {
604
+ return this.opened === false ? [] : this.core.replicator.peers
601
605
  }
602
606
 
603
607
  get globalCache () {
@@ -700,6 +704,18 @@ class Hypercore extends EventEmitter {
700
704
  if (this.opened === false) await this.opening
701
705
  if (!isValidIndex(bytes)) throw ASSERTION('seek is invalid')
702
706
 
707
+ const activeRequests = (opts && opts.activeRequests) || this.activeRequests
708
+
709
+ if (this.encryption && !this.core.manifest) {
710
+ const req = this.replicator.addUpgrade(activeRequests)
711
+ try {
712
+ await req.promise
713
+ } catch (err) {
714
+ if (isSessionMoved(err)) return this.seek(bytes, opts)
715
+ throw err
716
+ }
717
+ }
718
+
703
719
  const s = MerkleTree.seek(this.state, bytes, this.padding)
704
720
 
705
721
  const offset = await s.update()
@@ -709,7 +725,6 @@ class Hypercore extends EventEmitter {
709
725
 
710
726
  if (!this._shouldWait(opts, this.wait)) return null
711
727
 
712
- const activeRequests = (opts && opts.activeRequests) || this.activeRequests
713
728
  const req = this.core.replicator.addSeek(activeRequests, s)
714
729
 
715
730
  const timeout = opts && opts.timeout !== undefined ? opts.timeout : this.timeout
@@ -772,12 +787,10 @@ class Hypercore extends EventEmitter {
772
787
  // Copy the block as it might be shared with other sessions.
773
788
  block = b4a.from(block)
774
789
 
775
- if (this.encryption.compat !== this.core.compat) this._updateEncryption()
776
-
777
- await this.encryption.decrypt(index, block)
790
+ await this.encryption.decrypt(index, block, this.core)
778
791
  }
779
792
 
780
- return this._decode(encoding, block)
793
+ return this._decode(encoding, block, index)
781
794
  }
782
795
 
783
796
  async clear (start, end = start + 1, opts) {
@@ -923,7 +936,6 @@ class Hypercore extends EventEmitter {
923
936
  blocks = Array.isArray(blocks) ? blocks : [blocks]
924
937
 
925
938
  const preappend = this.encryption && this._preappend
926
- if (preappend) await this.encryption.ready()
927
939
 
928
940
  const buffers = this.encodeBatch !== null ? this.encodeBatch(blocks) : new Array(blocks.length)
929
941
 
@@ -1042,8 +1054,8 @@ class Hypercore extends EventEmitter {
1042
1054
  return state.buffer
1043
1055
  }
1044
1056
 
1045
- _decode (enc, block) {
1046
- if (this.padding) block = block.subarray(this.padding)
1057
+ _decode (enc, block, index) {
1058
+ if (this.encryption) block = block.subarray(this.encryption.padding(this.core, index))
1047
1059
  try {
1048
1060
  if (enc) return c.decode(enc, block)
1049
1061
  } catch {
@@ -1052,23 +1064,9 @@ class Hypercore extends EventEmitter {
1052
1064
  return block
1053
1065
  }
1054
1066
 
1055
- _updateEncryption () {
1056
- const e = this.encryption
1057
- if (HypercoreEncryption.isHypercoreEncryption(e)) return
1058
-
1059
- this.encryption = this._getLegacyEncryption(e.key, e.block)
1060
-
1061
- if (e === this.core.encryption) this.core.encryption = this.encryption
1062
- }
1063
-
1064
- _getLegacyEncryption (encryptionKey, block) {
1067
+ _getEncryptionProvider (encryptionKey, block) {
1065
1068
  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)
1069
+ return new DefaultEncryption(encryptionKey, this.key, { block, compat: this.core.compat })
1072
1070
  }
1073
1071
  }
1074
1072
 
@@ -1086,10 +1084,8 @@ async function preappend (blocks) {
1086
1084
  const offset = this.state.length
1087
1085
  const fork = this.state.encryptionFork
1088
1086
 
1089
- if (this.encryption.compat !== this.core.compat) this._updateEncryption()
1090
-
1091
1087
  for (let i = 0; i < blocks.length; i++) {
1092
- await this.encryption.encrypt(offset + i, blocks[i], fork)
1088
+ await this.encryption.encrypt(offset + i, blocks[i], fork, this.core)
1093
1089
  }
1094
1090
  }
1095
1091
 
@@ -1157,11 +1153,10 @@ function getEncryptionOption (opts) {
1157
1153
  return b4a.isBuffer(opts.encryption) ? { key: opts.encryption } : opts.encryption
1158
1154
  }
1159
1155
 
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])
1156
+ function isEncryptionProvider (e) {
1157
+ return e && isFunction(e.padding) && isFunction(e.encrypt) && isFunction(e.decrypt)
1158
+ }
1165
1159
 
1166
- return key
1160
+ function isFunction (fn) {
1161
+ return !!fn && typeof fn === 'function'
1167
1162
  }
package/lib/caps.js CHANGED
@@ -12,12 +12,12 @@ const [
12
12
  REPLICATE_RESPONDER,
13
13
  MANIFEST,
14
14
  DEFAULT_NAMESPACE,
15
- LEGACY_BLOCK_ENCRYPTION
15
+ DEFAULT_ENCRYPTION
16
16
  ] = crypto.namespace('hypercore', 6)
17
17
 
18
18
  exports.MANIFEST = MANIFEST
19
19
  exports.DEFAULT_NAMESPACE = DEFAULT_NAMESPACE
20
- exports.LEGACY_BLOCK_ENCRYPTION = LEGACY_BLOCK_ENCRYPTION
20
+ exports.DEFAULT_ENCRYPTION = DEFAULT_ENCRYPTION
21
21
 
22
22
  exports.replicate = function (isInitiator, key, handshakeHash) {
23
23
  const out = b4a.allocUnsafe(32)
@@ -0,0 +1,116 @@
1
+ const sodium = require('sodium-universal')
2
+ const c = require('compact-encoding')
3
+ const b4a = require('b4a')
4
+ const { DEFAULT_ENCRYPTION } = require('./caps')
5
+
6
+ const nonce = b4a.alloc(sodium.crypto_stream_NONCEBYTES)
7
+
8
+ module.exports = class DefaultEncryption {
9
+ static PADDING = 8
10
+
11
+ constructor (encryptionKey, hypercoreKey, { block = false, compat = true } = {}) {
12
+ this.key = encryptionKey
13
+ this.compat = compat
14
+
15
+ const keys = DefaultEncryption.deriveKeys(encryptionKey, hypercoreKey, block, compat)
16
+
17
+ this.blockKey = keys.block
18
+ this.blindingKey = keys.blinding
19
+ }
20
+
21
+ static deriveKeys (encryptionKey, hypercoreKey, block, compat) {
22
+ const subKeys = b4a.alloc(2 * sodium.crypto_stream_KEYBYTES)
23
+
24
+ const blockKey = block ? encryptionKey : subKeys.subarray(0, sodium.crypto_stream_KEYBYTES)
25
+ const blindingKey = subKeys.subarray(sodium.crypto_stream_KEYBYTES)
26
+
27
+ if (!block) {
28
+ if (compat) sodium.crypto_generichash_batch(blockKey, [encryptionKey], hypercoreKey)
29
+ else sodium.crypto_generichash_batch(blockKey, [DEFAULT_ENCRYPTION, hypercoreKey, encryptionKey])
30
+ }
31
+
32
+ sodium.crypto_generichash(blindingKey, blockKey)
33
+
34
+ return {
35
+ blinding: blindingKey,
36
+ block: blockKey
37
+ }
38
+ }
39
+
40
+ static blockEncryptionKey (hypercoreKey, encryptionKey) {
41
+ const blockKey = b4a.alloc(sodium.crypto_stream_KEYBYTES)
42
+ sodium.crypto_generichash_batch(blockKey, [DEFAULT_ENCRYPTION, hypercoreKey, encryptionKey])
43
+ return blockKey
44
+ }
45
+
46
+ static encrypt (index, block, fork, blockKey, blindingKey) {
47
+ const padding = block.subarray(0, DefaultEncryption.PADDING)
48
+ block = block.subarray(DefaultEncryption.PADDING)
49
+
50
+ c.uint64.encode({ start: 0, end: 8, buffer: padding }, fork)
51
+ c.uint64.encode({ start: 0, end: 8, buffer: nonce }, index)
52
+
53
+ // Zero out any previous padding.
54
+ nonce.fill(0, 8, 8 + padding.byteLength)
55
+
56
+ // Blind the fork ID, possibly risking reusing the nonce on a reorg of the
57
+ // Hypercore. This is fine as the blinding is best-effort and the latest
58
+ // fork ID shared on replication anyway.
59
+ sodium.crypto_stream_xor(
60
+ padding,
61
+ padding,
62
+ nonce,
63
+ blindingKey
64
+ )
65
+
66
+ nonce.set(padding, 8)
67
+
68
+ // The combination of a (blinded) fork ID and a block index is unique for a
69
+ // given Hypercore and is therefore a valid nonce for encrypting the block.
70
+ sodium.crypto_stream_xor(
71
+ block,
72
+ block,
73
+ nonce,
74
+ blockKey
75
+ )
76
+ }
77
+
78
+ static decrypt (index, block, blockKey) {
79
+ const padding = block.subarray(0, DefaultEncryption.PADDING)
80
+ block = block.subarray(DefaultEncryption.PADDING)
81
+
82
+ c.uint64.encode({ start: 0, end: 8, buffer: nonce }, index)
83
+
84
+ nonce.set(padding, 8)
85
+
86
+ // Decrypt the block using the blinded fork ID.
87
+ sodium.crypto_stream_xor(
88
+ block,
89
+ block,
90
+ nonce,
91
+ blockKey
92
+ )
93
+ }
94
+
95
+ encrypt (index, block, fork, core) {
96
+ if (core.compat !== this.compat) this._reload(core)
97
+ return DefaultEncryption.encrypt(index, block, fork, this.blockKey, this.blindingKey)
98
+ }
99
+
100
+ decrypt (index, block, core) {
101
+ if (core.compat !== this.compat) this._reload(core)
102
+ return DefaultEncryption.decrypt(index, block, this.blockKey)
103
+ }
104
+
105
+ padding () {
106
+ return DefaultEncryption.PADDING
107
+ }
108
+
109
+ _reload (core) {
110
+ const block = b4a.equals(this.key, this.blockKey)
111
+ const keys = DefaultEncryption.deriveKeys(this.key, core.key, { block, compat: core.compat })
112
+
113
+ this.blockKey = keys.blockKey
114
+ this.blindingKey = keys.blindingKey
115
+ }
116
+ }
package/lib/messages.js CHANGED
@@ -9,6 +9,7 @@ const EMPTY = b4a.alloc(0)
9
9
  const MANIFEST_PATCH = 0b00000001
10
10
  const MANIFEST_PROLOGUE = 0b00000010
11
11
  const MANIFEST_LINKED = 0b00000100
12
+ const MANIFEST_USER_DATA = 0b00001000
12
13
 
13
14
  const hashes = {
14
15
  preencode (state, m) {
@@ -141,7 +142,8 @@ const manifestv0 = {
141
142
  hash: c.fixed32.decode(state),
142
143
  length: 0
143
144
  },
144
- linked: null
145
+ linked: null,
146
+ userData: null
145
147
  }
146
148
  }
147
149
 
@@ -153,7 +155,8 @@ const manifestv0 = {
153
155
  quorum: 1,
154
156
  signers: [signer.decode(state)],
155
157
  prologue: null,
156
- linked: null
158
+ linked: null,
159
+ userData: null
157
160
  }
158
161
  }
159
162
 
@@ -166,7 +169,8 @@ const manifestv0 = {
166
169
  quorum: c.uint.decode(state),
167
170
  signers: signerArray.decode(state),
168
171
  prologue: null,
169
- linked: null
172
+ linked: null,
173
+ userData: null
170
174
  }
171
175
  }
172
176
  }
@@ -176,6 +180,7 @@ const fixed32Array = c.array(c.fixed32)
176
180
  const manifest = exports.manifest = {
177
181
  preencode (state, m) {
178
182
  state.end++ // version
183
+
179
184
  if (m.version === 0) return manifestv0.preencode(state, m)
180
185
 
181
186
  state.end++ // flags
@@ -183,34 +188,35 @@ const manifest = exports.manifest = {
183
188
 
184
189
  c.uint.preencode(state, m.quorum)
185
190
  signerArray.preencode(state, m.signers)
186
- if (m.prologue) prologue.preencode(state, m.prologue)
187
191
 
188
- if (m.linked) {
189
- fixed32Array.preencode(state, m.linked)
190
- }
192
+ if (m.prologue) prologue.preencode(state, m.prologue)
193
+ if (m.linked) fixed32Array.preencode(state, m.linked)
194
+ if (m.userData) c.buffer.preencode(state, m.userData)
191
195
  },
192
196
  encode (state, m) {
193
197
  c.uint.encode(state, m.version)
198
+
194
199
  if (m.version === 0) return manifestv0.encode(state, m)
195
200
 
196
201
  let flags = 0
197
202
  if (m.allowPatch) flags |= MANIFEST_PATCH
198
203
  if (m.prologue) flags |= MANIFEST_PROLOGUE
199
204
  if (m.linked) flags |= MANIFEST_LINKED
205
+ if (m.userData) flags |= MANIFEST_USER_DATA
200
206
 
201
207
  c.uint.encode(state, flags)
202
208
  hashes.encode(state, m.hash)
203
209
 
204
210
  c.uint.encode(state, m.quorum)
205
211
  signerArray.encode(state, m.signers)
206
- if (m.prologue) prologue.encode(state, m.prologue)
207
212
 
208
- if (m.linked) {
209
- fixed32Array.encode(state, m.linked)
210
- }
213
+ if (m.prologue) prologue.encode(state, m.prologue)
214
+ if (m.linked) fixed32Array.encode(state, m.linked)
215
+ if (m.userData) c.buffer.encode(state, m.userData)
211
216
  },
212
217
  decode (state) {
213
218
  const version = c.uint.decode(state)
219
+
214
220
  if (version === 0) return manifestv0.decode(state)
215
221
  if (version > 2) throw new Error('Unknown version: ' + version)
216
222
 
@@ -222,6 +228,7 @@ const manifest = exports.manifest = {
222
228
  const hasPatch = (flags & MANIFEST_PATCH) !== 0
223
229
  const hasPrologue = (flags & MANIFEST_PROLOGUE) !== 0
224
230
  const hasLinked = (flags & MANIFEST_LINKED) !== 0
231
+ const hasUserData = (flags & MANIFEST_USER_DATA) !== 0
225
232
 
226
233
  return {
227
234
  version,
@@ -230,7 +237,8 @@ const manifest = exports.manifest = {
230
237
  quorum,
231
238
  signers,
232
239
  prologue: hasPrologue ? prologue.decode(state) : null,
233
- linked: hasLinked ? fixed32Array.decode(state) : null
240
+ linked: hasLinked ? fixed32Array.decode(state) : null,
241
+ userData: hasUserData ? c.buffer.decode(state) : null
234
242
  }
235
243
  }
236
244
  }
@@ -936,7 +944,9 @@ oplog.header = {
936
944
  namespace: DEFAULT_NAMESPACE,
937
945
  publicKey: old.signer.publicKey
938
946
  }],
939
- prologue: null
947
+ prologue: null,
948
+ linked: null,
949
+ userData: null
940
950
  },
941
951
  keyPair: old.signer.secretKey ? old.signer : null,
942
952
  userData: old.userData,
package/lib/verifier.js CHANGED
@@ -183,7 +183,8 @@ module.exports = class Verifier {
183
183
  publicKey
184
184
  }],
185
185
  prologue: null,
186
- linked: null
186
+ linked: null,
187
+ userData: null
187
188
  }
188
189
  }
189
190
 
@@ -196,13 +197,14 @@ module.exports = class Verifier {
196
197
  if (!inp) return null
197
198
 
198
199
  const manifest = {
199
- version: getManifestVersion(inp), // only v2 if linked are present
200
+ version: getManifestVersion(inp), // defaults to v1
200
201
  hash: 'blake2b',
201
202
  allowPatch: !!inp.allowPatch,
202
203
  quorum: defaultQuorum(inp),
203
204
  signers: inp.signers ? inp.signers.map(parseSigner) : [],
204
205
  prologue: null,
205
- linked: null
206
+ linked: null,
207
+ userData: inp.userData || null
206
208
  }
207
209
 
208
210
  if (inp.hash && inp.hash !== 'blake2b') throw BAD_ARGUMENT('Only Blake2b hashes are supported')
@@ -216,8 +218,12 @@ module.exports = class Verifier {
216
218
  manifest.prologue.hash = unslab(manifest.prologue.hash)
217
219
  }
218
220
 
221
+ if (manifest.userData !== null && manifest.version < 2) {
222
+ throw BAD_ARGUMENT('Invalid field: userData')
223
+ }
224
+
219
225
  if (inp.linked && inp.linked.length) {
220
- if (manifest.version < 2) throw BAD_ARGUMENT('Invalid field')
226
+ if (manifest.version < 2) throw BAD_ARGUMENT('Invalid field: linked')
221
227
 
222
228
  for (const key of inp.linked) {
223
229
  if (!(b4a.isBuffer(key) && key.byteLength === 32)) {
@@ -328,5 +334,6 @@ function proofToVersion1 (proof) {
328
334
  function getManifestVersion (inp) {
329
335
  if (typeof inp.version === 'number') return inp.version
330
336
  if (inp.linked && inp.linked.length) return 2
337
+ if (inp.userData !== null) return 2
331
338
  return 1
332
339
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypercore",
3
- "version": "11.2.0",
3
+ "version": "11.4.0",
4
4
  "description": "Hypercore is a secure, distributed append-only log",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -50,7 +50,6 @@
50
50
  "fast-fifo": "^1.3.0",
51
51
  "flat-tree": "^1.9.0",
52
52
  "hypercore-crypto": "^3.2.1",
53
- "hypercore-encryption": "^1.0.0",
54
53
  "hypercore-errors": "^1.2.0",
55
54
  "hypercore-id-encoding": "^1.2.0",
56
55
  "hypercore-storage": "^1.0.0",