/*
 * This file must contain crypto functions of engineering mode sources.
 * Internal functions for use only by engineering mode module.
 */
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include "em_common.h"
#include "crypto/em_crypto_cert.h"

#include "openssl/err.h"
#include "openssl/evp.h"
#include "openssl/hmac.h"
#include "openssl/rsa.h"
#include "openssl/x509.h"
#include "openssl/err.h"
#include "openssl/crypto.h"

static EVP_PKEY *server_pubkey = NULL;
/**
 * Function : em_crypto_verify_cert()
 * Description : Verify cert with root CA public key
 * Parameters : @ cert : raw cert.
 *              @ len_cert : cert size.
 *              @ cert_type : cert type.
 * Return value : VERIFICATION_SUCCESS(0) if it is successful, or error value.
 */
static int em_crypto_verify_cert(const uint8_t *cert, const int len_cert, int *cert_type)
{
	int ret;

	EVP_PKEY *root_pubkey = NULL;
	EVP_PKEY *ca_pubkey = NULL;
	EVP_PKEY *dev_pubkey = NULL;

	X509 *server_cert = NULL;
	RSA *root_rsaPubKey = NULL;
	RSA *dev_rsaPubKey = NULL;

	const unsigned char *pCa_pubkey = ss_ca_pubkey;
	const unsigned char *pDev_pubkey = (unsigned char *)dev_em_pubkey;
	const unsigned char *pCert = cert;

	char cn[EM_LEN_CN] = {0,};

	EM_CHECK_NULL(__func__, EM_ERR_EM_CRYPTO_VERIFY_CERT, cert);

	if (len_cert <= 0 || len_cert > EM_LEN_CERTIFICATE) {
		LOGE("Cert len isn't normal(%u)\n", len_cert);
		ret = EM_ERR_EM_CRYPTO_VERIFY_CERT_LEN;
		goto out;
	}

	// Read root CA public key.
	if ((root_rsaPubKey = d2i_RSA_PUBKEY(NULL, &pCa_pubkey, sizeof(ss_ca_pubkey))) == NULL) {
		LOGE("Failed to read root CA pubkey. : %u\n", ERR_get_error());
		ret = EM_ERR_EM_CRYPTO_VERIFY_CERT_READ_CA_PUBKEY;
		goto out;
	}

	// Allocate memory for root key.
	if ((root_pubkey = EVP_PKEY_new()) == NULL) {
		LOGE("Failed to initialize root pubkey. : %u\n", ERR_get_error());
		ret = EM_ERR_EM_CRYPTO_VERIFY_CERT_MEMORY_PUB_KEY;
		goto out;
	}

	// Set RSA property.
	if (EVP_PKEY_set1_RSA(root_pubkey, root_rsaPubKey) != 1) {
		LOGE("Failed to set property for root pubkey. : %u\n", ERR_get_error());
		ret = EM_ERR_EM_CRYPTO_VERIFY_CERT_SET_RSA_PROP;
		goto out;
	}

	ca_pubkey = root_pubkey;

	// Read server cert.
	if ((server_cert = d2i_X509(NULL, &pCert, len_cert)) == NULL) {
		LOGE("Failed to read server cert. : %u %p\n", ERR_get_error(), pCert);
		ret = EM_ERR_EM_CRYPTO_VERIFY_CERT_SERVER_CERT;
		goto out;
	}

	switch (X509_verify(server_cert, ca_pubkey)) {
	case EM_OPENSSL_SUCCESS:
		LOGI("Verifing cert is success\n");
		break;

	case EM_OPENSSL_FAILED:
		LOGE("Failed to X509_verify(EM_OPENSSL_FAILED)\n");
		ret = EM_ERR_EM_CRYPTO_VERIFY_CERT_FAILED_VERIFY;
		{
			LOGI("Check one more\n");
			pDev_pubkey = (const unsigned char *)dev_em_pubkey;
			// Read root CA public key.
			if ((dev_rsaPubKey = d2i_RSA_PUBKEY(NULL, &pDev_pubkey, sizeof(dev_em_pubkey))) == NULL) {
				LOGE("Failed to read Dev CA pubkey. : %u\n", ERR_get_error());
				ret = EM_ERR_EM_CRYPTO_VERIFY_DCERT_READ_CA_PUBKEY;
				goto out;
			}

			// Allocate memory for root key.
			if ((dev_pubkey = EVP_PKEY_new()) == NULL) {
				LOGE("Failed to initialize dev pubkey. : %u\n", ERR_get_error());
				ret = EM_ERR_EM_CRYPTO_VERIFY_DCERT_MEMORY_PUB_KEY;
				goto out;
			}

			// Set RSA property.
			if (EVP_PKEY_set1_RSA(dev_pubkey, dev_rsaPubKey) != 1) {
				LOGE("Failed to set property for dev pubkey. : %u\n", ERR_get_error());
				ret = EM_ERR_EM_CRYPTO_VERIFY_DCERT_SET_RSA_PROP;
				goto out;
			}

			switch (X509_verify(server_cert, dev_pubkey)) {
			case EM_OPENSSL_SUCCESS:
				LOGI("DEV Cert verify success\n");
				*cert_type = EM_TYPE_DEV_CERT;
				break;
			default:
				LOGE("Failed to verifty DEV Cert\n");
				ret = EM_ERR_EM_CRYPTO_VERIFY_CERT_FAILED_VERIFY;
				goto out;
			}
		}
		break;
	default:
		LOGE("Failed to verify cert. : %s\n", ERR_error_string(ERR_get_error(), NULL));
		ret = EM_ERR_EM_CRYPTO_VERIFY_CERT_FAILED_UNKNOWN;
		goto out;
	}

	ret = em_crypto_get_subject_from_cert(cert, len_cert, EM_CERT_SUBJECT_CN, cn, sizeof(cn));
	if (ret != EM_SUCCESS) {
		LOGE("Failed to get CN from cert(0x%08x)\n", ret);
		goto out;
	}

	if (memcmp(EM_CERT_SUBJECT_CNV, cn, strlen(EM_CERT_SUBJECT_CNV)) != 0) {
		LOGE("Cert isn't EM cert(%s)\n", cn);
		ret = EM_ERR_EM_CRYPTO_VERIFY_CERT_INVALID_CN;
		goto out;
	}

	// Read server cert's public key.
	if ((server_pubkey = X509_get_pubkey(server_cert)) == NULL) {
		LOGE("Failed to read server pubkey. : %u\n", ERR_get_error());
		ret = EM_ERR_EM_CRYPTO_VERIFY_CERT_READ_SERVER_KEY;
		goto out;
	}

	ret = EM_SUCCESS;
 out:
	if (root_pubkey) {
		EVP_PKEY_free(root_pubkey);
		root_pubkey = NULL;
	}

	if (dev_pubkey) {
		EVP_PKEY_free(dev_pubkey);
		dev_pubkey = NULL;
	}

	if (root_rsaPubKey) {
		RSA_free(root_rsaPubKey);
		root_rsaPubKey = NULL;
	}

	if (dev_rsaPubKey) {
		RSA_free(dev_rsaPubKey);
		dev_rsaPubKey = NULL;
	}

	if (server_cert) {
		X509_free(server_cert);
		server_cert = NULL;
	}

	return ret;
}

