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;
189use paste::paste;
190
191mod aead_ctx;
192mod aes_gcm;
193mod chacha;
194pub mod chacha20_poly1305_openssh;
195mod nonce;
196pub mod nonce_sequence;
197mod poly1305;
198pub mod quic;
199mod rand_nonce;
200mod tls;
201mod unbound_key;
202
203pub use self::aes_gcm::{AES_128_GCM, AES_128_GCM_SIV, AES_192_GCM, AES_256_GCM, AES_256_GCM_SIV};
204pub use self::chacha::CHACHA20_POLY1305;
205pub use self::nonce::{Nonce, NONCE_LEN};
206pub use self::rand_nonce::RandomizedNonceKey;
207pub use self::tls::{TlsProtocolId, TlsRecordOpeningKey, TlsRecordSealingKey};
208pub use self::unbound_key::UnboundKey;
209
210/// A sequences of unique nonces.
211///
212/// A given `NonceSequence` must never return the same `Nonce` twice from
213/// `advance()`.
214///
215/// A simple counter is a reasonable (but probably not ideal) `NonceSequence`.
216///
217/// Intentionally not `Clone` or `Copy` since cloning would allow duplication
218/// of the sequence.
219pub trait NonceSequence {
220    /// Returns the next nonce in the sequence.
221    ///
222    /// # Errors
223    /// `error::Unspecified` if  "too many" nonces have been requested, where how many
224    /// is too many is up to the implementation of `NonceSequence`. An
225    /// implementation may that enforce a maximum number of records are
226    /// sent/received under a key this way. Once `advance()` fails, it must
227    /// fail for all subsequent calls.
228    fn advance(&mut self) -> Result<Nonce, Unspecified>;
229}
230
231/// An AEAD key bound to a nonce sequence.
232pub trait BoundKey<N: NonceSequence>: Debug {
233    /// Constructs a new key from the given `UnboundKey` and `NonceSequence`.
234    fn new(key: UnboundKey, nonce_sequence: N) -> Self;
235
236    /// The key's AEAD algorithm.
237    fn algorithm(&self) -> &'static Algorithm;
238}
239
240/// An AEAD key for authenticating and decrypting ("opening"), bound to a nonce
241/// sequence.
242///
243/// Intentionally not `Clone` or `Copy` since cloning would allow duplication
244/// of the nonce sequence.
245///
246/// Prefer [`RandomizedNonceKey`] for opening operations.
247pub struct OpeningKey<N: NonceSequence> {
248    key: UnboundKey,
249    nonce_sequence: N,
250}
251
252impl<N: NonceSequence> BoundKey<N> for OpeningKey<N> {
253    fn new(key: UnboundKey, nonce_sequence: N) -> Self {
254        Self {
255            key,
256            nonce_sequence,
257        }
258    }
259
260    #[inline]
261    fn algorithm(&self) -> &'static Algorithm {
262        self.key.algorithm()
263    }
264}
265
266impl<N: NonceSequence> Debug for OpeningKey<N> {
267    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
268        f.debug_struct("OpeningKey")
269            .field("algorithm", &self.algorithm())
270            .finish()
271    }
272}
273
274impl<N: NonceSequence> OpeningKey<N> {
275    /// Authenticates and decrypts (“opens”) data in place.
276    ///
277    /// `aad` is the additional authenticated data (AAD), if any.
278    ///
279    /// On input, `in_out` must be the ciphertext followed by the tag. When
280    /// `open_in_place()` returns `Ok(plaintext)`, the input ciphertext
281    /// has been overwritten by the plaintext; `plaintext` will refer to the
282    /// plaintext without the tag.
283    ///
284    /// Prefer [`RandomizedNonceKey::open_in_place`].
285    // # FIPS
286    // Use this method with one of the following algorithms:
287    // * `AES_128_GCM`
288    // * `AES_256_GCM`
289    //
290    /// # Errors
291    /// `error::Unspecified` when ciphertext is invalid. In this case, `in_out` may have been
292    /// overwritten in an unspecified way.
293    #[inline]
294    #[allow(clippy::needless_pass_by_value)]
295    pub fn open_in_place<'in_out, A>(
296        &mut self,
297        aad: Aad<A>,
298        in_out: &'in_out mut [u8],
299    ) -> Result<&'in_out mut [u8], Unspecified>
300    where
301        A: AsRef<[u8]>,
302    {
303        self.key
304            .open_within(self.nonce_sequence.advance()?, aad.as_ref(), in_out, 0..)
305    }
306
307    /// Authenticates and decrypts (“opens”) data in place, with a shift.
308    ///
309    /// `aad` is the additional authenticated data (AAD), if any.
310    ///
311    /// On input, `in_out[ciphertext_and_tag]` must be the ciphertext followed
312    /// by the tag. When `open_within()` returns `Ok(plaintext)`, the plaintext
313    /// will be at `in_out[0..plaintext.len()]`. In other words, the following
314    /// two code fragments are equivalent for valid values of
315    /// `ciphertext_and_tag`, except `open_within` will often be more efficient:
316    ///
317    ///
318    /// ```skip
319    /// let plaintext = key.open_within(aad, in_out, cipertext_and_tag)?;
320    /// ```
321    ///
322    /// ```skip
323    /// let ciphertext_and_tag_len = in_out[ciphertext_and_tag].len();
324    /// in_out.copy_within(ciphertext_and_tag, 0);
325    /// let plaintext = key.open_in_place(aad, &mut in_out[..ciphertext_and_tag_len])?;
326    /// ```
327    ///
328    /// Similarly, `key.open_within(aad, in_out, 0..)` is equivalent to
329    /// `key.open_in_place(aad, in_out)`.
330    ///
331    ///
332    /// The shifting feature is useful in the case where multiple packets are
333    /// being reassembled in place. Consider this example where the peer has
334    /// sent the message “Split stream reassembled in place” split into
335    /// three sealed packets:
336    ///
337    /// ```ascii-art
338    ///                 Packet 1                  Packet 2                 Packet 3
339    /// Input:  [Header][Ciphertext][Tag][Header][Ciphertext][Tag][Header][Ciphertext][Tag]
340    ///                      |         +--------------+                        |
341    ///               +------+   +-----+    +----------------------------------+
342    ///               v          v          v
343    /// Output: [Plaintext][Plaintext][Plaintext]
344    ///        “Split stream reassembled in place”
345    /// ```
346    ///
347    /// This reassembly be accomplished with three calls to `open_within()`.
348    ///
349    /// Prefer [`RandomizedNonceKey::open_in_place`].
350    // # FIPS
351    // Use this method with one of the following algorithms:
352    // * `AES_128_GCM`
353    // * `AES_256_GCM`
354    //
355    /// # Errors
356    /// `error::Unspecified` when ciphertext is invalid. In this case, `in_out` may have been
357    /// overwritten in an unspecified way.
358    #[inline]
359    #[allow(clippy::needless_pass_by_value)]
360    pub fn open_within<'in_out, A>(
361        &mut self,
362        aad: Aad<A>,
363        in_out: &'in_out mut [u8],
364        ciphertext_and_tag: RangeFrom<usize>,
365    ) -> Result<&'in_out mut [u8], Unspecified>
366    where
367        A: AsRef<[u8]>,
368    {
369        self.key.open_within(
370            self.nonce_sequence.advance()?,
371            aad.as_ref(),
372            in_out,
373            ciphertext_and_tag,
374        )
375    }
376
377    /// Returns a `OpeningKeyPreparedNonce` containing the next computed `Nonce` consumed from `NonceSequence`.
378    ///
379    /// The encapsulated Nonce will be used **if and only if** either
380    /// [OpeningKeyPreparedNonce::open_in_place] or [OpeningKeyPreparedNonce::open_within]
381    /// are invoked. Dropping `OpeningKeyPreparedNonce` without invoking either method results in the nonce remaining
382    /// consumed and unused within the associated `NonceSequence`. Subsequent calls to [OpeningKey] methods will
383    /// always use a proceeding nonce from the `NonceSequence` regardless of whether
384    /// a `OpeningKeyPreparedNonce` is consumed or not.
385    ///
386    /// # Errors
387    /// `Unspecified` if there is a failure computing the nonce for the next operation, i.e. `NonceSequence` exhausted.
388    pub fn prepare_nonce(&mut self) -> Result<OpeningKeyPreparedNonce<'_, N>, Unspecified> {
389        OpeningKeyPreparedNonce::new(self)
390    }
391}
392
393/// An AEAD key for encrypting and signing ("sealing"), bound to a nonce
394/// sequence.
395///
396/// Intentionally not `Clone` or `Copy` since cloning would allow duplication
397/// of the nonce sequence.
398///
399/// Prefer [`RandomizedNonceKey`] for sealing operations.
400pub struct SealingKey<N: NonceSequence> {
401    key: UnboundKey,
402    nonce_sequence: N,
403}
404
405impl<N: NonceSequence> BoundKey<N> for SealingKey<N> {
406    fn new(key: UnboundKey, nonce_sequence: N) -> Self {
407        Self {
408            key,
409            nonce_sequence,
410        }
411    }
412
413    #[inline]
414    fn algorithm(&self) -> &'static Algorithm {
415        self.key.algorithm()
416    }
417}
418
419impl<N: NonceSequence> Debug for SealingKey<N> {
420    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
421        f.debug_struct("SealingKey")
422            .field("algorithm", &self.algorithm())
423            .finish()
424    }
425}
426
427impl<N: NonceSequence> SealingKey<N> {
428    /// Deprecated. Renamed to `seal_in_place_append_tag`.
429    ///
430    /// Prefer [`RandomizedNonceKey::seal_in_place_append_tag`].
431    // # FIPS
432    // This method must not be used.
433    //
434    /// # Errors
435    /// See `seal_in_place_append_tag`
436    #[deprecated(note = "Renamed to `seal_in_place_append_tag`.")]
437    #[inline]
438    pub fn seal_in_place<A, InOut>(
439        &mut self,
440        aad: Aad<A>,
441        in_out: &mut InOut,
442    ) -> Result<(), Unspecified>
443    where
444        A: AsRef<[u8]>,
445        InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
446    {
447        self.seal_in_place_append_tag(aad, in_out)
448    }
449
450    /// Encrypts and signs (“seals”) data in place, appending the tag to the
451    /// resulting ciphertext.
452    ///
453    /// `key.seal_in_place_append_tag(aad, in_out)` is equivalent to:
454    ///
455    /// ```skip
456    /// key.seal_in_place_separate_tag(aad, in_out.as_mut())
457    ///     .map(|tag| in_out.extend(tag.as_ref()))
458    /// ```
459    ///
460    /// Prefer [`RandomizedNonceKey::seal_in_place_append_tag`].
461    // # FIPS
462    // This method must not be used.
463    //
464    /// # Errors
465    /// `error::Unspecified` when `nonce_sequence` cannot be advanced.
466    #[inline]
467    #[allow(clippy::needless_pass_by_value)]
468    pub fn seal_in_place_append_tag<A, InOut>(
469        &mut self,
470        aad: Aad<A>,
471        in_out: &mut InOut,
472    ) -> Result<(), Unspecified>
473    where
474        A: AsRef<[u8]>,
475        InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
476    {
477        self.key
478            .seal_in_place_append_tag(Some(self.nonce_sequence.advance()?), aad.as_ref(), in_out)
479            .map(|_| ())
480    }
481
482    /// Encrypts and signs (“seals”) data in place.
483    ///
484    /// `aad` is the additional authenticated data (AAD), if any. This is
485    /// authenticated but not encrypted. The type `A` could be a byte slice
486    /// `&[u8]`, a byte array `[u8; N]` for some constant `N`, `Vec<u8>`, etc.
487    /// If there is no AAD then use `Aad::empty()`.
488    ///
489    /// The plaintext is given as the input value of `in_out`. `seal_in_place()`
490    /// will overwrite the plaintext with the ciphertext and return the tag.
491    /// For most protocols, the caller must append the tag to the ciphertext.
492    /// The tag will be `self.algorithm.tag_len()` bytes long.
493    ///
494    /// Prefer [`RandomizedNonceKey::seal_in_place_separate_tag`].
495    // # FIPS
496    // This method must not be used.
497    //
498    /// # Errors
499    /// `error::Unspecified` when `nonce_sequence` cannot be advanced.
500    #[inline]
501    #[allow(clippy::needless_pass_by_value)]
502    pub fn seal_in_place_separate_tag<A>(
503        &mut self,
504        aad: Aad<A>,
505        in_out: &mut [u8],
506    ) -> Result<Tag, Unspecified>
507    where
508        A: AsRef<[u8]>,
509    {
510        self.key
511            .seal_in_place_separate_tag(Some(self.nonce_sequence.advance()?), aad.as_ref(), in_out)
512            .map(|(_, tag)| tag)
513    }
514
515    /// Returns a `SealingKeyPreparedNonce` containing the next computed `Nonce` consumed from `NonceSequence`.
516    ///
517    /// The encapsulated Nonce will be used **if and only if** either
518    /// [SealingKeyPreparedNonce::seal_in_place_append_tag] or [SealingKeyPreparedNonce::seal_in_place_separate_tag]
519    /// are invoked. Dropping `SealingKeyPreparedNonce` without invoking either method results in the nonce remaining
520    /// consumed and unused within the associated `NonceSequence`. Subsequent calls to [SealingKey] methods will
521    /// always use a proceeding nonce from the `NonceSequence` regardless of whether
522    /// a `SealingKeyPreparedNonce` is consumed or not.
523    ///
524    /// # Errors
525    /// `Unspecified` if there is a failure computing the nonce for the next operation, i.e. `NonceSequence` exhausted.
526    pub fn prepare_nonce(&mut self) -> Result<SealingKeyPreparedNonce<'_, N>, Unspecified> {
527        SealingKeyPreparedNonce::new(self)
528    }
529}
530
531macro_rules! nonce_seq_key_op_mut {
532    ($name:ident) => {
533        paste! {
534        /// A key operation with a precomputed nonce from a key's associated `NonceSequence`.
535        pub struct [<$name PreparedNonce>]<'a, N: NonceSequence> {
536            key: &'a mut $name<N>,
537            nonce: Nonce,
538        }
539
540        impl<'a, N: NonceSequence> [<$name PreparedNonce>]<'a, N> {
541            fn new(key: &'a mut $name<N>) -> Result<Self, Unspecified> {
542                let nonce = key.nonce_sequence.advance()?;
543                Ok(Self {
544                    key,
545                    nonce,
546                })
547            }
548        }
549
550        impl<N: NonceSequence> [<$name PreparedNonce>]<'_, N> {
551            /// Returns the prepared Nonce that is used for key methods invoked on [Self].
552            #[must_use]
553            pub fn nonce(&self) -> &Nonce {
554                &self.nonce
555            }
556        }
557
558        impl<N: NonceSequence> Debug for [<$name PreparedNonce>]<'_, N> {
559            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
560                f.debug_struct(stringify!([<$name PreparedNonce>])).finish_non_exhaustive()
561            }
562        }
563        }
564    };
565}
566
567nonce_seq_key_op_mut!(OpeningKey);
568nonce_seq_key_op_mut!(SealingKey);
569
570impl<N: NonceSequence> OpeningKeyPreparedNonce<'_, N> {
571    /// Authenticates and decrypts (“opens”) data in place.
572    ///
573    /// See [OpeningKey::open_in_place] for additional API information.
574    ///
575    /// # Errors
576    /// `error::Unspecified` when ciphertext is invalid. In this case, `in_out` may have been
577    /// overwritten in an unspecified way.
578    #[inline]
579    #[allow(clippy::needless_pass_by_value)]
580    pub fn open_in_place<A>(self, aad: Aad<A>, in_out: &mut [u8]) -> Result<&mut [u8], Unspecified>
581    where
582        A: AsRef<[u8]>,
583    {
584        self.open_within(aad, in_out, 0..)
585    }
586
587    /// Authenticates and decrypts (“opens”) data in place, with a shift.
588    ///
589    /// See [OpeningKey::open_within] for additional API information.
590    ///
591    /// # Errors
592    /// `error::Unspecified` when ciphertext is invalid. In this case, `in_out` may have been
593    /// overwritten in an unspecified way.
594    #[inline]
595    #[allow(clippy::needless_pass_by_value)]
596    pub fn open_within<A>(
597        self,
598        aad: Aad<A>,
599        in_out: &mut [u8],
600        ciphertext_and_tag: RangeFrom<usize>,
601    ) -> Result<&mut [u8], Unspecified>
602    where
603        A: AsRef<[u8]>,
604    {
605        self.key
606            .key
607            .open_within(self.nonce, aad.as_ref(), in_out, ciphertext_and_tag)
608    }
609}
610
611impl<N: NonceSequence> SealingKeyPreparedNonce<'_, N> {
612    /// Encrypts and signs (“seals”) data in place, appending the tag to the
613    /// resulting ciphertext.
614    ///
615    /// See [SealingKey::seal_in_place_append_tag] for additional API information.
616    ///
617    /// # Errors
618    /// `error::Unspecified` when `nonce_sequence` cannot be advanced.
619    #[inline]
620    #[allow(clippy::needless_pass_by_value)]
621    pub fn seal_in_place_append_tag<A, InOut>(
622        self,
623        aad: Aad<A>,
624        in_out: &mut InOut,
625    ) -> Result<(), Unspecified>
626    where
627        A: AsRef<[u8]>,
628        InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
629    {
630        self.key
631            .key
632            .seal_in_place_append_tag(Some(self.nonce), aad.as_ref(), in_out)
633            .map(|_| ())
634    }
635
636    /// Encrypts and signs (“seals”) data in place.
637    ///
638    /// See [`SealingKey::seal_in_place_separate_tag`] for additional API information.
639    ///
640    /// # Errors
641    /// `error::Unspecified` when `nonce_sequence` cannot be advanced.
642    #[inline]
643    #[allow(clippy::needless_pass_by_value)]
644    pub fn seal_in_place_separate_tag<A>(
645        self,
646        aad: Aad<A>,
647        in_out: &mut [u8],
648    ) -> Result<Tag, Unspecified>
649    where
650        A: AsRef<[u8]>,
651    {
652        self.key
653            .key
654            .seal_in_place_separate_tag(Some(self.nonce), aad.as_ref(), in_out)
655            .map(|(_, tag)| tag)
656    }
657}
658
659/// The additionally authenticated data (AAD) for an opening or sealing
660/// operation. This data is authenticated but is **not** encrypted.
661///
662/// The type `A` could be a byte slice `&[u8]`, a byte array `[u8; N]`
663/// for some constant `N`, `Vec<u8>`, etc.
664pub struct Aad<A: AsRef<[u8]>>(A);
665
666impl<A: AsRef<[u8]>> Aad<A> {
667    /// Construct the `Aad` from the given bytes.
668    #[inline]
669    pub fn from(aad: A) -> Self {
670        Aad(aad)
671    }
672}
673
674impl<A> AsRef<[u8]> for Aad<A>
675where
676    A: AsRef<[u8]>,
677{
678    fn as_ref(&self) -> &[u8] {
679        self.0.as_ref()
680    }
681}
682
683impl Aad<[u8; 0]> {
684    /// Construct an empty `Aad`.
685    #[must_use]
686    pub fn empty() -> Self {
687        Self::from([])
688    }
689}
690
691impl hkdf::KeyType for &'static Algorithm {
692    #[inline]
693    fn len(&self) -> usize {
694        self.key_len()
695    }
696}
697
698/// Immutable keys for use in situations where `OpeningKey`/`SealingKey` and
699/// `NonceSequence` cannot reasonably be used.
700///
701/// Prefer [`RandomizedNonceKey`] when practical.
702// # FIPS
703// The following conditions must be met:
704// * `UnboundKey`'s algorithm is one of:
705//   * `AES_128_GCM`
706//   * `AES_256_GCM`
707// * Use `open_in_place` or `open_within` only.
708pub struct LessSafeKey {
709    key: UnboundKey,
710}
711
712impl LessSafeKey {
713    /// Constructs a `LessSafeKey` from an `UnboundKey`.
714    #[must_use]
715    pub fn new(key: UnboundKey) -> Self {
716        Self { key }
717    }
718
719    /// Like [`OpeningKey::open_in_place()`], except it accepts an arbitrary nonce.
720    ///
721    /// `nonce` must be unique for every use of the key to open data.
722    ///
723    /// Prefer [`RandomizedNonceKey::open_in_place`].
724    // # FIPS
725    // Use this method with one of the following algorithms:
726    // * `AES_128_GCM`
727    // * `AES_256_GCM`
728    //
729    /// # Errors
730    /// `error::Unspecified` when ciphertext is invalid.
731    #[inline]
732    pub fn open_in_place<'in_out, A>(
733        &self,
734        nonce: Nonce,
735        aad: Aad<A>,
736        in_out: &'in_out mut [u8],
737    ) -> Result<&'in_out mut [u8], Unspecified>
738    where
739        A: AsRef<[u8]>,
740    {
741        self.open_within(nonce, aad, in_out, 0..)
742    }
743
744    /// Like [`OpeningKey::open_within()`], except it accepts an arbitrary nonce.
745    ///
746    /// `nonce` must be unique for every use of the key to open data.
747    ///
748    /// Prefer [`RandomizedNonceKey::open_in_place`].
749    // # FIPS
750    // Use this method with one of the following algorithms:
751    // * `AES_128_GCM`
752    // * `AES_256_GCM`
753    //
754    /// # Errors
755    /// `error::Unspecified` when ciphertext is invalid.
756    #[inline]
757    #[allow(clippy::needless_pass_by_value)]
758    pub fn open_within<'in_out, A>(
759        &self,
760        nonce: Nonce,
761        aad: Aad<A>,
762        in_out: &'in_out mut [u8],
763        ciphertext_and_tag: RangeFrom<usize>,
764    ) -> Result<&'in_out mut [u8], Unspecified>
765    where
766        A: AsRef<[u8]>,
767    {
768        self.key
769            .open_within(nonce, aad.as_ref(), in_out, ciphertext_and_tag)
770    }
771
772    /// Authenticates and decrypts (“opens”) data into another provided slice.
773    ///
774    /// `aad` is the additional authenticated data (AAD), if any.
775    ///
776    /// On input, `in_ciphertext` must be the ciphertext. The tag must be provided in
777    /// `in_tag`.
778    ///
779    /// The `out_plaintext` length must match the provided `in_ciphertext`.
780    ///
781    /// # Errors
782    /// `error::Unspecified` when ciphertext is invalid. In this case, `out_plaintext` may
783    /// have been overwritten in an unspecified way.
784    #[inline]
785    #[allow(clippy::needless_pass_by_value)]
786    pub fn open_separate_gather<A>(
787        &self,
788        nonce: Nonce,
789        aad: Aad<A>,
790        in_ciphertext: &[u8],
791        in_tag: &[u8],
792        out_plaintext: &mut [u8],
793    ) -> Result<(), Unspecified>
794    where
795        A: AsRef<[u8]>,
796    {
797        self.key
798            .open_separate_gather(&nonce, aad.as_ref(), in_ciphertext, in_tag, out_plaintext)
799    }
800
801    /// Deprecated. Renamed to `seal_in_place_append_tag()`.
802    ///
803    /// Prefer [`RandomizedNonceKey::seal_in_place_append_tag`].
804    // # FIPS
805    // This method must not be used.
806    //
807    #[deprecated(note = "Renamed to `seal_in_place_append_tag`.")]
808    #[inline]
809    #[allow(clippy::missing_errors_doc)]
810    pub fn seal_in_place<A, InOut>(
811        &self,
812        nonce: Nonce,
813        aad: Aad<A>,
814        in_out: &mut InOut,
815    ) -> Result<(), Unspecified>
816    where
817        A: AsRef<[u8]>,
818        InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
819    {
820        self.seal_in_place_append_tag(nonce, aad, in_out)
821    }
822
823    /// Like [`SealingKey::seal_in_place_append_tag()`], except it accepts an
824    /// arbitrary nonce.
825    ///
826    /// `nonce` must be unique for every use of the key to seal data.
827    ///
828    /// Prefer [`RandomizedNonceKey::seal_in_place_append_tag`].
829    // # FIPS
830    // This method must not be used.
831    //
832    /// # Errors
833    /// `error::Unspecified` if encryption operation fails.
834    #[inline]
835    #[allow(clippy::needless_pass_by_value)]
836    pub fn seal_in_place_append_tag<A, InOut>(
837        &self,
838        nonce: Nonce,
839        aad: Aad<A>,
840        in_out: &mut InOut,
841    ) -> Result<(), Unspecified>
842    where
843        A: AsRef<[u8]>,
844        InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
845    {
846        self.key
847            .seal_in_place_append_tag(Some(nonce), aad.as_ref(), in_out)
848            .map(|_| ())
849    }
850
851    /// Like `SealingKey::seal_in_place_separate_tag()`, except it accepts an
852    /// arbitrary nonce.
853    ///
854    /// `nonce` must be unique for every use of the key to seal data.
855    ///
856    /// Prefer [`RandomizedNonceKey::seal_in_place_separate_tag`].
857    // # FIPS
858    // This method must not be used.
859    //
860    /// # Errors
861    /// `error::Unspecified` if encryption operation fails.
862    #[inline]
863    #[allow(clippy::needless_pass_by_value)]
864    pub fn seal_in_place_separate_tag<A>(
865        &self,
866        nonce: Nonce,
867        aad: Aad<A>,
868        in_out: &mut [u8],
869    ) -> Result<Tag, Unspecified>
870    where
871        A: AsRef<[u8]>,
872    {
873        self.key
874            .seal_in_place_separate_tag(Some(nonce), aad.as_ref(), in_out)
875            .map(|(_, tag)| tag)
876    }
877
878    /// Encrypts and signs (“seals”) data in place with extra plaintext.
879    ///
880    /// `aad` is the additional authenticated data (AAD), if any. This is
881    /// authenticated but not encrypted. The type `A` could be a byte slice
882    /// `&[u8]`, a byte array `[u8; N]` for some constant `N`, `Vec<u8>`, etc.
883    /// If there is no AAD then use `Aad::empty()`.
884    ///
885    /// The plaintext is given as the input value of `in_out` and `extra_in`. `seal_in_place()`
886    /// will overwrite the plaintext contained in `in_out` with the ciphertext. The `extra_in` will
887    /// be encrypted into the `extra_out_and_tag`, along with the tag.
888    /// The `extra_out_and_tag` length must be equal to the `extra_len` and `self.algorithm.tag_len()`.
889    ///
890    /// `nonce` must be unique for every use of the key to seal data.
891    // # FIPS
892    // This method must not be used.
893    //
894    /// # Errors
895    /// `error::Unspecified` if encryption operation fails.
896    #[inline]
897    #[allow(clippy::needless_pass_by_value)]
898    pub fn seal_in_place_scatter<A>(
899        &self,
900        nonce: Nonce,
901        aad: Aad<A>,
902        in_out: &mut [u8],
903        extra_in: &[u8],
904        extra_out_and_tag: &mut [u8],
905    ) -> Result<(), Unspecified>
906    where
907        A: AsRef<[u8]>,
908    {
909        self.key.seal_in_place_separate_scatter(
910            nonce,
911            aad.as_ref(),
912            in_out,
913            extra_in,
914            extra_out_and_tag,
915        )
916    }
917
918    /// The key's AEAD algorithm.
919    #[inline]
920    #[must_use]
921    pub fn algorithm(&self) -> &'static Algorithm {
922        self.key.algorithm()
923    }
924}
925
926impl Debug for LessSafeKey {
927    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
928        f.debug_struct("LessSafeKey")
929            .field("algorithm", self.algorithm())
930            .finish()
931    }
932}
933
934/// An AEAD Algorithm.
935pub struct Algorithm {
936    init: fn(key: &[u8], tag_len: usize) -> Result<AeadCtx, Unspecified>,
937    key_len: usize,
938    id: AlgorithmID,
939
940    // /// Use `max_input_len!()` to initialize this.
941    // TODO: Make this `usize`.
942    max_input_len: u64,
943}
944
945impl Algorithm {
946    /// The length of the key.
947    #[inline]
948    #[must_use]
949    pub fn key_len(&self) -> usize {
950        self.key_len
951    }
952
953    /// The length of a tag.
954    ///
955    /// See also `MAX_TAG_LEN`.
956    #[inline]
957    #[must_use]
958    pub fn tag_len(&self) -> usize {
959        TAG_LEN
960    }
961
962    /// The length of the nonces.
963    #[inline]
964    #[must_use]
965    pub fn nonce_len(&self) -> usize {
966        NONCE_LEN
967    }
968}
969
970derive_debug_via_id!(Algorithm);
971
972#[derive(Debug, Eq, PartialEq, Copy, Clone)]
973#[allow(non_camel_case_types)]
974enum AlgorithmID {
975    AES_128_GCM,
976    AES_192_GCM,
977    AES_256_GCM,
978    AES_128_GCM_SIV,
979    AES_256_GCM_SIV,
980    CHACHA20_POLY1305,
981}
982
983impl PartialEq for Algorithm {
984    #[inline]
985    fn eq(&self, other: &Self) -> bool {
986        self.id == other.id
987    }
988}
989
990impl Eq for Algorithm {}
991
992/// An authentication tag.
993#[must_use]
994#[repr(C)]
995pub struct Tag([u8; MAX_TAG_LEN], usize);
996
997impl AsRef<[u8]> for Tag {
998    fn as_ref(&self) -> &[u8] {
999        self.0[..self.1].as_ref()
1000    }
1001}
1002
1003impl core::fmt::Debug for Tag {
1004    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> core::fmt::Result {
1005        f.debug_tuple("Tag").finish()
1006    }
1007}
1008
1009const MAX_KEY_LEN: usize = 32;
1010
1011// All the AEADs we support use 128-bit tags.
1012const TAG_LEN: usize = 16;
1013
1014/// The maximum length of a tag for the algorithms in this module.
1015pub const MAX_TAG_LEN: usize = TAG_LEN;
1016
1017#[cfg(test)]
1018mod tests {
1019    use nonce_sequence::Counter32Builder;
1020
1021    use super::*;
1022    use crate::iv::FixedLength;
1023    use crate::test::from_hex;
1024
1025    #[cfg(feature = "fips")]
1026    mod fips;
1027
1028    #[test]
1029    fn test_aes_128() {
1030        let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap();
1031        let og_nonce = from_hex("5bf11a0951f0bfc7ea5c9e58").unwrap();
1032        let plaintext = from_hex("00112233445566778899aabbccddeeff").unwrap();
1033        let unbound_key = UnboundKey::new(&AES_128_GCM, &key).unwrap();
1034        assert_eq!(&AES_128_GCM, unbound_key.algorithm());
1035
1036        assert_eq!(16, AES_128_GCM.tag_len());
1037        assert_eq!(12, AES_128_GCM.nonce_len());
1038
1039        let less_safe_key = LessSafeKey::new(unbound_key);
1040
1041        let nonce: [u8; NONCE_LEN] = og_nonce.as_slice().try_into().unwrap();
1042        let mut in_out = Vec::from(plaintext.as_slice());
1043
1044        #[allow(deprecated)]
1045        less_safe_key
1046            // Test coverage for `seal_in_place`, which calls `seal_in_place_append_tag`.
1047            .seal_in_place(Nonce(FixedLength::from(nonce)), Aad::empty(), &mut in_out)
1048            .unwrap();
1049
1050        let mut in_out_clone = in_out.clone();
1051        let nonce: [u8; NONCE_LEN] = og_nonce.as_slice().try_into().unwrap();
1052        assert!(less_safe_key
1053            .open_in_place(
1054                Nonce(FixedLength::from(nonce)),
1055                Aad::from("test"),
1056                &mut in_out_clone
1057            )
1058            .is_err());
1059
1060        let mut in_out_clone = in_out.clone();
1061        let mut nonce: [u8; NONCE_LEN] = og_nonce.as_slice().try_into().unwrap();
1062        nonce[0] = 0;
1063        assert!(less_safe_key
1064            .open_in_place(
1065                Nonce(FixedLength::from(nonce)),
1066                Aad::empty(),
1067                &mut in_out_clone
1068            )
1069            .is_err());
1070
1071        let nonce: [u8; NONCE_LEN] = og_nonce.as_slice().try_into().unwrap();
1072        less_safe_key
1073            .open_in_place(Nonce(FixedLength::from(nonce)), Aad::empty(), &mut in_out)
1074            .unwrap();
1075
1076        assert_eq!(plaintext, in_out[..plaintext.len()]);
1077    }
1078
1079    #[test]
1080    fn debug_prepared_nonce() {
1081        let mut sk = SealingKey::new(
1082            UnboundKey::new(&AES_128_GCM, &[0u8; 16]).unwrap(),
1083            Counter32Builder::new().build(),
1084        );
1085        let mut ok = OpeningKey::new(
1086            UnboundKey::new(&AES_128_GCM, &[0u8; 16]).unwrap(),
1087            Counter32Builder::new().build(),
1088        );
1089        let so = sk.prepare_nonce().unwrap();
1090        let oo = ok.prepare_nonce().unwrap();
1091        assert_eq!("SealingKeyPreparedNonce { .. }", format!("{so:?}"));
1092        assert_eq!("OpeningKeyPreparedNonce { .. }", format!("{oo:?}"));
1093    }
1094
1095    #[test]
1096    fn debug_tag() {
1097        let tag = Tag([0u8; MAX_TAG_LEN], MAX_TAG_LEN);
1098        assert_eq!("Tag", format!("{tag:?}"));
1099    }
1100}