/*!
 * In Samsung Ukraine R&D Center (SRK) under a contract between
 * LLC "Samsung Electronics Ukraine Company" (Kyiv, Ukraine)
 * and "Samsung Electronics Co", Ltd (Seoul, Republic of Korea)
 * Copyright: (c) Samsung Electronics Co, Ltd 2017. All rights reserved.
 */

#ifndef WSM_DEFINITIONS_V3_H_
#define WSM_DEFINITIONS_V3_H_

#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>


#ifdef __cplusplus
extern "C" {
#endif // ifdef __cplusplus


typedef int WSM_APPSKEY_ID;
typedef int WSM_SESSION_ID;

#include "wsm_transport.h"

typedef int wsm_thread_id_t; //!< Identifier of data <source/destination> thread

/*!
 * \var WSM_SESSION_EMPTY_ID
 * \brief This WSM identifier denotes that no connection is established yet for selected device.
 * WSM_Connect() treats it as an null identifier (this is like NULL pointer denoting nothing).
 *
 * Usage of WSM identifier:
 * \li Before open connection at first time Client Application set WSM_SESSION_EMPTY_ID as identifier
 * to appropriate variable and pass it to WSM_Connect() through *id* argument.
 * After that new connection will be created and after WSM_Connect() execution *id* argument
 * contains new generated by WSM identifier.
 *
 * wsm_config_t CA_config;
 * SESSION_ID CA_id = WSM_SESSION_EMPTY_ID;
 *
 * `WSM_Connect(&CA_id, &CA_config); //<-- WSM creates new connection`
 * `// now CA_id contains valid identifier used by other WSM API calls.`
 *
 * \li in case of broken connection Client Application pass variable holding previously
 * generated identifier to WSM_Connect(). WSM faces valid id and will do reconnection
 * without changing identifier.
 *
 * `WSM_Connect(&CA_id, &CA_config); //<-- id generated above`
 * `// CA_id has the same value, but connection has been reestablished`
 */
#define WSM_SESSION_EMPTY_ID    ((WSM_SESSION_ID)0)
#define WSM_APPSKEY_EMPTY_ID    ((WSM_APPSKEY_ID)0)
#define WSM_THREAD_EMPTY_ID     ((wsm_thread_id_t)0)

#define WSM_THREAD_ID_MIN       ((wsm_thread_id_t)0) //!< Minimal value of WSM thread ID
#define WSM_THREAD_ID_MAX       ((wsm_thread_id_t)16) //!< Max amount of WSM thread IDs

#define WSM_SESSION_ID_MIN      ((WSM_SESSION_ID)0) //!< Minimal value for WSM session ID
#define WSM_SESSION_ID_MAX      ((WSM_SESSION_ID)8) //!< Max amount of WSM session IDs

//!< Length of TA name set by WSM_QUERY_SET_TA_NAME must not exceed this value, i.e. be less than TA_NAME_LEN
#define TA_NAME_LEN 64

//!< The maximum length a generated nonce. The nonce generation requested with WSM_QUERY_GENERATE_NONCE.
#define WSM_NONCE_MAX_SIZE 64

/*!
 * \enum wsm_result_t
 * \brief Enumeration with all codes returned by WSM API
 */
typedef enum
{
    WSM_SUCCESS = 0, //!< Operation successfully completed;

    WSM_ERR_INVALID_INPUT      = -100, //!< Specified input parameters are unacceptable;
    WSM_ERR_OUT_OF_MEMORY      = -101, //!< Not enough memory to complete operation successfully;
    WSM_ERR_NOT_SUPPORTED      = -102, //!< Request isn't supported in scope of used session version;
    WSM_ERR_SESSION_LIMIT      = -103, //!< Impossible to open new session, because sessions limit has been reached;
    WSM_ERR_UNKNOWN_SESSION_ID = -104, //!< Session ID is not valid, probably because it's incorrect or this session was disconnected;

    WSM_ERR_UNABLE_TO_CONNECT   = -200, //!< Unable to connect to specified device;
    WSM_ERR_DISCONNECT          = -201, //!< Unable to disconnect;
    WSM_ERR_BROKEN_CONNECTION   = -202, //!< Connection with specified device is lost and it's impossible to reconnect;
    WSM_ERR_INCOMPLETE_TRANSFER = -203, //!< Sent less bytes than given by WSM_Send() or receive less ones than expected by WSM_Receive();
    WSM_ERR_MESSAGE_BUFFER_FULL = -204, //!< Received too many unexpected messages. Possible reason server and client use different thread id
    //!< It's required to reconnect (call at first WSM_Disconnect() and after that WSM_Connect)
    WSM_ERR_DATA_VALIDATION  = -205, //!< Data validation error. Failed CRC check on received data, probably because of some transport errors.
    WSM_ERR_NO_INCOMING_DATA = -206, //!< Reached all attempts specified for receive transport operation,
    //!< but packet for requested thread wasn't received. Possible reason: the remote device has not sent yet message.

    WSM_ERR_AUTHENTICATION = -300, //!< Unable to establish secure connection due to authentication error;
    WSM_ERR_UNKNOWN_KEY    = -301, //!< Given APPSKEY ID isn't valid;
    WSM_ERR_APPS_KEY_LIMIT = -302, //!< Reached maximum amount of AppsKey's;

    WSM_ERR_SYSTEM_ERROR   = -400, //!< System error occurred. It also relates to JNI errors on Android devices;
    WSM_ERR_JAVA_EXCEPTION = 401, //!< Exception has been thrown during execution of Java code by JNI. This code used only on Android platform;

    WSM_ERR_TA_NAME_NOT_SET    = -500, //!< It's required to set name of TA by WSM_Query() to perform TrustZone-specific operation;
    WSM_ERR_RECONNECT_REQUIRED = -501, //!< It's required to reconnect (call at first WSM_Disconnect() and after that WSM_Connect) because of error related to TrustZone

    WSM_ERR_INTEGRITY = -600, //!< WSM components validation error, some components were compromised;

    //!< Internal WSM error. In case of such error it's recommended to close session
    //!< by WSM_Disconnect() and call WSM_Connect() to create new one;
    WSM_ERR_INTERNAL = -999
} wsm_result_t;

/*!
 * Maximum size of one message for send/receive operation is 2MB.
 */
#define MAX_FILE_LENGTH (5 * 1024 * 1024)

/*!
 * \enum wsm_protocol_version_t
 * \brief This enumeration stores valid values of the major version of WSM Library.
 */
typedef enum
{
    WSM_LIBRARY_VERSION1 = 0x1,
    WSM_LIBRARY_VERSION2 = 0x2,
    WSM_LIBRARY_VERSION3 = 0x3
} wsm_library_version_t;


////////////////////////////////////////////////////////////////////////////
/// Data structure related to WSM_CreateAppsKey()
////////////////////////////////////////////////////////////////////////////

/*!
 * \enum wsm_appskey_type_t
 * \brief Enumeration with key types used by WSM.
 *        Client Application can select what encryption algorithm to use.
 */
typedef enum
{
    WSM_APPSKEY_TYPE_AES = 10,

    WSM_APPSKEY_DEFAULT = WSM_APPSKEY_TYPE_AES
} wsm_appskey_type_t;

/*!
 * \enum wsm_appskey_length_t
 * \brief Enumeration with key length.
 */
typedef enum
{
    WSM_APPSKEY_LENGTH_256 = 10,

    WSM_APPSKEY_LENGTH_DEFAULT = WSM_APPSKEY_LENGTH_256
} wsm_appskey_length_t;


/*!
 * \struct wsm_appskey_options_t
 * \brief Structure containing all information required by WSM_CreateAppsKey()
 */
typedef struct wsm_appskey_options_s
{
    wsm_appskey_type_t type;
    wsm_appskey_length_t length;

    //!< used only for backward compatibility with versions 1 and 2.
    unsigned long provider_id; //!< nProviderID in WSM v1, v2
    unsigned long *consumer_id; //!< pConsumerIDs in WSM v1, v2
    unsigned long consumer_id_num; //!< nNumConsumer  in WSM v1, v2
} wsm_appskey_options_t;

#define WSM_APPSKEY_OPTIONS_INITIALIZER()     \
    {                                         \
        .type = WSM_APPSKEY_DEFAULT,          \
        .length = WSM_APPSKEY_LENGTH_DEFAULT, \
        .provider_id = 0,                     \
        .consumer_id = NULL,                  \
        .consumer_id_num = 0                  \
    }


typedef enum
{
    /*!
     * This mode associated with <WSM_Encrypt/WSM_Decrypt> of WSM v1 library
     * (WSM v1 associated with session major version 1 respectively).
     * Session major version 1 is associated with sequence of WSMv2:
     *      1.1. WSM_set_protocol_version(1)
     *      1.2. do authentication
     *      1.3. WSM_Encrypt
     *
     * For backward compatibility purposes with <WSM_encrypt2/WSM_decrypt2> of
     * WSMv2 API offset value can be changed via WSM_Query interface (offset
     * value is zero by default).
     *
     * Currently this mode is compatible with major version of WSM Session 1
     * only.
     */
    WSM_CRYPTO_MODE_AES_CBC_V1 = 10,

    /*!
     * Standardized encipherment which guarantee authenticity and integrity.
     *
     * This mode can be used for backward compatibility purposes. It's
     * associated with <WSM_Encrypt/WSM_Decrypt> of WSM v2 library. For backward
     * compatibility with <WSM_encrypt2/WSM_decrypt2> of WSMv2 API offset value
     * can be changed via WSM_Query interface (offset value is zero by default).
     */
    WSM_CRYPTO_MODE_AES_GCM = 40,

    /*!
     * The mode is like WSM_CRYPTO_MODE_AES_GCM, but additionally with "nonce".
     * WSM library do all activity related to "nonce" internally (except case
     * when major version of session is less than 3).
     *
     * This mode can be used for backward compatibility purposes. It's
     * associated with <WSM_encryptWithNonce/WSM_decryptAndCheckNonce> of
     * WSM v1/v2 library. For backward compatibility (when session major
     * version value is less than 3) "nonce" value set up through WSM_Query interface.
     */
    WSM_CRYPTO_MODE_AES_GCM_NONCE = 41,

    /*!
     * Decrypt received message according to algorithm specified by sender (when
     * session major version value is 3 or higher only) and wrap result before return to the caller.
     *
     * \warning This mode is supported only when WSM works in SWD mode,
     * otherwise WSM_ERR_NOT_SUPPORTED will be returned. To check WSM mode
     * WSM_Query call can be used.
     *
     *
     * \note This mode can be used for backward compatibility with WSM v2 and
     * associates with WSM_decryptAndWrap.
     *
     * \warning When this mode used for backward compatibility with WSM v2
     * WSM_Encrypt shall be used by sender for correct working.
     */
    WSM_CRYPTO_MODE_DECRYPT_AND_WRAP = 150,

    /*!
     * This mode NOT associated with any encipherment.
     * It means that encrypt/decrypt operations will not be applied to a data
     * passed to called interface.
     *
     * \warning process data WITHOUT encipherment.
     */
    WSM_CRYPTO_MODE_NONE = 250,

    WSM_CRYPTO_MODE_UNDEFINED = 0 //!< not valid mode.
} wsm_crypto_mode_t; //!< required to choose encipherment for WSM_<Send/Receive>


////////////////////////////////////////////////////////////////////////////
/// Data structure related to WSM_Query()
////////////////////////////////////////////////////////////////////////////

/*!
 * \enum wsm_query_t
 * This enumeration defines request codes for [WSM_Query()](@ref WSM_Query).
 * Returned value will be saved in [wsm_response_t](@ref wsm_response_t) object.
 */
typedef enum
{
    /*! \var WSM_QUERY_CONNECTED_TO_DAEMON
     * \brief It returns a value that indicates if WSM Library connected to daemon
     *
     * [out] response->code WSM_ANSWER_CONNECTED_TO_DAEMON if connected
     * [out] response->code WSM_ANSWER_NOT_CONNECTED if not connected
     */
    WSM_QUERY_CONNECTED_TO_DAEMON = 1,

    /*! \var WSM_QUERY_CONNECTED_TO_REMOTE_DEVICE
     * \brief It returns a value that indicates WSM library is connected to remote device
     *
     * [out] response->code WSM_ANSWER_CONNECTED_TO_DEVICE if connected
     * [out] response->code WSM_ANSWER_NOT_CONNECTED if not connected
     */
    WSM_QUERY_CONNECTED_TO_REMOTE_DEVICE = 2,

    /*! \var WSM_QUERY_MODE
     * \brief It returns a value that indicates which mode was WSM launched in
     *
     * [out] response->code WSM_SWD_MODE
     * [out] response->code WSM_NWD_MODE
     */
    WSM_QUERY_MODE = 3,

    /*! \var WSM_QUERY_LIBRARY_FULL_VERSION
     * \brief A query for the full version of the WSM Library in form X.Y.Z
     * as hexadecimal number. For example, Library version 3.2.1 presented
     * as 0x00 0x03 0x02 0x01.
     *
     * \note WSM allocates memory for response->reference and users must free it.
     *
     * [out] response->reference the hexadecimal presentation of the full version.
     *       SHALL be freed after usage by callee.
     * [out] response->reference_len the length of data in the field 'response'.
     */
    WSM_QUERY_LIBRARY_FULL_VERSION = 4,

    /*! \var WSM_QUERY_LIBRARY_MAJOR_VERSION
     * \brief A query for the major version of the WSM Library.
     * For example, for version 3.0.5 major version is 3, thus 3 will be returned.
     * Please see valid values for major version in the wsm_library_version_t
     * enumeration.
     *
     * [out] response->number major version as a decimal number
     */
    WSM_QUERY_LIBRARY_MAJOR_VERSION = 5,

    /*! \var WSM_QUERY_LIBRARY_MINOR_VERSION
     * \brief A query for the minor version of the WSM Library.
     * For example, for version 3.0.5 minor version is 0, thus 0 will be returned.
     *
     * [out] response->number minor version as a decimal number
     */
    WSM_QUERY_LIBRARY_MINOR_VERSION = 6,

    /*! \var WSM_QUERY_LIBRARY_PATCH_VERSION
     * \brief A query for the patch version of the WSM Library.
     * For example, for version 3.0.5 patch version is 5, thus 5 will be returned.
     *
     * [out] response->number patch version as a decimal number
     */
    WSM_QUERY_LIBRARY_PATCH_VERSION = 7,

    /*! \var WSM_QUERY_SESSION_MAJOR_VERSION
     * \brief A query for the session major version.
     * Note, a WSM session can work on a version less than Library's version.
     *
     * [out] response->number session major version as a decimal number
     */
    WSM_QUERY_SESSION_MAJOR_VERSION = 8,

    /*! \var WSM_QUERY_SESSION_MINOR_VERSION
     * \brief A query for the session minor version.
     * Note, a WSM session can work on a version less than Library's version.
     *
     * [out] response->number session minor version as a decimal number
     */
    WSM_QUERY_SESSION_MINOR_VERSION = 9,

    /*! \var WSM_QUERY_SESSION_PATCH_VERSION
     * \brief A query for the session patch version.
     * Note, a WSM session can work on a version less than Library's version.
     *
     * [out] response->number session patch version as a decimal number
     */
    WSM_QUERY_SESSION_PATCH_VERSION = 10,

    /*! \var WSM_QUERY_ESAP_KEY
     * \brief Return ESAPKey if version 1 or 2 of WSM used, otherwise ignored.
     *
     * \note WSM allocates memory for response->reference and users must free it.
     *
     * [out] response->reference is a pointer to ESAPKey, received buffer must be freed after usage
     * [out] response->reference_len contains length of generated data.
     */
    WSM_QUERY_ESAP_KEY = 11,

    /*! \var WSM_QUERY_CONFIRM_MESSAGE
     * \brief Replaces WSM_generateConfirmMessage() from version 3.0.0
     *
     * \note WSM allocates memory for response->reference and users must free it.
     *
     * [out] response->reference is a pointer to Confirm message, received buffer must be freed after usage
     * [out] response->reference_len contains length of generated data.
     */
    WSM_QUERY_CONFIRM_MESSAGE = 12,

    /*! \var WSM_QUERY_ENC_KEY
     * \brief Replaces WSM_getWSMEncKey() starting from version 3.0.0
     *
     * \note WSM allocates memory for response->reference and users must free it.
     *
     * [out] response->reference is a pointer to Encryption key, received buffer must be freed after usage
     * [out] response->reference_len contains length of generated data.
     */
    WSM_QUERY_ENC_KEY = 13,

    /*! \var WSM_QUERY_CONFIRM_KEY
     * \brief Replaces WSM_getWSMConfirmKey() starting from version 3.0.0
     *
     * \note WSM allocates memory for response->reference and users must free it.
     *
     * [out] response->reference is a pointer to Confirmation key, received buffer must be freed after usage
     * [out] response->reference_len contains length of generated data.
     */
    WSM_QUERY_CONFIRM_KEY = 14,

    /*! \var WSM_QUERY_GENERATE_NONCE
     * \brief Replaces WSM_generateNonce() from version 3.0.0
     *
     * \note WSM allocates memory for response->reference and users must free it.
     *
     * [out] response->reference is a pointer to nonce, must be freed after usage
     * [in] response->reference_len specify length of generated data.
     *      Maximum length of nonce restricted by WSM_NONCE_MAX_SIZE constant
     */
    WSM_QUERY_GENERATE_NONCE = 15,

    /*! \var WSM_QUERY_SET_NONCE
     * \brief Associate nonce with AppsKey specified by <wsm_query_option_t.key_id>.
     *
     * [in] option->data holds a pointer to nonce,
     * [in] option->data_len contains length of nonce.
     */
    WSM_QUERY_SET_NONCE = 16,

    /*! \var WSM_QUERY_SET_TA_NAME
     * \brief Associate TA name with AppsKey to use it with crypto mode WSM_CRYPTO_MODE_DECRYPT_AND_WRAP
     *
     * [in] option->data holds a pointer to TA name,
     * [in] option->data_len contains length of TA name. Maximum length restricted by WSM_TA_NAME_LEN
     */
    WSM_QUERY_SET_TA_NAME = 17,

    /*! \var WSM_QUERY_GET_ENCRYPT_SIZE
     * \brief Returns amount of memory required to encrypt message of specified size.
     *
     * [in] option->data holds a pointer to array of two uint32_t values; first value is crypto mode (supported
     *                   modes are WSM_CRYPTO_MODE_AES_GCM and WSM_CRYPTO_MODE_AES_GCM_NONCE) and second is
     *                   a length of data customer is going to encrypt
     * [in] option->data_len should be 8 (4*2, size of two uint32_t values)
     */
    WSM_QUERY_GET_CIPHER_SIZE = 18,

    /*! \var QUERY_STOP_CONNECT
     * \brief Should be used only in Java-world in WsmSession.query() call
     */
    WSM_QUERY_STOP_CONNECT = 19

//!< Another requests can be added in future
} wsm_query_t;

typedef struct
{
    uint8_t *data; //!< pointer to data associate AppsKey with.
    //!< Used by WSM_QUERY_SET_NONCE, WSM_QUERY_SET_TA_NAME
    size_t data_length; //!< length of data in bytes

    WSM_APPSKEY_ID key_id; //!< ID of AppsKey data will be associated to
    WSM_SESSION_ID session; //!< WSM_ID for ConfirmMessage, ESAPkey, ConfrimKey, Nonce.
} wsm_query_option_t;

/*!
 * \enum wsm_answer_code_t
 * This enumeration contains codes as an answer for Client Application request.
 */
typedef enum
{
    WSM_ANSWER_SWD_MODE = 1,
    WSM_ANSWER_NWD_MODE = 2,

    WSM_ANSWER_NOT_CONNECTED       = 10,
    WSM_ANSWER_CONNECTED_TO_DAEMON = 11,
    WSM_ANSWER_CONNECTED_TO_DEVICE = 12,

    WSM_ANSWER_UNDEFINED =  0
} wsm_answer_code_t;


/*!
 * \struct wsm_response_t
 * This structure used to store response for [WSM_Query()](@ref WSM_Query) request.
 * Returned value can be:
 * * named constant from [wsm_answer_code](@ref wsm_answer_code_t) and stored in *code* field;
 * * integer value and stored in *number*;
 * * byte array and stored in *reference* field. Actual length of byte array will be in *reference_len*.
 */
typedef struct
{
    wsm_answer_code_t code;
    int32_t number;

    uint8_t *reference;
    uint32_t reference_len;
} wsm_response_t;

/*!
 * \struct wsm_wrap_args_t
 * All data required for WSM_delegatedWrap()
 */
typedef struct
{
    uint8_t *ta_name; //!< input parameter
    uint8_t  ta_name_len; //!< input parameter

    uint8_t  *plain; //!< input parameter
    uint32_t  plain_len; //!< input parameter

    uint8_t  *wrapped; //!< output parameter. Allocated by WSM. Freed by Client Application.
    uint32_t  wrapped_len; //!< output parameter
} wsm_wrap_args_t;


////////////////////////////////////////////////////////////////////////////
/// Data structures and constants related to WSM_Connect()
////////////////////////////////////////////////////////////////////////////

#define WSM_ESAP_KEY_SIZE_MAX ((size_t)96)
/*!
 * WSM version 1 and 2 requires to pass Server Id and Client Id.
 * This variable holds maximum accepted length for each Id and used only for
 * backward compatibility!
 * \note WSM version 3 doesn't use Server Id and Client Id!
 */
#define WSM_MAX_ID_LENGTH 128

/*!
 * \enum wsm_auth_protocol_t
 * \brief Authentication protocol ID.
 * By default WSMv3 authentication protocol used.
 */
typedef enum
{
    WSM_AUTH_PROTOCOL_V1   = 10,
    WSM_REAUTH_PROTOCOL_V1 = 11,

    WSM_AUTH_PROTOCOL   = 20,
    WSM_REAUTH_PROTOCOL = 21,

    WSM_AUTH_PROTOCOL_UNDEFINED = 0
} wsm_auth_protocol_t;


typedef struct
{
    wsm_auth_protocol_t protocol;

    struct
    {
        struct
        {
            uint8_t value[WSM_MAX_ID_LENGTH];

            /*!
             * contains actual length of ServerId.  Must be less or equal to
             * WSM_MAX_ID_LENGTH, otherwise WSM_ERR_INVALID_INPUT returned
             */
            size_t length;
        } server_id;

        struct
        {
            uint8_t value[WSM_MAX_ID_LENGTH];

            /*!
             * contains actual length of ClientId. Must be less or equal to
             * WSM_MAX_ID_LENGTH, otherwise WSM_ERR_INVALID_INPUT returned
             */
            size_t length;
        } client_id;

        /*!
         * \struct esap_s
         * \brief optional parameters which compatible with
         *        WSM_REAUTH_PROTOCOL and WSM_REAUTH_PROTOCOL_V1 only.
         */
        struct
        {
            /*!
             * optional pointer to ESAP key. When field not equal to NULL
             * "length" shall hold value WSM_ESAP_KEY_SIZE_V1 or WSM_ESAP_KEY_SIZE_V2
             */
            uint8_t *key;

            /*!
             * if "key" is not NULL, this field should be equal to
             * WSM_ESAP_KEY_SIZE_V1 or WSM_ESAP_KEY_SIZE_V2, according to protocol:
             *     - WSM_REAUTH_PROTOCOL_V1 associated with WSM_ESAP_KEY_SIZE_V1
             *     - WSM_REAUTH_PROTOCOL associated with WSM_ESAP_KEY_SIZE_V2.
             */
            size_t length;
        } esap;
    } options;

    bool ESAP_key_update; //!< If true, use reauth process with key update.
} wsm_auth_settings_t;


typedef struct
{
    wsm_transport_settings_t transport; //!< Transport protocol settings
    wsm_auth_settings_t auth; //!< Authentication settings
} wsm_config_t; //!< Global Settings for WSM


typedef struct
{
    wsm_thread_id_t thread;
    wsm_crypto_mode_t mode;
    uint8_t *characteristic_uuid;
} transmission_ctx_t;


#define WSM_TRANSMISSION_CTX_INITIALIZER \
    {                                    \
        .thread = WSM_THREAD_ID_MIN,     \
        .mode = WSM_CRYPTO_MODE_AES_GCM, \
        .characteristic_uuid = 0         \
    }


#ifdef __cplusplus
}
#endif // ifdef __cplusplus

#endif /* WSM_DEFINITIONS_V3_H_ */
