miden_crypto/aead/xchacha/
mod.rs1use alloc::{string::ToString, vec::Vec};
14
15use chacha20poly1305::{
16 XChaCha20Poly1305,
17 aead::{Aead, KeyInit, Payload},
18};
19use miden_crypto_derive::SilentDebug;
20use rand::CryptoRng;
21#[cfg(any(test, feature = "testing"))]
22use subtle::ConstantTimeEq;
23
24use crate::{
25 Felt,
26 aead::{AeadScheme, DataType, EncryptionError},
27 utils::{
28 BudgetedReader, ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
29 SliceReader, bytes_to_elements_exact, elements_to_bytes, read_sensitive_array,
30 zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing},
31 },
32};
33
34#[cfg(test)]
35mod test;
36
37const NONCE_SIZE_BYTES: usize = 24;
42const SK_SIZE_BYTES: usize = 32;
44
45#[derive(Debug, PartialEq, Eq)]
50pub struct EncryptedData {
51 data_type: DataType,
53 ciphertext: Vec<u8>,
55 nonce: Nonce,
57}
58
59#[derive(Clone, Debug, PartialEq, Eq)]
63pub struct Nonce {
64 inner: chacha20poly1305::XNonce,
65}
66
67impl Nonce {
68 pub fn with_rng<R: CryptoRng>(rng: &mut R) -> Self {
70 let mut bytes = [0u8; NONCE_SIZE_BYTES];
71 rng.fill_bytes(&mut bytes);
72 Self::from_slice(&bytes)
73 }
74
75 pub fn from_slice(bytes: &[u8; NONCE_SIZE_BYTES]) -> Self {
77 Nonce { inner: (*bytes).into() }
78 }
79}
80
81#[derive(SilentDebug)]
83pub struct SecretKey([u8; SK_SIZE_BYTES]);
84
85#[cfg(any(test, feature = "testing"))]
86impl PartialEq for SecretKey {
87 fn eq(&self, other: &Self) -> bool {
88 bool::from(self.0.ct_eq(&other.0))
90 }
91}
92
93#[cfg(any(test, feature = "testing"))]
94impl Eq for SecretKey {}
95
96fn read_encrypted_data_strict(ciphertext: &[u8]) -> Result<EncryptedData, EncryptionError> {
97 let mut reader = BudgetedReader::new(SliceReader::new(ciphertext), ciphertext.len());
98 let encrypted_data =
99 EncryptedData::read_from(&mut reader).map_err(|_| EncryptionError::FailedOperation)?;
100
101 if reader.has_more_bytes() {
102 return Err(EncryptionError::FailedOperation);
103 }
104
105 Ok(encrypted_data)
106}
107
108impl SecretKey {
109 #[cfg(feature = "std")]
114 #[allow(clippy::new_without_default)]
115 pub fn new() -> Self {
116 let mut rng = rand::rng();
117 Self::with_rng(&mut rng)
118 }
119
120 pub fn with_rng<R: CryptoRng>(rng: &mut R) -> Self {
122 let mut key = Zeroizing::new([0u8; SK_SIZE_BYTES]);
123 rng.fill_bytes(key.as_mut());
124 Self(*key)
125 }
126
127 #[cfg(feature = "std")]
133 pub fn encrypt_bytes(&self, data: &[u8]) -> Result<EncryptedData, EncryptionError> {
134 self.encrypt_bytes_with_associated_data(data, &[])
135 }
136
137 #[cfg(feature = "std")]
140 pub fn encrypt_bytes_with_associated_data(
141 &self,
142 data: &[u8],
143 associated_data: &[u8],
144 ) -> Result<EncryptedData, EncryptionError> {
145 let mut rng = rand::rng();
146 let nonce = Nonce::with_rng(&mut rng);
147
148 self.encrypt_bytes_with_nonce(data, associated_data, nonce)
149 }
150
151 pub fn encrypt_bytes_with_nonce(
153 &self,
154 data: &[u8],
155 associated_data: &[u8],
156 nonce: Nonce,
157 ) -> Result<EncryptedData, EncryptionError> {
158 let payload = Payload { msg: data, aad: associated_data };
159
160 let cipher = XChaCha20Poly1305::new(&self.0.into());
161
162 let ciphertext = cipher
163 .encrypt(&nonce.inner, payload)
164 .map_err(|_| EncryptionError::FailedOperation)?;
165
166 Ok(EncryptedData {
167 data_type: DataType::Bytes,
168 ciphertext,
169 nonce,
170 })
171 }
172
173 #[cfg(feature = "std")]
179 pub fn encrypt_elements(&self, data: &[Felt]) -> Result<EncryptedData, EncryptionError> {
180 self.encrypt_elements_with_associated_data(data, &[])
181 }
182
183 #[cfg(feature = "std")]
186 pub fn encrypt_elements_with_associated_data(
187 &self,
188 data: &[Felt],
189 associated_data: &[Felt],
190 ) -> Result<EncryptedData, EncryptionError> {
191 let mut rng = rand::rng();
192 let nonce = Nonce::with_rng(&mut rng);
193
194 self.encrypt_elements_with_nonce(data, associated_data, nonce)
195 }
196
197 pub fn encrypt_elements_with_nonce(
200 &self,
201 data: &[Felt],
202 associated_data: &[Felt],
203 nonce: Nonce,
204 ) -> Result<EncryptedData, EncryptionError> {
205 let data_bytes = elements_to_bytes(data);
206 let ad_bytes = elements_to_bytes(associated_data);
207
208 let mut encrypted_data = self.encrypt_bytes_with_nonce(&data_bytes, &ad_bytes, nonce)?;
209 encrypted_data.data_type = DataType::Elements;
210 Ok(encrypted_data)
211 }
212
213 pub fn decrypt_bytes(
222 &self,
223 encrypted_data: &EncryptedData,
224 ) -> Result<Vec<u8>, EncryptionError> {
225 self.decrypt_bytes_with_associated_data(encrypted_data, &[])
226 }
227
228 pub fn decrypt_bytes_with_associated_data(
234 &self,
235 encrypted_data: &EncryptedData,
236 associated_data: &[u8],
237 ) -> Result<Vec<u8>, EncryptionError> {
238 if encrypted_data.data_type != DataType::Bytes {
239 return Err(EncryptionError::InvalidDataType {
240 expected: DataType::Bytes,
241 found: encrypted_data.data_type,
242 });
243 }
244 self.decrypt_bytes_with_associated_data_unchecked(encrypted_data, associated_data)
245 }
246
247 fn decrypt_bytes_with_associated_data_unchecked(
249 &self,
250 encrypted_data: &EncryptedData,
251 associated_data: &[u8],
252 ) -> Result<Vec<u8>, EncryptionError> {
253 let EncryptedData { ciphertext, nonce, data_type: _ } = encrypted_data;
254 let payload = Payload { msg: ciphertext, aad: associated_data };
255
256 let cipher = XChaCha20Poly1305::new(&self.0.into());
257
258 cipher
259 .decrypt(&nonce.inner, payload)
260 .map_err(|_| EncryptionError::FailedOperation)
261 }
262
263 pub fn decrypt_elements(
272 &self,
273 encrypted_data: &EncryptedData,
274 ) -> Result<Vec<Felt>, EncryptionError> {
275 self.decrypt_elements_with_associated_data(encrypted_data, &[])
276 }
277
278 pub fn decrypt_elements_with_associated_data(
284 &self,
285 encrypted_data: &EncryptedData,
286 associated_data: &[Felt],
287 ) -> Result<Vec<Felt>, EncryptionError> {
288 if encrypted_data.data_type != DataType::Elements {
289 return Err(EncryptionError::InvalidDataType {
290 expected: DataType::Elements,
291 found: encrypted_data.data_type,
292 });
293 }
294
295 let ad_bytes = elements_to_bytes(associated_data);
296
297 let plaintext_bytes =
298 self.decrypt_bytes_with_associated_data_unchecked(encrypted_data, &ad_bytes)?;
299 match bytes_to_elements_exact(&plaintext_bytes) {
300 Some(elements) => Ok(elements),
301 None => Err(EncryptionError::FailedBytesToElementsConversion),
302 }
303 }
304}
305
306impl AsRef<[u8]> for SecretKey {
307 fn as_ref(&self) -> &[u8] {
308 &self.0
309 }
310}
311
312impl Drop for SecretKey {
313 fn drop(&mut self) {
314 self.zeroize();
315 }
316}
317
318impl Zeroize for SecretKey {
319 fn zeroize(&mut self) {
320 self.0.zeroize();
321 }
322}
323
324impl ZeroizeOnDrop for SecretKey {}
325
326pub struct XChaCha;
330
331impl AeadScheme for XChaCha {
332 const KEY_SIZE: usize = SK_SIZE_BYTES;
333
334 type Key = SecretKey;
335
336 fn key_from_bytes(bytes: &[u8]) -> Result<Self::Key, EncryptionError> {
337 if bytes.len() != SK_SIZE_BYTES {
338 return Err(EncryptionError::FailedOperation);
339 }
340
341 SecretKey::read_from_bytes_with_budget(bytes, SK_SIZE_BYTES)
342 .map_err(|_| EncryptionError::FailedOperation)
343 }
344
345 fn encrypt_bytes<R: CryptoRng>(
346 key: &Self::Key,
347 rng: &mut R,
348 plaintext: &[u8],
349 associated_data: &[u8],
350 ) -> Result<Vec<u8>, EncryptionError> {
351 let nonce = Nonce::with_rng(rng);
352 let encrypted_data = key
353 .encrypt_bytes_with_nonce(plaintext, associated_data, nonce)
354 .map_err(|_| EncryptionError::FailedOperation)?;
355 Ok(encrypted_data.to_bytes())
356 }
357
358 fn decrypt_bytes_with_associated_data(
359 key: &Self::Key,
360 ciphertext: &[u8],
361 associated_data: &[u8],
362 ) -> Result<Vec<u8>, EncryptionError> {
363 let encrypted_data = read_encrypted_data_strict(ciphertext)?;
364
365 key.decrypt_bytes_with_associated_data(&encrypted_data, associated_data)
366 .map_err(|_| EncryptionError::FailedOperation)
367 }
368}
369
370impl Serializable for SecretKey {
374 fn write_into<W: ByteWriter>(&self, target: &mut W) {
375 target.write_bytes(&self.0);
376 }
377}
378
379impl Deserializable for SecretKey {
380 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
381 let inner = read_sensitive_array::<SK_SIZE_BYTES, _>(source)?;
382
383 Ok(SecretKey(*inner))
384 }
385}
386
387impl Serializable for Nonce {
388 fn write_into<W: ByteWriter>(&self, target: &mut W) {
389 target.write_bytes(&self.inner);
390 }
391}
392
393impl Deserializable for Nonce {
394 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
395 let inner: [u8; NONCE_SIZE_BYTES] = source.read_array()?;
396
397 Ok(Nonce { inner: inner.into() })
398 }
399}
400
401impl Serializable for EncryptedData {
402 fn write_into<W: ByteWriter>(&self, target: &mut W) {
403 target.write_u8(self.data_type as u8);
404 target.write_usize(self.ciphertext.len());
405 target.write_bytes(&self.ciphertext);
406 target.write_bytes(&self.nonce.inner);
407 }
408}
409
410impl Deserializable for EncryptedData {
411 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
412 let data_type_value: u8 = source.read_u8()?;
413 let data_type = data_type_value.try_into().map_err(|_| {
414 DeserializationError::InvalidValue("invalid data type value".to_string())
415 })?;
416
417 let ciphertext = Vec::<u8>::read_from(source)?;
418
419 let inner: [u8; NONCE_SIZE_BYTES] = source.read_array()?;
420
421 Ok(Self {
422 ciphertext,
423 nonce: Nonce { inner: inner.into() },
424 data_type,
425 })
426 }
427}