amebazii/types/image/
boot.rs

1use std::io::{Cursor, Seek, Write};
2
3use crate::{
4    types::{
5        header::{EntryHeader, ImageHeader, KeyBlock},
6        BinarySize, FromStream, ToStream,
7    },
8    util::{hmac_sha256, skip_aligned, write_fill},
9    write_aligned,
10};
11
12use super::AsImage;
13
14/// Represents a boot image, including encryption public keys, hash, and segment data.
15///
16/// This struct contains the details of the boot image including the encryption and hash public keys,
17/// header, entry, text (payload), and a hash representing the integrity of the image.
18#[derive(Debug)]
19pub struct BootImage {
20    pub keyblock: KeyBlock,
21    /// The header of the boot image, containing general information about the image.
22    pub header: ImageHeader,
23
24    /// The entry header, typically pointing to the start of the executable code or data.
25    pub entry: EntryHeader,
26
27    /// The textual or executable payload of the boot image.
28    /// This can be any binary data contained within the image, typically the code or firmware.
29    text: Vec<u8>,
30
31    /// The hash of the boot image.
32    /// This is a 32-byte hash used to verify the integrity of the boot image.
33    hash: [u8; 32],
34}
35
36impl Default for BootImage {
37    /// Creates a new `BootImage` with default values.
38    ///
39    /// The `BootImage` is initialized with default values:
40    /// - Encryption and hash public keys are set to all `0xFF` bytes.
41    /// - The header and entry are initialized with their default values.
42    /// - The `text` field is an empty vector, and the `hash` field is set to all `0xFF` bytes.
43    fn default() -> Self {
44        BootImage {
45            keyblock: KeyBlock::default(),
46            header: ImageHeader::default(),
47            entry: EntryHeader::default(),
48            text: Vec::new(),
49            hash: [0xFF; 32],
50        }
51    }
52}
53
54impl BootImage {
55    /// Retrieves the text (code) content of the boot image.
56    ///
57    /// This method provides access to the `text` field of the `BootImage` as a byte slice.
58    /// It allows reading the raw byte data representing the text within the boot image.
59    ///
60    /// # Example
61    ///
62    /// ```
63    /// use amebazii::types::BootImage;
64    ///
65    /// let boot_image = BootImage::default();
66    /// let text = boot_image.get_text();
67    /// println!("Text data: {:?}", text);
68    /// ```
69    pub fn get_text(&self) -> &[u8] {
70        &self.text
71    }
72
73    /// Retrieves the hash value associated with the boot image.
74    ///
75    /// This method returns the `hash` field of the `BootImage`, which is a byte slice
76    /// representing the hash (cryptographic hash) of the boot image data.
77    pub fn get_hash(&self) -> &[u8] {
78        &self.hash
79    }
80
81    /// Sets the text content of the boot image.
82    ///
83    /// This method updates the `text` field of the `BootImage` with a new vector of bytes.
84    /// It allows modifying the raw byte data that represents the boot image’s text.
85    ///
86    /// # Arguments
87    ///
88    /// * `text` - A vector of bytes (`Vec<u8>`) to set as the new text content.
89    ///
90    /// # Example
91    ///
92    /// ```
93    /// use amebazii::types::BootImage;
94    ///
95    /// let mut boot_image = BootImage::default();
96    /// let new_text = vec![1, 2, 3, 4, 5];
97    /// boot_image.set_text(new_text);
98    /// assert_eq!(boot_image.get_text(), vec![1, 2, 3, 4, 5]);
99    /// ```
100    pub fn set_text(&mut self, text: Vec<u8>) {
101        self.text = text;
102    }
103}
104
105
106impl FromStream for BootImage {
107    /// Reads a `BootImage` from a binary stream.
108    ///
109    /// # Arguments:
110    /// - `reader`: The stream from which the data will be read. This must implement `std::io::Read` and `std::io::Seek`.
111    ///
112    /// # Returns:
113    /// - `Result<(), crate::error::Error>`: A `Result` indicating success or failure. If an error occurs during reading, it returns an error.
114    fn read_from<R>(&mut self, reader: &mut R) -> Result<(), crate::error::Error>
115    where
116        R: std::io::Read + std::io::Seek,
117    {
118        self.keyblock.read_from(reader)?;
119        self.header.read_from(reader)?;
120
121        // TODO: add support for encrypted boot images
122        self.entry.read_from(reader)?;
123
124        // Resize the `text` field to match the segment size in the header, then read it
125        self.text.resize(
126            self.header.segment_size as usize - EntryHeader::binary_size(),
127            0x00,
128        );
129        reader.read_exact(&mut self.text)?;
130
131        // Skip any padding (aligned to 0x20 bytes)
132        skip_aligned(reader, 0x20)?;
133
134        // Read the final hash for the boot image
135        reader.read_exact(&mut self.hash)?;
136        Ok(())
137    }
138}
139
140impl AsImage for BootImage {
141    /// Computes the segment size for the BootImage.
142    ///
143    /// The segment size includes the size of the `header`, `entry`, `text`, and the `hash`.
144    ///
145    /// # Returns:
146    /// - `u32`: The computed segment size.
147    fn build_segment_size(&self) -> u32 {
148        // Segment size is the sum of the header size, entry size, text size, and hash size.
149        // You can adjust this formula if your BootImage structure needs additional fields.
150        let new_size = self.text.len() as u32 + EntryHeader::binary_size() as u32;
151        new_size + (0x20 - (new_size % 0x20))
152    }
153
154    /// Sets the segment size for the BootImage.
155    ///
156    /// This method sets the `segment_size` field in the `header` of the `BootImage`.
157    ///
158    /// # Arguments:
159    /// - `size`: The segment size to set.
160    fn set_segment_size(&mut self, size: u32) {
161        self.header.segment_size = size as u32;
162        self.entry.length = size - EntryHeader::binary_size() as u32;
163    }
164
165    /// Computes the signature for the BootImage.
166    ///
167    /// This method computes the signature (e.g., HMAC or checksum) for the `BootImage` using the provided key.
168    ///
169    /// # Arguments:
170    /// - `key`: The key used to compute the signature.
171    ///
172    /// # Returns:
173    /// - `Result<Vec<u8>, crate::error::Error>`: The computed signature as a vector of bytes.
174    fn build_signature(&self, key: Option<&[u8]>) -> Result<Vec<u8>, crate::error::Error> {
175        let mut buffer = vec![
176            0x00;
177            KeyBlock::binary_size()
178                + ImageHeader::binary_size()
179                + self.build_segment_size() as usize
180        ];
181        let mut writer = Cursor::new(&mut buffer);
182
183        // Serialize the components of the BootImage into a buffer
184        self.keyblock.write_to(&mut writer)?;
185        self.header.write_to(&mut writer)?;
186        self.entry.write_to(&mut writer)?;
187        writer.write_all(&self.text)?;
188        write_aligned!(&mut writer, 0x20, 0x00, optional);
189
190        // The signature is generated using HMAC or any other algorithm.
191        Ok(hmac_sha256(key.unwrap(), &buffer)?.to_vec())
192    }
193
194    /// Sets the signature for the BootImage.
195    ///
196    /// This method sets the signature field of the `BootImage` (i.e., the `hash` field).
197    ///
198    /// # Arguments:
199    /// - `signature`: The computed signature to set in the `BootImage`.
200    fn set_signature(&mut self, signature: &[u8]) {
201        self.hash.copy_from_slice(signature);
202    }
203}
204
205impl ToStream for BootImage {
206    /// Writes a `BootImage` to a binary stream.
207    ///
208    /// # Arguments:
209    /// - `writer`: The stream to which the data will be written. This must implement `std::io::Write`.
210    ///
211    /// # Returns:
212    /// - `Result<(), crate::error::Error>`: A `Result` indicating success or failure. If an error occurs during writing, it returns an error.
213    fn write_to<W>(&self, writer: &mut W) -> Result<(), crate::error::Error>
214    where
215        W: std::io::Write + std::io::Seek,
216    {
217        self.keyblock.write_to(writer)?;
218        self.header.write_to(writer)?;
219        self.entry.write_to(writer)?;
220        writer.write_all(&self.text)?;
221
222        // Pad the text to a multiple of 0x20 bytes
223        let text_len = self.text.len();
224        if self.header.segment_size > text_len as u32 {
225            writer.write_all(&vec![
226                0x00;
227                (self.header.segment_size - text_len as u32) as usize
228            ])?;
229        }
230
231        writer.write_all(&self.hash)?;
232        Ok(())
233    }
234}