change some parameters in the library to meet the needs of the website httpbin.org

Fork of MiniTLS-GPL by Donatien Garnier

tls/tls_handshake.c

Committer:
MiniTLS
Date:
2014-06-09
Revision:
2:527a66d0a1a9
Child:
3:eb324ffffd2b

File content as of revision 2:527a66d0a1a9:

/*
MiniTLS - A super trimmed down TLS/SSL Library for embedded devices
Author: Donatien Garnier
Copyright (C) 2013-2014 AppNearMe Ltd

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.
*//**
 * \file tls_handshake.c
 * \copyright Copyright (c) AppNearMe Ltd 2013
 * \author Donatien Garnier
 */

#define __DEBUG__ 0
#ifndef __MODULE__
#define __MODULE__ "tls_handshake.c"
#endif

#include "core/fwk.h"

#include "tls_handshake.h"
#include "tls_record.h"
#include "tls_socket.h"
#include "tls_alert.h"
#include "crypto/crypto_ecc.h"
#include "crypto/crypto_sha1.h"
#include "crypto/crypto_sha256.h"
#include "crypto/crypto_hmac_sha1.h"
#include "crypto/crypto_hmac_sha256.h"

#define HANDSHAKE_MAX_PREMASTER_KEY_SIZE 64
#define HANDSHAKE_RSA_PREMASTER_KEY_SIZE 48

typedef enum __handshake_type
{
  hello_request = (0), client_hello = (1), server_hello = (2),
  certificate = (11), server_key_exchange = (12),
  certificate_request = (13), server_hello_done = (14),
  certificate_verify = (15), client_key_exchange = (16),
  finished = (20), unknown = (255)
} handshake_type_t;

static minitls_err_t send_hello_client(tls_handshake_t* handshake, buffer_t* buffer);
static minitls_err_t parse_hello_server(tls_handshake_t* handshake, buffer_t* buffer, bool* resumed);
static minitls_err_t parse_server_certificate(tls_handshake_t* handshake, buffer_t* buffer);
static minitls_err_t parse_server_key_exchange(tls_handshake_t* handshake, buffer_t* buffer);
static minitls_err_t parse_client_certificate_request(tls_handshake_t* handshake, buffer_t* buffer);
static minitls_err_t parse_hello_done_server(tls_handshake_t* handshake, buffer_t* buffer);
static minitls_err_t send_client_certificate(tls_handshake_t* handshake, buffer_t* buffer);
static minitls_err_t send_client_key_exchange(tls_handshake_t* handshake, buffer_t* buffer);
static minitls_err_t send_finished(tls_handshake_t* handshake, buffer_t* buffer);
static minitls_err_t parse_finished(tls_handshake_t* handshake, buffer_t* buffer);

static minitls_err_t generate_record_keys(tls_handshake_t* handshake);

//Receive stuff
static minitls_err_t parse_message(tls_handshake_t* handshake, buffer_t* buffer, handshake_type_t* message_type);

//Send stuff
static minitls_err_t prepare_message(tls_handshake_t* handshake, handshake_type_t message_type, buffer_t* buffer, size_t size);
static minitls_err_t prepare_message_adjust_size(tls_handshake_t* handshake, buffer_t* buffer, size_t size);
static minitls_err_t send_message(tls_handshake_t* handshake, buffer_t* buffer);

//Error handlers
static void handle_ret(tls_handshake_t* handshake, minitls_err_t ret);

/*
      P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
                             HMAC_hash(secret, A(2) + seed) +
                             HMAC_hash(secret, A(3) + seed) + ...

      PRF(secret, label, seed) = P_hash(secret, label + seed)
 */

//PRF computation
static minitls_err_t prf_compute(const uint8_t* secret, size_t secret_size,
    const char* label,
    const uint8_t* seed1, size_t seed1_size,
    const uint8_t* seed2, size_t seed2_size,
    uint8_t* out, size_t out_size);

//Record-level change cipher spec request
static minitls_err_t change_cipher_spec_request(tls_handshake_t* handshake, buffer_t* buffer);

minitls_err_t tls_handshake_init(tls_handshake_t* handshake, tls_socket_t* socket)
{
  handshake->tls_socket = socket;

  tls_handshake_clean(handshake);
  //memset(handshake->master_key, 0, sizeof(HANDSHAKE_MASTER_KEY_SIZE));

  //Reset state machine
  handshake->state = TLS_HANDSHAKE_INIT;

  return MINITLS_OK;
}


minitls_err_t tls_handshake_start(tls_handshake_t* handshake)
{
  minitls_err_t ret = send_hello_client(handshake, &handshake->tls_socket->record.buffer);
  if(ret)
  {
    handle_ret(handshake, ret);
    return ret;
  }
  handshake->state = TLS_HANDSHAKE_HELLO_SENT;
  return MINITLS_OK;
}

