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
;