1use std::marker::PhantomData;
66
67#[cfg(feature = "serde")]
68use serde::{Deserialize, Serialize};
69use subtle::ConstantTimeEq;
70use zeroize::Zeroize;
71
72use crate::constants::{
73 CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES, CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES,
74 CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES, CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES,
75 CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES, CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES,
76};
77use crate::error::Error;
78pub use crate::types::*;
79
80mod sealed {
81 pub trait Sealed {}
82}
83
84pub trait AeadAlgorithm:
90 sealed::Sealed + Clone + Copy + std::fmt::Debug + Default + Eq + PartialEq
91{
92}
93
94#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
96pub struct XChaCha20Poly1305Ietf;
97
98impl sealed::Sealed for XChaCha20Poly1305Ietf {}
99impl AeadAlgorithm for XChaCha20Poly1305Ietf {}
100
101#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
103pub struct ChaCha20Poly1305Ietf;
104
105impl sealed::Sealed for ChaCha20Poly1305Ietf {}
106impl AeadAlgorithm for ChaCha20Poly1305Ietf {}
107
108pub type Key = StackByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES>;
110pub type Nonce = StackByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES>;
112pub type Mac = StackByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES>;
114
115pub type DryocAead<Mac, Data> = AeadBox<XChaCha20Poly1305Ietf, Mac, Data>;
117pub type DryocAeadEnvelope<Nonce, Mac, Data> =
119 AeadEnvelope<XChaCha20Poly1305Ietf, Nonce, Mac, Data>;
120pub type VecBox = DryocAead<Mac, Vec<u8>>;
122pub type VecEnvelope = DryocAeadEnvelope<Nonce, Mac, Vec<u8>>;
124
125pub mod xchacha20poly1305_ietf {
127 #[cfg(any(all(feature = "protected", any(unix, windows)), all(doc, not(doctest))))]
128 pub use super::protected;
129 pub use super::{AeadAlgorithm, AeadBox, AeadEnvelope, XChaCha20Poly1305Ietf};
130
131 pub type Key = super::Key;
133 pub type Nonce = super::Nonce;
135 pub type Mac = super::Mac;
137 pub type DryocAead<Mac, Data> = super::DryocAead<Mac, Data>;
139 pub type DryocAeadEnvelope<Nonce, Mac, Data> = super::DryocAeadEnvelope<Nonce, Mac, Data>;
141 pub type VecBox = super::VecBox;
143 pub type VecEnvelope = super::VecEnvelope;
145}
146
147pub mod chacha20poly1305_ietf {
178 pub use super::{AeadAlgorithm, AeadBox, AeadEnvelope, ChaCha20Poly1305Ietf};
179 use crate::constants::{
180 CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES, CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES,
181 CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES,
182 };
183 pub use crate::types::*;
184
185 pub type Key = StackByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES>;
187 pub type Nonce = StackByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES>;
189 pub type Mac = StackByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES>;
191 pub type DryocAead<Mac, Data> = AeadBox<ChaCha20Poly1305Ietf, Mac, Data>;
193 pub type DryocAeadEnvelope<Nonce, Mac, Data> =
195 AeadEnvelope<ChaCha20Poly1305Ietf, Nonce, Mac, Data>;
196 pub type VecBox = DryocAead<Mac, Vec<u8>>;
198 pub type VecEnvelope = DryocAeadEnvelope<Nonce, Mac, Vec<u8>>;
200
201 #[cfg(any(all(feature = "protected", any(unix, windows)), all(doc, not(doctest))))]
202 #[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "protected")))]
203 pub mod protected {
204 use super::*;
206 pub use crate::protected::*;
207
208 pub type Key = HeapByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES>;
210 pub type Nonce = HeapByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES>;
212 pub type Mac = HeapByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES>;
214 pub type LockedBox = AeadBox<ChaCha20Poly1305Ietf, Locked<Mac>, LockedBytes>;
216 pub type LockedEnvelope =
218 AeadEnvelope<ChaCha20Poly1305Ietf, Locked<Nonce>, Locked<Mac>, LockedBytes>;
219 }
220}
221
222#[cfg(any(all(feature = "protected", any(unix, windows)), all(doc, not(doctest))))]
223#[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "protected")))]
224pub mod protected {
225 use super::*;
230 pub use crate::protected::*;
231
232 pub type Key = HeapByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES>;
234 pub type Nonce = HeapByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES>;
236 pub type Mac = HeapByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES>;
239
240 pub type LockedBox = AeadBox<XChaCha20Poly1305Ietf, Locked<Mac>, LockedBytes>;
242 pub type LockedEnvelope =
245 AeadEnvelope<XChaCha20Poly1305Ietf, Locked<Nonce>, Locked<Mac>, LockedBytes>;
246}
247
248#[cfg_attr(feature = "serde", derive(Clone, Debug, Serialize, Deserialize))]
249#[cfg_attr(not(feature = "serde"), derive(Clone, Debug))]
250pub struct AeadBox<Algorithm: AeadAlgorithm, Mac, Data> {
254 #[cfg_attr(feature = "serde", serde(skip))]
255 algorithm: PhantomData<Algorithm>,
256 tag: Mac,
257 data: Data,
258}
259
260#[cfg_attr(feature = "serde", derive(Clone, Debug, Serialize, Deserialize))]
261#[cfg_attr(not(feature = "serde"), derive(Clone, Debug))]
262pub struct AeadEnvelope<Algorithm: AeadAlgorithm, Nonce, Mac, Data> {
267 #[cfg_attr(feature = "serde", serde(skip))]
268 algorithm: PhantomData<Algorithm>,
269 nonce: Nonce,
270 tag: Mac,
271 data: Data,
272}
273
274#[cfg(feature = "wincode")]
275unsafe impl<C: wincode::config::Config> wincode::SchemaWrite<C> for VecBox {
279 type Src = Self;
280
281 fn size_of(src: &Self::Src) -> wincode::WriteResult<usize> {
282 Ok(<Vec<u8> as wincode::SchemaWrite<C>>::size_of(&src.data)?
283 + <[u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES] as wincode::SchemaWrite<
284 C,
285 >>::size_of(src.tag.as_array())?)
286 }
287
288 fn write(mut writer: impl wincode::io::Writer, src: &Self::Src) -> wincode::WriteResult<()> {
289 <Vec<u8> as wincode::SchemaWrite<C>>::write(writer.by_ref(), &src.data)?;
290 <[u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES] as wincode::SchemaWrite<C>>::write(
291 writer,
292 src.tag.as_array(),
293 )
294 }
295}
296
297#[cfg(feature = "wincode")]
298unsafe impl<'de, C: wincode::config::Config> wincode::SchemaRead<'de, C> for VecBox {
301 type Dst = Self;
302
303 fn read(
304 mut reader: impl wincode::io::Reader<'de>,
305 dst: &mut std::mem::MaybeUninit<Self::Dst>,
306 ) -> wincode::ReadResult<()> {
307 let data = <Vec<u8> as wincode::SchemaRead<'de, C>>::get(reader.by_ref())?;
308 let tag = <[u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES] as wincode::SchemaRead<
309 'de,
310 C,
311 >>::get(reader)?;
312 dst.write(Self {
313 algorithm: PhantomData,
314 tag: tag.into(),
315 data,
316 });
317 Ok(())
318 }
319}
320
321#[cfg(feature = "wincode")]
322unsafe impl<C: wincode::config::Config> wincode::SchemaWrite<C> for VecEnvelope {
326 type Src = Self;
327
328 fn size_of(src: &Self::Src) -> wincode::WriteResult<usize> {
329 Ok(
330 <[u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES] as wincode::SchemaWrite<
331 C,
332 >>::size_of(src.nonce.as_array())?
333 + <Vec<u8> as wincode::SchemaWrite<C>>::size_of(&src.data)?
334 + <[u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES] as wincode::SchemaWrite<
335 C,
336 >>::size_of(src.tag.as_array())?,
337 )
338 }
339
340 fn write(mut writer: impl wincode::io::Writer, src: &Self::Src) -> wincode::WriteResult<()> {
341 <[u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES] as wincode::SchemaWrite<C>>::write(
342 writer.by_ref(),
343 src.nonce.as_array(),
344 )?;
345 <Vec<u8> as wincode::SchemaWrite<C>>::write(writer.by_ref(), &src.data)?;
346 <[u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES] as wincode::SchemaWrite<C>>::write(
347 writer,
348 src.tag.as_array(),
349 )
350 }
351}
352
353#[cfg(feature = "wincode")]
354unsafe impl<'de, C: wincode::config::Config> wincode::SchemaRead<'de, C> for VecEnvelope {
358 type Dst = Self;
359
360 fn read(
361 mut reader: impl wincode::io::Reader<'de>,
362 dst: &mut std::mem::MaybeUninit<Self::Dst>,
363 ) -> wincode::ReadResult<()> {
364 let nonce = <[u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES] as wincode::SchemaRead<
365 'de,
366 C,
367 >>::get(reader.by_ref())?;
368 let data = <Vec<u8> as wincode::SchemaRead<'de, C>>::get(reader.by_ref())?;
369 let tag = <[u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES] as wincode::SchemaRead<
370 'de,
371 C,
372 >>::get(reader)?;
373 dst.write(Self {
374 algorithm: PhantomData,
375 nonce: nonce.into(),
376 tag: tag.into(),
377 data,
378 });
379 Ok(())
380 }
381}
382
383#[cfg(feature = "wincode")]
384unsafe impl<C: wincode::config::Config> wincode::SchemaWrite<C> for chacha20poly1305_ietf::VecBox {
388 type Src = Self;
389
390 fn size_of(src: &Self::Src) -> wincode::WriteResult<usize> {
391 Ok(<Vec<u8> as wincode::SchemaWrite<C>>::size_of(&src.data)?
392 + <[u8; CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES] as wincode::SchemaWrite<C>>::size_of(
393 src.tag.as_array(),
394 )?)
395 }
396
397 fn write(mut writer: impl wincode::io::Writer, src: &Self::Src) -> wincode::WriteResult<()> {
398 <Vec<u8> as wincode::SchemaWrite<C>>::write(writer.by_ref(), &src.data)?;
399 <[u8; CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES] as wincode::SchemaWrite<C>>::write(
400 writer,
401 src.tag.as_array(),
402 )
403 }
404}
405
406#[cfg(feature = "wincode")]
407unsafe impl<'de, C: wincode::config::Config> wincode::SchemaRead<'de, C>
411 for chacha20poly1305_ietf::VecBox
412{
413 type Dst = Self;
414
415 fn read(
416 mut reader: impl wincode::io::Reader<'de>,
417 dst: &mut std::mem::MaybeUninit<Self::Dst>,
418 ) -> wincode::ReadResult<()> {
419 let data = <Vec<u8> as wincode::SchemaRead<'de, C>>::get(reader.by_ref())?;
420 let tag = <[u8; CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES] as wincode::SchemaRead<
421 'de,
422 C,
423 >>::get(reader)?;
424 dst.write(Self {
425 algorithm: PhantomData,
426 tag: tag.into(),
427 data,
428 });
429 Ok(())
430 }
431}
432
433#[cfg(feature = "wincode")]
434unsafe impl<C: wincode::config::Config> wincode::SchemaWrite<C>
438 for chacha20poly1305_ietf::VecEnvelope
439{
440 type Src = Self;
441
442 fn size_of(src: &Self::Src) -> wincode::WriteResult<usize> {
443 Ok(
444 <[u8; CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES] as wincode::SchemaWrite<
445 C,
446 >>::size_of(src.nonce.as_array())?
447 + <Vec<u8> as wincode::SchemaWrite<C>>::size_of(&src.data)?
448 + <[u8; CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES] as wincode::SchemaWrite<
449 C,
450 >>::size_of(src.tag.as_array())?,
451 )
452 }
453
454 fn write(mut writer: impl wincode::io::Writer, src: &Self::Src) -> wincode::WriteResult<()> {
455 <[u8; CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES] as wincode::SchemaWrite<C>>::write(
456 writer.by_ref(),
457 src.nonce.as_array(),
458 )?;
459 <Vec<u8> as wincode::SchemaWrite<C>>::write(writer.by_ref(), &src.data)?;
460 <[u8; CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES] as wincode::SchemaWrite<C>>::write(
461 writer,
462 src.tag.as_array(),
463 )
464 }
465}
466
467#[cfg(feature = "wincode")]
468unsafe impl<'de, C: wincode::config::Config> wincode::SchemaRead<'de, C>
472 for chacha20poly1305_ietf::VecEnvelope
473{
474 type Dst = Self;
475
476 fn read(
477 mut reader: impl wincode::io::Reader<'de>,
478 dst: &mut std::mem::MaybeUninit<Self::Dst>,
479 ) -> wincode::ReadResult<()> {
480 let nonce = <[u8; CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES] as wincode::SchemaRead<
481 'de,
482 C,
483 >>::get(reader.by_ref())?;
484 let data = <Vec<u8> as wincode::SchemaRead<'de, C>>::get(reader.by_ref())?;
485 let tag = <[u8; CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES] as wincode::SchemaRead<
486 'de,
487 C,
488 >>::get(reader)?;
489 dst.write(Self {
490 algorithm: PhantomData,
491 nonce: nonce.into(),
492 tag: tag.into(),
493 data,
494 });
495 Ok(())
496 }
497}
498
499impl<Algorithm: AeadAlgorithm, Mac: Zeroize, Data: Zeroize> Zeroize
500 for AeadBox<Algorithm, Mac, Data>
501{
502 fn zeroize(&mut self) {
503 self.tag.zeroize();
504 self.data.zeroize();
505 }
506}
507
508impl<Algorithm: AeadAlgorithm, Nonce: Zeroize, Mac: Zeroize, Data: Zeroize> Zeroize
509 for AeadEnvelope<Algorithm, Nonce, Mac, Data>
510{
511 fn zeroize(&mut self) {
512 self.nonce.zeroize();
513 self.tag.zeroize();
514 self.data.zeroize();
515 }
516}
517
518impl<
519 Mac: NewByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES> + Zeroize,
520 Data: NewBytes + ResizableBytes + Zeroize,
521> AeadBox<XChaCha20Poly1305Ietf, Mac, Data>
522{
523 pub fn encrypt<
530 Message: Bytes + ?Sized,
531 Nonce: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES>,
532 SecretKey: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES>,
533 >(
534 message: &Message,
535 associated_data: Option<&[u8]>,
536 nonce: &Nonce,
537 key: &SecretKey,
538 ) -> Result<Self, Error> {
539 use crate::classic::crypto_aead_xchacha20poly1305_ietf::crypto_aead_xchacha20poly1305_ietf_encrypt_detached;
540
541 let mut new = Self {
542 algorithm: PhantomData,
543 tag: Mac::new_byte_array(),
544 data: Data::new_bytes(),
545 };
546 new.data.resize(message.len(), 0);
547
548 crypto_aead_xchacha20poly1305_ietf_encrypt_detached(
549 new.data.as_mut_slice(),
550 new.tag.as_mut_array(),
551 message.as_slice(),
552 associated_data,
553 nonce.as_array(),
554 key.as_array(),
555 )?;
556
557 Ok(new)
558 }
559}
560
561impl<
562 Nonce: NewByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES> + Zeroize,
563 Mac: NewByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES> + Zeroize,
564 Data: NewBytes + ResizableBytes + Zeroize,
565> AeadEnvelope<XChaCha20Poly1305Ietf, Nonce, Mac, Data>
566{
567 pub fn seal<
579 Message: Bytes + ?Sized,
580 SecretKey: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES>,
581 >(
582 message: &Message,
583 associated_data: Option<&[u8]>,
584 key: &SecretKey,
585 ) -> Result<Self, Error> {
586 let nonce = Nonce::generate();
587 let aead_box = AeadBox::<XChaCha20Poly1305Ietf, Mac, Data>::encrypt(
588 message,
589 associated_data,
590 &nonce,
591 key,
592 )?;
593 let (tag, data) = aead_box.into_parts();
594
595 Ok(Self {
596 algorithm: PhantomData,
597 nonce,
598 tag,
599 data,
600 })
601 }
602}
603
604impl<
605 'a,
606 Mac: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES>
607 + std::convert::TryFrom<&'a [u8]>
608 + Zeroize,
609 Data: Bytes + From<&'a [u8]> + Zeroize,
610> AeadBox<XChaCha20Poly1305Ietf, Mac, Data>
611{
612 pub fn from_bytes(bytes: &'a [u8]) -> Result<Self, Error> {
619 if bytes.len() < CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES {
620 Err(length_error!(
621 crate::ErrorContext::AeadCiphertext,
622 bytes.len(),
623 min CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES
624 ))
625 } else {
626 let (data, tag) =
627 bytes.split_at(bytes.len() - CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES);
628 Ok(Self {
629 algorithm: PhantomData,
630 tag: Mac::try_from(tag)
631 .map_err(|_| Error::invalid_encoding(crate::ErrorContext::AuthenticationTag))?,
632 data: Data::from(data),
633 })
634 }
635 }
636}
637
638impl<
639 'a,
640 Nonce: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES>
641 + std::convert::TryFrom<&'a [u8]>
642 + Zeroize,
643 Mac: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES>
644 + std::convert::TryFrom<&'a [u8]>
645 + Zeroize,
646 Data: Bytes + From<&'a [u8]> + Zeroize,
647> AeadEnvelope<XChaCha20Poly1305Ietf, Nonce, Mac, Data>
648{
649 pub fn from_bytes(bytes: &'a [u8]) -> Result<Self, Error> {
657 let minimum_len = CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES
658 + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES;
659 if bytes.len() < minimum_len {
660 Err(length_error!(crate::ErrorContext::AeadEnvelope, bytes.len(), min minimum_len))
661 } else {
662 let (nonce, rest) = bytes.split_at(CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES);
663 let (data, tag) = rest.split_at(rest.len() - CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES);
664 Ok(Self {
665 algorithm: PhantomData,
666 nonce: Nonce::try_from(nonce)
667 .map_err(|_| Error::invalid_encoding(crate::ErrorContext::Nonce))?,
668 tag: Mac::try_from(tag)
669 .map_err(|_| Error::invalid_encoding(crate::ErrorContext::AuthenticationTag))?,
670 data: Data::from(data),
671 })
672 }
673 }
674}
675
676impl<
677 Mac: NewByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES> + Zeroize,
678 Data: NewBytes + ResizableBytes + Zeroize,
679> AeadBox<ChaCha20Poly1305Ietf, Mac, Data>
680{
681 pub fn encrypt<
688 Message: Bytes + ?Sized,
689 Nonce: ByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES>,
690 SecretKey: ByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES>,
691 >(
692 message: &Message,
693 associated_data: Option<&[u8]>,
694 nonce: &Nonce,
695 key: &SecretKey,
696 ) -> Result<Self, Error> {
697 use crate::classic::crypto_aead_chacha20poly1305_ietf::crypto_aead_chacha20poly1305_ietf_encrypt_detached;
698
699 let mut new = Self {
700 algorithm: PhantomData,
701 tag: Mac::new_byte_array(),
702 data: Data::new_bytes(),
703 };
704 new.data.resize(message.len(), 0);
705
706 crypto_aead_chacha20poly1305_ietf_encrypt_detached(
707 new.data.as_mut_slice(),
708 new.tag.as_mut_array(),
709 message.as_slice(),
710 associated_data,
711 nonce.as_array(),
712 key.as_array(),
713 )?;
714
715 Ok(new)
716 }
717}
718
719impl<
720 'a,
721 Mac: ByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES> + std::convert::TryFrom<&'a [u8]> + Zeroize,
722 Data: Bytes + From<&'a [u8]> + Zeroize,
723> AeadBox<ChaCha20Poly1305Ietf, Mac, Data>
724{
725 pub fn from_bytes(bytes: &'a [u8]) -> Result<Self, Error> {
732 if bytes.len() < CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES {
733 Err(length_error!(
734 crate::ErrorContext::AeadCiphertext,
735 bytes.len(),
736 min CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES
737 ))
738 } else {
739 let (data, tag) =
740 bytes.split_at(bytes.len() - CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES);
741 Ok(Self {
742 algorithm: PhantomData,
743 tag: Mac::try_from(tag)
744 .map_err(|_| Error::invalid_encoding(crate::ErrorContext::AuthenticationTag))?,
745 data: Data::from(data),
746 })
747 }
748 }
749}
750
751impl<
752 'a,
753 Nonce: ByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES>
754 + std::convert::TryFrom<&'a [u8]>
755 + Zeroize,
756 Mac: ByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES> + std::convert::TryFrom<&'a [u8]> + Zeroize,
757 Data: Bytes + From<&'a [u8]> + Zeroize,
758> AeadEnvelope<ChaCha20Poly1305Ietf, Nonce, Mac, Data>
759{
760 pub fn from_bytes(bytes: &'a [u8]) -> Result<Self, Error> {
768 let minimum_len =
769 CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES + CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES;
770 if bytes.len() < minimum_len {
771 Err(length_error!(crate::ErrorContext::AeadEnvelope, bytes.len(), min minimum_len))
772 } else {
773 let (nonce, rest) = bytes.split_at(CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES);
774 let (data, tag) = rest.split_at(rest.len() - CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES);
775 Ok(Self {
776 algorithm: PhantomData,
777 nonce: Nonce::try_from(nonce)
778 .map_err(|_| Error::invalid_encoding(crate::ErrorContext::Nonce))?,
779 tag: Mac::try_from(tag)
780 .map_err(|_| Error::invalid_encoding(crate::ErrorContext::AuthenticationTag))?,
781 data: Data::from(data),
782 })
783 }
784 }
785}
786
787impl<Mac: ByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES>, Data: Bytes>
788 AeadBox<ChaCha20Poly1305Ietf, Mac, Data>
789{
790 pub fn decrypt<
799 Output: ResizableBytes + NewBytes,
800 Nonce: ByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES>,
801 SecretKey: ByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES>,
802 >(
803 &self,
804 associated_data: Option<&[u8]>,
805 nonce: &Nonce,
806 key: &SecretKey,
807 ) -> Result<Output, Error> {
808 use crate::classic::crypto_aead_chacha20poly1305_ietf::crypto_aead_chacha20poly1305_ietf_decrypt_detached;
809
810 let mut message = Output::new_bytes();
811 message.resize(self.data.as_slice().len(), 0);
812
813 crypto_aead_chacha20poly1305_ietf_decrypt_detached(
814 message.as_mut_slice(),
815 self.data.as_slice(),
816 self.tag.as_array(),
817 associated_data,
818 nonce.as_array(),
819 key.as_array(),
820 )?;
821
822 Ok(message)
823 }
824}
825
826impl<
827 Nonce: ByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES>,
828 Mac: ByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES>,
829 Data: Bytes,
830> AeadEnvelope<ChaCha20Poly1305Ietf, Nonce, Mac, Data>
831{
832 pub fn open<
842 Output: ResizableBytes + NewBytes,
843 SecretKey: ByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES>,
844 >(
845 &self,
846 associated_data: Option<&[u8]>,
847 key: &SecretKey,
848 ) -> Result<Output, Error> {
849 use crate::classic::crypto_aead_chacha20poly1305_ietf::crypto_aead_chacha20poly1305_ietf_decrypt_detached;
850
851 let mut message = Output::new_bytes();
852 message.resize(self.data.as_slice().len(), 0);
853
854 crypto_aead_chacha20poly1305_ietf_decrypt_detached(
855 message.as_mut_slice(),
856 self.data.as_slice(),
857 self.tag.as_array(),
858 associated_data,
859 self.nonce.as_array(),
860 key.as_array(),
861 )?;
862
863 Ok(message)
864 }
865}
866
867impl AeadBox<ChaCha20Poly1305Ietf, chacha20poly1305_ietf::Mac, Vec<u8>> {
868 pub fn encrypt_to_vecbox<
875 Message: Bytes + ?Sized,
876 SecretKey: ByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES>,
877 >(
878 message: &Message,
879 associated_data: Option<&[u8]>,
880 nonce: &chacha20poly1305_ietf::Nonce,
881 key: &SecretKey,
882 ) -> Result<Self, Error> {
883 Self::encrypt(message, associated_data, nonce, key)
884 }
885
886 pub fn decrypt_to_vec<SecretKey: ByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES>>(
894 &self,
895 associated_data: Option<&[u8]>,
896 nonce: &chacha20poly1305_ietf::Nonce,
897 key: &SecretKey,
898 ) -> Result<Vec<u8>, Error> {
899 self.decrypt(associated_data, nonce, key)
900 }
901
902 pub fn into_vec(mut self) -> Vec<u8> {
904 self.data.resize(
905 self.data.len() + CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES,
906 0,
907 );
908 let tag_offset = self.data.len() - CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES;
909 self.data[tag_offset..].copy_from_slice(self.tag.as_slice());
910 self.data
911 }
912}
913
914impl
915 AeadEnvelope<
916 ChaCha20Poly1305Ietf,
917 chacha20poly1305_ietf::Nonce,
918 chacha20poly1305_ietf::Mac,
919 Vec<u8>,
920 >
921{
922 pub fn open_to_vec<SecretKey: ByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES>>(
930 &self,
931 associated_data: Option<&[u8]>,
932 key: &SecretKey,
933 ) -> Result<Vec<u8>, Error> {
934 self.open(associated_data, key)
935 }
936
937 pub fn into_vec(self) -> Vec<u8> {
939 let mut output = self.nonce.to_vec();
940 output.extend_from_slice(self.data.as_slice());
941 output.extend_from_slice(self.tag.as_slice());
942 output
943 }
944}
945
946impl<Algorithm: AeadAlgorithm, Mac, Data> AeadBox<Algorithm, Mac, Data> {
947 pub fn from_parts(tag: Mac, data: Data) -> Self {
949 Self {
950 algorithm: PhantomData,
951 tag,
952 data,
953 }
954 }
955
956 pub fn tag(&self) -> &Mac {
958 &self.tag
959 }
960
961 pub fn data(&self) -> &Data {
963 &self.data
964 }
965
966 pub fn into_parts(self) -> (Mac, Data) {
968 (self.tag, self.data)
969 }
970}
971
972impl<Algorithm: AeadAlgorithm, Mac: Bytes, Data: Bytes> AeadBox<Algorithm, Mac, Data> {
973 pub fn to_vec(&self) -> Vec<u8> {
975 self.to_bytes()
976 }
977
978 pub fn to_bytes<Output: NewBytes + ResizableBytes>(&self) -> Output {
980 let mut data = Output::new_bytes();
981 data.resize(self.data.len() + self.tag.len(), 0);
982 let s = data.as_mut_slice();
983 s[..self.data.len()].copy_from_slice(self.data.as_slice());
984 s[self.data.len()..].copy_from_slice(self.tag.as_slice());
985 data
986 }
987}
988
989impl<Mac: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES>, Data: Bytes>
990 AeadBox<XChaCha20Poly1305Ietf, Mac, Data>
991{
992 pub fn decrypt<
1001 Output: ResizableBytes + NewBytes,
1002 Nonce: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES>,
1003 SecretKey: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES>,
1004 >(
1005 &self,
1006 associated_data: Option<&[u8]>,
1007 nonce: &Nonce,
1008 key: &SecretKey,
1009 ) -> Result<Output, Error> {
1010 use crate::classic::crypto_aead_xchacha20poly1305_ietf::crypto_aead_xchacha20poly1305_ietf_decrypt_detached;
1011
1012 let mut message = Output::new_bytes();
1013 message.resize(self.data.as_slice().len(), 0);
1014
1015 crypto_aead_xchacha20poly1305_ietf_decrypt_detached(
1016 message.as_mut_slice(),
1017 self.data.as_slice(),
1018 self.tag.as_array(),
1019 associated_data,
1020 nonce.as_array(),
1021 key.as_array(),
1022 )?;
1023
1024 Ok(message)
1025 }
1026}
1027
1028impl<Algorithm: AeadAlgorithm, Nonce, Mac, Data> AeadEnvelope<Algorithm, Nonce, Mac, Data> {
1029 pub fn from_parts(nonce: Nonce, tag: Mac, data: Data) -> Self {
1031 Self {
1032 algorithm: PhantomData,
1033 nonce,
1034 tag,
1035 data,
1036 }
1037 }
1038
1039 pub fn nonce(&self) -> &Nonce {
1041 &self.nonce
1042 }
1043
1044 pub fn tag(&self) -> &Mac {
1046 &self.tag
1047 }
1048
1049 pub fn data(&self) -> &Data {
1051 &self.data
1052 }
1053
1054 pub fn into_parts(self) -> (Nonce, Mac, Data) {
1056 (self.nonce, self.tag, self.data)
1057 }
1058}
1059
1060impl<Algorithm: AeadAlgorithm, Nonce: Bytes, Mac: Bytes, Data: Bytes>
1061 AeadEnvelope<Algorithm, Nonce, Mac, Data>
1062{
1063 pub fn to_vec(&self) -> Vec<u8> {
1065 self.to_bytes()
1066 }
1067
1068 pub fn to_bytes<Output: NewBytes + ResizableBytes>(&self) -> Output {
1070 let mut data = Output::new_bytes();
1071 data.resize(self.nonce.len() + self.data.len() + self.tag.len(), 0);
1072 let s = data.as_mut_slice();
1073 s[..self.nonce.len()].copy_from_slice(self.nonce.as_slice());
1074 s[self.nonce.len()..self.nonce.len() + self.data.len()]
1075 .copy_from_slice(self.data.as_slice());
1076 s[self.nonce.len() + self.data.len()..].copy_from_slice(self.tag.as_slice());
1077 data
1078 }
1079}
1080
1081impl<
1082 Nonce: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES>,
1083 Mac: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES>,
1084 Data: Bytes,
1085> AeadEnvelope<XChaCha20Poly1305Ietf, Nonce, Mac, Data>
1086{
1087 pub fn open<
1097 Output: ResizableBytes + NewBytes,
1098 SecretKey: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES>,
1099 >(
1100 &self,
1101 associated_data: Option<&[u8]>,
1102 key: &SecretKey,
1103 ) -> Result<Output, Error> {
1104 use crate::classic::crypto_aead_xchacha20poly1305_ietf::crypto_aead_xchacha20poly1305_ietf_decrypt_detached;
1105
1106 let mut message = Output::new_bytes();
1107 message.resize(self.data.as_slice().len(), 0);
1108
1109 crypto_aead_xchacha20poly1305_ietf_decrypt_detached(
1110 message.as_mut_slice(),
1111 self.data.as_slice(),
1112 self.tag.as_array(),
1113 associated_data,
1114 self.nonce.as_array(),
1115 key.as_array(),
1116 )?;
1117
1118 Ok(message)
1119 }
1120}
1121
1122impl DryocAead<Mac, Vec<u8>> {
1123 pub fn encrypt_to_vecbox<
1130 Message: Bytes + ?Sized,
1131 SecretKey: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES>,
1132 >(
1133 message: &Message,
1134 associated_data: Option<&[u8]>,
1135 nonce: &Nonce,
1136 key: &SecretKey,
1137 ) -> Result<Self, Error> {
1138 Self::encrypt(message, associated_data, nonce, key)
1139 }
1140
1141 pub fn decrypt_to_vec<SecretKey: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES>>(
1149 &self,
1150 associated_data: Option<&[u8]>,
1151 nonce: &Nonce,
1152 key: &SecretKey,
1153 ) -> Result<Vec<u8>, Error> {
1154 self.decrypt(associated_data, nonce, key)
1155 }
1156
1157 pub fn into_vec(mut self) -> Vec<u8> {
1159 self.data.resize(
1160 self.data.len() + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES,
1161 0,
1162 );
1163 let tag_offset = self.data.len() - CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES;
1164 self.data[tag_offset..].copy_from_slice(self.tag.as_slice());
1165 self.data
1166 }
1167}
1168
1169impl DryocAeadEnvelope<Nonce, Mac, Vec<u8>> {
1170 pub fn seal_to_vec<
1181 Message: Bytes + ?Sized,
1182 SecretKey: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES>,
1183 >(
1184 message: &Message,
1185 associated_data: Option<&[u8]>,
1186 key: &SecretKey,
1187 ) -> Result<Self, Error> {
1188 Self::seal(message, associated_data, key)
1189 }
1190
1191 pub fn open_to_vec<SecretKey: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES>>(
1199 &self,
1200 associated_data: Option<&[u8]>,
1201 key: &SecretKey,
1202 ) -> Result<Vec<u8>, Error> {
1203 self.open(associated_data, key)
1204 }
1205
1206 pub fn into_vec(self) -> Vec<u8> {
1208 let mut output = self.nonce.to_vec();
1209 output.extend_from_slice(self.data.as_slice());
1210 output.extend_from_slice(self.tag.as_slice());
1211 output
1212 }
1213}
1214
1215impl<'a, Algorithm: AeadAlgorithm, Mac, Data: From<&'a [u8]>> AeadBox<Algorithm, Mac, Data> {
1216 pub fn with_data_and_mac(tag: Mac, input: &'a [u8]) -> Self {
1219 Self {
1220 algorithm: PhantomData,
1221 tag,
1222 data: input.into(),
1223 }
1224 }
1225}
1226
1227impl<'a, Algorithm: AeadAlgorithm, Nonce, Mac, Data: From<&'a [u8]>>
1228 AeadEnvelope<Algorithm, Nonce, Mac, Data>
1229{
1230 pub fn with_nonce_data_and_mac(nonce: Nonce, tag: Mac, input: &'a [u8]) -> Self {
1233 Self {
1234 algorithm: PhantomData,
1235 nonce,
1236 tag,
1237 data: input.into(),
1238 }
1239 }
1240}
1241
1242impl<Algorithm: AeadAlgorithm, Mac: Bytes, Data: Bytes> PartialEq
1243 for AeadBox<Algorithm, Mac, Data>
1244{
1245 fn eq(&self, other: &Self) -> bool {
1246 self.tag.as_slice().ct_eq(other.tag.as_slice()).unwrap_u8() == 1
1247 && self
1248 .data
1249 .as_slice()
1250 .ct_eq(other.data.as_slice())
1251 .unwrap_u8()
1252 == 1
1253 }
1254}
1255
1256impl<Algorithm: AeadAlgorithm, Nonce: Bytes, Mac: Bytes, Data: Bytes> PartialEq
1257 for AeadEnvelope<Algorithm, Nonce, Mac, Data>
1258{
1259 fn eq(&self, other: &Self) -> bool {
1260 self.nonce
1261 .as_slice()
1262 .ct_eq(other.nonce.as_slice())
1263 .unwrap_u8()
1264 == 1
1265 && self.tag.as_slice().ct_eq(other.tag.as_slice()).unwrap_u8() == 1
1266 && self
1267 .data
1268 .as_slice()
1269 .ct_eq(other.data.as_slice())
1270 .unwrap_u8()
1271 == 1
1272 }
1273}
1274
1275#[cfg(test)]
1276mod tests {
1277 use super::*;
1278
1279 #[test]
1280 fn test_explicit_box_layout() {
1281 let key = Key::generate();
1282 let nonce = Nonce::generate();
1283 let message = b"hello";
1284 let aad = b"metadata";
1285
1286 let aead = VecBox::encrypt_to_vecbox(message, Some(aad), &nonce, &key).expect("encrypt");
1287 let bytes = aead.to_vec();
1288 assert_eq!(
1289 bytes.len(),
1290 message.len() + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES
1291 );
1292
1293 let parsed = VecBox::from_bytes(&bytes).expect("from bytes");
1294 let decrypted = parsed
1295 .decrypt_to_vec(Some(aad), &nonce, &key)
1296 .expect("decrypt");
1297 assert_eq!(decrypted, message);
1298 }
1299
1300 #[test]
1301 fn test_explicit_box_failures() {
1302 let key = Key::generate();
1303 let nonce = Nonce::generate();
1304 let message = b"hello";
1305 let aad = b"metadata";
1306
1307 let aead = VecBox::encrypt_to_vecbox(message, Some(aad), &nonce, &key).expect("encrypt");
1308
1309 aead.decrypt_to_vec(Some(b"wrong aad"), &nonce, &key)
1310 .expect_err("wrong aad should fail");
1311
1312 let mut wrong_key = key.clone();
1313 wrong_key.as_mut_slice()[0] ^= 1;
1314 aead.decrypt_to_vec(Some(aad), &nonce, &wrong_key)
1315 .expect_err("wrong key should fail");
1316
1317 let mut wrong_nonce = nonce.clone();
1318 wrong_nonce.as_mut_slice()[0] ^= 1;
1319 aead.decrypt_to_vec(Some(aad), &wrong_nonce, &key)
1320 .expect_err("wrong nonce should fail");
1321
1322 let mut modified_ciphertext = aead.clone();
1323 modified_ciphertext.data.as_mut_slice()[0] ^= 1;
1324 modified_ciphertext
1325 .decrypt_to_vec(Some(aad), &nonce, &key)
1326 .expect_err("modified ciphertext should fail");
1327
1328 let mut modified_tag = aead.clone();
1329 modified_tag.tag.as_mut_slice()[0] ^= 1;
1330 modified_tag
1331 .decrypt_to_vec(Some(aad), &nonce, &key)
1332 .expect_err("modified tag should fail");
1333 }
1334
1335 #[test]
1336 fn test_explicit_box_empty_message_and_no_aad() {
1337 let key = Key::generate();
1338 let nonce = Nonce::generate();
1339
1340 let aead = VecBox::encrypt_to_vecbox(&[], None, &nonce, &key).expect("encrypt");
1341 assert_eq!(
1342 aead.to_vec().len(),
1343 CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES
1344 );
1345
1346 let decrypted = aead
1347 .decrypt_to_vec(None, &nonce, &key)
1348 .expect("decrypt empty");
1349 assert!(decrypted.is_empty());
1350 }
1351
1352 #[test]
1353 fn test_envelope_layout() {
1354 let key = Key::generate();
1355 let message = b"hello";
1356 let aad = b"metadata";
1357
1358 let envelope = VecEnvelope::seal_to_vec(message, Some(aad), &key).expect("seal");
1359 let bytes = envelope.to_vec();
1360 assert_eq!(
1361 bytes.len(),
1362 CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES
1363 + message.len()
1364 + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES
1365 );
1366 assert_eq!(
1367 &bytes[..CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES],
1368 envelope.nonce().as_slice()
1369 );
1370
1371 let parsed = VecEnvelope::from_bytes(&bytes).expect("from bytes");
1372 let decrypted = parsed.open_to_vec(Some(aad), &key).expect("open");
1373 assert_eq!(decrypted, message);
1374 }
1375
1376 #[test]
1377 fn test_envelope_failures() {
1378 let key = Key::generate();
1379 let message = b"hello";
1380 let aad = b"metadata";
1381
1382 let envelope = VecEnvelope::seal_to_vec(message, Some(aad), &key).expect("seal");
1383
1384 envelope
1385 .open_to_vec(Some(b"wrong aad"), &key)
1386 .expect_err("wrong aad should fail");
1387
1388 let mut wrong_key = key.clone();
1389 wrong_key.as_mut_slice()[0] ^= 1;
1390 envelope
1391 .open_to_vec(Some(aad), &wrong_key)
1392 .expect_err("wrong key should fail");
1393
1394 let mut modified_nonce = envelope.clone();
1395 modified_nonce.nonce.as_mut_slice()[0] ^= 1;
1396 modified_nonce
1397 .open_to_vec(Some(aad), &key)
1398 .expect_err("modified nonce should fail");
1399
1400 let mut modified_ciphertext = envelope.clone();
1401 modified_ciphertext.data.as_mut_slice()[0] ^= 1;
1402 modified_ciphertext
1403 .open_to_vec(Some(aad), &key)
1404 .expect_err("modified ciphertext should fail");
1405
1406 let mut modified_tag = envelope.clone();
1407 modified_tag.tag.as_mut_slice()[0] ^= 1;
1408 modified_tag
1409 .open_to_vec(Some(aad), &key)
1410 .expect_err("modified tag should fail");
1411 }
1412
1413 #[test]
1414 fn test_envelope_empty_message_and_no_aad() {
1415 let key = Key::generate();
1416
1417 let envelope = VecEnvelope::seal_to_vec(&[], None, &key).expect("seal");
1418 assert_eq!(
1419 envelope.to_vec().len(),
1420 CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES
1421 + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES
1422 );
1423
1424 let decrypted = envelope.open_to_vec(None, &key).expect("open empty");
1425 assert!(decrypted.is_empty());
1426 }
1427
1428 #[test]
1429 fn test_from_bytes_boundaries() {
1430 assert!(VecBox::from_bytes(&[]).is_err());
1431
1432 let empty_box_bytes = [0u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES];
1433 let empty_box = VecBox::from_bytes(&empty_box_bytes).expect("empty box parses");
1434 assert!(empty_box.data().is_empty());
1435 assert_eq!(empty_box.tag().as_slice(), empty_box_bytes.as_slice());
1436
1437 let short_envelope_bytes = [0u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES
1438 + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES
1439 - 1];
1440 assert!(VecEnvelope::from_bytes(&short_envelope_bytes).is_err());
1441
1442 let empty_envelope_bytes = [0u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES
1443 + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES];
1444 let empty_envelope =
1445 VecEnvelope::from_bytes(&empty_envelope_bytes).expect("empty envelope parses");
1446 assert!(empty_envelope.data().is_empty());
1447 assert_eq!(
1448 empty_envelope.nonce().as_slice(),
1449 &empty_envelope_bytes[..CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES]
1450 );
1451 assert_eq!(
1452 empty_envelope.tag().as_slice(),
1453 &empty_envelope_bytes[CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES..]
1454 );
1455 }
1456
1457 #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
1458 mod property_tests {
1459 use proptest::prelude::*;
1460
1461 use super::*;
1462 use crate::classic::crypto_aead_xchacha20poly1305_ietf::{
1463 Mac as ClassicMac, crypto_aead_xchacha20poly1305_ietf_decrypt,
1464 crypto_aead_xchacha20poly1305_ietf_decrypt_detached,
1465 crypto_aead_xchacha20poly1305_ietf_decrypt_inplace,
1466 crypto_aead_xchacha20poly1305_ietf_encrypt,
1467 crypto_aead_xchacha20poly1305_ietf_encrypt_detached,
1468 crypto_aead_xchacha20poly1305_ietf_encrypt_inplace,
1469 };
1470
1471 fn length_strategy(max: usize) -> impl Strategy<Value = usize> {
1472 prop_oneof![
1473 Just(0usize),
1474 Just(1),
1475 Just(15),
1476 Just(16),
1477 Just(17),
1478 Just(63),
1479 Just(64),
1480 Just(65),
1481 Just(max.saturating_sub(1)),
1482 Just(max),
1483 0usize..=max,
1484 ]
1485 }
1486
1487 fn bytes_strategy(max: usize) -> impl Strategy<Value = Vec<u8>> {
1488 length_strategy(max).prop_flat_map(|len| prop::collection::vec(any::<u8>(), len))
1489 }
1490
1491 fn aad_strategy() -> impl Strategy<Value = Option<Vec<u8>>> {
1492 prop::option::of(bytes_strategy(256))
1493 }
1494
1495 proptest! {
1496 #![proptest_config(ProptestConfig::with_cases(96))]
1497
1498 #[test]
1499 fn proptest_classic_modes_and_rustaceous_layouts_agree(
1500 key in any::<[u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES]>(),
1501 nonce in any::<[u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES]>(),
1502 aad in aad_strategy(),
1503 message in bytes_strategy(512),
1504 ) {
1505 let aad = aad.as_deref();
1506
1507 let mut combined =
1508 vec![0u8; message.len() + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES];
1509 crypto_aead_xchacha20poly1305_ietf_encrypt(
1510 &mut combined,
1511 &message,
1512 aad,
1513 &nonce,
1514 &key,
1515 )
1516 .expect("classic combined encrypt");
1517
1518 let mut decrypted = vec![0u8; message.len()];
1519 crypto_aead_xchacha20poly1305_ietf_decrypt(
1520 &mut decrypted,
1521 &combined,
1522 aad,
1523 &nonce,
1524 &key,
1525 )
1526 .expect("classic combined decrypt");
1527 prop_assert_eq!(decrypted.as_slice(), message.as_slice());
1528
1529 let mut detached = vec![0u8; message.len()];
1530 let mut mac = ClassicMac::default();
1531 crypto_aead_xchacha20poly1305_ietf_encrypt_detached(
1532 &mut detached,
1533 &mut mac,
1534 &message,
1535 aad,
1536 &nonce,
1537 &key,
1538 )
1539 .expect("classic detached encrypt");
1540 prop_assert_eq!(&detached, &combined[..message.len()]);
1541 prop_assert_eq!(mac.as_slice(), &combined[message.len()..]);
1542
1543 let mut detached_decrypted = vec![0u8; message.len()];
1544 crypto_aead_xchacha20poly1305_ietf_decrypt_detached(
1545 &mut detached_decrypted,
1546 &detached,
1547 &mac,
1548 aad,
1549 &nonce,
1550 &key,
1551 )
1552 .expect("classic detached decrypt");
1553 prop_assert_eq!(detached_decrypted.as_slice(), message.as_slice());
1554
1555 let mut inplace = message.clone();
1556 inplace.resize(message.len() + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES, 0);
1557 crypto_aead_xchacha20poly1305_ietf_encrypt_inplace(
1558 &mut inplace,
1559 aad,
1560 &nonce,
1561 &key,
1562 )
1563 .expect("classic inplace encrypt");
1564 prop_assert_eq!(&inplace, &combined);
1565
1566 crypto_aead_xchacha20poly1305_ietf_decrypt_inplace(
1567 &mut inplace,
1568 aad,
1569 &nonce,
1570 &key,
1571 )
1572 .expect("classic inplace decrypt");
1573 prop_assert_eq!(&inplace[..message.len()], message.as_slice());
1574
1575 let rust_key = Key::from(key);
1576 let rust_nonce = Nonce::from(nonce);
1577 let aead = VecBox::encrypt_to_vecbox(&message, aad, &rust_nonce, &rust_key)
1578 .expect("rustaceous encrypt");
1579 let aead_bytes = aead.to_vec();
1580 prop_assert_eq!(aead_bytes.as_slice(), combined.as_slice());
1581 let aead_decrypted = aead
1582 .decrypt_to_vec(aad, &rust_nonce, &rust_key)
1583 .expect("rustaceous decrypt");
1584 prop_assert_eq!(aead_decrypted.as_slice(), message.as_slice());
1585
1586 let mut envelope_bytes = rust_nonce.to_vec();
1587 envelope_bytes.extend_from_slice(&combined);
1588 let envelope = VecEnvelope::from_bytes(&envelope_bytes).expect("envelope parses");
1589 prop_assert_eq!(envelope.to_vec(), envelope_bytes);
1590 let envelope_decrypted = envelope
1591 .open_to_vec(aad, &rust_key)
1592 .expect("rustaceous envelope open");
1593 prop_assert_eq!(envelope_decrypted.as_slice(), message.as_slice());
1594 }
1595
1596 #[test]
1597 fn proptest_tampering_is_rejected_without_mutating_outputs(
1598 key in any::<[u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES]>(),
1599 nonce in any::<[u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES]>(),
1600 aad in aad_strategy(),
1601 message in bytes_strategy(512),
1602 tamper_index in any::<usize>(),
1603 ) {
1604 let aad = aad.as_deref();
1605 let rust_key = Key::from(key);
1606 let rust_nonce = Nonce::from(nonce);
1607 let mut combined =
1608 vec![0u8; message.len() + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES];
1609 crypto_aead_xchacha20poly1305_ietf_encrypt(
1610 &mut combined,
1611 &message,
1612 aad,
1613 &nonce,
1614 &key,
1615 )
1616 .expect("classic combined encrypt");
1617
1618 let mut tampered = combined;
1619 let tamper_index = tamper_index % tampered.len();
1620 tampered[tamper_index] ^= 1;
1621
1622 let mut output = vec![0xa5; message.len()];
1623 let original_output = output.clone();
1624 prop_assert!(
1625 crypto_aead_xchacha20poly1305_ietf_decrypt(
1626 &mut output,
1627 &tampered,
1628 aad,
1629 &nonce,
1630 &key,
1631 )
1632 .is_err()
1633 );
1634 prop_assert_eq!(output, original_output);
1635
1636 let parsed_box = VecBox::from_bytes(&tampered).expect("tampered box parses");
1637 prop_assert!(
1638 parsed_box
1639 .decrypt_to_vec(aad, &rust_nonce, &rust_key)
1640 .is_err()
1641 );
1642
1643 let mut tampered_envelope = rust_nonce.to_vec();
1644 tampered_envelope.extend_from_slice(&tampered);
1645 let parsed_envelope =
1646 VecEnvelope::from_bytes(&tampered_envelope).expect("tampered envelope parses");
1647 prop_assert!(parsed_envelope.open_to_vec(aad, &rust_key).is_err());
1648 }
1649
1650 #[test]
1651 fn proptest_from_bytes_round_trips_or_rejects_by_length(
1652 raw in bytes_strategy(768),
1653 ) {
1654 match VecBox::from_bytes(&raw) {
1655 Ok(parsed) => {
1656 prop_assert!(raw.len() >= CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES);
1657 let bytes = parsed.to_vec();
1658 prop_assert_eq!(bytes.as_slice(), raw.as_slice());
1659 }
1660 Err(_) => {
1661 prop_assert!(raw.len() < CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES);
1662 }
1663 }
1664
1665 let envelope_min_len = CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES
1666 + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES;
1667 match VecEnvelope::from_bytes(&raw) {
1668 Ok(parsed) => {
1669 prop_assert!(raw.len() >= envelope_min_len);
1670 let bytes = parsed.to_vec();
1671 prop_assert_eq!(bytes.as_slice(), raw.as_slice());
1672 }
1673 Err(_) => {
1674 prop_assert!(raw.len() < envelope_min_len);
1675 }
1676 }
1677 }
1678 }
1679 }
1680}