#include "TZ_Vendor_tl.h"
#include "process_msr.h"
#include "com_dtypes.h"
#include "tima_config.h"
#include <qsee_oem_buffer.h>
#if ICCC_FEATURE
#ifndef TZ_ICCC
#include "tz_iccc_comdef.h"
#else
#include "iccc_core.h"
#endif
#endif
#define TZBSP_OEM_KAP_MAGIC             0x5afe0000
/*
 * Added by Ahmed Azab a.azab@samsung.com
 * System call to check the status of the Knox Active Protection (KAP) mode
 * The possible return values:
 *              0x5afe0011 -> KAP ON
 *              0x5afe0001 -> KAP ON, A security violation occured
 *              0x5afe0000 -> KAP OFF
 */
#define TZBSP_OEM_KAP_ON                0x11
#define TZBSP_OEM_KAP_VIOL              0x01
#define TZBSP_OEM_KAP_OFF               0x00
#define TZBSP_OEM_KAP_NOT_SUPPORTED     0xFF

#define QSEE_HASH_IDX_SHA1 2
#define QSEE_HASH_IDX_SHA256 3

#if !defined(CONFIG_QSEE)
uint32_t is_kap_on(
)
{
	//TODO: need api for LSI
	return TZ_API_OK;
}
uint8_t get_kap_status(
)
{
	//TODO: need api for LSI
	return TZBSP_OEM_KAP_NOT_SUPPORTED;
}
#else
uint8_t get_kap_status()
{
	uint32_t ret;
	uint32_t kap_fuse = 0;

	//to use following api, TZ APP should have privilege to use oem buffers
	ret = qsee_read_oem_buffer(sizeof(uint32)*3,(void*)(&kap_fuse),sizeof(uint32));
	if(ret != QSEE_OEM_BUFFER_SUCCESS) {
		TTY_LOG( "error reading kap status : %d",ret);
		return TZBSP_OEM_KAP_NOT_SUPPORTED;
	}

	TTY_LOG("kap fuse is: %x", kap_fuse);
	switch(kap_fuse) {
		case (TZBSP_OEM_KAP_MAGIC | TZBSP_OEM_KAP_ON):
			TTY_LOG( "kap on");
			return TZBSP_OEM_KAP_ON;
		case (TZBSP_OEM_KAP_MAGIC | TZBSP_OEM_KAP_OFF):
			TTY_LOG( "kap off");
			return TZBSP_OEM_KAP_OFF;
		case (TZBSP_OEM_KAP_MAGIC | TZBSP_OEM_KAP_VIOL):
			TTY_LOG( "kap violation");
			return TZBSP_OEM_KAP_VIOL;
		default:
			TTY_LOG( "unknown kap status, kap not supported");
			return TZBSP_OEM_KAP_NOT_SUPPORTED;
	}
}

uint32_t is_kap_on()
{
	uint8_t ret;

	ret = get_kap_status();
	switch(ret) {
		case TZBSP_OEM_KAP_ON :
		case TZBSP_OEM_KAP_NOT_SUPPORTED :
			return TZ_API_OK;
		case TZBSP_OEM_KAP_OFF :
		case TZBSP_OEM_KAP_VIOL :
			return TZ_API_ERROR;
		default :
			return TZ_API_OK;
	}
}
#endif

#define RSAEXPONENT	3
#define RSA_NUMBYTES  256
#define RSANUMWORDS (RSA_NUMBYTES/sizeof(uint32_t))

#define TIMA_SIGN_LEN 256
/* The first 16 bytes (0x00 ~ 0x0f) is for TIMA_MAGIC */
#define TIMA_MAGIC "TIMA3.0"
#define TIMA_MAGIC_LEN strlen(TIMA_MAGIC)
/* The second 16 bytes (0x10 ~ 0x1f) is for SVB_MAGIC */
#define SVB_MAGIC "SVB1.0"
#define SVB_MAGIC_LEN strlen(SVB_MAGIC)
/* The 16th byte is for license status */
#define KNOX_LICENSE_SIZE 2
#define KNOX_LICENSE_ENABLE 0x0057


