#include "ffi_test_utils.hpp"

#include <iostream>
#include <vector>

#include <android-base/logging.h>
#include <keymaster/km_openssl/attestation_record.h>
#include <keymaster/km_openssl/openssl_err.h>
#include <keymaster/km_openssl/openssl_utils.h>
#include <keymint_support/attestation_record.h>
#include <keymint_support/keymint_utils.h>
#include <openssl/mem.h>

using keymaster::ASN1_OBJECT_Ptr;
using keymaster::EVP_PKEY_Ptr;
using keymaster::X509_Ptr;
using std::endl;
using std::string;
using std::vector;

#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);

typedef std::vector<uint8_t> certificate_t;

/**
 * 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:");

string bin2hex(const vector<uint8_t>& data) {
    string retval;
    char nibble2hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
                           '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
    retval.reserve(data.size() * 2 + 1);
    for (uint8_t byte : data) {
        retval.push_back(nibble2hex[0x0F & (byte >> 4)]);
        retval.push_back(nibble2hex[0x0F & byte]);
    }
    return retval;
}

string x509NameToStr(X509_NAME* name) {
    char* s = X509_NAME_oneline(name, nullptr, 0);
    string retval(s);
    OPENSSL_free(s);
    return retval;
}

X509_Ptr parseCertBlob(const vector<uint8_t>& blob) {
    const uint8_t* p = blob.data();
    return X509_Ptr(d2i_X509(nullptr /* allocate new */, &p, blob.size()));
}

// Extract attestation record from cert. Returned object is still part of cert; don't free it
// separately.
ASN1_OCTET_STRING* getAttestationRecord(X509* certificate) {
    ASN1_OBJECT_Ptr oid(OBJ_txt2obj(aidl::android::hardware::security::keymint::kAttestionRecordOid,
                                    1 /* dotted string format */));
    if (!oid.get()) return nullptr;

    int location = X509_get_ext_by_OBJ(certificate, oid.get(), -1 /* search from beginning */);
    if (location == -1) return nullptr;

    X509_EXTENSION* attest_rec_ext = X509_get_ext(certificate, location);
    if (!attest_rec_ext) return nullptr;

    ASN1_OCTET_STRING* attest_rec = X509_EXTENSION_get_data(attest_rec_ext);
    return attest_rec;
}

bool ChainSignaturesAreValid(const vector<certificate_t>& chain, bool strict_issuer_check) {
    std::stringstream cert_data;

    for (size_t i = 0; i < chain.size(); ++i) {
        cert_data << bin2hex(chain[i]) << std::endl;

        X509_Ptr key_cert(parseCertBlob(chain[i]));
        X509_Ptr signing_cert;
        if (i < chain.size() - 1) {
            signing_cert = parseCertBlob(chain[i + 1]);
        } else {
            signing_cert = parseCertBlob(chain[i]);
        }
        if (!key_cert.get() || !signing_cert.get()) {
            LOG(ERROR) << cert_data.str();
            return false;
        }

        EVP_PKEY_Ptr signing_pubkey(X509_get_pubkey(signing_cert.get()));
        if (!signing_pubkey.get()) {
            LOG(ERROR) << cert_data.str();
            return false;
        }

        if (!X509_verify(key_cert.get(), signing_pubkey.get())) {
            // Handles the case of device-unique attestation chain which is not expected to be
            // self-signed - b/191361618
            // For device-unique attestation chain `strict_issuer_check` is not set, so ignore the
            // root certificate signature verification result and in all other cases return the
            // error.
            bool is_root_cert = (i == chain.size() - 1);
            if (strict_issuer_check || !is_root_cert) {
                LOG(ERROR) << "Verification of certificate " << i << " failed "
                           << "OpenSSL error string: " << ERR_error_string(ERR_get_error(), NULL)
                           << '\n'
                           << cert_data.str();
                return false;
            }
        }

        string cert_issuer = x509NameToStr(X509_get_issuer_name(key_cert.get()));
        string signer_subj = x509NameToStr(X509_get_subject_name(signing_cert.get()));
        if (cert_issuer != signer_subj && strict_issuer_check) {
            LOG(ERROR) << "Cert " << i << " has wrong issuer.\n"
                       << " Signer subject is " << signer_subj << " Issuer subject is "
                       << cert_issuer << endl
                       << cert_data.str();
        }
    }

    // Dump cert data.
    LOG(ERROR) << cert_data.str();
    return true;
}

