/**
 * \file LibDevKMApi.c
 * \brief Main API of SKM.
 * \author Dmytro Podgornyi (d.podgornyi@samsung.com)
 * \version 0.1
 * \date Created May 28, 2013
 * \par In Samsung Ukraine R&D Center (SURC) under a contract between
 * \par LLC "Samsung Electronics Ukraine Company" (Kiev, Ukraine) and
 * \par "Samsung Elecrtronics Co", Ltd (Seoul, Republic of Korea)
 * \par Copyright: (c) Samsung Electronics Co, Ltd 2012. All rights reserved.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <dirent.h>

#include "CommLayerData.h"
#include "TLV.h"
#include "OTAMLAPI.h"
#include "TAStartupCode.h"
#include "ServiceName.h"
#include "ServiceKey.h"
#include "Utils.h"
#include "log.h"
#include "version.h"
#ifndef USE_QSEE
#include <sys/stat.h>
#include <sys/mount.h>
#include <errno.h>
#endif

#include "MlDapApi.h"

#define VERSIONIZE(name) \
__attribute__((constructor)) void print_version_##name() \
{ \
	LOGI("================================================"); \
	LOGI(" %s v%s\n", #name, VERSION); \
	LOGI("================================================"); \
}

VERSIONIZE(MLDAP_LIB)

const char ml_apk_preload_path[] = "/system/app/MirrorLink.apk";
const char ml_apk_preload_name[] = "MirrorLink.apk";
const char download_apk_path[] = "/data/app/";
const char ml_apk_package[] = "com.sec.android.app.tmserver";
#if !defined(USE_QSEE_WRAP_WITH_SFS)

#ifdef USE_NEW_POS_PATH
const char drk_dir[] = "/mnt/vendor/efs/prov_data/dev_root/dev_root.dat";
#else
const char drk_dir[] = "/efs/prov_data/dev_root/dev_root.dat";
#endif /* USE_NEW_POS_PATH */

const char symm_key_path[] = "/efs/prov_data/dev_root/sym_key.dat";
const char ml_key_path[] = "/efs/prov_data/mldap/mldap.dat";
const char sd_key_path[] = "/efs/prov_data/sd/sd.dat";
const char sd_key_dir_path[] = "/efs/prov_data/sd/";

#else

const char drk_key_service_name[] = "DRK";     // "/efs/prov_data/dev_root/dev_root.dat"
const char ml_key_service_name [] = "MLDAP";   // "/efs/prov_data/mldap/mldap.dat"

int32_t getEncryptedTid(uint8_t* tid, uint32_t* tidLen)
{
	if (!tid || !tidLen) {
		LOGE("kmGetTid: Some argument is NULL");
		return WRONG_DATA;
	}

	return kmSendCmdInOut(GET_ENCRYPTED_TID, NULL, 0, tid, tidLen);
}

#endif

#if !defined(USE_QSEE_WRAP_WITH_SFS)
// Relatively random name of directory in /efs to match with qualcomm
char *PCR_FILEDIR  = "/efs/prov_data/pcr/";
char *PCR_FILENAME = "/efs/prov_data/pcr/pcr.dat";

// Creates directory (if it not exist)
int create_temp_file()
{
	int mkdir_status = mkdir(PCR_FILEDIR, 0700);
	return NO_ERROR;
}

int pcr_read(uint8_t* data, uint32_t* len)
{
	FILE *fPCR = NULL;
	struct stat fileInfo;
	if(stat(PCR_FILENAME, &fileInfo) < 0) {
		if(create_temp_file() != NO_ERROR) {
			return PLATFORM_INTERNAL_ERROR;
		}
	}

	fPCR = fopen(PCR_FILENAME, "rb");
	if (!fPCR) {
		LOGD("Failed to open PCR file, errno: %d", errno);
		LOGD("Assuming pcr_read is called first time");
		*len = 0;
		return NO_ERROR;
	}

	fseek(fPCR, 0, SEEK_END);
	long int total = ftell(fPCR);
	fseek(fPCR, 0, SEEK_SET);

	if (data == NULL || *len < total) {
		LOGE("Output buffer too small for PCR to be read");
		fclose(fPCR);
		return WRONG_DATA;
	}

	long int read = fread(data, 1, total, fPCR);
	if (read != total) {
		LOGE("IO error occured, errno: %d", errno);
		fclose(fPCR);
		return PLATFORM_INTERNAL_ERROR;
	}

	fclose(fPCR);
	*len = (uint32_t)read;
	return NO_ERROR;
}

