/*
 * =====================================================================================
 *
 *       Filename:  cryptoPlatform.c
 *
 *    Description:  Execute cryptograpical operation.
 *
 *        Version:  1.0
 *        Created:  03/28/2017 04:45:34 PM
 *       Compiler:  armcc
 *
 *         Author:  Dongwook Shim (), dw.shim@samsung.com
 *        Company:  Samsung Electronics
 *
 *        Copyright (c) 2017 by Samsung Electronics, All rights reserved.
 *
 * =====================================================================================
 */
#include "CommLayerData.h"
#include "commonConfig.h"
#include "taConfig.h"
#include "TLV.h"
#include "cryptoPlatform.h"
#include "certRevocationList.h"
#include "certParser.h"
#include "log.h"
#include "keyManager.h"
#include "secMemoryManager.h"
#include "teeCryptoApi.h"
#include "swbc_prov.h"
#include "x509v3.h"
#include "aes/aes.h"
#include "rsa/rsa.h"

#define AES256_KEY_SIZE     32
#define AES_GCM_IV_SIZE     12
#define PBKDF_ROUNDS        1025

#define GAK_BLOB_MAX_SIZE   (4096 * 4) // plain GAK Keybox element size : 13082 byte

// If you want to do self test, please activate below feature.
// #define DEBUG_SELF_TEST

int32_t unwrapGakBlob(const uint8_t* keyBlob, const uint32_t keyBlobLen, uint8_t* outData, uint32_t* outDataLen)
{
	int32_t ret = NOT_ERROR;
	uint8_t iv[IV_SIZE] = {0,};
	uint8_t encGakBlob[GAK_BLOB_MAX_SIZE] = {0,};
	uint8_t plainGakBlob[GAK_BLOB_MAX_SIZE] = {0,};
	uint8_t wrappedGakBlob[GAK_BLOB_MAX_SIZE] = {0,};
	uint32_t ivLen = 0, encGakBlobLen = 0, plainGakBlobLen = 0, wrappedGakBlobLen = GAK_BLOB_MAX_SIZE;
	Tlv_t *pTlv = (Tlv_t *)keyBlob;
	const SwbTableData_t swbGakDecTableData = {swbGakMc, swbGakDecTable,swbGakDecTableTag};

	if(keyBlob == NULL || keyBlobLen == 0 || outData == NULL || outDataLen == NULL)
	{
		LOGE("%s : Invalid agrument.", __func__);
		return ERR_TA_INVALID_ARGUMENT;
	}

	if(pTlv->tag != KEYBLOB_TAG_IV)
	{
		LOGE("Wrong tag(0x%X) inserted.", pTlv->tag);
		return ERR_TA_INVALID_BLOB;
	}

	ivLen = SEC_NTOHS(pTlv->dataLen);
	if(ivLen != sizeof(iv))
	{
		LOGE("Invalid IV length %d.", ivLen);
		return ERR_TA_INVALID_BLOB;
	}
	memcpy(iv, pTlv->data, sizeof(iv));

	pTlv = (Tlv_t *)(pTlv->data + ivLen);

	if(pTlv->tag != KEYBLOB_TAG_GAK)
	{
		LOGE("Wrong tag(0x%X) inserted.", pTlv->tag);
		ret = ERR_TA_INVALID_BLOB;
		goto cleanup;
	}

	encGakBlobLen = SEC_NTOHS(pTlv->dataLen);
	if(encGakBlobLen > GAK_BLOB_MAX_SIZE)
	{
		LOGE("Invalid GAK Blob length. %u.", encGakBlobLen);
		ret = ERR_TA_INVALID_BLOB;
		goto cleanup;
	}
	memcpy(encGakBlob, pTlv->data, encGakBlobLen);

	if((ret = swbAesCbcDecrypt(encGakBlob, encGakBlobLen, plainGakBlob, &plainGakBlobLen, iv, &swbGakDecTableData)) != NOT_ERROR)
	{
		LOGE("Failed to SWBC decryption with error %d.", ret);
		goto cleanup;
	}

	if((ret = createSecureObject((const uint8_t*)plainGakBlob, plainGakBlobLen, wrappedGakBlob, &wrappedGakBlobLen,
					getTaUid(TA_KEYMASTER), getTaUidLen(TA_KEYMASTER))) != NOT_ERROR)
	{
		LOGE("Failed to create Wrapped Object of GAK blob with error : %d.", ret);
		ret = ERR_PLATFORM_API_OPERATION_FAILED;
		goto cleanup;
	}

	if(wrappedGakBlobLen > *outDataLen)
	{
		LOGE("Buffer overflow detected. %u %u.", wrappedGakBlobLen, *outDataLen);
		ret = ERR_TA_BUFFER_OVERFLOW;
		goto cleanup;
	}

	memcpy(outData, wrappedGakBlob, wrappedGakBlobLen);
	*outDataLen = wrappedGakBlobLen;

cleanup:
	memset(iv, 0, sizeof(iv));
	memset(plainGakBlob, 0, sizeof(plainGakBlob));

	return ret;
}

