@statewalker/webrun-biscuit 0.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/LICENSE +21 -0
- package/README.md +262 -0
- package/dist/authorizer.d.ts +112 -0
- package/dist/authorizer.d.ts.map +1 -0
- package/dist/base64.d.ts +3 -0
- package/dist/base64.d.ts.map +1 -0
- package/dist/builder.d.ts +52 -0
- package/dist/builder.d.ts.map +1 -0
- package/dist/crypto.d.ts +22 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/datalog.d.ts +203 -0
- package/dist/datalog.d.ts.map +1 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3130 -0
- package/dist/parser.d.ts +80 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/print.d.ts +12 -0
- package/dist/print.d.ts.map +1 -0
- package/dist/proto.d.ts +132 -0
- package/dist/proto.d.ts.map +1 -0
- package/dist/version.d.ts +42 -0
- package/dist/version.d.ts.map +1 -0
- package/package.json +58 -0
- package/src/authorizer.ts +581 -0
- package/src/base64.ts +42 -0
- package/src/builder.ts +360 -0
- package/src/crypto.ts +208 -0
- package/src/datalog.ts +722 -0
- package/src/index.ts +93 -0
- package/src/parser.ts +606 -0
- package/src/print.ts +147 -0
- package/src/proto.ts +770 -0
- package/src/version.ts +158 -0
package/src/proto.ts
ADDED
|
@@ -0,0 +1,770 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strict proto2 codec for the Biscuit schema.
|
|
3
|
+
*
|
|
4
|
+
* Written against src/schema.proto. Unlike a generated codec it: decodes
|
|
5
|
+
* int64/uint64 as BigInt, tracks proto2 `optional` presence, validates UTF-8
|
|
6
|
+
* strictly, and rejects unknown fields, wrong wire types and missing
|
|
7
|
+
* `required` fields.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export class ProtoError extends Error {}
|
|
11
|
+
|
|
12
|
+
const utf8 = new TextDecoder("utf-8", { fatal: true });
|
|
13
|
+
const utf8enc = new TextEncoder();
|
|
14
|
+
|
|
15
|
+
/* ------------------------------------------------------------------ reader */
|
|
16
|
+
|
|
17
|
+
class Reader {
|
|
18
|
+
pos: number;
|
|
19
|
+
constructor(
|
|
20
|
+
readonly buf: Uint8Array,
|
|
21
|
+
pos = 0,
|
|
22
|
+
readonly end = buf.length,
|
|
23
|
+
) {
|
|
24
|
+
this.pos = pos;
|
|
25
|
+
}
|
|
26
|
+
get done(): boolean {
|
|
27
|
+
return this.pos >= this.end;
|
|
28
|
+
}
|
|
29
|
+
byte(): number {
|
|
30
|
+
if (this.pos >= this.end) throw new ProtoError("unexpected end of buffer");
|
|
31
|
+
return this.buf[this.pos++];
|
|
32
|
+
}
|
|
33
|
+
varint(): bigint {
|
|
34
|
+
let result = 0n;
|
|
35
|
+
let shift = 0n;
|
|
36
|
+
let b: number;
|
|
37
|
+
do {
|
|
38
|
+
if (shift > 63n) throw new ProtoError("varint overflow");
|
|
39
|
+
b = this.byte();
|
|
40
|
+
result |= BigInt(b & 0x7f) << shift;
|
|
41
|
+
shift += 7n;
|
|
42
|
+
} while (b & 0x80);
|
|
43
|
+
return BigInt.asUintN(64, result);
|
|
44
|
+
}
|
|
45
|
+
varintNum(): number {
|
|
46
|
+
const v = this.varint();
|
|
47
|
+
if (v > BigInt(Number.MAX_SAFE_INTEGER)) throw new ProtoError("varint too large for a number");
|
|
48
|
+
return Number(v);
|
|
49
|
+
}
|
|
50
|
+
bytes(): Uint8Array {
|
|
51
|
+
const len = this.varintNum();
|
|
52
|
+
if (this.pos + len > this.end) throw new ProtoError("length-delimited field overruns buffer");
|
|
53
|
+
const out = this.buf.subarray(this.pos, this.pos + len);
|
|
54
|
+
this.pos += len;
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
string(): string {
|
|
58
|
+
try {
|
|
59
|
+
return utf8.decode(this.bytes());
|
|
60
|
+
} catch {
|
|
61
|
+
throw new ProtoError("invalid UTF-8 in string field");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
sub(): Reader {
|
|
65
|
+
const b = this.bytes();
|
|
66
|
+
return new Reader(b, 0, b.length);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
type FieldHandler = (field: number, wire: number, r: Reader) => void;
|
|
71
|
+
|
|
72
|
+
function readFields(r: Reader, handler: FieldHandler): void {
|
|
73
|
+
while (!r.done) {
|
|
74
|
+
const tag = r.varintNum();
|
|
75
|
+
const field = tag >> 3;
|
|
76
|
+
const wire = tag & 7;
|
|
77
|
+
if (field === 0) throw new ProtoError("invalid field number 0");
|
|
78
|
+
handler(field, wire, r);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function expect(wire: number, want: number, field: number): void {
|
|
83
|
+
if (wire !== want) throw new ProtoError(`wrong wire type ${wire} for field ${field}`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function required<T>(v: T | undefined, name: string): T {
|
|
87
|
+
if (v === undefined) throw new ProtoError(`missing required field ${name}`);
|
|
88
|
+
return v;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* ------------------------------------------------------------------ writer */
|
|
92
|
+
|
|
93
|
+
class Writer {
|
|
94
|
+
private buf = new Uint8Array(256);
|
|
95
|
+
private pos = 0;
|
|
96
|
+
private grow(n: number): void {
|
|
97
|
+
if (this.pos + n <= this.buf.length) return;
|
|
98
|
+
let len = this.buf.length;
|
|
99
|
+
while (len < this.pos + n) len *= 2;
|
|
100
|
+
const next = new Uint8Array(len);
|
|
101
|
+
next.set(this.buf.subarray(0, this.pos));
|
|
102
|
+
this.buf = next;
|
|
103
|
+
}
|
|
104
|
+
varint(v: bigint | number): void {
|
|
105
|
+
let x = BigInt.asUintN(64, BigInt(v));
|
|
106
|
+
this.grow(10);
|
|
107
|
+
do {
|
|
108
|
+
let b = Number(x & 0x7fn);
|
|
109
|
+
x >>= 7n;
|
|
110
|
+
if (x) b |= 0x80;
|
|
111
|
+
this.buf[this.pos++] = b;
|
|
112
|
+
} while (x);
|
|
113
|
+
}
|
|
114
|
+
tag(field: number, wire: number): void {
|
|
115
|
+
this.varint((field << 3) | wire);
|
|
116
|
+
}
|
|
117
|
+
varintField(field: number, v: bigint | number): void {
|
|
118
|
+
this.tag(field, 0);
|
|
119
|
+
this.varint(v);
|
|
120
|
+
}
|
|
121
|
+
bytesField(field: number, v: Uint8Array): void {
|
|
122
|
+
this.tag(field, 2);
|
|
123
|
+
this.varint(v.length);
|
|
124
|
+
this.grow(v.length);
|
|
125
|
+
this.buf.set(v, this.pos);
|
|
126
|
+
this.pos += v.length;
|
|
127
|
+
}
|
|
128
|
+
stringField(field: number, v: string): void {
|
|
129
|
+
this.bytesField(field, utf8enc.encode(v));
|
|
130
|
+
}
|
|
131
|
+
messageField<T>(field: number, write: (v: T, w: Writer) => void, v: T): void {
|
|
132
|
+
const w = new Writer();
|
|
133
|
+
write(v, w);
|
|
134
|
+
this.bytesField(field, w.finish());
|
|
135
|
+
}
|
|
136
|
+
finish(): Uint8Array {
|
|
137
|
+
return this.buf.slice(0, this.pos);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* ------------------------------------------------------------------- types */
|
|
142
|
+
|
|
143
|
+
export type Algorithm = 0 | 1; // Ed25519 | SECP256R1
|
|
144
|
+
|
|
145
|
+
export interface PublicKeyMsg {
|
|
146
|
+
algorithm: Algorithm;
|
|
147
|
+
key: Uint8Array;
|
|
148
|
+
}
|
|
149
|
+
export interface ExternalSignature {
|
|
150
|
+
signature: Uint8Array;
|
|
151
|
+
publicKey: PublicKeyMsg;
|
|
152
|
+
}
|
|
153
|
+
export interface SignedBlock {
|
|
154
|
+
block: Uint8Array;
|
|
155
|
+
nextKey: PublicKeyMsg;
|
|
156
|
+
signature: Uint8Array;
|
|
157
|
+
externalSignature?: ExternalSignature;
|
|
158
|
+
version?: number;
|
|
159
|
+
}
|
|
160
|
+
export type Proof =
|
|
161
|
+
| { kind: "nextSecret"; value: Uint8Array }
|
|
162
|
+
| { kind: "finalSignature"; value: Uint8Array };
|
|
163
|
+
|
|
164
|
+
export interface BiscuitMsg {
|
|
165
|
+
rootKeyId?: number;
|
|
166
|
+
authority: SignedBlock;
|
|
167
|
+
blocks: SignedBlock[];
|
|
168
|
+
proof: Proof;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export type TermMsg =
|
|
172
|
+
| { kind: "variable"; value: number }
|
|
173
|
+
| { kind: "integer"; value: bigint }
|
|
174
|
+
| { kind: "string"; value: number }
|
|
175
|
+
| { kind: "date"; value: bigint }
|
|
176
|
+
| { kind: "bytes"; value: Uint8Array }
|
|
177
|
+
| { kind: "bool"; value: boolean }
|
|
178
|
+
| { kind: "set"; value: TermMsg[] }
|
|
179
|
+
| { kind: "null" }
|
|
180
|
+
| { kind: "array"; value: TermMsg[] }
|
|
181
|
+
| { kind: "map"; value: MapEntryMsg[] };
|
|
182
|
+
|
|
183
|
+
export type MapKeyMsg = { kind: "integer"; value: bigint } | { kind: "string"; value: number };
|
|
184
|
+
export interface MapEntryMsg {
|
|
185
|
+
key: MapKeyMsg;
|
|
186
|
+
value: TermMsg;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export type OpMsg =
|
|
190
|
+
| { kind: "value"; value: TermMsg }
|
|
191
|
+
| { kind: "unary"; op: number; ffiName?: number }
|
|
192
|
+
| { kind: "binary"; op: number; ffiName?: number }
|
|
193
|
+
| { kind: "closure"; params: number[]; ops: OpMsg[] };
|
|
194
|
+
|
|
195
|
+
export interface PredicateMsg {
|
|
196
|
+
name: number;
|
|
197
|
+
terms: TermMsg[];
|
|
198
|
+
}
|
|
199
|
+
export type ScopeMsg = { kind: "type"; value: 0 | 1 } | { kind: "publicKey"; value: number };
|
|
200
|
+
export interface RuleMsg {
|
|
201
|
+
head: PredicateMsg;
|
|
202
|
+
body: PredicateMsg[];
|
|
203
|
+
expressions: OpMsg[][];
|
|
204
|
+
scope: ScopeMsg[];
|
|
205
|
+
}
|
|
206
|
+
export interface CheckMsg {
|
|
207
|
+
queries: RuleMsg[];
|
|
208
|
+
kind?: 0 | 1 | 2; // One | All | Reject
|
|
209
|
+
}
|
|
210
|
+
export interface BlockMsg {
|
|
211
|
+
symbols: string[];
|
|
212
|
+
context?: string;
|
|
213
|
+
version?: number;
|
|
214
|
+
facts: PredicateMsg[];
|
|
215
|
+
rules: RuleMsg[];
|
|
216
|
+
checks: CheckMsg[];
|
|
217
|
+
scope: ScopeMsg[];
|
|
218
|
+
publicKeys: PublicKeyMsg[];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/* ------------------------------------------------------------------ decode */
|
|
222
|
+
|
|
223
|
+
function readPublicKey(r: Reader): PublicKeyMsg {
|
|
224
|
+
let algorithm: number | undefined;
|
|
225
|
+
let key: Uint8Array | undefined;
|
|
226
|
+
readFields(r, (f, w, rr) => {
|
|
227
|
+
if (f === 1) {
|
|
228
|
+
expect(w, 0, f);
|
|
229
|
+
algorithm = rr.varintNum();
|
|
230
|
+
} else if (f === 2) {
|
|
231
|
+
expect(w, 2, f);
|
|
232
|
+
key = rr.bytes();
|
|
233
|
+
} else throw new ProtoError(`unknown field ${f} in PublicKey`);
|
|
234
|
+
});
|
|
235
|
+
const alg = required(algorithm, "PublicKey.algorithm");
|
|
236
|
+
if (alg !== 0 && alg !== 1) throw new ProtoError(`unknown signature algorithm ${alg}`);
|
|
237
|
+
return { algorithm: alg as Algorithm, key: required(key, "PublicKey.key") };
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function readExternalSignature(r: Reader): ExternalSignature {
|
|
241
|
+
let signature: Uint8Array | undefined;
|
|
242
|
+
let publicKey: PublicKeyMsg | undefined;
|
|
243
|
+
readFields(r, (f, w, rr) => {
|
|
244
|
+
if (f === 1) {
|
|
245
|
+
expect(w, 2, f);
|
|
246
|
+
signature = rr.bytes();
|
|
247
|
+
} else if (f === 2) {
|
|
248
|
+
expect(w, 2, f);
|
|
249
|
+
publicKey = readPublicKey(rr.sub());
|
|
250
|
+
} else throw new ProtoError(`unknown field ${f} in ExternalSignature`);
|
|
251
|
+
});
|
|
252
|
+
return {
|
|
253
|
+
signature: required(signature, "ExternalSignature.signature"),
|
|
254
|
+
publicKey: required(publicKey, "ExternalSignature.publicKey"),
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function readSignedBlock(r: Reader): SignedBlock {
|
|
259
|
+
const out: Partial<SignedBlock> = {};
|
|
260
|
+
readFields(r, (f, w, rr) => {
|
|
261
|
+
if (f === 1) {
|
|
262
|
+
expect(w, 2, f);
|
|
263
|
+
out.block = rr.bytes();
|
|
264
|
+
} else if (f === 2) {
|
|
265
|
+
expect(w, 2, f);
|
|
266
|
+
out.nextKey = readPublicKey(rr.sub());
|
|
267
|
+
} else if (f === 3) {
|
|
268
|
+
expect(w, 2, f);
|
|
269
|
+
out.signature = rr.bytes();
|
|
270
|
+
} else if (f === 4) {
|
|
271
|
+
expect(w, 2, f);
|
|
272
|
+
out.externalSignature = readExternalSignature(rr.sub());
|
|
273
|
+
} else if (f === 5) {
|
|
274
|
+
expect(w, 0, f);
|
|
275
|
+
out.version = rr.varintNum();
|
|
276
|
+
} else throw new ProtoError(`unknown field ${f} in SignedBlock`);
|
|
277
|
+
});
|
|
278
|
+
return {
|
|
279
|
+
block: required(out.block, "SignedBlock.block"),
|
|
280
|
+
nextKey: required(out.nextKey, "SignedBlock.nextKey"),
|
|
281
|
+
signature: required(out.signature, "SignedBlock.signature"),
|
|
282
|
+
externalSignature: out.externalSignature,
|
|
283
|
+
version: out.version,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export function decodeBiscuit(buf: Uint8Array): BiscuitMsg {
|
|
288
|
+
const r = new Reader(buf);
|
|
289
|
+
let rootKeyId: number | undefined;
|
|
290
|
+
let authority: SignedBlock | undefined;
|
|
291
|
+
const blocks: SignedBlock[] = [];
|
|
292
|
+
let proof: Proof | undefined;
|
|
293
|
+
readFields(r, (f, w, rr) => {
|
|
294
|
+
if (f === 1) {
|
|
295
|
+
expect(w, 0, f);
|
|
296
|
+
rootKeyId = rr.varintNum();
|
|
297
|
+
} else if (f === 2) {
|
|
298
|
+
expect(w, 2, f);
|
|
299
|
+
authority = readSignedBlock(rr.sub());
|
|
300
|
+
} else if (f === 3) {
|
|
301
|
+
expect(w, 2, f);
|
|
302
|
+
blocks.push(readSignedBlock(rr.sub()));
|
|
303
|
+
} else if (f === 4) {
|
|
304
|
+
expect(w, 2, f);
|
|
305
|
+
const p = rr.sub();
|
|
306
|
+
readFields(p, (pf, pw, pr) => {
|
|
307
|
+
if (pf === 1) {
|
|
308
|
+
expect(pw, 2, pf);
|
|
309
|
+
proof = { kind: "nextSecret", value: pr.bytes() };
|
|
310
|
+
} else if (pf === 2) {
|
|
311
|
+
expect(pw, 2, pf);
|
|
312
|
+
proof = { kind: "finalSignature", value: pr.bytes() };
|
|
313
|
+
} else throw new ProtoError(`unknown field ${pf} in Proof`);
|
|
314
|
+
});
|
|
315
|
+
} else throw new ProtoError(`unknown field ${f} in Biscuit`);
|
|
316
|
+
});
|
|
317
|
+
return {
|
|
318
|
+
rootKeyId,
|
|
319
|
+
authority: required(authority, "Biscuit.authority"),
|
|
320
|
+
blocks,
|
|
321
|
+
proof: required(proof, "Biscuit.proof"),
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function readTerm(r: Reader): TermMsg {
|
|
326
|
+
let out: TermMsg | undefined;
|
|
327
|
+
readFields(r, (f, w, rr) => {
|
|
328
|
+
switch (f) {
|
|
329
|
+
case 1:
|
|
330
|
+
expect(w, 0, f);
|
|
331
|
+
out = { kind: "variable", value: rr.varintNum() };
|
|
332
|
+
break;
|
|
333
|
+
case 2:
|
|
334
|
+
expect(w, 0, f);
|
|
335
|
+
out = { kind: "integer", value: BigInt.asIntN(64, rr.varint()) };
|
|
336
|
+
break;
|
|
337
|
+
case 3:
|
|
338
|
+
expect(w, 0, f);
|
|
339
|
+
out = { kind: "string", value: rr.varintNum() };
|
|
340
|
+
break;
|
|
341
|
+
case 4:
|
|
342
|
+
expect(w, 0, f);
|
|
343
|
+
out = { kind: "date", value: rr.varint() };
|
|
344
|
+
break;
|
|
345
|
+
case 5:
|
|
346
|
+
expect(w, 2, f);
|
|
347
|
+
out = { kind: "bytes", value: rr.bytes() };
|
|
348
|
+
break;
|
|
349
|
+
case 6: {
|
|
350
|
+
expect(w, 0, f);
|
|
351
|
+
const v = rr.varintNum();
|
|
352
|
+
out = { kind: "bool", value: v !== 0 };
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
case 7: {
|
|
356
|
+
expect(w, 2, f);
|
|
357
|
+
const set: TermMsg[] = [];
|
|
358
|
+
readFields(rr.sub(), (sf, sw, sr) => {
|
|
359
|
+
if (sf !== 1) throw new ProtoError(`unknown field ${sf} in TermSet`);
|
|
360
|
+
expect(sw, 2, sf);
|
|
361
|
+
set.push(readTerm(sr.sub()));
|
|
362
|
+
});
|
|
363
|
+
out = { kind: "set", value: set };
|
|
364
|
+
break;
|
|
365
|
+
}
|
|
366
|
+
case 8:
|
|
367
|
+
expect(w, 2, f);
|
|
368
|
+
rr.bytes();
|
|
369
|
+
out = { kind: "null" };
|
|
370
|
+
break;
|
|
371
|
+
case 9: {
|
|
372
|
+
expect(w, 2, f);
|
|
373
|
+
const arr: TermMsg[] = [];
|
|
374
|
+
readFields(rr.sub(), (af, aw, ar) => {
|
|
375
|
+
if (af !== 1) throw new ProtoError(`unknown field ${af} in Array`);
|
|
376
|
+
expect(aw, 2, af);
|
|
377
|
+
arr.push(readTerm(ar.sub()));
|
|
378
|
+
});
|
|
379
|
+
out = { kind: "array", value: arr };
|
|
380
|
+
break;
|
|
381
|
+
}
|
|
382
|
+
case 10: {
|
|
383
|
+
expect(w, 2, f);
|
|
384
|
+
const entries: MapEntryMsg[] = [];
|
|
385
|
+
readFields(rr.sub(), (mf, mw, mr) => {
|
|
386
|
+
if (mf !== 1) throw new ProtoError(`unknown field ${mf} in Map`);
|
|
387
|
+
expect(mw, 2, mf);
|
|
388
|
+
entries.push(readMapEntry(mr.sub()));
|
|
389
|
+
});
|
|
390
|
+
out = { kind: "map", value: entries };
|
|
391
|
+
break;
|
|
392
|
+
}
|
|
393
|
+
default:
|
|
394
|
+
throw new ProtoError(`unknown field ${f} in Term`);
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
return required(out, "Term.Content");
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function readMapEntry(r: Reader): MapEntryMsg {
|
|
401
|
+
let key: MapKeyMsg | undefined;
|
|
402
|
+
let value: TermMsg | undefined;
|
|
403
|
+
readFields(r, (f, w, rr) => {
|
|
404
|
+
if (f === 1) {
|
|
405
|
+
expect(w, 2, f);
|
|
406
|
+
readFields(rr.sub(), (kf, kw, kr) => {
|
|
407
|
+
if (kf === 1) {
|
|
408
|
+
expect(kw, 0, kf);
|
|
409
|
+
key = { kind: "integer", value: BigInt.asIntN(64, kr.varint()) };
|
|
410
|
+
} else if (kf === 2) {
|
|
411
|
+
expect(kw, 0, kf);
|
|
412
|
+
key = { kind: "string", value: kr.varintNum() };
|
|
413
|
+
} else throw new ProtoError(`unknown field ${kf} in MapKey`);
|
|
414
|
+
});
|
|
415
|
+
} else if (f === 2) {
|
|
416
|
+
expect(w, 2, f);
|
|
417
|
+
value = readTerm(rr.sub());
|
|
418
|
+
} else throw new ProtoError(`unknown field ${f} in MapEntry`);
|
|
419
|
+
});
|
|
420
|
+
return { key: required(key, "MapEntry.key"), value: required(value, "MapEntry.value") };
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function readPredicate(r: Reader): PredicateMsg {
|
|
424
|
+
let name: number | undefined;
|
|
425
|
+
const terms: TermMsg[] = [];
|
|
426
|
+
readFields(r, (f, w, rr) => {
|
|
427
|
+
if (f === 1) {
|
|
428
|
+
expect(w, 0, f);
|
|
429
|
+
name = rr.varintNum();
|
|
430
|
+
} else if (f === 2) {
|
|
431
|
+
expect(w, 2, f);
|
|
432
|
+
terms.push(readTerm(rr.sub()));
|
|
433
|
+
} else throw new ProtoError(`unknown field ${f} in Predicate`);
|
|
434
|
+
});
|
|
435
|
+
return { name: required(name, "Predicate.name"), terms };
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function readOp(r: Reader): OpMsg {
|
|
439
|
+
let out: OpMsg | undefined;
|
|
440
|
+
readFields(r, (f, w, rr) => {
|
|
441
|
+
if (f === 1) {
|
|
442
|
+
expect(w, 2, f);
|
|
443
|
+
out = { kind: "value", value: readTerm(rr.sub()) };
|
|
444
|
+
} else if (f === 2 || f === 3) {
|
|
445
|
+
expect(w, 2, f);
|
|
446
|
+
let op: number | undefined;
|
|
447
|
+
let ffiName: number | undefined;
|
|
448
|
+
readFields(rr.sub(), (of_, ow, or_) => {
|
|
449
|
+
if (of_ === 1) {
|
|
450
|
+
expect(ow, 0, of_);
|
|
451
|
+
op = or_.varintNum();
|
|
452
|
+
} else if (of_ === 2) {
|
|
453
|
+
expect(ow, 0, of_);
|
|
454
|
+
ffiName = or_.varintNum();
|
|
455
|
+
} else throw new ProtoError(`unknown field ${of_} in Op`);
|
|
456
|
+
});
|
|
457
|
+
out = { kind: f === 2 ? "unary" : "binary", op: required(op, "Op.kind"), ffiName };
|
|
458
|
+
} else if (f === 4) {
|
|
459
|
+
expect(w, 2, f);
|
|
460
|
+
const params: number[] = [];
|
|
461
|
+
const ops: OpMsg[] = [];
|
|
462
|
+
readFields(rr.sub(), (cf, cw, cr) => {
|
|
463
|
+
if (cf === 1) {
|
|
464
|
+
if (cw === 0) params.push(cr.varintNum());
|
|
465
|
+
else if (cw === 2) {
|
|
466
|
+
const packed = cr.sub();
|
|
467
|
+
while (!packed.done) params.push(packed.varintNum());
|
|
468
|
+
} else throw new ProtoError("bad wire type for OpClosure.params");
|
|
469
|
+
} else if (cf === 2) {
|
|
470
|
+
expect(cw, 2, cf);
|
|
471
|
+
ops.push(readOp(cr.sub()));
|
|
472
|
+
} else throw new ProtoError(`unknown field ${cf} in OpClosure`);
|
|
473
|
+
});
|
|
474
|
+
out = { kind: "closure", params, ops };
|
|
475
|
+
} else throw new ProtoError(`unknown field ${f} in Op`);
|
|
476
|
+
});
|
|
477
|
+
return required(out, "Op.Content");
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function readScope(r: Reader): ScopeMsg {
|
|
481
|
+
let out: ScopeMsg | undefined;
|
|
482
|
+
readFields(r, (f, w, rr) => {
|
|
483
|
+
if (f === 1) {
|
|
484
|
+
expect(w, 0, f);
|
|
485
|
+
const v = rr.varintNum();
|
|
486
|
+
if (v !== 0 && v !== 1) throw new ProtoError(`unknown scope type ${v}`);
|
|
487
|
+
out = { kind: "type", value: v as 0 | 1 };
|
|
488
|
+
} else if (f === 2) {
|
|
489
|
+
expect(w, 0, f);
|
|
490
|
+
out = { kind: "publicKey", value: Number(BigInt.asIntN(64, rr.varint())) };
|
|
491
|
+
} else throw new ProtoError(`unknown field ${f} in Scope`);
|
|
492
|
+
});
|
|
493
|
+
return required(out, "Scope.Content");
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
function readRule(r: Reader): RuleMsg {
|
|
497
|
+
let head: PredicateMsg | undefined;
|
|
498
|
+
const body: PredicateMsg[] = [];
|
|
499
|
+
const expressions: OpMsg[][] = [];
|
|
500
|
+
const scope: ScopeMsg[] = [];
|
|
501
|
+
readFields(r, (f, w, rr) => {
|
|
502
|
+
if (f === 1) {
|
|
503
|
+
expect(w, 2, f);
|
|
504
|
+
head = readPredicate(rr.sub());
|
|
505
|
+
} else if (f === 2) {
|
|
506
|
+
expect(w, 2, f);
|
|
507
|
+
body.push(readPredicate(rr.sub()));
|
|
508
|
+
} else if (f === 3) {
|
|
509
|
+
expect(w, 2, f);
|
|
510
|
+
const ops: OpMsg[] = [];
|
|
511
|
+
readFields(rr.sub(), (ef, ew, er) => {
|
|
512
|
+
if (ef !== 1) throw new ProtoError(`unknown field ${ef} in Expression`);
|
|
513
|
+
expect(ew, 2, ef);
|
|
514
|
+
ops.push(readOp(er.sub()));
|
|
515
|
+
});
|
|
516
|
+
expressions.push(ops);
|
|
517
|
+
} else if (f === 4) {
|
|
518
|
+
expect(w, 2, f);
|
|
519
|
+
scope.push(readScope(rr.sub()));
|
|
520
|
+
} else throw new ProtoError(`unknown field ${f} in Rule`);
|
|
521
|
+
});
|
|
522
|
+
return { head: required(head, "Rule.head"), body, expressions, scope };
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
export function decodeBlock(buf: Uint8Array): BlockMsg {
|
|
526
|
+
const out: BlockMsg = {
|
|
527
|
+
symbols: [],
|
|
528
|
+
facts: [],
|
|
529
|
+
rules: [],
|
|
530
|
+
checks: [],
|
|
531
|
+
scope: [],
|
|
532
|
+
publicKeys: [],
|
|
533
|
+
};
|
|
534
|
+
readFields(new Reader(buf), (f, w, rr) => {
|
|
535
|
+
switch (f) {
|
|
536
|
+
case 1:
|
|
537
|
+
expect(w, 2, f);
|
|
538
|
+
out.symbols.push(rr.string());
|
|
539
|
+
break;
|
|
540
|
+
case 2:
|
|
541
|
+
expect(w, 2, f);
|
|
542
|
+
out.context = rr.string();
|
|
543
|
+
break;
|
|
544
|
+
case 3:
|
|
545
|
+
expect(w, 0, f);
|
|
546
|
+
out.version = rr.varintNum();
|
|
547
|
+
break;
|
|
548
|
+
case 4: {
|
|
549
|
+
expect(w, 2, f);
|
|
550
|
+
let pred: PredicateMsg | undefined;
|
|
551
|
+
readFields(rr.sub(), (ff, fw, fr) => {
|
|
552
|
+
if (ff !== 1) throw new ProtoError(`unknown field ${ff} in Fact`);
|
|
553
|
+
expect(fw, 2, ff);
|
|
554
|
+
pred = readPredicate(fr.sub());
|
|
555
|
+
});
|
|
556
|
+
out.facts.push(required(pred, "Fact.predicate"));
|
|
557
|
+
break;
|
|
558
|
+
}
|
|
559
|
+
case 5:
|
|
560
|
+
expect(w, 2, f);
|
|
561
|
+
out.rules.push(readRule(rr.sub()));
|
|
562
|
+
break;
|
|
563
|
+
case 6: {
|
|
564
|
+
expect(w, 2, f);
|
|
565
|
+
const queries: RuleMsg[] = [];
|
|
566
|
+
let kind: number | undefined;
|
|
567
|
+
readFields(rr.sub(), (cf, cw, cr) => {
|
|
568
|
+
if (cf === 1) {
|
|
569
|
+
expect(cw, 2, cf);
|
|
570
|
+
queries.push(readRule(cr.sub()));
|
|
571
|
+
} else if (cf === 2) {
|
|
572
|
+
expect(cw, 0, cf);
|
|
573
|
+
kind = cr.varintNum();
|
|
574
|
+
} else throw new ProtoError(`unknown field ${cf} in Check`);
|
|
575
|
+
});
|
|
576
|
+
if (kind !== undefined && kind !== 0 && kind !== 1 && kind !== 2)
|
|
577
|
+
throw new ProtoError(`unknown check kind ${kind}`);
|
|
578
|
+
out.checks.push({ queries, kind: kind as 0 | 1 | 2 | undefined });
|
|
579
|
+
break;
|
|
580
|
+
}
|
|
581
|
+
case 7:
|
|
582
|
+
expect(w, 2, f);
|
|
583
|
+
out.scope.push(readScope(rr.sub()));
|
|
584
|
+
break;
|
|
585
|
+
case 8:
|
|
586
|
+
expect(w, 2, f);
|
|
587
|
+
out.publicKeys.push(readPublicKey(rr.sub()));
|
|
588
|
+
break;
|
|
589
|
+
default:
|
|
590
|
+
throw new ProtoError(`unknown field ${f} in Block`);
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
return out;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/* ------------------------------------------------------------------ encode */
|
|
597
|
+
|
|
598
|
+
function writePublicKey(v: PublicKeyMsg, w: Writer): void {
|
|
599
|
+
w.varintField(1, v.algorithm);
|
|
600
|
+
w.bytesField(2, v.key);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function writeSignedBlock(v: SignedBlock, w: Writer): void {
|
|
604
|
+
w.bytesField(1, v.block);
|
|
605
|
+
w.messageField(2, writePublicKey, v.nextKey);
|
|
606
|
+
w.bytesField(3, v.signature);
|
|
607
|
+
if (v.externalSignature !== undefined)
|
|
608
|
+
w.messageField(
|
|
609
|
+
4,
|
|
610
|
+
(e: ExternalSignature, ww: Writer) => {
|
|
611
|
+
ww.bytesField(1, e.signature);
|
|
612
|
+
ww.messageField(2, writePublicKey, e.publicKey);
|
|
613
|
+
},
|
|
614
|
+
v.externalSignature,
|
|
615
|
+
);
|
|
616
|
+
if (v.version !== undefined) w.varintField(5, v.version);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
export function encodeBiscuit(v: BiscuitMsg): Uint8Array {
|
|
620
|
+
const w = new Writer();
|
|
621
|
+
if (v.rootKeyId !== undefined) w.varintField(1, v.rootKeyId);
|
|
622
|
+
w.messageField(2, writeSignedBlock, v.authority);
|
|
623
|
+
for (const b of v.blocks) w.messageField(3, writeSignedBlock, b);
|
|
624
|
+
w.messageField(
|
|
625
|
+
4,
|
|
626
|
+
(p: Proof, ww: Writer) => ww.bytesField(p.kind === "nextSecret" ? 1 : 2, p.value),
|
|
627
|
+
v.proof,
|
|
628
|
+
);
|
|
629
|
+
return w.finish();
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
function writeTerm(t: TermMsg, w: Writer): void {
|
|
633
|
+
switch (t.kind) {
|
|
634
|
+
case "variable":
|
|
635
|
+
w.varintField(1, t.value);
|
|
636
|
+
break;
|
|
637
|
+
case "integer":
|
|
638
|
+
w.varintField(2, t.value);
|
|
639
|
+
break;
|
|
640
|
+
case "string":
|
|
641
|
+
w.varintField(3, t.value);
|
|
642
|
+
break;
|
|
643
|
+
case "date":
|
|
644
|
+
w.varintField(4, t.value);
|
|
645
|
+
break;
|
|
646
|
+
case "bytes":
|
|
647
|
+
w.bytesField(5, t.value);
|
|
648
|
+
break;
|
|
649
|
+
case "bool":
|
|
650
|
+
w.varintField(6, t.value ? 1 : 0);
|
|
651
|
+
break;
|
|
652
|
+
case "set":
|
|
653
|
+
w.messageField(
|
|
654
|
+
7,
|
|
655
|
+
(items: TermMsg[], ww: Writer) => {
|
|
656
|
+
for (const i of items) ww.messageField(1, writeTerm, i);
|
|
657
|
+
},
|
|
658
|
+
t.value,
|
|
659
|
+
);
|
|
660
|
+
break;
|
|
661
|
+
case "null":
|
|
662
|
+
w.bytesField(8, new Uint8Array(0));
|
|
663
|
+
break;
|
|
664
|
+
case "array":
|
|
665
|
+
w.messageField(
|
|
666
|
+
9,
|
|
667
|
+
(items: TermMsg[], ww: Writer) => {
|
|
668
|
+
for (const i of items) ww.messageField(1, writeTerm, i);
|
|
669
|
+
},
|
|
670
|
+
t.value,
|
|
671
|
+
);
|
|
672
|
+
break;
|
|
673
|
+
case "map":
|
|
674
|
+
w.messageField(
|
|
675
|
+
10,
|
|
676
|
+
(entries: MapEntryMsg[], ww: Writer) => {
|
|
677
|
+
for (const e of entries)
|
|
678
|
+
ww.messageField(
|
|
679
|
+
1,
|
|
680
|
+
(en: MapEntryMsg, w3: Writer) => {
|
|
681
|
+
w3.messageField(
|
|
682
|
+
1,
|
|
683
|
+
(k: MapKeyMsg, w4: Writer) =>
|
|
684
|
+
w4.varintField(k.kind === "integer" ? 1 : 2, k.value),
|
|
685
|
+
en.key,
|
|
686
|
+
);
|
|
687
|
+
w3.messageField(2, writeTerm, en.value);
|
|
688
|
+
},
|
|
689
|
+
e,
|
|
690
|
+
);
|
|
691
|
+
},
|
|
692
|
+
t.value,
|
|
693
|
+
);
|
|
694
|
+
break;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
function writePredicate(p: PredicateMsg, w: Writer): void {
|
|
699
|
+
w.varintField(1, p.name);
|
|
700
|
+
for (const t of p.terms) w.messageField(2, writeTerm, t);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
function writeOp(op: OpMsg, w: Writer): void {
|
|
704
|
+
switch (op.kind) {
|
|
705
|
+
case "value":
|
|
706
|
+
w.messageField(1, writeTerm, op.value);
|
|
707
|
+
break;
|
|
708
|
+
case "unary":
|
|
709
|
+
case "binary":
|
|
710
|
+
w.messageField(
|
|
711
|
+
op.kind === "unary" ? 2 : 3,
|
|
712
|
+
(o: OpMsg & { op: number; ffiName?: number }, ww: Writer) => {
|
|
713
|
+
ww.varintField(1, o.op);
|
|
714
|
+
if (o.ffiName !== undefined) ww.varintField(2, o.ffiName);
|
|
715
|
+
},
|
|
716
|
+
op,
|
|
717
|
+
);
|
|
718
|
+
break;
|
|
719
|
+
case "closure":
|
|
720
|
+
w.messageField(
|
|
721
|
+
4,
|
|
722
|
+
(c: { params: number[]; ops: OpMsg[] }, ww: Writer) => {
|
|
723
|
+
for (const p of c.params) ww.varintField(1, p);
|
|
724
|
+
for (const o of c.ops) ww.messageField(2, writeOp, o);
|
|
725
|
+
},
|
|
726
|
+
op,
|
|
727
|
+
);
|
|
728
|
+
break;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
function writeScope(s: ScopeMsg, w: Writer): void {
|
|
733
|
+
w.varintField(s.kind === "type" ? 1 : 2, s.value);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
function writeRule(r: RuleMsg, w: Writer): void {
|
|
737
|
+
w.messageField(1, writePredicate, r.head);
|
|
738
|
+
for (const p of r.body) w.messageField(2, writePredicate, p);
|
|
739
|
+
for (const e of r.expressions)
|
|
740
|
+
w.messageField(
|
|
741
|
+
3,
|
|
742
|
+
(ops: OpMsg[], ww: Writer) => {
|
|
743
|
+
for (const o of ops) ww.messageField(1, writeOp, o);
|
|
744
|
+
},
|
|
745
|
+
e,
|
|
746
|
+
);
|
|
747
|
+
for (const s of r.scope) w.messageField(4, writeScope, s);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
export function encodeBlock(b: BlockMsg): Uint8Array {
|
|
751
|
+
const w = new Writer();
|
|
752
|
+
for (const s of b.symbols) w.stringField(1, s);
|
|
753
|
+
if (b.context !== undefined) w.stringField(2, b.context);
|
|
754
|
+
if (b.version !== undefined) w.varintField(3, b.version);
|
|
755
|
+
for (const f of b.facts)
|
|
756
|
+
w.messageField(4, (p: PredicateMsg, ww: Writer) => ww.messageField(1, writePredicate, p), f);
|
|
757
|
+
for (const r of b.rules) w.messageField(5, writeRule, r);
|
|
758
|
+
for (const c of b.checks)
|
|
759
|
+
w.messageField(
|
|
760
|
+
6,
|
|
761
|
+
(ch: CheckMsg, ww: Writer) => {
|
|
762
|
+
for (const q of ch.queries) ww.messageField(1, writeRule, q);
|
|
763
|
+
if (ch.kind !== undefined) ww.varintField(2, ch.kind);
|
|
764
|
+
},
|
|
765
|
+
c,
|
|
766
|
+
);
|
|
767
|
+
for (const s of b.scope) w.messageField(7, writeScope, s);
|
|
768
|
+
for (const k of b.publicKeys) w.messageField(8, writePublicKey, k);
|
|
769
|
+
return w.finish();
|
|
770
|
+
}
|