/**
 * Function : em_crypto_verify_signature()
 * Description : Verify signature with digest. Digest is SHA256 and RSA padding is PSS.
 * Parameters : @ signature : signature data.
 *              @ signaturelen : sagnature length.
 *              @ md : digest of verification data.
 *              @ md_len : digest length.
 * Return value : EM_SUCCESS(0) if it is successful, or error value.
 */
static int em_crypto_verify_signature(const uint8_t *signature, const int len_signature, const uint8_t *md,
				      const int len_md)
{
	int ret;
	RSA *pRsaKey = NULL;
	uint8_t dec_md[256] = {};

	EM_CHECK_NULL(__func__, EM_ERR_EM_CRYPTO_VERIFY_SIGN, signature, md);

	if (len_signature <= 0) {
		LOGE("len_signature isn't normal(%u)\n", len_signature);
		ret = EM_ERR_EM_CRYPTO_VERIFY_SIGN_SIGNATURE_LEN;
		goto out;
	}

	if (len_md <= 0) {
		LOGE("len_md isn't normal(%u)\n", len_md);
		ret = EM_ERR_EM_CRYPTO_VERIFY_SIGN_MD_LEN;
		goto out;
	}

	if ((pRsaKey = EVP_PKEY_get1_RSA(server_pubkey)) == NULL) {
		LOGE("Failed to get RSA key : %s\n", ERR_error_string(ERR_get_error(), NULL));
		ret = EM_ERR_EM_CRYPTO_VERIFY_SIGN_GET_RSA_KEY;
		goto out;
	}

	ret = RSA_check_key(pRsaKey);
	if (ret == -1) {
		LOGE("Invaild RSA key : %s\n", ERR_error_string(ERR_get_error(), NULL));
		ret = EM_ERR_EM_CRYPTO_VERIFY_SIGN_CHECK_RSA_KEY;
		goto out;
	}

	if (len_md != RSA_public_decrypt(len_signature, signature, dec_md, pRsaKey, RSA_PKCS1_PADDING)) {
		LOGE("Failed to decrypt : %s\n", ERR_error_string(ERR_get_error(), NULL));
		ret = EM_ERR_EM_CRYPTO_VERIFY_SIGN_RSA_DECRYPT;
		goto out;
	}

	if (memcmp(md, dec_md, len_md) != 0) {
		LOGE("Failed to verify signature\n");
		ret = EM_ERR_EM_CRYPTO_VERIFY_SIGN_NOT_MATCH_SIGN;
		goto out;
	}

	ret = EM_SUCCESS;
out:
	if (pRsaKey) {
		RSA_free(pRsaKey);
		pRsaKey = NULL;
	}

	return ret;
}

