amebazii/types/
header.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
use std::io;

use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};

use crate::{
    error::Error, is_valid_data, read_padding, read_valid_data, util::write_fill, write_data,
    write_padding,
};

use super::{
    enums::{ImageType, SectionType, XipPageRemapSize},
    BinarySize, DataRefType, DataType, FromStream, ToStream,
};

/// A struct representing the key block with two public keys:
/// * an encryption public key and
/// * a hash public key.
#[derive(Debug)]
pub struct KeyBlock {
    enc_pubkey: [u8; 32],  // Encryption public key (32 bytes)
    hash_pubkey: [u8; 32], // Hash public key (32 bytes)
}

impl Default for KeyBlock {
    /// Creates a default `KeyBlock` with both public keys initialized to `[0xFF; 32]`.
    ///
    /// # Returns
    /// - A `KeyBlock` instance with both the encryption and hash public keys set to `0xFF`.
    fn default() -> Self {
        KeyBlock {
            enc_pubkey: [0xFF; 32],
            hash_pubkey: [0xFF; 32],
        }
    }
}

impl BinarySize for KeyBlock {
    /// Returns the binary size of the `KeyBlock` in bytes.
    ///
    /// Since the struct contains two 32-byte fields (`enc_pubkey` and `hash_pubkey`), the total size is 64 bytes.
    ///
    /// # Returns
    /// - `64`: The size in bytes required to serialize a `KeyBlock`.
    fn binary_size() -> usize {
        64
    }
}

impl FromStream for KeyBlock {
    /// Deserializes a `KeyBlock` from a stream.
    ///
    /// This method reads 64 bytes (32 bytes for each of the `enc_pubkey` and `hash_pubkey`) from the provided
    /// reader and populates the `KeyBlock` instance fields accordingly.
    ///
    /// # Parameters
    /// - `reader`: A mutable reference to a reader implementing `io::Read` and `io::Seek`.
    ///
    /// # Returns
    /// - `Ok(())`: If the `KeyBlock` was successfully deserialized.
    /// - `Err(Error)`: If an error occurs while reading from the stream.
    fn read_from<R>(&mut self, reader: &mut R) -> Result<(), Error>
    where
        R: std::io::Read + std::io::Seek,
    {
        reader.read_exact(&mut self.enc_pubkey)?;
        reader.read_exact(&mut self.hash_pubkey)?;
        Ok(())
    }
}

impl ToStream for KeyBlock {
    /// Serializes a `KeyBlock` to a stream.
    ///
    /// # Parameters
    /// - `writer`: A mutable reference to a writer implementing `io::Write` and `io::Seek`.
    ///
    /// # Returns
    /// - `Ok(())`: If the `KeyBlock` was successfully written to the stream.
    /// - `Err(Error)`: If an error occurs while writing to the stream.
    fn write_to<W>(&self, writer: &mut W) -> Result<(), Error>
    where
        W: std::io::Write + std::io::Seek,
    {
        writer.write_all(&self.enc_pubkey)?;
        writer.write_all(&self.hash_pubkey)?;
        Ok(())
    }
}

impl KeyBlock {
    /// Checks if the encryption public key is valid.
    ///
    /// # Returns
    /// - `true` if the encryption public key is valid.
    /// - `false` if the encryption public key is invalid.
    pub fn is_enc_pubkey_valid(&self) -> bool {
        is_valid_data!(&self.enc_pubkey)
    }

    /// Checks if the hash public key is valid.
    ///
    /// # Returns
    /// - `true` if the hash public key is valid.
    /// - `false` if the hash public key is invalid.
    pub fn is_hash_pubkey_valid(&self) -> bool {
        is_valid_data!(&self.hash_pubkey)
    }

    /// Retrieves the encryption public key.
    ///
    /// # Returns
    /// - A reference to the encryption public key (32 bytes).
    pub fn get_enc_pubkey(&self) -> &[u8; 32] {
        &self.enc_pubkey
    }

    /// Retrieves the hash public key.
    ///
    /// # Returns
    /// - A reference to the hash public key (32 bytes).
    pub fn get_hash_pubkey(&self) -> &[u8; 32] {
        &self.hash_pubkey
    }

