@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/src/side-content.ts
CHANGED
|
@@ -1,69 +1,37 @@
|
|
|
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
|
|
|
51
19
|
/** A caller-built map of disclosed bytes, in either shape it can take. */
|
|
52
20
|
export type SideContentMap =
|
|
53
21
|
| Readonly<Record<string, Uint8Array | string | undefined>>
|
|
54
22
|
| ReadonlyMap<string, Uint8Array | string | undefined>;
|
|
55
23
|
|
|
56
|
-
/** One event's entry, and
|
|
24
|
+
/** One event's entry, and separately whether an entry existed at all. */
|
|
57
25
|
export interface SideEntry {
|
|
58
|
-
/**
|
|
26
|
+
/** Whether an entry exists for this id. Never inferred from the value. */
|
|
59
27
|
offered: boolean;
|
|
60
|
-
/** What it held, read without
|
|
28
|
+
/** What it held, read without invoking an accessor. */
|
|
61
29
|
value: Uint8Array | string | undefined;
|
|
62
30
|
}
|
|
63
31
|
|
|
64
32
|
const ABSENT: SideEntry = { offered: false, value: undefined };
|
|
65
33
|
|
|
66
|
-
/** Look up one event's out-of-band disclosure.
|
|
34
|
+
/** Look up one event's out-of-band disclosure. */
|
|
67
35
|
export function sideContentEntry(
|
|
68
36
|
content: SideContentMap | null | undefined,
|
|
69
37
|
eventId: string,
|
|
@@ -79,7 +47,7 @@ export function sideContentEntry(
|
|
|
79
47
|
return value === undefined || value === null ? ABSENT : { offered: true, value };
|
|
80
48
|
}
|
|
81
49
|
|
|
82
|
-
/** Whether the map carries
|
|
50
|
+
/** Whether the map carries an entry for this id. */
|
|
83
51
|
export function hasSideContent(
|
|
84
52
|
content: SideContentMap | null | undefined,
|
|
85
53
|
eventId: string,
|
|
@@ -88,8 +56,8 @@ export function hasSideContent(
|
|
|
88
56
|
}
|
|
89
57
|
|
|
90
58
|
/**
|
|
91
|
-
* The ids the map
|
|
92
|
-
*
|
|
59
|
+
* The ids the map carries, by the same rule, so a key that is not an entry is
|
|
60
|
+
* never reported as surplus disclosure.
|
|
93
61
|
*/
|
|
94
62
|
export function sideContentKeys(content: SideContentMap | null | undefined): string[] {
|
|
95
63
|
if (content === undefined || content === null) return [];
|
|
@@ -98,21 +66,10 @@ export function sideContentKeys(content: SideContentMap | null | undefined): str
|
|
|
98
66
|
}
|
|
99
67
|
|
|
100
68
|
/**
|
|
101
|
-
* Whether an event's
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
* absence — Go's `json.Marshal` on a nil pointer without `omitempty`, Python's
|
|
106
|
-
* `json.dumps(None)`. A bare `!== undefined` marks such an event OFFERED while
|
|
107
|
-
* no bytes are selected (only a `string` decodes), and the kernel then splits
|
|
108
|
-
* against itself: `verifyEvidenceBundle` reports `contentOk: null` and rolls up
|
|
109
|
-
* VERIFIED, while `disclosedContent` reports FAILED — "content was disclosed
|
|
110
|
-
* for this event but is not a decodable lowercase-hex string". One kernel, one
|
|
111
|
-
* bundle, two verdicts, reachable from any conforming producer.
|
|
112
|
-
*
|
|
113
|
-
* Non-nullish and not a string — a number, an object, a hex string that is not
|
|
114
|
-
* hex — remains a real, unusable disclosure and stays a finding, exactly as on
|
|
115
|
-
* the side-input path.
|
|
69
|
+
* Whether an event's in-body `events[].content` is a disclosure: anything
|
|
70
|
+
* non-nullish. `null` is how serializers spell an absent optional field, and
|
|
71
|
+
* treating it as offered would split the bundle verdict from
|
|
72
|
+
* `disclosedContent`.
|
|
116
73
|
*/
|
|
117
74
|
export function inBodyContentOffered(content: unknown): boolean {
|
|
118
75
|
return content !== undefined && content !== null;
|
package/src/types.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
// Wire types for Sanning Verifiable Event Envelopes (
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// over their canonical bytes, not this interface).
|
|
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.
|
|
5
4
|
|
|
6
5
|
export interface Subject {
|
|
7
6
|
type: string;
|
|
@@ -16,62 +15,82 @@ export interface Envelope {
|
|
|
16
15
|
signed_at: string;
|
|
17
16
|
public_key: string;
|
|
18
17
|
signature: string;
|
|
19
|
-
// Inline-binding profiles (envelope-spec §3) carry the committed payload
|
|
20
|
-
//
|
|
21
|
-
// the committed bytes to bind (verifyEnvelope's payloadBytes option).
|
|
18
|
+
// Inline-binding profiles (envelope-spec §3) carry the committed payload;
|
|
19
|
+
// external-commitment profiles do not, and the caller supplies the bytes.
|
|
22
20
|
payload?: Record<string, unknown>;
|
|
23
|
-
// Disclosure fields (§3.1):
|
|
24
|
-
// ones (they live inside the committed payload instead).
|
|
21
|
+
// Disclosure fields (§3.1): on Promoted profiles, inside the payload on Minimal ones.
|
|
25
22
|
event_type?: string;
|
|
26
23
|
subject?: Subject;
|
|
27
24
|
previous_hash?: string;
|
|
28
|
-
// Optional
|
|
29
|
-
// and the external-commitment locator.
|
|
25
|
+
// Optional: dev/production marking inside the signed scope, and the external-commitment locator.
|
|
30
26
|
environment?: string;
|
|
31
27
|
payload_ref?: string;
|
|
32
|
-
// Reserved
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
// can be added without invalidating the primary. Absence implies a single
|
|
36
|
-
// signer and MUST NOT be treated as a failure.
|
|
28
|
+
// Reserved (envelope-spec §7.1): countersignatures, outside the signed scope.
|
|
29
|
+
// The primary signature covers the envelope minus `signature` and
|
|
30
|
+
// `co_signatures`. Absence implies a single signer and MUST NOT be a failure.
|
|
37
31
|
co_signatures?: unknown[];
|
|
38
32
|
}
|
|
39
33
|
|
|
40
34
|
// Which payload field a matched content hash came from. tamper_detected commits
|
|
41
|
-
// to both the tampered ("observed")
|
|
35
|
+
// to both the tampered ("observed") and known-good ("baseline") bytes.
|
|
42
36
|
export type ContentRole = "asset" | "baseline" | "observed";
|
|
43
37
|
|
|
44
38
|
export interface VerifyOptions {
|
|
45
|
-
// External commitment (§3): the committed canonical payload bytes.
|
|
46
|
-
// supplied, SHA-256(payloadBytes) must equal the envelope's payload_hash.
|
|
39
|
+
// External commitment (§3): the committed canonical payload bytes. SHA-256 of them must equal `payload_hash`.
|
|
47
40
|
payloadBytes?: Uint8Array;
|
|
48
|
-
// Content bind: the caller's own hash of an artifact
|
|
49
|
-
// hash of a user's file) to match against the hashes the envelope commits to.
|
|
41
|
+
// Content bind: the caller's own hash of an artifact, matched against the hashes the envelope commits to.
|
|
50
42
|
expectedContentHash?: string;
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
// admit a profile this kernel does not. The Python twin is
|
|
54
|
-
// `accepted_spec_majors` (P-10, 0.13.0).
|
|
43
|
+
// Narrows the accept-set for this call, never widens it: `ACCEPTED_SPEC_MAJORS`
|
|
44
|
+
// intersected with this list.
|
|
55
45
|
acceptedSpecMajors?: readonly string[];
|
|
56
46
|
}
|
|
57
47
|
|
|
58
48
|
export interface VerificationResult {
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
// not fail ok — that is the "signature-valid, semantics-undetermined"
|
|
63
|
-
// outcome of envelope-spec §3.1/§6.2.
|
|
49
|
+
// Spec_version ok, Ed25519 signature ok, and the payload binding did not fail.
|
|
50
|
+
// `payloadHashOk === null` (no binding material) does not fail `ok`: the
|
|
51
|
+
// "signature-valid, semantics-undetermined" outcome of envelope-spec §3.1, §6 item 2.
|
|
64
52
|
ok: boolean;
|
|
65
53
|
specVersionOk: boolean;
|
|
66
|
-
// true: binding checked and matches
|
|
67
|
-
//
|
|
68
|
-
// missing/malformed. null: no binding material to check against.
|
|
54
|
+
// true: binding checked and matches. false: a check failed, or payload_hash is
|
|
55
|
+
// missing or malformed. null: no binding material to check against.
|
|
69
56
|
payloadHashOk: boolean | null;
|
|
70
57
|
signatureOk: boolean;
|
|
71
|
-
// Content bind:
|
|
72
|
-
//
|
|
73
|
-
//
|
|
58
|
+
// Content bind: whether the supplied hash matches a hash the envelope commits
|
|
59
|
+
// to; null when none was supplied. This defeats a lying gateway: the tag only
|
|
60
|
+
// gets to the candidate.
|
|
74
61
|
contentHashOk: boolean | null;
|
|
75
62
|
contentRole: ContentRole | null;
|
|
76
63
|
errors: string[];
|
|
64
|
+
/**
|
|
65
|
+
* Why `ok` is false: the worst of `faults`, or `null` where `ok` is true or only the
|
|
66
|
+
* content bind failed. A caller maps each fault to a claim (`ENVELOPE_FAULT_KINDS`) rather
|
|
67
|
+
* than reading `ok` alone: a signature or a binding that fails disproves, a key or an
|
|
68
|
+
* envelope that cannot be read and a version this verifier does not know leave a claim not
|
|
69
|
+
* proven, and a field that breaks the format under a signature that holds is the
|
|
70
|
+
* producer's error.
|
|
71
|
+
*/
|
|
72
|
+
fault: EnvelopeFault | null;
|
|
73
|
+
/**
|
|
74
|
+
* Every fault found, in `ENVELOPE_FAULTS` order. A fault that leaves one claim not proven
|
|
75
|
+
* never hides a disproof found beside it.
|
|
76
|
+
*/
|
|
77
|
+
faults: EnvelopeFault[];
|
|
77
78
|
}
|
|
79
|
+
|
|
80
|
+
/** Why an envelope does not verify, worst first: the disproofs, then what is not proven. */
|
|
81
|
+
export const ENVELOPE_FAULTS = [
|
|
82
|
+
// The Ed25519 signature does not hold.
|
|
83
|
+
"signature_failed",
|
|
84
|
+
// `payload_hash` is 64 hex characters that do not match the bytes it commits to.
|
|
85
|
+
"payload_unbound",
|
|
86
|
+
// The signature holds over a version this verifier knows, and a field breaks the
|
|
87
|
+
// envelope format (a missing `payload_hash` included).
|
|
88
|
+
"nonconforming",
|
|
89
|
+
// Not an object, or an object with none of the envelope's own members.
|
|
90
|
+
"not_an_envelope",
|
|
91
|
+
// `public_key` is not 64 hex characters: no signature can be checked.
|
|
92
|
+
"key_unreadable",
|
|
93
|
+
// A `spec_version` this verifier does not know.
|
|
94
|
+
"spec_version_unknown",
|
|
95
|
+
] as const;
|
|
96
|
+
export type EnvelopeFault = (typeof ENVELOPE_FAULTS)[number];
|