minitls_err_t tls_handshake_process(tls_handshake_t* handshake, buffer_t* buffer)
{
  //Parse buffer
  DBG("Parsing message");
  handshake_type_t type;
  minitls_err_t ret = parse_message(handshake, buffer, &type);
  if(ret)
  {
    ERR("Parsing error");
    handle_ret(handshake, ret);
    return ret;
  }

  bool resumed = false;

  DBG("Message type %d", type);

  switch(type)
  {
  case hello_request:
    if( (handshake->state == TLS_HANDSHAKE_INIT) || (handshake->state == TLS_HANDSHAKE_HELLO_SENT) )
    {
      DBG("Hello request");
    }
    else
    {
      ret = MINITLS_ERR_NOT_IMPLEMENTED;
      handle_ret(handshake, ret);
      return ret;
    }
    break;
  case server_hello:
    if(handshake->state == TLS_HANDSHAKE_HELLO_SENT)
    {
      DBG("Server hello");
      //Parse hello
      handshake->state = TLS_HANDSHAKE_HELLO_RECEIVED;
      ret = parse_hello_server(handshake, buffer, &resumed);
      if(ret)
      {
        handle_ret(handshake, ret);
        return ret;
      }
      if(resumed)
      {
        handshake->state = TLS_HANDSHAKE_HELLO_RECEIVED_SESSION_RESUMPTION;

        //Complete handshake
        DBG("Expanding master key");

        ret = generate_record_keys(handshake);
        if(ret)
        {
          handle_ret(handshake, ret);
          return ret;
        }

        DBG("Change cipher spec request");

        ret = change_cipher_spec_request(handshake, buffer);
        if(ret)
        {
          handle_ret(handshake, ret);
          return ret;
        }

        DBG("Changing cipher spec (TX)");
        //Now we can change cipher spec -- TX way
        ret = tls_record_change_cipher_spec(&handshake->tls_socket->record, true);
        if(ret)
        {
          handle_ret(handshake, ret);
          return ret;
        }

        DBG("Send finished");
        //Now send finished message
        ret = send_finished(handshake, buffer);
        if(ret)
        {
          handle_ret(handshake, ret);
          return ret;
        }

        handshake->state = TLS_HANDSHAKE_FINISHED_SENT;
      }
    }
    else
    {
      ret = MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
      handle_ret(handshake, ret);
      return ret;
    }
    break;
  case certificate:
    if(handshake->state == TLS_HANDSHAKE_HELLO_RECEIVED)
    {
      DBG("Server certificate");
      //Parse certificate
      handshake->state = TLS_HANDSHAKE_CERTIFICATE_RECEIVED;
      ret = parse_server_certificate(handshake, buffer);
      if(ret)
      {
        handle_ret(handshake, ret);
        return ret;
      }
    }
    else
    {
      ret = MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
      handle_ret(handshake, ret);
      return ret;
    }
    break;
  case server_key_exchange:
    //With other types of authentication methods (PSK, anon) we could receive this message after the server's hello; however we don't support these cipher suites
    if(CRYPTO_ECC && (handshake->state == TLS_HANDSHAKE_CERTIFICATE_RECEIVED))
    {
      DBG("Server key exchange");
      //Parse server key
      handshake->state = TLS_HANDSHAKE_SERVER_KEY_EXCHANGE_RECEIVED;
      ret = parse_server_key_exchange(handshake, buffer);
      if(ret)
      {
        handle_ret(handshake, ret);
        return ret;
      }
    }
    else
    {
      ret = MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
      handle_ret(handshake, ret);
      return ret;
    }
    break;
  case certificate_request:
    //Obvi, we could receive this right after certificate - but we don't support this
    if(handshake->state == TLS_HANDSHAKE_CERTIFICATE_RECEIVED)
    {
      WARN("Not supported");
      ret = MINITLS_ERR_NOT_IMPLEMENTED;
      handle_ret(handshake, ret);
      return ret;
    }
    else if( (CRYPTO_ECC && (handshake->state == TLS_HANDSHAKE_SERVER_KEY_EXCHANGE_RECEIVED)) || (CRYPTO_RSA && (handshake->state == TLS_HANDSHAKE_CERTIFICATE_RECEIVED)) )
    {
      DBG("Client certificate request");
      //Parse certificate request
      handshake->state = TLS_HANDSHAKE_CERTIFICATE_REQUEST_RECEIVED;
      handshake->certificate_requested = true;
      ret = parse_client_certificate_request(handshake, buffer);
      if(ret)
      {
        handle_ret(handshake, ret);
        return ret;
      }
    }
    else
    {
      ret = MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
      handle_ret(handshake, ret);
      return ret;
    }
    break;
  case server_hello_done:
    if( (CRYPTO_ECC && (handshake->state == TLS_HANDSHAKE_SERVER_KEY_EXCHANGE_RECEIVED)) || (CRYPTO_RSA && (handshake->state == TLS_HANDSHAKE_CERTIFICATE_RECEIVED))
        || (handshake->state == TLS_HANDSHAKE_CERTIFICATE_REQUEST_RECEIVED) )
    {
      DBG("Hello done");
      //Parse hello done
      handshake->state = TLS_HANDSHAKE_HELLO_DONE_RECEIVED;
      ret = parse_hello_done_server(handshake, buffer);
      if(ret)
      {
        handle_ret(handshake, ret);
        return ret;
      }

      //Now reply
      if(handshake->certificate_requested)
      {
        DBG("Send client certificate");
        //Send client certificate if requested
        ret = send_client_certificate(handshake, buffer);
        if(ret)
        {
          handle_ret(handshake, ret);
          return ret;
        }
        handshake->state = TLS_HANDSHAKE_CERTIFICATE_SENT;
      }

      DBG("Send client key exchange");
      //Send client key exchange
      ret = send_client_key_exchange(handshake, buffer);
      if(ret)
      {
        handle_ret(handshake, ret);
        return ret;
      }

      handshake->state = TLS_HANDSHAKE_CLIENT_KEY_EXCHANGE_SENT;

      //Send certificate verify -- not for now
      /*
      ret = send_client_certificate_verify(handshake, buffer);
      if(ret)
      {
        handle_ret(handshake, ret);
        return ret;
      }

      handshake->state = TLS_HANDSHAKE_CERTIFICATE_VERIFY_SENT;
      */

      DBG("Expanding master key");

      ret = generate_record_keys(handshake);
      if(ret)
      {
        handle_ret(handshake, ret);
        return ret;
      }

      DBG("Change cipher spec request");

      ret = change_cipher_spec_request(handshake, buffer);
      if(ret)
      {
        handle_ret(handshake, ret);
        return ret;
      }

      DBG("Changing cipher spec (TX)");
      //Now we can change cipher spec -- TX way
      ret = tls_record_change_cipher_spec(&handshake->tls_socket->record, true);
      if(ret)
      {
        handle_ret(handshake, ret);
        return ret;
      }

      DBG("Send finished");
      //Now send finished message
      ret = send_finished(handshake, buffer);
      if(ret)
      {
        handle_ret(handshake, ret);
        return ret;
      }

      handshake->state = TLS_HANDSHAKE_FINISHED_SENT;
    }
    else
    {
      ret = MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
      handle_ret(handshake, ret);
      return ret;
    }
    break;
  case finished:
    if(handshake->state == TLS_HANDSHAKE_FINISHED_SENT)
    {
      //First check that the ChangeCipherSpec message has been sent by the other party (and that we are therefore secure in both ways)
      DBG("Receive finished");
      if(!tls_record_is_secure(&handshake->tls_socket->record))
      {
        ERR("Record is insecure");
        ret = MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
        handle_ret(handshake, ret);
        return ret;
      }

      //Parse finished message and check integrity of exchange
      handshake->state = TLS_HANDSHAKE_FINISHED_RECEIVED;
      ret = parse_finished(handshake, buffer);
      if(ret)
      {
        ERR("Integrity check failed");
        handle_ret(handshake, ret);
        return ret;
      }

      DBG("Handshake done!");
      handshake->state = TLS_HANDSHAKE_DONE;
    }
    else
    {
      ret = MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
      handle_ret(handshake, ret);
      return ret;
    }
    break;
  default:
    ret = MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
    handle_ret(handshake, ret);
    return ret;
  }

  return MINITLS_OK;
}

void tls_handshake_clean(tls_handshake_t* handshake)
{
  memset(handshake->random_client, 0, HANDSHAKE_RANDOM_SIZE);
  memset(handshake->random_client, 0, HANDSHAKE_RANDOM_SIZE);

  handshake->certificate_requested = false;
  //memset(&handshake->ecc_curve, 0, sizeof(crypto_ecc_curve_t*));
  //handshake->ecc_curve_type = 0;
#if CRYPTO_ECC
  handshake->key_exchange.ecc.curve = NULL;
  memset(&handshake->key_exchange.ecc.server_key, 0, sizeof(crypto_ecc_public_key_t));
  memset(&handshake->key_exchange.ecc.client_key, 0, sizeof(crypto_ecc_private_key_t));
#endif
#if CRYPTO_RSA
  //
#endif

#if MINITLS_CFG_PROTOCOL_TLS_1_2
  crypto_sha256_init(&handshake->hash.sha256);
#endif
#if (MINITLS_CFG_PROTOCOL_TLS_1_1 || MINITLS_CFG_PROTOCOL_TLS_1_0 || MINITLS_CFG_PROTOCOL_SSL_3)
  crypto_sha1_init(&handshake->hash.md5_sha1.sha1);
  crypto_md5_init(&handshake->hash.md5_sha1.md5);
#endif

}

bool tls_handshake_is_done(tls_handshake_t* handshake)
{
  if(handshake->state == TLS_HANDSHAKE_DONE)
  {
    return true;
  }
  return false;
}

