/* * 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, mode: Option, key_length: Option, iv_size: Option, payload_size: Option, params: HashMap, } #[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> { 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 { let mut vectors: Vec = Vec::new(); let mut params: HashMap = HashMap::new(); let mut mode: Option = None; let mut op: Option = None; let mut key_length: Option = None; let mut iv_size: Option = None; let mut payload_size: Option; 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::().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 { 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) { let port = CString::try_new(RUST_HWCRYPTO_SERVICE_PORT).expect("Failed to allocate port name"); let hw_crypto: Strong = 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 = Vec::new(); let mut current_iv: Vec = Vec::new(); let mut new_iv: bool; let mut context: Option> = 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 = hex_to_bytes(v.params["PLAINTEXT"].as_str()).expect("Bad hex value"); let ciphertext: Vec = 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::::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") } }; } } }