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
21use crate::{
22 Felt,
23 aead::{AeadScheme, DataType, EncryptionError},
24 utils::{
25 ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
26 bytes_to_elements_exact, elements_to_bytes,
27 },
28 zeroize::{Zeroize, ZeroizeOnDrop},
29};
30
31#[cfg(test)]
32mod test;
33
34const NONCE_SIZE_BYTES: usize = 24;
39const SK_SIZE_BYTES: usize = 32;
41
42#[derive(Debug, PartialEq, Eq)]
47pub struct EncryptedData {
48 data_type: DataType,
50 ciphertext: Vec<u8>,
52 nonce: Nonce,
54}
55
56#[derive(Clone, Debug, PartialEq, Eq)]
60pub struct Nonce {
61 inner: chacha20poly1305::XNonce,
62}
63
64impl Nonce {
65 pub fn with_rng<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
67 use chacha20poly1305::aead::rand_core::SeedableRng;
73 let mut seed = [0_u8; 32];
74 rand::RngCore::fill_bytes(rng, &mut seed);
75 let rng = rand_hc::Hc128Rng::from_seed(seed);
76
77 Nonce {
78 inner: XChaCha20Poly1305::generate_nonce(rng),
79 }
80 }
81
82 pub fn from_slice(bytes: &[u8; NONCE_SIZE_BYTES]) -> Self {
84 Nonce { inner: (*bytes).into() }
85 }
86}
87
88#[derive(Debug, PartialEq, Eq)]
90pub struct SecretKey([u8; SK_SIZE_BYTES]);
91
92impl SecretKey {
93 #[cfg(feature = "std")]
98 #[allow(clippy::new_without_default)]
99 pub fn new() -> Self {
100 let mut rng = rand::rng();
101 Self::with_rng(&mut rng)
102 }
103
104 pub fn with_rng<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
106 use chacha20poly1305::aead::rand_core::SeedableRng;
112 let mut seed = [0_u8; 32];
113 rand::RngCore::fill_bytes(rng, &mut seed);
114 let rng = rand_hc::Hc128Rng::from_seed(seed);
115
116 let key = XChaCha20Poly1305::generate_key(rng);
117 Self(key.into())
118 }
119
120 #[cfg(feature = "std")]
126 pub fn encrypt_bytes(&self, data: &[u8]) -> Result<EncryptedData, EncryptionError> {
127 self.encrypt_bytes_with_associated_data(data, &[])
128 }
129
130 #[cfg(feature = "std")]
133 pub fn encrypt_bytes_with_associated_data(
134 &self,
135 data: &[u8],
136 associated_data: &[u8],
137 ) -> Result<EncryptedData, EncryptionError> {
138 let mut rng = rand::rng();
139 let nonce = Nonce::with_rng(&mut rng);
140
141 self.encrypt_bytes_with_nonce(data, associated_data, nonce)
142 }
143
144 pub fn encrypt_bytes_with_nonce(
146 &self,
147 data: &[u8],
148 associated_data: &[u8],
149 nonce: Nonce,
150 ) -> Result<EncryptedData, EncryptionError> {
151 let payload = chacha20poly1305::aead::Payload { msg: data, aad: associated_data };
152
153 let cipher = XChaCha20Poly1305::new(&self.0.into());
154
155 let ciphertext = cipher
156 .encrypt(&nonce.inner, payload)
157 .map_err(|_| EncryptionError::FailedOperation)?;
158
159 Ok(EncryptedData {
160 data_type: DataType::Bytes,
161 ciphertext,
162 nonce,
163 })
164 }
165
166 #[cfg(feature = "std")]
172 pub fn encrypt_elements(&self, data: &[Felt]) -> Result<EncryptedData, EncryptionError> {
173 self.encrypt_elements_with_associated_data(data, &[])
174 }
175
176 #[cfg(feature = "std")]
179 pub fn encrypt_elements_with_associated_data(
180 &self,
181 data: &[Felt],
182 associated_data: &[Felt],
183 ) -> Result<EncryptedData, EncryptionError> {
184 use rand::{SeedableRng, rngs::StdRng};
185 let mut rng = StdRng::from_os_rng();
186 let nonce = Nonce::with_rng(&mut rng);
187
188 self.encrypt_elements_with_nonce(data, associated_data, nonce)
189 }
190
191 pub fn encrypt_elements_with_nonce(
194 &self,
195 data: &[Felt],
196 associated_data: &[Felt],
197 nonce: Nonce,
198 ) -> Result<EncryptedData, EncryptionError> {
199 let data_bytes = elements_to_bytes(data);
200 let ad_bytes = elements_to_bytes(associated_data);
201
202 let mut encrypted_data = self.encrypt_bytes_with_nonce(&data_bytes, &ad_bytes, nonce)?;
203 encrypted_data.data_type = DataType::Elements;
204 Ok(encrypted_data)
205 }
206
207 pub fn decrypt_bytes(
216 &self,
217 encrypted_data: &EncryptedData,
218 ) -> Result<Vec<u8>, EncryptionError> {
219 self.decrypt_bytes_with_associated_data(encrypted_data, &[])
220 }
221
222 pub fn decrypt_bytes_with_associated_data(
228 &self,
229 encrypted_data: &EncryptedData,
230 associated_data: &[u8],
231 ) -> Result<Vec<u8>, EncryptionError> {
232 if encrypted_data.data_type != DataType::Bytes {
233 return Err(EncryptionError::InvalidDataType {
234 expected: DataType::Bytes,
235 found: encrypted_data.data_type,
236 });
237 }
238 self.decrypt_bytes_with_associated_data_unchecked(encrypted_data, associated_data)
239 }
240
241 fn decrypt_bytes_with_associated_data_unchecked(
243 &self,
244 encrypted_data: &EncryptedData,
245 associated_data: &[u8],
246 ) -> Result<Vec<u8>, EncryptionError> {
247 let EncryptedData { ciphertext, nonce, data_type: _ } = encrypted_data;
248 let payload = chacha20poly1305::aead::Payload { msg: ciphertext, aad: associated_data };
249
250 let cipher = XChaCha20Poly1305::new(&self.0.into());
251
252 cipher
253 .decrypt(&nonce.inner, payload)
254 .map_err(|_| EncryptionError::FailedOperation)
255 }
256
257 pub fn decrypt_elements(
266 &self,
267 encrypted_data: &EncryptedData,
268 ) -> Result<Vec<Felt>, EncryptionError> {
269 self.decrypt_elements_with_associated_data(encrypted_data, &[])
270 }
271
272 pub fn decrypt_elements_with_associated_data(
278 &self,
279 encrypted_data: &EncryptedData,
280 associated_data: &[Felt],
281 ) -> Result<Vec<Felt>, EncryptionError> {
282 if encrypted_data.data_type != DataType::Elements {
283 return Err(EncryptionError::InvalidDataType {
284 expected: DataType::Elements,
285 found: encrypted_data.data_type,
286 });
287 }
288
289 let ad_bytes = elements_to_bytes(associated_data);
290
291 let plaintext_bytes =
292 self.decrypt_bytes_with_associated_data_unchecked(encrypted_data, &ad_bytes)?;
293 match bytes_to_elements_exact(&plaintext_bytes) {
294 Some(elements) => Ok(elements),
295 None => Err(EncryptionError::FailedBytesToElementsConversion),
296 }
297 }
298}
299
300impl AsRef<[u8]> for SecretKey {
301 fn as_ref(&self) -> &[u8] {
302 &self.0
303 }
304}
305
306impl Drop for SecretKey {
307 fn drop(&mut self) {
308 self.zeroize();
309 }
310}
311
312impl Zeroize for SecretKey {
313 fn zeroize(&mut self) {
314 self.0.zeroize();
315 }
316}
317
318impl ZeroizeOnDrop for SecretKey {}
319
320pub struct XChaCha;
324
325impl AeadScheme for XChaCha {
326 const KEY_SIZE: usize = SK_SIZE_BYTES;
327
328 type Key = SecretKey;
329
330 fn key_from_bytes(bytes: &[u8]) -> Result<Self::Key, EncryptionError> {
331 SecretKey::read_from_bytes(bytes).map_err(|_| EncryptionError::FailedOperation)
332 }
333
334 fn encrypt_bytes<R: rand::CryptoRng + rand::RngCore>(
335 key: &Self::Key,
336 rng: &mut R,
337 plaintext: &[u8],
338 associated_data: &[u8],
339 ) -> Result<Vec<u8>, EncryptionError> {
340 let nonce = Nonce::with_rng(rng);
341 let encrypted_data = key
342 .encrypt_bytes_with_nonce(plaintext, associated_data, nonce)
343 .map_err(|_| EncryptionError::FailedOperation)?;
344 Ok(encrypted_data.to_bytes())
345 }
346
347 fn decrypt_bytes_with_associated_data(
348 key: &Self::Key,
349 ciphertext: &[u8],
350 associated_data: &[u8],
351 ) -> Result<Vec<u8>, EncryptionError> {
352 let encrypted_data = &EncryptedData::read_from_bytes(ciphertext).unwrap();
353 key.decrypt_bytes_with_associated_data(encrypted_data, associated_data)
354 .map_err(|_| EncryptionError::FailedOperation)
355 }
356}
357
358impl Serializable for SecretKey {
362 fn write_into<W: ByteWriter>(&self, target: &mut W) {
363 target.write_bytes(&self.0);
364 }
365}
366
367impl Deserializable for SecretKey {
368 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
369 let inner: [u8; SK_SIZE_BYTES] = source.read_array()?;
370
371 Ok(SecretKey(inner))
372 }
373}
374
375impl Serializable for Nonce {
376 fn write_into<W: ByteWriter>(&self, target: &mut W) {
377 target.write_bytes(&self.inner);
378 }
379}
380
381impl Deserializable for Nonce {
382 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
383 let inner: [u8; NONCE_SIZE_BYTES] = source.read_array()?;
384
385 Ok(Nonce { inner: inner.into() })
386 }
387}
388
389impl Serializable for EncryptedData {
390 fn write_into<W: ByteWriter>(&self, target: &mut W) {
391 target.write_u8(self.data_type as u8);
392 target.write_usize(self.ciphertext.len());
393 target.write_bytes(&self.ciphertext);
394 target.write_bytes(&self.nonce.inner);
395 }
396}
397
398impl Deserializable for EncryptedData {
399 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
400 let data_type_value: u8 = source.read_u8()?;
401 let data_type = data_type_value.try_into().map_err(|_| {
402 DeserializationError::InvalidValue("invalid data type value".to_string())
403 })?;
404
405 let ciphertext = Vec::<u8>::read_from(source)?;
406
407 let inner: [u8; NONCE_SIZE_BYTES] = source.read_array()?;
408
409 Ok(Self {
410 ciphertext,
411 nonce: Nonce { inner: inner.into() },
412 data_type,
413 })
414 }
415}