int em_crypto_hmac(unsigned char *hmac, const unsigned char *data, const unsigned int data_len,
		   const unsigned char *key, const unsigned int key_len)
{
	int ret;
	unsigned int hmac_len = EM_LEN_HMAC;

	EM_CHECK_NULL(__func__, EM_ERR_EM_CRYPTO_HMAC, hmac, data, key);

	if (!HMAC(EVP_sha256(), key, key_len, data, data_len, hmac, &hmac_len)) {
		LOGE("HMAC error(%s)\n", ERR_error_string(ERR_get_error(), NULL));
		ret = EM_ERR_EM_CRYPTO_HMAC_FUNC;
		goto out;
	}

	ret = EM_SUCCESS;
out:
	return ret;
}

int em_crypto_sha256(const unsigned char *data, const unsigned int data_len, unsigned char *digest)
{
	int ret;
	unsigned int digest_len = EM_LEN_SHA256;
	EVP_MD_CTX *pMdCtx = NULL;

	EM_CHECK_NULL(__func__, EM_ERR_EM_CRYPTO_SHA256, data, digest);

	if ((pMdCtx = EVP_MD_CTX_create()) == NULL) {
		LOGE("Failed EVP_MD_CTX_create\n");
		ret = EM_ERR_EM_CRYPTO_SHA256_CONTEXT;
		goto out;
	}

	if (!EVP_DigestInit_ex(pMdCtx, EVP_sha256(), NULL)) {
		LOGE("Failed EVP_DigestInit_ex\n");
		ret = EM_ERR_EM_CRYPTO_SHA256_DIGEST_INIT;
		goto out;
	}

	if (!EVP_DigestUpdate(pMdCtx, data, data_len)) {
		LOGE("Failed EVP_DigestUpdate\n");
		ret = EM_ERR_EM_CRYPTO_SHA256_DIGEST_UPDATE;
		goto out;
	}

	if (!EVP_DigestFinal_ex(pMdCtx, digest, &digest_len)) {
		LOGE("Failed EVP_DigestFinal_ex\n");
		ret = EM_ERR_EM_CRYPTO_SHA256_DIGEST_FINAL;
		goto out;
	}

	ret = EM_SUCCESS;
out:
	if (pMdCtx) {
		EVP_MD_CTX_destroy(pMdCtx);
		pMdCtx = NULL;
	}

	return ret;
}

