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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Azure confidential-VM attestation (`cmcp_runtime.tee.azure_cvm.AzureCVMProvider` + `cmcp_verify.azure_cvm`), **hardware-validated on live Azure SEV-SNP silicon**. Azure runs SNP behind a Hyper-V paravisor with no `/dev/sev-guest`; the SNP report is read from the vTPM NV index `0x01400001` and the guest cannot control `REPORT_DATA` (the paravisor binds the vTPM AK there). cMCP's nonce (`jwk_thumbprint || audit-root`) is therefore committed into an AK-signed TPM quote's qualifying data, with the AK rooted in silicon via the SNP report (`REPORT_DATA == sha256(runtime_data)`) and the VCEK→ASK→ARK chain (reusing `cmcp_verify.sev_snp`). Auto-detected first (before TPM/SEV-SNP) since Azure exposes no `/dev/sev-guest`. Maps to the `amd-sev-snp` platform and is distinguished at verify time by its evidence envelope, so no new platform identifier is required.
- Azure confidential-VM attestation (`cmcp_runtime.tee.azure_cvm.AzureCVMProvider` + `cmcp_verify.azure_cvm`), **hardware-validated on live Azure SEV-SNP silicon**. Azure runs SNP behind a Hyper-V paravisor with no `/dev/sev-guest`; the SNP report is read from the vTPM NV index `0x01400001` and the guest cannot control `REPORT_DATA` (the paravisor binds the vTPM AK there). cMCP's nonce (`jwk_thumbprint || audit-root`) is therefore committed into an AK-signed TPM quote's qualifying data, with the AK rooted in silicon via the SNP report (`REPORT_DATA == sha256(runtime_data)`) and the VCEK→ASK→ARK chain (reusing `cmcp_verify.sev_snp`). Auto-detected first (before TPM/SEV-SNP) since Azure exposes no `/dev/sev-guest`. Carries its own `runtime.platform` value `azure-cvm-sev-snp` (requires `agentrust-trace>=0.4`) so a consumer keying on `runtime.platform` knows the root of trust is vTPM-rooted, not a guest-controlled SNP `report_data`.
- `tool_transcript.entries`: privacy-preserving per-call view in the TRACE Claim (one entry per tool call with `tool_name`, `data_class` from the catalog, and the policy `decision`), derived from the audit chain so no raw parameters or response bodies are exposed. `tool_transcript.hash` continues to bind the full transcript to the audit-chain tip. Adds `transcript_entries_hash()` for offline recomputation. (#126)

## [0.3.0] - 2026-06-30
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ classifiers = [
]
requires-python = ">=3.11"
dependencies = [
"agentrust-trace>=0.1",
"agentrust-trace>=0.4",
"agent-manifest>=0.1.1",
"cryptography>=42.0",
"pyyaml>=6.0",
Expand Down
9 changes: 5 additions & 4 deletions src/cmcp_runtime/audit/trace_claim.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@

_PROVIDER_MAP: dict[str, str] = {
"sev-snp": "amd-sev-snp",
# Azure CVM is SEV-SNP (vTPM-rooted behind a paravisor); it maps to the same
# canonical platform. The verifier distinguishes it by evidence shape (a JSON
# envelope vs a raw SNP report), so no new platform literal is required.
"azure-cvm-sev-snp": "amd-sev-snp",
# Azure CVM is SEV-SNP behind a Hyper-V paravisor (vTPM-rooted, not
# direct-silicon). It has its own canonical platform value (agentrust-trace
# >=0.4) so a consumer keying on runtime.platform knows the root of trust is
# vTPM-rooted rather than a guest-controlled SNP report_data.
"azure-cvm-sev-snp": "azure-cvm-sev-snp",
"tdx": "intel-tdx",
"opaque": "intel-tdx",
"tpm": "tpm2",
Expand Down
24 changes: 4 additions & 20 deletions src/cmcp_verify/verify.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def _jwk_thumbprint_sha256(x_b64url: str) -> bytes:

_KNOWN_PLATFORMS = {
"amd-sev-snp",
"azure-cvm-sev-snp",
"intel-tdx",
"tpm2",
"nvidia-h100",
Expand All @@ -64,22 +65,6 @@ def _jwk_thumbprint_sha256(x_b64url: str) -> bytes:
}


def _is_azure_cvm_evidence(raw_evidence_b64: str | None) -> bool:
"""True if raw_evidence is the Azure CVM JSON envelope (vs a raw SNP report).

The AzureCVMProvider emits a JSON object ({"v":1,...}); a bare-metal SNP
report is a binary blob starting with its version field. Base64 of a JSON
object begins with 'ey' (i.e. '{"'); this cheap prefix check routes the
amd-sev-snp platform to the vTPM-rooted verifier without a new platform id.
"""
if not raw_evidence_b64:
return False
try:
return base64.b64decode(raw_evidence_b64)[:1] == b"{"
except Exception: # noqa: BLE001
return False


def _is_software_only(runtime: dict[str, Any]) -> bool:
"""True for dev-mode (non-attested) records.

Expand Down Expand Up @@ -810,14 +795,13 @@ def verify_trace_claim(
details["tpm_failure"] = tpm_result.failure_reason
unverified.extend(tpm_result.unverified_fields)
details.update(tpm_result.details)
elif platform == "amd-sev-snp" and _is_azure_cvm_evidence(_runtime.get("raw_evidence")):
elif platform == "azure-cvm-sev-snp":
# Azure confidential VM: SEV-SNP behind a Hyper-V paravisor, vTPM-rooted.
# The guest cannot control SNP REPORT_DATA (the paravisor binds the vTPM AK
# there), so cMCP's nonce is committed into an AK-signed TPM quote's extraData
# and the AK is rooted in silicon by the SNP report (REPORT_DATA ==
# sha256(runtime_data)). Distinguished from a bare-metal SNP report by the JSON
# evidence envelope AzureCVMProvider emits. Chain travels in the envelope; the
# ARK is pinned out of band. Hardware-validated on live Azure SEV-SNP.
# sha256(runtime_data)). The VCEK chain travels in the JSON evidence envelope;
# the ARK is pinned out of band. Hardware-validated on live Azure SEV-SNP.
from cmcp_verify.azure_cvm import verify_azure_cvm_measurement

raw_bytes = base64.b64decode(_runtime["raw_evidence"])
Expand Down