#include "TZ_Vendor_tl.h"
#include "jose.h"
#include "spay_parse_cert_tl.h"
#include "base64.h"
#include "cipher.h"
#include "cryptolib_init.h"

static const char h_alg[] = "{\"alg\":\"";
static const char h_kid[] = "\",\"kid\":\"";
static const char h_typ[] = "\",\"typ\":\"JOSE";
static const char h_cha[] = "\",\"channelSecurityContext\":\"RSA_PKI";
static const char h_enc[] = "\",\"enc\":\"";
static const char h_end[] = "\"}";
static const char h_cty[] = "\",\"cty\":\"JWE\"}";


uint32_t jwe_header(uint8_t * header, uint32_t * header_len,
		jwe_alg_t alg,
		uint8_t * kid, uint32_t kid_len,
		jwe_enc_t enc)
{
	int i = 0;

	if (*header_len < JWE_HEADER_MAX_LEN) {
		return JOSE_HEADER_LEN_TOO_SMALL;
	}

	/* {"alg":" */
	memcpy(header + i, h_alg, sizeof(h_alg)-1);
	i += sizeof(h_alg) - 1;

	switch (alg) {
	case RSA1_5:
		memcpy(header + i, "RSA1_5", sizeof("RSA1_5")-1);
		i += sizeof("RSA1_5") - 1;
		break;
	case RSA_OAEP:
		memcpy(header + i, "RSA-OAEP", sizeof("RSA-OAEP")-1);
		i += sizeof("RSA-OAEP") - 1;
		break;
	default:
		TTY_LOG("unsupported alg for jwe");
		return JOSE_UNSUPPORTED_ALG;
	}
	/* ","kid":" */
	memcpy(header + i, h_kid, sizeof(h_kid) - 1);
	i += sizeof(h_kid) - 1;
	DBG_LOG("kid_len: %d, MAX: %d", kid_len, JWE_KID_MAX_LEN);
	if (kid_len > JWE_KID_MAX_LEN) {
		TTY_LOG("TOO_LONG: kid_len: %d, MAX: %d", kid_len, JWE_KID_MAX_LEN);
		return JOSE_KID_LEN_TOO_LONG;
	}
	if (kid != NULL) {
		memcpy(header + i, kid, kid_len);
		i += kid_len;
	}

	memcpy(header + i, h_typ, sizeof(h_typ) - 1);
	i += sizeof(h_typ) - 1;

	memcpy(header + i, h_cha, sizeof(h_cha) - 1);
	i += sizeof(h_cha) - 1;

	memcpy(header + i, h_enc, sizeof(h_enc) - 1);
	i += sizeof(h_enc) - 1;

	/* enc */
	switch (enc) {
	case A128GCM:
		memcpy(header + i, "A128GCM", sizeof("A128GCM") - 1);
		i += sizeof("A128GCM") - 1;
		break;
	case A256GCM:
		memcpy(header + i, "A256GCM", sizeof("A256GCM") - 1);
		i += sizeof("A256GCM") - 1;
		break;
	default:
		return JOSE_UNSUPPORTED_ENC;
	}

	memcpy(header +i, h_end, sizeof(h_end) - 1);
	i += sizeof(h_end) - 1;

	*header_len = i;

	return JOSE_OK;
}

uint32_t jws_header(uint8_t * header, uint32_t * header_len,
		jws_alg_t alg,
		uint8_t * kid, uint32_t kid_len)
{
	int i = 0;

	if (*header_len < JWE_HEADER_MAX_LEN) {
		return JOSE_HEADER_LEN_TOO_SMALL;
	}

	/* {"alg":" */
	memcpy(header + i, h_alg, sizeof(h_alg)-1);
	i += sizeof(h_alg) - 1;
	switch (alg) {
	case RS256:
		memcpy(header + i, "RS256", sizeof("RS256")-1);
		i += sizeof("RS256") - 1;
		break;
	default:
		return JOSE_UNSUPPORTED_ALG;
	}
	/* ","kid":" */
	memcpy(header + i, h_kid, sizeof(h_kid) - 1);
	i += sizeof(h_kid) - 1;

	if (kid_len > JWE_KID_MAX_LEN) {
		return JOSE_KID_LEN_TOO_LONG;
	}
	memcpy(header + i, kid, kid_len);
	i += kid_len;

	memcpy(header + i, h_typ, sizeof(h_typ) - 1);
	i += sizeof(h_typ) - 1;

	memcpy(header + i, h_cty, sizeof(h_cty) - 1);
	i += sizeof(h_cty) - 1;

	*header_len = i;

	return JOSE_OK;
}

