1use chacha20poly1305::aead::{Aead, KeyInit, Payload};
2use chacha20poly1305::{XChaCha20Poly1305, XNonce};
3
4use crate::error::{EnigmaAeadError, EnigmaAeadResult};
5use crate::nonce::NonceGenerator;
6use crate::packet::{Packet, PacketHeader, PacketParts, HEADER_LEN, MAX_PACKET_SIZE, TAG_LEN};
7use crate::types::{AeadKey, KeyId, Nonce24};
8
9fn seal_raw(
10 key_bytes: &[u8; 32],
11 nonce: &[u8; 24],
12 plaintext: &[u8],
13 aad: &[u8],
14) -> EnigmaAeadResult<Vec<u8>> {
15 let cipher =
16 XChaCha20Poly1305::new_from_slice(key_bytes).map_err(|_| EnigmaAeadError::CryptoError)?;
17 let nonce_bytes = XNonce::from_slice(nonce);
18 cipher
19 .encrypt(
20 nonce_bytes,
21 Payload {
22 msg: plaintext,
23 aad,
24 },
25 )
26 .map_err(EnigmaAeadError::from)
27}
28
29fn open_raw(
30 key_bytes: &[u8; 32],
31 nonce: &[u8; 24],
32 ciphertext: &[u8],
33 aad: &[u8],
34) -> EnigmaAeadResult<Vec<u8>> {
35 if ciphertext.len() < TAG_LEN {
36 return Err(EnigmaAeadError::InvalidPacket("ciphertext too short"));
37 }
38 let cipher =
39 XChaCha20Poly1305::new_from_slice(key_bytes).map_err(|_| EnigmaAeadError::CryptoError)?;
40 let nonce_bytes = XNonce::from_slice(nonce);
41 cipher
42 .decrypt(
43 nonce_bytes,
44 Payload {
45 msg: ciphertext,
46 aad,
47 },
48 )
49 .map_err(EnigmaAeadError::from)
50}
51
52pub fn seal(
53 key: [u8; 32],
54 nonce: [u8; 24],
55 plaintext: &[u8],
56 aad: &[u8],
57) -> EnigmaAeadResult<Vec<u8>> {
58 seal_raw(&key, &nonce, plaintext, aad)
59}
60
61pub fn open(
62 key: [u8; 32],
63 nonce: [u8; 24],
64 ciphertext: &[u8],
65 aad: &[u8],
66) -> EnigmaAeadResult<Vec<u8>> {
67 open_raw(&key, &nonce, ciphertext, aad)
68}
69
70pub struct AeadBox {
71 key: AeadKey,
72 key_id: KeyId,
73}
74
75impl AeadBox {
76 pub fn new(key: [u8; 32]) -> Self {
77 Self {
78 key: AeadKey::new(key),
79 key_id: KeyId::zero(),
80 }
81 }
82
83 pub fn with_key_id(key: [u8; 32], key_id: [u8; 8]) -> Self {
84 Self {
85 key: AeadKey::new(key),
86 key_id: KeyId(key_id),
87 }
88 }
89
90 pub fn encrypt(&self, plaintext: &[u8], user_ad: &[u8]) -> EnigmaAeadResult<Vec<u8>> {
91 let nonce = NonceGenerator::random_nonce24();
92 self.encrypt_with_nonce_internal(plaintext, user_ad, nonce)
93 }
94
95 pub fn encrypt_with_nonce(
96 &self,
97 plaintext: &[u8],
98 user_ad: &[u8],
99 nonce: [u8; 24],
100 ) -> EnigmaAeadResult<Vec<u8>> {
101 self.encrypt_with_nonce_internal(plaintext, user_ad, Nonce24::new(nonce))
102 }
103
104 pub fn decrypt(&self, packet: &[u8], user_ad: &[u8]) -> EnigmaAeadResult<Vec<u8>> {
105 let parts = Packet::decode(packet)?;
106 self.decrypt_parts(parts, user_ad)
107 }
108
109 pub fn seal(&self, nonce: [u8; 24], plaintext: &[u8], aad: &[u8]) -> EnigmaAeadResult<Vec<u8>> {
110 seal_raw(self.key.as_bytes(), &nonce, plaintext, aad)
111 }
112
113 pub fn open(
114 &self,
115 nonce: [u8; 24],
116 ciphertext: &[u8],
117 aad: &[u8],
118 ) -> EnigmaAeadResult<Vec<u8>> {
119 open_raw(self.key.as_bytes(), &nonce, ciphertext, aad)
120 }
121
122 pub fn parse_packet(packet: &[u8]) -> EnigmaAeadResult<PacketParts> {
123 Packet::decode(packet)
124 }
125
126 fn encrypt_with_nonce_internal(
127 &self,
128 plaintext: &[u8],
129 user_ad: &[u8],
130 nonce: Nonce24,
131 ) -> EnigmaAeadResult<Vec<u8>> {
132 let projected = HEADER_LEN + plaintext.len() + TAG_LEN;
133 if projected > MAX_PACKET_SIZE {
134 return Err(EnigmaAeadError::SizeLimitExceeded);
135 }
136 let header = PacketHeader::new(self.key_id, nonce);
137 let header_bytes = header.to_bytes();
138 let mut ad = Vec::with_capacity(user_ad.len() + header_bytes.len());
139 ad.extend_from_slice(user_ad);
140 ad.extend_from_slice(&header_bytes);
141 let cipher = XChaCha20Poly1305::new_from_slice(self.key.as_bytes())
142 .map_err(|_| EnigmaAeadError::CryptoError)?;
143 let nonce_bytes = XNonce::from_slice(header.nonce.as_slice());
144 let ciphertext = cipher.encrypt(
145 nonce_bytes,
146 Payload {
147 msg: plaintext,
148 aad: &ad,
149 },
150 )?;
151 Packet::encode(&header, &ciphertext)
152 }
153
154 fn decrypt_parts(&self, parts: PacketParts, user_ad: &[u8]) -> EnigmaAeadResult<Vec<u8>> {
155 let header_bytes = parts.header_bytes();
156 let mut ad = Vec::with_capacity(user_ad.len() + header_bytes.len());
157 ad.extend_from_slice(user_ad);
158 ad.extend_from_slice(&header_bytes);
159 let cipher = XChaCha20Poly1305::new_from_slice(self.key.as_bytes())
160 .map_err(|_| EnigmaAeadError::CryptoError)?;
161 let nonce = XNonce::from_slice(parts.header.nonce.as_slice());
162 cipher
163 .decrypt(
164 nonce,
165 Payload {
166 msg: parts.ciphertext.as_slice(),
167 aad: &ad,
168 },
169 )
170 .map_err(EnigmaAeadError::from)
171 }
172}