/*
 * libwebsockets - small server side websockets and web server implementation
 *
 * Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include "private-lib-core.h"
#include "private-lib-jose-jwe.h"

/*
 * From RFC7518 JWA
 *
 * 4.6.  Key Agreement with Elliptic Curve Diffie-Hellman Ephemeral Static
 *    (ECDH-ES)
 *
 * This section defines the specifics of key agreement with Elliptic
 * Curve Diffie-Hellman Ephemeral Static [RFC6090], in combination with
 * the Concat KDF, as defined in Section 5.8.1 of [NIST.800-56A].  The
 * key agreement result can be used in one of two ways:
 *
 * 1.  directly as the Content Encryption Key (CEK) for the "enc"
 *     algorithm, in the Direct Key Agreement mode, or
 *
 * 2.  as a symmetric key used to wrap the CEK with the "A128KW",
 *     "A192KW", or "A256KW" algorithms, in the Key Agreement with Key
 *     Wrapping mode.
 *
 * A new ephemeral public key value MUST be generated for each key
 * agreement operation.
 *
 * In Direct Key Agreement mode, the output of the Concat KDF MUST be a
 * key of the same length as that used by the "enc" algorithm.  In this
 * case, the empty octet sequence is used as the JWE Encrypted Key
 * value.  The "alg" (algorithm) Header Parameter value "ECDH-ES" is
 * used in the Direct Key Agreement mode.
 *
 * In Key Agreement with Key Wrapping mode, the output of the Concat KDF
 * MUST be a key of the length needed for the specified key wrapping
 * algorithm.  In this case, the JWE Encrypted Key is the CEK wrapped
 * with the agreed-upon key.
 *
 * The following "alg" (algorithm) Header Parameter values are used to
 * indicate that the JWE Encrypted Key is the result of encrypting the
 * CEK using the result of the key agreement algorithm as the key
 * encryption key for the corresponding key wrapping algorithm:
 *
 * +-----------------+-------------------------------------------------+
 * | "alg" Param     | Key Management Algorithm                        |
 * | Value           |                                                 |
 * +-----------------+-------------------------------------------------+
 * | ECDH-ES+A128KW  | ECDH-ES using Concat KDF and CEK wrapped with   |
 * |                 | "A128KW"                                        |
 * | ECDH-ES+A192KW  | ECDH-ES using Concat KDF and CEK wrapped with   |
 * |                 | "A192KW"                                        |
 * | ECDH-ES+A256KW  | ECDH-ES using Concat KDF and CEK wrapped with   |
 * |                 | "A256KW"                                        |
 * +-----------------+-------------------------------------------------+
 *
 * 4.6.1.  Header Parameters Used for ECDH Key Agreement
 *
 * The following Header Parameter names are used for key agreement as
 * defined below.
 *
 * 4.6.1.1.  "epk" (Ephemeral Public Key) Header Parameter
 *
 * The "epk" (ephemeral public key) value created by the originator for
 * the use in key agreement algorithms.  This key is represented as a
 * JSON Web Key [JWK] public key value.  It MUST contain only public key
 * parameters and SHOULD contain only the minimum JWK parameters
 * necessary to represent the key; other JWK parameters included can be
 * checked for consistency and honored, or they can be ignored.  This
 * Header Parameter MUST be present and MUST be understood and processed
 * by implementations when these algorithms are used.
 *
 * 4.6.1.2.  "apu" (Agreement PartyUInfo) Header Parameter
 *
 * The "apu" (agreement PartyUInfo) value for key agreement algorithms
 * using it (such as "ECDH-ES"), represented as a base64url-encoded
 * string.  When used, the PartyUInfo value contains information about
 * the producer.  Use of this Header Parameter is OPTIONAL.  This Header
 * Parameter MUST be understood and processed by implementations when
 * these algorithms are used.
 *
 * 4.6.1.3.  "apv" (Agreement PartyVInfo) Header Parameter
 *
 * The "apv" (agreement PartyVInfo) value for key agreement algorithms
 * using it (such as "ECDH-ES"), represented as a base64url encoded
 * string.  When used, the PartyVInfo value contains information about
 * the recipient.  Use of this Header Parameter is OPTIONAL.  This
 * Header Parameter MUST be understood and processed by implementations
 * when these algorithms are used.
 *
 * 4.6.2.  Key Derivation for ECDH Key Agreement
 *
 * The key derivation process derives the agreed-upon key from the
 * shared secret Z established through the ECDH algorithm, per
 * Section 6.2.2.2 of [NIST.800-56A].
 *
 * Key derivation is performed using the Concat KDF, as defined in
 * Section 5.8.1 of [NIST.800-56A], where the Digest Method is SHA-256.
 * The Concat KDF parameters are set as follows:
 *
 * Z
 *    This is set to the representation of the shared secret Z as an
 *    octet sequence.
 *
 * keydatalen
 *    This is set to the number of bits in the desired output key.  For
 *    "ECDH-ES", this is length of the key used by the "enc" algorithm.
 *    For "ECDH-ES+A128KW", "ECDH-ES+A192KW", and "ECDH-ES+A256KW", this
 *    is 128, 192, and 256, respectively.
 *
 * AlgorithmID
 *    The AlgorithmID value is of the form Datalen || Data, where Data
 *    is a variable-length string of zero or more octets, and Datalen is
 *    a fixed-length, big-endian 32-bit counter that indicates the
 *    length (in octets) of Data.  In the Direct Key Agreement case,
 *    Data is set to the octets of the ASCII representation of the "enc"
 *    Header Parameter value.  In the Key Agreement with Key Wrapping
 *    case, Data is set to the octets of the ASCII representation of the
 *    "alg" (algorithm) Header Parameter value.
 *
 * PartyUInfo
 *    The PartyUInfo value is of the form Datalen || Data, where Data is
 *    a variable-length string of zero or more octets, and Datalen is a
 *    fixed-length, big-endian 32-bit counter that indicates the length
 *    (in octets) of Data.  If an "apu" (agreement PartyUInfo) Header
 *    Parameter is present, Data is set to the result of base64url
 *    decoding the "apu" value and Datalen is set to the number of
 *    octets in Data.  Otherwise, Datalen is set to 0 and Data is set to
 *    the empty octet sequence.
 *
 * PartyVInfo
 *    The PartyVInfo value is of the form Datalen || Data, where Data is
 *    a variable-length string of zero or more octets, and Datalen is a
 *    fixed-length, big-endian 32-bit counter that indicates the length
 *    (in octets) of Data.  If an "apv" (agreement PartyVInfo) Header
 *    Parameter is present, Data is set to the result of base64url
 *    decoding the "apv" value and Datalen is set to the number of
 *    octets in Data.  Otherwise, Datalen is set to 0 and Data is set to
 *    the empty octet sequence.
 *
 * SuppPubInfo
 *    This is set to the keydatalen represented as a 32-bit big-endian
 *    integer.
 *
 * SuppPrivInfo
 *    This is set to the empty octet sequence.
 *
 * Applications need to specify how the "apu" and "apv" Header
 * Parameters are used for that application.  The "apu" and "apv" values
 * MUST be distinct, when used.  Applications wishing to conform to
 * [NIST.800-56A] need to provide values that meet the requirements of
 * that document, e.g., by using values that identify the producer and
 * consumer.  Alternatively, applications MAY conduct key derivation in
 * a manner similar to "Diffie-Hellman Key Agreement Method" [RFC2631]:
 * in that case, the "apu" parameter MAY either be omitted or represent
 * a random 512-bit value (analogous to PartyAInfo in Ephemeral-Static
 * mode in RFC 2631) and the "apv" parameter SHOULD NOT be present.
 *
 */


