feat(policy): add stable RTMR1 signer anchor and revocation - #1035
haitaohuang wants to merge 8 commits into
Conversation
64a3f90 to
cf2bee7
Compare
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved correctness issues remain in signer-anchor binding, CRL-floor enforcement, and cumulative mapping updates.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds stable RTMR1 signer anchors, servTD signer-CRL enforcement, monotonic CRL floors, and cumulative TCB mapping support across runtime, tooling, migration, and emulator workflows.
Changes:
- Updates signer-anchor measurement and offline hash generation.
- Adds CRL validation, revocation checks, and CRL-number policy enforcement.
- Updates migration handling, collateral tooling, fixtures, build scripts, and configuration artifacts.
File summaries
| File | Reviewed change |
|---|---|
tools/servtd-collateral-generator/src/main.rs |
Collateral generator CLI updates |
tools/servtd-collateral-generator/src/build.rs |
Collateral serialization and build logic |
tools/servtd-collateral-generator/readme.md |
Generator usage documentation |
tools/migtd-hash/src/main.rs |
Hash tool CLI updates |
tools/migtd-hash/src/lib.rs |
TDINFO hashing and cumulative mapping updates |
tools/migtd-hash/Cargo.toml |
Tool package metadata |
src/policy/test/policy_v2/tcb_mapping.json |
Cumulative TCB mapping fixture |
src/policy/test/policy_v2/servtd_collateral.json |
ServTD collateral fixture |
src/policy/test/policy_v2/cert_chain/policy_issuer_chain.pem |
Policy issuer-chain fixture |
src/policy/src/v2/servtd_collateral.rs |
Collateral and one-hash mapping validation |
src/policy/src/v2/policy.rs |
Policy verification, CRL checks, and CRL floors |
src/policy/src/v2/mod.rs |
v2 policy module integration |
src/policy/src/v2/measurement.rs |
Canonical measurement and signer anchors |
src/policy/src/lib.rs |
Policy and event definitions |
src/migtd/src/spdm/spdm_rsp.rs |
Peer attestation response handling |
src/migtd/src/ratls/server_client.rs |
Authenticated RA-TLS certificate exchange |
src/migtd/src/migration/session.rs |
SERVTD extension transport |
src/migtd/src/migration/rebinding.rs |
Rebinding continuity handling |
src/migtd/src/migration/mod.rs |
Migration wire compatibility |
src/migtd/src/mig_policy.rs |
Peer policy and migration validation |
src/migtd/src/lib.rs |
MigTD module integration |
src/migtd/src/event_log.rs |
Policy-data event handling |
src/migtd/src/bin/migtd/main.rs |
Runtime policy measurements |
src/crypto/src/lib.rs |
Certificate and signer-chain validation |
src/crypto/src/crl.rs |
CRL parsing and verification |
sh_script/build_policy_v2.sh |
Policy build workflow |
sh_script/build_AzCVMEmu_policy_and_test.sh |
Emulator policy workflow |
sh_script/Azure/build_azure_mock_test.sh |
Azure mock policy workflow |
deps/td-shim-AzCVMEmu/tdx-tdcall/src/tdx_emu.rs |
TDX emulator support |
config/templates/td_identity.json |
TD Identity template |
config/templates/td_identity_signed.json |
Signed TD Identity template |
config/templates/tcb_mapping.json |
TCB mapping template |
config/templates/tcb_mapping_signed.json |
Signed TCB mapping template |
config/templates/servtd_collateral.json |
ServTD collateral template |
config/templates/policy_issuer_chain.pem |
Policy issuer-chain template |
config/AzCVMEmu/tcb_mapping.json |
Emulator TCB mapping |
Cargo.lock |
Dependency lockfile updates |
Review details
Suppressed comments (4)
src/policy/src/v2/policy.rs:558
servtd_crl_numis evaluated only throughGlobalPolicy, but both rebinding directions call the policy evaluators withskip_global = true. Consequently the new CRL floor is enforced for ordinary migration but silently skipped during rebinding, allowing a peer policy with a regressed CRL number in that path. Apply this floor separately when global platform checks are skipped, or stop skipping this constraint.
if let Some(property) = &self.servtd_crl_num {
let servtd_crl_num = value.servtd_crl_num.ok_or(PolicyError::CrlEvaluation)?;
if !property.evaluate_integer(servtd_crl_num, relative_reference.servtd_crl_num)? {
return Err(PolicyError::CrlEvaluation);
}
}
src/policy/src/v2/policy.rs:255
- Although this PR adds CRL authentication and revocation, the v2 policy tests do not exercise
servtd_crlwith a valid CRL, a revoked leaf/intermediate, or an issuer mismatch. Because this path controls signer authorization, add fixture-backed tests covering acceptance and fail-closed rejection before relying on the implementation.
if let Some(crl) = servtd_crl.as_deref() {
crypto::verify_signer_chain_not_revoked(issuer_chain, crl.as_bytes())
.map_err(|_| PolicyError::SignerRevoked)?;
crypto::verify_signer_chain_not_revoked(
servtd_identity_issuer_chain.as_bytes(),
tools/migtd-hash/src/lib.rs:263
Measurementsexplicitly accepts the legacytdinfoHashspelling, but this cumulative updater only readstdinfo_hash. Updating a legacy mapping therefore fails with a missing-field error instead of retaining its history, even though the policy parser supports that artifact. Read both spellings here.
tools/servtd-collateral-generator/src/main.rs:31- This removes the
--mapping-chainoption, butdoc/policy_v2.md:42still uses that option and describes the output as containing both issuer chains. Following the repository's policy-v2 instructions now fails with an unknown-argument error; update that documentation or retain a compatibility alias.
- Files reviewed: 36/41 changed files
- Comments generated: 3
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
Question: Is there any new test to validate the new revocation path?
|
|
On Implicit constraint: the same CRL-issuing CA must be common to both chains it's checked against. |
|
Comment: |
cf2bee7 to
7ea4a3a
Compare
|
Added documentation |
7ea4a3a to
fa8cc95
Compare
Added comments and fail close if CRL is missing. |
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved critical and moderate findings remain in Azure identity generation, SPDM element counts, and CRL contract compatibility.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (4)
src/migtd/src/spdm/spdm_req.rs:1025
- This rebind request likewise emits only four elements, but
VDM_MESSAGE_EXCHANGE_REBIND_ATTEST_INFO_REQ_WITH_HISTORY_INFO_ELEMENT_COUNTremains 5 and the responder enforces that value. A peer following the element-count contract can reject the rebind request; use the actual v2 count of 4 for this opcode.
src/migtd/src/spdm/spdm_req.rs:446 - Under
policy_v2, theTdReportInitelement is compiled out below, so this request contains four elements (quote, event log, policy hash, andSERVTD_EXT), but the header still advertises the shared count of 5. The responder also validates that value, so this is internally masked but produces a malformed request for any peer that validates the count against the actual payload. Define a policy-v2 request count of 4 (and retain 5 only for the legacy form).
src/policy/src/v2/servtd_collateral.rs:35 - The PR description says the authenticated servTD CRL is optional, but this new non-optional
Stringmakes serde reject every v2 collateral that omitsservtdCrl; the updated generator also requires--servtd-crl. If the CRL is intentionally mandatory, please align the PR contract and compatibility notes; otherwise make the field optional and condition enforcement on its presence.
tools/servtd-collateral-generator/src/build.rs:18 - The PR description calls the servTD signer CRL optional, but making this field a required
Stringmeans any collateral withoutservtdCrlnow fails deserialization (and the CLI also requires--servtd-crl). Please reconcile the implementation with the stated compatibility contract, or update the PR description and migration requirements if all v2 policies are intentionally required to carry a CRL.
- Files reviewed: 64/69 changed files
- Comments generated: 2
- Review effort level: Lite
fa8cc95 to
26ac92d
Compare
26ac92d to
8b4e793
Compare
Use SHA-384 over the complete unmasked TDINFO as the Policy v2 mapping key. Canonicalize policyData once with only the circular mapping removed so runtime verification and offline tooling extend identical RTMR2 bytes. Verify the signed mapping with the RTMR1-bound policy issuer chain, remove the separate mapping chain and obsolete mapping identity fields, and ignore the legacy outer policy signature as required by the proposal. Resolve the initial SVN through the source's authenticated JSON mapping and take its current SVN from the authenticated quote or TDREPORT evaluation. This allows an older destination to accept a newer source release without predicting its hash. Fail closed on missing authenticated SVN evidence and prevent the SERVTD_EXT current hash from overriding it. Signed-off-by: Haitao Huang <haitaohuang@microsoft.com> Assisted-by: GitHub Copilot CLI:GPT-5.6 Sol Assisted-by: GitHub Copilot CLI:gpt-6-astra
Carry authenticated SERVTD_EXT continuity evidence through migration and rebinding. Bind the peer policy issuer chain to attested RTMR1, reject lookup misses or SVN rollback, and fail closed when SERVTD_ATTR masking makes the endorsed unmasked hashes inapplicable. Match policy-v2 migration and rebinding request headers to their four encoded elements while retaining the five-element policy-v1 migration format. Signed-off-by: Haitao Huang <haitaohuang@microsoft.com> Assisted-by: GitHub Copilot CLI:GPT-5.6 Sol Assisted-by: GitHub Copilot CLI:gpt-6-astra
Retain authority-maintained hash history when adding a release, allow multiple hashes at one SVN, and reject conflicting duplicate assignments. Emit deterministic mapping bytes and validate signed mappings before use. Update the policy-v2 guide and mapping-update example for the cumulative workflow, including the required --mapping-isvsvn argument. Document mapping signer authority, trust assumptions, and the distinction between release-SVN continuity and independent policy-SVN ordering. Signed-off-by: Haitao Huang <haitaohuang@microsoft.com> Assisted-by: GitHub Copilot CLI:GPT-5.6 Sol Assisted-by: GitHub Copilot CLI:gpt-6-astra
Consume the signed identity used to build the measured release instead of re-signing it after recording tdinfo_hash. Reject any non-mapping policy change before replacing the final policy outputs, including identity, issuer-chain, and platform collateral changes. Document preparation before measurement, immutable release inputs, and a final image hash comparison against the recorded endorsement. Assisted-by: GitHub Copilot CLI:gpt-6-astra Signed-off-by: Haitao Huang <haitaohuang@microsoft.com>
Remove check_engine_not_older and its dedicated test because runtime continuity uses the MigTD helper instead. Preserve the equal-SVN assertion in the live continuity tests alongside the existing upgrade, downgrade, and lookup-failure coverage. Signed-off-by: Haitao Huang <haitaohuang@microsoft.com> Assisted-by: GitHub Copilot CLI:gpt-6-astra
Measure the policy signer as a stable root-certificate plus leaf-subject anchor in both runtime and offline hashing. Require authenticated, numbered servTD CRLs so omission cannot bypass signer-chain enforcement at initialization or peer validation, and support a monotonic servtd CRL policy floor.\n\nPort only the proposal-specific implementation from ms/integration while retaining tcbmapping's existing JSON mapping and identity model. Assisted-by: GitHub Copilot CLI:GPT-5.6 Sol Signed-off-by: Haitao Huang <haitaohuang@microsoft.com> Assisted-by: GitHub Copilot CLI:gpt-6-astra
Exercise signed empty CRLs, revoked leaf and intermediate certificates, issuer mismatches, invalid signatures, and CA constraints. Cover policy rejection and peer checks against verifier-owned revocation state, including a rotated local policy signer. Use public fixtures without retaining private keys. Extract the existing peer revocation block without changing its ordering or logging so tests can use independently verified policies without global state. Add serde_json only as a dev dependency for test policy construction. Signed-off-by: Haitao Huang <haitaohuang@microsoft.com> Assisted-by: GitHub Copilot CLI:gpt-6-astra
Accept only complete, direct, issuer-wide CRLs signed by the signing leaf\x27s immediate non-root CA with explicit cRLSign permission. Scope serial lookups to the signing leaf, authenticate peer CRL metadata under the same issuer as the local authoritative CRL, and keep peer revocation entries out of local decisions. Reject delta, partitioned, indirect, reason-scoped, malformed and unsupported critical inputs while retaining non-critical Microsoft metadata. Leave Intel platform CRL parsing unrestricted. Cover signed negative fixtures and update mock issuers and the supported-profile documentation. Assisted-by: GitHub Copilot CLI:gpt-6-astra Signed-off-by: Haitao Huang <haitaohuang@microsoft.com>
8b4e793 to
8ed42d0
Compare
Summary
Implements the RTMR1 signer-anchor proposal in #916 as a continuation of #1032:
migtd-hash;servtdCrlNumpolicy floor so peers cannot regress revocation state.The full policy issuer chain remains enrolled in CFV and recorded in the event payload. This changes only the RTMR1 measurement input and signer-revocation enforcement. The existing JSON TCB mapping and TD Identity model from #1032 are retained.
This PR is stacked on #1032 and is intended as its focused continuation.
Validation
Follow-up work
See the future-work section in #1032 for the broader roadmap. Remaining related items identified while checking the TCB-mapping and signer-anchor work are:
x5chainmaterial, apply the local servTD CRL to it, and consume authenticated peer CoRIM mappings during migration;nbfandexpclaims because MigTD does not have a trusted wall clock;