From 754315b4896da99e2f2e1bfc293a4264a86f8845 Mon Sep 17 00:00:00 2001 From: Chris Oo Date: Tue, 11 Nov 2025 15:21:42 -0800 Subject: [PATCH 01/10] igvm_defs: introduce corim measurement header --- igvm_defs/src/lib.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/igvm_defs/src/lib.rs b/igvm_defs/src/lib.rs index 45f836f..8be5737 100644 --- a/igvm_defs/src/lib.rs +++ b/igvm_defs/src/lib.rs @@ -341,6 +341,10 @@ pub enum IgvmVariableHeaderType { /// specified by a structure of type [`IGVM_VHS_PARAMETER`]. #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] IGVM_VHT_ENVIRONMENT_INFO_PARAMETER = 0x313, + /// A Corim measurement structure described by [`IGVM_VHS_CORIM_MEASUREMENT`]. + /// FIXME: should this be an init header to be early in the file? + #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] + IGVM_VHT_CORIM_MEASUREMENT = 0x314, } /// The range of header types for platform structures. @@ -1237,3 +1241,29 @@ pub enum VbsSigningAlgorithm { /// ECDSA P384. ECDSA_P384 = 0x1, } + +/// A structure defining a CoRIM measurement payload for a given platform. +/// +/// The payload described by this header is a COSE_Sign1 structure described in +/// section 4.2 in RFC https://datatracker.ietf.org/doc/draft-ietf-rats-corim/, which is a COSE_Sign1 structure with a CBOR corim payload. +/// +/// The CoRIM payload must adhere to the following specifications for each platform: +/// +/// | Platform | Specification | +/// |----------|---------------| +/// | Intel TDX | TBD | +/// | VBS | TBD | +/// | AMD SEV-SNP | TBD | +/// | ARM CCA | TBD | +#[repr(C)] +#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)] +struct IGVM_VHS_CORIM_MEASUREMENT { + /// Compatibility mask. + pub compatibility_mask: u32, + /// File offset for the CoRIM measurement payload. + pub file_offset: u32, + /// Size in bytes of the CoRIM measurement payload. + pub size_bytes: u32, + /// Reserved. + pub reserved: u32, +} From ab3dc4b8b92beae6555093551b4ada1d975aba35 Mon Sep 17 00:00:00 2001 From: Chris Oo Date: Wed, 12 Nov 2025 15:34:04 -0800 Subject: [PATCH 02/10] split corim measurement and payload --- igvm_defs/src/lib.rs | 44 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/igvm_defs/src/lib.rs b/igvm_defs/src/lib.rs index 8be5737..07cb057 100644 --- a/igvm_defs/src/lib.rs +++ b/igvm_defs/src/lib.rs @@ -345,6 +345,10 @@ pub enum IgvmVariableHeaderType { /// FIXME: should this be an init header to be early in the file? #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] IGVM_VHT_CORIM_MEASUREMENT = 0x314, + /// A Corim signature structure described by [`IGVM_VHS_CORIM_SIGNATURE`]. + /// FIXME: should this be an init header to be early in the file? + #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] + IGVM_VHT_CORIM_SIGNATURE = 0x315, } /// The range of header types for platform structures. @@ -1242,12 +1246,15 @@ pub enum VbsSigningAlgorithm { ECDSA_P384 = 0x1, } -/// A structure defining a CoRIM measurement payload for a given platform. +/// A structure defining a CoRIM CBOR payload for a given platform. TODO: rename +/// to remove measurement? /// -/// The payload described by this header is a COSE_Sign1 structure described in -/// section 4.2 in RFC https://datatracker.ietf.org/doc/draft-ietf-rats-corim/, which is a COSE_Sign1 structure with a CBOR corim payload. +/// The payload described by this header is a CBOR CoRIM payload. There may only +/// be one for a given platform. There may be an associated COSE_Sign1 structure +/// wrapping this payload, see [`IGVM_VHS_CORIM_SIGNATURE`]. /// -/// The CoRIM payload must adhere to the following specifications for each platform: +/// The CoRIM payload must adhere to the following specifications for each +/// platform: /// /// | Platform | Specification | /// |----------|---------------| @@ -1260,9 +1267,34 @@ pub enum VbsSigningAlgorithm { struct IGVM_VHS_CORIM_MEASUREMENT { /// Compatibility mask. pub compatibility_mask: u32, - /// File offset for the CoRIM measurement payload. + /// File offset for the CoRIM CBOR payload. + pub file_offset: u32, + /// Size in bytes of the CoRIM CBOR payload. + pub size_bytes: u32, + /// Reserved. + pub reserved: u32, +} + +/// This is a signed COSE_Sign1 structure wrapping a CoRIM CBOR payload for a +/// given platform. The payload measured by this CBOR is described the +/// corresponding [`IGVM_VHS_CORIM_MEASUREMENT`] structure. There cannot be this +/// structure without that one. +/// +/// Note that a user may choose to create a single CBOR containing this +/// COSE_Sign1 with the payload filled in by the other corim measurement +/// structure. +/// +/// The payload described by this header is a COSE_Sign1 structure described in +/// section 4.2 in RFC https://datatracker.ietf.org/doc/draft-ietf-rats-corim/, +/// which is a COSE_Sign1 structure with a CBOR corim payload. +#[repr(C)] +#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)] +struct IGVM_VHS_CORIM_SIGNATURE { + /// Compatibility mask. + pub compatibility_mask: u32, + /// File offset for the COSE_Sign1 measurement payload. pub file_offset: u32, - /// Size in bytes of the CoRIM measurement payload. + /// Size in bytes of the COSE_Sign1 measurement payload. pub size_bytes: u32, /// Reserved. pub reserved: u32, From ed9defa12da8f674d563ee1cddaa3e36609c8e41 Mon Sep 17 00:00:00 2001 From: Chris Oo Date: Tue, 10 Feb 2026 15:38:12 -0800 Subject: [PATCH 03/10] corim: update header names and clarify docs --- igvm_defs/src/lib.rs | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/igvm_defs/src/lib.rs b/igvm_defs/src/lib.rs index 07cb057..215cb16 100644 --- a/igvm_defs/src/lib.rs +++ b/igvm_defs/src/lib.rs @@ -341,10 +341,10 @@ pub enum IgvmVariableHeaderType { /// specified by a structure of type [`IGVM_VHS_PARAMETER`]. #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] IGVM_VHT_ENVIRONMENT_INFO_PARAMETER = 0x313, - /// A Corim measurement structure described by [`IGVM_VHS_CORIM_MEASUREMENT`]. + /// A Corim document structure described by [`IGVM_VHS_CORIM_DOCUMENT`]. /// FIXME: should this be an init header to be early in the file? #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] - IGVM_VHT_CORIM_MEASUREMENT = 0x314, + IGVM_VHT_CORIM_DOCUMENT = 0x314, /// A Corim signature structure described by [`IGVM_VHS_CORIM_SIGNATURE`]. /// FIXME: should this be an init header to be early in the file? #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] @@ -1246,12 +1246,11 @@ pub enum VbsSigningAlgorithm { ECDSA_P384 = 0x1, } -/// A structure defining a CoRIM CBOR payload for a given platform. TODO: rename -/// to remove measurement? +/// A structure defining a CoRIM CBOR document for a given platform. /// -/// The payload described by this header is a CBOR CoRIM payload. There may only +/// The data described by this header is a CBOR CoRIM document. There may only /// be one for a given platform. There may be an associated COSE_Sign1 structure -/// wrapping this payload, see [`IGVM_VHS_CORIM_SIGNATURE`]. +/// for this document, see [`IGVM_VHS_CORIM_SIGNATURE`]. /// /// The CoRIM payload must adhere to the following specifications for each /// platform: @@ -1264,7 +1263,7 @@ pub enum VbsSigningAlgorithm { /// | ARM CCA | TBD | #[repr(C)] #[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)] -struct IGVM_VHS_CORIM_MEASUREMENT { +struct IGVM_VHS_CORIM_DOCUMENT { /// Compatibility mask. pub compatibility_mask: u32, /// File offset for the CoRIM CBOR payload. @@ -1275,18 +1274,14 @@ struct IGVM_VHS_CORIM_MEASUREMENT { pub reserved: u32, } -/// This is a signed COSE_Sign1 structure wrapping a CoRIM CBOR payload for a -/// given platform. The payload measured by this CBOR is described the -/// corresponding [`IGVM_VHS_CORIM_MEASUREMENT`] structure. There cannot be this -/// structure without that one. +/// This structure descibres a COSE_Sign1 structure for a detached CoRIM CBOR +/// payload for a given platform. The payload measured by this CBOR is described +/// the corresponding [`IGVM_VHS_CORIM_DOCUMENT`] structure. There cannot be +/// this structure without that one. /// -/// Note that a user may choose to create a single CBOR containing this -/// COSE_Sign1 with the payload filled in by the other corim measurement -/// structure. -/// -/// The payload described by this header is a COSE_Sign1 structure described in -/// section 4.2 in RFC https://datatracker.ietf.org/doc/draft-ietf-rats-corim/, -/// which is a COSE_Sign1 structure with a CBOR corim payload. +/// For more information on the structure described by this header, see the +/// COSE_Sign1 structure described in section 4.2 in RFC +/// https://datatracker.ietf.org/doc/draft-ietf-rats-corim/. #[repr(C)] #[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)] struct IGVM_VHS_CORIM_SIGNATURE { From 928beb5fbb780cbeb02f59359b44e328b6b36ac8 Mon Sep 17 00:00:00 2001 From: Chris Oo Date: Wed, 11 Feb 2026 16:55:40 -0800 Subject: [PATCH 04/10] igvm: add corim header and basic validation --- igvm/src/lib.rs | 188 ++++++++++++++++++++++++++++++++++++++++++- igvm_defs/src/lib.rs | 8 +- 2 files changed, 188 insertions(+), 8 deletions(-) diff --git a/igvm/src/lib.rs b/igvm/src/lib.rs index 1398cce..7e72bf2 100644 --- a/igvm/src/lib.rs +++ b/igvm/src/lib.rs @@ -760,6 +760,17 @@ pub enum IgvmDirectiveHeader { signature: Box<[u8; 256]>, public_key: Box<[u8; 512]>, }, + CorimDocument { + compatibility_mask: u32, + // FUTURE: have the corim document in a typed structure, with the + // required per-plaform fields. + document: Vec, + }, + CorimSignature { + compatibility_mask: u32, + // FUTURE: have the corim signature in a typed structure + signature: Vec, + }, } impl fmt::Display for IgvmDirectiveHeader { @@ -947,6 +958,12 @@ pub enum BinaryHeaderError { UnsupportedX64Register(#[from] registers::UnsupportedRegister), #[error("unsupported AArch64 register")] UnsupportedAArch64Register(#[from] registers::UnsupportedRegister), + #[error("multiple corim documents for a given compatibility mask {0:x}")] + MultipleCorimDocuments(u32), + #[error("multiple corim signatures for a given compatibility mask {0:x}")] + MultipleCorimSignatures(u32), + #[error("corim document missing for compatibility mask {0:x} with corresponding signature")] + MissingCorimDocument(u32), } impl IgvmDirectiveHeader { @@ -974,6 +991,8 @@ impl IgvmDirectiveHeader { IgvmDirectiveHeader::ErrorRange { .. } => size_of::(), IgvmDirectiveHeader::SnpIdBlock { .. } => size_of::(), IgvmDirectiveHeader::VbsMeasurement { .. } => size_of::(), + IgvmDirectiveHeader::CorimDocument { .. } => size_of::(), + IgvmDirectiveHeader::CorimSignature { .. } => size_of::(), }; align_8(size_of::() + additional) @@ -1021,6 +1040,12 @@ impl IgvmDirectiveHeader { IgvmDirectiveHeader::EnvironmentInfo(_) => { IgvmVariableHeaderType::IGVM_VHT_ENVIRONMENT_INFO_PARAMETER } + IgvmDirectiveHeader::CorimDocument { .. } => { + IgvmVariableHeaderType::IGVM_VHT_CORIM_DOCUMENT + } + IgvmDirectiveHeader::CorimSignature { .. } => { + IgvmVariableHeaderType::IGVM_VHT_CORIM_SIGNATURE + } } } @@ -1421,6 +1446,48 @@ impl IgvmDirectiveHeader { variable_headers, ) } + IgvmDirectiveHeader::CorimDocument { + compatibility_mask, + document, + } => { + let file_offset = file_data.write_file_data(document); + + let corim_document = IGVM_VHS_CORIM_DOCUMENT { + compatibility_mask: *compatibility_mask, + reserved: 0, + file_offset, + size_bytes: document + .len() + .try_into() + .expect("corim document size must fit in u32"), + }; + append_header( + &corim_document, + IgvmVariableHeaderType::IGVM_VHT_CORIM_DOCUMENT, + variable_headers, + ); + } + IgvmDirectiveHeader::CorimSignature { + compatibility_mask, + signature, + } => { + let file_offset = file_data.write_file_data(signature); + + let corim_signature = IGVM_VHS_CORIM_SIGNATURE { + compatibility_mask: *compatibility_mask, + reserved: 0, + file_offset, + size_bytes: signature + .len() + .try_into() + .expect("corim signature size must fit in u32"), + }; + append_header( + &corim_signature, + IgvmVariableHeaderType::IGVM_VHT_CORIM_SIGNATURE, + variable_headers, + ); + } } Ok(()) @@ -1470,6 +1537,12 @@ impl IgvmDirectiveHeader { VbsMeasurement { compatibility_mask, .. } => Some(*compatibility_mask), + CorimDocument { + compatibility_mask, .. + } => Some(*compatibility_mask), + CorimSignature { + compatibility_mask, .. + } => Some(*compatibility_mask), } } @@ -1518,6 +1591,12 @@ impl IgvmDirectiveHeader { VbsMeasurement { compatibility_mask, .. } => Some(compatibility_mask), + CorimDocument { + compatibility_mask, .. + } => Some(compatibility_mask), + CorimSignature { + compatibility_mask, .. + } => Some(compatibility_mask), } } @@ -1675,10 +1754,17 @@ impl IgvmDirectiveHeader { return Err(BinaryHeaderError::UnalignedAddress(*gpa)); } } - //TODO: validate SNP + // TODO: validate SNP IgvmDirectiveHeader::SnpIdBlock { .. } => {} - //TODO: validate VBS + // TODO: validate VBS IgvmDirectiveHeader::VbsMeasurement { .. } => {} + // TODO: validate CoRIM document has the minimum fields required + // described by the corresponding specification for that platform. + IgvmDirectiveHeader::CorimDocument { .. } => {} + // TODO: validate CoRIM signature has the right fields, and + // correctly signs the corresponding document. This requires crypto + // crates and might need to be gated behind a feature flag. + IgvmDirectiveHeader::CorimSignature { .. } => {} } Ok(()) @@ -2053,6 +2139,44 @@ impl IgvmDirectiveHeader { { IgvmDirectiveHeader::DeviceTree(read_header(&mut variable_headers)?) } + IgvmVariableHeaderType::IGVM_VHT_CORIM_DOCUMENT + if length == size_of::() => + { + let IGVM_VHS_CORIM_DOCUMENT { + compatibility_mask, + reserved, + file_offset, + size_bytes, + } = read_header(&mut variable_headers)?; + + if reserved != 0 { + return Err(BinaryHeaderError::ReservedNotZero); + } + + IgvmDirectiveHeader::CorimDocument { + compatibility_mask, + document: extract_file_data(file_offset, size_bytes as usize)?, + } + } + IgvmVariableHeaderType::IGVM_VHT_CORIM_SIGNATURE + if length == size_of::() => + { + let IGVM_VHS_CORIM_SIGNATURE { + compatibility_mask, + reserved, + file_offset, + size_bytes, + } = read_header(&mut variable_headers)?; + + if reserved != 0 { + return Err(BinaryHeaderError::ReservedNotZero); + } + + IgvmDirectiveHeader::CorimSignature { + compatibility_mask, + signature: extract_file_data(file_offset, size_bytes as usize)?, + } + } _ => return Err(BinaryHeaderError::InvalidVariableHeaderType), }; @@ -2534,6 +2658,14 @@ impl IgvmFile { } let mut parameter_areas: BTreeMap = BTreeMap::new(); + // Track which compatibility masks have had a corim document header, + // only one allowed per compatibility mask. + let mut corim_document_seen: [bool; 32] = [false; 32]; + + // Track which compatibility masks have a corim document signature, only + // one allowed per compatibility mask. + let mut corim_document_signature_seen: [bool; 32] = [false; 32]; + // TODO: validate parameter usage offset falls within parameter area size for header in directive_headers { @@ -2648,6 +2780,52 @@ impl IgvmFile { IgvmDirectiveHeader::ErrorRange { .. } => {} // TODO: Validate ErrorRange IgvmDirectiveHeader::SnpIdBlock { .. } => {} // TODO: Validate Snp IgvmDirectiveHeader::VbsMeasurement { .. } => {} // TODO: Validate Vbs + IgvmDirectiveHeader::CorimDocument { + compatibility_mask, .. + } => { + // Validate that there is at most 1 corim document header + // for a given compatibility mask. + for single_mask in extract_individual_masks(*compatibility_mask) { + let mask_index = single_mask.trailing_zeros() as usize; + if corim_document_seen[mask_index] { + return Err(Error::InvalidBinaryDirectiveHeader( + BinaryHeaderError::MultipleCorimDocuments(single_mask), + )); + } + corim_document_seen[mask_index] = true; + } + + // TODO: validate actual corim document is what is expected + // for the given platform. Requires parsing the CBOR + // payload. + } + IgvmDirectiveHeader::CorimSignature { + compatibility_mask, .. + } => { + // Validate that there is at most 1 corim document signature + // for a given compatibility mask. + for single_mask in extract_individual_masks(*compatibility_mask) { + let mask_index = single_mask.trailing_zeros() as usize; + + if corim_document_signature_seen[mask_index] { + return Err(Error::InvalidBinaryDirectiveHeader( + BinaryHeaderError::MultipleCorimSignatures(single_mask), + )); + } + corim_document_signature_seen[mask_index] = true; + + // There must be a corim document for this compatibility + // mask, before this header. + if !corim_document_seen[mask_index] { + return Err(Error::InvalidBinaryDirectiveHeader( + BinaryHeaderError::MissingCorimDocument(single_mask), + )); + } + } + + // TODO: Validate signature is correct for the given corim + // document. + } } } @@ -3346,7 +3524,9 @@ impl IgvmFile { | SnpIdBlock { .. } | VbsMeasurement { .. } | X64VbsVpContext { .. } - | AArch64VbsVpContext { .. } => {} + | AArch64VbsVpContext { .. } + | CorimDocument { .. } + | CorimSignature { .. } => {} ParameterArea { parameter_area_index, .. @@ -4687,5 +4867,5 @@ mod tests { ) } - // Test serialize and deserialize + // Test corim header serialization and basic validation } diff --git a/igvm_defs/src/lib.rs b/igvm_defs/src/lib.rs index 215cb16..a24c739 100644 --- a/igvm_defs/src/lib.rs +++ b/igvm_defs/src/lib.rs @@ -1263,7 +1263,7 @@ pub enum VbsSigningAlgorithm { /// | ARM CCA | TBD | #[repr(C)] #[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)] -struct IGVM_VHS_CORIM_DOCUMENT { +pub struct IGVM_VHS_CORIM_DOCUMENT { /// Compatibility mask. pub compatibility_mask: u32, /// File offset for the CoRIM CBOR payload. @@ -1276,15 +1276,15 @@ struct IGVM_VHS_CORIM_DOCUMENT { /// This structure descibres a COSE_Sign1 structure for a detached CoRIM CBOR /// payload for a given platform. The payload measured by this CBOR is described -/// the corresponding [`IGVM_VHS_CORIM_DOCUMENT`] structure. There cannot be -/// this structure without that one. +/// the corresponding [`IGVM_VHS_CORIM_DOCUMENT`] structure, which must be +/// defined before this structure. /// /// For more information on the structure described by this header, see the /// COSE_Sign1 structure described in section 4.2 in RFC /// https://datatracker.ietf.org/doc/draft-ietf-rats-corim/. #[repr(C)] #[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)] -struct IGVM_VHS_CORIM_SIGNATURE { +pub struct IGVM_VHS_CORIM_SIGNATURE { /// Compatibility mask. pub compatibility_mask: u32, /// File offset for the COSE_Sign1 measurement payload. From 4a7b17b2ff9686b5bff79351d016e6cd2b9a871a Mon Sep 17 00:00:00 2001 From: Chris Oo Date: Fri, 13 Feb 2026 11:11:23 -0800 Subject: [PATCH 05/10] corim: add basic corim unittests --- igvm/src/lib.rs | 253 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 252 insertions(+), 1 deletion(-) diff --git a/igvm/src/lib.rs b/igvm/src/lib.rs index 7e72bf2..8a0feec 100644 --- a/igvm/src/lib.rs +++ b/igvm/src/lib.rs @@ -4867,5 +4867,256 @@ mod tests { ) } - // Test corim header serialization and basic validation + #[test] + fn test_corim_document() { + let file_data_offset = 0x5000; + let document: Vec = vec![0xA1, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]; + + let raw_header = IGVM_VHS_CORIM_DOCUMENT { + compatibility_mask: 0x1, + file_offset: file_data_offset, + size_bytes: document.len() as u32, + reserved: 0, + }; + + let header = IgvmDirectiveHeader::CorimDocument { + compatibility_mask: 0x1, + document: document.clone(), + }; + + test_variable_header( + IgvmRevision::V2 { + arch: Arch::X64, + page_size: PAGE_SIZE_4K as u32, + }, + header, + file_data_offset, + IgvmVariableHeaderType::IGVM_VHT_CORIM_DOCUMENT, + raw_header, + Some(document), + None, + ); + } + + #[test] + fn test_corim_signature() { + let file_data_offset = 0x6000; + let signature: Vec = vec![0xD2, 0x84, 0x43, 0xA1, 0x01, 0x26, 0xA0, 0x44]; + + let raw_header = IGVM_VHS_CORIM_SIGNATURE { + compatibility_mask: 0x1, + file_offset: file_data_offset, + size_bytes: signature.len() as u32, + reserved: 0, + }; + + let header = IgvmDirectiveHeader::CorimSignature { + compatibility_mask: 0x1, + signature: signature.clone(), + }; + + test_variable_header( + IgvmRevision::V2 { + arch: Arch::X64, + page_size: PAGE_SIZE_4K as u32, + }, + header, + file_data_offset, + IgvmVariableHeaderType::IGVM_VHT_CORIM_SIGNATURE, + raw_header, + Some(signature), + None, + ); + } + + mod corim { + use super::*; + + // TODO: when corim payload validation is added, these tests need to be + // updated to have real documents. + + fn validate(headers: &[IgvmDirectiveHeader]) -> Result<(), Error> { + IgvmFile::validate_directive_headers( + IgvmRevision::V2 { + arch: Arch::X64, + page_size: PAGE_SIZE_4K as u32, + }, + headers, + DirectiveHeaderValidationInfo { + used_vp_idents: Vec::new(), + page_table_regions: Vec::new(), + }, + ) + } + + #[test] + fn test_basic_roundtrip() { + let data1 = vec![1; PAGE_SIZE_4K as usize]; + let corim_doc = vec![0xA1, 0x02, 0x03, 0x04]; + let corim_sig = vec![0xD2, 0x84, 0x43, 0xA1]; + + let file = IgvmFile { + revision: IgvmRevision::V2 { + arch: Arch::X64, + page_size: PAGE_SIZE_4K as u32, + }, + platform_headers: vec![new_platform(0x1, IgvmPlatformType::VSM_ISOLATION)], + initialization_headers: vec![], + directive_headers: vec![ + new_page_data(0, 1, &data1), + IgvmDirectiveHeader::CorimDocument { + compatibility_mask: 0x1, + document: corim_doc.clone(), + }, + IgvmDirectiveHeader::CorimSignature { + compatibility_mask: 0x1, + signature: corim_sig.clone(), + }, + ], + }; + + let mut binary_file = Vec::new(); + file.serialize(&mut binary_file).unwrap(); + + let deserialized = IgvmFile::new_from_binary(&binary_file, None).unwrap(); + assert_igvm_equal(&file, &deserialized); + } + + #[test] + fn test_corim_document_and_signature_valid() { + let headers = vec![ + IgvmDirectiveHeader::CorimDocument { + compatibility_mask: 0x1, + document: vec![0x01, 0x02, 0x03], + }, + IgvmDirectiveHeader::CorimSignature { + compatibility_mask: 0x1, + signature: vec![0x04, 0x05, 0x06], + }, + ]; + assert!(validate(&headers).is_ok()); + } + + #[test] + fn test_corim_document_without_signature_valid() { + let headers = vec![IgvmDirectiveHeader::CorimDocument { + compatibility_mask: 0x1, + document: vec![0x01, 0x02, 0x03], + }]; + assert!(validate(&headers).is_ok()); + } + + #[test] + fn test_multiple_corim_documents_error() { + let headers = vec![ + IgvmDirectiveHeader::CorimDocument { + compatibility_mask: 0x1, + document: vec![0x01, 0x02], + }, + IgvmDirectiveHeader::CorimDocument { + compatibility_mask: 0x1, + document: vec![0x03, 0x04], + }, + ]; + assert!(matches!( + validate(&headers), + Err(Error::InvalidBinaryDirectiveHeader( + BinaryHeaderError::MultipleCorimDocuments(0x1) + )) + )); + } + + #[test] + fn test_multiple_corim_documents_different_masks_valid() { + let headers = vec![ + IgvmDirectiveHeader::CorimDocument { + compatibility_mask: 0x1, + document: vec![0x01, 0x02], + }, + IgvmDirectiveHeader::CorimDocument { + compatibility_mask: 0x2, + document: vec![0x03, 0x04], + }, + ]; + assert!(validate(&headers).is_ok()); + } + + #[test] + fn test_multiple_corim_signatures_error() { + let headers = vec![ + IgvmDirectiveHeader::CorimDocument { + compatibility_mask: 0x1, + document: vec![0x01, 0x02], + }, + IgvmDirectiveHeader::CorimSignature { + compatibility_mask: 0x1, + signature: vec![0x03, 0x04], + }, + IgvmDirectiveHeader::CorimSignature { + compatibility_mask: 0x1, + signature: vec![0x05, 0x06], + }, + ]; + assert!(matches!( + validate(&headers), + Err(Error::InvalidBinaryDirectiveHeader( + BinaryHeaderError::MultipleCorimSignatures(0x1) + )) + )); + } + + #[test] + fn test_corim_signature_without_document_error() { + let headers = vec![IgvmDirectiveHeader::CorimSignature { + compatibility_mask: 0x1, + signature: vec![0x01, 0x02], + }]; + assert!(matches!( + validate(&headers), + Err(Error::InvalidBinaryDirectiveHeader( + BinaryHeaderError::MissingCorimDocument(0x1) + )) + )); + } + + #[test] + fn test_corim_signature_wrong_mask_missing_document() { + let headers = vec![ + IgvmDirectiveHeader::CorimDocument { + compatibility_mask: 0x1, + document: vec![0x01, 0x02], + }, + IgvmDirectiveHeader::CorimSignature { + compatibility_mask: 0x2, + signature: vec![0x03, 0x04], + }, + ]; + assert!(matches!( + validate(&headers), + Err(Error::InvalidBinaryDirectiveHeader( + BinaryHeaderError::MissingCorimDocument(0x2) + )) + )); + } + + #[test] + fn test_corim_combined_mask_duplicate_document() { + let headers = vec![ + IgvmDirectiveHeader::CorimDocument { + compatibility_mask: 0x3, + document: vec![0x01, 0x02], + }, + IgvmDirectiveHeader::CorimDocument { + compatibility_mask: 0x1, + document: vec![0x03, 0x04], + }, + ]; + assert!(matches!( + validate(&headers), + Err(Error::InvalidBinaryDirectiveHeader( + BinaryHeaderError::MultipleCorimDocuments(0x1) + )) + )); + } + } } From 4f58ded18864e7ae7cef0324eb77e36d7ae07030 Mon Sep 17 00:00:00 2001 From: Chris Oo Date: Wed, 18 Mar 2026 15:16:33 -0700 Subject: [PATCH 06/10] feedback: move header to init header, fix docs --- igvm_defs/src/lib.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/igvm_defs/src/lib.rs b/igvm_defs/src/lib.rs index a24c739..6a1a253 100644 --- a/igvm_defs/src/lib.rs +++ b/igvm_defs/src/lib.rs @@ -248,6 +248,12 @@ pub enum IgvmVariableHeaderType { /// A page table relocation region described by /// [`IGVM_VHS_PAGE_TABLE_RELOCATION`]. IGVM_VHT_PAGE_TABLE_RELOCATION_REGION = 0x103, + /// A Corim document structure described by [`IGVM_VHS_CORIM_DOCUMENT`]. + #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] + IGVM_VHT_CORIM_DOCUMENT = 0x104, + /// A Corim signature structure described by [`IGVM_VHS_CORIM_SIGNATURE`]. + #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] + IGVM_VHT_CORIM_SIGNATURE = 0x105, // These are IGVM_VHT_RANGE_DIRECTIVE structures. /// A parameter area structure described by [`IGVM_VHS_PARAMETER_AREA`]. @@ -341,14 +347,6 @@ pub enum IgvmVariableHeaderType { /// specified by a structure of type [`IGVM_VHS_PARAMETER`]. #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] IGVM_VHT_ENVIRONMENT_INFO_PARAMETER = 0x313, - /// A Corim document structure described by [`IGVM_VHS_CORIM_DOCUMENT`]. - /// FIXME: should this be an init header to be early in the file? - #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] - IGVM_VHT_CORIM_DOCUMENT = 0x314, - /// A Corim signature structure described by [`IGVM_VHS_CORIM_SIGNATURE`]. - /// FIXME: should this be an init header to be early in the file? - #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] - IGVM_VHT_CORIM_SIGNATURE = 0x315, } /// The range of header types for platform structures. @@ -1252,7 +1250,7 @@ pub enum VbsSigningAlgorithm { /// be one for a given platform. There may be an associated COSE_Sign1 structure /// for this document, see [`IGVM_VHS_CORIM_SIGNATURE`]. /// -/// The CoRIM payload must adhere to the following specifications for each +/// The CoRIM document must adhere to the following specifications for each /// platform: /// /// | Platform | Specification | From ef31f47642bf3ee4cbcdc31623452cc40cd7f219 Mon Sep 17 00:00:00 2001 From: Chris Oo Date: Mon, 23 Mar 2026 15:51:35 -0700 Subject: [PATCH 07/10] feedback: fix igvm code after moving to init headers --- igvm/src/c_api.rs | 2 +- igvm/src/lib.rs | 520 +++++++++++++++++++++++++--------------------- 2 files changed, 287 insertions(+), 235 deletions(-) diff --git a/igvm/src/c_api.rs b/igvm/src/c_api.rs index ec80682..aa2d92d 100644 --- a/igvm/src/c_api.rs +++ b/igvm/src/c_api.rs @@ -290,7 +290,7 @@ fn get_header( .initialization_headers .get(index as usize) .ok_or(IgvmResult::IGVMAPI_INVALID_PARAMETER)? - .write_binary_header(&mut header_binary) + .write_binary_header(&mut header_binary, &mut FileDataSerializer::new(0)) .map_err(|_| IgvmResult::IGVMAPI_INVALID_FILE)?; } IgvmHeaderSection::HEADER_SECTION_DIRECTIVE => { diff --git a/igvm/src/lib.rs b/igvm/src/lib.rs index 8a0feec..0100a04 100644 --- a/igvm/src/lib.rs +++ b/igvm/src/lib.rs @@ -344,6 +344,17 @@ pub enum IgvmInitializationHeader { vp_index: u16, vtl: Vtl, }, + CorimDocument { + compatibility_mask: u32, + // FUTURE: have the corim document in a typed structure, with the + // required per-plaform fields. + document: Vec, + }, + CorimSignature { + compatibility_mask: u32, + // FUTURE: have the corim signature in a typed structure + signature: Vec, + }, } impl IgvmInitializationHeader { @@ -357,9 +368,13 @@ impl IgvmInitializationHeader { IgvmInitializationHeader::PageTableRelocationRegion { .. } => { size_of::() } + IgvmInitializationHeader::CorimDocument { .. } => size_of::(), + IgvmInitializationHeader::CorimSignature { .. } => { + size_of::() + } }; - size_of::() + additional + align_8(size_of::() + additional) } /// Get the [`IgvmVariableHeaderType`] for the initialization header. @@ -376,6 +391,12 @@ impl IgvmInitializationHeader { IgvmInitializationHeader::PageTableRelocationRegion { .. } => { IgvmVariableHeaderType::IGVM_VHT_PAGE_TABLE_RELOCATION_REGION } + IgvmInitializationHeader::CorimDocument { .. } => { + IgvmVariableHeaderType::IGVM_VHT_CORIM_DOCUMENT + } + IgvmInitializationHeader::CorimSignature { .. } => { + IgvmVariableHeaderType::IGVM_VHT_CORIM_SIGNATURE + } } } @@ -454,14 +475,23 @@ impl IgvmInitializationHeader { Ok(()) } + // TODO: validate CoRIM document has the minimum fields required + // described by the corresponding specification for that platform. + IgvmInitializationHeader::CorimDocument { .. } => Ok(()), + // TODO: validate CoRIM signature has the right fields, and + // correctly signs the corresponding document. This requires crypto + // crates and might need to be gated behind a feature flag. + IgvmInitializationHeader::CorimSignature { .. } => Ok(()), } } /// Create a new [`IgvmInitializationHeader`] from the binary slice provided. /// Returns the remaining slice of unused bytes. - fn new_from_binary_split( - mut variable_headers: &[u8], - ) -> Result<(Self, &[u8]), BinaryHeaderError> { + fn new_from_binary_split<'a>( + mut variable_headers: &'a [u8], + file_data: &'a [u8], + file_data_start: u32, + ) -> Result<(Self, &'a [u8]), BinaryHeaderError> { let IGVM_VHS_VARIABLE_HEADER { typ, length } = read_header::(&mut variable_headers)?; @@ -469,6 +499,23 @@ impl IgvmInitializationHeader { let length = length as usize; + // Extract file data from a given file offset with the given size. File + // offset of 0 results in no data. + let extract_file_data = + |file_offset: u32, size: usize| -> Result, BinaryHeaderError> { + if file_offset == 0 { + return Ok(Vec::new()); + } + + let start = (file_offset - file_data_start) as usize; + let end = start + size; + + file_data + .get(start..end) + .ok_or(BinaryHeaderError::InvalidDataSize) + .map(|slice| slice.to_vec()) + }; + let header = match typ { IgvmVariableHeaderType::IGVM_VHT_GUEST_POLICY if length == size_of::() => @@ -550,6 +597,44 @@ impl IgvmInitializationHeader { vtl: vtl.try_into().map_err(|_| BinaryHeaderError::InvalidVtl)?, } } + IgvmVariableHeaderType::IGVM_VHT_CORIM_DOCUMENT + if length == size_of::() => + { + let IGVM_VHS_CORIM_DOCUMENT { + compatibility_mask, + reserved, + file_offset, + size_bytes, + } = read_header(&mut variable_headers)?; + + if reserved != 0 { + return Err(BinaryHeaderError::ReservedNotZero); + } + + IgvmInitializationHeader::CorimDocument { + compatibility_mask, + document: extract_file_data(file_offset, size_bytes as usize)?, + } + } + IgvmVariableHeaderType::IGVM_VHT_CORIM_SIGNATURE + if length == size_of::() => + { + let IGVM_VHS_CORIM_SIGNATURE { + compatibility_mask, + reserved, + file_offset, + size_bytes, + } = read_header(&mut variable_headers)?; + + if reserved != 0 { + return Err(BinaryHeaderError::ReservedNotZero); + } + + IgvmInitializationHeader::CorimSignature { + compatibility_mask, + signature: extract_file_data(file_offset, size_bytes as usize)?, + } + } _ => return Err(BinaryHeaderError::InvalidVariableHeaderType), }; @@ -572,10 +657,20 @@ impl IgvmInitializationHeader { PageTableRelocationRegion { compatibility_mask, .. } => Some(*compatibility_mask), + CorimDocument { + compatibility_mask, .. + } => Some(*compatibility_mask), + CorimSignature { + compatibility_mask, .. + } => Some(*compatibility_mask), } } - fn write_binary_header(&self, variable_headers: &mut Vec) -> Result<(), BinaryHeaderError> { + fn write_binary_header( + &self, + variable_headers: &mut Vec, + file_data: &mut FileDataSerializer, + ) -> Result<(), BinaryHeaderError> { // Only serialize this header if valid. self.validate()?; @@ -665,6 +760,48 @@ impl IgvmInitializationHeader { variable_headers, ); } + IgvmInitializationHeader::CorimDocument { + compatibility_mask, + document, + } => { + let file_offset = file_data.write_file_data(document); + + let corim_document = IGVM_VHS_CORIM_DOCUMENT { + compatibility_mask: *compatibility_mask, + reserved: 0, + file_offset, + size_bytes: document + .len() + .try_into() + .expect("corim document size must fit in u32"), + }; + append_header( + &corim_document, + IgvmVariableHeaderType::IGVM_VHT_CORIM_DOCUMENT, + variable_headers, + ); + } + IgvmInitializationHeader::CorimSignature { + compatibility_mask, + signature, + } => { + let file_offset = file_data.write_file_data(signature); + + let corim_signature = IGVM_VHS_CORIM_SIGNATURE { + compatibility_mask: *compatibility_mask, + reserved: 0, + file_offset, + size_bytes: signature + .len() + .try_into() + .expect("corim signature size must fit in u32"), + }; + append_header( + &corim_signature, + IgvmVariableHeaderType::IGVM_VHT_CORIM_SIGNATURE, + variable_headers, + ); + } } Ok(()) @@ -760,17 +897,6 @@ pub enum IgvmDirectiveHeader { signature: Box<[u8; 256]>, public_key: Box<[u8; 512]>, }, - CorimDocument { - compatibility_mask: u32, - // FUTURE: have the corim document in a typed structure, with the - // required per-plaform fields. - document: Vec, - }, - CorimSignature { - compatibility_mask: u32, - // FUTURE: have the corim signature in a typed structure - signature: Vec, - }, } impl fmt::Display for IgvmDirectiveHeader { @@ -991,8 +1117,6 @@ impl IgvmDirectiveHeader { IgvmDirectiveHeader::ErrorRange { .. } => size_of::(), IgvmDirectiveHeader::SnpIdBlock { .. } => size_of::(), IgvmDirectiveHeader::VbsMeasurement { .. } => size_of::(), - IgvmDirectiveHeader::CorimDocument { .. } => size_of::(), - IgvmDirectiveHeader::CorimSignature { .. } => size_of::(), }; align_8(size_of::() + additional) @@ -1040,12 +1164,6 @@ impl IgvmDirectiveHeader { IgvmDirectiveHeader::EnvironmentInfo(_) => { IgvmVariableHeaderType::IGVM_VHT_ENVIRONMENT_INFO_PARAMETER } - IgvmDirectiveHeader::CorimDocument { .. } => { - IgvmVariableHeaderType::IGVM_VHT_CORIM_DOCUMENT - } - IgvmDirectiveHeader::CorimSignature { .. } => { - IgvmVariableHeaderType::IGVM_VHT_CORIM_SIGNATURE - } } } @@ -1446,48 +1564,6 @@ impl IgvmDirectiveHeader { variable_headers, ) } - IgvmDirectiveHeader::CorimDocument { - compatibility_mask, - document, - } => { - let file_offset = file_data.write_file_data(document); - - let corim_document = IGVM_VHS_CORIM_DOCUMENT { - compatibility_mask: *compatibility_mask, - reserved: 0, - file_offset, - size_bytes: document - .len() - .try_into() - .expect("corim document size must fit in u32"), - }; - append_header( - &corim_document, - IgvmVariableHeaderType::IGVM_VHT_CORIM_DOCUMENT, - variable_headers, - ); - } - IgvmDirectiveHeader::CorimSignature { - compatibility_mask, - signature, - } => { - let file_offset = file_data.write_file_data(signature); - - let corim_signature = IGVM_VHS_CORIM_SIGNATURE { - compatibility_mask: *compatibility_mask, - reserved: 0, - file_offset, - size_bytes: signature - .len() - .try_into() - .expect("corim signature size must fit in u32"), - }; - append_header( - &corim_signature, - IgvmVariableHeaderType::IGVM_VHT_CORIM_SIGNATURE, - variable_headers, - ); - } } Ok(()) @@ -1537,12 +1613,6 @@ impl IgvmDirectiveHeader { VbsMeasurement { compatibility_mask, .. } => Some(*compatibility_mask), - CorimDocument { - compatibility_mask, .. - } => Some(*compatibility_mask), - CorimSignature { - compatibility_mask, .. - } => Some(*compatibility_mask), } } @@ -1591,12 +1661,6 @@ impl IgvmDirectiveHeader { VbsMeasurement { compatibility_mask, .. } => Some(compatibility_mask), - CorimDocument { - compatibility_mask, .. - } => Some(compatibility_mask), - CorimSignature { - compatibility_mask, .. - } => Some(compatibility_mask), } } @@ -1758,13 +1822,6 @@ impl IgvmDirectiveHeader { IgvmDirectiveHeader::SnpIdBlock { .. } => {} // TODO: validate VBS IgvmDirectiveHeader::VbsMeasurement { .. } => {} - // TODO: validate CoRIM document has the minimum fields required - // described by the corresponding specification for that platform. - IgvmDirectiveHeader::CorimDocument { .. } => {} - // TODO: validate CoRIM signature has the right fields, and - // correctly signs the corresponding document. This requires crypto - // crates and might need to be gated behind a feature flag. - IgvmDirectiveHeader::CorimSignature { .. } => {} } Ok(()) @@ -2139,44 +2196,6 @@ impl IgvmDirectiveHeader { { IgvmDirectiveHeader::DeviceTree(read_header(&mut variable_headers)?) } - IgvmVariableHeaderType::IGVM_VHT_CORIM_DOCUMENT - if length == size_of::() => - { - let IGVM_VHS_CORIM_DOCUMENT { - compatibility_mask, - reserved, - file_offset, - size_bytes, - } = read_header(&mut variable_headers)?; - - if reserved != 0 { - return Err(BinaryHeaderError::ReservedNotZero); - } - - IgvmDirectiveHeader::CorimDocument { - compatibility_mask, - document: extract_file_data(file_offset, size_bytes as usize)?, - } - } - IgvmVariableHeaderType::IGVM_VHT_CORIM_SIGNATURE - if length == size_of::() => - { - let IGVM_VHS_CORIM_SIGNATURE { - compatibility_mask, - reserved, - file_offset, - size_bytes, - } = read_header(&mut variable_headers)?; - - if reserved != 0 { - return Err(BinaryHeaderError::ReservedNotZero); - } - - IgvmDirectiveHeader::CorimSignature { - compatibility_mask, - signature: extract_file_data(file_offset, size_bytes as usize)?, - } - } _ => return Err(BinaryHeaderError::InvalidVariableHeaderType), }; @@ -2547,6 +2566,14 @@ impl IgvmFile { Ok(()) }; + // Track which compatibility masks have had a corim document header, + // only one allowed per compatibility mask. + let mut corim_document_seen: [bool; 32] = [false; 32]; + + // Track which compatibility masks have a corim document signature, only + // one allowed per compatibility mask. + let mut corim_document_signature_seen: [bool; 32] = [false; 32]; + for header in initialization_headers { // Each individual header needs to be valid. header @@ -2628,7 +2655,46 @@ impl IgvmFile { }) } // TODO: validate SNP policy compatibility mask specifies SNP - _ => {} + IgvmInitializationHeader::GuestPolicy { .. } => {} + IgvmInitializationHeader::CorimDocument { + compatibility_mask, .. + } => { + // Validate that there is at most 1 corim document header + // for a given compatibility mask. + for single_mask in extract_individual_masks(*compatibility_mask) { + let mask_index = single_mask.trailing_zeros() as usize; + if corim_document_seen[mask_index] { + return Err(Error::InvalidBinaryInitializationHeader( + BinaryHeaderError::MultipleCorimDocuments(single_mask), + )); + } + corim_document_seen[mask_index] = true; + } + } + IgvmInitializationHeader::CorimSignature { + compatibility_mask, .. + } => { + // Validate that there is at most 1 corim document signature + // for a given compatibility mask. + for single_mask in extract_individual_masks(*compatibility_mask) { + let mask_index = single_mask.trailing_zeros() as usize; + + if corim_document_signature_seen[mask_index] { + return Err(Error::InvalidBinaryInitializationHeader( + BinaryHeaderError::MultipleCorimSignatures(single_mask), + )); + } + corim_document_signature_seen[mask_index] = true; + + // There must be a corim document for this compatibility + // mask, before this header. + if !corim_document_seen[mask_index] { + return Err(Error::InvalidBinaryInitializationHeader( + BinaryHeaderError::MissingCorimDocument(single_mask), + )); + } + } + } } } @@ -2658,14 +2724,6 @@ impl IgvmFile { } let mut parameter_areas: BTreeMap = BTreeMap::new(); - // Track which compatibility masks have had a corim document header, - // only one allowed per compatibility mask. - let mut corim_document_seen: [bool; 32] = [false; 32]; - - // Track which compatibility masks have a corim document signature, only - // one allowed per compatibility mask. - let mut corim_document_signature_seen: [bool; 32] = [false; 32]; - // TODO: validate parameter usage offset falls within parameter area size for header in directive_headers { @@ -2780,52 +2838,6 @@ impl IgvmFile { IgvmDirectiveHeader::ErrorRange { .. } => {} // TODO: Validate ErrorRange IgvmDirectiveHeader::SnpIdBlock { .. } => {} // TODO: Validate Snp IgvmDirectiveHeader::VbsMeasurement { .. } => {} // TODO: Validate Vbs - IgvmDirectiveHeader::CorimDocument { - compatibility_mask, .. - } => { - // Validate that there is at most 1 corim document header - // for a given compatibility mask. - for single_mask in extract_individual_masks(*compatibility_mask) { - let mask_index = single_mask.trailing_zeros() as usize; - if corim_document_seen[mask_index] { - return Err(Error::InvalidBinaryDirectiveHeader( - BinaryHeaderError::MultipleCorimDocuments(single_mask), - )); - } - corim_document_seen[mask_index] = true; - } - - // TODO: validate actual corim document is what is expected - // for the given platform. Requires parsing the CBOR - // payload. - } - IgvmDirectiveHeader::CorimSignature { - compatibility_mask, .. - } => { - // Validate that there is at most 1 corim document signature - // for a given compatibility mask. - for single_mask in extract_individual_masks(*compatibility_mask) { - let mask_index = single_mask.trailing_zeros() as usize; - - if corim_document_signature_seen[mask_index] { - return Err(Error::InvalidBinaryDirectiveHeader( - BinaryHeaderError::MultipleCorimSignatures(single_mask), - )); - } - corim_document_signature_seen[mask_index] = true; - - // There must be a corim document for this compatibility - // mask, before this header. - if !corim_document_seen[mask_index] { - return Err(Error::InvalidBinaryDirectiveHeader( - BinaryHeaderError::MissingCorimDocument(single_mask), - )); - } - } - - // TODO: Validate signature is correct for the given corim - // document. - } } } @@ -2877,17 +2889,17 @@ impl IgvmFile { } } + // dedup file data + let mut file_data = FileDataSerializer::new(file_data_section_start); + // Add initialization headers for header in &self.initialization_headers { header - .write_binary_header(&mut variable_header_binary) - .map_err(Error::InvalidBinaryDirectiveHeader)?; + .write_binary_header(&mut variable_header_binary, &mut file_data) + .map_err(Error::InvalidBinaryInitializationHeader)?; assert_eq!(variable_header_binary.len() % 8, 0); } - // dedup file data - let mut file_data = FileDataSerializer::new(file_data_section_start); - // Add directive headers for header in &self.directive_headers { header @@ -3123,9 +3135,12 @@ impl IgvmFile { } } - let (header, new_slice) = - IgvmInitializationHeader::new_from_binary_split(variable_headers) - .map_err(Error::InvalidBinaryInitializationHeader)?; + let (header, new_slice) = IgvmInitializationHeader::new_from_binary_split( + variable_headers, + file_data, + file_data_start, + ) + .map_err(Error::InvalidBinaryInitializationHeader)?; variable_headers = new_slice; @@ -3503,6 +3518,12 @@ impl IgvmFile { IgvmInitializationHeader::PageTableRelocationRegion { compatibility_mask, .. } => fixup_mask(compatibility_mask), + IgvmInitializationHeader::CorimDocument { + compatibility_mask, .. + } => fixup_mask(compatibility_mask), + IgvmInitializationHeader::CorimSignature { + compatibility_mask, .. + } => fixup_mask(compatibility_mask), } } @@ -3524,9 +3545,7 @@ impl IgvmFile { | SnpIdBlock { .. } | VbsMeasurement { .. } | X64VbsVpContext { .. } - | AArch64VbsVpContext { .. } - | CorimDocument { .. } - | CorimSignature { .. } => {} + | AArch64VbsVpContext { .. } => {} ParameterArea { parameter_area_index, .. @@ -4285,6 +4304,53 @@ mod tests { } } + /// Test an initialization variable header matches the supplied args. Also + /// tests round-trip serialization/deserialization. + fn test_init_variable_header( + header: IgvmInitializationHeader, + file_data_offset: u32, + header_type: IgvmVariableHeaderType, + expected_variable_binary_header: T, + expected_file_data: Option>, + ) { + let mut binary_header = Vec::new(); + let mut file_data = FileDataSerializer::new(file_data_offset as usize); + + header + .write_binary_header(&mut binary_header, &mut file_data) + .unwrap(); + + let file_data = file_data.take(); + + let common_header = IGVM_VHS_VARIABLE_HEADER::read_from_prefix(&binary_header[..]) + .expect("variable header must be present") + .0; + + assert_eq!(common_header.typ, header_type); + assert_eq!( + align_8(common_header.length as usize), + size_of_val(&expected_variable_binary_header) + ); + assert_eq!( + &binary_header[size_of_val(&common_header)..], + expected_variable_binary_header.as_bytes() + ); + + match &expected_file_data { + Some(data) => assert_eq!(data, &file_data), + None => assert!(file_data.is_empty()), + } + + let (reserialized_header, remaining) = IgvmInitializationHeader::new_from_binary_split( + &binary_header, + &file_data, + file_data_offset, + ) + .unwrap(); + assert!(remaining.is_empty()); + assert_eq!(header, reserialized_header); + } + // Test get binary header for each type. #[test] fn test_page_data() { @@ -4879,22 +4945,17 @@ mod tests { reserved: 0, }; - let header = IgvmDirectiveHeader::CorimDocument { + let header = IgvmInitializationHeader::CorimDocument { compatibility_mask: 0x1, document: document.clone(), }; - test_variable_header( - IgvmRevision::V2 { - arch: Arch::X64, - page_size: PAGE_SIZE_4K as u32, - }, + test_init_variable_header( header, file_data_offset, IgvmVariableHeaderType::IGVM_VHT_CORIM_DOCUMENT, raw_header, Some(document), - None, ); } @@ -4910,22 +4971,17 @@ mod tests { reserved: 0, }; - let header = IgvmDirectiveHeader::CorimSignature { + let header = IgvmInitializationHeader::CorimSignature { compatibility_mask: 0x1, signature: signature.clone(), }; - test_variable_header( - IgvmRevision::V2 { - arch: Arch::X64, - page_size: PAGE_SIZE_4K as u32, - }, + test_init_variable_header( header, file_data_offset, IgvmVariableHeaderType::IGVM_VHT_CORIM_SIGNATURE, raw_header, Some(signature), - None, ); } @@ -4935,18 +4991,15 @@ mod tests { // TODO: when corim payload validation is added, these tests need to be // updated to have real documents. - fn validate(headers: &[IgvmDirectiveHeader]) -> Result<(), Error> { - IgvmFile::validate_directive_headers( + fn validate(headers: &[IgvmInitializationHeader]) -> Result<(), Error> { + IgvmFile::validate_initialization_headers( IgvmRevision::V2 { arch: Arch::X64, page_size: PAGE_SIZE_4K as u32, }, headers, - DirectiveHeaderValidationInfo { - used_vp_idents: Vec::new(), - page_table_regions: Vec::new(), - }, ) + .map(|_| ()) } #[test] @@ -4961,18 +5014,17 @@ mod tests { page_size: PAGE_SIZE_4K as u32, }, platform_headers: vec![new_platform(0x1, IgvmPlatformType::VSM_ISOLATION)], - initialization_headers: vec![], - directive_headers: vec![ - new_page_data(0, 1, &data1), - IgvmDirectiveHeader::CorimDocument { + initialization_headers: vec![ + IgvmInitializationHeader::CorimDocument { compatibility_mask: 0x1, document: corim_doc.clone(), }, - IgvmDirectiveHeader::CorimSignature { + IgvmInitializationHeader::CorimSignature { compatibility_mask: 0x1, signature: corim_sig.clone(), }, ], + directive_headers: vec![new_page_data(0, 1, &data1)], }; let mut binary_file = Vec::new(); @@ -4985,11 +5037,11 @@ mod tests { #[test] fn test_corim_document_and_signature_valid() { let headers = vec![ - IgvmDirectiveHeader::CorimDocument { + IgvmInitializationHeader::CorimDocument { compatibility_mask: 0x1, document: vec![0x01, 0x02, 0x03], }, - IgvmDirectiveHeader::CorimSignature { + IgvmInitializationHeader::CorimSignature { compatibility_mask: 0x1, signature: vec![0x04, 0x05, 0x06], }, @@ -4999,7 +5051,7 @@ mod tests { #[test] fn test_corim_document_without_signature_valid() { - let headers = vec![IgvmDirectiveHeader::CorimDocument { + let headers = vec![IgvmInitializationHeader::CorimDocument { compatibility_mask: 0x1, document: vec![0x01, 0x02, 0x03], }]; @@ -5009,18 +5061,18 @@ mod tests { #[test] fn test_multiple_corim_documents_error() { let headers = vec![ - IgvmDirectiveHeader::CorimDocument { + IgvmInitializationHeader::CorimDocument { compatibility_mask: 0x1, document: vec![0x01, 0x02], }, - IgvmDirectiveHeader::CorimDocument { + IgvmInitializationHeader::CorimDocument { compatibility_mask: 0x1, document: vec![0x03, 0x04], }, ]; assert!(matches!( validate(&headers), - Err(Error::InvalidBinaryDirectiveHeader( + Err(Error::InvalidBinaryInitializationHeader( BinaryHeaderError::MultipleCorimDocuments(0x1) )) )); @@ -5029,11 +5081,11 @@ mod tests { #[test] fn test_multiple_corim_documents_different_masks_valid() { let headers = vec![ - IgvmDirectiveHeader::CorimDocument { + IgvmInitializationHeader::CorimDocument { compatibility_mask: 0x1, document: vec![0x01, 0x02], }, - IgvmDirectiveHeader::CorimDocument { + IgvmInitializationHeader::CorimDocument { compatibility_mask: 0x2, document: vec![0x03, 0x04], }, @@ -5044,22 +5096,22 @@ mod tests { #[test] fn test_multiple_corim_signatures_error() { let headers = vec![ - IgvmDirectiveHeader::CorimDocument { + IgvmInitializationHeader::CorimDocument { compatibility_mask: 0x1, document: vec![0x01, 0x02], }, - IgvmDirectiveHeader::CorimSignature { + IgvmInitializationHeader::CorimSignature { compatibility_mask: 0x1, signature: vec![0x03, 0x04], }, - IgvmDirectiveHeader::CorimSignature { + IgvmInitializationHeader::CorimSignature { compatibility_mask: 0x1, signature: vec![0x05, 0x06], }, ]; assert!(matches!( validate(&headers), - Err(Error::InvalidBinaryDirectiveHeader( + Err(Error::InvalidBinaryInitializationHeader( BinaryHeaderError::MultipleCorimSignatures(0x1) )) )); @@ -5067,13 +5119,13 @@ mod tests { #[test] fn test_corim_signature_without_document_error() { - let headers = vec![IgvmDirectiveHeader::CorimSignature { + let headers = vec![IgvmInitializationHeader::CorimSignature { compatibility_mask: 0x1, signature: vec![0x01, 0x02], }]; assert!(matches!( validate(&headers), - Err(Error::InvalidBinaryDirectiveHeader( + Err(Error::InvalidBinaryInitializationHeader( BinaryHeaderError::MissingCorimDocument(0x1) )) )); @@ -5082,18 +5134,18 @@ mod tests { #[test] fn test_corim_signature_wrong_mask_missing_document() { let headers = vec![ - IgvmDirectiveHeader::CorimDocument { + IgvmInitializationHeader::CorimDocument { compatibility_mask: 0x1, document: vec![0x01, 0x02], }, - IgvmDirectiveHeader::CorimSignature { + IgvmInitializationHeader::CorimSignature { compatibility_mask: 0x2, signature: vec![0x03, 0x04], }, ]; assert!(matches!( validate(&headers), - Err(Error::InvalidBinaryDirectiveHeader( + Err(Error::InvalidBinaryInitializationHeader( BinaryHeaderError::MissingCorimDocument(0x2) )) )); @@ -5102,18 +5154,18 @@ mod tests { #[test] fn test_corim_combined_mask_duplicate_document() { let headers = vec![ - IgvmDirectiveHeader::CorimDocument { + IgvmInitializationHeader::CorimDocument { compatibility_mask: 0x3, document: vec![0x01, 0x02], }, - IgvmDirectiveHeader::CorimDocument { + IgvmInitializationHeader::CorimDocument { compatibility_mask: 0x1, document: vec![0x03, 0x04], }, ]; assert!(matches!( validate(&headers), - Err(Error::InvalidBinaryDirectiveHeader( + Err(Error::InvalidBinaryInitializationHeader( BinaryHeaderError::MultipleCorimDocuments(0x1) )) )); From f90720431f9e24dee6d0016109c855a69b99c4bc Mon Sep 17 00:00:00 2001 From: Chris Oo Date: Mon, 23 Mar 2026 17:03:59 -0700 Subject: [PATCH 08/10] c_api: fix get_header_data to correctly return header data for new init corim headers --- igvm/src/c_api.rs | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/igvm/src/c_api.rs b/igvm/src/c_api.rs index aa2d92d..afdcdb4 100644 --- a/igvm/src/c_api.rs +++ b/igvm/src/c_api.rs @@ -322,17 +322,30 @@ fn get_header_data( let igvm = handle_lock.get_mut()?; let mut header_data = FileDataSerializer::new(0); - if section == IgvmHeaderSection::HEADER_SECTION_DIRECTIVE { - let header = igvm - .file - .directive_headers - .get(index as usize) - .ok_or(IgvmResult::IGVMAPI_INVALID_PARAMETER)?; - header - .write_binary_header(&mut Vec::::new(), &mut header_data) - .map_err(|_| IgvmResult::IGVMAPI_INVALID_FILE)?; - } else { - return Err(IgvmResult::IGVMAPI_INVALID_PARAMETER); + match section { + IgvmHeaderSection::HEADER_SECTION_INITIALIZATION => { + let header = igvm + .file + .initialization_headers + .get(index as usize) + .ok_or(IgvmResult::IGVMAPI_INVALID_PARAMETER)?; + header + .write_binary_header(&mut Vec::::new(), &mut header_data) + .map_err(|_| IgvmResult::IGVMAPI_INVALID_FILE)?; + } + IgvmHeaderSection::HEADER_SECTION_DIRECTIVE => { + let header = igvm + .file + .directive_headers + .get(index as usize) + .ok_or(IgvmResult::IGVMAPI_INVALID_PARAMETER)?; + header + .write_binary_header(&mut Vec::::new(), &mut header_data) + .map_err(|_| IgvmResult::IGVMAPI_INVALID_FILE)?; + } + _ => { + return Err(IgvmResult::IGVMAPI_INVALID_PARAMETER); + } } let header_data = header_data.take(); From f0a2058fdce159a6f97bf148915fb8366770171f Mon Sep 17 00:00:00 2001 From: Chris Oo Date: Mon, 23 Mar 2026 17:07:38 -0700 Subject: [PATCH 09/10] feedback: fix igvm_defs markdown table --- igvm_defs/src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/igvm_defs/src/lib.rs b/igvm_defs/src/lib.rs index 6a1a253..7907207 100644 --- a/igvm_defs/src/lib.rs +++ b/igvm_defs/src/lib.rs @@ -1253,12 +1253,12 @@ pub enum VbsSigningAlgorithm { /// The CoRIM document must adhere to the following specifications for each /// platform: /// -/// | Platform | Specification | -/// |----------|---------------| -/// | Intel TDX | TBD | -/// | VBS | TBD | -/// | AMD SEV-SNP | TBD | -/// | ARM CCA | TBD | +/// | Platform | Specification | +/// |---------------|---------------| +/// | Intel TDX | TBD | +/// | VBS | TBD | +/// | AMD SEV-SNP | TBD | +/// | ARM CCA | TBD | #[repr(C)] #[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)] pub struct IGVM_VHS_CORIM_DOCUMENT { From 07fa6dfe4938a005d5525776623bb7c3a4ad8239 Mon Sep 17 00:00:00 2001 From: Chris Oo Date: Mon, 23 Mar 2026 17:22:16 -0700 Subject: [PATCH 10/10] feedback: delete extra space --- igvm_defs/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/igvm_defs/src/lib.rs b/igvm_defs/src/lib.rs index 7907207..fd7291b 100644 --- a/igvm_defs/src/lib.rs +++ b/igvm_defs/src/lib.rs @@ -1272,7 +1272,7 @@ pub struct IGVM_VHS_CORIM_DOCUMENT { pub reserved: u32, } -/// This structure descibres a COSE_Sign1 structure for a detached CoRIM CBOR +/// This structure descibres a COSE_Sign1 structure for a detached CoRIM CBOR /// payload for a given platform. The payload measured by this CBOR is described /// the corresponding [`IGVM_VHS_CORIM_DOCUMENT`] structure, which must be /// defined before this structure.