    /// Retrieves a mutable reference to the encryption public key.
    ///
    /// # Returns
    /// - A mutable reference to the encryption public key (32 bytes).
    pub fn get_enc_pubkey_mut(&mut self) -> &mut [u8; 32] {
        &mut self.enc_pubkey
    }

    /// Retrieves a mutable reference to the hash public key.
    ///
    /// # Returns
    /// - A mutable reference to the hash public key (32 bytes).
    pub fn get_hash_pubkey_mut(&mut self) -> &mut [u8; 32] {
        &mut self.hash_pubkey
    }
}

// --- Generic Header ---
// According to _create_img_header
//          +---+---+---+---+----+----+----+---+----------+----------------+--------------+---------------+----+----+----+----+
//          | 0 | 1 | 2 | 3 | 4  | 5  | 6  | 7 | 8        | 9              | 10           | 11            | 12 | 13 | 14 | 15 |
// +========+===+===+===+===+====+====+====+===+==========+================+==============+===============+====+====+====+====+
// | 0x00   |  length: u32  | next_offset: u32 | type: u8 | is_encrypt: u8 | pkey_idx: u8 | key_valid: u8 |                   |
// +--------+---------------+------------------+----------+----------------+--------------+---------------+-------------------+
// | 0x10   |               |   serial: u32    |                                                                              |
// +--------+---------------+------------------+------------------------------------------------------------------------------+
// | 0x20   |                                                 key1: bytes[32]                                                 |
// +--------+-----------------------------------------------------------------------------------------------------------------+
// | 0x40   |                                                 key2: bytes[32]                                                 |
// +--------+-----------------------------------------------------------------------------------------------------------------+
// size: 0x60 = 96 bytes

/// Generic image header.
///
/// This struct contains metadata for an image, such as the segment size, offset to the next image header,
/// the image type, encryption flag, and user keys.
#[derive(Debug)]
pub struct ImageHeader {
    /// The size of the image segment in bytes.
    ///
    /// This field specifies the size of the image's data segment, which can be used to determine how much
    /// data is associated with the current image header.
    pub segment_size: u32,

    /// Offset to the next image header.
    ///
    /// If there is no next image header, this field is set to `None`. Otherwise, it holds the
    /// byte offset to the next image header.
    pub next_offset: Option<u32>,

    /// The type of the image.
    ///
    /// This field stores the image type, which can represent different image types such as boot images,
    /// partition tables, etc.
    pub img_type: ImageType,

    /// Flag indicating whether the image is encrypted.
    ///
    /// This boolean flag indicates whether the image is encrypted (`true`) or not (`false`).
    pub is_encrypt: bool,

    /// The serial number associated with the image. (version number)
    ///
    /// This field stores the image's serial number. It is initialized to `0xFFFF_FFFF` by default.
    pub serial: u32,

    /// User key 1, used for encryption
    pub user_key1: DataType<32>,

    /// User key 2, used for encryption
    pub user_key2: DataType<32>,
}

impl Default for ImageHeader {
    /// Creates a default `ImageHeader` instance with the following default values:
    /// - `segment_size`: `0` (default size is zero)
    /// - `next_offset`: `0xFFFF_FFFF` (indicating no next header) == None
    /// - `img_type`: `ImageType::Parttab` (default is partition table)
    /// - `is_encrypt`: `false` (default is no encryption)
    /// - `serial`: `0xFFFF_FFFF` (default invalid serial number)
    /// - `user_key1`: `[0xFF; 32]` (default invalid key)
    /// - `user_key2`: `[0xFF; 32]` (default invalid key)
    ///
    /// # Returns
    /// - A new `ImageHeader` with the default values.
    fn default() -> Self {
        ImageHeader {
            segment_size: 0,
            next_offset: None,            // invalid by default
            img_type: ImageType::Parttab, // partition table by default
            is_encrypt: false,
            serial: 0xFFFF_FFFF,
            user_key1: None, // invalid by default
            user_key2: None,
        }
    }
}

impl BinarySize for ImageHeader {
    /// Returns the binary size of the `ImageHeader` in bytes.
    #[inline]
    fn binary_size() -> usize {
        0x60 // 96 bytes total size
    }
}

