/**
* MIT License
*
* Copyright (c) 2018 Infineon Technologies AG
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE
*
*
* \file OCP.c
*
* \brief This file implements the API Layer of OCP Library.
*
* \addtogroup grOCP
* @{
*/
#include "optiga/optiga_dtls.h"
#include "optiga/cmd/CommandLib.h"
#include "optiga/dtls/AlertProtocol.h"
#ifdef MODULE_ENABLE_DTLS_MUTUAL_AUTH
/// @cond hidden
extern Void ConfigHL(fPerformHandshake_d* PfPerformHandshake_d,eConfiguration_d PeConfiguration);
extern Void ConfigRL(sConfigRL_d* PpsConfigRL,eConfiguration_d PeConfiguration);
extern Void ConfigTL(sConfigTL_d* PpsConfigTL,eConfiguration_d PeConfiguration);
extern Void ConfigCL(sConfigCL_d* PpsConfigCL,eConfiguration_d PeConfiguration);
extern int32_t DtlsHS_VerifyHR(uint8_t* PprgbData, uint16_t PwLen);
///Identifier for Session ID 1
#define SESSIONID_1 0xE100
///Session key is in used
#define INUSE 0x4A
///Session key is not in use
#define NOTUSED 0xA4
/**
* \brief Structure that defines OCP Application context data
*/
typedef struct sAppOCPCtx_d
{
///Pointer to function that performs handshake
fPerformHandshake_d pfPerformHandshake;
///Structure that contains Handshake data
sHandshake_d sHandshake;
///Structure that holds Record Layer Configuration
sConfigRL_d sConfigRL;
///Structure that holds logger parameters
sLogger_d sLogger;
///Authentication scheme
eAuthScheme_d eAuthScheme;
///Buffer to store the received application data
uint8_t* pAppDataBuf;
}sAppOCPCtx_d;
/**
* \brief Configures the Handshake, Record, Transport and Crypto Layers based on input parameters
*/
_STATIC_H Void OCP_Config(sAppOCPCtx_d* PpsAppOCPCntx,eConfiguration_d PeConfiguration)
{
ConfigHL(&(PpsAppOCPCntx->pfPerformHandshake),PeConfiguration);
ConfigRL(&(PpsAppOCPCntx->sConfigRL),PeConfiguration);
ConfigTL(PpsAppOCPCntx->sConfigRL.sRL.psConfigTL,PeConfiguration);
ConfigCL(PpsAppOCPCntx->sConfigRL.sRL.psConfigCL,PeConfiguration);
}
/**
* Allocates the memory for sAppOCPCtx_d
*/
_STATIC_H int32_t OCPAllocateMemory(sAppOCPCtx_d ** PppsAppOCPCntx)
{
int32_t i4Status = (int32_t)OCP_LIB_MALLOC_FAILURE;
#define PS_APPOCPCNTX (* PppsAppOCPCntx)
do
{
//Allocate the memory for OCP context data structure
PS_APPOCPCNTX = (sAppOCPCtx_d *)OCP_MALLOC(sizeof(sAppOCPCtx_d));
if(NULL == PS_APPOCPCNTX)
{
break;
}
(*PS_APPOCPCNTX).pAppDataBuf = NULL;
(*PS_APPOCPCNTX).sConfigRL.sRL.psConfigTL = NULL;
(*PS_APPOCPCNTX).sConfigRL.sRL.psConfigCL = NULL;
//Allocate the memory for the transport layer and crypto layer structure
PS_APPOCPCNTX->sConfigRL.sRL.psConfigTL = (sConfigTL_d*)OCP_MALLOC(sizeof(sConfigTL_d));
if(NULL == PS_APPOCPCNTX->sConfigRL.sRL.psConfigTL)
{
break;
}
PS_APPOCPCNTX->sConfigRL.sRL.psConfigTL->sTL.phTLHdl = NULL;
PS_APPOCPCNTX->sConfigRL.sRL.psConfigCL = (sConfigCL_d*)OCP_MALLOC(sizeof(sConfigCL_d));
if(NULL == PS_APPOCPCNTX->sConfigRL.sRL.psConfigCL)
{
break;
}
PS_APPOCPCNTX->sConfigRL.sRL.psConfigCL->sCL.phCryptoHdl = NULL;
PS_APPOCPCNTX->sConfigRL.sRL.phRLHdl = NULL;
i4Status = (int32_t)OCP_LIB_OK;
}while(FALSE);
#undef PS_APPOCPCNTX
return i4Status;
}
/**
* \brief Frees the allocated memory for sAppOCPCtx_d
*/
_STATIC_H Void OCPFreeMemory(sAppOCPCtx_d* PpsAppOCPCntx)
{
if(NULL != PpsAppOCPCntx)
{
if(NULL != (PpsAppOCPCntx)->pAppDataBuf)
{
OCP_FREE((PpsAppOCPCntx)->pAppDataBuf);
}
if(NULL != (PpsAppOCPCntx)->sConfigRL.sRL.psConfigCL)
{
#define S_RL (PpsAppOCPCntx)->sConfigRL
S_RL.sRL.psConfigCL->pfClose(&(PpsAppOCPCntx)->sConfigRL.sRL.psConfigCL->sCL);
OCP_FREE((PpsAppOCPCntx)->sConfigRL.sRL.psConfigCL);
}
if(NULL != (PpsAppOCPCntx)->sConfigRL.sRL.psConfigTL)
{
#define S_TL (PpsAppOCPCntx)->sConfigRL.sRL.psConfigTL->sTL
if(NULL != S_TL.phTLHdl)
{
//Free the allocated memory for Transport layer
OCP_FREE(S_TL.phTLHdl);
S_TL.phTLHdl = NULL;
}
OCP_FREE((PpsAppOCPCntx)->sConfigRL.sRL.psConfigTL);
}
//Free the memory held by record layer
S_RL.pfClose(&S_RL.sRL);
OCP_FREE(PpsAppOCPCntx);
}
#undef S_TL
#undef S_RL
}
/**
* \brief Structure defining Session registry content
*/
typedef struct sSessionRegistry_d
{
///DTLS Session ID
uint16_t wSessionId;
///OCP Handle
hdl_t hOCPHandle;
///Variable to indicate the session ID usage
uint8_t bInUse;
}sSessionRegistry_d;
///Static registry for holding Session key Id information
sSessionRegistry_d sSessionRegistry[1] ={
{SESSIONID_1, (hdl_t)NULL, NOTUSED}
};
/**
* This API returns the available Security Chip Session id
* that can be used by Command Library SetAuthScheme.
*
* \param[in,out] PwSessionId Available session id
*
* \retval #OCP_LIB_OK
* \retval #OCP_LIB_SESSIONID_UNAVAILABLE
*/
_STATIC_H int32_t Registry_GetSessionId(uint16_t* PwSessionId)
{
int32_t i4Status = (int32_t)OCP_LIB_SESSIONID_UNAVAILABLE;
uint8_t bCount = 0;
//Search the registry for unused session key id
for(bCount= 0;bCount<(sizeof(sSessionRegistry)/sizeof(sSessionRegistry_d));bCount++)
{
if(NOTUSED == sSessionRegistry[bCount].bInUse)
{
//return unused session key id
*PwSessionId = sSessionRegistry[bCount].wSessionId;
i4Status = (int32_t)OCP_LIB_OK;
break;
}
}
return i4Status;
}
/**
* This function updates OCP handle against the given session key id in the registry if the session key id is not already used.
* The session key is marked as in-use.
*
* \param[in] PdwSessionId Session id
* \param[in,out] PhOCPHandle Context to be updated for input session id
*
* \retval #OCP_LIB_OK
* \retval #OCP_LIB_ERROR
*/
_STATIC_H int32_t Registry_Update(uint32_t PdwSessionId,const hdl_t PhOCPHandle)
{
int32_t i4Status = (int32_t)OCP_LIB_ERROR;
uint8_t bCount;
//Search the table for the given session key id
for(bCount= 0;bCount<(sizeof(sSessionRegistry)/sizeof(sSessionRegistry_d));bCount++)
{
if((PdwSessionId == sSessionRegistry[bCount].wSessionId) &&
(NOTUSED == sSessionRegistry[bCount].bInUse))
{
//Update the OCP handle
sSessionRegistry[bCount].hOCPHandle = PhOCPHandle;
//Update the usage status
sSessionRegistry[bCount].bInUse = INUSE;
i4Status = (int32_t) OCP_LIB_OK;
break;
}
}
return i4Status;
}
/**
* This function frees the session key id used for the given OCP handle in the registry.
* The session key is marked as Not used.
*
* \param[in,out] PhOCPHandle Context for which session id to be freed
*
*/
//lint --e{818} suppress "PhOCPHandle is declared as const"
_STATIC_H Void Registry_Free(const hdl_t PhOCPHandle)
{
uint8_t bCount;
//Search the table for the given session key id
for(bCount= 0;bCount<(sizeof(sSessionRegistry)/sizeof(sSessionRegistry_d));bCount++)
{
if(PhOCPHandle == sSessionRegistry[bCount].hOCPHandle)
{
//Free the usage status
sSessionRegistry[bCount].bInUse = NOTUSED;
sSessionRegistry[bCount].hOCPHandle = NULL;
break;
}
}
}
/**
* This function Gets the session key id used for the given OCP handle in the registry.
*
* \param[in] PhOCPHandle OCP handle
* \param[out] PpwSessionId Pointer to the session ID
*
* \retval #OCP_LIB_OK
* \retval #OCP_LIB_SESSIONID_UNAVAILABLE
*/
//lint --e{818} suppress "PhOCPHandle is declared as const"
_STATIC_H int32_t Registry_GetHandleSessionID(const hdl_t PhOCPHandle, uint16_t *PpwSessionId)
{
int32_t i4Status = (int32_t)OCP_LIB_SESSIONID_UNAVAILABLE;
uint8_t bCount = 0;
//Search the table for the given OCP handle
for(bCount= 0;bCount<(sizeof(sSessionRegistry)/sizeof(sSessionRegistry_d));bCount++)
{
if(PhOCPHandle == sSessionRegistry[bCount].hOCPHandle)
{
*PpwSessionId = sSessionRegistry[bCount].wSessionId;
i4Status = (int32_t)OCP_LIB_OK;
break;
}
}
return i4Status;
}
/**
* This function validates whether handle is associated with a the session key id from the registry.
*
* \param[in] PhOCPHandle OCP handle
*
* \retval #OCP_LIB_OK
* \retval #OCP_LIB_SESSIONID_UNAVAILABLE
*/
//lint --e{818} suppress "PhOCPHandle is declared as const"
_STATIC_H int32_t Registry_ValidateHandleSessionID(const hdl_t PhOCPHandle)
{
int32_t i4Status = (int32_t)OCP_LIB_SESSIONID_UNAVAILABLE;
uint8_t bCount;
//Search the table for the given OCP handle
for(bCount= 0;bCount<(sizeof(sSessionRegistry)/sizeof(sSessionRegistry_d));bCount++)
{
if(PhOCPHandle == sSessionRegistry[bCount].hOCPHandle)
{
i4Status = (int32_t)OCP_LIB_OK;
break;
}
}
return i4Status;
}
/**
* This Function closes the session ,free the memory allocated and return close notify alert based on input parameter.
*
* Notes:
* - Under some erroneous conditions,error codes from Command Library and transport layer can also be returned.
*
* \param[in] PhAppOCPCtx OCP handle
* \param[in] PfFatalError Flag indicating whether fatal error as occurred or not
* \param[in] PwSessionId Session ID to be closed
*
*/
_STATIC_H Void CloseSession(hdl_t PhAppOCPCtx, bool_t PfFatalError, uint16_t PwSessionId)
{
sAppOCPCtx_d *psCntx = (sAppOCPCtx_d*)PhAppOCPCtx;
#define S_CONFIGURATION_TL (psCntx->sConfigRL.sRL.psConfigTL)
//lint --e{534} ,Return value is ignored as irrespective of this session will be closed"
if(psCntx->sHandshake.eAuthState != eAuthInitialised)
{
if(!PfFatalError)
{
SEND_ALERT(&psCntx->sConfigRL,(int32_t) OCP_RL_ERROR);
}
//Close the DTLS session on Security Chip
CmdLib_CloseSession(PwSessionId);
}
//Disconnect from the server via transport layer
S_CONFIGURATION_TL->pfDisconnect(&S_CONFIGURATION_TL->sTL);
//Clear the session reference ID registry for the handle
Registry_Free(PhAppOCPCtx);
//Free the memory associated with the handle
OCPFreeMemory(psCntx);
#undef S_CONFIGURATION_TL
}
/// @endcond
/**
* This API initialises the OCP context based on the user inputs provided in PpsAppOCPConfig.Once the OCP context
* is initialised,PphAppOCPCtx is returned as a handle.
* PphAppOCPCtx handle is used for all interactions with the OCP APIs #OCP_Connect, #OCP_Disconnect, #OCP_Send and #OCP_Receive.
*
*
* \image html OCPInit.png "OCP_Init()" width=20cm
*
*Pre Conditions:
* - Communication with the security chip is up and running.
* #optiga_comms_open() must be successfully executed.
* - The optiga comms context for command library is registered using #CmdLib_SetOptigaCommsContext().
*
*API Details:
* - Checks for an available session OID.
* - Opens application on the security chip using CmdLib_OpenApplication().
* - Allocates the memory for internal structures, initialises it and returns as a #hdl_t*
*
*User Input:
*The user must provide configuration information in #sAppOCPConfig_d
* - eMode allows the user to configure the OCP context as a client/server as per #eMode_d. Currently server mode is not supported.
* - eConfiguration allows the user to choose supported OCP context configuration as per #eConfiguration_d. Currently eTLS_12_TCP_HWCRYPTO is not supported.
* - wOIDDevCertificate allows the user to choose the supported certificate for client authentication.
* - If their is no client certificate then set it to 0x0000.
* - wOIDDevPrivKey allows the user to choose the private key used for client authentication.
* - psNetworkParams allows the user to configure the port, IP Address and maximum PMTU required for transport layer connection.
* - Valid IP address and port number must be provided. The correctness of the IP address and port number will not be verified.
* - PMTU value should range between 296 to 1500,else #OCP_LIB_UNSUPPORTED_PMTU error is returned.
* - Logger allows user to log data. User must provide the low level log writer through #sLogger_d.
* - pfGetUnixTIme(#fGetUnixTime_d) is a call-back function pointer that allows user to provide 32-bit Unix time format.
* - If pfGetUnixTIme is set to NULL, the unix time will not be sent to security chip.
* - If pfGetUnixTIme is not set to NULL or valid function pointer, the behavior would be unexpected.
* - The call back function pfGetUnixTIme is expected to return status s as #CALL_BACK_OK for success.
*
*Notes:
* - Currently, only 1 DTLS session is supported by security chip.
* - If user invokes OCP_Init, with out disconnecting/closing the previous session/context(if available), will lead to error #OCP_LIB_SESSIONID_UNAVAILABLE.
* - Under some failure conditions, error codes from lower layers could also be returned.
*
* \param[in] PpsAppOCPConfig Pointer to structure that contains the configuration from user
* \param[in,out] PphAppOCPCtx Pointer to the handle of OCP context
*
* \retval #OCP_LIB_OK
* \retval #OCP_LIB_ERROR
* \retval #OCP_LIB_NULL_PARAM
* \retval #OCP_LIB_UNSUPPORTED_CONFIG
* \retval #OCP_LIB_SESSIONID_UNAVAILABLE
* \retval #OCP_LIB_UNSUPPORTED_PMTU
*/
int32_t OCP_Init(const sAppOCPConfig_d* PpsAppOCPConfig,hdl_t* PphAppOCPCtx)
{
int32_t i4Status = (int32_t)OCP_LIB_ERROR;
sAppOCPCtx_d *psAppOCPCntx = NULL;
eAuthScheme_d eAuthScheme;
sOpenApp_d sOpenApp;
uint16_t wSessionKeyId;
do
{
//NULL check for input parameters
if((NULL == PpsAppOCPConfig) || (NULL == PphAppOCPCtx))
{
i4Status = (int32_t)OCP_LIB_NULL_PARAM;
break;
}
//Initialize handle to NULL
* PphAppOCPCtx = NULL;
//Check the registry whether a free session Id is available
i4Status = Registry_GetSessionId(&wSessionKeyId);
if(OCP_LIB_OK != i4Status)
{
break;
}
//Check for valid configuration
if(eDTLS_12_UDP_HWCRYPTO != PpsAppOCPConfig->eConfiguration)
{
i4Status = (int32_t)OCP_LIB_UNSUPPORTED_CONFIG;
break;
}
//Initialize the Auth Scheme type
if((eClient == PpsAppOCPConfig->eMode) && (eDTLS_12_UDP_HWCRYPTO == PpsAppOCPConfig->eConfiguration))
{
eAuthScheme = eDTLSClient;
}
else
{
//If other mode is selected
i4Status = (int32_t)OCP_LIB_UNSUPPORTED_MODE;
break;
}
//Check if the PMTU provided is within limit or not
if((PpsAppOCPConfig->sNetworkParams.wMaxPmtu > MAX_PMTU) || (PpsAppOCPConfig->sNetworkParams.wMaxPmtu < MIN_PMTU))
{
i4Status = (int32_t)OCP_LIB_UNSUPPORTED_PMTU;
break;
}
//Allocate the memory for the Context data structure.It will be used for all interaction with OCP APIs
i4Status = OCPAllocateMemory(&psAppOCPCntx);
if(OCP_LIB_OK != i4Status)
{
break;
}
OCP_Config(psAppOCPCntx,PpsAppOCPConfig->eConfiguration);
sOpenApp.eOpenType = eInit;
//Open Application
i4Status = CmdLib_OpenApplication(&sOpenApp);
if(CMD_LIB_OK != i4Status)
{
break;
}
//Assign the Auth Scheme
psAppOCPCntx->eAuthScheme = eAuthScheme;
//Assign the maximum path transfer unit
psAppOCPCntx->sHandshake.wMaxPmtu = PpsAppOCPConfig->sNetworkParams.wMaxPmtu;
//Assign the Certificate type to be used for Authentication
psAppOCPCntx->sHandshake.wOIDDevCertificate = PpsAppOCPConfig->wOIDDevCertificate;
//Assign the private key to be used for Authentication
psAppOCPCntx->sHandshake.wOIDDevPrivKey = PpsAppOCPConfig->wOIDDevPrivKey;
//Assign the callback function to get unix time
psAppOCPCntx->sHandshake.pfGetUnixTIme = PpsAppOCPConfig->pfGetUnixTIme;
//Assign the record layer configuration pointer to the handshake layer
psAppOCPCntx->sHandshake.psConfigRL = &psAppOCPCntx->sConfigRL;
psAppOCPCntx->sHandshake.eMode = PpsAppOCPConfig->eMode;
//Set the fatal error occur type to false;
psAppOCPCntx->sHandshake.fFatalError = FALSE;
//Assign the logger pointer for psAppOCPCntx layer
psAppOCPCntx->sLogger = PpsAppOCPConfig->sLogger;
//Assign the logger pointer for handshake layer
psAppOCPCntx->sHandshake.phLogger = PpsAppOCPConfig->sLogger;
//Assign the logger pointer for Record layer
psAppOCPCntx->sConfigRL.sRL.sLogger.pHdl = PpsAppOCPConfig->sLogger.pHdl;
psAppOCPCntx->sConfigRL.sRL.sLogger.phfWriter = PpsAppOCPConfig->sLogger.phfWriter;
/// @cond hidden
#define S_TL psAppOCPCntx->sConfigRL.sRL.psConfigTL->sTL
/// @endcond
//Assign the IP address to the transport layer parameter
S_TL.pzIpAddress = PpsAppOCPConfig->sNetworkParams.pzIpAddress;
//Assign the port number to the transport layer parameter
S_TL.wPort = PpsAppOCPConfig->sNetworkParams.wPort;
//Assign the UDP Timeout to the transport layer parameter
S_TL.wTimeout = 200;
//Assign the logger pointer for Transport layer
S_TL.sLogger.pHdl = PpsAppOCPConfig->sLogger.pHdl;
S_TL.sLogger.phfWriter = PpsAppOCPConfig->sLogger.phfWriter;
//Assign the initial connection state of transport layer
S_TL.eIsConnected = eDisconnected;
//Assign receive call function type
S_TL.eCallType = eNonBlocking;
//Assign the logger pointer for Transport layer
psAppOCPCntx->sConfigRL.sRL.psConfigCL->sCL.sLogger.pHdl = PpsAppOCPConfig->sLogger.pHdl;
psAppOCPCntx->sConfigRL.sRL.psConfigCL->sCL.sLogger.phfWriter = PpsAppOCPConfig->sLogger.phfWriter;
//Initialize all the modules
//Init for logger
//lint --e{611} suppress "This is ignored as it is known to the user for data type conversion"
LOG_SETWRITER((pFWriteData2)PpsAppOCPConfig->sLogger.phfWriter,(Void*)PpsAppOCPConfig->sLogger.pHdl);
//Init Record Layer
i4Status = psAppOCPCntx->sConfigRL.pfInit(&psAppOCPCntx->sConfigRL.sRL);
if(OCP_RL_OK != i4Status)
{
break;
}
//Init Transport Layer
i4Status = psAppOCPCntx->sConfigRL.sRL.psConfigTL->pfInit(&S_TL);
if(OCP_TL_OK != i4Status)
{
break;
}
//Init Crypto Layer
i4Status = psAppOCPCntx->sConfigRL.sRL.psConfigCL->pfInit(&psAppOCPCntx->sConfigRL.sRL.psConfigCL->sCL, (Void*)&wSessionKeyId);
if(OCP_CL_OK != i4Status)
{
break;
}
//Update the registry with the session ID being used
i4Status = Registry_Update(wSessionKeyId,(hdl_t) psAppOCPCntx);
if(OCP_LIB_OK != i4Status)
{
break;
}
//Set the Authentication state to initialised
psAppOCPCntx->sHandshake.eAuthState = eAuthInitialised;
*PphAppOCPCtx = (hdl_t) psAppOCPCntx;
psAppOCPCntx->sHandshake.PhAppOCPCtx = *PphAppOCPCtx;
i4Status = (int32_t)OCP_LIB_OK;
}while(FALSE);
if((OCP_LIB_OK != i4Status) && ((int32_t)OCP_LIB_NULL_PARAM != i4Status) && ((int32_t)OCP_LIB_SESSIONID_UNAVAILABLE != i4Status))
{
OCPFreeMemory(psAppOCPCntx);
}
/// @cond hidden
#undef S_TL
/// @endcond
return i4Status;
}
/**
* This API connects to the server and performs a DTLS handshake protocol as per DTLS v1.2
*
*
* \image html OCPConnect.png "OCP_Connect()" width=20cm
*
*Pre Conditions:
* - #OCP_Init() is successful and application context is available.
* - Server trust anchor must be available in the security chip.
*
*API Details:
* - Connects to the server via the transport layer.
* - Invokes CmdLib_SetAuthScheme() based on configuration.
* - Performs a DTLS Handshake.
*
*User Input:
* - User must provide a valid PhAppOCPCtx handle otherwise #OCP_LIB_SESSIONID_UNAVAILABLE is returned.
*
*Notes:
* - The default value of timeout for retransmission must be 2 seconds on the server side.
* - If a connection already exists on the given port and IP address, #OCP_LIB_CONNECTION_ALREADY_EXISTS is returned.
* - Under some failure conditions, error codes from lower layers could also be returned.
* - In case of a Failure other than #OCP_LIB_CONNECTION_ALREADY_EXISTS and #OCP_LIB_SESSIONID_UNAVAILABLE
* - The Session gets closed automatically.
* - The memory allocated in #OCP_Init() are freed.
* - OCP handle will not be set to NULL.It is upto the user to check return code and take appropriate action.
* - If the return value is #CMD_DEV_EXEC_ERROR, it might indicate that the application on the security chip is either
* closed or a reset has occurred.
*
*
* \param[in] PhAppOCPCtx Handle to OCP Context
*
* \retval #OCP_LIB_OK
* \retval #OCP_LIB_ERROR
* \retval #OCP_LIB_NULL_PARAM
* \retval #OCP_LIB_CONNECTION_ALREADY_EXISTS
* \retval #OCP_LIB_SESSIONID_UNAVAILABLE
*/
int32_t OCP_Connect(const hdl_t PhAppOCPCtx)
{
int32_t i4Status = (int32_t)OCP_LIB_ERROR;
sAuthScheme_d sAuthScheme;
/// @cond hidden
#define PS_CNTX ((sAppOCPCtx_d*)PhAppOCPCtx)
#define S_CONFIGURATION_TL (PS_CNTX->sConfigRL.sRL.psConfigTL)
#define S_CONFIGURATION_CL (PS_CNTX->sConfigRL.sRL.psConfigCL)
#define S_CONFIGURATION_RL (PS_CNTX->sConfigRL)
/// @endcond
do
{
//NULL check for handle
if(NULL == PS_CNTX)
{
i4Status = (int32_t)OCP_LIB_NULL_PARAM;
break;
}
i4Status = Registry_ValidateHandleSessionID(PhAppOCPCtx);
if(OCP_LIB_OK != i4Status)
{
break;
}
//Null checks for other pointers
if((NULL == S_CONFIGURATION_TL) || (NULL== S_CONFIGURATION_TL->pfConnect)|| (NULL == PS_CNTX->pfPerformHandshake)||
(NULL == S_CONFIGURATION_RL.pfSend)|| (NULL == S_CONFIGURATION_RL.pfRecv) || (NULL == S_CONFIGURATION_RL.pfClose) ||
(NULL == S_CONFIGURATION_TL->pfSend) || (NULL == S_CONFIGURATION_TL->pfRecv) || (NULL == S_CONFIGURATION_TL->pfDisconnect) ||
(NULL == S_CONFIGURATION_CL) || (NULL == S_CONFIGURATION_CL->pfEncrypt) || (NULL == S_CONFIGURATION_CL->pfDecrypt) ||
(NULL == S_CONFIGURATION_CL->pfClose))
{
i4Status = (int32_t)OCP_LIB_NULL_PARAM;
break;
}
//Check whether connection is already connected
if(eConnected == S_CONFIGURATION_TL->sTL.eIsConnected)
{
i4Status = (int32_t)OCP_LIB_CONNECTION_ALREADY_EXISTS;
break;
}
//Connect to server
i4Status = S_CONFIGURATION_TL->pfConnect(&S_CONFIGURATION_TL->sTL);
if(OCP_TL_OK != i4Status)
{
break;
}
//Get the Session OID from registry
i4Status = Registry_GetHandleSessionID(PhAppOCPCtx,&(PS_CNTX->sHandshake.wSessionOID));
if(OCP_LIB_OK != i4Status)
{
break;
}
//Initialize Auth scheme structure
sAuthScheme.eAuthScheme = PS_CNTX->eAuthScheme;
sAuthScheme.wDevicePrivKey = PS_CNTX->sHandshake.wOIDDevPrivKey;
sAuthScheme.wSessionKeyId = PS_CNTX->sHandshake.wSessionOID;
//Set the AuthScheme
i4Status = CmdLib_SetAuthScheme(&sAuthScheme);
if(CMD_LIB_OK != i4Status)
{
break;
}
//Perform Handshake
i4Status = PS_CNTX->pfPerformHandshake((hdl_t)&PS_CNTX->sHandshake);
if(OCP_HL_OK != i4Status)
{
break;
}
i4Status = (int32_t) OCP_LIB_OK;
}while(FALSE);
do
{
if((OCP_LIB_OK != i4Status) &&
((int32_t)OCP_LIB_CONNECTION_ALREADY_EXISTS != i4Status) &&
((int32_t)OCP_LIB_NULL_PARAM != i4Status) &&
((int32_t)OCP_LIB_SESSIONID_UNAVAILABLE != i4Status))
{
//lint --e{794} suppress "OCP_LIB_NULL_PARAM check address this lint issue which doesn't allow null pointer in this context,"
CloseSession(PhAppOCPCtx,PS_CNTX->sHandshake.fFatalError, PS_CNTX->sHandshake.wSessionOID);
LOG_TRANSPORTDBVAL(i4Status,eInfo);
}
}while(FALSE);
/// @cond hidden
#undef PS_CNTX
#undef S_CONFIGURATION_CL
#undef S_CONFIGURATION_TL
#undef S_CONFIGURATION_RL
/// @endcond
return i4Status;
}
/**
* This API sends application data to the DTLS server
*
*
* \image html OCPSend.png "OCP_Send()" width=20cm
*
*Pre Conditions:
* - #OCP_Connect() is successful and application context is available.
*
*API Details:
* - Sends application data to DTLS server.
* - Application data is sent only if Mutual Authentication Public Key Scheme (DTLS) was successfully performed.
* - Encryption of the application data is done at the record layer.
*
*
*User Input:
* - User must provide a valid PhAppOCPCtx handle.
* - User must provide the data to be sent and its length
* - If the length of the data to be sent is greater then #MAX_APP_DATALEN(PhAppOCPCtx), then #OCP_LIB_INVALID_LEN is returned.
* - If the length of the data to be sent is equal to zero, then #OCP_LIB_LENZERO_ERROR is returned.
*
*Notes:
* - The maximum length of data that can be sent by the API depends upon the PMTU value set during #OCP_Init().This length can be obtained by #MAX_APP_DATALEN(PhAppOCPCtx).
* - Fragmentation of data to be sent should be done by the application. This API does not perform data fragmentation.
* - If the record sequence number has reached maximum value for epoch 1, then #OCP_RL_SEQUENCE_OVERFLOW error is returned.
* User must call #OCP_Disconnect() in this condition.No Alert will be sent due to the unavailability of record sequence number.
* - Under some failure conditions, error codes from lower layers could also be returned.
* - In case of a Failure,
* - Existing session remains open and memory allocated during OCP_Init() is not freed.
* - PhAppOCPCtx handle is not set to NULL.
* - The API does not send any alert to the server.
* - If the return value is #CMD_DEV_EXEC_ERROR, it might indicate that the application on the security chip is either
* closed or a reset has occurred. In such a case, close the existing DTLS session using #OCP_Disconnect.
*
*
* \param[in] PhAppOCPCtx Handle to OCP Context
* \param[in] PprgbData Pointer to data to be sent
* \param[in] PwLen Length of the data to be sent
*
* \retval #OCP_LIB_OK
* \retval #OCP_LIB_ERROR
* \retval #OCP_LIB_NULL_PARAM
* \retval #OCP_LIB_SESSIONID_UNAVAILABLE
* \retval #OCP_LIB_AUTHENTICATION_NOTDONE
* \retval #OCP_LIB_MALLOC_FAILURE
* \retval #OCP_LIB_LENZERO_ERROR
* \retval #OCP_LIB_INVALID_LEN
* \retval #OCP_RL_SEQUENCE_OVERFLOW
*/
int32_t OCP_Send(const hdl_t PhAppOCPCtx,const uint8_t* PprgbData,uint16_t PwLen)
{
int32_t i4Status = (int32_t)OCP_LIB_ERROR;
/// @cond hidden
#define PS_CNTX ((sAppOCPCtx_d*)PhAppOCPCtx)
#define S_CONFIGURATION_RL (PS_CNTX->sConfigRL)
#define S_HS (PS_CNTX->sHandshake)
/// @endcond
do
{
//NULL check for handle
if((NULL == PS_CNTX) || (NULL == PprgbData))
{
i4Status = (int32_t)OCP_LIB_NULL_PARAM;
break;
}
//Validate the handle for the sessionID
i4Status = Registry_ValidateHandleSessionID(PhAppOCPCtx);
if(OCP_LIB_OK != i4Status)
{
break;
}
//Null checks for other pointers
if((NULL == PS_CNTX->sConfigRL.sRL.psConfigTL) || (NULL == S_CONFIGURATION_RL.pfSend) ||
(NULL == PS_CNTX->sConfigRL.sRL.psConfigTL->pfSend) || (NULL == PS_CNTX->sConfigRL.sRL.psConfigCL) ||
(NULL == PS_CNTX->sConfigRL.sRL.psConfigCL->pfEncrypt))
{
i4Status = (int32_t)OCP_LIB_NULL_PARAM;
break;
}
//Is Authentication session closed
if(S_HS.eAuthState == eAuthSessionClosed)
{
i4Status = (int32_t)OCP_LIB_OPERATION_NOT_ALLOWED;
break;
}
//Is Mutual Authentication complete
if(S_HS.eAuthState != eAuthCompleted)
{
i4Status = (int32_t)OCP_LIB_AUTHENTICATION_NOTDONE;
break;
}
//Zero Length
if(0x00 == PwLen)
{
i4Status = (int32_t)OCP_LIB_LENZERO_ERROR;
break;
}
//If length of data to be sent is greater then Max value
if(MAX_APP_DATALEN(PhAppOCPCtx) < PwLen)
{
i4Status = (int32_t)OCP_LIB_INVALID_LEN;
break;
}
//Memory need to be allocated
S_CONFIGURATION_RL.sRL.bContentType = CONTENTTYPE_APP_DATA;
S_CONFIGURATION_RL.sRL.bMemoryAllocated = FALSE;
//Call Record layer
i4Status = S_CONFIGURATION_RL.pfSend(&S_CONFIGURATION_RL.sRL, (uint8_t*)PprgbData, PwLen);
if(OCP_RL_OK != i4Status)
{
break;
}
i4Status = (int32_t)OCP_LIB_OK;
}while(FALSE);
/// @cond hidden
#undef PS_CNTX
#undef S_CONFIGURATION_RL
#undef S_HS
/// @endcond
return i4Status;
}
/**
* This API receives application data from the DTLS server
*
*
* \image html OCPRecv.png "OCP_Recv()" width=20cm
*
*Pre Conditions:
* - #OCP_Connect() is successful and application context is available.
*
*API Details:
* - Receives application data from the DTLS server.
* - Application data is received only if Mutual Authentication Public Key Scheme (DTLS) was successfully performed.
* - Received data is assumed to be encrypted and processed accordingly. Decryption of the application data is done at the record layer.
* - Total received application data length is updated in PpwLen.
*
*
*User Input:
* - User must provide a valid PhAppOCPCtx handle.
* - User must provide the buffer where application data should be returned.
* - User must provide the length of the buffer.
* - If the length of the buffer is equal to zero, then #OCP_LIB_LENZERO_ERROR is returned.
* - User must provide the timeout value in milliseconds. The value should be greater than 0 and maximum up to (2^16)-1.
* - If the timeout is zero #OCP_LIB_INVALID_TIMEOUT is returned.
*
*Notes:
* - The maximum length of data that can be received by the API depends upon the PMTU value set during #OCP_Init().This length is indicated by #MAX_APP_DATALEN(PhAppOCPCtx).
* - If required, the Re-Assembly of received data should be done by the application. This API does not perform data re-assembly.
* - Failure in decrypting data will return #OCP_LIB_DECRYPT_FAILURE.
* - If a fatal alert with valid description is received,
* - #OCP_AL_FATAL_ERROR is returned.
* - User must invoke #OCP_Disconnect() in this condition.Invoking OCP_Send() or OCP_Recv() will return #OCP_LIB_OPERATION_NOT_ALLOWED.
* - If a valid Hello request is received, the API internally sends a warning alert with description "no-renegotiation" to the server and then waits for data until timeout occurs.
* - If the length of buffer provided by the application is not sufficient to return received data, #OCP_LIB_INSUFFICIENT_MEMORY is returned. This data will not be returned in subsequent API invocation.
* - If timeout occurs,#OCP_LIB_TIMEOUT is returned.
* - Under some failure conditions, error codes from lower layers could also be returned.
* - In case of a Failure,
* - Existing session remains open and memory allocated during OCP_Init() is not freed.
* - PhAppOCPCtx handle is not set to NULL.
* - The API does not send any alert to the server.
* - PpwLen is set to zero.
* - If the return value is #CMD_DEV_EXEC_ERROR, it might indicate that the application on the security chip is either
* closed or a reset has occurred. In such a case, close the existing DTLS session using #OCP_Disconnect.
*
*
* \param[in] PhAppOCPCtx Handle to OCP Context
* \param[in,out] PprgbData Pointer to buffer where data is to be received
* \param[in,out] PpwLen Pointer to the length of buffer. Updated with actual length of received data.
* \param[in] PwTimeout Timeout in milliseconds
*
* \retval #OCP_LIB_OK
* \retval #OCP_LIB_ERROR
* \retval #OCP_LIB_NULL_PARAM
* \retval #OCP_LIB_SESSIONID_UNAVAILABLE
* \retval #OCP_LIB_AUTHENTICATION_NOTDONE
* \retval #OCP_LIB_MALLOC_FAILURE
* \retval #OCP_LIB_LENZERO_ERROR
* \retval #OCP_AL_FATAL_ERROR
* \retval #OCP_LIB_INSUFFICIENT_MEMORY
* \retval #OCP_LIB_INVALID_TIMEOUT
* \retval #OCP_LIB_TIMEOUT
* \retval #OCP_LIB_OPERATION_NOT_ALLOWED
*/
int32_t OCP_Receive(const hdl_t PhAppOCPCtx, uint8_t* PprgbData, uint16_t* PpwLen, uint16_t PwTimeout)
{
int32_t i4Status = (int32_t)OCP_LIB_ERROR;
sbBlob_d sAppData;
int32_t i4Alert;
uint32_t dwStarttime;
/// @cond hidden
#define PS_CNTX ((sAppOCPCtx_d*)PhAppOCPCtx)
#define S_CONFIGURATION_RL (PS_CNTX->sConfigRL)
#define S_HS (PS_CNTX->sHandshake)
/// @endcond
do
{
//NULL check for handle
if((NULL == PS_CNTX) || (NULL == PprgbData) || (NULL == PpwLen))
{
i4Status = (int32_t)OCP_LIB_NULL_PARAM;
break;
}
//Validate the handle for the sessionID
i4Status = Registry_ValidateHandleSessionID(PhAppOCPCtx);
if(OCP_LIB_OK != i4Status)
{
break;
}
//Null checks for other pointers
if((NULL == PS_CNTX->sConfigRL.sRL.psConfigTL) || (NULL == S_CONFIGURATION_RL.pfRecv) ||
(NULL == PS_CNTX->sConfigRL.sRL.psConfigTL->pfRecv)|| (NULL == PS_CNTX->sConfigRL.sRL.psConfigCL) ||
(NULL == PS_CNTX->sConfigRL.sRL.psConfigCL->pfDecrypt))
{
i4Status = (int32_t)OCP_LIB_NULL_PARAM;
break;
}
//Is Authentication session closed
if(S_HS.eAuthState == eAuthSessionClosed)
{
i4Status = (int32_t)OCP_LIB_OPERATION_NOT_ALLOWED;
break;
}
//Is Mutual Authentication Public Key Scheme (DTLS) complete
if(S_HS.eAuthState != eAuthCompleted)
{
i4Status = (int32_t)OCP_LIB_AUTHENTICATION_NOTDONE;
break;
}
//Zero Length
if(0x00 == *PpwLen)
{
i4Status = (int32_t)OCP_LIB_LENZERO_ERROR;
break;
}
if(0x00 == PwTimeout)
{
i4Status = (int32_t)OCP_LIB_INVALID_TIMEOUT;
break;
}
if(NULL == PS_CNTX->pAppDataBuf)
{
PS_CNTX->pAppDataBuf = OCP_MALLOC(TLBUFFER_SIZE);
if(NULL == PS_CNTX->pAppDataBuf)
{
i4Status = (int32_t)OCP_LIB_MALLOC_FAILURE;
break;
}
}
PS_CNTX->sConfigRL.sRL.psConfigTL->sTL.wTimeout = PwTimeout;
//Start value for the Flight timeout
dwStarttime = pal_os_timer_get_time_in_milliseconds();
do
{
PS_CNTX->sConfigRL.sRL.bContentType = CONTENTTYPE_APP_DATA;
sAppData.prgbStream = PS_CNTX->pAppDataBuf;
sAppData.wLen = TLBUFFER_SIZE;
i4Status = PS_CNTX->sConfigRL.pfRecv((sRL_d*)&(PS_CNTX->sConfigRL.sRL), sAppData.prgbStream, &sAppData.wLen);
//Application record received
if((int32_t)OCP_RL_APPDATA_RECEIVED == i4Status)
{
if(sAppData.wLen > *PpwLen)
{
i4Status = (int32_t)OCP_LIB_INSUFFICIENT_MEMORY;
break;
}
*PpwLen = sAppData.wLen;
Utility_Memmove(PprgbData, sAppData.prgbStream, sAppData.wLen);
i4Status = OCP_LIB_OK;
break;
}
//Alert record received
if((int32_t)OCP_RL_ALERT_RECEIVED == i4Status)
{
i4Status = Alert_ProcessMsg(&sAppData,&i4Alert);
if(((int32_t)OCP_AL_FATAL_ERROR == i4Alert))
{
S_HS.eAuthState = eAuthSessionClosed;
i4Status = i4Alert;
break;
}
}
//Malloc failure
if(((int32_t)OCP_RL_MALLOC_FAILURE == i4Status))
{
//Exit the state machine
break;
}
//Decryption failure
if(((int32_t)CMD_LIB_DECRYPT_FAILURE == i4Status))
{
i4Status = (int32_t)OCP_LIB_DECRYPT_FAILURE;
//Exit
break;
}
//Handshake record received
if((int32_t)OCP_RL_OK == i4Status)
{
i4Status = DtlsHS_VerifyHR(sAppData.prgbStream, sAppData.wLen);
if(i4Status == OCP_HL_OK)
{
SEND_ALERT(&PS_CNTX->sConfigRL,(int32_t) OCP_LIB_NO_RENEGOTIATE);
}
}
if((uint32_t)(pal_os_timer_get_time_in_milliseconds() - dwStarttime) > (uint32_t)PwTimeout)
{
i4Status = (int32_t)OCP_LIB_TIMEOUT;
break;
}
//Dynamically setting the UDP timeout
PS_CNTX->sConfigRL.sRL.psConfigTL->sTL.wTimeout = (uint16_t)(PwTimeout - (uint16_t)(pal_os_timer_get_time_in_milliseconds() - dwStarttime));
}while(TRUE);
}while(FALSE);
if((OCP_LIB_OK != i4Status) && (NULL != PpwLen))
{
*PpwLen = 0x00;
}
//lint --e{794} suppress "OCP_LIB_NULL_PARAM check addresses this lint issue which doesn't allow null pointer in this context,"
if(((int32_t)OCP_LIB_NULL_PARAM != i4Status) && ((int32_t)OCP_LIB_SESSIONID_UNAVAILABLE != i4Status) &&
((int32_t)OCP_LIB_OPERATION_NOT_ALLOWED != i4Status) && ((int32_t)OCP_LIB_AUTHENTICATION_NOTDONE != i4Status) &&
((int32_t)OCP_LIB_LENZERO_ERROR != i4Status) && ((int32_t)OCP_LIB_INVALID_TIMEOUT != i4Status) &&
(NULL != PS_CNTX->pAppDataBuf) && (PS_CNTX->sConfigRL.sRL.bMultipleRecord == 0))
{
OCP_FREE(PS_CNTX->pAppDataBuf);
PS_CNTX->pAppDataBuf = NULL;
}
/// @cond hidden
#undef PS_CNTX
#undef S_CONFIGURATION_RL
#undef S_HS
/// @endcond
return i4Status;
}
/**
* This API disconnects the client from the server and closes the DTLS session
*
*
* \image html OCPDisconnect.png "OCP_Disconnect()" width=20cm
*
*Pre Conditions:
* - OCP_Init() or OCP_Connect() is successful.
*
*API Details:
* - Applicable only if called after a successful OCP_Connect()
* - Closes DTLS session on security chip via CmdLib_CloseSession().
* - Sends closure alert to the server via the transport layer.
* - Disconnects from the server via the transport layer.
* - Applicable if called after successful OCP_Init() or OCP_Connect()
* - Clear memory associated with PhAppOCPCtx handle.
* - Clears the internal session reference Id registry.
* - PhAppOCPCtx handle will not be set to NULL.It is up to the user to check return code and take appropriate action.
*
*
*User Input:
* - User must provide a valid PhAppOCPCtx handle.
*
* Notes:
* - If the record sequence number has reached maximum value for epoch 1, No Alert will be send due to the unavailability of record sequence number.
* - DTLS server will not be notified of the fault condition that leads to failure while sending Close_Notify alert.
*
*
* \param[in] PhAppOCPCtx Handle to OCP Context
*
* \retval #OCP_LIB_OK
* \retval #OCP_LIB_NULL_PARAM
* \retval #OCP_LIB_ERROR
*/
int32_t OCP_Disconnect(hdl_t PhAppOCPCtx)
{
int32_t i4Status = (int32_t)OCP_LIB_ERROR;
uint16_t wSessionId = 0;
/// @cond hidden
#define PS_CNTX ((sAppOCPCtx_d*)PhAppOCPCtx)
#define S_CONFIGURATION_TL (PS_CNTX->sConfigRL.sRL.psConfigTL)
#define S_CONFIGURATION_CL (PS_CNTX->sConfigRL.sRL.psConfigCL)
/// @endcond
do
{
//NULL check for handle
if(NULL == PS_CNTX)
{
i4Status = (int32_t)OCP_LIB_NULL_PARAM;
break;
}
//Validate the handle for the sessionID
i4Status = Registry_ValidateHandleSessionID(PhAppOCPCtx);
if(OCP_LIB_OK != i4Status)
{
break;
}
//Null checks
if((NULL == S_CONFIGURATION_TL) || (NULL == S_CONFIGURATION_CL) || (NULL == S_CONFIGURATION_CL->pfClose) ||
(NULL == S_CONFIGURATION_TL->pfDisconnect) || (NULL == PS_CNTX->sConfigRL.pfClose))
{
i4Status = (int32_t)OCP_LIB_NULL_PARAM;
break;
}
//Get the session ID associated with the handle
i4Status = Registry_GetHandleSessionID(PhAppOCPCtx, &wSessionId);
if(OCP_LIB_OK != i4Status)
{
break;
}
//Close the session and free the memory allocated
CloseSession(PhAppOCPCtx, PS_CNTX->sHandshake.fFatalError, wSessionId);
PhAppOCPCtx = NULL;
}while(FALSE);
/// @cond hidden
#undef PS_CNTX
#undef S_CONFIGURATION_TL
#undef S_CONFIGURATION_CL
/// @endcond
return i4Status;
}
/**
* @}
*/
#endif /*MODULE_ENABLE_DTLS_MUTUAL_AUTH*/