autobee 2.11.0 → 2.11.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/index.js +16 -13
- package/lib/encoding.js +2 -2
- package/lib/encryption.js +259 -0
- package/lib/migrations.js +1 -1
- package/lib/writers.js +1 -1
- package/package.json +3 -4
package/index.js
CHANGED
|
@@ -6,7 +6,6 @@ const Hyperbee = require('hyperbee2')
|
|
|
6
6
|
const ID = require('hypercore-id-encoding')
|
|
7
7
|
const rrp = require('resolve-reject-promise')
|
|
8
8
|
const Signal = require('signal-promise')
|
|
9
|
-
const { AutobeeEncryption, WriterEncryption, ViewEncryption } = require('autobee-encryption')
|
|
10
9
|
const AutobeeWakeup = require('autobee-wakeup')
|
|
11
10
|
const Hypercore = require('hypercore')
|
|
12
11
|
const crypto = require('hypercore-crypto')
|
|
@@ -16,6 +15,7 @@ const boot = require('./lib/boot.js')
|
|
|
16
15
|
const { DEFAULT_MANIFEST_VERSION } = require('./lib/constants.js')
|
|
17
16
|
const { resolveWeight, currentWeight } = require('./lib/witness.js')
|
|
18
17
|
const encoding = require('./lib/encoding.js')
|
|
18
|
+
const { AutobeeEncryption, WriterEncryption, ViewEncryption } = require('./lib/encryption.js')
|
|
19
19
|
const FastForward = require('./lib/fast-forward.js')
|
|
20
20
|
const System = require('./lib/system.js')
|
|
21
21
|
const ApplyCalls = require('./lib/apply-calls.js')
|
|
@@ -684,18 +684,16 @@ module.exports = class Autobee extends ReadyResource {
|
|
|
684
684
|
}
|
|
685
685
|
|
|
686
686
|
async compactMaybe() {
|
|
687
|
-
if (!this.writers.writable) return
|
|
687
|
+
if (!this.writers.writable) return
|
|
688
688
|
|
|
689
|
-
if (await this._isReindexed()) return
|
|
689
|
+
if (await this._isReindexed()) return
|
|
690
690
|
|
|
691
|
-
|
|
692
|
-
|
|
691
|
+
await this._reindexBee(this._workingBee)
|
|
692
|
+
this.bee.move(this._workingBee.head())
|
|
693
693
|
|
|
694
|
-
|
|
694
|
+
await this._reindexBee(this.system.bee)
|
|
695
695
|
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
return view + system
|
|
696
|
+
this._reindexed = true
|
|
699
697
|
}
|
|
700
698
|
|
|
701
699
|
async _isReindexed() {
|
|
@@ -711,11 +709,16 @@ module.exports = class Autobee extends ReadyResource {
|
|
|
711
709
|
async _reindexBee(bee) {
|
|
712
710
|
const head = bee.head()
|
|
713
711
|
const local = bee.context.local
|
|
714
|
-
if (head === null) return
|
|
715
|
-
if (
|
|
716
|
-
if (!(await this._shouldReindex(head.key))) return
|
|
712
|
+
if (head === null) return
|
|
713
|
+
if (await this._shouldReindex(local.key)) return
|
|
714
|
+
if (!(await this._shouldReindex(head.key))) return
|
|
715
|
+
|
|
716
|
+
// a non-empty local core is a completed reindex whose head was never stored
|
|
717
|
+
if (local.length === 0) {
|
|
718
|
+
await bee.reindex(async (change) => !(await this._shouldReindex(change.head.key)))
|
|
719
|
+
}
|
|
717
720
|
|
|
718
|
-
|
|
721
|
+
bee.move({ key: local.key, length: local.length })
|
|
719
722
|
}
|
|
720
723
|
|
|
721
724
|
async _shouldReindex(key, { unknown = false, length = 0, timeout = 0 } = {}) {
|
package/lib/encoding.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
const b4a = require('b4a')
|
|
2
2
|
const c = require('compact-encoding')
|
|
3
3
|
const crypto = require('hypercore-crypto')
|
|
4
|
-
const { AutobeeEncryption, WriterEncryption } = require('autobee-encryption')
|
|
5
4
|
|
|
6
5
|
const { getEncoding } = require('../encoding/spec/autobee')
|
|
7
|
-
const { LEGACY_OPLOG_VERSION, OPLOG_VERSION } = require('./constants')
|
|
6
|
+
const { LEGACY_OPLOG_VERSION, OPLOG_VERSION } = require('./constants.js')
|
|
7
|
+
const { AutobeeEncryption, WriterEncryption } = require('./encryption.js')
|
|
8
8
|
|
|
9
9
|
const Oplog = getEncoding('@autobee/oplog')
|
|
10
10
|
const Link = getEncoding('@autobee/link')
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
const HypercoreEncryption = require('hypercore/lib/default-encryption.js')
|
|
2
|
+
const sodium = require('sodium-native')
|
|
3
|
+
const crypto = require('hypercore-crypto')
|
|
4
|
+
const c = require('compact-encoding')
|
|
5
|
+
const b4a = require('b4a')
|
|
6
|
+
|
|
7
|
+
const { DEFAULT_MANIFEST_VERSION } = require('./constants.js')
|
|
8
|
+
|
|
9
|
+
const ManifestData = {
|
|
10
|
+
preencode(state, m) {
|
|
11
|
+
c.uint.preencode(state, m.version)
|
|
12
|
+
state.end++ // max flag is 2 so always one byte
|
|
13
|
+
|
|
14
|
+
if (m.legacyBlocks) c.uint.preencode(state, m.legacyBlocks)
|
|
15
|
+
if (m.namespace) c.fixed32.preencode(state, m.namespace)
|
|
16
|
+
},
|
|
17
|
+
encode(state, m) {
|
|
18
|
+
const flags = (m.legacyBlocks ? 1 : 0) | (m.namespace ? 2 : 0)
|
|
19
|
+
|
|
20
|
+
c.uint.encode(state, m.version)
|
|
21
|
+
c.uint.encode(state, flags)
|
|
22
|
+
|
|
23
|
+
if (m.legacyBlocks) c.uint.encode(state, m.legacyBlocks)
|
|
24
|
+
if (m.namespace) c.fixed32.encode(state, m.namespace)
|
|
25
|
+
},
|
|
26
|
+
decode(state) {
|
|
27
|
+
const r0 = c.uint.decode(state)
|
|
28
|
+
const flags = c.uint.decode(state)
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
version: r0,
|
|
32
|
+
legacyBlocks: (flags & 1) !== 0 ? c.uint.decode(state) : 0,
|
|
33
|
+
namespace: (flags & 2) !== 0 ? c.fixed32.decode(state) : null
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const [, NS_VIEW_BLOCK_KEY, NS_HASH_KEY] = crypto.namespace('autobase', 4)
|
|
39
|
+
|
|
40
|
+
const [GENESIS_ENTROPY] = crypto.namespace('autobase/entropy', 2)
|
|
41
|
+
|
|
42
|
+
const nonce = b4a.alloc(sodium.crypto_stream_NONCEBYTES)
|
|
43
|
+
const hash = nonce.subarray(0, sodium.crypto_generichash_BYTES_MIN)
|
|
44
|
+
|
|
45
|
+
class AutobeeEncryption {
|
|
46
|
+
static PADDING = 8
|
|
47
|
+
|
|
48
|
+
constructor(auto) {
|
|
49
|
+
this.auto = auto
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
padding() {
|
|
53
|
+
return AutobeeEncryption.PADDING
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
isCompat(ctx) {
|
|
57
|
+
return ctx.manifest.version <= 1
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async getKeys(id, ctx) {
|
|
61
|
+
if (!this.auto.encryptionKey) return null
|
|
62
|
+
|
|
63
|
+
const entropy = GENESIS_ENTROPY
|
|
64
|
+
const block = this.blockKey(entropy, ctx)
|
|
65
|
+
const hash = crypto.hash([NS_HASH_KEY, block])
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
id,
|
|
69
|
+
block,
|
|
70
|
+
hash
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
blockKey(entropy, ctx) {
|
|
75
|
+
return getBlockKey(this.auto.key, this.auto.encryptionKey, entropy, ctx.key)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
compatKeys() {
|
|
79
|
+
throw new Error('Compatibility method is not specified')
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async encrypt(index, block, fork, ctx) {
|
|
83
|
+
// writer cores are long lasting, but view cores should be up to date
|
|
84
|
+
if (this instanceof ViewEncryption && ctx.manifest.version < DEFAULT_MANIFEST_VERSION) {
|
|
85
|
+
throw new Error(`Unexpected manifest: version < ${DEFAULT_MANIFEST_VERSION}`)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (this.isCompat(ctx, index)) {
|
|
89
|
+
const compat = this.compatKeys(ctx)
|
|
90
|
+
return HypercoreEncryption.encrypt(index, block, fork, compat.block, compat.blinding)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const keys = await this.getKeys(0, ctx)
|
|
94
|
+
|
|
95
|
+
encryptBlock(index, block, keys.id, keys.block, keys.hash)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async decrypt(index, block, ctx) {
|
|
99
|
+
if (this.isCompat(ctx, index)) {
|
|
100
|
+
return HypercoreEncryption.decrypt(index, block, this.compatKeys(ctx).block)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const padding = block.subarray(0, AutobeeEncryption.PADDING)
|
|
104
|
+
block = block.subarray(AutobeeEncryption.PADDING)
|
|
105
|
+
|
|
106
|
+
const type = padding[0]
|
|
107
|
+
switch (type) {
|
|
108
|
+
case 0:
|
|
109
|
+
return block // unencrypted
|
|
110
|
+
|
|
111
|
+
case 1:
|
|
112
|
+
break
|
|
113
|
+
|
|
114
|
+
default:
|
|
115
|
+
throw new Error('Unrecognised encryption type')
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const id = c.uint32.decode({ start: 4, end: 8, buffer: padding })
|
|
119
|
+
|
|
120
|
+
const keys = await this.getKeys(id, ctx)
|
|
121
|
+
|
|
122
|
+
c.uint64.encode({ start: 0, end: 8, buffer: nonce }, index)
|
|
123
|
+
nonce.set(padding, 8, 16)
|
|
124
|
+
|
|
125
|
+
// Decrypt the block using the full nonce
|
|
126
|
+
decrypt(block, nonce, keys.block)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
static encryptAnchor(block, bootstrap, encryptionKey, namespace) {
|
|
130
|
+
const entropy = GENESIS_ENTROPY
|
|
131
|
+
|
|
132
|
+
const blockKey = getBlockKey(bootstrap, encryptionKey, entropy, namespace)
|
|
133
|
+
const hashKey = crypto.hash([NS_HASH_KEY, block])
|
|
134
|
+
|
|
135
|
+
encryptBlock(0, block, 0, blockKey, hashKey)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
static async setSystemEncryption(bootstrap, encryptionKey, core, opts) {
|
|
139
|
+
await core.ready()
|
|
140
|
+
if (core.manifest.version === 1) {
|
|
141
|
+
const key = getCompatBlockKey(bootstrap, encryptionKey, '_system')
|
|
142
|
+
core.setEncryptionKey(key, { block: true })
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const info = {
|
|
147
|
+
key: bootstrap,
|
|
148
|
+
encryptionKey
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return core.setEncryption(new ViewEncryption(info, '_system'))
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
static getSystemEncryption(bootstrap, encryptionKey) {
|
|
155
|
+
return AutobeeEncryption.getViewEncryption(bootstrap, encryptionKey, '_system')
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
static getViewEncryption(bootstrap, encryptionKey, name) {
|
|
159
|
+
const info = { key: bootstrap, encryptionKey }
|
|
160
|
+
return new ViewEncryption(info, name)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
static getWriterEncryption(bootstrap, encryptionKey) {
|
|
164
|
+
return new WriterEncryption({ key: bootstrap, encryptionKey })
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
class ViewEncryption extends AutobeeEncryption {
|
|
169
|
+
constructor(auto, name) {
|
|
170
|
+
super(auto)
|
|
171
|
+
this.name = name
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
compatKeys() {
|
|
175
|
+
const block = getCompatBlockKey(this.auto.key, this.auto.encryptionKey, this.name)
|
|
176
|
+
return {
|
|
177
|
+
block,
|
|
178
|
+
blinding: crypto.hash(block)
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
blockKey(entropy, ctx) {
|
|
183
|
+
// compat encryption
|
|
184
|
+
if (ctx.manifest.version < DEFAULT_MANIFEST_VERSION) {
|
|
185
|
+
return getCompatBlockKey(this.auto.key, entropy, this.name)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return getBlockKey(this.auto.key, this.auto.encryptionKey, entropy, ctx.key)
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
class WriterEncryption extends AutobeeEncryption {
|
|
193
|
+
compatKeys(ctx) {
|
|
194
|
+
return HypercoreEncryption.deriveKeys(this.auto.encryptionKey, ctx.key)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
blockKey(entropy, ctx) {
|
|
198
|
+
// used for anchors
|
|
199
|
+
if (ctx.manifest.userData) {
|
|
200
|
+
const userData = c.decode(ManifestData, ctx.manifest.userData)
|
|
201
|
+
if (userData.namespace !== null) {
|
|
202
|
+
const b = getBlockKey(this.auto.key, this.auto.encryptionKey, entropy, userData.namespace)
|
|
203
|
+
return b
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return getBlockKey(this.auto.key, this.auto.encryptionKey, entropy, ctx.key)
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
module.exports = {
|
|
212
|
+
AutobeeEncryption,
|
|
213
|
+
WriterEncryption,
|
|
214
|
+
ViewEncryption
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function encrypt(block, nonce, key) {
|
|
218
|
+
sodium.crypto_stream_xor(block, block, nonce, key)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function decrypt(block, nonce, key) {
|
|
222
|
+
return encrypt(block, nonce, key) // symmetric
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function getBlockKey(bootstrap, encryptionKey, entropy, hypercoreKey) {
|
|
226
|
+
return (
|
|
227
|
+
encryptionKey &&
|
|
228
|
+
crypto.hash([NS_VIEW_BLOCK_KEY, bootstrap, encryptionKey, entropy, hypercoreKey])
|
|
229
|
+
)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function getCompatBlockKey(bootstrap, encryptionKey, name) {
|
|
233
|
+
if (typeof name === 'string') return getCompatBlockKey(bootstrap, encryptionKey, b4a.from(name))
|
|
234
|
+
return encryptionKey && crypto.hash([NS_VIEW_BLOCK_KEY, bootstrap, encryptionKey, name])
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function blockhash(block, padding, hashKey) {
|
|
238
|
+
sodium.crypto_generichash(hash, block, hashKey)
|
|
239
|
+
padding.set(hash.subarray(0, 8)) // copy first 8 bytes of hash
|
|
240
|
+
hash.fill(0) // clear nonce buffer
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function encryptBlock(index, block, id, blockKey, hashKey) {
|
|
244
|
+
const padding = block.subarray(0, AutobeeEncryption.PADDING)
|
|
245
|
+
block = block.subarray(AutobeeEncryption.PADDING)
|
|
246
|
+
|
|
247
|
+
blockhash(block, padding, hashKey)
|
|
248
|
+
c.uint32.encode({ start: 4, end: 8, buffer: padding }, id)
|
|
249
|
+
|
|
250
|
+
c.uint64.encode({ start: 0, end: 8, buffer: nonce }, index)
|
|
251
|
+
|
|
252
|
+
padding[0] = 1 // version in plaintext
|
|
253
|
+
|
|
254
|
+
nonce.set(padding, 8, 16)
|
|
255
|
+
|
|
256
|
+
// The combination of index, key id, fork id and block hash is very likely
|
|
257
|
+
// to be unique for a given Hypercore and therefore our nonce is suitable
|
|
258
|
+
encrypt(block, nonce, blockKey)
|
|
259
|
+
}
|
package/lib/migrations.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
const c = require('compact-encoding')
|
|
2
2
|
const b4a = require('b4a')
|
|
3
3
|
const crypto = require('hypercore-crypto')
|
|
4
|
-
const { AutobeeEncryption } = require('autobee-encryption')
|
|
5
4
|
const { decodeBlock } = require('hyperbee2/lib/encoding.js')
|
|
6
5
|
|
|
6
|
+
const { AutobeeEncryption } = require('./encryption.js')
|
|
7
7
|
const encoding = require('./encoding.js')
|
|
8
8
|
const { assert, bail } = require('./asserts.js')
|
|
9
9
|
|
package/lib/writers.js
CHANGED
|
@@ -3,7 +3,7 @@ const encoding = require('./encoding.js')
|
|
|
3
3
|
const Triggers = require('./triggers.js')
|
|
4
4
|
const { LEGACY_OPLOG_VERSION } = require('./constants.js')
|
|
5
5
|
const { resolveWeight } = require('./witness.js')
|
|
6
|
-
const { WriterEncryption } = require('
|
|
6
|
+
const { WriterEncryption } = require('./encryption.js')
|
|
7
7
|
const ReadWriteLock = require('read-write-mutexify')
|
|
8
8
|
|
|
9
9
|
const WRITER_PREFETCH = 10
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "autobee",
|
|
3
|
-
"version": "2.11.
|
|
3
|
+
"version": "2.11.1",
|
|
4
4
|
"description": "Bee based autobase",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Holepunch Inc.",
|
|
@@ -23,11 +23,10 @@
|
|
|
23
23
|
"test:generate": "brittle -r test/all.js test/*.js"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"autobee-encryption": "^0.1.2",
|
|
27
26
|
"autobee-wakeup": "^1.1.3",
|
|
28
27
|
"b4a": "^1.8.0",
|
|
29
|
-
"compact-encoding": "^3.
|
|
30
|
-
"hyperbee2": "^2.17.
|
|
28
|
+
"compact-encoding": "^3.4.0",
|
|
29
|
+
"hyperbee2": "^2.17.1",
|
|
31
30
|
"hypercore": "^11.37.0",
|
|
32
31
|
"hypercore-id-encoding": "^1.3.0",
|
|
33
32
|
"hyperschema": "^1.26.2",
|