/*
 * Copyright (C) 2016 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.
 */

#define TLOG_TAG "hwkey_gsc_srv"

#include <assert.h>
#include <lk/compiler.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <uapi/err.h>

#include <openssl/aes.h>
#include <openssl/cipher.h>
#include <openssl/digest.h>
#include <openssl/err.h>
#include <openssl/hkdf.h>

#include <interface/hwaes/hwaes.h>
#include <interface/hwkey/hwkey.h>
#include <lib/system_state/system_state.h>
#include <lib/tipc/tipc.h>
#include <lib/tipc/tipc_srv.h>
#include <trusty_log.h>

#include <hwkey_consts.h>
#include "hwkey_srv_priv.h"

/* 0 means unlimited number of connections */
#define HWKEY_MAX_NUM_CHANNELS 0

static uint8_t encrypt_key[32];
static uint8_t attest_key[32];
static int32_t rollback_ver;
static int32_t running_ver;

/*
 * Derive key V1 - HMAC SHA256 based Key derivation function
 */
uint32_t derive_key_v1(const uuid_t* uuid,
                       const uint8_t* ikm_data,
                       size_t ikm_len,
                       uint8_t* key_buf,
                       size_t key_len) {
    if (!ikm_len) {
        return HWKEY_ERR_BAD_LEN;
    }

    if (!HKDF(key_buf, key_len, EVP_sha256(), (const uint8_t*)encrypt_key,
              sizeof(encrypt_key), (const uint8_t*)uuid, sizeof(uuid_t),
              ikm_data, ikm_len)) {
        TLOGE("HDKF failed 0x%x\n", ERR_get_error());
        memset(key_buf, 0, key_len);
        return HWKEY_ERR_GENERIC;
    }

    return HWKEY_NO_ERROR;
}

/*
 * Context labels for key derivation contexts, see derive_key_versioned_v1() for
 * details.
 */
#define HWKEY_DERIVE_VERSIONED_CONTEXT_LABEL "DERIVE VERSIONED"
#define ROOT_OF_TRUST_DERIVE_CONTEXT_LABEL "TZOS"

#define HWKEY_DERIVE_VERSIONED_SALT "hwkey derive versioned salt"

static uint8_t context_buf[4096];

/**
 * fill_context_buf() - Add data to context_buf
 * @src: Pointer to data to copy into the context buf. If null, @len zero bytes
 *       will be added.
 * @len: Number of bytes of data to add.
 * @cur_position: Pointer to the next unwritten byte of context_buf. Updated
 *                with the new current position when successful.
 *
 * Return: HWKEY_NO_ERROR on success, HWKEY_ERR_BAD_LEN if @len will cause the
 * buffer to overflow.
 */
static uint32_t fill_context_buf(const void* src,
                                 size_t len,
                                 size_t* cur_position) {
    size_t new_position;
    if (len == 0) {
        return HWKEY_NO_ERROR;
    }
    if (__builtin_add_overflow(*cur_position, len, &new_position) ||
        new_position >= sizeof(context_buf)) {
        return HWKEY_ERR_BAD_LEN;
    }
    if (src == NULL) {
        memset(&context_buf[*cur_position], 0, len);
    } else {
        memcpy(&context_buf[*cur_position], src, len);
    }
    *cur_position = new_position;
    return HWKEY_NO_ERROR;
}

/*
 * In a real implementation this portion of the derivation should be done by a
 * trusted source of the Trusty OS rollback version. Doing the key derivation
 * here in the hwkey service protects against some app-level compromises, but
 * does not protect against compromise of any Trusty code that can derive
 * directly using the secret key derivation input - which in this sample
 * implementation would be the kernel and the hwkey service.
 *
 * This function MUST mix @rollback_version_source, @os_rollback_version, and
 * @hwkey_context into the derivation context in a way that the client cannot
 * forge.
 */
