aws_lc_rs/
cipher.rs

1// Copyright 2018 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//! Block and Stream Ciphers for Encryption and Decryption.
7//!
8//! # 🛑 Read Before Using
9//!
10//! This module provides access to block and stream cipher algorithms.
11//! The modes provided here only provide confidentiality, but **do not**
12//! provide integrity or authentication verification of ciphertext.
13//!
14//! These algorithms are provided solely for applications requiring them
15//! in order to maintain backwards compatibility in legacy applications.
16//!
17//! If you are developing new applications requiring data encryption see
18//! the algorithms provided in [`aead`](crate::aead).
19//!
20//! # Examples
21//!
22//! ## Encryption Modes
23//!
24//! ### AES-128 CBC
25//!
26//! ```rust
27//! # use std::error::Error;
28//! #
29//! # fn main() -> Result<(), Box<dyn Error>> {
30//! use aws_lc_rs::cipher::{
31//!     PaddedBlockDecryptingKey, PaddedBlockEncryptingKey, UnboundCipherKey, AES_128,
32//! };
33//! use std::io::Read;
34//!
35//! let original_message = "This is a secret message!".as_bytes();
36//! let mut in_out_buffer = Vec::from(original_message);
37//!
38//! let key_bytes: &[u8] = &[
39//!     0xff, 0x0b, 0xe5, 0x84, 0x64, 0x0b, 0x00, 0xc8, 0x90, 0x7a, 0x4b, 0xbf, 0x82, 0x7c, 0xb6,
40//!     0xd1,
41//! ];
42//!
43//! let key = UnboundCipherKey::new(&AES_128, key_bytes)?;
44//! let mut encrypting_key = PaddedBlockEncryptingKey::cbc_pkcs7(key)?;
45//! let context = encrypting_key.encrypt(&mut in_out_buffer)?;
46//!
47//! let key = UnboundCipherKey::new(&AES_128, key_bytes)?;
48//! let mut decrypting_key = PaddedBlockDecryptingKey::cbc_pkcs7(key)?;
49//! let plaintext = decrypting_key.decrypt(&mut in_out_buffer, context)?;
50//! assert_eq!(original_message, plaintext);
51//! #
52//! #
53//! # Ok(())
54//! # }
55//! ```
56//!
57//! ### AES-128 CTR
58//!
59//! ```rust
60//! # use std::error::Error;
61//! #
62//! # fn main() -> Result<(), Box<dyn Error>> {
63//! use aws_lc_rs::cipher::{DecryptingKey, EncryptingKey, UnboundCipherKey, AES_128};
64//!
65//! let original_message = "This is a secret message!".as_bytes();
66//! let mut in_out_buffer = Vec::from(original_message);
67//!
68//! let key_bytes: &[u8] = &[
69//!     0xff, 0x0b, 0xe5, 0x84, 0x64, 0x0b, 0x00, 0xc8, 0x90, 0x7a, 0x4b, 0xbf, 0x82, 0x7c, 0xb6,
70//!     0xd1,
71//! ];
72//!
73//! let key = UnboundCipherKey::new(&AES_128, key_bytes)?;
74//! let mut encrypting_key = EncryptingKey::ctr(key)?;
75//! let context = encrypting_key.encrypt(&mut in_out_buffer)?;
76//!
77//! let key = UnboundCipherKey::new(&AES_128, key_bytes)?;
78//! let mut decrypting_key = DecryptingKey::ctr(key)?;
79//! let plaintext = decrypting_key.decrypt(&mut in_out_buffer, context)?;
80//! assert_eq!(original_message, plaintext);
81//! #
82//! # Ok(())
83//! # }
84//! ```
85//!
86//! ### AES-128 CBC Streaming Cipher
87//!
88//! ```rust
89//! # use std::error::Error;
90//! #
91//! # fn main() -> Result<(), Box<dyn Error>> {
92//! use aws_lc_rs::cipher::{
93//!     StreamingDecryptingKey, StreamingEncryptingKey, UnboundCipherKey, AES_128,
94//! };
95//! let original_message = "This is a secret message!".as_bytes();
96//! let key_bytes: &[u8] = &[
97//!     0xff, 0x0b, 0xe5, 0x84, 0x64, 0x0b, 0x00, 0xc8, 0x90, 0x7a, 0x4b, 0xbf, 0x82, 0x7c,
98//!     0xb6, 0xd1,
99//! ];
100//! // Prepare ciphertext buffer
101//! let mut ciphertext_buffer = vec![0u8; original_message.len() + AES_128.block_len()];
102//! let ciphertext_slice = ciphertext_buffer.as_mut_slice();
103//!
104//! // Create StreamingEncryptingKey
105//! let key = UnboundCipherKey::new(&AES_128, key_bytes).unwrap();
106//! let mut encrypting_key = StreamingEncryptingKey::cbc_pkcs7(key).unwrap();
107//!
108//! // Encrypt
109//! let mut first_update = encrypting_key
110//!                            .update(original_message, ciphertext_slice)
111//!                            .unwrap();
112//! let first_update_len = first_update.written().len();
113//! let (context, final_update) = encrypting_key.finish(first_update.remainder_mut()).unwrap();
114//! let ciphertext_len = first_update_len + final_update.written().len();
115//! let ciphertext = &ciphertext_slice[0..ciphertext_len];
116//!
117//! // Prepare plaintext buffer
118//! let mut plaintext_buffer = vec![0u8; ciphertext_len + AES_128.block_len()];
119//! let plaintext_slice = plaintext_buffer.as_mut_slice();
120//!
121//! // Create StreamingDecryptingKey
122//! let key = UnboundCipherKey::new(&AES_128, key_bytes).unwrap();
123//! let mut decrypting_key = StreamingDecryptingKey::cbc_pkcs7(key, context).unwrap();
124//!
125//! // Decrypt
126//! let mut first_update = decrypting_key.update(ciphertext, plaintext_slice).unwrap();
127//! let first_update_len = first_update.written().len();
128//! let final_update = decrypting_key.finish(first_update.remainder_mut()).unwrap();
129//! let plaintext_len = first_update_len + final_update.written().len();
130//! let plaintext = &plaintext_slice[0..plaintext_len];
131//!
132//! assert_eq!(original_message, plaintext);
133//! #
134//! # Ok(())
135//! # }
136//! ```
137//!
138//! ### AES-128 CFB 128-bit mode
139//!
140//! ```rust
141//! # use std::error::Error;
142//! #
143//! # fn main() -> Result<(), Box<dyn Error>> {
144//! use aws_lc_rs::cipher::{DecryptingKey, EncryptingKey, UnboundCipherKey, AES_128};
145//!
146//! let original_message = "This is a secret message!".as_bytes();
147//! let mut in_out_buffer = Vec::from(original_message);
148//!
149//! let key_bytes: &[u8] = &[
150//!     0xff, 0x0b, 0xe5, 0x84, 0x64, 0x0b, 0x00, 0xc8, 0x90, 0x7a, 0x4b, 0xbf, 0x82, 0x7c, 0xb6,
151//!     0xd1,
152//! ];
153//!
154//! let key = UnboundCipherKey::new(&AES_128, key_bytes)?;
155//! let mut encrypting_key = EncryptingKey::cfb128(key)?;
156//! let context = encrypting_key.encrypt(&mut in_out_buffer)?;
157//!
158//! let key = UnboundCipherKey::new(&AES_128, key_bytes)?;
159//! let mut decrypting_key = DecryptingKey::cfb128(key)?;
160//! let plaintext = decrypting_key.decrypt(&mut in_out_buffer, context)?;
161//! assert_eq!(original_message, plaintext);
162//! #
163//! # Ok(())
164//! # }
165//! ```
166//!
167//! ## Constructing a `DecryptionContext` for decryption.
168//!
169//! ```rust
170//! # use std::error::Error;
171//! # fn main() -> Result<(), Box<dyn Error>> {
172//! use aws_lc_rs::cipher::{DecryptingKey, DecryptionContext, UnboundCipherKey, AES_128};
173//! use aws_lc_rs::iv::{FixedLength, IV_LEN_128_BIT};
174//!
175//! let context = DecryptionContext::Iv128(FixedLength::<IV_LEN_128_BIT>::from(&[
176//!     0x8d, 0xdb, 0x7d, 0xf1, 0x56, 0xf5, 0x1c, 0xde, 0x63, 0xe3, 0x4a, 0x34, 0xb0, 0xdf, 0x28,
177//!     0xf0,
178//! ]));
179//!
180//! let ciphertext: &[u8] = &[
181//!     0x79, 0x8c, 0x04, 0x58, 0xcf, 0x98, 0xb1, 0xe9, 0x97, 0x6b, 0xa1, 0xce,
182//! ];
183//!
184//! let mut in_out_buffer = Vec::from(ciphertext);
185//!
186//! let key = UnboundCipherKey::new(
187//!     &AES_128,
188//!     &[
189//!         0x5b, 0xfc, 0xe7, 0x5e, 0x57, 0xc5, 0x4d, 0xda, 0x2d, 0xd4, 0x7e, 0x07, 0x0a, 0xef,
190//!         0x43, 0x29,
191//!     ],
192//! )?;
193//! let mut decrypting_key = DecryptingKey::ctr(key)?;
194//! let plaintext = decrypting_key.decrypt(&mut in_out_buffer, context)?;
195//! assert_eq!("Hello World!".as_bytes(), plaintext);
196//!
197//! # Ok(())
198//! # }
199//! ```
200//!
201//! ## Getting an immutable reference to the IV slice.
202//!
203//! `TryFrom<&DecryptionContext>` is implemented for `&[u8]` allowing immutable references
204//! to IV bytes returned from cipher encryption operations. Note this is implemented as a `TryFrom` as it
205//! may fail for future enum variants that aren't representable as a single slice.
206//!
207//! ```rust
208//! # use std::error::Error;
209//! # fn main() -> Result<(), Box<dyn Error>> {
210//! # use aws_lc_rs::cipher::DecryptionContext;
211//! # use aws_lc_rs::iv::FixedLength;
212//! # let x: DecryptionContext = DecryptionContext::Iv128(FixedLength::from([0u8; 16]));
213//! // x is type `DecryptionContext`
214//! let iv: &[u8] = (&x).try_into()?;
215//! # Ok(())
216//! # }
217//! ```
218
219#![allow(clippy::module_name_repetitions)]
220
221pub(crate) mod aes;
222pub(crate) mod block;
223pub(crate) mod chacha;
224pub(crate) mod key;
225mod padded;
226mod streaming;
227
228pub use padded::{PaddedBlockDecryptingKey, PaddedBlockEncryptingKey};
229pub use streaming::{BufferUpdate, StreamingDecryptingKey, StreamingEncryptingKey};
230
231use crate::aws_lc::{
232    EVP_aes_128_cbc, EVP_aes_128_cfb128, EVP_aes_128_ctr, EVP_aes_128_ecb, EVP_aes_192_cbc,
233    EVP_aes_192_cfb128, EVP_aes_192_ctr, EVP_aes_192_ecb, EVP_aes_256_cbc, EVP_aes_256_cfb128,
234    EVP_aes_256_ctr, EVP_aes_256_ecb, EVP_CIPHER,
235};
236use crate::buffer::Buffer;
237use crate::error::Unspecified;
238use crate::hkdf;
239use crate::hkdf::KeyType;
240use crate::iv::{FixedLength, IV_LEN_128_BIT};
241use crate::ptr::ConstPointer;
242use core::fmt::Debug;
243use key::SymmetricCipherKey;
244
245/// The number of bytes in an AES 128-bit key
246pub use crate::cipher::aes::AES_128_KEY_LEN;
247
248/// The number of bytes in an AES 192-bit key
249pub use crate::cipher::aes::AES_192_KEY_LEN;
250
251/// The number of bytes in an AES 256-bit key
252pub use crate::cipher::aes::AES_256_KEY_LEN;
253
254const MAX_CIPHER_KEY_LEN: usize = AES_256_KEY_LEN;
255
256/// The number of bytes for an AES-CBC initialization vector (IV)
257pub use crate::cipher::aes::AES_CBC_IV_LEN;
258
259/// The number of bytes for an AES-CTR initialization vector (IV)
260pub use crate::cipher::aes::AES_CTR_IV_LEN;
261
262/// The number of bytes for an AES-CFB initialization vector (IV)
263pub use crate::cipher::aes::AES_CFB_IV_LEN;
264
265use crate::cipher::aes::AES_BLOCK_LEN;
266
267const MAX_CIPHER_BLOCK_LEN: usize = AES_BLOCK_LEN;
268
269/// The cipher operating mode.
270#[non_exhaustive]
271#[derive(Debug, PartialEq, Eq, Clone, Copy)]
272pub enum OperatingMode {
273    /// Cipher block chaining (CBC) mode.
274    CBC,
275
276    /// Counter (CTR) mode.
277    CTR,
278
279    /// CFB 128-bit mode.
280    CFB128,
281
282    /// Electronic Code Book (ECB) mode.
283    ECB,
284}
285
286impl OperatingMode {
287    fn evp_cipher(&self, algorithm: &Algorithm) -> ConstPointer<'_, EVP_CIPHER> {
288        unsafe {
289            ConstPointer::new_static(match (self, algorithm.id) {
290                (OperatingMode::CBC, AlgorithmId::Aes128) => EVP_aes_128_cbc(),
291                (OperatingMode::CTR, AlgorithmId::Aes128) => EVP_aes_128_ctr(),
292                (OperatingMode::CFB128, AlgorithmId::Aes128) => EVP_aes_128_cfb128(),
293                (OperatingMode::ECB, AlgorithmId::Aes128) => EVP_aes_128_ecb(),
294                (OperatingMode::CBC, AlgorithmId::Aes192) => EVP_aes_192_cbc(),
295                (OperatingMode::CTR, AlgorithmId::Aes192) => EVP_aes_192_ctr(),
296                (OperatingMode::CFB128, AlgorithmId::Aes192) => EVP_aes_192_cfb128(),
297                (OperatingMode::ECB, AlgorithmId::Aes192) => EVP_aes_192_ecb(),
298                (OperatingMode::CBC, AlgorithmId::Aes256) => EVP_aes_256_cbc(),
299                (OperatingMode::CTR, AlgorithmId::Aes256) => EVP_aes_256_ctr(),
300                (OperatingMode::CFB128, AlgorithmId::Aes256) => EVP_aes_256_cfb128(),
301                (OperatingMode::ECB, AlgorithmId::Aes256) => EVP_aes_256_ecb(),
302            })
303            .unwrap()
304        }
305    }
306}
307
308macro_rules! define_cipher_context {
309    ($name:ident, $other:ident) => {
310        /// The contextual data used to encrypt or decrypt data.
311        #[non_exhaustive]
312        pub enum $name {
313            /// A 128-bit Initialization Vector.
314            Iv128(FixedLength<IV_LEN_128_BIT>),
315
316            /// No Cipher Context
317            None,
318        }
319
320        impl<'a> TryFrom<&'a $name> for &'a [u8] {
321            type Error = Unspecified;
322
323            fn try_from(value: &'a $name) -> Result<Self, Unspecified> {
324                match value {
325                    $name::Iv128(iv) => Ok(iv.as_ref()),
326                    _ => Err(Unspecified),
327                }
328            }
329        }
330
331        impl Debug for $name {
332            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
333                match self {
334                    Self::Iv128(_) => write!(f, "Iv128"),
335                    Self::None => write!(f, "None"),
336                }
337            }
338        }
339
340        impl From<$other> for $name {
341            fn from(value: $other) -> Self {
342                match value {
343                    $other::Iv128(iv) => $name::Iv128(iv),
344                    $other::None => $name::None,
345                }
346            }
347        }
348    };
349}
350
351define_cipher_context!(EncryptionContext, DecryptionContext);
352define_cipher_context!(DecryptionContext, EncryptionContext);
353
354#[non_exhaustive]
355#[derive(Debug, PartialEq, Eq, Clone, Copy)]
356/// Cipher algorithm identifier.
357pub enum AlgorithmId {
358    /// AES 128-bit
359    Aes128,
360
361    /// AES 256-bit
362    Aes256,
363
364    /// AES 192-bit
365    Aes192,
366}
367
368/// A cipher algorithm.
369#[derive(Debug, PartialEq, Eq)]
370pub struct Algorithm {
371    id: AlgorithmId,
372    key_len: usize,
373    block_len: usize,
374}
375
376/// AES 128-bit cipher
377pub static AES_128: Algorithm = Algorithm {
378    id: AlgorithmId::Aes128,
379    key_len: AES_128_KEY_LEN,
380    block_len: AES_BLOCK_LEN,
381};
382
383/// AES 192-bit cipher
384pub static AES_192: Algorithm = Algorithm {
385    id: AlgorithmId::Aes192,
386    key_len: AES_192_KEY_LEN,
387    block_len: AES_BLOCK_LEN,
388};
389
390/// AES 256-bit cipher
391pub static AES_256: Algorithm = Algorithm {
392    id: AlgorithmId::Aes256,
393    key_len: AES_256_KEY_LEN,
394    block_len: AES_BLOCK_LEN,
395};
396
397impl Algorithm {
398    fn id(&self) -> &AlgorithmId {
399        &self.id
400    }
401
402    /// The block length of this cipher algorithm.
403    #[must_use]
404    pub const fn block_len(&self) -> usize {
405        self.block_len
406    }
407
408    fn new_encryption_context(
409        &self,
410        mode: OperatingMode,
411    ) -> Result<EncryptionContext, Unspecified> {
412        match self.id {
413            // TODO: Hopefully support CFB1, and CFB8
414            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => match mode {
415                OperatingMode::CBC | OperatingMode::CTR | OperatingMode::CFB128 => {
416                    Ok(EncryptionContext::Iv128(FixedLength::new()?))
417                }
418                OperatingMode::ECB => Ok(EncryptionContext::None),
419            },
420        }
421    }
422
423    fn is_valid_encryption_context(&self, mode: OperatingMode, input: &EncryptionContext) -> bool {
424        match self.id {
425            // TODO: Hopefully support CFB1, and CFB8
426            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => match mode {
427                OperatingMode::CBC | OperatingMode::CTR | OperatingMode::CFB128 => {
428                    matches!(input, EncryptionContext::Iv128(_))
429                }
430                OperatingMode::ECB => {
431                    matches!(input, EncryptionContext::None)
432                }
433            },
434        }
435    }
436
437    fn is_valid_decryption_context(&self, mode: OperatingMode, input: &DecryptionContext) -> bool {
438        // TODO: Hopefully support CFB1, and CFB8
439        match self.id {
440            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => match mode {
441                OperatingMode::CBC | OperatingMode::CTR | OperatingMode::CFB128 => {
442                    matches!(input, DecryptionContext::Iv128(_))
443                }
444                OperatingMode::ECB => {
445                    matches!(input, DecryptionContext::None)
446                }
447            },
448        }
449    }
450}
451
452#[allow(clippy::missing_fields_in_debug)]
453impl Debug for UnboundCipherKey {
454    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
455        f.debug_struct("UnboundCipherKey")
456            .field("algorithm", &self.algorithm)
457            .finish()
458    }
459}
460
461impl From<hkdf::Okm<'_, &'static Algorithm>> for UnboundCipherKey {
462    fn from(okm: hkdf::Okm<&'static Algorithm>) -> Self {
463        let mut key_bytes = [0; MAX_CIPHER_KEY_LEN];
464        let key_bytes = &mut key_bytes[..okm.len().key_len];
465        let algorithm = *okm.len();
466        okm.fill(key_bytes).unwrap();
467        Self::new(algorithm, key_bytes).unwrap()
468    }
469}
470
471impl KeyType for &'static Algorithm {
472    fn len(&self) -> usize {
473        self.key_len
474    }
475}
476
477/// A key bound to a particular cipher algorithm.
478pub struct UnboundCipherKey {
479    algorithm: &'static Algorithm,
480    key_bytes: Buffer<'static, &'static [u8]>,
481}
482
483impl UnboundCipherKey {
484    /// Constructs an [`UnboundCipherKey`].
485    ///
486    /// # Errors
487    ///
488    /// * [`Unspecified`] if `key_bytes.len()` does not match the length required by `algorithm`.
489    pub fn new(algorithm: &'static Algorithm, key_bytes: &[u8]) -> Result<Self, Unspecified> {
490        let key_bytes = Buffer::new(key_bytes.to_vec());
491        Ok(UnboundCipherKey {
492            algorithm,
493            key_bytes,
494        })
495    }
496
497    #[inline]
498    #[must_use]
499    /// Returns the algorithm associated with this key.
500    pub fn algorithm(&self) -> &'static Algorithm {
501        self.algorithm
502    }
503}
504
505impl TryInto<SymmetricCipherKey> for UnboundCipherKey {
506    type Error = Unspecified;
507
508    fn try_into(self) -> Result<SymmetricCipherKey, Self::Error> {
509        match self.algorithm.id() {
510            AlgorithmId::Aes128 => SymmetricCipherKey::aes128(self.key_bytes.as_ref()),
511            AlgorithmId::Aes192 => SymmetricCipherKey::aes192(self.key_bytes.as_ref()),
512            AlgorithmId::Aes256 => SymmetricCipherKey::aes256(self.key_bytes.as_ref()),
513        }
514    }
515}
516
517/// A cipher encryption key that does not perform block padding.
518pub struct EncryptingKey {
519    algorithm: &'static Algorithm,
520    key: SymmetricCipherKey,
521    mode: OperatingMode,
522}
523
524impl EncryptingKey {
525    /// Constructs an `EncryptingKey` operating in counter (CTR) mode using the provided key.
526    ///
527    // # FIPS
528    // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms:
529    // * `AES_128`
530    // * `AES_256`
531    //
532    /// # Errors
533    /// * [`Unspecified`]: Returned if there is an error constructing the `EncryptingKey`.
534    pub fn ctr(key: UnboundCipherKey) -> Result<Self, Unspecified> {
535        Self::new(key, OperatingMode::CTR)
536    }
537
538    /// Constructs an `EncryptingKey` operating in cipher feedback 128-bit mode (CFB128) using the provided key.
539    ///
540    // # FIPS
541    // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms:
542    // * `AES_128`
543    // * `AES_256`
544    //
545    /// # Errors
546    /// * [`Unspecified`]: Returned if there is an error constructing the `EncryptingKey`.
547    pub fn cfb128(key: UnboundCipherKey) -> Result<Self, Unspecified> {
548        Self::new(key, OperatingMode::CFB128)
549    }
550
551    /// Constructs an `EncryptingKey` operating in electronic code book mode (ECB) using the provided key.
552    ///
553    /// # ☠️ ️️️DANGER ☠️
554    /// Offered for computability purposes only. This is an extremely dangerous mode, and
555    /// very likely not what you want to use.
556    ///
557    // # FIPS
558    // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms:
559    // * `AES_128`
560    // * `AES_256`
561    //
562    /// # Errors
563    /// * [`Unspecified`]: Returned if there is an error constructing the `EncryptingKey`.
564    pub fn ecb(key: UnboundCipherKey) -> Result<Self, Unspecified> {
565        Self::new(key, OperatingMode::ECB)
566    }
567
568    #[allow(clippy::unnecessary_wraps)]
569    fn new(key: UnboundCipherKey, mode: OperatingMode) -> Result<Self, Unspecified> {
570        let algorithm = key.algorithm();
571        let key = key.try_into()?;
572        Ok(Self {
573            algorithm,
574            key,
575            mode,
576        })
577    }
578
579    /// Returns the cipher algorithm.
580    #[must_use]
581    pub fn algorithm(&self) -> &Algorithm {
582        self.algorithm
583    }
584
585    /// Returns the cipher operating mode.
586    #[must_use]
587    pub fn mode(&self) -> OperatingMode {
588        self.mode
589    }
590
591    /// Encrypts the data provided in `in_out` in-place.
592    /// Returns a [`DecryptionContext`] with the randomly generated IV that was used to encrypt
593    /// the data provided.
594    ///
595    /// If `EncryptingKey` is operating in `OperatingMode::ECB`, then `in_out.len()` must be a multiple
596    /// of the block length.
597    ///
598    /// # Errors
599    /// * [`Unspecified`]: Returned if cipher mode requires input to be a multiple of the block length,
600    ///   and `in_out.len()` is not. Otherwise, returned if encryption fails.
601    pub fn encrypt(&self, in_out: &mut [u8]) -> Result<DecryptionContext, Unspecified> {
602        let context = self.algorithm.new_encryption_context(self.mode)?;
603        self.less_safe_encrypt(in_out, context)
604    }
605
606    /// Encrypts the data provided in `in_out` in-place using the provided `EncryptionContext`.
607    /// This is considered "less safe" because the caller could potentially construct
608    /// a `EncryptionContext` from a previously used IV (initialization vector).
609    /// Returns a [`DecryptionContext`] produced from the provided `EncryptionContext`.
610    ///
611    /// If `EncryptingKey` is operating in `OperatingMode::ECB`, then `in_out.len()` must be a multiple
612    /// of the block length.
613    ///
614    /// # Errors
615    /// * [`Unspecified`]: Returned if cipher mode requires input to be a multiple of the block length,
616    ///   and `in_out.len()` is not. Otherwise returned if encryption fails.
617    pub fn less_safe_encrypt(
618        &self,
619        in_out: &mut [u8],
620        context: EncryptionContext,
621    ) -> Result<DecryptionContext, Unspecified> {
622        if !self
623            .algorithm()
624            .is_valid_encryption_context(self.mode, &context)
625        {
626            return Err(Unspecified);
627        }
628        encrypt(self.algorithm(), &self.key, self.mode, in_out, context)
629    }
630}
631
632impl Debug for EncryptingKey {
633    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
634        f.debug_struct("EncryptingKey")
635            .field("algorithm", self.algorithm)
636            .field("mode", &self.mode)
637            .finish_non_exhaustive()
638    }
639}
640
641/// A cipher decryption key that does not perform block padding.
642pub struct DecryptingKey {
643    algorithm: &'static Algorithm,
644    key: SymmetricCipherKey,
645    mode: OperatingMode,
646}
647
648impl DecryptingKey {
649    /// Constructs a cipher decrypting key operating in counter (CTR) mode using the provided key and context.
650    ///
651    // # FIPS
652    // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms:
653    // * `AES_128`
654    // * `AES_256`
655    //
656    /// # Errors
657    /// * [`Unspecified`]: Returned if there is an error during decryption.
658    pub fn ctr(key: UnboundCipherKey) -> Result<DecryptingKey, Unspecified> {
659        Self::new(key, OperatingMode::CTR)
660    }
661
662    /// Constructs a cipher decrypting key operating in cipher feedback 128-bit mode (CFB128) using the provided key and context.
663    ///
664    // # FIPS
665    // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms:
666    // * `AES_128`
667    // * `AES_256`
668    //
669    /// # Errors
670    /// * [`Unspecified`]: Returned if there is an error during decryption.
671    pub fn cfb128(key: UnboundCipherKey) -> Result<Self, Unspecified> {
672        Self::new(key, OperatingMode::CFB128)
673    }
674
675    /// Constructs an `DecryptingKey` operating in electronic code book (ECB) mode using the provided key.
676    ///
677    /// # ☠️ ️️️DANGER ☠️
678    /// Offered for computability purposes only. This is an extremely dangerous mode, and
679    /// very likely not what you want to use.
680    ///
681    // # FIPS
682    // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms:
683    // * `AES_128`
684    // * `AES_256`
685    //
686    /// # Errors
687    /// * [`Unspecified`]: Returned if there is an error constructing the `DecryptingKey`.
688    pub fn ecb(key: UnboundCipherKey) -> Result<Self, Unspecified> {
689        Self::new(key, OperatingMode::ECB)
690    }
691
692    #[allow(clippy::unnecessary_wraps)]
693    fn new(key: UnboundCipherKey, mode: OperatingMode) -> Result<Self, Unspecified> {
694        let algorithm = key.algorithm();
695        let key = key.try_into()?;
696        Ok(Self {
697            algorithm,
698            key,
699            mode,
700        })
701    }
702
703    /// Returns the cipher algorithm.
704    #[must_use]
705    pub fn algorithm(&self) -> &Algorithm {
706        self.algorithm
707    }
708
709    /// Returns the cipher operating mode.
710    #[must_use]
711    pub fn mode(&self) -> OperatingMode {
712        self.mode
713    }
714
715    /// Decrypts the data provided in `in_out` in-place.
716    /// Returns a references to the decrypted data.
717    ///
718    /// If `DecryptingKey` is operating in `OperatingMode::ECB`, then `in_out.len()` must be a multiple
719    /// of the block length.
720    ///
721    /// # Errors
722    /// * [`Unspecified`]: Returned if cipher mode requires input to be a multiple of the block length,
723    ///   and `in_out.len()` is not. Also returned if decryption fails.
724    pub fn decrypt<'in_out>(
725        &self,
726        in_out: &'in_out mut [u8],
727        context: DecryptionContext,
728    ) -> Result<&'in_out mut [u8], Unspecified> {
729        decrypt(self.algorithm, &self.key, self.mode, in_out, context)
730    }
731}
732
733impl Debug for DecryptingKey {
734    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
735        f.debug_struct("DecryptingKey")
736            .field("algorithm", &self.algorithm)
737            .field("mode", &self.mode)
738            .finish_non_exhaustive()
739    }
740}
741
742fn encrypt(
743    algorithm: &Algorithm,
744    key: &SymmetricCipherKey,
745    mode: OperatingMode,
746    in_out: &mut [u8],
747    context: EncryptionContext,
748) -> Result<DecryptionContext, Unspecified> {
749    let block_len = algorithm.block_len();
750
751    match mode {
752        OperatingMode::CBC | OperatingMode::ECB => {
753            if in_out.len() % block_len != 0 {
754                return Err(Unspecified);
755            }
756        }
757        _ => {}
758    }
759
760    match mode {
761        OperatingMode::CBC => match algorithm.id() {
762            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
763                aes::encrypt_cbc_mode(key, context, in_out)
764            }
765        },
766        OperatingMode::CTR => match algorithm.id() {
767            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
768                aes::encrypt_ctr_mode(key, context, in_out)
769            }
770        },
771        // TODO: Hopefully support CFB1, and CFB8
772        OperatingMode::CFB128 => match algorithm.id() {
773            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
774                aes::encrypt_cfb_mode(key, mode, context, in_out)
775            }
776        },
777        OperatingMode::ECB => match algorithm.id() {
778            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
779                aes::encrypt_ecb_mode(key, context, in_out)
780            }
781        },
782    }
783}
784
785fn decrypt<'in_out>(
786    algorithm: &'static Algorithm,
787    key: &SymmetricCipherKey,
788    mode: OperatingMode,
789    in_out: &'in_out mut [u8],
790    context: DecryptionContext,
791) -> Result<&'in_out mut [u8], Unspecified> {
792    let block_len = algorithm.block_len();
793
794    match mode {
795        OperatingMode::CBC | OperatingMode::ECB => {
796            if in_out.len() % block_len != 0 {
797                return Err(Unspecified);
798            }
799        }
800        _ => {}
801    }
802
803    match mode {
804        OperatingMode::CBC => match algorithm.id() {
805            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
806                aes::decrypt_cbc_mode(key, context, in_out)
807            }
808        },
809        OperatingMode::CTR => match algorithm.id() {
810            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
811                aes::decrypt_ctr_mode(key, context, in_out)
812            }
813        },
814        // TODO: Hopefully support CFB1, and CFB8
815        OperatingMode::CFB128 => match algorithm.id() {
816            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
817                aes::decrypt_cfb_mode(key, mode, context, in_out)
818            }
819        },
820        OperatingMode::ECB => match algorithm.id() {
821            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
822                aes::decrypt_ecb_mode(key, context, in_out)
823            }
824        },
825    }
826}
827
828#[cfg(test)]
829mod tests {
830    use super::*;
831    use crate::test::from_hex;
832
833    #[cfg(feature = "fips")]
834    mod fips;
835
836    #[test]
837    fn test_debug() {
838        {
839            let aes_128_key_bytes = from_hex("000102030405060708090a0b0c0d0e0f").unwrap();
840            let cipher_key = UnboundCipherKey::new(&AES_128, aes_128_key_bytes.as_slice()).unwrap();
841            assert_eq!("UnboundCipherKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 } }", format!("{cipher_key:?}"));
842        }
843
844        {
845            let aes_256_key_bytes =
846                from_hex("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f")
847                    .unwrap();
848            let cipher_key = UnboundCipherKey::new(&AES_256, aes_256_key_bytes.as_slice()).unwrap();
849            assert_eq!("UnboundCipherKey { algorithm: Algorithm { id: Aes256, key_len: 32, block_len: 16 } }", format!("{cipher_key:?}"));
850        }
851
852        {
853            let key_bytes = &[0u8; 16];
854            let key = PaddedBlockEncryptingKey::cbc_pkcs7(
855                UnboundCipherKey::new(&AES_128, key_bytes).unwrap(),
856            )
857            .unwrap();
858            assert_eq!("PaddedBlockEncryptingKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 }, mode: CBC, padding: PKCS7, .. }", format!("{key:?}"));
859            let mut data = vec![0u8; 16];
860            let context = key.encrypt(&mut data).unwrap();
861            assert_eq!("Iv128", format!("{context:?}"));
862            let key = PaddedBlockDecryptingKey::cbc_pkcs7(
863                UnboundCipherKey::new(&AES_128, key_bytes).unwrap(),
864            )
865            .unwrap();
866            assert_eq!("PaddedBlockDecryptingKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 }, mode: CBC, padding: PKCS7, .. }", format!("{key:?}"));
867        }
868
869        {
870            let key_bytes = &[0u8; 16];
871            let key =
872                EncryptingKey::ctr(UnboundCipherKey::new(&AES_128, key_bytes).unwrap()).unwrap();
873            assert_eq!("EncryptingKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 }, mode: CTR, .. }", format!("{key:?}"));
874            let mut data = vec![0u8; 16];
875            let context = key.encrypt(&mut data).unwrap();
876            assert_eq!("Iv128", format!("{context:?}"));
877            let key =
878                DecryptingKey::ctr(UnboundCipherKey::new(&AES_128, key_bytes).unwrap()).unwrap();
879            assert_eq!("DecryptingKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 }, mode: CTR, .. }", format!("{key:?}"));
880        }
881    }
882
883    fn helper_test_cipher_n_bytes(
884        key: &[u8],
885        alg: &'static Algorithm,
886        mode: OperatingMode,
887        n: usize,
888    ) {
889        let mut input: Vec<u8> = Vec::with_capacity(n);
890        for i in 0..n {
891            let byte: u8 = i.try_into().unwrap();
892            input.push(byte);
893        }
894
895        let cipher_key = UnboundCipherKey::new(alg, key).unwrap();
896        let encrypting_key = EncryptingKey::new(cipher_key, mode).unwrap();
897
898        let mut in_out = input.clone();
899        let decrypt_iv = encrypting_key.encrypt(&mut in_out).unwrap();
900
901        if n > 5 {
902            // There's no more than a 1 in 2^48 chance that this will fail randomly
903            assert_ne!(input.as_slice(), in_out);
904        }
905
906        let cipher_key2 = UnboundCipherKey::new(alg, key).unwrap();
907        let decrypting_key = DecryptingKey::new(cipher_key2, mode).unwrap();
908
909        let plaintext = decrypting_key.decrypt(&mut in_out, decrypt_iv).unwrap();
910        assert_eq!(input.as_slice(), plaintext);
911    }
912
913    #[test]
914    fn test_aes_128_ctr() {
915        let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap();
916        for i in 0..=50 {
917            helper_test_cipher_n_bytes(key.as_slice(), &AES_128, OperatingMode::CTR, i);
918        }
919    }
920
921    #[test]
922    fn test_aes_128_cfb128() {
923        let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap();
924        for i in 0..=50 {
925            helper_test_cipher_n_bytes(key.as_slice(), &AES_128, OperatingMode::CFB128, i);
926        }
927    }
928
929    #[test]
930    fn test_aes_256_cfb128() {
931        let key =
932            from_hex("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f").unwrap();
933        for i in 0..=50 {
934            helper_test_cipher_n_bytes(key.as_slice(), &AES_256, OperatingMode::CFB128, i);
935        }
936    }
937
938    #[test]
939    fn test_aes_256_ctr() {
940        let key =
941            from_hex("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f").unwrap();
942        for i in 0..=50 {
943            helper_test_cipher_n_bytes(key.as_slice(), &AES_256, OperatingMode::CTR, i);
944        }
945    }
946
947    #[test]
948    fn test_aes_128_ecb() {
949        let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap();
950        _ = key;
951    }
952
953    macro_rules! cipher_kat {
954        ($name:ident, $alg:expr, $mode:expr, $key:literal, $iv: literal, $plaintext:literal, $ciphertext:literal) => {
955            #[test]
956            fn $name() {
957                let key = from_hex($key).unwrap();
958                let input = from_hex($plaintext).unwrap();
959                let expected_ciphertext = from_hex($ciphertext).unwrap();
960                let mut iv = from_hex($iv).unwrap();
961                let iv = {
962                    let slice = iv.as_mut_slice();
963                    let mut iv = [0u8; $iv.len() / 2];
964                    {
965                        let x = iv.as_mut_slice();
966                        x.copy_from_slice(slice);
967                    }
968                    iv
969                };
970
971                let ec = EncryptionContext::Iv128(FixedLength::from(iv));
972
973                let alg = $alg;
974
975                let unbound_key = UnboundCipherKey::new(alg, &key).unwrap();
976
977                let encrypting_key = EncryptingKey::new(unbound_key, $mode).unwrap();
978
979                let mut in_out = input.clone();
980
981                let context = encrypting_key.less_safe_encrypt(&mut in_out, ec).unwrap();
982
983                assert_eq!(expected_ciphertext, in_out);
984
985                let unbound_key2 = UnboundCipherKey::new(alg, &key).unwrap();
986                let decrypting_key = DecryptingKey::new(unbound_key2, $mode).unwrap();
987
988                let plaintext = decrypting_key.decrypt(&mut in_out, context).unwrap();
989                assert_eq!(input.as_slice(), plaintext);
990            }
991        };
992        ($name:ident, $alg:expr, $mode:expr, $key:literal, $plaintext:literal, $ciphertext:literal) => {
993            #[test]
994            fn $name() {
995                let key = from_hex($key).unwrap();
996                let input = from_hex($plaintext).unwrap();
997                let expected_ciphertext = from_hex($ciphertext).unwrap();
998
999                let alg = $alg;
1000
1001                let unbound_key = UnboundCipherKey::new(alg, &key).unwrap();
1002
1003                let encrypting_key = EncryptingKey::new(unbound_key, $mode).unwrap();
1004
1005                let mut in_out = input.clone();
1006
1007                let context = encrypting_key
1008                    .less_safe_encrypt(&mut in_out, EncryptionContext::None)
1009                    .unwrap();
1010
1011                assert_eq!(expected_ciphertext, in_out);
1012
1013                let unbound_key2 = UnboundCipherKey::new(alg, &key).unwrap();
1014                let decrypting_key = DecryptingKey::new(unbound_key2, $mode).unwrap();
1015
1016                let plaintext = decrypting_key.decrypt(&mut in_out, context).unwrap();
1017                assert_eq!(input.as_slice(), plaintext);
1018            }
1019        };
1020    }
1021
1022    cipher_kat!(
1023        test_iv_aes_128_ctr_16_bytes,
1024        &AES_128,
1025        OperatingMode::CTR,
1026        "000102030405060708090a0b0c0d0e0f",
1027        "00000000000000000000000000000000",
1028        "00112233445566778899aabbccddeeff",
1029        "c6b01904c3da3df5e7d62bd96d153686"
1030    );
1031
1032    cipher_kat!(
1033        test_iv_aes_256_ctr_15_bytes,
1034        &AES_256,
1035        OperatingMode::CTR,
1036        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
1037        "00000000000000000000000000000000",
1038        "00112233445566778899aabbccddee",
1039        "f28122856e1cf9a7216a30d111f399"
1040    );
1041
1042    cipher_kat!(
1043        test_openssl_aes_128_ctr_15_bytes,
1044        &AES_128,
1045        OperatingMode::CTR,
1046        "244828580821c1652582c76e34d299f5",
1047        "093145d5af233f46072a5eb5adc11aa1",
1048        "3ee38cec171e6cf466bf0df98aa0e1",
1049        "bd7d928f60e3422d96b3f8cd614eb2"
1050    );
1051
1052    cipher_kat!(
1053        test_openssl_aes_256_ctr_15_bytes,
1054        &AES_256,
1055        OperatingMode::CTR,
1056        "0857db8240ea459bdf660b4cced66d1f2d3734ff2de7b81e92740e65e7cc6a1d",
1057        "f028ecb053f801102d11fccc9d303a27",
1058        "eca7285d19f3c20e295378460e8729",
1059        "b5098e5e788de6ac2f2098eb2fc6f8"
1060    );
1061
1062    cipher_kat!(
1063        test_sp800_38a_cfb128_aes128,
1064        &AES_128,
1065        OperatingMode::CFB128,
1066        "2b7e151628aed2a6abf7158809cf4f3c",
1067        "000102030405060708090a0b0c0d0e0f",
1068        "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
1069        "3b3fd92eb72dad20333449f8e83cfb4ac8a64537a0b3a93fcde3cdad9f1ce58b26751f67a3cbb140b1808cf187a4f4dfc04b05357c5d1c0eeac4c66f9ff7f2e6"
1070    );
1071
1072    cipher_kat!(
1073        test_sp800_38a_cfb128_aes256,
1074        &AES_256,
1075        OperatingMode::CFB128,
1076        "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
1077        "000102030405060708090a0b0c0d0e0f",
1078        "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
1079        "dc7e84bfda79164b7ecd8486985d386039ffed143b28b1c832113c6331e5407bdf10132415e54b92a13ed0a8267ae2f975a385741ab9cef82031623d55b1e471"
1080    );
1081
1082    cipher_kat!(
1083        test_sp800_38a_ecb_aes128,
1084        &AES_128,
1085        OperatingMode::ECB,
1086        "2b7e151628aed2a6abf7158809cf4f3c",
1087        "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
1088        "3ad77bb40d7a3660a89ecaf32466ef97f5d3d58503b9699de785895a96fdbaaf43b1cd7f598ece23881b00e3ed0306887b0c785e27e8ad3f8223207104725dd4"
1089    );
1090
1091    cipher_kat!(
1092        test_sp800_38a_ecb_aes256,
1093        &AES_256,
1094        OperatingMode::ECB,
1095        "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
1096        "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
1097        "f3eed1bdb5d2a03c064b5a7e3db181f8591ccb10d410ed26dc5ba74a31362870b6ed21b99ca6f4f9f153e7b1beafed1d23304b7a39f9f3ff067d8d8f9e24ecc7"
1098    );
1099}