impl FromStream for ImageHeader {
    /// Reads an `ImageHeader` from a stream, populating its fields.
    ///
    /// # Parameters
    /// - `reader`: A mutable reference to a reader that implements both `io::Read` and `io::Seek`. This is used
    ///   to read the raw byte data representing the `ImageHeader`.
    ///
    /// # Returns
    /// - `Ok(())`: If the header is successfully read from the stream and all fields are populated.
    /// - `Err(Error)`: If any errors occur while reading the data from the stream.
    fn read_from<R>(&mut self, reader: &mut R) -> Result<(), Error>
    where
        R: std::io::Read + std::io::Seek,
    {
        self.segment_size = reader.read_u32::<LittleEndian>()?;

        let offset = reader.read_u32::<LittleEndian>()?;
        self.next_offset = if offset != 0xFFFF_FFFF {
            Some(offset)
        } else {
            None
        };

        self.img_type = ImageType::try_from(reader.read_u8()?)?;
        self.is_encrypt = reader.read_u8()? != 0;

        // Skip the next byte (reserved - seems to be pkey_index)
        read_padding!(reader, 1);

        // REVIIST: unused
        let _flags = reader.read_u8()?;

        read_padding!(reader, 8);
        self.serial = reader.read_u32::<LittleEndian>()?;
        read_padding!(reader, 8);

        read_valid_data!(self.user_key1, 32, reader);
        read_valid_data!(self.user_key2, 32, reader);
        Ok(())
    }
}

impl ToStream for ImageHeader {
    /// Serializes the `ImageHeader` struct to a stream.
    ///
    /// # Parameters
    /// - `writer`: A mutable reference to a type that implements the `std::io::Write` trait. The data will
    ///   be written to this stream.
    ///
    /// # Returns
    /// - `Ok(())`: If the data is written successfully.
    /// - `Err(Error)`: If there is an issue writing the data to the stream.
    fn write_to<W>(&self, writer: &mut W) -> Result<(), Error>
    where
        W: std::io::Write,
    {
        writer.write_u32::<LittleEndian>(self.segment_size)?;
        writer.write_u32::<LittleEndian>(self.next_offset.unwrap_or(0xFFFF_FFFF))?;
        writer.write_u8(self.img_type as u8)?;
        writer.write_u8(self.is_encrypt as u8)?;
        writer.write_u8(0x00)?;

        // Write the key validity flags (1 byte), combining key1_valid and key2_valid
        let key1_valid = self.is_key1_valid();
        let key2_valid = self.is_key2_valid();
        writer.write_u8((key1_valid as u8) | ((key2_valid as u8) << 1))?;

        write_padding!(writer, 8);
        writer.write_u32::<LittleEndian>(self.serial)?;
        write_padding!(writer, 8);

        // If key1 is valid, write user_key1 (32 bytes), otherwise write padding
        write_data!(writer, self.user_key1, 32);
        write_data!(writer, self.user_key2, 32);
        Ok(())
    }
}

impl ImageHeader {
    // ------------------------------------------------------------------------------------
    // instance methods
    // ------------------------------------------------------------------------------------

    /// Checks if the first user key (`user_key1`) is valid.
    ///
    /// # Returns
    /// - `true` if `user_key1` is valid (i.e., not all bytes are `0xFF`).
    /// - `false` if `user_key1` is invalid (i.e., all bytes are `0xFF`).
    pub fn is_key1_valid(&self) -> bool {
        match &self.user_key1 {
            None => false,
            Some(key) => key.iter().any(|&byte| byte != 0xFF),
        }
    }

    /// Checks if the second user key (`user_key2`) is valid.
    ///
    /// # Returns
    /// - `true` if `user_key2` is valid (i.e., not all bytes are `0xFF`).
    /// - `false` if `user_key2` is invalid (i.e., all bytes are `0xFF`).
    pub fn is_key2_valid(&self) -> bool {
        match &self.user_key2 {
            None => false,
            Some(key) => key.iter().any(|&byte| byte != 0xFF),
        }
    }

    /// Checks if there is a next image header.
    ///
    /// The `next_offset` field indicates the offset to the next image header. If the value
    /// is `0xFFFF_FFFF`, it means there is no next image. This method returns `true` if the
    /// `next_offset` is not `0xFFFF_FFFF`, indicating there is a next image header.
    ///
    /// # Returns
    /// - `true` if there is a next image (i.e., `next_offset` is not `0xFFFF_FFFF`).
    /// - `false` if there is no next image (i.e., `next_offset` is `0xFFFF_FFFF`).
    pub fn has_next(&self) -> bool {
        self.next_offset.is_some()
    }

