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
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `ca2a-runtime`: config, error registry, and delegation credential model
- `ca2a_runtime.provenance`: linked delegation-record DAG with tamper and reparent detection, bound to authority via `cross_check_chain`
- `experiments/`: reproducible claim suite C1-C6. C1 (attenuation), C2 (cross-chain replay), and C5 (provenance DAG) are fully reproducible; C3, C4, C6 SKIP until their Tier 2/3 dependency lands. Each claim has a CI test.
- SEV-SNP attestation backend (Tier 3): `ca2a_runtime.tee.sev_snp` (report parsing, `SevSnpProvider`) and `ca2a_verify.sev_snp` (VCEK chain verification, ECDSA-P384 report-signature verification, measurement/report-data binding), all fail-closed. Chain path validated against the real AMD Milan root; report-signature path validated with synthetic vectors. Report generation requires a real SEV-SNP guest.
- Repository scaffold: governance, CI/CD, docs framework, and packaging at parity with the agentrust-io house standard

### Not yet implemented

- Runtime peer-delegation enforcement (Tier 2, see ROADMAP.md)
- Sealed peer channel (Tier 2)
- Real hardware attestation backends (Tier 3, shared critical path with cmcp)
- Intel TDX and TPM attestation backends (Tier 3); end-to-end SEV-SNP validation against real hardware vectors

[Unreleased]: https://github.com/agentrust-io/ca2a/commits/main
2 changes: 1 addition & 1 deletion LIMITATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ cA2A is a pre-release profile in active design. This document states plainly wha

- **Runtime peer-delegation enforcement.** The runtime does not yet accept a delegation credential on a live inbound peer call, verify it in the request path, and intersect the delegated scope with a local Cedar policy. This is Tier 2 on the roadmap.
- **Sealed peer channel.** Payloads are not yet sealed to a peer's attested measurement. Until this lands, do not send confidential task payloads across a trust boundary and assume they are protected.
- **Real hardware attestation.** Attestation verification fails closed and the hardware signature/quote step is not implemented (SEV-SNP VCEK chain, Intel TDX quote via QVL/PCS, TPM AK cert + checkquote). Until at least one real backend lands, cA2A must not be described as attested across trust domains. This is Tier 3 and is a shared critical path with cmcp.
- **Real hardware attestation.** The **SEV-SNP verifier is implemented**: report parsing, VCEK certificate chain verification, ECDSA-P384 report-signature verification, and measurement/report-data binding, all fail-closed. The chain path is validated against the genuine AMD Milan root chain; the report-signature path is validated with synthetic vectors, since a real report plus VCEK pair needs SEV-SNP hardware. Report generation (`SevSnpProvider.attest`) still requires a real SEV-SNP guest. **Intel TDX and TPM backends are not yet implemented (Tier 3).** Until a backend verifies a real quote end to end against a golden measurement on hardware, cA2A must not be described as fully attested across trust domains.

## Out of scope

Expand Down
3 changes: 3 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ Already implemented and tested elsewhere; cA2A depends on it rather than reimple

Real hardware attestation verification (SEV-SNP VCEK chain, Intel TDX quote via QVL/PCS, TPM AK cert + checkquote). This is a dependency for any cross-operator trust claim, single-agent or multi-agent, and is shared with cmcp. At least one real hardware backend must land before cA2A is marketed as attested across trust domains, so the demo matches the claim.

- **SEV-SNP verifier: landed.** Report parsing, VCEK chain verification (validated against the real AMD Milan root), ECDSA-P384 report-signature verification, and measurement/report-data binding, all fail-closed. Report generation still requires a real SEV-SNP guest. See `ca2a_verify.sev_snp` and [docs/spec/attestation.md](docs/spec/attestation.md).
- **Pending:** Intel TDX and TPM backends; end-to-end validation of the report-signature path against real hardware vectors; then unblock claim C6 (cross-operator attestation).

## v1.0: Stable profile

- Stable delegation credential and TRACE link schema with documented versioning guarantees
Expand Down
14 changes: 12 additions & 2 deletions docs/spec/attestation.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,24 @@ An `AttestationReport` carries `platform`, `measurement`, the bound `public_key`
| Provider | Platform | Status |
|---|---|---|
| `software-only` | none | Available; for development and CI. Reports `platform: software-only`, never a hardware platform string. |
| `sev-snp` | AMD SEV-SNP | Verifier implemented (see below). Report generation requires a real SEV-SNP guest. |
| `tpm` | TPM 2.0 / vTPM | Tier 3, not yet implemented |
| `sev-snp` | AMD SEV-SNP | Tier 3, not yet implemented |
| `tdx` | Intel TDX | Tier 3, not yet implemented |
| `opaque` | OPAQUE Confidential Runtime | Tier 3, explicit opt-in, not auto-selected |

