Skip to main content

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}