Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion LIMITATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ cA2A is a pre-release profile in active design. This document states plainly wha

## What is stubbed or not yet implemented

- **Hardware-attested live binding.** The live transport and handshake run today in **software mode only** (the reference server/client above, `assurance="none"`). The remaining gap is hardware: driving the `verifier` seam in `ca2a_runtime.attestation` off a real SEV-SNP, TDX, or TPM quote (wrapping `ca2a_verify`) so the peer's channel key is bound to a hardware-verified enclave measurement, and relying on the enclave to hold the channel private key. There is also no CLI listener (`ca2a start`); serving is via `ca2a_runtime.transport.server.serve`. A software-mode live call is Tier 2 progress, not a claim that cA2A is attested across trust domains.
- **Hardware-attested live binding.** The `verifier` seam in `ca2a_runtime.attestation` has now been driven off a real SEV-SNP quote on an Azure confidential VM: `verify_offer` returned `assurance="hardware"`, a payload was sealed to a channel key a hardware-verified measurement vouches for, and both a measurement mismatch and a stale nonce were rejected. See [docs/hardware-validation.md](docs/hardware-validation.md). Two gaps remain. First, the reference server/client still run in **software mode** by default (`assurance="none"`); the hardware path is a validated capability, not the default configuration, and there is no CLI listener (`ca2a start`) since serving is via `ca2a_runtime.transport.server.serve`. Second, attestation on that run was one-directional: a follow-on cross-operator run (an Azure SEV-SNP peer calling a GCP Intel TDX peer, recorded in the same document) had the caller appraise the callee's real TDX quote before sealing, but the callee did not appraise the caller in return. Mutual simultaneous attestation is the remaining step, and both peers were driven by one operator's harness.
- **Sealed peer channel (hardware property).** The channel is implemented: a payload is sealed to the peer's attested X25519 key (X25519 ECDH, HKDF-SHA256, ChaCha20-Poly1305), and only the holder of the peer's private key can open it. On a live call the handshake now gates the seal on a channel key the caller has appraised, but in software mode that appraisal is `assurance="none"`. Until the seal is bound to a hardware-verified measurement (above), do not assume a payload is confined to a specific attested measurement. Adapter-decoded `sealed_payload` bytes are opaque ciphertext only.
- **Real hardware attestation.** The **SEV-SNP and Intel TDX verifiers now appraise genuine hardware evidence end to end**: a real Azure CVM SEV-SNP report (VCEK chain to the AMD ARK-Milan root, ECDSA-P384 report signature, measurement binding) and a real GCP C3 DCAP v4 TDX quote (PCK chain to the Intel SGX Root CA, QE binding, quote signature, MRTD binding), both fail-closed and both rejecting a tampered copy. Runs are recorded in [docs/hardware-validation.md](docs/hardware-validation.md). The **TPM 2.0 verifier** (AK chain to a caller-supplied vendor root, AK signature, magic/type, qualifying-data and PCR-digest binding) is implemented but synthetic-vector validated only. Quote *generation* still requires the respective hardware for all three. This validates the verifier, not a running attested peer: until the `verifier` seam in `ca2a_runtime.attestation` is driven off a live quote on a confidential VM, cA2A must not be described as attested across trust domains.

