gnutls-commit
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[SCM] GNU gnutls branch, master, updated. gnutls_2_9_10-365-g20151ed


From: Nikos Mavrogiannopoulos
Subject: [SCM] GNU gnutls branch, master, updated. gnutls_2_9_10-365-g20151ed
Date: Fri, 17 Sep 2010 06:22:12 +0000

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU gnutls".

http://git.savannah.gnu.org/cgit/gnutls.git/commit/?id=20151edffdb8d99c7feb986a2f102df76314cb7d

The branch, master has been updated
       via  20151edffdb8d99c7feb986a2f102df76314cb7d (commit)
       via  e463a5b53fdc5f3ce46c297f405e3054e11ba73d (commit)
       via  6dbb25fc258e75faffae57ffba004930313140a0 (commit)
      from  976acc75484c5cf08f9d6bd19c45f680b7940d81 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 20151edffdb8d99c7feb986a2f102df76314cb7d
Author: Jonathan Bastien-Filiatrault <address@hidden>
Date:   Thu Sep 16 23:32:06 2010 -0400

    Explicit symmetric cipher state versionning.
    
    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 <address@hidden>
    Signed-off-by: Nikos Mavrogiannopoulos <address@hidden>

commit e463a5b53fdc5f3ce46c297f405e3054e11ba73d
Author: Jonathan Bastien-Filiatrault <address@hidden>
Date:   Thu Sep 16 23:32:05 2010 -0400

    Add gnutls_assert_val idiom.
    
     This warrants being made in an inline function or macro since it is
     used throughout the code. This converts 4 line repetitive blocks into
     1 line.
    
    Signed-off-by: Jonathan Bastien-Filiatrault <address@hidden>
    Signed-off-by: Nikos Mavrogiannopoulos <address@hidden>

commit 6dbb25fc258e75faffae57ffba004930313140a0
Author: Nikos Mavrogiannopoulos <address@hidden>
Date:   Tue Sep 14 13:34:56 2010 +0200

    updated for 2.11.1

-----------------------------------------------------------------------

Summary of changes:
 AUTHORS                   |    6 +-
 NEWS                      |   20 +-
 configure.ac              |    2 +-
 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     |  936 ++++++++++++++++++---------------------------
 lib/gnutls_constate.h     |   32 +-
 lib/gnutls_errors.h       |   17 +
 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 +-
 21 files changed, 690 insertions(+), 788 deletions(-)

diff --git a/AUTHORS b/AUTHORS
index 967b539..117af3d 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -55,9 +55,6 @@ Daiki Ueno <address@hidden>
 Added TLS Session Ticket (RFC 5077) support,
 finished client-side TLS 1.2 support.
 
-Jonathan Bastien-Filiatrault <address@hidden>
-Fix TLS-version checks.
-
 Brad Hards <address@hidden>
 Add X.509 Issuer Alternative Name functions.
 
@@ -65,9 +62,10 @@ Boyan Kasarov <address@hidden>
 C++ fixes.
 
 Steve Dispensa <address@hidden>
-TLS safe renegotiation fix.
+Initial TLS safe renegotiation patch.
 
 Jonathan Bastien-Filiatrault <address@hidden>
+Fix TLS-version checks.
 Redesign and implementation of the buffering layer.
 
 -----BEGIN PGP PUBLIC KEY BLOCK-----
diff --git a/NEWS b/NEWS
index 6a9f279..565e87e 100644
--- a/NEWS
+++ b/NEWS
@@ -3,7 +3,17 @@ Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005,
               2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
 See the end for copying conditions.
 
-* Version 2.11.1 (unreleased)
+* Version 2.11.1 (released 2010-09-14)
+
+** libgnutls: Nettle is the default crypto back end. Use --with-libgcrypt
+to use the libgcrypt back end.
+
+** libgnutls: Depend on nettle 2.1. This makes nettle a fully working
+backend crypto library.
+
+** libgnutls: Added RSA_NULL_SHA1 and SHA256 ciphersuites.
+
+** libgnutls: Several updates in the buffering internal interface.
 
 ** libgnutls: Is now more liberal in the PEM decoding. That is spaces and 
 tabs are being skipped.
