1use alloc::{format, string::ToString, vec::Vec};
9
10use miden_crypto_derive::{SilentDebug, SilentDisplay};
11use rand::{
12 CryptoRng,
13 distr::{Distribution, Uniform},
14};
15#[cfg(any(test, feature = "testing"))]
16use subtle::ConstantTimeEq;
17
18use super::{AeadScheme, DataType, EncryptionError};
19use crate::{
20 Felt, Word, ZERO,
21 field::PrimeCharacteristicRing,
22 utils::{
23 BudgetedReader, ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
24 SliceReader, bytes_to_elements_exact, bytes_to_elements_with_padding, elements_to_bytes,
25 padded_elements_to_bytes, read_sensitive_array,
26 zeroize::{Zeroize, ZeroizeOnDrop},
27 },
28};
29
30pub mod expanded;
32
33#[cfg(test)]
34mod tests;
35
36pub const SECRET_KEY_SIZE: usize = 4;
38
39pub const SK_SIZE_BYTES: usize = SECRET_KEY_SIZE * Felt::NUM_BYTES;
41
42pub const NONCE_SIZE: usize = 4;
44
45pub const NONCE_SIZE_BYTES: usize = NONCE_SIZE * Felt::NUM_BYTES;
47
48pub const AUTH_TAG_SIZE: usize = 2;
50
51pub const MAX_AUTHENTICATED_INPUT_FELTS: usize = 1 << 28;
57
58pub const MAX_VERIFICATION_DEGREE_BUDGET_PER_KEY: u64 = 1 << 28;
65
66#[derive(Debug, Clone, PartialEq, Eq)]
68pub struct EncryptedData {
69 data_type: DataType,
70 ciphertext: Vec<Felt>,
71 auth_tag: AuthTag,
72 nonce: Nonce,
73}
74
75impl EncryptedData {
76 pub fn from_parts(
78 data_type: DataType,
79 ciphertext: Vec<Felt>,
80 auth_tag: AuthTag,
81 nonce: Nonce,
82 ) -> Result<Self, EncryptionError> {
83 validate_ciphertext(&ciphertext)?;
84 Ok(Self { data_type, ciphertext, auth_tag, nonce })
85 }
86
87 pub fn data_type(&self) -> DataType {
89 self.data_type
90 }
91
92 pub fn ciphertext(&self) -> &[Felt] {
94 &self.ciphertext
95 }
96
97 pub fn auth_tag(&self) -> &AuthTag {
99 &self.auth_tag
100 }
101
102 pub fn nonce(&self) -> &Nonce {
104 &self.nonce
105 }
106}
107
108#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
110pub struct AuthTag([Felt; AUTH_TAG_SIZE]);
111
112impl AuthTag {
113 pub fn new(elements: [Felt; AUTH_TAG_SIZE]) -> Self {
115 Self(elements)
116 }
117
118 pub fn to_elements(self) -> [Felt; AUTH_TAG_SIZE] {
120 self.0
121 }
122}
123
124#[derive(Clone, SilentDebug, SilentDisplay)]
126pub struct SecretKey([Felt; SECRET_KEY_SIZE]);
127
128impl SecretKey {
129 #[cfg(feature = "std")]
131 #[allow(clippy::new_without_default)]
132 pub fn new() -> Self {
133 Self::with_rng(&mut rand::rng())
134 }
135
136 pub fn with_rng<R: CryptoRng + ?Sized>(rng: &mut R) -> Self {
138 Self(sample_felts(rng))
139 }
140
141 pub fn from_elements(elements: [Felt; SECRET_KEY_SIZE]) -> Self {
143 Self(elements)
144 }
145
146 pub fn to_elements(&self) -> [Felt; SECRET_KEY_SIZE] {
150 self.0
151 }
152
153 #[cfg(feature = "std")]
155 pub fn encrypt_elements(&self, plaintext: &[Felt]) -> Result<EncryptedData, EncryptionError> {
156 self.encrypt_elements_with_associated_data(plaintext, &[])
157 }
158
159 #[cfg(feature = "std")]
161 pub fn encrypt_elements_with_associated_data(
162 &self,
163 plaintext: &[Felt],
164 associated_data: &[Felt],
165 ) -> Result<EncryptedData, EncryptionError> {
166 self.encrypt_elements_with_nonce(
167 plaintext,
168 associated_data,
169 Nonce::with_rng(&mut rand::rng()),
170 )
171 }
172
173 pub fn encrypt_elements_with_nonce(
177 &self,
178 plaintext: &[Felt],
179 associated_data: &[Felt],
180 nonce: Nonce,
181 ) -> Result<EncryptedData, EncryptionError> {
182 validate_encryption_lengths(plaintext.len(), associated_data.len())?;
183 let authenticated_data = bind_data_type(DataType::Elements, associated_data)?;
184 let (ciphertext, tag) = expanded::encrypt_felts_expanded_authenticated(
185 self.as_word(),
186 nonce.as_word(),
187 &authenticated_data,
188 plaintext,
189 );
190
191 EncryptedData::from_parts(DataType::Elements, ciphertext, AuthTag(tag), nonce)
192 }
193
194 #[cfg(feature = "std")]
196 pub fn encrypt_bytes(&self, plaintext: &[u8]) -> Result<EncryptedData, EncryptionError> {
197 self.encrypt_bytes_with_associated_data(plaintext, &[])
198 }
199
200 #[cfg(feature = "std")]
202 pub fn encrypt_bytes_with_associated_data(
203 &self,
204 plaintext: &[u8],
205 associated_data: &[u8],
206 ) -> Result<EncryptedData, EncryptionError> {
207 self.encrypt_bytes_with_nonce(plaintext, associated_data, Nonce::with_rng(&mut rand::rng()))
208 }
209
210 pub fn encrypt_bytes_with_nonce(
214 &self,
215 plaintext: &[u8],
216 associated_data: &[u8],
217 nonce: Nonce,
218 ) -> Result<EncryptedData, EncryptionError> {
219 let plaintext = bytes_to_elements_with_padding(plaintext);
220 let associated_data = bytes_to_elements_with_padding(associated_data);
221 validate_encryption_lengths(plaintext.len(), associated_data.len())?;
222 let authenticated_data = bind_data_type(DataType::Bytes, &associated_data)?;
223 let (ciphertext, tag) = expanded::encrypt_felts_expanded_authenticated(
224 self.as_word(),
225 nonce.as_word(),
226 &authenticated_data,
227 &plaintext,
228 );
229
230 EncryptedData::from_parts(DataType::Bytes, ciphertext, AuthTag(tag), nonce)
231 }
232
233 pub fn decrypt_elements(
235 &self,
236 encrypted_data: &EncryptedData,
237 ) -> Result<Vec<Felt>, EncryptionError> {
238 self.decrypt_elements_with_associated_data(encrypted_data, &[])
239 }
240
241 pub fn decrypt_elements_with_associated_data(
243 &self,
244 encrypted_data: &EncryptedData,
245 associated_data: &[Felt],
246 ) -> Result<Vec<Felt>, EncryptionError> {
247 ensure_data_type(encrypted_data, DataType::Elements)?;
248 let authenticated_data = bind_data_type(DataType::Elements, associated_data)?;
249 self.decrypt_felts(encrypted_data, &authenticated_data)
250 }
251
252 pub fn decrypt_bytes(
254 &self,
255 encrypted_data: &EncryptedData,
256 ) -> Result<Vec<u8>, EncryptionError> {
257 self.decrypt_bytes_with_associated_data(encrypted_data, &[])
258 }
259
260 pub fn decrypt_bytes_with_associated_data(
262 &self,
263 encrypted_data: &EncryptedData,
264 associated_data: &[u8],
265 ) -> Result<Vec<u8>, EncryptionError> {
266 ensure_data_type(encrypted_data, DataType::Bytes)?;
267 let associated_data = bytes_to_elements_with_padding(associated_data);
268 let authenticated_data = bind_data_type(DataType::Bytes, &associated_data)?;
269 let plaintext = self.decrypt_felts(encrypted_data, &authenticated_data)?;
270 let bytes =
271 padded_elements_to_bytes(&plaintext).ok_or(EncryptionError::MalformedPadding)?;
272
273 if bytes_to_elements_with_padding(&bytes) != plaintext {
274 return Err(EncryptionError::MalformedPadding);
275 }
276 Ok(bytes)
277 }
278
279 fn decrypt_felts(
280 &self,
281 encrypted_data: &EncryptedData,
282 authenticated_data: &[Felt],
283 ) -> Result<Vec<Felt>, EncryptionError> {
284 validate_ciphertext(&encrypted_data.ciphertext)?;
285 expanded::checked_mac_input_len(authenticated_data.len(), encrypted_data.ciphertext.len())
286 .ok_or(EncryptionError::InputTooLong)?;
287 expanded::decrypt_felts_expanded_authenticated(
288 self.as_word(),
289 encrypted_data.nonce.as_word(),
290 authenticated_data,
291 &encrypted_data.ciphertext,
292 encrypted_data.auth_tag.0,
293 )
294 .ok_or(EncryptionError::InvalidAuthTag)
295 }
296
297 fn as_word(&self) -> Word {
298 Word::new(self.0)
299 }
300}
301
302#[cfg(any(test, feature = "testing"))]
303impl PartialEq for SecretKey {
304 fn eq(&self, other: &Self) -> bool {
305 self.0.iter().zip(other.0).fold(true, |equal, (left, right)| {
306 equal & bool::from(left.as_canonical_u64_ct().ct_eq(&right.as_canonical_u64_ct()))
307 })
308 }
309}
310
311#[cfg(any(test, feature = "testing"))]
312impl Eq for SecretKey {}
313
314impl Zeroize for SecretKey {
315 fn zeroize(&mut self) {
316 for element in &mut self.0 {
317 unsafe { core::ptr::write_volatile(element, ZERO) };
318 }
319 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
320 }
321}
322
323impl Drop for SecretKey {
324 fn drop(&mut self) {
325 self.zeroize();
326 }
327}
328
329impl ZeroizeOnDrop for SecretKey {}
330
331#[derive(Debug, Clone, Copy, PartialEq, Eq)]
333pub struct Nonce([Felt; NONCE_SIZE]);
334
335impl Nonce {
336 pub fn with_rng<R: CryptoRng + ?Sized>(rng: &mut R) -> Self {
338 Self(sample_felts(rng))
339 }
340
341 pub fn to_elements(self) -> [Felt; NONCE_SIZE] {
343 self.0
344 }
345
346 fn as_word(self) -> Word {
347 Word::new(self.0)
348 }
349}
350
351impl From<Word> for Nonce {
352 fn from(word: Word) -> Self {
353 Self(word.into())
354 }
355}
356
357impl From<[Felt; NONCE_SIZE]> for Nonce {
358 fn from(elements: [Felt; NONCE_SIZE]) -> Self {
359 Self(elements)
360 }
361}
362
363impl From<Nonce> for Word {
364 fn from(nonce: Nonce) -> Self {
365 nonce.as_word()
366 }
367}
368
369impl Serializable for SecretKey {
370 fn write_into<W: ByteWriter>(&self, target: &mut W) {
371 target.write_bytes(&elements_to_bytes(&self.0));
372 }
373}
374
375impl Deserializable for SecretKey {
376 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
377 let bytes = read_sensitive_array::<SK_SIZE_BYTES, _>(source)?;
378 let elements = bytes_to_elements_exact(bytes.as_slice())
379 .and_then(|elements| elements.try_into().ok())
380 .ok_or_else(|| {
381 DeserializationError::InvalidValue("malformed secret key".to_string())
382 })?;
383 Ok(Self(elements))
384 }
385}
386
387impl Serializable for Nonce {
388 fn write_into<W: ByteWriter>(&self, target: &mut W) {
389 target.write_bytes(&elements_to_bytes(&self.0));
390 }
391}
392
393impl Deserializable for Nonce {
394 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
395 let bytes: [u8; NONCE_SIZE_BYTES] = source.read_array()?;
396 let elements = bytes_to_elements_exact(&bytes)
397 .and_then(|elements| elements.try_into().ok())
398 .ok_or_else(|| DeserializationError::InvalidValue("malformed nonce".to_string()))?;
399 Ok(Self(elements))
400 }
401}
402
403impl Serializable for EncryptedData {
404 fn write_into<W: ByteWriter>(&self, target: &mut W) {
405 target.write_u8(self.data_type as u8);
406 self.ciphertext.write_into(target);
407 target.write_many(self.nonce.0);
408 target.write_many(self.auth_tag.0);
409 }
410}
411
412impl Deserializable for EncryptedData {
413 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
414 let data_type = source.read_u8()?.try_into().map_err(|_| {
415 DeserializationError::InvalidValue("invalid encrypted-data type".to_string())
416 })?;
417 let ciphertext = Vec::<Felt>::read_from(source)?;
418 let nonce = Nonce(source.read()?);
419 let auth_tag = AuthTag(source.read()?);
420
421 Self::from_parts(data_type, ciphertext, auth_tag, nonce).map_err(|error| {
422 DeserializationError::InvalidValue(format!("malformed Eidos ciphertext: {error}"))
423 })
424 }
425}
426
427pub struct AeadEidos;
429
430impl AeadScheme for AeadEidos {
431 const KEY_SIZE: usize = SK_SIZE_BYTES;
432
433 type Key = SecretKey;
434
435 fn key_from_bytes(bytes: &[u8]) -> Result<Self::Key, EncryptionError> {
436 if bytes.len() != SK_SIZE_BYTES {
437 return Err(EncryptionError::FailedOperation);
438 }
439 SecretKey::read_from_bytes_with_budget(bytes, SK_SIZE_BYTES)
440 .map_err(|_| EncryptionError::FailedOperation)
441 }
442
443 fn key_from_uniform_bytes(bytes: &[u8]) -> Result<Self::Key, EncryptionError> {
444 if bytes.len() != SK_SIZE_BYTES {
445 return Err(EncryptionError::FailedOperation);
446 }
447
448 let (chunks, remainder) = bytes.as_chunks::<{ Felt::NUM_BYTES }>();
449 debug_assert!(remainder.is_empty());
450 Ok(SecretKey::from_elements(core::array::from_fn(|i| {
451 Felt::from_u64(u64::from_le_bytes(chunks[i]))
452 })))
453 }
454
455 fn encrypt_bytes<R: CryptoRng>(
456 key: &Self::Key,
457 rng: &mut R,
458 plaintext: &[u8],
459 associated_data: &[u8],
460 ) -> Result<Vec<u8>, EncryptionError> {
461 let encrypted =
462 key.encrypt_bytes_with_nonce(plaintext, associated_data, Nonce::with_rng(rng))?;
463 Ok(encrypted.to_bytes())
464 }
465
466 fn decrypt_bytes_with_associated_data(
467 key: &Self::Key,
468 ciphertext: &[u8],
469 associated_data: &[u8],
470 ) -> Result<Vec<u8>, EncryptionError> {
471 let encrypted = read_encrypted_data_strict(ciphertext)?;
472 key.decrypt_bytes_with_associated_data(&encrypted, associated_data)
473 }
474
475 fn encrypt_elements<R: CryptoRng>(
476 key: &Self::Key,
477 rng: &mut R,
478 plaintext: &[Felt],
479 associated_data: &[Felt],
480 ) -> Result<Vec<u8>, EncryptionError> {
481 let encrypted =
482 key.encrypt_elements_with_nonce(plaintext, associated_data, Nonce::with_rng(rng))?;
483 Ok(encrypted.to_bytes())
484 }
485
486 fn decrypt_elements_with_associated_data(
487 key: &Self::Key,
488 ciphertext: &[u8],
489 associated_data: &[Felt],
490 ) -> Result<Vec<Felt>, EncryptionError> {
491 let encrypted = read_encrypted_data_strict(ciphertext)?;
492 key.decrypt_elements_with_associated_data(&encrypted, associated_data)
493 }
494}
495
496fn read_encrypted_data_strict(ciphertext: &[u8]) -> Result<EncryptedData, EncryptionError> {
497 let mut reader = BudgetedReader::new(SliceReader::new(ciphertext), ciphertext.len());
498 let encrypted =
499 EncryptedData::read_from(&mut reader).map_err(|_| EncryptionError::FailedOperation)?;
500 if reader.has_more_bytes() {
501 return Err(EncryptionError::FailedOperation);
502 }
503 Ok(encrypted)
504}
505
506fn sample_felts<R: CryptoRng + ?Sized, const N: usize>(rng: &mut R) -> [Felt; N] {
507 let distribution =
508 Uniform::new(0, Felt::ORDER).expect("the field order defines a valid sampling range");
509 core::array::from_fn(|_| Felt::new_unchecked(distribution.sample(rng)))
510}
511
512fn bind_data_type(
513 data_type: DataType,
514 associated_data: &[Felt],
515) -> Result<Vec<Felt>, EncryptionError> {
516 let capacity = associated_data.len().checked_add(1).ok_or(EncryptionError::InputTooLong)?;
517 u32::try_from(capacity).map_err(|_| EncryptionError::InputTooLong)?;
518
519 let mut bound = Vec::with_capacity(capacity);
520 bound.push(Felt::from_u32(data_type as u8 as u32));
521 bound.extend_from_slice(associated_data);
522 Ok(bound)
523}
524
525fn validate_encryption_lengths(
526 plaintext_len: usize,
527 associated_data_len: usize,
528) -> Result<(), EncryptionError> {
529 let ciphertext_len = plaintext_len.checked_mul(2).ok_or(EncryptionError::InputTooLong)?;
530 let authenticated_data_len =
531 associated_data_len.checked_add(1).ok_or(EncryptionError::InputTooLong)?;
532 expanded::checked_mac_input_len(authenticated_data_len, ciphertext_len)
533 .ok_or(EncryptionError::InputTooLong)?;
534 Ok(())
535}
536
537fn validate_ciphertext(ciphertext: &[Felt]) -> Result<(), EncryptionError> {
538 if !ciphertext.len().is_multiple_of(2)
539 || ciphertext.iter().any(|felt| felt.as_canonical_u64() > u64::from(u32::MAX))
540 {
541 return Err(EncryptionError::MalformedCiphertext);
542 }
543 expanded::checked_mac_input_len(1, ciphertext.len()).ok_or(EncryptionError::InputTooLong)?;
546 Ok(())
547}
548
549fn ensure_data_type(
550 encrypted_data: &EncryptedData,
551 expected: DataType,
552) -> Result<(), EncryptionError> {
553 if encrypted_data.data_type != expected {
554 return Err(EncryptionError::InvalidDataType {
555 expected,
556 found: encrypted_data.data_type,
557 });
558 }
559 Ok(())
560}