diff --git a/docs/SPEC.md b/docs/SPEC.md index bbd9466..bda1405 100644 --- a/docs/SPEC.md +++ b/docs/SPEC.md @@ -1,6 +1,6 @@ # cA2A Profile Specification -**Status**: draft, v0.1. This document describes the cA2A profile: a binding on A2A that makes agent-to-agent delegation attested, attenuated, confidential, and provable. +Status: draft, v0.1. This document describes the cA2A profile: a binding on A2A that makes agent-to-agent delegation attested, attenuated, confidential, and provable. ## Scope of this document @@ -8,10 +8,10 @@ cA2A is a profile, not a transport. It does not define how tasks are moved betwe ## Design tenets -1. **Profile, not protocol.** Bind to A2A and to IETF RATS, EAT, and SCITT rather than reinvent them. -2. **Neutral by construction.** No coupling to a single silicon vendor, cloud, or AI platform. This is the claim a vendor-anchored verifier cannot make. -3. **Fail closed.** Absence of evidence is denial, not a warning. A missing attestation or an unverifiable chain denies the call. -4. **Offline verifiable.** A third party can verify a delegation chain and its provenance DAG without contacting or trusting any operator. +1. Profile, not protocol: Bind to A2A and to IETF RATS, EAT, and SCITT rather than reinvent them. +2. Neutral by construction: No coupling to a single silicon vendor, cloud, or AI platform. This is the claim a vendor-anchored verifier cannot make. +3. Fail closed: Absence of evidence is denial, not a warning. A missing attestation or an unverifiable chain denies the call. +4. Offline verifiable: A third party can verify a delegation chain and its provenance DAG without contacting or trusting any operator. ## Components diff --git a/docs/spec/attestation.md b/docs/spec/attestation.md index b8bb1e1..069d29f 100644 --- a/docs/spec/attestation.md +++ b/docs/spec/attestation.md @@ -25,25 +25,25 @@ An `AttestationReport` carries `platform`, `measurement`, the bound `public_key` `ca2a_verify.sev_snp.verify_sev_snp_report` appraises an AMD SEV-SNP attestation report offline, in three fail-closed steps: -1. **Certificate chain.** The VCEK is verified up to a trusted AMD root (ARK) through `ARK -> ASK -> VCEK`. Each certificate must be validly issued by the next, and the root must match a trusted anchor by fingerprint. -2. **Report signature.** The ECDSA-P384 signature (stored as little-endian `r` and `s`) is verified against the VCEK public key over the report body (`report[:0x2A0]`). -3. **Binding.** The launch `measurement` and the `report_data` (which carries the runtime key and nonce) are checked against expected values. +1. Certificate chain: the VCEK is verified up to a trusted AMD root (ARK) through `ARK -> ASK -> VCEK`. Each certificate must be validly issued by the next, and the root must match a trusted anchor by fingerprint. +2. Report signature: the ECDSA-P384 signature (stored as little-endian `r` and `s`) is verified against the VCEK public key over the report body (`report[:0x2A0]`). +3. Binding: the launch `measurement` and the `report_data` (which carries the runtime key and nonce) are checked against expected values. -**What is validated.** The chain-verification path is exercised against the genuine AMD Milan ARK/ASK root chain fetched from AMD KDS (`tests/fixtures/sev_snp/`). The report-signature path is exercised end to end with a synthetic VCEK and report, because a genuine report plus VCEK pair requires real SEV-SNP hardware. Producing a report (`SevSnpProvider.attest`) fails closed off hardware (`AttestationUnsupported`). +What is validated. The chain-verification path is exercised against the genuine AMD Milan ARK/ASK root chain fetched from AMD KDS (`tests/fixtures/sev_snp/`). The report-signature path is exercised end to end with a synthetic VCEK and report, because a genuine report plus VCEK pair requires real SEV-SNP hardware. Producing a report (`SevSnpProvider.attest`) fails closed off hardware (`AttestationUnsupported`). -**Cross-operator use.** Two operators in separate trust domains each bind their sealed-channel public key into a report and verify the counterparty's report against a pinned golden measurement. This composes into mutual attestation, confidential cross-operator delegation (seal to the attested key), and binary-swap detection (a changed measurement is rejected), validated in software as claim C6. See the [call graph](call-graph.md) and the `claim6-cross-operator-attestation` experiment. +Cross-operator use. Two operators in separate trust domains each bind their sealed-channel public key into a report and verify the counterparty's report against a pinned golden measurement. This composes into mutual attestation, confidential cross-operator delegation (seal to the attested key), and binary-swap detection (a changed measurement is rejected), validated in software as claim C6. See the [call graph](call-graph.md) and the `claim6-cross-operator-attestation` experiment. ## TDX verification `ca2a_verify.tdx.verify_tdx_quote` appraises an Intel TDX quote (DCAP, ECDSA-256) offline in four fail-closed steps: the PCK certificate chain is verified up to a trusted Intel root; the Quoting Enclave report is verified against the PCK; the attestation key is confirmed to be the one the QE report data commits to (SHA-256 of the key and the QE auth data); and the attestation key's signature over the quote body is verified, along with the launch measurement (MRTD) and report data. -**What is validated.** The chain-verification path accepts the genuine self-signed Intel SGX Root CA fetched from Intel (`tests/fixtures/tdx/`) and rejects an untrusted root. The multi-level signature path (PCK to QE report to attestation key to quote) is exercised end to end with a synthetic self-consistent quote, because a genuine quote requires a TDX guest. Byte offsets follow the Intel DCAP Quote v4 layout; end-to-end validation against a real hardware quote requires a TDX guest and remains open. +What is validated. The chain-verification path accepts the genuine self-signed Intel SGX Root CA fetched from Intel (`tests/fixtures/tdx/`) and rejects an untrusted root. The multi-level signature path (PCK to QE report to attestation key to quote) is exercised end to end with a synthetic self-consistent quote, because a genuine quote requires a TDX guest. Byte offsets follow the Intel DCAP Quote v4 layout; end-to-end validation against a real hardware quote requires a TDX guest and remains open. ## TPM verification `ca2a_verify.tpm.verify_tpm_quote` appraises a TPM 2.0 quote (`TPMS_ATTEST`) offline: the AK certificate chain is verified to a trusted root, the AK signature over the attest blob is verified (ECDSA-SHA256 or RSA PKCS#1 v1.5), the structure is confirmed to be a TPM-generated quote (magic and type), and the qualifying data (the verifier's nonce) and the PCR digest (the platform measurement) are checked against expected values. -**What is validated.** Unlike SEV-SNP and TDX, TPM attestation keys chain to per-vendor EK roots, so there is no single published root to validate against; the caller supplies the vendor roots it trusts, and the verifier is exercised against synthetic self-consistent vectors. Producing a quote (`TpmProvider.attest`) fails closed off a real TPM. +What is validated. Unlike SEV-SNP and TDX, TPM attestation keys chain to per-vendor EK roots, so there is no single published root to validate against; the caller supplies the vendor roots it trusts, and the verifier is exercised against synthetic self-consistent vectors. Producing a quote (`TpmProvider.attest`) fails closed off a real TPM. ## Fail closed diff --git a/docs/spec/cedar-policy.md b/docs/spec/cedar-policy.md index c77dfd9..7f60208 100644 --- a/docs/spec/cedar-policy.md +++ b/docs/spec/cedar-policy.md @@ -6,7 +6,7 @@ When a peer accepts a delegated task, two independent trust decisions meet. The effective = delegated_scope ∩ local_policy_allow ``` -**Status.** The intersection semantics are **implemented** as an enforcement decision core in `ca2a_runtime.peer` (`effective_scope`, `enforce_peer_call`), and the local policy can be either a capability allow set (`ca2a_runtime.policy.LocalPolicy`) or a **real Cedar policy engine** (`ca2a_runtime.cedar.CedarPolicy`, backed by `cedarpy`, the same engine cMCP runs). Both satisfy the `ca2a_runtime.policy.Policy` protocol, so they are interchangeable in the peer path. Validated by experiment C3 and the Cedar unit tests. What remains is wiring the decision core to a **live A2A transport** rather than a direct call. See [call-graph.md](call-graph.md) and [ROADMAP.md](../../ROADMAP.md). +Status: the intersection semantics are implemented as an enforcement decision core in `ca2a_runtime.peer` (`effective_scope`, `enforce_peer_call`), and the local policy can be either a capability allow set (`ca2a_runtime.policy.LocalPolicy`) or a real Cedar policy engine (`ca2a_runtime.cedar.CedarPolicy`, backed by `cedarpy`, the same engine cMCP runs). Both satisfy the `ca2a_runtime.policy.Policy` protocol, so they are interchangeable in the peer path. Validated by experiment C3 and the Cedar unit tests. What remains is wiring the decision core to a live A2A transport rather than a direct call. See [call-graph.md](call-graph.md) and [ROADMAP.md](../../ROADMAP.md). ## Cedar policy @@ -24,8 +24,8 @@ effective_scope(chain, policy) # delegated leaf scope AND what Cedar allows Each side owns half of the decision, and neither may escalate the other. -- **Delegation is authoritative on the caller's side.** A capability that is allowed by the callee's local policy but absent from the verified delegated scope is dropped. The caller was never granted it upstream, so local policy cannot manufacture it. -- **Local policy is authoritative on the callee's side.** A capability present in the delegated scope but denied by local policy is dropped. The delegator cannot force the callee to honor something the callee's own policy forbids. +- Delegation is authoritative on the caller's side: a capability that is allowed by the callee's local policy but absent from the verified delegated scope is dropped. The caller was never granted it upstream, so local policy cannot manufacture it. +- Local policy is authoritative on the callee's side: a capability present in the delegated scope but denied by local policy is dropped. The delegator cannot force the callee to honor something the callee's own policy forbids. The safe combination is therefore the set intersection, not a union and not an override. The effective set is never larger than either input. This is defense in depth: an error or compromise on one side cannot widen what the other side permits. @@ -69,7 +69,7 @@ Today the field is validated by `Ca2aConfig.from_dict` but has no runtime effect ## Illustrative shape -The following shows the intended computation. It is **illustrative only** and is **not the Cedar engine**: it stands in a plain set intersection for what the runtime will do by mapping each capability to a Cedar authorization query. The `experiments/claim3-scope-policy-intersection/run.py` harness prints this same shape behind a SKIP banner, because the real path is gated on Tier 2. +The following shows the intended computation. It is illustrative only and is not the Cedar engine: it stands in a plain set intersection for what the runtime will do by mapping each capability to a Cedar authorization query. The `experiments/claim3-scope-policy-intersection/run.py` harness prints this same shape behind a SKIP banner, because the real path is gated on Tier 2. ```python # Illustrative only. NOT the Cedar engine and NOT the runtime path. @@ -92,7 +92,7 @@ In the wired implementation `local_policy_allow` is not a precomputed set. Each The scope-policy intersection depends on two Tier 2 runtime pieces, neither of which is built: -- **Runtime peer-delegation enforcement.** A call-time gate that accepts a delegation credential on a live inbound peer call and runs verification in the request path. `verify_chain` already checks the chain in isolation; there is no point at which it gates an actual peer-to-peer call. -- **Cedar binding.** The wiring from a verified delegated scope to the cmcp Cedar engine, and the effective-permission computation above. +- Runtime peer-delegation enforcement: a call-time gate that accepts a delegation credential on a live inbound peer call and runs verification in the request path. `verify_chain` already checks the chain in isolation; there is no point at which it gates an actual peer-to-peer call. +- Cedar binding: the wiring from a verified delegated scope to the cmcp Cedar engine, and the effective-permission computation above. Until both land, do not describe cA2A as enforcing local policy on delegated calls. See [ROADMAP.md](../../ROADMAP.md). diff --git a/docs/spec/conformance.md b/docs/spec/conformance.md index da21eb2..bc87c58 100644 --- a/docs/spec/conformance.md +++ b/docs/spec/conformance.md @@ -1,6 +1,6 @@ # Conformance -An implementation may claim **cA2A-compatible** for a given version when it passes all MUST-level tests in the cA2A conformance suite for that version. This ties directly to the trademark language in [CHARTER.md](../../CHARTER.md): the mark asserts that a deployment satisfies the attestation, attenuation, sealing, and provenance requirements defined here. +An implementation may claim cA2A-compatible for a given version when it passes all MUST-level tests in the cA2A conformance suite for that version. This ties directly to the trademark language in [CHARTER.md](../../CHARTER.md): the mark asserts that a deployment satisfies the attestation, attenuation, sealing, and provenance requirements defined here. ## The normative suite @@ -24,8 +24,8 @@ pytest tests/conformance/ -v ## Levels -- **MUST**: required for a cA2A-compatible claim. Partial conformance (MUST only) is sufficient. -- **SHOULD**: recommended; indicates a higher-quality implementation. +- MUST: required for a cA2A-compatible claim. Partial conformance (MUST only) is sufficient. +- SHOULD: recommended; indicates a higher-quality implementation. Test IDs are stable: once assigned, an ID is never reused even if the test is removed. This lets a conformance report for one version be compared against another. diff --git a/docs/spec/failure-modes.md b/docs/spec/failure-modes.md index 54f6511..38d9110 100644 --- a/docs/spec/failure-modes.md +++ b/docs/spec/failure-modes.md @@ -2,7 +2,7 @@ cA2A fails closed. Every check on the delegation path either produces positive, verifiable evidence that a request is authorized, or it denies. There is no third state where a request proceeds with a warning attached. The guiding principle is this: -**Absence of evidence is denial, not a warning.** +Absence of evidence is denial, not a warning. An unsigned credential, a measurement that cannot be checked, a provenance record whose parent link does not resolve: each of these is treated as a failure, not a soft signal. This page enumerates the failure paths, the error each one raises, and which paths are enforced today versus which fail closed pending Tier 2/3 work. diff --git a/docs/spec/threat-model.md b/docs/spec/threat-model.md index 276320d..d2a5a7f 100644 --- a/docs/spec/threat-model.md +++ b/docs/spec/threat-model.md @@ -34,4 +34,4 @@ Out of adversary scope: breaking the underlying cryptographic primitives (Ed2551 ## Residual risks in this release -Because attestation and sealing are not yet implemented (Tier 2/3), this release defends bounded authority and provenance-of-intent (via signed chains) but does **not** yet defend peer integrity or task confidentiality at runtime. Do not rely on cA2A for confidentiality across a trust boundary until the sealed channel and a real attestation backend land. See [LIMITATIONS.md](../../LIMITATIONS.md). +Because attestation and sealing are not yet implemented (Tier 2/3), this release defends bounded authority and provenance-of-intent (via signed chains) but does not yet defend peer integrity or task confidentiality at runtime. Do not rely on cA2A for confidentiality across a trust boundary until the sealed channel and a real attestation backend land. See [LIMITATIONS.md](../../LIMITATIONS.md). diff --git a/docs/spec/transport.md b/docs/spec/transport.md index 0d29317..92b0a99 100644 --- a/docs/spec/transport.md +++ b/docs/spec/transport.md @@ -9,22 +9,22 @@ cA2A is a profile on A2A, not a competing transport. A2A moves tasks and context | Extension URI + namespaced metadata keys (below) | Specified | | `ca2a_runtime.transport` adapter: A2A metadata ↔ `PeerRequest` | Implemented (parse/attach only) | | Hand-off into `handle_peer_request` once a `PeerRequest` exists | Implemented in-process; callers invoke it after parsing | -| Reference HTTP server/client (`ca2a_runtime.transport.server`/`client`) | Implemented, software mode. A **reference** transport, not part of the profile | +| Reference HTTP server/client (`ca2a_runtime.transport.server`/`client`) | Implemented, software mode. A reference transport, not part of the profile | | Live attestation handshake on an inbound call | Implemented in software mode (`ca2a_runtime.attestation`, `assurance="none"`) | | Seal gated on the appraised channel key on a live call | Implemented in software mode | -| Seal bound to a **hardware-verified** measurement | Not yet — needs a real quote via the `verifier` seam (Tier 3) | -| `ca2a start` CLI listener | Not yet — serving is via `transport.server.serve` | +| Seal bound to a hardware-verified measurement | Not yet: needs a real quote via the `verifier` seam (Tier 3) | +| `ca2a start` CLI listener | Not yet: serving is via `transport.server.serve` | -The reference HTTP server/client run a live call end to end **in software mode** (`assurance="none"`). That is progress on Tier 2 transport wiring and a convenience for running the peer path off hardware. It is **not** evidence that cA2A is attested across trust domains: that needs the hardware `verifier` seam driven by a real quote. See [LIMITATIONS.md](../../LIMITATIONS.md) and [ROADMAP.md](../../ROADMAP.md). +The reference HTTP server/client run a live call end to end in software mode (`assurance="none"`). That is progress on Tier 2 transport wiring and a convenience for running the peer path off hardware. It is not evidence that cA2A is attested across trust domains: that needs the hardware `verifier` seam driven by a real quote. See [LIMITATIONS.md](../../LIMITATIONS.md) and [ROADMAP.md](../../ROADMAP.md). ## Overlay, not fork cA2A does not define its own transport, message framing, or handshake. It rides inside A2A. Two pieces of cA2A data travel with a task: -- The **delegation credential** (or the chain root-to-leaf), naming issuer, subject, scope, depth, and parent link. See [the delegation chain](delegation-chain.md). -- The **sealing metadata**, carrying an opaque sealed ciphertext when the caller seals the task payload. Sealing crypto is implemented; binding that seal to a verified peer measurement on a live call is still Tier 2/3. See [the sealed channel](sealed-channel.md). +- The delegation credential (or the chain root-to-leaf), naming issuer, subject, scope, depth, and parent link. See [the delegation chain](delegation-chain.md). +- The sealing metadata, carrying an opaque sealed ciphertext when the caller seals the task payload. Sealing crypto is implemented; binding that seal to a verified peer measurement on a live call is still Tier 2/3. See [the sealed channel](sealed-channel.md). -Both ride in A2A extension fields. The **profile** claims no new wire format and no new endpoint: removing every cA2A field leaves a valid A2A task. The reference HTTP transport (`ca2a_runtime.transport.server`) does expose convenience endpoints, including an attestation-handshake path for fetching the callee's channel key, but those belong to the reference transport, not the profile. They are one way to carry the extension fields, not a requirement of the profile, and any A2A server can carry them instead. +Both ride in A2A extension fields. The profile claims no new wire format and no new endpoint: removing every cA2A field leaves a valid A2A task. The reference HTTP transport (`ca2a_runtime.transport.server`) does expose convenience endpoints, including an attestation-handshake path for fetching the callee's channel key, but those belong to the reference transport, not the profile. They are one way to carry the extension fields, not a requirement of the profile, and any A2A server can carry them instead. ## Extension URI and metadata keys @@ -43,7 +43,7 @@ Namespaced keys on A2A `metadata` (message and/or params): | `https://agentrust.io/extensions/ca2a/v0.1/requested_capability` | string | Capability the callee must grant | | `https://agentrust.io/extensions/ca2a/v0.1/record_id` | string | Provenance record id for this hop | | `https://agentrust.io/extensions/ca2a/v0.1/parent_record_hash` | string or `null` | Parent TRACE/provenance hash; `null` for a root hop | -| `https://agentrust.io/extensions/ca2a/v0.1/sealed_payload` | string (base64url) or omitted | Opaque sealed ciphertext only — **not** a verified measurement binding | +| `https://agentrust.io/extensions/ca2a/v0.1/sealed_payload` | string (base64url) or omitted | Opaque sealed ciphertext only, not a verified measurement binding | Constants and helpers live in `ca2a_runtime.transport`. @@ -55,9 +55,9 @@ The credential and sealing metadata are carried in A2A `metadata` maps on the ta Carrying the trust envelope in extension fields is what keeps the profile an overlay: -- A **non-cA2A peer** does not understand the extension fields and ignores them. The task is handled as a plain A2A task. -- A **cA2A-aware peer** that sees **no** cA2A keys treats the message as ordinary A2A input (`parse_peer_request` returns `None`). It must not invent a partial trust state. -- A **cA2A-aware peer** that sees **any** cA2A key fails closed on malformed or incomplete cA2A metadata (`TransportError`), then — once parsed — runs the inbound enforcement pipeline. +- A non-cA2A peer does not understand the extension fields and ignores them. The task is handled as a plain A2A task. +- A cA2A-aware peer that sees no cA2A keys treats the message as ordinary A2A input (`parse_peer_request` returns `None`). It must not invent a partial trust state. +- A cA2A-aware peer that sees any cA2A key fails closed on malformed or incomplete cA2A metadata (`TransportError`), then, once parsed, runs the inbound enforcement pipeline. ## Adapter API