int em_crypto_aes_256_gcm_encrypt(const unsigned char *plaintext, const unsigned int len_plaintext,
				  unsigned char *ciphertext, unsigned int *len_ciphertext, const unsigned char *key,
				  const unsigned int len_key, const unsigned char *iv, const unsigned int len_iv,
				  unsigned char *tag, unsigned int len_tag)
{
	EVP_CIPHER_CTX *ctx = NULL;
	int len;
	int ret;

	EM_CHECK_NULL(__func__, EM_ERR_EM_CRYPTO_AES_GCM_DECRYPT, plaintext, ciphertext, len_ciphertext, key, iv, tag);

	ctx = EVP_CIPHER_CTX_new();
	if (ctx == NULL) {
		LOGE("Failed EVP_CIPHER_CTX_new\n");
		ret = EM_ERR_EM_CRYPTO_AES_GCM_ENCRYPT_CONTEXT;
		goto out;
	}

	if (!EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL)) {
		LOGE("Failed EVP_EncryptInit_ex, gcm\n");
		ret = EM_ERR_EM_CRYPTO_AES_GCM_ENCRYPT_INIT;
		goto out;
	}

	if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, len_iv, NULL)) {
		LOGE("Failed EVP_CIPHER_CTX_ctrl, salt(%s)\n", ERR_error_string(ERR_get_error(), NULL));
		ret = EM_ERR_EM_CRYPTO_AES_GCM_ENCRYPT_CTRL_IV;
		goto out;
	}

	if (!EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv)) {
		LOGE("EVP_EncryptInit_ex, key and iv \n");
		ret = EM_ERR_EM_CRYPTO_AES_GCM_ENCRYPT_INIT_KEY_IV;
		goto out;
	}

	if (!EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, len_plaintext)) {
		LOGE("Failed EVP_EncryptUpdate plaintext\n");
		ret = EM_ERR_EM_CRYPTO_AES_GCM_ENCRYPT_UPDATE;
		goto out;
	}

	*len_ciphertext = len;

	if (!EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) {
		LOGE("Failed EVP_EncryptFinal_ex\n");
		ret = EM_ERR_EM_CRYPTO_AES_GCM_ENCRYPT_FINAL;
		goto out;
	}
	*len_ciphertext += len;

	if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, len_tag, tag)) {
		LOGE("Failed EVP_CIPHER_CTX_ctrl, get tag\n");
		ret = EM_ERR_EM_CRYPTO_AES_GCM_ENCRYPT_CTRL_TAG;
		goto out;
	}

	ret = EM_SUCCESS;
out:
	if (ctx != NULL)
		EVP_CIPHER_CTX_free(ctx);

	return ret;
}

