/*
 * (c) Copyright 2016 Samsung Research America, Inc.
 *                  All rights reserved
 *
 *  MPS Lab
 *
 * File: jws.c
 * Author: r.kothari@samsung.com
 * Creation Date: Aug 25, 2016
 * Co-Author: jianwei.qian@samsung.com
 * Update Date: Aug 12, 2020
 *
 */
/*
 * Copyright (C) 2016 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 "jws.h"
#include "TZ_Vendor_tl.h"
#include "pebble_defs.h"
#include "base64.h"
#include "parson.h"
#include "cipher.h"

/* Given input JWS Params, this returns the NULL terminated
 * param string of the requested header tag */
//TODO: Extend Header Composition by dynamic selection field.
static uint32_t get_param_str(const char *tag, jws_params *params, uint8_t *out,
		uint32_t out_len) {
	uint32_t ret = JWS_GEN_ERROR_INTERNAL;
	char *str = NULL;
	uint8_t kid[JWS_KID_MAX_LEN] = { 0 };
	uint32_t kid_len = sizeof(kid);

	if (!tag || !params || !out || !out_len) {
		PEBBLE_LOG("Invalid params");
		return JWS_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	if (!strncmp(tag, "alg", strlen("alg"))) {
		if (JWS_SIG_ALG_RS256 == params->alg_type) {
			str = "RS256";
		}
	} else if (!strncmp(tag, "typ", strlen("typ"))) {
		if (JWS_PAYLOAD_TYPE == params->payload_type) {
//            str = "JWS";
			str = "JWT";

		}
	} else if (!strncmp(tag, "kid", strlen("kid"))) {
		DBG_LOG("MC_PAY checking for kid !!!");
		if (params->kid.value != NULL && params->kid.len > 0) {
			kid_len = params->kid.len;
			memcpy(kid, params->kid.value, kid_len);
			memset(kid + kid_len, '\0', 1); // Null terminate a string
			str = (char*) kid;
			DBG_LOG("kidString : %s", str);
		}
	}

	if (!str) {
		PEBBLE_LOG("Invalid / Unsupported params for %s", tag);
		ret = JWS_GEN_ERROR_INVALID_INPUT_PARAM;
		goto error;
	}

	if (out_len <= strlen((str))) {
		PEBBLE_LOG("Insufficient buffer");
		ret = JWS_GEN_ERROR_INSUFFICIENT_BUFFER;
		goto error;
	}

	strcpy((char* )out, str);

	ret = JWS_OK;

	error: str = NULL;
	memset(kid, 0, sizeof(kid));

	return ret;
}

/* Based on the input params, create a JWS Header structure as below
 * {"alg":"RS256", "kid": "xxxx"}
 */
static uint32_t create_header(jws_params *params, uint8_t *out_data,
		uint32_t *out_len) {
	uint32_t ret = JWS_GEN_ERROR_INTERNAL;
	uint8_t val_str[JWS_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 = "kid";
	char *tag3 = "typ";
	const char *hdr_tags[] = { tag1, tag2, tag3 };

	if (!params || !out_data || !out_len || !*out_len) {
		PEBBLE_LOG("Invalid Parameters");
		return JWS_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 != JWS_OK) {
			PEBBLE_LOG(
					"get_param_str on tag %d failed, ret = %d, tag=0x%s, paramsptr=0x%x",
					i, ret, hdr_tags[i], (int )params);
			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_JWS
    DBG_LOG("%s", out_data);
#endif

	ret = JWS_OK;

	error:
	memset(val_str, 0, sizeof(val_str));
	buf = NULL;

	return ret;
}

static uint32_t append_header(jws_params *params, uint8_t *out_data,
		uint32_t out_len) {
	uint32_t ret = JWS_GEN_ERROR_INTERNAL;
	uint8_t buf[JWS_MAX_BUF_LEN] = { 0 };
	uint32_t buf_len = sizeof(buf);

	if (!params || !out_data || !out_len) {
		PEBBLE_LOG("Invalid parameters");
		return JWS_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	/* Create Header */
	ret = create_header(params, buf, &buf_len);

	if (ret != JWS_OK) {
		PEBBLE_LOG("create_header failed, %d", ret);
		goto error;
	}

	ret = encode_append(buf, buf_len, out_data, out_len, true, NULL, NULL);

	if (ret != JWS_OK) {
		PEBBLE_LOG("encode_append failed, ret = %d", ret);
		goto error;
	}

	ret = JWS_OK;

	error:
	memset(buf, 0, sizeof(buf));

	return ret;
}

/**
 * input: in (payload, not yet encoded to based64)
 * output: append encoded payload to the string in out, whose len is at most out_len
 */
static uint32_t append_payload(uint8_t *in, uint32_t in_len, uint8_t *out,
		uint32_t out_len) {
	uint32_t ret = JWS_GEN_ERROR_INTERNAL;

	if (!in || !in_len || !out || !out_len) {
		PEBBLE_LOG("Invalid arguments");
		return JWS_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	ret = encode_append(in, in_len, out, out_len, false, NULL, NULL);

	if (ret != JWS_OK) {
		PEBBLE_LOG("encode_append failed, ret = %d", ret);
		return ret;
	}

	return JWS_OK;
}

static uint32_t gen_signature(jws_params *params, uint8_t *in, uint32_t in_len,
		rsa_key_info_t *key, uint8_t *out, uint32_t *out_len) {
	uint32_t ret = JWS_GEN_ERROR_INTERNAL;

	if (!params || !in || !in_len || !key || !out || !out_len || !*out_len) {
		PEBBLE_LOG("Invalid arguments");
		return JWS_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	if (params->alg_type != JWS_SIG_ALG_RS256) {
		PEBBLE_LOG("Unsupported signature algorithm requested");
		return JWS_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	DBG_LOG("MC_PAY: Key mod"); DBG_DUMP(key->rsaKey->modulus.value, key->rsaKey->modulus.len);

	DBG_LOG("MC_PAY: Key pub exponent"); DBG_DUMP(key->rsaKey->exponent.value, key->rsaKey->exponent.len);

	DBG_LOG("MC_PAY: Key private exponent"); DBG_DUMP(key->rsaKey->privateExponent.value, key->rsaKey->privateExponent.len);

	ret = TZ_sign_CKM_SHA256_RSA_PKCS(
			key->modulus, // TZ_sign_...
			key->modulus_len, key->pub_expo, key->pub_expo_len, key->priv_expo,
			key->priv_expo_len, in, in_len, out, out_len);

	if (ret != TZ_API_OK) {
		PEBBLE_LOG("TZ_sign_CKM_SHA256_RSA_PKCS failed, ret = %d", ret);
		return JWS_CRYPT_ERROR_SIGN;
	}

	return JWS_OK;
}

static uint32_t verify_signature(jws_params *params, rsa_key_info_t *key,
		uint8_t *msg, uint32_t msg_len, uint8_t *sig, uint32_t sig_len) {
	uint32_t ret = JWS_GEN_ERROR_INTERNAL;
	bool is_valid_sig = false;

	if (!params || !msg || !msg_len || !key || !sig || !sig_len) {
		PEBBLE_LOG("Invalid arguments");
		return JWS_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	if (params->alg_type != JWS_SIG_ALG_RS256) {
		PEBBLE_LOG("Unsupported signature algorithm requested");
		return JWS_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	ret = TZ_verify_CKM_SHA256_RSA_PKCS(key->modulus, key->modulus_len,
			key->pub_expo, key->pub_expo_len, msg, msg_len, sig, sig_len,
			&is_valid_sig);

	if (ret != TZ_API_OK || !is_valid_sig) {
		PEBBLE_LOG(
				"TZ_sign_CKM_SHA256_RSA_PKCS failed or signature invalid, ret = %d",
				ret);
		return JWS_CRYPT_ERROR_SIGN;
	}

	return JWS_OK;
}

static uint32_t append_signature(uint8_t *in, uint32_t in_len, uint8_t *out,
		uint32_t out_len) {
	uint32_t ret = JWS_GEN_ERROR_INTERNAL;

	if (!in || !in_len || !out || !out_len) {
		PEBBLE_LOG("Invalid arguments");
		return JWS_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	ret = encode_append(in, in_len, out, out_len, false, NULL, NULL);

	if (ret != JWS_OK) {
		PEBBLE_LOG("encode_append failed, ret = %d", ret);
		return ret;
	}

	return JWS_OK;
}

static uint32_t validate_params(jws_params *params, rsa_key_info_t *key,
		uint8_t *in, uint32_t in_len, uint8_t *out, uint32_t *out_len,
		uint32_t mode) {
	if (!params || !key || !in || !in_len || !out || !out_len || !*out_len) {
		PEBBLE_LOG("Invalid arguments");
		return JWS_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	if (params->alg_type <= JWS_SIG_ALG_NONE
			|| params->alg_type >= JWS_SIG_ALG_MAX) {
		PEBBLE_LOG("Invalid Signature algorithm type");
		return JWS_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	if (params->payload_type != JWS_PAYLOAD_TYPE) {
		PEBBLE_LOG("Unsupported payload type");
		return JWS_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	if (params->alg_type == JWS_SIG_ALG_RS256) {
		if (!key || key->modulus_len <= 0 || key->pub_expo_len <= 0) {
			PEBBLE_LOG("Invalid Public Key");
			return JWS_GEN_ERROR_INVALID_INPUT_PARAM;
		}

		if (JWS_MODE_SIGN == mode) {
			if (key->priv_expo_len <= 0) {
				PEBBLE_LOG("Invalid Private Key");
				return JWS_GEN_ERROR_INVALID_INPUT_PARAM;
			}
		}
	}

	return JWS_OK;

}

uint32_t create_payload_internal(jws_params *params, rsa_key_info_t *key,
		uint8_t *in, uint32_t in_len, uint8_t *out, uint32_t *out_len) {
	uint32_t ret = JWS_GEN_ERROR_INTERNAL;
	uint8_t sig[JWS_MAX_BUF_LEN] = { 0 };
	uint32_t sig_len = sizeof(sig);
	uint32_t total = 0;

	DBG_LOG("PAYPAL create_payload_internal");
	ret = validate_params(params, key, in, in_len, out, out_len, JWS_MODE_SIGN);

	if (ret != JWS_OK) {
		PEBBLE_LOG("validate_params failed, ret = %d", ret);
		goto error;
	}

	DBG_LOG("PAYPAL append_header");
	ret = append_header(params, out, *out_len);

	if (ret != JWS_OK) {
		PEBBLE_LOG("append_header failed, ret = %d", ret);
		goto error;
	}

	DBG_LOG("After JWS Header Encoding : strlen(out_data) = %d, - %d ", strlen((char *)out), *out_len);
#ifdef DEBUG_JWS
    DUMPSTRING ("out_data", (char *) out, strlen((char *) out));
#endif

	DBG_LOG("PAYPAL append_payload");
	ret = append_payload(in, in_len, out, *out_len);

	if (ret != JWS_OK) {
		PEBBLE_LOG("append_payload failed, ret = %d", ret);
		goto error;
	}

	DBG_LOG("After payload, before signing: strlen(out_data) = %d", strlen((char *)out));
#ifdef DEBUG_JWS

    DUMPSTRING ("out_data", (char *) out, strlen((char *) out));
#endif

	ret = gen_signature(params, out, strlen((char*) out), key, sig, &sig_len);

	if (ret != JWS_OK) {
		PEBBLE_LOG("gen_signature failed, ret = %d", ret);
		goto error;
	}

	DBG_LOG("generated_signature = %d", sig_len);
#ifdef DEBUG_JWS
    DBG_DUMP(sig, sig_len);
#endif

	DBG_LOG("PAYPAL: append_signature");
	ret = append_signature(sig, sig_len, out, *out_len);

	if (ret != JWS_OK) {
		PEBBLE_LOG("append_signature failed, ret = %d", ret);
		goto error;
	}

	total = strlen((char*) out);

	if (total >= *out_len) {
		PEBBLE_LOG("Insufficient buffer");
		ret = JWS_GEN_ERROR_INSUFFICIENT_BUFFER;
		goto error;
	}

	*out_len = total;

	DBG_LOG("Final JWS Encoding : total *out_len = %d", *out_len);
#ifdef DEBUG_JWS

    DUMPSTRING ("out_data", (char *) out, *out_len);
#endif

	ret = JWS_OK;

	error:
	memset(sig, 0, sizeof(sig));
	return ret;
}

/**
 * input: payload, not yet encoded to base64; rsa key; out_len: the max out size
 * output: out: formatted jws object; out_len: actual len of the object
 * Fixed alg_type = JWS_SIG_ALG_RS256
 */
uint32_t create_jws_payload(uint8_t *payload, uint32_t payload_len,
		rsa_key_info_t *key, uint8_t *out, uint32_t *out_len) {
	return create_jws_payload_with_algo(payload, payload_len, key,
			JWS_SIG_ALG_RS256, out, out_len);
//    uint32_t ret = JWS_GEN_ERROR_INTERNAL;
//    jws_params params ;
//
//    if (!payload || !payload_len || !key ||!out || !out_len || !*out_len)
//    {
//        PEBBLE_LOG("Invalid arguments");
//        return JWS_GEN_ERROR_INVALID_INPUT_PARAM;
//    }
//
//    memset(out, 0, *out_len);
//
//    /* Set up JWS Params - Start */
//    memset(&params, 0, sizeof(params));
//
//    params.payload_type  = JWS_PAYLOAD_TYPE;
//    params.alg_type      = JWS_SIG_ALG_RS256;
//    params.kid.value     = key->kid;
//    params.kid.len       = key->kid_len;
//
//    DBG_LOG("PAYPAL jws private keyId: ");
//    //DBG_DUMP(params.kid.value, params.kid.len);
//    /* Set up JWS Params - End */
//
//    ret = create_payload_internal (&params, key, payload, payload_len,
//                                   out, out_len);
//
//    if (ret != JWS_OK)
//    {
//        PEBBLE_LOG("create_payload_internal failed, ret = %d", ret);
//        goto error;
//    }
//
//    ret = JWS_OK;
//
//    DBG_LOG("PAYPAL jws JWS_OK ");
//error :
//    memset(&params, 0, sizeof(params));
//
//    return ret;
}

uint32_t create_jws_payload_with_algo(uint8_t *payload, uint32_t payload_len,
		rsa_key_info_t *key, jws_sig_alg_type alg_type, uint8_t *out,
		uint32_t *out_len) {
	uint32_t ret = JWS_GEN_ERROR_INTERNAL;
	jws_params params;

	if (!payload || !payload_len || !key || !out || !out_len || !*out_len) {
		PEBBLE_LOG("Invalid arguments");
		return JWS_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	memset(out, 0, *out_len);

	/* Set up JWS Params - Start */
	memset(&params, 0, sizeof(params));

	params.payload_type = JWS_PAYLOAD_TYPE;
	params.alg_type = alg_type;
	params.kid.value = key->kid;
	params.kid.len = key->kid_len;
	/* Set up JWS Params - End */

	ret = create_payload_internal(&params, key, payload, payload_len, out,
			out_len);

	if (ret != JWS_OK) {
		PEBBLE_LOG("create_payload_internal failed, ret = %d", ret);
		goto error;
	}

	ret = JWS_OK;

	error:
	memset(&params, 0, sizeof(params));

	return ret;
}

/* Verify header against supported algorithms & key id */
/* Expected a NULL terminated base64 encoded hdr */
static uint32_t verify_header(jws_params *params, char *encoded_hdr) {
	uint32_t ret = JWS_GEN_ERROR_INTERNAL;
	uint8_t hdr[JWS_MAX_BUF_LEN] = { 0 };
	uint32_t hdr_len = sizeof(hdr);
	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[JWS_MAX_HDR_VALUE_SIZE] = { 0 };
	char *tag1 = "alg";
	//char *tag2 = "kid";
	//char *tag3 =  "typ";
	//const char * hdr_tags[] = {tag1, tag2, tag3};
	const char *hdr_tags[] = { tag1 };

	DBG_LOG("Enter verify_header.");

	if (!params || !encoded_hdr || !strlen(encoded_hdr) || !params->kid.value
			|| !params->kid.len) {
		PEBBLE_LOG("Invalid params");
		return JWS_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	ret = base64url_decode_nopad((uint8_t*) encoded_hdr, strlen(encoded_hdr),
			hdr, &hdr_len);

	DBG_LOG("Enter verify_header 2.");

	if (ret != BASE64_OK) {
		PEBBLE_LOG("base64url_decode failed for header, ret = %d", ret);
		ret = JWS_CRYPT_ERROR_BASE64_DECODE_FAILED;
		goto error;
	}

	/* Need to NULL terminate hdr, for json parser to work */
	if (hdr_len >= sizeof(hdr)) {
		PEBBLE_LOG("Insufficient buffer for header");
		return JWS_GEN_ERROR_INSUFFICIENT_BUFFER;
	}
	hdr[hdr_len] = 0;

	json_parser_init();

	DBG_LOG("Enter verify_header 3.1");

	parsed_json = json_parse_string((const char*) hdr);
	DBG_LOG("Enter Parsing string4 = %s", hdr);

	if (parsed_json == NULL) {
		PEBBLE_LOG("json_parse_string failed");
		ret = JWS_PROCESS_CP_JSONPARSE_ERROR;
		goto error;
	}

	DBG_LOG("Enter verify_header 4.");

	json_object = json_value_get_object(parsed_json);

	if (json_object == NULL) {
		PEBBLE_LOG("json_value_get_object failed");
		ret = JWS_PROCESS_CP_JSONPARSE_ERROR;
		goto error;
	}

	DBG_LOG("Enter verify_header 5.");

	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 = JWS_GEN_ERROR_MISSING_DATA;
			goto error;
		}

		ret = get_param_str(hdr_tags[i], params, expected_str,
				sizeof(expected_str));

		if (ret != JWS_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 mismatch, expected = %s, actual = %s",
					expected_str, value); DBG_LOG("expected = %s", expected_str); DBG_LOG("actual = %s", value);
			ret = JWS_CRYPT_ERROR_UNEXPECTED_DATA;
			goto error;
		}
	}

	DBG_LOG("Enter verify_header 6.");
	ret = JWS_OK;

	error: if (parsed_json) {
		json_value_free(parsed_json);
		parsed_json = NULL;
	}
	memset(hdr, 0, sizeof(hdr));
	parsed_json = NULL;
	json_object = NULL;
	value = NULL;

	return ret;
}

uint32_t extract_payload_internal(jws_params *params, rsa_key_info_t *key,
		uint8_t *in, uint32_t in_len, uint8_t *out, uint32_t *out_len) {
	uint32_t ret = JWS_GEN_ERROR_INTERNAL;
	char *hdr = NULL;
	char *payload = NULL;
	char *signature = NULL;
	uint32_t sign_input_len = 0;
	uint8_t sig[JWS_MAX_BUF_LEN] = { 0 };
	uint32_t sig_len = sizeof(sig);
	uint32_t hdr_len = 0;
	uint32_t payload_len = 0;

	ret = validate_params(params, key, in, in_len, out, out_len,
			JWS_MODE_VERIFY);

	if (ret != JWS_OK) {
		PEBBLE_LOG("validate_params failed, ret = %d", ret);
		goto error;
	}

	hdr = strtok((char*) in, ".");
	payload = strtok(NULL, ".");
	signature = strtok(NULL, ".");

	if (!hdr) {
		PEBBLE_LOG("Bad JWS format. No header found");
		ret = JWS_GEN_ERROR_MISSING_DATA;
		goto error;
	}

	if (!payload) {
		PEBBLE_LOG("Bad JWS format. No payload found");
		ret = JWS_GEN_ERROR_MISSING_DATA;
		goto error;
	}

	if (!signature) {
		PEBBLE_LOG("Bad JWS format. No signature found");
		ret = JWS_GEN_ERROR_MISSING_DATA;
		goto error;
	}
#ifdef DEBUG_JWS
    DBG_LOG("hdr, len = %d", strlen(hdr));
    DUMPSTRING("hdr", hdr, strlen(hdr));

    DBG_LOG("payload, len = %d", strlen(payload));
    DUMPSTRING("payload", payload, strlen(payload));

    DBG_LOG("signature, len = %d", strlen(signature));
    DUMPSTRING("signature", signature, strlen(signature));
#endif

	ret = verify_header(params, hdr);

	if (ret != JWS_OK) {
		PEBBLE_LOG("verify_header failed, ret = %d", ret);
		goto error;
	}

	if (!payload || !strlen(payload) || !signature || !strlen(signature)) {
		PEBBLE_LOG("Bad JWS format. No payload or signature found");
		ret = JWS_GEN_ERROR_MISSING_DATA;
		goto error;
	}

	ret = base64url_decode_nopad((uint8_t*) signature, strlen(signature), sig,
			&sig_len);

	if (ret != BASE64_OK) {
		PEBBLE_LOG("base64url_decode failed for signature, ret = %d", ret);
		ret = JWS_CRYPT_ERROR_BASE64_DECODE_FAILED;
		goto error;
	}

#ifdef DEBUG_JWS
    DBG_LOG("base64decode signature, len = %d", sig_len);
    DBG_DUMP(sig, sig_len);
#endif

	hdr_len = strlen(hdr);
	payload_len = strlen(payload);

	if (hdr_len + payload_len >= in_len) {
		PEBBLE_LOG("Unexpected JWS data");
		ret = JWS_GEN_ERROR_MISSING_DATA;
		goto error;
	}

	/*strtok puts a NULL after finding a token */
	/*For signature matching, we need the exact same string */
	in[hdr_len] = 0x2E; //".";

	sign_input_len = hdr_len + payload_len + 1; //1, for "." between hdr & payload

#ifdef DEBUG_JWS
    DBG_LOG("signing input, len = %d", sign_input_len);
    DUMPSTRING("signing input", in, sign_input_len);
#endif

	ret = verify_signature(params, key, in, sign_input_len, sig, sig_len);

	if (ret != JWS_OK) {
		PEBBLE_LOG("verify_signature failed, ret = %d", ret);
		goto error;
	}

	ret = base64url_decode_nopad((uint8_t*) payload, strlen(payload), out,
			out_len);

	if (ret != BASE64_OK) {
		PEBBLE_LOG("base64url_decode failed for payload, ret = %d", ret);
		ret = JWS_CRYPT_ERROR_BASE64_DECODE_FAILED;
		goto error;
	}

	ret = JWS_OK;

	error: hdr = NULL;
	payload = NULL;
	signature = NULL;
	memset(sig, 0, sizeof(sig));

	return ret;
}

/**
 * input: in: formatted jws object; key: rsa private key
 * output: plaintext payload (decoded from base64 to bytes); out_len: actual length
 */
uint32_t extract_jws_payload(uint8_t *in, uint32_t in_len, rsa_key_info_t *key,
		uint8_t *out, uint32_t *out_len) {
	return extract_jws_payload_with_algo(in, in_len, key, JWS_SIG_ALG_RS256,
			out, out_len);
//    uint32_t ret = JWS_GEN_ERROR_INTERNAL;
//    jws_params params ;
//    uint8_t  in_buf[JWS_MAX_BUF_LEN] = {0};
//
//    if (!in || !in_len || !key || !out || !out_len || !*out_len)
//    {
//        PEBBLE_LOG("Invalid arguments");
//        return JWS_GEN_ERROR_INVALID_INPUT_PARAM;
//    }
//
//    /* "=", to NULL terminate the last byte, for strtok to work */
//    if (in_len >= sizeof(in_buf))
//    {
//        PEBBLE_LOG("Insufficient buffer for payload copy: %d - %d",in_len,sizeof(in_buf));
//        return JWS_GEN_ERROR_INSUFFICIENT_BUFFER;
//    }
//
//    memcpy (in_buf, in, in_len);
//    in_buf[in_len] = 0;
//
//    /* Set up JWS Params - Start */
//    memset(&params, 0, sizeof(params));
//    params.payload_type  = JWS_PAYLOAD_TYPE;
//    params.alg_type      = JWS_SIG_ALG_RS256;
//    params.kid.value     = key->kid;
//    params.kid.len       = key->kid_len;
//    /* Set up JWS Params - End */
//
//    ret = extract_payload_internal (&params, key, in_buf, in_len, out, out_len);
//
//    if (ret != JWS_OK)
//    {
//        PEBBLE_LOG("extract_jwe_payload failed, ret = %d", ret);
//        goto error;
//    }
//
//    ret = JWS_OK;
//
//error:
//    memset(&params, 0, sizeof(params));
//    memset(in_buf, 0, sizeof(in_buf));
//
//    return ret;
}

uint32_t extract_jws_payload_with_algo(uint8_t *payload, uint32_t payload_len,
		rsa_key_info_t *key, jws_sig_alg_type alg_type, uint8_t *out,
		uint32_t *out_len) {
	uint32_t ret = JWS_GEN_ERROR_INTERNAL;
	jws_params params;
	uint8_t in_buf[JWS_MAX_BUF_LEN] = { 0 };

	if (!payload || !payload_len || !key || !out || !out_len || !*out_len) {
		PEBBLE_LOG("Invalid arguments");
		return JWS_GEN_ERROR_INVALID_INPUT_PARAM;
	}

	/* "=", to NULL terminate the last byte, for strtok to work */
	if (payload_len >= sizeof(in_buf)) {
		PEBBLE_LOG("Insufficient buffer for payload copy");
		return JWS_GEN_ERROR_INSUFFICIENT_BUFFER;
	}

	memcpy(in_buf, payload, payload_len);
	in_buf[payload_len] = 0;

	/* Set up JWS Params - Start */
	memset(&params, 0, sizeof(params));

	params.payload_type = JWS_PAYLOAD_TYPE;
	params.alg_type = alg_type;
	params.kid.value = key->kid;
	params.kid.len = key->kid_len;
	/* Set up JWS Params - End */

	ret = extract_payload_internal(&params, key, in_buf, payload_len, out,
			out_len);

	if (ret != JWS_OK) {
		PEBBLE_LOG("extract_jwe_payload failed, ret = %d", ret);
		goto error;
	}

	ret = JWS_OK;

	error:
	memset(&params, 0, sizeof(params));
	memset(in_buf, 0, sizeof(in_buf));

	return ret;
}
