Skip to main content

spideroak_crypto/
aead.rs

1//! Authenticated Encryption with Additional Associated Data per
2//! [RFC 5116].
3//!
4//! [RFC 5116]: https://www.rfc-editor.org/rfc/rfc5116
5
6use core::{
7    cmp::{Eq, PartialEq},
8    fmt::{self, Debug},
9    iter::IntoIterator,
10    mem::{self, size_of},
11    ops::{BitXor, Deref, DerefMut},
12    result::Result,
13};
14
15use buggy::{Bug, BugExt};
16use ctutils::{Choice, CtEq};
17use hybrid_array::{Array, ArraySize};
18use typenum::{
19    U16, U65536, Unsigned,
20    type_operators::{IsGreaterOrEqual, IsLess},
21};
22use zeroize::Zeroize;
23
24use crate::{
25    csprng::{Csprng, Random},
26    kdf::{Expand, Kdf, KdfError, Prk},
27    keys::{SecretKey, SecretKeyBytes, raw_key},
28    util::const_assert,
29};
30
31// Some of the bounds for `Aead` are at least 32 bits, prevent
32// the crate from being built for, e.g., a 16-bit CPU. If we ever
33// need to support such a CPU we will need to revisit the API.
34const_assert!(size_of::<usize>() >= 4);
35
36/// The output buffer is too small.
37///
38/// It contains the size that the buffer needs to be for the
39/// call to succeed, if known.
40#[derive(Copy, Clone, Debug, Eq, PartialEq)]
41pub struct BufferTooSmallError(pub Option<usize>);
42
43impl BufferTooSmallError {
44    /// Returns a human-readable string describing the error.
45    pub const fn as_str(&self) -> &'static str {
46        "dest buffer too small"
47    }
48}
49
50impl fmt::Display for BufferTooSmallError {
51    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52        if let Some(n) = self.0 {
53            write!(f, "{} (need {})", self.as_str(), n)
54        } else {
55            write!(f, "{}", self.as_str())
56        }
57    }
58}
59
60impl core::error::Error for BufferTooSmallError {}
61
62/// An error from a [`Nonce`].
63#[derive(Copy, Clone, Debug, Eq, PartialEq)]
64pub struct InvalidNonceSize;
65
66impl InvalidNonceSize {
67    /// Returns a human-readable string describing the error.
68    pub const fn as_str(&self) -> &'static str {
69        "nonce size is invalid"
70    }
71}
72
73impl fmt::Display for InvalidNonceSize {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        write!(f, "{}", self.as_str())
76    }
77}
78
79impl core::error::Error for InvalidNonceSize {}
80
81/// An error from an [`Aead`] seal.
82#[derive(Debug, Eq, PartialEq)]
83pub enum SealError {
84    /// An internal bug was discovered.
85    Bug(Bug),
86    /// An unknown or internal error has occurred.
87    Other(&'static str),
88    /// The size of the key is incorrect.
89    InvalidKeySize,
90    /// The size of the nonce is incorrect.
91    InvalidNonceSize(InvalidNonceSize),
92    /// The size of the overhead is incorrect.
93    InvalidOverheadSize,
94    /// The plaintext is too long.
95    PlaintextTooLong,
96    /// The additional data is too long.
97    AdditionalDataTooLong,
98    /// The output buffer is too small.
99    BufferTooSmall(BufferTooSmallError),
100    /// The plaintext could not be encrypted.
101    Encryption,
102}
103
104impl SealError {
105    /// Returns a human-readable string describing the error.
106    pub fn as_str(&self) -> &'static str {
107        match self {
108            Self::Bug(err) => err.msg(),
109            Self::Other(msg) => msg,
110            Self::InvalidKeySize => "invalid key size",
111            Self::InvalidNonceSize(err) => err.as_str(),
112            Self::InvalidOverheadSize => "invalid overhead size",
113            Self::PlaintextTooLong => "plaintext too long",
114            Self::AdditionalDataTooLong => "additional data too long",
115            Self::Encryption => "encryption error",
116            Self::BufferTooSmall(err) => err.as_str(),
117        }
118    }
119}
120
121impl fmt::Display for SealError {
122    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123        match self {
124            Self::Bug(err) => write!(f, "{}", err),
125            Self::BufferTooSmall(err) => write!(f, "{}", err),
126            Self::InvalidNonceSize(err) => write!(f, "{}", err),
127            _ => write!(f, "{}", self.as_str()),
128        }
129    }
130}
131
132impl core::error::Error for SealError {
133    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
134        match self {
135            Self::Bug(err) => Some(err),
136            Self::BufferTooSmall(err) => Some(err),
137            Self::InvalidNonceSize(err) => Some(err),
138            _ => None,
139        }
140    }
141}
142
143impl From<BufferTooSmallError> for SealError {
144    fn from(value: BufferTooSmallError) -> Self {
145        SealError::BufferTooSmall(value)
146    }
147}
148
149impl From<Bug> for SealError {
150    fn from(value: Bug) -> Self {
151        SealError::Bug(value)
152    }
153}
154
155impl From<InvalidNonceSize> for SealError {
156    fn from(value: InvalidNonceSize) -> Self {
157        SealError::InvalidNonceSize(value)
158    }
159}
160
161/// An error from an [`Aead`] open.
162#[derive(Debug, Eq, PartialEq)]
163pub enum OpenError {
164    /// An internal bug was discovered.
165    Bug(Bug),
166    /// An unknown or internal error has occurred.
167    Other(&'static str),
168    /// The size of the key is incorrect.
169    InvalidKeySize,
170    /// The size of the nonce is incorrect.
171    InvalidNonceSize(InvalidNonceSize),
172    /// The size of the overhead is incorrect.
173    InvalidOverheadSize,
174    /// The plaintext is too long.
175    PlaintextTooLong,
176    /// The ciphertext is too long.
177    CiphertextTooLong,
178    /// The additional data is too long.
179    AdditionalDataTooLong,
180    /// The output buffer is too small.
181    BufferTooSmall(BufferTooSmallError),
182    /// The ciphertext could not be authenticated.
183    Authentication,
184}
185
186impl OpenError {
187    /// Returns a human-readable string describing the error.
188    pub fn as_str(&self) -> &'static str {
189        match self {
190            Self::Bug(err) => err.msg(),
191            Self::Other(msg) => msg,
192            Self::InvalidKeySize => "invalid key size",
193            Self::InvalidNonceSize(err) => err.as_str(),
194            Self::InvalidOverheadSize => "invalid overhead size",
195            Self::PlaintextTooLong => "plaintext too long",
196            Self::CiphertextTooLong => "ciphertext too long",
197            Self::AdditionalDataTooLong => "additional data too long",
198            Self::Authentication => "authentication error",
199            Self::BufferTooSmall(err) => err.as_str(),
200        }
201    }
202}
203
204impl fmt::Display for OpenError {
205    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206        match self {
207            Self::Bug(err) => write!(f, "{}", err),
208            Self::BufferTooSmall(err) => write!(f, "{}", err),
209            Self::InvalidNonceSize(err) => write!(f, "{}", err),
210            _ => write!(f, "{}", self.as_str()),
211        }
212    }
213}
214
215impl core::error::Error for OpenError {
216    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
217        match self {
218            Self::Bug(err) => Some(err),
219            Self::BufferTooSmall(err) => Some(err),
220            Self::InvalidNonceSize(err) => Some(err),
221            _ => None,
222        }
223    }
224}
225
226impl From<BufferTooSmallError> for OpenError {
227    fn from(value: BufferTooSmallError) -> Self {
228        OpenError::BufferTooSmall(value)
229    }
230}
231
232impl From<Bug> for OpenError {
233    fn from(value: Bug) -> Self {
234        OpenError::Bug(value)
235    }
236}
237
238impl From<InvalidNonceSize> for OpenError {
239    fn from(value: InvalidNonceSize) -> Self {
240        OpenError::InvalidNonceSize(value)
241    }
242}
243
244/// The lifetime of a cryptographic key.
245///
246/// It can be decremented to track usage. For example:
247///
248/// ```rust
249/// # use spideroak_crypto::aead::Lifetime;
250/// let mut remain = Lifetime::Messages(3);
251/// assert_eq!(remain, 3);
252///
253/// remain = remain.consume(1).expect("should be 2");
254/// assert_eq!(remain, 2);
255///
256/// remain = remain.consume(1).expect("should be 1");
257/// assert_eq!(remain, 1);
258///
259/// remain = remain.consume(1).expect("should be 0");
260/// assert_eq!(remain, 0);
261///
262/// assert!(remain.consume(1).is_none());
263/// ```
264#[derive(Copy, Clone, Debug, Eq, PartialEq)]
265pub enum Lifetime {
266    /// The key can handle an unlimited number of messages or
267    /// bytes.
268    Unlimited,
269    /// The maximum number of messages that can be sealed.
270    ///
271    /// In other words, the maximum number of calls to
272    /// [`Aead::seal`], etc.
273    Messages(u64),
274    /// The maximum number of bytes that can be encrypted.
275    Bytes(u64),
276}
277
278impl Lifetime {
279    const fn as_u64(self) -> u64 {
280        match self {
281            Self::Unlimited => u64::MAX,
282            Self::Messages(x) => x,
283            Self::Bytes(x) => x,
284        }
285    }
286
287    /// Decrements the lifetime by the length of the plaintext,
288    /// `bytes`.
289    #[inline]
290    #[must_use]
291    pub fn consume(self, bytes: u64) -> Option<Self> {
292        match self {
293            Self::Unlimited => Some(Self::Unlimited),
294            Self::Messages(x) => x.checked_sub(1).map(Self::Messages),
295            Self::Bytes(x) => x.checked_sub(bytes).map(Self::Bytes),
296        }
297    }
298
299    /// Decrements the lifetime by the length of the plaintext,
300    /// `bytes`.
301    #[inline]
302    #[must_use]
303    pub fn consume_mut(&mut self, bytes: u64) -> bool {
304        self.consume(bytes).inspect(|v| *self = *v).is_some()
305    }
306}
307
308impl PartialEq<u64> for Lifetime {
309    fn eq(&self, other: &u64) -> bool {
310        self.as_u64() == *other
311    }
312}
313
314/// A symmetric cipher implementing a particular Authenticated
315/// Encryption with Associated Data (AEAD) algorithm per
316/// [RFC 5116].
317///
318/// Briefly, AEAD encryption is a construction with four inputs:
319///
320///  1. uniformly random key `K`
321///  2. nonce `N` that is unique for each unique `(K, P)` tuple
322///  3. plaintext `P` which will be encrypted
323///  4. associated data `A` that will be authenticated, but *not*
324///     encrypted
325///
326/// It outputs a ciphertext `C` which is at least as long as `P`.
327/// AEAD decryption works in the inverse manner. For formal and
328/// more comprehensive documentation, see [RFC 5116].
329///
330/// # Requirements
331///
332/// This API is more restrictive than [RFC 5116]. Specifically,
333/// the cipher must:
334///
335/// * Have at least a 128-bit security level for confidentiality.
336/// * Have at least a 128-bit security level for authenticity.
337/// * Have a minimum key size of 16 octets (128 bits).
338/// * Accept plaintexts at least 2³² - 1 octets (2³⁵ - 8 bits) long.
339/// * Accept associated data at least 2³² - 1 (2³⁵ - 8 bits) octets
340///   long.
341///
342/// Examples of AEAD algorithms that fulfill these requirements
343/// include [AES-256-GCM], [ChaCha20-Poly1305], and [Ascon].
344///
345/// It is highly recommended to use a nonce misuse-resistant
346/// AEAD, like [AES-GCM-SIV].
347///
348/// [AES-256-GCM]: https://nvlpubs.nist.gov/nistpubs/legacy/sp/nistspecialpublication800-38d.pdf
349/// [AES-GCM-SIV]: https://www.rfc-editor.org/rfc/rfc8452.html
350/// [Ascon]: https://csrc.nist.gov/News/2023/lightweight-cryptography-nist-selects-ascon
351/// [ChaCha20-Poly1305]: https://datatracker.ietf.org/doc/html/rfc8439
352/// [RFC 5116]: https://www.rfc-editor.org/rfc/rfc5116.html
353pub trait Aead {
354    /// The lifetime of a cryptographic key.
355    const LIFETIME: Lifetime;
356
357    /// The size in octets of a key used by this [`Aead`].
358    ///
359    /// Must be at least 16 octets and less than 2¹⁶ octets.
360    type KeySize: ArraySize + IsGreaterOrEqual<U16> + IsLess<U65536> + 'static;
361    /// Shorthand for [`KeySize`][Self::KeySize].
362    const KEY_SIZE: usize = Self::KeySize::USIZE;
363
364    /// The size in octets of a nonce used by this [`Aead`].
365    ///
366    /// Must be less than 2¹⁶ octets.
367    type NonceSize: ArraySize + IsLess<U65536> + 'static;
368    /// Shorthand for [`NonceSize`][Self::NonceSize].
369    const NONCE_SIZE: usize = Self::NonceSize::USIZE;
370
371    /// The size in octets of authentication overhead added to
372    /// encrypted plaintexts.
373    ///
374    /// For regular AEADs, this is the size of the authentication
375    /// tag. For other AEADs, like [`CommittingAead`], this is
376    /// the size of the authentication tag and key commitment.
377    ///
378    /// Must be at least 16 octets (128 bits).
379    type Overhead: ArraySize + IsGreaterOrEqual<U16> + 'static;
380    /// Shorthand for [`Overhead`][Self::Overhead].
381    const OVERHEAD: usize = Self::Overhead::USIZE;
382
383    /// The maximum size in octets of a plaintext allowed by this
384    /// [`Aead`] (i.e., `P_MAX`).
385    ///
386    /// Must be at least 2³² - 1 octets.
387    const MAX_PLAINTEXT_SIZE: u64;
388    /// The maximum size in octets of additional data allowed by
389    /// this [`Aead`] (i.e., `A_MAX`).
390    ///
391    /// Must be at least 2³² - 1 octets.
392    const MAX_ADDITIONAL_DATA_SIZE: u64;
393    /// The maximum size in octets of a ciphertext allowed by
394    /// this [`Aead`] (i.e., `C_MAX`).
395    ///
396    /// Must be at least 2³² - 1 octets and
397    /// [`OVERHEAD`][Self::OVERHEAD] octets larger than
398    /// [`MAX_PLAINTEXT_SIZE`][Self::MAX_PLAINTEXT_SIZE].
399    const MAX_CIPHERTEXT_SIZE: u64 =
400        match Self::MAX_PLAINTEXT_SIZE.checked_add(Self::OVERHEAD as u64) {
401            Some(n) => n,
402            None => panic!("overflow"),
403        };
404
405    /// The key used by the [`Aead`].
406    type Key: SecretKey<Size = Self::KeySize>;
407
408    /// Creates a new [`Aead`].
409    fn new(key: &Self::Key) -> Self;
410
411    /// Encrypts and authenticates `plaintext`, writing the
412    /// resulting ciphertext to `dst`.
413    ///
414    /// Only `plaintext.len()` + [`Self::OVERHEAD`] bytes of
415    /// `dst` will be written to.
416    ///
417    /// # Requirements
418    ///
419    /// * `dst` must be at least [`Self::OVERHEAD`] bytes longer
420    ///   than `plaintext`.
421    /// * `nonce` must be exactly [`Self::NONCE_SIZE`] bytes
422    ///   long.
423    /// * `plaintext` must be at most [`Self::MAX_PLAINTEXT_SIZE`]
424    ///   bytes long.
425    /// * `additional_data` must be at most
426    ///   [`Self::MAX_ADDITIONAL_DATA_SIZE`] bytes long.
427    ///
428    /// It must not be used more than permitted by its
429    /// [`lifetime`][`Aead::LIFETIME`].
430    fn seal(
431        &self,
432        mut dst: &mut [u8],
433        nonce: &[u8],
434        plaintext: &[u8],
435        additional_data: &[u8],
436    ) -> Result<(), SealError> {
437        check_seal_params::<Self>(&mut dst, nonce, plaintext, additional_data)?;
438        dst[..plaintext.len()].copy_from_slice(plaintext);
439        let tag_idx = dst
440            .len()
441            .checked_sub(Self::OVERHEAD)
442            .assume("out length must be >= overhead")?;
443        let (dst, overhead) = dst.split_at_mut(tag_idx);
444        self.seal_in_place(nonce, dst, overhead, additional_data)
445            // Encryption failed, make sure that we do not
446            // release any invalid plaintext to the caller.
447            .inspect_err(|_| dst.zeroize())
448    }
449
450    /// Encrypts and authenticates `data` in-place.
451    ///
452    /// The authentication overhead is written to `overhead`.
453    ///
454    /// # Requirements
455    ///
456    /// * `nonce` must be exactly [`Self::NONCE_SIZE`] bytes
457    ///   long.
458    /// * `data` must be at most [`Self::MAX_PLAINTEXT_SIZE`]
459    ///   bytes long.
460    /// * `overhead` must be exactly [`Self::OVERHEAD`] bytes
461    ///   long.
462    /// * `additional_data` must be at most
463    ///   [`Self::MAX_ADDITIONAL_DATA_SIZE`] bytes long.
464    ///
465    /// It must not be used more than permitted by its
466    /// [`lifetime`][`Aead::LIFETIME`].
467    fn seal_in_place(
468        &self,
469        nonce: &[u8],
470        data: &mut [u8],
471        overhead: &mut [u8],
472        additional_data: &[u8],
473    ) -> Result<(), SealError>;
474
475    /// Decrypts and authenticates `ciphertext`, writing the
476    /// resulting plaintext to `dst`.
477    ///
478    /// Only `ciphertext.len()` - [`Self::OVERHEAD`] bytes of
479    /// `dst` will be written to.
480    ///
481    /// # Requirements
482    ///
483    /// * `dst` must be at least `ciphertext.len()` -
484    ///   [`Self::OVERHEAD`] bytes long.
485    /// * `nonce` must be exactly [`Self::NONCE_SIZE`] bytes
486    ///   long.
487    /// * `ciphertext` must be at most
488    ///   [`Self::MAX_CIPHERTEXT_SIZE`] bytes long.
489    /// * `additional_data` must be at most
490    ///   [`Self::MAX_ADDITIONAL_DATA_SIZE`] bytes long.
491    fn open(
492        &self,
493        dst: &mut [u8],
494        nonce: &[u8],
495        ciphertext: &[u8],
496        additional_data: &[u8],
497    ) -> Result<(), OpenError> {
498        check_open_params::<Self>(dst, nonce, ciphertext, additional_data)?;
499
500        let max = ciphertext.len().checked_sub(Self::OVERHEAD).assume(
501            "`ciphertext.len() >= Self::OVERHEAD` should be enforced by `check_open_params`",
502        )?;
503        let (ciphertext, overhead) = ciphertext.split_at(max);
504        let out = &mut dst[..max];
505        out.copy_from_slice(ciphertext);
506        self.open_in_place(nonce, out, overhead, additional_data)
507            // Decryption failed, ensure that we do not release
508            // any invalid plaintext to the caller.
509            .inspect_err(|_| out.zeroize())
510    }
511
512    /// Decrypts and authenticates `data` in-place.
513    ///
514    /// # Requirements
515    ///
516    /// * `nonce` must be exactly [`Self::NONCE_SIZE`] bytes
517    ///   long.
518    /// * `data` must be at most [`Self::MAX_CIPHERTEXT_SIZE`] -
519    ///   [`Self::OVERHEAD`] bytes long.
520    /// * `overhead` must be exactly [`Self::OVERHEAD`] bytes
521    ///   long.
522    /// * `additional_data` must be at most
523    ///   [`Self::MAX_ADDITIONAL_DATA_SIZE`] bytes long.
524    fn open_in_place(
525        &self,
526        nonce: &[u8],
527        data: &mut [u8],
528        overhead: &[u8],
529        additional_data: &[u8],
530    ) -> Result<(), OpenError>;
531}
532
533/// Shorthand which the compiler does not understand without
534/// a good amount of hand holding.
535pub type KeyData<A> = SecretKeyBytes<<<A as Aead>::Key as SecretKey>::Size>;
536
537/// An authentication tag.
538pub type Tag<A> = Array<u8, <A as Aead>::Overhead>;
539
540const fn check_aead_params<A: Aead + ?Sized>() {
541    const {
542        assert!(A::KEY_SIZE >= 16);
543        assert!(A::OVERHEAD >= 16);
544        assert!(A::MAX_PLAINTEXT_SIZE >= u32::MAX as u64);
545        assert!(A::MAX_CIPHERTEXT_SIZE == A::MAX_PLAINTEXT_SIZE + (A::OVERHEAD as u64));
546        assert!(A::MAX_ADDITIONAL_DATA_SIZE >= u32::MAX as u64);
547    }
548}
549
550/// Checks that the parameters to [`Aead::seal`] have the correct
551/// lengths, etc.
552///
553/// Trims `dst` to `..plaintext.len() + A::OVERHEAD` if correctly sized.
554pub fn check_seal_params<A: Aead + ?Sized>(
555    dst: &mut &mut [u8],
556    nonce: &[u8],
557    plaintext: &[u8],
558    additional_data: &[u8],
559) -> Result<(), SealError> {
560    check_aead_params::<A>();
561
562    let need = match plaintext.len().checked_add(A::OVERHEAD) {
563        // Overflow.
564        None => return Err(SealError::PlaintextTooLong),
565        Some(n) => n,
566    };
567    if need > dst.len() {
568        return Err(SealError::BufferTooSmall(BufferTooSmallError(Some(need))));
569    }
570    *dst = &mut mem::take(dst)[..need];
571
572    if nonce.len() != A::NONCE_SIZE {
573        return Err(SealError::InvalidNonceSize(InvalidNonceSize));
574    }
575    if plaintext.len() as u64 > A::MAX_PLAINTEXT_SIZE {
576        return Err(SealError::PlaintextTooLong);
577    }
578    if additional_data.len() as u64 > A::MAX_ADDITIONAL_DATA_SIZE {
579        return Err(SealError::AdditionalDataTooLong);
580    }
581
582    Ok(())
583}
584
585/// Checks that the parameters to [`Aead::seal_in_place`] have
586/// the correct lengths, etc.
587pub const fn check_seal_in_place_params<A: Aead + ?Sized>(
588    nonce: &[u8],
589    data: &[u8],
590    overhead: &[u8],
591    additional_data: &[u8],
592) -> Result<(), SealError> {
593    check_aead_params::<A>();
594
595    if nonce.len() != A::NONCE_SIZE {
596        return Err(SealError::InvalidNonceSize(InvalidNonceSize));
597    }
598    if data.len() as u64 > A::MAX_PLAINTEXT_SIZE {
599        return Err(SealError::PlaintextTooLong);
600    }
601    if overhead.len() > A::OVERHEAD {
602        return Err(SealError::InvalidOverheadSize);
603    }
604    if additional_data.len() as u64 > A::MAX_ADDITIONAL_DATA_SIZE {
605        return Err(SealError::AdditionalDataTooLong);
606    }
607    Ok(())
608}
609
610/// Checks that the parameters to [`Aead::open`] have the correct
611/// lengths, etc.
612pub const fn check_open_params<A: Aead + ?Sized>(
613    dst: &[u8],
614    nonce: &[u8],
615    ciphertext: &[u8],
616    additional_data: &[u8],
617) -> Result<(), OpenError> {
618    check_aead_params::<A>();
619
620    let need = match ciphertext.len().checked_sub(A::OVERHEAD) {
621        // If the ciphertext does not have a full tag, etc. it
622        // cannot be authenticated.
623        None => return Err(OpenError::Authentication),
624        Some(n) => n,
625    };
626    if need > dst.len() {
627        return Err(OpenError::BufferTooSmall(BufferTooSmallError(Some(need))));
628    }
629    if nonce.len() != A::NONCE_SIZE {
630        return Err(OpenError::InvalidNonceSize(InvalidNonceSize));
631    }
632    // The case where the `ciphertext.len()` < `A::OVERHEAD` is
633    // covered by the `match` expression above.
634    if ciphertext.len() as u64 > A::MAX_CIPHERTEXT_SIZE {
635        return Err(OpenError::CiphertextTooLong);
636    }
637    if additional_data.len() as u64 > A::MAX_ADDITIONAL_DATA_SIZE {
638        return Err(OpenError::AdditionalDataTooLong);
639    }
640    Ok(())
641}
642
643/// Checks that the parameters to [`Aead::open_in_place`] have
644/// the correct lengths, etc.
645pub const fn check_open_in_place_params<A: Aead + ?Sized>(
646    nonce: &[u8],
647    data: &[u8],
648    overhead: &[u8],
649    additional_data: &[u8],
650) -> Result<(), OpenError> {
651    check_aead_params::<A>();
652
653    if nonce.len() != A::NONCE_SIZE {
654        return Err(OpenError::InvalidNonceSize(InvalidNonceSize));
655    }
656    let Some(max_len) = A::MAX_PLAINTEXT_SIZE.checked_sub(A::OVERHEAD as u64) else {
657        return Err(OpenError::Other(
658            "implementation bug: `Aead::MAX_PLAINTEXT_SIZE < Aead::OVERHEAD`",
659        ));
660    };
661    if data.len() as u64 > max_len {
662        return Err(OpenError::PlaintextTooLong);
663    }
664    if overhead.len() > A::OVERHEAD {
665        return Err(OpenError::InvalidOverheadSize);
666    }
667    if additional_data.len() as u64 > A::MAX_ADDITIONAL_DATA_SIZE {
668        return Err(OpenError::AdditionalDataTooLong);
669    }
670    Ok(())
671}
672
673raw_key! {
674    /// An [`Aead`] key.
675    pub AeadKey,
676}
677
678impl<N: ArraySize> AeadKey<N> {
679    // Used by `crate::rust::Aes256Gcm::new`.
680    pub(crate) fn as_array<const U: usize>(&self) -> &[u8; U]
681    where
682        N: ArraySize<ArrayType<u8> = [u8; U]>,
683    {
684        self.0.as_array()
685    }
686}
687
688/// An [`Aead`] nonce.
689#[derive(Clone, Default, Hash, Eq, PartialEq)]
690#[repr(transparent)]
691pub struct Nonce<N: ArraySize>(Array<u8, N>);
692
693impl<N: ArraySize> Nonce<N> {
694    /// The size in octets of the nonce.
695    pub const SIZE: usize = N::USIZE;
696
697    /// Returns the size in octets of the nonce.
698    #[inline]
699    #[allow(clippy::len_without_is_empty)]
700    pub const fn len(&self) -> usize {
701        Self::SIZE
702    }
703
704    // For `aranya-crypto`. Do not use.
705    #[doc(hidden)]
706    pub fn into_inner(self) -> Array<u8, N> {
707        self.0
708    }
709
710    pub(crate) const fn from_bytes(nonce: Array<u8, N>) -> Self {
711        Self(nonce)
712    }
713
714    pub(crate) fn try_from_slice(data: &[u8]) -> Result<Self, InvalidNonceSize> {
715        let nonce = Array::try_from(data).map_err(|_| InvalidNonceSize)?;
716        Ok(Self(nonce))
717    }
718}
719
720impl<N: ArraySize> Copy for Nonce<N> where N::ArrayType<u8>: Copy {}
721
722impl<N: ArraySize> Debug for Nonce<N> {
723    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
724        f.debug_tuple("Nonce").field(&self.0).finish()
725    }
726}
727
728impl<N: ArraySize> Deref for Nonce<N> {
729    type Target = [u8];
730
731    #[inline]
732    fn deref(&self) -> &Self::Target {
733        &self.0
734    }
735}
736
737impl<N: ArraySize> DerefMut for Nonce<N> {
738    #[inline]
739    fn deref_mut(&mut self) -> &mut Self::Target {
740        &mut self.0
741    }
742}
743
744impl<N: ArraySize> BitXor for Nonce<N> {
745    type Output = Self;
746
747    #[inline]
748    fn bitxor(mut self, rhs: Self) -> Self::Output {
749        for (x, y) in self.0.iter_mut().zip(&rhs.0) {
750            *x ^= y;
751        }
752        self
753    }
754}
755
756impl<N: ArraySize> BitXor for &Nonce<N> {
757    type Output = Nonce<N>;
758
759    #[inline]
760    fn bitxor(self, rhs: Self) -> Self::Output {
761        let mut lhs = self.clone();
762        for (x, y) in lhs.0.iter_mut().zip(&rhs.0) {
763            *x ^= y;
764        }
765        lhs
766    }
767}
768
769impl<N: ArraySize> CtEq for Nonce<N> {
770    #[inline]
771    fn ct_eq(&self, other: &Self) -> Choice {
772        self.0.ct_eq(&other.0)
773    }
774}
775
776impl<N: ArraySize> Random for Nonce<N> {
777    fn random<R: Csprng>(rng: R) -> Self {
778        Self(Random::random(rng))
779    }
780}
781
782impl<N: ArraySize> Expand for Nonce<N>
783where
784    N: IsLess<U65536>,
785{
786    type Size = N;
787
788    fn expand_multi<'a, K, I>(prk: &Prk<K::PrkSize>, info: I) -> Result<Self, KdfError>
789    where
790        K: Kdf,
791        I: IntoIterator<Item = &'a [u8]>,
792        I::IntoIter: Clone,
793    {
794        Ok(Self(Expand::expand_multi::<K, I>(prk, info)?))
795    }
796}
797
798impl<N: ArraySize> TryFrom<&[u8]> for Nonce<N> {
799    type Error = InvalidNonceSize;
800
801    fn try_from(data: &[u8]) -> Result<Self, InvalidNonceSize> {
802        Self::try_from_slice(data)
803    }
804}
805
806/// A marker trait signifying that the [`Aead`] is IND-CCA2
807/// secure.
808pub trait IndCca2: Aead {}
809
810/// A marker trait signifying that the [`Aead`] is committing.
811pub trait CommittingAead: Aead {}
812
813/// A marker trait signifying that the [`Aead`] is CMT-1 secure.
814///
815/// It provides a commitment over the key and nothing else.
816pub trait Cmt1Aead: CommittingAead {}
817
818/// A marker trait signifying that the [`Aead`] is CMT-3 secure.
819///
820/// It provides a commitment over the key, nonce, and additional
821/// data, but not plaintext.
822pub trait Cmt3Aead: Cmt1Aead {}
823
824/// A marker trait signifying that the [`Aead`] is CMT-4 secure.
825///
826/// It provides a commitment over everything: the key, nonce,
827/// plaintext, and additional data.
828pub trait Cmt4Aead: Cmt3Aead {}
829
830#[cfg(feature = "committing-aead")]
831mod committing {
832    use core::{fmt, marker::PhantomData, num::NonZeroU64, result::Result};
833
834    use buggy::{Bug, BugExt};
835    use hybrid_array::{Array, ArraySize};
836    use typenum::{
837        U16, U65536, Unsigned,
838        type_operators::{IsGreaterOrEqual, IsLess},
839    };
840
841    use super::{Aead, KeyData, Nonce, OpenError, SealError};
842    use crate::import::{ExportError, ImportError};
843
844    /// A symmetric block cipher.
845    #[doc(hidden)]
846    pub trait BlockCipher {
847        /// The size in octets of a the cipher's block.
848        type BlockSize: ArraySize + IsGreaterOrEqual<U16> + IsLess<U65536> + 'static;
849        /// Shorthand for [`BlockSize::USIZE`][Self::BlockSize];
850        const BLOCK_SIZE: usize = Self::BlockSize::USIZE;
851        /// The cipher's key.
852        type Key;
853
854        /// Creates a new instance of the block cipher.
855        fn new(key: &Self::Key) -> Self;
856        /// Encrypts `block` in place.
857        fn encrypt_block(&self, block: &mut Array<u8, Self::BlockSize>);
858    }
859
860    /// An implementation of the Counter-then-Xor (CX) PRF per
861    /// [bellare].
862    ///
863    /// [bellare]: https://eprint.iacr.org/2022/268
864    #[doc(hidden)]
865    pub struct CtrThenXorPrf<A, C> {
866        _aead: PhantomData<fn() -> A>,
867        _cipher: PhantomData<fn() -> C>,
868    }
869
870    impl<A, C> CtrThenXorPrf<A, C>
871    where
872        A: Aead,
873        C: BlockCipher<Key = A::Key>,
874        // The paper requires m < n where m is the nonce space
875        // and n is the block size.
876        A::NonceSize: IsLess<C::BlockSize>,
877    {
878        /// Returns the key commitment and new key (P,L) for
879        /// (K,M).
880        #[inline]
881        #[allow(clippy::type_complexity)] // internal method
882        pub fn commit(
883            key: &A::Key,
884            nonce: &Nonce<A::NonceSize>,
885        ) -> Result<(Array<u8, C::BlockSize>, KeyData<A>), Bug> {
886            let mut cx = Default::default();
887            let key = Self::commit_into(&mut cx, key, nonce)?;
888            Ok((cx, key))
889        }
890
891        /// Same as [`commit`][Self::commit], but writes directly
892        /// to `cx`.
893        pub fn commit_into(
894            cx: &mut Array<u8, C::BlockSize>,
895            key: &A::Key,
896            nonce: &Nonce<A::NonceSize>,
897        ) -> Result<KeyData<A>, Bug> {
898            /// Pad is a one-to-one encoding that converts the
899            /// pair (M,i) in {0,1}^m x {1,...,2^(n-m)} into an
900            /// n-bit string.
901            ///
902            /// We let `i` be a `u64` since it's large enough to
903            /// never overflow.
904            #[inline(always)]
905            fn pad<C: BlockCipher>(
906                m: &[u8],
907                i: NonZeroU64,
908            ) -> Result<Array<u8, C::BlockSize>, Bug> {
909                // This is checked by `Self`'s generic bounds, but it
910                // doesn't hurt to double check.
911                debug_assert!(m.len() < C::BlockSize::USIZE);
912
913                let mut b = Array::<u8, C::BlockSize>::default();
914                b[..m.len()].copy_from_slice(m);
915                let x = i.get().to_le_bytes();
916                let n = usize::checked_sub(b.len(), m.len())
917                    .assume("nonce size <= block size")?
918                    .min(x.len());
919                b[m.len()..].copy_from_slice(&x[..n]);
920                Ok(b)
921            }
922
923            let mut i = NonZeroU64::MIN;
924            let cipher = C::new(key);
925            let nonce = nonce.as_ref();
926
927            let v_1 = {
928                // X_i <- pad(M, i)
929                let x_1 = pad::<C>(nonce, i)?;
930
931                // V_i <- E_k(X_i);
932                let mut v_1 = {
933                    // Make a copy since we need `x_1` for the
934                    // XOR.
935                    let mut tmp = x_1.clone();
936                    cipher.encrypt_block(&mut tmp);
937                    tmp
938                };
939
940                // V_1 = V_1 ^ X_1;
941                for (v, x) in v_1.iter_mut().zip(x_1.iter()) {
942                    *v ^= x;
943                }
944                v_1
945            };
946            cx.copy_from_slice(&v_1);
947
948            let mut key = KeyData::<A>::default();
949            for chunk in key.as_bytes_mut().chunks_mut(C::BLOCK_SIZE) {
950                i = i
951                    .checked_add(1)
952                    // It should be impossible to overflow. At
953                    // one nanosecond per op, this will take
954                    // upward of 500 years.
955                    .assume("should be impossible to overflow")?;
956
957                // V_i <- E_k(X_i);
958                let v_i = {
959                    // X_i <- pad(M, i)
960                    let mut x_i = pad::<C>(nonce, i)?;
961                    cipher.encrypt_block(&mut x_i);
962                    x_i
963                };
964                chunk.copy_from_slice(&v_i[..chunk.len()]);
965            }
966            Ok(key)
967        }
968    }
969
970    impl<A, C> fmt::Debug for CtrThenXorPrf<A, C> {
971        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
972            f.debug_struct("CtrThenXorPrf").finish_non_exhaustive()
973        }
974    }
975
976    /// An error occurred during the UNAE-then-Commit transform.
977    #[derive(Debug, Eq, PartialEq)]
978    pub enum UtcError {
979        /// An internal bug was discovered.
980        Bug(Bug),
981        /// The transformed AEAD key could not be imported.
982        Import(ImportError),
983    }
984
985    impl UtcError {
986        const fn as_str(&self) -> &'static str {
987            match self {
988                Self::Bug(_) => "bug",
989                Self::Import(_) => "unable to import HtE transformed key",
990            }
991        }
992    }
993
994    impl fmt::Display for UtcError {
995        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
996            match self {
997                Self::Bug(err) => write!(f, "{}: {err}", self.as_str()),
998                Self::Import(err) => write!(f, "{}: {err}", self.as_str()),
999            }
1000        }
1001    }
1002
1003    impl core::error::Error for UtcError {
1004        fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
1005            match self {
1006                Self::Bug(err) => Some(err),
1007                Self::Import(err) => Some(err),
1008            }
1009        }
1010    }
1011
1012    impl From<Bug> for UtcError {
1013        fn from(err: Bug) -> Self {
1014            Self::Bug(err)
1015        }
1016    }
1017
1018    impl From<ImportError> for UtcError {
1019        fn from(err: ImportError) -> Self {
1020            Self::Import(err)
1021        }
1022    }
1023
1024    impl From<UtcError> for SealError {
1025        fn from(err: UtcError) -> SealError {
1026            SealError::Other(err.as_str())
1027        }
1028    }
1029
1030    impl From<UtcError> for OpenError {
1031        fn from(err: UtcError) -> OpenError {
1032            OpenError::Other(err.as_str())
1033        }
1034    }
1035
1036    /// Implements the UNAE-Then-Commit (UtC) transform to turn
1037    /// a standard AEAD into a CMT-1 AEAD.
1038    ///
1039    /// - `name`: The name of the resulting [`Aead`].
1040    /// - `inner`: The underlying [`Aead`].
1041    /// - `cipher`: The underlying [`BlockCipher`].
1042    /// - `doc`: A string to use for documentation.
1043    ///
1044    /// # ⚠️ Warning
1045    /// <div class="warning">
1046    /// This is a low-level feature. You should not be using it
1047    /// unless you understand what you are doing.
1048    /// </div>
1049    ///
1050    /// # Example
1051    ///
1052    /// ```rust,ignore
1053    /// # #[cfg(feature = "committing-aead")]
1054    /// # {
1055    /// use spideroak_crypto::utc_aead;
1056    /// utc_aead!(Cmt1Aes256Gcm, Aes256Gcm, Aes256, "CMT-1 AES-256-GCM.");
1057    /// # }
1058    /// ```
1059    #[cfg_attr(feature = "committing-aead", macro_export)]
1060    #[cfg_attr(docsrs, doc(cfg(feature = "committing-aead")))]
1061    macro_rules! utc_aead {
1062        ($name:ident, $inner:ty, $cipher:ty, $doc:expr $(, $oid:expr)? $(,)?) => {
1063            #[doc = $doc]
1064            #[derive(Debug)]
1065            pub struct $name {
1066                key: <$inner as $crate::aead::Aead>::Key,
1067            }
1068
1069            impl $name {
1070                const COMMITMENT_SIZE: usize = <<$cipher as $crate::aead::BlockCipher>::BlockSize as
1071                                                                        $crate::typenum::Unsigned>::USIZE;
1072            }
1073
1074            impl $crate::aead::CommittingAead for $name {}
1075
1076            impl $crate::aead::Cmt1Aead for $name {}
1077
1078            impl $crate::aead::Aead for $name {
1079                const LIFETIME: $crate::aead::Lifetime = <$inner as $crate::aead::Aead>::LIFETIME;
1080
1081                type KeySize = <$inner as $crate::aead::Aead>::KeySize;
1082                type NonceSize = <$inner as $crate::aead::Aead>::NonceSize;
1083                type Overhead = $crate::typenum::Sum<
1084                    <$inner as $crate::aead::Aead>::Overhead,
1085                    // UtC has one block of overhead.
1086                    <$cipher as $crate::aead::BlockCipher>::BlockSize,
1087                >;
1088
1089                const MAX_PLAINTEXT_SIZE: u64 = <$inner as $crate::aead::Aead>::MAX_PLAINTEXT_SIZE;
1090                const MAX_ADDITIONAL_DATA_SIZE: u64 =
1091                    <$inner as $crate::aead::Aead>::MAX_ADDITIONAL_DATA_SIZE;
1092
1093                type Key = <$inner as $crate::aead::Aead>::Key;
1094
1095                #[inline]
1096                fn new(key: &Self::Key) -> Self {
1097                    Self { key: key.clone() }
1098                }
1099
1100                fn seal(
1101                    &self,
1102                    mut dst: &mut [u8],
1103                    nonce: &[u8],
1104                    plaintext: &[u8],
1105                    additional_data: &[u8],
1106                ) -> ::core::result::Result<(), $crate::aead::SealError> {
1107                    $crate::aead::check_seal_params::<Self>(
1108                        &mut dst,
1109                        nonce,
1110                        plaintext,
1111                        additional_data,
1112                    )?;
1113
1114                    let (dst, cx) = $crate::buggy::BugExt::assume(
1115                        dst.split_last_chunk_mut::<{Self::COMMITMENT_SIZE}>(),
1116                        "`COMMITMENT_SIZE` fits in `out`",
1117                    )?;
1118                    let key_bytes = $crate::aead::CtrThenXorPrf::<$inner, $cipher>::commit_into(
1119                        cx.into(),
1120                        &self.key,
1121                        &nonce.try_into()?,
1122                    )?;
1123                    let key = $crate::import::Import::<_>::import(key_bytes.as_bytes())
1124                        .map_err($crate::aead::UtcError::Import)?;
1125                    <$inner as $crate::aead::Aead>::new(&key).seal(
1126                        dst,
1127                        nonce,
1128                        plaintext,
1129                        additional_data,
1130                    )
1131                }
1132
1133                fn seal_in_place(
1134                    &self,
1135                    nonce: &[u8],
1136                    data: &mut [u8],
1137                    overhead: &mut [u8],
1138                    additional_data: &[u8],
1139                ) -> ::core::result::Result<(), $crate::aead::SealError> {
1140                    $crate::aead::check_seal_in_place_params::<Self>(
1141                        nonce,
1142                        data,
1143                        overhead,
1144                        additional_data,
1145                    )?;
1146
1147                    let (tag, cx) = $crate::buggy::BugExt::assume(
1148                        overhead.split_last_chunk_mut::<{Self::COMMITMENT_SIZE}>(),
1149                        "`COMMITMENT_SIZE` fits in `overhead`",
1150                    )?;
1151                    let key_bytes = $crate::aead::CtrThenXorPrf::<$inner, $cipher>::commit_into(
1152                        cx.into(),
1153                        &self.key,
1154                        &nonce.try_into()?,
1155                    )?;
1156                    let key = $crate::import::Import::<_>::import(key_bytes.as_bytes())
1157                        .map_err($crate::aead::UtcError::Import)?;
1158                    <$inner as $crate::aead::Aead>::new(&key).seal_in_place(
1159                        nonce,
1160                        data,
1161                        tag,
1162                        additional_data,
1163                    )
1164                }
1165
1166                fn open(
1167                    &self,
1168                    dst: &mut [u8],
1169                    nonce: &[u8],
1170                    ciphertext: &[u8],
1171                    additional_data: &[u8],
1172                ) -> ::core::result::Result<(), $crate::aead::OpenError> {
1173                    $crate::aead::check_open_params::<Self>(
1174                        dst,
1175                        nonce,
1176                        ciphertext,
1177                        additional_data,
1178                    )?;
1179
1180                    let (ciphertext, got_cx) = $crate::buggy::BugExt::assume(
1181                        ciphertext.split_last_chunk::<{Self::COMMITMENT_SIZE}>(),
1182                        "`COMMITMENT_SIZE` fits in `ciphertext`",
1183                    )?;
1184                    let (want_cx, key_bytes) = $crate::aead::CtrThenXorPrf::<$inner, $cipher>::commit(
1185                        &self.key,
1186                        &nonce.try_into()?,
1187                    )?;
1188                    if $crate::ctutils::CtEq::ct_ne(
1189                        want_cx.as_slice(),
1190                        got_cx,
1191                    ).to_bool() {
1192                        Err($crate::aead::OpenError::Authentication)
1193                    } else {
1194                        let key = $crate::import::Import::<_>::import(key_bytes.as_bytes())
1195                            .map_err($crate::aead::UtcError::Import)?;
1196                        <$inner as $crate::aead::Aead>::new(&key).open(
1197                            dst,
1198                            nonce,
1199                            ciphertext,
1200                            additional_data,
1201                        )
1202                    }
1203                }
1204
1205                fn open_in_place(
1206                    &self,
1207                    nonce: &[u8],
1208                    data: &mut [u8],
1209                    overhead: &[u8],
1210                    additional_data: &[u8],
1211                ) -> ::core::result::Result<(), $crate::aead::OpenError> {
1212                    $crate::aead::check_open_in_place_params::<Self>(
1213                        nonce,
1214                        data,
1215                        overhead,
1216                        additional_data,
1217                    )?;
1218
1219                    let (overhead, got_cx) = $crate::buggy::BugExt::assume(
1220                        overhead.split_last_chunk::<{Self::COMMITMENT_SIZE}>(),
1221                        "`COMMITMENT_SIZE` fits in `overhead`",
1222                    )?;
1223                    let (want_cx, key_bytes) = $crate::aead::CtrThenXorPrf::<$inner, $cipher>::commit(
1224                        &self.key,
1225                        &nonce.try_into()?,
1226                    )?;
1227                    if $crate::ctutils::CtEq::ct_ne(
1228                        want_cx.as_slice(),
1229                        got_cx,
1230                    ).to_bool() {
1231                        Err($crate::aead::OpenError::Authentication)
1232                    } else {
1233                        let key = $crate::import::Import::<_>::import(key_bytes.as_bytes())
1234                            .map_err($crate::aead::UtcError::Import)?;
1235                        <$inner as $crate::aead::Aead>::new(&key).open_in_place(
1236                            nonce,
1237                            data,
1238                            overhead,
1239                            additional_data,
1240                        )
1241                    }
1242                }
1243            }
1244
1245            $(impl $crate::oid::Identified for $name {
1246                const OID: &$crate::oid::Oid = $oid;
1247            })?
1248        };
1249    }
1250    pub(crate) use utc_aead;
1251
1252    /// An error occurred during the Hash-then-Encrypt transform.
1253    #[derive(Debug, Eq, PartialEq)]
1254    pub enum HteError {
1255        /// The current AEAD key could not be exported.
1256        Export(ExportError),
1257        /// The transformed AEAD key could not be imported.
1258        Import(ImportError),
1259    }
1260
1261    impl HteError {
1262        const fn as_str(&self) -> &'static str {
1263            match self {
1264                Self::Export(_) => "unable to export inner secret key",
1265                Self::Import(_) => "unable to import HtE transformed key",
1266            }
1267        }
1268    }
1269
1270    impl fmt::Display for HteError {
1271        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1272            match self {
1273                Self::Export(err) => write!(f, "{}: {err}", self.as_str()),
1274                Self::Import(err) => write!(f, "{}: {err}", self.as_str()),
1275            }
1276        }
1277    }
1278
1279    impl core::error::Error for HteError {
1280        fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
1281            match self {
1282                Self::Export(err) => Some(err),
1283                Self::Import(err) => Some(err),
1284            }
1285        }
1286    }
1287
1288    impl From<ExportError> for HteError {
1289        fn from(err: ExportError) -> Self {
1290            Self::Export(err)
1291        }
1292    }
1293
1294    impl From<ImportError> for HteError {
1295        fn from(err: ImportError) -> Self {
1296            Self::Import(err)
1297        }
1298    }
1299
1300    impl From<HteError> for SealError {
1301        fn from(err: HteError) -> SealError {
1302            SealError::Other(err.as_str())
1303        }
1304    }
1305
1306    impl From<HteError> for OpenError {
1307        fn from(err: HteError) -> OpenError {
1308            OpenError::Other(err.as_str())
1309        }
1310    }
1311
1312    /// Implements the Hash-then-Encrypt (HtE) transform to turn
1313    /// a CMT-1 AEAD into a CMT-4 AEAD.
1314    ///
1315    /// - `name`: The name of the resulting [`Aead`].
1316    /// - `inner`: The underlying [`Aead`].
1317    /// - `hash`: A hash function.
1318    /// - `doc`: A string to use for documentation.
1319    ///
1320    /// # ⚠️ Warning
1321    /// <div class="warning">
1322    /// This is a low-level feature. You should not be using it
1323    /// unless you understand what you are doing.
1324    /// </div>
1325    ///
1326    /// # Example
1327    ///
1328    /// ```rust,ignore
1329    /// # #[cfg(feature = "committing-aead")]
1330    /// # {
1331    /// use spideroak_crypto::hte_aead;
1332    /// hte_aead!(Cmt4Aes256Gcm, Cmt1Aes256Gcm, Sha256, "CMT-4 AES-256-GCM.");
1333    /// # }
1334    /// ```
1335    #[cfg_attr(feature = "committing-aead", macro_export)]
1336    #[cfg_attr(docsrs, doc(cfg(feature = "committing-aead")))]
1337    macro_rules! hte_aead {
1338        ($name:ident, $inner:ty, $hash:ty, $doc:expr $(, $oid:expr)? $(,)?) => {
1339            #[doc = $doc]
1340            #[derive(Debug)]
1341            pub struct $name {
1342                key: <$inner as $crate::aead::Aead>::Key,
1343            }
1344
1345            impl $name {
1346                fn hash(
1347                    &self,
1348                    nonce: &[u8],
1349                    ad: &[u8],
1350                ) -> ::core::result::Result<
1351                    <$inner as $crate::aead::Aead>::Key,
1352                    $crate::aead::HteError,
1353                > {
1354                    // The nonce length is fixed, so use
1355                    // HMAC(K || N || A)[1 : k] per Theorem 3.2.
1356                    let tag = {
1357                        let bytes = $crate::keys::SecretKey::try_export_secret(&self.key)?;
1358                        let key = $crate::hmac::HmacKey::<$hash>::new(
1359                            $crate::keys::RawSecretBytes::raw_secret_bytes(&bytes),
1360                        );
1361                        let mut hmac = $crate::hmac::Hmac::<$hash>::new(&key);
1362                        hmac.update(nonce);
1363                        hmac.update(ad);
1364                        hmac.tag()
1365                    };
1366                    let mut key_bytes = $crate::hybrid_array::Array::<
1367                        u8,
1368                        <<$inner as $crate::aead::Aead>::Key as $crate::keys::SecretKey>::Size,
1369                    >::default();
1370                    let k = ::core::cmp::min(tag.len(), key_bytes.as_slice().len());
1371                    key_bytes
1372                        .as_mut_slice()
1373                        .copy_from_slice(&tag.as_bytes()[..k]);
1374                    let key =
1375                        <<$inner as $crate::aead::Aead>::Key as $crate::import::Import<_>>::import(
1376                            key_bytes.as_slice(),
1377                        )?;
1378                    Ok(key)
1379                }
1380            }
1381
1382            // The `where` bound is important as it enforces the
1383            // requirement that `$inner` be a CMT-1 AEAD.
1384            impl $crate::aead::CommittingAead for $name where $inner: $crate::aead::Cmt1Aead {}
1385
1386            impl $crate::aead::Cmt1Aead for $name {}
1387
1388            impl $crate::aead::Cmt3Aead for $name {}
1389
1390            impl $crate::aead::Cmt4Aead for $name where $inner: $crate::aead::Cmt1Aead {}
1391
1392            impl $crate::aead::Aead for $name {
1393                const LIFETIME: $crate::aead::Lifetime = <$inner as $crate::aead::Aead>::LIFETIME;
1394
1395                type KeySize = <$inner as $crate::aead::Aead>::KeySize;
1396                type NonceSize = <$inner as $crate::aead::Aead>::NonceSize;
1397                // HtE has no additional overhead.
1398                type Overhead = <$inner as $crate::aead::Aead>::Overhead;
1399
1400                const MAX_PLAINTEXT_SIZE: u64 = <$inner as $crate::aead::Aead>::MAX_PLAINTEXT_SIZE;
1401                const MAX_ADDITIONAL_DATA_SIZE: u64 =
1402                    <$inner as $crate::aead::Aead>::MAX_ADDITIONAL_DATA_SIZE;
1403
1404                type Key = <$inner as $crate::aead::Aead>::Key;
1405
1406                #[inline]
1407                fn new(key: &Self::Key) -> Self {
1408                    Self { key: key.clone() }
1409                }
1410
1411                fn seal(
1412                    &self,
1413                    mut dst: &mut [u8],
1414                    nonce: &[u8],
1415                    plaintext: &[u8],
1416                    additional_data: &[u8],
1417                ) -> ::core::result::Result<(), $crate::aead::SealError> {
1418                    $crate::aead::check_seal_params::<Self>(
1419                        &mut dst,
1420                        nonce,
1421                        plaintext,
1422                        additional_data,
1423                    )?;
1424
1425                    let key = self.hash(nonce, additional_data)?;
1426                    <$inner as $crate::aead::Aead>::new(&key).seal(
1427                        dst,
1428                        nonce,
1429                        plaintext,
1430                        additional_data,
1431                    )
1432                }
1433
1434                fn seal_in_place(
1435                    &self,
1436                    nonce: &[u8],
1437                    data: &mut [u8],
1438                    overhead: &mut [u8],
1439                    additional_data: &[u8],
1440                ) -> ::core::result::Result<(), $crate::aead::SealError> {
1441                    $crate::aead::check_seal_in_place_params::<Self>(
1442                        nonce,
1443                        data,
1444                        overhead,
1445                        additional_data,
1446                    )?;
1447
1448                    let key = self.hash(nonce, additional_data)?;
1449                    <$inner as $crate::aead::Aead>::new(&key).seal_in_place(
1450                        nonce,
1451                        data,
1452                        overhead,
1453                        additional_data,
1454                    )
1455                }
1456
1457                fn open(
1458                    &self,
1459                    dst: &mut [u8],
1460                    nonce: &[u8],
1461                    ciphertext: &[u8],
1462                    additional_data: &[u8],
1463                ) -> ::core::result::Result<(), $crate::aead::OpenError> {
1464                    $crate::aead::check_open_params::<Self>(
1465                        dst,
1466                        nonce,
1467                        ciphertext,
1468                        additional_data,
1469                    )?;
1470
1471                    let key = self.hash(nonce, additional_data)?;
1472                    <$inner as $crate::aead::Aead>::new(&key).open(
1473                        dst,
1474                        nonce,
1475                        ciphertext,
1476                        additional_data,
1477                    )
1478                }
1479
1480                fn open_in_place(
1481                    &self,
1482                    nonce: &[u8],
1483                    data: &mut [u8],
1484                    overhead: &[u8],
1485                    additional_data: &[u8],
1486                ) -> ::core::result::Result<(), $crate::aead::OpenError> {
1487                    $crate::aead::check_open_in_place_params::<Self>(
1488                        nonce,
1489                        data,
1490                        overhead,
1491                        additional_data,
1492                    )?;
1493
1494                    let key = self.hash(nonce, additional_data)?;
1495                    <$inner as $crate::aead::Aead>::new(&key).open_in_place(
1496                        nonce,
1497                        data,
1498                        overhead,
1499                        additional_data,
1500                    )
1501                }
1502            }
1503
1504            $(impl $crate::oid::Identified for $name {
1505                const OID: &$crate::oid::Oid = $oid;
1506            })?
1507        };
1508    }
1509    pub(crate) use hte_aead;
1510}
1511#[cfg(feature = "committing-aead")]
1512#[cfg_attr(docsrs, doc(cfg(feature = "committing-aead")))]
1513pub use committing::*;