#define HELLO_CLIENT_SIZE 41
#define HELLO_CLIENT_EXTENSIONS_HEADER_SIZE 2
#define HELLO_CLIENT_MAX_FRAGMENT_EXTENSION_SIZE 5
#define HELLO_CLIENT_SIGNATURE_ALGORITHM_EXTENSION_SIZE 8
#define HELLO_CLIENT_SUPPORTED_ECC_CURVES_SIZE 8

#if CRYPTO_ECC
const uint8_t TLS_CIPHERSUITE_ECDHE_ECDSA_WITH_AES_128_CBC_SHA[]  = { 0xC0, 0x09 };
#elif CRYPTO_RSA
const uint8_t TLS_CIPHERSUITE_RSA_WITH_AES_128_CBC_SHA[]  = { 0x00, 0x2F };
#endif

#define TLS_COMPRESSION_METHOD_NULL 0x00
#define TLS_EXTENSION_TYPE_MAX_FRAGMENT_LENGTH 1
#define TLS_EXTENSION_TYPE_SIGNATURE_ALGORITHM 13
#define TLS_EXTENSION_TYPE_SUPPORTED_ECC_CURVES 10

typedef enum __hash_algorithm {
          none = (0), md5 = (1), sha1 = (2), sha224 = (3), sha256 = (4), sha384 = (5),
          sha512 = (6)
} hash_algorithm_t;

typedef enum __signature_algorithm { anonymous = (0), rsa = (1), dsa = (2), ecdsa = (3)
} signature_algorithm_t;

minitls_err_t send_hello_client(tls_handshake_t* handshake, buffer_t* buffer)
{
  //Form hello packet
  /*
  struct {
    ProtocolVersion client_version;
    Random random;
    SessionID session_id;
    CipherSuite cipher_suites<2..2^16-2>;
    CompressionMethod compression_methods<1..2^8-1>;
    select (extensions_present) {
        case false:
            struct {};
        case true:
            Extension extensions<0..2^16-1>;
    };
  } ClientHello;
  */
  minitls_err_t ret;

  //Write header
  if(handshake->tls_socket->record.max_fragment_size < TLS_DEFAULT_MAX_FRAGMENT_SIZE)
  {
    ret = prepare_message(handshake, client_hello, buffer, HELLO_CLIENT_SIZE + handshake->tls_socket->session.session_id_length +
        HELLO_CLIENT_EXTENSIONS_HEADER_SIZE
#if CRYPTO_ECC
        + HELLO_CLIENT_SIGNATURE_ALGORITHM_EXTENSION_SIZE + HELLO_CLIENT_SUPPORTED_ECC_CURVES_SIZE
#endif
        + HELLO_CLIENT_MAX_FRAGMENT_EXTENSION_SIZE);
  }
  else
  {
    ret = prepare_message(handshake, client_hello, buffer, HELLO_CLIENT_SIZE + handshake->tls_socket->session.session_id_length +
        HELLO_CLIENT_EXTENSIONS_HEADER_SIZE
#if CRYPTO_ECC
        + HELLO_CLIENT_SIGNATURE_ALGORITHM_EXTENSION_SIZE + HELLO_CLIENT_SUPPORTED_ECC_CURVES_SIZE
#endif
        );
  }
  if(ret)
  {
    return ret;
  }

  //Generate 28-bytes long random
  crypto_prng_get(handshake->tls_socket->minitls->prng, handshake->random_client, HANDSHAKE_RANDOM_SIZE);

  //Write most recent protocol version supported
#if MINITLS_CFG_PROTOCOL_TLS_1_2
  buffer_nu8_write(buffer, TLS_1_2_VERSION_MAJOR);
  buffer_nu8_write(buffer, TLS_1_2_VERSION_MINOR);
#elif MINITLS_CFG_PROTOCOL_TLS_1_1
  buffer_nu8_write(buffer, TLS_1_1_VERSION_MAJOR);
  buffer_nu8_write(buffer, TLS_1_1_VERSION_MINOR);
#elif MINITLS_CFG_PROTOCOL_TLS_1_0
  buffer_nu8_write(buffer, TLS_1_0_VERSION_MAJOR);
  buffer_nu8_write(buffer, TLS_1_0_VERSION_MINOR);
#elif MINITLS_CFG_PROTOCOL_SSL_3
  buffer_nu8_write(buffer, SSL_3_VERSION_MAJOR);
  buffer_nu8_write(buffer, SSL_3_VERSION_MINOR);
#else
#error No SSL/TLS protocol version enabled
#endif

  //Write random
  //buffer_nu32_write(buffer, 0); //UNIX Timestamp -- not mandatory, write 0s -- Don't do this, just use a 32bytes long random
  buffer_nbytes_write(buffer, handshake->random_client, HANDSHAKE_RANDOM_SIZE);
  //Write session ID
  buffer_nu8_write(buffer, handshake->tls_socket->session.session_id_length);
  buffer_nbytes_write(buffer, handshake->tls_socket->session.session_id, handshake->tls_socket->session.session_id_length);
  //Write the one cipher suite we support
  buffer_nu16_write(buffer, 2); //2 bytes long
#if CRYPTO_ECC
  buffer_nbytes_write(buffer, TLS_CIPHERSUITE_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 2);
#elif CRYPTO_RSA
  buffer_nbytes_write(buffer, TLS_CIPHERSUITE_RSA_WITH_AES_128_CBC_SHA, 2);
#else
#error You must enable one cipher suite
#endif
  //Write the one compression method we support (null)
  buffer_nu8_write(buffer, 1); //1 byte long
  buffer_nu8_write(buffer, TLS_COMPRESSION_METHOD_NULL);

  if(handshake->tls_socket->record.max_fragment_size < TLS_DEFAULT_MAX_FRAGMENT_SIZE)
  {
    //3 extensions (ECC) / 1 (RSA)
    buffer_nu16_write(buffer,
#if CRYPTO_ECC
        + HELLO_CLIENT_SIGNATURE_ALGORITHM_EXTENSION_SIZE + HELLO_CLIENT_SUPPORTED_ECC_CURVES_SIZE
#endif
        + HELLO_CLIENT_MAX_FRAGMENT_EXTENSION_SIZE);
  }
  else
  {
    //2 (ECC) extensions / 0 (RSA)
    buffer_nu16_write(buffer, 0
#if CRYPTO_ECC
        + HELLO_CLIENT_SIGNATURE_ALGORITHM_EXTENSION_SIZE + HELLO_CLIENT_SUPPORTED_ECC_CURVES_SIZE
#endif
    );
  }
#if CRYPTO_ECC
  //Use signature algorithm extension
  buffer_nu16_write(buffer, TLS_EXTENSION_TYPE_SIGNATURE_ALGORITHM);
  /*
     enum {
          none(0), md5(1), sha1(2), sha224(3), sha256(4), sha384(5),
          sha512(6), (255)
      } HashAlgorithm;

      enum { anonymous(0), rsa(1), dsa(2), ecdsa(3), (255) }
        SignatureAlgorithm;

      struct {
            HashAlgorithm hash;
            SignatureAlgorithm signature;
      } SignatureAndHashAlgorithm;

      SignatureAndHashAlgorithm
        supported_signature_algorithms<2..2^16-2>;
   */

  buffer_nu16_write(buffer, 4); //2 bytes of list length, 2 bytes of hash and signature algo
  buffer_nu16_write(buffer, 2); //2 bytes of hash and signature algo

  //Supported algo is ECDSA-SHA1
  buffer_nu8_write(buffer, sha1);
  buffer_nu8_write(buffer, ecdsa);

  //Use supported elliptic curves extensions

  buffer_nu16_write(buffer, TLS_EXTENSION_TYPE_SUPPORTED_ECC_CURVES);
  /*
  struct {
      NamedCurve elliptic_curve_list<1..2^16-1>
  } EllipticCurveList;
  */
  buffer_nu16_write(buffer, 4); //2 bytes of list length, 2 bytes of curve type
  buffer_nu16_write(buffer, 2); //2 bytes of curve type
  buffer_nu16_write(buffer, secp192r1); //The one curve we support -- TODO clean this up
#endif

  if(handshake->tls_socket->record.max_fragment_size < TLS_DEFAULT_MAX_FRAGMENT_SIZE)
  {
    //Use MaxFragmentLength extension to decrease the maximum fragment length (RFC4366)
    buffer_nu16_write(buffer, TLS_EXTENSION_TYPE_MAX_FRAGMENT_LENGTH);

    buffer_nu16_write(buffer, 1); //1 byte of data
    /*
    enum{
      2^9(1), 2^10(2), 2^11(3), 2^12(4), (255)
    } MaxFragmentLength;
    */
    if( handshake->tls_socket->record.max_fragment_size >= 4096 )
    {
      buffer_nu8_write(buffer, 4);
    }
    else if( handshake->tls_socket->record.max_fragment_size >= 2048 )
    {
      buffer_nu8_write(buffer, 3);
    }
    else if( handshake->tls_socket->record.max_fragment_size >= 1024 )
    {
      buffer_nu8_write(buffer, 2);
    }
    else if( handshake->tls_socket->record.max_fragment_size >= 512 )
    {
      buffer_nu8_write(buffer, 1);
    }
    else
    {
      WARN("Fragment size is too small - using 512 bytes fragment size");
      buffer_nu8_write(buffer, 1);
    }
  }

  ret = send_message(handshake, buffer);
  if(ret)
  {
    return ret;
  }

  return MINITLS_OK;
}