uint32_t construct_jwe_transaction_info(
		uint8_t * kid, uint32_t kid_len,
		uint8_t * root_modulus, uint32_t root_modulus_len,
		uint8_t * root_pubexp, uint32_t root_pubexp_len,
		uint8_t * intermediate_cert, uint32_t intermediate_cert_len,
		uint8_t * merchant_cert, uint32_t merchant_cert_len,
		uint8_t * transaction_info, uint32_t transaction_info_len,
		uint8_t * jwe_output, uint32_t * jwe_output_len)
{
	uint32_t ret_val = JOSE_UNKNOWN_ERROR;

	uint8_t intermediate_modulus[TZ_SPAY_MAX_RSA_KEY_LEN];
	uint32_t intermediate_modulus_len = sizeof(intermediate_modulus);
	uint8_t intermediate_pubexp[TZ_SPAY_MAX_RSA_KEY_LEN];
	uint32_t intermediate_pubexp_len = sizeof(intermediate_pubexp);

	uint8_t merchant_modulus[TZ_SPAY_MAX_RSA_KEY_LEN];
	uint32_t merchant_modulus_len = sizeof(merchant_modulus);
	uint8_t merchant_pubexp[TZ_SPAY_MAX_RSA_KEY_LEN];
	uint32_t merchant_pubexp_len = sizeof(merchant_pubexp);

	//TODO: NEED TO CHECK transaction_info SIZE
	if (!cryptolib_init()) {
		TTY_LOG("cryptolib_init failed");
		goto exit;
	}

	if (intermediate_cert != NULL && intermediate_cert_len != 0) {
		//verify intermediate_cert which is signed by root
		if (parse_verify_cert(intermediate_cert, intermediate_cert_len,
					root_modulus, root_modulus_len,
					root_pubexp, root_pubexp_len,
					intermediate_modulus, &intermediate_modulus_len,
					intermediate_pubexp, &intermediate_pubexp_len)) {
			TTY_LOG("jose: failed to parse and verify intermediate certificate");
			ret_val = JOSE_INAPP_CERT_FAILED;
			goto exit;
		}
		DBG_LOG("parse intermediate cert success");
		DBG_DUMP(intermediate_modulus, intermediate_modulus_len);

		//verify merchant_cert
		if (parse_verify_cert(merchant_cert, merchant_cert_len,
					intermediate_modulus, intermediate_modulus_len,
					intermediate_pubexp, intermediate_pubexp_len,
					merchant_modulus, &merchant_modulus_len,
					merchant_pubexp, &merchant_pubexp_len)) {
			TTY_LOG("jose: failed to parse and verify merchant certificate");
			ret_val = JOSE_INAPP_CERT_FAILED;
			goto exit;
		}
		DBG_LOG("parse merchant cert success");
		DBG_DUMP(merchant_modulus, merchant_modulus_len);
	} else {
		//verify merchant_cert which is signed by root directly
		if (parse_verify_cert(merchant_cert, merchant_cert_len,
					root_modulus, root_modulus_len,
					root_pubexp, root_pubexp_len,
					merchant_modulus, &merchant_modulus_len,
					merchant_pubexp, &merchant_pubexp_len)) {
			TTY_LOG("jose: failed to parse and verify merchant certificate");
			ret_val = JOSE_INAPP_CERT_FAILED;
			goto exit;
		}
		DBG_LOG("parse merchant cert success");
		DBG_DUMP(merchant_modulus, merchant_modulus_len);
	}

	TTY_LOG("Calling construct_jwe");
	ret_val = construct_jwe(kid, kid_len, 0,
		merchant_modulus, merchant_modulus_len, merchant_pubexp, merchant_pubexp_len,
		transaction_info, transaction_info_len,
		jwe_output, jwe_output_len);

exit:
	memset(merchant_modulus, 0, sizeof(merchant_modulus));
	merchant_modulus_len = 0;
	memset(merchant_pubexp, 0, sizeof(merchant_pubexp));
	merchant_pubexp_len = 0;

	return ret_val;
}

