/*
 * Copyright 2017-2024 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
#ifndef FSL_HASHCRYPT_H_
#define FSL_HASHCRYPT_H_

#include "fsl_common.h"

/*! @brief HASHCRYPT status return codes. */
enum _hashcrypt_status
{
    kStatus_HASHCRYPT_Again =
        MAKE_STATUS(kStatusGroup_HASHCRYPT, 0), /*!< Non-blocking function shall be called again. */
};

/*******************************************************************************
 * Definitions
 *******************************************************************************/

/*!
 * @addtogroup hashcrypt_driver
 * @{
 */
/*! @name Driver version */
/*! @{ */
/*! @brief HASHCRYPT driver version. Version 2.2.15.
 *
 * Current version: 2.2.15
 *
 * Change log:
 * - Version 2.0.0
 *   - Initial version
 * - Version 2.0.1
 *   - Support loading AES key from unaligned address
 * - Version 2.0.2
 *   - Support loading AES key from unaligned address for different compiler and core variants
 * - Version 2.0.3
 *   - Remove SHA512 and AES ICB algorithm definitions
 * - Version 2.0.4
 *   - Add SHA context switch support
 * - Version 2.1.0
 *   - Update the register name and macro to align with new header.
 * - Version 2.1.1
 *   - Fix MISRA C-2012.
 * - Version 2.1.2
 *   - Support loading AES input data from unaligned address.
 * - Version 2.1.3
 *   - Fix MISRA C-2012.
 * - Version 2.1.4
 *   - Fix context switch cannot work when switching from AES.
 * - Version 2.1.5
 *   - Add data synchronization barrier inside hashcrypt_sha_ldm_stm_16_words()
 *     to prevent possible optimization issue.
 * - Version 2.2.0
 *   - Add AES-OFB and AES-CFB mixed IP/SW modes.
 * - Version 2.2.1
 *   - Add data synchronization barrier inside hashcrypt_sha_ldm_stm_16_words()
 *     prevent compiler from reordering memory write when -O2 or higher is used.
 * - Version 2.2.2
 *   - Add data synchronization barrier inside hashcrypt_sha_ldm_stm_16_words()
 *     to fix optimization issue
 * - Version 2.2.3
 *   - Added check for size in hashcrypt_aes_one_block to prevent overflowing COUNT field in MEMCTRL register, if its
 * bigger than COUNT field do a multiple runs.
 * - Version 2.2.4
 *   - In all HASHCRYPT_AES_xx functions have been added setting CTRL_MODE bitfield to 0 after processing data, which
 * decreases power consumption.
 * - Version 2.2.5
 *   - Add data synchronization barrier and instruction  synchronization barrier inside
 * hashcrypt_sha_process_message_data() to fix optimization issue
 * - Version 2.2.6
 *   - Add data synchronization barrier inside HASHCRYPT_SHA_Update() and hashcrypt_get_data() function to fix
 * optimization issue on MDK and ARMGCC release targets
 * - Version 2.2.7
 *   - Add data synchronization barrier inside HASHCRYPT_SHA_Update() to fix optimization issue on MCUX IDE release
 * target
 * - Version 2.2.8
 *   - Unify hashcrypt hashing behavior between aligned and unaligned input data
 * - Version 2.2.9
 *   - Add handling of set ERROR bit in the STATUS register
 * - Version 2.2.10
 *   - Fix missing error statement in hashcrypt_save_running_hash()
 * - Version 2.2.11
 *   - Fix incorrect SHA-256 calculation for long messages with reload
 * - Version 2.2.12
 *   - Fix hardfault issue on the Keil compiler due to unaligned memcpy() input on some optimization levels
 * - Version 2.2.13
 *   - Added function hashcrypt_seed_prng() which loading random number into PRNG_SEED register before AES operation for
 * SCA protection
 * - Version 2.2.14
 *   - Modify function hashcrypt_get_data() to prevent issue with unaligned access
 * - Version 2.2.15
 *    - Add wait on DIGEST BIT inside hashcrypt_sha_one_block() to fix issues with some optimization flags
 */
#define FSL_HASHCRYPT_DRIVER_VERSION (MAKE_VERSION(2, 2, 15))
/*! @} */

/*! @brief Algorithm definitions correspond with the values for Mode field in Control register !*/
#define HASHCRYPT_MODE_SHA1   0x1
#define HASHCRYPT_MODE_SHA256 0x2
#define HASHCRYPT_MODE_AES    0x4

