Webserver+3d print

Dependents:   Nucleo

cyclone_ssl/tls_server_misc.c

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

File content as of revision 0:8918a71cdbe9:

/**
 * @file tls_server_misc.c
 * @brief Helper functions (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.
 *
 * @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 "pem.h"
#include "debug.h"

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


/**
 * @brief Format PSK identity hint
 * @param[in] context Pointer to the TLS context
 * @param[in] p Output stream where to write the PSK identity hint
 * @param[out] written Total number of bytes that have been written
 * @return Error code
 **/

error_t tlsFormatPskIdentityHint(TlsContext *context,
   uint8_t *p, size_t *written)
{
   size_t n;
   TlsPskIdentityHint *pskIdentityHint;

   //Point to the PSK identity hint
   pskIdentityHint = (TlsPskIdentityHint *) p;

   //Initialize length field
   n = 0;

#if (TLS_PSK_SUPPORT == ENABLED || TLS_RSA_PSK_SUPPORT == ENABLED || \
   TLS_DHE_PSK_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
   //Any PSK identity hint defined?
   if(context->pskIdentityHint != NULL)
   {
      //Determine the length of the PSK identity hint
      n = strlen(context->pskIdentityHint);
      //Copy PSK identity hint
      memcpy(pskIdentityHint->value, context->pskIdentityHint, n);
   }
#endif

   //The PSK identity hint is preceded by a 2-byte length field
   pskIdentityHint->length = htons(n);

   //Total number of bytes that have been written
   *written = sizeof(TlsPskIdentityHint) + n;

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Format server's key exchange parameters
 * @param[in] context Pointer to the TLS context
 * @param[in] p Output stream where to write the PSK identity hint
 * @param[out] written Total number of bytes that have been written
 * @return Error code
 **/

error_t tlsFormatServerKeyParams(TlsContext *context,
    uint8_t *p, size_t *written)
{
   error_t error;

   //Initialize status code
   error = NO_ERROR;

   //Total number of bytes that have been written
   *written = 0;

#if (TLS_DH_ANON_SUPPORT == ENABLED || TLS_DHE_RSA_SUPPORT == ENABLED || \
   TLS_DHE_DSS_SUPPORT == ENABLED || TLS_DHE_PSK_SUPPORT == ENABLED)
   //Diffie-Hellman 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)
   {
      size_t n;

      //Generate an ephemeral key pair
      error = dhGenerateKeyPair(&context->dhContext,
         context->prngAlgo, context->prngContext);

      //Check status code
      if(!error)
      {
         //Debug message
         TRACE_DEBUG("Diffie-Hellman parameters:\r\n");
         TRACE_DEBUG("  Prime modulus:\r\n");
         TRACE_DEBUG_MPI("    ", &context->dhContext.params.p);
         TRACE_DEBUG("  Generator:\r\n");
         TRACE_DEBUG_MPI("    ", &context->dhContext.params.g);
         TRACE_DEBUG("  Server public value:\r\n");
         TRACE_DEBUG_MPI("    ", &context->dhContext.ya);

         //Encode the prime modulus to an opaque vector
         error = tlsWriteMpi(&context->dhContext.params.p, p, &n);
      }

      //Check status code
      if(!error)
      {
         //Advance data pointer
         p += n;
         //Total number of bytes that have been written
         *written += n;

         //Encode the generator to an opaque vector
         error = tlsWriteMpi(&context->dhContext.params.g, p, &n);
      }

      //Check status code
      if(!error)
      {
         //Advance data pointer
         p += n;
         //Total number of bytes that have been written
         *written += n;

         //Encode the server's public value to an opaque vector
         error = tlsWriteMpi(&context->dhContext.ya, p, &n);
      }

      //Check status code
      if(!error)
      {
         //Advance data pointer
         p += n;
         //Adjust the length of the key exchange parameters
         *written += n;
      }
   }
   else
#endif
#if (TLS_ECDH_ANON_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED || \
   TLS_ECDHE_ECDSA_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
   //ECDH key exchange method?
   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)
   {
      size_t n;
      const EcCurveInfo *curveInfo;

      //Retrieve the elliptic curve to be used
      curveInfo = tlsGetCurveInfo(context->namedCurve);

      //Make sure the elliptic curve is supported
      if(curveInfo != NULL)
      {
         //Load EC domain parameters
         error = ecLoadDomainParameters(&context->ecdhContext.params,
            curveInfo);

         //Check status code
         if(!error)
         {
            //Generate an ephemeral key pair
            error = ecdhGenerateKeyPair(&context->ecdhContext,
               context->prngAlgo, context->prngContext);
         }

         //Check status code
         if(!error)
         {
            //Debug message
            TRACE_DEBUG("  Server public key X:\r\n");
            TRACE_DEBUG_MPI("    ", &context->ecdhContext.qa.x);
            TRACE_DEBUG("  Server public key Y:\r\n");
            TRACE_DEBUG_MPI("    ", &context->ecdhContext.qa.y);

            //Set the type of the elliptic curve domain parameters
            *p = TLS_EC_CURVE_TYPE_NAMED_CURVE;

            //Advance data pointer
            p += sizeof(uint8_t);
            //Total number of bytes that have been written
            *written += sizeof(uint8_t);

            //Write elliptic curve identifier
            STORE16BE(context->namedCurve, p);

            //Advance data pointer
            p += sizeof(uint16_t);
            //Total number of bytes that have been written
            *written += sizeof(uint16_t);

            //Write server's public key
            error = tlsWriteEcPoint(&context->ecdhContext.params,
               &context->ecdhContext.qa, p, &n);
         }

         //Check status code
         if(!error)
         {
            //Advance data pointer
            p +=n;
            //Total number of bytes that have been written
            *written += n;
         }
      }
      else
      {
         //The specified elliptic curve is not supported
         error = ERROR_FAILURE;
      }
   }
   else
