@sanning/proof 0.19.1 → 0.20.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/README.md +30 -29
- package/dist/agent-record.d.ts +62 -0
- package/dist/agent-record.js +527 -0
- package/dist/agent-schemas.generated.d.ts +15 -0
- package/dist/agent-schemas.generated.js +5759 -0
- package/dist/case-file.d.ts +32 -89
- package/dist/case-file.js +41 -93
- package/dist/case.d.ts +339 -822
- package/dist/case.js +667 -1160
- package/dist/chain.d.ts +19 -47
- package/dist/chain.js +78 -205
- package/dist/claims.d.ts +211 -0
- package/dist/claims.js +1081 -0
- package/dist/cli.d.ts +45 -68
- package/dist/cli.js +618 -1022
- package/dist/compose.js +23 -65
- package/dist/crypto.d.ts +7 -8
- package/dist/crypto.js +60 -147
- package/dist/declaration.d.ts +68 -194
- package/dist/declaration.js +175 -518
- package/dist/environment.d.ts +9 -30
- package/dist/environment.js +17 -66
- package/dist/evidence.d.ts +119 -160
- package/dist/evidence.js +744 -1103
- package/dist/exit-codes.d.ts +11 -27
- package/dist/exit-codes.js +24 -59
- package/dist/file-match.d.ts +33 -79
- package/dist/file-match.js +33 -69
- package/dist/grading.d.ts +98 -135
- package/dist/grading.js +224 -380
- package/dist/handoff.d.ts +62 -160
- package/dist/handoff.js +153 -393
- package/dist/index.d.ts +14 -10
- package/dist/index.js +60 -167
- package/dist/json-schema.d.ts +29 -0
- package/dist/json-schema.js +298 -0
- package/dist/merkle.js +16 -28
- package/dist/message-parts.d.ts +56 -212
- package/dist/message-parts.js +142 -502
- package/dist/side-content.d.ts +11 -22
- package/dist/side-content.js +22 -65
- package/dist/types.d.ts +17 -0
- package/dist/types.js +19 -5
- package/dist/verifier.d.ts +40 -114
- package/dist/verifier.js +196 -535
- package/dist/witness-cache.d.ts +24 -45
- package/dist/witness-cache.js +38 -81
- package/dist/witness-lookup.d.ts +36 -57
- package/dist/witness-lookup.js +89 -139
- package/dist/zip-reader.d.ts +18 -33
- package/dist/zip-reader.js +19 -34
- package/package.json +11 -3
- package/src/agent-record.ts +608 -0
- package/src/agent-schemas.generated.ts +5764 -0
- package/src/case-file.ts +45 -109
- package/src/case.ts +814 -1328
- package/src/chain.ts +85 -224
- package/src/claims.ts +1253 -0
- package/src/cli.ts +714 -1176
- package/src/compose.ts +33 -79
- package/src/crypto.ts +60 -147
- package/src/declaration.ts +219 -656
- package/src/environment.ts +17 -65
- package/src/evidence.ts +958 -1381
- package/src/exit-codes.ts +24 -63
- package/src/file-match.ts +40 -89
- package/src/grading.ts +287 -421
- package/src/handoff.ts +175 -427
- package/src/index.ts +131 -177
- package/src/json-schema.ts +295 -0
- package/src/merkle.ts +16 -28
- package/src/message-parts.ts +155 -533
- package/src/side-content.ts +25 -68
- package/src/types.ts +55 -36
- package/src/verifier.ts +202 -526
- package/src/witness-cache.ts +45 -94
- package/src/witness-lookup.ts +107 -177
- package/src/zip-reader.ts +20 -38
- package/vectors/README.md +52 -34
- package/vectors/agent-record.json +2175 -0
- package/vectors/case-file-index.json +1 -1
- package/vectors/case-split.json +2 -2
- package/vectors/chain-reading.json +3 -3
- package/vectors/file-match.json +285 -148
- package/vectors/jcs-reader.json +6 -6
- package/vectors/pack-grading.json +102 -25
- package/vectors/sandbox-evidence.json +573 -581
- package/vectors/stamp-on-chain.json +18 -18
- package/vectors/witness-cache.json +49 -49
- package/dist/agent-proof.d.ts +0 -28
- package/dist/agent-proof.js +0 -226
- package/dist/run-adapters.d.ts +0 -10
- package/dist/run-adapters.js +0 -35
- package/dist/run.d.ts +0 -209
- package/dist/run.js +0 -1405
- package/src/agent-proof.ts +0 -264
- package/src/run-adapters.ts +0 -44
- package/src/run.ts +0 -1598
package/dist/side-content.d.ts
CHANGED
|
@@ -1,36 +1,25 @@
|
|
|
1
1
|
/** A caller-built map of disclosed bytes, in either shape it can take. */
|
|
2
2
|
export type SideContentMap = Readonly<Record<string, Uint8Array | string | undefined>> | ReadonlyMap<string, Uint8Array | string | undefined>;
|
|
3
|
-
/** One event's entry, and
|
|
3
|
+
/** One event's entry, and separately whether an entry existed at all. */
|
|
4
4
|
export interface SideEntry {
|
|
5
|
-
/**
|
|
5
|
+
/** Whether an entry exists for this id. Never inferred from the value. */
|
|
6
6
|
offered: boolean;
|
|
7
|
-
/** What it held, read without
|
|
7
|
+
/** What it held, read without invoking an accessor. */
|
|
8
8
|
value: Uint8Array | string | undefined;
|
|
9
9
|
}
|
|
10
|
-
/** Look up one event's out-of-band disclosure.
|
|
10
|
+
/** Look up one event's out-of-band disclosure. */
|
|
11
11
|
export declare function sideContentEntry(content: SideContentMap | null | undefined, eventId: string): SideEntry;
|
|
12
|
-
/** Whether the map carries
|
|
12
|
+
/** Whether the map carries an entry for this id. */
|
|
13
13
|
export declare function hasSideContent(content: SideContentMap | null | undefined, eventId: string): boolean;
|
|
14
14
|
/**
|
|
15
|
-
* The ids the map
|
|
16
|
-
*
|
|
15
|
+
* The ids the map carries, by the same rule, so a key that is not an entry is
|
|
16
|
+
* never reported as surplus disclosure.
|
|
17
17
|
*/
|
|
18
18
|
export declare function sideContentKeys(content: SideContentMap | null | undefined): string[];
|
|
19
19
|
/**
|
|
20
|
-
* Whether an event's
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* absence — Go's `json.Marshal` on a nil pointer without `omitempty`, Python's
|
|
25
|
-
* `json.dumps(None)`. A bare `!== undefined` marks such an event OFFERED while
|
|
26
|
-
* no bytes are selected (only a `string` decodes), and the kernel then splits
|
|
27
|
-
* against itself: `verifyEvidenceBundle` reports `contentOk: null` and rolls up
|
|
28
|
-
* VERIFIED, while `disclosedContent` reports FAILED — "content was disclosed
|
|
29
|
-
* for this event but is not a decodable lowercase-hex string". One kernel, one
|
|
30
|
-
* bundle, two verdicts, reachable from any conforming producer.
|
|
31
|
-
*
|
|
32
|
-
* Non-nullish and not a string — a number, an object, a hex string that is not
|
|
33
|
-
* hex — remains a real, unusable disclosure and stays a finding, exactly as on
|
|
34
|
-
* the side-input path.
|
|
20
|
+
* Whether an event's in-body `events[].content` is a disclosure: anything
|
|
21
|
+
* non-nullish. `null` is how serializers spell an absent optional field, and
|
|
22
|
+
* treating it as offered would split the bundle verdict from
|
|
23
|
+
* `disclosedContent`.
|
|
35
24
|
*/
|
|
36
25
|
export declare function inBodyContentOffered(content: unknown): boolean;
|
package/dist/side-content.js
CHANGED
|
@@ -1,54 +1,22 @@
|
|
|
1
|
-
//
|
|
1
|
+
// The one answer to "did the holder supply bytes for this event_id?", shared by
|
|
2
|
+
// the verifier and the grader so the two cannot disagree.
|
|
2
3
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// an event's content was offered at all (`evidence.ts`), and the grader,
|
|
6
|
-
// accounting which records a pack disclosed (`grading.ts`). It had two answers.
|
|
7
|
-
// On one and the same content map the grader said PRESENT while the verifier
|
|
8
|
-
// said nothing was offered, so `gradePack` returned `incomplete` with ZERO
|
|
9
|
-
// causes: a surface saying "incomplete" and unable to say why. That is exactly
|
|
10
|
-
// the outcome `grading.ts`'s own comment claims was removed, reappearing
|
|
11
|
-
// because the fix landed on one of the two definitions.
|
|
4
|
+
// An entry exists when the map carries an own, enumerable, data property (or
|
|
5
|
+
// `Map` key) holding something other than `null`/`undefined`:
|
|
12
6
|
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
7
|
+
// own `event_id` is producer-chosen, and `constructor` or `__proto__`
|
|
8
|
+
// must not resolve through the prototype chain.
|
|
9
|
+
// enumerable what `Object.keys` shows is the map the caller built.
|
|
10
|
+
// data an accessor is never invoked: a getter can throw, and a
|
|
11
|
+
// verifier does not raise on adversarial input.
|
|
12
|
+
// non-nullish `undefined` is how `Object.fromEntries` spells "no entry".
|
|
13
|
+
// Anything else, even unusable, is a disclosure and a finding.
|
|
16
14
|
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
// property (or `Map` key) holding something other than `null`/`undefined`.
|
|
21
|
-
// This is the STRICTER of the two definitions that were in the kernel — the
|
|
22
|
-
// verifier's — and it is stricter on purpose. Every clause was paid for:
|
|
23
|
-
//
|
|
24
|
-
// own — an `event_id` is PRODUCER-CHOSEN, and every key on
|
|
25
|
-
// `Object.prototype` is a legal one: `constructor`,
|
|
26
|
-
// `toString`, `valueOf`, `hasOwnProperty`, `isPrototypeOf`,
|
|
27
|
-
// `propertyIsEnumerable`, `toLocaleString`, `__proto__`. An
|
|
28
|
-
// indexed lookup walks the prototype chain and resolves every
|
|
29
|
-
// one of them, so absent evidence read as good evidence.
|
|
30
|
-
// enumerable — what a caller means by "the map I built" is what
|
|
31
|
-
// `Object.keys` shows; nothing hidden behind it counts.
|
|
32
|
-
// data — an accessor is NEVER invoked. A getter can throw, and this
|
|
33
|
-
// kernel's standing rule is that a verifier does not raise on
|
|
34
|
-
// adversarial input. Not hypothetical: a plain-object map that
|
|
35
|
-
// has taken a `__proto__` write carries `Uint8Array.prototype`
|
|
36
|
-
// as its prototype, and reading `byteLength`, `length`,
|
|
37
|
-
// `buffer` or `byteOffset` off a plain object through it throws
|
|
38
|
-
// a TypeError. Reading a descriptor cannot.
|
|
39
|
-
// non-nullish — `{ [id]: undefined }` is how `Object.fromEntries` and friends
|
|
40
|
-
// spell "no entry", and `new Map([[id, undefined]])` is the
|
|
41
|
-
// same statement in the other shape. Anything else non-nullish
|
|
42
|
-
// — a number, an object, a string that is not hex — IS a real,
|
|
43
|
-
// unusable disclosure and stays a finding.
|
|
44
|
-
//
|
|
45
|
-
// `offered` is what separates "nothing was disclosed" (undetermined) from
|
|
46
|
-
// "something was disclosed and is unusable" (a finding), and absent disclosure
|
|
47
|
-
// is undetermined, not failed (evidence-bundle.md §5.1). So it is returned
|
|
48
|
-
// SEPARATELY from the value and is never inferred from it: `value !== undefined`
|
|
49
|
-
// is the conflation this module exists to remove, in both directions.
|
|
15
|
+
// `offered` is returned separately from the value and never inferred from it:
|
|
16
|
+
// nothing disclosed is undetermined, something unusable is a finding
|
|
17
|
+
// (evidence-bundle.md §5.1).
|
|
50
18
|
const ABSENT = { offered: false, value: undefined };
|
|
51
|
-
/** Look up one event's out-of-band disclosure.
|
|
19
|
+
/** Look up one event's out-of-band disclosure. */
|
|
52
20
|
export function sideContentEntry(content, eventId) {
|
|
53
21
|
if (content === undefined || content === null)
|
|
54
22
|
return ABSENT;
|
|
@@ -62,13 +30,13 @@ export function sideContentEntry(content, eventId) {
|
|
|
62
30
|
const value = desc.value;
|
|
63
31
|
return value === undefined || value === null ? ABSENT : { offered: true, value };
|
|
64
32
|
}
|
|
65
|
-
/** Whether the map carries
|
|
33
|
+
/** Whether the map carries an entry for this id. */
|
|
66
34
|
export function hasSideContent(content, eventId) {
|
|
67
35
|
return sideContentEntry(content, eventId).offered;
|
|
68
36
|
}
|
|
69
37
|
/**
|
|
70
|
-
* The ids the map
|
|
71
|
-
*
|
|
38
|
+
* The ids the map carries, by the same rule, so a key that is not an entry is
|
|
39
|
+
* never reported as surplus disclosure.
|
|
72
40
|
*/
|
|
73
41
|
export function sideContentKeys(content) {
|
|
74
42
|
if (content === undefined || content === null)
|
|
@@ -77,21 +45,10 @@ export function sideContentKeys(content) {
|
|
|
77
45
|
return keys.filter((id) => hasSideContent(content, id));
|
|
78
46
|
}
|
|
79
47
|
/**
|
|
80
|
-
* Whether an event's
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* absence — Go's `json.Marshal` on a nil pointer without `omitempty`, Python's
|
|
85
|
-
* `json.dumps(None)`. A bare `!== undefined` marks such an event OFFERED while
|
|
86
|
-
* no bytes are selected (only a `string` decodes), and the kernel then splits
|
|
87
|
-
* against itself: `verifyEvidenceBundle` reports `contentOk: null` and rolls up
|
|
88
|
-
* VERIFIED, while `disclosedContent` reports FAILED — "content was disclosed
|
|
89
|
-
* for this event but is not a decodable lowercase-hex string". One kernel, one
|
|
90
|
-
* bundle, two verdicts, reachable from any conforming producer.
|
|
91
|
-
*
|
|
92
|
-
* Non-nullish and not a string — a number, an object, a hex string that is not
|
|
93
|
-
* hex — remains a real, unusable disclosure and stays a finding, exactly as on
|
|
94
|
-
* the side-input path.
|
|
48
|
+
* Whether an event's in-body `events[].content` is a disclosure: anything
|
|
49
|
+
* non-nullish. `null` is how serializers spell an absent optional field, and
|
|
50
|
+
* treating it as offered would split the bundle verdict from
|
|
51
|
+
* `disclosedContent`.
|
|
95
52
|
*/
|
|
96
53
|
export function inBodyContentOffered(content) {
|
|
97
54
|
return content !== undefined && content !== null;
|
package/dist/types.d.ts
CHANGED
|
@@ -32,4 +32,21 @@ export interface VerificationResult {
|
|
|
32
32
|
contentHashOk: boolean | null;
|
|
33
33
|
contentRole: ContentRole | null;
|
|
34
34
|
errors: string[];
|
|
35
|
+
/**
|
|
36
|
+
* Why `ok` is false: the worst of `faults`, or `null` where `ok` is true or only the
|
|
37
|
+
* content bind failed. A caller maps each fault to a claim (`ENVELOPE_FAULT_KINDS`) rather
|
|
38
|
+
* than reading `ok` alone: a signature or a binding that fails disproves, a key or an
|
|
39
|
+
* envelope that cannot be read and a version this verifier does not know leave a claim not
|
|
40
|
+
* proven, and a field that breaks the format under a signature that holds is the
|
|
41
|
+
* producer's error.
|
|
42
|
+
*/
|
|
43
|
+
fault: EnvelopeFault | null;
|
|
44
|
+
/**
|
|
45
|
+
* Every fault found, in `ENVELOPE_FAULTS` order. A fault that leaves one claim not proven
|
|
46
|
+
* never hides a disproof found beside it.
|
|
47
|
+
*/
|
|
48
|
+
faults: EnvelopeFault[];
|
|
35
49
|
}
|
|
50
|
+
/** Why an envelope does not verify, worst first: the disproofs, then what is not proven. */
|
|
51
|
+
export declare const ENVELOPE_FAULTS: readonly ["signature_failed", "payload_unbound", "nonconforming", "not_an_envelope", "key_unreadable", "spec_version_unknown"];
|
|
52
|
+
export type EnvelopeFault = (typeof ENVELOPE_FAULTS)[number];
|
package/dist/types.js
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
|
-
// Wire types for Sanning Verifiable Event Envelopes (
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
export
|
|
1
|
+
// Wire types for Sanning Verifiable Event Envelopes (specs/envelope-spec.md).
|
|
2
|
+
// Only the fields the verifier consumes are modelled; envelopes are verified
|
|
3
|
+
// over their canonical bytes, not this interface.
|
|
4
|
+
/** Why an envelope does not verify, worst first: the disproofs, then what is not proven. */
|
|
5
|
+
export const ENVELOPE_FAULTS = [
|
|
6
|
+
// The Ed25519 signature does not hold.
|
|
7
|
+
"signature_failed",
|
|
8
|
+
// `payload_hash` is 64 hex characters that do not match the bytes it commits to.
|
|
9
|
+
"payload_unbound",
|
|
10
|
+
// The signature holds over a version this verifier knows, and a field breaks the
|
|
11
|
+
// envelope format (a missing `payload_hash` included).
|
|
12
|
+
"nonconforming",
|
|
13
|
+
// Not an object, or an object with none of the envelope's own members.
|
|
14
|
+
"not_an_envelope",
|
|
15
|
+
// `public_key` is not 64 hex characters: no signature can be checked.
|
|
16
|
+
"key_unreadable",
|
|
17
|
+
// A `spec_version` this verifier does not know.
|
|
18
|
+
"spec_version_unknown",
|
|
19
|
+
];
|
package/dist/verifier.d.ts
CHANGED
|
@@ -1,144 +1,54 @@
|
|
|
1
|
-
import type { ContentRole, Envelope, VerificationResult, VerifyOptions } from "./types.js";
|
|
1
|
+
import type { ContentRole, Envelope, EnvelopeFault, VerificationResult, VerifyOptions } from "./types.js";
|
|
2
2
|
export declare const ACCEPTED_SPEC_MAJORS: readonly string[];
|
|
3
3
|
export declare const MAX_CANONICAL_DEPTH = 128;
|
|
4
4
|
export declare function exceedsDepth(value: unknown, max: number): boolean;
|
|
5
5
|
/**
|
|
6
|
-
* The bytes an envelope's inline `payload` commits to: `JCS(payload)`. The
|
|
7
|
-
* place this is
|
|
8
|
-
* bytes
|
|
9
|
-
* record through them, so the binding rule and the linkage rule cannot
|
|
10
|
-
* disagree about which bytes a record is. Throws where the payload cannot be
|
|
11
|
-
* canonicalized. The twin is Python's `inline_payload_bytes`.
|
|
6
|
+
* The bytes an envelope's inline `payload` commits to: `JCS(payload)`. The one
|
|
7
|
+
* place this is defined, so payload binding and chain linkage agree on a
|
|
8
|
+
* record's bytes. Throws where the payload cannot be canonicalized.
|
|
12
9
|
*/
|
|
13
10
|
export declare function inlinePayloadBytes(envelope: {
|
|
14
11
|
payload: unknown;
|
|
15
12
|
}): Uint8Array;
|
|
16
13
|
export declare function jcs(value: unknown): string;
|
|
17
|
-
/**
|
|
18
|
-
* Read bytes that claim to be `jcs(value)` (RFC 8785), strictly. The Python
|
|
19
|
-
* twin is `jcs_loads` in `src/sanning_proof/canonicalize.py`, and every site in
|
|
20
|
-
* either kernel that reads bytes a producer signed goes through one of the two.
|
|
21
|
-
*
|
|
22
|
-
* ONE RULE, SPELLED OUT, BECAUSE TWO BUILT-INS ARE NOT ONE RULE. Five checks
|
|
23
|
-
* carry it, none of them left to a decoder's defaults: strict UTF-8, no leading
|
|
24
|
-
* byte-order mark, no non-finite literal (this kernel's `JSON.parse` rejects
|
|
25
|
-
* `NaN`, `Infinity` and `-Infinity` natively, and Python names them in a
|
|
26
|
-
* `parse_constant` hook), no object that repeats a member name
|
|
27
|
-
* (`DuplicateKeyError`, proof#144), and no unpaired surrogate in the parsed
|
|
28
|
-
* value.
|
|
29
|
-
*
|
|
30
|
-
* THE FOURTH CHECK IS NOT THE FIRST ONE TWICE. The strict decode refuses a
|
|
31
|
-
* surrogate written as raw bytes; it cannot see one written as the ASCII
|
|
32
|
-
* escape `\ud800`, which `JSON.parse` turns into a lone code unit AFTER the
|
|
33
|
-
* decode has passed. `"c\ud800x"` is pure ASCII on the wire, so this reader
|
|
34
|
-
* accepted it while its own text above said it does not, and one poisoned
|
|
35
|
-
* record then deleted the Python kernel's whole chain reading (the gate on
|
|
36
|
-
* proof#154). Both spellings are the same malformed record, and both are
|
|
37
|
-
* refused here.
|
|
38
|
-
*
|
|
39
|
-
* `new TextDecoder()` alone is none of that. It replaces every invalid sequence
|
|
40
|
-
* with U+FFFD and strips a BOM. Python's `json.loads(<bytes>)` is not it
|
|
41
|
-
* either, in the opposite direction: CPython sniffs the encoding and decodes
|
|
42
|
-
* with `errors="surrogatepass"`, so it strips a UTF-8 BOM, accepts a UTF-16 or
|
|
43
|
-
* UTF-32 body, and passes a lone surrogate and a CESU-8 pair through. Both
|
|
44
|
-
* defaults have shipped a split verdict on one pack: a raw `0x80` inside a
|
|
45
|
-
* stamp body read `verified` here and `failed` there (gate 4), and then a
|
|
46
|
-
* `BOM + JCS(record)` checkpoint read `verified` there and `mixed` here (gate
|
|
47
|
-
* 5), both on proof#140. JCS is defined over UTF-8 and I-JSON (RFC 7493)
|
|
48
|
-
* forbids a lone surrogate, so each of those inputs is MALFORMED rather than
|
|
49
|
-
* another spelling of the same record.
|
|
50
|
-
*
|
|
51
|
-
* Throws for anything that is not conformant; every call site treats a throw as
|
|
52
|
-
* "this reader cannot read these bytes" and names the check that failed.
|
|
53
|
-
*/
|
|
54
|
-
/**
|
|
55
|
-
* Bytes as strict UTF-8, a leading byte-order mark KEPT as a character.
|
|
56
|
-
* `utf8_strict` in the Python twin's `canonicalize.py` is the same rule, and
|
|
57
|
-
* `jcsParse` below performs the identical decode. Throws on bytes that are not
|
|
58
|
-
* UTF-8.
|
|
59
|
-
*/
|
|
14
|
+
/** Bytes as strict UTF-8, a leading byte-order mark kept as a character. Throws on invalid UTF-8. */
|
|
60
15
|
export declare function utf8Strict(bytes: Uint8Array): string;
|
|
61
|
-
export declare function jcsParse(bytes: Uint8Array): unknown;
|
|
62
16
|
/**
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* `exceeds_text_depth` in `canonicalize.py`.
|
|
17
|
+
* Read bytes that claim to be `jcs(value)`, strictly: valid UTF-8, no leading
|
|
18
|
+
* BOM, no non-finite literal, no repeated member name, and no unpaired
|
|
19
|
+
* surrogate (including one written as a `\ud800` escape). Every site that reads
|
|
20
|
+
* bytes a producer signed goes through this or its Python twin `jcs_loads`, so
|
|
21
|
+
* the kernels cannot split on decoder defaults.
|
|
69
22
|
*
|
|
70
|
-
*
|
|
71
|
-
* stops near 1,000 levels, `JSON.parse` and CPython 3.13 read far deeper, Go's
|
|
72
|
-
* decoder stops at 10,000 (enclave-attestation section 2.1).
|
|
73
|
-
*/
|
|
74
|
-
export declare function exceedsTextDepth(bytes: Uint8Array, max: number): boolean;
|
|
75
|
-
/**
|
|
76
|
-
* Whether a parsed value holds a number no IEEE 754 double holds, at any
|
|
77
|
-
* depth. `JSON.parse` reads `1e400`, and an integer of 400 digits, as
|
|
78
|
-
* `Infinity`. Iterative: the value is a producer's. The Python twin is
|
|
79
|
-
* `has_non_finite_number`.
|
|
23
|
+
* @throws When the bytes are not conformant.
|
|
80
24
|
*/
|
|
81
|
-
export declare function
|
|
82
|
-
/**
|
|
83
|
-
* The deepest a JSON text an enclave reader parses may nest, and the depth
|
|
84
|
-
* every kernel canonicalizes to (`MAX_CANONICAL_DEPTH`).
|
|
85
|
-
*/
|
|
86
|
-
export declare const MAX_READ_DEPTH = 128;
|
|
87
|
-
/**
|
|
88
|
-
* `jcsParse`, within the reader limits of enclave-attestation section 2.1:
|
|
89
|
-
* the bytes nest at most `MAX_READ_DEPTH` levels, checked on the bytes first,
|
|
90
|
-
* and every number in the value is a finite double. Throws past either limit,
|
|
91
|
-
* and wherever `jcsParse` throws. The Python twin is `jcs_loads_within_limits`.
|
|
92
|
-
*/
|
|
93
|
-
export declare function jcsParseWithinLimits(bytes: Uint8Array): unknown;
|
|
25
|
+
export declare function jcsParse(bytes: Uint8Array): unknown;
|
|
94
26
|
/**
|
|
95
|
-
* A JSON object that names one member twice
|
|
96
|
-
*
|
|
97
|
-
* the same kernel wording in both: `duplicate key ` and the name as
|
|
98
|
-
* `renderKey` spells it.
|
|
99
|
-
*
|
|
100
|
-
* JCS (RFC 8785) is defined over I-JSON, and I-JSON forbids duplicate member
|
|
101
|
-
* names, so bytes that repeat a name are MALFORMED rather than another spelling
|
|
102
|
-
* of the last value. `JSON.parse` and Python's `json.loads` both keep the last
|
|
103
|
-
* value and say nothing, which let a bundle carrying `"spec_version":"junk"`
|
|
104
|
-
* before its real `spec_version` read `verified` in both kernels (proof#144).
|
|
27
|
+
* A JSON object that names one member twice. I-JSON (RFC 7493 §2.3) forbids it,
|
|
28
|
+
* so the bytes are malformed rather than a spelling of the last value.
|
|
105
29
|
*/
|
|
106
30
|
export declare class DuplicateKeyError extends Error {
|
|
107
31
|
readonly key: string;
|
|
108
32
|
constructor(key: string);
|
|
109
33
|
}
|
|
110
34
|
/**
|
|
111
|
-
* A member name as a quoted, pure-ASCII string,
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
* `\uXXXX` in lower-case hex. A rendering longer than 64 characters is cut
|
|
115
|
-
* there and `...` follows the closing quote.
|
|
35
|
+
* A member name as a quoted, pure-ASCII string, identical in both kernels.
|
|
36
|
+
* Printable ASCII is kept (`"` and `\` escaped), every other code unit is
|
|
37
|
+
* `\uXXXX`, and anything past 64 characters is cut with `...`.
|
|
116
38
|
*/
|
|
117
39
|
export declare function renderKey(key: string): string;
|
|
118
40
|
/**
|
|
119
|
-
* `JSON.parse(text)`, refusing an object that repeats a member name.
|
|
120
|
-
*
|
|
41
|
+
* `JSON.parse(text)`, refusing an object that repeats a member name. Parses
|
|
42
|
+
* first, so non-JSON fails as JSON, then rescans for repeats.
|
|
121
43
|
*
|
|
122
|
-
*
|
|
123
|
-
* text is scanned once more AFTER it parsed. The parse runs first, so text
|
|
124
|
-
* that is not JSON at all fails as JSON and never as a duplicate, as it does in
|
|
125
|
-
* the Python twin. Throws `DuplicateKeyError`.
|
|
44
|
+
* @throws DuplicateKeyError
|
|
126
45
|
*/
|
|
127
46
|
export declare function parseRefusingDuplicates(text: string): unknown;
|
|
128
47
|
/**
|
|
129
|
-
* The repeated member name to report
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
* as they CLOSE, innermost first, which is the order Python's
|
|
134
|
-
* `object_pairs_hook` sees them in; inside one object the name reported is the
|
|
135
|
-
* first whose repeat comes earliest. Names compare after unescaping, so `"a"`
|
|
136
|
-
* and `"\u0061"` are one name, as they are to both parsers.
|
|
137
|
-
*
|
|
138
|
-
* ITERATIVE, with an explicit stack, for the reason `rejectUnpairedSurrogates`
|
|
139
|
-
* is: this walks bytes a producer wrote. It relies on the text being valid
|
|
140
|
-
* JSON, which is why it runs only after `JSON.parse` succeeded: outside a
|
|
141
|
-
* string, only `{`, `[`, `}`, `]`, `,` and `"` change what comes next.
|
|
48
|
+
* The repeated member name to report, or `undefined`. Objects are checked as
|
|
49
|
+
* they close, innermost first, matching Python's `object_pairs_hook`, so both
|
|
50
|
+
* kernels name the same key. Iterative, because the text is producer-written;
|
|
51
|
+
* runs only on text `JSON.parse` accepted.
|
|
142
52
|
*/
|
|
143
53
|
export declare function firstDuplicateKey(text: string): string | undefined;
|
|
144
54
|
export declare function specVersionSupported(specVersion: string, accepted?: readonly string[]): boolean;
|
|
@@ -148,3 +58,19 @@ export declare function contentHashes(env: Envelope): {
|
|
|
148
58
|
hash: string;
|
|
149
59
|
}[];
|
|
150
60
|
export declare function verifyEnvelope(env: Envelope, options?: VerifyOptions): Promise<VerificationResult>;
|
|
61
|
+
type FaultChecks = {
|
|
62
|
+
signatureOk: boolean;
|
|
63
|
+
payloadHashOk: boolean | null;
|
|
64
|
+
specVersionOk: boolean;
|
|
65
|
+
fieldsOk: boolean;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Every one of `ENVELOPE_FAULTS` that applies, in that order. Each is judged without the
|
|
69
|
+
* others: a key that cannot be read checks no signature, and still the bytes are checked
|
|
70
|
+
* against `payload_hash`. A field's form is judged only under a signature that holds over a
|
|
71
|
+
* version this verifier knows.
|
|
72
|
+
*/
|
|
73
|
+
export declare function envelopeFaults(env: unknown, checks: FaultChecks): EnvelopeFault[];
|
|
74
|
+
/** The worst of `envelopeFaults`, or `null`. */
|
|
75
|
+
export declare function envelopeFault(env: unknown, checks: FaultChecks): EnvelopeFault | null;
|
|
76
|
+
export {};
|