/*
 * Copyright (C) 2022 - 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.
 */

#ifndef LC_AEAD_H
#define LC_AEAD_H

#include "lc_aes.h"
#include "lc_memory_support.h"
#include "lc_status.h"

#ifdef __cplusplus
extern "C" {
#endif

/// \cond DO_NOT_DOCUMENT
struct lc_aead {
	int (*setkey)(void *state, const uint8_t *key, const size_t keylen,
		      const uint8_t *iv, size_t ivlen);
	int (*encrypt)(void *state, const uint8_t *plaintext,
		       uint8_t *ciphertext, size_t datalen, const uint8_t *aad,
		       size_t aadlen, uint8_t *tag, size_t taglen);
	int (*enc_init)(void *state, const uint8_t *aad, size_t aadlen);
	int (*enc_update)(void *state, const uint8_t *plaintext,
			  uint8_t *ciphertext, size_t datalen);
	int (*enc_final)(void *state, uint8_t *tag, size_t taglen);
	int (*decrypt)(void *state, const uint8_t *ciphertext,
		       uint8_t *plaintext, size_t datalen, const uint8_t *aad,
		       size_t aadlen, const uint8_t *tag, size_t taglen);
	int (*dec_init)(void *state, const uint8_t *aad, size_t aadlen);
	int (*dec_update)(void *state, const uint8_t *ciphertext,
			  uint8_t *plaintext, size_t datalen);
	int (*dec_final)(void *state, const uint8_t *tag, size_t taglen);
	void (*zero)(void *state);
	uint64_t algorithm_type;
};

#define LC_AEAD_MAX_KEYSIZE 64
#define LC_AEAD_MIN_KEYSIZE 16

struct lc_aead_ctx {
	uint8_t key[LC_AEAD_MAX_KEYSIZE];
	uint8_t keylen;
	const struct lc_aead *aead;
	void *aead_state;
};

#define LC_AEAD_CTX_NO_STATE(name, cb)                                         \
	name->aead = cb;                                                       \
	name->aead_state = NULL

#define LC_AEAD_CTX(name, cb)                                                  \
	name->aead = cb;                                                       \
	name->aead_state = (uint8_t *)(name) + sizeof(struct lc_aead_ctx)

#define LC_AEAD_HASH_ALIGN_CTX(name, cb)                                       \
	name->aead = cb;                                                       \
	name->aead_state = LC_ALIGN_HASH_MASK((uint8_t *)(name) +              \
					      sizeof(struct lc_aead_ctx))
/// \endcond

/** @defgroup AEAD Authenticated Encryption with Associated Data
 *
 * Concept of AEAD algorithms in leancrypto
 *
 * All AEAD algorithms can be used with the API calls documented below. However,
 * the allocation part is AEAD-algorithm-specific. Thus, perform the following
 * steps
 *
 * 1. Allocation: Use the stack or heap allocation functions documented in
 *    lc_chacha20_poly1305.h, lc_aes_gcm.h,
 *    lc_ascon_lightweight.h, lc_ascon_keccak.h, lc_cshake_crypt.h,
 *    lc_kmac_crypt.h, lc_hash_crypt.h, lc_symhmac.h, or lc_symkmac.h.
 *
 * 2. Use the returned cipher handle with the API calls below.
 *
 * Concept of parallel use of cipher contexts
 *
 * In threaded environments the caller may want to reuse one cipher handle
 * to be called parallel by different threads. This is achieved by the
 * consideration that
 *
 * 1. A "key cipher handle" will only hold the key which is static for all
 *    context invocations.
 *
 * 2. A separate "volatile cipher handle" will process the actual data.
 *
 * Therefore, perform the following steps:
 *
 * Prior to parallel use:
 * struct lc_aead_ctx key_ctx;
 * LC_AEAD_CTX_NO_STATE(&key_ctx);
 * lc_aead_setkey(key_ctx, key, keylen, NULL, 0);
 *
 * Parallel use, invoke for each thread:
 * volatile_ctx = allocate algorithm
 * lc_aead_setkey_from_ctx(volatile_ctx, key_ctx, iv, ivlen);
 * lc_aead_[en|de]crypt(); or stream operation
 * lc_aead_zero[_free](volatile_ctx);
 *
 * After parallel use:
 * lc_sym_zero[_free](key_ctx);
 */

/**
 * @ingroup AEAD
 * @brief Zeroize AEAD context
 *
 * @param [in] ctx AEAD context to be zeroized
 */
void lc_aead_zero(struct lc_aead_ctx *ctx);

/**
 * @ingroup AEAD
 * @brief Zeroize and free AEAD context
 *
 * @param [in] ctx AEAD context to be zeroized and freed
 */
void lc_aead_zero_free(struct lc_aead_ctx *ctx);

/**
 * @ingroup AEAD
 * @brief Set the key for the AEAD encyption or decryption operation
 *
 * @param [in] ctx AEAD context handle
 * @param [in] key Buffer with key
 * @param [in] keylen Length of key buffer
 * @param [in] iv initialization vector to be used
 * @param [in] ivlen length of initialization vector
 *
 * The algorithm supports a key of arbitrary size. The only requirement is that
 * the same key is used for decryption as for encryption.
 *
 * \note The key / IV are allowed to be unset with the following result:
 * * If key and IV are set: AEAD context is initialized and ready for use with
 *   encrypt or decrypt.
 * * If neither key nor IV are given, an error is returned as this is invalid.
 * * If key is given, but no IV: the key is retained for later use, and it
 *   initializes the context. The retention is of interest for
 *   \p lc_aead_setkey_from_ctx.
 * * If no key is given but an IV: the previously retained key is used together
 *   with the IV to initialize the context.
 *
 * @return 0 upon success; < 0 on error
 */
int lc_aead_setkey(struct lc_aead_ctx *ctx, const uint8_t *key,
		   const size_t keylen, const uint8_t *iv, size_t ivlen);

/**
 * @ingroup AEAD
 * @brief Set the key for the AEAD encyption or decryption operation
 *
 * This call is intended to support parallel uses of the AEAD key context.
 * The key can be set once in the \p key_ctx and reused by new AEAD contexts
 * for normal operation.
 *
 * @param [in] ctx AEAD context handle
 * @param [in] key_ctx AEAD context with retained key (see the case in
 * 		       \p lc_aead_setkey where a key but no IV is set)
 * @param [in] iv initialization vector to be used
 * @param [in] ivlen length of initialization vector
 *
 * \note It is only permissible to use the function where the \p ctx and
 * \p key_ctx are using the same algorithm.
 *
 * @return 0 upon success; < 0 on error
 */
int lc_aead_setkey_from_ctx(struct lc_aead_ctx *ctx,
			    const struct lc_aead_ctx *key_ctx,
			    const uint8_t *iv, size_t ivlen);

/**
 * @ingroup AEAD
 * @brief AEAD-encrypt data in one call
 *
 * @param [in] ctx AEAD context handle with key set / IV
 * @param [in] plaintext Plaintext data to be encrypted
 * @param [out] ciphertext Ciphertext data buffer to be filled
 * @param [in] datalen Length of the plaintext and ciphertext data buffers
 *		       \note the encryption operation is symmetric and
 *			     generates as much output as input.
 * @param [in] aad Additional authenticate data to be processed - this is data
 *		   which is not encrypted, but considered as part of the
 *		   authentication.
 * @param [in] aadlen Length of the AAD buffer
 * @param [out] tag Buffer to be filled with tag
 * @param [in] taglen Length of tag buffer. The full tag size hc_get_tagsize().
 *		      If the buffer is smaller, a truncated tag value is
 *		      returned.
 *
 * @return amount of processed bytes on success, < 0 on error
 */
int lc_aead_encrypt(struct lc_aead_ctx *ctx, const uint8_t *plaintext,
		    uint8_t *ciphertext, size_t datalen, const uint8_t *aad,
		    size_t aadlen, uint8_t *tag, size_t taglen);

/**
 * @ingroup AEAD
 * @brief Initialize AEAD encryption
 *
 * This call allows multiple successive _update calls to process data.
 *
 * @param [in] ctx AEAD context handle with key set / IV
 * @param [in] aad Additional authenticate data to be processed - this is data
 *		   which is not encrypted, but considered as part of the
 *		   authentication.
 * @param [in] aadlen Length of the AAD buffer
 *
 * @return amount of processed bytes on success, < 0 on error
 */
int lc_aead_enc_init(struct lc_aead_ctx *ctx, const uint8_t *aad,
		     size_t aadlen);

/**
 * @ingroup AEAD
 * @brief AEAD-encrypt data - send partial data
 *
 * \note This operation can be invoked multiple times and must be completed
 * with a call to lc_aead_enc_final.
 *
 * @param [in] ctx AEAD context handle with key set / IV
 * @param [in] plaintext Plaintext data to be encrypted
 * @param [out] ciphertext Ciphertext data buffer to be filled
 * @param [in] datalen Length of the plaintext and ciphertext data buffers
 *		       \note the encryption operation is symmetric and
 *			     generates as much output as input.
 *
 * @return amount of processed bytes on success, < 0 on error
 */
int lc_aead_enc_update(struct lc_aead_ctx *ctx, const uint8_t *plaintext,
		       uint8_t *ciphertext, size_t datalen);

/**
 * @ingroup AEAD
 * @brief Complete AEAD encryption - Obtain the authentication tag from the
 *	  encryption operation
 *
 * @param [in] ctx AEAD context handle with key set / IV
 * @param [out] tag Buffer to be filled with tag
 * @param [in] taglen Length of tag buffer. The full tag size hc_get_tagsize().
 *		      If the buffer is smaller, a truncated tag value is
 *		      returned.
 *
 * @return amount of processed bytes on success, < 0 on error
 */
int lc_aead_enc_final(struct lc_aead_ctx *ctx, uint8_t *tag, size_t taglen);

/**
 * @ingroup AEAD
 * @brief AEAD-decrypt data in one call
 *
 * @param [in] ctx AEAD context handle with key set / IV
 * @param [in] ciphertext Ciphertext data to be decrypted
 * @param [out] plaintext Plaintext data buffer to be filled
 * @param [in] datalen Length of the plaintext and ciphertext data buffers
 *		       \note the encryption operation is symmetric and
 *			     generates as much output as input.
 * @param [in] aad Additional authenticate data to be processed - this is data
 *		   which is not decrypted, but considered as part of the
 *		   authentication.
 * @param [in] aadlen Length of the AAD buffer
 * @param [in] tag Authentication tag generated by encryption operation
 * @param [in] taglen Length of tag buffer.
 *
 *
 * @return 0 on successful authentication, < 0 on error
 *	   (-EBADMSG means authentication error)
 */
int lc_aead_decrypt(struct lc_aead_ctx *ctx, const uint8_t *ciphertext,
		    uint8_t *plaintext, size_t datalen, const uint8_t *aad,
		    size_t aadlen, const uint8_t *tag, size_t taglen);

/**
 * @ingroup AEAD
 * @brief Initialize AEAD decryption
 *
 * This call allows multiple successive _update calls to process data.
 *
 * @param [in] ctx AEAD context handle with key set / IV
 * @param [in] aad Additional authenticate data to be processed - this is data
 *		   which is not encrypted, but considered as part of the
 *		   authentication.
 * @param [in] aadlen Length of the AAD buffer
 *
 * @return amount of processed bytes on success, < 0 on error
 */
int lc_aead_dec_init(struct lc_aead_ctx *ctx, const uint8_t *aad,
		     size_t aadlen);

/**
 * @ingroup AEAD
 * @brief AEAD-decrypt data - send partial data
 *
 * \note This operation can be invoked multiple times and must be completed
 * with a call to lc_aead_dec_final.
 *
 * @param [in] ctx AEAD context handle with key set / IV
 * @param [in] ciphertext Ciphertext data to be decrypted
 * @param [out] plaintext Plaintext data buffer to be filled
 * @param [in] datalen Length of the plaintext and ciphertext data buffers
 *		       \note the encryption operation is symmetric and
 *			     generates as much output as input.
 *
 * @return amount of processed bytes on success, < 0 on error
 */
int lc_aead_dec_update(struct lc_aead_ctx *ctx, const uint8_t *ciphertext,
		       uint8_t *plaintext, size_t datalen);

/**
 * @ingroup AEAD
 * @brief AEAD-decrypt data - Perform authentication
 *
 * @param [in] ctx AEAD context handle with key set / IV
 * @param [in] tag Authentication tag generated by encryption operation
 * @param [in] taglen Length of tag buffer.
 *
 *
 * @return 0 on successful authentication, < 0 on error
 *	   (-EBADMSG means authentication error)
 */
int lc_aead_dec_final(struct lc_aead_ctx *ctx, const uint8_t *tag,
		      size_t taglen);

/**
 * @ingroup AEAD
 * @brief Obtain algorithm status
 *
 * @param [in] aead AEAD algorithm instance
 *
 * @return algorithm status
 */
enum lc_alg_status_val lc_aead_alg_status(const struct lc_aead *aead);

/**
 * @ingroup AEAD
 * @brief Obtain algorithm status
 *
 * @param [in] ctx AEAD context handle
 *
 * @return algorithm status
 */
enum lc_alg_status_val lc_aead_ctx_alg_status(const struct lc_aead_ctx *ctx);

#ifdef __cplusplus
}
#endif

#endif /* LC_AEAD_H */