static int32_t encryptCertificateSigningRequest(TeeAppId_t teeAppId, const uint8_t* csr, const uint32_t csrLen,
		uint8_t* outData, uint32_t* outDataLen)
{
	int32_t ret = NOT_ERROR;
	uint8_t aad[AES_BLOCK_SIZE], aesKey[AES256_KEY_SIZE], iv[IV_SIZE], authTag[AES_GCM_TAG_SIZE];
	uint8_t *pAesEncData = NULL, *pUnwrappedData = NULL;
	uint8_t wbEncAesKey[AES256_KEY_SIZE * 2];
	uint32_t wbEncAesKeyLen = sizeof(wbEncAesKey), unwrappedDataLen = csrLen;
	uint8_t tlvKeyBuf[RSA_BIT_SIZE_DEFAULT / 8], wrappedKeyData[RSA_BIT_SIZE_DEFAULT / 8];
	int32_t tlvKeyBufDataLen = 0;
#if (defined DEBUG_SELF_TEST)
	uint8_t *pAesDecData = NULL;
	uint8_t wbDecAesKey[AES256_KEY_SIZE];
	uint32_t wbDecAesKeyLen = sizeof(wbDecAesKey);
	const SwbTableData_t swbSakDecTableData = {swbSakMc, swbSakDecTable, swbSakDecTableTag};
#endif	// End of DEBUG_SELF_TEST
	KEY *pHsmRootCaKey = NULL;
	const SwbTableData_t swbSakEncTableData = {swbSakMc, swbSakEncTable, swbSakEncTableTag};

	if(csr == NULL || csrLen == 0 || csrLen > CSR_DATA_MAX_SIZE || outData == NULL || outDataLen == NULL)
	{
		LOGE("%s : Invalid agrument.", __func__);
		return ERR_TA_INVALID_ARGUMENT;
	}

	// Unwrap encrypted CSR data.
	if((pUnwrappedData = (uint8_t *)secMemoryManagerMalloc(csrLen)) == NULL)
	{
		LOGE("Failed to allocate memory.");
		return ERR_TA_NOT_ENOUGH_MEMORY;
	}

	memset(pUnwrappedData, 0, csrLen);

	if((ret = openSecureObject(csr, csrLen, pUnwrappedData, &unwrappedDataLen,
					getTaUid(teeAppId), getTaUidLen(teeAppId))) != NOT_ERROR)
	{
		LOGE("Failed to unwrap CSR with error %d.", ret);
		goto cleanup;
	}

	// Encrypt CSR with AES256-GCM.
	memset(aad, 0, sizeof(aad));
	memset(iv, 0, sizeof(iv));
	memset(aesKey, 0, sizeof(aesKey));
	memset(authTag, 0, sizeof(authTag));

	if((ret = getRandBlock(aad, sizeof(aad))) != sizeof(aad))
	{
		LOGE("Failed to generate AAD.");
		goto cleanup;
	}

	if((ret = getRandBlock(aesKey, sizeof(aesKey))) != sizeof(aesKey))
	{
		LOGE("Failed to generate AES key.");
		goto cleanup;
	}

	if((ret = getRandBlock(iv, sizeof(iv))) != sizeof(iv))
	{
		LOGE("Failed to generate IV.");
		goto cleanup;
	}

	if((pAesEncData = (uint8_t *)secMemoryManagerMalloc(unwrappedDataLen)) == NULL)
	{
		LOGE("Failed to allocate memory.");
		ret = ERR_TA_NOT_ENOUGH_MEMORY;
		goto cleanup;
	}

	memset(pAesEncData, 0, unwrappedDataLen);

	if((ret = encryptDataAES_GCM(pUnwrappedData, unwrappedDataLen, aesKey, sizeof(aesKey), iv, AES_GCM_IV_SIZE,
				aad, sizeof(aad), pAesEncData, authTag, sizeof(authTag))) != NOT_ERROR)
	{
		LOGE("AES256_GCM encryption is failed with error : %d", ret);
		goto cleanup;
	}

#if (defined DEBUG_SELF_TEST)
	// Verify data.
	if((pAesDecData = (uint8_t *)secMemoryManagerMalloc(unwrappedDataLen)) == NULL)
	{
		LOGE("Failed to allocate memory.");
		ret = ERR_TA_NOT_ENOUGH_MEMORY;
		goto cleanup;
	}

	memset(pAesDecData, 0, unwrappedDataLen);

	if((ret = decryptDataAES_GCM(pAesEncData, unwrappedDataLen, aesKey, sizeof(aesKey), iv, AES_GCM_IV_SIZE,
				aad, sizeof(aad), pAesDecData, authTag, sizeof(authTag))) != NOT_ERROR)
	{
		LOGE("AES256_GCM decryption is failed with error : %d", ret);
		goto cleanup;
	}

	if(memcmp(pUnwrappedData, pAesDecData, unwrappedDataLen))
	{
		LOGE("AES256_GCM data verification is failed.");
		ret = ERR_TA_VERIFICATION_FAILED;
		goto cleanup;
	}
#endif	// End of DEBUG_SELF_TEST

	LOGD("Encrypt AES key with WB.");

	memset(wbEncAesKey, 0, sizeof(wbEncAesKey));

	if((ret = swbAesCbcEncrypt(aesKey, sizeof(aesKey), wbEncAesKey, &wbEncAesKeyLen,
					iv, &swbSakEncTableData)) != NOT_ERROR)
	{
		LOGE("Failed to SWBC encryption with error %d.", ret);
		goto cleanup;
	}

#if (defined DEBUG_SELF_TEST)
	// Verify SWBC operation.
	memset(wbDecAesKey, 0, sizeof(wbDecAesKey));

	if((ret = swbAesCbcDecrypt(wbEncAesKey, wbEncAesKeyLen, wbDecAesKey, &wbDecAesKeyLen,
					iv, &swbSakDecTableData)) != NOT_ERROR)
	{
		LOGE("Failed to SWBC decryption with error %d.", ret);
		goto cleanup;
	}

	if(memcmp(aesKey, wbDecAesKey, sizeof(aesKey)))
	{
		LOGE("SWBC data verification is failed.");
		ret = ERR_TA_VERIFICATION_FAILED;
		goto cleanup;
	}
#endif	// End of DEBUG_SELF_TEST

	//  Make blob with ecrypted AES key and IV.
	memset(tlvKeyBuf, 0, sizeof(tlvKeyBuf));

	if((ret = tlvInit(tlvKeyBuf, sizeof(tlvKeyBuf))) != NOT_ERROR)
	{
		LOGE("Failed to init Tlv buffer with error %d.", ret);
		ret += ERR_TA_BASE;
		goto cleanup;
	}

	if((ret = tlvAdd(tlvKeyBuf, sizeof(tlvKeyBuf),
					TLV_WRAPPED_KEY, wbEncAesKey, wbEncAesKeyLen)) != NOT_ERROR)
	{
		LOGE("Failed to set Tlv buffer with error %d.", ret);
		ret += ERR_TA_BASE;
		goto cleanup;
	}

	if((ret = tlvAdd(tlvKeyBuf, sizeof(tlvKeyBuf), TLV_IV, iv, sizeof(iv))) != NOT_ERROR)
	{
		LOGE("Failed to set Tlv buffer with error %d.", ret);
		ret += ERR_TA_BASE;
		goto cleanup;
	}

	if((tlvKeyBufDataLen = tlvSize(tlvKeyBuf, sizeof(tlvKeyBuf))) > sizeof(wrappedKeyData) ||
			tlvKeyBufDataLen < 0)
	{
		LOGE("Tlv buffer overflow. %d %d", tlvKeyBufDataLen, sizeof(wrappedKeyData));
		ret = ERR_TA_BUFFER_OVERFLOW;
		goto cleanup;
	}

	switch(teeAppId)
	{
		case TA_SKM :
		case TA_KEYMASTER :
			// Encrypt CSR data with HSM pubkey.
			if((ret = getCAPublicKey(&pHsmRootCaKey, RSA_ENC_KEY)) != NOT_ERROR)
			{
				LOGE("Failed to get CA pubkey with error %d.", ret);
				goto cleanup;
			}

			if((ret = KEY_public_encrypt(pHsmRootCaKey, tlvKeyBufDataLen, tlvKeyBuf,
						wrappedKeyData, RSA_PKCS1_PADDING)) != NOT_ERROR)
			{
				LOGE("RSA encryption failed with error %d.", ret);
				goto cleanup;
			}
			break;

		default :
			LOGE("Not supported AppId : %d", teeAppId);
			ret = ERR_TA_INVALID_ARGUMENT;
			goto cleanup;
	}

	// Make ECSR blob.
	memset(outData, 0, *outDataLen);

	if((ret = tlvInit(outData, *outDataLen)) != NOT_ERROR)
	{
		LOGE("Failed to init Tlv buffer with error %d.", ret);
		ret += ERR_TA_BASE;
		goto cleanup;
	}

	if((ret = tlvAdd(outData, *outDataLen, TLV_ENCRYPTED_DATA_BLOB, pAesEncData, unwrappedDataLen)) != NOT_ERROR)
	{
		LOGE("Failed to set Tlv buffer with error %d.", ret);
		ret += ERR_TA_BASE;
		goto cleanup;
	}

	if((ret = tlvAdd(outData, *outDataLen, TLV_WRAPPED_KEY,
					wrappedKeyData, sizeof(wrappedKeyData))) != NOT_ERROR)
	{
		LOGE("Failed to set Tlv buffer with error %d.", ret);
		ret += ERR_TA_BASE;
		goto cleanup;
	}

	if((ret = tlvAdd(outData, *outDataLen, TLV_AUTH_DATA, aad, sizeof(aad))) != NOT_ERROR)
	{
		LOGE("Failed to set Tlv buffer with error %d.", ret);
		ret += ERR_TA_BASE;
		goto cleanup;
	}

	if((ret = tlvAdd(outData, *outDataLen, TLV_AUTH_TAG, authTag, sizeof(authTag))) != NOT_ERROR)
	{
		LOGE("Failed to set Tlv buffer with error %d.", ret);
		ret += ERR_TA_BASE;
		goto cleanup;
	}

	if((ret = tlvSize(outData, *outDataLen)) > *outDataLen || ret < 0)
	{
		LOGE("Tlv buffer overflow. %d %d", tlvKeyBufDataLen, sizeof(wrappedKeyData));
		ret = ERR_TA_BUFFER_OVERFLOW;
		goto cleanup;
	}

	// Update outDataLen to TlvBufferSize.
	*outDataLen = ret;

	ret = NOT_ERROR;

	LOGD("ECSR has been successfully generated.");

cleanup :
#if (defined DEBUG_SELF_TEST)
	if(pAesDecData)
	{
		memset(pAesDecData, 0, unwrappedDataLen);
		secMemoryManagerFree(pAesDecData);
	}
#endif	// End of DEBUG_SELF_TEST
	if(pAesEncData)
	{
		memset(pAesEncData, 0, unwrappedDataLen);
		secMemoryManagerFree(pAesEncData);
	}

	if(pHsmRootCaKey)
		KEY_free(pHsmRootCaKey);

	if(pUnwrappedData)
	{
		memset(pUnwrappedData, 0, csrLen);
		secMemoryManagerFree(pUnwrappedData);
	}

	return ret;
}