## SEV-SNP verification

`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.

**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`).

## Fail closed

Hardware providers `detect()` to False until their backend lands, so they are never selected automatically, and verification fails closed when evidence is absent. This is deliberate: cA2A must not be described as attested across trust domains until at least one real hardware backend verifies a quote. See [LIMITATIONS.md](../../LIMITATIONS.md).
Providers without a backend `detect()` to False, so they are never selected automatically, and verification fails closed when evidence is absent or invalid. This is deliberate: cA2A must not be described as attested across trust domains until a real hardware backend verifies a quote against a golden measurement. TDX and TPM backends remain Tier 3. See [LIMITATIONS.md](../../LIMITATIONS.md).

## Why this is the critical path

Expand Down
3 changes: 2 additions & 1 deletion src/ca2a_runtime/tee/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
"""

from ca2a_runtime.tee.base import AttestationReport, BaseProvider
from ca2a_runtime.tee.sev_snp import SevSnpProvider, SevSnpReport

__all__ = ["AttestationReport", "BaseProvider"]
__all__ = ["AttestationReport", "BaseProvider", "SevSnpProvider", "SevSnpReport"]
105 changes: 105 additions & 0 deletions src/ca2a_runtime/tee/sev_snp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
"""AMD SEV-SNP attestation report parsing and the SEV-SNP provider.

This module parses the SEV-SNP ``ATTESTATION_REPORT`` structure (1184 bytes,
AMD SEV-SNP ABI) and exposes the fields the cA2A verifier appraises: the launch
measurement, the report data (which binds the runtime key and nonce), and the
ECDSA-P384 signature over the report body. The verification and certificate
chain appraisal live in :mod:`ca2a_verify.sev_snp`.

Producing a report requires a real SEV-SNP guest (``/dev/sev-guest``), so
:meth:`SevSnpProvider.attest` fails closed off hardware. The verifier does not
need hardware and is exercised against the real AMD root certificate chain plus
synthetic report vectors in the test suite.
"""

from __future__ import annotations

import struct
from dataclasses import dataclass

from ca2a_runtime.errors import AttestationFailed, AttestationUnsupported
from ca2a_runtime.tee.base import AttestationReport, BaseProvider

# Layout of the SEV-SNP ATTESTATION_REPORT (offsets in bytes).
REPORT_SIZE = 0x4A0 # 1184
SIG_OFFSET = 0x2A0 # signature covers report[:SIG_OFFSET]
REPORT_DATA_OFFSET = 0x50
REPORT_DATA_LEN = 64
MEASUREMENT_OFFSET = 0x90
MEASUREMENT_LEN = 48
# ECDSA-P384 signature: r then s, each in a 72-byte little-endian field.
SIG_COMPONENT_LEN = 72
SIG_ALGO_ECDSA_P384_SHA384 = 1

SEV_GUEST_DEVICE = "/dev/sev-guest"


@dataclass(frozen=True)
class SevSnpReport:
"""The parsed subset of a SEV-SNP attestation report cA2A appraises."""

version: int
guest_svn: int
policy: int
vmpl: int
signature_algo: int
measurement: bytes
report_data: bytes
raw: bytes

@property
def signed_body(self) -> bytes:
"""The bytes the report signature is computed over."""
return self.raw[:SIG_OFFSET]

@property
def signature_rs(self) -> tuple[int, int]:
"""The (r, s) ECDSA signature components, decoded from little-endian."""
r = int.from_bytes(self.raw[SIG_OFFSET : SIG_OFFSET + SIG_COMPONENT_LEN], "little")
s = int.from_bytes(
self.raw[SIG_OFFSET + SIG_COMPONENT_LEN : SIG_OFFSET + 2 * SIG_COMPONENT_LEN],
"little",
)
return r, s

@classmethod
def parse(cls, blob: bytes) -> SevSnpReport:
"""Parse a raw report, raising AttestationFailed on any malformed input."""
if len(blob) < REPORT_SIZE:
raise AttestationFailed(
"SEV-SNP report too short",
detail=f"got {len(blob)} bytes, need at least {REPORT_SIZE}",
)
version, guest_svn, policy = struct.unpack_from("<IIQ", blob, 0)
(vmpl,) = struct.unpack_from("<I", blob, 0x30)
(signature_algo,) = struct.unpack_from("<I", blob, 0x34)
report_data = blob[REPORT_DATA_OFFSET : REPORT_DATA_OFFSET + REPORT_DATA_LEN]
measurement = blob[MEASUREMENT_OFFSET : MEASUREMENT_OFFSET + MEASUREMENT_LEN]
return cls(
version=version,
guest_svn=guest_svn,
policy=policy,
vmpl=vmpl,
signature_algo=signature_algo,
measurement=measurement,
report_data=report_data,
raw=bytes(blob[:REPORT_SIZE]),
)


class SevSnpProvider(BaseProvider):
"""AMD SEV-SNP provider. Report generation requires a real SEV-SNP guest."""

platform = "sev-snp"

@classmethod
def detect(cls) -> bool:
import os

return os.path.exists(SEV_GUEST_DEVICE)

def attest(self, public_key: str, nonce: str) -> AttestationReport:
raise AttestationUnsupported(
"SEV-SNP report generation requires a real SEV-SNP guest",
detail=f"{SEV_GUEST_DEVICE} not present; run on an AMD SEV-SNP confidential VM",
)
103 changes: 103 additions & 0 deletions src/ca2a_verify/sev_snp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
"""Offline appraisal of an AMD SEV-SNP attestation report.

