lllv_core/
capsule.rs

1//! Capsule struct plus signing/verification helpers for the LLLV binary format.
2#[cfg(not(feature = "std"))]
3use alloc::vec::Vec;
4#[cfg(feature = "std")]
5use std::vec::Vec;
6
7use crate::{
8    errors::LllvError,
9    header::{CapsuleFlags, CapsuleHeader},
10    version::HEADER_LEN,
11};
12use blake3::hash;
13use ed25519_dalek::{Signature, Signer, SigningKey, VerifyingKey};
14
15#[derive(Clone, Debug)]
16pub struct Capsule {
17    pub header: CapsuleHeader,
18    pub payload: Vec<u8>,
19}
20
21impl Capsule {
22    /// Cria uma cápsula assinada.
23    ///
24    /// # Errors
25    ///
26    /// - `LllvError::TimestampOverflow` se o timestamp não couber em `u64`
27    /// - `LllvError::MismatchedLengths` se o payload exceder `u32::MAX`
28    /// - `LllvError::Crypto` se a assinatura falhar
29    pub fn create(
30        dim: u16,
31        payload: &[u8],
32        flags: CapsuleFlags,
33        sk: &SigningKey,
34    ) -> Result<Self, LllvError> {
35        let cid = *hash(payload).as_bytes();
36        #[cfg(feature = "std")]
37        let ts_ms = u64::try_from(
38            std::time::SystemTime::now()
39                .duration_since(std::time::UNIX_EPOCH)
40                .map_err(|_| LllvError::TimestampOverflow)?
41                .as_millis(),
42        )
43        .map_err(|_| LllvError::TimestampOverflow)?;
44        #[cfg(not(feature = "std"))]
45        let ts_ms = 0u64; // TODO: usar clock externo em no_std
46        let len = u32::try_from(payload.len()).map_err(|_| LllvError::MismatchedLengths)?;
47        let mut header = CapsuleHeader::empty(dim, flags, cid, len, ts_ms);
48
49        // sign(header_without_sig || payload)
50        let mut to_sign = Vec::with_capacity(HEADER_LEN - 64 + payload.len());
51        to_sign.extend_from_slice(&header.to_bytes_wo_sig());
52        to_sign.extend_from_slice(payload);
53
54        let sig: Signature = sk.sign(&to_sign);
55        header.sig.copy_from_slice(&sig.to_bytes());
56
57        Ok(Self {
58            header,
59            payload: payload.to_vec(),
60        })
61    }
62
63    #[must_use]
64    pub fn to_bytes(&self) -> Vec<u8> {
65        let mut v = Vec::with_capacity(HEADER_LEN + self.payload.len());
66        v.extend_from_slice(&self.header.to_bytes());
67        v.extend_from_slice(&self.payload);
68        v
69    }
70
71    /// # Errors
72    ///
73    /// - `LllvError::InvalidHeaderLen` se o buffer for curto
74    /// - `LllvError::MismatchedLengths` se o payload não corresponder ao header
75    pub fn from_bytes(raw: &[u8]) -> Result<Self, LllvError> {
76        let header = CapsuleHeader::from_bytes(raw)?;
77        let payload = raw[HEADER_LEN..].to_vec();
78        if payload.len() != header.len as usize {
79            return Err(LllvError::MismatchedLengths);
80        }
81        Ok(Self { header, payload })
82    }
83
84    /// Verifica **integridade** (CID cobre payload). Não verifica autoria.
85    /// # Errors
86    ///
87    /// - `LllvError::BadSignature` se o CID recalculado divergir
88    pub fn verify_cid(&self) -> Result<(), LllvError> {
89        let cid_now = *hash(&self.payload).as_bytes();
90        if cid_now != self.header.cid {
91            return Err(LllvError::BadSignature);
92        }
93        Ok(())
94    }
95
96    /// Verifica **integridade e autenticidade** com a chave pública.
97    ///
98    /// # Errors
99    ///
100    /// - Propaga erros de `verify_cid`
101    /// - `LllvError::BadSignature` se a verificação Ed25519 falhar
102    pub fn verify_with(&self, pk: &VerifyingKey) -> Result<(), LllvError> {
103        self.verify_cid()?;
104        let mut to_verify = Vec::with_capacity(HEADER_LEN - 64 + self.payload.len());
105        to_verify.extend_from_slice(&self.header.to_bytes_wo_sig());
106        to_verify.extend_from_slice(&self.payload);
107
108        let sig = ed25519_dalek::Signature::from_bytes(&self.header.sig);
109        pk.verify_strict(&to_verify, &sig)
110            .map_err(|_| LllvError::BadSignature)
111    }
112
113    #[deprecated(
114        since = "0.1.0",
115        note = "use verify_with(&pk) para autenticidade ou verify_cid() para integridade"
116    )]
117    /// Verifica integridade somente (equivalente a `verify_cid`).
118    ///
119    /// # Errors
120    ///
121    /// - Propaga os mesmos erros de `verify_cid`
122    pub fn verify(&self) -> Result<(), LllvError> {
123        self.verify_cid()
124    }
125}