/**
 * \file aes.h
 * \brief Header defining the API for OQS AES
 *
 * SPDX-License-Identifier: MIT
 */

#ifndef OQS_AES_H
#define OQS_AES_H

#include <stdint.h>
#include <stdlib.h>

#define AES256_KEYBYTES 32
#define AESCTR_NONCEBYTES 12
#define AES_BLOCKBYTES 16

typedef void *aes256ctx;

#define aes256_ecb_keyexp(r, key) OQS_AES256_ECB_load_schedule((key), (r), 1);
#define aes256_ecb(out, in, nblocks, ctx) OQS_AES256_ECB_enc_sch((in), (nblocks) * AES_BLOCKBYTES, *(ctx), (out));
#define aes256_ctr_keyexp(r, key) OQS_AES256_CTR_load_schedule((key), (r));
#define aes256_ctr(out, outlen, iv, ctx) OQS_AES256_CTR_sch((iv), AESCTR_NONCEBYTES, *(ctx), (out), (outlen))
#define aes256_ctx_release(ctx) OQS_AES256_free_schedule(*(ctx));


/** copied from common.h **/

#define OQS_EXIT_IF_NULLPTR(x)  \
    do {                        \
        if ( (x) == (void*)0 )  \
            exit(EXIT_FAILURE); \
    } while (0)

/** copied from common.c **/

/**
 * Zeros out `len` bytes of memory starting at `ptr`.
 *
 * Designed to be protected against optimizing compilers which try to remove
 * "unnecessary" operations.  Should be used for all buffers containing secret
 * data.
 *
 * @param[in] ptr The start of the memory to zero out.
 * @param[in] len The number of bytes to zero out.
 */
void OQS_MEM_cleanse(void *ptr, size_t len);

/**
 * Zeros out `len` bytes of memory starting at `ptr`, then frees `ptr`.
 *
 * Can be called with `ptr = NULL`, in which case no operation is performed.
 *
 * Designed to be protected against optimizing compilers which try to remove
 * "unnecessary" operations.  Should be used for all buffers containing secret
 * data.
 *
 * @param[in] ptr The start of the memory to zero out and free.
 * @param[in] len The number of bytes to zero out.
 */
void OQS_MEM_secure_free(void *ptr, size_t len);



/**
 * Function to fill a key schedule given an initial key for use in ECB mode.
 *
 * @param key            Initial Key.
 * @param schedule       Abstract data structure for a key schedule.
 * @param for_encryption 1 if key schedule is for encryption, 0 if for decryption.
 */
void OQS_AES128_ECB_load_schedule(const uint8_t *key, void **schedule, int for_encryption);

/**
 * Function to free a key schedule.
 *
 * @param schedule       Schedule generated with OQS_AES128_ECB_load_schedule().
 */
void OQS_AES128_free_schedule(void *schedule);

/**
 * Function to encrypt blocks of plaintext using ECB mode.
 * A schedule based on the key is generated and used internally.
 *
 * @param plaintext     Plaintext to be encrypted.
 * @param plaintext_len Length on the plaintext in bytes. Must be a multiple of 16.
 * @param key           Key to be used for encryption.
 * @param ciphertext    Pointer to a block of memory which >= in size to the plaintext block. The result will be written here.
 */
void OQS_AES128_ECB_enc(const uint8_t *plaintext, const size_t plaintext_len, const uint8_t *key, uint8_t *ciphertext);

/**
 * Function to decrypt blocks of plaintext using ECB mode.
 * A schedule based on the key is generated and used internally.
 *
 * @param ciphertext     Ciphertext to be decrypted.
 * @param ciphertext_len Length on the ciphertext in bytes. Must be a multiple of 16.
 * @param key            Key to be used for encryption.
 * @param plaintext      Pointer to a block of memory which >= in size to the ciphertext block. The result will be written here.
 */
void OQS_AES128_ECB_dec(const uint8_t *ciphertext, const size_t ciphertext_len, const uint8_t *key, uint8_t *plaintext);

/**
 * Same as OQS_AES128_ECB_enc() except a schedule generated by
 * OQS_AES128_ECB_load_schedule() is passed rather then a key. This is faster
 * if the same schedule is used for multiple encryptions since it does
 * not have to be regenerated from the key.
 */