Appraisal has three parts, all fail-closed:

1. Certificate chain: the VCEK is verified up to a trusted AMD root (ARK) via
ARK (self-signed) -> ASK -> VCEK. The chain-verification path is exercised
against the real AMD root chain in the test suite.
2. Report signature: the ECDSA-P384 report signature is verified against the
VCEK public key over the report body.
3. Binding: the launch measurement and the report data (which carries the
runtime key and nonce) are checked against expected values.

Any missing or mismatched step raises AttestationFailed. The verifier needs no
hardware.
"""

from __future__ import annotations

from cryptography import x509
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric.utils import encode_dss_signature
from cryptography.hazmat.primitives.hashes import SHA256, SHA384

from ca2a_runtime.errors import AttestationFailed
from ca2a_runtime.tee.sev_snp import SEV_GUEST_DEVICE, SIG_ALGO_ECDSA_P384_SHA384, SevSnpReport

__all__ = ["SEV_GUEST_DEVICE", "verify_cert_chain", "verify_sev_snp_report"]


def verify_cert_chain(
chain: list[x509.Certificate], trusted_roots: list[x509.Certificate]
) -> None:
"""Verify a leaf-to-root certificate chain against a set of trusted roots.

``chain`` is ordered leaf first (VCEK), root last (ARK). Each certificate
must be directly issued by the next, and the final certificate must match a
trusted root by fingerprint. Raises AttestationFailed on any failure.
"""
if not chain:
raise AttestationFailed("empty certificate chain")

for i in range(len(chain) - 1):
child, issuer = chain[i], chain[i + 1]
try:
child.verify_directly_issued_by(issuer)
except (ValueError, TypeError, InvalidSignature) as exc:
raise AttestationFailed(
f"certificate at position {i} is not validly issued by the next",
detail=str(exc),
) from exc

root = chain[-1]
trusted = {c.fingerprint(SHA256()) for c in trusted_roots}
if root.fingerprint(SHA256()) not in trusted:
raise AttestationFailed(
"chain root is not a trusted AMD root",
detail=root.subject.rfc4514_string(),
)


def verify_sev_snp_report(
report_bytes: bytes,
vcek_chain: list[x509.Certificate],
*,
trusted_roots: list[x509.Certificate],
expected_measurement: bytes | None = None,
expected_report_data: bytes | None = None,
) -> SevSnpReport:
"""Appraise a SEV-SNP report offline. Raises AttestationFailed on any failure.