uint32_t samsung_modulus[RSANUMWORDS] =
    { 1891720543U, 2898899684U, 2253445094U, 1936295747U, 4196704067U,
	3864872433U, 1472239824U, 3164687093U, 338896034U, 904012785U,
	2451936152U, 827648720U, 365656865U,
	637536106U, 1388833157U, 2732715088U, 1940841272U, 1539208106U,
	1863720856U, 2896482916U, 3409408324U,
	4045575973U, 3038854981U, 2023178681U, 712543360U, 57729334U,
	2201215102U, 2563951780U, 3027128989U,
	2774880565U, 436072510U, 4203237951U, 176406685U, 2260374784U,
	2948096985U, 2744760779U, 3277277305U,
	818397983U, 3771428155U, 2861543228U, 2799946394U, 1811432048U,
	2474392044U, 3149618135U, 2995766846U,
	4184031422U, 81011589U, 3844396389U, 727248317U, 2603428677U,
	4223592821U, 692124161U, 17089674U,
	3445043668U, 79863921U, 3175980450U, 3248655378U, 3631812347U,
	3034309458U, 2462841710U, 405598924U,
	185102806U, 860543909U, 3072136338U
};

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;
}

static  uint32_t tima_verify_signature(
	uint8_t * msg,
	uint32_t len,
	uint8_t * sig_ptr,
	uint32_t type
)
{
	uint32_t final_modulus[RSANUMWORDS];
	uint32_t i, j;
	uint8_t exponent = RSAEXPONENT;
	bool isValidSig = false;
	uint32_t ret = TZ_API_ERROR;

	/* Prepare the modulus - provided modulus is little endian
	 * Needs to change according to mobicore expectations
	 */
	for (i = 0, j = (RSANUMWORDS - 1); i < RSANUMWORDS; i++, j--)
		final_modulus[i] = switch_endianness(samsung_modulus[j]);

	if (type == 0xEA) {
		ret =  TZ_verify_CKM_SHA256_RSA_PKCS(((uint8_t *) & final_modulus[0]), RSA_NUMBYTES,
									   ((uint8_t *) & exponent), sizeof(uint8_t),
									   msg, len, sig_ptr, RSA_NUMBYTES, &isValidSig);
	}
	else {
		ret =  TZ_verify_CKM_SHA1_RSA_PKCS(((uint8_t *) & final_modulus[0]), RSA_NUMBYTES,
									   ((uint8_t *) & exponent), sizeof(uint8_t),
									   msg, len, sig_ptr, RSA_NUMBYTES, &isValidSig);
	}

	if (TZ_API_OK != ret) {
		TTY_LOG("Signature verification API failure");
		goto verify_sig_exit;
	}

	if (isValidSig == true) {
		TTY_LOG("Signature is valid");
		ret = TZ_API_OK;
		goto verify_sig_exit;
	} else {
		ret =
			TZ_verify_special_qsee_padding((uint8_t *) &
						samsung_modulus[0],
						RSA_NUMBYTES, &exponent,
						sizeof(uint8_t), msg, len,
						sig_ptr, RSA_NUMBYTES,
						&isValidSig, (type == 0xEA) ? QSEE_HASH_IDX_SHA256 : QSEE_HASH_IDX_SHA1);
		if (TZ_API_OK == ret) {
			TTY_LOG("Signature is valid");
			ret = TZ_API_OK;
		} else {
			TTY_LOG("Invalid signature");
		}
	}
verify_sig_exit:
	return ret;
}

uint32_t read_measurement(
	void *msg,
	uint32_t size,
	uint8_t * msr,
	uint16_t * msr_size
)
{
	uint32_t ret;
	unsigned char type = 0;
	uint8_t *signature = NULL;

	/*check for knox build option */
	memcpy(&type, msg, 1);
	if (type != 0xE9 && type != 0xEA) {
		TTY_LOG("TIMA trustlet: tima only works with knox images!");
		//return 1;
	}

	if (size <= TIMA_SIGN_LEN
	    || size > TIMA_MSR_MAX_SIZE + TIMA_SIGN_LEN + 1) {
		TTY_LOG
		    ("TIMA trustlet: tima_measurement_info has the wrong size (%d)!",
		     size);
		return 1;
	}

	signature = (uint8_t *) msg + size - TIMA_SIGN_LEN;
	ret = tima_verify_signature(msg, size - TIMA_SIGN_LEN, signature, type);
	if (ret != 0) {
		TTY_LOG
		    ("TIMA trustlet: tima_measurement_info has the wrong signature!");
		return 1;
	}

	*msr_size = size - TIMA_SIGN_LEN - 1;
	memcpy(msr, ((uint8_t *) msg) + 1, *msr_size);

	return 0;
}

