@peerbit/trusted-network 6.0.113 → 6.0.114
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/dist/src/v2-authority-entry.d.ts +13 -17
- package/dist/src/v2-authority-entry.d.ts.map +1 -1
- package/dist/src/v2-authority-entry.js +78 -147
- package/dist/src/v2-authority-entry.js.map +1 -1
- package/dist/src/v2-policy-engine.d.ts.map +1 -1
- package/dist/src/v2-policy-engine.js +4 -42
- package/dist/src/v2-policy-engine.js.map +1 -1
- package/dist/src/v2-resource-fence-entry.d.ts.map +1 -1
- package/dist/src/v2-resource-fence-entry.js +115 -11
- package/dist/src/v2-resource-fence-entry.js.map +1 -1
- package/dist/src/v2.d.ts.map +1 -1
- package/dist/src/v2.js +22 -0
- package/dist/src/v2.js.map +1 -1
- package/package.json +4 -4
- package/src/v2-authority-entry.ts +110 -221
- package/src/v2-policy-engine.ts +7 -54
- package/src/v2-resource-fence-entry.ts +180 -10
- package/src/v2.ts +25 -0
|
@@ -1,45 +1,36 @@
|
|
|
1
1
|
import { deserialize, serialize } from "@dao-xyz/borsh";
|
|
2
|
-
import {
|
|
3
|
-
calculateRawCid,
|
|
4
|
-
cidifyString,
|
|
5
|
-
codecMap,
|
|
6
|
-
defaultHasher,
|
|
7
|
-
stringifyCid,
|
|
8
|
-
} from "@peerbit/blocks-interface";
|
|
9
2
|
import { DecryptedThing, verify } from "@peerbit/crypto";
|
|
10
|
-
import { Entry,
|
|
3
|
+
import { Entry, EntryV0, NO_ENCODING } from "@peerbit/log";
|
|
11
4
|
import { equals } from "uint8arrays";
|
|
12
5
|
import {
|
|
13
6
|
NetworkDescriptorV2,
|
|
14
7
|
assertNetworkDescriptorV2,
|
|
15
|
-
copyUint8ArrayV2,
|
|
16
8
|
copyUint8ArrayWithLengthV2,
|
|
17
9
|
exactUint8ArrayByteLengthV2,
|
|
18
10
|
} from "./v2.js";
|
|
19
11
|
|
|
20
|
-
/** Internal limits selected by a concrete TrustedNetwork v2 record profile. */
|
|
21
|
-
export type AuthorityEntryV0LimitsV2 = {
|
|
22
|
-
maximumEntryBytes: number;
|
|
23
|
-
maximumDirectParents: number;
|
|
24
|
-
exactPayloadBytes?: number;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
12
|
export type AuthenticatedAuthorityEntryV0V2 = {
|
|
28
13
|
descriptor: NetworkDescriptorV2;
|
|
29
14
|
entryBytes: Uint8Array;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
gid: string;
|
|
33
|
-
metaData?: Uint8Array;
|
|
34
|
-
directParents: Array<{ cid: string; digest: Uint8Array }>;
|
|
15
|
+
entry: EntryV0<Uint8Array>;
|
|
16
|
+
metaBytes: Uint8Array;
|
|
35
17
|
payloadBytes: Uint8Array;
|
|
18
|
+
reservedBytes: Uint8Array;
|
|
19
|
+
hasHash: boolean;
|
|
20
|
+
directParentCount: number;
|
|
36
21
|
};
|
|
37
22
|
|
|
38
|
-
type
|
|
23
|
+
export type AuthorityEntryV0ProfileV2 = (
|
|
24
|
+
entry: AuthenticatedAuthorityEntryV0V2,
|
|
25
|
+
) => void;
|
|
26
|
+
|
|
27
|
+
type ScannedEntryV0StructureV2 = {
|
|
39
28
|
metaBytes: Uint8Array;
|
|
40
29
|
payloadBytes: Uint8Array;
|
|
41
|
-
signatureBytes: Uint8Array;
|
|
42
30
|
signableBytes: Uint8Array;
|
|
31
|
+
reservedBytes: Uint8Array;
|
|
32
|
+
hasHash: boolean;
|
|
33
|
+
directParentCount: number;
|
|
43
34
|
};
|
|
44
35
|
|
|
45
36
|
class BoundsReaderV2 {
|
|
@@ -54,6 +45,10 @@ class BoundsReaderV2 {
|
|
|
54
45
|
return this.offset;
|
|
55
46
|
}
|
|
56
47
|
|
|
48
|
+
get remaining(): number {
|
|
49
|
+
return this.bytes.byteLength - this.offset;
|
|
50
|
+
}
|
|
51
|
+
|
|
57
52
|
readU8(label: string): number {
|
|
58
53
|
return this.readExact(1, label)[0]!;
|
|
59
54
|
}
|
|
@@ -103,40 +98,36 @@ const readPublicWrapperV2 = (
|
|
|
103
98
|
reader: BoundsReaderV2,
|
|
104
99
|
label: string,
|
|
105
100
|
): Uint8Array => {
|
|
106
|
-
|
|
107
|
-
|
|
101
|
+
if (
|
|
102
|
+
reader.readU8(`${label} MaybeEncrypted variant`) !== 0 ||
|
|
103
|
+
reader.readU8(`${label} DecryptedThing variant`) !== 0
|
|
104
|
+
) {
|
|
105
|
+
throw new Error(`Authority EntryV0 ${label} must be public`);
|
|
106
|
+
}
|
|
108
107
|
return reader.readBytes(label);
|
|
109
108
|
};
|
|
110
109
|
|
|
111
|
-
const
|
|
112
|
-
metaBytes: Uint8Array,
|
|
113
|
-
authorityBytes: Uint8Array,
|
|
114
|
-
maximumDirectParents: number,
|
|
115
|
-
): void => {
|
|
110
|
+
const scanMetaStructureV2 = (metaBytes: Uint8Array): number => {
|
|
116
111
|
const reader = new BoundsReaderV2(metaBytes);
|
|
117
112
|
reader.expectU8(0, "metadata variant");
|
|
118
113
|
reader.expectU8(0, "clock variant");
|
|
119
|
-
|
|
120
|
-
throw new Error("Authority EntryV0 clock id is not the policy authority");
|
|
121
|
-
}
|
|
114
|
+
reader.readBytes("clock id");
|
|
122
115
|
reader.expectU8(0, "timestamp variant");
|
|
123
116
|
reader.readExact(8, "timestamp wall time");
|
|
124
117
|
reader.readExact(4, "timestamp logical time");
|
|
125
118
|
reader.readBytes("gid");
|
|
126
119
|
|
|
127
120
|
const directParentCount = reader.readU32("direct-parent count");
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
);
|
|
121
|
+
// Every Borsh string starts with a four-byte length. Reject an impossible
|
|
122
|
+
// input-backed count before iterating or invoking the generic decoder.
|
|
123
|
+
if (directParentCount > Math.floor(reader.remaining / 4)) {
|
|
124
|
+
throw new Error("Authority EntryV0 has impossible direct-parent count");
|
|
132
125
|
}
|
|
133
126
|
for (let i = 0; i < directParentCount; i++) {
|
|
134
127
|
reader.readBytes("direct parent");
|
|
135
128
|
}
|
|
136
129
|
|
|
137
|
-
|
|
138
|
-
throw new Error("Authority EntryV0 must be an APPEND entry");
|
|
139
|
-
}
|
|
130
|
+
reader.readU8("entry type");
|
|
140
131
|
const metaDataOption = reader.readU8("metadata data option");
|
|
141
132
|
if (metaDataOption === 1) {
|
|
142
133
|
reader.readBytes("metadata data");
|
|
@@ -144,206 +135,111 @@ const scanMetaV2 = (
|
|
|
144
135
|
throw new Error("Authority EntryV0 has invalid metadata data option");
|
|
145
136
|
}
|
|
146
137
|
reader.expectDone("metadata");
|
|
138
|
+
return directParentCount;
|
|
147
139
|
};
|
|
148
140
|
|
|
149
|
-
const
|
|
150
|
-
payloadBytes: Uint8Array,
|
|
151
|
-
exactPayloadBytes?: number,
|
|
152
|
-
): void => {
|
|
141
|
+
const scanPayloadStructureV2 = (payloadBytes: Uint8Array): Uint8Array => {
|
|
153
142
|
const reader = new BoundsReaderV2(payloadBytes);
|
|
154
143
|
reader.expectU8(0, "payload variant");
|
|
155
144
|
const data = reader.readBytes("payload data");
|
|
156
|
-
if (
|
|
157
|
-
exactPayloadBytes !== undefined &&
|
|
158
|
-
data.byteLength !== exactPayloadBytes
|
|
159
|
-
) {
|
|
160
|
-
throw new Error(
|
|
161
|
-
`Authority EntryV0 payload must contain exactly ${exactPayloadBytes} bytes`,
|
|
162
|
-
);
|
|
163
|
-
}
|
|
164
145
|
reader.expectDone("payload");
|
|
146
|
+
return data;
|
|
165
147
|
};
|
|
166
148
|
|
|
167
|
-
const
|
|
168
|
-
const SECP256K1_LOW_S_MAX_HEX =
|
|
169
|
-
"7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0";
|
|
170
|
-
|
|
171
|
-
const isLowerHexByteV2 = (value: number): boolean =>
|
|
172
|
-
(value >= 0x30 && value <= 0x39) || (value >= 0x61 && value <= 0x66);
|
|
173
|
-
|
|
174
|
-
const assertCanonicalSecp256k1SignatureV2 = (signature: Uint8Array): void => {
|
|
175
|
-
if (
|
|
176
|
-
signature.byteLength !== SECP256K1_SIGNATURE_TEXT_BYTES ||
|
|
177
|
-
signature[0] !== 0x30 ||
|
|
178
|
-
signature[1] !== 0x78
|
|
179
|
-
) {
|
|
180
|
-
throw new Error("Authority EntryV0 secp256k1 signature is not canonical");
|
|
181
|
-
}
|
|
182
|
-
for (let i = 2; i < signature.byteLength; i++) {
|
|
183
|
-
if (!isLowerHexByteV2(signature[i]!)) {
|
|
184
|
-
throw new Error("Authority EntryV0 secp256k1 signature is not canonical");
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
if (
|
|
188
|
-
signature[130] !== 0x31 ||
|
|
189
|
-
(signature[131] !== 0x62 && signature[131] !== 0x63)
|
|
190
|
-
) {
|
|
191
|
-
throw new Error("Authority EntryV0 secp256k1 signature is not canonical");
|
|
192
|
-
}
|
|
193
|
-
for (let i = 0; i < SECP256K1_LOW_S_MAX_HEX.length; i++) {
|
|
194
|
-
const actual = signature[66 + i]!;
|
|
195
|
-
const maximum = SECP256K1_LOW_S_MAX_HEX.charCodeAt(i);
|
|
196
|
-
if (actual < maximum) break;
|
|
197
|
-
if (actual > maximum) {
|
|
198
|
-
throw new Error("Authority EntryV0 secp256k1 signature is not canonical");
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
const scanSignatureV2 = (
|
|
204
|
-
signatureBytes: Uint8Array,
|
|
205
|
-
authorityBytes: Uint8Array,
|
|
206
|
-
): void => {
|
|
149
|
+
const scanSignatureStructureV2 = (signatureBytes: Uint8Array): void => {
|
|
207
150
|
const reader = new BoundsReaderV2(signatureBytes);
|
|
208
151
|
reader.expectU8(0, "signature variant");
|
|
209
|
-
|
|
152
|
+
reader.readBytes("signature data");
|
|
210
153
|
const publicKeyVariant = reader.readU8("signature public-key variant");
|
|
211
154
|
const publicKeyLength =
|
|
212
155
|
publicKeyVariant === 0 ? 32 : publicKeyVariant === 1 ? 33 : undefined;
|
|
213
156
|
if (publicKeyLength === undefined) {
|
|
214
157
|
throw new Error("Authority EntryV0 uses an unsupported signing key");
|
|
215
158
|
}
|
|
216
|
-
|
|
217
|
-
const serializedPublicKey = new Uint8Array(publicKeyLength + 1);
|
|
218
|
-
serializedPublicKey[0] = publicKeyVariant;
|
|
219
|
-
serializedPublicKey.set(publicKey, 1);
|
|
220
|
-
if (!equals(serializedPublicKey, authorityBytes)) {
|
|
221
|
-
throw new Error("Authority EntryV0 signer is not the policy authority");
|
|
222
|
-
}
|
|
223
|
-
if (publicKeyVariant === 1) {
|
|
224
|
-
assertCanonicalSecp256k1SignatureV2(signature);
|
|
225
|
-
}
|
|
159
|
+
reader.readExact(publicKeyLength, "signature public key");
|
|
226
160
|
reader.readU8("signature prehash");
|
|
227
161
|
reader.expectDone("signature");
|
|
228
162
|
};
|
|
229
163
|
|
|
230
|
-
const
|
|
164
|
+
const scanEntryV0StructureV2 = (
|
|
231
165
|
entryBytes: Uint8Array,
|
|
232
|
-
|
|
233
|
-
limits: AuthorityEntryV0LimitsV2,
|
|
234
|
-
): ScannedAuthorityEntryV0 => {
|
|
166
|
+
): ScannedEntryV0StructureV2 => {
|
|
235
167
|
const reader = new BoundsReaderV2(entryBytes);
|
|
236
168
|
reader.expectU8(0, "entry variant");
|
|
237
169
|
const metaBytes = readPublicWrapperV2(reader, "metadata");
|
|
238
|
-
const
|
|
239
|
-
const
|
|
240
|
-
if (
|
|
241
|
-
reserved[0] !== 0 ||
|
|
242
|
-
reserved[1] !== 0 ||
|
|
243
|
-
reserved[2] !== 0 ||
|
|
244
|
-
reserved[3] !== 0
|
|
245
|
-
) {
|
|
246
|
-
throw new Error("Authority EntryV0 reserved bytes must be zero");
|
|
247
|
-
}
|
|
170
|
+
const payloadContainerBytes = readPublicWrapperV2(reader, "payload");
|
|
171
|
+
const reservedBytes = reader.readExact(4, "reserved bytes");
|
|
248
172
|
const signablePrefixLength = reader.position;
|
|
249
|
-
reader.
|
|
173
|
+
if (reader.readU8("signatures option") !== 1) {
|
|
174
|
+
throw new Error("Authority EntryV0 must contain exactly one signature");
|
|
175
|
+
}
|
|
250
176
|
reader.expectU8(0, "signatures variant");
|
|
251
177
|
if (reader.readU32("signature count") !== 1) {
|
|
252
178
|
throw new Error("Authority EntryV0 must contain exactly one signature");
|
|
253
179
|
}
|
|
254
180
|
const signatureBytes = readPublicWrapperV2(reader, "signature");
|
|
255
|
-
reader.
|
|
181
|
+
const hashOption = reader.readU8("hash option");
|
|
182
|
+
if (hashOption === 1) {
|
|
183
|
+
reader.readBytes("hash");
|
|
184
|
+
} else if (hashOption !== 0) {
|
|
185
|
+
throw new Error("Authority EntryV0 has invalid hash option");
|
|
186
|
+
}
|
|
256
187
|
reader.expectDone("storage");
|
|
257
188
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
189
|
+
const directParentCount = scanMetaStructureV2(metaBytes);
|
|
190
|
+
const payloadBytes = scanPayloadStructureV2(payloadContainerBytes);
|
|
191
|
+
scanSignatureStructureV2(signatureBytes);
|
|
261
192
|
const signableBytes = new Uint8Array(signablePrefixLength + 2);
|
|
262
193
|
signableBytes.set(entryBytes.subarray(0, signablePrefixLength));
|
|
263
|
-
return {
|
|
194
|
+
return {
|
|
195
|
+
metaBytes,
|
|
196
|
+
payloadBytes,
|
|
197
|
+
signableBytes,
|
|
198
|
+
reservedBytes,
|
|
199
|
+
hasHash: hashOption === 1,
|
|
200
|
+
directParentCount,
|
|
201
|
+
};
|
|
264
202
|
};
|
|
265
203
|
|
|
266
|
-
const
|
|
267
|
-
if (
|
|
268
|
-
|
|
269
|
-
limits.maximumEntryBytes < 1 ||
|
|
270
|
-
!Number.isSafeInteger(limits.maximumDirectParents) ||
|
|
271
|
-
limits.maximumDirectParents < 0 ||
|
|
272
|
-
(limits.exactPayloadBytes !== undefined &&
|
|
273
|
-
(!Number.isSafeInteger(limits.exactPayloadBytes) ||
|
|
274
|
-
limits.exactPayloadBytes < 0))
|
|
275
|
-
) {
|
|
276
|
-
throw new Error("Invalid internal authority EntryV0 limits");
|
|
204
|
+
const validateMaximumEntryBytesV2 = (maximumEntryBytes: number): void => {
|
|
205
|
+
if (!Number.isSafeInteger(maximumEntryBytes) || maximumEntryBytes < 1) {
|
|
206
|
+
throw new Error("Invalid internal authority EntryV0 byte limit");
|
|
277
207
|
}
|
|
278
208
|
};
|
|
279
209
|
|
|
280
|
-
const
|
|
281
|
-
parents: string[],
|
|
282
|
-
): Array<{ cid: string; digest: Uint8Array }> => {
|
|
283
|
-
const seen = new Set<string>();
|
|
284
|
-
return parents.map((cid) => {
|
|
285
|
-
let parsed: ReturnType<typeof cidifyString>;
|
|
286
|
-
try {
|
|
287
|
-
parsed = cidifyString(cid);
|
|
288
|
-
} catch {
|
|
289
|
-
throw new Error(
|
|
290
|
-
"Authority EntryV0 direct parents must use canonical CIDv1/raw/sha2-256",
|
|
291
|
-
);
|
|
292
|
-
}
|
|
293
|
-
if (
|
|
294
|
-
!cid ||
|
|
295
|
-
parsed.version !== 1 ||
|
|
296
|
-
parsed.code !== codecMap.raw.code ||
|
|
297
|
-
parsed.multihash.code !== defaultHasher.code ||
|
|
298
|
-
parsed.multihash.digest.byteLength !== 32 ||
|
|
299
|
-
stringifyCid(parsed) !== cid
|
|
300
|
-
) {
|
|
301
|
-
throw new Error(
|
|
302
|
-
"Authority EntryV0 direct parents must use canonical CIDv1/raw/sha2-256",
|
|
303
|
-
);
|
|
304
|
-
}
|
|
305
|
-
if (seen.has(cid)) {
|
|
306
|
-
throw new Error("Authority EntryV0 direct parents must be unique");
|
|
307
|
-
}
|
|
308
|
-
seen.add(cid);
|
|
309
|
-
return {
|
|
310
|
-
cid,
|
|
311
|
-
digest: copyUint8ArrayV2(parsed.multihash.digest),
|
|
312
|
-
};
|
|
313
|
-
});
|
|
314
|
-
};
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* Authenticate one bounded raw EntryV0 authority envelope. This establishes
|
|
318
|
-
* canonical bytes, authority signature, and structural facts only. It does not
|
|
319
|
-
* establish policy acceptance, causal ancestry, freshness, or durability.
|
|
320
|
-
*/
|
|
321
|
-
export const authenticateAuthorityEntryV0V2 = async (
|
|
210
|
+
export const captureAuthorityEntryV0BytesV2 = (
|
|
322
211
|
entryBytes: Uint8Array,
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
)
|
|
326
|
-
validateLimitsV2(limits);
|
|
212
|
+
maximumEntryBytes: number,
|
|
213
|
+
): Uint8Array => {
|
|
214
|
+
validateMaximumEntryBytesV2(maximumEntryBytes);
|
|
327
215
|
let byteLength: number;
|
|
328
216
|
try {
|
|
329
217
|
byteLength = exactUint8ArrayByteLengthV2(entryBytes);
|
|
330
218
|
} catch {
|
|
331
219
|
throw new Error("Authority entry must use canonical EntryV0 bytes");
|
|
332
220
|
}
|
|
333
|
-
if (byteLength < 1 || byteLength >
|
|
221
|
+
if (byteLength < 1 || byteLength > maximumEntryBytes) {
|
|
334
222
|
throw new Error(
|
|
335
|
-
`Authority EntryV0 must contain 1-${
|
|
223
|
+
`Authority EntryV0 must contain 1-${maximumEntryBytes} bytes`,
|
|
336
224
|
);
|
|
337
225
|
}
|
|
338
|
-
|
|
226
|
+
return copyUint8ArrayWithLengthV2(entryBytes, byteLength);
|
|
227
|
+
};
|
|
339
228
|
|
|
229
|
+
/**
|
|
230
|
+
* Authenticate one bounded raw EntryV0 authority envelope. This establishes
|
|
231
|
+
* canonical bytes and the sole public authority signature only. It does not
|
|
232
|
+
* establish a concrete record profile, policy acceptance, causal ancestry,
|
|
233
|
+
* freshness, or durability.
|
|
234
|
+
*/
|
|
235
|
+
export const authenticateCapturedAuthorityEntryV0V2 = async (
|
|
236
|
+
capturedEntryBytes: Uint8Array,
|
|
237
|
+
descriptor: NetworkDescriptorV2,
|
|
238
|
+
assertProfile?: AuthorityEntryV0ProfileV2,
|
|
239
|
+
): Promise<AuthenticatedAuthorityEntryV0V2> => {
|
|
340
240
|
assertNetworkDescriptorV2(descriptor);
|
|
341
241
|
const authorityBytes = serialize(descriptor.policyAuthority);
|
|
342
|
-
const scanned =
|
|
343
|
-
capturedEntryBytes,
|
|
344
|
-
authorityBytes,
|
|
345
|
-
limits,
|
|
346
|
-
);
|
|
242
|
+
const scanned = scanEntryV0StructureV2(capturedEntryBytes);
|
|
347
243
|
const capturedDescriptor = deserialize(
|
|
348
244
|
serialize(descriptor),
|
|
349
245
|
NetworkDescriptorV2,
|
|
@@ -374,37 +270,28 @@ export const authenticateAuthorityEntryV0V2 = async (
|
|
|
374
270
|
}
|
|
375
271
|
|
|
376
272
|
entry.init({ encoding: NO_ENCODING });
|
|
377
|
-
const meta = entry.meta;
|
|
378
273
|
const payload = entry.payload;
|
|
379
274
|
const signatures = entry.signatures;
|
|
380
|
-
if (
|
|
381
|
-
!equals(scanned.metaBytes, serialize(meta)) ||
|
|
382
|
-
!equals(scanned.payloadBytes, serialize(payload)) ||
|
|
383
|
-
!equals(scanned.signatureBytes, serialize(signatures[0]!))
|
|
384
|
-
) {
|
|
385
|
-
throw new Error("Authority EntryV0 nested encoding is not canonical");
|
|
386
|
-
}
|
|
387
|
-
if (
|
|
388
|
-
meta.type !== EntryType.APPEND ||
|
|
389
|
-
meta.next.length > limits.maximumDirectParents ||
|
|
390
|
-
!equals(meta.clock.id, authorityBytes)
|
|
391
|
-
) {
|
|
392
|
-
throw new Error("Authority EntryV0 metadata does not match its profile");
|
|
393
|
-
}
|
|
394
275
|
if (
|
|
395
276
|
signatures.length !== 1 ||
|
|
396
277
|
!equals(serialize(signatures[0]!.publicKey), authorityBytes)
|
|
397
278
|
) {
|
|
398
279
|
throw new Error("Authority EntryV0 signer is not the policy authority");
|
|
399
280
|
}
|
|
400
|
-
if (
|
|
401
|
-
|
|
402
|
-
payload.data.byteLength !== limits.exactPayloadBytes
|
|
403
|
-
) {
|
|
404
|
-
throw new Error(
|
|
405
|
-
`Authority EntryV0 payload must contain exactly ${limits.exactPayloadBytes} bytes`,
|
|
406
|
-
);
|
|
281
|
+
if (!equals(scanned.payloadBytes, payload.data)) {
|
|
282
|
+
throw new Error("Authority EntryV0 payload framing is inconsistent");
|
|
407
283
|
}
|
|
284
|
+
const authenticated: AuthenticatedAuthorityEntryV0V2 = {
|
|
285
|
+
descriptor: capturedDescriptor,
|
|
286
|
+
entryBytes: capturedEntryBytes,
|
|
287
|
+
entry,
|
|
288
|
+
metaBytes: scanned.metaBytes,
|
|
289
|
+
payloadBytes: payload.data,
|
|
290
|
+
reservedBytes: scanned.reservedBytes,
|
|
291
|
+
hasHash: scanned.hasHash,
|
|
292
|
+
directParentCount: scanned.directParentCount,
|
|
293
|
+
};
|
|
294
|
+
assertProfile?.(authenticated);
|
|
408
295
|
|
|
409
296
|
let signatureIsValid = false;
|
|
410
297
|
try {
|
|
@@ -416,15 +303,17 @@ export const authenticateAuthorityEntryV0V2 = async (
|
|
|
416
303
|
throw new Error("Authority EntryV0 signature is invalid");
|
|
417
304
|
}
|
|
418
305
|
|
|
419
|
-
|
|
420
|
-
return {
|
|
421
|
-
descriptor: capturedDescriptor,
|
|
422
|
-
entryBytes: capturedEntryBytes,
|
|
423
|
-
entryCid: prepared.cid,
|
|
424
|
-
entryDigest: copyUint8ArrayV2(prepared.block.cid.multihash.digest),
|
|
425
|
-
gid: meta.gid,
|
|
426
|
-
metaData: meta.data === undefined ? undefined : copyUint8ArrayV2(meta.data),
|
|
427
|
-
directParents: canonicalDirectParentsV2(meta.next),
|
|
428
|
-
payloadBytes: copyUint8ArrayV2(payload.data),
|
|
429
|
-
};
|
|
306
|
+
return authenticated;
|
|
430
307
|
};
|
|
308
|
+
|
|
309
|
+
export const authenticateAuthorityEntryV0V2 = async (
|
|
310
|
+
entryBytes: Uint8Array,
|
|
311
|
+
descriptor: NetworkDescriptorV2,
|
|
312
|
+
maximumEntryBytes: number,
|
|
313
|
+
assertProfile?: AuthorityEntryV0ProfileV2,
|
|
314
|
+
): Promise<AuthenticatedAuthorityEntryV0V2> =>
|
|
315
|
+
authenticateCapturedAuthorityEntryV0V2(
|
|
316
|
+
captureAuthorityEntryV0BytesV2(entryBytes, maximumEntryBytes),
|
|
317
|
+
descriptor,
|
|
318
|
+
assertProfile,
|
|
319
|
+
);
|
package/src/v2-policy-engine.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { deserialize, serialize } from "@dao-xyz/borsh";
|
|
2
|
-
import {
|
|
3
|
-
import { Entry, EntryV0, NO_ENCODING } from "@peerbit/log";
|
|
2
|
+
import { PublicSignKey } from "@peerbit/crypto";
|
|
4
3
|
import { compare, equals } from "uint8arrays";
|
|
4
|
+
import { authenticateCapturedAuthorityEntryV0V2 } from "./v2-authority-entry.js";
|
|
5
5
|
import {
|
|
6
6
|
NetworkDescriptorV2,
|
|
7
7
|
PolicySnapshotBodyV2,
|
|
@@ -304,58 +304,11 @@ const authenticateCapturedPolicySnapshotEntryV2 = async (
|
|
|
304
304
|
canonicalEntryBytes: Uint8Array,
|
|
305
305
|
descriptor: NetworkDescriptorV2,
|
|
306
306
|
): Promise<ValidatedPolicySnapshotV2> => {
|
|
307
|
-
const
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
throw new Error("Policy snapshot entry encoding is not canonical");
|
|
313
|
-
}
|
|
314
|
-
if (!(authenticatedEntry._meta instanceof DecryptedThing)) {
|
|
315
|
-
throw new Error("Policy snapshot metadata must be public");
|
|
316
|
-
}
|
|
317
|
-
if (!(authenticatedEntry._payload instanceof DecryptedThing)) {
|
|
318
|
-
throw new Error("Policy snapshot payload must be public");
|
|
319
|
-
}
|
|
320
|
-
if (
|
|
321
|
-
authenticatedEntry._signatures === undefined ||
|
|
322
|
-
authenticatedEntry._signatures.signatures.length !== 1
|
|
323
|
-
) {
|
|
324
|
-
throw new Error("Policy snapshot must contain exactly one signature");
|
|
325
|
-
}
|
|
326
|
-
if (
|
|
327
|
-
!(authenticatedEntry._signatures.signatures[0] instanceof DecryptedThing)
|
|
328
|
-
) {
|
|
329
|
-
throw new Error("Policy snapshot signature must be public");
|
|
330
|
-
}
|
|
331
|
-
authenticatedEntry.init({ encoding: NO_ENCODING });
|
|
332
|
-
|
|
333
|
-
const signatures = await authenticatedEntry.getSignatures();
|
|
334
|
-
if (signatures.length !== 1) {
|
|
335
|
-
throw new Error("Policy snapshot must resolve exactly one signature");
|
|
336
|
-
}
|
|
337
|
-
const signature = signatures[0]!;
|
|
338
|
-
if (
|
|
339
|
-
!equals(
|
|
340
|
-
serialize(signature.publicKey),
|
|
341
|
-
serialize(descriptor.policyAuthority),
|
|
342
|
-
)
|
|
343
|
-
) {
|
|
344
|
-
throw new Error("Policy snapshot signer is not the policy authority");
|
|
345
|
-
}
|
|
346
|
-
if (!(await verify(signature, authenticatedEntry.getSignableBytes()))) {
|
|
347
|
-
throw new Error("Policy snapshot authority signature is invalid");
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
const payload = await authenticatedEntry.getPayloadValue();
|
|
351
|
-
let canonicalPayload: Uint8Array;
|
|
352
|
-
try {
|
|
353
|
-
canonicalPayload = copyBytes(payload as Uint8Array);
|
|
354
|
-
} catch {
|
|
355
|
-
throw new Error(
|
|
356
|
-
"Policy snapshot payload must contain canonical body bytes",
|
|
357
|
-
);
|
|
358
|
-
}
|
|
307
|
+
const authenticated = await authenticateCapturedAuthorityEntryV0V2(
|
|
308
|
+
canonicalEntryBytes,
|
|
309
|
+
descriptor,
|
|
310
|
+
);
|
|
311
|
+
const canonicalPayload = authenticated.payloadBytes;
|
|
359
312
|
const body = decodePolicySnapshotBodyV2(canonicalPayload, descriptor);
|
|
360
313
|
const digest = digestPolicySnapshotBodyV2(body);
|
|
361
314
|
return {
|