/*============================================================================
 * OpcUa_P_OpenSSL_AES_CBC_Encrypt
 *===========================================================================*/
OpcUa_StatusCode OpcUa_P_OpenSSL_AES_CBC_Encrypt(
    OpcUa_CryptoProvider*   a_pProvider,
    OpcUa_Byte*             a_pPlainText,
    OpcUa_UInt32            a_plainTextLen,
    OpcUa_Key*              a_key,
    OpcUa_Byte*             a_pInitalVector,
    OpcUa_Byte*             a_pCipherText,
    OpcUa_UInt32*           a_pCipherTextLen)
{
    AES_KEY         key;

    OpcUa_Byte      pInitalVector[16];

    OpcUa_InitializeStatus(OpcUa_Module_P_OpenSSL, "AES_CBC_Encrypt");

    OpcUa_ReferenceParameter(a_pProvider);

    OpcUa_ReturnErrorIfArgumentNull(a_pPlainText);
    OpcUa_ReturnErrorIfArgumentNull(a_key);
    OpcUa_ReturnErrorIfArgumentNull(a_key->Key.Data);
    OpcUa_ReturnErrorIfArgumentNull(a_pInitalVector);
    OpcUa_ReturnErrorIfArgumentNull(a_pCipherTextLen);

    if(a_plainTextLen % 16 != 0)
    {
        uStatus = OpcUa_BadInvalidArgument;
        OpcUa_GotoErrorIfBad(uStatus);
    }

    *a_pCipherTextLen = a_plainTextLen;

    /* if just the output length is needed for the caller of this function */
    if(a_pCipherText == OpcUa_Null)
    {
        OpcUa_ReturnStatusCode;
    }

    /* we have to pass the key length in bits instead of bytes */
    if(AES_set_encrypt_key(a_key->Key.Data, a_key->Key.Length * 8, &key) < 0)
    {
        uStatus = OpcUa_Bad;
        OpcUa_GotoErrorIfBad(uStatus);
    }

    /* copy the IV because the AES_cbc_encrypt function overwrites it. */
    OpcUa_P_Memory_MemCpy(pInitalVector, 16, a_pInitalVector, 16);

    /* encrypt data */
    AES_cbc_encrypt(    a_pPlainText,
                        a_pCipherText,
                        a_plainTextLen,
                        &key,
                        pInitalVector,
                        AES_ENCRYPT);

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;

OpcUa_FinishErrorHandling;
}
static
OpcUa_StatusCode OpcUa_UadpPayloadHeader_WriteBinary(OpcUa_UadpNetworkMessage* a_pValue, OpcUa_Byte **a_pBuffer, OpcUa_UInt32 *a_pSize)
{
    OpcUa_Int32 ii;

    OpcUa_InitializeStatus(OpcUa_Module_PubSub, "OpcUa_UadpPayloadHeader_WriteBinary");

    OpcUa_ReturnErrorIfArgumentNull(a_pValue);
    OpcUa_ReturnErrorIfArgumentNull(a_pBuffer);
    OpcUa_ReturnErrorIfArgumentNull(*a_pBuffer);
    OpcUa_ReturnErrorIfArgumentNull(a_pSize);

    if(a_pValue->NetworkMessageHeader.NetworkMessageFlags & OpcUa_UadpNetworkMessageFlags_Chunk)
    {
        OpcUa_UadpEncode_Direct(UInt16, &a_pValue->Payload[0].Header.DataSetWriterId);
    }
    else
    {
        OpcUa_UadpEncode_Direct(Byte, &a_pValue->MessageCount);
        for(ii = 0; ii < a_pValue->MessageCount; ii++)
        {
            OpcUa_UadpEncode_Direct(UInt16, &a_pValue->Payload[ii].Header.DataSetWriterId);
        }
    }

    OpcUa_ReturnStatusCode;
    OpcUa_BeginErrorHandling;
    /* nothing to do */
    OpcUa_FinishErrorHandling;
}
/*============================================================================
 * OpcUa_EnumeratedType_FindValue
 *===========================================================================*/
OPCUA_EXPORT OpcUa_StatusCode OpcUa_EnumeratedType_FindValue(
    OpcUa_EnumeratedType* a_pType,
    OpcUa_StringA         a_sName,
    OpcUa_Int32*          a_pValue)
{
    OpcUa_UInt32 ii = 0;

    OpcUa_InitializeStatus(OpcUa_Module_Serializer, "EnumeratedType_FindValue");

    OpcUa_ReturnErrorIfArgumentNull(a_pType);
    OpcUa_ReturnErrorIfArgumentNull(a_sName);
    OpcUa_ReturnErrorIfArgumentNull(a_pValue);

    *a_pValue = 0;

    for (ii = 0; a_pType->Values[ii].Name != OpcUa_Null; ii++)
    {
        if (OpcUa_StrCmpA(a_pType->Values[ii].Name, a_sName) == 0)
        {
            *a_pValue = a_pType->Values[ii].Value;
            break;
        }
    }

    OpcUa_GotoErrorIfTrue(a_pType->Values[ii].Name == OpcUa_Null, OpcUa_BadInvalidArgument);

    OpcUa_ReturnStatusCode;
    OpcUa_BeginErrorHandling;

    /* nothing to do */

    OpcUa_FinishErrorHandling;
}
/*============================================================================
 * Bind to Socket
 *===========================================================================*/
