/* Ascon-128 and Ascon-128a specific code
 *
 * Copyright (C) 2024 - 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 "alignment.h"
#include "ascon_internal.h"
#include "build_bug_on.h"
#include "compare.h"
#include "fips_mode.h"
#include "lc_ascon_hash.h"
#include "lc_ascon_lightweight.h"
#include "timecop.h"
#include "visibility.h"

/*
 * Ascon with standard Ascon permutation
 */
#define LC_AEAD_ASCON_128a_IV 0x00001000808c0001

static int lc_ascon_setiv(struct lc_ascon_cryptor *ascon, size_t keylen,
			  int nocheck);
static void ascon_aead_selftest(void)
{
	/*
	 * Vector 1089 from genkat_crypto_aead_asconaead128_ref generated by
	 * code https://github.com/ascon/ascon-c
	 */
	LC_FIPS_RODATA_SECTION
	static const uint8_t pt[] = { FIPS140_MOD(0x00),
				      0x01,
				      0x02,
				      0x03,
				      0x04,
				      0x05,
				      0x06,
				      0x07,
				      0x08,
				      0x09,
				      0x0A,
				      0x0B,
				      0x0C,
				      0x0D,
				      0x0E,
				      0x0F,
				      0x10,
				      0x11,
				      0x12,
				      0x13,
				      0x14,
				      0x15,
				      0x16,
				      0x17,
				      0x18,
				      0x19,
				      0x1A,
				      0x1B,
				      0x1C,
				      0x1D,
				      0x1E,
				      0x1F };
	LC_FIPS_RODATA_SECTION
	static const uint8_t key[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
				       0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
				       0x0C, 0x0D, 0x0E, 0x0F };
	LC_FIPS_RODATA_SECTION
	static const uint8_t exp_ct[] = { 0x4C, 0x08, 0x6D, 0x27, 0xA3, 0xB5,
					  0x1A, 0x23, 0x33, 0xCF, 0xC7, 0xF2,
					  0x21, 0x72, 0xA9, 0xBC, 0xAD, 0x88,
					  0xB8, 0xD4, 0xD7, 0x7E, 0x50, 0x62,
					  0x2D, 0x78, 0x83, 0x45, 0xFA, 0x7B,
					  0xEE, 0x44 };
	LC_FIPS_RODATA_SECTION
	static const uint8_t exp_tag[] = { 0x68, 0x91, 0x5D, 0x3F, 0x94, 0x22,
					   0x28, 0x9F, 0x23, 0x49, 0xD6, 0xA3,
					   0xB4, 0x16, 0x03, 0x97 };

	uint8_t out_enc[sizeof(exp_ct)];
	uint8_t tag[sizeof(exp_tag)];

	LC_SELFTEST_RUN(lc_ascon_aead->algorithm_type);

	LC_AL_CTX_ON_STACK(al);

	/* One shot encryption with pt ptr != ct ptr */
	if (lc_ascon_setkey_int(al->aead_state, key, sizeof(key), key,
				sizeof(key), 1, lc_ascon_setiv))
		goto out;
	lc_aead_encrypt(al, pt, out_enc, sizeof(pt), pt, sizeof(pt), tag,
			sizeof(tag));
	lc_aead_zero(al);
	if (lc_compare_selftest(
		    lc_ascon_aead->algorithm_type, out_enc, exp_ct,
		    sizeof(exp_ct),
		    "Ascon lightweight crypt: Encryption, ciphertext"))
		goto out2;
	if (lc_compare_selftest(lc_ascon_aead->algorithm_type, tag, exp_tag,
				sizeof(exp_tag),
				"Ascon lightweight crypt: Encryption, tag"))
		goto out2;
	/* One shot decryption with pt ptr != ct ptr */
	if (lc_ascon_setkey_int(al->aead_state, key, sizeof(key), key,
				sizeof(key), 1, lc_ascon_setiv))
		goto out;
	lc_aead_decrypt(al, out_enc, out_enc, sizeof(out_enc), pt, sizeof(pt),
			tag, sizeof(tag));

out:
	lc_compare_selftest(lc_ascon_aead->algorithm_type, out_enc, pt,
			    sizeof(pt),
			    "Ascon lightweight crypt: Decryption, plaintext");
out2:
	lc_aead_zero(al);
}

static int lc_ascon_setiv(struct lc_ascon_cryptor *ascon, size_t keylen,
			  int nocheck)
{
	const struct lc_hash *hash = ascon->hash;
	uint64_t *state_mem = ascon->state;

	/* Check that the key store is sufficiently large */
	BUILD_BUG_ON(sizeof(ascon->key) < 16);

	switch (hash->sponge_rate) {
	case 128 / 8: /* Ascon 128a */

		if (!nocheck) {
			ascon_aead_selftest();
			LC_SELFTEST_COMPLETED(lc_ascon_aead->algorithm_type);
		}

		if (keylen != 16)
			return -EINVAL;
		state_mem[0] = LC_AEAD_ASCON_128a_IV;
		ascon->keylen = 16;
		ascon->roundb = 8;

		return 0;
	default:
		break;
	}

	return -EINVAL;
}

static int lc_ascon_setkey(void *state, const uint8_t *key, size_t keylen,
			   const uint8_t *nonce, size_t noncelen)
{
	return lc_ascon_setkey_int(state, key, keylen, nonce, noncelen, 0,
				   lc_ascon_setiv);
}

static const struct lc_aead _lc_ascon_aead = {
	.setkey = lc_ascon_setkey,
	.encrypt = lc_ascon_encrypt,
	.enc_init = lc_ascon_aad_interface,
	.enc_update = lc_ascon_enc_update_interface,
	.enc_final = lc_ascon_enc_final_interface,
	.decrypt = lc_ascon_decrypt,
	.dec_init = lc_ascon_aad_interface,
	.dec_update = lc_ascon_dec_update_interface,
	.dec_final = lc_ascon_dec_final_interface,
	.zero = lc_ascon_zero_interface,
	.algorithm_type = LC_ALG_STATUS_ASCON_AEAD_128,
};
LC_INTERFACE_SYMBOL(const struct lc_aead *, lc_ascon_aead) = &_lc_ascon_aead;

LC_INTERFACE_FUNCTION(int, lc_al_alloc, struct lc_aead_ctx **ctx)
{
	struct lc_aead_ctx *tmp = NULL;
	struct lc_ascon_cryptor *ascon;
	int ret;

	ret = lc_alloc_aligned((void **)&tmp, LC_ASCON_ALIGNMENT,
			       LC_AL_CTX_SIZE);
	if (ret)
		return -ret;

	LC_ASCON_SET_CTX(tmp, lc_ascon_128a, lc_ascon_aead);

	ascon = tmp->aead_state;
	ascon->statesize = LC_ASCON_HASH_STATE_SIZE;
	ascon->taglen = 16;

	*ctx = tmp;

	return 0;
}