uint32_t construct_jws_transaction_info(
		uint8_t * kid, uint32_t kid_len,
		uint8_t * modulus, uint32_t modulus_len,
		uint8_t * exponent, uint32_t exponent_len,
		uint8_t * private_exponent, uint32_t private_exponent_len,
		uint8_t * root_modulus, uint32_t root_modulus_len,
		uint8_t * root_pubexp, uint32_t root_pubexp_len,
		uint8_t * intermediate_cert, uint32_t intermediate_cert_len,
		uint8_t * merchant_cert, uint32_t merchant_cert_len,
		uint8_t * transaction_info, uint32_t transaction_info_len,
		uint8_t * jws_output, uint32_t * jws_output_len)
{
	uint32_t ret_val = JOSE_UNKNOWN_ERROR;
	uint8_t jwe_output[JOSE_MAX_BUF_SIZE];
	uint32_t jwe_output_len = sizeof(jwe_output);

	ret_val = construct_jwe_transaction_info(
			kid, kid_len,
			root_modulus, root_modulus_len,
			root_pubexp, root_pubexp_len,
			intermediate_cert, intermediate_cert_len,
			merchant_cert, merchant_cert_len,
			transaction_info, transaction_info_len,
			jwe_output, &jwe_output_len);

	if (ret_val != JOSE_OK) {
		TTY_LOG("construct_jwe_transaction_info failed");
		goto exit;
	}

	ret_val = construct_jws_only_transaction_info(kid, kid_len,
			modulus, modulus_len,
			exponent, exponent_len,
			private_exponent, private_exponent_len,
			jwe_output, jwe_output_len,
			jws_output, jws_output_len);

exit:
	if (ret_val != 0)
		*jws_output_len = 0;

	return ret_val;

}