minitls_err_t parse_hello_server(tls_handshake_t* handshake, buffer_t* buffer, bool* resumed)
{
  //Header has already been parsed
  /*
  struct {
      ProtocolVersion server_version;
      Random random;
      SessionID session_id;
      CipherSuite cipher_suite;
      CompressionMethod compression_method;
      select (extensions_present) {
          case false:
              struct {};
          case true:
              Extension extensions<0..2^16-1>;
      };
  } ServerHello;
  */

  if( buffer_length(buffer) < 35) //protocol version + random + session_id length
  {
    return MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
  }

  tls_protocol_version_t version;
  version.major = buffer_nu8_read(buffer);
  version.minor = buffer_nu8_read(buffer);

  //Check that version is one of the supported ones
  if(
#if MINITLS_CFG_PROTOCOL_TLS_1_2
      ( (version.major != TLS_1_2_VERSION_MAJOR) || (version.minor != TLS_1_2_VERSION_MINOR) ) &&
#endif
#if MINITLS_CFG_PROTOCOL_TLS_1_1
      ( (version.major != TLS_1_1_VERSION_MAJOR) || (version.minor != TLS_1_1_VERSION_MINOR) ) &&
#endif
#if MINITLS_CFG_PROTOCOL_TLS_1_0
      ( (version.major != TLS_1_0_VERSION_MAJOR) || (version.minor != TLS_1_0_VERSION_MINOR) ) &&
#endif
#if MINITLS_CFG_PROTOCOL_SSL_3
      ( (version.major != SSL_3_VERSION_MAJOR) || (version.minor != SSL_3_VERSION_MINOR) ) &&
#endif
      true )
  {
    ERR("Version mismatch");
    return MINITLS_ERR_PROTOCOL_VERSION;
  }

  //Set record version accordingly
  tls_record_set_protocol_version(&handshake->tls_socket->record, version.major, version.minor);

  //buffer_nu32_read(buffer); //UNIX Timestamp -- Don't read that, read it as part of the random
  buffer_nbytes_read(buffer, handshake->random_server, HANDSHAKE_RANDOM_SIZE);
  size_t server_session_id_length = buffer_nu8_read(buffer);
  if( buffer_length(buffer) < server_session_id_length + 3 ) //session id + cipher suite + compression mode
  {
    return MINITLS_ERR_PROTOCOL_NON_CONFORMANT; ///Will raise a "decode error" alert
  }
  if(server_session_id_length > SESSION_ID_MAX_SIZE) //Buffer overflow attack
  {
    return MINITLS_ERR_PROTOCOL_NON_CONFORMANT; ///Will raise a "decode error" alert
  }

  uint8_t server_session_id[SESSION_ID_MAX_SIZE];
  buffer_nbytes_read(buffer, server_session_id, server_session_id_length);

  if( (handshake->tls_socket->session.session_id_length == 0)
      || (server_session_id_length != handshake->tls_socket->session.session_id_length)
      || (memcmp(server_session_id, handshake->tls_socket->session.session_id, server_session_id_length) != 0))
  {
    //Session ID does not match, start a new one
    handshake->tls_socket->session.session_id_length = server_session_id_length;
    memcpy(handshake->tls_socket->session.session_id, server_session_id, server_session_id_length);
    *resumed = false;
  }
  else
  {
    //Resume session
    *resumed = true;
  }

  uint8_t cipher_suite[2];
  buffer_nbytes_read(buffer, cipher_suite, 2);
  uint8_t compression_mode = buffer_nu8_read(buffer);

  if(
#if CRYPTO_ECC
      (memcmp(cipher_suite, TLS_CIPHERSUITE_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 2) != 0)
#elif CRYPTO_RSA
      (memcmp(cipher_suite, TLS_CIPHERSUITE_RSA_WITH_AES_128_CBC_SHA, 2) != 0)
#else
#error
#endif
      || (compression_mode != TLS_COMPRESSION_METHOD_NULL) )
  {
    ERR("Unsupported cipher suite / compression method");
    return MINITLS_ERR_PROTOCOL_NON_CONFORMANT; //Protocol error as we did not advertise any other cipher suite/compression method in the hello client message
  }

  if( buffer_length(buffer) > 0 )
  {
    //Extensions are present, TODO
    DBG("Some extensions are present; ignoring them");
  }

  DBG("Hello Server OK");

  return MINITLS_OK;
}

