amebazii/types/fst.rs
1use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
2use std::io;
3
4use super::{enums::*, BinarySize, DataRefType, DataType, FromStream, ToStream};
5use crate::{
6 error::Error, is_valid_data, keys::DEFAULT_VALID_PATTERN, read_padding, util::write_fill,
7 write_data, write_padding,
8};
9
10/// # Firmware Security Table (FST)
11///
12/// The `FST` struct represents the firmware security table (FST) of a sub-image within a
13/// firmware image. This table holds information about the encryption algorithm, hash
14/// algorithm, security keys, and other configuration for firmware partitions.
15///
16/// ## Layout
17/// ```text
18/// +-------+-------+--------+-------+-----------+---------------+---+---+---+---+----+----+----+----+----+----+
19/// | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
20/// +========+=======+=======+========+=======+===========+===============+===+===+===+===+====+====+====+====+====+====+
21/// | 0x00 | enc_algo: u16 | hash_algo: u16 | part_size: u32 | valipat: bytes[8] |
22/// +--------+--------------------------------+-----------+---------------+---------------------------------------------+
23/// | 0x10 | | flags: u8 | key_flags: u8 | |
24/// +--------+--------------------------------+-----------+---------------+---------------------------------------------+
25/// | 0x20 | cipher_key: bytes[32] |
26/// +--------+----------------------------------------------------------------------------------------------------------+
27/// | 0x40 | cipher_iv: bytes[16] |
28/// +--------+----------------------------------------------------------------------------------------------------------+
29/// | 0x50 | |
30/// +--------+----------------------------------------------------------------------------------------------------------+
31/// ```
32/// - Size = 0x60 = 96 bytes
33///
34/// **Note:** Encryption and cipher-related fields are placeholders, as encryption is not
35/// currently supported. These fields are implemented as `Option<T>` to allow future extension
36/// if encryption support is added later.
37///
38/// # Example:
39/// ```rust
40/// let mut fst = FST::default();
41///
42/// // set hash algorithm
43/// fst.hash_algo = Some(HashAlgo::Sha256);
44///
45/// // clear encryption algorithm
46/// fst.enc_algo = None;
47/// ```
48#[derive(Debug)]
49pub struct FST {
50 /// encryption algorithm (not supported)
51 pub enc_algo: Option<EncryptionAlgo>,
52
53 /// The hash algorithm used for hashing. Default is `Sha256`.
54 pub hash_algo: Option<HashAlgo>,
55
56 pub partition_size: u32,
57 valid_pattern: [u8; 8],
58
59 cipher_key: DataType<32>,
60 cipher_iv: DataType<16>,
61}
62
63impl Default for FST {
64 fn default() -> FST {
65 return FST {
66 enc_algo: None, // currently encryption is not supported
67 hash_algo: Some(HashAlgo::Sha256),
68 partition_size: 0, // default is zero
69 valid_pattern: DEFAULT_VALID_PATTERN.clone(),
70 cipher_key: None,
71 cipher_iv: None,
72 };
73 }
74}
75
76impl BinarySize for FST {
77 /// Returns the binary size of the `FST` structure in bytes.
78 ///
79 /// # Returns:
80 /// The size of the `FST` struct in bytes, which is `0x60` (96 bytes).
81 #[inline]
82 fn binary_size() -> usize {
83 return 0x60;
84 }
85}
86
87impl FST {
88 // ------------------------------------------------------------------------------------
89 // instance methods
90 // ------------------------------------------------------------------------------------
91
92 /// Checks if the cipher key and IV are valid.
93 ///
94 /// # Returns:
95 /// - `true`: If both the cipher key and IV are valid.
96 /// - `false`: If either the cipher key or IV is not set or invalid.
97 pub fn is_cipher_key_iv_valid(&self) -> bool {
98 match (&self.cipher_key, &self.cipher_iv) {
99 (Some(key), Some(iv)) => is_valid_data!(key) && is_valid_data!(iv),
100 _ => false,
101 }
102 }
103
104 /// Returns a reference to the validation pattern used for the FST structure.
105 ///
106 /// # Returns:
107 /// A reference to the 8-byte validation pattern.
108 pub fn get_pattern(&self) -> &[u8; 8] {
109 return &self.valid_pattern;
110 }
111
112 /// Returns a reference to the cipher key if it is set.
113 ///
114 /// # Returns:
115 /// An `Option` containing a reference to the 32-byte cipher key.
116 ///
117 /// ```rust
118 /// let fst = FST::default();
119 /// if let Some(cipher_key) = fst.get_cipher_key() {
120 /// // Handle valid cipher key
121 /// }
122 /// ```
123 pub fn get_cipher_key(&self) -> DataRefType<'_, 32> {
124 return self.cipher_key.as_ref();
125 }
126
127 /// Returns a reference to the cipher IV if it is set.
128 ///
129 /// # Returns:
130 /// An `Option` containing a reference to the 16-byte cipher IV.
131 pub fn get_cipher_iv(&self) -> DataRefType<'_, 16> {
132 return self.cipher_iv.as_ref();
133 }
134
135 pub fn set_cipher_iv(&mut self, iv: DataType<16>) {
136 self.cipher_iv = iv;
137 }
138
139 pub fn set_cipher_key(&mut self, key: DataType<32>) {
140 self.cipher_key = key;
141 }
142
143 pub fn set_valid_pattern(&mut self, pattern: [u8; 8]) {
144 self.valid_pattern = pattern;
145 }
146}
147
148impl FromStream for FST {
149 /// Reads the `FST` structure from a stream and parses its data.
150 ///
151 /// # Parameters:
152 /// - `reader`: A mutable reference to a reader that implements `std::io::Read` and
153 /// `std::io::Seek` traits. This could be a file, buffer, or network stream.
154 ///
155 /// # Returns:
156 /// - On failure, an `Error` is returned.
157 fn read_from<R>(&mut self, reader: &mut R) -> Result<(), Error>
158 where
159 R: std::io::Read + std::io::Seek,
160 {
161 // Read the encryption algorithm (u16 to EncryptionAlgo), even though it may
162 // be unset later on
163 self.enc_algo = Some(EncryptionAlgo::try_from(
164 reader.read_u16::<LittleEndian>()?,
165 )?);
166
167 self.hash_algo = Some(HashAlgo::try_from(reader.read_u16::<LittleEndian>()?)?);
168 self.partition_size = reader.read_u32::<LittleEndian>()?;
169 reader.read_exact(&mut self.valid_pattern)?; // 8 bytes
170
171 // 4 bytes padding
172 read_padding!(reader, 4);
173
174 let flags = reader.read_u8()? & 0b11;
175 let enc_enabled = flags & 0b01 == 0x01;
176 let hash_enabled = flags & 0b10 != 0;
177 // REVISIT: necessary?
178 if !enc_enabled {
179 self.enc_algo = None;
180 }
181 if !hash_enabled {
182 self.hash_algo = None;
183 }
184
185 if reader.read_u8()? & 0b1 == 1 {
186 // keys are valid
187 reader.seek(std::io::SeekFrom::Current(10))?;
188 let mut key = [0; 32];
189 let mut iv = [0; 16];
190 reader.read_exact(&mut key)?; // 32 bytes
191 reader.read_exact(&mut iv)?; // 16 bytes
192
193 self.cipher_key = Some(key);
194 self.cipher_iv = Some(iv);
195 // align to 96
196 read_padding!(reader, 16);
197 } else {
198 // align to 96
199 read_padding!(reader, 74); // 16 + 32 + 16 + 10
200 }
201 return Ok(());
202 }
203}
204
205impl ToStream for FST {
206 /// Writes the `FST` structure to a stream
207 ///
208 /// # Parameters:
209 /// - `writer`: A mutable reference to a writer that implements the `std::io::Write` trait.
210 /// This could be a file, buffer, or network stream where the `FST` will be written.
211 fn write_to<W>(&self, writer: &mut W) -> Result<(), Error>
212 where
213 W: std::io::Write,
214 {
215 // Write the encryption algorithm and hash algorithm (u16) or a default value
216 writer.write_u16::<LittleEndian>(self.enc_algo.unwrap_or_default() as u16)?;
217 writer.write_u16::<LittleEndian>(self.hash_algo.unwrap_or_default() as u16)?;
218 writer.write_u32::<LittleEndian>(self.partition_size)?;
219 writer.write_all(&self.valid_pattern)?; // 8 bytes
220
221 // padding
222 write_padding!(writer, 4);
223
224 let flags = if self.enc_algo.is_some() { 0b01 } else { 0 }
225 | if self.hash_algo.is_some() { 0b10 } else { 0 };
226 writer.write_u8(flags & 0b11)?; // 2 bits
227 writer.write_u8(self.is_cipher_key_iv_valid() as u8)?;
228
229 // padding
230 write_padding!(writer, 10);
231 write_data!(writer, self.cipher_key, 32);
232 write_data!(writer, self.cipher_iv, 16);
233 // align to 96
234 write_padding!(writer, 16);
235 Ok(())
236 }
237}