/*
 * Copyright (C) 2020 - 2026, Stephan Mueller <smueller@chronox.de>
 *
 * License: see LICENSE file in root directory
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
 * WHICH ARE HEREBY DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */

#include "compare.h"
#include "lc_ascon_hash.h"
#include "test_helper_common.h"
#include "visibility.h"

#include "ascon_avx512.h"
#include "ascon_arm_neon.h"
#include "ascon_c.h"

#define LC_EXEC_ONE_TEST(ascon_impl)                                           \
	if (ascon_impl)                                                        \
	ret += ascon_128_tester(ascon_impl, #ascon_impl)

static int ascon_128_tester(const struct lc_hash *ascon, const char *name)
{
	/*
	 * Vector 5 from genkat_crypto_hash_asconhash256_ref generated by code
	 * https://github.com/ascon/ascon-c
	 */
	static const uint8_t msg[] = { 0x00, 0x01, 0x02, 0x03 };
	static const uint8_t exp[] = { 0xD7, 0xE4, 0xC7, 0xED, 0x9B, 0x8A, 0x32,
				       0x5C, 0xD0, 0x8B, 0x9E, 0xF2, 0x59, 0xF8,
				       0x87, 0x70, 0x54, 0xEC, 0xD8, 0x30, 0x4F,
				       0xE1, 0xB2, 0xD7, 0xFD, 0x84, 0x71, 0x37,
				       0xDF, 0x67, 0x27, 0xEE };
	uint8_t act[LC_ASCON_HASH_DIGESTSIZE];
	int ret;
	LC_ASCON_CTX_ON_STACK(ascon_stack, ascon);

	printf("hash ctx %s (%s implementation) len %u\n", name,
	       ascon == lc_ascon_256_c ? "C" : "accelerated",
	       (unsigned int)LC_HASH_CTX_SIZE);

	if (lc_hash(ascon, msg, sizeof(msg), act))
		return 1;
	ret = lc_compare(act, exp, sizeof(act), "Ascon 128");

	if (lc_hash_init(ascon_stack))
		return 1;
	lc_hash_update(ascon_stack, msg, sizeof(msg));
	lc_hash_final(ascon_stack, act);
	lc_hash_zero(ascon_stack);
	ret += lc_compare(act, exp, sizeof(act), "Ascon 128 stack");

	return ret;
}

LC_TEST_FUNC(int, main, int argc, char *argv[])
{
	int ret = 0;

	(void)argc;
	(void)argv;

	LC_EXEC_ONE_TEST(lc_ascon_256);
	LC_EXEC_ONE_TEST(lc_ascon_256_c);
	LC_EXEC_ONE_TEST(lc_ascon_256_avx512);
	LC_EXEC_ONE_TEST(lc_ascon_256_arm_neon);

	ret = test_validate_status(ret, lc_hash_alg_status(lc_ascon_256), 1);
	ret = test_validate_status(ret, lc_hash_alg_status(lc_ascon_256_c),
				   lc_ascon_256 == lc_ascon_256_c);
	ret = test_validate_status(ret, lc_hash_alg_status(lc_ascon_256_avx512),
				   lc_ascon_256 == lc_ascon_256_avx512);
	ret = test_validate_status(ret,
				   lc_hash_alg_status(lc_ascon_256_arm_neon),
				   lc_ascon_256 == lc_ascon_256_arm_neon);
	ret += test_print_status();

	return ret;
}
