update main-with-bazel from master branch
This commit is contained in:
@@ -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[..]);
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user