int pcr_write(uint8_t* data, uint32_t dataLen)
{
	FILE* fPCR = fopen(PCR_FILENAME, "wb");
	if (!fPCR) {
		LOGE("IO error occured, errno: %d", errno);
		return PLATFORM_INTERNAL_ERROR;
	}

	size_t n = fwrite(data, sizeof(uint8_t), dataLen, fPCR);
	if (n != dataLen) {
		LOGE("IO error occured, errno: %d", errno);
		fclose(fPCR);
		return PLATFORM_INTERNAL_ERROR;
	}

	fclose(fPCR);
	return NO_ERROR;
}
#endif

/**
 * Check whether SKM already generated service key for MLDAP
 * If not, generate service key
 * @returns NO_ERROR if OK or error num.
 * @throws nothing.
 */
int GenerateServiceKey(void)
{
	int32_t res = NO_ERROR;
	uint8_t TID[MAX_TID_SIZE];
	uint32_t TIDLen = MAX_TID_SIZE ;// sizeof(TID);
	struct KeyInfo keyInfo;
	strncpy((char*)keyInfo.serviceName, "MLDAP", MAX_SERVICE_NAME);
	keyInfo.keyLen = 2048;
	keyInfo.crt = 1;

	res = csVerifyServiceKey(APCS_KEY, &keyInfo);
	if (NO_ERROR != res) {
#if defined(USE_ENCAPSULATED_TID) && defined(USE_QSEE)
		res = getEncryptedTid(TID, &TIDLen);
#else
		res = kmGetTid(TID, &TIDLen);
#endif
		if (NO_ERROR != res) {
			LOGE("GenerateServiceKey: Can not get TID");
			return res;
		}

		res = csGenerateServiceKey(RSA_KEY, &keyInfo, TID, TIDLen);
		if (NO_ERROR != res) {
			LOGE("GenerateServiceKey: Can not get generate service key");
			return res;
		}

		res = csVerifyServiceKey(APCS_KEY, &keyInfo);
		if (NO_ERROR != res) {
			LOGE("GenerateServiceKey: csVerifyServiceKey returned %d", res);
		}
	}
	return res;
}
/**
 * MLOtaInitialize will initialize the Secure Context.
 * @returns 0 if OK or error num.
 * @throws nothing.
 */
int MLOtaInitialize() {
	int32_t res = NO_ERROR;
	res = kmLockAndLoadTA();
	if (NO_ERROR != res) {
		LOGE("MLOtaInitialize failed\n");
	   return res;
	}
	LOGE("MLOtaInitialize PASS\n");
  return res;

}
/**
 * MLOtaDeInitialize will initialize the Secure Context.
 * @returns 0 if OK or error num.
 * @throws nothing.
 */
int MLOtaDeInitialize() {
	int32_t res = NO_ERROR;
	res = kmUnloadAndUnlockTA();
	if (NO_ERROR != res) {
		LOGE("MLOtaDeInitialize failed\n");
		return res;
	}
	LOGE("MLOtaDeInitialize PASS\n");
	return res;
}
/**
 * Create and return Session context. Fetch ML Server Device public key and UID.
 * @param *session OUT - Session context.
 * @param *otaMlPK OUT - OTA ML Public Key.
 * @param *otaMlPKLen IN/OUT - OTA ML Public Key Length.
 * @param *otaMlUid OUT - OTA ML UID (device ID).
 * @param *otaMlUidLen IN/OUT - OTA ML UID (device ID) Length.
 * @returns 0 if OK or error num.
 * @throws nothing.
 */
