Skip to content

consolidate TPM quote verification onto agent-manifest instead of three divergent copies #447

Description

@imran-siddique

The same TPM quote verification primitive exists three times across the stack, at three different assurance levels.

agent-manifest cmcp ca2a
Lines 205 ~350 98
Structural parse Yes Yes Yes
Signature verified Yes (RSA + ECDSA) Yes, since #444 No
AK chain + pinned root Yes, SHA-256 fingerprint No (#431) No
PCR digest binding Yes Partial No
Collects a quote verifier only Yes No PCR read at all

ca2a/src/ca2a_runtime/tee/tpm.py is parse-only: no pcr_read, no quote, no signature check, no AK. So cA2A has no working TPM attestation today.

This was already designed

The 0.5.0 changelog says the verification API was generalized "so cmcp and ca2a can delegate their full SNP/TDX/TPM crypto to this package (via PyPI) without changing behavior or rewriting their test fixtures."

The pieces are all in place and were never used for TPM:

  • verify_tpm_quote and parse_tpm_quote are in __all__ of the released agent-manifest 0.7.0.
  • cmcp already declares agent-manifest>=0.6.1; cA2A declares >=0.5.

No new agent-manifest release is required.

Proven against real hardware

agent_manifest.verify_tpm_quote was run against a genuine Azure Trusted Launch vTPM quote (Standard_D2s_v5, eastus, manufacturer MSFT), with the platform AK certificate from NV 0x01C101D0 and the full chain fetched over AIA:

CN=<vm-id>.TrustedVM.Azure.windows.net
  CN=Azure Cloud Virtual TPM CA - 11
    CN=Azure Cloud Virtual TPM CA 2025
      CN=Azure Virtual TPM Root Certificate Authority 2023, O=Microsoft Corporation  (self-signed)

Results with that root pinned:

full verify        : True
wrong nonce        : False
tampered attest    : False
untrusted root     : rejected (TpmVerificationError)

So #431 needs no new cryptographic code. Delegating gets signature verification and a pinned chain at once.

Plan

  1. cmcp verifier: parse the TPMT_SIGNATURE wire format locally (agent-manifest expects a raw signature), then delegate all crypto and chain verification to agent_manifest.verify_tpm_quote. Drop cmcp's own signature verification added in feat(verify): verify the TPM quote signature, validated on real vTPM hardware #444, now duplicate. Ship the Azure vTPM root as a pinned trust anchor.
  2. cmcp runtime: read the AK certificate from NV 0x01C101D0 and quote with persistent handle 0x81000003 when present, falling back to a transient key otherwise, and carry the chain on the report.
  3. cA2A: delegate to the same function, retiring the 98-line stub.

Closes #431 as a side effect of step 1 and 2. Split into per-repo PRs so each is hardware-validated independently.

Why it matters beyond tidiness

Every bug fixed this week (#434 corrupted measurement, #430 dead quote path, #436 dishonest tiering, #429 unverified signature) was in cmcp's private copy. agent-manifest's implementation had none of them. One verifier means one set of properties and one place to audit.

Metadata

Metadata

Assignees

No one assigned

    Labels

    attestationTEE / hardware attestationsecurityThreat model, attack surface, OWASPspecSpecification or design decisiontrack:teeTEE provider implementation

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions