Add `gnutls/dtls.h' to the distribution.
[gnutls.git] / lib / ext_session_ticket.c
blobf51c127e7b2611c64c0779608e53cf4c3de33089
1 /*
2 * Copyright (C) 2009, 2010 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 2.1 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
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
21 * USA
25 #include <gnutls_int.h>
26 #include <gnutls_errors.h>
27 #include <gnutls_datum.h>
28 #include <gnutls_algorithms.h>
29 #include <gnutls_handshake.h>
30 #include <gnutls_num.h>
31 #include <gnutls_constate.h>
32 #include <gnutls_session_pack.h>
33 #include <random.h>
34 #include <ext_session_ticket.h>
35 #include <gnutls_mbuffers.h>
36 #include <gnutls_extensions.h>
37 #include <gnutls_constate.h>
39 #ifdef ENABLE_SESSION_TICKET
41 #define KEY_NAME_SIZE SESSION_TICKET_KEY_NAME_SIZE
42 #define KEY_SIZE SESSION_TICKET_KEY_SIZE
43 #define IV_SIZE SESSION_TICKET_IV_SIZE
44 #define MAC_SECRET_SIZE SESSION_TICKET_MAC_SECRET_SIZE
46 #define MAC_SIZE 32
48 static int session_ticket_recv_params (gnutls_session_t session,
49 const opaque * data, size_t data_size);
50 static int session_ticket_send_params (gnutls_session_t session,
51 gnutls_buffer_st* extdata);
52 static int session_ticket_unpack (gnutls_buffer_st * ps,
53 extension_priv_data_t * _priv);
54 static int session_ticket_pack (extension_priv_data_t _priv,
55 gnutls_buffer_st * ps);
56 static void session_ticket_deinit_data (extension_priv_data_t priv);
58 extension_entry_st ext_mod_session_ticket = {
59 .name = "SESSION TICKET",
60 .type = GNUTLS_EXTENSION_SESSION_TICKET,
61 .parse_type = GNUTLS_EXT_TLS,
63 .recv_func = session_ticket_recv_params,
64 .send_func = session_ticket_send_params,
65 .pack_func = session_ticket_pack,
66 .unpack_func = session_ticket_unpack,
67 .deinit_func = session_ticket_deinit_data,
70 struct gnutls_session_ticket_key_st
72 opaque key_name[SESSION_TICKET_KEY_NAME_SIZE];
73 opaque key[SESSION_TICKET_KEY_SIZE];
74 opaque mac_secret[SESSION_TICKET_MAC_SECRET_SIZE];
77 typedef struct
79 int session_ticket_enable;
80 int session_ticket_renew;
81 opaque session_ticket_IV[SESSION_TICKET_IV_SIZE];
83 opaque *session_ticket;
84 int session_ticket_len;
86 struct gnutls_session_ticket_key_st key;
87 } session_ticket_ext_st;
89 struct ticket
91 opaque key_name[KEY_NAME_SIZE];
92 opaque IV[IV_SIZE];
93 opaque *encrypted_state;
94 uint16_t encrypted_state_len;
95 opaque mac[MAC_SIZE];
98 static int
99 digest_ticket (const gnutls_datum_t * key, struct ticket *ticket,
100 opaque * digest)
102 digest_hd_st digest_hd;
103 uint16_t length16;
104 int ret;
106 ret = _gnutls_hmac_init (&digest_hd, GNUTLS_MAC_SHA256, key->data,
107 key->size);
108 if (ret < 0)
110 gnutls_assert ();
111 return ret;
113 _gnutls_hmac (&digest_hd, ticket->key_name, KEY_NAME_SIZE);
114 _gnutls_hmac (&digest_hd, ticket->IV, IV_SIZE);
115 length16 = _gnutls_conv_uint16 (ticket->encrypted_state_len);
116 _gnutls_hmac (&digest_hd, &length16, 2);
117 _gnutls_hmac (&digest_hd, ticket->encrypted_state,
118 ticket->encrypted_state_len);
119 _gnutls_hmac_deinit (&digest_hd, digest);
121 return 0;
124 static int
125 decrypt_ticket (gnutls_session_t session, session_ticket_ext_st * priv,
126 struct ticket *ticket)
128 cipher_hd_st cipher_hd;
129 gnutls_datum_t key, IV, mac_secret, state;
130 opaque final[MAC_SECRET_SIZE];
131 time_t timestamp = time (0);
132 int ret;
134 /* Check the integrity of ticket using HMAC-SHA-256. */
135 mac_secret.data = (void *) priv->key.mac_secret;
136 mac_secret.size = MAC_SECRET_SIZE;
137 ret = digest_ticket (&mac_secret, ticket, final);
138 if (ret < 0)
140 gnutls_assert ();
141 return ret;
144 if (memcmp (ticket->mac, final, MAC_SIZE))
146 gnutls_assert ();
147 return GNUTLS_E_DECRYPTION_FAILED;
150 /* Decrypt encrypted_state using 128-bit AES in CBC mode. */
151 key.data = (void *) priv->key.key;
152 key.size = KEY_SIZE;
153 IV.data = ticket->IV;
154 IV.size = IV_SIZE;
155 ret =
156 _gnutls_cipher_init (&cipher_hd, GNUTLS_CIPHER_AES_128_CBC, &key, &IV);
157 if (ret < 0)
159 gnutls_assert ();
160 return ret;
162 ret = _gnutls_cipher_decrypt (&cipher_hd, ticket->encrypted_state,
163 ticket->encrypted_state_len);
164 _gnutls_cipher_deinit (&cipher_hd);
165 if (ret < 0)
167 gnutls_assert ();
168 return ret;
171 /* Unpack security parameters. */
172 state.data = ticket->encrypted_state;
173 state.size = ticket->encrypted_state_len;
174 ret = _gnutls_session_unpack (session, &state);
175 if (ret < 0)
177 gnutls_assert ();
178 return ret;
181 if (timestamp - session->internals.resumed_security_parameters.timestamp >
182 session->internals.expire_time
183 || session->internals.resumed_security_parameters.timestamp > timestamp)
185 gnutls_assert ();
186 return GNUTLS_E_EXPIRED;
189 session->internals.resumed = RESUME_TRUE;
191 return 0;
194 static int
195 encrypt_ticket (gnutls_session_t session, session_ticket_ext_st * priv,
196 struct ticket *ticket)
198 cipher_hd_st cipher_hd;
199 gnutls_datum_t key, IV, mac_secret, state, encrypted_state;
200 int blocksize;
201 int ret;
203 /* Pack security parameters. */
204 ret = _gnutls_session_pack (session, &state);
205 if (ret < 0)
207 gnutls_assert ();
208 return ret;
210 blocksize = gnutls_cipher_get_block_size (GNUTLS_CIPHER_AES_128_CBC);
212 encrypted_state.size =
213 ((state.size + blocksize - 1) / blocksize) * blocksize;
214 encrypted_state.data = gnutls_malloc (encrypted_state.size);
215 if (!encrypted_state.data)
217 gnutls_assert ();
218 _gnutls_free_datum (&state);
219 return GNUTLS_E_MEMORY_ERROR;
221 memset (encrypted_state.data, 0, encrypted_state.size);
222 memcpy (encrypted_state.data, state.data, state.size);
223 _gnutls_free_datum (&state);
225 /* Encrypt state using 128-bit AES in CBC mode. */
226 key.data = (void *) priv->key.key;
227 key.size = KEY_SIZE;
228 IV.data = priv->session_ticket_IV;
229 IV.size = IV_SIZE;
230 ret =
231 _gnutls_cipher_init (&cipher_hd, GNUTLS_CIPHER_AES_128_CBC, &key, &IV);
232 if (ret < 0)
234 gnutls_assert ();
235 _gnutls_free_datum (&encrypted_state);
236 return ret;
239 ret = _gnutls_cipher_encrypt (&cipher_hd, encrypted_state.data,
240 encrypted_state.size);
241 _gnutls_cipher_deinit (&cipher_hd);
242 if (ret < 0)
244 gnutls_assert ();
245 _gnutls_free_datum (&encrypted_state);
246 return ret;
249 /* Fill the ticket structure to compute MAC. */
250 memcpy (ticket->key_name, priv->key.key_name, KEY_NAME_SIZE);
251 memcpy (ticket->IV, IV.data, IV.size);
252 ticket->encrypted_state_len = encrypted_state.size;
253 ticket->encrypted_state = encrypted_state.data;
255 mac_secret.data = priv->key.mac_secret;
256 mac_secret.size = MAC_SECRET_SIZE;
257 ret = digest_ticket (&mac_secret, ticket, ticket->mac);
258 if (ret < 0)
260 gnutls_assert ();
261 _gnutls_free_datum (&encrypted_state);
262 return ret;
265 return 0;
268 static int
269 session_ticket_recv_params (gnutls_session_t session,
270 const opaque * data, size_t _data_size)
272 ssize_t data_size = _data_size;
273 session_ticket_ext_st *priv = NULL;
274 extension_priv_data_t epriv;
275 int ret;
277 ret =
278 _gnutls_ext_get_session_data (session, GNUTLS_EXTENSION_SESSION_TICKET,
279 &epriv);
280 if (ret < 0)
282 return 0;
284 priv = epriv.ptr;
286 if (!priv->session_ticket_enable)
287 return 0;
289 if (session->security_parameters.entity == GNUTLS_SERVER)
291 struct ticket ticket;
292 const opaque *encrypted_state;
293 int ret;
295 /* The client requested a new session ticket. */
296 if (data_size == 0)
298 priv->session_ticket_renew = 1;
299 return 0;
302 DECR_LEN (data_size, KEY_NAME_SIZE);
303 memcpy (ticket.key_name, data, KEY_NAME_SIZE);
304 data += KEY_NAME_SIZE;
306 /* If the key name of the ticket does not match the one that we
307 hold, issue a new ticket. */
308 if (memcmp (ticket.key_name, priv->key.key_name, KEY_NAME_SIZE))
310 priv->session_ticket_renew = 1;
311 return 0;
314 DECR_LEN (data_size, IV_SIZE);
315 memcpy (ticket.IV, data, IV_SIZE);
316 data += IV_SIZE;
318 DECR_LEN (data_size, 2);
319 ticket.encrypted_state_len = _gnutls_read_uint16 (data);
320 data += 2;
322 encrypted_state = data;
324 DECR_LEN (data_size, ticket.encrypted_state_len);
325 data += ticket.encrypted_state_len;
327 DECR_LEN (data_size, MAC_SIZE);
328 memcpy (ticket.mac, data, MAC_SIZE);
330 ticket.encrypted_state = gnutls_malloc (ticket.encrypted_state_len);
331 if (!ticket.encrypted_state)
333 gnutls_assert ();
334 return GNUTLS_E_MEMORY_ERROR;
336 memcpy (ticket.encrypted_state, encrypted_state,
337 ticket.encrypted_state_len);
339 ret = decrypt_ticket (session, priv, &ticket);
340 gnutls_free (ticket.encrypted_state);
341 if (ret < 0)
343 priv->session_ticket_renew = 1;
344 return 0;
347 else /* Client */
349 if (data_size == 0)
351 priv->session_ticket_renew = 1;
352 return 0;
356 return 0;
359 /* returns a positive number if we send the extension data, zero if we
360 do not want to send it, and a negative number on failure.
362 static int
363 session_ticket_send_params (gnutls_session_t session,
364 gnutls_buffer_st * extdata)
366 session_ticket_ext_st *priv = NULL;
367 extension_priv_data_t epriv;
368 int ret;
370 ret =
371 _gnutls_ext_get_session_data (session, GNUTLS_EXTENSION_SESSION_TICKET,
372 &epriv);
373 if (ret >= 0)
374 priv = epriv.ptr;
376 if (priv == NULL || !priv->session_ticket_enable)
377 return 0;
379 if (session->security_parameters.entity == GNUTLS_SERVER)
381 if (priv && priv->session_ticket_renew)
383 return GNUTLS_E_INT_RET_0;
386 else
388 ret =
389 _gnutls_ext_get_resumed_session_data (session,
390 GNUTLS_EXTENSION_SESSION_TICKET,
391 &epriv);
392 if (ret >= 0)
393 priv = epriv.ptr;
395 /* no previous data. Just advertize it */
396 if (ret < 0)
397 return GNUTLS_E_INT_RET_0;
399 /* previous data had session tickets disabled. Don't advertize. Ignore. */
400 if (!priv->session_ticket_enable)
401 return 0;
403 if (priv->session_ticket_len > 0)
405 ret = _gnutls_buffer_append_data( extdata, priv->session_ticket, priv->session_ticket_len);
406 if (ret < 0)
407 return gnutls_assert_val(ret);
409 return priv->session_ticket_len;
412 return 0;
416 static void
417 session_ticket_deinit_data (extension_priv_data_t epriv)
419 session_ticket_ext_st *priv = epriv.ptr;
421 gnutls_free (priv->session_ticket);
422 gnutls_free (priv);
425 static int
426 session_ticket_pack (extension_priv_data_t epriv, gnutls_buffer_st * ps)
428 session_ticket_ext_st *priv = epriv.ptr;
429 int ret;
431 BUFFER_APPEND_PFX (ps, priv->session_ticket, priv->session_ticket_len);
432 BUFFER_APPEND_NUM (ps, priv->session_ticket_enable);
434 return 0;
437 static int
438 session_ticket_unpack (gnutls_buffer_st * ps, extension_priv_data_t * _priv)
440 session_ticket_ext_st *priv = NULL;
441 int ret;
442 extension_priv_data_t epriv;
443 gnutls_datum ticket;
445 priv = gnutls_calloc (1, sizeof (*priv));
446 if (priv == NULL)
448 gnutls_assert ();
449 return GNUTLS_E_MEMORY_ERROR;
452 BUFFER_POP_DATUM (ps, &ticket);
453 priv->session_ticket = ticket.data;
454 priv->session_ticket_len = ticket.size;
455 BUFFER_POP_NUM (ps, priv->session_ticket_enable);
457 epriv.ptr = priv;
458 *_priv = epriv;
460 return 0;
462 error:
463 gnutls_free (priv);
464 return ret;
470 * gnutls_session_ticket_key_generate:
471 * @key: is a pointer to a #gnutls_datum_t which will contain a newly
472 * created key.
474 * Generate a random key to encrypt security parameters within
475 * SessionTicket.
477 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
478 * error code.
480 * Since: 2.10.0
483 gnutls_session_ticket_key_generate (gnutls_datum_t * key)
485 int ret;
487 key->size = sizeof (struct gnutls_session_ticket_key_st);
488 key->data = gnutls_malloc (key->size);
489 if (!key->data)
491 gnutls_assert ();
492 return GNUTLS_E_MEMORY_ERROR;
495 ret = _gnutls_rnd (GNUTLS_RND_RANDOM, key->data, key->size);
496 if (ret < 0)
498 gnutls_assert ();
499 _gnutls_free_datum (key);
500 return ret;
503 return 0;
507 * gnutls_session_ticket_enable_client:
508 * @session: is a #gnutls_session_t structure.
510 * Request that the client should attempt session resumption using
511 * SessionTicket.
513 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
514 * error code.
516 * Since: 2.10.0
519 gnutls_session_ticket_enable_client (gnutls_session_t session)
521 session_ticket_ext_st *priv = NULL;
522 extension_priv_data_t epriv;
524 if (!session)
526 gnutls_assert ();
527 return GNUTLS_E_INVALID_REQUEST;
530 priv = gnutls_calloc (1, sizeof (*priv));
531 if (priv == NULL)
533 gnutls_assert ();
534 return GNUTLS_E_MEMORY_ERROR;
536 priv->session_ticket_enable = 1;
537 epriv.ptr = priv;
539 _gnutls_ext_set_session_data (session,
540 GNUTLS_EXTENSION_SESSION_TICKET, epriv);
542 return 0;
546 * gnutls_session_ticket_enable_server:
547 * @session: is a #gnutls_session_t structure.
548 * @key: key to encrypt session parameters.
550 * Request that the server should attempt session resumption using
551 * SessionTicket. @key must be initialized with
552 * gnutls_session_ticket_key_generate().
554 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
555 * error code.
557 * Since: 2.10.0
560 gnutls_session_ticket_enable_server (gnutls_session_t session,
561 const gnutls_datum_t * key)
563 int ret;
564 session_ticket_ext_st *priv = NULL;
565 extension_priv_data_t epriv;
567 if (!session || !key
568 || key->size != sizeof (struct gnutls_session_ticket_key_st))
570 gnutls_assert ();
571 return GNUTLS_E_INVALID_REQUEST;
574 priv = gnutls_calloc (1, sizeof (*priv));
575 if (priv == NULL)
577 gnutls_assert ();
578 return GNUTLS_E_MEMORY_ERROR;
580 epriv.ptr = priv;
582 ret = _gnutls_rnd (GNUTLS_RND_RANDOM, priv->session_ticket_IV, IV_SIZE);
583 if (ret < 0)
585 gnutls_assert ();
586 return ret;
589 memcpy (&priv->key, key->data, key->size);
590 priv->session_ticket_enable = 1;
592 _gnutls_ext_set_session_data (session,
593 GNUTLS_EXTENSION_SESSION_TICKET, epriv);
595 return 0;
599 _gnutls_send_new_session_ticket (gnutls_session_t session, int again)
601 mbuffer_st *bufel = NULL;
602 uint8_t *data = NULL, *p;
603 int data_size = 0;
604 int ret;
605 struct ticket ticket;
606 uint16_t ticket_len;
607 session_ticket_ext_st *priv = NULL;
608 extension_priv_data_t epriv;
609 uint16_t epoch_saved = session->security_parameters.epoch_write;
611 if (again == 0)
613 ret =
614 _gnutls_ext_get_session_data (session,
615 GNUTLS_EXTENSION_SESSION_TICKET,
616 &epriv);
617 if (ret < 0)
618 return 0;
619 priv = epriv.ptr;
621 if (!priv->session_ticket_renew)
622 return 0;
624 /* XXX: Temporarily set write algorithms to be used.
625 _gnutls_write_connection_state_init() does this job, but it also
626 triggers encryption, while NewSessionTicket should not be
627 encrypted in the record layer. */
628 ret =
629 _gnutls_epoch_set_keys (session,
630 session->security_parameters.epoch_next);
631 if (ret < 0)
633 gnutls_assert ();
634 return ret;
637 session->security_parameters.epoch_write =
638 session->security_parameters.epoch_next;
640 ret = encrypt_ticket (session, priv, &ticket);
641 session->security_parameters.epoch_write = epoch_saved;
642 if (ret < 0)
644 gnutls_assert ();
645 return ret;
648 ticket_len = KEY_NAME_SIZE + IV_SIZE + 2 + ticket.encrypted_state_len
649 + MAC_SIZE;
651 bufel = _gnutls_handshake_alloc (session, 4 + 2 + ticket_len, 4+2+ticket_len);
652 if (!bufel)
654 gnutls_assert ();
655 gnutls_free (ticket.encrypted_state);
656 return GNUTLS_E_MEMORY_ERROR;
659 data = _mbuffer_get_udata_ptr (bufel);
660 p = data;
662 _gnutls_write_uint32 (session->internals.expire_time, p);
663 p += 4;
665 _gnutls_write_uint16 (ticket_len, p);
666 p += 2;
668 memcpy (p, ticket.key_name, KEY_NAME_SIZE);
669 p += KEY_NAME_SIZE;
671 memcpy (p, ticket.IV, IV_SIZE);
672 p += IV_SIZE;
674 _gnutls_write_uint16 (ticket.encrypted_state_len, p);
675 p += 2;
677 memcpy (p, ticket.encrypted_state, ticket.encrypted_state_len);
678 gnutls_free (ticket.encrypted_state);
679 p += ticket.encrypted_state_len;
681 memcpy (p, ticket.mac, MAC_SIZE);
682 p += MAC_SIZE;
684 data_size = p - data;
686 return _gnutls_send_handshake (session, data_size ? bufel : NULL,
687 GNUTLS_HANDSHAKE_NEW_SESSION_TICKET);
691 _gnutls_recv_new_session_ticket (gnutls_session_t session)
693 uint8_t *data = NULL, *p;
694 int data_size;
695 uint32_t lifetime_hint;
696 uint16_t ticket_len;
697 int ret;
698 session_ticket_ext_st *priv = NULL;
699 extension_priv_data_t epriv;
701 ret =
702 _gnutls_ext_get_session_data (session, GNUTLS_EXTENSION_SESSION_TICKET,
703 &epriv);
704 if (ret < 0)
706 gnutls_assert ();
707 return 0;
709 priv = epriv.ptr;
711 if (!priv->session_ticket_renew)
712 return 0;
714 ret = _gnutls_recv_handshake (session, &data, &data_size,
715 GNUTLS_HANDSHAKE_NEW_SESSION_TICKET,
716 MANDATORY_PACKET);
717 if (ret < 0)
719 gnutls_assert ();
720 return ret;
723 p = data;
724 DECR_LENGTH_COM (data_size, 4, goto error);
725 lifetime_hint = _gnutls_read_uint32 (p);
726 p += 4;
728 DECR_LENGTH_COM (data_size, 2, goto error);
729 ticket_len = _gnutls_read_uint16 (p);
730 p += 2;
732 DECR_LENGTH_COM (data_size, ticket_len, goto error);
733 priv->session_ticket = gnutls_realloc (priv->session_ticket, ticket_len);
734 if (!priv->session_ticket)
736 gnutls_assert ();
737 gnutls_free (data);
738 return GNUTLS_E_MEMORY_ERROR;
740 memcpy (priv->session_ticket, p, ticket_len);
741 gnutls_free (data);
742 priv->session_ticket_len = ticket_len;
744 /* Discard the current session ID. (RFC5077 3.4) */
745 ret = _gnutls_generate_session_id (session->security_parameters.session_id,
746 &session->
747 security_parameters.session_id_size);
748 if (ret < 0)
750 gnutls_assert ();
751 gnutls_free (priv->session_ticket);
752 priv->session_ticket = NULL;
753 return GNUTLS_E_INTERNAL_ERROR;
755 return 0;
757 error:
758 gnutls_free (data);
759 return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
762 #endif