int em_crypto_aes_256_gcm_decrypt(const unsigned char *ciphertext, const unsigned int len_ciphertext,
				  unsigned char *plaintext, unsigned int *len_plaintext, const unsigned char *key,
				  const unsigned int len_key, const unsigned char *iv, const unsigned int len_iv,
				  unsigned char *tag, unsigned int len_tag)
{
	EVP_CIPHER_CTX *ctx = NULL;
	int len = 0;
	int ret;

	EM_CHECK_NULL(__func__, EM_ERR_EM_CRYPTO_AES_GCM_DECRYPT, ciphertext, plaintext, len_plaintext, key, iv, tag);

	/* Create and initialise the context */
	ctx = EVP_CIPHER_CTX_new();
	if (ctx == NULL) {
		LOGE("Failed EVP_CIPHER_CTX_new\n");
		ret = EM_ERR_EM_CRYPTO_AES_GCM_DECRYPT_CONTEXT;
		goto out;
	}

	/* Initialise the decryption operation. */
	if (!EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL)) {
		LOGE("Failed EVP_DecryptInit_ex, gcm\n");
		ret = EM_ERR_EM_CRYPTO_AES_GCM_DECRYPT_INIT;
		goto out;
	}

	/* Set IV length. Not necessary if this is 12 bytes (96 bits) */
	if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, len_iv, NULL)) {
		LOGE("Failed EVP_CIPHER_CTX_ctrl, salt\n");
		ret = EM_ERR_EM_CRYPTO_AES_GCM_DECRYPT_CTRL_IV;
		goto out;
	}

	/* Initialise key and IV */
	if (!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) {
		LOGE("Failed EVP_DecryptInit_ex, key and iv \n");
		ret = EM_ERR_EM_CRYPTO_AES_GCM_DECRYPT_INIT_KEY_IV;
		goto out;
	}

	/* Provide the message to be decrypted, and obtain the plaintext output.
	 * EVP_DecryptUpdate can be called multiple times if necessary
	 */
	if (!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, len_ciphertext)) {
		LOGE("Failed EVP_DecryptUpdate, ciphertext\n");
		ret = EM_ERR_EM_CRYPTO_AES_GCM_DECRYPT_UPDATE;
		goto out;
	}
	*len_plaintext = len;

	/* Set expected tag value. Works in OpenSSL 1.0.1d and later */
	if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, len_tag, tag)) {
		LOGE("Failed EVP_CIPHER_CTX_ctrl, tag\n");
		ret = EM_ERR_EM_CRYPTO_AES_GCM_DECRYPT_CTRL_TAG;
		goto out;
	}

	/* Finalise the decryption. A positive return value indicates success,
	 * anything else is a failure - the plaintext is not trustworthy.
	 */
	if (!EVP_DecryptFinal_ex(ctx, plaintext + len, &len)) {
		LOGE("Failed DecryptFinal_ex\n");
		ret = EM_ERR_EM_CRYPTO_AES_GCM_DECRYPT_FINAL;
		goto out;
	}
	*len_plaintext += len;

	ret = EM_SUCCESS;
out:
	/* Clean up */
	if (ctx != NULL)
		EVP_CIPHER_CTX_free(ctx);

	return ret;
}

int em_crypto_verify_rsa_signature(const unsigned char *cert, const unsigned int len_cert, const unsigned char *sig,
				   const unsigned int len_sig, const unsigned char *data, const unsigned int len_data)
{
	int ret, cert_type = 0;
	uint8_t digest[EM_LEN_SHA256] = {0,};
	uint32_t digest_size = EM_LEN_SHA256;

	EM_CHECK_NULL(__func__, EM_ERR_EM_CRYPTO_VERIFY_RSA_SIGNATURE, cert, sig, data);

	if ((server_pubkey = EVP_PKEY_new()) == NULL) {
		LOGE("Failed to initialize server pubkey. : %u\n", ERR_get_error());
		ret = EM_ERR_EM_CRYPTO_VERIFY_RSA_SIGNATURE;
		goto out;
	}

	ret = em_crypto_sha256(data, len_data, digest);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to make digest(%08x)\n", ret);
		goto out;
	}

	ret = em_crypto_verify_cert(cert, len_cert, &cert_type);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to verify cert(%08x)\n", ret);
		goto out;
	}

	ret = em_crypto_verify_signature(sig, len_sig, digest, digest_size);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to verify signature(0x%08x)\n", ret);
		goto out;
	}

	if (cert_type == EM_TYPE_DEV_CERT)
		ret = EM_DEV_OK;
out:
	if (ret != EM_SUCCESS && (uint32_t)ret != EM_DEV_OK)
		LOGE("SSL error : %s\n", ERR_error_string(ERR_get_error(), NULL));

	if (server_pubkey) {
		EVP_PKEY_free(server_pubkey);
		server_pubkey = NULL;
	}

	return ret;
}

