/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*

sec_templ.c

GENERAL DESCRIPTION
 Implemets common Template & MetaData related API

*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/
#define LOG_NDEBUG 0
#define LOG_TAG "sec_templ"
	
#include <comdef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
	
#include "sec_main.h"
#include "authhat_protocol.h"
#include "sec_common.h"
#include "sec_templ.h"

/** golbal functions */
void integer_to_string(UINT32 val, UINT8* ar, UINT32 index) {
    UINT32 loop, shift;

    for (loop = 0, shift = 24 ; loop < 4 ; loop++, shift -= 8) {
        ar[index + loop] = (UINT8)(val >> shift);
    }
}

void string_to_integer(UINT32* val, UINT8* ar, UINT32 index) {
    UINT32 loop, shift, temp;
    *val = 0;

    for (loop = 0, shift = 24 ; loop < 4; loop++, shift -= 8) {
        temp = ar[index + loop];
        *val |= (temp << shift);
    }
}

void integer64_to_array(uint64_t val, UINT8 *ar, UINT32 index) {
    UINT32 loop, shift;

    for(loop = 0, shift = 56; loop < 8; loop++, shift -= 8) {
        ar[index + loop] = (UINT8)(val >> shift);
    }
}

void array_to_integer64(uint64_t *val, UINT8 *ar, UINT32 index) {
    uint64_t temp = 0;
    uint64_t loop = 0;
    uint64_t shift = 0;
    *val = 0;

    for (loop = 0, shift = 56; loop < 8; loop++, shift -= 8) {
        temp = ar[index + loop];
        *val |= (temp << shift);
    }
}