static uint32_t root_of_trust_derive_key(bool shared,
                                         uint32_t rollback_version_source,
                                         int32_t os_rollback_version,
                                         const uint8_t* hwkey_context,
                                         size_t hwkey_context_len,
                                         uint8_t* key_buf,
                                         size_t key_len) {
    size_t context_len = 0;
    int rc;
    const size_t root_of_trust_context_len =
            sizeof(ROOT_OF_TRUST_DERIVE_CONTEXT_LABEL) +
            sizeof(rollback_version_source) + sizeof(os_rollback_version);
    const uint8_t* secret_key;
    size_t secret_key_len;
    size_t total_len;

    /*
     * We need to move the hwkey_context (currently at the beginning of
     * context_buf) over to make room for the root-of-trust context injected
     * before it. We avoid the need for a separate buffer by memmoving it first
     * then adding the context into the space we made.
     */
    if (__builtin_add_overflow(hwkey_context_len, root_of_trust_context_len,
                               &total_len) ||
        total_len >= sizeof(context_buf)) {
        return HWKEY_ERR_BAD_LEN;
    }
    memmove(&context_buf[root_of_trust_context_len], hwkey_context,
            hwkey_context_len);

    /*
     * Add a fixed label to ensure that another user of the same key derivation
     * primitive will not collide with this use, regardless of the provided
     * hwkey_context (as long as other users also add a different fixed label).
     */
    rc = fill_context_buf(ROOT_OF_TRUST_DERIVE_CONTEXT_LABEL,
                          sizeof(ROOT_OF_TRUST_DERIVE_CONTEXT_LABEL),
                          &context_len);
    if (rc) {
        return rc;
    }
    /* Keys for different version limit sources must be different */
    rc = fill_context_buf(&rollback_version_source,
                          sizeof(rollback_version_source), &context_len);
    if (rc) {
        return rc;
    }
    /*
     * Keys with different rollback versions must not be the same. This is part
     * of the root-of-trust context to ensure that a compromised kernel cannot
     * forge a version (if the root of trust is outside of Trusty)
     */
    rc = fill_context_buf(&os_rollback_version, sizeof(os_rollback_version),
                          &context_len);
    if (rc) {
        return rc;
    }

    assert(root_of_trust_context_len == context_len);

    /*
     * We already moved the hwkey_context into place after the root of trust
     * context.
     */
    context_len += hwkey_context_len;

    if (shared) {
        secret_key = attest_key;
        secret_key_len = sizeof(attest_key);
    } else {
        secret_key = encrypt_key;
        secret_key_len = sizeof(encrypt_key);
    }

    if (!HKDF(key_buf, key_len, EVP_sha256(), secret_key, secret_key_len,
              (const uint8_t*)HWKEY_DERIVE_VERSIONED_SALT,
              sizeof(HWKEY_DERIVE_VERSIONED_SALT), context_buf, context_len)) {
        TLOGE("HDKF failed 0x%x\n", ERR_get_error());
        memset(key_buf, 0, key_len);
        return HWKEY_ERR_GENERIC;
    }
    return HWKEY_NO_ERROR;
}

int32_t get_current_os_rollback_version(uint32_t source) {
    switch (source) {
    case HWKEY_ROLLBACK_RUNNING_VERSION:
        return running_ver;

    case HWKEY_ROLLBACK_COMMITTED_VERSION:
        return rollback_ver;

    default:
        TLOGE("Unknown rollback version source: %u\n", source);
        return ERR_NOT_VALID;
    }
}

/*
 * Derive a versioned key - HMAC SHA256 based versioned key derivation function
 */
