Skip to content

fix(cli): let verifiers pin a TPM CA bundle - #548

Open
noah-ing wants to merge 1 commit into
agentrust-io:mainfrom
noah-ing:feat/tpm-trust-anchor-cli
Open

fix(cli): let verifiers pin a TPM CA bundle#548
noah-ing wants to merge 1 commit into
agentrust-io:mainfrom
noah-ing:feat/tpm-trust-anchor-cli

Conversation

@noah-ing

Copy link
Copy Markdown
Contributor

What

Add --trusted-tpm-ca FILE to cmcp verify. The command reads and parse-checks a non-empty PEM X.509 certificate bundle, then supplies those exact caller-controlled bytes to the existing trusted_tpm_ca_pem verifier input.

The option, tests, and documentation are explicitly TPM 2.0-only. This change does not add AMD SEV-SNP or Intel TDX CLI trust inputs.

Why

The TPM quote-verification path merged in #469 requires a verifier-owned CA bundle, but only Python callers could provide it. A CLI caller could supply a complete signed TPM claim and still had no way to authenticate its attestation-key chain, leaving the claim partially verified.

This is a narrow usability follow-up to #469 and the now-closed #370. It is separate from #520/#453: that work concerns producer-side assembly and hardware validation of one Azure vTPM certificate hierarchy, while this change exposes the already-existing verifier-side trust input.

Security impact

Positive, narrowly scoped trust-boundary wiring. The trust bundle remains an explicit, verifier-controlled input; cMCP does not trust a root carried by the claim and does not silently select a bundled default. Unreadable or malformed bundle input is rejected before claim verification.

Only trusted_tpm_ca_pem is passed. The CLI does not set trusted_ark_pem or trusted_intel_root_pem, does not change TPM chain or quote validation, and does not change how AMD SEV-SNP or Intel TDX claims are evaluated.

Test plan

  • pytest tests/unit/ -v --tb=short --cov=src — 1,240 passed, 6 skipped, 1 xfailed; 85.06% coverage
  • ruff check src/ tests/ passes
  • mypy src/cmcp_runtime/ src/cmcp_verify/ passes — 63 source files
  • bandit -r src/ -c pyproject.toml passes — no findings
  • pip-audit --skip-editable --ignore-vuln CVE-2026-69247 passes — no known vulnerabilities, one repository-documented ignore
  • Manual test performed: a complete signed synthetic TPM claim verifies through the real CLI when its CA is supplied with --trusted-tpm-ca
  • Strict documentation build passes
  • Wheel and source distribution build; twine check passes for both artifacts
  • AGT governance verification passes — OWASP ASI 10/10, runtime evidence 6/6

Focused regressions verify that malformed PEM is rejected before verification, valid bundle bytes reach trusted_tpm_ca_pem exactly, unrelated platform trust inputs are omitted, and CLI help keeps the option TPM-only.

DCO sign-off

@noah-ing
noah-ing requested a review from a team as a code owner August 23, 2026 00:08
@github-actions

github-actions Bot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

🟡 Contributor Check: MEDIUM

Check Result
Profile MEDIUM
Credential LOW
Overall MEDIUM

Automated check by AgenTrust Contributor Check.

@github-actions github-actions Bot added the needs-review:MEDIUM Contributor check flagged MEDIUM risk label Aug 23, 2026
@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

imran-siddique
imran-siddique previously approved these changes Aug 23, 2026

@imran-siddique imran-siddique left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Read the CLI change rather than the test plan. Approving.

The bundle is passed through as the caller's own bytes. _load_tpm_ca_bundle parses with load_pem_x509_certificates purely as a validity gate and then returns the file contents unchanged, rather than returning the parsed certificates re-serialized. That is the right call for a pinned trust anchor: the verifier compares against exactly what the operator put on disk, and a round trip through the parser is one more place for the bytes to change without anyone noticing.

Rejection happens before verification, and the empty case is covered. Unreadable file, non-PEM content, and a syntactically valid file containing zero certificates all raise ClickException before verify is reached. The zero-certificate case is the one usually missed, because load_pem_x509_certificates returns an empty list rather than raising, and an empty bundle would otherwise read as "no CA pinned" instead of as an error.

Scope holds. Only trusted_tpm_ca_pem is set. trusted_ark_pem and trusted_intel_root_pem stay unset, chain and quote validation are untouched, and the help text keeps the option TPM-only so nobody reaches for it on an SEV-SNP or TDX claim.

Non-blocking, and you already scoped it out correctly against #453, but worth recording on the thread for whoever uses this on Azure: this works for one of the two vTPM AK certificate hierarchies Azure issues concurrently. The Global Virtual TPM CA - 03 chain carries no AIA extension, so no pinned root makes it chain, and that is a producer-side problem this option cannot solve. Separating that from the verifier-side input was the right boundary to draw.

@imran-siddique

Copy link
Copy Markdown
Member

Approved, and it will merge as soon as this is rebased. #546 landed a few minutes after I reviewed and put a conflict in your way that has nothing to do with your change.

Rebase onto 717064b. CHANGELOG.md is the only conflicting file, and the resolution is to keep both bullets: #546's catalog-approval schema entry and your --trusted-tpm-ca entry are unrelated additions that happen to land at the same insertion point. Nothing else in your branch touches anything #546 touched.

I resolved it locally before asking you to, so this is verified rather than assumed:

The approval stands, so no re-review is needed. Push the rebase and it goes in.

Signed-off-by: Noah Ingwers <98993329+noah-ing@users.noreply.github.com>
@noah-ing

Copy link
Copy Markdown
Contributor Author

Rebased onto 717064b and kept both CHANGELOG.md bullets. I confirmed the merged tree is still exactly the six intended files (+133/-3), the editable install resolves cmcp_runtime from this checkout, and the 67 targeted tests pass. All hosted checks are green again. Thanks for checking the conflict locally and making the resolution so clear.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-review:MEDIUM Contributor check flagged MEDIUM risk

Projects

None yet

Development

Successfully merging this pull request may close these issues.

security: verify attestation report signatures (VCEK / DCAP / EK)

3 participants