minitls_err_t parse_server_certificate(tls_handshake_t* handshake, buffer_t* buffer)
{
  if( buffer_length(buffer) < 3) //Certificate list size
  {
    return MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
  }

  size_t cert_list_size = buffer_nu24_read(buffer);

  if( buffer_length(buffer) < cert_list_size ) //Certificate list
  {
    return MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
  }

  if( buffer_length(buffer) < 3) //Certificate size
  {
    return MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
  }

  size_t cert_size = buffer_nu24_read(buffer);

  if( (buffer_length(buffer) < cert_size) || (cert_size + 3 > cert_list_size) ) //Certificate
  {
    return MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
  }

  //Only parse first certificate in the chain, ignore others
  if( cert_size != handshake->tls_socket->minitls->certificate->certificate_size )
  {
    ERR("Certificate does not match");
    //Cannot contain certificate, return security error
    return MINITLS_ERR_WRONG_CERTIFICATE;
  }

  //Do a plain, stupid, comparison with the certificate we have
  if( memcmp( handshake->tls_socket->minitls->certificate->certificate, buffer_current_read_position(buffer), handshake->tls_socket->minitls->certificate->certificate_size) != 0 )
  {
    ERR("Certificate does not match");
    //This is not the same certificate
    return MINITLS_ERR_WRONG_CERTIFICATE;
  }
  buffer_n_discard(buffer, handshake->tls_socket->minitls->certificate->certificate_size);


  return MINITLS_OK;
}

minitls_err_t parse_server_key_exchange(tls_handshake_t* handshake, buffer_t* buffer)
{
#if CRYPTO_ECC
  //For now we only support the ECDHE_ECDSA key exchange algo, so we hardcode the recovery of ephemeral key values here
  /*
  enum { explicit_prime (1), explicit_char2 (2),
         named_curve (3), reserved(248..255) } ECCurveType;

  enum {
      sect163k1 (1), sect163r1 (2), sect163r2 (3),
      sect193r1 (4), sect193r2 (5), sect233k1 (6),
      sect233r1 (7), sect239k1 (8), sect283k1 (9),
      sect283r1 (10), sect409k1 (11), sect409r1 (12),
      sect571k1 (13), sect571r1 (14), secp160k1 (15),
      secp160r1 (16), secp160r2 (17), secp192k1 (18),
      secp192r1 (19), secp224k1 (20), secp224r1 (21),
      secp256k1 (22), secp256r1 (23), secp384r1 (24),
      secp521r1 (25),
      reserved (0xFE00..0xFEFF),
      arbitrary_explicit_prime_curves(0xFF01),
      arbitrary_explicit_char2_curves(0xFF02),
      (0xFFFF)
  } NamedCurve;

  struct {
      ECCurveType    curve_type;
      select (curve_type) {
          case explicit_prime:
              opaque      prime_p <1..2^8-1>;
              ECCurve     curve;
              ECPoint     base;
              opaque      order <1..2^8-1>;
              opaque      cofactor <1..2^8-1>;
          case explicit_char2:
              uint16      m;
              ECBasisType basis;
              select (basis) {
                  case ec_trinomial:
                      opaque  k <1..2^8-1>;
                  case ec_pentanomial:
                      opaque  k1 <1..2^8-1>;
                      opaque  k2 <1..2^8-1>;
                      opaque  k3 <1..2^8-1>;
              };
              ECCurve     curve;
              ECPoint     base;
              opaque      order <1..2^8-1>;
              opaque      cofactor <1..2^8-1>;
          case named_curve:
              NamedCurve namedcurve;
      };
  } ECParameters;

  struct {
    opaque point <1..2^8-1>;
  } ECPoint;

  struct {
    ECParameters    curve_params;
    ECPoint         public;
  } ServerECDHParams;

  //signed_params - TLS1.2 specific
  struct {
     SignatureAndHashAlgorithm algorithm;
     opaque signature<0..2^16-1>;
  } DigitallySigned;

  enum { ecdsa } SignatureAlgorithm;

  select (SignatureAlgorithm) {
      case ecdsa:
          digitally-signed struct {
              opaque sha_hash[sha_size];
          };
  } Signature;


  ServerKeyExchange.signed_params.sha_hash
      SHA(ClientHello.random + ServerHello.random +
                                        ServerKeyExchange.params);

  select (KeyExchangeAlgorithm) {
      case ec_diffie_hellman:
          ServerECDHParams    params;
          Signature           signed_params;
  } ServerKeyExchange;
  */

  //Save position as we will have to perform a hash starting from here
  uint8_t* params_position = buffer_current_read_position(buffer);

  //Only supporting named curves as we have no means of validating other new curves
  if( buffer_length(buffer) < 1 )
  {
    return MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
  }

  uint8_t curve_desc_type = buffer_nu8_read(buffer);
  if(curve_desc_type != 3) //Named curve
  {
    WARN("Not a named curve");
    return MINITLS_ERR_NOT_IMPLEMENTED;
  }

  crypto_ecc_curve_type_t curve_type = buffer_nu16_read(buffer);

  DBG("ECC Curve %d", curve_type);

  minitls_err_t ret = crypto_ecc_curve_get(&handshake->key_exchange.ecc.curve, curve_type);
  if(ret)
  {
    ERR("Could not get curve");
    return ret;
  }

  if( buffer_length(buffer) < 1 )
  {
    return MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
  }

  size_t point_size = buffer_nu8_read(buffer);

  if( buffer_length(buffer) < point_size )
  {
    return MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
  }

  //Read point
  ret = crypto_ecc_ansi_x963_import(&handshake->key_exchange.ecc.server_key, handshake->key_exchange.ecc.curve, buffer_current_read_position(buffer), point_size);
  buffer_n_discard(buffer, point_size);
  if(ret)
  {
    ERR("Error decoding ANSI X9.63 key");
    return ret;
  }

  size_t params_size = buffer_current_read_position(buffer) - params_position;

  if( buffer_length(buffer) < 2 )
  {
    return MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
  }

  //Check hash and signature algorithms -- this was introduced in TLS1.2
  uint8_t hash_alg = buffer_nu8_read(buffer);
  uint8_t sig_alg = buffer_nu8_read(buffer);

  //We only support SHA1 / ECDSA so check that this is what the server is offering

  if( (hash_alg != sha1) || (sig_alg != ecdsa) )
  {
    ERR("Unsupported hash/signature algorithms");
  }

  //Signature length is 2 bytes long
  if( buffer_length(buffer) < 2 )
  {
    return MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
  }

  size_t signature_length = buffer_nu16_read(buffer);

  //Signature length is 2 bytes long
  if( buffer_length(buffer) < signature_length )
  {
    return MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
  }

  //Compute hash
  uint8_t hash_result[SHA1_SIZE];
  crypto_sha1_t sha1;
  crypto_sha1_init(&sha1);
  crypto_sha1_update(&sha1, handshake->random_client, HANDSHAKE_RANDOM_SIZE);
  crypto_sha1_update(&sha1, handshake->random_server, HANDSHAKE_RANDOM_SIZE);
  crypto_sha1_update(&sha1, params_position, params_size);
  crypto_sha1_end(&sha1, hash_result);

  DBG("SHA-1 Hash:");
  DBG_BLOCK(
  buffer_t hash_result_buf;
  buffer_byref(&hash_result_buf, hash_result, SHA1_SIZE);
  buffer_dump(&hash_result_buf); )

  //Finally check signature
  ret = crypto_ecc_dsa_check(&handshake->tls_socket->minitls->certificate->public_key.ecc, hash_result, SHA1_SIZE, buffer_current_read_position(buffer), signature_length);
  buffer_n_discard(buffer, signature_length);
  if(ret)
  {
    ERR("Signature check failed");
    return ret;
  }

  DBG("Server key exchange parsed");

  return MINITLS_OK;
#else
  return MINITLS_ERR_PROTOCOL_NON_CONFORMANT; //Illegal for these cipher suites
#endif
}