uint32_t derive_key_versioned_v1(
        const uuid_t* uuid,
        bool shared,
        uint32_t rollback_version_source,
        int32_t rollback_versions[HWKEY_ROLLBACK_VERSION_INDEX_COUNT],
        const uint8_t* user_context,
        size_t user_context_len,
        uint8_t* key_buf,
        size_t key_len) {
    size_t context_len = 0;
    int i;
    uint32_t rc = HWKEY_NO_ERROR;
    int32_t os_rollback_version =
            rollback_versions[HWKEY_ROLLBACK_VERSION_OS_INDEX];
    int32_t os_rollback_version_current =
            get_current_os_rollback_version(rollback_version_source);

    if (os_rollback_version_current < 0) {
        rc = HWKEY_ERR_NOT_VALID;
        goto err;
    }

    if (os_rollback_version > os_rollback_version_current) {
        TLOGE("Requested rollback version too new: %u\n", os_rollback_version);
        rc = HWKEY_ERR_NOT_FOUND;
        goto err;
    }

    /* short-circuit derivation if we have nothing to derive */
    if (key_len == 0) {
        rc = HWKEY_NO_ERROR;
        goto err;
    }

    /* for compatibility with unversioned derive, require a context */
    if (!key_buf || !user_context || user_context_len == 0) {
        rc = HWKEY_ERR_NOT_VALID;
        goto err;
    }

    /*
     * This portion of the context may always be added by the hwkey service, as
     * it deals with the identity of the client requesting the key derivation.
     */
    /*
     * Fixed label ensures that this derivation will not collide with a
     * different user of root_of_trust_derive_key(), regardless of the provided
     * user context (as long as other users also add a different fixed label).
     */
    rc = fill_context_buf(HWKEY_DERIVE_VERSIONED_CONTEXT_LABEL,
                          sizeof(HWKEY_DERIVE_VERSIONED_CONTEXT_LABEL),
                          &context_len);
    if (rc) {
        goto err;
    }
    /* Keys for different apps must be different */
    rc = fill_context_buf(uuid, sizeof(*uuid), &context_len);
    if (rc) {
        goto err;
    }
    for (i = 0; i < HWKEY_ROLLBACK_VERSION_SUPPORTED_COUNT; ++i) {
        /*
         * We skip the OS version because the root-of-trust should be inserting
         * that, and we don't want to mask a buggy implementation there in
         * testing. If the root-of-trust somehow did not insert the OS version,
         * we want to notice.
         */
        if (i == HWKEY_ROLLBACK_VERSION_OS_INDEX) {
            continue;
        }
        rc = fill_context_buf(&rollback_versions[i], sizeof(*rollback_versions),
                              &context_len);
        if (rc) {
            goto err;
        }
    }
    /* Reserve space for additional versions in the future */
    if (HWKEY_ROLLBACK_VERSION_SUPPORTED_COUNT <
        HWKEY_ROLLBACK_VERSION_INDEX_COUNT) {
        rc = fill_context_buf(NULL,
                              sizeof(*rollback_versions) *
                                      (HWKEY_ROLLBACK_VERSION_INDEX_COUNT -
                                       HWKEY_ROLLBACK_VERSION_SUPPORTED_COUNT),
                              &context_len);
        if (rc) {
            goto err;
        }
    }
    /*
     * Clients need to be able to generate multiple different keys in the same
     * app.
     */
    rc = fill_context_buf(user_context, user_context_len, &context_len);
    if (rc) {
        goto err;
    }

    rc = root_of_trust_derive_key(shared, rollback_version_source,
                                  os_rollback_version, context_buf, context_len,
                                  key_buf, key_len);
    if (rc) {
        goto err;
    }

err:
    memset(context_buf, 0, sizeof(context_buf));
    return rc;
}

/* UUID of HWCRYPTO_UNITTEST application */
static const uuid_t hwcrypto_unittest_uuid = HWCRYPTO_UNITTEST_APP_UUID;
static const uuid_t hwcrypto_unittest_rust_uuid =
        HWCRYPTO_UNITTEST_RUST_APP_UUID;

#if WITH_HWCRYPTO_UNITTEST
/*
 *  Support for hwcrypto unittest keys should be only enabled
 *  to test hwcrypto related APIs
 */

static uint8_t _unittest_key32[32] = "unittestkeyslotunittestkeyslotun";
static uint32_t get_unittest_key32(uint8_t* kbuf,
                                   size_t kbuf_len,
                                   size_t* klen) {
    assert(kbuf);
    assert(klen);
    assert(kbuf_len >= sizeof(_unittest_key32));

    /* just return predefined key */
    memcpy(kbuf, _unittest_key32, sizeof(_unittest_key32));
    *klen = sizeof(_unittest_key32);

    return HWKEY_NO_ERROR;
}

static uint32_t get_unittest_key32_handler(const struct hwkey_keyslot* slot,
                                           uint8_t* kbuf,
                                           size_t kbuf_len,
                                           size_t* klen) {
    return get_unittest_key32(kbuf, kbuf_len, klen);
}

/*
 * "unittestderivedkeyslotunittestde" encrypted with _unittest_key32 using an
 * all 0 IV. IV is prepended to the ciphertext.
 */
