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