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