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        ConstPointer::new(match (self, algorithm.id) {
289            (OperatingMode::CBC, AlgorithmId::Aes128) => unsafe { EVP_aes_128_cbc() },
290            (OperatingMode::CTR, AlgorithmId::Aes128) => unsafe { EVP_aes_128_ctr() },
291            (OperatingMode::CFB128, AlgorithmId::Aes128) => unsafe { EVP_aes_128_cfb128() },
292            (OperatingMode::ECB, AlgorithmId::Aes128) => unsafe { EVP_aes_128_ecb() },
293            (OperatingMode::CBC, AlgorithmId::Aes192) => unsafe { EVP_aes_192_cbc() },
294            (OperatingMode::CTR, AlgorithmId::Aes192) => unsafe { EVP_aes_192_ctr() },
295            (OperatingMode::CFB128, AlgorithmId::Aes192) => unsafe { EVP_aes_192_cfb128() },
296            (OperatingMode::ECB, AlgorithmId::Aes192) => unsafe { EVP_aes_192_ecb() },
297            (OperatingMode::CBC, AlgorithmId::Aes256) => unsafe { EVP_aes_256_cbc() },
298            (OperatingMode::CTR, AlgorithmId::Aes256) => unsafe { EVP_aes_256_ctr() },
299            (OperatingMode::CFB128, AlgorithmId::Aes256) => unsafe { EVP_aes_256_cfb128() },
300            (OperatingMode::ECB, AlgorithmId::Aes256) => unsafe { EVP_aes_256_ecb() },
301        })
302        .unwrap()
303    }
304}
305
306macro_rules! define_cipher_context {
307    ($name:ident, $other:ident) => {
308        /// The contextual data used to encrypt or decrypt data.
309        #[non_exhaustive]
310        pub enum $name {
311            /// A 128-bit Initialization Vector.
312            Iv128(FixedLength<IV_LEN_128_BIT>),
313
314            /// No Cipher Context
315            None,
316        }
317
318        impl<'a> TryFrom<&'a $name> for &'a [u8] {
319            type Error = Unspecified;
320
321            fn try_from(value: &'a $name) -> Result<Self, Unspecified> {
322                match value {
323                    $name::Iv128(iv) => Ok(iv.as_ref()),
324                    _ => Err(Unspecified),
325                }
326            }
327        }
328
329        impl Debug for $name {
330            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
331                match self {
332                    Self::Iv128(_) => write!(f, "Iv128"),
333                    Self::None => write!(f, "None"),
334                }
335            }
336        }
337
338        impl From<$other> for $name {
339            fn from(value: $other) -> Self {
340                match value {
341                    $other::Iv128(iv) => $name::Iv128(iv),
342                    $other::None => $name::None,
343                }
344            }
345        }
346    };
347}
348
349define_cipher_context!(EncryptionContext, DecryptionContext);
350define_cipher_context!(DecryptionContext, EncryptionContext);
351
352#[non_exhaustive]
353#[derive(Debug, PartialEq, Eq, Clone, Copy)]
354/// Cipher algorithm identifier.
355pub enum AlgorithmId {
356    /// AES 128-bit
357    Aes128,
358
359    /// AES 256-bit
360    Aes256,
361
362    /// AES 192-bit
363    Aes192,
364}
365
366/// A cipher algorithm.
367#[derive(Debug, PartialEq, Eq)]
368pub struct Algorithm {
369    id: AlgorithmId,
370    key_len: usize,
371    block_len: usize,
372}
373
374/// AES 128-bit cipher
375pub static AES_128: Algorithm = Algorithm {
376    id: AlgorithmId::Aes128,
377    key_len: AES_128_KEY_LEN,
378    block_len: AES_BLOCK_LEN,
379};
380
381/// AES 192-bit cipher
382pub static AES_192: Algorithm = Algorithm {
383    id: AlgorithmId::Aes192,
384    key_len: AES_192_KEY_LEN,
385    block_len: AES_BLOCK_LEN,
386};
387
388/// AES 256-bit cipher
389pub static AES_256: Algorithm = Algorithm {
390    id: AlgorithmId::Aes256,
391    key_len: AES_256_KEY_LEN,
392    block_len: AES_BLOCK_LEN,
393};
394
395impl Algorithm {
396    fn id(&self) -> &AlgorithmId {
397        &self.id
398    }
399
400    /// The block length of this cipher algorithm.
401    #[must_use]
402    pub const fn block_len(&self) -> usize {
403        self.block_len
404    }
405
406    fn new_encryption_context(
407        &self,
408        mode: OperatingMode,
409    ) -> Result<EncryptionContext, Unspecified> {
410        match self.id {
411            // TODO: Hopefully support CFB1, and CFB8
412            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => match mode {
413                OperatingMode::CBC | OperatingMode::CTR | OperatingMode::CFB128 => {
414                    Ok(EncryptionContext::Iv128(FixedLength::new()?))
415                }
416                OperatingMode::ECB => Ok(EncryptionContext::None),
417            },
418        }
419    }
420
421    fn is_valid_encryption_context(&self, mode: OperatingMode, input: &EncryptionContext) -> bool {
422        match self.id {
423            // TODO: Hopefully support CFB1, and CFB8
424            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => match mode {
425                OperatingMode::CBC | OperatingMode::CTR | OperatingMode::CFB128 => {
426                    matches!(input, EncryptionContext::Iv128(_))
427                }
428                OperatingMode::ECB => {
429                    matches!(input, EncryptionContext::None)
430                }
431            },
432        }
433    }
434
435    fn is_valid_decryption_context(&self, mode: OperatingMode, input: &DecryptionContext) -> bool {
436        // TODO: Hopefully support CFB1, and CFB8
437        match self.id {
438            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => match mode {
439                OperatingMode::CBC | OperatingMode::CTR | OperatingMode::CFB128 => {
440                    matches!(input, DecryptionContext::Iv128(_))
441                }
442                OperatingMode::ECB => {
443                    matches!(input, DecryptionContext::None)
444                }
445            },
446        }
447    }
448}
449
450#[allow(clippy::missing_fields_in_debug)]
451impl Debug for UnboundCipherKey {
452    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
453        f.debug_struct("UnboundCipherKey")
454            .field("algorithm", &self.algorithm)
455            .finish()
456    }
457}
458
459impl From<hkdf::Okm<'_, &'static Algorithm>> for UnboundCipherKey {
460    fn from(okm: hkdf::Okm<&'static Algorithm>) -> Self {
461        let mut key_bytes = [0; MAX_CIPHER_KEY_LEN];
462        let key_bytes = &mut key_bytes[..okm.len().key_len];
463        let algorithm = *okm.len();
464        okm.fill(key_bytes).unwrap();
465        Self::new(algorithm, key_bytes).unwrap()
466    }
467}
468
469impl KeyType for &'static Algorithm {
470    fn len(&self) -> usize {
471        self.key_len
472    }
473}
474
475/// A key bound to a particular cipher algorithm.
476pub struct UnboundCipherKey {
477    algorithm: &'static Algorithm,
478    key_bytes: Buffer<'static, &'static [u8]>,
479}
480
481impl UnboundCipherKey {
482    /// Constructs an [`UnboundCipherKey`].
483    ///
484    /// # Errors
485    ///
486    /// * [`Unspecified`] if `key_bytes.len()` does not match the length required by `algorithm`.
487    pub fn new(algorithm: &'static Algorithm, key_bytes: &[u8]) -> Result<Self, Unspecified> {
488        let key_bytes = Buffer::new(key_bytes.to_vec());
489        Ok(UnboundCipherKey {
490            algorithm,
491            key_bytes,
492        })
493    }
494
495    #[inline]
496    #[must_use]
497    /// Returns the algorithm associated with this key.
498    pub fn algorithm(&self) -> &'static Algorithm {
499        self.algorithm
500    }
501}
502
503impl TryInto<SymmetricCipherKey> for UnboundCipherKey {
504    type Error = Unspecified;
505
506    fn try_into(self) -> Result<SymmetricCipherKey, Self::Error> {
507        match self.algorithm.id() {
508            AlgorithmId::Aes128 => SymmetricCipherKey::aes128(self.key_bytes.as_ref()),
509            AlgorithmId::Aes192 => SymmetricCipherKey::aes192(self.key_bytes.as_ref()),
510            AlgorithmId::Aes256 => SymmetricCipherKey::aes256(self.key_bytes.as_ref()),
511        }
512    }
513}
514
515/// A cipher encryption key that does not perform block padding.
516pub struct EncryptingKey {
517    algorithm: &'static Algorithm,
518    key: SymmetricCipherKey,
519    mode: OperatingMode,
520}
521
522impl EncryptingKey {
523    /// Constructs an `EncryptingKey` operating in counter (CTR) mode using the provided key.
524    ///
525    // # FIPS
526    // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms:
527    // * `AES_128`
528    // * `AES_256`
529    //
530    /// # Errors
531    /// * [`Unspecified`]: Returned if there is an error constructing the `EncryptingKey`.
532    pub fn ctr(key: UnboundCipherKey) -> Result<Self, Unspecified> {
533        Self::new(key, OperatingMode::CTR)
534    }
535
536    /// Constructs an `EncryptingKey` operating in cipher feedback 128-bit mode (CFB128) using the provided key.
537    ///
538    // # FIPS
539    // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms:
540    // * `AES_128`
541    // * `AES_256`
542    //
543    /// # Errors
544    /// * [`Unspecified`]: Returned if there is an error constructing the `EncryptingKey`.
545    pub fn cfb128(key: UnboundCipherKey) -> Result<Self, Unspecified> {
546        Self::new(key, OperatingMode::CFB128)
547    }
548
549    /// Constructs an `EncryptingKey` operating in electronic code book mode (ECB) using the provided key.
550    ///
551    /// # ☠️ ️️️DANGER ☠️
552    /// Offered for computability purposes only. This is an extremely dangerous mode, and
553    /// very likely not what you want to use.
554    ///
555    // # FIPS
556    // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms:
557    // * `AES_128`
558    // * `AES_256`
559    //
560    /// # Errors
561    /// * [`Unspecified`]: Returned if there is an error constructing the `EncryptingKey`.
562    pub fn ecb(key: UnboundCipherKey) -> Result<Self, Unspecified> {
563        Self::new(key, OperatingMode::ECB)
564    }
565
566    #[allow(clippy::unnecessary_wraps)]
567    fn new(key: UnboundCipherKey, mode: OperatingMode) -> Result<Self, Unspecified> {
568        let algorithm = key.algorithm();
569        let key = key.try_into()?;
570        Ok(Self {
571            algorithm,
572            key,
573            mode,
574        })
575    }
576
577    /// Returns the cipher algorithm.
578    #[must_use]
579    pub fn algorithm(&self) -> &Algorithm {
580        self.algorithm
581    }
582
583    /// Returns the cipher operating mode.
584    #[must_use]
585    pub fn mode(&self) -> OperatingMode {
586        self.mode
587    }
588
589    /// Encrypts the data provided in `in_out` in-place.
590    /// Returns a [`DecryptionContext`] with the randomly generated IV that was used to encrypt
591    /// the data provided.
592    ///
593    /// If `EncryptingKey` is operating in `OperatingMode::ECB`, then `in_out.len()` must be a multiple
594    /// of the block length.
595    ///
596    /// # Errors
597    /// * [`Unspecified`]: Returned if cipher mode requires input to be a multiple of the block length,
598    ///   and `in_out.len()` is not. Otherwise, returned if encryption fails.
599    pub fn encrypt(&self, in_out: &mut [u8]) -> Result<DecryptionContext, Unspecified> {
600        let context = self.algorithm.new_encryption_context(self.mode)?;
601        self.less_safe_encrypt(in_out, context)
602    }
603
604    /// Encrypts the data provided in `in_out` in-place using the provided `EncryptionContext`.
605    /// This is considered "less safe" because the caller could potentially construct
606    /// a `EncryptionContext` from a previously used IV (initialization vector).
607    /// Returns a [`DecryptionContext`] produced from the provided `EncryptionContext`.
608    ///
609    /// If `EncryptingKey` is operating in `OperatingMode::ECB`, then `in_out.len()` must be a multiple
610    /// of the block length.
611    ///
612    /// # Errors
613    /// * [`Unspecified`]: Returned if cipher mode requires input to be a multiple of the block length,
614    ///   and `in_out.len()` is not. Otherwise returned if encryption fails.
615    pub fn less_safe_encrypt(
616        &self,
617        in_out: &mut [u8],
618        context: EncryptionContext,
619    ) -> Result<DecryptionContext, Unspecified> {
620        if !self
621            .algorithm()
622            .is_valid_encryption_context(self.mode, &context)
623        {
624            return Err(Unspecified);
625        }
626        encrypt(self.algorithm(), &self.key, self.mode, in_out, context)
627    }
628}
629
630impl Debug for EncryptingKey {
631    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
632        f.debug_struct("EncryptingKey")
633            .field("algorithm", self.algorithm)
634            .field("mode", &self.mode)
635            .finish_non_exhaustive()
636    }
637}
638
639/// A cipher decryption key that does not perform block padding.
640pub struct DecryptingKey {
641    algorithm: &'static Algorithm,
642    key: SymmetricCipherKey,
643    mode: OperatingMode,
644}
645
646impl DecryptingKey {
647    /// Constructs a cipher decrypting key operating in counter (CTR) mode using the provided key and context.
648    ///
649    // # FIPS
650    // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms:
651    // * `AES_128`
652    // * `AES_256`
653    //
654    /// # Errors
655    /// * [`Unspecified`]: Returned if there is an error during decryption.
656    pub fn ctr(key: UnboundCipherKey) -> Result<DecryptingKey, Unspecified> {
657        Self::new(key, OperatingMode::CTR)
658    }
659
660    /// Constructs a cipher decrypting key operating in cipher feedback 128-bit mode (CFB128) using the provided key and context.
661    ///
662    // # FIPS
663    // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms:
664    // * `AES_128`
665    // * `AES_256`
666    //
667    /// # Errors
668    /// * [`Unspecified`]: Returned if there is an error during decryption.
669    pub fn cfb128(key: UnboundCipherKey) -> Result<Self, Unspecified> {
670        Self::new(key, OperatingMode::CFB128)
671    }
672
673    /// Constructs an `DecryptingKey` operating in electronic code book (ECB) mode using the provided key.
674    ///
675    /// # ☠️ ️️️DANGER ☠️
676    /// Offered for computability purposes only. This is an extremely dangerous mode, and
677    /// very likely not what you want to use.
678    ///
679    // # FIPS
680    // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms:
681    // * `AES_128`
682    // * `AES_256`
683    //
684    /// # Errors
685    /// * [`Unspecified`]: Returned if there is an error constructing the `DecryptingKey`.
686    pub fn ecb(key: UnboundCipherKey) -> Result<Self, Unspecified> {
687        Self::new(key, OperatingMode::ECB)
688    }
689
690    #[allow(clippy::unnecessary_wraps)]
691    fn new(key: UnboundCipherKey, mode: OperatingMode) -> Result<Self, Unspecified> {
692        let algorithm = key.algorithm();
693        let key = key.try_into()?;
694        Ok(Self {
695            algorithm,
696            key,
697            mode,
698        })
699    }
700
701    /// Returns the cipher algorithm.
702    #[must_use]
703    pub fn algorithm(&self) -> &Algorithm {
704        self.algorithm
705    }
706
707    /// Returns the cipher operating mode.
708    #[must_use]
709    pub fn mode(&self) -> OperatingMode {
710        self.mode
711    }
712
713    /// Decrypts the data provided in `in_out` in-place.
714    /// Returns a references to the decrypted data.
715    ///
716    /// If `DecryptingKey` is operating in `OperatingMode::ECB`, then `in_out.len()` must be a multiple
717    /// of the block length.
718    ///
719    /// # Errors
720    /// * [`Unspecified`]: Returned if cipher mode requires input to be a multiple of the block length,
721    ///   and `in_out.len()` is not. Also returned if decryption fails.
722    pub fn decrypt<'in_out>(
723        &self,
724        in_out: &'in_out mut [u8],
725        context: DecryptionContext,
726    ) -> Result<&'in_out mut [u8], Unspecified> {
727        decrypt(self.algorithm, &self.key, self.mode, in_out, context)
728    }
729}
730
731impl Debug for DecryptingKey {
732    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
733        f.debug_struct("DecryptingKey")
734            .field("algorithm", &self.algorithm)
735            .field("mode", &self.mode)
736            .finish_non_exhaustive()
737    }
738}
739
740fn encrypt(
741    algorithm: &Algorithm,
742    key: &SymmetricCipherKey,
743    mode: OperatingMode,
744    in_out: &mut [u8],
745    context: EncryptionContext,
746) -> Result<DecryptionContext, Unspecified> {
747    let block_len = algorithm.block_len();
748
749    match mode {
750        OperatingMode::CBC | OperatingMode::ECB => {
751            if in_out.len() % block_len != 0 {
752                return Err(Unspecified);
753            }
754        }
755        _ => {}
756    }
757
758    match mode {
759        OperatingMode::CBC => match algorithm.id() {
760            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
761                aes::encrypt_cbc_mode(key, context, in_out)
762            }
763        },
764        OperatingMode::CTR => match algorithm.id() {
765            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
766                aes::encrypt_ctr_mode(key, context, in_out)
767            }
768        },
769        // TODO: Hopefully support CFB1, and CFB8
770        OperatingMode::CFB128 => match algorithm.id() {
771            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
772                aes::encrypt_cfb_mode(key, mode, context, in_out)
773            }
774        },
775        OperatingMode::ECB => match algorithm.id() {
776            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
777                aes::encrypt_ecb_mode(key, context, in_out)
778            }
779        },
780    }
781}
782
783fn decrypt<'in_out>(
784    algorithm: &'static Algorithm,
785    key: &SymmetricCipherKey,
786    mode: OperatingMode,
787    in_out: &'in_out mut [u8],
788    context: DecryptionContext,
789) -> Result<&'in_out mut [u8], Unspecified> {
790    let block_len = algorithm.block_len();
791
792    match mode {
793        OperatingMode::CBC | OperatingMode::ECB => {
794            if in_out.len() % block_len != 0 {
795                return Err(Unspecified);
796            }
797        }
798        _ => {}
799    }
800
801    match mode {
802        OperatingMode::CBC => match algorithm.id() {
803            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
804                aes::decrypt_cbc_mode(key, context, in_out)
805            }
806        },
807        OperatingMode::CTR => match algorithm.id() {
808            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
809                aes::decrypt_ctr_mode(key, context, in_out)
810            }
811        },
812        // TODO: Hopefully support CFB1, and CFB8
813        OperatingMode::CFB128 => match algorithm.id() {
814            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
815                aes::decrypt_cfb_mode(key, mode, context, in_out)
816            }
817        },
818        OperatingMode::ECB => match algorithm.id() {
819            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
820                aes::decrypt_ecb_mode(key, context, in_out)
821            }
822        },
823    }
824}
825
826#[cfg(test)]
827mod tests {
828    use super::*;
829    use crate::test::from_hex;
830
831    #[cfg(feature = "fips")]
832    mod fips;
833
834    #[test]
835    fn test_debug() {
836        {
837            let aes_128_key_bytes = from_hex("000102030405060708090a0b0c0d0e0f").unwrap();
838            let cipher_key = UnboundCipherKey::new(&AES_128, aes_128_key_bytes.as_slice()).unwrap();
839            assert_eq!("UnboundCipherKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 } }", format!("{cipher_key:?}"));
840        }
841
842        {
843            let aes_256_key_bytes =
844                from_hex("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f")
845                    .unwrap();
846            let cipher_key = UnboundCipherKey::new(&AES_256, aes_256_key_bytes.as_slice()).unwrap();
847            assert_eq!("UnboundCipherKey { algorithm: Algorithm { id: Aes256, key_len: 32, block_len: 16 } }", format!("{cipher_key:?}"));
848        }
849
850        {
851            let key_bytes = &[0u8; 16];
852            let key = PaddedBlockEncryptingKey::cbc_pkcs7(
853                UnboundCipherKey::new(&AES_128, key_bytes).unwrap(),
854            )
855            .unwrap();
856            assert_eq!("PaddedBlockEncryptingKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 }, mode: CBC, padding: PKCS7, .. }", format!("{key:?}"));
857            let mut data = vec![0u8; 16];
858            let context = key.encrypt(&mut data).unwrap();
859            assert_eq!("Iv128", format!("{context:?}"));
860            let key = PaddedBlockDecryptingKey::cbc_pkcs7(
861                UnboundCipherKey::new(&AES_128, key_bytes).unwrap(),
862            )
863            .unwrap();
864            assert_eq!("PaddedBlockDecryptingKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 }, mode: CBC, padding: PKCS7, .. }", format!("{key:?}"));
865        }
866
867        {
868            let key_bytes = &[0u8; 16];
869            let key =
870                EncryptingKey::ctr(UnboundCipherKey::new(&AES_128, key_bytes).unwrap()).unwrap();
871            assert_eq!("EncryptingKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 }, mode: CTR, .. }", format!("{key:?}"));
872            let mut data = vec![0u8; 16];
873            let context = key.encrypt(&mut data).unwrap();
874            assert_eq!("Iv128", format!("{context:?}"));
875            let key =
876                DecryptingKey::ctr(UnboundCipherKey::new(&AES_128, key_bytes).unwrap()).unwrap();
877            assert_eq!("DecryptingKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 }, mode: CTR, .. }", format!("{key:?}"));
878        }
879    }
880
881    fn helper_test_cipher_n_bytes(
882        key: &[u8],
883        alg: &'static Algorithm,
884        mode: OperatingMode,
885        n: usize,
886    ) {
887        let mut input: Vec<u8> = Vec::with_capacity(n);
888        for i in 0..n {
889            let byte: u8 = i.try_into().unwrap();
890            input.push(byte);
891        }
892
893        let cipher_key = UnboundCipherKey::new(alg, key).unwrap();
894        let encrypting_key = EncryptingKey::new(cipher_key, mode).unwrap();
895
896        let mut in_out = input.clone();
897        let decrypt_iv = encrypting_key.encrypt(&mut in_out).unwrap();
898
899        if n > 5 {
900            // There's no more than a 1 in 2^48 chance that this will fail randomly
901            assert_ne!(input.as_slice(), in_out);
902        }
903
904        let cipher_key2 = UnboundCipherKey::new(alg, key).unwrap();
905        let decrypting_key = DecryptingKey::new(cipher_key2, mode).unwrap();
906
907        let plaintext = decrypting_key.decrypt(&mut in_out, decrypt_iv).unwrap();
908        assert_eq!(input.as_slice(), plaintext);
909    }
910
911    #[test]
912    fn test_aes_128_ctr() {
913        let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap();
914        for i in 0..=50 {
915            helper_test_cipher_n_bytes(key.as_slice(), &AES_128, OperatingMode::CTR, i);
916        }
917    }
918
919    #[test]
920    fn test_aes_128_cfb128() {
921        let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap();
922        for i in 0..=50 {
923            helper_test_cipher_n_bytes(key.as_slice(), &AES_128, OperatingMode::CFB128, i);
924        }
925    }
926
927    #[test]
928    fn test_aes_256_cfb128() {
929        let key =
930            from_hex("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f").unwrap();
931        for i in 0..=50 {
932            helper_test_cipher_n_bytes(key.as_slice(), &AES_256, OperatingMode::CFB128, i);
933        }
934    }
935
936    #[test]
937    fn test_aes_256_ctr() {
938        let key =
939            from_hex("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f").unwrap();
940        for i in 0..=50 {
941            helper_test_cipher_n_bytes(key.as_slice(), &AES_256, OperatingMode::CTR, i);
942        }
943    }
944
945    #[test]
946    fn test_aes_128_ecb() {
947        let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap();
948        _ = key;
949    }
950
951    macro_rules! cipher_kat {
952        ($name:ident, $alg:expr, $mode:expr, $key:literal, $iv: literal, $plaintext:literal, $ciphertext:literal) => {
953            #[test]
954            fn $name() {
955                let key = from_hex($key).unwrap();
956                let input = from_hex($plaintext).unwrap();
957                let expected_ciphertext = from_hex($ciphertext).unwrap();
958                let mut iv = from_hex($iv).unwrap();
959                let iv = {
960                    let slice = iv.as_mut_slice();
961                    let mut iv = [0u8; $iv.len() / 2];
962                    {
963                        let x = iv.as_mut_slice();
964                        x.copy_from_slice(slice);
965                    }
966                    iv
967                };
968
969                let ec = EncryptionContext::Iv128(FixedLength::from(iv));
970
971                let alg = $alg;
972
973                let unbound_key = UnboundCipherKey::new(alg, &key).unwrap();
974
975                let encrypting_key = EncryptingKey::new(unbound_key, $mode).unwrap();
976
977                let mut in_out = input.clone();
978
979                let context = encrypting_key.less_safe_encrypt(&mut in_out, ec).unwrap();
980
981                assert_eq!(expected_ciphertext, in_out);
982
983                let unbound_key2 = UnboundCipherKey::new(alg, &key).unwrap();
984                let decrypting_key = DecryptingKey::new(unbound_key2, $mode).unwrap();
985
986                let plaintext = decrypting_key.decrypt(&mut in_out, context).unwrap();
987                assert_eq!(input.as_slice(), plaintext);
988            }
989        };
990        ($name:ident, $alg:expr, $mode:expr, $key:literal, $plaintext:literal, $ciphertext:literal) => {
991            #[test]
992            fn $name() {
993                let key = from_hex($key).unwrap();
994                let input = from_hex($plaintext).unwrap();
995                let expected_ciphertext = from_hex($ciphertext).unwrap();
996
997                let alg = $alg;
998
999                let unbound_key = UnboundCipherKey::new(alg, &key).unwrap();
1000
1001                let encrypting_key = EncryptingKey::new(unbound_key, $mode).unwrap();
1002
1003                let mut in_out = input.clone();
1004
1005                let context = encrypting_key
1006                    .less_safe_encrypt(&mut in_out, EncryptionContext::None)
1007                    .unwrap();
1008
1009                assert_eq!(expected_ciphertext, in_out);
1010
1011                let unbound_key2 = UnboundCipherKey::new(alg, &key).unwrap();
1012                let decrypting_key = DecryptingKey::new(unbound_key2, $mode).unwrap();
1013
1014                let plaintext = decrypting_key.decrypt(&mut in_out, context).unwrap();
1015                assert_eq!(input.as_slice(), plaintext);
1016            }
1017        };
1018    }
1019
1020    cipher_kat!(
1021        test_iv_aes_128_ctr_16_bytes,
1022        &AES_128,
1023        OperatingMode::CTR,
1024        "000102030405060708090a0b0c0d0e0f",
1025        "00000000000000000000000000000000",
1026        "00112233445566778899aabbccddeeff",
1027        "c6b01904c3da3df5e7d62bd96d153686"
1028    );
1029
1030    cipher_kat!(
1031        test_iv_aes_256_ctr_15_bytes,
1032        &AES_256,
1033        OperatingMode::CTR,
1034        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
1035        "00000000000000000000000000000000",
1036        "00112233445566778899aabbccddee",
1037        "f28122856e1cf9a7216a30d111f399"
1038    );
1039
1040    cipher_kat!(
1041        test_openssl_aes_128_ctr_15_bytes,
1042        &AES_128,
1043        OperatingMode::CTR,
1044        "244828580821c1652582c76e34d299f5",
1045        "093145d5af233f46072a5eb5adc11aa1",
1046        "3ee38cec171e6cf466bf0df98aa0e1",
1047        "bd7d928f60e3422d96b3f8cd614eb2"
1048    );
1049
1050    cipher_kat!(
1051        test_openssl_aes_256_ctr_15_bytes,
1052        &AES_256,
1053        OperatingMode::CTR,
1054        "0857db8240ea459bdf660b4cced66d1f2d3734ff2de7b81e92740e65e7cc6a1d",
1055        "f028ecb053f801102d11fccc9d303a27",
1056        "eca7285d19f3c20e295378460e8729",
1057        "b5098e5e788de6ac2f2098eb2fc6f8"
1058    );
1059
1060    cipher_kat!(
1061        test_sp800_38a_cfb128_aes128,
1062        &AES_128,
1063        OperatingMode::CFB128,
1064        "2b7e151628aed2a6abf7158809cf4f3c",
1065        "000102030405060708090a0b0c0d0e0f",
1066        "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
1067        "3b3fd92eb72dad20333449f8e83cfb4ac8a64537a0b3a93fcde3cdad9f1ce58b26751f67a3cbb140b1808cf187a4f4dfc04b05357c5d1c0eeac4c66f9ff7f2e6"
1068    );
1069
1070    cipher_kat!(
1071        test_sp800_38a_cfb128_aes256,
1072        &AES_256,
1073        OperatingMode::CFB128,
1074        "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
1075        "000102030405060708090a0b0c0d0e0f",
1076        "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
1077        "dc7e84bfda79164b7ecd8486985d386039ffed143b28b1c832113c6331e5407bdf10132415e54b92a13ed0a8267ae2f975a385741ab9cef82031623d55b1e471"
1078    );
1079
1080    cipher_kat!(
1081        test_sp800_38a_ecb_aes128,
1082        &AES_128,
1083        OperatingMode::ECB,
1084        "2b7e151628aed2a6abf7158809cf4f3c",
1085        "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
1086        "3ad77bb40d7a3660a89ecaf32466ef97f5d3d58503b9699de785895a96fdbaaf43b1cd7f598ece23881b00e3ed0306887b0c785e27e8ad3f8223207104725dd4"
1087    );
1088
1089    cipher_kat!(
1090        test_sp800_38a_ecb_aes256,
1091        &AES_256,
1092        OperatingMode::ECB,
1093        "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
1094        "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
1095        "f3eed1bdb5d2a03c064b5a7e3db181f8591ccb10d410ed26dc5ba74a31362870b6ed21b99ca6f4f9f153e7b1beafed1d23304b7a39f9f3ff067d8d8f9e24ecc7"
1096    );
1097}