    /// Gets the first user key (`user_key1`).
    ///
    /// # Returns
    /// A reference to the `user_key1` array (32 bytes).
    pub fn get_user_key1(&self) -> DataRefType<32> {
        self.user_key1.as_ref()
    }

    /// Gets the second user key (`user_key2`).
    ///
    /// # Returns
    /// A reference to the `user_key2` array (32 bytes).
    pub fn get_user_key2(&self) -> DataRefType<32> {
        self.user_key2.as_ref()
    }

    /// Sets the first user key (`user_key1`) in the image header.
    ///
    /// # Arguments
    ///
    /// * `key` - A `DataType<32>` representing the new value for `user_key1`.
    pub fn set_user_key1(&mut self, key: DataType<32>) {
        self.user_key1 = key;
    }

    /// Sets the second user key (`user_key2`) in the image header.
    ///
    /// # Arguments
    ///
    /// * `key` - A `DataType<32>` representing the new value for `user_key2`.
    pub fn set_user_key2(&mut self, key: DataType<32>) {
        self.user_key2 = key;
    }
}

// --- Sub-Image Header ---
// Layout
//          +---+---+---+---+----+----+----+---+----------------------+-----------------+-----------------+------------------+----+----+----+----+
//          | 0 | 1 | 2 | 3 | 4  | 5  | 6  | 7 | 8                    | 9               | 10              | 11               | 12 | 13 | 14 | 15 |
// +========+===+===+===+===+====+====+====+===+======================+=================+=================+==================+====+====+====+====+
// | 0x00   |  length: u32  | next_offset: u32 |     sec_type: u8     | sce_enabled: u8 | xip_pg_size: u8 | xip_sec_size: u8 |                   |
// +--------+---------------+------------------+----------------------+-----------------+-----------------+------------------+-------------------+
// | 0x10   |        validpat: bytes[8]        | xip_key_iv_valid: u8 |                                                                          |
// +--------+----------------------------------+----------------------+--------------------------------------------------------------------------+
// | 0x20   |                                                         xip_key: bytes[16]                                                         |
// +--------+------------------------------------------------------------------------------------------------------------------------------------+
// | 0x30   |                                                         xip_iv: bytes[16]                                                          |
// +--------+------------------------------------------------------------------------------------------------------------------------------------+
// | 0x40   |                                                        alignment: bytes[32]                                                        |
// +--------+------------------------------------------------------------------------------------------------------------------------------------+
// size: 0x60 = 96 bytes

/// Represents the header of a section in a binary image.
///
/// The `SectionHeader` struct contains information about the section's length, type,
/// offset to the next section, as well as details about encryption, remapping, and
/// validation. The struct is designed to handle sections in a memory or file layout
/// where the sections could represent different types of data, such as executable code
/// (XIP), memory regions (DTCM, ITCM), and more. Fields like `xip_page_size`, `xip_key`,
/// and `xip_iv` are especially relevant when dealing with Execute-In-Place (XIP) sections.
#[derive(Debug)]
pub struct SectionHeader {
    /// The length of the section in bytes.
    ///
    /// This field stores the total size of the section, which may include data and metadata.
    /// The exact meaning and content depend on the section type (`sect_type`).
    pub length: u32,

    /// Offset to the next section.
    ///
    /// This field indicates the position of the next section in memory. A value of `0xFFFF_FFFF`
    /// indicates that this is the last section, and no further sections exist.
    pub next_offset: Option<u32>,

    /// The type of the current section.
    pub sect_type: SectionType,

    /// Indicates whether Secure Copy Engine (SCE) is enabled for this section.
    pub sce_enabled: bool,

    /// XIP (Execute-In-Place) page size and remapping setting.
    ///
    /// This field indicates the page size used for remapping during XIP operations. The value is an
    /// integer representing one of three possible values: 0 (16K), 1 (32K), and 2 (64K).
    pub xip_page_size: XipPageRemapSize,

    /// Block size for XIP remapping.
    ///
    /// This field defines the block size used for XIP remapping, typically represented in bytes.
    /// The default value is `0`, which means that remapping block size is not defined.
    pub xip_block_size: u8,

