/*
 * (c) Copyright 2015 Samsung Research America, Inc.
 *                  All rights reserved
 *
 *  MPS Lab
 *
 * File: jwe.c
 * Author: r.kothari@samsung.com
 * Creation Date: Dec 4, 2015
 * Co-Author: zheng.z@samsung.com
 * Improved Date: Sep 6, 2016
 * Co-Author: jianwei.qian@samsung.com
 * Update Date: Aug 12, 2020
 */
/*
 * Copyright (C) 2015 Samsung Electronics Co., Ltd. All rights reserved.
 *
 * Mobile Communication Division,
 * Digital Media & Communications Business, Samsung Electronics Co., Ltd.
 *
 * This software and its documentation are confidential and proprietary
 * information of Samsung Electronics Co., Ltd.  No part of the software and
 * documents may be copied, reproduced, transmitted, translated, or reduced to
 * any electronic medium or machine-readable form without the prior written
 * consent of Samsung Electronics.
 *
 * Samsung Electronics makes no representations with respect to the contents,
 * and assumes no responsibility for any errors that might appear in the
 * software and documents. This publication and the contents hereof are subject
 * to change without notice.
 */

#include "jwe.h"
#include "TZ_Vendor_tl.h"
#include "pebble_defs.h"
#include "pebble_core.h"
#include "base64.h"
#include "cipher.h"
#include "aes_hsha2_aead.h"
#include "parson.h"
#include "openssl/rsa.h"

/* Given input JWE Params, this returns the NULL terminated
 * param string of the requested header tag */

#define JWE_KID_MAX_LEN   128

static uint32_t get_param_str(const char *tag, jwe_params *params, uint8_t *out,
		uint32_t out_len) {
	uint32_t ret = JWE_GEN_ERROR_INTERNAL;
	char *str = NULL;

	if (!tag || !params || !out || !out_len) {
		PEBBLE_LOG("Invalid params");
		return JWE_GEN_ERROR_INVALID_INPUT_PARAM;
	}
	//DUMPSTRING ("tag", (char *) tag, strlen("alg"));

	if (!strncmp(tag, "alg", strlen("alg"))) {
		if (CEK_ENC_ALG_RSA1_5 == params->alg_type) {
			str = "RSA1_5";
		} else if (CEK_ENC_ALG_RSA_OAEP == params->alg_type) {
			str = "RSA-OAEP";
		}
	} else if (!strncmp(tag, "enc", strlen("enc"))) {
		if (CONTENT_ENC_ALG_A128GCM == params->enc_type) {
			str = "A128GCM";
		} else if (CONTENT_ENC_ALG_A256GCM == params->enc_type) {
			str = "A256GCM";
		} else if (CONTENT_ENC_ALG_A128CBC_HS256 == params->enc_type) {
			str = "A128CBC-HS256";
		}
	} else if (!strncmp(tag, "kid", strlen("kid"))) {
		if (params->kid.value != NULL && params->kid.len > 0) {
			//ret = base64url_encode_nopad(params->kid.value, params->kid.len, kid, &kid_len);

			//if (ret != BASE64_OK)
			//{
			//    PEBBLE_LOG("base64url_encode_nopad for kid failed, ret = %d", ret);
			//    ret= JWE_CRYPT_ERROR_BASE64_ENCODE_FAILED;
			//    goto error;
			//}

			str = (char*) params->kid.value;
		}
	}

	if (!str) {
		PEBBLE_LOG("Invalid / Unsupported params for %s", tag);
		ret = JWE_GEN_ERROR_INVALID_INPUT_PARAM;
		goto error;
	}

	if (out_len <= strlen((str))) {
		PEBBLE_LOG("Insufficient buffer");
		ret = JWE_GEN_ERROR_INSUFFICIENT_BUFFER;
		goto error;
	}

	strcpy((char* )out, str);

	ret = JWE_OK;

	error:

	return ret;
}

/* Based on the input params, create a JWE Header structure as below
 * {"alg":"RSA1_5","enc":"A128CM", "kid": "xxxx"}
 */
