From c769f90982e3cc61baf79a6caa6b8d9924733963 Mon Sep 17 00:00:00 2001 From: Imran Siddique Date: Mon, 27 Jul 2026 11:02:32 -0700 Subject: [PATCH] docs(spec): correct implementation status that drifted behind the code Three spec pages still described the repo as it stood around 0.1.0a1, and in places said the opposite of what ships. call-graph said the TDX and TPM backends are not implemented and that peer attestation is not wired into the call path. All three verifiers are implemented, and the handshake gates the seal on an appraised channel key. It also still listed the Cedar engine binding as pending. component-model said the sealed channel is a placeholder that fails closed and the TEE layer is interface-only, and had no entry at all for peer, policy, cedar, transport, node, attestation or trace_binding. Its error list was three codes short and the CLI had grown a third subcommand. attestation said real-hardware validation of the SEV-SNP and TDX paths remains open, which the runs recorded in docs/hardware-validation.md have now closed. TPM is called out explicitly as the one backend with no real quote behind it, rather than being left to look equivalent. The corrections go both ways: several understated what is built, and the TPM and live-call caveats are stated more sharply than before. No bold emphasis, per the house-style cleanup in #59. Co-Authored-By: Claude Opus 5 (1M context) --- docs/spec/attestation.md | 6 ++--- docs/spec/call-graph.md | 10 +++++--- docs/spec/component-model.md | 48 ++++++++++++++++++++++++------------ 3 files changed, 41 insertions(+), 23 deletions(-) diff --git a/docs/spec/attestation.md b/docs/spec/attestation.md index 069d29f..1c1d392 100644 --- a/docs/spec/attestation.md +++ b/docs/spec/attestation.md @@ -29,7 +29,7 @@ An `AttestationReport` carries `platform`, `measurement`, the bound `public_key` 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/`), and the full path (VCEK chain to the ARK, ECDSA-P384 report signature, measurement binding, and rejection of a flipped bit) is exercised against a genuine SEV-SNP report captured from an Azure confidential VM, alongside the synthetic vectors. Producing a report (`SevSnpProvider.attest`) still fails closed off hardware (`AttestationUnsupported`). See [hardware validation](../hardware-validation.md). 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. @@ -37,13 +37,13 @@ Cross-operator use. Two operators in separate trust domains each bind their seal `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, and the multi-level signature path (PCK to QE report to attestation key to quote) is exercised both with a synthetic self-consistent quote and against a genuine DCAP v4 quote captured from a GCP C3 confidential VM. Byte offsets follow the Intel DCAP Quote v4 layout, including the nested type-6 `QE_REPORT_CERTIFICATION_DATA` header that wraps the QE report and the type-5 PCK chain; reading the QE report directly after the attestation key is six bytes early and rejects every genuine quote. Quote *generation* still requires a TDX guest. See [hardware validation](../hardware-validation.md). ## 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 only. No real AK-signed quote has been run through it, so unlike the other two backends the TPM path is not hardware-validated. See [hardware validation](../hardware-validation.md). ## Fail closed diff --git a/docs/spec/call-graph.md b/docs/spec/call-graph.md index 73b9e8a..287b822 100644 --- a/docs/spec/call-graph.md +++ b/docs/spec/call-graph.md @@ -35,8 +35,8 @@ If any step raises, the call is denied. Absence of evidence is denial, not a war | Step | What it enforces | Function / type | Status | |---|---|---|---| | 1. Chain verification | Signature, continuity, attenuation, depth bound, anti-replay | `verify_chain` | Implemented | -| 2. Peer attestation | Peer measurement matches an expected value | `ca2a_verify.sev_snp.verify_sev_snp_report` | SEV-SNP verifier implemented; not yet wired into the call path; report needs hardware | -| 3. Scope intersection | Delegated scope intersected with local policy | `ca2a_runtime.peer.effective_scope`, `enforce_peer_call` | Implemented (decision core); Cedar engine binding pending (#10) | +| 2. Peer attestation | Peer measurement matches an expected value | `ca2a_verify.sev_snp`, `.tdx`, `.tpm`; `ca2a_runtime.attestation` | Verifiers implemented and wired into the handshake. SEV-SNP and TDX appraise real hardware evidence; on a live call off hardware the appraisal is `assurance="none"` | +| 3. Scope intersection | Delegated scope intersected with local policy | `ca2a_runtime.peer.effective_scope`, `enforce_peer_call` | Implemented, with `LocalPolicy` or the Cedar engine (`CedarPolicy`) | | 4. Payload sealing | Payload sealed to the peer's attested key | `SealedChannel.seal`, `open_sealed` | Implemented (crypto); binding to a verified report on the live path pending | | 5. Provenance record | A `DelegationRecord` emitted and linked to its parent | `enforce_peer_call`, `record_for`, `verify_dag` | Implemented (emitted by the decision core) | | Inbound pipeline handler | Verify, enforce, open sealed payload, emit record off a parsed request | `handle_peer_request`, `PeerRequest` | Implemented (transport-agnostic) | @@ -57,9 +57,11 @@ delegated_scope = leaf.scope # frozenset[str], the authority to enforce below `max_depth` comes from `Ca2aConfig.max_delegation_depth` (default 8). This step is deterministic and offline: it contacts no operator and depends only on the signed bytes. It is the only step that gates an inbound call in this release. -## Step 2: verify peer attestation (pending, Tier 3) +## Step 2: verify peer attestation (implemented; software mode on a live call) -The runtime confirms the peer is running attested, measured code before it is trusted with the task, by checking the peer's report measurement against an expected value under a fresh nonce. A SEV-SNP verifier exists (`ca2a_verify.sev_snp.verify_sev_snp_report`: VCEK chain, report-signature, measurement binding), but it is not yet wired into this call path, producing a report requires real SEV-SNP hardware, and TDX/TPM backends are not implemented. Verification fails closed when evidence is absent. Do not treat this step as active on the live path yet. See [attestation.md](attestation.md). +The runtime confirms the peer is running attested, measured code before it is trusted with the task, by checking the peer's report measurement against an expected value under a fresh nonce. Verifiers exist and are fail-closed for all three platforms: `ca2a_verify.sev_snp` (VCEK chain, ECDSA-P384 report signature, measurement binding), `ca2a_verify.tdx` (DCAP v4 including the nested type-6 QE certification data, PCK chain to the Intel SGX Root CA, QE binding, MRTD) and `ca2a_verify.tpm` (AK chain, AK signature, qualifying-data and PCR-digest binding). The SEV-SNP and TDX verifiers appraise genuine hardware evidence; TPM is synthetic-vector validated only. See [hardware validation](../hardware-validation.md). + +The handshake in `ca2a_runtime.attestation` gates the seal on an appraised channel key, so this step is wired into the call path. What is still missing is hardware on the live path: producing a report requires the platform, so off hardware the appraisal records `assurance="none"` and the `verifier` seam is not driven off a real quote. Do not read a completed software-mode call as an attested one. See [attestation.md](attestation.md). ## Step 3: intersect scope with local policy (implemented as a decision core) diff --git a/docs/spec/component-model.md b/docs/spec/component-model.md index 0494dd4..55cf380 100644 --- a/docs/spec/component-model.md +++ b/docs/spec/component-model.md @@ -18,11 +18,23 @@ The cA2A runtime is a set of small, composable modules under `src/`. Each maps t ### channel -`ca2a_runtime.channel.sealed` defines `SealedChannel`, the measurement-bound peer channel. Instantiation is allowed so the runtime can be wired against the interface, but `seal()` and `open()` fail closed with `SEALED_CHANNEL_ERROR` today. This is Tier 2 and not yet implemented; do not send confidential payloads across a trust boundary and assume they are protected. See [sealed channel](sealed-channel.md) and [LIMITATIONS.md](../../LIMITATIONS.md). +`ca2a_runtime.channel` defines `SealedChannel`, `generate_channel_keypair`, and `open_sealed`: HPKE-style sealing (X25519 ECDH, HKDF-SHA256, ChaCha20-Poly1305) of a payload to the peer's channel key. Only the holder of that key opens it; a wrong key or tampered ciphertext fails closed. The cryptography is implemented. What is *not* yet true is the hardware property: until the key is bound to a hardware-verified measurement, do not assume a payload is confined to a specific attested enclave. See [sealed channel](sealed-channel.md) and [LIMITATIONS.md](../../LIMITATIONS.md). + +### peer, policy and cedar + +`ca2a_runtime.peer` is the inbound decision core. `effective_scope(chain, policy)` verifies the chain and returns the delegated leaf scope intersected with local policy; `enforce_peer_call(...)` enforces a requested capability against it and emits a linked provenance record, raising `SCOPE_NOT_PERMITTED` (carrying a denial record) when the capability falls outside. `handle_peer_request(request, ...)` composes the full transport-agnostic pipeline: verify, enforce, open any sealed payload with the enclave key, emit the record. `ca2a_runtime.policy` defines the `Policy` protocol and `LocalPolicy` (allow set); `ca2a_runtime.cedar.CedarPolicy` is the Cedar-engine implementation, interchangeable with it. Implemented. See [Cedar policy](cedar-policy.md) and [call graph](call-graph.md). ### tee -`ca2a_runtime.tee.base` defines the provider interface and evidence model. `AttestationReport` is a frozen dataclass binding a `public_key` to a `measurement` under a `nonce` on a named `platform`. `BaseProvider` is an ABC with `detect()` and `attest(public_key, nonce)`. Real hardware providers (TPM, SEV-SNP, TDX, OPAQUE) are Tier 3 and not implemented; their `detect()` returns False so they are never auto-selected, and verification fails closed. See [attestation](attestation.md). +`ca2a_runtime.tee.base` defines the provider interface and evidence model. `AttestationReport` is a frozen dataclass binding a `public_key` to a `measurement` under a `nonce` on a named `platform`. `BaseProvider` is an ABC with `detect()` and `attest(public_key, nonce)`. Verifiers for SEV-SNP, Intel TDX and TPM 2.0 are implemented in `ca2a_verify` and appraise real evidence, all fail-closed; `ca2a_runtime.tee.software.SoftwareProvider` supplies a no-hardware provider that is never auto-selected. Quote *generation* requires the corresponding hardware, so each hardware provider's `detect()` returns False off that platform. The OPAQUE provider is not implemented. See [attestation](attestation.md) and [hardware validation](../hardware-validation.md). + +### transport, node and attestation handshake + +`ca2a_runtime.transport.a2a_adapter` parses cA2A extension metadata on an A2A message into a `PeerRequest` and attaches it in the reverse direction, failing closed on malformed metadata (`TRANSPORT_ERROR`) and returning `None` when no cA2A keys are present. `ca2a_runtime.transport.server`/`client` are a standard-library reference HTTP transport, and `ca2a_runtime.node.PeerNode` composes provider, policy, adapter and handler. `ca2a_runtime.attestation` (offer, verify, seal) gates sealing on a channel key the caller has appraised under a fresh nonce. All implemented and exercised end to end in software mode; the reference transport is a convenience, not part of the profile. See [transport](transport.md). + +### trace_binding and canonical + +`ca2a_runtime.trace_binding` lifts each hop into a signed TRACE Trust Record carrying the A2A `delegation` block (`build_trace_record`, `sign_trace_record`, `emit_dag`, `trace_record_hash`, `HopContext`), built on `agentrust-trace`. `ca2a_verify.verify_trace_dag` verifies a signed DAG offline and `cross_check_trace_dag` ties it to the chain. `ca2a_runtime.canonical` is the RFC 8785 JCS canonicalizer the signatures are computed over. Implemented. See [TRACE A2A profile](trace-a2a-profile.md). ### config @@ -30,11 +42,11 @@ The cA2A runtime is a set of small, composable modules under `src/`. Each maps t ### errors -`ca2a_runtime.errors` is the central registry. Every error is a `CA2AError` subclass carrying a stable `code` and an `http_status`: `CONFIG_ERROR`, `INVALID_CREDENTIAL`, `SCOPE_ESCALATION`, `BROKEN_DELEGATION_LINK`, `DELEGATION_DEPTH_EXCEEDED`, `CREDENTIAL_REPLAY`, `ATTESTATION_UNSUPPORTED`, `ATTESTATION_FAILED`, `SEALED_CHANNEL_ERROR`, `PROVENANCE_LINK_BROKEN`. See [error codes](error-codes.md). +`ca2a_runtime.errors` is the central registry. Every error is a `CA2AError` subclass carrying a stable `code` and an `http_status`: `CONFIG_ERROR`, `INVALID_CREDENTIAL`, `SCOPE_ESCALATION`, `BROKEN_DELEGATION_LINK`, `DELEGATION_DEPTH_EXCEEDED`, `CREDENTIAL_REPLAY`, `ATTESTATION_UNSUPPORTED`, `ATTESTATION_FAILED`, `SEALED_CHANNEL_ERROR`, `PROVENANCE_LINK_BROKEN`, `SCOPE_NOT_PERMITTED`, `TRACE_RECORD_INVALID`, `TRANSPORT_ERROR`. See [error codes](error-codes.md). ### cli -`ca2a_runtime.cli` exposes the `ca2a` command with two subcommands: `validate-config --config` (loads and validates a `Ca2aConfig`) and `verify-chain --chain [--max-depth]` (calls `verify_chain_file` and prints a JSON result). Both are implemented and operate offline. +`ca2a_runtime.cli` exposes the `ca2a` command with three subcommands: `validate-config --config` (loads and validates a `Ca2aConfig`), `verify-chain --chain [--max-depth]` (calls `verify_chain_file` and prints a JSON result), and `verify-dag --dag [--chain] [--max-depth]` (verifies a provenance DAG, optionally cross-checking it against the chain, and reports `outcome: denied` when the leaf documents a refusal). All implemented and offline. ## Component map @@ -43,20 +55,24 @@ The cA2A runtime is a set of small, composable modules under `src/`. Each maps t | delegation | `ca2a_runtime.delegation.credential` | `DelegationCredential`, `new_keypair`, `verify_chain` | Implemented | | provenance | `ca2a_runtime.provenance` | `DelegationRecord`, `record_for`, `verify_dag`, `cross_check_chain` | Implemented | | verify | `ca2a_verify.verify` | `verify_delegation_chain`, `verify_chain_file`, `ChainResult` | Implemented | -| config | `ca2a_runtime.config` | `Ca2aConfig` | Surface implemented; peer path pending (Tier 2) | +| config | `ca2a_runtime.config` | `Ca2aConfig` | Implemented | | errors | `ca2a_runtime.errors` | `CA2AError` and subclasses | Implemented | -| cli | `ca2a_runtime.cli` | `ca2a validate-config`, `ca2a verify-chain` | Implemented | -| channel | `ca2a_runtime.channel.sealed` | `SealedChannel` | Placeholder, fails closed (Tier 2) | -| tee | `ca2a_runtime.tee.base` | `BaseProvider`, `AttestationReport` | Interface only; hardware backends pending (Tier 3) | +| cli | `ca2a_runtime.cli` | `validate-config`, `verify-chain`, `verify-dag` | Implemented | +| channel | `ca2a_runtime.channel` | `SealedChannel`, `generate_channel_keypair`, `open_sealed` | Crypto implemented; binding to a hardware-verified measurement pending | +| peer | `ca2a_runtime.peer` | `effective_scope`, `enforce_peer_call`, `handle_peer_request` | Implemented | +| policy | `ca2a_runtime.policy`, `ca2a_runtime.cedar` | `Policy`, `LocalPolicy`, `CedarPolicy` | Implemented | +| transport | `ca2a_runtime.transport`, `ca2a_runtime.node` | `parse_peer_request`, `serve`, `PeerNode` | Implemented (reference transport, software mode) | +| attestation | `ca2a_runtime.attestation` | offer, verify, seal | Implemented; appraisal is `assurance="none"` off hardware | +| tee | `ca2a_runtime.tee` | `BaseProvider`, `AttestationReport`, `SoftwareProvider` | Interface and software provider implemented; quote generation needs the platform | +| verifiers | `ca2a_verify.sev_snp`, `.tdx`, `.tpm` | `verify_sev_snp_report`, `verify_tdx_quote`, `verify_tpm_quote` | Implemented and fail-closed. SEV-SNP and TDX appraise real hardware evidence; TPM is synthetic-vector validated only | +| trace_binding | `ca2a_runtime.trace_binding`, `ca2a_verify.dag` | `emit_dag`, `verify_trace_dag`, `cross_check_trace_dag` | Implemented | ## How they compose on an inbound peer call -The intended peer path threads these components together. Steps 2 through 5 below are the target composition; the implemented parts today are the chain and provenance verification an offline verifier can run over signed evidence. - -1. A hands B a child credential with `scope ⊆` A's scope. This is the [delegation](delegation-chain.md) model, implemented. -2. Before B accepts, the runtime verifies the chain with `verify_chain` and intersects the delegated scope with a local Cedar policy under B's `enforcement_mode`. Chain verification is implemented; runtime enforcement and Cedar scope intersection are Tier 2 and not yet built. See [Cedar policy](cedar-policy.md). -3. B's `tee` provider produces an `AttestationReport`; the runtime checks the measurement. The interface exists, but no hardware backend verifies a quote yet (Tier 3), so this fails closed. See [attestation](attestation.md). -4. The task payload is sealed to B's measurement through `SealedChannel`. Tier 2, fails closed today. See [sealed channel](sealed-channel.md). -5. B emits a `DelegationRecord` linking to A's record via `record_for`, and any verifier can later run `verify_dag` and `cross_check_chain` offline. Implemented. See [TRACE A2A profile](trace-a2a-profile.md). +1. A hands B a child credential with `scope ⊆` A's scope. This is the [delegation](delegation-chain.md) model. Implemented. +2. B appraises the peer's attestation evidence before sealing anything to its channel key. The handshake and the verifiers are implemented, and the SEV-SNP and TDX verifiers appraise genuine hardware evidence, but on a live call the appraisal runs in software mode at `assurance="none"`. See [attestation](attestation.md) and [hardware validation](../hardware-validation.md). +3. The runtime verifies the chain with `verify_chain` and intersects the delegated scope with B's local policy (`LocalPolicy` or `CedarPolicy`), enforcing the requested capability. Implemented. See [Cedar policy](cedar-policy.md). +4. The task payload is sealed to B's channel key through `SealedChannel` and opened with B's enclave-bound key. The cryptography is implemented; binding the seal to a hardware-verified measurement is the remaining hardware step. +5. B emits a `DelegationRecord` linking to A's record, or a denial record if the call was refused, and any verifier runs `verify_dag` and `cross_check_chain` offline. Implemented. See [TRACE A2A profile](trace-a2a-profile.md). -What ships today is the offline path: given signed credentials and records, `ca2a_verify` and `provenance` reconstruct and check the delegation tree without trusting the operators that produced it. The runtime peer enforcement, sealed channel, Cedar intersection, and hardware attestation that would gate a live call are pending. See [failure modes](failure-modes.md), [ROADMAP.md](../../ROADMAP.md), and [LIMITATIONS.md](../../LIMITATIONS.md). +What ships today runs the whole path live in software mode, and everything except steps 2 and 4's hardware property is verifiable offline by a party trusting neither operator. What does not ship is a peer whose channel key is bound to a hardware-verified measurement on a live call, which is why cA2A is not described as attested across trust domains. See [failure modes](failure-modes.md), [ROADMAP.md](../../ROADMAP.md), and [LIMITATIONS.md](../../LIMITATIONS.md).