/*
 * libwebsockets - small server side websockets and web server implementation
 *
 * Copyright (C) 2010 - 2021 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.
 *
 * Shared JWK handling that's the same whether JOSE or COSE
 */

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

static const char *meta_names[] = {
	"kty", "kid", "use", "key_ops", "x5c", "alg"
};

static const char meta_b64[] = { 0, 0, 0, 0, 1, 0 };

static const char *oct_names[] = {
	"k"
};
static const char oct_b64[] = { 1 };

static const char *rsa_names[] = {
	"e", "n", "d", "p", "q", "dp", "dq", "qi"
};
static const char rsa_b64[] = { 1, 1, 1, 1, 1, 1, 1, 1 };

static const char *ec_names[] = {
	"crv", "x", "d", "y",
};
static const char ec_b64[] = { 0, 1, 1, 1 };

static const char *okp_names[] = {
	"crv", "x", "d",
};
static const char okp_b64[] = { 0, 1, 1 };

int
lws_jwk_dump(struct lws_jwk *jwk)
{
	const char **enames, *b64;
	int elems;
	int n;

	(void)enames;
	(void)meta_names;

	switch (jwk->kty) {
	default:
	case LWS_GENCRYPTO_KTY_UNKNOWN:
		lwsl_err("%s: jwk %p: unknown type\n", __func__, jwk);

		return 1;
	case LWS_GENCRYPTO_KTY_OCT:
		elems = LWS_GENCRYPTO_OCT_KEYEL_COUNT;
		enames = oct_names;
		b64 = oct_b64;
		break;
	case LWS_GENCRYPTO_KTY_RSA:
		elems = LWS_GENCRYPTO_RSA_KEYEL_COUNT;
		enames = rsa_names;
		b64 = rsa_b64;
		break;
	case LWS_GENCRYPTO_KTY_EC:
		elems = LWS_GENCRYPTO_EC_KEYEL_COUNT;
		enames = ec_names;
		b64 = ec_b64;
		break;
	case LWS_GENCRYPTO_KTY_OKP:
		elems = LWS_GENCRYPTO_OKP_KEYEL_COUNT;
		enames = okp_names;
		b64 = okp_b64;
		break;
	}

	lwsl_info("%s: jwk %p\n", __func__, jwk);

	for (n = 0; n < LWS_COUNT_JWK_ELEMENTS; n++) {
		if (jwk->meta[n].buf && meta_b64[n]) {
			lwsl_info("  meta: %s\n", meta_names[n]);
			lwsl_hexdump_info(jwk->meta[n].buf, jwk->meta[n].len);
		}
		if (jwk->meta[n].buf && !meta_b64[n])
			lwsl_info("  meta: %s: '%s'\n", meta_names[n],
					jwk->meta[n].buf);
	}

	for (n = 0; n < elems; n++) {
		if (jwk->e[n].buf && b64[n]) {
			lwsl_info("  e: %s\n", enames[n]);
			lwsl_hexdump_info(jwk->e[n].buf, jwk->e[n].len);
		}
		if (jwk->e[n].buf && !b64[n])
			lwsl_info("  e: %s: '%s'\n", enames[n], jwk->e[n].buf);
	}

	return 0;
}

int
_lws_jwk_set_el_jwk(struct lws_gencrypto_keyelem *e, char *in, size_t len)
{
	e->buf = lws_malloc(len + 1, "jwk");
	if (!e->buf)
		return -1;

	memcpy(e->buf, in, len);
	e->buf[len] = '\0';
	e->len = (uint32_t)len;

	return 0;
}

void
lws_jwk_destroy_elements(struct lws_gencrypto_keyelem *el, int m)
{
	int n;

	for (n = 0; n < m; n++)
		if (el[n].buf) {
			/* wipe all key material when it goes out of scope */
			lws_explicit_bzero(el[n].buf, el[n].len);
			lws_free_set_NULL(el[n].buf);
			el[n].len = 0;
		}
}

void
lws_jwk_destroy(struct lws_jwk *jwk)
{
	lws_jwk_destroy_elements(jwk->e, LWS_ARRAY_SIZE(jwk->e));
	lws_jwk_destroy_elements(jwk->meta, LWS_ARRAY_SIZE(jwk->meta));
}

void
lws_jwk_init_jps(struct lws_jwk_parse_state *jps,
		 struct lws_jwk *jwk, lws_jwk_key_import_callback cb,
		 void *user)
{
	if (jwk)
		memset(jwk, 0, sizeof(*jwk));

	jps->jwk		= jwk;
	jps->possible		= F_RSA | F_EC | F_OCT | F_OKP;
	jps->per_key_cb		= cb;
	jps->user		= user;
	jps->pos		= 0;
	jps->seen		= 0;
	jps->cose_state		= 0;
}

