Repository: aws/aws-lc-rs (language: rust)

Issue:
[Feature Request] Support CFB mode for AES
### Problem:

The [TPM2 specification Part 1: Architecture 24.4 Symmetric Encrypt](https://trustedcomputinggroup.org/resource/tpm-library-specification/) requires the use of an AES 128 bit key using CFB mode. Currently CFB mode is not exposed in aws-ls-rs which blocks us from using it for the particular use case.

### Requirements:

- Encryption using an AES 128 bit key with CFB mode
- The IV vector needs to be configurable (in the use case it will be set to all zero)


Discussion / hints:
Hey @mariusknaust 

Can you confirm what bit-mode of CFB you need? I tried looking in the TPM2 specification but did not see it explicitly state the bit-mode in use. Typically that implies CFB 128-bit mode, but just want to double check. AWS-LC supports 1, 8, and 128 bit-modes for CFB, but want to confirm what you need to make sure we are able to provide it.

Thanks
Hi @skmcgrail,

thanks for picking this up. For the TPM2 use case we need to use AES for a 128 bit key in CFB mode with **128-bit shift** (what would be `EVP_aes_128_cfb128` in OpenSSL).

Fix patch:
diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs
index 31d85865b27..e5c8608dc42 100644
--- a/aws-lc-rs/src/cipher.rs
+++ b/aws-lc-rs/src/cipher.rs
@@ -11,10 +11,10 @@
 //! The modes provided here only provide confidentiality, but **do not**
 //! provide integrity or authentication verification of ciphertext.
 //!
-//! These algorithms are provided solely for applications requring them
-//! in order to maintain backwards compatability in legacy applications.
+//! These algorithms are provided solely for applications requiring them
+//! in order to maintain backwards compatibility in legacy applications.
 //!
-//! If you are developing new applications requring data encryption see
+//! If you are developing new applications requiring data encryption see
 //! the algorithms provided in [`aead`](crate::aead).
 //!
 //! # Examples
@@ -135,6 +135,35 @@
 //! # }
 //! ```
 //!
+//! ### AES-128 CFB 128-bit mode
+//!
+//! ```rust
+//! # use std::error::Error;
+//! #
+//! # fn main() -> Result<(), Box<dyn Error>> {
+//! use aws_lc_rs::cipher::{DecryptingKey, EncryptingKey, UnboundCipherKey, AES_128};
+//!
+//! let original_message = "This is a secret message!".as_bytes();
+//! let mut in_out_buffer = Vec::from(original_message);
+//!
+//! let key_bytes: &[u8] = &[
+//!     0xff, 0x0b, 0xe5, 0x84, 0x64, 0x0b, 0x00, 0xc8, 0x90, 0x7a, 0x4b, 0xbf, 0x82, 0x7c, 0xb6,
+//!     0xd1,
+//! ];
+//!
+//! let key = UnboundCipherKey::new(&AES_128, key_bytes)?;
+//! let mut encrypting_key = EncryptingKey::cfb128(key)?;
+//! let context = encrypting_key.encrypt(&mut in_out_buffer)?;
+//!
+//! let key = UnboundCipherKey::new(&AES_128, key_bytes)?;
+//! let mut decrypting_key = DecryptingKey::cfb128(key)?;
+//! let plaintext = decrypting_key.decrypt(&mut in_out_buffer, context)?;
+//! assert_eq!(original_message, plaintext);
+//! #
+//! # Ok(())
+//! # }
+//! ```
+//!
 //! ## Constructing a `DecryptionContext` for decryption.
 //!
 //! ```rust
@@ -207,11 +236,11 @@ use crate::hkdf::KeyType;
 use crate::iv::{FixedLength, IV_LEN_128_BIT};
 use crate::ptr::ConstPointer;
 use aws_lc::{
-    AES_cbc_encrypt, AES_ctr128_encrypt, EVP_aes_128_cbc, EVP_aes_128_ctr, EVP_aes_256_cbc,
-    EVP_aes_256_ctr, AES_DECRYPT, AES_ENCRYPT, AES_KEY, EVP_CIPHER,
+    AES_cbc_encrypt, AES_cfb128_encrypt, AES_ctr128_encrypt, EVP_aes_128_cbc, EVP_aes_128_cfb128,
+    EVP_aes_128_ctr, EVP_aes_256_cbc, EVP_aes_256_cfb128, EVP_aes_256_ctr, AES_DECRYPT,
+    AES_ENCRYPT, AES_KEY, EVP_CIPHER,
 };
 use core::fmt::Debug;
-use core::mem::MaybeUninit;
 use key::SymmetricCipherKey;
 use zeroize::Zeroize;
 
@@ -228,6 +257,10 @@ pub const AES_CBC_IV_LEN: usize = 16;
 
 /// The number of bytes for an AES-CTR initialization vector (IV)
 pub const AES_CTR_IV_LEN: usize = 16;
+
+/// The number of bytes for an AES-CFB initialization vector (IV)
+pub const AES_CFB_IV_LEN: usize = 16;
+
 const AES_BLOCK_LEN: usize = 16;
 
 const MAX_CIPHER_BLOCK_LEN: usize = AES_BLOCK_LEN;
@@ -241,6 +274,9 @@ pub enum OperatingMode {
 
     /// Counter (CTR) mode.
     CTR,
+
+    /// CFB 128-bit mode.
+    CFB128,
 }
 
 impl OperatingMode {
@@ -249,8 +285,10 @@ impl OperatingMode {
         ConstPointer::new(match (self, algorithm.id) {
             (OperatingMode::CBC, AlgorithmId::Aes128) => unsafe { EVP_aes_128_cbc() },
             (OperatingMode::CTR, AlgorithmId::Aes128) => unsafe { EVP_aes_128_ctr() },
+            (OperatingMode::CFB128, AlgorithmId::Aes128) => unsafe { EVP_aes_128_cfb128() },
             (OperatingMode::CBC, AlgorithmId::Aes256) => unsafe { EVP_aes_256_cbc() },
             (OperatingMode::CTR, AlgorithmId::Aes256) => unsafe { EVP_aes_256_ctr() },
+            (OperatingMode::CFB128, AlgorithmId::Aes256) => unsafe { EVP_aes_256_cfb128() },
         })
         .unwrap()
     }
@@ -345,8 +383,9 @@ impl Algorithm {
         mode: OperatingMode,
     ) -> Result<EncryptionContext, Unspecified> {
         match self.id {
+            // TODO: Hopefully support CFB1, and CFB8
             AlgorithmId::Aes128 | AlgorithmId::Aes256 => match mode {
-                OperatingMode::CBC | OperatingMode::CTR => {
+                OperatingMode::CBC | OperatingMode::CTR | OperatingMode::CFB128 => {
                     Ok(EncryptionContext::Iv128(FixedLength::new()?))
                 }
             },
@@ -355,8 +394,9 @@ impl Algorithm {
 
     fn is_valid_encryption_context(&self, mode: OperatingMode, input: &EncryptionContext) -> bool {
         match self.id {
+            // TODO: Hopefully support CFB1, and CFB8
             AlgorithmId::Aes128 | AlgorithmId::Aes256 => match mode {
-                OperatingMode::CBC | OperatingMode::CTR => {
+                OperatingMode::CBC | OperatingMode::CTR | OperatingMode::CFB128 => {
                     matches!(input, EncryptionContext::Iv128(_))
                 }
             },
@@ -364,9 +404,10 @@ impl Algorithm {
     }
 
     fn is_valid_decryption_context(&self, mode: OperatingMode, input: &DecryptionContext) -> bool {
+        // TODO: Hopefully support CFB1, and CFB8
         match self.id {
             AlgorithmId::Aes128 | AlgorithmId::Aes256 => match mode {
-                OperatingMode::CBC | OperatingMode::CTR => {
+                OperatingMode::CBC | OperatingMode::CTR | OperatingMode::CFB128 => {
                     matches!(input, DecryptionContext::Iv128(_))
                 }
             },
@@ -459,6 +500,19 @@ impl EncryptingKey {
         EncryptingKey::new(key, OperatingMode::CTR)
     }
 
+    /// Constructs an `EncryptingKey` operating in cipher feedback 128-bit mode (CFB128) using the provided key.
+    ///
+    // # FIPS
+    // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms:
+    // * `AES_128`
+    // * `AES_256`
+    //
+    /// # Errors
+    /// * [`Unspecified`]: Returned if there is an error constructing the `EncryptingKey`.
+    pub fn cfb128(key: UnboundCipherKey) -> Result<EncryptingKey, Unspecified> {
+        EncryptingKey::new(key, OperatingMode::CFB128)
+    }
+
     #[allow(clippy::unnecessary_wraps)]
     fn new(key: UnboundCipherKey, mode: OperatingMode) -> Result<EncryptingKey, Unspecified> {
         let algorithm = key.algorithm();
@@ -547,6 +601,19 @@ impl DecryptingKey {
         DecryptingKey::new(key, OperatingMode::CTR)
     }
 
+    /// Constructs a cipher decrypting key operating in cipher feedback 128-bit mode (CFB128) using the provided key and context.
+    ///
+    // # FIPS
+    // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms:
+    // * `AES_128`
+    // * `AES_256`
+    //
+    /// # Errors
+    /// * [`Unspecified`]: Returned if there is an error during decryption.
+    pub fn cfb128(key: UnboundCipherKey) -> Result<DecryptingKey, Unspecified> {
+        DecryptingKey::new(key, OperatingMode::CFB128)
+    }
+
     #[allow(clippy::unnecessary_wraps)]
     fn new(key: UnboundCipherKey, mode: OperatingMode) -> Result<DecryptingKey, Unspecified> {
         let algorithm = key.algorithm();
@@ -603,13 +670,8 @@ fn encrypt(
 ) -> Result<DecryptionContext, Unspecified> {
     let block_len = algorithm.block_len();
 
-    match mode {
-        OperatingMode::CTR => {}
-        _ => {
-            if (in_out.len() % block_len) != 0 {
-                return Err(Unspecified);
-            }
-        }
+    if mode == OperatingMode::CBC && (in_out.len() % block_len) != 0 {
+        return Err(Unspecified);
     }
 
     match mode {
@@ -619,6 +681,12 @@ fn encrypt(
         OperatingMode::CTR => match algorithm.id() {
             AlgorithmId::Aes128 | AlgorithmId::Aes256 => encrypt_aes_ctr_mode(key, context, in_out),
         },
+        // TODO: Hopefully support CFB1, and CFB8
+        OperatingMode::CFB128 => match algorithm.id() {
+            AlgorithmId::Aes128 | AlgorithmId::Aes256 => {
+                encrypt_aes_cfb_mode(key, mode, context, in_out)
+            }
+        },
     }
 }
 
@@ -631,13 +699,8 @@ fn decrypt<'in_out>(
 ) -> Result<&'in_out mut [u8], Unspecified> {
     let block_len = algorithm.block_len();
 
-    match mode {
-        OperatingMode::CTR => {}
-        _ => {
-            if (in_out.len() % block_len) != 0 {
-                return Err(Unspecified);
-            }
-        }
+    if mode == OperatingMode::CBC && (in_out.len() % block_len) != 0 {
+        return Err(Unspecified);
     }
 
     match mode {
@@ -647,6 +710,12 @@ fn decrypt<'in_out>(
         OperatingMode::CTR => match algorithm.id() {
             AlgorithmId::Aes128 | AlgorithmId::Aes256 => decrypt_aes_ctr_mode(key, context, in_out),
         },
+        // TODO: Hopefully support CFB1, and CFB8
+        OperatingMode::CFB128 => match algorithm.id() {
+            AlgorithmId::Aes128 | AlgorithmId::Aes256 => {
+                decrypt_aes_cfb_mode(key, mode, context, in_out)
+            }
+        },
     }
 }
 
@@ -660,7 +729,7 @@ fn encrypt_aes_ctr_mode(
         SymmetricCipherKey::Aes128 { enc_key, .. } | SymmetricCipherKey::Aes256 { enc_key, .. } => {
             enc_key
         }
-        _ => return Err(Unspecified),
+        _ => unreachable!(),
     };
 
     let mut iv = {
@@ -696,7 +765,7 @@ fn encrypt_aes_cbc_mode(
         SymmetricCipherKey::Aes128 { enc_key, .. } | SymmetricCipherKey::Aes256 { enc_key, .. } => {
             enc_key
         }
-        _ => return Err(Unspecified),
+        _ => unreachable!(),
     };
 
     let mut iv = {
@@ -722,7 +791,7 @@ fn decrypt_aes_cbc_mode<'in_out>(
         SymmetricCipherKey::Aes128 { dec_key, .. } | SymmetricCipherKey::Aes256 { dec_key, .. } => {
             dec_key
         }
-        _ => return Err(Unspecified),
+        _ => unreachable!(),
     };
 
     let mut iv = {
@@ -737,8 +806,75 @@ fn decrypt_aes_cbc_mode<'in_out>(
     Ok(in_out)
 }
 
+#[allow(clippy::needless_pass_by_value)]
+fn encrypt_aes_cfb_mode(
+    key: &SymmetricCipherKey,
+    mode: OperatingMode,
+    context: EncryptionContext,
+    in_out: &mut [u8],
+) -> Result<DecryptionContext, Unspecified> {
+    #[allow(clippy::match_wildcard_for_single_variants)]
+    let key = match &key {
+        SymmetricCipherKey::Aes128 { enc_key, .. } | SymmetricCipherKey::Aes256 { enc_key, .. } => {
+            enc_key
+        }
+        _ => unreachable!(),
+    };
+
+    let mut iv = {
+        let mut iv = [0u8; AES_CFB_IV_LEN];
+        iv.copy_from_slice((&context).try_into()?);
+        iv
+    };
+
+    let cfb_encrypt: fn(&AES_KEY, &mut [u8], &mut [u8]) = match mode {
+        // TODO: Hopefully support CFB1, and CFB8
+        OperatingMode::CFB128 => aes_cfb128_encrypt,
+        _ => unreachable!(),
+    };
+
+    cfb_encrypt(key, &mut iv, in_out);
+    iv.zeroize();
+
+    Ok(context.into())
+}
+
+#[allow(clippy::needless_pass_by_value)]
+fn decrypt_aes_cfb_mode<'in_out>(
+    key: &SymmetricCipherKey,
+    mode: OperatingMode,
+    context: DecryptionContext,
+    in_out: &'in_out mut [u8],
+) -> Result<&'in_out mut [u8], Unspecified> {
+    #[allow(clippy::match_wildcard_for_single_variants)]
+    let key = match &key {
+        SymmetricCipherKey::Aes128 { enc_key, .. } | SymmetricCipherKey::Aes256 { enc_key, .. } => {
+            enc_key
+        }
+        _ => unreachable!(),
+    };
+
+    let mut iv = {
+        let mut iv = [0u8; AES_CFB_IV_LEN];
+        iv.copy_from_slice((&context).try_into()?);
+        iv
+    };
+
+    let cfb_decrypt: fn(&AES_KEY, &mut [u8], &mut [u8]) = match mode {
+        // TODO: Hopefully support CFB1, and CFB8
+        OperatingMode::CFB128 => aes_cfb128_decrypt,
+        _ => unreachable!(),
+    };
+
+    cfb_decrypt(key, &mut iv, in_out);
+
+    iv.zeroize();
+
+    Ok(in_out)
+}
+
 fn aes_ctr128_encrypt(key: &AES_KEY, iv: &mut [u8], block_buffer: &mut [u8], in_out: &mut [u8]) {
-    let mut num = MaybeUninit::<u32>::new(0);
+    let mut num: u32 = 0;
 
     indicator_check!(unsafe {
         AES_ctr128_encrypt(
@@ -748,7 +884,7 @@ fn aes_ctr128_encrypt(key: &AES_KEY, iv: &mut [u8], block_buffer: &mut [u8], in_
             key,
             iv.as_mut_ptr(),
             block_buffer.as_mut_ptr(),
-            num.as_mut_ptr(),
+            &mut num,
         );
     });
 
@@ -781,6 +917,36 @@ fn aes_cbc_decrypt(key: &AES_KEY, iv: &mut [u8], in_out: &mut [u8]) {
     });
 }
 
+fn aes_cfb128_encrypt(key: &AES_KEY, iv: &mut [u8], in_out: &mut [u8]) {
+    let mut num: i32 = 0;
+    indicator_check!(unsafe {
+        AES_cfb128_encrypt(
+            in_out.as_ptr(),
+            in_out.as_mut_ptr(),
+            in_out.len(),
+            key,
+            iv.as_mut_ptr(),
+            &mut num,
+            AES_ENCRYPT,
+        );
+    });
+}
+
+fn aes_cfb128_decrypt(key: &AES_KEY, iv: &mut [u8], in_out: &mut [u8]) {
+    let mut num: i32 = 0;
+    indicator_check!(unsafe {
+        AES_cfb128_encrypt(
+            in_out.as_ptr(),
+            in_out.as_mut_ptr(),
+            in_out.len(),
+            key,
+            iv.as_mut_ptr(),
+            &mut num,
+            AES_DECRYPT,
+        );
+    });
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -963,4 +1129,24 @@ mod tests {
         "eca7285d19f3c20e295378460e8729",
         "b5098e5e788de6ac2f2098eb2fc6f8"
     );
+
+    cipher_kat!(
+        test_sp800_38a_cfb128_aes128,
+        &AES_128,
+        OperatingMode::CFB128,
+        "2b7e151628aed2a6abf7158809cf4f3c",
+        "000102030405060708090a0b0c0d0e0f",
+        "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
+        "3b3fd92eb72dad20333449f8e83cfb4ac8a64537a0b3a93fcde3cdad9f1ce58b26751f67a3cbb140b1808cf187a4f4dfc04b05357c5d1c0eeac4c66f9ff7f2e6"
+    );
+
+    cipher_kat!(
+        test_sp800_38a_cfb128_aes256,
+        &AES_256,
+        OperatingMode::CFB128,
+        "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
+        "000102030405060708090a0b0c0d0e0f",
+        "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
+        "dc7e84bfda79164b7ecd8486985d386039ffed143b28b1c832113c6331e5407bdf10132415e54b92a13ed0a8267ae2f975a385741ab9cef82031623d55b1e471"
+    );
 }
diff --git a/aws-lc-rs/src/cipher/streaming.rs b/aws-lc-rs/src/cipher/streaming.rs
index b12029abeff..e990678bc07 100644
--- a/aws-lc-rs/src/cipher/streaming.rs
+++ b/aws-lc-rs/src/cipher/streaming.rs
@@ -217,6 +217,33 @@ impl StreamingEncryptingKey {
         Self::less_safe_cbc_pkcs7(key, context)
     }
 
+    /// Constructs a `StreamingEncryptingKey` for encrypting data using the CFB128 cipher mode.
+    /// The resulting ciphertext will be the same length as the plaintext.
+    ///
+    /// # Errors
+    /// Returns and error on an internal failure.
+    pub fn cfb128(key: UnboundCipherKey) -> Result<Self, Unspecified> {
+        let context = key
+            .algorithm()
+            .new_encryption_context(OperatingMode::CFB128)?;
+        Self::less_safe_cfb128(key, context)
+    }
+
+    /// Constructs a `StreamingEncryptingKey` for encrypting data using the CFB128 cipher mode.
+    /// The resulting ciphertext will be the same length as the plaintext.
+    ///
+    /// This is considered less safe because the caller could potentially construct
+    /// an `EncryptionContext` from a previously used initialization vector (IV).
+    ///
+    /// # Errors
+    /// Returns an error on an internal failure.
+    pub fn less_safe_cfb128(
+        key: UnboundCipherKey,
+        context: EncryptionContext,
+    ) -> Result<Self, Unspecified> {
+        Self::new(key, OperatingMode::CFB128, context)
+    }
+
     /// Constructs a `StreamingEncryptingKey` for encrypting data using the CBC cipher mode
     /// with pkcs7 padding.
     /// The resulting ciphertext will be longer than the plaintext; padding is added
@@ -380,6 +407,15 @@ impl StreamingDecryptingKey {
     ) -> Result<Self, Unspecified> {
         Self::new(key, OperatingMode::CBC, context)
     }
+
+    // Constructs a `StreamingDecryptingKey` for decrypting using the CFB128 cipher mode.
+    /// The resulting plaintext will be the same length as the ciphertext.
+    ///
+    /// # Errors
+    /// Returns an error on an internal failure.
+    pub fn cfb128(key: UnboundCipherKey, context: DecryptionContext) -> Result<Self, Unspecified> {
+        Self::new(key, OperatingMode::CFB128, context)
+    }
 }
 
 #[cfg(test)]
@@ -434,7 +470,7 @@ mod tests {
                 assert!(ciphertext.len() > plaintext.len());
                 assert!(ciphertext.len() <= plaintext.len() + alg.block_len());
             }
-            OperatingMode::CTR => {
+            _ => {
                 assert_eq!(ciphertext.len(), plaintext.len());
             }
         }
@@ -483,7 +519,7 @@ mod tests {
                 assert!(ciphertext.len() > plaintext.len());
                 assert!(ciphertext.len() <= plaintext.len() + alg.block_len());
             }
-            OperatingMode::CTR => {
+            _ => {
                 assert_eq!(ciphertext.len(), plaintext.len());
             }
         }
@@ -519,6 +555,7 @@ mod tests {
 
     helper_stream_step_encrypt_test!(cbc_pkcs7);
     helper_stream_step_encrypt_test!(ctr);
+    helper_stream_step_encrypt_test!(cfb128);
 
     #[test]
     fn test_step_cbc() {
@@ -631,6 +668,61 @@ mod tests {
         }
     }
 
+    #[test]
+    fn test_step_cfb128() {
+        let random = SystemRandom::new();
+        let mut key = [0u8; AES_256_KEY_LEN];
+        random.fill(&mut key).unwrap();
+
+        let encrypting_key_creator = || {
+            let key = UnboundCipherKey::new(&AES_256, &key.clone()).unwrap();
+            StreamingEncryptingKey::cfb128(key).unwrap()
+        };
+        let decrypting_key_creator = |decryption_ctx: DecryptionContext| {
+            let key = UnboundCipherKey::new(&AES_256, &key.clone()).unwrap();
+            StreamingDecryptingKey::cfb128(key, decryption_ctx).unwrap()
+        };
+
+        for i in 13..=21 {
+            for j in 124..=131 {
+                helper_test_cfb128_stream_encrypt_step_n_bytes(
+                    encrypting_key_creator,
+                    decrypting_key_creator,
+                    j,
+                    i,
+                );
+            }
+            for j in 124..=131 {
+                helper_test_cfb128_stream_encrypt_step_n_bytes(
+                    encrypting_key_creator,
+                    decrypting_key_creator,
+                    j,
+                    j - i,
+                );
+            }
+        }
+        for j in 124..=131 {
+            helper_test_cfb128_stream_encrypt_step_n_bytes(
+                encrypting_key_creator,
+                decrypting_key_creator,
+                j,
+                j,
+            );
+            helper_test_cfb128_stream_encrypt_step_n_bytes(
+                encrypting_key_creator,
+                decrypting_key_creator,
+                j,
+                256,
+            );
+            helper_test_cfb128_stream_encrypt_step_n_bytes(
+                encrypting_key_creator,
+                decrypting_key_creator,
+                j,
+                1,
+            );
+        }
+    }
+
     macro_rules! streaming_cipher_kat {
         ($name:ident, $alg:expr, $mode:expr, $key:literal, $iv: literal, $plaintext:literal, $ciphertext:literal, $from_step:literal, $to_step:literal) => {
             #[test]
@@ -783,4 +875,52 @@ mod tests {
         2,
         9
     );
+
+    streaming_cipher_kat!(
+        test_openssl_aes_128_cfb128_16_bytes,
+        &AES_128,
+        OperatingMode::CFB128,
+        "5c353f739429bbd48b7e3f9a76facf4d",
+        "7b2c7ce17a9b6a59a9e64253b98c8cd1",
+        "add1bcebeaabe9423d4e916400e877c5",
+        "8440ec442e4135a613ddb2ce26107e10",
+        2,
+        9
+    );
+
+    streaming_cipher_kat!(
+        test_openssl_aes_128_cfb128_15_bytes,
+        &AES_128,
+        OperatingMode::CFB128,
+        "e1f39d70ad378efc1ac318aa8ac4489f",
+        "ec78c3d54fff2fe09678c7883024ddce",
+        "b8c905004b2a92a323769f1b8dc1b2",
+        "964c3e9bf8bf2a3cca02d8e2e75608",
+        2,
+        9
+    );
+
+    streaming_cipher_kat!(
+        test_openssl_aes_256_cfb128_16_bytes,
+        &AES_256,
+        OperatingMode::CFB128,
+        "0e8117d0984d6acb957a5d6ca526a12fa612ce5de2daadebd42c14d28a0a192e",
+        "09147a153b230a40cd7bf4197ad0e825",
+        "13f4540a4e06394148ade31a6f678787",
+        "250e590e47b7613b7d0a53f684e970d6",
+        2,
+        9
+    );
+
+    streaming_cipher_kat!(
+        test_openssl_aes_256_cfb128_15_bytes,
+        &AES_256,
+        OperatingMode::CFB128,
+        "5cb17d8d5b9dbd81e4f1e0a2c82ebf36cf61156388fb7abf99d4526622858225",
+        "13c77415ec24f3e2f784f228478a85be",
+        "3efa583df4405aab61e18155aa7e0d",
+        "c1f2ffe8aa5064199e8f4f1b388303",
+        2,
+        9
+    );
 }


Test patch:
diff --git a/aws-lc-rs-testing/benches/kem_benchmark.rs b/aws-lc-rs-testing/benches/kem_benchmark.rs
index e906b3d2723..afe708156a2 100644
--- a/aws-lc-rs-testing/benches/kem_benchmark.rs
+++ b/aws-lc-rs-testing/benches/kem_benchmark.rs
@@ -7,6 +7,7 @@ use aws_lc_rs::{
 };
 use criterion::{criterion_group, criterion_main, Criterion};
 
+#[allow(deprecated)]
 const UNSTABLE_ALGORITHMS: &[Option<&aws_lc_rs::kem::Algorithm<AlgorithmId>>] = &[
     get_algorithm(AlgorithmId::Kyber512_R3),
     get_algorithm(AlgorithmId::Kyber768_R3),
diff --git a/aws-lc-rs/src/test.rs b/aws-lc-rs/src/test.rs
index a88716252fb..ccc2f6675fd 100644
--- a/aws-lc-rs/src/test.rs
+++ b/aws-lc-rs/src/test.rs
@@ -202,7 +202,7 @@ impl TestCase {
         let result = if s.starts_with('\"') {
             // The value is a quoted UTF-8 string.
 
-            let mut bytes = Vec::with_capacity(s.as_bytes().len());
+            let mut bytes = Vec::with_capacity(s.len());
             let mut s = s.as_bytes().iter().skip(1);
             loop {
                 let b = match s.next() {