#pragma once

/// @file
/// @brief Define API encryption functions
///
/// @copyright Copyright 2019 InfoTeCS.

#include <stdint.h>
#include <stddef.h>

#if ( __GNUC__ >= 4 ) || defined( __clang__ )
#   define CipherApiExport __attribute__( ( visibility( "default" ) ) )
#else
#   define CipherApiExport
#endif

enum CypherApiVersion
{
    CIPHER_API_VERSION = 2
};

enum CryptoResult
{
    SUCCESS = 0,
    BUFFER_TOO_SMALL = 0x000A0001,
    BUFFER_IS_NULLPTR = 0x000A0002,
    ENCRYPT_ERROR = 0x000A0003,
    DECRYPT_ERROR = 0x000A0004,
    KEY_NOT_FOUND_ERROR = 0x000A0005,
    BUFFER_TOO_LARGE = 0x000A0006,
    CIPHER_SUITE_NOT_AVAILABLE = 0x000A0007,
    NOT_AVAILABLE = 0x000A000A,
};

///@brief Supported cipher suites
typedef enum
{
    csG28147_CFB = 0,
    csG3412_MAGMA = 1,
    csITCS_AES256 = 4,
    csG28147_CTR = 5,
    csTFIPS = 7,
    csG3412_KUZNYECHIK = 14,
} CipherSuite;

/// @brief Command execution result
typedef struct
{
    /// @brief Function output code (0 - success)
    uint32_t code;

    /// @brief Error message (blank if success)
    /// @remarks Does not require the memory release. The message is stored till the next command call.
    const char* message;
} CipherApiReturnCode;

/// @brief Encrypt data using the host keys and the csG28147_CFB cipher suite (obsolete, use EncryptBlobEx)
/// @param[in] targetNodeId - ID of the host the encryption is performed for
/// @param[in] in - Input buffer
/// @param[in] inSize - Input buffer size
/// @param[out] out - Encrypted data buffer
/// @param[in,out] outSize - Encrypted data buffer size
/// @remarks Input buffer must be at least 16 bytes; the output buffer must not be less than the input one + 12 bytes
/// @return Command execution result
typedef CipherApiReturnCode ( *EncryptBlob )(
    uint32_t targetNodeId, uint8_t* in, size_t inSize, uint8_t* out, size_t* outSize );

/// @brief Decrypt data using the host key and the csG28147_CFB cipher suite (obsolete, use DecryptBlobEx)
/// @param[in] sourceNodeId - ID of the host the encryption is performed for
/// @param[in] in - Input buffer
/// @param[in] inSize - Input buffer size
/// @param[out] out - Decrypted data buffer
/// @param[in,out] outSize - Decrypted data buffer size
/// @remarks Input buffer must be at least 28 bytes; the output buffer must not be less than the input one - 12 bytes
/// @return Command execution result
typedef CipherApiReturnCode ( *DecryptBlob )(
    uint32_t sourceNodeId, uint8_t* in, size_t inSize, uint8_t* out, size_t* outSize );

/// @brief Check whether the specified cipher suite is supported
/// @param[in] cipherSuite - Cipher suite
/// @return Command execution result
typedef CipherApiReturnCode ( *IsCipherSuiteSupported )( uint32_t cipherSuite );

/// @brief Encrypt data using the host keys and the specified cipher suite
/// @param[in] cipherSuite - Cipher suite
/// @param[in] targetNodeId - ID of the host the encryption is performed for
/// @param[in] in - Input buffer
/// @param[in] inSize - Input buffer size
/// @param[out] out - Encrypted data buffer
/// @param[in,out] outSize - Encrypted data buffer size
/// @remarks Input buffer must be at least 16 bytes; the output buffer must not be less than the input one + 22 bytes
/// @return Command execution result
typedef CipherApiReturnCode ( *EncryptBlobEx )(
    uint32_t cipherSuite, uint32_t targetNodeId, uint8_t* in, size_t inSize, uint8_t* out, size_t* outSize );

/// @brief Decrypt host data using the host keys and the specified cipher suite
/// @param[in] cipherSuite - Cipher suite
/// @param[in] sourceNodeId - ID of the host the encryption is performed for
/// @param[in] in - Input buffer
/// @param[in] inSize - Input buffer size
/// @param[out] out - Decrypted data buffer
/// @param[in,out] outSize - Decrypted data buffer size
/// @remarks Input buffer must be at least 38 bytes; the output buffer must not be less than the input one - 22 bytes
/// @return Command execution result
typedef CipherApiReturnCode ( *DecryptBlobEx )(
    uint32_t cipherSuite, uint32_t sourceNodeId, uint8_t* in, size_t inSize, uint8_t* out, size_t* outSize );

/// @brief Encryption API functions (described above)
typedef struct
{
    // API_VERSION_1
    EncryptBlob encryptBlob;
    DecryptBlob decryptBlob;

    // API_VERSION_2
    IsCipherSuiteSupported isCipherSuiteSupported;
    EncryptBlobEx encryptBlobEx;
    DecryptBlobEx decryptBlobEx;
} ItcsCipherApi;

#ifdef __cplusplus
extern "C" {
#endif

/// @brief Get the current encryption API version
/// @remarks When updating API, check it for compatibility; otherwise, correct operation is not guaranteed
/// @return Current encryption API version
CipherApiExport uint32_t GetCipherApiVersion();

/// @brief Get the encryption API structure pointer
/// @return Pointer to the API encryption structure
CipherApiExport ItcsCipherApi* GetCipherApi();

#ifdef __cplusplus
}
#endif