int
lws_jwk_dup_oct(struct lws_jwk *jwk, const void *key, int len)
{
	unsigned int ulen = (unsigned int)len;

	jwk->e[LWS_GENCRYPTO_KTY_OCT].buf = lws_malloc(ulen, __func__);
	if (!jwk->e[LWS_GENCRYPTO_KTY_OCT].buf)
		return -1;

	jwk->kty = LWS_GENCRYPTO_KTY_OCT;
	jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].len = ulen;

	memcpy(jwk->e[LWS_GENCRYPTO_KTY_OCT].buf, key, ulen);

	return 0;
}

int
lws_jwk_generate(struct lws_context *context, struct lws_jwk *jwk,
	         enum lws_gencrypto_kty kty, int bits, const char *curve)
{
	size_t sn;
	int n;

	memset(jwk, 0, sizeof(*jwk));

	jwk->kty = (int)kty;
	jwk->private_key = 1;

	switch (kty) {
	case LWS_GENCRYPTO_KTY_RSA:
	{
		struct lws_genrsa_ctx ctx;

		lwsl_notice("%s: generating %d bit RSA key\n", __func__, bits);
		n = lws_genrsa_new_keypair(context, &ctx, LGRSAM_PKCS1_1_5,
					    jwk->e, bits);
		lws_genrsa_destroy(&ctx);
		if (n) {
			lwsl_err("%s: problem generating RSA key\n", __func__);
			return 1;
		}
	}
		break;
	case LWS_GENCRYPTO_KTY_OCT:
		sn = (unsigned int)lws_gencrypto_bits_to_bytes(bits);
		jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].buf = lws_malloc(sn, "oct");
		if (!jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].buf)
			return 1;
		jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].len = (uint32_t)sn;
		if (lws_get_random(context,
			     jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].buf, sn) != sn) {
			lwsl_err("%s: problem getting random\n", __func__);
			return 1;
		}
		break;
	case LWS_GENCRYPTO_KTY_EC:
	{
		struct lws_genec_ctx ctx;

		if (!curve) {
			lwsl_err("%s: must have a named curve\n", __func__);

			return 1;
		}

		if (lws_genecdsa_create(&ctx, context, NULL))
			return 1;

		lwsl_notice("%s: generating ECDSA key on curve %s\n", __func__,
				curve);

		n = lws_genecdsa_new_keypair(&ctx, curve, jwk->e);
		lws_genec_destroy(&ctx);
		if (n) {
			lwsl_err("%s: problem generating ECDSA key\n", __func__);
			return 1;
		}
	}
		break;

	case LWS_GENCRYPTO_KTY_OKP:
	{
		struct lws_genec_ctx ctx;

		if (!curve) {
			lwsl_err("%s: must have a named curve\n", __func__);

			return 1;
		}

		if (lws_geneddsa_create(&ctx, context, NULL))
			return 1;

		lwsl_notice("%s: generating EdDSA key on curve %s\n", __func__,
				curve);

		n = lws_geneddsa_new_keypair(&ctx, curve, jwk->e);
		lws_genec_destroy(&ctx);
		if (n) {
			lwsl_err("%s: problem generating EdDSA key\n", __func__);
			return 1;
		}
	}
		break;

	case LWS_GENCRYPTO_KTY_UNKNOWN:
	default:
		lwsl_err("%s: unknown kty\n", __func__);
		return 1;
	}

	return 0;
}

int
lws_jwk_rfc7638_fingerprint(struct lws_jwk *jwk, char *digest32)
{
	struct lws_genhash_ctx hash_ctx;
	size_t tmpsize = 2536;
	char *tmp;
	int n, m = (int)tmpsize;

	tmp = lws_malloc(tmpsize, "rfc7638 tmp");

	n = lws_jwk_export(jwk, LWSJWKF_EXPORT_NOCRLF, tmp, &m);
	if (n < 0)
		goto bail;

	if (lws_genhash_init(&hash_ctx, LWS_GENHASH_TYPE_SHA256))
		goto bail;

	if (lws_genhash_update(&hash_ctx, tmp, (unsigned int)n)) {
		lws_genhash_destroy(&hash_ctx, NULL);

		goto bail;
	}
	lws_free(tmp);

	if (lws_genhash_destroy(&hash_ctx, digest32))
		return -1;

	return 0;

bail:
	lws_free(tmp);

	return -1;
}

int
lws_jwk_strdup_meta(struct lws_jwk *jwk, enum enum_jwk_meta_tok idx,
		    const char *in, int len)
{
	jwk->meta[idx].buf = lws_malloc((unsigned int)len, __func__);
	if (!jwk->meta[idx].buf)
		return 1;
	jwk->meta[idx].len = (uint32_t)(unsigned int)len;
	memcpy(jwk->meta[idx].buf, in, (unsigned int)len);

	return 0;
}

