/*
 *  Copyright (c) 2021, The OpenThread Authors.
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *  3. Neither the name of the copyright holder nor the
 *     names of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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 ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * @file
 * @brief
 *   This file includes the platform abstraction for Crypto operations.
 */

#ifndef OPENTHREAD_PLATFORM_CRYPTO_H_
#define OPENTHREAD_PLATFORM_CRYPTO_H_

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

#include <openthread/error.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @addtogroup plat-crypto
 *
 * @brief
 *   This module includes the platform abstraction for Crypto.
 *
 * @{
 *
 */

/**
 * This enumeration defines the key types.
 *
 */
typedef enum
{
    OT_CRYPTO_KEY_TYPE_RAW,  ///< Key Type: Raw Data.
    OT_CRYPTO_KEY_TYPE_AES,  ///< Key Type: AES.
    OT_CRYPTO_KEY_TYPE_HMAC, ///< Key Type: HMAC.
} otCryptoKeyType;

/**
 * This enumeration defines the key algorithms.
 *
 */
typedef enum
{
    OT_CRYPTO_KEY_ALG_VENDOR,       ///< Key Algorithm: Vendor Defined.
    OT_CRYPTO_KEY_ALG_AES_ECB,      ///< Key Algorithm: AES ECB.
    OT_CRYPTO_KEY_ALG_HMAC_SHA_256, ///< Key Algorithm: HMAC SHA-256.
} otCryptoKeyAlgorithm;

/**
 * This enumeration defines the key usage flags.
 *
 */
enum
{
    OT_CRYPTO_KEY_USAGE_NONE      = 0,      ///< Key Usage: Key Usage is empty.
    OT_CRYPTO_KEY_USAGE_EXPORT    = 1 << 0, ///< Key Usage: Key can be exported.
    OT_CRYPTO_KEY_USAGE_ENCRYPT   = 1 << 1, ///< Key Usage: Encryption (vendor defined).
    OT_CRYPTO_KEY_USAGE_DECRYPT   = 1 << 2, ///< Key Usage: AES ECB.
    OT_CRYPTO_KEY_USAGE_SIGN_HASH = 1 << 3, ///< Key Usage: HMAC SHA-256.
};

/**
 * This enumeration defines the key storage types.
 *
 */
typedef enum
{
    OT_CRYPTO_KEY_STORAGE_VOLATILE,   ///< Key Persistence: Key is volatile.
    OT_CRYPTO_KEY_STORAGE_PERSISTENT, ///< Key Persistence: Key is persistent.
} otCryptoKeyStorage;

/**
 * This datatype represents the key reference.
 *
 */
typedef uint32_t otCryptoKeyRef;

/**
 * @struct otCryptoKey
 *
 * This structure represents the Key Material required for Crypto operations.
 *
 */
typedef struct otCryptoKey
{
    const uint8_t *mKey;       ///< Pointer to the buffer containing key. NULL indicates to use `mKeyRef`.
    uint16_t       mKeyLength; ///< The key length in bytes (applicable when `mKey` is not NULL).
    uint32_t       mKeyRef;    ///< The PSA key ref (requires `mKey` to be NULL).
} otCryptoKey;

/**
 * @struct otCryptoContext
 *
 * This structure stores the context object for platform APIs.
 *
 */
typedef struct otCryptoContext
{
    void *   mContext;     ///< Pointer to the context.
    uint16_t mContextSize; ///< The length of the context in bytes.
} otCryptoContext;

/**
 * Initialize the Crypto module.
 *
 */
void otPlatCryptoInit(void);

/**
 * Import a key into PSA ITS.
 *
 * @param[in,out] aKeyRef           Pointer to the key ref to be used for crypto operations.
 * @param[in]     aKeyType          Key Type encoding for the key.
 * @param[in]     aKeyAlgorithm     Key algorithm encoding for the key.
 * @param[in]     aKeyUsage         Key Usage encoding for the key (combinations of `OT_CRYPTO_KEY_USAGE_*`).
 * @param[in]     aKeyPersistence   Key Persistence for this key
 * @param[in]     aKey              Actual key to be imported.
 * @param[in]     aKeyLen           Length of the key to be imported.
 *
 * @retval OT_ERROR_NONE          Successfully imported the key.
 * @retval OT_ERROR_FAILED        Failed to import the key.
 * @retval OT_ERROR_INVALID_ARGS  @p aKey was set to NULL.
 *
 * @note If OT_CRYPTO_KEY_STORAGE_PERSISTENT is passed for aKeyPersistence then @p aKeyRef is input and platform
 *       should use the given aKeyRef and MUST not change it.
 *
 *       If OT_CRYPTO_KEY_STORAGE_VOLATILE is passed for aKeyPersistence then @p aKeyRef is output, the initial
 *       value does not matter and platform API MUST update it to return the new key ref.
 *
 *       This API is only used by OT core when `OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE` is enabled.
 *
 */
otError otPlatCryptoImportKey(otCryptoKeyRef *     aKeyRef,
                              otCryptoKeyType      aKeyType,
                              otCryptoKeyAlgorithm aKeyAlgorithm,
                              int                  aKeyUsage,
                              otCryptoKeyStorage   aKeyPersistence,
                              const uint8_t *      aKey,
                              size_t               aKeyLen);

/**
 * Export a key stored in PSA ITS.
 *
 * @param[in]   aKeyRef           The key ref to be used for crypto operations.
 * @param[out]  aBuffer           Pointer to the buffer where key needs to be exported.
 * @param[in]   aBufferLen        Length of the buffer passed to store the exported key.
 * @param[out]  aKeyLen           Pointer to return the length of the exported key.
 *
 * @retval OT_ERROR_NONE          Successfully exported  @p aKeyRef.
 * @retval OT_ERROR_FAILED        Failed to export @p aKeyRef.
 * @retval OT_ERROR_INVALID_ARGS  @p aBuffer was NULL
 *
 * @note This API is only used by OT core when `OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE` is enabled.
 *
 */
otError otPlatCryptoExportKey(otCryptoKeyRef aKeyRef, uint8_t *aBuffer, size_t aBufferLen, size_t *aKeyLen);

/**
 * Destroy a key stored in PSA ITS.
 *
 * @param[in]   aKeyRef          The key ref to be destroyed
 *
 * @retval OT_ERROR_NONE          Successfully destroyed the key.
 * @retval OT_ERROR_FAILED        Failed to destroy the key.
 *
 * @note This API is only used by OT core when `OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE` is enabled.
 *
 */
otError otPlatCryptoDestroyKey(otCryptoKeyRef aKeyRef);

/**
 * Check if the key ref passed has an associated key in PSA ITS.
 *
 * @param[in]  aKeyRef          The Key Ref to check.
 *
 * @retval TRUE                 There is an associated key with @p aKeyRef.
 * @retval FALSE                There is no associated key with @p aKeyRef.
 *
 * @note This API is only used by OT core when `OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE` is enabled.
 *
 */
bool otPlatCryptoHasKey(otCryptoKeyRef aKeyRef);

/**
 * Initialize the HMAC operation.
 *
 * @param[in]  aContext          Context for HMAC operation.
 *
 * @retval OT_ERROR_NONE          Successfully initialized HMAC operation.
 * @retval OT_ERROR_FAILED        Failed to initialize HMAC operation.
 * @retval OT_ERROR_INVALID_ARGS  @p aContext was NULL
 *
 * @note The platform driver shall point the context to the correct object such as psa_mac_operation_t or
 *       mbedtls_md_context_t.
 *
 */
otError otPlatCryptoHmacSha256Init(otCryptoContext *aContext);

/**
 * Uninitialize the HMAC operation.
 *
 * @param[in]  aContext          Context for HMAC operation.
 *
 * @retval OT_ERROR_NONE          Successfully uninitialized HMAC operation.
 * @retval OT_ERROR_FAILED        Failed to uninitialized HMAC operation.
 * @retval OT_ERROR_INVALID_ARGS  @p aContext was NULL
 *
 */
otError otPlatCryptoHmacSha256Deinit(otCryptoContext *aContext);

/**
 * Start HMAC operation.
 *
 * @param[in]  aContext           Context for HMAC operation.
 * @param[in]  aKey               Key material to be used for for HMAC operation.
 *
 * @retval OT_ERROR_NONE          Successfully started HMAC operation.
 * @retval OT_ERROR_FAILED        Failed to start HMAC operation.
 * @retval OT_ERROR_INVALID_ARGS  @p aContext or @p aKey was NULL
 *
 */
otError otPlatCryptoHmacSha256Start(otCryptoContext *aContext, const otCryptoKey *aKey);

/**
 * Update the HMAC operation with new input.
 *
 * @param[in]  aContext           Context for HMAC operation.
 * @param[in]  aBuf               A pointer to the input buffer.
 * @param[in]  aBufLength         The length of @p aBuf in bytes.
 *
 * @retval OT_ERROR_NONE          Successfully updated HMAC with new input operation.
 * @retval OT_ERROR_FAILED        Failed to update HMAC operation.
 * @retval OT_ERROR_INVALID_ARGS  @p aContext or @p aBuf was NULL
 *
 */
otError otPlatCryptoHmacSha256Update(otCryptoContext *aContext, const void *aBuf, uint16_t aBufLength);

/**
 * Complete the HMAC operation.
 *
 * @param[in]  aContext           Context for HMAC operation.
 * @param[out] aBuf               A pointer to the output buffer.
 * @param[in]  aBufLength         The length of @p aBuf in bytes.
 *
 * @retval OT_ERROR_NONE          Successfully completed HMAC operation.
 * @retval OT_ERROR_FAILED        Failed to complete HMAC operation.
 * @retval OT_ERROR_INVALID_ARGS  @p aContext or @p aBuf was NULL
 *
 */
otError otPlatCryptoHmacSha256Finish(otCryptoContext *aContext, uint8_t *aBuf, size_t aBufLength);

/**
 * Initialise the AES operation.
 *
 * @param[in]  aContext           Context for AES operation.
 *
 * @retval OT_ERROR_NONE          Successfully Initialised AES operation.
 * @retval OT_ERROR_FAILED        Failed to Initialise AES operation.
 * @retval OT_ERROR_INVALID_ARGS  @p aContext was NULL
 * @retval OT_ERROR_NO_BUFS       Cannot allocate the context.
 *
 * @note The platform driver shall point the context to the correct object such as psa_key_id
 *       or mbedtls_aes_context_t.
 *
 */
otError otPlatCryptoAesInit(otCryptoContext *aContext);

/**
 * Set the key for AES operation.
 *
 * @param[in]  aContext           Context for AES operation.
 * @param[out] aKey               Key to use for AES operation.
 *
 * @retval OT_ERROR_NONE          Successfully set the key for AES operation.
 * @retval OT_ERROR_FAILED        Failed to set the key for AES operation.
 * @retval OT_ERROR_INVALID_ARGS  @p aContext or @p aKey was NULL
 *
 */
otError otPlatCryptoAesSetKey(otCryptoContext *aContext, const otCryptoKey *aKey);

/**
 * Encrypt the given data.
 *
 * @param[in]  aContext           Context for AES operation.
 * @param[in]  aInput             Pointer to the input buffer.
 * @param[in]  aOutput            Pointer to the output buffer.
 *
 * @retval OT_ERROR_NONE          Successfully encrypted @p aInput.
 * @retval OT_ERROR_FAILED        Failed to encrypt @p aInput.
 * @retval OT_ERROR_INVALID_ARGS  @p aContext or @p aKey or @p aOutput were NULL
 *
 */
otError otPlatCryptoAesEncrypt(otCryptoContext *aContext, const uint8_t *aInput, uint8_t *aOutput);

/**
 * Free the AES context.
 *
 * @param[in]  aContext           Context for AES operation.
 *
 * @retval OT_ERROR_NONE          Successfully freed AES context.
 * @retval OT_ERROR_FAILED        Failed to free AES context.
 * @retval OT_ERROR_INVALID_ARGS  @p aContext was NULL
 *
 */
otError otPlatCryptoAesFree(otCryptoContext *aContext);

/**
 * Initialise the HKDF context.
 *
 * @param[in]  aContext           Context for HKDF operation.
 *
 * @retval OT_ERROR_NONE          Successfully Initialised AES operation.
 * @retval OT_ERROR_FAILED        Failed to Initialise AES operation.
 * @retval OT_ERROR_INVALID_ARGS  @p aContext was NULL
 *
 * @note The platform driver shall point the context to the correct object such as psa_key_derivation_operation_t
 *       or HmacSha256::Hash
 *
 */
otError otPlatCryptoHkdfInit(otCryptoContext *aContext);

/**
 * Perform HKDF Expand step.
 *
 * @param[in]  aContext           Operation context for HKDF operation.
 * @param[in]  aInfo              Pointer to the Info sequence.
 * @param[in]  aInfoLength        Length of the Info sequence.
 * @param[out] aOutputKey         Pointer to the output Key.
 * @param[in]  aOutputKeyLength   Size of the output key buffer.
 *
 * @retval OT_ERROR_NONE          HKDF Expand was successful.
 * @retval OT_ERROR_FAILED        HKDF Expand failed.
 * @retval OT_ERROR_INVALID_ARGS  @p aContext was NULL
 *
 */
otError otPlatCryptoHkdfExpand(otCryptoContext *aContext,
                               const uint8_t *  aInfo,
                               uint16_t         aInfoLength,
                               uint8_t *        aOutputKey,
                               uint16_t         aOutputKeyLength);

/**
 * Perform HKDF Extract step.
 *
 * @param[in]  aContext           Operation context for HKDF operation.
 * @param[in]  aSalt              Pointer to the Salt for HKDF.
 * @param[in]  aSaltLength        Length of Salt.
 * @param[in]  aInputKey          Pointer to the input key.
 *
 * @retval OT_ERROR_NONE          HKDF Extract was successful.
 * @retval OT_ERROR_FAILED        HKDF Extract failed.
 *
 */
otError otPlatCryptoHkdfExtract(otCryptoContext *  aContext,
                                const uint8_t *    aSalt,
                                uint16_t           aSaltLength,
                                const otCryptoKey *aInputKey);

/**
 * Uninitialize the HKDF context.
 *
 * @param[in]  aContext           Context for HKDF operation.
 *
 * @retval OT_ERROR_NONE          Successfully un-initialised HKDF operation.
 * @retval OT_ERROR_FAILED        Failed to un-initialised HKDF operation.
 * @retval OT_ERROR_INVALID_ARGS  @p aContext was NULL
 *
 */
otError otPlatCryptoHkdfDeinit(otCryptoContext *aContext);

/**
 * Initialise the SHA-256 operation.
 *
 * @param[in]  aContext           Context for SHA-256 operation.
 *
 * @retval OT_ERROR_NONE          Successfully initialised SHA-256 operation.
 * @retval OT_ERROR_FAILED        Failed to initialise SHA-256 operation.
 * @retval OT_ERROR_INVALID_ARGS  @p aContext was NULL
 *
 *
 * @note The platform driver shall point the context to the correct object such as psa_hash_operation_t
 *       or mbedtls_sha256_context.
 */
otError otPlatCryptoSha256Init(otCryptoContext *aContext);

/**
 * Uninitialize the SHA-256 operation.
 *
 * @param[in]  aContext           Context for SHA-256 operation.
 *
 * @retval OT_ERROR_NONE          Successfully un-initialised SHA-256 operation.
 * @retval OT_ERROR_FAILED        Failed to un-initialised SHA-256 operation.
 * @retval OT_ERROR_INVALID_ARGS  @p aContext was NULL
 *
 */
otError otPlatCryptoSha256Deinit(otCryptoContext *aContext);

/**
 * Start SHA-256 operation.
 *
 * @param[in]  aContext           Context for SHA-256 operation.
 *
 * @retval OT_ERROR_NONE          Successfully started SHA-256 operation.
 * @retval OT_ERROR_FAILED        Failed to start SHA-256 operation.
 * @retval OT_ERROR_INVALID_ARGS  @p aContext was NULL
 *
 */
otError otPlatCryptoSha256Start(otCryptoContext *aContext);

/**
 * Update SHA-256 operation with new input.
 *
 * @param[in]  aContext           Context for SHA-256 operation.
 * @param[in]  aBuf               A pointer to the input buffer.
 * @param[in]  aBufLength         The length of @p aBuf in bytes.
 *
 * @retval OT_ERROR_NONE          Successfully updated SHA-256 with new input operation.
 * @retval OT_ERROR_FAILED        Failed to update SHA-256 operation.
 * @retval OT_ERROR_INVALID_ARGS  @p aContext or @p aBuf was NULL
 *
 */
otError otPlatCryptoSha256Update(otCryptoContext *aContext, const void *aBuf, uint16_t aBufLength);

/**
 * Finish SHA-256 operation.
 *
 * @param[in]  aContext           Context for SHA-256 operation.
 * @param[in]  aHash              A pointer to the output buffer, where hash needs to be stored.
 * @param[in]  aHashSize          The length of @p aHash in bytes.
 *
 * @retval OT_ERROR_NONE          Successfully completed the SHA-256 operation.
 * @retval OT_ERROR_FAILED        Failed to complete SHA-256 operation.
 * @retval OT_ERROR_INVALID_ARGS  @p aContext or @p aHash was NULL
 *
 */
otError otPlatCryptoSha256Finish(otCryptoContext *aContext, uint8_t *aHash, uint16_t aHashSize);

/**
 * Initialize cryptographically-secure pseudorandom number generator (CSPRNG).
 *
 */
void otPlatCryptoRandomInit(void);

/**
 * Deinitialize cryptographically-secure pseudorandom number generator (CSPRNG).
 *
 */
void otPlatCryptoRandomDeinit(void);

/**
 * Fills a given buffer with cryptographically secure random bytes.
 *
 * @param[out] aBuffer  A pointer to a buffer to fill with the random bytes.
 * @param[in]  aSize    Size of buffer (number of bytes to fill).
 *
 * @retval OT_ERROR_NONE     Successfully filled buffer with random values.
 * @retval OT_ERROR_FAILED   Operation failed.
 *
 */
otError otPlatCryptoRandomGet(uint8_t *aBuffer, uint16_t aSize);

/**
 * @}
 *
 */

#ifdef __cplusplus
} // end of extern "C"
#endif
#endif // OPENTHREAD_PLATFORM_CRYPTO_H_
