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>
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
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
];
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
;
91 opaque key_name
[KEY_NAME_SIZE
];
93 opaque
*encrypted_state
;
94 uint16_t encrypted_state_len
;
99 digest_ticket (const gnutls_datum_t
* key
, struct ticket
*ticket
,
102 digest_hd_st digest_hd
;
106 ret
= _gnutls_hmac_init (&digest_hd
, GNUTLS_MAC_SHA256
, key
->data
,
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
);
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);
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
);
144 if (memcmp (ticket
->mac
, final
, MAC_SIZE
))
147 return GNUTLS_E_DECRYPTION_FAILED
;
150 /* Decrypt encrypted_state using 128-bit AES in CBC mode. */
151 key
.data
= (void *) priv
->key
.key
;
153 IV
.data
= ticket
->IV
;
156 _gnutls_cipher_init (&cipher_hd
, GNUTLS_CIPHER_AES_128_CBC
, &key
, &IV
);
162 ret
= _gnutls_cipher_decrypt (&cipher_hd
, ticket
->encrypted_state
,
163 ticket
->encrypted_state_len
);
164 _gnutls_cipher_deinit (&cipher_hd
);
171 /* Unpack security parameters. */
172 state
.data
= ticket
->encrypted_state
;
173 state
.size
= ticket
->encrypted_state_len
;
174 ret
= _gnutls_session_unpack (session
, &state
);
181 if (timestamp
- session
->internals
.resumed_security_parameters
.timestamp
>
182 session
->internals
.expire_time
183 || session
->internals
.resumed_security_parameters
.timestamp
> timestamp
)
186 return GNUTLS_E_EXPIRED
;
189 session
->internals
.resumed
= RESUME_TRUE
;
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
;
203 /* Pack security parameters. */
204 ret
= _gnutls_session_pack (session
, &state
);
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
)
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
;
228 IV
.data
= priv
->session_ticket_IV
;
231 _gnutls_cipher_init (&cipher_hd
, GNUTLS_CIPHER_AES_128_CBC
, &key
, &IV
);
235 _gnutls_free_datum (&encrypted_state
);
239 ret
= _gnutls_cipher_encrypt (&cipher_hd
, encrypted_state
.data
,
240 encrypted_state
.size
);
241 _gnutls_cipher_deinit (&cipher_hd
);
245 _gnutls_free_datum (&encrypted_state
);
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
);
261 _gnutls_free_datum (&encrypted_state
);
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
;
278 _gnutls_ext_get_session_data (session
, GNUTLS_EXTENSION_SESSION_TICKET
,
286 if (!priv
->session_ticket_enable
)
289 if (session
->security_parameters
.entity
== GNUTLS_SERVER
)
291 struct ticket ticket
;
292 const opaque
*encrypted_state
;
295 /* The client requested a new session ticket. */
298 priv
->session_ticket_renew
= 1;
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;
314 DECR_LEN (data_size
, IV_SIZE
);
315 memcpy (ticket
.IV
, data
, IV_SIZE
);
318 DECR_LEN (data_size
, 2);
319 ticket
.encrypted_state_len
= _gnutls_read_uint16 (data
);
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
)
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
);
343 priv
->session_ticket_renew
= 1;
351 priv
->session_ticket_renew
= 1;
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.
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
;
371 _gnutls_ext_get_session_data (session
, GNUTLS_EXTENSION_SESSION_TICKET
,
376 if (priv
== NULL
|| !priv
->session_ticket_enable
)
379 if (session
->security_parameters
.entity
== GNUTLS_SERVER
)
381 if (priv
&& priv
->session_ticket_renew
)
383 return GNUTLS_E_INT_RET_0
;
389 _gnutls_ext_get_resumed_session_data (session
,
390 GNUTLS_EXTENSION_SESSION_TICKET
,
395 /* no previous data. Just advertize it */
397 return GNUTLS_E_INT_RET_0
;
399 /* previous data had session tickets disabled. Don't advertize. Ignore. */
400 if (!priv
->session_ticket_enable
)
403 if (priv
->session_ticket_len
> 0)
405 ret
= _gnutls_buffer_append_data( extdata
, priv
->session_ticket
, priv
->session_ticket_len
);
407 return gnutls_assert_val(ret
);
409 return priv
->session_ticket_len
;
417 session_ticket_deinit_data (extension_priv_data_t epriv
)
419 session_ticket_ext_st
*priv
= epriv
.ptr
;
421 gnutls_free (priv
->session_ticket
);
426 session_ticket_pack (extension_priv_data_t epriv
, gnutls_buffer_st
* ps
)
428 session_ticket_ext_st
*priv
= epriv
.ptr
;
431 BUFFER_APPEND_PFX (ps
, priv
->session_ticket
, priv
->session_ticket_len
);
432 BUFFER_APPEND_NUM (ps
, priv
->session_ticket_enable
);
438 session_ticket_unpack (gnutls_buffer_st
* ps
, extension_priv_data_t
* _priv
)
440 session_ticket_ext_st
*priv
= NULL
;
442 extension_priv_data_t epriv
;
445 priv
= gnutls_calloc (1, sizeof (*priv
));
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
);
470 * gnutls_session_ticket_key_generate:
471 * @key: is a pointer to a #gnutls_datum_t which will contain a newly
474 * Generate a random key to encrypt security parameters within
477 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
483 gnutls_session_ticket_key_generate (gnutls_datum_t
* key
)
487 key
->size
= sizeof (struct gnutls_session_ticket_key_st
);
488 key
->data
= gnutls_malloc (key
->size
);
492 return GNUTLS_E_MEMORY_ERROR
;
495 ret
= _gnutls_rnd (GNUTLS_RND_RANDOM
, key
->data
, key
->size
);
499 _gnutls_free_datum (key
);
507 * gnutls_session_ticket_enable_client:
508 * @session: is a #gnutls_session_t structure.
510 * Request that the client should attempt session resumption using
513 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
519 gnutls_session_ticket_enable_client (gnutls_session_t session
)
521 session_ticket_ext_st
*priv
= NULL
;
522 extension_priv_data_t epriv
;
527 return GNUTLS_E_INVALID_REQUEST
;
530 priv
= gnutls_calloc (1, sizeof (*priv
));
534 return GNUTLS_E_MEMORY_ERROR
;
536 priv
->session_ticket_enable
= 1;
539 _gnutls_ext_set_session_data (session
,
540 GNUTLS_EXTENSION_SESSION_TICKET
, epriv
);
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
560 gnutls_session_ticket_enable_server (gnutls_session_t session
,
561 const gnutls_datum_t
* key
)
564 session_ticket_ext_st
*priv
= NULL
;
565 extension_priv_data_t epriv
;
568 || key
->size
!= sizeof (struct gnutls_session_ticket_key_st
))
571 return GNUTLS_E_INVALID_REQUEST
;
574 priv
= gnutls_calloc (1, sizeof (*priv
));
578 return GNUTLS_E_MEMORY_ERROR
;
582 ret
= _gnutls_rnd (GNUTLS_RND_RANDOM
, priv
->session_ticket_IV
, IV_SIZE
);
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
);
599 _gnutls_send_new_session_ticket (gnutls_session_t session
, int again
)
601 mbuffer_st
*bufel
= NULL
;
602 uint8_t *data
= NULL
, *p
;
605 struct ticket ticket
;
607 session_ticket_ext_st
*priv
= NULL
;
608 extension_priv_data_t epriv
;
609 uint16_t epoch_saved
= session
->security_parameters
.epoch_write
;
614 _gnutls_ext_get_session_data (session
,
615 GNUTLS_EXTENSION_SESSION_TICKET
,
621 if (!priv
->session_ticket_renew
)
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. */
629 _gnutls_epoch_set_keys (session
,
630 session
->security_parameters
.epoch_next
);
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
;
648 ticket_len
= KEY_NAME_SIZE
+ IV_SIZE
+ 2 + ticket
.encrypted_state_len
651 bufel
= _gnutls_handshake_alloc (session
, 4 + 2 + ticket_len
, 4+2+ticket_len
);
655 gnutls_free (ticket
.encrypted_state
);
656 return GNUTLS_E_MEMORY_ERROR
;
659 data
= _mbuffer_get_udata_ptr (bufel
);
662 _gnutls_write_uint32 (session
->internals
.expire_time
, p
);
665 _gnutls_write_uint16 (ticket_len
, p
);
668 memcpy (p
, ticket
.key_name
, KEY_NAME_SIZE
);
671 memcpy (p
, ticket
.IV
, IV_SIZE
);
674 _gnutls_write_uint16 (ticket
.encrypted_state_len
, p
);
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
);
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
;
695 uint32_t lifetime_hint
;
698 session_ticket_ext_st
*priv
= NULL
;
699 extension_priv_data_t epriv
;
702 _gnutls_ext_get_session_data (session
, GNUTLS_EXTENSION_SESSION_TICKET
,
711 if (!priv
->session_ticket_renew
)
714 ret
= _gnutls_recv_handshake (session
, &data
, &data_size
,
715 GNUTLS_HANDSHAKE_NEW_SESSION_TICKET
,
724 DECR_LENGTH_COM (data_size
, 4, goto error
);
725 lifetime_hint
= _gnutls_read_uint32 (p
);
728 DECR_LENGTH_COM (data_size
, 2, goto error
);
729 ticket_len
= _gnutls_read_uint16 (p
);
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
)
738 return GNUTLS_E_MEMORY_ERROR
;
740 memcpy (priv
->session_ticket
, p
, ticket_len
);
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
,
747 security_parameters
.session_id_size
);
751 gnutls_free (priv
->session_ticket
);
752 priv
->session_ticket
= NULL
;
753 return GNUTLS_E_INTERNAL_ERROR
;
759 return GNUTLS_E_UNEXPECTED_PACKET_LENGTH
;