@@ -17,14 +27,6 @@ TLS spec.
 system calls. Applications that depended on it being 1 should explicitly call
 gnutls_transport_set_lowat().
 
-** libgnutls: Nettle is the default crypto back end. Use --with-libgcrypt
-to use the libgcrypt back end.
-
-** libgnutls: Depend on nettle 2.1. This makes nettle a fully working
-backend crypto library.
-
-** libgnutls: Added RSA_NULL_SHA1 and SHA256 ciphersuites.
-
 ** libgnutls: Updated documentation and gnutls_pk_params_t mappings
 to ECRYPT II recommendations. Mappings were moved to a single location
 and DSA keys are handled differently (since DSA2 allows for 1024,2048
diff --git a/configure.ac b/configure.ac
index 0ec3cc4..086ab8f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -22,7 +22,7 @@ dnl Process this file with autoconf to produce a configure 
script.
 # USA
 
 AC_PREREQ(2.61)
-AC_INIT([GnuTLS], [2.11.0], address@hidden)
+AC_INIT([GnuTLS], [2.11.1], address@hidden)
 AC_CONFIG_AUX_DIR([build-aux])
 AC_CONFIG_MACRO_DIR([m4])
 
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);
 
-  return _gnutls_set_keys (session, hash_size, IV_size, key_size,
-                          export_flag);
+  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
+      || _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);
+
+  if (params->initialized)
+    return 0;
+
+  _gnutls_record_log
+    ("REC[%p]: Initializing epoch #%u\n", session, params->epoch);
 
-  mac_algo = session->security_parameters.write_mac_algorithm;
-  algo = session->security_parameters.write_bulk_cipher_algorithm;
+  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;
+  security_parameters_st *src = 
&session->internals.resumed_security_parameters;
+  security_parameters_st *dst = &session->security_parameters;
 
-  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;
-}
-
-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;
-
-  _gnutls_uint64zero (session->connection_state.read_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
- */
+  /* 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;
-
-      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_check_algos (session,
+                                
&session->security_parameters.current_cipher_suite,
+                                session->internals.compression_method);
+      if (ret < 0)
+       return ret;
 
+      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,350 +578,230 @@ _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;
-
-      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_check_algos (session,
+                                
&session->security_parameters.current_cipher_suite,
+                                session->internals.compression_method);
+      if (ret < 0)
+       return ret;
+
+      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;
-       }
-
+  session->security_parameters.epoch_write = epoch_next;
+  _gnutls_epoch_gc (session);
 
-      /* 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;
-           }
+  return 0;
+}
 
-       }
+/* Sets the specified kx algorithm into pending session
+ */
+int
+_gnutls_set_kx (gnutls_session_t session, gnutls_kx_algorithm_t algo)
+{
 
+  if (_gnutls_kx_is_ok (algo) == 0)
+    {
+      session->security_parameters.kx_algorithm = algo;
+    }
+  else
+    return gnutls_assert_val (GNUTLS_E_INTERNAL_ERROR);
 
-      break;
+  if (_gnutls_kx_priority (session, algo) < 0)
+    return gnutls_assert_val (GNUTLS_E_UNWANTED_ALGORITHM);
 
-    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);
+  return 0;
+}
 
