Skip to content

Commit

Permalink
Merge pull request #390 from roy-hopkins/igvm_measure_target
Browse files Browse the repository at this point in the history
igvmmeasure: Implement launch measurement calculation for SEV and SEV-ES
  • Loading branch information
joergroedel authored Jun 28, 2024
2 parents 126400b + 9a195ab commit 4426095
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 65 deletions.
8 changes: 1 addition & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ bitflags = "2.4"
clap = { version = "4.4.14", default-features = false}
gdbstub = { version = "0.6.6", default-features = false }
gdbstub_arch = { version = "0.2.4" }
hmac-sha512 = "1.1.5"
sha2 = "0.10.8"
igvm_defs = { version = "0.3.2", default-features = false}
igvm = { version = "0.3.2", default-features = false}
intrusive-collections = "0.9.6"
Expand Down
2 changes: 1 addition & 1 deletion igvmmeasure/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ edition = "2021"
# see https://doc.rust-lang.org/cargo/reference/features.html#feature-unification
[target.'cfg(all(target_os = "linux"))'.dependencies]
clap = { workspace = true, default-features = true, features = ["derive"] }
hmac-sha512.workspace = true
sha2.workspace = true
igvm.workspace = true
igvm_defs.workspace = true
p384.workspace = true
Expand Down
4 changes: 4 additions & 0 deletions igvmmeasure/src/cmd_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ pub enum Commands {

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)]
pub enum Platform {
/// Calculate the launch measurement for SEV
Sev,
/// Calculate the launch measurement for SEV-ES
SevEs,
/// Calculate the launch measurement for SEV-SNP
SevSnp,
}
4 changes: 3 additions & 1 deletion igvmmeasure/src/id_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,15 @@ pub struct SevIdBlockBuilder {

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

let mut ld = [0u8; 48];
ld.copy_from_slice(measure.digest());

Ok(Self {
compatibility_mask,
id_block: SevIdBlock {
Expand Down
133 changes: 88 additions & 45 deletions igvmmeasure/src/igvm_measure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ use std::error::Error;

use igvm::snp_defs::SevVmsa;
use igvm::{IgvmDirectiveHeader, IgvmFile};
use igvm_defs::{IgvmPageDataFlags, IgvmPageDataType, PAGE_SIZE_4K};
use igvm_defs::{IgvmPageDataFlags, IgvmPageDataType, IgvmPlatformType, PAGE_SIZE_4K};
use sha2::{Digest, Sha256};
use zerocopy::AsBytes;

use crate::page_info::PageInfo;
Expand Down Expand Up @@ -123,19 +124,35 @@ impl std::fmt::Display for SnpPageType {
}
}

#[derive(Debug)]
struct IgvmMeasureContext {
digest_snp: [u8; 48],
digest_es: Sha256,
}

impl Default for IgvmMeasureContext {
fn default() -> Self {
Self {
digest_snp: [0u8; 48],
digest_es: Sha256::default(),
}
}
}

#[derive(Debug)]
pub struct IgvmMeasure {
show_progress: bool,
check_kvm: bool,
native_zero: bool,
digest: [u8; 48],
digest: Vec<u8>,
last_page_type: SnpPageType,
last_gpa: u64,
last_next_gpa: u64,
last_len: u64,
compatibility_mask: u32,
vmsa_count: u32,
id_block_ld: Option<[u8; 48]>,
platform: IgvmPlatformType,
}

const PAGE_SIZE_2M: u64 = 2 * 1024 * 1024;
Expand All @@ -146,39 +163,45 @@ impl IgvmMeasure {
check_kvm: bool,
native_zero: bool,
compatibility_mask: u32,
platform: IgvmPlatformType,
igvm: &IgvmFile,
) -> Result<Self, Box<dyn Error>> {
let mut result = Self {
show_progress,
check_kvm,
native_zero,
digest: [0u8; 48],
digest: vec![],
last_page_type: SnpPageType::None,
last_gpa: 0,
last_next_gpa: 0,
last_len: 0,
compatibility_mask,
vmsa_count: 0,
id_block_ld: None,
platform,
};
result.do_measure(igvm)?;
Ok(result)
}

pub fn check_id_block(&self) -> Result<(), IgvmMeasureError> {
if let Some(expected_ld) = self.id_block_ld {
if expected_ld != self.digest {
let mut ld = [0u8; 48];
ld.copy_from_slice(self.digest());
if expected_ld != ld {
return Err(IgvmMeasureError::IDBlockMismatch(expected_ld));
}
}
Ok(())
}

pub fn digest(&self) -> [u8; 48] {
self.digest
pub fn digest(&self) -> &Vec<u8> {
&self.digest
}

fn do_measure(&mut self, igvm: &IgvmFile) -> Result<(), Box<dyn Error>> {
let mut ctx = IgvmMeasureContext::default();

for directive in igvm.directives() {
match directive {
IgvmDirectiveHeader::PageData {
Expand All @@ -189,7 +212,7 @@ impl IgvmMeasure {
data,
} => {
if (*compatibility_mask & self.compatibility_mask) != 0 {
self.measure_page(*gpa, flags, *data_type, data)?;
self.measure_page(&mut ctx, *gpa, flags, *data_type, data)?;
}
}
IgvmDirectiveHeader::ParameterInsert(param) => {
Expand All @@ -198,6 +221,7 @@ impl IgvmMeasure {
return Err(IgvmMeasureError::InvalidVmsaOrder.into());
}
self.measure_page(
&mut ctx,
param.gpa,
&IgvmPageDataFlags::new().with_unmeasured(true),
IgvmPageDataType::NORMAL,
Expand All @@ -211,7 +235,7 @@ impl IgvmMeasure {
vp_index: _vp_index,
vmsa,
} => {
self.measure_vmsa(*gpa, *compatibility_mask, vmsa)?;
self.measure_vmsa(&mut ctx, *gpa, *compatibility_mask, vmsa)?;
}
IgvmDirectiveHeader::SnpIdBlock {
compatibility_mask,
Expand All @@ -226,6 +250,12 @@ impl IgvmMeasure {
}
}
self.log_page(SnpPageType::None, 0, 0);

if (self.platform == IgvmPlatformType::SEV_ES) || (self.platform == IgvmPlatformType::SEV) {
self.digest = ctx.digest_es.finalize_reset().to_vec();
} else {
self.digest = ctx.digest_snp.to_vec();
}
Ok(())
}

Expand All @@ -251,6 +281,7 @@ impl IgvmMeasure {

fn measure_page(
&mut self,
ctx: &mut IgvmMeasureContext,
gpa: u64,
flags: &IgvmPageDataFlags,
data_type: IgvmPageDataType,
Expand All @@ -265,11 +296,12 @@ impl IgvmMeasure {

if data.is_empty() {
for page_offset in (0..page_len).step_by(PAGE_SIZE_4K as usize) {
self.measure_page_4k(gpa + page_offset, flags, data_type, &vec![]);
self.measure_page_4k(ctx, gpa + page_offset, flags, data_type, &vec![]);
}
} else {
for (index, page_data) in data.chunks(PAGE_SIZE_4K as usize).enumerate() {
self.measure_page_4k(
ctx,
gpa + index as u64 * PAGE_SIZE_4K,
flags,
data_type,
Expand All @@ -282,49 +314,55 @@ impl IgvmMeasure {

fn measure_page_4k(
&mut self,
ctx: &mut IgvmMeasureContext,
gpa: u64,
flags: &IgvmPageDataFlags,
data_type: IgvmPageDataType,
data: &Vec<u8>,
) {
let page_info = match data_type {
IgvmPageDataType::NORMAL => {
if flags.unmeasured() {
self.log_page(SnpPageType::Unmeasured, gpa, PAGE_SIZE_4K);
Some(PageInfo::new_unmeasured_page(self.digest, gpa))
} else if data.is_empty() {
if self.native_zero {
self.log_page(SnpPageType::Zero, gpa, PAGE_SIZE_4K);
Some(PageInfo::new_zero_page(self.digest, gpa))
if self.platform == IgvmPlatformType::SEV_SNP {
let page_info = match data_type {
IgvmPageDataType::NORMAL => {
if flags.unmeasured() {
self.log_page(SnpPageType::Unmeasured, gpa, PAGE_SIZE_4K);
Some(PageInfo::new_unmeasured_page(ctx.digest_snp, gpa))
} else if data.is_empty() {
if self.native_zero {
self.log_page(SnpPageType::Zero, gpa, PAGE_SIZE_4K);
Some(PageInfo::new_zero_page(ctx.digest_snp, gpa))
} else {
self.log_page(SnpPageType::Normal, gpa, PAGE_SIZE_4K);
Some(PageInfo::new_normal_page(
ctx.digest_snp,
gpa,
&vec![0u8; PAGE_SIZE_4K as usize],
))
}
} else {
self.log_page(SnpPageType::Normal, gpa, PAGE_SIZE_4K);
Some(PageInfo::new_normal_page(
self.digest,
gpa,
&vec![0u8; PAGE_SIZE_4K as usize],
))
self.log_page(SnpPageType::Normal, gpa, data.len() as u64);
Some(PageInfo::new_normal_page(ctx.digest_snp, gpa, data))
}
} else {
self.log_page(SnpPageType::Normal, gpa, data.len() as u64);
Some(PageInfo::new_normal_page(self.digest, gpa, data))
}
IgvmPageDataType::SECRETS => {
self.log_page(SnpPageType::Secrets, gpa, PAGE_SIZE_4K);
Some(PageInfo::new_secrets_page(ctx.digest_snp, gpa))
}
IgvmPageDataType::CPUID_DATA => {
self.log_page(SnpPageType::CpuId, gpa, PAGE_SIZE_4K);
Some(PageInfo::new_cpuid_page(ctx.digest_snp, gpa))
}
IgvmPageDataType::CPUID_XF => {
self.log_page(SnpPageType::CpuId, gpa, PAGE_SIZE_4K);
Some(PageInfo::new_cpuid_page(ctx.digest_snp, gpa))
}
_ => None,
};
if let Some(page_info) = page_info {
ctx.digest_snp = page_info.update_hash();
}
IgvmPageDataType::SECRETS => {
self.log_page(SnpPageType::Secrets, gpa, PAGE_SIZE_4K);
Some(PageInfo::new_secrets_page(self.digest, gpa))
}
IgvmPageDataType::CPUID_DATA => {
self.log_page(SnpPageType::CpuId, gpa, PAGE_SIZE_4K);
Some(PageInfo::new_cpuid_page(self.digest, gpa))
}
IgvmPageDataType::CPUID_XF => {
self.log_page(SnpPageType::CpuId, gpa, PAGE_SIZE_4K);
Some(PageInfo::new_cpuid_page(self.digest, gpa))
}
_ => None,
};
if let Some(page_info) = page_info {
self.digest = page_info.update_hash();
} else {
self.log_page(SnpPageType::Normal, gpa, PAGE_SIZE_4K);
ctx.digest_es.update(data);
}
}

Expand All @@ -348,6 +386,7 @@ impl IgvmMeasure {

fn measure_vmsa(
&mut self,
ctx: &mut IgvmMeasureContext,
gpa: u64,
_compatibility_mask: u32,
vmsa: &SevVmsa,
Expand All @@ -357,8 +396,12 @@ impl IgvmMeasure {
let mut vmsa_page = vmsa.as_bytes().to_vec();
vmsa_page.resize(PAGE_SIZE_4K as usize, 0);
self.log_page(SnpPageType::Vmsa, gpa, PAGE_SIZE_4K);
let page_info = PageInfo::new_vmsa_page(self.digest, gpa, &vmsa_page);
self.digest = page_info.update_hash();
if self.platform == IgvmPlatformType::SEV_SNP {
let page_info = PageInfo::new_vmsa_page(ctx.digest_snp, gpa, &vmsa_page);
ctx.digest_snp = page_info.update_hash();
} else {
ctx.digest_es.update(vmsa_page.as_bytes());
}
self.vmsa_count += 1;

Ok(())
Expand Down
23 changes: 17 additions & 6 deletions igvmmeasure/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::fs::{self, File};
use std::io::Write;

use clap::Parser;
use cmd_options::{CmdOptions, Commands};
use cmd_options::{CmdOptions, Commands, Platform};
use igvm::IgvmFile;
use igvm_defs::IgvmPlatformType;
use igvm_measure::IgvmMeasure;
Expand All @@ -32,15 +32,21 @@ fn main() -> Result<(), Box<dyn Error>> {
e
})?;
let igvm = IgvmFile::new_from_binary(igvm_buffer.as_bytes(), None)?;
let compatibility_mask = get_compatibility_mask(&igvm, IgvmPlatformType::SEV_SNP).ok_or(
String::from("IGVM file is not compatible with the specified platform."),
)?;
let platform = match options.platform {
Platform::Sev => IgvmPlatformType::SEV,
Platform::SevEs => IgvmPlatformType::SEV_ES,
Platform::SevSnp => IgvmPlatformType::SEV_SNP,
};
let compatibility_mask = get_compatibility_mask(&igvm, platform).ok_or(String::from(
"IGVM file is not compatible with the specified platform.",
))?;

let measure = IgvmMeasure::measure(
options.verbose,
options.check_kvm,
options.native_zero,
compatibility_mask,
platform,
&igvm,
)?;

Expand All @@ -53,7 +59,12 @@ fn main() -> Result<(), Box<dyn Error>> {
output,
id_key,
author_key,
} => sign_command(&output, &id_key, &author_key, &igvm, &measure)?,
} => {
if options.platform != Platform::SevSnp {
return Err("Signing is only supported for SEV-SNP".into());
}
sign_command(&output, &id_key, &author_key, &igvm, &measure)?;
}
}

Ok(())
Expand Down Expand Up @@ -84,7 +95,7 @@ fn measure_command(
);
}

if !ignore_idblock {
if (options.platform == Platform::SevSnp) && !ignore_idblock {
measure.check_id_block()?;
}

Expand Down
Loading

0 comments on commit 4426095

Please sign in to comment.