Skip to content

Commit 4426095

Browse files
authored
Merge pull request #390 from roy-hopkins/igvm_measure_target
igvmmeasure: Implement launch measurement calculation for SEV and SEV-ES
2 parents 126400b + 9a195ab commit 4426095

File tree

8 files changed

+125
-65
lines changed

8 files changed

+125
-65
lines changed

Cargo.lock

+1-7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ bitflags = "2.4"
3535
clap = { version = "4.4.14", default-features = false}
3636
gdbstub = { version = "0.6.6", default-features = false }
3737
gdbstub_arch = { version = "0.2.4" }
38-
hmac-sha512 = "1.1.5"
38+
sha2 = "0.10.8"
3939
igvm_defs = { version = "0.3.2", default-features = false}
4040
igvm = { version = "0.3.2", default-features = false}
4141
intrusive-collections = "0.9.6"

igvmmeasure/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ edition = "2021"
77
# see https://doc.rust-lang.org/cargo/reference/features.html#feature-unification
88
[target.'cfg(all(target_os = "linux"))'.dependencies]
99
clap = { workspace = true, default-features = true, features = ["derive"] }
10-
hmac-sha512.workspace = true
10+
sha2.workspace = true
1111
igvm.workspace = true
1212
igvm_defs.workspace = true
1313
p384.workspace = true

igvmmeasure/src/cmd_options.rs

+4
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ pub enum Commands {
8585

8686
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)]
8787
pub enum Platform {
88+
/// Calculate the launch measurement for SEV
89+
Sev,
90+
/// Calculate the launch measurement for SEV-ES
91+
SevEs,
8892
/// Calculate the launch measurement for SEV-SNP
8993
SevSnp,
9094
}