minitls_err_t parse_client_certificate_request(tls_handshake_t* handshake, buffer_t* buffer)
{
  //Ignore that, we won't offer a certificate in return anyway (or an empty one)
  return MINITLS_OK;
}

minitls_err_t parse_hello_done_server(tls_handshake_t* handshake, buffer_t* buffer)
{
  if( buffer_length(buffer) != 0 )
  {
    return MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
  }
  return MINITLS_OK;
}

#define CLIENT_CERTIFICATE_NOCERTS_SIZE 3

minitls_err_t send_client_certificate(tls_handshake_t* handshake, buffer_t* buffer)
{
  //Send a 0-length certificate structure

  minitls_err_t ret;

  //Write header
  ret = prepare_message(handshake, certificate, buffer, CLIENT_CERTIFICATE_NOCERTS_SIZE);
  if(ret)
  {
    return ret;
  }

  buffer_nu24_write(buffer, 0); //empty list

  ret = send_message(handshake, buffer);
  if(ret)
  {
    return ret;
  }

  return MINITLS_OK;
}

#define CLIENT_KEY_EXCHANGE_BASE_SIZE 0

minitls_err_t send_client_key_exchange(tls_handshake_t* handshake, buffer_t* buffer)
{
  minitls_err_t ret;

  //Write header
  ret = prepare_message(handshake, client_key_exchange, buffer, CLIENT_KEY_EXCHANGE_BASE_SIZE);
  if(ret)
  {
    return ret;
  }


#if CRYPTO_ECC
  //Send a 0-length certificate structure

  //Key size
  DBG_BLOCK(
  size_t key_size = crypto_ecc_get_key_size_for_curve(handshake->ecc_curve);

  DBG("Generating ephemeral key of size %d", key_size);
  )

  //Generate client key
  ret = crypto_ecc_generate_key(&handshake->key_exchange.ecc.client_key, handshake->key_exchange.ecc.curve, handshake->tls_socket->minitls->prng);
  if(ret)
  {
    return ret;
  }

  DBG("Key generated");

  //Skip key size
  size_t point_size = 0;
  buffer_nu8_write(buffer, point_size);

  DBG("Exporting key in X9.63 format");

  //Write key
  ret = crypto_ecc_ansi_x963_export(crypto_ecc_get_public_key(&handshake->key_exchange.ecc.client_key), /*handshake->ecc_curve,*/ buffer_current_write_position(buffer), buffer_space(buffer), &point_size);
  if(ret)
  {
    return ret;
  }

  buffer_n_skip(buffer, point_size);

  DBG("Adjusting size");

  //Rewind, write position and re-adjust
  size_t length = buffer_length(buffer);
  buffer_set_length(buffer, length - point_size - 1);

  buffer_nu8_write(buffer, point_size);

  buffer_set_length(buffer, length);

  //Adjust message size
  ret = prepare_message_adjust_size(handshake, buffer, CLIENT_KEY_EXCHANGE_BASE_SIZE + 1 + point_size);
  if(ret)
  {
    return ret;
  }

  ret = send_message(handshake, buffer);
  if(ret)
  {
    return ret;
  }
#elif CRYPTO_RSA
  /*
  struct {
      ProtocolVersion client_version;
      opaque random[46];
  } PreMasterSecret;

  client_version
     The latest (newest) version supported by the client.  This is
     used to detect version rollback attacks.

  random
     46 securely-generated random bytes.

  struct {
      public-key-encrypted PreMasterSecret pre_master_secret;
  } EncryptedPreMasterSecret;

  pre_master_secret
     This random value is generated by the client and is used to
     generate the master secret, as specified in Section 8.1.
     */

  //Premaster key is generated first

#else
#error
#endif

  DBG("Generating premaster key");

  uint8_t premaster_key[HANDSHAKE_MAX_PREMASTER_KEY_SIZE];
  size_t premaster_key_size = 0;

#if CRYPTO_ECC
  //At this point we can generate the pre-master key
  ret = crypto_ecc_dh_generate_shared_secret(&handshake->key_exchange.ecc.client_key, &handshake->key_exchange.ecc.server_key, premaster_key, HANDSHAKE_MAX_PREMASTER_KEY_SIZE, &premaster_key_size);
  if(ret)
  {
    memset(premaster_key, 0, HANDSHAKE_MAX_PREMASTER_KEY_SIZE); //Don't want to leak this
    return ret;
  }
#elif CRYPTO_RSA
  //The two first bytes should be the SSL/TLS version advertised in ClientHello (prevents version rollback attacks)
#if MINITLS_CFG_PROTOCOL_TLS_1_2
  premaster_key[0] = TLS_1_2_VERSION_MAJOR;
  premaster_key[1] = TLS_1_2_VERSION_MINOR;
#elif MINITLS_CFG_PROTOCOL_TLS_1_1
  premaster_key[0] = TLS_1_1_VERSION_MAJOR;
  premaster_key[1] = TLS_1_1_VERSION_MINOR;
#elif MINITLS_CFG_PROTOCOL_TLS_1_0
  premaster_key[0] = TLS_1_0_VERSION_MAJOR;
  premaster_key[1] = TLS_1_0_VERSION_MINOR;
#elif MINITLS_CFG_PROTOCOL_SSL_3
  premaster_key[0] = SSL_3_VERSION_MAJOR;
  premaster_key[1] = SSL_3_VERSION_MINOR;
#else
#error No SSL/TLS protocol version enabled
#endif

  //Other 46 bytes are random
  crypto_prng_get(handshake->tls_socket->minitls->prng, &premaster_key[2], HANDSHAKE_RSA_PREMASTER_KEY_SIZE - 2);
  premaster_key_size = HANDSHAKE_RSA_PREMASTER_KEY_SIZE;

  //Encrypt it using RSA
  uint8_t encrypted_premaster_key[128];
  size_t encrypted_premaster_key_size = 0;
  ret = crypto_rsa_encrypt(&handshake->tls_socket->minitls->certificate->public_key.rsa,
      premaster_key, premaster_key_size,
      encrypted_premaster_key, sizeof(encrypted_premaster_key), &encrypted_premaster_key_size, handshake->tls_socket->minitls->prng);
  if(ret)
  {
    return ret;
  }

  DBG("Encrypted premaster key size: %d", encrypted_premaster_key_size);

  //Now send it
  buffer_nu16_write(buffer, encrypted_premaster_key_size);
  buffer_nbytes_write(buffer, encrypted_premaster_key, encrypted_premaster_key_size);

  //Adjust message size
  ret = prepare_message_adjust_size(handshake, buffer, CLIENT_KEY_EXCHANGE_BASE_SIZE + 2 + encrypted_premaster_key_size);
  if(ret)
  {
    return ret;
  }

  ret = send_message(handshake, buffer);
  if(ret)
  {
    return ret;
  }
#endif

  DBG_BLOCK(
  DBG("Premaster key size: %d", premaster_key_size);

  buffer_t dump_buf;
  DBG("Premaster key");
  buffer_byref(&dump_buf, premaster_key, premaster_key_size);
  buffer_dump(&dump_buf);

  DBG("Random client");
  buffer_byref(&dump_buf, handshake->random_client, HANDSHAKE_RANDOM_SIZE);
  buffer_dump(&dump_buf);

  DBG("Random server");
  buffer_byref(&dump_buf, handshake->random_server, HANDSHAKE_RANDOM_SIZE);
  buffer_dump(&dump_buf);
  )


  //Now generate the shared AES128 key

  DBG("Expanding the key");

  /*
   master_secret = PRF(pre_master_secret, "master secret",
                          ClientHello.random + ServerHello.random)
                          [0..47];
   */

  ret = prf_compute(premaster_key, premaster_key_size, "master secret",
      handshake->random_client, HANDSHAKE_RANDOM_SIZE,
      handshake->random_server, HANDSHAKE_RANDOM_SIZE,
      handshake->tls_socket->session.master_key, HANDSHAKE_MASTER_KEY_SIZE);
  memset(premaster_key, 0, HANDSHAKE_MAX_PREMASTER_KEY_SIZE); //Don't want to leak this
  if(ret)
  {
    return ret;
  }

  DBG_BLOCK(
  DBG("Key expanded into master key");
  buffer_byref(&dump_buf, handshake->tls_socket->session.master_key, HANDSHAKE_MASTER_KEY_SIZE);
  buffer_dump(&dump_buf);
  )

  return MINITLS_OK;
}

