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