hypercore 11.3.0 → 11.4.1

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')
@@ -35,37 +34,6 @@ const inspect = Symbol.for('nodejs.util.inspect.custom')
35
34
  // but we enforce 15mb to ensure smooth replication (each block is transmitted atomically)
36
35
  const MAX_SUGGESTED_BLOCK_SIZE = 15 * 1024 * 1024
37
36
 
38
- class DefaultEncryption extends HypercoreEncryption {
39
- constructor (encryptionKey, isBlock, opts) {
40
- super(opts)
41
-
42
- this.encryptionKey = encryptionKey
43
- this.isBlock = !!isBlock
44
-
45
- this.blockKey = null
46
- }
47
-
48
- _init (context) {
49
- this.blockKey = getLegacyBlockKey(context.key, this.encryptionKey, context.compat, this.isBlock)
50
- }
51
-
52
- _getBlockKey (id, context) {
53
- if (!this.blockKey) this._init(context)
54
-
55
- return {
56
- id: 0,
57
- version: context.manifest.version <= 1 ? 0 : 1,
58
- key: this.blockKey
59
- }
60
- }
61
-
62
- _getBlindingKey (context) {
63
- if (!this.blockKey) this._init(context)
64
-
65
- return crypto.hash(this.blockKey)
66
- }
67
- }
68
-
69
37
  class Hypercore extends EventEmitter {
70
38
  constructor (storage, key, opts) {
71
39
  super()
@@ -171,6 +139,8 @@ class Hypercore extends EventEmitter {
171
139
 
172
140
  static MAX_SUGGESTED_BLOCK_SIZE = MAX_SUGGESTED_BLOCK_SIZE
173
141
 
142
+ static DefaultEncryption = DefaultEncryption
143
+
174
144
  static key (manifest, { compat, version, namespace } = {}) {
175
145
  if (b4a.isBuffer(manifest)) manifest = { version, signers: [{ publicKey: manifest, namespace }] }
176
146
  return compat ? manifest.signers[0].publicKey : manifestHash(createManifest(manifest))
@@ -181,7 +151,7 @@ class Hypercore extends EventEmitter {
181
151
  }
182
152
 
183
153
  static blockEncryptionKey (key, encryptionKey) {
184
- return HypercoreEncryption.blockEncryptionKey(key, encryptionKey)
154
+ return DefaultEncryption.blockEncryptionKey(key, encryptionKey)
185
155
  }
186
156
 
187
157
  static getProtocolMuxer (stream) {
@@ -278,8 +248,8 @@ class Hypercore extends EventEmitter {
278
248
  return
279
249
  }
280
250
 
281
- if (!(encryption instanceof HypercoreEncryption)) {
282
- throw new Error('Expected hypercore encryption provider')
251
+ if (!isEncryptionProvider(encryption)) {
252
+ throw new Error('Provider does not satisfy HypercoreEncryption interface')
283
253
  }
284
254
 
285
255
  this.encryption = encryption
@@ -361,7 +331,7 @@ class Hypercore extends EventEmitter {
361
331
 
362
332
  const e = getEncryptionOption(opts)
363
333
  if (!this.core.encryption && e) {
364
- if (e instanceof HypercoreEncryption) {
334
+ if (isEncryptionProvider(e)) {
365
335
  this.core.encryption = e
366
336
  } else {
367
337
  this.core.encryption = this._getEncryptionProvider(e.key, e.block)
@@ -624,7 +594,7 @@ class Hypercore extends EventEmitter {
624
594
 
625
595
  get padding () {
626
596
  if (this.encryption && this.key && this.manifest) {
627
- return this.encryption.padding(this.core)
597
+ return this.encryption.padding(this.core, this.length)
628
598
  }
629
599
 
630
600
  return 0
@@ -820,7 +790,7 @@ class Hypercore extends EventEmitter {
820
790
  await this.encryption.decrypt(index, block, this.core)
821
791
  }
822
792
 
823
- return this._decode(encoding, block)
793
+ return this._decode(encoding, block, index)
824
794
  }
825
795
 
826
796
  async clear (start, end = start + 1, opts) {
@@ -966,7 +936,6 @@ class Hypercore extends EventEmitter {
966
936
  blocks = Array.isArray(blocks) ? blocks : [blocks]
967
937
 
968
938
  const preappend = this.encryption && this._preappend
969
- await this._ensureEncryption()
970
939
 
971
940
  const buffers = this.encodeBatch !== null ? this.encodeBatch(blocks) : new Array(blocks.length)
972
941
 
@@ -1085,8 +1054,8 @@ class Hypercore extends EventEmitter {
1085
1054
  return state.buffer
1086
1055
  }
1087
1056
 
1088
- _decode (enc, block) {
1089
- 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))
1090
1059
  try {
1091
1060
  if (enc) return c.decode(enc, block)
1092
1061
  } catch {
@@ -1095,14 +1064,9 @@ class Hypercore extends EventEmitter {
1095
1064
  return block
1096
1065
  }
1097
1066
 
1098
- _ensureEncryption () {
1099
- if (!this.encryption) return
1100
- if (this.encryption.version === -1) return this.encryption.load(-1, this.core)
1101
- }
1102
-
1103
1067
  _getEncryptionProvider (encryptionKey, block) {
1104
1068
  if (!encryptionKey) return null
1105
- return new DefaultEncryption(encryptionKey, block)
1069
+ return new DefaultEncryption(encryptionKey, this.key, { block, compat: this.core.compat })
1106
1070
  }
1107
1071
  }
1108
1072
 
@@ -1189,13 +1153,10 @@ function getEncryptionOption (opts) {
1189
1153
  return b4a.isBuffer(opts.encryption) ? { key: opts.encryption } : opts.encryption
1190
1154
  }
1191
1155
 
1192
- function getLegacyBlockKey (hypercoreKey, encryptionKey, compat, isBlock) {
1193
- if (isBlock) return encryptionKey
1194
-
1195
- const key = b4a.alloc(HypercoreEncryption.KEYBYTES)
1196
-
1197
- if (compat) sodium.crypto_generichash_batch(key, [encryptionKey], hypercoreKey)
1198
- 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
+ }
1199
1159
 
1200
- return key
1160
+ function isFunction (fn) {
1161
+ return !!fn && typeof fn === 'function'
1201
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) 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.3.0",
3
+ "version": "11.4.1",
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": "^2.0.0",
54
53
  "hypercore-errors": "^1.2.0",
55
54
  "hypercore-id-encoding": "^1.2.0",
56
55
  "hypercore-storage": "^1.0.0",