feat(tee): hardware-validated Azure CVM (vTPM-rooted SEV-SNP) attestation#407
Merged
Conversation
…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>
This was referenced Jul 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 SNPREPORT_DATA. None of that holds on Azure:/dev/sev-guest. The SNP report is read from the vTPM NV index0x01400001("HCLA" wrapper, SNP report at offset0x20).REPORT_DATA. The paravisor sets it tosha256(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-signedTPM2_Quote(qualifying data). Auto-detected first indetect.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 (reusingcmcp_verify.sev_snp), theREPORT_DATA == sha256(runtime_data)binding, that the AK equals theHCLAkPubin runtime_data, and that the AK-signed quote'sextraDatacommits the nonce. Fully fail-closed.amd-sev-snpplatform and is distinguished at verify time by its JSON evidence envelope — no new platform literal inagentrust-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) → commitsha256(nonce);tpm2_quote -memits a rawTPMS_ATTESTwith no outerTPM2Bsize prefix.Tests
CMCP_AZURE_FIXTURE_DIR); the real report/chip_idis not committed.ruffclean,mypyclean, full unit suite 813 passed / 6 skipped.🤖 Generated with Claude Code