aws_lc_rs/
aead.rs

1// Copyright 2015-2016 Brian Smith.
2// SPDX-License-Identifier: ISC
3// Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4// SPDX-License-Identifier: Apache-2.0 OR ISC
5
6//! Authenticated Encryption with Associated Data (AEAD).
7//!
8//! See [Authenticated encryption: relations among notions and analysis of the
9//! generic composition paradigm][AEAD] for an introduction to the concept of
10//! AEADs.
11//!
12//! [AEAD]: https://eprint.iacr.org/2000/025
13//! [`crypto.cipher.AEAD`]: https://golang.org/pkg/crypto/cipher/#AEAD
14//!
15//! # Randomized Nonce API
16//!
17//! [`RandomizedNonceKey`] provides a simplified API interface that doesn't
18//! require the caller to handle construction of a `NonceSequence` or `Nonce` values
19//! themselves.
20//!
21//! ```rust
22//! # use std::error::Error;
23//! #
24//! # fn main() -> Result<(), Box<dyn Error>> {
25//! use aws_lc_rs::aead::{Aad, RandomizedNonceKey, AES_128_GCM};
26//!
27//! let key_bytes = &[
28//!     0xa5, 0xf3, 0x8d, 0x0d, 0x2d, 0x7c, 0x48, 0x56, 0xe7, 0xf3, 0xc3, 0x63, 0x0d, 0x40, 0x5b,
29//!     0x9e,
30//! ];
31//!
32//! // Create AES-128-GCM key
33//! let key = RandomizedNonceKey::new(&AES_128_GCM, key_bytes)?;
34//!
35//! let message = "test message";
36//! let mut in_out = Vec::from(message);
37//!
38//! // Seal the plaintext message (in_out) and append the tag to the ciphertext.
39//! // The randomized nonce used for encryption will be returned.
40//! let nonce = key.seal_in_place_append_tag(Aad::empty(), &mut in_out)?;
41//!
42//! // Open the ciphertext message (in_out), using the provided nonce, and validating the tag.
43//! let plaintext = key.open_in_place(nonce, Aad::empty(), &mut in_out)?;
44//!
45//! assert_eq!(message.as_bytes(), plaintext);
46//! #   Ok(())
47//! # }
48//! ```
49//!
50//! # TLS AEAD APIs
51//!
52//! Systems developers creating TLS protocol implementations should use
53//! [`TlsRecordSealingKey`] and [`TlsRecordOpeningKey`] respectively for AEAD.
54//!
55//! # Nonce Sequence APIs
56//!
57//! The [`UnboundKey`], [`OpeningKey`], [`SealingKey`], and [`LessSafeKey`] types are the
58//! AEAD API's provided for compatibility with the original *ring* API.
59//!
60//! Users should prefer [`RandomizedNonceKey`] which provides a simplified experience around
61//! Nonce construction.
62//!
63//! ```
64//! use aws_lc_rs::aead::{
65//!     nonce_sequence, Aad, BoundKey, OpeningKey, SealingKey, UnboundKey, AES_128_GCM,
66//! };
67//! use aws_lc_rs::rand;
68//! use aws_lc_rs::test::from_hex;
69//!
70//! let plaintext = "plaintext value";
71//!
72//! // Generate random bytes for secret key
73//! let mut key_bytes = [0u8; 16];
74//! rand::fill(&mut key_bytes).expect("Unable to generate key");
75//!
76//! // Contextual information must match between encryption and decryption
77//! let aad_content = "aws-lc-rs documentation";
78//! let sequence_id = 0xabcdef01u32.to_be_bytes();
79//!
80//! // Buffer containing plaintext. This will be modified to contain the ciphertext.
81//! let mut in_out_buffer = Vec::from(plaintext);
82//!
83//! // Construct a SealingKey for encryption
84//! let unbound_key = UnboundKey::new(&AES_128_GCM, &key_bytes).unwrap();
85//! let nonce_sequence = nonce_sequence::Counter64Builder::new()
86//!     .identifier(sequence_id)
87//!     .build();
88//! let mut sealing_key = SealingKey::new(unbound_key, nonce_sequence);
89//!
90//! // Encrypt a value using the SealingKey
91//! let aad = Aad::from(aad_content);
92//! sealing_key
93//!     .seal_in_place_append_tag(aad, &mut in_out_buffer)
94//!     .expect("Encryption failed");
95//!
96//! // The buffer now contains the ciphertext followed by a "tag" value.
97//! let plaintext_len = in_out_buffer.len() - AES_128_GCM.tag_len();
98//!
99//! // Construct an OpeningKey for decryption
100//! let unbound_key = UnboundKey::new(&AES_128_GCM, &key_bytes).unwrap();
101//! let nonce_sequence = nonce_sequence::Counter64Builder::new()
102//!     .identifier(sequence_id)
103//!     .build();
104//! let mut opening_key = OpeningKey::new(unbound_key, nonce_sequence);
105//!
106//! // Decrypt the value using the OpeningKey
107//! let aad = Aad::from(aad_content);
108//! opening_key
109//!     .open_in_place(aad, &mut in_out_buffer)
110//!     .expect("Decryption failed");
111//!
112//! let decrypted_plaintext = core::str::from_utf8(&in_out_buffer[0..plaintext_len]).unwrap();
113//!
114//! assert_eq!(plaintext, decrypted_plaintext);
115//! ```
116//!
117//! ## Prepared Nonce API's with Nonce Sequence
118//!
119//! If you prefer to use the [NonceSequence] based API's, and need to know the [Nonce] explicit nonce used for a
120//! cryptographic key operation operation, then [SealingKeyPreparedNonce] and
121//! [OpeningKeyPreparedNonce] are available to you.
122//!
123//! ```rust
124//! # use std::error::Error;
125//! #
126//! # fn main() -> Result<(), Box<dyn Error>> {
127//! use aws_lc_rs::aead::{
128//!     nonce_sequence::Counter32Builder, Aad, BoundKey, OpeningKey, SealingKey, UnboundKey,
129//!     AES_128_GCM,
130//! };
131//! use std::vec::Vec;
132//!
133//! let key_bytes = &[
134//!     0xa5, 0xf3, 0x8d, 0x0d, 0x2d, 0x7c, 0x48, 0x56, 0xe7, 0xf3, 0xc3, 0x63, 0x0d, 0x40, 0x5b,
135//!     0x9e,
136//! ];
137//!
138//! // Create AES-128-GCM SealingKey
139//! let mut sealing_key = SealingKey::new(
140//!     UnboundKey::new(&AES_128_GCM, key_bytes)?,
141//!     Counter32Builder::new()
142//!         .identifier([0, 1, 2, 3, 4, 5, 6, 7])
143//!         .build(),
144//! );
145//!
146//! // Create AES-128-GCM OpeningKey
147//! let mut opening_key = OpeningKey::new(
148//!     UnboundKey::new(&AES_128_GCM, key_bytes)?,
149//!     Counter32Builder::new()
150//!         .identifier([0, 1, 2, 3, 4, 5, 6, 7])
151//!         .build(),
152//! );
153//!
154//! let message = "test message";
155//! let mut in_out = Vec::from(message);
156//!
157//! // Create a SealingKeyPreparedNonce which consumes a nonce from the underlying sequence
158//! let seal_prepared_nonce = sealing_key.prepare_nonce()?;
159//!
160//! // Query the nonce that will be used for our seal operation with our prepared nonce
161//! let seal_nonce_bytes = Vec::from(seal_prepared_nonce.nonce().as_ref());
162//!
163//! // Use the prepared nonce and seal the plaintext
164//! seal_prepared_nonce.seal_in_place_append_tag(Aad::empty(), &mut in_out)?;
165//!
166//! // Create a OpeningKeyPreparedNonce which consumes a nonce from the underlying sequence
167//! let open_prepared_nonce = opening_key.prepare_nonce()?;
168//!
169//! // Query the nonce that will be used for our seal operation with our prepared nonce
170//! let open_nonce_bytes = Vec::from(open_prepared_nonce.nonce().as_ref());
171//!
172//! // Since we initialized the Counter32Builder the same between both builders the nonce here
173//! // will match the one from the opening key.
174//! assert_eq!(seal_nonce_bytes.as_slice(), open_nonce_bytes.as_slice());
175//!
176//! let plaintext = open_prepared_nonce.open_in_place(Aad::empty(), &mut in_out)?;
177//!
178//! assert_eq!(message.as_bytes(), plaintext);
179//! #   Ok(())
180//! # }
181//! ```
182
183use crate::error::Unspecified;
184use crate::{derive_debug_via_id, hkdf};
185use aead_ctx::AeadCtx;
186use core::fmt::Debug;
187use core::ops::RangeFrom;
188use core::stringify;
189
190mod aead_ctx;
191mod aes_gcm;
192mod chacha;
193pub mod chacha20_poly1305_openssh;
194mod nonce;
195pub mod nonce_sequence;
196mod poly1305;
197pub mod quic;
198mod rand_nonce;
199mod tls;
200mod unbound_key;
201
202pub use self::aes_gcm::{AES_128_GCM, AES_128_GCM_SIV, AES_192_GCM, AES_256_GCM, AES_256_GCM_SIV};
203pub use self::chacha::CHACHA20_POLY1305;
204pub use self::nonce::{Nonce, NONCE_LEN};
205pub use self::rand_nonce::RandomizedNonceKey;
206pub use self::tls::{TlsProtocolId, TlsRecordOpeningKey, TlsRecordSealingKey};
207pub use self::unbound_key::UnboundKey;
208
209/// A sequences of unique nonces.
210///
211/// A given `NonceSequence` must never return the same `Nonce` twice from
212/// `advance()`.
213///
214/// A simple counter is a reasonable (but probably not ideal) `NonceSequence`.
215///
216/// Intentionally not `Clone` or `Copy` since cloning would allow duplication
217/// of the sequence.
218pub trait NonceSequence {
219    /// Returns the next nonce in the sequence.
220    ///
221    /// # Errors
222    /// `error::Unspecified` if  "too many" nonces have been requested, where how many
223    /// is too many is up to the implementation of `NonceSequence`. An
224    /// implementation may that enforce a maximum number of records are
225    /// sent/received under a key this way. Once `advance()` fails, it must
226    /// fail for all subsequent calls.
227    fn advance(&mut self) -> Result<Nonce, Unspecified>;
228}
229
230/// An AEAD key bound to a nonce sequence.
231pub trait BoundKey<N: NonceSequence>: Debug {
232    /// Constructs a new key from the given `UnboundKey` and `NonceSequence`.
233    fn new(key: UnboundKey, nonce_sequence: N) -> Self;
234
235    /// The key's AEAD algorithm.
236    fn algorithm(&self) -> &'static Algorithm;
237}
238
239/// An AEAD key for authenticating and decrypting ("opening"), bound to a nonce
240/// sequence.
241///
242/// Intentionally not `Clone` or `Copy` since cloning would allow duplication
243/// of the nonce sequence.
244///
245/// Prefer [`RandomizedNonceKey`] for opening operations.
246pub struct OpeningKey<N: NonceSequence> {
247    key: UnboundKey,
248    nonce_sequence: N,
249}
250
251impl<N: NonceSequence> BoundKey<N> for OpeningKey<N> {
252    fn new(key: UnboundKey, nonce_sequence: N) -> Self {
253        Self {
254            key,
255            nonce_sequence,
256        }
257    }
258
259    #[inline]
260    fn algorithm(&self) -> &'static Algorithm {
261        self.key.algorithm()
262    }
263}
264
265impl<N: NonceSequence> Debug for OpeningKey<N> {
266    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
267        f.debug_struct("OpeningKey")
268            .field("algorithm", &self.algorithm())
269            .finish()
270    }
271}
272
273impl<N: NonceSequence> OpeningKey<N> {
274    /// Authenticates and decrypts (“opens”) data in place.
275    ///
276    /// `aad` is the additional authenticated data (AAD), if any.
277    ///
278    /// On input, `in_out` must be the ciphertext followed by the tag. When
279    /// `open_in_place()` returns `Ok(plaintext)`, the input ciphertext
280    /// has been overwritten by the plaintext; `plaintext` will refer to the
281    /// plaintext without the tag.
282    ///
283    /// Prefer [`RandomizedNonceKey::open_in_place`].
284    // # FIPS
285    // Use this method with one of the following algorithms:
286    // * `AES_128_GCM`
287    // * `AES_256_GCM`
288    //
289    /// # Errors
290    /// `error::Unspecified` when ciphertext is invalid. In this case, `in_out` may have been
291    /// overwritten in an unspecified way.
292    #[inline]
293    #[allow(clippy::needless_pass_by_value)]
294    pub fn open_in_place<'in_out, A>(
295        &mut self,
296        aad: Aad<A>,
297        in_out: &'in_out mut [u8],
298    ) -> Result<&'in_out mut [u8], Unspecified>
299    where
300        A: AsRef<[u8]>,
301    {
302        self.key
303            .open_within(self.nonce_sequence.advance()?, aad.as_ref(), in_out, 0..)
304    }
305
306    /// Authenticates and decrypts (“opens”) data in place, with a shift.
307    ///
308    /// `aad` is the additional authenticated data (AAD), if any.
309    ///
310    /// On input, `in_out[ciphertext_and_tag]` must be the ciphertext followed
311    /// by the tag. When `open_within()` returns `Ok(plaintext)`, the plaintext
312    /// will be at `in_out[0..plaintext.len()]`. In other words, the following
313    /// two code fragments are equivalent for valid values of
314    /// `ciphertext_and_tag`, except `open_within` will often be more efficient:
315    ///
316    ///
317    /// ```skip
318    /// let plaintext = key.open_within(aad, in_out, cipertext_and_tag)?;
319    /// ```
320    ///
321    /// ```skip
322    /// let ciphertext_and_tag_len = in_out[ciphertext_and_tag].len();
323    /// in_out.copy_within(ciphertext_and_tag, 0);
324    /// let plaintext = key.open_in_place(aad, &mut in_out[..ciphertext_and_tag_len])?;
325    /// ```
326    ///
327    /// Similarly, `key.open_within(aad, in_out, 0..)` is equivalent to
328    /// `key.open_in_place(aad, in_out)`.
329    ///
330    ///
331    /// The shifting feature is useful in the case where multiple packets are
332    /// being reassembled in place. Consider this example where the peer has
333    /// sent the message “Split stream reassembled in place” split into
334    /// three sealed packets:
335    ///
336    /// ```ascii-art
337    ///                 Packet 1                  Packet 2                 Packet 3
338    /// Input:  [Header][Ciphertext][Tag][Header][Ciphertext][Tag][Header][Ciphertext][Tag]
339    ///                      |         +--------------+                        |
340    ///               +------+   +-----+    +----------------------------------+
341    ///               v          v          v
342    /// Output: [Plaintext][Plaintext][Plaintext]
343    ///        “Split stream reassembled in place”
344    /// ```
345    ///
346    /// This reassembly be accomplished with three calls to `open_within()`.
347    ///
348    /// Prefer [`RandomizedNonceKey::open_in_place`].
349    // # FIPS
350    // Use this method with one of the following algorithms:
351    // * `AES_128_GCM`
352    // * `AES_256_GCM`
353    //
354    /// # Errors
355    /// `error::Unspecified` when ciphertext is invalid. In this case, `in_out` may have been
356    /// overwritten in an unspecified way.
357    #[inline]
358    #[allow(clippy::needless_pass_by_value)]
359    pub fn open_within<'in_out, A>(
360        &mut self,
361        aad: Aad<A>,
362        in_out: &'in_out mut [u8],
363        ciphertext_and_tag: RangeFrom<usize>,
364    ) -> Result<&'in_out mut [u8], Unspecified>
365    where
366        A: AsRef<[u8]>,
367    {
368        self.key.open_within(
369            self.nonce_sequence.advance()?,
370            aad.as_ref(),
371            in_out,
372            ciphertext_and_tag,
373        )
374    }
375
376    /// Returns a `OpeningKeyPreparedNonce` containing the next computed `Nonce` consumed from `NonceSequence`.
377    ///
378    /// The encapsulated Nonce will be used **if and only if** either
379    /// [OpeningKeyPreparedNonce::open_in_place] or [OpeningKeyPreparedNonce::open_within]
380    /// are invoked. Dropping `OpeningKeyPreparedNonce` without invoking either method results in the nonce remaining
381    /// consumed and unused within the associated `NonceSequence`. Subsequent calls to [OpeningKey] methods will
382    /// always use a proceeding nonce from the `NonceSequence` regardless of whether
383    /// a `OpeningKeyPreparedNonce` is consumed or not.
384    ///
385    /// # Errors
386    /// `Unspecified` if there is a failure computing the nonce for the next operation, i.e. `NonceSequence` exhausted.
387    pub fn prepare_nonce(&mut self) -> Result<OpeningKeyPreparedNonce<'_, N>, Unspecified> {
388        OpeningKeyPreparedNonce::new(self)
389    }
390}
391
392/// An AEAD key for encrypting and signing ("sealing"), bound to a nonce
393/// sequence.
394///
395/// Intentionally not `Clone` or `Copy` since cloning would allow duplication
396/// of the nonce sequence.
397///
398/// Prefer [`RandomizedNonceKey`] for sealing operations.
399pub struct SealingKey<N: NonceSequence> {
400    key: UnboundKey,
401    nonce_sequence: N,
402}
403
404impl<N: NonceSequence> BoundKey<N> for SealingKey<N> {
405    fn new(key: UnboundKey, nonce_sequence: N) -> Self {
406        Self {
407            key,
408            nonce_sequence,
409        }
410    }
411
412    #[inline]
413    fn algorithm(&self) -> &'static Algorithm {
414        self.key.algorithm()
415    }
416}
417
418impl<N: NonceSequence> Debug for SealingKey<N> {
419    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
420        f.debug_struct("SealingKey")
421            .field("algorithm", &self.algorithm())
422            .finish()
423    }
424}
425
426impl<N: NonceSequence> SealingKey<N> {
427    /// Deprecated. Renamed to `seal_in_place_append_tag`.
428    ///
429    /// Prefer [`RandomizedNonceKey::seal_in_place_append_tag`].
430    // # FIPS
431    // This method must not be used.
432    //
433    /// # Errors
434    /// See `seal_in_place_append_tag`
435    #[deprecated(note = "Renamed to `seal_in_place_append_tag`.")]
436    #[inline]
437    pub fn seal_in_place<A, InOut>(
438        &mut self,
439        aad: Aad<A>,
440        in_out: &mut InOut,
441    ) -> Result<(), Unspecified>
442    where
443        A: AsRef<[u8]>,
444        InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
445    {
446        self.seal_in_place_append_tag(aad, in_out)
447    }
448
449    /// Encrypts and signs (“seals”) data in place, appending the tag to the
450    /// resulting ciphertext.
451    ///
452    /// `key.seal_in_place_append_tag(aad, in_out)` is equivalent to:
453    ///
454    /// ```skip
455    /// key.seal_in_place_separate_tag(aad, in_out.as_mut())
456    ///     .map(|tag| in_out.extend(tag.as_ref()))
457    /// ```
458    ///
459    /// Prefer [`RandomizedNonceKey::seal_in_place_append_tag`].
460    // # FIPS
461    // This method must not be used.
462    //
463    /// # Errors
464    /// `error::Unspecified` when `nonce_sequence` cannot be advanced.
465    #[inline]
466    #[allow(clippy::needless_pass_by_value)]
467    pub fn seal_in_place_append_tag<A, InOut>(
468        &mut self,
469        aad: Aad<A>,
470        in_out: &mut InOut,
471    ) -> Result<(), Unspecified>
472    where
473        A: AsRef<[u8]>,
474        InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
475    {
476        self.key
477            .seal_in_place_append_tag(Some(self.nonce_sequence.advance()?), aad.as_ref(), in_out)
478            .map(|_| ())
479    }
480
481    /// Encrypts and signs (“seals”) data in place.
482    ///
483    /// `aad` is the additional authenticated data (AAD), if any. This is
484    /// authenticated but not encrypted. The type `A` could be a byte slice
485    /// `&[u8]`, a byte array `[u8; N]` for some constant `N`, `Vec<u8>`, etc.
486    /// If there is no AAD then use `Aad::empty()`.
487    ///
488    /// The plaintext is given as the input value of `in_out`. `seal_in_place()`
489    /// will overwrite the plaintext with the ciphertext and return the tag.
490    /// For most protocols, the caller must append the tag to the ciphertext.
491    /// The tag will be `self.algorithm.tag_len()` bytes long.
492    ///
493    /// Prefer [`RandomizedNonceKey::seal_in_place_separate_tag`].
494    // # FIPS
495    // This method must not be used.
496    //
497    /// # Errors
498    /// `error::Unspecified` when `nonce_sequence` cannot be advanced.
499    #[inline]
500    #[allow(clippy::needless_pass_by_value)]
501    pub fn seal_in_place_separate_tag<A>(
502        &mut self,
503        aad: Aad<A>,
504        in_out: &mut [u8],
505    ) -> Result<Tag, Unspecified>
506    where
507        A: AsRef<[u8]>,
508    {
509        self.key
510            .seal_in_place_separate_tag(Some(self.nonce_sequence.advance()?), aad.as_ref(), in_out)
511            .map(|(_, tag)| tag)
512    }
513
514    /// Returns a `SealingKeyPreparedNonce` containing the next computed `Nonce` consumed from `NonceSequence`.
515    ///
516    /// The encapsulated Nonce will be used **if and only if** either
517    /// [SealingKeyPreparedNonce::seal_in_place_append_tag] or [SealingKeyPreparedNonce::seal_in_place_separate_tag]
518    /// are invoked. Dropping `SealingKeyPreparedNonce` without invoking either method results in the nonce remaining
519    /// consumed and unused within the associated `NonceSequence`. Subsequent calls to [SealingKey] methods will
520    /// always use a proceeding nonce from the `NonceSequence` regardless of whether
521    /// a `SealingKeyPreparedNonce` is consumed or not.
522    ///
523    /// # Errors
524    /// `Unspecified` if there is a failure computing the nonce for the next operation, i.e. `NonceSequence` exhausted.
525    pub fn prepare_nonce(&mut self) -> Result<SealingKeyPreparedNonce<'_, N>, Unspecified> {
526        SealingKeyPreparedNonce::new(self)
527    }
528}
529
530macro_rules! nonce_seq_key_op_mut {
531    ($name:ident, $name_prep_nonce:ident) => {
532        /// A key operation with a precomputed nonce from a key's associated `NonceSequence`.
533        pub struct $name_prep_nonce<'a, N: NonceSequence> {
534            key: &'a mut $name<N>,
535            nonce: Nonce,
536        }
537
538        impl<'a, N: NonceSequence> $name_prep_nonce<'a, N> {
539            fn new(key: &'a mut $name<N>) -> Result<Self, Unspecified> {
540                let nonce = key.nonce_sequence.advance()?;
541                Ok(Self { key, nonce })
542            }
543        }
544
545        impl<N: NonceSequence> $name_prep_nonce<'_, N> {
546            /// Returns the prepared Nonce that is used for key methods invoked on [Self].
547            #[must_use]
548            pub fn nonce(&self) -> &Nonce {
549                &self.nonce
550            }
551        }
552
553        impl<N: NonceSequence> Debug for $name_prep_nonce<'_, N> {
554            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
555                f.debug_struct(stringify!($name_prep_nonce))
556                    .finish_non_exhaustive()
557            }
558        }
559    };
560}
561
562nonce_seq_key_op_mut!(OpeningKey, OpeningKeyPreparedNonce);
563nonce_seq_key_op_mut!(SealingKey, SealingKeyPreparedNonce);
564
565impl<N: NonceSequence> OpeningKeyPreparedNonce<'_, N> {
566    /// Authenticates and decrypts (“opens”) data in place.
567    ///
568    /// See [OpeningKey::open_in_place] for additional API information.
569    ///
570    /// # Errors
571    /// `error::Unspecified` when ciphertext is invalid. In this case, `in_out` may have been
572    /// overwritten in an unspecified way.
573    #[inline]
574    #[allow(clippy::needless_pass_by_value)]
575    pub fn open_in_place<A>(self, aad: Aad<A>, in_out: &mut [u8]) -> Result<&mut [u8], Unspecified>
576    where
577        A: AsRef<[u8]>,
578    {
579        self.open_within(aad, in_out, 0..)
580    }
581
582    /// Authenticates and decrypts (“opens”) data in place, with a shift.
583    ///
584    /// See [OpeningKey::open_within] for additional API information.
585    ///
586    /// # Errors
587    /// `error::Unspecified` when ciphertext is invalid. In this case, `in_out` may have been
588    /// overwritten in an unspecified way.
589    #[inline]
590    #[allow(clippy::needless_pass_by_value)]
591    pub fn open_within<A>(
592        self,
593        aad: Aad<A>,
594        in_out: &mut [u8],
595        ciphertext_and_tag: RangeFrom<usize>,
596    ) -> Result<&mut [u8], Unspecified>
597    where
598        A: AsRef<[u8]>,
599    {
600        self.key
601            .key
602            .open_within(self.nonce, aad.as_ref(), in_out, ciphertext_and_tag)
603    }
604}
605
606impl<N: NonceSequence> SealingKeyPreparedNonce<'_, N> {
607    /// Encrypts and signs (“seals”) data in place, appending the tag to the
608    /// resulting ciphertext.
609    ///
610    /// See [SealingKey::seal_in_place_append_tag] for additional API information.
611    ///
612    /// # Errors
613    /// `error::Unspecified` when `nonce_sequence` cannot be advanced.
614    #[inline]
615    #[allow(clippy::needless_pass_by_value)]
616    pub fn seal_in_place_append_tag<A, InOut>(
617        self,
618        aad: Aad<A>,
619        in_out: &mut InOut,
620    ) -> Result<(), Unspecified>
621    where
622        A: AsRef<[u8]>,
623        InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
624    {
625        self.key
626            .key
627            .seal_in_place_append_tag(Some(self.nonce), aad.as_ref(), in_out)
628            .map(|_| ())
629    }
630
631    /// Encrypts and signs (“seals”) data in place.
632    ///
633    /// See [`SealingKey::seal_in_place_separate_tag`] for additional API information.
634    ///
635    /// # Errors
636    /// `error::Unspecified` when `nonce_sequence` cannot be advanced.
637    #[inline]
638    #[allow(clippy::needless_pass_by_value)]
639    pub fn seal_in_place_separate_tag<A>(
640        self,
641        aad: Aad<A>,
642        in_out: &mut [u8],
643    ) -> Result<Tag, Unspecified>
644    where
645        A: AsRef<[u8]>,
646    {
647        self.key
648            .key
649            .seal_in_place_separate_tag(Some(self.nonce), aad.as_ref(), in_out)
650            .map(|(_, tag)| tag)
651    }
652}
653
654/// The additionally authenticated data (AAD) for an opening or sealing
655/// operation. This data is authenticated but is **not** encrypted.
656///
657/// The type `A` could be a byte slice `&[u8]`, a byte array `[u8; N]`
658/// for some constant `N`, `Vec<u8>`, etc.
659pub struct Aad<A: AsRef<[u8]>>(A);
660
661impl<A: AsRef<[u8]>> Aad<A> {
662    /// Construct the `Aad` from the given bytes.
663    #[inline]
664    pub fn from(aad: A) -> Self {
665        Aad(aad)
666    }
667}
668
669impl<A> AsRef<[u8]> for Aad<A>
670where
671    A: AsRef<[u8]>,
672{
673    fn as_ref(&self) -> &[u8] {
674        self.0.as_ref()
675    }
676}
677
678impl Aad<[u8; 0]> {
679    /// Construct an empty `Aad`.
680    #[must_use]
681    pub fn empty() -> Self {
682        Self::from([])
683    }
684}
685
686impl hkdf::KeyType for &'static Algorithm {
687    #[inline]
688    fn len(&self) -> usize {
689        self.key_len()
690    }
691}
692
693/// Immutable keys for use in situations where `OpeningKey`/`SealingKey` and
694/// `NonceSequence` cannot reasonably be used.
695///
696/// Prefer [`RandomizedNonceKey`] when practical.
697// # FIPS
698// The following conditions must be met:
699// * `UnboundKey`'s algorithm is one of:
700//   * `AES_128_GCM`
701//   * `AES_256_GCM`
702// * Use `open_in_place` or `open_within` only.
703pub struct LessSafeKey {
704    key: UnboundKey,
705}
706
707impl LessSafeKey {
708    /// Constructs a `LessSafeKey` from an `UnboundKey`.
709    #[must_use]
710    pub fn new(key: UnboundKey) -> Self {
711        Self { key }
712    }
713
714    /// Like [`OpeningKey::open_in_place()`], except it accepts an arbitrary nonce.
715    ///
716    /// `nonce` must be unique for every use of the key to open data.
717    ///
718    /// Prefer [`RandomizedNonceKey::open_in_place`].
719    // # FIPS
720    // Use this method with one of the following algorithms:
721    // * `AES_128_GCM`
722    // * `AES_256_GCM`
723    //
724    /// # Errors
725    /// `error::Unspecified` when ciphertext is invalid.
726    #[inline]
727    pub fn open_in_place<'in_out, A>(
728        &self,
729        nonce: Nonce,
730        aad: Aad<A>,
731        in_out: &'in_out mut [u8],
732    ) -> Result<&'in_out mut [u8], Unspecified>
733    where
734        A: AsRef<[u8]>,
735    {
736        self.open_within(nonce, aad, in_out, 0..)
737    }
738
739    /// Like [`OpeningKey::open_within()`], except it accepts an arbitrary nonce.
740    ///
741    /// `nonce` must be unique for every use of the key to open data.
742    ///
743    /// Prefer [`RandomizedNonceKey::open_in_place`].
744    // # FIPS
745    // Use this method with one of the following algorithms:
746    // * `AES_128_GCM`
747    // * `AES_256_GCM`
748    //
749    /// # Errors
750    /// `error::Unspecified` when ciphertext is invalid.
751    #[inline]
752    #[allow(clippy::needless_pass_by_value)]
753    pub fn open_within<'in_out, A>(
754        &self,
755        nonce: Nonce,
756        aad: Aad<A>,
757        in_out: &'in_out mut [u8],
758        ciphertext_and_tag: RangeFrom<usize>,
759    ) -> Result<&'in_out mut [u8], Unspecified>
760    where
761        A: AsRef<[u8]>,
762    {
763        self.key
764            .open_within(nonce, aad.as_ref(), in_out, ciphertext_and_tag)
765    }
766
767    /// Authenticates and decrypts (“opens”) data into another provided slice.
768    ///
769    /// `aad` is the additional authenticated data (AAD), if any.
770    ///
771    /// On input, `in_ciphertext` must be the ciphertext. The tag must be provided in
772    /// `in_tag`.
773    ///
774    /// The `out_plaintext` length must match the provided `in_ciphertext`.
775    ///
776    /// # Errors
777    /// `error::Unspecified` when ciphertext is invalid. In this case, `out_plaintext` may
778    /// have been overwritten in an unspecified way.
779    #[inline]
780    #[allow(clippy::needless_pass_by_value)]
781    pub fn open_separate_gather<A>(
782        &self,
783        nonce: Nonce,
784        aad: Aad<A>,
785        in_ciphertext: &[u8],
786        in_tag: &[u8],
787        out_plaintext: &mut [u8],
788    ) -> Result<(), Unspecified>
789    where
790        A: AsRef<[u8]>,
791    {
792        self.key
793            .open_separate_gather(&nonce, aad.as_ref(), in_ciphertext, in_tag, out_plaintext)
794    }
795
796    /// Deprecated. Renamed to `seal_in_place_append_tag()`.
797    ///
798    /// Prefer [`RandomizedNonceKey::seal_in_place_append_tag`].
799    // # FIPS
800    // This method must not be used.
801    //
802    #[deprecated(note = "Renamed to `seal_in_place_append_tag`.")]
803    #[inline]
804    #[allow(clippy::missing_errors_doc)]
805    pub fn seal_in_place<A, InOut>(
806        &self,
807        nonce: Nonce,
808        aad: Aad<A>,
809        in_out: &mut InOut,
810    ) -> Result<(), Unspecified>
811    where
812        A: AsRef<[u8]>,
813        InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
814    {
815        self.seal_in_place_append_tag(nonce, aad, in_out)
816    }
817
818    /// Like [`SealingKey::seal_in_place_append_tag()`], except it accepts an
819    /// arbitrary nonce.
820    ///
821    /// `nonce` must be unique for every use of the key to seal data.
822    ///
823    /// Prefer [`RandomizedNonceKey::seal_in_place_append_tag`].
824    // # FIPS
825    // This method must not be used.
826    //
827    /// # Errors
828    /// `error::Unspecified` if encryption operation fails.
829    #[inline]
830    #[allow(clippy::needless_pass_by_value)]
831    pub fn seal_in_place_append_tag<A, InOut>(
832        &self,
833        nonce: Nonce,
834        aad: Aad<A>,
835        in_out: &mut InOut,
836    ) -> Result<(), Unspecified>
837    where
838        A: AsRef<[u8]>,
839        InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
840    {
841        self.key
842            .seal_in_place_append_tag(Some(nonce), aad.as_ref(), in_out)
843            .map(|_| ())
844    }
845
846    /// Like `SealingKey::seal_in_place_separate_tag()`, except it accepts an
847    /// arbitrary nonce.
848    ///
849    /// `nonce` must be unique for every use of the key to seal data.
850    ///
851    /// Prefer [`RandomizedNonceKey::seal_in_place_separate_tag`].
852    // # FIPS
853    // This method must not be used.
854    //
855    /// # Errors
856    /// `error::Unspecified` if encryption operation fails.
857    #[inline]
858    #[allow(clippy::needless_pass_by_value)]
859    pub fn seal_in_place_separate_tag<A>(
860        &self,
861        nonce: Nonce,
862        aad: Aad<A>,
863        in_out: &mut [u8],
864    ) -> Result<Tag, Unspecified>
865    where
866        A: AsRef<[u8]>,
867    {
868        self.key
869            .seal_in_place_separate_tag(Some(nonce), aad.as_ref(), in_out)
870            .map(|(_, tag)| tag)
871    }
872
873    /// Encrypts and signs (“seals”) data in place with extra plaintext.
874    ///
875    /// `aad` is the additional authenticated data (AAD), if any. This is
876    /// authenticated but not encrypted. The type `A` could be a byte slice
877    /// `&[u8]`, a byte array `[u8; N]` for some constant `N`, `Vec<u8>`, etc.
878    /// If there is no AAD then use `Aad::empty()`.
879    ///
880    /// The plaintext is given as the input value of `in_out` and `extra_in`. `seal_in_place()`
881    /// will overwrite the plaintext contained in `in_out` with the ciphertext. The `extra_in` will
882    /// be encrypted into the `extra_out_and_tag`, along with the tag.
883    /// The `extra_out_and_tag` length must be equal to the `extra_len` and `self.algorithm.tag_len()`.
884    ///
885    /// `nonce` must be unique for every use of the key to seal data.
886    // # FIPS
887    // This method must not be used.
888    //
889    /// # Errors
890    /// `error::Unspecified` if encryption operation fails.
891    #[inline]
892    #[allow(clippy::needless_pass_by_value)]
893    pub fn seal_in_place_scatter<A>(
894        &self,
895        nonce: Nonce,
896        aad: Aad<A>,
897        in_out: &mut [u8],
898        extra_in: &[u8],
899        extra_out_and_tag: &mut [u8],
900    ) -> Result<(), Unspecified>
901    where
902        A: AsRef<[u8]>,
903    {
904        self.key.seal_in_place_separate_scatter(
905            nonce,
906            aad.as_ref(),
907            in_out,
908            extra_in,
909            extra_out_and_tag,
910        )
911    }
912
913    /// The key's AEAD algorithm.
914    #[inline]
915    #[must_use]
916    pub fn algorithm(&self) -> &'static Algorithm {
917        self.key.algorithm()
918    }
919}
920
921impl Debug for LessSafeKey {
922    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
923        f.debug_struct("LessSafeKey")
924            .field("algorithm", self.algorithm())
925            .finish()
926    }
927}
928
929/// An AEAD Algorithm.
930pub struct Algorithm {
931    init: fn(key: &[u8], tag_len: usize) -> Result<AeadCtx, Unspecified>,
932    key_len: usize,
933    id: AlgorithmID,
934
935    // /// Use `max_input_len!()` to initialize this.
936    // TODO: Make this `usize`.
937    max_input_len: u64,
938}
939
940impl Algorithm {
941    /// The length of the key.
942    #[inline]
943    #[must_use]
944    pub fn key_len(&self) -> usize {
945        self.key_len
946    }
947
948    /// The length of a tag.
949    ///
950    /// See also `MAX_TAG_LEN`.
951    #[inline]
952    #[must_use]
953    pub fn tag_len(&self) -> usize {
954        TAG_LEN
955    }
956
957    /// The length of the nonces.
958    #[inline]
959    #[must_use]
960    pub fn nonce_len(&self) -> usize {
961        NONCE_LEN
962    }
963}
964
965derive_debug_via_id!(Algorithm);
966
967#[derive(Debug, Eq, PartialEq, Copy, Clone)]
968#[allow(non_camel_case_types)]
969enum AlgorithmID {
970    AES_128_GCM,
971    AES_192_GCM,
972    AES_256_GCM,
973    AES_128_GCM_SIV,
974    AES_256_GCM_SIV,
975    CHACHA20_POLY1305,
976}
977
978impl PartialEq for Algorithm {
979    #[inline]
980    fn eq(&self, other: &Self) -> bool {
981        self.id == other.id
982    }
983}
984
985impl Eq for Algorithm {}
986
987/// An authentication tag.
988#[must_use]
989#[repr(C)]
990pub struct Tag([u8; MAX_TAG_LEN], usize);
991
992impl AsRef<[u8]> for Tag {
993    fn as_ref(&self) -> &[u8] {
994        self.0[..self.1].as_ref()
995    }
996}
997
998impl core::fmt::Debug for Tag {
999    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> core::fmt::Result {
1000        f.debug_tuple("Tag").finish()
1001    }
1002}
1003
1004const MAX_KEY_LEN: usize = 32;
1005
1006// All the AEADs we support use 128-bit tags.
1007const TAG_LEN: usize = 16;
1008
1009/// The maximum length of a tag for the algorithms in this module.
1010pub const MAX_TAG_LEN: usize = TAG_LEN;
1011
1012#[cfg(test)]
1013mod tests {
1014    use nonce_sequence::Counter32Builder;
1015
1016    use super::*;
1017    use crate::iv::FixedLength;
1018    use crate::test::from_hex;
1019
1020    #[cfg(feature = "fips")]
1021    mod fips;
1022
1023    #[test]
1024    fn test_aes_128() {
1025        let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap();
1026        let og_nonce = from_hex("5bf11a0951f0bfc7ea5c9e58").unwrap();
1027        let plaintext = from_hex("00112233445566778899aabbccddeeff").unwrap();
1028        let unbound_key = UnboundKey::new(&AES_128_GCM, &key).unwrap();
1029        assert_eq!(&AES_128_GCM, unbound_key.algorithm());
1030
1031        assert_eq!(16, AES_128_GCM.tag_len());
1032        assert_eq!(12, AES_128_GCM.nonce_len());
1033
1034        let less_safe_key = LessSafeKey::new(unbound_key);
1035
1036        let nonce: [u8; NONCE_LEN] = og_nonce.as_slice().try_into().unwrap();
1037        let mut in_out = Vec::from(plaintext.as_slice());
1038
1039        #[allow(deprecated)]
1040        less_safe_key
1041            // Test coverage for `seal_in_place`, which calls `seal_in_place_append_tag`.
1042            .seal_in_place(Nonce(FixedLength::from(nonce)), Aad::empty(), &mut in_out)
1043            .unwrap();
1044
1045        let mut in_out_clone = in_out.clone();
1046        let nonce: [u8; NONCE_LEN] = og_nonce.as_slice().try_into().unwrap();
1047        assert!(less_safe_key
1048            .open_in_place(
1049                Nonce(FixedLength::from(nonce)),
1050                Aad::from("test"),
1051                &mut in_out_clone
1052            )
1053            .is_err());
1054
1055        let mut in_out_clone = in_out.clone();
1056        let mut nonce: [u8; NONCE_LEN] = og_nonce.as_slice().try_into().unwrap();
1057        nonce[0] = 0;
1058        assert!(less_safe_key
1059            .open_in_place(
1060                Nonce(FixedLength::from(nonce)),
1061                Aad::empty(),
1062                &mut in_out_clone
1063            )
1064            .is_err());
1065
1066        let nonce: [u8; NONCE_LEN] = og_nonce.as_slice().try_into().unwrap();
1067        less_safe_key
1068            .open_in_place(Nonce(FixedLength::from(nonce)), Aad::empty(), &mut in_out)
1069            .unwrap();
1070
1071        assert_eq!(plaintext, in_out[..plaintext.len()]);
1072    }
1073
1074    #[test]
1075    fn debug_prepared_nonce() {
1076        let mut sk = SealingKey::new(
1077            UnboundKey::new(&AES_128_GCM, &[0u8; 16]).unwrap(),
1078            Counter32Builder::new().build(),
1079        );
1080        let mut ok = OpeningKey::new(
1081            UnboundKey::new(&AES_128_GCM, &[0u8; 16]).unwrap(),
1082            Counter32Builder::new().build(),
1083        );
1084        let so = sk.prepare_nonce().unwrap();
1085        let oo = ok.prepare_nonce().unwrap();
1086        assert_eq!("SealingKeyPreparedNonce { .. }", format!("{so:?}"));
1087        assert_eq!("OpeningKeyPreparedNonce { .. }", format!("{oo:?}"));
1088    }
1089
1090    #[test]
1091    fn debug_tag() {
1092        let tag = Tag([0u8; MAX_TAG_LEN], MAX_TAG_LEN);
1093        assert_eq!("Tag", format!("{tag:?}"));
1094    }
1095}