/* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! Module providing a shim for the different crypto operations. use android_hardware_security_see_hwcrypto::aidl::android::hardware::security::see::hwcrypto::types::{ HmacOperationParameters::HmacOperationParameters, KeyUse::KeyUse, SymmetricCryptoParameters::SymmetricCryptoParameters, SymmetricOperation::SymmetricOperation, }; use android_hardware_security_see_hwcrypto::aidl::android::hardware::security::see::hwcrypto::{ OperationParameters::OperationParameters, PatternParameters::PatternParameters, }; use hwcryptohal_common::{err::HwCryptoError, hwcrypto_err}; use kmr_common::crypto::{ self, Aes, Hmac, KeyMaterial, SymmetricOperation as CryptoSymmetricOperation, }; use crate::cmd_processing::DataToProcess; use crate::crypto_provider; use crate::helpers; use crate::opaque_key::OpaqueKey; // Pattern for cbcs operations. cbcs is based on partially encryption using AES-CBC as defined in // IEC 23001-7:2016 enum CbcsPattern { Protected(usize), Clear(usize), } struct CbcsPatternParams { num_encrypted_bytes: usize, num_clear_bytes: usize, current_pattern: CbcsPattern, } impl CbcsPatternParams { fn new(pattern_parameters: &PatternParameters) -> Result { let mut num_encrypted_blocks: usize = pattern_parameters.numberBlocksProcess.try_into().map_err(|e| { hwcrypto_err!(BAD_PARAMETER, "number encrypted blocks cannot be negative: {:?}", e) })?; let num_clear_blocks: usize = pattern_parameters.numberBlocksCopy.try_into().map_err(|e| { hwcrypto_err!(BAD_PARAMETER, "number clear blocks cannot be negative: {:?}", e) })?; // Special case. Some encoders pass a 0,0 to represent full sample encryption. Treating it // the same as 1, 0. if (num_encrypted_blocks == 0) && (num_clear_blocks == 0) { num_encrypted_blocks = 1; } let num_encrypted_bytes = num_encrypted_blocks .checked_mul(crypto::aes::BLOCK_SIZE) .ok_or(hwcrypto_err!(BAD_PARAMETER, "number encrypted blocks was too high"))?; let num_clear_bytes = num_clear_blocks .checked_mul(crypto::aes::BLOCK_SIZE) .ok_or(hwcrypto_err!(BAD_PARAMETER, "number clear blocks was too high"))?; // Patterns starts with a number of protected blocks let current_pattern = CbcsPattern::Protected(num_encrypted_bytes); Ok(Self { num_encrypted_bytes, num_clear_bytes, current_pattern }) } } pub(crate) trait ICryptographicOperation: Send { // Returns the required minimum size in bytes the output buffer needs to have for the given // `input` fn get_operation_req_size( &self, input: Option<&DataToProcess>, is_finish: bool, ) -> Result; fn operation<'a>( &mut self, input: Option<&mut DataToProcess<'a>>, output: &mut DataToProcess<'a>, is_finish: bool, ) -> Result; fn is_active(&self) -> bool; #[allow(dead_code)] fn update_aad(&mut self, _input: &DataToProcess) -> Result<(), HwCryptoError> { Err(hwcrypto_err!( BAD_PARAMETER, "update aad only valid for authenticated symmetric operations" )) } fn set_operation_pattern( &mut self, _patter_parameter: &PatternParameters, ) -> Result<(), HwCryptoError> { Err(hwcrypto_err!(BAD_PARAMETER, "set_operation_pattern only supported for AES CBC")) } } trait IBaseCryptoOperation: Send { fn update<'a>( &mut self, input: &mut DataToProcess<'a>, output: &mut DataToProcess<'a>, ) -> Result; fn finish(&mut self, output: &mut DataToProcess) -> Result; fn get_req_size_finish(&self) -> Result; fn get_req_size_update(&self, input: &DataToProcess) -> Result; fn is_active(&self) -> bool; fn update_aad(&mut self, _input: &DataToProcess) -> Result<(), HwCryptoError> { Err(hwcrypto_err!( BAD_PARAMETER, "update aad only valid for authenticated symmetric operations" )) } fn set_operation_pattern( &mut self, patter_parameter: &PatternParameters, ) -> Result<(), HwCryptoError>; } impl ICryptographicOperation for T { fn get_operation_req_size( &self, input: Option<&DataToProcess>, is_finish: bool, ) -> Result { if is_finish { self.get_req_size_finish() } else { let input = input.ok_or_else(|| hwcrypto_err!(BAD_PARAMETER, "input was not provided"))?; self.get_req_size_update(input) } } fn operation<'a>( &mut self, mut input: Option<&mut DataToProcess<'a>>, output: &mut DataToProcess<'a>, is_finish: bool, ) -> Result { if is_finish { self.finish(output) } else { let input = input.take().ok_or(hwcrypto_err!(BAD_PARAMETER, "input was not provided"))?; self.update(input, output) } } fn is_active(&self) -> bool { self.is_active() } fn update_aad(&mut self, input: &DataToProcess) -> Result<(), HwCryptoError> { self.update_aad(input) } fn set_operation_pattern( &mut self, patter_parameter: &PatternParameters, ) -> Result<(), HwCryptoError> { self.set_operation_pattern(patter_parameter) } } // Newtype used because the traits we currently use for cryptographic operations cannot directly // either process `VolatileSlice`s or use pointers to memory, so we need to make a copy of the data. // TODO: refactor traits to not require copying the input for VolatileSlices struct TempBuffer(Vec); impl TempBuffer { fn new() -> Self { TempBuffer(Vec::new()) } fn read_into_buffer_reference<'a>( &'a mut self, input: &'a mut DataToProcess, len: Option, ) -> Result<&'a [u8], HwCryptoError> { let len = len.unwrap_or(input.len()); if len > input.len() { return Err(hwcrypto_err!( BAD_PARAMETER, "end {} out of slice bounds for slice size {}", len, input.len() )); } if !input.is_non_volatile_slice_backed() { let slice = input.try_slice(len)?; Ok(slice) } else { self.0.clear(); self.0.try_reserve(len)?; // Addition should be safe because try_reserve didn't fail self.0.resize_with(len, Default::default); input.read_into_slice(self.0.as_mut_slice(), Some(len))?; Ok(&self.0[..]) } } } pub(crate) struct HmacOperation { accumulating_op: Option>, } impl HmacOperation { fn new(parameters: &HmacOperationParameters) -> Result { let opaque_key: OpaqueKey = parameters .key .as_ref() .ok_or(hwcrypto_err!(BAD_PARAMETER, "hmac key not provided"))? .try_into()?; Self::check_parameters(&opaque_key, parameters)?; let digest = helpers::aidl_to_rust_digest(&opaque_key.get_key_type())?; let hmac = crypto_provider::HmacImpl; let accumulating_op = match opaque_key.key_material { KeyMaterial::Hmac(key) => hmac.begin(key.clone(), digest).map_err(|e| { hwcrypto_err!(GENERIC_ERROR, "couldn't begin hmac operation: {:?}", e) }), _ => Err(hwcrypto_err!(BAD_PARAMETER, "Invalid key type for HMAC operation")), }?; Ok(HmacOperation { accumulating_op: Some(accumulating_op) }) } fn check_parameters( opaque_key: &OpaqueKey, _parameters: &HmacOperationParameters, ) -> Result<(), HwCryptoError> { if !opaque_key.key_usage_supported(KeyUse::SIGN) { return Err(hwcrypto_err!(BAD_PARAMETER, "Provided key cannot be used for signing")); } match &opaque_key.key_material { KeyMaterial::Hmac(_) => Ok(()), _ => Err(hwcrypto_err!(BAD_PARAMETER, "Invalid key type for HMAC operation")), } } } impl IBaseCryptoOperation for HmacOperation { fn get_req_size_update(&self, _input: &DataToProcess) -> Result { Ok(0) } fn get_req_size_finish(&self) -> Result { Ok(crypto_provider::HMAC_MAX_SIZE) } fn update( &mut self, input: &mut DataToProcess, _output: &mut DataToProcess, ) -> Result { let op = self .accumulating_op .as_mut() .ok_or(hwcrypto_err!(BAD_STATE, "operation was already finished"))?; // TODO: refactor traits to not require copying the input for VolatileSlices let mut input_buffer = TempBuffer::new(); let input_data = input_buffer.read_into_buffer_reference(input, None)?; op.update(input_data)?; Ok(0) } fn finish(&mut self, output: &mut DataToProcess) -> Result { let op = self .accumulating_op .take() .ok_or(hwcrypto_err!(BAD_STATE, "operation was already finished"))?; let req_size = self.get_req_size_finish()?; if output.len() != req_size { return Err(hwcrypto_err!(BAD_PARAMETER, "input size was not {}", req_size)); } let output_data = op.finish()?; let output_len = output_data.len(); output.append_slice(output_data.as_slice())?; Ok(output_len) } fn is_active(&self) -> bool { self.accumulating_op.is_some() } fn set_operation_pattern( &mut self, _patter_parameter: &PatternParameters, ) -> Result<(), HwCryptoError> { Err(hwcrypto_err!(BAD_PARAMETER, "set_operation_pattern only supported for AES CBC")) } } pub(crate) struct AesOperation { opaque_key: OpaqueKey, emitting_op: Option>, dir: CryptoSymmetricOperation, remaining_unaligned_data_size: usize, block_based_encryption: bool, cbcs_pattern: Option, operation_started: bool, } impl AesOperation { fn new( opaque_key: OpaqueKey, dir: SymmetricOperation, parameters: &SymmetricCryptoParameters, ) -> Result { AesOperation::check_cipher_parameters(&opaque_key, dir, parameters)?; let key_material = &opaque_key.key_material; let dir = helpers::aidl_to_rust_symmetric_direction(dir)?; let emitting_op = match key_material { KeyMaterial::Aes(key) => { let aes = crypto_provider::AesImpl; let mode = helpers::aidl_to_rust_aes_cipher_params(parameters, &opaque_key)?; aes.begin(key.clone(), mode, dir).map_err(|e| { hwcrypto_err!(GENERIC_ERROR, "couldn't begin aes operation: {:?}", e) }) } _ => Err(hwcrypto_err!(BAD_PARAMETER, "Invalid key type for AES symmetric operation")), }?; let block_based_encryption = helpers::symmetric_encryption_block_based(parameters)?; let aes_operation = Self { opaque_key, emitting_op: Some(emitting_op), dir, remaining_unaligned_data_size: 0, block_based_encryption, cbcs_pattern: None, operation_started: false, }; Ok(aes_operation) } fn check_cipher_parameters( opaque_key: &OpaqueKey, dir: SymmetricOperation, parameters: &SymmetricCryptoParameters, ) -> Result<(), HwCryptoError> { opaque_key.symmetric_operation_is_compatible(dir)?; opaque_key.parameters_are_compatible_symmetric_cipher(parameters) } // Returns the size required to process the current block and how much extra data was cached for // a future call fn get_update_req_size_with_remainder( &self, input: &DataToProcess, ) -> Result<(usize, usize), HwCryptoError> { let input_size = input.len(); self.get_req_size_from_len(input_size) } fn get_req_size_from_len(&self, input_len: usize) -> Result<(usize, usize), HwCryptoError> { if self.block_based_encryption { match self.dir { CryptoSymmetricOperation::Encrypt => { let input_size = input_len + self.remaining_unaligned_data_size; let extra_data_len = input_size % crypto::aes::BLOCK_SIZE; Ok((input_size - extra_data_len, extra_data_len)) } CryptoSymmetricOperation::Decrypt => { Ok((AesOperation::round_to_block_size(input_len), 0)) } } } else { Ok((input_len, 0)) } } fn round_to_block_size(size: usize) -> usize { ((size + crypto::aes::BLOCK_SIZE - 1) / crypto::aes::BLOCK_SIZE) * crypto::aes::BLOCK_SIZE } fn cbcs_update<'a>( &mut self, input: &mut DataToProcess<'a>, output: &mut DataToProcess<'a>, ) -> Result { let total_size = input.len(); if (total_size % crypto::aes::BLOCK_SIZE) != 0 { return Err(hwcrypto_err!( BAD_PARAMETER, "input size was not multiple of {}: {}", crypto::aes::BLOCK_SIZE, input.len() )); } if output.len() != input.len() { return Err(hwcrypto_err!(BAD_PARAMETER, "output size was not {}", input.len())); } let cbcs_pattern = self .cbcs_pattern .as_mut() .ok_or(hwcrypto_err!(BAD_PARAMETER, "not a cbcs operation"))?; // TODO: refactor to remove need of input copy for memory slices let mut input_buff = TempBuffer::new(); let mut remaining_len = total_size; let aes_op = self .emitting_op .as_mut() .ok_or(hwcrypto_err!(BAD_STATE, "operation was already finished"))?; while remaining_len > 0 { match cbcs_pattern.current_pattern { CbcsPattern::Protected(num_encrypted_bytes) => { let encrypted_bytes = std::cmp::min(remaining_len, num_encrypted_bytes); let input_data = input_buff.read_into_buffer_reference(input, Some(encrypted_bytes))?; let output_data = aes_op.update(input_data)?; output.append_slice(output_data.as_slice())?; if remaining_len > num_encrypted_bytes { // There is still data to process, advance index and change pattern to clear // In this case encrypted_bytes == num_encrypted_bytes cbcs_pattern.current_pattern = CbcsPattern::Clear(cbcs_pattern.num_clear_bytes); } else { // We processed all available data, check if we should change pattern or // keep the same if num_encrypted_bytes > remaining_len { // We are still on the protected pattern area cbcs_pattern.current_pattern = CbcsPattern::Protected(num_encrypted_bytes - remaining_len); } else { // We need to switch to a clear area cbcs_pattern.current_pattern = CbcsPattern::Clear(cbcs_pattern.num_clear_bytes); } break; } remaining_len -= num_encrypted_bytes; } CbcsPattern::Clear(num_clear_bytes) => { let clear_bytes = std::cmp::min(remaining_len, num_clear_bytes); output.read_from_slice(input, Some(clear_bytes))?; if remaining_len > num_clear_bytes { // There is still data to process, advance index and change pattern to // protected. In this case clear_bytes == num_clear_bytes cbcs_pattern.current_pattern = CbcsPattern::Protected(cbcs_pattern.num_encrypted_bytes); } else { // We processed all available data, check if we should change pattern or // keep the same if num_clear_bytes > remaining_len { // We are still on the clear pattern area cbcs_pattern.current_pattern = CbcsPattern::Clear(num_clear_bytes - remaining_len); } else { // We need to switch to a protected area cbcs_pattern.current_pattern = CbcsPattern::Protected(cbcs_pattern.num_encrypted_bytes); } break; } remaining_len -= num_clear_bytes; } } } Ok(total_size) } } impl IBaseCryptoOperation for AesOperation { fn update<'a>( &mut self, input: &mut DataToProcess<'a>, output: &mut DataToProcess<'a>, ) -> Result { self.operation_started = true; if self.cbcs_pattern.is_some() { return self.cbcs_update(input, output); } let (req_size, unaligned_size) = self.get_update_req_size_with_remainder(&input)?; if output.len() != req_size { return Err(hwcrypto_err!(BAD_PARAMETER, "input size was not {}", req_size)); } let op = self .emitting_op .as_mut() .ok_or(hwcrypto_err!(BAD_STATE, "operation was already finished"))?; // TODO: refactor traits to not require copying the input for VolatileSlices let mut input_buffer = TempBuffer::new(); let input_data = input_buffer.read_into_buffer_reference(input, None)?; let output_data = op.update(input_data)?; let output_len = output_data.len(); output.append_slice(output_data.as_slice())?; self.remaining_unaligned_data_size = unaligned_size; Ok(output_len) } fn finish(&mut self, output: &mut DataToProcess) -> Result { let op = self .emitting_op .take() .ok_or(hwcrypto_err!(BAD_STATE, "operation was already finished"))?; let req_size = self.get_req_size_finish()?; if output.len() != req_size { return Err(hwcrypto_err!(BAD_PARAMETER, "input size was not {}", req_size)); } let output_data = op.finish()?; let output_len = output_data.len(); output.append_slice(output_data.as_slice())?; self.remaining_unaligned_data_size = 0; self.operation_started = false; Ok(output_len) } fn update_aad(&mut self, _input: &DataToProcess) -> Result<(), HwCryptoError> { unimplemented!("GCM AES note supported yet"); } fn get_req_size_finish(&self) -> Result { if self.cbcs_pattern.is_some() { // On CBCS patterns we do not have more data to write on finish, because there is no // padding needed and all operations were done using block boundaries. Ok(0) } else { let (req_size_to_process, _) = self.get_req_size_from_len(0)?; match self.dir { CryptoSymmetricOperation::Encrypt => { Ok(req_size_to_process + crypto::aes::BLOCK_SIZE) } CryptoSymmetricOperation::Decrypt => Ok(crypto::aes::BLOCK_SIZE), } } } fn get_req_size_update(&self, input: &DataToProcess) -> Result { if self.cbcs_pattern.is_some() { // On CBCS patterns we are currently processing a number of bytes multiple of block // sizes, so the space needed is always the size of the input. if (input.len() % crypto::aes::BLOCK_SIZE) != 0 { return Err(hwcrypto_err!( BAD_PARAMETER, "input size was not multiple of {}: {}", crypto::aes::BLOCK_SIZE, input.len() )); } Ok(input.len()) } else { let (req_size, _) = self.get_update_req_size_with_remainder(input)?; Ok(req_size) } } fn is_active(&self) -> bool { self.emitting_op.is_some() } fn set_operation_pattern( &mut self, pattern_parameters: &PatternParameters, ) -> Result<(), HwCryptoError> { self.opaque_key.supports_pattern_encryption()?; // We only support setting a pattern if we have not started encrypting/decrypting if self.operation_started { return Err(hwcrypto_err!(BAD_STATE, "pattern cannot be set if operation has started")); } // We do not support changing an already set up pattern if self.cbcs_pattern.is_some() { return Err(hwcrypto_err!(BAD_STATE, "pattern has already been set")); } self.cbcs_pattern = Some(CbcsPatternParams::new(pattern_parameters)?); Ok(()) } } pub(crate) struct CopyOperation; impl ICryptographicOperation for CopyOperation { fn get_operation_req_size( &self, input: Option<&DataToProcess>, _is_finish: bool, ) -> Result { let input = input.ok_or_else(|| hwcrypto_err!(BAD_PARAMETER, "input was not provided"))?; Ok(input.len()) } fn operation<'a>( &mut self, input: Option<&mut DataToProcess<'a>>, output: &mut DataToProcess<'a>, _is_finish: bool, ) -> Result { let num_bytes_copy = self.get_operation_req_size(input.as_deref(), false)?; let mut input = input.ok_or_else(|| hwcrypto_err!(BAD_PARAMETER, "input was not provided"))?; output.read_from_slice(&mut input, None)?; Ok(num_bytes_copy) } fn is_active(&self) -> bool { true } } pub(crate) struct CryptographicOperation; impl CryptographicOperation { pub(crate) fn new_binder( crypto_operation_parameters: &OperationParameters, ) -> Result, HwCryptoError> { match crypto_operation_parameters { OperationParameters::SymmetricCrypto(symmetric_params) => { if let Some(key) = &symmetric_params.key { let opaque_key: OpaqueKey = key.try_into()?; let dir = symmetric_params.direction; let parameters = &symmetric_params.parameters; AesOperation::check_cipher_parameters(&opaque_key, dir, parameters)?; let aes_operation = AesOperation::new(opaque_key, dir, parameters)?; Ok(Box::new(aes_operation)) } else { Err(hwcrypto_err!(BAD_PARAMETER, "key was null")) } } OperationParameters::Hmac(params) => { let hmac_op = HmacOperation::new(params)?; Ok(Box::new(hmac_op)) } _ => unimplemented!("operation not implemented yet"), } } } // Implementing ICryptographicOperation for () to use it as a type for when we need to pass a `None` // on an `Option<&impl ICryptographicOperation>` impl ICryptographicOperation for () { fn get_operation_req_size( &self, _input: Option<&DataToProcess>, _is_finish: bool, ) -> Result { Err(hwcrypto_err!(UNSUPPORTED, "cannot get size for null operation")) } fn operation( &mut self, _input: Option<&mut DataToProcess>, _output: &mut DataToProcess, _is_finish: bool, ) -> Result { Err(hwcrypto_err!(UNSUPPORTED, "nothing to execute on null operation")) } fn is_active(&self) -> bool { false } } #[cfg(test)] mod tests { use super::*; use android_hardware_security_see_hwcrypto::aidl::android::hardware::security::see::hwcrypto::types::{ AesCipherMode::AesCipherMode, CipherModeParameters::CipherModeParameters, KeyLifetime::KeyLifetime, KeyType::KeyType, KeyUse::KeyUse, SymmetricCryptoParameters::SymmetricCryptoParameters, SymmetricOperation::SymmetricOperation, SymmetricOperationParameters::SymmetricOperationParameters, }; use android_hardware_security_see_hwcrypto::aidl::android::hardware::security::see::hwcrypto::{ KeyPolicy::KeyPolicy, }; use test::{expect, expect_eq}; use tipc::Uuid; fn connection_info() -> Uuid { // TODO: This is a temporary mock function for testing until we move to use DICE policies. Uuid::new_from_string("f41a7796-975a-4279-8cc4-b73f8820430d").unwrap() } #[test] fn use_aes_key() { let usage = KeyUse::ENCRYPT_DECRYPT; let key_type = KeyType::AES_256_CBC_PKCS7_PADDING; let policy = KeyPolicy { usage, keyLifetime: KeyLifetime::EPHEMERAL, keyPermissions: Vec::new(), keyType: key_type, keyManagementKey: false, }; let handle = OpaqueKey::generate_opaque_key(&policy, connection_info()) .expect("couldn't generate key"); let nonce = [0u8; 16]; let parameters = SymmetricCryptoParameters::Aes(AesCipherMode::Cbc(CipherModeParameters { nonce: nonce.into(), })); let direction = SymmetricOperation::ENCRYPT; let sym_op_params = SymmetricOperationParameters { key: Some(handle.clone()), direction, parameters }; let op_params = OperationParameters::SymmetricCrypto(sym_op_params); let input_to_encrypt = "hello world1234"; let mut input_data = input_to_encrypt.as_bytes().to_vec(); let mut input_slice = DataToProcess::new_from_slice(&mut input_data[..]); let mut op = CryptographicOperation::new_binder(&op_params).expect("couldn't create aes operation"); let req_size = op .get_operation_req_size(Some(&input_slice), false) .expect("couldn't get required_size"); expect_eq!(req_size, 0, "Required size for encryptiong less than a block should be 0"); let mut output_data = vec![]; let mut output_slice = DataToProcess::new_from_slice(&mut output_data[..]); let written_bytes = op .operation(Some(&mut input_slice), &mut output_slice, false) .expect("couldn't update"); expect_eq!(written_bytes, 0, "Written bytes for encryptiong less than a block should be 0"); let req_size_finish = op.get_operation_req_size(None, true).expect("couldn't get required_size"); expect_eq!( req_size_finish, 16, "Required size for encryptiong less than a block should be a block" ); output_data.append(&mut vec![0u8; 16]); let mut output_slice = DataToProcess::new_from_slice(&mut output_data[..]); op.operation(None, &mut output_slice, true).expect("couldn't finish"); let mut output_slice = DataToProcess::new_from_slice(&mut output_data[0..0]); let mut input_slice = DataToProcess::new_from_slice(&mut input_data[..]); let update_op = op.operation(Some(&mut input_slice), &mut output_slice, false); expect!(update_op.is_err(), "shouldn't be able to run operations anymore"); let mut output_slice = DataToProcess::new_from_slice(&mut output_data[0..0]); let finish_op = op.operation(None, &mut output_slice, true); expect!(finish_op.is_err(), "shouldn't be able to run operations anymore"); let direction = SymmetricOperation::DECRYPT; let parameters = SymmetricCryptoParameters::Aes(AesCipherMode::Cbc(CipherModeParameters { nonce: nonce.into(), })); let sym_op_params = SymmetricOperationParameters { key: Some(handle), direction, parameters }; let op_params = OperationParameters::SymmetricCrypto(sym_op_params); let mut op = CryptographicOperation::new_binder(&op_params).expect("couldn't create aes operation"); let mut output_slice = DataToProcess::new_from_slice(&mut output_data[..]); let req_size = op .get_operation_req_size(Some(&output_slice), false) .expect("couldn't get required_size"); let mut decrypted_data = vec![0; req_size]; let mut decrypted_slice = DataToProcess::new_from_slice(&mut decrypted_data[..]); let mut decrypted_data_size = op .operation(Some(&mut output_slice), &mut decrypted_slice, false) .expect("couldn't update"); let decrypted_data_start = decrypted_data_size; let req_size_finish = op.get_operation_req_size(None, true).expect("couldn't get required_size"); let decrypted_data_end = decrypted_data_size + req_size_finish; let mut decrypted_slice = DataToProcess::new_from_slice( &mut decrypted_data[decrypted_data_start..decrypted_data_end], ); let total_finish_size = op.operation(None, &mut decrypted_slice, true).expect("couldn't finish"); decrypted_data_size += total_finish_size; decrypted_data.truncate(decrypted_data_size); expect_eq!(input_to_encrypt.len(), decrypted_data_size, "bad length for decrypted data"); let decrypted_str = String::from_utf8(decrypted_data).unwrap(); expect_eq!(input_to_encrypt, decrypted_str, "bad data decrypted"); } #[test] fn process_aes_encrypt_decrypt_operations() { let usage = KeyUse::ENCRYPT_DECRYPT; let key_type = KeyType::AES_256_CBC_PKCS7_PADDING; let policy = KeyPolicy { usage, keyLifetime: KeyLifetime::EPHEMERAL, keyPermissions: Vec::new(), keyType: key_type, keyManagementKey: false, }; let handle = OpaqueKey::generate_opaque_key(&policy, connection_info()) .expect("couldn't generate key"); let nonce = [0u8; 16]; let parameters = SymmetricCryptoParameters::Aes(AesCipherMode::Cbc(CipherModeParameters { nonce: nonce.into(), })); let direction = SymmetricOperation::ENCRYPT; let sym_op_params = SymmetricOperationParameters { key: Some(handle.clone()), direction, parameters }; let op_params = OperationParameters::SymmetricCrypto(sym_op_params); let mut op = CryptographicOperation::new_binder(&op_params).expect("couldn't create aes operation"); let input_to_encrypt = "test encryption string"; let mut input_data = input_to_encrypt.as_bytes().to_vec(); let mut input_slice = DataToProcess::new_from_slice(&mut input_data[..]); let req_size = op .get_operation_req_size(Some(&input_slice), false) .expect("couldn't get required_size"); expect_eq!(req_size, 16, "Implementation should try to encrypt a block in this case"); let mut output_data = vec![0; 200]; let mut output_slice = DataToProcess::new_from_slice(&mut output_data[..req_size]); let mut total_encryption_size = 0; let written_bytes = op .operation(Some(&mut input_slice), &mut output_slice, false) .expect("couldn't update"); total_encryption_size += written_bytes; expect_eq!(written_bytes, 16, "A block should have been encrypted"); let input_to_encrypt_2 = " for this "; let mut input_data = input_to_encrypt_2.as_bytes().to_vec(); let mut input_slice = DataToProcess::new_from_slice(&mut input_data[..]); let req_size = op .get_operation_req_size(Some(&input_slice), false) .expect("couldn't get required_size"); let output_start = written_bytes; let output_stop = written_bytes + req_size; expect_eq!(req_size, 16, "Implementation should try to encrypt a block in this case"); let mut output_slice = DataToProcess::new_from_slice(&mut output_data[output_start..output_stop]); let written_bytes = op .operation(Some(&mut input_slice), &mut output_slice, false) .expect("couldn't update"); expect_eq!(written_bytes, 16, "A block should have been encrypted"); total_encryption_size += written_bytes; let output_start = output_start + written_bytes; let input_to_encrypt_3 = "test"; let mut input_data = input_to_encrypt_3.as_bytes().to_vec(); let mut input_slice = DataToProcess::new_from_slice(&mut input_data[..]); let req_size = op .get_operation_req_size(Some(&input_slice), false) .expect("couldn't get required_size"); expect_eq!(req_size, 0, "Required size for encryptiong less than a block should be 0"); let mut output_slice = DataToProcess::new_from_slice(&mut output_data[output_start..output_start]); let written_bytes = op .operation(Some(&mut input_slice), &mut output_slice, false) .expect("couldn't update"); total_encryption_size += written_bytes; expect_eq!(written_bytes, 0, "No bytes should have been written"); let input_to_encrypt_4 = " is"; let mut input_data = input_to_encrypt_4.as_bytes().to_vec(); let mut input_slice = DataToProcess::new_from_slice(&mut input_data[..]); let req_size = op .get_operation_req_size(Some(&input_slice), false) .expect("couldn't get required_size"); expect_eq!(req_size, 0, "Required size for encryptiong less than a block should be 0"); let mut output_slice = DataToProcess::new_from_slice(&mut output_data[output_start..output_start]); let written_bytes = op .operation(Some(&mut input_slice), &mut output_slice, false) .expect("couldn't update"); expect_eq!(written_bytes, 0, "No bytes should have been written"); total_encryption_size += written_bytes; let input_to_encrypt_5 = " a "; let mut input_data = input_to_encrypt_5.as_bytes().to_vec(); let mut input_slice = DataToProcess::new_from_slice(&mut input_data[..]); let req_size = op .get_operation_req_size(Some(&input_slice), false) .expect("couldn't get required_size"); expect_eq!(req_size, 0, "Required size for encryptiong less than a block should be 0"); let mut output_slice = DataToProcess::new_from_slice(&mut output_data[output_start..output_start]); let written_bytes = op .operation(Some(&mut input_slice), &mut output_slice, false) .expect("couldn't update"); expect_eq!(written_bytes, 0, "No bytes should have been written"); total_encryption_size += written_bytes; let input_to_encrypt_6 = "random one."; let mut input_data = input_to_encrypt_6.as_bytes().to_vec(); let mut input_slice = DataToProcess::new_from_slice(&mut input_data[..]); let req_size = op .get_operation_req_size(Some(&input_slice), false) .expect("couldn't get required_size"); expect_eq!(req_size, 16, "Implementation should try to encrypt a block in this case"); let output_stop = output_start + req_size; let mut output_slice = DataToProcess::new_from_slice(&mut output_data[output_start..output_stop]); let written_bytes = op .operation(Some(&mut input_slice), &mut output_slice, false) .expect("couldn't update"); total_encryption_size += written_bytes; expect_eq!(written_bytes, 16, "A block should have been encrypted"); let output_start = output_start + written_bytes; let req_size_finish = op.get_operation_req_size(None, true).expect("couldn't get required_size"); expect_eq!( req_size_finish, 16, "Required size for encryptiong less than a block should be a block" ); let output_stop = output_start + req_size_finish; let mut output_slice = DataToProcess::new_from_slice(&mut output_data[output_start..output_stop]); let finish_written_bytes = op.operation(None, &mut output_slice, true).expect("couldn't finish"); expect_eq!(finish_written_bytes, 16, "With padding we should have written a block"); total_encryption_size += finish_written_bytes; output_data.truncate(total_encryption_size); // Decrypting let mut decrypted_data_size = 0; let direction = SymmetricOperation::DECRYPT; let parameters = SymmetricCryptoParameters::Aes(AesCipherMode::Cbc(CipherModeParameters { nonce: nonce.into(), })); let sym_op_params = SymmetricOperationParameters { key: Some(handle), direction, parameters }; let op_params = OperationParameters::SymmetricCrypto(sym_op_params); let mut op = CryptographicOperation::new_binder(&op_params).expect("couldn't create aes operation"); let mut decrypted_data = vec![0; total_encryption_size]; let mut output_slice = DataToProcess::new_from_slice(&mut output_data[..4]); let req_size = op .get_operation_req_size(Some(&output_slice), false) .expect("couldn't get required_size"); expect_eq!(req_size, 16, "worse case space for this size of input is a block"); let mut decrypted_slice = DataToProcess::new_from_slice(&mut decrypted_data[..16]); let written_bytes = op .operation(Some(&mut output_slice), &mut decrypted_slice, false) .expect("couldn't update"); decrypted_data_size += written_bytes; expect_eq!(written_bytes, 0, "No bytes should have been written"); let mut output_slice = DataToProcess::new_from_slice(&mut output_data[4..32]); let req_size = op .get_operation_req_size(Some(&output_slice), false) .expect("couldn't get required_size"); expect_eq!(req_size, 32, "worse case space for this size of input is 2 blocks"); let mut decrypted_slice = DataToProcess::new_from_slice(&mut decrypted_data[..32]); let written_bytes = op .operation(Some(&mut output_slice), &mut decrypted_slice, false) .expect("couldn't update"); decrypted_data_size += written_bytes; expect_eq!(written_bytes, 16, "One block should have been written"); let mut output_slice = DataToProcess::new_from_slice(&mut output_data[32..50]); let req_size = op .get_operation_req_size(Some(&output_slice), false) .expect("couldn't get required_size"); expect_eq!(req_size, 32, "worse case space for this size of input is 2 blocks"); let mut decrypted_slice = DataToProcess::new_from_slice(&mut decrypted_data[16..48]); let written_bytes = op .operation(Some(&mut output_slice), &mut decrypted_slice, false) .expect("couldn't update"); decrypted_data_size += written_bytes; expect_eq!(written_bytes, 32, "Two block should have been written"); let mut output_slice = DataToProcess::new_from_slice(&mut output_data[50..64]); let req_size = op .get_operation_req_size(Some(&output_slice), false) .expect("couldn't get required_size"); expect_eq!(req_size, 16, "worse case space for this size of input is 1 block"); let mut decrypted_slice = DataToProcess::new_from_slice(&mut decrypted_data[48..64]); let written_bytes = op .operation(Some(&mut output_slice), &mut decrypted_slice, false) .expect("couldn't update"); decrypted_data_size += written_bytes; expect_eq!(written_bytes, 0, "No blocks should have been written"); let req_size_finish = op.get_operation_req_size(None, true).expect("couldn't get required_size"); expect_eq!(req_size_finish, 16, "Max size required to finish should be 1 block"); let mut decrypted_slice = DataToProcess::new_from_slice(&mut decrypted_data[48..64]); let total_finish_size = op.operation(None, &mut decrypted_slice, true).expect("couldn't finish"); decrypted_data_size += total_finish_size; decrypted_data.truncate(decrypted_data_size); let decrypted_msg = String::from_utf8(decrypted_data).expect("couldn't decode receivedd message"); let original_msg = input_to_encrypt.to_owned() + input_to_encrypt_2 + input_to_encrypt_3 + input_to_encrypt_4 + input_to_encrypt_5 + input_to_encrypt_6; expect_eq!(original_msg.len(), decrypted_msg.len(), "bad length for decrypted data"); expect_eq!(original_msg, decrypted_msg, "bad data decrypted"); } }