Skip to content

Commit d4e11cd

Browse files
committed
seralizer: add set_corim_signature API
Signed-off-by: Ming-Wei Shih <mishih@microsoft.com>
1 parent 031a4a5 commit d4e11cd

1 file changed

Lines changed: 199 additions & 0 deletions

File tree

igvm/src/serializer.rs

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ pub struct IgvmSerializer<'a> {
8181
measurements: Vec<IgvmPlatformMeasurement>,
8282
extra_init_headers: Vec<IgvmInitializationHeader>,
8383
extra_directive_headers: Vec<IgvmDirectiveHeader>,
84+
suppressed_corim_masks: Vec<u32>,
8485
}
8586

8687
impl<'a> IgvmSerializer<'a> {
@@ -104,6 +105,7 @@ impl<'a> IgvmSerializer<'a> {
104105
measurements: Vec::new(),
105106
extra_init_headers: Vec::new(),
106107
extra_directive_headers: Vec::new(),
108+
suppressed_corim_masks: Vec::new(),
107109
};
108110

109111
// Eagerly compute the launch measurement for every supported
@@ -301,6 +303,89 @@ impl<'a> IgvmSerializer<'a> {
301303
}
302304
}
303305

306+
/// Set the CoRIM signature for a platform while preserving its
307+
/// corresponding CoRIM document.
308+
///
309+
/// On serialize, any in-file CoRIM document/signature pair for this
310+
/// platform's compatibility mask is suppressed and replaced with exactly
311+
/// one CoRIM document (the existing one) and one CoRIM signature.
312+
///
313+
/// This keeps CoRIM document/signature ordering valid and avoids callers
314+
/// needing to reconstruct a full [`IgvmFile`] just to patch signatures.
315+
#[cfg(feature = "corim")]
316+
#[cfg_attr(docsrs, doc(cfg(feature = "corim")))]
317+
pub fn set_corim_signature(
318+
&mut self,
319+
platform: IgvmPlatformType,
320+
signature: Vec<u8>,
321+
) -> Result<&[u8], Error> {
322+
let compatibility_mask = self.lookup_compatibility_mask(platform)?;
323+
324+
let existing_document = self
325+
.existing_corim_document(compatibility_mask)
326+
.ok_or_else(|| {
327+
Error::CorimGeneration(format!(
328+
"no CoRIM document found for {platform:?} (compatibility mask 0x{compatibility_mask:X})"
329+
))
330+
})?;
331+
332+
if !self.suppressed_corim_masks.contains(&compatibility_mask) {
333+
self.suppressed_corim_masks.push(compatibility_mask);
334+
}
335+
336+
self.extra_init_headers.retain(|h| match h {
337+
IgvmInitializationHeader::CorimDocument {
338+
compatibility_mask: mask,
339+
..
340+
}
341+
| IgvmInitializationHeader::CorimSignature {
342+
compatibility_mask: mask,
343+
..
344+
} => *mask != compatibility_mask,
345+
_ => true,
346+
});
347+
348+
self.extra_init_headers
349+
.push(IgvmInitializationHeader::CorimDocument {
350+
compatibility_mask,
351+
document: existing_document,
352+
});
353+
self.extra_init_headers
354+
.push(IgvmInitializationHeader::CorimSignature {
355+
compatibility_mask,
356+
signature,
357+
});
358+
359+
match self.extra_init_headers.last() {
360+
Some(IgvmInitializationHeader::CorimSignature { signature, .. }) => {
361+
Ok(signature.as_slice())
362+
}
363+
_ => unreachable!("just pushed a CorimSignature"),
364+
}
365+
}
366+
367+
/// Resolve the effective CoRIM document for a compatibility mask,
368+
/// preferring staged serializer additions over the base file.
369+
fn existing_corim_document(&self, compatibility_mask: u32) -> Option<Vec<u8>> {
370+
if let Some(document) = self.extra_init_headers.iter().rev().find_map(|h| match h {
371+
IgvmInitializationHeader::CorimDocument {
372+
compatibility_mask: mask,
373+
document,
374+
} if *mask == compatibility_mask => Some(document.clone()),
375+
_ => None,
376+
}) {
377+
return Some(document);
378+
}
379+
380+
self.file.initializations().iter().find_map(|h| match h {
381+
IgvmInitializationHeader::CorimDocument {
382+
compatibility_mask: mask,
383+
document,
384+
} if *mask == compatibility_mask => Some(document.clone()),
385+
_ => None,
386+
})
387+
}
388+
304389
/// Resolve a [`LaunchMeasurement`]'s populated measurements and CES
305390
/// triples into the internal builder form, then build the CoRIM bytes.
306391
#[cfg(feature = "corim")]
@@ -400,6 +485,18 @@ impl<'a> IgvmSerializer<'a> {
400485
// Clone the file and append the extra headers so that
401486
// the original IgvmFile::serialize handles all the work.
402487
let mut file = self.file.clone();
488+
file.initializations_mut().retain(|h| {
489+
let mask = match h {
490+
IgvmInitializationHeader::CorimDocument {
491+
compatibility_mask, ..
492+
}
493+
| IgvmInitializationHeader::CorimSignature {
494+
compatibility_mask, ..
495+
} => *compatibility_mask,
496+
_ => return true,
497+
};
498+
!self.suppressed_corim_masks.contains(&mask)
499+
});
403500
file.initializations_mut()
404501
.extend(self.extra_init_headers.iter().cloned());
405502
file.directives_mut()
@@ -500,6 +597,36 @@ mod tests {
500597
.unwrap()
501598
}
502599

600+
fn make_vbs_file_with_corim(document: Vec<u8>, signature: Vec<u8>) -> IgvmFile {
601+
IgvmFile::new(
602+
IgvmRevision::V2 {
603+
arch: Arch::X64,
604+
page_size: PAGE_SIZE_4K as u32,
605+
},
606+
vec![new_platform(0x1, IgvmPlatformType::VSM_ISOLATION)],
607+
vec![
608+
IgvmInitializationHeader::CorimDocument {
609+
compatibility_mask: 0x1,
610+
document,
611+
},
612+
IgvmInitializationHeader::CorimSignature {
613+
compatibility_mask: 0x1,
614+
signature,
615+
},
616+
],
617+
vec![
618+
new_page_data(0, 1, &[0xAA; PAGE_SIZE_4K as usize]),
619+
new_page_data(1, 1, &[0xBB; PAGE_SIZE_4K as usize]),
620+
crate::IgvmDirectiveHeader::X64VbsVpContext {
621+
vtl: Vtl::Vtl0,
622+
registers: vec![X86Register::Rip(0x1000)],
623+
compatibility_mask: 0x1,
624+
},
625+
],
626+
)
627+
.unwrap()
628+
}
629+
503630
fn new_snp_id_block(mask: u32, ld: [u8; 48]) -> crate::IgvmDirectiveHeader {
504631
crate::IgvmDirectiveHeader::SnpIdBlock {
505632
compatibility_mask: mask,
@@ -797,6 +924,78 @@ mod tests {
797924
assert_eq!(file.directives().len(), directive_count_before);
798925
}
799926

927+
#[test]
928+
fn set_corim_signature_preserves_document_and_updates_signature() {
929+
let original_document = b"existing-corim-document".to_vec();
930+
let original_signature = b"old-signature".to_vec();
931+
let new_signature = b"new-signature".to_vec();
932+
933+
let file = make_vbs_file_with_corim(original_document.clone(), original_signature);
934+
let mut serializer = IgvmSerializer::new(&file).unwrap();
935+
936+
serializer
937+
.set_corim_signature(IgvmPlatformType::VSM_ISOLATION, new_signature.clone())
938+
.unwrap();
939+
940+
let mut output = Vec::new();
941+
serializer.serialize(&mut output).unwrap();
942+
943+
let deserialized = IgvmFile::new_from_binary(&output, None).unwrap();
944+
let mut found_document = None;
945+
let mut found_signature = None;
946+
let mut document_index = None;
947+
let mut signature_index = None;
948+
for (idx, h) in deserialized.initializations().iter().enumerate() {
949+
match h {
950+
IgvmInitializationHeader::CorimDocument {
951+
compatibility_mask,
952+
document,
953+
} if *compatibility_mask == 0x1 => {
954+
found_document = Some(document.clone());
955+
document_index = Some(idx);
956+
}
957+
IgvmInitializationHeader::CorimSignature {
958+
compatibility_mask,
959+
signature,
960+
} if *compatibility_mask == 0x1 => {
961+
found_signature = Some(signature.clone());
962+
signature_index = Some(idx);
963+
}
964+
_ => {}
965+
}
966+
}
967+
968+
assert_eq!(found_document, Some(original_document));
969+
assert_eq!(found_signature, Some(new_signature));
970+
assert!(document_index.is_some());
971+
assert!(signature_index.is_some());
972+
assert!(document_index.unwrap() < signature_index.unwrap());
973+
}
974+
975+
#[test]
976+
fn set_corim_signature_requires_existing_document() {
977+
let file = make_vbs_file();
978+
let mut serializer = IgvmSerializer::new(&file).unwrap();
979+
980+
let err = serializer
981+
.set_corim_signature(IgvmPlatformType::VSM_ISOLATION, b"new-sig".to_vec())
982+
.unwrap_err();
983+
assert!(err.to_string().contains("no CoRIM document found"));
984+
}
985+
986+
#[test]
987+
fn file_not_mutated_after_set_corim_signature() {
988+
let file = make_vbs_file_with_corim(b"doc".to_vec(), b"sig".to_vec());
989+
let init_before = file.initializations().to_vec();
990+
991+
let mut serializer = IgvmSerializer::new(&file).unwrap();
992+
serializer
993+
.set_corim_signature(IgvmPlatformType::VSM_ISOLATION, b"new-sig".to_vec())
994+
.unwrap();
995+
996+
assert_eq!(file.initializations(), init_before.as_slice());
997+
}
998+
800999
// -- Two-stage builder tests -------------------------------------
8011000

8021001
#[test]

0 commit comments

Comments
 (0)