#include <stdint.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include "ServiceKey.h"
#include "libdk_native_client.h"
#include "TAStartupCode.h"
#include "log.h"
#include "Utils.h"

#if !defined(USE_QSEE_WRAP_WITH_SFS)
const char sk_key_dir[] = "/efs/prov_data/mldap/";
const char sk_key_path[] = "/efs/prov_data/mldap/mldap.dat";
#endif

int32_t readKeyFile( const char* key_file_path, uint8_t* wrappedKey, uint32_t* wrappedKeyLen )
{
	FILE *fp;
	int32_t res = 0;
	if (!key_file_path || !wrappedKey || !wrappedKeyLen) {
		return WRONG_DATA;
	}
	fp = fopen(key_file_path, "rb");
	if (!fp) {
		LOGE("fopen() FAILED! returned = %d", res);
		return READ_KEY_ERROR;
	}
	res = fread(wrappedKey, sizeof(uint8_t), *wrappedKeyLen, fp);
	fclose(fp);
	if (res <= 0) {
		LOGE("fread() FAILED! returned = %d", res);
		return READ_KEY_ERROR;
	}

	*wrappedKeyLen = res;
	return NO_ERROR;
}

int32_t writeKeyFile( const char* key_file_path, uint8_t* wrappedKey, uint32_t wrappedKeyLen )
{
	FILE *fp;
	int32_t res = 0;
	if (!key_file_path || !wrappedKey || !wrappedKeyLen) {
		return WRONG_DATA;
	}
	fp = fopen(key_file_path, "wb");
	if (!fp) {
		LOGE("fopen() FAILED! returned = %d", res);
		return WRITE_KEY_ERROR;
	}
	res = fwrite(wrappedKey, sizeof(uint8_t), wrappedKeyLen, fp);
	fclose(fp);
	if (res != wrappedKeyLen) {
		LOGE("fwrite() FAILED! returned = %d", res);
		return WRITE_KEY_ERROR;
	}

	return NO_ERROR;
}

int csGenerateServiceKey(uint8_t keyType, const struct KeyInfo *keyInfo, const uint8_t *TID, uint32_t TIDLen){
	int32_t ret  = 0;
	uint8_t wrappedKey[MAX_TRANSFER_SIZE] = {0};
	uint32_t wrappedKeyLen = sizeof(wrappedKey);

	uint8_t encryptedKey[MAX_TRANSFER_SIZE] = {0};
	uint32_t encryptedKeyLen = sizeof(encryptedKey);

	uint8_t isEnableTlv = 0;

	// Checking existance DRK
	if ((ret = isExistDeviceRootKey(KEY_TYPE_RSA)) != DRK_IS_EXIST) {
		LOGE("DRK is not existed. Error = %d", ret);
		goto handleError;
	}

	// Request DRK to generate Service Key
	LOGD("createServiceKeySession: serviceName: %s | keyType: %d", (keyInfo->serviceName), KEY_TYPE_RSA);
	if ((wrappedKeyLen = createServiceKeySession((char *)(keyInfo->serviceName), KEY_TYPE_RSA, isEnableTlv, (char *)wrappedKey, sizeof(wrappedKey))) < NO_ERROR) {
		LOGE("Failed to create Key. Error = %d", ret);
		goto cleanUp;
	}
#ifdef USE_QSEE
	// Store ServiceKey via SFS
	if ((ret = kmSendCmdStoreServiceKey(wrappedKey, wrappedKeyLen, encryptedKey, &encryptedKeyLen)) != NO_ERROR) {
		LOGE("ServiceKey: Store ML Key failed");
		goto cleanUp;
	}
#ifndef USE_QSEE_WRAP_WITH_SFS
	if ((access(sk_key_dir, F_OK)) != NO_ERROR) {
		if((mkdir(sk_key_dir, S_IRWXU | S_IRGRP | S_IROTH)) != NO_ERROR) {
			LOGD("Can't make directory %s", sk_key_dir);
		}
	}
	if (writeKeyFile(sk_key_path, encryptedKey, encryptedKeyLen) != NO_ERROR) {
		LOGE("ServiceKey: Store ML Key failed");
		ret = WRITE_KEY_ERROR;
	}
	LOGD("csGenerateServiceKey: ServiceKey stored. Len = [%d]", encryptedKeyLen);
#endif
#else
	if ((access(sk_key_dir, F_OK)) != NO_ERROR) {
		if ((mkdir(sk_key_dir, S_IRWXU | S_IRGRP | S_IROTH)) != NO_ERROR) {
			LOGD("Can't make directory %s", sk_key_dir);
		}
	}
	if (writeKeyFile(sk_key_path, wrappedKey, wrappedKeyLen) != NO_ERROR) {
		LOGE("ServiceKey: Store ML Key failed");
		ret = WRITE_KEY_ERROR;
	}
	LOGD("csGenerateServiceKey: ServiceKey stored. Len = [%d]", wrappedKeyLen);
#endif

	ret = NO_ERROR;

cleanUp:
#ifdef USE_QSEE
	releaseServiceKeySession();
#endif
handleError:
	return ret;
}

int csVerifyServiceKey(uint8_t keyType, const struct KeyInfo *keyInfo){
	int32_t ret = 0;
	time_t currTime;
	struct tm *localTime;
	uint8_t wrappedKey[MAX_TRANSFER_SIZE] = {0};
	uint32_t wrappedKeyLen = sizeof(wrappedKey);

	//Get system time
	currTime = time(NULL);
	localTime = localtime (&currTime);
	localTime->tm_mon += 1;
#if !defined(USE_QSEE_WRAP_WITH_SFS)
	if ((ret = readKeyFile(sk_key_path, wrappedKey, &wrappedKeyLen)) != NO_ERROR) {
		LOGE("ServiceKey: Read ML Key failed");
		ret = READ_KEY_ERROR;
		goto cleanUp;
	}
#endif
	// Verify Service Key
	if ((ret = kmSendCmdVerifyServiceKey(wrappedKey, wrappedKeyLen, (uint8_t*)localTime, sizeof(struct tm))) != NO_ERROR) {
		LOGE("ServiceKey: kmVerifyCert failed");
		goto cleanUp;
	}

cleanUp:
	return ret;
}

int csReadKeyUID(uint8_t *out, uint32_t *outLen){
	int32_t ret = NO_ERROR;
	if ((ret = getDrkUID(KEY_TYPE_RSA, (char *)out, *outLen)) != NO_ERROR) {
		LOGE("Failed to get Service Key. Error = %d", ret);
	}
	return ret;
}