int em_crypto_aes_256_ctr_encrypt(const unsigned char *plaintext, const int len_plaintext, const unsigned char *key,
				  const unsigned char *iv, unsigned char *ciphertext, int *len_ciphertext)
{
	EVP_CIPHER_CTX *ctx = NULL;
	int len = 0;
	int ret;

	EM_CHECK_NULL(__func__, EM_ERR_EM_CRYPTO_AES_CTR_ENCRYPT, plaintext, key, iv, ciphertext, len_ciphertext);

	ctx = EVP_CIPHER_CTX_new();
	if (ctx == NULL) {
		LOGE("Failed EVP_CIPHER_CTX_new\n");
		ret = EM_ERR_EM_CRYPTO_AES_CTR_ENCRYPT_CTX;
		goto out;
	}

	if (!EVP_EncryptInit_ex(ctx, EVP_aes_256_ctr(), NULL, key, iv)) {
		LOGE("Failed EVP_EncryptInit_ex, CTR\n");
		ret = EM_ERR_EM_CRYPTO_AES_CTR_ENCRYPT_INIT;
		goto out;
	}

	if (!EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, len_plaintext)) {
		LOGE("Failed EVP_EncryptUpdate\n");
		ret = EM_ERR_EM_CRYPTO_AES_CTR_ENCRYPT_UPDATE;
		goto out;
	}
	*len_ciphertext = len;

	if (!EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) {
		LOGE("Failed EVP_EncryptFinal_ex\n");
		ret = EM_ERR_EM_CRYPTO_AES_CTR_ENCRYPT_FINAL;
		goto out;
	}
	*len_ciphertext += len;

	ret = EM_SUCCESS;
out:
	if (ret != EM_SUCCESS)
		LOGE("SSL error : %s\n", ERR_error_string(ERR_get_error(), NULL));

	if (ctx)
		EVP_CIPHER_CTX_free(ctx);

	return ret;
}

int em_crypto_aes_256_ctr_decrypt(const unsigned char *ciphertext, const int len_ciphertext, const unsigned char *key,
				  const unsigned char *iv, unsigned char *plaintext, int *len_plaintext)
{
	EVP_CIPHER_CTX *ctx = NULL;
	int len = 0;
	int ret;

	EM_CHECK_NULL(__func__, EM_ERR_EM_CRYPTO_AES_CTR_DECRYPT, ciphertext, key, iv, plaintext, len_plaintext);

	ctx = EVP_CIPHER_CTX_new();
	if (ctx == NULL) {
		LOGE("Failed EVP_CIPHER_CTX_new\n");
		ret = EM_ERR_EM_CRYPTO_AES_CTR_DECRYPT_CTX;
		goto out;
	}

	if (!EVP_DecryptInit_ex(ctx, EVP_aes_256_ctr(), NULL, NULL, NULL)) {
		LOGE("Failed EVP_DecryptInit_ex, gcm\n");
		ret = EM_ERR_EM_CRYPTO_AES_CTR_DECRYPT_INIT;
		goto out;
	}

	if (!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) {
		LOGE("Failed EVP_DecryptInit_ex, key and iv \n");
		ret = EM_ERR_EM_CRYPTO_AES_CTR_DECRYPT_INIT_KEY_IV;
		goto out;
	}

	if (!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, len_ciphertext)) {
		LOGE("Failed EVP_DecryptUpdate, ciphertext\n");
		ret = EM_ERR_EM_CRYPTO_AES_CTR_DECRYPT_UPDATE;
		goto out;
	}
	*len_plaintext = len;

	if (!EVP_DecryptFinal_ex(ctx, plaintext + len, &len)) {
		LOGE("Failed EVP_DecryptFinal_ex\n");
		ret = EM_ERR_EM_CRYPTO_AES_CTR_DECRYPT_FINAL;
		goto out;
	}
	*len_plaintext += len;

	ret = EM_SUCCESS;
out:
	if (ret != EM_SUCCESS)
		LOGE("SSL error : %s\n", ERR_error_string(ERR_get_error(), NULL));

	if (ctx)
		EVP_CIPHER_CTX_free(ctx);

	return ret;
}