    /// A valid pattern used to verify the integrity of the section header.
    valid_pattern: [u8; 8],

    /// The encryption key used for XIP operations.
    ///
    /// This is a 16-byte key used during the XIP process. If encryption is enabled for the section,
    /// this key is used for decryption. The default value is an array of `0xFF` bytes, indicating an
    /// invalid key by default.
    xip_key: DataType<16>,

    /// The initialization vector (IV) used for XIP encryption.
    ///
    /// This is a 16-byte initialization vector (IV) used in conjunction with the `xip_key` during XIP
    /// encryption operations. As with the key, it is initialized to an invalid value of `0xFF` bytes.
    xip_iv: DataType<16>,
}

impl BinarySize for SectionHeader {
    /// Returns the binary size (in bytes) of the `SectionHeader` struct.
    ///
    /// # Returns
    /// Returns the binary size as a constant `usize` value, which is `0x60` (96 bytes).
    #[inline]
    fn binary_size() -> usize {
        return 0x60;
    }
}

impl Default for SectionHeader {
    /// Returns the default `SectionHeader` instance with predefined values.
    ///
    /// - `length` set to `0`, representing an empty section.
    /// - `next_offset` set to `0xFFFF_FFFF`, which indicates the absence of a next section.
    /// - `sect_type` set to `SectionType::XIP`, as a default section type.
    /// - `sce_enabled` set to `false` (assuming encryption is not enabled by default).
    /// - `xip_page_size` set to `XipPageRemapSize::_16K`, the smallest page size for XIP remapping.
    /// - `xip_block_size` set to `0`, indicating no block size set.
    /// - `valid_pattern` set to a default validation pattern of increasing values.
    /// - `xip_key` and `xip_iv` both set to `0xFF`, representing uninitialized keys/IV.
    ///
    /// # Returns
    /// Returns a `SectionHeader` with the default values.
    fn default() -> SectionHeader {
        return SectionHeader {
            length: 0,
            next_offset: None, // make last as default
            sect_type: SectionType::XIP,
            sce_enabled: false, // encrypted currently not supported
            xip_page_size: XipPageRemapSize::_16K,
            xip_block_size: 0,
            valid_pattern: [0, 1, 2, 3, 4, 5, 6, 7],
            xip_key: None,
            xip_iv: None,
        };
    }
}

impl FromStream for SectionHeader {
    /// Reads the `SectionHeader` struct from a stream.
    ///
    /// # Error Handling
    /// This method may return an error if there is an issue with reading from the stream or if
    /// an invalid value is encountered during deserialization (e.g., an invalid enum value for `sect_type`
    /// or `xip_page_size`). Any errors encountered during reading from the stream will be propagated as an `Error`.
    ///
    /// # Returns
    /// - `Ok(())` if the `SectionHeader` was successfully read from the stream.
    /// - `Err(Error)` if there was an issue reading from the stream or an invalid value was encountered.
    ///
    /// # Panics
    /// This function assumes that the binary data is well-formed and follows the expected format. If
    /// the format is incorrect or the stream is malformed, this function will return an error instead of
    /// panicking. However, it is still important to handle errors appropriately in calling code.
    fn read_from<R>(&mut self, reader: &mut R) -> Result<(), Error>
    where
        R: std::io::Read + std::io::Seek,
    {
        self.length = reader.read_u32::<LittleEndian>()?;
        self.next_offset = match reader.read_u32::<LittleEndian>()? {
            0xFFFF_FFFF => None,
            offset => Some(offset),
        };
        self.sect_type = SectionType::try_from(reader.read_u8()?)?;
        self.sce_enabled = reader.read_u8()? != 0;
        self.xip_page_size = XipPageRemapSize::try_from(reader.read_u8()?)?;
        self.xip_block_size = reader.read_u8()?;

        read_padding!(reader, 4);
        reader.read_exact(&mut self.valid_pattern)?;

        let sce_key_iv_valid = reader.read_u8()? & 0b01 == 1;
        read_padding!(reader, 7);

        if sce_key_iv_valid {
            read_valid_data!(self.xip_key, 16, reader);
            read_valid_data!(self.xip_iv, 16, reader);
            // Align to 96 bytes (by skipping 32 bytes)
            read_padding!(reader, 32);
        } else {
            self.xip_key = None;
            self.xip_iv = None;
            // Align to 96 bytes by skipping 64 bytes if no key/IV is present
            read_padding!(reader, 64);
        }
        Ok(())
    }
}