/*
 * - ECDH-ES[-variant] comes in the jose "alg" and just covers key agreement.
 *   The "enc" action is completely separate and handled elsewhere.  However
 *   the key size throughout is determined by the needs of the "enc" action.
 *
 * - The jwe->jws.jwk is the PEER - the encryption consumer's - public key.
 *
 * - The public part of the ephemeral key comes out in jose.jwk_ephemeral
 *
 * - Return shared secret length or < 0 for error
 *
 * - Unwrapped CEK in EKEY.  If any, wrapped CEK in "wrapped".
 *
 * - Caller responsibility to cleanse EKEY.
 */

static int
lws_jwe_encrypt_ecdh(struct lws_jwe *jwe, char *temp, int *temp_len,
		     uint8_t *cek)
{
	uint8_t shared_secret[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES],
		derived[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES];
	int m, n, ret = -1, ot = *temp_len, ss_len = sizeof(shared_secret),
	    enc_hlen = (int)lws_genhmac_size(jwe->jose.enc_alg->hmac_type);
	struct lws_genec_ctx ecctx;
	struct lws_jwk *ephem = &jwe->jose.recipient[jwe->recip].jwk_ephemeral;

	if (jwe->jws.jwk->kty != LWS_GENCRYPTO_KTY_EC) {
		lwsl_err("%s: unexpected kty %d\n", __func__, jwe->jws.jwk->kty);

		return -1;
	}

	ephem->kty = LWS_GENCRYPTO_KTY_EC;
	ephem->private_key = 1;

	/* Generate jose.jwk_ephemeral on the peer public key curve */

	if (lws_genecdh_create(&ecctx, jwe->jws.context, NULL)) {
		lwsl_err("%s: lws_genecdh_create failed\n", __func__);
		goto bail;
	}

	/* ephemeral context gets random key on same curve as recip pubkey */
	if (lws_genecdh_new_keypair(&ecctx, LDHS_OURS, (const char *)
				jwe->jws.jwk->e[LWS_GENCRYPTO_EC_KEYEL_CRV].buf,
				ephem->e)) {
		lwsl_err("%s: lws_genecdh_new_keypair failed\n", __func__);
		goto bail;
	}

	/* peer context gets js->jwk key */
	if (lws_genecdh_set_key(&ecctx, jwe->jws.jwk->e, LDHS_THEIRS)) {
		lwsl_err("%s: setting peer pubkey failed\n", __func__);
		goto bail;
	}

	/* combine our ephemeral key and the peer pubkey to get the secret */
	ss_len = (int)jwe->jws.jwk->e[LWS_GENCRYPTO_EC_KEYEL_X].len;

	if (lws_genecdh_compute_shared_secret(&ecctx, shared_secret, &ss_len)) {
		lwsl_notice("%s: lws_genecdh_compute_shared_secret failed\n",
				__func__);

		goto bail;
	}

	/*
	 * The private part of the ephemeral key is finished with...
	 * cleanse and free it.  We need to keep the public part around so we
	 * can publish it with the JWE as "epk".
	 */

	lws_explicit_bzero(ephem->e[LWS_GENCRYPTO_EC_KEYEL_D].buf,
			   ephem->e[LWS_GENCRYPTO_EC_KEYEL_D].len);
	lws_free_set_NULL(ephem->e[LWS_GENCRYPTO_EC_KEYEL_D].buf);
	ephem->e[LWS_GENCRYPTO_EC_KEYEL_D].len = 0;
	ephem->private_key = 0;

	/*
	 * Derive the CEK from the shared secret... amount of bytes written to
	 * derived matches bitcount in jwe->jose.enc_alg->keybits_fixed
	 *
	 * In Direct Key Agreement mode, the output of the Concat KDF MUST be a
	 * key of the same length as that used by the "enc" algorithm.
	 */

	if (lws_jwa_concat_kdf(jwe,
			jwe->jose.alg->algtype_crypto == LWS_JOSE_ENCTYPE_NONE,
			derived, shared_secret, ss_len)) {
		lwsl_notice("%s: lws_jwa_concat_kdf failed\n", __func__);

		goto bail;
	}

	/* in P-521 case, we get a 66-byte shared secret for a 64-byte key */
	if (ss_len < enc_hlen) {
		lwsl_err("%s: concat KDF bad derived key len %d\n", __func__,
			 ss_len);
		goto bail;
	}

	/*
	 * For "ECDH-ES", that was it, and we use what we just wrapped in
	 * wrapped as the CEK without publishing it.
	 *
	 * For "ECDH-ES-AES[128,192,256]KW", we generate a new, random CEK and
	 * then wrap it using the key we just wrapped, and make the wrapped
	 * version available in EKEY.
	 */

	if (jwe->jose.alg->algtype_crypto != LWS_JOSE_ENCTYPE_NONE) {
		struct lws_gencrypto_keyelem el;
		struct lws_genaes_ctx aesctx;

		/* generate the actual CEK in cek */

		if (lws_get_random(jwe->jws.context, cek, (unsigned int)enc_hlen) !=
							(size_t)enc_hlen) {
			lwsl_err("Problem getting random\n");
			goto bail;
		}

		/* wrap with the derived key */

		el.buf = derived;
		el.len = (unsigned int)jwe->jose.alg->keybits_fixed / 8;

		if (lws_genaes_create(&aesctx, LWS_GAESO_ENC, LWS_GAESM_KW, &el,
					1, NULL)) {

			lwsl_notice("%s: lws_genaes_create\n", __func__);
			goto bail;
		}

		/* wrap CEK into EKEY */

		n = lws_genaes_crypt(&aesctx, cek, (unsigned int)enc_hlen,
				     (void *)jwe->jws.map.buf[LJWE_EKEY],
				     NULL, NULL, NULL, 0);
		m = lws_genaes_destroy(&aesctx, NULL, 0);
		if (n < 0) {
			lwsl_err("%s: encrypt cek fail\n", __func__);
			goto bail;
		}
		if (m < 0) {
			lwsl_err("%s: lws_genaes_destroy fail\n", __func__);
			goto bail;
		}

		jwe->jws.map.len[LJWE_EKEY] = (unsigned int)enc_hlen + 8;

		/* Wrapped CEK is in EKEY. Random CEK is in cek. */

	} else /* direct derived CEK is in cek */
		memcpy(cek, derived, (unsigned int)enc_hlen);

	/* rewrite the protected JOSE header to have the epk pieces */

	jwe->jws.map.buf[LJWE_JOSE] = temp;

	m = n = lws_snprintf(temp, (size_t)*temp_len,
			     "{\"alg\":\"%s\", \"enc\":\"%s\", \"epk\":",
			     jwe->jose.alg->alg, jwe->jose.enc_alg->alg);
	*temp_len -= n;

	n = lws_jwk_export(ephem, 0, temp + (ot - *temp_len), temp_len);
	if (n < 0) {
		lwsl_err("%s: ephemeral export failed\n", __func__);
		goto bail;
	}
	m += n;

	n = lws_snprintf(temp + (ot - *temp_len), (size_t)*temp_len, "}");
	*temp_len -= n + 1;
	m += n;
	jwe->jws.map.len[LJWE_JOSE] = (unsigned int)m;

	/* create a b64 version of the JOSE header, needed later for AAD */

	if (lws_jws_encode_b64_element(&jwe->jws.map_b64, LJWE_JOSE,
				       temp + (ot - *temp_len), temp_len,
				       jwe->jws.map.buf[LJWE_JOSE],
				       jwe->jws.map.len[LJWE_JOSE]))
		return -1;

	ret = enc_hlen;

bail:
	lws_genec_destroy(&ecctx);

	/* cleanse the shared secret (watch out for cek at parent too) */
	lws_explicit_bzero(shared_secret, sizeof(shared_secret));
	lws_explicit_bzero(derived, sizeof(derived));

	return ret;
}

