Webserver+3d print

Dependents:   Nucleo

cyclone_ssl/tls_server.c

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

File content as of revision 0:8918a71cdbe9:

/**
 * @file tls_server.c
 * @brief Handshake message processing (TLS server)
 *
 * @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 "tls.h"
#include "tls_cipher_suites.h"
#include "tls_server.h"
#include "tls_server_misc.h"
#include "tls_common.h"
#include "tls_record.h"
#include "tls_cache.h"
#include "tls_misc.h"
#include "x509.h"
#include "pem.h"
#include "date_time.h"
#include "debug.h"

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


/**
 * @brief TLS server handshake
 *
 * TLS handshake protocol is responsible for the authentication
 * and key exchange necessary to establish a secure session
 *
 * @param[in] context Pointer to the TLS context
 * @return Error code
 **/

error_t tlsServerHandshake(TlsContext *context)
{
   error_t error;

   //Initialize status code
   error = NO_ERROR;

   //Wait for the handshake to complete
   do
   {
      //Flush send buffer
      if(context->state != TLS_STATE_CLOSED)
         error = tlsWriteProtocolData(context, NULL, 0, TLS_TYPE_NONE);

      //Check status code
      if(!error)
      {
         //Check whether the handshake is complete
         if(context->state == TLS_STATE_APPLICATION_DATA)
         {
            //At this is point, the handshake is complete and the server
            //starts to exchange application-layer data
            break;
         }

         //The TLS handshake is implemented as a state machine
         //representing the current location in the protocol
         switch(context->state)
         {
         //Default state?
         case TLS_STATE_INIT:
            //The client initiates the TLS handshake by sending a ClientHello
            //message to the server
            context->state = TLS_STATE_CLIENT_HELLO;
            break;
         //Sending ServerHello message?
         case TLS_STATE_SERVER_HELLO:
            //The server will send this message in response to a ClientHello
            //message when it was able to find an acceptable set of algorithms
            error = tlsSendServerHello(context);
            break;
         //Sending Certificate message?
         case TLS_STATE_SERVER_CERTIFICATE:
            //The server must send a Certificate message whenever the agreed-
            //upon key exchange method uses certificates for authentication. This
            //message will always immediately follow the ServerHello message
            error = tlsSendCertificate(context);
            break;
         //Sending ServerKeyExchange message?
         case TLS_STATE_SERVER_KEY_EXCHANGE:
            //The ServerKeyExchange message is sent by the server only when the
            //server Certificate message (if sent) does not contain enough data
            //to allow the client to exchange a premaster secret
            error = tlsSendServerKeyExchange(context);
            break;
         //Sending Certificate message?
         case TLS_STATE_CERTIFICATE_REQUEST:
            //A non-anonymous server can optionally request a certificate from the
            //client, if appropriate for the selected cipher suite. This message,
            //if sent, will immediately follow the ServerKeyExchange message
            error = tlsSendCertificateRequest(context);
            break;
         //Sending ServerHelloDone message?
         case TLS_STATE_SERVER_HELLO_DONE:
            //The ServerHelloDone message is sent by the server to indicate the
            //end of the ServerHello and associated messages
            error = tlsSendServerHelloDone(context);
            break;
         //Sending ChangeCipherSpec message?
         case TLS_STATE_SERVER_CHANGE_CIPHER_SPEC:
            //The ChangeCipherSpec message is sent by the server and to notify the
            //client that subsequent records will be protected under the newly
            //negotiated CipherSpec and keys
            error = tlsSendChangeCipherSpec(context);
            break;
         //Sending Finished message?
         case TLS_STATE_SERVER_FINISHED:
            //A Finished message is always sent immediately after a changeCipherSpec
            //message to verify that the key exchange and authentication processes
            //were successful
            error = tlsSendFinished(context);
            break;
         //Waiting for a message from the client?
         case TLS_STATE_CLIENT_HELLO:
         case TLS_STATE_CLIENT_CERTIFICATE:
         case TLS_STATE_CLIENT_KEY_EXCHANGE:
         case TLS_STATE_CERTIFICATE_VERIFY:
         case TLS_STATE_CLIENT_CHANGE_CIPHER_SPEC:
         case TLS_STATE_CLIENT_FINISHED:
            //Parse incoming handshake message
            error = tlsParseClientMessage(context);
            break;
         //Sending Alert message?
         case TLS_STATE_CLOSING:
            //Mark the TLS connection as closed
            context->state = TLS_STATE_CLOSED;
            break;
         //TLS connection closed?
         case TLS_STATE_CLOSED:
            //Debug message
            TRACE_WARNING("TLS handshake failure!\r\n");
            //Report an error
            error = ERROR_HANDSHAKE_FAILED;
            break;
         //Invalid state?
         default:
            //Report an error
            error = ERROR_UNEXPECTED_STATE;
            break;
         }
      }

      //Abort TLS handshake if an error was encountered
   } while(!error);

   //Successful TLS handshake?
   if(!error)
   {
      //Save current session in the session cache for further reuse
      tlsSaveToCache(context);
   }
   else
   {
      //Send an alert message to the client, if applicable
      tlsProcessError(context, error);
   }

   //Return status code
   return error;
}


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

