miden_crypto/aead/xchacha/
mod.rs1use alloc::{string::ToString, vec::Vec};
14
15use chacha20poly1305::{
16 XChaCha20Poly1305,
17 aead::{Aead, AeadCore, KeyInit},
18};
19use rand::{CryptoRng, RngCore};
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: RngCore + CryptoRng>(rng: &mut R) -> Self {
69 use chacha20poly1305::aead::rand_core::SeedableRng;
75 let mut seed = [0_u8; 32];
76 rand::RngCore::fill_bytes(rng, &mut seed);
77 let rng = rand_hc::Hc128Rng::from_seed(seed);
78
79 Nonce {
80 inner: XChaCha20Poly1305::generate_nonce(rng),
81 }
82 }
83
84 pub fn from_slice(bytes: &[u8; NONCE_SIZE_BYTES]) -> Self {
86 Nonce { inner: (*bytes).into() }
87 }
88}
89
90#[derive(Debug)]
92pub struct SecretKey([u8; SK_SIZE_BYTES]);
93
94#[cfg(any(test, feature = "testing"))]
95impl PartialEq for SecretKey {
96 fn eq(&self, other: &Self) -> bool {
97 bool::from(self.0.ct_eq(&other.0))
99 }
100}
101
102#[cfg(any(test, feature = "testing"))]
103impl Eq for SecretKey {}
104
105impl SecretKey {
106 #[cfg(feature = "std")]
111 #[allow(clippy::new_without_default)]
112 pub fn new() -> Self {
113 let mut rng = rand::rng();
114 Self::with_rng(&mut rng)
115 }
116
117 pub fn with_rng<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
119 use chacha20poly1305::aead::rand_core::SeedableRng;
125 let mut seed = [0_u8; 32];
126 rand::RngCore::fill_bytes(rng, &mut seed);
127 let rng = rand_hc::Hc128Rng::from_seed(seed);
128
129 let key = XChaCha20Poly1305::generate_key(rng);
130 Self(key.into())
131 }
132
133 #[cfg(feature = "std")]
139 pub fn encrypt_bytes(&self, data: &[u8]) -> Result<EncryptedData, EncryptionError> {
140 self.encrypt_bytes_with_associated_data(data, &[])
141 }
142
143 #[cfg(feature = "std")]
146 pub fn encrypt_bytes_with_associated_data(
147 &self,
148 data: &[u8],
149 associated_data: &[u8],
150 ) -> Result<EncryptedData, EncryptionError> {
151 let mut rng = rand::rng();
152 let nonce = Nonce::with_rng(&mut rng);
153
154 self.encrypt_bytes_with_nonce(data, associated_data, nonce)
155 }
156
157 pub fn encrypt_bytes_with_nonce(
159 &self,
160 data: &[u8],
161 associated_data: &[u8],
162 nonce: Nonce,
163 ) -> Result<EncryptedData, EncryptionError> {
164 let payload = chacha20poly1305::aead::Payload { msg: data, aad: associated_data };
165
166 let cipher = XChaCha20Poly1305::new(&self.0.into());
167
168 let ciphertext = cipher
169 .encrypt(&nonce.inner, payload)
170 .map_err(|_| EncryptionError::FailedOperation)?;
171
172 Ok(EncryptedData {
173 data_type: DataType::Bytes,
174 ciphertext,
175 nonce,
176 })
177 }
178
179 #[cfg(feature = "std")]
185 pub fn encrypt_elements(&self, data: &[Felt]) -> Result<EncryptedData, EncryptionError> {
186 self.encrypt_elements_with_associated_data(data, &[])
187 }
188
189 #[cfg(feature = "std")]
192 pub fn encrypt_elements_with_associated_data(
193 &self,
194 data: &[Felt],
195 associated_data: &[Felt],
196 ) -> Result<EncryptedData, EncryptionError> {
197 let mut rng = rand::rng();
198 let nonce = Nonce::with_rng(&mut rng);
199
200 self.encrypt_elements_with_nonce(data, associated_data, nonce)
201 }
202
203 pub fn encrypt_elements_with_nonce(
206 &self,
207 data: &[Felt],
208 associated_data: &[Felt],
209 nonce: Nonce,
210 ) -> Result<EncryptedData, EncryptionError> {
211 let data_bytes = elements_to_bytes(data);
212 let ad_bytes = elements_to_bytes(associated_data);
213
214 let mut encrypted_data = self.encrypt_bytes_with_nonce(&data_bytes, &ad_bytes, nonce)?;
215 encrypted_data.data_type = DataType::Elements;
216 Ok(encrypted_data)
217 }
218
219 pub fn decrypt_bytes(
228 &self,
229 encrypted_data: &EncryptedData,
230 ) -> Result<Vec<u8>, EncryptionError> {
231 self.decrypt_bytes_with_associated_data(encrypted_data, &[])
232 }
233
234 pub fn decrypt_bytes_with_associated_data(
240 &self,
241 encrypted_data: &EncryptedData,
242 associated_data: &[u8],
243 ) -> Result<Vec<u8>, EncryptionError> {
244 if encrypted_data.data_type != DataType::Bytes {
245 return Err(EncryptionError::InvalidDataType {
246 expected: DataType::Bytes,
247 found: encrypted_data.data_type,
248 });
249 }
250 self.decrypt_bytes_with_associated_data_unchecked(encrypted_data, associated_data)
251 }
252
253 fn decrypt_bytes_with_associated_data_unchecked(
255 &self,
256 encrypted_data: &EncryptedData,
257 associated_data: &[u8],
258 ) -> Result<Vec<u8>, EncryptionError> {
259 let EncryptedData { ciphertext, nonce, data_type: _ } = encrypted_data;
260 let payload = chacha20poly1305::aead::Payload { msg: ciphertext, aad: associated_data };
261
262 let cipher = XChaCha20Poly1305::new(&self.0.into());
263
264 cipher
265 .decrypt(&nonce.inner, payload)
266 .map_err(|_| EncryptionError::FailedOperation)
267 }
268
269 pub fn decrypt_elements(
278 &self,
279 encrypted_data: &EncryptedData,
280 ) -> Result<Vec<Felt>, EncryptionError> {
281 self.decrypt_elements_with_associated_data(encrypted_data, &[])
282 }
283
284 pub fn decrypt_elements_with_associated_data(
290 &self,
291 encrypted_data: &EncryptedData,
292 associated_data: &[Felt],
293 ) -> Result<Vec<Felt>, EncryptionError> {
294 if encrypted_data.data_type != DataType::Elements {
295 return Err(EncryptionError::InvalidDataType {
296 expected: DataType::Elements,
297 found: encrypted_data.data_type,
298 });
299 }
300
301 let ad_bytes = elements_to_bytes(associated_data);
302
303 let plaintext_bytes =
304 self.decrypt_bytes_with_associated_data_unchecked(encrypted_data, &ad_bytes)?;
305 match bytes_to_elements_exact(&plaintext_bytes) {
306 Some(elements) => Ok(elements),
307 None => Err(EncryptionError::FailedBytesToElementsConversion),
308 }
309 }
310}
311
312impl AsRef<[u8]> for SecretKey {
313 fn as_ref(&self) -> &[u8] {
314 &self.0
315 }
316}
317
318impl Drop for SecretKey {
319 fn drop(&mut self) {
320 self.zeroize();
321 }
322}
323
324impl Zeroize for SecretKey {
325 fn zeroize(&mut self) {
326 self.0.zeroize();
327 }
328}
329
330impl ZeroizeOnDrop for SecretKey {}
331
332pub struct XChaCha;
336
337impl AeadScheme for XChaCha {
338 const KEY_SIZE: usize = SK_SIZE_BYTES;
339
340 type Key = SecretKey;
341
342 fn key_from_bytes(bytes: &[u8]) -> Result<Self::Key, EncryptionError> {
343 if bytes.len() != SK_SIZE_BYTES {
344 return Err(EncryptionError::FailedOperation);
345 }
346
347 SecretKey::read_from_bytes_with_budget(bytes, SK_SIZE_BYTES)
348 .map_err(|_| EncryptionError::FailedOperation)
349 }
350
351 fn encrypt_bytes<R: rand::CryptoRng + rand::RngCore>(
352 key: &Self::Key,
353 rng: &mut R,
354 plaintext: &[u8],
355 associated_data: &[u8],
356 ) -> Result<Vec<u8>, EncryptionError> {
357 let nonce = Nonce::with_rng(rng);
358 let encrypted_data = key
359 .encrypt_bytes_with_nonce(plaintext, associated_data, nonce)
360 .map_err(|_| EncryptionError::FailedOperation)?;
361 Ok(encrypted_data.to_bytes())
362 }
363
364 fn decrypt_bytes_with_associated_data(
365 key: &Self::Key,
366 ciphertext: &[u8],
367 associated_data: &[u8],
368 ) -> Result<Vec<u8>, EncryptionError> {
369 let encrypted_data =
370 EncryptedData::read_from_bytes_with_budget(ciphertext, ciphertext.len())
371 .map_err(|_| EncryptionError::FailedOperation)?;
372
373 key.decrypt_bytes_with_associated_data(&encrypted_data, associated_data)
374 .map_err(|_| EncryptionError::FailedOperation)
375 }
376}
377
378impl Serializable for SecretKey {
382 fn write_into<W: ByteWriter>(&self, target: &mut W) {
383 target.write_bytes(&self.0);
384 }
385}
386
387impl Deserializable for SecretKey {
388 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
389 let inner: [u8; SK_SIZE_BYTES] = source.read_array()?;
390
391 Ok(SecretKey(inner))
392 }
393}
394
395impl Serializable for Nonce {
396 fn write_into<W: ByteWriter>(&self, target: &mut W) {
397 target.write_bytes(&self.inner);
398 }
399}
400
401impl Deserializable for Nonce {
402 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
403 let inner: [u8; NONCE_SIZE_BYTES] = source.read_array()?;
404
405 Ok(Nonce { inner: inner.into() })
406 }
407}
408
409impl Serializable for EncryptedData {
410 fn write_into<W: ByteWriter>(&self, target: &mut W) {
411 target.write_u8(self.data_type as u8);
412 target.write_usize(self.ciphertext.len());
413 target.write_bytes(&self.ciphertext);
414 target.write_bytes(&self.nonce.inner);
415 }
416}
417
418impl Deserializable for EncryptedData {
419 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
420 let data_type_value: u8 = source.read_u8()?;
421 let data_type = data_type_value.try_into().map_err(|_| {
422 DeserializationError::InvalidValue("invalid data type value".to_string())
423 })?;
424
425 let ciphertext = Vec::<u8>::read_from(source)?;
426
427 let inner: [u8; NONCE_SIZE_BYTES] = source.read_array()?;
428
429 Ok(Self {
430 ciphertext,
431 nonce: Nonce { inner: inner.into() },
432 data_type,
433 })
434 }
435}