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 let mut rng = rand::rng();
185 let nonce = Nonce::with_rng(&mut rng);
186
187 self.encrypt_elements_with_nonce(data, associated_data, nonce)
188 }
189
190 pub fn encrypt_elements_with_nonce(
193 &self,
194 data: &[Felt],
195 associated_data: &[Felt],
196 nonce: Nonce,
197 ) -> Result<EncryptedData, EncryptionError> {
198 let data_bytes = elements_to_bytes(data);
199 let ad_bytes = elements_to_bytes(associated_data);
200
201 let mut encrypted_data = self.encrypt_bytes_with_nonce(&data_bytes, &ad_bytes, nonce)?;
202 encrypted_data.data_type = DataType::Elements;
203 Ok(encrypted_data)
204 }
205
206 pub fn decrypt_bytes(
215 &self,
216 encrypted_data: &EncryptedData,
217 ) -> Result<Vec<u8>, EncryptionError> {
218 self.decrypt_bytes_with_associated_data(encrypted_data, &[])
219 }
220
221 pub fn decrypt_bytes_with_associated_data(
227 &self,
228 encrypted_data: &EncryptedData,
229 associated_data: &[u8],
230 ) -> Result<Vec<u8>, EncryptionError> {
231 if encrypted_data.data_type != DataType::Bytes {
232 return Err(EncryptionError::InvalidDataType {
233 expected: DataType::Bytes,
234 found: encrypted_data.data_type,
235 });
236 }
237 self.decrypt_bytes_with_associated_data_unchecked(encrypted_data, associated_data)
238 }
239
240 fn decrypt_bytes_with_associated_data_unchecked(
242 &self,
243 encrypted_data: &EncryptedData,
244 associated_data: &[u8],
245 ) -> Result<Vec<u8>, EncryptionError> {
246 let EncryptedData { ciphertext, nonce, data_type: _ } = encrypted_data;
247 let payload = chacha20poly1305::aead::Payload { msg: ciphertext, aad: associated_data };
248
249 let cipher = XChaCha20Poly1305::new(&self.0.into());
250
251 cipher
252 .decrypt(&nonce.inner, payload)
253 .map_err(|_| EncryptionError::FailedOperation)
254 }
255
256 pub fn decrypt_elements(
265 &self,
266 encrypted_data: &EncryptedData,
267 ) -> Result<Vec<Felt>, EncryptionError> {
268 self.decrypt_elements_with_associated_data(encrypted_data, &[])
269 }
270
271 pub fn decrypt_elements_with_associated_data(
277 &self,
278 encrypted_data: &EncryptedData,
279 associated_data: &[Felt],
280 ) -> Result<Vec<Felt>, EncryptionError> {
281 if encrypted_data.data_type != DataType::Elements {
282 return Err(EncryptionError::InvalidDataType {
283 expected: DataType::Elements,
284 found: encrypted_data.data_type,
285 });
286 }
287
288 let ad_bytes = elements_to_bytes(associated_data);
289
290 let plaintext_bytes =
291 self.decrypt_bytes_with_associated_data_unchecked(encrypted_data, &ad_bytes)?;
292 match bytes_to_elements_exact(&plaintext_bytes) {
293 Some(elements) => Ok(elements),
294 None => Err(EncryptionError::FailedBytesToElementsConversion),
295 }
296 }
297}
298
299impl AsRef<[u8]> for SecretKey {
300 fn as_ref(&self) -> &[u8] {
301 &self.0
302 }
303}
304
305impl Drop for SecretKey {
306 fn drop(&mut self) {
307 self.zeroize();
308 }
309}
310
311impl Zeroize for SecretKey {
312 fn zeroize(&mut self) {
313 self.0.zeroize();
314 }
315}
316
317impl ZeroizeOnDrop for SecretKey {}
318
319pub struct XChaCha;
323
324impl AeadScheme for XChaCha {
325 const KEY_SIZE: usize = SK_SIZE_BYTES;
326
327 type Key = SecretKey;
328
329 fn key_from_bytes(bytes: &[u8]) -> Result<Self::Key, EncryptionError> {
330 SecretKey::read_from_bytes(bytes).map_err(|_| EncryptionError::FailedOperation)
331 }
332
333 fn encrypt_bytes<R: rand::CryptoRng + rand::RngCore>(
334 key: &Self::Key,
335 rng: &mut R,
336 plaintext: &[u8],
337 associated_data: &[u8],
338 ) -> Result<Vec<u8>, EncryptionError> {
339 let nonce = Nonce::with_rng(rng);
340 let encrypted_data = key
341 .encrypt_bytes_with_nonce(plaintext, associated_data, nonce)
342 .map_err(|_| EncryptionError::FailedOperation)?;
343 Ok(encrypted_data.to_bytes())
344 }
345
346 fn decrypt_bytes_with_associated_data(
347 key: &Self::Key,
348 ciphertext: &[u8],
349 associated_data: &[u8],
350 ) -> Result<Vec<u8>, EncryptionError> {
351 let encrypted_data = &EncryptedData::read_from_bytes(ciphertext).unwrap();
352 key.decrypt_bytes_with_associated_data(encrypted_data, associated_data)
353 .map_err(|_| EncryptionError::FailedOperation)
354 }
355}
356
357impl Serializable for SecretKey {
361 fn write_into<W: ByteWriter>(&self, target: &mut W) {
362 target.write_bytes(&self.0);
363 }
364}
365
366impl Deserializable for SecretKey {
367 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
368 let inner: [u8; SK_SIZE_BYTES] = source.read_array()?;
369
370 Ok(SecretKey(inner))
371 }
372}
373
374impl Serializable for Nonce {
375 fn write_into<W: ByteWriter>(&self, target: &mut W) {
376 target.write_bytes(&self.inner);
377 }
378}
379
380impl Deserializable for Nonce {
381 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
382 let inner: [u8; NONCE_SIZE_BYTES] = source.read_array()?;
383
384 Ok(Nonce { inner: inner.into() })
385 }
386}
387
388impl Serializable for EncryptedData {
389 fn write_into<W: ByteWriter>(&self, target: &mut W) {
390 target.write_u8(self.data_type as u8);
391 target.write_usize(self.ciphertext.len());
392 target.write_bytes(&self.ciphertext);
393 target.write_bytes(&self.nonce.inner);
394 }
395}
396
397impl Deserializable for EncryptedData {
398 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
399 let data_type_value: u8 = source.read_u8()?;
400 let data_type = data_type_value.try_into().map_err(|_| {
401 DeserializationError::InvalidValue("invalid data type value".to_string())
402 })?;
403
404 let ciphertext = Vec::<u8>::read_from(source)?;
405
406 let inner: [u8; NONCE_SIZE_BYTES] = source.read_array()?;
407
408 Ok(Self {
409 ciphertext,
410 nonce: Nonce { inner: inner.into() },
411 data_type,
412 })
413 }
414}