int em_crypto_rsa_encrypt(const uint8_t *cert, const int len_cert, const uint8_t *in, const int len_in, uint8_t *out,
			  int *len_out)
{
	int ret;
	int cert_type = 0;
	RSA *pRsaKey = NULL;

	EM_CHECK_NULL(__func__, EM_ERR_EM_CRYPTO_RSA_ENCRYPT, cert, in, out, len_out);

	ret = em_crypto_verify_cert(cert, len_cert, &cert_type);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to verify cert(%08x)\n", ret);
		goto out;
	}

	// DEV_CERT can be installed. but it's not used.
	LOGI("CERT TYPE : %u\n", cert_type);

	// get RSA key
	if ((pRsaKey = EVP_PKEY_get1_RSA(server_pubkey)) == NULL) {
		LOGE("Failed to get RSA key(%s)\n", ERR_error_string(ERR_get_error(), NULL));
		ret = EM_ERR_EM_CRYPTO_RSA_ENCRYPT_GET_PUB_KEY;
		goto out;
	}

	// Check RSA key
	ret = RSA_check_key(pRsaKey);
	if (ret == -1) {
		LOGE("Invaild RSA key(%s)\n", ERR_error_string(ERR_get_error(), NULL));
		ret = EM_ERR_EM_CRYPTO_RSA_ENCRYPT_CHECK_KEY;
		goto out;
	}

	// RSA encrypt
	*len_out = RSA_public_encrypt(len_in, in, out, pRsaKey, RSA_PKCS1_OAEP_PADDING);
	if (*len_out == -1) {
		LOGE("No OAEP support(%s)\n", ERR_error_string(ERR_get_error(), NULL));
		ret = EM_ERR_EM_CRYPTO_RSA_ENCRYPT_FAILED;
		goto out;
	}

	ret = EM_SUCCESS;
out:
	if (server_pubkey) {
		EVP_PKEY_free(server_pubkey);
		server_pubkey = NULL;
	}

	if (pRsaKey) {
		RSA_free(pRsaKey);
		pRsaKey = NULL;
	}

	return ret;
}

int em_crypto_get_subject_from_cert(const unsigned char *cert, const int len_cert, const char* type, char *out, int out_len)
{
	int ret;

	const unsigned char *pCert = cert;
	X509 *server_cert = NULL;
	char *subj = NULL;
	char *subj_val = NULL;
	int type_len = 0;

	EM_CHECK_NULL(__func__, EM_ERR_EM_CRYPTO_GET_SUBJECT, cert, out, type);

	type_len = strlen(type);	
	if ((server_cert = d2i_X509(NULL, &pCert, len_cert)) == NULL) {
		LOGE("Failed to read server cert. : %u %p\n", (int)ERR_get_error(), pCert);
		ret = EM_ERR_EM_CRYPTO_GET_SUBJECT_LEN_CERT;
		goto out;
	}

	subj = X509_NAME_oneline(X509_get_subject_name(server_cert), NULL, 0);
	if (subj == NULL) {
		LOGE("Failed to get subject name. : %u\n", (int)ERR_get_error());
		ret = EM_ERR_EM_CRYPTO_GET_SUBJECT_X509_NAME_ONLINE;
		goto out;
	}

	subj_val = strtok(subj, "/");
	while (1) {
		if (subj_val == NULL) {
			LOGE("Value of type does not exist.\n");
			ret = EM_ERR_EM_CRYPTO_GET_SUBJECT_NOT_EXIST;
			goto out;
		}

		if (!memcmp(subj_val, type, type_len))
			break;

		subj_val = strtok(NULL, "/");
	}

	subj_val += type_len;// size of type
	LOGI("%s%s\n", type, subj_val);

	if ((size_t)out_len < strlen(subj_val)) {
		LOGE("out buf has no enough size. (%u<%u)\n", out_len, (uint32_t)strlen(subj_val));
		ret = EM_ERR_EM_CRYPTO_GET_SUBJECT_LEN;
		goto out;
	}

	memcpy(out, subj_val, strlen(subj_val));
	ret = EM_SUCCESS;
out:
	if (subj) {
		OPENSSL_free(subj);
		subj = NULL;
	}

	if (server_cert) {
		X509_free(server_cert);
		server_cert = NULL;
	}

	return ret;
}

