Skip to main content

streaming_crypto/core_api/headers/
decode.rs

1// ## 📂 File: `src/headers/decode.rs`
2//! src/headers/decode.rs
3//!
4//! Header decoding utilities.
5//!
6//! Design notes:
7//! - Deserializes a fixed 80‑byte buffer into a `HeaderV1` struct.
8//! - Field order must match `encode.rs` exactly for ABI stability.
9//! - Validation is performed after decoding to reject malformed or incompatible streams.
10//! - Treat header as authoritative source for strategy and chunk sizing.
11
12use crate::headers::types::{HeaderV1, HeaderError};
13
14/// Deserialize an 80‑byte little‑endian header into `HeaderV1`.
15///
16/// # Returns
17/// - `Ok(HeaderV1)` if decoding and validation succeed.
18/// - `Err(HeaderError)` if buffer length mismatches or validation fails.
19///
20/// # Notes
21/// - Field order must match the struct layout in `encode.rs`.
22/// - Uses helper functions for compact little‑endian reads.
23/// - Debug assertion ensures exactly 80 bytes are consumed.
24#[inline]
25pub fn decode_header_le(buf: &[u8]) -> Result<HeaderV1, HeaderError> {
26    // Ensure buffer has at least HEADER_LEN_V1 bytes.
27    if buf.len() < HeaderV1::LEN {
28        return Err(HeaderError::BufferTooShort { have: buf.len(), need: HeaderV1::LEN });
29    }
30
31    // Cursor helpers
32    let mut i = 0usize;
33    #[inline] fn get_u16(buf: &[u8], i: &mut usize) -> u16 { let v = u16::from_le_bytes(buf[*i..*i+2].try_into().unwrap()); *i += 2; v }
34    #[inline] fn get_u32(buf: &[u8], i: &mut usize) -> u32 { let v = u32::from_le_bytes(buf[*i..*i+4].try_into().unwrap()); *i += 4; v }
35    #[inline] fn get_u64(buf: &[u8], i: &mut usize) -> u64 { let v = u64::from_le_bytes(buf[*i..*i+8].try_into().unwrap()); *i += 8; v }
36    #[inline] fn get_bytes<const N: usize>(buf: &[u8], i: &mut usize) -> [u8; N] {
37        let mut dst = [0u8; N]; dst.copy_from_slice(&buf[*i..*i+N]); *i += N; dst
38    }
39
40    // Initialize header with defaults.
41    let mut h = HeaderV1::default();
42
43    // Field order must match encode.rs layout.
44    h.magic          = get_bytes::<4>(buf, &mut i);   // 0..4   magic number
45    h.version        = get_u16(buf, &mut i);            // 4..6   version
46    h.alg_profile    = get_u16(buf, &mut i);            // 6..8   algorithm profile
47    h.cipher         = get_u16(buf, &mut i);            // 8..10  cipher suite
48    h.hkdf_prf       = get_u16(buf, &mut i);            // 10..12 HKDF PRF
49    h.compression    = get_u16(buf, &mut i);            // 12..14 compression codec
50    h.strategy       = get_u16(buf, &mut i);            // 14..16 strategy
51    h.aad_domain     = get_u16(buf, &mut i);            // 16..18 AAD domain
52    h.flags          = get_u16(buf, &mut i);            // 18..20 flags bitmask
53    h.chunk_size     = get_u32(buf, &mut i);            // 20..24 chunk size
54    h.plaintext_size = get_u64(buf, &mut i);            // 24..32 total plaintext size
55    h.crc32          = get_u32(buf, &mut i);            // 32..36 CRC32 checksum
56    h.dict_id        = get_u32(buf, &mut i);            // 36..40 dictionary ID
57    h.salt           = get_bytes::<16>(buf, &mut i); // 40..56 salt (16 bytes)
58    h.key_id         = get_u32(buf, &mut i);            // 56..60 key identifier
59    h.parallel_hint  = get_u32(buf, &mut i);            // 60..64 parallelization hint
60    h.enc_time_ns    = get_u64(buf, &mut i);            // 64..72 encryption timestamp (ns)
61    h.reserved       = get_bytes::<8>(buf, &mut i);  // 72..80 reserved bytes
62
63    // Sanity check: ensure we consumed exactly HEADER_LEN_V1 bytes.
64    if i != HeaderV1::LEN {
65        return Err(HeaderError::BufferTooShort { have: i, need: HeaderV1::LEN });
66    }
67
68    // ✅ CRC32 validation: compute over first 32 bytes (0..32)
69    let computed_crc = crc32fast::hash(&buf[0..32]);
70    if h.crc32 != computed_crc {
71        return Err(HeaderError::InvalidCrc32 { have: h.crc32 as usize, need: computed_crc as usize });
72    }
73
74    // Validate decoded header fields.
75    h.validate()?;
76
77    Ok(h)
78}