summaryrefslogtreecommitdiff
path: root/keystore2/test_utils
diff options
context:
space:
mode:
authorRajesh Nyamagoud <nyamagoud@google.com>2023-08-17 22:27:40 +0000
committerRajesh Nyamagoud <nyamagoud@google.com>2023-08-29 17:03:35 +0000
commit10f02e705a379fd6e9e207ffc976f3644a64f2ca (patch)
treed879de2961cfe710578699245163a3403db51632 /keystore2/test_utils
parent07ef2a8b150a220056ca0ab43ea852bf8cd85bae (diff)
downloadsecurity-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.bp122
-rw-r--r--keystore2/test_utils/ffi_test_utils.cpp598
-rw-r--r--keystore2/test_utils/ffi_test_utils.hpp13
-rw-r--r--keystore2/test_utils/ffi_test_utils.rs104
-rw-r--r--keystore2/test_utils/lib.rs1
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;