#endif
   //Any other exchange method?
   {
      //It is not legal to send the ServerKeyExchange message when a key
      //exchange method other than DHE_DSS, DHE_RSA, DH_anon, ECDHE_RSA,
      //ECDHE_ECDSA or ECDH_anon is selected
      error = ERROR_FAILURE;
   }

   //Return status code
   return error;
}


/**
 * @brief Generate signature over the server's key exchange parameters
 * @param[in] context Pointer to the TLS context
 * @param[in] p Output stream where to write the PSK identity hint
 * @param[in] params Pointer to the server's key exchange parameters
 * @param[in] paramsLen Length of the server's key exchange parameters
 * @param[out] written Total number of bytes that have been written
 * @return Error code
 **/

error_t tlsGenerateServerKeySignature(TlsContext *context,
   uint8_t *p, const uint8_t *params, size_t paramsLen, size_t *written)
{
   error_t error;

   //Initialize status code
   error = NO_ERROR;

   //Total number of bytes that have been written
   *written = 0;

#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)
   {
      TlsDigitalSignature *signature;

      //Point to the digitally-signed element
      signature = (TlsDigitalSignature *) p;

#if (TLS_DHE_RSA_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED)
      //Check whether DHE_RSA or ECDHE_RSA key exchange method is currently used
      if(context->keyExchMethod == TLS_KEY_EXCH_DHE_RSA ||
         context->keyExchMethod == TLS_KEY_EXCH_ECDHE_RSA)
      {
         Md5Context *md5Context;
         Sha1Context *sha1Context;
         RsaPrivateKey privateKey;

         //Initialize RSA private key
         rsaInitPrivateKey(&privateKey);

         //Allocate a memory buffer to hold the MD5 context
         md5Context = tlsAllocMem(sizeof(Md5Context));

         //Successful memory allocation?
         if(md5Context != NULL)
         {
            //Compute MD5(ClientHello.random + ServerHello.random + ServerDhParams)
            md5Init(md5Context);
            md5Update(md5Context, context->random, 64);
            md5Update(md5Context, params, paramsLen);
            md5Final(md5Context, context->verifyData);

            //Release previously allocated memory
            tlsFreeMem(md5Context);
         }
         else
         {
            //Failed to allocate memory
            error = ERROR_OUT_OF_MEMORY;
         }

         //Check status code
         if(!error)
         {
            //Allocate a memory buffer to hold the SHA-1 context
            sha1Context = tlsAllocMem(sizeof(Sha1Context));

            //Successful memory allocation?
            if(sha1Context != NULL)
            {
               //Compute SHA(ClientHello.random + ServerHello.random + ServerDhParams)
               sha1Init(sha1Context);
               sha1Update(sha1Context, context->random, 64);
               sha1Update(sha1Context, params, paramsLen);
               sha1Final(sha1Context, context->verifyData + MD5_DIGEST_SIZE);

               //Release previously allocated memory
               tlsFreeMem(sha1Context);
            }
            else
            {
               //Failed to allocate memory
               error = ERROR_OUT_OF_MEMORY;
            }
         }

         //Check status code
         if(!error)
         {
            //Decode the PEM structure that holds the RSA private key
            error = pemReadRsaPrivateKey(context->cert->privateKey,
               context->cert->privateKeyLength, &privateKey);
         }

         //Check status code
         if(!error)
         {
            //Sign the key exchange parameters using RSA
            error = tlsGenerateRsaSignature(&privateKey,
               context->verifyData, signature->value, written);
         }

         //Release previously allocated resources
         rsaFreePrivateKey(&privateKey);
      }
      else
#endif
#if (TLS_DHE_DSS_SUPPORT == ENABLED)
      //Check whether DHE_DSS key exchange method is currently used
      if(context->keyExchMethod == TLS_KEY_EXCH_DHE_DSS)
      {
         Sha1Context *sha1Context;
         DsaPrivateKey privateKey;

         //Initialize DSA private key
         dsaInitPrivateKey(&privateKey);

         //Allocate a memory buffer to hold the SHA-1 context
         sha1Context = tlsAllocMem(sizeof(Sha1Context));

         //Successful memory allocation?
         if(sha1Context != NULL)
         {
            //Compute SHA(ClientHello.random + ServerHello.random + ServerDhParams)
            sha1Init(sha1Context);
            sha1Update(sha1Context, context->random, 64);
            sha1Update(sha1Context, params, paramsLen);
            sha1Final(sha1Context, context->verifyData);

            //Release previously allocated memory
            tlsFreeMem(sha1Context);
         }
         else
         {
            //Failed to allocate memory
            error = ERROR_OUT_OF_MEMORY;
         }

         //Check status code
         if(!error)
         {
            //Decode the PEM structure that holds the DSA private key
            error = pemReadDsaPrivateKey(context->cert->privateKey,
               context->cert->privateKeyLength, &privateKey);
         }

         //Check status code
         if(!error)
         {
            //Sign the key exchange parameters using DSA
            error = tlsGenerateDsaSignature(context->prngAlgo,
               context->prngContext, &privateKey, context->verifyData,
               SHA1_DIGEST_SIZE, signature->value, written);
         }

         //Release previously allocated resources
         dsaFreePrivateKey(&privateKey);
      }
      else
#endif
#if (TLS_ECDHE_ECDSA_SUPPORT == ENABLED)
      //Check whether ECDHE_ECDSA key exchange method is currently used
      if(context->keyExchMethod == TLS_KEY_EXCH_ECDHE_ECDSA)
      {
         Sha1Context *sha1Context;
         EcDomainParameters ecParams;
         Mpi privateKey;

         //Initialize EC domain parameters
         ecInitDomainParameters(&ecParams);
         //Initialize EC private key
         mpiInit(&privateKey);

         //Allocate a memory buffer to hold the SHA-1 context
         sha1Context = tlsAllocMem(sizeof(Sha1Context));

         //Successful memory allocation?
         if(sha1Context != NULL)
         {
            //Compute SHA(ClientHello.random + ServerHello.random + ServerDhParams)
            sha1Init(sha1Context);
            sha1Update(sha1Context, context->random, 64);
            sha1Update(sha1Context, params, paramsLen);
            sha1Final(sha1Context, context->verifyData);

            //Release previously allocated memory
            tlsFreeMem(sha1Context);
         }
         else
         {
            //Failed to allocate memory
            error = ERROR_OUT_OF_MEMORY;
         }

         //Check status code
         if(!error)
         {
            //Decode the PEM structure that holds the EC domain parameters
            error = pemReadEcParameters(context->cert->privateKey,
               context->cert->privateKeyLength, &ecParams);
         }

         //Check status code
         if(!error)
         {
            //Decode the PEM structure that holds the EC private key
            error = pemReadEcPrivateKey(context->cert->privateKey,
               context->cert->privateKeyLength, &privateKey);
         }

         //Check status code
         if(!error)
         {
            //Sign the key exchange parameters using ECDSA
            error = tlsGenerateEcdsaSignature(&ecParams, context->prngAlgo,
               context->prngContext, &privateKey, context->verifyData,
               SHA1_DIGEST_SIZE, signature->value, written);
         }

         //Release previously allocated resources
         ecFreeDomainParameters(&ecParams);
         mpiFree(&privateKey);
      }
      else
#endif
      //Invalid signature algorithm?
      {
         //Report an error
         error = ERROR_UNSUPPORTED_SIGNATURE_ALGO;
      }

      //Fix the length of the digitally-signed element
      signature->length = htons(*written);
      //Adjust the length of the signature
      *written += sizeof(TlsDigitalSignature);
   }
   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)
   {
      TlsDigitalSignature2 *signature;
      const HashAlgo *hashAlgo;
      HashContext *hashContext;

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

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

      //Make sure the hash algorithm is supported
      if(hashAlgo != NULL)
      {
         //Allocate a memory buffer to hold the hash context
         hashContext = tlsAllocMem(hashAlgo->contextSize);

         //Successful memory allocation?
         if(hashContext != NULL)
         {
            //Compute SHA(ClientHello.random + ServerHello.random + ServerDhParams)
            hashAlgo->init(hashContext);
            hashAlgo->update(hashContext, context->random, 64);
            hashAlgo->update(hashContext, params, paramsLen);
            hashAlgo->final(hashContext, NULL);

#if (TLS_DHE_RSA_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED)
            //Check whether DHE_RSA or ECDHE_RSA key exchange method is currently used
            if(context->keyExchMethod == TLS_KEY_EXCH_DHE_RSA ||
               context->keyExchMethod == TLS_KEY_EXCH_ECDHE_RSA)
            {
               RsaPrivateKey privateKey;

               //Initialize RSA private key
               rsaInitPrivateKey(&privateKey);

               //Set the relevant signature algorithm
               signature->algorithm.signature = TLS_SIGN_ALGO_RSA;
               signature->algorithm.hash = context->signHashAlgo;

               //Decode the PEM structure that holds the RSA private key
               error = pemReadRsaPrivateKey(context->cert->privateKey,
                  context->cert->privateKeyLength, &privateKey);

               //Check status code
               if(!error)
               {
                  //Use the signature algorithm defined in PKCS #1 v1.5
                  error = rsassaPkcs1v15Sign(&privateKey, hashAlgo,
                     hashContext->digest, signature->value, written);
               }

               //Release previously allocated resources
               rsaFreePrivateKey(&privateKey);
            }
            else
#endif
#if (TLS_DHE_DSS_SUPPORT == ENABLED)
            //Check whether DHE_DSS key exchange method is currently used
            if(context->keyExchMethod == TLS_KEY_EXCH_DHE_DSS)
            {
               DsaPrivateKey privateKey;

               //Initialize DSA private key
               dsaInitPrivateKey(&privateKey);

               //Set the relevant signature algorithm
               signature->algorithm.signature = TLS_SIGN_ALGO_DSA;
               signature->algorithm.hash = context->signHashAlgo;

               //Decode the PEM structure that holds the DSA private key
               error = pemReadDsaPrivateKey(context->cert->privateKey,
                  context->cert->privateKeyLength, &privateKey);

               //Check status code
               if(!error)
               {
                  //Sign the key exchange parameters using DSA
                  error = tlsGenerateDsaSignature(context->prngAlgo,
                     context->prngContext, &privateKey, hashContext->digest,
                     hashAlgo->digestSize, signature->value, written);
               }

               //Release previously allocated resources
               dsaFreePrivateKey(&privateKey);
            }
            else
#endif
#if (TLS_ECDHE_ECDSA_SUPPORT == ENABLED)
            //Check whether ECDHE_ECDSA key exchange method is currently used
            if(context->keyExchMethod == TLS_KEY_EXCH_ECDHE_ECDSA)
            {
               EcDomainParameters ecParams;
               Mpi privateKey;

               //Initialize EC domain parameters
               ecInitDomainParameters(&ecParams);
               //Initialize EC private key
               mpiInit(&privateKey);

               //Set the relevant signature algorithm
               signature->algorithm.signature = TLS_SIGN_ALGO_ECDSA;
               signature->algorithm.hash = context->signHashAlgo;

               //Decode the PEM structure that holds the EC domain parameters
               error = pemReadEcParameters(context->cert->privateKey,
                  context->cert->privateKeyLength, &ecParams);

               //Check status code
               if(!error)
               {
                  //Decode the PEM structure that holds the EC private key
                  error = pemReadEcPrivateKey(context->cert->privateKey,
                     context->cert->privateKeyLength, &privateKey);
               }

               //Check status code
               if(!error)
               {
                  //Sign the key exchange parameters using ECDSA
                  error = tlsGenerateEcdsaSignature(&ecParams, context->prngAlgo,
                     context->prngContext, &privateKey, hashContext->digest,
                     hashAlgo->digestSize, signature->value, written);
               }

               //Release previously allocated memory
               ecFreeDomainParameters(&ecParams);
               mpiFree(&privateKey);
            }
            else
#endif
            //Invalid signature algorithm?
            {
               //Report an error
               error = ERROR_UNSUPPORTED_SIGNATURE_ALGO;
            }

            //Release previously allocated memory
            tlsFreeMem(hashContext);
         }
         else
         {
            //Failed to allocate memory
            error = ERROR_OUT_OF_MEMORY;
         }
      }
      else
      {
         //Hash algorithm not supported
         error = ERROR_INVALID_SIGNATURE;
      }

      //Fix the length of the digitally-signed element
      signature->length = htons(*written);
      //Adjust the length of the message
      *written += sizeof(TlsDigitalSignature2);
   }
   else
