| /* |
| * Copyright (C) 2023 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. |
| */ |
| #[cfg(test)] |
| mod tests { |
| pub(crate) const RUST_HWCRYPTO_SERVICE_PORT: &str = "com.android.trusty.rust.hwcryptohal.V1"; |
| |
| use android_hardware_security_see_hwcrypto::aidl::android::hardware::security::see::hwcrypto::{ |
| types::{ |
| AesCipherMode::AesCipherMode, AesKey::AesKey, |
| CipherModeParameters::CipherModeParameters, ExplicitKeyMaterial::ExplicitKeyMaterial, |
| KeyLifetime::KeyLifetime, KeyType::KeyType, KeyUse::KeyUse, |
| OperationData::OperationData, SymmetricCryptoParameters::SymmetricCryptoParameters, |
| SymmetricOperation::SymmetricOperation, |
| SymmetricOperationParameters::SymmetricOperationParameters, |
| }, |
| CryptoOperation::CryptoOperation, |
| CryptoOperationErrorAdditionalInfo::CryptoOperationErrorAdditionalInfo, |
| CryptoOperationSet::CryptoOperationSet, |
| ICryptoOperationContext::ICryptoOperationContext, |
| IHwCryptoKey::IHwCryptoKey, |
| KeyPolicy::KeyPolicy, |
| OperationParameters::OperationParameters, |
| }; |
| use binder::Strong; |
| use rpcbinder::RpcSession; |
| use std::collections::HashMap; |
| use test::expect; |
| use trusty_std::ffi::{CString, FallibleCString}; |
| |
| #[derive(Debug, Clone, PartialEq)] |
| enum OPERATION { |
| ENCRYPT, |
| DECRYPT, |
| } |
| |
| #[derive(Debug, Clone, PartialEq)] |
| enum MODE { |
| CBC, |
| CTR, |
| } |
| |
| #[derive(Debug)] |
| struct Vector { |
| op: Option<OPERATION>, |
| mode: Option<MODE>, |
| key_length: Option<u16>, |
| iv_size: Option<u16>, |
| payload_size: Option<u16>, |
| params: HashMap<String, String>, |
| } |
| |
| #[test] |
| fn aes_vector_test() { |
| run_aes_vectors(parse_vectors(include_str!("vectors/CAVP/KAT_AES/CBCGFSbox128.rsp"))); |
| run_aes_vectors(parse_vectors(include_str!("vectors/CAVP/KAT_AES/CBCGFSbox256.rsp"))); |
| run_aes_vectors(parse_vectors(include_str!("vectors/CAVP/KAT_AES/CBCKeySbox128.rsp"))); |
| run_aes_vectors(parse_vectors(include_str!("vectors/CAVP/KAT_AES/CBCKeySbox256.rsp"))); |
| run_aes_vectors(parse_vectors(include_str!("vectors/CAVP/KAT_AES/CBCVarKey128.rsp"))); |
| run_aes_vectors(parse_vectors(include_str!("vectors/CAVP/KAT_AES/CBCVarKey256.rsp"))); |
| run_aes_vectors(parse_vectors(include_str!("vectors/CAVP/KAT_AES/CBCVarTxt128.rsp"))); |
| run_aes_vectors(parse_vectors(include_str!("vectors/CAVP/KAT_AES/CBCVarTxt256.rsp"))); |
| |
| run_aes_vectors(parse_vectors(include_str!("vectors/CAVP/aesmmt/CBCMMT128.rsp"))); |
| run_aes_vectors(parse_vectors(include_str!("vectors/CAVP/aesmmt/CBCMMT256.rsp"))); |
| |
| run_aes_vectors(parse_vectors(include_str!("vectors/NIST/CTR/ctr_128.rsp"))); |
| run_aes_vectors(parse_vectors(include_str!("vectors/NIST/CTR/ctr_256.rsp"))); |
| } |
| |
| fn hex_to_bytes(s: &str) -> Option<Vec<u8>> { |
| if s.len() % 2 == 0 { |
| (0..s.len()) |
| .step_by(2) |
| .map(|i| s.get(i..i + 2).and_then(|sub| u8::from_str_radix(sub, 16).ok())) |
| .collect() |
| } else { |
| None |
| } |
| } |
| |
| fn parse_vectors(raw: &str) -> Vec<Vector> { |
| let mut vectors: Vec<Vector> = Vec::new(); |
| |
| let mut params: HashMap<String, String> = HashMap::new(); |
| let mut mode: Option<MODE> = None; |
| let mut op: Option<OPERATION> = None; |
| let mut key_length: Option<u16> = None; |
| let mut iv_size: Option<u16> = None; |
| let mut payload_size: Option<u16>; |
| |
| for line in raw.lines() { |
| // Check for header settings |
| if line.contains("test data for") { |
| let parts: Vec<&str> = line.split("test data for").collect(); |
| let mode_str = parts[1].trim(); |
| |
| match mode_str { |
| "CBC" => { |
| mode = Some(MODE::CBC); |
| iv_size = Some(128); |
| } |
| "CTR" => { |
| mode = Some(MODE::CTR); |
| iv_size = Some(128); |
| } |
| _ => { |
| mode = None; |
| iv_size = None; |
| } |
| }; |
| } |
| |
| // Check for key length |
| if line.contains("Key Length") { |
| let parts: Vec<&str> = line.split(":").collect(); |
| key_length = Some(parts[1].trim().parse::<u16>().unwrap()); |
| } |
| |
| // Check for encrypt or decrypt |
| if line.contains("[ENCRYPT]") { |
| op = Some(OPERATION::ENCRYPT); |
| } |
| |
| if line.contains("[DECRYPT]") { |
| op = Some(OPERATION::DECRYPT); |
| } |
| |
| // Check for vector components |
| if line.contains("=") { |
| let words: Vec<_> = line.split_whitespace().filter(|s| s != &"=").collect(); |
| params.insert(words[0].to_string(), words[1].to_string()); |
| } |
| |
| // Check for vector completion |
| if line.trim().len() == 0 && params.len() > 0 { |
| // Vector complete, add to array |
| payload_size = Some((params["PLAINTEXT"].len() * 4).try_into().unwrap()); |
| |
| let current_vector = Vector { |
| op: op.clone(), |
| mode: mode.clone(), |
| key_length: key_length.clone(), |
| iv_size: iv_size.clone(), |
| payload_size: payload_size.clone(), |
| params: params.clone(), |
| }; |
| params.clear(); |
| vectors.push(current_vector); |
| } |
| } |
| |
| // Add last vector to array if not yet added |
| if params.len() > 0 { |
| payload_size = Some((params["PLAINTEXT"].len() * 4).try_into().unwrap()); |
| |
| let current_vector = Vector { |
| op: op.clone(), |
| mode: mode.clone(), |
| key_length: key_length.clone(), |
| iv_size: iv_size.clone(), |
| payload_size: payload_size.clone(), |
| params: params.clone(), |
| }; |
| params.clear(); |
| vectors.push(current_vector); |
| } |
| |
| vectors |
| } |
| |
| fn get_key_type(key_length: &u16, mode: &MODE) -> Option<KeyType> { |
| match key_length { |
| 128 => match mode { |
| MODE::CBC => Some(KeyType::AES_128_CBC_NO_PADDING), |
| MODE::CTR => Some(KeyType::AES_128_CTR), |
| }, |
| 256 => match mode { |
| MODE::CBC => Some(KeyType::AES_256_CBC_NO_PADDING), |
| MODE::CTR => Some(KeyType::AES_256_CTR), |
| }, |
| _ => None, |
| } |
| } |
| |
| fn run_aes_vectors(vectors: Vec<Vector>) { |
| let port = |
| CString::try_new(RUST_HWCRYPTO_SERVICE_PORT).expect("Failed to allocate port name"); |
| let hw_crypto: Strong<dyn IHwCryptoKey> = |
| RpcSession::new().setup_trusty_client(port.as_c_str()).expect("Failed to connect"); |
| let hw_crypto_ops = |
| hw_crypto.getHwCryptoOperations().expect("couldn't get key crypto ops."); |
| |
| let mut current_key: Vec<u8> = Vec::new(); |
| let mut current_iv: Vec<u8> = Vec::new(); |
| let mut new_iv: bool; |
| |
| let mut context: Option<Strong<dyn ICryptoOperationContext>> = None; |
| |
| for v in vectors { |
| if v.params.contains_key("IV") { |
| current_key = hex_to_bytes(v.params["KEY"].as_str()).expect("Bad hex value"); |
| expect!( |
| current_key.len() * 8 == v.key_length.unwrap() as usize, |
| "Invalid key length" |
| ); |
| |
| current_iv = hex_to_bytes(v.params["IV"].as_str()).expect("Bad hex value"); |
| expect!(current_iv.len() * 8 == v.iv_size.unwrap() as usize, "Invalid IV length"); |
| |
| new_iv = true; |
| context = None; |
| } else { |
| new_iv = false; |
| } |
| |
| let plaintext: Vec<u8> = |
| hex_to_bytes(v.params["PLAINTEXT"].as_str()).expect("Bad hex value"); |
| let ciphertext: Vec<u8> = |
| hex_to_bytes(v.params["CIPHERTEXT"].as_str()).expect("Bad hex value"); |
| |
| expect!(plaintext.len() * 8 == v.payload_size.unwrap() as usize, "Invalid data length"); |
| expect!( |
| ciphertext.len() * 8 == v.payload_size.unwrap() as usize, |
| "Invalid data length" |
| ); |
| |
| let policy = KeyPolicy { |
| usage: KeyUse::ENCRYPT_DECRYPT, |
| keyLifetime: KeyLifetime::PORTABLE, |
| keyPermissions: Vec::new(), |
| keyType: get_key_type(&((current_key.len() * 8) as u16), &v.mode.as_ref().unwrap()) |
| .expect("Invalid key size or mode"), |
| keyManagementKey: false, |
| }; |
| |
| let aes_key_material: ExplicitKeyMaterial = match current_key.len() * 8 { |
| 128 => ExplicitKeyMaterial::Aes(AesKey::Aes128( |
| current_key.clone().try_into().expect("Bad key"), |
| )), |
| 256 => ExplicitKeyMaterial::Aes(AesKey::Aes256( |
| current_key.clone().try_into().expect("Bad key"), |
| )), |
| _ => panic!("Unsupported key length"), |
| }; |
| |
| let key = hw_crypto |
| .importClearKey(&aes_key_material, &policy) |
| .expect("Couldn't import clear key"); |
| |
| let parameters = match v.mode.clone().unwrap() { |
| MODE::CBC => { |
| SymmetricCryptoParameters::Aes(AesCipherMode::Cbc(CipherModeParameters { |
| nonce: current_iv.clone().try_into().expect("Failed to set IV"), |
| })) |
| } |
| MODE::CTR => { |
| SymmetricCryptoParameters::Aes(AesCipherMode::Ctr(CipherModeParameters { |
| nonce: current_iv.clone().try_into().expect("Failed to set IV"), |
| })) |
| } |
| }; |
| |
| let direction = match v.op.as_ref().unwrap() { |
| OPERATION::ENCRYPT => SymmetricOperation::ENCRYPT, |
| OPERATION::DECRYPT => SymmetricOperation::DECRYPT, |
| }; |
| |
| let sym_op_params = |
| SymmetricOperationParameters { key: Some(key.clone()), direction, parameters }; |
| let op_params = OperationParameters::SymmetricCrypto(sym_op_params); |
| |
| let mut cmd_list = Vec::<CryptoOperation>::new(); |
| let data_output = OperationData::DataBuffer(Vec::new()); |
| |
| // Build command list |
| cmd_list.push(CryptoOperation::DataOutput(data_output)); |
| |
| if v.mode.clone().unwrap() != MODE::CTR || new_iv { |
| cmd_list.push(CryptoOperation::SetOperationParameters(op_params)); |
| // For CTR, only do this when IV changes |
| } |
| |
| let input_data = match v.op.as_ref().unwrap() { |
| OPERATION::ENCRYPT => OperationData::DataBuffer(plaintext.clone()), |
| OPERATION::DECRYPT => OperationData::DataBuffer(ciphertext.clone()), |
| }; |
| cmd_list.push(CryptoOperation::DataInput(input_data)); |
| |
| if v.mode.clone().unwrap() != MODE::CTR { |
| cmd_list.push(CryptoOperation::Finish(None)); // For CTR, don't do this |
| } |
| |
| if v.mode.clone().unwrap() != MODE::CTR { |
| // Clear context unless processing CTR vectors |
| context = None; |
| } |
| |
| let crypto_op_set = |
| CryptoOperationSet { context: context.clone(), operations: cmd_list }; |
| let mut crypto_sets = Vec::new(); |
| crypto_sets.push(crypto_op_set); |
| let mut additional_error_info = |
| CryptoOperationErrorAdditionalInfo { failingCommandIndex: 0 }; |
| |
| let mut op_result = hw_crypto_ops |
| .processCommandList(&mut crypto_sets, &mut additional_error_info) |
| .expect("couldn't process commands"); |
| |
| // Capture context to be used with CTR vectors whenever we have a new IV |
| if new_iv { |
| context = op_result.remove(0).context; |
| } |
| |
| // Verify results |
| let CryptoOperation::DataOutput(OperationData::DataBuffer(processed_data)) = |
| crypto_sets.remove(0).operations.remove(0) |
| else { |
| panic!("not reachable, we created this object above on the test"); |
| }; |
| |
| match v.op.as_ref().unwrap() { |
| OPERATION::ENCRYPT => { |
| expect!(processed_data.to_vec() == ciphertext, "Known answer mismatch") |
| } |
| OPERATION::DECRYPT => { |
| expect!(processed_data.to_vec() == plaintext, "Known answer mismatch") |
| } |
| }; |
| } |
| } |
| } |