lllv_core/
header.rs

1//! Capsule header layout, serialization, and parsing utilities.
2use crate::{
3    errors::LllvError,
4    version::{CAP_MAGIC, CAP_VER, HEADER_LEN},
5};
6
7#[derive(Copy, Clone, Debug, PartialEq, Eq)]
8pub struct CapsuleHeader {
9    pub magic: u16,    // 2
10    pub ver: u8,       // 1
11    pub flags: u8,     // 1
12    pub ts_ms: u64,    // 8
13    pub cid: [u8; 32], // 32 (blake3(payload))
14    pub dim: u16,      // 2
15    pub len: u32,      // 4 (payload bytes)
16    pub sig: [u8; 64], // 64 (ed25519(header_wo_sig||payload))
17}
18
19bitflags::bitflags! {
20    #[derive(Default, Copy, Clone)]
21    pub struct CapsuleFlags: u8 {
22        const NONE       = 0b0000_0000;
23        const ENCRYPTED  = 0b0000_0001; // payload = nonce(12) || ciphertext
24        const RECEIPTREQ = 0b0000_0010; // reserva
25    }
26}
27
28impl CapsuleHeader {
29    #[must_use]
30    pub const fn empty(dim: u16, flags: CapsuleFlags, cid: [u8; 32], len: u32, ts_ms: u64) -> Self {
31        Self {
32            magic: CAP_MAGIC,
33            ver: CAP_VER,
34            flags: flags.bits(),
35            ts_ms,
36            cid,
37            dim,
38            len,
39            sig: [0u8; 64],
40        }
41    }
42
43    #[must_use]
44    pub fn to_bytes_wo_sig(&self) -> [u8; HEADER_LEN - 64] {
45        let mut out = [0u8; HEADER_LEN - 64];
46        let mut i = 0usize;
47        out[i..=i + 1].copy_from_slice(&self.magic.to_le_bytes());
48        i += 2;
49        out[i..=i].copy_from_slice(&[self.ver]);
50        i += 1;
51        out[i..=i].copy_from_slice(&[self.flags]);
52        i += 1;
53        out[i..=i + 7].copy_from_slice(&self.ts_ms.to_le_bytes());
54        i += 8;
55        out[i..=i + 31].copy_from_slice(&self.cid);
56        i += 32;
57        out[i..=i + 1].copy_from_slice(&self.dim.to_le_bytes());
58        i += 2;
59        out[i..=i + 3].copy_from_slice(&self.len.to_le_bytes());
60        i += 4;
61        debug_assert_eq!(i, HEADER_LEN - 64);
62        out
63    }
64
65    #[must_use]
66    pub fn to_bytes(&self) -> [u8; HEADER_LEN] {
67        let mut out = [0u8; HEADER_LEN];
68        let (head, tail) = out.split_at_mut(HEADER_LEN - 64);
69        head.copy_from_slice(&self.to_bytes_wo_sig());
70        tail.copy_from_slice(&self.sig);
71        out
72    }
73
74    /// Parse header from raw bytes.
75    ///
76    /// # Errors
77    ///
78    /// - `LllvError::InvalidHeaderLen` se o buffer for menor que `HEADER_LEN`
79    /// - `LllvError::InvalidMagic` ou `InvalidVersion` se os campos não baterem
80    pub fn from_bytes(raw: &[u8]) -> Result<Self, LllvError> {
81        if raw.len() < HEADER_LEN {
82            return Err(LllvError::InvalidHeaderLen);
83        }
84        let mut i = 0usize;
85        let magic = u16::from_le_bytes(
86            raw[i..i + 2]
87                .try_into()
88                .map_err(|_| LllvError::InvalidHeaderLen)?,
89        );
90        i += 2;
91        let ver = raw[i];
92        i += 1;
93        let flags = raw[i];
94        i += 1;
95        let ts_ms = u64::from_le_bytes(
96            raw[i..i + 8]
97                .try_into()
98                .map_err(|_| LllvError::InvalidHeaderLen)?,
99        );
100        i += 8;
101        let mut cid = [0u8; 32];
102        cid.copy_from_slice(&raw[i..i + 32]);
103        i += 32;
104        let dim = u16::from_le_bytes(
105            raw[i..i + 2]
106                .try_into()
107                .map_err(|_| LllvError::InvalidHeaderLen)?,
108        );
109        i += 2;
110        let len = u32::from_le_bytes(
111            raw[i..i + 4]
112                .try_into()
113                .map_err(|_| LllvError::InvalidHeaderLen)?,
114        );
115        i += 4;
116        let mut sig = [0u8; 64];
117        sig.copy_from_slice(&raw[i..i + 64]);
118
119        if magic != CAP_MAGIC {
120            return Err(LllvError::InvalidMagic);
121        }
122        if ver != CAP_VER {
123            return Err(LllvError::InvalidVersion);
124        }
125
126        Ok(Self {
127            magic,
128            ver,
129            flags,
130            ts_ms,
131            cid,
132            dim,
133            len,
134            sig,
135        })
136    }
137}