/*
 * app_attestation.c
 */

#include <qsee_hash.h>

#include "app_main.h"
#include "app_cipher.h"
#include "app_core.h"
#include "app_saveData.h"
#include "app_readData.h"
#include "app_attestation.h"

#include "tz_iccc_comdef.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[QSEE_SHA256_HASH_SZ] = {0, };
    uint32_t digest_len = sizeof(digest);
    uint32_t ret = ICCC_SUCCESS;

    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 < ((uint32_t)sizeof(sectimer_type) / (uint32_t)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 = %#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 = %#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);
        memcpy(blob + *blob_len, (uint8_t *)&sectimer_value, sizeof(sectimer_value)); 
        *blob_len += (uint32_t)sizeof(sectimer_value);

        ICCC_LOG("TZ_ICCC: ATN blob after adding sectimer type %#x", sectimer_type[i][0]);
    }

    // Generate digest
    ret = TZ_SHA256_digest(blob, *blob_len, digest, &digest_len);
    if (ret) {
        ICCC_LOG("TZ_ICCC: TZ_SHA256_digest failed with ret = %#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
    ret = Iccc_Core_SaveData_TA(ATN_BLOB_HASH, (uint32_t*) &digest[0]);
    if (ret) {
        ICCC_LOG("TZ_ICCC: Error saving ATN blob hash, ret = 0x%x\n", ret);
        goto error;
    }

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;
    }
    memcpy(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 {
        memcpy(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;
}