OpcUa_StatusCode OpcUa_P_RawSocket_Bind(    OpcUa_RawSocket a_RawSocket,
                                            OpcUa_Int16     a_nPort)
{
    OpcUa_Int32         intSize    = 0;
    SOCKET              winSocket  = (SOCKET)OPCUA_P_SOCKET_INVALID;
    struct sockaddr_in  srv;
    struct sockaddr     *pName;

OpcUa_InitializeStatus(OpcUa_Module_Socket, "P_Bind");

    OpcUa_GotoErrorIfArgumentNull(a_RawSocket);
    winSocket = (SOCKET)a_RawSocket;

    intSize = sizeof(struct sockaddr_in);
    OpcUa_MemSet(&srv, 0, intSize);

    srv.sin_addr.s_addr = INADDR_ANY;
    srv.sin_port        = htons(a_nPort);
    srv.sin_family      = AF_INET;
    pName               = (struct sockaddr*)&srv;

    if(bind(winSocket, pName, intSize) == OPCUA_P_SOCKET_SOCKETERROR)
    {
        uStatus = OpcUa_BadCommunicationError;
        goto Error;
    }

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
static
OpcUa_StatusCode OpcUa_UadpGroupHeader_WriteBinary(OpcUa_UadpGroupHeader* a_pValue, OpcUa_Byte **a_pBuffer, OpcUa_UInt32 *a_pSize)
{
    OpcUa_InitializeStatus(OpcUa_Module_PubSub, "OpcUa_UadpGroupHeader_WriteBinary");

    OpcUa_ReturnErrorIfArgumentNull(a_pValue);
    OpcUa_ReturnErrorIfArgumentNull(a_pBuffer);
    OpcUa_ReturnErrorIfArgumentNull(*a_pBuffer);
    OpcUa_ReturnErrorIfArgumentNull(a_pSize);

    OpcUa_UadpEncode_Direct(Byte, &a_pValue->GroupFlags);

    if(a_pValue->GroupFlags & OpcUa_UadpGroupHeader_EncodingBit_WriterGroupId)
    {
        OpcUa_UadpEncode_Direct(UInt16, &a_pValue->WriterGroupId);
    }
    if(a_pValue->GroupFlags & OpcUa_UadpGroupHeader_EncodingBit_GroupVersion)
    {
        OpcUa_UadpEncode_Direct(UInt32, &a_pValue->GroupVersion);
    }
    if(a_pValue->GroupFlags & OpcUa_UadpGroupHeader_EncodingBit_NetworkMessageNumber)
    {
        OpcUa_UadpEncode_Direct(UInt16, &a_pValue->NetworkMessageNumber);
    }
    if(a_pValue->GroupFlags & OpcUa_UadpGroupHeader_EncodingBit_SequenceNumber)
    {
        OpcUa_UadpEncode_Direct(UInt16, &a_pValue->SequenceNumber);
    }
    a_pValue->SequenceNumber++;

    OpcUa_ReturnStatusCode;
    OpcUa_BeginErrorHandling;
    /* nothing to do */
    OpcUa_FinishErrorHandling;
}
/*============================================================================
 * OpcUa_TcpSecureChannel_CheckSequenceNumber
 *===========================================================================*/
OpcUa_StatusCode OpcUa_TcpSecureChannel_CheckSequenceNumber(OpcUa_SecureChannel* a_pSecureChannel,
                                                            OpcUa_UInt32         a_uSequenceNumber)
{
OpcUa_InitializeStatus(OpcUa_Module_SecureChannel, "CheckSequenceNumber");

    OpcUa_ReturnErrorIfArgumentNull(a_pSecureChannel);

    OPCUA_SECURECHANNEL_LOCK(a_pSecureChannel);

    /* The SequenceNumber shall also monotonically increase for all Messages
       and shall not wrap around until it is greater than 4 294 966 271
       (UInt32.MaxValue - 1 024). The first number after the wrap around
       shall be less than 1 024. */
    if(!((a_uSequenceNumber == a_pSecureChannel->uLastSequenceNumberRcvd + 1) ||
         ((a_uSequenceNumber < 1024) &&
          (a_pSecureChannel->uLastSequenceNumberRcvd > 4294966271u))))
    {
        OpcUa_GotoErrorWithStatus(OpcUa_BadSequenceNumberInvalid);
    }

    a_pSecureChannel->uLastSequenceNumberRcvd = a_uSequenceNumber;

    OPCUA_SECURECHANNEL_UNLOCK(a_pSecureChannel);

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;

    OPCUA_SECURECHANNEL_UNLOCK(a_pSecureChannel);

OpcUa_FinishErrorHandling;
}
/*
 * ToDo: problems with RSA_PKCS1_OAEP_PADDING -> RSA_PKCS1_PSS_PADDING is
 * needed (Version 0.9.9); RSA_PKCS1_OAEP_PADDING is just for encryption
 */
OpcUa_StatusCode OpcUa_P_OpenSSL_RSA_Private_Sign(
    OpcUa_CryptoProvider* a_pProvider,
    OpcUa_ByteString      a_data,
    OpcUa_Key*            a_privateKey,
    OpcUa_Int16           a_padding,          /* e.g. RSA_PKCS1_PADDING */
    OpcUa_ByteString*     a_pSignature)       /* output length >= key length */
{
    EVP_PKEY*               pSSLPrivateKey  = OpcUa_Null;
    const unsigned char*    pData           = OpcUa_Null;
    int                     iErr            = 0;

OpcUa_InitializeStatus(OpcUa_Module_P_OpenSSL, "RSA_Private_Sign");

    /* unused parameters */
    OpcUa_ReferenceParameter(a_pProvider);
    OpcUa_ReferenceParameter(a_padding);

    /* check parameters */
    OpcUa_ReturnErrorIfArgumentNull(a_privateKey);
    OpcUa_ReturnErrorIfArgumentNull(a_pSignature);
    pData = a_privateKey->Key.Data;
    OpcUa_ReturnErrorIfArgumentNull(pData);
    OpcUa_ReturnErrorIfTrue((a_privateKey->Type != OpcUa_Crypto_KeyType_Rsa_Private), OpcUa_BadInvalidArgument);

    /* convert private key and check key length against buffer length */
    pSSLPrivateKey = d2i_PrivateKey(EVP_PKEY_RSA, OpcUa_Null, &pData, a_privateKey->Key.Length);
    OpcUa_GotoErrorIfTrue((pSSLPrivateKey == OpcUa_Null), OpcUa_BadUnexpectedError);
    OpcUa_GotoErrorIfTrue((a_pSignature->Length < RSA_size(pSSLPrivateKey->pkey.rsa)), OpcUa_BadInvalidArgument);

    /* sign data */
    iErr = RSA_sign(NID_sha1, a_data.Data, a_data.Length, a_pSignature->Data, (unsigned int*)&a_pSignature->Length, pSSLPrivateKey->pkey.rsa);
    OpcUa_GotoErrorIfTrue((iErr != 1), OpcUa_BadUnexpectedError);

    /* free internal key representation */
    EVP_PKEY_free(pSSLPrivateKey);

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;

    if(OpcUa_IsEqual(OpcUa_BadUnexpectedError))
    {
        long    lErr    = ERR_get_error();
        char*   szErr   = ERR_error_string(lErr, 0);

        if(szErr != OpcUa_Null)
        {
            OpcUa_P_Trace("*** RSA_Private_Sign: ");
            OpcUa_P_Trace(szErr);
            OpcUa_P_Trace(" ***\n");
        }
    }

    if(pSSLPrivateKey != OpcUa_Null)
    {
        EVP_PKEY_free(pSSLPrivateKey);
    }

OpcUa_FinishErrorHandling;
}
/*============================================================================
 * Create a UDP sender socket
 *===========================================================================*/
OpcUa_StatusCode OPCUA_DLLCALL OpcUa_P_SocketUdp_CreateSender(  OpcUa_StringA    a_LocalIpAddress,
                                                                OpcUa_StringA    a_RemoteIpAddress,
                                                                OpcUa_Int16      a_RemotePort,
                                                                OpcUa_Byte       a_TimeToLive,
                                                                OpcUa_Socket*    a_pSocket)
{
    OpcUa_InternalUdpSocket* pInternalSocket = OpcUa_Null;

OpcUa_InitializeStatus(OpcUa_Module_Socket, "CreateUdpSender");

    OpcUa_ReturnErrorIfArgumentNull(a_pSocket);

    *a_pSocket = OpcUa_Null;

    OpcUa_GotoErrorIfArgumentNull(a_RemoteIpAddress);

    if(a_RemotePort == 0)
    {
        OpcUa_GotoErrorWithStatus(OpcUa_BadInvalidArgument);
    }

    pInternalSocket = (OpcUa_InternalUdpSocket*)OpcUa_P_Memory_Alloc(sizeof(OpcUa_InternalUdpSocket));
    OpcUa_GotoErrorIfAllocFailed(pInternalSocket);

    pInternalSocket->pSocketServiceTable      = &OpcUa_UdpSocketServiceTable;

    if(strchr(a_RemoteIpAddress, ':'))
    {
        uStatus = OpcUa_P_RawSocket_CreateUdpV6(&pInternalSocket->rawSocket,
                                                OpcUa_True,
                                                a_LocalIpAddress,
                                                a_RemoteIpAddress,
                                                a_RemotePort,
                                                a_TimeToLive);
        OpcUa_GotoErrorIfBad(uStatus);
    }
    else
    {
        uStatus = OpcUa_P_RawSocket_CreateUdp(&pInternalSocket->rawSocket,
                                              OpcUa_True,
                                              a_LocalIpAddress,
                                              a_RemoteIpAddress,
                                              a_RemotePort,
                                              a_TimeToLive);
        OpcUa_GotoErrorIfBad(uStatus);
    }

    *a_pSocket = pInternalSocket;

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;

    if(pInternalSocket != OpcUa_Null)
    {
        OpcUa_P_Memory_Free(pInternalSocket);
    }

OpcUa_FinishErrorHandling;
}
/*============================================================================
 * OpcUa_P_OpenSSL_X509_GetSignature
 *===========================================================================*/
OpcUa_StatusCode OpcUa_P_OpenSSL_X509_GetSignature(
    OpcUa_CryptoProvider*       a_pProvider,
    OpcUa_ByteString*           a_pCertificate,
    OpcUa_Signature*            a_pSignature)
{
    X509*                   pX509Certificate    = OpcUa_Null;
    const unsigned char*    pTemp               = OpcUa_Null;

OpcUa_InitializeStatus(OpcUa_Module_P_OpenSSL, "X509_GetSignature");

    OpcUa_ReferenceParameter(a_pProvider);

    OpcUa_ReturnErrorIfArgumentNull(a_pProvider);
    OpcUa_ReturnErrorIfArgumentNull(a_pCertificate);
    OpcUa_ReturnErrorIfArgumentNull(a_pSignature);

    /* d2i_X509 modifies the given pointer -> use local replacement */
    pTemp = a_pCertificate->Data;

    d2i_X509(&pX509Certificate, &pTemp, a_pCertificate->Length);

    if(pX509Certificate == OpcUa_Null)
    {
        uStatus = OpcUa_Bad;
        OpcUa_GotoErrorIfBad(uStatus);
    }

    a_pSignature->Signature.Length = pX509Certificate->signature->length;

    a_pSignature->Algorithm = OBJ_obj2nid(pX509Certificate->sig_alg->algorithm);

    if(a_pSignature->Algorithm == NID_undef)
    {
        uStatus = OpcUa_Bad;
        OpcUa_GotoErrorIfBad(uStatus);
    }

    if(a_pSignature->Signature.Data != OpcUa_Null)
    {
        uStatus = OpcUa_P_Memory_MemCpy(a_pSignature->Signature.Data,
                                        a_pSignature->Signature.Length,
                                        pX509Certificate->signature->data,
                                        pX509Certificate->signature->length);

    }

    X509_free(pX509Certificate);

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;

    if(pX509Certificate != OpcUa_Null)
    {
        X509_free(pX509Certificate);
    }

OpcUa_FinishErrorHandling;
}
/*============================================================================
 * OpcUa_P_OpenSSL_RSA_GenerateKeys
 *===========================================================================*/
OpcUa_StatusCode OpcUa_P_OpenSSL_RSA_GenerateKeys(
    OpcUa_CryptoProvider*   a_pProvider,
    OpcUa_UInt32            a_bits,
    OpcUa_Key*              a_pPublicKey,
    OpcUa_Key*              a_pPrivateKey)
{
    RSA*            pRsa;
    unsigned char*  pData;

    OpcUa_InitializeStatus(OpcUa_Module_P_OpenSSL, "RSA_GenerateKeys");

    OpcUa_ReturnErrorIfArgumentNull(a_pProvider);
    OpcUa_ReturnErrorIfArgumentNull(a_pPublicKey);
    OpcUa_ReturnErrorIfArgumentNull(a_pPrivateKey);

    OpcUa_ReferenceParameter(a_pProvider);

    /* Just 1024 or 2048 bits should be allowed for compatibility reasons */
    if ((a_bits != 1024) && (a_bits != 2048) && (a_bits != 3072) && (a_bits != 4096))
    {
        uStatus = OpcUa_BadInvalidArgument;
        OpcUa_GotoErrorIfBad(uStatus);
    }

    if(a_pPublicKey->Key.Data == OpcUa_Null)
    {
       a_pPublicKey->Key.Length = a_bits;
       OpcUa_ReturnStatusCode;
    }

    if(a_pPrivateKey->Key.Data == OpcUa_Null)
    {
       a_pPrivateKey->Key.Length = a_bits;
       OpcUa_ReturnStatusCode;
    }

    pRsa = RSA_generate_key(a_bits, RSA_F4, NULL, OpcUa_Null);

    pData = a_pPublicKey->Key.Data;
    a_pPublicKey->Key.Length = i2d_RSAPublicKey(pRsa, &pData);

    pData = a_pPrivateKey->Key.Data;
    a_pPrivateKey->Key.Length = i2d_RSAPrivateKey(pRsa, &pData);

    /* clean up */
    if(pRsa != OpcUa_Null)
    {
       RSA_free(pRsa);
    }

    a_pPublicKey->Type = OpcUa_Crypto_Rsa_Alg_Id;
    a_pPrivateKey->Type = OpcUa_Crypto_Rsa_Alg_Id;

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;

OpcUa_FinishErrorHandling;
}
/*
ToDo: problems with RSA_PKCS1_OAEP_PADDING -> find solution
*/
OpcUa_StatusCode OpcUa_P_OpenSSL_RSA_Public_Verify(
    OpcUa_CryptoProvider* a_pProvider,
    OpcUa_ByteString      a_data,
    OpcUa_Key*            a_publicKey,
    OpcUa_Int16           a_padding,
    OpcUa_ByteString*     a_pSignature)
{
    EVP_PKEY*       pPublicKey      = OpcUa_Null;
    OpcUa_Int32     keySize         = 0;
    const unsigned char *pData;

    OpcUa_InitializeStatus(OpcUa_Module_P_OpenSSL, "RSA_Public_Verify");

    OpcUa_ReferenceParameter(a_pProvider);
    OpcUa_ReferenceParameter(a_padding);

    OpcUa_ReturnErrorIfArgumentNull(a_data.Data);
    OpcUa_ReturnErrorIfArgumentNull(a_publicKey);
    OpcUa_ReturnErrorIfArgumentNull(a_publicKey->Key.Data);
    OpcUa_ReturnErrorIfArgumentNull(a_pSignature);

    if(a_publicKey->Type != OpcUa_Crypto_KeyType_Rsa_Public)
    {
        OpcUa_GotoErrorWithStatus(OpcUa_BadInvalidArgument);
    }

    pData = a_publicKey->Key.Data;
    pPublicKey = d2i_PublicKey(EVP_PKEY_RSA,OpcUa_Null, &pData, a_publicKey->Key.Length);

    if(pPublicKey == OpcUa_Null)
    {
        OpcUa_GotoErrorWithStatus(OpcUa_BadInvalidArgument);
    }

    keySize = RSA_size(pPublicKey->pkey.rsa);

    if((a_pSignature->Length%keySize) != 0)
    {
        OpcUa_GotoErrorWithStatus(OpcUa_Bad);
    }

    if (RSA_verify(NID_sha1, a_data.Data, a_data.Length, a_pSignature->Data, a_pSignature->Length, pPublicKey->pkey.rsa) != 1)
    {
        OpcUa_GotoErrorWithStatus(OpcUa_Bad);
    }

    EVP_PKEY_free(pPublicKey);

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;

    if (pPublicKey != OpcUa_Null)
    {
        EVP_PKEY_free(pPublicKey);
    }

OpcUa_FinishErrorHandling;
}
/*============================================================================
 * Connect Socket for Client.
 *===========================================================================*/
OpcUa_StatusCode OpcUa_P_RawSocket_Connect( OpcUa_RawSocket a_RawSocket,
                                            OpcUa_Int16     a_nPort,
                                            OpcUa_StringA   a_sHost)
{
    int                 intSize   = 0;
    SOCKET              winSocket = (SOCKET)OPCUA_P_SOCKET_INVALID;
    struct sockaddr     *pName;
    struct sockaddr_in  srv;
    char*               localhost = "127.0.0.1";

OpcUa_InitializeStatus(OpcUa_Module_Socket, "P_Connect");

    OpcUa_GotoErrorIfArgumentNull(a_RawSocket);
    winSocket = (SOCKET)a_RawSocket;

    intSize = sizeof(struct sockaddr_in);
    OpcUa_MemSet(&srv, 0, intSize);

    if(!strcmp("localhost", a_sHost))
    {
        a_sHost = localhost;
    }

    srv.sin_addr.s_addr = inet_addr(a_sHost);

    if(srv.sin_addr.s_addr == INADDR_NONE)
    {
        return OpcUa_BadInvalidArgument;
    }

    srv.sin_port   = htons(a_nPort);
    srv.sin_family = AF_INET;

    pName = (struct sockaddr *) &srv;

    if(connect(winSocket, pName, intSize) == OPCUA_P_SOCKET_SOCKETERROR)
    {
        int result = OpcUa_P_RawSocket_GetLastError((OpcUa_RawSocket)winSocket);

        /* a connect takes some time and this "error" is common with nonblocking sockets */
        if(result == WSAEWOULDBLOCK || result == WSAEINPROGRESS)
        {
            uStatus = OpcUa_BadWouldBlock;
        }
        else
        {
            uStatus = OpcUa_BadCommunicationError;
        }
        goto Error;
    }

    uStatus = OpcUa_Good;

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
/*============================================================================
 * OpcUa_P_OpenSSL_Random_Key_Generate
 *===========================================================================*/
OpcUa_StatusCode OpcUa_P_OpenSSL_Random_Key_Generate(
    OpcUa_CryptoProvider* a_pProvider,
    OpcUa_Int32           a_keyLen,
    OpcUa_Key*            a_pKey)
{
    OpcUa_CryptoProviderConfig* pConfig = OpcUa_Null;
    OpcUa_Int32                 keyLen  = 0;

    OpcUa_InitializeStatus(OpcUa_Module_P_OpenSSL, "Random_Key_Generate");

    OpcUa_ReturnErrorIfArgumentNull(a_pProvider);
    OpcUa_ReturnErrorIfArgumentNull(a_pKey);

    OpcUa_ReferenceParameter(a_pProvider);

    keyLen = a_keyLen;

    if(keyLen < 0)
    {
        if(a_pProvider->Handle != OpcUa_Null)
        {
            /* get default configuration */
            pConfig = (OpcUa_CryptoProviderConfig*)a_pProvider->Handle;
            keyLen = pConfig->SymmetricKeyLength;
        }
        else
        {
            uStatus = OpcUa_BadInvalidArgument;
            OpcUa_GotoErrorIfBad(uStatus);
        }
    }
    else if(keyLen > MAX_GENERATED_OUTPUT_LEN)
    {
            uStatus = OpcUa_BadInvalidArgument;
            OpcUa_GotoErrorIfBad(uStatus);
    }

    a_pKey->Key.Length = keyLen;
    a_pKey->Type = OpcUa_Crypto_KeyType_Random;

    if(a_pKey->Key.Data == OpcUa_Null)
    {
        OpcUa_ReturnStatusCode;
    }

    if(RAND_bytes(a_pKey->Key.Data, a_pKey->Key.Length) == 0)
    {
        uStatus = OpcUa_Bad;
        OpcUa_GotoErrorIfBad(uStatus);
    }

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;


OpcUa_FinishErrorHandling;
}
/*============================================================================
 * Create a new signal socket
 *===========================================================================*/
OpcUa_StatusCode OpcUa_P_SocketManager_NewSignalSocket(OpcUa_SocketManager a_pSocketManager)
{
    OpcUa_InternalSocket*           pIntSignalSocket = OpcUa_Null;
    OpcUa_InternalSocketManager*    pInternalSocketManager      = (OpcUa_InternalSocketManager*)a_pSocketManager;

OpcUa_InitializeStatus(OpcUa_Module_Socket, "NewSignalSocket");

    OpcUa_GotoErrorIfArgumentNull(a_pSocketManager);

    pIntSignalSocket = (OpcUa_InternalSocket*)OpcUa_SocketManager_FindFreeSocket(a_pSocketManager, OpcUa_True);

    if(pIntSignalSocket == OpcUa_Null)
    {
        uStatus = OpcUa_BadResourceUnavailable;
        goto Error;
    }

    uStatus = OpcUa_P_RawSocket_CreateSocketPair( &pIntSignalSocket->rawSocket,
                                                  &pInternalSocketManager->pCookie);
    OpcUa_GotoErrorIfBad(uStatus);

    pIntSignalSocket->Flags.EventMask =   OPCUA_SOCKET_CLOSE_EVENT
                                        | OPCUA_SOCKET_READ_EVENT
                                        | OPCUA_SOCKET_EXCEPT_EVENT
                                        | OPCUA_SOCKET_TIMEOUT_EVENT;

    uStatus = OpcUa_P_RawSocket_SetBlockMode (pIntSignalSocket->rawSocket, OpcUa_False);
    if (OpcUa_IsBad(uStatus))
    {
        OpcUa_P_RawSocket_Close(pIntSignalSocket->rawSocket);
        OpcUa_P_RawSocket_Close(pInternalSocketManager->pCookie);
        OpcUa_GotoErrorWithStatus(uStatus);
    }

    uStatus = OpcUa_P_RawSocket_SetBlockMode (pInternalSocketManager->pCookie, OpcUa_False);
    if (OpcUa_IsBad(uStatus))
    {
        OpcUa_P_RawSocket_Close(pIntSignalSocket->rawSocket);
        OpcUa_P_RawSocket_Close(pInternalSocketManager->pCookie);
        OpcUa_GotoErrorWithStatus(uStatus);
    }

    OPCUA_SOCKET_SETVALID(pIntSignalSocket);

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;

    if(pIntSignalSocket)
    {
        pIntSignalSocket->rawSocket = (OpcUa_RawSocket)OPCUA_P_SOCKET_INVALID;
        OPCUA_SOCKET_INVALIDATE(pIntSignalSocket);
    }

OpcUa_FinishErrorHandling;
}
/*============================================================================
 * Clean the platform network interface up.
 *===========================================================================*/
OpcUa_StatusCode OPCUA_DLLCALL OpcUa_P_Socket_CleanupNetwork(OpcUa_Void)
{
OpcUa_InitializeStatus(OpcUa_Module_Socket, "CleanupNetwork");

    /* cleanup platform networking */
    uStatus = OpcUa_P_RawSocket_CleanupNetwork();
    OpcUa_GotoErrorIfBad(uStatus);

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
/*============================================================================
 * OpcUa_ProxyStub_AddTypes
 *===========================================================================*/
OpcUa_StatusCode OpcUa_ProxyStub_AddTypes(OpcUa_EncodeableType** a_ppTypes)
{
OpcUa_InitializeStatus(OpcUa_Module_ProxyStub, "AddTypes");

    uStatus = OpcUa_EncodeableTypeTable_AddTypes(   &OpcUa_ProxyStub_g_EncodeableTypes,
                                                    a_ppTypes);
    OpcUa_GotoErrorIfBad(uStatus);

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
/*============================================================================
 * OpcUa_P_OpenSSL_SeedPRNG
 *===========================================================================*/
OpcUa_StatusCode OPCUA_DLLCALL OpcUa_P_OpenSSL_SeedPRNG( OpcUa_Byte* seed,
                                                         OpcUa_Int   bytes)
{
    OpcUa_InitializeStatus(OpcUa_Module_P_OpenSSL, "SeedPRNG");

    RAND_seed(seed, bytes);

OpcUa_ReturnStatusCode;

OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
/*============================================================================
 * OpcUa_TcpSecureChannel_RenewSecurityToken
 *===========================================================================*/
OpcUa_StatusCode OpcUa_TcpSecureChannel_RenewSecurityToken( OpcUa_SecureChannel*         a_pSecureChannel,
                                                            OpcUa_ChannelSecurityToken*  a_pSecurityToken,
                                                            OpcUa_UInt32                 a_tokenLifeTime,
                                                            OpcUa_ChannelSecurityToken** a_ppSecurityToken)
{
    OpcUa_ChannelSecurityToken* pSecurityToken      = OpcUa_Null;

OpcUa_InitializeStatus(OpcUa_Module_SecureChannel, "RenewSecurityToken");

    /* check parameters */
    OpcUa_ReturnErrorIfArgumentNull(a_pSecureChannel);
    OpcUa_ReturnErrorIfArgumentNull(a_pSecurityToken);
    OpcUa_ReturnErrorIfArgumentNull(a_ppSecurityToken);

    OPCUA_SECURECHANNEL_LOCK(a_pSecureChannel);

    /* initialize outparameters */
    *a_ppSecurityToken = OpcUa_Null;

    /*** create token ***/
    pSecurityToken = (OpcUa_ChannelSecurityToken*)OpcUa_Alloc(sizeof(OpcUa_ChannelSecurityToken));
    OpcUa_GotoErrorIfAllocFailed(pSecurityToken);

    OpcUa_ChannelSecurityToken_Initialize(pSecurityToken);

    pSecurityToken->TokenId         = a_pSecureChannel->NextTokenId;
    pSecurityToken->ChannelId       = a_pSecurityToken->ChannelId;
    pSecurityToken->CreatedAt       = OPCUA_P_DATETIME_UTCNOW();

    OpcUa_Trace(OPCUA_TRACE_LEVEL_INFO, "OpcUa_TcpSecureChannel_RenewSecurityToken: TOKEN ID is %u-%u\n", pSecurityToken->ChannelId, pSecurityToken->TokenId);

    OpcUa_TcpSecureChannel_ReviseLifetime(a_tokenLifeTime, &pSecurityToken->RevisedLifetime);

    /* increment renew counter */
    a_pSecureChannel->NextTokenId++;

    OPCUA_SECURECHANNEL_UNLOCK(a_pSecureChannel);

    /* assign outparameter */
    *a_ppSecurityToken  = pSecurityToken;

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;

    OPCUA_SECURECHANNEL_UNLOCK(a_pSecureChannel);

    if(pSecurityToken != OpcUa_Null)
    {
        OpcUa_Free(pSecurityToken);
    }

OpcUa_FinishErrorHandling;
}
/*============================================================================
 * Signal a certain event on a socket.
 *===========================================================================*/
OpcUa_StatusCode OPCUA_DLLCALL OpcUa_P_SocketManager_SignalEvent(   OpcUa_SocketManager     a_pSocketManager,
                                                                    OpcUa_UInt32            a_uEvent,
                                                                    OpcUa_Boolean           a_bAllManagers)
{
OpcUa_InitializeStatus(OpcUa_Module_Socket, "SignalEvent");

    uStatus = OpcUa_P_SocketManager_InterruptLoop(a_pSocketManager, a_uEvent, a_bAllManagers);

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
/*============================================================================
 * OpcUa_TcpSecureChannel_UnlockWriteMutex
 *===========================================================================*/
OpcUa_StatusCode OpcUa_TcpSecureChannel_UnlockWriteMutex(OpcUa_SecureChannel* a_pSecureChannel)
{
OpcUa_InitializeStatus(OpcUa_Module_SecureChannel, "UnlockWriteMutex");

    /* check parameters */
    OpcUa_ReturnErrorIfArgumentNull(a_pSecureChannel);

    OPCUA_SECURECHANNEL_UNLOCK_WRITE(a_pSecureChannel);

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
/*============================================================================
 * Set socket user data
 *===========================================================================*/
static OpcUa_StatusCode OpcUa_P_SocketService_UdpSetUserData(OpcUa_Socket a_pSocket,
                                                             OpcUa_Void*  a_pvUserData)
{
OpcUa_InitializeStatus(OpcUa_Module_Socket, "UdpSetUserData");

    OpcUa_GotoErrorIfArgumentNull(a_pSocket);
    OpcUa_ReferenceParameter(a_pvUserData);

    OpcUa_GotoErrorWithStatus(OpcUa_BadNotImplemented);

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
/*============================================================================
 * OpcUa_P_OpenSSL_X509_AddCustomExtension
 *===========================================================================*/
OpcUa_StatusCode OpcUa_P_OpenSSL_X509_AddCustomExtension(
    X509**                   a_ppCertificate,
    OpcUa_Crypto_Extension*  a_pExtension,
    X509V3_CTX*              a_pX509V3Context)
{
    X509_EXTENSION*     pExtension   = OpcUa_Null;
    char*               pName        = OpcUa_Null;
    char*               pValue       = OpcUa_Null;

OpcUa_InitializeStatus(OpcUa_Module_P_OpenSSL, "X509_AddCustomExtension");

    OpcUa_ReturnErrorIfArgumentNull(a_pX509V3Context);
    OpcUa_ReturnErrorIfArgumentNull(a_pExtension->key);
    OpcUa_ReturnErrorIfArgumentNull(a_pExtension->value);

    pName = (char*)a_pExtension->key;
    pValue = (char*)a_pExtension->value;

    /* create the extension. */
    pExtension = X509V3_EXT_conf(
        OpcUa_Null,
        a_pX509V3Context,
        pName,
        pValue);

    if(pExtension == OpcUa_Null)
    {
        OpcUa_GotoErrorWithStatus(OpcUa_Bad);
    }

    /* add it to the certificate. */
    if(!X509_add_ext(*a_ppCertificate, pExtension, -1))
    {
        OpcUa_GotoErrorWithStatus(OpcUa_Bad);
    }

    /* free the extension. */
    X509_EXTENSION_free(pExtension);

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;

    if(pExtension != OpcUa_Null)
    {
        X509_EXTENSION_free(pExtension);
    }

OpcUa_FinishErrorHandling;
}
/*============================================================================
 * OpcUa_TcpSecureChannel_Delete
 *===========================================================================*/
OpcUa_StatusCode OpcUa_TcpSecureChannel_Delete(OpcUa_SecureChannel**   a_ppSecureChannel)
{
OpcUa_InitializeStatus(OpcUa_Module_SecureChannel, "Delete");

    if (a_ppSecureChannel != OpcUa_Null && *a_ppSecureChannel != OpcUa_Null)
    {
        OpcUa_TcpSecureChannel_Clear(*a_ppSecureChannel);
        OpcUa_Free(*a_ppSecureChannel);
        *a_ppSecureChannel = OpcUa_Null;
    }

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
/*============================================================================
 * Close UDP Socket.
 *===========================================================================*/
static OpcUa_StatusCode OpcUa_P_SocketService_UdpClose(OpcUa_Socket a_pSocket)
{
    OpcUa_InternalUdpSocket* pInternalSocket = (OpcUa_InternalUdpSocket*)a_pSocket;

OpcUa_InitializeStatus(OpcUa_Module_Socket, "UdpClose");

    OpcUa_GotoErrorIfArgumentNull(a_pSocket);

    OpcUa_P_RawSocket_Close(pInternalSocket->rawSocket);
    OpcUa_P_Memory_Free(pInternalSocket);

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
/*============================================================================
 * Get address information about the peer
 *===========================================================================*/
OpcUa_StatusCode OpcUa_P_RawSocket_GetPeerInfo( OpcUa_Socket    a_RawSocket,
                                                OpcUa_CharA*    a_achPeerInfoBuffer,
                                                OpcUa_UInt32    a_uiPeerInfoBufferSize)
{
    int                 apiResult       = 0;
    struct sockaddr_in  sockAddrIn;
    size_t              TempLen         = sizeof(struct sockaddr_in);
    SOCKET              winSocket       = (SOCKET)OPCUA_P_SOCKET_INVALID;
    char*               pchAddrBuf      = OpcUa_Null;
    OpcUa_UInt16        usPort          = 0;

OpcUa_InitializeStatus(OpcUa_Module_Socket, "GetPeerInfo");

    /* initial parameter check */
    OpcUa_ReturnErrorIfTrue((a_RawSocket == (OpcUa_RawSocket)OPCUA_P_SOCKET_INVALID), OpcUa_BadInvalidArgument);
    OpcUa_ReturnErrorIfTrue((a_uiPeerInfoBufferSize < OPCUA_P_PEERINFO_MIN_SIZE), OpcUa_BadInvalidArgument);
    OpcUa_ReturnErrorIfArgumentNull(a_achPeerInfoBuffer);

    winSocket = (SOCKET)a_RawSocket;
    apiResult = getpeername(winSocket, (struct sockaddr*)&sockAddrIn, (int*)&TempLen);

    OpcUa_ReturnErrorIfTrue((apiResult != 0), OpcUa_BadInternalError);

    /* IP */
    pchAddrBuf = inet_ntoa(sockAddrIn.sin_addr);
    OpcUa_GotoErrorIfTrue(pchAddrBuf == OpcUa_Null, OpcUa_BadInternalError);

    /* Port */
    usPort = OpcUa_P_RawSocket_NToHS((OpcUa_UInt16)sockAddrIn.sin_port);

    /* build result string */
    TempLen = strlen(pchAddrBuf);

#if OPCUA_USE_SAFE_FUNCTIONS
    OpcUa_GotoErrorIfTrue((strncpy_s(a_achPeerInfoBuffer, a_uiPeerInfoBufferSize + 1, pchAddrBuf, TempLen) != 0), OpcUa_Bad);
    a_achPeerInfoBuffer[TempLen] = ':';
    TempLen++;
    sprintf_s(&a_achPeerInfoBuffer[TempLen], a_uiPeerInfoBufferSize - TempLen, "%u", usPort);
#else /* OPCUA_USE_SAFE_FUNCTIONS */
    OpcUa_GotoErrorIfTrue((strncpy(a_achPeerInfoBuffer, pchAddrBuf, TempLen) != a_achPeerInfoBuffer), OpcUa_Bad);
    a_achPeerInfoBuffer[TempLen] = ':';
    sprintf(&a_achPeerInfoBuffer[TempLen + 1], "%u", usPort);
#endif /* OPCUA_USE_SAFE_FUNCTIONS */

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
/*============================================================================
 * Select usable socket. (maxfds ignored in win32)
 *===========================================================================*/
OpcUa_StatusCode OpcUa_P_RawSocket_Select(  OpcUa_RawSocket         a_MaxFds,
                                            OpcUa_P_Socket_Array*   a_pFdSetRead,
                                            OpcUa_P_Socket_Array*   a_pFdSetWrite,
                                            OpcUa_P_Socket_Array*   a_pFdSetException,
                                            OpcUa_TimeVal*          a_pTimeout)
{
    int                 apiResult  = 0;
    struct timeval      timeout;

OpcUa_InitializeStatus(OpcUa_Module_Socket, "P_Select");

    OpcUa_ReferenceParameter(a_MaxFds);

    OpcUa_GotoErrorIfArgumentNull(a_pFdSetRead);
    OpcUa_GotoErrorIfArgumentNull(a_pFdSetWrite);
    OpcUa_GotoErrorIfArgumentNull(a_pFdSetException);

    timeout.tv_sec  = a_pTimeout->uintSeconds;
    timeout.tv_usec = a_pTimeout->uintMicroSeconds;

    apiResult = select( 0,
                        (fd_set*)a_pFdSetRead,
                        (fd_set*)a_pFdSetWrite,
                        (fd_set*)a_pFdSetException,
                        &timeout);

    if(apiResult == OPCUA_P_SOCKET_SOCKETERROR)
    {
        apiResult = WSAGetLastError();

        switch(apiResult)
        {
        case WSAENOTSOCK:
            {
                uStatus = OpcUa_BadInvalidArgument;
                break;
            }
        default:
            {
                uStatus = OpcUa_BadCommunicationError;
            }
        }
    }

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
/*============================================================================
 * OpcUa_TcpSecureChannel_Close
 *===========================================================================*/
OpcUa_StatusCode OpcUa_TcpSecureChannel_Close(OpcUa_SecureChannel*   a_pSecureChannel)
{
    OpcUa_InitializeStatus(OpcUa_Module_SecureChannel, "Close");

    OpcUa_ReturnErrorIfArgumentNull(a_pSecureChannel);

    OPCUA_SECURECHANNEL_LOCK(a_pSecureChannel);

    a_pSecureChannel->State = OpcUa_SecureChannelState_Closed;

    OPCUA_SECURECHANNEL_UNLOCK(a_pSecureChannel);

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
/*============================================================================
 * OpcUa_ProxyStub_SetNamespaceUris
 *===========================================================================*/
OpcUa_StatusCode OpcUa_ProxyStub_SetNamespaceUris(OpcUa_StringA* a_psNamespaceUris)
{
OpcUa_InitializeStatus(OpcUa_Module_ProxyStub, "SetNamespaceUris");

    /* discard existing strings */
    OpcUa_StringTable_Clear(&OpcUa_ProxyStub_g_NamespaceUris);

    /* update table */
    uStatus = OpcUa_StringTable_AddStringList(  &OpcUa_ProxyStub_g_NamespaceUris,
                                                a_psNamespaceUris);
    OpcUa_GotoErrorIfBad(uStatus);

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
/*============================================================================
 * Get IP Address and Port Number of the Peer
 *===========================================================================*/
static OpcUa_StatusCode OpcUa_P_SocketService_UdpGetPeerInfo(  OpcUa_Socket  a_pSocket,
                                                               OpcUa_CharA*  a_achPeerInfoBuffer,
                                                               OpcUa_UInt32  a_uiPeerInfoBufferSize)
{
OpcUa_InitializeStatus(OpcUa_Module_Socket, "UdpGetPeerInfo");

    OpcUa_GotoErrorIfArgumentNull(a_pSocket);
    OpcUa_GotoErrorIfArgumentNull(a_achPeerInfoBuffer);
    OpcUa_ReferenceParameter(a_uiPeerInfoBufferSize);

    OpcUa_GotoErrorWithStatus(OpcUa_BadNotImplemented);

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
/*============================================================================
 * OpcUa_TcpSecureChannel_ReleaseSecuritySet
 *===========================================================================*/
OpcUa_StatusCode OpcUa_TcpSecureChannel_ReleaseSecuritySet( OpcUa_SecureChannel*    a_pSecureChannel,
                                                            OpcUa_UInt32            a_uTokenId)
{
OpcUa_InitializeStatus(OpcUa_Module_SecureChannel, "ReleaseSecuritySet");

    OpcUa_ReturnErrorIfArgumentNull(a_pSecureChannel);

    OpcUa_ReferenceParameter(a_uTokenId);

    OpcUa_Trace(OPCUA_TRACE_LEVEL_DEBUG, "ReleaseSecurityKeyset: Keyset for token %u released.\n", a_uTokenId);

    OPCUA_SECURECHANNEL_UNLOCK(a_pSecureChannel);

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}