enigma_aead/
packet.rs

1use crate::error::{EnigmaAeadError, EnigmaAeadResult};
2use crate::types::{KeyId, Nonce24};
3
4pub const MAGIC: &[u8; 4] = b"ENA1";
5pub const VERSION: u8 = 1;
6pub const ALG_ID_XCHACHA20: u8 = 1;
7pub const TAG_LEN: usize = 16;
8pub const HEADER_META_LEN: usize = 1 + 1 + 1 + 1 + 8 + 24;
9pub const HEADER_LEN: usize = 4 + HEADER_META_LEN;
10pub const MAX_PACKET_SIZE: usize = 16 * 1024 * 1024;
11pub const MIN_PACKET_SIZE: usize = HEADER_LEN + TAG_LEN;
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
14pub struct PacketHeader {
15    pub version: u8,
16    pub alg_id: u8,
17    pub flags: u8,
18    pub reserved: u8,
19    pub key_id: KeyId,
20    pub nonce: Nonce24,
21}
22
23impl PacketHeader {
24    pub fn new(key_id: KeyId, nonce: Nonce24) -> Self {
25        Self {
26            version: VERSION,
27            alg_id: ALG_ID_XCHACHA20,
28            flags: 0,
29            reserved: 0,
30            key_id,
31            nonce,
32        }
33    }
34
35    pub fn to_bytes(&self) -> [u8; HEADER_LEN] {
36        let mut bytes = [0u8; HEADER_LEN];
37        bytes[..4].copy_from_slice(MAGIC);
38        bytes[4] = self.version;
39        bytes[5] = self.alg_id;
40        bytes[6] = self.flags;
41        bytes[7] = self.reserved;
42        bytes[8..16].copy_from_slice(&self.key_id.0);
43        bytes[16..40].copy_from_slice(self.nonce.as_slice());
44        bytes
45    }
46}
47
48#[derive(Clone, Debug, PartialEq, Eq)]
49pub struct PacketParts {
50    pub header: PacketHeader,
51    pub ciphertext: Vec<u8>,
52}
53
54impl PacketParts {
55    pub fn header_bytes(&self) -> [u8; HEADER_LEN] {
56        self.header.to_bytes()
57    }
58}
59
60pub struct Packet;
61
62impl Packet {
63    pub fn encode(header: &PacketHeader, ciphertext: &[u8]) -> EnigmaAeadResult<Vec<u8>> {
64        if ciphertext.len() < TAG_LEN {
65            return Err(EnigmaAeadError::InvalidPacket("ciphertext too short"));
66        }
67        let total = HEADER_LEN + ciphertext.len();
68        if total > MAX_PACKET_SIZE {
69            return Err(EnigmaAeadError::SizeLimitExceeded);
70        }
71        let mut out = Vec::with_capacity(total);
72        out.extend_from_slice(&header.to_bytes());
73        out.extend_from_slice(ciphertext);
74        Ok(out)
75    }
76
77    pub fn decode(packet: &[u8]) -> EnigmaAeadResult<PacketParts> {
78        if packet.len() > MAX_PACKET_SIZE {
79            return Err(EnigmaAeadError::SizeLimitExceeded);
80        }
81        if packet.len() < MIN_PACKET_SIZE {
82            return Err(EnigmaAeadError::InvalidPacket("packet too small"));
83        }
84        if &packet[..4] != MAGIC {
85            return Err(EnigmaAeadError::InvalidPacket("bad magic"));
86        }
87        let version = packet[4];
88        if version != VERSION {
89            return Err(EnigmaAeadError::UnsupportedVersion(version));
90        }
91        let alg_id = packet[5];
92        if alg_id != ALG_ID_XCHACHA20 {
93            return Err(EnigmaAeadError::UnsupportedAlgorithm(alg_id));
94        }
95        let flags = packet[6];
96        let reserved = packet[7];
97        let mut key_id_bytes = [0u8; 8];
98        key_id_bytes.copy_from_slice(&packet[8..16]);
99        let mut nonce_bytes = [0u8; 24];
100        nonce_bytes.copy_from_slice(&packet[16..40]);
101        let ciphertext = packet[HEADER_LEN..].to_vec();
102        if ciphertext.len() < TAG_LEN {
103            return Err(EnigmaAeadError::InvalidPacket("ciphertext too short"));
104        }
105        Ok(PacketParts {
106            header: PacketHeader {
107                version,
108                alg_id,
109                flags,
110                reserved,
111                key_id: KeyId(key_id_bytes),
112                nonce: Nonce24::new(nonce_bytes),
113            },
114            ciphertext,
115        })
116    }
117}