void OQS_AES128_ECB_enc_sch(const uint8_t *plaintext, const size_t plaintext_len, const void *schedule, uint8_t *ciphertext);

/**
 * Same as OQS_AES128_ECB_dec() except a schedule generated by
 * OQS_AES128_ECB_load_schedule() is passed rather then a key. This is faster
 * if the same schedule is used for multiple encryptions since it does
 * not have to be regenerated from the key.
 */
void OQS_AES128_ECB_dec_sch(const uint8_t *ciphertext, const size_t ciphertext_len, const void *schedule, uint8_t *plaintext);

/**
 * Function to fill a key schedule given an initial key for use in ECB mode.
 *
 * @param key            Initial Key.
 * @param schedule       Abstract data structure for a key schedule.
 * @param for_encryption 1 if key schedule is for encryption, 0 if for decryption.
 */
void OQS_AES256_ECB_load_schedule(const uint8_t *key, void **schedule, int for_encryption);

/**
 * Function to fill a key schedule given an initial key for use in CTR mode.
 *
 * @param key            Initial Key.
 * @param schedule       Abstract data structure for a key schedule.
 */
void OQS_AES256_CTR_load_schedule(const uint8_t *key, void **schedule);

/**
 * Function to free a key schedule.
 *
 * @param schedule       Schedule generated with OQS_AES256_ECB_load_schedule
 *                       or OQS_AES256_CTR_load_schedule.
 */
void OQS_AES256_free_schedule(void *schedule);

/**
 * Function to encrypt blocks of plaintext using ECB mode.
 * A schedule based on the key is generated and used internally.
 *
 * @param plaintext     Plaintext to be encrypted.
 * @param plaintext_len Length on the plaintext in bytes. Must be a multiple of 16.
 * @param key           Key to be used for encryption.
 * @param ciphertext    Pointer to a block of memory which >= in size to the plaintext block. The result will be written here.
 */
void OQS_AES256_ECB_enc(const uint8_t *plaintext, const size_t plaintext_len, const uint8_t *key, uint8_t *ciphertext);

/**
 * Function to decrypt blocks of plaintext using ECB mode.
 * A schedule based on the key is generated and used internally.
 *
 * @param ciphertext     Ciphertext to be decrypted.
 * @param ciphertext_len Length on the ciphertext in bytes. Must be a multiple of 16.
 * @param key            Key to be used for encryption.
 * @param plaintext      Pointer to a block of memory which >= in size to the ciphertext block. The result will be written here.
 */
void OQS_AES256_ECB_dec(const uint8_t *ciphertext, const size_t ciphertext_len, const uint8_t *key, uint8_t *plaintext);

/**
 * Same as OQS_AES256_ECB_enc() except a schedule generated by
 * OQS_AES256_ECB_load_schedule() is passed rather then a key. This is faster
 * if the same schedule is used for multiple encryptions since it does
 * not have to be regenerated from the key.
 */
void OQS_AES256_ECB_enc_sch(const uint8_t *plaintext, const size_t plaintext_len, const void *schedule, uint8_t *ciphertext);

/**
 * Same as OQS_AES256_ECB_dec() except a schedule generated by
 * OQS_AES256_ECB_load_schedule() is passed rather then a key. This is faster
 * if the same schedule is used for multiple encryptions since it does
 * not have to be regenerated from the key.
 */
void OQS_AES256_ECB_dec_sch(const uint8_t *ciphertext, const size_t ciphertext_len, const void *schedule, uint8_t *plaintext);

/**
 * AES counter mode keystream generator.  A scheduled generated by
 * OQS_AES256_CTR_load_schedule() is passed rather then a key.
 *
 * Handles a 12- or 16-byte IV.  If a 12-byte IV is given, then 4 counter
 * bytes are initialized to all zeros.
 *
 * @param iv       12- or 16-byte initialization vector.
 * @param iv_len   Lengh of IV in bytes.
 * @param schedule Abstract data structure for a key schedule.
 * @param out      Pointer to a block of memory which is big enough to contain out_len bytes; the result will be written here.
 * @param out_len  Length of output bytes to generate.
 */
void OQS_AES256_CTR_sch(const uint8_t *iv, size_t iv_len, const void *schedule, uint8_t *out, size_t out_len);

#if defined(__cplusplus)
} // extern "C"
#endif

#endif // OQS_AES_H