static uint8_t _unittest_encrypted_key32[48] = {
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x3e, 0x2b, 0x02, 0x54, 0x54, 0x8c, 0xa7, 0xb8,
        0xa3, 0xfa, 0xf5, 0xd0, 0xbc, 0x1d, 0x40, 0x11, 0xac, 0x68, 0xbb, 0xf0,
        0x55, 0xa3, 0xc5, 0x49, 0x3e, 0x77, 0x4a, 0x8b, 0x3f, 0x33, 0x56, 0x07,
};

static unsigned int _unittest_encrypted_key32_size =
        sizeof(_unittest_encrypted_key32);

static uint32_t get_unittest_key32_derived(
        const struct hwkey_derived_keyslot_data* data,
        uint8_t* kbuf,
        size_t kbuf_len,
        size_t* klen) {
    return get_unittest_key32(kbuf, kbuf_len, klen);
}

static const struct hwkey_derived_keyslot_data hwcrypto_unittest_derived_data =
        {
                .encrypted_key_data = _unittest_encrypted_key32,
                .encrypted_key_size_ptr = &_unittest_encrypted_key32_size,
                .retriever = get_unittest_key32_derived,
};

static uint32_t derived_keyslot_handler(const struct hwkey_keyslot* slot,
                                        uint8_t* kbuf,
                                        size_t kbuf_len,
                                        size_t* klen) {
    assert(slot);
    return hwkey_get_derived_key(slot->priv, kbuf, kbuf_len, klen);
}

static const uuid_t* unittest_allowed_opaque_key_uuids[] = {
        &hwcrypto_unittest_uuid,
        &hwcrypto_unittest_rust_uuid,
};

static uint32_t get_unittest_key32_opaque(
        const struct hwkey_opaque_handle_data* data,
        uint8_t* kbuf,
        size_t kbuf_len,
        size_t* klen) {
    return get_unittest_key32(kbuf, kbuf_len, klen);
}

static struct hwkey_opaque_handle_data unittest_opaque_handle_data = {
        .allowed_uuids = unittest_allowed_opaque_key_uuids,
        .allowed_uuids_len = countof(unittest_allowed_opaque_key_uuids),
        .retriever = get_unittest_key32_opaque,
};

static struct hwkey_opaque_handle_data unittest_opaque_handle_data2 = {
        .allowed_uuids = unittest_allowed_opaque_key_uuids,
        .allowed_uuids_len = countof(unittest_allowed_opaque_key_uuids),
        .retriever = get_unittest_key32_opaque,
};

static struct hwkey_opaque_handle_data unittest_opaque_handle_data_noaccess = {
        .allowed_uuids = NULL,
        .allowed_uuids_len = 0,
        .retriever = get_unittest_key32_opaque,
};

/*
 * Adapter to cast hwkey_opaque_handle_data.priv field to struct
 * hwkey_derived_keyslot_data*
 */
static uint32_t get_derived_key_opaque(
        const struct hwkey_opaque_handle_data* data,
        uint8_t* kbuf,
        size_t kbuf_len,
        size_t* klen) {
    assert(data);
    return hwkey_get_derived_key(data->priv, kbuf, kbuf_len, klen);
}

static struct hwkey_opaque_handle_data unittest_opaque_derived_data = {
        .allowed_uuids = unittest_allowed_opaque_key_uuids,
        .allowed_uuids_len = countof(unittest_allowed_opaque_key_uuids),
        .retriever = get_derived_key_opaque,
        .priv = &hwcrypto_unittest_derived_data,
};

#endif /* WITH_HWCRYPTO_UNITTEST */

/*
 *  RPMB Key support
 */
#define RPMB_SS_AUTH_KEY_SIZE 32
#define RPMB_SS_AUTH_KEY_ID "com.android.trusty.storage_auth.rpmb"

/* Secure storage service app uuid */
static const uuid_t ss_uuid = SECURE_STORAGE_SERVER_APP_UUID;

static uint8_t rpmb_salt[RPMB_SS_AUTH_KEY_SIZE] = {
        0x42, 0x18, 0xa9, 0xf2, 0xf6, 0xb1, 0xf5, 0x35, 0x06, 0x37, 0x9f,
        0xba, 0xcc, 0x1a, 0xc9, 0x36, 0xf4, 0x83, 0x04, 0xd4, 0xf1, 0x65,
        0x91, 0x32, 0xa6, 0xae, 0xda, 0x27, 0x4d, 0x21, 0xdb, 0x40};

