Conversation
This was referenced Sep 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Six audit findings on the verifier's result contract — what a relying party can safely read off a response — worked to a conclusion: four confirmed, two refuted. Several are documentation and pinning rather than behaviour, which is the right outcome for a contract.
cargo test -p dstack-verifier -p dstack-attest -p dstack-types: all green.cargo clippy --all-targetson each: clean, except one pre-existingitems after a test modulewarning indstack-attest(verified present onorigin/nextby stashing).The one that reproduces on
nextTDX-lite lets a requester mint an
os_image_hashand still getos_image_hash_verified: trueTook the real
tdx-lite-attestation.jsonfixture, appended one valid line to thechecksum_fileit carries, setos_image_hash = sha256(new manifest), re-encoded. Onorigin/nextthat returns:with nothing in the response distinguishing it from the legacy path, where the hash is checked against a downloaded image.
This is self-consistent by design — it is only meaningful because the caller allowlists
os_image_hash— but the two paths' guarantees differ by an entire trust anchor and a caller could not tell which one ran. The new testtdx_lite_os_image_hash_is_anchored_to_the_requesters_own_documentwas run verbatim (minus the new assertion) in a scratch worktree atorigin/next: it passes, i.e. the mint reproduces.Added
os_image_hash_anchor: Option<OsImageHashAnchor>—published_image|measurement_document|quoted_pcrs— filled from one exhaustiveos_image_hash_anchor()that matches on the same two thingsverify_os_image_hashdispatches on, so a new platform variant fails the build in both places rather than silently defaulting.Compatibility checked rather than assumed: default
None⇒null; the only consumer in the tree that models the response isct_monitor/src/main.rs:56and it has nodeny_unknown_fields; no SDK models it at all (the KMS anddstack-authdo their own verification), so there is no four-language parity obligation.Also confirmed
os_image_is_devis only knowable on the legacy path, and the README said otherwiseverification.rs:944is the only assignment. I checked what a dev image actually differs by:is_devexists only inmetadata.json, and no measurement document carries it —Tdx/Sev/Gcp/AwsOsImageMeasurementDocumentall carrychecksum_file, which bindsmetadata.json's digest, not its contents. So it is not knowable on any self-contained path, and "populate it everywhere" would mean addingmetadata.jsonto the measured material.The README was wrong — it listed only GCP TDX and Nitro Enclave as the null cases, while TDX-lite (the primary path today), SEV-SNP and AWS are all null too. So a relying party following its advice to "reject dev images" silently had no signal.
Corrected in both the prose and the two inline samples, with the replacement advice stated: allowlist
os_image_hash— a dev image is a different image with a different hash.self_contained_paths_report_no_os_image_metadatakeeps README and code from drifting apart again.A malformed
vm_configwas reported as a measurement failureNote the empty hash in the URL — and that an all-defaults
VmConfigalso silently selectsLegacy.require_declared_os_image_hashnow rejects an empty or non-32-byteos_image_hashwithvm_config declares no os_image_hash. Verified all five paths use a 32-byte sha256, and that the SNP nested-config fallback cannot diverge for any shape the VMM emits.The measurement cache had no eviction
store_measurements_in_cachewrites one file per VM shape and nothing removes it — including when the MRs then fail to match.A cap, not a TTL: entries never go stale, since a shape deterministically yields one measurement set, so the only reason to drop one is space.
MEASUREMENT_CACHE_MAX_ENTRIES = 1024(~400 B/entry ≈ 400 KB, far above any real deployment), evicting oldest-by-mtime, filtering on.jsonso a concurrentNamedTempFileis untouched, and best-effort — a prune failure is not a verification failure. Composes with #1251: touches onlystore_measurements_in_cache, notvm_config_cache_key.Refuted
report_datadomain tags are a parsing convention, not a capabilityThe claim was that an in-CVM app minting a quote with
report_data = sha512("ratls-cert:" || <any SPKI>)forges a key-possession proof. Four links break it:tdx_quote/prefixquirk.DstackGuest.GetQuote,Attestand v1Attestall take the 64 bytes verbatim throughpad64— arbitraryreport_datais the documented primitive, not a hole in one method.app_id,compose_hash,instance_idand the MRs come from the system-owned RTMR3 log, andEmitEventwas removed in 0.6.0 with the message "runtime RTMR3 events are system-owned and cannot be extended by apps".ratls-cert:as possession also forces key use.ra-rpc/client.rs:157androcket_helper.rs:548verify against a completed handshake's key; the KMS'ssign_certrunscsr.verify(&signature)beforeverify_with_ra_pubkey.kms-root-ca:buys nothing. No code in the tree verifies that tag; it is produced once bydstack-util gen-ca-certinside the KMS CVM, and a minted one carries the minter's identity. Minting one with the KMS's identity means being inside the KMS CVM — a single trust domain.Net: an app can only re-prove what
GetTlsKey/IssueCertalready give it.report_data_domain_tags_are_reproducible_by_any_callerpins thatCustom("ratls-cert") ≡ RaTlsCertbyte-for-byte and that"raw"passes content through, so the tag is demonstrably not a boundary. Newsecurity-model.mdsection plus a verification-checklist item. No guest-agent code touched, so no overlap with #1256.deny_unknown_fieldswould break the documented workflowVerificationRequest:verifier/README.mdtells callers tocurl .../GetQuote -o quote.jsonand post it verbatim — and theGetQuoteresponse carriesreport_data. Denying unknowns rejects the documented workflow.VmConfig: a forward-compatible wire type flowing VMM → guest → KMS/verifier, whose own doc comments say fields are "absent on images built before this landed". Denying unknowns makes a newer VMM's config unverifiable by an older verifier — failing closed on an honest deployment.Both pinned with tests; the README now states that extra fields are ignored. The other half of that finding — the misleading error — is fixed above.
TCB is surfaced, not gated — confirmed as documented, and now pinned
validate_tcbnever readsreport.status, and dcap-qvl 0.5.3's only gate isTcbStatus::is_valid(), false forRevokedalone. So/verifyreturnsis_valid: trueforOutOfDate, exactly assecurity-model.md:370says. The gap was that nothing pinned it, so a dcap-qvl bump could change it silently.Extracted
CvmVerifier::verify_attestedas a seam, then two matrices: all sevenTcbStatusvalues throughvalidate_tcb, andtcb_status_is_surfaced_without_changing_is_valid, which drives a real verified TDX attestation through the whole result path with only the status varied and pins the exactis_valid/tcb_status/boot_info.tcbStatustriple per status — including""→null, which fails closed. The README now says outright thatis_validignores it.Merge safety
git merge-treeagainst #1205, #1238, #1249, #1251, #1252 and #1256: all six merge clean. I also built and rancargo test -p dstack-verifier -p dstack-attest -p dstack-typeson the merged trees for #1251 (the big overlap), #1238 (whose GCP hunk sits next to the new precondition), #1205 and #1249 — all green. The README edit near theos_image_is_devsample sits close to #1251'sevent_log_verifiedcomment hunk; merge-tree confirms no conflict.