/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*

sec_common.c

GENERAL DESCRIPTION
 Implemets QSEE version Platform Abstraction Layer(PAL) interface function(s) - common + crypto

*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/

#include <comdef.h>
#include <stdio.h>
#include <stdbool.h>

#include "qsee_log.h"     /* qsee_log */
#include "qsee_heap.h"    /* qsee_malloc / qsee_free */
#include "qsee_cipher.h"
#include "qsee_hash.h"
#include "qsee_prng.h"
#include "qsee_kdf.h"
#include "qsee_hmac.h"
#include "qsee_message.h"
#include "qsee_timer.h"
#include "qsee_core.h"

#include "authhat_protocol.h"
#include "sec_templ.h"
#include "sec_common.h"


static char key_label[AUTHHAT_SUBMODULE_MAX_KEY_LEN / 2] = {"Samsung Security"};
static char key_context_iris[AUTHHAT_SUBMODULE_MAX_KEY_LEN / 2] = {"iris challenge key"};
static char key_context_face[AUTHHAT_SUBMODULE_MAX_KEY_LEN / 2] = {"secfr challenge key"};

/** Memory allocation API */
void* PAL_MemoryAlloc(size_t size) {
    void *buffer = NULL;
    buffer = qsee_malloc(size);

    if (buffer == NULL) {
        PAL_DbgLog("allocation failed size : 0x%x", size);
    }

    return buffer;
}

/** Memory free API */
void PAL_MemoryFree(void* buffer) {
    if (buffer != NULL) {
        qsee_free(buffer);
    }
}

/** Print out log message API */
void PAL_DbgLog(const char* fmt, ...) {
    va_list args;
    char log_message[PAL_LOG_MSG_MAX];
    int ret = 0;

    va_start(args, fmt);
    ret = vsnprintf(log_message, PAL_LOG_MSG_MAX, fmt, args);
    va_end(args);

    if (ret < 0) {
        qsee_log(QSEE_LOG_MSG_ERROR, "Log message is too long.");
    } else {
        qsee_log(QSEE_LOG_MSG_ERROR, "%s", log_message);
    }
}

UINT32 PAL_get_uniqueKey(UINT32 tz_type, UINT8* unique_key) {

int rv = AUTHHAT_RESULT_SUCCESS;

    UINT8 key_input[AUTHHAT_SUBMODULE_KDFINPUT_KEY_LEN] = { 0, };
    uint8_t uuid_ptr[AUTHHAT_SUBMODULE_UUID_KEY_LEN] = { 0, };
    uint32_t uuid_length = AUTHHAT_SUBMODULE_UUID_KEY_LEN;

    rv = qsee_get_device_uuid(uuid_ptr, &uuid_length);
    if (rv != AUTHHAT_RESULT_SUCCESS) {
        PAL_DbgLog("PAL_get_uniqueKey fail - qsee_get_device_uuid error : %d", rv);
        return AUTHHAT_RESULT_FAIL_GET_UNIQUE_KEY;
    }

    int idx = 0;
    while (idx < AUTHHAT_SUBMODULE_KDFINPUT_KEY_LEN) {
        if (idx < uuid_length) {
            key_input[idx] = uuid_ptr[idx];
        } else {	
            key_input[idx] = key_input[idx - uuid_length] ^ key_input[idx - uuid_length + 1];
        }
        idx++;
    }

    if (tz_type == BIO_SUBMODULE_SEC_IRIS) {
        rv = qsee_kdf((void*)key_input, AUTHHAT_SUBMODULE_AES_KEY_LEN, (void*)(&key_label), AUTHHAT_SUBMODULE_AES_KEY_LEN, (void*)(&key_context_iris), AUTHHAT_SUBMODULE_HMAC_KEY_LEN, (void*)unique_key, AUTHHAT_SUBMODULE_MAX_KEY_LEN);
    } else if (tz_type == BIO_SUBMODULE_SEC_FR) {
        rv = qsee_kdf((void*)key_input, AUTHHAT_SUBMODULE_AES_KEY_LEN, (void*)(&key_label), AUTHHAT_SUBMODULE_AES_KEY_LEN, (void*)(&key_context_face), AUTHHAT_SUBMODULE_HMAC_KEY_LEN, (void*)unique_key, AUTHHAT_SUBMODULE_MAX_KEY_LEN);
    }
 
    if (rv != AUTHHAT_RESULT_SUCCESS) {
        PAL_DbgLog("PAL_get_uniqueKey fail - qsee_kdf error : %d", rv);
        return AUTHHAT_RESULT_FAIL_GET_UNIQUE_KEY;
    }

    return rv;
}

UINT32 PAL_generate_randBytes(UINT8* rands, UINT32 rands_len) {
    int rv = AUTHHAT_RESULT_SUCCESS;
    unsigned int length = 0;

    if (rands_len <= 0) {
        PAL_DbgLog("PAL_generate_randBytes fail - length is not normal length : %d", rands_len);
        rv = AUTHHAT_RESULT_FAIL_GENERATE_RANDOM_KEY;
        return rv;
    }

    length = qsee_prng_getdata((uint8*)rands, rands_len);

    if (length != rands_len) {
        PAL_DbgLog("PAL_generate_randBytes fail - qsee_prng_getdata failed");
        rv = AUTHHAT_RESULT_FAIL_GENERATE_RANDOM_KEY;
        return rv;
    }

    return rv;
}

