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:
3:eb324ffffd2b
Parent:
2:527a66d0a1a9
--- a/tls/tls_record.c	Mon Jun 09 14:57:54 2014 +0000
+++ b/tls/tls_record.c	Tue Jun 10 14:22:36 2014 +0000
@@ -22,7 +22,7 @@
  * \author Donatien Garnier
  */
 
-#define __DEBUG__ 0
+#define __DEBUG__ 4
 #ifndef __MODULE__
 #define __MODULE__ "tls_record.c"
 #endif
@@ -138,6 +138,7 @@
   //Init security
   record->security_rx_state = TLS_SECURITY_NONE;
   record->security_tx_state = TLS_SECURITY_NONE;
+  record->security_type = TLS_SECURITY_TYPE_NULL_NULL_NULL;
 
   //Memset keys
   memset(&record->client_write_mac_key, 0, TLS_HMAC_SHA1_KEY_SIZE);
@@ -286,54 +287,73 @@
 
   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 ) )
+#if CRYPTO_AES_128
+    if(record->security_type == TLS_SECURITY_TYPE_AES_128_CBC_SHA)
     {
-      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);
+      DBG("IV + Ciphertext");
+      DBG_BLOCK(buffer_dump(&record->buffer);)
 
-    //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;
-    }
+      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);
 
-    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);
+      //Decrypt message
+      ret = crypto_aes_128_cbc_decrypt( &record->cipher_rx.aes_128, &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;
+      }
 
-    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;
-    }
+      DBG("Plaintext + MAC + padding + padding length");
+      DBG_BLOCK(buffer_dump(&record->buffer);)
 
-    int p;
-    //Check each padding byte
-    for(p = 0; p < padding_length; p++)
-    {
-      if( *(buffer_current_write_position(&record->buffer) - 1 - p) != padding_length )
+      //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");
+        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);
+      //Remove trailing padding + padding length
+      buffer_set_length(&record->buffer, buffer_length(&record->buffer) - 1 - padding_length);
+    }
+    else
+#endif
+#if CRYPTO_ARC4
+    if(record->security_type == TLS_SECURITY_TYPE_ARC4_SHA)
+    {
+      DBG("Ciphertext");
+      DBG_BLOCK(buffer_dump(&record->buffer);)
+
+      //Decrypt message
+      crypto_arc4_process( &record->cipher_rx.arc4, &record->buffer );
+    }
+    else
+#endif
+    {}
 
     DBG("Plaintext + MAC");
     DBG_BLOCK(buffer_dump(&record->buffer);)
@@ -487,35 +507,54 @@
     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)
+#if CRYPTO_AES_128
+    if(record->security_type == TLS_SECURITY_TYPE_AES_128_CBC_SHA)
     {
-      return MINITLS_ERR_BUFFER_TOO_SMALL;
-    }
+
+      //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++)
-    {
+      for(padding_item = 0; padding_item < padding_length; padding_item++)
+      {
+        buffer_nu8_write(payload, padding_length);
+      }
+
       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 );
 
-    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);
+      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)
+      //Encrypt message
+      ret = crypto_aes_128_cbc_encrypt( &record->cipher_tx.aes_128, &header_iv, payload );
+      if(ret)
+      {
+        ERR("Failed to encipher, ret %d", ret);
+        return ret;
+      }
+    }
+    else
+#endif
+#if CRYPTO_ARC4
+    if(record->security_type == TLS_SECURITY_TYPE_ARC4_SHA)
     {
-      ERR("Failed to encipher, ret %d", ret);
-      return ret;
+      //No IV
+      buffer_init( &header_iv, NULL, 0 ); //0 Length
+
+      //Encrypt message
+      crypto_arc4_process( &record->cipher_tx.arc4, payload );
     }
+    else
+#endif
+    {}
 
     DBG("Ciphertext");
     DBG_BLOCK(buffer_dump(payload);)
@@ -569,7 +608,7 @@
 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)
+  if( (security != TLS_SECURITY_TYPE_AES_128_CBC_SHA) && (security != TLS_SECURITY_TYPE_ARC4_SHA) )
   {
     return MINITLS_ERR_NOT_IMPLEMENTED;
   }
@@ -577,7 +616,7 @@
   //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->client_write_cipher_key, client_write_cipher_key, AES_128_KEY_SIZE); //TODO generic key size
   memcpy(&record->server_write_cipher_key, server_write_cipher_key, AES_128_KEY_SIZE);
 
   //Intialize cipher
@@ -585,11 +624,27 @@
   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);
+  switch(security)
+  {
+#if CRYPTO_AES_128
+  case TLS_SECURITY_TYPE_AES_128_CBC_SHA:
+    crypto_aes_128_init(&record->cipher_tx.aes_128, record->client_write_cipher_key, expand_encryption_key);
+    crypto_aes_128_init(&record->cipher_rx.aes_128, record->server_write_cipher_key, expand_decryption_key);
+    break;
+#endif
+#if CRYPTO_ARC4
+  case TLS_SECURITY_TYPE_ARC4_SHA:
+    crypto_arc4_init(&record->cipher_tx.arc4, record->client_write_cipher_key, TLS_ARC4_KEY_SIZE );
+    crypto_arc4_init(&record->cipher_rx.arc4, record->server_write_cipher_key, TLS_ARC4_KEY_SIZE);
+    break;
+#endif
+  default:
+    break;
+  }
 
   record->security_tx_state = TLS_SECURITY_INTIALIZED;
   record->security_rx_state = TLS_SECURITY_INTIALIZED;
+  record->security_type = security;
 
   return MINITLS_OK;
 }
@@ -684,7 +739,7 @@
      return ret;
     }
 
-    int count = socket_recv(record->socket_fd, buffer_current_write_position(&record->buffer), size - buffer_length(&record->buffer));
+    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);