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}