Webserver+3d print

Dependents:   Nucleo

cyclone_ssl/tls.c

Committer:
Sergunb
Date:
2017-02-04
Revision:
0:8918a71cdbe9

File content as of revision 0:8918a71cdbe9:

/**
 * @file tls.c
 * @brief TLS (Transport Layer Security)
 *
 * @section License
 *
 * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
 *
 * This file is part of CycloneSSL Open.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * @section Description
 *
 * The TLS protocol provides communications security over the Internet. The
 * protocol allows client/server applications to communicate in a way that
 * is designed to prevent eavesdropping, tampering, or message forgery
 *
 * @author Oryx Embedded SARL (www.oryx-embedded.com)
 * @version 1.7.6
 **/

//Switch to the appropriate trace level
#define TRACE_LEVEL TLS_TRACE_LEVEL

//Dependencies
#include <string.h>
#include <ctype.h>
#include "tls.h"
#include "tls_client.h"
#include "tls_server.h"
#include "tls_common.h"
#include "tls_record.h"
#include "tls_misc.h"
#include "x509.h"
#include "pem.h"
#include "debug.h"

//Check SSL library configuration
#if (TLS_SUPPORT == ENABLED)


/**
 * @brief TLS context initialization
 * @return Handle referencing the fully initialized TLS context
 **/

TlsContext *tlsInit(void)
{
   TlsContext *context;

   //Allocate a memory buffer to hold the TLS context
   context = tlsAllocMem(sizeof(TlsContext));

   //Successful memory allocation?
   if(context != NULL)
   {
      //Clear TLS context
      memset(context, 0, sizeof(TlsContext));

      //Default state
      context->state = TLS_STATE_INIT;
      //Default operation mode
      context->entity = TLS_CONNECTION_END_CLIENT;
      //Default TLS version
      context->version = TLS_MIN_VERSION;
      //Default client authentication mode
      context->clientAuthMode = TLS_CLIENT_AUTH_NONE;

#if (TLS_DH_ANON_SUPPORT == ENABLED || TLS_DHE_RSA_SUPPORT == ENABLED || \
   TLS_DHE_DSS_SUPPORT == ENABLED || TLS_DHE_PSK_SUPPORT == ENABLED)
      //Initialize Diffie-Hellman context
      dhInit(&context->dhContext);
#endif

#if (TLS_ECDH_ANON_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED || \
   TLS_ECDHE_ECDSA_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
      //Initialize ECDH context
      ecdhInit(&context->ecdhContext);
#endif

#if (TLS_RSA_SIGN_SUPPORT == ENABLED || TLS_RSA_SUPPORT == ENABLED || \
   TLS_DHE_RSA_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED)
      //Initialize peer's RSA public key
      rsaInitPublicKey(&context->peerRsaPublicKey);
#endif

#if (TLS_DSA_SIGN_SUPPORT == ENABLED || TLS_DHE_DSS_SUPPORT == ENABLED)
      //Initialize peer's DSA public key
      dsaInitPublicKey(&context->peerDsaPublicKey);
#endif

#if (TLS_ECDSA_SIGN_SUPPORT == ENABLED || TLS_ECDHE_ECDSA_SUPPORT == ENABLED)
      //Initialize peer's EC domain parameters
      ecInitDomainParameters(&context->peerEcParams);
      //Initialize peer's EC public key
      ecInit(&context->peerEcPublicKey);
#endif

      //Set the maximum fragment length for outgoing TLS records
      context->txRecordMaxLen = TLS_MAX_RECORD_LENGTH;

      //Compute the corresponding buffer size
      context->txBufferSize = TLS_MAX_RECORD_LENGTH +
         sizeof(TlsRecord) + TLS_MAX_RECORD_OVERHEAD;

      //Save the maximum fragment length for incoming TLS records
      context->rxRecordMaxLen = TLS_MAX_RECORD_LENGTH;

      //Compute the corresponding buffer size
      context->rxBufferSize = TLS_MAX_RECORD_LENGTH +
         sizeof(TlsRecord) + TLS_MAX_RECORD_OVERHEAD;
   }

   //Return a pointer to the freshly created TLS context
   return context;
}


/**
 * @brief Set send and receive callbacks (I/O abstraction layer)
 * @param[in] context Pointer to the TLS context
 * @param[in] handle Handle for I/O operations
 * @param[in] sendCallback Send callback function
 * @param[in] receiveCallback Receive callback function
 * @return Error code
 **/