-      if (rc < 0
-         && session->security_parameters.write_bulk_cipher_algorithm !=
-         GNUTLS_CIPHER_NULL)
-       {
-         gnutls_assert ();
-         return GNUTLS_E_INTERNAL_ERROR;
-       }
+static inline int
+epoch_resolve(gnutls_session_t session,
+             unsigned int epoch_rel, uint16_t *epoch_out)
+{
+  switch (epoch_rel)
+    {
+    case EPOCH_READ_CURRENT:
+      *epoch_out = session->security_parameters.epoch_read;
+      return 0;
 
-      /* 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;
-           }
-       }
+    case EPOCH_WRITE_CURRENT:
+      *epoch_out = session->security_parameters.epoch_write;
+      return 0;
 
-      break;
+    case EPOCH_NEXT:
+      *epoch_out = session->security_parameters.epoch_next;
+      return 0;
 
     default:
-      gnutls_assert ();
-      return GNUTLS_E_INTERNAL_ERROR;
-    }
+      if (epoch_rel > 0xffffu)
+       return gnutls_assert_val (GNUTLS_E_INVALID_REQUEST);
 
+      *epoch_out = epoch_rel;
+      return 0;
+    }
+}
 
-  session->connection_state.write_compression_state =
-    _gnutls_comp_init (session->
-                      security_parameters.write_compression_algorithm, 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 (session->connection_state.write_compression_state == GNUTLS_COMP_FAILED)
+  if (epoch_index >= MAX_EPOCH_INDEX)
     {
       gnutls_assert ();
-      return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM;
+      return NULL;
     }
 
-  return 0;
+  /* The slot may still be empty (NULL) */
+  return &session->record_parameters[epoch_index];
 }
 
-/* Sets the specified cipher into the pending session 
- */
 int
-_gnutls_set_read_cipher (gnutls_session_t session,
-                        gnutls_cipher_algorithm_t algo)
+_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;
 
-  if (_gnutls_cipher_is_ok (algo) == 0)
-    {
-      if (_gnutls_cipher_priority (session, algo) < 0)
-       {
-         gnutls_assert ();
-         return GNUTLS_E_UNWANTED_ALGORITHM;
-       }
+  ret = epoch_resolve (session, epoch_rel, &epoch);
+  if (ret < 0)
+    return gnutls_assert_val (ret);
 
-      session->security_parameters.read_bulk_cipher_algorithm = algo;
+  params = epoch_get_slot (session, epoch);
+  if (params == NULL || *params == NULL)
+    return gnutls_assert_val (GNUTLS_E_INVALID_REQUEST);
 
-    }
-  else
-    {
-      gnutls_assert ();
-      return GNUTLS_E_INTERNAL_ERROR;
-    }
+  *params_out = *params;
 
   return 0;
-
 }
 
 int
-_gnutls_set_write_cipher (gnutls_session_t session,
-                         gnutls_cipher_algorithm_t algo)
+_gnutls_epoch_alloc(gnutls_session_t session, uint16_t epoch,
+                   record_parameters_st **out)
 {
+  record_parameters_st **slot;
 
-  if (_gnutls_cipher_is_ok (algo) == 0)
-    {
-      if (_gnutls_cipher_priority (session, algo) < 0)
-       {
-         gnutls_assert ();
-         return GNUTLS_E_UNWANTED_ALGORITHM;
-       }
+  _gnutls_record_log
+    ("REC[%p]: Allocating epoch #%u\n", session, epoch);
 
-      session->security_parameters.write_bulk_cipher_algorithm = algo;
+  slot = epoch_get_slot(session, epoch);
 
-    }
-  else
-    {
-      gnutls_assert ();
-      return GNUTLS_E_INTERNAL_ERROR;
-    }
+  /* If slot out of range or not empty. */
+  if (slot == NULL)
+    return gnutls_assert_val (GNUTLS_E_INVALID_REQUEST);
 
-  return 0;
+  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;
 
-/* Sets the specified algorithm into pending compression session 
- */
-int
-_gnutls_set_read_compression (gnutls_session_t session,
-                             gnutls_compression_method_t algo)
-{
+  if (out != NULL)
+    *out = *slot;
 
-  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)
+static inline int
+epoch_alive (gnutls_session_t session, record_parameters_st *params)
 {
+  const security_parameters_st *sp = &session->security_parameters;
 
-  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;
-
+  /* 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);
 }
 
-/* Sets the specified kx algorithm into pending session 
- */
-int
-_gnutls_set_kx (gnutls_session_t session, gnutls_kx_algorithm_t algo)
+void
+_gnutls_epoch_gc (gnutls_session_t session)
 {
-
-  if (_gnutls_kx_is_ok (algo) == 0)
-    {
-      session->security_parameters.kx_algorithm = algo;
-    }
-  else
-    {
-      gnutls_assert ();
-      return GNUTLS_E_INTERNAL_ERROR;
-    }
-  if (_gnutls_kx_priority (session, algo) < 0)
-    {
-      gnutls_assert ();
-      /* we shouldn't get here */
-      return GNUTLS_E_UNWANTED_ALGORITHM;
-    }
-
-  return 0;
-
+  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);
 }
 
-/* Sets the specified mac algorithm into pending session */
-int
-_gnutls_set_read_mac (gnutls_session_t session, gnutls_mac_algorithm_t algo)
+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);
 