int em_crypto_verify_recovery_data(const uint8_t *data, uint32_t offset, const uint8_t *source,
				   const uint32_t len_source, uint32_t *server_type)
{
	uint32_t ret, i = 0;
	em_default_meta meta = {};
	em_default_element elem = {};

	uint8_t *cert = NULL;
	uint32_t len_cert = 0;

	uint8_t *signature = NULL;
	uint32_t len_signature = 0;

	EM_CHECK_NULL(__func__, EM_ERR_EM_CRYPTO_VERIFY_RECOVERY_DATA, data, source, server_type);

	cert = (uint8_t *)em_calloc(sizeof(uint8_t), EM_LEN_CERTIFICATE);
	if (cert == NULL) {
		LOGE("Failed to allocate cert\n");
		ret = EM_ERR_EM_CRYPTO_VERIFY_RECOVERY_DATA_CERT;
		goto out;
	}

	signature = (uint8_t *)em_calloc(sizeof(uint8_t), EM_LEN_SIGNATURE);
	if (signature == NULL) {
		LOGE("Failed to allocate signature\n");
		ret = EM_ERR_EM_CRYPTO_VERIFY_RECOVERY_DATA_SIG;
		goto out;
	}

	memcpy(&meta, data + offset, sizeof(em_default_meta));
	offset += sizeof(em_default_meta);

	if (memcmp(meta.magic, EM_MAGIC_INTE, EM_LEN_ESI_MAGIC) != 0) {
		LOGE("magic error(%02x/%02x/%02x/%02x)\n", meta.magic[0], meta.magic[1], meta.magic[2],
		     meta.magic[3]);
		ret = EM_ERR_EM_CRYPTO_VERIFY_RECOVERY_DATA_MAGIC;
		goto out;
	}

	// TODo CHECK  COUNT

	for (i = 0; i < meta.num_of_data; i++) {
		if (offset >= EM_LEN_RECOVERY_DATA) {
			LOGE("offset isn't normal(%u/%u)\n", offset, (uint32_t)EM_LEN_RECOVERY_DATA);
			ret = EM_ERR_EM_CRYPTO_VERIFY_RECOVERY_DATA_OFFSET;
			goto out;
		}

		memcpy(&elem, data + offset, sizeof(em_default_element));
		offset += sizeof(em_default_element);

		if (offset + elem.len > EM_LEN_RECOVERY_DATA) {
			LOGE("offset isn't  normal(%08x: %u/%u)\n", elem.type, offset,
			     (uint32_t)EM_LEN_RECOVERY_DATA);
			ret = EM_ERR_EM_CRYPTO_VERIFY_RECOVERY_DATA_PARSE;
			goto out;
		}

		switch (elem.type) {
		case EM_INFO_TYPE_INTE_SIGNATURE:
			if (elem.len > EM_LEN_SIGNATURE) {
				LOGE("Unexpected sig size(%u)\n", elem.len);
				ret = EM_ERR_EM_CRYPTO_VERIFY_RECOVERY_DATA_S_LEN;
				goto out;
			}
			memcpy(signature, data + offset, elem.len);
			len_signature = elem.len;
			break;
		case EM_INFO_TYPE_INTE_SERVER_CERT:
			if (elem.len > EM_LEN_CERTIFICATE) {
				LOGE("Unexpected server cert size(%u)\n", elem.len);
				ret = EM_ERR_EM_CRYPTO_VERIFY_RECOVERY_DATA_C_LEN;
				goto out;
			}
			memcpy(cert, data + offset, elem.len);
			len_cert = elem.len;
			break;
		default:
			LOGE("Unknow type(0x%04x)\n", elem.type);
			break;
		}
		offset += elem.len;
	}

	ret = em_crypto_verify_rsa_signature(cert, len_cert, signature, len_signature, source, len_source);
	if (ret != EM_SUCCESS) {
		LOGE("Failed to verify rsa_signaturei(%08x)\n", ret);
		goto out;
	}

	//	TODOTODOTODO
	//	*server_type = em_is_token_from_fac(&inte_info);
	ret = em_token_is_from_fac(cert, len_cert);
	*server_type = (ret == EM_ERR_EM_TOKEN_IS_FROM_FAC_FROM_FAC) ? EM_FLAG_EM_TOKEN_FROM_FAC : ret;

	ret = EM_SUCCESS;
out:
	if (cert)
		em_free(cert);

	if (signature)
		em_free(signature);

	return ret;
}