int MLOtaGetDevicePK(MLOtaSession* session, unsigned char* otaMlPK, unsigned int* otaMlPKLen, unsigned char* otaMlUid, unsigned int* otaMlUidLen)
{
	uint8_t buffer[MAX_TRANSFER_SIZE];
	uint32_t bufferLen = MAX_TRANSFER_SIZE ;// sizeof(buffer);
	uint8_t* ptr = buffer;
#if !defined(USE_QSEE_WRAP_WITH_SFS)
	uint8_t wrappedKey[MAX_TRANSFER_SIZE];
#endif

	int32_t res = NO_ERROR;

	if (!otaMlPK || !otaMlPKLen || !otaMlUid || !otaMlUidLen) {
		LOGE("Some argument is NULL");
		return WRONG_DATA;
	}

	res = kmSendCmdGetOtaSdPk(buffer, &bufferLen);
	if (res != NO_ERROR) {
		return res;
	}

	// Copy public key
	*otaMlPKLen = *(uint32_t*)ptr;
	LOGD("Public key size %d", *otaMlPKLen);
	if (*otaMlPKLen > MAX_TRANSFER_SIZE) {
		return WRONG_DATA;
	}
	ptr += sizeof(uint32_t);
	memcpy(otaMlPK, ptr, *otaMlPKLen);
	ptr += *otaMlPKLen;

#if !defined(USE_QSEE_WRAP_WITH_SFS)
	// Copy wrapped private key
	bufferLen -= (ptr - buffer);
	LOGD("Wrapped private key size %d", bufferLen);
	memcpy(wrappedKey, ptr, bufferLen);

	res = mkdirWrapper(sd_key_dir_path, 0755);
	if (res != NO_ERROR) {
		LOGD("Failed to make %s directory", sd_key_dir_path);
		return res;
	}
	if (writeKeyFile(sd_key_path, wrappedKey, bufferLen) != NO_ERROR) {
		return WRITE_KEY_ERROR;
	}
	sync();
#endif

	res = csReadKeyUID(otaMlUid, otaMlUidLen);
	return res;
}

/**
 * Sign response to CA Server.
 * @param *session IN - Session context.
 * @param *inData IN - Input data for signing.
 * @param *inDataLen IN - Length of Input data for signing.
 * @param *signature OUT - Signature of Input Data.
 * @param *signatureLen IN/OUT - Length of Signature of Input Data.
 * @returns 0 if OK or error num.
 * @throws nothing.
 */
int MLOtaSignRequest(MLOtaSession* session, unsigned char* inData, unsigned int* inDataLen, unsigned char* signature, unsigned int* signatureLen)
{
	int32_t res = NO_ERROR;
	uint8_t wrappedKey[MAX_TRANSFER_SIZE];
	uint32_t wrappedKeyLen = sizeof(wrappedKey);

	if (!inData || !inDataLen || !signature || !signatureLen) {
		LOGE("MLOtaSignRequest: Some argument is NULL");
		return WRONG_DATA;
	}

	res = GenerateServiceKey();
	if (res != NO_ERROR) {
		LOGE("MLOtaSignRequest: GenerateServiceKey returned %d", res);
		return res;
	}

#if defined(USE_QSEE_WRAP_WITH_SFS)
	wrappedKeyLen = 0;
#else
	res = readKeyFile(ml_key_path, wrappedKey, &wrappedKeyLen);
#endif
	if (NO_ERROR != res) {
		LOGE("MLOtaSignRequest: READ_KEY_ERROR");
		res = READ_KEY_ERROR;
		return res;
	}

	res = kmSendCmdSignOtaData(wrappedKey, wrappedKeyLen, inData, *inDataLen, signature, signatureLen);
	if (res != NO_ERROR) {
		LOGE("MLOtaSignRequest: kmSendCmdSignOtaData() failed");
	}

	return res;
}

/**
 * Save retrieved ML Server Device / Manufacturer certificates.
 * @param *session IN - Session context.
 * @param *certMlSD IN - MlSD Certificate.
 * @param *certMlSDLen IN - Length of MlSD Certificate.
 * @param *certMlSM IN - MlSM Certificate.
 * @param *certMlSMLen IN - Length of MlSM Certificate.
 * @returns 0 if OK or error num.
 * @throws nothing.
 */
