documented fix
[gnutls.git] / lib / ext / session_ticket.c
blobc94242cac2b449852f5161221568a8012cfa1c94
1 /*
2 * Copyright (C) 2009-2012 Free Software Foundation, Inc.
4 * Author: Daiki Ueno
6 * This file is part of GnuTLS.
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 3 of
11 * the License, or (at your option) any later version.
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.
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/>
23 #include <gnutls_int.h>
24 #include <gnutls_errors.h>
25 #include <gnutls_datum.h>
26 #include <algorithms.h>
27 #include <gnutls_handshake.h>
28 #include <gnutls_num.h>
29 #include <gnutls_constate.h>
30 #include <gnutls_session_pack.h>
31 #include <random.h>
32 #include <ext/session_ticket.h>
33 #include <gnutls_mbuffers.h>
34 #include <gnutls_extensions.h>
35 #include <gnutls_constate.h>
37 #define KEY_NAME_SIZE SESSION_TICKET_KEY_NAME_SIZE
38 #define KEY_SIZE SESSION_TICKET_KEY_SIZE
39 #define IV_SIZE SESSION_TICKET_IV_SIZE
40 #define MAC_SECRET_SIZE SESSION_TICKET_MAC_SECRET_SIZE
42 #define MAC_SIZE 32
44 static int session_ticket_recv_params (gnutls_session_t session,
45 const uint8_t * data, size_t data_size);
46 static int session_ticket_send_params (gnutls_session_t session,
47 gnutls_buffer_st* extdata);
48 static int session_ticket_unpack (gnutls_buffer_st * ps,
49 extension_priv_data_t * _priv);
50 static int session_ticket_pack (extension_priv_data_t _priv,
51 gnutls_buffer_st * ps);
52 static void session_ticket_deinit_data (extension_priv_data_t priv);
54 extension_entry_st ext_mod_session_ticket = {
55 .name = "SESSION TICKET",
56 .type = GNUTLS_EXTENSION_SESSION_TICKET,
57 .parse_type = GNUTLS_EXT_TLS,
59 .recv_func = session_ticket_recv_params,
60 .send_func = session_ticket_send_params,
61 .pack_func = session_ticket_pack,
62 .unpack_func = session_ticket_unpack,
63 .deinit_func = session_ticket_deinit_data,
66 #define SESSION_KEY_SIZE (SESSION_TICKET_KEY_NAME_SIZE+SESSION_TICKET_KEY_SIZE+SESSION_TICKET_MAC_SECRET_SIZE)
67 #define NAME_POS (0)
68 #define KEY_POS (SESSION_TICKET_KEY_NAME_SIZE)
69 #define MAC_SECRET_POS (SESSION_TICKET_KEY_NAME_SIZE+SESSION_TICKET_KEY_SIZE)
71 typedef struct
73 int session_ticket_enable;
74 int session_ticket_renew;
75 uint8_t session_ticket_IV[SESSION_TICKET_IV_SIZE];
77 uint8_t *session_ticket;
78 int session_ticket_len;
80 uint8_t key[SESSION_KEY_SIZE];
81 } session_ticket_ext_st;
83 struct ticket
85 uint8_t key_name[KEY_NAME_SIZE];
86 uint8_t IV[IV_SIZE];
87 uint8_t *encrypted_state;
88 uint16_t encrypted_state_len;
89 uint8_t mac[MAC_SIZE];
92 static int
93 digest_ticket (const gnutls_datum_t * key, struct ticket *ticket,
94 uint8_t * digest)
96 digest_hd_st digest_hd;
97 uint16_t length16;
98 int ret;
100 ret = _gnutls_hmac_init (&digest_hd, GNUTLS_MAC_SHA256, key->data,
101 key->size);
102 if (ret < 0)
104 gnutls_assert ();
105 return ret;
107 _gnutls_hmac (&digest_hd, ticket->key_name, KEY_NAME_SIZE);
108 _gnutls_hmac (&digest_hd, ticket->IV, IV_SIZE);
109 length16 = _gnutls_conv_uint16 (ticket->encrypted_state_len);
110 _gnutls_hmac (&digest_hd, &length16, 2);
111 _gnutls_hmac (&digest_hd, ticket->encrypted_state,
112 ticket->encrypted_state_len);
113 _gnutls_hmac_deinit (&digest_hd, digest);
115 return 0;
118 static int
119 decrypt_ticket (gnutls_session_t session, session_ticket_ext_st * priv,
120 struct ticket *ticket)
122 cipher_hd_st cipher_hd;
123 gnutls_datum_t key, IV, mac_secret, state;
124 uint8_t final[MAC_SECRET_SIZE];
125 time_t timestamp = gnutls_time (0);
126 int ret;
128 /* Check the integrity of ticket using HMAC-SHA-256. */
129 mac_secret.data = (void *) &priv->key[MAC_SECRET_POS];
130 mac_secret.size = MAC_SECRET_SIZE;
131 ret = digest_ticket (&mac_secret, ticket, final);
132 if (ret < 0)
134 gnutls_assert ();
135 return ret;
138 if (memcmp (ticket->mac, final, MAC_SIZE))
140 gnutls_assert ();
141 return GNUTLS_E_DECRYPTION_FAILED;
144 /* Decrypt encrypted_state using 128-bit AES in CBC mode. */
145 key.data = (void *) &priv->key[KEY_POS];
146 key.size = KEY_SIZE;
147 IV.data = ticket->IV;
148 IV.size = IV_SIZE;
149 ret =
150 _gnutls_cipher_init (&cipher_hd, GNUTLS_CIPHER_AES_128_CBC, &key, &IV, 0);
151 if (ret < 0)
153 gnutls_assert ();
154 return ret;
156 ret = _gnutls_cipher_decrypt (&cipher_hd, ticket->encrypted_state,
157 ticket->encrypted_state_len);
158 _gnutls_cipher_deinit (&cipher_hd);
159 if (ret < 0)
161 gnutls_assert ();
162 return ret;
165 /* Unpack security parameters. */
166 state.data = ticket->encrypted_state;
167 state.size = ticket->encrypted_state_len;
168 ret = _gnutls_session_unpack (session, &state);
169 if (ret < 0)
171 gnutls_assert ();
172 return ret;
175 if (timestamp - session->internals.resumed_security_parameters.timestamp >
176 session->internals.expire_time
177 || session->internals.resumed_security_parameters.timestamp > timestamp)
179 gnutls_assert ();
180 return GNUTLS_E_EXPIRED;
183 session->internals.resumed = RESUME_TRUE;
185 return 0;
188 static int
189 encrypt_ticket (gnutls_session_t session, session_ticket_ext_st * priv,
190 struct ticket *ticket)
192 cipher_hd_st cipher_hd;
193 gnutls_datum_t key, IV, mac_secret, state, encrypted_state;
194 int blocksize;
195 int ret;
197 /* Pack security parameters. */
198 ret = _gnutls_session_pack (session, &state);
199 if (ret < 0)
201 gnutls_assert ();
202 return ret;
204 blocksize = gnutls_cipher_get_block_size (GNUTLS_CIPHER_AES_128_CBC);
206 encrypted_state.size =
207 ((state.size + blocksize - 1) / blocksize) * blocksize;
208 encrypted_state.data = gnutls_malloc (encrypted_state.size);
209 if (!encrypted_state.data)
211 gnutls_assert ();
212 _gnutls_free_datum (&state);
213 return GNUTLS_E_MEMORY_ERROR;
215 memset (encrypted_state.data, 0, encrypted_state.size);
216 memcpy (encrypted_state.data, state.data, state.size);
217 _gnutls_free_datum (&state);
219 /* Encrypt state using 128-bit AES in CBC mode. */
220 key.data = (void *) &priv->key[KEY_POS];
221 key.size = KEY_SIZE;
222 IV.data = priv->session_ticket_IV;
223 IV.size = IV_SIZE;
224 ret =
225 _gnutls_cipher_init (&cipher_hd, GNUTLS_CIPHER_AES_128_CBC, &key, &IV, 1);
226 if (ret < 0)
228 gnutls_assert ();
229 _gnutls_free_datum (&encrypted_state);
230 return ret;
233 ret = _gnutls_cipher_encrypt (&cipher_hd, encrypted_state.data,
234 encrypted_state.size);
235 _gnutls_cipher_deinit (&cipher_hd);
236 if (ret < 0)
238 gnutls_assert ();
239 _gnutls_free_datum (&encrypted_state);
240 return ret;
243 /* Fill the ticket structure to compute MAC. */
244 memcpy (ticket->key_name, &priv->key[NAME_POS], KEY_NAME_SIZE);
245 memcpy (ticket->IV, IV.data, IV.size);
246 ticket->encrypted_state_len = encrypted_state.size;
247 ticket->encrypted_state = encrypted_state.data;
249 mac_secret.data = &priv->key[MAC_SECRET_POS];
250 mac_secret.size = MAC_SECRET_SIZE;
251 ret = digest_ticket (&mac_secret, ticket, ticket->mac);
252 if (ret < 0)
254 gnutls_assert ();
255 _gnutls_free_datum (&encrypted_state);
256 return ret;
259 return 0;
262 static int
263 session_ticket_recv_params (gnutls_session_t session,
264 const uint8_t * data, size_t _data_size)
266 ssize_t data_size = _data_size;
267 session_ticket_ext_st *priv = NULL;
268 extension_priv_data_t epriv;
269 int ret;
271 ret =
272 _gnutls_ext_get_session_data (session, GNUTLS_EXTENSION_SESSION_TICKET,
273 &epriv);
274 if (ret < 0)
276 return 0;
278 priv = epriv.ptr;
280 if (!priv->session_ticket_enable)
281 return 0;
283 if (session->security_parameters.entity == GNUTLS_SERVER)
285 struct ticket ticket;
286 const uint8_t *encrypted_state;
287 int ret;
289 /* The client requested a new session ticket. */
290 if (data_size == 0)
292 priv->session_ticket_renew = 1;
293 return 0;
296 DECR_LEN (data_size, KEY_NAME_SIZE);
297 memcpy (ticket.key_name, data, KEY_NAME_SIZE);
298 data += KEY_NAME_SIZE;
300 /* If the key name of the ticket does not match the one that we
301 hold, issue a new ticket. */
302 if (memcmp (ticket.key_name, &priv->key[NAME_POS], KEY_NAME_SIZE))
304 priv->session_ticket_renew = 1;
305 return 0;
308 DECR_LEN (data_size, IV_SIZE);
309 memcpy (ticket.IV, data, IV_SIZE);
310 data += IV_SIZE;
312 DECR_LEN (data_size, 2);
313 ticket.encrypted_state_len = _gnutls_read_uint16 (data);
314 data += 2;
316 encrypted_state = data;
318 DECR_LEN (data_size, ticket.encrypted_state_len);
319 data += ticket.encrypted_state_len;
321 DECR_LEN (data_size, MAC_SIZE);
322 memcpy (ticket.mac, data, MAC_SIZE);
324 ticket.encrypted_state = gnutls_malloc (ticket.encrypted_state_len);
325 if (!ticket.encrypted_state)
327 gnutls_assert ();
328 return GNUTLS_E_MEMORY_ERROR;
330 memcpy (ticket.encrypted_state, encrypted_state,
331 ticket.encrypted_state_len);
333 ret = decrypt_ticket (session, priv, &ticket);
334 gnutls_free (ticket.encrypted_state);
335 if (ret < 0)
337 priv->session_ticket_renew = 1;
338 return 0;
341 else /* Client */
343 if (data_size == 0)
345 priv->session_ticket_renew = 1;
346 return 0;
350 return 0;
353 /* returns a positive number if we send the extension data, (0) if we
354 do not want to send it, and a negative number on failure.
356 static int
357 session_ticket_send_params (gnutls_session_t session,
358 gnutls_buffer_st * extdata)
360 session_ticket_ext_st *priv = NULL;
361 extension_priv_data_t epriv;
362 int ret;
364 ret =
365 _gnutls_ext_get_session_data (session, GNUTLS_EXTENSION_SESSION_TICKET,
366 &epriv);
367 if (ret >= 0)
368 priv = epriv.ptr;
370 if (priv == NULL || !priv->session_ticket_enable)
371 return 0;
373 if (session->security_parameters.entity == GNUTLS_SERVER)
375 if (priv && priv->session_ticket_renew)
377 return GNUTLS_E_INT_RET_0;
380 else
382 ret =
383 _gnutls_ext_get_resumed_session_data (session,
384 GNUTLS_EXTENSION_SESSION_TICKET,
385 &epriv);
386 if (ret >= 0)
387 priv = epriv.ptr;
389 /* no previous data. Just advertize it */
390 if (ret < 0)
391 return GNUTLS_E_INT_RET_0;
393 /* previous data had session tickets disabled. Don't advertize. Ignore. */
394 if (!priv->session_ticket_enable)
395 return 0;
397 if (priv->session_ticket_len > 0)
399 ret = _gnutls_buffer_append_data( extdata, priv->session_ticket, priv->session_ticket_len);
400 if (ret < 0)
401 return gnutls_assert_val(ret);
403 return priv->session_ticket_len;
406 return 0;
410 static void
411 session_ticket_deinit_data (extension_priv_data_t epriv)
413 session_ticket_ext_st *priv = epriv.ptr;
415 gnutls_free (priv->session_ticket);
416 gnutls_free (priv);
419 static int
420 session_ticket_pack (extension_priv_data_t epriv, gnutls_buffer_st * ps)
422 session_ticket_ext_st *priv = epriv.ptr;
423 int ret;
425 BUFFER_APPEND_PFX4 (ps, priv->session_ticket, priv->session_ticket_len);
426 BUFFER_APPEND_NUM (ps, priv->session_ticket_enable);
428 return 0;
431 static int
432 session_ticket_unpack (gnutls_buffer_st * ps, extension_priv_data_t * _priv)
434 session_ticket_ext_st *priv = NULL;
435 int ret;
436 extension_priv_data_t epriv;
437 gnutls_datum_t ticket;
439 priv = gnutls_calloc (1, sizeof (*priv));
440 if (priv == NULL)
442 gnutls_assert ();
443 return GNUTLS_E_MEMORY_ERROR;
446 BUFFER_POP_DATUM (ps, &ticket);
447 priv->session_ticket = ticket.data;
448 priv->session_ticket_len = ticket.size;
449 BUFFER_POP_NUM (ps, priv->session_ticket_enable);
451 epriv.ptr = priv;
452 *_priv = epriv;
454 return 0;
456 error:
457 gnutls_free (priv);
458 return ret;
464 * gnutls_session_ticket_key_generate:
465 * @key: is a pointer to a #gnutls_datum_t which will contain a newly
466 * created key.
468 * Generate a random key to encrypt security parameters within
469 * SessionTicket.
471 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
472 * error code.
474 * Since: 2.10.0
477 gnutls_session_ticket_key_generate (gnutls_datum_t * key)
479 return gnutls_key_generate(key, SESSION_KEY_SIZE);
483 * gnutls_session_ticket_enable_client:
484 * @session: is a #gnutls_session_t structure.
486 * Request that the client should attempt session resumption using
487 * SessionTicket.
489 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
490 * error code.
492 * Since: 2.10.0
495 gnutls_session_ticket_enable_client (gnutls_session_t session)
497 session_ticket_ext_st *priv = NULL;
498 extension_priv_data_t epriv;
500 if (!session)
502 gnutls_assert ();
503 return GNUTLS_E_INVALID_REQUEST;
506 priv = gnutls_calloc (1, sizeof (*priv));
507 if (priv == NULL)
509 gnutls_assert ();
510 return GNUTLS_E_MEMORY_ERROR;
512 priv->session_ticket_enable = 1;
513 epriv.ptr = priv;
515 _gnutls_ext_set_session_data (session,
516 GNUTLS_EXTENSION_SESSION_TICKET, epriv);
518 return 0;
522 * gnutls_session_ticket_enable_server:
523 * @session: is a #gnutls_session_t structure.
524 * @key: key to encrypt session parameters.
526 * Request that the server should attempt session resumption using
527 * SessionTicket. @key must be initialized with
528 * gnutls_session_ticket_key_generate().
530 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
531 * error code.
533 * Since: 2.10.0
536 gnutls_session_ticket_enable_server (gnutls_session_t session,
537 const gnutls_datum_t * key)
539 int ret;
540 session_ticket_ext_st *priv = NULL;
541 extension_priv_data_t epriv;
543 if (!session || !key
544 || key->size != SESSION_KEY_SIZE)
546 gnutls_assert ();
547 return GNUTLS_E_INVALID_REQUEST;
550 priv = gnutls_calloc (1, sizeof (*priv));
551 if (priv == NULL)
553 gnutls_assert ();
554 return GNUTLS_E_MEMORY_ERROR;
556 epriv.ptr = priv;
558 ret = _gnutls_rnd (GNUTLS_RND_NONCE, priv->session_ticket_IV, IV_SIZE);
559 if (ret < 0)
561 gnutls_assert ();
562 return ret;
565 memcpy (&priv->key, key->data, key->size);
566 priv->session_ticket_enable = 1;
568 _gnutls_ext_set_session_data (session,
569 GNUTLS_EXTENSION_SESSION_TICKET, epriv);
571 return 0;
575 _gnutls_send_new_session_ticket (gnutls_session_t session, int again)
577 mbuffer_st *bufel = NULL;
578 uint8_t *data = NULL, *p;
579 int data_size = 0;
580 int ret;
581 struct ticket ticket;
582 uint16_t ticket_len;
583 session_ticket_ext_st *priv = NULL;
584 extension_priv_data_t epriv;
585 uint16_t epoch_saved = session->security_parameters.epoch_write;
587 if (again == 0)
589 ret =
590 _gnutls_ext_get_session_data (session,
591 GNUTLS_EXTENSION_SESSION_TICKET,
592 &epriv);
593 if (ret < 0)
594 return 0;
595 priv = epriv.ptr;
597 if (!priv->session_ticket_renew)
598 return 0;
600 /* XXX: Temporarily set write algorithms to be used.
601 _gnutls_write_connection_state_init() does this job, but it also
602 triggers encryption, while NewSessionTicket should not be
603 encrypted in the record layer. */
604 ret =
605 _gnutls_epoch_set_keys (session,
606 session->security_parameters.epoch_next);
607 if (ret < 0)
609 gnutls_assert ();
610 return ret;
613 session->security_parameters.epoch_write =
614 session->security_parameters.epoch_next;
616 ret = encrypt_ticket (session, priv, &ticket);
617 session->security_parameters.epoch_write = epoch_saved;
618 if (ret < 0)
620 gnutls_assert ();
621 return ret;
624 ticket_len = KEY_NAME_SIZE + IV_SIZE + 2 + ticket.encrypted_state_len
625 + MAC_SIZE;
627 bufel = _gnutls_handshake_alloc (session, 4 + 2 + ticket_len, 4+2+ticket_len);
628 if (!bufel)
630 gnutls_assert ();
631 gnutls_free (ticket.encrypted_state);
632 return GNUTLS_E_MEMORY_ERROR;
635 data = _mbuffer_get_udata_ptr (bufel);
636 p = data;
638 _gnutls_write_uint32 (session->internals.expire_time, p);
639 p += 4;
641 _gnutls_write_uint16 (ticket_len, p);
642 p += 2;
644 memcpy (p, ticket.key_name, KEY_NAME_SIZE);
645 p += KEY_NAME_SIZE;
647 memcpy (p, ticket.IV, IV_SIZE);
648 p += IV_SIZE;
650 _gnutls_write_uint16 (ticket.encrypted_state_len, p);
651 p += 2;
653 memcpy (p, ticket.encrypted_state, ticket.encrypted_state_len);
654 gnutls_free (ticket.encrypted_state);
655 p += ticket.encrypted_state_len;
657 memcpy (p, ticket.mac, MAC_SIZE);
658 p += MAC_SIZE;
660 data_size = p - data;
662 session->internals.ticket_sent = 1;
664 return _gnutls_send_handshake (session, data_size ? bufel : NULL,
665 GNUTLS_HANDSHAKE_NEW_SESSION_TICKET);
669 _gnutls_recv_new_session_ticket (gnutls_session_t session)
671 uint8_t *p;
672 int data_size;
673 gnutls_buffer_st buf;
674 uint16_t ticket_len;
675 int ret;
676 session_ticket_ext_st *priv = NULL;
677 extension_priv_data_t epriv;
679 ret =
680 _gnutls_ext_get_session_data (session, GNUTLS_EXTENSION_SESSION_TICKET,
681 &epriv);
682 if (ret < 0)
684 gnutls_assert ();
685 return 0;
687 priv = epriv.ptr;
689 if (!priv->session_ticket_renew)
690 return 0;
692 ret = _gnutls_recv_handshake (session,
693 GNUTLS_HANDSHAKE_NEW_SESSION_TICKET,
694 0, &buf);
695 if (ret < 0)
696 return gnutls_assert_val_fatal(ret);
698 p = buf.data;
699 data_size = buf.length;
701 DECR_LENGTH_COM (data_size, 4, ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH; goto error);
702 /* skip over lifetime hint */
703 p += 4;
705 DECR_LENGTH_COM (data_size, 2, ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH; goto error);
706 ticket_len = _gnutls_read_uint16 (p);
707 p += 2;
709 DECR_LENGTH_COM (data_size, ticket_len, ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH; goto error);
710 priv->session_ticket = gnutls_realloc (priv->session_ticket, ticket_len);
711 if (!priv->session_ticket)
713 gnutls_assert ();
714 ret = GNUTLS_E_MEMORY_ERROR;
715 goto error;
717 memcpy (priv->session_ticket, p, ticket_len);
718 priv->session_ticket_len = ticket_len;
720 /* Discard the current session ID. (RFC5077 3.4) */
721 ret = _gnutls_generate_session_id (session->security_parameters.session_id,
722 &session->
723 security_parameters.session_id_size);
724 if (ret < 0)
726 gnutls_assert ();
727 gnutls_free (priv->session_ticket);
728 priv->session_ticket = NULL;
729 ret = GNUTLS_E_INTERNAL_ERROR;
730 goto error;
732 ret = 0;
734 error:
735 _gnutls_buffer_clear (&buf);
737 return ret;