diff options
author | Maurice Lam <yukl@google.com> | 2023-09-08 16:34:43 -0700 |
---|---|---|
committer | Boringssl LUCI CQ <boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com> | 2023-10-03 23:20:57 +0000 |
commit | 81ed2b3f6a135449772c46980067b8d4f71f5c82 (patch) | |
tree | cefd632ae9f6be1fc5d2adfa4f14f19ed86c4193 /rust | |
parent | bd20800c22fc8402611b537287bd6948c3f2a5a8 (diff) | |
download | boringssl-81ed2b3f6a135449772c46980067b8d4f71f5c82.zip boringssl-81ed2b3f6a135449772c46980067b8d4f71f5c82.tar.gz boringssl-81ed2b3f6a135449772c46980067b8d4f71f5c82.tar.bz2 |
Implement bssl-crypto wrappers for AES-CBC
- Create an internal `BlockCipher` trait similar to the existing
`StreamCipher` trait for AES-CBC.
- Create wrappers in the internal `Cipher` struct for one-shot
allocating encryption and decryption operations.
Change-Id: I17f667b3b92f907bc14c3454ee49b88cb91c49f3
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/63125
Commit-Queue: Bob Beck <bbe@google.com>
Reviewed-by: Bob Beck <bbe@google.com>
Diffstat (limited to 'rust')
-rw-r--r-- | rust/bssl-crypto/src/cipher/aes_cbc.rs | 194 | ||||
-rw-r--r-- | rust/bssl-crypto/src/cipher/aes_ctr.rs | 8 | ||||
-rw-r--r-- | rust/bssl-crypto/src/cipher/mod.rs | 278 |
3 files changed, 462 insertions, 18 deletions
diff --git a/rust/bssl-crypto/src/cipher/aes_cbc.rs b/rust/bssl-crypto/src/cipher/aes_cbc.rs new file mode 100644 index 0000000..6d22a18 --- /dev/null +++ b/rust/bssl-crypto/src/cipher/aes_cbc.rs @@ -0,0 +1,194 @@ +/* Copyright (c) 2023, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +extern crate alloc; + +use crate::cipher::{ + BlockCipher, Cipher, CipherError, CipherInitPurpose, EvpAes128Cbc, EvpAes256Cbc, +}; +use alloc::vec::Vec; + +/// AES-CBC-128 Cipher implementation. +pub struct Aes128Cbc(Cipher<EvpAes128Cbc>); + +impl BlockCipher for Aes128Cbc { + type Key = [u8; 16]; + type Nonce = [u8; 16]; + + fn new_encrypt(key: &Self::Key, nonce: &Self::Nonce) -> Self { + Self(Cipher::new(key, nonce, CipherInitPurpose::Encrypt)) + } + + fn new_decrypt(key: &Self::Key, nonce: &Self::Nonce) -> Self { + Self(Cipher::new(key, nonce, CipherInitPurpose::Decrypt)) + } + + fn encrypt_padded(self, buffer: &[u8]) -> Result<Vec<u8>, CipherError> { + // Note: Padding is enabled because we did not disable it with `EVP_CIPHER_CTX_set_padding` + self.0.encrypt(buffer) + } + + fn decrypt_padded(self, buffer: &[u8]) -> Result<Vec<u8>, CipherError> { + // Note: Padding is enabled because we did not disable it with `EVP_CIPHER_CTX_set_padding` + self.0.decrypt(buffer) + } +} + +/// AES-CBC-256 Cipher implementation. +pub struct Aes256Cbc(Cipher<EvpAes256Cbc>); + +impl BlockCipher for Aes256Cbc { + type Key = [u8; 32]; + type Nonce = [u8; 16]; + + fn new_encrypt(key: &Self::Key, nonce: &Self::Nonce) -> Self { + Self(Cipher::new(key, nonce, CipherInitPurpose::Encrypt)) + } + + fn new_decrypt(key: &Self::Key, nonce: &Self::Nonce) -> Self { + Self(Cipher::new(key, nonce, CipherInitPurpose::Decrypt)) + } + + fn encrypt_padded(self, buffer: &[u8]) -> Result<Vec<u8>, CipherError> { + // Note: Padding is enabled because we did not disable it with `EVP_CIPHER_CTX_set_padding` + self.0.encrypt(buffer) + } + + fn decrypt_padded(self, buffer: &[u8]) -> Result<Vec<u8>, CipherError> { + // Note: Padding is enabled because we did not disable it with `EVP_CIPHER_CTX_set_padding` + self.0.decrypt(buffer) + } +} + +#[allow(clippy::expect_used)] +#[cfg(test)] +mod test { + use super::*; + use crate::test_helpers::decode_hex; + + #[test] + fn aes_128_cbc_test_encrypt() { + // https://github.com/google/wycheproof/blob/master/testvectors/aes_cbc_pkcs5_test.json#L30 + // tcId: 2 + let iv = decode_hex("c9ee3cd746bf208c65ca9e72a266d54f"); + let key = decode_hex("e09eaa5a3f5e56d279d5e7a03373f6ea"); + + let cipher = Aes128Cbc::new_encrypt(&key, &iv); + let msg: [u8; 16] = decode_hex("ef4eab37181f98423e53e947e7050fd0"); + + let output = cipher.encrypt_padded(&msg).expect("Failed to encrypt"); + + let expected_ciphertext: [u8; 32] = + decode_hex("d1fa697f3e2e04d64f1a0da203813ca5bc226a0b1d42287b2a5b994a66eaf14a"); + assert_eq!(expected_ciphertext, &output[..]); + } + + #[test] + fn aes_128_cbc_test_encrypt_more_than_one_block() { + // https://github.com/google/wycheproof/blob/master/testvectors/aes_cbc_pkcs5_test.json#L210 + // tcId: 20 + let iv = decode_hex("54f2459e40e002763144f4752cde2fb5"); + let key = decode_hex("831e664c9e3f0c3094c0b27b9d908eb2"); + + let cipher = Aes128Cbc::new_encrypt(&key, &iv); + let msg: [u8; 17] = decode_hex("26603bb76dd0a0180791c4ed4d3b058807"); + + let output = cipher.encrypt_padded(&msg).expect("Failed to encrypt"); + + let expected_ciphertext: [u8; 32] = + decode_hex("8d55dc10584e243f55d2bdbb5758b7fabcd58c8d3785f01c7e3640b2a1dadcd9"); + assert_eq!(expected_ciphertext, &output[..]); + } + + #[test] + fn aes_128_cbc_test_decrypt() { + // https://github.com/google/wycheproof/blob/master/testvectors/aes_cbc_pkcs5_test.json#L30 + // tcId: 2 + let key = decode_hex("e09eaa5a3f5e56d279d5e7a03373f6ea"); + let iv = decode_hex("c9ee3cd746bf208c65ca9e72a266d54f"); + let cipher = Aes128Cbc::new_decrypt(&key, &iv); + let ciphertext: [u8; 32] = + decode_hex("d1fa697f3e2e04d64f1a0da203813ca5bc226a0b1d42287b2a5b994a66eaf14a"); + let decrypted = cipher + .decrypt_padded(&ciphertext) + .expect("Failed to decrypt"); + let expected_plaintext: [u8; 16] = decode_hex("ef4eab37181f98423e53e947e7050fd0"); + assert_eq!(expected_plaintext, &decrypted[..]); + } + + #[test] + fn aes_128_cbc_test_decrypt_empty_message() { + // https://github.com/google/wycheproof/blob/master/testvectors/aes_cbc_pkcs5_test.json#L20 + // tcId: 1 + let key = decode_hex("e34f15c7bd819930fe9d66e0c166e61c"); + let iv = decode_hex("da9520f7d3520277035173299388bee2"); + let cipher = Aes128Cbc::new_decrypt(&key, &iv); + let ciphertext: [u8; 16] = decode_hex("b10ab60153276941361000414aed0a9d"); + let decrypted = cipher + .decrypt_padded(&ciphertext) + .expect("Failed to decrypt"); + let expected_plaintext: [u8; 0] = decode_hex(""); + assert_eq!(expected_plaintext, &decrypted[..]); + } + + #[test] + pub fn aes_256_cbc_test_encrypt() { + // https://github.com/google/wycheproof/blob/master/testvectors/aes_cbc_pkcs5_test.json#L1412 + // tcId: 124 + let iv = decode_hex("9ec7b863ac845cad5e4673da21f5b6a9"); + let key = decode_hex("612e837843ceae7f61d49625faa7e7494f9253e20cb3adcea686512b043936cd"); + + let cipher = Aes256Cbc::new_encrypt(&key, &iv); + let msg: [u8; 16] = decode_hex("cc37fae15f745a2f40e2c8b192f2b38d"); + + let output = cipher.encrypt_padded(&msg).expect("Failed to encrypt"); + + let expected_ciphertext: [u8; 32] = + decode_hex("299295be47e9f5441fe83a7a811c4aeb2650333e681e69fa6b767d28a6ccf282"); + assert_eq!(expected_ciphertext, &output[..]); + } + + #[test] + pub fn aes_256_cbc_test_encrypt_more_than_one_block() { + // https://github.com/google/wycheproof/blob/master/testvectors/aes_cbc_pkcs5_test.json#L1582C24-L1582C24 + // tcId: 141 + let iv = decode_hex("4b74bd981ea9d074757c3e2ef515e5fb"); + let key = decode_hex("73216fafd0022d0d6ee27198b2272578fa8f04dd9f44467fbb6437aa45641bf7"); + + let cipher = Aes256Cbc::new_encrypt(&key, &iv); + let msg: [u8; 17] = decode_hex("d5247b8f6c3edcbfb1d591d13ece23d2f5"); + + let output = cipher.encrypt_padded(&msg).expect("Failed to encrypt"); + + let expected_ciphertext: [u8; 32] = + decode_hex("fbea776fb1653635f88e2937ed2450ba4e9063e96d7cdba04928f01cb85492fe"); + assert_eq!(expected_ciphertext, &output[..]); + } + + #[test] + fn aes_256_cbc_test_decrypt() { + // https://github.com/google/wycheproof/blob/master/testvectors/aes_cbc_pkcs5_test.json#L1452 + // tcId: 128 + let key = decode_hex("ea3b016bdd387dd64d837c71683808f335dbdc53598a4ea8c5f952473fafaf5f"); + let iv = decode_hex("fae3e2054113f6b3b904aadbfe59655c"); + let cipher = Aes256Cbc::new_decrypt(&key, &iv); + let ciphertext: [u8; 16] = decode_hex("b90c326b72eb222ddb4dae47f2bc223c"); + let decrypted = cipher + .decrypt_padded(&ciphertext) + .expect("Failed to decrypt"); + let expected_plaintext: [u8; 2] = decode_hex("6601"); + assert_eq!(expected_plaintext, &decrypted[..]); + } +} diff --git a/rust/bssl-crypto/src/cipher/aes_ctr.rs b/rust/bssl-crypto/src/cipher/aes_ctr.rs index 1375d3e..c9a122f 100644 --- a/rust/bssl-crypto/src/cipher/aes_ctr.rs +++ b/rust/bssl-crypto/src/cipher/aes_ctr.rs @@ -13,7 +13,9 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -use crate::cipher::{Cipher, CipherError, EvpAes128Ctr, EvpAes256Ctr, StreamCipher}; +use crate::cipher::{ + Cipher, CipherError, CipherInitPurpose, EvpAes128Ctr, EvpAes256Ctr, StreamCipher, +}; /// AES-CTR-128 Cipher implementation. pub struct Aes128Ctr(Cipher<EvpAes128Ctr>); @@ -24,7 +26,7 @@ impl StreamCipher for Aes128Ctr { /// Creates a new AES-128-CTR cipher instance from key material. fn new(key: &Self::Key, nonce: &Self::Nonce) -> Self { - Self(Cipher::new(key, nonce)) + Self(Cipher::new(key, nonce, CipherInitPurpose::Encrypt)) } /// Applies the keystream in-place, advancing the counter state appropriately. @@ -42,7 +44,7 @@ impl StreamCipher for Aes256Ctr { /// Creates a new AES-256-CTR cipher instance from key material. fn new(key: &Self::Key, nonce: &Self::Nonce) -> Self { - Self(Cipher::new(key, nonce)) + Self(Cipher::new(key, nonce, CipherInitPurpose::Encrypt)) } /// Applies the keystream in-place, advancing the counter state appropriately. diff --git a/rust/bssl-crypto/src/cipher/mod.rs b/rust/bssl-crypto/src/cipher/mod.rs index 2ff6b3a..16def56 100644 --- a/rust/bssl-crypto/src/cipher/mod.rs +++ b/rust/bssl-crypto/src/cipher/mod.rs @@ -13,7 +13,11 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +extern crate alloc; + use crate::{CSlice, CSliceMut}; +use alloc::vec; +use alloc::vec::Vec; use bssl_sys::EVP_CIPHER; use core::ffi::c_int; use core::marker::PhantomData; @@ -21,6 +25,9 @@ use core::marker::PhantomData; /// AES-CTR stream cipher operations. pub mod aes_ctr; +/// AES-CBC stream cipher operations. +pub mod aes_cbc; + /// Error returned in the event of an unsuccessful cipher operation. #[derive(Debug)] pub struct CipherError; @@ -42,6 +49,33 @@ pub trait StreamCipher { fn apply_keystream(&mut self, buffer: &mut [u8]) -> Result<(), CipherError>; } +/// Synchronous block cipher trait. +pub trait BlockCipher { + /// The byte array key type which specifies the size of the key used to instantiate the cipher. + type Key: AsRef<[u8]>; + + /// The byte array nonce type which specifies the size of the nonce used in the cipher + /// operations. + type Nonce: AsRef<[u8]>; + + /// Instantiate a new instance of a block cipher for encryption from a `key` and `iv`. + fn new_encrypt(key: &Self::Key, iv: &Self::Nonce) -> Self; + + /// Instantiate a new instance of a block cipher for decryption from a `key` and `iv`. + fn new_decrypt(key: &Self::Key, iv: &Self::Nonce) -> Self; + + /// Encrypts the given data in `buffer`, and returns the result (with padding) in a newly + /// allocated vector, or a [`CipherError`] if the operation was unsuccessful. + fn encrypt_padded(self, buffer: &[u8]) -> Result<Vec<u8>, CipherError>; + + /// Decrypts the given data in a `buffer`, and returns the result (with padding removed) in a + /// newly allocated vector, or a [`CipherError`] if the operation was unsuccessful. + fn decrypt_padded(self, buffer: &[u8]) -> Result<Vec<u8>, CipherError>; +} + +/// A cipher type, where `Key` is the size of the Key and `Nonce` is the size of the nonce or IV. +/// This must only be exposed publicly by types who ensure that `Key` is the correct size for the +/// given CipherType. This can be checked via `bssl_sys::EVP_CIPHER_key_length`. trait EvpCipherType { type Key: AsRef<[u8]>; type Nonce: AsRef<[u8]>; @@ -70,19 +104,41 @@ impl EvpCipherType for EvpAes256Ctr { } } -// Internal cipher implementation which wraps EVP_CIPHER_*, where K is the size of the Key and I is -// the size of the IV. This must only be exposed publicly by types who ensure that K is the correct -// size for the given CipherType. This can be checked via bssl_sys::EVP_CIPHER_key_length. -// -// WARNING: This is not safe to re-use for the CBC mode of operation since it is applying the -// key stream in-place. +struct EvpAes128Cbc; +impl EvpCipherType for EvpAes128Cbc { + type Key = [u8; 16]; + type Nonce = [u8; 16]; + fn evp_cipher() -> *const EVP_CIPHER { + // Safety: + // - this just returns a constant value + unsafe { bssl_sys::EVP_aes_128_cbc() } + } +} + +struct EvpAes256Cbc; +impl EvpCipherType for EvpAes256Cbc { + type Key = [u8; 32]; + type Nonce = [u8; 16]; + fn evp_cipher() -> *const EVP_CIPHER { + // Safety: + // - this just returns a constant value + unsafe { bssl_sys::EVP_aes_256_cbc() } + } +} + +enum CipherInitPurpose { + Encrypt, + Decrypt, +} + +/// Internal cipher implementation which wraps `EVP_CIPHER_*` struct Cipher<C: EvpCipherType> { ctx: *mut bssl_sys::EVP_CIPHER_CTX, _marker: PhantomData<C>, } impl<C: EvpCipherType> Cipher<C> { - fn new(key: &C::Key, iv: &C::Nonce) -> Self { + fn new(key: &C::Key, iv: &C::Nonce, purpose: CipherInitPurpose) -> Self { // Safety: // - Panics on allocation failure. let ctx = unsafe { bssl_sys::EVP_CIPHER_CTX_new() }; @@ -94,14 +150,25 @@ impl<C: EvpCipherType> Cipher<C> { // Safety: // - Key size and iv size must be properly set by the higher level wrapper types. // - Panics on allocation failure. - let result = unsafe { - bssl_sys::EVP_EncryptInit_ex( - ctx, - C::evp_cipher(), - core::ptr::null_mut(), - key_cslice.as_ptr(), - iv_cslice.as_ptr(), - ) + let result = match purpose { + CipherInitPurpose::Encrypt => unsafe { + bssl_sys::EVP_EncryptInit_ex( + ctx, + C::evp_cipher(), + core::ptr::null_mut(), + key_cslice.as_ptr(), + iv_cslice.as_ptr(), + ) + }, + CipherInitPurpose::Decrypt => unsafe { + bssl_sys::EVP_DecryptInit_ex( + ctx, + C::evp_cipher(), + core::ptr::null_mut(), + key_cslice.as_ptr(), + iv_cslice.as_ptr(), + ) + }, }; assert_eq!(result, 1); @@ -111,7 +178,20 @@ impl<C: EvpCipherType> Cipher<C> { } } + fn cipher_mode(&self) -> u32 { + // Safety: + // - The cipher context is initialized with EVP_EncryptInit_ex in `new` + unsafe { bssl_sys::EVP_CIPHER_CTX_mode(self.ctx) } + } + fn apply_keystream_in_place(&mut self, buffer: &mut [u8]) -> Result<(), CipherError> { + // WARNING: This is not safe to re-use for the CBC mode of operation since it is applying + // the key stream in-place. + assert_eq!( + self.cipher_mode(), + bssl_sys::EVP_CIPH_CTR_MODE as u32, + "Cannot use apply_keystraem_in_place for non-CTR modes" + ); let mut cslice_buf_mut = CSliceMut::from(buffer); let mut out_len = 0; @@ -135,6 +215,143 @@ impl<C: EvpCipherType> Cipher<C> { Err(CipherError) } } + + #[allow(clippy::expect_used)] + fn encrypt(self, buffer: &[u8]) -> Result<Vec<u8>, CipherError> { + // Safety: self.ctx is initialized with a cipher in `new()`. + let block_size_u32 = unsafe { bssl_sys::EVP_CIPHER_CTX_block_size(self.ctx) }; + let block_size: usize = block_size_u32 + .try_into() + .expect("Block size should always fit in usize"); + // Allocate an output vec that is large enough for both EncryptUpdate and EncryptFinal + // operations + let max_encrypt_update_output_size = buffer.len() + block_size - 1; + let max_encrypt_final_output_size = block_size; + let mut output_vec = + vec![0_u8; max_encrypt_update_output_size + max_encrypt_final_output_size]; + // EncryptUpdate block + let update_out_len_usize = { + let mut cslice_out_buf_mut = CSliceMut::from(&mut output_vec[..]); + let mut update_out_len = 0; + + let cslice_in_buf = CSlice::from(buffer); + let in_buff_len_int = c_int::try_from(cslice_in_buf.len()).map_err(|_| CipherError)?; + + // Safety: + // - `EVP_EncryptUpdate` requires that "The number of output bytes may be up to `in_len` + // plus the block length minus one and `out` must have sufficient space". This is the + // `max_encrypt_update_output_size` part of the output_vec's capacity. + let update_result = unsafe { + bssl_sys::EVP_EncryptUpdate( + self.ctx, + cslice_out_buf_mut.as_mut_ptr(), + &mut update_out_len, + cslice_in_buf.as_ptr(), + in_buff_len_int, + ) + }; + if update_result != 1 { + return Err(CipherError); + } + update_out_len + .try_into() + .expect("Output length should always fit in usize") + }; + + // EncryptFinal block + { + // Slice indexing here will not panic because we ensured `output_vec` is larger than + // what `EncryptUpdate` will write. + #[allow(clippy::indexing_slicing)] + let mut cslice_finalize_buf_mut = + CSliceMut::from(&mut output_vec[update_out_len_usize..]); + let mut final_out_len = 0; + let final_result = unsafe { + bssl_sys::EVP_EncryptFinal_ex( + self.ctx, + cslice_finalize_buf_mut.as_mut_ptr(), + &mut final_out_len, + ) + }; + let final_put_len_usize = + <usize>::try_from(final_out_len).expect("Output length should always fit in usize"); + if final_result == 1 { + output_vec.truncate(update_out_len_usize + final_put_len_usize) + } else { + return Err(CipherError); + } + } + Ok(output_vec) + } + + #[allow(clippy::expect_used)] + fn decrypt(self, in_buffer: &[u8]) -> Result<Vec<u8>, CipherError> { + // Safety: self.ctx is initialized with a cipher in `new()`. + let block_size_u32 = unsafe { bssl_sys::EVP_CIPHER_CTX_block_size(self.ctx) }; + let block_size: usize = block_size_u32 + .try_into() + .expect("Block size should always fit in usize"); + // Allocate an output vec that is large enough for both DecryptUpdate and DecryptFinal + // operations + let max_decrypt_update_output_size = in_buffer.len() + block_size - 1; + let max_decrypt_final_output_size = block_size; + let mut output_vec = + vec![0_u8; max_decrypt_update_output_size + max_decrypt_final_output_size]; + + // DecryptUpdate block + let update_out_len_usize = { + let mut cslice_out_buf_mut = CSliceMut::from(&mut output_vec[..]); + let mut update_out_len = 0; + + let cslice_in_buf = CSlice::from(in_buffer); + let in_buff_len_int = c_int::try_from(cslice_in_buf.len()).map_err(|_| CipherError)?; + + // Safety: + // - `EVP_DecryptUpdate` requires that "The number of output bytes may be up to `in_len` + // plus the block length minus one and `out` must have sufficient space". This is the + // `max_decrypt_update_output_size` part of the output_vec's capacity. + let update_result = unsafe { + bssl_sys::EVP_DecryptUpdate( + self.ctx, + cslice_out_buf_mut.as_mut_ptr(), + &mut update_out_len, + cslice_in_buf.as_ptr(), + in_buff_len_int, + ) + }; + if update_result != 1 { + return Err(CipherError); + } + update_out_len + .try_into() + .expect("Output length should always fit in usize") + }; + + // DecryptFinal block + { + // Slice indexing here will not panic because we ensured `output_vec` is larger than + // what `DecryptUpdate` will write. + #[allow(clippy::indexing_slicing)] + let mut cslice_final_buf_mut = CSliceMut::from(&mut output_vec[update_out_len_usize..]); + let mut final_out_len = 0; + let final_result = unsafe { + bssl_sys::EVP_DecryptFinal_ex( + self.ctx, + cslice_final_buf_mut.as_mut_ptr(), + &mut final_out_len, + ) + }; + let final_put_len_usize = + <usize>::try_from(final_out_len).expect("Output length should always fit in usize"); + + if final_result == 1 { + output_vec.truncate(update_out_len_usize + final_put_len_usize) + } else { + return Err(CipherError); + } + } + Ok(output_vec) + } } impl<C: EvpCipherType> Drop for Cipher<C> { @@ -144,3 +361,34 @@ impl<C: EvpCipherType> Drop for Cipher<C> { unsafe { bssl_sys::EVP_CIPHER_CTX_free(self.ctx) } } } + +#[cfg(test)] +mod test { + use crate::cipher::{CipherInitPurpose, EvpAes128Cbc, EvpAes128Ctr}; + + use super::Cipher; + + #[test] + fn test_cipher_mode() { + assert_eq!( + Cipher::<EvpAes128Ctr>::new(&[0; 16], &[0; 16], CipherInitPurpose::Encrypt) + .cipher_mode(), + bssl_sys::EVP_CIPH_CTR_MODE as u32 + ); + + assert_eq!( + Cipher::<EvpAes128Cbc>::new(&[0; 16], &[0; 16], CipherInitPurpose::Encrypt) + .cipher_mode(), + bssl_sys::EVP_CIPH_CBC_MODE as u32 + ); + } + + #[should_panic] + #[test] + fn test_apply_keystream_on_cbc() { + let mut cipher = + Cipher::<EvpAes128Cbc>::new(&[0; 16], &[0; 16], CipherInitPurpose::Encrypt); + let mut buf = [0; 16]; + let _ = cipher.apply_keystream_in_place(&mut buf); // This should panic + } +} |