uint32_t construct_jws_only_transaction_info(
		uint8_t * kid, uint32_t kid_len,
		uint8_t * modulus, uint32_t modulus_len,
		uint8_t * exponent, uint32_t exponent_len,
		uint8_t * private_exponent, uint32_t private_exponent_len,
		uint8_t * transaction_info, uint32_t transaction_info_len,
		uint8_t * jws_output, uint32_t * jws_output_len
)
{
	uint32_t ret_val = JOSE_UNKNOWN_ERROR;
	uint32_t i = 0;
	uint32_t encoded_len = 0;
	uint8_t tmpbuf[JOSE_MAX_BUF_SIZE];
	uint32_t tmpbuf_len = sizeof(tmpbuf);

    /* JWS header - START */
	ret_val = jws_header(tmpbuf, &tmpbuf_len, RS256, kid, kid_len);
	if (ret_val != JOSE_OK) {
		TTY_LOG("generate jws header failed, %d", ret_val);
		goto exit;
	}

    DBG_LOG("jws before encoding");
	DBG_DUMP(tmpbuf, tmpbuf_len);

    encoded_len = *jws_output_len;
	ret_val = base64url_encode_nopad(tmpbuf, tmpbuf_len, jws_output, &encoded_len);
	if (ret_val != BASE64_OK) {
		TTY_LOG("encode jwe header failed, %d", ret_val);
		goto exit;
	}

	DBG_LOG("jws after encoding ha;sdvhuas;dva;sdvzsdv");
	DBG_DUMP(jws_output + i, encoded_len);

	i += encoded_len;
	if (i >= *jws_output_len) {
		TTY_LOG("buf not enough after encoding jws header");
		ret_val = JOSE_UNKNOWN_ERROR;
		goto exit;
	}
	jws_output[i++] = '.';
    /* JWS header - END */

	/* encode JWS payload - START */
	encoded_len = *jws_output_len - i;
	ret_val = base64url_encode_nopad(transaction_info, transaction_info_len, jws_output + i,
			&encoded_len);
	if (ret_val != BASE64_OK) {
		TTY_LOG("encode JWS payload failed, %d", ret_val);
		goto exit;
	}

	i += encoded_len;
	if (i >= *jws_output_len) {
		TTY_LOG("buf not enough after encoding jws payload");
		ret_val = JOSE_UNKNOWN_ERROR;
		goto exit;
	}
	jws_output[i++] = '.';
    /* encode JWS payload - END */

	/* generate sig for jws_header.jws_payload - START */
	if (modulus == NULL || modulus_len == 0
			|| exponent == NULL || exponent_len == 0
			|| private_exponent == NULL || private_exponent_len == 0) {
		TTY_LOG("signing key not ready");
		ret_val = JOSE_UNKNOWN_ERROR;
		goto exit;
	}
	tmpbuf_len = sizeof(tmpbuf);
	ret_val = TZ_sign_CKM_SHA256_RSA_PKCS(modulus, modulus_len,
					      exponent, exponent_len,
					      private_exponent, private_exponent_len,
					      jws_output, i - 1,
					      tmpbuf, &tmpbuf_len);
	encoded_len = *jws_output_len - i;
	ret_val = base64url_encode_nopad(tmpbuf, tmpbuf_len, jws_output + i,
			&encoded_len);
	if (ret_val != BASE64_OK) {
		TTY_LOG("encode jws signature failed, %d", ret_val);
		goto exit;
	}
	i += encoded_len;
	*jws_output_len = i;

    /* generate sig for jws_header.jws_payload - END */
	ret_val = JOSE_OK;

exit:
	if (ret_val != 0)
		*jws_output_len = 0;
	/* clear memory */
	memset(tmpbuf, 0, sizeof(tmpbuf));
	tmpbuf_len = sizeof(tmpbuf);

	return ret_val;
}

