[PATCH 2/2] Explicit symmetric cipher state versionning.

Jonathan Bastien-Filiatrault joe at x2a.org
Fri Sep 17 05:32:06 CEST 2010


This introduces the concept of a "cipher epoch". The epoch number is
the number of successful handshakes and is incremented by one each
time. This concept is native to DTLS and this patch makes the
symmetric cipher state explicit for TLS in preparation for DTLS. This
concept was implicit in plain TLS and ChangeCipherSpec messages
triggered a "pending state copy". Now, we the current epoch number is
simply incremented to the parameters negotiated by the handshake.

The main side effects of this patch is a slightly more abstract
internal API and, in some cases, simpler code. The session blob format
is also changed a bit since this patch avoids storing information that
is now redundant. If this breaks library users' expectations, this
side effect can be negated.

The cipher_specs structure has been removed. The conn_state has become
record_state_st. Only symmetric cipher information is
versioned. Things such as key exchange algorithm and the master secret
are not versioned and their handling is unchanged.

I have tested this patch as much as I could. It introduces no test
suite regressions on my x64 Debian GNU/Linux system.

Do not hesitate to point out shortcomings or suggest changes. Since
this is a big diff, I am expecting this to be an iterative process.

Signed-off-by: Jonathan Bastien-Filiatrault <joe at x2a.org>
---
 lib/ext_session_ticket.c  |   54 +--
 lib/gnutls_alert.c        |    2 +-
 lib/gnutls_buffers.c      |    1 +
 lib/gnutls_cipher.c       |   78 ++--
 lib/gnutls_cipher.h       |   10 +-
 lib/gnutls_compress.c     |   11 +-
 lib/gnutls_compress.h     |    6 +-
 lib/gnutls_constate.c     |  970 +++++++++++++++++++--------------------------
 lib/gnutls_constate.h     |   32 +-
 lib/gnutls_handshake.c    |   38 ++-
 lib/gnutls_int.h          |   84 +++--
 lib/gnutls_num.h          |    2 +-
 lib/gnutls_record.c       |   74 +++-
 lib/gnutls_record.h       |    2 +-
 lib/gnutls_session_pack.c |   30 +-
 lib/gnutls_state.c        |   71 ++--
 libextra/gnutls_ia.c      |    2 +-
 17 files changed, 676 insertions(+), 791 deletions(-)

diff --git a/lib/ext_session_ticket.c b/lib/ext_session_ticket.c
index 69e4ee9..4c02518 100644
--- a/lib/ext_session_ticket.c
+++ b/lib/ext_session_ticket.c
@@ -34,6 +34,7 @@
 #include <ext_session_ticket.h>
 #include <gnutls_mbuffers.h>
 #include <gnutls_extensions.h>
+#include <gnutls_constate.h>
 
 #ifdef ENABLE_SESSION_TICKET
 
@@ -579,30 +580,6 @@ gnutls_session_ticket_enable_server (gnutls_session_t session,
   return 0;
 }
 
