diff --git a/docs/security/security-model.md b/docs/security/security-model.md index 5a0491867..157c8ca80 100644 --- a/docs/security/security-model.md +++ b/docs/security/security-model.md @@ -317,6 +317,29 @@ AML access to encrypted/private guest RAM. Verification now rejects tampered tables before the CVM is trusted with keys; the sandbox bounds what tampered AML could have done in the first place. +### A host-declared QEMU version selects between digests, it does not add one + +QEMU 10.2 stopped rewriting the Linux setup header for confidential guests +(commit a7542a38f399, "x86/loader: Don't update kernel header for CoCo VMs"), +because doing so changed the kernel bytes away from the file the operator +passed to `-kernel` and broke TDX attestation. A TDX CVM therefore measures the +patched kernel into RTMR[1] under QEMU <= 10.1 and the kernel as built under +>= 10.2, so the OS image records both Authenticode digests and the verifier +picks one using the host-declared `vm_config.qemu_version`. + +That declaration is untrusted, and it does not need to be trusted. Both +candidates are committed by the same measurement document, which is itself +bound to `os_image_hash`, so a lying host can only choose between two digests +that already belong to the image it declared. Whichever it picks still has to +equal the RTMR[1] the hardware signed, so misdeclaring the version cannot make +a different kernel verify -- it can only turn a good CVM into a rejected one, +which is the host degrading its own deployment. + +Images built before the measurement document carried both digests are rejected +on the document's version number, with an error naming the unsupported version, +rather than half-decoded into a document that is missing the digest the CVM +actually needs. + ### TCB status is surfaced, not gated, during verification dstack's `validate_tcb` does not reject a quote based on its TCB status string (`UpToDate`, `OutOfDate`, `ConfigurationNeeded`, `SWHardeningNeeded`, ...). It only enforces hard invariants: debug mode must be off, and the SEAM/service-TD measurements must be well-formed. The verified report carries the `status` field through to the caller. diff --git a/dstack/dstack-mr/cli/src/main.rs b/dstack/dstack-mr/cli/src/main.rs index 783f087d5..6a4d158a1 100644 --- a/dstack/dstack-mr/cli/src/main.rs +++ b/dstack/dstack-mr/cli/src/main.rs @@ -49,6 +49,13 @@ struct MachineConfig { #[arg(long)] pic: Option, + /// Whether QEMU rewrites the Linux setup header before serving the kernel + /// over fw_cfg. Defaults to the behavior of --qemu-version: QEMU >= 10.2 + /// leaves it alone for confidential guests. Set this only for a fork whose + /// behavior disagrees with its version number. + #[arg(long)] + patch_kernel_header: Option, + /// Enable SMM #[arg(long, default_value = "false")] smm: Bool, @@ -126,6 +133,7 @@ fn main() -> Result<()> { .initrd(&initrd_path) .kernel_cmdline(&cmdline) .maybe_two_pass_add_pages(config.two_pass_add_pages) + .maybe_patch_kernel_header(config.patch_kernel_header) .maybe_pic(config.pic) .smm(config.smm) .maybe_pci_hole64_size(config.pci_hole64_size) @@ -341,6 +349,7 @@ fn run_diagnose(config: &DiagnoseConfig) -> Result<()> { .root_verity(true) .hotplug_off(vm.hotplug_off) .maybe_two_pass_add_pages(vm.qemu_single_pass_add_pages) + .maybe_patch_kernel_header(vm.qemu_patches_kernel_header) .maybe_pic(vm.pic) .maybe_qemu_version(vm.qemu_version.clone()) .maybe_pci_hole64_size(if vm.pci_hole64_size > 0 { diff --git a/dstack/dstack-mr/src/kernel.rs b/dstack/dstack-mr/src/kernel.rs index a4e969563..cccc703a6 100644 --- a/dstack/dstack-mr/src/kernel.rs +++ b/dstack/dstack-mr/src/kernel.rs @@ -237,15 +237,30 @@ pub(crate) fn patched_kernel_authenticode_sha384( authenticode_sha384_hash(&kd).context("Failed to compute kernel hash") } -/// Measures a QEMU-patched TDX kernel image. +/// Compute the first RTMR[1] event digest for a kernel QEMU serves untouched: +/// the Authenticode SHA-384 hash of the image file exactly as built. +/// +/// QEMU >= 10.2 takes this path for every confidential guest, TDX included. +pub(crate) fn kernel_authenticode_sha384(kernel_data: &[u8]) -> Result> { + authenticode_sha384_hash(kernel_data).context("failed to compute kernel hash") +} + +/// Measures the TDX kernel image QEMU hands to OVMF. +/// +/// `patch_kernel_header` selects between the two kernel images QEMU can serve +/// over fw_cfg; see [`crate::machine::VersionedOptions::patch_kernel_header`]. pub(crate) fn rtmr1_log( kernel_data: &[u8], initrd_size: u32, mem_size: u64, acpi_data_size: u32, + patch_kernel_header: bool, ) -> Result>> { - let kernel_hash = - patched_kernel_authenticode_sha384(kernel_data, initrd_size, mem_size, acpi_data_size)?; + let kernel_hash = if patch_kernel_header { + patched_kernel_authenticode_sha384(kernel_data, initrd_size, mem_size, acpi_data_size)? + } else { + kernel_authenticode_sha384(kernel_data)? + }; Ok(vec![ kernel_hash, measure_sha384(b"Calling EFI Application from Boot Option"), diff --git a/dstack/dstack-mr/src/machine.rs b/dstack/dstack-mr/src/machine.rs index eda97e61f..d0aa40565 100644 --- a/dstack/dstack-mr/src/machine.rs +++ b/dstack/dstack-mr/src/machine.rs @@ -20,6 +20,9 @@ pub struct Machine<'a> { pub initrd: &'a str, pub kernel_cmdline: &'a str, pub two_pass_add_pages: Option, + /// Override for whether QEMU rewrites the Linux setup header before serving + /// the kernel over fw_cfg. `None` derives it from `qemu_version`. + pub patch_kernel_header: Option, pub pic: Option, pub qemu_version: Option, #[builder(default = false)] @@ -91,18 +94,41 @@ impl Machine<'_> { default_pic = false; default_two_pass = false; }; + let default_patch_kernel_header = version < QEMU_COCO_KERNEL_HEADER_UNPATCHED; Ok(VersionedOptions { version, pic: self.pic.unwrap_or(default_pic), two_pass_add_pages: self.two_pass_add_pages.unwrap_or(default_two_pass), + patch_kernel_header: self + .patch_kernel_header + .unwrap_or(default_patch_kernel_header), }) } } +/// First QEMU release that stops rewriting the Linux setup header for +/// confidential guests, from commit a7542a38f399 ("x86/loader: Don't update +/// kernel header for CoCo VMs"), which widened the pre-existing SEV-only skip +/// (`!sev_enabled()`) to every confidential guest (`!MACHINE(x86ms)->cgs`). +/// +/// The commit was not backported to 10.1 or earlier, and every release since +/// -- 10.2.x, 11.0.x, 11.1.x and master -- keeps the widened check, so the +/// boundary is a single step at 10.2.0 rather than a per-release quirk. +pub const QEMU_COCO_KERNEL_HEADER_UNPATCHED: (u32, u32, u32) = (10, 2, 0); + pub struct VersionedOptions { pub version: (u32, u32, u32), pub pic: bool, pub two_pass_add_pages: bool, + /// Whether QEMU rewrites the Linux setup header (`type_of_loader`, + /// `loadflags`, `heap_end_ptr`, `cmdline_addr`, `initrd_addr`, + /// `initrd_size`) before exposing the kernel over fw_cfg. + /// + /// QEMU <= 10.1 does, so OVMF measures the patched image into RTMR[1]. + /// QEMU >= 10.2 leaves it alone for every confidential guest, so OVMF + /// measures the kernel file as built and the digest no longer depends on + /// guest memory size. + pub patch_kernel_header: bool, } #[derive(Debug, Clone)] @@ -137,6 +163,7 @@ impl Machine<'_> { initrd_data.len() as u32, self.memory_size, 0x28000, + self.versioned_options()?.patch_kernel_header, )?; debug_print_log("RTMR1", &rtmr1_log); let rtmr1 = measure_log(&rtmr1_log); @@ -160,3 +187,75 @@ impl Machine<'_> { }) } } + +#[cfg(test)] +mod tests { + use super::*; + + const TDX_TEST_MEMORY: u64 = 0x8000_0000; + + fn patch_kernel_header_for(version: &str) -> bool { + Machine::builder() + .cpu_count(1) + .memory_size(TDX_TEST_MEMORY) + .firmware("") + .kernel("") + .initrd("") + .kernel_cmdline("") + .hugepages(false) + .num_gpus(0) + .num_nvswitches(0) + .hotplug_off(false) + .root_verity(true) + .qemu_version(version.to_string()) + .build() + .versioned_options() + .unwrap() + .patch_kernel_header + } + + /// QEMU commit a7542a38f399 landed in 10.2.0 and was not backported, so the + /// boundary is exactly between 10.1.x and 10.2.0. + #[test] + fn kernel_header_patching_stops_at_qemu_10_2() { + for version in ["8.2.2", "9.1.0", "9.2.1", "10.0.0", "10.1.0", "10.1.9"] { + assert!( + patch_kernel_header_for(version), + "QEMU {version} still patches the setup header" + ); + } + for version in ["10.2.0", "10.2.1", "10.2.4", "11.0.0", "11.1.0", "12.0.0"] { + assert!( + !patch_kernel_header_for(version), + "QEMU {version} leaves the setup header alone for CoCo guests" + ); + } + } + + /// A fork can carry or omit the commit against its version number, so the + /// explicit override has to win over the version-derived default. + #[test] + fn explicit_patch_kernel_header_overrides_the_version_default() { + for (version, override_value) in [("10.2.1", true), ("9.2.1", false)] { + let machine = Machine::builder() + .cpu_count(1) + .memory_size(TDX_TEST_MEMORY) + .firmware("") + .kernel("") + .initrd("") + .kernel_cmdline("") + .hugepages(false) + .num_gpus(0) + .num_nvswitches(0) + .hotplug_off(false) + .root_verity(true) + .qemu_version(version.to_string()) + .patch_kernel_header(override_value) + .build(); + assert_eq!( + machine.versioned_options().unwrap().patch_kernel_header, + override_value + ); + } + } +} diff --git a/dstack/dstack-mr/src/main.rs b/dstack/dstack-mr/src/main.rs index 7b5e52d53..00aaa2352 100644 --- a/dstack/dstack-mr/src/main.rs +++ b/dstack/dstack-mr/src/main.rs @@ -23,7 +23,7 @@ usage: dstack-mr snp-measurement-hash features: - split-cbor-measurement-v3"; + split-cbor-measurement-v4"; fn main() -> Result<()> { let mut args = std::env::args().skip(1); diff --git a/dstack/dstack-mr/src/tdx.rs b/dstack/dstack-mr/src/tdx.rs index 3cdec25a0..e31df6066 100644 --- a/dstack/dstack-mr/src/tdx.rs +++ b/dstack/dstack-mr/src/tdx.rs @@ -11,9 +11,11 @@ //! intentionally excluded and must come from `VmConfig`. use crate::kernel::{ - patched_kernel_authenticode_sha384, tdx_kernel_hash_uses_precomputed_high_mem, - TDX_KERNEL_HASH_COMPAT_2G_MEMORY, TDX_KERNEL_HASH_STABLE_MIN_MEMORY, + kernel_authenticode_sha384, patched_kernel_authenticode_sha384, + tdx_kernel_hash_uses_precomputed_high_mem, TDX_KERNEL_HASH_COMPAT_2G_MEMORY, + TDX_KERNEL_HASH_STABLE_MIN_MEMORY, }; +use crate::machine::{VersionedOptions, QEMU_COCO_KERNEL_HEADER_UNPATCHED}; use crate::tdvf::{rtmr0_log_from_td_hob_hash_with_acpi_hashes, AcpiTableHashes, Tdvf}; use crate::util::{measure_log, measure_sha384}; use anyhow::{bail, Context, Result}; @@ -77,6 +79,7 @@ fn machine_from_vm_config(vm_config: &VmConfig, ovmf_variant: OvmfVariant) -> cr .root_verity(true) .hotplug_off(vm_config.hotplug_off) .maybe_two_pass_add_pages(vm_config.qemu_single_pass_add_pages) + .maybe_patch_kernel_header(vm_config.qemu_patches_kernel_header) .maybe_pic(vm_config.pic) .maybe_qemu_version(vm_config.qemu_version.clone()) .maybe_pci_hole64_size(if vm_config.pci_hole64_size > 0 { @@ -119,11 +122,7 @@ pub fn expected_rtmr0_acpi_hashes( }) } -fn select_mrtd(measurement: &TdxOsImageMeasurement, vm_config: &VmConfig) -> Result> { - let machine = machine_from_vm_config(vm_config, measurement.tdvf.ovmf_variant); - let opts = machine - .versioned_options() - .context("failed to resolve QEMU measurement options")?; +fn select_mrtd(measurement: &TdxOsImageMeasurement, opts: &VersionedOptions) -> Result> { let mrtd = if opts.two_pass_add_pages { &measurement.tdvf.mrtd.two_pass } else { @@ -132,6 +131,47 @@ fn select_mrtd(measurement: &TdxOsImageMeasurement, vm_config: &VmConfig) -> Res validate_bytes_field(mrtd, "tdx.measurement.tdvf.mrtd", 48) } +/// Pick the kernel Authenticode digest matching the QEMU that booted the CVM. +/// +/// Under QEMU >= 10.2 the digest covers the kernel file as built, so it holds +/// for any guest memory size. Under <= 10.1 it covers QEMU's patched setup +/// header, which is only stable at the memory sizes +/// [`tdx_kernel_hash_uses_precomputed_high_mem`] accepts. +fn select_kernel_authenticode( + measurement: &TdxOsImageMeasurement, + vm_config: &VmConfig, + opts: &VersionedOptions, +) -> Result> { + let (digest, field) = if opts.patch_kernel_header { + if !tdx_kernel_hash_uses_precomputed_high_mem(vm_config.memory_size) { + bail!( + "TDX lite attestation without image download on QEMU {}.{}.{} requires memory_size == {} bytes ({} MiB) or >= {} bytes ({} MiB); got {} bytes. QEMU >= {}.{}.{} removes this restriction because it no longer rewrites the kernel setup header for confidential guests", + opts.version.0, + opts.version.1, + opts.version.2, + TDX_KERNEL_HASH_COMPAT_2G_MEMORY, + TDX_KERNEL_HASH_COMPAT_2G_MEMORY / 1024 / 1024, + TDX_KERNEL_HASH_STABLE_MIN_MEMORY, + TDX_KERNEL_HASH_STABLE_MIN_MEMORY / 1024 / 1024, + vm_config.memory_size, + QEMU_COCO_KERNEL_HEADER_UNPATCHED.0, + QEMU_COCO_KERNEL_HEADER_UNPATCHED.1, + QEMU_COCO_KERNEL_HEADER_UNPATCHED.2, + ); + } + ( + &measurement.image.patched_kernel_authenticode, + "tdx.measurement.image.patched_kernel_authenticode", + ) + } else { + ( + &measurement.image.kernel_authenticode, + "tdx.measurement.image.kernel_authenticode", + ) + }; + validate_bytes_field(digest, field, 48) +} + fn read_varuint(input: &mut &[u8]) -> Result { let mut value = 0u64; let mut shift = 0u32; @@ -321,7 +361,9 @@ pub fn tdx_os_image_measurement_for_image_dir(image_dir: &Path) -> Result Result Result<[u8; 32]> /// Compute expected TDX measurements from self-contained TDX measurement /// material and the three ACPI table digests captured in RTMR[0]. /// -/// This path intentionally does not download or read the OS image. Because -/// QEMU's patched kernel Authenticode hash depends on exact guest RAM below -/// `TDX_KERNEL_HASH_STABLE_MIN_MEMORY`, the no-image-download path supports -/// CVMs at or above that threshold plus the exact 2 GiB placement, which QEMU -/// patches to the same kernel bytes as the high-memory case. +/// This path intentionally does not download or read the OS image. On QEMU +/// <= 10.1 the patched kernel Authenticode hash depends on exact guest RAM +/// below `TDX_KERNEL_HASH_STABLE_MIN_MEMORY`, so those CVMs are supported only +/// at or above that threshold plus the exact 2 GiB placement, which QEMU +/// patches to the same kernel bytes as the high-memory case. QEMU >= 10.2 does +/// not patch the kernel at all, so any memory size works there; see +/// [`select_kernel_authenticode`]. pub fn tdx_measurements_from_measurement_document( document: &TdxOsImageMeasurementDocument, vm_config: &VmConfig, acpi_hashes: &TdxRtmr0AcpiHashes, ) -> Result { - if !tdx_kernel_hash_uses_precomputed_high_mem(vm_config.memory_size) { - bail!( - "TDX lite attestation without image download requires memory_size == {} bytes ({} MiB) or >= {} bytes ({} MiB); got {} bytes", - TDX_KERNEL_HASH_COMPAT_2G_MEMORY, - TDX_KERNEL_HASH_COMPAT_2G_MEMORY / 1024 / 1024, - TDX_KERNEL_HASH_STABLE_MIN_MEMORY, - TDX_KERNEL_HASH_STABLE_MIN_MEMORY / 1024 / 1024, - vm_config.memory_size - ); - } - let measurement = document .decode_measurement() .map_err(anyhow::Error::msg) .context("failed to decode TDX measurement CBOR")?; - let mrtd = select_mrtd(&measurement, vm_config)?; + let opts = machine_from_vm_config(vm_config, measurement.tdvf.ovmf_variant) + .versioned_options() + .context("failed to resolve QEMU measurement options")?; + let mrtd = select_mrtd(&measurement, &opts)?; let td_hob_hash = measure_td_hob_from_witness_data(&measurement.tdvf.td_hob_witness, vm_config.memory_size) @@ -403,11 +440,7 @@ pub fn tdx_measurements_from_measurement_document( .context("failed to compute RTMR0 from measurement document")?; let rtmr0 = measure_log(&rtmr0_log); - let kernel_hash = validate_bytes_field( - &measurement.image.kernel_authenticode, - "tdx.measurement.image.kernel_authenticode", - 48, - )?; + let kernel_hash = select_kernel_authenticode(&measurement, vm_config, &opts)?; let rtmr1 = measure_log(&rtmr1_log_from_kernel_hash(kernel_hash)); let initrd_hash = validate_bytes_field( @@ -482,6 +515,7 @@ pub fn tdx_measurements_for_image_dir_without_rtmr0( .root_verity(true) .hotplug_off(vm_config.hotplug_off) .maybe_two_pass_add_pages(vm_config.qemu_single_pass_add_pages) + .maybe_patch_kernel_header(vm_config.qemu_patches_kernel_header) .maybe_pic(vm_config.pic) .maybe_qemu_version(vm_config.qemu_version.clone()) .maybe_pci_hole64_size(if vm_config.pci_hole64_size > 0 { @@ -507,6 +541,10 @@ pub fn tdx_measurements_for_image_dir_without_rtmr0( initrd_data.len() as u32, vm_config.memory_size, 0x28000, + machine + .versioned_options() + .context("failed to resolve QEMU measurement options")? + .patch_kernel_header, ) .context("failed to compute RTMR1")?; let rtmr1 = measure_log(&rtmr1_log); @@ -573,6 +611,7 @@ pub fn tdx_measurements_for_image_dir_with_acpi_hashes( .root_verity(true) .hotplug_off(vm_config.hotplug_off) .maybe_two_pass_add_pages(vm_config.qemu_single_pass_add_pages) + .maybe_patch_kernel_header(vm_config.qemu_patches_kernel_header) .maybe_pic(vm_config.pic) .maybe_qemu_version(vm_config.qemu_version.clone()) .maybe_pci_hole64_size(if vm_config.pci_hole64_size > 0 { @@ -611,6 +650,10 @@ pub fn tdx_measurements_for_image_dir_with_acpi_hashes( initrd_data.len() as u32, vm_config.memory_size, 0x28000, + machine + .versioned_options() + .context("failed to resolve QEMU measurement options")? + .patch_kernel_header, ) .context("failed to compute RTMR1")?; let rtmr1 = measure_log(&rtmr1_log); @@ -628,3 +671,156 @@ pub fn tdx_measurements_for_image_dir_with_acpi_hashes( rtmr2, }) } + +#[cfg(test)] +mod tests { + use super::*; + use dstack_types::TdxAttestationVariant; + + /// Kernel digests and the RTMR[1] they produce, captured from two dstack + /// CVMs running on QEMU 10.2.1 (Ubuntu `1:10.2.1+ds-1ubuntu3.2`). + /// + /// `unpatched` is what the guest actually measured; `patched` is what + /// dstack-mr computed before QEMU commit a7542a38f399 was accounted for, + /// and `rtmr1` is the value in the TDX quote. Pinning all three keeps the + /// selection honest in both directions. + struct KernelVector { + patched: &'static str, + unpatched: &'static str, + rtmr1_unpatched: &'static str, + rtmr1_patched: &'static str, + memory_size: u64, + } + + /// 2 GiB CVM on the dstack-0.6.0-rc0 image. + const SMALL_VM: KernelVector = KernelVector { + patched: "a6dc51e745a0e537afddfc7e06567109692e7840f0334f0392affea35039d10bbb460d86c7310c82de8029a111816ef0", + unpatched: "ae9a1504c977b39f6a94558e7701d136ffe6cc30b061db5be7f8a8bd3d0f37bf0d2adcafd23eaf69f2ecd13318b5d83b", + rtmr1_unpatched: "74f00f634fe914dc875dc285d8efe1c1ecda4cf10e207f5abd079b559ac5e02ffd64eb75f03ae353cbe72f6ebdd9eca8", + rtmr1_patched: "4ffc8bcd33abc1bbf1d772196dc6d9f68cbe64c07b16a74ac085aa22ae976731bbb34a59255f119ec1b2ba53018cfa8e", + memory_size: 0x8000_0000, + }; + + /// 768 GiB, 8-GPU CVM on a dstack-0.6.0-next image. + const LARGE_VM: KernelVector = KernelVector { + patched: "67c69774a5b01786e051f78bcc85db7cc6cd3bda99ad01a30d1e7827f74c78242c4961d5b99ca5674f15560762a7c230", + unpatched: "e5fde8577a8e1f422c78ddc1be9001c14c6f8487d2df78068bfe580d960a78c28db5e17248e6181eb15ff61527c4362d", + rtmr1_unpatched: "a839af66b8879b40a48452e170943ce88347d6a294d988eef21df357a0e93d69e7cee07d0cb0fdc3e7e2f19d820be524", + rtmr1_patched: "a0e85dfd584783413eef452c7668bf93bbfed983f817a66f47389f096b304171d2b09b9883ad46b8b9d688c1ab027cf1", + memory_size: 824_633_720_832, + }; + + fn measurement_with(vector: &KernelVector) -> TdxOsImageMeasurement { + TdxOsImageMeasurement { + image: TdxImageMeasurement { + kernel_cmdline_sha384: vec![0x11; 48], + kernel_authenticode: hex::decode(vector.unpatched).unwrap(), + patched_kernel_authenticode: hex::decode(vector.patched).unwrap(), + initrd_sha384: vec![0x22; 48], + }, + tdvf: TdxTdvfMeasurement { + ovmf_variant: OvmfVariant::default(), + mrtd: TdxMrtdCandidates { + single_pass: vec![0x33; 48], + two_pass: vec![0x44; 48], + }, + td_hob_witness: vec![0x00], + }, + } + } + + fn vm_config(qemu_version: &str, memory_size: u64) -> VmConfig { + VmConfig { + os_image_hash: vec![], + cpu_count: 1, + memory_size, + qemu_single_pass_add_pages: None, + pic: None, + qemu_patches_kernel_header: None, + qemu_version: Some(qemu_version.to_string()), + pci_hole64_size: 0, + hugepages: false, + num_gpus: 0, + num_nvswitches: 0, + num_nics: 1, + num_verity_volumes: 0, + swtpm: false, + hotplug_off: false, + image: None, + host_share_mode: "9p".to_string(), + ovmf_variant: None, + tdx_attestation_variant: TdxAttestationVariant::default(), + tdx_measurement: None, + gcp_measurement: None, + aws_measurement: None, + } + } + + fn select_kernel_authenticode_for( + measurement: &TdxOsImageMeasurement, + config: &VmConfig, + ) -> Result> { + let opts = machine_from_vm_config(config, measurement.tdvf.ovmf_variant) + .versioned_options() + .unwrap(); + select_kernel_authenticode(measurement, config, &opts) + } + + fn rtmr1_for(vector: &KernelVector, qemu_version: &str) -> String { + let measurement = measurement_with(vector); + let config = vm_config(qemu_version, vector.memory_size); + hex::encode(measure_log(&rtmr1_log_from_kernel_hash( + select_kernel_authenticode_for(&measurement, &config).unwrap(), + ))) + } + + /// The regression this whole change exists for: on QEMU 10.2 the quoted + /// RTMR[1] only reproduces from the unpatched kernel digest. + #[test] + fn qemu_10_2_reproduces_the_rtmr1_the_hardware_quoted() { + for vector in [&SMALL_VM, &LARGE_VM] { + assert_eq!(rtmr1_for(vector, "10.2.1"), vector.rtmr1_unpatched); + } + } + + /// QEMU <= 10.1 must keep resolving to the patched digest, which is the + /// value dstack-mr produced before this change. + #[test] + fn qemu_10_1_still_selects_the_patched_kernel_digest() { + for vector in [&SMALL_VM, &LARGE_VM] { + assert_eq!(rtmr1_for(vector, "10.1.0"), vector.rtmr1_patched); + } + } + + /// The patched digest is only stable at the memory sizes QEMU patches + /// identically, so the guard has to stay on the <= 10.1 branch. + #[test] + fn odd_memory_sizes_are_rejected_only_while_qemu_patches_the_kernel() { + let measurement = measurement_with(&SMALL_VM); + // 2.5 GiB: above the 2 GiB special case, below the threshold where + // QEMU's patched initrd placement stops moving with memory size. + let odd_memory = 0xA000_0000; + + let err = select_kernel_authenticode_for(&measurement, &vm_config("10.1.0", odd_memory)) + .expect_err("QEMU 10.1 cannot verify an unmodeled memory size"); + assert!( + err.to_string().contains("memory_size"), + "unexpected error: {err}" + ); + + let digest = select_kernel_authenticode_for(&measurement, &vm_config("10.2.1", odd_memory)) + .expect("QEMU 10.2 does not patch the kernel, so memory size is irrelevant"); + assert_eq!(hex::encode(digest), SMALL_VM.unpatched); + } + + /// A fork override has to reach the candidate selection, not just the + /// version mapping. + #[test] + fn the_vm_config_override_selects_the_candidate() { + let measurement = measurement_with(&SMALL_VM); + let mut config = vm_config("10.2.1", SMALL_VM.memory_size); + config.qemu_patches_kernel_header = Some(true); + let digest = select_kernel_authenticode_for(&measurement, &config).unwrap(); + assert_eq!(hex::encode(digest), SMALL_VM.patched); + } +} diff --git a/dstack/dstack-types/src/lib.rs b/dstack/dstack-types/src/lib.rs index 86df226b2..7be060c81 100644 --- a/dstack/dstack-types/src/lib.rs +++ b/dstack/dstack-types/src/lib.rs @@ -1436,6 +1436,13 @@ pub struct VmConfig { pub qemu_single_pass_add_pages: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub pic: Option, + /// Override for whether QEMU rewrites the Linux setup header before serving + /// the kernel over fw_cfg, which decides the RTMR[1] kernel digest. + /// Absent means "derive it from `qemu_version`", which is right for every + /// upstream release; set it only for a fork that carries or omits QEMU + /// commit a7542a38f399 against its version number. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub qemu_patches_kernel_header: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub qemu_version: Option, #[serde(default)] @@ -2025,12 +2032,23 @@ pub struct TdxImageMeasurement { /// `initrd=initrd` suffix, encoded as UTF-16LE with a trailing NUL. #[serde(with = "hex_bytes")] pub kernel_cmdline_sha384: Vec, - /// Authenticode SHA-384 digest of the QEMU-patched kernel image when the - /// guest memory is at or above QEMU's high-memory TDX initrd placement - /// threshold. Below that threshold the patched kernel header depends on the - /// exact guest memory size, so the no-image-download verifier rejects it. + /// Authenticode SHA-384 of the kernel file as built, byte for byte. + /// + /// This is what OVMF measures into RTMR[1] under QEMU >= 10.2, which no + /// longer rewrites the Linux setup header for confidential guests. It does + /// not depend on guest memory size. #[serde(with = "hex_bytes")] pub kernel_authenticode: Vec, + /// Authenticode SHA-384 of the same kernel after QEMU rewrites the Linux + /// setup header (`type_of_loader`, `loadflags`, `heap_end_ptr`, + /// `cmdline_addr`, `initrd_addr`, `initrd_size`). + /// + /// This is what OVMF measures into RTMR[1] under QEMU <= 10.1. The patched + /// header encodes the initrd placement, so this digest is only stable at + /// the guest memory sizes the verifier accepts on the no-image-download + /// path. + #[serde(with = "hex_bytes")] + pub patched_kernel_authenticode: Vec, /// SHA-384 of the initrd file bytes. This is the second RTMR[2] event. #[serde(with = "hex_bytes")] pub initrd_sha384: Vec, @@ -2061,9 +2079,13 @@ struct CborTdxImageMeasurement { /// Measured kernel cmdline SHA-384. #[serde(rename = "cmdline_sha384", with = "hex_bytes")] kernel_cmdline_sha384: Vec, - /// QEMU-patched kernel Authenticode SHA-384. + /// Authenticode SHA-384 of the kernel as built (QEMU >= 10.2). #[serde(with = "hex_bytes")] kernel_authenticode: Vec, + /// Authenticode SHA-384 of the kernel with QEMU's setup-header rewrite + /// applied (QEMU <= 10.1). + #[serde(with = "hex_bytes")] + patched_kernel_authenticode: Vec, /// Initrd SHA-384. #[serde(with = "hex_bytes")] initrd_sha384: Vec, @@ -2093,43 +2115,60 @@ struct CborTdxOsImageMeasurement { tdvf: CborTdxTdvfMeasurement, } -impl From<&TdxOsImageMeasurement> for CborTdxOsImageMeasurement { - fn from(measurement: &TdxOsImageMeasurement) -> Self { +/// Reads just the version, so a document from a different format generation is +/// reported as an unsupported version rather than as a field-shape error. +#[derive(Debug, Deserialize)] +struct CborTdxOsImageMeasurementVersion { + version: u32, +} + +impl CborTdxTdvfMeasurement { + fn from_measurement(tdvf: &TdxTdvfMeasurement) -> Self { + Self { + ovmf_variant: tdvf.ovmf_variant, + mrtd: CborTdxMrtdCandidates { + single_pass: tdvf.mrtd.single_pass.clone(), + two_pass: tdvf.mrtd.two_pass.clone(), + }, + td_hob_witness: tdvf.td_hob_witness.clone(), + } + } + + fn into_measurement(self) -> TdxTdvfMeasurement { + TdxTdvfMeasurement { + ovmf_variant: self.ovmf_variant, + mrtd: TdxMrtdCandidates { + single_pass: self.mrtd.single_pass, + two_pass: self.mrtd.two_pass, + }, + td_hob_witness: self.td_hob_witness, + } + } +} + +impl CborTdxOsImageMeasurement { + fn from_measurement(measurement: &TdxOsImageMeasurement) -> Self { Self { version: TdxOsImageMeasurement::VERSION, image: CborTdxImageMeasurement { kernel_cmdline_sha384: measurement.image.kernel_cmdline_sha384.clone(), kernel_authenticode: measurement.image.kernel_authenticode.clone(), + patched_kernel_authenticode: measurement.image.patched_kernel_authenticode.clone(), initrd_sha384: measurement.image.initrd_sha384.clone(), }, - tdvf: CborTdxTdvfMeasurement { - ovmf_variant: measurement.tdvf.ovmf_variant, - mrtd: CborTdxMrtdCandidates { - single_pass: measurement.tdvf.mrtd.single_pass.clone(), - two_pass: measurement.tdvf.mrtd.two_pass.clone(), - }, - td_hob_witness: measurement.tdvf.td_hob_witness.clone(), - }, + tdvf: CborTdxTdvfMeasurement::from_measurement(&measurement.tdvf), } } -} -impl From for TdxOsImageMeasurement { - fn from(measurement: CborTdxOsImageMeasurement) -> Self { - Self { + fn into_measurement(self) -> TdxOsImageMeasurement { + TdxOsImageMeasurement { image: TdxImageMeasurement { - kernel_cmdline_sha384: measurement.image.kernel_cmdline_sha384, - kernel_authenticode: measurement.image.kernel_authenticode, - initrd_sha384: measurement.image.initrd_sha384, - }, - tdvf: TdxTdvfMeasurement { - ovmf_variant: measurement.tdvf.ovmf_variant, - mrtd: TdxMrtdCandidates { - single_pass: measurement.tdvf.mrtd.single_pass, - two_pass: measurement.tdvf.mrtd.two_pass, - }, - td_hob_witness: measurement.tdvf.td_hob_witness, + kernel_cmdline_sha384: self.image.kernel_cmdline_sha384, + kernel_authenticode: self.image.kernel_authenticode, + patched_kernel_authenticode: self.image.patched_kernel_authenticode, + initrd_sha384: self.image.initrd_sha384, }, + tdvf: self.tdvf.into_measurement(), } } } @@ -2146,32 +2185,59 @@ pub struct TdxOsImageMeasurementDocument { } impl TdxOsImageMeasurement { - pub const VERSION: u32 = 3; - - /// CBOR representation stored as `measurement.tdx.cbor`. + /// Format version of `measurement.tdx.cbor`. Bumped from 3 to 4 when + /// `image.kernel_authenticode` (a single digest, which was always the + /// QEMU-patched one) became the pair of digests QEMU 10.2 made necessary. + /// + /// Only this version is accepted. Version 3 shipped in the `v0.6.0-rc0` + /// prereleases and no stable release, so it is rejected by number rather + /// than carried along: an image built against it reports an unsupported + /// version on every QEMU and has to be re-emitted, which also gives it a + /// new `os_image_hash`. + pub const VERSION: u32 = 4; + + /// CBOR representation stored as `measurement.tdx.cbor`. The image digest + /// in `sha256sum.txt` is taken over exactly these bytes. pub fn to_cbor_vec(&self) -> Vec { cbor_to_vec( - &CborTdxOsImageMeasurement::from(self), + &CborTdxOsImageMeasurement::from_measurement(self), "TdxOsImageMeasurement", ) } pub fn from_cbor_slice(bytes: &[u8]) -> Result { - let cbor = cbor_from_slice::(bytes, "TdxOsImageMeasurement")?; - if cbor.version != Self::VERSION { + const CONTEXT: &str = "TdxOsImageMeasurement"; + // The version is read first so a document from another format + // generation is reported as an unsupported version rather than as a + // confusing field-shape error. + Self::check_version(bytes)?; + Ok(cbor_from_slice::(bytes, CONTEXT)?.into_measurement()) + } + + pub fn cbor_json_value_from_slice(bytes: &[u8]) -> Result { + const CONTEXT: &str = "TdxOsImageMeasurement"; + Self::check_version(bytes)?; + let cbor = cbor_from_slice::(bytes, CONTEXT)?; + serde_json::to_value(cbor) + .map_err(|e| format!("{CONTEXT}: failed to convert CBOR to JSON: {e}")) + } + + fn check_version(bytes: &[u8]) -> Result<(), String> { + let version = Self::declared_version(bytes)?; + if version != Self::VERSION { return Err(format!( - "TdxOsImageMeasurement: unsupported version {}, expected {}", - cbor.version, - Self::VERSION + "TdxOsImageMeasurement: unsupported version {version}, expected {}", + Self::VERSION, )); } - Ok(cbor.into()) + Ok(()) } - pub fn cbor_json_value_from_slice(bytes: &[u8]) -> Result { - let cbor = cbor_from_slice::(bytes, "TdxOsImageMeasurement")?; - serde_json::to_value(cbor) - .map_err(|e| format!("TdxOsImageMeasurement: failed to convert CBOR to JSON: {e}")) + fn declared_version(bytes: &[u8]) -> Result { + Ok( + cbor_from_slice::(bytes, "TdxOsImageMeasurement")? + .version, + ) } /// SHA-256 over the CBOR measurement material. @@ -2702,3 +2768,96 @@ mod appcompose_sdk_parity { ); } } + +#[cfg(test)] +mod tdx_measurement_cbor_tests { + use super::*; + + fn measurement() -> TdxOsImageMeasurement { + TdxOsImageMeasurement { + image: TdxImageMeasurement { + kernel_cmdline_sha384: vec![0x11; 48], + kernel_authenticode: vec![0x22; 48], + patched_kernel_authenticode: vec![0x33; 48], + initrd_sha384: vec![0x44; 48], + }, + tdvf: TdxTdvfMeasurement { + ovmf_variant: OvmfVariant::default(), + mrtd: TdxMrtdCandidates { + single_pass: vec![0x55; 48], + two_pass: vec![0x66; 48], + }, + td_hob_witness: vec![0x01, 0x02, 0x03], + }, + } + } + + /// The two kernel digests are distinct values that must not be transposed + /// or collapsed, so the round trip asserts they come back apart. + #[test] + fn both_kernel_digests_survive_a_cbor_round_trip() { + let original = measurement(); + let decoded = TdxOsImageMeasurement::from_cbor_slice(&original.to_cbor_vec()).unwrap(); + assert_eq!(decoded, original); + assert_eq!(decoded.image.kernel_authenticode, vec![0x22; 48]); + assert_eq!(decoded.image.patched_kernel_authenticode, vec![0x33; 48]); + } + + #[test] + fn the_encoded_document_declares_the_current_version() { + let value = TdxOsImageMeasurement::cbor_json_value_from_slice(&measurement().to_cbor_vec()) + .unwrap(); + assert_eq!( + value["version"], + serde_json::json!(TdxOsImageMeasurement::VERSION) + ); + } + + /// CBOR stores the version as the single unsigned byte following the + /// "version" key, so rewriting that byte forges another version. + fn with_version(measurement: &TdxOsImageMeasurement, version: u8) -> Vec { + let key = [0x67, b'v', b'e', b'r', b's', b'i', b'o', b'n']; + let mut cbor = measurement.to_cbor_vec(); + let at = cbor + .windows(key.len()) + .position(|window| window == key) + .expect("encoded document contains a version key"); + cbor[at + key.len()] = version; + cbor + } + + #[test] + fn unknown_versions_are_rejected() { + let cbor = with_version(&measurement(), 2); + let err = TdxOsImageMeasurement::from_cbor_slice(&cbor).unwrap_err(); + assert!(err.contains("unsupported version 2"), "unexpected: {err}"); + } + + /// A real v3 `measurement.tdx.cbor`, whose `image.kernel_authenticode` is a + /// single digest -- always the QEMU-patched one -- instead of the pair. + /// + /// v3 never shipped in a tagged release, so it is rejected on its version + /// rather than carried along. Checking the version before the payload is + /// what turns this into "unsupported version 3, expected 4" instead of a + /// field-shape error that says nothing about how to fix the image. + const V3_GOLDEN_HEX: &str = concat!( + "a36776657273696f6e0365696d616765a36e636d646c696e655f73686133383458307862", + "80842b7364287a3a70d96f7e309252857beb45fb1f91314a2ea863db0adc04c8431ecbf2", + "9a966405604631a5aab8736b65726e656c5f61757468656e7469636f64655830ac7e632d", + "cf5cd2a1fe5c1f41f4d9b8219570e64ed3c61038fdbf25404e6f542ffd57f276bc507630", + "7efaf882e6d641776d696e697472645f73686133383458304fe4f7710134a61d7def357a", + "dd6ac50bdbfeee5032a4c100375e207216ffe42a3bd5822b24e679f91501fff795b81521", + "6474647666a3646f766d6669707265323032353035646d727464a26b73696e676c655f70", + "6173735830a6f2ac9451810686a4db259fe8fa5438dc4a58bda9fd2f5b1fb09283357055", + "00d29a15c92387416a2f52dddce99c83f86874776f5f706173735830fd685522ce791dfe", + "f67414614eb07d03fc07a32c5a66f36288b329dab92b724b1564c73d436ffb9ea84488c5", + "1ac5a1c56674645f686f624c80100904000609020b021010", + ); + + #[test] + fn a_real_v3_document_is_rejected_on_its_version() { + let bytes = hex::decode(V3_GOLDEN_HEX).expect("golden is valid hex"); + let err = TdxOsImageMeasurement::from_cbor_slice(&bytes).unwrap_err(); + assert!(err.contains("unsupported version 3"), "unexpected: {err}"); + } +} diff --git a/dstack/verifier/fixtures/tdx-lite-attestation.json b/dstack/verifier/fixtures/tdx-lite-attestation.json index 93f4af0ab..e71e8cb1a 100644 --- a/dstack/verifier/fixtures/tdx-lite-attestation.json +++ b/dstack/verifier/fixtures/tdx-lite-attestation.json @@ -1,3 +1,3 @@ { - "attestation": "0000394e040002008100000000000000939a7233f79c4ca9940a0db3957f06071026ff2bbebac59cc1ef911279d9481b000000000c010400000000000000000000000000d0d80c085166ba78ccc69af268e5753cf0f3394523cb4ff7c50b08d9265c82489c099c377be6a400e4d2b57da924012c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000e702060000000000fd685522ce791dfef67414614eb07d03fc07a32c5a66f36288b329dab92b724b1564c73d436ffb9ea84488c51ac5a1c50186b0e55f2fa8e4fb69d890f14f54d5612707646e2573d54e0d2ddaaade77caa9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f8438db36b96f85d8752ff7f24a89ec05c79ec9eda2ba732c897fb970ca429365b7471b1c054cb84f17b1c2b23ba66402023546e7f3b9d1228e274f70c44d481162540f8452544520a796a52f06879709b81a824a26792a7822327504b0d2aee4c1b739ed451a637b0f82642e48a5ea83925d23633c72e7385c8e9aca4175e133ed1625b7d92eb39edf509c27ff392dc6f24c170d0fd63fc2b1b53202eea47b013978437fa6982cf5e0438ff95c208994aaa0f4ebab2e3a66824b5b56869137e646970313a3a736563703235366b31632d706b3a41353570576d74654a494a4f6a385f7049372d707a654478793147327131384744763838484e526442586b51cc1000008bca152d0454bdfd5adab1bc3a527884f77ea7993d32ee0e4426b2ae0fe42bf3f5642d6abd763b4f4c6042133e2ed79cce743f2c54ff4c7ea5d712dc1172ec244fe5b32ac6ffeb104614bcb8894c7aaafbbbe6f6bfd852f5dcd6cf400557ee764e62850d955975d93eff63b17e6e13e329a7bb13926706c0430017d543ab01920600461000000404191b04ff0006000000000000000000000000000000000000000000000000000000000000000000000000000000001500000000000000e700000000000000e5a3a7b5d830c2953b98534c6c59a3a34fdc34e933f7f5898f0a85cf08846bca0000000000000000000000000000000000000000000000000000000000000000dc9e2a7c6f948f17474e34a7fc43ed030f7c1563f1babddf6340c82e0e54a8c500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c1140324365c08f021a721dbe9175cb89dcd2235e2bd00bfb235b2a66b8c783600000000000000000000000000000000000000000000000000000000000000002af8cd12d44e0d22f904b15c02968b57b668e7f2487ba308e1d9a269ea125e48b243f7d32bb8551e1e3c2c09bd2162d36941eeb47be50b9b55a766a14d0cfe302000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f05005e0e00002d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d49494538444343424a6167417749424167495556706163774c766c316d476155506b384b4375504141334769465177436759494b6f5a497a6a3045417749770a634445694d434147413155454177775a535735305a577767553064594946424453794251624746305a6d397962534244515445614d42674741315545436777520a535735305a577767513239796347397959585270623234784644415342674e564241634d43314e68626e526849454e7359584a684d51737743515944565151490a44414a445154454c4d416b474131554542684d4356564d774868634e4d6a59774e4445314d4441314d4455345768634e4d7a4d774e4445314d4441314d4455340a576a42774d534977494159445651514444426c4a626e526c624342545231676755454e4c49454e6c636e52705a6d6c6a5958526c4d526f77474159445651514b0a4442464a626e526c6243424462334a7762334a6864476c76626a45554d424947413155454277774c553246756447456751327868636d4578437a414a42674e560a4241674d416b4e424d517377435159445651514745774a56557a425a4d424d4742797147534d34394167454743437147534d343941774548413049414245586a0a53374265726c3262726b65543677707878436a556536564775577268586e51767a41395862524768356b68637671766b566b427874715935475759544f6551340a5948496a636b7974734c6c5531774b594a74576a67674d4d4d4949444344416642674e5648534d4547444157674253566231334e765276683655424a796454300a4d383442567776655644427242674e56485238455a4442694d47436758714263686c706f64485277637a6f764c32467761533530636e567a6447566b633256790a646d6c6a5a584d75615735305a577775593239744c334e6e6543396a5a584a3061575a7059324630615739754c3359304c33426a61324e796244396a595431770a624746305a6d397962535a6c626d4e765a476c755a7a316b5a584977485159445652304f42425945464362386b6b73714d364c384f6765734c713943337339440a7a5333504d41344741315564447745422f775145417749477744414d42674e5648524d4241663845416a41414d4949434f51594a4b6f5a496876684e415130420a424949434b6a4343416959774867594b4b6f5a496876684e4151304241515151514e367178312b487a7758704c373859496b716c646a434341574d47436971470a534962345451454e41514977676746544d42414743797147534962345451454e41514942416745454d42414743797147534962345451454e41514943416745450a4d42414743797147534962345451454e41514944416745434d42414743797147534962345451454e41514945416745434d42414743797147534962345451454e0a41514946416745454d42414743797147534962345451454e41514947416745424d42414743797147534962345451454e41514948416745414d424147437971470a534962345451454e41514949416745464d42414743797147534962345451454e4151494a416745414d42414743797147534962345451454e4151494b416745410a4d42414743797147534962345451454e4151494c416745414d42414743797147534962345451454e4151494d416745414d42414743797147534962345451454e0a4151494e416745414d42414743797147534962345451454e4151494f416745414d42414743797147534962345451454e41514950416745414d424147437971470a534962345451454e41514951416745414d42414743797147534962345451454e415149524167454e4d42384743797147534962345451454e41514953424241450a42414943424145414251414141414141414141414d42414743697147534962345451454e41514d45416741414d42514743697147534962345451454e415151450a42704441627741414144415042676f71686b69472b45304244514546436745424d42344743697147534962345451454e4151594545464a37386f7137314543670a6c7536335265417a675430775241594b4b6f5a496876684e41513042427a41324d42414743797147534962345451454e415163424151482f4d424147437971470a534962345451454e41516343415145414d42414743797147534962345451454e415163444151482f4d416f4743437147534d343942414d43413067414d4555430a494778676472434e7a344753716d32647a4c45533874757663717230444d692b427537533771537133325343416945417439454f6377584f6a31484a4c4462750a6d473357414549577962624f61635959612b7253384366526c514d3d0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d4949436c6a4343416a32674177494241674956414a567658633239472b487051456e4a3150517a7a674658433935554d416f4743437147534d343942414d430a4d476778476a415942674e5642414d4d45556c756447567349464e48574342536232393049454e424d526f77474159445651514b4442464a626e526c624342440a62334a7762334a6864476c76626a45554d424947413155454277774c553246756447456751327868636d4578437a414a42674e564241674d416b4e424d5173770a435159445651514745774a56557a4165467730784f4441314d6a45784d4455774d5442614677307a4d7a41314d6a45784d4455774d5442614d484178496a41670a42674e5642414d4d47556c756447567349464e4857434251513073675547786864475a76636d306751304578476a415942674e5642416f4d45556c75644756730a49454e76636e4276636d4630615739754d5251774567594456515148444174545957353059534244624746795954454c4d416b474131554543417743513045780a437a414a42674e5642415954416c56544d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a304441516344516741454e53422f377432316c58534f0a3243757a7078773734654a423732457944476757357258437478327456544c7136684b6b367a2b5569525a436e71523770734f766771466553786c6d546c4a6c0a65546d693257597a33714f42757a43427544416642674e5648534d4547444157674251695a517a575770303069664f44744a5653763141624f536347724442530a42674e5648523845537a424a4d45656752614244686b466f64485277637a6f764c324e6c636e52705a6d6c6a5958526c63793530636e567a6447566b633256790a646d6c6a5a584d75615735305a577775593239744c306c756447567355306459556d397664454e424c6d526c636a416442674e5648513445466751556c5739640a7a62306234656c4153636e553944504f4156634c336c517744675944565230504151482f42415144416745474d42494741315564457745422f7751494d4159420a4166384341514177436759494b6f5a497a6a30454177494452774177524149675873566b6930772b6936565947573355462f32327561586530594a446a3155650a6e412b546a44316169356343494359623153416d4435786b66545670766f34556f79695359787244574c6d5552344349394e4b7966504e2b0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d4949436a7a4343416a53674177494241674955496d554d316c71644e496e7a6737535655723951477a6b6e42717777436759494b6f5a497a6a3045417749770a614445614d4267474131554541777752535735305a5777675530645949464a766233516751304578476a415942674e5642416f4d45556c756447567349454e760a636e4276636d4630615739754d5251774567594456515148444174545957353059534244624746795954454c4d416b47413155454341774351304578437a414a0a42674e5642415954416c56544d423458445445344d4455794d5445774e4455784d466f58445451354d54497a4d54497a4e546b314f566f77614445614d4267470a4131554541777752535735305a5777675530645949464a766233516751304578476a415942674e5642416f4d45556c756447567349454e76636e4276636d46300a615739754d5251774567594456515148444174545957353059534244624746795954454c4d416b47413155454341774351304578437a414a42674e56424159540a416c56544d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a3044415163445167414543366e45774d4449595a4f6a2f69505773437a61454b69370a314f694f534c52466857476a626e42564a66566e6b59347533496a6b4459594c304d784f346d717379596a6c42616c54565978465032734a424b357a6c4b4f420a757a43427544416642674e5648534d4547444157674251695a517a575770303069664f44744a5653763141624f5363477244425342674e5648523845537a424a0a4d45656752614244686b466f64485277637a6f764c324e6c636e52705a6d6c6a5958526c63793530636e567a6447566b63325679646d6c6a5a584d75615735300a5a577775593239744c306c756447567355306459556d397664454e424c6d526c636a416442674e564851344546675155496d554d316c71644e496e7a673753560a55723951477a6b6e4271777744675944565230504151482f42415144416745474d42494741315564457745422f7751494d4159424166384341514577436759490a4b6f5a497a6a3045417749445351417752674968414f572f35516b522b533943695344634e6f6f774c7550524c735747662f59693747535839344267775477670a41694541344a306c72486f4d732b586f356f2f7358364f39515778485241765a55474f6452513763767152586171493d0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000a000000c0095d04cf26fe03aef6e3561fa24c1aa1cea93f4aeaf563b1f9f7616184c53454875925759434769cec2490acb563a3372c616370692d6c6f6164657224414350492044415441000000000a000000c08d9a4d4777a1bc77ecd9d8d37a4628129a80052a510320159a20a923bd07a0e90d8d1f2e1ebf088992b25f0d0fa672ef24616370692d7273647024414350492044415441000000000a000000c03070721e169bc41884724cb0e6b3082e1baf249083d8b389181ba50b9afa951057876c380b8870e8c2facf2eff67a2b62c616370692d7461626c6573244143504920444154410300000001000008004073797374656d2d707265706172696e6700030000000100000800186170702d69645086b0e55f2fa8e4fb69d890f14f54d5612707646e03000000010000080030636f6d706f73652d686173688086b0e55f2fa8e4fb69d890f14f54d5612707646e2573d54e0d2ddaaade77caa90300000001000008002c696e7374616e63652d696450050bf89570575fe8fab4cb8f0a62a9e64efe8ead03000000010000080030626f6f742d6d722d646f6e6500030000000100000800346f732d696d6167652d686173688007a2388c7a6a1b6a646d443f1517990a4ec294471d63146cda9d56972765051d030000000100000800306b65792d70726f766964657231037b226e616d65223a226b6d73222c226964223a223330353933303133303630373261383634386365336430323031303630383261383634386365336430333031303730333432303030343266373165323334643733333961316365616361303963336333393165623831366335333366393830616461616233346631366561643039336666306163313030643963303332353361333035366636643237373335313235343333313830623365363163353461373866336664313333333738363965303035316465653036227d0300000001000008002873746f726167652d66730c7a66730300000001000008003073797374656d2d726561647900244073797374656d2d707265706172696e6700186170702d69645086b0e55f2fa8e4fb69d890f14f54d5612707646e30636f6d706f73652d686173688086b0e55f2fa8e4fb69d890f14f54d5612707646e2573d54e0d2ddaaade77caa92c696e7374616e63652d696450050bf89570575fe8fab4cb8f0a62a9e64efe8ead30626f6f742d6d722d646f6e6500346f732d696d6167652d686173688007a2388c7a6a1b6a646d443f1517990a4ec294471d63146cda9d56972765051d306b65792d70726f766964657231037b226e616d65223a226b6d73222c226964223a223330353933303133303630373261383634386365336430323031303630383261383634386365336430333031303730333432303030343266373165323334643733333961316365616361303963336333393165623831366335333366393830616461616233346631366561643039336666306163313030643963303332353361333035366636643237373335313235343333313830623365363163353461373866336664313333333738363965303035316465653036227d2873746f726167652d66730c7a66733073797374656d2d726561647900646970313a3a736563703235366b31632d706b3a41353570576d74654a494a4f6a385f7049372d707a654478793147327131384744763838484e526442586b514d107b226f735f696d6167655f68617368223a2265366635636665633230633032653762393762616132313364306637313830323062353565303430313732643930636362636239343664353663386230396462222c226370755f636f756e74223a322c226d656d6f72795f73697a65223a323134373438333634382c2271656d755f76657273696f6e223a22382e322e32222c227063695f686f6c6536345f73697a65223a302c22687567657061676573223a66616c73652c226e756d5f67707573223a302c226e756d5f6e767377697463686573223a302c22686f74706c75675f6f6666223a66616c73652c22696d616765223a2264737461636b2d302e362e30222c22686f73745f73686172655f6d6f6465223a223970222c226f766d665f76617269616e74223a22707265323032353035222c227464785f6174746573746174696f6e5f76617269616e74223a226c697465222c227464785f6d6561737572656d656e74223a7b226d6561737572656d656e74223a226f3264325a584a7a61573975413256706257466e5a614e755932316b62476c755a56397a6147457a4f4452594d486869674951726332516f656a70773257392b4d4a4a5368587672526673666b54464b4c71686a32777263424d6844487376796d705a6b425742474d61577175484e725a584a755a577866595856306147567564476c6a6232526c57444373666d4d747a317a536f66356348304830326267686c58446d5474504745446a3976795641546d39554c2f3158386e6138554859776676723467756257515864746157357064484a6b58334e6f59544d344e466777542b54336351453070683139377a5636335772464339762b376c4179704d45414e3134676368622f35436f37315949724a4f5a352b5255422f2f6556754255685a48526b646d616a5a47393262575a7063484a6c4d6a41794e5441315a4731796447536961334e70626d64735a56397759584e7a5744436d3871795555594547687154624a5a2f6f2b6c51343345705976616e394c317366734a4b444e584256414e4b6146636b6a683046714c314c64334f6d63672f686f644864765833426863334e594d50316f56534c4f6552332b396e5155595536776651503842364d73576d627a596f697a4b6471354b334a4c4657544850554e762b35366f52496a464773576878575a305a46396f62324a4d6742414a424141474351494c41684151222c22636865636b73756d5f66696c65223a224f474d355a546b314e575977596a55334e324e6a4f446b304e5745344f5446684d7a526a59325a684e5749345a544134596d49315a54566b4d6a51354f546b794e6a417a5a5459305a4751784e4467334d47557a5979416762575668633356795a57316c626e5175644752344c6d4e696233494b227d2c22737065635f76657273696f6e223a317d" + "attestation": "0000394e040002008100000000000000939a7233f79c4ca9940a0db3957f06071026ff2bbebac59cc1ef911279d9481b000000000c010400000000000000000000000000d0d80c085166ba78ccc69af268e5753cf0f3394523cb4ff7c50b08d9265c82489c099c377be6a400e4d2b57da924012c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000e702060000000000fd685522ce791dfef67414614eb07d03fc07a32c5a66f36288b329dab92b724b1564c73d436ffb9ea84488c51ac5a1c50186b0e55f2fa8e4fb69d890f14f54d5612707646e2573d54e0d2ddaaade77caa9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f8438db36b96f85d8752ff7f24a89ec05c79ec9eda2ba732c897fb970ca429365b7471b1c054cb84f17b1c2b23ba66402023546e7f3b9d1228e274f70c44d481162540f8452544520a796a52f06879709b81a824a26792a7822327504b0d2aee4c1b739ed451a637b0f82642e48a5ea83925d23633c72e7385c8e9aca4175e133ed1625b7d92eb39edf509c27ff392dc6f24c170d0fd63fc2b1b53202eea47b013978437fa6982cf5e0438ff95c208994aaa0f4ebab2e3a66824b5b56869137e646970313a3a736563703235366b31632d706b3a41353570576d74654a494a4f6a385f7049372d707a654478793147327131384744763838484e526442586b51cc1000008bca152d0454bdfd5adab1bc3a527884f77ea7993d32ee0e4426b2ae0fe42bf3f5642d6abd763b4f4c6042133e2ed79cce743f2c54ff4c7ea5d712dc1172ec244fe5b32ac6ffeb104614bcb8894c7aaafbbbe6f6bfd852f5dcd6cf400557ee764e62850d955975d93eff63b17e6e13e329a7bb13926706c0430017d543ab01920600461000000404191b04ff0006000000000000000000000000000000000000000000000000000000000000000000000000000000001500000000000000e700000000000000e5a3a7b5d830c2953b98534c6c59a3a34fdc34e933f7f5898f0a85cf08846bca0000000000000000000000000000000000000000000000000000000000000000dc9e2a7c6f948f17474e34a7fc43ed030f7c1563f1babddf6340c82e0e54a8c500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c1140324365c08f021a721dbe9175cb89dcd2235e2bd00bfb235b2a66b8c783600000000000000000000000000000000000000000000000000000000000000002af8cd12d44e0d22f904b15c02968b57b668e7f2487ba308e1d9a269ea125e48b243f7d32bb8551e1e3c2c09bd2162d36941eeb47be50b9b55a766a14d0cfe302000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f05005e0e00002d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d49494538444343424a6167417749424167495556706163774c766c316d476155506b384b4375504141334769465177436759494b6f5a497a6a3045417749770a634445694d434147413155454177775a535735305a577767553064594946424453794251624746305a6d397962534244515445614d42674741315545436777520a535735305a577767513239796347397959585270623234784644415342674e564241634d43314e68626e526849454e7359584a684d51737743515944565151490a44414a445154454c4d416b474131554542684d4356564d774868634e4d6a59774e4445314d4441314d4455345768634e4d7a4d774e4445314d4441314d4455340a576a42774d534977494159445651514444426c4a626e526c624342545231676755454e4c49454e6c636e52705a6d6c6a5958526c4d526f77474159445651514b0a4442464a626e526c6243424462334a7762334a6864476c76626a45554d424947413155454277774c553246756447456751327868636d4578437a414a42674e560a4241674d416b4e424d517377435159445651514745774a56557a425a4d424d4742797147534d34394167454743437147534d343941774548413049414245586a0a53374265726c3262726b65543677707878436a556536564775577268586e51767a41395862524768356b68637671766b566b427874715935475759544f6551340a5948496a636b7974734c6c5531774b594a74576a67674d4d4d4949444344416642674e5648534d4547444157674253566231334e765276683655424a796454300a4d383442567776655644427242674e56485238455a4442694d47436758714263686c706f64485277637a6f764c32467761533530636e567a6447566b633256790a646d6c6a5a584d75615735305a577775593239744c334e6e6543396a5a584a3061575a7059324630615739754c3359304c33426a61324e796244396a595431770a624746305a6d397962535a6c626d4e765a476c755a7a316b5a584977485159445652304f42425945464362386b6b73714d364c384f6765734c713943337339440a7a5333504d41344741315564447745422f775145417749477744414d42674e5648524d4241663845416a41414d4949434f51594a4b6f5a496876684e415130420a424949434b6a4343416959774867594b4b6f5a496876684e4151304241515151514e367178312b487a7758704c373859496b716c646a434341574d47436971470a534962345451454e41514977676746544d42414743797147534962345451454e41514942416745454d42414743797147534962345451454e41514943416745450a4d42414743797147534962345451454e41514944416745434d42414743797147534962345451454e41514945416745434d42414743797147534962345451454e0a41514946416745454d42414743797147534962345451454e41514947416745424d42414743797147534962345451454e41514948416745414d424147437971470a534962345451454e41514949416745464d42414743797147534962345451454e4151494a416745414d42414743797147534962345451454e4151494b416745410a4d42414743797147534962345451454e4151494c416745414d42414743797147534962345451454e4151494d416745414d42414743797147534962345451454e0a4151494e416745414d42414743797147534962345451454e4151494f416745414d42414743797147534962345451454e41514950416745414d424147437971470a534962345451454e41514951416745414d42414743797147534962345451454e415149524167454e4d42384743797147534962345451454e41514953424241450a42414943424145414251414141414141414141414d42414743697147534962345451454e41514d45416741414d42514743697147534962345451454e415151450a42704441627741414144415042676f71686b69472b45304244514546436745424d42344743697147534962345451454e4151594545464a37386f7137314543670a6c7536335265417a675430775241594b4b6f5a496876684e41513042427a41324d42414743797147534962345451454e415163424151482f4d424147437971470a534962345451454e41516343415145414d42414743797147534962345451454e415163444151482f4d416f4743437147534d343942414d43413067414d4555430a494778676472434e7a344753716d32647a4c45533874757663717230444d692b427537533771537133325343416945417439454f6377584f6a31484a4c4462750a6d473357414549577962624f61635959612b7253384366526c514d3d0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d4949436c6a4343416a32674177494241674956414a567658633239472b487051456e4a3150517a7a674658433935554d416f4743437147534d343942414d430a4d476778476a415942674e5642414d4d45556c756447567349464e48574342536232393049454e424d526f77474159445651514b4442464a626e526c624342440a62334a7762334a6864476c76626a45554d424947413155454277774c553246756447456751327868636d4578437a414a42674e564241674d416b4e424d5173770a435159445651514745774a56557a4165467730784f4441314d6a45784d4455774d5442614677307a4d7a41314d6a45784d4455774d5442614d484178496a41670a42674e5642414d4d47556c756447567349464e4857434251513073675547786864475a76636d306751304578476a415942674e5642416f4d45556c75644756730a49454e76636e4276636d4630615739754d5251774567594456515148444174545957353059534244624746795954454c4d416b474131554543417743513045780a437a414a42674e5642415954416c56544d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a304441516344516741454e53422f377432316c58534f0a3243757a7078773734654a423732457944476757357258437478327456544c7136684b6b367a2b5569525a436e71523770734f766771466553786c6d546c4a6c0a65546d693257597a33714f42757a43427544416642674e5648534d4547444157674251695a517a575770303069664f44744a5653763141624f536347724442530a42674e5648523845537a424a4d45656752614244686b466f64485277637a6f764c324e6c636e52705a6d6c6a5958526c63793530636e567a6447566b633256790a646d6c6a5a584d75615735305a577775593239744c306c756447567355306459556d397664454e424c6d526c636a416442674e5648513445466751556c5739640a7a62306234656c4153636e553944504f4156634c336c517744675944565230504151482f42415144416745474d42494741315564457745422f7751494d4159420a4166384341514177436759494b6f5a497a6a30454177494452774177524149675873566b6930772b6936565947573355462f32327561586530594a446a3155650a6e412b546a44316169356343494359623153416d4435786b66545670766f34556f79695359787244574c6d5552344349394e4b7966504e2b0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d4949436a7a4343416a53674177494241674955496d554d316c71644e496e7a6737535655723951477a6b6e42717777436759494b6f5a497a6a3045417749770a614445614d4267474131554541777752535735305a5777675530645949464a766233516751304578476a415942674e5642416f4d45556c756447567349454e760a636e4276636d4630615739754d5251774567594456515148444174545957353059534244624746795954454c4d416b47413155454341774351304578437a414a0a42674e5642415954416c56544d423458445445344d4455794d5445774e4455784d466f58445451354d54497a4d54497a4e546b314f566f77614445614d4267470a4131554541777752535735305a5777675530645949464a766233516751304578476a415942674e5642416f4d45556c756447567349454e76636e4276636d46300a615739754d5251774567594456515148444174545957353059534244624746795954454c4d416b47413155454341774351304578437a414a42674e56424159540a416c56544d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a3044415163445167414543366e45774d4449595a4f6a2f69505773437a61454b69370a314f694f534c52466857476a626e42564a66566e6b59347533496a6b4459594c304d784f346d717379596a6c42616c54565978465032734a424b357a6c4b4f420a757a43427544416642674e5648534d4547444157674251695a517a575770303069664f44744a5653763141624f5363477244425342674e5648523845537a424a0a4d45656752614244686b466f64485277637a6f764c324e6c636e52705a6d6c6a5958526c63793530636e567a6447566b63325679646d6c6a5a584d75615735300a5a577775593239744c306c756447567355306459556d397664454e424c6d526c636a416442674e564851344546675155496d554d316c71644e496e7a673753560a55723951477a6b6e4271777744675944565230504151482f42415144416745474d42494741315564457745422f7751494d4159424166384341514577436759490a4b6f5a497a6a3045417749445351417752674968414f572f35516b522b533943695344634e6f6f774c7550524c735747662f59693747535839344267775477670a41694541344a306c72486f4d732b586f356f2f7358364f39515778485241765a55474f6452513763767152586171493d0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000a000000c0095d04cf26fe03aef6e3561fa24c1aa1cea93f4aeaf563b1f9f7616184c53454875925759434769cec2490acb563a3372c616370692d6c6f6164657224414350492044415441000000000a000000c08d9a4d4777a1bc77ecd9d8d37a4628129a80052a510320159a20a923bd07a0e90d8d1f2e1ebf088992b25f0d0fa672ef24616370692d7273647024414350492044415441000000000a000000c03070721e169bc41884724cb0e6b3082e1baf249083d8b389181ba50b9afa951057876c380b8870e8c2facf2eff67a2b62c616370692d7461626c6573244143504920444154410300000001000008004073797374656d2d707265706172696e6700030000000100000800186170702d69645086b0e55f2fa8e4fb69d890f14f54d5612707646e03000000010000080030636f6d706f73652d686173688086b0e55f2fa8e4fb69d890f14f54d5612707646e2573d54e0d2ddaaade77caa90300000001000008002c696e7374616e63652d696450050bf89570575fe8fab4cb8f0a62a9e64efe8ead03000000010000080030626f6f742d6d722d646f6e6500030000000100000800346f732d696d6167652d686173688007a2388c7a6a1b6a646d443f1517990a4ec294471d63146cda9d56972765051d030000000100000800306b65792d70726f766964657231037b226e616d65223a226b6d73222c226964223a223330353933303133303630373261383634386365336430323031303630383261383634386365336430333031303730333432303030343266373165323334643733333961316365616361303963336333393165623831366335333366393830616461616233346631366561643039336666306163313030643963303332353361333035366636643237373335313235343333313830623365363163353461373866336664313333333738363965303035316465653036227d0300000001000008002873746f726167652d66730c7a66730300000001000008003073797374656d2d726561647900244073797374656d2d707265706172696e6700186170702d69645086b0e55f2fa8e4fb69d890f14f54d5612707646e30636f6d706f73652d686173688086b0e55f2fa8e4fb69d890f14f54d5612707646e2573d54e0d2ddaaade77caa92c696e7374616e63652d696450050bf89570575fe8fab4cb8f0a62a9e64efe8ead30626f6f742d6d722d646f6e6500346f732d696d6167652d686173688007a2388c7a6a1b6a646d443f1517990a4ec294471d63146cda9d56972765051d306b65792d70726f766964657231037b226e616d65223a226b6d73222c226964223a223330353933303133303630373261383634386365336430323031303630383261383634386365336430333031303730333432303030343266373165323334643733333961316365616361303963336333393165623831366335333366393830616461616233346631366561643039336666306163313030643963303332353361333035366636643237373335313235343333313830623365363163353461373866336664313333333738363965303035316465653036227d2873746f726167652d66730c7a66733073797374656d2d726561647900646970313a3a736563703235366b31632d706b3a41353570576d74654a494a4f6a385f7049372d707a654478793147327131384744763838484e526442586b51fd117b226f735f696d6167655f68617368223a2233613064666332373965396461386361366233363539313032363838333739656561323865373961303035623833613937653362303538333361326162613032222c226370755f636f756e74223a322c226d656d6f72795f73697a65223a323134373438333634382c2271656d755f76657273696f6e223a22382e322e32222c227063695f686f6c6536345f73697a65223a302c22687567657061676573223a66616c73652c226e756d5f67707573223a302c226e756d5f6e767377697463686573223a302c22686f74706c75675f6f6666223a66616c73652c22696d616765223a2264737461636b2d302e362e30222c22686f73745f73686172655f6d6f6465223a223970222c226f766d665f76617269616e74223a22707265323032353035222c227464785f6174746573746174696f6e5f76617269616e74223a226c697465222c227464785f6d6561737572656d656e74223a7b226d6561737572656d656e74223a226f3264325a584a7a61573975424756706257466e5a6152755932316b62476c755a56397a6147457a4f4452594d486869674951726332516f656a70773257392b4d4a4a5368587672526673666b54464b4c71686a32777263424d6844487376796d705a6b425742474d61577175484e725a584a755a577866595856306147567564476c6a6232526c5744414135533451376165492f4d5a3657715a31687176346e3434483851464236545234316c2f44496a554147317561412f4643346b4f55742b7a69466b6a6e315268344733426864474e6f5a57526661325679626d5673583246316447686c626e52705932396b5a5667777248356a4c6339633071482b58423942394e6d34495a5677356b3754786841342f62386c5145357656432f39562f4a32764642324d4837362b494c6d316b463362576c75615852795a46397a6147457a4f4452594d452f6b393345424e4b59646665383165743171785176622f7535514d71544241446465494849572f2b51714f3957434b79546d65666b5641662f336c626756495752305a485a6d6f325276646d316d615842795a5449774d6a55774e575274636e526b6f6d747a6157356e624756666347467a6331677770764b736c464742426f616b32795766365070554f4e784b574c32702f53396248374353677a5677565144536d68584a493464426169395333647a706e495034614852336231397759584e7a57444439614655697a6e6b642f765a304647464f734830442f41656a4c46706d38324b4973796e61755374795378566b787a3144622f756571455349785272466f63566d6447526661473969544941514351514142676b434377495145413d3d222c22636865636b73756d5f66696c65223a225a4445794e44497a4e7a63354d6a4e684e3245354e6d4a6a4d32597a5a474a684d446c684f44426a4e324a6d5a5441345a4441794f5751315a6a41334f5459774e5745324e6d4d345a6d49354f4759324d4459344e69416762575668633356795a57316c626e5175644752344c6d4e696233494b227d2c22737065635f76657273696f6e223a317d" } diff --git a/dstack/verifier/fixtures/tdx-lite-getquote.json b/dstack/verifier/fixtures/tdx-lite-getquote.json index 57a30c837..4bc79eb7f 100644 --- a/dstack/verifier/fixtures/tdx-lite-getquote.json +++ b/dstack/verifier/fixtures/tdx-lite-getquote.json @@ -2,5 +2,5 @@ "quote": "040002008100000000000000939a7233f79c4ca9940a0db3957f06071026ff2bbebac59cc1ef911279d9481b000000000c010400000000000000000000000000d0d80c085166ba78ccc69af268e5753cf0f3394523cb4ff7c50b08d9265c82489c099c377be6a400e4d2b57da924012c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000e702060000000000fd685522ce791dfef67414614eb07d03fc07a32c5a66f36288b329dab92b724b1564c73d436ffb9ea84488c51ac5a1c50186b0e55f2fa8e4fb69d890f14f54d5612707646e2573d54e0d2ddaaade77caa9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f8438db36b96f85d8752ff7f24a89ec05c79ec9eda2ba732c897fb970ca429365b7471b1c054cb84f17b1c2b23ba66402023546e7f3b9d1228e274f70c44d481162540f8452544520a796a52f06879709b81a824a26792a7822327504b0d2aee4c1b739ed451a637b0f82642e48a5ea83925d23633c72e7385c8e9aca4175e133ed1625b7d92eb39edf509c27ff392dc6f24c170d0fd63fc2b1b53202eea47b013978437fa6982cf5e0438ff95c208994aaa0f4ebab2e3a66824b5b56869137e646970313a3a736563703235366b31632d706b3a41353570576d74654a494a4f6a385f7049372d707a654478793147327131384744763838484e526442586b51cc1000008bca152d0454bdfd5adab1bc3a527884f77ea7993d32ee0e4426b2ae0fe42bf3f5642d6abd763b4f4c6042133e2ed79cce743f2c54ff4c7ea5d712dc1172ec244fe5b32ac6ffeb104614bcb8894c7aaafbbbe6f6bfd852f5dcd6cf400557ee764e62850d955975d93eff63b17e6e13e329a7bb13926706c0430017d543ab01920600461000000404191b04ff0006000000000000000000000000000000000000000000000000000000000000000000000000000000001500000000000000e700000000000000e5a3a7b5d830c2953b98534c6c59a3a34fdc34e933f7f5898f0a85cf08846bca0000000000000000000000000000000000000000000000000000000000000000dc9e2a7c6f948f17474e34a7fc43ed030f7c1563f1babddf6340c82e0e54a8c500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c1140324365c08f021a721dbe9175cb89dcd2235e2bd00bfb235b2a66b8c783600000000000000000000000000000000000000000000000000000000000000002af8cd12d44e0d22f904b15c02968b57b668e7f2487ba308e1d9a269ea125e48b243f7d32bb8551e1e3c2c09bd2162d36941eeb47be50b9b55a766a14d0cfe302000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f05005e0e00002d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d49494538444343424a6167417749424167495556706163774c766c316d476155506b384b4375504141334769465177436759494b6f5a497a6a3045417749770a634445694d434147413155454177775a535735305a577767553064594946424453794251624746305a6d397962534244515445614d42674741315545436777520a535735305a577767513239796347397959585270623234784644415342674e564241634d43314e68626e526849454e7359584a684d51737743515944565151490a44414a445154454c4d416b474131554542684d4356564d774868634e4d6a59774e4445314d4441314d4455345768634e4d7a4d774e4445314d4441314d4455340a576a42774d534977494159445651514444426c4a626e526c624342545231676755454e4c49454e6c636e52705a6d6c6a5958526c4d526f77474159445651514b0a4442464a626e526c6243424462334a7762334a6864476c76626a45554d424947413155454277774c553246756447456751327868636d4578437a414a42674e560a4241674d416b4e424d517377435159445651514745774a56557a425a4d424d4742797147534d34394167454743437147534d343941774548413049414245586a0a53374265726c3262726b65543677707878436a556536564775577268586e51767a41395862524768356b68637671766b566b427874715935475759544f6551340a5948496a636b7974734c6c5531774b594a74576a67674d4d4d4949444344416642674e5648534d4547444157674253566231334e765276683655424a796454300a4d383442567776655644427242674e56485238455a4442694d47436758714263686c706f64485277637a6f764c32467761533530636e567a6447566b633256790a646d6c6a5a584d75615735305a577775593239744c334e6e6543396a5a584a3061575a7059324630615739754c3359304c33426a61324e796244396a595431770a624746305a6d397962535a6c626d4e765a476c755a7a316b5a584977485159445652304f42425945464362386b6b73714d364c384f6765734c713943337339440a7a5333504d41344741315564447745422f775145417749477744414d42674e5648524d4241663845416a41414d4949434f51594a4b6f5a496876684e415130420a424949434b6a4343416959774867594b4b6f5a496876684e4151304241515151514e367178312b487a7758704c373859496b716c646a434341574d47436971470a534962345451454e41514977676746544d42414743797147534962345451454e41514942416745454d42414743797147534962345451454e41514943416745450a4d42414743797147534962345451454e41514944416745434d42414743797147534962345451454e41514945416745434d42414743797147534962345451454e0a41514946416745454d42414743797147534962345451454e41514947416745424d42414743797147534962345451454e41514948416745414d424147437971470a534962345451454e41514949416745464d42414743797147534962345451454e4151494a416745414d42414743797147534962345451454e4151494b416745410a4d42414743797147534962345451454e4151494c416745414d42414743797147534962345451454e4151494d416745414d42414743797147534962345451454e0a4151494e416745414d42414743797147534962345451454e4151494f416745414d42414743797147534962345451454e41514950416745414d424147437971470a534962345451454e41514951416745414d42414743797147534962345451454e415149524167454e4d42384743797147534962345451454e41514953424241450a42414943424145414251414141414141414141414d42414743697147534962345451454e41514d45416741414d42514743697147534962345451454e415151450a42704441627741414144415042676f71686b69472b45304244514546436745424d42344743697147534962345451454e4151594545464a37386f7137314543670a6c7536335265417a675430775241594b4b6f5a496876684e41513042427a41324d42414743797147534962345451454e415163424151482f4d424147437971470a534962345451454e41516343415145414d42414743797147534962345451454e415163444151482f4d416f4743437147534d343942414d43413067414d4555430a494778676472434e7a344753716d32647a4c45533874757663717230444d692b427537533771537133325343416945417439454f6377584f6a31484a4c4462750a6d473357414549577962624f61635959612b7253384366526c514d3d0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d4949436c6a4343416a32674177494241674956414a567658633239472b487051456e4a3150517a7a674658433935554d416f4743437147534d343942414d430a4d476778476a415942674e5642414d4d45556c756447567349464e48574342536232393049454e424d526f77474159445651514b4442464a626e526c624342440a62334a7762334a6864476c76626a45554d424947413155454277774c553246756447456751327868636d4578437a414a42674e564241674d416b4e424d5173770a435159445651514745774a56557a4165467730784f4441314d6a45784d4455774d5442614677307a4d7a41314d6a45784d4455774d5442614d484178496a41670a42674e5642414d4d47556c756447567349464e4857434251513073675547786864475a76636d306751304578476a415942674e5642416f4d45556c75644756730a49454e76636e4276636d4630615739754d5251774567594456515148444174545957353059534244624746795954454c4d416b474131554543417743513045780a437a414a42674e5642415954416c56544d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a304441516344516741454e53422f377432316c58534f0a3243757a7078773734654a423732457944476757357258437478327456544c7136684b6b367a2b5569525a436e71523770734f766771466553786c6d546c4a6c0a65546d693257597a33714f42757a43427544416642674e5648534d4547444157674251695a517a575770303069664f44744a5653763141624f536347724442530a42674e5648523845537a424a4d45656752614244686b466f64485277637a6f764c324e6c636e52705a6d6c6a5958526c63793530636e567a6447566b633256790a646d6c6a5a584d75615735305a577775593239744c306c756447567355306459556d397664454e424c6d526c636a416442674e5648513445466751556c5739640a7a62306234656c4153636e553944504f4156634c336c517744675944565230504151482f42415144416745474d42494741315564457745422f7751494d4159420a4166384341514177436759494b6f5a497a6a30454177494452774177524149675873566b6930772b6936565947573355462f32327561586530594a446a3155650a6e412b546a44316169356343494359623153416d4435786b66545670766f34556f79695359787244574c6d5552344349394e4b7966504e2b0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d4949436a7a4343416a53674177494241674955496d554d316c71644e496e7a6737535655723951477a6b6e42717777436759494b6f5a497a6a3045417749770a614445614d4267474131554541777752535735305a5777675530645949464a766233516751304578476a415942674e5642416f4d45556c756447567349454e760a636e4276636d4630615739754d5251774567594456515148444174545957353059534244624746795954454c4d416b47413155454341774351304578437a414a0a42674e5642415954416c56544d423458445445344d4455794d5445774e4455784d466f58445451354d54497a4d54497a4e546b314f566f77614445614d4267470a4131554541777752535735305a5777675530645949464a766233516751304578476a415942674e5642416f4d45556c756447567349454e76636e4276636d46300a615739754d5251774567594456515148444174545957353059534244624746795954454c4d416b47413155454341774351304578437a414a42674e56424159540a416c56544d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a3044415163445167414543366e45774d4449595a4f6a2f69505773437a61454b69370a314f694f534c52466857476a626e42564a66566e6b59347533496a6b4459594c304d784f346d717379596a6c42616c54565978465032734a424b357a6c4b4f420a757a43427544416642674e5648534d4547444157674251695a517a575770303069664f44744a5653763141624f5363477244425342674e5648523845537a424a0a4d45656752614244686b466f64485277637a6f764c324e6c636e52705a6d6c6a5958526c63793530636e567a6447566b63325679646d6c6a5a584d75615735300a5a577775593239744c306c756447567355306459556d397664454e424c6d526c636a416442674e564851344546675155496d554d316c71644e496e7a673753560a55723951477a6b6e4271777744675944565230504151482f42415144416745474d42494741315564457745422f7751494d4159424166384341514577436759490a4b6f5a497a6a3045417749445351417752674968414f572f35516b522b533943695344634e6f6f774c7550524c735747662f59693747535839344267775477670a41694541344a306c72486f4d732b586f356f2f7358364f39515778485241765a55474f6452513763767152586171493d0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "event_log": "[{\"imr\":0,\"event_type\":2147483659,\"digest\":\"0b8772e5b0b41b83e6044a68397e02f49fb47066b4fbe4917ea2c45c64f323fdacbb37948f821ebaf8bc9c938ba8a749\",\"event\":\"\",\"event_payload\":\"\"},{\"imr\":0,\"event_type\":2147483658,\"digest\":\"344bc51c980ba621aaa00da3ed7436f7d6e549197dfe699515dfa2c6583d95e6412af21c097d473155875ffd561d6790\",\"event\":\"\",\"event_payload\":\"\"},{\"imr\":0,\"event_type\":2147483649,\"digest\":\"9dc3a1f80bcec915391dcda5ffbb15e7419f77eab462bbf72b42166fb70d50325e37b36f93537a863769bcf9bedae6fb\",\"event\":\"\",\"event_payload\":\"\"},{\"imr\":0,\"event_type\":2147483649,\"digest\":\"6f2e3cbc14f9def86980f5f66fd85e99d63e69a73014ed8a5633ce56eca5b64b692108c56110e22acadcef58c3250f1b\",\"event\":\"\",\"event_payload\":\"\"},{\"imr\":0,\"event_type\":2147483649,\"digest\":\"d607c0efb41c0d757d69bca0615c3a9ac0b1db06c557d992e906c6b7dee40e0e031640c7bfd7bcd35844ef9edeadc6f9\",\"event\":\"\",\"event_payload\":\"\"},{\"imr\":0,\"event_type\":2147483649,\"digest\":\"08a74f8963b337acb6c93682f934496373679dd26af1089cb4eaf0c30cf260a12e814856385ab8843e56a9acea19e127\",\"event\":\"\",\"event_payload\":\"\"},{\"imr\":0,\"event_type\":2147483649,\"digest\":\"18cc6e01f0c6ea99aa23f8a280423e94ad81d96d0aeb5180504fc0f7a40cb3619dd39bd6a95ec1680a86ed6ab0f9828d\",\"event\":\"\",\"event_payload\":\"\"},{\"imr\":0,\"event_type\":4,\"digest\":\"394341b7182cd227c5c6b07ef8000cdfd86136c4292b8e576573ad7ed9ae41019f5818b4b971c9effc60e1ad9f1289f0\",\"event\":\"\",\"event_payload\":\"\"},{\"imr\":0,\"event_type\":10,\"digest\":\"095d04cf26fe03aef6e3561fa24c1aa1cea93f4aeaf563b1f9f7616184c53454875925759434769cec2490acb563a337\",\"event\":\"acpi-loader\",\"event_payload\":\"414350492044415441\"},{\"imr\":0,\"event_type\":10,\"digest\":\"8d9a4d4777a1bc77ecd9d8d37a4628129a80052a510320159a20a923bd07a0e90d8d1f2e1ebf088992b25f0d0fa672ef\",\"event\":\"acpi-rsdp\",\"event_payload\":\"414350492044415441\"},{\"imr\":0,\"event_type\":10,\"digest\":\"3070721e169bc41884724cb0e6b3082e1baf249083d8b389181ba50b9afa951057876c380b8870e8c2facf2eff67a2b6\",\"event\":\"acpi-tables\",\"event_payload\":\"414350492044415441\"},{\"imr\":1,\"event_type\":2147483651,\"digest\":\"ac7e632dcf5cd2a1fe5c1f41f4d9b8219570e64ed3c61038fdbf25404e6f542ffd57f276bc5076307efaf882e6d64177\",\"event\":\"\",\"event_payload\":\"\"},{\"imr\":0,\"event_type\":2147483650,\"digest\":\"1dd6f7b457ad880d840d41c961283bab688e94e4b59359ea45686581e90feccea3c624b1226113f824f315eb60ae0a7c\",\"event\":\"\",\"event_payload\":\"\"},{\"imr\":0,\"event_type\":2147483650,\"digest\":\"23ada07f5261f12f34a0bd8e46760962d6b4d576a416f1fea1c64bc656b1d28eacf7047ae6e967c58fd2a98bfa74c298\",\"event\":\"\",\"event_payload\":\"\"},{\"imr\":1,\"event_type\":2147483655,\"digest\":\"77a0dab2312b4e1e57a84d865a21e5b2ee8d677a21012ada819d0a98988078d3d740f6346bfe0abaa938ca20439a8d71\",\"event\":\"\",\"event_payload\":\"\"},{\"imr\":1,\"event_type\":4,\"digest\":\"394341b7182cd227c5c6b07ef8000cdfd86136c4292b8e576573ad7ed9ae41019f5818b4b971c9effc60e1ad9f1289f0\",\"event\":\"\",\"event_payload\":\"\"},{\"imr\":2,\"event_type\":6,\"digest\":\"786280842b7364287a3a70d96f7e309252857beb45fb1f91314a2ea863db0adc04c8431ecbf29a966405604631a5aab8\",\"event\":\"\",\"event_payload\":\"\"},{\"imr\":2,\"event_type\":6,\"digest\":\"4fe4f7710134a61d7def357add6ac50bdbfeee5032a4c100375e207216ffe42a3bd5822b24e679f91501fff795b81521\",\"event\":\"\",\"event_payload\":\"\"},{\"imr\":1,\"event_type\":2147483655,\"digest\":\"214b0bef1379756011344877743fdc2a5382bac6e70362d624ccf3f654407c1b4badf7d8f9295dd3dabdef65b27677e0\",\"event\":\"\",\"event_payload\":\"\"},{\"imr\":1,\"event_type\":2147483655,\"digest\":\"0a2e01c85deae718a530ad8c6d20a84009babe6c8989269e950d8cf440c6e997695e64d455c4174a652cd080f6230b74\",\"event\":\"\",\"event_payload\":\"\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"\",\"event\":\"system-preparing\",\"event_payload\":\"\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"\",\"event\":\"app-id\",\"event_payload\":\"86b0e55f2fa8e4fb69d890f14f54d5612707646e\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"\",\"event\":\"compose-hash\",\"event_payload\":\"86b0e55f2fa8e4fb69d890f14f54d5612707646e2573d54e0d2ddaaade77caa9\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"\",\"event\":\"instance-id\",\"event_payload\":\"050bf89570575fe8fab4cb8f0a62a9e64efe8ead\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"\",\"event\":\"boot-mr-done\",\"event_payload\":\"\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"\",\"event\":\"os-image-hash\",\"event_payload\":\"07a2388c7a6a1b6a646d443f1517990a4ec294471d63146cda9d56972765051d\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"\",\"event\":\"key-provider\",\"event_payload\":\"7b226e616d65223a226b6d73222c226964223a223330353933303133303630373261383634386365336430323031303630383261383634386365336430333031303730333432303030343266373165323334643733333961316365616361303963336333393165623831366335333366393830616461616233346631366561643039336666306163313030643963303332353361333035366636643237373335313235343333313830623365363163353461373866336664313333333738363965303035316465653036227d\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"\",\"event\":\"storage-fs\",\"event_payload\":\"7a6673\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"\",\"event\":\"system-ready\",\"event_payload\":\"\"}]", "report_data": "646970313a3a736563703235366b31632d706b3a41353570576d74654a494a4f6a385f7049372d707a654478793147327131384744763838484e526442586b51", - "vm_config": "{\"os_image_hash\":\"e6f5cfec20c02e7b97baa213d0f718020b55e040172d90ccbcb946d56c8b09db\",\"cpu_count\":2,\"memory_size\":2147483648,\"qemu_version\":\"8.2.2\",\"pci_hole64_size\":0,\"hugepages\":false,\"num_gpus\":0,\"num_nvswitches\":0,\"hotplug_off\":false,\"image\":\"dstack-0.6.0\",\"host_share_mode\":\"9p\",\"ovmf_variant\":\"pre202505\",\"tdx_attestation_variant\":\"lite\",\"tdx_measurement\":{\"measurement\":\"o2d2ZXJzaW9uA2VpbWFnZaNuY21kbGluZV9zaGEzODRYMHhigIQrc2Qoejpw2W9+MJJShXvrRfsfkTFKLqhj2wrcBMhDHsvympZkBWBGMaWquHNrZXJuZWxfYXV0aGVudGljb2RlWDCsfmMtz1zSof5cH0H02bghlXDmTtPGEDj9vyVATm9UL/1X8na8UHYwfvr4gubWQXdtaW5pdHJkX3NoYTM4NFgwT+T3cQE0ph197zV63WrFC9v+7lAypMEAN14gchb/5Co71YIrJOZ5+RUB//eVuBUhZHRkdmajZG92bWZpcHJlMjAyNTA1ZG1ydGSia3NpbmdsZV9wYXNzWDCm8qyUUYEGhqTbJZ/o+lQ43EpYvan9L1sfsJKDNXBVANKaFckjh0FqL1Ld3Omcg/hodHdvX3Bhc3NYMP1oVSLOeR3+9nQUYU6wfQP8B6MsWmbzYoizKdq5K3JLFWTHPUNv+56oRIjFGsWhxWZ0ZF9ob2JMgBAJBAAGCQILAhAQ\",\"checksum_file\":\"OGM5ZTk1NWYwYjU3N2NjODk0NWE4OTFhMzRjY2ZhNWI4ZTA4YmI1ZTVkMjQ5OTkyNjAzZTY0ZGQxNDg3MGUzYyAgbWVhc3VyZW1lbnQudGR4LmNib3IK\"},\"spec_version\":1}" + "vm_config": "{\"os_image_hash\":\"3a0dfc279e9da8ca6b3659102688379eea28e79a005b83a97e3b05833a2aba02\",\"cpu_count\":2,\"memory_size\":2147483648,\"qemu_version\":\"8.2.2\",\"pci_hole64_size\":0,\"hugepages\":false,\"num_gpus\":0,\"num_nvswitches\":0,\"hotplug_off\":false,\"image\":\"dstack-0.6.0\",\"host_share_mode\":\"9p\",\"ovmf_variant\":\"pre202505\",\"tdx_attestation_variant\":\"lite\",\"tdx_measurement\":{\"measurement\":\"o2d2ZXJzaW9uBGVpbWFnZaRuY21kbGluZV9zaGEzODRYMHhigIQrc2Qoejpw2W9+MJJShXvrRfsfkTFKLqhj2wrcBMhDHsvympZkBWBGMaWquHNrZXJuZWxfYXV0aGVudGljb2RlWDAA5S4Q7aeI/MZ6WqZ1hqv4n44H8QFB6TR41l/DIjUAG1uaA/FC4kOUt+ziFkjn1Rh4G3BhdGNoZWRfa2VybmVsX2F1dGhlbnRpY29kZVgwrH5jLc9c0qH+XB9B9Nm4IZVw5k7TxhA4/b8lQE5vVC/9V/J2vFB2MH76+ILm1kF3bWluaXRyZF9zaGEzODRYME/k93EBNKYdfe81et1qxQvb/u5QMqTBADdeIHIW/+QqO9WCKyTmefkVAf/3lbgVIWR0ZHZmo2Rvdm1maXByZTIwMjUwNWRtcnRkomtzaW5nbGVfcGFzc1gwpvKslFGBBoak2yWf6PpUONxKWL2p/S9bH7CSgzVwVQDSmhXJI4dBai9S3dzpnIP4aHR3b19wYXNzWDD9aFUiznkd/vZ0FGFOsH0D/AejLFpm82KIsynauStySxVkxz1Db/ueqESIxRrFocVmdGRfaG9iTIAQCQQABgkCCwIQEA==\",\"checksum_file\":\"ZDEyNDIzNzc5MjNhN2E5NmJjM2YzZGJhMDlhODBjN2JmZTA4ZDAyOWQ1ZjA3OTYwNWE2NmM4ZmI5OGY2MDY4NiAgbWVhc3VyZW1lbnQudGR4LmNib3IK\"},\"spec_version\":1}" } diff --git a/dstack/verifier/fixtures/tdx-lite-qemu-10-2-attestation.json b/dstack/verifier/fixtures/tdx-lite-qemu-10-2-attestation.json new file mode 100644 index 000000000..22148b1fd --- /dev/null +++ b/dstack/verifier/fixtures/tdx-lite-qemu-10-2-attestation.json @@ -0,0 +1,3 @@ +{ + "attestation": "83a776657273696f6e01a8706c6174666f726d82a46b696e64a3746478a46461746182a571756f7465dc147b04000200cc8100000000000000cc93cc9a7233ccf7cc9c4ccca9cc940a0dccb3cc957f060731cc891a79ccb240ccaf16cc9d0029ccbdcc8228cce6ccb300000000090304000000000000000000000000002d2dcce1024616cc84ccf14ccc8d09cc84cca0cc9dcc895e3ecc9e15cc944ccce020cca03bcc977e1f114d5e1eccd32eccf666cca47fccd1cc9a5851ccb3cc800eccda3accfa00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000cce702060000000000cc87ccd2cc91ccaacce3cca54d4c1ccca7cc84ccbc1a68ccd02f3acce70c7bccc7ccec61ccb8ccdccca95ecc9169ccf12903ccd7ccd55c596fcca8ccc1cc9acc89603bccee046eccce7f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000131221cca6ccabccfe1dccfeccf41accc259cc8c1d731b44ccd1cc9270ccb3cc9059ccc5cc9eccdecc8acc82ccff2c18cca812ccaf58cccfcc90ccd00470cc85ccf2ccbfccaf54ccfb122974ccf00f634fcce914ccdccc875dccc2cc85ccd8ccefcce1ccc1ccecccda4cccf10e207f5accbd07cc9b55cc9accc5cce02fccfd64cceb75ccf03acce353cccbcce72f6eccbdccd9cceccca8cceacc91ccd5ccb853cc964f711fccd60c71ccd1ccd4cca96eccbecc975801610acc86cc9eccbeccdbcc95ccfcccaa372d4961377d13cc9a6f3a3b21ccc4ccac4352ccf70748ccb8ccab0626583dccabccd6586fccedccae473fcc84ccc73d09ccec30ccabccd816ccd26163096d19390a183f3fccffcce1ccb5ccbd26ccf7ccfbccd40dcc8246cce9ccf67fcc88ccf6ccdacca719ccfd2f5762ccec4f06ccfbcccfccc9cc8356cc9856cce85d7939ccf57fcce0cca5cc92695dccd3cc84cc93cca9cc9e002eccf1ccbfcc934251ccd3ccb4ccf5cc89cce90b7bccc31a79cce6cc9411cc93cc9165410fccfbcc8bccea2ccccc100000ccc7cc9714cccc240606175fcc9a0accb7ccfcccdb09cced2eccab332dccd040ccca0568cca5ccfc51cc9eccaf05cce85507cc896bccdbcca3ccc4cca6ccecccf05fcceccce942cca54dccdc2e67cccdccf9cce01ccce6ccc00bccbfcca81a41ccfeccdd264bcc8f68cc8eccbeccfbcce63e0dcccacc90ccecccbacce0cce5663dccd6ccd4cc9bccf6ccf942256e1acc8856ccea6f6811166a1acca4ccecccf8cc8c5d0c727138ccabccc9ccd00c1ccc8fcc852ccc832fcc927155ccbecce726cce9cc8fccb70600461000000406090905ccff0002000000000000000000000000000000000000000000000000000000000000000000000000000000001500000000000000cce700000000000000ccde1e1bccbbccc8ccedccef08283ecc8accd51ccce112ccb5ccb11accf473050828543a0c73ccb914cca33e710000000000000000000000000000000000000000000000000000000000000000ccdccc9e2a7c6fcc94cc8f17474e34cca7ccfc43cced030f7c1563ccf1ccbaccbdccdf6340ccc82e0e54cca8ccc500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ccd1ccf5ccf8593e4249ccffccd4ccc52b1370295ecc91ccbeccd7ccb9cc9124cc9acceb5fccb30dcc991eccbf47ccb7ccbf00000000000000000000000000000000000000000000000000000000000000002b07cc98ccf7073fccb2cc8fcca3ccb8ccc3cc9fcce47f4804ccb6ccb9ccfecc8d392203ccf74c0a67cceaccef7bccf4cc9a547bcc9f35cc9c0eccd1ccfa31cca23f5bcc81ccd576132d4149cca3ccf0cca0747ecc8cccea15035fccb9634d2000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f05005e0e00002d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d49494538544343424a616741774942416749554e3958714f6d566f6a6c424c7856354956514853636f494e41713077436759494b6f5a497a6a3045417749770a634445694d434147413155454177775a535735305a577767553064594946424453794251624746305a6d397962534244515445614d42674741315545436777520a535735305a577767513239796347397959585270623234784644415342674e564241634d43314e68626e526849454e7359584a684d51737743515944565151490a44414a445154454c4d416b474131554542684d4356564d774868634e4d6a59774f5441794d6a41774e6a55325768634e4d7a4d774f5441794d6a41774e6a55320a576a42774d534977494159445651514444426c4a626e526c624342545231676755454e4c49454e6c636e52705a6d6c6a5958526c4d526f77474159445651514b0a4442464a626e526c6243424462334a7762334a6864476c76626a45554d424947413155454277774c553246756447456751327868636d4578437a414a42674e560a4241674d416b4e424d517377435159445651514745774a56557a425a4d424d4742797147534d34394167454743437147534d3439417745484130494142504c6b0a2f7549366b6c4c496b787155332f434b674c5730446a2f6a4f6961443238686d4b6f462b3937473037546f395477707335555a2f44754e555546314658382f6c0a367049427a54734a3244314244547a5a6958536a67674d4d4d4949444344416642674e5648534d4547444157674253566231334e765276683655424a796454300a4d383442567776655644427242674e56485238455a4442694d47436758714263686c706f64485277637a6f764c32467761533530636e567a6447566b633256790a646d6c6a5a584d75615735305a577775593239744c334e6e6543396a5a584a3061575a7059324630615739754c3359304c33426a61324e796244396a595431770a624746305a6d397962535a6c626d4e765a476c755a7a316b5a584977485159445652304f42425945464a72566a75346632416755614d5636526742544a456e370a6e5936394d41344741315564447745422f775145417749477744414d42674e5648524d4241663845416a41414d4949434f51594a4b6f5a496876684e415130420a424949434b6a4343416959774867594b4b6f5a496876684e415130424151515156613032666977465771502f456d6579464939467a44434341574d47436971470a534962345451454e41514977676746544d42414743797147534962345451454e41514942416745454d42414743797147534962345451454e41514943416745450a4d42414743797147534962345451454e41514944416745434d42414743797147534962345451454e41514945416745434d42414743797147534962345451454e0a41514946416745454d42414743797147534962345451454e41514947416745424d42414743797147534962345451454e41514948416745414d424147437971470a534962345451454e41514949416745434d42414743797147534962345451454e4151494a416745414d42414743797147534962345451454e4151494b416745410a4d42414743797147534962345451454e4151494c416745414d42414743797147534962345451454e4151494d416745414d42414743797147534962345451454e0a4151494e416745414d42414743797147534962345451454e4151494f416745414d42414743797147534962345451454e41514950416745414d424147437971470a534962345451454e41514951416745414d42414743797147534962345451454e415149524167454e4d42384743797147534962345451454e41514953424241450a42414943424145414167414141414141414141414d42414743697147534962345451454e41514d45416741414d42514743697147534962345451454e415151450a42674367625167414144415042676f71686b69472b45304244514546436745424d42344743697147534962345451454e41515945454f324e6c4d316f716573740a41326e3249363856664938775241594b4b6f5a496876684e41513042427a41324d42414743797147534962345451454e415163424151482f4d424147437971470a534962345451454e41516343415145414d42414743797147534962345451454e415163444151482f4d416f4743437147534d343942414d4341306b414d4559430a4951435061715878444f6634316655727436444979484132687437524c54676f7662643357423643596e4a3044514968414a35584e5043394f4e7859765049710a6a305a2f394b6442417451584b77514f31374c4430433133597575620a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d4949436c6a4343416a32674177494241674956414a567658633239472b487051456e4a3150517a7a674658433935554d416f4743437147534d343942414d430a4d476778476a415942674e5642414d4d45556c756447567349464e48574342536232393049454e424d526f77474159445651514b4442464a626e526c624342440a62334a7762334a6864476c76626a45554d424947413155454277774c553246756447456751327868636d4578437a414a42674e564241674d416b4e424d5173770a435159445651514745774a56557a4165467730784f4441314d6a45784d4455774d5442614677307a4d7a41314d6a45784d4455774d5442614d484178496a41670a42674e5642414d4d47556c756447567349464e4857434251513073675547786864475a76636d306751304578476a415942674e5642416f4d45556c75644756730a49454e76636e4276636d4630615739754d5251774567594456515148444174545957353059534244624746795954454c4d416b474131554543417743513045780a437a414a42674e5642415954416c56544d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a304441516344516741454e53422f377432316c58534f0a3243757a7078773734654a423732457944476757357258437478327456544c7136684b6b367a2b5569525a436e71523770734f766771466553786c6d546c4a6c0a65546d693257597a33714f42757a43427544416642674e5648534d4547444157674251695a517a575770303069664f44744a5653763141624f536347724442530a42674e5648523845537a424a4d45656752614244686b466f64485277637a6f764c324e6c636e52705a6d6c6a5958526c63793530636e567a6447566b633256790a646d6c6a5a584d75615735305a577775593239744c306c756447567355306459556d397664454e424c6d526c636a416442674e5648513445466751556c5739640a7a62306234656c4153636e553944504f4156634c336c517744675944565230504151482f42415144416745474d42494741315564457745422f7751494d4159420a4166384341514177436759494b6f5a497a6a30454177494452774177524149675873566b6930772b6936565947573355462f32327561586530594a446a3155650a6e412b546a44316169356343494359623153416d4435786b66545670766f34556f79695359787244574c6d5552344349394e4b7966504e2b0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d4949436a7a4343416a53674177494241674955496d554d316c71644e496e7a6737535655723951477a6b6e42717777436759494b6f5a497a6a3045417749770a614445614d4267474131554541777752535735305a5777675530645949464a766233516751304578476a415942674e5642416f4d45556c756447567349454e760a636e4276636d4630615739754d5251774567594456515148444174545957353059534244624746795954454c4d416b47413155454341774351304578437a414a0a42674e5642415954416c56544d423458445445344d4455794d5445774e4455784d466f58445451354d54497a4d54497a4e546b314f566f77614445614d4267470a4131554541777752535735305a5777675530645949464a766233516751304578476a415942674e5642416f4d45556c756447567349454e76636e4276636d46300a615739754d5251774567594456515148444174545957353059534244624746795954454c4d416b47413155454341774351304578437a414a42674e56424159540a416c56544d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a3044415163445167414543366e45774d4449595a4f6a2f69505773437a61454b69370a314f694f534c52466857476a626e42564a66566e6b59347533496a6b4459594c304d784f346d717379596a6c42616c54565978465032734a424b357a6c4b4f420a757a43427544416642674e5648534d4547444157674251695a517a575770303069664f44744a5653763141624f5363477244425342674e5648523845537a424a0a4d45656752614244686b466f64485277637a6f764c324e6c636e52705a6d6c6a5958526c63793530636e567a6447566b63325679646d6c6a5a584d75615735300a5a577775593239744c306c756447567355306459556d397664454e424c6d526c636a416442674e564851344546675155496d554d316c71644e496e7a673753560a55723951477a6b6e4271777744675944565230504151482f42415144416745474d42494741315564457745422f7751494d4159424166384341514577436759490a4b6f5a497a6a3045417749445351417752674968414f572f35516b522b533943695344634e6f6f774c7550524c735747662f59693747535839344267775477670a41694541344a306c72486f4d732b586f356f2f7358364f39515778485241765a55474f6452513763767152586171493d0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a96576656e745f6c6f679c85a3696d7200aa6576656e745f747970650aa6646967657374c430d8c90b5d0f756dcd707c190313b44e3129abc6c20118efdd32b78f84fa64a6c51396fa43debc993dec00b85718bf7491a56576656e74ab616370692d6c6f61646572ad6576656e745f7061796c6f6164c40941435049204441544185a3696d7200aa6576656e745f747970650aa6646967657374c430ab0063753f886063d08d1e65031fdcd12e210764b476210edf90e006af701c20df1710ab3fcb7dee651727f4a369f29aa56576656e74a9616370692d72736470ad6576656e745f7061796c6f6164c40941435049204441544185a3696d7200aa6576656e745f747970650aa6646967657374c4308583382b5eb22b3b7b8194777aa82ae7060c6b5e8387ee59926a9dc429e1069a9215c24dd299cee42611fd1960711c50a56576656e74ab616370692d7461626c6573ad6576656e745f7061796c6f6164c40941435049204441544185a3696d7203aa6576656e745f74797065ce08000001a6646967657374c430f9974020ef507068183313d0ca808e0d1ca9b2d1ad0c61f5784e7157c362c06536f5ddacdad4451693f48fcc72fff624a56576656e74b073797374656d2d707265706172696e67ad6576656e745f7061796c6f6164c40085a3696d7203aa6576656e745f74797065ce08000001a6646967657374c43091d0b47582da5e129f4bce69d77ef236c882dbf715321826b09e88b8ccb69442f0ff97d84f1026d08ce1eeb21d156620a56576656e74a66170702d6964ad6576656e745f7061796c6f6164c414081267be9fad577c7b71a6a550ea81510b661afa85a3696d7203aa6576656e745f74797065ce08000001a6646967657374c43009420aa67d6fd180f5640527724b9d115501e0e72967b7563622fbd7e069cf9d53cb0d9837c41641b9466555d8072defa56576656e74ac636f6d706f73652d68617368ad6576656e745f7061796c6f6164c420081267be9fad577c7b71a6a550ea81510b661afa7c43d31265ed670cdedcb15e85a3696d7203aa6576656e745f74797065ce08000001a6646967657374c430aa25b837d81098d56e726cdfd4664eae9a1269362479753d04bdfd239a45388a056f245841910931ea09ff8278b9d482a56576656e74af6770752d706f6c6963792d68617368ad6576656e745f7061796c6f6164c42044136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a85a3696d7203aa6576656e745f74797065ce08000001a6646967657374c430305a62e30e8f4ca791946c3ede6755cfacebe02be9101f0bccf2591509a0c8e8095bc83b3d53bfc5d70d6c7cf7813fc5a56576656e74ab696e7374616e63652d6964ad6576656e745f7061796c6f6164c40085a3696d7203aa6576656e745f74797065ce08000001a6646967657374c43098bd7e6bd3952720b65027fd494834045d06b4a714bf737a06b874638b3ea00ff402f7f583e3e3b05e921c8570433ac6a56576656e74ac626f6f742d6d722d646f6e65ad6576656e745f7061796c6f6164c40085a3696d7203aa6576656e745f74797065ce08000001a6646967657374c43012b5586b61f47ec6e6979c53f90b3bc84cbf0e57f997580e8757ff24382f346b99389ce47ba88f77232fd10a974acea4a56576656e74ac6b65792d70726f7669646572ad6576656e745f7061796c6f6164c45c7b226e616d65223a226c6f63616c2d736778222c226964223a2238653631656462383637623433333066363238626237363831336161373934303865663064636166393562386534363066376134646536343030326439626132227d85a3696d7203aa6576656e745f74797065ce08000001a6646967657374c430ba51104636900268b0e059fa3d266419d079d1e94aea26fb9fcbb8d764bf4c89a67ac271b8a0d1a3989945132a111fc7a56576656e74aa73746f726167652d6673ad6576656e745f7061796c6f6164c4037a667385a3696d7203aa6576656e745f74797065ce08000001a6646967657374c4301a76b2a80a0be71eae59f80945d876351a7a3fb8e9fd1ff1cede5734aa84ea11fd72b4edfbb6f04e5a85edd114c751bda56576656e74ac73797374656d2d7265616479ad6576656e745f7061796c6f6164c400a5737461636b82a46b696e64a664737461636ba46461746183ab7265706f72745f64617461dc0040cc88ccf6ccdacca719ccfd2f5762ccec4f06ccfbcccfccc9cc8356cc9856cce85d7939ccf57fcce0cca5cc92695dccd3cc84cc93cca9cc9e002eccf1ccbfcc934251ccd3ccb4ccf5cc89cce90b7bccc31a79cce6cc9411cc93cc9165410fccfbcc8bccea2cae72756e74696d655f6576656e74739982a56576656e74b073797374656d2d707265706172696e67a77061796c6f6164c40082a56576656e74a66170702d6964a77061796c6f6164c414081267be9fad577c7b71a6a550ea81510b661afa82a56576656e74ac636f6d706f73652d68617368a77061796c6f6164c420081267be9fad577c7b71a6a550ea81510b661afa7c43d31265ed670cdedcb15e82a56576656e74af6770752d706f6c6963792d68617368a77061796c6f6164c42044136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a82a56576656e74ab696e7374616e63652d6964a77061796c6f6164c40082a56576656e74ac626f6f742d6d722d646f6e65a77061796c6f6164c40082a56576656e74ac6b65792d70726f7669646572a77061796c6f6164c45c7b226e616d65223a226c6f63616c2d736778222c226964223a2238653631656462383637623433333066363238626237363831336161373934303865663064636166393562386534363066376134646536343030326439626132227d82a56576656e74aa73746f726167652d6673a77061796c6f6164c4037a667382a56576656e74ac73797374656d2d7265616479a77061796c6f6164c400a6636f6e666967da04847b226f735f696d6167655f68617368223a2261336134393366303765643065396564303730396131303163663531323437383261386334333264663932643032653835303231613835633762323736396339222c226370755f636f756e74223a312c226d656d6f72795f73697a65223a323134373438333634382c2271656d755f76657273696f6e223a2231302e322e31222c227063695f686f6c6536345f73697a65223a302c22687567657061676573223a66616c73652c226e756d5f67707573223a302c226e756d5f6e767377697463686573223a302c22686f74706c75675f6f6666223a66616c73652c22696d616765223a2264737461636b2d302e362e302d726330222c22686f73745f73686172655f6d6f6465223a223970222c226f766d665f76617269616e74223a22707265323032353035222c227464785f6174746573746174696f6e5f76617269616e74223a226c697465222c227464785f6d6561737572656d656e74223a7b22636865636b73756d5f66696c65223a224f444935596a4a6b596a55344d6a45304d474d775a6a46684e444932596a4d314e6d4e6d5a6a5a6a4e4455345a6a41354f446c6a4d544d795a5468685a5467344e6a526c4e444d304d6d59344e5468694f5459784d43416762575668633356795a57316c626e5175644752344c6d4e696233494b222c226d6561737572656d656e74223a226f3264325a584a7a61573975424756706257466e5a6152755932316b62476c755a56397a6147457a4f4452594d5076632b65396551435756324f4c4a78424c6a736f4f645341314b4646364452394872794b466c32534431584749694848387177372b364734446c6f665038626e4e725a584a755a577866595856306147567564476c6a6232526c574443756d6855457958657a6e32715556593533416445322f2b624d4d4c42683231766e2b4b69395051383376773071334b2f535071397038757a524d786931324474344733426864474e6f5a57526661325679626d5673583246316447686c626e52705932396b5a5667777074785235305767355465763366782b426c5a7843576b75654544774d3038446b712f2b6f3141353051753752673247787a454d677436414b6145526757377762576c75615852795a46397a6147457a4f4452594d4534302f4f6f4f567279545743757a724e433379653137504c51616a466d436261326c417a34763838664e793373305a554c514d32516c6755564e6f6e4a75773252305a485a6d6f325276646d316d615842795a5449774d6a55774e575274636e526b6f6d747a6157356e624756666347467a6331677768394b5271754f6c5455776370345338476d6a514c7a726e4448764837474734334b6c656b576e784b5150583156785a62366a426d6f6c674f2b34456273352f614852336231397759584e7a574444794179303331494c7a71714f30677566666a4e6b5836345a42325a315275725a4d2f3235556e5258316a7254636679427a5a663143536f535458486d347933526d6447526661473969544941514351514142676b434377495145413d3d227d2c22737065635f76657273696f6e223a317d" +} diff --git a/dstack/verifier/fixtures/tdx-lite.README.md b/dstack/verifier/fixtures/tdx-lite.README.md index 10d782e25..a247b5ae2 100644 --- a/dstack/verifier/fixtures/tdx-lite.README.md +++ b/dstack/verifier/fixtures/tdx-lite.README.md @@ -15,6 +15,9 @@ Files: quote, event log, and vm_config -- the shape `DstackGuest.GetQuote` returns. `GetQuoteResponse` is Intel TDX only and carries no versioned attestation; use `Attest` for the platform-adaptive form. +- `tdx-lite-qemu-10-2-attestation.json`: the same shape captured from a CVM + running QEMU 10.2.1, which measures the kernel as built rather than the one + QEMU patched. See "QEMU 10.2 fixture" below. Captured with: @@ -32,9 +35,16 @@ Important fixture properties: - `vm_config.tdx_attestation_variant = "lite"` - `vm_config.memory_size = 2147483648` (2 GiB) -- `vm_config.os_image_hash = e6f5cfec20c02e7b97baa213d0f718020b55e040172d90ccbcb946d56c8b09db` +- `vm_config.os_image_hash = 3a0dfc279e9da8ca6b3659102688379eea28e79a005b83a97e3b05833a2aba02` - `vm_config.tdx_measurement.{checksum_file,measurement}` are JSON base64 byte strings. +- The measurement document is v4. The capture predates that format, so its + `measurement.tdx.cbor` was rewritten in place: `kernel_authenticode` was + filled in from `~/.dstack/images/dstack-0.6.0`, whose + `patched_kernel_authenticode` and `initrd_sha384` both equal the captured + ones and therefore whose `bzImage` is the byte-identical file this CVM + booted. `checksum_file` and `os_image_hash` were recomputed over the new + bytes; nothing the quote commits to changed. - The raw top-level `event_log` and stripped attestation keep the three named RTMR0 `ACPI DATA` digests (`acpi-loader`, `acpi-rsdp`, `acpi-tables`) and marker payloads needed by the lite verifier, plus RTMR3 runtime events. @@ -65,3 +75,38 @@ Expected result: `Valid: true`, with quote, event log, OS image hash, and ACPI tables all verified. The ACPI digests are regenerated in-process from the fixture's VM shape (2 vCPUs, 2 GiB, QEMU 8.2.2) and must equal the ones the captured CVM reported. + +## QEMU 10.2 fixture + +`tdx-lite-qemu-10-2-attestation.json` covers the other kernel measurement. +QEMU commit a7542a38f399 ("x86/loader: Don't update kernel header for CoCo +VMs", released in 10.2.0) stopped rewriting the Linux setup header for +confidential guests, so from 10.2 on a TDX CVM measures the kernel file as +built. Before dstack modeled that, this attestation failed with +`RTMR1 mismatch: expected=4ffc8bcd... actual=74f00f63...`. + +It was captured from a live CVM, not reconstructed: + +- quote, event log and RTMR3 runtime events come from the running CVM's + attestation blob, unmodified +- `vm_config` is the CVM's own, with `tdx_measurement` regenerated by + `dstack-mr tdx-measurement-cbor` over that CVM's image directory so the + document carries both kernel Authenticode digests, and `os_image_hash` + updated to match the rebuilt `checksum_file` + +Both digests came from that image directory, so unlike the 8.2.2 fixture above +nothing had to be reconstructed for this one. + +Important fixture properties: + +- `vm_config.qemu_version = "10.2.1"` (Ubuntu `1:10.2.1+ds-1ubuntu3.2`) +- `vm_config.memory_size = 2147483648` (2 GiB), 1 vCPU +- `vm_config.os_image_hash = a3a493f07ed0e9ed0709a101cf5124782a8c432df92d02e85021a85c7b2769c9` +- `image.kernel_authenticode` (the kernel as built) is the digest this fixture + exercises; `image.patched_kernel_authenticode` is the one the pre-10.2 path + would have used, and is what produced the RTMR1 mismatch above + +To confirm it is a real regression test rather than a self-consistent capture, +point `QEMU_COCO_KERNEL_HEADER_UNPATCHED` (in `dstack-mr/src/machine.rs`) at a +version above 10.2.1 and re-run it: verification fails with the RTMR1 mismatch +above. diff --git a/dstack/verifier/src/verification.rs b/dstack/verifier/src/verification.rs index 8f46bf27b..d0e97e5dd 100644 --- a/dstack/verifier/src/verification.rs +++ b/dstack/verifier/src/verification.rs @@ -183,7 +183,9 @@ fn collect_rtmr_mismatch( // Bump whenever expected RTMR computation changes so stale entries get ignored. // v3: all supported OVMF measurements use the Pre202505 RTMR[0] layout. -const MEASUREMENT_CACHE_VERSION: u32 = 3; +// v4: RTMR[1] measures the unpatched kernel on QEMU >= 10.2, so entries cached +// for such a CVM before that was modeled hold the wrong digest. +const MEASUREMENT_CACHE_VERSION: u32 = 4; #[derive(Clone, Serialize, Deserialize)] struct CachedMeasurement { @@ -332,6 +334,7 @@ impl CvmVerifier { .root_verity(true) .hotplug_off(vm_config.hotplug_off) .maybe_two_pass_add_pages(vm_config.qemu_single_pass_add_pages) + .maybe_patch_kernel_header(vm_config.qemu_patches_kernel_header) .maybe_pic(vm_config.pic) .maybe_qemu_version(vm_config.qemu_version.clone()) .maybe_pci_hole64_size(if vm_config.pci_hole64_size > 0 { @@ -2247,6 +2250,40 @@ mod tests { "TDX lite verification must not download or cache OS images" ); } + /// Captured from a CVM on QEMU 10.2.1, which stops rewriting the kernel + /// setup header for confidential guests. Before that was modeled, this + /// attestation failed with an RTMR1 mismatch, so the fixture pins the + /// regression against a real quote rather than a reconstructed one. + #[tokio::test] + async fn verifies_tdx_lite_fixture_on_qemu_10_2() { + let request: VerificationRequest = serde_json::from_str(include_str!( + "../fixtures/tdx-lite-qemu-10-2-attestation.json" + )) + .expect("QEMU 10.2 TDX lite verifier fixture parses"); + let cache = tempfile::tempdir().expect("temp cache dir"); + let image_cache_dir = cache.path().join("cache"); + let verifier = CvmVerifier::new( + image_cache_dir.display().to_string(), + "http://127.0.0.1:9/should-not-download/{OS_IMAGE_HASH}.tar.gz".to_string(), + Duration::from_secs(1), + test_attestation_verifier(), + ); + + let response = verifier.verify(request).await.expect("verifier runs"); + assert!(response.is_valid, "{:?}", response.reason); + assert!(response.details.quote_verified); + assert!(response.details.event_log_verified); + assert!(response.details.os_image_hash_verified); + assert!(response.details.acpi_tables_verified); + assert_eq!( + response.details.tee_variant, + Some(ra_tls::attestation::TeeVariant::DstackTdx) + ); + assert!( + !image_cache_dir.exists(), + "TDX lite verification must not download or cache OS images" + ); + } /// The captured VM ran 2 vCPUs; a VM shape that disagrees with the quote /// must not reproduce its ACPI digests, which is what makes the recomputed diff --git a/dstack/vmm/src/app.rs b/dstack/vmm/src/app.rs index 836176d4b..a72663907 100644 --- a/dstack/vmm/src/app.rs +++ b/dstack/vmm/src/app.rs @@ -1804,6 +1804,7 @@ fn make_vm_config( memory_size: manifest.memory as u64 * 1024 * 1024, qemu_single_pass_add_pages: cfg.cvm.qemu_single_pass_add_pages, pic: cfg.cvm.qemu_pic, + qemu_patches_kernel_header: cfg.cvm.qemu_patches_kernel_header, qemu_version: cfg.cvm.qemu_version.clone(), pci_hole64_size: cfg.cvm.qemu_pci_hole64_size, hugepages: manifest.hugepages, @@ -2416,6 +2417,7 @@ mod tests { image: TdxImageMeasurement { kernel_cmdline_sha384: vec![0x10; 48], kernel_authenticode: vec![0x20; 48], + patched_kernel_authenticode: vec![0x21; 48], initrd_sha384: vec![0x30; 48], }, tdvf: TdxTdvfMeasurement { diff --git a/dstack/vmm/src/config.rs b/dstack/vmm/src/config.rs index 2e82e67c6..1b09c036b 100644 --- a/dstack/vmm/src/config.rs +++ b/dstack/vmm/src/config.rs @@ -350,6 +350,11 @@ pub struct CvmConfig { pub qemu_single_pass_add_pages: Option, /// QEMU pic pub qemu_pic: Option, + /// Whether QEMU rewrites the Linux setup header before serving the kernel + /// over fw_cfg. Leave unset: it is derived from `qemu_version`, which is + /// right for every upstream release. Set it only for a QEMU fork whose + /// behavior disagrees with its version number. + pub qemu_patches_kernel_header: Option, /// QEMU qemu_version pub qemu_version: Option, /// QEMU pci_hole64_size diff --git a/dstack/vmm/vmm.toml b/dstack/vmm/vmm.toml index c7b7accf9..bf05fa63d 100644 --- a/dstack/vmm/vmm.toml +++ b/dstack/vmm/vmm.toml @@ -59,6 +59,7 @@ use_mrconfigid = true # QEMU flags #qemu_single_pass_add_pages = false #qemu_pic = true +#qemu_patches_kernel_header = true #qemu_version = "" # Size of the 64-bit PCI hole. Accepts a plain byte count, hex, or a unit # suffix: "512G", "2T", "1P" -- all binary multipliers, as are the "1PB" and diff --git a/tools/vm-runner/vm-runner.py b/tools/vm-runner/vm-runner.py index 7bce7fe4f..712a57257 100755 --- a/tools/vm-runner/vm-runner.py +++ b/tools/vm-runner/vm-runner.py @@ -130,7 +130,30 @@ def update_guest_config(config_file: str, data: Dict): json.dump(config, f, indent=4) -def gen_vm_config(vm_dir, host_port, manifest=None, os_image_hash=None): +def detect_qemu_version(qemu_path): + """Return QEMU's `major.minor.micro`, or None if it cannot be determined. + + The measured RTMR values depend on the QEMU version -- notably RTMR[1], + since QEMU >= 10.2 stops rewriting the kernel setup header for confidential + guests -- so a vm_config without this field makes the verifier fall back to + a default version and compute the wrong digests. + """ + try: + output = subprocess.check_output( + [qemu_path, '--version'], text=True, stderr=subprocess.DEVNULL) + except (OSError, subprocess.SubprocessError) as exc: + logging.warning("failed to run %s --version: %s", qemu_path, exc) + return None + match = re.search(r'QEMU emulator version (\d+\.\d+\.\d+)', output) + if not match: + logging.warning("could not parse QEMU version from: %s", + output.splitlines()[0] if output else '') + return None + return match.group(1) + + +def gen_vm_config(vm_dir, host_port, manifest=None, os_image_hash=None, + qemu_version=None): shared_dir = os.path.join(vm_dir, 'shared') for filename in ['config.json', '.sys-config.json']: config_file = os.path.join(shared_dir, filename) @@ -139,12 +162,15 @@ def gen_vm_config(vm_dir, host_port, manifest=None, os_image_hash=None): "host_vsock_port": host_port }) if manifest: + vm_config = { + "os_image_hash": os_image_hash, + "cpu_count": manifest['vcpu'], + "memory_size": manifest['memory'] * 1024 * 1024 + } + if qemu_version: + vm_config["qemu_version"] = qemu_version update_guest_config(config_file, { - "vm_config": json.dumps({ - "os_image_hash": os_image_hash, - "cpu_count": manifest['vcpu'], - "memory_size": manifest['memory'] * 1024 * 1024 - }) + "vm_config": json.dumps(vm_config) }) @@ -408,7 +434,8 @@ def run_instance(self, vm_dir: str, host_port: int, imgdir: Optional[str] = None os_image_hash = open(os.path.join( image_path, 'digest.txt'), 'r').read().strip() - gen_vm_config(vm_dir, host_port, manifest, os_image_hash) + gen_vm_config(vm_dir, host_port, manifest, os_image_hash, + detect_qemu_version(self.config.qemu_path)) mem_gb = manifest['memory'] // 1024 vcpu_count = manifest['vcpu']