@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/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022-2026 statewalker
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# @statewalker/webrun-biscuit
|
|
2
|
+
|
|
3
|
+
[Biscuit](https://biscuitsec.org) authorization tokens in pure TypeScript: the protobuf codec, the
|
|
4
|
+
signature chain, the Datalog engine, the text parser, the authorizer and the token builder. No WASM,
|
|
5
|
+
no Node built-ins in `src/`, two runtime dependencies. It runs wherever the rest of the wire runs —
|
|
6
|
+
Node, browsers, Workers and Durable Objects.
|
|
7
|
+
|
|
8
|
+
## Why this package exists
|
|
9
|
+
|
|
10
|
+
There is no other pure JS/TS Biscuit implementation. npm carries `@biscuit-auth/biscuit-wasm` — the
|
|
11
|
+
Rust crate compiled to WebAssembly — and a web-components package built on it. Nothing else.
|
|
12
|
+
|
|
13
|
+
For a browser-first or Durable-Object context a WASM blob is a real cost, not a stylistic objection:
|
|
14
|
+
bundle size, an instantiation step, and a platform surface that is not available everywhere. A token
|
|
15
|
+
format whose whole point is that the holder can attenuate it locally should not require half a
|
|
16
|
+
megabyte of WebAssembly to do so.
|
|
17
|
+
|
|
18
|
+
So this is a reimplementation, and the entire testing strategy below exists because a reimplementation
|
|
19
|
+
of a security primitive is only worth having if you can show it agrees with the original.
|
|
20
|
+
|
|
21
|
+
## Installing and calling it
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
npm install @statewalker/webrun-biscuit
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Two runtime dependencies, both external to the published bundle:
|
|
28
|
+
[`@noble/curves`](https://www.npmjs.com/package/@noble/curves) and
|
|
29
|
+
[`@noble/hashes`](https://www.npmjs.com/package/@noble/hashes). ESM only.
|
|
30
|
+
|
|
31
|
+
Mint a token, attenuate it, seal it, then verify and authorize:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { Biscuit, generateKeypair } from "@statewalker/webrun-biscuit";
|
|
35
|
+
|
|
36
|
+
const root = generateKeypair();
|
|
37
|
+
|
|
38
|
+
const token = Biscuit.build(root.secretKey, 'user("alice"); right("file1", "read");')
|
|
39
|
+
.attenuate('check if operation("read");')
|
|
40
|
+
.seal()
|
|
41
|
+
.toBase64();
|
|
42
|
+
|
|
43
|
+
const verified = Biscuit.fromBase64(token).verify(root.publicKey);
|
|
44
|
+
const result = verified.authorize('operation("read"); allow if user("alice");');
|
|
45
|
+
// { kind: 'ok', policy: 0 }
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`verify()` throws on an inauthentic token and returns a `VerifiedBiscuit`. That is deliberate: the
|
|
49
|
+
type system then prevents authorizing a token whose signature chain was never checked, which is the
|
|
50
|
+
mistake worth designing against.
|
|
51
|
+
|
|
52
|
+
### Examples
|
|
53
|
+
|
|
54
|
+
secp256r1 works the same way, with the algorithm passed at both ends:
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
const root = generateKeypair(1);
|
|
58
|
+
const token = Biscuit.build(root.secretKey, 'user("alice");', { algorithm: 1 });
|
|
59
|
+
const verified = Biscuit.fromBase64(token.toBase64()).verify(root.publicKey, 1);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
For key rotation, read the issuer's key id before choosing a root key. The token is unverified at
|
|
63
|
+
that point, so treat the id as a hint rather than a claim:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { peekRootKeyId } from "@statewalker/webrun-biscuit";
|
|
67
|
+
|
|
68
|
+
const key = roots[peekRootKeyId(bytes) ?? 0];
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
A third party can sign a block without ever holding the token:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
const request = Biscuit.fromBase64(token).thirdPartyRequest();
|
|
75
|
+
const response = thirdPartyBlock(request, externalSecret, 'group("admin");');
|
|
76
|
+
const extended = Biscuit.fromBase64(token).appendThirdParty(response);
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Internals
|
|
80
|
+
|
|
81
|
+
### How the modules stack up
|
|
82
|
+
|
|
83
|
+
Each module depends only on the ones above it.
|
|
84
|
+
|
|
85
|
+
| path | what it does |
|
|
86
|
+
| --- | --- |
|
|
87
|
+
| `src/proto.ts` | strict proto2 codec — BigInt int64, presence tracking, UTF-8 validation, unknown-field rejection |
|
|
88
|
+
| `src/crypto.ts` | Ed25519 + secp256r1 chain, payload v0/v1, sealing, external signatures |
|
|
89
|
+
| `src/datalog.ts` | term model, canonical keys, origin-tracked fixpoint engine, expression VM |
|
|
90
|
+
| `src/parser.ts` | Datalog text syntax to the runtime model |
|
|
91
|
+
| `src/print.ts` | pretty-printer for error messages and world snapshots |
|
|
92
|
+
| `src/version.ts` | block version bounds and Datalog feature gates |
|
|
93
|
+
| `src/authorizer.ts` | symbol resolution, block loading, authorization, world snapshots |
|
|
94
|
+
| `src/builder.ts` | minting, attenuation, sealing, third-party blocks |
|
|
95
|
+
| `src/base64.ts` | URL-safe base64, runtime-agnostic |
|
|
96
|
+
| `src/index.ts` | the public API — `Biscuit`, `VerifiedBiscuit` |
|
|
97
|
+
|
|
98
|
+
The one awkward edge is that `builder` imports `parseAuthorizer` and `DEFAULT_SYMBOLS` from
|
|
99
|
+
`authorizer`. Both are block-level concerns that happen to live there; it is a naming artefact rather
|
|
100
|
+
than a layering problem, and it would move to a `symbols.ts` if the file grew.
|
|
101
|
+
|
|
102
|
+
### Decisions that would otherwise get undone
|
|
103
|
+
|
|
104
|
+
**Terms are compared through canonical string keys.** JS `Map` and `Set` are reference-keyed, while
|
|
105
|
+
the Rust implementation leans on derived `Hash`/`Ord` over a `BTreeSet`. Every structural comparison
|
|
106
|
+
and every de-duplication in the engine therefore goes through `termKey()`. Bypassing it gives you a
|
|
107
|
+
set that silently holds duplicates.
|
|
108
|
+
|
|
109
|
+
**Symbols are resolved to strings at load time** rather than kept as indices. Simpler, and
|
|
110
|
+
semantically equivalent — the only observable difference is that set ordering follows string order
|
|
111
|
+
instead of symbol-index order, which shows up in printing and not in results.
|
|
112
|
+
|
|
113
|
+
**Facts are indexed by `name/arity`**, cached per trusted-origin set and invalidated by a generation
|
|
114
|
+
counter, so a join never scans unrelated predicates.
|
|
115
|
+
|
|
116
|
+
**`Uint8Array` everywhere, never `Buffer`**, in `src/`. That is what makes the package runtime-
|
|
117
|
+
agnostic; tests may use Node APIs, the library may not.
|
|
118
|
+
|
|
119
|
+
**Default run limits are looser than the reference.** The reference defaults `max_time` to 1
|
|
120
|
+
millisecond, which is unreachably tight for a cold JS engine; this one defaults to 1 second. A caller
|
|
121
|
+
exposed to untrusted tokens should lower it deliberately rather than treat the default as a
|
|
122
|
+
denial-of-service bound.
|
|
123
|
+
|
|
124
|
+
### The corpus is fetched, not vendored
|
|
125
|
+
|
|
126
|
+
`scripts/fetch-samples.mjs` downloads the official corpus into `samples/` (gitignored), pinned to
|
|
127
|
+
commit `b3d3fe2` of the **specification repository**,
|
|
128
|
+
[`eclipse-biscuit/biscuit`](https://github.com/eclipse-biscuit/biscuit). The samples belong to the
|
|
129
|
+
biscuit project, so they are not copied into this repo.
|
|
130
|
+
|
|
131
|
+
The canonical corpus lives in `samples/current` there, **not** in any single implementation: the copy
|
|
132
|
+
shipped inside `biscuit-rust` has drifted, and differs in `samples.json` and `test034_array_map.bc`
|
|
133
|
+
by one check.
|
|
134
|
+
|
|
135
|
+
The deprecated `v1` and `v2` corpora are fetched too, as negative fixtures. A current implementation
|
|
136
|
+
must reject them: every block declares a Datalog version, and versions outside 3 to 6 are invalid.
|
|
137
|
+
Without that check a v2 token verifies cleanly, which is a vulnerability rather than a compatibility
|
|
138
|
+
nicety.
|
|
139
|
+
|
|
140
|
+
Beyond the final authorization result, `09-world-snapshot` compares the entire post-run world against
|
|
141
|
+
the corpus — every derived fact, with its origin set. That catches rule-evaluation errors that happen
|
|
142
|
+
not to change the verdict.
|
|
143
|
+
|
|
144
|
+
### A green suite is not the claim; a suite that can fail is
|
|
145
|
+
|
|
146
|
+
```sh
|
|
147
|
+
pnpm test # the main suite, 156 tests
|
|
148
|
+
pnpm test:cross # against the reference implementation, 55 tests
|
|
149
|
+
pnpm test:all # both
|
|
150
|
+
pnpm mutate # inject 10 known defects, require the suite to catch each
|
|
151
|
+
pnpm build # dist/ plus declarations
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
| file | covers |
|
|
155
|
+
| --- | --- |
|
|
156
|
+
| `01-proto` | byte-exact round-trip of all 38 sample tokens, i64 extremes, strict rejection |
|
|
157
|
+
| `02-crypto` | signature chain outcomes and revocation ids for all samples |
|
|
158
|
+
| `03-datalog` | engine unit tests ported from the Rust `datalog` module |
|
|
159
|
+
| `04-parser` | terms, precedence, closures, scopes, predicate/expression disambiguation |
|
|
160
|
+
| `05-conformance` | the official `samples.json` corpus — 38 tokens, 50 validations |
|
|
161
|
+
| `06-builder` | write path, corpus rebuilt from source, forged seals, deny policies |
|
|
162
|
+
| `07-api` | base64 and the public facade |
|
|
163
|
+
| `08-hardening` | property round-trips, mutation/truncation fuzzing, join scaling |
|
|
164
|
+
| `09-world-snapshot` | the post-run world per origin, against the official snapshots |
|
|
165
|
+
| `10-versions` | version bounds, feature gates, rejection of the deprecated v1/v2 corpora |
|
|
166
|
+
|
|
167
|
+
`scripts/mutate.mjs` is the check on all of it. It injects ten defects — a lenient protobuf decoder,
|
|
168
|
+
wrapping i64 arithmetic, `check all` degraded to `check if`, a universally trusting authorizer, `deny`
|
|
169
|
+
treated as `allow`, unchecked seal signatures, missing version bounds, and more — and requires each to
|
|
170
|
+
break at least one test. A surviving mutation is a hole in the tests, not a success. All ten are
|
|
171
|
+
caught. Two of them were **not** caught when the harness was first written: nothing verified a forged
|
|
172
|
+
seal signature, and nothing exercised a matching `deny` policy.
|
|
173
|
+
|
|
174
|
+
Each mutation's `find` string is a literal excerpt of the source, and must match exactly once. That is
|
|
175
|
+
why a reformat of `src/` makes the harness fail loudly rather than quietly stop testing anything.
|
|
176
|
+
|
|
177
|
+
### Cross-reference tests run against the reference itself
|
|
178
|
+
|
|
179
|
+
`tests/cross/` checks interoperability against the Rust `biscuit-auth` crate compiled to WebAssembly
|
|
180
|
+
and published as `@biscuit-auth/biscuit-wasm`. These tests are only meaningful because the other side
|
|
181
|
+
is genuinely upstream, not a second reading of the same spec.
|
|
182
|
+
|
|
183
|
+
| file | direction |
|
|
184
|
+
| --- | --- |
|
|
185
|
+
| `01-native-to-ts` | the reference mints, we read — including a differential check on verdict, policy index and revocation ids |
|
|
186
|
+
| `02-ts-to-native` | we mint, the reference reads |
|
|
187
|
+
| `03-interop-chains` | blocks appended alternately by both, sealing honoured across the boundary, base64 interop, agreement on the stamped Datalog version |
|
|
188
|
+
| `04-random-differential` | generated programs, both directions, both algorithms — the reference is the oracle |
|
|
189
|
+
|
|
190
|
+
Nothing in `04` hard-codes the expected answer: each generated program is authorized by both
|
|
191
|
+
implementations and the verdicts compared, so a disagreement is a finding either way. Programs are
|
|
192
|
+
built around a (resource, operation, user) scenario and then perturbed rather than sampled uniformly,
|
|
193
|
+
because uniform sampling produces mostly `noMatchingPolicy`, where the engine barely runs.
|
|
194
|
+
|
|
195
|
+
That suite is kept out of `pnpm test` on purpose, and it skips gracefully when the reference is not
|
|
196
|
+
installed, so it never becomes a hard dependency of the main suite.
|
|
197
|
+
|
|
198
|
+
## What will surprise you
|
|
199
|
+
|
|
200
|
+
**The reference build's first `authorize` call reports a timeout that is not real.** Every fresh
|
|
201
|
+
Authorizer reports a `RunLimit` timeout on its first call even with a 60-second budget; later calls on
|
|
202
|
+
the same object return in under a millisecond. `referenceOutcome` retries, so a genuine timeout still
|
|
203
|
+
fails. The same defect shows up under CPU contention on any call, which is why `pnpm test:cross` is a
|
|
204
|
+
separate script: a flaky reference must never be able to redden the main suite.
|
|
205
|
+
|
|
206
|
+
**The reference's run limits are a serde `Duration`.** `max_time` must be `{ secs, nanos }`. Passing a
|
|
207
|
+
plain number of nanoseconds does **not** error — deserialization fails quietly and the *default* 1 ms
|
|
208
|
+
limit applies, which then times out. It looks exactly like a slow engine.
|
|
209
|
+
|
|
210
|
+
**`PublicKey.toBytes()` throws** in that build. `toString()` returns `"<algorithm>/<hex>"` and is the
|
|
211
|
+
reliable accessor.
|
|
212
|
+
|
|
213
|
+
**The reference package cannot be imported in Node.** Its entry point does
|
|
214
|
+
`import * as wasm from "./biscuit_bg.wasm"`, which Node will not resolve, and its `exports` map has no
|
|
215
|
+
`require` condition, so `require.resolve` fails too. `tests/cross/reference.ts` finds the package by
|
|
216
|
+
walking up to `node_modules`, reads the `.wasm`, and supplies its imports by hand. Running Node with
|
|
217
|
+
`--experimental-wasm-modules` does work, but needing a flag for `pnpm test` is worse.
|
|
218
|
+
|
|
219
|
+
**A failed corpus download used to poison `samples/` permanently.** `samples.json` was written before
|
|
220
|
+
the tokens it names while the "already fetched" guard checked only `samples.json`, so an interrupted
|
|
221
|
+
fetch left a directory that every later run skipped as complete — and the tests then failed on missing
|
|
222
|
+
files forever. The manifest is now written last, and the guard checks every file it names. Downloads
|
|
223
|
+
are also pooled at six with retries, because `raw.githubusercontent.com` resets connections when
|
|
224
|
+
several dozen requests arrive at once and surfaces it as a bare `TypeError: fetch failed`.
|
|
225
|
+
|
|
226
|
+
**`pnpm test` fetches the corpus explicitly, not through `pretest`.** pnpm does not run `pre`/`post`
|
|
227
|
+
scripts by default, so a `pretest` hook would silently never fire and the suite would fail on a clean
|
|
228
|
+
checkout with missing samples.
|
|
229
|
+
|
|
230
|
+
## Known deviations from the reference
|
|
231
|
+
|
|
232
|
+
**Regex uses JS `RegExp`, not RE2.** Every corpus pattern matches, but backreferences and lookaround
|
|
233
|
+
are accepted where Rust would reject them. Documented, not enforced.
|
|
234
|
+
|
|
235
|
+
**New blocks are signed with signature payload version 1** unconditionally. Datalog block versions, by
|
|
236
|
+
contrast, are computed from content: a block declares the lowest version that can legally carry it.
|
|
237
|
+
|
|
238
|
+
**Arrays and maps do not trigger the v3.3 feature gate.** The reference's `contains_v3_3_term` flags
|
|
239
|
+
only `null` and sets containing `null`, so a block using arrays may legally declare v3.1. This looks
|
|
240
|
+
like an upstream oversight and is mirrored deliberately, because matching the reference matters more
|
|
241
|
+
than being right here. `03-interop-chains` confirms it empirically.
|
|
242
|
+
|
|
243
|
+
**Not implemented:** authorizer snapshots (`AuthorizerSnapshot` / `SnapshotBlock`); a `query(rule)` API
|
|
244
|
+
for pulling facts out of an evaluated world, where `authorizeDetailed` exposes the whole world more
|
|
245
|
+
coarsely; a revocation-checking helper, since revocation ids are exposed but comparing them against a
|
|
246
|
+
list is left to the caller; and wire-compatible third-party blocks — `thirdPartyRequest()` returns a
|
|
247
|
+
plain object rather than the `ThirdPartyBlockRequest` protobuf message, so our own end-to-end
|
|
248
|
+
third-party flow works and is tested, but the cross-implementation one does not.
|
|
249
|
+
|
|
250
|
+
Semi-naive evaluation is not implemented either. The fixpoint re-derives every fact each round; the
|
|
251
|
+
index makes that cheap at token scale, but not at request scale with large fact sets.
|
|
252
|
+
|
|
253
|
+
## Design notes
|
|
254
|
+
|
|
255
|
+
`docs/webrun-biscuit/` in this repository carries the working documents from the implementation
|
|
256
|
+
sessions: how the reference actually works, why `mapbox/pbf` was rejected for the protobuf layer, what
|
|
257
|
+
the red-green cycle caught, the corpus findings, and what it took to make random differential testing
|
|
258
|
+
actually detect a defect.
|
|
259
|
+
|
|
260
|
+
## License
|
|
261
|
+
|
|
262
|
+
MIT — see the repository `LICENSE`.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turns a serialized token plus authorizer code into an authorization result,
|
|
3
|
+
* mirroring the evaluation order of the reference implementation.
|
|
4
|
+
*/
|
|
5
|
+
import { type Check, type ExternFn, type Fact, type Rule, type RunLimits, type Scope } from "./datalog.js";
|
|
6
|
+
export declare const DEFAULT_SYMBOLS: string[];
|
|
7
|
+
export declare class TokenError extends Error {
|
|
8
|
+
readonly kind: "Format" | "Symbol";
|
|
9
|
+
constructor(kind: "Format" | "Symbol", message: string);
|
|
10
|
+
}
|
|
11
|
+
export interface RuntimeBlock {
|
|
12
|
+
facts: Fact[];
|
|
13
|
+
rules: Rule[];
|
|
14
|
+
checks: Check[];
|
|
15
|
+
scopes: Scope[];
|
|
16
|
+
externalKey?: string;
|
|
17
|
+
/** variable id -> name, for printing rules in error messages */
|
|
18
|
+
varNames: Map<number, string>;
|
|
19
|
+
}
|
|
20
|
+
export interface LoadedToken {
|
|
21
|
+
blocks: RuntimeBlock[];
|
|
22
|
+
publicKeyToBlockIds: Map<string, number[]>;
|
|
23
|
+
revocationIds: string[];
|
|
24
|
+
/** the issuer's key identifier, when the token carries one — callers use it
|
|
25
|
+
* to pick the right root key during rotation */
|
|
26
|
+
rootKeyId?: number;
|
|
27
|
+
}
|
|
28
|
+
export declare function loadToken(bytes: Uint8Array, rootPublicKey: Uint8Array,
|
|
29
|
+
/** the root key's algorithm: 0 = Ed25519, 1 = secp256r1 */
|
|
30
|
+
rootAlgorithm?: 0 | 1): LoadedToken;
|
|
31
|
+
/**
|
|
32
|
+
* Reads the key identifier from a token **without verifying it**, so a caller
|
|
33
|
+
* can choose which root key to verify against. The token is untrusted at this
|
|
34
|
+
* point: the id is a hint, not a claim.
|
|
35
|
+
*/
|
|
36
|
+
export declare function peekRootKeyId(bytes: Uint8Array): number | undefined;
|
|
37
|
+
export type FailedCheck = {
|
|
38
|
+
source: "authorizer";
|
|
39
|
+
checkId: number;
|
|
40
|
+
} | {
|
|
41
|
+
source: "block";
|
|
42
|
+
blockId: number;
|
|
43
|
+
checkId: number;
|
|
44
|
+
};
|
|
45
|
+
export type AuthorizationResult = {
|
|
46
|
+
kind: "ok";
|
|
47
|
+
policy: number;
|
|
48
|
+
} | {
|
|
49
|
+
kind: "unauthorized";
|
|
50
|
+
policy: {
|
|
51
|
+
allow: number;
|
|
52
|
+
} | {
|
|
53
|
+
deny: number;
|
|
54
|
+
};
|
|
55
|
+
checks: FailedCheck[];
|
|
56
|
+
} | {
|
|
57
|
+
kind: "noMatchingPolicy";
|
|
58
|
+
checks: FailedCheck[];
|
|
59
|
+
} | {
|
|
60
|
+
kind: "execution";
|
|
61
|
+
error: string;
|
|
62
|
+
} | {
|
|
63
|
+
kind: "invalidBlockRule";
|
|
64
|
+
blockId: number;
|
|
65
|
+
rule: string;
|
|
66
|
+
} | {
|
|
67
|
+
kind: "format";
|
|
68
|
+
error: string;
|
|
69
|
+
};
|
|
70
|
+
/** the shape of the `world` snapshot in the official sample corpus */
|
|
71
|
+
export interface WorldSnapshot {
|
|
72
|
+
facts: {
|
|
73
|
+
origin: (number | null)[];
|
|
74
|
+
facts: string[];
|
|
75
|
+
}[];
|
|
76
|
+
rules: {
|
|
77
|
+
origin: number;
|
|
78
|
+
rules: string[];
|
|
79
|
+
}[];
|
|
80
|
+
checks: {
|
|
81
|
+
origin: number;
|
|
82
|
+
checks: string[];
|
|
83
|
+
}[];
|
|
84
|
+
policies: string[];
|
|
85
|
+
}
|
|
86
|
+
export interface AuthorizeDetails {
|
|
87
|
+
result: AuthorizationResult;
|
|
88
|
+
world: WorldSnapshot;
|
|
89
|
+
}
|
|
90
|
+
interface AuthorizerCode {
|
|
91
|
+
facts: Fact[];
|
|
92
|
+
rules: Rule[];
|
|
93
|
+
checks: Check[];
|
|
94
|
+
policies: {
|
|
95
|
+
kind: "allow" | "deny";
|
|
96
|
+
queries: Rule[];
|
|
97
|
+
}[];
|
|
98
|
+
scopes: Scope[];
|
|
99
|
+
varNames: Map<number, string>;
|
|
100
|
+
}
|
|
101
|
+
export declare function parseAuthorizer(src: string): AuthorizerCode;
|
|
102
|
+
export interface AuthorizeOptions {
|
|
103
|
+
limits?: RunLimits;
|
|
104
|
+
/** extern functions callable as `.extern::name(...)` */
|
|
105
|
+
externs?: Map<string, ExternFn>;
|
|
106
|
+
}
|
|
107
|
+
export declare function authorize(token: LoadedToken, authorizerSrc: string, options?: AuthorizeOptions): AuthorizationResult;
|
|
108
|
+
/** Same as `authorize`, and also returns the post-run world, in the shape the
|
|
109
|
+
* official sample corpus records it. */
|
|
110
|
+
export declare function authorizeDetailed(token: LoadedToken, authorizerSrc: string, options?: AuthorizeOptions): AuthorizeDetails;
|
|
111
|
+
export {};
|
|
112
|
+
//# sourceMappingURL=authorizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authorizer.d.ts","sourceRoot":"","sources":["../src/authorizer.ts"],"names":[],"mappings":"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"}
|
package/dist/base64.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base64.d.ts","sourceRoot":"","sources":["../src/base64.ts"],"names":[],"mappings":"AAQA,wBAAgB,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAclD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAiBnD"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The write path: minting tokens, attenuating them with extra blocks, and
|
|
3
|
+
* sealing them. New blocks are signed with signature payload version 1, and
|
|
4
|
+
* declare the lowest Datalog version their content legally requires.
|
|
5
|
+
*/
|
|
6
|
+
import { generateKeypair } from "./crypto.js";
|
|
7
|
+
import type { Check, Predicate, Rule, Scope } from "./datalog.js";
|
|
8
|
+
import { type BlockMsg, type PublicKeyMsg } from "./proto.js";
|
|
9
|
+
export declare class BuilderError extends Error {
|
|
10
|
+
}
|
|
11
|
+
export interface Keypair {
|
|
12
|
+
secretKey: Uint8Array;
|
|
13
|
+
publicKey: Uint8Array;
|
|
14
|
+
}
|
|
15
|
+
export { generateKeypair };
|
|
16
|
+
export interface BlockContent {
|
|
17
|
+
facts: {
|
|
18
|
+
predicate: Predicate;
|
|
19
|
+
}[];
|
|
20
|
+
rules: Rule[];
|
|
21
|
+
checks: Check[];
|
|
22
|
+
scopes: Scope[];
|
|
23
|
+
}
|
|
24
|
+
export declare function buildBlockMsg(content: BlockContent, knownSymbols?: string[], knownKeys?: string[], minVersion?: number): BlockMsg;
|
|
25
|
+
export interface BuildOptions {
|
|
26
|
+
rootKeyId?: number;
|
|
27
|
+
/** supply a next keypair instead of generating one (tests, determinism) */
|
|
28
|
+
nextKeypair?: Keypair;
|
|
29
|
+
algorithm?: 0 | 1;
|
|
30
|
+
}
|
|
31
|
+
/** Mint a new token whose authority block holds `code`. */
|
|
32
|
+
export declare function buildToken(rootSecret: Uint8Array, code: string | BlockContent, options?: BuildOptions): Uint8Array;
|
|
33
|
+
/** Append an attenuation block. Uses the token's own proof secret to sign. */
|
|
34
|
+
export declare function attenuate(tokenBytes: Uint8Array, code: string | BlockContent, options?: BuildOptions): Uint8Array;
|
|
35
|
+
/** Seal a token so no further block can be appended. */
|
|
36
|
+
export declare function sealToken(tokenBytes: Uint8Array): Uint8Array;
|
|
37
|
+
export interface ThirdPartyRequest {
|
|
38
|
+
/** signature of the block this attenuation will follow */
|
|
39
|
+
previousSignature: Uint8Array;
|
|
40
|
+
}
|
|
41
|
+
/** What a token holder sends to a third party that will sign a block. */
|
|
42
|
+
export declare function thirdPartyRequest(tokenBytes: Uint8Array): ThirdPartyRequest;
|
|
43
|
+
export interface ThirdPartyResponse {
|
|
44
|
+
block: Uint8Array;
|
|
45
|
+
signature: Uint8Array;
|
|
46
|
+
publicKey: PublicKeyMsg;
|
|
47
|
+
}
|
|
48
|
+
/** The third party builds and signs a block without holding the token. */
|
|
49
|
+
export declare function thirdPartyBlock(request: ThirdPartyRequest, externalSecret: Uint8Array, code: string | BlockContent, algorithm?: 0 | 1): ThirdPartyResponse;
|
|
50
|
+
/** The token holder appends a block signed by a third party. */
|
|
51
|
+
export declare function appendThirdParty(tokenBytes: Uint8Array, response: ThirdPartyResponse, options?: BuildOptions): Uint8Array;
|
|
52
|
+
//# sourceMappingURL=builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAIL,eAAe,EAIhB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,KAAK,EAAM,SAAS,EAAE,IAAI,EAAE,KAAK,EAAQ,MAAM,cAAc,CAAC;AAC5E,OAAO,EAEL,KAAK,QAAQ,EAQb,KAAK,YAAY,EAIlB,MAAM,YAAY,CAAC;AAMpB,qBAAa,YAAa,SAAQ,KAAK;CAAG;AAE1C,MAAM,WAAW,OAAO;IACtB,SAAS,EAAE,UAAU,CAAC;IACtB,SAAS,EAAE,UAAU,CAAC;CACvB;AACD,OAAO,EAAE,eAAe,EAAE,CAAC;AAgH3B,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE;QAAE,SAAS,EAAE,SAAS,CAAA;KAAE,EAAE,CAAC;IAClC,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,MAAM,EAAE,KAAK,EAAE,CAAC;CACjB;AAED,wBAAgB,aAAa,CAC3B,OAAO,EAAE,YAAY,EACrB,YAAY,GAAE,MAAM,EAAO,EAC3B,SAAS,GAAE,MAAM,EAAO,EACxB,UAAU,SAAI,GACb,QAAQ,CAkBV;AA4BD,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;CACnB;AAED,2DAA2D;AAC3D,wBAAgB,UAAU,CACxB,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,MAAM,GAAG,YAAY,EAC3B,OAAO,GAAE,YAAiB,GACzB,UAAU,CAgBZ;AAED,8EAA8E;AAC9E,wBAAgB,SAAS,CACvB,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,MAAM,GAAG,YAAY,EAC3B,OAAO,GAAE,YAAiB,GACzB,UAAU,CA4BZ;AAED,wDAAwD;AACxD,wBAAgB,SAAS,CAAC,UAAU,EAAE,UAAU,GAAG,UAAU,CAO5D;AAID,MAAM,WAAW,iBAAiB;IAChC,0DAA0D;IAC1D,iBAAiB,EAAE,UAAU,CAAC;CAC/B;AAED,yEAAyE;AACzE,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,UAAU,GAAG,iBAAiB,CAI3E;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,UAAU,CAAC;IAClB,SAAS,EAAE,UAAU,CAAC;IACtB,SAAS,EAAE,YAAY,CAAC;CACzB;AAED,0EAA0E;AAC1E,wBAAgB,eAAe,CAC7B,OAAO,EAAE,iBAAiB,EAC1B,cAAc,EAAE,UAAU,EAC1B,IAAI,EAAE,MAAM,GAAG,YAAY,EAC3B,SAAS,GAAE,CAAC,GAAG,CAAK,GACnB,kBAAkB,CAWpB;AAED,gEAAgE;AAChE,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,GAAE,YAAiB,GACzB,UAAU,CA2BZ"}
|
package/dist/crypto.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { BiscuitMsg, PublicKeyMsg, SignedBlock } from "./proto.js";
|
|
2
|
+
export declare class SignatureError extends Error {
|
|
3
|
+
}
|
|
4
|
+
declare const bytesEqual: (a: Uint8Array, b: Uint8Array) => boolean;
|
|
5
|
+
export declare function publicKeyFromSecret(secret: Uint8Array, algorithm: number): Uint8Array;
|
|
6
|
+
export declare function blockPayloadV0(data: Uint8Array, nextKey: PublicKeyMsg, externalSig?: Uint8Array): Uint8Array;
|
|
7
|
+
export declare function authorityPayloadV1(data: Uint8Array, nextKey: PublicKeyMsg, version: number): Uint8Array;
|
|
8
|
+
export declare function blockPayloadV1(data: Uint8Array, nextKey: PublicKeyMsg, externalSig: Uint8Array | undefined, previousSignature: Uint8Array, version: number): Uint8Array;
|
|
9
|
+
export declare function externalPayloadV1(data: Uint8Array, previousSignature: Uint8Array, version: number): Uint8Array;
|
|
10
|
+
export declare function sealPayloadV0(block: SignedBlock): Uint8Array;
|
|
11
|
+
export declare function sign(payload: Uint8Array, secret: Uint8Array, algorithm: number): Uint8Array;
|
|
12
|
+
/** a fresh keypair for the given algorithm */
|
|
13
|
+
export declare function generateKeypair(algorithm?: 0 | 1): {
|
|
14
|
+
secretKey: Uint8Array;
|
|
15
|
+
publicKey: Uint8Array;
|
|
16
|
+
};
|
|
17
|
+
export { bytesEqual };
|
|
18
|
+
/** Verifies the full signature chain and the proof. Throws on failure. */
|
|
19
|
+
export declare function verifyToken(token: BiscuitMsg, rootPublicKey: Uint8Array, rootAlgorithm?: number): void;
|
|
20
|
+
/** Revocation identifier of each block: its signature bytes. */
|
|
21
|
+
export declare function revocationIds(token: BiscuitMsg): Uint8Array[];
|
|
22
|
+
//# sourceMappingURL=crypto.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAExE,qBAAa,cAAe,SAAQ,KAAK;CAAG;AAkB5C,QAAA,MAAM,UAAU,MAAO,UAAU,KAAK,UAAU,KAAG,OACK,CAAC;AAwBzD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,GAAG,UAAU,CAIrF;AAID,wBAAgB,cAAc,CAC5B,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,YAAY,EACrB,WAAW,CAAC,EAAE,UAAU,GACvB,UAAU,CAEZ;AAED,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,MAAM,GACd,UAAU,CAWZ;AAED,wBAAgB,cAAc,CAC5B,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,YAAY,EACrB,WAAW,EAAE,UAAU,GAAG,SAAS,EACnC,iBAAiB,EAAE,UAAU,EAC7B,OAAO,EAAE,MAAM,GACd,UAAU,CAcZ;AAED,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,UAAU,EAChB,iBAAiB,EAAE,UAAU,EAC7B,OAAO,EAAE,MAAM,GACd,UAAU,CASZ;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,UAAU,CAE5D;AAED,wBAAgB,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,GAAG,UAAU,CAK3F;AAED,8CAA8C;AAC9C,wBAAgB,eAAe,CAAC,SAAS,GAAE,CAAC,GAAG,CAAK,GAAG;IACrD,SAAS,EAAE,UAAU,CAAC;IACtB,SAAS,EAAE,UAAU,CAAC;CACvB,CAIA;AAED,OAAO,EAAE,UAAU,EAAE,CAAC;AAItB,0EAA0E;AAC1E,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,aAAa,SAAI,GAAG,IAAI,CAuDjG;AAED,gEAAgE;AAChE,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,EAAE,CAE7D"}
|