/*
 * Generate RPMB Secure Storage Authentication key
 */
static uint32_t get_rpmb_ss_auth_key(const struct hwkey_keyslot* slot,
                                     uint8_t* kbuf,
                                     size_t kbuf_len,
                                     size_t* klen) {
    int rc;
    int out_len;
    EVP_CIPHER_CTX evp;

    assert(kbuf);
    assert(klen);

    EVP_CIPHER_CTX_init(&evp);

    rc = EVP_EncryptInit_ex(&evp, EVP_aes_256_cbc(), NULL, encrypt_key,
                            NULL);
    if (!rc)
        goto evp_err;

    rc = EVP_CIPHER_CTX_set_padding(&evp, 0);
    if (!rc)
        goto evp_err;

    size_t min_kbuf_len =
            RPMB_SS_AUTH_KEY_SIZE + EVP_CIPHER_CTX_key_length(&evp);
    if (kbuf_len < min_kbuf_len) {
        TLOGE("buffer too small: (%zd vs. %zd )\n", kbuf_len, min_kbuf_len);
        goto other_err;
    }

    rc = EVP_EncryptUpdate(&evp, kbuf, &out_len, rpmb_salt, sizeof(rpmb_salt));
    if (!rc)
        goto evp_err;

    if ((size_t)out_len != RPMB_SS_AUTH_KEY_SIZE) {
        TLOGE("output length mismatch (%zd vs %zd)\n", (size_t)out_len,
              sizeof(rpmb_salt));
        goto other_err;
    }

    rc = EVP_EncryptFinal_ex(&evp, NULL, &out_len);
    if (!rc)
        goto evp_err;

    *klen = RPMB_SS_AUTH_KEY_SIZE;

    EVP_CIPHER_CTX_cleanup(&evp);
    return HWKEY_NO_ERROR;

evp_err:
    TLOGE("EVP err 0x%x\n", ERR_get_error());
other_err:
    EVP_CIPHER_CTX_cleanup(&evp);
    return HWKEY_ERR_GENERIC;
}

/*
 * Keymint KAK support
 */
#define KM_KAK_SIZE 32
/* TODO import this constant from KM TA when build support is ready */
#define KM_KAK_ID "com.android.trusty.keymint.kak"

/* KM app uuid */
static const uuid_t km_uuid = KM_APP_UUID;

/* KM rust app uuid */
static const uuid_t km_rust_uuid = KM_RUST_APP_UUID;

#if TEST_BUILD
/* KM rust unit test uuid */
static const uuid_t km_rust_unittest_uuid = KM_RUST_UNITTEST_UUID;

/* HWCrypto HAL rust unit test uuid */
static const uuid_t hwcryptohal_rust_unittest_uuid =
        HWCRYPTOHAL_UNITTEST_RUST_APP_UUID;
#endif

/* HWCrypto HAL rust unit test uuid */
static const uuid_t hwcryptohal_rust_uuid = HWCRYPTOHAL_RUST_APP_UUID;

static uint8_t kak_salt[KM_KAK_SIZE];

/*
 * This should be replaced with a device-specific implementation such that
 * any Strongbox on the device will have the same KAK.
 */
static uint32_t get_km_kak_key(const struct hwkey_keyslot* slot,
                               uint8_t* kbuf,
                               size_t kbuf_len,
                               size_t* klen) {
    assert(kbuf);
    assert(klen);

    if (kbuf_len < KM_KAK_SIZE) {
        return HWKEY_ERR_BAD_LEN;
    }
    *klen = KM_KAK_SIZE;

    return derive_key_v1(slot->uuid, kak_salt, KM_KAK_SIZE, kbuf, *klen);
}

static const uuid_t hwaes_uuid = SAMPLE_HWAES_APP_UUID;

#if WITH_HWCRYPTO_UNITTEST
static const uuid_t hwaes_unittest_uuid = HWAES_UNITTEST_APP_UUID;
static const uuid_t hwaes_bench_uuid = HWAES_BENCH_APP_UUID;

static const uuid_t* hwaes_unittest_allowed_opaque_key_uuids[] = {
        &hwaes_uuid,
};