impl ToStream for SectionHeader {
    /// Serializes the `SectionHeader` struct to a stream.
    ///
    /// # Parameters
    /// - `writer`: A mutable reference to a type that implements the `std::io::Write` trait. The data will
    ///   be written to this stream.
    ///
    /// # Returns
    /// - `Ok(())`: If the data is written successfully.
    /// - `Err(Error)`: If there is an issue writing the data to the stream.
    fn write_to<W>(&self, writer: &mut W) -> Result<(), Error>
    where
        W: io::Write + io::Seek,
    {
        writer.write_u32::<LittleEndian>(self.length)?;
        writer.write_u32::<LittleEndian>(self.next_offset.unwrap_or(0xFFFF_FFFF))?;
        writer.write_u8(self.sect_type as u8)?;
        writer.write_u8(self.sce_enabled as u8)?;
        writer.write_u8(self.xip_page_size as u8)?;
        writer.write_u8(self.xip_block_size)?;

        write_padding!(writer, 4);
        writer.write_all(&self.valid_pattern)?;
        writer.write_u8(self.xip_key_iv_valid() as u8)?;
        write_padding!(writer, 7);

        write_data!(writer, self.xip_key, 16);
        write_data!(writer, self.xip_iv, 16);
        write_padding!(writer, 32);
        Ok(())
    }
}

impl SectionHeader {
    // ------------------------------------------------------------------------------------
    // instance methods
    // ------------------------------------------------------------------------------------

    /// Checks if the section has a next section.
    ///
    /// This method checks if the `next_offset` field of the `SectionHeader` is set to the special
    /// value `0xFFFF_FFFF`, which indicates that there is no next section. If `next_offset` is
    /// different from this value, it implies that there is a subsequent section, and the method
    /// returns `true`. Otherwise, it returns `false`, indicating this is the last section.
    ///
    /// # Returns
    /// - `true` if the section has a next section (i.e., `next_offset` is not `0xFFFF_FFFF`).
    /// - `false` if this is the last section (i.e., `next_offset` is `0xFFFF_FFFF`).
    pub fn has_next(&self) -> bool {
        return self.next_offset.is_some();
    }

    /// Checks if both the `xip_key` and `xip_iv` fields are valid.
    ///
    /// This method uses the `is_valid_data!` macro to check the validity of both the `xip_key`
    /// and `xip_iv` fields. It returns `true` if both fields are valid (i.e., they do not consist
    /// entirely of `0xFF` bytes). If either field is invalid, it returns `false`.
    ///
    /// This method is typically used to determine if the section is encrypted and can be used
    /// for cryptographic operations.
    ///
    /// # Returns
    /// - `true` if both `xip_key` and `xip_iv` are valid.
    /// - `false` if either `xip_key` or `xip_iv` is invalid.
    pub fn xip_key_iv_valid(&self) -> bool {
        match (&self.xip_key, &self.xip_iv) {
            (Some(xip_key), Some(xip_iv)) => {
                return is_valid_data!(xip_key) && is_valid_data!(xip_iv);
            }
            _ => return false,
        }
    }

    /// Retrieves the XIP key.
    pub fn get_xip_key(&self) -> DataRefType<16> {
        self.xip_key.as_ref()
    }

    /// Retrieves the XIP IV.
    pub fn get_xip_iv(&self) -> DataRefType<16> {
        self.xip_iv.as_ref()
    }

    /// Retrieves the valid pattern.
    pub fn get_valid_pattern(&self) -> &[u8; 8] {
        &self.valid_pattern
    }

    pub fn set_xip_iv(&mut self, iv: DataType<16>) {
        self.xip_iv = iv;
    }

    pub fn set_xip_key(&mut self, key: DataType<16>) {
        self.xip_key = key;
    }
}

