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}