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:
2:527a66d0a1a9
Child:
3:eb324ffffd2b
diff -r 27b41ba7e847 -r 527a66d0a1a9 tls/tls_record.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tls/tls_record.c	Mon Jun 09 14:57:54 2014 +0000
@@ -0,0 +1,838 @@
+/*
+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_record.c
+ * \copyright Copyright (c) AppNearMe Ltd 2013
+ * \author Donatien Garnier
+ */
+
+#define __DEBUG__ 0
+#ifndef __MODULE__
+#define __MODULE__ "tls_record.c"
+#endif
+
+#include "core/fwk.h"
+#include "inc/minitls_config.h"
+#include "inc/minitls_errors.h"
+#include "tls_record.h"
+#include "tls_alert.h"
+
+#include "tls_handshake.h"
+#include "tls_socket.h"
+
+#include "socket/socket.h"
+
+#include "crypto/crypto_aes_128_cbc.h"
+#include "crypto/crypto_hmac_sha1.h"
+
+static minitls_err_t record_wait_readable(tls_record_t* record);
+static minitls_err_t record_wait_writeable(tls_record_t* record);
+
+static minitls_err_t record_socket_read(tls_record_t* record, size_t size);
+static minitls_err_t record_socket_write(tls_record_t* record, buffer_t* data);
+
+static minitls_err_t tls_mac_append( const uint8_t* key, tls_content_type_t content_type, tls_protocol_version_t version,
+    uint64_t sequence_number, buffer_t* buffer );
+static minitls_err_t tls_mac_check( const uint8_t* key, tls_content_type_t content_type, tls_protocol_version_t version,
+    uint64_t sequence_number, buffer_t* buffer );
+
+typedef struct __tls_fragment_header
+{
+  tls_content_type_t type;
+  tls_protocol_version_t version;
+  uint16_t length; //(MAX 2^14 + 2048 = 18432)
+} tls_fragment_header_t;
+
+#define FRAGMENT_HEADER_SIZE 5
+
+#define DEFAULT_READ_TIMEOUT 20000
+#define DEFAULT_WRITE_TIMEOUT 20000
+
+minitls_err_t tls_record_init(tls_record_t* record, tls_socket_t* sock, uint8_t* buf, size_t buf_size)
+{
+
+  record->handshake_done = false;
+
+  //Open BSD socket
+  record->socket_fd = socket_socket();
+  if(record->socket_fd < 0)
+  {
+    ERR("Could not create socket descriptor");
+    return MINITLS_ERR_SOCKET_ERROR;
+  }
+
+  record->read_timeout = DEFAULT_READ_TIMEOUT;
+  record->write_timeout = DEFAULT_WRITE_TIMEOUT;
+
+  if(buf_size >= TLS_DEFAULT_MAX_FRAGMENT_SIZE)
+  {
+    record->max_fragment_size = TLS_DEFAULT_MAX_FRAGMENT_SIZE;
+  }
+  else if( buf_size >= 4096 + TLS_ENCRYPTION_MAX_OVERHEAD )
+  {
+    record->max_fragment_size = 4096;
+  }
+  else if( buf_size >= 2048 + TLS_ENCRYPTION_MAX_OVERHEAD )
+  {
+    record->max_fragment_size = 2048;
+  }
+  else if( buf_size >= 1024 + TLS_ENCRYPTION_MAX_OVERHEAD )
+  {
+    record->max_fragment_size = 1024;
+  }
+  else if( buf_size >= 512 + TLS_ENCRYPTION_MAX_OVERHEAD )
+  {
+    record->max_fragment_size = 512;
+  }
+  else
+  {
+    ERR("Buffer is too small");
+    return MINITLS_ERR_BUFFER_TOO_SMALL;
+  }
+
+  DBG("Max fragment size: %d bytes", record->max_fragment_size);
+
+  if( (buf_size != TLS_DEFAULT_MAX_FRAGMENT_SIZE)
+      && (buf_size != (record->max_fragment_size + TLS_ENCRYPTION_MAX_OVERHEAD)) )
+  {
+    WARN("Buffer size is not optimum");
+  }
+
+  //Initialize with oldest protocol version by default (as recommended by RFC 5246's Annex E)
+#if MINITLS_CFG_PROTOCOL_SSL_3
+  record->version.major = SSL_3_VERSION_MAJOR;
+  record->version.minor = SSL_3_VERSION_MINOR;
+#elif MINITLS_CFG_PROTOCOL_TLS_1_0
+  record->version.major = TLS_1_0_VERSION_MAJOR;
+  record->version.minor = TLS_1_0_VERSION_MINOR;
+#elif MINITLS_CFG_PROTOCOL_TLS_1_1
+  record->version.major = TLS_1_1_VERSION_MAJOR;
+  record->version.minor = TLS_1_1_VERSION_MINOR;
+#elif MINITLS_CFG_PROTOCOL_TLS_1_2
+  record->version.major = TLS_1_2_VERSION_MAJOR;
+  record->version.minor = TLS_1_2_VERSION_MINOR;
+#else
+#error No SSL/TLS protocol version enabled
+#endif
+
+  buffer_init(&record->buffer, buf, buf_size);
+
+  record->tls_socket = sock;
+
+  //Init security
+  record->security_rx_state = TLS_SECURITY_NONE;
+  record->security_tx_state = TLS_SECURITY_NONE;
+
+  //Memset keys
+  memset(&record->client_write_mac_key, 0, TLS_HMAC_SHA1_KEY_SIZE);
+  memset(&record->server_write_mac_key, 0, TLS_HMAC_SHA1_KEY_SIZE);
+  memset(&record->client_write_cipher_key, 0, AES_128_KEY_SIZE);
+  memset(&record->server_write_cipher_key, 0, AES_128_KEY_SIZE);
+
+  return MINITLS_OK;
+}
+
+void tls_record_set_protocol_version(tls_record_t* record, uint8_t major, uint8_t minor)
+{
+  record->version.major = major;
+  record->version.minor = minor;
+}
+
+void tls_record_get_protocol_version(tls_record_t* record, uint8_t* major, uint8_t* minor)
+{
+  *major = record->version.major;
+  *minor = record->version.minor;
+}
+
+minitls_err_t tls_record_change_cipher_spec(tls_record_t* record, bool tx_nrx)
+{
+  if(tx_nrx)
+  {
+    if(record->security_tx_state == TLS_SECURITY_INTIALIZED)
+    {
+      record->security_tx_state = TLS_SECURITY_ACTIVE;
+      return MINITLS_OK;
+    }
+    else
+    {
+      return MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
+    }
+  }
+  else
+  {
+    if(record->security_rx_state == TLS_SECURITY_INTIALIZED)
+    {
+      record->security_rx_state = TLS_SECURITY_ACTIVE;
+      return MINITLS_OK;
+    }
+    else
+    {
+      return MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
+    }
+  }
+}
+
+bool tls_record_is_secure(tls_record_t* record)
+{
+  if( record->security_tx_state != TLS_SECURITY_ACTIVE )
+  {
+    return false;
+  }
+  if( record->security_rx_state != TLS_SECURITY_ACTIVE )
+  {
+    return false;
+  }
+  return true;
+}
+
+minitls_err_t tls_record_connect(tls_record_t* record, const char* hostname, uint16_t port)
+{
+  DBG("Trying to connect to %s:%d", hostname, port);
+
+  int r = socket_connect(record->socket_fd, hostname, port);
+  if(r < 0)
+  {
+    socket_close(record->socket_fd);
+    record->socket_fd = -1;
+    return MINITLS_ERR_SOCKET_ERROR;
+  }
+
+  return MINITLS_OK;
+}
+
+minitls_err_t tls_record_process(tls_record_t* record)
+{
+  //Reset buffer length
+  buffer_reset(&record->buffer);
+
+  //Read header
+  minitls_err_t ret = record_socket_read(record, FRAGMENT_HEADER_SIZE);
+  if(ret == MINITLS_ERR_SOCKET_CLOSED)
+  {
+    return MINITLS_ERR_SOCKET_CLOSED;
+  }
+  else if(ret)
+  {
+    ERR("Socket err %d", ret);
+    tls_alert_send( record, TLS_ALERT_FATAL, INTERNAL_ERROR, &record->buffer);
+    return ret;
+  }
+
+  //Read version
+  tls_fragment_header_t header;
+
+  header.type = buffer_nu8_read(&record->buffer);
+  header.version.major = buffer_nu8_read(&record->buffer);
+  header.version.minor = buffer_nu8_read(&record->buffer);
+  header.length = buffer_nu16_read(&record->buffer);
+
+#if 1 //TODO how to relax this?
+  if( (header.version.major != record->version.major) || (header.version.minor != record->version.minor) )
+  {
+    ERR("Version mismatch");
+    tls_alert_send( record, TLS_ALERT_FATAL, PROTOCOL_VERSION, &record->buffer);
+    return MINITLS_ERR_PROTOCOL_VERSION;
+  }
+#endif
+  //Check content type
+  //Check that encryption level is OK for this content type
+  switch( header.type )
+  {
+  //All of these are OK in plain mode
+  case TLS_CHANGE_CIPHER_SPEC:
+  case TLS_ALERT:
+  case TLS_HANDSHAKE:
+    break;
+  //This is only acceptable in ciphered mode:
+  case TLS_APPLICATION_DATA:
+    if( (!tls_record_is_secure(record)) || (!record->handshake_done) )
+    {
+      tls_alert_send( record, TLS_ALERT_FATAL, INSUFFICIENT_SECURITY, &record->buffer);
+      return MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
+    }
+    break;
+  default:
+    tls_alert_send( record, TLS_ALERT_FATAL, ILLEGAL_PARAMETER, &record->buffer);
+    return MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
+  }
+
+  //Reset buffer
+  buffer_reset(&record->buffer);
+
+  //Read payload
+  ret = record_socket_read(record, header.length);
+  if(ret)
+  {
+    ERR("Socket err %d", ret);
+    tls_alert_send( record, TLS_ALERT_FATAL, INTERNAL_ERROR, &record->buffer);
+    return ret;
+  }
+
+  if( record->security_rx_state == TLS_SECURITY_ACTIVE )
+  {
+    DBG("IV + Ciphertext");
+    DBG_BLOCK(buffer_dump(&record->buffer);)
+
+    buffer_t buffer_iv_header;
+    if( (buffer_length(&record->buffer) < 2*AES_128_BLOCK_SIZE) || ( (buffer_length(&record->buffer) % AES_128_BLOCK_SIZE) != 0 ) )
+    {
+      tls_alert_send( record, TLS_ALERT_FATAL, UNEXPECTED_MESSAGE, &record->buffer );
+      return MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
+    }
+    buffer_byref(&buffer_iv_header, buffer_current_read_position(&record->buffer), AES_128_BLOCK_SIZE); //Extract IV vector
+    buffer_n_discard(&record->buffer, AES_128_BLOCK_SIZE);
+
+    //Decrypt message
+    ret = crypto_aes_128_cbc_decrypt( &record->cipher_rx, &buffer_iv_header, &record->buffer );
+    if(ret)
+    {
+      ERR("Failed to decipher, ret %d", ret);
+      tls_alert_send( record, TLS_ALERT_FATAL, DECRYPT_ERROR, &record->buffer );
+      return ret;
+    }
+
+    DBG("Plaintext + MAC + padding + padding length");
+    DBG_BLOCK(buffer_dump(&record->buffer);)
+
+    //Check and remove padding
+    size_t padding_length = *(buffer_current_write_position(&record->buffer) - 1);
+
+    if( padding_length + 1 > buffer_length(&record->buffer) )
+    {
+      ERR("Wrong padding length");
+      tls_alert_send( record, TLS_ALERT_FATAL, BAD_RECORD_MAC, &record->buffer );
+      return MINITLS_ERR_CRYPTO;
+    }
+
+    int p;
+    //Check each padding byte
+    for(p = 0; p < padding_length; p++)
+    {
+      if( *(buffer_current_write_position(&record->buffer) - 1 - p) != padding_length )
+      {
+        ERR("Wrong padding");
+        tls_alert_send( record, TLS_ALERT_FATAL, BAD_RECORD_MAC, &record->buffer );
+        return MINITLS_ERR_CRYPTO;
+      }
+    }
+
+    //Remove trailing padding + padding length
+    buffer_set_length(&record->buffer, buffer_length(&record->buffer) - 1 - padding_length);
+
+    DBG("Plaintext + MAC");
+    DBG_BLOCK(buffer_dump(&record->buffer);)
+
+    //Check MAC
+    ret = tls_mac_check( record->server_write_mac_key, header.type, header.version, record->sequence_number_rx, &record->buffer );
+    if(ret)
+    {
+      ERR("MAC Check failed, ret %d", ret);
+      tls_alert_send( record, TLS_ALERT_FATAL, BAD_RECORD_MAC, &record->buffer );
+      return ret;
+    }
+
+    DBG("Plaintext");
+    DBG_BLOCK(buffer_dump(&record->buffer);)
+
+    //Increment seq number
+    record->sequence_number_rx++;
+  }
+  else
+  {
+    //No security
+  }
+
+  //Now dispatch depending on content type
+  switch( header.type )
+  {
+  case TLS_CHANGE_CIPHER_SPEC:
+    ret = tls_record_change_cipher_spec(record, false);
+    if(ret)
+    {
+      ERR("Invalid change cipher spec request, ret %d", ret);
+      tls_alert_send( record, TLS_ALERT_FATAL, UNEXPECTED_MESSAGE, &record->buffer );
+      return ret;
+    }
+    break;
+  case TLS_ALERT:
+    ret = tls_alert_process( record, &record->buffer );
+    if(ret)
+    {
+      tls_record_close(record);
+      //Close connection in any case
+      if(ret == MINITLS_ERR_CONNECTION_CLOSED)
+      {
+        DBG("Connection closed by remote party");
+        return MINITLS_OK;
+      }
+      //FIXME Do something
+      ERR("Alert received, ret %d", ret);
+      return ret;
+    }
+    break;
+  case TLS_HANDSHAKE:
+    if(/*(record->tls_socket->handshake != NULL) &&*/ !tls_handshake_is_done(&record->tls_socket->handshake))
+    {
+      ret = tls_handshake_process(&record->tls_socket->handshake, &record->buffer );
+      if(ret)
+      {
+        ERR("Handshake process returned %d", ret);
+        //TLS alert already sent by handshake function
+        tls_handshake_clean(&record->tls_socket->handshake); //Cleanup handshake
+        //record->tls_socket->handshake = NULL;
+        return ret;
+      }
+      if(tls_handshake_is_done(&record->tls_socket->handshake))
+      {
+        tls_handshake_clean(&record->tls_socket->handshake); //Cleanup handshake
+        //record->tls_socket->handshake = NULL;
+        record->handshake_done = true; //Enable application data layer
+      }
+      return MINITLS_OK;
+    }
+    else
+    {
+      ERR("Unexpected handshake message, ret %d", ret);
+      tls_alert_send( record, TLS_ALERT_FATAL, UNEXPECTED_MESSAGE, &record->buffer );
+      return ret;
+    }
+  case TLS_APPLICATION_DATA:
+    //Pass message to socket layer
+    return tls_socket_readable_callback(record->tls_socket,  &record->buffer);
+  default:
+    //Has already been checked above
+    return MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
+  }
+
+  return MINITLS_OK;
+}
+
+minitls_err_t tls_record_send(tls_record_t* record, tls_content_type_t content_type, buffer_t* payload)
+{
+  minitls_err_t ret;
+  int padding_item;
+  /*
+  struct {
+      opaque IV[SecurityParameters.record_iv_length];
+      block-ciphered struct {
+          opaque content[TLSCompressed.length];
+          opaque MAC[SecurityParameters.mac_length];
+          uint8 padding[GenericBlockCipher.padding_length];
+          uint8 padding_length;
+      };
+  } GenericBlockCipher;
+  */
+
+  //Check content type
+  //Check that encryption level is OK for this content type
+  switch( content_type )
+  {
+  //All of these are OK in plain mode
+  case TLS_CHANGE_CIPHER_SPEC:
+  case TLS_ALERT:
+  case TLS_HANDSHAKE:
+    break;
+  //This is only acceptable in ciphered mode:
+  case TLS_APPLICATION_DATA:
+    if( (!tls_record_is_secure(record)) || (!record->handshake_done) )
+    {
+      tls_alert_send( record, TLS_ALERT_FATAL, INSUFFICIENT_SECURITY, &record->buffer);
+      return MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
+    }
+    break;
+  default:
+    tls_alert_send( record, TLS_ALERT_FATAL, ILLEGAL_PARAMETER, &record->buffer);
+    return MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
+  }
+
+  //Buffer must have enough space to add IV (head) + MAC (tail) + padding (tail)
+
+  buffer_t header_iv;
+  uint8_t header_iv_data[AES_128_BLOCK_SIZE];
+  if( record->security_tx_state == TLS_SECURITY_ACTIVE )
+  {
+    //FIXME generate a random IV
+
+    DBG("Plaintext");
+    DBG_BLOCK(buffer_dump(payload);)
+
+    //Compute & append MAC
+    DBG("Sequence number: %d", record->sequence_number_tx);
+    ret = tls_mac_append( record->client_write_mac_key, content_type, record->version, record->sequence_number_tx, payload );
+    if(ret)
+    {
+      ERR("Could not append MAC, ret %d", ret);
+      return ret;
+    }
+
+    //Increment sequence number
+    record->sequence_number_tx++;
+
+    DBG("Plaintext + MAC");
+    DBG_BLOCK(buffer_dump(payload);)
+
+    //Add padding
+    size_t padding_length = AES_128_BLOCK_SIZE - (buffer_length(payload) % AES_128_BLOCK_SIZE) - 1;
+    if(buffer_space(payload) < padding_length)
+    {
+      return MINITLS_ERR_BUFFER_TOO_SMALL;
+    }
+
+    for(padding_item = 0; padding_item < padding_length; padding_item++)
+    {
+      buffer_nu8_write(payload, padding_length);
+    }
+
+    buffer_nu8_write(payload, padding_length);
+
+    DBG("Plaintext + MAC + Padding + Padding Length");
+    DBG_BLOCK(buffer_dump(payload);)
+
+    buffer_init( &header_iv, header_iv_data, AES_128_BLOCK_SIZE );
+
+    crypto_prng_get(record->tls_socket->minitls->prng, buffer_current_write_position(&header_iv), AES_128_BLOCK_SIZE);
+    buffer_n_skip(&header_iv, AES_128_BLOCK_SIZE);
+
+    //Encrypt message
+    ret = crypto_aes_128_cbc_encrypt( &record->cipher_tx, &header_iv, payload );
+    if(ret)
+    {
+      ERR("Failed to encipher, ret %d", ret);
+      return ret;
+    }
+
+    DBG("Ciphertext");
+    DBG_BLOCK(buffer_dump(payload);)
+  }
+  else
+  {
+    buffer_init( &header_iv, NULL, 0 ); //0 Length
+  }
+
+  //Now send message header
+  tls_fragment_header_t header;
+  header.type = content_type;
+  header.version.major = record->version.major;
+  header.version.minor = record->version.minor;
+  header.length = buffer_length( &header_iv ) + buffer_length(payload);
+
+  buffer_t header_fragment;
+  uint8_t header_fragment_data[FRAGMENT_HEADER_SIZE];
+
+  buffer_init( &header_fragment, header_fragment_data, FRAGMENT_HEADER_SIZE );
+
+  buffer_nu8_write(&header_fragment, header.type);
+  buffer_nu8_write(&header_fragment, header.version.major);
+  buffer_nu8_write(&header_fragment, header.version.minor);
+  buffer_nu16_write(&header_fragment, header.length);
+
+  //Send fragment header
+  ret = record_socket_write(record, &header_fragment);
+  if(ret)
+  {
+    return ret;
+  }
+
+  //Send IV
+  ret = record_socket_write(record, &header_iv);
+  if(ret)
+  {
+    return ret;
+  }
+
+  //Send payload
+  ret = record_socket_write(record, payload);
+  if(ret)
+  {
+    return ret;
+  }
+
+  return MINITLS_OK;
+}
+
+minitls_err_t tls_record_set_keys(tls_record_t* record, tls_security_type_t security, const uint8_t* client_write_mac_key, const uint8_t* server_write_mac_key,
+    const uint8_t* client_write_cipher_key, const uint8_t* server_write_cipher_key)
+{
+  if(security != TLS_SECURITY_TYPE_AES_128_CBC_SHA)
+  {
+    return MINITLS_ERR_NOT_IMPLEMENTED;
+  }
+
+  //Copy keys
+  memcpy(&record->client_write_mac_key, client_write_mac_key, TLS_HMAC_SHA1_KEY_SIZE);
+  memcpy(&record->server_write_mac_key, server_write_mac_key, TLS_HMAC_SHA1_KEY_SIZE);
+  memcpy(&record->client_write_cipher_key, client_write_cipher_key, AES_128_KEY_SIZE);
+  memcpy(&record->server_write_cipher_key, server_write_cipher_key, AES_128_KEY_SIZE);
+
+  //Intialize cipher
+
+  record->sequence_number_tx = 0;
+  record->sequence_number_rx = 0;
+
+  crypto_aes_128_init(&record->cipher_tx, record->client_write_cipher_key, expand_encryption_key);
+  crypto_aes_128_init(&record->cipher_rx, record->server_write_cipher_key, expand_decryption_key);
+
+  record->security_tx_state = TLS_SECURITY_INTIALIZED;
+  record->security_rx_state = TLS_SECURITY_INTIALIZED;
+
+  return MINITLS_OK;
+}
+
+minitls_err_t tls_record_close(tls_record_t* record)
+{
+  if(record->socket_fd < 0) //Already closed
+  {
+    return MINITLS_OK;
+  }
+
+  //Don't really care about the return
+  tls_alert_send(record, TLS_ALERT_WARNING, CLOSE_NOTIFY, &record->buffer);
+
+  //Close socket
+  socket_close(record->socket_fd);
+  record->socket_fd = -1;
+
+  return MINITLS_OK;
+}
+
+minitls_err_t tls_record_set_read_timeout(tls_record_t* record, int timeout)
+{
+  record->read_timeout = timeout;
+
+  return MINITLS_OK;
+}
+
+minitls_err_t tls_record_set_write_timeout(tls_record_t* record, int timeout)
+{
+  record->write_timeout = timeout;
+
+  return MINITLS_OK;
+}
+
+minitls_err_t record_wait_readable(tls_record_t* record)
+{
+  if(record->socket_fd < 0)
+  {
+    return MINITLS_ERR_SOCKET_CLOSED;
+  }
+
+  //Wait for record to be readable
+  int ret = socket_wait_readable(record->socket_fd, record->read_timeout );
+  if( ret < 0 )
+  {
+    //Timeout
+    return MINITLS_ERR_TIMEOUT;
+  }
+  return MINITLS_OK;
+}
+
+minitls_err_t record_wait_writeable(tls_record_t* record)
+{
+  if(record->socket_fd < 0)
+  {
+    return MINITLS_ERR_SOCKET_CLOSED;
+  }
+
+  //Wait for record to be writeable
+  int ret = socket_wait_writeable(record->socket_fd, record->write_timeout );
+  if( ret < 0 )
+  {
+    //Timeout
+    return MINITLS_ERR_TIMEOUT;
+  }
+  return MINITLS_OK;
+}
+
+minitls_err_t record_socket_read(tls_record_t* record, size_t size)
+{
+  minitls_err_t ret;
+  if(record->socket_fd < 0)
+  {
+    return MINITLS_ERR_SOCKET_CLOSED;
+  }
+
+  DBG("Trying to read %d bytes", size);
+  while(size > 0)
+  {
+    //Read Fragment length
+    if( buffer_space(&record->buffer) < size )
+    {
+      ERR("Won't be able to read packet (%d bytes to read - %d bytes of space)", size, buffer_space(&record->buffer));
+      return MINITLS_ERR_BUFFER_TOO_SMALL;
+    }
+
+    ret = record_wait_readable(record);
+    if(ret)
+    {
+     ERR("Timeout");
+     return ret;
+    }
+
+    int count = socket_recv(record->socket_fd, buffer_current_write_position(&record->buffer), size - buffer_length(&record->buffer));
+    if( count > 0 )
+    {
+      buffer_n_skip(&record->buffer, count);
+      size -= count;
+    }
+    else if( count == 0 )
+    {
+      WARN("Socket closed");
+      return MINITLS_ERR_SOCKET_CLOSED;
+    }
+    else
+    {
+      ERR("Error (returned %d)", count);
+      return MINITLS_ERR_SOCKET_ERROR;
+    }
+  }
+
+  DBG_BLOCK(buffer_dump(&record->buffer);)
+
+  return MINITLS_OK;
+}
+
+minitls_err_t record_socket_write(tls_record_t* record, buffer_t* data)
+{
+  minitls_err_t ret;
+  if(record->socket_fd < 0)
+  {
+    return MINITLS_ERR_SOCKET_CLOSED;
+  }
+
+  DBG("Trying to write %d bytes", buffer_length(data));
+  DBG_BLOCK(buffer_dump(data);)
+  while(buffer_length(data) > 0)
+  {
+    ret = record_wait_writeable(record);
+    if(ret)
+    {
+     ERR("Timeout");
+     return ret;
+    }
+
+    int count = socket_send(record->socket_fd, buffer_current_read_position(data), buffer_length(data));
+    if( count > 0 )
+    {
+      buffer_n_discard(data, count);
+    }
+    else if( count == 0 )
+    {
+      WARN("Socket closed");
+      return MINITLS_ERR_SOCKET_CLOSED;
+    }
+    else
+    {
+      ERR("Error (returned %d)", count);
+      return MINITLS_ERR_SOCKET_ERROR;
+    }
+  }
+  DBG("Done");
+  return MINITLS_OK;
+}
+
+
+minitls_err_t tls_mac_append( const uint8_t* key, tls_content_type_t content_type, tls_protocol_version_t version,
+    uint64_t sequence_number, buffer_t* buffer )
+{
+  crypto_hmac_sha1_t mac;
+  crypto_hmac_sha1_init(&mac, key, TLS_HMAC_SHA1_KEY_SIZE);
+
+  if( buffer_space(buffer) < HMAC_SHA1_SIZE )
+  {
+    return MINITLS_ERR_BUFFER_TOO_SMALL;
+  }
+
+  uint8_t header_buf[13];
+  buffer_t header;
+
+  buffer_init(&header, header_buf, 13);
+
+  buffer_nu64_write(&header, sequence_number);
+
+  buffer_nu8_write(&header, content_type);
+
+  buffer_nu8_write(&header, version.major);
+  buffer_nu8_write(&header, version.minor);
+
+  buffer_nu16_write(&header, buffer_length(buffer));
+
+  crypto_hmac_sha1_update(&mac, header_buf, 13);
+  crypto_hmac_sha1_update(&mac, buffer_current_read_position(buffer), buffer_length(buffer));
+  crypto_hmac_sha1_end(&mac, buffer_current_write_position(buffer));
+  buffer_n_skip(buffer, HMAC_SHA1_SIZE);
+
+  return MINITLS_OK;
+}
+
+minitls_err_t tls_mac_check( const uint8_t* key, tls_content_type_t content_type, tls_protocol_version_t version,
+    uint64_t sequence_number, buffer_t* buffer )
+{
+  crypto_hmac_sha1_t mac;
+  crypto_hmac_sha1_init(&mac, key, TLS_HMAC_SHA1_KEY_SIZE);
+
+  if( buffer_length(buffer) < HMAC_SHA1_SIZE )
+  {
+    return MINITLS_ERR_PROTOCOL_NON_CONFORMANT;
+  }
+
+  size_t data_offset = buffer_get_read_offset(buffer);
+  size_t data_length = buffer_length(buffer) - HMAC_SHA1_SIZE;
+
+  uint8_t check[HMAC_SHA1_SIZE];
+
+  uint8_t header_buf[13];
+  buffer_t header;
+
+  buffer_init(&header, header_buf, 13);
+
+  buffer_nu64_write(&header, sequence_number);
+
+  buffer_nu8_write(&header, content_type);
+
+  buffer_nu8_write(&header, version.major);
+  buffer_nu8_write(&header, version.minor);
+
+  buffer_nu16_write(&header, data_length);
+
+  crypto_hmac_sha1_update(&mac, header_buf, 13);
+  crypto_hmac_sha1_update(&mac, buffer_current_read_position(buffer), data_length);
+  buffer_n_discard(buffer, data_length);
+  crypto_hmac_sha1_end(&mac, check);
+
+  if( memcmp(buffer_current_read_position(buffer), check, HMAC_SHA1_SIZE) != 0 )
+  {
+    ERR("MAC differs; computed MAC was:");
+
+    buffer_t computed_mac;
+    buffer_byref(&computed_mac, check, HMAC_SHA1_SIZE);
+    DBG_BLOCK(buffer_dump(&computed_mac);)
+
+    return MINITLS_ERR_WRONG_MAC;
+  }
+
+  //Reset buffer position and discard MAC
+  buffer_set_read_offset(buffer, data_offset);
+  buffer_set_length(buffer, data_length);
+
+  return MINITLS_OK;
+}
+
+
+
+