UINT32 authhat_encode_metadata(UINT32 tz_type,			
        UINT8* inMetaData, UINT32 inMetaDataSize,
        UINT8* outMetaData, UINT32* outMetaDataSize) {
    UINT32 rv = AUTHHAT_RESULT_SUCCESS;
    UINT32 pos = 0;
    UINT8 paddingLen = 0;
    UINT8 uniqueKey[AUTHHAT_SUBMODULE_MAX_KEY_LEN] = {0, };
    UINT8 metadataKey[AUTHHAT_SUBMODULE_MAX_KEY_LEN] = {0, };

    PAL_DbgLog("authhat_encode_metadata start - size : %d", inMetaDataSize);

    if (tz_type == BIO_SUBMODULE_SEC_IRIS) {
        paddingLen = 16 - (
		    ((AUTHHAT_SUBMODULE_METADATA_ID_LEN + AUTHHAT_SUBMODULE_META_DATA_HEADER_LEN + AUTHHAT_SUBMODULE_META_DATA_SIZE_OF_PADDING_LEN)
            + (inMetaDataSize + AUTHHAT_SUBMODULE_HMAC_LEN))
            % 16);
    } else if (tz_type == BIO_SUBMODULE_SEC_FR) {
        paddingLen = 16 - (
            ((AUTHHAT_SUBMODULE_META_DATA_HEADER_LEN + AUTHHAT_SUBMODULE_META_DATA_SIZE_OF_DATA_LEN + AUTHHAT_SUBMODULE_META_DATA_SIZE_OF_PADDING_LEN)
            + (inMetaDataSize + AUTHHAT_SUBMODULE_HMAC_LEN))
            % 16);
    } else {
    	rv = AUTHHAT_RESULT_INVALID_PARAMETER;
		PAL_DbgLog("authhat_encode_metadata fail - tz_type check fail : %d", tz_type);
		return rv;
    }
	
    /* generate Key for AES and HMAC for metadata */
    rv = PAL_generate_randBytes(metadataKey, AUTHHAT_SUBMODULE_MAX_KEY_LEN);
    if (rv != AUTHHAT_RESULT_SUCCESS) {
        PAL_DbgLog("authhat_encode_metadata fail - PAL_generate_randBytes error : %d", rv);
        return rv;
    }

    do {
        /* jump encrypted data length */
        pos += AUTHHAT_SUBMODULE_META_DATA_SIZE_OF_TOTAL_LEN;

	    if (tz_type == BIO_SUBMODULE_SEC_IRIS) {	
            pos += AUTHHAT_SUBMODULE_METADATA_ID_LEN;	
        } else if (tz_type == BIO_SUBMODULE_SEC_FR) {
            integer_to_string(tz_type, outMetaData, pos);
            pos += AUTHHAT_SUBMODULE_META_DATA_HEADER_LEN;		
        } else {
    	    rv = AUTHHAT_RESULT_INVALID_PARAMETER;
		    PAL_DbgLog("authhat_encode_metadata fail - bioSubModule_type check fail : %d", tz_type);
		    return rv;
        }

        /* add data size */
        integer_to_string(inMetaDataSize, outMetaData, pos);
        pos += AUTHHAT_SUBMODULE_META_DATA_SIZE_OF_DATA_LEN;

        /* add padding length and padding */
        *(outMetaData + pos) = paddingLen;
        pos += AUTHHAT_SUBMODULE_META_DATA_SIZE_OF_PADDING_LEN;
        memset((outMetaData + pos), 0, paddingLen);
        pos += paddingLen;

        /* add data */
        if ((inMetaDataSize > 0) && (inMetaDataSize <= (AUTHHAT_SUBMODULE_MAX_META_DATA_LEN / 2))) {
            memcpy((outMetaData + pos), inMetaData, inMetaDataSize);
            pos += inMetaDataSize;
        } else {
            PAL_DbgLog("authhat_encode_metadata error - wrong input data length : %d", inMetaDataSize);
            rv = (UINT32) AUTHHAT_RESULT_FAIL_GENERAL;
            break;
        }

        /* add encrypted data length */
        integer_to_string((pos + AUTHHAT_SUBMODULE_HMAC_LEN + AUTHHAT_SUBMODULE_IV_LEN - AUTHHAT_SUBMODULE_META_DATA_SIZE_OF_TOTAL_LEN), outMetaData, 0);

        /* inner hmac (half back of metadataKey for inner HMAC) */
        rv = PAL_generate_HMAC(outMetaData, pos, (metadataKey + AUTHHAT_SUBMODULE_AES_KEY_LEN), AUTHHAT_SUBMODULE_HMAC_KEY_LEN, (outMetaData + pos));
        if (rv != AUTHHAT_RESULT_SUCCESS) {
            PAL_DbgLog("authhat_encode_metadata error - internal inner hmac error : %d", rv);
            break;
        }
        pos += AUTHHAT_SUBMODULE_HMAC_LEN;

        /* first encrpytion & add iv (half front of metadataKey is used for first encryption) */
        rv = PAL_AES_CBC_encrypt((outMetaData + AUTHHAT_SUBMODULE_META_DATA_SIZE_OF_TOTAL_LEN),
                (outMetaData + AUTHHAT_SUBMODULE_META_DATA_SIZE_OF_TOTAL_LEN),
                (pos - AUTHHAT_SUBMODULE_META_DATA_SIZE_OF_TOTAL_LEN), (outMetaData + pos), metadataKey);
        if (rv != AUTHHAT_RESULT_SUCCESS) {
            PAL_DbgLog("authhat_encode_metadata - 1st encryption fail : %d", rv);
            break;
        }
        pos += AUTHHAT_SUBMODULE_IV_LEN;

        /* add 64 bytes metadata key */
        memcpy((outMetaData + pos), metadataKey, AUTHHAT_SUBMODULE_MAX_KEY_LEN);

        /* generate Device unique Key */
        rv = PAL_get_uniqueKey(tz_type, uniqueKey);
        if (rv != AUTHHAT_RESULT_SUCCESS) {
            PAL_DbgLog("authhat_encode_metadata fail - PAL_get_uniqueKey error : %d", rv);
            break;
        }

        /* second encryption and add iv (half front of unique_key is used for second encryption) */
        rv = PAL_AES_CBC_encrypt((outMetaData + pos), (outMetaData + pos), AUTHHAT_SUBMODULE_MAX_KEY_LEN, 
                (outMetaData + pos + AUTHHAT_SUBMODULE_MAX_KEY_LEN), uniqueKey);
        if (rv != AUTHHAT_RESULT_SUCCESS) {
            PAL_DbgLog("authhat_encode_metadata - 2nd encryption fail : %d", rv);
            break;
        }

        /* jump pos as many as encrypted KEY LEN + IV_LEN */
        pos += (AUTHHAT_SUBMODULE_MAX_KEY_LEN + AUTHHAT_SUBMODULE_IV_LEN);

        /* outer hmac (half back of unique_key for outer HMAC ) */
        rv = PAL_generate_HMAC(outMetaData, pos, uniqueKey + AUTHHAT_SUBMODULE_AES_KEY_LEN, AUTHHAT_SUBMODULE_HMAC_KEY_LEN, outMetaData + pos);
        if (rv != AUTHHAT_RESULT_SUCCESS) {
            PAL_DbgLog("authhat_encode_metadata - internal outer hmac error : %d", rv);
            break;
        }
        pos += AUTHHAT_SUBMODULE_HMAC_LEN;

        /* set output data size */
        *outMetaDataSize = pos;
    } while(0);

    memset(uniqueKey, 0, AUTHHAT_SUBMODULE_MAX_KEY_LEN);
    memset(metadataKey, 0, AUTHHAT_SUBMODULE_MAX_KEY_LEN);

    PAL_DbgLog("authhat_encode_metadata end with %d", rv);
    return rv;
}