/*! @brief Algorithm used for Hashcrypt operation */
typedef enum _hashcrypt_algo_t
{
    kHASHCRYPT_Sha1   = HASHCRYPT_MODE_SHA1,   /*!< SHA_1 */
    kHASHCRYPT_Sha256 = HASHCRYPT_MODE_SHA256, /*!< SHA_256 */
    kHASHCRYPT_Aes    = HASHCRYPT_MODE_AES,    /*!< AES */
} hashcrypt_algo_t;

/*! @} */

/*******************************************************************************
 * AES Definitions
 *******************************************************************************/

/*!
 * @addtogroup hashcrypt_driver_aes
 * @{
 */

/*! AES block size in bytes */
#define HASHCRYPT_AES_BLOCK_SIZE 16U
#define AES_ENCRYPT              0
#define AES_DECRYPT              1

/*! @brief AES mode */
typedef enum _hashcrypt_aes_mode_t
{
    kHASHCRYPT_AesEcb = 0U, /*!< AES ECB mode */
    kHASHCRYPT_AesCbc = 1U, /*!< AES CBC mode */
    kHASHCRYPT_AesCtr = 2U, /*!< AES CTR mode */
} hashcrypt_aes_mode_t;

/*! @brief Size of AES key */
typedef enum _hashcrypt_aes_keysize_t
{
    kHASHCRYPT_Aes128     = 0U, /*!< AES 128 bit key */
    kHASHCRYPT_Aes192     = 1U, /*!< AES 192 bit key */
    kHASHCRYPT_Aes256     = 2U, /*!< AES 256 bit key */
    kHASHCRYPT_InvalidKey = 3U, /*!< AES invalid key */
} hashcrypt_aes_keysize_t;

/*! @brief HASHCRYPT key source selection.
 *
 */
typedef enum _hashcrypt_key
{
    kHASHCRYPT_UserKey   = 0xc3c3U, /*!< HASHCRYPT user key */
    kHASHCRYPT_SecretKey = 0x3c3cU, /*!< HASHCRYPT secret key (dedicated hw bus from PUF) */
} hashcrypt_key_t;

/*! @brief Specify HASHCRYPT's key resource. */
struct _hashcrypt_handle
{
    uint32_t keyWord[8];     /*!< Copy of user key (set by HASHCRYPT_AES_SetKey(). */
    hashcrypt_aes_keysize_t keySize;
    hashcrypt_key_t keyType; /*!< For operations with key (such as AES encryption/decryption), specify key type. */
} __attribute__((aligned));

typedef struct _hashcrypt_handle hashcrypt_handle_t;

/*!
 *@}
 */ /* end of hashcrypt_driver_aes */

/*******************************************************************************
 * HASH Definitions
 ******************************************************************************/
/*!
 * @addtogroup hashcrypt_driver_hash
 * @{
 */

/*! @brief HASHCRYPT HASH Context size. */
#if defined(FSL_FEATURE_HASHCRYPT_HAS_RELOAD_FEATURE) && (FSL_FEATURE_HASHCRYPT_HAS_RELOAD_FEATURE > 0)
#define HASHCRYPT_HASH_CTX_SIZE 30
#else
#define HASHCRYPT_HASH_CTX_SIZE 22
#endif

/*! @brief Storage type used to save hash context. */
typedef struct _hashcrypt_hash_ctx_t
{
    uint32_t x[HASHCRYPT_HASH_CTX_SIZE]; /*!< storage */
} hashcrypt_hash_ctx_t;

/*! @brief HASHCRYPT background hash callback function. */
typedef void (*hashcrypt_callback_t)(HASHCRYPT_Type *base, hashcrypt_hash_ctx_t *ctx, status_t status, void *userData);

/*!
 *@}
 */ /* end of hashcrypt_driver_hash */

/*******************************************************************************
 * API
 ******************************************************************************/
