Skip to content

Commit 54f13bb

Browse files
authored
Merge pull request #341 from Dstack-TEE/verifier
Add rust implementation of dstack-verifier
2 parents 9bfa979 + 96ac391 commit 54f13bb

26 files changed

+2619
-38
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# SPDX-FileCopyrightText: © 2025 Phala Network <[email protected]>
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
name: Verifier Release
6+
7+
on:
8+
workflow_dispatch:
9+
push:
10+
tags:
11+
- 'verifier-v*'
12+
permissions:
13+
attestations: write
14+
id-token: write
15+
contents: write
16+
packages: write
17+
18+
jobs:
19+
build-and-release:
20+
runs-on: ubuntu-latest
21+
steps:
22+
- name: Checkout repository
23+
uses: actions/checkout@v4
24+
25+
- name: Parse version from tag
26+
run: |
27+
VERSION=${GITHUB_REF#refs/tags/verifier-v}
28+
echo "VERSION=$VERSION" >> $GITHUB_ENV
29+
echo "Parsed version: $VERSION"
30+
31+
- name: Log in to Docker Hub
32+
uses: docker/login-action@v3
33+
with:
34+
username: ${{ vars.DOCKERHUB_USERNAME }}
35+
password: ${{ secrets.DOCKERHUB_TOKEN }}
36+
37+
- name: Set up Docker Buildx
38+
uses: docker/setup-buildx-action@v3
39+
40+
- name: Get Git commit timestamps
41+
run: |
42+
echo "TIMESTAMP=$(git log -1 --pretty=%ct)" >> $GITHUB_ENV
43+
echo "GIT_REV=$(git rev-parse HEAD)" >> $GITHUB_ENV
44+
45+
- name: Build and push Docker image
46+
id: build-and-push
47+
uses: docker/build-push-action@v5
48+
env:
49+
SOURCE_DATE_EPOCH: ${{ env.TIMESTAMP }}
50+
with:
51+
context: verifier
52+
file: verifier/builder/Dockerfile
53+
push: true
54+
tags: ${{ vars.DOCKERHUB_USERNAME }}/dstack-verifier:${{ env.VERSION }}
55+
platforms: linux/amd64
56+
provenance: false
57+
build-args: |
58+
DSTACK_REV=${{ env.GIT_REV }}
59+
DSTACK_SRC_URL=${{ github.server_url }}/${{ github.repository }}.git
60+
SOURCE_DATE_EPOCH=${{ env.TIMESTAMP }}
61+
62+
- name: Generate artifact attestation
63+
uses: actions/attest-build-provenance@v1
64+
with:
65+
subject-name: "docker.io/${{ vars.DOCKERHUB_USERNAME }}/dstack-verifier"
66+
subject-digest: ${{ steps.build-and-push.outputs.digest }}
67+
push-to-registry: true
68+
69+
- name: GitHub Release
70+
uses: softprops/action-gh-release@v1
71+
with:
72+
name: "Verifier Release v${{ env.VERSION }}"
73+
body: |
74+
## Docker Image Information
75+
76+
**Image**: `docker.io/${{ vars.DOCKERHUB_USERNAME }}/dstack-verifier:${{ env.VERSION }}`
77+
78+
**Digest (SHA256)**: `${{ steps.build-and-push.outputs.digest }}`
79+
80+
**Verification**: [Verify on Sigstore](https://search.sigstore.dev/?hash=${{ steps.build-and-push.outputs.digest }})

Cargo.lock

Lines changed: 25 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ members = [
4949
"serde-duration",
5050
"dstack-mr",
5151
"dstack-mr/cli",
52+
"verifier",
5253
"no_std_check",
5354
]
5455
resolver = "2"

REUSE.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,3 +162,13 @@ SPDX-License-Identifier = "CC0-1.0"
162162
path = "dstack-util/tests/fixtures/*"
163163
SPDX-FileCopyrightText = "NONE"
164164
SPDX-License-Identifier = "CC0-1.0"
165+
166+
[[annotations]]
167+
path = "verifier/fixtures/*"
168+
SPDX-FileCopyrightText = "NONE"
169+
SPDX-License-Identifier = "CC0-1.0"
170+
171+
[[annotations]]
172+
path = "verifier/builder/shared/*.txt"
173+
SPDX-FileCopyrightText = "NONE"
174+
SPDX-License-Identifier = "CC0-1.0"

dstack-mr/src/acpi.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use crate::Machine;
1313
const LDR_LENGTH: usize = 4096;
1414
const FIXED_STRING_LEN: usize = 56;
1515

16+
#[derive(Debug, Clone)]
1617
pub struct Tables {
1718
pub tables: Vec<u8>,
1819
pub rsdp: Vec<u8>,

dstack-mr/src/kernel.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// SPDX-License-Identifier: Apache-2.0
44

5-
use crate::{measure_log, measure_sha384, num::read_le, utf16_encode, util::debug_print_log};
5+
use crate::{measure_sha384, num::read_le, utf16_encode};
66
use anyhow::{bail, Context, Result};
77
use object::pe;
88
use sha2::{Digest, Sha384};
@@ -201,24 +201,22 @@ fn patch_kernel(
201201
}
202202

203203
/// Measures a QEMU-patched TDX kernel image.
204-
pub(crate) fn measure_kernel(
204+
pub(crate) fn rtmr1_log(
205205
kernel_data: &[u8],
206206
initrd_size: u32,
207207
mem_size: u64,
208208
acpi_data_size: u32,
209-
) -> Result<Vec<u8>> {
209+
) -> Result<Vec<Vec<u8>>> {
210210
let kd = patch_kernel(kernel_data, initrd_size, mem_size, acpi_data_size)
211211
.context("Failed to patch kernel")?;
212212
let kernel_hash = authenticode_sha384_hash(&kd).context("Failed to compute kernel hash")?;
213-
let rtmr1_log = vec![
213+
Ok(vec![
214214
kernel_hash,
215215
measure_sha384(b"Calling EFI Application from Boot Option"),
216216
measure_sha384(&[0x00, 0x00, 0x00, 0x00]), // Separator
217217
measure_sha384(b"Exit Boot Services Invocation"),
218218
measure_sha384(b"Exit Boot Services Returned with Success"),
219-
];
220-
debug_print_log("RTMR1", &rtmr1_log);
221-
Ok(measure_log(&rtmr1_log))
219+
])
222220
}
223221

224222
/// Measures the kernel command line by converting to UTF-16LE and hashing.

dstack-mr/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@
55
use serde::{Deserialize, Serialize};
66
use serde_human_bytes as hex_bytes;
77

8-
pub use machine::Machine;
8+
pub use machine::{Machine, TdxMeasurementDetails};
99

1010
use util::{measure_log, measure_sha384, utf16_encode};
1111

12+
pub type RtmrLog = Vec<Vec<u8>>;
13+
pub type RtmrLogs = [RtmrLog; 3];
14+
1215
mod acpi;
1316
mod kernel;
1417
mod machine;

dstack-mr/src/machine.rs

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
//
33
// SPDX-License-Identifier: Apache-2.0
44

5+
use crate::acpi::Tables;
56
use crate::tdvf::Tdvf;
67
use crate::util::debug_print_log;
7-
use crate::{kernel, TdxMeasurements};
8+
use crate::{kernel, RtmrLogs, TdxMeasurements};
89
use crate::{measure_log, measure_sha384};
910
use anyhow::{bail, Context, Result};
1011
use fs_err as fs;
@@ -78,21 +79,41 @@ pub struct VersionedOptions {
7879
pub two_pass_add_pages: bool,
7980
}
8081

82+
#[derive(Debug, Clone)]
83+
pub struct TdxMeasurementDetails {
84+
pub measurements: TdxMeasurements,
85+
pub rtmr_logs: RtmrLogs,
86+
pub acpi_tables: Tables,
87+
}
88+
8189
impl Machine<'_> {
8290
pub fn measure(&self) -> Result<TdxMeasurements> {
91+
self.measure_with_logs().map(|details| details.measurements)
92+
}
93+
94+
pub fn measure_with_logs(&self) -> Result<TdxMeasurementDetails> {
8395
debug!("measuring machine: {self:#?}");
8496
let fw_data = fs::read(self.firmware)?;
8597
let kernel_data = fs::read(self.kernel)?;
8698
let initrd_data = fs::read(self.initrd)?;
8799
let tdvf = Tdvf::parse(&fw_data).context("Failed to parse TDVF metadata")?;
100+
88101
let mrtd = tdvf.mrtd(self).context("Failed to compute MR TD")?;
89-
let rtmr0 = tdvf.rtmr0(self).context("Failed to compute RTMR0")?;
90-
let rtmr1 = kernel::measure_kernel(
102+
103+
let (rtmr0_log, acpi_tables) = tdvf
104+
.rtmr0_log(self)
105+
.context("Failed to compute RTMR0 log")?;
106+
debug_print_log("RTMR0", &rtmr0_log);
107+
let rtmr0 = measure_log(&rtmr0_log);
108+
109+
let rtmr1_log = kernel::rtmr1_log(
91110
&kernel_data,
92111
initrd_data.len() as u32,
93112
self.memory_size,
94113
0x28000,
95114
)?;
115+
debug_print_log("RTMR1", &rtmr1_log);
116+
let rtmr1 = measure_log(&rtmr1_log);
96117

97118
let rtmr2_log = vec![
98119
kernel::measure_cmdline(self.kernel_cmdline),
@@ -101,11 +122,15 @@ impl Machine<'_> {
101122
debug_print_log("RTMR2", &rtmr2_log);
102123
let rtmr2 = measure_log(&rtmr2_log);
103124

104-
Ok(TdxMeasurements {
105-
mrtd,
106-
rtmr0,
107-
rtmr1,
108-
rtmr2,
125+
Ok(TdxMeasurementDetails {
126+
measurements: TdxMeasurements {
127+
mrtd,
128+
rtmr0,
129+
rtmr1,
130+
rtmr2,
131+
},
132+
rtmr_logs: [rtmr0_log, rtmr1_log, rtmr2_log],
133+
acpi_tables,
109134
})
110135
}
111136
}

dstack-mr/src/tdvf.rs

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ use anyhow::{anyhow, bail, Context, Result};
66
use hex_literal::hex;
77
use sha2::{Digest, Sha384};
88

9+
use crate::acpi::Tables;
910
use crate::num::read_le;
10-
use crate::util::debug_print_log;
11-
use crate::{measure_log, measure_sha384, utf16_encode, Machine};
11+
use crate::{measure_log, measure_sha384, utf16_encode, Machine, RtmrLog};
1212

1313
const PAGE_SIZE: u64 = 0x1000;
1414
const MR_EXTEND_GRANULARITY: usize = 0x100;
@@ -233,7 +233,13 @@ impl<'a> Tdvf<'a> {
233233
})
234234
}
235235

236+
#[allow(dead_code)]
236237
pub fn rtmr0(&self, machine: &Machine) -> Result<Vec<u8>> {
238+
let (rtmr0_log, _) = self.rtmr0_log(machine)?;
239+
Ok(measure_log(&rtmr0_log))
240+
}
241+
242+
pub fn rtmr0_log(&self, machine: &Machine) -> Result<(RtmrLog, Tables)> {
237243
let td_hob_hash = self.measure_td_hob(machine.memory_size)?;
238244
let cfv_image_hash = hex!("344BC51C980BA621AAA00DA3ED7436F7D6E549197DFE699515DFA2C6583D95E6412AF21C097D473155875FFD561D6790");
239245
let boot000_hash = hex!("23ADA07F5261F12F34A0BD8E46760962D6B4D576A416F1FEA1C64BC656B1D28EACF7047AE6E967C58FD2A98BFA74C298");
@@ -245,23 +251,24 @@ impl<'a> Tdvf<'a> {
245251

246252
// RTMR0 calculation
247253

248-
let rtmr0_log = vec![
249-
td_hob_hash,
250-
cfv_image_hash.to_vec(),
251-
measure_tdx_efi_variable("8BE4DF61-93CA-11D2-AA0D-00E098032B8C", "SecureBoot")?,
252-
measure_tdx_efi_variable("8BE4DF61-93CA-11D2-AA0D-00E098032B8C", "PK")?,
253-
measure_tdx_efi_variable("8BE4DF61-93CA-11D2-AA0D-00E098032B8C", "KEK")?,
254-
measure_tdx_efi_variable("D719B2CB-3D3A-4596-A3BC-DAD00E67656F", "db")?,
255-
measure_tdx_efi_variable("D719B2CB-3D3A-4596-A3BC-DAD00E67656F", "dbx")?,
256-
measure_sha384(&[0x00, 0x00, 0x00, 0x00]), // Separator
257-
acpi_loader_hash,
258-
acpi_rsdp_hash,
259-
acpi_tables_hash,
260-
measure_sha384(&[0x00, 0x00]), // BootOrder
261-
boot000_hash.to_vec(),
262-
];
263-
debug_print_log("RTMR0", &rtmr0_log);
264-
Ok(measure_log(&rtmr0_log))
254+
Ok((
255+
vec![
256+
td_hob_hash,
257+
cfv_image_hash.to_vec(),
258+
measure_tdx_efi_variable("8BE4DF61-93CA-11D2-AA0D-00E098032B8C", "SecureBoot")?,
259+
measure_tdx_efi_variable("8BE4DF61-93CA-11D2-AA0D-00E098032B8C", "PK")?,
260+
measure_tdx_efi_variable("8BE4DF61-93CA-11D2-AA0D-00E098032B8C", "KEK")?,
261+
measure_tdx_efi_variable("D719B2CB-3D3A-4596-A3BC-DAD00E67656F", "db")?,
262+
measure_tdx_efi_variable("D719B2CB-3D3A-4596-A3BC-DAD00E67656F", "dbx")?,
263+
measure_sha384(&[0x00, 0x00, 0x00, 0x00]), // Separator
264+
acpi_loader_hash,
265+
acpi_rsdp_hash,
266+
acpi_tables_hash,
267+
measure_sha384(&[0x00, 0x00]), // BootOrder
268+
boot000_hash.to_vec(),
269+
],
270+
tables,
271+
))
265272
}
266273

267274
fn measure_td_hob(&self, memory_size: u64) -> Result<Vec<u8>> {

kms/dstack-app/builder/Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ RUN cd dstack && cargo build --release -p dstack-kms --target x86_64-unknown-lin
2727
FROM debian:bookworm@sha256:0d8498a0e9e6a60011df39aab78534cfe940785e7c59d19dfae1eb53ea59babe
2828
COPY ./shared /build
2929
WORKDIR /build
30-
ARG QEMU_REV=d98440811192c08eafc07c7af110593c6b3758ff
30+
ARG QEMU_REV=dbcec07c0854bf873d346a09e87e4c993ccf2633
3131
RUN ./pin-packages.sh ./qemu-pinned-packages.txt && \
3232
apt-get update && \
3333
apt-get install -y --no-install-recommends \
@@ -43,7 +43,7 @@ RUN ./pin-packages.sh ./qemu-pinned-packages.txt && \
4343
flex \
4444
bison && \
4545
rm -rf /var/lib/apt/lists/* /var/log/* /var/cache/ldconfig/aux-cache
46-
RUN git clone https://github.com/kvinwang/qemu-tdx.git --depth 1 --branch passthrough-dump-acpi --single-branch && \
46+
RUN git clone https://github.com/kvinwang/qemu-tdx.git --depth 1 --branch dstack-qemu-9.2.1 --single-branch && \
4747
cd qemu-tdx && git fetch --depth 1 origin ${QEMU_REV} && \
4848
git checkout ${QEMU_REV} && \
4949
../config-qemu.sh ./build /usr/local && \

0 commit comments

Comments
 (0)