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}