uint32_t boot_measurement(
	uint8_t * msr,
	uint16_t msr_size
)
{
 	uint32_t ret;

	ret = qsee_read_oem_buffer(TIMA_BOOT_MEASUREMENT_OFFSET,msr,msr_size);
	if(ret) {
		TTY_LOG("%s: Failed to read boot msr, ret:%d, msr_size:%d", __func__, ret, msr_size);
	}

	return ret;
}

#if defined(CONFIG_SMDK5420) || defined(CONFIG_QSEE) || defined(CONFIG_SMDK5422) || defined(CONFIG_SMDK5430) || defined(CONFIG_SMDK3470) || defined(CONFIG_SMDK4415)
uint32_t read_golden_measurement(
	uint8_t * msr,
	uint16_t * msr_size
)
{
	uint32_t ret;
	uint16_t golden_msr_size = 0;
	uint8_t tima_magic_golden_msr[TIMA_MSR_MAX_SIZE] = { 0 };

	TTY_LOG("TIMA trustlet: Reading golden measurement");
	
	ret = qsee_read_oem_buffer(TIMA_GOLDEN_MEASUREMENT_OFFSET,(void *)tima_magic_golden_msr,TIMA_MSR_MAX_SIZE);
	if(ret) {
		TTY_LOG("%s: Failed to read golden msr, ret:%d", __func__, ret);
		goto exit;
	}	
	
	if ((ret =
	     memcmp(tima_magic_golden_msr, TIMA_MAGIC, TIMA_MAGIC_LEN)) != 0) {
		TTY_LOG("TIMA trustlet: Secure memory is not initialized");
		ret = 3;
		goto exit;
	}
	
	//read the measurement size from the secure memory which follows the TIMA_MAGIC
	memcpy(&golden_msr_size, (tima_magic_golden_msr + TIMA_MAGIC_LEN + KNOX_LICENSE_SIZE),
	       sizeof(golden_msr_size));
	TTY_LOG("TIMA trustlet: Measurement size is : %d", golden_msr_size);
	
	//Check if the measurement size is corrupt
	if (golden_msr_size > (TIMA_MSR_MAX_SIZE - TIMA_MAGIC_LEN - KNOX_LICENSE_SIZE - sizeof(golden_msr_size))) {
		TTY_LOG
		    ("TIMA trustlet: Secure memory is not initialized, wrong measurement size");
		ret = 3;
		goto exit;
	}
	
	//copy the measurement size to the output parameter
	*msr_size = golden_msr_size;
	
	// Then copy the measurements to the output buffer
	memcpy(msr,
	       (tima_magic_golden_msr + TIMA_MAGIC_LEN + KNOX_LICENSE_SIZE +
		sizeof(golden_msr_size)), golden_msr_size);

exit:
	return ret;
}

uint32_t init_golden_measurement(
	uint8_t * msr,
	uint16_t msr_size
)
{
	uint32_t ret;
	uint8_t tima_magic_golden_msr[TIMA_MSR_MAX_SIZE] = { 0 };

	TTY_LOG("TIMA trustlet: init golden measurement msr_len = %d", msr_size);

	// write TIMA_MAGIC at the beginning of the measurement area
	memcpy(tima_magic_golden_msr, TIMA_MAGIC, TIMA_MAGIC_LEN);

	// write the size of the measurements following TIMA_MAGIC
	memcpy((tima_magic_golden_msr + TIMA_MAGIC_LEN + KNOX_LICENSE_SIZE), &msr_size,
	       sizeof(msr_size));

	// finally, write the measurements to the secure mem
	memcpy((tima_magic_golden_msr + TIMA_MAGIC_LEN + KNOX_LICENSE_SIZE + sizeof(msr_size)), msr,
	       msr_size);
	
	ret = qsee_write_oem_buffer(TIMA_GOLDEN_MEASUREMENT_OFFSET,tima_magic_golden_msr,TIMA_MSR_MAX_SIZE);
	if(ret) {
		TTY_LOG("%s: Failed to init golden msr, ret:%d", __func__, ret);
	}

	TTY_LOG("TIMA trustlet: successfully written to secure memory");
	TTY_LOG("TZ_COMMON: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
			tima_magic_golden_msr[0], tima_magic_golden_msr[0 + 1], tima_magic_golden_msr[0 + 2], tima_magic_golden_msr[0 + 3], tima_magic_golden_msr[0 + 4],
			tima_magic_golden_msr[0 + 5], tima_magic_golden_msr[0 + 6], tima_magic_golden_msr[0 + 7], tima_magic_golden_msr[0 + 8], tima_magic_golden_msr[0 + 9],
			tima_magic_golden_msr[0 + 10], tima_magic_golden_msr[0 + 11], tima_magic_golden_msr[0 + 12], tima_magic_golden_msr[0 + 13],
			tima_magic_golden_msr[0 + 14], tima_magic_golden_msr[0 + 15]);

	return ret;
}

