updated doc
[gnutls.git] / lib / ext_session_ticket.c
blobb6ca2ba1501090a78b5c31269e153710b2266854
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>
38 #include <system.h>
40 #ifdef ENABLE_SESSION_TICKET
42 #define KEY_NAME_SIZE SESSION_TICKET_KEY_NAME_SIZE
43 #define KEY_SIZE SESSION_TICKET_KEY_SIZE
44 #define IV_SIZE SESSION_TICKET_IV_SIZE
45 #define MAC_SECRET_SIZE SESSION_TICKET_MAC_SECRET_SIZE
47 #define MAC_SIZE 32
49 static int session_ticket_recv_params (gnutls_session_t session,
50 const opaque * data, size_t data_size);
51 static int session_ticket_send_params (gnutls_session_t session,
52 opaque * data, size_t data_size);
53 static int session_ticket_unpack (gnutls_buffer_st * ps,
54 extension_priv_data_t * _priv);
55 static int session_ticket_pack (extension_priv_data_t _priv,
56 gnutls_buffer_st * ps);
57 static void session_ticket_deinit_data (extension_priv_data_t priv);
59 extension_entry_st ext_mod_session_ticket = {
60 .name = "SESSION TICKET",
61 .type = GNUTLS_EXTENSION_SESSION_TICKET,
62 .parse_type = GNUTLS_EXT_TLS,
64 .recv_func = session_ticket_recv_params,
65 .send_func = session_ticket_send_params,
66 .pack_func = session_ticket_pack,
67 .unpack_func = session_ticket_unpack,
68 .deinit_func = session_ticket_deinit_data,
71 struct gnutls_session_ticket_key_st
73 opaque key_name[SESSION_TICKET_KEY_NAME_SIZE];
74 opaque key[SESSION_TICKET_KEY_SIZE];
75 opaque mac_secret[SESSION_TICKET_MAC_SECRET_SIZE];
78 typedef struct
80 int session_ticket_enable;
81 int session_ticket_renew;
82 opaque session_ticket_IV[SESSION_TICKET_IV_SIZE];
84 opaque *session_ticket;
85 int session_ticket_len;
87 struct gnutls_session_ticket_key_st key;
88 } session_ticket_ext_st;
90 struct ticket
92 opaque key_name[KEY_NAME_SIZE];
93 opaque IV[IV_SIZE];
94 opaque *encrypted_state;
95 uint16_t encrypted_state_len;
96 opaque mac[MAC_SIZE];
99 static int
100 digest_ticket (const gnutls_datum_t * key, struct ticket *ticket,
101 opaque * digest)
103 digest_hd_st digest_hd;
104 uint16_t length16;
105 int ret;
107 ret = _gnutls_hmac_init (&digest_hd, GNUTLS_MAC_SHA256, key->data,
108 key->size);
109 if (ret < 0)
111 gnutls_assert ();
112 return ret;
114 _gnutls_hmac (&digest_hd, ticket->key_name, KEY_NAME_SIZE);
115 _gnutls_hmac (&digest_hd, ticket->IV, IV_SIZE);
116 length16 = _gnutls_conv_uint16 (ticket->encrypted_state_len);
117 _gnutls_hmac (&digest_hd, &length16, 2);
118 _gnutls_hmac (&digest_hd, ticket->encrypted_state,
119 ticket->encrypted_state_len);
120 _gnutls_hmac_deinit (&digest_hd, digest);
122 return 0;
125 static int
126 decrypt_ticket (gnutls_session_t session, session_ticket_ext_st * priv,
127 struct ticket *ticket)
129 cipher_hd_st cipher_hd;
130 gnutls_datum_t key, IV, mac_secret, state;
131 opaque final[MAC_SECRET_SIZE];
132 time_t timestamp = gnutls_time (0);
133 int ret;
135 /* Check the integrity of ticket using HMAC-SHA-256. */
136 mac_secret.data = (void *) priv->key.mac_secret;
137 mac_secret.size = MAC_SECRET_SIZE;
138 ret = digest_ticket (&mac_secret, ticket, final);
139 if (ret < 0)
141 gnutls_assert ();
142 return ret;
145 if (memcmp (ticket->mac, final, MAC_SIZE))
147 gnutls_assert ();
148 return GNUTLS_E_DECRYPTION_FAILED;
151 /* Decrypt encrypted_state using 128-bit AES in CBC mode. */
152 key.data = (void *) priv->key.key;
153 key.size = KEY_SIZE;
154 IV.data = ticket->IV;
155 IV.size = IV_SIZE;
156 ret =
157 _gnutls_cipher_init (&cipher_hd, GNUTLS_CIPHER_AES_128_CBC, &key, &IV);
158 if (ret < 0)
160 gnutls_assert ();
161 return ret;
163 ret = _gnutls_cipher_decrypt (&cipher_hd, ticket->encrypted_state,
164 ticket->encrypted_state_len);
165 _gnutls_cipher_deinit (&cipher_hd);
166 if (ret < 0)
168 gnutls_assert ();
169 return ret;
172 /* Unpack security parameters. */
173 state.data = ticket->encrypted_state;
174 state.size = ticket->encrypted_state_len;
175 ret = _gnutls_session_unpack (session, &state);
176 if (ret < 0)
178 gnutls_assert ();
179 return ret;
182 if (timestamp - session->internals.resumed_security_parameters.timestamp >
183 session->internals.expire_time
184 || session->internals.resumed_security_parameters.timestamp > timestamp)
186 gnutls_assert ();
187 return GNUTLS_E_EXPIRED;
190 session->internals.resumed = RESUME_TRUE;
192 return 0;
195 static int
196 encrypt_ticket (gnutls_session_t session, session_ticket_ext_st * priv,
197 struct ticket *ticket)
199 cipher_hd_st cipher_hd;
200 gnutls_datum_t key, IV, mac_secret, state, encrypted_state;
201 int blocksize;
202 int ret;
204 /* Pack security parameters. */
205 ret = _gnutls_session_pack (session, &state);
206 if (ret < 0)
208 gnutls_assert ();
209 return ret;
211 blocksize = gnutls_cipher_get_block_size (GNUTLS_CIPHER_AES_128_CBC);
213 encrypted_state.size =
214 ((state.size + blocksize - 1) / blocksize) * blocksize;
215 encrypted_state.data = gnutls_malloc (encrypted_state.size);
216 if (!encrypted_state.data)
218 gnutls_assert ();
219 _gnutls_free_datum (&state);
220 return GNUTLS_E_MEMORY_ERROR;
222 memset (encrypted_state.data, 0, encrypted_state.size);
223 memcpy (encrypted_state.data, state.data, state.size);
224 _gnutls_free_datum (&state);
226 /* Encrypt state using 128-bit AES in CBC mode. */
227 key.data = (void *) priv->key.key;
228 key.size = KEY_SIZE;
229 IV.data = priv->session_ticket_IV;
230 IV.size = IV_SIZE;
231 ret =
232 _gnutls_cipher_init (&cipher_hd, GNUTLS_CIPHER_AES_128_CBC, &key, &IV);
233 if (ret < 0)
235 gnutls_assert ();
236 _gnutls_free_datum (&encrypted_state);
237 return ret;
240 ret = _gnutls_cipher_encrypt (&cipher_hd, encrypted_state.data,
241 encrypted_state.size);
242 _gnutls_cipher_deinit (&cipher_hd);
243 if (ret < 0)
245 gnutls_assert ();
246 _gnutls_free_datum (&encrypted_state);
247 return ret;
250 /* Fill the ticket structure to compute MAC. */
251 memcpy (ticket->key_name, priv->key.key_name, KEY_NAME_SIZE);
252 memcpy (ticket->IV, IV.data, IV.size);
253 ticket->encrypted_state_len = encrypted_state.size;
254 ticket->encrypted_state = encrypted_state.data;
256 mac_secret.data = priv->key.mac_secret;
257 mac_secret.size = MAC_SECRET_SIZE;
258 ret = digest_ticket (&mac_secret, ticket, ticket->mac);
259 if (ret < 0)
261 gnutls_assert ();
262 _gnutls_free_datum (&encrypted_state);
263 return ret;
266 return 0;
269 static int
270 session_ticket_recv_params (gnutls_session_t session,
271 const opaque * data, size_t _data_size)
273 ssize_t data_size = _data_size;
274 session_ticket_ext_st *priv = NULL;
275 extension_priv_data_t epriv;
276 int ret;
278 ret =
279 _gnutls_ext_get_session_data (session, GNUTLS_EXTENSION_SESSION_TICKET,
280 &epriv);
281 if (ret < 0)
283 return 0;
285 priv = epriv.ptr;
287 if (!priv->session_ticket_enable)
288 return 0;
290 if (session->security_parameters.entity == GNUTLS_SERVER)
292 struct ticket ticket;
293 const opaque *encrypted_state;
294 int ret;
296 /* The client requested a new session ticket. */
297 if (data_size == 0)
299 priv->session_ticket_renew = 1;
300 return 0;
303 DECR_LEN (data_size, KEY_NAME_SIZE);
304 memcpy (ticket.key_name, data, KEY_NAME_SIZE);
305 data += KEY_NAME_SIZE;
307 /* If the key name of the ticket does not match the one that we
308 hold, issue a new ticket. */
309 if (memcmp (ticket.key_name, priv->key.key_name, KEY_NAME_SIZE))
311 priv->session_ticket_renew = 1;
312 return 0;
315 DECR_LEN (data_size, IV_SIZE);
316 memcpy (ticket.IV, data, IV_SIZE);
317 data += IV_SIZE;
319 DECR_LEN (data_size, 2);
320 ticket.encrypted_state_len = _gnutls_read_uint16 (data);
321 data += 2;
323 encrypted_state = data;
325 DECR_LEN (data_size, ticket.encrypted_state_len);
326 data += ticket.encrypted_state_len;
328 DECR_LEN (data_size, MAC_SIZE);
329 memcpy (ticket.mac, data, MAC_SIZE);
331 ticket.encrypted_state = gnutls_malloc (ticket.encrypted_state_len);
332 if (!ticket.encrypted_state)
334 gnutls_assert ();
335 return GNUTLS_E_MEMORY_ERROR;
337 memcpy (ticket.encrypted_state, encrypted_state,
338 ticket.encrypted_state_len);
340 ret = decrypt_ticket (session, priv, &ticket);
341 gnutls_free (ticket.encrypted_state);
342 if (ret < 0)
344 priv->session_ticket_renew = 1;
345 return 0;
348 else /* Client */
350 if (data_size == 0)
352 priv->session_ticket_renew = 1;
353 return 0;
357 return 0;
360 /* returns a positive number if we send the extension data, zero if we
361 do not want to send it, and a negative number on failure.
363 static int
364 session_ticket_send_params (gnutls_session_t session,
365 opaque * data, size_t _data_size)
367 ssize_t data_size = _data_size;
368 session_ticket_ext_st *priv = NULL;
369 extension_priv_data_t epriv;
370 int ret;
372 ret =
373 _gnutls_ext_get_session_data (session, GNUTLS_EXTENSION_SESSION_TICKET,
374 &epriv);
375 if (ret >= 0)
376 priv = epriv.ptr;
378 if (priv == NULL || !priv->session_ticket_enable)
379 return 0;
381 if (session->security_parameters.entity == GNUTLS_SERVER)
383 if (priv && priv->session_ticket_renew)
385 return GNUTLS_E_INT_RET_0;
388 else
390 ret =
391 _gnutls_ext_get_resumed_session_data (session,
392 GNUTLS_EXTENSION_SESSION_TICKET,
393 &epriv);
394 if (ret >= 0)
395 priv = epriv.ptr;
397 /* no previous data. Just advertize it */
398 if (ret < 0)
399 return GNUTLS_E_INT_RET_0;
401 /* previous data had session tickets disabled. Don't advertize. Ignore. */
402 if (!priv->session_ticket_enable)
403 return 0;
405 if (priv->session_ticket_len > 0)
407 DECR_LENGTH_RET (data_size, priv->session_ticket_len,
408 GNUTLS_E_SHORT_MEMORY_BUFFER);
409 memcpy (data, priv->session_ticket, priv->session_ticket_len);
411 return priv->session_ticket_len;
414 return 0;
418 static void
419 session_ticket_deinit_data (extension_priv_data_t epriv)
421 session_ticket_ext_st *priv = epriv.ptr;
423 gnutls_free (priv->session_ticket);
424 gnutls_free (priv);
427 static int
428 session_ticket_pack (extension_priv_data_t epriv, gnutls_buffer_st * ps)
430 session_ticket_ext_st *priv = epriv.ptr;
431 int ret;
433 BUFFER_APPEND_PFX (ps, priv->session_ticket, priv->session_ticket_len);
434 BUFFER_APPEND_NUM (ps, priv->session_ticket_enable);
436 return 0;
439 static int
440 session_ticket_unpack (gnutls_buffer_st * ps, extension_priv_data_t * _priv)
442 session_ticket_ext_st *priv = NULL;
443 int ret;
444 extension_priv_data_t epriv;
445 gnutls_datum ticket;
447 priv = gnutls_calloc (1, sizeof (*priv));
448 if (priv == NULL)
450 gnutls_assert ();
451 return GNUTLS_E_MEMORY_ERROR;
454 BUFFER_POP_DATUM (ps, &ticket);
455 priv->session_ticket = ticket.data;
456 priv->session_ticket_len = ticket.size;
457 BUFFER_POP_NUM (ps, priv->session_ticket_enable);
459 epriv.ptr = priv;
460 *_priv = epriv;
462 return 0;
464 error:
465 gnutls_free (priv);
466 return ret;
472 * gnutls_session_ticket_key_generate:
473 * @key: is a pointer to a #gnutls_datum_t which will contain a newly
474 * created key.
476 * Generate a random key to encrypt security parameters within
477 * SessionTicket.
479 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
480 * error code.
482 * Since: 2.10.0
485 gnutls_session_ticket_key_generate (gnutls_datum_t * key)
487 int ret;
489 key->size = sizeof (struct gnutls_session_ticket_key_st);
490 key->data = gnutls_malloc (key->size);
491 if (!key->data)
493 gnutls_assert ();
494 return GNUTLS_E_MEMORY_ERROR;
497 ret = _gnutls_rnd (GNUTLS_RND_RANDOM, key->data, key->size);
498 if (ret < 0)
500 gnutls_assert ();
501 _gnutls_free_datum (key);
502 return ret;
505 return 0;
509 * gnutls_session_ticket_enable_client:
510 * @session: is a #gnutls_session_t structure.
512 * Request that the client should attempt session resumption using
513 * SessionTicket.
515 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
516 * error code.
518 * Since: 2.10.0
521 gnutls_session_ticket_enable_client (gnutls_session_t session)
523 session_ticket_ext_st *priv = NULL;
524 extension_priv_data_t epriv;
526 if (!session)
528 gnutls_assert ();
529 return GNUTLS_E_INVALID_REQUEST;
532 priv = gnutls_calloc (1, sizeof (*priv));
533 if (priv == NULL)
535 gnutls_assert ();
536 return GNUTLS_E_MEMORY_ERROR;
538 priv->session_ticket_enable = 1;
539 epriv.ptr = priv;
541 _gnutls_ext_set_session_data (session,
542 GNUTLS_EXTENSION_SESSION_TICKET, epriv);
544 return 0;
548 * gnutls_session_ticket_enable_server:
549 * @session: is a #gnutls_session_t structure.
550 * @key: key to encrypt session parameters.
552 * Request that the server should attempt session resumption using
553 * SessionTicket. @key must be initialized with
554 * gnutls_session_ticket_key_generate().
556 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
557 * error code.
559 * Since: 2.10.0
562 gnutls_session_ticket_enable_server (gnutls_session_t session,
563 const gnutls_datum_t * key)
565 int ret;
566 session_ticket_ext_st *priv = NULL;
567 extension_priv_data_t epriv;
569 if (!session || !key
570 || key->size != sizeof (struct gnutls_session_ticket_key_st))
572 gnutls_assert ();
573 return GNUTLS_E_INVALID_REQUEST;
576 priv = gnutls_calloc (1, sizeof (*priv));
577 if (priv == NULL)
579 gnutls_assert ();
580 return GNUTLS_E_MEMORY_ERROR;
582 epriv.ptr = priv;
584 ret = _gnutls_rnd (GNUTLS_RND_RANDOM, priv->session_ticket_IV, IV_SIZE);
585 if (ret < 0)
587 gnutls_assert ();
588 return ret;
591 memcpy (&priv->key, key->data, key->size);
592 priv->session_ticket_enable = 1;
594 _gnutls_ext_set_session_data (session,
595 GNUTLS_EXTENSION_SESSION_TICKET, epriv);
597 return 0;
601 _gnutls_send_new_session_ticket (gnutls_session_t session, int again)
603 mbuffer_st *bufel = NULL;
604 uint8_t *data = NULL, *p;
605 int data_size = 0;
606 int ret;
607 struct ticket ticket;
608 uint16_t ticket_len;
609 session_ticket_ext_st *priv = NULL;
610 extension_priv_data_t epriv;
611 uint16_t epoch_saved = session->security_parameters.epoch_write;
613 if (again == 0)
615 ret =
616 _gnutls_ext_get_session_data (session,
617 GNUTLS_EXTENSION_SESSION_TICKET,
618 &epriv);
619 if (ret < 0)
620 return 0;
621 priv = epriv.ptr;
623 if (!priv->session_ticket_renew)
624 return 0;
626 /* XXX: Temporarily set write algorithms to be used.
627 _gnutls_write_connection_state_init() does this job, but it also
628 triggers encryption, while NewSessionTicket should not be
629 encrypted in the record layer. */
630 ret =
631 _gnutls_epoch_set_keys (session,
632 session->security_parameters.epoch_next);
633 if (ret < 0)
635 gnutls_assert ();
636 return ret;
639 session->security_parameters.epoch_write =
640 session->security_parameters.epoch_next;
642 ret = encrypt_ticket (session, priv, &ticket);
643 session->security_parameters.epoch_write = epoch_saved;
644 if (ret < 0)
646 gnutls_assert ();
647 return ret;
650 ticket_len = KEY_NAME_SIZE + IV_SIZE + 2 + ticket.encrypted_state_len
651 + MAC_SIZE;
653 bufel =
654 _gnutls_handshake_alloc (4 + 2 + ticket_len, 4 + 2 + ticket_len);
655 if (!bufel)
657 gnutls_assert ();
658 gnutls_free (ticket.encrypted_state);
659 return GNUTLS_E_MEMORY_ERROR;
662 data = _mbuffer_get_udata_ptr (bufel);
663 p = data;
665 _gnutls_write_uint32 (session->internals.expire_time, p);
666 p += 4;
668 _gnutls_write_uint16 (ticket_len, p);
669 p += 2;
671 memcpy (p, ticket.key_name, KEY_NAME_SIZE);
672 p += KEY_NAME_SIZE;
674 memcpy (p, ticket.IV, IV_SIZE);
675 p += IV_SIZE;
677 _gnutls_write_uint16 (ticket.encrypted_state_len, p);
678 p += 2;
680 memcpy (p, ticket.encrypted_state, ticket.encrypted_state_len);
681 gnutls_free (ticket.encrypted_state);
682 p += ticket.encrypted_state_len;
684 memcpy (p, ticket.mac, MAC_SIZE);
685 p += MAC_SIZE;
687 data_size = p - data;
689 ret = _gnutls_send_handshake (session, data_size ? bufel : NULL,
690 GNUTLS_HANDSHAKE_NEW_SESSION_TICKET);
692 return ret;
696 _gnutls_recv_new_session_ticket (gnutls_session_t session)
698 uint8_t *data = NULL, *p;
699 int data_size;
700 uint16_t ticket_len;
701 int ret;
702 session_ticket_ext_st *priv = NULL;
703 extension_priv_data_t epriv;
705 ret =
706 _gnutls_ext_get_session_data (session, GNUTLS_EXTENSION_SESSION_TICKET,
707 &epriv);
708 if (ret < 0)
710 gnutls_assert ();
711 return 0;
713 priv = epriv.ptr;
715 if (!priv->session_ticket_renew)
716 return 0;
718 ret = _gnutls_recv_handshake (session, &data, &data_size,
719 GNUTLS_HANDSHAKE_NEW_SESSION_TICKET,
720 MANDATORY_PACKET);
721 if (ret < 0)
723 gnutls_assert ();
724 return ret;
727 p = data;
728 DECR_LENGTH_COM (data_size, 4, goto error);
729 _gnutls_read_uint32 (p);
730 p += 4;
732 DECR_LENGTH_COM (data_size, 2, goto error);
733 ticket_len = _gnutls_read_uint16 (p);
734 p += 2;
736 DECR_LENGTH_COM (data_size, ticket_len, goto error);
737 priv->session_ticket = gnutls_realloc (priv->session_ticket, ticket_len);
738 if (!priv->session_ticket)
740 gnutls_assert ();
741 gnutls_free (data);
742 return GNUTLS_E_MEMORY_ERROR;
744 memcpy (priv->session_ticket, p, ticket_len);
745 gnutls_free (data);
746 priv->session_ticket_len = ticket_len;
748 /* Discard the current session ID. (RFC5077 3.4) */
749 ret = _gnutls_generate_session_id (session->security_parameters.session_id,
750 &session->
751 security_parameters.session_id_size);
752 if (ret < 0)
754 gnutls_assert ();
755 gnutls_free (priv->session_ticket);
756 priv->session_ticket = NULL;
757 return GNUTLS_E_INTERNAL_ERROR;
759 return 0;
761 error:
762 gnutls_free (data);
763 return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
766 #endif