uint32_t construct_jwe(
		uint8_t * kid, uint32_t kid_len,
		uint8_t isAadNeeded,
		uint8_t * merchant_modulus, uint32_t merchant_modulus_len,
		uint8_t * merchant_pubexp, uint32_t merchant_pubexp_len,
		uint8_t * transaction_info, uint32_t transaction_info_len,
		uint8_t * jwe_output, uint32_t * jwe_output_len)
{
	uint32_t ret_val = JOSE_UNKNOWN_ERROR;
	uint32_t i = 0;
	uint8_t * jwe_header_ptr = NULL;
	uint32_t jwe_header_len = 0;
	uint8_t cek[AES_KEY_SIZE];
	uint32_t cek_len = sizeof(cek);
	uint8_t iv[IV_SIZE];
	uint32_t iv_len = sizeof(iv);
	uint8_t tmpbuf[JOSE_MAX_BUF_SIZE];
	uint32_t tmpbuf_len = sizeof(tmpbuf);
	uint8_t tmpbuf2[JOSE_MAX_BUF_SIZE];
	uint32_t tmpbuf2_len = sizeof(tmpbuf2);
	uint8_t * aad = NULL;
	uint32_t aad_len = 0;

	TTY_LOG("In construct_jwe ");

    if(merchant_modulus == NULL || merchant_modulus_len > TZ_SPAY_MAX_RSA_KEY_LEN) {
		TTY_LOG("Invalid merchant modulus or modulus len %d", merchant_modulus_len);
		goto exit;
    }

    if(merchant_pubexp == NULL || merchant_pubexp_len > TZ_SPAY_MAX_RSA_KEY_LEN) {
		TTY_LOG("Invalid merchant pubexp or pubexp len %d", merchant_pubexp_len);
		goto exit;
    }
	/* JWE header */
	ret_val = jwe_header(tmpbuf, &tmpbuf_len, RSA1_5, kid, kid_len,
			A128GCM);
	if (ret_val != JOSE_OK) {
		TTY_LOG("generate jwe header failed, %d", ret_val);
		goto exit;
	}

	ret_val = base64url_encode_nopad(tmpbuf, tmpbuf_len, tmpbuf2 + i,
			&tmpbuf2_len);
	if (ret_val != BASE64_OK) {
		TTY_LOG("encode jwe header failed, %d", ret_val);
		goto exit;
	}

	jwe_header_ptr = tmpbuf2 + i;
	jwe_header_len = tmpbuf2_len;

	i += tmpbuf2_len;
	if (i >= sizeof(tmpbuf2)) {
		TTY_LOG("buf not enough after encoding jwe header");
		ret_val = JOSE_UNKNOWN_ERROR;
		goto exit;
	}
    
	tmpbuf2[i++] = '.';
	if (i >= sizeof(tmpbuf2)) {
		TTY_LOG("buf not enough after encoding jwe header");
		ret_val = JOSE_UNKNOWN_ERROR;
		goto exit;
	}

	/* create CEK */
	ret_val = TZ_gen_rand_data(cek, &cek_len);
	if (ret_val != TZ_API_OK) {
		TTY_LOG("CEK generation failed");
		goto exit;
	}
	DBG_LOG("CEK before encryption");
	DBG_DUMP(cek, cek_len);

	tmpbuf_len = sizeof(tmpbuf);
	DBG_LOG("merchant_modulus:");
	DBG_DUMP(merchant_modulus, merchant_modulus_len);
	DBG_LOG("merchant_pubexp:");
	DBG_DUMP(merchant_pubexp, merchant_pubexp_len);

	ret_val = TZ_rsa_encrypt(merchant_modulus,
				 merchant_modulus_len,
				 merchant_pubexp,
				 merchant_pubexp_len,
				 cek,
				 cek_len, tmpbuf, &tmpbuf_len);

	if (ret_val != TZ_API_OK) {
		TTY_LOG("encrypt CEK failed");
		goto exit;
	}
	DBG_LOG("encrypted CEK before encoding");
	DBG_DUMP(tmpbuf, tmpbuf_len);

	tmpbuf2_len = sizeof(tmpbuf2) - i;
	ret_val = base64url_encode_nopad(tmpbuf, tmpbuf_len, tmpbuf2 + i,
			&tmpbuf2_len);
	if (ret_val != BASE64_OK) {
		TTY_LOG("encode encrypted CEK failed");
		goto exit;
	}
	i += tmpbuf2_len;
	if (i >= sizeof(tmpbuf2)) {
		TTY_LOG("buf not enough after encoding encrypted CEK");
		ret_val = JOSE_UNKNOWN_ERROR;
		goto exit;
	}
    
	tmpbuf2[i++] = '.';
	if (i >= sizeof(tmpbuf2)) {
		TTY_LOG("buf not enough after encoding encrypted CEK");
		ret_val = JOSE_UNKNOWN_ERROR;
		goto exit;
	}

	/* generate IV */
	ret_val = TZ_gen_rand_data(iv, &iv_len);
	if (ret_val != TZ_API_OK) {
		TTY_LOG("iv generation failed");
		goto exit;
	}
	DBG_LOG("iv before encoding");
	DBG_DUMP(iv, iv_len);

	tmpbuf2_len = sizeof(tmpbuf2) - i;
	ret_val = base64url_encode_nopad(iv, iv_len, tmpbuf2 + i, &tmpbuf2_len);
	if (ret_val != BASE64_OK) {
		TTY_LOG("encode iv failed");
		goto exit;
	}
	i += tmpbuf2_len;
	if (i >= sizeof(tmpbuf2)) {
		TTY_LOG("buf not enough after encoding iv");
		ret_val = JOSE_UNKNOWN_ERROR;
		goto exit;
	}

	tmpbuf2[i++] = '.';
	if (i >= sizeof(tmpbuf2)) {
		TTY_LOG("buf not enough after encoding iv");
		ret_val = JOSE_UNKNOWN_ERROR;
		goto exit;
	}

	/* ciphertext */
	tmpbuf_len = sizeof(tmpbuf);
	if (tmpbuf_len < transaction_info_len) {
		TTY_LOG("tmpbuf too small");
		ret_val = JOSE_UNKNOWN_ERROR;
		goto exit;
	}
	memcpy(tmpbuf, transaction_info, transaction_info_len);

	if (isAadNeeded) {
		aad = jwe_header_ptr;
		aad_len = jwe_header_len;
		DBG_LOG("Using the jwe header as the AAD for the aesgcm algo");
	} else {
		TTY_LOG("AAD is NULL for aesgcm");
	}

	ret_val = aes_gcm_encrypt(cek, cek_len, iv, iv_len,
			aad, aad_len, tmpbuf, transaction_info_len, &tmpbuf_len);
	if (ret_val != SUCCESS) {
		TTY_LOG("aes_gcm_encrypt failed, %d", ret_val);
		goto exit;
	}
	DBG_LOG("ciphertext before encoding");
	DBG_DUMP(tmpbuf, tmpbuf_len);

	tmpbuf2_len = sizeof(tmpbuf2) - i;
	ret_val = base64url_encode_nopad(tmpbuf, tmpbuf_len - AES_GCM_TAG_SIZE,
			tmpbuf2 + i, &tmpbuf2_len);
	if (ret_val != BASE64_OK) {
		TTY_LOG("encode ciphertext failed, %d", ret_val);
		goto exit;
	}
	i += tmpbuf2_len;
	if (i >= sizeof(tmpbuf2)) {
		TTY_LOG("buf not enough after encoding ciphertext");
		ret_val = JOSE_UNKNOWN_ERROR;
		goto exit;
	}

    tmpbuf2[i++] = '.';
	if (i >= sizeof(tmpbuf2)) {
		TTY_LOG("buf not enough after encoding iv");
		ret_val = JOSE_UNKNOWN_ERROR;
		goto exit;
	}

	/* authentication tag */
	tmpbuf2_len = sizeof(tmpbuf2) - i;
	ret_val = base64url_encode_nopad(tmpbuf + tmpbuf_len - AES_GCM_TAG_SIZE,
			AES_GCM_TAG_SIZE, tmpbuf2 + i, &tmpbuf2_len);
	if (ret_val != BASE64_OK) {
		TTY_LOG("encode ciphertext failed, %d", ret_val);
		goto exit;
	}
	i += tmpbuf2_len;
	if (i >= sizeof(tmpbuf2)) {
		TTY_LOG("buf not enough after encoding tag");
		ret_val = JOSE_UNKNOWN_ERROR;
		goto exit;
	}
	tmpbuf2_len = i; /* length of jwe, don't touch tmpbuf2_len anymore */

	if (tmpbuf2_len <= *jwe_output_len) {
		memcpy(jwe_output, tmpbuf2, tmpbuf2_len);
		*jwe_output_len = tmpbuf2_len;
		ret_val = JOSE_OK;
	} else {
		TTY_LOG("JOSE: jwe_output_len too small");
		*jwe_output_len = 0;
		ret_val = JOSE_UNKNOWN_ERROR;
	}

exit:
	/* clear memory */
	memset(tmpbuf2, 0, sizeof(tmpbuf2));
	tmpbuf2_len = sizeof(tmpbuf2);
	memset(tmpbuf, 0, sizeof(tmpbuf));
	tmpbuf_len = sizeof(tmpbuf);
	memset(cek, 0, sizeof(cek));
	cek_len = sizeof(cek);
	memset(iv, 0, sizeof(iv));
	iv_len = sizeof(iv);

	return ret_val;
}

