@motebit/crypto 1.2.0 → 1.2.1

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.
@@ -1,237 +0,0 @@
1
- /**
2
- * Witness-omission dispute — sign + verify (retention phase 4b-3).
3
- *
4
- * Permissive floor (Apache-2.0). Zero monorepo deps beyond
5
- * `@motebit/protocol` types. Routes signing through
6
- * `@motebit/crypto/suite-dispatch`.
7
- *
8
- * Path A quorum's soft-accountability layer for `append_only_horizon`
9
- * certs: a peer who believes `cert.witnessed_by[]` wrongly omits them
10
- * files this dispute within 24h of `cert.issued_at`. Two evidence
11
- * shapes:
12
- *
13
- * - `inclusion_proof` — disputant proves their peer pubkey is in
14
- * the cert's `federation_graph_anchor.merkle_root` via a Merkle
15
- * inclusion proof. The verifier reconstructs against the cert's
16
- * anchor root.
17
- *
18
- * - `alternative_peering` — disputant supplies a signed peering
19
- * artifact from the cert issuer (today: a federation Heartbeat,
20
- * `motebit-concat-ed25519-hex-v1`) whose timestamp window covers
21
- * `cert.horizon_ts ± 5 min` (mirrors heartbeat suspension
22
- * threshold). The cert's published anchor is claimed incomplete.
23
- *
24
- * The verifier returns a per-step result; certificates remain
25
- * TERMINAL per `retention-policy.md` decision 5 — a sustained dispute
26
- * is a reputation hit on the issuer, not a cert invalidation.
27
- */
28
- import { canonicalJson, fromBase64Url, hexToBytes, toBase64Url } from "./signing.js";
29
- import { signBySuite, verifyBySuite } from "./suite-dispatch.js";
30
- import { verifyMerkleInclusion } from "./merkle.js";
31
- import { WITNESS_OMISSION_DISPUTE_WINDOW_MS } from "./deletion-certificate.js";
32
- // ── Constants ────────────────────────────────────────────────────────
33
- const WITNESS_OMISSION_DISPUTE_SUITE = "motebit-jcs-ed25519-b64-v1";
34
- const FEDERATION_HEARTBEAT_SUITE = "motebit-concat-ed25519-hex-v1";
35
- /**
36
- * Heartbeat-as-peering-evidence freshness window. A heartbeat whose
37
- * `timestamp` is within ±5 min of `cert.horizon_ts` proves the issuer
38
- * was alive on that peering relationship at the horizon — mirrors
39
- * `HEARTBEAT_REMOVE_THRESHOLD = 5` × `heartbeat_interval = 60s` in
40
- * `services/relay/src/federation.ts`.
41
- */
42
- const HEARTBEAT_FRESHNESS_WINDOW_MS = 5 * 60 * 1000;
43
- // ── Canonicalization ─────────────────────────────────────────────────
44
- /**
45
- * Compute the canonical signing bytes for a `WitnessOmissionDispute`.
46
- * Strips `signature` so the disputant signs every other field —
47
- * including `cert_signature`, `cert_issuer`, and the evidence body.
48
- */
49
- export function canonicalizeWitnessOmissionDispute(dispute) {
50
- const { signature, ...body } = dispute;
51
- void signature;
52
- return new TextEncoder().encode(canonicalJson(body));
53
- }
54
- // ── Signing ──────────────────────────────────────────────────────────
55
- /**
56
- * Sign a `WitnessOmissionDispute` as the disputant. Caller provides
57
- * the unsigned body (everything except `suite` and `signature`); this
58
- * function appends both.
59
- */
60
- export async function signWitnessOmissionDispute(body, privateKey) {
61
- const withSuite = {
62
- ...body,
63
- suite: WITNESS_OMISSION_DISPUTE_SUITE,
64
- signature: "",
65
- };
66
- const bytes = canonicalizeWitnessOmissionDispute(withSuite);
67
- const sig = await signBySuite(WITNESS_OMISSION_DISPUTE_SUITE, bytes, privateKey);
68
- return { ...withSuite, signature: toBase64Url(sig) };
69
- }
70
- // ── Verifier ─────────────────────────────────────────────────────────
71
- /**
72
- * Verify a `WitnessOmissionDispute` against the disputed cert.
73
- *
74
- * Step ladder (fail-closed throughout):
75
- * 1. Window: `now - cert.issued_at <= WINDOW` AND
76
- * `filed_at ∈ [cert.issued_at, cert.issued_at + WINDOW]`.
77
- * 2. Cert binding: `dispute.cert_signature === cert.signature` and
78
- * `dispute.cert_issuer` matches the cert's subject.
79
- * 3. Disputant signature over `canonicalJson(dispute minus signature)`.
80
- * 4. Evidence: dispatched by `evidence.kind` —
81
- * - `inclusion_proof`: requires cert has a `federation_graph_anchor`;
82
- * verifies the Merkle proof against `anchor.merkle_root`.
83
- * - `alternative_peering`: dispatches on `peering_artifact`'s
84
- * self-described shape (today: federation Heartbeat); verifies
85
- * the embedded signature against the issuer pubkey and the
86
- * timestamp window covers `cert.horizon_ts`.
87
- */
88
- export async function verifyWitnessOmissionDispute(dispute, ctx) {
89
- const errors = [];
90
- const { cert } = ctx;
91
- // ── Step 1: window (two gates) ────────────────────────────────────
92
- const windowEnd = cert.issued_at + WITNESS_OMISSION_DISPUTE_WINDOW_MS;
93
- const wallClockOpen = ctx.now <= windowEnd;
94
- const filedAtInRange = dispute.filed_at >= cert.issued_at && dispute.filed_at <= windowEnd;
95
- const windowOpen = wallClockOpen && filedAtInRange;
96
- if (!wallClockOpen) {
97
- errors.push(`dispute window expired: now (${ctx.now}) > cert.issued_at + ${WITNESS_OMISSION_DISPUTE_WINDOW_MS}ms`);
98
- }
99
- if (!filedAtInRange) {
100
- errors.push("dispute.filed_at outside [cert.issued_at, cert.issued_at + WINDOW] — disputant-attested clock cannot widen window");
101
- }
102
- // ── Step 2: cert binding ──────────────────────────────────────────
103
- let certBindingValid = true;
104
- if (dispute.cert_signature !== cert.signature) {
105
- errors.push("dispute.cert_signature does not match cert.signature");
106
- certBindingValid = false;
107
- }
108
- const certSubjectId = cert.subject.kind === "motebit"
109
- ? cert.subject.motebit_id
110
- : cert.subject.operator_id;
111
- if (dispute.cert_issuer !== certSubjectId) {
112
- errors.push(`dispute.cert_issuer (${dispute.cert_issuer}) does not match cert subject (${certSubjectId})`);
113
- certBindingValid = false;
114
- }
115
- // ── Step 3: disputant signature ───────────────────────────────────
116
- let disputantSignatureValid = false;
117
- if (ctx.disputantPublicKey === null) {
118
- errors.push("disputant public key not resolvable");
119
- }
120
- else if (dispute.suite !== WITNESS_OMISSION_DISPUTE_SUITE) {
121
- errors.push(`unexpected suite: ${String(dispute.suite)}`);
122
- }
123
- else {
124
- const bytes = canonicalizeWitnessOmissionDispute(dispute);
125
- let sigBytes;
126
- try {
127
- sigBytes = fromBase64Url(dispute.signature);
128
- }
129
- catch {
130
- sigBytes = new Uint8Array(0);
131
- }
132
- if (sigBytes.length === 0) {
133
- errors.push("dispute.signature decode failed");
134
- }
135
- else {
136
- disputantSignatureValid = await verifyBySuite(dispute.suite, bytes, sigBytes, ctx.disputantPublicKey);
137
- if (!disputantSignatureValid)
138
- errors.push("dispute.signature does not verify");
139
- }
140
- }
141
- // Short-circuit evidence verification when prior gates already failed.
142
- // The dispute is invalid; the evidence-step result is meaningless.
143
- if (!windowOpen || !certBindingValid || !disputantSignatureValid) {
144
- return {
145
- valid: false,
146
- errors,
147
- steps: {
148
- window_open: windowOpen,
149
- cert_binding_valid: certBindingValid,
150
- disputant_signature_valid: disputantSignatureValid,
151
- evidence_valid: null,
152
- },
153
- };
154
- }
155
- // ── Step 4: evidence dispatch ─────────────────────────────────────
156
- const evidenceValid = await verifyEvidence(dispute.evidence, cert, ctx, errors);
157
- return {
158
- valid: errors.length === 0,
159
- errors,
160
- steps: {
161
- window_open: windowOpen,
162
- cert_binding_valid: certBindingValid,
163
- disputant_signature_valid: disputantSignatureValid,
164
- evidence_valid: evidenceValid,
165
- },
166
- };
167
- }
168
- async function verifyEvidence(evidence, cert, ctx, errors) {
169
- if (evidence.kind === "inclusion_proof") {
170
- const anchor = cert.federation_graph_anchor;
171
- if (anchor === undefined) {
172
- errors.push("inclusion_proof evidence requires cert.federation_graph_anchor — none present");
173
- return false;
174
- }
175
- if (anchor.leaf_count === 0) {
176
- errors.push("inclusion_proof evidence rejected: cert is self-witnessed (anchor.leaf_count=0)");
177
- return false;
178
- }
179
- const ok = await verifyMerkleInclusion(evidence.leaf_hash, evidence.proof.leaf_index, evidence.proof.siblings, evidence.proof.layer_sizes, anchor.merkle_root);
180
- if (!ok)
181
- errors.push("inclusion proof does not reconstruct to anchor.merkle_root");
182
- return ok;
183
- }
184
- // alternative_peering — closed union; TS narrows after the if-branch.
185
- return verifyAlternativePeeringArtifact(evidence.peering_artifact, cert, ctx, errors);
186
- }
187
- /**
188
- * Verify a peering artifact embedded in `alternative_peering` evidence.
189
- * Today: federation Heartbeat is the canonical shape — the only
190
- * recurring signed peering attestation in `relay-federation-v1`.
191
- *
192
- * Heartbeat signing payload (FEDERATION_SUITE = motebit-concat-ed25519-hex-v1):
193
- * `${relay_id}|${timestamp}|${suite}` — UTF-8 concatenation, hex sig.
194
- *
195
- * The verifier dispatches on the artifact's self-described shape:
196
- * presence of `relay_id` (string) + `timestamp` (number) + `signature`
197
- * (hex string) identifies the heartbeat shape. Future: PeeringConfirm
198
- * or other peering attestations land as additive dispatch arms.
199
- */
200
- async function verifyAlternativePeeringArtifact(artifact, cert, ctx, errors) {
201
- const relayId = artifact["relay_id"];
202
- const timestamp = artifact["timestamp"];
203
- const signatureHex = artifact["signature"];
204
- if (typeof relayId !== "string" ||
205
- typeof timestamp !== "number" ||
206
- typeof signatureHex !== "string") {
207
- errors.push("alternative_peering artifact unrecognized — expected federation Heartbeat shape (relay_id, timestamp, signature)");
208
- return false;
209
- }
210
- const certSubjectId = cert.subject.kind === "motebit"
211
- ? cert.subject.motebit_id
212
- : cert.subject.operator_id;
213
- if (relayId !== certSubjectId) {
214
- errors.push(`peering artifact relay_id (${relayId}) does not match cert issuer (${certSubjectId})`);
215
- return false;
216
- }
217
- if (Math.abs(timestamp - cert.horizon_ts) > HEARTBEAT_FRESHNESS_WINDOW_MS) {
218
- errors.push(`peering artifact timestamp (${timestamp}) outside ±${HEARTBEAT_FRESHNESS_WINDOW_MS}ms of cert.horizon_ts (${cert.horizon_ts})`);
219
- return false;
220
- }
221
- let sigBytes;
222
- /* c8 ignore start -- defensive catch; `hexToBytes` (signing.ts) uses parseInt which silently returns NaN for non-hex chars rather than throwing, so this catch is unreachable today. Keep for forward-compat: a future hex-decode primitive that throws (e.g. native Uint8Array.fromHex) would route through this branch and fail closed rather than passing garbage bytes through to verifyBySuite. */
223
- try {
224
- sigBytes = hexToBytes(signatureHex);
225
- }
226
- catch {
227
- errors.push("peering artifact signature is not valid hex");
228
- return false;
229
- }
230
- /* c8 ignore stop */
231
- const payload = new TextEncoder().encode(`${relayId}|${timestamp}|${FEDERATION_HEARTBEAT_SUITE}`);
232
- const ok = await verifyBySuite(FEDERATION_HEARTBEAT_SUITE, payload, sigBytes, ctx.issuerPublicKey);
233
- if (!ok)
234
- errors.push("peering artifact signature does not verify against cert issuer pubkey");
235
- return ok;
236
- }
237
- //# sourceMappingURL=witness-omission-dispute.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"witness-omission-dispute.js","sourceRoot":"","sources":["../src/witness-omission-dispute.ts"],"names":[],"mappings":"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"}