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