int32_t encryptDrkCertificateSigningRequest(const uint8_t* csr, const uint32_t csrLen, uint8_t* outData, uint32_t* outDataLen)
{
	return encryptCertificateSigningRequest(TA_SKM, csr, csrLen, outData, outDataLen);
}

int32_t encryptSakCertificateSigningRequest(const uint8_t* csr, const uint32_t csrLen, uint8_t* outData, uint32_t* outDataLen)
{
	return encryptCertificateSigningRequest(TA_KEYMASTER, csr, csrLen, outData, outDataLen);
}

#if (defined USE_QSEE) && (defined USE_QSEE_SFS)
int32_t getDrkV1ForSkm(uint8_t *outData, uint32_t *outDataLen)
{
	int32_t ret = NOT_ERROR;
	uint8_t plainBuf[MAX_SKM_BUF_SIZE];
	uint32_t plainBufLen = sizeof(plainBuf);

	if(outData == NULL || outDataLen == NULL)
	{
		LOGE("%s : Invalid agrument.", __func__);
		return ERR_TA_INVALID_ARGUMENT;
	}

	memset(plainBuf, 0, sizeof(plainBuf));

	if((ret = openLocalSecureObject(NULL, 0, plainBuf, &plainBufLen)) != NOT_ERROR)
	{
		LOGE("Failed to open secure object with error : %d.", ret);
		goto cleanup;
	}

	if((ret = createSecureObject(plainBuf, plainBufLen, outData, outDataLen, getTaUid(TA_SKM), getTaUidLen(TA_SKM))) != NOT_ERROR)
		LOGE("Failed to make secure object with error : %d.", ret);

cleanup :
	memset(plainBuf, 0, sizeof(plainBuf));
	return ret;
}
#endif // End of USE_QSEE && USE_QSEE_SFS

