Skip to main content

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//! ## Feature flags
202//!
203//! ### `legacy-des`
204//!
205//! Enables single DES and Triple DES (`DES_FOR_LEGACY_USE_ONLY`,
206//! `DES_EDE_FOR_LEGACY_USE_ONLY`, `DES_EDE3_FOR_LEGACY_USE_ONLY`) for legacy
207//! interoperability. Only CBC and ECB modes are supported. All exposed items
208//! are `#[deprecated]`. See the crate-level [feature flag
209//! documentation](crate#legacy-des) for details and security caveats.
210//!
211//! ## Getting an immutable reference to the IV slice.
212//!
213//! `TryFrom<&DecryptionContext>` is implemented for `&[u8]` allowing immutable references
214//! to IV bytes returned from cipher encryption operations. Note this is implemented as a `TryFrom` as it
215//! may fail for future enum variants that aren't representable as a single slice.
216//!
217//! ```rust
218//! # use std::error::Error;
219//! # fn main() -> Result<(), Box<dyn Error>> {
220//! # use aws_lc_rs::cipher::DecryptionContext;
221//! # use aws_lc_rs::iv::FixedLength;
222//! # let x: DecryptionContext = DecryptionContext::Iv128(FixedLength::from([0u8; 16]));
223//! // x is type `DecryptionContext`
224//! let iv: &[u8] = (&x).try_into()?;
225//! # Ok(())
226//! # }
227//! ```
228
229#![allow(clippy::module_name_repetitions)]
230
231pub(crate) mod aes;
232pub(crate) mod block;
233pub(crate) mod chacha;
234#[cfg(feature = "legacy-des")]
235pub(crate) mod des;
236pub(crate) mod key;
237mod padded;
238mod streaming;
239
240pub use padded::{PaddedBlockDecryptingKey, PaddedBlockEncryptingKey};
241pub use streaming::{BufferUpdate, StreamingDecryptingKey, StreamingEncryptingKey};
242
243use crate::aws_lc::{
244    EVP_aes_128_cbc, EVP_aes_128_cfb1, EVP_aes_128_cfb128, EVP_aes_128_cfb8, EVP_aes_128_ctr,
245    EVP_aes_128_ecb, EVP_aes_128_ofb, EVP_aes_192_cbc, EVP_aes_192_cfb1, EVP_aes_192_cfb128,
246    EVP_aes_192_cfb8, EVP_aes_192_ctr, EVP_aes_192_ecb, EVP_aes_192_ofb, EVP_aes_256_cbc,
247    EVP_aes_256_cfb1, EVP_aes_256_cfb128, EVP_aes_256_cfb8, EVP_aes_256_ctr, EVP_aes_256_ecb,
248    EVP_aes_256_ofb, EVP_CIPHER,
249};
250#[cfg(feature = "legacy-des")]
251use crate::aws_lc::{
252    EVP_des_cbc, EVP_des_ecb, EVP_des_ede, EVP_des_ede3_cbc, EVP_des_ede3_ecb, EVP_des_ede_cbc,
253};
254use crate::buffer::Buffer;
255use crate::error::{KeyRejected, Unspecified};
256use crate::hkdf;
257use crate::hkdf::KeyType;
258#[cfg(feature = "legacy-des")]
259use crate::iv::IV_LEN_64_BIT;
260use crate::iv::{FixedLength, IV_LEN_128_BIT};
261use crate::ptr::ConstPointer;
262use core::fmt::Debug;
263use key::SymmetricCipherKey;
264
265/// The number of bytes in an AES 128-bit key
266pub use crate::cipher::aes::AES_128_KEY_LEN;
267
268/// The number of bytes in an AES 192-bit key
269pub use crate::cipher::aes::AES_192_KEY_LEN;
270
271/// The number of bytes in an AES 256-bit key
272pub use crate::cipher::aes::AES_256_KEY_LEN;
273
274/// The number of bytes in a single DES key.
275///
276/// Single DES provides only 56 bits of effective security and is retained
277/// for interoperability only.
278#[cfg(feature = "legacy-des")]
279#[cfg_attr(aws_lc_rs_docsrs, doc(cfg(feature = "legacy-des")))]
280#[deprecated(
281    since = "1.17.0",
282    note = "Single DES is a legacy algorithm retained for interoperability only. Prefer an AES-based algorithm."
283)]
284pub use crate::cipher::des::DES_KEY_LEN;
285
286/// The number of bytes in a 2TDEA (DES-EDE, 2-key Triple DES) key.
287///
288/// 2-key Triple DES is a legacy algorithm and has been disallowed for encryption
289/// by NIST SP 800-131A Rev. 2 since 2015. It is retained for interoperability only.
290#[cfg(feature = "legacy-des")]
291#[cfg_attr(aws_lc_rs_docsrs, doc(cfg(feature = "legacy-des")))]
292#[deprecated(
293    since = "1.17.0",
294    note = "2-key Triple DES is a legacy algorithm retained for interoperability only; NIST SP 800-131A Rev. 2 disallows its use for encryption. Prefer an AES-based algorithm."
295)]
296pub use crate::cipher::des::DES_EDE_KEY_LEN;
297
298/// The number of bytes in a 3TDEA (DES-EDE3, 3-key Triple DES) key.
299///
300/// 3DES is a legacy algorithm and has been disallowed for encryption by
301/// NIST SP 800-131A Rev. 2 after 2023. It is retained for interoperability only.
302#[cfg(feature = "legacy-des")]
303#[cfg_attr(aws_lc_rs_docsrs, doc(cfg(feature = "legacy-des")))]
304#[deprecated(
305    since = "1.17.0",
306    note = "3DES is a legacy algorithm retained for interoperability only; NIST SP 800-131A Rev. 2 disallows its use for encryption. Prefer an AES-based algorithm."
307)]
308pub use crate::cipher::des::DES_EDE3_KEY_LEN;
309
310const MAX_CIPHER_KEY_LEN: usize = AES_256_KEY_LEN;
311
312/// The number of bytes for an AES-CBC initialization vector (IV)
313pub use crate::cipher::aes::AES_CBC_IV_LEN;
314
315/// The number of bytes for an AES-CTR initialization vector (IV)
316pub use crate::cipher::aes::AES_CTR_IV_LEN;
317
318/// The number of bytes for an AES-CFB initialization vector (IV)
319pub use crate::cipher::aes::AES_CFB_IV_LEN;
320
321/// The number of bytes for an AES-OFB initialization vector (IV)
322pub use crate::cipher::aes::AES_OFB_IV_LEN;
323
324/// The number of bytes for a DES/3DES-CBC initialization vector (IV).
325///
326/// DES and 3DES are legacy algorithms and have been disallowed for encryption by
327/// NIST SP 800-131A Rev. 2 after 2023. They are retained for interoperability only.
328#[cfg(feature = "legacy-des")]
329#[cfg_attr(aws_lc_rs_docsrs, doc(cfg(feature = "legacy-des")))]
330#[deprecated(
331    since = "1.17.0",
332    note = "DES/3DES are legacy algorithms retained for interoperability only; NIST SP 800-131A Rev. 2 disallows their use for encryption. Prefer an AES-based algorithm."
333)]
334pub use crate::cipher::des::DES_CBC_IV_LEN;
335
336use crate::cipher::aes::AES_BLOCK_LEN;
337#[cfg(feature = "legacy-des")]
338use crate::cipher::des::DES_BLOCK_LEN;
339
340const MAX_CIPHER_BLOCK_LEN: usize = AES_BLOCK_LEN;
341
342/// The cipher operating mode.
343#[non_exhaustive]
344#[derive(Debug, PartialEq, Eq, Clone, Copy)]
345pub enum OperatingMode {
346    /// Cipher block chaining (CBC) mode.
347    CBC,
348
349    /// Counter (CTR) mode.
350    CTR,
351
352    /// CFB 128-bit mode.
353    CFB128,
354
355    /// Electronic Code Book (ECB) mode.
356    ECB,
357
358    /// CFB 8-bit mode.
359    CFB8,
360
361    /// CFB 1-bit mode.
362    ///
363    /// This API is byte-oriented: input and output lengths are counted in
364    /// bytes, and each byte is processed as eight successive 1-bit segments.
365    CFB1,
366
367    /// Output feedback (OFB) mode.
368    OFB,
369}
370
371impl OperatingMode {
372    fn evp_cipher(&self, algorithm: &Algorithm) -> ConstPointer<'_, EVP_CIPHER> {
373        let alg = match (self, algorithm.id) {
374            (OperatingMode::CBC, AlgorithmId::Aes128) => unsafe { EVP_aes_128_cbc() },
375            (OperatingMode::CTR, AlgorithmId::Aes128) => unsafe { EVP_aes_128_ctr() },
376            (OperatingMode::CFB128, AlgorithmId::Aes128) => unsafe { EVP_aes_128_cfb128() },
377            (OperatingMode::CFB8, AlgorithmId::Aes128) => unsafe { EVP_aes_128_cfb8() },
378            (OperatingMode::CFB1, AlgorithmId::Aes128) => unsafe { EVP_aes_128_cfb1() },
379            (OperatingMode::OFB, AlgorithmId::Aes128) => unsafe { EVP_aes_128_ofb() },
380            (OperatingMode::ECB, AlgorithmId::Aes128) => unsafe { EVP_aes_128_ecb() },
381            (OperatingMode::CBC, AlgorithmId::Aes192) => unsafe { EVP_aes_192_cbc() },
382            (OperatingMode::CTR, AlgorithmId::Aes192) => unsafe { EVP_aes_192_ctr() },
383            (OperatingMode::CFB128, AlgorithmId::Aes192) => unsafe { EVP_aes_192_cfb128() },
384            (OperatingMode::CFB8, AlgorithmId::Aes192) => unsafe { EVP_aes_192_cfb8() },
385            (OperatingMode::CFB1, AlgorithmId::Aes192) => unsafe { EVP_aes_192_cfb1() },
386            (OperatingMode::OFB, AlgorithmId::Aes192) => unsafe { EVP_aes_192_ofb() },
387            (OperatingMode::ECB, AlgorithmId::Aes192) => unsafe { EVP_aes_192_ecb() },
388            (OperatingMode::CBC, AlgorithmId::Aes256) => unsafe { EVP_aes_256_cbc() },
389            (OperatingMode::CTR, AlgorithmId::Aes256) => unsafe { EVP_aes_256_ctr() },
390            (OperatingMode::CFB128, AlgorithmId::Aes256) => unsafe { EVP_aes_256_cfb128() },
391            (OperatingMode::CFB8, AlgorithmId::Aes256) => unsafe { EVP_aes_256_cfb8() },
392            (OperatingMode::CFB1, AlgorithmId::Aes256) => unsafe { EVP_aes_256_cfb1() },
393            (OperatingMode::OFB, AlgorithmId::Aes256) => unsafe { EVP_aes_256_ofb() },
394            (OperatingMode::ECB, AlgorithmId::Aes256) => unsafe { EVP_aes_256_ecb() },
395            #[cfg(feature = "legacy-des")]
396            #[allow(deprecated)]
397            (OperatingMode::CBC, AlgorithmId::DesForLegacyUseOnly) => unsafe { EVP_des_cbc() },
398            #[cfg(feature = "legacy-des")]
399            #[allow(deprecated)]
400            (OperatingMode::ECB, AlgorithmId::DesForLegacyUseOnly) => unsafe { EVP_des_ecb() },
401            #[cfg(feature = "legacy-des")]
402            #[allow(deprecated)]
403            (OperatingMode::CBC, AlgorithmId::DesEdeForLegacyUseOnly) => unsafe {
404                EVP_des_ede_cbc()
405            },
406            #[cfg(feature = "legacy-des")]
407            #[allow(deprecated)]
408            (OperatingMode::ECB, AlgorithmId::DesEdeForLegacyUseOnly) => unsafe { EVP_des_ede() },
409            #[cfg(feature = "legacy-des")]
410            #[allow(deprecated)]
411            (OperatingMode::CBC, AlgorithmId::DesEde3ForLegacyUseOnly) => unsafe {
412                EVP_des_ede3_cbc()
413            },
414            #[cfg(feature = "legacy-des")]
415            #[allow(deprecated)]
416            (OperatingMode::ECB, AlgorithmId::DesEde3ForLegacyUseOnly) => unsafe {
417                EVP_des_ede3_ecb()
418            },
419            #[cfg(feature = "legacy-des")]
420            #[allow(deprecated)]
421            (
422                OperatingMode::CTR
423                | OperatingMode::CFB128
424                | OperatingMode::CFB8
425                | OperatingMode::CFB1
426                | OperatingMode::OFB,
427                AlgorithmId::DesForLegacyUseOnly
428                | AlgorithmId::DesEdeForLegacyUseOnly
429                | AlgorithmId::DesEde3ForLegacyUseOnly,
430            ) => {
431                // `supports_mode()` rejects these combinations at key-construction
432                // time, so this branch is unreachable in practice.
433                unreachable!("DES does not support CTR, CFB or OFB modes")
434            }
435        };
436        unsafe { ConstPointer::new_static(alg).unwrap() }
437    }
438}
439
440macro_rules! define_cipher_context {
441    ($name:ident, $other:ident) => {
442        /// The contextual data used to encrypt or decrypt data.
443        #[non_exhaustive]
444        pub enum $name {
445            /// A 128-bit Initialization Vector.
446            Iv128(FixedLength<IV_LEN_128_BIT>),
447
448            /// A 64-bit Initialization Vector.
449            ///
450            /// Used by DES and 3DES in CBC mode only. ECB mode uses
451            /// [`Self::None`] regardless of the cipher's block size, and
452            /// no other cipher in this crate produces or consumes a 64-bit IV.
453            #[cfg(feature = "legacy-des")]
454            #[cfg_attr(aws_lc_rs_docsrs, doc(cfg(feature = "legacy-des")))]
455            Iv64(FixedLength<IV_LEN_64_BIT>),
456
457            /// No Cipher Context
458            None,
459        }
460
461        impl<'a> TryFrom<&'a $name> for &'a [u8] {
462            type Error = Unspecified;
463
464            fn try_from(value: &'a $name) -> Result<Self, Unspecified> {
465                match value {
466                    $name::Iv128(iv) => Ok(iv.as_ref()),
467                    #[cfg(feature = "legacy-des")]
468                    $name::Iv64(iv) => Ok(iv.as_ref()),
469                    _ => Err(Unspecified),
470                }
471            }
472        }
473
474        impl Debug for $name {
475            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
476                match self {
477                    Self::Iv128(_) => write!(f, "Iv128"),
478                    #[cfg(feature = "legacy-des")]
479                    Self::Iv64(_) => write!(f, "Iv64"),
480                    Self::None => write!(f, "None"),
481                }
482            }
483        }
484
485        impl From<$other> for $name {
486            fn from(value: $other) -> Self {
487                match value {
488                    $other::Iv128(iv) => $name::Iv128(iv),
489                    #[cfg(feature = "legacy-des")]
490                    $other::Iv64(iv) => $name::Iv64(iv),
491                    $other::None => $name::None,
492                }
493            }
494        }
495    };
496}
497
498define_cipher_context!(EncryptionContext, DecryptionContext);
499define_cipher_context!(DecryptionContext, EncryptionContext);
500
501#[non_exhaustive]
502#[derive(Debug, PartialEq, Eq, Clone, Copy)]
503/// Cipher algorithm identifier.
504pub enum AlgorithmId {
505    /// AES 128-bit
506    Aes128,
507
508    /// AES 256-bit
509    Aes256,
510
511    /// AES 192-bit
512    Aes192,
513
514    /// Single DES (for legacy use only).
515    ///
516    /// Single DES provides only 56 bits of effective security and has been
517    /// considered insecure for decades.
518    #[cfg(feature = "legacy-des")]
519    #[cfg_attr(aws_lc_rs_docsrs, doc(cfg(feature = "legacy-des")))]
520    #[deprecated(
521        since = "1.17.0",
522        note = "Single DES is a legacy algorithm retained for interoperability only. Prefer an AES-based algorithm."
523    )]
524    DesForLegacyUseOnly,
525
526    /// 2TDEA (for legacy use only).
527    ///
528    /// 2-key Triple DES has been disallowed for encryption by
529    /// NIST SP 800-131A Rev. 2 since 2015.
530    #[cfg(feature = "legacy-des")]
531    #[cfg_attr(aws_lc_rs_docsrs, doc(cfg(feature = "legacy-des")))]
532    #[deprecated(
533        since = "1.17.0",
534        note = "2-key Triple DES is a legacy algorithm retained for interoperability only; NIST SP 800-131A Rev. 2 disallows its use for encryption. Prefer an AES-based algorithm."
535    )]
536    DesEdeForLegacyUseOnly,
537
538    /// 3TDEA (for legacy use only).
539    ///
540    /// 3DES has been disallowed for encryption by NIST SP 800-131A Rev. 2
541    /// after 2023.
542    #[cfg(feature = "legacy-des")]
543    #[cfg_attr(aws_lc_rs_docsrs, doc(cfg(feature = "legacy-des")))]
544    #[deprecated(
545        since = "1.17.0",
546        note = "3DES is a legacy algorithm retained for interoperability only; NIST SP 800-131A Rev. 2 disallows its use for encryption. Prefer an AES-based algorithm."
547    )]
548    DesEde3ForLegacyUseOnly,
549}
550
551/// A cipher algorithm.
552#[derive(Debug, PartialEq, Eq)]
553pub struct Algorithm {
554    id: AlgorithmId,
555    key_len: usize,
556    block_len: usize,
557}
558
559/// AES 128-bit cipher
560pub const AES_128: Algorithm = Algorithm {
561    id: AlgorithmId::Aes128,
562    key_len: AES_128_KEY_LEN,
563    block_len: AES_BLOCK_LEN,
564};
565
566/// AES 192-bit cipher
567pub const AES_192: Algorithm = Algorithm {
568    id: AlgorithmId::Aes192,
569    key_len: AES_192_KEY_LEN,
570    block_len: AES_BLOCK_LEN,
571};
572
573/// AES 256-bit cipher
574pub const AES_256: Algorithm = Algorithm {
575    id: AlgorithmId::Aes256,
576    key_len: AES_256_KEY_LEN,
577    block_len: AES_BLOCK_LEN,
578};
579
580/// Single DES cipher, for legacy interoperability only.
581///
582/// Single DES provides only 56 bits of effective security and has been
583/// considered insecure for decades. It is retained here solely for
584/// interoperability with legacy systems. New designs must use an AES-based
585/// algorithm.
586///
587/// Only CBC and ECB operating modes are supported. Weak and semi-weak DES
588/// keys are rejected.
589#[cfg(feature = "legacy-des")]
590#[cfg_attr(aws_lc_rs_docsrs, doc(cfg(feature = "legacy-des")))]
591#[allow(deprecated)]
592#[deprecated(
593    since = "1.17.0",
594    note = "Single DES is a legacy algorithm retained for interoperability only. Prefer an AES-based algorithm."
595)]
596pub const DES_FOR_LEGACY_USE_ONLY: Algorithm = Algorithm {
597    id: AlgorithmId::DesForLegacyUseOnly,
598    key_len: DES_KEY_LEN,
599    block_len: DES_BLOCK_LEN,
600};
601
602/// 2TDEA (DES-EDE, 2-key Triple DES) cipher, for legacy interoperability only.
603///
604/// 2-key Triple DES is a legacy algorithm and has been disallowed for encryption
605/// by [NIST SP 800-131A Rev. 2](https://csrc.nist.gov/publications/detail/sp/800-131a/rev-2/final)
606/// since 2015 โ€” several years earlier than 3-key Triple DES. It is retained here
607/// solely for interoperability. New designs must use an AES-based algorithm.
608///
609/// Only CBC and ECB operating modes are supported. Key material is validated
610/// beyond the length check: weak and semi-weak DES subkeys are rejected, and
611/// K1 must differ from K2 (otherwise the cipher degenerates to single-DES).
612/// This validation is deferred โ€” constructing an [`UnboundCipherKey`] only
613/// checks length; the weak-key and pairwise-distinctness checks fire when the
614/// [`UnboundCipherKey`] is passed to an [`EncryptingKey`], [`DecryptingKey`],
615/// [`PaddedBlockEncryptingKey`], [`PaddedBlockDecryptingKey`],
616/// [`StreamingEncryptingKey`], or [`StreamingDecryptingKey`] constructor.
617#[cfg(feature = "legacy-des")]
618#[cfg_attr(aws_lc_rs_docsrs, doc(cfg(feature = "legacy-des")))]
619#[allow(deprecated)]
620#[deprecated(
621    since = "1.17.0",
622    note = "2-key Triple DES is a legacy algorithm retained for interoperability only; NIST SP 800-131A Rev. 2 disallows its use for encryption. Prefer an AES-based algorithm."
623)]
624pub const DES_EDE_FOR_LEGACY_USE_ONLY: Algorithm = Algorithm {
625    id: AlgorithmId::DesEdeForLegacyUseOnly,
626    key_len: DES_EDE_KEY_LEN,
627    block_len: DES_BLOCK_LEN,
628};
629
630/// 3TDEA (DES-EDE3, 3-key Triple DES) cipher, for legacy interoperability only.
631///
632/// 3DES is a legacy algorithm and has been disallowed for encryption by
633/// [NIST SP 800-131A Rev. 2](https://csrc.nist.gov/publications/detail/sp/800-131a/rev-2/final)
634/// after 2023. It is retained here solely for interoperability. New designs
635/// must use an AES-based algorithm.
636///
637/// Only CBC and ECB operating modes are supported. Key material is validated
638/// beyond the length check: weak and semi-weak DES subkeys are rejected, and
639/// K1, K2, and K3 must all be pairwise distinct (otherwise 3TDEA degenerates
640/// to either single-DES or 2TDEA). This validation is deferred โ€”
641/// constructing an [`UnboundCipherKey`] only checks length; the weak-key and
642/// pairwise-distinctness checks fire when the [`UnboundCipherKey`] is passed
643/// to an [`EncryptingKey`], [`DecryptingKey`], [`PaddedBlockEncryptingKey`],
644/// [`PaddedBlockDecryptingKey`], [`StreamingEncryptingKey`], or
645/// [`StreamingDecryptingKey`] constructor.
646///
647/// Callers who specifically need 2-key Triple DES (2TDEA) must use
648/// [`DES_EDE_FOR_LEGACY_USE_ONLY`] (16-byte key) rather than encoding 2TDEA
649/// as a 24-byte `K1 โ€– K2 โ€– K1` key here.
650///
651/// # Example: 3DES-CBC interop round-trip
652///
653/// ```rust
654/// # #[cfg(feature = "legacy-des")]
655/// # #[allow(deprecated)]
656/// # fn main() -> Result<(), aws_lc_rs::error::Unspecified> {
657/// use aws_lc_rs::cipher::{
658///     DecryptingKey, EncryptingKey, UnboundCipherKey, DES_EDE3_FOR_LEGACY_USE_ONLY,
659/// };
660///
661/// let key_bytes: [u8; 24] = [
662///     0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
663///     0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01,
664///     0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23,
665/// ];
666/// let plaintext = *b"8-byte..16-byte.";
667///
668/// let key = UnboundCipherKey::new(&DES_EDE3_FOR_LEGACY_USE_ONLY, &key_bytes)?;
669/// let mut buf = plaintext.to_vec();
670/// let ctx = EncryptingKey::cbc(key)?.encrypt(&mut buf)?;
671///
672/// let key = UnboundCipherKey::new(&DES_EDE3_FOR_LEGACY_USE_ONLY, &key_bytes)?;
673/// let recovered = DecryptingKey::cbc(key)?.decrypt(&mut buf, ctx)?;
674/// assert_eq!(&plaintext[..], recovered);
675/// # Ok(())
676/// # }
677/// # #[cfg(not(feature = "legacy-des"))]
678/// # fn main() {}
679/// ```
680#[cfg(feature = "legacy-des")]
681#[cfg_attr(aws_lc_rs_docsrs, doc(cfg(feature = "legacy-des")))]
682#[allow(deprecated)]
683#[deprecated(
684    since = "1.17.0",
685    note = "3DES is a legacy algorithm retained for interoperability only; NIST SP 800-131A Rev. 2 disallows its use for encryption. Prefer an AES-based algorithm."
686)]
687pub const DES_EDE3_FOR_LEGACY_USE_ONLY: Algorithm = Algorithm {
688    id: AlgorithmId::DesEde3ForLegacyUseOnly,
689    key_len: DES_EDE3_KEY_LEN,
690    block_len: DES_BLOCK_LEN,
691};
692
693impl Algorithm {
694    fn id(&self) -> &AlgorithmId {
695        &self.id
696    }
697
698    /// The block length of this cipher algorithm.
699    #[must_use]
700    pub const fn block_len(&self) -> usize {
701        self.block_len
702    }
703
704    fn new_encryption_context(
705        &self,
706        mode: OperatingMode,
707    ) -> Result<EncryptionContext, Unspecified> {
708        match self.id {
709            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => match mode {
710                OperatingMode::CBC
711                | OperatingMode::CTR
712                | OperatingMode::CFB128
713                | OperatingMode::CFB8
714                | OperatingMode::CFB1
715                | OperatingMode::OFB => Ok(EncryptionContext::Iv128(FixedLength::new()?)),
716                OperatingMode::ECB => Ok(EncryptionContext::None),
717            },
718            #[cfg(feature = "legacy-des")]
719            #[allow(deprecated)]
720            AlgorithmId::DesForLegacyUseOnly
721            | AlgorithmId::DesEdeForLegacyUseOnly
722            | AlgorithmId::DesEde3ForLegacyUseOnly => match mode {
723                OperatingMode::CBC => Ok(EncryptionContext::Iv64(FixedLength::new()?)),
724                OperatingMode::ECB => Ok(EncryptionContext::None),
725                _ => Err(Unspecified),
726            },
727        }
728    }
729
730    fn is_valid_encryption_context(&self, mode: OperatingMode, input: &EncryptionContext) -> bool {
731        match self.id {
732            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => match mode {
733                OperatingMode::CBC
734                | OperatingMode::CTR
735                | OperatingMode::CFB128
736                | OperatingMode::CFB8
737                | OperatingMode::CFB1
738                | OperatingMode::OFB => matches!(input, EncryptionContext::Iv128(_)),
739                OperatingMode::ECB => {
740                    matches!(input, EncryptionContext::None)
741                }
742            },
743            #[cfg(feature = "legacy-des")]
744            #[allow(deprecated)]
745            AlgorithmId::DesForLegacyUseOnly
746            | AlgorithmId::DesEdeForLegacyUseOnly
747            | AlgorithmId::DesEde3ForLegacyUseOnly => match mode {
748                OperatingMode::CBC => matches!(input, EncryptionContext::Iv64(_)),
749                OperatingMode::ECB => matches!(input, EncryptionContext::None),
750                _ => false,
751            },
752        }
753    }
754
755    fn is_valid_decryption_context(&self, mode: OperatingMode, input: &DecryptionContext) -> bool {
756        match self.id {
757            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => match mode {
758                OperatingMode::CBC
759                | OperatingMode::CTR
760                | OperatingMode::CFB128
761                | OperatingMode::CFB8
762                | OperatingMode::CFB1
763                | OperatingMode::OFB => matches!(input, DecryptionContext::Iv128(_)),
764                OperatingMode::ECB => {
765                    matches!(input, DecryptionContext::None)
766                }
767            },
768            #[cfg(feature = "legacy-des")]
769            #[allow(deprecated)]
770            AlgorithmId::DesForLegacyUseOnly
771            | AlgorithmId::DesEdeForLegacyUseOnly
772            | AlgorithmId::DesEde3ForLegacyUseOnly => match mode {
773                OperatingMode::CBC => matches!(input, DecryptionContext::Iv64(_)),
774                OperatingMode::ECB => matches!(input, DecryptionContext::None),
775                _ => false,
776            },
777        }
778    }
779
780    /// Returns `true` if the algorithm supports the given operating mode.
781    ///
782    /// Used to reject invalid `(algorithm, mode)` combinations (e.g. DES with
783    /// CTR or CFB128) at key-construction time rather than deferring the
784    /// failure to the first encrypt/decrypt call.
785    fn supports_mode(&self, mode: OperatingMode) -> bool {
786        match self.id {
787            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => matches!(
788                mode,
789                OperatingMode::CBC
790                    | OperatingMode::CTR
791                    | OperatingMode::CFB128
792                    | OperatingMode::CFB8
793                    | OperatingMode::CFB1
794                    | OperatingMode::OFB
795                    | OperatingMode::ECB
796            ),
797            #[cfg(feature = "legacy-des")]
798            #[allow(deprecated)]
799            AlgorithmId::DesForLegacyUseOnly
800            | AlgorithmId::DesEdeForLegacyUseOnly
801            | AlgorithmId::DesEde3ForLegacyUseOnly => {
802                matches!(mode, OperatingMode::CBC | OperatingMode::ECB)
803            }
804        }
805    }
806}
807
808#[allow(clippy::missing_fields_in_debug)]
809impl Debug for UnboundCipherKey {
810    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
811        f.debug_struct("UnboundCipherKey")
812            .field("algorithm", &self.algorithm)
813            .finish()
814    }
815}
816
817impl From<hkdf::Okm<'_, &'static Algorithm>> for UnboundCipherKey {
818    fn from(okm: hkdf::Okm<&'static Algorithm>) -> Self {
819        let mut key_bytes = [0; MAX_CIPHER_KEY_LEN];
820        let key_bytes = &mut key_bytes[..okm.len().key_len];
821        let algorithm = *okm.len();
822        okm.fill(key_bytes).unwrap();
823        Self::new(algorithm, key_bytes).unwrap()
824    }
825}
826
827impl KeyType for &'static Algorithm {
828    fn len(&self) -> usize {
829        self.key_len
830    }
831}
832
833/// A key bound to a particular cipher algorithm.
834pub struct UnboundCipherKey {
835    algorithm: &'static Algorithm,
836    key_bytes: Buffer<'static, &'static [u8]>,
837}
838
839impl UnboundCipherKey {
840    /// Constructs an [`UnboundCipherKey`].
841    ///
842    /// # Errors
843    ///
844    /// * [`Unspecified`] if `key_bytes.len()` does not match the length required by `algorithm`.
845    pub fn new(algorithm: &'static Algorithm, key_bytes: &[u8]) -> Result<Self, Unspecified> {
846        if key_bytes.len() != algorithm.key_len {
847            return Err(Unspecified);
848        }
849        let key_bytes = Buffer::new(key_bytes.to_vec());
850        Ok(UnboundCipherKey {
851            algorithm,
852            key_bytes,
853        })
854    }
855
856    #[inline]
857    #[must_use]
858    /// Returns the algorithm associated with this key.
859    pub fn algorithm(&self) -> &'static Algorithm {
860        self.algorithm
861    }
862
863    /// Validates the key material against algorithm-specific constraints beyond
864    /// the length check performed by [`UnboundCipherKey::new`].
865    ///
866    /// For DES algorithms (behind `legacy-des`), this rejects weak DES subkeys
867    /// and degenerate key configurations (e.g. K1 == K2 for 2TDEA). For other
868    /// algorithms this is a no-op since the only constraint is key length.
869    ///
870    /// The streaming cipher constructors call this because they pass raw key
871    /// bytes directly to the EVP API rather than going through
872    /// [`SymmetricCipherKey`] construction, which would otherwise perform these
873    /// checks. This is necessary because the EVP layer internally uses
874    /// `DES_set_key_unchecked`, which skips weak-key detection.
875    #[allow(clippy::unused_self)]
876    #[allow(clippy::unnecessary_wraps)]
877    fn validate_key_material(&self) -> Result<(), KeyRejected> {
878        #[cfg(feature = "legacy-des")]
879        #[allow(deprecated)]
880        match self.algorithm.id() {
881            AlgorithmId::DesForLegacyUseOnly => {
882                let _ = SymmetricCipherKey::prepare_des(self.key_bytes.as_ref())?;
883            }
884            AlgorithmId::DesEdeForLegacyUseOnly => {
885                let _ = SymmetricCipherKey::prepare_des_ede(self.key_bytes.as_ref())?;
886            }
887            AlgorithmId::DesEde3ForLegacyUseOnly => {
888                let _ = SymmetricCipherKey::prepare_des_ede3(self.key_bytes.as_ref())?;
889            }
890            _ => {}
891        }
892        Ok(())
893    }
894}
895
896impl TryInto<SymmetricCipherKey> for UnboundCipherKey {
897    type Error = KeyRejected;
898
899    fn try_into(self) -> Result<SymmetricCipherKey, Self::Error> {
900        match self.algorithm.id() {
901            AlgorithmId::Aes128 => SymmetricCipherKey::aes128(self.key_bytes.as_ref()),
902            AlgorithmId::Aes192 => SymmetricCipherKey::aes192(self.key_bytes.as_ref()),
903            AlgorithmId::Aes256 => SymmetricCipherKey::aes256(self.key_bytes.as_ref()),
904            #[cfg(feature = "legacy-des")]
905            #[allow(deprecated)]
906            AlgorithmId::DesForLegacyUseOnly => SymmetricCipherKey::des(self.key_bytes.as_ref()),
907            #[cfg(feature = "legacy-des")]
908            #[allow(deprecated)]
909            AlgorithmId::DesEdeForLegacyUseOnly => {
910                SymmetricCipherKey::des_ede(self.key_bytes.as_ref())
911            }
912            #[cfg(feature = "legacy-des")]
913            #[allow(deprecated)]
914            AlgorithmId::DesEde3ForLegacyUseOnly => {
915                SymmetricCipherKey::des_ede3(self.key_bytes.as_ref())
916            }
917        }
918    }
919}
920
921/// A cipher encryption key that does not perform block padding.
922pub struct EncryptingKey {
923    algorithm: &'static Algorithm,
924    key: SymmetricCipherKey,
925    mode: OperatingMode,
926}
927
928impl EncryptingKey {
929    /// Constructs an `EncryptingKey` operating in counter (CTR) mode using the provided key.
930    ///
931    // # FIPS
932    // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms:
933    // * `AES_128`
934    // * `AES_256`
935    //
936    /// # Errors
937    /// * [`Unspecified`]: Returned if there is an error constructing the `EncryptingKey`.
938    ///   With `legacy-des` enabled, also returned if `key`'s algorithm does not
939    ///   support CTR mode (e.g. `DES_FOR_LEGACY_USE_ONLY`,
940    ///   `DES_EDE_FOR_LEGACY_USE_ONLY`, `DES_EDE3_FOR_LEGACY_USE_ONLY`).
941    pub fn ctr(key: UnboundCipherKey) -> Result<Self, Unspecified> {
942        Self::new(key, OperatingMode::CTR)
943    }
944
945    /// Constructs an `EncryptingKey` operating in cipher feedback 128-bit mode (CFB128) using the provided key.
946    ///
947    // # FIPS
948    // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms:
949    // * `AES_128`
950    // * `AES_256`
951    //
952    /// # Errors
953    /// * [`Unspecified`]: Returned if there is an error constructing the `EncryptingKey`.
954    ///   With `legacy-des` enabled, also returned if `key`'s algorithm does not
955    ///   support CFB128 mode (e.g. `DES_FOR_LEGACY_USE_ONLY`,
956    ///   `DES_EDE_FOR_LEGACY_USE_ONLY`, `DES_EDE3_FOR_LEGACY_USE_ONLY`).
957    pub fn cfb128(key: UnboundCipherKey) -> Result<Self, Unspecified> {
958        Self::new(key, OperatingMode::CFB128)
959    }
960
961    /// Constructs an `EncryptingKey` operating in cipher feedback 8-bit mode (CFB8) using the provided key.
962    ///
963    // # FIPS
964    // AES OFB, CFB1 and CFB8 are not FIPS-approved services, for any AES key size.
965    //
966    /// # Errors
967    /// * [`Unspecified`]: Returned if there is an error constructing the `EncryptingKey`.
968    ///   With `legacy-des` enabled, also returned if `key`'s algorithm does not
969    ///   support CFB8 mode (e.g. `DES_FOR_LEGACY_USE_ONLY`,
970    ///   `DES_EDE_FOR_LEGACY_USE_ONLY`, `DES_EDE3_FOR_LEGACY_USE_ONLY`).
971    pub fn cfb8(key: UnboundCipherKey) -> Result<Self, Unspecified> {
972        Self::new(key, OperatingMode::CFB8)
973    }
974
975    /// Constructs an `EncryptingKey` operating in cipher feedback 1-bit mode (CFB1) using the provided key.
976    ///
977    /// This API is byte-oriented: the input length is counted in bytes, and
978    /// each byte is processed as eight successive 1-bit segments.
979    ///
980    // # FIPS
981    // AES OFB, CFB1 and CFB8 are not FIPS-approved services, for any AES key size.
982    //
983    /// # Errors
984    /// * [`Unspecified`]: Returned if there is an error constructing the `EncryptingKey`.
985    ///   With `legacy-des` enabled, also returned if `key`'s algorithm does not
986    ///   support CFB1 mode (e.g. `DES_FOR_LEGACY_USE_ONLY`,
987    ///   `DES_EDE_FOR_LEGACY_USE_ONLY`, `DES_EDE3_FOR_LEGACY_USE_ONLY`).
988    pub fn cfb1(key: UnboundCipherKey) -> Result<Self, Unspecified> {
989        Self::new(key, OperatingMode::CFB1)
990    }
991
992    /// Constructs an `EncryptingKey` operating in output feedback mode (OFB) using the provided key.
993    ///
994    // # FIPS
995    // AES OFB, CFB1 and CFB8 are not FIPS-approved services, for any AES key size.
996    //
997    /// # Errors
998    /// * [`Unspecified`]: Returned if there is an error constructing the `EncryptingKey`.
999    ///   With `legacy-des` enabled, also returned if `key`'s algorithm does not
1000    ///   support OFB mode (e.g. `DES_FOR_LEGACY_USE_ONLY`,
1001    ///   `DES_EDE_FOR_LEGACY_USE_ONLY`, `DES_EDE3_FOR_LEGACY_USE_ONLY`).
1002    pub fn ofb(key: UnboundCipherKey) -> Result<Self, Unspecified> {
1003        Self::new(key, OperatingMode::OFB)
1004    }
1005
1006    /// Constructs an `EncryptingKey` operating in cipher block chaining (CBC) mode using the provided key.
1007    ///
1008    /// # โ˜ ๏ธ ๏ธ๏ธ๏ธDANGER โ˜ ๏ธ
1009    /// Offered for compatibility purposes only. This is an extremely dangerous mode, and
1010    /// very likely not what you want to use.
1011    ///
1012    // # FIPS
1013    // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms:
1014    // * `AES_128`
1015    // * `AES_256`
1016    //
1017    // `DES_FOR_LEGACY_USE_ONLY`, `DES_EDE_FOR_LEGACY_USE_ONLY` and
1018    // `DES_EDE3_FOR_LEGACY_USE_ONLY` are never FIPS-approved.
1019    //
1020    /// # Errors
1021    /// * [`Unspecified`]: Returned if there is an error constructing the `EncryptingKey`.
1022    ///   With `legacy-des` enabled, also returned if `key` was constructed with
1023    ///   `DES_FOR_LEGACY_USE_ONLY`, `DES_EDE_FOR_LEGACY_USE_ONLY` or
1024    ///   `DES_EDE3_FOR_LEGACY_USE_ONLY` and the provided key material contains
1025    ///   weak or semi-weak DES subkeys, or (for Triple DES) a degenerate subkey
1026    ///   configuration (e.g. `K1 == K2` for 2TDEA, or any pairwise equality
1027    ///   for 3TDEA).
1028    pub fn cbc(key: UnboundCipherKey) -> Result<Self, Unspecified> {
1029        Self::new(key, OperatingMode::CBC)
1030    }
1031
1032    /// Constructs an `EncryptingKey` operating in electronic code book mode (ECB) using the provided key.
1033    ///
1034    /// # โ˜ ๏ธ ๏ธ๏ธ๏ธDANGER โ˜ ๏ธ
1035    /// Offered for compatibility purposes only. This is an extremely dangerous mode, and
1036    /// very likely not what you want to use.
1037    ///
1038    // # FIPS
1039    // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms:
1040    // * `AES_128`
1041    // * `AES_256`
1042    //
1043    // `DES_FOR_LEGACY_USE_ONLY`, `DES_EDE_FOR_LEGACY_USE_ONLY` and
1044    // `DES_EDE3_FOR_LEGACY_USE_ONLY` are never FIPS-approved.
1045    //
1046    /// # Errors
1047    /// * [`Unspecified`]: Returned if there is an error constructing the `EncryptingKey`.
1048    ///   With `legacy-des` enabled, also returned if `key` was constructed with
1049    ///   `DES_FOR_LEGACY_USE_ONLY`, `DES_EDE_FOR_LEGACY_USE_ONLY` or
1050    ///   `DES_EDE3_FOR_LEGACY_USE_ONLY` and the provided key material contains
1051    ///   weak or semi-weak DES subkeys, or (for Triple DES) a degenerate subkey
1052    ///   configuration (e.g. `K1 == K2` for 2TDEA, or any pairwise equality
1053    ///   for 3TDEA).
1054    pub fn ecb(key: UnboundCipherKey) -> Result<Self, Unspecified> {
1055        Self::new(key, OperatingMode::ECB)
1056    }
1057
1058    #[allow(clippy::unnecessary_wraps)]
1059    fn new(key: UnboundCipherKey, mode: OperatingMode) -> Result<Self, Unspecified> {
1060        let algorithm = key.algorithm();
1061        if !algorithm.supports_mode(mode) {
1062            return Err(Unspecified);
1063        }
1064        let key = key.try_into()?;
1065        Ok(Self {
1066            algorithm,
1067            key,
1068            mode,
1069        })
1070    }
1071
1072    /// Returns the cipher algorithm.
1073    #[must_use]
1074    pub fn algorithm(&self) -> &Algorithm {
1075        self.algorithm
1076    }
1077
1078    /// Returns the cipher operating mode.
1079    #[must_use]
1080    pub fn mode(&self) -> OperatingMode {
1081        self.mode
1082    }
1083
1084    /// Encrypts the data provided in `in_out` in-place.
1085    /// Returns a [`DecryptionContext`] with the randomly generated IV that was used to encrypt
1086    /// the data provided.
1087    ///
1088    /// If `EncryptingKey` is operating in `OperatingMode::ECB`, then `in_out.len()` must be a multiple
1089    /// of the block length.
1090    ///
1091    /// # Errors
1092    /// * [`Unspecified`]: Returned if cipher mode requires input to be a multiple of the block length,
1093    ///   and `in_out.len()` is not. Otherwise, returned if encryption fails.
1094    pub fn encrypt(&self, in_out: &mut [u8]) -> Result<DecryptionContext, Unspecified> {
1095        let context = self.algorithm.new_encryption_context(self.mode)?;
1096        self.less_safe_encrypt(in_out, context)
1097    }
1098
1099    /// Encrypts the data provided in `in_out` in-place using the provided `EncryptionContext`.
1100    /// This is considered "less safe" because the caller could potentially construct
1101    /// a `EncryptionContext` from a previously used IV (initialization vector).
1102    /// Returns a [`DecryptionContext`] produced from the provided `EncryptionContext`.
1103    ///
1104    /// If `EncryptingKey` is operating in `OperatingMode::ECB`, then `in_out.len()` must be a multiple
1105    /// of the block length.
1106    ///
1107    /// For `OperatingMode::CBC` and the CFB modes (`CFB128`, `CFB8`, `CFB1`), the IV must be
1108    /// unpredictable and must not be reused with the same key. For `OperatingMode::OFB`,
1109    /// the IV must not be reused with the same key.
1110    ///
1111    /// # Errors
1112    /// * [`Unspecified`]: Returned if cipher mode requires input to be a multiple of the block length,
1113    ///   and `in_out.len()` is not. Otherwise returned if encryption fails.
1114    pub fn less_safe_encrypt(
1115        &self,
1116        in_out: &mut [u8],
1117        context: EncryptionContext,
1118    ) -> Result<DecryptionContext, Unspecified> {
1119        if !self
1120            .algorithm()
1121            .is_valid_encryption_context(self.mode, &context)
1122        {
1123            return Err(Unspecified);
1124        }
1125        encrypt(self.algorithm(), &self.key, self.mode, in_out, context)
1126    }
1127}
1128
1129impl Debug for EncryptingKey {
1130    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1131        f.debug_struct("EncryptingKey")
1132            .field("algorithm", self.algorithm)
1133            .field("mode", &self.mode)
1134            .finish_non_exhaustive()
1135    }
1136}
1137
1138/// A cipher decryption key that does not perform block padding.
1139pub struct DecryptingKey {
1140    algorithm: &'static Algorithm,
1141    key: SymmetricCipherKey,
1142    mode: OperatingMode,
1143}
1144
1145impl DecryptingKey {
1146    /// Constructs a cipher decrypting key operating in counter (CTR) mode using the provided key and context.
1147    ///
1148    // # FIPS
1149    // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms:
1150    // * `AES_128`
1151    // * `AES_256`
1152    //
1153    /// # Errors
1154    /// * [`Unspecified`]: Returned if there is an error during decryption.
1155    ///   With `legacy-des` enabled, also returned if `key`'s algorithm does not
1156    ///   support CTR mode (e.g. `DES_FOR_LEGACY_USE_ONLY`,
1157    ///   `DES_EDE_FOR_LEGACY_USE_ONLY`, `DES_EDE3_FOR_LEGACY_USE_ONLY`).
1158    pub fn ctr(key: UnboundCipherKey) -> Result<DecryptingKey, Unspecified> {
1159        Self::new(key, OperatingMode::CTR)
1160    }
1161
1162    /// Constructs a cipher decrypting key operating in cipher feedback 128-bit mode (CFB128) using the provided key and context.
1163    ///
1164    // # FIPS
1165    // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms:
1166    // * `AES_128`
1167    // * `AES_256`
1168    //
1169    /// # Errors
1170    /// * [`Unspecified`]: Returned if there is an error during decryption.
1171    ///   With `legacy-des` enabled, also returned if `key`'s algorithm does not
1172    ///   support CFB128 mode (e.g. `DES_FOR_LEGACY_USE_ONLY`,
1173    ///   `DES_EDE_FOR_LEGACY_USE_ONLY`, `DES_EDE3_FOR_LEGACY_USE_ONLY`).
1174    pub fn cfb128(key: UnboundCipherKey) -> Result<Self, Unspecified> {
1175        Self::new(key, OperatingMode::CFB128)
1176    }
1177
1178    /// Constructs a `DecryptingKey` operating in cipher feedback 8-bit mode (CFB8) using the provided key.
1179    ///
1180    // # FIPS
1181    // AES OFB, CFB1 and CFB8 are not FIPS-approved services, for any AES key size.
1182    //
1183    /// # Errors
1184    /// * [`Unspecified`]: Returned if there is an error during decryption.
1185    ///   With `legacy-des` enabled, also returned if `key`'s algorithm does not
1186    ///   support CFB8 mode (e.g. `DES_FOR_LEGACY_USE_ONLY`,
1187    ///   `DES_EDE_FOR_LEGACY_USE_ONLY`, `DES_EDE3_FOR_LEGACY_USE_ONLY`).
1188    pub fn cfb8(key: UnboundCipherKey) -> Result<Self, Unspecified> {
1189        Self::new(key, OperatingMode::CFB8)
1190    }
1191
1192    /// Constructs a `DecryptingKey` operating in cipher feedback 1-bit mode (CFB1) using the provided key.
1193    ///
1194    /// This API is byte-oriented: the input length is counted in bytes, and
1195    /// each byte is processed as eight successive 1-bit segments.
1196    ///
1197    // # FIPS
1198    // AES OFB, CFB1 and CFB8 are not FIPS-approved services, for any AES key size.
1199    //
1200    /// # Errors
1201    /// * [`Unspecified`]: Returned if there is an error during decryption.
1202    ///   With `legacy-des` enabled, also returned if `key`'s algorithm does not
1203    ///   support CFB1 mode (e.g. `DES_FOR_LEGACY_USE_ONLY`,
1204    ///   `DES_EDE_FOR_LEGACY_USE_ONLY`, `DES_EDE3_FOR_LEGACY_USE_ONLY`).
1205    pub fn cfb1(key: UnboundCipherKey) -> Result<Self, Unspecified> {
1206        Self::new(key, OperatingMode::CFB1)
1207    }
1208
1209    /// Constructs a `DecryptingKey` operating in output feedback mode (OFB) using the provided key.
1210    ///
1211    // # FIPS
1212    // AES OFB, CFB1 and CFB8 are not FIPS-approved services, for any AES key size.
1213    //
1214    /// # Errors
1215    /// * [`Unspecified`]: Returned if there is an error during decryption.
1216    ///   With `legacy-des` enabled, also returned if `key`'s algorithm does not
1217    ///   support OFB mode (e.g. `DES_FOR_LEGACY_USE_ONLY`,
1218    ///   `DES_EDE_FOR_LEGACY_USE_ONLY`, `DES_EDE3_FOR_LEGACY_USE_ONLY`).
1219    pub fn ofb(key: UnboundCipherKey) -> Result<Self, Unspecified> {
1220        Self::new(key, OperatingMode::OFB)
1221    }
1222
1223    /// Constructs an `DecryptingKey` operating in cipher block chaining (CBC) mode using the provided key and context.
1224    ///
1225    /// # โ˜ ๏ธ ๏ธ๏ธ๏ธDANGER โ˜ ๏ธ
1226    /// Offered for compatibility purposes only. This is an extremely dangerous mode, and
1227    /// very likely not what you want to use.
1228    ///
1229    // # FIPS
1230    // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms:
1231    // * `AES_128`
1232    // * `AES_256`
1233    //
1234    // `DES_FOR_LEGACY_USE_ONLY`, `DES_EDE_FOR_LEGACY_USE_ONLY` and
1235    // `DES_EDE3_FOR_LEGACY_USE_ONLY` are never FIPS-approved.
1236    //
1237    /// # Errors
1238    /// * [`Unspecified`]: Returned if there is an error during decryption.
1239    ///   With `legacy-des` enabled, also returned if `key` was constructed with
1240    ///   `DES_FOR_LEGACY_USE_ONLY`, `DES_EDE_FOR_LEGACY_USE_ONLY` or
1241    ///   `DES_EDE3_FOR_LEGACY_USE_ONLY` and the provided key material contains
1242    ///   weak or semi-weak DES subkeys, or (for Triple DES) a degenerate subkey
1243    ///   configuration (e.g. `K1 == K2` for 2TDEA, or any pairwise equality
1244    ///   for 3TDEA).
1245    pub fn cbc(key: UnboundCipherKey) -> Result<DecryptingKey, Unspecified> {
1246        Self::new(key, OperatingMode::CBC)
1247    }
1248
1249    /// Constructs an `DecryptingKey` operating in electronic code book (ECB) mode using the provided key.
1250    ///
1251    /// # โ˜ ๏ธ ๏ธ๏ธ๏ธDANGER โ˜ ๏ธ
1252    /// Offered for compatibility purposes only. This is an extremely dangerous mode, and
1253    /// very likely not what you want to use.
1254    ///
1255    // # FIPS
1256    // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms:
1257    // * `AES_128`
1258    // * `AES_256`
1259    //
1260    // `DES_FOR_LEGACY_USE_ONLY`, `DES_EDE_FOR_LEGACY_USE_ONLY` and
1261    // `DES_EDE3_FOR_LEGACY_USE_ONLY` are never FIPS-approved.
1262    //
1263    /// # Errors
1264    /// * [`Unspecified`]: Returned if there is an error constructing the `DecryptingKey`.
1265    ///   With `legacy-des` enabled, also returned if `key` was constructed with
1266    ///   `DES_FOR_LEGACY_USE_ONLY`, `DES_EDE_FOR_LEGACY_USE_ONLY` or
1267    ///   `DES_EDE3_FOR_LEGACY_USE_ONLY` and the provided key material contains
1268    ///   weak or semi-weak DES subkeys, or (for Triple DES) a degenerate subkey
1269    ///   configuration (e.g. `K1 == K2` for 2TDEA, or any pairwise equality
1270    ///   for 3TDEA).
1271    pub fn ecb(key: UnboundCipherKey) -> Result<Self, Unspecified> {
1272        Self::new(key, OperatingMode::ECB)
1273    }
1274
1275    #[allow(clippy::unnecessary_wraps)]
1276    fn new(key: UnboundCipherKey, mode: OperatingMode) -> Result<Self, Unspecified> {
1277        let algorithm = key.algorithm();
1278        if !algorithm.supports_mode(mode) {
1279            return Err(Unspecified);
1280        }
1281        let key = key.try_into()?;
1282        Ok(Self {
1283            algorithm,
1284            key,
1285            mode,
1286        })
1287    }
1288
1289    /// Returns the cipher algorithm.
1290    #[must_use]
1291    pub fn algorithm(&self) -> &Algorithm {
1292        self.algorithm
1293    }
1294
1295    /// Returns the cipher operating mode.
1296    #[must_use]
1297    pub fn mode(&self) -> OperatingMode {
1298        self.mode
1299    }
1300
1301    /// Decrypts the data provided in `in_out` in-place.
1302    /// Returns a references to the decrypted data.
1303    ///
1304    /// If `DecryptingKey` is operating in `OperatingMode::ECB`, then `in_out.len()` must be a multiple
1305    /// of the block length.
1306    ///
1307    /// # Errors
1308    /// * [`Unspecified`]: Returned if cipher mode requires input to be a multiple of the block length,
1309    ///   and `in_out.len()` is not. Also returned if `context` is not valid for this key's
1310    ///   algorithm and mode, or if decryption fails.
1311    pub fn decrypt<'in_out>(
1312        &self,
1313        in_out: &'in_out mut [u8],
1314        context: DecryptionContext,
1315    ) -> Result<&'in_out mut [u8], Unspecified> {
1316        if !self
1317            .algorithm()
1318            .is_valid_decryption_context(self.mode, &context)
1319        {
1320            return Err(Unspecified);
1321        }
1322        decrypt(self.algorithm, &self.key, self.mode, in_out, context)
1323    }
1324}
1325
1326impl Debug for DecryptingKey {
1327    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1328        f.debug_struct("DecryptingKey")
1329            .field("algorithm", &self.algorithm)
1330            .field("mode", &self.mode)
1331            .finish_non_exhaustive()
1332    }
1333}
1334
1335fn encrypt(
1336    algorithm: &Algorithm,
1337    key: &SymmetricCipherKey,
1338    mode: OperatingMode,
1339    in_out: &mut [u8],
1340    context: EncryptionContext,
1341) -> Result<DecryptionContext, Unspecified> {
1342    let block_len = algorithm.block_len();
1343
1344    match mode {
1345        OperatingMode::CBC | OperatingMode::ECB if in_out.len() % block_len != 0 => {
1346            return Err(Unspecified);
1347        }
1348        _ => {}
1349    }
1350
1351    match mode {
1352        OperatingMode::CBC => match algorithm.id() {
1353            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
1354                aes::encrypt_cbc_mode(key, context, in_out)
1355            }
1356            #[cfg(feature = "legacy-des")]
1357            #[allow(deprecated)]
1358            AlgorithmId::DesForLegacyUseOnly
1359            | AlgorithmId::DesEdeForLegacyUseOnly
1360            | AlgorithmId::DesEde3ForLegacyUseOnly => des::encrypt_cbc_mode(key, context, in_out),
1361        },
1362        OperatingMode::CTR => match algorithm.id() {
1363            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
1364                aes::encrypt_ctr_mode(key, context, in_out)
1365            }
1366            #[cfg(feature = "legacy-des")]
1367            #[allow(deprecated)]
1368            AlgorithmId::DesForLegacyUseOnly
1369            | AlgorithmId::DesEdeForLegacyUseOnly
1370            | AlgorithmId::DesEde3ForLegacyUseOnly => {
1371                unreachable!("DES does not support CTR mode.")
1372            }
1373        },
1374        OperatingMode::CFB128 | OperatingMode::CFB8 | OperatingMode::CFB1 => match algorithm.id() {
1375            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
1376                aes::encrypt_cfb_mode(key, mode, context, in_out)
1377            }
1378            #[cfg(feature = "legacy-des")]
1379            #[allow(deprecated)]
1380            AlgorithmId::DesForLegacyUseOnly
1381            | AlgorithmId::DesEdeForLegacyUseOnly
1382            | AlgorithmId::DesEde3ForLegacyUseOnly => {
1383                unreachable!("DES does not support CFB modes.")
1384            }
1385        },
1386        OperatingMode::OFB => match algorithm.id() {
1387            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
1388                aes::encrypt_ofb_mode(key, context, in_out)
1389            }
1390            #[cfg(feature = "legacy-des")]
1391            #[allow(deprecated)]
1392            AlgorithmId::DesForLegacyUseOnly
1393            | AlgorithmId::DesEdeForLegacyUseOnly
1394            | AlgorithmId::DesEde3ForLegacyUseOnly => {
1395                unreachable!("DES does not support OFB mode.")
1396            }
1397        },
1398        OperatingMode::ECB => match algorithm.id() {
1399            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
1400                aes::encrypt_ecb_mode(key, context, in_out)
1401            }
1402            #[cfg(feature = "legacy-des")]
1403            #[allow(deprecated)]
1404            AlgorithmId::DesForLegacyUseOnly
1405            | AlgorithmId::DesEdeForLegacyUseOnly
1406            | AlgorithmId::DesEde3ForLegacyUseOnly => des::encrypt_ecb_mode(key, context, in_out),
1407        },
1408    }
1409}
1410
1411fn decrypt<'in_out>(
1412    algorithm: &'static Algorithm,
1413    key: &SymmetricCipherKey,
1414    mode: OperatingMode,
1415    in_out: &'in_out mut [u8],
1416    context: DecryptionContext,
1417) -> Result<&'in_out mut [u8], Unspecified> {
1418    let block_len = algorithm.block_len();
1419
1420    match mode {
1421        OperatingMode::CBC | OperatingMode::ECB if in_out.len() % block_len != 0 => {
1422            return Err(Unspecified);
1423        }
1424        _ => {}
1425    }
1426
1427    match mode {
1428        OperatingMode::CBC => match algorithm.id() {
1429            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
1430                aes::decrypt_cbc_mode(key, context, in_out)
1431            }
1432            #[cfg(feature = "legacy-des")]
1433            #[allow(deprecated)]
1434            AlgorithmId::DesForLegacyUseOnly
1435            | AlgorithmId::DesEdeForLegacyUseOnly
1436            | AlgorithmId::DesEde3ForLegacyUseOnly => des::decrypt_cbc_mode(key, context, in_out),
1437        },
1438        OperatingMode::CTR => match algorithm.id() {
1439            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
1440                aes::decrypt_ctr_mode(key, context, in_out)
1441            }
1442            #[cfg(feature = "legacy-des")]
1443            #[allow(deprecated)]
1444            AlgorithmId::DesForLegacyUseOnly
1445            | AlgorithmId::DesEdeForLegacyUseOnly
1446            | AlgorithmId::DesEde3ForLegacyUseOnly => {
1447                unreachable!("DES does not support CTR mode.")
1448            }
1449        },
1450        OperatingMode::CFB128 | OperatingMode::CFB8 | OperatingMode::CFB1 => match algorithm.id() {
1451            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
1452                aes::decrypt_cfb_mode(key, mode, context, in_out)
1453            }
1454            #[cfg(feature = "legacy-des")]
1455            #[allow(deprecated)]
1456            AlgorithmId::DesForLegacyUseOnly
1457            | AlgorithmId::DesEdeForLegacyUseOnly
1458            | AlgorithmId::DesEde3ForLegacyUseOnly => {
1459                unreachable!("DES does not support CFB modes.")
1460            }
1461        },
1462        OperatingMode::OFB => match algorithm.id() {
1463            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
1464                aes::decrypt_ofb_mode(key, context, in_out)
1465            }
1466            #[cfg(feature = "legacy-des")]
1467            #[allow(deprecated)]
1468            AlgorithmId::DesForLegacyUseOnly
1469            | AlgorithmId::DesEdeForLegacyUseOnly
1470            | AlgorithmId::DesEde3ForLegacyUseOnly => {
1471                unreachable!("DES does not support OFB mode.")
1472            }
1473        },
1474        OperatingMode::ECB => match algorithm.id() {
1475            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
1476                aes::decrypt_ecb_mode(key, context, in_out)
1477            }
1478            #[cfg(feature = "legacy-des")]
1479            #[allow(deprecated)]
1480            AlgorithmId::DesForLegacyUseOnly
1481            | AlgorithmId::DesEdeForLegacyUseOnly
1482            | AlgorithmId::DesEde3ForLegacyUseOnly => des::decrypt_ecb_mode(key, context, in_out),
1483        },
1484    }
1485}
1486
1487#[cfg(test)]
1488mod tests {
1489    use super::*;
1490    use crate::test::from_hex;
1491
1492    #[cfg(feature = "fips")]
1493    mod fips;
1494
1495    #[test]
1496    fn test_debug() {
1497        {
1498            let aes_128_key_bytes = from_hex("000102030405060708090a0b0c0d0e0f").unwrap();
1499            let cipher_key = UnboundCipherKey::new(&AES_128, aes_128_key_bytes.as_slice()).unwrap();
1500            assert_eq!("UnboundCipherKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 } }", format!("{cipher_key:?}"));
1501        }
1502
1503        {
1504            let aes_256_key_bytes =
1505                from_hex("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f")
1506                    .unwrap();
1507            let cipher_key = UnboundCipherKey::new(&AES_256, aes_256_key_bytes.as_slice()).unwrap();
1508            assert_eq!("UnboundCipherKey { algorithm: Algorithm { id: Aes256, key_len: 32, block_len: 16 } }", format!("{cipher_key:?}"));
1509        }
1510
1511        {
1512            let key_bytes = &[0u8; 16];
1513            let key = PaddedBlockEncryptingKey::cbc_pkcs7(
1514                UnboundCipherKey::new(&AES_128, key_bytes).unwrap(),
1515            )
1516            .unwrap();
1517            assert_eq!("PaddedBlockEncryptingKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 }, mode: CBC, padding: PKCS7, .. }", format!("{key:?}"));
1518            let mut data = vec![0u8; 16];
1519            let context = key.encrypt(&mut data).unwrap();
1520            assert_eq!("Iv128", format!("{context:?}"));
1521            let key = PaddedBlockDecryptingKey::cbc_pkcs7(
1522                UnboundCipherKey::new(&AES_128, key_bytes).unwrap(),
1523            )
1524            .unwrap();
1525            assert_eq!("PaddedBlockDecryptingKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 }, mode: CBC, padding: PKCS7, .. }", format!("{key:?}"));
1526        }
1527
1528        {
1529            let key_bytes = &[0u8; 16];
1530            let key =
1531                EncryptingKey::ctr(UnboundCipherKey::new(&AES_128, key_bytes).unwrap()).unwrap();
1532            assert_eq!("EncryptingKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 }, mode: CTR, .. }", format!("{key:?}"));
1533            let mut data = vec![0u8; 16];
1534            let context = key.encrypt(&mut data).unwrap();
1535            assert_eq!("Iv128", format!("{context:?}"));
1536            let key =
1537                DecryptingKey::ctr(UnboundCipherKey::new(&AES_128, key_bytes).unwrap()).unwrap();
1538            assert_eq!("DecryptingKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 }, mode: CTR, .. }", format!("{key:?}"));
1539        }
1540    }
1541
1542    fn helper_test_cipher_n_bytes(
1543        key: &[u8],
1544        alg: &'static Algorithm,
1545        mode: OperatingMode,
1546        n: usize,
1547    ) {
1548        let mut input: Vec<u8> = Vec::with_capacity(n);
1549        for i in 0..n {
1550            let byte: u8 = i.try_into().unwrap();
1551            input.push(byte);
1552        }
1553
1554        let cipher_key = UnboundCipherKey::new(alg, key).unwrap();
1555        let encrypting_key = EncryptingKey::new(cipher_key, mode).unwrap();
1556
1557        let mut in_out = input.clone();
1558        let decrypt_iv = encrypting_key.encrypt(&mut in_out).unwrap();
1559
1560        if n > 5 {
1561            // There's no more than a 1 in 2^48 chance that this will fail randomly
1562            assert_ne!(input.as_slice(), in_out);
1563        }
1564
1565        let cipher_key2 = UnboundCipherKey::new(alg, key).unwrap();
1566        let decrypting_key = DecryptingKey::new(cipher_key2, mode).unwrap();
1567
1568        let plaintext = decrypting_key.decrypt(&mut in_out, decrypt_iv).unwrap();
1569        assert_eq!(input.as_slice(), plaintext);
1570    }
1571
1572    #[test]
1573    fn test_aes_128_ctr() {
1574        let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap();
1575        for i in 0..=50 {
1576            helper_test_cipher_n_bytes(key.as_slice(), &AES_128, OperatingMode::CTR, i);
1577        }
1578    }
1579
1580    #[test]
1581    fn test_aes_128_cfb128() {
1582        let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap();
1583        for i in 0..=50 {
1584            helper_test_cipher_n_bytes(key.as_slice(), &AES_128, OperatingMode::CFB128, i);
1585        }
1586    }
1587
1588    #[test]
1589    fn test_aes_256_cfb128() {
1590        let key =
1591            from_hex("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f").unwrap();
1592        for i in 0..=50 {
1593            helper_test_cipher_n_bytes(key.as_slice(), &AES_256, OperatingMode::CFB128, i);
1594        }
1595    }
1596
1597    #[test]
1598    fn test_aes_256_ctr() {
1599        let key =
1600            from_hex("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f").unwrap();
1601        for i in 0..=50 {
1602            helper_test_cipher_n_bytes(key.as_slice(), &AES_256, OperatingMode::CTR, i);
1603        }
1604    }
1605
1606    #[test]
1607    fn test_aes_128_cbc() {
1608        let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap();
1609        // CBC mode requires input to be a multiple of block size (16 bytes)
1610        for i in 0..=3 {
1611            let size = i * 16; // Test with 0, 16, 32, 48 bytes
1612            helper_test_cipher_n_bytes(key.as_slice(), &AES_128, OperatingMode::CBC, size);
1613        }
1614    }
1615
1616    #[test]
1617    fn test_aes_256_cbc() {
1618        let key =
1619            from_hex("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f").unwrap();
1620        // CBC mode requires input to be a multiple of block size (16 bytes)
1621        for i in 0..=3 {
1622            let size = i * 16; // Test with 0, 16, 32, 48 bytes
1623            helper_test_cipher_n_bytes(key.as_slice(), &AES_256, OperatingMode::CBC, size);
1624        }
1625    }
1626
1627    #[test]
1628    fn test_aes_128_ecb() {
1629        let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap();
1630        _ = key;
1631    }
1632
1633    macro_rules! cipher_kat {
1634        ($name:ident, $alg:expr, $mode:expr, $key:literal, $iv: literal, $plaintext:literal, $ciphertext:literal) => {
1635            #[test]
1636            fn $name() {
1637                let key = from_hex($key).unwrap();
1638                let input = from_hex($plaintext).unwrap();
1639                let expected_ciphertext = from_hex($ciphertext).unwrap();
1640                let mut iv = from_hex($iv).unwrap();
1641                let iv = {
1642                    let slice = iv.as_mut_slice();
1643                    let mut iv = [0u8; $iv.len() / 2];
1644                    {
1645                        let x = iv.as_mut_slice();
1646                        x.copy_from_slice(slice);
1647                    }
1648                    iv
1649                };
1650
1651                let ec = EncryptionContext::Iv128(FixedLength::from(iv));
1652
1653                let alg = $alg;
1654
1655                let unbound_key = UnboundCipherKey::new(alg, &key).unwrap();
1656
1657                let encrypting_key = EncryptingKey::new(unbound_key, $mode).unwrap();
1658
1659                let mut in_out = input.clone();
1660
1661                let context = encrypting_key.less_safe_encrypt(&mut in_out, ec).unwrap();
1662
1663                assert_eq!(expected_ciphertext, in_out);
1664
1665                let unbound_key2 = UnboundCipherKey::new(alg, &key).unwrap();
1666                let decrypting_key = DecryptingKey::new(unbound_key2, $mode).unwrap();
1667
1668                let plaintext = decrypting_key.decrypt(&mut in_out, context).unwrap();
1669                assert_eq!(input.as_slice(), plaintext);
1670            }
1671        };
1672        ($name:ident, $alg:expr, $mode:expr, $key:literal, $plaintext:literal, $ciphertext:literal) => {
1673            #[test]
1674            fn $name() {
1675                let key = from_hex($key).unwrap();
1676                let input = from_hex($plaintext).unwrap();
1677                let expected_ciphertext = from_hex($ciphertext).unwrap();
1678
1679                let alg = $alg;
1680
1681                let unbound_key = UnboundCipherKey::new(alg, &key).unwrap();
1682
1683                let encrypting_key = EncryptingKey::new(unbound_key, $mode).unwrap();
1684
1685                let mut in_out = input.clone();
1686
1687                let context = encrypting_key
1688                    .less_safe_encrypt(&mut in_out, EncryptionContext::None)
1689                    .unwrap();
1690
1691                assert_eq!(expected_ciphertext, in_out);
1692
1693                let unbound_key2 = UnboundCipherKey::new(alg, &key).unwrap();
1694                let decrypting_key = DecryptingKey::new(unbound_key2, $mode).unwrap();
1695
1696                let plaintext = decrypting_key.decrypt(&mut in_out, context).unwrap();
1697                assert_eq!(input.as_slice(), plaintext);
1698            }
1699        };
1700    }
1701
1702    cipher_kat!(
1703        test_iv_aes_128_ctr_16_bytes,
1704        &AES_128,
1705        OperatingMode::CTR,
1706        "000102030405060708090a0b0c0d0e0f",
1707        "00000000000000000000000000000000",
1708        "00112233445566778899aabbccddeeff",
1709        "c6b01904c3da3df5e7d62bd96d153686"
1710    );
1711
1712    cipher_kat!(
1713        test_iv_aes_256_ctr_15_bytes,
1714        &AES_256,
1715        OperatingMode::CTR,
1716        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
1717        "00000000000000000000000000000000",
1718        "00112233445566778899aabbccddee",
1719        "f28122856e1cf9a7216a30d111f399"
1720    );
1721
1722    cipher_kat!(
1723        test_openssl_aes_128_ctr_15_bytes,
1724        &AES_128,
1725        OperatingMode::CTR,
1726        "244828580821c1652582c76e34d299f5",
1727        "093145d5af233f46072a5eb5adc11aa1",
1728        "3ee38cec171e6cf466bf0df98aa0e1",
1729        "bd7d928f60e3422d96b3f8cd614eb2"
1730    );
1731
1732    cipher_kat!(
1733        test_openssl_aes_256_ctr_15_bytes,
1734        &AES_256,
1735        OperatingMode::CTR,
1736        "0857db8240ea459bdf660b4cced66d1f2d3734ff2de7b81e92740e65e7cc6a1d",
1737        "f028ecb053f801102d11fccc9d303a27",
1738        "eca7285d19f3c20e295378460e8729",
1739        "b5098e5e788de6ac2f2098eb2fc6f8"
1740    );
1741
1742    cipher_kat!(
1743        test_sp800_38a_cfb128_aes128,
1744        &AES_128,
1745        OperatingMode::CFB128,
1746        "2b7e151628aed2a6abf7158809cf4f3c",
1747        "000102030405060708090a0b0c0d0e0f",
1748        "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
1749        "3b3fd92eb72dad20333449f8e83cfb4ac8a64537a0b3a93fcde3cdad9f1ce58b26751f67a3cbb140b1808cf187a4f4dfc04b05357c5d1c0eeac4c66f9ff7f2e6"
1750    );
1751
1752    cipher_kat!(
1753        test_sp800_38a_cfb128_aes256,
1754        &AES_256,
1755        OperatingMode::CFB128,
1756        "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
1757        "000102030405060708090a0b0c0d0e0f",
1758        "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
1759        "dc7e84bfda79164b7ecd8486985d386039ffed143b28b1c832113c6331e5407bdf10132415e54b92a13ed0a8267ae2f975a385741ab9cef82031623d55b1e471"
1760    );
1761
1762    cipher_kat!(
1763        test_sp800_38a_ecb_aes128,
1764        &AES_128,
1765        OperatingMode::ECB,
1766        "2b7e151628aed2a6abf7158809cf4f3c",
1767        "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
1768        "3ad77bb40d7a3660a89ecaf32466ef97f5d3d58503b9699de785895a96fdbaaf43b1cd7f598ece23881b00e3ed0306887b0c785e27e8ad3f8223207104725dd4"
1769    );
1770
1771    cipher_kat!(
1772        test_sp800_38a_ecb_aes256,
1773        &AES_256,
1774        OperatingMode::ECB,
1775        "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
1776        "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
1777        "f3eed1bdb5d2a03c064b5a7e3db181f8591ccb10d410ed26dc5ba74a31362870b6ed21b99ca6f4f9f153e7b1beafed1d23304b7a39f9f3ff067d8d8f9e24ecc7"
1778    );
1779
1780    cipher_kat!(
1781        test_sp800_38a_cbc_aes128,
1782        &AES_128,
1783        OperatingMode::CBC,
1784        "2b7e151628aed2a6abf7158809cf4f3c",
1785        "000102030405060708090a0b0c0d0e0f",
1786        "6bc1bee22e409f96e93d7e117393172a",
1787        "7649abac8119b246cee98e9b12e9197d"
1788    );
1789
1790    cipher_kat!(
1791        test_sp800_38a_cbc_aes256,
1792        &AES_256,
1793        OperatingMode::CBC,
1794        "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
1795        "000102030405060708090a0b0c0d0e0f",
1796        "6bc1bee22e409f96e93d7e117393172a",
1797        "f58c4c04d6e5f1ba779eabfb5f7bfbd6"
1798    );
1799
1800    cipher_kat!(
1801        test_sp800_38a_cbc_aes128_multi_block,
1802        &AES_128,
1803        OperatingMode::CBC,
1804        "2b7e151628aed2a6abf7158809cf4f3c",
1805        "000102030405060708090a0b0c0d0e0f",
1806        "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
1807        "7649abac8119b246cee98e9b12e9197d5086cb9b507219ee95db113a917678b273bed6b8e3c1743b7116e69e222295163ff1caa1681fac09120eca307586e1a7"
1808    );
1809
1810    cipher_kat!(
1811        test_sp800_38a_cbc_aes256_multi_block,
1812        &AES_256,
1813        OperatingMode::CBC,
1814        "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
1815        "000102030405060708090a0b0c0d0e0f",
1816        "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
1817        "f58c4c04d6e5f1ba779eabfb5f7bfbd69cfc4e967edb808d679f777bc6702c7d39f23369a9d9bacfa530e26304231461b2eb05e2c39be9fcda6c19078c6a9d1b"
1818    );
1819
1820    // NIST SP 800-38A, Appendix F
1821    // <https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf>
1822
1823    // Source: F.4.1
1824    cipher_kat!(
1825        test_sp800_38a_ofb_aes128,
1826        &AES_128,
1827        OperatingMode::OFB,
1828        "2b7e151628aed2a6abf7158809cf4f3c",
1829        "000102030405060708090a0b0c0d0e0f",
1830        "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
1831        "3b3fd92eb72dad20333449f8e83cfb4a7789508d16918f03f53c52dac54ed8259740051e9c5fecf64344f7a82260edcc304c6528f659c77866a510d9c1d6ae5e"
1832    );
1833
1834    // Source: F.4.3
1835    cipher_kat!(
1836        test_sp800_38a_ofb_aes192,
1837        &AES_192,
1838        OperatingMode::OFB,
1839        "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b",
1840        "000102030405060708090a0b0c0d0e0f",
1841        "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
1842        "cdc80d6fddf18cab34c25909c99a4174fcc28b8d4c63837c09e81700c11004018d9a9aeac0f6596f559c6d4daf59a5f26d9f200857ca6c3e9cac524bd9acc92a"
1843    );
1844
1845    // Source: F.4.5
1846    cipher_kat!(
1847        test_sp800_38a_ofb_aes256,
1848        &AES_256,
1849        OperatingMode::OFB,
1850        "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
1851        "000102030405060708090a0b0c0d0e0f",
1852        "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
1853        "dc7e84bfda79164b7ecd8486985d38604febdc6740d20b3ac88f6ad82a4fb08d71ab47a086e86eedf39d1c5bba97c4080126141d67f37be8538f5a8be740e484"
1854    );
1855
1856    // Source: F.3.7, via AWS-LC cfb_test.cc
1857    cipher_kat!(
1858        test_sp800_38a_cfb8_aes128,
1859        &AES_128,
1860        OperatingMode::CFB8,
1861        "2b7e151628aed2a6abf7158809cf4f3c",
1862        "000102030405060708090a0b0c0d0e0f",
1863        "6bc1bee22e409f96e93d7e117393172aae2d",
1864        "3b79424c9c0dd436bace9e0ed4586a4f32b9"
1865    );
1866
1867    // Source: F.3.9, via AWS-LC cfb_test.cc
1868    cipher_kat!(
1869        test_sp800_38a_cfb8_aes192,
1870        &AES_192,
1871        OperatingMode::CFB8,
1872        "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b",
1873        "000102030405060708090a0b0c0d0e0f",
1874        "6bc1bee22e409f96e93d7e117393172aae2d",
1875        "cda2521ef0a905ca44cd057cbf0d47a0678a"
1876    );
1877
1878    // Source: F.3.11, via AWS-LC cfb_test.cc
1879    cipher_kat!(
1880        test_sp800_38a_cfb8_aes256,
1881        &AES_256,
1882        OperatingMode::CFB8,
1883        "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
1884        "000102030405060708090a0b0c0d0e0f",
1885        "6bc1bee22e409f96e93d7e117393172aae2d",
1886        "dc1f1a8520a64db55fcc8ac554844e889700"
1887    );
1888
1889    // Source: F.3.1, via AWS-LC cfb_test.cc
1890    cipher_kat!(
1891        test_sp800_38a_cfb1_aes128,
1892        &AES_128,
1893        OperatingMode::CFB1,
1894        "2b7e151628aed2a6abf7158809cf4f3c",
1895        "000102030405060708090a0b0c0d0e0f",
1896        "6bc1",
1897        "68b3"
1898    );
1899
1900    // Source: F.3.3, via AWS-LC cfb_test.cc
1901    cipher_kat!(
1902        test_sp800_38a_cfb1_aes192,
1903        &AES_192,
1904        OperatingMode::CFB1,
1905        "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b",
1906        "000102030405060708090a0b0c0d0e0f",
1907        "6bc1",
1908        "9359"
1909    );
1910
1911    // Source: F.3.5, via AWS-LC cfb_test.cc
1912    cipher_kat!(
1913        test_sp800_38a_cfb1_aes256,
1914        &AES_256,
1915        OperatingMode::CFB1,
1916        "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
1917        "000102030405060708090a0b0c0d0e0f",
1918        "6bc1",
1919        "9029"
1920    );
1921
1922    #[cfg(feature = "legacy-des")]
1923    #[test]
1924    #[allow(deprecated)]
1925    fn test_des_rejects_weak_key() {
1926        let weak_keys: [&str; 4] = [
1927            "0101010101010101",
1928            "fefefefefefefefe",
1929            "e0e0e0e0f1f1f1f1",
1930            "1f1f1f1f0e0e0e0e",
1931        ];
1932        for weak in &weak_keys {
1933            let key = from_hex(weak).unwrap();
1934            assert!(
1935                UnboundCipherKey::new(&DES_FOR_LEGACY_USE_ONLY, &key)
1936                    .and_then(EncryptingKey::cbc)
1937                    .is_err(),
1938                "expected weak DES key {weak} to be rejected"
1939            );
1940        }
1941    }
1942
1943    #[cfg(feature = "legacy-des")]
1944    #[test]
1945    #[allow(deprecated)]
1946    fn test_des_rejects_semi_weak_key() {
1947        let semi_weak_keys: [&str; 12] = [
1948            "01fe01fe01fe01fe",
1949            "fe01fe01fe01fe01",
1950            "1fe01fe00ef10ef1",
1951            "e01fe01ff10ef10e",
1952            "01e001e001f101f1",
1953            "e001e001f101f101",
1954            "1ffe1ffe0efe0efe",
1955            "fe1ffe1ffe0efe0e",
1956            "011f011f010e010e",
1957            "1f011f010e010e01",
1958            "e0fee0fef1fef1fe",
1959            "fee0fee0fef1fef1",
1960        ];
1961        for semi_weak in &semi_weak_keys {
1962            let key = from_hex(semi_weak).unwrap();
1963            assert!(
1964                UnboundCipherKey::new(&DES_FOR_LEGACY_USE_ONLY, &key)
1965                    .and_then(EncryptingKey::cbc)
1966                    .is_err(),
1967                "expected semi-weak DES key {semi_weak} to be rejected"
1968            );
1969        }
1970    }
1971
1972    #[cfg(feature = "legacy-des")]
1973    #[test]
1974    #[allow(deprecated)]
1975    fn test_des_ede_rejects_k1_equal_k2() {
1976        // SP 800-67 ยง3.1 requires K1 != K2 for 2-Key TDEA. If K1 == K2 then
1977        // 2TDEA collapses to single-DES (56-bit effective security).
1978        let weak_key = from_hex("0123456789abcdef0123456789abcdef").unwrap();
1979        let result = UnboundCipherKey::new(&DES_EDE_FOR_LEGACY_USE_ONLY, &weak_key)
1980            .and_then(EncryptingKey::cbc);
1981        assert!(result.is_err(), "expected K1 == K2 to be rejected");
1982
1983        // Sanity check: differing K1 and K2 of the same length is accepted.
1984        let good_key = from_hex("0123456789abcdef23456789abcdef01").unwrap();
1985        UnboundCipherKey::new(&DES_EDE_FOR_LEGACY_USE_ONLY, &good_key)
1986            .and_then(EncryptingKey::cbc)
1987            .expect("valid 2TDEA key should be accepted");
1988    }
1989
1990    #[cfg(feature = "legacy-des")]
1991    #[test]
1992    #[allow(deprecated)]
1993    fn test_des_ede_rejects_weak_subkey() {
1994        // Each of DES_set_key's four weak keys must be rejected when used as
1995        // either subkey of a 2TDEA key. The weak keys (with correct odd
1996        // parity) are 0101..01, FEFE..FE, E0E0..E0F1F1..F1, and
1997        // 1F1F..1F0E0E..0E; see openssl/des.h.
1998        let weak_keys: [&str; 4] = [
1999            "0101010101010101",
2000            "fefefefefefefefe",
2001            "e0e0e0e0f1f1f1f1",
2002            "1f1f1f1f0e0e0e0e",
2003        ];
2004        let distinct = "23456789abcdef01";
2005        for weak in &weak_keys {
2006            // Weak K1, non-weak K2.
2007            let key = from_hex(&format!("{weak}{distinct}")).unwrap();
2008            assert!(
2009                UnboundCipherKey::new(&DES_EDE_FOR_LEGACY_USE_ONLY, &key)
2010                    .and_then(EncryptingKey::cbc)
2011                    .is_err(),
2012                "expected weak DES K1 = {weak} to be rejected in 2TDEA"
2013            );
2014            // Non-weak K1, weak K2.
2015            let key = from_hex(&format!("{distinct}{weak}")).unwrap();
2016            assert!(
2017                UnboundCipherKey::new(&DES_EDE_FOR_LEGACY_USE_ONLY, &key)
2018                    .and_then(EncryptingKey::cbc)
2019                    .is_err(),
2020                "expected weak DES K2 = {weak} to be rejected in 2TDEA"
2021            );
2022        }
2023    }
2024
2025    #[cfg(feature = "legacy-des")]
2026    #[test]
2027    #[allow(deprecated)]
2028    fn test_des_ede3_rejects_degenerate_keys() {
2029        // SP 800-67 ยง2 (Keying Option 1) requires K1, K2, and K3 to be pairwise
2030        // independent. We enforce that all three subkeys are distinct so the cipher
2031        // cannot degenerate to single-DES (K1==K2 or K2==K3) or to 2TDEA (K1==K3).
2032        // Callers who want 2TDEA must use `DES_EDE_FOR_LEGACY_USE_ONLY` with a 16-byte key.
2033        let k1_eq_k2 = from_hex("0123456789abcdef0123456789abcdef456789abcdef0123").unwrap();
2034        assert!(
2035            UnboundCipherKey::new(&DES_EDE3_FOR_LEGACY_USE_ONLY, &k1_eq_k2)
2036                .and_then(EncryptingKey::cbc)
2037                .is_err(),
2038            "expected K1 == K2 to be rejected for 3TDEA"
2039        );
2040
2041        let k2_eq_k3 = from_hex("0123456789abcdef23456789abcdef0123456789abcdef01").unwrap();
2042        assert!(
2043            UnboundCipherKey::new(&DES_EDE3_FOR_LEGACY_USE_ONLY, &k2_eq_k3)
2044                .and_then(EncryptingKey::cbc)
2045                .is_err(),
2046            "expected K2 == K3 to be rejected for 3TDEA"
2047        );
2048
2049        let k1_eq_k3 = from_hex("0123456789abcdef23456789abcdef010123456789abcdef").unwrap();
2050        assert!(
2051            UnboundCipherKey::new(&DES_EDE3_FOR_LEGACY_USE_ONLY, &k1_eq_k3)
2052                .and_then(EncryptingKey::cbc)
2053                .is_err(),
2054            "expected K1 == K3 (2TDEA-in-3TDEA form) to be rejected for 3TDEA"
2055        );
2056
2057        // All three distinct is accepted.
2058        let good_key = from_hex("0123456789abcdef23456789abcdef01456789abcdef0123").unwrap();
2059        UnboundCipherKey::new(&DES_EDE3_FOR_LEGACY_USE_ONLY, &good_key)
2060            .and_then(EncryptingKey::cbc)
2061            .expect("valid 3TDEA key should be accepted");
2062    }
2063
2064    #[cfg(feature = "legacy-des")]
2065    #[test]
2066    #[allow(deprecated)]
2067    fn test_des_ede3_rejects_weak_subkey() {
2068        // Each of `DES_set_key`'s four weak keys must be rejected when used
2069        // as any subkey of a 3TDEA key. The weak keys (with correct odd
2070        // parity) are 0101..01, FEFE..FE, E0E0..E0F1F1..F1, and
2071        // 1F1F..1F0E0E..0E; see openssl/des.h.
2072        let weak_keys: [&str; 4] = [
2073            "0101010101010101",
2074            "fefefefefefefefe",
2075            "e0e0e0e0f1f1f1f1",
2076            "1f1f1f1f0e0e0e0e",
2077        ];
2078        let fillers = ["23456789abcdef01", "456789abcdef0123"];
2079
2080        for weak in &weak_keys {
2081            for position in 0..3 {
2082                // Place the weak key at `position` and fill the other two
2083                // slots with distinct non-weak subkeys so that the K1 != K2
2084                // and K2 != K3 checks do not short-circuit the weak-key
2085                // rejection we are trying to exercise.
2086                let mut parts = [""; 3];
2087                parts[position] = *weak;
2088                parts[(position + 1) % 3] = fillers[0];
2089                parts[(position + 2) % 3] = fillers[1];
2090                let key = from_hex(&format!("{}{}{}", parts[0], parts[1], parts[2])).unwrap();
2091                assert!(
2092                    UnboundCipherKey::new(&DES_EDE3_FOR_LEGACY_USE_ONLY, &key)
2093                        .and_then(EncryptingKey::cbc)
2094                        .is_err(),
2095                    "expected weak DES subkey {weak} at position {position} to be rejected"
2096                );
2097            }
2098        }
2099    }
2100
2101    #[cfg(feature = "legacy-des")]
2102    #[test]
2103    #[allow(deprecated)]
2104    fn test_des_ede_rejects_semi_weak_subkey() {
2105        // DES_set_key also rejects semi-weak keys (return value -2).
2106        // There are 6 semi-weak key pairs (12 keys total); each member of a
2107        // pair encrypts what the other decrypts, making the cipher an
2108        // involuntary self-inverse. The semi-weak keys are listed in
2109        // openssl/des.h. We verify that using any one of them as either
2110        // subkey of a 2TDEA key is rejected.
2111        // The 12 DES semi-weak keys (6 pairs); each key in a pair is its own
2112        // partner's inverse. These are exactly the keys for which DES_set_key
2113        // returns -2 (along with the 4 weak keys already tested separately).
2114        // Values taken from OpenSSL/AWS-LC des.h.
2115        let semi_weak_keys: [&str; 12] = [
2116            "01fe01fe01fe01fe",
2117            "fe01fe01fe01fe01",
2118            "1fe01fe00ef10ef1",
2119            "e01fe01ff10ef10e",
2120            "01e001e001f101f1",
2121            "e001e001f101f101",
2122            "1ffe1ffe0efe0efe",
2123            "fe1ffe1ffe0efe0e",
2124            "011f011f010e010e",
2125            "1f011f010e010e01",
2126            "e0fee0fef1fef1fe",
2127            "fee0fee0fef1fef1",
2128        ];
2129        let distinct = "0123456789abcdef";
2130        for semi_weak in &semi_weak_keys {
2131            let key = from_hex(&format!("{semi_weak}{distinct}")).unwrap();
2132            assert!(
2133                UnboundCipherKey::new(&DES_EDE_FOR_LEGACY_USE_ONLY, &key)
2134                    .and_then(EncryptingKey::cbc)
2135                    .is_err(),
2136                "expected semi-weak DES K1 = {semi_weak} to be rejected in 2TDEA"
2137            );
2138            let key = from_hex(&format!("{distinct}{semi_weak}")).unwrap();
2139            assert!(
2140                UnboundCipherKey::new(&DES_EDE_FOR_LEGACY_USE_ONLY, &key)
2141                    .and_then(EncryptingKey::cbc)
2142                    .is_err(),
2143                "expected semi-weak DES K2 = {semi_weak} to be rejected in 2TDEA"
2144            );
2145        }
2146    }
2147
2148    #[cfg(feature = "legacy-des")]
2149    #[test]
2150    #[allow(deprecated)]
2151    fn test_des_ede3_rejects_semi_weak_subkey() {
2152        // Same 12 semi-weak keys as above; verify rejection at each of the
2153        // three subkey positions in a 3TDEA key.
2154        let semi_weak_keys: [&str; 12] = [
2155            "01fe01fe01fe01fe",
2156            "fe01fe01fe01fe01",
2157            "1fe01fe00ef10ef1",
2158            "e01fe01ff10ef10e",
2159            "01e001e001f101f1",
2160            "e001e001f101f101",
2161            "1ffe1ffe0efe0efe",
2162            "fe1ffe1ffe0efe0e",
2163            "011f011f010e010e",
2164            "1f011f010e010e01",
2165            "e0fee0fef1fef1fe",
2166            "fee0fee0fef1fef1",
2167        ];
2168        let fillers = ["0123456789abcdef", "23456789abcdef01"];
2169
2170        for semi_weak in &semi_weak_keys {
2171            for position in 0..3 {
2172                let mut parts = [""; 3];
2173                parts[position] = *semi_weak;
2174                parts[(position + 1) % 3] = fillers[0];
2175                parts[(position + 2) % 3] = fillers[1];
2176                let key = from_hex(&format!("{}{}{}", parts[0], parts[1], parts[2])).unwrap();
2177                assert!(
2178                    UnboundCipherKey::new(&DES_EDE3_FOR_LEGACY_USE_ONLY, &key)
2179                        .and_then(EncryptingKey::cbc)
2180                        .is_err(),
2181                    "expected semi-weak DES subkey {semi_weak} at position {position} to be rejected in 3TDEA"
2182                );
2183            }
2184        }
2185    }
2186
2187    #[test]
2188    fn test_decrypting_key_rejects_invalid_context() {
2189        type Constructor = fn(UnboundCipherKey) -> Result<DecryptingKey, Unspecified>;
2190        let key = || UnboundCipherKey::new(&AES_128, &[0u8; 16]).unwrap();
2191        let iv_modes: [(&str, Constructor); 6] = [
2192            ("CBC", DecryptingKey::cbc),
2193            ("CTR", DecryptingKey::ctr),
2194            ("CFB128", DecryptingKey::cfb128),
2195            ("CFB8", DecryptingKey::cfb8),
2196            ("CFB1", DecryptingKey::cfb1),
2197            ("OFB", DecryptingKey::ofb),
2198        ];
2199        for (name, constructor) in iv_modes {
2200            let decrypting_key = constructor(key()).unwrap();
2201            let mut in_out = [0u8; 16];
2202            assert!(
2203                decrypting_key
2204                    .decrypt(&mut in_out, DecryptionContext::None)
2205                    .is_err(),
2206                "AES + {name} + DecryptionContext::None should be rejected"
2207            );
2208            #[cfg(feature = "legacy-des")]
2209            assert!(
2210                decrypting_key
2211                    .decrypt(
2212                        &mut in_out,
2213                        DecryptionContext::Iv64(FixedLength::from([0u8; 8]))
2214                    )
2215                    .is_err(),
2216                "AES + {name} + DecryptionContext::Iv64 should be rejected"
2217            );
2218        }
2219
2220        let decrypting_key = DecryptingKey::ecb(key()).unwrap();
2221        assert!(
2222            decrypting_key
2223                .decrypt(
2224                    &mut [0u8; 16],
2225                    DecryptionContext::Iv128(FixedLength::from([0u8; 16]))
2226                )
2227                .is_err(),
2228            "AES + ECB + DecryptionContext::Iv128 should be rejected"
2229        );
2230    }
2231
2232    #[cfg(feature = "legacy-des")]
2233    #[test]
2234    #[allow(deprecated)]
2235    fn test_des_rejects_unsupported_modes() {
2236        // DES/2TDEA/3TDEA only support CBC and ECB in this crate; CTR, CFB128, CFB8,
2237        // CFB1 and OFB must be rejected at construction time rather than at encrypt time.
2238
2239        // Each constructor reports whether it accepted the key.
2240        type Constructor = fn(UnboundCipherKey) -> bool;
2241
2242        let algorithms: [(&str, &'static Algorithm, &str); 3] = [
2243            ("DES", &DES_FOR_LEGACY_USE_ONLY, "0123456789abcdef"),
2244            (
2245                "2TDEA",
2246                &DES_EDE_FOR_LEGACY_USE_ONLY,
2247                "0123456789abcdef23456789abcdef01",
2248            ),
2249            (
2250                "3TDEA",
2251                &DES_EDE3_FOR_LEGACY_USE_ONLY,
2252                "0123456789abcdef23456789abcdef01456789abcdef0123",
2253            ),
2254        ];
2255        let constructors: [(&str, Constructor); 23] = [
2256            ("CTR (encrypt)", |k| EncryptingKey::ctr(k).is_ok()),
2257            ("CFB128 (encrypt)", |k| EncryptingKey::cfb128(k).is_ok()),
2258            ("CTR (decrypt)", |k| DecryptingKey::ctr(k).is_ok()),
2259            ("CFB128 (decrypt)", |k| DecryptingKey::cfb128(k).is_ok()),
2260            ("CTR (streaming encrypt)", |k| {
2261                StreamingEncryptingKey::ctr(k).is_ok()
2262            }),
2263            ("CFB128 (streaming encrypt)", |k| {
2264                StreamingEncryptingKey::cfb128(k).is_ok()
2265            }),
2266            ("CTR (streaming decrypt)", |k| {
2267                StreamingDecryptingKey::ctr(k, DecryptionContext::None).is_ok()
2268            }),
2269            ("CFB128 (streaming decrypt)", |k| {
2270                StreamingDecryptingKey::cfb128(k, DecryptionContext::None).is_ok()
2271            }),
2272            ("CFB8 (encrypt)", |k| EncryptingKey::cfb8(k).is_ok()),
2273            ("CFB8 (decrypt)", |k| DecryptingKey::cfb8(k).is_ok()),
2274            ("CFB8 (streaming encrypt)", |k| {
2275                StreamingEncryptingKey::cfb8(k).is_ok()
2276            }),
2277            ("CFB8 (streaming encrypt, context)", |k| {
2278                StreamingEncryptingKey::less_safe_cfb8(k, EncryptionContext::None).is_ok()
2279            }),
2280            ("CFB8 (streaming decrypt)", |k| {
2281                StreamingDecryptingKey::cfb8(k, DecryptionContext::None).is_ok()
2282            }),
2283            ("CFB1 (encrypt)", |k| EncryptingKey::cfb1(k).is_ok()),
2284            ("CFB1 (decrypt)", |k| DecryptingKey::cfb1(k).is_ok()),
2285            ("CFB1 (streaming encrypt)", |k| {
2286                StreamingEncryptingKey::cfb1(k).is_ok()
2287            }),
2288            ("CFB1 (streaming encrypt, context)", |k| {
2289                StreamingEncryptingKey::less_safe_cfb1(k, EncryptionContext::None).is_ok()
2290            }),
2291            ("CFB1 (streaming decrypt)", |k| {
2292                StreamingDecryptingKey::cfb1(k, DecryptionContext::None).is_ok()
2293            }),
2294            ("OFB (encrypt)", |k| EncryptingKey::ofb(k).is_ok()),
2295            ("OFB (decrypt)", |k| DecryptingKey::ofb(k).is_ok()),
2296            ("OFB (streaming encrypt)", |k| {
2297                StreamingEncryptingKey::ofb(k).is_ok()
2298            }),
2299            ("OFB (streaming encrypt, context)", |k| {
2300                StreamingEncryptingKey::less_safe_ofb(k, EncryptionContext::None).is_ok()
2301            }),
2302            ("OFB (streaming decrypt)", |k| {
2303                StreamingDecryptingKey::ofb(k, DecryptionContext::None).is_ok()
2304            }),
2305        ];
2306
2307        for (alg_name, algorithm, key_hex) in algorithms {
2308            let key_bytes = from_hex(key_hex).unwrap();
2309            for (mode_name, construct) in constructors {
2310                // `UnboundCipherKey::new` only checks the key length, so the
2311                // rejection below must come from the mode check.
2312                let key = UnboundCipherKey::new(algorithm, &key_bytes).unwrap();
2313                assert!(
2314                    !construct(key),
2315                    "{alg_name} + {mode_name} should be rejected"
2316                );
2317            }
2318        }
2319    }
2320
2321    #[cfg(feature = "legacy-des")]
2322    #[test]
2323    #[allow(deprecated)]
2324    fn test_des_cbc() {
2325        let key = from_hex("0123456789abcdef").unwrap();
2326        for i in 0..=3 {
2327            let size = i * 8;
2328            helper_test_cipher_n_bytes(
2329                key.as_slice(),
2330                &DES_FOR_LEGACY_USE_ONLY,
2331                OperatingMode::CBC,
2332                size,
2333            );
2334        }
2335    }
2336
2337    #[cfg(feature = "legacy-des")]
2338    #[test]
2339    #[allow(deprecated)]
2340    fn test_des_ecb() {
2341        let key = from_hex("0123456789abcdef").unwrap();
2342        for i in 0..=3 {
2343            let size = i * 8;
2344            helper_test_cipher_n_bytes(
2345                key.as_slice(),
2346                &DES_FOR_LEGACY_USE_ONLY,
2347                OperatingMode::ECB,
2348                size,
2349            );
2350        }
2351    }
2352
2353    #[cfg(feature = "legacy-des")]
2354    #[test]
2355    #[allow(deprecated)]
2356    fn test_des_ede_cbc() {
2357        let key = from_hex("0123456789abcdef23456789abcdef01").unwrap();
2358        for i in 0..=3 {
2359            let size = i * 8;
2360            helper_test_cipher_n_bytes(
2361                key.as_slice(),
2362                &DES_EDE_FOR_LEGACY_USE_ONLY,
2363                OperatingMode::CBC,
2364                size,
2365            );
2366        }
2367    }
2368
2369    #[cfg(feature = "legacy-des")]
2370    #[test]
2371    #[allow(deprecated)]
2372    fn test_des_ede_ecb() {
2373        let key = from_hex("0123456789abcdef23456789abcdef01").unwrap();
2374        for i in 0..=3 {
2375            let size = i * 8;
2376            helper_test_cipher_n_bytes(
2377                key.as_slice(),
2378                &DES_EDE_FOR_LEGACY_USE_ONLY,
2379                OperatingMode::ECB,
2380                size,
2381            );
2382        }
2383    }
2384
2385    #[cfg(feature = "legacy-des")]
2386    #[test]
2387    #[allow(deprecated)]
2388    fn test_des_ede3_cbc() {
2389        let key = from_hex("0123456789abcdef23456789abcdef01456789abcdef0123").unwrap();
2390        for i in 0..=3 {
2391            let size = i * 8;
2392            helper_test_cipher_n_bytes(
2393                key.as_slice(),
2394                &DES_EDE3_FOR_LEGACY_USE_ONLY,
2395                OperatingMode::CBC,
2396                size,
2397            );
2398        }
2399    }
2400
2401    #[cfg(feature = "legacy-des")]
2402    #[test]
2403    #[allow(deprecated)]
2404    fn test_des_ede3_ecb() {
2405        let key = from_hex("0123456789abcdef23456789abcdef01456789abcdef0123").unwrap();
2406        for i in 0..=3 {
2407            let size = i * 8;
2408            helper_test_cipher_n_bytes(
2409                key.as_slice(),
2410                &DES_EDE3_FOR_LEGACY_USE_ONLY,
2411                OperatingMode::ECB,
2412                size,
2413            );
2414        }
2415    }
2416
2417    #[cfg(feature = "legacy-des")]
2418    macro_rules! des_cipher_kat {
2419        ($name:ident, $alg:expr, $mode:expr, $key:literal, $iv:literal, $plaintext:literal, $ciphertext:literal) => {
2420            #[test]
2421            #[allow(deprecated)]
2422            fn $name() {
2423                let key = from_hex($key).unwrap();
2424                let input = from_hex($plaintext).unwrap();
2425                let expected_ciphertext = from_hex($ciphertext).unwrap();
2426
2427                let ec = if $iv.len() == 0 {
2428                    EncryptionContext::None
2429                } else {
2430                    let iv_arr: [u8; 8] = from_hex($iv).unwrap().try_into().unwrap();
2431                    EncryptionContext::Iv64(FixedLength::from(iv_arr))
2432                };
2433
2434                let unbound_key = UnboundCipherKey::new($alg, &key).unwrap();
2435                let encrypting_key = EncryptingKey::new(unbound_key, $mode).unwrap();
2436                let mut in_out = input.clone();
2437                let context = encrypting_key.less_safe_encrypt(&mut in_out, ec).unwrap();
2438                assert_eq!(expected_ciphertext, in_out);
2439
2440                let unbound_key2 = UnboundCipherKey::new($alg, &key).unwrap();
2441                let decrypting_key = DecryptingKey::new(unbound_key2, $mode).unwrap();
2442                let plaintext = decrypting_key.decrypt(&mut in_out, context).unwrap();
2443                assert_eq!(input.as_slice(), plaintext);
2444            }
2445        };
2446    }
2447
2448    #[cfg(feature = "legacy-des")]
2449    des_cipher_kat!(
2450        test_des_cbc_kat,
2451        &DES_FOR_LEGACY_USE_ONLY,
2452        OperatingMode::CBC,
2453        "0123456789abcdef",
2454        "f69f2445df4f9b17",
2455        "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51",
2456        "2d121f90fcf68631ed788fcfe31a62dffa784891fa512ef928ea856d934257c6"
2457    );
2458
2459    #[cfg(feature = "legacy-des")]
2460    des_cipher_kat!(
2461        test_des_ecb_kat,
2462        &DES_FOR_LEGACY_USE_ONLY,
2463        OperatingMode::ECB,
2464        "0123456789abcdef",
2465        "",
2466        "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51",
2467        "7277a00dc1c1c36b2256338b7a9f36ef6e511022cfebec489a4a53a1c7d22e5b"
2468    );
2469
2470    #[cfg(feature = "legacy-des")]
2471    des_cipher_kat!(
2472        test_sp800_67_des_ede_cbc,
2473        &DES_EDE_FOR_LEGACY_USE_ONLY,
2474        OperatingMode::CBC,
2475        "0123456789abcdef23456789abcdef01",
2476        "f69f2445df4f9b17",
2477        "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51",
2478        "7401CE1EAB6D003CAFF84BF47B36CC2154F0238F9FFECD8F6ACF118392B45581"
2479    );
2480
2481    #[cfg(feature = "legacy-des")]
2482    des_cipher_kat!(
2483        test_sp800_67_des_ede_ecb,
2484        &DES_EDE_FOR_LEGACY_USE_ONLY,
2485        OperatingMode::ECB,
2486        "0123456789abcdef23456789abcdef01",
2487        "",
2488        "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51",
2489        "06EDE3D82884090AFF322C19F0518486730576972A666E58B6C88CF107340D3D"
2490    );
2491
2492    #[cfg(feature = "legacy-des")]
2493    des_cipher_kat!(
2494        test_sp800_67_des_ede3_cbc,
2495        &DES_EDE3_FOR_LEGACY_USE_ONLY,
2496        OperatingMode::CBC,
2497        "0123456789abcdef23456789abcdef01456789abcdef0123",
2498        "f69f2445df4f9b17",
2499        "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51",
2500        "2079c3d53aa763e193b79e2569ab5262516570481f25b50f73c0bda85c8e0da7"
2501    );
2502
2503    #[cfg(feature = "legacy-des")]
2504    des_cipher_kat!(
2505        test_sp800_67_des_ede3_ecb,
2506        &DES_EDE3_FOR_LEGACY_USE_ONLY,
2507        OperatingMode::ECB,
2508        "0123456789abcdef23456789abcdef01456789abcdef0123",
2509        "",
2510        "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51",
2511        "714772f339841d34267fcc4bd2949cc3ee11c22a576a303876183f99c0b6de87"
2512    );
2513
2514    #[test]
2515    #[allow(deprecated)]
2516    fn test_ecb_rejects_iv_context_after_dispatch() {
2517        // The public entry points validate the context before dispatching, so
2518        // this calls the internal `encrypt`/`decrypt` directly to check that the
2519        // ECB helpers also return an error, rather than panic, for an IV context.
2520        let cases: &[(&'static Algorithm, &str)] = &[
2521            (&AES_128, "000102030405060708090a0b0c0d0e0f"),
2522            #[cfg(feature = "legacy-des")]
2523            (
2524                &DES_EDE3_FOR_LEGACY_USE_ONLY,
2525                "0123456789abcdef23456789abcdef01456789abcdef0123",
2526            ),
2527        ];
2528        for &(algorithm, key_hex) in cases {
2529            let key_bytes = from_hex(key_hex).unwrap();
2530            let encrypting_key =
2531                EncryptingKey::ecb(UnboundCipherKey::new(algorithm, &key_bytes).unwrap()).unwrap();
2532            let decrypting_key =
2533                DecryptingKey::ecb(UnboundCipherKey::new(algorithm, &key_bytes).unwrap()).unwrap();
2534            let mut in_out = vec![0u8; algorithm.block_len()];
2535            let iv = [0u8; IV_LEN_128_BIT];
2536
2537            assert!(encrypt(
2538                algorithm,
2539                &encrypting_key.key,
2540                OperatingMode::ECB,
2541                &mut in_out,
2542                EncryptionContext::Iv128(FixedLength::from(iv)),
2543            )
2544            .is_err());
2545            assert!(decrypt(
2546                algorithm,
2547                &decrypting_key.key,
2548                OperatingMode::ECB,
2549                &mut in_out,
2550                DecryptionContext::Iv128(FixedLength::from(iv)),
2551            )
2552            .is_err());
2553        }
2554    }
2555}