/*
 * 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_KDF_CTR_H
#define LC_KDF_CTR_H

#include "ext_headers.h"
#include "lc_hash.h"
#include "lc_rng.h"
#include "lc_hmac.h"
#include "lc_status.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @ingroup KDF
 * @brief Key-based Key Derivation in Counter Mode - SP800-108 - initialization
 *
 * @param [in,out] hmac_ctx The caller is expected to provide an allocated HMAC
 *			    cipher handle in. Yet, the caller does not need to
 *			    perform any operations on the handle. The extract
 *			    phase adjusts the HMAC cipher handle so that it is
 *			    ready for the expand phase.
 * @param [in] key Input Keying Material (see RFC5869)
 * @param [in] keylen Length of ikm buffer
 *
 * @return 0 on success, < 0 on error
 */
int lc_kdf_ctr_init(struct lc_hmac_ctx *hmac_ctx, const uint8_t *key,
		    size_t keylen);

/**
 * @ingroup KDF
 * @brief Key-based Key Derivation in Counter Mode - SP800-108 - data generation
 *
 * @param [in] hmac_ctx Cipher handle for the operation. This call expects
 *			the caller to hand in a HMAC cipher handle that has
 *			been initialized with hkdf_extract.
 * @param [in] label Optional context and application specific information. This
 *		     may be NULL.
 * @param [in] labellen Size of label buffer.
 * @param [out] dst Buffer to store the derived bits in
 * @param [in] dlen Size of the destination buffer.
 *
 * @return 0 on success, < 0 on error
 */
int lc_kdf_ctr_generate(struct lc_hmac_ctx *hmac_ctx, const uint8_t *label,
			size_t labellen, uint8_t *dst, size_t dlen);

/**
 * @ingroup KDF
 * @brief One-shot Key-based Key Derivation in Counter Mode - SP800-108
 *
 * @param [in] hash Hash implementation to use for the KDF operation - this
 *		    hash implementation is used for the HMAC calls.
 * @param [in] key Key from which the new key is to be derived from
 * @param [in] keylen Length of the key buffer.
 * @param [in] label Optional label string that is used to diversify the key
 * @param [in] labellen Length of the label buffer
 * @param [out] dst Buffer that is filled with the derived key. This buffer
 *		    with the size of keylen must be allocated by the caller.
 * @param [in] dlen Length of the key that shall be derived.
 *
 * @return 0 on success, < 0 on error
 */
int lc_kdf_ctr(const struct lc_hash *hash, const uint8_t *key, size_t keylen,
	       const uint8_t *label, size_t labellen, uint8_t *dst,
	       size_t dlen);

/**
 * @ingroup KDF
 * @brief Obtain algorithm status
 *
 * @param [in] hash Hash algorithm instance
 *
 * @return algorithm status
 */
enum lc_alg_status_val lc_kdf_ctr_alg_status(const struct lc_hash *hash);

/***************************** Counter KDF as RNG *****************************/

/// \cond DO_NOT_DOCUMENT
struct lc_kdf_ctr_ctx {
	struct lc_hmac_ctx hmac_ctx;
	struct lc_hmac_key key;
	uint32_t counter;
	uint8_t rng_initialized : 1;
};

#define LC_CTR_KDF_STATE_SIZE (LC_HMAC_CTX_SIZE)
#define LC_CTR_KDF_CTX_SIZE                                                    \
	(sizeof(struct lc_kdf_ctr_ctx) + LC_CTR_KDF_STATE_SIZE)

#define _LC_CTR_KDF_SET_CTX(name, hashname, ctx, offset)                       \
	LC_HMAC_SET_CTX((&(name)->hmac_ctx), hashname)

#define LC_CTR_KDF_SET_CTX(name, hashname)                                     \
	_LC_CTR_KDF_SET_CTX(name, hashname, name, sizeof(struct lc_kdf_ctr_ctx))

/* CTR_KDF DRNG implementation */
extern const struct lc_rng *lc_kdf_ctr_rng;

#define LC_CTR_KDF_DRNG_CTX_SIZE                                               \
	(sizeof(struct lc_rng_ctx) +                                           \
	 LC_HASH_STATE_SIZE_ALIGN(LC_CTR_KDF_CTX_SIZE))

#define LC_CTR_KDF_DRNG_SET_CTX(name, hashname)                                \
	LC_CTR_KDF_SET_CTX(name, hashname)

#define LC_CTR_KDF_RNG_CTX(name, hashname)                                     \
	LC_RNG_CTX(name, lc_kdf_ctr_rng, LC_HASH_COMMON_ALIGNMENT);            \
	LC_CTR_KDF_DRNG_SET_CTX(((struct lc_kdf_ctr_ctx *)(name->rng_state)),  \
				hashname);                                     \
	lc_rng_zero(name)
/// \endcond

/**
 * @ingroup KDFasRNG
 * @brief Allocate stack memory for the Counter KDF DRNG context
 *
 * @param [in] name Name of the stack variable
 * @param [in] hashname Reference to lc_hash implementation used for CTR KDF
 *
 * \warning You MUST seed the DRNG!
 */
#define LC_CTR_KDF_DRNG_CTX_ON_STACK(name, hashname)                           \
	_Pragma("GCC diagnostic push") _Pragma(                                \
		"GCC diagnostic ignored \"-Wdeclaration-after-statement\"")    \
		_Pragma("GCC diagnostic ignored \"-Wcast-align\"")             \
			LC_ALIGNED_BUFFER(name##_ctx_buf,                      \
					  LC_CTR_KDF_DRNG_CTX_SIZE,            \
					  LC_HASH_COMMON_ALIGNMENT);           \
	struct lc_rng_ctx *name = (struct lc_rng_ctx *)name##_ctx_buf;         \
	LC_CTR_KDF_RNG_CTX(name, hashname);                                    \
	_Pragma("GCC diagnostic pop")

/**
 * @ingroup KDFasRNG
 * @brief Allocation of a Counter KDF DRNG context
 *
 * @param [out] state Counter KDF DRNG context allocated by the function
 * @param [in] hash Reference to lc_hash implementation used for CTR KDF
 *
 * The cipher handle including its memory is allocated with this function.
 *
 * The memory is pinned so that the DRNG state cannot be swapped out to disk.
 *
 * \warning You MUST seed the DRNG!
 *
 * @return 0 upon success; < 0 on error
 */
int lc_kdf_ctr_rng_alloc(struct lc_rng_ctx **state, const struct lc_hash *hash);

#ifdef __cplusplus
}
#endif

#endif /* LC_KDF_CTR_H */
