/*
* Copyright (C) 2001-2012 Free Software Foundation, Inc.
*
* Author: Nikos Mavrogiannopoulos
*
* This file is part of GnuTLS.
*
* The GnuTLS is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see
*
*/
/* Functions that are supposed to run after the handshake procedure is
* finished. These functions activate the established security parameters.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static const char keyexp[] = "key expansion";
static const int keyexp_length = sizeof (keyexp) - 1;
static const char ivblock[] = "IV block";
static const int ivblock_length = sizeof (ivblock) - 1;
static const char cliwrite[] = "client write key";
static const int cliwrite_length = sizeof (cliwrite) - 1;
static const char servwrite[] = "server write key";
static const int servwrite_length = sizeof (servwrite) - 1;
#define EXPORT_FINAL_KEY_SIZE 16
/* This function is to be called after handshake, when master_secret,
* client_random and server_random have been initialized.
* This function creates the keys and stores them into pending session.
* (session->cipher_specs)
*/
static int
_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
*/
uint8_t rnd[2 * GNUTLS_RANDOM_SIZE];
uint8_t rrnd[2 * GNUTLS_RANDOM_SIZE];
int pos, ret;
int block_size;
char buf[65];
/* avoid using malloc */
uint8_t 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->security_parameters.entity == GNUTLS_CLIENT)
{
client_write = ¶ms->write;
server_write = ¶ms->read;
}
else
{
client_write = ¶ms->read;
server_write = ¶ms->write;
}
block_size = 2 * hash_size + 2 * key_size;
if (export_flag == 0)
block_size += 2 * IV_size;
memcpy (rnd, session->security_parameters.server_random,
GNUTLS_RANDOM_SIZE);
memcpy (&rnd[GNUTLS_RANDOM_SIZE],
session->security_parameters.client_random, GNUTLS_RANDOM_SIZE);
memcpy (rrnd, session->security_parameters.client_random,
GNUTLS_RANDOM_SIZE);
memcpy (&rrnd[GNUTLS_RANDOM_SIZE],
session->security_parameters.server_random, GNUTLS_RANDOM_SIZE);
if (session->security_parameters.version == GNUTLS_SSL3)
{ /* SSL 3 */
ret =
_gnutls_ssl3_generate_random
(session->security_parameters.master_secret, GNUTLS_MASTER_SIZE, rnd,
2 * GNUTLS_RANDOM_SIZE, block_size, key_block);
}
else
{ /* TLS 1.0 */
ret =
_gnutls_PRF (session, session->security_parameters.master_secret,
GNUTLS_MASTER_SIZE, keyexp, keyexp_length,
rnd, 2 * GNUTLS_RANDOM_SIZE, block_size, key_block);
}
if (ret < 0)
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));
pos = 0;
if (hash_size > 0)
{
if (_gnutls_set_datum
(&client_write->mac_secret, &key_block[pos], hash_size) < 0)
return gnutls_assert_val (GNUTLS_E_MEMORY_ERROR);
pos += hash_size;
if (_gnutls_set_datum
(&server_write->mac_secret, &key_block[pos], hash_size) < 0)
return gnutls_assert_val (GNUTLS_E_MEMORY_ERROR);
pos += hash_size;
}
if (key_size > 0)
{
uint8_t key1[EXPORT_FINAL_KEY_SIZE];
uint8_t key2[EXPORT_FINAL_KEY_SIZE];
uint8_t *client_write_key, *server_write_key;
int client_write_key_size, server_write_key_size;
if (export_flag == 0)
{
client_write_key = &key_block[pos];
client_write_key_size = key_size;
pos += key_size;
server_write_key = &key_block[pos];
server_write_key_size = key_size;
pos += key_size;
}
else
{ /* export */
client_write_key = key1;
server_write_key = key2;
/* generate the final keys */
if (session->security_parameters.version == GNUTLS_SSL3)
{ /* SSL 3 */
ret =
_gnutls_ssl3_hash_md5 (&key_block[pos],
key_size, rrnd,
2 * GNUTLS_RANDOM_SIZE,
EXPORT_FINAL_KEY_SIZE,
client_write_key);
}
else
{ /* TLS 1.0 */
ret =
_gnutls_PRF (session, &key_block[pos], key_size,
cliwrite, cliwrite_length,
rrnd,
2 * GNUTLS_RANDOM_SIZE,
EXPORT_FINAL_KEY_SIZE, client_write_key);
}
if (ret < 0)
return gnutls_assert_val (ret);
client_write_key_size = EXPORT_FINAL_KEY_SIZE;
pos += key_size;
if (session->security_parameters.version == GNUTLS_SSL3)
{ /* SSL 3 */
ret =
_gnutls_ssl3_hash_md5 (&key_block[pos], key_size,
rnd, 2 * GNUTLS_RANDOM_SIZE,
EXPORT_FINAL_KEY_SIZE,
server_write_key);
}
else
{ /* TLS 1.0 */
ret =
_gnutls_PRF (session, &key_block[pos], key_size,
servwrite, servwrite_length,
rrnd, 2 * GNUTLS_RANDOM_SIZE,
EXPORT_FINAL_KEY_SIZE, server_write_key);
}
if (ret < 0)
return gnutls_assert_val (ret);
server_write_key_size = EXPORT_FINAL_KEY_SIZE;
pos += key_size;
}
if (_gnutls_set_datum
(&client_write->key, client_write_key, client_write_key_size) < 0)
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,
client_write_key_size, buf,
sizeof (buf), NULL));
if (_gnutls_set_datum
(&server_write->key, server_write_key, server_write_key_size) < 0)
return gnutls_assert_val (GNUTLS_E_MEMORY_ERROR);
_gnutls_hard_log ("INT: SERVER WRITE KEY [%d]: %s\n",
server_write_key_size,
_gnutls_bin2hex (server_write_key,
server_write_key_size, buf,
sizeof (buf), NULL));
}
/* IV generation in export and non export ciphers.
*/
if (IV_size > 0 && export_flag == 0)
{
if (_gnutls_set_datum
(&client_write->IV, &key_block[pos], IV_size) < 0)
return gnutls_assert_val (GNUTLS_E_MEMORY_ERROR);
pos += IV_size;
if (_gnutls_set_datum
(&server_write->IV, &key_block[pos], IV_size) < 0)
return gnutls_assert_val (GNUTLS_E_MEMORY_ERROR);
}
else if (IV_size > 0 && export_flag != 0)
{
uint8_t iv_block[MAX_CIPHER_BLOCK_SIZE * 2];
if (session->security_parameters.version == GNUTLS_SSL3)
{ /* SSL 3 */
ret = _gnutls_ssl3_hash_md5 ("", 0,
rrnd, GNUTLS_RANDOM_SIZE * 2,
IV_size, iv_block);
if (ret < 0)
return gnutls_assert_val (ret);
ret = _gnutls_ssl3_hash_md5 ("", 0, rnd,
GNUTLS_RANDOM_SIZE * 2,
IV_size, &iv_block[IV_size]);
}
else
{ /* TLS 1.0 */
ret = _gnutls_PRF (session, (uint8_t*)"", 0,
ivblock, ivblock_length, rrnd,
2 * GNUTLS_RANDOM_SIZE, IV_size * 2, iv_block);
}
if (ret < 0)
return gnutls_assert_val (ret);
if (_gnutls_set_datum (&client_write->IV, iv_block, IV_size) < 0)
return gnutls_assert_val (GNUTLS_E_MEMORY_ERROR);
if (_gnutls_set_datum
(&server_write->IV, &iv_block[IV_size], IV_size) < 0)
return gnutls_assert_val (GNUTLS_E_MEMORY_ERROR);
}
return 0;
}
static int
_gnutls_init_record_state (record_parameters_st * params, gnutls_protocol_t ver, int read,
record_state_st * state)
{
int ret;
gnutls_datum_t * iv = NULL;
if (!_gnutls_version_has_explicit_iv(ver))
{
iv = &state->IV;
}
ret = _gnutls_auth_cipher_init (&state->cipher_state,
params->cipher_algorithm, &state->key, iv,
params->mac_algorithm, &state->mac_secret, (ver==GNUTLS_SSL3)?1:0, 1-read/*1==encrypt*/);
if (ret < 0 && params->cipher_algorithm != GNUTLS_CIPHER_NULL)
return gnutls_assert_val (ret);
ret =
_gnutls_comp_init (&state->compression_state, params->compression_algorithm, read/*1==decompress*/);
if (ret < 0)
return gnutls_assert_val (ret);
return 0;
}
int
_gnutls_epoch_set_cipher_suite (gnutls_session_t session,
int epoch_rel, const uint8_t suite[2])
{
gnutls_cipher_algorithm_t cipher_algo;
gnutls_mac_algorithm_t mac_algo;
record_parameters_st *params;
int ret;
ret = _gnutls_epoch_get (session, epoch_rel, ¶ms);
if (ret < 0)
return gnutls_assert_val (ret);
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);
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;
}
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, ¶ms);
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 cipher_algo;
gnutls_mac_algorithm_t mac_algo;
gnutls_compression_method_t comp_algo;
record_parameters_st *params;
int ret;
gnutls_protocol_t ver = gnutls_protocol_get_version (session);
ret = _gnutls_epoch_get (session, epoch, ¶ms);
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);
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_hmac_get_algo_len (mac_algo);
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, ver, 1, ¶ms->read);
if (ret < 0)
return gnutls_assert_val (ret);
ret = _gnutls_init_record_state (params, ver, 0, ¶ms->write);
if (ret < 0)
return gnutls_assert_val (ret);
params->record_sw_size = 0;
_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->cipher_suite, src->cipher_suite, 2); \
memcpy( dst->master_secret, src->master_secret, GNUTLS_MASTER_SIZE); \
memcpy( dst->client_random, src->client_random, GNUTLS_RANDOM_SIZE); \
memcpy( dst->server_random, src->server_random, GNUTLS_RANDOM_SIZE); \
memcpy( dst->session_id, src->session_id, TLS_MAX_SESSION_ID_SIZE); \
dst->session_id_size = src->session_id_size; \
dst->cert_type = src->cert_type; \
dst->compression_method = src->compression_method; \
dst->timestamp = src->timestamp; \
dst->max_record_recv_size = src->max_record_recv_size; \
dst->max_record_send_size = src->max_record_send_size; \
dst->version = src->version;
static void
_gnutls_set_resumed_parameters (gnutls_session_t session)
{
security_parameters_st *src =
&session->internals.resumed_security_parameters;
security_parameters_st *dst = &session->security_parameters;
CPY_COMMON;
}
/* Sets the current connection session to conform with the
* Security parameters(pending session), and initializes encryption.
* Actually it initializes and starts encryption ( so it needs
* secrets and random numbers to have been negotiated)
* This is to be called after sending the Change Cipher Spec packet.
*/
int
_gnutls_connection_state_init (gnutls_session_t session)
{
int ret;
/* Setup the master secret
*/
if ((ret = _gnutls_generate_master (session, 0)) < 0)
return gnutls_assert_val (ret);
return 0;
}
static int
_gnutls_check_algos (gnutls_session_t session,
const uint8_t suite[2],
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;
}
int _gnutls_epoch_get_compression(gnutls_session_t session, int epoch)
{
record_parameters_st *params;
int ret;
ret = _gnutls_epoch_get (session, epoch, ¶ms);
if (ret < 0)
return GNUTLS_COMP_UNKNOWN;
return params->compression_algorithm;
}
/* Initializes the read connection session
* (read encrypted data)
*/
int
_gnutls_read_connection_state_init (gnutls_session_t session)
{
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)
{
ret = _gnutls_check_algos (session,
session->
security_parameters.cipher_suite,
_gnutls_epoch_get_compression(session, epoch_next));
if (ret < 0)
return ret;
ret = _gnutls_set_kx (session,
_gnutls_cipher_suite_get_kx_algo
(session->
security_parameters.cipher_suite));
if (ret < 0)
return ret;
}
else if (session->security_parameters.entity == GNUTLS_CLIENT)
_gnutls_set_resumed_parameters (session);
ret = _gnutls_epoch_set_keys (session, epoch_next);
if (ret < 0)
return ret;
_gnutls_handshake_log ("HSK[%p]: Cipher Suite: %s\n",
session,
_gnutls_cipher_suite_get_name
(session->
security_parameters.cipher_suite));
session->security_parameters.epoch_read = epoch_next;
return 0;
}
/* Initializes the write connection session
* (write encrypted data)
*/
int
_gnutls_write_connection_state_init (gnutls_session_t session)
{
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)
{
ret = _gnutls_check_algos (session,
session->
security_parameters.cipher_suite,
_gnutls_epoch_get_compression(session, epoch_next));
if (ret < 0)
return ret;
ret = _gnutls_set_kx (session,
_gnutls_cipher_suite_get_kx_algo
(session->
security_parameters.cipher_suite));
if (ret < 0)
return ret;
}
else if (session->security_parameters.entity == GNUTLS_SERVER)
_gnutls_set_resumed_parameters (session);
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.cipher_suite));
_gnutls_handshake_log
("HSK[%p]: Initializing internal [write] cipher sessions\n", session);
session->security_parameters.epoch_write = epoch_next;
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);
if (_gnutls_kx_priority (session, algo) < 0)
return gnutls_assert_val (GNUTLS_E_UNWANTED_ALGORITHM);
return 0;
}
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;
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;
}
}
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_handshake_log("Epoch %d out of range (idx: %d, max: %d)\n", (int)epoch, (int)epoch_index, MAX_EPOCH_INDEX);
gnutls_assert ();
return NULL;
}
/* The slot may still be empty (NULL) */
return &session->record_parameters[epoch_index];
}
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_epoch_alloc (gnutls_session_t session, uint16_t epoch,
record_parameters_st ** out)
{
record_parameters_st **slot;
_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 (IS_DTLS (session))
_gnutls_write_uint16 (epoch, UINT64DATA((*slot)->write.sequence_number));
if (out != NULL)
*out = *slot;
return 0;
}
static inline int
epoch_is_active(gnutls_session_t session, record_parameters_st * params)
{
const security_parameters_st *sp = &session->security_parameters;
if (params->epoch == sp->epoch_read)
return 1;
if (params->epoch == sp->epoch_write)
return 1;
if (params->epoch == sp->epoch_next)
return 1;
return 0;
}
static inline int
epoch_alive (gnutls_session_t session, record_parameters_st * params)
{
if (params->usage_cnt > 0)
return 1;
return epoch_is_active(session, params);
}
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)
{
if (!epoch_is_active(session, session->record_parameters[i]) && session->record_parameters[i]->usage_cnt)
_gnutls_record_log ("REC[%p]: Note inactive epoch %d has %d users\n", session, session->record_parameters[i]->epoch, session->record_parameters[i]->usage_cnt);
if (!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 d)
{
_gnutls_free_datum (&state->mac_secret);
_gnutls_free_datum (&state->IV);
_gnutls_free_datum (&state->key);
_gnutls_auth_cipher_deinit (&state->cipher_state);
if (state->compression_state.handle != NULL)
_gnutls_comp_deinit (&state->compression_state, d);
}
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 (¶ms->read, 1);
free_record_state (¶ms->write, 0);
gnutls_free (params);
}