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}