#define VERIFY_DATA_LENGTH 12
#define FINISHED_SIZE 12 // 12 bytes PRF // -- no 13 //length + 12 bytes prf

minitls_err_t send_finished(tls_handshake_t* handshake, buffer_t* buffer)
{
  minitls_err_t ret;

  //Write header
  ret = prepare_message(handshake, finished, buffer, FINISHED_SIZE);
  if(ret)
  {
    return ret;
  }

  crypto_sha256_t hash;
  crypto_sha256_copy(&hash, &handshake->hash.sha256); //We need to keep the global hash to check the server's finished message

  //Compute final hash
  uint8_t result[SHA256_SIZE];
  crypto_sha256_end(&hash, result);

  /*
      struct {
          opaque verify_data[verify_data_length];
      } Finished;

      verify_data
         PRF(master_secret, finished_label, Hash(handshake_messages))
            [0..verify_data_length-1];
  */

  //buffer_nu8_write(buffer, VERIFY_DATA_LENGTH); //This is optional but anyway -- NOPE

  ret = prf_compute(handshake->tls_socket->session.master_key, HANDSHAKE_MASTER_KEY_SIZE, "client finished",
      result, SHA256_SIZE,
      NULL, 0,
      buffer_current_write_position(buffer), VERIFY_DATA_LENGTH);
  buffer_n_skip(buffer, VERIFY_DATA_LENGTH);
  if(ret)
  {
    return ret;
  }

  ret = send_message(handshake, buffer);
  if(ret)
  {
    return ret;
  }

  return MINITLS_OK;
}

minitls_err_t parse_finished(tls_handshake_t* handshake, buffer_t* buffer)
{
  if(buffer_length(buffer) < VERIFY_DATA_LENGTH)
  {
    return MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
  }
/*
  size_t length = VERIFY_DATA_LENGTH;
  if(buffer_length(buffer) > VERIFY_DATA_LENGTH)
  {
    length = buffer_nu8_read(buffer);
  }
*/
  if( (buffer_length(buffer)/*length*/ != VERIFY_DATA_LENGTH) )
  {
    return MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
  }

  crypto_sha256_t hash;
  crypto_sha256_copy(&hash, &handshake->hash.sha256); //We need to keep the global hash to check the server's finished message

  //Compute final hash
  uint8_t result[SHA256_SIZE];
  crypto_sha256_end(&hash, result);

  /*
      struct {
          opaque verify_data[verify_data_length];
      } Finished;

      verify_data
         PRF(master_secret, finished_label, Hash(handshake_messages))
            [0..verify_data_length-1];
  */

  uint8_t prf[VERIFY_DATA_LENGTH];
  minitls_err_t ret = prf_compute(handshake->tls_socket->session.master_key, HANDSHAKE_MASTER_KEY_SIZE, "server finished",
      result, SHA256_SIZE,
      NULL, 0,
      prf, VERIFY_DATA_LENGTH);
  if(ret)
  {
    return ret;
  }


  if(memcmp(prf, buffer_current_read_position(buffer), VERIFY_DATA_LENGTH) != 0)
  {
    ERR("PRF differs; computed PRF was:");

    DBG_BLOCK(
    buffer_t computed_prf;
    buffer_byref(&computed_prf, prf, VERIFY_DATA_LENGTH);
    buffer_dump(&computed_prf);)

    return MINITLS_ERR_WRONG_MAC;
  }

  buffer_n_discard(buffer, VERIFY_DATA_LENGTH);

  return MINITLS_OK;
}

minitls_err_t generate_record_keys(tls_handshake_t* handshake)
{
  //Expand master key

  /*
   To generate the key material, compute

      key_block = PRF(SecurityParameters.master_secret,
                      "key expansion",
                      SecurityParameters.server_random +
                      SecurityParameters.client_random);

   until enough output has been generated.  Then, the key_block is
   partitioned as follows:

      client_write_MAC_key[SecurityParameters.mac_key_length]
      server_write_MAC_key[SecurityParameters.mac_key_length]
      client_write_key[SecurityParameters.enc_key_length]
      server_write_key[SecurityParameters.enc_key_length]
      client_write_IV[SecurityParameters.fixed_iv_length]
      server_write_IV[SecurityParameters.fixed_iv_length]
   */

  /*
  Cipher        Type    Material  Size  Size
  ------------  ------  --------  ----  -----
  AES_128_CBC   Block      16      16     16

  MAC       Algorithm    mac_length  mac_key_length
  --------  -----------  ----------  --------------
  SHA       HMAC-SHA1       20            20
  */

  //For our cipher we don't need the initialization vectors

  DBG("Expand master key");

  //Expand key
  uint8_t key_expansion[2*TLS_HMAC_SHA1_KEY_SIZE + 2*AES_128_KEY_SIZE];
  minitls_err_t ret = prf_compute(handshake->tls_socket->session.master_key, HANDSHAKE_MASTER_KEY_SIZE, "key expansion",
      handshake->random_server, HANDSHAKE_RANDOM_SIZE,
      handshake->random_client, HANDSHAKE_RANDOM_SIZE,
      key_expansion, 2*TLS_HMAC_SHA1_KEY_SIZE + 2*AES_128_KEY_SIZE);
  if(ret)
  {
    return ret;
  }

  //Init cipher/mac
  tls_record_set_keys(&handshake->tls_socket->record, TLS_SECURITY_TYPE_AES_128_CBC_SHA,
      &key_expansion[0*TLS_HMAC_SHA1_KEY_SIZE + 0*AES_128_KEY_SIZE],
      &key_expansion[1*TLS_HMAC_SHA1_KEY_SIZE + 0*AES_128_KEY_SIZE],
      &key_expansion[2*TLS_HMAC_SHA1_KEY_SIZE + 0*AES_128_KEY_SIZE],
      &key_expansion[2*TLS_HMAC_SHA1_KEY_SIZE + 1*AES_128_KEY_SIZE]
  );

  return MINITLS_OK;
}

#define HANDSHAKE_HEADER_SIZE 4

