Skip to content

docs(tee): correct the Azure vTPM trust-anchor claim after hardware measurement - #75

Merged
imran-siddique merged 1 commit into
mainfrom
docs/tpm-hardware-findings
Aug 1, 2026
Merged

docs(tee): correct the Azure vTPM trust-anchor claim after hardware measurement#75
imran-siddique merged 1 commit into
mainfrom
docs/tpm-hardware-findings

Conversation

@imran-siddique

Copy link
Copy Markdown
Contributor

Ran the TPM path on a real Azure Trusted Launch vTPM (Standard_D2s_v7, eastus2, 2026-08-01) to validate what #74 shipped. Most of it holds up. One claim I wrote in #74 does not, so this corrects it.

Docs only. The verifier behaved correctly throughout; there is no code change.

What passed on hardware

Check Result
detect() on a vTPM host True
attest() produces a quote 145B attest, 262B TPMT_SIGNATURE, 1403B chain
Platform AK path used (not the transient fallback) shipped certificate certifies the quoting key
parse_tpmt_signature on real TPM output RSASSA 0x0014 / SHA-256 0x000b, 256B bare signature
Bare signature verifies against the shipped AK pass, RSA PKCS#1 v1.5 / SHA-256
Tampered attest blob rejected pass
extraData == derived key-and-nonce binding pass
Chunked NV read 994B certificate read without incident

The binding line is the one that matters most, since it is the core claim of #74: the key really is committed inside the signed structure on real hardware, not just in synthetic vectors.

What failed, and why it matters

verify_tpm_report could not chain to the pinned root:

AttestationFailed: TPM quote verification failed
  detail: TpmVerificationError: AK chain root is not among the supplied trusted TPM roots

On this host the AK certificate at NV 0x01C101D0 is:

  • 994 bytes, not 1596
  • issued by CN=Global Virtual TPM CA - 03, a different hierarchy than the pinned chain
  • carrying no AIA extension at all

With no AIA there is nothing to walk, so _chain_from_leaf correctly returns the leaf alone. tpm2_getcap handles-nv-index showed only 0x01C101D0, so the intermediates are not stored elsewhere in NV either. Chained verification is therefore impossible on that host, and failing closed is the right outcome, since key provenance genuinely cannot be established.

The earlier observation is also real: a Standard_D2s_v5 in eastus presented a 1596-byte certificate under Azure Cloud Virtual TPM CA - 11 with a walkable AIA chain reaching Azure Virtual TPM Root Certificate Authority 2023. Azure runs more than one vTPM CA generation.

So the claim I wrote in #74, that AZURE_VTPM_ROOT_2023_PEM is "the one root cA2A has validated on hardware", is wrong as a general statement. The constant stays, now documented as one observed hierarchy rather than a guarantee, with the fleet variance recorded in the module, LIMITATIONS.md, and the attestation spec. A deployment must pin the hierarchy its own hosts actually present.

One caveat retired

LIMITATIONS.md previously said collector and verifier could not run in one process, because installing agent-manifest conflicts with the distribution's tpm2_pytss. Building tpm2-pytss from source inside a venv fixes it: pytss + agent-manifest + cryptography 49.0.0 coexisted and both halves ran in one interpreter. The blocker on that host is the missing certificate chain, not tooling.

Net effect on the TPM tier

Evidence is genuine, and its signature and binding are verifiable. Key provenance is host-dependent. That is a weaker claim than #74 implied and a more useful one to hold.

🤖 Generated with Claude Code

…easurement

Ran the TPM path on a real Azure Trusted Launch vTPM (Standard_D2s_v7, eastus2,
2026-08-01). Most of what #74 shipped holds up on hardware. One thing I wrote
there does not.

tpm_roots.py described AZURE_VTPM_ROOT_2023_PEM as "the one root cA2A has
validated on hardware". That is not true fleet-wide. On this host the AK
certificate at NV 0x01C101D0 is 994 bytes, is issued by
CN=Global Virtual TPM CA - 03, and carries no AIA extension at all. With nothing
to walk, the collector ships the leaf alone; tpm2_getcap handles-nv-index showed
only 0x01C101D0, so the intermediates are not in NV either. No chain reaches the
pinned root and verify_tpm_report fails closed with "AK chain root is not among
the supplied trusted TPM roots", which is the correct outcome since key
provenance genuinely cannot be established there.

The earlier observation (Standard_D2s_v5, eastus, 1596-byte certificate under
Azure Cloud Virtual TPM CA - 11 with a walkable AIA chain to that root) is also
real. Azure runs more than one vTPM CA generation, so a deployment must pin the
hierarchy its own hosts present rather than assuming this one.

What passed on hardware, now recorded rather than hedged: the collector produced
a genuine platform-AK quote, the shipped certificate certifies the quoting key,
parse_tpmt_signature unwrapped a real TPMT_SIGNATURE (RSASSA/SHA-256) and the
bare signature verified against the shipped key, a tampered attest blob was
rejected, and extraData equalled the derived key-and-nonce binding.

Also retires the caveat that collector and verifier could not run in one
process: building tpm2-pytss from source inside a venv resolves the conflict
with agent-manifest's cryptography. The blocker on that host is the missing
chain, not tooling.

No code change; the verifier behaved correctly throughout.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@imran-siddique
imran-siddique merged commit 76c1a31 into main Aug 1, 2026
12 checks passed
@imran-siddique
imran-siddique deleted the docs/tpm-hardware-findings branch August 1, 2026 04:39
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