diff --git a/src/dir/gen2.rs b/src/dir/gen2.rs index 5522a21..e1e81b7 100644 --- a/src/dir/gen2.rs +++ b/src/dir/gen2.rs @@ -8,7 +8,7 @@ use zerocopy::{FromBytes, Ref}; use zerocopy_derive::{FromBytes, Immutable, IntoBytes}; use crate::Removables; -use crate::dir::man::{self, Manifest}; +use crate::dir::man::Manifest; // These must never be removed. They are essential for platform initialization. pub const ALWAYS_RETAIN: &[&str] = &[ @@ -197,7 +197,7 @@ pub struct Header { #[derive(Serialize, Deserialize, Clone, Debug)] #[repr(C)] pub struct Directory { - pub manifest: (Manifest, Vec), + pub manifest: Manifest, pub header: Header, pub modules: Vec, pub offset: usize, @@ -210,7 +210,7 @@ impl Display for Directory { let n = &self.name; let o = self.offset; let s = self.size; - let (m, _) = self.manifest; + let m = &self.manifest; write!(f, "{n} @ {o:08x}, {s} bytes, {m}") } } @@ -222,11 +222,7 @@ impl Directory { let Ok(manifest) = Manifest::new(data) else { return Err("cannot parse Gen 2 directory manifest".to_string()); }; - - let data_len = manifest.data_len(); - let mdata = &data[man::MANIFEST_SIZE..man::MANIFEST_SIZE + data_len]; - - let Ok((header, _)) = Header::read_from_prefix(mdata) else { + let Ok((header, _)) = Header::read_from_prefix(&manifest.mdata) else { return Err("cannot parse ME FW Gen 2 directory header".to_string()); }; let name = match from_utf8(&header.name) { @@ -236,20 +232,17 @@ impl Directory { // Check magic bytes of first entry. let pos = HEADER_SIZE; - let m = &mdata[pos..pos + 4]; + let slice = &manifest.mdata[pos..]; + let m = &slice[..4]; if !m.eq(MODULE_MAGIC_BYTES) { return Err(format!( "entry magic not found, got {m:02x?}, wanted {MODULE_MAGIC_BYTES:02x?} ({MODULE_MAGIC})" )); } // Parse the entries themselves. - let slice = &mdata[pos..]; let count = manifest.header.entries as usize; let Ok((r, _)) = Ref::<_, [Entry]>::from_prefix_with_elems(slice, count) else { - return Err(format!( - "cannot parse ME FW Gen 2 directory entries @ {:08x}", - pos - )); + return Err(format!("cannot parse ME FW Gen 2 directory entries",)); }; let entries = r.to_vec(); @@ -299,7 +292,7 @@ impl Directory { let size = data.len(); Ok(Self { - manifest: (manifest, mdata.to_vec()), + manifest, header, modules, offset, diff --git a/src/dir/gen3.rs b/src/dir/gen3.rs index 1d84e32..815a964 100644 --- a/src/dir/gen3.rs +++ b/src/dir/gen3.rs @@ -104,7 +104,7 @@ impl Display for CPDEntry { #[repr(C)] pub struct CodePartitionDirectory { pub header: CPDHeader, - pub manifest: Result<(Manifest, Vec), String>, + pub manifest: Result, pub entries: Vec, pub offset: usize, pub size: usize, @@ -125,7 +125,7 @@ impl Display for CodePartitionDirectory { let n = &self.name; let l1 = format!("{n} @ {o:08x}, checksum or version: {checksum:08x}"); let l2 = match &self.manifest { - Ok((m, _)) => { + Ok(m) => { let h = stringify_vec(m.hash_key()); let m = format!("{m}"); let kh = format!("Key hash: {h}"); @@ -174,16 +174,7 @@ impl CodePartitionDirectory { if let Some(e) = entries.iter().find(|e| e.name() == manifest_name) { let o = e.flags_and_offset.offset() as usize; let end = o + e.size as usize; - match Manifest::new(&data[o..end]) { - Ok(m) => { - // The manifest carries additional data after its header - // and key material. - let header_len = m.header_len(); - let mdata = data[o + header_len..end].to_vec(); - Ok((m, mdata)) - } - Err(e) => Err(e), - } + Manifest::new(&data[o..end]) } else { Err("no manifest found".to_string()) } diff --git a/src/dir/man.rs b/src/dir/man.rs index 5c49f34..1e92ab0 100644 --- a/src/dir/man.rs +++ b/src/dir/man.rs @@ -64,7 +64,7 @@ pub struct Header { pub flags: u32, pub vendor: Vendor, pub date: Date, - pub size: u32, // in dwords, dword size is 32bit + pub manifest_len: u32, // in dwords, dword size is 32bit pub magic: [u8; 4], // NOTE: only for Gen 2 ME firmware pub entries: u32, @@ -93,10 +93,28 @@ impl Display for Header { } } +impl Header { + /// Get the header length including signature + pub fn header_len(&self) -> usize { + self.header_len as usize * 4 + } + + /// Get the length of the manifest including its data + pub fn manifest_len(&self) -> usize { + self.manifest_len as usize * 4 + } + + /// Get the length of the data after the header + pub fn data_len(&self) -> usize { + let mlen = self.manifest_len(); + let hlen = self.header_len(); + mlen - hlen + } +} + #[derive(Immutable, IntoBytes, FromBytes, Serialize, Deserialize, Clone, Copy, Debug)] #[repr(C)] -pub struct Manifest { - pub header: Header, +pub struct Signature { #[serde(with = "serde_bytes")] pub rsa_pub_key: [u8; 0x100], pub rsa_pub_exp: u32, @@ -104,11 +122,19 @@ pub struct Manifest { pub rsa_sig: [u8; 0x100], } +#[derive(Serialize, Deserialize, Clone, Debug)] +#[repr(C)] +pub struct Manifest { + pub header: Header, + pub signature: Signature, + pub mdata: Vec, +} + pub const MANIFEST_SIZE: usize = core::mem::size_of::(); impl<'a> Manifest { pub fn new(data: &'a [u8]) -> Result { - let (manifest, _) = match Self::read_from_prefix(data) { + let (header, slice) = match Header::read_from_prefix(data) { Ok(r) => r, Err(e) => { let err = format!("Manifest cannot be parsed: {e:?}"); @@ -116,56 +142,57 @@ impl<'a> Manifest { } }; - if manifest.header.magic != MANIFEST2_MAGIC_BYTES { + if header.magic != MANIFEST2_MAGIC_BYTES { let err = format!( "Manifest magic not found: wanted {MANIFEST2_MAGIC_BYTES:02x?} ({MANIFEST2_MAGIC}), got {:02x?}", - manifest.header.magic + header.magic ); return Err(err); } - Ok(manifest) - } - - /// Get the header length - pub fn header_len(&self) -> usize { - self.header.header_len as usize * 4 - } - - /// Get the size of the manifest and its data - pub fn size(&self) -> usize { - self.header.size as usize * 4 - } + let (signature, _) = match Signature::read_from_prefix(slice) { + Ok(r) => r, + Err(e) => { + let err = format!("Signature cannot be parsed: {e:?}"); + return Err(err); + } + }; - /// Get the length of the data after the manifest - pub fn data_len(&self) -> usize { - let mlen = self.size(); - let hlen = self.header_len(); - mlen - hlen + // The manifest carries additional data after its header and signature. + // Note that header_len includes the signature. + let header_len = header.header_len(); + let size = header.manifest_len(); + let mdata = &data[header_len..size]; + + Ok(Self { + header, + signature, + mdata: mdata.to_vec(), + }) } /// Get the MD5 hash over the RSA public key and exponent. - pub fn hash_key(self: Self) -> Vec { - let k = self.rsa_pub_key.as_bytes(); - let e = self.rsa_pub_exp; + pub fn hash_key(&self) -> Vec { + let k = self.signature.rsa_pub_key.as_bytes(); + let e = self.signature.rsa_pub_exp; let ke = [k, &e.to_le_bytes()].concat(); md5::compute(ke).to_vec() } - /// Verify the manifest. Pass extra bytes from after the manifest. - pub fn verify(&self, ebytes: &[u8]) -> bool { + /// Verify the manifest. + pub fn verify(&self) -> bool { use num_bigint::BigUint; use sha2::{Digest, Sha256}; - let modulus = BigUint::from_bytes_le(&self.rsa_pub_key); - let exponent = BigUint::from(self.rsa_pub_exp); - let signature = BigUint::from_bytes_le(&self.rsa_sig); + let modulus = BigUint::from_bytes_le(&self.signature.rsa_pub_key); + let exponent = BigUint::from(self.signature.rsa_pub_exp); + let signature = BigUint::from_bytes_le(&self.signature.rsa_sig); let sb = signature.modpow(&exponent, &modulus).to_bytes_be(); let header = self.header.as_bytes(); let mut hasher = Sha256::new(); hasher.update(&header); - hasher.update(&ebytes); + hasher.update(&self.mdata); let hash = hasher.finalize(); let hb = hash.as_bytes(); @@ -177,7 +204,7 @@ impl<'a> Manifest { impl Display for Manifest { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let h = self.header; - let exp = self.rsa_pub_exp; + let exp = self.signature.rsa_pub_exp; write!(f, "{h}, RSA exp {exp}") } } diff --git a/src/part/gen2.rs b/src/part/gen2.rs index 3f98d7f..9549b08 100644 --- a/src/part/gen2.rs +++ b/src/part/gen2.rs @@ -20,8 +20,7 @@ pub struct DirPartition { impl DirPartition { pub fn check_signature(&self) -> Result<(), String> { - let (m, mdata) = &self.dir.manifest; - if m.verify(&mdata) { + if self.dir.manifest.verify() { return Ok(()); } else { return Err("hash mismatch".into()); diff --git a/src/part/gen3.rs b/src/part/gen3.rs index 10719b9..9ccae5c 100644 --- a/src/part/gen3.rs +++ b/src/part/gen3.rs @@ -21,8 +21,8 @@ pub struct CPDPartition { impl CPDPartition { pub fn check_signature(&self) -> Result<(), String> { - if let Ok((m, mdata)) = &self.cpd.manifest { - if m.verify(&mdata) { + if let Ok(m) = &self.cpd.manifest { + if m.verify() { return Ok(()); } else { return Err("hash mismatch".into()); diff --git a/src/part/partitions.rs b/src/part/partitions.rs index e5ac85c..b20d8d4 100644 --- a/src/part/partitions.rs +++ b/src/part/partitions.rs @@ -79,7 +79,7 @@ impl Partitions { if let Some(Gen2Partition::Dir(d)) = parts.iter().find(|p| matches!(p, Gen2Partition::Dir(_))) { - let (m, _) = d.dir.manifest; + let m = &d.dir.manifest; Some(m.header.version) } else { None @@ -89,7 +89,7 @@ impl Partitions { if let Some(Gen3Partition::Dir(d)) = parts.iter().find(|p| matches!(p, Gen3Partition::Dir(_))) { - if let Ok((m, _)) = d.cpd.manifest { + if let Ok(m) = &d.cpd.manifest { Some(m.header.version) } else { None