error_t tlsParseClientMessage(TlsContext *context)
{
   error_t error;
   size_t length;
   void *message;
   TlsContentType contentType;

   //A message can be fragmented across several records...
   error = tlsReadProtocolData(context, &message, &length, &contentType);

   //Check status code
   if(!error)
   {
      //Handshake message received?
      if(contentType == TLS_TYPE_HANDSHAKE)
      {
         //Check handshake message type
         switch(((TlsHandshake *) message)->msgType)
         {
         //ClientHello message received?
         case TLS_TYPE_CLIENT_HELLO:
            //When a client first connects to a server, it is required to send
            //the ClientHello as its first message
            error = tlsParseClientHello(context, message, length);
            break;
         //Certificate message received?
         case TLS_TYPE_CERTIFICATE:
            //This is the first message the client can send after receiving a
            //ServerHelloDone message. This message is only sent if the server
            //requests a certificate
            error = tlsParseCertificate(context, message, length);
            break;
         //ClientKeyExchange message received?
         case TLS_TYPE_CLIENT_KEY_EXCHANGE:
            //This message is always sent by the client. It must immediately
            //follow the client certificate message, if it is sent. Otherwise,
            //it must be the first message sent by the client after it receives
            //the ServerHelloDone message
            error = tlsParseClientKeyExchange(context, message, length);
            break;
         //CertificateVerify message received?
         case TLS_TYPE_CERTIFICATE_VERIFY:
            //This message is used to provide explicit verification of a client
            //certificate. This message is only sent following a client certificate
            //that has signing capability. When sent, it must immediately follow
            //the clientKeyExchange message
            error = tlsParseCertificateVerify(context, message, length);
            break;
         //Finished message received?
         case TLS_TYPE_FINISHED:
            //A Finished message is always sent immediately after a changeCipherSpec
            //message to verify that the key exchange and authentication processes
            //were successful
            error = tlsParseFinished(context, message, length);
            break;
         //Invalid handshake message received?
         default:
            //Report an error
            error = ERROR_UNEXPECTED_MESSAGE;
            break;
         }
      }
      //ChangeCipherSpec message received?
      else if(contentType == TLS_TYPE_CHANGE_CIPHER_SPEC)
      {
         //The ChangeCipherSpec message is sent by the client and to notify the
         //server that subsequent records will be protected under the newly
         //negotiated CipherSpec and keys
         error = tlsParseChangeCipherSpec(context, message, length);
      }
      //Alert message received?
      else if(contentType == TLS_TYPE_ALERT)
      {
         //Parse Alert message
         error = tlsParseAlert(context, message, length);
      }
      //Application data received?
      else
      {
         //The client cannot transmit application data
         //before the handshake is completed
         error = ERROR_UNEXPECTED_MESSAGE;
      }

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

   //Return status code
   return error;
}


/**
 * @brief Send ServerHello message
 *
 * The server will send this message in response to a ClientHello
 * message when it was able to find an acceptable set of algorithms.
 * If it cannot find such a match, it will respond with a handshake
 * failure alert
 *
 * @param[in] context Pointer to the TLS context
 * @return Error code
 **/

error_t tlsSendServerHello(TlsContext *context)
{
   error_t error;
   size_t length;
   TlsServerHello *message;

   //Point to the buffer where to format the message
   message = (TlsServerHello *) context->txBuffer;

   //Generate the server random value using a cryptographically-safe
   //pseudorandom number generator
   error = tlsGenerateRandomValue(context, &context->serverRandom);

   //Check status code
   if(!error)
   {
      //Format ServerHello message
      error = tlsFormatServerHello(context, message, &length);
   }

   //Check status code
   if(!error)
   {
      //Debug message
      TRACE_INFO("Sending ServerHello message (%" PRIuSIZE " bytes)...\r\n", length);
      TRACE_DEBUG_ARRAY("  ", message, length);

      //Send handshake message
      error = tlsWriteProtocolData(context, message, length, TLS_TYPE_HANDSHAKE);
   }

   //Check status code
   if(error == NO_ERROR || error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
   {
#if (TLS_SESSION_RESUME_SUPPORT == ENABLED)
      //Use abbreviated handshake?
      if(context->resume)
      {
         //Derive session keys from the master secret
         error = tlsGenerateKeys(context);

         //Key material successfully generated?
         if(!error)
         {
            //At this point, both client and server must send ChangeCipherSpec
            //messages and proceed directly to Finished messages
            context->state = TLS_STATE_SERVER_CHANGE_CIPHER_SPEC;
         }
      }
      else
#endif
      {
         //Perform a full handshake
         context->state = TLS_STATE_SERVER_CERTIFICATE;
      }
   }

   //Return status code
   return error;
}


/**
 * @brief Send ServerKeyExchange message
 *
 * The ServerKeyExchange message is sent by the server only when the
 * server Certificate message does not contain enough data to allow
 * the client to exchange a premaster secret
 *
 * @param[in] context Pointer to the TLS context
 * @return Error code
 **/

error_t tlsSendServerKeyExchange(TlsContext *context)
{
   error_t error;
   size_t length;
   TlsServerKeyExchange *message;

   //Initialize status code
   error = NO_ERROR;

   //Point to the buffer where to format the message
   message = (TlsServerKeyExchange *) context->txBuffer;
   //Initialize length
   length = 0;

   //The ServerKeyExchange message is sent by the server only when the server
   //Certificate message (if sent) does not contain enough data to allow the
   //client to exchange a premaster secret
   if(context->keyExchMethod == TLS_KEY_EXCH_DH_ANON ||
      context->keyExchMethod == TLS_KEY_EXCH_DHE_RSA ||
      context->keyExchMethod == TLS_KEY_EXCH_DHE_DSS ||
      context->keyExchMethod == TLS_KEY_EXCH_DHE_PSK ||
      context->keyExchMethod == TLS_KEY_EXCH_ECDH_ANON ||
      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_RSA ||
      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_ECDSA ||
      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_PSK)
   {
      //Format ServerKeyExchange message
      error = tlsFormatServerKeyExchange(context, message, &length);
   }
   else if(context->keyExchMethod == TLS_KEY_EXCH_PSK ||
      context->keyExchMethod == TLS_KEY_EXCH_RSA_PSK)
   {
#if (TLS_PSK_SUPPORT == ENABLED || TLS_RSA_PSK_SUPPORT == ENABLED || \
   TLS_DHE_PSK_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
      //If no PSK identity hint is provided by the server, the
      //ServerKeyExchange message is omitted...
      if(context->pskIdentityHint != NULL)
      {
         //Format ServerKeyExchange message
         error = tlsFormatServerKeyExchange(context, message, &length);
      }
#endif
   }

   //Check status code
   if(!error)
   {
      //Any message to send?
      if(length > 0)
      {
         //Debug message
         TRACE_INFO("Sending ServerKeyExchange message (%" PRIuSIZE " bytes)...\r\n", length);
         TRACE_DEBUG_ARRAY("  ", message, length);

         //Send handshake message
         error = tlsWriteProtocolData(context, message, length, TLS_TYPE_HANDSHAKE);
      }
   }

   //Check status code
   if(error == NO_ERROR || error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
   {
      //Prepare to send a CertificateRequest message...
      context->state = TLS_STATE_CERTIFICATE_REQUEST;
   }

   //Return status code
   return error;
}


/**
 * @brief Send CertificateRequest message
 *
 * A server can optionally request a certificate from the client, if
 * appropriate for the selected cipher suite. This message will
 * immediately follow the ServerKeyExchange message
 *
 * @param[in] context Pointer to the TLS context
 * @return Error code
 **/

error_t tlsSendCertificateRequest(TlsContext *context)
{
   error_t error;
   size_t length;
   TlsCertificateRequest *message;

   //Initialize status code
   error = NO_ERROR;

#if (TLS_RSA_SIGN_SUPPORT == ENABLED || TLS_DSA_SIGN_SUPPORT == ENABLED || TLS_ECDSA_SIGN_SUPPORT == ENABLED)
   //A server can optionally request a certificate from the client
   if(context->clientAuthMode != TLS_CLIENT_AUTH_NONE)
   {
      //Non-anonymous key exchange?
      if(context->keyExchMethod == TLS_KEY_EXCH_RSA ||
         context->keyExchMethod == TLS_KEY_EXCH_DHE_RSA ||
         context->keyExchMethod == TLS_KEY_EXCH_DHE_DSS ||
         context->keyExchMethod == TLS_KEY_EXCH_ECDHE_RSA ||
         context->keyExchMethod == TLS_KEY_EXCH_ECDHE_ECDSA ||
         context->keyExchMethod == TLS_KEY_EXCH_RSA_PSK)
      {
         //Point to the buffer where to format the message
         message = (TlsCertificateRequest *) context->txBuffer;

         //Format CertificateRequest message
         error = tlsFormatCertificateRequest(context, message, &length);

         //Check status code
         if(!error)
         {
            //Debug message
            TRACE_INFO("Sending CertificateRequest message (%" PRIuSIZE " bytes)...\r\n", length);
            TRACE_DEBUG_ARRAY("  ", message, length);

            //Send handshake message
            error = tlsWriteProtocolData(context, message, length, TLS_TYPE_HANDSHAKE);
         }
      }
   }
#endif

   //Check status code
   if(error == NO_ERROR || error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
   {
      //Prepare to send a ServerHelloDone message...
      context->state = TLS_STATE_SERVER_HELLO_DONE;
   }

   //Return status code
   return error;
}


/**
 * @brief Send ServerHelloDone message
 *
 * The ServerHelloDone message is sent by the server to indicate the
 * end of the ServerHello and associated messages. After sending this
 * message, the server will wait for a client response
 *
 * @param[in] context Pointer to the TLS context
 * @return Error code
 **/

error_t tlsSendServerHelloDone(TlsContext *context)
{
   error_t error;
   size_t length;
   TlsServerHelloDone *message;

   //Point to the buffer where to format the message
   message = (TlsServerHelloDone *) context->txBuffer;

   //Format ServerHelloDone message
   error = tlsFormatServerHelloDone(context, message, &length);

   //Check status code
   if(!error)
   {
      //Debug message
      TRACE_INFO("Sending ServerHelloDone message (%" PRIuSIZE " bytes)...\r\n", length);
      TRACE_DEBUG_ARRAY("  ", message, length);

      //Send handshake message
      error = tlsWriteProtocolData(context, message, length, TLS_TYPE_HANDSHAKE);
   }

   //Check status code
   if(error == NO_ERROR || error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
   {
      //The client must send a Certificate message if the server requests it
      if(context->clientAuthMode != TLS_CLIENT_AUTH_NONE)
         context->state = TLS_STATE_CLIENT_CERTIFICATE;
      else
         context->state = TLS_STATE_CLIENT_KEY_EXCHANGE;
   }

   //Return status code
   return error;
}


/**
 * @brief Format ServerHello message
 * @param[in] context Pointer to the TLS context
 * @param[out] message Buffer where to format the ServerHello message
 * @param[out] length Length of the resulting ServerHello message
 * @return Error code
 **/

error_t tlsFormatServerHello(TlsContext *context,
   TlsServerHello *message, size_t *length)
{
   uint8_t *p;
   TlsExtensions *extensionList;

   //Handshake message type
   message->msgType = TLS_TYPE_SERVER_HELLO;

   //This field contains the lower of the version suggested by the client
   //in the ClientHello and the highest supported by the server
   message->serverVersion = htons(context->version);

   //Server random value
   message->random = context->serverRandom;

   //Point to the session ID
   p = message->sessionId;
   //Total length of the message
   *length = sizeof(TlsServerHello);

#if (TLS_SESSION_RESUME_SUPPORT == ENABLED)
   //The SessionID uniquely identifies the current session
   message->sessionIdLength = (uint8_t)context->sessionIdLen;
   memcpy(message->sessionId, context->sessionId, context->sessionIdLen);
#else
   //The server may return an empty session ID to indicate that the session
   //will not be cached and therefore cannot be resumed
   message->sessionIdLength = 0;
#endif

   //Debug message
   TRACE_INFO("Session ID (%" PRIu8 " bytes):\r\n", message->sessionIdLength);
   TRACE_INFO_ARRAY("  ", message->sessionId, message->sessionIdLength);

   //Advance data pointer
   p += message->sessionIdLength;
   //Adjust the length of the message
   *length += message->sessionIdLength;

   //The single cipher suite selected by the server
   STORE16BE(context->cipherSuite, p);
   //Advance data pointer
   p += sizeof(TlsCipherSuite);
   //Adjust the length of the message
   *length += sizeof(TlsCipherSuite);

   //The single compression algorithm selected by the server
   *p = context->compressionMethod;
   //Advance data pointer
   p += sizeof(TlsCompressionMethod);
   //Adjust the length of the message
   *length += sizeof(TlsCompressionMethod);

   //Only extensions offered by the client can appear in the server's list
   extensionList = (TlsExtensions *) p;
   //Total length of the extension list
   extensionList->length = 0;

   //Point to the first extension of the list
   p += sizeof(TlsExtensions);

#if (TLS_ECDH_ANON_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED || \
   TLS_ECDHE_ECDSA_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
   //A server that selects an ECC cipher suite in response to a ClientHello
   //message including an EcPointFormats extension appends this extension
   //to its ServerHello message
   if(tlsIsEccCipherSuite(context->cipherSuite) && context->ecPointFormatExtFound)
   {
      uint_t n;
      TlsExtension *extension;
      TlsEcPointFormatList *ecPointFormatList;

      //Add the EcPointFormats extension
      extension = (TlsExtension *) p;
      //Type of the extension
      extension->type = HTONS(TLS_EXT_EC_POINT_FORMATS);

      //Point to the list of supported EC point formats
      ecPointFormatList = (TlsEcPointFormatList *) extension->value;
      //Items in the list are ordered according to server's preferences
      n = 0;

      //The server can parse only the uncompressed point format...
      ecPointFormatList->value[n++] = TLS_EC_POINT_FORMAT_UNCOMPRESSED;
      //Fix the length of the list
      ecPointFormatList->length = n;

      //Consider the 2-byte length field that precedes the list
      n += sizeof(TlsEcPointFormatList);
      //Fix the length of the extension
      extension->length = htons(n);

      //Compute the length, in bytes, of the EcPointFormats extension
      n += sizeof(TlsExtension);
      //Fix the length of the extension list
      extensionList->length += n;

      //Point to the next field
      p += n;
      //Total length of the message
      *length += n;
   }
#endif

   //Check whether the extension list is empty
   if(extensionList->length > 0)
   {
      //Convert the length of the extension list to network byte order
      extensionList->length = htons(extensionList->length);
      //Total length of the message
      *length += sizeof(TlsExtensions);
   }

   //Fix the length field
   STORE24BE(*length - sizeof(TlsHandshake), message->length);

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Format ServerKeyExchange message
 * @param[in] context Pointer to the TLS context
 * @param[out] message Buffer where to format the ServerKeyExchange message
 * @param[out] length Length of the resulting ServerKeyExchange message
 * @return Error code
 **/

error_t tlsFormatServerKeyExchange(TlsContext *context,
   TlsServerKeyExchange *message, size_t *length)
{
   error_t error;
   size_t n;
   size_t paramsLen;
   uint8_t *p;
   uint8_t *params;

   //Handshake message type
   message->msgType = TLS_TYPE_SERVER_KEY_EXCHANGE;

   //Point to the body of the handshake message
   p = message->data;
   //Length of the handshake message
   *length = 0;

   //PSK key exchange method?
   if(context->keyExchMethod == TLS_KEY_EXCH_PSK ||
      context->keyExchMethod == TLS_KEY_EXCH_RSA_PSK ||
      context->keyExchMethod == TLS_KEY_EXCH_DHE_PSK ||
      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_PSK)
   {
      //To help the client in selecting which identity to use, the server
      //can provide a PSK identity hint in the ServerKeyExchange message
      error = tlsFormatPskIdentityHint(context, p, &n);
      //Any error to report?
      if(error)
         return error;

      //Advance data pointer
      p += n;
      //Adjust the length of the message
      *length += n;
   }

   //Diffie-Hellman or ECDH key exchange method?
   if(context->keyExchMethod == TLS_KEY_EXCH_DH_ANON ||
      context->keyExchMethod == TLS_KEY_EXCH_DHE_RSA ||
      context->keyExchMethod == TLS_KEY_EXCH_DHE_DSS ||
      context->keyExchMethod == TLS_KEY_EXCH_DHE_PSK ||
      context->keyExchMethod == TLS_KEY_EXCH_ECDH_ANON ||
      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_RSA ||
      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_ECDSA ||
      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_PSK)
   {
      //Point to the server's key exchange parameters
      params = p;

      //Format server's key exchange parameters
      error = tlsFormatServerKeyParams(context, p, &paramsLen);
      //Any error to report?
      if(error)
         return error;

      //Advance data pointer
      p += paramsLen;
      //Adjust the length of the message
      *length += paramsLen;
   }

   //For non-anonymous Diffie-Hellman and ECDH key exchanges, a signature
   //over the server's key exchange parameters shall be generated
   if(context->keyExchMethod == TLS_KEY_EXCH_DHE_RSA ||
      context->keyExchMethod == TLS_KEY_EXCH_DHE_DSS ||
      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_RSA ||
      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_ECDSA)
   {
      //Sign server's key exchange parameters
      error = tlsGenerateServerKeySignature(context, p, params, paramsLen, &n);
      //Any error to report?
      if(error)
         return error;

      //Advance data pointer
      p += n;
      //Adjust the length of the message
      *length += n;
   }

   //Fix the length field
   STORE24BE(*length, message->length);
   //Length of the complete handshake message
   *length += sizeof(TlsHandshake);

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Format CertificateRequest message
 * @param[in] context Pointer to the TLS context
 * @param[out] message Buffer where to format the CertificateRequest message
 * @param[out] length Length of the resulting CertificateRequest message
 * @return Error code
 **/

error_t tlsFormatCertificateRequest(TlsContext *context,
   TlsCertificateRequest *message, size_t *length)
{
   error_t error;
   size_t n;
   uint8_t *p;
   const char_t *pemCert;
   size_t pemCertLength;
   uint8_t *derCert;
   size_t derCertSize;
   size_t derCertLength;
   X509CertificateInfo *certInfo;
   TlsCertAuthorities *certAuthorities;

   //Initialize status code
   error = NO_ERROR;

   //Handshake message type
   message->msgType = TLS_TYPE_CERTIFICATE_REQUEST;

   //Enumerate the types of certificate types that the client may offer
   n = 0;

#if (TLS_RSA_SIGN_SUPPORT == ENABLED)
   //Accept certificates that contain a RSA public key
   message->certificateTypes[n++] = TLS_CERT_RSA_SIGN;
#endif
#if (TLS_DSA_SIGN_SUPPORT == ENABLED)
   //Accept certificates that contain a DSA public key
   message->certificateTypes[n++] = TLS_CERT_DSS_SIGN;
#endif
#if (TLS_ECDSA_SIGN_SUPPORT == ENABLED)
   //Accept certificates that contain an ECDSA public key
   message->certificateTypes[n++] = TLS_CERT_ECDSA_SIGN;
#endif

   //Set the length of the list
   message->certificateTypesLength = (uint8_t) n;
   //Total length of the message
   *length = sizeof(TlsCertificateRequest) + n;

#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_MIN_VERSION <= TLS_VERSION_1_2)
   //Check whether TLS 1.2 is currently used
   if(context->version == TLS_VERSION_1_2)
   {
      TlsSignHashAlgos *supportedSignAlgos;

      //Point to the list of the hash/signature algorithm pairs that
      //the server is able to verify
      supportedSignAlgos = PTR_OFFSET(message, *length);

      //Enumerate the hash/signature algorithm pairs in descending
      //order of preference
      n = 0;

#if (TLS_RSA_SIGN_SUPPORT == ENABLED)
      //SHA-1 with RSA is always supported
      supportedSignAlgos->value[n].signature = TLS_SIGN_ALGO_RSA;
      supportedSignAlgos->value[n++].hash = TLS_HASH_ALGO_SHA1;

      //The hash algorithm used for PRF operations can also be used for signing
      if(context->prfHashAlgo == SHA256_HASH_ALGO)
      {
         //SHA-256 with RSA is supported
         supportedSignAlgos->value[n].signature = TLS_SIGN_ALGO_RSA;
         supportedSignAlgos->value[n++].hash = TLS_HASH_ALGO_SHA256;
      }
#if (TLS_SHA384_SUPPORT == ENABLED)
      else if(context->prfHashAlgo == SHA384_HASH_ALGO)
      {
         //SHA-384 with RSA is supported
         supportedSignAlgos->value[n].signature = TLS_SIGN_ALGO_RSA;
         supportedSignAlgos->value[n++].hash = TLS_HASH_ALGO_SHA384;
      }
#endif
#endif
#if (TLS_DSA_SIGN_SUPPORT == ENABLED)
      //DSA with SHA-1 is always supported
      supportedSignAlgos->value[n].signature = TLS_SIGN_ALGO_DSA;
      supportedSignAlgos->value[n++].hash = TLS_HASH_ALGO_SHA1;

      //The hash algorithm used for PRF operations can also be used for signing
      if(context->prfHashAlgo == SHA256_HASH_ALGO)
      {
         //DSA with SHA-256 is supported
         supportedSignAlgos->value[n].signature = TLS_SIGN_ALGO_DSA;
         supportedSignAlgos->value[n++].hash = TLS_HASH_ALGO_SHA256;
      }
#endif
#if (TLS_ECDSA_SIGN_SUPPORT == ENABLED)
      //ECDSA with SHA-1 is always supported
      supportedSignAlgos->value[n].signature = TLS_SIGN_ALGO_ECDSA;
      supportedSignAlgos->value[n++].hash = TLS_HASH_ALGO_SHA1;

      //The hash algorithm used for PRF operations can also be used for signing
      if(context->prfHashAlgo == SHA256_HASH_ALGO)
      {
         //ECDSA with SHA-256 is supported
         supportedSignAlgos->value[n].signature = TLS_SIGN_ALGO_ECDSA;
         supportedSignAlgos->value[n++].hash = TLS_HASH_ALGO_SHA256;
      }
#if (TLS_SHA384_SUPPORT == ENABLED)
      else if(context->prfHashAlgo == SHA384_HASH_ALGO)
      {
         //ECDSA with SHA-384 is supported
         supportedSignAlgos->value[n].signature = TLS_SIGN_ALGO_ECDSA;
         supportedSignAlgos->value[n++].hash = TLS_HASH_ALGO_SHA384;
      }
#endif
#endif
      //Fix the length of the list
      supportedSignAlgos->length = htons(n * sizeof(TlsSignHashAlgo));
      //Total length of the message
      *length += sizeof(TlsSignHashAlgos) + n * sizeof(TlsSignHashAlgo);
   }
#endif

   //Point to the list of the distinguished names of acceptable
   //certificate authorities
   certAuthorities = PTR_OFFSET(message, *length);
   //Total length of the message
   *length += sizeof(TlsCertAuthorities);

   //Point to the first certificate authority
   p = certAuthorities->value;
   //Length of the list in bytes
   n = 0;

   //Point to the first trusted CA certificate
   pemCert = context->trustedCaList;
   //Get the total length, in bytes, of the trusted CA list
   pemCertLength = context->trustedCaListLen;

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

   //Allocate a memory buffer to store X.509 certificate info
   certInfo = tlsAllocMem(sizeof(X509CertificateInfo));

   //Successful memory allocation?
   if(certInfo != NULL)
   {
      //Loop through the list of trusted CA certificates
      while(pemCertLength > 0)
      {
         //Decode PEM certificate
         error = pemReadCertificate(&pemCert, &pemCertLength,
            &derCert, &derCertSize, &derCertLength);

         //Any error to report?
         if(error)
         {
            //End of file detected
            error = NO_ERROR;
            break;
         }

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

         //Total length of the message
         *length += certInfo->subject.rawDataLen + 2;

         //Prevent the buffer from overflowing
         if(*length > context->txRecordMaxLen)
         {
            //Report an error
            error = ERROR_MESSAGE_TOO_LONG;
            break;
         }

         //Each distinguished name is preceded by a 2-byte length field
         STORE16BE(certInfo->subject.rawDataLen, p);
         //The distinguished name shall be DER encoded
         memcpy(p + 2, certInfo->subject.rawData, certInfo->subject.rawDataLen);

         //Advance data pointer
         p += certInfo->subject.rawDataLen + 2;
         //Adjust the length of the list
         n += certInfo->subject.rawDataLen + 2;
      }

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

      //Fix the length of the list
      certAuthorities->length = htons(n);

      //Fix the length field
      STORE24BE(*length - sizeof(TlsHandshake), message->length);
   }
   else
   {
      //Report an error
      error = ERROR_OUT_OF_MEMORY;
   }

   //Return status code
   return error;
}


/**
 * @brief Format ServerHelloDone message
 * @param[in] context Pointer to the TLS context
 * @param[out] message Buffer where to format the ServerHelloDone message
 * @param[out] length Length of the resulting ServerHelloDone message
 * @return Error code
 **/

error_t tlsFormatServerHelloDone(TlsContext *context,
   TlsServerHelloDone *message, size_t *length)
{
   //Handshake message type
   message->msgType = TLS_TYPE_SERVER_HELLO_DONE;

   //The ServerHelloDone message does not contain any data
   STORE24BE(0, message->length);

   //Length of the complete handshake message
   *length = sizeof(TlsHandshake);

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Parse ClientHello message
 *
 * When a client first connects to a server, it is required to send
 * the ClientHello as its first message. The client can also send a
 * ClientHello in response to a HelloRequest or on its own initiative
 * in order to renegotiate the security parameters in an existing
 * connection
 *
 * @param[in] context Pointer to the TLS context
 * @param[in] message Incoming ClientHello message to parse
 * @param[in] length Message length
 * @return Error code
 **/

error_t tlsParseClientHello(TlsContext *context, const TlsClientHello *message, size_t length)
{
   error_t error;
   size_t i;
   size_t j;
   size_t k;
   size_t n;
   bool_t acceptable;
   uint8_t certType;
   const uint8_t *p;
   const TlsCipherSuites *cipherSuites;
   const TlsCompressionMethods *compressionMethods;
   const TlsExtension *extension;
   const TlsSignHashAlgos *supportedSignAlgos;
   const TlsEllipticCurveList *curveList;

   //Debug message
   TRACE_INFO("ClientHello message received (%" PRIuSIZE " bytes)...\r\n", length);
   TRACE_DEBUG_ARRAY("  ", message, length);

   //Check the length of the ClientHello message
   if(length < sizeof(TlsClientHello))
      return ERROR_DECODING_FAILED;

   //Check current state
   if(context->state != TLS_STATE_CLIENT_HELLO)
      return ERROR_UNEXPECTED_MESSAGE;

   //Point to the session ID
   p = (uint8_t *) message + sizeof(TlsClientHello);
   //Remaining bytes to process
   n = length - sizeof(TlsClientHello);

   //Check the length of the session ID
   if(message->sessionIdLength > n)
      return ERROR_DECODING_FAILED;
   if(message->sessionIdLength > 32)
      return ERROR_ILLEGAL_PARAMETER;

   //Debug message
   TRACE_INFO("Session ID (%" PRIu8 " bytes):\r\n", message->sessionIdLength);
   TRACE_INFO_ARRAY("  ", message->sessionId, message->sessionIdLength);

   //Point to the next field
   p += message->sessionIdLength;
   //Remaining bytes to process
   n -= message->sessionIdLength;

   //Malformed ClientHello message?
   if(n < sizeof(TlsCipherSuites))
      return ERROR_DECODING_FAILED;

   //List of cryptographic algorithms supported by the client
   cipherSuites = (TlsCipherSuites *) p;
   //Remaining bytes to process
   n -= sizeof(TlsCipherSuites);

   //Check the length of the list
   if(ntohs(cipherSuites->length) < 2)
      return ERROR_ILLEGAL_PARAMETER;
   if(ntohs(cipherSuites->length) > n)
      return ERROR_DECODING_FAILED;

   //Get the number of cipher suite identifiers present in the list
   k = ntohs(cipherSuites->length) / 2;

   //Debug message
   TRACE_DEBUG("Cipher suites:\r\n");

   //Dump the list of cipher suites
   for(i = 0; i < k; i++)
   {
      //Debug message
      TRACE_DEBUG("  0x%04" PRIX16 " (%s)\r\n", ntohs(cipherSuites->value[i]),
         tlsGetCipherSuiteName(ntohs(cipherSuites->value[i])));
   }

   //Point to the next field
   p += sizeof(TlsCipherSuites) + ntohs(cipherSuites->length);
   //Remaining bytes to process
   n -= ntohs(cipherSuites->length);

   //Malformed ClientHello message?
   if(n < sizeof(TlsCompressionMethods))
      return ERROR_DECODING_FAILED;

   //List of compression algorithms supported by the client
   compressionMethods = (TlsCompressionMethods *) p;
   //Remaining bytes to process
   n -= sizeof(TlsCompressionMethods);

   //Check the length of the list
   if(compressionMethods->length < 1)
      return ERROR_ILLEGAL_PARAMETER;
   if(compressionMethods->length > n)
      return ERROR_DECODING_FAILED;

   //Point to the next field
   p += sizeof(TlsCompressionMethods) + compressionMethods->length;
   //Remaining bytes to process
   n -= compressionMethods->length;

   //Parse the list of extensions offered by the client
   extension = tlsGetExtension(p, n, TLS_EXT_ELLIPTIC_CURVES);

   //The EllipticCurves extension was found?
   if(extension)
   {
      //This extension allows a client to enumerate the elliptic curves it supports
      curveList = (TlsEllipticCurveList *) extension->value;

      //Check the length of the list
      if(ntohs(extension->length) < sizeof(TlsEllipticCurveList))
         return ERROR_DECODING_FAILED;
      if(ntohs(extension->length) < (sizeof(TlsEllipticCurveList) + ntohs(curveList->length)))
         return ERROR_DECODING_FAILED;
   }
   else
   {
      //The client may omit the SignatureAlgorithms extension
      curveList = NULL;
   }

   //Parse the list of extensions offered by the client
   extension = tlsGetExtension(p, n, TLS_EXT_EC_POINT_FORMATS);

   //The EcPointFormats extension was found?
   if(extension)
      context->ecPointFormatExtFound = TRUE;
   else
      context->ecPointFormatExtFound = FALSE;

   //Parse the list of extensions offered by the client
   extension = tlsGetExtension(p, n, TLS_EXT_SIGNATURE_ALGORITHMS);

   //The SignatureAlgorithms extension was found?
   if(extension)
   {
      //Point to the list of supported hash/signature algorithm pairs
      supportedSignAlgos = (TlsSignHashAlgos *) extension->value;

      //Check the length of the list
      if(ntohs(extension->length) < sizeof(TlsSignHashAlgos))
         return ERROR_DECODING_FAILED;
      if(ntohs(extension->length) < (sizeof(TlsSignHashAlgos) + ntohs(supportedSignAlgos->length)))
         return ERROR_DECODING_FAILED;
   }
   else
   {
      //The client may omit the SignatureAlgorithms extension
      supportedSignAlgos = NULL;
   }

   //Get the version the client wishes to use during this session
   context->clientVersion = ntohs(message->clientVersion);

   //If a TLS server receives a ClientHello containing a version number
   //greater than the highest version supported by the server, it must
   //reply according to the highest version supported by the server
   error = tlsSetVersion(context, MIN(context->clientVersion, TLS_MAX_VERSION));
   //The specified TLS version is not supported?
   if(error)
      return error;

   //Save client random value
   context->clientRandom = message->random;

#if (TLS_SESSION_RESUME_SUPPORT == ENABLED)
   //Check whether session caching is supported
   if(context->cache != NULL)
   {
      //If the session ID was non-empty, the server will look in
      //its session cache for a match
      TlsSession *session = tlsFindCache(context->cache,
         message->sessionId, message->sessionIdLength);

      //Check whether a matching entry has been found in the cache
      if(session != NULL)
      {
         //Restore session parameters
         tlsRestoreSession(context, session);

         //Select the relevant cipher suite
         error = tlsSetCipherSuite(context, session->cipherSuite);
         //Any error to report?
         if(error)
            return error;

         //Perform abbreviated handshake
         context->resume = TRUE;
      }
      else
      {
         //Generate a new random ID
         error = context->prngAlgo->read(context->prngContext, context->sessionId, 32);
         //Any error to report?
         if(error)
            return error;

         //Session ID is limited to 32 bytes
         context->sessionIdLen = 32;
         //Perform a full handshake
         context->resume = FALSE;
      }
   }
   else
#endif
   {
      //This session cannot be resumed
      context->sessionIdLen = 0;
      //Perform a full handshake
      context->resume = FALSE;
   }

   //Full handshake?
   if(!context->resume)
   {
      //Get the size of the cipher suite list
      k = ntohs(cipherSuites->length) / 2;

      //The cipher suite list contains the combinations of cryptographic algorithms
      //supported by the client in order of the client's preference
      for(i = 0; i < k; i++)
      {
         //Check whether the current cipher suite is supported
         error = tlsSetCipherSuite(context, ntohs(cipherSuites->value[i]));

         //Successful processing?
         if(!error)
         {
            //ECC cipher suite?
            if(context->keyExchMethod == TLS_KEY_EXCH_ECDH_ANON ||
               context->keyExchMethod == TLS_KEY_EXCH_ECDHE_RSA ||
               context->keyExchMethod == TLS_KEY_EXCH_ECDHE_ECDSA ||
               context->keyExchMethod == TLS_KEY_EXCH_ECDHE_PSK)
            {
               //One of the proposed ECC cipher suites must be negotiated only
               //if the server can successfully complete the handshake while
               //using the curves and point formats supported by the client
               error = tlsSelectNamedCurve(context, curveList);
            }
         }

         //Successful processing?
         if(!error)
         {
            //The server requires a valid certificate whenever the agreed-upon
            //key exchange method uses certificates for authentication
            if(context->keyExchMethod == TLS_KEY_EXCH_RSA ||
               context->keyExchMethod == TLS_KEY_EXCH_DHE_RSA ||
               context->keyExchMethod == TLS_KEY_EXCH_ECDHE_RSA ||
               context->keyExchMethod == TLS_KEY_EXCH_RSA_PSK)
            {
               //RSA, DHE_RSA, ECDHE_RSA and RAS_PSK key exchange methods
               //require a RSA certificate
               certType = TLS_CERT_RSA_SIGN;
            }
            else if(context->keyExchMethod == TLS_KEY_EXCH_DHE_DSS)
            {
               //DHE_DSS key exchange method requires a DSA certificate
               certType = TLS_CERT_DSS_SIGN;
            }
            else if(context->keyExchMethod == TLS_KEY_EXCH_ECDHE_ECDSA)
            {
               //ECDHE_ECDSA key exchange method requires an ECDSA certificate
               certType = TLS_CERT_ECDSA_SIGN;
            }
            else
            {
               //DH_anon and ECDH_anon key exchange methods do not require any certificate
               certType = TLS_CERT_NONE;
            }

            //Check whether a certificate is required
            if(certType != TLS_CERT_NONE)
            {
               //Do not accept the specified cipher suite unless a suitable
               //certificate has been previously loaded by the user
               error = ERROR_NO_CERTIFICATE;

               //Loop through the list of available certificates
               for(j = 0; j < context->numCerts; j++)
               {
                  //Check whether the current certificate is acceptable
                  acceptable = tlsIsCertificateAcceptable(&context->certs[j],
                     &certType, 1, supportedSignAlgos, curveList, NULL);

                  //Is the certificate suitable for the selected cipher suite?
                  if(acceptable)
                  {
                     //The hash algorithm to be used when generating signatures must be
                     //one of those present in the SignatureAlgorithms extension
                     error = tlsSelectSignHashAlgo(context,
                        context->certs[j].signAlgo, supportedSignAlgos);

                     //If all the requirements were met, the certificate can be
                     //used in conjunction with the selected cipher suite
                     if(!error)
                     {
                        context->cert = &context->certs[j];
                        break;
                     }
                  }
               }
            }
         }

         //If the list contains cipher suites the server does not recognize,
         //support, or wish to use, the server must ignore those cipher
         //suites, and process the remaining ones as usual
         if(!error)
            break;
      }

      //If no acceptable choices are presented, return a handshake failure
      //alert and close the connection
      if(error)
         return ERROR_HANDSHAKE_FAILED;

      //The list of the compression methods supported by the client
      //is sorted by client preference
      for(i = 0; i < compressionMethods->length; i++)
      {
         //Check whether the algorithm to be used for data compression is supported
         error = tlsSetCompressionMethod(context, compressionMethods->value[i]);

         //If the compression method is not supported, process the remaining ones
         if(!error)
            break;
      }

      //If no compression algorithm is acceptable, return a handshake failure
      //alert and close the connection
      if(error)
         return ERROR_HANDSHAKE_FAILED;
   }

   //Initialize handshake message hashing
   error = tlsInitHandshakeHash(context);
   //Any error to report?
   if(error)
      return error;

   //Update the hash value with the incoming handshake message
   tlsUpdateHandshakeHash(context, message, length);

   //Prepare to send ServerHello message...
   context->state = TLS_STATE_SERVER_HELLO;
   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Parse ClientKeyExchange message
 *
 * This message is always sent by the client. It must immediately
 * follow the client Certificate message, if it is sent. Otherwise,
 * it must be the first message sent by the client after it receives
 * the ServerHelloDone message
 *
 * @param[in] context Pointer to the TLS context
 * @param[in] message Incoming ClientKeyExchange message to parse
 * @param[in] length Message length
 * @return Error code
 **/

error_t tlsParseClientKeyExchange(TlsContext *context, const TlsClientKeyExchange *message, size_t length)
{
   error_t error;
   size_t n;
   const uint8_t *p;

   //Debug message
   TRACE_INFO("ClientKeyExchange message received (%" PRIuSIZE " bytes)...\r\n", length);
   TRACE_DEBUG_ARRAY("  ", message, length);

   //Check the length of the ClientKeyExchange message
   if(length < sizeof(TlsClientKeyExchange))
      return ERROR_DECODING_FAILED;

   //Check current state
   if(context->state == TLS_STATE_CLIENT_CERTIFICATE)
   {
      //A an non-anonymous server can optionally request a certificate from the client
      if(context->keyExchMethod == TLS_KEY_EXCH_RSA ||
         context->keyExchMethod == TLS_KEY_EXCH_DHE_RSA ||
         context->keyExchMethod == TLS_KEY_EXCH_DHE_DSS ||
         context->keyExchMethod == TLS_KEY_EXCH_ECDHE_RSA ||
         context->keyExchMethod == TLS_KEY_EXCH_ECDHE_ECDSA ||
         context->keyExchMethod == TLS_KEY_EXCH_RSA_PSK)
      {
         //If client authentication is required by the server for the handshake
         //to continue, it may respond with a fatal handshake failure alert
         if(context->clientAuthMode == TLS_CLIENT_AUTH_REQUIRED)
            return ERROR_HANDSHAKE_FAILED;
      }
   }
   else if(context->state != TLS_STATE_CLIENT_KEY_EXCHANGE)
   {
      //Send a fatal alert to the client
      return ERROR_UNEXPECTED_MESSAGE;
   }

   //Update the hash value with the incoming handshake message
   tlsUpdateHandshakeHash(context, message, length);

   //Point to the body of the handshake message
   p = message->data;
   //Remaining bytes to process
   length -= sizeof(TlsClientKeyExchange);

   //PSK key exchange method?
   if(context->keyExchMethod == TLS_KEY_EXCH_PSK ||
      context->keyExchMethod == TLS_KEY_EXCH_RSA_PSK ||
      context->keyExchMethod == TLS_KEY_EXCH_DHE_PSK ||
      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_PSK)
   {
      //The PSK identity is sent in cleartext
      error = tlsParsePskIdentity(context, p, length, &n);
      //Any error to report?
      if(error)
         return error;

      //Point to the next field
      p += n;
      //Remaining bytes to process
      length -= n;
   }

   //RSA, Diffie-Hellman or ECDH key exchange method?
   if(context->keyExchMethod != TLS_KEY_EXCH_PSK)
   {
      //Parse client's key exchange parameters
      error = tlsParseClientKeyParams(context, p, length, &n);
      //Any error to report?
      if(error)
         return error;

      //Point to the next field
      p += n;
      //Remaining bytes to process
      length -= n;
   }

   //If the amount of data in the message does not precisely match the format
   //of the ClientKeyExchange message, then send a fatal alert
   if(length != 0)
      return ERROR_DECODING_FAILED;

   //PSK key exchange method?
   if(context->keyExchMethod == TLS_KEY_EXCH_PSK ||
      context->keyExchMethod == TLS_KEY_EXCH_RSA_PSK ||
      context->keyExchMethod == TLS_KEY_EXCH_DHE_PSK ||
      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_PSK)
   {
      //Generate premaster secret
      error = tlsGeneratePskPremasterSecret(context);
      //Any error to report?
      if(error)
         return error;
   }

   //Derive session keys from the premaster secret
   error = tlsGenerateKeys(context);
   //Unable to generate key material?
   if(error)
      return error;

   //Update FSM state
   if(context->peerCertType != TLS_CERT_NONE)
      context->state = TLS_STATE_CERTIFICATE_VERIFY;
   else
      context->state = TLS_STATE_CLIENT_CHANGE_CIPHER_SPEC;

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Parse CertificateVerify message
 *
 * The CertificateVerify message is used to provide explicit verification
 * of a client certificate. This message is only sent following a client
 * certificate that has signing capability
 *
 * @param[in] context Pointer to the TLS context
 * @param[in] message Incoming CertificateVerify message to parse
 * @param[in] length Message length
 * @return Error code
 **/

error_t tlsParseCertificateVerify(TlsContext *context, const TlsCertificateVerify *message, size_t length)
{
   error_t error;
   size_t n;

   //Debug message
   TRACE_INFO("CertificateVerify message received (%" PRIuSIZE " bytes)...\r\n", length);
   TRACE_DEBUG_ARRAY("  ", message, length);

   //Check the length of the CertificateVerify message
   if(length < sizeof(TlsCertificateVerify))
      return ERROR_DECODING_FAILED;

   //Check current state
   if(context->state != TLS_STATE_CERTIFICATE_VERIFY)
      return ERROR_UNEXPECTED_MESSAGE;

   //Remaining bytes to process
   n = length - sizeof(TlsCertificateVerify);

#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_MIN_VERSION <= TLS_VERSION_1_1)
   //SSL 3.0, TLS 1.0 or TLS 1.1 currently selected?
   if(context->version <= TLS_VERSION_1_1)
   {
      //Point to the digitally-signed element
      TlsDigitalSignature *signature = (TlsDigitalSignature *) message->signature;

      //Check the length of the digitally-signed element
      if(n < sizeof(TlsDigitalSignature))
         return ERROR_DECODING_FAILED;
      if(n < (sizeof(TlsDigitalSignature) + ntohs(signature->length)))
         return ERROR_DECODING_FAILED;

#if (TLS_RSA_SIGN_SUPPORT == ENABLED)
      //The client's certificate contains a valid RSA public key?
      if(context->peerCertType == TLS_CERT_RSA_SIGN)
      {
         //Digest all the handshake messages starting at ClientHello (using MD5)
         error = tlsFinalizeHandshakeHash(context, MD5_HASH_ALGO,
            context->handshakeMd5Context, "", context->verifyData);
         //Any error to report?
         if(error)
            return error;

         //Digest all the handshake messages starting at ClientHello (using SHA-1)
         error = tlsFinalizeHandshakeHash(context, SHA1_HASH_ALGO,
            context->handshakeSha1Context, "", context->verifyData + MD5_DIGEST_SIZE);
         //Any error to report?
         if(error)
            return error;

         //Verify RSA signature using client's public key
         error = tlsVerifyRsaSignature(&context->peerRsaPublicKey,
            context->verifyData, signature->value, ntohs(signature->length));
      }
      else
#endif
#if (TLS_DSA_SIGN_SUPPORT == ENABLED)
      //The client's certificate contains a valid DSA public key?
      if(context->peerCertType == TLS_CERT_DSS_SIGN)
      {
         //Digest all the handshake messages starting at ClientHello
         error = tlsFinalizeHandshakeHash(context, SHA1_HASH_ALGO,
            context->handshakeSha1Context, "", context->verifyData);
         //Any error to report?
         if(error)
            return error;

         //Verify DSA signature using client's public key
         error = tlsVerifyDsaSignature(&context->peerDsaPublicKey, context->verifyData,
            SHA1_DIGEST_SIZE, signature->value, ntohs(signature->length));
      }
      else
#endif
#if (TLS_ECDSA_SIGN_SUPPORT == ENABLED)
      //The client's certificate contains a valid ECDSA public key?
      if(context->peerCertType == TLS_CERT_ECDSA_SIGN)
      {
         //Digest all the handshake messages starting at ClientHello
         error = tlsFinalizeHandshakeHash(context, SHA1_HASH_ALGO,
            context->handshakeSha1Context, "", context->verifyData);
         //Any error to report?
         if(error)
            return error;

         //Verify ECDSA signature using client's public key
         error = tlsVerifyEcdsaSignature(&context->peerEcParams, &context->peerEcPublicKey,
            context->verifyData, SHA1_DIGEST_SIZE, signature->value, ntohs(signature->length));
      }
      else
#endif
      //Invalid signature algorithm?
      {
         //Report an error
         error = ERROR_UNSUPPORTED_SIGNATURE_ALGO;
      }
   }
   else
#endif
#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_MIN_VERSION <= TLS_VERSION_1_2)
   //TLS 1.2 currently selected?
   if(context->version == TLS_VERSION_1_2)
   {
      const HashAlgo *hashAlgo;

      //Point to the digitally-signed element
      TlsDigitalSignature2 *signature = (TlsDigitalSignature2 *) message->signature;

      //Check the length of the digitally-signed element
      if(n < sizeof(TlsDigitalSignature2))
         return ERROR_DECODING_FAILED;
      if(n < (sizeof(TlsDigitalSignature2) + ntohs(signature->length)))
         return ERROR_DECODING_FAILED;

      //Retrieve the hash algorithm used for signing
      hashAlgo = tlsGetHashAlgo(signature->algorithm.hash);

      //Digest all the handshake messages starting at ClientHello
      if(hashAlgo == SHA1_HASH_ALGO)
      {
         //Use SHA-1 hash algorithm
         error = tlsFinalizeHandshakeHash(context, SHA1_HASH_ALGO,
            context->handshakeSha1Context, "", context->verifyData);
      }
      else if(hashAlgo == context->prfHashAlgo)
      {
         //Use PRF hash algorithm (SHA-256 or SHA-384)
         error = tlsFinalizeHandshakeHash(context, hashAlgo,
            context->handshakeHashContext, "", context->verifyData);
      }
      else
      {
         //The specified hash algorithm is not supported
         error = ERROR_UNSUPPORTED_SIGNATURE_ALGO;
      }

      //Any error to report?
      if(error)
         return error;

#if (TLS_RSA_SIGN_SUPPORT == ENABLED)
      //The client's certificate contains a valid RSA public key?
      if(signature->algorithm.signature == TLS_SIGN_ALGO_RSA)
      {
         //Use the signature verification algorithm defined in PKCS #1 v1.5
         error = rsassaPkcs1v15Verify(&context->peerRsaPublicKey, hashAlgo,
            context->verifyData, signature->value, ntohs(signature->length));
      }
      else
#endif
#if (TLS_DSA_SIGN_SUPPORT == ENABLED)
      //The client's certificate contains a valid DSA public key?
      if(signature->algorithm.signature == TLS_SIGN_ALGO_DSA)
      {
         //Verify DSA signature using client's public key
         error = tlsVerifyDsaSignature(&context->peerDsaPublicKey, context->verifyData,
            hashAlgo->digestSize, signature->value, ntohs(signature->length));
      }
      else
#endif
#if (TLS_ECDSA_SIGN_SUPPORT == ENABLED)
      //The client's certificate contains a valid ECDSA public key?
      if(signature->algorithm.signature == TLS_SIGN_ALGO_ECDSA)
      {
         //Verify ECDSA signature using client's public key
         error = tlsVerifyEcdsaSignature(&context->peerEcParams, &context->peerEcPublicKey,
            context->verifyData, hashAlgo->digestSize, signature->value, ntohs(signature->length));
      }
      else
#endif
      //Invalid signature algorithm?
      {
         //Report an error
         error = ERROR_UNSUPPORTED_SIGNATURE_ALGO;
      }
   }
   else
#endif
   {
      //The negotiated TLS version is not valid
      error = ERROR_INVALID_VERSION;
   }

   //Update the hash value with the incoming handshake message
   tlsUpdateHandshakeHash(context, message, length);

   //Prepare to receive a ChangeCipherSpec message...
   context->state = TLS_STATE_CLIENT_CHANGE_CIPHER_SPEC;
   //Return status code
   return error;
}

#endif