static struct hwkey_opaque_handle_data hwaes_unittest_opaque_handle_data = {
        .allowed_uuids = hwaes_unittest_allowed_opaque_key_uuids,
        .allowed_uuids_len = countof(hwaes_unittest_allowed_opaque_key_uuids),
        .retriever = get_unittest_key32_opaque,
};
#endif


static const uuid_t gatekeeper_uuid = GATEKEEPER_APP_UUID;
static const uuid_t hwbcc_uuid = SAMPLE_HWBCC_APP_UUID;
static const uuid_t hwbcc_unittest_uuid = HWBCC_UNITTEST_APP_UUID;

/* Clients that are allowed to connect to this service */
static const uuid_t* allowed_clients[] = {
        /* Needs to derive keys and access keyslot RPMB_SS_AUTH_KEY_ID */
        &ss_uuid,
        /* Needs to derive keys and access keyslot KM_KAK_ID */
        &km_uuid,
        &km_rust_uuid,
#if TEST_BUILD
        &km_rust_unittest_uuid,
        /*HWCrypto HAL needs to derive key and access keylots*/
        &hwcryptohal_rust_unittest_uuid,
#endif
        &hwcryptohal_rust_uuid,
        /* Needs access to opaque keys */
        &hwaes_uuid,
        /* Needs to derive keys */
        &gatekeeper_uuid,

        /* Needs to derive keys even if it doesn't have test keyslots */
        &hwcrypto_unittest_uuid,
        &hwcrypto_unittest_rust_uuid,

#if WITH_HWCRYPTO_UNITTEST
        &hwaes_unittest_uuid,
        &hwaes_bench_uuid,
#endif
        /* Needs to derive keys */
        &hwbcc_uuid,
        /* Needs to derive keys */
        &hwbcc_unittest_uuid,
};

/*
 *  List of keys slots that hwkey service supports
 */
static const struct hwkey_keyslot _keys[] = {
        {
                .uuid = &ss_uuid,
                .key_id = RPMB_SS_AUTH_KEY_ID,
                .handler = get_rpmb_ss_auth_key,
        },
        {
                .uuid = &km_uuid,
                .key_id = KM_KAK_ID,
                .handler = get_km_kak_key,
        },
        {
                .uuid = &km_rust_uuid,
                .key_id = KM_KAK_ID,
                .handler = get_km_kak_key,
        },
#if TEST_BUILD
        {
                .uuid = &km_rust_unittest_uuid,
                .key_id = KM_KAK_ID,
                .handler = get_km_kak_key,
        },
#endif
#if WITH_HWCRYPTO_UNITTEST
        {
                .uuid = &hwcrypto_unittest_uuid,
                .key_id = "com.android.trusty.hwcrypto.unittest.key32",
                .handler = get_unittest_key32_handler,
        },
        {
                .uuid = &hwcrypto_unittest_rust_uuid,
                .key_id = "com.android.trusty.hwcrypto.unittest.key32",
                .handler = get_unittest_key32_handler,
        },
        {
                .uuid = &hwcrypto_unittest_uuid,
                .key_id = "com.android.trusty.hwcrypto.unittest.derived_key32",
                .priv = &hwcrypto_unittest_derived_data,
                .handler = derived_keyslot_handler,
        },
        {
                .uuid = &hwcrypto_unittest_rust_uuid,
                .key_id = "com.android.trusty.hwcrypto.unittest.derived_key32",
                .priv = &hwcrypto_unittest_derived_data,
                .handler = derived_keyslot_handler,
        },
        {
                .uuid = &hwcrypto_unittest_uuid,
                .key_id = "com.android.trusty.hwcrypto.unittest.opaque_handle",
                .handler = get_key_handle,
                .priv = &unittest_opaque_handle_data,
        },
        {
                .uuid = &hwcrypto_unittest_rust_uuid,
                .key_id = "com.android.trusty.hwcrypto.unittest.opaque_handle",
                .handler = get_key_handle,
                .priv = &unittest_opaque_handle_data,
        },
        {
                .uuid = &hwcrypto_unittest_uuid,
                .key_id = "com.android.trusty.hwcrypto.unittest.opaque_handle2",
                .handler = get_key_handle,
                .priv = &unittest_opaque_handle_data2,
        },
        {
                .uuid = &hwcrypto_unittest_rust_uuid,
                .key_id = "com.android.trusty.hwcrypto.unittest.opaque_handle2",
                .handler = get_key_handle,
                .priv = &unittest_opaque_handle_data2,
        },
        {
                .uuid = &hwcrypto_unittest_uuid,
                .key_id =
                        "com.android.trusty.hwcrypto.unittest.opaque_handle_noaccess",
                .handler = get_key_handle,
                .priv = &unittest_opaque_handle_data_noaccess,
        },
        {
                .uuid = &hwcrypto_unittest_rust_uuid,
                .key_id =
                        "com.android.trusty.hwcrypto.unittest.opaque_handle_noaccess",
                .handler = get_key_handle,
                .priv = &unittest_opaque_handle_data_noaccess,
        },
        {
                .uuid = &hwcrypto_unittest_uuid,
                .key_id = "com.android.trusty.hwcrypto.unittest.opaque_derived",
                .handler = get_key_handle,
                .priv = &unittest_opaque_derived_data,
        },
        {
                .uuid = &hwcrypto_unittest_rust_uuid,
                .key_id = "com.android.trusty.hwcrypto.unittest.opaque_derived",
                .handler = get_key_handle,
                .priv = &unittest_opaque_derived_data,
        },
        {
                .uuid = &hwaes_unittest_uuid,
                .key_id = "com.android.trusty.hwaes.unittest.opaque_handle",
                .handler = get_key_handle,
                .priv = &hwaes_unittest_opaque_handle_data,
        },
        {
                .uuid = &hwaes_bench_uuid,
                .key_id = "com.android.trusty.hwaes.unittest.opaque_handle",
                .handler = get_key_handle,
                .priv = &hwaes_unittest_opaque_handle_data,
        },
#endif /* WITH_HWCRYPTO_UNITTEST */
};

