/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */
/* Copyright (c) 2025 Brett Sheffield <bacs@librecast.net> */

#define _GNU_SOURCE /* recvmmsg */
#include <librecast_pvt.h>
#include <librecast/net.h>
#include <librecast/key.h>
#include <string.h>

int lc_token_new(lc_token_t *token, lc_keypair_t *signing_key, uint8_t *bearer_key,
	lc_channel_t *chan, uint8_t capbits, uint64_t valid_sec)
{
	const unsigned char *m = (unsigned char *)token;
	const unsigned long long mlen = sizeof(lc_token_t) - crypto_sign_BYTES;
	struct timespec expires;
	token->version = 0xff;
	token->capbits = capbits;
	token->expires = valid_sec;
	if (valid_sec) {
		if (clock_gettime(CLOCK_REALTIME, &expires) == -1) return -1;
		token->expires += expires.tv_sec;
	}
	memcpy(token->channel, lc_channel_get_hash(chan), sizeof token->channel);
	memcpy(token->signkey, signing_key->pk, sizeof token->signkey);
	memcpy(token->bearkey, bearer_key, sizeof token->bearkey);
	if (crypto_sign_detached(token->sig, NULL, m, mlen, signing_key->sk) == -1)
		return (errno = ENOTRECOVERABLE), -1;
	return 0;
}

int lc_keypair_new(lc_keypair_t *key, int flags)
{
	if (flags & LC_KEY_SIG) {
		return crypto_sign_keypair(key->pk, key->sk);
	}
	if (flags & LC_KEY_ENC) {
		return crypto_box_keypair(key->pk, key->sk);
	}
	return (errno = EINVAL), -1;
}

int lc_keyring_init(lc_keyring_t *keyring, size_t nkeys)
{
	keyring->key = calloc(nkeys, sizeof(uint8_t *));
	if (!keyring->key) return -1;
	keyring->nkeys = 0;
	keyring->len = nkeys;
	return 0;
}

void lc_keyring_free(lc_keyring_t *keyring)
{
	free(keyring->key);
}

int lc_keyring_has(lc_keyring_t *keyring, uint8_t *key)
{
	if (keyring->key) {
		for (size_t n = 0; n < keyring->nkeys; n++) {
			if (!keyring->key[n]) continue;
			if (!memcmp(keyring->key[n], key, crypto_sign_PUBLICKEYBYTES))
				return -1;
		}
	}
	return 0;
}

int lc_keyring_add(lc_keyring_t *keyring, uint8_t *key)
{
	if (keyring->nkeys >= keyring->len) return (errno = ENOMEM), -1;
	keyring->key[keyring->nkeys++] = key;
	return 0;
}

int lc_keyring_del(lc_keyring_t *keyring, uint8_t *key)
{
	if (keyring->key) {
		for (size_t n = 0; n < keyring->nkeys; n++) {
			if (!keyring->key[n]) continue;
			if (!memcmp(keyring->key[n], key, crypto_sign_PUBLICKEYBYTES)) {
				/* key found, delete it */
				keyring->nkeys--;
				if (keyring->nkeys > 0) {
					size_t sz = sizeof(uint8_t *) * (keyring->nkeys - n);
					memmove(&keyring->key[n], &keyring->key[n+1], sz);
				}
				return 0;
			}
		}
	}
	return (errno = ENOKEY), -1;
}

void lc_channel_filter_set(lc_channel_t *chan, lc_filter_t *filter)
{
	chan->filter = filter;
}

void lc_channel_token_set(lc_channel_t *chan, lc_token_t *token)
{
	chan->token = token;
}
