A super trimmed down TLS stack, GPL licensed

Dependents:   MiniTLS-HTTPS-Example

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.

Revision:
0:35aa5be3b78d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tls/tls_handshake.c	Fri Jun 06 10:49:02 2014 +0000
@@ -0,0 +1,1671 @@
+/*
+MuTLS - 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 mutls_err_t send_hello_client(tls_handshake_t* handshake, buffer_t* buffer);
+static mutls_err_t parse_hello_server(tls_handshake_t* handshake, buffer_t* buffer, bool* resumed);
+static mutls_err_t parse_server_certificate(tls_handshake_t* handshake, buffer_t* buffer);
+static mutls_err_t parse_server_key_exchange(tls_handshake_t* handshake, buffer_t* buffer);
+static mutls_err_t parse_client_certificate_request(tls_handshake_t* handshake, buffer_t* buffer);
+static mutls_err_t parse_hello_done_server(tls_handshake_t* handshake, buffer_t* buffer);
+static mutls_err_t send_client_certificate(tls_handshake_t* handshake, buffer_t* buffer);
+static mutls_err_t send_client_key_exchange(tls_handshake_t* handshake, buffer_t* buffer);
+static mutls_err_t send_finished(tls_handshake_t* handshake, buffer_t* buffer);
+static mutls_err_t parse_finished(tls_handshake_t* handshake, buffer_t* buffer);
+
+static mutls_err_t generate_record_keys(tls_handshake_t* handshake);
+
+//Receive stuff
+static mutls_err_t parse_message(tls_handshake_t* handshake, buffer_t* buffer, handshake_type_t* message_type);
+
+//Send stuff
+static mutls_err_t prepare_message(tls_handshake_t* handshake, handshake_type_t message_type, buffer_t* buffer, size_t size);
+static mutls_err_t prepare_message_adjust_size(tls_handshake_t* handshake, buffer_t* buffer, size_t size);
+static mutls_err_t send_message(tls_handshake_t* handshake, buffer_t* buffer);
+
+//Error handlers
+static void handle_ret(tls_handshake_t* handshake, mutls_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 mutls_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 mutls_err_t change_cipher_spec_request(tls_handshake_t* handshake, buffer_t* buffer);
+
+mutls_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 MUTLS_OK;
+}
+
+
+mutls_err_t tls_handshake_start(tls_handshake_t* handshake)
+{
+  mutls_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 MUTLS_OK;
+}
+
+mutls_err_t tls_handshake_process(tls_handshake_t* handshake, buffer_t* buffer)
+{
+  //Parse buffer
+  DBG("Parsing message");
+  handshake_type_t type;
+  mutls_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 = MUTLS_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 = MUTLS_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 = MUTLS_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 = MUTLS_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 = MUTLS_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 = MUTLS_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 = MUTLS_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 = MUTLS_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 = MUTLS_ERR_PROTOCOL_NON_CONFORMANT;
+      handle_ret(handshake, ret);
+      return ret;
+    }
+    break;
+  default:
+    ret = MUTLS_ERR_PROTOCOL_NON_CONFORMANT;
+    handle_ret(handshake, ret);
+    return ret;
+  }
+
+  return MUTLS_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 MUTLS_CFG_PROTOCOL_TLS_1_2
+  crypto_sha256_init(&handshake->hash.sha256);
+#endif
+#if (MUTLS_CFG_PROTOCOL_TLS_1_1 || MUTLS_CFG_PROTOCOL_TLS_1_0 || MUTLS_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;
+
+mutls_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;
+  */
+  mutls_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->mutls->prng, handshake->random_client, HANDSHAKE_RANDOM_SIZE);
+
+  //Write most recent protocol version supported
+#if MUTLS_CFG_PROTOCOL_TLS_1_2
+  buffer_nu8_write(buffer, TLS_1_2_VERSION_MAJOR);
+  buffer_nu8_write(buffer, TLS_1_2_VERSION_MINOR);
+#elif MUTLS_CFG_PROTOCOL_TLS_1_1
+  buffer_nu8_write(buffer, TLS_1_1_VERSION_MAJOR);
+  buffer_nu8_write(buffer, TLS_1_1_VERSION_MINOR);
+#elif MUTLS_CFG_PROTOCOL_TLS_1_0
+  buffer_nu8_write(buffer, TLS_1_0_VERSION_MAJOR);
+  buffer_nu8_write(buffer, TLS_1_0_VERSION_MINOR);
+#elif MUTLS_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 MUTLS_OK;
+}
+
+mutls_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 MUTLS_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 MUTLS_CFG_PROTOCOL_TLS_1_2
+      ( (version.major != TLS_1_2_VERSION_MAJOR) || (version.minor != TLS_1_2_VERSION_MINOR) ) &&
+#endif
+#if MUTLS_CFG_PROTOCOL_TLS_1_1
+      ( (version.major != TLS_1_1_VERSION_MAJOR) || (version.minor != TLS_1_1_VERSION_MINOR) ) &&
+#endif
+#if MUTLS_CFG_PROTOCOL_TLS_1_0
+      ( (version.major != TLS_1_0_VERSION_MAJOR) || (version.minor != TLS_1_0_VERSION_MINOR) ) &&
+#endif
+#if MUTLS_CFG_PROTOCOL_SSL_3
+      ( (version.major != SSL_3_VERSION_MAJOR) || (version.minor != SSL_3_VERSION_MINOR) ) &&
+#endif
+      true )
+  {
+    ERR("Version mismatch");
+    return MUTLS_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 MUTLS_ERR_PROTOCOL_NON_CONFORMANT; ///Will raise a "decode error" alert
+  }
+  if(server_session_id_length > SESSION_ID_MAX_SIZE) //Buffer overflow attack
+  {
+    return MUTLS_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 MUTLS_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 MUTLS_OK;
+}
+
+mutls_err_t parse_server_certificate(tls_handshake_t* handshake, buffer_t* buffer)
+{
+  if( buffer_length(buffer) < 3) //Certificate list size
+  {
+    return MUTLS_ERR_PROTOCOL_NON_CONFORMANT;
+  }
+
+  size_t cert_list_size = buffer_nu24_read(buffer);
+
+  if( buffer_length(buffer) < cert_list_size ) //Certificate list
+  {
+    return MUTLS_ERR_PROTOCOL_NON_CONFORMANT;
+  }
+
+  if( buffer_length(buffer) < 3) //Certificate size
+  {
+    return MUTLS_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 MUTLS_ERR_PROTOCOL_NON_CONFORMANT;
+  }
+
+  //Only parse first certificate in the chain, ignore others
+  if( cert_size != handshake->tls_socket->mutls->certificate->certificate_size )
+  {
+    ERR("Certificate does not match");
+    //Cannot contain certificate, return security error
+    return MUTLS_ERR_WRONG_CERTIFICATE;
+  }
+
+  //Do a plain, stupid, comparison with the certificate we have
+  if( memcmp( handshake->tls_socket->mutls->certificate->certificate, buffer_current_read_position(buffer), handshake->tls_socket->mutls->certificate->certificate_size) != 0 )
+  {
+    ERR("Certificate does not match");
+    //This is not the same certificate
+    return MUTLS_ERR_WRONG_CERTIFICATE;
+  }
+  buffer_n_discard(buffer, handshake->tls_socket->mutls->certificate->certificate_size);
+
+
+  return MUTLS_OK;
+}
+
+mutls_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 MUTLS_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 MUTLS_ERR_NOT_IMPLEMENTED;
+  }
+
+  crypto_ecc_curve_type_t curve_type = buffer_nu16_read(buffer);
+
+  DBG("ECC Curve %d", curve_type);
+
+  mutls_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 MUTLS_ERR_PROTOCOL_NON_CONFORMANT;
+  }
+
+  size_t point_size = buffer_nu8_read(buffer);
+
+  if( buffer_length(buffer) < point_size )
+  {
+    return MUTLS_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 MUTLS_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 MUTLS_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 MUTLS_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->mutls->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 MUTLS_OK;
+#else
+  return MUTLS_ERR_PROTOCOL_NON_CONFORMANT; //Illegal for these cipher suites
+#endif
+}
+
+mutls_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 MUTLS_OK;
+}
+
+mutls_err_t parse_hello_done_server(tls_handshake_t* handshake, buffer_t* buffer)
+{
+  if( buffer_length(buffer) != 0 )
+  {
+    return MUTLS_ERR_PROTOCOL_NON_CONFORMANT;
+  }
+  return MUTLS_OK;
+}
+
+#define CLIENT_CERTIFICATE_NOCERTS_SIZE 3
+
+mutls_err_t send_client_certificate(tls_handshake_t* handshake, buffer_t* buffer)
+{
+  //Send a 0-length certificate structure
+
+  mutls_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 MUTLS_OK;
+}
+
+#define CLIENT_KEY_EXCHANGE_BASE_SIZE 0
+
+mutls_err_t send_client_key_exchange(tls_handshake_t* handshake, buffer_t* buffer)
+{
+  mutls_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->mutls->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 MUTLS_CFG_PROTOCOL_TLS_1_2
+  premaster_key[0] = TLS_1_2_VERSION_MAJOR;
+  premaster_key[1] = TLS_1_2_VERSION_MINOR;
+#elif MUTLS_CFG_PROTOCOL_TLS_1_1
+  premaster_key[0] = TLS_1_1_VERSION_MAJOR;
+  premaster_key[1] = TLS_1_1_VERSION_MINOR;
+#elif MUTLS_CFG_PROTOCOL_TLS_1_0
+  premaster_key[0] = TLS_1_0_VERSION_MAJOR;
+  premaster_key[1] = TLS_1_0_VERSION_MINOR;
+#elif MUTLS_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->mutls->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->mutls->certificate->public_key.rsa,
+      premaster_key, premaster_key_size,
+      encrypted_premaster_key, sizeof(encrypted_premaster_key), &encrypted_premaster_key_size, handshake->tls_socket->mutls->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 MUTLS_OK;
+}
+
+#define VERIFY_DATA_LENGTH 12
+#define FINISHED_SIZE 12 // 12 bytes PRF // -- no 13 //length + 12 bytes prf
+
+mutls_err_t send_finished(tls_handshake_t* handshake, buffer_t* buffer)
+{
+  mutls_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 MUTLS_OK;
+}
+
+mutls_err_t parse_finished(tls_handshake_t* handshake, buffer_t* buffer)
+{
+  if(buffer_length(buffer) < VERIFY_DATA_LENGTH)
+  {
+    return MUTLS_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 MUTLS_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];
+  mutls_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 MUTLS_ERR_WRONG_MAC;
+  }
+
+  buffer_n_discard(buffer, VERIFY_DATA_LENGTH);
+
+  return MUTLS_OK;
+}
+
+mutls_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];
+  mutls_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 MUTLS_OK;
+}
+
+#define HANDSHAKE_HEADER_SIZE 4
+
+mutls_err_t parse_message(tls_handshake_t* handshake, buffer_t* buffer, handshake_type_t* message_type)
+{
+  if( buffer_length(buffer) < HANDSHAKE_HEADER_SIZE )
+  {
+    return MUTLS_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 MUTLS_ERR_PROTOCOL_NON_CONFORMANT;
+  }
+
+  return MUTLS_OK;
+}
+
+mutls_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 MUTLS_ERR_BUFFER_TOO_SMALL;
+  }
+
+  buffer_nu8_write(buffer, (uint8_t)message_type);
+  buffer_nu24_write(buffer, size);
+
+  return MUTLS_OK;
+}
+
+mutls_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 MUTLS_ERR_BUFFER_TOO_SMALL;
+  }
+
+  buffer_set_length(buffer, 1); //Skip message type
+
+  buffer_nu24_write(buffer, size);
+
+  buffer_set_length(buffer, length);
+
+  return MUTLS_OK;
+}
+
+
+mutls_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
+
+  mutls_err_t ret = tls_record_send(&handshake->tls_socket->record, TLS_HANDSHAKE, buffer);
+  if(ret)
+  {
+    ERR("Send returned %d", ret);
+    return ret;
+  }
+  return MUTLS_OK;
+}
+
+void handle_ret(tls_handshake_t* handshake, mutls_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 MUTLS_ERR_WRONG_CERTIFICATE:
+    tls_alert_send(&handshake->tls_socket->record, TLS_ALERT_FATAL, CERTIFICATE_UNKNOWN, &handshake->tls_socket->record.buffer);
+    break;
+  case MUTLS_ERR_PROTOCOL_NON_CONFORMANT:
+    tls_alert_send(&handshake->tls_socket->record, TLS_ALERT_FATAL, UNEXPECTED_MESSAGE, &handshake->tls_socket->record.buffer);
+    break;
+  case MUTLS_ERR_NOT_IMPLEMENTED:
+  default:
+    tls_alert_send(&handshake->tls_socket->record, TLS_ALERT_FATAL, HANDSHAKE_FAILURE, &handshake->tls_socket->record.buffer);
+    break;
+  }
+}
+
+
+mutls_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;
+
+  //mutls_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 MUTLS_OK;
+}
+
+mutls_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 MUTLS_ERR_BUFFER_TOO_SMALL;
+  }
+
+  buffer_nu8_write(buffer, 1);
+
+  mutls_err_t ret = tls_record_send(&handshake->tls_socket->record, TLS_CHANGE_CIPHER_SPEC, buffer);
+  if(ret)
+  {
+    return ret;
+  }
+
+  return MUTLS_OK;
+}