// -- entry header --
// Layout recovered from
//  - entry_header_t *_create_entry_header(uint length, uint load_address, uint entry_address)
//
//          +---+---+---+---+----+----+----+----+----+----+-----+----+----+----+----+----+
//          | 0 | 1 | 2 | 3 | 4  | 5  | 6  | 7  | 8  | 9  | 10  | 11 | 12 | 13 | 14 | 15 |
// +========+===+===+===+===+====+====+====+====+====+====+=====+====+====+====+====+====+
// | 0x00   |  length: u32  | load_address: u32 | entry_address: u32 |                   |
// +--------+---------------+-------------------+--------------------+-------------------+
// | 0x10   |                                                                            |
// +--------+----------------------------------------------------------------------------+
// size = 0x20

/// `EntryHeader` represents the header of a specific entry within a binary image.
/// It includes metadata about the entry, such as its length, load address, and entry point.
///
/// This structure is used to describe a segment or block of data that is loaded into memory
/// at a specified address and contains an entry point that the system can use to jump to
/// the start of execution.
///
/// Fields:
/// - `length`: The length of the entry in bytes. This defines how much memory the entry occupies.
/// - `load_address`: The memory address at which the entry is loaded into the system's memory space.
/// - `entry_address`: The address to which control is transferred when the entry is executed.
///   By default, it's set to `0xFFFF_FFFF` (None), which indicates an invalid entry address.
#[derive(Debug)]
pub struct EntryHeader {
    /// The length of the entry in bytes.
    pub length: u32,

    /// The load address in memory where the entry will be loaded.
    pub load_address: u32,

    /// The entry address, the address to which the system will jump to start execution.
    /// Defaults to `0xFFFF_FFFF` (None), indicating an invalid address.
    pub entry_address: Option<u32>,
}

impl BinarySize for EntryHeader {
    /// Returns the binary size of the `EntryHeader` struct in bytes.
    ///
    /// # Returns
    /// - `0x20`: The size of the `EntryHeader` struct in bytes.
    fn binary_size() -> usize {
        return 0x20;
    }
}

impl Default for EntryHeader {
    /// Returns the default values for the `EntryHeader` struct.
    ///
    /// The default values are:
    /// - `length`: `0` (indicating no data or length of the entry).
    /// - `load_address`: `0` (indicating the entry is not loaded anywhere).
    /// - `entry_address`: `0xFFFF_FFFF` (None) (invalid entry address, typically indicating no valid entry).
    ///
    /// # Returns
    /// - `EntryHeader`: A struct with default values.
    fn default() -> EntryHeader {
        return EntryHeader {
            length: 0,
            load_address: 0,
            entry_address: None,
        };
    }
}

impl FromStream for EntryHeader {
    /// Reads an `EntryHeader` from the provided reader (e.g., a file or memory buffer).
    ///
    /// # Arguments
    /// - `reader`: A mutable reference to an object that implements `Read` and `Seek` (e.g., a file or buffer).
    ///
    /// # Returns
    /// - `Ok(())`: If the deserialization is successful.
    /// - `Err(Error)`: If there is an error reading from the stream, such as an unexpected end of data.
    fn read_from<R>(&mut self, reader: &mut R) -> Result<(), Error>
    where
        R: std::io::Read + std::io::Seek,
    {
        self.length = reader.read_u32::<LittleEndian>()?;
        self.load_address = reader.read_u32::<LittleEndian>()?;
        self.entry_address = match reader.read_u32::<LittleEndian>()? {
            0xFFFF_FFFF => None,
            address => Some(address),
        };
        read_padding!(reader, 20);
        Ok(())
    }
}

impl ToStream for EntryHeader {
    /// Serializes the `EntryHeader` struct to a stream.
    ///
    /// # Parameters
    /// - `writer`: A mutable reference to a type that implements the `std::io::Write` trait. The data will
    ///   be written to this stream.
    ///
    /// # Returns
    /// - `Ok(())`: If the data is written successfully.
    /// - `Err(Error)`: If there is an issue writing the data to the stream.
    fn write_to<W>(&self, writer: &mut W) -> Result<(), Error>
    where
        W: io::Write + io::Seek,
    {
        writer.write_u32::<LittleEndian>(self.length)?;
        writer.write_u32::<LittleEndian>(self.load_address)?;
        writer.write_u32::<LittleEndian>(self.entry_address.unwrap_or(0xFFFF_FFFF))?;
        write_padding!(writer, 20);
        Ok(())
    }
}