static uint32_t create_header(jwe_params *params, uint8_t *out_data,
		uint32_t *out_len) {
	uint32_t ret = JWE_GEN_ERROR_INTERNAL;
	uint8_t val_str[JWE_MAX_HDR_VALUE_SIZE] = { 0 };
	int32_t tag_count = 0;
	int32_t i = 0;
	char *buf = NULL;
	uint32_t buf_len = 0;

	char *tag1 = "alg";
	char *tag2 = "enc";
	char *tag3 = "kid";
	const char *hdr_tags[] = { tag1, tag2
#ifndef TEST_JWE
			, tag3
#endif
			};

	if (!params || !out_data || !out_len || !*out_len) {
		PEBBLE_LOG("Invalid Parameters");
		return JWE_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	buf = (char*) out_data;
	*buf = 0;
	buf_len = *out_len;

	strlcat(buf, "{", buf_len);

	tag_count = sizeof(hdr_tags) / sizeof(*hdr_tags);

	for (i = 0; i < tag_count; i++) {
		ret = get_param_str(hdr_tags[i], params, val_str, sizeof(val_str));

		if (ret != JWE_OK) {
			PEBBLE_LOG("get_param_str failed, ret = %d", ret);
			goto error;
		}

		if (i != 0)
			strlcat(buf, ",", buf_len);

		strlcat(buf, "\"", buf_len);
		strlcat(buf, hdr_tags[i], buf_len);
		strlcat(buf, "\":", buf_len);

		strlcat(buf, "\"", buf_len);
		strlcat(buf, (const char*) val_str, buf_len);
		strlcat(buf, "\"", buf_len);
	}

	strlcat(buf, "}", buf_len);

	*out_len = strlen((char*) out_data);

#ifdef DEBUG_JWE
    DBG_LOG("%s", out_data);
#endif

	ret = JWE_OK;

	error:
	memset(val_str, 0, sizeof(val_str));

	return ret;
}

#ifndef TEST_JWE
static uint32_t get_cek_len(jwe_params *params) {
	switch (params->enc_type) {
	case CONTENT_ENC_ALG_A128GCM:
		return AES128_KEY_SIZE;

	case CONTENT_ENC_ALG_A256GCM:
		return AES256_KEY_SIZE;

	case CONTENT_ENC_ALG_A128CBC_HS256:
		return A128CBC_HS256_KEY_SIZE;

	default:
		return 0;
	}
}
#endif

static uint32_t get_iv_len(jwe_params *params) {
	switch (params->enc_type) {
	case CONTENT_ENC_ALG_A128GCM:
	case CONTENT_ENC_ALG_A256GCM:
		return AES_GCM_IV_SIZE;

	case CONTENT_ENC_ALG_A128CBC_HS256:
		return AES_HSHA2_IV_SIZE;

	default:
		return 0;
	}
}

static uint32_t validate_params(jwe_params *params, rsa_key_info_t *cek_key,
		const uint8_t *in_data, uint32_t in_len, uint8_t *out_data,
		uint32_t *out_len, uint32_t mode) {
	if (!params || !cek_key || !in_data || in_len <= 0
			|| in_len >= JWE_MAX_BUF_LEN || !out_data || !out_len
			|| *out_len <= 0) {
		PEBBLE_LOG("Invalid Params");
		return JWE_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	if (params->alg_type <= CEK_ENC_ALG_NONE
			|| params->alg_type >= CEK_ENC_ALG_MAX
			|| params->enc_type <= CONTENT_ENC_ALG_NONE
			|| params->enc_type >= CONTENT_ENC_ALG_MAX) {
		PEBBLE_LOG("Invalid CEK_ENC_ALG type");
		return JWE_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	if (params->alg_type == CEK_ENC_ALG_RSA1_5) // || params->alg_type == CEK_ENC_ALG_RSA_OAEP)
			{
		if (!cek_key || cek_key->modulus_len <= 0
				|| cek_key->pub_expo_len <= 0) {
			PEBBLE_LOG("Invalid Public Key");
			return JWE_GEN_ERROR_INVALID_INPUT_PARAM;
		}

		if (JWE_MODE_DEC == mode) {
			if (cek_key->priv_expo_len <= 0) {
				PEBBLE_LOG("Invalid Private Key");
				return JWE_GEN_ERROR_INVALID_INPUT_PARAM;
			}
		}
	}

	if (*out_len < JWE_MIN_BUF_LEN) {
		PEBBLE_LOG("Insufficient out len");
		return JWE_GEN_ERROR_INSUFFICIENT_BUFFER;
	}

	return JWE_OK;

}

/**
 * input: data (header/payload/sig/etc, not yet encoded to base64). first (if data is header)
 * output: out_data, a string of len <= out_len
 */
uint32_t encode_append(uint8_t *data, uint32_t data_len, uint8_t *out_data,
		uint32_t out_len,
		bool first, uint32_t *bytes_added, uint8_t **pos_added) {
	uint32_t ret = JWE_GEN_ERROR_INTERNAL;
	uint8_t *pOut = NULL;
	uint32_t tmp_len = 0;
	uint32_t total = 0;

	if (!data || !data_len || !out_data || !out_len) {
		PEBBLE_LOG("Invalid parameters");
		return JWE_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	if (!first) {
		total = strlcat((char*) out_data, ".", out_len);

		if (total >= out_len) {
			PEBBLE_LOG("Insufficient buffer");
			ret = JWE_GEN_ERROR_INSUFFICIENT_BUFFER;
			goto error;
		}
	}

	pOut = out_data + strlen((char*) out_data);
	tmp_len = out_len - strlen((char*) out_data);

	if (pOut >= (out_data + out_len) || tmp_len > out_len) {
		PEBBLE_LOG("Buffer out of bounds");
		ret = JWE_GEN_ERROR_INSUFFICIENT_BUFFER;
		goto error;
	}

	/* Base64 URL encode Header */
	ret = base64url_encode_nopad(data, data_len, pOut, &tmp_len);

	if (ret != BASE64_OK) {
		PEBBLE_LOG(
				"base64url_encode_nopad failed, ret = %d, data_len = %d, tmp_len = %d",
				ret, data_len, tmp_len);
		ret = JWE_CRYPT_ERROR_BASE64_ENCODE_FAILED;
		goto error;
	}

	if (bytes_added)
		*bytes_added = tmp_len;

	if (pos_added)
		*pos_added = pOut;

	ret = JWE_OK;

	error: pOut = NULL;

	return ret;
}

static uint32_t append_header(jwe_params *params, uint32_t *header_len,
		uint8_t *out_data, uint32_t out_len) {
	uint32_t ret = JWE_GEN_ERROR_INTERNAL;
	uint8_t buf[JWE_MAX_BUF_LEN] = { 0 };
	uint32_t buf_len = sizeof(buf);
	uint8_t *pOut = NULL;

	if (!params || !header_len || !out_data || !out_len) {
		PEBBLE_LOG("Invalid parameters");
		return JWE_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	/* Create Header */
	ret = create_header(params, buf, &buf_len);

	if (ret != JWE_OK) {
		PEBBLE_LOG("create_header failed, %d", ret);
		goto error;
	}

#ifdef TEST_JWE
    if (strcmp ((const char *)buf, (const char *)get_expected_jose_header()))
    {
        DBG_LOG("%s: strcmp (buf, get_expected_jose_header() failed", __FUNCTION__);
        ret = JWE_CRYPT_ERROR_UNEXPECTED_DATA;
        goto error;
    }
#endif

	ret = encode_append(buf, buf_len, out_data, out_len, true, header_len,
			&pOut);

	if (ret != JWE_OK) {
		PEBBLE_LOG("encode_append  failed, ret = %d", ret);
		goto error;
	}

#ifdef DEBUG_JWE
    DBG_LOG("After Header Encoding : strlen(out_data) = %d", strlen((char *)out_data));
    DUMPSTRING ("out_data", (char *) out_data, strlen((char *) out_data));
#endif

#ifdef TEST_JWE
    if (strcmp ((const char *)pOut, (const char *)get_expected_jose_header_b64()))
    {
        DBG_LOG("generate_jwe_payload: strcmp (buf, get_expected_jose_header_b64() failed");
        ret = JWE_CRYPT_ERROR_UNEXPECTED_DATA;
        goto error;
    }
#endif

	ret = JWE_OK;

	error:
	memset(buf, 0, sizeof(buf));
	buf_len = 0;
	pOut = NULL;

	return ret;
}

static uint32_t append_cek(jwe_params *params, rsa_key_info_t *cek_enc_key,
		uint8_t *cek, uint32_t *cek_len, uint8_t *out_data, uint32_t out_len) {
	uint32_t ret = JWE_GEN_ERROR_INTERNAL;
	uint8_t buf[JWE_MAX_BUF_LEN] = { 0 };
	uint32_t buf_len = sizeof(buf);
	uint32_t rsa_padding; //algorithm_t cek_enc_algo;
	uint32_t cek_len_tmp = 0;

	if (!params || !cek_enc_key || !cek || !cek_len || !*cek_len || !out_data
			|| !out_len) {
		PEBBLE_LOG("Invalid parameters");
		return JWE_GEN_ERROR_INVALID_INPUT_PARAM;
	}

#ifdef TEST_JWE
    cek_len_tmp = get_cek_len();

    if (*cek_len < cek_len_tmp)
    {
        PEBBLE_LOG("Insufficient buffer");
        ret = JWE_GEN_ERROR_INSUFFICIENT_BUFFER;
        goto error;
    }
    else
    {
        memcpy (cek, get_test_cek(), *cek_len);
        *cek_len = cek_len_tmp;
    }
#else
	cek_len_tmp = get_cek_len(params);

	if (*cek_len < cek_len_tmp) {
		PEBBLE_LOG("Insufficient buffer");
		ret = JWE_GEN_ERROR_INSUFFICIENT_BUFFER;
		goto error;
	}

	/* Generate CEK */
	ret = TZ_gen_rand_data(cek, &cek_len_tmp);

	if (ret != TZ_API_OK || cek_len_tmp != get_cek_len(params)) {
		PEBBLE_LOG("TZ_gen_rand_data failed for CEK, ret = %d", ret);
		ret = JWE_CRYPT_ERROR_GET_RANDOM_FAILED;
		goto error;
	}

	*cek_len = cek_len_tmp;

#endif

#ifdef TEST_JWE
    ret = get_test_modulus_exponent_d (cek_enc_key->rsaKey->modulus.value, &cek_enc_key->rsaKey->modulus.len,
                                       cek_enc_key->rsaKey->exponent.value, &cek_enc_key->rsaKey->exponent.len,
                                       NULL, NULL);

    if (ret != JWE_OK)
    {
        PEBBLE_LOG("get_test_modulus_exponent failed, ret = %d", ret);
        goto error;
    }

    DBG_LOG("test modulus : mod_len = %d", cek_enc_key->rsaKey->modulus.len);
    DBG_DUMP(cek_enc_key->rsaKey->modulus.value, cek_enc_key->rsaKey->modulus.len);
    DBG_LOG("test exponent : exp_len = %d", cek_enc_key->rsaKey->exponent.len);
    DBG_DUMP(cek_enc_key->rsaKey->exponent.value, cek_enc_key->rsaKey->exponent.len);
#endif

	if (CEK_ENC_ALG_RSA1_5 == params->alg_type) {
		rsa_padding = RSA_PKCS1_PADDING; //cek_enc_algo = ALGO_RSA_PKCS1;
	} else if (CEK_ENC_ALG_RSA_OAEP == params->alg_type) {
		rsa_padding = RSA_PKCS1_OAEP_PADDING; //cek_enc_algo = ALGO_RSA_OAEP;
	} else {
		PEBBLE_LOG("Unsupported CEK Encryption algorithm");
		ret = JWE_GEN_ERROR_INVALID_INPUT_PARAM;
		goto error;
	}

	/* Encrypt CEK with Receipient's public key */
	ret = pebble_rsa_encrypt(cek_enc_key->modulus, cek_enc_key->modulus_len,
			cek_enc_key->pub_expo, cek_enc_key->pub_expo_len, cek, *cek_len,
			buf, &buf_len, rsa_padding);

	if (ret != TZ_API_OK) {
		PEBBLE_LOG("TZ_rsa_encrypt CEK failed, ret = %d", ret);
		ret = JWE_CRYPT_ERROR_ENCRYPT_ERROR;
		goto error;
	}

	ret = encode_append(buf, buf_len, out_data, out_len, false, NULL, NULL);

	if (ret != JWE_OK) {
		PEBBLE_LOG("encode_append  failed, ret = %d", ret);
		goto error;
	}

#ifdef DEBUG_JWE
    DBG_LOG("After Enc CEK Encoding : strlen(out_data) = %d", strlen((char *)out_data));
    DUMPSTRING ("out_data", (char *) out_data, strlen((char *) out_data));
#endif

	ret = JWE_OK;

	error:
	memset(buf, 0, sizeof(buf));
	buf_len = 0;

	return ret;
}

static uint32_t append_iv(jwe_params *params, uint8_t *iv, uint32_t *iv_len,
		uint8_t *out_data, uint32_t out_len) {
	uint32_t ret = JWE_GEN_ERROR_INTERNAL;
	uint8_t *pOut = NULL;
	uint32_t iv_len_tmp = 0;

	if (!params || !iv || !iv_len || !*iv_len || !out_data || !out_len) {
		PEBBLE_LOG("Invalid parameters");
		return JWE_GEN_ERROR_INVALID_INPUT_PARAM;
	}

#ifdef TEST_JWE
    ret = get_test_iv (iv, &iv_len_tmp);

    if (*iv_len < iv_len_tmp)
    {
        PEBBLE_LOG("Insufficient buffer");
        ret = JWE_GEN_ERROR_INSUFFICIENT_BUFFER;
        goto error;
    }

#else
	iv_len_tmp = get_iv_len(params);

	if (*iv_len < iv_len_tmp) {
		PEBBLE_LOG("Insufficient buffer");
		ret = JWE_GEN_ERROR_INSUFFICIENT_BUFFER;
		goto error;
	}

	/* Generate IV */
	ret = TZ_gen_rand_data(iv, &iv_len_tmp);

#endif

	if (ret != TZ_API_OK || iv_len_tmp != get_iv_len(params)) {
		PEBBLE_LOG("TZ_gen_rand_data failed for IV, ret = %d", ret);
		ret = JWE_CRYPT_ERROR_GET_RANDOM_FAILED;
		goto error;
	}

	*iv_len = iv_len_tmp;

#ifdef DEBUG_JWE
    DBG_LOG("IV Bytes");
    DBG_DUMP(iv, *iv_len);
#endif

	ret = encode_append(iv, *iv_len, out_data, out_len, false, NULL, &pOut);

	if (ret != JWE_OK) {
		PEBBLE_LOG("encode_append  failed, ret = %d", ret);
		goto error;
	}

#ifdef TEST_JWE
    if (strcmp ((const char *)pOut, (const char *)get_expected_enc_iv()))
    {
        DBG_LOG("generate_jwe_payload:encode iv failed, %d", ret);
        ret = JWE_CRYPT_ERROR_UNEXPECTED_DATA;
        goto error;
    }
#endif

#ifdef DEBUG_JWE
    DBG_LOG(":After Enc IV Encoding : strlen(out_data) = %d", strlen((char *)out_data));
    DUMPSTRING ("out_data", (char *) out_data, strlen((char *) out_data));
#endif

	ret = JWE_OK;

	error: pOut = NULL;

	return ret;
}

static uint32_t append_cipher_text(jwe_params *params, uint32_t header_len,
		uint8_t *cek, uint32_t cek_len, uint8_t *iv, uint32_t iv_len,
		const uint8_t *in_data, uint32_t in_len, uint8_t *tag,
		uint32_t *tag_len, uint8_t *out_data, uint32_t out_len) {
	uint32_t ret = JWE_GEN_ERROR_INTERNAL;
	uint8_t buf[JWE_MAX_BUF_LEN] = { 0 };
	uint32_t buf_len = sizeof(buf);
	uint32_t cipher_text_len = 0;
	uint8_t *pOut = NULL;

	if (!params || !header_len || !cek || !cek_len || !iv || !iv_len || !in_data
			|| !in_len || !tag || !tag_len || !*tag_len || !out_data
			|| !out_len) {
		PEBBLE_LOG("Invalid parameters");
		return JWE_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	if (in_len > sizeof(buf)) {
		PEBBLE_LOG("Insufficient buffer");
		ret = JWE_GEN_ERROR_INSUFFICIENT_BUFFER;
		goto error;
	}

	memcpy(buf, in_data, in_len);

#ifdef TEST_JWE
    if (memcmp (out_data, get_expected_aad(), get_expected_aad_len()))
    {
        DBG_LOG("generate_jwe_payload: expected aad data doesn't match actual");
        ret = JWE_CRYPT_ERROR_UNEXPECTED_DATA;
        goto error;
    }
#endif

	/* Set Authentication Data = ASCII(BASE64URL(UTF8(JWE Protected Header)))
	 *   - Header bytes are available at the front of out_data, of header_len
	 */

	/* Perform Authentication Encryption on PlainText with Encryption algorithm */
	/* Using CEK, IV, Authentication Data */
	/* Generates Cipher Text and Authentication Tag */

	switch (params->enc_type) {
	case CONTENT_ENC_ALG_A128GCM:
	case CONTENT_ENC_ALG_A256GCM: {
		ret = pebble_aes_gcm_encrypt(cek, cek_len, iv, iv_len, out_data,
				header_len, in_data, in_len, buf, tag);

		if (ret != SUCCESS) {
			PEBBLE_LOG("aes_gcm_encrypt failed, ret = %d", ret);
			ret = JWE_CRYPT_ERROR_ENCRYPT_ERROR;
			goto error;
		}
		cipher_text_len = in_len;
		*tag_len = AES_GCM_TAG_SIZE;
	}
		break;

	case CONTENT_ENC_ALG_A128CBC_HS256: {
		aead_enc_in_params in = { 0 };
		aead_enc_out_params out = { 0 };
		in.key = cek;
		in.key_len = cek_len;
		in.iv = iv;
		in.iv_len = iv_len;
		in.aad = out_data;
		in.aad_len = header_len;
		in.plain_text = (uint8_t*) in_data;
		in.plain_text_len = in_len;
		out.cipher_text = buf;
		out.cipher_text_len = &buf_len;
		out.tag = tag;
		out.tag_len = tag_len;

		ret = aes_hsha2_aead_encrypt(A128CBC_HS256, &in, &out);

		if (ret != JWE_OK) {
			PEBBLE_LOG("aes_hsha2_aaed_encrypt failed, ret = %d", ret);
			ret = JWE_CRYPT_ERROR_ENCRYPT_ERROR;
			goto error;
		}

		cipher_text_len = buf_len;
	}
		break;

	default:
		PEBBLE_LOG("Un-supported content encryption type")
		;
		ret = JWE_GEN_ERROR_INVALID_INPUT_PARAM;
		goto error;
	}

#ifdef DEBUG_JWE
    DBG_LOG("CipherText Bytes");
    DBG_DUMP(buf, cipher_text_len);

    DBG_LOG("Tag Bytes");
    DBG_DUMP(tag, *tag_len);
#endif

#ifdef TEST_JWE
    if (memcmp (buf, get_expected_cipher_text(), get_expected_cipher_text_len()))
    {
        DBG_LOG("expected cipher text bytes doesn't match actual");
        ret = JWE_CRYPT_ERROR_UNEXPECTED_DATA;
        goto error;
    }

    if (memcmp (tag, get_expected_auth_tag(), get_expected_auth_tag_len()))
    {
        DBG_LOG("expected auth tag bytes doesn't match actual");
        ret = JWE_CRYPT_ERROR_UNEXPECTED_DATA;
        goto error;
    }
#endif

	ret = encode_append(buf, cipher_text_len, out_data, out_len, false, NULL,
			&pOut);

	if (ret != JWE_OK) {
		PEBBLE_LOG("encode_append  failed, ret = %d", ret);
		goto error;
	}

#ifdef TEST_JWE
    if (strcmp((const char *)pOut, (const char *)get_expected_enc_cipher_text()))
    {
        PEBBLE_LOG("expected encoded cipher text doesn't match actual");
        ret = JWE_CRYPT_ERROR_UNEXPECTED_DATA;
        goto error;
    }
#endif

#ifdef DEBUG_JWE
    DBG_LOG("After CipherText Encoding : strlen(out_data) = %d", strlen((char *)out_data));
    DUMPSTRING ("out_data", (char *) out_data, strlen((char *) out_data));
#endif

	ret = JWE_OK;

	error:
	memset(buf, 0, sizeof(buf));
	buf_len = 0;
	pOut = NULL;

	return ret;
}

static uint32_t append_tag(uint8_t *tag, uint32_t tag_len, uint8_t *out_data,
		uint32_t out_len) {
	uint32_t ret = JWE_GEN_ERROR_INTERNAL;
	uint8_t *pOut = NULL;

	if (!tag || !tag_len || !out_data || !out_len) {
		PEBBLE_LOG("Invalid parameters");
		return JWE_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	ret = encode_append(tag, tag_len, out_data, out_len, false, NULL, &pOut);

	if (ret != JWE_OK) {
		PEBBLE_LOG("encode_append  failed, ret = %d", ret);
		goto error;
	}

#ifdef TEST_JWE
    if (strcmp((const char *)pOut, (const char *)get_expected_enc_auth_tag()))
    {
        DBG_LOG("generate_jwe_payload: expected encoded auth tag doesn't match actual");
        ret = JWE_CRYPT_ERROR_UNEXPECTED_DATA;
        goto error;
    }
#endif

	ret = JWE_OK;

	error: pOut = NULL;
	return ret;
}

uint32_t generate_jwe_payload(jwe_params *params, rsa_key_info_t *cek_enc_key,
		const uint8_t *in_data, uint32_t in_len, uint8_t *out_data,
		uint32_t *out_len) {
	uint32_t ret = JWE_GEN_ERROR_INTERNAL;
	uint8_t cek[JWE_MAX_CEK_SIZE] = { 0 };
	uint32_t cek_len = sizeof(cek);
	uint8_t iv[JWE_MAX_IV_SIZE] = { 0 };
	uint32_t iv_len = sizeof(iv);
	uint32_t header_len = 0;
	uint8_t tag[JWE_MAX_TAG_SIZE] = { 0 };
	uint32_t tag_len = sizeof(tag);
	uint32_t total = 0;

	ret = validate_params(params, cek_enc_key, in_data, in_len, out_data,
			out_len, JWE_MODE_ENC);

	if (ret != JWE_OK) {
		PEBBLE_LOG("validate_params failed, ret = %d", ret);
		goto error;
	}

	/* Assemble final representation in  Compact Serialization
	 * BASE64URL(UTF8(JWE Protected Header)) ||
	 * ’.’ || BASE64URL(JWE Encrypted Key) ||
	 * ’.’ || BASE64URL(JWE Initialization Vector) ||
	 * ’.’ || BASE64URL(JWE Ciphertext) ||
	 * ’.’ || BASE64URL(JWE Authentication Tag)
	 *
	 */

	ret = append_header(params, &header_len, out_data, *out_len);

	if (ret != JWE_OK) {
		PEBBLE_LOG("append_header failed, ret = %d", ret);
		goto error;
	}

	ret = append_cek(params, cek_enc_key, cek, &cek_len, out_data, *out_len);

	if (ret != JWE_OK) {
		PEBBLE_LOG("append_cek failed, ret = %d", ret);
		goto error;
	}

	ret = append_iv(params, iv, &iv_len, out_data, *out_len);

	if (ret != JWE_OK) {
		PEBBLE_LOG("append_iv failed, ret = %d", ret);
		goto error;
	}

	ret = append_cipher_text(params, header_len, cek, cek_len, iv, iv_len,
			in_data, in_len, tag, &tag_len, out_data, *out_len);

	if (ret != JWE_OK) {
		PEBBLE_LOG("append_cipher_text  failed, ret = %d", ret);
		//DUMPSTRING ("generate_jwe_payload: after append_cipher_text", (char *) out_data, *out_len);

		goto error;
	}

	ret = append_tag(tag, tag_len, out_data, *out_len);

	DUMPSTRING ("generate_jwe_payload: after append_tag", (char *) out_data, *out_len);

	if (ret != JWE_OK) {
		PEBBLE_LOG("append_tag  failed, ret = %d", ret);
		goto error;
	}

	total = strlen((char*) out_data);

	if (total >= *out_len) {
		PEBBLE_LOG("Insufficient buffer");
		ret = JWE_GEN_ERROR_INSUFFICIENT_BUFFER;
		goto error;
	}

	*out_len = total;

#ifdef DEBUG_JWE
    DBG_LOG("Final Encoding : *out_len = %d", *out_len);
    DUMPSTRING ("out_data", (char *) out_data, *out_len);
#endif

	error:
	/* clear memory */
	memset(cek, 0, sizeof(cek));
	cek_len = 0;
	memset(iv, 0, sizeof(iv));
	iv_len = 0;
	memset(tag, 0, sizeof(tag));
	tag_len = 0;

	return ret;
}

/* Expects a NULL terminated "in" pointer */
static uint32_t decode_extract(char *label, const uint8_t *in, bool first,
		uint8_t *out, uint32_t *out_len, uint8_t **enc, uint32_t *enc_len) {
	uint32_t ret = JWE_GEN_ERROR_INTERNAL;
	uint8_t *encoded = NULL;
	uint32_t encoded_len = 0;

	if ((first && !in) || !out || !out_len || !*out_len || !label) {
		PEBBLE_LOG("Invalid params");
		return JWE_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	if (first)
		encoded = (uint8_t*) strtok((char*) in, ".");
	else
		encoded = (uint8_t*) strtok(NULL, ".");

	if (!encoded) {
		PEBBLE_LOG("Missing token");
		ret = JWE_GEN_ERROR_MISSING_DATA;
		goto error;
	}

	encoded_len = strlen((char*) encoded);

	if (enc && enc_len) {
		*enc = encoded;
		*enc_len = encoded_len;
	}

#ifdef DEBUG_JWE
    DBG_LOG("Encoded %s bytes", label);
    DUMPSTRING("Encoded bytes", (char *)encoded, encoded_len);
#endif

	ret = base64url_decode_nopad(encoded, encoded_len, out, out_len);

	if (ret != BASE64_OK) {
		PEBBLE_LOG("base64url_decode failed for %s, ret = %d", label, ret);
		ret = JWE_CRYPT_ERROR_BASE64_DECODE_FAILED;
		goto error;
	}

#ifdef DEBUG_JWE
    DBG_LOG("Decoded %s Bytes", label);
    DBG_DUMP(out, *out_len);
#endif

	ret = JWE_OK;

	error: encoded = NULL;

	return ret;
}

/* Exact speficied field value */
static uint32_t extract_header_field_internal(char *tag, uint8_t *hdr,
		uint32_t hdr_len, char *value, uint32_t *value_len) {
	uint32_t ret = JWE_GEN_ERROR_INTERNAL;
	uint8_t buf[JWE_HEADER_BUF_LEN] = { 0 };
	JSON_Value *parsed_json = NULL;
	JSON_Object *json_object = NULL;
	const char *field_str = NULL;

	if (!tag || !hdr || !hdr_len) {
		PEBBLE_LOG("Invalid params");
		return JWE_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	if (hdr_len >= sizeof(buf)) {
		PEBBLE_LOG("Insufficient buffer");
		return JWE_GEN_ERROR_INSUFFICIENT_BUFFER;
	}

	/* Make a NULL terminated string out of header, for json parsing */
	memcpy(buf, hdr, hdr_len);
	buf[hdr_len] = 0;

	DBG_LOG("extract_header_field_internal 1");
	json_parser_init();

	DBG_LOG("extract_header_field_internal 2");
	parsed_json = json_parse_string((const char*) buf);
	DBG_LOG("extract_header_field_internal 3");

	if (parsed_json == NULL) {
		PEBBLE_LOG("json_parse_string failed");
		ret = JWE_PROCESS_CP_JSONPARSE_ERROR;
		goto error;
	}

	DBG_LOG("extract_header_field_internal 4");
	json_object = json_value_get_object(parsed_json);
	DBG_LOG("extract_header_field_internal 5");

	if (json_object == NULL) {
		PEBBLE_LOG("json_value_get_object failed");
		ret = JWE_PROCESS_CP_JSONPARSE_ERROR;
		goto error;
	}

	DBG_LOG("extract_header_field_internal 5");
	field_str = json_object_get_string(json_object, tag);
	DBG_LOG("extract_header_field_internal 6");

	if (!field_str) {
		PEBBLE_LOG("%s not found in header", tag);
		ret = JWE_GEN_ERROR_MISSING_DATA;
		goto error;
	}

	strcpy(value, field_str); DBG_LOG("extract_header_field_internal 7");

	*value_len = strlen(value);

#ifdef DEBUG_JWE
    DBG_LOG("extract_header_field_internal: Extracted Header Field is %s", value);
#endif
	ret = JWE_OK;

	error: if (parsed_json) {
		json_value_free(parsed_json);
		parsed_json = NULL;
	}
	memset(buf, 0, sizeof(buf));
	parsed_json = NULL;
	json_object = NULL;
	value = NULL;

	return ret;
} // Zheng Zhou

uint32_t extract_header_field(char *tag, uint8_t *in_data, uint32_t in_data_len,
		char *value, uint32_t *value_len) {

	uint8_t dec_buf[JWE_MAX_BUF_LEN];
	uint8_t in_buf[JWE_MAX_BUF_LEN];
	uint8_t *hdr = NULL;
	uint32_t hdr_len = 0;

	uint32_t ret = JWE_GEN_ERROR_INTERNAL;
	uint8_t *enc_hdr = NULL;
	uint32_t enc_hdr_len = 0;

	if (!tag || !in_data || !in_data_len) {
		PEBBLE_LOG("Invalid params");
		return JWE_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	if (in_data_len >= JWE_MAX_BUF_LEN) {

		PEBBLE_LOG("Not enough buffer");
		return JWE_GEN_ERROR_INVALID_INPUT_PARAM;

	}

	memcpy(in_buf, in_data, in_data_len);
	in_buf[in_data_len] = 0;

	hdr = dec_buf;
	hdr_len = sizeof(dec_buf);

	ret = decode_extract("Header", in_buf, true, hdr, &hdr_len, &enc_hdr,
			&enc_hdr_len);

	if (ret != JWE_OK || !enc_hdr || !enc_hdr_len) {
		PEBBLE_LOG("decode_extract failed, ret = %d", ret);
		return ret;
	}

	ret = extract_header_field_internal(tag, hdr, hdr_len, value, value_len);

	if (ret != JWE_OK) {
		PEBBLE_LOG("extract_header_field failed, ret = %d", ret);
		return ret;
	}
#ifdef DEBUG_JWE
    DBG_LOG("extract_header_field: Extracted Header Field is %s", value);
#endif

	return JWE_OK;
}

/* Verify header against supported algorithms & key id */
static uint32_t verify_header(jwe_params *params, uint8_t *hdr,
		uint32_t hdr_len) {
	uint32_t ret = JWE_GEN_ERROR_INTERNAL;
	uint8_t buf[JWE_HEADER_BUF_LEN] = { 0 };
	JSON_Value *parsed_json = NULL;
	JSON_Object *json_object = NULL;
	const char *value = NULL;
	int32_t i = 0;
	int32_t tag_count = 0;
	uint8_t expected_str[JWE_MAX_HDR_VALUE_SIZE] = { 0 };

	char *tag1 = "alg";
	char *tag2 = "enc";
	//char *tag3 =  "kid";
	const char *hdr_tags[] = { tag1, tag2
	//#ifndef TEST_JWE
	//    , tag3
	//#endif
			};

	if (!params || !hdr || !hdr_len
#ifndef TEST_JWE
			|| !params->kid.value || !params->kid.len
#endif
			) {
		PEBBLE_LOG("Invalid params");
		return JWE_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	if (hdr_len >= sizeof(buf)) {
		PEBBLE_LOG("Insufficient buffer");
		return JWE_GEN_ERROR_INSUFFICIENT_BUFFER;
	}

	/* Make a NULL terminated string out of header, for json parsing */
	memcpy(buf, hdr, hdr_len);
	buf[hdr_len] = 0;

	json_parser_init();

	parsed_json = json_parse_string((const char*) buf);

	if (parsed_json == NULL) {
		PEBBLE_LOG("json_parse_string failed");
		ret = JWE_PROCESS_CP_JSONPARSE_ERROR;
		goto error;
	}

	json_object = json_value_get_object(parsed_json);

	if (json_object == NULL) {
		PEBBLE_LOG("json_value_get_object failed");
		ret = JWE_PROCESS_CP_JSONPARSE_ERROR;
		goto error;
	}

	tag_count = sizeof(hdr_tags) / sizeof(*hdr_tags);

	for (i = 0; i < tag_count; i++) {
		value = json_object_get_string(json_object, hdr_tags[i]);

		if (!value) {
			PEBBLE_LOG("%s not found in header", hdr_tags[i]);
			ret = JWE_GEN_ERROR_MISSING_DATA;
			goto error;
		}

		ret = get_param_str(hdr_tags[i], params, expected_str,
				sizeof(expected_str));

		if (ret != JWE_OK) {
			PEBBLE_LOG("get_param_str failed, ret = %d", ret);
			goto error;
		}

		if (strncmp(value, (const char*) expected_str,
				strlen((const char*) expected_str))) {
			PEBBLE_LOG("Header tags %s mismatch, expected = %s, actual = %s",
					hdr_tags[i], expected_str, value); DBG_LOG("expected = %s", expected_str); DBG_LOG("actual = %s", value);
			ret = JWE_CRYPT_ERROR_UNEXPECTED_DATA;
			goto error;
		}
	}

	ret = JWE_OK;

	error: if (parsed_json) {
		json_value_free(parsed_json);
		parsed_json = NULL;
	}
	memset(buf, 0, sizeof(buf));
	parsed_json = NULL;
	json_object = NULL;
	value = NULL;

	return ret;
}

static uint32_t extract_header(jwe_params *params, const uint8_t *in,
		uint8_t *hdr, uint32_t *hdr_len) {
	uint32_t ret = JWE_GEN_ERROR_INTERNAL;
	uint8_t *enc_hdr = NULL;
	uint32_t enc_hdr_len = 0;
	uint32_t orig_hdr_len = 0;

	if (!params || !in || !hdr || !hdr_len || !*hdr_len) {
		PEBBLE_LOG("Invalid params");
		return JWE_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	orig_hdr_len = *hdr_len;

	ret = decode_extract("Header", in, true, hdr, hdr_len, &enc_hdr,
			&enc_hdr_len);

	if (ret != JWE_OK || !enc_hdr || !enc_hdr_len) {
		PEBBLE_LOG("decode_extract failed, ret = %d", ret);
		return ret;
	}

	ret = verify_header(params, hdr, *hdr_len);

	if (ret != JWE_OK) {
		PEBBLE_LOG("verify_header failed, ret = %d", ret);
		return ret;
	}

	if (orig_hdr_len < enc_hdr_len) {
		PEBBLE_LOG("Insufficient buffer for encoded header");
		return JWE_GEN_ERROR_INSUFFICIENT_BUFFER;
	}

	/* AAD is ASCII(BASE64URL(UTF8(JWE Protected Header)))
	 * Hence returning encoded header, instead of decoded.
	 */
	memcpy(hdr, enc_hdr, enc_hdr_len);
	*hdr_len = enc_hdr_len;

	return JWE_OK;
}

static uint32_t extract_cek(jwe_params *params, rsa_key_info_t *cek_dec_key,
		uint8_t *cek, uint32_t *cek_len) {
	uint32_t ret = JWE_GEN_ERROR_INTERNAL;
	uint8_t dec_buf[JWE_MAX_BUF_LEN] = { 0 };
	uint32_t dec_len = sizeof(dec_buf);
	uint8_t *encrypted_cek = dec_buf;
	uint32_t encrypted_cek_len = dec_len;
	//algorithm_t cek_enc_algo;
	uint32_t rsa_padding;

	if (!params || !cek_dec_key || !cek || !cek_len || !*cek_len) {
		PEBBLE_LOG("Invalid params");
		return JWE_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	ret = decode_extract("CEK", NULL, false, encrypted_cek, &encrypted_cek_len,
			NULL, NULL);

	if (ret != JWE_OK) {
		PEBBLE_LOG("decode_extract failed for cek, ret = %d", ret);
		goto error;
	}

#ifdef TEST_JWE
    ret = get_test_modulus_exponent_d (cek_dec_key->rsaKey->modulus.value, &cek_dec_key->rsaKey->modulus.len,
                                       cek_dec_key->rsaKey->exponent.value, &cek_dec_key->rsaKey->exponent.len,
                                       cek_dec_key->rsaKey->privateExponent.value, &cek_dec_key->rsaKey->privateExponent.len);

    if (ret != JWE_OK)
    {
        PEBBLE_LOG("get_test_modulus_exponent_d failed, ret = %d", ret);
        goto error;
    }
#endif

#ifdef DEBUG_JWE
    DBG_LOG("test modulus : mod_len = %d", cek_dec_key->rsaKey->modulus.len);
    DBG_DUMP(cek_dec_key->rsaKey->modulus.value, cek_dec_key->rsaKey->modulus.len);
    DBG_LOG("test exponent : exp_len = %d", cek_dec_key->rsaKey->exponent.len);
    DBG_DUMP(cek_dec_key->rsaKey->exponent.value, cek_dec_key->rsaKey->exponent.len);
    DBG_LOG("test d : d_len = %d", cek_dec_key->rsaKey->privateExponent.len);
    DBG_DUMP(cek_dec_key->rsaKey->privateExponent.value, cek_dec_key->rsaKey->privateExponent.len);
#endif

	if (CEK_ENC_ALG_RSA1_5 == params->alg_type) {
		//cek_enc_algo = ALGO_RSA_PKCS1;
		rsa_padding = RSA_PKCS1_PADDING;
	} else if (CEK_ENC_ALG_RSA_OAEP == params->alg_type) {
		//cek_enc_algo = ALGO_RSA_OAEP;
		rsa_padding = RSA_PKCS1_OAEP_PADDING;
	} else {
		PEBBLE_LOG("Unsupported CEK Encryption algorithm");
		ret = JWE_GEN_ERROR_INVALID_INPUT_PARAM;
		goto error;
	}

	ret = pebble_rsa_decrypt(cek_dec_key->modulus, cek_dec_key->modulus_len,
			cek_dec_key->pub_expo, cek_dec_key->pub_expo_len,
			cek_dec_key->priv_expo, cek_dec_key->priv_expo_len, encrypted_cek,
			encrypted_cek_len, cek, cek_len, rsa_padding);

	if (ret != TZ_API_OK) {
		PEBBLE_LOG("TZ_rsa_decrypt_with_alg failed for CEK, ret = %d", ret);
		ret = JWE_CRYPT_ERROR_ENCRYPT_ERROR;
		goto error;
	}

#ifdef DBEUG_JWE
    DBG_LOG("Decrypted CEK Bytes");
    DBG_DUMP(cek, *cek_len);
#endif

	ret = JWE_OK;

	error:
	memset(dec_buf, 0, sizeof(dec_buf));
	encrypted_cek = NULL;
	return ret;
}

static uint32_t extract_iv(jwe_params *params, uint8_t *iv, uint32_t *iv_len) {
	uint32_t ret = JWE_GEN_ERROR_INTERNAL;

	if (!params || !iv || !iv_len || !*iv_len) {
		PEBBLE_LOG("Invalid params");
		return JWE_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	ret = decode_extract("IV", NULL, false, iv, iv_len, NULL, NULL);

	if (ret != JWE_OK) {
		PEBBLE_LOG("decode_extract failed for IV, ret = %d", ret);
		return ret;
	}

	if (*iv_len != get_iv_len(params)) {
		PEBBLE_LOG("Unexpected IV size");
		return JWE_CRYPT_ERROR_UNEXPECTED_DATA;
	}

	return JWE_OK;
}

static uint32_t extract_cipher_text(uint8_t *cipher_text,
		uint32_t *cipher_text_len) {
	uint32_t ret = JWE_GEN_ERROR_INTERNAL;

	if (!cipher_text || !cipher_text_len || !*cipher_text_len) {
		PEBBLE_LOG("Invalid params");
		return JWE_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	ret = decode_extract("CipherText", NULL, false, cipher_text,
			cipher_text_len, NULL, NULL);

	if (ret != JWE_OK) {
		PEBBLE_LOG("decode_extract failed for Cipher Text, ret = %d", ret);
		return ret;
	}

	return JWE_OK;
}

static uint32_t extract_tag(uint8_t *tag, uint32_t *tag_len) {
	uint32_t ret = JWE_GEN_ERROR_INTERNAL;

	if (!tag || !tag_len || !*tag_len) {
		PEBBLE_LOG("Invalid params");
		return JWE_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	ret = decode_extract("Tag", NULL, false, tag, tag_len, NULL, NULL);

	if (ret != JWE_OK) {
		PEBBLE_LOG("decode_extract failed for Tag, ret = %d", ret);
		return ret;
	}

	return JWE_OK;
}

static uint32_t extract_plain_text(jwe_params *params, uint8_t *cek,
		uint32_t cek_len, uint8_t *iv, uint32_t iv_len, uint8_t *hdr,
		uint32_t hdr_len, uint8_t *ct, uint32_t ct_len, uint8_t *tag,
		uint32_t tag_len, uint8_t *out_data, uint32_t *out_len) {
	uint32_t ret = JWE_GEN_ERROR_INTERNAL;

	if (!params || !cek || !cek_len || !iv || !iv_len || !hdr || !hdr_len || !ct
			|| !ct_len || !tag || !tag_len || !out_data || !out_len
			|| !*out_len) {
		PEBBLE_LOG("Invalid params");
		return JWE_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	switch (params->enc_type) {
	case CONTENT_ENC_ALG_A128GCM:
	case CONTENT_ENC_ALG_A256GCM: {
		ret = pebble_aes_gcm_decrypt(cek, cek_len, iv, iv_len, hdr, hdr_len, ct,
				ct_len, tag, tag_len, out_data);
		if (ret != SUCCESS) {
			PEBBLE_LOG("aes_gcm_decrypt failed, ret = %d", ret);
			ret = JWE_CRYPT_ERROR_DECRYPT_ERROR;
			goto error;
		}
		if (ct_len > *out_len) // i.e. pt_len > *out_len
				{
			PEBBLE_LOG("Insufficient buffer after aes_gcm_decrypt");
			ret = JWE_GEN_ERROR_INSUFFICIENT_BUFFER;
			goto error;
		}
		*out_len = ct_len;
	}
		break;

	case CONTENT_ENC_ALG_A128CBC_HS256: {
		aead_dec_in_params in = { 0 };
		aead_dec_out_params out = { 0 };

		in.key = cek;
		in.key_len = cek_len;
		in.iv = iv;
		in.iv_len = iv_len;
		in.aad = hdr;
		in.aad_len = hdr_len;
		in.tag = tag;
		in.tag_len = tag_len;
		in.cipher_text = ct;
		in.cipher_text_len = ct_len;
		out.plain_text = out_data;
		out.plain_text_len = out_len;

		ret = aes_hsha2_aead_decrypt(A128CBC_HS256, &in, &out);

		if (ret != JWE_OK) {
			PEBBLE_LOG("aes_hsha2_aead_decrypt failed, ret =  %d", ret);
			memset(&in, 0, sizeof(in));
			memset(&out, 0, sizeof(out));
			goto error;
		}
	}
		break;

	default:
		PEBBLE_LOG("Unsupported content encryption algorithm")
		;
		ret = JWE_GEN_ERROR_INVALID_INPUT_PARAM;
		goto error;
	}

	ret = JWE_OK;

	error:

	return ret;
}

uint32_t extract_jwe_payload_internal(jwe_params *params,
		rsa_key_info_t *cek_dec_key, const uint8_t *in_data, uint32_t in_len,
		uint8_t *out_data, uint32_t *out_len) {
	uint32_t ret = JWE_GEN_ERROR_INTERNAL;
	uint8_t in_buf[JWE_MAX_BUF_LEN];
	uint8_t dec_buf[JWE_MAX_BUF_LEN];
	uint32_t dec_len = sizeof(dec_buf);
	uint8_t *cek = NULL;
	uint32_t cek_len = 0;
	uint8_t *iv = NULL;
	uint32_t iv_len = 0;
	uint8_t *hdr = NULL;
	uint32_t hdr_len = 0;
	uint8_t *cipher_text = NULL;
	uint32_t cipher_text_len = 0;
	uint8_t *tag = NULL;
	uint32_t tag_len = 0;
	uint32_t total = 0;

	ret = validate_params(params, cek_dec_key, in_data, in_len, out_data,
			out_len, JWE_MODE_DEC);

	if (ret != JWE_OK) {
		PEBBLE_LOG("validate_params failed, ret = %d", ret);
		return ret;
	}

	memset(out_data, 0, *out_len);

	/* "=", to NULL terminate the last byte, for strtok to work */
	if (in_len >= sizeof(in_buf)) {
		PEBBLE_LOG("Insufficient buffer for plaintext");
		return JWE_GEN_ERROR_INSUFFICIENT_BUFFER;
	}

	memcpy(in_buf, in_data, in_len);
	in_buf[in_len] = 0;

	total = 0;
	hdr = dec_buf;
	hdr_len = sizeof(dec_buf);

	ret = extract_header(params, in_buf, hdr, &hdr_len);

	if (ret != JWE_OK) {
		PEBBLE_LOG("extract_header failed, ret = %d", ret);
		goto error;
	}

	total += hdr_len;
	cek = dec_buf + total;
	cek_len = sizeof(dec_buf) - total;

	ret = extract_cek(params, cek_dec_key, cek, &cek_len);

	if (ret != JWE_OK) {
		PEBBLE_LOG("extract_cek failed, ret = %d", ret);
		goto error;
	}

	total += cek_len;
	iv = dec_buf + total;
	iv_len = sizeof(dec_buf) - total;

	ret = extract_iv(params, iv, &iv_len);

	if (ret != JWE_OK) {
		PEBBLE_LOG("extract_iv failed, ret = %d", ret);
		goto error;
	}

	total += iv_len;
	cipher_text = dec_buf + total;
	cipher_text_len = dec_len - total;

	ret = extract_cipher_text(cipher_text, &cipher_text_len);

	if (ret != JWE_OK) {
		PEBBLE_LOG("extract_cipher_text failed, ret = %d", ret);
		goto error;
	}

	total += cipher_text_len;
	tag = dec_buf + total;
	tag_len = dec_len - total;

	ret = extract_tag(tag, &tag_len);

	if (ret != JWE_OK) {
		PEBBLE_LOG("extract_tag failed, ret = %d", ret);
		goto error;
	}

	ret = extract_plain_text(params, cek, cek_len, iv, iv_len, hdr, hdr_len,
			cipher_text, cipher_text_len, tag, tag_len, out_data, out_len);
	if (ret != JWE_OK) {
		PEBBLE_LOG("extract_plain_text failed, ret = %d", ret);
		goto error;
	}

#ifdef DEBUG_JWE
    DBG_LOG("Plain Text Bytes");
    DBG_DUMP(out_data, *out_len);
#endif

	ret = JWE_OK;

	error: if (ret != JWE_OK)
		*out_len = 0;

	memset(in_buf, 0, sizeof(in_buf));
	memset(dec_buf, 0, sizeof(dec_buf));
	hdr = NULL;
	cek = NULL;
	iv = NULL;
	cipher_text = NULL;
	tag = NULL;

	return ret;
}

/**
 * extract decoded plaintext payload from a JWE object using {"alg": "RSA-OAEP", "enc": "A128GCM"}
 * Input: payload (actually it's the whole JWE string); rsa key; out_len: the max out size
 * output: out: decoded plaintext (not in base64 anymore); out_len: actual len of the object
 */
uint32_t extract_jwe_payload(uint8_t *payload, uint32_t payload_len,
		rsa_key_info_t *key, uint8_t *out, uint32_t *out_len) {
	return extract_jwe_payload_with_algo(payload, payload_len, key,
			CEK_ENC_ALG_RSA_OAEP, CONTENT_ENC_ALG_A256GCM, out, out_len);
//    uint32_t ret = JWE_GEN_ERROR_INTERNAL;
//    jwe_params params = {0};
//
//    if (!payload || !payload_len || !key || !out || !out_len || !*out_len)
//    {
//        PEBBLE_LOG("Invalid arguments");
//        return JWE_GEN_ERROR_INVALID_INPUT_PARAM;
//    }
//
//    params.alg_type  = CEK_ENC_ALG_RSA_OAEP; //CEK_ENC_ALG_RSA1_5;
//    params.enc_type  = CONTENT_ENC_ALG_A256GCM;//CONTENT_ENC_ALG_A128CBC_HS256 (not works)
//    params.kid.value = key->kid;
//    params.kid.len   = key->kid_len;
//
//
//    PEBBLE_LOG_DEBUG("Before extract_jwe_payload_internal, payload_len = %d", payload_len);
//    //DUMPSTRING("payload", payload, payload_len);
//
//    ret = extract_jwe_payload_internal (&params, key, payload, payload_len, out, out_len);
//
//    if (ret != JWE_OK)
//    {
//        PEBBLE_LOG("extract_jwe_payload_internal failed, ret = %d", ret);
//        goto error;
//    }
//
//    ret = JWE_OK;
//
//error:
//    memset(&params,0, sizeof(params));
//
//    return ret;
}

/**
 * extract decoded plaintext payload from a JWE object using the provided alg_type and enc_type
 */
uint32_t extract_jwe_payload_with_algo(uint8_t *payload, uint32_t payload_len,
		rsa_key_info_t *key, cek_enc_alg_type alg_type,
		content_enc_alg_type enc_type, uint8_t *out, uint32_t *out_len) {
	uint32_t ret = JWE_GEN_ERROR_INTERNAL;
	jwe_params params = { 0 };

	if (!payload || !payload_len || !key || !out || !out_len || !*out_len) {
		PEBBLE_LOG("Invalid arguments");
		return JWE_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	params.alg_type = alg_type;
	params.enc_type = enc_type;
	params.kid.value = key->kid;
	params.kid.len = key->kid_len;

	DBG_LOG("Before extract_jwe_payload_internal, payload_len = %d", payload_len); DUMPSTRING("payload", payload, payload_len);

	ret = extract_jwe_payload_internal(&params, key, payload, payload_len, out,
			out_len);

	if (ret != JWE_OK) {
		PEBBLE_LOG("extract_jwe_payload_internal failed, ret = %d", ret);
		goto error;
	}

	ret = JWE_OK;

	error:
	memset(&params, 0, sizeof(params));

	return ret;
}

/**
 * create a JWE object from a plaintext payload using {"alg": "RSA-OAEP", "enc": "A128GCM"}
 * input: payload, not yet encoded to base64; rsa key; out_len: the max out size
 * output: out: formatted jwe object; out_len: actual len of the object
 */
uint32_t create_jwe_payload(uint8_t *payload, uint32_t payload_len,
		rsa_key_info_t *key, uint8_t *out, uint32_t *out_len) {
	return create_jwe_payload_with_algo(payload, payload_len, key,
			CEK_ENC_ALG_RSA_OAEP, CONTENT_ENC_ALG_A256GCM, out, out_len);
//    uint32_t ret = JWE_GEN_ERROR_INTERNAL;
//    jwe_params params;
//
//    if (!payload || !payload_len || !key || !key->modulus_len ||
//            !key->pub_expo_len || !out || !out_len)
//    {
//        PEBBLE_LOG("Invalid arguments");
//        return JWE_GEN_ERROR_INVALID_INPUT_PARAM;
//    }
//
//    memset (out, 0, *out_len);
//
//    /* Set up JWE Params - Start */
//    memset(&params, 0, sizeof(params));
//
//#ifdef TEST_JWE
//    params.kid.value = NULL;
//    params.kid.len   = 0;
//    payload =  get_test_plaintext();
//    payload_len = get_test_plaintext_len();
//
//#ifdef TEST_RSAOAEP_A256GCM
//    params.alg_type = CEK_ENC_ALG_RSA_OAEP;
//    params.enc_type = CONTENT_ENC_ALG_A256GCM;
//#elif defined TEST_RSA1_5_A128CBC_HS256
//    params.alg_type = CEK_ENC_ALG_RSA1_5;
//    params.enc_type = CONTENT_ENC_ALG_A128CBC_HS256;
//#else
//#error
//#endif //TEST_RSAxxx
//
//#else //!TEST_JWE
//    params.alg_type = CEK_ENC_ALG_RSA_OAEP; //CEK_ENC_ALG_RSA1_5;
//    params.enc_type = CONTENT_ENC_ALG_A256GCM;//CONTENT_ENC_ALG_A128CBC_HS256 (not works) //CONTENT_ENC_ALG_A128GCM;
//    params.kid.value = key->kid;
//    params.kid.len   = key->kid_len;
//#endif //TEST_JWE
//
//    /* Set up JWE Params - End */
//
//
//#ifdef DEBUG_JWE
//    DBG_LOG("Input Message for JWE Generation:");
//    DUMPSTRING ("payload", (char *) payload, strlen((char *) payload));
//    DBG_LOG("Kid string for JWE generation:");
//    DUMPSTRING ("kid", (char *) params.kid.value, strlen((char *)params.kid.value));
//#endif
//
//    ret = generate_jwe_payload (&params, key, payload, payload_len, out, out_len);
//
//    if (ret != JWE_OK)
//    {
//        PEBBLE_LOG("generate_jwe_payload failed, ret = %d", ret);
//        goto error;
//    }
//
//#if 0
//    /* Test self-verification */
//    {
//        uint8_t dec_data[JWE_MAX_BUF_LEN];
//        uint32_t  dec_len = sizeof(dec_data);
//        uint8_t * pvt_exp = NULL;
//        uint32_t pvt_exp_len = 0;
//
//        ret = get_server_root_pvtkey (&pvt_exp, &pvt_exp_len);
//
//        if (ret != JWE_OK)
//        {
//            PEBBLE_LOG("get_server_root_pvtkey failed, ret = %d", ret);
//            goto error;
//        }
//
//        rsaKey.privateExponent.value = pvt_exp;
//        rsaKey.privateExponent.len   = pvt_exp_len;
//
//        ret = extract_jwe_payload (&params, &cek_enc_key, out, *out_len, dec_data, &dec_len );
//
//        if (ret != JWE_OK)
//        {
//            PEBBLE_LOG("extract_jwe_payload failed, ret = %d", ret);
//            goto error;
//        }
//
//        if (dec_len != payload_len || memcmp (payload, dec_data, dec_len))
//        {
//            PEBBLE_LOG("extracted jwe payload do not match");
//            ret = JWE_CRYPT_ERROR_UNEXPECTED_DATA;
//            goto error;
//        }
//
//#ifdef DEBUG_JWE
//        DBG_LOG("After jwe extract ");
//        DUMPSTRING("dec_data", dec_data, dec_len);
//#endif
//    }
//#endif
//
//    ret = JWE_OK;
//
//error :
//
//    return ret;
}

/**
 * create a JWE object from a plaintext payload using the provided alg_type and enc_type
 */
uint32_t create_jwe_payload_with_algo(uint8_t *payload, uint32_t payload_len,
		rsa_key_info_t *key, cek_enc_alg_type alg_type,
		content_enc_alg_type enc_type, uint8_t *out, uint32_t *out_len) {
	uint32_t ret = JWE_GEN_ERROR_INTERNAL;
	jwe_params params;

	if (!payload || !key || !key->modulus_len || !key->pub_expo_len || !out
			|| !out_len) {
		PEBBLE_LOG("Invalid arguments");
		return JWE_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	memset(out, 0, *out_len);

	/* Set up JWE Params - Start */
	memset(&params, 0, sizeof(params));

#ifdef TEST_JWE
    params.kid.value = NULL;
    params.kid.len   = 0;
    payload =  get_test_plaintext();
    payload_len = get_test_plaintext_len();

#ifdef TEST_RSAOAEP_A256GCM
    params.alg_type = CEK_ENC_ALG_RSA_OAEP;
    params.enc_type = CONTENT_ENC_ALG_A256GCM;
#elif defined TEST_RSA1_5_A128CBC_HS256
    params.alg_type = CEK_ENC_ALG_RSA1_5;
    params.enc_type = CONTENT_ENC_ALG_A128CBC_HS256;
#else
#error
#endif //TEST_RSAxxx

#else //!TEST_JWE
	params.alg_type = alg_type; //CEK_ENC_ALG_RSA_OAEP;
	params.enc_type = enc_type; //CONTENT_ENC_ALG_A256GCM;
	params.kid.value = key->kid;
	params.kid.len = key->kid_len;
#endif //TEST_JWE

	/* Set up JWE Params - End */

#ifdef DEBUG_JWE
    DBG_LOG("Input Message for JWE Generation:");
    DUMPSTRING ("payload", (char *) payload, strlen((char *) payload));
    DUMPSTRING ("kid", (char *) params.kid.value, strlen((char *)params.kid.value));
#endif

	ret = generate_jwe_payload(&params, key, payload, payload_len, out,
			out_len);

	if (ret != JWE_OK) {
		PEBBLE_LOG("generate_jwe_payload failed, ret = %d", ret);
		goto error;
	}

	ret = JWE_OK;

	error:

	return ret;
}