-  if (_gnutls_mac_is_ok (algo) == 0)
-    {
-      session->security_parameters.read_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;
-    }
-
-
-  return 0;
+  _gnutls_cipher_deinit (&state->cipher_state);
 
+  if (state->compression_state != NULL)
+    _gnutls_comp_deinit (state->compression_state, read);
 }
 
-int
-_gnutls_set_write_mac (gnutls_session_t session, gnutls_mac_algorithm_t algo)
+void
+_gnutls_epoch_free (gnutls_session_t session, record_parameters_st *params)
 {
+  _gnutls_record_log
+    ("REC[%p]: Epoch #%u freed\n", session, params->epoch);
 
-  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;
-    }
-
-
-  return 0;
+  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_errors.h b/lib/gnutls_errors.h
index 8bc0c6f..7ed3da9 100644
--- a/lib/gnutls_errors.h
+++ b/lib/gnutls_errors.h
@@ -83,4 +83,21 @@ _gnutls_log (int, const char *fmt, ...)
 
 #endif /* C99_MACROS */
 
+/* GCC won't inline this by itself and results in a "fatal warning"
+   otherwise. Making this a macro has been tried, but it interacts
+   badly with the do..while in the expansion. Welcome to the dark
+   side. */
+static inline
+#ifdef __GNUC__
+ __attribute__ ((always_inline))
+#endif
+int
+gnutls_assert_val_int (int val, const char* file, int line)
+{
+  _gnutls_debug_log( "ASSERT: %s:%d\n", file, line);
+  return val;
+}
+
+#define gnutls_assert_val(x) gnutls_assert_val_int(x, __FILE__, __LINE__)
+
 #endif /* GNUTLS_ERRORS_H */
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 */
+
+  /* The epoch used to read and write */
+  uint16_t epoch_read;
+  uint16_t epoch_write;
 
-  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 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,29 +275,29 @@ _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;
 
-  (*session)->security_parameters.entity = con_end;
-
-  /* the default certificate type for TLS */
-  (*session)->security_parameters.cert_type = DEFAULT_CERT_TYPE;
+  ret = _gnutls_epoch_alloc (*session, 0, &epoch);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_MEMORY_ERROR;
+    }
 
-/* Set the defaults for initial handshake */
-  (*session)->security_parameters.read_bulk_cipher_algorithm =
-    (*session)->security_parameters.write_bulk_cipher_algorithm =
-    GNUTLS_CIPHER_NULL;
+  /* Set all NULL algos on epoch 0 */
+  _gnutls_epoch_set_null_algos(*session, epoch);
 
-  (*session)->security_parameters.read_mac_algorithm =
-    (*session)->security_parameters.write_mac_algorithm = GNUTLS_MAC_NULL;
+  (*session)->security_parameters.epoch_next = 1;
 
-  (*session)->security_parameters.read_compression_algorithm =
-    GNUTLS_COMP_NULL;
-  (*session)->security_parameters.write_compression_algorithm =
-    GNUTLS_COMP_NULL;
+  (*session)->security_parameters.entity = con_end;
 
-  (*session)->internals.enable_private = 0;
+  /* the default certificate type for TLS */
+  (*session)->security_parameters.cert_type = DEFAULT_CERT_TYPE;
 
   /* Initialize buffers */
   _gnutls_buffer_init (&(*session)->internals.application_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);


hooks/post-receive
-- 
GNU gnutls



reply via email to

[Prev in Thread] Current Thread [Next in Thread]