/* 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: true on success, otherwise false.
 */
bool extractCertFromCertChainBuffer(uint8_t* certs_chain_buffer, int certs_chain_buffer_size,
                                    int& index, certificate_t& cert) {
    if (index >= certs_chain_buffer_size) {
        return false;
    }

    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 false;
            }
        }
        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 = std::move(cert_bytes);
        }
    } else {
        // SEQUENCE TAG MISSING.
        return false;
    }

    return true;
}

bool getCertificateChain(rust::Vec<rust::u8>& chainBuffer, std::vector<certificate_t>& certChain) {
    uint8_t* data = chainBuffer.data();
    int index = 0;
    int data_size = chainBuffer.size();

    while (index < data_size) {
        certificate_t cert;
        if (!extractCertFromCertChainBuffer(data, data_size, index, cert)) {
            return false;
        }
        certChain.push_back(std::move(cert));
    }
    return true;
}

bool validateCertChain(rust::Vec<rust::u8> cert_buf, uint32_t cert_len, bool strict_issuer_check) {
    std::vector<certificate_t> cert_chain = std::vector<certificate_t>();
    if (cert_len <= 0) {
        return false;
    }
    if (!getCertificateChain(cert_buf, cert_chain)) {
        return false;
    }

    std::stringstream cert_data;
    for (int i = 0; i < cert_chain.size(); i++) {
        cert_data << bin2hex(cert_chain[i]) << std::endl;
    }
    LOG(INFO) << cert_data.str() << "\n";

    return ChainSignaturesAreValid(cert_chain, strict_issuer_check);
}

