Skip to content

feat(tee): hardware-validated Azure CVM (vTPM-rooted SEV-SNP) attestation#407

Merged
imran-siddique merged 2 commits into
mainfrom
feat/azure-cvm-attestation
Jul 21, 2026
Merged

feat(tee): hardware-validated Azure CVM (vTPM-rooted SEV-SNP) attestation#407
imran-siddique merged 2 commits into
mainfrom
feat/azure-cvm-attestation

Conversation

@imran-siddique

Copy link
Copy Markdown
Contributor

What

Adds Azure confidential-VM attestation to cMCP, validated end to end on a live Azure SEV-SNP VM (DCasv5), matching the treatment just landed in agent-manifest.

Why the design is what it is

cMCP's collector assumed the classic model: open /dev/sev-guest, ioctl a report, and write its nonce (jwk_thumbprint(gateway_key) || sha256(audit_root), i.e. CRYPTO-001 + AUDIT-006) into the guest-controlled SNP REPORT_DATA. None of that holds on Azure:

  • Azure runs SEV-SNP behind a Hyper-V paravisor — there is no /dev/sev-guest. The SNP report is read from the vTPM NV index 0x01400001 ("HCLA" wrapper, SNP report at offset 0x20).
  • The guest cannot control REPORT_DATA. The paravisor sets it to sha256(runtime_data), binding the vTPM AK. Confirmed on the live report: REPORT_DATA[:32] == sha256(runtime_data) exactly.

So on Azure cMCP's nonce must be committed via the vTPM, not the SNP report.

What's in the PR

  • AzureCVMProvider (collector, cmcp_runtime/tee/azure_cvm.py): reads the SNP/HCL report from the vTPM, verifies the AK-to-silicon binding, and commits the nonce into an AK-signed TPM2_Quote (qualifying data). Auto-detected first in detect.py (Azure exposes no /dev/sev-guest).
  • verify_azure_cvm_measurement (cmcp_verify/azure_cvm.py): verifies SNP report signature + VCEK→ASK→ARK to a pinned ARK (reusing cmcp_verify.sev_snp), the REPORT_DATA == sha256(runtime_data) binding, that the AK equals the HCLAkPub in runtime_data, and that the AK-signed quote's extraData commits the nonce. Fully fail-closed.
  • Azure maps to the existing amd-sev-snp platform and is distinguished at verify time by its JSON evidence envelope — no new platform literal in agentrust-trace, so no cross-package release is needed. The bare-metal SNP path is unchanged.

Verified on real silicon

nonce → AK-signed quote (extraData) → AK == HCLAkPub bound in SNP REPORT_DATA → SNP report signed by VCEK → VCEK ← ASK ← ARK (AMD Milan root) — all six checks green on the live VM.

Two real bugs the hardware surfaced (and fixed): the Azure vTPM rejects a 64-byte quote qualifyingData (TPM_RC_SIZE) → commit sha256(nonce); tpm2_quote -m emits a raw TPMS_ATTEST with no outer TPM2B size prefix.

Tests

  • Synthetic self-consistent path (SNP report + RSA ASK/ARK chain + EC VCEK + RSA AK-signed quote) covering happy path, tampered quote nonce, wrong AK, broken paravisor binding, missing chain → partial, plus a monkeypatched collector↔verifier round-trip.
  • Env-gated real-hardware fixture test (CMCP_AZURE_FIXTURE_DIR); the real report/chip_id is not committed.
  • Local: ruff clean, mypy clean, full unit suite 813 passed / 6 skipped.

🤖 Generated with Claude Code

imran-siddique and others added 2 commits July 20, 2026 21:36
…tion

Validated end to end on a live Azure SEV-SNP VM (DCasv5). Azure runs SNP behind
a Hyper-V paravisor: there is no /dev/sev-guest and the guest cannot control SNP
REPORT_DATA (the paravisor sets it to sha256(runtime_data), binding the vTPM AK).
cMCP's existing collector assumed a guest-controlled REPORT_DATA, so it could not
attest on Azure.

- Add AzureCVMProvider (collector): reads the SNP/HCL report from vTPM NV
  0x01400001, confirms REPORT_DATA == sha256(runtime_data), and commits cMCP's
  nonce into an AK-signed TPM quote (qualifying data). Auto-detected first.
- Add cmcp_verify.azure_cvm: verifies the SNP report signature + VCEK<-ASK<-ARK
  chain (reusing cmcp_verify.sev_snp), the AK-to-silicon binding, that the AK
  equals HCLAkPub, and that the quote's extraData commits the nonce. Fail-closed.
- Azure maps to the amd-sev-snp platform and is distinguished at verify time by
  its evidence envelope, so no new platform literal (agentrust-trace) is needed.
- Chain: nonce -> AK-signed quote -> AK == HCLAkPub in SNP REPORT_DATA ->
  SNP report signed by VCEK -> VCEK <- ASK <- ARK. Every link validated on hw.

Real bugs found on hardware: the vTPM caps quote qualifyingData below 64 bytes
(commit sha256(nonce) instead); tpm2_quote emits a raw TPMS_ATTEST with no
TPM2B size prefix. Tests: synthetic self-consistent path + an env-gated
real-vector test (CMCP_AZURE_FIXTURE_DIR); real chip_id not committed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Match cMCP's existing nosec convention for the tpm2 subprocess calls (B603/B607),
the fixed-host AMD KDS urlopen (B310), and the TPM hash-alg id map that lists
SHA1 (B303, identification only). No behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant