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().as_slice());
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().as_slice());
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_in_place()`], except the authentication tag is
740 /// passed separately.
741 ///
742 /// `in_out` contains the ciphertext on input and is overwritten with the
743 /// plaintext on success. `tag` is the authentication tag, e.g. as produced
744 /// by [`Self::seal_in_place_separate_tag()`].
745 ///
746 /// `nonce` must be unique for every use of the key to open data.
747 // # FIPS
748 // This method must not be used.
749 //
750 /// # Errors
751 /// `error::Unspecified` when ciphertext is invalid. In this case, `in_out` may
752 /// have been overwritten in an unspecified way.
753 #[inline]
754 #[allow(clippy::needless_pass_by_value)]
755 pub fn open_in_place_separate_tag<'in_out, A>(
756 &self,
757 nonce: Nonce,
758 aad: Aad<A>,
759 tag: &[u8],
760 in_out: &'in_out mut [u8],
761 ) -> Result<&'in_out mut [u8], Unspecified>
762 where
763 A: AsRef<[u8]>,
764 {
765 self.key
766 .open_in_place_separate_tag(&nonce, aad.as_ref(), tag, in_out)?;
767 Ok(in_out)
768 }
769
770 /// Like [`OpeningKey::open_within()`], except it accepts an arbitrary nonce.
771 ///
772 /// `nonce` must be unique for every use of the key to open data.
773 ///
774 /// Prefer [`RandomizedNonceKey::open_in_place`].
775 // # FIPS
776 // Use this method with one of the following algorithms:
777 // * `AES_128_GCM`
778 // * `AES_256_GCM`
779 //
780 /// # Errors
781 /// `error::Unspecified` when ciphertext is invalid.
782 #[inline]
783 #[allow(clippy::needless_pass_by_value)]
784 pub fn open_within<'in_out, A>(
785 &self,
786 nonce: Nonce,
787 aad: Aad<A>,
788 in_out: &'in_out mut [u8],
789 ciphertext_and_tag: RangeFrom<usize>,
790 ) -> Result<&'in_out mut [u8], Unspecified>
791 where
792 A: AsRef<[u8]>,
793 {
794 self.key
795 .open_within(nonce, aad.as_ref(), in_out, ciphertext_and_tag)
796 }
797
798 /// Authenticates and decrypts ("opens") data into another provided slice.
799 ///
800 /// `aad` is the additional authenticated data (AAD), if any.
801 ///
802 /// On input, `in_ciphertext` must be the ciphertext. The tag must be provided in
803 /// `in_tag`.
804 ///
805 /// The `out_plaintext` length must match the provided `in_ciphertext`.
806 ///
807 /// # Errors
808 /// `error::Unspecified` when ciphertext is invalid. In this case, `out_plaintext` may
809 /// have been overwritten in an unspecified way.
810 #[inline]
811 #[allow(clippy::needless_pass_by_value)]
812 pub fn open_separate_gather<A>(
813 &self,
814 nonce: Nonce,
815 aad: Aad<A>,
816 in_ciphertext: &[u8],
817 in_tag: &[u8],
818 out_plaintext: &mut [u8],
819 ) -> Result<(), Unspecified>
820 where
821 A: AsRef<[u8]>,
822 {
823 self.key
824 .open_separate_gather(&nonce, aad.as_ref(), in_ciphertext, in_tag, out_plaintext)
825 }
826
827 /// Deprecated. Renamed to `seal_in_place_append_tag()`.
828 ///
829 /// Prefer [`RandomizedNonceKey::seal_in_place_append_tag`].
830 // # FIPS
831 // This method must not be used.
832 //
833 #[deprecated(note = "Renamed to `seal_in_place_append_tag`.")]
834 #[inline]
835 #[allow(clippy::missing_errors_doc)]
836 pub fn seal_in_place<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.seal_in_place_append_tag(nonce, aad, in_out)
847 }
848
849 /// Like [`SealingKey::seal_in_place_append_tag()`], except it accepts an
850 /// arbitrary nonce.
851 ///
852 /// `nonce` must be unique for every use of the key to seal data.
853 ///
854 /// Prefer [`RandomizedNonceKey::seal_in_place_append_tag`].
855 // # FIPS
856 // This method must not be used.
857 //
858 /// # Errors
859 /// `error::Unspecified` if encryption operation fails.
860 #[inline]
861 #[allow(clippy::needless_pass_by_value)]
862 pub fn seal_in_place_append_tag<A, InOut>(
863 &self,
864 nonce: Nonce,
865 aad: Aad<A>,
866 in_out: &mut InOut,
867 ) -> Result<(), Unspecified>
868 where
869 A: AsRef<[u8]>,
870 InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
871 {
872 self.key
873 .seal_in_place_append_tag(Some(nonce), aad.as_ref(), in_out)
874 .map(|_| ())
875 }
876
877 /// Like `SealingKey::seal_in_place_separate_tag()`, except it accepts an
878 /// arbitrary nonce.
879 ///
880 /// `nonce` must be unique for every use of the key to seal data.
881 ///
882 /// Prefer [`RandomizedNonceKey::seal_in_place_separate_tag`].
883 // # FIPS
884 // This method must not be used.
885 //
886 /// # Errors
887 /// `error::Unspecified` if encryption operation fails.
888 #[inline]
889 #[allow(clippy::needless_pass_by_value)]
890 pub fn seal_in_place_separate_tag<A>(
891 &self,
892 nonce: Nonce,
893 aad: Aad<A>,
894 in_out: &mut [u8],
895 ) -> Result<Tag, Unspecified>
896 where
897 A: AsRef<[u8]>,
898 {
899 self.key
900 .seal_in_place_separate_tag(Some(nonce), aad.as_ref(), in_out)
901 .map(|(_, tag)| tag)
902 }
903
904 /// Encrypts and signs (“seals”) data in place with extra plaintext.
905 ///
906 /// `aad` is the additional authenticated data (AAD), if any. This is
907 /// authenticated but not encrypted. The type `A` could be a byte slice
908 /// `&[u8]`, a byte array `[u8; N]` for some constant `N`, `Vec<u8>`, etc.
909 /// If there is no AAD then use `Aad::empty()`.
910 ///
911 /// The plaintext is given as the input value of `in_out` and `extra_in`. `seal_in_place()`
912 /// will overwrite the plaintext contained in `in_out` with the ciphertext. The `extra_in` will
913 /// be encrypted into the `extra_out_and_tag`, along with the tag.
914 /// The `extra_out_and_tag` length must be equal to the `extra_len` and `self.algorithm.tag_len()`.
915 ///
916 /// `nonce` must be unique for every use of the key to seal data.
917 // # FIPS
918 // This method must not be used.
919 //
920 /// # Errors
921 /// `error::Unspecified` if encryption operation fails.
922 #[inline]
923 #[allow(clippy::needless_pass_by_value)]
924 pub fn seal_in_place_scatter<A>(
925 &self,
926 nonce: Nonce,
927 aad: Aad<A>,
928 in_out: &mut [u8],
929 extra_in: &[u8],
930 extra_out_and_tag: &mut [u8],
931 ) -> Result<(), Unspecified>
932 where
933 A: AsRef<[u8]>,
934 {
935 self.key.seal_in_place_separate_scatter(
936 nonce,
937 aad.as_ref(),
938 in_out,
939 extra_in,
940 extra_out_and_tag,
941 )
942 }
943
944 /// The key's AEAD algorithm.
945 #[inline]
946 #[must_use]
947 pub fn algorithm(&self) -> &'static Algorithm {
948 self.key.algorithm()
949 }
950}
951
952impl Debug for LessSafeKey {
953 fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
954 f.debug_struct("LessSafeKey")
955 .field("algorithm", self.algorithm())
956 .finish()
957 }
958}
959
960/// An AEAD Algorithm.
961pub struct Algorithm {
962 init: fn(key: &[u8], tag_len: usize) -> Result<AeadCtx, Unspecified>,
963 key_len: usize,
964 id: AlgorithmID,
965
966 // /// Use `max_input_len!()` to initialize this.
967 // TODO: Make this `usize`.
968 max_input_len: u64,
969}
970
971impl Algorithm {
972 /// The length of the key.
973 #[inline]
974 #[must_use]
975 pub fn key_len(&self) -> usize {
976 self.key_len
977 }
978
979 /// The length of a tag.
980 ///
981 /// See also `MAX_TAG_LEN`.
982 #[inline]
983 #[must_use]
984 pub fn tag_len(&self) -> usize {
985 TAG_LEN
986 }
987
988 /// The length of the nonces.
989 #[inline]
990 #[must_use]
991 pub fn nonce_len(&self) -> usize {
992 NONCE_LEN
993 }
994}
995
996derive_debug_via_id!(Algorithm);
997
998#[derive(Debug, Eq, PartialEq, Copy, Clone)]
999#[allow(non_camel_case_types)]
1000enum AlgorithmID {
1001 AES_128_GCM,
1002 AES_192_GCM,
1003 AES_256_GCM,
1004 AES_128_GCM_SIV,
1005 AES_256_GCM_SIV,
1006 CHACHA20_POLY1305,
1007}
1008
1009impl PartialEq for Algorithm {
1010 #[inline]
1011 fn eq(&self, other: &Self) -> bool {
1012 self.id == other.id
1013 }
1014}
1015
1016impl Eq for Algorithm {}
1017
1018/// An authentication tag.
1019#[must_use]
1020#[repr(C)]
1021pub struct Tag([u8; MAX_TAG_LEN], usize);
1022
1023impl AsRef<[u8]> for Tag {
1024 fn as_ref(&self) -> &[u8] {
1025 self.0[..self.1].as_ref()
1026 }
1027}
1028
1029impl core::fmt::Debug for Tag {
1030 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> core::fmt::Result {
1031 f.debug_tuple("Tag").finish()
1032 }
1033}
1034
1035const MAX_KEY_LEN: usize = 32;
1036
1037// All the AEADs we support use 128-bit tags.
1038const TAG_LEN: usize = 16;
1039
1040/// The maximum length of a tag for the algorithms in this module.
1041pub const MAX_TAG_LEN: usize = TAG_LEN;
1042
1043#[cfg(test)]
1044mod tests {
1045 use nonce_sequence::Counter32Builder;
1046
1047 use super::*;
1048 use crate::iv::FixedLength;
1049 use crate::test::from_hex;
1050
1051 #[cfg(feature = "fips")]
1052 mod fips;
1053
1054 #[test]
1055 fn test_aes_128() {
1056 let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap();
1057 let og_nonce = from_hex("5bf11a0951f0bfc7ea5c9e58").unwrap();
1058 let plaintext = from_hex("00112233445566778899aabbccddeeff").unwrap();
1059 let unbound_key = UnboundKey::new(&AES_128_GCM, &key).unwrap();
1060 assert_eq!(&AES_128_GCM, unbound_key.algorithm());
1061
1062 assert_eq!(16, AES_128_GCM.tag_len());
1063 assert_eq!(12, AES_128_GCM.nonce_len());
1064
1065 let less_safe_key = LessSafeKey::new(unbound_key);
1066
1067 let nonce: [u8; NONCE_LEN] = og_nonce.as_slice().try_into().unwrap();
1068 let mut in_out = Vec::from(plaintext.as_slice());
1069
1070 #[allow(deprecated)]
1071 less_safe_key
1072 // Test coverage for `seal_in_place`, which calls `seal_in_place_append_tag`.
1073 .seal_in_place(Nonce(FixedLength::from(nonce)), Aad::empty(), &mut in_out)
1074 .unwrap();
1075
1076 let mut in_out_clone = in_out.clone();
1077 let nonce: [u8; NONCE_LEN] = og_nonce.as_slice().try_into().unwrap();
1078 assert!(less_safe_key
1079 .open_in_place(
1080 Nonce(FixedLength::from(nonce)),
1081 Aad::from("test"),
1082 &mut in_out_clone
1083 )
1084 .is_err());
1085
1086 let mut in_out_clone = in_out.clone();
1087 let mut nonce: [u8; NONCE_LEN] = og_nonce.as_slice().try_into().unwrap();
1088 nonce[0] = 0;
1089 assert!(less_safe_key
1090 .open_in_place(
1091 Nonce(FixedLength::from(nonce)),
1092 Aad::empty(),
1093 &mut in_out_clone
1094 )
1095 .is_err());
1096
1097 let nonce: [u8; NONCE_LEN] = og_nonce.as_slice().try_into().unwrap();
1098 less_safe_key
1099 .open_in_place(Nonce(FixedLength::from(nonce)), Aad::empty(), &mut in_out)
1100 .unwrap();
1101
1102 assert_eq!(plaintext, in_out[..plaintext.len()]);
1103 }
1104
1105 #[test]
1106 fn debug_prepared_nonce() {
1107 let mut sk = SealingKey::new(
1108 UnboundKey::new(&AES_128_GCM, &[0u8; 16]).unwrap(),
1109 Counter32Builder::new().build(),
1110 );
1111 let mut ok = OpeningKey::new(
1112 UnboundKey::new(&AES_128_GCM, &[0u8; 16]).unwrap(),
1113 Counter32Builder::new().build(),
1114 );
1115 let so = sk.prepare_nonce().unwrap();
1116 let oo = ok.prepare_nonce().unwrap();
1117 assert_eq!("SealingKeyPreparedNonce { .. }", format!("{so:?}"));
1118 assert_eq!("OpeningKeyPreparedNonce { .. }", format!("{oo:?}"));
1119 }
1120
1121 #[test]
1122 fn debug_tag() {
1123 let tag = Tag([0u8; MAX_TAG_LEN], MAX_TAG_LEN);
1124 assert_eq!("Tag", format!("{tag:?}"));
1125 }
1126}