/*
 *  Initialize HWKEY service
 */
static int hwkey_start_service(struct tipc_hset* hset) {
    TLOGD("Start HWKEY service\n");

    static struct tipc_port_acl acl = {
            .flags = IPC_PORT_ALLOW_TA_CONNECT,
            .uuid_num = countof(allowed_clients),
            .uuids = allowed_clients,
    };

    static struct tipc_port port = {
            .name = HWKEY_PORT,
            .msg_max_size = HWKEY_MAX_MSG_SIZE,
            .msg_queue_len = 1,
            .acl = &acl,
    };

    static struct tipc_srv_ops ops = {
            .on_message = hwkey_chan_handle_msg,
            .on_connect = hwkey_chan_ctx_create,
            .on_channel_cleanup = hwkey_chan_ctx_close,
    };

    return tipc_add_service(hset, &port, 1, HWKEY_MAX_NUM_CHANNELS, &ops);
}

/*
 *  Initialize Fake HWKEY service provider
 */
int hwkey_init_srv_provider(struct tipc_hset* hset, uint8_t* encrypt,
        size_t encrypt_size, uint8_t* attest, size_t attest_size,
        uint8_t* auth_token_key_seed, size_t auth_token_key_seed_size,
        int32_t rollback_version, int32_t running_version) {
    int rc;

    rollback_ver = rollback_version;
    running_ver = running_version;

    if (encrypt_size != sizeof(encrypt_key)) {
        TLOGE("bad encryption key size\n");
        abort();
    }
    memcpy(encrypt_key, encrypt, sizeof(encrypt_key));
    memset(encrypt, 0, encrypt_size);

    if (attest_size != sizeof(attest_key)) {
        TLOGE("bad attestion key size\n");
        abort();
    }
    memcpy(attest_key, attest, sizeof(attest_key));
    memset(attest, 0, attest_size);

    if (auth_token_key_seed_size != sizeof(kak_salt)) {
        TLOGE("bad auth token key seed size\n");
        abort();
    }
    memcpy(kak_salt, auth_token_key_seed, sizeof(kak_salt));
    memset(auth_token_key_seed, 0, auth_token_key_seed_size);

    /* install key handlers */
    hwkey_install_keys(_keys, countof(_keys));

    /* start service */
    rc = hwkey_start_service(hset);
    if (rc != NO_ERROR) {
        TLOGE("failed (%d) to start HWKEY service\n", rc);
    }

    return rc;
}