minitls_err_t parse_message(tls_handshake_t* handshake, buffer_t* buffer, handshake_type_t* message_type)
{
  if( buffer_length(buffer) < HANDSHAKE_HEADER_SIZE )
  {
    return MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
  }

  //Update SHA to use in FINISHED message
  if(handshake->state < TLS_HANDSHAKE_FINISHED_SENT) //Don't update if it is the FINISHED message sent from the server
  {
    //If TLS1.2 or protocol version unknown for now
    crypto_sha256_update(&handshake->hash.sha256, buffer_current_read_position(buffer), buffer_length(buffer));

    //FIXME SSL3-TLS1.1 or protocol version unknown for now

  }

  *message_type = (uint8_t) buffer_nu8_read(buffer);
  size_t size = buffer_nu24_read(buffer);

  if( buffer_length(buffer) != size )
  {
    return MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
  }

  return MINITLS_OK;
}

minitls_err_t prepare_message(tls_handshake_t* handshake, handshake_type_t message_type, buffer_t* buffer, size_t size)
{
  buffer_reset(buffer);
  if( buffer_size(buffer) < size + HANDSHAKE_HEADER_SIZE /* header size*/ )
  {
    ERR("Buffer too small");
    return MINITLS_ERR_BUFFER_TOO_SMALL;
  }

  buffer_nu8_write(buffer, (uint8_t)message_type);
  buffer_nu24_write(buffer, size);

  return MINITLS_OK;
}

minitls_err_t prepare_message_adjust_size(tls_handshake_t* handshake, buffer_t* buffer, size_t size)
{
  size_t length = buffer_length(buffer);

  buffer_reset(buffer);
  if( buffer_size(buffer) < size + HANDSHAKE_HEADER_SIZE /* header size*/ )
  {
    return MINITLS_ERR_BUFFER_TOO_SMALL;
  }

  buffer_set_length(buffer, 1); //Skip message type

  buffer_nu24_write(buffer, size);

  buffer_set_length(buffer, length);

  return MINITLS_OK;
}


minitls_err_t send_message(tls_handshake_t* handshake, buffer_t* buffer)
{
  //Update SHA to use in FINISHED messages
  crypto_sha256_update(&handshake->hash.sha256, buffer_current_read_position(buffer), buffer_length(buffer));

  //DEBUG TODO: check mismatch with length

  minitls_err_t ret = tls_record_send(&handshake->tls_socket->record, TLS_HANDSHAKE, buffer);
  if(ret)
  {
    ERR("Send returned %d", ret);
    return ret;
  }
  return MINITLS_OK;
}

void handle_ret(tls_handshake_t* handshake, minitls_err_t ret)
{
  ERR("Will return error %d", ret);

  //Make handshake fail
  handshake->state = TLS_HANDSHAKE_FAILED;

  //Send alert to other party
  switch(ret)
  {
  case MINITLS_ERR_WRONG_CERTIFICATE:
    tls_alert_send(&handshake->tls_socket->record, TLS_ALERT_FATAL, CERTIFICATE_UNKNOWN, &handshake->tls_socket->record.buffer);
    break;
  case MINITLS_ERR_PROTOCOL_NON_CONFORMANT:
    tls_alert_send(&handshake->tls_socket->record, TLS_ALERT_FATAL, UNEXPECTED_MESSAGE, &handshake->tls_socket->record.buffer);
    break;
  case MINITLS_ERR_NOT_IMPLEMENTED:
  default:
    tls_alert_send(&handshake->tls_socket->record, TLS_ALERT_FATAL, HANDSHAKE_FAILURE, &handshake->tls_socket->record.buffer);
    break;
  }
}


minitls_err_t prf_compute(const uint8_t* secret, size_t secret_size,
    const char* label,
    const uint8_t* seed1, size_t seed1_size,
    const uint8_t* seed2, size_t seed2_size,
    uint8_t* out, size_t out_size)
{
  //PRF TLS1.2
  //TODO add PRF SSL3-TLS1.1

  //DBG("PRF: Secret %p [%d] - label '%s' - Seed %p [%d] - out %p [%d]", secret, secret_size, label, seed, seed_size, out, out_size);
  //We are using the HMAC-SHA256 MAC (non negotiable)

  /*
  P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
                         HMAC_hash(secret, A(2) + seed) +
                         HMAC_hash(secret, A(3) + seed) + ...

  A(0) = seed
  A(i) = HMAC_hash(secret, A(i-1))

  PRF(secret, label, seed) = P_hash(secret, label + seed)

  */

  //MAC_HMAC_SHA1_SIZE
  uint8_t mac[HMAC_SHA256_SIZE];
  uint8_t A[HMAC_SHA256_SIZE];

  //A[0] = seed
  crypto_hmac_sha256_t hmac_sha256;

  //minitls_err_t ret;

  size_t current_size = 0;

  while(current_size < out_size)
  {
    //DBG("Current size %d", current_size);

    //Compute A[n]=f(A[n-1])
    crypto_hmac_sha256_init(&hmac_sha256, secret, secret_size);

    if(current_size == 0) //First iteration
    {
      crypto_hmac_sha256_update(&hmac_sha256, (const uint8_t*)label, strlen(label));

      crypto_hmac_sha256_update(&hmac_sha256, seed1, seed1_size);
      if(seed2 != NULL)
      {
        crypto_hmac_sha256_update(&hmac_sha256, seed2, seed2_size);
      }
    }
    else
    {
      crypto_hmac_sha256_update(&hmac_sha256, A, HMAC_SHA256_SIZE);
    }

    crypto_hmac_sha256_end(&hmac_sha256, A);

    //Compute HMAC_hash(secret, A[n] + seed)
    crypto_hmac_sha256_init(&hmac_sha256, secret, secret_size);

    crypto_hmac_sha256_update(&hmac_sha256, A, HMAC_SHA256_SIZE);

    crypto_hmac_sha256_update(&hmac_sha256, (const uint8_t*)label, strlen(label));

    crypto_hmac_sha256_update(&hmac_sha256, seed1, seed1_size);
    if(seed2 != NULL)
    {
      crypto_hmac_sha256_update(&hmac_sha256, seed2, seed2_size);
    }

    crypto_hmac_sha256_end(&hmac_sha256, mac);

    //Copy and truncate if needed
    size_t append_size = MIN( out_size - current_size, HMAC_SHA256_SIZE );
    memcpy(out + current_size, mac, append_size);

    current_size += append_size;
  }

  return MINITLS_OK;
}

minitls_err_t change_cipher_spec_request(tls_handshake_t* handshake, buffer_t* buffer)
{
  /*
  The change cipher spec protocol exists to signal transitions in
  ciphering strategies.  The protocol consists of a single message,
  which is encrypted and compressed under the current (not the pending)
  connection state.  The message consists of a single byte of value 1.

  struct {
      enum { change_cipher_spec(1), (255) } type;
  } ChangeCipherSpec;
  */
  buffer_reset(buffer);
  if( buffer_size(buffer) < 1 )
  {
   return MINITLS_ERR_BUFFER_TOO_SMALL;
  }

  buffer_nu8_write(buffer, 1);

  minitls_err_t ret = tls_record_send(&handshake->tls_socket->record, TLS_CHANGE_CIPHER_SPEC, buffer);
  if(ret)
  {
    return ret;
  }

  return MINITLS_OK;
}