#if (defined DRK_TEST_API_ENABLED)
int32_t verifyServiceKey(const uint8_t *inData, const uint32_t inDataLen, uint8_t *outData, uint32_t *outDataLen)
{
	int32_t ret = NOT_ERROR;
	uint8_t plainBlob[MAX_SKM_BUF_SIZE];
	uint8_t *pDrkCert = NULL, *pServiceCert = NULL, *pPrivKey = NULL;
	uint32_t plainBlobLen = sizeof(plainBlob), drkCertLen = 0, serviceCertLen = 0, privKeyLen = 0;
	struct x509_certificate x509DrkCert, x509ServiceCert;
	KEY *pDrkKey = NULL, *pServiceKey = NULL;
	Tlv_t *pTlv = (Tlv_t *)plainBlob;

	LOGI("%s start...", __func__);

	if(inData == NULL || outData == NULL || outDataLen == NULL)
	{
		LOGE("%s : Invalid argument.", __func__);
		return ERR_TA_INVALID_ARGUMENT;
	}

	memset(plainBlob, 0, sizeof(plainBlob));

	if((ret = openSecureObject(inData, inDataLen, plainBlob, &plainBlobLen, getTaUid(TA_SKM), getTaUidLen(TA_SKM))) != NOT_ERROR)
	{
		LOGE("Failed to service key blob with error %d.", ret);
		goto cleanup;
	}

	if(pTlv->tag != KEYBLOB_TAG_CERT)
	{
		LOGE("Drk Cert tag is not matched - 0x%X.", pTlv->tag);
		ret = ERR_TA_INVALID_BLOB;
		goto cleanup;
	}

	drkCertLen = (uint32_t)pTlv->dataLen;
	pDrkCert = pTlv->data;
	pTlv = (Tlv_t *)(pTlv->data + pTlv->dataLen);

	if(pTlv->tag != KEYBLOB_TAG_CERT)
	{
		LOGE("Service Cert tag is not matched - 0x%X.", pTlv->tag);
		ret = ERR_TA_INVALID_BLOB;
		goto cleanup;
	}

	serviceCertLen = (uint32_t)pTlv->dataLen;
	pServiceCert = pTlv->data;
	pTlv = (Tlv_t *)(pTlv->data + pTlv->dataLen);

	if(pTlv->tag != KEYBLOB_TAG_PRIVATE_KEY)
	{
		LOGE("Key tag is not matched - 0x%X.", pTlv->tag);
		ret = ERR_TA_INVALID_BLOB;
		goto cleanup;
	}

	privKeyLen = (uint32_t)pTlv->dataLen;
	pPrivKey = pTlv->data;

	if(drkCertLen > MAX_CERT_LEN || serviceCertLen > MAX_CERT_LEN || privKeyLen > MAX_KEY_LEN ||
			drkCertLen + serviceCertLen + privKeyLen + 3 * TAGLENGTH_FIELD_SIZE > plainBlobLen ||
			pDrkCert < plainBlob || pServiceCert < plainBlob || pPrivKey < plainBlob ||
			pDrkCert > plainBlob + sizeof(plainBlob) || pServiceCert > plainBlob + sizeof(plainBlob) ||
			pPrivKey > plainBlob + sizeof(plainBlob))
	{
		LOGE("Parsed parameter value is invalid.");
		ret = ERR_TA_INVALID_BLOB;
		goto cleanup;
	}

	if((ret = verifyCertificateWithCA(pDrkCert, drkCertLen, &x509DrkCert)) != NOT_ERROR)
	{
		LOGE("Failed to verify device root key with error %d.", ret);
		goto cleanup;
	}

	if((pDrkKey = KEY_new(getCertPublicKeyType(&x509DrkCert))) == NULL)
	{
		LOGE("Failed to allocate service key structure.");
		ret = ERR_TA_NOT_ENOUGH_MEMORY;
		goto cleanup;
	}

	if((ret = KEY_populate_keys(pDrkKey, x509DrkCert.subject_public_key, 
					x509DrkCert.subject_public_key_len, NULL, 0)) != NOT_ERROR)
	{
		LOGE("Failed to populate key with error %d.", ret);
		goto cleanup;
	}

	if((ret = checkCertificate(pServiceCert, serviceCertLen, pDrkKey)) != NOT_ERROR)
	{
		LOGE("Failed to certificate verification with error %d.", ret);
		goto cleanup;
	}

	if((ret = x509_certificate_parse(pServiceCert, serviceCertLen, &x509ServiceCert)) != X509_PARSE_OK)
	{
		ret += ERR_TA_X509_PARSE_BASE;
		LOGE("Failed to parse X.509 certificate with error %d.", ret);
		goto cleanup;
	}

	if((pServiceKey = KEY_new(getCertPublicKeyType(&x509ServiceCert))) == NULL)
	{
		LOGE("Failed to allocate service key structure.");
		ret = ERR_TA_NOT_ENOUGH_MEMORY;
		goto cleanup;
	}

	if((ret = KEY_populate_keys(pServiceKey, x509ServiceCert.subject_public_key,
				x509ServiceCert.subject_public_key_len, pPrivKey, privKeyLen)) != NOT_ERROR)
	{
		LOGE("Failed to populate key with error %d.", ret);
		goto cleanup;
	}

	if((ret = KEY_check_keypair(pServiceKey)) != NOT_ERROR)
	{
		LOGE("Failed to verify key pair with error %d.", ret);
		goto cleanup;
	}

	memcpy(outData, pServiceCert, serviceCertLen);
	*outDataLen = serviceCertLen;

cleanup :
	if(pServiceKey)
		KEY_free(pServiceKey);

	if(pDrkKey)
		KEY_free(pDrkKey);

	memset(plainBlob, 0, sizeof(plainBlob));

	LOGI("%s end...", __func__);

	return ret;
}
#endif // End of DRK_TEST_API_ENABLED
