pub(crate) mod builder;
pub mod profile;
use std::collections::HashSet;
pub use igvm_defs::IgvmPlatformType;
use crate::CorimTemplate;
const TDX_TAG_ID: [u8; 16] = [
0xe0, 0x51, 0x00, 0x81, 0x7b, 0x78, 0x5b, 0x2a, 0x97, 0xa6, 0xd7, 0x3d, 0x89, 0x0e, 0x07, 0xb6,
];
const SEV_SNP_TAG_ID: [u8; 16] = [
0x77, 0xe8, 0x06, 0x1e, 0x46, 0x34, 0x5e, 0x53, 0xa8, 0x48, 0xd1, 0xd0, 0x9e, 0x99, 0x68, 0x43,
];
const VBS_TAG_ID: [u8; 16] = [
0x2e, 0x29, 0x06, 0x8e, 0xe0, 0xfa, 0x59, 0xe6, 0xb0, 0xff, 0x3b, 0xfe, 0x09, 0x13, 0x2e, 0x13,
];
pub(crate) struct PlatformInfo {
pub vendor: &'static str,
pub model: &'static str,
pub mkey: &'static str,
pub digest_alg: i64,
pub digest_len: usize,
pub tag_id: [u8; 16],
}
const NI_SHA256: i64 = 1;
const NI_SHA384: i64 = 7;
pub(crate) fn known_platforms() -> &'static [PlatformInfo] {
&[
PlatformInfo {
vendor: "Intel",
model: "TDX",
mkey: "MRTD",
digest_alg: NI_SHA384,
digest_len: 48,
tag_id: TDX_TAG_ID,
},
PlatformInfo {
vendor: "AMD",
model: "SEV-SNP",
mkey: "MEASUREMENT",
digest_alg: NI_SHA384,
digest_len: 48,
tag_id: SEV_SNP_TAG_ID,
},
PlatformInfo {
vendor: "Microsoft",
model: "VBS",
mkey: "MEASUREMENT",
digest_alg: NI_SHA256,
digest_len: 32,
tag_id: VBS_TAG_ID,
},
]
}
fn platform_info(platform: IgvmPlatformType) -> Option<&'static PlatformInfo> {
let (vendor, model) = match platform {
IgvmPlatformType::TDX => ("Intel", "TDX"),
IgvmPlatformType::SEV_SNP => ("AMD", "SEV-SNP"),
IgvmPlatformType::VSM_ISOLATION => ("Microsoft", "VBS"),
_ => return None,
};
known_platforms()
.iter()
.find(|p| p.vendor == vendor && p.model == model)
}
pub fn measurement_info(
platform: IgvmPlatformType,
kind: MeasurementKind,
) -> Option<(&'static str, i64, usize)> {
let info = platform_info(platform)?;
match kind {
MeasurementKind::Launch => Some((info.mkey, info.digest_alg, info.digest_len)),
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[non_exhaustive]
pub enum MeasurementKind {
Launch,
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error(
"unsupported platform type {0:?}: only SEV_SNP, TDX, and \
VSM_ISOLATION are supported for CoRIM launch measurements"
)]
UnsupportedPlatform(IgvmPlatformType),
#[error("measurement {kind:?} is not defined for platform {platform:?}")]
UnsupportedMeasurement {
platform: IgvmPlatformType,
kind: MeasurementKind,
},
#[error(
"measurement {kind:?} is not populated; call \
LaunchMeasurement::set_measurement first"
)]
MeasurementNotPopulated {
kind: MeasurementKind,
},
#[error("measurement {kind:?} already selected in this CES triple")]
DuplicateSelection {
kind: MeasurementKind,
},
#[error("CES triple must select at least one measurement")]
EmptySelection,
#[error("CoRIM build failed")]
Build(#[source] Box<dyn std::error::Error + Send + Sync>),
}
#[derive(Debug, Clone)]
pub struct LaunchMeasurement {
platform: IgvmPlatformType,
measurements: HashSet<MeasurementKind>,
triples: Vec<CesTriple>,
}
impl LaunchMeasurement {
pub fn for_platform(platform: IgvmPlatformType) -> Result<Self, Error> {
if platform_info(platform).is_none() {
return Err(Error::UnsupportedPlatform(platform));
}
Ok(Self {
platform,
measurements: HashSet::new(),
triples: Vec::new(),
})
}
pub fn platform(&self) -> IgvmPlatformType {
self.platform
}
pub fn set_measurement(&mut self, kind: MeasurementKind) -> Result<&mut Self, Error> {
if measurement_info(self.platform, kind).is_none() {
return Err(Error::UnsupportedMeasurement {
platform: self.platform,
kind,
});
}
self.measurements.insert(kind);
Ok(self)
}
pub fn populated_measurements(&self) -> impl Iterator<Item = MeasurementKind> + '_ {
self.measurements.iter().copied()
}
pub fn is_populated(&self, kind: MeasurementKind) -> bool {
self.measurements.contains(&kind)
}
pub fn endorse(&mut self, svn: u64) -> CesTripleBuilder<'_> {
CesTripleBuilder {
endorsement: self,
svn,
selected: Vec::new(),
}
}
pub fn triples(&self) -> &[CesTriple] {
&self.triples
}
pub fn build(self) -> CorimTemplate {
CorimTemplate::LaunchMeasurement(self)
}
pub(crate) fn measurement_kinds(&self) -> &HashSet<MeasurementKind> {
&self.measurements
}
}
#[derive(Debug)]
pub struct CesTripleBuilder<'a> {
endorsement: &'a mut LaunchMeasurement,
svn: u64,
selected: Vec<MeasurementKind>,
}
impl CesTripleBuilder<'_> {
pub fn with(mut self, kind: MeasurementKind) -> Result<Self, Error> {
if !self.endorsement.measurements.contains(&kind) {
return Err(Error::MeasurementNotPopulated { kind });
}
if self.selected.contains(&kind) {
return Err(Error::DuplicateSelection { kind });
}
self.selected.push(kind);
Ok(self)
}
pub fn finish(self) -> Result<(), Error> {
if self.selected.is_empty() {
return Err(Error::EmptySelection);
}
self.endorsement.triples.push(CesTriple {
svn: self.svn,
selected: self.selected,
});
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct CesTriple {
svn: u64,
selected: Vec<MeasurementKind>,
}
impl CesTriple {
pub fn svn(&self) -> u64 {
self.svn
}
pub fn selected_measurements(&self) -> &[MeasurementKind] {
&self.selected
}
}