/**
 * utils.c
 *
 * Created on 2020-11-27
 * @author: qyc
 *
 * @explain: 
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "utils.h"
#include "wsgcrypt.h"
#include "pint.h"

/*
 * Computes HKDF-Expand-Label(Secret, Label, Hash(context_value), Length) with a
 * custom label prefix. If "context_hash" is NULL, then an empty context is
 * used. Otherwise it must have the same length as the hash algorithm output.
 */
static gboolean tls13_hkdf_expand_label_context(int md, const StringInfo *secret, const char *label_prefix, const char *label, const guint8 *context_hash, guint8 context_length, guint16 out_len, guchar **out)
{
	/* RFC 8446 Section 7.1:
	 * HKDF-Expand-Label(Secret, Label, Context, Length) =
	 *      HKDF-Expand(Secret, HkdfLabel, Length)
	 * struct {
	 *     uint16 length = Length;
	 *     opaque label<7..255> = "tls13 " + Label; // "tls13 " is label prefix.
	 *     opaque context<0..255> = Context;
	 * } HkdfLabel;
	 *
	 * RFC 5869 HMAC-based Extract-and-Expand Key Derivation Function (HKDF):
	 * HKDF-Expand(PRK, info, L) -> OKM
	 */
	gcry_error_t err;
	const guint label_prefix_length = (guint)strlen(label_prefix);
	const guint label_length = (guint)strlen(label);

	// Some sanity checks
	g_assert(label_length > 0 && label_prefix_length + label_length <= 255);

	// info = HkdfLabel { length, label, context }
	GByteArray *info = g_byte_array_new();
	const guint16 length = g_htons(out_len);
	g_byte_array_append(info, (const guint8 *)&length, sizeof(length));

	const guint8 label_vector_length = label_prefix_length + label_length;
	g_byte_array_append(info, &label_vector_length, 1);
	g_byte_array_append(info, (const guint8 *)label_prefix, label_prefix_length);
	g_byte_array_append(info, (const guint8 *)label, label_length);

	g_byte_array_append(info, &context_length, 1);
	if (context_length)
		g_byte_array_append(info, context_hash, context_length);
	
	*out = (guchar *)g_malloc(out_len);
	err = hkdf_expand(md, secret->data, secret->data_len, info->data, info->len, *out, out_len);
	g_byte_array_free(info, TRUE);

	if (err) {
		printf("%s failed %d: %s\n", G_STRFUNC, md, gcry_strerror(err));
		g_free(*out);
		*out = NULL;
		return FALSE;
	}

	return TRUE;
}

gboolean tls13_hkdf_expand_label(int md, const StringInfo *secret, const char *label_prefix, const char *label, guint16 out_len, guchar **out)
{
	return tls13_hkdf_expand_label_context(md, secret, label_prefix, label, NULL, 0, out_len, out);
}

static guint8 tvb_get_guint8(const char *tvb, const gint offset)
{
	const guint8 *ptr;

	ptr = (guint8 *)tvb + offset;
	return *ptr;
}

static guint16 tvb_get_ntohs(const char *tvb, const gint offset)
{
	const guint8 *ptr;

	ptr = (guint8 *)tvb + offset;
	return pntoh16(ptr);
}

static guint32 tvb_get_ntohl(const char *tvb, const gint offset)
{
	const guint8 *ptr;

	ptr = (guint8 *)tvb + offset;
	return pntoh32(ptr);
}

static guint64 tvb_get_ntoh64(const char *tvb, const gint offset)
{
	const guint8 *ptr;

	ptr = (guint8 *)tvb + offset;
	return pntoh64(ptr);
}

guint tvb_get_varint(const char *tvb, guint offset, guint maxlen, guint64 *value, const guint encoding)
{
	*value = 0;

	if (encoding & ENC_VARINT_QUIC) {
		// calculate variable length
		*value = tvb_get_guint8(tvb, offset);
		switch((*value) >> 6) {
			case 0: /* 0b00 => 1 byte length (6 bits Usable) */
				(*value) &= 0x3F;
				return 1;
			case 1: /* 0b01 => 2 bytes length (14 bits Usable) */
				*value = tvb_get_ntohs(tvb, offset) & 0x3FFF;
				return 2;
			case 2: /* 0b10 => 4 bytes length (30 bits Usable) */
				*value = tvb_get_ntohl(tvb, offset) & 0x3FFFFFFF;
				return 4;
			case 3: /* 0b11 => 8 bytes length (62 bits Usable) */
				*value = tvb_get_ntoh64(tvb, offset) & G_GUINT64_CONSTANT(0x3FFFFFFFFFFFFFFF);
				return 8;
			default: /* No Possible */
				g_assert_not_reached();
				break;
		}
	}

	// 10 bytes scanned, but no bytes' msb is zero
	return 0;
}
