2 * Copyright (C) 2009, 2010 Free Software Foundation, Inc.
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,
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>
34 #include <ext_session_ticket.h>
35 #include <gnutls_mbuffers.h>
36 #include <gnutls_extensions.h>
37 #include <gnutls_constate.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
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];
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;
92 opaque key_name[KEY_NAME_SIZE];
94 opaque *encrypted_state;
95 uint16_t encrypted_state_len;
100 digest_ticket (const gnutls_datum_t * key, struct ticket *ticket,
103 digest_hd_st digest_hd;
107 ret = _gnutls_hmac_init (&digest_hd, GNUTLS_MAC_SHA256, key->data,
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);
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);
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);
145 if (memcmp (ticket->mac, final, MAC_SIZE))
148 return GNUTLS_E_DECRYPTION_FAILED;
151 /* Decrypt encrypted_state using 128-bit AES in CBC mode. */
152 key.data = (void *) priv->key.key;
154 IV.data = ticket->IV;
157 _gnutls_cipher_init (&cipher_hd, GNUTLS_CIPHER_AES_128_CBC, &key, &IV);
163 ret = _gnutls_cipher_decrypt (&cipher_hd, ticket->encrypted_state,
164 ticket->encrypted_state_len);
165 _gnutls_cipher_deinit (&cipher_hd);
172 /* Unpack security parameters. */
173 state.data = ticket->encrypted_state;
174 state.size = ticket->encrypted_state_len;
175 ret = _gnutls_session_unpack (session, &state);
182 if (timestamp - session->internals.resumed_security_parameters.timestamp >
183 session->internals.expire_time
184 || session->internals.resumed_security_parameters.timestamp > timestamp)
187 return GNUTLS_E_EXPIRED;
190 session->internals.resumed = RESUME_TRUE;
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;
204 /* Pack security parameters. */
205 ret = _gnutls_session_pack (session, &state);
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)
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;
229 IV.data = priv->session_ticket_IV;
232 _gnutls_cipher_init (&cipher_hd, GNUTLS_CIPHER_AES_128_CBC, &key, &IV);
236 _gnutls_free_datum (&encrypted_state);
240 ret = _gnutls_cipher_encrypt (&cipher_hd, encrypted_state.data,
241 encrypted_state.size);
242 _gnutls_cipher_deinit (&cipher_hd);
246 _gnutls_free_datum (&encrypted_state);
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);
262 _gnutls_free_datum (&encrypted_state);
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;
279 _gnutls_ext_get_session_data (session, GNUTLS_EXTENSION_SESSION_TICKET,
287 if (!priv->session_ticket_enable)
290 if (session->security_parameters.entity == GNUTLS_SERVER)
292 struct ticket ticket;
293 const opaque *encrypted_state;
296 /* The client requested a new session ticket. */
299 priv->session_ticket_renew = 1;
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;
315 DECR_LEN (data_size, IV_SIZE);
316 memcpy (ticket.IV, data, IV_SIZE);
319 DECR_LEN (data_size, 2);
320 ticket.encrypted_state_len = _gnutls_read_uint16 (data);
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)
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);
344 priv->session_ticket_renew = 1;
352 priv->session_ticket_renew = 1;
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.
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;
373 _gnutls_ext_get_session_data (session, GNUTLS_EXTENSION_SESSION_TICKET,
378 if (priv == NULL || !priv->session_ticket_enable)
381 if (session->security_parameters.entity == GNUTLS_SERVER)
383 if (priv && priv->session_ticket_renew)
385 return GNUTLS_E_INT_RET_0;
391 _gnutls_ext_get_resumed_session_data (session,
392 GNUTLS_EXTENSION_SESSION_TICKET,
397 /* no previous data. Just advertize it */
399 return GNUTLS_E_INT_RET_0;
401 /* previous data had session tickets disabled. Don't advertize. Ignore. */
402 if (!priv->session_ticket_enable)
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;
419 session_ticket_deinit_data (extension_priv_data_t epriv)
421 session_ticket_ext_st *priv = epriv.ptr;
423 gnutls_free (priv->session_ticket);
428 session_ticket_pack (extension_priv_data_t epriv, gnutls_buffer_st * ps)
430 session_ticket_ext_st *priv = epriv.ptr;
433 BUFFER_APPEND_PFX (ps, priv->session_ticket, priv->session_ticket_len);
434 BUFFER_APPEND_NUM (ps, priv->session_ticket_enable);
440 session_ticket_unpack (gnutls_buffer_st * ps, extension_priv_data_t * _priv)
442 session_ticket_ext_st *priv = NULL;
444 extension_priv_data_t epriv;
447 priv = gnutls_calloc (1, sizeof (*priv));
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);
472 * gnutls_session_ticket_key_generate:
473 * @key: is a pointer to a #gnutls_datum_t which will contain a newly
476 * Generate a random key to encrypt security parameters within
479 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
485 gnutls_session_ticket_key_generate (gnutls_datum_t * key)
489 key->size = sizeof (struct gnutls_session_ticket_key_st);
490 key->data = gnutls_malloc (key->size);
494 return GNUTLS_E_MEMORY_ERROR;
497 ret = _gnutls_rnd (GNUTLS_RND_RANDOM, key->data, key->size);
501 _gnutls_free_datum (key);
509 * gnutls_session_ticket_enable_client:
510 * @session: is a #gnutls_session_t structure.
512 * Request that the client should attempt session resumption using
515 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
521 gnutls_session_ticket_enable_client (gnutls_session_t session)
523 session_ticket_ext_st *priv = NULL;
524 extension_priv_data_t epriv;
529 return GNUTLS_E_INVALID_REQUEST;
532 priv = gnutls_calloc (1, sizeof (*priv));
536 return GNUTLS_E_MEMORY_ERROR;
538 priv->session_ticket_enable = 1;
541 _gnutls_ext_set_session_data (session,
542 GNUTLS_EXTENSION_SESSION_TICKET, epriv);
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
562 gnutls_session_ticket_enable_server (gnutls_session_t session,
563 const gnutls_datum_t * key)
566 session_ticket_ext_st *priv = NULL;
567 extension_priv_data_t epriv;
570 || key->size != sizeof (struct gnutls_session_ticket_key_st))
573 return GNUTLS_E_INVALID_REQUEST;
576 priv = gnutls_calloc (1, sizeof (*priv));
580 return GNUTLS_E_MEMORY_ERROR;
584 ret = _gnutls_rnd (GNUTLS_RND_RANDOM, priv->session_ticket_IV, IV_SIZE);
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);
601 _gnutls_send_new_session_ticket (gnutls_session_t session, int again)
603 mbuffer_st *bufel = NULL;
604 uint8_t *data = NULL, *p;
607 struct ticket ticket;
609 session_ticket_ext_st *priv = NULL;
610 extension_priv_data_t epriv;
611 uint16_t epoch_saved = session->security_parameters.epoch_write;
616 _gnutls_ext_get_session_data (session,
617 GNUTLS_EXTENSION_SESSION_TICKET,
623 if (!priv->session_ticket_renew)
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. */
631 _gnutls_epoch_set_keys (session,
632 session->security_parameters.epoch_next);
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;
650 ticket_len = KEY_NAME_SIZE + IV_SIZE + 2 + ticket.encrypted_state_len
654 _gnutls_handshake_alloc (4 + 2 + ticket_len, 4 + 2 + ticket_len);
658 gnutls_free (ticket.encrypted_state);
659 return GNUTLS_E_MEMORY_ERROR;
662 data = _mbuffer_get_udata_ptr (bufel);
665 _gnutls_write_uint32 (session->internals.expire_time, p);
668 _gnutls_write_uint16 (ticket_len, p);
671 memcpy (p, ticket.key_name, KEY_NAME_SIZE);
674 memcpy (p, ticket.IV, IV_SIZE);
677 _gnutls_write_uint16 (ticket.encrypted_state_len, p);
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);
687 data_size = p - data;
689 ret = _gnutls_send_handshake (session, data_size ? bufel : NULL,
690 GNUTLS_HANDSHAKE_NEW_SESSION_TICKET);
696 _gnutls_recv_new_session_ticket (gnutls_session_t session)
698 uint8_t *data = NULL, *p;
702 session_ticket_ext_st *priv = NULL;
703 extension_priv_data_t epriv;
706 _gnutls_ext_get_session_data (session, GNUTLS_EXTENSION_SESSION_TICKET,
715 if (!priv->session_ticket_renew)
718 ret = _gnutls_recv_handshake (session, &data, &data_size,
719 GNUTLS_HANDSHAKE_NEW_SESSION_TICKET,
728 DECR_LENGTH_COM (data_size, 4, goto error);
729 _gnutls_read_uint32 (p);
732 DECR_LENGTH_COM (data_size, 2, goto error);
733 ticket_len = _gnutls_read_uint16 (p);
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)
742 return GNUTLS_E_MEMORY_ERROR;
744 memcpy (priv->session_ticket, p, ticket_len);
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,
751 security_parameters.session_id_size);
755 gnutls_free (priv->session_ticket);
756 priv->session_ticket = NULL;
757 return GNUTLS_E_INTERNAL_ERROR;
763 return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;