From 52de096b43f764d5876c5bc36a7818ba6b78418d Mon Sep 17 00:00:00 2001 From: iret77 <63622643+iret77@users.noreply.github.com> Date: Wed, 10 Jun 2026 16:25:36 +0000 Subject: [PATCH] docs: SD-JWT predicate disclosure for mandates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue #18: verifying a plaintext Mandate hands the counterparty the negotiation envelope (max_value = reservation price, escalation_threshold, allowed_counterparties = relationship map). s5.3 (M4) now requires that Mandates SHOULD be issued as SD-JWT VCs with predicate support — strongly RECOMMENDED for contracting scope — proving 'value within cap' and 'counterparty in allowed set' without disclosing cap or list. The inherent escalation-time threshold leak is documented as acceptable. s6.6 cross-references mandates explicitly; new s11 threat row; Appendix C updated. --- SPEC.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/SPEC.md b/SPEC.md index ca89094..9d390ff 100644 --- a/SPEC.md +++ b/SPEC.md @@ -265,6 +265,7 @@ A Mandate **MUST** specify at least: - **Per-action value** (`max_value`, `escalation_threshold`) is fully checkable by a counterparty's Verifier: the action's value and the mandate cap are both present, and value **MUST** be expressed in the mandate's currency using a declared FX reference (`constraints.fx_ref`) when the agreement prices in another asset (e.g., `USDC.e`→`EUR`). - **Aggregate value** (`aggregate_value` over a window) is **not** enforceable by a counterparty, because an agent's other Threads are private and not globally discoverable. It is enforceable only by (a) the **Principal's own accounting** (replaying the agent's anchored Threads) or (b) a designated **mandate-state oracle** the Principal trusts. The spec states this limitation rather than pretending counterparties can police aggregates; the **per-action** cap is the counterparty-checkable guarantee. - **Non-Contracting actions need a value mapping.** Notarization and dispute actions have no price. For threshold purposes their "value" is defined per scope: a notarization's value is its posted bond/fee; a dispute action inherits the disputed Thread's escrow value. Mandates **MAY** set separate caps per `scope` so escalation cannot be silently bypassed by routing through Pillar II/III. +- **Verification must not leak the negotiation envelope.** Checking a Mandate is the counterparty's job — but a plaintext Mandate hands them `max_value` (the reservation price), `escalation_threshold`, and `allowed_counterparties` (a business-relationship map). Mandates **SHOULD** therefore be issued as **SD-JWT VCs with predicate support** (§6.6) — for `contracting`-scope Mandates this is strongly RECOMMENDED — so the Agent proves *"this action's value is within my per-action cap"* and *"this counterparty is within my allowed set"* without disclosing the cap or the list. **Residual leak, stated honestly:** a successful escalation co-signature still reveals "the threshold is ≤ this action's value"; that is inherent in threshold semantics and acceptable. Implementations that ship plaintext mandates by default are leaking their Principals' negotiation envelopes. ### 5.4 Trust framework for third parties @@ -423,7 +424,7 @@ Quantum resistance is a **hard requirement at the ANP layer** (a chain that cann ### 6.6 Confidentiality controls -- **Selective disclosure.** Objects MAY be issued as SD-JWT VCs so a party reveals only required fields to a given Verifier (e.g., prove "price ≤ cap" without revealing the price). +- **Selective disclosure.** Objects MAY be issued as SD-JWT VCs so a party reveals only required fields to a given Verifier (e.g., prove "price ≤ cap" without revealing the price). This applies in particular to **Mandates** (§5.3, M4) — the document's most negotiation-sensitive credential. - **Commit-reveal.** For sealed bids or sensitive terms, a party MAY anchor only a salted commitment first and reveal the Object later (e.g., during a dispute). The salt prevents dictionary attacks on low-entropy terms. - **Encryption at rest/in transit** of off-chain Objects is the parties' responsibility; ANP defines the envelope, not the storage. @@ -790,6 +791,7 @@ ANP uses the **Settlement Layer's native asset and/or stablecoins**. Native **EU | **Unbacked penalties** | Optional `performance_bond` posted at `execute`, sized to maximum penalty exposure; Verifiers flag terms whose penalty exposure exceeds the collateral reachable by `enforce` (§7.3/§10.3). | | **Micro-value Threads without economic recourse** | Small-claims `process_profile`: pre-agreed formula split on dispute, reputation-fed outcomes, no arbiter/fees (§9.4) — honestly scoped to repeat-player ecosystems; optimistic profile otherwise. | | **Mandate-chain abuse / DoS** | Bounded `max_depth`, caching, signature-verification limits per Thread. | +| **Mandate verification leaks negotiation caps** | SD-JWT predicate disclosure for Mandates ("value within cap", "counterparty in set") instead of plaintext caps/lists (§5.3/§6.6); residual escalation-threshold leak disclosed as inherent. | | **Retroactive mandate revocation / status-list rollback** | Anchor-time evaluation of authority; revocation is ex nunc only; Verifiers retain the signed, dated status-list credential as evidence; RECOMMENDED periodic status-list snapshot anchors (§5.3). | | **Mutable schema / trust-list references (semantic TOCTOU)** | All binding references are content-addressed `{id, hash}` pairs pinned in the accepted head (§7.3/§5.4); schema & suite registries are versioned, hash-identified artifacts selected by `anp_version` (Appendix A); Verifiers reject hash mismatches. | | **Quantum adversary (binding layer & semantic state)** | PQC-capable suites (ML-DSA/SLH-DSA), SHA-384/SHA3-384 anchors; anchor `status` mutations are void without a PQC-signed Object — enforced by Verifiers off-chain, and on-chain via native PQC verification or optimistic status enforcement where the profile provides it (§6.5) — so a forged chain key cannot rewrite *meaning*. | @@ -966,6 +968,7 @@ Seller asserts "delivered, criteria met" → `ASSERTED`. Buyer files `dispute` w - **Chain vs. attachment linkage (§6.1, §7.4, §8.3; review issue #5):** `accept`, `approve`, `witness`, `evidence`, and `revoke` are now **attachment Objects** that reference their target via an anchored hash in `body` (`accepts_hash`, `approves_hash`, `attestation_hash`, `dispute_hash`, `revokes`) and carry `previous_hash: null` — N-party `accept`s therefore no longer fork the linear chain. `sequence` is normatively defined (predecessor + 1 on the chain; the target's value for attachments); the ledger's anchor order is the ordering authority; an `accept` is valid iff its `accepts_hash` equals the head current at its anchoring time. - **Contract-readable dispute state (§6.2.1, §9.4, §13.1, §13.4; review issue #6):** the uncontested enforcement path requires proving the *absence* of a `dispute` on-chain, which a contract cannot do over a generic event stream. Profiles **MUST** realize `dispute`/`enforce` anchors as stateful, contract-readable records in the escrow's state space; §13.1 gains requirements 6 (contract-readable dispute/enforcement state) and 7 (anchor discoverability); the Hedera row in §13.4 now discloses that HCS-only anchoring cannot serve this path. - **Mandate revocation hardened against TOCTOU/repudiation (§5.3, §11; review issue #8):** authority is evaluated at the Object's **anchor timestamp**; revocation is **ex nunc** only (never retroactive against already-anchored Objects); Verifiers **MUST** retain the fetched status-list credential as point-in-time evidence, and issuers SHOULD anchor periodic status-list snapshots. New threat-table row "retroactive mandate revocation / status-list rollback". +- **Mandate selective disclosure (§5.3, §6.6, §11; review issue #18):** Mandates SHOULD be issued as SD-JWT VCs with predicate support (strongly RECOMMENDED for `contracting` scope), so verification proves "within cap" / "counterparty allowed" without disclosing `max_value`, `escalation_threshold`, or `allowed_counterparties`; the inherent escalation-time threshold leak is documented. - **Status-list mechanism generalized (§5.3, §8.6, §13.2; review issue #27):** Core now requires a *bit-array status list expressed as a signed, dated credential with provable point-in-time state*; W3C Bitstring Status List is RECOMMENDED, and profiles MAY bind StatusList2021 / RevocationBitmap2022 as equivalents. The IOTA profile binds the latter two and discloses IOTA Identity's pre-GA status. - **PQC status gate honestly scoped + optimistic enforcement (§6.5, §11, §13.2, §16.1; review issues #7, #26):** the v0.2 MUST ("a chain key alone MUST NOT be sufficient") was unenforceable on-chain (no ML-DSA/SLH-DSA natives in Move/EVM). v0.3 scopes the gate honestly — semantic validity of `status` transitions is enforced by Verifiers off-chain; on-chain enforcement is a declared profile capability (native PQC verification, or the newly specified **optimistic status enforcement** pattern, RECOMMENDED where natives are missing). The IOTA reference profile **MUST** use optimistic enforcement for `disputed`/`enforced` and is updated with its verified crypto-native inventory and IOTA Identity's first-party PQC suite support. - **Grind-resistant VRF selection (§8.3, §9.2, §11, §13.1; review issue #9):** VRF seeds for witness and arbiter draws **MUST** combine the anchored hash with chain randomness fixed only *after* the anchor (seeding by requester-controlled content alone is forbidden); profiles supporting VRF selection MUST name a post-anchor randomness source.