Follow-up from cmcp#447 (retiring the triplicated TPM verifier) and ca2a#73.
The gap
verify_tpm_quote takes a bare signature. But the format real TPM tooling emits is TPMT_SIGNATURE: that is what tpm2_quote -s writes and what tpm2-pytss signature.marshal() returns. agent-manifest models TPMS_ATTEST (parse_tpm_quote) and the chain plus signature verification, but nothing unwraps the signature envelope.
So every consumer that collects a quote from actual hardware has to parse TPMT_SIGNATURE itself before it can call the canonical verifier:
cmcp_verify/tpm.py has parse_tpmt_signature (added in cmcp#448)
ca2a_verify/tpm.py now has its own (ca2a#74)
That is the same wire-format parser in two places, which is a smaller version of exactly the problem cmcp#447 set out to retire. Both were written against the same spec and both are load-bearing: without them the canonical verifier cannot be called on real evidence at all.
Proposal
Export parse_tpmt_signature(blob) -> ParsedSignature (fields: sig_alg, hash_alg, signature) covering the three schemes a quote can use:
TPM_ALG_RSASSA (0x0014) and TPM_ALG_RSAPSS (0x0016): size-prefixed TPM2B_PUBLIC_KEY_RSA
TPM_ALG_ECDSA (0x0018): two size-prefixed integers, R then S, re-encoded as a DER sequence so cryptography verifies it directly
Optionally, accept a marshalled TPMT_SIGNATURE directly in verify_tpm_quote by sniffing the envelope, which would let callers pass hardware output through untouched.
Worth noting while this is open: verify_tpm_quote currently hardcodes SHA-256 for both the ECDSA and RSA paths, and RSA is PKCS#1 v1.5 only. Azure vTPM signs RSASSA/SHA-256 so that combination is validated, but a quote using RSAPSS or a SHA-384 digest would not verify. If TPMT_SIGNATURE parsing lands here, the hash_alg it carries is the natural way to select the digest instead of assuming one.
Sequencing
Not urgent, and deliberately not blocking: ca2a#74 shipped with a local copy so the collector work was not gated on a release here. When this lands, both repos drop their copies and bump their floor.
/cc @imran-siddique
Follow-up from cmcp#447 (retiring the triplicated TPM verifier) and ca2a#73.
The gap
verify_tpm_quotetakes a bare signature. But the format real TPM tooling emits isTPMT_SIGNATURE: that is whattpm2_quote -swrites and what tpm2-pytsssignature.marshal()returns. agent-manifest modelsTPMS_ATTEST(parse_tpm_quote) and the chain plus signature verification, but nothing unwraps the signature envelope.So every consumer that collects a quote from actual hardware has to parse
TPMT_SIGNATUREitself before it can call the canonical verifier:cmcp_verify/tpm.pyhasparse_tpmt_signature(added in cmcp#448)ca2a_verify/tpm.pynow has its own (ca2a#74)That is the same wire-format parser in two places, which is a smaller version of exactly the problem cmcp#447 set out to retire. Both were written against the same spec and both are load-bearing: without them the canonical verifier cannot be called on real evidence at all.
Proposal
Export
parse_tpmt_signature(blob) -> ParsedSignature(fields:sig_alg,hash_alg,signature) covering the three schemes a quote can use:TPM_ALG_RSASSA(0x0014) andTPM_ALG_RSAPSS(0x0016): size-prefixedTPM2B_PUBLIC_KEY_RSATPM_ALG_ECDSA(0x0018): two size-prefixed integers, R then S, re-encoded as a DER sequence socryptographyverifies it directlyOptionally, accept a marshalled
TPMT_SIGNATUREdirectly inverify_tpm_quoteby sniffing the envelope, which would let callers pass hardware output through untouched.Worth noting while this is open:
verify_tpm_quotecurrently hardcodes SHA-256 for both the ECDSA and RSA paths, and RSA is PKCS#1 v1.5 only. Azure vTPM signs RSASSA/SHA-256 so that combination is validated, but a quote using RSAPSS or a SHA-384 digest would not verify. IfTPMT_SIGNATUREparsing lands here, thehash_algit carries is the natural way to select the digest instead of assuming one.Sequencing
Not urgent, and deliberately not blocking: ca2a#74 shipped with a local copy so the collector work was not gated on a release here. When this lands, both repos drop their copies and bump their floor.
/cc @imran-siddique