Tizen 2.0 Release
[external/libgnutls26.git] / lib / gnutls_constate.c
1 /*
2  * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2008, 2010 Free
3  * Software Foundation, Inc.
4  *
5  * Author: Nikos Mavrogiannopoulos
6  *
7  * This file is part of GnuTLS.
8  *
9  * The GnuTLS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1 of
12  * the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
22  * USA
23  *
24  */
25
26 /* Functions that are supposed to run after the handshake procedure is
27  * finished. These functions activate the established security parameters.
28  */
29
30 #include <gnutls_int.h>
31 #include <gnutls_constate.h>
32 #include <gnutls_errors.h>
33 #include <gnutls_kx.h>
34 #include <gnutls_algorithms.h>
35 #include <gnutls_num.h>
36 #include <gnutls_datum.h>
37 #include <gnutls_state.h>
38 #include <gnutls_extensions.h>
39 #include <gnutls_buffers.h>
40
41 static const char keyexp[] = "key expansion";
42 static const int keyexp_length = sizeof (keyexp) - 1;
43
44 static const char ivblock[] = "IV block";
45 static const int ivblock_length = sizeof (ivblock) - 1;
46
47 static const char cliwrite[] = "client write key";
48 static const int cliwrite_length = sizeof (cliwrite) - 1;
49
50 static const char servwrite[] = "server write key";
51 static const int servwrite_length = sizeof (servwrite) - 1;
52
53 #define EXPORT_FINAL_KEY_SIZE 16
54
55 /* This function is to be called after handshake, when master_secret,
56  *  client_random and server_random have been initialized. 
57  * This function creates the keys and stores them into pending session.
58  * (session->cipher_specs)
59  */
60 static int
61 _gnutls_set_keys (gnutls_session_t session, record_parameters_st * params,
62                   int hash_size, int IV_size, int key_size, int export_flag)
63 {
64   /* FIXME: This function is too long
65    */
66   opaque rnd[2 * GNUTLS_RANDOM_SIZE];
67   opaque rrnd[2 * GNUTLS_RANDOM_SIZE];
68   int pos, ret;
69   int block_size;
70   char buf[65];
71   /* avoid using malloc */
72   opaque key_block[2 * MAX_HASH_SIZE + 2 * MAX_CIPHER_KEY_SIZE +
73                    2 * MAX_CIPHER_BLOCK_SIZE];
74   record_state_st *client_write, *server_write;
75
76   client_write =
77     session->security_parameters.entity ==
78     GNUTLS_CLIENT ? &params->write : &params->read;
79   server_write =
80     session->security_parameters.entity ==
81     GNUTLS_SERVER ? &params->write : &params->read;
82
83   block_size = 2 * hash_size + 2 * key_size;
84   if (export_flag == 0)
85     block_size += 2 * IV_size;
86
87   memcpy (rnd, session->security_parameters.server_random,
88           GNUTLS_RANDOM_SIZE);
89   memcpy (&rnd[GNUTLS_RANDOM_SIZE],
90           session->security_parameters.client_random, GNUTLS_RANDOM_SIZE);
91
92   memcpy (rrnd, session->security_parameters.client_random,
93           GNUTLS_RANDOM_SIZE);
94   memcpy (&rrnd[GNUTLS_RANDOM_SIZE],
95           session->security_parameters.server_random, GNUTLS_RANDOM_SIZE);
96
97   if (session->security_parameters.version == GNUTLS_SSL3)
98     {                           /* SSL 3 */
99       ret =
100         _gnutls_ssl3_generate_random
101         (session->security_parameters.master_secret, GNUTLS_MASTER_SIZE, rnd,
102          2 * GNUTLS_RANDOM_SIZE, block_size, key_block);
103     }
104   else
105     {                           /* TLS 1.0 */
106       ret =
107         _gnutls_PRF (session, session->security_parameters.master_secret,
108                      GNUTLS_MASTER_SIZE, keyexp, keyexp_length,
109                      rnd, 2 * GNUTLS_RANDOM_SIZE, block_size, key_block);
110     }
111
112   if (ret < 0)
113     return gnutls_assert_val (ret);
114
115   _gnutls_hard_log ("INT: KEY BLOCK[%d]: %s\n", block_size,
116                     _gnutls_bin2hex (key_block, block_size, buf,
117                                      sizeof (buf), NULL));
118
119   pos = 0;
120   if (hash_size > 0)
121     {
122
123       if (_gnutls_sset_datum
124           (&client_write->mac_secret, &key_block[pos], hash_size) < 0)
125         return gnutls_assert_val (GNUTLS_E_MEMORY_ERROR);
126
127       pos += hash_size;
128
129       if (_gnutls_sset_datum
130           (&server_write->mac_secret, &key_block[pos], hash_size) < 0)
131         return gnutls_assert_val (GNUTLS_E_MEMORY_ERROR);
132
133       pos += hash_size;
134     }
135
136   if (key_size > 0)
137     {
138       opaque key1[EXPORT_FINAL_KEY_SIZE];
139       opaque key2[EXPORT_FINAL_KEY_SIZE];
140       opaque *client_write_key, *server_write_key;
141       int client_write_key_size, server_write_key_size;
142
143       if (export_flag == 0)
144         {
145           client_write_key = &key_block[pos];
146           client_write_key_size = key_size;
147
148           pos += key_size;
149
150           server_write_key = &key_block[pos];
151           server_write_key_size = key_size;
152
153           pos += key_size;
154
155         }
156       else
157         {                       /* export */
158           client_write_key = key1;
159           server_write_key = key2;
160
161           /* generate the final keys */
162
163           if (session->security_parameters.version == GNUTLS_SSL3)
164             {                   /* SSL 3 */
165               ret =
166                 _gnutls_ssl3_hash_md5 (&key_block[pos],
167                                        key_size, rrnd,
168                                        2 * GNUTLS_RANDOM_SIZE,
169                                        EXPORT_FINAL_KEY_SIZE,
170                                        client_write_key);
171
172             }
173           else
174             {                   /* TLS 1.0 */
175               ret =
176                 _gnutls_PRF (session, &key_block[pos], key_size,
177                              cliwrite, cliwrite_length,
178                              rrnd,
179                              2 * GNUTLS_RANDOM_SIZE,
180                              EXPORT_FINAL_KEY_SIZE, client_write_key);
181             }
182
183           if (ret < 0)
184             return gnutls_assert_val (ret);
185
186           client_write_key_size = EXPORT_FINAL_KEY_SIZE;
187           pos += key_size;
188
189           if (session->security_parameters.version == GNUTLS_SSL3)
190             {                   /* SSL 3 */
191               ret =
192                 _gnutls_ssl3_hash_md5 (&key_block[pos], key_size,
193                                        rnd, 2 * GNUTLS_RANDOM_SIZE,
194                                        EXPORT_FINAL_KEY_SIZE,
195                                        server_write_key);
196             }
197           else
198             {                   /* TLS 1.0 */
199               ret =
200                 _gnutls_PRF (session, &key_block[pos], key_size,
201                              servwrite, servwrite_length,
202                              rrnd, 2 * GNUTLS_RANDOM_SIZE,
203                              EXPORT_FINAL_KEY_SIZE, server_write_key);
204             }
205
206           if (ret < 0)
207             return gnutls_assert_val (ret);
208
209           server_write_key_size = EXPORT_FINAL_KEY_SIZE;
210           pos += key_size;
211         }
212
213       if (_gnutls_sset_datum
214           (&client_write->key, client_write_key, client_write_key_size) < 0)
215         return gnutls_assert_val (GNUTLS_E_MEMORY_ERROR);
216
217       _gnutls_hard_log ("INT: CLIENT WRITE KEY [%d]: %s\n",
218                         client_write_key_size,
219                         _gnutls_bin2hex (client_write_key,
220                                          client_write_key_size, buf,
221                                          sizeof (buf), NULL));
222
223       if (_gnutls_sset_datum
224           (&server_write->key, server_write_key, server_write_key_size) < 0)
225         return gnutls_assert_val (GNUTLS_E_MEMORY_ERROR);
226
227       _gnutls_hard_log ("INT: SERVER WRITE KEY [%d]: %s\n",
228                         server_write_key_size,
229                         _gnutls_bin2hex (server_write_key,
230                                          server_write_key_size, buf,
231                                          sizeof (buf), NULL));
232
233     }
234
235
236   /* IV generation in export and non export ciphers.
237    */
238   if (IV_size > 0 && export_flag == 0)
239     {
240       if (_gnutls_sset_datum
241           (&client_write->IV, &key_block[pos], IV_size) < 0)
242         return gnutls_assert_val (GNUTLS_E_MEMORY_ERROR);
243
244       pos += IV_size;
245
246       if (_gnutls_sset_datum
247           (&server_write->IV, &key_block[pos], IV_size) < 0)
248         return gnutls_assert_val (GNUTLS_E_MEMORY_ERROR);
249
250       pos += IV_size;
251
252     }
253   else if (IV_size > 0 && export_flag != 0)
254     {
255       opaque iv_block[MAX_CIPHER_BLOCK_SIZE * 2];
256
257       if (session->security_parameters.version == GNUTLS_SSL3)
258         {                       /* SSL 3 */
259           ret = _gnutls_ssl3_hash_md5 ("", 0,
260                                        rrnd, GNUTLS_RANDOM_SIZE * 2,
261                                        IV_size, iv_block);
262
263           if (ret < 0)
264             return gnutls_assert_val (ret);
265
266
267           ret = _gnutls_ssl3_hash_md5 ("", 0, rnd,
268                                        GNUTLS_RANDOM_SIZE * 2,
269                                        IV_size, &iv_block[IV_size]);
270
271         }
272       else
273         {                       /* TLS 1.0 */
274           ret = _gnutls_PRF (session, "", 0,
275                              ivblock, ivblock_length, rrnd,
276                              2 * GNUTLS_RANDOM_SIZE, IV_size * 2, iv_block);
277         }
278
279       if (ret < 0)
280         return gnutls_assert_val (ret);
281
282       if (_gnutls_sset_datum (&client_write->IV, iv_block, IV_size) < 0)
283         return gnutls_assert_val (GNUTLS_E_MEMORY_ERROR);
284
285       if (_gnutls_sset_datum
286           (&server_write->IV, &iv_block[IV_size], IV_size) < 0)
287         return gnutls_assert_val (GNUTLS_E_MEMORY_ERROR);
288     }
289
290   return 0;
291 }
292
293 static int
294 _gnutls_init_record_state (record_parameters_st * params, int read,
295                            record_state_st * state)
296 {
297   int ret;
298
299   ret = _gnutls_cipher_init (&state->cipher_state,
300                              params->cipher_algorithm,
301                              &state->key, &state->IV);
302   if (ret < 0 && params->cipher_algorithm != GNUTLS_CIPHER_NULL)
303     return gnutls_assert_val (ret);
304
305   state->compression_state =
306     _gnutls_comp_init (params->compression_algorithm, read);
307
308   if (state->compression_state == GNUTLS_COMP_FAILED)
309     return gnutls_assert_val (GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM);
310
311   return 0;
312 }
313
314 int
315 _gnutls_epoch_set_cipher_suite (gnutls_session_t session,
316                                 int epoch_rel, cipher_suite_st * suite)
317 {
318   gnutls_cipher_algorithm_t cipher_algo;
319   gnutls_mac_algorithm_t mac_algo;
320   record_parameters_st *params;
321   int ret;
322
323   ret = _gnutls_epoch_get (session, epoch_rel, &params);
324   if (ret < 0)
325     return gnutls_assert_val (ret);
326
327   if (params->initialized
328       || params->cipher_algorithm != GNUTLS_CIPHER_UNKNOWN
329       || params->mac_algorithm != GNUTLS_MAC_UNKNOWN)
330     return gnutls_assert_val (GNUTLS_E_INTERNAL_ERROR);
331
332   cipher_algo = _gnutls_cipher_suite_get_cipher_algo (suite);
333   mac_algo = _gnutls_cipher_suite_get_mac_algo (suite);
334
335   if (_gnutls_cipher_is_ok (cipher_algo) != 0
336       || _gnutls_mac_is_ok (mac_algo) != 0)
337     return gnutls_assert_val (GNUTLS_E_UNWANTED_ALGORITHM);
338
339   params->cipher_algorithm = cipher_algo;
340   params->mac_algorithm = mac_algo;
341
342   return 0;
343 }
344
345 int
346 _gnutls_epoch_set_compression (gnutls_session_t session,
347                                int epoch_rel,
348                                gnutls_compression_method_t comp_algo)
349 {
350   record_parameters_st *params;
351   int ret;
352
353   ret = _gnutls_epoch_get (session, epoch_rel, &params);
354   if (ret < 0)
355     return gnutls_assert_val (ret);
356
357   if (params->initialized
358       || params->compression_algorithm != GNUTLS_COMP_UNKNOWN)
359     return gnutls_assert_val (GNUTLS_E_INTERNAL_ERROR);
360
361   if (_gnutls_compression_is_ok (comp_algo) != 0)
362     return gnutls_assert_val (GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM);
363
364   params->compression_algorithm = comp_algo;
365
366   return 0;
367 }
368
369 void
370 _gnutls_epoch_set_null_algos (gnutls_session_t session,
371                               record_parameters_st * params)
372 {
373   /* This is only called on startup. We are extra paranoid about this
374      because it may cause unencrypted application data to go out on
375      the wire. */
376   if (params->initialized || params->epoch != 0)
377     {
378       gnutls_assert ();
379       return;
380     }
381
382   params->cipher_algorithm = GNUTLS_CIPHER_NULL;
383   params->mac_algorithm = GNUTLS_MAC_NULL;
384   params->compression_algorithm = GNUTLS_COMP_NULL;
385   params->initialized = 1;
386 }
387
388 int
389 _gnutls_epoch_set_keys (gnutls_session_t session, uint16_t epoch)
390 {
391   int hash_size;
392   int IV_size;
393   int key_size, export_flag;
394   gnutls_cipher_algorithm_t cipher_algo;
395   gnutls_mac_algorithm_t mac_algo;
396   gnutls_compression_method_t comp_algo;
397   record_parameters_st *params;
398   int ret;
399
400   ret = _gnutls_epoch_get (session, epoch, &params);
401   if (ret < 0)
402     return gnutls_assert_val (ret);
403
404   if (params->initialized)
405     return 0;
406
407   _gnutls_record_log
408     ("REC[%p]: Initializing epoch #%u\n", session, params->epoch);
409
410   cipher_algo = params->cipher_algorithm;
411   mac_algo = params->mac_algorithm;
412   comp_algo = params->compression_algorithm;
413
414   if (_gnutls_cipher_is_ok (cipher_algo) != 0
415       || _gnutls_mac_is_ok (mac_algo) != 0)
416     return gnutls_assert_val (GNUTLS_E_INTERNAL_ERROR);
417
418   if (_gnutls_compression_is_ok (comp_algo) != 0)
419     return gnutls_assert_val (GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM);
420
421   IV_size = _gnutls_cipher_get_iv_size (cipher_algo);
422   key_size = gnutls_cipher_get_key_size (cipher_algo);
423   export_flag = _gnutls_cipher_get_export_flag (cipher_algo);
424   hash_size = _gnutls_hash_get_algo_len (mac_algo);
425
426   ret = _gnutls_set_keys
427     (session, params, hash_size, IV_size, key_size, export_flag);
428   if (ret < 0)
429     return gnutls_assert_val (ret);
430
431   ret = _gnutls_init_record_state (params, 1, &params->read);
432   if (ret < 0)
433     return gnutls_assert_val (ret);
434
435   ret = _gnutls_init_record_state (params, 0, &params->write);
436   if (ret < 0)
437     return gnutls_assert_val (ret);
438
439   _gnutls_record_log ("REC[%p]: Epoch #%u ready\n", session, params->epoch);
440
441   params->initialized = 1;
442   return 0;
443 }
444
445
446 #define CPY_COMMON dst->entity = src->entity; \
447         dst->kx_algorithm = src->kx_algorithm; \
448         memcpy( &dst->current_cipher_suite, &src->current_cipher_suite, sizeof(cipher_suite_st)); \
449         memcpy( dst->master_secret, src->master_secret, GNUTLS_MASTER_SIZE); \
450         memcpy( dst->client_random, src->client_random, GNUTLS_RANDOM_SIZE); \
451         memcpy( dst->server_random, src->server_random, GNUTLS_RANDOM_SIZE); \
452         memcpy( dst->session_id, src->session_id, TLS_MAX_SESSION_ID_SIZE); \
453         dst->session_id_size = src->session_id_size; \
454         dst->cert_type = src->cert_type; \
455         dst->timestamp = src->timestamp; \
456         dst->max_record_recv_size = src->max_record_recv_size; \
457         dst->max_record_send_size = src->max_record_send_size; \
458         dst->version = src->version
459
460 static void
461 _gnutls_set_resumed_parameters (gnutls_session_t session)
462 {
463   security_parameters_st *src =
464     &session->internals.resumed_security_parameters;
465   security_parameters_st *dst = &session->security_parameters;
466
467   CPY_COMMON;
468 }
469
470 /* Sets the current connection session to conform with the
471  * Security parameters(pending session), and initializes encryption.
472  * Actually it initializes and starts encryption ( so it needs
473  * secrets and random numbers to have been negotiated)
474  * This is to be called after sending the Change Cipher Spec packet.
475  */
476 int
477 _gnutls_connection_state_init (gnutls_session_t session)
478 {
479   int ret;
480
481 /* Setup the master secret 
482  */
483   if ((ret = _gnutls_generate_master (session, 0)) < 0)
484     return gnutls_assert_val (ret);
485
486   return 0;
487 }
488
489
490
491 static int
492 _gnutls_check_algos (gnutls_session_t session,
493                      cipher_suite_st * suite,
494                      gnutls_compression_method_t comp_algo)
495 {
496   gnutls_cipher_algorithm_t cipher_algo;
497   gnutls_mac_algorithm_t mac_algo;
498
499   cipher_algo = _gnutls_cipher_suite_get_cipher_algo (suite);
500   mac_algo = _gnutls_cipher_suite_get_mac_algo (suite);
501
502   if (_gnutls_cipher_is_ok (cipher_algo) != 0)
503     return gnutls_assert_val (GNUTLS_E_INTERNAL_ERROR);
504
505   if (_gnutls_cipher_priority (session, cipher_algo) < 0)
506     return gnutls_assert_val (GNUTLS_E_UNWANTED_ALGORITHM);
507
508
509   if (_gnutls_mac_is_ok (mac_algo) != 0)
510     return gnutls_assert_val (GNUTLS_E_INTERNAL_ERROR);
511
512   if (_gnutls_mac_priority (session, mac_algo) < 0)
513     return gnutls_assert_val (GNUTLS_E_UNWANTED_ALGORITHM);
514
515
516   if (_gnutls_compression_is_ok (comp_algo) != 0)
517     return gnutls_assert_val (GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM);
518
519   return 0;
520 }
521
522 /* Initializes the read connection session
523  * (read encrypted data)
524  */
525 int
526 _gnutls_read_connection_state_init (gnutls_session_t session)
527 {
528   const uint16_t epoch_next = session->security_parameters.epoch_next;
529   int ret;
530
531   /* Update internals from CipherSuite selected.
532    * If we are resuming just copy the connection session
533    */
534   if (session->internals.resumed == RESUME_FALSE)
535     {
536       ret = _gnutls_check_algos (session,
537                                  &session->
538                                  security_parameters.current_cipher_suite,
539                                  session->internals.compression_method);
540       if (ret < 0)
541         return ret;
542
543       ret = _gnutls_set_kx (session,
544                             _gnutls_cipher_suite_get_kx_algo
545                             (&session->
546                              security_parameters.current_cipher_suite));
547       if (ret < 0)
548         return ret;
549     }
550   else if (session->security_parameters.entity == GNUTLS_CLIENT)
551     _gnutls_set_resumed_parameters (session);
552
553   ret = _gnutls_epoch_set_keys (session, epoch_next);
554   if (ret < 0)
555     return ret;
556
557   _gnutls_handshake_log ("HSK[%p]: Cipher Suite: %s\n",
558                          session,
559                          _gnutls_cipher_suite_get_name
560                          (&session->
561                           security_parameters.current_cipher_suite));
562
563   session->security_parameters.epoch_read = epoch_next;
564   _gnutls_epoch_gc (session);
565
566   return 0;
567 }
568
569
570
571 /* Initializes the write connection session
572  * (write encrypted data)
573  */
574 int
575 _gnutls_write_connection_state_init (gnutls_session_t session)
576 {
577   const uint16_t epoch_next = session->security_parameters.epoch_next;
578   int ret;
579
580 /* Update internals from CipherSuite selected.
581  * If we are resuming just copy the connection session
582  */
583   if (session->internals.resumed == RESUME_FALSE)
584     {
585       ret = _gnutls_check_algos (session,
586                                  &session->
587                                  security_parameters.current_cipher_suite,
588                                  session->internals.compression_method);
589       if (ret < 0)
590         return ret;
591
592       ret = _gnutls_set_kx (session,
593                             _gnutls_cipher_suite_get_kx_algo
594                             (&session->
595                              security_parameters.current_cipher_suite));
596       if (ret < 0)
597         return ret;
598     }
599   else if (session->security_parameters.entity == GNUTLS_SERVER)
600     _gnutls_set_resumed_parameters (session);
601
602   ret = _gnutls_epoch_set_keys (session, epoch_next);
603   if (ret < 0)
604     return gnutls_assert_val (ret);
605
606   _gnutls_handshake_log ("HSK[%p]: Cipher Suite: %s\n", session,
607                          _gnutls_cipher_suite_get_name
608                          (&session->
609                           security_parameters.current_cipher_suite));
610
611   _gnutls_handshake_log
612     ("HSK[%p]: Initializing internal [write] cipher sessions\n", session);
613
614   session->security_parameters.epoch_write = epoch_next;
615   _gnutls_epoch_gc (session);
616
617   return 0;
618 }
619
620 /* Sets the specified kx algorithm into pending session
621  */
622 int
623 _gnutls_set_kx (gnutls_session_t session, gnutls_kx_algorithm_t algo)
624 {
625
626   if (_gnutls_kx_is_ok (algo) == 0)
627     {
628       session->security_parameters.kx_algorithm = algo;
629     }
630   else
631     return gnutls_assert_val (GNUTLS_E_INTERNAL_ERROR);
632
633   if (_gnutls_kx_priority (session, algo) < 0)
634     return gnutls_assert_val (GNUTLS_E_UNWANTED_ALGORITHM);
635
636   return 0;
637 }
638
639 static inline int
640 epoch_resolve (gnutls_session_t session,
641                unsigned int epoch_rel, uint16_t * epoch_out)
642 {
643   switch (epoch_rel)
644     {
645     case EPOCH_READ_CURRENT:
646       *epoch_out = session->security_parameters.epoch_read;
647       return 0;
648
649     case EPOCH_WRITE_CURRENT:
650       *epoch_out = session->security_parameters.epoch_write;
651       return 0;
652
653     case EPOCH_NEXT:
654       *epoch_out = session->security_parameters.epoch_next;
655       return 0;
656
657     default:
658       if (epoch_rel > 0xffffu)
659         return gnutls_assert_val (GNUTLS_E_INVALID_REQUEST);
660
661       *epoch_out = epoch_rel;
662       return 0;
663     }
664 }
665
666 static inline record_parameters_st **
667 epoch_get_slot (gnutls_session_t session, uint16_t epoch)
668 {
669   uint16_t epoch_index = epoch - session->security_parameters.epoch_min;
670
671   if (epoch_index >= MAX_EPOCH_INDEX)
672     {
673       gnutls_assert ();
674       return NULL;
675     }
676
677   /* The slot may still be empty (NULL) */
678   return &session->record_parameters[epoch_index];
679 }
680
681 int
682 _gnutls_epoch_get (gnutls_session_t session, unsigned int epoch_rel,
683                    record_parameters_st ** params_out)
684 {
685   uint16_t epoch;
686   record_parameters_st **params;
687   int ret;
688
689   ret = epoch_resolve (session, epoch_rel, &epoch);
690   if (ret < 0)
691     return gnutls_assert_val (ret);
692
693   params = epoch_get_slot (session, epoch);
694   if (params == NULL || *params == NULL)
695     return gnutls_assert_val (GNUTLS_E_INVALID_REQUEST);
696
697   *params_out = *params;
698
699   return 0;
700 }
701
702 int
703 _gnutls_epoch_alloc (gnutls_session_t session, uint16_t epoch,
704                      record_parameters_st ** out)
705 {
706   record_parameters_st **slot;
707
708   _gnutls_record_log ("REC[%p]: Allocating epoch #%u\n", session, epoch);
709
710   slot = epoch_get_slot (session, epoch);
711
712   /* If slot out of range or not empty. */
713   if (slot == NULL)
714     return gnutls_assert_val (GNUTLS_E_INVALID_REQUEST);
715
716   if (*slot != NULL)
717     return gnutls_assert_val (GNUTLS_E_INVALID_REQUEST);
718
719   *slot = gnutls_calloc (1, sizeof (record_parameters_st));
720   if (*slot == NULL)
721     return gnutls_assert_val (GNUTLS_E_MEMORY_ERROR);
722
723   (*slot)->epoch = epoch;
724   (*slot)->cipher_algorithm = GNUTLS_CIPHER_UNKNOWN;
725   (*slot)->mac_algorithm = GNUTLS_MAC_UNKNOWN;
726   (*slot)->compression_algorithm = GNUTLS_COMP_UNKNOWN;
727
728   if (out != NULL)
729     *out = *slot;
730
731   return 0;
732 }
733
734 static inline int
735 epoch_alive (gnutls_session_t session, record_parameters_st * params)
736 {
737   const security_parameters_st *sp = &session->security_parameters;
738
739   /* DTLS will, in addition, need to check the epoch timeout value. */
740   return (params->epoch == sp->epoch_read
741           || params->epoch == sp->epoch_write
742           || params->epoch == sp->epoch_next);
743 }
744
745 void
746 _gnutls_epoch_gc (gnutls_session_t session)
747 {
748   int i, j;
749   unsigned int min_index = 0;
750
751   _gnutls_record_log ("REC[%p]: Start of epoch cleanup\n", session);
752
753   /* Free all dead cipher state */
754   for (i = 0; i < MAX_EPOCH_INDEX; i++)
755     if (session->record_parameters[i] != NULL
756         && !epoch_alive (session, session->record_parameters[i]))
757       {
758         _gnutls_epoch_free (session, session->record_parameters[i]);
759         session->record_parameters[i] = NULL;
760       }
761
762   /* Look for contiguous NULLs at the start of the array */
763   for (i = 0; i < MAX_EPOCH_INDEX && session->record_parameters[i] == NULL;
764        i++);
765   min_index = i;
766
767   /* Pick up the slack in the epoch window. */
768   for (i = 0, j = min_index; j < MAX_EPOCH_INDEX; i++, j++)
769     session->record_parameters[i] = session->record_parameters[j];
770
771   /* Set the new epoch_min */
772   if (session->record_parameters[0] != NULL)
773     session->security_parameters.epoch_min =
774       session->record_parameters[0]->epoch;
775
776   _gnutls_record_log ("REC[%p]: End of epoch cleanup\n", session);
777 }
778
779 static inline void
780 free_record_state (record_state_st * state, int read)
781 {
782   _gnutls_free_datum (&state->mac_secret);
783   _gnutls_free_datum (&state->IV);
784   _gnutls_free_datum (&state->key);
785
786   _gnutls_cipher_deinit (&state->cipher_state);
787
788   if (state->compression_state != NULL)
789     _gnutls_comp_deinit (state->compression_state, read);
790 }
791
792 void
793 _gnutls_epoch_free (gnutls_session_t session, record_parameters_st * params)
794 {
795   _gnutls_record_log ("REC[%p]: Epoch #%u freed\n", session, params->epoch);
796
797   free_record_state (&params->read, 1);
798   free_record_state (&params->write, 0);
799
800   gnutls_free (params);
801 }