jose_format_t jose_format_determine(uint8_t * in_data, uint32_t in_data_len)
{
	uint32_t dot_count = 0;
	uint32_t i = 0;

	while (i < in_data_len){
		i++;
		if(in_data[i] == '.'){
			dot_count++;
		}
	}
	DBG_LOG("[JOSE]JOSE FORMAT determine: input");
	DBG_DUMP(in_data, in_data_len);

	if(dot_count == 2){
		TTY_LOG("JOSE_JWS_FROMAT");
		return JOSE_JWS_FORMAT;
	}
	else if(dot_count==4){
		TTY_LOG("JOSE_JWE_FROMAT");
		return JOSE_JWE_FORMAT;
	}
	else{
		TTY_LOG("JOSE_UNKNOWN_FROMAT");
		return JOSE_UNKNOWN_FORMAT;
	}
}

//TODO : Currently RSA2048/AES-GCM only.
uint32_t jose_decrypt_jwe(
	uint8_t * in_data, uint32_t in_data_len, jose_rsa2048_privinfo_t *device_ecertinfo,
	uint8_t * out_data, uint32_t * out_data_len
)
{
	uint32_t ret_val = JOSE_UNKNOWN_ERROR;
	uint32_t i = 0;
	uint32_t dot_i = 0;
	uint32_t ciphertext_len = 0;
	uint32_t tag_len = 0;

	uint8_t tmpbuf[JOSE_MAX_BUF_SIZE];
	uint32_t tmpbuf_len = JOSE_MAX_BUF_SIZE;
	uint8_t cek[AES_KEY_SIZE];
	uint32_t cek_len = sizeof(cek);
	uint8_t iv[IV_SIZE];
	uint32_t iv_len = sizeof(iv);
	uint8_t * aad = NULL;
	uint32_t aad_len = 0;
	uint8_t * jwe_header_ptr = NULL;
	uint32_t jwe_header_len = 0;

	int dot_count = 0;
	int j = 0;
	if (!cryptolib_init()) {
		TTY_LOG("[JOSE]: cryptolib_init failed");
		goto exit;
	}

	DBG_LOG("[JOSE]: in_data");
	DBG_DUMP(in_data, in_data_len);

	DBG_LOG("[JOSE]: in_data");
	DBG_DUMP(in_data, in_data_len);

	while (j < in_data_len){
		j++;
		if(in_data[j] == '.'){
			dot_count++;
		}
	}

	/* jwe header */
	i = 0;
	if (jose_find_dot(in_data, in_data_len, i, &dot_i)) {
		TTY_LOG("[JOSE]: failed to find dot after header");
		ret_val = JOSE_UNSUPPORTED_ALG;
		goto exit;
	}

	jwe_header_ptr = in_data+i;
	jwe_header_len = dot_i-i;

	ret_val =
		base64url_decode_nopad(in_data + i, dot_i - i, tmpbuf, &tmpbuf_len);
	if (ret_val != BASE64_OK) {
		TTY_LOG("[JOSE]: decode jwe header failed");
		goto exit;
	}

	/* encrypted CEK */
	i = dot_i + 1;
	if (jose_find_dot(in_data, in_data_len, i, &dot_i)) {
		ret_val = JOSE_UNSUPPORTED_ALG;
		goto exit;
	}
	tmpbuf_len = sizeof(tmpbuf);
	ret_val =
		base64url_decode_nopad(in_data + i, dot_i - i, tmpbuf, &tmpbuf_len);
	if (ret_val != BASE64_OK) {
		TTY_LOG("[JOSE]: decode encrypted cek failed");
		goto exit;
	}

	DBG_LOG("[JOSE]: ret_val for encrypted CEK: %d", ret_val);
	DBG_DUMP(tmpbuf, tmpbuf_len);

	ret_val = TZ_rsa_decrypt(device_ecertinfo->modulus,
				device_ecertinfo->modulus_len,
				device_ecertinfo->pubexp,
				device_ecertinfo->pubexp_len,
				device_ecertinfo->privExp,
				device_ecertinfo->privExp_len,
				tmpbuf, tmpbuf_len, cek, &cek_len);

	if (ret_val != TZ_API_OK) {
		TTY_LOG("[JOSE]: decrypt CEK failed, %d", ret_val);
		goto exit;
	}

	/* iv */
	i = dot_i + 1;
	if (jose_find_dot(in_data, in_data_len, i, &dot_i)) {
		TTY_LOG("[JOSE]: failed to find dot after iv");
		ret_val = JOSE_UNSUPPORTED_ALG;
		goto exit;
	}
	tmpbuf_len = sizeof(tmpbuf);

	ret_val =
		base64url_decode_nopad(in_data + i, dot_i - i, tmpbuf, &tmpbuf_len);
	if (ret_val != BASE64_OK) {
		goto exit;
	}
	if (tmpbuf_len > sizeof(iv)) {
		DBG_LOG("[JOSE]: iv is:");
		DBG_DUMP(tmpbuf, tmpbuf_len);
		ret_val = JOSE_UNSUPPORTED_ENC;
		goto exit;
	}
	memcpy(iv, tmpbuf, tmpbuf_len);
	iv_len = tmpbuf_len;
	DBG_LOG("[JOSE]: ret_val for iv: %d", ret_val);
	DBG_DUMP(iv, iv_len);

	/* ciphertext */
	i = dot_i + 1;
	if (jose_find_dot(in_data, in_data_len, i, &dot_i)) {
		TTY_LOG("[JOSE]: failed to find dot after ciphertext");
		ret_val = JOSE_UNSUPPORTED_ALG;
		goto exit;
	}
	tmpbuf_len = sizeof(tmpbuf);
	ret_val =
		base64url_decode_nopad(in_data + i, dot_i - i, tmpbuf, &tmpbuf_len);
	if (ret_val != BASE64_OK) {
		TTY_LOG("[JOSE]: decode ciphertext failed, %d", ret_val);
		goto exit;
	}
	ciphertext_len = tmpbuf_len;

	/* tag, all the rest */
	i = dot_i + 1;
	tmpbuf_len = sizeof(tmpbuf) - ciphertext_len;	/* leave space for tag */
	ret_val = base64url_decode_nopad(in_data + i, in_data_len - i,tmpbuf + ciphertext_len, &tmpbuf_len);
	if (ret_val != BASE64_OK) {
		TTY_LOG("[JOSE]: decode tag failed, %d", ret_val);
		goto exit;
	}
	tag_len = tmpbuf_len;

	aad = jwe_header_ptr;
	aad_len = jwe_header_len;

	/* decrypt content */
	ret_val = aes_gcm_decrypt(cek, cek_len, iv, iv_len, aad, aad_len, tmpbuf, ciphertext_len + tag_len, &tmpbuf_len);
	if (ret_val != SUCCESS) {
		TTY_LOG("[JOSE]: failed to decrypt ciphertext, %d", ret_val);
		goto exit;
	}
	if (tmpbuf_len <= 1 || tmpbuf_len > *out_data_len) {
		ret_val = JOSE_UNSUPPORTED_ENC;
		TTY_LOG("[JOSE]: plaintext is larger than out buffer or empty");
		goto exit;
	}
	memcpy(out_data, tmpbuf, tmpbuf_len);
	*out_data_len = tmpbuf_len;

	ret_val = JOSE_OK;

exit:
	if (ret_val != 0) {
		*out_data_len = 0;
	}

	/* clear memory */
	memset(tmpbuf, 0, sizeof(tmpbuf));
	tmpbuf_len = sizeof(tmpbuf);
	memset(cek, 0, sizeof(cek));
	cek_len = sizeof(cek);
	memset(iv, 0, sizeof(iv));
	iv_len = sizeof(iv);
	return ret_val;
}

int jose_find_dot(
	uint8_t * in_data,
	uint32_t in_data_len,
	uint32_t start_i,
	uint32_t * dot_i
)
{
	uint32_t i = 0;
	if (start_i > in_data_len) {
		TTY_LOG("start_i(%d) out of bound", start_i);
		return -1;
	}
	while ((in_data[start_i + i] != '.') && (start_i + i < in_data_len))
		i++;
	if (start_i + i == in_data_len) {
		TTY_LOG("end of data, no dot found");
		return -2;
	}
	*dot_i = start_i + i;
	return 0;
}

