mbn/
hash_table_segment.rs

1use std::mem::size_of;
2
3use crate::{
4    byte_read::ByteReader, error::ParseError, CommonMetadata, MbnHeader, MbnHeaderV6, MbnHeaderV7,
5    Metadata, Result,
6};
7
8/// Trailing padding of [`HashTableSegment`].
9#[derive(Clone, Copy, Debug)]
10pub struct Padding {
11    /// Byte used for padding, typically `0xFF`.
12    pub content: u8,
13    /// Length of padding in bytes.
14    pub len: usize,
15}
16
17/// Entry of hash table.
18#[derive(Clone, Copy, Debug)]
19pub enum HashEntry {
20    Sha1([u8; 20]),
21    Sha256([u8; 32]),
22    Sha384([u8; 48]),
23}
24
25/// Hash table segment representation.
26#[derive(Clone, Debug)]
27pub struct HashTableSegment {
28    /// MBN header, including locations of fields inside hash segment.
29    pub mbn_header: MbnHeader,
30    /// Common metadata, only available if header version is greater than 7.
31    pub common_metadata: Option<CommonMetadata>,
32    /// Information about the image supplied by QTI.
33    pub qti_metadata: Option<Metadata>,
34    /// Information about the image supplied by OEM.
35    pub metadata: Option<Metadata>,
36    /// Hashes of other segments in the ELF file.
37    pub hash_table: Vec<HashEntry>,
38    /// QTI signature.
39    pub qti_signature: Vec<u8>,
40    /// QTI certificate chain.
41    pub qti_certificate_chain: Vec<u8>,
42    /// OEM signature.
43    pub signature: Vec<u8>,
44    /// OEM certificate chain.
45    pub certificate_chain: Vec<u8>,
46    /// Padding bytes.
47    pub padding: Padding,
48}
49
50impl HashEntry {
51    /// Convert itself to a slice without copy.
52    pub fn as_bytes(&self) -> &[u8] {
53        match self {
54            HashEntry::Sha1(entry) => entry,
55            HashEntry::Sha256(entry) => entry,
56            HashEntry::Sha384(entry) => entry,
57        }
58    }
59}
60
61impl HashTableSegment {
62    /// Parse byte stream to hash table segment.
63    ///
64    /// NOTE: The unparsed parts at the end of byte stream are considered padding.
65    ///
66    /// Hint: Many `*.mbn` files are actually ELF files, please parse them via [`from_elf()`](crate::from_elf())
67    /// instead of this function.
68    pub fn parse(raw: &[u8]) -> Result<Self> {
69        let mut reader = ByteReader::new(raw);
70
71        let mbn_header = reader.read()?;
72
73        let common_metadata = match &mbn_header {
74            MbnHeader::V7(
75                header @ MbnHeaderV7 {
76                    common_meta_size: 1..,
77                    ..
78                },
79            ) => {
80                if header.common_meta_size != 24 {
81                    return Err(ParseError::CommonMetadataNotAligned(
82                        header.common_meta_size,
83                    ));
84                }
85                Some(reader.read::<CommonMetadata>()?)
86            }
87            _ => None,
88        };
89
90        let qti_metadata = match mbn_header {
91            MbnHeader::V6(MbnHeaderV6 {
92                qti_metadata_size: qti_metadata_size @ 1..,
93                ..
94            })
95            | MbnHeader::V7(MbnHeaderV7 {
96                qti_metadata_size: qti_metadata_size @ 1..,
97                ..
98            }) => match qti_metadata_size {
99                120 => Some(Metadata::Len120(reader.read()?)),
100                224 => Some(Metadata::Len224(reader.read()?)),
101                _ => return Err(ParseError::MetadataNotAligned(qti_metadata_size)),
102            },
103            _ => None,
104        };
105
106        let metadata = match mbn_header {
107            MbnHeader::V6(MbnHeaderV6 {
108                metadata_size: metadata_size @ 1..,
109                ..
110            })
111            | MbnHeader::V7(MbnHeaderV7 {
112                metadata_size: metadata_size @ 1..,
113                ..
114            }) => match metadata_size {
115                120 => Some(Metadata::Len120(reader.read()?)),
116                224 => Some(Metadata::Len224(reader.read()?)),
117                _ => return Err(ParseError::MetadataNotAligned(metadata_size)),
118            },
119            _ => None,
120        };
121
122        let mut hash_table = vec![];
123        let code_size = match &mbn_header {
124            MbnHeader::V3Len40(header) => header.code_size,
125            MbnHeader::V3Len80(header) => header.code_size,
126            MbnHeader::V5(header) => header.code_size,
127            MbnHeader::V6(header) => header.code_size,
128            MbnHeader::V7(header) => header.code_size,
129        };
130
131        let sha_algo = 'algo: {
132            match common_metadata {
133                Some(CommonMetadata {
134                    hash_table_algorithm: 2,
135                    ..
136                }) => break 'algo "SHA256",
137                Some(CommonMetadata {
138                    hash_table_algorithm: 3,
139                    ..
140                }) => break 'algo "SHA384",
141                _ => (),
142            };
143
144            if let MbnHeader::V6(_) = &mbn_header {
145                break 'algo "SHA384";
146            }
147
148            let maybe_dummy = reader.peek::<[u8; 20]>(20)?;
149            if maybe_dummy.iter().all(|x| *x == 0) {
150                break 'algo "SHA1";
151            }
152            let maybe_dummy = reader.peek::<[u8; 32]>(32)?;
153            if maybe_dummy.iter().all(|x| *x == 0) {
154                break 'algo "SHA256";
155            }
156            let maybe_dummy = reader.peek::<[u8; 48]>(48)?;
157            if maybe_dummy.iter().all(|x| *x == 0) {
158                break 'algo "SHA384";
159            }
160
161            return Err(ParseError::HashTableNotAligned(code_size));
162        };
163
164        match sha_algo {
165            "SHA1" => {
166                if code_size % 20 != 0 {
167                    return Err(ParseError::HashTableNotAligned(code_size));
168                }
169                for _ in 0..code_size / 20 {
170                    hash_table.push(HashEntry::Sha1(reader.read()?));
171                }
172            }
173            "SHA256" => {
174                if code_size % 32 != 0 {
175                    return Err(ParseError::HashTableNotAligned(code_size));
176                }
177                for _ in 0..code_size / 32 {
178                    hash_table.push(HashEntry::Sha256(reader.read()?));
179                }
180            }
181            "SHA384" => {
182                if code_size % 48 != 0 {
183                    return Err(ParseError::HashTableNotAligned(code_size));
184                }
185                for _ in 0..code_size / 48 {
186                    hash_table.push(HashEntry::Sha384(reader.read()?));
187                }
188            }
189            _ => unreachable!(),
190        };
191
192        let qti_signature = match &mbn_header {
193            MbnHeader::V5(header) => reader.skip(header.qti_signature_size as usize)?,
194            MbnHeader::V6(header) => reader.skip(header.qti_signature_size as usize)?,
195            _ => vec![],
196        };
197
198        let qti_certificate_chain = match &mbn_header {
199            MbnHeader::V5(header) => reader.skip(header.qti_cert_chain_size as usize)?,
200            MbnHeader::V6(header) => reader.skip(header.qti_cert_chain_size as usize)?,
201            _ => vec![],
202        };
203
204        let signature = match &mbn_header {
205            MbnHeader::V3Len40(header) => reader.skip(header.signature_size as usize)?,
206            MbnHeader::V3Len80(header) => reader.skip(header.signature_size as usize)?,
207            MbnHeader::V5(header) => reader.skip(header.signature_size as usize)?,
208            MbnHeader::V6(header) => reader.skip(header.signature_size as usize)?,
209            MbnHeader::V7(header) => reader.skip(header.signature_size as usize)?,
210        };
211
212        let certificate_chain = match &mbn_header {
213            MbnHeader::V3Len40(header) => reader.skip(header.cert_chain_size as usize)?,
214            MbnHeader::V3Len80(header) => reader.skip(header.cert_chain_size as usize)?,
215            MbnHeader::V5(header) => reader.skip(header.cert_chain_size as usize)?,
216            MbnHeader::V6(header) => reader.skip(header.cert_chain_size as usize)?,
217            MbnHeader::V7(header) => reader.skip(header.cert_chain_size as usize)?,
218        };
219
220        let mut padding = Padding {
221            content: 0xFF,
222            len: reader.available(),
223        };
224        if padding.len > 0 {
225            padding.content = reader.read()?;
226        }
227
228        Ok(Self {
229            mbn_header,
230            common_metadata,
231            qti_metadata,
232            metadata,
233            hash_table,
234            qti_signature,
235            qti_certificate_chain,
236            signature,
237            certificate_chain,
238            padding,
239        })
240    }
241
242    /// Dump hash table segment to byte stream.
243    ///
244    /// * `padding`: Write padding to byte stream or not.
245    pub fn dump<W: std::io::Write>(&self, writer: &mut W, padding: bool) -> Result<()> {
246        writer.write_all(self.mbn_header.as_bytes())?;
247        if let Some(common_metadata) = &self.common_metadata {
248            writer.write_all(common_metadata.as_bytes())?;
249        }
250        if let Some(metadata) = &self.qti_metadata {
251            writer.write_all(metadata.as_bytes())?;
252        }
253        if let Some(metadata) = &self.metadata {
254            writer.write_all(metadata.as_bytes())?;
255        }
256        for hash in &self.hash_table {
257            writer.write_all(hash.as_bytes())?;
258        }
259        writer.write_all(&self.qti_signature)?;
260        writer.write_all(&self.qti_certificate_chain)?;
261        writer.write_all(&self.signature)?;
262        writer.write_all(&self.certificate_chain)?;
263        if padding {
264            let padding = vec![self.padding.content; self.padding.len];
265            writer.write_all(&padding)?;
266        }
267
268        Ok(())
269    }
270
271    /// Get the length of the segment in bytes.
272    pub fn len(&self) -> usize {
273        let mut len = match self.mbn_header {
274            MbnHeader::V3Len40(_) | MbnHeader::V5(_) => 40,
275            MbnHeader::V3Len80(_) => 80,
276            MbnHeader::V6(_) => 48,
277            MbnHeader::V7(_) => 40,
278        };
279
280        if self.qti_metadata.is_some() {
281            len += 120;
282        }
283        if self.metadata.is_some() {
284            len += 120;
285        }
286        len += self.hash_table.len() * 48;
287        len += self.qti_signature.len();
288        len += self.qti_certificate_chain.len();
289        len += self.signature.len();
290        len += self.certificate_chain.len();
291        len += self.padding.len;
292        len
293    }
294
295    /// Adjust field values of the MBN header to suitable values and the length of padding bytes.
296    ///
297    /// * `Padding_to`: pad the segment to a specified size.
298    pub fn adjust(&mut self, padding_to: usize) {
299        self.mbn_header.adjust_header_version();
300        self.mbn_header.adjust_image_src();
301        match &mut self.mbn_header {
302            MbnHeader::V3Len40(header) => {
303                header.code_size = self
304                    .hash_table
305                    .iter()
306                    .fold(0, |acc, entry| acc + entry.as_bytes().len())
307                    as u32;
308                header.signature_size = self.signature.len() as u32;
309                header.cert_chain_size = self.certificate_chain.len() as u32;
310            }
311            MbnHeader::V3Len80(header) => {
312                header.code_size = self
313                    .hash_table
314                    .iter()
315                    .fold(0, |acc, entry| acc + entry.as_bytes().len())
316                    as u32;
317                header.signature_size = self.signature.len() as u32;
318                header.cert_chain_size = self.certificate_chain.len() as u32;
319            }
320            MbnHeader::V5(header) => {
321                header.code_size = self
322                    .hash_table
323                    .iter()
324                    .fold(0, |acc, entry| acc + entry.as_bytes().len())
325                    as u32;
326                header.qti_signature_size = self.qti_signature.len() as u32;
327                header.qti_cert_chain_size = self.qti_certificate_chain.len() as u32;
328                header.signature_size = self.signature.len() as u32;
329                header.cert_chain_size = self.certificate_chain.len() as u32;
330            }
331            MbnHeader::V6(header) => {
332                header.qti_metadata_size = if let Some(metadata) = &self.qti_metadata {
333                    metadata.as_bytes().len() as u32
334                } else {
335                    0
336                };
337                header.metadata_size = if let Some(metadata) = &self.metadata {
338                    metadata.as_bytes().len() as u32
339                } else {
340                    0
341                };
342                header.code_size = self
343                    .hash_table
344                    .iter()
345                    .fold(0, |acc, entry| acc + entry.as_bytes().len())
346                    as u32;
347                header.qti_signature_size = self.qti_signature.len() as u32;
348                header.qti_cert_chain_size = self.qti_certificate_chain.len() as u32;
349                header.signature_size = self.signature.len() as u32;
350                header.cert_chain_size = self.certificate_chain.len() as u32;
351            }
352            MbnHeader::V7(header) => {
353                header.common_meta_size = if self.common_metadata.is_some() {
354                    size_of::<CommonMetadata>() as u32
355                } else {
356                    0
357                };
358                header.qti_metadata_size = if let Some(metadata) = &self.qti_metadata {
359                    metadata.as_bytes().len() as u32
360                } else {
361                    0
362                };
363                header.metadata_size = if let Some(metadata) = &self.metadata {
364                    metadata.as_bytes().len() as u32
365                } else {
366                    0
367                };
368                header.code_size = self
369                    .hash_table
370                    .iter()
371                    .fold(0, |acc, entry| acc + entry.as_bytes().len())
372                    as u32;
373                header.qti_signature_size = self.qti_signature.len() as u32;
374                header.qti_cert_chain_size = self.qti_certificate_chain.len() as u32;
375                header.signature_size = self.signature.len() as u32;
376                header.cert_chain_size = self.certificate_chain.len() as u32;
377            }
378        }
379        self.mbn_header.adjust_image_size();
380
381        let len_no_padding = self.len() - self.padding.len;
382        if len_no_padding >= padding_to {
383            self.padding.len = 0;
384        } else {
385            self.padding.len = padding_to - len_no_padding;
386        }
387    }
388}