Expand Down
4 changes: 3 additions & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ Real hardware attestation verification (SEV-SNP VCEK chain, Intel TDX quote via
- **TDX verifier: landed and validated on real evidence.** DCAP Quote v4 parsing (including the nested type-6 QE certification data), PCK chain to the genuine Intel SGX Root CA, QE report signature, attestation-key binding, quote signature, and MRTD binding, all fail-closed, run against a genuine GCP C3 quote. Quote generation requires a real TDX guest. See `ca2a_verify.tdx`.
- **TPM 2.0 verifier: landed.** TPMS_ATTEST parsing, AK chain to a caller-supplied vendor root, AK signature (ECDSA or RSA), magic/type checks, and qualifying-data/PCR-digest binding, all fail-closed. Quote generation requires a real TPM. See `ca2a_verify.tpm`.
- **Cross-operator attestation (C6): validated in software.** A two-operator harness (SEV-SNP verifier + measurement pinning + sealed channel) shows independent keys, mutual attestation, confidential cross-operator delegation, and binary-swap detection. All six claims (C1-C6) are now validated experiments.
- **Pending:** the TPM signature path against a real quote, and a live attested peer (the `verifier` seam driven off a hardware quote on a running confidential VM). SEV-SNP and TDX appraisal of real evidence is done. The transport that parses A2A messages into a `PeerRequest` has **landed** (`ca2a_runtime.transport.a2a`), running in software mode; the hardware seam is the `verifier` callable in `ca2a_runtime.attestation`.
- **Live attested peer: landed.** The `verifier` seam has been driven off a real SEV-SNP quote on an Azure confidential VM, so `verify_offer` returned `assurance="hardware"` and a payload was sealed to a hardware-vouched channel key; measurement mismatch and stale nonce both rejected. See [docs/hardware-validation.md](docs/hardware-validation.md).
- **Cross-operator, cross-TEE run: landed.** An Azure SEV-SNP peer appraised a GCP Intel TDX peer's real quote, sealed a delegated task to the attested key, and the TDX enclave opened it, enforced the attenuated scope, allowed `tool:search` and refused `tool:purchase` with a denial record returned across the boundary. See [docs/hardware-validation.md](docs/hardware-validation.md).
- **Pending:** mutual simultaneous attestation (that run was one-directional) and the TPM certificate-chain path. TPM parsing, bindings and the AK signature are validated against a real Azure vTPM quote; SEV-SNP and TDX appraisal of real evidence is done. The transport that parses A2A messages into a `PeerRequest` has **landed** (`ca2a_runtime.transport.a2a`), running in software mode; the hardware seam is the `verifier` callable in `ca2a_runtime.attestation`.

## v1.0: Stable profile

Expand Down
133 changes: 119 additions & 14 deletions docs/hardware-validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,28 @@ and the run is recorded below.
|---|---|---|---|---|
| AMD SEV-SNP (Azure CVM, vTPM-rooted) | Yes | Yes, to the real AMD ARK-Milan root | Yes | **Yes**, 2026-07-27, capture of 2026-07-20 |
| Intel TDX (GCP C3, non-paravisor) | Yes | Yes, to the real Intel SGX Root CA | Yes | **Yes**, 2026-07-27, capture of 2026-07-21 |
| TPM 2.0 | Yes | Yes, to a caller-supplied vendor root | Yes | No. Synthetic vectors only |
| TPM 2.0 (Azure vTPM, Trusted Launch) | Yes | Yes, to a caller-supplied vendor root | Yes | **Partly**, 2026-07-27. Parse, bindings and AK signature yes; certificate chain no, see below |

## What these runs do and do not establish

They establish that the appraisal path in `ca2a_verify` accepts genuine evidence
from that silicon and fails closed on a tampered copy. They do **not** establish
that cA2A runs inside a TEE: quote *generation* still requires the hardware, and
the `verifier` seam in `ca2a_runtime.attestation` is not yet driven off a live
quote on a running peer. A live cA2A call is still software mode
(`assurance="none"`).

So the claim that is now true is "the cA2A verifier appraises real SEV-SNP and
TDX evidence." The claim that is still not true is "cA2A is attested across trust
domains." The second needs a peer whose channel key is bound to a hardware
measurement on a live confidential VM, which stays on the critical path in
[ROADMAP.md](../ROADMAP.md).
from that silicon and fails closed on a tampered copy.

As of 2026-07-27 the `verifier` seam in `ca2a_runtime.attestation` has also been
driven off a live SEV-SNP quote on a running confidential VM, so `verify_offer`
returned `assurance="hardware"` instead of `"none"` for the first time, and a
payload was sealed to a channel key that a hardware-verified measurement vouches
for. See the live-peer section below.

A cross-operator, cross-TEE-family run followed on the same day: an AMD SEV-SNP
peer on Azure calling an Intel TDX peer on GCP, each in a different cloud under a
different operator. See the cross-TEE section below.

Quote generation still requires the platform, so nothing running off a CVM is
attested, and the committed `examples/cross-operator-delegation` harness remains
software-attested with synthetic vectors: the hardware runs are recorded here
rather than shipped as fixtures, because the evidence embeds per-CPU
identifiers.

Verification is also bounded to a remote or rogue-admin adversary, not to one
with physical access: [TEE.fail](https://tee.fail) extracts attestation keys from
Expand Down Expand Up @@ -70,10 +76,109 @@ flat layout, so the tests validated the defect. Failure was closed, so this was
false negative rather than an unsound accept, but the TDX path had never worked
against real evidence. Synthetic self-consistency is not validation.

## Live attested peer on a SEV-SNP confidential VM

Run 2026-07-27 on a `Standard_DC2ads_v5` Azure CVM (Ubuntu 24.04 CVM image,
eastus). The guest reports `Detected confidential virtualization sev-snp` with
`SEV: Status: vTom` and no `/dev/sev-guest`, the expected paravisor shape.

Azure SEV-SNP is paravisor-mediated, so the guest cannot write `REPORT_DATA`
directly. The binding is therefore one hop longer than bare-metal SNP:

```
channel key + caller nonce -> AK-signed TPM quote (extraData)
vTPM AK -> SNP REPORT_DATA == sha256(runtime_data)
SNP report -> VCEK -> ASK -> ARK-Milan (AMD KDS)
```

Driving `ca2a_runtime.attestation`'s `verifier` seam off that chain:

| Result | Value |
|---|---|
| `assurance` | `hardware` (previously always `none`) |
| Verified measurement | matches the launch measurement in the live SNP report |
| Sealed payload round trip | opened inside the enclave, 102 bytes of ciphertext |
| Binary swap | rejected, "offered measurement does not match the verified report" |
| Stale nonce | rejected before any hardware work |

This is the item that gated dropping the software-mode caveat on the sealed
channel: the payload was sealed to a key that a hardware-verified measurement
vouches for, not to an unappraised key. The transcript and the bridge script are
kept out of the repository with the other captures.

What it is not: a two-operator run. One peer was attested, by a caller on the
same host. Two independently operated attested peers, ideally on different TEE
families, is the next step and is what the cross-operator claim needs.

## Cross-operator, cross-TEE: Azure SEV-SNP calling GCP TDX

Run 2026-07-27. Peer A is an Azure `Standard_DC2ads_v5` SEV-SNP CVM in eastus.
Peer B is a GCP `c3-standard-4` Intel TDX CVM (`tdx: Guest detected`,
configfs-TSM) in us-central1-a. Different clouds, different operators, unrelated
attestation formats: an AMD VCEK chain on one side, an Intel PCK chain on the
other. B served over HTTP with a firewall rule admitting only A's address.

Non-paravisor TDX is guest-controlled, so B binds its channel key into the quote
directly: `REPORTDATA = sha256(channel_pub || nonce)`, with the nonce chosen by A.

| Step | Result |
|---|---|
| A appraises B's TDX quote | 8,000 bytes, verified against the pinned Intel SGX Root CA |
| Channel key bound to the quote | Yes, `REPORTDATA` matched the recomputed binding |
| B's MRTD | `c1ee9c16e3afc506...` recorded by A |
| Stale nonce | Rejected; freshness is enforced, not assumed |
| Sealed delegated task | 107 bytes sealed by A, opened **inside B's TDX enclave** |
| Delegated capability `tool:search` | Allowed; effective scope `[task:read, tool:search]` |
| Over-scoped `tool:purchase` | Refused, HTTP 403 `SCOPE_NOT_PERMITTED` |
| Denial record | Returned across the trust boundary with `decision: deny`, the requested capability, the effective scope and the reason |

This is the run the cross-operator claim rested on. The four primitives held
simultaneously across a real trust boundary: attenuated delegation (B enforced a
scope narrowed by a chain B did not issue), peer attestation on real hardware
(A refused to seal until B's quote appraised), payload confidentiality (the task
was readable only inside B's measured guest), and provenance (both the allow and
the deny produced linked records).

What it still is not: both peers were driven by one operator's harness, and B's
appraisal of A's SNP report was not exercised in this run, so the attestation was
one-directional. Mutual simultaneous attestation is the remaining step.

## TPM 2.0, Azure Trusted Launch vTPM

Evidence: a `TPMS_ATTEST` quote over PCRs 0-7 (SHA-256) from a `Standard_D2s_v7`
Ubuntu 24.04 VM with Trusted Launch, vTPM and secure boot enabled, with a fresh
32-byte nonce as qualifying data. The AK was created in-guest with
`tpm2_createak` (RSA, RSASSA, SHA-256).

What the run checks: `TPMS_ATTEST` parsing against a real blob (magic
`0xFF544347`, type `0x8018`), the qualifying-data binding equalling the nonce
byte for byte, a non-zero PCR digest matching what the TPM reported at quote
time, the AK's RSA PKCS#1 v1.5 SHA-256 signature over the attest blob, and
rejection of a single flipped bit.

```
CA2A_TPM_FIXTURE_DIR=<capture dir> pytest tests/unit/test_tpm.py
```

What it does **not** check, which matters: the AK certificate chain. Azure's
pre-provisioned AK certificate (read from vTPM NV index `0x01C101D0`, subject
`CN=<host>.TrustedVM.Azure.windows.net`, issuer `CN=Global Virtual TPM CA - 03`)
certifies a *different* key than the one that signed this quote, so it does not
verify against it, and the certificate carries no AIA extension, so its issuing
intermediate is not fetchable from it. Chain-to-vendor-root therefore remains
unexercised for TPM. This is exactly why the TPM verifier takes caller-supplied
trust roots rather than pinning one, unlike SEV-SNP (AMD ARK) and TDX (Intel SGX
Root CA); the shared `verify_cert_chain` those two use is the same code path and
is exercised against real vendor roots there.

Note also that this is a Hyper-V vTPM, which is what Azure confidential and
Trusted Launch VMs actually present, not a discrete TPM chip.

## Not yet validated

- **TPM 2.0**: implemented and synthetic-vector validated; needs a real AK-signed
quote plus a vendor AK certificate chain.
- **TPM certificate chain**: needs a quote signed by Azure's pre-provisioned AK
(the one its NV certificate covers) plus Microsoft's `Global Virtual TPM CA`
intermediate, which is not distributed with the certificate.
- **Live attested peer binding**: the handshake gating the sealed channel on a
verified channel key runs in software mode. Driving it off a real quote on a
confidential VM is the remaining hardware property, and the precondition for
Expand Down
18 changes: 9 additions & 9 deletions examples/rejection-with-proof/chain.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"chain": [
{
"credential_id": "cred-0-orchestrator",
"issuer": "667e9aa02b1e19e4ef9a24627e10b97e099532dff71a1065a343a6037a8e3c3a",
"subject": "2108c06f9eb264876fc88c0c79c0986177a87222a776ce1b0de7c24483084ea3",
"issuer": "30831ec7f2a3abdcc223d22dc89311e3e8870cfbea931149d50e07077d518e9b",
"subject": "3f542267fb293071807ea37ac01a1be5f4b4bd9823f10156f1c998545fb2abca",
"scope": [
"task:read",
"task:write",
Expand All @@ -12,31 +12,31 @@
],
"depth": 0,
"parent_id": null,
"signature": "bf10c51cda37517c28f20f66f9f216eade1fd49c766003f2343a7bf5a8b0d423b704f936dd55851a3debb2afb982dd8ba685dd18c171c3bb6a025442063cb20f"
"signature": "493358a97965be0a78a81cc832028dd494e65a16d7622842661080b08af7b800f4c71e267c4d2745485964504209f836f93d1923d10453ba6375427b68399803"
},
{
"credential_id": "cred-1-researcher",
"issuer": "2108c06f9eb264876fc88c0c79c0986177a87222a776ce1b0de7c24483084ea3",
"subject": "de5c05ea1d7946c8f2c6ffde727cc6ed0da7b906ff2e1219f5916a3cc372f46b",
"issuer": "3f542267fb293071807ea37ac01a1be5f4b4bd9823f10156f1c998545fb2abca",
"subject": "f327db8ed54be433ee43fec9f4c144d3f0d84555eb4654989c4458087e23b99d",
"scope": [
"task:read",
"tool:purchase",
"tool:search"
],
"depth": 1,
"parent_id": "cred-0-orchestrator",
"signature": "1702778d81160bc46f1044b1b628e28b010d9d45c0767c1d1ce50b9f4ebfb3fe954455805ed5e9fdf7d72cc8ec18bb83cd23966ba88d95de21fa94b8b4026f06"
"signature": "35ba31dccb83829c4c8f9f4215aeb19b49aafd0033aef1d5ebb876a8a3d0110797f6f5a013ab55171a969c4940a1288b1591d6a090edf5df6b0a8b4db8b08300"
},
{
"credential_id": "cred-2-retriever",
"issuer": "de5c05ea1d7946c8f2c6ffde727cc6ed0da7b906ff2e1219f5916a3cc372f46b",
"subject": "6eb31885c7ac3d745186ed36a2e77b754325a1ceba6f81b76994a36866d68f52",
"issuer": "f327db8ed54be433ee43fec9f4c144d3f0d84555eb4654989c4458087e23b99d",
"subject": "555d4389edcb44043216dbce5079bd61edaa5873f0674b8c57b4c7b79f441cd5",
"scope": [
"tool:search"
],
"depth": 2,
"parent_id": "cred-1-researcher",
"signature": "08999679fff8d3d0940fb94c085ed549511aa026bf9b702da91e8244387db3d86c50004e547950009cf5ce7bf7625bf67893583011097622bed1782de844cf0d"
"signature": "f134bd6fd0f55573c285dc42debaf2944360d060ded3596d2ba85f6a0a223cbd7d99a7a111cf358793f01ca2454f3d7e301021b3789f84b6e90553e726d34904"
}
]
}
14 changes: 7 additions & 7 deletions examples/rejection-with-proof/dag.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{
"record_id": "rec-0-orchestrator",
"credential_id": "cred-0-orchestrator",
"subject": "2108c06f9eb264876fc88c0c79c0986177a87222a776ce1b0de7c24483084ea3",
"subject": "3f542267fb293071807ea37ac01a1be5f4b4bd9823f10156f1c998545fb2abca",
"scope": [
"task:read",
"task:write",
Expand All @@ -15,31 +15,31 @@
{
"record_id": "rec-1-researcher",
"credential_id": "cred-1-researcher",
"subject": "de5c05ea1d7946c8f2c6ffde727cc6ed0da7b906ff2e1219f5916a3cc372f46b",
"subject": "f327db8ed54be433ee43fec9f4c144d3f0d84555eb4654989c4458087e23b99d",
"scope": [
"task:read",
"tool:purchase",
"tool:search"
],
"parent_record_hash": "2bb2e3f8b86b5874219bf2a5818bf576863daeb947b2d3461ea6eb6abd8622e2"
"parent_record_hash": "d2b7837dc31713ee5ce1adac414e787f88a42e83663ac4c333318127ff4606b5"
},
{
"record_id": "rec-2-retriever",
"credential_id": "cred-2-retriever",
"subject": "6eb31885c7ac3d745186ed36a2e77b754325a1ceba6f81b76994a36866d68f52",
"subject": "555d4389edcb44043216dbce5079bd61edaa5873f0674b8c57b4c7b79f441cd5",
"scope": [
"tool:search"
],
"parent_record_hash": "35a4fad0f11b40ed72e47239c3d844ddd262bc1d4fd439913cc5bd0d916b57ec"
"parent_record_hash": "3dac51b23f24cde0396bc9bc3ae0f06aaf7bf13c9e40800f24d499869beee8e4"
},
{
"record_id": "rec-denied-purchase",
"credential_id": "cred-2-retriever",
"subject": "6eb31885c7ac3d745186ed36a2e77b754325a1ceba6f81b76994a36866d68f52",
"subject": "555d4389edcb44043216dbce5079bd61edaa5873f0674b8c57b4c7b79f441cd5",
"scope": [
"tool:search"
],
"parent_record_hash": "56d85126ea94f17f88fe4d0b1df35c300b2f9f79a2dfbcae814be097616da7ab",
"parent_record_hash": "6b10750d625c1cff83b5868948eeb7a6ebdbbe427ce3ba2bc802c258c8cdc82d",
"decision": "deny",
"requested_capability": "tool:purchase",
"effective_scope": [
Expand Down
Loading