int MLOtaSaveDeviceCert(MLOtaSession* session, unsigned char* certMlSD, unsigned int* certMlSDLen, unsigned char* certMlSM0, unsigned int* certMlSM0Len, unsigned char* certMlSM1, unsigned int* certMlSM1Len)
{
	int32_t res = NO_ERROR;
	uint8_t wrappedKey[MAX_TRANSFER_SIZE];
	uint32_t wrappedKeyLen = 0;
	 uint8_t keyBlob[MAX_TRANSFER_SIZE];
	uint32_t keyBlobLen = sizeof(keyBlob);
	DIR* dir;

	if (!certMlSD || !certMlSDLen || !certMlSM0 || !certMlSM0Len || !certMlSM1 || !certMlSM1Len) {
		LOGE("MLOtaSaveDeviceCert: Some argument is NULL");
		return WRONG_DATA;
	}

#if !defined(USE_QSEE_WRAP_WITH_SFS)
	wrappedKeyLen = sizeof(wrappedKey);
	dir = opendir( sd_key_dir_path );
	if (dir) {
		/* Directory exists. */
		closedir(dir);
	} else {
		if (mkdir(sd_key_dir_path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) {
			LOGE("Can't create directory %s", sd_key_dir_path);
			res = WRONG_DATA;
		}
	}
	if (res == NO_ERROR && readKeyFile(sd_key_path, wrappedKey, &wrappedKeyLen) != NO_ERROR) {
		LOGE("MLOtaSaveDeviceCert: readKeyFile failed");
		return READ_KEY_ERROR;
	}
#endif

	res = kmSendCmdSaveOtaCerts(certMlSD, *certMlSDLen, certMlSM0, *certMlSM0Len, certMlSM1, *certMlSM1Len, wrappedKey, wrappedKeyLen, keyBlob, &keyBlobLen);
	if (res != NO_ERROR) {
		LOGE("MLOtaSaveDeviceCert: kmSendCmdSaveOtaCerts failed");
		return res;
	}

#if !defined(USE_QSEE_WRAP_WITH_SFS)
	if (writeKeyFile(sd_key_path, keyBlob, keyBlobLen) != NO_ERROR) {
		LOGE("MLOtaSaveDeviceCert: writeKeyFile failed");
		res = WRITE_KEY_ERROR;
	}
#endif
	return res;
}

/**
 * Check ML Server Device certificate presence, validity and status.
 * @returns 0 if OK or error num.
 * @throws nothing.
 */
int MLOtaCheckDeviceCertificate(void)
{
	int32_t res = NO_ERROR;
	uint8_t wrappedKey[MAX_TRANSFER_SIZE];
	uint32_t wrappedKeyLen = 0;
	time_t currTime;
	struct tm *localTime;

	/* Get system time */
	currTime = time(NULL);
	localTime = localtime (&currTime);
	localTime->tm_mon += 1;

	LOGE("Local time = %d:%d:%d  %d:%d:%d",
		localTime->tm_hour,
		localTime->tm_min,
		localTime->tm_sec,
		localTime->tm_mon,
		localTime->tm_mday,
		localTime->tm_year + 1900);

#if !defined(USE_QSEE_WRAP_WITH_SFS)
	wrappedKeyLen = sizeof(wrappedKey);
	if (readKeyFile(sd_key_path, wrappedKey, &wrappedKeyLen ) != NO_ERROR) {
		LOGE("MLOtaCheckDeviceCertificate: readKeyFile failed");
		res = READ_KEY_ERROR;
		goto cleanup;
	}
#endif
	res = kmVerifySDCert(wrappedKey, wrappedKeyLen, (uint8_t*)localTime, sizeof(struct tm));
	if (res != NO_ERROR) {
		LOGE("MLOtaCheckDeviceCertificate: kmVerifySDCert failed");
		goto cleanup;
	}

cleanup:
	return res;
}

/**
 * Get retrieved ML Device Certificate.
 * @param *MLOtaDeviceCert IN/OUT - Buffer for ML Device Certificate.
 * @param *MLOtaDeviceCertLen OUT - Length of Buffer for ML Device Certificate.
 * @returns 0 if OK or error num.
 * @throws nothing.
 */
int MLOtaGetDeviceCert(void* MLOtaDeviceCert, unsigned int* MLOtaDeviceCertLen)
{
	int32_t res = NO_ERROR;
	uint8_t wrappedKey[MAX_TRANSFER_SIZE];
	uint32_t wrappedKeyLen = sizeof(wrappedKey);

	if (!MLOtaDeviceCert || !MLOtaDeviceCertLen) {
		LOGE("Some argument is NULL");
		return WRONG_DATA;
	}

	res = GenerateServiceKey();
	if (res != NO_ERROR) {
		LOGE("MLOtaSignRequest: GenerateServiceKey returned %d", res);
		return res;
	}

#if defined(USE_QSEE_WRAP_WITH_SFS)
	wrappedKeyLen = 0;
#else
	res = readKeyFile( ml_key_path, wrappedKey, &wrappedKeyLen );
#endif
	if (NO_ERROR != res) {
		return READ_KEY_ERROR;
	}

	res = kmSendCmdGetOtaCertWithKey(CERT_ML, MLOtaDeviceCert, MLOtaDeviceCertLen, wrappedKey, wrappedKeyLen);
	if (res != NO_ERROR) {
		LOGE("MLOtaGetDeviceCert: kmSendCmdGetOtaCertWithKey() failed");
	}
	return res;
}

/**
 * Get retrieved ML DRK Certificate.
 * @param *MLOtaDRKCert IN/OUT - Buffer for ML DRK Certificate.
 * @param *MLOtaDRKCertLen OUT - Length of Buffer for ML DRK Certificate.
 * @returns 0 if OK or error num.
 * @throws nothing.
 */
int MLOtaGetDRKCert(void* MLOtaDRKCert, unsigned int* MLOtaDRKCertLen)
{
	int32_t res = NO_ERROR;
	uint8_t wrappedKey[MAX_TRANSFER_SIZE];
	uint32_t wrappedKeyLen = sizeof(wrappedKey);

	if (!MLOtaDRKCert || !MLOtaDRKCertLen) {
		LOGE("Some argument is NULL");
		return WRONG_DATA;
	}

	res = GenerateServiceKey();
	if (res != NO_ERROR) {
		LOGE("MLOtaSignRequest: GenerateServiceKey returned %d", res);
		return res;
	}

#if defined(USE_QSEE_WRAP_WITH_SFS)
	wrappedKeyLen = 0;
#else
	res = readKeyFile(ml_key_path, wrappedKey, &wrappedKeyLen);
#endif
	if (NO_ERROR != res) {
		return READ_KEY_ERROR;
	}

	res = kmSendCmdGetOtaCertWithKey(CERT_DRK, MLOtaDRKCert, MLOtaDRKCertLen, wrappedKey, wrappedKeyLen);
	if (res != NO_ERROR) {
		LOGE("MLOtaGetDRKCert: kmSendCmdGetOtaCertWithKey() failed");
	}
	return res;
}

/**
 * Removes /efs/prov_data/sd/sd.dat
 * @returns 0 if OK or error num.
 * @throws nothing.
 */
int MLOtaDeleteDeviceKeyAndCert()
{
	int32_t res = NO_ERROR;

#ifndef USE_QSEE
	res = remove(sd_key_path);     // make directory emty
	if (res != 0) {
		LOGE("remove(sd_key_path) failed, res=%d", res);
	}
	res = remove(sd_key_dir_path); // remove directory
	if (res != 0) {
		LOGE("remove(sd_key_dir_path) failed, res=%d", res);
	}
#else
	res = kmSendCmd(OTA_DELETE_SD_FILE, NULL, 0);
	if (res != NO_ERROR) {
		LOGE("MLOtaDeleteDeviceKeyAndCert: kmSendCmd() failed");
	}
#endif

	return res;
}

/********************* MLDAP DAP API *************************/

/* Fetches 20 Byte PCR buffer */
MlDapApiError_t getPcr(uint8_t* buf, uint32_t* bufLen)
{
	int ret;
#if !defined(USE_QSEE_WRAP_WITH_SFS)
	uint8_t tmp[MAX_FILE_PATH_LEN];
	uint32_t tmpLen = sizeof(tmp);
#endif


#if !defined(USE_QSEE_WRAP_WITH_SFS)
	if (pcr_read(tmp, &tmpLen) != NO_ERROR) {
		LOGE("Failed to read PCR data");
		return MLDAPAPIERR_PCR_ERROR;
	}
	ret = kmSendCmdInOut(DAP_PCR_READ, tmp, tmpLen, buf, bufLen);
#else /* USE_QSEE */
	ret = kmSendCmdInOut(DAP_PCR_READ, NULL, 0, buf, bufLen);
#endif

	if (ret != NO_ERROR) {
		return MLDAPAPIERR_PCR_ERROR;
	}

	return MLDAPAPIERR_NO_ERROR;
}

/* Extend the PCR buffer using input data and read new PCR buffer to buf */
MlDapApiError_t extendPcr(uint8_t* inData, uint32_t inLen, uint8_t* buf, uint32_t* bufLen)
{
	MlDapApiError_t ret = MLDAPAPIERR_PCR_ERROR;

#if !defined(USE_QSEE_WRAP_WITH_SFS)
	uint8_t tmp[MAX_TRANSFER_SIZE];
	uint32_t tmpLen = sizeof(tmp);
	uint8_t tlv[MAX_TRANSFER_SIZE];
	uint32_t tlvLen = sizeof(tlv);
	uint8_t TID[MAX_TID_SIZE];
	uint32_t TIDLen = sizeof(TID);
#endif

#if !defined(USE_QSEE_WRAP_WITH_SFS)
	if (pcr_read(tmp, &tmpLen) != NO_ERROR) {
		LOGE("pcr_read(tmp, &tmpLen) error");
		goto cleanup;
	}

	if (kmGetTid(TID, &TIDLen) != NO_ERROR) {
		LOGE("kmGetTid(TID, &TIDLen) error");
		goto cleanup;
	}

	if (tlvInit(tlv, tlvLen) != NO_ERROR) {
		LOGE("tlvInit(ptr, tlvLen) error");
		goto cleanup;
	}

	if (tlvAdd(tlv, tlvLen, TLV_WRAPPED_PCR, tmp, tmpLen) != NO_ERROR) {
		LOGE("tlvAdd(ptr, tlvLen, TLV_SIGN_DATA_BLOB, data, dataLen) error");
		goto cleanup;
	}

	if (tlvAdd(tlv, tlvLen, TLV_EXTEND_PCR_DATA, inData, inLen) != NO_ERROR) {
		LOGE("tlvAdd(ptr, tlvLen, TLV_SIGN_DATA_BLOB, data, dataLen) error");
		goto cleanup;
	}

	if (tlvAdd(tlv, tlvLen, TLV_TID, TID, TIDLen) != NO_ERROR) {
		LOGE("tlvAdd(tlv, tlvLen, TLV_TID, TID, TIDLen) error");
		goto cleanup;
	}

	tlvLen = tlvSize(tlv, tlvLen);

	tmpLen = sizeof(tmp);
	if (kmSendCmdInOut(DAP_PCR_EXTEND, tlv, tlvLen, tmp, &tmpLen) != NO_ERROR) {
		LOGE("kmSendCmdInOut(DAP_PCR_EXTEND, tlv, tlvLen, tmp, &tmpLen) error");
		goto cleanup;
	}

	if (pcr_write(tmp, tmpLen) != NO_ERROR) {
		LOGE("pcr_write(tmp, tmpLen) error");
		goto cleanup;
	}

#else
	if (kmSendCmdInOut(DAP_PCR_EXTEND, inData, inLen, NULL, 0) != NO_ERROR) {
		goto cleanup;
	}
#endif

	ret = MLDAPAPIERR_NO_ERROR;

cleanup:

	if (ret == MLDAPAPIERR_NO_ERROR) {
		ret = getPcr(buf, bufLen);
	}

	return ret;
}

MlDapApiError_t getDeviceCertificate(uint8_t* buf, uint32_t* bufLen)
{
	int32_t res = MLDAPAPIERR_NO_ERROR;
	uint8_t wrappedKey[MAX_TRANSFER_SIZE];
	uint32_t wrappedKeyLen = 0;

	if (!buf || !bufLen) {
		LOGE("Some argument is NULL");
		return MLDAPAPIERR_BUFFER_TOO_SMALL;
	}

#if !defined(USE_QSEE_WRAP_WITH_SFS)
	wrappedKeyLen = sizeof(wrappedKey);
	if (readKeyFile( sd_key_path, wrappedKey, &wrappedKeyLen ) != NO_ERROR ) {
		res = MLDAPAPIERR_READ_KEY_ERROR;
		goto cleanup;
	}
#endif
	res = kmSendCmdGetOtaCertWithKey(CERT_SD, buf, bufLen, wrappedKey, wrappedKeyLen);

	if (res != NO_ERROR) {
		res = MLDAPAPIERR_DECODE_ERROR;
		goto cleanup;
	}

cleanup:
	return res;
}


MlDapApiError_t getManufactureCertificate(uint8_t* certSM0, uint32_t* certSM0Len, uint8_t* certSM1, uint32_t* certSM1Len)
{
	int32_t res = MLDAPAPIERR_NO_ERROR;
	uint8_t wrappedKey[MAX_TRANSFER_SIZE];
	uint32_t wrappedKeyLen = 0;

	if (!certSM0 || !certSM0Len || !certSM1 || !certSM1Len) {
		LOGE("Some argument is NULL");
		return MLDAPAPIERR_BUFFER_TOO_SMALL;
	}

#if !defined(USE_QSEE_WRAP_WITH_SFS)
	wrappedKeyLen = sizeof(wrappedKey);
	if (readKeyFile( sd_key_path, wrappedKey, &wrappedKeyLen ) != NO_ERROR) {
		res = MLDAPAPIERR_READ_KEY_ERROR;
		goto cleanup;
	}
#endif
	res = kmSendCmdGetOtaCertWithKey(CERT_SM0, certSM0, certSM0Len, wrappedKey, wrappedKeyLen);

	if (res != NO_ERROR) {
		res = MLDAPAPIERR_DECODE_ERROR;
		goto cleanup;
	}

	res = kmSendCmdGetOtaCertWithKey(CERT_SM1, certSM1, certSM1Len, wrappedKey, wrappedKeyLen);

	if (res != NO_ERROR) {
		res = MLDAPAPIERR_DECODE_ERROR;
		goto cleanup;
	}

cleanup:
	return res;
}

MlDapApiError_t generateSignature(uint8_t* inData, uint32_t inLen, uint8_t* buf, uint32_t* bufLen)
{
	int32_t res = MLDAPAPIERR_NO_ERROR;
	uint8_t wrappedKey[MAX_TRANSFER_SIZE];
	uint32_t wrappedKeyLen = 0;

	if (!inData || !inLen || !buf || !bufLen) {
		LOGE("Some argument is NULL");
		return WRONG_DATA;
	}

#if !defined(USE_QSEE_WRAP_WITH_SFS)
	wrappedKeyLen = sizeof(wrappedKey);
	if (readKeyFile(sd_key_path, wrappedKey, &wrappedKeyLen) != NO_ERROR) {
		res = MLDAPAPIERR_READ_KEY_ERROR;
	}
#endif
	if (res == NO_ERROR) {
		res = kmSendCmdSignDapData(wrappedKey, wrappedKeyLen, inData, inLen, buf, bufLen);
	}

	return res;
}

int getOemFlag(void)
{
	int32_t res = NO_ERROR;

	res = kmSendCmd(DAP_GET_OEM_FLAG, NULL, 0);

	if (res != NO_ERROR) {
		LOGE("getOemFlag: kmSendCmd() failed");
	}

	return res;
}

int apkFilter(const struct dirent *p)
{
	if (strstr(p->d_name, ml_apk_package))
		return 1;

	return 0;
}

int getApkPath(uint8_t *filePath, uint8_t *fileName)
{
	int32_t res = NO_ERROR;
	int filecount = 0;
	struct dirent **namelist;

	if ((filecount = scandir(download_apk_path, &namelist, apkFilter, alphasort)) <= 0) {
		if (access(ml_apk_preload_path, 0) == 0) {
			sprintf((char *)filePath, "%s", ml_apk_preload_path);
			sprintf((char *)fileName, "%s", ml_apk_preload_name);
		} else {
			res = INTEGRITY_ERROR;
		}
	} else {
		//LOGE("filecount-1 : %d : %s", filecount-1, namelist[filecount-1]->d_name);
		sprintf((char *)filePath, "%s%s", download_apk_path, namelist[filecount-1]->d_name);
		sprintf((char *)fileName, "%s", namelist[filecount-1]->d_name);
	}

	return res;
}

int checkServicekey(void)
{
	LOGI("MldapChecker keystring app called : checkServicekey()");
	int32_t res = NO_ERROR;
	struct KeyInfo keyInfo;
	strncpy((char*)keyInfo.serviceName, "MLDAP", MAX_SERVICE_NAME);
	keyInfo.keyLen = 2048;
	keyInfo.crt = 1;

	res = csVerifyServiceKey(APCS_KEY, &keyInfo);

	return res;
}

int checkCert(void)
{
	int res=NO_ERROR;

	LOGI("MldapChecker keystring app called : checkCert()");
	res = MLOtaCheckDeviceCertificate();

	return res;
}