/*
 * app_attestation.c
 */

#include <tee_internal_api.h>

#include "app_main.h"
#include "app_cipher.h"
#include "app_core.h"
#include "app_driver.h"
#include "app_saveData.h"
#include "app_readData.h"
#include "app_attestation.h"

#include "icccOperations_v4.h"

static uint32_t switch_endianness(uint32_t val)
{
    uint32_t result;
    result = ((val & 0xff) << 24) | (((val >> 8) & 0xff) << 16) |
             (((val >> 16) & 0xff) << 8) | ((val >> 24) & 0xff);
    return result;
}

uint32_t Iccc_SaveATN_TA(uint8_t *blob, uint32_t *blob_len)
{
    uint32_t sectimer_header_size = sizeof(uint8_t) * 3; // Type(1 byte) + Length(2 Byte)
    uint32_t sectimer_type[2][2] = {
        {SECTIMER_BASE,     ICCC_ATN_RESULT_TYPE_SECTIMER_BASE},
        {SECTIMER_STATUS,   ICCC_ATN_RESULT_TYPE_SECTIMER_STATUS}
    };
    uint32_t sectimer_value;
    uint8_t digest[SHA256_DIGEST_LENGTH] = {0, };
    uint32_t digest_len = sizeof(digest);
    uint32_t ret = ICCC_SUCCESS;

    uint32_t secure_param_type;
    uint32_t secure_param_addr;
    uint32_t secure_mem_addr;
    uint16_t magic_str = 0;

    ICCC_LOG("TZ_ICCC: Iccc_SaveATN_TA: given blob from N/W, blob_len = %d", *blob_len);

    // Add securetimer value to blob before generate hash
    for (uint32_t i = 0; i < sizeof(sectimer_type) / sizeof(sectimer_type[0]); i++) {
        sectimer_value = -1;
        if (*blob_len + sectimer_header_size + sizeof(sectimer_value) > ICCC_ATN_BLOB_MAX_SIZE) {
            ICCC_LOG("TZ_ICCC: invalid blob length: (*blob_len + sectimer_header_size + sizeof(sectimer_value)) = 0x%x, ret = 0x%x",
                                                     *blob_len + sectimer_header_size + sizeof(sectimer_value), ret);
            return ICCC_ERROR_ATTESTATION_FAILED;
        }

        ret = Iccc_Core_ReadData_TA(sectimer_type[i][0], &sectimer_value);
        if (ret) {
            ICCC_LOG("TZ_ICCC: Error reading sectimer type = 0x%x, value = %d, ret = 0x%x\n", sectimer_type[i][0], sectimer_value, ret);
            ret = ICCC_ERROR_ATTESTATION_FAILED;
            goto error;
        }
        sectimer_value = switch_endianness(sectimer_value);

        blob[(*blob_len)++] = sectimer_type[i][1];
        blob[(*blob_len)++] = 0;
        blob[(*blob_len)++] = sizeof(sectimer_value);
        TEE_MemMove(blob + *blob_len, (uint8_t *)&sectimer_value, sizeof(sectimer_value)); 
        *blob_len += sizeof(sectimer_value);

        ICCC_LOG("TZ_ICCC: ATN blob after adding sectimer type 0x%x", sectimer_type[i][0]);
    }

    // Generate digest
    ret = Iccc_digest_SHA256(blob, *blob_len, digest, &digest_len);
    if (ret) {
        ICCC_LOG("TZ_ICCC: Iccc_digest_SHA256 failed with ret = 0x%x\n", ret);
        ret = ICCC_ERROR_ATTESTATION_FAILED;
        goto error;
    }
    if (digest_len != SHA256_DIGEST_LENGTH) {
        ICCC_LOG("TZ_ICCC: invalid SHA256 digest length with ret = 0x%x, length = %d", ret, digest_len);
        ret = ICCC_ERROR_ATTESTATION_FAILED;
        goto error;
    }
    //ICCC_LOG("TZ_ICCC: sha256 result = %s", digest);

    // Save hash to secure memory
    secure_mem_addr = get_sec_ICCC_address(PARAM_FOR_ICCC_SEC_MEM);
    secure_param_type = (ATN_BLOB_HASH >> 20) & (0xF);
    secure_param_addr = (uint32_t)((ATN_BLOB_HASH & (0x1F)) * ICCC_SECURE_PARAMETERS_READING_LENGTH);

    ret = Iccc_check_magic(secure_param_type, secure_mem_addr, &magic_str);
    if (ret) {
        ICCC_LOG("TZ_ICCC: check magic failed");
        ICCC_LOG_DEBUG("TZ_ICCC: Iccc_check_magic failed with ret = %x ", ret);
        return ICCC_ERROR_ATTESTATION_FAILED;
    }
    if (secure_param_type == 0x0 && magic_str == TA_MAGIC_STR) {
        if (secure_param_addr > sizeof(ta_secure_info_t)) {
            ICCC_LOG("TZ_ICCC: secure_param_addr error");
            ICCC_LOG_DEBUG("TZ_ICCC: secure_param_addr > sizeof(ta_secure_info_t)");
            return ICCC_ERROR_ATTESTATION_FAILED;
        }
        if (0 != (ret = Iccc_phys_write((void *)(secure_mem_addr + ICCC_TA_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)),
                                                 SHA256_DIGEST_LENGTH, (void *)(digest)))) {
            ICCC_LOG("TZ_ICCC: Iccc_phys_write failed");
            ICCC_LOG_DEBUG("TZ_ICCC: Iccc_phys_write failed: (secure_mem_addr + ICCC_TA_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)) = 0x%x, ret = 0x%x",
                                                             (secure_mem_addr + ICCC_TA_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)), ret);
            ret = ICCC_ERROR_ATTESTATION_FAILED;
        }
    }

error:
    return ret;
}

uint32_t ICCC_attestation(tz_iccc_attestation_payload_t *sendmsg, tz_iccc_attestation_payload_t *respmsg)
{
    uint8_t blob[ICCC_ATN_BLOB_MAX_SIZE] = {0, };
    uint32_t blob_len = 0;
    uint32_t ret = ICCC_SUCCESS;

    ICCC_LOG("TZ_ICCC: ICCC_attestation");

    if (respmsg == NULL || sendmsg == NULL) {
        ICCC_LOG("TZ_ICCC: sendmsg or respmsg is NULL ");
        return ICCC_FAILURE;
    }

    blob_len =  sendmsg->content.iccc_req.blob_len;
    if (blob_len >= ICCC_ATN_BLOB_MAX_SIZE) {
        ICCC_LOG("TZ_ICCC: invalid blob length = %d", blob_len);
        return ICCC_ERROR_ATTESTATION_FAILED;
    }
    TEE_MemMove(blob, sendmsg->content.iccc_req.blob, blob_len);

    ret = Iccc_SaveATN_TA(blob, &blob_len);
    if (ret) {
        ICCC_LOG("TZ_ICCC: Iccc_SaveATN_TA failed with ret = %#x ", ret);
        ret = ICCC_ERROR_ATTESTATION_FAILED;
    } else {
        TEE_MemMove(respmsg->content.iccc_rsp.blob, blob, blob_len); 
        respmsg->content.iccc_rsp.blob_len = blob_len;
    }
    respmsg->content.iccc_rsp.ret = ret;

    ICCC_LOG("TZ_ICCC: Result ATN blob len = %d", respmsg->content.iccc_rsp.blob_len);

    return ret;
}