/**
 * 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 = false;

    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()) {
        LOG(ERROR) << "createWrappedKey - Failed to allocate a memory";
        cxx_result.error = true;
        return cxx_result;
    }

    // Fill version = 0
    if (!ASN1_INTEGER_set(sec_key_wrapper->version, 0)) {
        LOG(ERROR) << "createWrappedKey - Error while filling version: "
                   << keymaster::TranslateLastOpenSslError();
        cxx_result.error = true;
        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)) {
        LOG(ERROR) << "createWrappedKey - Error while filling encrypted transport key: "
                   << keymaster::TranslateLastOpenSslError();
        cxx_result.error = true;
        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)) {
        LOG(ERROR) << "createWrappedKey - Error while filling encrypted secure key: "
                   << keymaster::TranslateLastOpenSslError();
        cxx_result.error = true;
        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 = true;
        return cxx_result;
    }

    // Fill secure key format.
    if (!ASN1_INTEGER_set(sec_key_wrapper->key_desc->key_format, KM_KEY_FORMAT_RAW)) {
        LOG(ERROR) << "createWrappedKey - Error while filling secure key format: "
                   << keymaster::TranslateLastOpenSslError();
        cxx_result.error = true;
        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)) {
        LOG(ERROR) << "createWrappedKey - Error while filling IV: "
                   << keymaster::TranslateLastOpenSslError();
        cxx_result.error = true;
        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)) {
        LOG(ERROR) << "createWrappedKey - Error while filling GCM-tag: "
                   << keymaster::TranslateLastOpenSslError();
        cxx_result.error = true;
        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) {
        LOG(ERROR) << "createWrappedKey - Error while performing DER encode: "
                   << keymaster::TranslateLastOpenSslError();
        cxx_result.error = true;
        return cxx_result;
    }
    std::vector<uint8_t> asn1_data(asn1_data_len, 0);

    if (!asn1_data.data()) {
        LOG(ERROR) << "createWrappedKey - Failed to allocate a memory for asn1_data";
        cxx_result.error = true;
        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 = true;
        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,
                                   int32_t expected_sec_level) {
    CxxResult cxx_result{};
    cxx_result.error = false;

    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));

    X509_Ptr cert(parseCertBlob(cert_bytes));
    if (!cert.get()) {
        LOG(ERROR) << "getValueFromAttestRecord - Failed to allocate a memory for certificate";
        cxx_result.error = true;
        return cxx_result;
    }

    ASN1_OCTET_STRING* attest_rec = getAttestationRecord(cert.get());
    if (!attest_rec) {
        LOG(ERROR) << "getValueFromAttestRecord - Error in getAttestationRecord: "
                   << keymaster::TranslateLastOpenSslError();
        cxx_result.error = true;
        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;

    int32_t error =
        static_cast<int32_t>(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));
    if (error) {
        LOG(ERROR) << "getValueFromAttestRecord - Error in parse_attestation_record: " << error;
        cxx_result.error = true;
        return cxx_result;
    }

    aidl::android::hardware::security::keymint::Tag auth_tag =
        static_cast<aidl::android::hardware::security::keymint::Tag>(tag);
    aidl::android::hardware::security::keymint::SecurityLevel tag_security_level =
        static_cast<aidl::android::hardware::security::keymint::SecurityLevel>(expected_sec_level);

    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) {
            LOG(ERROR) << "getValueFromAttestRecord - Attestation-application-id missing.";
            cxx_result.error = true;
            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) {
            LOG(ERROR) << "getValueFromAttestRecord - Attestation-challenge missing.";
            cxx_result.error = true;
            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) {
            LOG(ERROR) << "getValueFromAttestRecord - unsupported tag - UNIQUE_ID.";
            cxx_result.error = true;
            return cxx_result;
        }
        std::move(att_unique_id.begin(), att_unique_id.end(), std::back_inserter(cxx_result.data));
        return cxx_result;
    }

    if (auth_tag == aidl::android::hardware::security::keymint::Tag::USAGE_COUNT_LIMIT) {
        aidl::android::hardware::security::keymint::KeyParameter param;
        int pos = att_hw_enforced.find(auth_tag);
        if (tag_security_level ==
                aidl::android::hardware::security::keymint::SecurityLevel::SOFTWARE ||
            tag_security_level ==
                aidl::android::hardware::security::keymint::SecurityLevel::KEYSTORE) {
            pos = att_sw_enforced.find(auth_tag);
            if (pos == -1) {
                LOG(ERROR) << "USAGE_COUNT_LIMIT not found in software enforced auth list";
                cxx_result.error = KM_ERROR_INVALID_TAG;
                return cxx_result;
            }
            param = att_sw_enforced[pos];
        } else {
            pos = att_hw_enforced.find(auth_tag);
            if (pos == -1) {
                LOG(ERROR) << "USAGE_COUNT_LIMIT not found in hardware enforced auth list";
                cxx_result.error = KM_ERROR_INVALID_TAG;
                return cxx_result;
            }
            param = att_hw_enforced[pos];
        }
        std::string val = std::to_string(
            param.value
                .get<aidl::android::hardware::security::keymint::KeyParameterValue::integer>());
        std::move(val.begin(), val.end(), std::back_inserter(cxx_result.data));
        return cxx_result;
    }

    int pos = att_hw_enforced.find(auth_tag);
    if (pos == -1) {
        LOG(ERROR) << "getValueFromAttestRecord - unsupported tag.";
        cxx_result.error = true;
        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;
}

uint32_t getOsVersion() {
    return aidl::android::hardware::security::keymint::getOsVersion();
}

uint32_t getOsPatchlevel() {
    return aidl::android::hardware::security::keymint::getOsPatchlevel();
}

uint32_t getVendorPatchlevel() {
    return aidl::android::hardware::security::keymint::getVendorPatchlevel();
}