#if defined(__cplusplus)
extern "C" {
#endif

/*!
 * @addtogroup hashcrypt_driver
 * @{
 */

/*!
 * @brief Enables clock and disables reset for HASHCRYPT peripheral.
 *
 * Enable clock and disable reset for HASHCRYPT.
 *
 * @param base HASHCRYPT base address
 */
void HASHCRYPT_Init(HASHCRYPT_Type *base);

/*!
 * @brief Disables clock for HASHCRYPT peripheral.
 *
 * Disable clock and enable reset.
 *
 * @param base HASHCRYPT base address
 */
void HASHCRYPT_Deinit(HASHCRYPT_Type *base);

/*!
 *@}
 */ /* end of hashcrypt_driver */

/*******************************************************************************
 * AES API
 ******************************************************************************/

/*!
 * @addtogroup hashcrypt_driver_aes
 * @{
 */

/*!
 * @brief Set AES key to hashcrypt_handle_t struct and optionally to HASHCRYPT.
 *
 * Sets the AES key for encryption/decryption with the hashcrypt_handle_t structure.
 * The hashcrypt_handle_t input argument specifies key source.
 *
 * @param   base HASHCRYPT peripheral base address.
 * @param   handle Handle used for the request.
 * @param   key 0-mod-4 aligned pointer to AES key.
 * @param   keySize AES key size in bytes. Shall equal 16, 24 or 32.
 * @return  status from set key operation
 */
status_t HASHCRYPT_AES_SetKey(HASHCRYPT_Type *base, hashcrypt_handle_t *handle, const uint8_t *key, size_t keySize);

/*!
 * @brief Encrypts AES on one or multiple 128-bit block(s).
 *
 * Encrypts AES.
 * The source plaintext and destination ciphertext can overlap in system memory.
 *
 * @param base HASHCRYPT peripheral base address
 * @param handle Handle used for this request.
 * @param plaintext Input plain text to encrypt
 * @param[out] ciphertext Output cipher text
 * @param size Size of input and output data in bytes. Must be multiple of 16 bytes.
 * @return Status from encrypt operation
 */
status_t HASHCRYPT_AES_EncryptEcb(
    HASHCRYPT_Type *base, hashcrypt_handle_t *handle, const uint8_t *plaintext, uint8_t *ciphertext, size_t size);

/*!
 * @brief Decrypts AES on one or multiple 128-bit block(s).
 *
 * Decrypts AES.
 * The source ciphertext and destination plaintext can overlap in system memory.
 *
 * @param base HASHCRYPT peripheral base address
 * @param handle Handle used for this request.
 * @param ciphertext Input plain text to encrypt
 * @param[out] plaintext Output cipher text
 * @param size Size of input and output data in bytes. Must be multiple of 16 bytes.
 * @return Status from decrypt operation
 */
status_t HASHCRYPT_AES_DecryptEcb(
    HASHCRYPT_Type *base, hashcrypt_handle_t *handle, const uint8_t *ciphertext, uint8_t *plaintext, size_t size);

/*!
 * @brief Encrypts AES using CBC block mode.
 *
 * @param base HASHCRYPT peripheral base address
 * @param handle Handle used for this request.
 * @param plaintext Input plain text to encrypt
 * @param[out] ciphertext Output cipher text
 * @param size Size of input and output data in bytes. Must be multiple of 16 bytes.
 * @param iv Input initial vector to combine with the first input block.
 * @return Status from encrypt operation
 */
status_t HASHCRYPT_AES_EncryptCbc(HASHCRYPT_Type *base,
                                  hashcrypt_handle_t *handle,
                                  const uint8_t *plaintext,
                                  uint8_t *ciphertext,
                                  size_t size,
                                  const uint8_t iv[16]);

/*!
 * @brief Decrypts AES using CBC block mode.
 *
 * @param base HASHCRYPT peripheral base address
 * @param handle Handle used for this request.
 * @param ciphertext Input cipher text to decrypt
 * @param[out] plaintext Output plain text
 * @param size Size of input and output data in bytes. Must be multiple of 16 bytes.
 * @param iv Input initial vector to combine with the first input block.
 * @return Status from decrypt operation
 */
status_t HASHCRYPT_AES_DecryptCbc(HASHCRYPT_Type *base,
                                  hashcrypt_handle_t *handle,
                                  const uint8_t *ciphertext,
                                  uint8_t *plaintext,
                                  size_t size,
                                  const uint8_t iv[16]);

/*!
 * @brief Encrypts or decrypts AES using CTR block mode.
 *
 * Encrypts or decrypts AES using CTR block mode.
 * AES CTR mode uses only forward AES cipher and same algorithm for encryption and decryption.
 * The only difference between encryption and decryption is that, for encryption, the input argument
 * is plain text and the output argument is cipher text. For decryption, the input argument is cipher text
 * and the output argument is plain text.
 *
 * @param base HASHCRYPT peripheral base address
 * @param handle Handle used for this request.
 * @param input Input data for CTR block mode
 * @param[out] output Output data for CTR block mode
 * @param size Size of input and output data in bytes
 * @param[in,out] counter Input counter (updates on return)
 * @param[out] counterlast Output cipher of last counter, for chained CTR calls (statefull encryption). NULL can be
 * passed if chained calls are
 * not used.
 * @param[out] szLeft Output number of bytes in left unused in counterlast block. NULL can be passed if chained calls
 * are not used.
 * @return Status from encrypt operation
 */
status_t HASHCRYPT_AES_CryptCtr(HASHCRYPT_Type *base,
                                hashcrypt_handle_t *handle,
                                const uint8_t *input,
                                uint8_t *output,
                                size_t size,
                                uint8_t counter[HASHCRYPT_AES_BLOCK_SIZE],
                                uint8_t counterlast[HASHCRYPT_AES_BLOCK_SIZE],
                                size_t *szLeft);

/*!
 * @brief Encrypts or decrypts AES using OFB block mode.
 *
 * Encrypts or decrypts AES using OFB block mode.
 * AES OFB mode uses only forward AES cipher and same algorithm for encryption and decryption.
 * The only difference between encryption and decryption is that, for encryption, the input argument
 * is plain text and the output argument is cipher text. For decryption, the input argument is cipher text
 * and the output argument is plain text.
 *
 * @param base HASHCRYPT peripheral base address
 * @param handle Handle used for this request.
 * @param input Input data for OFB block mode
 * @param[out] output Output data for OFB block mode
 * @param size Size of input and output data in bytes
 * @param iv Input initial vector to combine with the first input block.
 * @return Status from encrypt operation
 */

status_t HASHCRYPT_AES_CryptOfb(HASHCRYPT_Type *base,
                                hashcrypt_handle_t *handle,
                                const uint8_t *input,
                                uint8_t *output,
                                size_t size,
                                const uint8_t iv[HASHCRYPT_AES_BLOCK_SIZE]);

/*!
 * @brief Encrypts AES using CFB block mode.
 *
 * @param base HASHCRYPT peripheral base address
 * @param handle Handle used for this request.
 * @param plaintext Input plain text to encrypt
 * @param[out] ciphertext Output cipher text
 * @param size Size of input and output data in bytes. Must be multiple of 16 bytes.
 * @param iv Input initial vector to combine with the first input block.
 * @return Status from encrypt operation
 */

status_t HASHCRYPT_AES_EncryptCfb(HASHCRYPT_Type *base,
                                  hashcrypt_handle_t *handle,
                                  const uint8_t *plaintext,
                                  uint8_t *ciphertext,
                                  size_t size,
                                  const uint8_t iv[16]);

/*!
 * @brief Decrypts AES using CFB block mode.
 *
 * @param base HASHCRYPT peripheral base address
 * @param handle Handle used for this request.
 * @param ciphertext Input cipher text to decrypt
 * @param[out] plaintext Output plaintext text
 * @param size Size of input and output data in bytes. Must be multiple of 16 bytes.
 * @param iv Input initial vector to combine with the first input block.
 * @return Status from encrypt operation
 */

status_t HASHCRYPT_AES_DecryptCfb(HASHCRYPT_Type *base,
                                  hashcrypt_handle_t *handle,
                                  const uint8_t *ciphertext,
                                  uint8_t *plaintext,
                                  size_t size,
                                  const uint8_t iv[16]);
/*!
 *@}
 */ /* end of hashcrypt_driver_aes */

/*******************************************************************************
 * HASH API
 ******************************************************************************/

/*!
 * @addtogroup hashcrypt_driver_hash
 * @{
 */

/*!
 * @brief Create HASH on given data
 *
 * Perform the full SHA in one function call. The function is blocking.
 *
 * @param base HASHCRYPT peripheral base address
 * @param algo Underlaying algorithm to use for hash computation.
 * @param input Input data
 * @param inputSize Size of input data in bytes
 * @param[out] output Output hash data
 * @param[out] outputSize Output parameter storing the size of the output hash in bytes
 * @return Status of the one call hash operation.
 */
status_t HASHCRYPT_SHA(HASHCRYPT_Type *base,
                       hashcrypt_algo_t algo,
                       const uint8_t *input,
                       size_t inputSize,
                       uint8_t *output,
                       size_t *outputSize);

/*!
 * @brief Initialize HASH context
 *
 * This function initializes the HASH.
 *
 * @param base HASHCRYPT peripheral base address
 * @param[out] ctx Output hash context
 * @param algo Underlaying algorithm to use for hash computation.
 * @return Status of initialization
 */
status_t HASHCRYPT_SHA_Init(HASHCRYPT_Type *base, hashcrypt_hash_ctx_t *ctx, hashcrypt_algo_t algo);

/*!
 * @brief Add data to current HASH
 *
 * Add data to current HASH. This can be called repeatedly with an arbitrary amount of data to be
 * hashed. The functions blocks. If it returns kStatus_Success, the running hash
 * has been updated (HASHCRYPT has processed the input data), so the memory at \p input pointer
 * can be released back to system. The HASHCRYPT context buffer is updated with the running hash
 * and with all necessary information to support possible context switch.
 *
 * @param base HASHCRYPT peripheral base address
 * @param[in,out] ctx HASH context
 * @param input Input data
 * @param inputSize Size of input data in bytes
 * @return Status of the hash update operation
 */
status_t HASHCRYPT_SHA_Update(HASHCRYPT_Type *base, hashcrypt_hash_ctx_t *ctx, const uint8_t *input, size_t inputSize);

/*!
 * @brief Finalize hashing
 *
 * Outputs the final hash (computed by HASHCRYPT_HASH_Update()) and erases the context.
 *
 * @param base HASHCRYPT peripheral base address
 * @param[in,out] ctx Input hash context
 * @param[out] output Output hash data
 * @param[in,out] outputSize Optional parameter (can be passed as NULL). On function entry, it specifies the size of
 * output[] buffer. On function return, it stores the number of updated output bytes.
 * @return Status of the hash finish operation
 */
status_t HASHCRYPT_SHA_Finish(HASHCRYPT_Type *base, hashcrypt_hash_ctx_t *ctx, uint8_t *output, size_t *outputSize);

/*!
 *@}
 */ /* end of hashcrypt_driver_hash */

/*!
 * @addtogroup hashcrypt_background_driver_hash
 * @{
 */

/*!
 * @brief Initializes the HASHCRYPT handle for background hashing.
 *
 * This function initializes the hash context for background hashing
 * (Non-blocking) APIs. This is less typical interface to hash function, but can be used
 * for parallel processing, when main CPU has something else to do.
 * Example is digital signature RSASSA-PKCS1-V1_5-VERIFY((n,e),M,S) algorithm, where
 * background hashing of M can be started, then CPU can compute S^e mod n
 * (in parallel with background hashing) and once the digest becomes available,
 * CPU can proceed to comparison of EM with EM'.
 *
 * @param base HASHCRYPT peripheral base address.
 * @param[out] ctx Hash context.
 * @param callback Callback function.
 * @param userData User data (to be passed as an argument to callback function, once callback is invoked from isr).
 */
void HASHCRYPT_SHA_SetCallback(HASHCRYPT_Type *base,
                               hashcrypt_hash_ctx_t *ctx,
                               hashcrypt_callback_t callback,
                               void *userData);

/*!
 * @brief Create running hash on given data.
 *
 * Configures the HASHCRYPT to compute new running hash as AHB master
 * and returns immediately. HASHCRYPT AHB Master mode supports only aligned \p input
 * address and can be called only once per continuous block of data. Every call to this function
 * must be preceded with HASHCRYPT_SHA_Init() and finished with HASHCRYPT_SHA_Finish().
 * Once callback function is invoked by HASHCRYPT isr, it should set a flag
 * for the main application to finalize the hashing (padding) and to read out the final digest
 * by calling HASHCRYPT_SHA_Finish().
 *
 * @param base HASHCRYPT peripheral base address
 * @param ctx Specifies callback. Last incomplete 512-bit block of the input is copied into clear buffer for padding.
 * @param input 32-bit word aligned pointer to Input data.
 * @param inputSize Size of input data in bytes (must be word aligned)
 * @return Status of the hash update operation.
 */
status_t HASHCRYPT_SHA_UpdateNonBlocking(HASHCRYPT_Type *base,
                                         hashcrypt_hash_ctx_t *ctx,
                                         const uint8_t *input,
                                         size_t inputSize);
/*!
 *@}
 */ /* end of hashcrypt_background_driver_hash */

#if defined(__cplusplus)
}
#endif

#endif /* FSL_HASHCRYPT_H_ */
