From 5f89d7001f75686bcad25b9f389de6933af256ec Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Tue, 16 May 2023 17:00:25 -0400 Subject: [PATCH 01/54] API changes POC --- aws-lc-rs/src/cipher.rs | 121 ++++++++++++++++++++++++++++++++++++++++ aws-lc-rs/src/iv.rs | 56 +++++++++++++++++++ aws-lc-rs/src/lib.rs | 1 + 3 files changed, 178 insertions(+) create mode 100644 aws-lc-rs/src/iv.rs diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index 7b94aaba322..cc7f89f4f95 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -3,6 +3,8 @@ // Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR ISC +#![allow(dead_code, unused_variables)] + pub(crate) mod aes; pub(crate) mod block; pub(crate) mod chacha; @@ -11,6 +13,8 @@ use crate::cipher::aes::{encrypt_block_aes, Aes128Key, Aes256Key}; use crate::cipher::block::Block; use crate::cipher::chacha::ChaCha20Key; use crate::error::Unspecified; +use crate::iv::IV; +use crate::rand; use aws_lc::{AES_set_encrypt_key, AES_KEY}; use std::mem::{size_of, transmute, MaybeUninit}; use std::os::raw::c_uint; @@ -41,6 +45,88 @@ impl Drop for SymmetricCipherKey { } } +enum AlgorithmId { + Aes128ctr, + Aes128cbc, + Aes256ctr, + Aes256cbc, + Chacha20, +} +pub struct Algorithm(AlgorithmId); + +pub const AES128_CTR: Algorithm<16, 16> = Algorithm(AlgorithmId::Aes128ctr); +pub const AES128_CBC: Algorithm<16, 16> = Algorithm(AlgorithmId::Aes128cbc); +pub const AES256_CTR: Algorithm<32, 16> = Algorithm(AlgorithmId::Aes256ctr); +pub const AES256_CBC: Algorithm<32, 16> = Algorithm(AlgorithmId::Aes256cbc); +pub const CHACHA20: Algorithm<32, 12> = Algorithm(AlgorithmId::Chacha20); + +pub struct CipherKey { + algorithm: &'static Algorithm, + key: [u8; KEYSIZE], +} + +impl CipherKey { + fn new( + algorithm: &'static Algorithm, + key_bytes: &[u8], + ) -> Result { + let key = key_bytes.try_into()?; + Ok(CipherKey { algorithm, key }) + } +} + +pub struct EncryptingKey { + cipher_key: CipherKey, + iv: IV, +} + +impl EncryptingKey { + fn new( + cipher_key: CipherKey, + ) -> Result, Unspecified> { + let mut iv_bytes = [0u8; IVSIZE]; + rand::fill(&mut iv_bytes)?; + Ok(EncryptingKey { + cipher_key, + iv: IV::assume_unique_for_key(iv_bytes), + }) + } + + #[must_use] + fn encrypt_in_place(self, in_out: &mut [u8]) -> Result, Unspecified> { + // TODO: THIS IS A PROOF OF CONCEPT + // do nothing + Ok(self.iv) + } + + fn encrypt_append_padding(self, in_out: INOUT) -> Result, Unspecified> + where + INOUT: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, + { + todo!() + } +} + +pub struct DecryptingKey { + cipher_key: CipherKey, + iv: IV, +} + +impl DecryptingKey { + fn new( + cipher_key: CipherKey, + iv: IV, + ) -> DecryptingKey { + DecryptingKey { cipher_key, iv } + } + + fn decrypt_in_place(&self, in_out: &mut [u8]) -> Result<(), Unspecified> { + // TODO: THIS IS A PROOF OF CONCEPT + // do nothing + Ok(()) + } +} + impl SymmetricCipherKey { pub(crate) fn aes128(key_bytes: &[u8]) -> Result { if key_bytes.len() != 16 { @@ -157,4 +243,39 @@ mod tests { assert_eq!(expected_result.as_slice(), result.as_ref()); } + + #[test] + fn test_aes_128_ctr() { + let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); + let mut input = from_hex("00112233445566778899aabbccddeeff").unwrap(); + + let cipher_key = CipherKey::new(&AES128_CTR, &key).unwrap(); + let encrypting_key = EncryptingKey::new(cipher_key).unwrap(); + + let mut inout = input.as_mut_slice(); + + let decrypt_iv = encrypting_key.encrypt_in_place(inout).unwrap(); + + let cipher_key2 = CipherKey::new(&AES128_CTR, &key).unwrap(); + let decrypting_key = DecryptingKey::new(cipher_key2, decrypt_iv); + + decrypting_key.decrypt_in_place(inout); + } + + #[test] + fn test_aes_128_cbc() { + let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); + let mut input = from_hex("00112233445566778899aabbccddeeff").unwrap(); + + let cipher_key = CipherKey::new(&AES128_CBC, &key).unwrap(); + let encrypting_key = EncryptingKey::new(cipher_key).unwrap(); + + let mut inout = input.as_mut_slice(); + let decrypt_iv = encrypting_key.encrypt_in_place(inout).unwrap(); + + let cipher_key2 = CipherKey::new(&AES128_CBC, &key).unwrap(); + let decrypting_key = DecryptingKey::new(cipher_key2, decrypt_iv); + + decrypting_key.decrypt_in_place(inout); + } } diff --git a/aws-lc-rs/src/iv.rs b/aws-lc-rs/src/iv.rs new file mode 100644 index 00000000000..def296ea275 --- /dev/null +++ b/aws-lc-rs/src/iv.rs @@ -0,0 +1,56 @@ +// Copyright 2018 Brian Smith. +// SPDX-License-Identifier: ISC +// Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 OR ISC +#![allow(dead_code)] + +use crate::error; + +pub struct IV([u8; L]); + +impl IV { + /// Constructs a `IV` with the given value, assuming that the value is + /// unique for the lifetime of the key it is being used with. + /// + /// Fails if `value` isn't `L` bytes long. + /// # Errors + /// `error::Unspecified` when byte slice length is not `L` + #[inline] + pub fn try_assume_unique_for_key(value: &[u8]) -> Result { + let value: &[u8; L] = value.try_into()?; + Ok(Self::assume_unique_for_key(*value)) + } + + /// Constructs a `Nonce` with the given value, assuming that the value is + /// unique for the lifetime of the key it is being used with. + #[inline] + #[must_use] + pub fn assume_unique_for_key(value: [u8; L]) -> Self { + Self(value) + } + + pub fn size(&self) -> usize { + return L; + } +} + +impl AsRef<[u8; L]> for IV { + #[inline] + fn as_ref(&self) -> &[u8; L] { + &self.0 + } +} + +impl From<&[u8; L]> for IV { + #[inline] + fn from(bytes: &[u8; L]) -> Self { + IV(bytes.to_owned()) + } +} + +impl From<[u8; L]> for IV { + #[inline] + fn from(bytes: [u8; L]) -> Self { + IV(bytes) + } +} diff --git a/aws-lc-rs/src/lib.rs b/aws-lc-rs/src/lib.rs index bb63d203b7b..21f587a752d 100644 --- a/aws-lc-rs/src/lib.rs +++ b/aws-lc-rs/src/lib.rs @@ -151,6 +151,7 @@ mod cbs; mod ec; mod ed25519; mod evp_pkey; +mod iv; mod ptr; use aws_lc::{ From 1ba1b6f333e593d92ce699bf64bc9b5a14b414c2 Mon Sep 17 00:00:00 2001 From: Sean McGrail Date: Tue, 16 May 2023 22:23:53 -0700 Subject: [PATCH 02/54] CBC & CTR implementations --- aws-lc-rs/src/cipher.rs | 272 +++++++++++++++++++++++++++++++++------- aws-lc-rs/src/iv.rs | 7 ++ 2 files changed, 236 insertions(+), 43 deletions(-) diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index cc7f89f4f95..0b20b042f1f 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -12,10 +12,11 @@ pub(crate) mod chacha; use crate::cipher::aes::{encrypt_block_aes, Aes128Key, Aes256Key}; use crate::cipher::block::Block; use crate::cipher::chacha::ChaCha20Key; -use crate::error::Unspecified; +use crate::error::{self, Unspecified}; use crate::iv::IV; use crate::rand; use aws_lc::{AES_set_encrypt_key, AES_KEY}; +use aws_lc_sys::{AES_cbc_encrypt, AES_ctr128_encrypt, AES_DECRYPT, AES_ENCRYPT}; use std::mem::{size_of, transmute, MaybeUninit}; use std::os::raw::c_uint; use std::ptr; @@ -52,38 +53,94 @@ enum AlgorithmId { Aes256cbc, Chacha20, } -pub struct Algorithm(AlgorithmId); -pub const AES128_CTR: Algorithm<16, 16> = Algorithm(AlgorithmId::Aes128ctr); -pub const AES128_CBC: Algorithm<16, 16> = Algorithm(AlgorithmId::Aes128cbc); -pub const AES256_CTR: Algorithm<32, 16> = Algorithm(AlgorithmId::Aes256ctr); -pub const AES256_CBC: Algorithm<32, 16> = Algorithm(AlgorithmId::Aes256cbc); -pub const CHACHA20: Algorithm<32, 12> = Algorithm(AlgorithmId::Chacha20); +enum OperatingMode { + Block, + Stream, +} + +pub struct Algorithm( + AlgorithmId, + OperatingMode, +); + +pub const AES128_CTR: Algorithm<16, 16, 16> = + Algorithm(AlgorithmId::Aes128ctr, OperatingMode::Stream); +pub const AES128_CBC: Algorithm<16, 16, 16> = + Algorithm(AlgorithmId::Aes128cbc, OperatingMode::Block); +pub const AES256_CTR: Algorithm<32, 16, 16> = + Algorithm(AlgorithmId::Aes256ctr, OperatingMode::Stream); +pub const AES256_CBC: Algorithm<32, 16, 16> = + Algorithm(AlgorithmId::Aes256cbc, OperatingMode::Block); +pub const CHACHA20: Algorithm<32, 12, 64> = Algorithm(AlgorithmId::Chacha20, OperatingMode::Stream); + +impl + Algorithm +{ + #[inline] + fn get_id(&self) -> &AlgorithmId { + return &self.0; + } -pub struct CipherKey { - algorithm: &'static Algorithm, - key: [u8; KEYSIZE], + #[inline] + fn is_block_mode(&self) -> bool { + match &self.1 { + OperatingMode::Block => true, + _ => false, + } + } + + #[inline] + fn is_stream_mode(&self) -> bool { + match &self.1 { + OperatingMode::Stream => true, + _ => false, + } + } } -impl CipherKey { +pub struct CipherKey { + algorithm: &'static Algorithm, + key: SymmetricCipherKey, +} + +impl + CipherKey +{ + #[inline] + fn get_algorithm(&self) -> &'static Algorithm { + return self.algorithm; + } +} + +impl + CipherKey +{ fn new( - algorithm: &'static Algorithm, + algorithm: &'static Algorithm, key_bytes: &[u8], ) -> Result { - let key = key_bytes.try_into()?; + let key: &[u8; KEYSIZE] = key_bytes.try_into()?; + let key = match algorithm.get_id() { + AlgorithmId::Aes128ctr | AlgorithmId::Aes128cbc => SymmetricCipherKey::aes128(key), + AlgorithmId::Aes256ctr | AlgorithmId::Aes256cbc => SymmetricCipherKey::aes256(key), + AlgorithmId::Chacha20 => SymmetricCipherKey::chacha20(key), + }?; Ok(CipherKey { algorithm, key }) } } -pub struct EncryptingKey { - cipher_key: CipherKey, +pub struct EncryptingKey { + cipher_key: CipherKey, iv: IV, } -impl EncryptingKey { +impl + EncryptingKey +{ fn new( - cipher_key: CipherKey, - ) -> Result, Unspecified> { + cipher_key: CipherKey, + ) -> Result, Unspecified> { let mut iv_bytes = [0u8; IVSIZE]; rand::fill(&mut iv_bytes)?; Ok(EncryptingKey { @@ -93,37 +150,161 @@ impl EncryptingKey { } #[must_use] - fn encrypt_in_place(self, in_out: &mut [u8]) -> Result, Unspecified> { - // TODO: THIS IS A PROOF OF CONCEPT - // do nothing - Ok(self.iv) - } - - fn encrypt_append_padding(self, in_out: INOUT) -> Result, Unspecified> + fn encrypt(self, in_out: &mut INOUT) -> Result, Unspecified> where INOUT: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, { - todo!() + let alg = self.cipher_key.get_algorithm(); + + if alg.is_block_mode() { + let in_out_len = in_out.as_mut().len(); + // This implements PKCS#7 padding scheme, used by aws-lc if we were using EVP_CIPHER API's + let remainder = in_out_len % BLOCK_SIZE; + if remainder == 0 { + let block_size: u8 = BLOCK_SIZE.try_into().map_err(|_| error::Unspecified)?; + in_out.extend(vec![block_size; BLOCK_SIZE].iter()); + } else { + let v: u8 = remainder.try_into().map_err(|_| error::Unspecified)?; + // Heap allocation :( + in_out.extend(vec![v; BLOCK_SIZE - remainder].iter()); + } + } + + let in_out = in_out.as_mut(); + + let mut iv = [0u8; IVSIZE]; + iv.copy_from_slice(self.iv.as_ref()); + + match alg.get_id() { + AlgorithmId::Aes128ctr | AlgorithmId::Aes256ctr => { + let mut num = MaybeUninit::::new(0); + let key = match &self.cipher_key.key { + SymmetricCipherKey::Aes128(_, key) | SymmetricCipherKey::Aes256(_, key) => key, + _ => return Err(error::Unspecified), + }; + + let mut buf = [0u8; BLOCK_SIZE]; + + unsafe { + AES_ctr128_encrypt( + in_out.as_ptr(), + in_out.as_mut_ptr(), + in_out.len(), + key, + iv.as_mut_ptr(), + buf.as_mut_slice().as_mut_ptr(), + num.as_mut_ptr(), + ) + }; + + Zeroize::zeroize(buf.as_mut_slice()) + } + AlgorithmId::Aes128cbc | AlgorithmId::Aes256cbc => { + let key = match &self.cipher_key.key { + SymmetricCipherKey::Aes128(_, key) | SymmetricCipherKey::Aes256(_, key) => key, + _ => return Err(error::Unspecified), + }; + unsafe { + AES_cbc_encrypt( + in_out.as_ptr(), + in_out.as_mut_ptr(), + in_out.len(), + key, + iv.as_mut_ptr(), + AES_ENCRYPT, + ) + } + } + AlgorithmId::Chacha20 => todo!(), + } + Ok(self.iv) } } -pub struct DecryptingKey { - cipher_key: CipherKey, +pub struct DecryptingKey { + cipher_key: CipherKey, iv: IV, } -impl DecryptingKey { +impl + DecryptingKey +{ fn new( - cipher_key: CipherKey, + cipher_key: CipherKey, iv: IV, - ) -> DecryptingKey { + ) -> DecryptingKey { DecryptingKey { cipher_key, iv } } - fn decrypt_in_place(&self, in_out: &mut [u8]) -> Result<(), Unspecified> { - // TODO: THIS IS A PROOF OF CONCEPT - // do nothing - Ok(()) + #[must_use] + fn decrypt(mut self, in_out: &mut [u8]) -> Result { + let alg = self.cipher_key.get_algorithm(); + + let mut final_len = in_out.len(); + + let iv = self.iv.as_mut(); + + match alg.get_id() { + AlgorithmId::Aes128ctr | AlgorithmId::Aes256ctr => { + let mut num = MaybeUninit::::new(0); + let key = match &self.cipher_key.key { + SymmetricCipherKey::Aes128(_, key) | SymmetricCipherKey::Aes256(_, key) => key, + _ => return Err(error::Unspecified), + }; + let mut buf = [0u8; BLOCK_SIZE]; + unsafe { + AES_ctr128_encrypt( + in_out.as_ptr(), + in_out.as_mut_ptr(), + in_out.len(), + key, + iv.as_mut_ptr(), + buf.as_mut_slice().as_mut_ptr(), + num.as_mut_ptr(), + ) + }; + Zeroize::zeroize(buf.as_mut_slice()) + } + AlgorithmId::Aes128cbc | AlgorithmId::Aes256cbc => { + let key = match &self.cipher_key.key { + SymmetricCipherKey::Aes128(_, key) | SymmetricCipherKey::Aes256(_, key) => key, + _ => return Err(error::Unspecified), + }; + unsafe { + AES_cbc_encrypt( + in_out.as_ptr(), + in_out.as_mut_ptr(), + in_out.len(), + key, + iv.as_mut_ptr(), + AES_DECRYPT, + ) + } + } + AlgorithmId::Chacha20 => todo!(), + } + + if alg.is_block_mode() { + let block_size: u8 = BLOCK_SIZE.try_into().map_err(|_| error::Unspecified)?; + + if in_out.len() == 0 || in_out.len() < BLOCK_SIZE { + return Err(error::Unspecified); + } + let padding: u8 = in_out[in_out.len() - 1]; + if padding == 0 || padding > block_size { + return Err(error::Unspecified); + } + + for i in (in_out.len() - padding as usize)..in_out.len() { + if in_out[i] != padding { + return Err(error::Unspecified); + } + } + + final_len = in_out.len() - padding as usize; + }; + + Ok(final_len) } } @@ -247,35 +428,40 @@ mod tests { #[test] fn test_aes_128_ctr() { let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); - let mut input = from_hex("00112233445566778899aabbccddeeff").unwrap(); + let input = from_hex("00112233445566778899aabbccddeeff").unwrap(); let cipher_key = CipherKey::new(&AES128_CTR, &key).unwrap(); let encrypting_key = EncryptingKey::new(cipher_key).unwrap(); - let mut inout = input.as_mut_slice(); + let mut ciphertext = input.clone(); - let decrypt_iv = encrypting_key.encrypt_in_place(inout).unwrap(); + let decrypt_iv = encrypting_key.encrypt(&mut ciphertext).unwrap(); let cipher_key2 = CipherKey::new(&AES128_CTR, &key).unwrap(); let decrypting_key = DecryptingKey::new(cipher_key2, decrypt_iv); - decrypting_key.decrypt_in_place(inout); + decrypting_key.decrypt(&mut ciphertext).unwrap(); + + assert_eq!(input.as_slice(), ciphertext.as_slice()); } #[test] fn test_aes_128_cbc() { let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); - let mut input = from_hex("00112233445566778899aabbccddeeff").unwrap(); + let input = from_hex("00112233445566778899aabbccddeeff").unwrap(); let cipher_key = CipherKey::new(&AES128_CBC, &key).unwrap(); let encrypting_key = EncryptingKey::new(cipher_key).unwrap(); - let mut inout = input.as_mut_slice(); - let decrypt_iv = encrypting_key.encrypt_in_place(inout).unwrap(); + let mut ciphertext = input.clone(); + let decrypt_iv = encrypting_key.encrypt(&mut ciphertext).unwrap(); let cipher_key2 = CipherKey::new(&AES128_CBC, &key).unwrap(); let decrypting_key = DecryptingKey::new(cipher_key2, decrypt_iv); - decrypting_key.decrypt_in_place(inout); + let plaintext_len = decrypting_key.decrypt(&mut ciphertext).unwrap(); + let plaintext = ciphertext.as_slice(); + let plaintext = &plaintext[..plaintext_len]; + assert_eq!(input.as_slice(), plaintext); } } diff --git a/aws-lc-rs/src/iv.rs b/aws-lc-rs/src/iv.rs index def296ea275..164fb82bdde 100644 --- a/aws-lc-rs/src/iv.rs +++ b/aws-lc-rs/src/iv.rs @@ -34,6 +34,13 @@ impl IV { } } +impl AsMut<[u8; L]> for IV { + #[inline] + fn as_mut(&mut self) -> &mut [u8; L] { + &mut self.0 + } +} + impl AsRef<[u8; L]> for IV { #[inline] fn as_ref(&self) -> &[u8; L] { From 8fe49ec12f3cedfd6d4a24a4917bdd82d7539652 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Wed, 17 May 2023 08:48:28 -0400 Subject: [PATCH 03/54] Minor cleanup --- aws-lc-rs/src/cipher.rs | 58 ++++++++++++++++++----------------------- aws-lc-rs/src/iv.rs | 4 +-- 2 files changed, 27 insertions(+), 35 deletions(-) diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index 0b20b042f1f..c795b5dcc0d 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -3,7 +3,7 @@ // Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR ISC -#![allow(dead_code, unused_variables)] +#![allow(dead_code, clippy::module_name_repetitions)] pub(crate) mod aes; pub(crate) mod block; @@ -12,7 +12,7 @@ pub(crate) mod chacha; use crate::cipher::aes::{encrypt_block_aes, Aes128Key, Aes256Key}; use crate::cipher::block::Block; use crate::cipher::chacha::ChaCha20Key; -use crate::error::{self, Unspecified}; +use crate::error::Unspecified; use crate::iv::IV; use crate::rand; use aws_lc::{AES_set_encrypt_key, AES_KEY}; @@ -79,23 +79,17 @@ impl { #[inline] fn get_id(&self) -> &AlgorithmId { - return &self.0; + &self.0 } #[inline] fn is_block_mode(&self) -> bool { - match &self.1 { - OperatingMode::Block => true, - _ => false, - } + matches!(&self.1, OperatingMode::Block) } #[inline] fn is_stream_mode(&self) -> bool { - match &self.1 { - OperatingMode::Stream => true, - _ => false, - } + matches!(&self.1, OperatingMode::Stream) } } @@ -109,7 +103,7 @@ impl { #[inline] fn get_algorithm(&self) -> &'static Algorithm { - return self.algorithm; + self.algorithm } } @@ -149,7 +143,6 @@ impl }) } - #[must_use] fn encrypt(self, in_out: &mut INOUT) -> Result, Unspecified> where INOUT: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, @@ -161,10 +154,10 @@ impl // This implements PKCS#7 padding scheme, used by aws-lc if we were using EVP_CIPHER API's let remainder = in_out_len % BLOCK_SIZE; if remainder == 0 { - let block_size: u8 = BLOCK_SIZE.try_into().map_err(|_| error::Unspecified)?; + let block_size: u8 = BLOCK_SIZE.try_into().map_err(|_| Unspecified)?; in_out.extend(vec![block_size; BLOCK_SIZE].iter()); } else { - let v: u8 = remainder.try_into().map_err(|_| error::Unspecified)?; + let v: u8 = remainder.try_into().map_err(|_| Unspecified)?; // Heap allocation :( in_out.extend(vec![v; BLOCK_SIZE - remainder].iter()); } @@ -180,7 +173,7 @@ impl let mut num = MaybeUninit::::new(0); let key = match &self.cipher_key.key { SymmetricCipherKey::Aes128(_, key) | SymmetricCipherKey::Aes256(_, key) => key, - _ => return Err(error::Unspecified), + SymmetricCipherKey::ChaCha20(_) => return Err(Unspecified), }; let mut buf = [0u8; BLOCK_SIZE]; @@ -194,15 +187,15 @@ impl iv.as_mut_ptr(), buf.as_mut_slice().as_mut_ptr(), num.as_mut_ptr(), - ) + ); }; - Zeroize::zeroize(buf.as_mut_slice()) + Zeroize::zeroize(buf.as_mut_slice()); } AlgorithmId::Aes128cbc | AlgorithmId::Aes256cbc => { let key = match &self.cipher_key.key { SymmetricCipherKey::Aes128(_, key) | SymmetricCipherKey::Aes256(_, key) => key, - _ => return Err(error::Unspecified), + SymmetricCipherKey::ChaCha20(_) => return Err(Unspecified), }; unsafe { AES_cbc_encrypt( @@ -212,7 +205,7 @@ impl key, iv.as_mut_ptr(), AES_ENCRYPT, - ) + ); } } AlgorithmId::Chacha20 => todo!(), @@ -236,7 +229,6 @@ impl DecryptingKey { cipher_key, iv } } - #[must_use] fn decrypt(mut self, in_out: &mut [u8]) -> Result { let alg = self.cipher_key.get_algorithm(); @@ -249,7 +241,7 @@ impl let mut num = MaybeUninit::::new(0); let key = match &self.cipher_key.key { SymmetricCipherKey::Aes128(_, key) | SymmetricCipherKey::Aes256(_, key) => key, - _ => return Err(error::Unspecified), + SymmetricCipherKey::ChaCha20(_) => return Err(Unspecified), }; let mut buf = [0u8; BLOCK_SIZE]; unsafe { @@ -261,14 +253,14 @@ impl iv.as_mut_ptr(), buf.as_mut_slice().as_mut_ptr(), num.as_mut_ptr(), - ) + ); }; - Zeroize::zeroize(buf.as_mut_slice()) + Zeroize::zeroize(buf.as_mut_slice()); } AlgorithmId::Aes128cbc | AlgorithmId::Aes256cbc => { let key = match &self.cipher_key.key { SymmetricCipherKey::Aes128(_, key) | SymmetricCipherKey::Aes256(_, key) => key, - _ => return Err(error::Unspecified), + SymmetricCipherKey::ChaCha20(_) => return Err(Unspecified), }; unsafe { AES_cbc_encrypt( @@ -278,26 +270,26 @@ impl key, iv.as_mut_ptr(), AES_DECRYPT, - ) + ); } } AlgorithmId::Chacha20 => todo!(), } if alg.is_block_mode() { - let block_size: u8 = BLOCK_SIZE.try_into().map_err(|_| error::Unspecified)?; + let block_size: u8 = BLOCK_SIZE.try_into().map_err(|_| Unspecified)?; - if in_out.len() == 0 || in_out.len() < BLOCK_SIZE { - return Err(error::Unspecified); + if in_out.is_empty() || in_out.len() < BLOCK_SIZE { + return Err(Unspecified); } let padding: u8 = in_out[in_out.len() - 1]; if padding == 0 || padding > block_size { - return Err(error::Unspecified); + return Err(Unspecified); } - for i in (in_out.len() - padding as usize)..in_out.len() { - if in_out[i] != padding { - return Err(error::Unspecified); + for item in in_out.iter().skip(in_out.len() - padding as usize) { + if *item != padding { + return Err(Unspecified); } } diff --git a/aws-lc-rs/src/iv.rs b/aws-lc-rs/src/iv.rs index 164fb82bdde..f3bc5c14c1d 100644 --- a/aws-lc-rs/src/iv.rs +++ b/aws-lc-rs/src/iv.rs @@ -29,8 +29,8 @@ impl IV { Self(value) } - pub fn size(&self) -> usize { - return L; + pub fn size() -> usize { + L } } From 0912be83bb5763422b32743f3e8abbfe6c610542 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Wed, 17 May 2023 12:04:28 -0400 Subject: [PATCH 04/54] Add decrypt AES_KEY; add tests --- aws-lc-rs/src/aead/key_inner.rs | 6 +- aws-lc-rs/src/aead/quic.rs | 8 +- aws-lc-rs/src/cipher.rs | 181 ++++++++++++++++++++++++-------- 3 files changed, 147 insertions(+), 48 deletions(-) diff --git a/aws-lc-rs/src/aead/key_inner.rs b/aws-lc-rs/src/aead/key_inner.rs index 04816bae0ac..c97a2d69164 100644 --- a/aws-lc-rs/src/aead/key_inner.rs +++ b/aws-lc-rs/src/aead/key_inner.rs @@ -30,7 +30,7 @@ impl KeyInner { pub(crate) fn new(key: SymmetricCipherKey) -> Result { unsafe { match key { - SymmetricCipherKey::Aes128(..) => { + SymmetricCipherKey::Aes128 { .. } => { let aead = EVP_aead_aes_128_gcm(); let mut aead_ctx = MaybeUninit::::uninit(); @@ -46,7 +46,7 @@ impl KeyInner { } Ok(KeyInner::AES_128_GCM(key, aead_ctx.assume_init())) } - SymmetricCipherKey::Aes256(..) => { + SymmetricCipherKey::Aes256 { .. } => { let aead = EVP_aead_aes_256_gcm(); let mut aead_ctx = MaybeUninit::::uninit(); @@ -62,7 +62,7 @@ impl KeyInner { } Ok(KeyInner::AES_256_GCM(key, aead_ctx.assume_init())) } - SymmetricCipherKey::ChaCha20(..) => { + SymmetricCipherKey::ChaCha20 { .. } => { let aead = EVP_aead_chacha20_poly1305(); let mut aead_ctx = MaybeUninit::::uninit(); diff --git a/aws-lc-rs/src/aead/quic.rs b/aws-lc-rs/src/aead/quic.rs index 6d8bce93a60..a2ad4ad8786 100644 --- a/aws-lc-rs/src/aead/quic.rs +++ b/aws-lc-rs/src/aead/quic.rs @@ -168,10 +168,10 @@ fn cipher_new_mask(key: &KeyInner, sample: Sample) -> Result<[u8; 5], error::Uns let block = block::Block::from(&sample); let encrypted_block = match cipher_key { - SymmetricCipherKey::Aes128(.., aes_key) | SymmetricCipherKey::Aes256(.., aes_key) => { - encrypt_block_aes(aes_key, block) + SymmetricCipherKey::Aes128 { enc_key, .. } | SymmetricCipherKey::Aes256 { enc_key, .. } => { + encrypt_block_aes(enc_key, block) } - SymmetricCipherKey::ChaCha20(key_bytes) => { + SymmetricCipherKey::ChaCha20 { raw_key } => { let plaintext = block.as_ref(); let counter_bytes: &[u8; 4] = plaintext[0..=3] .try_into() @@ -182,7 +182,7 @@ fn cipher_new_mask(key: &KeyInner, sample: Sample) -> Result<[u8; 5], error::Uns let input = block::Block::zero(); unsafe { let counter = std::mem::transmute::<[u8; 4], u32>(*counter_bytes).to_le(); - encrypt_block_chacha20(key_bytes, input, nonce, counter)? + encrypt_block_chacha20(raw_key, input, nonce, counter)? } } }; diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index c795b5dcc0d..f9fc3955073 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -15,7 +15,7 @@ use crate::cipher::chacha::ChaCha20Key; use crate::error::Unspecified; use crate::iv::IV; use crate::rand; -use aws_lc::{AES_set_encrypt_key, AES_KEY}; +use aws_lc::{AES_set_decrypt_key, AES_set_encrypt_key, AES_KEY}; use aws_lc_sys::{AES_cbc_encrypt, AES_ctr128_encrypt, AES_DECRYPT, AES_ENCRYPT}; use std::mem::{size_of, transmute, MaybeUninit}; use std::os::raw::c_uint; @@ -23,9 +23,19 @@ use std::ptr; use zeroize::Zeroize; pub(crate) enum SymmetricCipherKey { - Aes128(Aes128Key, AES_KEY), - Aes256(Aes256Key, AES_KEY), - ChaCha20(ChaCha20Key), + Aes128 { + raw_key: Aes128Key, + enc_key: AES_KEY, + dec_key: AES_KEY, + }, + Aes256 { + raw_key: Aes256Key, + enc_key: AES_KEY, + dec_key: AES_KEY, + }, + ChaCha20 { + raw_key: ChaCha20Key, + }, } unsafe impl Send for SymmetricCipherKey {} @@ -36,12 +46,20 @@ impl Drop for SymmetricCipherKey { fn drop(&mut self) { // Aes128Key, Aes256Key and ChaCha20Key implement Drop separately. match self { - SymmetricCipherKey::Aes128(_, aes_key) | SymmetricCipherKey::Aes256(_, aes_key) => unsafe { + SymmetricCipherKey::Aes128 { + enc_key, dec_key, .. + } + | SymmetricCipherKey::Aes256 { + enc_key, dec_key, .. + } => unsafe { + #[allow(clippy::transmute_ptr_to_ptr)] + let enc_bytes: &mut [u8; size_of::()] = transmute(enc_key); + enc_bytes.zeroize(); #[allow(clippy::transmute_ptr_to_ptr)] - let value: &mut [u8; size_of::()] = transmute(aes_key); - value.zeroize(); + let dec_bytes: &mut [u8; size_of::()] = transmute(dec_key); + dec_bytes.zeroize(); }, - SymmetricCipherKey::ChaCha20(_) => {} + SymmetricCipherKey::ChaCha20 { .. } => {} } } } @@ -132,6 +150,17 @@ pub struct EncryptingKey EncryptingKey { + #[cfg(test)] + fn new_with_iv( + cipher_key: CipherKey, + iv_bytes: [u8; IVSIZE], + ) -> EncryptingKey { + EncryptingKey { + cipher_key, + iv: IV::assume_unique_for_key(iv_bytes), + } + } + fn new( cipher_key: CipherKey, ) -> Result, Unspecified> { @@ -157,9 +186,10 @@ impl let block_size: u8 = BLOCK_SIZE.try_into().map_err(|_| Unspecified)?; in_out.extend(vec![block_size; BLOCK_SIZE].iter()); } else { - let v: u8 = remainder.try_into().map_err(|_| Unspecified)?; + let padding_size = BLOCK_SIZE - remainder; + let v: u8 = padding_size.try_into().map_err(|_| Unspecified)?; // Heap allocation :( - in_out.extend(vec![v; BLOCK_SIZE - remainder].iter()); + in_out.extend(vec![v; padding_size].iter()); } } @@ -172,8 +202,9 @@ impl AlgorithmId::Aes128ctr | AlgorithmId::Aes256ctr => { let mut num = MaybeUninit::::new(0); let key = match &self.cipher_key.key { - SymmetricCipherKey::Aes128(_, key) | SymmetricCipherKey::Aes256(_, key) => key, - SymmetricCipherKey::ChaCha20(_) => return Err(Unspecified), + SymmetricCipherKey::Aes128 { enc_key, .. } + | SymmetricCipherKey::Aes256 { enc_key, .. } => enc_key, + SymmetricCipherKey::ChaCha20 { .. } => return Err(Unspecified), }; let mut buf = [0u8; BLOCK_SIZE]; @@ -194,8 +225,9 @@ impl } AlgorithmId::Aes128cbc | AlgorithmId::Aes256cbc => { let key = match &self.cipher_key.key { - SymmetricCipherKey::Aes128(_, key) | SymmetricCipherKey::Aes256(_, key) => key, - SymmetricCipherKey::ChaCha20(_) => return Err(Unspecified), + SymmetricCipherKey::Aes128 { enc_key, .. } + | SymmetricCipherKey::Aes256 { enc_key, .. } => enc_key, + SymmetricCipherKey::ChaCha20 { .. } => return Err(Unspecified), }; unsafe { AES_cbc_encrypt( @@ -240,8 +272,9 @@ impl AlgorithmId::Aes128ctr | AlgorithmId::Aes256ctr => { let mut num = MaybeUninit::::new(0); let key = match &self.cipher_key.key { - SymmetricCipherKey::Aes128(_, key) | SymmetricCipherKey::Aes256(_, key) => key, - SymmetricCipherKey::ChaCha20(_) => return Err(Unspecified), + SymmetricCipherKey::Aes128 { enc_key, .. } + | SymmetricCipherKey::Aes256 { enc_key, .. } => enc_key, + SymmetricCipherKey::ChaCha20 { .. } => return Err(Unspecified), }; let mut buf = [0u8; BLOCK_SIZE]; unsafe { @@ -259,8 +292,9 @@ impl } AlgorithmId::Aes128cbc | AlgorithmId::Aes256cbc => { let key = match &self.cipher_key.key { - SymmetricCipherKey::Aes128(_, key) | SymmetricCipherKey::Aes256(_, key) => key, - SymmetricCipherKey::ChaCha20(_) => return Err(Unspecified), + SymmetricCipherKey::Aes128 { dec_key, .. } + | SymmetricCipherKey::Aes256 { dec_key, .. } => dec_key, + SymmetricCipherKey::ChaCha20 { .. } => return Err(Unspecified), }; unsafe { AES_cbc_encrypt( @@ -307,23 +341,35 @@ impl SymmetricCipherKey { } unsafe { - let mut aes_key = MaybeUninit::::uninit(); + let mut enc_key = MaybeUninit::::uninit(); #[allow(clippy::cast_possible_truncation)] if 0 != AES_set_encrypt_key( key_bytes.as_ptr(), (key_bytes.len() * 8) as c_uint, - aes_key.as_mut_ptr(), + enc_key.as_mut_ptr(), + ) { + return Err(Unspecified); + } + let enc_key = enc_key.assume_init(); + + let mut dec_key = MaybeUninit::::uninit(); + #[allow(clippy::cast_possible_truncation)] + if 0 != AES_set_decrypt_key( + key_bytes.as_ptr(), + (key_bytes.len() * 8) as c_uint, + dec_key.as_mut_ptr(), ) { return Err(Unspecified); } - let aes_key = aes_key.assume_init(); + let dec_key = dec_key.assume_init(); let mut kb = MaybeUninit::<[u8; 16]>::uninit(); ptr::copy_nonoverlapping(key_bytes.as_ptr(), kb.as_mut_ptr().cast(), 16); - Ok(SymmetricCipherKey::Aes128( - Aes128Key(kb.assume_init()), - aes_key, - )) + Ok(SymmetricCipherKey::Aes128 { + raw_key: Aes128Key(kb.assume_init()), + enc_key, + dec_key, + }) } } @@ -332,23 +378,35 @@ impl SymmetricCipherKey { return Err(Unspecified); } unsafe { - let mut aes_key = MaybeUninit::::uninit(); + let mut enc_key = MaybeUninit::::uninit(); #[allow(clippy::cast_possible_truncation)] if 0 != AES_set_encrypt_key( key_bytes.as_ptr(), (key_bytes.len() * 8) as c_uint, - aes_key.as_mut_ptr(), + enc_key.as_mut_ptr(), ) { return Err(Unspecified); } - let aes_key = aes_key.assume_init(); + let enc_key = enc_key.assume_init(); + + let mut dec_key = MaybeUninit::::uninit(); + #[allow(clippy::cast_possible_truncation)] + if 0 != AES_set_decrypt_key( + key_bytes.as_ptr(), + (key_bytes.len() * 8) as c_uint, + dec_key.as_mut_ptr(), + ) { + return Err(Unspecified); + } + let dec_key = dec_key.assume_init(); let mut kb = MaybeUninit::<[u8; 32]>::uninit(); ptr::copy_nonoverlapping(key_bytes.as_ptr(), kb.as_mut_ptr().cast(), 32); - Ok(SymmetricCipherKey::Aes256( - Aes256Key(kb.assume_init()), - aes_key, - )) + Ok(SymmetricCipherKey::Aes256 { + raw_key: Aes256Key(kb.assume_init()), + enc_key, + dec_key, + }) } } @@ -359,16 +417,18 @@ impl SymmetricCipherKey { let mut kb = MaybeUninit::<[u8; 32]>::uninit(); unsafe { ptr::copy_nonoverlapping(key_bytes.as_ptr(), kb.as_mut_ptr().cast(), 32); - Ok(SymmetricCipherKey::ChaCha20(ChaCha20Key(kb.assume_init()))) + Ok(SymmetricCipherKey::ChaCha20 { + raw_key: ChaCha20Key(kb.assume_init()), + }) } } #[inline] pub(super) fn key_bytes(&self) -> &[u8] { match self { - SymmetricCipherKey::Aes128(bytes, ..) => &bytes.0, - SymmetricCipherKey::Aes256(bytes, ..) => &bytes.0, - SymmetricCipherKey::ChaCha20(bytes) => &bytes.0, + SymmetricCipherKey::Aes128 { raw_key, .. } => &raw_key.0, + SymmetricCipherKey::Aes256 { raw_key, .. } => &raw_key.0, + SymmetricCipherKey::ChaCha20 { raw_key, .. } => &raw_key.0, } } @@ -376,10 +436,9 @@ impl SymmetricCipherKey { #[inline] pub(crate) fn encrypt_block(&self, block: Block) -> Block { match self { - SymmetricCipherKey::Aes128(.., aes_key) | SymmetricCipherKey::Aes256(.., aes_key) => { - encrypt_block_aes(aes_key, block) - } - SymmetricCipherKey::ChaCha20(..) => panic!("Unsupported algorithm!"), + SymmetricCipherKey::Aes128 { enc_key, .. } + | SymmetricCipherKey::Aes256 { enc_key, .. } => encrypt_block_aes(enc_key, block), + SymmetricCipherKey::ChaCha20 { .. } => panic!("Unsupported algorithm!"), } } } @@ -438,7 +497,27 @@ mod tests { } #[test] - fn test_aes_128_cbc() { + fn test_aes_128_cbc_15_bytes() { + let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); + let input = from_hex("00112233445566778899aabbccddee").unwrap(); + + let cipher_key = CipherKey::new(&AES128_CBC, &key).unwrap(); + let encrypting_key = EncryptingKey::new(cipher_key).unwrap(); + + let mut ciphertext = input.clone(); + let decrypt_iv = encrypting_key.encrypt(&mut ciphertext).unwrap(); + + let cipher_key2 = CipherKey::new(&AES128_CBC, &key).unwrap(); + let decrypting_key = DecryptingKey::new(cipher_key2, decrypt_iv); + + let plaintext_len = decrypting_key.decrypt(&mut ciphertext).unwrap(); + let plaintext = ciphertext.as_slice(); + let plaintext = &plaintext[..plaintext_len]; + assert_eq!(input.as_slice(), plaintext); + } + + #[test] + fn test_aes_128_cbc_16_bytes() { let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); let input = from_hex("00112233445566778899aabbccddeeff").unwrap(); @@ -456,4 +535,24 @@ mod tests { let plaintext = &plaintext[..plaintext_len]; assert_eq!(input.as_slice(), plaintext); } + + #[test] + fn test_aes_128_cbc_17_bytes() { + let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); + let input = from_hex("00112233445566778899aabbccddeeff00").unwrap(); + + let cipher_key = CipherKey::new(&AES128_CBC, &key).unwrap(); + let encrypting_key = EncryptingKey::new(cipher_key).unwrap(); + + let mut ciphertext = input.clone(); + let decrypt_iv = encrypting_key.encrypt(&mut ciphertext).unwrap(); + + let cipher_key2 = CipherKey::new(&AES128_CBC, &key).unwrap(); + let decrypting_key = DecryptingKey::new(cipher_key2, decrypt_iv); + + let plaintext_len = decrypting_key.decrypt(&mut ciphertext).unwrap(); + let plaintext = ciphertext.as_slice(); + let plaintext = &plaintext[..plaintext_len]; + assert_eq!(input.as_slice(), plaintext); + } } From 2394ed48f05b39f89b6dd35e8b6fc55373e2ab62 Mon Sep 17 00:00:00 2001 From: Sean McGrail Date: Wed, 17 May 2023 09:24:39 -0700 Subject: [PATCH 05/54] Add some constants --- aws-lc-rs/src/cipher.rs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index f9fc3955073..24c9d62d758 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -82,15 +82,25 @@ pub struct Algorithm = +const AES128_KEY_LEN: usize = 16; +const AES256_KEY_LEN: usize = 32; +const AES_IV_LEN: usize = 16; +const AES_BLOCK_LEN: usize = 16; + +const CHACHA20_KEY_LEN: usize = 32; +const CHACHA20_IV_LEN: usize = 12; +const CHACHA20_BLOCK_LEN: usize = 64; + +pub const AES128_CTR: Algorithm = Algorithm(AlgorithmId::Aes128ctr, OperatingMode::Stream); -pub const AES128_CBC: Algorithm<16, 16, 16> = +pub const AES128_CBC: Algorithm = Algorithm(AlgorithmId::Aes128cbc, OperatingMode::Block); -pub const AES256_CTR: Algorithm<32, 16, 16> = +pub const AES256_CTR: Algorithm = Algorithm(AlgorithmId::Aes256ctr, OperatingMode::Stream); -pub const AES256_CBC: Algorithm<32, 16, 16> = +pub const AES256_CBC: Algorithm = Algorithm(AlgorithmId::Aes256cbc, OperatingMode::Block); -pub const CHACHA20: Algorithm<32, 12, 64> = Algorithm(AlgorithmId::Chacha20, OperatingMode::Stream); +pub const CHACHA20: Algorithm = + Algorithm(AlgorithmId::Chacha20, OperatingMode::Stream); impl Algorithm From d11bfe71afe709685fd883fc04b1cb1b5b5017e2 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Wed, 17 May 2023 12:43:26 -0400 Subject: [PATCH 06/54] Fix clippy for FIPS --- aws-lc-rs/src/cipher.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index 24c9d62d758..2138ca29b68 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -15,8 +15,10 @@ use crate::cipher::chacha::ChaCha20Key; use crate::error::Unspecified; use crate::iv::IV; use crate::rand; -use aws_lc::{AES_set_decrypt_key, AES_set_encrypt_key, AES_KEY}; -use aws_lc_sys::{AES_cbc_encrypt, AES_ctr128_encrypt, AES_DECRYPT, AES_ENCRYPT}; +use aws_lc::{ + AES_cbc_encrypt, AES_ctr128_encrypt, AES_set_decrypt_key, AES_set_encrypt_key, AES_DECRYPT, + AES_ENCRYPT, AES_KEY, +}; use std::mem::{size_of, transmute, MaybeUninit}; use std::os::raw::c_uint; use std::ptr; From ad645887926be58bfc8fe518d0008c635d11d5a7 Mon Sep 17 00:00:00 2001 From: Sean McGrail Date: Wed, 17 May 2023 10:03:52 -0700 Subject: [PATCH 07/54] Make certian items public, padding strategy --- aws-lc-rs/src/cipher.rs | 221 +++++++++++++++++++++------------------- aws-lc-rs/src/lib.rs | 12 +-- 2 files changed, 119 insertions(+), 114 deletions(-) diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index 2138ca29b68..a31357dede2 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -66,46 +66,51 @@ impl Drop for SymmetricCipherKey { } } +#[derive(Clone, Copy)] enum AlgorithmId { Aes128ctr, Aes128cbc, Aes256ctr, Aes256cbc, - Chacha20, } +#[derive(Clone, Copy)] +enum PaddingStrategy { + Unpadded, + PKCS7, +} + +#[derive(Clone, Copy)] enum OperatingMode { - Block, + Block(PaddingStrategy), Stream, } -pub struct Algorithm( +pub struct Algorithm( AlgorithmId, OperatingMode, ); -const AES128_KEY_LEN: usize = 16; -const AES256_KEY_LEN: usize = 32; -const AES_IV_LEN: usize = 16; +pub const AES_128_KEY_LEN: usize = 16; +pub const AES_256_KEY_LEN: usize = 32; +pub const AES_IV_LEN: usize = 16; const AES_BLOCK_LEN: usize = 16; -const CHACHA20_KEY_LEN: usize = 32; -const CHACHA20_IV_LEN: usize = 12; -const CHACHA20_BLOCK_LEN: usize = 64; - -pub const AES128_CTR: Algorithm = +pub const AES_128_CTR: Algorithm = Algorithm(AlgorithmId::Aes128ctr, OperatingMode::Stream); -pub const AES128_CBC: Algorithm = - Algorithm(AlgorithmId::Aes128cbc, OperatingMode::Block); -pub const AES256_CTR: Algorithm = +pub const AES_128_CBC_PKCS7_PADDING: Algorithm = Algorithm( + AlgorithmId::Aes128cbc, + OperatingMode::Block(PaddingStrategy::PKCS7), +); +pub const AES_256_CTR: Algorithm = Algorithm(AlgorithmId::Aes256ctr, OperatingMode::Stream); -pub const AES256_CBC: Algorithm = - Algorithm(AlgorithmId::Aes256cbc, OperatingMode::Block); -pub const CHACHA20: Algorithm = - Algorithm(AlgorithmId::Chacha20, OperatingMode::Stream); +pub const AES_256_CBC_PKCS7_PADDING: Algorithm = Algorithm( + AlgorithmId::Aes256cbc, + OperatingMode::Block(PaddingStrategy::PKCS7), +); -impl - Algorithm +impl + Algorithm { #[inline] fn get_id(&self) -> &AlgorithmId { @@ -113,70 +118,64 @@ impl } #[inline] - fn is_block_mode(&self) -> bool { - matches!(&self.1, OperatingMode::Block) - } - - #[inline] - fn is_stream_mode(&self) -> bool { - matches!(&self.1, OperatingMode::Stream) + fn get_operating_mode(&self) -> &OperatingMode { + return &self.1; } } -pub struct CipherKey { - algorithm: &'static Algorithm, +pub struct CipherKey { + algorithm: &'static Algorithm, key: SymmetricCipherKey, } -impl - CipherKey +impl + CipherKey { #[inline] - fn get_algorithm(&self) -> &'static Algorithm { + fn get_algorithm(&self) -> &'static Algorithm { self.algorithm } } -impl - CipherKey +impl + CipherKey { - fn new( - algorithm: &'static Algorithm, + pub fn new( + algorithm: &'static Algorithm, key_bytes: &[u8], ) -> Result { - let key: &[u8; KEYSIZE] = key_bytes.try_into()?; + let key: &[u8; KEY_LEN] = key_bytes.try_into()?; let key = match algorithm.get_id() { AlgorithmId::Aes128ctr | AlgorithmId::Aes128cbc => SymmetricCipherKey::aes128(key), AlgorithmId::Aes256ctr | AlgorithmId::Aes256cbc => SymmetricCipherKey::aes256(key), - AlgorithmId::Chacha20 => SymmetricCipherKey::chacha20(key), }?; Ok(CipherKey { algorithm, key }) } } -pub struct EncryptingKey { - cipher_key: CipherKey, - iv: IV, +pub struct EncryptingKey { + cipher_key: CipherKey, + iv: IV, } -impl - EncryptingKey +impl + EncryptingKey { #[cfg(test)] fn new_with_iv( - cipher_key: CipherKey, - iv_bytes: [u8; IVSIZE], - ) -> EncryptingKey { + cipher_key: CipherKey, + iv_bytes: [u8; IV_LEN], + ) -> EncryptingKey { EncryptingKey { cipher_key, iv: IV::assume_unique_for_key(iv_bytes), } } - fn new( - cipher_key: CipherKey, - ) -> Result, Unspecified> { - let mut iv_bytes = [0u8; IVSIZE]; + pub fn new( + cipher_key: CipherKey, + ) -> Result, Unspecified> { + let mut iv_bytes = [0u8; IV_LEN]; rand::fill(&mut iv_bytes)?; Ok(EncryptingKey { cipher_key, @@ -184,30 +183,36 @@ impl }) } - fn encrypt(self, in_out: &mut INOUT) -> Result, Unspecified> + pub fn encrypt(self, in_out: &mut InOut) -> Result, Unspecified> where - INOUT: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, + InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, { let alg = self.cipher_key.get_algorithm(); - if alg.is_block_mode() { - let in_out_len = in_out.as_mut().len(); - // This implements PKCS#7 padding scheme, used by aws-lc if we were using EVP_CIPHER API's - let remainder = in_out_len % BLOCK_SIZE; - if remainder == 0 { - let block_size: u8 = BLOCK_SIZE.try_into().map_err(|_| Unspecified)?; - in_out.extend(vec![block_size; BLOCK_SIZE].iter()); - } else { - let padding_size = BLOCK_SIZE - remainder; - let v: u8 = padding_size.try_into().map_err(|_| Unspecified)?; - // Heap allocation :( - in_out.extend(vec![v; padding_size].iter()); - } + match alg.get_operating_mode() { + OperatingMode::Block(strategy) => match strategy { + PaddingStrategy::PKCS7 => { + let in_out_len = in_out.as_mut().len(); + // This implements PKCS#7 padding scheme, used by aws-lc if we were using EVP_CIPHER API's + let remainder = in_out_len % BLOCK_LEN; + if remainder == 0 { + let block_size: u8 = BLOCK_LEN.try_into().map_err(|_| Unspecified)?; + in_out.extend(vec![block_size; BLOCK_LEN].iter()); + } else { + let padding_size = BLOCK_LEN - remainder; + let v: u8 = padding_size.try_into().map_err(|_| Unspecified)?; + // Heap allocation :( + in_out.extend(vec![v; padding_size].iter()); + } + } + PaddingStrategy::Unpadded => {}, + }, + OperatingMode::Stream => {}, } let in_out = in_out.as_mut(); - let mut iv = [0u8; IVSIZE]; + let mut iv = [0u8; IV_LEN]; iv.copy_from_slice(self.iv.as_ref()); match alg.get_id() { @@ -219,7 +224,7 @@ impl SymmetricCipherKey::ChaCha20 { .. } => return Err(Unspecified), }; - let mut buf = [0u8; BLOCK_SIZE]; + let mut buf = [0u8; BLOCK_LEN]; unsafe { AES_ctr128_encrypt( @@ -252,28 +257,27 @@ impl ); } } - AlgorithmId::Chacha20 => todo!(), } Ok(self.iv) } } -pub struct DecryptingKey { - cipher_key: CipherKey, - iv: IV, +pub struct DecryptingKey { + cipher_key: CipherKey, + iv: IV, } -impl - DecryptingKey +impl + DecryptingKey { - fn new( - cipher_key: CipherKey, - iv: IV, - ) -> DecryptingKey { + pub fn new( + cipher_key: CipherKey, + iv: IV, + ) -> DecryptingKey { DecryptingKey { cipher_key, iv } } - fn decrypt(mut self, in_out: &mut [u8]) -> Result { + pub fn decrypt(mut self, in_out: &mut [u8]) -> Result { let alg = self.cipher_key.get_algorithm(); let mut final_len = in_out.len(); @@ -288,7 +292,7 @@ impl | SymmetricCipherKey::Aes256 { enc_key, .. } => enc_key, SymmetricCipherKey::ChaCha20 { .. } => return Err(Unspecified), }; - let mut buf = [0u8; BLOCK_SIZE]; + let mut buf = [0u8; BLOCK_LEN]; unsafe { AES_ctr128_encrypt( in_out.as_ptr(), @@ -319,28 +323,33 @@ impl ); } } - AlgorithmId::Chacha20 => todo!(), } - if alg.is_block_mode() { - let block_size: u8 = BLOCK_SIZE.try_into().map_err(|_| Unspecified)?; - - if in_out.is_empty() || in_out.len() < BLOCK_SIZE { - return Err(Unspecified); - } - let padding: u8 = in_out[in_out.len() - 1]; - if padding == 0 || padding > block_size { - return Err(Unspecified); - } - - for item in in_out.iter().skip(in_out.len() - padding as usize) { - if *item != padding { - return Err(Unspecified); + match alg.get_operating_mode() { + OperatingMode::Block(strategy) => match strategy { + PaddingStrategy::PKCS7 => { + let block_size: u8 = BLOCK_LEN.try_into().map_err(|_| Unspecified)?; + + if in_out.is_empty() || in_out.len() < BLOCK_LEN { + return Err(Unspecified); + } + let padding: u8 = in_out[in_out.len() - 1]; + if padding == 0 || padding > block_size { + return Err(Unspecified); + } + + for item in in_out.iter().skip(in_out.len() - padding as usize) { + if *item != padding { + return Err(Unspecified); + } + } + + final_len = in_out.len() - padding as usize; } - } - - final_len = in_out.len() - padding as usize; - }; + PaddingStrategy::Unpadded => {} + }, + OperatingMode::Stream => {} + } Ok(final_len) } @@ -493,14 +502,14 @@ mod tests { let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); let input = from_hex("00112233445566778899aabbccddeeff").unwrap(); - let cipher_key = CipherKey::new(&AES128_CTR, &key).unwrap(); + let cipher_key = CipherKey::new(&AES_128_CTR, &key).unwrap(); let encrypting_key = EncryptingKey::new(cipher_key).unwrap(); let mut ciphertext = input.clone(); let decrypt_iv = encrypting_key.encrypt(&mut ciphertext).unwrap(); - let cipher_key2 = CipherKey::new(&AES128_CTR, &key).unwrap(); + let cipher_key2 = CipherKey::new(&AES_128_CTR, &key).unwrap(); let decrypting_key = DecryptingKey::new(cipher_key2, decrypt_iv); decrypting_key.decrypt(&mut ciphertext).unwrap(); @@ -513,13 +522,13 @@ mod tests { let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); let input = from_hex("00112233445566778899aabbccddee").unwrap(); - let cipher_key = CipherKey::new(&AES128_CBC, &key).unwrap(); + let cipher_key = CipherKey::new(&AES_128_CBC_PKCS7_PADDING, &key).unwrap(); let encrypting_key = EncryptingKey::new(cipher_key).unwrap(); let mut ciphertext = input.clone(); let decrypt_iv = encrypting_key.encrypt(&mut ciphertext).unwrap(); - let cipher_key2 = CipherKey::new(&AES128_CBC, &key).unwrap(); + let cipher_key2 = CipherKey::new(&AES_128_CBC_PKCS7_PADDING, &key).unwrap(); let decrypting_key = DecryptingKey::new(cipher_key2, decrypt_iv); let plaintext_len = decrypting_key.decrypt(&mut ciphertext).unwrap(); @@ -533,13 +542,13 @@ mod tests { let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); let input = from_hex("00112233445566778899aabbccddeeff").unwrap(); - let cipher_key = CipherKey::new(&AES128_CBC, &key).unwrap(); + let cipher_key = CipherKey::new(&AES_128_CBC_PKCS7_PADDING, &key).unwrap(); let encrypting_key = EncryptingKey::new(cipher_key).unwrap(); let mut ciphertext = input.clone(); let decrypt_iv = encrypting_key.encrypt(&mut ciphertext).unwrap(); - let cipher_key2 = CipherKey::new(&AES128_CBC, &key).unwrap(); + let cipher_key2 = CipherKey::new(&AES_128_CBC_PKCS7_PADDING, &key).unwrap(); let decrypting_key = DecryptingKey::new(cipher_key2, decrypt_iv); let plaintext_len = decrypting_key.decrypt(&mut ciphertext).unwrap(); @@ -553,13 +562,13 @@ mod tests { let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); let input = from_hex("00112233445566778899aabbccddeeff00").unwrap(); - let cipher_key = CipherKey::new(&AES128_CBC, &key).unwrap(); + let cipher_key = CipherKey::new(&AES_128_CBC_PKCS7_PADDING, &key).unwrap(); let encrypting_key = EncryptingKey::new(cipher_key).unwrap(); let mut ciphertext = input.clone(); let decrypt_iv = encrypting_key.encrypt(&mut ciphertext).unwrap(); - let cipher_key2 = CipherKey::new(&AES128_CBC, &key).unwrap(); + let cipher_key2 = CipherKey::new(&AES_128_CBC_PKCS7_PADDING, &key).unwrap(); let decrypting_key = DecryptingKey::new(cipher_key2, decrypt_iv); let plaintext_len = decrypting_key.decrypt(&mut ciphertext).unwrap(); diff --git a/aws-lc-rs/src/lib.rs b/aws-lc-rs/src/lib.rs index 21f587a752d..f7087976aba 100644 --- a/aws-lc-rs/src/lib.rs +++ b/aws-lc-rs/src/lib.rs @@ -137,22 +137,18 @@ pub mod rand; pub mod signature; pub mod test; -mod cipher; - -mod rsa; - -mod debug; - -mod endian; - mod bn; mod cbb; mod cbs; +pub mod cipher; +mod debug; mod ec; mod ed25519; +mod endian; mod evp_pkey; mod iv; mod ptr; +mod rsa; use aws_lc::{ CRYPTO_library_init, ERR_error_string, ERR_get_error, FIPS_mode, ERR_GET_FUNC, ERR_GET_LIB, From 6d7b128bba4af5abe0d34c2b1c7d7c567760cad8 Mon Sep 17 00:00:00 2001 From: Sean McGrail Date: Wed, 17 May 2023 11:47:37 -0700 Subject: [PATCH 08/54] Fix format --- aws-lc-rs/src/cipher.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index a31357dede2..36e260c063b 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -98,16 +98,18 @@ const AES_BLOCK_LEN: usize = 16; pub const AES_128_CTR: Algorithm = Algorithm(AlgorithmId::Aes128ctr, OperatingMode::Stream); -pub const AES_128_CBC_PKCS7_PADDING: Algorithm = Algorithm( - AlgorithmId::Aes128cbc, - OperatingMode::Block(PaddingStrategy::PKCS7), -); +pub const AES_128_CBC_PKCS7_PADDING: Algorithm = + Algorithm( + AlgorithmId::Aes128cbc, + OperatingMode::Block(PaddingStrategy::PKCS7), + ); pub const AES_256_CTR: Algorithm = Algorithm(AlgorithmId::Aes256ctr, OperatingMode::Stream); -pub const AES_256_CBC_PKCS7_PADDING: Algorithm = Algorithm( - AlgorithmId::Aes256cbc, - OperatingMode::Block(PaddingStrategy::PKCS7), -); +pub const AES_256_CBC_PKCS7_PADDING: Algorithm = + Algorithm( + AlgorithmId::Aes256cbc, + OperatingMode::Block(PaddingStrategy::PKCS7), + ); impl Algorithm @@ -205,9 +207,9 @@ impl in_out.extend(vec![v; padding_size].iter()); } } - PaddingStrategy::Unpadded => {}, + PaddingStrategy::Unpadded => {} }, - OperatingMode::Stream => {}, + OperatingMode::Stream => {} } let in_out = in_out.as_mut(); From 5e6ee99ed13b5de5dc88afaed01b9eb6736f0bb8 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Wed, 17 May 2023 15:30:19 -0400 Subject: [PATCH 09/54] Fix clippy --- aws-lc-rs/src/cipher.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index 36e260c063b..7c28f65b321 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -3,7 +3,11 @@ // Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR ISC -#![allow(dead_code, clippy::module_name_repetitions)] +#![allow( + missing_docs, + clippy::missing_errors_doc, + clippy::module_name_repetitions +)] pub(crate) mod aes; pub(crate) mod block; @@ -74,6 +78,7 @@ enum AlgorithmId { Aes256cbc, } +#[allow(dead_code)] #[derive(Clone, Copy)] enum PaddingStrategy { Unpadded, @@ -121,7 +126,7 @@ impl #[inline] fn get_operating_mode(&self) -> &OperatingMode { - return &self.1; + &self.1 } } @@ -272,6 +277,7 @@ pub struct DecryptingKey DecryptingKey { + #[must_use] pub fn new( cipher_key: CipherKey, iv: IV, From b80b8f0e60ce2cf4474bcf22cb81bd0662caa181 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Wed, 17 May 2023 15:57:42 -0400 Subject: [PATCH 10/54] Change decrypt return value --- aws-lc-rs/src/cipher.rs | 109 ++++++++++++++++++---------------------- 1 file changed, 49 insertions(+), 60 deletions(-) diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index 7c28f65b321..0b78983b27c 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -285,7 +285,10 @@ impl DecryptingKey { cipher_key, iv } } - pub fn decrypt(mut self, in_out: &mut [u8]) -> Result { + pub fn decrypt<'in_out>( + mut self, + in_out: &'in_out mut [u8], + ) -> Result<&'in_out mut [u8], Unspecified> { let alg = self.cipher_key.get_algorithm(); let mut final_len = in_out.len(); @@ -359,7 +362,7 @@ impl OperatingMode::Stream => {} } - Ok(final_len) + Ok(&mut in_out[0..final_len]) } } @@ -505,83 +508,69 @@ mod tests { assert_eq!(expected_result.as_slice(), result.as_ref()); } - #[test] - fn test_aes_128_ctr() { - let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); - let input = from_hex("00112233445566778899aabbccddeeff").unwrap(); + fn helper_test_cipher_n_bytes< + const KEY_LEN: usize, + const IV_LEN: usize, + const BLOCK_LEN: usize, + >( + key: &[u8], + alg: &'static Algorithm, + n: usize, + ) { + let mut input: Vec = Vec::with_capacity(n); + for i in 0..n { + let byte: u8 = i.try_into().unwrap(); + input.push(byte); + } - let cipher_key = CipherKey::new(&AES_128_CTR, &key).unwrap(); + let cipher_key = CipherKey::new(alg, &key).unwrap(); let encrypting_key = EncryptingKey::new(cipher_key).unwrap(); let mut ciphertext = input.clone(); - let decrypt_iv = encrypting_key.encrypt(&mut ciphertext).unwrap(); - let cipher_key2 = CipherKey::new(&AES_128_CTR, &key).unwrap(); - let decrypting_key = DecryptingKey::new(cipher_key2, decrypt_iv); - - decrypting_key.decrypt(&mut ciphertext).unwrap(); - - assert_eq!(input.as_slice(), ciphertext.as_slice()); - } + assert_ne!(input.as_slice(), ciphertext); - #[test] - fn test_aes_128_cbc_15_bytes() { - let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); - let input = from_hex("00112233445566778899aabbccddee").unwrap(); - - let cipher_key = CipherKey::new(&AES_128_CBC_PKCS7_PADDING, &key).unwrap(); - let encrypting_key = EncryptingKey::new(cipher_key).unwrap(); - - let mut ciphertext = input.clone(); - let decrypt_iv = encrypting_key.encrypt(&mut ciphertext).unwrap(); - - let cipher_key2 = CipherKey::new(&AES_128_CBC_PKCS7_PADDING, &key).unwrap(); + let cipher_key2 = CipherKey::new(alg, &key).unwrap(); let decrypting_key = DecryptingKey::new(cipher_key2, decrypt_iv); - let plaintext_len = decrypting_key.decrypt(&mut ciphertext).unwrap(); - let plaintext = ciphertext.as_slice(); - let plaintext = &plaintext[..plaintext_len]; + let plaintext = decrypting_key.decrypt(&mut ciphertext).unwrap(); assert_eq!(input.as_slice(), plaintext); } #[test] - fn test_aes_128_cbc_16_bytes() { + fn test_aes_128_cbc() { let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); - let input = from_hex("00112233445566778899aabbccddeeff").unwrap(); - - let cipher_key = CipherKey::new(&AES_128_CBC_PKCS7_PADDING, &key).unwrap(); - let encrypting_key = EncryptingKey::new(cipher_key).unwrap(); - - let mut ciphertext = input.clone(); - let decrypt_iv = encrypting_key.encrypt(&mut ciphertext).unwrap(); - - let cipher_key2 = CipherKey::new(&AES_128_CBC_PKCS7_PADDING, &key).unwrap(); - let decrypting_key = DecryptingKey::new(cipher_key2, decrypt_iv); + for i in 0..49 { + helper_test_cipher_n_bytes(key.as_slice(), &AES_128_CBC_PKCS7_PADDING, i); + } + } - let plaintext_len = decrypting_key.decrypt(&mut ciphertext).unwrap(); - let plaintext = ciphertext.as_slice(); - let plaintext = &plaintext[..plaintext_len]; - assert_eq!(input.as_slice(), plaintext); + #[test] + fn test_aes_256_cbc() { + let key = + from_hex("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f").unwrap(); + for i in 0..49 { + helper_test_cipher_n_bytes(key.as_slice(), &AES_256_CBC_PKCS7_PADDING, i); + } } #[test] - fn test_aes_128_cbc_17_bytes() { + fn test_aes_128_ctr() { let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); - let input = from_hex("00112233445566778899aabbccddeeff00").unwrap(); - - let cipher_key = CipherKey::new(&AES_128_CBC_PKCS7_PADDING, &key).unwrap(); - let encrypting_key = EncryptingKey::new(cipher_key).unwrap(); - - let mut ciphertext = input.clone(); - let decrypt_iv = encrypting_key.encrypt(&mut ciphertext).unwrap(); - - let cipher_key2 = CipherKey::new(&AES_128_CBC_PKCS7_PADDING, &key).unwrap(); - let decrypting_key = DecryptingKey::new(cipher_key2, decrypt_iv); + // TODO: test 0 bytes. + for i in 1..49 { + helper_test_cipher_n_bytes(key.as_slice(), &AES_128_CTR, i); + } + } - let plaintext_len = decrypting_key.decrypt(&mut ciphertext).unwrap(); - let plaintext = ciphertext.as_slice(); - let plaintext = &plaintext[..plaintext_len]; - assert_eq!(input.as_slice(), plaintext); + #[test] + fn test_aes_256_ctr() { + let key = + from_hex("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f").unwrap(); + // TODO: test 0 bytes. + for i in 1..49 { + helper_test_cipher_n_bytes(key.as_slice(), &AES_256_CTR, i); + } } } From db0d53ef242dc3d99ca1ca2b91461c515bc1e6ce Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Wed, 17 May 2023 16:49:57 -0400 Subject: [PATCH 11/54] More unit tests --- aws-lc-rs/src/cipher.rs | 100 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 90 insertions(+), 10 deletions(-) diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index 0b78983b27c..71bc2718431 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -285,10 +285,7 @@ impl DecryptingKey { cipher_key, iv } } - pub fn decrypt<'in_out>( - mut self, - in_out: &'in_out mut [u8], - ) -> Result<&'in_out mut [u8], Unspecified> { + pub fn decrypt(mut self, in_out: &mut [u8]) -> Result<&mut [u8], Unspecified> { let alg = self.cipher_key.get_algorithm(); let mut final_len = in_out.len(); @@ -523,7 +520,7 @@ mod tests { input.push(byte); } - let cipher_key = CipherKey::new(alg, &key).unwrap(); + let cipher_key = CipherKey::new(alg, key).unwrap(); let encrypting_key = EncryptingKey::new(cipher_key).unwrap(); let mut ciphertext = input.clone(); @@ -531,7 +528,7 @@ mod tests { assert_ne!(input.as_slice(), ciphertext); - let cipher_key2 = CipherKey::new(alg, &key).unwrap(); + let cipher_key2 = CipherKey::new(alg, key).unwrap(); let decrypting_key = DecryptingKey::new(cipher_key2, decrypt_iv); let plaintext = decrypting_key.decrypt(&mut ciphertext).unwrap(); @@ -541,7 +538,7 @@ mod tests { #[test] fn test_aes_128_cbc() { let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); - for i in 0..49 { + for i in 0..=50 { helper_test_cipher_n_bytes(key.as_slice(), &AES_128_CBC_PKCS7_PADDING, i); } } @@ -550,7 +547,7 @@ mod tests { fn test_aes_256_cbc() { let key = from_hex("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f").unwrap(); - for i in 0..49 { + for i in 0..=50 { helper_test_cipher_n_bytes(key.as_slice(), &AES_256_CBC_PKCS7_PADDING, i); } } @@ -559,7 +556,7 @@ mod tests { fn test_aes_128_ctr() { let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); // TODO: test 0 bytes. - for i in 1..49 { + for i in 1..=50 { helper_test_cipher_n_bytes(key.as_slice(), &AES_128_CTR, i); } } @@ -569,8 +566,91 @@ mod tests { let key = from_hex("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f").unwrap(); // TODO: test 0 bytes. - for i in 1..49 { + for i in 1..=50 { helper_test_cipher_n_bytes(key.as_slice(), &AES_256_CTR, i); } } + + #[test] + fn test_iv_aes_128_cbc_16_bytes() { + let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); + let input = from_hex("00112233445566778899aabbccddeeff").unwrap(); + let expected_ciphertext = + from_hex("69c4e0d86a7b0430d8cdb78070b4c55a9e978e6d16b086570ef794ef97984232").unwrap(); + let iv = [0u8; AES_IV_LEN]; + let cipher_key = CipherKey::new(&AES_128_CBC_PKCS7_PADDING, &key).unwrap(); + let encrypting_key = EncryptingKey::new_with_iv(cipher_key, iv); + + let mut ciphertext = input.clone(); + let decrypt_iv = encrypting_key.encrypt(&mut ciphertext).unwrap(); + assert_eq!(expected_ciphertext, ciphertext); + + let cipher_key2 = CipherKey::new(&AES_128_CBC_PKCS7_PADDING, &key).unwrap(); + let decrypting_key = DecryptingKey::new(cipher_key2, decrypt_iv); + + let plaintext = decrypting_key.decrypt(&mut ciphertext).unwrap(); + assert_eq!(input.as_slice(), plaintext); + } + + #[test] + fn test_iv_aes_256_cbc_15_bytes() { + let key = + from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(); + let input = from_hex("00112233445566778899aabbccddee").unwrap(); + let expected_ciphertext = from_hex("2ddfb635a651a43f582997966840ca0c").unwrap(); + let iv = [0u8; AES_IV_LEN]; + let cipher_key = CipherKey::new(&AES_256_CBC_PKCS7_PADDING, &key).unwrap(); + let encrypting_key = EncryptingKey::new_with_iv(cipher_key, iv); + + let mut ciphertext = input.clone(); + let decrypt_iv = encrypting_key.encrypt(&mut ciphertext).unwrap(); + assert_eq!(expected_ciphertext, ciphertext); + + let cipher_key2 = CipherKey::new(&AES_256_CBC_PKCS7_PADDING, &key).unwrap(); + let decrypting_key = DecryptingKey::new(cipher_key2, decrypt_iv); + + let plaintext = decrypting_key.decrypt(&mut ciphertext).unwrap(); + assert_eq!(input.as_slice(), plaintext); + } + + #[test] + fn test_iv_aes_128_ctr_16_bytes() { + let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); + let input = from_hex("00112233445566778899aabbccddeeff").unwrap(); + let expected_ciphertext = from_hex("c6b01904c3da3df5e7d62bd96d153686").unwrap(); + let iv = [0u8; AES_IV_LEN]; + let cipher_key = CipherKey::new(&AES_128_CTR, &key).unwrap(); + let encrypting_key = EncryptingKey::new_with_iv(cipher_key, iv); + + let mut ciphertext = input.clone(); + let decrypt_iv = encrypting_key.encrypt(&mut ciphertext).unwrap(); + assert_eq!(expected_ciphertext, ciphertext); + + let cipher_key2 = CipherKey::new(&AES_128_CTR, &key).unwrap(); + let decrypting_key = DecryptingKey::new(cipher_key2, decrypt_iv); + + let plaintext = decrypting_key.decrypt(&mut ciphertext).unwrap(); + assert_eq!(input.as_slice(), plaintext); + } + + #[test] + fn test_iv_aes_256_ctr_15_bytes() { + let key = + from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(); + let input = from_hex("00112233445566778899aabbccddee").unwrap(); + let expected_ciphertext = from_hex("f28122856e1cf9a7216a30d111f399").unwrap(); + let iv = [0u8; AES_IV_LEN]; + let cipher_key = CipherKey::new(&AES_256_CTR, &key).unwrap(); + let encrypting_key = EncryptingKey::new_with_iv(cipher_key, iv); + + let mut ciphertext = input.clone(); + let decrypt_iv = encrypting_key.encrypt(&mut ciphertext).unwrap(); + assert_eq!(expected_ciphertext, ciphertext); + + let cipher_key2 = CipherKey::new(&AES_256_CTR, &key).unwrap(); + let decrypting_key = DecryptingKey::new(cipher_key2, decrypt_iv); + + let plaintext = decrypting_key.decrypt(&mut ciphertext).unwrap(); + assert_eq!(input.as_slice(), plaintext); + } } From 726a1183f2d1be477ccf5eabc02afb7fcab0c8a0 Mon Sep 17 00:00:00 2001 From: Sean McGrail Date: Wed, 17 May 2023 15:18:41 -0700 Subject: [PATCH 12/54] Add OpenSSL test vectors --- aws-lc-rs/src/cipher.rs | 178 +++++++++++++++++++++++++--------------- 1 file changed, 110 insertions(+), 68 deletions(-) diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index 71bc2718431..79618f009be 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -571,86 +571,128 @@ mod tests { } } - #[test] - fn test_iv_aes_128_cbc_16_bytes() { - let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); - let input = from_hex("00112233445566778899aabbccddeeff").unwrap(); - let expected_ciphertext = - from_hex("69c4e0d86a7b0430d8cdb78070b4c55a9e978e6d16b086570ef794ef97984232").unwrap(); - let iv = [0u8; AES_IV_LEN]; - let cipher_key = CipherKey::new(&AES_128_CBC_PKCS7_PADDING, &key).unwrap(); - let encrypting_key = EncryptingKey::new_with_iv(cipher_key, iv); - - let mut ciphertext = input.clone(); - let decrypt_iv = encrypting_key.encrypt(&mut ciphertext).unwrap(); - assert_eq!(expected_ciphertext, ciphertext); + macro_rules! cipher_test_vector { + ($name:ident, $cipher:expr, $key:literal, $iv: literal, $plaintext:literal, $ciphertext:literal) => { + #[test] + fn $name() { + let key = from_hex($key).unwrap(); + let input = from_hex($plaintext).unwrap(); + let expected_ciphertext = from_hex($ciphertext).unwrap(); + let mut iv = from_hex($iv).unwrap(); + let iv = { + let slice = iv.as_mut_slice(); + let mut iv = [0u8; 16]; + { + let x = iv.as_mut_slice(); + x.copy_from_slice(slice); + } + iv + }; - let cipher_key2 = CipherKey::new(&AES_128_CBC_PKCS7_PADDING, &key).unwrap(); - let decrypting_key = DecryptingKey::new(cipher_key2, decrypt_iv); + let alg = $cipher; + let cipher_key = CipherKey::new(alg, &key).unwrap(); + let encrypting_key = EncryptingKey::new_with_iv(cipher_key, iv.try_into().unwrap()); - let plaintext = decrypting_key.decrypt(&mut ciphertext).unwrap(); - assert_eq!(input.as_slice(), plaintext); - } + let mut ciphertext = input.clone(); + let decrypt_iv = encrypting_key.encrypt(&mut ciphertext).unwrap(); + assert_eq!(expected_ciphertext, ciphertext); - #[test] - fn test_iv_aes_256_cbc_15_bytes() { - let key = - from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(); - let input = from_hex("00112233445566778899aabbccddee").unwrap(); - let expected_ciphertext = from_hex("2ddfb635a651a43f582997966840ca0c").unwrap(); - let iv = [0u8; AES_IV_LEN]; - let cipher_key = CipherKey::new(&AES_256_CBC_PKCS7_PADDING, &key).unwrap(); - let encrypting_key = EncryptingKey::new_with_iv(cipher_key, iv); + let cipher_key2 = CipherKey::new(alg, &key).unwrap(); + let decrypting_key = DecryptingKey::new(cipher_key2, decrypt_iv); - let mut ciphertext = input.clone(); - let decrypt_iv = encrypting_key.encrypt(&mut ciphertext).unwrap(); - assert_eq!(expected_ciphertext, ciphertext); + let plaintext = decrypting_key.decrypt(&mut ciphertext).unwrap(); + assert_eq!(input.as_slice(), plaintext); + } + }; + } - let cipher_key2 = CipherKey::new(&AES_256_CBC_PKCS7_PADDING, &key).unwrap(); - let decrypting_key = DecryptingKey::new(cipher_key2, decrypt_iv); + cipher_test_vector!( + test_iv_aes_128_cbc_16_bytes, + &AES_128_CBC_PKCS7_PADDING, + "000102030405060708090a0b0c0d0e0f", + "00000000000000000000000000000000", + "00112233445566778899aabbccddeeff", + "69c4e0d86a7b0430d8cdb78070b4c55a9e978e6d16b086570ef794ef97984232" + ); - let plaintext = decrypting_key.decrypt(&mut ciphertext).unwrap(); - assert_eq!(input.as_slice(), plaintext); - } + cipher_test_vector!( + test_iv_aes_256_cbc_15_bytes, + &AES_256_CBC_PKCS7_PADDING, + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "00000000000000000000000000000000", + "00112233445566778899aabbccddee", + "2ddfb635a651a43f582997966840ca0c" + ); - #[test] - fn test_iv_aes_128_ctr_16_bytes() { - let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); - let input = from_hex("00112233445566778899aabbccddeeff").unwrap(); - let expected_ciphertext = from_hex("c6b01904c3da3df5e7d62bd96d153686").unwrap(); - let iv = [0u8; AES_IV_LEN]; - let cipher_key = CipherKey::new(&AES_128_CTR, &key).unwrap(); - let encrypting_key = EncryptingKey::new_with_iv(cipher_key, iv); + cipher_test_vector!( + test_iv_aes_128_ctr_16_bytes, + &AES_128_CTR, + "000102030405060708090a0b0c0d0e0f", + "00000000000000000000000000000000", + "00112233445566778899aabbccddeeff", + "c6b01904c3da3df5e7d62bd96d153686" + ); - let mut ciphertext = input.clone(); - let decrypt_iv = encrypting_key.encrypt(&mut ciphertext).unwrap(); - assert_eq!(expected_ciphertext, ciphertext); + cipher_test_vector!( + test_iv_aes_256_ctr_15_bytes, + &AES_256_CTR, + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "00000000000000000000000000000000", + "00112233445566778899aabbccddee", + "f28122856e1cf9a7216a30d111f399" + ); - let cipher_key2 = CipherKey::new(&AES_128_CTR, &key).unwrap(); - let decrypting_key = DecryptingKey::new(cipher_key2, decrypt_iv); + cipher_test_vector!( + test_openssl_128_ctr_15_bytes, + &AES_128_CTR, + "244828580821c1652582c76e34d299f5", + "093145d5af233f46072a5eb5adc11aa1", + "3ee38cec171e6cf466bf0df98aa0e1", + "bd7d928f60e3422d96b3f8cd614eb2" + ); - let plaintext = decrypting_key.decrypt(&mut ciphertext).unwrap(); - assert_eq!(input.as_slice(), plaintext); - } + cipher_test_vector!( + test_openssl_256_ctr_15_bytes, + &AES_256_CTR, + "0857db8240ea459bdf660b4cced66d1f2d3734ff2de7b81e92740e65e7cc6a1d", + "f028ecb053f801102d11fccc9d303a27", + "eca7285d19f3c20e295378460e8729", + "b5098e5e788de6ac2f2098eb2fc6f8" + ); - #[test] - fn test_iv_aes_256_ctr_15_bytes() { - let key = - from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(); - let input = from_hex("00112233445566778899aabbccddee").unwrap(); - let expected_ciphertext = from_hex("f28122856e1cf9a7216a30d111f399").unwrap(); - let iv = [0u8; AES_IV_LEN]; - let cipher_key = CipherKey::new(&AES_256_CTR, &key).unwrap(); - let encrypting_key = EncryptingKey::new_with_iv(cipher_key, iv); + cipher_test_vector!( + test_openssl_aes_128_cbc_15_bytes, + &AES_128_CBC_PKCS7_PADDING, + "053304bb3899e1d99db9d29343ea782d", + "b5313560244a4822c46c2a0c9d0cf7fd", + "a3e4c990356c01f320043c3d8d6f43", + "ad96993f248bd6a29760ec7ccda95ee1" + ); - let mut ciphertext = input.clone(); - let decrypt_iv = encrypting_key.encrypt(&mut ciphertext).unwrap(); - assert_eq!(expected_ciphertext, ciphertext); + cipher_test_vector!( + test_openssl_aes_128_cbc_16_bytes, + &AES_128_CBC_PKCS7_PADDING, + "95af71f1c63e4a1d0b0b1a27fb978283", + "89e40797dca70197ff87d3dbb0ef2802", + "aece7b5e3c3df1ffc9802d2dfe296dc7", + "301b5dab49fb11e919d0d39970d06739301919743304f23f3cbc67d28564b25b" + ); - let cipher_key2 = CipherKey::new(&AES_256_CTR, &key).unwrap(); - let decrypting_key = DecryptingKey::new(cipher_key2, decrypt_iv); + cipher_test_vector!( + test_openssl_aes_256_cbc_15_bytes, + &AES_256_CBC_PKCS7_PADDING, + "d369e03e9752784917cc7bac1db7399598d9555e691861d9dd7b3292a693ef57", + "1399bb66b2f6ad99a7f064140eaaa885", + "7385f5784b85bf0a97768ddd896d6d", + "4351082bac9b4593ae8848cc9dfb5a01" + ); - let plaintext = decrypting_key.decrypt(&mut ciphertext).unwrap(); - assert_eq!(input.as_slice(), plaintext); - } + cipher_test_vector!( + test_openssl_aes_256_cbc_16_bytes, + &AES_256_CBC_PKCS7_PADDING, + "d4a8206dcae01242f9db79a4ecfe277d0f7bb8ccbafd8f9809adb39f35aa9b41", + "24f6076548fb9d93c8f7ed9f6e661ef9", + "a39c1fdf77ea3e1f18178c0ec237c70a", + "f1af484830a149ee0387b854d65fe87ca0e62efc1c8e6909d4b9ab8666470453" + ); } From 6bd64bc65091869fc267c508f65585532e28541c Mon Sep 17 00:00:00 2001 From: Sean McGrail Date: Wed, 17 May 2023 15:21:28 -0700 Subject: [PATCH 13/54] Test case names --- aws-lc-rs/src/cipher.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index 79618f009be..8293c5240f4 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -643,7 +643,7 @@ mod tests { ); cipher_test_vector!( - test_openssl_128_ctr_15_bytes, + test_openssl_aes_128_ctr_15_bytes, &AES_128_CTR, "244828580821c1652582c76e34d299f5", "093145d5af233f46072a5eb5adc11aa1", @@ -652,7 +652,7 @@ mod tests { ); cipher_test_vector!( - test_openssl_256_ctr_15_bytes, + test_openssl_aes_256_ctr_15_bytes, &AES_256_CTR, "0857db8240ea459bdf660b4cced66d1f2d3734ff2de7b81e92740e65e7cc6a1d", "f028ecb053f801102d11fccc9d303a27", From 5cd281198c0b181192df0d739ae9653251a51b5b Mon Sep 17 00:00:00 2001 From: Sean McGrail Date: Wed, 17 May 2023 18:03:35 -0700 Subject: [PATCH 14/54] Add documentation --- aws-lc-rs/src/cipher.rs | 102 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 97 insertions(+), 5 deletions(-) diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index 8293c5240f4..88443342134 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -3,11 +3,42 @@ // Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR ISC -#![allow( - missing_docs, - clippy::missing_errors_doc, - clippy::module_name_repetitions -)] +//! Block and Stream Cipher for Encryption and Decryption. +//! +//! # 🛑 Read Before Using +//! +//! This module provides access to block and stream cipher algorithms. +//! 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. +//! +//! If you are developing new applications requring data encryption see +//! the algorithms provided in [`aead`](crate::aead). +//! +//! # Examples +//! ``` +//! use aws_lc_rs::cipher::{CipherKey, DecryptingKey, EncryptingKey, AES_128_CTR}; +//! +//! let mut plaintext = Vec::from("This is a secret message!"); +//! +//! let key_bytes: &[u8] = &[ +//! 0xff, 0x0b, 0xe5, 0x84, 0x64, 0x0b, 0x00, 0xc8, 0x90, 0x7a, 0x4b, 0xbf, 0x82, 0x7c, 0xb6, +//! 0xd1, +//! ]; +//! +//! let key = CipherKey::new(&AES_128_CTR, key_bytes).unwrap(); +//! let encrypting_key = EncryptingKey::new(key).unwrap(); +//! let iv = encrypting_key.encrypt(&mut plaintext).unwrap(); +//! +//! let key = CipherKey::new(&AES_128_CTR, key_bytes).unwrap(); +//! let decrypting_key = DecryptingKey::new(key, iv); +//! let plaintext = decrypting_key.decrypt(&mut plaintext).unwrap(); +//! ``` +//! + +#![allow(clippy::module_name_repetitions)] pub(crate) mod aes; pub(crate) mod block; @@ -91,25 +122,51 @@ enum OperatingMode { Stream, } +/// A Block or Stream Cipher Algorithm +/// +/// # Supported Algorithms +/// +/// ## Counter (CTR) Modes +/// +/// * [`AES_128_CTR`] +/// * [`AES_256_CTR`] +/// +/// ## Cipher block chaining (CBC) Modes +/// +/// * [`AES_128_CBC_PKCS7_PADDING`] +/// * [`AES_256_CBC_PKCS7_PADDING`] +/// pub struct Algorithm( AlgorithmId, OperatingMode, ); +/// The number of bytes in an AES 128-bit key pub const AES_128_KEY_LEN: usize = 16; + +/// The number of bytes in an AES 256-bit key pub const AES_256_KEY_LEN: usize = 32; + +/// The number of bytes for an AES initalization vector (IV) pub const AES_IV_LEN: usize = 16; const AES_BLOCK_LEN: usize = 16; +/// AES-128 Counter (CTR) Mode pub const AES_128_CTR: Algorithm = Algorithm(AlgorithmId::Aes128ctr, OperatingMode::Stream); + +/// AES-128 Cipher block chaining (CBC) Mode using PKCS#7 padding. pub const AES_128_CBC_PKCS7_PADDING: Algorithm = Algorithm( AlgorithmId::Aes128cbc, OperatingMode::Block(PaddingStrategy::PKCS7), ); + +/// AES-256 Counter (CTR) Mode pub const AES_256_CTR: Algorithm = Algorithm(AlgorithmId::Aes256ctr, OperatingMode::Stream); + +/// AES-256 Cipher block chaining (CBC) Mode using PKCS#7 padding. pub const AES_256_CBC_PKCS7_PADDING: Algorithm = Algorithm( AlgorithmId::Aes256cbc, @@ -130,6 +187,7 @@ impl } } +/// A key bound to a particular cipher algorithm. pub struct CipherKey { algorithm: &'static Algorithm, key: SymmetricCipherKey, @@ -147,6 +205,13 @@ impl impl CipherKey { + /// Constructs a [`CipherKey`]. + /// + /// # Errors + /// + /// * [`Unspecified`] if `key_bytes.len()` does not match the + /// length required by `algorithm`. + /// pub fn new( algorithm: &'static Algorithm, key_bytes: &[u8], @@ -160,6 +225,7 @@ impl } } +/// An encryting cipher key. pub struct EncryptingKey { cipher_key: CipherKey, iv: IV, @@ -179,6 +245,12 @@ impl } } + /// Constructs a new [`EncryptingKey`]. + /// + /// # Errors + /// + /// * [`Unspecified`]: Returned if a randomized IV fails to be generated. + /// pub fn new( cipher_key: CipherKey, ) -> Result, Unspecified> { @@ -190,6 +262,15 @@ impl }) } + /// Encrypts the data `in_out` in-place. If the algorithm bound to this key uses padding + /// then the `in_out` will be extended to add the necessary padding. + /// + /// Returns the initalization vector necessary to later decrypt the data. + /// + /// # Errors + /// + /// * [`Unspecified`]: Returned if the data fails to be encrypted. + /// pub fn encrypt(self, in_out: &mut InOut) -> Result, Unspecified> where InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, @@ -269,6 +350,7 @@ impl } } +/// An decrypting cipher key. pub struct DecryptingKey { cipher_key: CipherKey, iv: IV, @@ -277,6 +359,7 @@ pub struct DecryptingKey DecryptingKey { + /// Constructs a new [`DecryptingKey`]. #[must_use] pub fn new( cipher_key: CipherKey, @@ -285,6 +368,15 @@ impl DecryptingKey { cipher_key, iv } } + /// Decrypts the data `in_out` in-place. + /// + /// Returns a reference to the decrypted data. If the algorithm bound to this key uses padding, + /// then the returned slice reference will have it's length adjusted to remove the padding bytes. + /// + /// # Errors + /// + /// * [`Unspecified`]: Returned if the data fails to be decrypted. + /// pub fn decrypt(mut self, in_out: &mut [u8]) -> Result<&mut [u8], Unspecified> { let alg = self.cipher_key.get_algorithm(); From fd4f1c0fe0372d3cbcc64ee5848775208b3e9d19 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Thu, 18 May 2023 10:00:08 -0400 Subject: [PATCH 15/54] Minor doc/test adjustments --- aws-lc-rs/src/cipher.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index 88443342134..ba757bdda92 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -265,7 +265,7 @@ impl /// Encrypts the data `in_out` in-place. If the algorithm bound to this key uses padding /// then the `in_out` will be extended to add the necessary padding. /// - /// Returns the initalization vector necessary to later decrypt the data. + /// Returns the initialization vector necessary to later decrypt the data. /// /// # Errors /// @@ -370,8 +370,9 @@ impl /// Decrypts the data `in_out` in-place. /// - /// Returns a reference to the decrypted data. If the algorithm bound to this key uses padding, - /// then the returned slice reference will have it's length adjusted to remove the padding bytes. + /// Returns a slice reference to the decrypted data within `in_out`. If the algorithm bound to + /// this key uses padding then the returned slice reference is length adjusted to exclude + /// the padding bytes. /// /// # Errors /// @@ -618,7 +619,10 @@ mod tests { let mut ciphertext = input.clone(); let decrypt_iv = encrypting_key.encrypt(&mut ciphertext).unwrap(); - assert_ne!(input.as_slice(), ciphertext); + if n > 5 { + // There's no more than a 1 in 2^48 chance that this will fail randomly + assert_ne!(input.as_slice(), ciphertext); + } let cipher_key2 = CipherKey::new(alg, key).unwrap(); let decrypting_key = DecryptingKey::new(cipher_key2, decrypt_iv); @@ -647,8 +651,7 @@ mod tests { #[test] fn test_aes_128_ctr() { let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); - // TODO: test 0 bytes. - for i in 1..=50 { + for i in 0..=50 { helper_test_cipher_n_bytes(key.as_slice(), &AES_128_CTR, i); } } @@ -657,8 +660,7 @@ mod tests { fn test_aes_256_ctr() { let key = from_hex("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f").unwrap(); - // TODO: test 0 bytes. - for i in 1..=50 { + for i in 0..=50 { helper_test_cipher_n_bytes(key.as_slice(), &AES_256_CTR, i); } } @@ -673,7 +675,7 @@ mod tests { let mut iv = from_hex($iv).unwrap(); let iv = { let slice = iv.as_mut_slice(); - let mut iv = [0u8; 16]; + let mut iv = [0u8; $iv.len() / 2]; { let x = iv.as_mut_slice(); x.copy_from_slice(slice); From 261db7a01bf9e136ea108d3b252f91f39c7cd29c Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Thu, 18 May 2023 14:18:00 -0400 Subject: [PATCH 16/54] Renaming UnboundCipherKey and NonceIV --- aws-lc-rs/src/cipher.rs | 76 ++++++++++++++++++++--------------------- aws-lc-rs/src/iv.rs | 26 +++++++++----- 2 files changed, 55 insertions(+), 47 deletions(-) diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index ba757bdda92..cc68d5e21dd 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -19,7 +19,7 @@ //! //! # Examples //! ``` -//! use aws_lc_rs::cipher::{CipherKey, DecryptingKey, EncryptingKey, AES_128_CTR}; +//! use aws_lc_rs::cipher::{UnboundCipherKey, DecryptingKey, EncryptingKey, AES_128_CTR}; //! //! let mut plaintext = Vec::from("This is a secret message!"); //! @@ -28,11 +28,11 @@ //! 0xd1, //! ]; //! -//! let key = CipherKey::new(&AES_128_CTR, key_bytes).unwrap(); +//! let key = UnboundCipherKey::new(&AES_128_CTR, key_bytes).unwrap(); //! let encrypting_key = EncryptingKey::new(key).unwrap(); //! let iv = encrypting_key.encrypt(&mut plaintext).unwrap(); //! -//! let key = CipherKey::new(&AES_128_CTR, key_bytes).unwrap(); +//! let key = UnboundCipherKey::new(&AES_128_CTR, key_bytes).unwrap(); //! let decrypting_key = DecryptingKey::new(key, iv); //! let plaintext = decrypting_key.decrypt(&mut plaintext).unwrap(); //! ``` @@ -48,8 +48,7 @@ use crate::cipher::aes::{encrypt_block_aes, Aes128Key, Aes256Key}; use crate::cipher::block::Block; use crate::cipher::chacha::ChaCha20Key; use crate::error::Unspecified; -use crate::iv::IV; -use crate::rand; +use crate::iv::NonceIV; use aws_lc::{ AES_cbc_encrypt, AES_ctr128_encrypt, AES_set_decrypt_key, AES_set_encrypt_key, AES_DECRYPT, AES_ENCRYPT, AES_KEY, @@ -188,13 +187,13 @@ impl } /// A key bound to a particular cipher algorithm. -pub struct CipherKey { +pub struct UnboundCipherKey { algorithm: &'static Algorithm, key: SymmetricCipherKey, } impl - CipherKey + UnboundCipherKey { #[inline] fn get_algorithm(&self) -> &'static Algorithm { @@ -203,9 +202,9 @@ impl } impl - CipherKey + UnboundCipherKey { - /// Constructs a [`CipherKey`]. + /// Constructs a [`UnboundCipherKey`]. /// /// # Errors /// @@ -221,27 +220,29 @@ impl AlgorithmId::Aes128ctr | AlgorithmId::Aes128cbc => SymmetricCipherKey::aes128(key), AlgorithmId::Aes256ctr | AlgorithmId::Aes256cbc => SymmetricCipherKey::aes256(key), }?; - Ok(CipherKey { algorithm, key }) + Ok(UnboundCipherKey { algorithm, key }) } } /// An encryting cipher key. pub struct EncryptingKey { - cipher_key: CipherKey, - iv: IV, + cipher_key: UnboundCipherKey, + iv: NonceIV, } impl EncryptingKey { - #[cfg(test)] - fn new_with_iv( - cipher_key: CipherKey, + /// Constructs a new [`EncryptingKey`] using provided `cipher_key` and `iv`. + /// It is recommended to use `EncryptingKey::new` to avoid the potential reuse of an + /// initialization vector (`IV`). + pub fn less_safe_new_using_iv( + cipher_key: UnboundCipherKey, iv_bytes: [u8; IV_LEN], ) -> EncryptingKey { EncryptingKey { cipher_key, - iv: IV::assume_unique_for_key(iv_bytes), + iv: NonceIV::assume_unique_for_key(iv_bytes), } } @@ -252,13 +253,11 @@ impl /// * [`Unspecified`]: Returned if a randomized IV fails to be generated. /// pub fn new( - cipher_key: CipherKey, + cipher_key: UnboundCipherKey, ) -> Result, Unspecified> { - let mut iv_bytes = [0u8; IV_LEN]; - rand::fill(&mut iv_bytes)?; Ok(EncryptingKey { cipher_key, - iv: IV::assume_unique_for_key(iv_bytes), + iv: NonceIV::::new()?, }) } @@ -271,7 +270,7 @@ impl /// /// * [`Unspecified`]: Returned if the data fails to be encrypted. /// - pub fn encrypt(self, in_out: &mut InOut) -> Result, Unspecified> + pub fn encrypt(self, in_out: &mut InOut) -> Result, Unspecified> where InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, { @@ -352,8 +351,8 @@ impl /// An decrypting cipher key. pub struct DecryptingKey { - cipher_key: CipherKey, - iv: IV, + cipher_key: UnboundCipherKey, + iv: NonceIV, } impl @@ -362,8 +361,8 @@ impl /// Constructs a new [`DecryptingKey`]. #[must_use] pub fn new( - cipher_key: CipherKey, - iv: IV, + cipher_key: UnboundCipherKey, + iv: NonceIV, ) -> DecryptingKey { DecryptingKey { cipher_key, iv } } @@ -613,21 +612,21 @@ mod tests { input.push(byte); } - let cipher_key = CipherKey::new(alg, key).unwrap(); + let cipher_key = UnboundCipherKey::new(alg, key).unwrap(); let encrypting_key = EncryptingKey::new(cipher_key).unwrap(); - let mut ciphertext = input.clone(); - let decrypt_iv = encrypting_key.encrypt(&mut ciphertext).unwrap(); + let mut in_out = input.clone(); + let decrypt_iv = encrypting_key.encrypt(&mut in_out).unwrap(); if n > 5 { // There's no more than a 1 in 2^48 chance that this will fail randomly - assert_ne!(input.as_slice(), ciphertext); + assert_ne!(input.as_slice(), in_out); } - let cipher_key2 = CipherKey::new(alg, key).unwrap(); + let cipher_key2 = UnboundCipherKey::new(alg, key).unwrap(); let decrypting_key = DecryptingKey::new(cipher_key2, decrypt_iv); - let plaintext = decrypting_key.decrypt(&mut ciphertext).unwrap(); + let plaintext = decrypting_key.decrypt(&mut in_out).unwrap(); assert_eq!(input.as_slice(), plaintext); } @@ -684,17 +683,18 @@ mod tests { }; let alg = $cipher; - let cipher_key = CipherKey::new(alg, &key).unwrap(); - let encrypting_key = EncryptingKey::new_with_iv(cipher_key, iv.try_into().unwrap()); + let cipher_key = UnboundCipherKey::new(alg, &key).unwrap(); + let encrypting_key = + EncryptingKey::less_safe_new_using_iv(cipher_key, iv.try_into().unwrap()); - let mut ciphertext = input.clone(); - let decrypt_iv = encrypting_key.encrypt(&mut ciphertext).unwrap(); - assert_eq!(expected_ciphertext, ciphertext); + let mut in_out = input.clone(); + let decrypt_iv = encrypting_key.encrypt(&mut in_out).unwrap(); + assert_eq!(expected_ciphertext, in_out); - let cipher_key2 = CipherKey::new(alg, &key).unwrap(); + let cipher_key2 = UnboundCipherKey::new(alg, &key).unwrap(); let decrypting_key = DecryptingKey::new(cipher_key2, decrypt_iv); - let plaintext = decrypting_key.decrypt(&mut ciphertext).unwrap(); + let plaintext = decrypting_key.decrypt(&mut in_out).unwrap(); assert_eq!(input.as_slice(), plaintext); } }; diff --git a/aws-lc-rs/src/iv.rs b/aws-lc-rs/src/iv.rs index f3bc5c14c1d..08638c9a37b 100644 --- a/aws-lc-rs/src/iv.rs +++ b/aws-lc-rs/src/iv.rs @@ -4,11 +4,12 @@ // SPDX-License-Identifier: Apache-2.0 OR ISC #![allow(dead_code)] -use crate::error; +use crate::error::Unspecified; +use crate::{error, rand}; -pub struct IV([u8; L]); +pub struct NonceIV([u8; L]); -impl IV { +impl NonceIV { /// Constructs a `IV` with the given value, assuming that the value is /// unique for the lifetime of the key it is being used with. /// @@ -32,32 +33,39 @@ impl IV { pub fn size() -> usize { L } + + /// Constructs a new IVNonce from pseudo-random bytes. + pub fn new() -> Result { + let mut iv_bytes = [0u8; L]; + rand::fill(&mut iv_bytes)?; + Ok(Self(iv_bytes)) + } } -impl AsMut<[u8; L]> for IV { +impl AsMut<[u8; L]> for NonceIV { #[inline] fn as_mut(&mut self) -> &mut [u8; L] { &mut self.0 } } -impl AsRef<[u8; L]> for IV { +impl AsRef<[u8; L]> for NonceIV { #[inline] fn as_ref(&self) -> &[u8; L] { &self.0 } } -impl From<&[u8; L]> for IV { +impl From<&[u8; L]> for NonceIV { #[inline] fn from(bytes: &[u8; L]) -> Self { - IV(bytes.to_owned()) + NonceIV(bytes.to_owned()) } } -impl From<[u8; L]> for IV { +impl From<[u8; L]> for NonceIV { #[inline] fn from(bytes: [u8; L]) -> Self { - IV(bytes) + NonceIV(bytes) } } From b1758a308ab216c41c7882fdeb2e81d94c8632d3 Mon Sep 17 00:00:00 2001 From: Sean McGrail <549813+skmcgrail@users.noreply.github.com> Date: Tue, 16 May 2023 08:39:06 -0700 Subject: [PATCH 17/54] LessSafeCipherKey implementation & refactor --- aws-lc-rs/src/aead.rs | 14 +- .../src/aead/chacha20_poly1305_openssh.rs | 5 +- aws-lc-rs/src/aead/nonce.rs | 21 +- .../src/aead/nonce_sequence/counter32.rs | 35 +- .../src/aead/nonce_sequence/counter64.rs | 35 +- aws-lc-rs/src/cipher.rs | 345 +++++++++++------- aws-lc-rs/src/iv.rs | 34 +- aws-lc-rs/src/lib.rs | 2 +- 8 files changed, 308 insertions(+), 183 deletions(-) diff --git a/aws-lc-rs/src/aead.rs b/aws-lc-rs/src/aead.rs index 35faa1f2407..ce16479326a 100644 --- a/aws-lc-rs/src/aead.rs +++ b/aws-lc-rs/src/aead.rs @@ -823,7 +823,7 @@ pub(crate) fn aead_open_combined( #[cfg(test)] mod tests { use super::*; - use crate::test::from_hex; + use crate::{iv::NonceIV, test::from_hex}; #[test] fn test_aes_128() { @@ -844,25 +844,29 @@ mod tests { #[allow(deprecated)] less_safe_key // Test coverage for `seal_in_place`, which calls `seal_in_place_append_tag`. - .seal_in_place(Nonce(nonce), Aad::empty(), &mut in_out) + .seal_in_place(Nonce(NonceIV::from(nonce)), Aad::empty(), &mut in_out) .unwrap(); let mut in_out_clone = in_out.clone(); let nonce: [u8; NONCE_LEN] = og_nonce.as_slice().try_into().unwrap(); assert!(less_safe_key - .open_in_place(Nonce(nonce), Aad::from("test"), &mut in_out_clone) + .open_in_place( + Nonce(NonceIV::from(nonce)), + Aad::from("test"), + &mut in_out_clone + ) .is_err()); let mut in_out_clone = in_out.clone(); let mut nonce: [u8; NONCE_LEN] = og_nonce.as_slice().try_into().unwrap(); nonce[0] = 0; assert!(less_safe_key - .open_in_place(Nonce(nonce), Aad::empty(), &mut in_out_clone) + .open_in_place(Nonce(NonceIV::from(nonce)), Aad::empty(), &mut in_out_clone) .is_err()); let nonce: [u8; NONCE_LEN] = og_nonce.as_slice().try_into().unwrap(); less_safe_key - .open_in_place(Nonce(nonce), Aad::empty(), &mut in_out) + .open_in_place(Nonce(NonceIV::from(nonce)), Aad::empty(), &mut in_out) .unwrap(); assert_eq!(plaintext, in_out[..plaintext.len()]); diff --git a/aws-lc-rs/src/aead/chacha20_poly1305_openssh.rs b/aws-lc-rs/src/aead/chacha20_poly1305_openssh.rs index d6cf266650c..6c2d297e565 100644 --- a/aws-lc-rs/src/aead/chacha20_poly1305_openssh.rs +++ b/aws-lc-rs/src/aead/chacha20_poly1305_openssh.rs @@ -23,6 +23,7 @@ use super::{poly1305, Nonce, Tag}; use crate::cipher::block::BLOCK_LEN; use crate::cipher::chacha::{self, ChaCha20Key}; +use crate::iv::NonceIV; use crate::{constant_time, endian::BigEndian, error}; use core::convert::TryInto; @@ -55,7 +56,7 @@ impl SealingKey { tag_out: &mut [u8; TAG_LEN], ) { let nonce = make_nonce(sequence_number); - let poly_key = derive_poly1305_key(&self.key.k_2, Nonce(nonce.0)); + let poly_key = derive_poly1305_key(&self.key.k_2, Nonce(NonceIV::from(nonce.as_ref()))); { let (len_in_out, data_and_padding_in_out) = @@ -130,7 +131,7 @@ impl OpeningKey { // We must verify the tag before decrypting so that // `ciphertext_in_plaintext_out` is unmodified if verification fails. // This is beyond what we guarantee. - let poly_key = derive_poly1305_key(&self.key.k_2, Nonce(nonce.0)); + let poly_key = derive_poly1305_key(&self.key.k_2, Nonce(NonceIV::from(nonce.as_ref()))); verify(poly_key, ciphertext_in_plaintext_out, tag)?; let plaintext_in_ciphertext_out = &mut ciphertext_in_plaintext_out[PACKET_LENGTH_LEN..]; diff --git a/aws-lc-rs/src/aead/nonce.rs b/aws-lc-rs/src/aead/nonce.rs index 525ca21b8a2..67b938ef7cc 100644 --- a/aws-lc-rs/src/aead/nonce.rs +++ b/aws-lc-rs/src/aead/nonce.rs @@ -5,7 +5,7 @@ use crate::endian::{ArrayEncoding, BigEndian, Encoding}; use crate::error; -use std::convert::TryInto; +use crate::iv::NonceIV; use std::mem::transmute_copy; /// A nonce for a single AEAD opening or sealing operation. @@ -14,7 +14,7 @@ use std::mem::transmute_copy; /// /// `Nonce` intentionally doesn't implement `Clone` to ensure that each one is /// consumed at most once. -pub struct Nonce(pub(crate) [u8; NONCE_LEN]); +pub struct Nonce(pub(crate) NonceIV); impl Nonce { /// Constructs a `Nonce` with the given value, assuming that the value is @@ -25,8 +25,9 @@ impl Nonce { /// `error::Unspecified` when byte slice length is not `NONCE_LEN` #[inline] pub fn try_assume_unique_for_key(value: &[u8]) -> Result { - let value: &[u8; NONCE_LEN] = value.try_into()?; - Ok(Self::assume_unique_for_key(*value)) + Ok(Self(NonceIV::::try_assume_unique_for_key( + value, + )?)) } /// Constructs a `Nonce` with the given value, assuming that the value is @@ -34,21 +35,21 @@ impl Nonce { #[inline] #[must_use] pub fn assume_unique_for_key(value: [u8; NONCE_LEN]) -> Self { - Self(value) + Self(NonceIV::::assume_unique_for_key(value)) } } impl AsRef<[u8; NONCE_LEN]> for Nonce { #[inline] fn as_ref(&self) -> &[u8; NONCE_LEN] { - &self.0 + self.0.as_ref() } } impl From<&[u8; NONCE_LEN]> for Nonce { #[inline] fn from(bytes: &[u8; NONCE_LEN]) -> Self { - Nonce(bytes.to_owned()) + Self(NonceIV::from(bytes)) } } @@ -57,7 +58,7 @@ impl From<&[u32; NONCE_LEN / 4]> for Nonce { fn from(values: &[u32; NONCE_LEN / 4]) -> Self { unsafe { let bytes: [u8; NONCE_LEN] = transmute_copy(values); - Nonce(bytes) + Nonce(NonceIV::from(bytes)) } } } @@ -66,7 +67,7 @@ impl From> for Nonce { #[inline] fn from(number: BigEndian) -> Self { let nonce = [BigEndian::ZERO, BigEndian::ZERO, number]; - Nonce(*(nonce.as_byte_array())) + Nonce(NonceIV::from(*(nonce.as_byte_array()))) } } @@ -76,7 +77,7 @@ impl From<&[u8; IV_LEN]> for Nonce { fn from(bytes: &[u8; IV_LEN]) -> Self { let mut nonce_bytes = [0u8; NONCE_LEN]; nonce_bytes.copy_from_slice(&bytes[0..NONCE_LEN]); - Nonce(nonce_bytes) + Nonce(NonceIV::from(nonce_bytes)) } } diff --git a/aws-lc-rs/src/aead/nonce_sequence/counter32.rs b/aws-lc-rs/src/aead/nonce_sequence/counter32.rs index c7d8ac248b2..bb722eb9936 100644 --- a/aws-lc-rs/src/aead/nonce_sequence/counter32.rs +++ b/aws-lc-rs/src/aead/nonce_sequence/counter32.rs @@ -3,6 +3,7 @@ use crate::aead::{Nonce, NonceSequence, NONCE_LEN}; use crate::error::Unspecified; +use crate::iv::NonceIV; /// `Counter32` is an implementation of the `NonceSequence` trait. /// The internal state of a `Counter32` is a 32-bit unsigned counter that @@ -118,7 +119,7 @@ impl NonceSequence for Counter32 { nonce_bytes[..8].copy_from_slice(&self.identifier); nonce_bytes[8..].copy_from_slice(&counter_bytes); self.counter = self.counter.wrapping_add(1); - Ok(Nonce(nonce_bytes)) + Ok(Nonce(NonceIV::from(nonce_bytes))) } } @@ -134,7 +135,8 @@ mod tests { .counter(7) .build(); assert_eq!(0, cns.generated()); - let nonce = cns.advance().unwrap().0; + let nonce = cns.advance().unwrap(); + let nonce = nonce.as_ref(); assert_eq!(8, cns.counter()); assert_eq!( [0xA1, 0xB2, 0xC3, 0xD4, 0xA2, 0xB3, 0xC4, 0xD5], @@ -144,9 +146,10 @@ mod tests { assert_eq!(1, cns.generated()); assert_eq!( nonce, - [0xA1, 0xB2, 0xC3, 0xD4, 0xA2, 0xB3, 0xC4, 0xD5, 0, 0, 0, 7] + &[0xA1, 0xB2, 0xC3, 0xD4, 0xA2, 0xB3, 0xC4, 0xD5, 0, 0, 0, 7] ); - let nonce = cns.advance().unwrap().0; + let nonce = cns.advance().unwrap(); + let nonce = nonce.as_ref(); assert_eq!(2, cns.generated()); assert_eq!(9, cns.counter()); assert_eq!( @@ -155,17 +158,19 @@ mod tests { ); assert_eq!( nonce, - [0xA1, 0xB2, 0xC3, 0xD4, 0xA2, 0xB3, 0xC4, 0xD5, 0, 0, 0, 8] + &[0xA1, 0xB2, 0xC3, 0xD4, 0xA2, 0xB3, 0xC4, 0xD5, 0, 0, 0, 8] ); } #[test] fn test_counter32() { let mut cns = Counter32Builder::new().counter(0x_4CB0_16EA_u32).build(); - let nonce = cns.advance().unwrap().0; - assert_eq!(nonce, [0, 0, 0, 0, 0, 0, 0, 0, 0x4C, 0xB0, 0x16, 0xEA]); - let nonce = cns.advance().unwrap().0; - assert_eq!(nonce, [0, 0, 0, 0, 0, 0, 0, 0, 0x4C, 0xB0, 0x16, 0xEB]); + let nonce = cns.advance().unwrap(); + let nonce = nonce.as_ref(); + assert_eq!(nonce, &[0, 0, 0, 0, 0, 0, 0, 0, 0x4C, 0xB0, 0x16, 0xEA]); + let nonce = cns.advance().unwrap(); + let nonce = nonce.as_ref(); + assert_eq!(nonce, &[0, 0, 0, 0, 0, 0, 0, 0, 0x4C, 0xB0, 0x16, 0xEB]); } #[test] @@ -174,10 +179,12 @@ mod tests { .counter(0x_6A_u32) .identifier(0x_7B_u64.to_be_bytes()) .build(); - let nonce = cns.advance().unwrap().0; - assert_eq!(nonce, [0, 0, 0, 0, 0, 0, 0, 0x7B, 0, 0, 0, 0x6A]); - let nonce = cns.advance().unwrap().0; - assert_eq!(nonce, [0, 0, 0, 0, 0, 0, 0, 0x7B, 0, 0, 0, 0x6B]); + let nonce = cns.advance().unwrap(); + let nonce = nonce.as_ref(); + assert_eq!(nonce, &[0, 0, 0, 0, 0, 0, 0, 0x7B, 0, 0, 0, 0x6A]); + let nonce = cns.advance().unwrap(); + let nonce = nonce.as_ref(); + assert_eq!(nonce, &[0, 0, 0, 0, 0, 0, 0, 0x7B, 0, 0, 0, 0x6B]); } #[test] @@ -185,7 +192,7 @@ mod tests { let mut cns = Counter32Builder::new().limit(1).build(); assert_eq!(1, cns.limit()); assert_eq!(0, cns.generated()); - let _nonce = cns.advance().unwrap().0; + let _nonce = cns.advance().unwrap(); assert_eq!(1, cns.generated()); assert!(cns.advance().is_err()); } diff --git a/aws-lc-rs/src/aead/nonce_sequence/counter64.rs b/aws-lc-rs/src/aead/nonce_sequence/counter64.rs index 8e294101606..067f7678799 100644 --- a/aws-lc-rs/src/aead/nonce_sequence/counter64.rs +++ b/aws-lc-rs/src/aead/nonce_sequence/counter64.rs @@ -3,6 +3,7 @@ use crate::aead::{Nonce, NonceSequence, NONCE_LEN}; use crate::error::Unspecified; +use crate::iv::NonceIV; /// `Counter64` is an implementation of the `NonceSequence` trait. /// The internal state of a `Counter64` is a 64-bit unsigned counter that @@ -116,7 +117,7 @@ impl NonceSequence for Counter64 { nonce_bytes[..4].copy_from_slice(&self.identifier); nonce_bytes[4..].copy_from_slice(&bytes); self.counter = self.counter.wrapping_add(1); - Ok(Nonce(nonce_bytes)) + Ok(Nonce(NonceIV::from(nonce_bytes))) } } @@ -132,17 +133,19 @@ mod tests { .counter(7) .build(); assert_eq!(0, cns.generated()); - let nonce = cns.advance().unwrap().0; + let nonce = cns.advance().unwrap(); + let nonce = nonce.as_ref(); assert_eq!(8, cns.counter()); assert_eq!([0xA1, 0xB2, 0xC3, 0xD4], cns.identifier()); assert_eq!(u64::MAX, cns.limit()); assert_eq!(1, cns.generated()); - assert_eq!(nonce, [0xA1, 0xB2, 0xC3, 0xD4, 0, 0, 0, 0, 0, 0, 0, 7]); - let nonce = cns.advance().unwrap().0; + assert_eq!(nonce, &[0xA1, 0xB2, 0xC3, 0xD4, 0, 0, 0, 0, 0, 0, 0, 7]); + let nonce = cns.advance().unwrap(); + let nonce = nonce.as_ref(); assert_eq!(2, cns.generated()); assert_eq!(9, cns.counter()); assert_eq!([0xA1, 0xB2, 0xC3, 0xD4], cns.identifier()); - assert_eq!(nonce, [0xA1, 0xB2, 0xC3, 0xD4, 0, 0, 0, 0, 0, 0, 0, 8]); + assert_eq!(nonce, &[0xA1, 0xB2, 0xC3, 0xD4, 0, 0, 0, 0, 0, 0, 0, 8]); } #[test] @@ -150,10 +153,12 @@ mod tests { let mut cns = Counter64Builder::new() .counter(0x0002_4CB0_16EA_u64) .build(); - let nonce = cns.advance().unwrap().0; - assert_eq!(nonce, [0, 0, 0, 0, 0, 0, 0, 0x02, 0x4C, 0xB0, 0x16, 0xEA]); - let nonce = cns.advance().unwrap().0; - assert_eq!(nonce, [0, 0, 0, 0, 0, 0, 0, 0x02, 0x4C, 0xB0, 0x16, 0xEB]); + let nonce = cns.advance().unwrap(); + let nonce = nonce.as_ref(); + assert_eq!(nonce, &[0, 0, 0, 0, 0, 0, 0, 0x02, 0x4C, 0xB0, 0x16, 0xEA]); + let nonce = cns.advance().unwrap(); + let nonce = nonce.as_ref(); + assert_eq!(nonce, &[0, 0, 0, 0, 0, 0, 0, 0x02, 0x4C, 0xB0, 0x16, 0xEB]); } #[test] @@ -162,10 +167,12 @@ mod tests { .counter(0x_6A_u64) .identifier(0x_7B_u32.to_be_bytes()) .build(); - let nonce = cns.advance().unwrap().0; - assert_eq!(nonce, [0, 0, 0, 0x7B, 0, 0, 0, 0, 0, 0, 0, 0x6A]); - let nonce = cns.advance().unwrap().0; - assert_eq!(nonce, [0, 0, 0, 0x7B, 0, 0, 0, 0, 0, 0, 0, 0x6B]); + let nonce = cns.advance().unwrap(); + let nonce = nonce.as_ref(); + assert_eq!(nonce, &[0, 0, 0, 0x7B, 0, 0, 0, 0, 0, 0, 0, 0x6A]); + let nonce = cns.advance().unwrap(); + let nonce = nonce.as_ref(); + assert_eq!(nonce, &[0, 0, 0, 0x7B, 0, 0, 0, 0, 0, 0, 0, 0x6B]); } #[test] @@ -173,7 +180,7 @@ mod tests { let mut cns = Counter64Builder::new().limit(1).build(); assert_eq!(1, cns.limit()); assert_eq!(0, cns.generated()); - let _nonce = cns.advance().unwrap().0; + let _nonce = cns.advance().unwrap(); assert_eq!(1, cns.generated()); assert!(cns.advance().is_err()); } diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index cc68d5e21dd..2b04e6871ae 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -274,78 +274,12 @@ impl where InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, { - let alg = self.cipher_key.get_algorithm(); - - match alg.get_operating_mode() { - OperatingMode::Block(strategy) => match strategy { - PaddingStrategy::PKCS7 => { - let in_out_len = in_out.as_mut().len(); - // This implements PKCS#7 padding scheme, used by aws-lc if we were using EVP_CIPHER API's - let remainder = in_out_len % BLOCK_LEN; - if remainder == 0 { - let block_size: u8 = BLOCK_LEN.try_into().map_err(|_| Unspecified)?; - in_out.extend(vec![block_size; BLOCK_LEN].iter()); - } else { - let padding_size = BLOCK_LEN - remainder; - let v: u8 = padding_size.try_into().map_err(|_| Unspecified)?; - // Heap allocation :( - in_out.extend(vec![v; padding_size].iter()); - } - } - PaddingStrategy::Unpadded => {} - }, - OperatingMode::Stream => {} - } - - let in_out = in_out.as_mut(); - - let mut iv = [0u8; IV_LEN]; - iv.copy_from_slice(self.iv.as_ref()); - - match alg.get_id() { - AlgorithmId::Aes128ctr | AlgorithmId::Aes256ctr => { - let mut num = MaybeUninit::::new(0); - let key = match &self.cipher_key.key { - SymmetricCipherKey::Aes128 { enc_key, .. } - | SymmetricCipherKey::Aes256 { enc_key, .. } => enc_key, - SymmetricCipherKey::ChaCha20 { .. } => return Err(Unspecified), - }; - - let mut buf = [0u8; BLOCK_LEN]; - - unsafe { - AES_ctr128_encrypt( - in_out.as_ptr(), - in_out.as_mut_ptr(), - in_out.len(), - key, - iv.as_mut_ptr(), - buf.as_mut_slice().as_mut_ptr(), - num.as_mut_ptr(), - ); - }; - - Zeroize::zeroize(buf.as_mut_slice()); - } - AlgorithmId::Aes128cbc | AlgorithmId::Aes256cbc => { - let key = match &self.cipher_key.key { - SymmetricCipherKey::Aes128 { enc_key, .. } - | SymmetricCipherKey::Aes256 { enc_key, .. } => enc_key, - SymmetricCipherKey::ChaCha20 { .. } => return Err(Unspecified), - }; - unsafe { - AES_cbc_encrypt( - in_out.as_ptr(), - in_out.as_mut_ptr(), - in_out.len(), - key, - iv.as_mut_ptr(), - AES_ENCRYPT, - ); - } - } - } - Ok(self.iv) + encrypt( + self.cipher_key.get_algorithm(), + &self.cipher_key.key, + self.iv, + in_out, + ) } } @@ -377,82 +311,225 @@ impl /// /// * [`Unspecified`]: Returned if the data fails to be decrypted. /// + #[allow(unused_mut)] pub fn decrypt(mut self, in_out: &mut [u8]) -> Result<&mut [u8], Unspecified> { - let alg = self.cipher_key.get_algorithm(); + decrypt( + self.cipher_key.algorithm, + &self.cipher_key.key, + self.iv, + in_out, + ) + } +} - let mut final_len = in_out.len(); +/// Less safe cipher key that allows for specifying a user provided [`NonceIV`]. +pub struct LessSafeCipherKey { + algorithm: &'static Algorithm, + key: SymmetricCipherKey, +} - let iv = self.iv.as_mut(); +impl + LessSafeCipherKey +{ + /// Encrypts the data `in_out` in-place. If the algorithm bound to this key uses padding + /// then the `in_out` will be extended to add the necessary padding. + /// + /// Returns the initialization vector necessary to later decrypt the data. + /// + /// # Errors + /// + /// * [`Unspecified`]: Returned if the data fails to be encrypted. + /// + pub fn encrypt( + &self, + iv: NonceIV, + in_out: &mut InOut, + ) -> Result, Unspecified> + where + InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, + { + encrypt(self.algorithm, &self.key, iv, in_out) + } - match alg.get_id() { - AlgorithmId::Aes128ctr | AlgorithmId::Aes256ctr => { - let mut num = MaybeUninit::::new(0); - let key = match &self.cipher_key.key { - SymmetricCipherKey::Aes128 { enc_key, .. } - | SymmetricCipherKey::Aes256 { enc_key, .. } => enc_key, - SymmetricCipherKey::ChaCha20 { .. } => return Err(Unspecified), - }; - let mut buf = [0u8; BLOCK_LEN]; - unsafe { - AES_ctr128_encrypt( - in_out.as_ptr(), - in_out.as_mut_ptr(), - in_out.len(), - key, - iv.as_mut_ptr(), - buf.as_mut_slice().as_mut_ptr(), - num.as_mut_ptr(), - ); - }; - Zeroize::zeroize(buf.as_mut_slice()); - } - AlgorithmId::Aes128cbc | AlgorithmId::Aes256cbc => { - let key = match &self.cipher_key.key { - SymmetricCipherKey::Aes128 { dec_key, .. } - | SymmetricCipherKey::Aes256 { dec_key, .. } => dec_key, - SymmetricCipherKey::ChaCha20 { .. } => return Err(Unspecified), - }; - unsafe { - AES_cbc_encrypt( - in_out.as_ptr(), - in_out.as_mut_ptr(), - in_out.len(), - key, - iv.as_mut_ptr(), - AES_DECRYPT, - ); + /// Decrypts the data `in_out` in-place. + /// + /// Returns a slice reference to the decrypted data within `in_out`. If the algorithm bound to + /// this key uses padding then the returned slice reference is length adjusted to exclude + /// the padding bytes. + /// + /// # Errors + /// + /// * [`Unspecified`]: Returned if the data fails to be decrypted. + /// + #[allow(unused_mut)] + pub fn decrypt<'a>( + &self, + iv: NonceIV, + in_out: &'a mut [u8], + ) -> Result<&'a mut [u8], Unspecified> { + decrypt(self.algorithm, &self.key, iv, in_out) + } +} + +fn encrypt( + algorithm: &Algorithm, + key: &SymmetricCipherKey, + iv: NonceIV, + in_out: &mut InOut, +) -> Result, Unspecified> +where + InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, +{ + match algorithm.get_operating_mode() { + OperatingMode::Block(strategy) => match strategy { + PaddingStrategy::PKCS7 => { + let in_out_len = in_out.as_mut().len(); + // This implements PKCS#7 padding scheme, used by aws-lc if we were using EVP_CIPHER API's + let remainder = in_out_len % BLOCK_LEN; + if remainder == 0 { + let block_size: u8 = BLOCK_LEN.try_into().map_err(|_| Unspecified)?; + in_out.extend(vec![block_size; BLOCK_LEN].iter()); + } else { + let padding_size = BLOCK_LEN - remainder; + let v: u8 = padding_size.try_into().map_err(|_| Unspecified)?; + // Heap allocation :( + in_out.extend(vec![v; padding_size].iter()); } } + PaddingStrategy::Unpadded => {} + }, + OperatingMode::Stream => {} + } + + let in_out = in_out.as_mut(); + + let mut ivec = [0u8; IV_LEN]; + ivec.copy_from_slice(iv.as_ref()); + + match algorithm.get_id() { + AlgorithmId::Aes128ctr | AlgorithmId::Aes256ctr => { + let mut num = MaybeUninit::::new(0); + let key = match key { + SymmetricCipherKey::Aes128 { enc_key, .. } + | SymmetricCipherKey::Aes256 { enc_key, .. } => enc_key, + SymmetricCipherKey::ChaCha20 { .. } => return Err(Unspecified), + }; + + let mut buf = [0u8; BLOCK_LEN]; + + unsafe { + AES_ctr128_encrypt( + in_out.as_ptr(), + in_out.as_mut_ptr(), + in_out.len(), + key, + ivec.as_mut_ptr(), + buf.as_mut_slice().as_mut_ptr(), + num.as_mut_ptr(), + ); + }; + + Zeroize::zeroize(buf.as_mut_slice()); } + AlgorithmId::Aes128cbc | AlgorithmId::Aes256cbc => { + let key = match key { + SymmetricCipherKey::Aes128 { enc_key, .. } + | SymmetricCipherKey::Aes256 { enc_key, .. } => enc_key, + SymmetricCipherKey::ChaCha20 { .. } => return Err(Unspecified), + }; + unsafe { + AES_cbc_encrypt( + in_out.as_ptr(), + in_out.as_mut_ptr(), + in_out.len(), + key, + ivec.as_mut_ptr(), + AES_ENCRYPT, + ); + } + } + } + Ok(iv) +} - match alg.get_operating_mode() { - OperatingMode::Block(strategy) => match strategy { - PaddingStrategy::PKCS7 => { - let block_size: u8 = BLOCK_LEN.try_into().map_err(|_| Unspecified)?; +fn decrypt<'a, const KEY_LEN: usize, const IV_LEN: usize, const BLOCK_LEN: usize>( + algorithm: &Algorithm, + key: &SymmetricCipherKey, + iv: NonceIV, + in_out: &'a mut [u8], +) -> Result<&'a mut [u8], Unspecified> { + let mut final_len = in_out.len(); + + let mut iv: [u8; IV_LEN] = iv.try_into()?; + + match algorithm.get_id() { + AlgorithmId::Aes128ctr | AlgorithmId::Aes256ctr => { + let mut num = MaybeUninit::::new(0); + let key = match key { + SymmetricCipherKey::Aes128 { enc_key, .. } + | SymmetricCipherKey::Aes256 { enc_key, .. } => enc_key, + SymmetricCipherKey::ChaCha20 { .. } => return Err(Unspecified), + }; + let mut buf = [0u8; BLOCK_LEN]; + unsafe { + AES_ctr128_encrypt( + in_out.as_ptr(), + in_out.as_mut_ptr(), + in_out.len(), + key, + iv.as_mut_ptr(), + buf.as_mut_slice().as_mut_ptr(), + num.as_mut_ptr(), + ); + }; + Zeroize::zeroize(buf.as_mut_slice()); + } + AlgorithmId::Aes128cbc | AlgorithmId::Aes256cbc => { + let key = match key { + SymmetricCipherKey::Aes128 { dec_key, .. } + | SymmetricCipherKey::Aes256 { dec_key, .. } => dec_key, + SymmetricCipherKey::ChaCha20 { .. } => return Err(Unspecified), + }; + unsafe { + AES_cbc_encrypt( + in_out.as_ptr(), + in_out.as_mut_ptr(), + in_out.len(), + key, + iv.as_mut_ptr(), + AES_DECRYPT, + ); + } + } + } - if in_out.is_empty() || in_out.len() < BLOCK_LEN { - return Err(Unspecified); - } - let padding: u8 = in_out[in_out.len() - 1]; - if padding == 0 || padding > block_size { - return Err(Unspecified); - } + match algorithm.get_operating_mode() { + OperatingMode::Block(strategy) => match strategy { + PaddingStrategy::PKCS7 => { + let block_size: u8 = BLOCK_LEN.try_into().map_err(|_| Unspecified)?; - for item in in_out.iter().skip(in_out.len() - padding as usize) { - if *item != padding { - return Err(Unspecified); - } - } + if in_out.is_empty() || in_out.len() < BLOCK_LEN { + return Err(Unspecified); + } + let padding: u8 = in_out[in_out.len() - 1]; + if padding == 0 || padding > block_size { + return Err(Unspecified); + } - final_len = in_out.len() - padding as usize; + for item in in_out.iter().skip(in_out.len() - padding as usize) { + if *item != padding { + return Err(Unspecified); + } } - PaddingStrategy::Unpadded => {} - }, - OperatingMode::Stream => {} - } - Ok(&mut in_out[0..final_len]) + final_len = in_out.len() - padding as usize; + } + PaddingStrategy::Unpadded => {} + }, + OperatingMode::Stream => {} } + + Ok(&mut in_out[0..final_len]) } impl SymmetricCipherKey { diff --git a/aws-lc-rs/src/iv.rs b/aws-lc-rs/src/iv.rs index 08638c9a37b..e15ebaf2dbc 100644 --- a/aws-lc-rs/src/iv.rs +++ b/aws-lc-rs/src/iv.rs @@ -4,13 +4,17 @@ // SPDX-License-Identifier: Apache-2.0 OR ISC #![allow(dead_code)] +//! Initialization Vector (IV) cryptographic primitives + use crate::error::Unspecified; use crate::{error, rand}; +/// An initalization vector that must be unique for the lifetime of the associated key +/// it is used with. pub struct NonceIV([u8; L]); impl NonceIV { - /// Constructs a `IV` with the given value, assuming that the value is + /// Constructs a [`NonceIV`] with the given value, assuming that the value is /// unique for the lifetime of the key it is being used with. /// /// Fails if `value` isn't `L` bytes long. @@ -22,7 +26,7 @@ impl NonceIV { Ok(Self::assume_unique_for_key(*value)) } - /// Constructs a `Nonce` with the given value, assuming that the value is + /// Constructs a [`NonceIV`] with the given value, assuming that the value is /// unique for the lifetime of the key it is being used with. #[inline] #[must_use] @@ -30,11 +34,18 @@ impl NonceIV { Self(value) } + /// Returns the size of the nonce in bytes. + #[allow(clippy::must_use_candidate)] pub fn size() -> usize { L } - /// Constructs a new IVNonce from pseudo-random bytes. + /// Constructs a new [`NonceIV`] from pseudo-random bytes. + /// + /// # Errors + /// + /// * [`Unspecified`]: Returned if there is a failure generating `L` bytes. + /// pub fn new() -> Result { let mut iv_bytes = [0u8; L]; rand::fill(&mut iv_bytes)?; @@ -69,3 +80,20 @@ impl From<[u8; L]> for NonceIV { NonceIV(bytes) } } + +impl TryFrom<&[u8]> for NonceIV { + type Error = Unspecified; + + fn try_from(value: &[u8]) -> Result { + NonceIV::::try_assume_unique_for_key(value) + } +} + +impl TryFrom> for [u8; L] { + type Error = Unspecified; + + fn try_from(value: NonceIV) -> Result { + let value: [u8; L] = value.0.try_into().map_err(|_|Unspecified)?; + Ok(value) + } +} diff --git a/aws-lc-rs/src/lib.rs b/aws-lc-rs/src/lib.rs index f7087976aba..bdb595fc023 100644 --- a/aws-lc-rs/src/lib.rs +++ b/aws-lc-rs/src/lib.rs @@ -146,7 +146,7 @@ mod ec; mod ed25519; mod endian; mod evp_pkey; -mod iv; +pub mod iv; mod ptr; mod rsa; From 692e05135880f9fca1e0eeb22893f6b8964d7651 Mon Sep 17 00:00:00 2001 From: Sean McGrail Date: Thu, 18 May 2023 20:01:41 -0700 Subject: [PATCH 18/54] Non-generic public interface --- aws-lc-rs/src/aead.rs | 10 +- .../src/aead/chacha20_poly1305_openssh.rs | 6 +- aws-lc-rs/src/aead/nonce.rs | 16 +- .../src/aead/nonce_sequence/counter32.rs | 4 +- .../src/aead/nonce_sequence/counter64.rs | 4 +- aws-lc-rs/src/cipher.rs | 444 ++++++++++-------- aws-lc-rs/src/iv.rs | 77 ++- 7 files changed, 335 insertions(+), 226 deletions(-) diff --git a/aws-lc-rs/src/aead.rs b/aws-lc-rs/src/aead.rs index ce16479326a..329c96a2137 100644 --- a/aws-lc-rs/src/aead.rs +++ b/aws-lc-rs/src/aead.rs @@ -823,7 +823,7 @@ pub(crate) fn aead_open_combined( #[cfg(test)] mod tests { use super::*; - use crate::{iv::NonceIV, test::from_hex}; + use crate::{iv::FixedLength, test::from_hex}; #[test] fn test_aes_128() { @@ -844,14 +844,14 @@ mod tests { #[allow(deprecated)] less_safe_key // Test coverage for `seal_in_place`, which calls `seal_in_place_append_tag`. - .seal_in_place(Nonce(NonceIV::from(nonce)), Aad::empty(), &mut in_out) + .seal_in_place(Nonce(FixedLength::from(nonce)), Aad::empty(), &mut in_out) .unwrap(); let mut in_out_clone = in_out.clone(); let nonce: [u8; NONCE_LEN] = og_nonce.as_slice().try_into().unwrap(); assert!(less_safe_key .open_in_place( - Nonce(NonceIV::from(nonce)), + Nonce(FixedLength::from(nonce)), Aad::from("test"), &mut in_out_clone ) @@ -861,12 +861,12 @@ mod tests { let mut nonce: [u8; NONCE_LEN] = og_nonce.as_slice().try_into().unwrap(); nonce[0] = 0; assert!(less_safe_key - .open_in_place(Nonce(NonceIV::from(nonce)), Aad::empty(), &mut in_out_clone) + .open_in_place(Nonce(FixedLength::from(nonce)), Aad::empty(), &mut in_out_clone) .is_err()); let nonce: [u8; NONCE_LEN] = og_nonce.as_slice().try_into().unwrap(); less_safe_key - .open_in_place(Nonce(NonceIV::from(nonce)), Aad::empty(), &mut in_out) + .open_in_place(Nonce(FixedLength::from(nonce)), Aad::empty(), &mut in_out) .unwrap(); assert_eq!(plaintext, in_out[..plaintext.len()]); diff --git a/aws-lc-rs/src/aead/chacha20_poly1305_openssh.rs b/aws-lc-rs/src/aead/chacha20_poly1305_openssh.rs index 6c2d297e565..ad7673d022f 100644 --- a/aws-lc-rs/src/aead/chacha20_poly1305_openssh.rs +++ b/aws-lc-rs/src/aead/chacha20_poly1305_openssh.rs @@ -23,7 +23,7 @@ use super::{poly1305, Nonce, Tag}; use crate::cipher::block::BLOCK_LEN; use crate::cipher::chacha::{self, ChaCha20Key}; -use crate::iv::NonceIV; +use crate::iv::FixedLength; use crate::{constant_time, endian::BigEndian, error}; use core::convert::TryInto; @@ -56,7 +56,7 @@ impl SealingKey { tag_out: &mut [u8; TAG_LEN], ) { let nonce = make_nonce(sequence_number); - let poly_key = derive_poly1305_key(&self.key.k_2, Nonce(NonceIV::from(nonce.as_ref()))); + let poly_key = derive_poly1305_key(&self.key.k_2, Nonce(FixedLength::from(nonce.as_ref()))); { let (len_in_out, data_and_padding_in_out) = @@ -131,7 +131,7 @@ impl OpeningKey { // We must verify the tag before decrypting so that // `ciphertext_in_plaintext_out` is unmodified if verification fails. // This is beyond what we guarantee. - let poly_key = derive_poly1305_key(&self.key.k_2, Nonce(NonceIV::from(nonce.as_ref()))); + let poly_key = derive_poly1305_key(&self.key.k_2, Nonce(FixedLength::from(nonce.as_ref()))); verify(poly_key, ciphertext_in_plaintext_out, tag)?; let plaintext_in_ciphertext_out = &mut ciphertext_in_plaintext_out[PACKET_LENGTH_LEN..]; diff --git a/aws-lc-rs/src/aead/nonce.rs b/aws-lc-rs/src/aead/nonce.rs index 67b938ef7cc..7fd57207010 100644 --- a/aws-lc-rs/src/aead/nonce.rs +++ b/aws-lc-rs/src/aead/nonce.rs @@ -5,7 +5,7 @@ use crate::endian::{ArrayEncoding, BigEndian, Encoding}; use crate::error; -use crate::iv::NonceIV; +use crate::iv::FixedLength; use std::mem::transmute_copy; /// A nonce for a single AEAD opening or sealing operation. @@ -14,7 +14,7 @@ use std::mem::transmute_copy; /// /// `Nonce` intentionally doesn't implement `Clone` to ensure that each one is /// consumed at most once. -pub struct Nonce(pub(crate) NonceIV); +pub struct Nonce(pub(crate) FixedLength); impl Nonce { /// Constructs a `Nonce` with the given value, assuming that the value is @@ -25,7 +25,7 @@ impl Nonce { /// `error::Unspecified` when byte slice length is not `NONCE_LEN` #[inline] pub fn try_assume_unique_for_key(value: &[u8]) -> Result { - Ok(Self(NonceIV::::try_assume_unique_for_key( + Ok(Self(FixedLength::::try_assume_unique_for_key( value, )?)) } @@ -35,7 +35,7 @@ impl Nonce { #[inline] #[must_use] pub fn assume_unique_for_key(value: [u8; NONCE_LEN]) -> Self { - Self(NonceIV::::assume_unique_for_key(value)) + Self(FixedLength::::assume_unique_for_key(value)) } } @@ -49,7 +49,7 @@ impl AsRef<[u8; NONCE_LEN]> for Nonce { impl From<&[u8; NONCE_LEN]> for Nonce { #[inline] fn from(bytes: &[u8; NONCE_LEN]) -> Self { - Self(NonceIV::from(bytes)) + Self(FixedLength::from(bytes)) } } @@ -58,7 +58,7 @@ impl From<&[u32; NONCE_LEN / 4]> for Nonce { fn from(values: &[u32; NONCE_LEN / 4]) -> Self { unsafe { let bytes: [u8; NONCE_LEN] = transmute_copy(values); - Nonce(NonceIV::from(bytes)) + Nonce(FixedLength::from(bytes)) } } } @@ -67,7 +67,7 @@ impl From> for Nonce { #[inline] fn from(number: BigEndian) -> Self { let nonce = [BigEndian::ZERO, BigEndian::ZERO, number]; - Nonce(NonceIV::from(*(nonce.as_byte_array()))) + Nonce(FixedLength::from(*(nonce.as_byte_array()))) } } @@ -77,7 +77,7 @@ impl From<&[u8; IV_LEN]> for Nonce { fn from(bytes: &[u8; IV_LEN]) -> Self { let mut nonce_bytes = [0u8; NONCE_LEN]; nonce_bytes.copy_from_slice(&bytes[0..NONCE_LEN]); - Nonce(NonceIV::from(nonce_bytes)) + Nonce(FixedLength::from(nonce_bytes)) } } diff --git a/aws-lc-rs/src/aead/nonce_sequence/counter32.rs b/aws-lc-rs/src/aead/nonce_sequence/counter32.rs index bb722eb9936..8f04f4fb765 100644 --- a/aws-lc-rs/src/aead/nonce_sequence/counter32.rs +++ b/aws-lc-rs/src/aead/nonce_sequence/counter32.rs @@ -3,7 +3,7 @@ use crate::aead::{Nonce, NonceSequence, NONCE_LEN}; use crate::error::Unspecified; -use crate::iv::NonceIV; +use crate::iv::FixedLength; /// `Counter32` is an implementation of the `NonceSequence` trait. /// The internal state of a `Counter32` is a 32-bit unsigned counter that @@ -119,7 +119,7 @@ impl NonceSequence for Counter32 { nonce_bytes[..8].copy_from_slice(&self.identifier); nonce_bytes[8..].copy_from_slice(&counter_bytes); self.counter = self.counter.wrapping_add(1); - Ok(Nonce(NonceIV::from(nonce_bytes))) + Ok(Nonce(FixedLength::from(nonce_bytes))) } } diff --git a/aws-lc-rs/src/aead/nonce_sequence/counter64.rs b/aws-lc-rs/src/aead/nonce_sequence/counter64.rs index 067f7678799..0ebcb74dfe3 100644 --- a/aws-lc-rs/src/aead/nonce_sequence/counter64.rs +++ b/aws-lc-rs/src/aead/nonce_sequence/counter64.rs @@ -3,7 +3,7 @@ use crate::aead::{Nonce, NonceSequence, NONCE_LEN}; use crate::error::Unspecified; -use crate::iv::NonceIV; +use crate::iv::FixedLength; /// `Counter64` is an implementation of the `NonceSequence` trait. /// The internal state of a `Counter64` is a 64-bit unsigned counter that @@ -117,7 +117,7 @@ impl NonceSequence for Counter64 { nonce_bytes[..4].copy_from_slice(&self.identifier); nonce_bytes[4..].copy_from_slice(&bytes); self.counter = self.counter.wrapping_add(1); - Ok(Nonce(NonceIV::from(nonce_bytes))) + Ok(Nonce(FixedLength::from(nonce_bytes))) } } diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index 2b04e6871ae..0c9a05bec89 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -48,7 +48,7 @@ use crate::cipher::aes::{encrypt_block_aes, Aes128Key, Aes256Key}; use crate::cipher::block::Block; use crate::cipher::chacha::ChaCha20Key; use crate::error::Unspecified; -use crate::iv::NonceIV; +use crate::iv::{FixedLength, NonceIV}; use aws_lc::{ AES_cbc_encrypt, AES_ctr128_encrypt, AES_set_decrypt_key, AES_set_encrypt_key, AES_DECRYPT, AES_ENCRYPT, AES_KEY, @@ -100,14 +100,6 @@ impl Drop for SymmetricCipherKey { } } -#[derive(Clone, Copy)] -enum AlgorithmId { - Aes128ctr, - Aes128cbc, - Aes256ctr, - Aes256cbc, -} - #[allow(dead_code)] #[derive(Clone, Copy)] enum PaddingStrategy { @@ -135,8 +127,7 @@ enum OperatingMode { /// * [`AES_128_CBC_PKCS7_PADDING`] /// * [`AES_256_CBC_PKCS7_PADDING`] /// -pub struct Algorithm( - AlgorithmId, +pub struct SymmetricCipher( OperatingMode, ); @@ -150,60 +141,89 @@ pub const AES_256_KEY_LEN: usize = 32; pub const AES_IV_LEN: usize = 16; const AES_BLOCK_LEN: usize = 16; +pub enum Algorithm { + AES_128_CTR(SymmetricCipher), + AES_256_CTR(SymmetricCipher), + AES_128_CBC_PKCS7_PADDING(SymmetricCipher), + AES_256_CBC_PKCS7_PADDING(SymmetricCipher), +} + +impl Algorithm { + fn get_operating_mode(&self) -> &OperatingMode { + match self { + Algorithm::AES_128_CTR(v) => v.get_operating_mode(), + Algorithm::AES_256_CTR(v) => v.get_operating_mode(), + Algorithm::AES_128_CBC_PKCS7_PADDING(v) => v.get_operating_mode(), + Algorithm::AES_256_CBC_PKCS7_PADDING(v) => v.get_operating_mode(), + } + } + + fn get_block_len(&self) -> usize { + match self { + Algorithm::AES_128_CTR(v) => v.get_block_size(), + Algorithm::AES_256_CTR(v) => v.get_block_size(), + Algorithm::AES_128_CBC_PKCS7_PADDING(v) => v.get_block_size(), + Algorithm::AES_256_CBC_PKCS7_PADDING(v) => v.get_block_size(), + } + } +} + /// AES-128 Counter (CTR) Mode -pub const AES_128_CTR: Algorithm = - Algorithm(AlgorithmId::Aes128ctr, OperatingMode::Stream); +pub const AES_128_CTR: Algorithm = Algorithm::AES_128_CTR(SymmetricCipher(OperatingMode::Stream)); /// AES-128 Cipher block chaining (CBC) Mode using PKCS#7 padding. -pub const AES_128_CBC_PKCS7_PADDING: Algorithm = - Algorithm( - AlgorithmId::Aes128cbc, - OperatingMode::Block(PaddingStrategy::PKCS7), - ); +pub const AES_128_CBC_PKCS7_PADDING: Algorithm = Algorithm::AES_128_CBC_PKCS7_PADDING( + SymmetricCipher(OperatingMode::Block(PaddingStrategy::PKCS7)), +); /// AES-256 Counter (CTR) Mode -pub const AES_256_CTR: Algorithm = - Algorithm(AlgorithmId::Aes256ctr, OperatingMode::Stream); +pub const AES_256_CTR: Algorithm = Algorithm::AES_256_CTR(SymmetricCipher(OperatingMode::Stream)); /// AES-256 Cipher block chaining (CBC) Mode using PKCS#7 padding. -pub const AES_256_CBC_PKCS7_PADDING: Algorithm = - Algorithm( - AlgorithmId::Aes256cbc, - OperatingMode::Block(PaddingStrategy::PKCS7), - ); +pub const AES_256_CBC_PKCS7_PADDING: Algorithm = Algorithm::AES_256_CBC_PKCS7_PADDING( + SymmetricCipher(OperatingMode::Block(PaddingStrategy::PKCS7)), +); impl - Algorithm + SymmetricCipher { #[inline] - fn get_id(&self) -> &AlgorithmId { + fn get_operating_mode(&self) -> &OperatingMode { &self.0 } - #[inline] - fn get_operating_mode(&self) -> &OperatingMode { - &self.1 + fn try_into_key<'a>(&self, key: &'a [u8]) -> Result<&'a [u8; KEY_LEN], Unspecified> { + let key: &'a [u8; KEY_LEN] = key.try_into()?; + Ok(key) + } + + fn get_block_size(&self) -> usize { + BLOCK_LEN + } + + fn new_block(&self) -> [u8; BLOCK_LEN] { + [0u8; BLOCK_LEN] + } + + fn new_randomized_nonce(&self) -> Result { + match IV_LEN { + 16 => Ok(NonceIV::Size128(FixedLength::<16>::new()?)), + _ => Err(Unspecified), + } + } + + fn unsafe_nonce_array(&self) -> [u8; IV_LEN] { + [0u8; IV_LEN] } } /// A key bound to a particular cipher algorithm. -pub struct UnboundCipherKey { - algorithm: &'static Algorithm, +pub struct UnboundCipherKey { + algorithm: &'static Algorithm, key: SymmetricCipherKey, } -impl - UnboundCipherKey -{ - #[inline] - fn get_algorithm(&self) -> &'static Algorithm { - self.algorithm - } -} - -impl - UnboundCipherKey -{ +impl UnboundCipherKey { /// Constructs a [`UnboundCipherKey`]. /// /// # Errors @@ -211,53 +231,49 @@ impl /// * [`Unspecified`] if `key_bytes.len()` does not match the /// length required by `algorithm`. /// - pub fn new( - algorithm: &'static Algorithm, - key_bytes: &[u8], - ) -> Result { - let key: &[u8; KEY_LEN] = key_bytes.try_into()?; - let key = match algorithm.get_id() { - AlgorithmId::Aes128ctr | AlgorithmId::Aes128cbc => SymmetricCipherKey::aes128(key), - AlgorithmId::Aes256ctr | AlgorithmId::Aes256cbc => SymmetricCipherKey::aes256(key), + pub fn new(algorithm: &'static Algorithm, key_bytes: &[u8]) -> Result { + let key = match algorithm { + Algorithm::AES_128_CTR(v) | Algorithm::AES_128_CBC_PKCS7_PADDING(v) => { + let key = v.try_into_key(key_bytes)?; + SymmetricCipherKey::aes128(key) + } + Algorithm::AES_256_CTR(v) | Algorithm::AES_256_CBC_PKCS7_PADDING(v) => { + let key = v.try_into_key(key_bytes)?; + SymmetricCipherKey::aes256(key) + } }?; Ok(UnboundCipherKey { algorithm, key }) } + + #[inline] + fn get_algorithm(&self) -> &'static Algorithm { + self.algorithm + } } /// An encryting cipher key. -pub struct EncryptingKey { - cipher_key: UnboundCipherKey, - iv: NonceIV, +pub struct EncryptingKey { + cipher_key: UnboundCipherKey, + iv: NonceIV, } -impl - EncryptingKey -{ - /// Constructs a new [`EncryptingKey`] using provided `cipher_key` and `iv`. - /// It is recommended to use `EncryptingKey::new` to avoid the potential reuse of an - /// initialization vector (`IV`). - pub fn less_safe_new_using_iv( - cipher_key: UnboundCipherKey, - iv_bytes: [u8; IV_LEN], - ) -> EncryptingKey { - EncryptingKey { - cipher_key, - iv: NonceIV::assume_unique_for_key(iv_bytes), - } - } - +impl EncryptingKey { /// Constructs a new [`EncryptingKey`]. /// /// # Errors /// /// * [`Unspecified`]: Returned if a randomized IV fails to be generated. /// - pub fn new( - cipher_key: UnboundCipherKey, - ) -> Result, Unspecified> { + pub fn new(key: UnboundCipherKey) -> Result { + let iv = match key.get_algorithm() { + Algorithm::AES_128_CTR(v) => v.new_randomized_nonce(), + Algorithm::AES_256_CTR(v) => v.new_randomized_nonce(), + Algorithm::AES_128_CBC_PKCS7_PADDING(v) => v.new_randomized_nonce(), + Algorithm::AES_256_CBC_PKCS7_PADDING(v) => v.new_randomized_nonce(), + }?; Ok(EncryptingKey { - cipher_key, - iv: NonceIV::::new()?, + cipher_key: key, + iv, }) } @@ -270,7 +286,7 @@ impl /// /// * [`Unspecified`]: Returned if the data fails to be encrypted. /// - pub fn encrypt(self, in_out: &mut InOut) -> Result, Unspecified> + pub fn encrypt(self, in_out: &mut InOut) -> Result where InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, { @@ -284,20 +300,15 @@ impl } /// An decrypting cipher key. -pub struct DecryptingKey { - cipher_key: UnboundCipherKey, - iv: NonceIV, +pub struct DecryptingKey { + cipher_key: UnboundCipherKey, + iv: NonceIV, } -impl - DecryptingKey -{ +impl DecryptingKey { /// Constructs a new [`DecryptingKey`]. #[must_use] - pub fn new( - cipher_key: UnboundCipherKey, - iv: NonceIV, - ) -> DecryptingKey { + pub fn new(cipher_key: UnboundCipherKey, iv: NonceIV) -> DecryptingKey { DecryptingKey { cipher_key, iv } } @@ -323,14 +334,26 @@ impl } /// Less safe cipher key that allows for specifying a user provided [`NonceIV`]. -pub struct LessSafeCipherKey { - algorithm: &'static Algorithm, +pub struct LessSafeCipherKey { + algorithm: &'static Algorithm, key: SymmetricCipherKey, } -impl - LessSafeCipherKey -{ +impl LessSafeCipherKey { + pub fn new(algorithm: &'static Algorithm, key_bytes: &[u8]) -> Result { + let key = match algorithm { + Algorithm::AES_128_CTR(v) | Algorithm::AES_128_CBC_PKCS7_PADDING(v) => { + let key = v.try_into_key(key_bytes)?; + SymmetricCipherKey::aes128(key) + } + Algorithm::AES_256_CTR(v) | Algorithm::AES_256_CBC_PKCS7_PADDING(v) => { + let key = v.try_into_key(key_bytes)?; + SymmetricCipherKey::aes256(key) + } + }?; + Ok(LessSafeCipherKey { algorithm, key }) + } + /// Encrypts the data `in_out` in-place. If the algorithm bound to this key uses padding /// then the `in_out` will be extended to add the necessary padding. /// @@ -340,11 +363,7 @@ impl /// /// * [`Unspecified`]: Returned if the data fails to be encrypted. /// - pub fn encrypt( - &self, - iv: NonceIV, - in_out: &mut InOut, - ) -> Result, Unspecified> + pub fn encrypt(&self, iv: NonceIV, in_out: &mut InOut) -> Result where InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, { @@ -364,33 +383,35 @@ impl #[allow(unused_mut)] pub fn decrypt<'a>( &self, - iv: NonceIV, + iv: NonceIV, in_out: &'a mut [u8], ) -> Result<&'a mut [u8], Unspecified> { decrypt(self.algorithm, &self.key, iv, in_out) } } -fn encrypt( - algorithm: &Algorithm, +fn encrypt( + algorithm: &'static Algorithm, key: &SymmetricCipherKey, - iv: NonceIV, + iv: NonceIV, in_out: &mut InOut, -) -> Result, Unspecified> +) -> Result where InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, { + let block_len: usize = algorithm.get_block_len(); + match algorithm.get_operating_mode() { OperatingMode::Block(strategy) => match strategy { PaddingStrategy::PKCS7 => { let in_out_len = in_out.as_mut().len(); // This implements PKCS#7 padding scheme, used by aws-lc if we were using EVP_CIPHER API's - let remainder = in_out_len % BLOCK_LEN; + let remainder = in_out_len % block_len; if remainder == 0 { - let block_size: u8 = BLOCK_LEN.try_into().map_err(|_| Unspecified)?; - in_out.extend(vec![block_size; BLOCK_LEN].iter()); + let block_size: u8 = block_len.try_into().map_err(|_| Unspecified)?; + in_out.extend(vec![block_size; block_len].iter()); } else { - let padding_size = BLOCK_LEN - remainder; + let padding_size = block_len - remainder; let v: u8 = padding_size.try_into().map_err(|_| Unspecified)?; // Heap allocation :( in_out.extend(vec![v; padding_size].iter()); @@ -403,114 +424,161 @@ where let in_out = in_out.as_mut(); - let mut ivec = [0u8; IV_LEN]; - ivec.copy_from_slice(iv.as_ref()); + let mut ivec = { + let mut ivec = match algorithm { + Algorithm::AES_128_CTR(v) | Algorithm::AES_128_CBC_PKCS7_PADDING(v) => { + v.unsafe_nonce_array() + } + Algorithm::AES_256_CTR(v) | Algorithm::AES_256_CBC_PKCS7_PADDING(v) => { + v.unsafe_nonce_array() + } + }; + ivec.copy_from_slice(iv.as_ref()); + ivec + }; - match algorithm.get_id() { - AlgorithmId::Aes128ctr | AlgorithmId::Aes256ctr => { - let mut num = MaybeUninit::::new(0); + match algorithm { + Algorithm::AES_128_CTR(v) => { let key = match key { - SymmetricCipherKey::Aes128 { enc_key, .. } - | SymmetricCipherKey::Aes256 { enc_key, .. } => enc_key, - SymmetricCipherKey::ChaCha20 { .. } => return Err(Unspecified), + SymmetricCipherKey::Aes128 { enc_key, .. } => enc_key, + _ => return Err(Unspecified), }; - let mut buf = [0u8; BLOCK_LEN]; - - unsafe { - AES_ctr128_encrypt( - in_out.as_ptr(), - in_out.as_mut_ptr(), - in_out.len(), - key, - ivec.as_mut_ptr(), - buf.as_mut_slice().as_mut_ptr(), - num.as_mut_ptr(), - ); + let mut buf = v.new_block(); + aes_ctr128_encrypt(key, &mut ivec, &mut buf, in_out); + } + Algorithm::AES_256_CTR(v) => { + let key = match key { + SymmetricCipherKey::Aes256 { enc_key, .. } => enc_key, + _ => return Err(Unspecified), }; - Zeroize::zeroize(buf.as_mut_slice()); + let mut buf = v.new_block(); + + aes_ctr128_encrypt(key, &mut ivec, &mut buf, in_out); } - AlgorithmId::Aes128cbc | AlgorithmId::Aes256cbc => { + Algorithm::AES_128_CBC_PKCS7_PADDING(_) => { let key = match key { - SymmetricCipherKey::Aes128 { enc_key, .. } - | SymmetricCipherKey::Aes256 { enc_key, .. } => enc_key, - SymmetricCipherKey::ChaCha20 { .. } => return Err(Unspecified), + SymmetricCipherKey::Aes128 { enc_key, .. } => enc_key, + _ => return Err(Unspecified), }; - unsafe { - AES_cbc_encrypt( - in_out.as_ptr(), - in_out.as_mut_ptr(), - in_out.len(), - key, - ivec.as_mut_ptr(), - AES_ENCRYPT, - ); - } + aes_cbc_encrypt(key, &mut ivec, in_out); + } + Algorithm::AES_256_CBC_PKCS7_PADDING(_) => { + let key = match key { + SymmetricCipherKey::Aes256 { enc_key, .. } => enc_key, + _ => return Err(Unspecified), + }; + aes_cbc_encrypt(key, &mut ivec, in_out); } } Ok(iv) } -fn decrypt<'a, const KEY_LEN: usize, const IV_LEN: usize, const BLOCK_LEN: usize>( - algorithm: &Algorithm, +fn aes_ctr128_encrypt(key: &AES_KEY, iv: &mut [u8], block_buffer: &mut [u8], in_out: &mut [u8]) { + let mut num = MaybeUninit::::new(0); + + unsafe { + AES_ctr128_encrypt( + in_out.as_ptr(), + in_out.as_mut_ptr(), + in_out.len(), + key, + iv.as_mut_ptr(), + block_buffer.as_mut_ptr(), + num.as_mut_ptr(), + ); + }; + + Zeroize::zeroize(block_buffer); +} + +fn aes_cbc_encrypt(key: &AES_KEY, iv: &mut [u8], in_out: &mut [u8]) { + unsafe { + AES_cbc_encrypt( + in_out.as_ptr(), + in_out.as_mut_ptr(), + in_out.len(), + key, + iv.as_mut_ptr(), + AES_ENCRYPT, + ); + } +} + +fn aes_cbc_decrypt(key: &AES_KEY, iv: &mut [u8], in_out: &mut [u8]) { + unsafe { + AES_cbc_encrypt( + in_out.as_ptr(), + in_out.as_mut_ptr(), + in_out.len(), + key, + iv.as_mut_ptr(), + AES_DECRYPT, + ); + } +} + +fn decrypt<'a>( + algorithm: &'static Algorithm, key: &SymmetricCipherKey, - iv: NonceIV, + mut iv: NonceIV, in_out: &'a mut [u8], ) -> Result<&'a mut [u8], Unspecified> { let mut final_len = in_out.len(); - let mut iv: [u8; IV_LEN] = iv.try_into()?; + let iv = iv.as_mut(); - match algorithm.get_id() { - AlgorithmId::Aes128ctr | AlgorithmId::Aes256ctr => { - let mut num = MaybeUninit::::new(0); + match algorithm { + Algorithm::AES_128_CTR(v) => { let key = match key { - SymmetricCipherKey::Aes128 { enc_key, .. } - | SymmetricCipherKey::Aes256 { enc_key, .. } => enc_key, - SymmetricCipherKey::ChaCha20 { .. } => return Err(Unspecified), + SymmetricCipherKey::Aes128 { enc_key, .. } => enc_key, + _ => return Err(Unspecified), }; - let mut buf = [0u8; BLOCK_LEN]; - unsafe { - AES_ctr128_encrypt( - in_out.as_ptr(), - in_out.as_mut_ptr(), - in_out.len(), - key, - iv.as_mut_ptr(), - buf.as_mut_slice().as_mut_ptr(), - num.as_mut_ptr(), - ); + + let mut buf = v.new_block(); + aes_ctr128_encrypt(key, iv, &mut buf, in_out); + } + Algorithm::AES_256_CTR(v) => { + let key = match key { + SymmetricCipherKey::Aes256 { enc_key, .. } => enc_key, + _ => return Err(Unspecified), }; - Zeroize::zeroize(buf.as_mut_slice()); + + let mut buf = v.new_block(); + + aes_ctr128_encrypt(key, iv, &mut buf, in_out); } - AlgorithmId::Aes128cbc | AlgorithmId::Aes256cbc => { + Algorithm::AES_128_CBC_PKCS7_PADDING(_) => { let key = match key { - SymmetricCipherKey::Aes128 { dec_key, .. } - | SymmetricCipherKey::Aes256 { dec_key, .. } => dec_key, - SymmetricCipherKey::ChaCha20 { .. } => return Err(Unspecified), + SymmetricCipherKey::Aes128 { dec_key, .. } => dec_key, + _ => return Err(Unspecified), }; - unsafe { - AES_cbc_encrypt( - in_out.as_ptr(), - in_out.as_mut_ptr(), - in_out.len(), - key, - iv.as_mut_ptr(), - AES_DECRYPT, - ); - } + aes_cbc_decrypt(key, iv, in_out); + } + Algorithm::AES_256_CBC_PKCS7_PADDING(_) => { + let key = match key { + SymmetricCipherKey::Aes256 { dec_key, .. } => dec_key, + _ => return Err(Unspecified), + }; + aes_cbc_decrypt(key, iv, in_out); } } match algorithm.get_operating_mode() { OperatingMode::Block(strategy) => match strategy { PaddingStrategy::PKCS7 => { - let block_size: u8 = BLOCK_LEN.try_into().map_err(|_| Unspecified)?; + let block_len = algorithm.get_block_len(); - if in_out.is_empty() || in_out.len() < BLOCK_LEN { + let block_size: u8 = algorithm + .get_block_len() + .try_into() + .map_err(|_| Unspecified)?; + + if in_out.is_empty() || in_out.len() < block_len { return Err(Unspecified); } + let padding: u8 = in_out[in_out.len() - 1]; if padding == 0 || padding > block_size { return Err(Unspecified); @@ -674,15 +742,7 @@ mod tests { assert_eq!(expected_result.as_slice(), result.as_ref()); } - fn helper_test_cipher_n_bytes< - const KEY_LEN: usize, - const IV_LEN: usize, - const BLOCK_LEN: usize, - >( - key: &[u8], - alg: &'static Algorithm, - n: usize, - ) { + fn helper_test_cipher_n_bytes(key: &[u8], alg: &'static Algorithm, n: usize) { let mut input: Vec = Vec::with_capacity(n); for i in 0..n { let byte: u8 = i.try_into().unwrap(); @@ -760,12 +820,12 @@ mod tests { }; let alg = $cipher; - let cipher_key = UnboundCipherKey::new(alg, &key).unwrap(); - let encrypting_key = - EncryptingKey::less_safe_new_using_iv(cipher_key, iv.try_into().unwrap()); + let encrypting_key = LessSafeCipherKey::new(alg, &key).unwrap(); let mut in_out = input.clone(); - let decrypt_iv = encrypting_key.encrypt(&mut in_out).unwrap(); + let decrypt_iv = encrypting_key + .encrypt(NonceIV::try_from(iv).unwrap(), &mut in_out) + .unwrap(); assert_eq!(expected_ciphertext, in_out); let cipher_key2 = UnboundCipherKey::new(alg, &key).unwrap(); diff --git a/aws-lc-rs/src/iv.rs b/aws-lc-rs/src/iv.rs index e15ebaf2dbc..7328ad410d9 100644 --- a/aws-lc-rs/src/iv.rs +++ b/aws-lc-rs/src/iv.rs @@ -9,11 +9,60 @@ use crate::error::Unspecified; use crate::{error, rand}; +pub enum NonceIV { + Size128(FixedLength<16>), +} + +impl NonceIV { + /// Constructs a [`NonceIV`] with the given value, assuming that the value is + /// unique for the lifetime of the key it is being used with. + /// + /// # Errors + /// `error::Unspecified` when byte slice length is not a supported length. + #[inline] + pub fn try_assume_unique_for_key(value: &[u8]) -> Result { + match value.len() { + 16 => Ok(NonceIV::Size128(FixedLength::<16>::try_from(value)?)), + _ => Err(error::Unspecified), + } + } + + /// Returns the size of the nonce in bytes. + #[allow(clippy::must_use_candidate)] + pub fn size(&self) -> usize { + match self { + NonceIV::Size128(v) => v.size(), + } + } +} + +impl AsRef<[u8]> for NonceIV { + fn as_ref(&self) -> &[u8] { + match self { + NonceIV::Size128(fv) => fv.as_ref(), + } + } +} + +impl AsMut<[u8]> for NonceIV { + fn as_mut(&mut self) -> &mut [u8] { + match self { + NonceIV::Size128(fv) => fv.as_mut(), + } + } +} + +impl From<[u8; 16]> for NonceIV { + fn from(value: [u8; 16]) -> Self { + NonceIV::Size128(FixedLength::<16>(value)) + } +} + /// An initalization vector that must be unique for the lifetime of the associated key /// it is used with. -pub struct NonceIV([u8; L]); +pub struct FixedLength([u8; L]); -impl NonceIV { +impl FixedLength { /// Constructs a [`NonceIV`] with the given value, assuming that the value is /// unique for the lifetime of the key it is being used with. /// @@ -36,7 +85,7 @@ impl NonceIV { /// Returns the size of the nonce in bytes. #[allow(clippy::must_use_candidate)] - pub fn size() -> usize { + pub fn size(&self) -> usize { L } @@ -53,47 +102,47 @@ impl NonceIV { } } -impl AsMut<[u8; L]> for NonceIV { +impl AsMut<[u8; L]> for FixedLength { #[inline] fn as_mut(&mut self) -> &mut [u8; L] { &mut self.0 } } -impl AsRef<[u8; L]> for NonceIV { +impl AsRef<[u8; L]> for FixedLength { #[inline] fn as_ref(&self) -> &[u8; L] { &self.0 } } -impl From<&[u8; L]> for NonceIV { +impl From<&[u8; L]> for FixedLength { #[inline] fn from(bytes: &[u8; L]) -> Self { - NonceIV(bytes.to_owned()) + FixedLength(bytes.to_owned()) } } -impl From<[u8; L]> for NonceIV { +impl From<[u8; L]> for FixedLength { #[inline] fn from(bytes: [u8; L]) -> Self { - NonceIV(bytes) + FixedLength(bytes) } } -impl TryFrom<&[u8]> for NonceIV { +impl TryFrom<&[u8]> for FixedLength { type Error = Unspecified; fn try_from(value: &[u8]) -> Result { - NonceIV::::try_assume_unique_for_key(value) + FixedLength::::try_assume_unique_for_key(value) } } -impl TryFrom> for [u8; L] { +impl TryFrom> for [u8; L] { type Error = Unspecified; - fn try_from(value: NonceIV) -> Result { - let value: [u8; L] = value.0.try_into().map_err(|_|Unspecified)?; + fn try_from(value: FixedLength) -> Result { + let value: [u8; L] = value.0.try_into().map_err(|_| Unspecified)?; Ok(value) } } From 6758847737806ff1ebcba7dd2b9b5cfb4f93020c Mon Sep 17 00:00:00 2001 From: Sean McGrail Date: Thu, 18 May 2023 20:12:18 -0700 Subject: [PATCH 19/54] cargo fmt --- aws-lc-rs/src/aead.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/aws-lc-rs/src/aead.rs b/aws-lc-rs/src/aead.rs index 329c96a2137..9bf8df335d1 100644 --- a/aws-lc-rs/src/aead.rs +++ b/aws-lc-rs/src/aead.rs @@ -861,7 +861,11 @@ mod tests { let mut nonce: [u8; NONCE_LEN] = og_nonce.as_slice().try_into().unwrap(); nonce[0] = 0; assert!(less_safe_key - .open_in_place(Nonce(FixedLength::from(nonce)), Aad::empty(), &mut in_out_clone) + .open_in_place( + Nonce(FixedLength::from(nonce)), + Aad::empty(), + &mut in_out_clone + ) .is_err()); let nonce: [u8; NONCE_LEN] = og_nonce.as_slice().try_into().unwrap(); From a36cee9b2e6f0c15becc577061483c2993a7e1ef Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Mon, 22 May 2023 16:59:38 -0400 Subject: [PATCH 20/54] Implementation cleanup --- aws-lc-rs/src/cipher.rs | 206 +++++++++++++++++++--------------------- 1 file changed, 96 insertions(+), 110 deletions(-) diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index 0c9a05bec89..f5e1c80ba68 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -107,6 +107,35 @@ enum PaddingStrategy { PKCS7, } +impl OperatingMode { + fn add_padding(&self, block_len: usize, in_out: &mut InOut) -> Result<(), Unspecified> + where + InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, + { + match self { + OperatingMode::Block(strategy) => match strategy { + PaddingStrategy::PKCS7 => { + let in_out_len = in_out.as_mut().len(); + // This implements PKCS#7 padding scheme, used by aws-lc if we were using EVP_CIPHER API's + let remainder = in_out_len % block_len; + if remainder == 0 { + let block_size: u8 = block_len.try_into().map_err(|_| Unspecified)?; + in_out.extend(vec![block_size; block_len].iter()); + } else { + let padding_size = block_len - remainder; + let v: u8 = padding_size.try_into().map_err(|_| Unspecified)?; + // Heap allocation :( + in_out.extend(vec![v; padding_size].iter()); + } + } + PaddingStrategy::Unpadded => {} + }, + OperatingMode::Stream => {} + } + Ok(()) + } +} + #[derive(Clone, Copy)] enum OperatingMode { Block(PaddingStrategy), @@ -127,7 +156,7 @@ enum OperatingMode { /// * [`AES_128_CBC_PKCS7_PADDING`] /// * [`AES_256_CBC_PKCS7_PADDING`] /// -pub struct SymmetricCipher( +pub struct CipherConfig( OperatingMode, ); @@ -142,10 +171,10 @@ pub const AES_IV_LEN: usize = 16; const AES_BLOCK_LEN: usize = 16; pub enum Algorithm { - AES_128_CTR(SymmetricCipher), - AES_256_CTR(SymmetricCipher), - AES_128_CBC_PKCS7_PADDING(SymmetricCipher), - AES_256_CBC_PKCS7_PADDING(SymmetricCipher), + AES_128_CTR(CipherConfig), + AES_256_CTR(CipherConfig), + AES_128_CBC_PKCS7_PADDING(CipherConfig), + AES_256_CBC_PKCS7_PADDING(CipherConfig), } impl Algorithm { @@ -166,26 +195,65 @@ impl Algorithm { Algorithm::AES_256_CBC_PKCS7_PADDING(v) => v.get_block_size(), } } + + fn new_randomized_nonce(&self) -> Result { + match self { + _ => Ok(NonceIV::Size128(FixedLength::<16>::new()?)), + } + } + + fn new_symmetric_cipher_key( + &self, + key_bytes: &[u8], + ) -> Result { + match self { + Algorithm::AES_128_CTR(v) | Algorithm::AES_128_CBC_PKCS7_PADDING(v) => { + let key = v.try_into_key(key_bytes)?; + SymmetricCipherKey::aes128(key) + } + Algorithm::AES_256_CTR(v) | Algorithm::AES_256_CBC_PKCS7_PADDING(v) => { + let key = v.try_into_key(key_bytes)?; + SymmetricCipherKey::aes256(key) + } + } + } + + fn prepare_for_encrypt(&self, in_out: &mut InOut) -> Result<(), Unspecified> + where + InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, + { + self.get_operating_mode() + .add_padding(self.get_block_len(), in_out)?; + Ok(()) + } + + const fn max_block_size() -> usize { + 16 + } + + const fn max_iv_size() -> usize { + 16 + } } /// AES-128 Counter (CTR) Mode -pub const AES_128_CTR: Algorithm = Algorithm::AES_128_CTR(SymmetricCipher(OperatingMode::Stream)); +pub const AES_128_CTR: Algorithm = Algorithm::AES_128_CTR(CipherConfig(OperatingMode::Stream)); /// AES-128 Cipher block chaining (CBC) Mode using PKCS#7 padding. pub const AES_128_CBC_PKCS7_PADDING: Algorithm = Algorithm::AES_128_CBC_PKCS7_PADDING( - SymmetricCipher(OperatingMode::Block(PaddingStrategy::PKCS7)), + CipherConfig(OperatingMode::Block(PaddingStrategy::PKCS7)), ); /// AES-256 Counter (CTR) Mode -pub const AES_256_CTR: Algorithm = Algorithm::AES_256_CTR(SymmetricCipher(OperatingMode::Stream)); +pub const AES_256_CTR: Algorithm = Algorithm::AES_256_CTR(CipherConfig(OperatingMode::Stream)); /// AES-256 Cipher block chaining (CBC) Mode using PKCS#7 padding. pub const AES_256_CBC_PKCS7_PADDING: Algorithm = Algorithm::AES_256_CBC_PKCS7_PADDING( - SymmetricCipher(OperatingMode::Block(PaddingStrategy::PKCS7)), + CipherConfig(OperatingMode::Block(PaddingStrategy::PKCS7)), ); impl - SymmetricCipher + CipherConfig { #[inline] fn get_operating_mode(&self) -> &OperatingMode { @@ -204,17 +272,6 @@ impl fn new_block(&self) -> [u8; BLOCK_LEN] { [0u8; BLOCK_LEN] } - - fn new_randomized_nonce(&self) -> Result { - match IV_LEN { - 16 => Ok(NonceIV::Size128(FixedLength::<16>::new()?)), - _ => Err(Unspecified), - } - } - - fn unsafe_nonce_array(&self) -> [u8; IV_LEN] { - [0u8; IV_LEN] - } } /// A key bound to a particular cipher algorithm. @@ -232,16 +289,7 @@ impl UnboundCipherKey { /// length required by `algorithm`. /// pub fn new(algorithm: &'static Algorithm, key_bytes: &[u8]) -> Result { - let key = match algorithm { - Algorithm::AES_128_CTR(v) | Algorithm::AES_128_CBC_PKCS7_PADDING(v) => { - let key = v.try_into_key(key_bytes)?; - SymmetricCipherKey::aes128(key) - } - Algorithm::AES_256_CTR(v) | Algorithm::AES_256_CBC_PKCS7_PADDING(v) => { - let key = v.try_into_key(key_bytes)?; - SymmetricCipherKey::aes256(key) - } - }?; + let key = algorithm.new_symmetric_cipher_key(key_bytes)?; Ok(UnboundCipherKey { algorithm, key }) } @@ -265,12 +313,7 @@ impl EncryptingKey { /// * [`Unspecified`]: Returned if a randomized IV fails to be generated. /// pub fn new(key: UnboundCipherKey) -> Result { - let iv = match key.get_algorithm() { - Algorithm::AES_128_CTR(v) => v.new_randomized_nonce(), - Algorithm::AES_256_CTR(v) => v.new_randomized_nonce(), - Algorithm::AES_128_CBC_PKCS7_PADDING(v) => v.new_randomized_nonce(), - Algorithm::AES_256_CBC_PKCS7_PADDING(v) => v.new_randomized_nonce(), - }?; + let iv = key.get_algorithm().new_randomized_nonce()?; Ok(EncryptingKey { cipher_key: key, iv, @@ -341,16 +384,7 @@ pub struct LessSafeCipherKey { impl LessSafeCipherKey { pub fn new(algorithm: &'static Algorithm, key_bytes: &[u8]) -> Result { - let key = match algorithm { - Algorithm::AES_128_CTR(v) | Algorithm::AES_128_CBC_PKCS7_PADDING(v) => { - let key = v.try_into_key(key_bytes)?; - SymmetricCipherKey::aes128(key) - } - Algorithm::AES_256_CTR(v) | Algorithm::AES_256_CBC_PKCS7_PADDING(v) => { - let key = v.try_into_key(key_bytes)?; - SymmetricCipherKey::aes256(key) - } - }?; + let key = algorithm.new_symmetric_cipher_key(key_bytes)?; Ok(LessSafeCipherKey { algorithm, key }) } @@ -399,77 +433,29 @@ fn encrypt( where InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, { - let block_len: usize = algorithm.get_block_len(); - - match algorithm.get_operating_mode() { - OperatingMode::Block(strategy) => match strategy { - PaddingStrategy::PKCS7 => { - let in_out_len = in_out.as_mut().len(); - // This implements PKCS#7 padding scheme, used by aws-lc if we were using EVP_CIPHER API's - let remainder = in_out_len % block_len; - if remainder == 0 { - let block_size: u8 = block_len.try_into().map_err(|_| Unspecified)?; - in_out.extend(vec![block_size; block_len].iter()); - } else { - let padding_size = block_len - remainder; - let v: u8 = padding_size.try_into().map_err(|_| Unspecified)?; - // Heap allocation :( - in_out.extend(vec![v; padding_size].iter()); - } - } - PaddingStrategy::Unpadded => {} - }, - OperatingMode::Stream => {} - } + algorithm.prepare_for_encrypt(in_out)?; let in_out = in_out.as_mut(); - let mut ivec = { - let mut ivec = match algorithm { - Algorithm::AES_128_CTR(v) | Algorithm::AES_128_CBC_PKCS7_PADDING(v) => { - v.unsafe_nonce_array() - } - Algorithm::AES_256_CTR(v) | Algorithm::AES_256_CBC_PKCS7_PADDING(v) => { - v.unsafe_nonce_array() - } - }; - ivec.copy_from_slice(iv.as_ref()); - ivec - }; + let mut ivec = [0u8; Algorithm::max_iv_size()]; + ivec.copy_from_slice(iv.as_ref()); - match algorithm { - Algorithm::AES_128_CTR(v) => { - let key = match key { - SymmetricCipherKey::Aes128 { enc_key, .. } => enc_key, - _ => return Err(Unspecified), - }; + let mut buf = [0u8; Algorithm::max_block_size()]; - let mut buf = v.new_block(); - aes_ctr128_encrypt(key, &mut ivec, &mut buf, in_out); + // This works b/c we currently only support AES keys + let aes_key = match key { + SymmetricCipherKey::Aes128 { enc_key, .. } | SymmetricCipherKey::Aes256 { enc_key, .. } => { + Ok(enc_key) } - Algorithm::AES_256_CTR(v) => { - let key = match key { - SymmetricCipherKey::Aes256 { enc_key, .. } => enc_key, - _ => return Err(Unspecified), - }; - - let mut buf = v.new_block(); + SymmetricCipherKey::ChaCha20 { .. } => Err(Unspecified), + }?; - aes_ctr128_encrypt(key, &mut ivec, &mut buf, in_out); - } - Algorithm::AES_128_CBC_PKCS7_PADDING(_) => { - let key = match key { - SymmetricCipherKey::Aes128 { enc_key, .. } => enc_key, - _ => return Err(Unspecified), - }; - aes_cbc_encrypt(key, &mut ivec, in_out); + match algorithm { + Algorithm::AES_128_CTR(..) | Algorithm::AES_256_CTR(..) => { + aes_ctr128_encrypt(aes_key, &mut ivec, &mut buf, in_out); } - Algorithm::AES_256_CBC_PKCS7_PADDING(_) => { - let key = match key { - SymmetricCipherKey::Aes256 { enc_key, .. } => enc_key, - _ => return Err(Unspecified), - }; - aes_cbc_encrypt(key, &mut ivec, in_out); + Algorithm::AES_128_CBC_PKCS7_PADDING(..) | Algorithm::AES_256_CBC_PKCS7_PADDING(..) => { + aes_cbc_encrypt(aes_key, &mut ivec, in_out); } } Ok(iv) From 41507493676ee5cdbbc630ada84083aa3497877b Mon Sep 17 00:00:00 2001 From: Sean McGrail Date: Mon, 22 May 2023 16:19:12 -0700 Subject: [PATCH 21/54] API Cleanup --- aws-lc-rs/src/cipher.rs | 197 ++++++++++++++++++++++------------------ aws-lc-rs/src/iv.rs | 7 +- 2 files changed, 111 insertions(+), 93 deletions(-) diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index f5e1c80ba68..cebb95c62e0 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -108,7 +108,7 @@ enum PaddingStrategy { } impl OperatingMode { - fn add_padding(&self, block_len: usize, in_out: &mut InOut) -> Result<(), Unspecified> + fn add_padding(self, block_len: usize, in_out: &mut InOut) -> Result<(), Unspecified> where InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, { @@ -134,6 +134,36 @@ impl OperatingMode { } Ok(()) } + + fn remove_padding(self, block_len: usize, in_out: &mut [u8]) -> Result<&mut [u8], Unspecified> { + match self { + OperatingMode::Block(strategy) => match strategy { + PaddingStrategy::PKCS7 => { + let block_size: u8 = block_len.try_into().map_err(|_| Unspecified)?; + + if in_out.is_empty() || in_out.len() < block_len { + return Err(Unspecified); + } + + let padding: u8 = in_out[in_out.len() - 1]; + if padding == 0 || padding > block_size { + return Err(Unspecified); + } + + for item in in_out.iter().skip(in_out.len() - padding as usize) { + if *item != padding { + return Err(Unspecified); + } + } + + let final_len = in_out.len() - padding as usize; + Ok(&mut in_out[0..final_len]) + } + PaddingStrategy::Unpadded => Ok(in_out), + }, + OperatingMode::Stream => Ok(in_out), + } + } } #[derive(Clone, Copy)] @@ -142,6 +172,21 @@ enum OperatingMode { Stream, } +/// A cipher configuration description. +pub struct CipherConfig( + OperatingMode, +); + +/// The number of bytes in an AES 128-bit key +pub const AES_128_KEY_LEN: usize = 16; + +/// The number of bytes in an AES 256-bit key +pub const AES_256_KEY_LEN: usize = 32; + +/// The number of bytes for an AES initalization vector (IV) +pub const AES_IV_LEN: usize = 16; +const AES_BLOCK_LEN: usize = 16; + /// A Block or Stream Cipher Algorithm /// /// # Supported Algorithms @@ -155,51 +200,39 @@ enum OperatingMode { /// /// * [`AES_128_CBC_PKCS7_PADDING`] /// * [`AES_256_CBC_PKCS7_PADDING`] -/// -pub struct CipherConfig( - OperatingMode, -); - -/// The number of bytes in an AES 128-bit key -pub const AES_128_KEY_LEN: usize = 16; +///l +pub enum Algorithm { + /// AES-128 Counter (CTR) Mode + Aes128Ctr(CipherConfig), -/// The number of bytes in an AES 256-bit key -pub const AES_256_KEY_LEN: usize = 32; + /// AES-256 Counter (CTR) Mode + Aes256Ctr(CipherConfig), -/// The number of bytes for an AES initalization vector (IV) -pub const AES_IV_LEN: usize = 16; -const AES_BLOCK_LEN: usize = 16; + /// AES-128 Cipher block chaining (CBC) Mode + Aes128CbcPkcs7Padding(CipherConfig), -pub enum Algorithm { - AES_128_CTR(CipherConfig), - AES_256_CTR(CipherConfig), - AES_128_CBC_PKCS7_PADDING(CipherConfig), - AES_256_CBC_PKCS7_PADDING(CipherConfig), + /// AES-256 Cipher block chaining (CBC) Mode + Aes256CbcPkcs7Padding(CipherConfig), } impl Algorithm { fn get_operating_mode(&self) -> &OperatingMode { match self { - Algorithm::AES_128_CTR(v) => v.get_operating_mode(), - Algorithm::AES_256_CTR(v) => v.get_operating_mode(), - Algorithm::AES_128_CBC_PKCS7_PADDING(v) => v.get_operating_mode(), - Algorithm::AES_256_CBC_PKCS7_PADDING(v) => v.get_operating_mode(), + Algorithm::Aes128Ctr(v) | Algorithm::Aes128CbcPkcs7Padding(v) => v.get_operating_mode(), + Algorithm::Aes256Ctr(v) | Algorithm::Aes256CbcPkcs7Padding(v) => v.get_operating_mode(), } } fn get_block_len(&self) -> usize { match self { - Algorithm::AES_128_CTR(v) => v.get_block_size(), - Algorithm::AES_256_CTR(v) => v.get_block_size(), - Algorithm::AES_128_CBC_PKCS7_PADDING(v) => v.get_block_size(), - Algorithm::AES_256_CBC_PKCS7_PADDING(v) => v.get_block_size(), + Algorithm::Aes128Ctr(v) | Algorithm::Aes128CbcPkcs7Padding(v) => v.get_block_len(), + Algorithm::Aes256Ctr(v) | Algorithm::Aes256CbcPkcs7Padding(v) => v.get_block_len(), } } + #[allow(clippy::unused_self)] fn new_randomized_nonce(&self) -> Result { - match self { - _ => Ok(NonceIV::Size128(FixedLength::<16>::new()?)), - } + Ok(NonceIV::Size128(FixedLength::<16>::new()?)) } fn new_symmetric_cipher_key( @@ -207,11 +240,11 @@ impl Algorithm { key_bytes: &[u8], ) -> Result { match self { - Algorithm::AES_128_CTR(v) | Algorithm::AES_128_CBC_PKCS7_PADDING(v) => { + Algorithm::Aes128Ctr(v) | Algorithm::Aes128CbcPkcs7Padding(v) => { let key = v.try_into_key(key_bytes)?; SymmetricCipherKey::aes128(key) } - Algorithm::AES_256_CTR(v) | Algorithm::AES_256_CBC_PKCS7_PADDING(v) => { + Algorithm::Aes256Ctr(v) | Algorithm::Aes256CbcPkcs7Padding(v) => { let key = v.try_into_key(key_bytes)?; SymmetricCipherKey::aes256(key) } @@ -227,30 +260,37 @@ impl Algorithm { Ok(()) } - const fn max_block_size() -> usize { + fn finalalize_decryption<'a>(&self, in_out: &'a mut [u8]) -> Result<&'a mut [u8], Unspecified> { + let in_out = self + .get_operating_mode() + .remove_padding(self.get_block_len(), in_out.as_mut())?; + Ok(in_out) + } + + const fn max_block_len() -> usize { 16 } - const fn max_iv_size() -> usize { + const fn max_iv_len() -> usize { 16 } } /// AES-128 Counter (CTR) Mode -pub const AES_128_CTR: Algorithm = Algorithm::AES_128_CTR(CipherConfig(OperatingMode::Stream)); +pub const AES_128_CTR: Algorithm = Algorithm::Aes128Ctr(CipherConfig(OperatingMode::Stream)); /// AES-128 Cipher block chaining (CBC) Mode using PKCS#7 padding. -pub const AES_128_CBC_PKCS7_PADDING: Algorithm = Algorithm::AES_128_CBC_PKCS7_PADDING( - CipherConfig(OperatingMode::Block(PaddingStrategy::PKCS7)), -); +pub const AES_128_CBC_PKCS7_PADDING: Algorithm = + Algorithm::Aes128CbcPkcs7Padding(CipherConfig(OperatingMode::Block(PaddingStrategy::PKCS7))); /// AES-256 Counter (CTR) Mode -pub const AES_256_CTR: Algorithm = Algorithm::AES_256_CTR(CipherConfig(OperatingMode::Stream)); +pub const AES_256_CTR: Algorithm = Algorithm::Aes256Ctr(CipherConfig(OperatingMode::Stream)); /// AES-256 Cipher block chaining (CBC) Mode using PKCS#7 padding. -pub const AES_256_CBC_PKCS7_PADDING: Algorithm = Algorithm::AES_256_CBC_PKCS7_PADDING( - CipherConfig(OperatingMode::Block(PaddingStrategy::PKCS7)), -); +pub const AES_256_CBC_PKCS7_PADDING: Algorithm = + Algorithm::Aes256CbcPkcs7Padding(CipherConfig(OperatingMode::Block(PaddingStrategy::PKCS7))); + +const MAX_BLOCK_LEN: usize = 16; impl CipherConfig @@ -260,18 +300,16 @@ impl &self.0 } + #[allow(clippy::unused_self)] fn try_into_key<'a>(&self, key: &'a [u8]) -> Result<&'a [u8; KEY_LEN], Unspecified> { let key: &'a [u8; KEY_LEN] = key.try_into()?; Ok(key) } - fn get_block_size(&self) -> usize { + #[allow(clippy::unused_self)] + fn get_block_len(&self) -> usize { BLOCK_LEN } - - fn new_block(&self) -> [u8; BLOCK_LEN] { - [0u8; BLOCK_LEN] - } } /// A key bound to a particular cipher algorithm. @@ -383,6 +421,11 @@ pub struct LessSafeCipherKey { } impl LessSafeCipherKey { + /// Constructs a new [`LessSafeCipherKey`]. + /// + /// # Errors + /// * [`Unspecified`]: Returned if `key_bytes` is not the proper byte length for the selected algorithm. + /// pub fn new(algorithm: &'static Algorithm, key_bytes: &[u8]) -> Result { let key = algorithm.new_symmetric_cipher_key(key_bytes)?; Ok(LessSafeCipherKey { algorithm, key }) @@ -437,10 +480,10 @@ where let in_out = in_out.as_mut(); - let mut ivec = [0u8; Algorithm::max_iv_size()]; + let mut ivec = [0u8; Algorithm::max_iv_len()]; ivec.copy_from_slice(iv.as_ref()); - let mut buf = [0u8; Algorithm::max_block_size()]; + let mut buf = [0u8; Algorithm::max_block_len()]; // This works b/c we currently only support AES keys let aes_key = match key { @@ -451,10 +494,10 @@ where }?; match algorithm { - Algorithm::AES_128_CTR(..) | Algorithm::AES_256_CTR(..) => { + Algorithm::Aes128Ctr(..) | Algorithm::Aes256Ctr(..) => { aes_ctr128_encrypt(aes_key, &mut ivec, &mut buf, in_out); } - Algorithm::AES_128_CBC_PKCS7_PADDING(..) | Algorithm::AES_256_CBC_PKCS7_PADDING(..) => { + Algorithm::Aes128CbcPkcs7Padding(..) | Algorithm::Aes256CbcPkcs7Padding(..) => { aes_cbc_encrypt(aes_key, &mut ivec, in_out); } } @@ -511,38 +554,42 @@ fn decrypt<'a>( mut iv: NonceIV, in_out: &'a mut [u8], ) -> Result<&'a mut [u8], Unspecified> { - let mut final_len = in_out.len(); - let iv = iv.as_mut(); match algorithm { - Algorithm::AES_128_CTR(v) => { + Algorithm::Aes128Ctr(v) => { let key = match key { SymmetricCipherKey::Aes128 { enc_key, .. } => enc_key, _ => return Err(Unspecified), }; - let mut buf = v.new_block(); + let mut buf = [0u8; MAX_BLOCK_LEN]; + + assert!(buf.len() >= v.get_block_len()); + aes_ctr128_encrypt(key, iv, &mut buf, in_out); } - Algorithm::AES_256_CTR(v) => { + Algorithm::Aes256Ctr(v) => { let key = match key { SymmetricCipherKey::Aes256 { enc_key, .. } => enc_key, _ => return Err(Unspecified), }; - let mut buf = v.new_block(); + let mut buf = [0u8; MAX_BLOCK_LEN]; + + assert!(buf.len() >= v.get_block_len()); aes_ctr128_encrypt(key, iv, &mut buf, in_out); } - Algorithm::AES_128_CBC_PKCS7_PADDING(_) => { + Algorithm::Aes128CbcPkcs7Padding(_) => { let key = match key { SymmetricCipherKey::Aes128 { dec_key, .. } => dec_key, _ => return Err(Unspecified), }; + aes_cbc_decrypt(key, iv, in_out); } - Algorithm::AES_256_CBC_PKCS7_PADDING(_) => { + Algorithm::Aes256CbcPkcs7Padding(_) => { let key = match key { SymmetricCipherKey::Aes256 { dec_key, .. } => dec_key, _ => return Err(Unspecified), @@ -551,39 +598,9 @@ fn decrypt<'a>( } } - match algorithm.get_operating_mode() { - OperatingMode::Block(strategy) => match strategy { - PaddingStrategy::PKCS7 => { - let block_len = algorithm.get_block_len(); - - let block_size: u8 = algorithm - .get_block_len() - .try_into() - .map_err(|_| Unspecified)?; - - if in_out.is_empty() || in_out.len() < block_len { - return Err(Unspecified); - } - - let padding: u8 = in_out[in_out.len() - 1]; - if padding == 0 || padding > block_size { - return Err(Unspecified); - } - - for item in in_out.iter().skip(in_out.len() - padding as usize) { - if *item != padding { - return Err(Unspecified); - } - } - - final_len = in_out.len() - padding as usize; - } - PaddingStrategy::Unpadded => {} - }, - OperatingMode::Stream => {} - } + let in_out = algorithm.finalalize_decryption(in_out)?; - Ok(&mut in_out[0..final_len]) + Ok(in_out) } impl SymmetricCipherKey { diff --git a/aws-lc-rs/src/iv.rs b/aws-lc-rs/src/iv.rs index 7328ad410d9..c49c18d8eb4 100644 --- a/aws-lc-rs/src/iv.rs +++ b/aws-lc-rs/src/iv.rs @@ -9,8 +9,10 @@ use crate::error::Unspecified; use crate::{error, rand}; +/// A cryptographic nonce. pub enum NonceIV { - Size128(FixedLength<16>), + /// 128-bit (16 byte) nonce. + Size128(FixedLength<{ 128 / 8 }>), } impl NonceIV { @@ -142,7 +144,6 @@ impl TryFrom> for [u8; L] { type Error = Unspecified; fn try_from(value: FixedLength) -> Result { - let value: [u8; L] = value.0.try_into().map_err(|_| Unspecified)?; - Ok(value) + Ok(value.0) } } From c8009bd553d971e8de32fcd992a45c2f05a87ded Mon Sep 17 00:00:00 2001 From: Sean McGrail Date: Mon, 22 May 2023 17:36:26 -0700 Subject: [PATCH 22/54] Start of benchmarks --- aws-lc-rs/Cargo.toml | 7 +++- aws-lc-rs/benches/cipher_benchmark.rs | 42 +++++++++++++++++++ aws-lc-rs/benches/data/cipher_aes_128_ctr.txt | 14 +++++++ aws-lc-rs/src/iv.rs | 8 ++++ 4 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 aws-lc-rs/benches/cipher_benchmark.rs create mode 100644 aws-lc-rs/benches/data/cipher_aes_128_ctr.txt diff --git a/aws-lc-rs/Cargo.toml b/aws-lc-rs/Cargo.toml index f0fcbb8905b..a3c6a5818c9 100644 --- a/aws-lc-rs/Cargo.toml +++ b/aws-lc-rs/Cargo.toml @@ -50,7 +50,8 @@ criterion = { version = "0.5.0", features = ["csv_output"]} ring = "0.16" regex = "1.6.0" lazy_static = "1.4.0" -clap = {version = "4.1.8", features = ["derive"]} +clap = { version = "4.1.8", features = ["derive"] } +openssl = { version = "0.10.52", features = ["vendored"] } [[bench]] name = "aead_benchmark" @@ -91,3 +92,7 @@ harness = false [[bench]] name = "agreement_benchmark" harness = false + +[[bench]] +name = "cipher_benchmark" +harness = false diff --git a/aws-lc-rs/benches/cipher_benchmark.rs b/aws-lc-rs/benches/cipher_benchmark.rs new file mode 100644 index 00000000000..0acce29c61e --- /dev/null +++ b/aws-lc-rs/benches/cipher_benchmark.rs @@ -0,0 +1,42 @@ +use aws_lc_rs::{ + cipher::{LessSafeCipherKey, AES_128_CTR}, + iv::NonceIV, + test, test_file, +}; +use criterion::{criterion_group, criterion_main, Criterion}; + +fn test_aes_128_ctr(c: &mut Criterion) { + test::run( + test_file!("data/cipher_aes_128_ctr.txt"), + |_section, test_case| { + let key = test_case.consume_bytes("KEY"); + let iv = test_case.consume_bytes("IV"); + let data = test_case.consume_bytes("IN"); + + let mut group = c.benchmark_group(format!("AES-128-CTR-{}-bytes", data.len())); + + group.bench_function("AWS-LC", |b| { + b.iter(|| { + let key = LessSafeCipherKey::new(&AES_128_CTR, &key).unwrap(); + let iv: NonceIV = iv.as_slice().try_into().unwrap(); + let mut in_out = Vec::from(data.as_slice()); + let iv = key.encrypt(iv, &mut in_out).unwrap(); + let _ = key.decrypt(iv, &mut in_out).unwrap(); + }) + }); + + group.bench_function("openssl", |b| { + b.iter(|| { + use openssl::symm::{decrypt, encrypt, Cipher}; + let data = encrypt(Cipher::aes_128_ctr(), &key, Some(&iv), &data).unwrap(); + let _ = decrypt(Cipher::aes_128_ctr(), &key, Some(&iv), data.as_ref()).unwrap(); + }) + }); + + Ok(()) + }, + ); +} + +criterion_group!(benches, test_aes_128_ctr); +criterion_main!(benches); diff --git a/aws-lc-rs/benches/data/cipher_aes_128_ctr.txt b/aws-lc-rs/benches/data/cipher_aes_128_ctr.txt new file mode 100644 index 00000000000..f43a97b7d57 --- /dev/null +++ b/aws-lc-rs/benches/data/cipher_aes_128_ctr.txt @@ -0,0 +1,14 @@ +# 1 block +KEY = d1ae485dbe0d98ae4eff24ae075a5d28 +IV = a7cbf70ede88eb876887c9e7ed60a108 +IN = 6d00bced19251c9d4c9a8ee7ead11881 + +# 16 blocks +KEY = 78f094594b7ee4194bc2baed0562856e +IV = 5591c315865d3b312292be1c8b3babdf +IN = 98b4eae2434eb203583d837f90b17d92c5aaf51cb6d160566a691d1e254067e1a038416723ab31036775e560d6c9d692a1d083dc66b348f1a47fd69c5a890044345d9c0acad8db746280b42fdd17cd07a2c7df684d979d06eb41df6b5dabf1ff6d64ad54462966dfbb1dc8d3d19085b9f9b85f2892ddce92f0b4da4fd9d97d60d9ef171f27a895bc2e00aaa7532a1230536998f246e005688e698eb7edecf05bfda05b93ac63f7eabff0296d9442a0a0c3942985b86e2b5f3a38df65be6fc8ee690ecadab6acc3b75bc4580f54101bcedf6c131081faab8c3e8a322e252260dada51e63dfe470d0d0199d2e9c2f50a77a48d382e3986c26d0db8915ef2e25e28 + +# 256 blocks +KEY = 4da3482c72e5eec455e919054118103a +IV = a4a809574a1a9575b4256cc704a53e46 +IN = a606b6054c433bf7b052aa96b5438ced6675050987c7f9036bb9bc261e5c422c05b155c85bf319f31cef15370f8adb6b574484bfa61cbc203b2875c9d86fae358ac3a59db1230f0e614d68df10ad53077da9b609996f7fdcca9ccc3ca32c1474e563cbb6c97d8113b5f6591f72ac42ac9cdf76d4660a29b5d9b2c8157bd83ce02eb895a8b300a61f886c5d4ca7d07a7166cbd0b9d2ad818e0b1e6ae86ce17b15b67a468446268bf23f61f4fab478d1dee9443ba82b981b5f133561a3019da21b86d848530e6bfaa874d8866cab995ab7a944acac71bb1b7e398dd94a7d69f25f692a2824dcb2354e846d561d4a13d89cd69cd148e4e14d4169da0334b17664b7910f3ad5ab71ba45549fcfdad84da05795b15d6256c99110a5585584230b343ed08990a8b3033da1a07bc47de455548456b4b95f78761482811cbdf39ea35231eebf1f0792667302a269ac534d4a52b1b1927c245b566c9181002db0f6eaf22293196713e514311a658dc616e2ec4f9d8790368e627cf0b741be2a346700ae5456496464162e496ebbc19801059c9c53da709cc74687ecd5fa3d3ef740642f8740a630ff65f9b7b391c6def821e0f01e3c70503846e15e1d53204978a7d85a4eb8c3b654e3d3a7719b8661396235a3455a52de6f3e29c3333dd430eb166ad835d26cfa3be41bd3c3cafdd2bf1956447bbec205cf254f906569fd8bcf5ebaa3d3445f21938b3314e132fbbbb796a3e9abd393277650d5061833e12d6f009baf6543e0bab99be09b39975caff634c5988263e95e1861231b54228d93b3709ea17e29f0614d77da6578b9807b0e8f603f81417ddfe64936b5a337bf8e6055658d58a8680d07d9439fd453274d58dcd4cf6a5eb6a21e96dbdec9d534ee803f3ad830e58d18836ea51964098acd9af4bade9c473387288ce7ef9a73b843e272904e1bd1629e941be8590bb0c254202f93035a9d1708214a5fa0c66cce0f7db2084f63c06de42aab893a587af9b232d2dbdbbc1c85e7005845ad6de9b04ec002743392a4a6282132da9209bd5df5adf4358ac914f83186a58386629c6b1e3032ce2be4454ba7cbff15bc340fd14a51da3b54cd35ea1593b2cabd32004f45f5ae4ec6c7ed79d3d9c512d7299ccf4466958bf379211dffb90b2d8f3737dcf358f659880184707cacbff2184c39134d874fc85c21aabe6fab711f18170b0e5fd38838e19c64b77a6709612f4cd8ada37d31b5ec01347e3b7f777d0a4eb0b7f70094f6856b5bee20118cbd982d6fbf90831b73a4eeabade1243cc52a440f735daf53a6eb12d354faba5938e7e41c6c9d79bbb63d44e1b766659cf7f20d9b2b45fe66ca8fc294f191a0e5ecf7c189afab57687bc599534d5bdcc001c145d0a852e0954d5318e76350dea1afc84a274faad3eb24cafb70e5073fe214a83c8251be1a9e106d86149f569a9ef9aa1f2d473bcb06072a6cb994f400175c788b431f44f7e70f0d7563a0850111f9835b3553d138436e60e2f45a1d147bc6c111447faa79001b05b70f11919ddb1a5681a74c8e08aa892d5a1af635305e18d67e116d26662ca92072be5b9a85cd9da0b97d47f2019b12daa6a5b03f18569857a42e139a5c59cb8b876b0a6965383b4b067d2eb9d3d382b5f96c05d1747eeb39c42815f028698bb94b45fbf501104bb5127ac9213c505460e8598a099bdf2ba9f6c7afca91d34125d642a1fc9746b685ea3adcb7ad49b9ceafda3cef3f0254a28c4000611780946c5de2255af12da37e983c2b41b92ddffd59de694aa79ddc18f49f56ec43bc138c21095d9b6cd82607679a386902957c240c8e4d9196030c2798685bc78b4abbcb823eaeed99995b009bd45c2d667c7652f0d65cf781a1ae5d7348412436f78410fd0b6caf9a80caeb3e9ca2a11345399b37111cd0c579cd24ddf86652634ad6cb1b2fda3e1a194f695e2d1c3247cee2b8a88e6d373d3ac6d29b594306b749ae74b0507e4c70e29536e0a6dc5b5e85c25936117b3e6d0eee31c6685b2e45d152f74de49f82820338653dd2bd43224bfe5f0c94247a1c8e13a03bc0b9eaec114789ca4e4939fd267d261377cafe12e5adadb5df05be0555c11a78b38e454bcbf5dfe263aade057224c0aa7632a4a4ca17f9c97d983d261204a798dcf651d60e0b93f3b4337aa06c793c812fa088ebc9b07df4ca79a3105a33d3b3706c468f258c2a6b7b4a2f4fa1d589eb795c4a0b325b963cbc99eb14a23b5657f9717a6608631bbff1b94337d0c66fe7cdb391476d7c14180b538d83df38086640ff48b1e3f4c7048cef1970df7c43af5c9d586d0cdef4cf51f96aa4539b7d9608261863949e57781b6148b4892a1f42e0169e1486428a8ef5bdb0bf1a00e27c79ae2740e601c54bf5e7c9c96f82a7df1f437ea1d423170617c02751959cb25bbe310ef28ee26718580f85a454e2fb35215b3c52422f69109549c49aa3e4b4e126f2a8a38cc79c4aee2e900cc5073d1a590ada05cdd4a84c6c56247fedb47efe1b8aa33bce4e616623d7e1b250c242e6cd763250b1623db116fce701f46ddb7c9d63bf3ddb173961ae354194c3d2674a228ae977f7de78f7517e0e5612272b6dfc627dfa01f7cc0f4d6c3b5d591bb3c80f77eb23e9d2385666efe0c7fc98ce3fa02c24bc6209bd6db3ba5976d443ff106a41827b156a37a7afc62c916972a410a0ad7cb33188e7db331ec7fb51bdc1fb5537aedc0d1476e1d71adf060439f57a1b83a5af191b0e12ca533b0348e997dce20798fdd2d01c5424c7d606b435c8141578210447d6dec63b06739722a138f0fd2bcdf6cc455b175b07fe0e96c7003662ad71b047884205ce1a83e9a96b9acfa2d837a1b17b4ad865b778646517a33a9515b09b7659f932d7cc660eb5bc39805ed8b1831d72365d772f7875259da722a18274010483df24868c8347cc1dcc67d25fa148a355c0bcdeac84f5aad0af892b24df2928059508c98003ed734ab6eeaeac67060e992c2c1f991247439b514ca3ebe038a4adf04d5d6514e17bbd9ae58c160eaa2a8606d1f9e4dc6e03f48c2c94230f602cfb2c5b285d5ac0d00bdd8392302cfce89fca520e2c91f6b870028a2850b8da2ef86c1088a61a3780bc0f3eca4b28bf84842e455c320b256c26c2b89a03cb9e73821a1014008a175793d35f60641c972352bd3851ec88246960a18672a68dc062d97009f3d34e194c32c124b22e966b63d199e102508b6e23e9d7b0ea38e5562a4198688162219bd70a1b4eb2f77077e6d1b5cc4556a7b9825cd4528e9f96fb7fc65b247baabe903c9de016642f5cd71de47f9c605e985973a3bb0699f54406e66606836d9d7c8b707c8964b59a0f963e0d5f6cc23c05f52b49118d8fcb4e488381104435583ce71583b285c02e4ceff41c81a7b9639d4d0d40dc1f4fca3ff58406426a1aa9a026bd258b6e0f103f1a0850d289b3126afaf17cdaaf5ace15c735363155f4b1c869d6421d015ad499bc9081891686542fb9ddb6f58c3c69e23ace91d75685e93161bf80ff3cb4f3dc946d94b4cece9ececbc9a20975543c7f358346a028d774c811d72ae68da1fad92e6cc6aa497c6365f2003f8e4754faafe1a7b48e626e5613ffe880c404ad0a8104186ccc5c4392f94316f437db2d0bfe4c68394ce764377e810c8a3c9b938c8f4b7727fd1d067f3b38490663ca3183c453cbe760cb0a92d236dd153f51f45b2193437b3340f8a6a44ff30dfbb7e3a57dec7cd80b3288797147e95bfb821c84a7401595f0521c20337a9e9e31714b0b4b7c4708604db81ae0b5755b6c04491d81023ac7f6a32b8f92e46cc339922f381dd6e475d0a43c03707fd2b5796e272d2977569e1ecf43fdc98a41dcd017e10abd0b9ce75ca0fcf3d01b777c765dc6763cfee650802e1c50f04f9995b0b742100fcb6bb955e27974607877207c48df18ae3dc424900c30ac2ac84c7c89594211a3117d53f68c39f7456eb51cace912c7c592580e087bffd86d6c80e89d9d605bbbc29f4197b46136d04ca4d78415b308ec3800867b6abff0bc610a75295c3833e6eb6cd7de5b40e21d083228a9eb0293809061ab63810d035904f75575bcc3c229e37c6fbdde651f3a77bb99998ac6234cfdfd04b6e0e90082387798fff8497de30e66183cd545ac1964857ee1bd277d8594fec991ca9429abc0393c307187dd41e3eb9bffce0b6882c85de6d68b2638b1061c534effd25f7bc702ba2e109fa78ff5e45e308e2e45df1b9846fe3694ee7e2a7c1cb82b9d2b0f0b2e86c69acad5b98d30df79444445eb1143605110219ebf6f203626632d426b1583f83101ba1abcabe233b5409c1b48f20e6453a66a6cf15361679ae99aac1971559ecf58896f5c950ac6b7482784872665d3ce563c89dec404332dc32c19eb69093d556341d7ec2beac4a70ee6a07023b0e8ca7dd945199d46acf05c476b78fe4883b9a193294b3dbf5b0b8d143d7a7682d016ad4e6741eef91de74565c2c57030d86c598b271041cdb8bb5125cf43f9d64f83774f3b4032c0accf6c2787a4599011d4b9ba848b50fb47da918f6800de5055b3a566fb858dbd2f9d6db758d801ce2494ce22ec824e74300b030014ba3c54f0b8544d737e7761f9c2aced8114e5a92827928e9c000dd4353b92fc61f32d172b9133a5c3386adb3030c159b252b01163b2093830f7487d095baf7aacb2f135aee40e04d866d07c97ff273af7251cb6e8aedeca0f5e06f9776464ff8a0bf95aa0033c597f3cb4144ebd2b6dc5b1114e544cd22d445b4cb504fdfd7a392e5c300eb9c463c027f5e48e8e81dd819ada10ce2aa1c3c37c70ad5970289c3204274bf8762611041bf9c5b6864bbf5fc8d31b0772626bfe1b1f5f634bdaf36f1652d5687a69b21e4010b3e5d82f31c63e07904b104b87809d7e2f52f58c3008ae8fa98fae65e8e7609671e89a3e1bb4a0eab52b5e541863bc2fa850b855f4ecc7e08e5a9293bfb48ba6f03f5042816234b304c6f2af3df3029123f44ea52e99d2b59f3211a16096aa03cd8e7fcdc277665f32cac3f023a1468cd2aea4e8863c63dc0af27b3a42a071f4e16970ee06c1fc287df2401e0f2d719ab8e217efeb114c2c0bf18c41933f898245395e77961815ed05e9366d20b2083a5b681ff823609b1b67d1cdcb9ab11b5e80b971ade516186b4d1fe4e4528a35d790e96d857bf78658eda334140ed19f5599b474dbc7f3934bf5459dbdfff6b201a7cff6ddef2d621b30e6e5ba532a1dd3b8b68540f94b32e9ac7b40ef06804147e31e2d7c5c3344a2fcf9ccd4726d4484f05966a4ddbc6ee717d9d71b1f33afc7afa5fdf2234297c4b8db3e9f4de2a884bf91a534066f4ad988d6c498a09abd95b5ffbafe5119953878bc6611f56fa26662802ee6dcd759a17bd9b0c4391ff7ad8eccf7bfba5a97450d179df34116b5c7c85a6bbd6eb61b596227c968e3118640954e17fb01455bfee4051d4b6ee2c01c3f5fe4a4acf965c94f5aa9a699349f4ebec88ebfe1deeb8bbc512f2d11609c52eede49ca6313b192110230d0b58624d1d05a28248bd29acedf348c67a3c686892a1b2f167731e164eebad6f3a3216af54c201daca26a6027b0e7d3c05bf00f26091b451e17489f1c05b475158a14071d0103c692f8fb6c4621077fbe591da55f648a35fa359e993499fdb135c466e6994ab954fc773186de49b20bef7a1b6cb8954d49e22ba1d27103677faca2f9f8b19b92dd4169b564ddb7ce90fc66acc113418ff84d36382f3fe01a92fe54c54093d0386b67 diff --git a/aws-lc-rs/src/iv.rs b/aws-lc-rs/src/iv.rs index c49c18d8eb4..b7a343cbb40 100644 --- a/aws-lc-rs/src/iv.rs +++ b/aws-lc-rs/src/iv.rs @@ -60,6 +60,14 @@ impl From<[u8; 16]> for NonceIV { } } +impl TryFrom<&[u8]> for NonceIV { + type Error = Unspecified; + + fn try_from(value: &[u8]) -> Result { + Ok(NonceIV::Size128(FixedLength::<16>::try_from(value)?)) + } +} + /// An initalization vector that must be unique for the lifetime of the associated key /// it is used with. pub struct FixedLength([u8; L]); From 1f253ad40318846d53d40067a6e412c488e67855 Mon Sep 17 00:00:00 2001 From: Sean McGrail Date: Tue, 23 May 2023 08:59:14 -0700 Subject: [PATCH 23/54] More benchmarks --- aws-lc-rs/benches/cipher_benchmark.rs | 105 ++++++++++++------ aws-lc-rs/benches/data/cipher_aes_128_cbc.txt | 14 +++ aws-lc-rs/benches/data/cipher_aes_256_cbc.txt | 14 +++ aws-lc-rs/benches/data/cipher_aes_256_ctr.txt | 14 +++ 4 files changed, 116 insertions(+), 31 deletions(-) create mode 100644 aws-lc-rs/benches/data/cipher_aes_128_cbc.txt create mode 100644 aws-lc-rs/benches/data/cipher_aes_256_cbc.txt create mode 100644 aws-lc-rs/benches/data/cipher_aes_256_ctr.txt diff --git a/aws-lc-rs/benches/cipher_benchmark.rs b/aws-lc-rs/benches/cipher_benchmark.rs index 0acce29c61e..070423e11b6 100644 --- a/aws-lc-rs/benches/cipher_benchmark.rs +++ b/aws-lc-rs/benches/cipher_benchmark.rs @@ -1,42 +1,85 @@ use aws_lc_rs::{ - cipher::{LessSafeCipherKey, AES_128_CTR}, + cipher::{ + LessSafeCipherKey, AES_128_CBC_PKCS7_PADDING, AES_128_CTR, AES_256_CBC_PKCS7_PADDING, + AES_256_CTR, + }, iv::NonceIV, test, test_file, }; use criterion::{criterion_group, criterion_main, Criterion}; +use openssl::symm::Cipher; -fn test_aes_128_ctr(c: &mut Criterion) { - test::run( - test_file!("data/cipher_aes_128_ctr.txt"), - |_section, test_case| { - let key = test_case.consume_bytes("KEY"); - let iv = test_case.consume_bytes("IV"); - let data = test_case.consume_bytes("IN"); - - let mut group = c.benchmark_group(format!("AES-128-CTR-{}-bytes", data.len())); - - group.bench_function("AWS-LC", |b| { - b.iter(|| { - let key = LessSafeCipherKey::new(&AES_128_CTR, &key).unwrap(); - let iv: NonceIV = iv.as_slice().try_into().unwrap(); - let mut in_out = Vec::from(data.as_slice()); - let iv = key.encrypt(iv, &mut in_out).unwrap(); - let _ = key.decrypt(iv, &mut in_out).unwrap(); - }) - }); +macro_rules! benchmark { + ($fn:ident, $test:literal, $file:literal, $awslc:expr, $openssl:expr) => { + fn $fn(c: &mut Criterion) { + test::run(test_file!($file), |_section, test_case| { + let key = test_case.consume_bytes("KEY"); + let iv = test_case.consume_bytes("IV"); + let data = test_case.consume_bytes("IN"); - group.bench_function("openssl", |b| { - b.iter(|| { - use openssl::symm::{decrypt, encrypt, Cipher}; - let data = encrypt(Cipher::aes_128_ctr(), &key, Some(&iv), &data).unwrap(); - let _ = decrypt(Cipher::aes_128_ctr(), &key, Some(&iv), data.as_ref()).unwrap(); - }) - }); + let mut group = c.benchmark_group(format!("{}-{}-bytes", $test, data.len())); + + group.bench_function("AWS-LC", |b| { + b.iter(|| { + let key = LessSafeCipherKey::new($awslc, &key).unwrap(); + let iv: NonceIV = iv.as_slice().try_into().unwrap(); + let mut in_out = Vec::from(data.as_slice()); + let iv = key.encrypt(iv, &mut in_out).unwrap(); + let _ = key.decrypt(iv, &mut in_out).unwrap(); + }) + }); - Ok(()) - }, - ); + group.bench_function("OpenSSL", |b| { + b.iter(|| { + use openssl::symm::{decrypt, encrypt}; + let data = encrypt($openssl, &key, Some(&iv), &data).unwrap(); + let _ = decrypt($openssl, &key, Some(&iv), data.as_ref()).unwrap(); + }) + }); + + Ok(()) + }); + } + }; } -criterion_group!(benches, test_aes_128_ctr); +benchmark!( + test_aes_128_ctr, + "AES-128-CTR", + "data/cipher_aes_128_ctr.txt", + &AES_128_CTR, + Cipher::aes_128_ctr() +); + +benchmark!( + test_aes_256_ctr, + "AES-256-CTR", + "data/cipher_aes_256_ctr.txt", + &AES_256_CTR, + Cipher::aes_256_ctr() +); + +benchmark!( + test_aes_128_cbc, + "AES-128-CBC", + "data/cipher_aes_128_cbc.txt", + &AES_128_CBC_PKCS7_PADDING, + Cipher::aes_128_cbc() +); + +benchmark!( + test_aes_256_cbc, + "AES-256-CBC", + "data/cipher_aes_256_cbc.txt", + &AES_256_CBC_PKCS7_PADDING, + Cipher::aes_256_cbc() +); + +criterion_group!( + benches, + test_aes_128_ctr, + test_aes_128_cbc, + test_aes_256_ctr, + test_aes_256_cbc +); criterion_main!(benches); diff --git a/aws-lc-rs/benches/data/cipher_aes_128_cbc.txt b/aws-lc-rs/benches/data/cipher_aes_128_cbc.txt new file mode 100644 index 00000000000..f43a97b7d57 --- /dev/null +++ b/aws-lc-rs/benches/data/cipher_aes_128_cbc.txt @@ -0,0 +1,14 @@ +# 1 block +KEY = d1ae485dbe0d98ae4eff24ae075a5d28 +IV = a7cbf70ede88eb876887c9e7ed60a108 +IN = 6d00bced19251c9d4c9a8ee7ead11881 + +# 16 blocks +KEY = 78f094594b7ee4194bc2baed0562856e +IV = 5591c315865d3b312292be1c8b3babdf +IN = 98b4eae2434eb203583d837f90b17d92c5aaf51cb6d160566a691d1e254067e1a038416723ab31036775e560d6c9d692a1d083dc66b348f1a47fd69c5a890044345d9c0acad8db746280b42fdd17cd07a2c7df684d979d06eb41df6b5dabf1ff6d64ad54462966dfbb1dc8d3d19085b9f9b85f2892ddce92f0b4da4fd9d97d60d9ef171f27a895bc2e00aaa7532a1230536998f246e005688e698eb7edecf05bfda05b93ac63f7eabff0296d9442a0a0c3942985b86e2b5f3a38df65be6fc8ee690ecadab6acc3b75bc4580f54101bcedf6c131081faab8c3e8a322e252260dada51e63dfe470d0d0199d2e9c2f50a77a48d382e3986c26d0db8915ef2e25e28 + +# 256 blocks +KEY = 4da3482c72e5eec455e919054118103a +IV = a4a809574a1a9575b4256cc704a53e46 +IN = a606b6054c433bf7b052aa96b5438ced6675050987c7f9036bb9bc261e5c422c05b155c85bf319f31cef15370f8adb6b574484bfa61cbc203b2875c9d86fae358ac3a59db1230f0e614d68df10ad53077da9b609996f7fdcca9ccc3ca32c1474e563cbb6c97d8113b5f6591f72ac42ac9cdf76d4660a29b5d9b2c8157bd83ce02eb895a8b300a61f886c5d4ca7d07a7166cbd0b9d2ad818e0b1e6ae86ce17b15b67a468446268bf23f61f4fab478d1dee9443ba82b981b5f133561a3019da21b86d848530e6bfaa874d8866cab995ab7a944acac71bb1b7e398dd94a7d69f25f692a2824dcb2354e846d561d4a13d89cd69cd148e4e14d4169da0334b17664b7910f3ad5ab71ba45549fcfdad84da05795b15d6256c99110a5585584230b343ed08990a8b3033da1a07bc47de455548456b4b95f78761482811cbdf39ea35231eebf1f0792667302a269ac534d4a52b1b1927c245b566c9181002db0f6eaf22293196713e514311a658dc616e2ec4f9d8790368e627cf0b741be2a346700ae5456496464162e496ebbc19801059c9c53da709cc74687ecd5fa3d3ef740642f8740a630ff65f9b7b391c6def821e0f01e3c70503846e15e1d53204978a7d85a4eb8c3b654e3d3a7719b8661396235a3455a52de6f3e29c3333dd430eb166ad835d26cfa3be41bd3c3cafdd2bf1956447bbec205cf254f906569fd8bcf5ebaa3d3445f21938b3314e132fbbbb796a3e9abd393277650d5061833e12d6f009baf6543e0bab99be09b39975caff634c5988263e95e1861231b54228d93b3709ea17e29f0614d77da6578b9807b0e8f603f81417ddfe64936b5a337bf8e6055658d58a8680d07d9439fd453274d58dcd4cf6a5eb6a21e96dbdec9d534ee803f3ad830e58d18836ea51964098acd9af4bade9c473387288ce7ef9a73b843e272904e1bd1629e941be8590bb0c254202f93035a9d1708214a5fa0c66cce0f7db2084f63c06de42aab893a587af9b232d2dbdbbc1c85e7005845ad6de9b04ec002743392a4a6282132da9209bd5df5adf4358ac914f83186a58386629c6b1e3032ce2be4454ba7cbff15bc340fd14a51da3b54cd35ea1593b2cabd32004f45f5ae4ec6c7ed79d3d9c512d7299ccf4466958bf379211dffb90b2d8f3737dcf358f659880184707cacbff2184c39134d874fc85c21aabe6fab711f18170b0e5fd38838e19c64b77a6709612f4cd8ada37d31b5ec01347e3b7f777d0a4eb0b7f70094f6856b5bee20118cbd982d6fbf90831b73a4eeabade1243cc52a440f735daf53a6eb12d354faba5938e7e41c6c9d79bbb63d44e1b766659cf7f20d9b2b45fe66ca8fc294f191a0e5ecf7c189afab57687bc599534d5bdcc001c145d0a852e0954d5318e76350dea1afc84a274faad3eb24cafb70e5073fe214a83c8251be1a9e106d86149f569a9ef9aa1f2d473bcb06072a6cb994f400175c788b431f44f7e70f0d7563a0850111f9835b3553d138436e60e2f45a1d147bc6c111447faa79001b05b70f11919ddb1a5681a74c8e08aa892d5a1af635305e18d67e116d26662ca92072be5b9a85cd9da0b97d47f2019b12daa6a5b03f18569857a42e139a5c59cb8b876b0a6965383b4b067d2eb9d3d382b5f96c05d1747eeb39c42815f028698bb94b45fbf501104bb5127ac9213c505460e8598a099bdf2ba9f6c7afca91d34125d642a1fc9746b685ea3adcb7ad49b9ceafda3cef3f0254a28c4000611780946c5de2255af12da37e983c2b41b92ddffd59de694aa79ddc18f49f56ec43bc138c21095d9b6cd82607679a386902957c240c8e4d9196030c2798685bc78b4abbcb823eaeed99995b009bd45c2d667c7652f0d65cf781a1ae5d7348412436f78410fd0b6caf9a80caeb3e9ca2a11345399b37111cd0c579cd24ddf86652634ad6cb1b2fda3e1a194f695e2d1c3247cee2b8a88e6d373d3ac6d29b594306b749ae74b0507e4c70e29536e0a6dc5b5e85c25936117b3e6d0eee31c6685b2e45d152f74de49f82820338653dd2bd43224bfe5f0c94247a1c8e13a03bc0b9eaec114789ca4e4939fd267d261377cafe12e5adadb5df05be0555c11a78b38e454bcbf5dfe263aade057224c0aa7632a4a4ca17f9c97d983d261204a798dcf651d60e0b93f3b4337aa06c793c812fa088ebc9b07df4ca79a3105a33d3b3706c468f258c2a6b7b4a2f4fa1d589eb795c4a0b325b963cbc99eb14a23b5657f9717a6608631bbff1b94337d0c66fe7cdb391476d7c14180b538d83df38086640ff48b1e3f4c7048cef1970df7c43af5c9d586d0cdef4cf51f96aa4539b7d9608261863949e57781b6148b4892a1f42e0169e1486428a8ef5bdb0bf1a00e27c79ae2740e601c54bf5e7c9c96f82a7df1f437ea1d423170617c02751959cb25bbe310ef28ee26718580f85a454e2fb35215b3c52422f69109549c49aa3e4b4e126f2a8a38cc79c4aee2e900cc5073d1a590ada05cdd4a84c6c56247fedb47efe1b8aa33bce4e616623d7e1b250c242e6cd763250b1623db116fce701f46ddb7c9d63bf3ddb173961ae354194c3d2674a228ae977f7de78f7517e0e5612272b6dfc627dfa01f7cc0f4d6c3b5d591bb3c80f77eb23e9d2385666efe0c7fc98ce3fa02c24bc6209bd6db3ba5976d443ff106a41827b156a37a7afc62c916972a410a0ad7cb33188e7db331ec7fb51bdc1fb5537aedc0d1476e1d71adf060439f57a1b83a5af191b0e12ca533b0348e997dce20798fdd2d01c5424c7d606b435c8141578210447d6dec63b06739722a138f0fd2bcdf6cc455b175b07fe0e96c7003662ad71b047884205ce1a83e9a96b9acfa2d837a1b17b4ad865b778646517a33a9515b09b7659f932d7cc660eb5bc39805ed8b1831d72365d772f7875259da722a18274010483df24868c8347cc1dcc67d25fa148a355c0bcdeac84f5aad0af892b24df2928059508c98003ed734ab6eeaeac67060e992c2c1f991247439b514ca3ebe038a4adf04d5d6514e17bbd9ae58c160eaa2a8606d1f9e4dc6e03f48c2c94230f602cfb2c5b285d5ac0d00bdd8392302cfce89fca520e2c91f6b870028a2850b8da2ef86c1088a61a3780bc0f3eca4b28bf84842e455c320b256c26c2b89a03cb9e73821a1014008a175793d35f60641c972352bd3851ec88246960a18672a68dc062d97009f3d34e194c32c124b22e966b63d199e102508b6e23e9d7b0ea38e5562a4198688162219bd70a1b4eb2f77077e6d1b5cc4556a7b9825cd4528e9f96fb7fc65b247baabe903c9de016642f5cd71de47f9c605e985973a3bb0699f54406e66606836d9d7c8b707c8964b59a0f963e0d5f6cc23c05f52b49118d8fcb4e488381104435583ce71583b285c02e4ceff41c81a7b9639d4d0d40dc1f4fca3ff58406426a1aa9a026bd258b6e0f103f1a0850d289b3126afaf17cdaaf5ace15c735363155f4b1c869d6421d015ad499bc9081891686542fb9ddb6f58c3c69e23ace91d75685e93161bf80ff3cb4f3dc946d94b4cece9ececbc9a20975543c7f358346a028d774c811d72ae68da1fad92e6cc6aa497c6365f2003f8e4754faafe1a7b48e626e5613ffe880c404ad0a8104186ccc5c4392f94316f437db2d0bfe4c68394ce764377e810c8a3c9b938c8f4b7727fd1d067f3b38490663ca3183c453cbe760cb0a92d236dd153f51f45b2193437b3340f8a6a44ff30dfbb7e3a57dec7cd80b3288797147e95bfb821c84a7401595f0521c20337a9e9e31714b0b4b7c4708604db81ae0b5755b6c04491d81023ac7f6a32b8f92e46cc339922f381dd6e475d0a43c03707fd2b5796e272d2977569e1ecf43fdc98a41dcd017e10abd0b9ce75ca0fcf3d01b777c765dc6763cfee650802e1c50f04f9995b0b742100fcb6bb955e27974607877207c48df18ae3dc424900c30ac2ac84c7c89594211a3117d53f68c39f7456eb51cace912c7c592580e087bffd86d6c80e89d9d605bbbc29f4197b46136d04ca4d78415b308ec3800867b6abff0bc610a75295c3833e6eb6cd7de5b40e21d083228a9eb0293809061ab63810d035904f75575bcc3c229e37c6fbdde651f3a77bb99998ac6234cfdfd04b6e0e90082387798fff8497de30e66183cd545ac1964857ee1bd277d8594fec991ca9429abc0393c307187dd41e3eb9bffce0b6882c85de6d68b2638b1061c534effd25f7bc702ba2e109fa78ff5e45e308e2e45df1b9846fe3694ee7e2a7c1cb82b9d2b0f0b2e86c69acad5b98d30df79444445eb1143605110219ebf6f203626632d426b1583f83101ba1abcabe233b5409c1b48f20e6453a66a6cf15361679ae99aac1971559ecf58896f5c950ac6b7482784872665d3ce563c89dec404332dc32c19eb69093d556341d7ec2beac4a70ee6a07023b0e8ca7dd945199d46acf05c476b78fe4883b9a193294b3dbf5b0b8d143d7a7682d016ad4e6741eef91de74565c2c57030d86c598b271041cdb8bb5125cf43f9d64f83774f3b4032c0accf6c2787a4599011d4b9ba848b50fb47da918f6800de5055b3a566fb858dbd2f9d6db758d801ce2494ce22ec824e74300b030014ba3c54f0b8544d737e7761f9c2aced8114e5a92827928e9c000dd4353b92fc61f32d172b9133a5c3386adb3030c159b252b01163b2093830f7487d095baf7aacb2f135aee40e04d866d07c97ff273af7251cb6e8aedeca0f5e06f9776464ff8a0bf95aa0033c597f3cb4144ebd2b6dc5b1114e544cd22d445b4cb504fdfd7a392e5c300eb9c463c027f5e48e8e81dd819ada10ce2aa1c3c37c70ad5970289c3204274bf8762611041bf9c5b6864bbf5fc8d31b0772626bfe1b1f5f634bdaf36f1652d5687a69b21e4010b3e5d82f31c63e07904b104b87809d7e2f52f58c3008ae8fa98fae65e8e7609671e89a3e1bb4a0eab52b5e541863bc2fa850b855f4ecc7e08e5a9293bfb48ba6f03f5042816234b304c6f2af3df3029123f44ea52e99d2b59f3211a16096aa03cd8e7fcdc277665f32cac3f023a1468cd2aea4e8863c63dc0af27b3a42a071f4e16970ee06c1fc287df2401e0f2d719ab8e217efeb114c2c0bf18c41933f898245395e77961815ed05e9366d20b2083a5b681ff823609b1b67d1cdcb9ab11b5e80b971ade516186b4d1fe4e4528a35d790e96d857bf78658eda334140ed19f5599b474dbc7f3934bf5459dbdfff6b201a7cff6ddef2d621b30e6e5ba532a1dd3b8b68540f94b32e9ac7b40ef06804147e31e2d7c5c3344a2fcf9ccd4726d4484f05966a4ddbc6ee717d9d71b1f33afc7afa5fdf2234297c4b8db3e9f4de2a884bf91a534066f4ad988d6c498a09abd95b5ffbafe5119953878bc6611f56fa26662802ee6dcd759a17bd9b0c4391ff7ad8eccf7bfba5a97450d179df34116b5c7c85a6bbd6eb61b596227c968e3118640954e17fb01455bfee4051d4b6ee2c01c3f5fe4a4acf965c94f5aa9a699349f4ebec88ebfe1deeb8bbc512f2d11609c52eede49ca6313b192110230d0b58624d1d05a28248bd29acedf348c67a3c686892a1b2f167731e164eebad6f3a3216af54c201daca26a6027b0e7d3c05bf00f26091b451e17489f1c05b475158a14071d0103c692f8fb6c4621077fbe591da55f648a35fa359e993499fdb135c466e6994ab954fc773186de49b20bef7a1b6cb8954d49e22ba1d27103677faca2f9f8b19b92dd4169b564ddb7ce90fc66acc113418ff84d36382f3fe01a92fe54c54093d0386b67 diff --git a/aws-lc-rs/benches/data/cipher_aes_256_cbc.txt b/aws-lc-rs/benches/data/cipher_aes_256_cbc.txt new file mode 100644 index 00000000000..f0aa7c4330b --- /dev/null +++ b/aws-lc-rs/benches/data/cipher_aes_256_cbc.txt @@ -0,0 +1,14 @@ +# 1 block +KEY = 26b8020ed590d535b26d294de8f379ef8db9d969da6945866974594ad24f2652 +IV = 2981adfc2b36df9d806ccc231902d49e +IN = 6d00bced19251c9d4c9a8ee7ead11881 + +# 16 blocks +KEY = e5d74245cb21bf24deb0dc2fca02ada3c7494ae9ebc148b3d4e3b66c32e286b1 +IV = e4d591e0745efeef8cc9ce3c743a7227 +IN = 98b4eae2434eb203583d837f90b17d92c5aaf51cb6d160566a691d1e254067e1a038416723ab31036775e560d6c9d692a1d083dc66b348f1a47fd69c5a890044345d9c0acad8db746280b42fdd17cd07a2c7df684d979d06eb41df6b5dabf1ff6d64ad54462966dfbb1dc8d3d19085b9f9b85f2892ddce92f0b4da4fd9d97d60d9ef171f27a895bc2e00aaa7532a1230536998f246e005688e698eb7edecf05bfda05b93ac63f7eabff0296d9442a0a0c3942985b86e2b5f3a38df65be6fc8ee690ecadab6acc3b75bc4580f54101bcedf6c131081faab8c3e8a322e252260dada51e63dfe470d0d0199d2e9c2f50a77a48d382e3986c26d0db8915ef2e25e28 + +# 256 blocks +KEY = 55cafb5ac2edbbe3fdffee32cc9d5ed954dcaf80585671086404b275d7472c15 +IV = c76cb076894620ca19466a4b9f512082 +IN = a606b6054c433bf7b052aa96b5438ced6675050987c7f9036bb9bc261e5c422c05b155c85bf319f31cef15370f8adb6b574484bfa61cbc203b2875c9d86fae358ac3a59db1230f0e614d68df10ad53077da9b609996f7fdcca9ccc3ca32c1474e563cbb6c97d8113b5f6591f72ac42ac9cdf76d4660a29b5d9b2c8157bd83ce02eb895a8b300a61f886c5d4ca7d07a7166cbd0b9d2ad818e0b1e6ae86ce17b15b67a468446268bf23f61f4fab478d1dee9443ba82b981b5f133561a3019da21b86d848530e6bfaa874d8866cab995ab7a944acac71bb1b7e398dd94a7d69f25f692a2824dcb2354e846d561d4a13d89cd69cd148e4e14d4169da0334b17664b7910f3ad5ab71ba45549fcfdad84da05795b15d6256c99110a5585584230b343ed08990a8b3033da1a07bc47de455548456b4b95f78761482811cbdf39ea35231eebf1f0792667302a269ac534d4a52b1b1927c245b566c9181002db0f6eaf22293196713e514311a658dc616e2ec4f9d8790368e627cf0b741be2a346700ae5456496464162e496ebbc19801059c9c53da709cc74687ecd5fa3d3ef740642f8740a630ff65f9b7b391c6def821e0f01e3c70503846e15e1d53204978a7d85a4eb8c3b654e3d3a7719b8661396235a3455a52de6f3e29c3333dd430eb166ad835d26cfa3be41bd3c3cafdd2bf1956447bbec205cf254f906569fd8bcf5ebaa3d3445f21938b3314e132fbbbb796a3e9abd393277650d5061833e12d6f009baf6543e0bab99be09b39975caff634c5988263e95e1861231b54228d93b3709ea17e29f0614d77da6578b9807b0e8f603f81417ddfe64936b5a337bf8e6055658d58a8680d07d9439fd453274d58dcd4cf6a5eb6a21e96dbdec9d534ee803f3ad830e58d18836ea51964098acd9af4bade9c473387288ce7ef9a73b843e272904e1bd1629e941be8590bb0c254202f93035a9d1708214a5fa0c66cce0f7db2084f63c06de42aab893a587af9b232d2dbdbbc1c85e7005845ad6de9b04ec002743392a4a6282132da9209bd5df5adf4358ac914f83186a58386629c6b1e3032ce2be4454ba7cbff15bc340fd14a51da3b54cd35ea1593b2cabd32004f45f5ae4ec6c7ed79d3d9c512d7299ccf4466958bf379211dffb90b2d8f3737dcf358f659880184707cacbff2184c39134d874fc85c21aabe6fab711f18170b0e5fd38838e19c64b77a6709612f4cd8ada37d31b5ec01347e3b7f777d0a4eb0b7f70094f6856b5bee20118cbd982d6fbf90831b73a4eeabade1243cc52a440f735daf53a6eb12d354faba5938e7e41c6c9d79bbb63d44e1b766659cf7f20d9b2b45fe66ca8fc294f191a0e5ecf7c189afab57687bc599534d5bdcc001c145d0a852e0954d5318e76350dea1afc84a274faad3eb24cafb70e5073fe214a83c8251be1a9e106d86149f569a9ef9aa1f2d473bcb06072a6cb994f400175c788b431f44f7e70f0d7563a0850111f9835b3553d138436e60e2f45a1d147bc6c111447faa79001b05b70f11919ddb1a5681a74c8e08aa892d5a1af635305e18d67e116d26662ca92072be5b9a85cd9da0b97d47f2019b12daa6a5b03f18569857a42e139a5c59cb8b876b0a6965383b4b067d2eb9d3d382b5f96c05d1747eeb39c42815f028698bb94b45fbf501104bb5127ac9213c505460e8598a099bdf2ba9f6c7afca91d34125d642a1fc9746b685ea3adcb7ad49b9ceafda3cef3f0254a28c4000611780946c5de2255af12da37e983c2b41b92ddffd59de694aa79ddc18f49f56ec43bc138c21095d9b6cd82607679a386902957c240c8e4d9196030c2798685bc78b4abbcb823eaeed99995b009bd45c2d667c7652f0d65cf781a1ae5d7348412436f78410fd0b6caf9a80caeb3e9ca2a11345399b37111cd0c579cd24ddf86652634ad6cb1b2fda3e1a194f695e2d1c3247cee2b8a88e6d373d3ac6d29b594306b749ae74b0507e4c70e29536e0a6dc5b5e85c25936117b3e6d0eee31c6685b2e45d152f74de49f82820338653dd2bd43224bfe5f0c94247a1c8e13a03bc0b9eaec114789ca4e4939fd267d261377cafe12e5adadb5df05be0555c11a78b38e454bcbf5dfe263aade057224c0aa7632a4a4ca17f9c97d983d261204a798dcf651d60e0b93f3b4337aa06c793c812fa088ebc9b07df4ca79a3105a33d3b3706c468f258c2a6b7b4a2f4fa1d589eb795c4a0b325b963cbc99eb14a23b5657f9717a6608631bbff1b94337d0c66fe7cdb391476d7c14180b538d83df38086640ff48b1e3f4c7048cef1970df7c43af5c9d586d0cdef4cf51f96aa4539b7d9608261863949e57781b6148b4892a1f42e0169e1486428a8ef5bdb0bf1a00e27c79ae2740e601c54bf5e7c9c96f82a7df1f437ea1d423170617c02751959cb25bbe310ef28ee26718580f85a454e2fb35215b3c52422f69109549c49aa3e4b4e126f2a8a38cc79c4aee2e900cc5073d1a590ada05cdd4a84c6c56247fedb47efe1b8aa33bce4e616623d7e1b250c242e6cd763250b1623db116fce701f46ddb7c9d63bf3ddb173961ae354194c3d2674a228ae977f7de78f7517e0e5612272b6dfc627dfa01f7cc0f4d6c3b5d591bb3c80f77eb23e9d2385666efe0c7fc98ce3fa02c24bc6209bd6db3ba5976d443ff106a41827b156a37a7afc62c916972a410a0ad7cb33188e7db331ec7fb51bdc1fb5537aedc0d1476e1d71adf060439f57a1b83a5af191b0e12ca533b0348e997dce20798fdd2d01c5424c7d606b435c8141578210447d6dec63b06739722a138f0fd2bcdf6cc455b175b07fe0e96c7003662ad71b047884205ce1a83e9a96b9acfa2d837a1b17b4ad865b778646517a33a9515b09b7659f932d7cc660eb5bc39805ed8b1831d72365d772f7875259da722a18274010483df24868c8347cc1dcc67d25fa148a355c0bcdeac84f5aad0af892b24df2928059508c98003ed734ab6eeaeac67060e992c2c1f991247439b514ca3ebe038a4adf04d5d6514e17bbd9ae58c160eaa2a8606d1f9e4dc6e03f48c2c94230f602cfb2c5b285d5ac0d00bdd8392302cfce89fca520e2c91f6b870028a2850b8da2ef86c1088a61a3780bc0f3eca4b28bf84842e455c320b256c26c2b89a03cb9e73821a1014008a175793d35f60641c972352bd3851ec88246960a18672a68dc062d97009f3d34e194c32c124b22e966b63d199e102508b6e23e9d7b0ea38e5562a4198688162219bd70a1b4eb2f77077e6d1b5cc4556a7b9825cd4528e9f96fb7fc65b247baabe903c9de016642f5cd71de47f9c605e985973a3bb0699f54406e66606836d9d7c8b707c8964b59a0f963e0d5f6cc23c05f52b49118d8fcb4e488381104435583ce71583b285c02e4ceff41c81a7b9639d4d0d40dc1f4fca3ff58406426a1aa9a026bd258b6e0f103f1a0850d289b3126afaf17cdaaf5ace15c735363155f4b1c869d6421d015ad499bc9081891686542fb9ddb6f58c3c69e23ace91d75685e93161bf80ff3cb4f3dc946d94b4cece9ececbc9a20975543c7f358346a028d774c811d72ae68da1fad92e6cc6aa497c6365f2003f8e4754faafe1a7b48e626e5613ffe880c404ad0a8104186ccc5c4392f94316f437db2d0bfe4c68394ce764377e810c8a3c9b938c8f4b7727fd1d067f3b38490663ca3183c453cbe760cb0a92d236dd153f51f45b2193437b3340f8a6a44ff30dfbb7e3a57dec7cd80b3288797147e95bfb821c84a7401595f0521c20337a9e9e31714b0b4b7c4708604db81ae0b5755b6c04491d81023ac7f6a32b8f92e46cc339922f381dd6e475d0a43c03707fd2b5796e272d2977569e1ecf43fdc98a41dcd017e10abd0b9ce75ca0fcf3d01b777c765dc6763cfee650802e1c50f04f9995b0b742100fcb6bb955e27974607877207c48df18ae3dc424900c30ac2ac84c7c89594211a3117d53f68c39f7456eb51cace912c7c592580e087bffd86d6c80e89d9d605bbbc29f4197b46136d04ca4d78415b308ec3800867b6abff0bc610a75295c3833e6eb6cd7de5b40e21d083228a9eb0293809061ab63810d035904f75575bcc3c229e37c6fbdde651f3a77bb99998ac6234cfdfd04b6e0e90082387798fff8497de30e66183cd545ac1964857ee1bd277d8594fec991ca9429abc0393c307187dd41e3eb9bffce0b6882c85de6d68b2638b1061c534effd25f7bc702ba2e109fa78ff5e45e308e2e45df1b9846fe3694ee7e2a7c1cb82b9d2b0f0b2e86c69acad5b98d30df79444445eb1143605110219ebf6f203626632d426b1583f83101ba1abcabe233b5409c1b48f20e6453a66a6cf15361679ae99aac1971559ecf58896f5c950ac6b7482784872665d3ce563c89dec404332dc32c19eb69093d556341d7ec2beac4a70ee6a07023b0e8ca7dd945199d46acf05c476b78fe4883b9a193294b3dbf5b0b8d143d7a7682d016ad4e6741eef91de74565c2c57030d86c598b271041cdb8bb5125cf43f9d64f83774f3b4032c0accf6c2787a4599011d4b9ba848b50fb47da918f6800de5055b3a566fb858dbd2f9d6db758d801ce2494ce22ec824e74300b030014ba3c54f0b8544d737e7761f9c2aced8114e5a92827928e9c000dd4353b92fc61f32d172b9133a5c3386adb3030c159b252b01163b2093830f7487d095baf7aacb2f135aee40e04d866d07c97ff273af7251cb6e8aedeca0f5e06f9776464ff8a0bf95aa0033c597f3cb4144ebd2b6dc5b1114e544cd22d445b4cb504fdfd7a392e5c300eb9c463c027f5e48e8e81dd819ada10ce2aa1c3c37c70ad5970289c3204274bf8762611041bf9c5b6864bbf5fc8d31b0772626bfe1b1f5f634bdaf36f1652d5687a69b21e4010b3e5d82f31c63e07904b104b87809d7e2f52f58c3008ae8fa98fae65e8e7609671e89a3e1bb4a0eab52b5e541863bc2fa850b855f4ecc7e08e5a9293bfb48ba6f03f5042816234b304c6f2af3df3029123f44ea52e99d2b59f3211a16096aa03cd8e7fcdc277665f32cac3f023a1468cd2aea4e8863c63dc0af27b3a42a071f4e16970ee06c1fc287df2401e0f2d719ab8e217efeb114c2c0bf18c41933f898245395e77961815ed05e9366d20b2083a5b681ff823609b1b67d1cdcb9ab11b5e80b971ade516186b4d1fe4e4528a35d790e96d857bf78658eda334140ed19f5599b474dbc7f3934bf5459dbdfff6b201a7cff6ddef2d621b30e6e5ba532a1dd3b8b68540f94b32e9ac7b40ef06804147e31e2d7c5c3344a2fcf9ccd4726d4484f05966a4ddbc6ee717d9d71b1f33afc7afa5fdf2234297c4b8db3e9f4de2a884bf91a534066f4ad988d6c498a09abd95b5ffbafe5119953878bc6611f56fa26662802ee6dcd759a17bd9b0c4391ff7ad8eccf7bfba5a97450d179df34116b5c7c85a6bbd6eb61b596227c968e3118640954e17fb01455bfee4051d4b6ee2c01c3f5fe4a4acf965c94f5aa9a699349f4ebec88ebfe1deeb8bbc512f2d11609c52eede49ca6313b192110230d0b58624d1d05a28248bd29acedf348c67a3c686892a1b2f167731e164eebad6f3a3216af54c201daca26a6027b0e7d3c05bf00f26091b451e17489f1c05b475158a14071d0103c692f8fb6c4621077fbe591da55f648a35fa359e993499fdb135c466e6994ab954fc773186de49b20bef7a1b6cb8954d49e22ba1d27103677faca2f9f8b19b92dd4169b564ddb7ce90fc66acc113418ff84d36382f3fe01a92fe54c54093d0386b67 diff --git a/aws-lc-rs/benches/data/cipher_aes_256_ctr.txt b/aws-lc-rs/benches/data/cipher_aes_256_ctr.txt new file mode 100644 index 00000000000..f0aa7c4330b --- /dev/null +++ b/aws-lc-rs/benches/data/cipher_aes_256_ctr.txt @@ -0,0 +1,14 @@ +# 1 block +KEY = 26b8020ed590d535b26d294de8f379ef8db9d969da6945866974594ad24f2652 +IV = 2981adfc2b36df9d806ccc231902d49e +IN = 6d00bced19251c9d4c9a8ee7ead11881 + +# 16 blocks +KEY = e5d74245cb21bf24deb0dc2fca02ada3c7494ae9ebc148b3d4e3b66c32e286b1 +IV = e4d591e0745efeef8cc9ce3c743a7227 +IN = 98b4eae2434eb203583d837f90b17d92c5aaf51cb6d160566a691d1e254067e1a038416723ab31036775e560d6c9d692a1d083dc66b348f1a47fd69c5a890044345d9c0acad8db746280b42fdd17cd07a2c7df684d979d06eb41df6b5dabf1ff6d64ad54462966dfbb1dc8d3d19085b9f9b85f2892ddce92f0b4da4fd9d97d60d9ef171f27a895bc2e00aaa7532a1230536998f246e005688e698eb7edecf05bfda05b93ac63f7eabff0296d9442a0a0c3942985b86e2b5f3a38df65be6fc8ee690ecadab6acc3b75bc4580f54101bcedf6c131081faab8c3e8a322e252260dada51e63dfe470d0d0199d2e9c2f50a77a48d382e3986c26d0db8915ef2e25e28 + +# 256 blocks +KEY = 55cafb5ac2edbbe3fdffee32cc9d5ed954dcaf80585671086404b275d7472c15 +IV = c76cb076894620ca19466a4b9f512082 +IN = a606b6054c433bf7b052aa96b5438ced6675050987c7f9036bb9bc261e5c422c05b155c85bf319f31cef15370f8adb6b574484bfa61cbc203b2875c9d86fae358ac3a59db1230f0e614d68df10ad53077da9b609996f7fdcca9ccc3ca32c1474e563cbb6c97d8113b5f6591f72ac42ac9cdf76d4660a29b5d9b2c8157bd83ce02eb895a8b300a61f886c5d4ca7d07a7166cbd0b9d2ad818e0b1e6ae86ce17b15b67a468446268bf23f61f4fab478d1dee9443ba82b981b5f133561a3019da21b86d848530e6bfaa874d8866cab995ab7a944acac71bb1b7e398dd94a7d69f25f692a2824dcb2354e846d561d4a13d89cd69cd148e4e14d4169da0334b17664b7910f3ad5ab71ba45549fcfdad84da05795b15d6256c99110a5585584230b343ed08990a8b3033da1a07bc47de455548456b4b95f78761482811cbdf39ea35231eebf1f0792667302a269ac534d4a52b1b1927c245b566c9181002db0f6eaf22293196713e514311a658dc616e2ec4f9d8790368e627cf0b741be2a346700ae5456496464162e496ebbc19801059c9c53da709cc74687ecd5fa3d3ef740642f8740a630ff65f9b7b391c6def821e0f01e3c70503846e15e1d53204978a7d85a4eb8c3b654e3d3a7719b8661396235a3455a52de6f3e29c3333dd430eb166ad835d26cfa3be41bd3c3cafdd2bf1956447bbec205cf254f906569fd8bcf5ebaa3d3445f21938b3314e132fbbbb796a3e9abd393277650d5061833e12d6f009baf6543e0bab99be09b39975caff634c5988263e95e1861231b54228d93b3709ea17e29f0614d77da6578b9807b0e8f603f81417ddfe64936b5a337bf8e6055658d58a8680d07d9439fd453274d58dcd4cf6a5eb6a21e96dbdec9d534ee803f3ad830e58d18836ea51964098acd9af4bade9c473387288ce7ef9a73b843e272904e1bd1629e941be8590bb0c254202f93035a9d1708214a5fa0c66cce0f7db2084f63c06de42aab893a587af9b232d2dbdbbc1c85e7005845ad6de9b04ec002743392a4a6282132da9209bd5df5adf4358ac914f83186a58386629c6b1e3032ce2be4454ba7cbff15bc340fd14a51da3b54cd35ea1593b2cabd32004f45f5ae4ec6c7ed79d3d9c512d7299ccf4466958bf379211dffb90b2d8f3737dcf358f659880184707cacbff2184c39134d874fc85c21aabe6fab711f18170b0e5fd38838e19c64b77a6709612f4cd8ada37d31b5ec01347e3b7f777d0a4eb0b7f70094f6856b5bee20118cbd982d6fbf90831b73a4eeabade1243cc52a440f735daf53a6eb12d354faba5938e7e41c6c9d79bbb63d44e1b766659cf7f20d9b2b45fe66ca8fc294f191a0e5ecf7c189afab57687bc599534d5bdcc001c145d0a852e0954d5318e76350dea1afc84a274faad3eb24cafb70e5073fe214a83c8251be1a9e106d86149f569a9ef9aa1f2d473bcb06072a6cb994f400175c788b431f44f7e70f0d7563a0850111f9835b3553d138436e60e2f45a1d147bc6c111447faa79001b05b70f11919ddb1a5681a74c8e08aa892d5a1af635305e18d67e116d26662ca92072be5b9a85cd9da0b97d47f2019b12daa6a5b03f18569857a42e139a5c59cb8b876b0a6965383b4b067d2eb9d3d382b5f96c05d1747eeb39c42815f028698bb94b45fbf501104bb5127ac9213c505460e8598a099bdf2ba9f6c7afca91d34125d642a1fc9746b685ea3adcb7ad49b9ceafda3cef3f0254a28c4000611780946c5de2255af12da37e983c2b41b92ddffd59de694aa79ddc18f49f56ec43bc138c21095d9b6cd82607679a386902957c240c8e4d9196030c2798685bc78b4abbcb823eaeed99995b009bd45c2d667c7652f0d65cf781a1ae5d7348412436f78410fd0b6caf9a80caeb3e9ca2a11345399b37111cd0c579cd24ddf86652634ad6cb1b2fda3e1a194f695e2d1c3247cee2b8a88e6d373d3ac6d29b594306b749ae74b0507e4c70e29536e0a6dc5b5e85c25936117b3e6d0eee31c6685b2e45d152f74de49f82820338653dd2bd43224bfe5f0c94247a1c8e13a03bc0b9eaec114789ca4e4939fd267d261377cafe12e5adadb5df05be0555c11a78b38e454bcbf5dfe263aade057224c0aa7632a4a4ca17f9c97d983d261204a798dcf651d60e0b93f3b4337aa06c793c812fa088ebc9b07df4ca79a3105a33d3b3706c468f258c2a6b7b4a2f4fa1d589eb795c4a0b325b963cbc99eb14a23b5657f9717a6608631bbff1b94337d0c66fe7cdb391476d7c14180b538d83df38086640ff48b1e3f4c7048cef1970df7c43af5c9d586d0cdef4cf51f96aa4539b7d9608261863949e57781b6148b4892a1f42e0169e1486428a8ef5bdb0bf1a00e27c79ae2740e601c54bf5e7c9c96f82a7df1f437ea1d423170617c02751959cb25bbe310ef28ee26718580f85a454e2fb35215b3c52422f69109549c49aa3e4b4e126f2a8a38cc79c4aee2e900cc5073d1a590ada05cdd4a84c6c56247fedb47efe1b8aa33bce4e616623d7e1b250c242e6cd763250b1623db116fce701f46ddb7c9d63bf3ddb173961ae354194c3d2674a228ae977f7de78f7517e0e5612272b6dfc627dfa01f7cc0f4d6c3b5d591bb3c80f77eb23e9d2385666efe0c7fc98ce3fa02c24bc6209bd6db3ba5976d443ff106a41827b156a37a7afc62c916972a410a0ad7cb33188e7db331ec7fb51bdc1fb5537aedc0d1476e1d71adf060439f57a1b83a5af191b0e12ca533b0348e997dce20798fdd2d01c5424c7d606b435c8141578210447d6dec63b06739722a138f0fd2bcdf6cc455b175b07fe0e96c7003662ad71b047884205ce1a83e9a96b9acfa2d837a1b17b4ad865b778646517a33a9515b09b7659f932d7cc660eb5bc39805ed8b1831d72365d772f7875259da722a18274010483df24868c8347cc1dcc67d25fa148a355c0bcdeac84f5aad0af892b24df2928059508c98003ed734ab6eeaeac67060e992c2c1f991247439b514ca3ebe038a4adf04d5d6514e17bbd9ae58c160eaa2a8606d1f9e4dc6e03f48c2c94230f602cfb2c5b285d5ac0d00bdd8392302cfce89fca520e2c91f6b870028a2850b8da2ef86c1088a61a3780bc0f3eca4b28bf84842e455c320b256c26c2b89a03cb9e73821a1014008a175793d35f60641c972352bd3851ec88246960a18672a68dc062d97009f3d34e194c32c124b22e966b63d199e102508b6e23e9d7b0ea38e5562a4198688162219bd70a1b4eb2f77077e6d1b5cc4556a7b9825cd4528e9f96fb7fc65b247baabe903c9de016642f5cd71de47f9c605e985973a3bb0699f54406e66606836d9d7c8b707c8964b59a0f963e0d5f6cc23c05f52b49118d8fcb4e488381104435583ce71583b285c02e4ceff41c81a7b9639d4d0d40dc1f4fca3ff58406426a1aa9a026bd258b6e0f103f1a0850d289b3126afaf17cdaaf5ace15c735363155f4b1c869d6421d015ad499bc9081891686542fb9ddb6f58c3c69e23ace91d75685e93161bf80ff3cb4f3dc946d94b4cece9ececbc9a20975543c7f358346a028d774c811d72ae68da1fad92e6cc6aa497c6365f2003f8e4754faafe1a7b48e626e5613ffe880c404ad0a8104186ccc5c4392f94316f437db2d0bfe4c68394ce764377e810c8a3c9b938c8f4b7727fd1d067f3b38490663ca3183c453cbe760cb0a92d236dd153f51f45b2193437b3340f8a6a44ff30dfbb7e3a57dec7cd80b3288797147e95bfb821c84a7401595f0521c20337a9e9e31714b0b4b7c4708604db81ae0b5755b6c04491d81023ac7f6a32b8f92e46cc339922f381dd6e475d0a43c03707fd2b5796e272d2977569e1ecf43fdc98a41dcd017e10abd0b9ce75ca0fcf3d01b777c765dc6763cfee650802e1c50f04f9995b0b742100fcb6bb955e27974607877207c48df18ae3dc424900c30ac2ac84c7c89594211a3117d53f68c39f7456eb51cace912c7c592580e087bffd86d6c80e89d9d605bbbc29f4197b46136d04ca4d78415b308ec3800867b6abff0bc610a75295c3833e6eb6cd7de5b40e21d083228a9eb0293809061ab63810d035904f75575bcc3c229e37c6fbdde651f3a77bb99998ac6234cfdfd04b6e0e90082387798fff8497de30e66183cd545ac1964857ee1bd277d8594fec991ca9429abc0393c307187dd41e3eb9bffce0b6882c85de6d68b2638b1061c534effd25f7bc702ba2e109fa78ff5e45e308e2e45df1b9846fe3694ee7e2a7c1cb82b9d2b0f0b2e86c69acad5b98d30df79444445eb1143605110219ebf6f203626632d426b1583f83101ba1abcabe233b5409c1b48f20e6453a66a6cf15361679ae99aac1971559ecf58896f5c950ac6b7482784872665d3ce563c89dec404332dc32c19eb69093d556341d7ec2beac4a70ee6a07023b0e8ca7dd945199d46acf05c476b78fe4883b9a193294b3dbf5b0b8d143d7a7682d016ad4e6741eef91de74565c2c57030d86c598b271041cdb8bb5125cf43f9d64f83774f3b4032c0accf6c2787a4599011d4b9ba848b50fb47da918f6800de5055b3a566fb858dbd2f9d6db758d801ce2494ce22ec824e74300b030014ba3c54f0b8544d737e7761f9c2aced8114e5a92827928e9c000dd4353b92fc61f32d172b9133a5c3386adb3030c159b252b01163b2093830f7487d095baf7aacb2f135aee40e04d866d07c97ff273af7251cb6e8aedeca0f5e06f9776464ff8a0bf95aa0033c597f3cb4144ebd2b6dc5b1114e544cd22d445b4cb504fdfd7a392e5c300eb9c463c027f5e48e8e81dd819ada10ce2aa1c3c37c70ad5970289c3204274bf8762611041bf9c5b6864bbf5fc8d31b0772626bfe1b1f5f634bdaf36f1652d5687a69b21e4010b3e5d82f31c63e07904b104b87809d7e2f52f58c3008ae8fa98fae65e8e7609671e89a3e1bb4a0eab52b5e541863bc2fa850b855f4ecc7e08e5a9293bfb48ba6f03f5042816234b304c6f2af3df3029123f44ea52e99d2b59f3211a16096aa03cd8e7fcdc277665f32cac3f023a1468cd2aea4e8863c63dc0af27b3a42a071f4e16970ee06c1fc287df2401e0f2d719ab8e217efeb114c2c0bf18c41933f898245395e77961815ed05e9366d20b2083a5b681ff823609b1b67d1cdcb9ab11b5e80b971ade516186b4d1fe4e4528a35d790e96d857bf78658eda334140ed19f5599b474dbc7f3934bf5459dbdfff6b201a7cff6ddef2d621b30e6e5ba532a1dd3b8b68540f94b32e9ac7b40ef06804147e31e2d7c5c3344a2fcf9ccd4726d4484f05966a4ddbc6ee717d9d71b1f33afc7afa5fdf2234297c4b8db3e9f4de2a884bf91a534066f4ad988d6c498a09abd95b5ffbafe5119953878bc6611f56fa26662802ee6dcd759a17bd9b0c4391ff7ad8eccf7bfba5a97450d179df34116b5c7c85a6bbd6eb61b596227c968e3118640954e17fb01455bfee4051d4b6ee2c01c3f5fe4a4acf965c94f5aa9a699349f4ebec88ebfe1deeb8bbc512f2d11609c52eede49ca6313b192110230d0b58624d1d05a28248bd29acedf348c67a3c686892a1b2f167731e164eebad6f3a3216af54c201daca26a6027b0e7d3c05bf00f26091b451e17489f1c05b475158a14071d0103c692f8fb6c4621077fbe591da55f648a35fa359e993499fdb135c466e6994ab954fc773186de49b20bef7a1b6cb8954d49e22ba1d27103677faca2f9f8b19b92dd4169b564ddb7ce90fc66acc113418ff84d36382f3fe01a92fe54c54093d0386b67 From f1c8096f7b683dfe7099bf8248ff3e3bb856a52e Mon Sep 17 00:00:00 2001 From: Sean McGrail Date: Wed, 24 May 2023 09:08:48 -0700 Subject: [PATCH 24/54] Rename Unpadded to NoPadding --- aws-lc-rs/src/cipher.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index cebb95c62e0..68fb98764af 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -103,7 +103,7 @@ impl Drop for SymmetricCipherKey { #[allow(dead_code)] #[derive(Clone, Copy)] enum PaddingStrategy { - Unpadded, + NoPadding, PKCS7, } @@ -128,7 +128,7 @@ impl OperatingMode { in_out.extend(vec![v; padding_size].iter()); } } - PaddingStrategy::Unpadded => {} + PaddingStrategy::NoPadding => {} }, OperatingMode::Stream => {} } @@ -159,7 +159,7 @@ impl OperatingMode { let final_len = in_out.len() - padding as usize; Ok(&mut in_out[0..final_len]) } - PaddingStrategy::Unpadded => Ok(in_out), + PaddingStrategy::NoPadding => Ok(in_out), }, OperatingMode::Stream => Ok(in_out), } From 8f95f1210fb54ec6a0465d755c1993ca8db2c26f Mon Sep 17 00:00:00 2001 From: Sean McGrail Date: Thu, 25 May 2023 16:08:51 -0700 Subject: [PATCH 25/54] Redesign for CBC/CTR --- aws-lc-rs/src/cipher.rs | 1016 ++++++++++++++++++++++++--------------- aws-lc-rs/src/iv.rs | 59 --- 2 files changed, 639 insertions(+), 436 deletions(-) diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index 68fb98764af..c2eb9aa7332 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -48,7 +48,7 @@ use crate::cipher::aes::{encrypt_block_aes, Aes128Key, Aes256Key}; use crate::cipher::block::Block; use crate::cipher::chacha::ChaCha20Key; use crate::error::Unspecified; -use crate::iv::{FixedLength, NonceIV}; +use crate::iv::{self, FixedLength}; use aws_lc::{ AES_cbc_encrypt, AES_ctr128_encrypt, AES_set_decrypt_key, AES_set_encrypt_key, AES_DECRYPT, AES_ENCRYPT, AES_KEY, @@ -100,83 +100,65 @@ impl Drop for SymmetricCipherKey { } } +/// The cipher block padding strategy. #[allow(dead_code)] #[derive(Clone, Copy)] -enum PaddingStrategy { - NoPadding, +pub enum PaddingStrategy { + /// PKCS#7 Padding. PKCS7, } -impl OperatingMode { +impl PaddingStrategy { fn add_padding(self, block_len: usize, in_out: &mut InOut) -> Result<(), Unspecified> where InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, { match self { - OperatingMode::Block(strategy) => match strategy { - PaddingStrategy::PKCS7 => { - let in_out_len = in_out.as_mut().len(); - // This implements PKCS#7 padding scheme, used by aws-lc if we were using EVP_CIPHER API's - let remainder = in_out_len % block_len; - if remainder == 0 { - let block_size: u8 = block_len.try_into().map_err(|_| Unspecified)?; - in_out.extend(vec![block_size; block_len].iter()); - } else { - let padding_size = block_len - remainder; - let v: u8 = padding_size.try_into().map_err(|_| Unspecified)?; - // Heap allocation :( - in_out.extend(vec![v; padding_size].iter()); - } + PaddingStrategy::PKCS7 => { + let in_out_len = in_out.as_mut().len(); + // This implements PKCS#7 padding scheme, used by aws-lc if we were using EVP_CIPHER API's + let remainder = in_out_len % block_len; + if remainder == 0 { + let block_size: u8 = block_len.try_into().map_err(|_| Unspecified)?; + in_out.extend(vec![block_size; block_len].iter()); + } else { + let padding_size = block_len - remainder; + let v: u8 = padding_size.try_into().map_err(|_| Unspecified)?; + // Heap allocation :( + in_out.extend(vec![v; padding_size].iter()); } - PaddingStrategy::NoPadding => {} - }, - OperatingMode::Stream => {} + } } Ok(()) } fn remove_padding(self, block_len: usize, in_out: &mut [u8]) -> Result<&mut [u8], Unspecified> { match self { - OperatingMode::Block(strategy) => match strategy { - PaddingStrategy::PKCS7 => { - let block_size: u8 = block_len.try_into().map_err(|_| Unspecified)?; + PaddingStrategy::PKCS7 => { + let block_size: u8 = block_len.try_into().map_err(|_| Unspecified)?; - if in_out.is_empty() || in_out.len() < block_len { - return Err(Unspecified); - } + if in_out.is_empty() || in_out.len() < block_len { + return Err(Unspecified); + } - let padding: u8 = in_out[in_out.len() - 1]; - if padding == 0 || padding > block_size { - return Err(Unspecified); - } + let padding: u8 = in_out[in_out.len() - 1]; + if padding == 0 || padding > block_size { + return Err(Unspecified); + } - for item in in_out.iter().skip(in_out.len() - padding as usize) { - if *item != padding { - return Err(Unspecified); - } + for item in in_out.iter().skip(in_out.len() - padding as usize) { + if *item != padding { + return Err(Unspecified); } - - let final_len = in_out.len() - padding as usize; - Ok(&mut in_out[0..final_len]) } - PaddingStrategy::NoPadding => Ok(in_out), - }, - OperatingMode::Stream => Ok(in_out), + + let final_len = in_out.len() - padding as usize; + Ok(&mut in_out[0..final_len]) + } } } } -#[derive(Clone, Copy)] -enum OperatingMode { - Block(PaddingStrategy), - Stream, -} - -/// A cipher configuration description. -pub struct CipherConfig( - OperatingMode, -); - /// The number of bytes in an AES 128-bit key pub const AES_128_KEY_LEN: usize = 16; @@ -187,128 +169,94 @@ pub const AES_256_KEY_LEN: usize = 32; pub const AES_IV_LEN: usize = 16; const AES_BLOCK_LEN: usize = 16; -/// A Block or Stream Cipher Algorithm -/// -/// # Supported Algorithms -/// -/// ## Counter (CTR) Modes -/// -/// * [`AES_128_CTR`] -/// * [`AES_256_CTR`] -/// -/// ## Cipher block chaining (CBC) Modes -/// -/// * [`AES_128_CBC_PKCS7_PADDING`] -/// * [`AES_256_CBC_PKCS7_PADDING`] -///l -pub enum Algorithm { - /// AES-128 Counter (CTR) Mode - Aes128Ctr(CipherConfig), - - /// AES-256 Counter (CTR) Mode - Aes256Ctr(CipherConfig), - - /// AES-128 Cipher block chaining (CBC) Mode - Aes128CbcPkcs7Padding(CipherConfig), - - /// AES-256 Cipher block chaining (CBC) Mode - Aes256CbcPkcs7Padding(CipherConfig), -} - -impl Algorithm { - fn get_operating_mode(&self) -> &OperatingMode { - match self { - Algorithm::Aes128Ctr(v) | Algorithm::Aes128CbcPkcs7Padding(v) => v.get_operating_mode(), - Algorithm::Aes256Ctr(v) | Algorithm::Aes256CbcPkcs7Padding(v) => v.get_operating_mode(), - } - } - - fn get_block_len(&self) -> usize { - match self { - Algorithm::Aes128Ctr(v) | Algorithm::Aes128CbcPkcs7Padding(v) => v.get_block_len(), - Algorithm::Aes256Ctr(v) | Algorithm::Aes256CbcPkcs7Padding(v) => v.get_block_len(), - } - } - - #[allow(clippy::unused_self)] - fn new_randomized_nonce(&self) -> Result { - Ok(NonceIV::Size128(FixedLength::<16>::new()?)) - } +/// The cipher operating mode. +#[non_exhaustive] +#[derive(Clone, Copy)] +pub enum OperatingMode { + /// Cipher block chaining (CBC) mode. + CBC, - fn new_symmetric_cipher_key( - &self, - key_bytes: &[u8], - ) -> Result { - match self { - Algorithm::Aes128Ctr(v) | Algorithm::Aes128CbcPkcs7Padding(v) => { - let key = v.try_into_key(key_bytes)?; - SymmetricCipherKey::aes128(key) - } - Algorithm::Aes256Ctr(v) | Algorithm::Aes256CbcPkcs7Padding(v) => { - let key = v.try_into_key(key_bytes)?; - SymmetricCipherKey::aes256(key) - } - } - } + /// Counter (CTR) mode. + CTR, +} - fn prepare_for_encrypt(&self, in_out: &mut InOut) -> Result<(), Unspecified> - where - InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, - { - self.get_operating_mode() - .add_padding(self.get_block_len(), in_out)?; - Ok(()) - } +/// The contextual data used to encrypted/decrypt data. +#[non_exhaustive] +pub enum DecryptionContext { + /// A 128-bit Initalization Vector. + Iv128(iv::FixedLength<16>), - fn finalalize_decryption<'a>(&self, in_out: &'a mut [u8]) -> Result<&'a mut [u8], Unspecified> { - let in_out = self - .get_operating_mode() - .remove_padding(self.get_block_len(), in_out.as_mut())?; - Ok(in_out) - } + /// No input to the cipher mode. + None, +} - const fn max_block_len() -> usize { - 16 - } +#[non_exhaustive] +/// Cipher algorithm identifier. +pub enum AlgorithmId { + /// AES 128-bit + Aes128, - const fn max_iv_len() -> usize { - 16 - } + /// AES 256-bit + Aes256, } -/// AES-128 Counter (CTR) Mode -pub const AES_128_CTR: Algorithm = Algorithm::Aes128Ctr(CipherConfig(OperatingMode::Stream)); +/// A cipher algorithm. +pub struct Algorithm { + id: AlgorithmId, + key_len: usize, + block_len: usize, +} -/// AES-128 Cipher block chaining (CBC) Mode using PKCS#7 padding. -pub const AES_128_CBC_PKCS7_PADDING: Algorithm = - Algorithm::Aes128CbcPkcs7Padding(CipherConfig(OperatingMode::Block(PaddingStrategy::PKCS7))); +/// AES 128-bit cipher +pub const AES_128: Algorithm = Algorithm { + id: AlgorithmId::Aes128, + key_len: AES_128_KEY_LEN, + block_len: AES_BLOCK_LEN, +}; -/// AES-256 Counter (CTR) Mode -pub const AES_256_CTR: Algorithm = Algorithm::Aes256Ctr(CipherConfig(OperatingMode::Stream)); +/// AES 256-bit cipher +pub const AES_256: Algorithm = Algorithm { + id: AlgorithmId::Aes256, + key_len: AES_256_KEY_LEN, + block_len: AES_BLOCK_LEN, +}; -/// AES-256 Cipher block chaining (CBC) Mode using PKCS#7 padding. -pub const AES_256_CBC_PKCS7_PADDING: Algorithm = - Algorithm::Aes256CbcPkcs7Padding(CipherConfig(OperatingMode::Block(PaddingStrategy::PKCS7))); +impl Algorithm { + fn id(&self) -> &AlgorithmId { + &self.id + } -const MAX_BLOCK_LEN: usize = 16; + #[allow(unused)] + const fn key_len(&self) -> usize { + self.key_len + } -impl - CipherConfig -{ - #[inline] - fn get_operating_mode(&self) -> &OperatingMode { - &self.0 + const fn block_len(&self) -> usize { + self.block_len } - #[allow(clippy::unused_self)] - fn try_into_key<'a>(&self, key: &'a [u8]) -> Result<&'a [u8; KEY_LEN], Unspecified> { - let key: &'a [u8; KEY_LEN] = key.try_into()?; - Ok(key) + fn new_decrypting_context( + &self, + mode: OperatingMode, + ) -> Result { + match self.id { + AlgorithmId::Aes128 | AlgorithmId::Aes256 => match mode { + OperatingMode::CBC | OperatingMode::CTR => { + Ok(DecryptionContext::Iv128(FixedLength::new()?)) + } + }, + } } - #[allow(clippy::unused_self)] - fn get_block_len(&self) -> usize { - BLOCK_LEN + fn is_valid_decryption_context(&self, mode: OperatingMode, input: &DecryptionContext) -> bool { + match self.id { + AlgorithmId::Aes128 | AlgorithmId::Aes256 => match mode { + OperatingMode::CBC | OperatingMode::CTR => match input { + DecryptionContext::Iv128(_) => true, + _ => false, + }, + }, + } } } @@ -327,181 +275,436 @@ impl UnboundCipherKey { /// length required by `algorithm`. /// pub fn new(algorithm: &'static Algorithm, key_bytes: &[u8]) -> Result { - let key = algorithm.new_symmetric_cipher_key(key_bytes)?; + let key = match algorithm.id() { + AlgorithmId::Aes128 => SymmetricCipherKey::aes128(key_bytes), + AlgorithmId::Aes256 => SymmetricCipherKey::aes256(key_bytes), + }?; Ok(UnboundCipherKey { algorithm, key }) } #[inline] - fn get_algorithm(&self) -> &'static Algorithm { + fn algorithm(&self) -> &'static Algorithm { self.algorithm } } -/// An encryting cipher key. -pub struct EncryptingKey { - cipher_key: UnboundCipherKey, - iv: NonceIV, +/// A cipher encryption key that performs block padding. +pub struct PaddedBlockEncryptingKey { + key: UnboundCipherKey, + mode: OperatingMode, + padding: PaddingStrategy, + context: DecryptionContext, } -impl EncryptingKey { - /// Constructs a new [`EncryptingKey`]. - /// - /// # Errors - /// - /// * [`Unspecified`]: Returned if a randomized IV fails to be generated. - /// - pub fn new(key: UnboundCipherKey) -> Result { - let iv = key.get_algorithm().new_randomized_nonce()?; - Ok(EncryptingKey { - cipher_key: key, - iv, +impl PaddedBlockEncryptingKey { + /// Constructs a new `PaddedBlockEncryptingKey` cipher with chaining block cipher (CBC) mode. + /// Plaintext data is padded following the PKCS#7 scheme. + pub fn cbc_pkcs7(key: UnboundCipherKey) -> Result { + PaddedBlockEncryptingKey::new(key, OperatingMode::CBC, PaddingStrategy::PKCS7, None) + } + + /// Constructs a new `PaddedBlockEncryptingKey` cipher with chaining block cipher (CBC) mode. + /// The users provided context will be used for the CBC initalization-vector. + /// Plaintext data is padded following the PKCS#7 scheme. + pub fn less_safe_cbc_pkcs7( + key: UnboundCipherKey, + context: DecryptionContext, + ) -> Result { + PaddedBlockEncryptingKey::new( + key, + OperatingMode::CBC, + PaddingStrategy::PKCS7, + Some(context), + ) + } + + fn new( + key: UnboundCipherKey, + mode: OperatingMode, + padding: PaddingStrategy, + context: Option, + ) -> Result { + let mode_input = match context { + Some(mi) => { + if !key.algorithm().is_valid_decryption_context(mode, &mi) { + return Err(Unspecified); + } + mi + } + None => key.algorithm.new_decrypting_context(mode)?, + }; + + Ok(PaddedBlockEncryptingKey { + key, + mode, + padding, + context: mode_input, }) } - /// Encrypts the data `in_out` in-place. If the algorithm bound to this key uses padding - /// then the `in_out` will be extended to add the necessary padding. - /// - /// Returns the initialization vector necessary to later decrypt the data. + /// Returns the cipher algorithm. + pub fn algorithm(&self) -> &Algorithm { + self.key.algorithm() + } + + /// Returns the cipher operating mode. + pub fn mode(&self) -> OperatingMode { + self.mode + } + + /// Returns the cipher padding strategy. + pub fn padding(&self) -> PaddingStrategy { + self.padding + } + + /// Pads and encrypts data provided in `in_out` in-place. + /// Returns a references to the encryted data. /// /// # Errors + /// * [`Unspecified`]: Returned if encryption fails. /// - /// * [`Unspecified`]: Returned if the data fails to be encrypted. - /// - pub fn encrypt(self, in_out: &mut InOut) -> Result + pub fn encrypt(self, in_out: &mut InOut) -> Result where - InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, + InOut: AsMut<[u8]> + for<'a> Extend<&'a u8>, { - encrypt( - self.cipher_key.get_algorithm(), - &self.cipher_key.key, - self.iv, - in_out, - ) + self.padding + .add_padding(self.algorithm().block_len(), in_out)?; + TryInto::::try_into(self)?.encrypt(in_out.as_mut()) } } -/// An decrypting cipher key. -pub struct DecryptingKey { - cipher_key: UnboundCipherKey, - iv: NonceIV, +impl TryFrom for EncryptingKey { + type Error = Unspecified; + + fn try_from(value: PaddedBlockEncryptingKey) -> Result { + EncryptingKey::new(value.key, value.mode, Some(value.context)) + } } -impl DecryptingKey { - /// Constructs a new [`DecryptingKey`]. - #[must_use] - pub fn new(cipher_key: UnboundCipherKey, iv: NonceIV) -> DecryptingKey { - DecryptingKey { cipher_key, iv } +/// A cipher decryption key that performs block padding. +pub struct PaddedBlockDecryptingKey { + key: UnboundCipherKey, + mode: OperatingMode, + padding: PaddingStrategy, + mode_input: DecryptionContext, +} + +impl PaddedBlockDecryptingKey { + /// Constructs a new `PaddedBlockDecryptingKey` cipher with chaining block cipher (CBC) mode. + /// Decrypted data is unpadded following the PKCS#7 scheme. + pub fn cbc_pkcs7( + key: UnboundCipherKey, + mode_input: DecryptionContext, + ) -> Result { + PaddedBlockDecryptingKey::new(key, OperatingMode::CBC, PaddingStrategy::PKCS7, mode_input) } - /// Decrypts the data `in_out` in-place. - /// - /// Returns a slice reference to the decrypted data within `in_out`. If the algorithm bound to - /// this key uses padding then the returned slice reference is length adjusted to exclude - /// the padding bytes. + fn new( + key: UnboundCipherKey, + mode: OperatingMode, + padding: PaddingStrategy, + mode_input: DecryptionContext, + ) -> Result { + if !key + .algorithm() + .is_valid_decryption_context(mode, &mode_input) + { + return Err(Unspecified); + } + + Ok(PaddedBlockDecryptingKey { + key, + mode, + padding, + mode_input, + }) + } + + /// Returns the cipher algorithm. + pub fn algorithm(&self) -> &Algorithm { + self.key.algorithm() + } + + /// Returns the cipher operating mode. + pub fn mode(&self) -> OperatingMode { + self.mode + } + + /// Returns the cipher padding strategy. + pub fn padding(&self) -> PaddingStrategy { + self.padding + } + + /// Decrypts and unpads data provided in `in_out` in-place. + /// Returns a references to the decrypted data. /// /// # Errors + /// * [`Unspecified`]: Returned if decryption fails. /// - /// * [`Unspecified`]: Returned if the data fails to be decrypted. - /// - #[allow(unused_mut)] - pub fn decrypt(mut self, in_out: &mut [u8]) -> Result<&mut [u8], Unspecified> { - decrypt( - self.cipher_key.algorithm, - &self.cipher_key.key, - self.iv, - in_out, - ) + pub fn decrypt<'a>(self, in_out: &'a mut [u8]) -> Result<&'a mut [u8], Unspecified> { + let block_len = self.algorithm().block_len(); + let padding = self.padding; + let mut in_out = TryInto::::try_into(self)?.decrypt(in_out)?; + in_out = padding.remove_padding(block_len, in_out)?; + Ok(in_out) } } -/// Less safe cipher key that allows for specifying a user provided [`NonceIV`]. -pub struct LessSafeCipherKey { - algorithm: &'static Algorithm, - key: SymmetricCipherKey, +/// A cipher encryption key that does not perform block padding. +pub struct EncryptingKey { + key: UnboundCipherKey, + mode: OperatingMode, + context: DecryptionContext, } -impl LessSafeCipherKey { - /// Constructs a new [`LessSafeCipherKey`]. - /// - /// # Errors - /// * [`Unspecified`]: Returned if `key_bytes` is not the proper byte length for the selected algorithm. - /// - pub fn new(algorithm: &'static Algorithm, key_bytes: &[u8]) -> Result { - let key = algorithm.new_symmetric_cipher_key(key_bytes)?; - Ok(LessSafeCipherKey { algorithm, key }) +impl EncryptingKey { + /// Constructs an `EncryptingKey` operating in counter (CTR) mode using the provided key. + pub fn ctr(key: UnboundCipherKey) -> Result { + EncryptingKey::new(key, OperatingMode::CTR, None) } - /// Encrypts the data `in_out` in-place. If the algorithm bound to this key uses padding - /// then the `in_out` will be extended to add the necessary padding. - /// - /// Returns the initialization vector necessary to later decrypt the data. - /// - /// # Errors - /// - /// * [`Unspecified`]: Returned if the data fails to be encrypted. - /// - pub fn encrypt(&self, iv: NonceIV, in_out: &mut InOut) -> Result - where - InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, - { - encrypt(self.algorithm, &self.key, iv, in_out) + /// Constructs an `EncryptingKey` operating in counter (CTR) mode using the provided key. + /// The users provided context will be used for the CTR mode initalization-vector. + pub fn less_safe_ctr( + key: UnboundCipherKey, + context: DecryptionContext, + ) -> Result { + EncryptingKey::new(key, OperatingMode::CTR, Some(context)) } - /// Decrypts the data `in_out` in-place. - /// - /// Returns a slice reference to the decrypted data within `in_out`. If the algorithm bound to - /// this key uses padding then the returned slice reference is length adjusted to exclude - /// the padding bytes. + fn new( + key: UnboundCipherKey, + mode: OperatingMode, + context: Option, + ) -> Result { + let context = match context { + Some(mi) => { + if !key.algorithm().is_valid_decryption_context(mode, &mi) { + return Err(Unspecified); + } + mi + } + None => key.algorithm.new_decrypting_context(mode)?, + }; + + Ok(EncryptingKey { key, mode, context }) + } + + /// Returns the cipher algorithm. + pub fn algorithm(&self) -> &Algorithm { + self.key.algorithm() + } + + /// Returns the cipher operating mode. + pub fn mode(&self) -> OperatingMode { + self.mode + } + + /// Encrypts the data provided in `in_out` in-place. + /// Returns a references to the decrypted data. /// /// # Errors + /// * [`Unspecified`]: Returned if cipher mode requires input to be a multiple of the block length, + /// and `in_out.len()` is not. Otherwise returned if encryption fails. /// - /// * [`Unspecified`]: Returned if the data fails to be decrypted. - /// - #[allow(unused_mut)] - pub fn decrypt<'a>( - &self, - iv: NonceIV, - in_out: &'a mut [u8], - ) -> Result<&'a mut [u8], Unspecified> { - decrypt(self.algorithm, &self.key, iv, in_out) + pub fn encrypt(self, in_out: &mut [u8]) -> Result { + let block_len = self.algorithm().block_len(); + + match self.mode { + OperatingMode::CTR => {} + _ => { + if (in_out.len() % block_len) != 0 { + return Err(Unspecified); + } + } + } + + match self.mode { + OperatingMode::CBC => match self.key.algorithm().id() { + AlgorithmId::Aes128 | AlgorithmId::Aes256 => { + encrypt_aes_cbc_mode(self.key, self.context, in_out) + } + }, + OperatingMode::CTR => match self.key.algorithm().id() { + AlgorithmId::Aes128 | AlgorithmId::Aes256 => { + encrypt_aes_ctr_mode(self.key, self.context, in_out) + } + }, + } } } -fn encrypt( - algorithm: &'static Algorithm, - key: &SymmetricCipherKey, - iv: NonceIV, - in_out: &mut InOut, -) -> Result -where - InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, -{ - algorithm.prepare_for_encrypt(in_out)?; - - let in_out = in_out.as_mut(); - - let mut ivec = [0u8; Algorithm::max_iv_len()]; - ivec.copy_from_slice(iv.as_ref()); - - let mut buf = [0u8; Algorithm::max_block_len()]; - - // This works b/c we currently only support AES keys - let aes_key = match key { - SymmetricCipherKey::Aes128 { enc_key, .. } | SymmetricCipherKey::Aes256 { enc_key, .. } => { - Ok(enc_key) +/// A cipher decryption key that does not perform block padding. +pub struct DecryptingKey { + key: UnboundCipherKey, + mode: OperatingMode, + mode_input: DecryptionContext, +} + +impl DecryptingKey { + /// Constructs a cipher decrypting key operating in counter (CTR) mode using the provided key and context. + pub fn ctr( + key: UnboundCipherKey, + context: DecryptionContext, + ) -> Result { + DecryptingKey::new(key, OperatingMode::CTR, context) + } + + fn new( + key: UnboundCipherKey, + mode: OperatingMode, + mode_input: DecryptionContext, + ) -> Result { + if !key + .algorithm() + .is_valid_decryption_context(mode, &mode_input) + { + return Err(Unspecified); } - SymmetricCipherKey::ChaCha20 { .. } => Err(Unspecified), - }?; - match algorithm { - Algorithm::Aes128Ctr(..) | Algorithm::Aes256Ctr(..) => { - aes_ctr128_encrypt(aes_key, &mut ivec, &mut buf, in_out); + Ok(DecryptingKey { + key, + mode, + mode_input, + }) + } + + /// Returns the cipher algorithm. + pub fn algorithm(&self) -> &Algorithm { + self.key.algorithm() + } + + /// Returns the cipher operating mode. + pub fn mode(&self) -> OperatingMode { + self.mode + } + + /// Decrypts the data provided in `in_out` in-place. + /// Returns a references to the decrypted data. + /// + /// # Errors + /// * [`Unspecified`]: Returned if cipher mode requires input to be a multiple of the block length, + /// and `in_out.len()` is not. Otherwise returned if decryption fails. + /// + pub fn decrypt<'a>(self, in_out: &'a mut [u8]) -> Result<&'a mut [u8], Unspecified> { + let block_len = self.algorithm().block_len(); + + match self.mode { + OperatingMode::CTR => {} + _ => { + if (in_out.len() % block_len) != 0 { + return Err(Unspecified); + } + } } - Algorithm::Aes128CbcPkcs7Padding(..) | Algorithm::Aes256CbcPkcs7Padding(..) => { - aes_cbc_encrypt(aes_key, &mut ivec, in_out); + + match self.mode { + OperatingMode::CBC => match self.key.algorithm().id() { + AlgorithmId::Aes128 | AlgorithmId::Aes256 => { + decrypt_aes_cbc_mode(self.key, self.mode_input, in_out).map(|_| in_out) + } + }, + OperatingMode::CTR => match self.key.algorithm().id() { + AlgorithmId::Aes128 | AlgorithmId::Aes256 => { + decrypt_aes_ctr_mode(self.key, self.mode_input, in_out).map(|_| in_out) + } + }, } } - Ok(iv) +} + +impl TryFrom for DecryptingKey { + type Error = Unspecified; + + fn try_from(value: PaddedBlockDecryptingKey) -> Result { + DecryptingKey::new(value.key, value.mode, value.mode_input) + } +} + +fn encrypt_aes_ctr_mode( + key: UnboundCipherKey, + context: DecryptionContext, + in_out: &mut [u8], +) -> Result { + let key = match &key.key { + SymmetricCipherKey::Aes128 { enc_key, .. } => enc_key, + SymmetricCipherKey::Aes256 { enc_key, .. } => enc_key, + _ => return Err(Unspecified), + }; + + let mut iv = { + let mut iv = [0u8; AES_IV_LEN]; + iv.copy_from_slice(match &context { + DecryptionContext::Iv128(iv) => iv.as_ref(), + _ => return Err(Unspecified), + }); + iv + }; + + let mut buffer = [0u8; AES_BLOCK_LEN]; + + aes_ctr128_encrypt(key, &mut iv, &mut buffer, in_out); + + Ok(context) +} + +fn decrypt_aes_ctr_mode( + key: UnboundCipherKey, + context: DecryptionContext, + in_out: &mut [u8], +) -> Result { + // it's the same in CTR, just providing a nice named wrapper to match + encrypt_aes_ctr_mode(key, context, in_out) +} + +fn encrypt_aes_cbc_mode( + key: UnboundCipherKey, + context: DecryptionContext, + in_out: &mut [u8], +) -> Result { + let key = match &key.key { + SymmetricCipherKey::Aes128 { enc_key, .. } => enc_key, + SymmetricCipherKey::Aes256 { enc_key, .. } => enc_key, + _ => return Err(Unspecified), + }; + + let mut iv = { + let mut iv = [0u8; AES_IV_LEN]; + iv.copy_from_slice(match &context { + DecryptionContext::Iv128(iv) => iv.as_ref(), + _ => return Err(Unspecified), + }); + iv + }; + + aes_cbc_encrypt(key, &mut iv, in_out); + + Ok(context) +} + +fn decrypt_aes_cbc_mode( + key: UnboundCipherKey, + context: DecryptionContext, + in_out: &mut [u8], +) -> Result { + let key = match &key.key { + SymmetricCipherKey::Aes128 { dec_key, .. } => dec_key, + SymmetricCipherKey::Aes256 { dec_key, .. } => dec_key, + _ => return Err(Unspecified), + }; + + let mut iv = { + let mut iv = [0u8; AES_IV_LEN]; + iv.copy_from_slice(match &context { + DecryptionContext::Iv128(iv) => iv.as_ref(), + _ => return Err(Unspecified), + }); + iv + }; + + aes_cbc_decrypt(key, &mut iv, in_out); + + Ok(context) } fn aes_ctr128_encrypt(key: &AES_KEY, iv: &mut [u8], block_buffer: &mut [u8], in_out: &mut [u8]) { @@ -548,64 +751,9 @@ fn aes_cbc_decrypt(key: &AES_KEY, iv: &mut [u8], in_out: &mut [u8]) { } } -fn decrypt<'a>( - algorithm: &'static Algorithm, - key: &SymmetricCipherKey, - mut iv: NonceIV, - in_out: &'a mut [u8], -) -> Result<&'a mut [u8], Unspecified> { - let iv = iv.as_mut(); - - match algorithm { - Algorithm::Aes128Ctr(v) => { - let key = match key { - SymmetricCipherKey::Aes128 { enc_key, .. } => enc_key, - _ => return Err(Unspecified), - }; - - let mut buf = [0u8; MAX_BLOCK_LEN]; - - assert!(buf.len() >= v.get_block_len()); - - aes_ctr128_encrypt(key, iv, &mut buf, in_out); - } - Algorithm::Aes256Ctr(v) => { - let key = match key { - SymmetricCipherKey::Aes256 { enc_key, .. } => enc_key, - _ => return Err(Unspecified), - }; - - let mut buf = [0u8; MAX_BLOCK_LEN]; - - assert!(buf.len() >= v.get_block_len()); - - aes_ctr128_encrypt(key, iv, &mut buf, in_out); - } - Algorithm::Aes128CbcPkcs7Padding(_) => { - let key = match key { - SymmetricCipherKey::Aes128 { dec_key, .. } => dec_key, - _ => return Err(Unspecified), - }; - - aes_cbc_decrypt(key, iv, in_out); - } - Algorithm::Aes256CbcPkcs7Padding(_) => { - let key = match key { - SymmetricCipherKey::Aes256 { dec_key, .. } => dec_key, - _ => return Err(Unspecified), - }; - aes_cbc_decrypt(key, iv, in_out); - } - } - - let in_out = algorithm.finalalize_decryption(in_out)?; - - Ok(in_out) -} - impl SymmetricCipherKey { pub(crate) fn aes128(key_bytes: &[u8]) -> Result { - if key_bytes.len() != 16 { + if key_bytes.len() != AES_128_KEY_LEN { return Err(Unspecified); } @@ -745,7 +893,12 @@ mod tests { assert_eq!(expected_result.as_slice(), result.as_ref()); } - fn helper_test_cipher_n_bytes(key: &[u8], alg: &'static Algorithm, n: usize) { + fn helper_test_cipher_n_bytes( + key: &[u8], + alg: &'static Algorithm, + mode: OperatingMode, + n: usize, + ) { let mut input: Vec = Vec::with_capacity(n); for i in 0..n { let byte: u8 = i.try_into().unwrap(); @@ -753,7 +906,7 @@ mod tests { } let cipher_key = UnboundCipherKey::new(alg, key).unwrap(); - let encrypting_key = EncryptingKey::new(cipher_key).unwrap(); + let encrypting_key = EncryptingKey::new(cipher_key, mode, None).unwrap(); let mut in_out = input.clone(); let decrypt_iv = encrypting_key.encrypt(&mut in_out).unwrap(); @@ -764,7 +917,40 @@ mod tests { } let cipher_key2 = UnboundCipherKey::new(alg, key).unwrap(); - let decrypting_key = DecryptingKey::new(cipher_key2, decrypt_iv); + let decrypting_key = DecryptingKey::new(cipher_key2, mode, decrypt_iv).unwrap(); + + let plaintext = decrypting_key.decrypt(&mut in_out).unwrap(); + assert_eq!(input.as_slice(), plaintext); + } + + fn helper_test_padded_cipher_n_bytes( + key: &[u8], + alg: &'static Algorithm, + mode: OperatingMode, + padding: PaddingStrategy, + n: usize, + ) { + let mut input: Vec = Vec::with_capacity(n); + for i in 0..n { + let byte: u8 = i.try_into().unwrap(); + input.push(byte); + } + + let cipher_key = UnboundCipherKey::new(alg, key).unwrap(); + let encrypting_key = + PaddedBlockEncryptingKey::new(cipher_key, mode, padding, None).unwrap(); + + let mut in_out = input.clone(); + let decrypt_iv = encrypting_key.encrypt(&mut in_out).unwrap(); + + if n > 5 { + // There's no more than a 1 in 2^48 chance that this will fail randomly + assert_ne!(input.as_slice(), in_out); + } + + let cipher_key2 = UnboundCipherKey::new(alg, key).unwrap(); + let decrypting_key = + PaddedBlockDecryptingKey::new(cipher_key2, mode, padding, decrypt_iv).unwrap(); let plaintext = decrypting_key.decrypt(&mut in_out).unwrap(); assert_eq!(input.as_slice(), plaintext); @@ -774,7 +960,13 @@ mod tests { fn test_aes_128_cbc() { let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); for i in 0..=50 { - helper_test_cipher_n_bytes(key.as_slice(), &AES_128_CBC_PKCS7_PADDING, i); + helper_test_padded_cipher_n_bytes( + key.as_slice(), + &AES_128, + OperatingMode::CBC, + PaddingStrategy::PKCS7, + i, + ); } } @@ -783,7 +975,13 @@ mod tests { let key = from_hex("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f").unwrap(); for i in 0..=50 { - helper_test_cipher_n_bytes(key.as_slice(), &AES_256_CBC_PKCS7_PADDING, i); + helper_test_padded_cipher_n_bytes( + key.as_slice(), + &AES_256, + OperatingMode::CBC, + PaddingStrategy::PKCS7, + i, + ); } } @@ -791,7 +989,7 @@ mod tests { fn test_aes_128_ctr() { let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); for i in 0..=50 { - helper_test_cipher_n_bytes(key.as_slice(), &AES_128_CTR, i); + helper_test_cipher_n_bytes(key.as_slice(), &AES_128, OperatingMode::CTR, i); } } @@ -800,12 +998,55 @@ mod tests { let key = from_hex("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f").unwrap(); for i in 0..=50 { - helper_test_cipher_n_bytes(key.as_slice(), &AES_256_CTR, i); + helper_test_cipher_n_bytes(key.as_slice(), &AES_256, OperatingMode::CTR, i); } } - macro_rules! cipher_test_vector { - ($name:ident, $cipher:expr, $key:literal, $iv: literal, $plaintext:literal, $ciphertext:literal) => { + macro_rules! padded_cipher_kat { + ($name:ident, $alg:expr, $mode:expr, $padding:expr, $key:literal, $iv: literal, $plaintext:literal, $ciphertext:literal) => { + #[test] + fn $name() { + let key = from_hex($key).unwrap(); + let input = from_hex($plaintext).unwrap(); + let expected_ciphertext = from_hex($ciphertext).unwrap(); + let mut iv = from_hex($iv).unwrap(); + let iv = { + let slice = iv.as_mut_slice(); + let mut iv = [0u8; $iv.len() / 2]; + { + let x = iv.as_mut_slice(); + x.copy_from_slice(slice); + } + iv + }; + + let dc = DecryptionContext::Iv128(FixedLength::from(iv)); + + let alg = $alg; + + let unbound_key = UnboundCipherKey::new(alg, &key).unwrap(); + + let encrypting_key = + PaddedBlockEncryptingKey::new(unbound_key, $mode, $padding, Some(dc)).unwrap(); + + let mut in_out = input.clone(); + + let context = encrypting_key.encrypt(&mut in_out).unwrap(); + + assert_eq!(expected_ciphertext, in_out); + + let unbound_key2 = UnboundCipherKey::new(alg, &key).unwrap(); + let decrypting_key = + PaddedBlockDecryptingKey::new(unbound_key2, $mode, $padding, context).unwrap(); + + let plaintext = decrypting_key.decrypt(&mut in_out).unwrap(); + assert_eq!(input.as_slice(), plaintext); + } + }; + } + + macro_rules! cipher_kat { + ($name:ident, $alg:expr, $mode:expr, $key:literal, $iv: literal, $plaintext:literal, $ciphertext:literal) => { #[test] fn $name() { let key = from_hex($key).unwrap(); @@ -822,17 +1063,22 @@ mod tests { iv }; - let alg = $cipher; - let encrypting_key = LessSafeCipherKey::new(alg, &key).unwrap(); + let dc = DecryptionContext::Iv128(FixedLength::from(iv)); + + let alg = $alg; + + let unbound_key = UnboundCipherKey::new(alg, &key).unwrap(); + + let encrypting_key = EncryptingKey::new(unbound_key, $mode, Some(dc)).unwrap(); let mut in_out = input.clone(); - let decrypt_iv = encrypting_key - .encrypt(NonceIV::try_from(iv).unwrap(), &mut in_out) - .unwrap(); + + let context = encrypting_key.encrypt(&mut in_out).unwrap(); + assert_eq!(expected_ciphertext, in_out); - let cipher_key2 = UnboundCipherKey::new(alg, &key).unwrap(); - let decrypting_key = DecryptingKey::new(cipher_key2, decrypt_iv); + let unbound_key2 = UnboundCipherKey::new(alg, &key).unwrap(); + let decrypting_key = DecryptingKey::new(unbound_key2, $mode, context).unwrap(); let plaintext = decrypting_key.decrypt(&mut in_out).unwrap(); assert_eq!(input.as_slice(), plaintext); @@ -840,90 +1086,106 @@ mod tests { }; } - cipher_test_vector!( + padded_cipher_kat!( test_iv_aes_128_cbc_16_bytes, - &AES_128_CBC_PKCS7_PADDING, + &AES_128, + OperatingMode::CBC, + PaddingStrategy::PKCS7, "000102030405060708090a0b0c0d0e0f", "00000000000000000000000000000000", "00112233445566778899aabbccddeeff", "69c4e0d86a7b0430d8cdb78070b4c55a9e978e6d16b086570ef794ef97984232" ); - cipher_test_vector!( + padded_cipher_kat!( test_iv_aes_256_cbc_15_bytes, - &AES_256_CBC_PKCS7_PADDING, + &AES_256, + OperatingMode::CBC, + PaddingStrategy::PKCS7, "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "00000000000000000000000000000000", "00112233445566778899aabbccddee", "2ddfb635a651a43f582997966840ca0c" ); - cipher_test_vector!( + cipher_kat!( test_iv_aes_128_ctr_16_bytes, - &AES_128_CTR, + &AES_128, + OperatingMode::CTR, "000102030405060708090a0b0c0d0e0f", "00000000000000000000000000000000", "00112233445566778899aabbccddeeff", "c6b01904c3da3df5e7d62bd96d153686" ); - cipher_test_vector!( + cipher_kat!( test_iv_aes_256_ctr_15_bytes, - &AES_256_CTR, + &AES_256, + OperatingMode::CTR, "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "00000000000000000000000000000000", "00112233445566778899aabbccddee", "f28122856e1cf9a7216a30d111f399" ); - cipher_test_vector!( + cipher_kat!( test_openssl_aes_128_ctr_15_bytes, - &AES_128_CTR, + &AES_128, + OperatingMode::CTR, "244828580821c1652582c76e34d299f5", "093145d5af233f46072a5eb5adc11aa1", "3ee38cec171e6cf466bf0df98aa0e1", "bd7d928f60e3422d96b3f8cd614eb2" ); - cipher_test_vector!( + cipher_kat!( test_openssl_aes_256_ctr_15_bytes, - &AES_256_CTR, + &AES_256, + OperatingMode::CTR, "0857db8240ea459bdf660b4cced66d1f2d3734ff2de7b81e92740e65e7cc6a1d", "f028ecb053f801102d11fccc9d303a27", "eca7285d19f3c20e295378460e8729", "b5098e5e788de6ac2f2098eb2fc6f8" ); - cipher_test_vector!( + padded_cipher_kat!( test_openssl_aes_128_cbc_15_bytes, - &AES_128_CBC_PKCS7_PADDING, + &AES_128, + OperatingMode::CBC, + PaddingStrategy::PKCS7, "053304bb3899e1d99db9d29343ea782d", "b5313560244a4822c46c2a0c9d0cf7fd", "a3e4c990356c01f320043c3d8d6f43", "ad96993f248bd6a29760ec7ccda95ee1" ); - cipher_test_vector!( + padded_cipher_kat!( test_openssl_aes_128_cbc_16_bytes, - &AES_128_CBC_PKCS7_PADDING, + &AES_128, + OperatingMode::CBC, + PaddingStrategy::PKCS7, "95af71f1c63e4a1d0b0b1a27fb978283", "89e40797dca70197ff87d3dbb0ef2802", "aece7b5e3c3df1ffc9802d2dfe296dc7", "301b5dab49fb11e919d0d39970d06739301919743304f23f3cbc67d28564b25b" ); - cipher_test_vector!( + padded_cipher_kat!( test_openssl_aes_256_cbc_15_bytes, - &AES_256_CBC_PKCS7_PADDING, + &AES_256, + OperatingMode::CBC, + PaddingStrategy::PKCS7, "d369e03e9752784917cc7bac1db7399598d9555e691861d9dd7b3292a693ef57", "1399bb66b2f6ad99a7f064140eaaa885", "7385f5784b85bf0a97768ddd896d6d", "4351082bac9b4593ae8848cc9dfb5a01" ); - cipher_test_vector!( + padded_cipher_kat!( test_openssl_aes_256_cbc_16_bytes, - &AES_256_CBC_PKCS7_PADDING, + &AES_256, + OperatingMode::CBC, + PaddingStrategy::PKCS7, "d4a8206dcae01242f9db79a4ecfe277d0f7bb8ccbafd8f9809adb39f35aa9b41", "24f6076548fb9d93c8f7ed9f6e661ef9", "a39c1fdf77ea3e1f18178c0ec237c70a", diff --git a/aws-lc-rs/src/iv.rs b/aws-lc-rs/src/iv.rs index b7a343cbb40..f29ab1a131d 100644 --- a/aws-lc-rs/src/iv.rs +++ b/aws-lc-rs/src/iv.rs @@ -9,65 +9,6 @@ use crate::error::Unspecified; use crate::{error, rand}; -/// A cryptographic nonce. -pub enum NonceIV { - /// 128-bit (16 byte) nonce. - Size128(FixedLength<{ 128 / 8 }>), -} - -impl NonceIV { - /// Constructs a [`NonceIV`] with the given value, assuming that the value is - /// unique for the lifetime of the key it is being used with. - /// - /// # Errors - /// `error::Unspecified` when byte slice length is not a supported length. - #[inline] - pub fn try_assume_unique_for_key(value: &[u8]) -> Result { - match value.len() { - 16 => Ok(NonceIV::Size128(FixedLength::<16>::try_from(value)?)), - _ => Err(error::Unspecified), - } - } - - /// Returns the size of the nonce in bytes. - #[allow(clippy::must_use_candidate)] - pub fn size(&self) -> usize { - match self { - NonceIV::Size128(v) => v.size(), - } - } -} - -impl AsRef<[u8]> for NonceIV { - fn as_ref(&self) -> &[u8] { - match self { - NonceIV::Size128(fv) => fv.as_ref(), - } - } -} - -impl AsMut<[u8]> for NonceIV { - fn as_mut(&mut self) -> &mut [u8] { - match self { - NonceIV::Size128(fv) => fv.as_mut(), - } - } -} - -impl From<[u8; 16]> for NonceIV { - fn from(value: [u8; 16]) -> Self { - NonceIV::Size128(FixedLength::<16>(value)) - } -} - -impl TryFrom<&[u8]> for NonceIV { - type Error = Unspecified; - - fn try_from(value: &[u8]) -> Result { - Ok(NonceIV::Size128(FixedLength::<16>::try_from(value)?)) - } -} - /// An initalization vector that must be unique for the lifetime of the associated key /// it is used with. pub struct FixedLength([u8; L]); From 40c80288a415a5da3cd1fd184aca551d559e4d6a Mon Sep 17 00:00:00 2001 From: Sean McGrail Date: Thu, 25 May 2023 19:54:03 -0700 Subject: [PATCH 26/54] Clippy Cleanup --- aws-lc-rs/benches/cipher_benchmark.rs | 123 ++++++++++++++++++++------ aws-lc-rs/src/cipher.rs | 117 ++++++++++++++++++------ aws-lc-rs/src/iv.rs | 6 +- 3 files changed, 191 insertions(+), 55 deletions(-) diff --git a/aws-lc-rs/benches/cipher_benchmark.rs b/aws-lc-rs/benches/cipher_benchmark.rs index 070423e11b6..75108e63e1e 100644 --- a/aws-lc-rs/benches/cipher_benchmark.rs +++ b/aws-lc-rs/benches/cipher_benchmark.rs @@ -1,19 +1,28 @@ -use aws_lc_rs::{ - cipher::{ - LessSafeCipherKey, AES_128_CBC_PKCS7_PADDING, AES_128_CTR, AES_256_CBC_PKCS7_PADDING, - AES_256_CTR, - }, - iv::NonceIV, - test, test_file, +use aws_lc_rs::cipher::{ + DecryptingKey, DecryptionContext, EncryptingKey, OperatingMode, PaddedBlockDecryptingKey, + PaddedBlockEncryptingKey, UnboundCipherKey, AES_128, AES_256, }; +use aws_lc_rs::{test, test_file}; use criterion::{criterion_group, criterion_main, Criterion}; use openssl::symm::Cipher; -macro_rules! benchmark { - ($fn:ident, $test:literal, $file:literal, $awslc:expr, $openssl:expr) => { +macro_rules! openssl_bench { + ($group:ident, $openssl: expr, $key:ident, $iv:ident, $data:ident) => { + $group.bench_function("OpenSSL", |b| { + b.iter(|| { + use openssl::symm::{decrypt, encrypt}; + let data = encrypt($openssl, &$key, Some(&$iv), &$data).unwrap(); + let _ = decrypt($openssl, &$key, Some(&$iv), data.as_ref()).unwrap(); + }) + }); + }; +} + +macro_rules! benchmark_padded { + ($fn:ident, $test:literal, $file:literal, $awslc:expr, $mode:expr, $openssl:expr) => { fn $fn(c: &mut Criterion) { test::run(test_file!($file), |_section, test_case| { - let key = test_case.consume_bytes("KEY"); + let key_bytes = test_case.consume_bytes("KEY"); let iv = test_case.consume_bytes("IV"); let data = test_case.consume_bytes("IN"); @@ -21,57 +30,119 @@ macro_rules! benchmark { group.bench_function("AWS-LC", |b| { b.iter(|| { - let key = LessSafeCipherKey::new($awslc, &key).unwrap(); - let iv: NonceIV = iv.as_slice().try_into().unwrap(); + let key = UnboundCipherKey::new($awslc, &key_bytes).unwrap(); + let iv: DecryptionContext = + DecryptionContext::Iv128(iv.as_slice().try_into().unwrap()); + + let encrypt_key = match $mode { + OperatingMode::CBC => { + PaddedBlockEncryptingKey::less_safe_cbc_pkcs7(key, iv) + } + _ => unreachable!(), + } + .unwrap(); + let mut in_out = Vec::from(data.as_slice()); - let iv = key.encrypt(iv, &mut in_out).unwrap(); - let _ = key.decrypt(iv, &mut in_out).unwrap(); + let context = encrypt_key.encrypt(&mut in_out).unwrap(); + + let key = UnboundCipherKey::new($awslc, &key_bytes).unwrap(); + + let decrypt_key = match $mode { + OperatingMode::CBC => PaddedBlockDecryptingKey::cbc_pkcs7(key, context), + _ => unreachable!(), + } + .unwrap(); + + let _ = decrypt_key.decrypt(&mut in_out).unwrap(); }) }); - group.bench_function("OpenSSL", |b| { + openssl_bench!(group, $openssl, key_bytes, iv, data); + + Ok(()) + }); + } + }; +} + +macro_rules! benchmark_unpadded { + ($fn:ident, $test:literal, $file:literal, $awslc:expr, $mode:expr, $openssl:expr) => { + fn $fn(c: &mut Criterion) { + test::run(test_file!($file), |_section, test_case| { + let key_bytes = test_case.consume_bytes("KEY"); + let iv = test_case.consume_bytes("IV"); + let data = test_case.consume_bytes("IN"); + + let mut group = c.benchmark_group(format!("{}-{}-bytes", $test, data.len())); + + group.bench_function("AWS-LC", |b| { b.iter(|| { - use openssl::symm::{decrypt, encrypt}; - let data = encrypt($openssl, &key, Some(&iv), &data).unwrap(); - let _ = decrypt($openssl, &key, Some(&iv), data.as_ref()).unwrap(); + let key = UnboundCipherKey::new($awslc, &key_bytes).unwrap(); + let iv: DecryptionContext = + DecryptionContext::Iv128(iv.as_slice().try_into().unwrap()); + + let encrypt_key = match $mode { + OperatingMode::CTR => EncryptingKey::less_safe_ctr(key, iv), + _ => unreachable!(), + } + .unwrap(); + + let mut in_out = Vec::from(data.as_slice()); + let context = encrypt_key.encrypt(&mut in_out).unwrap(); + + let key = UnboundCipherKey::new($awslc, &key_bytes).unwrap(); + + let decrypt_key = match $mode { + OperatingMode::CTR => DecryptingKey::ctr(key, context), + _ => unreachable!(), + } + .unwrap(); + + let _ = decrypt_key.decrypt(&mut in_out).unwrap(); }) }); + openssl_bench!(group, $openssl, key_bytes, iv, data); + Ok(()) }); } }; } -benchmark!( +benchmark_unpadded!( test_aes_128_ctr, "AES-128-CTR", "data/cipher_aes_128_ctr.txt", - &AES_128_CTR, + &AES_128, + OperatingMode::CTR, Cipher::aes_128_ctr() ); -benchmark!( +benchmark_unpadded!( test_aes_256_ctr, "AES-256-CTR", "data/cipher_aes_256_ctr.txt", - &AES_256_CTR, + &AES_256, + OperatingMode::CTR, Cipher::aes_256_ctr() ); -benchmark!( +benchmark_padded!( test_aes_128_cbc, "AES-128-CBC", "data/cipher_aes_128_cbc.txt", - &AES_128_CBC_PKCS7_PADDING, + &AES_128, + OperatingMode::CBC, Cipher::aes_128_cbc() ); -benchmark!( +benchmark_padded!( test_aes_256_cbc, "AES-256-CBC", "data/cipher_aes_256_cbc.txt", - &AES_256_CBC_PKCS7_PADDING, + &AES_256, + OperatingMode::CBC, Cipher::aes_256_cbc() ); diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index c2eb9aa7332..aeb6295c911 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -18,8 +18,34 @@ //! the algorithms provided in [`aead`](crate::aead). //! //! # Examples +//! +//! ## AES-128 CBC Mode Encryption +//! +//! ``` +//! use aws_lc_rs::cipher::{ +//! PaddedBlockDecryptingKey, PaddedBlockEncryptingKey, UnboundCipherKey, AES_128, +//! }; +//! +//! let mut plaintext = Vec::from("This is a secret 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).unwrap(); +//! let encrypting_key = PaddedBlockEncryptingKey::cbc_pkcs7(key).unwrap(); +//! let context = encrypting_key.encrypt(&mut plaintext).unwrap(); +//! +//! let key = UnboundCipherKey::new(&AES_128, key_bytes).unwrap(); +//! let decrypting_key = PaddedBlockDecryptingKey::cbc_pkcs7(key, context).unwrap(); +//! let plaintext = decrypting_key.decrypt(&mut plaintext).unwrap(); +//! ``` +//! +//! ## AES-128 CTR Mode Encryption +//! //! ``` -//! use aws_lc_rs::cipher::{UnboundCipherKey, DecryptingKey, EncryptingKey, AES_128_CTR}; +//! use aws_lc_rs::cipher::{DecryptingKey, EncryptingKey, UnboundCipherKey, AES_128}; //! //! let mut plaintext = Vec::from("This is a secret message!"); //! @@ -28,12 +54,12 @@ //! 0xd1, //! ]; //! -//! let key = UnboundCipherKey::new(&AES_128_CTR, key_bytes).unwrap(); -//! let encrypting_key = EncryptingKey::new(key).unwrap(); -//! let iv = encrypting_key.encrypt(&mut plaintext).unwrap(); +//! let key = UnboundCipherKey::new(&AES_128, key_bytes).unwrap(); +//! let encrypting_key = EncryptingKey::ctr(key).unwrap(); +//! let context = encrypting_key.encrypt(&mut plaintext).unwrap(); //! -//! let key = UnboundCipherKey::new(&AES_128_CTR, key_bytes).unwrap(); -//! let decrypting_key = DecryptingKey::new(key, iv); +//! let key = UnboundCipherKey::new(&AES_128, key_bytes).unwrap(); +//! let decrypting_key = DecryptingKey::ctr(key, context).unwrap(); //! let plaintext = decrypting_key.decrypt(&mut plaintext).unwrap(); //! ``` //! @@ -251,10 +277,9 @@ impl Algorithm { fn is_valid_decryption_context(&self, mode: OperatingMode, input: &DecryptionContext) -> bool { match self.id { AlgorithmId::Aes128 | AlgorithmId::Aes256 => match mode { - OperatingMode::CBC | OperatingMode::CTR => match input { - DecryptionContext::Iv128(_) => true, - _ => false, - }, + OperatingMode::CBC | OperatingMode::CTR => { + matches!(input, DecryptionContext::Iv128(_)) + } }, } } @@ -299,6 +324,10 @@ pub struct PaddedBlockEncryptingKey { impl PaddedBlockEncryptingKey { /// Constructs a new `PaddedBlockEncryptingKey` cipher with chaining block cipher (CBC) mode. /// Plaintext data is padded following the PKCS#7 scheme. + /// + /// # Errors + /// * [`Unspecified`]: Returned if there is an error cosntruct a `PaddedBlockEncryptingKey`. + /// pub fn cbc_pkcs7(key: UnboundCipherKey) -> Result { PaddedBlockEncryptingKey::new(key, OperatingMode::CBC, PaddingStrategy::PKCS7, None) } @@ -306,6 +335,10 @@ impl PaddedBlockEncryptingKey { /// Constructs a new `PaddedBlockEncryptingKey` cipher with chaining block cipher (CBC) mode. /// The users provided context will be used for the CBC initalization-vector. /// Plaintext data is padded following the PKCS#7 scheme. + /// + /// # Errors + /// * [`Unspecified`]: Returned if there is an error constructing a `PaddedBlockEncryptingKey`. + /// pub fn less_safe_cbc_pkcs7( key: UnboundCipherKey, context: DecryptionContext, @@ -343,16 +376,19 @@ impl PaddedBlockEncryptingKey { } /// Returns the cipher algorithm. + #[must_use] pub fn algorithm(&self) -> &Algorithm { self.key.algorithm() } /// Returns the cipher operating mode. + #[must_use] pub fn mode(&self) -> OperatingMode { self.mode } /// Returns the cipher padding strategy. + #[must_use] pub fn padding(&self) -> PaddingStrategy { self.padding } @@ -392,6 +428,10 @@ pub struct PaddedBlockDecryptingKey { impl PaddedBlockDecryptingKey { /// Constructs a new `PaddedBlockDecryptingKey` cipher with chaining block cipher (CBC) mode. /// Decrypted data is unpadded following the PKCS#7 scheme. + /// + /// # Errors + /// * [`Unspecified`]: Returned if there is an error constructing the `PaddedBlockDecryptingKey`. + /// pub fn cbc_pkcs7( key: UnboundCipherKey, mode_input: DecryptionContext, @@ -421,16 +461,19 @@ impl PaddedBlockDecryptingKey { } /// Returns the cipher algorithm. + #[must_use] pub fn algorithm(&self) -> &Algorithm { self.key.algorithm() } /// Returns the cipher operating mode. + #[must_use] pub fn mode(&self) -> OperatingMode { self.mode } /// Returns the cipher padding strategy. + #[must_use] pub fn padding(&self) -> PaddingStrategy { self.padding } @@ -441,7 +484,7 @@ impl PaddedBlockDecryptingKey { /// # Errors /// * [`Unspecified`]: Returned if decryption fails. /// - pub fn decrypt<'a>(self, in_out: &'a mut [u8]) -> Result<&'a mut [u8], Unspecified> { + pub fn decrypt(self, in_out: &mut [u8]) -> Result<&mut [u8], Unspecified> { let block_len = self.algorithm().block_len(); let padding = self.padding; let mut in_out = TryInto::::try_into(self)?.decrypt(in_out)?; @@ -459,12 +502,20 @@ pub struct EncryptingKey { impl EncryptingKey { /// Constructs an `EncryptingKey` operating in counter (CTR) mode using the provided key. + /// + /// # Errors + /// * [`Unspecified`]: Returned if there is an error construct the `EncryptingKey`. + /// pub fn ctr(key: UnboundCipherKey) -> Result { EncryptingKey::new(key, OperatingMode::CTR, None) } /// Constructs an `EncryptingKey` operating in counter (CTR) mode using the provided key. /// The users provided context will be used for the CTR mode initalization-vector. + /// + /// # Errors + /// * [`Unspecified`]: Returned if there is an error creating the `EncryptingKey`. + /// pub fn less_safe_ctr( key: UnboundCipherKey, context: DecryptionContext, @@ -491,11 +542,13 @@ impl EncryptingKey { } /// Returns the cipher algorithm. + #[must_use] pub fn algorithm(&self) -> &Algorithm { self.key.algorithm() } /// Returns the cipher operating mode. + #[must_use] pub fn mode(&self) -> OperatingMode { self.mode } @@ -522,12 +575,12 @@ impl EncryptingKey { match self.mode { OperatingMode::CBC => match self.key.algorithm().id() { AlgorithmId::Aes128 | AlgorithmId::Aes256 => { - encrypt_aes_cbc_mode(self.key, self.context, in_out) + encrypt_aes_cbc_mode(&self.key, self.context, in_out) } }, OperatingMode::CTR => match self.key.algorithm().id() { AlgorithmId::Aes128 | AlgorithmId::Aes256 => { - encrypt_aes_ctr_mode(self.key, self.context, in_out) + encrypt_aes_ctr_mode(&self.key, self.context, in_out) } }, } @@ -543,6 +596,10 @@ pub struct DecryptingKey { impl DecryptingKey { /// Constructs a cipher decrypting key operating in counter (CTR) mode using the provided key and context. + /// + /// # Errors + /// * [`Unspecified`]: Returned if there is an error during decryption. + /// pub fn ctr( key: UnboundCipherKey, context: DecryptionContext, @@ -570,11 +627,13 @@ impl DecryptingKey { } /// Returns the cipher algorithm. + #[must_use] pub fn algorithm(&self) -> &Algorithm { self.key.algorithm() } /// Returns the cipher operating mode. + #[must_use] pub fn mode(&self) -> OperatingMode { self.mode } @@ -586,7 +645,7 @@ impl DecryptingKey { /// * [`Unspecified`]: Returned if cipher mode requires input to be a multiple of the block length, /// and `in_out.len()` is not. Otherwise returned if decryption fails. /// - pub fn decrypt<'a>(self, in_out: &'a mut [u8]) -> Result<&'a mut [u8], Unspecified> { + pub fn decrypt(self, in_out: &mut [u8]) -> Result<&mut [u8], Unspecified> { let block_len = self.algorithm().block_len(); match self.mode { @@ -601,12 +660,12 @@ impl DecryptingKey { match self.mode { OperatingMode::CBC => match self.key.algorithm().id() { AlgorithmId::Aes128 | AlgorithmId::Aes256 => { - decrypt_aes_cbc_mode(self.key, self.mode_input, in_out).map(|_| in_out) + decrypt_aes_cbc_mode(&self.key, self.mode_input, in_out).map(|_| in_out) } }, OperatingMode::CTR => match self.key.algorithm().id() { AlgorithmId::Aes128 | AlgorithmId::Aes256 => { - decrypt_aes_ctr_mode(self.key, self.mode_input, in_out).map(|_| in_out) + decrypt_aes_ctr_mode(&self.key, self.mode_input, in_out).map(|_| in_out) } }, } @@ -622,13 +681,15 @@ impl TryFrom for DecryptingKey { } fn encrypt_aes_ctr_mode( - key: UnboundCipherKey, + key: &UnboundCipherKey, context: DecryptionContext, in_out: &mut [u8], ) -> Result { + #[allow(clippy::match_wildcard_for_single_variants)] let key = match &key.key { - SymmetricCipherKey::Aes128 { enc_key, .. } => enc_key, - SymmetricCipherKey::Aes256 { enc_key, .. } => enc_key, + SymmetricCipherKey::Aes128 { enc_key, .. } | SymmetricCipherKey::Aes256 { enc_key, .. } => { + enc_key + } _ => return Err(Unspecified), }; @@ -649,7 +710,7 @@ fn encrypt_aes_ctr_mode( } fn decrypt_aes_ctr_mode( - key: UnboundCipherKey, + key: &UnboundCipherKey, context: DecryptionContext, in_out: &mut [u8], ) -> Result { @@ -658,13 +719,15 @@ fn decrypt_aes_ctr_mode( } fn encrypt_aes_cbc_mode( - key: UnboundCipherKey, + key: &UnboundCipherKey, context: DecryptionContext, in_out: &mut [u8], ) -> Result { + #[allow(clippy::match_wildcard_for_single_variants)] let key = match &key.key { - SymmetricCipherKey::Aes128 { enc_key, .. } => enc_key, - SymmetricCipherKey::Aes256 { enc_key, .. } => enc_key, + SymmetricCipherKey::Aes128 { enc_key, .. } | SymmetricCipherKey::Aes256 { enc_key, .. } => { + enc_key + } _ => return Err(Unspecified), }; @@ -683,13 +746,15 @@ fn encrypt_aes_cbc_mode( } fn decrypt_aes_cbc_mode( - key: UnboundCipherKey, + key: &UnboundCipherKey, context: DecryptionContext, in_out: &mut [u8], ) -> Result { + #[allow(clippy::match_wildcard_for_single_variants)] let key = match &key.key { - SymmetricCipherKey::Aes128 { dec_key, .. } => dec_key, - SymmetricCipherKey::Aes256 { dec_key, .. } => dec_key, + SymmetricCipherKey::Aes128 { dec_key, .. } | SymmetricCipherKey::Aes256 { dec_key, .. } => { + dec_key + } _ => return Err(Unspecified), }; diff --git a/aws-lc-rs/src/iv.rs b/aws-lc-rs/src/iv.rs index f29ab1a131d..14953a0df7a 100644 --- a/aws-lc-rs/src/iv.rs +++ b/aws-lc-rs/src/iv.rs @@ -14,7 +14,7 @@ use crate::{error, rand}; pub struct FixedLength([u8; L]); impl FixedLength { - /// Constructs a [`NonceIV`] with the given value, assuming that the value is + /// Constructs a [`FixedLength`] with the given value, assuming that the value is /// unique for the lifetime of the key it is being used with. /// /// Fails if `value` isn't `L` bytes long. @@ -26,7 +26,7 @@ impl FixedLength { Ok(Self::assume_unique_for_key(*value)) } - /// Constructs a [`NonceIV`] with the given value, assuming that the value is + /// Constructs a [`FixedLength`] with the given value, assuming that the value is /// unique for the lifetime of the key it is being used with. #[inline] #[must_use] @@ -40,7 +40,7 @@ impl FixedLength { L } - /// Constructs a new [`NonceIV`] from pseudo-random bytes. + /// Constructs a new [`FixedLength`] from pseudo-random bytes. /// /// # Errors /// From 5a17f38e6fd901f65fdba4bf2ab230d1ebcaabe8 Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Tue, 6 Jun 2023 14:48:15 -0400 Subject: [PATCH 27/54] Minor cipher refactoring; Add integ tests (#141) --- Cargo.toml | 1 + aws-lc-rs/benches/cipher_benchmark.rs | 10 +- aws-lc-rs/src/aead.rs | 1 + aws-lc-rs/src/aead/aes_gcm.rs | 2 +- aws-lc-rs/src/aead/chacha.rs | 2 +- aws-lc-rs/src/aead/key_inner.rs | 2 +- aws-lc-rs/src/aead/quic.rs | 9 +- aws-lc-rs/src/cipher.rs | 311 +++++-------------------- aws-lc-rs/src/cipher/key.rs | 197 ++++++++++++++++ aws-lc-rs/src/hkdf.rs | 2 + aws-lc-rs/src/hmac.rs | 1 + aws-lc-rs/tests/cipher_test.rs | 319 ++++++++++++++++++++++++++ 12 files changed, 595 insertions(+), 262 deletions(-) create mode 100644 aws-lc-rs/src/cipher/key.rs create mode 100644 aws-lc-rs/tests/cipher_test.rs diff --git a/Cargo.toml b/Cargo.toml index 5d0f69e8989..1577b22e1bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ members = [ "aws-lc-sys", "aws-lc-fips-sys" ] +resolver = "2" [profile.bench] lto = true diff --git a/aws-lc-rs/benches/cipher_benchmark.rs b/aws-lc-rs/benches/cipher_benchmark.rs index 75108e63e1e..ee9d1075c04 100644 --- a/aws-lc-rs/benches/cipher_benchmark.rs +++ b/aws-lc-rs/benches/cipher_benchmark.rs @@ -1,5 +1,5 @@ use aws_lc_rs::cipher::{ - DecryptingKey, DecryptionContext, EncryptingKey, OperatingMode, PaddedBlockDecryptingKey, + CipherContext, DecryptingKey, EncryptingKey, OperatingMode, PaddedBlockDecryptingKey, PaddedBlockEncryptingKey, UnboundCipherKey, AES_128, AES_256, }; use aws_lc_rs::{test, test_file}; @@ -31,8 +31,8 @@ macro_rules! benchmark_padded { group.bench_function("AWS-LC", |b| { b.iter(|| { let key = UnboundCipherKey::new($awslc, &key_bytes).unwrap(); - let iv: DecryptionContext = - DecryptionContext::Iv128(iv.as_slice().try_into().unwrap()); + let iv: CipherContext = + CipherContext::Iv128(iv.as_slice().try_into().unwrap()); let encrypt_key = match $mode { OperatingMode::CBC => { @@ -78,8 +78,8 @@ macro_rules! benchmark_unpadded { group.bench_function("AWS-LC", |b| { b.iter(|| { let key = UnboundCipherKey::new($awslc, &key_bytes).unwrap(); - let iv: DecryptionContext = - DecryptionContext::Iv128(iv.as_slice().try_into().unwrap()); + let iv: CipherContext = + CipherContext::Iv128(iv.as_slice().try_into().unwrap()); let encrypt_key = match $mode { OperatingMode::CTR => EncryptingKey::less_safe_ctr(key, iv), diff --git a/aws-lc-rs/src/aead.rs b/aws-lc-rs/src/aead.rs index 9bf8df335d1..b6a866d2247 100644 --- a/aws-lc-rs/src/aead.rs +++ b/aws-lc-rs/src/aead.rs @@ -463,6 +463,7 @@ pub struct UnboundKey { algorithm: &'static Algorithm, } +#[allow(clippy::missing_fields_in_debug)] impl Debug for UnboundKey { fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { f.debug_struct("UnboundKey") diff --git a/aws-lc-rs/src/aead/aes_gcm.rs b/aws-lc-rs/src/aead/aes_gcm.rs index 50dc3eaa6e1..f9bea23c9cb 100644 --- a/aws-lc-rs/src/aead/aes_gcm.rs +++ b/aws-lc-rs/src/aead/aes_gcm.rs @@ -5,7 +5,7 @@ use crate::aead::{Aad, Algorithm, AlgorithmID, Nonce, Tag, MAX_TAG_LEN}; use std::mem::MaybeUninit; use crate::aead::key_inner::KeyInner; -use crate::cipher::SymmetricCipherKey; +use crate::cipher::key::SymmetricCipherKey; use crate::error::Unspecified; use aws_lc::EVP_AEAD_CTX_seal_scatter; use std::ptr::null; diff --git a/aws-lc-rs/src/aead/chacha.rs b/aws-lc-rs/src/aead/chacha.rs index a9795158f00..08b850002a9 100644 --- a/aws-lc-rs/src/aead/chacha.rs +++ b/aws-lc-rs/src/aead/chacha.rs @@ -7,7 +7,7 @@ use crate::aead::key_inner::KeyInner; use crate::aead::{Algorithm, AlgorithmID}; use crate::cipher::chacha::KEY_LEN; -use crate::cipher::SymmetricCipherKey; +use crate::cipher::key::SymmetricCipherKey; use crate::error; diff --git a/aws-lc-rs/src/aead/key_inner.rs b/aws-lc-rs/src/aead/key_inner.rs index c97a2d69164..beacf44703e 100644 --- a/aws-lc-rs/src/aead/key_inner.rs +++ b/aws-lc-rs/src/aead/key_inner.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 OR ISC use crate::aead::TAG_LEN; -use crate::cipher::SymmetricCipherKey; +use crate::cipher::key::SymmetricCipherKey; use crate::error; use aws_lc::{ diff --git a/aws-lc-rs/src/aead/quic.rs b/aws-lc-rs/src/aead/quic.rs index a2ad4ad8786..6669db37552 100644 --- a/aws-lc-rs/src/aead/quic.rs +++ b/aws-lc-rs/src/aead/quic.rs @@ -10,7 +10,8 @@ use crate::aead::key_inner::KeyInner; use crate::cipher::aes::encrypt_block_aes; use crate::cipher::chacha::encrypt_block_chacha20; -use crate::cipher::{self, block, SymmetricCipherKey}; +use crate::cipher::key::SymmetricCipherKey; +use crate::cipher::{block, key}; use crate::hkdf::KeyType; use crate::{derive_debug_via_id, error, hkdf}; use core::convert::TryFrom; @@ -145,19 +146,19 @@ pub static CHACHA20: Algorithm = Algorithm { #[inline] fn aes_init_128(key: &[u8]) -> Result { - let aes_key = cipher::SymmetricCipherKey::aes128(key)?; + let aes_key = key::SymmetricCipherKey::aes128(key)?; KeyInner::new(aes_key) } #[inline] fn aes_init_256(key: &[u8]) -> Result { - let aes_key = cipher::SymmetricCipherKey::aes256(key)?; + let aes_key = key::SymmetricCipherKey::aes256(key)?; KeyInner::new(aes_key) } #[inline] fn chacha20_init(key: &[u8]) -> Result { - let chacha20 = cipher::SymmetricCipherKey::chacha20(key)?; + let chacha20 = key::SymmetricCipherKey::chacha20(key)?; KeyInner::new(chacha20) } diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index aeb6295c911..50957aced22 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -69,66 +69,19 @@ pub(crate) mod aes; pub(crate) mod block; pub(crate) mod chacha; +pub(crate) mod key; -use crate::cipher::aes::{encrypt_block_aes, Aes128Key, Aes256Key}; -use crate::cipher::block::Block; -use crate::cipher::chacha::ChaCha20Key; use crate::error::Unspecified; -use crate::iv::{self, FixedLength}; -use aws_lc::{ - AES_cbc_encrypt, AES_ctr128_encrypt, AES_set_decrypt_key, AES_set_encrypt_key, AES_DECRYPT, - AES_ENCRYPT, AES_KEY, -}; -use std::mem::{size_of, transmute, MaybeUninit}; -use std::os::raw::c_uint; -use std::ptr; +use crate::iv::FixedLength; +use aws_lc::{AES_cbc_encrypt, AES_ctr128_encrypt, AES_DECRYPT, AES_ENCRYPT, AES_KEY}; +use key::SymmetricCipherKey; +use std::fmt::Debug; +use std::mem::MaybeUninit; use zeroize::Zeroize; -pub(crate) enum SymmetricCipherKey { - Aes128 { - raw_key: Aes128Key, - enc_key: AES_KEY, - dec_key: AES_KEY, - }, - Aes256 { - raw_key: Aes256Key, - enc_key: AES_KEY, - dec_key: AES_KEY, - }, - ChaCha20 { - raw_key: ChaCha20Key, - }, -} - -unsafe impl Send for SymmetricCipherKey {} -// The AES_KEY value is only used as a `*const AES_KEY` in calls to `AES_encrypt`. -unsafe impl Sync for SymmetricCipherKey {} - -impl Drop for SymmetricCipherKey { - fn drop(&mut self) { - // Aes128Key, Aes256Key and ChaCha20Key implement Drop separately. - match self { - SymmetricCipherKey::Aes128 { - enc_key, dec_key, .. - } - | SymmetricCipherKey::Aes256 { - enc_key, dec_key, .. - } => unsafe { - #[allow(clippy::transmute_ptr_to_ptr)] - let enc_bytes: &mut [u8; size_of::()] = transmute(enc_key); - enc_bytes.zeroize(); - #[allow(clippy::transmute_ptr_to_ptr)] - let dec_bytes: &mut [u8; size_of::()] = transmute(dec_key); - dec_bytes.zeroize(); - }, - SymmetricCipherKey::ChaCha20 { .. } => {} - } - } -} - /// The cipher block padding strategy. -#[allow(dead_code)] -#[derive(Clone, Copy)] +#[non_exhaustive] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum PaddingStrategy { /// PKCS#7 Padding. PKCS7, @@ -197,7 +150,7 @@ const AES_BLOCK_LEN: usize = 16; /// The cipher operating mode. #[non_exhaustive] -#[derive(Clone, Copy)] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum OperatingMode { /// Cipher block chaining (CBC) mode. CBC, @@ -208,15 +161,27 @@ pub enum OperatingMode { /// The contextual data used to encrypted/decrypt data. #[non_exhaustive] -pub enum DecryptionContext { +pub enum CipherContext { /// A 128-bit Initalization Vector. - Iv128(iv::FixedLength<16>), + Iv128(FixedLength<16>), /// No input to the cipher mode. None, } +impl<'a> TryFrom<&'a CipherContext> for &'a [u8] { + type Error = Unspecified; + + fn try_from(value: &'a CipherContext) -> Result { + match value { + CipherContext::Iv128(iv) => Ok(iv.as_ref()), + CipherContext::None => Err(Unspecified), + } + } +} + #[non_exhaustive] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] /// Cipher algorithm identifier. pub enum AlgorithmId { /// AES 128-bit @@ -227,6 +192,7 @@ pub enum AlgorithmId { } /// A cipher algorithm. +#[derive(Debug, PartialEq, Eq)] pub struct Algorithm { id: AlgorithmId, key_len: usize, @@ -261,24 +227,21 @@ impl Algorithm { self.block_len } - fn new_decrypting_context( - &self, - mode: OperatingMode, - ) -> Result { + fn new_cipher_context(&self, mode: OperatingMode) -> Result { match self.id { AlgorithmId::Aes128 | AlgorithmId::Aes256 => match mode { OperatingMode::CBC | OperatingMode::CTR => { - Ok(DecryptionContext::Iv128(FixedLength::new()?)) + Ok(CipherContext::Iv128(FixedLength::new()?)) } }, } } - fn is_valid_decryption_context(&self, mode: OperatingMode, input: &DecryptionContext) -> bool { + fn is_valid_cipher_context(&self, mode: OperatingMode, input: &CipherContext) -> bool { match self.id { AlgorithmId::Aes128 | AlgorithmId::Aes256 => match mode { OperatingMode::CBC | OperatingMode::CTR => { - matches!(input, DecryptionContext::Iv128(_)) + matches!(input, CipherContext::Iv128(_)) } }, } @@ -318,7 +281,7 @@ pub struct PaddedBlockEncryptingKey { key: UnboundCipherKey, mode: OperatingMode, padding: PaddingStrategy, - context: DecryptionContext, + context: CipherContext, } impl PaddedBlockEncryptingKey { @@ -341,7 +304,7 @@ impl PaddedBlockEncryptingKey { /// pub fn less_safe_cbc_pkcs7( key: UnboundCipherKey, - context: DecryptionContext, + context: CipherContext, ) -> Result { PaddedBlockEncryptingKey::new( key, @@ -355,16 +318,16 @@ impl PaddedBlockEncryptingKey { key: UnboundCipherKey, mode: OperatingMode, padding: PaddingStrategy, - context: Option, + context: Option, ) -> Result { let mode_input = match context { Some(mi) => { - if !key.algorithm().is_valid_decryption_context(mode, &mi) { + if !key.algorithm().is_valid_cipher_context(mode, &mi) { return Err(Unspecified); } mi } - None => key.algorithm.new_decrypting_context(mode)?, + None => key.algorithm.new_cipher_context(mode)?, }; Ok(PaddedBlockEncryptingKey { @@ -399,7 +362,7 @@ impl PaddedBlockEncryptingKey { /// # Errors /// * [`Unspecified`]: Returned if encryption fails. /// - pub fn encrypt(self, in_out: &mut InOut) -> Result + pub fn encrypt(self, in_out: &mut InOut) -> Result where InOut: AsMut<[u8]> + for<'a> Extend<&'a u8>, { @@ -422,7 +385,7 @@ pub struct PaddedBlockDecryptingKey { key: UnboundCipherKey, mode: OperatingMode, padding: PaddingStrategy, - mode_input: DecryptionContext, + mode_input: CipherContext, } impl PaddedBlockDecryptingKey { @@ -434,7 +397,7 @@ impl PaddedBlockDecryptingKey { /// pub fn cbc_pkcs7( key: UnboundCipherKey, - mode_input: DecryptionContext, + mode_input: CipherContext, ) -> Result { PaddedBlockDecryptingKey::new(key, OperatingMode::CBC, PaddingStrategy::PKCS7, mode_input) } @@ -443,12 +406,9 @@ impl PaddedBlockDecryptingKey { key: UnboundCipherKey, mode: OperatingMode, padding: PaddingStrategy, - mode_input: DecryptionContext, + mode_input: CipherContext, ) -> Result { - if !key - .algorithm() - .is_valid_decryption_context(mode, &mode_input) - { + if !key.algorithm().is_valid_cipher_context(mode, &mode_input) { return Err(Unspecified); } @@ -497,7 +457,7 @@ impl PaddedBlockDecryptingKey { pub struct EncryptingKey { key: UnboundCipherKey, mode: OperatingMode, - context: DecryptionContext, + context: CipherContext, } impl EncryptingKey { @@ -518,7 +478,7 @@ impl EncryptingKey { /// pub fn less_safe_ctr( key: UnboundCipherKey, - context: DecryptionContext, + context: CipherContext, ) -> Result { EncryptingKey::new(key, OperatingMode::CTR, Some(context)) } @@ -526,16 +486,16 @@ impl EncryptingKey { fn new( key: UnboundCipherKey, mode: OperatingMode, - context: Option, + context: Option, ) -> Result { let context = match context { Some(mi) => { - if !key.algorithm().is_valid_decryption_context(mode, &mi) { + if !key.algorithm().is_valid_cipher_context(mode, &mi) { return Err(Unspecified); } mi } - None => key.algorithm.new_decrypting_context(mode)?, + None => key.algorithm.new_cipher_context(mode)?, }; Ok(EncryptingKey { key, mode, context }) @@ -560,7 +520,7 @@ impl EncryptingKey { /// * [`Unspecified`]: Returned if cipher mode requires input to be a multiple of the block length, /// and `in_out.len()` is not. Otherwise returned if encryption fails. /// - pub fn encrypt(self, in_out: &mut [u8]) -> Result { + pub fn encrypt(self, in_out: &mut [u8]) -> Result { let block_len = self.algorithm().block_len(); match self.mode { @@ -591,7 +551,7 @@ impl EncryptingKey { pub struct DecryptingKey { key: UnboundCipherKey, mode: OperatingMode, - mode_input: DecryptionContext, + mode_input: CipherContext, } impl DecryptingKey { @@ -602,7 +562,7 @@ impl DecryptingKey { /// pub fn ctr( key: UnboundCipherKey, - context: DecryptionContext, + context: CipherContext, ) -> Result { DecryptingKey::new(key, OperatingMode::CTR, context) } @@ -610,12 +570,9 @@ impl DecryptingKey { fn new( key: UnboundCipherKey, mode: OperatingMode, - mode_input: DecryptionContext, + mode_input: CipherContext, ) -> Result { - if !key - .algorithm() - .is_valid_decryption_context(mode, &mode_input) - { + if !key.algorithm().is_valid_cipher_context(mode, &mode_input) { return Err(Unspecified); } @@ -682,9 +639,9 @@ impl TryFrom for DecryptingKey { fn encrypt_aes_ctr_mode( key: &UnboundCipherKey, - context: DecryptionContext, + context: CipherContext, in_out: &mut [u8], -) -> Result { +) -> Result { #[allow(clippy::match_wildcard_for_single_variants)] let key = match &key.key { SymmetricCipherKey::Aes128 { enc_key, .. } | SymmetricCipherKey::Aes256 { enc_key, .. } => { @@ -695,10 +652,7 @@ fn encrypt_aes_ctr_mode( let mut iv = { let mut iv = [0u8; AES_IV_LEN]; - iv.copy_from_slice(match &context { - DecryptionContext::Iv128(iv) => iv.as_ref(), - _ => return Err(Unspecified), - }); + iv.copy_from_slice((&context).try_into()?); iv }; @@ -711,18 +665,18 @@ fn encrypt_aes_ctr_mode( fn decrypt_aes_ctr_mode( key: &UnboundCipherKey, - context: DecryptionContext, + context: CipherContext, in_out: &mut [u8], -) -> Result { +) -> Result { // it's the same in CTR, just providing a nice named wrapper to match encrypt_aes_ctr_mode(key, context, in_out) } fn encrypt_aes_cbc_mode( key: &UnboundCipherKey, - context: DecryptionContext, + context: CipherContext, in_out: &mut [u8], -) -> Result { +) -> Result { #[allow(clippy::match_wildcard_for_single_variants)] let key = match &key.key { SymmetricCipherKey::Aes128 { enc_key, .. } | SymmetricCipherKey::Aes256 { enc_key, .. } => { @@ -733,10 +687,7 @@ fn encrypt_aes_cbc_mode( let mut iv = { let mut iv = [0u8; AES_IV_LEN]; - iv.copy_from_slice(match &context { - DecryptionContext::Iv128(iv) => iv.as_ref(), - _ => return Err(Unspecified), - }); + iv.copy_from_slice((&context).try_into()?); iv }; @@ -747,9 +698,9 @@ fn encrypt_aes_cbc_mode( fn decrypt_aes_cbc_mode( key: &UnboundCipherKey, - context: DecryptionContext, + context: CipherContext, in_out: &mut [u8], -) -> Result { +) -> Result { #[allow(clippy::match_wildcard_for_single_variants)] let key = match &key.key { SymmetricCipherKey::Aes128 { dec_key, .. } | SymmetricCipherKey::Aes256 { dec_key, .. } => { @@ -760,10 +711,7 @@ fn decrypt_aes_cbc_mode( let mut iv = { let mut iv = [0u8; AES_IV_LEN]; - iv.copy_from_slice(match &context { - DecryptionContext::Iv128(iv) => iv.as_ref(), - _ => return Err(Unspecified), - }); + iv.copy_from_slice((&context).try_into()?); iv }; @@ -816,148 +764,11 @@ fn aes_cbc_decrypt(key: &AES_KEY, iv: &mut [u8], in_out: &mut [u8]) { } } -impl SymmetricCipherKey { - pub(crate) fn aes128(key_bytes: &[u8]) -> Result { - if key_bytes.len() != AES_128_KEY_LEN { - return Err(Unspecified); - } - - unsafe { - let mut enc_key = MaybeUninit::::uninit(); - #[allow(clippy::cast_possible_truncation)] - if 0 != AES_set_encrypt_key( - key_bytes.as_ptr(), - (key_bytes.len() * 8) as c_uint, - enc_key.as_mut_ptr(), - ) { - return Err(Unspecified); - } - let enc_key = enc_key.assume_init(); - - let mut dec_key = MaybeUninit::::uninit(); - #[allow(clippy::cast_possible_truncation)] - if 0 != AES_set_decrypt_key( - key_bytes.as_ptr(), - (key_bytes.len() * 8) as c_uint, - dec_key.as_mut_ptr(), - ) { - return Err(Unspecified); - } - let dec_key = dec_key.assume_init(); - - let mut kb = MaybeUninit::<[u8; 16]>::uninit(); - ptr::copy_nonoverlapping(key_bytes.as_ptr(), kb.as_mut_ptr().cast(), 16); - Ok(SymmetricCipherKey::Aes128 { - raw_key: Aes128Key(kb.assume_init()), - enc_key, - dec_key, - }) - } - } - - pub(crate) fn aes256(key_bytes: &[u8]) -> Result { - if key_bytes.len() != 32 { - return Err(Unspecified); - } - unsafe { - let mut enc_key = MaybeUninit::::uninit(); - #[allow(clippy::cast_possible_truncation)] - if 0 != AES_set_encrypt_key( - key_bytes.as_ptr(), - (key_bytes.len() * 8) as c_uint, - enc_key.as_mut_ptr(), - ) { - return Err(Unspecified); - } - let enc_key = enc_key.assume_init(); - - let mut dec_key = MaybeUninit::::uninit(); - #[allow(clippy::cast_possible_truncation)] - if 0 != AES_set_decrypt_key( - key_bytes.as_ptr(), - (key_bytes.len() * 8) as c_uint, - dec_key.as_mut_ptr(), - ) { - return Err(Unspecified); - } - let dec_key = dec_key.assume_init(); - - let mut kb = MaybeUninit::<[u8; 32]>::uninit(); - ptr::copy_nonoverlapping(key_bytes.as_ptr(), kb.as_mut_ptr().cast(), 32); - Ok(SymmetricCipherKey::Aes256 { - raw_key: Aes256Key(kb.assume_init()), - enc_key, - dec_key, - }) - } - } - - pub(crate) fn chacha20(key_bytes: &[u8]) -> Result { - if key_bytes.len() != 32 { - return Err(Unspecified); - } - let mut kb = MaybeUninit::<[u8; 32]>::uninit(); - unsafe { - ptr::copy_nonoverlapping(key_bytes.as_ptr(), kb.as_mut_ptr().cast(), 32); - Ok(SymmetricCipherKey::ChaCha20 { - raw_key: ChaCha20Key(kb.assume_init()), - }) - } - } - - #[inline] - pub(super) fn key_bytes(&self) -> &[u8] { - match self { - SymmetricCipherKey::Aes128 { raw_key, .. } => &raw_key.0, - SymmetricCipherKey::Aes256 { raw_key, .. } => &raw_key.0, - SymmetricCipherKey::ChaCha20 { raw_key, .. } => &raw_key.0, - } - } - - #[allow(dead_code)] - #[inline] - pub(crate) fn encrypt_block(&self, block: Block) -> Block { - match self { - SymmetricCipherKey::Aes128 { enc_key, .. } - | SymmetricCipherKey::Aes256 { enc_key, .. } => encrypt_block_aes(enc_key, block), - SymmetricCipherKey::ChaCha20 { .. } => panic!("Unsupported algorithm!"), - } - } -} - #[cfg(test)] mod tests { use super::*; - use crate::cipher::block::BLOCK_LEN; use crate::test::from_hex; - #[test] - fn test_encrypt_block_aes_128() { - let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); - let input = from_hex("00112233445566778899aabbccddeeff").unwrap(); - let expected_result = from_hex("69c4e0d86a7b0430d8cdb78070b4c55a").unwrap(); - let input_block: [u8; BLOCK_LEN] = <[u8; BLOCK_LEN]>::try_from(input).unwrap(); - - let aes128 = SymmetricCipherKey::aes128(key.as_slice()).unwrap(); - let result = aes128.encrypt_block(Block::from(&input_block)); - - assert_eq!(expected_result.as_slice(), result.as_ref()); - } - - #[test] - fn test_encrypt_block_aes_256() { - let key = - from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(); - let input = from_hex("00112233445566778899aabbccddeeff").unwrap(); - let expected_result = from_hex("8ea2b7ca516745bfeafc49904b496089").unwrap(); - let input_block: [u8; BLOCK_LEN] = <[u8; BLOCK_LEN]>::try_from(input).unwrap(); - - let aes128 = SymmetricCipherKey::aes256(key.as_slice()).unwrap(); - let result = aes128.encrypt_block(Block::from(&input_block)); - - assert_eq!(expected_result.as_slice(), result.as_ref()); - } - fn helper_test_cipher_n_bytes( key: &[u8], alg: &'static Algorithm, @@ -1085,7 +896,7 @@ mod tests { iv }; - let dc = DecryptionContext::Iv128(FixedLength::from(iv)); + let dc = CipherContext::Iv128(FixedLength::from(iv)); let alg = $alg; @@ -1128,7 +939,7 @@ mod tests { iv }; - let dc = DecryptionContext::Iv128(FixedLength::from(iv)); + let dc = CipherContext::Iv128(FixedLength::from(iv)); let alg = $alg; diff --git a/aws-lc-rs/src/cipher/key.rs b/aws-lc-rs/src/cipher/key.rs new file mode 100644 index 00000000000..305540cb70c --- /dev/null +++ b/aws-lc-rs/src/cipher/key.rs @@ -0,0 +1,197 @@ +use crate::cipher::aes::{encrypt_block_aes, Aes128Key, Aes256Key}; +use crate::cipher::block::Block; +use crate::cipher::chacha::ChaCha20Key; +use crate::cipher::AES_128_KEY_LEN; +use crate::error::Unspecified; +use aws_lc::{AES_set_decrypt_key, AES_set_encrypt_key, AES_KEY}; +use core::ptr::copy_nonoverlapping; +use std::mem::{size_of, transmute, MaybeUninit}; +use std::os::raw::c_uint; +use std::ptr; +use zeroize::Zeroize; + +pub(crate) enum SymmetricCipherKey { + Aes128 { + raw_key: Aes128Key, + enc_key: AES_KEY, + dec_key: AES_KEY, + }, + Aes256 { + raw_key: Aes256Key, + enc_key: AES_KEY, + dec_key: AES_KEY, + }, + ChaCha20 { + raw_key: ChaCha20Key, + }, +} + +unsafe impl Send for SymmetricCipherKey {} + +// The AES_KEY value is only used as a `*const AES_KEY` in calls to `AES_encrypt`. +unsafe impl Sync for SymmetricCipherKey {} + +impl Drop for SymmetricCipherKey { + fn drop(&mut self) { + // Aes128Key, Aes256Key and ChaCha20Key implement Drop separately. + match self { + SymmetricCipherKey::Aes128 { + enc_key, dec_key, .. + } + | SymmetricCipherKey::Aes256 { + enc_key, dec_key, .. + } => unsafe { + #[allow(clippy::transmute_ptr_to_ptr)] + let enc_bytes: &mut [u8; size_of::()] = transmute(enc_key); + enc_bytes.zeroize(); + #[allow(clippy::transmute_ptr_to_ptr)] + let dec_bytes: &mut [u8; size_of::()] = transmute(dec_key); + dec_bytes.zeroize(); + }, + SymmetricCipherKey::ChaCha20 { .. } => {} + } + } +} + +impl SymmetricCipherKey { + pub(crate) fn aes128(key_bytes: &[u8]) -> Result { + if key_bytes.len() != AES_128_KEY_LEN { + return Err(Unspecified); + } + + unsafe { + let mut enc_key = MaybeUninit::::uninit(); + #[allow(clippy::cast_possible_truncation)] + if 0 != AES_set_encrypt_key( + key_bytes.as_ptr(), + (key_bytes.len() * 8) as c_uint, + enc_key.as_mut_ptr(), + ) { + return Err(Unspecified); + } + let enc_key = enc_key.assume_init(); + + let mut dec_key = MaybeUninit::::uninit(); + #[allow(clippy::cast_possible_truncation)] + if 0 != AES_set_decrypt_key( + key_bytes.as_ptr(), + (key_bytes.len() * 8) as c_uint, + dec_key.as_mut_ptr(), + ) { + return Err(Unspecified); + } + let dec_key = dec_key.assume_init(); + + let mut kb = MaybeUninit::<[u8; 16]>::uninit(); + ptr::copy_nonoverlapping(key_bytes.as_ptr(), kb.as_mut_ptr().cast(), 16); + Ok(SymmetricCipherKey::Aes128 { + raw_key: Aes128Key(kb.assume_init()), + enc_key, + dec_key, + }) + } + } + + pub(crate) fn aes256(key_bytes: &[u8]) -> Result { + if key_bytes.len() != 32 { + return Err(Unspecified); + } + unsafe { + let mut enc_key = MaybeUninit::::uninit(); + #[allow(clippy::cast_possible_truncation)] + if 0 != AES_set_encrypt_key( + key_bytes.as_ptr(), + (key_bytes.len() * 8) as c_uint, + enc_key.as_mut_ptr(), + ) { + return Err(Unspecified); + } + let enc_key = enc_key.assume_init(); + + let mut dec_key = MaybeUninit::::uninit(); + #[allow(clippy::cast_possible_truncation)] + if 0 != AES_set_decrypt_key( + key_bytes.as_ptr(), + (key_bytes.len() * 8) as c_uint, + dec_key.as_mut_ptr(), + ) { + return Err(Unspecified); + } + let dec_key = dec_key.assume_init(); + + let mut kb = MaybeUninit::<[u8; 32]>::uninit(); + copy_nonoverlapping(key_bytes.as_ptr(), kb.as_mut_ptr().cast(), 32); + Ok(SymmetricCipherKey::Aes256 { + raw_key: Aes256Key(kb.assume_init()), + enc_key, + dec_key, + }) + } + } + + pub(crate) fn chacha20(key_bytes: &[u8]) -> Result { + if key_bytes.len() != 32 { + return Err(Unspecified); + } + let mut kb = MaybeUninit::<[u8; 32]>::uninit(); + unsafe { + copy_nonoverlapping(key_bytes.as_ptr(), kb.as_mut_ptr().cast(), 32); + Ok(SymmetricCipherKey::ChaCha20 { + raw_key: ChaCha20Key(kb.assume_init()), + }) + } + } + + #[inline] + pub(crate) fn key_bytes(&self) -> &[u8] { + match self { + SymmetricCipherKey::Aes128 { raw_key, .. } => &raw_key.0, + SymmetricCipherKey::Aes256 { raw_key, .. } => &raw_key.0, + SymmetricCipherKey::ChaCha20 { raw_key, .. } => &raw_key.0, + } + } + + #[allow(dead_code)] + #[inline] + pub(crate) fn encrypt_block(&self, block: Block) -> Block { + match self { + SymmetricCipherKey::Aes128 { enc_key, .. } + | SymmetricCipherKey::Aes256 { enc_key, .. } => encrypt_block_aes(enc_key, block), + SymmetricCipherKey::ChaCha20 { .. } => panic!("Unsupported algorithm!"), + } + } +} + +#[cfg(test)] +mod tests { + use crate::cipher::block::{Block, BLOCK_LEN}; + use crate::cipher::key::SymmetricCipherKey; + use crate::test::from_hex; + + #[test] + fn test_encrypt_block_aes_128() { + let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); + let input = from_hex("00112233445566778899aabbccddeeff").unwrap(); + let expected_result = from_hex("69c4e0d86a7b0430d8cdb78070b4c55a").unwrap(); + let input_block: [u8; BLOCK_LEN] = <[u8; BLOCK_LEN]>::try_from(input).unwrap(); + + let aes128 = SymmetricCipherKey::aes128(key.as_slice()).unwrap(); + let result = aes128.encrypt_block(Block::from(&input_block)); + + assert_eq!(expected_result.as_slice(), result.as_ref()); + } + + #[test] + fn test_encrypt_block_aes_256() { + let key = + from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(); + let input = from_hex("00112233445566778899aabbccddeeff").unwrap(); + let expected_result = from_hex("8ea2b7ca516745bfeafc49904b496089").unwrap(); + let input_block: [u8; BLOCK_LEN] = <[u8; BLOCK_LEN]>::try_from(input).unwrap(); + + let aes128 = SymmetricCipherKey::aes256(key.as_slice()).unwrap(); + let result = aes128.encrypt_block(Block::from(&input_block)); + + assert_eq!(expected_result.as_slice(), result.as_ref()); + } +} diff --git a/aws-lc-rs/src/hkdf.rs b/aws-lc-rs/src/hkdf.rs index f2875ad374a..c4d635b0ba7 100644 --- a/aws-lc-rs/src/hkdf.rs +++ b/aws-lc-rs/src/hkdf.rs @@ -92,6 +92,7 @@ pub struct Salt { key_len: usize, } +#[allow(clippy::missing_fields_in_debug)] impl fmt::Debug for Salt { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("hkdf::Salt") @@ -207,6 +208,7 @@ pub struct Prk { key_len: usize, } +#[allow(clippy::missing_fields_in_debug)] impl fmt::Debug for Prk { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("hkdf::Prk") diff --git a/aws-lc-rs/src/hmac.rs b/aws-lc-rs/src/hmac.rs index 039605ecc2c..46cbedf2740 100644 --- a/aws-lc-rs/src/hmac.rs +++ b/aws-lc-rs/src/hmac.rs @@ -206,6 +206,7 @@ unsafe impl Send for Key {} // All uses of *mut HMAC_CTX require the creation of a Context, which will clone the Key. unsafe impl Sync for Key {} +#[allow(clippy::missing_fields_in_debug)] impl core::fmt::Debug for Key { fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { f.debug_struct("Key") diff --git a/aws-lc-rs/tests/cipher_test.rs b/aws-lc-rs/tests/cipher_test.rs new file mode 100644 index 00000000000..d2f0c899ca3 --- /dev/null +++ b/aws-lc-rs/tests/cipher_test.rs @@ -0,0 +1,319 @@ +use aws_lc_rs::cipher::{ + CipherContext, DecryptingKey, EncryptingKey, OperatingMode, PaddedBlockDecryptingKey, + PaddedBlockEncryptingKey, PaddingStrategy, UnboundCipherKey, AES_128, AES_256, +}; +use aws_lc_rs::iv::FixedLength; +use aws_lc_rs::test::from_hex; + +macro_rules! padded_cipher_kat { + ($name:ident, $alg:expr, $mode:expr, $padding:expr, $constructor:ident, $key:literal, $iv: literal, $plaintext:literal, $ciphertext:literal) => { paste::item! { + #[test] + fn $name() { + let key = from_hex($key).unwrap(); + let input = from_hex($plaintext).unwrap(); + let expected_ciphertext = from_hex($ciphertext).unwrap(); + + let iv = from_hex($iv).unwrap(); + let fixed_iv = FixedLength::try_from(iv.as_slice()).unwrap(); + let context = CipherContext::Iv128(fixed_iv); + + let unbound_key = UnboundCipherKey::new($alg, &key).unwrap(); + + let encrypting_key = + PaddedBlockEncryptingKey::[](unbound_key, context).unwrap(); + assert_eq!($mode, encrypting_key.mode()); + assert_eq!($padding, encrypting_key.padding()); + assert_eq!($alg, encrypting_key.algorithm()); + let mut in_out = input.clone(); + let context = encrypting_key.encrypt(&mut in_out).unwrap(); + assert_eq!(expected_ciphertext.as_slice(), in_out.as_slice()); + + let unbound_key2 = UnboundCipherKey::new($alg, &key).unwrap(); + let decrypting_key = + PaddedBlockDecryptingKey::$constructor(unbound_key2, context).unwrap(); + assert_eq!($mode, decrypting_key.mode()); + assert_eq!($padding, decrypting_key.padding()); + assert_eq!($alg, decrypting_key.algorithm()); + let plaintext = decrypting_key.decrypt(&mut in_out).unwrap(); + assert_eq!(input.as_slice(), plaintext); + } + }}; +} + +macro_rules! cipher_kat { + ($name:ident, $alg:expr, $mode:expr, $constructor:ident, $key:literal, $iv: literal, $plaintext:literal, $ciphertext:literal) => { + paste::item! { + #[test] + fn $name() { + let key = from_hex($key).unwrap(); + let input = from_hex($plaintext).unwrap(); + let expected_ciphertext = from_hex($ciphertext).unwrap(); + + let iv = from_hex($iv).unwrap(); + let fixed_iv = FixedLength::try_from(iv.as_slice()).unwrap(); + let context = CipherContext::Iv128(fixed_iv); + + let unbound_key = UnboundCipherKey::new($alg, &key).unwrap(); + + let encrypting_key = + EncryptingKey::[](unbound_key, context).unwrap(); + assert_eq!($mode, encrypting_key.mode()); + assert_eq!($alg, encrypting_key.algorithm()); + let mut in_out = input.clone(); + let context = encrypting_key.encrypt(in_out.as_mut_slice()).unwrap(); + assert_eq!(expected_ciphertext.as_slice(), in_out); + + let unbound_key2 = UnboundCipherKey::new($alg, &key).unwrap(); + let decrypting_key = + DecryptingKey::$constructor(unbound_key2, context).unwrap(); + assert_eq!($mode, decrypting_key.mode()); + assert_eq!($alg, decrypting_key.algorithm()); + let plaintext = decrypting_key.decrypt(&mut in_out).unwrap(); + assert_eq!(input.as_slice(), plaintext); + } + } + }; +} + +macro_rules! padded_cipher_rt { + ($name:ident, $alg:expr, $mode:expr, $padding:expr, $constructor:ident, $key:literal, $plaintext:literal) => { + paste::item! { + #[test] + fn $name() { + let key = from_hex($key).unwrap(); + let input = from_hex($plaintext).unwrap(); + let unbound_key = UnboundCipherKey::new($alg, &key).unwrap(); + + let encrypting_key = PaddedBlockEncryptingKey::$constructor(unbound_key).unwrap(); + assert_eq!($mode, encrypting_key.mode()); + assert_eq!($padding, encrypting_key.padding()); + assert_eq!($alg, encrypting_key.algorithm()); + let mut in_out = input.clone(); + let context = encrypting_key.encrypt(&mut in_out).unwrap(); + + let unbound_key2 = UnboundCipherKey::new($alg, &key).unwrap(); + let decrypting_key = + PaddedBlockDecryptingKey::$constructor(unbound_key2, context).unwrap(); + assert_eq!($mode, decrypting_key.mode()); + assert_eq!($padding, decrypting_key.padding()); + assert_eq!($alg, decrypting_key.algorithm()); + let plaintext = decrypting_key.decrypt(&mut in_out).unwrap(); + assert_eq!(input.as_slice(), plaintext); + } + } + }; +} + +macro_rules! cipher_rt { + ($name:ident, $alg:expr, $mode:expr, $constructor:ident, $key:literal, $plaintext:literal) => { + #[test] + fn $name() { + let key = from_hex($key).unwrap(); + let input = from_hex($plaintext).unwrap(); + let unbound_key = UnboundCipherKey::new($alg, &key).unwrap(); + + let encrypting_key = EncryptingKey::$constructor(unbound_key).unwrap(); + assert_eq!($mode, encrypting_key.mode()); + assert_eq!($alg, encrypting_key.algorithm()); + let mut in_out = input.clone(); + let context = encrypting_key.encrypt(in_out.as_mut_slice()).unwrap(); + + let unbound_key2 = UnboundCipherKey::new($alg, &key).unwrap(); + let decrypting_key = DecryptingKey::$constructor(unbound_key2, context).unwrap(); + assert_eq!($mode, decrypting_key.mode()); + assert_eq!($alg, decrypting_key.algorithm()); + let plaintext = decrypting_key.decrypt(&mut in_out).unwrap(); + assert_eq!(input.as_slice(), plaintext); + } + }; +} + +padded_cipher_kat!( + test_kat_aes_128_cbc_16_bytes, + &AES_128, + OperatingMode::CBC, + PaddingStrategy::PKCS7, + cbc_pkcs7, + "000102030405060708090a0b0c0d0e0f", + "00000000000000000000000000000000", + "00112233445566778899aabbccddeeff", + "69c4e0d86a7b0430d8cdb78070b4c55a9e978e6d16b086570ef794ef97984232" +); + +padded_cipher_kat!( + test_kat_aes_256_cbc_15_bytes, + &AES_256, + OperatingMode::CBC, + PaddingStrategy::PKCS7, + cbc_pkcs7, + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "00000000000000000000000000000000", + "00112233445566778899aabbccddee", + "2ddfb635a651a43f582997966840ca0c" +); + +cipher_kat!( + test_kat_aes_128_ctr_16_bytes, + &AES_128, + OperatingMode::CTR, + ctr, + "000102030405060708090a0b0c0d0e0f", + "00000000000000000000000000000000", + "00112233445566778899aabbccddeeff", + "c6b01904c3da3df5e7d62bd96d153686" +); + +cipher_kat!( + test_kat_aes_256_ctr_15_bytes, + &AES_256, + OperatingMode::CTR, + ctr, + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "00000000000000000000000000000000", + "00112233445566778899aabbccddee", + "f28122856e1cf9a7216a30d111f399" +); + +cipher_kat!( + test_kat_aes_128_ctr_15_bytes, + &AES_128, + OperatingMode::CTR, + ctr, + "244828580821c1652582c76e34d299f5", + "093145d5af233f46072a5eb5adc11aa1", + "3ee38cec171e6cf466bf0df98aa0e1", + "bd7d928f60e3422d96b3f8cd614eb2" +); + +cipher_kat!( + test_kat_256_ctr_15_bytes, + &AES_256, + OperatingMode::CTR, + ctr, + "0857db8240ea459bdf660b4cced66d1f2d3734ff2de7b81e92740e65e7cc6a1d", + "f028ecb053f801102d11fccc9d303a27", + "eca7285d19f3c20e295378460e8729", + "b5098e5e788de6ac2f2098eb2fc6f8" +); + +padded_cipher_kat!( + test_kat_aes_128_cbc_15_bytes, + &AES_128, + OperatingMode::CBC, + PaddingStrategy::PKCS7, + cbc_pkcs7, + "053304bb3899e1d99db9d29343ea782d", + "b5313560244a4822c46c2a0c9d0cf7fd", + "a3e4c990356c01f320043c3d8d6f43", + "ad96993f248bd6a29760ec7ccda95ee1" +); + +padded_cipher_kat!( + test_kat_128_cbc_16_bytes, + &AES_128, + OperatingMode::CBC, + PaddingStrategy::PKCS7, + cbc_pkcs7, + "95af71f1c63e4a1d0b0b1a27fb978283", + "89e40797dca70197ff87d3dbb0ef2802", + "aece7b5e3c3df1ffc9802d2dfe296dc7", + "301b5dab49fb11e919d0d39970d06739301919743304f23f3cbc67d28564b25b" +); + +padded_cipher_kat!( + test_kat_aes_256_cbc_16_bytes, + &AES_256, + OperatingMode::CBC, + PaddingStrategy::PKCS7, + cbc_pkcs7, + "d4a8206dcae01242f9db79a4ecfe277d0f7bb8ccbafd8f9809adb39f35aa9b41", + "24f6076548fb9d93c8f7ed9f6e661ef9", + "a39c1fdf77ea3e1f18178c0ec237c70a", + "f1af484830a149ee0387b854d65fe87ca0e62efc1c8e6909d4b9ab8666470453" +); + +padded_cipher_rt!( + test_rt_aes_128_cbc_16_bytes, + &AES_128, + OperatingMode::CBC, + PaddingStrategy::PKCS7, + cbc_pkcs7, + "000102030405060708090a0b0c0d0e0f", + "00112233445566778899aabbccddeeff" +); + +padded_cipher_rt!( + test_rt_aes_256_cbc_15_bytes, + &AES_256, + OperatingMode::CBC, + PaddingStrategy::PKCS7, + cbc_pkcs7, + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "00112233445566778899aabbccddee" +); + +cipher_rt!( + test_rt_aes_128_ctr_16_bytes, + &AES_128, + OperatingMode::CTR, + ctr, + "000102030405060708090a0b0c0d0e0f", + "00112233445566778899aabbccddeeff" +); + +cipher_rt!( + test_rt_aes_256_ctr_15_bytes, + &AES_256, + OperatingMode::CTR, + ctr, + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "00112233445566778899aabbccddee" +); + +cipher_rt!( + test_rt_aes_128_ctr_15_bytes, + &AES_128, + OperatingMode::CTR, + ctr, + "244828580821c1652582c76e34d299f5", + "3ee38cec171e6cf466bf0df98aa0e1" +); + +cipher_rt!( + test_rt_256_ctr_15_bytes, + &AES_256, + OperatingMode::CTR, + ctr, + "0857db8240ea459bdf660b4cced66d1f2d3734ff2de7b81e92740e65e7cc6a1d", + "eca7285d19f3c20e295378460e8729" +); + +padded_cipher_rt!( + test_rt_aes_128_cbc_15_bytes, + &AES_128, + OperatingMode::CBC, + PaddingStrategy::PKCS7, + cbc_pkcs7, + "053304bb3899e1d99db9d29343ea782d", + "a3e4c990356c01f320043c3d8d6f43" +); + +padded_cipher_rt!( + test_rt_128_cbc_16_bytes, + &AES_128, + OperatingMode::CBC, + PaddingStrategy::PKCS7, + cbc_pkcs7, + "95af71f1c63e4a1d0b0b1a27fb978283", + "aece7b5e3c3df1ffc9802d2dfe296dc7" +); + +padded_cipher_rt!( + test_rt_aes_256_cbc_16_bytes, + &AES_256, + OperatingMode::CBC, + PaddingStrategy::PKCS7, + cbc_pkcs7, + "d4a8206dcae01242f9db79a4ecfe277d0f7bb8ccbafd8f9809adb39f35aa9b41", + "a39c1fdf77ea3e1f18178c0ec237c70a" +); From c72d0412f22b85edf2c031b8e064999b3aa4ffcf Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Tue, 6 Jun 2023 15:33:00 -0400 Subject: [PATCH 28/54] Zeroize IVs (#143) --- aws-lc-rs/src/cipher.rs | 3 +++ aws-lc-rs/src/iv.rs | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index 50957aced22..4fe1c244b2f 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -659,6 +659,7 @@ fn encrypt_aes_ctr_mode( let mut buffer = [0u8; AES_BLOCK_LEN]; aes_ctr128_encrypt(key, &mut iv, &mut buffer, in_out); + iv.zeroize(); Ok(context) } @@ -692,6 +693,7 @@ fn encrypt_aes_cbc_mode( }; aes_cbc_encrypt(key, &mut iv, in_out); + iv.zeroize(); Ok(context) } @@ -716,6 +718,7 @@ fn decrypt_aes_cbc_mode( }; aes_cbc_decrypt(key, &mut iv, in_out); + iv.zeroize(); Ok(context) } diff --git a/aws-lc-rs/src/iv.rs b/aws-lc-rs/src/iv.rs index 14953a0df7a..2a4385afd60 100644 --- a/aws-lc-rs/src/iv.rs +++ b/aws-lc-rs/src/iv.rs @@ -8,6 +8,7 @@ use crate::error::Unspecified; use crate::{error, rand}; +use zeroize::Zeroize; /// An initalization vector that must be unique for the lifetime of the associated key /// it is used with. @@ -53,6 +54,12 @@ impl FixedLength { } } +impl Drop for FixedLength { + fn drop(&mut self) { + self.0.zeroize(); + } +} + impl AsMut<[u8; L]> for FixedLength { #[inline] fn as_mut(&mut self) -> &mut [u8; L] { From 89ff782c1230396b4937a45617482e5e2a4ce482 Mon Sep 17 00:00:00 2001 From: Sean McGrail <549813+skmcgrail@users.noreply.github.com> Date: Tue, 6 Jun 2023 14:06:01 -0700 Subject: [PATCH 29/54] Add Cipher CLI example for CBC/CTR API (#142) * Add Cipher CLI to Examples * Make Clippy Happy --- aws-lc-rs/Cargo.toml | 3 +- aws-lc-rs/examples/cipher.rs | 233 +++++++++++++++++++++++++++++++++++ 2 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 aws-lc-rs/examples/cipher.rs diff --git a/aws-lc-rs/Cargo.toml b/aws-lc-rs/Cargo.toml index a3c6a5818c9..9b110e9eb7b 100644 --- a/aws-lc-rs/Cargo.toml +++ b/aws-lc-rs/Cargo.toml @@ -46,12 +46,13 @@ mirai-annotations = "1.12.0" [dev-dependencies] paste = "1.0" -criterion = { version = "0.5.0", features = ["csv_output"]} +criterion = { version = "0.5.0", features = ["csv_output"] } ring = "0.16" regex = "1.6.0" lazy_static = "1.4.0" clap = { version = "4.1.8", features = ["derive"] } openssl = { version = "0.10.52", features = ["vendored"] } +hex = "0.4.3" [[bench]] name = "aead_benchmark" diff --git a/aws-lc-rs/examples/cipher.rs b/aws-lc-rs/examples/cipher.rs new file mode 100644 index 00000000000..3efad0ebb04 --- /dev/null +++ b/aws-lc-rs/examples/cipher.rs @@ -0,0 +1,233 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 OR ISC + +//! cipher - Perform symmetric cipher encryption/decryption on utf8 plaintext. +//! +//! *cipher* is an example program demonstrating the `aws_lc_rs::cipher` API for *aws-lc-rs*. +//! It demonstrates CTR & CBC mode encryption using AES 128 or 256 bit keys. +//! +//! The program can be run from the command line using cargo: +//! ```sh +//! $ cargo run --example cipher -- --mode ctr encrypt "Hello World" +//! key: b331133eb742497c67ced9520c9a7de3 +//! iv: 4e967c7b799e0670431888e2e959e154 +//! ciphertext: 88bcbd8d1656d60de739c5 +//! +//! $ cargo run --example cipher -- --mode ctr --key b331133eb742497c67ced9520c9a7de3 decrypt --iv 4e967c7b799e0670431888e2e959e154 88bcbd8d1656d60de739c5 +//! Hello World +//! +//! $ cargo run --example cipher -- --mode cbc encrypt "Hello World" +//! key: 6489d8ce0c4facf18b872705a05d5ee4 +//! iv: 5cd56fb752830ec2459889226c5431bd +//! ciphertext: 6311c14e8104730be124ce1e57e51fe3 +//! +//! $ cargo run --example cipher -- --mode cbc --key 6489d8ce0c4facf18b872705a05d5ee4 decrypt --iv 5cd56fb752830ec2459889226c5431bd 6311c14e8104730be124ce1e57e51fe3 +//! Hello World +//! ``` +use aws_lc_rs::{ + cipher::{ + CipherContext, DecryptingKey, EncryptingKey, PaddedBlockDecryptingKey, + PaddedBlockEncryptingKey, UnboundCipherKey, AES_128, AES_256, + }, + iv::FixedLength, +}; +use clap::{Parser, Subcommand, ValueEnum}; + +#[derive(Parser)] +#[command(author, version, name = "cipher")] +struct Cli { + #[arg( + short, + long, + help = "AES 128 or 256 bit key in hex, if not provided defaults to 128" + )] + key: Option, + + #[arg(short, long, value_enum, help = "AES cipher mode")] + mode: Mode, + + #[command(subcommand)] + command: Commands, +} + +#[derive(ValueEnum, Clone, Copy)] +enum Mode { + Ctr, + Cbc, +} + +#[derive(Subcommand)] +enum Commands { + Encrypt { + #[arg(short, long, help = "Initalization Vector (IV)")] + iv: Option, + plaintext: String, + }, + Decrypt { + #[arg(short, long, help = "Initalization Vector (IV)")] + iv: String, + ciphertext: String, + }, +} + +fn main() -> Result<(), &'static str> { + let cli = Cli::parse(); + + let key = if let Some(key) = cli.key { + match hex::decode(key) { + Ok(v) => v, + Err(..) => { + return Err("invalid key"); + } + } + } else { + let mut v = vec![0u8; 16]; + aws_lc_rs::rand::fill(v.as_mut_slice()).map_err(|_| "failed to generate key")?; + v + }; + + match (cli.command, cli.mode) { + (Commands::Encrypt { iv, plaintext }, Mode::Ctr) => aes_ctr_encrypt(&key, iv, plaintext), + (Commands::Encrypt { iv, plaintext }, Mode::Cbc) => aes_cbc_encrypt(&key, iv, plaintext), + (Commands::Decrypt { iv, ciphertext }, Mode::Ctr) => aes_ctr_decrypt(&key, iv, ciphertext), + (Commands::Decrypt { iv, ciphertext }, Mode::Cbc) => aes_cbc_decrypt(&key, iv, ciphertext), + }?; + + Ok(()) +} + +fn aes_ctr_encrypt(key: &[u8], iv: Option, plaintext: String) -> Result<(), &'static str> { + let hex_key = hex::encode(key); + let key = new_unbound_key(key)?; + + let key = match iv { + Some(iv) => { + let iv = { + let v = hex::decode(iv).map_err(|_| "invalid iv")?; + let v: FixedLength<16> = v.as_slice().try_into().map_err(|_| "invalid iv")?; + v + }; + EncryptingKey::less_safe_ctr(key, CipherContext::Iv128(iv)) + } + None => EncryptingKey::ctr(key), + } + .map_err(|_| "failed to initalized aes encryption")?; + + let mut ciphertext = Vec::from(plaintext); + + let context = key + .encrypt(ciphertext.as_mut()) + .map_err(|_| "Failed to encrypt plaintext")?; + + let iv: &[u8] = (&context) + .try_into() + .map_err(|_| "unexpected encryption context")?; + + let ciphertext = hex::encode(ciphertext.as_slice()); + + println!("key: {hex_key}"); + println!("iv: {}", hex::encode(iv)); + println!("ciphertext: {ciphertext}"); + + Ok(()) +} + +fn aes_ctr_decrypt(key: &[u8], iv: String, ciphertext: String) -> Result<(), &'static str> { + let key = new_unbound_key(key)?; + let iv = { + let v = hex::decode(iv).map_err(|_| "invalid iv")?; + let v: FixedLength<16> = v.as_slice().try_into().map_err(|_| "invalid iv")?; + v + }; + + let key = DecryptingKey::ctr(key, CipherContext::Iv128(iv)) + .map_err(|_| "failed to initalized aes decryption")?; + + let mut ciphertext = + hex::decode(ciphertext).map_err(|_| "ciphertext is not valid hex encoding")?; + + let plaintext = key + .decrypt(ciphertext.as_mut()) + .map_err(|_| "failed to decrypt ciphertext")?; + + let plaintext = + String::from_utf8(plaintext.into()).map_err(|_| "decrypted text was not a utf8 string")?; + + println!("{plaintext}"); + + Ok(()) +} + +fn aes_cbc_encrypt(key: &[u8], iv: Option, plaintext: String) -> Result<(), &'static str> { + let hex_key = hex::encode(key); + let key = new_unbound_key(key)?; + + let key = match iv { + Some(iv) => { + let iv = { + let v = hex::decode(iv).map_err(|_| "invalid iv")?; + let v: FixedLength<16> = v.as_slice().try_into().map_err(|_| "invalid iv")?; + v + }; + PaddedBlockEncryptingKey::less_safe_cbc_pkcs7(key, CipherContext::Iv128(iv)) + } + None => PaddedBlockEncryptingKey::cbc_pkcs7(key), + } + .map_err(|_| "failed to initalized aes encryption")?; + + let mut ciphertext = Vec::from(plaintext); + + let context = key + .encrypt(&mut ciphertext) + .map_err(|_| "Failed to encrypt plaintext")?; + + let iv: &[u8] = (&context) + .try_into() + .map_err(|_| "unexpected encryption context")?; + + let ciphertext = hex::encode(ciphertext.as_slice()); + + println!("key: {hex_key}"); + println!("iv: {}", hex::encode(iv)); + println!("ciphertext: {ciphertext}"); + + Ok(()) +} + +fn aes_cbc_decrypt(key: &[u8], iv: String, ciphertext: String) -> Result<(), &'static str> { + let key = new_unbound_key(key)?; + let iv = { + let v = hex::decode(iv).map_err(|_| "invalid iv")?; + let v: FixedLength<16> = v.as_slice().try_into().map_err(|_| "invalid iv")?; + v + }; + + let key = PaddedBlockDecryptingKey::cbc_pkcs7(key, CipherContext::Iv128(iv)) + .map_err(|_| "failed to initalized aes decryption")?; + + let mut ciphertext = + hex::decode(ciphertext).map_err(|_| "ciphertext is not valid hex encoding")?; + + let plaintext = key + .decrypt(ciphertext.as_mut()) + .map_err(|_| "failed to decrypt ciphertext")?; + + let plaintext = + String::from_utf8(plaintext.into()).map_err(|_| "decrypted text was not a utf8 string")?; + + println!("{plaintext}"); + + Ok(()) +} + +fn new_unbound_key(key: &[u8]) -> Result { + let alg = match key.len() { + 16 => &AES_128, + 32 => &AES_256, + _ => { + return Err("invalid aes key length"); + } + }; + + UnboundCipherKey::new(alg, key).map_err(|_| "failed to construct aes key") +} From af189c537d3f9f64a3166789e920a57d420823ae Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Tue, 6 Jun 2023 19:44:21 -0400 Subject: [PATCH 30/54] Improve test coverage (#144) --- aws-lc-rs/src/cipher.rs | 9 +++----- aws-lc-rs/src/iv.rs | 26 ++++++++++++++++------- aws-lc-rs/tests/cipher_test.rs | 38 ++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 13 deletions(-) diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index 4fe1c244b2f..d522defcb82 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -148,6 +148,8 @@ pub const AES_256_KEY_LEN: usize = 32; pub const AES_IV_LEN: usize = 16; const AES_BLOCK_LEN: usize = 16; +const IV_LEN_128_BIT: usize = 16; + /// The cipher operating mode. #[non_exhaustive] #[derive(Debug, PartialEq, Eq, Clone, Copy)] @@ -163,7 +165,7 @@ pub enum OperatingMode { #[non_exhaustive] pub enum CipherContext { /// A 128-bit Initalization Vector. - Iv128(FixedLength<16>), + Iv128(FixedLength), /// No input to the cipher mode. None, @@ -218,11 +220,6 @@ impl Algorithm { &self.id } - #[allow(unused)] - const fn key_len(&self) -> usize { - self.key_len - } - const fn block_len(&self) -> usize { self.block_len } diff --git a/aws-lc-rs/src/iv.rs b/aws-lc-rs/src/iv.rs index 2a4385afd60..34b741fb5dd 100644 --- a/aws-lc-rs/src/iv.rs +++ b/aws-lc-rs/src/iv.rs @@ -60,13 +60,6 @@ impl Drop for FixedLength { } } -impl AsMut<[u8; L]> for FixedLength { - #[inline] - fn as_mut(&mut self) -> &mut [u8; L] { - &mut self.0 - } -} - impl AsRef<[u8; L]> for FixedLength { #[inline] fn as_ref(&self) -> &[u8; L] { @@ -103,3 +96,22 @@ impl TryFrom> for [u8; L] { Ok(value.0) } } + +#[cfg(test)] +mod tests { + use crate::iv::FixedLength; + + #[test] + fn test_size() { + let fixed = FixedLength::from([0u8; 16]); + assert_eq!(16, fixed.size()); + + let array = [0u8; 12]; + let fixed = FixedLength::<12>::try_from(array.as_slice()).unwrap(); + assert_eq!(12, fixed.size()); + + assert!(FixedLength::<16>::try_from(array.as_slice()).is_err()); + + assert!(TryInto::<[u8; 12]>::try_into(fixed).is_ok()); + } +} diff --git a/aws-lc-rs/tests/cipher_test.rs b/aws-lc-rs/tests/cipher_test.rs index d2f0c899ca3..72876e5c262 100644 --- a/aws-lc-rs/tests/cipher_test.rs +++ b/aws-lc-rs/tests/cipher_test.rs @@ -261,6 +261,15 @@ cipher_rt!( "00112233445566778899aabbccddeeff" ); +cipher_rt!( + test_rt_aes_128_ctr_17_bytes, + &AES_128, + OperatingMode::CTR, + ctr, + "000102030405060708090a0b0c0d0e0f", + "00112233445566778899aabbccddeeff01" +); + cipher_rt!( test_rt_aes_256_ctr_15_bytes, &AES_256, @@ -288,6 +297,15 @@ cipher_rt!( "eca7285d19f3c20e295378460e8729" ); +cipher_rt!( + test_rt_256_ctr_17_bytes, + &AES_256, + OperatingMode::CTR, + ctr, + "0857db8240ea459bdf660b4cced66d1f2d3734ff2de7b81e92740e65e7cc6a1d", + "eca7285d19f3c20e295378460e872934" +); + padded_cipher_rt!( test_rt_aes_128_cbc_15_bytes, &AES_128, @@ -308,6 +326,16 @@ padded_cipher_rt!( "aece7b5e3c3df1ffc9802d2dfe296dc7" ); +padded_cipher_rt!( + test_rt_128_cbc_17_bytes, + &AES_128, + OperatingMode::CBC, + PaddingStrategy::PKCS7, + cbc_pkcs7, + "95af71f1c63e4a1d0b0b1a27fb978283", + "aece7b5e3c3df1ffc9802d2dfe296dc734" +); + padded_cipher_rt!( test_rt_aes_256_cbc_16_bytes, &AES_256, @@ -317,3 +345,13 @@ padded_cipher_rt!( "d4a8206dcae01242f9db79a4ecfe277d0f7bb8ccbafd8f9809adb39f35aa9b41", "a39c1fdf77ea3e1f18178c0ec237c70a" ); + +padded_cipher_rt!( + test_rt_aes_256_cbc_17_bytes, + &AES_256, + OperatingMode::CBC, + PaddingStrategy::PKCS7, + cbc_pkcs7, + "d4a8206dcae01242f9db79a4ecfe277d0f7bb8ccbafd8f9809adb39f35aa9b41", + "a39c1fdf77ea3e1f18178c0ec237c70a34" +); From 1e02c29030ce813b2b11886bea1fd9218e113422 Mon Sep 17 00:00:00 2001 From: Sean McGrail <549813+skmcgrail@users.noreply.github.com> Date: Tue, 6 Jun 2023 17:05:00 -0700 Subject: [PATCH 31/54] Remove TryFrom conversions for padding to unpadded (#145) --- aws-lc-rs/src/cipher.rs | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index d522defcb82..794f9672fa1 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -83,7 +83,7 @@ use zeroize::Zeroize; #[non_exhaustive] #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum PaddingStrategy { - /// PKCS#7 Padding. + /// PKCS#7 Padding. ([See RFC 5652](https://datatracker.ietf.org/doc/html/rfc5652#section-6.3)) PKCS7, } @@ -365,15 +365,11 @@ impl PaddedBlockEncryptingKey { { self.padding .add_padding(self.algorithm().block_len(), in_out)?; - TryInto::::try_into(self)?.encrypt(in_out.as_mut()) + self.into_encrypting_key()?.encrypt(in_out.as_mut()) } -} - -impl TryFrom for EncryptingKey { - type Error = Unspecified; - fn try_from(value: PaddedBlockEncryptingKey) -> Result { - EncryptingKey::new(value.key, value.mode, Some(value.context)) + fn into_encrypting_key(self) -> Result { + EncryptingKey::new(self.key, self.mode, Some(self.context)) } } @@ -444,10 +440,14 @@ impl PaddedBlockDecryptingKey { pub fn decrypt(self, in_out: &mut [u8]) -> Result<&mut [u8], Unspecified> { let block_len = self.algorithm().block_len(); let padding = self.padding; - let mut in_out = TryInto::::try_into(self)?.decrypt(in_out)?; + let mut in_out = self.into_decrypting_key()?.decrypt(in_out)?; in_out = padding.remove_padding(block_len, in_out)?; Ok(in_out) } + + fn into_decrypting_key(self) -> Result { + DecryptingKey::new(self.key, self.mode, self.mode_input) + } } /// A cipher encryption key that does not perform block padding. @@ -626,14 +626,6 @@ impl DecryptingKey { } } -impl TryFrom for DecryptingKey { - type Error = Unspecified; - - fn try_from(value: PaddedBlockDecryptingKey) -> Result { - DecryptingKey::new(value.key, value.mode, value.mode_input) - } -} - fn encrypt_aes_ctr_mode( key: &UnboundCipherKey, context: CipherContext, From a5653dc356a8252726cece1ed84737d7389ce813 Mon Sep 17 00:00:00 2001 From: Sean McGrail <549813+skmcgrail@users.noreply.github.com> Date: Tue, 6 Jun 2023 18:37:24 -0700 Subject: [PATCH 32/54] Examples use `?`, not `try!`, not `unwrap` (#146) --- aws-lc-rs/src/cipher.rs | 43 +++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index 794f9672fa1..aa658a7c592 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -21,7 +21,10 @@ //! //! ## AES-128 CBC Mode Encryption //! -//! ``` +//! ```rust +//! # use std::error::Error; +//! # +//! # fn main() -> Result<(), Box> { //! use aws_lc_rs::cipher::{ //! PaddedBlockDecryptingKey, PaddedBlockEncryptingKey, UnboundCipherKey, AES_128, //! }; @@ -33,18 +36,25 @@ //! 0xd1, //! ]; //! -//! let key = UnboundCipherKey::new(&AES_128, key_bytes).unwrap(); -//! let encrypting_key = PaddedBlockEncryptingKey::cbc_pkcs7(key).unwrap(); -//! let context = encrypting_key.encrypt(&mut plaintext).unwrap(); +//! let key = UnboundCipherKey::new(&AES_128, key_bytes)?; +//! let encrypting_key = PaddedBlockEncryptingKey::cbc_pkcs7(key)?; +//! let context = encrypting_key.encrypt(&mut plaintext)?; //! -//! let key = UnboundCipherKey::new(&AES_128, key_bytes).unwrap(); -//! let decrypting_key = PaddedBlockDecryptingKey::cbc_pkcs7(key, context).unwrap(); -//! let plaintext = decrypting_key.decrypt(&mut plaintext).unwrap(); +//! let key = UnboundCipherKey::new(&AES_128, key_bytes)?; +//! let decrypting_key = PaddedBlockDecryptingKey::cbc_pkcs7(key, context)?; +//! let plaintext = decrypting_key.decrypt(&mut plaintext)?; +//! # +//! # +//! # Ok(()) +//! # } //! ``` //! //! ## AES-128 CTR Mode Encryption //! -//! ``` +//! ```rust +//! # use std::error::Error; +//! # +//! # fn main() -> Result<(), Box> { //! use aws_lc_rs::cipher::{DecryptingKey, EncryptingKey, UnboundCipherKey, AES_128}; //! //! let mut plaintext = Vec::from("This is a secret message!"); @@ -54,13 +64,16 @@ //! 0xd1, //! ]; //! -//! let key = UnboundCipherKey::new(&AES_128, key_bytes).unwrap(); -//! let encrypting_key = EncryptingKey::ctr(key).unwrap(); -//! let context = encrypting_key.encrypt(&mut plaintext).unwrap(); +//! let key = UnboundCipherKey::new(&AES_128, key_bytes)?; +//! let encrypting_key = EncryptingKey::ctr(key)?; +//! let context = encrypting_key.encrypt(&mut plaintext)?; //! -//! let key = UnboundCipherKey::new(&AES_128, key_bytes).unwrap(); -//! let decrypting_key = DecryptingKey::ctr(key, context).unwrap(); -//! let plaintext = decrypting_key.decrypt(&mut plaintext).unwrap(); +//! let key = UnboundCipherKey::new(&AES_128, key_bytes)?; +//! let decrypting_key = DecryptingKey::ctr(key, context)?; +//! let plaintext = decrypting_key.decrypt(&mut plaintext)?; +//! # +//! # Ok(()) +//! # } //! ``` //! @@ -252,7 +265,7 @@ pub struct UnboundCipherKey { } impl UnboundCipherKey { - /// Constructs a [`UnboundCipherKey`]. + /// Constructs an [`UnboundCipherKey`]. /// /// # Errors /// From e8f11bcc41371e42e0595bf6a9dba1f9143ebdd7 Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Wed, 7 Jun 2023 11:05:08 -0400 Subject: [PATCH 33/54] Support KKDF; Minor doc improvements (#147) --- aws-lc-rs/src/cipher.rs | 71 +++++++++++++++++++++++++++++++----- aws-lc-rs/src/lib.rs | 2 +- aws-lc-rs/tests/hkdf_test.rs | 7 +++- 3 files changed, 68 insertions(+), 12 deletions(-) diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index aa658a7c592..bac555bfb67 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -3,7 +3,7 @@ // Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR ISC -//! Block and Stream Cipher for Encryption and Decryption. +//! Block and Stream Ciphers for Encryption and Decryption. //! //! # 🛑 Read Before Using //! @@ -25,11 +25,13 @@ //! # use std::error::Error; //! # //! # fn main() -> Result<(), Box> { +//! use std::io::Read; //! use aws_lc_rs::cipher::{ //! PaddedBlockDecryptingKey, PaddedBlockEncryptingKey, UnboundCipherKey, AES_128, //! }; //! -//! let mut plaintext = Vec::from("This is a secret message!"); +//! 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, @@ -38,11 +40,12 @@ //! //! let key = UnboundCipherKey::new(&AES_128, key_bytes)?; //! let encrypting_key = PaddedBlockEncryptingKey::cbc_pkcs7(key)?; -//! let context = encrypting_key.encrypt(&mut plaintext)?; +//! let context = encrypting_key.encrypt(&mut in_out_buffer)?; //! //! let key = UnboundCipherKey::new(&AES_128, key_bytes)?; //! let decrypting_key = PaddedBlockDecryptingKey::cbc_pkcs7(key, context)?; -//! let plaintext = decrypting_key.decrypt(&mut plaintext)?; +//! let plaintext = decrypting_key.decrypt(&mut in_out_buffer)?; +//! assert_eq!(original_message, plaintext); //! # //! # //! # Ok(()) @@ -57,7 +60,8 @@ //! # fn main() -> Result<(), Box> { //! use aws_lc_rs::cipher::{DecryptingKey, EncryptingKey, UnboundCipherKey, AES_128}; //! -//! let mut plaintext = Vec::from("This is a secret message!"); +//! 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, @@ -66,11 +70,12 @@ //! //! let key = UnboundCipherKey::new(&AES_128, key_bytes)?; //! let encrypting_key = EncryptingKey::ctr(key)?; -//! let context = encrypting_key.encrypt(&mut plaintext)?; +//! let context = encrypting_key.encrypt(&mut in_out_buffer)?; //! //! let key = UnboundCipherKey::new(&AES_128, key_bytes)?; //! let decrypting_key = DecryptingKey::ctr(key, context)?; -//! let plaintext = decrypting_key.decrypt(&mut plaintext)?; +//! let plaintext = decrypting_key.decrypt(&mut in_out_buffer)?; +//! assert_eq!(original_message, plaintext); //! # //! # Ok(()) //! # } @@ -85,6 +90,8 @@ pub(crate) mod chacha; pub(crate) mod key; use crate::error::Unspecified; +use crate::hkdf; +use crate::hkdf::KeyType; use crate::iv::FixedLength; use aws_lc::{AES_cbc_encrypt, AES_ctr128_encrypt, AES_DECRYPT, AES_ENCRYPT, AES_KEY}; use key::SymmetricCipherKey; @@ -215,14 +222,14 @@ pub struct Algorithm { } /// AES 128-bit cipher -pub const AES_128: Algorithm = Algorithm { +pub static AES_128: Algorithm = Algorithm { id: AlgorithmId::Aes128, key_len: AES_128_KEY_LEN, block_len: AES_BLOCK_LEN, }; /// AES 256-bit cipher -pub const AES_256: Algorithm = Algorithm { +pub static AES_256: Algorithm = Algorithm { id: AlgorithmId::Aes256, key_len: AES_256_KEY_LEN, block_len: AES_BLOCK_LEN, @@ -258,6 +265,31 @@ impl Algorithm { } } +#[allow(clippy::missing_fields_in_debug)] +impl Debug for UnboundCipherKey { + fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { + f.debug_struct("UnboundCipherKey") + .field("algorithm", &self.algorithm) + .finish() + } +} + +impl From> for UnboundCipherKey { + fn from(okm: hkdf::Okm<&'static Algorithm>) -> Self { + let mut key_bytes = [0; AES_256_KEY_LEN]; + let key_bytes = &mut key_bytes[..okm.len().key_len]; + let algorithm = *okm.len(); + okm.fill(key_bytes).unwrap(); + Self::new(algorithm, key_bytes).unwrap() + } +} + +impl KeyType for &'static Algorithm { + fn len(&self) -> usize { + self.key_len + } +} + /// A key bound to a particular cipher algorithm. pub struct UnboundCipherKey { algorithm: &'static Algorithm, @@ -281,7 +313,9 @@ impl UnboundCipherKey { } #[inline] - fn algorithm(&self) -> &'static Algorithm { + #[must_use] + /// Returns the algorithm associated with this key. + pub fn algorithm(&self) -> &'static Algorithm { self.algorithm } } @@ -774,6 +808,23 @@ mod tests { use super::*; use crate::test::from_hex; + #[test] + fn test_debug() { + { + let aes_128_key_bytes = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); + let cipher_key = UnboundCipherKey::new(&AES_128, aes_128_key_bytes.as_slice()).unwrap(); + assert_eq!("UnboundCipherKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 } }", format!("{cipher_key:?}")); + } + + { + let aes_256_key_bytes = + from_hex("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f") + .unwrap(); + let cipher_key = UnboundCipherKey::new(&AES_256, aes_256_key_bytes.as_slice()).unwrap(); + assert_eq!("UnboundCipherKey { algorithm: Algorithm { id: Aes256, key_len: 32, block_len: 16 } }", format!("{cipher_key:?}")); + } + } + fn helper_test_cipher_n_bytes( key: &[u8], alg: &'static Algorithm, diff --git a/aws-lc-rs/src/lib.rs b/aws-lc-rs/src/lib.rs index bdb595fc023..43b3a5aaa57 100644 --- a/aws-lc-rs/src/lib.rs +++ b/aws-lc-rs/src/lib.rs @@ -83,7 +83,7 @@ //! //! #### - bindgen #### //! Causes `aws-lc-sys` or `aws-lc-fips-sys` to generates fresh bindings for AWS-LC instead of using -//! the pre-generated bindings. This feature require `libclang` to be installed. See the +//! the pre-generated bindings. This feature requires `libclang` to be installed. See the //! [requirements](https://rust-lang.github.io/rust-bindgen/requirements.html) //! for [rust-bindgen](https://github.com/rust-lang/rust-bindgen) //! diff --git a/aws-lc-rs/tests/hkdf_test.rs b/aws-lc-rs/tests/hkdf_test.rs index c85dd7294dc..2916778d1c5 100644 --- a/aws-lc-rs/tests/hkdf_test.rs +++ b/aws-lc-rs/tests/hkdf_test.rs @@ -3,7 +3,7 @@ // Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR ISC -use aws_lc_rs::{aead, digest, error, hkdf, hmac, test, test_file}; +use aws_lc_rs::{aead, cipher, digest, error, hkdf, hmac, test, test_file}; #[test] fn hkdf_tests() { @@ -122,6 +122,11 @@ fn hkdf_key_types() { let okm = prk.expand(&[b"info"], aead_alg).unwrap(); let _aead_prk_key = aead::UnboundKey::from(okm); } + + for cipher_alg in [&cipher::AES_256, &cipher::AES_128] { + let okm = prk.expand(&[b"info"], cipher_alg).unwrap(); + let _aead_prk_key = cipher::UnboundCipherKey::from(okm); + } } } From 7d594474b522b42da5fefb2bcd37233c5ebd4d79 Mon Sep 17 00:00:00 2001 From: Sean McGrail <549813+skmcgrail@users.noreply.github.com> Date: Wed, 7 Jun 2023 13:08:06 -0700 Subject: [PATCH 34/54] Implement Debug trait for Cipher types (#148) --- aws-lc-rs/src/cipher.rs | 113 ++++++++++++++++++++++++++++++++++------ 1 file changed, 96 insertions(+), 17 deletions(-) diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index bac555bfb67..58f482f802e 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -202,6 +202,15 @@ impl<'a> TryFrom<&'a CipherContext> for &'a [u8] { } } +impl Debug for CipherContext { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Iv128(_) => write!(f, "Iv128"), + Self::None => write!(f, "None"), + } + } +} + #[non_exhaustive] #[derive(Debug, PartialEq, Eq, Clone, Copy)] /// Cipher algorithm identifier. @@ -420,12 +429,23 @@ impl PaddedBlockEncryptingKey { } } +impl Debug for PaddedBlockEncryptingKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PaddedBlockEncryptingKey") + .field("key", &self.key) + .field("mode", &self.mode) + .field("padding", &self.padding) + .field("context", &self.context) + .finish() + } +} + /// A cipher decryption key that performs block padding. pub struct PaddedBlockDecryptingKey { key: UnboundCipherKey, mode: OperatingMode, padding: PaddingStrategy, - mode_input: CipherContext, + context: CipherContext, } impl PaddedBlockDecryptingKey { @@ -437,18 +457,18 @@ impl PaddedBlockDecryptingKey { /// pub fn cbc_pkcs7( key: UnboundCipherKey, - mode_input: CipherContext, + context: CipherContext, ) -> Result { - PaddedBlockDecryptingKey::new(key, OperatingMode::CBC, PaddingStrategy::PKCS7, mode_input) + PaddedBlockDecryptingKey::new(key, OperatingMode::CBC, PaddingStrategy::PKCS7, context) } fn new( key: UnboundCipherKey, mode: OperatingMode, padding: PaddingStrategy, - mode_input: CipherContext, + context: CipherContext, ) -> Result { - if !key.algorithm().is_valid_cipher_context(mode, &mode_input) { + if !key.algorithm().is_valid_cipher_context(mode, &context) { return Err(Unspecified); } @@ -456,7 +476,7 @@ impl PaddedBlockDecryptingKey { key, mode, padding, - mode_input, + context, }) } @@ -493,7 +513,18 @@ impl PaddedBlockDecryptingKey { } fn into_decrypting_key(self) -> Result { - DecryptingKey::new(self.key, self.mode, self.mode_input) + DecryptingKey::new(self.key, self.mode, self.context) + } +} + +impl Debug for PaddedBlockDecryptingKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PaddedBlockDecryptingKey") + .field("key", &self.key) + .field("mode", &self.mode) + .field("padding", &self.padding) + .field("context", &self.context) + .finish() } } @@ -591,11 +622,21 @@ impl EncryptingKey { } } +impl Debug for EncryptingKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("EncryptingKey") + .field("key", &self.key) + .field("mode", &self.mode) + .field("context", &self.context) + .finish() + } +} + /// A cipher decryption key that does not perform block padding. pub struct DecryptingKey { key: UnboundCipherKey, mode: OperatingMode, - mode_input: CipherContext, + context: CipherContext, } impl DecryptingKey { @@ -614,17 +655,13 @@ impl DecryptingKey { fn new( key: UnboundCipherKey, mode: OperatingMode, - mode_input: CipherContext, + context: CipherContext, ) -> Result { - if !key.algorithm().is_valid_cipher_context(mode, &mode_input) { + if !key.algorithm().is_valid_cipher_context(mode, &context) { return Err(Unspecified); } - Ok(DecryptingKey { - key, - mode, - mode_input, - }) + Ok(DecryptingKey { key, mode, context }) } /// Returns the cipher algorithm. @@ -661,18 +698,28 @@ impl DecryptingKey { match self.mode { OperatingMode::CBC => match self.key.algorithm().id() { AlgorithmId::Aes128 | AlgorithmId::Aes256 => { - decrypt_aes_cbc_mode(&self.key, self.mode_input, in_out).map(|_| in_out) + decrypt_aes_cbc_mode(&self.key, self.context, in_out).map(|_| in_out) } }, OperatingMode::CTR => match self.key.algorithm().id() { AlgorithmId::Aes128 | AlgorithmId::Aes256 => { - decrypt_aes_ctr_mode(&self.key, self.mode_input, in_out).map(|_| in_out) + decrypt_aes_ctr_mode(&self.key, self.context, in_out).map(|_| in_out) } }, } } } +impl Debug for DecryptingKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("DecryptingKey") + .field("key", &self.key) + .field("mode", &self.mode) + .field("context", &self.context) + .finish() + } +} + fn encrypt_aes_ctr_mode( key: &UnboundCipherKey, context: CipherContext, @@ -823,6 +870,38 @@ mod tests { let cipher_key = UnboundCipherKey::new(&AES_256, aes_256_key_bytes.as_slice()).unwrap(); assert_eq!("UnboundCipherKey { algorithm: Algorithm { id: Aes256, key_len: 32, block_len: 16 } }", format!("{cipher_key:?}")); } + + { + let key_bytes = &[0u8; 16]; + let key = PaddedBlockEncryptingKey::cbc_pkcs7( + UnboundCipherKey::new(&AES_128, key_bytes).unwrap(), + ) + .unwrap(); + assert_eq!("PaddedBlockEncryptingKey { key: UnboundCipherKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 } }, mode: CBC, padding: PKCS7, context: Iv128 }", format!("{key:?}")); + let mut data = vec![0u8; 16]; + let context = key.encrypt(&mut data).unwrap(); + assert_eq!("Iv128", format!("{context:?}")); + let key = PaddedBlockDecryptingKey::cbc_pkcs7( + UnboundCipherKey::new(&AES_128, key_bytes).unwrap(), + context, + ) + .unwrap(); + assert_eq!("PaddedBlockDecryptingKey { key: UnboundCipherKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 } }, mode: CBC, padding: PKCS7, context: Iv128 }", format!("{key:?}")); + } + + { + let key_bytes = &[0u8; 16]; + let key = + EncryptingKey::ctr(UnboundCipherKey::new(&AES_128, key_bytes).unwrap()).unwrap(); + assert_eq!("EncryptingKey { key: UnboundCipherKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 } }, mode: CTR, context: Iv128 }", format!("{key:?}")); + let mut data = vec![0u8; 16]; + let context = key.encrypt(&mut data).unwrap(); + assert_eq!("Iv128", format!("{context:?}")); + let key = + DecryptingKey::ctr(UnboundCipherKey::new(&AES_128, key_bytes).unwrap(), context) + .unwrap(); + assert_eq!("DecryptingKey { key: UnboundCipherKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 } }, mode: CTR, context: Iv128 }", format!("{key:?}")); + } } fn helper_test_cipher_n_bytes( From 561f28b43e7a6c4fdc2030d171cd981a9416ce7f Mon Sep 17 00:00:00 2001 From: Sean McGrail <549813+skmcgrail@users.noreply.github.com> Date: Fri, 9 Jun 2023 09:43:37 -0700 Subject: [PATCH 35/54] Provide better documentation for CipherContext (#151) --- aws-lc-rs/src/cipher.rs | 65 +++++++++++++++++++++++++++++++++++++++++ aws-lc-rs/src/iv.rs | 2 +- 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index 58f482f802e..ba3bede4b9f 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -182,6 +182,71 @@ pub enum OperatingMode { } /// The contextual data used to encrypted/decrypt data. +/// +/// # Examples +/// +/// ## Constructing a `CipherContext` for decryption. +/// +/// ```rust +/// # use std::error::Error; +/// # fn main() -> Result<(), Box> { +/// use aws_lc_rs::cipher::{CipherContext, DecryptingKey, UnboundCipherKey, AES_128}; +/// use aws_lc_rs::iv::FixedLength; +/// +/// let context = CipherContext::Iv128(FixedLength::<16>::from(&[ +/// 0x8d, 0xdb, 0x7d, 0xf1, 0x56, 0xf5, 0x1c, 0xde, 0x63, 0xe3, 0x4a, 0x34, 0xb0, 0xdf, 0x28, +/// 0xf0, +/// ])); +/// +/// let ciphertext: &[u8] = &[ +/// 0x79, 0x8c, 0x04, 0x58, 0xcf, 0x98, 0xb1, 0xe9, 0x97, 0x6b, 0xa1, 0xce, +/// ]; +/// +/// let mut in_out_buffer = Vec::from(ciphertext); +/// +/// let key = UnboundCipherKey::new( +/// &AES_128, +/// &[ +/// 0x5b, 0xfc, 0xe7, 0x5e, 0x57, 0xc5, 0x4d, 0xda, 0x2d, 0xd4, 0x7e, 0x07, 0x0a, 0xef, +/// 0x43, 0x29, +/// ], +/// )?; +/// let decrypting_key = DecryptingKey::ctr(key, context)?; +/// let plaintext = decrypting_key.decrypt(&mut in_out_buffer)?; +/// assert_eq!("Hello World!".as_bytes(), plaintext); +/// +/// # Ok(()) +/// # } +/// ``` +/// +/// ## Getting an immutable reference to an IV slice. +/// +/// `CipherContext` implements `TryFrom<&CipherContext>` for `&[u8]` allowing immutable references +/// to IV bytes returned from cipher encryption operations. +/// +/// ```rust +/// # use std::error::Error; +/// # fn main() -> Result<(), Box> { +/// use aws_lc_rs::cipher::CipherContext; +/// # use aws_lc_rs::cipher::{EncryptingKey, UnboundCipherKey, AES_128}; +/// # let original_message = "Hello World!".as_bytes(); +/// # let mut in_out_buffer = Vec::from(original_message); +/// # let key_bytes: &[u8] = &[ +/// # 0x68, 0xf9, 0x46, 0x1a, 0xde, 0x8d, 0x35, 0x38, 0x7b, 0x50, 0xcc, 0x9a, 0x36, 0x64, 0xf8, +/// # 0x9d, +/// # ]; +/// # let key = UnboundCipherKey::new(&AES_128, key_bytes)?; +/// # let encrypting_key = EncryptingKey::ctr(key)?; +/// # +/// let context: CipherContext = encrypting_key.encrypt(&mut in_out_buffer)?; +/// let iv_bytes: &[u8] = (&context).try_into()?; +/// assert_eq!(16, iv_bytes.len()); +/// # +/// # Ok(()) +/// # } +/// ``` +/// +/// #[non_exhaustive] pub enum CipherContext { /// A 128-bit Initalization Vector. diff --git a/aws-lc-rs/src/iv.rs b/aws-lc-rs/src/iv.rs index 34b741fb5dd..7e0191eaa2a 100644 --- a/aws-lc-rs/src/iv.rs +++ b/aws-lc-rs/src/iv.rs @@ -35,7 +35,7 @@ impl FixedLength { Self(value) } - /// Returns the size of the nonce in bytes. + /// Returns the size of the iv in bytes. #[allow(clippy::must_use_candidate)] pub fn size(&self) -> usize { L From 586018acde06b82a9ecda6d0bad4092658c6a1ad Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Mon, 12 Jun 2023 08:24:05 -0400 Subject: [PATCH 36/54] Update aws-lc-rs/src/cipher/key.rs Co-authored-by: Samuel Chiang --- aws-lc-rs/src/cipher/key.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aws-lc-rs/src/cipher/key.rs b/aws-lc-rs/src/cipher/key.rs index 305540cb70c..1767321484a 100644 --- a/aws-lc-rs/src/cipher/key.rs +++ b/aws-lc-rs/src/cipher/key.rs @@ -1,3 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 OR ISC + use crate::cipher::aes::{encrypt_block_aes, Aes128Key, Aes256Key}; use crate::cipher::block::Block; use crate::cipher::chacha::ChaCha20Key; From 01e49e35de92c9fa6ddb6fe13c7d0a488eeccb78 Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Mon, 12 Jun 2023 08:24:22 -0400 Subject: [PATCH 37/54] Update aws-lc-rs/tests/cipher_test.rs Co-authored-by: Samuel Chiang --- aws-lc-rs/tests/cipher_test.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aws-lc-rs/tests/cipher_test.rs b/aws-lc-rs/tests/cipher_test.rs index 72876e5c262..5e56bd24d31 100644 --- a/aws-lc-rs/tests/cipher_test.rs +++ b/aws-lc-rs/tests/cipher_test.rs @@ -1,3 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 OR ISC + use aws_lc_rs::cipher::{ CipherContext, DecryptingKey, EncryptingKey, OperatingMode, PaddedBlockDecryptingKey, PaddedBlockEncryptingKey, PaddingStrategy, UnboundCipherKey, AES_128, AES_256, From c134c7b6c98259acfb4451a7718db180ace6f233 Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Mon, 12 Jun 2023 08:24:48 -0400 Subject: [PATCH 38/54] Update aws-lc-rs/tests/hkdf_test.rs Co-authored-by: Samuel Chiang --- aws-lc-rs/tests/hkdf_test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws-lc-rs/tests/hkdf_test.rs b/aws-lc-rs/tests/hkdf_test.rs index 2916778d1c5..e9eb1df82c8 100644 --- a/aws-lc-rs/tests/hkdf_test.rs +++ b/aws-lc-rs/tests/hkdf_test.rs @@ -125,7 +125,7 @@ fn hkdf_key_types() { for cipher_alg in [&cipher::AES_256, &cipher::AES_128] { let okm = prk.expand(&[b"info"], cipher_alg).unwrap(); - let _aead_prk_key = cipher::UnboundCipherKey::from(okm); + let _aes_prk_key = cipher::UnboundCipherKey::from(okm); } } } From 3599aa33a975b8301bff9e05731989f470b19ba5 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Mon, 12 Jun 2023 08:31:24 -0400 Subject: [PATCH 39/54] Use MAX_CIPHER_KEY_LEN --- aws-lc-rs/src/cipher.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index ba3bede4b9f..4ccec01bd1b 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -164,6 +164,8 @@ pub const AES_128_KEY_LEN: usize = 16; /// The number of bytes in an AES 256-bit key pub const AES_256_KEY_LEN: usize = 32; +const MAX_CIPHER_KEY_LEN: usize = AES_256_KEY_LEN; + /// The number of bytes for an AES initalization vector (IV) pub const AES_IV_LEN: usize = 16; const AES_BLOCK_LEN: usize = 16; @@ -350,7 +352,7 @@ impl Debug for UnboundCipherKey { impl From> for UnboundCipherKey { fn from(okm: hkdf::Okm<&'static Algorithm>) -> Self { - let mut key_bytes = [0; AES_256_KEY_LEN]; + let mut key_bytes = [0; MAX_CIPHER_KEY_LEN]; let key_bytes = &mut key_bytes[..okm.len().key_len]; let algorithm = *okm.len(); okm.fill(key_bytes).unwrap(); From fbb03fc7ee82d8e16bc8f4db6e267a3735cb2050 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Mon, 12 Jun 2023 08:47:58 -0400 Subject: [PATCH 40/54] Use 16-byte padding-buffer to avoid extra heap allocation --- aws-lc-rs/src/cipher.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index 4ccec01bd1b..ba44a9459c5 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -114,17 +114,20 @@ impl PaddingStrategy { { match self { PaddingStrategy::PKCS7 => { + let mut padding_buffer = [MAX_CIPHER_BLOCK_LEN_U8; MAX_CIPHER_BLOCK_LEN]; + let in_out_len = in_out.as_mut().len(); // This implements PKCS#7 padding scheme, used by aws-lc if we were using EVP_CIPHER API's let remainder = in_out_len % block_len; if remainder == 0 { - let block_size: u8 = block_len.try_into().map_err(|_| Unspecified)?; - in_out.extend(vec![block_size; block_len].iter()); + // Possible heap allocation here :( + in_out.extend(padding_buffer.iter()); } else { let padding_size = block_len - remainder; let v: u8 = padding_size.try_into().map_err(|_| Unspecified)?; - // Heap allocation :( - in_out.extend(vec![v; padding_size].iter()); + padding_buffer.fill(v); + // Possible heap allocation here :( + in_out.extend(padding_buffer[0..padding_size].iter()); } } } @@ -170,6 +173,9 @@ const MAX_CIPHER_KEY_LEN: usize = AES_256_KEY_LEN; pub const AES_IV_LEN: usize = 16; const AES_BLOCK_LEN: usize = 16; +const MAX_CIPHER_BLOCK_LEN: usize = AES_BLOCK_LEN; +const MAX_CIPHER_BLOCK_LEN_U8: u8 = AES_BLOCK_LEN as u8; + const IV_LEN_128_BIT: usize = 16; /// The cipher operating mode. From 2c2fb71511a3f87251f022c63608b31da32baf9f Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Mon, 12 Jun 2023 08:56:44 -0400 Subject: [PATCH 41/54] Satisfy clippy --- aws-lc-rs/src/cipher.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index ba44a9459c5..f50b4bebe5d 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -174,7 +174,7 @@ pub const AES_IV_LEN: usize = 16; const AES_BLOCK_LEN: usize = 16; const MAX_CIPHER_BLOCK_LEN: usize = AES_BLOCK_LEN; -const MAX_CIPHER_BLOCK_LEN_U8: u8 = AES_BLOCK_LEN as u8; +const MAX_CIPHER_BLOCK_LEN_U8: u8 = 16; // AES_BLOCK_LEN const IV_LEN_128_BIT: usize = 16; From c565627569445ece45676b36b8ff67c411a02c6f Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Mon, 12 Jun 2023 13:48:16 -0400 Subject: [PATCH 42/54] No branch needed --- aws-lc-rs/src/cipher.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index f50b4bebe5d..7a53396a334 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -114,21 +114,16 @@ impl PaddingStrategy { { match self { PaddingStrategy::PKCS7 => { - let mut padding_buffer = [MAX_CIPHER_BLOCK_LEN_U8; MAX_CIPHER_BLOCK_LEN]; + let mut padding_buffer = [0u8; MAX_CIPHER_BLOCK_LEN]; let in_out_len = in_out.as_mut().len(); // This implements PKCS#7 padding scheme, used by aws-lc if we were using EVP_CIPHER API's let remainder = in_out_len % block_len; - if remainder == 0 { - // Possible heap allocation here :( - in_out.extend(padding_buffer.iter()); - } else { - let padding_size = block_len - remainder; - let v: u8 = padding_size.try_into().map_err(|_| Unspecified)?; - padding_buffer.fill(v); - // Possible heap allocation here :( - in_out.extend(padding_buffer[0..padding_size].iter()); - } + let padding_size = block_len - remainder; + let v: u8 = padding_size.try_into().map_err(|_| Unspecified)?; + padding_buffer.fill(v); + // Possible heap allocation here :( + in_out.extend(padding_buffer[0..padding_size].iter()); } } Ok(()) @@ -174,7 +169,6 @@ pub const AES_IV_LEN: usize = 16; const AES_BLOCK_LEN: usize = 16; const MAX_CIPHER_BLOCK_LEN: usize = AES_BLOCK_LEN; -const MAX_CIPHER_BLOCK_LEN_U8: u8 = 16; // AES_BLOCK_LEN const IV_LEN_128_BIT: usize = 16; From fd70cec7b45e4c9ab8418e9c8ae5cf2ad15fa86b Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Wed, 14 Jun 2023 09:08:13 -0400 Subject: [PATCH 43/54] =?UTF-8?q?Spelling:=20Initialize=20=F0=9F=A4=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aws-lc-rs/examples/cipher.rs | 8 ++++---- aws-lc-rs/src/cipher.rs | 8 ++++---- aws-lc-rs/src/iv.rs | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/aws-lc-rs/examples/cipher.rs b/aws-lc-rs/examples/cipher.rs index 3efad0ebb04..e70a0c763bb 100644 --- a/aws-lc-rs/examples/cipher.rs +++ b/aws-lc-rs/examples/cipher.rs @@ -111,7 +111,7 @@ fn aes_ctr_encrypt(key: &[u8], iv: Option, plaintext: String) -> Result< } None => EncryptingKey::ctr(key), } - .map_err(|_| "failed to initalized aes encryption")?; + .map_err(|_| "failed to initialized aes encryption")?; let mut ciphertext = Vec::from(plaintext); @@ -141,7 +141,7 @@ fn aes_ctr_decrypt(key: &[u8], iv: String, ciphertext: String) -> Result<(), &'s }; let key = DecryptingKey::ctr(key, CipherContext::Iv128(iv)) - .map_err(|_| "failed to initalized aes decryption")?; + .map_err(|_| "failed to initialized aes decryption")?; let mut ciphertext = hex::decode(ciphertext).map_err(|_| "ciphertext is not valid hex encoding")?; @@ -173,7 +173,7 @@ fn aes_cbc_encrypt(key: &[u8], iv: Option, plaintext: String) -> Result< } None => PaddedBlockEncryptingKey::cbc_pkcs7(key), } - .map_err(|_| "failed to initalized aes encryption")?; + .map_err(|_| "failed to initialized aes encryption")?; let mut ciphertext = Vec::from(plaintext); @@ -203,7 +203,7 @@ fn aes_cbc_decrypt(key: &[u8], iv: String, ciphertext: String) -> Result<(), &'s }; let key = PaddedBlockDecryptingKey::cbc_pkcs7(key, CipherContext::Iv128(iv)) - .map_err(|_| "failed to initalized aes decryption")?; + .map_err(|_| "failed to initialized aes decryption")?; let mut ciphertext = hex::decode(ciphertext).map_err(|_| "ciphertext is not valid hex encoding")?; diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index 7a53396a334..86a2781cf96 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -164,7 +164,7 @@ pub const AES_256_KEY_LEN: usize = 32; const MAX_CIPHER_KEY_LEN: usize = AES_256_KEY_LEN; -/// The number of bytes for an AES initalization vector (IV) +/// The number of bytes for an AES initialization vector (IV) pub const AES_IV_LEN: usize = 16; const AES_BLOCK_LEN: usize = 16; @@ -251,7 +251,7 @@ pub enum OperatingMode { /// #[non_exhaustive] pub enum CipherContext { - /// A 128-bit Initalization Vector. + /// A 128-bit Initialization Vector. Iv128(FixedLength), /// No input to the cipher mode. @@ -416,7 +416,7 @@ impl PaddedBlockEncryptingKey { } /// Constructs a new `PaddedBlockEncryptingKey` cipher with chaining block cipher (CBC) mode. - /// The users provided context will be used for the CBC initalization-vector. + /// The users provided context will be used for the CBC initialization-vector. /// Plaintext data is padded following the PKCS#7 scheme. /// /// # Errors @@ -613,7 +613,7 @@ impl EncryptingKey { } /// Constructs an `EncryptingKey` operating in counter (CTR) mode using the provided key. - /// The users provided context will be used for the CTR mode initalization-vector. + /// The users provided context will be used for the CTR mode initialization-vector. /// /// # Errors /// * [`Unspecified`]: Returned if there is an error creating the `EncryptingKey`. diff --git a/aws-lc-rs/src/iv.rs b/aws-lc-rs/src/iv.rs index 7e0191eaa2a..01967b048ac 100644 --- a/aws-lc-rs/src/iv.rs +++ b/aws-lc-rs/src/iv.rs @@ -10,7 +10,7 @@ use crate::error::Unspecified; use crate::{error, rand}; use zeroize::Zeroize; -/// An initalization vector that must be unique for the lifetime of the associated key +/// An initialization vector that must be unique for the lifetime of the associated key /// it is used with. pub struct FixedLength([u8; L]); From 7a05c7c8992ac5ef85685eabdb73a4e37eeeeb89 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Wed, 14 Jun 2023 09:33:21 -0400 Subject: [PATCH 44/54] Fix typos; Rename redundant tests --- aws-lc-rs/src/cipher.rs | 4 ++-- aws-lc-rs/tests/cipher_test.rs | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index 86a2781cf96..9b5e1a9fb5b 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -409,7 +409,7 @@ impl PaddedBlockEncryptingKey { /// Plaintext data is padded following the PKCS#7 scheme. /// /// # Errors - /// * [`Unspecified`]: Returned if there is an error cosntruct a `PaddedBlockEncryptingKey`. + /// * [`Unspecified`]: Returned if there is an error cosntructing a `PaddedBlockEncryptingKey`. /// pub fn cbc_pkcs7(key: UnboundCipherKey) -> Result { PaddedBlockEncryptingKey::new(key, OperatingMode::CBC, PaddingStrategy::PKCS7, None) @@ -606,7 +606,7 @@ impl EncryptingKey { /// Constructs an `EncryptingKey` operating in counter (CTR) mode using the provided key. /// /// # Errors - /// * [`Unspecified`]: Returned if there is an error construct the `EncryptingKey`. + /// * [`Unspecified`]: Returned if there is an error constructing the `EncryptingKey`. /// pub fn ctr(key: UnboundCipherKey) -> Result { EncryptingKey::new(key, OperatingMode::CTR, None) diff --git a/aws-lc-rs/tests/cipher_test.rs b/aws-lc-rs/tests/cipher_test.rs index 5e56bd24d31..0d47b1c9b50 100644 --- a/aws-lc-rs/tests/cipher_test.rs +++ b/aws-lc-rs/tests/cipher_test.rs @@ -189,7 +189,7 @@ cipher_kat!( ); cipher_kat!( - test_kat_256_ctr_15_bytes, + test_kat_aes_256_ctr_15_bytes_2, &AES_256, OperatingMode::CTR, ctr, @@ -212,7 +212,7 @@ padded_cipher_kat!( ); padded_cipher_kat!( - test_kat_128_cbc_16_bytes, + test_kat_aes_128_cbc_16_bytes_2, &AES_128, OperatingMode::CBC, PaddingStrategy::PKCS7, @@ -292,7 +292,7 @@ cipher_rt!( ); cipher_rt!( - test_rt_256_ctr_15_bytes, + test_rt_aes_256_ctr_15_bytes_2, &AES_256, OperatingMode::CTR, ctr, @@ -301,7 +301,7 @@ cipher_rt!( ); cipher_rt!( - test_rt_256_ctr_17_bytes, + test_rt_aes_256_ctr_17_bytes, &AES_256, OperatingMode::CTR, ctr, @@ -320,7 +320,7 @@ padded_cipher_rt!( ); padded_cipher_rt!( - test_rt_128_cbc_16_bytes, + test_rt_aes_128_cbc_16_bytes_2, &AES_128, OperatingMode::CBC, PaddingStrategy::PKCS7, From 66fecc72178116ad9a450454d93908b482bd0132 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Wed, 14 Jun 2023 10:14:56 -0400 Subject: [PATCH 45/54] Use constants for key/iv lengths --- aws-lc-rs/examples/cipher.rs | 6 ++++-- aws-lc-rs/src/cipher/key.rs | 13 ++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/aws-lc-rs/examples/cipher.rs b/aws-lc-rs/examples/cipher.rs index e70a0c763bb..7e4d8d79ace 100644 --- a/aws-lc-rs/examples/cipher.rs +++ b/aws-lc-rs/examples/cipher.rs @@ -24,6 +24,7 @@ //! $ cargo run --example cipher -- --mode cbc --key 6489d8ce0c4facf18b872705a05d5ee4 decrypt --iv 5cd56fb752830ec2459889226c5431bd 6311c14e8104730be124ce1e57e51fe3 //! Hello World //! ``` +use aws_lc_rs::cipher::{AES_128_KEY_LEN, AES_IV_LEN}; use aws_lc_rs::{ cipher::{ CipherContext, DecryptingKey, EncryptingKey, PaddedBlockDecryptingKey, @@ -81,7 +82,7 @@ fn main() -> Result<(), &'static str> { } } } else { - let mut v = vec![0u8; 16]; + let mut v = vec![0u8; AES_128_KEY_LEN]; aws_lc_rs::rand::fill(v.as_mut_slice()).map_err(|_| "failed to generate key")?; v }; @@ -166,7 +167,8 @@ fn aes_cbc_encrypt(key: &[u8], iv: Option, plaintext: String) -> Result< Some(iv) => { let iv = { let v = hex::decode(iv).map_err(|_| "invalid iv")?; - let v: FixedLength<16> = v.as_slice().try_into().map_err(|_| "invalid iv")?; + let v: FixedLength = + v.as_slice().try_into().map_err(|_| "invalid iv")?; v }; PaddedBlockEncryptingKey::less_safe_cbc_pkcs7(key, CipherContext::Iv128(iv)) diff --git a/aws-lc-rs/src/cipher/key.rs b/aws-lc-rs/src/cipher/key.rs index 1767321484a..4aa27a4d15f 100644 --- a/aws-lc-rs/src/cipher/key.rs +++ b/aws-lc-rs/src/cipher/key.rs @@ -4,13 +4,12 @@ use crate::cipher::aes::{encrypt_block_aes, Aes128Key, Aes256Key}; use crate::cipher::block::Block; use crate::cipher::chacha::ChaCha20Key; -use crate::cipher::AES_128_KEY_LEN; +use crate::cipher::{AES_128_KEY_LEN, AES_256_KEY_LEN}; use crate::error::Unspecified; use aws_lc::{AES_set_decrypt_key, AES_set_encrypt_key, AES_KEY}; use core::ptr::copy_nonoverlapping; use std::mem::{size_of, transmute, MaybeUninit}; use std::os::raw::c_uint; -use std::ptr; use zeroize::Zeroize; pub(crate) enum SymmetricCipherKey { @@ -85,8 +84,8 @@ impl SymmetricCipherKey { } let dec_key = dec_key.assume_init(); - let mut kb = MaybeUninit::<[u8; 16]>::uninit(); - ptr::copy_nonoverlapping(key_bytes.as_ptr(), kb.as_mut_ptr().cast(), 16); + let mut kb = MaybeUninit::<[u8; AES_128_KEY_LEN]>::uninit(); + copy_nonoverlapping(key_bytes.as_ptr(), kb.as_mut_ptr().cast(), AES_128_KEY_LEN); Ok(SymmetricCipherKey::Aes128 { raw_key: Aes128Key(kb.assume_init()), enc_key, @@ -96,7 +95,7 @@ impl SymmetricCipherKey { } pub(crate) fn aes256(key_bytes: &[u8]) -> Result { - if key_bytes.len() != 32 { + if key_bytes.len() != AES_256_KEY_LEN { return Err(Unspecified); } unsafe { @@ -122,8 +121,8 @@ impl SymmetricCipherKey { } let dec_key = dec_key.assume_init(); - let mut kb = MaybeUninit::<[u8; 32]>::uninit(); - copy_nonoverlapping(key_bytes.as_ptr(), kb.as_mut_ptr().cast(), 32); + let mut kb = MaybeUninit::<[u8; AES_256_KEY_LEN]>::uninit(); + copy_nonoverlapping(key_bytes.as_ptr(), kb.as_mut_ptr().cast(), AES_256_KEY_LEN); Ok(SymmetricCipherKey::Aes256 { raw_key: Aes256Key(kb.assume_init()), enc_key, From 2aa68449ea928aa5fe35c557feeed26c79510e6b Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Wed, 14 Jun 2023 10:38:58 -0400 Subject: [PATCH 46/54] Separate constants for AES CTR/CBC IV lengths --- aws-lc-rs/examples/cipher.rs | 4 ++-- aws-lc-rs/src/cipher.rs | 15 +++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/aws-lc-rs/examples/cipher.rs b/aws-lc-rs/examples/cipher.rs index 7e4d8d79ace..df7a80627d4 100644 --- a/aws-lc-rs/examples/cipher.rs +++ b/aws-lc-rs/examples/cipher.rs @@ -24,7 +24,7 @@ //! $ cargo run --example cipher -- --mode cbc --key 6489d8ce0c4facf18b872705a05d5ee4 decrypt --iv 5cd56fb752830ec2459889226c5431bd 6311c14e8104730be124ce1e57e51fe3 //! Hello World //! ``` -use aws_lc_rs::cipher::{AES_128_KEY_LEN, AES_IV_LEN}; +use aws_lc_rs::cipher::{AES_128_KEY_LEN, AES_CBC_IV_LEN}; use aws_lc_rs::{ cipher::{ CipherContext, DecryptingKey, EncryptingKey, PaddedBlockDecryptingKey, @@ -167,7 +167,7 @@ fn aes_cbc_encrypt(key: &[u8], iv: Option, plaintext: String) -> Result< Some(iv) => { let iv = { let v = hex::decode(iv).map_err(|_| "invalid iv")?; - let v: FixedLength = + let v: FixedLength = v.as_slice().try_into().map_err(|_| "invalid iv")?; v }; diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index 9b5e1a9fb5b..e8770339751 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -164,8 +164,11 @@ pub const AES_256_KEY_LEN: usize = 32; const MAX_CIPHER_KEY_LEN: usize = AES_256_KEY_LEN; -/// The number of bytes for an AES initialization vector (IV) -pub const AES_IV_LEN: usize = 16; +/// The number of bytes for an AES-CBC initialization vector (IV) +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; const AES_BLOCK_LEN: usize = 16; const MAX_CIPHER_BLOCK_LEN: usize = AES_BLOCK_LEN; @@ -748,7 +751,7 @@ impl DecryptingKey { /// /// # Errors /// * [`Unspecified`]: Returned if cipher mode requires input to be a multiple of the block length, - /// and `in_out.len()` is not. Otherwise returned if decryption fails. + /// and `in_out.len()` is not. Also returned if decryption fails. /// pub fn decrypt(self, in_out: &mut [u8]) -> Result<&mut [u8], Unspecified> { let block_len = self.algorithm().block_len(); @@ -801,7 +804,7 @@ fn encrypt_aes_ctr_mode( }; let mut iv = { - let mut iv = [0u8; AES_IV_LEN]; + let mut iv = [0u8; AES_CTR_IV_LEN]; iv.copy_from_slice((&context).try_into()?); iv }; @@ -837,7 +840,7 @@ fn encrypt_aes_cbc_mode( }; let mut iv = { - let mut iv = [0u8; AES_IV_LEN]; + let mut iv = [0u8; AES_CBC_IV_LEN]; iv.copy_from_slice((&context).try_into()?); iv }; @@ -862,7 +865,7 @@ fn decrypt_aes_cbc_mode( }; let mut iv = { - let mut iv = [0u8; AES_IV_LEN]; + let mut iv = [0u8; AES_CBC_IV_LEN]; iv.copy_from_slice((&context).try_into()?); iv }; From 7438df08277a1030178d3ca8f007f27ff2900d59 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Wed, 14 Jun 2023 12:46:06 -0400 Subject: [PATCH 47/54] Remove unnecessary from FixedLength --- aws-lc-rs/src/aead/nonce.rs | 6 ++---- aws-lc-rs/src/iv.rs | 25 +++---------------------- 2 files changed, 5 insertions(+), 26 deletions(-) diff --git a/aws-lc-rs/src/aead/nonce.rs b/aws-lc-rs/src/aead/nonce.rs index 7fd57207010..0ef3b4365d5 100644 --- a/aws-lc-rs/src/aead/nonce.rs +++ b/aws-lc-rs/src/aead/nonce.rs @@ -25,9 +25,7 @@ impl Nonce { /// `error::Unspecified` when byte slice length is not `NONCE_LEN` #[inline] pub fn try_assume_unique_for_key(value: &[u8]) -> Result { - Ok(Self(FixedLength::::try_assume_unique_for_key( - value, - )?)) + Ok(Self(FixedLength::::try_from(value)?)) } /// Constructs a `Nonce` with the given value, assuming that the value is @@ -35,7 +33,7 @@ impl Nonce { #[inline] #[must_use] pub fn assume_unique_for_key(value: [u8; NONCE_LEN]) -> Self { - Self(FixedLength::::assume_unique_for_key(value)) + Self(FixedLength::::from(value)) } } diff --git a/aws-lc-rs/src/iv.rs b/aws-lc-rs/src/iv.rs index 01967b048ac..f2a11335efd 100644 --- a/aws-lc-rs/src/iv.rs +++ b/aws-lc-rs/src/iv.rs @@ -7,7 +7,7 @@ //! Initialization Vector (IV) cryptographic primitives use crate::error::Unspecified; -use crate::{error, rand}; +use crate::rand; use zeroize::Zeroize; /// An initialization vector that must be unique for the lifetime of the associated key @@ -15,26 +15,6 @@ use zeroize::Zeroize; pub struct FixedLength([u8; L]); impl FixedLength { - /// Constructs a [`FixedLength`] with the given value, assuming that the value is - /// unique for the lifetime of the key it is being used with. - /// - /// Fails if `value` isn't `L` bytes long. - /// # Errors - /// `error::Unspecified` when byte slice length is not `L` - #[inline] - pub fn try_assume_unique_for_key(value: &[u8]) -> Result { - let value: &[u8; L] = value.try_into()?; - Ok(Self::assume_unique_for_key(*value)) - } - - /// Constructs a [`FixedLength`] with the given value, assuming that the value is - /// unique for the lifetime of the key it is being used with. - #[inline] - #[must_use] - pub fn assume_unique_for_key(value: [u8; L]) -> Self { - Self(value) - } - /// Returns the size of the iv in bytes. #[allow(clippy::must_use_candidate)] pub fn size(&self) -> usize { @@ -85,7 +65,8 @@ impl TryFrom<&[u8]> for FixedLength { type Error = Unspecified; fn try_from(value: &[u8]) -> Result { - FixedLength::::try_assume_unique_for_key(value) + let value: &[u8; L] = value.try_into()?; + Ok(Self::from(*value)) } } From 255c1a146149222b1cbb8c5414141153ac4a80b1 Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Wed, 14 Jun 2023 15:52:17 -0400 Subject: [PATCH 48/54] Run openssl benchmarks with openssl-benchmarks feature (#154) --- aws-lc-rs/Cargo.toml | 1 + aws-lc-rs/benches/cipher_benchmark.rs | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/aws-lc-rs/Cargo.toml b/aws-lc-rs/Cargo.toml index 9b110e9eb7b..b58c39ed9a9 100644 --- a/aws-lc-rs/Cargo.toml +++ b/aws-lc-rs/Cargo.toml @@ -28,6 +28,7 @@ default = ["aws-lc-sys", "alloc", "ring-io", "ring-sig-verify"] ring-io = ["dep:untrusted"] ring-sig-verify = ["dep:untrusted"] ring-benchmarks = [] +openssl-benchmarks = [] bindgen = ["aws-lc-sys?/bindgen", "aws-lc-fips-sys?/bindgen"] asan = ["aws-lc-sys?/asan", "aws-lc-fips-sys?/asan"] diff --git a/aws-lc-rs/benches/cipher_benchmark.rs b/aws-lc-rs/benches/cipher_benchmark.rs index ee9d1075c04..c50d1986206 100644 --- a/aws-lc-rs/benches/cipher_benchmark.rs +++ b/aws-lc-rs/benches/cipher_benchmark.rs @@ -4,8 +4,10 @@ use aws_lc_rs::cipher::{ }; use aws_lc_rs::{test, test_file}; use criterion::{criterion_group, criterion_main, Criterion}; +#[cfg(feature = "openssl-benchmarks")] use openssl::symm::Cipher; +#[cfg(feature = "openssl-benchmarks")] macro_rules! openssl_bench { ($group:ident, $openssl: expr, $key:ident, $iv:ident, $data:ident) => { $group.bench_function("OpenSSL", |b| { @@ -18,6 +20,13 @@ macro_rules! openssl_bench { }; } +#[cfg(not(feature = "openssl-benchmarks"))] +macro_rules! openssl_bench { + ($group:ident, $openssl: expr, $key:ident, $iv:ident, $data:ident) => { + // NO-OP + }; +} + macro_rules! benchmark_padded { ($fn:ident, $test:literal, $file:literal, $awslc:expr, $mode:expr, $openssl:expr) => { fn $fn(c: &mut Criterion) { From 3681904552d8799b9f1f6fcfaf56ec89d4ba96f9 Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Thu, 15 Jun 2023 11:16:58 -0400 Subject: [PATCH 49/54] Update aws-lc-rs/benches/cipher_benchmark.rs Co-authored-by: Cameron Bytheway --- aws-lc-rs/benches/cipher_benchmark.rs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/aws-lc-rs/benches/cipher_benchmark.rs b/aws-lc-rs/benches/cipher_benchmark.rs index c50d1986206..666a62fba71 100644 --- a/aws-lc-rs/benches/cipher_benchmark.rs +++ b/aws-lc-rs/benches/cipher_benchmark.rs @@ -4,27 +4,19 @@ use aws_lc_rs::cipher::{ }; use aws_lc_rs::{test, test_file}; use criterion::{criterion_group, criterion_main, Criterion}; -#[cfg(feature = "openssl-benchmarks")] -use openssl::symm::Cipher; -#[cfg(feature = "openssl-benchmarks")] macro_rules! openssl_bench { - ($group:ident, $openssl: expr, $key:ident, $iv:ident, $data:ident) => { + ($group:ident, $openssl: expr, $key:ident, $iv:ident, $data:ident) => {{ + #[cfg(feature = "openssl-benchmarks")] $group.bench_function("OpenSSL", |b| { + use openssl::symm::Cipher; b.iter(|| { use openssl::symm::{decrypt, encrypt}; let data = encrypt($openssl, &$key, Some(&$iv), &$data).unwrap(); let _ = decrypt($openssl, &$key, Some(&$iv), data.as_ref()).unwrap(); }) }); - }; -} - -#[cfg(not(feature = "openssl-benchmarks"))] -macro_rules! openssl_bench { - ($group:ident, $openssl: expr, $key:ident, $iv:ident, $data:ident) => { - // NO-OP - }; + }}; } macro_rules! benchmark_padded { From 4eba1b91df1236441acfe5a646d6f5f6a9780a79 Mon Sep 17 00:00:00 2001 From: Sean McGrail <549813+skmcgrail@users.noreply.github.com> Date: Thu, 15 Jun 2023 10:00:21 -0700 Subject: [PATCH 50/54] CBC/CTR API Improvements (#155) * CBC/CTR API Improvements * Cleanup unnecessary paste usage --- aws-lc-rs/benches/cipher_benchmark.rs | 18 +- aws-lc-rs/examples/cipher.rs | 56 ++-- aws-lc-rs/src/cipher.rs | 379 ++++++++++++-------------- aws-lc-rs/tests/cipher_test.rs | 135 ++++----- 4 files changed, 263 insertions(+), 325 deletions(-) diff --git a/aws-lc-rs/benches/cipher_benchmark.rs b/aws-lc-rs/benches/cipher_benchmark.rs index 666a62fba71..ff528d83476 100644 --- a/aws-lc-rs/benches/cipher_benchmark.rs +++ b/aws-lc-rs/benches/cipher_benchmark.rs @@ -36,25 +36,23 @@ macro_rules! benchmark_padded { CipherContext::Iv128(iv.as_slice().try_into().unwrap()); let encrypt_key = match $mode { - OperatingMode::CBC => { - PaddedBlockEncryptingKey::less_safe_cbc_pkcs7(key, iv) - } + OperatingMode::CBC => PaddedBlockEncryptingKey::cbc_pkcs7(key), _ => unreachable!(), } .unwrap(); let mut in_out = Vec::from(data.as_slice()); - let context = encrypt_key.encrypt(&mut in_out).unwrap(); + let context = encrypt_key.less_safe_encrypt(&mut in_out, iv).unwrap(); let key = UnboundCipherKey::new($awslc, &key_bytes).unwrap(); let decrypt_key = match $mode { - OperatingMode::CBC => PaddedBlockDecryptingKey::cbc_pkcs7(key, context), + OperatingMode::CBC => PaddedBlockDecryptingKey::cbc_pkcs7(key), _ => unreachable!(), } .unwrap(); - let _ = decrypt_key.decrypt(&mut in_out).unwrap(); + let _ = decrypt_key.decrypt(&mut in_out, context).unwrap(); }) }); @@ -83,23 +81,23 @@ macro_rules! benchmark_unpadded { CipherContext::Iv128(iv.as_slice().try_into().unwrap()); let encrypt_key = match $mode { - OperatingMode::CTR => EncryptingKey::less_safe_ctr(key, iv), + OperatingMode::CTR => EncryptingKey::ctr(key), _ => unreachable!(), } .unwrap(); let mut in_out = Vec::from(data.as_slice()); - let context = encrypt_key.encrypt(&mut in_out).unwrap(); + let context = encrypt_key.less_safe_encrypt(&mut in_out, iv).unwrap(); let key = UnboundCipherKey::new($awslc, &key_bytes).unwrap(); let decrypt_key = match $mode { - OperatingMode::CTR => DecryptingKey::ctr(key, context), + OperatingMode::CTR => DecryptingKey::ctr(key), _ => unreachable!(), } .unwrap(); - let _ = decrypt_key.decrypt(&mut in_out).unwrap(); + let _ = decrypt_key.decrypt(&mut in_out, context).unwrap(); }) }); diff --git a/aws-lc-rs/examples/cipher.rs b/aws-lc-rs/examples/cipher.rs index df7a80627d4..9554b7dd5ec 100644 --- a/aws-lc-rs/examples/cipher.rs +++ b/aws-lc-rs/examples/cipher.rs @@ -101,24 +101,22 @@ fn aes_ctr_encrypt(key: &[u8], iv: Option, plaintext: String) -> Result< let hex_key = hex::encode(key); let key = new_unbound_key(key)?; - let key = match iv { + let key = EncryptingKey::ctr(key).map_err(|_| "failed to initalized aes encryption")?; + + let mut ciphertext = Vec::from(plaintext); + + let context = match iv { Some(iv) => { - let iv = { + let context = { let v = hex::decode(iv).map_err(|_| "invalid iv")?; let v: FixedLength<16> = v.as_slice().try_into().map_err(|_| "invalid iv")?; - v + CipherContext::Iv128(v) }; - EncryptingKey::less_safe_ctr(key, CipherContext::Iv128(iv)) + key.less_safe_encrypt(ciphertext.as_mut(), context) } - None => EncryptingKey::ctr(key), + None => key.encrypt(ciphertext.as_mut()), } - .map_err(|_| "failed to initialized aes encryption")?; - - let mut ciphertext = Vec::from(plaintext); - - let context = key - .encrypt(ciphertext.as_mut()) - .map_err(|_| "Failed to encrypt plaintext")?; + .map_err(|_| "failed to encrypt plaintext")?; let iv: &[u8] = (&context) .try_into() @@ -141,14 +139,13 @@ fn aes_ctr_decrypt(key: &[u8], iv: String, ciphertext: String) -> Result<(), &'s v }; - let key = DecryptingKey::ctr(key, CipherContext::Iv128(iv)) - .map_err(|_| "failed to initialized aes decryption")?; + let key = DecryptingKey::ctr(key).map_err(|_| "failed to initalized aes decryption")?; let mut ciphertext = hex::decode(ciphertext).map_err(|_| "ciphertext is not valid hex encoding")?; let plaintext = key - .decrypt(ciphertext.as_mut()) + .decrypt(ciphertext.as_mut(), CipherContext::Iv128(iv)) .map_err(|_| "failed to decrypt ciphertext")?; let plaintext = @@ -163,25 +160,24 @@ fn aes_cbc_encrypt(key: &[u8], iv: Option, plaintext: String) -> Result< let hex_key = hex::encode(key); let key = new_unbound_key(key)?; - let key = match iv { + let key = PaddedBlockEncryptingKey::cbc_pkcs7(key) + .map_err(|_| "failed to initalized aes encryption")?; + + let mut ciphertext = Vec::from(plaintext); + + let context = match iv { Some(iv) => { - let iv = { + let context = { let v = hex::decode(iv).map_err(|_| "invalid iv")?; let v: FixedLength = v.as_slice().try_into().map_err(|_| "invalid iv")?; - v + CipherContext::Iv128(v) }; - PaddedBlockEncryptingKey::less_safe_cbc_pkcs7(key, CipherContext::Iv128(iv)) + key.less_safe_encrypt(&mut ciphertext, context) } - None => PaddedBlockEncryptingKey::cbc_pkcs7(key), + None => key.encrypt(&mut ciphertext), } - .map_err(|_| "failed to initialized aes encryption")?; - - let mut ciphertext = Vec::from(plaintext); - - let context = key - .encrypt(&mut ciphertext) - .map_err(|_| "Failed to encrypt plaintext")?; + .map_err(|_| "failed to initalized aes encryption")?; let iv: &[u8] = (&context) .try_into() @@ -204,14 +200,14 @@ fn aes_cbc_decrypt(key: &[u8], iv: String, ciphertext: String) -> Result<(), &'s v }; - let key = PaddedBlockDecryptingKey::cbc_pkcs7(key, CipherContext::Iv128(iv)) - .map_err(|_| "failed to initialized aes decryption")?; + let key = PaddedBlockDecryptingKey::cbc_pkcs7(key) + .map_err(|_| "failed to initalized aes decryption")?; let mut ciphertext = hex::decode(ciphertext).map_err(|_| "ciphertext is not valid hex encoding")?; let plaintext = key - .decrypt(ciphertext.as_mut()) + .decrypt(ciphertext.as_mut(), CipherContext::Iv128(iv)) .map_err(|_| "failed to decrypt ciphertext")?; let plaintext = diff --git a/aws-lc-rs/src/cipher.rs b/aws-lc-rs/src/cipher.rs index e8770339751..4a30eb18969 100644 --- a/aws-lc-rs/src/cipher.rs +++ b/aws-lc-rs/src/cipher.rs @@ -39,12 +39,12 @@ //! ]; //! //! let key = UnboundCipherKey::new(&AES_128, key_bytes)?; -//! let encrypting_key = PaddedBlockEncryptingKey::cbc_pkcs7(key)?; +//! let mut encrypting_key = PaddedBlockEncryptingKey::cbc_pkcs7(key)?; //! let context = encrypting_key.encrypt(&mut in_out_buffer)?; //! //! let key = UnboundCipherKey::new(&AES_128, key_bytes)?; -//! let decrypting_key = PaddedBlockDecryptingKey::cbc_pkcs7(key, context)?; -//! let plaintext = decrypting_key.decrypt(&mut in_out_buffer)?; +//! let mut decrypting_key = PaddedBlockDecryptingKey::cbc_pkcs7(key)?; +//! let plaintext = decrypting_key.decrypt(&mut in_out_buffer, context)?; //! assert_eq!(original_message, plaintext); //! # //! # @@ -69,12 +69,12 @@ //! ]; //! //! let key = UnboundCipherKey::new(&AES_128, key_bytes)?; -//! let encrypting_key = EncryptingKey::ctr(key)?; +//! let mut encrypting_key = EncryptingKey::ctr(key)?; //! let context = encrypting_key.encrypt(&mut in_out_buffer)?; //! //! let key = UnboundCipherKey::new(&AES_128, key_bytes)?; -//! let decrypting_key = DecryptingKey::ctr(key, context)?; -//! let plaintext = decrypting_key.decrypt(&mut in_out_buffer)?; +//! let mut decrypting_key = DecryptingKey::ctr(key)?; +//! let plaintext = decrypting_key.decrypt(&mut in_out_buffer, context)?; //! assert_eq!(original_message, plaintext); //! # //! # Ok(()) @@ -102,7 +102,7 @@ use zeroize::Zeroize; /// The cipher block padding strategy. #[non_exhaustive] #[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum PaddingStrategy { +enum PaddingStrategy { /// PKCS#7 Padding. ([See RFC 5652](https://datatracker.ietf.org/doc/html/rfc5652#section-6.3)) PKCS7, } @@ -216,8 +216,8 @@ pub enum OperatingMode { /// 0x43, 0x29, /// ], /// )?; -/// let decrypting_key = DecryptingKey::ctr(key, context)?; -/// let plaintext = decrypting_key.decrypt(&mut in_out_buffer)?; +/// let mut decrypting_key = DecryptingKey::ctr(key)?; +/// let plaintext = decrypting_key.decrypt(&mut in_out_buffer, context)?; /// assert_eq!("Hello World!".as_bytes(), plaintext); /// /// # Ok(()) @@ -241,7 +241,7 @@ pub enum OperatingMode { /// # 0x9d, /// # ]; /// # let key = UnboundCipherKey::new(&AES_128, key_bytes)?; -/// # let encrypting_key = EncryptingKey::ctr(key)?; +/// # let mut encrypting_key = EncryptingKey::ctr(key)?; /// # /// let context: CipherContext = encrypting_key.encrypt(&mut in_out_buffer)?; /// let iv_bytes: &[u8] = (&context).try_into()?; @@ -404,7 +404,6 @@ pub struct PaddedBlockEncryptingKey { key: UnboundCipherKey, mode: OperatingMode, padding: PaddingStrategy, - context: CipherContext, } impl PaddedBlockEncryptingKey { @@ -415,50 +414,16 @@ impl PaddedBlockEncryptingKey { /// * [`Unspecified`]: Returned if there is an error cosntructing a `PaddedBlockEncryptingKey`. /// pub fn cbc_pkcs7(key: UnboundCipherKey) -> Result { - PaddedBlockEncryptingKey::new(key, OperatingMode::CBC, PaddingStrategy::PKCS7, None) - } - - /// Constructs a new `PaddedBlockEncryptingKey` cipher with chaining block cipher (CBC) mode. - /// The users provided context will be used for the CBC initialization-vector. - /// Plaintext data is padded following the PKCS#7 scheme. - /// - /// # Errors - /// * [`Unspecified`]: Returned if there is an error constructing a `PaddedBlockEncryptingKey`. - /// - pub fn less_safe_cbc_pkcs7( - key: UnboundCipherKey, - context: CipherContext, - ) -> Result { - PaddedBlockEncryptingKey::new( - key, - OperatingMode::CBC, - PaddingStrategy::PKCS7, - Some(context), - ) + PaddedBlockEncryptingKey::new(key, OperatingMode::CBC, PaddingStrategy::PKCS7) } + #[allow(clippy::unnecessary_wraps)] fn new( key: UnboundCipherKey, mode: OperatingMode, padding: PaddingStrategy, - context: Option, ) -> Result { - let mode_input = match context { - Some(mi) => { - if !key.algorithm().is_valid_cipher_context(mode, &mi) { - return Err(Unspecified); - } - mi - } - None => key.algorithm.new_cipher_context(mode)?, - }; - - Ok(PaddedBlockEncryptingKey { - key, - mode, - padding, - context: mode_input, - }) + Ok(PaddedBlockEncryptingKey { key, mode, padding }) } /// Returns the cipher algorithm. @@ -473,10 +438,18 @@ impl PaddedBlockEncryptingKey { self.mode } - /// Returns the cipher padding strategy. - #[must_use] - pub fn padding(&self) -> PaddingStrategy { - self.padding + /// Pads and encrypts data provided in `in_out` in-place. + /// Returns a references to the encryted data. + /// + /// # Errors + /// * [`Unspecified`]: Returned if encryption fails. + /// + pub fn encrypt(&self, in_out: &mut InOut) -> Result + where + InOut: AsMut<[u8]> + for<'a> Extend<&'a u8>, + { + let context = self.key.algorithm.new_cipher_context(self.mode)?; + self.less_safe_encrypt(in_out, context) } /// Pads and encrypts data provided in `in_out` in-place. @@ -485,17 +458,25 @@ impl PaddedBlockEncryptingKey { /// # Errors /// * [`Unspecified`]: Returned if encryption fails. /// - pub fn encrypt(self, in_out: &mut InOut) -> Result + pub fn less_safe_encrypt( + &self, + in_out: &mut InOut, + context: CipherContext, + ) -> Result where InOut: AsMut<[u8]> + for<'a> Extend<&'a u8>, { + if !self + .key + .algorithm() + .is_valid_cipher_context(self.mode, &context) + { + return Err(Unspecified); + } + self.padding .add_padding(self.algorithm().block_len(), in_out)?; - self.into_encrypting_key()?.encrypt(in_out.as_mut()) - } - - fn into_encrypting_key(self) -> Result { - EncryptingKey::new(self.key, self.mode, Some(self.context)) + encrypt(&self.key, self.mode, in_out.as_mut(), context) } } @@ -505,7 +486,6 @@ impl Debug for PaddedBlockEncryptingKey { .field("key", &self.key) .field("mode", &self.mode) .field("padding", &self.padding) - .field("context", &self.context) .finish() } } @@ -515,7 +495,6 @@ pub struct PaddedBlockDecryptingKey { key: UnboundCipherKey, mode: OperatingMode, padding: PaddingStrategy, - context: CipherContext, } impl PaddedBlockDecryptingKey { @@ -525,29 +504,17 @@ impl PaddedBlockDecryptingKey { /// # Errors /// * [`Unspecified`]: Returned if there is an error constructing the `PaddedBlockDecryptingKey`. /// - pub fn cbc_pkcs7( - key: UnboundCipherKey, - context: CipherContext, - ) -> Result { - PaddedBlockDecryptingKey::new(key, OperatingMode::CBC, PaddingStrategy::PKCS7, context) + pub fn cbc_pkcs7(key: UnboundCipherKey) -> Result { + PaddedBlockDecryptingKey::new(key, OperatingMode::CBC, PaddingStrategy::PKCS7) } + #[allow(clippy::unnecessary_wraps)] fn new( key: UnboundCipherKey, mode: OperatingMode, padding: PaddingStrategy, - context: CipherContext, ) -> Result { - if !key.algorithm().is_valid_cipher_context(mode, &context) { - return Err(Unspecified); - } - - Ok(PaddedBlockDecryptingKey { - key, - mode, - padding, - context, - }) + Ok(PaddedBlockDecryptingKey { key, mode, padding }) } /// Returns the cipher algorithm. @@ -562,29 +529,31 @@ impl PaddedBlockDecryptingKey { self.mode } - /// Returns the cipher padding strategy. - #[must_use] - pub fn padding(&self) -> PaddingStrategy { - self.padding - } - /// Decrypts and unpads data provided in `in_out` in-place. /// Returns a references to the decrypted data. /// /// # Errors /// * [`Unspecified`]: Returned if decryption fails. /// - pub fn decrypt(self, in_out: &mut [u8]) -> Result<&mut [u8], Unspecified> { + pub fn decrypt<'in_out>( + &self, + in_out: &'in_out mut [u8], + context: CipherContext, + ) -> Result<&'in_out mut [u8], Unspecified> { + if !self + .key + .algorithm() + .is_valid_cipher_context(self.mode, &context) + { + return Err(Unspecified); + } + let block_len = self.algorithm().block_len(); let padding = self.padding; - let mut in_out = self.into_decrypting_key()?.decrypt(in_out)?; + let mut in_out = decrypt(&self.key, self.mode, in_out, context)?; in_out = padding.remove_padding(block_len, in_out)?; Ok(in_out) } - - fn into_decrypting_key(self) -> Result { - DecryptingKey::new(self.key, self.mode, self.context) - } } impl Debug for PaddedBlockDecryptingKey { @@ -593,7 +562,6 @@ impl Debug for PaddedBlockDecryptingKey { .field("key", &self.key) .field("mode", &self.mode) .field("padding", &self.padding) - .field("context", &self.context) .finish() } } @@ -602,7 +570,6 @@ impl Debug for PaddedBlockDecryptingKey { pub struct EncryptingKey { key: UnboundCipherKey, mode: OperatingMode, - context: CipherContext, } impl EncryptingKey { @@ -612,38 +579,12 @@ impl EncryptingKey { /// * [`Unspecified`]: Returned if there is an error constructing the `EncryptingKey`. /// pub fn ctr(key: UnboundCipherKey) -> Result { - EncryptingKey::new(key, OperatingMode::CTR, None) + EncryptingKey::new(key, OperatingMode::CTR) } - /// Constructs an `EncryptingKey` operating in counter (CTR) mode using the provided key. - /// The users provided context will be used for the CTR mode initialization-vector. - /// - /// # Errors - /// * [`Unspecified`]: Returned if there is an error creating the `EncryptingKey`. - /// - pub fn less_safe_ctr( - key: UnboundCipherKey, - context: CipherContext, - ) -> Result { - EncryptingKey::new(key, OperatingMode::CTR, Some(context)) - } - - fn new( - key: UnboundCipherKey, - mode: OperatingMode, - context: Option, - ) -> Result { - let context = match context { - Some(mi) => { - if !key.algorithm().is_valid_cipher_context(mode, &mi) { - return Err(Unspecified); - } - mi - } - None => key.algorithm.new_cipher_context(mode)?, - }; - - Ok(EncryptingKey { key, mode, context }) + #[allow(clippy::unnecessary_wraps)] + fn new(key: UnboundCipherKey, mode: OperatingMode) -> Result { + Ok(EncryptingKey { key, mode }) } /// Returns the cipher algorithm. @@ -665,30 +606,31 @@ impl EncryptingKey { /// * [`Unspecified`]: Returned if cipher mode requires input to be a multiple of the block length, /// and `in_out.len()` is not. Otherwise returned if encryption fails. /// - pub fn encrypt(self, in_out: &mut [u8]) -> Result { - let block_len = self.algorithm().block_len(); - - match self.mode { - OperatingMode::CTR => {} - _ => { - if (in_out.len() % block_len) != 0 { - return Err(Unspecified); - } - } - } + pub fn encrypt(&self, in_out: &mut [u8]) -> Result { + let context = self.key.algorithm.new_cipher_context(self.mode)?; + self.less_safe_encrypt(in_out, context) + } - match self.mode { - OperatingMode::CBC => match self.key.algorithm().id() { - AlgorithmId::Aes128 | AlgorithmId::Aes256 => { - encrypt_aes_cbc_mode(&self.key, self.context, in_out) - } - }, - OperatingMode::CTR => match self.key.algorithm().id() { - AlgorithmId::Aes128 | AlgorithmId::Aes256 => { - encrypt_aes_ctr_mode(&self.key, self.context, in_out) - } - }, + /// Encrypts the data provided in `in_out` in-place using the provided `CipherContext`. + /// Returns a references to the decrypted data. + /// + /// # Errors + /// * [`Unspecified`]: Returned if cipher mode requires input to be a multiple of the block length, + /// and `in_out.len()` is not. Otherwise returned if encryption fails. + /// + pub fn less_safe_encrypt( + &self, + in_out: &mut [u8], + context: CipherContext, + ) -> Result { + if !self + .key + .algorithm() + .is_valid_cipher_context(self.mode, &context) + { + return Err(Unspecified); } + encrypt(&self.key, self.mode, in_out, context) } } @@ -697,7 +639,6 @@ impl Debug for EncryptingKey { f.debug_struct("EncryptingKey") .field("key", &self.key) .field("mode", &self.mode) - .field("context", &self.context) .finish() } } @@ -706,7 +647,6 @@ impl Debug for EncryptingKey { pub struct DecryptingKey { key: UnboundCipherKey, mode: OperatingMode, - context: CipherContext, } impl DecryptingKey { @@ -715,23 +655,13 @@ impl DecryptingKey { /// # Errors /// * [`Unspecified`]: Returned if there is an error during decryption. /// - pub fn ctr( - key: UnboundCipherKey, - context: CipherContext, - ) -> Result { - DecryptingKey::new(key, OperatingMode::CTR, context) + pub fn ctr(key: UnboundCipherKey) -> Result { + DecryptingKey::new(key, OperatingMode::CTR) } - fn new( - key: UnboundCipherKey, - mode: OperatingMode, - context: CipherContext, - ) -> Result { - if !key.algorithm().is_valid_cipher_context(mode, &context) { - return Err(Unspecified); - } - - Ok(DecryptingKey { key, mode, context }) + #[allow(clippy::unnecessary_wraps)] + fn new(key: UnboundCipherKey, mode: OperatingMode) -> Result { + Ok(DecryptingKey { key, mode }) } /// Returns the cipher algorithm. @@ -753,30 +683,12 @@ impl DecryptingKey { /// * [`Unspecified`]: Returned if cipher mode requires input to be a multiple of the block length, /// and `in_out.len()` is not. Also returned if decryption fails. /// - pub fn decrypt(self, in_out: &mut [u8]) -> Result<&mut [u8], Unspecified> { - let block_len = self.algorithm().block_len(); - - match self.mode { - OperatingMode::CTR => {} - _ => { - if (in_out.len() % block_len) != 0 { - return Err(Unspecified); - } - } - } - - match self.mode { - OperatingMode::CBC => match self.key.algorithm().id() { - AlgorithmId::Aes128 | AlgorithmId::Aes256 => { - decrypt_aes_cbc_mode(&self.key, self.context, in_out).map(|_| in_out) - } - }, - OperatingMode::CTR => match self.key.algorithm().id() { - AlgorithmId::Aes128 | AlgorithmId::Aes256 => { - decrypt_aes_ctr_mode(&self.key, self.context, in_out).map(|_| in_out) - } - }, - } + pub fn decrypt<'in_out>( + &self, + in_out: &'in_out mut [u8], + context: CipherContext, + ) -> Result<&'in_out mut [u8], Unspecified> { + decrypt(&self.key, self.mode, in_out, context) } } @@ -785,11 +697,68 @@ impl Debug for DecryptingKey { f.debug_struct("DecryptingKey") .field("key", &self.key) .field("mode", &self.mode) - .field("context", &self.context) .finish() } } +fn encrypt( + key: &UnboundCipherKey, + mode: OperatingMode, + in_out: &mut [u8], + context: CipherContext, +) -> Result { + let block_len = key.algorithm().block_len(); + + match mode { + OperatingMode::CTR => {} + _ => { + if (in_out.len() % block_len) != 0 { + return Err(Unspecified); + } + } + } + + match mode { + OperatingMode::CBC => match key.algorithm().id() { + AlgorithmId::Aes128 | AlgorithmId::Aes256 => encrypt_aes_cbc_mode(key, context, in_out), + }, + OperatingMode::CTR => match key.algorithm().id() { + AlgorithmId::Aes128 | AlgorithmId::Aes256 => encrypt_aes_ctr_mode(key, context, in_out), + }, + } +} + +fn decrypt<'in_out>( + key: &UnboundCipherKey, + mode: OperatingMode, + in_out: &'in_out mut [u8], + context: CipherContext, +) -> Result<&'in_out mut [u8], Unspecified> { + let block_len = key.algorithm().block_len(); + + match mode { + OperatingMode::CTR => {} + _ => { + if (in_out.len() % block_len) != 0 { + return Err(Unspecified); + } + } + } + + match mode { + OperatingMode::CBC => match key.algorithm().id() { + AlgorithmId::Aes128 | AlgorithmId::Aes256 => { + decrypt_aes_cbc_mode(key, context, in_out).map(|_| in_out) + } + }, + OperatingMode::CTR => match key.algorithm().id() { + AlgorithmId::Aes128 | AlgorithmId::Aes256 => { + decrypt_aes_ctr_mode(key, context, in_out).map(|_| in_out) + } + }, + } +} + fn encrypt_aes_ctr_mode( key: &UnboundCipherKey, context: CipherContext, @@ -947,30 +916,28 @@ mod tests { UnboundCipherKey::new(&AES_128, key_bytes).unwrap(), ) .unwrap(); - assert_eq!("PaddedBlockEncryptingKey { key: UnboundCipherKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 } }, mode: CBC, padding: PKCS7, context: Iv128 }", format!("{key:?}")); + assert_eq!("PaddedBlockEncryptingKey { key: UnboundCipherKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 } }, mode: CBC, padding: PKCS7 }", format!("{key:?}")); let mut data = vec![0u8; 16]; let context = key.encrypt(&mut data).unwrap(); assert_eq!("Iv128", format!("{context:?}")); let key = PaddedBlockDecryptingKey::cbc_pkcs7( UnboundCipherKey::new(&AES_128, key_bytes).unwrap(), - context, ) .unwrap(); - assert_eq!("PaddedBlockDecryptingKey { key: UnboundCipherKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 } }, mode: CBC, padding: PKCS7, context: Iv128 }", format!("{key:?}")); + assert_eq!("PaddedBlockDecryptingKey { key: UnboundCipherKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 } }, mode: CBC, padding: PKCS7 }", format!("{key:?}")); } { let key_bytes = &[0u8; 16]; let key = EncryptingKey::ctr(UnboundCipherKey::new(&AES_128, key_bytes).unwrap()).unwrap(); - assert_eq!("EncryptingKey { key: UnboundCipherKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 } }, mode: CTR, context: Iv128 }", format!("{key:?}")); + assert_eq!("EncryptingKey { key: UnboundCipherKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 } }, mode: CTR }", format!("{key:?}")); let mut data = vec![0u8; 16]; let context = key.encrypt(&mut data).unwrap(); assert_eq!("Iv128", format!("{context:?}")); let key = - DecryptingKey::ctr(UnboundCipherKey::new(&AES_128, key_bytes).unwrap(), context) - .unwrap(); - assert_eq!("DecryptingKey { key: UnboundCipherKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 } }, mode: CTR, context: Iv128 }", format!("{key:?}")); + DecryptingKey::ctr(UnboundCipherKey::new(&AES_128, key_bytes).unwrap()).unwrap(); + assert_eq!("DecryptingKey { key: UnboundCipherKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 } }, mode: CTR }", format!("{key:?}")); } } @@ -987,7 +954,7 @@ mod tests { } let cipher_key = UnboundCipherKey::new(alg, key).unwrap(); - let encrypting_key = EncryptingKey::new(cipher_key, mode, None).unwrap(); + let encrypting_key = EncryptingKey::new(cipher_key, mode).unwrap(); let mut in_out = input.clone(); let decrypt_iv = encrypting_key.encrypt(&mut in_out).unwrap(); @@ -998,9 +965,9 @@ mod tests { } let cipher_key2 = UnboundCipherKey::new(alg, key).unwrap(); - let decrypting_key = DecryptingKey::new(cipher_key2, mode, decrypt_iv).unwrap(); + let decrypting_key = DecryptingKey::new(cipher_key2, mode).unwrap(); - let plaintext = decrypting_key.decrypt(&mut in_out).unwrap(); + let plaintext = decrypting_key.decrypt(&mut in_out, decrypt_iv).unwrap(); assert_eq!(input.as_slice(), plaintext); } @@ -1018,8 +985,7 @@ mod tests { } let cipher_key = UnboundCipherKey::new(alg, key).unwrap(); - let encrypting_key = - PaddedBlockEncryptingKey::new(cipher_key, mode, padding, None).unwrap(); + let encrypting_key = PaddedBlockEncryptingKey::new(cipher_key, mode, padding).unwrap(); let mut in_out = input.clone(); let decrypt_iv = encrypting_key.encrypt(&mut in_out).unwrap(); @@ -1030,10 +996,9 @@ mod tests { } let cipher_key2 = UnboundCipherKey::new(alg, key).unwrap(); - let decrypting_key = - PaddedBlockDecryptingKey::new(cipher_key2, mode, padding, decrypt_iv).unwrap(); + let decrypting_key = PaddedBlockDecryptingKey::new(cipher_key2, mode, padding).unwrap(); - let plaintext = decrypting_key.decrypt(&mut in_out).unwrap(); + let plaintext = decrypting_key.decrypt(&mut in_out, decrypt_iv).unwrap(); assert_eq!(input.as_slice(), plaintext); } @@ -1108,19 +1073,19 @@ mod tests { let unbound_key = UnboundCipherKey::new(alg, &key).unwrap(); let encrypting_key = - PaddedBlockEncryptingKey::new(unbound_key, $mode, $padding, Some(dc)).unwrap(); + PaddedBlockEncryptingKey::new(unbound_key, $mode, $padding).unwrap(); let mut in_out = input.clone(); - let context = encrypting_key.encrypt(&mut in_out).unwrap(); + let context = encrypting_key.less_safe_encrypt(&mut in_out, dc).unwrap(); assert_eq!(expected_ciphertext, in_out); let unbound_key2 = UnboundCipherKey::new(alg, &key).unwrap(); let decrypting_key = - PaddedBlockDecryptingKey::new(unbound_key2, $mode, $padding, context).unwrap(); + PaddedBlockDecryptingKey::new(unbound_key2, $mode, $padding).unwrap(); - let plaintext = decrypting_key.decrypt(&mut in_out).unwrap(); + let plaintext = decrypting_key.decrypt(&mut in_out, context).unwrap(); assert_eq!(input.as_slice(), plaintext); } }; @@ -1150,18 +1115,18 @@ mod tests { let unbound_key = UnboundCipherKey::new(alg, &key).unwrap(); - let encrypting_key = EncryptingKey::new(unbound_key, $mode, Some(dc)).unwrap(); + let encrypting_key = EncryptingKey::new(unbound_key, $mode).unwrap(); let mut in_out = input.clone(); - let context = encrypting_key.encrypt(&mut in_out).unwrap(); + let context = encrypting_key.less_safe_encrypt(&mut in_out, dc).unwrap(); assert_eq!(expected_ciphertext, in_out); let unbound_key2 = UnboundCipherKey::new(alg, &key).unwrap(); - let decrypting_key = DecryptingKey::new(unbound_key2, $mode, context).unwrap(); + let decrypting_key = DecryptingKey::new(unbound_key2, $mode).unwrap(); - let plaintext = decrypting_key.decrypt(&mut in_out).unwrap(); + let plaintext = decrypting_key.decrypt(&mut in_out, context).unwrap(); assert_eq!(input.as_slice(), plaintext); } }; diff --git a/aws-lc-rs/tests/cipher_test.rs b/aws-lc-rs/tests/cipher_test.rs index 0d47b1c9b50..61f29318ca4 100644 --- a/aws-lc-rs/tests/cipher_test.rs +++ b/aws-lc-rs/tests/cipher_test.rs @@ -3,13 +3,13 @@ use aws_lc_rs::cipher::{ CipherContext, DecryptingKey, EncryptingKey, OperatingMode, PaddedBlockDecryptingKey, - PaddedBlockEncryptingKey, PaddingStrategy, UnboundCipherKey, AES_128, AES_256, + PaddedBlockEncryptingKey, UnboundCipherKey, AES_128, AES_256, }; use aws_lc_rs::iv::FixedLength; use aws_lc_rs::test::from_hex; macro_rules! padded_cipher_kat { - ($name:ident, $alg:expr, $mode:expr, $padding:expr, $constructor:ident, $key:literal, $iv: literal, $plaintext:literal, $ciphertext:literal) => { paste::item! { + ($name:ident, $alg:expr, $mode:expr, $constructor:ident, $key:literal, $iv: literal, $plaintext:literal, $ciphertext:literal) => { #[test] fn $name() { let key = from_hex($key).unwrap(); @@ -22,87 +22,78 @@ macro_rules! padded_cipher_kat { let unbound_key = UnboundCipherKey::new($alg, &key).unwrap(); - let encrypting_key = - PaddedBlockEncryptingKey::[](unbound_key, context).unwrap(); + let encrypting_key = PaddedBlockEncryptingKey::$constructor(unbound_key).unwrap(); assert_eq!($mode, encrypting_key.mode()); - assert_eq!($padding, encrypting_key.padding()); assert_eq!($alg, encrypting_key.algorithm()); let mut in_out = input.clone(); - let context = encrypting_key.encrypt(&mut in_out).unwrap(); + let context = encrypting_key + .less_safe_encrypt(&mut in_out, context) + .unwrap(); assert_eq!(expected_ciphertext.as_slice(), in_out.as_slice()); let unbound_key2 = UnboundCipherKey::new($alg, &key).unwrap(); - let decrypting_key = - PaddedBlockDecryptingKey::$constructor(unbound_key2, context).unwrap(); + let decrypting_key = PaddedBlockDecryptingKey::$constructor(unbound_key2).unwrap(); assert_eq!($mode, decrypting_key.mode()); - assert_eq!($padding, decrypting_key.padding()); assert_eq!($alg, decrypting_key.algorithm()); - let plaintext = decrypting_key.decrypt(&mut in_out).unwrap(); + let plaintext = decrypting_key.decrypt(&mut in_out, context).unwrap(); assert_eq!(input.as_slice(), plaintext); } - }}; + }; } macro_rules! cipher_kat { ($name:ident, $alg:expr, $mode:expr, $constructor:ident, $key:literal, $iv: literal, $plaintext:literal, $ciphertext:literal) => { - paste::item! { - #[test] - fn $name() { - let key = from_hex($key).unwrap(); - let input = from_hex($plaintext).unwrap(); - let expected_ciphertext = from_hex($ciphertext).unwrap(); - - let iv = from_hex($iv).unwrap(); - let fixed_iv = FixedLength::try_from(iv.as_slice()).unwrap(); - let context = CipherContext::Iv128(fixed_iv); - - let unbound_key = UnboundCipherKey::new($alg, &key).unwrap(); - - let encrypting_key = - EncryptingKey::[](unbound_key, context).unwrap(); - assert_eq!($mode, encrypting_key.mode()); - assert_eq!($alg, encrypting_key.algorithm()); - let mut in_out = input.clone(); - let context = encrypting_key.encrypt(in_out.as_mut_slice()).unwrap(); - assert_eq!(expected_ciphertext.as_slice(), in_out); - - let unbound_key2 = UnboundCipherKey::new($alg, &key).unwrap(); - let decrypting_key = - DecryptingKey::$constructor(unbound_key2, context).unwrap(); - assert_eq!($mode, decrypting_key.mode()); - assert_eq!($alg, decrypting_key.algorithm()); - let plaintext = decrypting_key.decrypt(&mut in_out).unwrap(); - assert_eq!(input.as_slice(), plaintext); - } + #[test] + fn $name() { + let key = from_hex($key).unwrap(); + let input = from_hex($plaintext).unwrap(); + let expected_ciphertext = from_hex($ciphertext).unwrap(); + + let iv = from_hex($iv).unwrap(); + let fixed_iv = FixedLength::try_from(iv.as_slice()).unwrap(); + let context = CipherContext::Iv128(fixed_iv); + + let unbound_key = UnboundCipherKey::new($alg, &key).unwrap(); + + let encrypting_key = EncryptingKey::$constructor(unbound_key).unwrap(); + assert_eq!($mode, encrypting_key.mode()); + assert_eq!($alg, encrypting_key.algorithm()); + let mut in_out = input.clone(); + let context = encrypting_key + .less_safe_encrypt(in_out.as_mut_slice(), context) + .unwrap(); + assert_eq!(expected_ciphertext.as_slice(), in_out); + + let unbound_key2 = UnboundCipherKey::new($alg, &key).unwrap(); + let decrypting_key = DecryptingKey::$constructor(unbound_key2).unwrap(); + assert_eq!($mode, decrypting_key.mode()); + assert_eq!($alg, decrypting_key.algorithm()); + let plaintext = decrypting_key.decrypt(&mut in_out, context).unwrap(); + assert_eq!(input.as_slice(), plaintext); } }; } macro_rules! padded_cipher_rt { - ($name:ident, $alg:expr, $mode:expr, $padding:expr, $constructor:ident, $key:literal, $plaintext:literal) => { - paste::item! { - #[test] - fn $name() { - let key = from_hex($key).unwrap(); - let input = from_hex($plaintext).unwrap(); - let unbound_key = UnboundCipherKey::new($alg, &key).unwrap(); - - let encrypting_key = PaddedBlockEncryptingKey::$constructor(unbound_key).unwrap(); - assert_eq!($mode, encrypting_key.mode()); - assert_eq!($padding, encrypting_key.padding()); - assert_eq!($alg, encrypting_key.algorithm()); - let mut in_out = input.clone(); - let context = encrypting_key.encrypt(&mut in_out).unwrap(); - - let unbound_key2 = UnboundCipherKey::new($alg, &key).unwrap(); - let decrypting_key = - PaddedBlockDecryptingKey::$constructor(unbound_key2, context).unwrap(); - assert_eq!($mode, decrypting_key.mode()); - assert_eq!($padding, decrypting_key.padding()); - assert_eq!($alg, decrypting_key.algorithm()); - let plaintext = decrypting_key.decrypt(&mut in_out).unwrap(); - assert_eq!(input.as_slice(), plaintext); - } + ($name:ident, $alg:expr, $mode:expr, $constructor:ident, $key:literal, $plaintext:literal) => { + #[test] + fn $name() { + let key = from_hex($key).unwrap(); + let input = from_hex($plaintext).unwrap(); + let unbound_key = UnboundCipherKey::new($alg, &key).unwrap(); + + let encrypting_key = PaddedBlockEncryptingKey::$constructor(unbound_key).unwrap(); + assert_eq!($mode, encrypting_key.mode()); + assert_eq!($alg, encrypting_key.algorithm()); + let mut in_out = input.clone(); + let context = encrypting_key.encrypt(&mut in_out).unwrap(); + + let unbound_key2 = UnboundCipherKey::new($alg, &key).unwrap(); + let decrypting_key = PaddedBlockDecryptingKey::$constructor(unbound_key2).unwrap(); + assert_eq!($mode, decrypting_key.mode()); + assert_eq!($alg, decrypting_key.algorithm()); + let plaintext = decrypting_key.decrypt(&mut in_out, context).unwrap(); + assert_eq!(input.as_slice(), plaintext); } }; } @@ -122,10 +113,10 @@ macro_rules! cipher_rt { let context = encrypting_key.encrypt(in_out.as_mut_slice()).unwrap(); let unbound_key2 = UnboundCipherKey::new($alg, &key).unwrap(); - let decrypting_key = DecryptingKey::$constructor(unbound_key2, context).unwrap(); + let decrypting_key = DecryptingKey::$constructor(unbound_key2).unwrap(); assert_eq!($mode, decrypting_key.mode()); assert_eq!($alg, decrypting_key.algorithm()); - let plaintext = decrypting_key.decrypt(&mut in_out).unwrap(); + let plaintext = decrypting_key.decrypt(&mut in_out, context).unwrap(); assert_eq!(input.as_slice(), plaintext); } }; @@ -135,7 +126,6 @@ padded_cipher_kat!( test_kat_aes_128_cbc_16_bytes, &AES_128, OperatingMode::CBC, - PaddingStrategy::PKCS7, cbc_pkcs7, "000102030405060708090a0b0c0d0e0f", "00000000000000000000000000000000", @@ -147,7 +137,6 @@ padded_cipher_kat!( test_kat_aes_256_cbc_15_bytes, &AES_256, OperatingMode::CBC, - PaddingStrategy::PKCS7, cbc_pkcs7, "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "00000000000000000000000000000000", @@ -203,7 +192,6 @@ padded_cipher_kat!( test_kat_aes_128_cbc_15_bytes, &AES_128, OperatingMode::CBC, - PaddingStrategy::PKCS7, cbc_pkcs7, "053304bb3899e1d99db9d29343ea782d", "b5313560244a4822c46c2a0c9d0cf7fd", @@ -215,7 +203,6 @@ padded_cipher_kat!( test_kat_aes_128_cbc_16_bytes_2, &AES_128, OperatingMode::CBC, - PaddingStrategy::PKCS7, cbc_pkcs7, "95af71f1c63e4a1d0b0b1a27fb978283", "89e40797dca70197ff87d3dbb0ef2802", @@ -227,7 +214,6 @@ padded_cipher_kat!( test_kat_aes_256_cbc_16_bytes, &AES_256, OperatingMode::CBC, - PaddingStrategy::PKCS7, cbc_pkcs7, "d4a8206dcae01242f9db79a4ecfe277d0f7bb8ccbafd8f9809adb39f35aa9b41", "24f6076548fb9d93c8f7ed9f6e661ef9", @@ -239,7 +225,6 @@ padded_cipher_rt!( test_rt_aes_128_cbc_16_bytes, &AES_128, OperatingMode::CBC, - PaddingStrategy::PKCS7, cbc_pkcs7, "000102030405060708090a0b0c0d0e0f", "00112233445566778899aabbccddeeff" @@ -249,7 +234,6 @@ padded_cipher_rt!( test_rt_aes_256_cbc_15_bytes, &AES_256, OperatingMode::CBC, - PaddingStrategy::PKCS7, cbc_pkcs7, "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "00112233445566778899aabbccddee" @@ -313,7 +297,6 @@ padded_cipher_rt!( test_rt_aes_128_cbc_15_bytes, &AES_128, OperatingMode::CBC, - PaddingStrategy::PKCS7, cbc_pkcs7, "053304bb3899e1d99db9d29343ea782d", "a3e4c990356c01f320043c3d8d6f43" @@ -323,7 +306,6 @@ padded_cipher_rt!( test_rt_aes_128_cbc_16_bytes_2, &AES_128, OperatingMode::CBC, - PaddingStrategy::PKCS7, cbc_pkcs7, "95af71f1c63e4a1d0b0b1a27fb978283", "aece7b5e3c3df1ffc9802d2dfe296dc7" @@ -333,7 +315,6 @@ padded_cipher_rt!( test_rt_128_cbc_17_bytes, &AES_128, OperatingMode::CBC, - PaddingStrategy::PKCS7, cbc_pkcs7, "95af71f1c63e4a1d0b0b1a27fb978283", "aece7b5e3c3df1ffc9802d2dfe296dc734" @@ -343,7 +324,6 @@ padded_cipher_rt!( test_rt_aes_256_cbc_16_bytes, &AES_256, OperatingMode::CBC, - PaddingStrategy::PKCS7, cbc_pkcs7, "d4a8206dcae01242f9db79a4ecfe277d0f7bb8ccbafd8f9809adb39f35aa9b41", "a39c1fdf77ea3e1f18178c0ec237c70a" @@ -353,7 +333,6 @@ padded_cipher_rt!( test_rt_aes_256_cbc_17_bytes, &AES_256, OperatingMode::CBC, - PaddingStrategy::PKCS7, cbc_pkcs7, "d4a8206dcae01242f9db79a4ecfe277d0f7bb8ccbafd8f9809adb39f35aa9b41", "a39c1fdf77ea3e1f18178c0ec237c70a34" From b3aab2b533ced72421cd62172082a7dc77e978c9 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Thu, 15 Jun 2023 14:07:56 -0400 Subject: [PATCH 51/54] Avoid using transmute --- aws-lc-rs/src/cipher/key.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/aws-lc-rs/src/cipher/key.rs b/aws-lc-rs/src/cipher/key.rs index 4aa27a4d15f..e977995a786 100644 --- a/aws-lc-rs/src/cipher/key.rs +++ b/aws-lc-rs/src/cipher/key.rs @@ -8,7 +8,7 @@ use crate::cipher::{AES_128_KEY_LEN, AES_256_KEY_LEN}; use crate::error::Unspecified; use aws_lc::{AES_set_decrypt_key, AES_set_encrypt_key, AES_KEY}; use core::ptr::copy_nonoverlapping; -use std::mem::{size_of, transmute, MaybeUninit}; +use std::mem::{size_of, MaybeUninit}; use std::os::raw::c_uint; use zeroize::Zeroize; @@ -43,11 +43,15 @@ impl Drop for SymmetricCipherKey { | SymmetricCipherKey::Aes256 { enc_key, dec_key, .. } => unsafe { - #[allow(clippy::transmute_ptr_to_ptr)] - let enc_bytes: &mut [u8; size_of::()] = transmute(enc_key); + let enc_bytes: &mut [u8; size_of::()] = (enc_key as *mut AES_KEY) + .cast::<[u8; size_of::()]>() + .as_mut() + .unwrap(); enc_bytes.zeroize(); - #[allow(clippy::transmute_ptr_to_ptr)] - let dec_bytes: &mut [u8; size_of::()] = transmute(dec_key); + let dec_bytes: &mut [u8; size_of::()] = (dec_key as *mut AES_KEY) + .cast::<[u8; size_of::()]>() + .as_mut() + .unwrap(); dec_bytes.zeroize(); }, SymmetricCipherKey::ChaCha20 { .. } => {} From a1061cc903ec66229cb1ea4dba57a40add594afb Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Mon, 19 Jun 2023 12:51:17 -0400 Subject: [PATCH 52/54] Copyright for cipher_benchmark.rs --- aws-lc-rs/benches/cipher_benchmark.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aws-lc-rs/benches/cipher_benchmark.rs b/aws-lc-rs/benches/cipher_benchmark.rs index ff528d83476..d8a3bdeff4a 100644 --- a/aws-lc-rs/benches/cipher_benchmark.rs +++ b/aws-lc-rs/benches/cipher_benchmark.rs @@ -1,3 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 OR ISC + use aws_lc_rs::cipher::{ CipherContext, DecryptingKey, EncryptingKey, OperatingMode, PaddedBlockDecryptingKey, PaddedBlockEncryptingKey, UnboundCipherKey, AES_128, AES_256, From ca347356720dc832fda22a6abbbad335d2114240 Mon Sep 17 00:00:00 2001 From: Sean McGrail Date: Mon, 19 Jun 2023 10:11:45 -0700 Subject: [PATCH 53/54] Use constants in example --- aws-lc-rs/examples/cipher.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/aws-lc-rs/examples/cipher.rs b/aws-lc-rs/examples/cipher.rs index 9554b7dd5ec..b13bfc09a14 100644 --- a/aws-lc-rs/examples/cipher.rs +++ b/aws-lc-rs/examples/cipher.rs @@ -24,7 +24,7 @@ //! $ cargo run --example cipher -- --mode cbc --key 6489d8ce0c4facf18b872705a05d5ee4 decrypt --iv 5cd56fb752830ec2459889226c5431bd 6311c14e8104730be124ce1e57e51fe3 //! Hello World //! ``` -use aws_lc_rs::cipher::{AES_128_KEY_LEN, AES_CBC_IV_LEN}; +use aws_lc_rs::cipher::{AES_128_KEY_LEN, AES_256_KEY_LEN, AES_CBC_IV_LEN, AES_CTR_IV_LEN}; use aws_lc_rs::{ cipher::{ CipherContext, DecryptingKey, EncryptingKey, PaddedBlockDecryptingKey, @@ -109,7 +109,8 @@ fn aes_ctr_encrypt(key: &[u8], iv: Option, plaintext: String) -> Result< Some(iv) => { let context = { let v = hex::decode(iv).map_err(|_| "invalid iv")?; - let v: FixedLength<16> = v.as_slice().try_into().map_err(|_| "invalid iv")?; + let v: FixedLength = + v.as_slice().try_into().map_err(|_| "invalid iv")?; CipherContext::Iv128(v) }; key.less_safe_encrypt(ciphertext.as_mut(), context) @@ -135,7 +136,7 @@ fn aes_ctr_decrypt(key: &[u8], iv: String, ciphertext: String) -> Result<(), &'s let key = new_unbound_key(key)?; let iv = { let v = hex::decode(iv).map_err(|_| "invalid iv")?; - let v: FixedLength<16> = v.as_slice().try_into().map_err(|_| "invalid iv")?; + let v: FixedLength = v.as_slice().try_into().map_err(|_| "invalid iv")?; v }; @@ -196,7 +197,7 @@ fn aes_cbc_decrypt(key: &[u8], iv: String, ciphertext: String) -> Result<(), &'s let key = new_unbound_key(key)?; let iv = { let v = hex::decode(iv).map_err(|_| "invalid iv")?; - let v: FixedLength<16> = v.as_slice().try_into().map_err(|_| "invalid iv")?; + let v: FixedLength = v.as_slice().try_into().map_err(|_| "invalid iv")?; v }; @@ -220,8 +221,8 @@ fn aes_cbc_decrypt(key: &[u8], iv: String, ciphertext: String) -> Result<(), &'s fn new_unbound_key(key: &[u8]) -> Result { let alg = match key.len() { - 16 => &AES_128, - 32 => &AES_256, + AES_128_KEY_LEN => &AES_128, + AES_256_KEY_LEN => &AES_256, _ => { return Err("invalid aes key length"); } From 2987a36399577c8b9dd95b182cfcd2db29529d81 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Mon, 19 Jun 2023 14:30:26 -0400 Subject: [PATCH 54/54] udeps: we use openssl for benchmarks --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f15ad65d83e..b42106737a8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -141,7 +141,9 @@ jobs: args: cargo-udeps - name: Run cargo udeps - run: cargo udeps --workspace --all-targets + # we only use openssl when the openssl-benchmarks feature is enabled. + # openssl is a dev-dependency so it can't be optional. + run: cargo udeps --workspace --all-targets --features openssl-benchmarks env: RUSTC_WRAPPER: ""