``vcek_chain`` is ordered leaf (VCEK) first, root (ARK) last.
"""
report = SevSnpReport.parse(report_bytes)

if report.signature_algo != SIG_ALGO_ECDSA_P384_SHA384:
raise AttestationFailed(
"unsupported report signature algorithm",
detail=f"algo={report.signature_algo}, expected {SIG_ALGO_ECDSA_P384_SHA384}",
)

verify_cert_chain(vcek_chain, trusted_roots)

vcek_key = vcek_chain[0].public_key()
if not isinstance(vcek_key, ec.EllipticCurvePublicKey):
raise AttestationFailed("VCEK does not carry an elliptic-curve public key")

r, s = report.signature_rs
der_sig = encode_dss_signature(r, s)
try:
vcek_key.verify(der_sig, report.signed_body, ec.ECDSA(SHA384()))
except InvalidSignature as exc:
raise AttestationFailed("SEV-SNP report signature failed to verify") from exc

if expected_measurement is not None and report.measurement != expected_measurement:
raise AttestationFailed(
"measurement does not match the expected value",
detail=f"got {report.measurement.hex()}",
)
if expected_report_data is not None and report.report_data != expected_report_data:
raise AttestationFailed("report data does not match the expected binding")

return report
74 changes: 74 additions & 0 deletions tests/fixtures/sev_snp/amd_milan_cert_chain.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIGYzCCBBKgAwIBAgIDAQAAMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAIC
BQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBMHsxFDAS
BgNVBAsMC0VuZ2luZWVyaW5nMQswCQYDVQQGEwJVUzEUMBIGA1UEBwwLU2FudGEg
Q2xhcmExCzAJBgNVBAgMAkNBMR8wHQYDVQQKDBZBZHZhbmNlZCBNaWNybyBEZXZp
Y2VzMRIwEAYDVQQDDAlBUkstTWlsYW4wHhcNMjAxMDIyMTcyMzA1WhcNNDUxMDIy
MTcyMzA1WjB7MRQwEgYDVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDAS
BgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5j
ZWQgTWljcm8gRGV2aWNlczESMBAGA1UEAwwJQVJLLU1pbGFuMIICIjANBgkqhkiG
9w0BAQEFAAOCAg8AMIICCgKCAgEA0Ld52RJOdeiJlqK2JdsVmD7FktuotWwX1fNg
W41XY9Xz1HEhSUmhLz9Cu9DHRlvgJSNxbeYYsnJfvyjx1MfU0V5tkKiU1EesNFta
1kTA0szNisdYc9isqk7mXT5+KfGRbfc4V/9zRIcE8jlHN61S1ju8X93+6dxDUrG2
SzxqJ4BhqyYmUDruPXJSX4vUc01P7j98MpqOS95rORdGHeI52Naz5m2B+O+vjsC0
60d37jY9LFeuOP4Meri8qgfi2S5kKqg/aF6aPtuAZQVR7u3KFYXP59XmJgtcog05
gmI0T/OitLhuzVvpZcLph0odh/1IPXqx3+MnjD97A7fXpqGd/y8KxX7jksTEzAOg
bKAeam3lm+3yKIcTYMlsRMXPcjNbIvmsBykD//xSniusuHBkgnlENEWx1UcbQQrs
+gVDkuVPhsnzIRNgYvM48Y+7LGiJYnrmE8xcrexekBxrva2V9TJQqnN3Q53kt5vi
Qi3+gCfmkwC0F0tirIZbLkXPrPwzZ0M9eNxhIySb2npJfgnqz55I0u33wh4r0ZNQ
eTGfw03MBUtyuzGesGkcw+loqMaq1qR4tjGbPYxCvpCq7+OgpCCoMNit2uLo9M18
fHz10lOMT8nWAUvRZFzteXCm+7PHdYPlmQwUw3LvenJ/ILXoQPHfbkH0CyPfhl1j
WhJFZasCAwEAAaN+MHwwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSFrBrRQ/fI
rFXUxR1BSKvVeErUUzAPBgNVHRMBAf8EBTADAQH/MDoGA1UdHwQzMDEwL6AtoCuG
KWh0dHBzOi8va2RzaW50Zi5hbWQuY29tL3ZjZWsvdjEvTWlsYW4vY3JsMEYGCSqG
SIb3DQEBCjA5oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZI
AWUDBAICBQCiAwIBMKMDAgEBA4ICAQC6m0kDp6zv4Ojfgy+zleehsx6ol0ocgVel
ETobpx+EuCsqVFRPK1jZ1sp/lyd9+0fQ0r66n7kagRk4Ca39g66WGTJMeJdqYriw
STjjDCKVPSesWXYPVAyDhmP5n2v+BYipZWhpvqpaiO+EGK5IBP+578QeW/sSokrK
dHaLAxG2LhZxj9aF73fqC7OAJZ5aPonw4RE299FVarh1Tx2eT3wSgkDgutCTB1Yq
zT5DuwvAe+co2CIVIzMDamYuSFjPN0BCgojl7V+bTou7dMsqIu/TW/rPCX9/EUcp
KGKqPQ3P+N9r1hjEFY1plBg93t53OOo49GNI+V1zvXPLI6xIFVsh+mto2RtgEX/e
pmMKTNN6psW88qg7c1hTWtN6MbRuQ0vm+O+/2tKBF2h8THb94OvvHHoFDpbCELlq
HnIYhxy0YKXGyaW1NjfULxrrmxVW4wcn5E8GddmvNa6yYm8scJagEi13mhGu4Jqh
3QU3sf8iUSUr09xQDwHtOQUVIqx4maBZPBtSMf+qUDtjXSSq8lfWcd8bLr9mdsUn
JZJ0+tuPMKmBnSH860llKk+VpVQsgqbzDIvOLvD6W1Umq25boxCYJ+TuBoa4s+HH
CViAvgT9kf/rBq1d+ivj6skkHxuzcxbk1xv6ZGxrteJxVH7KlX7YRdZ6eARKwLe4
AFZEAwoKCQ==
-----END CERTIFICATE-----
Loading
Loading