#endif
   {
      //The negotiated TLS version is not valid
      error = ERROR_INVALID_VERSION;
   }

   //Return status code
   return error;
}


/**
 * @brief Parse PSK identity
 * @param[in] context Pointer to the TLS context
 * @param[in] p Input stream where to read the PSK identity hint
 * @param[in] length Number of bytes available in the input stream
 * @param[out] consumed Total number of bytes that have been consumed
 * @return Error code
 **/

error_t tlsParsePskIdentity(TlsContext *context,
    const uint8_t *p, size_t length, size_t *consumed)
{
   size_t n;
   TlsPskIdentity *pskIdentity;

   //Malformed ClientKeyExchange message?
   if(length < sizeof(TlsPskIdentity))
      return ERROR_DECODING_FAILED;

   //Point to the PSK identity
   pskIdentity = (TlsPskIdentity *) p;

   //Retrieve the length of the PSK identity
   n = ntohs(pskIdentity->length);

   //Make sure the length field is valid
   if(length < (sizeof(TlsPskIdentity) + n))
      return ERROR_DECODING_FAILED;

#if (TLS_PSK_SUPPORT == ENABLED || TLS_RSA_PSK_SUPPORT == ENABLED || \
   TLS_DHE_PSK_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
   //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(n > 0)
   {
      //Allocate a memory block to hold the PSK identity
      context->pskIdentity = tlsAllocMem(n + 1);
      //Failed to allocate memory?
      if(context->pskIdentity == NULL)
         return ERROR_OUT_OF_MEMORY;

      //Save the PSK identity
      memcpy(context->pskIdentity, pskIdentity->value, n);
      //Properly terminate the string
      context->pskIdentity[n] = '\0';
   }

   //Any registered callback?
   if(context->pskCallback != NULL)
   {
      error_t error;

      //Invoke user callback function
      if(context->pskIdentity != NULL)
         error = context->pskCallback(context, context->pskIdentity);
      else
         error = context->pskCallback(context, "");

      //Any error to report?
      if(error)
         return ERROR_UNKNOWN_IDENTITY;
   }
#endif

   //Total number of bytes that have been consumed
   *consumed = sizeof(TlsPskIdentity) + n;

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Parse client's key exchange parameters
 * @param[in] context Pointer to the TLS context
 * @param[in] p Input stream where to read the client's key exchange parameters
 * @param[in] length Number of bytes available in the input stream
 * @param[out] consumed Total number of bytes that have been consumed
 * @return Error code
 **/

error_t tlsParseClientKeyParams(TlsContext *context,
    const uint8_t *p, size_t length, size_t *consumed)
{
   error_t error;

#if (TLS_RSA_SUPPORT == ENABLED || TLS_RSA_PSK_SUPPORT == ENABLED)
   //RSA key exchange method?
   if(context->keyExchMethod == TLS_KEY_EXCH_RSA ||
      context->keyExchMethod == TLS_KEY_EXCH_RSA_PSK)
   {
      size_t n;
      uint16_t version;
      RsaPrivateKey privateKey;

      //The RSA-encrypted premaster secret in a ClientKeyExchange is preceded by
      //two length bytes. SSL 3.0 implementations do not include these bytes
      if(context->version > SSL_VERSION_3_0)
      {
         //Malformed ClientKeyExchange message?
         if(length < 2)
            return ERROR_DECODING_FAILED;

         //Decode the length field
         n = LOAD16BE(p);

         //Check the length of the RSA-encrypted premaster secret
         if(n > (length - 2))
            return ERROR_DECODING_FAILED;

         //Save the length of the RSA-encrypted premaster secret
         length = n;
         //Advance the pointer over the length field
         p += 2;
         //Total number of bytes that have been consumed
         *consumed = length + 2;
      }
      else
      {
         //Total number of bytes that have been consumed
         *consumed = length;
      }

      //Initialize RSA private key
      rsaInitPrivateKey(&privateKey);

      //Decode the PEM structure that holds the RSA private key
      error = pemReadRsaPrivateKey(context->cert->privateKey,
         context->cert->privateKeyLength, &privateKey);

      //Check status code
      if(!error)
      {
         //Decrypt the premaster secret using the server private key
         error = rsaesPkcs1v15Decrypt(&privateKey, p, length, context->premasterSecret,
            TLS_MAX_PREMASTER_SECRET_SIZE, &context->premasterSecretLen);
      }

      //Release RSA private key
      rsaFreePrivateKey(&privateKey);

      //Retrieve the latest version supported by the client. This is used
      //to detect version roll-back attacks
      version = LOAD16BE(context->premasterSecret);

      //The best way to avoid vulnerability to the Bleichenbacher attack is to
      //treat incorrectly formatted messages in a manner indistinguishable from
      //correctly formatted RSA blocks
      if(error || context->premasterSecretLen != 48 || version != context->clientVersion)
      {
         //When it receives an incorrectly formatted RSA block, the server
         //should generate a random 48-byte value and proceed using it as
         //the premaster secret
         error = context->prngAlgo->read(context->prngContext, context->premasterSecret, 48);

         //Fix the length of the premaster secret
         context->premasterSecretLen = 48;
      }
   }
   else
#endif
#if (TLS_DH_ANON_SUPPORT == ENABLED || TLS_DHE_RSA_SUPPORT == ENABLED || \
   TLS_DHE_DSS_SUPPORT == ENABLED || TLS_DHE_PSK_SUPPORT == ENABLED)
   //Diffie-Hellman 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)
   {
      size_t n;

      //Convert the client's public value to a multiple precision integer
      error = tlsReadMpi(&context->dhContext.yb, p, length, &n);

      //Check status code
      if(!error)
      {
         //Total number of bytes that have been consumed
         *consumed = n;

         //Verify client's public value
         error = dhCheckPublicKey(&context->dhContext.params, &context->dhContext.yb);
      }

      //Check status code
      if(!error)
      {
         //Calculate the negotiated key Z
         error = dhComputeSharedSecret(&context->dhContext, context->premasterSecret,
            TLS_MAX_PREMASTER_SECRET_SIZE, &context->premasterSecretLen);
      }

      //Check status code
      if(!error)
      {
         //Leading bytes of Z that contain all zero bits are stripped before
         //it is used as the premaster secret (RFC 4346, section 8.2.1)
         for(n = 0; n < context->premasterSecretLen; n++)
         {
            if(context->premasterSecret[n] != 0x00)
               break;
         }

         //Any leading zero bytes?
         if(n > 0)
         {
            //Strip leading zero bytes from the negotiated key
            memmove(context->premasterSecret, context->premasterSecret + n,
               context->premasterSecretLen - n);

            //Adjust the length of the premaster secret
            context->premasterSecretLen -= n;
         }
      }
   }
   else
#endif
#if (TLS_ECDH_ANON_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED || \
   TLS_ECDHE_ECDSA_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
   //ECDH key exchange method?
   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)
   {
      size_t n;

      //Decode client's public key
      error = tlsReadEcPoint(&context->ecdhContext.params,
         &context->ecdhContext.qb, p, length, &n);

      //Check status code
      if(!error)
      {
         //Total number of bytes that have been consumed
         *consumed = n;

         //Verify client's public key and make sure that it is on the same
         //elliptic curve as the server's ECDH key
         error = ecdhCheckPublicKey(&context->ecdhContext.params,
            &context->ecdhContext.qb);
      }

      //Check status code
      if(!error)
      {
         //Calculate the shared secret Z. Leading zeros found in this octet
         //string must not be truncated (see RFC 4492, section 5.10)
         error = ecdhComputeSharedSecret(&context->ecdhContext, context->premasterSecret,
            TLS_MAX_PREMASTER_SECRET_SIZE, &context->premasterSecretLen);
      }
   }
   else
#endif
   //Invalid key exchange method?
   {
      //The specified key exchange method is not supported
      error = ERROR_UNSUPPORTED_KEY_EXCH_METHOD;
   }

   //Return status code
   return error;
}


#endif