UINT32 PAL_generate_HMAC(UINT8* in_msg, UINT32 msg_len, UINT8* key, UINT32 key_len, UINT8* out_digest) {
    int rv = AUTHHAT_RESULT_SUCCESS;

    if ((key == NULL) || (key_len > AUTHHAT_SUBMODULE_HMAC_KEY_LEN)) {
        PAL_DbgLog("PAL_generate_HMAC fail - key || key_len are not normal");
        rv = AUTHHAT_RESULT_FAIL_HMAC;
        return rv;
    }

    rv = qsee_hmac(QSEE_HMAC_SHA256, (uint8*)in_msg, msg_len, (uint8*)key, key_len, (uint8*)out_digest);

    if (rv != AUTHHAT_RESULT_SUCCESS) {
        PAL_DbgLog("PAL_generate_HMAC fail - qsee_hmac API failed");
        rv = AUTHHAT_RESULT_FAIL_HMAC;
        return rv;
    }

    return rv;
}

UINT32 PAL_AES_CBC_encrypt(UINT8* in_ar, UINT8* out_ar, UINT32 in_ar_size, UINT8* iv, UINT8* key) {
    int rv = AUTHHAT_RESULT_SUCCESS;
    int lc_cipher_len = 0;
    UINT8 temp_iv[AUTHHAT_SUBMODULE_IV_LEN] = {0};
    qsee_cipher_ctx *ctx = 0;
    QSEE_CIPHER_ALGO_ET alg  = QSEE_CIPHER_ALGO_AES_256;
    QSEE_CIPHER_MODE_ET mode = QSEE_CIPHER_MODE_CBC;
    QSEE_CIPHER_PAD_ET  pad  = QSEE_CIPHER_PAD_NO_PAD;

    if (iv != NULL) {
        rv = PAL_generate_randBytes(temp_iv, AUTHHAT_SUBMODULE_IV_LEN);

        if (rv != AUTHHAT_RESULT_SUCCESS) {
            PAL_DbgLog("PAL_AES_CBC_encrypt fail - random generation error ");
            rv = AUTHHAT_RESULT_FAIL_GENERAL;
            goto end;
        }

        memcpy(iv, temp_iv, AUTHHAT_SUBMODULE_IV_LEN);
    }

    if (key == NULL) {
        PAL_DbgLog("PAL_AES_CBC_encrypt fail - key buffer is null");
        rv = AUTHHAT_RESULT_FAIL_KEY_NULL;
        goto end;
    }

    /*--------------------------------------------------------------------
        Init ctx
    ----------------------------------------------------------------------*/
    if (qsee_cipher_init(alg, &ctx) < 0) {
        PAL_DbgLog("PAL_AES_CBC_encrypt fail - qsee_cipher_init API failed");
        rv = AUTHHAT_RESULT_FAIL_ON_ENCRYPT;
        goto end;
    }

    /*--------------------------------------------------------------------
        Set key for encryption
    ----------------------------------------------------------------------*/

    if (qsee_cipher_set_param(ctx, QSEE_CIPHER_PARAM_KEY, key, AUTHHAT_SUBMODULE_AES_KEY_LEN) < 0) {
        PAL_DbgLog("PAL_AES_CBC_encrypt fail - qsee_cipher_set_parm API failed 1");
        rv = AUTHHAT_RESULT_FAIL_ON_ENCRYPT;
        goto end;
    }

    /*--------------------------------------------------------------------
        Set AES mode
    ----------------------------------------------------------------------*/
    if (qsee_cipher_set_param(ctx, QSEE_CIPHER_PARAM_MODE, &mode, sizeof(QSEE_CIPHER_MODE_ET)) < 0) {
        PAL_DbgLog("PAL_AES_CBC_encrypt fail - qsee_cipher_set_parm API failed 2");
        rv = AUTHHAT_RESULT_FAIL_ON_ENCRYPT;
        goto end;
    }

    /*--------------------------------------------------------------------
        Set PAD type
    ----------------------------------------------------------------------*/
    if (qsee_cipher_set_param(ctx, QSEE_CIPHER_PARAM_PAD, &pad, sizeof(QSEE_CIPHER_PAD_ET)) < 0) {
        PAL_DbgLog("PAL_AES_CBC_encrypt fail - qsee_cipher_set_parm API failed 3");
        rv = AUTHHAT_RESULT_FAIL_ON_ENCRYPT;
        goto end;
    }

    /*--------------------------------------------------------------------
        Set IV only if not NULL
    ----------------------------------------------------------------------*/
    if (iv != NULL) {
        if (qsee_cipher_set_param(ctx, QSEE_CIPHER_PARAM_IV, iv, AUTHHAT_SUBMODULE_IV_LEN) < 0) {
            PAL_DbgLog("PAL_AES_CBC_encrypt fail - qsee_cipher_set_parm API failed 4");
            rv = AUTHHAT_RESULT_FAIL_ON_ENCRYPT;
            goto end;
        }
    }

    /*-----------------------------------------------------------------------
        Now encrypt the data
    -------------------------------------------------------------------------*/
    lc_cipher_len = in_ar_size;

    if ((qsee_cipher_encrypt(ctx, (uint8*)in_ar, in_ar_size, (uint8*)out_ar, (uint32*)&lc_cipher_len)) < 0) {
        PAL_DbgLog("PAL_AES_CBC_encrypt fail - qsee_cipher_encrypt API failed");
        rv = AUTHHAT_RESULT_FAIL_ON_ENCRYPT;
        goto end;
    }

end:

    if (ctx) {
        if (qsee_cipher_free_ctx(ctx) < 0) {
            PAL_DbgLog("PAL_AES_CBC_encrypt fail - qsee_cipher_free_ctx API failed");
            goto end;
        }

        ctx = 0;
    }

    return rv;
}