uint16_t read_license_status()
{
	uint32_t ret;
	uint16_t status = 0;

	TTY_LOG("TIMA trustlet: Reading knox license status");

	ret = qsee_read_oem_buffer(TIMA_GOLDEN_MEASUREMENT_OFFSET+TIMA_MAGIC_LEN, &status, KNOX_LICENSE_SIZE);
	if(ret) {
		TTY_LOG("%s: Failed to read knox license status, ret:%d", __func__, ret);
	}
	TTY_LOG("read license status is: %x", status);
	return status;
}

uint32_t set_license_status(uint8_t status)
{
	uint32_t ret = TZ_API_ERROR;
	uint16_t license_disable = 0;
	uint16_t license_enable = KNOX_LICENSE_ENABLE;

	TTY_LOG("TIMA trustlet: set license status %x", status);

	if (status == 0) {
		ret = qsee_write_oem_buffer(TIMA_GOLDEN_MEASUREMENT_OFFSET+TIMA_MAGIC_LEN, &license_disable, KNOX_LICENSE_SIZE);
	} else {
		ret = qsee_write_oem_buffer(TIMA_GOLDEN_MEASUREMENT_OFFSET+TIMA_MAGIC_LEN, &license_enable, KNOX_LICENSE_SIZE);
	}

	if (ret) {
		TTY_LOG("%s: Failed to set knox license status, ret:%d", __func__, ret);
	} else {
		TTY_LOG("%s: successfully set knox license status, ret:%d", __func__, ret);
	}

	return ret;
}

uint32_t is_high_security_mode()
{
	if (read_license_status() == KNOX_LICENSE_ENABLE) {
		TTY_LOG("high security mode");
		return TZ_API_OK;
	}
	TTY_LOG("low security mode");
	return TZ_API_ERROR;
}

uint32_t check_license_kap()
{
	if (is_high_security_mode() == TZ_API_OK)
		return is_kap_on();
	return TZ_API_OK;
}
#endif

uint32_t is_tamper_fuse_bit_set()
{
#ifndef TEMP_DEV_SKIP_MSR_COMPARE
	uint32_t warranty_bit = 1;
#ifdef TZ_ICCC
	if (ICCC_SUCCESS == Iccc_Core_ReadData_TA(WARRANTY_BIT, &warranty_bit))
#else
	if (ICCC_SUCCESS == Iccc_ReadData_TA(WARRANTY_BIT, &warranty_bit))
#endif
	{
	    if(warranty_bit == 0)
	    {
		    TTY_LOG("TZ_COMMON: warranty_bit valid");
		    return 0;
        } else {
            TTY_LOG("TZ_COMMON: warranty_bit invalid");
	    }
	} else {
		TTY_LOG("TZ_COMMON: ICCC failure for WB");
	}
	
	
#else
	TTY_LOG("TEMP_DEV_SKIP_MSR_COMPARE");
	return 0;
#endif
	return 1;
}

int32_t is_svb_enabled()
{
	char buf[32];

	int32_t ret;
	ret = qsee_read_oem_buffer(SVB_MAGIC_OFFSET, (void *)buf, SVB_MAGIC_LEN);
	if (ret) {
		TTY_LOG("fails to read svb magic");
		return -1;
	}

	if ((ret = memcmp((uint8_t *)buf, (uint8_t *)SVB_MAGIC, SVB_MAGIC_LEN)) != 0) {
		TTY_LOG("SVB is not enabled.\n");
		return 0;
	}

	TTY_LOG("SVB is enabled.\n");
	return 1;
}