diff options
author | Rajesh Nyamagoud <nyamagoud@google.com> | 2023-08-17 22:27:40 +0000 |
---|---|---|
committer | Rajesh Nyamagoud <nyamagoud@google.com> | 2023-08-29 17:03:35 +0000 |
commit | 10f02e705a379fd6e9e207ffc976f3644a64f2ca (patch) | |
tree | d879de2961cfe710578699245163a3403db51632 /keystore2/test_utils | |
parent | 07ef2a8b150a220056ca0ab43ea852bf8cd85bae (diff) | |
download | security-10f02e705a379fd6e9e207ffc976f3644a64f2ca.tar.gz |
Re-structured keystore test-utils module.
- Created separate build file.
- Moved ffi-utils from keystore2-client-tests to test-utils.
- Updated calling apis.
Test: atest keystore2_client_tests; atest keystore2_test_utils_test;
atest keystore2_test
Bug: 194359114
Change-Id: Ia2404218b7d13a9ae43b3fc4e481899576d24e63
Diffstat (limited to 'keystore2/test_utils')
-rw-r--r-- | keystore2/test_utils/Android.bp | 122 | ||||
-rw-r--r-- | keystore2/test_utils/ffi_test_utils.cpp | 598 | ||||
-rw-r--r-- | keystore2/test_utils/ffi_test_utils.hpp | 13 | ||||
-rw-r--r-- | keystore2/test_utils/ffi_test_utils.rs | 104 | ||||
-rw-r--r-- | keystore2/test_utils/lib.rs | 1 |
5 files changed, 838 insertions, 0 deletions
diff --git a/keystore2/test_utils/Android.bp b/keystore2/test_utils/Android.bp new file mode 100644 index 00000000..eb04209a --- /dev/null +++ b/keystore2/test_utils/Android.bp @@ -0,0 +1,122 @@ +// Copyright 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. + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "system_security_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["system_security_license"], +} + +rust_defaults { + name: "libkeystore2_test_utils_defaults", + defaults: [ + "keymint_use_latest_hal_aidl_rust", + "keystore2_use_latest_aidl_rust", + ], + rustlibs: [ + "libanyhow", + "libbinder_rs", + "libcxx", + "libkeystore2_selinux", + "liblog_rust", + "libnix", + "librand", + "libserde", + "libserde_cbor", + "libthiserror", + ], + static_libs: [ + "libgtest", + "libkeymint_vts_test_utils", + "libkeystore2_ffi_test_utils", + ], + shared_libs: [ + // libkeymint_vts_test_utils needs to be static for atest, + // and that pulls in keymint shared lib dependency + "android.hardware.security.keymint-V3-ndk", + "libbase", + "libbinder", + "libbinder_ndk", + "libcppbor_external", + "libcppcose_rkp", + "libcrypto", + "libcutils", + "libkeymaster_messages", + "libkeymaster_portable", + "libkeymint_remote_prov_support", + "libkeymint_support", + "libkeystore-engine", + "libutils", + "packagemanager_aidl-cpp", + ], +} + +rust_library { + name: "libkeystore2_test_utils", + crate_name: "keystore2_test_utils", + srcs: ["lib.rs"], + defaults: ["libkeystore2_test_utils_defaults"], +} + +rust_test { + name: "keystore2_test_utils_test", + srcs: ["lib.rs"], + defaults: ["libkeystore2_test_utils_defaults"], + test_suites: ["general-tests"], + require_root: true, + auto_gen_config: true, + compile_multilib: "first", +} + +cc_library_static { + name: "libkeystore2_ffi_test_utils", + srcs: ["ffi_test_utils.cpp"], + defaults: [ + "keymint_vts_defaults", + "hidl_defaults", + ], + generated_headers: [ + "cxx-bridge-header", + "libkeystore2_ffi_test_utils_bridge_header", + ], + generated_sources: ["libkeystore2_ffi_test_utils_bridge_code"], + static_libs: [ + "libkeymint_vts_test_utils", + ], + shared_libs: [ + "libcppbor_external", + "libkeymaster_messages", + "libkeymaster_portable", + "libkeystore-engine", + ], +} + +genrule { + name: "libkeystore2_ffi_test_utils_bridge_code", + tools: ["cxxbridge"], + cmd: "$(location cxxbridge) $(in) >> $(out)", + srcs: ["ffi_test_utils.rs"], + out: ["libkeystore2_test_utils_cxx_generated.cc"], +} + +genrule { + name: "libkeystore2_ffi_test_utils_bridge_header", + tools: ["cxxbridge"], + cmd: "$(location cxxbridge) $(in) --header >> $(out)", + srcs: ["ffi_test_utils.rs"], + out: ["ffi_test_utils.rs.h"], +} diff --git a/keystore2/test_utils/ffi_test_utils.cpp b/keystore2/test_utils/ffi_test_utils.cpp new file mode 100644 index 00000000..7fbfb8b2 --- /dev/null +++ b/keystore2/test_utils/ffi_test_utils.cpp @@ -0,0 +1,598 @@ +#include "ffi_test_utils.hpp" + +#include <iostream> + +#include <android-base/logging.h> + +#include <KeyMintAidlTestBase.h> +#include <aidl/android/hardware/security/keymint/ErrorCode.h> +#include <keymaster/UniquePtr.h> + +#include <vector> + +#include <hardware/keymaster_defs.h> +#include <keymaster/android_keymaster_utils.h> +#include <keymaster/keymaster_tags.h> + +#include <keymaster/km_openssl/attestation_record.h> +#include <keymaster/km_openssl/openssl_err.h> +#include <keymaster/km_openssl/openssl_utils.h> + +#include <android-base/logging.h> + +using aidl::android::hardware::security::keymint::ErrorCode; + +#define TAG_SEQUENCE 0x30 +#define LENGTH_MASK 0x80 +#define LENGTH_VALUE_MASK 0x7F + +/* EVP_PKEY_from_keystore is from system/security/keystore-engine. */ +extern "C" EVP_PKEY* EVP_PKEY_from_keystore(const char* key_id); + +/** + * ASN.1 structure for `KeyDescription` Schema. + * See `IKeyMintDevice.aidl` for documentation of the `KeyDescription` schema. + * KeyDescription ::= SEQUENCE( + * keyFormat INTEGER, # Values from KeyFormat enum. + * keyParams AuthorizationList, + * ) + */ +typedef struct key_description { + ASN1_INTEGER* key_format; + keymaster::KM_AUTH_LIST* key_params; +} TEST_KEY_DESCRIPTION; + +ASN1_SEQUENCE(TEST_KEY_DESCRIPTION) = { + ASN1_SIMPLE(TEST_KEY_DESCRIPTION, key_format, ASN1_INTEGER), + ASN1_SIMPLE(TEST_KEY_DESCRIPTION, key_params, keymaster::KM_AUTH_LIST), +} ASN1_SEQUENCE_END(TEST_KEY_DESCRIPTION); +DECLARE_ASN1_FUNCTIONS(TEST_KEY_DESCRIPTION); + +/** + * ASN.1 structure for `SecureKeyWrapper` Schema. + * See `IKeyMintDevice.aidl` for documentation of the `SecureKeyWrapper` schema. + * SecureKeyWrapper ::= SEQUENCE( + * version INTEGER, # Contains value 0 + * encryptedTransportKey OCTET_STRING, + * initializationVector OCTET_STRING, + * keyDescription KeyDescription, + * encryptedKey OCTET_STRING, + * tag OCTET_STRING + * ) + */ +typedef struct secure_key_wrapper { + ASN1_INTEGER* version; + ASN1_OCTET_STRING* encrypted_transport_key; + ASN1_OCTET_STRING* initialization_vector; + TEST_KEY_DESCRIPTION* key_desc; + ASN1_OCTET_STRING* encrypted_key; + ASN1_OCTET_STRING* tag; +} TEST_SECURE_KEY_WRAPPER; + +ASN1_SEQUENCE(TEST_SECURE_KEY_WRAPPER) = { + ASN1_SIMPLE(TEST_SECURE_KEY_WRAPPER, version, ASN1_INTEGER), + ASN1_SIMPLE(TEST_SECURE_KEY_WRAPPER, encrypted_transport_key, ASN1_OCTET_STRING), + ASN1_SIMPLE(TEST_SECURE_KEY_WRAPPER, initialization_vector, ASN1_OCTET_STRING), + ASN1_SIMPLE(TEST_SECURE_KEY_WRAPPER, key_desc, TEST_KEY_DESCRIPTION), + ASN1_SIMPLE(TEST_SECURE_KEY_WRAPPER, encrypted_key, ASN1_OCTET_STRING), + ASN1_SIMPLE(TEST_SECURE_KEY_WRAPPER, tag, ASN1_OCTET_STRING), +} ASN1_SEQUENCE_END(TEST_SECURE_KEY_WRAPPER); +DECLARE_ASN1_FUNCTIONS(TEST_SECURE_KEY_WRAPPER); + +IMPLEMENT_ASN1_FUNCTIONS(TEST_SECURE_KEY_WRAPPER); +IMPLEMENT_ASN1_FUNCTIONS(TEST_KEY_DESCRIPTION); + +struct TEST_KEY_DESCRIPTION_Delete { + void operator()(TEST_KEY_DESCRIPTION* p) { TEST_KEY_DESCRIPTION_free(p); } +}; +struct TEST_SECURE_KEY_WRAPPER_Delete { + void operator()(TEST_SECURE_KEY_WRAPPER* p) { TEST_SECURE_KEY_WRAPPER_free(p); } +}; + +const std::string keystore2_grant_id_prefix("ks2_keystore-engine_grant_id:"); + +/* This function extracts a certificate from the certs_chain_buffer at the given + * offset. Each DER encoded certificate starts with TAG_SEQUENCE followed by the + * total length of the certificate. The length of the certificate is determined + * as per ASN.1 encoding rules for the length octets. + * + * @param certs_chain_buffer: buffer containing DER encoded X.509 certificates + * arranged sequentially. + * @data_size: Length of the DER encoded X.509 certificates buffer. + * @index: DER encoded X.509 certificates buffer offset. + * @cert: Encoded certificate to be extracted from buffer as outcome. + * @return: ErrorCode::OK on success, otherwise ErrorCode::UNKNOWN_ERROR. + */ +ErrorCode +extractCertFromCertChainBuffer(uint8_t* certs_chain_buffer, int certs_chain_buffer_size, int& index, + aidl::android::hardware::security::keymint::Certificate& cert) { + if (index >= certs_chain_buffer_size) { + return ErrorCode::UNKNOWN_ERROR; + } + + uint32_t length = 0; + std::vector<uint8_t> cert_bytes; + if (certs_chain_buffer[index] == TAG_SEQUENCE) { + // Short form. One octet. Bit 8 has value "0" and bits 7-1 give the length. + if (0 == (certs_chain_buffer[index + 1] & LENGTH_MASK)) { + length = (uint32_t)certs_chain_buffer[index]; + // Add SEQ and Length fields + length += 2; + } else { + // Long form. Two to 127 octets. Bit 8 of first octet has value "1" and + // bits 7-1 give the number of additional length octets. Second and following + // octets give the actual length. + int additionalBytes = certs_chain_buffer[index + 1] & LENGTH_VALUE_MASK; + if (additionalBytes == 0x01) { + length = certs_chain_buffer[index + 2]; + // Add SEQ and Length fields + length += 3; + } else if (additionalBytes == 0x02) { + length = (certs_chain_buffer[index + 2] << 8 | certs_chain_buffer[index + 3]); + // Add SEQ and Length fields + length += 4; + } else if (additionalBytes == 0x04) { + length = certs_chain_buffer[index + 2] << 24; + length |= certs_chain_buffer[index + 3] << 16; + length |= certs_chain_buffer[index + 4] << 8; + length |= certs_chain_buffer[index + 5]; + // Add SEQ and Length fields + length += 6; + } else { + // Length is larger than uint32_t max limit. + return ErrorCode::UNKNOWN_ERROR; + } + } + cert_bytes.insert(cert_bytes.end(), (certs_chain_buffer + index), + (certs_chain_buffer + index + length)); + index += length; + + for (int i = 0; i < cert_bytes.size(); i++) { + cert.encodedCertificate = std::move(cert_bytes); + } + } else { + // SEQUENCE TAG MISSING. + return ErrorCode::UNKNOWN_ERROR; + } + + return ErrorCode::OK; +} + +ErrorCode getCertificateChain( + rust::Vec<rust::u8>& chainBuffer, + std::vector<aidl::android::hardware::security::keymint::Certificate>& certChain) { + uint8_t* data = chainBuffer.data(); + int index = 0; + int data_size = chainBuffer.size(); + + while (index < data_size) { + aidl::android::hardware::security::keymint::Certificate cert = + aidl::android::hardware::security::keymint::Certificate(); + if (extractCertFromCertChainBuffer(data, data_size, index, cert) != ErrorCode::OK) { + return ErrorCode::UNKNOWN_ERROR; + } + certChain.push_back(std::move(cert)); + } + return ErrorCode::OK; +} + +bool validateCertChain(rust::Vec<rust::u8> cert_buf, uint32_t cert_len, bool strict_issuer_check) { + std::vector<aidl::android::hardware::security::keymint::Certificate> cert_chain = + std::vector<aidl::android::hardware::security::keymint::Certificate>(); + if (cert_len <= 0) { + return false; + } + if (getCertificateChain(cert_buf, cert_chain) != ErrorCode::OK) { + return false; + } + + for (int i = 0; i < cert_chain.size(); i++) { + std::cout << cert_chain[i].toString() << "\n"; + } + auto result = aidl::android::hardware::security::keymint::test::ChainSignaturesAreValid( + cert_chain, strict_issuer_check); + + if (result == testing::AssertionSuccess()) return true; + + return false; +} + +/** + * Below mentioned key parameters are used to create authorization list of + * secure key. + * Algorithm: AES-256 + * Padding: PKCS7 + * Blockmode: ECB + * Purpose: Encrypt, Decrypt + */ +keymaster::AuthorizationSet build_wrapped_key_auth_list() { + return keymaster::AuthorizationSet(keymaster::AuthorizationSetBuilder() + .AesEncryptionKey(256) + .Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_ECB) + .Authorization(keymaster::TAG_PADDING, KM_PAD_PKCS7) + .Authorization(keymaster::TAG_NO_AUTH_REQUIRED)); +} + +/** + * Creates ASN.1 DER-encoded data corresponding to `KeyDescription` schema as + * AAD. See `IKeyMintDevice.aidl` for documentation of the `KeyDescription` schema. + */ +CxxResult buildAsn1DerEncodedWrappedKeyDescription() { + CxxResult cxx_result{}; + keymaster_error_t error; + cxx_result.error = KM_ERROR_OK; + + keymaster::UniquePtr<TEST_KEY_DESCRIPTION, TEST_KEY_DESCRIPTION_Delete> key_description( + TEST_KEY_DESCRIPTION_new()); + if (!key_description.get()) { + cxx_result.error = KM_ERROR_MEMORY_ALLOCATION_FAILED; + return cxx_result; + } + + // Fill secure key authorizations. + keymaster::AuthorizationSet auth_list = build_wrapped_key_auth_list(); + error = build_auth_list(auth_list, key_description->key_params); + if (error != KM_ERROR_OK) { + cxx_result.error = error; + return cxx_result; + } + + // Fill secure key format. + if (!ASN1_INTEGER_set(key_description->key_format, KM_KEY_FORMAT_RAW)) { + cxx_result.error = keymaster::TranslateLastOpenSslError(); + return cxx_result; + } + + // Perform ASN.1 DER encoding of KeyDescription. + int asn1_data_len = i2d_TEST_KEY_DESCRIPTION(key_description.get(), nullptr); + if (asn1_data_len < 0) { + cxx_result.error = keymaster::TranslateLastOpenSslError(); + return cxx_result; + } + std::vector<uint8_t> asn1_data(asn1_data_len, 0); + + if (!asn1_data.data()) { + cxx_result.error = KM_ERROR_MEMORY_ALLOCATION_FAILED; + return cxx_result; + } + + uint8_t* p = asn1_data.data(); + asn1_data_len = i2d_TEST_KEY_DESCRIPTION(key_description.get(), &p); + if (asn1_data_len < 0) { + cxx_result.error = keymaster::TranslateLastOpenSslError(); + return cxx_result; + } + + std::move(asn1_data.begin(), asn1_data.end(), std::back_inserter(cxx_result.data)); + + return cxx_result; +} + +/** + * Creates wrapped key material to import in ASN.1 DER-encoded data corresponding to + * `SecureKeyWrapper` schema. See `IKeyMintDevice.aidl` for documentation of the `SecureKeyWrapper` + * schema. + */ +CxxResult createWrappedKey(rust::Vec<rust::u8> encrypted_secure_key, + rust::Vec<rust::u8> encrypted_transport_key, rust::Vec<rust::u8> iv, + rust::Vec<rust::u8> tag) { + CxxResult cxx_result{}; + keymaster_error_t error; + cxx_result.error = KM_ERROR_OK; + + uint8_t* enc_secure_key_data = encrypted_secure_key.data(); + int enc_secure_key_size = encrypted_secure_key.size(); + + uint8_t* iv_data = iv.data(); + int iv_size = iv.size(); + + uint8_t* tag_data = tag.data(); + int tag_size = tag.size(); + + uint8_t* enc_transport_key_data = encrypted_transport_key.data(); + int enc_transport_key_size = encrypted_transport_key.size(); + + keymaster::UniquePtr<TEST_SECURE_KEY_WRAPPER, TEST_SECURE_KEY_WRAPPER_Delete> sec_key_wrapper( + TEST_SECURE_KEY_WRAPPER_new()); + if (!sec_key_wrapper.get()) { + cxx_result.error = KM_ERROR_MEMORY_ALLOCATION_FAILED; + return cxx_result; + } + + // Fill version = 0 + if (!ASN1_INTEGER_set(sec_key_wrapper->version, 0)) { + cxx_result.error = keymaster::TranslateLastOpenSslError(); + return cxx_result; + } + + // Fill encrypted transport key. + if (enc_transport_key_size && + !ASN1_OCTET_STRING_set(sec_key_wrapper->encrypted_transport_key, enc_transport_key_data, + enc_transport_key_size)) { + cxx_result.error = keymaster::TranslateLastOpenSslError(); + return cxx_result; + } + + // Fill encrypted secure key. + if (enc_secure_key_size && !ASN1_OCTET_STRING_set(sec_key_wrapper->encrypted_key, + enc_secure_key_data, enc_secure_key_size)) { + cxx_result.error = keymaster::TranslateLastOpenSslError(); + return cxx_result; + } + + // Fill secure key authorization list. + keymaster::AuthorizationSet auth_list = build_wrapped_key_auth_list(); + error = build_auth_list(auth_list, sec_key_wrapper->key_desc->key_params); + if (error != KM_ERROR_OK) { + cxx_result.error = error; + return cxx_result; + } + + // Fill secure key format. + if (!ASN1_INTEGER_set(sec_key_wrapper->key_desc->key_format, KM_KEY_FORMAT_RAW)) { + cxx_result.error = keymaster::TranslateLastOpenSslError(); + return cxx_result; + } + + // Fill initialization vector used for encrypting secure key. + if (iv_size && + !ASN1_OCTET_STRING_set(sec_key_wrapper->initialization_vector, iv_data, iv_size)) { + cxx_result.error = keymaster::TranslateLastOpenSslError(); + return cxx_result; + } + + // Fill GCM-tag, extracted during secure key encryption. + if (tag_size && !ASN1_OCTET_STRING_set(sec_key_wrapper->tag, tag_data, tag_size)) { + cxx_result.error = keymaster::TranslateLastOpenSslError(); + return cxx_result; + } + + // ASN.1 DER-encoding of secure key wrapper. + int asn1_data_len = i2d_TEST_SECURE_KEY_WRAPPER(sec_key_wrapper.get(), nullptr); + if (asn1_data_len < 0) { + cxx_result.error = keymaster::TranslateLastOpenSslError(); + return cxx_result; + } + std::vector<uint8_t> asn1_data(asn1_data_len, 0); + + if (!asn1_data.data()) { + cxx_result.error = KM_ERROR_MEMORY_ALLOCATION_FAILED; + return cxx_result; + } + + uint8_t* p = asn1_data.data(); + asn1_data_len = i2d_TEST_SECURE_KEY_WRAPPER(sec_key_wrapper.get(), &p); + if (asn1_data_len < 0) { + cxx_result.error = keymaster::TranslateLastOpenSslError(); + return cxx_result; + } + + std::move(asn1_data.begin(), asn1_data.end(), std::back_inserter(cxx_result.data)); + + return cxx_result; +} + +/** + * Perform EC/RSA sign operation using `EVP_PKEY`. + */ +bool performSignData(const char* data, size_t data_len, EVP_PKEY* pkey, unsigned char** signature, + size_t* signature_len) { + // Create the signing context + EVP_MD_CTX* md_ctx = EVP_MD_CTX_new(); + if (md_ctx == NULL) { + LOG(ERROR) << "Failed to create signing context"; + return false; + } + + // Initialize the signing operation + if (EVP_DigestSignInit(md_ctx, NULL, EVP_sha256(), NULL, pkey) != 1) { + LOG(ERROR) << "Failed to initialize signing operation"; + EVP_MD_CTX_free(md_ctx); + return false; + } + + // Sign the data + if (EVP_DigestSignUpdate(md_ctx, data, data_len) != 1) { + LOG(ERROR) << "Failed to sign data"; + EVP_MD_CTX_free(md_ctx); + return false; + } + + // Determine the length of the signature + if (EVP_DigestSignFinal(md_ctx, NULL, signature_len) != 1) { + LOG(ERROR) << "Failed to determine signature length"; + EVP_MD_CTX_free(md_ctx); + return false; + } + + // Allocate memory for the signature + *signature = (unsigned char*)malloc(*signature_len); + if (*signature == NULL) { + LOG(ERROR) << "Failed to allocate memory for the signature"; + EVP_MD_CTX_free(md_ctx); + return false; + } + + // Perform the final signing operation + if (EVP_DigestSignFinal(md_ctx, *signature, signature_len) != 1) { + LOG(ERROR) << "Failed to perform signing operation"; + free(*signature); + EVP_MD_CTX_free(md_ctx); + return false; + } + + EVP_MD_CTX_free(md_ctx); + return true; +} + +/** + * Perform EC/RSA verify operation using `EVP_PKEY`. + */ +int performVerifySignature(const char* data, size_t data_len, EVP_PKEY* pkey, + const unsigned char* signature, size_t signature_len) { + // Create the verification context + EVP_MD_CTX* md_ctx = EVP_MD_CTX_new(); + if (md_ctx == NULL) { + LOG(ERROR) << "Failed to create verification context"; + return false; + } + + // Initialize the verification operation + if (EVP_DigestVerifyInit(md_ctx, NULL, EVP_sha256(), NULL, pkey) != 1) { + LOG(ERROR) << "Failed to initialize verification operation"; + EVP_MD_CTX_free(md_ctx); + return false; + } + + // Verify the data + if (EVP_DigestVerifyUpdate(md_ctx, data, data_len) != 1) { + LOG(ERROR) << "Failed to verify data"; + EVP_MD_CTX_free(md_ctx); + return false; + } + + // Perform the verification operation + int ret = EVP_DigestVerifyFinal(md_ctx, signature, signature_len); + EVP_MD_CTX_free(md_ctx); + + return ret == 1; +} + +/** + * Extract the `EVP_PKEY` for the given KeyMint Key and perform Sign/Verify operations + * using extracted `EVP_PKEY`. + */ +bool performCryptoOpUsingKeystoreEngine(int64_t grant_id) { + const int KEY_ID_LEN = 20; + char key_id[KEY_ID_LEN] = ""; + snprintf(key_id, KEY_ID_LEN, "%" PRIx64, grant_id); + std::string str_key = std::string(keystore2_grant_id_prefix) + key_id; + bool result = false; + +#if defined(OPENSSL_IS_BORINGSSL) + EVP_PKEY* evp = EVP_PKEY_from_keystore(str_key.c_str()); + if (!evp) { + LOG(ERROR) << "Error while loading a key from keystore-engine"; + return false; + } + + int algo_type = EVP_PKEY_id(evp); + if (algo_type != EVP_PKEY_RSA && algo_type != EVP_PKEY_EC) { + LOG(ERROR) << "Unsupported Algorithm. Only RSA and EC are allowed."; + EVP_PKEY_free(evp); + return false; + } + + unsigned char* signature = NULL; + size_t signature_len = 0; + const char* INPUT_DATA = "MY MESSAGE FOR SIGN"; + size_t data_len = strlen(INPUT_DATA); + if (!performSignData(INPUT_DATA, data_len, evp, &signature, &signature_len)) { + LOG(ERROR) << "Failed to sign data"; + EVP_PKEY_free(evp); + return false; + } + + result = performVerifySignature(INPUT_DATA, data_len, evp, signature, signature_len); + if (!result) { + LOG(ERROR) << "Signature verification failed"; + } else { + LOG(INFO) << "Signature verification success"; + } + + free(signature); + EVP_PKEY_free(evp); +#endif + return result; +} + +CxxResult getValueFromAttestRecord(rust::Vec<rust::u8> cert_buf, int32_t tag) { + CxxResult cxx_result{}; + cxx_result.error = KM_ERROR_OK; + + uint8_t* cert_data = cert_buf.data(); + int cert_data_size = cert_buf.size(); + + std::vector<uint8_t> cert_bytes; + cert_bytes.insert(cert_bytes.end(), cert_data, (cert_data + cert_data_size)); + + aidl::android::hardware::security::keymint::X509_Ptr cert( + aidl::android::hardware::security::keymint::test::parse_cert_blob(cert_bytes)); + if (!cert.get()) { + cxx_result.error = KM_ERROR_MEMORY_ALLOCATION_FAILED; + return cxx_result; + } + + ASN1_OCTET_STRING* attest_rec = + aidl::android::hardware::security::keymint::test::get_attestation_record(cert.get()); + if (!attest_rec) { + cxx_result.error = keymaster::TranslateLastOpenSslError(); + return cxx_result; + } + + aidl::android::hardware::security::keymint::AuthorizationSet att_sw_enforced; + aidl::android::hardware::security::keymint::AuthorizationSet att_hw_enforced; + uint32_t att_attestation_version; + uint32_t att_keymint_version; + aidl::android::hardware::security::keymint::SecurityLevel att_attestation_security_level; + aidl::android::hardware::security::keymint::SecurityLevel att_keymint_security_level; + std::vector<uint8_t> att_challenge; + std::vector<uint8_t> att_unique_id; + std::vector<uint8_t> att_app_id; + + auto error = aidl::android::hardware::security::keymint::parse_attestation_record( + attest_rec->data, attest_rec->length, &att_attestation_version, + &att_attestation_security_level, &att_keymint_version, &att_keymint_security_level, + &att_challenge, &att_sw_enforced, &att_hw_enforced, &att_unique_id); + EXPECT_EQ(ErrorCode::OK, error); + if (error != ErrorCode::OK) { + cxx_result.error = static_cast<int32_t>(error); + return cxx_result; + } + + aidl::android::hardware::security::keymint::Tag auth_tag = + static_cast<aidl::android::hardware::security::keymint::Tag>(tag); + + if (auth_tag == aidl::android::hardware::security::keymint::Tag::ATTESTATION_APPLICATION_ID) { + int pos = att_sw_enforced.find( + aidl::android::hardware::security::keymint::Tag::ATTESTATION_APPLICATION_ID); + if (pos == -1) { + cxx_result.error = KM_ERROR_ATTESTATION_APPLICATION_ID_MISSING; + return cxx_result; + } + aidl::android::hardware::security::keymint::KeyParameter param = att_sw_enforced[pos]; + std::vector<uint8_t> val = + param.value.get<aidl::android::hardware::security::keymint::KeyParameterValue::blob>(); + std::move(val.begin(), val.end(), std::back_inserter(cxx_result.data)); + return cxx_result; + } + + if (auth_tag == aidl::android::hardware::security::keymint::Tag::ATTESTATION_CHALLENGE) { + if (att_challenge.size() == 0) { + cxx_result.error = KM_ERROR_ATTESTATION_CHALLENGE_MISSING; + return cxx_result; + } + std::move(att_challenge.begin(), att_challenge.end(), std::back_inserter(cxx_result.data)); + return cxx_result; + } + + if (auth_tag == aidl::android::hardware::security::keymint::Tag::UNIQUE_ID) { + if (att_unique_id.size() == 0) { + cxx_result.error = KM_ERROR_UNSUPPORTED_TAG; + return cxx_result; + } + std::move(att_unique_id.begin(), att_unique_id.end(), std::back_inserter(cxx_result.data)); + return cxx_result; + } + + int pos = att_hw_enforced.find(auth_tag); + if (pos == -1) { + cxx_result.error = KM_ERROR_UNSUPPORTED_TAG; + return cxx_result; + } + aidl::android::hardware::security::keymint::KeyParameter param = att_hw_enforced[pos]; + std::vector<uint8_t> val = + param.value.get<aidl::android::hardware::security::keymint::KeyParameterValue::blob>(); + std::move(val.begin(), val.end(), std::back_inserter(cxx_result.data)); + return cxx_result; +} diff --git a/keystore2/test_utils/ffi_test_utils.hpp b/keystore2/test_utils/ffi_test_utils.hpp new file mode 100644 index 00000000..3ed7edc1 --- /dev/null +++ b/keystore2/test_utils/ffi_test_utils.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include "rust/cxx.h" +#include "ffi_test_utils.rs.h" + +bool validateCertChain(rust::Vec<rust::u8> cert_buf, uint32_t cert_len, bool strict_issuer_check); +CxxResult createWrappedKey(rust::Vec<rust::u8> encrypted_secure_key, + rust::Vec<rust::u8> encrypted_transport_key, + rust::Vec<rust::u8> iv, + rust::Vec<rust::u8> tag); +CxxResult buildAsn1DerEncodedWrappedKeyDescription(); +bool performCryptoOpUsingKeystoreEngine(int64_t grant_id); +CxxResult getValueFromAttestRecord(rust::Vec<rust::u8> cert_buf, int32_t tag); diff --git a/keystore2/test_utils/ffi_test_utils.rs b/keystore2/test_utils/ffi_test_utils.rs new file mode 100644 index 00000000..019c26b1 --- /dev/null +++ b/keystore2/test_utils/ffi_test_utils.rs @@ -0,0 +1,104 @@ +// Copyright 2022, 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. + +//! This module implements helper methods to access the functionalities implemented in CPP. + +use crate::key_generations::Error; +use android_hardware_security_keymint::aidl::android::hardware::security::keymint::Tag::Tag; + +#[cxx::bridge] +mod ffi { + struct CxxResult { + data: Vec<u8>, + error: i32, + } + + unsafe extern "C++" { + include!("ffi_test_utils.hpp"); + fn validateCertChain(cert_buf: Vec<u8>, cert_len: u32, strict_issuer_check: bool) -> bool; + fn createWrappedKey( + encrypted_secure_key: Vec<u8>, + encrypted_transport_key: Vec<u8>, + iv: Vec<u8>, + tag: Vec<u8>, + ) -> CxxResult; + fn buildAsn1DerEncodedWrappedKeyDescription() -> CxxResult; + fn performCryptoOpUsingKeystoreEngine(grant_id: i64) -> bool; + fn getValueFromAttestRecord(cert_buf: Vec<u8>, tag: i32) -> CxxResult; + } +} + +/// Validate given certificate chain. +pub fn validate_certchain(cert_buf: &[u8]) -> Result<bool, Error> { + if ffi::validateCertChain(cert_buf.to_vec(), cert_buf.len().try_into().unwrap(), true) { + return Ok(true); + } + + Err(Error::ValidateCertChainFailed) +} + +/// Collect the result from CxxResult into a Rust supported structure. +fn get_result(result: ffi::CxxResult) -> Result<Vec<u8>, Error> { + if result.error == 0 && !result.data.is_empty() { + Ok(result.data) + } else { + Err(Error::DerEncodeFailed) + } +} + +/// Creates wrapped key material to import in ASN.1 DER-encoded data corresponding to +/// `SecureKeyWrapper`. See `IKeyMintDevice.aidl` for documentation of the `SecureKeyWrapper` +/// schema. +pub fn create_wrapped_key( + encrypted_secure_key: &[u8], + encrypted_transport_key: &[u8], + iv: &[u8], + tag: &[u8], +) -> Result<Vec<u8>, Error> { + get_result(ffi::createWrappedKey( + encrypted_secure_key.to_vec(), + encrypted_transport_key.to_vec(), + iv.to_vec(), + tag.to_vec(), + )) +} + +/// Creates ASN.1 DER-encoded data corresponding to `KeyDescription` schema. +/// See `IKeyMintDevice.aidl` for documentation of the `KeyDescription` schema. +/// Below mentioned key parameters are used - +/// Algorithm: AES-256 +/// Padding: PKCS7 +/// Blockmode: ECB +/// Purpose: Encrypt, Decrypt +pub fn create_wrapped_key_additional_auth_data() -> Result<Vec<u8>, Error> { + get_result(ffi::buildAsn1DerEncodedWrappedKeyDescription()) +} + +/// Performs crypto operation using Keystore-Engine APIs. +pub fn perform_crypto_op_using_keystore_engine(grant_id: i64) -> Result<bool, Error> { + if ffi::performCryptoOpUsingKeystoreEngine(grant_id) { + return Ok(true); + } + + Err(Error::Keystore2EngineOpFailed) +} + +/// Get the value of the given `Tag` from attestation record. +pub fn get_value_from_attest_record(cert_buf: &[u8], tag: Tag) -> Result<Vec<u8>, Error> { + let result = ffi::getValueFromAttestRecord(cert_buf.to_vec(), tag.0); + if result.error == 0 && !result.data.is_empty() { + return Ok(result.data); + } + Err(Error::AttestRecordGetValueFailed) +} diff --git a/keystore2/test_utils/lib.rs b/keystore2/test_utils/lib.rs index c63bfacc..a373a2fa 100644 --- a/keystore2/test_utils/lib.rs +++ b/keystore2/test_utils/lib.rs @@ -22,6 +22,7 @@ use std::{env::temp_dir, ops::Deref}; use android_system_keystore2::aidl::android::system::keystore2::IKeystoreService::IKeystoreService; pub mod authorizations; +pub mod ffi_test_utils; pub mod key_generations; pub mod run_as; |