int
lws_jwe_encrypt_ecdh_cbc_hs(struct lws_jwe *jwe, char *temp, int *temp_len)
{
	int ss_len, // kw_hlen = lws_genhash_size(jwe->jose.alg->hash_type),
	    enc_hlen = (int)lws_genhmac_size(jwe->jose.enc_alg->hmac_type);
	uint8_t cek[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES];
	int ekbytes = jwe->jose.alg->keybits_fixed ?
			jwe->jose.alg->keybits_fixed / 8 : enc_hlen;
	int n, ot = *temp_len, ret = -1;

	/* if we will produce an EKEY, make space for it */

	if (jwe->jose.alg->algtype_crypto != LWS_JOSE_ENCTYPE_NONE) {
		if (lws_jws_alloc_element(&jwe->jws.map, LJWE_EKEY,
					  temp + (ot - *temp_len), temp_len,
					  (unsigned int)enc_hlen + 8, 0))
			goto bail;
	}

	/* decrypt the CEK */

	ss_len = lws_jwe_encrypt_ecdh(jwe, temp + (ot - *temp_len), temp_len, cek);
	if (ss_len < 0) {
		lwsl_err("%s: lws_jwe_encrypt_ecdh failed\n", __func__);
		return -1;
	}

	/* cek contains the unwrapped CEK.  EKEY may contain wrapped CEK */

	/* make space for the payload encryption pieces */

	if (lws_jws_alloc_element(&jwe->jws.map, LJWE_ATAG,
				  temp + (ot - *temp_len),
				  temp_len, (unsigned int)enc_hlen / 2, 0))
		goto bail;

	if (lws_jws_alloc_element(&jwe->jws.map, LJWE_IV,
				  temp + (ot - *temp_len),
				  temp_len, LWS_JWE_AES_IV_BYTES, 0))
		goto bail;

	/* Perform the authenticated encryption on CTXT...
	 * ...the AAD is b64u(protected JOSE header) */

	n = lws_jwe_encrypt_cbc_hs(jwe, cek,
				   (uint8_t *)jwe->jws.map_b64.buf[LJWE_JOSE],
				   (int)jwe->jws.map_b64.len[LJWE_JOSE]);
	if (n < 0) {
		lwsl_notice("%s: lws_jwe_encrypt_cbc_hs failed\n", __func__);
		goto bail;
	}

	ret = 0;

bail:
	/* if fail or direct CEK, cleanse and remove EKEY */
	if (ret || jwe->jose.enc_alg->algtype_crypto == LWS_JOSE_ENCTYPE_NONE) {
		if (jwe->jws.map.len[LJWE_EKEY])
			lws_explicit_bzero((void *)jwe->jws.map.buf[LJWE_EKEY],
					   jwe->jws.map.len[LJWE_EKEY]);
		jwe->jws.map.len[LJWE_EKEY] = 0;
	}

	lws_explicit_bzero(cek, (unsigned int)ekbytes);

	return ret;
}

