| /* |
| * 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::aidl::android::hardware::security::see::hwcrypto::types::{ |
| HmacOperationParameters::HmacOperationParameters, KeyUse::KeyUse, |
| SymmetricCryptoParameters::SymmetricCryptoParameters, SymmetricOperation::SymmetricOperation, |
| }; |
| use android_hardware_security_see::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<Self, HwCryptoError> { |
| 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<usize, HwCryptoError>; |
| |
| fn operation<'a>( |
| &mut self, |
| input: Option<&mut DataToProcess<'a>>, |
| output: &mut DataToProcess<'a>, |
| is_finish: bool, |
| ) -> Result<usize, HwCryptoError>; |
| |
| 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<usize, HwCryptoError>; |
| |
| fn finish(&mut self, output: &mut DataToProcess) -> Result<usize, HwCryptoError>; |
| |
| fn get_req_size_finish(&self) -> Result<usize, HwCryptoError>; |
| |
| fn get_req_size_update(&self, input: &DataToProcess) -> Result<usize, HwCryptoError>; |
| |
| 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<T: IBaseCryptoOperation> ICryptographicOperation for T { |
| fn get_operation_req_size( |
| &self, |
| input: Option<&DataToProcess>, |
| is_finish: bool, |
| ) -> Result<usize, HwCryptoError> { |
| 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<usize, HwCryptoError> { |
| 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<u8>); |
| |
| impl TempBuffer { |
| fn new() -> Self { |
| TempBuffer(Vec::new()) |
| } |
| |
| fn read_into_buffer_reference<'a>( |
| &'a mut self, |
| input: &'a mut DataToProcess, |
| len: Option<usize>, |
| ) -> 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<Box<dyn crypto::AccumulatingOperation>>, |
| } |
| |
| impl HmacOperation { |
| fn new(parameters: &HmacOperationParameters) -> Result<Self, HwCryptoError> { |
| 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<usize, HwCryptoError> { |
| Ok(0) |
| } |
| |
| fn get_req_size_finish(&self) -> Result<usize, HwCryptoError> { |
| Ok(crypto_provider::HMAC_MAX_SIZE) |
| } |
| |
| fn update( |
| &mut self, |
| input: &mut DataToProcess, |
| _output: &mut DataToProcess, |
| ) -> Result<usize, HwCryptoError> { |
| 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<usize, HwCryptoError> { |
| 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<Box<dyn crypto::EmittingOperation>>, |
| dir: CryptoSymmetricOperation, |
| remaining_unaligned_data_size: usize, |
| block_based_encryption: bool, |
| cbcs_pattern: Option<CbcsPatternParams>, |
| operation_started: bool, |
| } |
| |
| impl AesOperation { |
| fn new( |
| opaque_key: OpaqueKey, |
| dir: SymmetricOperation, |
| parameters: &SymmetricCryptoParameters, |
| ) -> Result<Self, HwCryptoError> { |
| 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<usize, HwCryptoError> { |
| 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<usize, HwCryptoError> { |
| 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<usize, HwCryptoError> { |
| 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<usize, HwCryptoError> { |
| 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<usize, HwCryptoError> { |
| 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<usize, HwCryptoError> { |
| 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<usize, HwCryptoError> { |
| 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<Box<dyn ICryptographicOperation>, 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<usize, HwCryptoError> { |
| 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<usize, HwCryptoError> { |
| 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::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::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"); |
| } |
| } |