streaming_crypto/core_api/headers/encode.rs
1// ## 📂 File: `src/headers/encode.rs`
2//! src/headers/encode.rs
3//!
4//! Header encoding utilities.
5//!
6//! Design notes:
7//! - Serializes `HeaderV1` into a fixed 80‑byte buffer in little‑endian order.
8//! - Field order must match `types.rs` layout exactly for ABI stability.
9//! - Validation is performed before encoding to fail fast on invalid headers.
10//! - Returns a fixed `[u8; HEADER_LEN_V1]` buffer on success.
11
12use crate::headers::types::{HeaderV1, HeaderError};
13
14/// Serialize a `HeaderV1` into an 80‑byte buffer in little‑endian order.
15///
16/// # Returns
17/// - `Ok([u8; HEADER_LEN_V1])` containing the encoded header bytes.
18/// - `Err(HeaderError)` if validation fails (bad magic, zero salt, invalid enums, etc.).
19///
20/// # Notes
21/// - Field order must match the struct layout in `types.rs`.
22/// - Uses helper functions for compact little‑endian writes.
23/// - Debug assertion ensures exactly 80 bytes are written.
24#[inline]
25pub fn encode_header_le(h: &HeaderV1) -> Result<[u8; HeaderV1::LEN], HeaderError> {
26 // Validate header fields before encoding.
27 h.validate()?;
28
29 // Fixed output buffer of 80 bytes.
30 let mut out = [0u8; HeaderV1::LEN];
31 // Write cursor index.
32 let mut i = 0usize;
33
34 // Helper functions for little‑endian writes.
35 fn put_u16(out: &mut [u8], i: &mut usize, v: u16) {
36 out[*i..*i + 2].copy_from_slice(&v.to_le_bytes());
37 *i += 2;
38 }
39 fn put_u32(out: &mut [u8], i: &mut usize, v: u32) {
40 out[*i..*i + 4].copy_from_slice(&v.to_le_bytes());
41 *i += 4;
42 }
43 fn put_u64(out: &mut [u8], i: &mut usize, v: u64) {
44 out[*i..*i + 8].copy_from_slice(&v.to_le_bytes());
45 *i += 8;
46 }
47 fn put_bytes(out: &mut [u8], i: &mut usize, b: &[u8]) {
48 out[*i..*i + b.len()].copy_from_slice(b);
49 *i += b.len();
50 }
51
52 // Field order must match HeaderV1 layout and documentation.
53 // Write everything up to plaintext_size (offset 0..32)
54 put_bytes(&mut out, &mut i, &h.magic); // 0..4 magic number
55 put_u16(&mut out, &mut i, h.version); // 4..6 version
56 put_u16(&mut out, &mut i, h.alg_profile); // 6..8 algorithm profile
57 put_u16(&mut out, &mut i, h.cipher); // 8..10 cipher suite
58 put_u16(&mut out, &mut i, h.hkdf_prf); // 10..12 HKDF PRF
59 put_u16(&mut out, &mut i, h.compression); // 12..14 compression codec
60 put_u16(&mut out, &mut i, h.strategy); // 14..16 strategy
61 put_u16(&mut out, &mut i, h.aad_domain); // 16..18 AAD domain
62 put_u16(&mut out, &mut i, h.flags); // 18..20 flags bitmask
63 put_u32(&mut out, &mut i, h.chunk_size); // 20..24 chunk size
64 put_u64(&mut out, &mut i, h.plaintext_size); // 24..32 total plaintext size
65
66 // Compute CRC32 over the first 32 bytes
67 let computed_crc = crc32fast::hash(&out[0..32]);
68 // write computed CRC directly, instead of h.crc32
69
70 // Now write crc32 and the rest
71 put_u32(&mut out, &mut i, computed_crc); // 32..36 CRC32 checksum
72 put_u32(&mut out, &mut i, h.dict_id); // 36..40 dictionary ID
73 put_bytes(&mut out, &mut i, &h.salt); // 40..56 salt (16 bytes)
74 put_u32(&mut out, &mut i, h.key_id); // 56..60 key identifier
75 put_u32(&mut out, &mut i, h.parallel_hint); // 60..64 parallelization hint
76 put_u64(&mut out, &mut i, h.enc_time_ns); // 64..72 encryption timestamp (ns)
77 put_bytes(&mut out, &mut i, &h.reserved); // 72..80 reserved bytes
78
79 // Sanity check: ensure we wrote exactly HEADER_LEN_V1 bytes.
80 debug_assert_eq!(i, HeaderV1::LEN, "encoding wrote incorrect length");
81
82 Ok(out)
83}