miden_crypto/aead/xchacha/
mod.rs1use alloc::{string::ToString, vec::Vec};
14
15use chacha20poly1305::{
16 XChaCha20Poly1305,
17 aead::{Aead, KeyInit, Payload},
18};
19use rand::CryptoRng;
20#[cfg(any(test, feature = "testing"))]
21use subtle::ConstantTimeEq;
22
23use crate::{
24 Felt,
25 aead::{AeadScheme, DataType, EncryptionError},
26 utils::{
27 ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
28 bytes_to_elements_exact, elements_to_bytes,
29 zeroize::{Zeroize, ZeroizeOnDrop},
30 },
31};
32
33#[cfg(test)]
34mod test;
35
36const NONCE_SIZE_BYTES: usize = 24;
41const SK_SIZE_BYTES: usize = 32;
43
44#[derive(Debug, PartialEq, Eq)]
49pub struct EncryptedData {
50 data_type: DataType,
52 ciphertext: Vec<u8>,
54 nonce: Nonce,
56}
57
58#[derive(Clone, Debug, PartialEq, Eq)]
62pub struct Nonce {
63 inner: chacha20poly1305::XNonce,
64}
65
66impl Nonce {
67 pub fn with_rng<R: CryptoRng>(rng: &mut R) -> Self {
69 let mut bytes = [0u8; NONCE_SIZE_BYTES];
70 rng.fill_bytes(&mut bytes);
71 Self::from_slice(&bytes)
72 }
73
74 pub fn from_slice(bytes: &[u8; NONCE_SIZE_BYTES]) -> Self {
76 Nonce { inner: (*bytes).into() }
77 }
78}
79
80#[derive(Debug)]
82pub struct SecretKey([u8; SK_SIZE_BYTES]);
83
84#[cfg(any(test, feature = "testing"))]
85impl PartialEq for SecretKey {
86 fn eq(&self, other: &Self) -> bool {
87 bool::from(self.0.ct_eq(&other.0))
89 }
90}
91
92#[cfg(any(test, feature = "testing"))]
93impl Eq for SecretKey {}
94
95impl SecretKey {
96 #[cfg(feature = "std")]
101 #[allow(clippy::new_without_default)]
102 pub fn new() -> Self {
103 let mut rng = rand::rng();
104 Self::with_rng(&mut rng)
105 }
106
107 pub fn with_rng<R: CryptoRng>(rng: &mut R) -> Self {
109 let mut key = [0u8; SK_SIZE_BYTES];
110 rng.fill_bytes(&mut key);
111 Self(key)
112 }
113
114 #[cfg(feature = "std")]
120 pub fn encrypt_bytes(&self, data: &[u8]) -> Result<EncryptedData, EncryptionError> {
121 self.encrypt_bytes_with_associated_data(data, &[])
122 }
123
124 #[cfg(feature = "std")]
127 pub fn encrypt_bytes_with_associated_data(
128 &self,
129 data: &[u8],
130 associated_data: &[u8],
131 ) -> Result<EncryptedData, EncryptionError> {
132 let mut rng = rand::rng();
133 let nonce = Nonce::with_rng(&mut rng);
134
135 self.encrypt_bytes_with_nonce(data, associated_data, nonce)
136 }
137
138 pub fn encrypt_bytes_with_nonce(
140 &self,
141 data: &[u8],
142 associated_data: &[u8],
143 nonce: Nonce,
144 ) -> Result<EncryptedData, EncryptionError> {
145 let payload = Payload { msg: data, aad: associated_data };
146
147 let cipher = XChaCha20Poly1305::new(&self.0.into());
148
149 let ciphertext = cipher
150 .encrypt(&nonce.inner, payload)
151 .map_err(|_| EncryptionError::FailedOperation)?;
152
153 Ok(EncryptedData {
154 data_type: DataType::Bytes,
155 ciphertext,
156 nonce,
157 })
158 }
159
160 #[cfg(feature = "std")]
166 pub fn encrypt_elements(&self, data: &[Felt]) -> Result<EncryptedData, EncryptionError> {
167 self.encrypt_elements_with_associated_data(data, &[])
168 }
169
170 #[cfg(feature = "std")]
173 pub fn encrypt_elements_with_associated_data(
174 &self,
175 data: &[Felt],
176 associated_data: &[Felt],
177 ) -> Result<EncryptedData, EncryptionError> {
178 let mut rng = rand::rng();
179 let nonce = Nonce::with_rng(&mut rng);
180
181 self.encrypt_elements_with_nonce(data, associated_data, nonce)
182 }
183
184 pub fn encrypt_elements_with_nonce(
187 &self,
188 data: &[Felt],
189 associated_data: &[Felt],
190 nonce: Nonce,
191 ) -> Result<EncryptedData, EncryptionError> {
192 let data_bytes = elements_to_bytes(data);
193 let ad_bytes = elements_to_bytes(associated_data);
194
195 let mut encrypted_data = self.encrypt_bytes_with_nonce(&data_bytes, &ad_bytes, nonce)?;
196 encrypted_data.data_type = DataType::Elements;
197 Ok(encrypted_data)
198 }
199
200 pub fn decrypt_bytes(
209 &self,
210 encrypted_data: &EncryptedData,
211 ) -> Result<Vec<u8>, EncryptionError> {
212 self.decrypt_bytes_with_associated_data(encrypted_data, &[])
213 }
214
215 pub fn decrypt_bytes_with_associated_data(
221 &self,
222 encrypted_data: &EncryptedData,
223 associated_data: &[u8],
224 ) -> Result<Vec<u8>, EncryptionError> {
225 if encrypted_data.data_type != DataType::Bytes {
226 return Err(EncryptionError::InvalidDataType {
227 expected: DataType::Bytes,
228 found: encrypted_data.data_type,
229 });
230 }
231 self.decrypt_bytes_with_associated_data_unchecked(encrypted_data, associated_data)
232 }
233
234 fn decrypt_bytes_with_associated_data_unchecked(
236 &self,
237 encrypted_data: &EncryptedData,
238 associated_data: &[u8],
239 ) -> Result<Vec<u8>, EncryptionError> {
240 let EncryptedData { ciphertext, nonce, data_type: _ } = encrypted_data;
241 let payload = Payload { msg: ciphertext, aad: associated_data };
242
243 let cipher = XChaCha20Poly1305::new(&self.0.into());
244
245 cipher
246 .decrypt(&nonce.inner, payload)
247 .map_err(|_| EncryptionError::FailedOperation)
248 }
249
250 pub fn decrypt_elements(
259 &self,
260 encrypted_data: &EncryptedData,
261 ) -> Result<Vec<Felt>, EncryptionError> {
262 self.decrypt_elements_with_associated_data(encrypted_data, &[])
263 }
264
265 pub fn decrypt_elements_with_associated_data(
271 &self,
272 encrypted_data: &EncryptedData,
273 associated_data: &[Felt],
274 ) -> Result<Vec<Felt>, EncryptionError> {
275 if encrypted_data.data_type != DataType::Elements {
276 return Err(EncryptionError::InvalidDataType {
277 expected: DataType::Elements,
278 found: encrypted_data.data_type,
279 });
280 }
281
282 let ad_bytes = elements_to_bytes(associated_data);
283
284 let plaintext_bytes =
285 self.decrypt_bytes_with_associated_data_unchecked(encrypted_data, &ad_bytes)?;
286 match bytes_to_elements_exact(&plaintext_bytes) {
287 Some(elements) => Ok(elements),
288 None => Err(EncryptionError::FailedBytesToElementsConversion),
289 }
290 }
291}
292
293impl AsRef<[u8]> for SecretKey {
294 fn as_ref(&self) -> &[u8] {
295 &self.0
296 }
297}
298
299impl Drop for SecretKey {
300 fn drop(&mut self) {
301 self.zeroize();
302 }
303}
304
305impl Zeroize for SecretKey {
306 fn zeroize(&mut self) {
307 self.0.zeroize();
308 }
309}
310
311impl ZeroizeOnDrop for SecretKey {}
312
313pub struct XChaCha;
317
318impl AeadScheme for XChaCha {
319 const KEY_SIZE: usize = SK_SIZE_BYTES;
320
321 type Key = SecretKey;
322
323 fn key_from_bytes(bytes: &[u8]) -> Result<Self::Key, EncryptionError> {
324 if bytes.len() != SK_SIZE_BYTES {
325 return Err(EncryptionError::FailedOperation);
326 }
327
328 SecretKey::read_from_bytes_with_budget(bytes, SK_SIZE_BYTES)
329 .map_err(|_| EncryptionError::FailedOperation)
330 }
331
332 fn encrypt_bytes<R: CryptoRng>(
333 key: &Self::Key,
334 rng: &mut R,
335 plaintext: &[u8],
336 associated_data: &[u8],
337 ) -> Result<Vec<u8>, EncryptionError> {
338 let nonce = Nonce::with_rng(rng);
339 let encrypted_data = key
340 .encrypt_bytes_with_nonce(plaintext, associated_data, nonce)
341 .map_err(|_| EncryptionError::FailedOperation)?;
342 Ok(encrypted_data.to_bytes())
343 }
344
345 fn decrypt_bytes_with_associated_data(
346 key: &Self::Key,
347 ciphertext: &[u8],
348 associated_data: &[u8],
349 ) -> Result<Vec<u8>, EncryptionError> {
350 let encrypted_data =
351 EncryptedData::read_from_bytes_with_budget(ciphertext, ciphertext.len())
352 .map_err(|_| EncryptionError::FailedOperation)?;
353
354 key.decrypt_bytes_with_associated_data(&encrypted_data, associated_data)
355 .map_err(|_| EncryptionError::FailedOperation)
356 }
357}
358
359impl Serializable for SecretKey {
363 fn write_into<W: ByteWriter>(&self, target: &mut W) {
364 target.write_bytes(&self.0);
365 }
366}
367
368impl Deserializable for SecretKey {
369 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
370 let inner: [u8; SK_SIZE_BYTES] = source.read_array()?;
371
372 Ok(SecretKey(inner))
373 }
374}
375
376impl Serializable for Nonce {
377 fn write_into<W: ByteWriter>(&self, target: &mut W) {
378 target.write_bytes(&self.inner);
379 }
380}
381
382impl Deserializable for Nonce {
383 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
384 let inner: [u8; NONCE_SIZE_BYTES] = source.read_array()?;
385
386 Ok(Nonce { inner: inner.into() })
387 }
388}
389
390impl Serializable for EncryptedData {
391 fn write_into<W: ByteWriter>(&self, target: &mut W) {
392 target.write_u8(self.data_type as u8);
393 target.write_usize(self.ciphertext.len());
394 target.write_bytes(&self.ciphertext);
395 target.write_bytes(&self.nonce.inner);
396 }
397}
398
399impl Deserializable for EncryptedData {
400 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
401 let data_type_value: u8 = source.read_u8()?;
402 let data_type = data_type_value.try_into().map_err(|_| {
403 DeserializationError::InvalidValue("invalid data type value".to_string())
404 })?;
405
406 let ciphertext = Vec::<u8>::read_from(source)?;
407
408 let inner: [u8; NONCE_SIZE_BYTES] = source.read_array()?;
409
410 Ok(Self {
411 ciphertext,
412 nonce: Nonce { inner: inner.into() },
413 data_type,
414 })
415 }
416}