error_t tlsSetIoCallbacks(TlsContext *context, TlsIoHandle handle,
   TlsIoSendCallback sendCallback, TlsIoReceiveCallback receiveCallback)
{
   //Check parameters
   if(context == NULL || sendCallback == NULL || receiveCallback == NULL)
      return ERROR_INVALID_PARAMETER;

   //Save I/O handle
   context->handle = handle;

   //Save send and receive callback functions
   context->sendCallback = sendCallback;
   context->receiveCallback = receiveCallback;

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Set operation mode (client or server)
 * @param[in] context Pointer to the TLS context
 * @param[in] entity Specifies whether this entity is considered a client or a server
 * @return Error code
 **/

error_t tlsSetConnectionEnd(TlsContext *context, TlsConnectionEnd entity)
{
   //Invalid TLS context?
   if(context == NULL)
      return ERROR_INVALID_PARAMETER;
   //Invalid parameter?
   if(entity != TLS_CONNECTION_END_CLIENT && entity != TLS_CONNECTION_END_SERVER)
      return ERROR_INVALID_PARAMETER;

   //Check whether TLS operates as a client or a server
   context->entity = entity;

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Set the pseudo-random number generator to be used
 * @param[in] context Pointer to the TLS context
 * @param[in] prngAlgo PRNG algorithm
 * @param[in] prngContext Pointer to the PRNG context
 * @return Error code
 **/

error_t tlsSetPrng(TlsContext *context, const PrngAlgo *prngAlgo, void *prngContext)
{
   //Invalid TLS context?
   if(context == NULL)
      return ERROR_INVALID_PARAMETER;
   //Invalid parameters?
   if(prngAlgo == NULL || prngContext == NULL)
      return ERROR_INVALID_PARAMETER;

   //PRNG algorithm that will be used to generate random numbers
   context->prngAlgo = prngAlgo;
   //PRNG context
   context->prngContext = prngContext;

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Set the name of the remote server
 * @param[in] context Pointer to the TLS context
 * @param[in] serverName Fully qualified domain name of the server
 * @return Error code
 **/

error_t tlsSetServerName(TlsContext *context, const char_t *serverName)
{
   size_t i;
   size_t length;

   //Invalid parameters?
   if(context == NULL || serverName == NULL)
      return ERROR_INVALID_PARAMETER;

   //Retrieve the length of the server name
   length = strlen(serverName);

   //Check whether the server name has already been configured
   if(context->serverName != NULL)
   {
      //Release memory
      tlsFreeMem(context->serverName);
      context->serverName = NULL;
   }

   //Valid server name?
   if(length > 0)
   {
      //Allocate a memory block to hold the hostname
      context->serverName = tlsAllocMem(length + 1);
      //Failed to allocate memory?
      if(context->serverName == NULL)
         return ERROR_OUT_OF_MEMORY;

      //Convert the hostname into lowercase
      for(i = 0; i < length; i++)
         context->serverName[i] = tolower((uint8_t) serverName[i]);

      //Properly terminate the string with a NULL character
      context->serverName[length] = '\0';
   }

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Set session cache
 * @param[in] context Pointer to the TLS context
 * @param[in] cache Session cache that will be used to save/resume TLS sessions
 * @return Error code
 **/

error_t tlsSetCache(TlsContext *context, TlsCache *cache)
{
   //Check parameters
   if(context == NULL || cache == NULL)
      return ERROR_INVALID_PARAMETER;

   //The cache will be used to save/resume TLS sessions
   context->cache = cache;

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Set client authentication mode
 * @param[in] context Pointer to the TLS context
 * @param[in] mode Client authentication mode
 * @return Error code
 **/

error_t tlsSetClientAuthMode(TlsContext *context, TlsClientAuthMode mode)
{
   //Invalid TLS context?
   if(context == NULL)
      return ERROR_INVALID_PARAMETER;

   //Save client authentication mode
   context->clientAuthMode = mode;

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Set TLS buffer size
 * @param[in] context Pointer to the TLS context
 * @param[in] txBufferSize TX buffer size
 * @param[in] rxBufferSize RX buffer size
 * @return Error code
 **/

error_t tlsSetBufferSize(TlsContext *context,
   size_t txBufferSize, size_t rxBufferSize)
{
   //Invalid TLS context?
   if(context == NULL)
      return ERROR_INVALID_PARAMETER;
   //Check parameters
   if(txBufferSize < 512 || rxBufferSize < 512)
      return ERROR_INVALID_PARAMETER;

   //Save the maximum fragment length for outgoing TLS records
   context->txRecordMaxLen = txBufferSize;

   //Compute the corresponding buffer size
   context->txBufferSize = txBufferSize +
      sizeof(TlsRecord) + TLS_MAX_RECORD_OVERHEAD;

   //Save the maximum fragment length for incoming TLS records
   context->rxRecordMaxLen = rxBufferSize;

   //Compute the corresponding buffer size
   context->rxBufferSize = rxBufferSize +
      sizeof(TlsRecord) + TLS_MAX_RECORD_OVERHEAD;

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Specify the list of allowed cipher suites
 * @param[in] context Pointer to the TLS context
 * @param[in] cipherSuites Pointer to the cipher suite list
 * @param[in] length Number of cipher suites in the list
 * @return Error code
 **/

error_t tlsSetCipherSuites(TlsContext *context,
   const uint16_t *cipherSuites, uint_t length)
{
   //Invalid TLS context?
   if(context == NULL)
      return ERROR_INVALID_PARAMETER;
   //Check parameters
   if(cipherSuites == NULL && length != 0)
      return ERROR_INVALID_PARAMETER;

   //Restrict the cipher suites that can be used
   context->cipherSuites = cipherSuites;
   context->numCipherSuites = length;

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Import Diffie-Hellman parameters
 * @param[in] context Pointer to the TLS context
 * @param[in] params PEM structure that holds Diffie-Hellman parameters
 * @param[in] length Total length of the DER structure
 * @return Error code
 **/

error_t tlsSetDhParameters(TlsContext *context,
   const char_t *params, size_t length)
{
#if (TLS_DH_ANON_SUPPORT == ENABLED || TLS_DHE_RSA_SUPPORT == ENABLED || \
   TLS_DHE_DSS_SUPPORT == ENABLED || TLS_DHE_PSK_SUPPORT == ENABLED)
   //Invalid TLS context?
   if(context == NULL)
      return ERROR_INVALID_PARAMETER;
   //Check parameters
   if(params == NULL && length != 0)
      return ERROR_INVALID_PARAMETER;

   //Decode the PEM structure that holds Diffie-Hellman parameters
   return pemReadDhParameters(params, length, &context->dhContext.params);
#else
   //Diffie-Hellman is not implemented
   return ERROR_NOT_IMPLEMENTED;
#endif
}


/**
 * @brief Set the list of supported ALPN protocols
 * @param[in] context Pointer to the TLS context
 * @param[in] protocolList Comma-delimited list of supported protocols
 * @return Error code
 **/

error_t tlsSetAlpnProtocolList(TlsContext *context, const char_t *protocolList)
{
#if (TLS_ALPN_SUPPORT == ENABLED)
   size_t length;

   //Invalid parameters?
   if(context == NULL || protocolList == NULL)
      return ERROR_INVALID_PARAMETER;

   //Retrieve the length of the list
   length = strlen(protocolList);

   //Check whether the list of supported protocols has already been configured
   if(context->protocolList != NULL)
   {
      //Release memory
      tlsFreeMem(context->protocolList);
      context->protocolList = NULL;
   }

   //Check whether the list of protocols is valid
   if(length > 0)
   {
      //Allocate a memory block to hold the list
      context->protocolList = tlsAllocMem(length + 1);
      //Failed to allocate memory?
      if(context->protocolList == NULL)
         return ERROR_OUT_OF_MEMORY;

      //Save the list of supported protocols
      strcpy(context->protocolList, protocolList);
   }

   //Successful processing
   return NO_ERROR;
#else
   //ALPN is not implemented
   return ERROR_NOT_IMPLEMENTED;
#endif
}


/**
 * @brief Get the name of the negotiated ALPN protocol
 * @param[in] context Pointer to the TLS context
 * @return Pointer to the protocol name
 **/

const char_t *tlsGetAlpnProtocol(TlsContext *context)
{
   //Not implemented
   return NULL;
}


/**
 * @brief Set the pre-shared key to be used
 * @param[in] context Pointer to the TLS context
 * @param[in] psk Pointer to the pre-shared key
 * @param[in] pskLength Length of the pre-shared key, in bytes
 * @return Error code
 **/

error_t tlsSetPsk(TlsContext *context, const uint8_t *psk, size_t pskLength)
{
#if (TLS_PSK_SUPPORT == ENABLED || TLS_RSA_PSK_SUPPORT == ENABLED || \
   TLS_DHE_PSK_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
   //Invalid TLS context?
   if(context == NULL)
      return ERROR_INVALID_PARAMETER;
   //Check parameters
   if(psk == NULL && pskLength != 0)
      return ERROR_INVALID_PARAMETER;

   //Check whether the pre-shared key has already been configured
   if(context->psk != NULL)
   {
      //Release memory
      memset(context->psk, 0, context->pskLen);
      tlsFreeMem(context->psk);
      //Re-initialize length
      context->pskLen = 0;
   }

   //Valid PSK?
   if(pskLength > 0)
   {
      //Allocate a memory block to hold the pre-shared key
      context->psk = tlsAllocMem(pskLength);
      //Failed to allocate memory?
      if(context->psk == NULL)
         return ERROR_OUT_OF_MEMORY;

      //Save the pre-shared key
      memcpy(context->psk, psk, pskLength);
      //Save the length of the key
      context->pskLen = pskLength;
   }

   //Successful processing
   return NO_ERROR;
#else
   //PSK key exchange is not implemented
   return ERROR_NOT_IMPLEMENTED;
#endif
}


/**
 * @brief Set the PSK identity to be used by the client
 * @param[in] context Pointer to the TLS context
 * @param[in] pskIdentity NULL-terminated string that contains the PSK identity
 * @return Error code
 **/

error_t tlsSetPskIdentity(TlsContext *context, const char_t *pskIdentity)
{
#if (TLS_PSK_SUPPORT == ENABLED || TLS_RSA_PSK_SUPPORT == ENABLED || \
   TLS_DHE_PSK_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
   size_t length;

   //Invalid parameters?
   if(context == NULL || pskIdentity == NULL)
      return ERROR_INVALID_PARAMETER;

   //Retrieve the length of the PSK identity
   length = strlen(pskIdentity);

   //Check whether the PSK identity has already been configured
   if(context->pskIdentity != NULL)
   {
      //Release memory
      tlsFreeMem(context->pskIdentity);
      context->pskIdentity = NULL;
   }

   //Valid PSK identity?
   if(length > 0)
   {
      //Allocate a memory block to hold the PSK identity
      context->pskIdentity = tlsAllocMem(length + 1);
      //Failed to allocate memory?
      if(context->pskIdentity == NULL)
         return ERROR_OUT_OF_MEMORY;

      //Save the PSK identity
      strcpy(context->pskIdentity, pskIdentity);
   }

   //Successful processing
   return NO_ERROR;
#else
   //PSK key exchange is not implemented
   return ERROR_NOT_IMPLEMENTED;
#endif
}


/**
 * @brief Set the PSK identity hint to be used by the server
 * @param[in] context Pointer to the TLS context
 * @param[in] pskIdentityHint NULL-terminated string that contains the PSK identity hint
 * @return Error code
 **/

error_t tlsSetPskIdentityHint(TlsContext *context, const char_t *pskIdentityHint)
{
#if (TLS_PSK_SUPPORT == ENABLED || TLS_RSA_PSK_SUPPORT == ENABLED || \
   TLS_DHE_PSK_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
   size_t length;

   //Invalid parameters?
   if(context == NULL || pskIdentityHint == NULL)
      return ERROR_INVALID_PARAMETER;

   //Retrieve the length of the PSK identity hint
   length = strlen(pskIdentityHint);

   //Check whether the PSK identity hint has already been configured
   if(context->pskIdentityHint != NULL)
   {
      //Release memory
      tlsFreeMem(context->pskIdentityHint);
      context->pskIdentityHint = NULL;
   }

   //Valid PSK identity hint?
   if(length > 0)
   {
      //Allocate a memory block to hold the PSK identity hint
      context->pskIdentityHint = tlsAllocMem(length + 1);
      //Failed to allocate memory?
      if(context->pskIdentityHint == NULL)
         return ERROR_OUT_OF_MEMORY;

      //Save the PSK identity hint
      strcpy(context->pskIdentityHint, pskIdentityHint);
   }

   //Successful processing
   return NO_ERROR;
#else
   //PSK key exchange is not implemented
   return ERROR_NOT_IMPLEMENTED;
#endif
}


/**
 * @brief Register the PSK callback function
 * @param[in] context Pointer to the TLS context
 * @param[in] pskCallback PSK callback function
 * @return Error code
 **/

error_t tlsSetPskCallback(TlsContext *context, TlsPskCallback pskCallback)
{
#if (TLS_PSK_SUPPORT == ENABLED || TLS_RSA_PSK_SUPPORT == ENABLED || \
   TLS_DHE_PSK_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
   //Invalid parameters?
   if(context == NULL || pskCallback == NULL)
      return ERROR_INVALID_PARAMETER;

   //Save the PSK callback function
   context->pskCallback = pskCallback;

   //Successful processing
   return NO_ERROR;
#else
   //PSK key exchange is not implemented
   return ERROR_NOT_IMPLEMENTED;
#endif
}


/**
 * @brief Import a trusted CA list
 * @param[in] context Pointer to the TLS context
 * @param[in] trustedCaList List of trusted CA (PEM format)
 * @param[in] length Total length of the list
 * @return Error code
 **/

error_t tlsSetTrustedCaList(TlsContext *context,
   const char_t *trustedCaList, size_t length)
{
   //Invalid TLS context?
   if(context == NULL)
      return ERROR_INVALID_PARAMETER;
   //Check parameters
   if(trustedCaList == NULL && length != 0)
      return ERROR_INVALID_PARAMETER;

   //Save the certificate chain
   context->trustedCaList = trustedCaList;
   context->trustedCaListLen = length;

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Import a certificate and the corresponding private key
 * @param[in] context Pointer to the TLS context
 * @param[in] certChain Certificate chain (PEM format)
 * @param[in] certChainLength Total length of the certificate chain
 * @param[in] privateKey Private key (PEM format)
 * @param[in] privateKeyLength Total length of the private key
 * @return Error code
 **/

error_t tlsAddCertificate(TlsContext *context, const char_t *certChain,
   size_t certChainLength, const char_t *privateKey, size_t privateKeyLength)
{
   error_t error;
   const char_t *p;
   size_t n;
   uint8_t *derCert;
   size_t derCertSize;
   size_t derCertLength;
   X509CertificateInfo *certInfo;
   TlsCertificateType certType;
   TlsSignatureAlgo certSignAlgo;
   TlsHashAlgo certHashAlgo;
   TlsEcNamedCurve namedCurve;

   //Invalid TLS context?
   if(context == NULL)
      return ERROR_INVALID_PARAMETER;

   //Check parameters
   if(certChain == NULL || certChainLength == 0)
      return ERROR_INVALID_PARAMETER;
   if(privateKey == NULL || privateKeyLength == 0)
      return ERROR_INVALID_PARAMETER;

   //Make sure there is enough room to add the certificate
   if(context->numCerts >= TLS_MAX_CERTIFICATES)
      return ERROR_OUT_OF_RESOURCES;

   //Allocate a memory buffer to store X.509 certificate info
   certInfo = tlsAllocMem(sizeof(X509CertificateInfo));
   //Failed to allocate memory?
   if(certInfo == NULL)
      return ERROR_OUT_OF_MEMORY;

   //Point to the beginning of the certificate chain
   p = certChain;
   n = certChainLength;

   //DER encoded certificate
   derCert = NULL;
   derCertSize = 0;
   derCertLength = 0;

   //Start of exception handling block
   do
   {
      //Decode end entity certificate
      error = pemReadCertificate(&p, &n, &derCert, &derCertSize, &derCertLength);
      //Any error to report?
      if(error)
         break;

      //Parse X.509 certificate
      error = x509ParseCertificate(derCert, derCertLength, certInfo);
      //Failed to parse the X.509 certificate?
      if(error)
         break;

      //Retrieve the signature algorithm that has been used to sign the certificate
      error = tlsGetCertificateType(certInfo, &certType,
         &certSignAlgo, &certHashAlgo, &namedCurve);
      //The specified signature algorithm is not supported?
      if(error)
         break;

      //End of exception handling block
   } while(0);

   //Check whether the certificate is acceptable
   if(!error)
   {
      //Point to the structure that describes the certificate
      TlsCertDesc *cert = &context->certs[context->numCerts];

      //Save the certificate chain and the corresponding private key
      cert->certChain = certChain;
      cert->certChainLength = certChainLength;
      cert->privateKey = privateKey;
      cert->privateKeyLength = privateKeyLength;
      cert->type = certType;
      cert->signAlgo = certSignAlgo;
      cert->hashAlgo = certHashAlgo;
      cert->namedCurve = namedCurve;

      //Update the number of certificates
      context->numCerts++;
   }

   //Release previously allocated memory
   tlsFreeMem(derCert);
   tlsFreeMem(certInfo);

   //Return status code
   return error;
}


/**
 * @brief Initiate the TLS handshake
 * @param[in] context Pointer to the TLS context
 * @return Error code
 **/

error_t tlsConnect(TlsContext *context)
{
   error_t error;

   //Invalid TLS context?
   if(context == NULL)
      return ERROR_INVALID_PARAMETER;

   //Ensure the I/O callback functions are properly registered
   if(context->sendCallback == NULL || context->receiveCallback == NULL)
      return ERROR_NOT_CONFIGURED;

   //Verify that the PRNG is properly set
   if(context->prngAlgo == NULL || context->prngContext == NULL)
      return ERROR_NOT_CONFIGURED;

   //Check current state
   if(context->state == TLS_STATE_INIT)
   {
      //Allocate send buffer if necessary
      if(context->txBuffer == NULL)
      {
         //Allocate TX buffer
         context->txBuffer = tlsAllocMem(context->txBufferSize);

         //Failed to allocate memory?
         if(context->txBuffer == NULL)
            return ERROR_OUT_OF_MEMORY;

         //Clear TX buffer
         memset(context->txBuffer, 0, context->txBufferSize);
      }

      //Allocate receive buffer if necessary
      if(context->rxBuffer == NULL)
      {
         //Allocate RX buffer
         context->rxBuffer = tlsAllocMem(context->rxBufferSize);

         //Failed to allocate memory?
         if(context->rxBuffer == NULL)
         {
            //Clean up side effects
            tlsFreeMem(context->txBuffer);
            context->txBuffer = NULL;
            //Report an error
            return ERROR_OUT_OF_MEMORY;
         }

         //Clear RX buffer
         memset(context->rxBuffer, 0, context->rxBufferSize);
      }
   }

   //Perform TLS handshake
   error = tlsHandshake(context);
   //Return status code
   return error;
}


/**
 * @brief Send application data to the remote host using TLS
 * @param[in] context Pointer to the TLS context
 * @param[in] data Pointer to a buffer containing the data to be transmitted
 * @param[in] length Number of bytes to be transmitted
 * @param[out] written Actual number of bytes written (optional parameter)
 * @param[in] flags Set of flags that influences the behavior of this function
 * @return Error code
 **/

error_t tlsWrite(TlsContext *context, const void *data,
   size_t length, size_t *written, uint_t flags)
{
   error_t error;
   size_t n;
   size_t totalLength;

   //Invalid TLS context?
   if(context == NULL)
      return ERROR_INVALID_PARAMETER;

   //Check parameters
   if(data == NULL && length != 0)
      return ERROR_INVALID_PARAMETER;

   //Ensure the I/O callback functions are properly registered
   if(context->sendCallback == NULL || context->receiveCallback == NULL)
      return ERROR_NOT_CONFIGURED;

   //Initialize status code
   error = NO_ERROR;

   //Actual number of bytes written
   totalLength = 0;

   //Send as much data as possible
   while(totalLength < length)
   {
      //Check current state
      if(context->state == TLS_STATE_APPLICATION_DATA)
      {
         //Calculate the number of bytes to write at a time
         n = MIN(length - totalLength, context->txRecordMaxLen);
         //The record length must not exceed 16384 bytes
         n = MIN(n, TLS_MAX_RECORD_LENGTH);

         //Send application data
         error = tlsWriteProtocolData(context, data, n, TLS_TYPE_APPLICATION_DATA);

         //Check status code
         if(!error)
         {
            //Advance data pointer
            data = (uint8_t *) data + n;
            //Update byte counter
            totalLength += n;
         }
         else
         {
            //Send an alert message to the peer, if applicable
            tlsProcessError(context, error);
         }
      }
      else
      {
         //The connection has not yet been established
         error = ERROR_NOT_CONNECTED;
      }

      //Any error to report?
      if(error)
         break;
   }

   //Total number of data that have been written
   if(written != NULL)
      *written = totalLength;

   //Return status code
   return error;
}


/**
 * @brief Receive application data from a the remote host using TLS
 * @param[in] context Pointer to the TLS context
 * @param[out] data Buffer into which received data will be placed
 * @param[in] size Maximum number of bytes that can be received
 * @param[out] received Number of bytes that have been received
 * @param[in] flags Set of flags that influences the behavior of this function
 * @return Error code
 **/

error_t tlsRead(TlsContext *context, void *data,
   size_t size, size_t *received, uint_t flags)
{
   error_t error;
   size_t i;
   size_t n;
   uint8_t *p;
   TlsContentType contentType;

   //Invalid TLS context?
   if(context == NULL)
      return ERROR_INVALID_PARAMETER;

   //Check parameters
   if(data == NULL && received == NULL)
      return ERROR_INVALID_PARAMETER;

   //Ensure the I/O callback functions are properly registered
   if(context->sendCallback == NULL || context->receiveCallback == NULL)
      return ERROR_NOT_CONFIGURED;

   //Initialize status code
   error = NO_ERROR;

   //No data has been read yet
   *received = 0;

   //Read as much data as possible
   while(*received < size)
   {
      //Check current state
      if(context->state == TLS_STATE_APPLICATION_DATA)
      {
         //The TLS record layer receives uninterpreted data from higher layers
         error = tlsReadProtocolData(context, (void **) &p, &n, &contentType);

         //Check status code
         if(!error)
         {
            //Application data received?
            if(contentType == TLS_TYPE_APPLICATION_DATA)
            {
               //Limit the number of bytes to read at a time
               n = MIN(n, size - *received);

               //The TLS_FLAG_BREAK_CHAR flag causes the function to stop reading
               //data as soon as the specified break character is encountered
               if(flags & TLS_FLAG_BREAK_CHAR)
               {
                  //Retrieve the break character code
                  char_t c = LSB(flags);

                  //Search for the specified break character
                  for(i = 0; i < n && p[i] != c; i++);
                  //Adjust the number of data to read
                  n = MIN(n, i + 1);

                  //Copy data to user buffer
                  memcpy(data, p, n);
                  //Total number of data that have been read
                  *received += n;

                  //Advance data pointer
                  context->rxBufferPos += n;
                  //Number of bytes still pending in the receive buffer
                  context->rxBufferLen -= n;

                  //Check whether a break character has been found
                  if(n > 0 && p[n - 1] == c)
                     break;
               }
               else
               {
                  //Copy data to user buffer
                  memcpy(data, p, n);
                  //Total number of data that have been read
                  *received += n;

                  //Advance data pointer
                  context->rxBufferPos += n;
                  //Number of bytes still pending in the receive buffer
                  context->rxBufferLen -= n;

                  //The TLS_FLAG_WAIT_ALL flag causes the function to return
                  //only when the requested number of bytes have been read
                  if(!(flags & TLS_FLAG_WAIT_ALL))
                     break;
               }

               //Advance data pointer
               data = (uint8_t *) data + n;
            }
            //Alert message received?
            else if(contentType == TLS_TYPE_ALERT)
            {
               //Parse Alert message
               error = tlsParseAlert(context, (TlsAlert *) p, n);

               //Advance data pointer
               context->rxBufferPos += n;
               //Number of bytes still pending in the receive buffer
               context->rxBufferLen -= n;
            }
            //An inappropriate message was received?
            else
            {
               //Report an error
               error = ERROR_UNEXPECTED_MESSAGE;
            }
         }

         //Any error to report?
         if(error)
         {
            //Send an alert message to the peer, if applicable
            tlsProcessError(context, error);
         }
      }
      else if(context->state == TLS_STATE_CLOSING ||
         context->state == TLS_STATE_CLOSED)
      {
         //Check whether a fatal alert message has been sent or received
         if(context->fatalAlertSent || context->fatalAlertReceived)
         {
            //Alert messages with a level of fatal result in the immediate
            //termination of the connection
            error = ERROR_FAILURE;
         }
         else
         {
            //The user must be satisfied with data already on hand
            if(*received > 0)
            {
               //Some data are pending in the receive buffer
               error = NO_ERROR;
               break;
            }
            else
            {
               //The receive buffer is empty
               error = ERROR_END_OF_STREAM;
            }
         }
      }
      else
      {
         //The connection has not yet been established
         error = ERROR_NOT_CONNECTED;
      }

      //Any error to report?
      if(error)
         break;
   }

   //Return status code
   return error;
}


/**
 * @brief Gracefully close TLS session
 * @param[in] context Pointer to the TLS context
 **/

error_t tlsShutdown(TlsContext *context)
{
   //Either party may initiate a close by sending a close_notify alert
   return tlsShutdownEx(context, FALSE);
}


/**
 * @brief Gracefully close TLS session
 * @param[in] context Pointer to the TLS context
 * @param[in] waitForCloseNotify Wait for the close notify alert from the peer
 **/

error_t tlsShutdownEx(TlsContext *context, bool_t waitForCloseNotify)
{
   error_t error;
   size_t n;
   uint8_t *p;
   TlsContentType contentType;

   //Invalid TLS context?
   if(context == NULL)
      return ERROR_INVALID_PARAMETER;

   //Ensure the I/O callback functions are properly registered
   if(context->sendCallback == NULL || context->receiveCallback == NULL)
      return ERROR_NOT_CONFIGURED;

   //Initialize status code
   error = NO_ERROR;

   //Wait for the TLS session to be closed
   while(context->state != TLS_STATE_CLOSED)
   {
      //Check current state
      if(context->state == TLS_STATE_APPLICATION_DATA)
      {
         //Flush send buffer
         error = tlsWriteProtocolData(context, NULL, 0, TLS_TYPE_NONE);

         //Check status code
         if(!error)
         {
            //Either party may initiate a close by sending a close_notify alert
            context->state = TLS_STATE_CLOSING;
         }
      }
      if(context->state == TLS_STATE_CLOSING)
      {
         //Flush send buffer
         error = tlsWriteProtocolData(context, NULL, 0, TLS_TYPE_NONE);

         //Check status code
         if(!error)
         {
            //Unless some other fatal alert has been transmitted, each party
            //is required to send a close_notify alert before closing the
            //write side of the connection
            if(context->fatalAlertSent || context->fatalAlertReceived)
            {
               //Close the connection immediately
               context->state = TLS_STATE_CLOSED;
            }
            else if(!context->closeNotifySent)
            {
               //Notifies the recipient that the sender will not send any
               //more messages on this connection
               error = tlsSendAlert(context, TLS_ALERT_LEVEL_WARNING,
                  TLS_ALERT_CLOSE_NOTIFY);
            }
            else if(!context->closeNotifyReceived && waitForCloseNotify)
            {
               //Wait for the responding close_notify alert
               error = tlsReadProtocolData(context, (void **) &p, &n, &contentType);

               //Check status code
               if(!error)
               {
                  //Application data received?
                  if(contentType == TLS_TYPE_APPLICATION_DATA)
                  {
                     //Advance data pointer
                     context->rxBufferPos += n;
                     //Number of bytes still pending in the receive buffer
                     context->rxBufferLen -= n;
                  }
                  //Alert message received?
                  else if(contentType == TLS_TYPE_ALERT)
                  {
                     //Parse Alert message
                     error = tlsParseAlert(context, (TlsAlert *) p, n);

                     //Advance data pointer
                     context->rxBufferPos += n;
                     //Number of bytes still pending in the receive buffer
                     context->rxBufferLen -= n;
                  }
                  //An inappropriate message was received?
                  else
                  {
                     //Report an error
                     error = ERROR_UNEXPECTED_MESSAGE;
                  }
               }
            }
            else
            {
               //The connection is closed
               context->state = TLS_STATE_CLOSED;
            }
         }
      }
      else
      {
         //Report an error
         error = ERROR_NOT_CONNECTED;
      }

      //Any error to report?
      if(error)
         break;
   }

   //Return status code
   return error;
}


/**
 * @brief Release TLS context
 * @param[in] context Pointer to the TLS context
 **/

void tlsFree(TlsContext *context)
{
   //Valid TLS context?
   if(context != NULL)
   {
      //Release server name
      if(context->serverName != NULL)
         tlsFreeMem(context->serverName);

#if (TLS_ALPN_SUPPORT == ENABLED)
      //Release the list of supported protocols
      if(context->protocolList != NULL)
         tlsFreeMem(context->protocolList);
#endif

#if (TLS_PSK_SUPPORT == ENABLED || TLS_RSA_PSK_SUPPORT == ENABLED || \
   TLS_DHE_PSK_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
      //Release the pre-shared key
      if(context->psk != NULL)
      {
         memset(context->psk, 0, context->pskLen);
         tlsFreeMem(context->psk);
      }

      //Release the PSK identity
      if(context->pskIdentity != NULL)
         tlsFreeMem(context->pskIdentity);

      //Release the PSK identity hint
      if(context->pskIdentityHint != NULL)
         tlsFreeMem(context->pskIdentityHint);
#endif

#if (TLS_DH_ANON_SUPPORT == ENABLED || TLS_DHE_RSA_SUPPORT == ENABLED || \
   TLS_DHE_DSS_SUPPORT == ENABLED || TLS_DHE_PSK_SUPPORT == ENABLED)
      //Release Diffie-Hellman context
      dhFree(&context->dhContext);
#endif

#if (TLS_ECDH_ANON_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED || \
   TLS_ECDHE_ECDSA_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
      //Release ECDH context
      ecdhFree(&context->ecdhContext);
#endif

#if (TLS_RSA_SIGN_SUPPORT == ENABLED || TLS_RSA_SUPPORT == ENABLED || \
   TLS_DHE_RSA_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED)
      //Release peer's RSA public key
      rsaFreePublicKey(&context->peerRsaPublicKey);
#endif

#if (TLS_DSA_SIGN_SUPPORT == ENABLED || TLS_DHE_DSS_SUPPORT == ENABLED)
      //Release peer's DSA public key
      dsaFreePublicKey(&context->peerDsaPublicKey);
#endif

#if (TLS_ECDSA_SIGN_SUPPORT == ENABLED || TLS_ECDHE_ECDSA_SUPPORT == ENABLED)
      //Release peer's EC domain parameters
      ecFreeDomainParameters(&context->peerEcParams);
      //Release peer's EC public key
      ecFree(&context->peerEcPublicKey);
#endif

      //Release send buffer
      if(context->txBuffer != NULL)
      {
         memset(context->txBuffer, 0, context->txBufferSize);
         tlsFreeMem(context->txBuffer);
      }

      //Release receive buffer
      if(context->rxBuffer != NULL)
      {
         memset(context->rxBuffer, 0, context->rxBufferSize);
         tlsFreeMem(context->rxBuffer);
      }

      //Release the MD5 context used to compute verify data
      if(context->handshakeMd5Context != NULL)
      {
         memset(context->handshakeMd5Context, 0, sizeof(Md5Context));
         tlsFreeMem(context->handshakeMd5Context);
      }

      //Release the SHA-1 context used to compute verify data
      if(context->handshakeSha1Context != NULL)
      {
         memset(context->handshakeSha1Context, 0, sizeof(Sha1Context));
         tlsFreeMem(context->handshakeSha1Context);
      }

      //Release the hash context used to compute verify data (TLS 1.2)
      if(context->handshakeHashContext != NULL)
      {
         memset(context->handshakeHashContext, 0, context->prfHashAlgo->contextSize);
         tlsFreeMem(context->handshakeHashContext);
      }

      //Release the encryption context (TX path)
      if(context->writeCipherContext != NULL)
      {
         memset(context->writeCipherContext, 0, context->cipherAlgo->contextSize);
         tlsFreeMem(context->writeCipherContext);
      }

      //Release the encryption context (RX path)
      if(context->readCipherContext != NULL)
      {
         memset(context->readCipherContext, 0, context->cipherAlgo->contextSize);
         tlsFreeMem(context->readCipherContext);
      }

#if (TLS_GCM_CIPHER_SUPPORT == ENABLED)
      //Release the GCM context (TX path)
      if(context->writeGcmContext != NULL)
      {
         memset(context->writeGcmContext, 0, sizeof(GcmContext));
         tlsFreeMem(context->writeGcmContext);
      }

      //Release the GCM context (RX path)
      if(context->readGcmContext != NULL)
      {
         memset(context->readGcmContext, 0, sizeof(GcmContext));
         tlsFreeMem(context->readGcmContext);
      }
#endif

      //Clear the TLS context before freeing memory
      memset(context, 0, sizeof(TlsContext));
      tlsFreeMem(context);
   }
}


/**
 * @brief Save TLS session
 * @param[in] context Pointer to the TLS context
 * @param[out] session Buffer where to store the current session parameters
 * @return Error code
 **/

error_t tlsSaveSession(const TlsContext *context, TlsSession *session)
{
   //Check parameters
   if(context == NULL || session == NULL)
      return ERROR_INVALID_PARAMETER;

   //Invalid session parameters?
   if(!context->sessionIdLen || !context->cipherSuite)
      return ERROR_FAILURE;

   //Save session identifier
   memcpy(session->id, context->sessionId, context->sessionIdLen);
   session->idLength = context->sessionIdLen;

   //Get current time
   session->timestamp = osGetSystemTime();

   //Negotiated cipher suite and compression method
   session->cipherSuite = context->cipherSuite;
   session->compressionMethod = context->compressionMethod;

   //Save master secret
   memcpy(session->masterSecret, context->masterSecret, 48);

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Restore TLS session
 * @param[in] context Pointer to the TLS context
 * @param[in] session Pointer to the session to be restored
 * @return Error code
 **/

error_t tlsRestoreSession(TlsContext *context, const TlsSession *session)
{
   //Check parameters
   if(context == NULL || session == NULL)
      return ERROR_INVALID_PARAMETER;

   //Restore session identifier
   memcpy(context->sessionId, session->id, session->idLength);
   context->sessionIdLen = session->idLength;

   //Negotiated cipher suite and compression method
   context->cipherSuite = session->cipherSuite;
   context->compressionMethod = session->compressionMethod;

   //Restore master secret
   memcpy(context->masterSecret, session->masterSecret, 48);

   //Successful processing
   return NO_ERROR;
}

#endif