igvmmeasure/src/id_block.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,15 @@ pub struct SevIdBlockBuilder {
3939

4040
impl SevIdBlockBuilder {
4141
pub fn build(igvm: &IgvmFile, measure: &IgvmMeasure) -> Result<Self, Box<dyn Error>> {
42-
let ld = measure.digest();
4342
let compatibility_mask = get_compatibility_mask(igvm, IgvmPlatformType::SEV_SNP).ok_or(
4443
String::from("IGVM file is not compatible with the specified platform."),
4544
)?;
4645
let policy = get_policy(igvm, compatibility_mask)
4746
.ok_or(String::from("IGVM file does not contain a guest policy."))?;
4847

48+
let mut ld = [0u8; 48];
49+
ld.copy_from_slice(measure.digest());
50+
4951
Ok(Self {
5052
compatibility_mask,
5153
id_block: SevIdBlock {

igvmmeasure/src/igvm_measure.rs

+88-45
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ use std::error::Error;
88

99
use igvm::snp_defs::SevVmsa;
1010
use igvm::{IgvmDirectiveHeader, IgvmFile};
11-
use igvm_defs::{IgvmPageDataFlags, IgvmPageDataType, PAGE_SIZE_4K};
11+
use igvm_defs::{IgvmPageDataFlags, IgvmPageDataType, IgvmPlatformType, PAGE_SIZE_4K};
12+
use sha2::{Digest, Sha256};
1213
use zerocopy::AsBytes;
1314

1415
use crate::page_info::PageInfo;
@@ -123,19 +124,35 @@ impl std::fmt::Display for SnpPageType {
123124
}
124125
}
125126

127+
#[derive(Debug)]
128+
struct IgvmMeasureContext {
129+
digest_snp: [u8; 48],
130+
digest_es: Sha256,
131+
}
132+
133+
impl Default for IgvmMeasureContext {
134+
fn default() -> Self {
135+
Self {
136+
digest_snp: [0u8; 48],
137+
digest_es: Sha256::default(),
138+
}
139+
}
140+
}
141+
126142
#[derive(Debug)]
127143
pub struct IgvmMeasure {
128144
show_progress: bool,
129145
check_kvm: bool,
130146
native_zero: bool,
131-
digest: [u8; 48],
147+
digest: Vec<u8>,
132148
last_page_type: SnpPageType,
133149
last_gpa: u64,
134150
last_next_gpa: u64,
135151
last_len: u64,
136152
compatibility_mask: u32,
137153
vmsa_count: u32,
138154
id_block_ld: Option<[u8; 48]>,
155+
platform: IgvmPlatformType,
139156
}
140157

141158
const PAGE_SIZE_2M: u64 = 2 * 1024 * 1024;
@@ -146,39 +163,45 @@ impl IgvmMeasure {
146163
check_kvm: bool,
147164
native_zero: bool,
148165
compatibility_mask: u32,
166+
platform: IgvmPlatformType,
149167
igvm: &IgvmFile,
150168
) -> Result<Self, Box<dyn Error>> {
151169
let mut result = Self {
152170
show_progress,
153171
check_kvm,
154172
native_zero,
155-
digest: [0u8; 48],
173+
digest: vec![],
156174
last_page_type: SnpPageType::None,
157175
last_gpa: 0,
158176
last_next_gpa: 0,
159177
last_len: 0,
160178
compatibility_mask,
161179
vmsa_count: 0,
162180
id_block_ld: None,
181+
platform,
163182
};
164183
result.do_measure(igvm)?;
165184
Ok(result)
166185
}
167186

168187
pub fn check_id_block(&self) -> Result<(), IgvmMeasureError> {
169188
if let Some(expected_ld) = self.id_block_ld {
170-
if expected_ld != self.digest {
189+
let mut ld = [0u8; 48];
190+
ld.copy_from_slice(self.digest());
191+
if expected_ld != ld {
171192
return Err(IgvmMeasureError::IDBlockMismatch(expected_ld));
172193
}
173194
}
174195
Ok(())
175196
}
176197

177-
pub fn digest(&self) -> [u8; 48] {
178-
self.digest
198+
pub fn digest(&self) -> &Vec<u8> {
199+
&self.digest
179200
}
180201

181202
fn do_measure(&mut self, igvm: &IgvmFile) -> Result<(), Box<dyn Error>> {
203+
let mut ctx = IgvmMeasureContext::default();
204+
182205
for directive in igvm.directives() {
183206
match directive {
184207
IgvmDirectiveHeader::PageData {
@@ -189,7 +212,7 @@ impl IgvmMeasure {
189212
data,
190213
} => {
191214
if (*compatibility_mask & self.compatibility_mask) != 0 {
192-
self.measure_page(*gpa, flags, *data_type, data)?;
215+
self.measure_page(&mut ctx, *gpa, flags, *data_type, data)?;
193216
}
194217
}
195218
IgvmDirectiveHeader::ParameterInsert(param) => {
@@ -198,6 +221,7 @@ impl IgvmMeasure {
198221
return Err(IgvmMeasureError::InvalidVmsaOrder.into());
199222
}
200223
self.measure_page(
224+
&mut ctx,
201225
param.gpa,
202226
&IgvmPageDataFlags::new().with_unmeasured(true),
203227
IgvmPageDataType::NORMAL,
@@ -211,7 +235,7 @@ impl IgvmMeasure {
211235
vp_index: _vp_index,
212236
vmsa,
213237
} => {
214-
self.measure_vmsa(*gpa, *compatibility_mask, vmsa)?;
238+
self.measure_vmsa(&mut ctx, *gpa, *compatibility_mask, vmsa)?;
215239
}
216240
IgvmDirectiveHeader::SnpIdBlock {
217241
compatibility_mask,
@@ -226,6 +250,12 @@ impl IgvmMeasure {
226250
}
227251
}
228252
self.log_page(SnpPageType::None, 0, 0);
253+
254+
if (self.platform == IgvmPlatformType::SEV_ES) || (self.platform == IgvmPlatformType::SEV) {
255+
self.digest = ctx.digest_es.finalize_reset().to_vec();
256+
} else {
257+
self.digest = ctx.digest_snp.to_vec();
258+
}
229259
Ok(())
230260
}
231261

@@ -251,6 +281,7 @@ impl IgvmMeasure {
251281

252282
fn measure_page(
253283
&mut self,
284+
ctx: &mut IgvmMeasureContext,
254285
gpa: u64,
255286
flags: &IgvmPageDataFlags,
256287
data_type: IgvmPageDataType,
@@ -265,11 +296,12 @@ impl IgvmMeasure {
265296

266297
if data.is_empty() {
267298
for page_offset in (0..page_len).step_by(PAGE_SIZE_4K as usize) {
268-
self.measure_page_4k(gpa + page_offset, flags, data_type, &vec![]);
299+
self.measure_page_4k(ctx, gpa + page_offset, flags, data_type, &vec![]);
269300
}
270301
} else {
271302
for (index, page_data) in data.chunks(PAGE_SIZE_4K as usize).enumerate() {
272303
self.measure_page_4k(
304+
ctx,
273305
gpa + index as u64 * PAGE_SIZE_4K,
274306
flags,
275307
data_type,
@@ -282,49 +314,55 @@ impl IgvmMeasure {
282314

283315
fn measure_page_4k(
284316
&mut self,
317+
ctx: &mut IgvmMeasureContext,
285318
gpa: u64,
286319
flags: &IgvmPageDataFlags,
287320
data_type: IgvmPageDataType,
288321
data: &Vec<u8>,
289322
) {
290-
let page_info = match data_type {
291-
IgvmPageDataType::NORMAL => {
292-
if flags.unmeasured() {
293-
self.log_page(SnpPageType::Unmeasured, gpa, PAGE_SIZE_4K);
294-
Some(PageInfo::new_unmeasured_page(self.digest, gpa))
295-
} else if data.is_empty() {
296-
if self.native_zero {
297-
self.log_page(SnpPageType::Zero, gpa, PAGE_SIZE_4K);
298-
Some(PageInfo::new_zero_page(self.digest, gpa))
323+
if self.platform == IgvmPlatformType::SEV_SNP {
324+
let page_info = match data_type {
325+
IgvmPageDataType::NORMAL => {
326+
if flags.unmeasured() {
327+
self.log_page(SnpPageType::Unmeasured, gpa, PAGE_SIZE_4K);
328+
Some(PageInfo::new_unmeasured_page(ctx.digest_snp, gpa))
329+
} else if data.is_empty() {
330+
if self.native_zero {
331+
self.log_page(SnpPageType::Zero, gpa, PAGE_SIZE_4K);
332+
Some(PageInfo::new_zero_page(ctx.digest_snp, gpa))
333+
} else {
334+
self.log_page(SnpPageType::Normal, gpa, PAGE_SIZE_4K);
335+
Some(PageInfo::new_normal_page(
336+
ctx.digest_snp,
337+
gpa,
338+
&vec![0u8; PAGE_SIZE_4K as usize],
339+
))
340+
}
299341
} else {
300-
self.log_page(SnpPageType::Normal, gpa, PAGE_SIZE_4K);
301-
Some(PageInfo::new_normal_page(
302-
self.digest,
303-
gpa,
304-
&vec![0u8; PAGE_SIZE_4K as usize],
305-
))
342+
self.log_page(SnpPageType::Normal, gpa, data.len() as u64);
343+
Some(PageInfo::new_normal_page(ctx.digest_snp, gpa, data))
306344
}
307-
} else {
308-
self.log_page(SnpPageType::Normal, gpa, data.len() as u64);
309-
Some(PageInfo::new_normal_page(self.digest, gpa, data))
310345
}
346+
IgvmPageDataType::SECRETS => {
347+
self.log_page(SnpPageType::Secrets, gpa, PAGE_SIZE_4K);
348+
Some(PageInfo::new_secrets_page(ctx.digest_snp, gpa))
349+
}
350+
IgvmPageDataType::CPUID_DATA => {
351+
self.log_page(SnpPageType::CpuId, gpa, PAGE_SIZE_4K);
352+
Some(PageInfo::new_cpuid_page(ctx.digest_snp, gpa))
353+
}
354+
IgvmPageDataType::CPUID_XF => {
355+
self.log_page(SnpPageType::CpuId, gpa, PAGE_SIZE_4K);
356+
Some(PageInfo::new_cpuid_page(ctx.digest_snp, gpa))
357+
}
358+
_ => None,
359+
};
360+
if let Some(page_info) = page_info {
361+
ctx.digest_snp = page_info.update_hash();
311362
}
312-
IgvmPageDataType::SECRETS => {
313-
self.log_page(SnpPageType::Secrets, gpa, PAGE_SIZE_4K);
314-
Some(PageInfo::new_secrets_page(self.digest, gpa))
315-
}
316-
IgvmPageDataType::CPUID_DATA => {
317-
self.log_page(SnpPageType::CpuId, gpa, PAGE_SIZE_4K);
318-
Some(PageInfo::new_cpuid_page(self.digest, gpa))
319-
}
320-
IgvmPageDataType::CPUID_XF => {
321-
self.log_page(SnpPageType::CpuId, gpa, PAGE_SIZE_4K);
322-
Some(PageInfo::new_cpuid_page(self.digest, gpa))
323-
}
324-
_ => None,
325-
};
326-
if let Some(page_info) = page_info {
327-
self.digest = page_info.update_hash();
363+
} else {
364+
self.log_page(SnpPageType::Normal, gpa, PAGE_SIZE_4K);
365+
ctx.digest_es.update(data);
328366
}
329367
}
330368

@@ -348,6 +386,7 @@ impl IgvmMeasure {
348386

349387
fn measure_vmsa(
350388
&mut self,
389+
ctx: &mut IgvmMeasureContext,
351390
gpa: u64,
352391
_compatibility_mask: u32,
353392
vmsa: &SevVmsa,
@@ -357,8 +396,12 @@ impl IgvmMeasure {
357396
let mut vmsa_page = vmsa.as_bytes().to_vec();
358397
vmsa_page.resize(PAGE_SIZE_4K as usize, 0);
359398
self.log_page(SnpPageType::Vmsa, gpa, PAGE_SIZE_4K);
360-
let page_info = PageInfo::new_vmsa_page(self.digest, gpa, &vmsa_page);
361-
self.digest = page_info.update_hash();
399+
if self.platform == IgvmPlatformType::SEV_SNP {
400+
let page_info = PageInfo::new_vmsa_page(ctx.digest_snp, gpa, &vmsa_page);
401+
ctx.digest_snp = page_info.update_hash();
402+
} else {
403+
ctx.digest_es.update(vmsa_page.as_bytes());
404+
}
362405
self.vmsa_count += 1;
363406

364407
Ok(())

igvmmeasure/src/main.rs

+17-6
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::fs::{self, File};
99
use std::io::Write;
1010

1111
use clap::Parser;
12-
use cmd_options::{CmdOptions, Commands};
12+
use cmd_options::{CmdOptions, Commands, Platform};
1313
use igvm::IgvmFile;
1414
use igvm_defs::IgvmPlatformType;
1515
use igvm_measure::IgvmMeasure;
@@ -32,15 +32,21 @@ fn main() -> Result<(), Box<dyn Error>> {
3232
e
3333
})?;
3434
let igvm = IgvmFile::new_from_binary(igvm_buffer.as_bytes(), None)?;
35-
let compatibility_mask = get_compatibility_mask(&igvm, IgvmPlatformType::SEV_SNP).ok_or(
36-
String::from("IGVM file is not compatible with the specified platform."),
37-
)?;
35+
let platform = match options.platform {
36+
Platform::Sev => IgvmPlatformType::SEV,
37+
Platform::SevEs => IgvmPlatformType::SEV_ES,
38+
Platform::SevSnp => IgvmPlatformType::SEV_SNP,
39+
};
40+
let compatibility_mask = get_compatibility_mask(&igvm, platform).ok_or(String::from(
41+
"IGVM file is not compatible with the specified platform.",
42+
))?;
3843

3944
let measure = IgvmMeasure::measure(
4045
options.verbose,
4146
options.check_kvm,
4247
options.native_zero,
4348
compatibility_mask,
49+
platform,
4450
&igvm,
4551
)?;
4652

@@ -53,7 +59,12 @@ fn main() -> Result<(), Box<dyn Error>> {
5359
output,
5460
id_key,
5561
author_key,
56-
} => sign_command(&output, &id_key, &author_key, &igvm, &measure)?,
62+
} => {
63+
if options.platform != Platform::SevSnp {
64+
return Err("Signing is only supported for SEV-SNP".into());
65+
}
66+
sign_command(&output, &id_key, &author_key, &igvm, &measure)?;
67+
}
5768
}
5869

5970
Ok(())
@@ -84,7 +95,7 @@ fn measure_command(
8495
);
8596
}
8697

87-
if !ignore_idblock {
98+
if (options.platform == Platform::SevSnp) && !ignore_idblock {
8899
measure.check_id_block()?;
89100
}
90101

0 commit comments

Comments
 (0)