From ba9757391b3070f694547d9b68e31bb1f673f86a Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Tue, 16 Jun 2026 20:09:17 -0700 Subject: [PATCH 1/2] mkimage: emit digest.sev.txt (AMD SEV-SNP os_image_hash) When the SEV firmware is shipped, run `dstack-vmm sev-os-image-hash` over the output image to produce digest.sev.txt: the AMD SEV-SNP os_image_hash computed from the SEV firmware (ovmf-sev.fd) + kernel/initrd/cmdline/rootfs. Unlike the TDX digest.txt (a content hash that includes the TDX firmware and is vCPU-independent by construction), the SEV os_image_hash is the image-invariant projection of the SNP launch measurement, so it matches what the KMS verifier derives at attestation time. Gracefully skipped (with a warning) when the SEV firmware is absent or the dstack-vmm tool is not available; build.sh's build_host stage builds it to the repo root. The tool path can be overridden with DSTACK_VMM_BIN. Verified: digest.sev.txt is produced and equals the dstack-vmm output and the KMS-derived os_image_hash (cross-checked by a unit test in dstack). --- mkimage.sh | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/mkimage.sh b/mkimage.sh index b83c5c1..d4c8cfc 100755 --- a/mkimage.sh +++ b/mkimage.sh @@ -300,6 +300,28 @@ sha256sum ovmf.fd bzImage initramfs.cpio.gz metadata.json > sha256sum.txt sha256sum sha256sum.txt | awk '{print $1}' > digest.txt popd +# digest.sev.txt: the AMD SEV-SNP os_image_hash. Unlike the TDX digest.txt +# (a content hash that includes the TDX firmware), this is computed by +# dstack-vmm from the SEV firmware (ovmf-sev.fd) + kernel/initrd/cmdline/rootfs +# and matches the os_image_hash the KMS verifier derives from a launch +# measurement. Only emitted when the SEV firmware shipped and the tool exists. +HAVE_DIGEST_SEV=0 +if [ "$HAVE_OVMF_SEV" = "1" ]; then + if [ -z "${DSTACK_VMM_BIN:-}" ]; then + for c in "$SCRIPT_DIR/dstack-vmm" "$SCRIPT_DIR/rust-target/release/dstack-vmm" \ + "$SCRIPT_DIR/dstack/target/release/dstack-vmm"; do + [ -x "$c" ] && DSTACK_VMM_BIN="$c" && break + done + fi + if [ -n "${DSTACK_VMM_BIN:-}" ] && [ -x "${DSTACK_VMM_BIN}" ]; then + echo "Generating digest.sev.txt via ${DSTACK_VMM_BIN}" + "${DSTACK_VMM_BIN}" sev-os-image-hash "${OUTPUT_DIR}" > "${OUTPUT_DIR}/digest.sev.txt" + HAVE_DIGEST_SEV=1 + else + echo "Warning: dstack-vmm not found; skipping digest.sev.txt (set DSTACK_VMM_BIN)" >&2 + fi +fi + # Create UKI artifacts (disk.raw and auth_hash.txt) in OUTPUT_DIR UKI_CREATED=0 if [ "$ENABLE_UKI_IMAGE" = "1" ]; then @@ -331,6 +353,9 @@ if [ x$DSTACK_TAR_RELEASE = x1 ]; then if [ "$HAVE_OVMF_SEV" = "1" ]; then BARE_METAL_FILES="$BARE_METAL_FILES ovmf-sev.fd" fi + if [ "$HAVE_DIGEST_SEV" = "1" ]; then + BARE_METAL_FILES="$BARE_METAL_FILES digest.sev.txt" + fi (cd "$PARENT_DIR" && tar -czvf ${IMAGE_TAR} $(for f in $BARE_METAL_FILES; do echo "$TAR_DIR_NAME/$f"; done)) echo From dcf407063a01815b7bf49f74443adcab1e6effe8 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Wed, 17 Jun 2026 19:59:31 -0700 Subject: [PATCH 2/2] mkimage: compute digest.sev.txt via dstack-mr, build it if needed The AMD SEV-SNP os_image_hash logic moved from dstack-vmm into the dstack-mr crate (which now has a `sev-os-image-hash` command). Generate digest.sev.txt with dstack-mr instead of dstack-vmm, and make it required rather than best-effort: if a prebuilt dstack-mr is not found, build it (cargo build --release -p dstack-mr) instead of silently skipping. The VMM now reads this file at deploy time, so the image must ship it. --- mkimage.sh | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/mkimage.sh b/mkimage.sh index d4c8cfc..0e8b005 100755 --- a/mkimage.sh +++ b/mkimage.sh @@ -301,25 +301,28 @@ sha256sum sha256sum.txt | awk '{print $1}' > digest.txt popd # digest.sev.txt: the AMD SEV-SNP os_image_hash. Unlike the TDX digest.txt -# (a content hash that includes the TDX firmware), this is computed by -# dstack-vmm from the SEV firmware (ovmf-sev.fd) + kernel/initrd/cmdline/rootfs -# and matches the os_image_hash the KMS verifier derives from a launch -# measurement. Only emitted when the SEV firmware shipped and the tool exists. +# (a content hash that includes the TDX firmware), this is computed by the +# `dstack-mr` tool from the SEV firmware (ovmf-sev.fd) + kernel/initrd/cmdline/ +# rootfs and matches the os_image_hash the KMS verifier derives from a launch +# measurement. The VMM reads this file at deploy time instead of recomputing it, +# so it is required (not best-effort): if `dstack-mr` is not prebuilt, build it. HAVE_DIGEST_SEV=0 if [ "$HAVE_OVMF_SEV" = "1" ]; then - if [ -z "${DSTACK_VMM_BIN:-}" ]; then - for c in "$SCRIPT_DIR/dstack-vmm" "$SCRIPT_DIR/rust-target/release/dstack-vmm" \ - "$SCRIPT_DIR/dstack/target/release/dstack-vmm"; do - [ -x "$c" ] && DSTACK_VMM_BIN="$c" && break + DSTACK_SRC="${DSTACK_SRC:-$SCRIPT_DIR/dstack}" + if [ -z "${DSTACK_MR_BIN:-}" ]; then + for c in "$SCRIPT_DIR/dstack-mr" "$SCRIPT_DIR/rust-target/release/dstack-mr" \ + "$DSTACK_SRC/target/release/dstack-mr"; do + [ -x "$c" ] && DSTACK_MR_BIN="$c" && break done fi - if [ -n "${DSTACK_VMM_BIN:-}" ] && [ -x "${DSTACK_VMM_BIN}" ]; then - echo "Generating digest.sev.txt via ${DSTACK_VMM_BIN}" - "${DSTACK_VMM_BIN}" sev-os-image-hash "${OUTPUT_DIR}" > "${OUTPUT_DIR}/digest.sev.txt" - HAVE_DIGEST_SEV=1 - else - echo "Warning: dstack-vmm not found; skipping digest.sev.txt (set DSTACK_VMM_BIN)" >&2 + if [ -z "${DSTACK_MR_BIN:-}" ]; then + echo "Building dstack-mr to compute digest.sev.txt" + ( cd "$DSTACK_SRC" && cargo build --release -p dstack-mr ) + DSTACK_MR_BIN="$DSTACK_SRC/target/release/dstack-mr" fi + echo "Generating digest.sev.txt via ${DSTACK_MR_BIN}" + "${DSTACK_MR_BIN}" sev-os-image-hash "${OUTPUT_DIR}" > "${OUTPUT_DIR}/digest.sev.txt" + HAVE_DIGEST_SEV=1 fi # Create UKI artifacts (disk.raw and auth_hash.txt) in OUTPUT_DIR