-#define SAVE_WRITE_SECURITY_PARAMETERS					\
-  do									\
-    {									\
-      write_bulk_cipher_algorithm =					\
-	session->security_parameters.write_bulk_cipher_algorithm;	\
-      write_mac_algorithm =						\
-	session->security_parameters.write_mac_algorithm;		\
-      write_compression_algorithm =					\
-	session->security_parameters.write_compression_algorithm;	\
-    }									\
-  while (0)
-
-#define RESTORE_WRITE_SECURITY_PARAMETERS				\
-  do									\
-    {									\
-      session->security_parameters.write_bulk_cipher_algorithm =	\
-	write_bulk_cipher_algorithm;					\
-      session->security_parameters.write_mac_algorithm =		\
-	write_mac_algorithm;						\
-      session->security_parameters.write_compression_algorithm =	\
-	write_compression_algorithm;					\
-    }									\
-  while (0)
-
 int
 _gnutls_send_new_session_ticket (gnutls_session_t session, int again)
 {
@@ -612,11 +589,9 @@ _gnutls_send_new_session_ticket (gnutls_session_t session, int again)
   int ret;
   struct ticket ticket;
   uint16_t ticket_len;
-  gnutls_cipher_algorithm_t write_bulk_cipher_algorithm;
-  gnutls_mac_algorithm_t write_mac_algorithm;
-  gnutls_compression_method_t write_compression_algorithm;
   session_ticket_ext_st* priv=NULL;
   extension_priv_data_t epriv;
+  uint16_t epoch_saved = session->security_parameters.epoch_write;
 
   if (again == 0)
     {
@@ -632,28 +607,17 @@ _gnutls_send_new_session_ticket (gnutls_session_t session, int again)
          _gnutls_write_connection_state_init() does this job, but it also
          triggers encryption, while NewSessionTicket should not be
          encrypted in the record layer. */
-      SAVE_WRITE_SECURITY_PARAMETERS;
-      ret = _gnutls_set_write_cipher (session,
-				      _gnutls_cipher_suite_get_cipher_algo
-				      (&session->
-				       security_parameters.current_cipher_suite));
+      ret = _gnutls_epoch_set_keys (session, session->security_parameters.epoch_next);
       if (ret < 0)
-	return ret;
+	{
+	  gnutls_assert ();
+	  return ret;
+	}
 
-      ret = _gnutls_set_write_mac (session,
-				   _gnutls_cipher_suite_get_mac_algo
-				   (&session->
-				    security_parameters.current_cipher_suite));
-      if (ret < 0)
-	return ret;
-      ret = _gnutls_set_write_compression (session,
-					   session->
-					   internals.compression_method);
-      if (ret < 0)
-	return ret;
+      session->security_parameters.epoch_write = session->security_parameters.epoch_next;
 
       ret = encrypt_ticket (session, priv, &ticket);
-      RESTORE_WRITE_SECURITY_PARAMETERS;
+      session->security_parameters.epoch_write = epoch_saved;
       if (ret < 0)
 	{
 	  gnutls_assert ();
diff --git a/lib/gnutls_alert.c b/lib/gnutls_alert.c
index 170ca7e..c376fd5 100644
--- a/lib/gnutls_alert.c
+++ b/lib/gnutls_alert.c
@@ -133,7 +133,7 @@ gnutls_alert_send (gnutls_session_t session, gnutls_alert_level_t level,
   _gnutls_record_log ("REC: Sending Alert[%d|%d] - %s\n", data[0],
 		      data[1], name);
 
-  if ((ret = _gnutls_send_int (session, GNUTLS_ALERT, -1, data, 2, MBUFFER_FLUSH)) >= 0)
+  if ((ret = _gnutls_send_int (session, GNUTLS_ALERT, -1, EPOCH_WRITE_CURRENT, data, 2, MBUFFER_FLUSH)) >= 0)
     return 0;
   else
     return ret;
diff --git a/lib/gnutls_buffers.c b/lib/gnutls_buffers.c
index be84e90..1964c72 100644
--- a/lib/gnutls_buffers.c
+++ b/lib/gnutls_buffers.c
@@ -753,6 +753,7 @@ _gnutls_handshake_io_write_flush (gnutls_session_t session)
     {
       ret = _gnutls_send_int (session, GNUTLS_HANDSHAKE,
 			      session->internals.handshake_send_buffer_htype,
+			      EPOCH_WRITE_CURRENT,
 			      msg.data, msg.size, 0/* do not flush */);
 
       if (ret >= 0)
diff --git a/lib/gnutls_cipher.c b/lib/gnutls_cipher.c
index 5e327e2..bf73ab7 100644
--- a/lib/gnutls_cipher.c
+++ b/lib/gnutls_cipher.c
@@ -45,8 +45,10 @@
 inline static int
 is_write_comp_null (gnutls_session_t session)
 {
-  if (session->security_parameters.write_compression_algorithm ==
-      GNUTLS_COMP_NULL)
+  record_parameters_st *record_params;
+
+  _gnutls_epoch_get (session, EPOCH_WRITE_CURRENT, &record_params);
+  if (record_params->compression_algorithm == GNUTLS_COMP_NULL)
     return 0;
 
   return 1;
@@ -55,8 +57,10 @@ is_write_comp_null (gnutls_session_t session)
 inline static int
 is_read_comp_null (gnutls_session_t session)
 {
-  if (session->security_parameters.read_compression_algorithm ==
-      GNUTLS_COMP_NULL)
+  record_parameters_st *record_params;
+
+  _gnutls_epoch_get (session, EPOCH_READ_CURRENT, &record_params);
+  if (record_params->compression_algorithm == GNUTLS_COMP_NULL)
     return 0;
 
   return 1;
@@ -72,7 +76,8 @@ int
 _gnutls_encrypt (gnutls_session_t session, const opaque * headers,
 		 size_t headers_size, const opaque * data,
 		 size_t data_size, opaque * ciphertext,
-		 size_t ciphertext_size, content_type_t type, int random_pad)
+		 size_t ciphertext_size, content_type_t type, int random_pad,
+		 record_parameters_st *params)
 {
   gnutls_datum_t plain;
   gnutls_datum_t comp;
@@ -92,7 +97,7 @@ _gnutls_encrypt (gnutls_session_t session, const opaque * headers,
       /* Here comp is allocated and must be 
        * freed.
        */
-      ret = _gnutls_m_plaintext2compressed (session, &comp, &plain);
+      ret = _gnutls_m_plaintext2compressed (session, &comp, &plain, params);
       if (ret < 0)
 	{
 	  gnutls_assert ();
@@ -102,7 +107,7 @@ _gnutls_encrypt (gnutls_session_t session, const opaque * headers,
 
   ret = _gnutls_compressed2ciphertext (session, &ciphertext[headers_size],
 				       ciphertext_size - headers_size,
-				       comp, type, random_pad);
+				       comp, type, random_pad, params);
 
   if (free_comp)
     _gnutls_free_datum (&comp);
@@ -127,7 +132,8 @@ _gnutls_encrypt (gnutls_session_t session, const opaque * headers,
 int
 _gnutls_decrypt (gnutls_session_t session, opaque * ciphertext,
 		 size_t ciphertext_size, uint8_t * data,
-		 size_t max_data_size, content_type_t type)
+		 size_t max_data_size, content_type_t type,
+		 record_parameters_st *params)
 {
   gnutls_datum_t gtxt;
   gnutls_datum_t gcipher;
@@ -141,7 +147,7 @@ _gnutls_decrypt (gnutls_session_t session, opaque * ciphertext,
 
   ret =
     _gnutls_ciphertext2compressed (session, data, max_data_size,
-				   gcipher, type);
+				   gcipher, type, params);
   if (ret < 0)
     {
       return ret;
@@ -161,7 +167,7 @@ _gnutls_decrypt (gnutls_session_t session, opaque * ciphertext,
 
       gcomp.data = data;
       gcomp.size = ret;
-      ret = _gnutls_m_compressed2plaintext (session, &gtxt, &gcomp);
+      ret = _gnutls_m_compressed2plaintext (session, &gtxt, &gcomp, params);
       if (ret < 0)
 	{
 	  return ret;
@@ -335,7 +341,8 @@ int
 _gnutls_compressed2ciphertext (gnutls_session_t session,
 			       opaque * cipher_data, int cipher_size,
 			       gnutls_datum_t compressed,
-			       content_type_t _type, int random_pad)
+			       content_type_t _type, int random_pad,
+			       record_parameters_st *params)
 {
   uint8_t MAC[MAX_HASH_SIZE];
   uint16_t c_length;
@@ -345,14 +352,11 @@ _gnutls_compressed2ciphertext (gnutls_session_t session,
   opaque preamble[PREAMBLE_SIZE];
   int preamble_size;
   int hash_size =
-    _gnutls_hash_get_algo_len (session->
-			       security_parameters.write_mac_algorithm);
+    _gnutls_hash_get_algo_len (params->mac_algorithm);
   int blocksize =
-    gnutls_cipher_get_block_size (session->
-				  security_parameters.write_bulk_cipher_algorithm);
+    gnutls_cipher_get_block_size (params->cipher_algorithm);
   cipher_type_t block_algo =
-    _gnutls_cipher_is_block (session->
-			     security_parameters.write_bulk_cipher_algorithm);
+    _gnutls_cipher_is_block (params->cipher_algorithm);
   opaque *data_ptr;
   int ver = gnutls_protocol_get_version (session);
 
@@ -361,13 +365,13 @@ _gnutls_compressed2ciphertext (gnutls_session_t session,
 
   c_length = _gnutls_conv_uint16 (compressed.size);
 
-  if (session->security_parameters.write_mac_algorithm != GNUTLS_MAC_NULL)
+  if (params->mac_algorithm != GNUTLS_MAC_NULL)
     {				/* actually when the algorithm in not the NULL one */
       digest_hd_st td;
 
-      ret = mac_init (&td, session->security_parameters.write_mac_algorithm,
-		      session->connection_state.write_mac_secret.data,
-		      session->connection_state.write_mac_secret.size, ver);
+      ret = mac_init (&td, params->mac_algorithm,
+		      params->write.mac_secret.data,
+		      params->write.mac_secret.size, ver);
 
       if (ret < 0)
 	{
@@ -376,7 +380,7 @@ _gnutls_compressed2ciphertext (gnutls_session_t session,
 	}
       preamble_size =
 	make_preamble (UINT64DATA
-		       (session->connection_state.write_sequence_number),
+		       (params->write.sequence_number),
 		       type, c_length, ver, preamble);
       mac_hash (&td, preamble, preamble_size, ver);
       mac_hash (&td, compressed.data, compressed.size, ver);
@@ -436,7 +440,7 @@ _gnutls_compressed2ciphertext (gnutls_session_t session,
   /* Actual encryption (inplace).
    */
   ret =
-    _gnutls_cipher_encrypt (&session->connection_state.write_cipher_state,
+    _gnutls_cipher_encrypt (&params->write.cipher_state,
 			    cipher_data, length);
   if (ret < 0)
     {
@@ -455,7 +459,8 @@ int
 _gnutls_ciphertext2compressed (gnutls_session_t session,
 			       opaque * compress_data,
 			       int compress_size,
-			       gnutls_datum_t ciphertext, uint8_t type)
+			       gnutls_datum_t ciphertext, uint8_t type,
+			       record_parameters_st *params)
 {
   uint8_t MAC[MAX_HASH_SIZE];
   uint16_t c_length;
@@ -467,23 +472,19 @@ _gnutls_ciphertext2compressed (gnutls_session_t session,
   int preamble_size;
   int ver = gnutls_protocol_get_version (session);
   int hash_size =
-    _gnutls_hash_get_algo_len (session->
-			       security_parameters.read_mac_algorithm);
+    _gnutls_hash_get_algo_len (params->mac_algorithm);
 
   blocksize =
-    gnutls_cipher_get_block_size (session->
-				  security_parameters.read_bulk_cipher_algorithm);
+    gnutls_cipher_get_block_size (params->cipher_algorithm);
 
 
   /* actual decryption (inplace)
    */
-  switch (_gnutls_cipher_is_block
-	  (session->security_parameters.read_bulk_cipher_algorithm))
+  switch (_gnutls_cipher_is_block (params->cipher_algorithm))
     {
     case CIPHER_STREAM:
       if ((ret =
-	   _gnutls_cipher_decrypt (&session->
-				   connection_state.read_cipher_state,
+	   _gnutls_cipher_decrypt (&params->read.cipher_state,
 				   ciphertext.data, ciphertext.size)) < 0)
 	{
 	  gnutls_assert ();
@@ -501,8 +502,7 @@ _gnutls_ciphertext2compressed (gnutls_session_t session,
 	}
 
       if ((ret =
-	   _gnutls_cipher_decrypt (&session->
-				   connection_state.read_cipher_state,
+	   _gnutls_cipher_decrypt (&params->read.cipher_state,
 				   ciphertext.data, ciphertext.size)) < 0)
 	{
 	  gnutls_assert ();
@@ -562,13 +562,13 @@ _gnutls_ciphertext2compressed (gnutls_session_t session,
   /* Pass the type, version, length and compressed through
    * MAC.
    */
-  if (session->security_parameters.read_mac_algorithm != GNUTLS_MAC_NULL)
+  if (params->mac_algorithm != GNUTLS_MAC_NULL)
     {
       digest_hd_st td;
 
-      ret = mac_init (&td, session->security_parameters.read_mac_algorithm,
-		      session->connection_state.read_mac_secret.data,
-		      session->connection_state.read_mac_secret.size, ver);
+      ret = mac_init (&td, params->mac_algorithm,
+		      params->read.mac_secret.data,
+		      params->read.mac_secret.size, ver);
 
       if (ret < 0)
 	{
@@ -578,7 +578,7 @@ _gnutls_ciphertext2compressed (gnutls_session_t session,
 
       preamble_size =
 	make_preamble (UINT64DATA
-		       (session->connection_state.read_sequence_number), type,
+		       (params->read.sequence_number), type,
 		       c_length, ver, preamble);
       mac_hash (&td, preamble, preamble_size, ver);
       if (length > 0)
diff --git a/lib/gnutls_cipher.h b/lib/gnutls_cipher.h
index d2265b3..75a5aa4 100644
--- a/lib/gnutls_cipher.h
+++ b/lib/gnutls_cipher.h
@@ -27,16 +27,18 @@ int _gnutls_encrypt (gnutls_session_t session, const opaque * headers,
 		     size_t headers_size, const opaque * data,
 		     size_t data_size, opaque * ciphertext,
 		     size_t ciphertext_size, content_type_t type,
-		     int random_pad);
+		     int random_pad, record_parameters_st *params);
 
 int _gnutls_decrypt (gnutls_session_t session, opaque * ciphertext,
 		     size_t ciphertext_size, uint8_t * data, size_t data_size,
-		     content_type_t type);
+		     content_type_t type, record_parameters_st *params);
 int _gnutls_compressed2ciphertext (gnutls_session_t session,
 				   opaque * cipher_data, int cipher_size,
 				   gnutls_datum_t compressed,
-				   content_type_t _type, int random_pad);
+				   content_type_t _type, int random_pad,
+				   record_parameters_st *params);
 int _gnutls_ciphertext2compressed (gnutls_session_t session,
 				   opaque * compress_data,
 				   int compress_size,
-				   gnutls_datum_t ciphertext, uint8_t type);
+				   gnutls_datum_t ciphertext, uint8_t type,
+				   record_parameters_st *params);
diff --git a/lib/gnutls_compress.c b/lib/gnutls_compress.c
index 0a49df1..4efa0f1 100644
--- a/lib/gnutls_compress.c
+++ b/lib/gnutls_compress.c
@@ -30,6 +30,7 @@
 #include "gnutls_int.h"
 #include "gnutls_compress.h"
 #include "gnutls_errors.h"
+#include "gnutls_constate.h"
 #include <gnutls_algorithms.h>
 #include <gnutls/gnutls.h>
 
@@ -38,13 +39,14 @@
 int
 _gnutls_m_plaintext2compressed (gnutls_session_t session,
 				gnutls_datum_t * compressed,
-				const gnutls_datum_t * plaintext)
+				const gnutls_datum_t * plaintext,
+				const record_parameters_st *params)
 {
   int size;
   opaque *data;
 
   size =
-    _gnutls_compress (session->connection_state.write_compression_state,
+    _gnutls_compress (params->write.compression_state,
 		      plaintext->data, plaintext->size, &data,
 		      MAX_RECORD_SEND_SIZE + EXTRA_COMP_SIZE);
   if (size < 0)
@@ -61,13 +63,14 @@ _gnutls_m_plaintext2compressed (gnutls_session_t session,
 int
 _gnutls_m_compressed2plaintext (gnutls_session_t session,
 				gnutls_datum_t * plain,
-				const gnutls_datum_t * compressed)
+				const gnutls_datum_t * compressed,
+				const record_parameters_st *params)
 {
   int size;
   opaque *data;
 
   size =
-    _gnutls_decompress (session->connection_state.read_compression_state,
+    _gnutls_decompress (params->read.compression_state,
 			compressed->data, compressed->size, &data,
 			MAX_RECORD_RECV_SIZE);
   if (size < 0)
diff --git a/lib/gnutls_compress.h b/lib/gnutls_compress.h
index 273c8fe..93b2786 100644
--- a/lib/gnutls_compress.h
+++ b/lib/gnutls_compress.h
@@ -27,10 +27,12 @@
 
 int _gnutls_m_plaintext2compressed (gnutls_session_t session,
 				    gnutls_datum_t * compressed,
-				    const gnutls_datum_t * plaintext);
+				    const gnutls_datum_t * plaintext,
+				    const record_parameters_st *params);
 int _gnutls_m_compressed2plaintext (gnutls_session_t session,
 				    gnutls_datum_t * plain,
-				    const gnutls_datum_t * compressed);
+				    const gnutls_datum_t * compressed,
+				    const record_parameters_st *params);
 
 /* Algorithm handling. */
 int _gnutls_supported_compression_methods (gnutls_session_t session,
diff --git a/lib/gnutls_constate.c b/lib/gnutls_constate.c
index b91abc4..9fa0665 100644
--- a/lib/gnutls_constate.c
+++ b/lib/gnutls_constate.c
@@ -36,6 +36,7 @@
 #include <gnutls_datum.h>
 #include <gnutls_state.h>
 #include <gnutls_extensions.h>
+#include <gnutls_buffers.h>
 
 static const char keyexp[] = "key expansion";
 static const int keyexp_length = sizeof (keyexp) - 1;
@@ -57,7 +58,7 @@ static const int servwrite_length = sizeof (servwrite) - 1;
  * (session->cipher_specs)
  */
 static int
-_gnutls_set_keys (gnutls_session_t session, int hash_size, int IV_size,
+_gnutls_set_keys (gnutls_session_t session, record_parameters_st *params, int hash_size, int IV_size,
 		  int key_size, int export_flag)
 {
   /* FIXME: This function is too long
@@ -70,15 +71,10 @@ _gnutls_set_keys (gnutls_session_t session, int hash_size, int IV_size,
   /* avoid using malloc */
   opaque key_block[2 * MAX_HASH_SIZE + 2 * MAX_CIPHER_KEY_SIZE +
 		   2 * MAX_CIPHER_BLOCK_SIZE];
+  record_state_st *client_write, *server_write;
 
-  if (session->cipher_specs.generated_keys != 0)
-    {
-      /* keys have already been generated.
-       * reset generated_keys and exit normally.
-       */
-      session->cipher_specs.generated_keys = 0;
-      return 0;
-    }
+  client_write = session->security_parameters.entity == GNUTLS_CLIENT ? &params->write : &params->read;
+  server_write = session->security_parameters.entity == GNUTLS_SERVER ? &params->write : &params->read;
 
   block_size = 2 * hash_size + 2 * key_size;
   if (export_flag == 0)
@@ -110,42 +106,28 @@ _gnutls_set_keys (gnutls_session_t session, int hash_size, int IV_size,
     }
 
   if (ret < 0)
-    {
-      gnutls_assert ();
-      return ret;
-    }
+    return gnutls_assert_val (ret);
 
   _gnutls_hard_log ("INT: KEY BLOCK[%d]: %s\n", block_size,
 		    _gnutls_bin2hex (key_block, block_size, buf,
 				     sizeof (buf), NULL));
 
-  _gnutls_free_datum (&session->cipher_specs.server_write_mac_secret);
-  _gnutls_free_datum (&session->cipher_specs.client_write_mac_secret);
-  _gnutls_free_datum (&session->cipher_specs.server_write_IV);
-  _gnutls_free_datum (&session->cipher_specs.client_write_IV);
-  _gnutls_free_datum (&session->cipher_specs.server_write_key);
-  _gnutls_free_datum (&session->cipher_specs.client_write_key);
-
   pos = 0;
   if (hash_size > 0)
     {
 
       if (_gnutls_sset_datum
-	  (&session->cipher_specs.client_write_mac_secret,
+	  (&client_write->mac_secret,
 	   &key_block[pos], hash_size) < 0)
-	{
-	  gnutls_assert ();
-	  return GNUTLS_E_MEMORY_ERROR;
-	}
+	return gnutls_assert_val (GNUTLS_E_MEMORY_ERROR);
+
       pos += hash_size;
 
       if (_gnutls_sset_datum
-	  (&session->cipher_specs.server_write_mac_secret,
+	  (&server_write->mac_secret,
 	   &key_block[pos], hash_size) < 0)
-	{
-	  gnutls_assert ();
-	  return GNUTLS_E_MEMORY_ERROR;
-	}
+	return gnutls_assert_val (GNUTLS_E_MEMORY_ERROR);
+
       pos += hash_size;
     }
 
@@ -197,10 +179,7 @@ _gnutls_set_keys (gnutls_session_t session, int hash_size, int IV_size,
 	    }
 
 	  if (ret < 0)
-	    {
-	      gnutls_assert ();
-	      return ret;
-	    }
+	    return gnutls_assert_val (ret);
 
 	  client_write_key_size = EXPORT_FINAL_KEY_SIZE;
 	  pos += key_size;
@@ -223,22 +202,17 @@ _gnutls_set_keys (gnutls_session_t session, int hash_size, int IV_size,
 	    }
 
 	  if (ret < 0)
-	    {
-	      gnutls_assert ();
-	      return ret;
-	    }
+	    return gnutls_assert_val (ret);
 
 	  server_write_key_size = EXPORT_FINAL_KEY_SIZE;
 	  pos += key_size;
 	}
 
       if (_gnutls_sset_datum
-	  (&session->cipher_specs.client_write_key,
+	  (&client_write->key,
 	   client_write_key, client_write_key_size) < 0)
-	{
-	  gnutls_assert ();
-	  return GNUTLS_E_MEMORY_ERROR;
-	}
+	return gnutls_assert_val (GNUTLS_E_MEMORY_ERROR);
+
       _gnutls_hard_log ("INT: CLIENT WRITE KEY [%d]: %s\n",
 			client_write_key_size,
 			_gnutls_bin2hex (client_write_key,
@@ -246,12 +220,9 @@ _gnutls_set_keys (gnutls_session_t session, int hash_size, int IV_size,
 					 sizeof (buf), NULL));
 
       if (_gnutls_sset_datum
-	  (&session->cipher_specs.server_write_key,
+	  (&server_write->key,
 	   server_write_key, server_write_key_size) < 0)
-	{
-	  gnutls_assert ();
-	  return GNUTLS_E_MEMORY_ERROR;
-	}
+	return gnutls_assert_val (GNUTLS_E_MEMORY_ERROR);
 
       _gnutls_hard_log ("INT: SERVER WRITE KEY [%d]: %s\n",
 			server_write_key_size,
@@ -267,21 +238,17 @@ _gnutls_set_keys (gnutls_session_t session, int hash_size, int IV_size,
   if (IV_size > 0 && export_flag == 0)
     {
       if (_gnutls_sset_datum
-	  (&session->cipher_specs.client_write_IV, &key_block[pos],
+	  (&client_write->IV, &key_block[pos],
 	   IV_size) < 0)
-	{
-	  gnutls_assert ();
-	  return GNUTLS_E_MEMORY_ERROR;
-	}
+	return gnutls_assert_val (GNUTLS_E_MEMORY_ERROR);
+
       pos += IV_size;
 
       if (_gnutls_sset_datum
-	  (&session->cipher_specs.server_write_IV, &key_block[pos],
+	  (&server_write->IV, &key_block[pos],
 	   IV_size) < 0)
-	{
-	  gnutls_assert ();
-	  return GNUTLS_E_MEMORY_ERROR;
-	}
+	return gnutls_assert_val (GNUTLS_E_MEMORY_ERROR);
+
       pos += IV_size;
 
     }
@@ -296,10 +263,8 @@ _gnutls_set_keys (gnutls_session_t session, int hash_size, int IV_size,
 				       IV_size, iv_block);
 
 	  if (ret < 0)
-	    {
-	      gnutls_assert ();
-	      return ret;
-	    }
+	    return gnutls_assert_val (ret);
+
 
 	  ret = _gnutls_ssl3_hash_md5 ("", 0, rnd,
 				       GNUTLS_RANDOM_SIZE * 2,
@@ -314,74 +279,177 @@ _gnutls_set_keys (gnutls_session_t session, int hash_size, int IV_size,
 	}
 
       if (ret < 0)
-	{
-	  gnutls_assert ();
-	  return ret;
-	}
+	return gnutls_assert_val (ret);
 
       if (_gnutls_sset_datum
-	  (&session->cipher_specs.client_write_IV, iv_block, IV_size) < 0)
-	{
-	  gnutls_assert ();
-	  return GNUTLS_E_MEMORY_ERROR;
-	}
+	  (&client_write->IV, iv_block, IV_size) < 0)
+	return gnutls_assert_val (GNUTLS_E_MEMORY_ERROR);
 
       if (_gnutls_sset_datum
-	  (&session->cipher_specs.server_write_IV,
+	  (&server_write->IV,
 	   &iv_block[IV_size], IV_size) < 0)
-	{
-	  gnutls_assert ();
-	  return GNUTLS_E_MEMORY_ERROR;
-	}
+	return gnutls_assert_val (GNUTLS_E_MEMORY_ERROR);
     }
 
-  session->cipher_specs.generated_keys = 1;
-
   return 0;
 }
 
 static int
-_gnutls_set_read_keys (gnutls_session_t session)
+_gnutls_init_record_state (record_parameters_st *params, int read,
+			   record_state_st *state)
 {
-  int hash_size;
-  int IV_size;
-  int key_size, export_flag;
-  gnutls_cipher_algorithm_t algo;
+  int ret;
+
+  ret = _gnutls_cipher_init (&state->cipher_state,
+			     params->cipher_algorithm,
+			     &state->key, &state->IV);
+  if (ret < 0
+      && params->cipher_algorithm != GNUTLS_CIPHER_NULL)
+    return gnutls_assert_val (ret);
+
+  state->compression_state =
+    _gnutls_comp_init (params->compression_algorithm, read);
+
+  if (state->compression_state == GNUTLS_COMP_FAILED)
+    return gnutls_assert_val (GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM);
+
+  return 0;
+}
+
+int
+_gnutls_epoch_set_cipher_suite (gnutls_session_t session,
+				int epoch_rel,
+				cipher_suite_st *suite)
+{
+  gnutls_cipher_algorithm_t cipher_algo;
   gnutls_mac_algorithm_t mac_algo;
+  record_parameters_st *params;
+  int ret;
 
-  mac_algo = session->security_parameters.read_mac_algorithm;
-  algo = session->security_parameters.read_bulk_cipher_algorithm;
+  ret = _gnutls_epoch_get (session, epoch_rel, &params);
+  if (ret < 0)
+    return gnutls_assert_val (ret);
 
-  hash_size = _gnutls_hash_get_algo_len (mac_algo);
-  IV_size = _gnutls_cipher_get_iv_size (algo);
-  key_size = gnutls_cipher_get_key_size (algo);
-  export_flag = _gnutls_cipher_get_export_flag (algo);
+  if (params->initialized
+      || params->cipher_algorithm != GNUTLS_CIPHER_UNKNOWN
+      || params->mac_algorithm != GNUTLS_MAC_UNKNOWN)
+    return gnutls_assert_val (GNUTLS_E_INTERNAL_ERROR);
+
+  cipher_algo =_gnutls_cipher_suite_get_cipher_algo (suite);
+  mac_algo = _gnutls_cipher_suite_get_mac_algo (suite);
 
-  return _gnutls_set_keys (session, hash_size, IV_size, key_size,
-			   export_flag);
+  if (_gnutls_cipher_is_ok (cipher_algo) != 0
+      || _gnutls_mac_is_ok (mac_algo) != 0)
+    return gnutls_assert_val (GNUTLS_E_UNWANTED_ALGORITHM);
+
+  params->cipher_algorithm = cipher_algo;
+  params->mac_algorithm = mac_algo;
+
+  return 0;
 }
 
-static int
-_gnutls_set_write_keys (gnutls_session_t session)
+int
+_gnutls_epoch_set_compression (gnutls_session_t session,
+			       int epoch_rel,
+			       gnutls_compression_method_t comp_algo)
+{
+  record_parameters_st *params;
+  int ret;
+
+  ret = _gnutls_epoch_get (session, epoch_rel, &params);
+  if (ret < 0)
+    return gnutls_assert_val (ret);
+
+  if (params->initialized
+      || params->compression_algorithm != GNUTLS_COMP_UNKNOWN)
+    return gnutls_assert_val (GNUTLS_E_INTERNAL_ERROR);
+
+  if (_gnutls_compression_is_ok (comp_algo) != 0)
+    return gnutls_assert_val (GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM);
+
+  params->compression_algorithm = comp_algo;
+
+  return 0;
+}
+
+void
+_gnutls_epoch_set_null_algos (gnutls_session_t session,
+			      record_parameters_st *params)
+{
+  /* This is only called on startup. We are extra paranoid about this
+     because it may cause unencrypted application data to go out on
+     the wire. */
+  if (params->initialized || params->epoch != 0)
+    {
+      gnutls_assert ();
+      return;
+    }
+
+  params->cipher_algorithm =  GNUTLS_CIPHER_NULL;
+  params->mac_algorithm = GNUTLS_MAC_NULL;
+  params->compression_algorithm =  GNUTLS_COMP_NULL;
+  params->initialized =  1;
+}
+
+int
+_gnutls_epoch_set_keys (gnutls_session_t session, uint16_t epoch)
 {
   int hash_size;
   int IV_size;
   int key_size, export_flag;
-  gnutls_cipher_algorithm_t algo;
+  gnutls_cipher_algorithm_t cipher_algo;
   gnutls_mac_algorithm_t mac_algo;
+  gnutls_compression_method_t comp_algo;
+  record_parameters_st *params;
+  int ret;
+
+  ret = _gnutls_epoch_get (session, epoch, &params);
+  if (ret < 0)
+    return gnutls_assert_val (ret);
 
-  mac_algo = session->security_parameters.write_mac_algorithm;
-  algo = session->security_parameters.write_bulk_cipher_algorithm;
+  if (params->initialized)
+    return 0;
 
+  _gnutls_record_log
+    ("REC[%p]: Initializing epoch #%u\n", session, params->epoch);
+
+  cipher_algo = params->cipher_algorithm;
+  mac_algo = params->mac_algorithm;
+  comp_algo = params->compression_algorithm;
+
+  if (_gnutls_cipher_is_ok (cipher_algo) != 0
+      || _gnutls_mac_is_ok (mac_algo) != 0)
+    return gnutls_assert_val (GNUTLS_E_INTERNAL_ERROR);
+
+  if (_gnutls_compression_is_ok (comp_algo) != 0)
+    return gnutls_assert_val (GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM);
+
+  IV_size = _gnutls_cipher_get_iv_size (cipher_algo);
+  key_size = gnutls_cipher_get_key_size (cipher_algo);
+  export_flag = _gnutls_cipher_get_export_flag (cipher_algo);
   hash_size = _gnutls_hash_get_algo_len (mac_algo);
-  IV_size = _gnutls_cipher_get_iv_size (algo);
-  key_size = gnutls_cipher_get_key_size (algo);
-  export_flag = _gnutls_cipher_get_export_flag (algo);
 
-  return _gnutls_set_keys (session, hash_size, IV_size, key_size,
-			   export_flag);
+  ret = _gnutls_set_keys
+    (session, params, hash_size, IV_size, key_size, export_flag);
+  if (ret < 0)
+    return gnutls_assert_val (ret);
+
+  ret = _gnutls_init_record_state (params, 1, &params->read);
+  if (ret < 0)
+      return gnutls_assert_val (ret);
+
+  ret = _gnutls_init_record_state (params, 0, &params->write);
+  if (ret < 0)
+    return gnutls_assert_val (ret);
+
+  _gnutls_record_log
+    ("REC[%p]: Epoch #%u ready\n", session, params->epoch);
+
+  params->initialized = 1;
+  return 0;
 }
 
+
 #define CPY_COMMON dst->entity = src->entity; \
 	dst->kx_algorithm = src->kx_algorithm; \
 	memcpy( &dst->current_cipher_suite, &src->current_cipher_suite, sizeof(cipher_suite_st)); \
@@ -397,25 +465,12 @@ _gnutls_set_write_keys (gnutls_session_t session)
 	dst->version = src->version
 
 static void
-_gnutls_cpy_read_security_parameters (security_parameters_st *
-				      dst, security_parameters_st * src)
+_gnutls_set_resumed_parameters (gnutls_session_t session)
 {
-  CPY_COMMON;
-
-  dst->read_bulk_cipher_algorithm = src->read_bulk_cipher_algorithm;
-  dst->read_mac_algorithm = src->read_mac_algorithm;
-  dst->read_compression_algorithm = src->read_compression_algorithm;
-}
+  security_parameters_st *src = &session->internals.resumed_security_parameters;
+  security_parameters_st *dst = &session->security_parameters;
 
-static void
-_gnutls_cpy_write_security_parameters (security_parameters_st *
-				       dst, security_parameters_st * src)
-{
   CPY_COMMON;
-
-  dst->write_bulk_cipher_algorithm = src->write_bulk_cipher_algorithm;
-  dst->write_mac_algorithm = src->write_mac_algorithm;
-  dst->write_compression_algorithm = src->write_compression_algorithm;
 }
 
 /* Sets the current connection session to conform with the
@@ -432,69 +487,76 @@ _gnutls_connection_state_init (gnutls_session_t session)
 /* Setup the master secret 
  */
   if ((ret = _gnutls_generate_master (session, 0)) < 0)
-    {
-      gnutls_assert ();
-      return ret;
-    }
-
+    return gnutls_assert_val (ret);
 
   return 0;
 }
 
 
+
+static int
+_gnutls_check_algos (gnutls_session_t session,
+		     cipher_suite_st *suite, gnutls_compression_method_t comp_algo)
+{
+  gnutls_cipher_algorithm_t cipher_algo;
+  gnutls_mac_algorithm_t mac_algo;
+
+  cipher_algo = _gnutls_cipher_suite_get_cipher_algo (suite);
+  mac_algo = _gnutls_cipher_suite_get_mac_algo (suite);
+
+  if (_gnutls_cipher_is_ok (cipher_algo) != 0)
+    return gnutls_assert_val (GNUTLS_E_INTERNAL_ERROR);
+
+  if (_gnutls_cipher_priority (session, cipher_algo) < 0)
+    return gnutls_assert_val (GNUTLS_E_UNWANTED_ALGORITHM);
+
+
+  if (_gnutls_mac_is_ok (mac_algo) != 0)
+    return gnutls_assert_val (GNUTLS_E_INTERNAL_ERROR);
+
+  if (_gnutls_mac_priority (session, mac_algo) < 0)
+    return gnutls_assert_val (GNUTLS_E_UNWANTED_ALGORITHM);
+
+
+  if (_gnutls_compression_is_ok (comp_algo) != 0)
+    return gnutls_assert_val (GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM);
+
+  return 0;
+}
+
 /* Initializes the read connection session
  * (read encrypted data)
  */
 int
 _gnutls_read_connection_state_init (gnutls_session_t session)
 {
-  int mac_size;
-  int rc;
+  const uint16_t epoch_next = session->security_parameters.epoch_next;
+  int ret;
 
-  _gnutls_uint64zero (session->connection_state.read_sequence_number);
-
-/* Update internals from CipherSuite selected.
- * If we are resuming just copy the connection session
- */
+  /* Update internals from CipherSuite selected.
+   * If we are resuming just copy the connection session
+   */
   if (session->internals.resumed == RESUME_FALSE)
     {
-      rc = _gnutls_set_read_cipher (session,
-				    _gnutls_cipher_suite_get_cipher_algo
-				    (&session->
-				     security_parameters.current_cipher_suite));
-      if (rc < 0)
-	return rc;
-      rc = _gnutls_set_read_mac (session,
-				 _gnutls_cipher_suite_get_mac_algo
-				 (&session->
-				  security_parameters.current_cipher_suite));
-      if (rc < 0)
-	return rc;
+      ret = _gnutls_check_algos (session,
+				 &session->security_parameters.current_cipher_suite,
+				 session->internals.compression_method);
+      if (ret < 0)
+	return ret;
 
-      rc = _gnutls_set_kx (session,
-			   _gnutls_cipher_suite_get_kx_algo
-			   (&session->
-			    security_parameters.current_cipher_suite));
-      if (rc < 0)
-	return rc;
-
-      rc = _gnutls_set_read_compression (session,
-					 session->
-					 internals.compression_method);
-      if (rc < 0)
-	return rc;
-    }
-  else
-    { /* RESUME_TRUE */
-      _gnutls_cpy_read_security_parameters (&session->security_parameters,
-					    &session->
-					    internals.resumed_security_parameters);
+      ret = _gnutls_set_kx (session,
+			    _gnutls_cipher_suite_get_kx_algo
+			    (&session->
+			     security_parameters.current_cipher_suite));
+      if (ret < 0)
+	return ret;
     }
+  else if (session->security_parameters.entity == GNUTLS_CLIENT)
+    _gnutls_set_resumed_parameters (session);
 
-
-  rc = _gnutls_set_read_keys (session);
-  if (rc < 0)
-    return rc;
+  ret = _gnutls_epoch_set_keys (session, epoch_next);
+  if (ret < 0)
+    return ret;
 
   _gnutls_handshake_log ("HSK[%p]: Cipher Suite: %s\n",
 			 session,
@@ -502,124 +564,8 @@ _gnutls_read_connection_state_init (gnutls_session_t session)
 			 (&session->
 			  security_parameters.current_cipher_suite));
 
-  if (_gnutls_compression_is_ok
-      (session->security_parameters.read_compression_algorithm) != 0)
-    {
-      gnutls_assert ();
-      return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM;
-    }
-
-  if (_gnutls_mac_is_ok
-      (session->security_parameters.read_mac_algorithm) != 0)
-    {
-      gnutls_assert ();
-      return GNUTLS_E_INTERNAL_ERROR;
-    }
-
-  /* Free all the previous keys/ sessions etc.
-   */
-  if (session->connection_state.read_mac_secret.data != NULL)
-    _gnutls_free_datum (&session->connection_state.read_mac_secret);
-
-  _gnutls_cipher_deinit (&session->connection_state.read_cipher_state);
-
-  if (session->connection_state.read_compression_state != NULL)
-    _gnutls_comp_deinit (session->connection_state.read_compression_state, 1);
-
-
-  mac_size =
-    _gnutls_hash_get_algo_len (session->
-			       security_parameters.read_mac_algorithm);
-
-  _gnutls_handshake_log
-    ("HSK[%p]: Initializing internal [read] cipher sessions\n", session);
-
-  switch (session->security_parameters.entity)
-    {
-    case GNUTLS_SERVER:
-      /* initialize cipher session
-       */
-      rc = _gnutls_cipher_init (&session->connection_state.read_cipher_state,
-				session->security_parameters.
-				read_bulk_cipher_algorithm,
-				&session->cipher_specs.client_write_key,
-				&session->cipher_specs.client_write_IV);
-      if (rc < 0
-	  && session->security_parameters.read_bulk_cipher_algorithm !=
-	  GNUTLS_CIPHER_NULL)
-	{
-	  gnutls_assert ();
-	  return rc;
-	}
-
-      /* copy mac secrets from cipherspecs, to connection
-       * session.
-       */
-      if (mac_size > 0)
-	{
-	  if (_gnutls_sset_datum (&session->connection_state.read_mac_secret,
-				  session->
-				  cipher_specs.client_write_mac_secret.data,
-				  session->
-				  cipher_specs.client_write_mac_secret.size) <
-	      0)
-	    {
-	      gnutls_assert ();
-	      return GNUTLS_E_MEMORY_ERROR;
-	    }
-
-	}
-
-      break;
-
-    case GNUTLS_CLIENT:
-      rc = _gnutls_cipher_init (&session->connection_state.read_cipher_state,
-				session->security_parameters.
-				read_bulk_cipher_algorithm,
-				&session->cipher_specs.server_write_key,
-				&session->cipher_specs.server_write_IV);
-
-      if (rc < 0
-	  && session->security_parameters.read_bulk_cipher_algorithm !=
-	  GNUTLS_CIPHER_NULL)
-	{
-	  gnutls_assert ();
-	  return GNUTLS_E_INTERNAL_ERROR;
-	}
-
-
-      /* copy mac secret to connection session
-       */
-      if (mac_size > 0)
-	{
-	  if (_gnutls_sset_datum (&session->connection_state.read_mac_secret,
-				  session->
-				  cipher_specs.server_write_mac_secret.data,
-				  session->
-				  cipher_specs.server_write_mac_secret.size) <
-	      0)
-	    {
-	      gnutls_assert ();
-	      return GNUTLS_E_MEMORY_ERROR;
-	    }
-	}
-
-      break;
-
-    default:			/* this check is useless */
-      gnutls_assert ();
-      return GNUTLS_E_INTERNAL_ERROR;
-    }
-
-  session->connection_state.read_compression_state =
-    _gnutls_comp_init (session->
-		       security_parameters.read_compression_algorithm, 1);
-
-  if (session->connection_state.read_compression_state == GNUTLS_COMP_FAILED)
-    {
-      gnutls_assert ();
-      return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM;
-    }
+  session->security_parameters.epoch_read = epoch_next;
+  _gnutls_epoch_gc (session);
 
   return 0;
 }
@@ -632,280 +578,49 @@ _gnutls_read_connection_state_init (gnutls_session_t session)
 int
 _gnutls_write_connection_state_init (gnutls_session_t session)
 {
-  int mac_size;
-  int rc;
-
-  _gnutls_uint64zero (session->connection_state.write_sequence_number);
+  const uint16_t epoch_next = session->security_parameters.epoch_next;
+  int ret;
 
 /* Update internals from CipherSuite selected.
  * If we are resuming just copy the connection session
  */
   if (session->internals.resumed == RESUME_FALSE)
     {
-      rc = _gnutls_set_write_cipher (session,
-				     _gnutls_cipher_suite_get_cipher_algo
-				     (&session->
-				      security_parameters.current_cipher_suite));
-      if (rc < 0)
-	return rc;
-      rc = _gnutls_set_write_mac (session,
-				  _gnutls_cipher_suite_get_mac_algo
-				  (&session->
-				   security_parameters.current_cipher_suite));
-      if (rc < 0)
-	return rc;
+      ret = _gnutls_check_algos (session,
+				 &session->security_parameters.current_cipher_suite,
+				 session->internals.compression_method);
+      if (ret < 0)
+	return ret;
 
-      rc = _gnutls_set_kx (session,
-			   _gnutls_cipher_suite_get_kx_algo
-			   (&session->
-			    security_parameters.current_cipher_suite));
-      if (rc < 0)
-	return rc;
-
-      rc = _gnutls_set_write_compression (session,
-					  session->
-					  internals.compression_method);
-      if (rc < 0)
-	return rc;
-    }
-  else
-    {				/* RESUME_TRUE */
-      _gnutls_cpy_write_security_parameters (&session->security_parameters,
-					     &session->
-					     internals.resumed_security_parameters);
-      _gnutls_ext_restore_resumed_session(session);
+      ret = _gnutls_set_kx (session,
+			    _gnutls_cipher_suite_get_kx_algo
+			    (&session->
+			     security_parameters.current_cipher_suite));
+      if (ret < 0)
+	return ret;
     }
+  else if (session->security_parameters.entity == GNUTLS_SERVER)
+    _gnutls_set_resumed_parameters (session);
 
-  rc = _gnutls_set_write_keys (session);
-  if (rc < 0)
-    return rc;
+  ret = _gnutls_epoch_set_keys (session, epoch_next);
+  if (ret < 0)
+    return gnutls_assert_val (ret);
 
   _gnutls_handshake_log ("HSK[%p]: Cipher Suite: %s\n", session,
 			 _gnutls_cipher_suite_get_name
 			 (&session->
 			  security_parameters.current_cipher_suite));
 
-  if (_gnutls_compression_is_ok
-      (session->security_parameters.write_compression_algorithm) != 0)
-    {
-      gnutls_assert ();
-      return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM;
-    }
-
-  if (_gnutls_mac_is_ok
-      (session->security_parameters.write_mac_algorithm) != 0)
-    {
-      gnutls_assert ();
-      return GNUTLS_E_INTERNAL_ERROR;
-    }
-
-
-
-  /* Free all the previous keys/ sessions etc.
-   */
-  if (session->connection_state.write_mac_secret.data != NULL)
-    _gnutls_free_datum (&session->connection_state.write_mac_secret);
-
-  _gnutls_cipher_deinit (&session->connection_state.write_cipher_state);
-
-  if (session->connection_state.write_compression_state != NULL)
-    _gnutls_comp_deinit (session->connection_state.write_compression_state,
-			 0);
-
-  mac_size =
-    _gnutls_hash_get_algo_len (session->
-			       security_parameters.write_mac_algorithm);
-
   _gnutls_handshake_log
     ("HSK[%p]: Initializing internal [write] cipher sessions\n", session);
 
-  switch (session->security_parameters.entity)
-    {
-    case GNUTLS_SERVER:
-      /* initialize cipher session
-       */
-      rc = _gnutls_cipher_init (&session->connection_state.write_cipher_state,
-				session->
-				security_parameters.write_bulk_cipher_algorithm,
-				&session->cipher_specs.server_write_key,
-				&session->cipher_specs.server_write_IV);
-
-      if (rc < 0
-	  && session->security_parameters.write_bulk_cipher_algorithm !=
-	  GNUTLS_CIPHER_NULL)
-	{
-	  gnutls_assert ();
-	  return GNUTLS_E_INTERNAL_ERROR;
-	}
-
-
-      /* copy mac secrets from cipherspecs, to connection
-       * session.
-       */
-      if (mac_size > 0)
-	{
-	  if (_gnutls_sset_datum (&session->connection_state.write_mac_secret,
-				  session->
-				  cipher_specs.server_write_mac_secret.data,
-				  session->
-				  cipher_specs.server_write_mac_secret.size) <
-	      0)
-	    {
-	      gnutls_assert ();
-	      return GNUTLS_E_MEMORY_ERROR;
-	    }
-
-	}
-
-
-      break;
-
-    case GNUTLS_CLIENT:
-      rc = _gnutls_cipher_init (&session->connection_state.write_cipher_state,
-				session->
-				security_parameters.write_bulk_cipher_algorithm,
-				&session->cipher_specs.client_write_key,
-				&session->cipher_specs.client_write_IV);
-
-      if (rc < 0
-	  && session->security_parameters.write_bulk_cipher_algorithm !=
-	  GNUTLS_CIPHER_NULL)
-	{
-	  gnutls_assert ();
-	  return GNUTLS_E_INTERNAL_ERROR;
-	}
-
-      /* copy mac secret to connection session
-       */
-      if (mac_size > 0)
-	{
-	  if (_gnutls_sset_datum (&session->connection_state.write_mac_secret,
-				  session->
-				  cipher_specs.client_write_mac_secret.data,
-				  session->
-				  cipher_specs.client_write_mac_secret.size) <
-	      0)
-	    {
-	      gnutls_assert ();
-	      return GNUTLS_E_MEMORY_ERROR;
-	    }
-	}
-
-      break;
-
-    default:
-      gnutls_assert ();
-      return GNUTLS_E_INTERNAL_ERROR;
-    }
-
-
-  session->connection_state.write_compression_state =
-    _gnutls_comp_init (session->
-		       security_parameters.write_compression_algorithm, 0);
-
-  if (session->connection_state.write_compression_state == GNUTLS_COMP_FAILED)
-    {
-      gnutls_assert ();
-      return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM;
-    }
-
-  return 0;
-}
-
-/* Sets the specified cipher into the pending session 
- */
-int
-_gnutls_set_read_cipher (gnutls_session_t session,
-			 gnutls_cipher_algorithm_t algo)
-{
-
-  if (_gnutls_cipher_is_ok (algo) == 0)
-    {
-      if (_gnutls_cipher_priority (session, algo) < 0)
-	{
-	  gnutls_assert ();
-	  return GNUTLS_E_UNWANTED_ALGORITHM;
-	}
-
-      session->security_parameters.read_bulk_cipher_algorithm = algo;
-
-    }
-  else
-    {
-      gnutls_assert ();
-      return GNUTLS_E_INTERNAL_ERROR;
-    }
+  session->security_parameters.epoch_write = epoch_next;
+  _gnutls_epoch_gc (session);
 
   return 0;
-
-}
-
-int
-_gnutls_set_write_cipher (gnutls_session_t session,
-			  gnutls_cipher_algorithm_t algo)
-{
-
-  if (_gnutls_cipher_is_ok (algo) == 0)
-    {
-      if (_gnutls_cipher_priority (session, algo) < 0)
-	{
-	  gnutls_assert ();
-	  return GNUTLS_E_UNWANTED_ALGORITHM;
-	}
-
-      session->security_parameters.write_bulk_cipher_algorithm = algo;
-
-    }
-  else
-    {
-      gnutls_assert ();
-      return GNUTLS_E_INTERNAL_ERROR;
-    }
-
-  return 0;
-
-}
-
-
-/* Sets the specified algorithm into pending compression session 
- */
-int
-_gnutls_set_read_compression (gnutls_session_t session,
-			      gnutls_compression_method_t algo)
-{
-
-  if (_gnutls_compression_is_ok (algo) == 0)
-    {
-      session->security_parameters.read_compression_algorithm = algo;
-    }
-  else
-    {
-      gnutls_assert ();
-      return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM;
-    }
-  return 0;
-
-}
-
-int
-_gnutls_set_write_compression (gnutls_session_t session,
-			       gnutls_compression_method_t algo)
-{
-
-  if (_gnutls_compression_is_ok (algo) == 0)
-    {
-      session->security_parameters.write_compression_algorithm = algo;
-    }
-  else
-    {
-      gnutls_assert ();
-      return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM;
-    }
-  return 0;
-
 }
 
-/* Sets the specified kx algorithm into pending session 
+/* Sets the specified kx algorithm into pending session
  */
 int
 _gnutls_set_kx (gnutls_session_t session, gnutls_kx_algorithm_t algo)
@@ -916,66 +631,177 @@ _gnutls_set_kx (gnutls_session_t session, gnutls_kx_algorithm_t algo)
       session->security_parameters.kx_algorithm = algo;
     }
   else
-    {
-      gnutls_assert ();
-      return GNUTLS_E_INTERNAL_ERROR;
-    }
+    return gnutls_assert_val (GNUTLS_E_INTERNAL_ERROR);
+
   if (_gnutls_kx_priority (session, algo) < 0)
-    {
-      gnutls_assert ();
-      /* we shouldn't get here */
-      return GNUTLS_E_UNWANTED_ALGORITHM;
-    }
+    return gnutls_assert_val (GNUTLS_E_UNWANTED_ALGORITHM);
 
   return 0;
-
 }
 
-/* Sets the specified mac algorithm into pending session */
-int
-_gnutls_set_read_mac (gnutls_session_t session, gnutls_mac_algorithm_t algo)
+static inline int
+epoch_resolve(gnutls_session_t session,
+	      unsigned int epoch_rel, uint16_t *epoch_out)
 {
-
-  if (_gnutls_mac_is_ok (algo) == 0)
-    {
-      session->security_parameters.read_mac_algorithm = algo;
-    }
-  else
+  switch (epoch_rel)
     {
-      gnutls_assert ();
-      return GNUTLS_E_INTERNAL_ERROR;
+    case EPOCH_READ_CURRENT:
+      *epoch_out = session->security_parameters.epoch_read;
+      return 0;
+
+    case EPOCH_WRITE_CURRENT:
+      *epoch_out = session->security_parameters.epoch_write;
+      return 0;
+
+    case EPOCH_NEXT:
+      *epoch_out = session->security_parameters.epoch_next;
+      return 0;
+
+    default:
+      if (epoch_rel > 0xffffu)
+	return gnutls_assert_val (GNUTLS_E_INVALID_REQUEST);
+
+      *epoch_out = epoch_rel;
+      return 0;
     }
-  if (_gnutls_mac_priority (session, algo) < 0)
+}
+
+static inline record_parameters_st**
+epoch_get_slot(gnutls_session_t session, uint16_t epoch)
+{
+  uint16_t epoch_index =
+    epoch - session->security_parameters.epoch_min;
+
+  if (epoch_index >= MAX_EPOCH_INDEX)
     {
       gnutls_assert ();
-      return GNUTLS_E_UNWANTED_ALGORITHM;
+      return NULL;
     }
 
+  /* The slot may still be empty (NULL) */
+  return &session->record_parameters[epoch_index];
+}
 
-  return 0;
+int
+_gnutls_epoch_get (gnutls_session_t session, unsigned int epoch_rel,
+		   record_parameters_st **params_out)
+{
+  uint16_t epoch;
+  record_parameters_st **params;
+  int ret;
+
+  ret = epoch_resolve (session, epoch_rel, &epoch);
+  if (ret < 0)
+    return gnutls_assert_val (ret);
+
+  params = epoch_get_slot (session, epoch);
+  if (params == NULL || *params == NULL)
+    return gnutls_assert_val (GNUTLS_E_INVALID_REQUEST);
 
+  *params_out = *params;
+
+  return 0;
 }
 
 int
-_gnutls_set_write_mac (gnutls_session_t session, gnutls_mac_algorithm_t algo)
+_gnutls_epoch_alloc(gnutls_session_t session, uint16_t epoch,
+		    record_parameters_st **out)
 {
+  record_parameters_st **slot;
 
-  if (_gnutls_mac_is_ok (algo) == 0)
-    {
-      session->security_parameters.write_mac_algorithm = algo;
-    }
-  else
-    {
-      gnutls_assert ();
-      return GNUTLS_E_INTERNAL_ERROR;
-    }
-  if (_gnutls_mac_priority (session, algo) < 0)
-    {
-      gnutls_assert ();
-      return GNUTLS_E_UNWANTED_ALGORITHM;
-    }
+  _gnutls_record_log
+    ("REC[%p]: Allocating epoch #%u\n", session, epoch);
+
+  slot = epoch_get_slot(session, epoch);
+
+  /* If slot out of range or not empty. */
+  if (slot == NULL)
+    return gnutls_assert_val (GNUTLS_E_INVALID_REQUEST);
+
+  if(*slot != NULL)
+    return gnutls_assert_val (GNUTLS_E_INVALID_REQUEST);
+
+  *slot = gnutls_calloc (1, sizeof (record_parameters_st));
+  if (*slot == NULL)
+    return gnutls_assert_val (GNUTLS_E_MEMORY_ERROR);
+
+  (*slot)->epoch = epoch;
+  (*slot)->cipher_algorithm =  GNUTLS_CIPHER_UNKNOWN;
+  (*slot)->mac_algorithm = GNUTLS_MAC_UNKNOWN;
+  (*slot)->compression_algorithm =  GNUTLS_COMP_UNKNOWN;
 
+  if (out != NULL)
+    *out = *slot;
 
   return 0;
+}
+
+static inline int
+epoch_alive (gnutls_session_t session, record_parameters_st *params)
+{
+  const security_parameters_st *sp = &session->security_parameters;
+
+  /* DTLS will, in addition, need to check the epoch timeout value. */
+  return (params->epoch == sp->epoch_read
+	  || params->epoch == sp->epoch_write
+	  || params->epoch == sp->epoch_next);
+}
+
+void
+_gnutls_epoch_gc (gnutls_session_t session)
+{
+  int i, j;
+  unsigned int min_index = 0;
+
+  _gnutls_record_log
+    ("REC[%p]: Start of epoch cleanup\n", session);
+
+  /* Free all dead cipher state */
+  for(i=0; i < MAX_EPOCH_INDEX; i++)
+    if (session->record_parameters[i] != NULL
+	&& !epoch_alive (session, session->record_parameters[i]))
+      {
+	_gnutls_epoch_free (session, session->record_parameters[i]);
+	session->record_parameters[i] = NULL;
+      }
+
+  /* Look for contiguous NULLs at the start of the array */
+  for(i=0; i < MAX_EPOCH_INDEX && session->record_parameters[i] == NULL; i++);
+  min_index = i;
+
+  /* Pick up the slack in the epoch window. */
+  for(i=0, j=min_index; j < MAX_EPOCH_INDEX; i++, j++)
+    session->record_parameters[i] = session->record_parameters[j];
+
+  /* Set the new epoch_min */
+  if (session->record_parameters[0] != NULL)
+    session->security_parameters.epoch_min = session->record_parameters[0]->epoch;
+
+  _gnutls_record_log
+    ("REC[%p]: End of epoch cleanup\n", session);
+}
+
+static inline void
+free_record_state (record_state_st *state, int read)
+{
+  _gnutls_free_datum (&state->mac_secret);
+  _gnutls_free_datum (&state->IV);
+  _gnutls_free_datum (&state->key);
+
+  _gnutls_cipher_deinit (&state->cipher_state);
+
+  if (state->compression_state != NULL)
+    _gnutls_comp_deinit (state->compression_state, read);
+}
+
+void
+_gnutls_epoch_free (gnutls_session_t session, record_parameters_st *params)
+{
+  _gnutls_record_log
+    ("REC[%p]: Epoch #%u freed\n", session, params->epoch);
+
+  free_record_state (&params->read, 1);
+  free_record_state (&params->write, 0);
 
+  gnutls_free (params);
 }
diff --git a/lib/gnutls_constate.h b/lib/gnutls_constate.h
index ba5b056..513ab1c 100644
--- a/lib/gnutls_constate.h
+++ b/lib/gnutls_constate.h
@@ -23,19 +23,27 @@
  *
  */
 
+#ifndef GNUTLS_CONSTATE_H
+#define GNUTLS_CONSTATE_H
+
+int _gnutls_epoch_set_cipher_suite (gnutls_session_t session, int epoch_rel,
+				    cipher_suite_st *suite);
+int _gnutls_epoch_set_compression (gnutls_session_t session, int epoch_rel,
+				   gnutls_compression_method_t comp_algo);
+void _gnutls_epoch_set_null_algos (gnutls_session_t session,
+				   record_parameters_st *params);
+int _gnutls_epoch_set_keys (gnutls_session_t session, uint16_t epoch);
 int _gnutls_connection_state_init (gnutls_session_t session);
 int _gnutls_read_connection_state_init (gnutls_session_t session);
 int _gnutls_write_connection_state_init (gnutls_session_t session);
-int _gnutls_set_write_cipher (gnutls_session_t session,
-			      gnutls_cipher_algorithm_t algo);
-int _gnutls_set_write_mac (gnutls_session_t session,
-			   gnutls_mac_algorithm_t algo);
-int _gnutls_set_read_cipher (gnutls_session_t session,
-			     gnutls_cipher_algorithm_t algo);
-int _gnutls_set_read_mac (gnutls_session_t session,
-			  gnutls_mac_algorithm_t algo);
-int _gnutls_set_read_compression (gnutls_session_t session,
-				  gnutls_compression_method_t algo);
-int _gnutls_set_write_compression (gnutls_session_t session,
-				   gnutls_compression_method_t algo);
+
 int _gnutls_set_kx (gnutls_session_t session, gnutls_kx_algorithm_t algo);
+
+int _gnutls_epoch_get (gnutls_session_t session, unsigned int epoch_rel,
+		       record_parameters_st **params_out);
+int _gnutls_epoch_alloc(gnutls_session_t session, uint16_t epoch,
+			record_parameters_st **out);
+void _gnutls_epoch_gc (gnutls_session_t session);
+void _gnutls_epoch_free (gnutls_session_t session, record_parameters_st *state);
+
+#endif
diff --git a/lib/gnutls_handshake.c b/lib/gnutls_handshake.c
index 5418462..b7f1c54 100644
--- a/lib/gnutls_handshake.c
+++ b/lib/gnutls_handshake.c
@@ -124,8 +124,11 @@ resume_copy_required_values (gnutls_session_t session)
 	  session->internals.resumed_security_parameters.current_cipher_suite.
 	  suite, 2);
 
-  session->internals.compression_method =
-    session->internals.resumed_security_parameters.read_compression_algorithm;
+  _gnutls_epoch_set_cipher_suite (session, EPOCH_NEXT,
+				  &session->internals.resumed_security_parameters.current_cipher_suite);
+  _gnutls_epoch_set_compression (session, EPOCH_NEXT,
+				 session->internals.resumed_compression_method);
+
   /* or write_compression_algorithm
    * they are the same
    */
@@ -931,6 +934,10 @@ _gnutls_server_select_suite (gnutls_session_t session, opaque * data,
 		 _gnutls_cipher_suite_get_name (&cs));
 	      memcpy (session->security_parameters.current_cipher_suite.suite,
 		      ciphers[i].suite, 2);
+	      _gnutls_epoch_set_cipher_suite (session, EPOCH_NEXT,
+					      &session->security_parameters.current_cipher_suite);
+
+
 	      retval = 0;
 	      goto finish;
 	    }
@@ -1013,6 +1020,8 @@ _gnutls_server_select_comp_method (gnutls_session_t session,
 	      session->internals.compression_method = method;
 	      gnutls_free (comps);
 
+	      _gnutls_epoch_set_compression (session, EPOCH_NEXT, method);
+
 	      _gnutls_handshake_log
 		("HSK[%p]: Selected Compression Method: %s\n", session,
 		 gnutls_compression_get_name (session->
@@ -1565,6 +1574,7 @@ _gnutls_client_set_ciphersuite (gnutls_session_t session, opaque suite[2])
     }
 
   memcpy (session->security_parameters.current_cipher_suite.suite, suite, 2);
+  _gnutls_epoch_set_cipher_suite (session, EPOCH_NEXT, &session->security_parameters.current_cipher_suite);
 
   _gnutls_handshake_log ("HSK[%p]: Selected cipher suite: %s\n", session,
 			 _gnutls_cipher_suite_get_name
@@ -1645,7 +1655,7 @@ _gnutls_client_set_comp_method (gnutls_session_t session, opaque comp_method)
 
   session->internals.compression_method =
     _gnutls_compression_get_id (comp_method);
-
+  _gnutls_epoch_set_compression (session, EPOCH_NEXT, session->internals.compression_method);
 
   return 0;
 }
@@ -1678,6 +1688,12 @@ _gnutls_client_check_if_resuming (gnutls_session_t session,
 	      session->security_parameters.server_random, GNUTLS_RANDOM_SIZE);
       memcpy (session->internals.resumed_security_parameters.client_random,
 	      session->security_parameters.client_random, GNUTLS_RANDOM_SIZE);
+
+      _gnutls_epoch_set_cipher_suite
+	(session, EPOCH_NEXT, &session->internals.resumed_security_parameters.current_cipher_suite);
+      _gnutls_epoch_set_compression
+	(session, EPOCH_NEXT, session->internals.resumed_compression_method);
+
       session->internals.resumed = RESUME_TRUE;	/* we are resuming */
 
       return 0;
@@ -2594,6 +2610,20 @@ int
 gnutls_handshake (gnutls_session_t session)
 {
   int ret;
+  record_parameters_st *params;
+
+  ret = _gnutls_epoch_get (session, session->security_parameters.epoch_next,
+			   &params);
+  if (ret < 0)
+    {
+      /* We assume the epoch is not allocated if _gnutls_epoch_get fails. */
+      ret = _gnutls_epoch_alloc (session, session->security_parameters.epoch_next, NULL);
+      if (ret < 0)
+	{
+	  gnutls_assert ();
+	  return ret;
+	}
+    }
 
   if (session->security_parameters.entity == GNUTLS_CLIENT)
     {
@@ -2629,6 +2659,8 @@ gnutls_handshake (gnutls_session_t session)
   _gnutls_handshake_io_buffer_clear (session);
   _gnutls_handshake_internal_state_clear (session);
 
+  session->security_parameters.epoch_next++;
+
   return 0;
 }
 
diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
index d8cd028..5d1b64e 100644
--- a/lib/gnutls_int.h
+++ b/lib/gnutls_int.h
@@ -285,6 +285,12 @@ struct gnutls_key_st
 typedef struct gnutls_key_st *gnutls_key_st;
 
 
+struct record_state_st;
+typedef struct record_state_st record_state_st;
+
+struct record_parameters_st;
+typedef struct record_parameters_st record_parameters_st;
+
 /* STATE (cont) */
 
 #include <gnutls_hash_int.h>
@@ -334,18 +340,17 @@ typedef struct
 {
   gnutls_connection_end_t entity;
   gnutls_kx_algorithm_t kx_algorithm;
-  /* we've got separate write/read bulk/macs because
-   * there is a time in handshake where the peer has
-   * null cipher and we don't
-   */
-  gnutls_cipher_algorithm_t read_bulk_cipher_algorithm;
-  gnutls_mac_algorithm_t read_mac_algorithm;
-  gnutls_compression_method_t read_compression_algorithm;
+  handshake_mac_type_t handshake_mac_handle_type;      /* one of HANDSHAKE_TYPE_10 and HANDSHAKE_TYPE_12 */
 
-  gnutls_cipher_algorithm_t write_bulk_cipher_algorithm;
-  gnutls_mac_algorithm_t write_mac_algorithm;
-  gnutls_compression_method_t write_compression_algorithm;
-  handshake_mac_type_t handshake_mac_handle_type;	/* one of HANDSHAKE_TYPE_10 and HANDSHAKE_TYPE_12 */
+  /* The epoch used to read and write */
+  uint16_t epoch_read;
+  uint16_t epoch_write;
+
+  /* The epoch that the next handshake will initialize. */
+  uint16_t epoch_next;
+
+  /* The epoch at index 0 of record_parameters. */
+  uint16_t epoch_min;
 
   /* this is the ciphersuite we are going to use 
    * moved here from internals in order to be restored
@@ -378,34 +383,36 @@ typedef struct
   int do_recv_supplemental, do_send_supplemental;
 } security_parameters_st;
 
-/* This structure holds the generated keys
- */
-typedef struct
+struct record_state_st
 {
-  gnutls_datum_t server_write_mac_secret;
-  gnutls_datum_t client_write_mac_secret;
-  gnutls_datum_t server_write_IV;
-  gnutls_datum_t client_write_IV;
-  gnutls_datum_t server_write_key;
-  gnutls_datum_t client_write_key;
-  int generated_keys;		/* zero if keys have not
-				 * been generated. Non zero
-				 * otherwise.
-				 */
-} cipher_specs_st;
+  gnutls_datum_t mac_secret;
+  gnutls_datum_t IV;
+  gnutls_datum_t key;
+  cipher_hd_st   cipher_state;
+  comp_hd_t      compression_state;
+  uint64         sequence_number;
+};
 
+/* These are used to resolve relative epochs. These values are just
+   outside the 16 bit range to prevent off-by-one errors. An absolute
+   epoch may be referred to by its numeric id in the range
+   0x0000-0xffff. */
+#define EPOCH_READ_CURRENT  70000
+#define EPOCH_WRITE_CURRENT 70001
+#define EPOCH_NEXT          70002
 
-typedef struct
+struct record_parameters_st
 {
-  cipher_hd_st write_cipher_state;
-  cipher_hd_st read_cipher_state;
-  comp_hd_t read_compression_state;
-  comp_hd_t write_compression_state;
-  gnutls_datum_t read_mac_secret;
-  gnutls_datum_t write_mac_secret;
-  uint64 read_sequence_number;
-  uint64 write_sequence_number;
-} conn_stat_st;
+  uint16_t epoch;
+  int initialized;
+
+  gnutls_cipher_algorithm_t   cipher_algorithm;
+  gnutls_mac_algorithm_t      mac_algorithm;
+  gnutls_compression_method_t compression_algorithm;
+
+  record_state_st read;
+  record_state_st write;
+};
 
 typedef struct
 {
@@ -528,6 +535,7 @@ typedef struct
   /* resumed session */
   int resumed:1;	/* RESUME_TRUE or FALSE - if we are resuming a session */
   security_parameters_st resumed_security_parameters;
+  gnutls_compression_method_t resumed_compression_method;
 
   /* sockets internals */
   int lowat;
@@ -719,11 +727,13 @@ typedef struct
    */
 } internals_st;
 
+/* Maximum number of epochs we keep around. */
+#define MAX_EPOCH_INDEX 16
+
 struct gnutls_session_int
 {
   security_parameters_st security_parameters;
-  cipher_specs_st cipher_specs;
-  conn_stat_st connection_state;
+  record_parameters_st *record_parameters[MAX_EPOCH_INDEX];
   internals_st internals;
   gnutls_key_st key;
 };
diff --git a/lib/gnutls_num.h b/lib/gnutls_num.h
index 18a13ca..e35b92f 100644
--- a/lib/gnutls_num.h
+++ b/lib/gnutls_num.h
@@ -44,6 +44,6 @@ uint32_t _gnutls_uint64touint32 (const uint64 *);
 
 int _gnutls_uint64pp (uint64 *);
 # define _gnutls_uint64zero(x) x.i[0] = x.i[1] = x.i[2] = x.i[3] = x.i[4] = x.i[5] = x.i[6] = x.i[7] = 0
-# define UINT64DATA(x) x.i
+# define UINT64DATA(x) (x.i)
 
 #endif /* GNUTLS_NUM_H */
diff --git a/lib/gnutls_record.c b/lib/gnutls_record.c
index 27e5e56..972c8ad 100644
--- a/lib/gnutls_record.c
+++ b/lib/gnutls_record.c
@@ -42,6 +42,7 @@
 #include "gnutls_num.h"
 #include "gnutls_record.h"
 #include "gnutls_datum.h"
+#include "gnutls_constate.h"
 #include "ext_max_record.h"
 #include <gnutls_state.h>
 #include <gnutls_dh.h>
@@ -334,8 +335,8 @@ copy_record_version (gnutls_session_t session,
  */
 ssize_t
 _gnutls_send_int (gnutls_session_t session, content_type_t type,
-		  gnutls_handshake_description_t htype, const void *_data,
-		  size_t sizeofdata, unsigned int mflags)
+		  gnutls_handshake_description_t htype, unsigned int epoch_rel,
+		  const void *_data, size_t sizeofdata, unsigned int mflags)
 {
   mbuffer_st *bufel;
   size_t cipher_size;
@@ -343,6 +344,24 @@ _gnutls_send_int (gnutls_session_t session, content_type_t type,
   int data2send_size;
   uint8_t headers[5];
   const uint8_t *data = _data;
+  record_parameters_st *record_params;
+  record_state_st *record_state;
+
+  ret = _gnutls_epoch_get (session, epoch_rel, &record_params);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+
+  /* Safeguard against processing data with an incomplete cipher state. */
+  if (!record_params->initialized)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  record_state = &record_params->write;
 
   /* Do not allow null pointer if the send buffer is empty.
    * If the previous send was interrupted then a null pointer is
@@ -372,8 +391,7 @@ _gnutls_send_int (gnutls_session_t session, content_type_t type,
 
   _gnutls_record_log
     ("REC[%p]: Sending Packet[%d] %s(%d) with length: %d\n", session,
-     (int) _gnutls_uint64touint32 (&session->
-				   connection_state.write_sequence_number),
+     (int) _gnutls_uint64touint32 (&record_state->sequence_number),
      _gnutls_packet2str (type), type, (int) sizeofdata);
 
   if (sizeofdata > MAX_RECORD_SEND_SIZE)
@@ -411,7 +429,7 @@ _gnutls_send_int (gnutls_session_t session, content_type_t type,
 	_gnutls_encrypt (session, headers, RECORD_HEADER_SIZE, data,
 			 data2send_size, _mbuffer_get_udata_ptr(bufel), cipher_size, type,
 			 (session->internals.priorities.no_padding ==
-			  0) ? 1 : 0);
+			  0) ? 1 : 0, record_params);
       if (cipher_size <= 0)
 	{
 	  gnutls_assert ();
@@ -426,8 +444,7 @@ _gnutls_send_int (gnutls_session_t session, content_type_t type,
 
       /* increase sequence number
        */
-      if (_gnutls_uint64pp
-	  (&session->connection_state.write_sequence_number) != 0)
+      if (_gnutls_uint64pp (&record_state->sequence_number) != 0)
 	{
 	  session_invalidate (session);
 	  gnutls_assert ();
@@ -467,7 +484,7 @@ _gnutls_send_int (gnutls_session_t session, content_type_t type,
 		      session,
 		      (int)
 		      _gnutls_uint64touint32
-		      (&session->connection_state.write_sequence_number),
+		      (&record_state->sequence_number),
 		      _gnutls_packet2str (type), type, (int)cipher_size);
 
   return retval;
@@ -484,7 +501,7 @@ _gnutls_send_change_cipher_spec (gnutls_session_t session, int again)
   _gnutls_handshake_log ("REC[%p]: Sent ChangeCipherSpec\n", session);
 
   if (again == 0)
-    return _gnutls_send_int (session, GNUTLS_CHANGE_CIPHER_SPEC, -1, data, 1, MBUFFER_FLUSH);
+    return _gnutls_send_int (session, GNUTLS_CHANGE_CIPHER_SPEC, -1, EPOCH_WRITE_CURRENT, data, 1, MBUFFER_FLUSH);
   else
     {
       return _gnutls_io_write_flush (session);
@@ -845,8 +862,8 @@ get_temp_recv_buffer (gnutls_session_t session, gnutls_datum_t * tmp)
  */
 ssize_t
 _gnutls_recv_int (gnutls_session_t session, content_type_t type,
-		  gnutls_handshake_description_t htype, opaque * data,
-		  size_t sizeofdata)
+		  gnutls_handshake_description_t htype,
+		  opaque * data, size_t sizeofdata)
 {
   int decrypted_length;
   opaque version[2];
@@ -857,6 +874,24 @@ _gnutls_recv_int (gnutls_session_t session, content_type_t type,
   uint16_t header_size;
   int empty_packet = 0;
   gnutls_datum_t data_enc, tmp;
+  record_parameters_st *record_params;
+  record_state_st *record_state;
+
+  ret = _gnutls_epoch_get (session, EPOCH_READ_CURRENT, &record_params);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+
+  /* Safeguard against processing data with an incomplete cipher state. */
+  if (!record_params->initialized)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  record_state = &record_params->read;
 
   if (type != GNUTLS_ALERT && (sizeofdata == 0 || data == NULL))
     {
@@ -951,13 +986,11 @@ begin:
 
   _gnutls_record_log
     ("REC[%p]: Expected Packet[%d] %s(%d) with length: %d\n", session,
-     (int) _gnutls_uint64touint32 (&session->
-				   connection_state.read_sequence_number),
+     (int) _gnutls_uint64touint32 (&record_state->sequence_number),
      _gnutls_packet2str (type), type, (int) sizeofdata);
   _gnutls_record_log ("REC[%p]: Received Packet[%d] %s(%d) with length: %d\n",
 		      session,
-		      (int) _gnutls_uint64touint32 (&session->
-						    connection_state.read_sequence_number),
+		      (int) _gnutls_uint64touint32 (&record_state->sequence_number),
 		      _gnutls_packet2str (recv_type), recv_type, length);
 
   if (length > MAX_RECV_SIZE)
@@ -1010,7 +1043,7 @@ begin:
  */
   ret =
     _gnutls_decrypt (session, ciphertext, length, tmp.data, tmp.size,
-		     recv_type);
+		     recv_type, record_params);
   if (ret < 0)
     {
       session_unresumable (session);
@@ -1042,13 +1075,12 @@ begin:
 
   _gnutls_record_log
     ("REC[%p]: Decrypted Packet[%d] %s(%d) with length: %d\n", session,
-     (int) _gnutls_uint64touint32 (&session->
-				   connection_state.read_sequence_number),
+     (int) _gnutls_uint64touint32 (&record_state->sequence_number),
      _gnutls_packet2str (recv_type), recv_type, decrypted_length);
 
 /* increase sequence number 
  */
-  if (_gnutls_uint64pp (&session->connection_state.read_sequence_number) != 0)
+  if (_gnutls_uint64pp (&record_state->sequence_number) != 0)
     {
       session_invalidate (session);
       gnutls_assert ();
@@ -1148,8 +1180,8 @@ ssize_t
 gnutls_record_send (gnutls_session_t session, const void *data,
 		    size_t sizeofdata)
 {
-  return _gnutls_send_int (session, GNUTLS_APPLICATION_DATA, -1, data,
-			   sizeofdata, MBUFFER_FLUSH);
+  return _gnutls_send_int (session, GNUTLS_APPLICATION_DATA, -1, EPOCH_WRITE_CURRENT,
+			   data, sizeofdata, MBUFFER_FLUSH);
 }
 
 /**
diff --git a/lib/gnutls_record.h b/lib/gnutls_record.h
index 6d35247..76c8520 100644
--- a/lib/gnutls_record.h
+++ b/lib/gnutls_record.h
@@ -30,7 +30,7 @@
 # include <gnutls_buffers.h>
 
 ssize_t _gnutls_send_int (gnutls_session_t session, content_type_t type,
-			  gnutls_handshake_description_t htype,
+			  gnutls_handshake_description_t htype, unsigned int epoch_rel,
 			  const void *data, size_t sizeofdata, unsigned int mflags);
 ssize_t _gnutls_recv_int (gnutls_session_t session, content_type_t type,
 			  gnutls_handshake_description_t, opaque * data,
diff --git a/lib/gnutls_session_pack.c b/lib/gnutls_session_pack.c
index 70de27d..542abb3 100644
--- a/lib/gnutls_session_pack.c
+++ b/lib/gnutls_session_pack.c
@@ -42,6 +42,7 @@
 #include <gnutls_datum.h>
 #include <gnutls_num.h>
 #include <gnutls_extensions.h>
+#include <gnutls_constate.h>
 
 static int pack_certificate_auth_info (gnutls_session_t,
 				       gnutls_buffer_st * packed_session);
@@ -740,6 +741,21 @@ pack_security_parameters (gnutls_session_t session,
   int ret;
   int size_offset;
   size_t cur_size;
+  record_parameters_st *params;
+
+  if ( session->security_parameters.epoch_read
+       != session->security_parameters.epoch_write)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  ret = _gnutls_epoch_get (session, EPOCH_READ_CURRENT, &params);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      return ret;
+    }
 
   /* move after the auth info stuff.
    */
@@ -750,14 +766,9 @@ pack_security_parameters (gnutls_session_t session,
 
   BUFFER_APPEND(ps, &session->security_parameters.entity, 1);
   BUFFER_APPEND(ps, &session->security_parameters.kx_algorithm, 1);
-  BUFFER_APPEND(ps, &session->security_parameters.read_bulk_cipher_algorithm, 1);
-  BUFFER_APPEND(ps, &session->security_parameters.read_mac_algorithm, 1);
-  BUFFER_APPEND(ps, &session->security_parameters.read_compression_algorithm, 1);
-  BUFFER_APPEND(ps, &session->security_parameters.write_bulk_cipher_algorithm, 1);
-  BUFFER_APPEND(ps, &session->security_parameters.write_mac_algorithm, 1);
-  BUFFER_APPEND(ps, &session->security_parameters.write_compression_algorithm, 1);
   BUFFER_APPEND(ps, &session->security_parameters.current_cipher_suite.suite[0], 1);
   BUFFER_APPEND(ps, &session->security_parameters.current_cipher_suite.suite[1], 1);
+  BUFFER_APPEND(ps, &params->compression_algorithm, 1);
   BUFFER_APPEND(ps, &session->security_parameters.cert_type, 1);
   BUFFER_APPEND(ps, &session->security_parameters.version, 1);
 
@@ -795,15 +806,10 @@ unpack_security_parameters (gnutls_session_t session,
 
   BUFFER_POP(ps, &session->internals.resumed_security_parameters.entity, 1);
   BUFFER_POP(ps, &session->internals.resumed_security_parameters.kx_algorithm, 1);
-  BUFFER_POP(ps, &session->internals.resumed_security_parameters.read_bulk_cipher_algorithm, 1);
-  BUFFER_POP(ps, &session->internals.resumed_security_parameters.read_mac_algorithm, 1);
-  BUFFER_POP(ps, &session->internals.resumed_security_parameters.read_compression_algorithm, 1);
-  BUFFER_POP(ps, &session->internals.resumed_security_parameters.write_bulk_cipher_algorithm, 1);
-  BUFFER_POP(ps, &session->internals.resumed_security_parameters.write_mac_algorithm, 1);
-  BUFFER_POP(ps, &session->internals.resumed_security_parameters.write_compression_algorithm, 1);
   BUFFER_POP(ps, &session->internals.resumed_security_parameters.current_cipher_suite.suite[0], 1);
   BUFFER_POP(ps, &session->internals.resumed_security_parameters.
     current_cipher_suite.suite[1], 1);
+  BUFFER_POP(ps, &session->internals.resumed_compression_method, 1);
   BUFFER_POP(ps, &session->internals.resumed_security_parameters.cert_type, 1);
   BUFFER_POP(ps, &session->internals.resumed_security_parameters.version, 1);
 
diff --git a/lib/gnutls_state.c b/lib/gnutls_state.c
index b25d31b..7e21cf0 100644
--- a/lib/gnutls_state.c
+++ b/lib/gnutls_state.c
@@ -40,6 +40,7 @@
 #include <gnutls_buffers.h>
 #include <gnutls_mbuffers.h>
 #include <gnutls_state.h>
+#include <gnutls_constate.h>
 #include <auth_cert.h>
 #include <auth_anon.h>
 #include <auth_psk.h>
@@ -73,7 +74,10 @@ _gnutls_session_cert_type_set (gnutls_session_t session,
 gnutls_cipher_algorithm_t
 gnutls_cipher_get (gnutls_session_t session)
 {
-  return session->security_parameters.read_bulk_cipher_algorithm;
+  record_parameters_st *record_params;
+  _gnutls_epoch_get (session, EPOCH_READ_CURRENT, &record_params);
+
+  return record_params->cipher_algorithm;
 }
 
 /**
@@ -119,7 +123,10 @@ gnutls_kx_get (gnutls_session_t session)
 gnutls_mac_algorithm_t
 gnutls_mac_get (gnutls_session_t session)
 {
-  return session->security_parameters.read_mac_algorithm;
+  record_parameters_st *record_params;
+  _gnutls_epoch_get (session, EPOCH_READ_CURRENT, &record_params);
+
+  return record_params->mac_algorithm;
 }
 
 /**
@@ -134,7 +141,10 @@ gnutls_mac_get (gnutls_session_t session)
 gnutls_compression_method_t
 gnutls_compression_get (gnutls_session_t session)
 {
-  return session->security_parameters.read_compression_algorithm;
+  record_parameters_st *record_params;
+  _gnutls_epoch_get (session, EPOCH_READ_CURRENT, &record_params);
+
+  return record_params->compression_algorithm;
 }
 
 /* Check if the given certificate type is supported.
@@ -265,30 +275,30 @@ _gnutls_handshake_internal_state_clear (gnutls_session_t session)
 int
 gnutls_init (gnutls_session_t * session, gnutls_connection_end_t con_end)
 {
+  int ret;
+  record_parameters_st *epoch;
+
   *session = gnutls_calloc (1, sizeof (struct gnutls_session_int));
   if (*session == NULL)
     return GNUTLS_E_MEMORY_ERROR;
 
+  ret = _gnutls_epoch_alloc (*session, 0, &epoch);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_MEMORY_ERROR;
+    }
+
+  /* Set all NULL algos on epoch 0 */
+  _gnutls_epoch_set_null_algos(*session, epoch);
+
+  (*session)->security_parameters.epoch_next = 1;
+
   (*session)->security_parameters.entity = con_end;
 
   /* the default certificate type for TLS */
   (*session)->security_parameters.cert_type = DEFAULT_CERT_TYPE;
 
-/* Set the defaults for initial handshake */
-  (*session)->security_parameters.read_bulk_cipher_algorithm =
-    (*session)->security_parameters.write_bulk_cipher_algorithm =
-    GNUTLS_CIPHER_NULL;
-
-  (*session)->security_parameters.read_mac_algorithm =
-    (*session)->security_parameters.write_mac_algorithm = GNUTLS_MAC_NULL;
-
-  (*session)->security_parameters.read_compression_algorithm =
-    GNUTLS_COMP_NULL;
-  (*session)->security_parameters.write_compression_algorithm =
-    GNUTLS_COMP_NULL;
-
-  (*session)->internals.enable_private = 0;
-
   /* Initialize buffers */
   _gnutls_buffer_init (&(*session)->internals.application_data_buffer);
   _gnutls_buffer_init (&(*session)->internals.handshake_data_buffer);
@@ -372,6 +382,7 @@ _gnutls_session_is_resumable (gnutls_session_t session)
 void
 gnutls_deinit (gnutls_session_t session)
 {
+  unsigned int i;
 
   if (session == NULL)
     return;
@@ -383,8 +394,12 @@ gnutls_deinit (gnutls_session_t session)
   _gnutls_handshake_io_buffer_clear (session);
   _gnutls_ext_free_session_data (session);
 
-  _gnutls_free_datum (&session->connection_state.read_mac_secret);
-  _gnutls_free_datum (&session->connection_state.write_mac_secret);
+  for(i=0; i < MAX_EPOCH_INDEX; i++)
+    if (session->record_parameters[i] != NULL)
+      {
+	_gnutls_epoch_free (session, session->record_parameters[i]);
+	session->record_parameters[i] = NULL;
+      }
 
   _gnutls_buffer_clear (&session->internals.ia_data_buffer);
   _gnutls_buffer_clear (&session->internals.handshake_hash_buffer);
@@ -396,22 +411,6 @@ gnutls_deinit (gnutls_session_t session)
   gnutls_credentials_clear (session);
   _gnutls_selected_certs_deinit (session);
 
-  _gnutls_cipher_deinit (&session->connection_state.read_cipher_state);
-  _gnutls_cipher_deinit (&session->connection_state.write_cipher_state);
-
-  if (session->connection_state.read_compression_state != NULL)
-    _gnutls_comp_deinit (session->connection_state.read_compression_state, 1);
-  if (session->connection_state.write_compression_state != NULL)
-    _gnutls_comp_deinit (session->connection_state.write_compression_state,
-			 0);
-
-  _gnutls_free_datum (&session->cipher_specs.server_write_mac_secret);
-  _gnutls_free_datum (&session->cipher_specs.client_write_mac_secret);
-  _gnutls_free_datum (&session->cipher_specs.server_write_IV);
-  _gnutls_free_datum (&session->cipher_specs.client_write_IV);
-  _gnutls_free_datum (&session->cipher_specs.server_write_key);
-  _gnutls_free_datum (&session->cipher_specs.client_write_key);
-
   if (session->key != NULL)
     {
       _gnutls_mpi_release (&session->key->KEY);
diff --git a/libextra/gnutls_ia.c b/libextra/gnutls_ia.c
index e2ff0d9..f303386 100644
--- a/libextra/gnutls_ia.c
+++ b/libextra/gnutls_ia.c
@@ -97,7 +97,7 @@ _gnutls_send_inner_application (gnutls_session_t session,
       memcpy (p + 4, data, sizeofdata);
     }
 
-  len = _gnutls_send_int (session, GNUTLS_INNER_APPLICATION, -1, p, plen, MBUFFER_FLUSH);
+  len = _gnutls_send_int (session, GNUTLS_INNER_APPLICATION, -1, EPOCH_WRITE_CURRENT, p, plen, MBUFFER_FLUSH);
 
   if (p)
     gnutls_free (p);
-- 
1.7.1





More information about the Gnutls-devel mailing list