/*
 * jwe->jws.jwk is recipient private key
 *
 * If kw mode, then EKEY is the wrapped CEK
 *
 *
 */

static int
lws_jwe_auth_and_decrypt_ecdh(struct lws_jwe *jwe)
{
	uint8_t shared_secret[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES],
		derived[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES];
	int enc_hlen = (int)lws_genhmac_size(jwe->jose.enc_alg->hmac_type);
	int ekbytes = jwe->jose.enc_alg->keybits_fixed ?
			jwe->jose.enc_alg->keybits_fixed / 8 : enc_hlen;
	struct lws_genec_ctx ecctx;
	int n, ret = -1, ss_len = sizeof(shared_secret);

	if (jwe->jws.jwk->kty != LWS_GENCRYPTO_KTY_EC) {
		lwsl_err("%s: unexpected kty %d\n", __func__, jwe->jws.jwk->kty);

		return -1;
	}

	if (jwe->jose.recipient[jwe->recip].jwk_ephemeral.kty !=
			LWS_GENCRYPTO_KTY_EC) {
		lwsl_err("%s: missing epk\n", __func__);

		return -1;
	}

	/*
	 * Recompute the shared secret...
	 *
	 * - direct:  it's the CEK
	 *
	 * - aeskw: apply it as AES keywrap to EKEY to get the CEK
	 */

	/* Generate jose.jwk_ephemeral on the peer public key curve */

	if (lws_genecdh_create(&ecctx, jwe->jws.context, NULL))
		goto bail;

	/* Load our private key into our side of the ecdh context */

	if (lws_genecdh_set_key(&ecctx, jwe->jws.jwk->e, LDHS_OURS)) {
		lwsl_err("%s: setting our private key failed\n", __func__);
		goto bail;
	}

	/* Import the ephemeral public key into the peer side */
	if (lws_genecdh_set_key(&ecctx,
			jwe->jose.recipient[jwe->recip].jwk_ephemeral.e,
			LDHS_THEIRS)) {
		lwsl_err("%s: setting epk pubkey failed\n", __func__);
		goto bail;
	}

	/* combine their ephemeral key and our private key to get the secret */
	ss_len = (int)jwe->jws.jwk->e[LWS_GENCRYPTO_EC_KEYEL_X].len;

	if (lws_genecdh_compute_shared_secret(&ecctx, shared_secret, &ss_len)) {
		lwsl_notice("%s: lws_genecdh_compute_shared_secret failed\n",
				__func__);

		goto bail;
	}

	lws_genec_destroy(&ecctx);

	if (ss_len < enc_hlen) {
		lwsl_err("%s: ss_len %d ekbytes %d\n", __func__, ss_len, enc_hlen);
		goto bail;
	}

	/*
	 * Derive the CEK from the shared secret... amount of bytes written to
	 * cek[] matches bitcount in jwe->jose.enc_alg->keybits_fixed
	 */

	if (lws_jwa_concat_kdf(jwe,
			jwe->jose.alg->algtype_crypto == LWS_JOSE_ENCTYPE_NONE,
			derived, shared_secret, ss_len)) {
		lwsl_notice("%s: lws_jwa_concat_kdf failed\n", __func__);

		goto bail;
	}

	/*
	 * "ECDH-ES": derived is the CEK
	 * "ECDH-ES-AES[128,192,256]KW": wrapped key is in EKEY,
	 *				 "derived" contains KEK
	 */

	if (jwe->jose.alg->algtype_crypto != LWS_JOSE_ENCTYPE_NONE) {
		struct lws_gencrypto_keyelem el;
		struct lws_genaes_ctx aesctx;
		int m;

		/* Confirm space for EKEY */

		if (jwe->jws.map.len[LJWE_EKEY] < (unsigned int)enc_hlen) {
			lwsl_err("%s: missing EKEY\n", __func__);

			goto bail;
		}

		/* unwrap with the KEK we derived */

		el.buf = derived;
		el.len = (unsigned int)jwe->jose.alg->keybits_fixed / 8;

		if (lws_genaes_create(&aesctx, LWS_GAESO_DEC, LWS_GAESM_KW,
				      &el, 1, NULL)) {

			lwsl_notice("%s: lws_genaes_create\n", __func__);
			goto bail;
		}

		/* decrypt the EKEY to end up with CEK in "shared_secret" */

		n = lws_genaes_crypt(&aesctx,
				     (const uint8_t *)jwe->jws.map.buf[LJWE_EKEY],
				     jwe->jws.map.len[LJWE_EKEY],
				     (uint8_t *)shared_secret,
				     NULL, NULL, NULL, 0);
		m = lws_genaes_destroy(&aesctx, NULL, 0);
		if (n < 0) {
			lwsl_err("%s: decrypt cek fail\n", __func__);
			goto bail;
		}
		if (m < 0) {
			lwsl_err("%s: lws_genaes_destroy fail\n", __func__);
			goto bail;
		}
	} else
		memcpy(shared_secret, derived, (unsigned int)enc_hlen);

	/* either way, the recovered CEK is in shared_secret */

	if (lws_jwe_auth_and_decrypt_cbc_hs(jwe, shared_secret,
			(uint8_t *)jwe->jws.map_b64.buf[LJWE_JOSE],
			(int)jwe->jws.map_b64.len[LJWE_JOSE]) < 0) {
		lwsl_err("%s: lws_jwe_auth_and_decrypt_cbc_hs fail\n", __func__);
		goto bail;
	}

	/* if all went well, then CTXT is now the plaintext */
	ret = 0;

bail:
	/* cleanse wrapped on stack that contained the CEK / wrapped key */
	lws_explicit_bzero(derived, (unsigned int)ekbytes);
	/* cleanse the shared secret */
	lws_explicit_bzero(shared_secret, (unsigned int)ekbytes);

	return ret;
}

int
lws_jwe_auth_and_decrypt_ecdh_cbc_hs(struct lws_jwe *jwe,
				     char *temp, int *temp_len)
{
	/* create a b64 version of the JOSE header, needed later for AAD */

	if (lws_jws_encode_b64_element(&jwe->jws.map_b64, LJWE_JOSE,
				       temp, temp_len,
				       jwe->jws.map.buf[LJWE_JOSE],
				       jwe->jws.map.len[LJWE_JOSE]))
		return -1;

	return lws_jwe_auth_and_decrypt_ecdh(jwe);
}
