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

#include "test.h"
#include <key.h>
#include <librecast.h>
#include <sys/stat.h>

static int verify_token(state_t *state, key_combo_t *authring, key_combo_t *bearring,
		lc_channel_t *chan, /* uint64_t valid_sec,*/ uint8_t capbits)
{
	char *pathname;
	int rc;
	/* check token was written to disk */
	pathname = key_cap_path(state, lc_channel_get_hash(chan), 14);
	if (!test_assert(pathname != NULL, "key_cap_path() returned '%s'", pathname))
		return test_status;
	struct stat sb;
	rc = stat(pathname, &sb);
	test_assert(rc == 0, "stat() '%s' - file exists", pathname);
	free(pathname);
	if (test_status) return test_status;
	if (!test_assert((sb.st_mode & S_IFMT) == S_IFREG, "is a regular file")) return test_status;

	lc_token_t token = {0};
	rc = key_cap_load(&token, state, lc_channel_get_hash(chan), 14);
	if (!test_assert(rc == 0, "key_cap_load()")) return test_status;

	/* verify token */
	unsigned char *cap = (unsigned char *)&token;
	unsigned long long caplen = sizeof token - crypto_sign_BYTES;
	if (!test_assert(sodium_init() != -1, "sodium_init()")) return test_status;

	/* - check signature */
	test_assert(crypto_sign_verify_detached(token.sig, cap, caplen, authring->s.pk) == 0,
		"crypto_sign_open() - verify signature");

	/* check bearer key and capbits */
	test_assert(memcmp(token.bearkey, bearring->s.pk, crypto_sign_PUBLICKEYBYTES) == 0, "bearerkey match");
	test_assert(token.capbits == capbits, "capbits set");
	test_assert(token.expires > 0, "expires set");
	return test_status;
}

static int create_and_save_token(state_t *state, key_combo_t *authring, key_combo_t *bearring)
{
	lc_ctx_t *lctx;
	lc_channel_t *chan;
	uint64_t valid_sec = 60;
	uint8_t capbits = 0;
	int flags = 0;
	int rc;

	/* create channel for token */
	lctx = lc_ctx_new();
	if (!test_assert(lctx != NULL, "lc_ctx_new()"))
		return -1;
	chan = lc_channel_new(lctx, "Black Lives (Still) Matter");
	if (!test_assert(chan != NULL, "lc_channel_new()"))
		goto free_lctx;

	/* create and discard token */
	rc = key_cap_issue(state, &authring->s, bearring->s.pk, chan, capbits, valid_sec, flags);
	if (!test_assert(rc == 0, "key_cap_issue() returned %i", rc)) return test_status;

	/* create and save token */
	flags = KEY_CAP_SAVE;
	rc = key_cap_issue(state, &authring->s, bearring->s.pk, chan, capbits, valid_sec, flags);
	if (!test_assert(rc == 0, "key_cap_issue() returned %i", rc)) return test_status;

	verify_token(state, authring, bearring, chan, /*valid_sec,*/ capbits);

free_lctx:
	lc_ctx_free(lctx);

	return test_status;
}

int main(void)
{
	char name[] = "key_cap_issue() - save token to disk";
	char fakehome[] = "0000-0012-XXXXXX";
	key_combo_t authring, bearring;
	int rc;

	test_name(name);

	/* first, create authority keypair */
	rc = key_gen_combopair_hex(&authring);
	if (!test_assert(rc == 0, "key_gen_combopair() - AUTHORITY"))
		return test_status;

	/* next, create bearer keypair */
	rc = key_gen_combopair_hex(&bearring);
	if (!test_assert(rc == 0, "key_gen_combopair() - BEARER"))
		return test_status;

	/* create fake home directory */
	if (!test_assert(mkdtemp(fakehome) != NULL, "mkdtemp()")) {
		perror("mkdtemp");
		return test_status;
	}

	/* create state directories */
	state_t state = {0};
	rc = state_dirs(&state, fakehome);
	if (!test_assert(rc == 0, "state_dirs() returned %i", rc))
		return test_status;

	create_and_save_token(&state, &authring, &bearring);

	free_state(&state);

	return test_status;
}
