2 * Copyright (C) 2009-2012 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 3 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 License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>
23 #include <gnutls_int.h>
24 #include <gnutls_errors.h>
25 #include <gnutls_datum.h>
26 #include <algorithms.h>
27 #include <gnutls_handshake.h>
28 #include <gnutls_num.h>
29 #include <gnutls_constate.h>
30 #include <gnutls_session_pack.h>
32 #include <ext/session_ticket.h>
33 #include <gnutls_mbuffers.h>
34 #include <gnutls_extensions.h>
35 #include <gnutls_constate.h>
37 #define KEY_NAME_SIZE SESSION_TICKET_KEY_NAME_SIZE
38 #define KEY_SIZE SESSION_TICKET_KEY_SIZE
39 #define IV_SIZE SESSION_TICKET_IV_SIZE
40 #define MAC_SECRET_SIZE SESSION_TICKET_MAC_SECRET_SIZE
44 static int session_ticket_recv_params (gnutls_session_t session
,
45 const uint8_t * data
, size_t data_size
);
46 static int session_ticket_send_params (gnutls_session_t session
,
47 gnutls_buffer_st
* extdata
);
48 static int session_ticket_unpack (gnutls_buffer_st
* ps
,
49 extension_priv_data_t
* _priv
);
50 static int session_ticket_pack (extension_priv_data_t _priv
,
51 gnutls_buffer_st
* ps
);
52 static void session_ticket_deinit_data (extension_priv_data_t priv
);
54 extension_entry_st ext_mod_session_ticket
= {
55 .name
= "SESSION TICKET",
56 .type
= GNUTLS_EXTENSION_SESSION_TICKET
,
57 .parse_type
= GNUTLS_EXT_MANDATORY
,
59 .recv_func
= session_ticket_recv_params
,
60 .send_func
= session_ticket_send_params
,
61 .pack_func
= session_ticket_pack
,
62 .unpack_func
= session_ticket_unpack
,
63 .deinit_func
= session_ticket_deinit_data
,
66 #define SESSION_KEY_SIZE (SESSION_TICKET_KEY_NAME_SIZE+SESSION_TICKET_KEY_SIZE+SESSION_TICKET_MAC_SECRET_SIZE)
68 #define KEY_POS (SESSION_TICKET_KEY_NAME_SIZE)
69 #define MAC_SECRET_POS (SESSION_TICKET_KEY_NAME_SIZE+SESSION_TICKET_KEY_SIZE)
73 int session_ticket_enable
;
74 int session_ticket_renew
;
75 uint8_t session_ticket_IV
[SESSION_TICKET_IV_SIZE
];
77 uint8_t *session_ticket
;
78 int session_ticket_len
;
80 uint8_t key
[SESSION_KEY_SIZE
];
81 } session_ticket_ext_st
;
85 uint8_t key_name
[KEY_NAME_SIZE
];
87 uint8_t *encrypted_state
;
88 uint16_t encrypted_state_len
;
89 uint8_t mac
[MAC_SIZE
];
93 digest_ticket (const gnutls_datum_t
* key
, struct ticket
*ticket
,
96 digest_hd_st digest_hd
;
100 ret
= _gnutls_hmac_init (&digest_hd
, GNUTLS_MAC_SHA256
, key
->data
,
107 _gnutls_hmac (&digest_hd
, ticket
->key_name
, KEY_NAME_SIZE
);
108 _gnutls_hmac (&digest_hd
, ticket
->IV
, IV_SIZE
);
109 length16
= _gnutls_conv_uint16 (ticket
->encrypted_state_len
);
110 _gnutls_hmac (&digest_hd
, &length16
, 2);
111 _gnutls_hmac (&digest_hd
, ticket
->encrypted_state
,
112 ticket
->encrypted_state_len
);
113 _gnutls_hmac_deinit (&digest_hd
, digest
);
119 decrypt_ticket (gnutls_session_t session
, session_ticket_ext_st
* priv
,
120 struct ticket
*ticket
)
122 cipher_hd_st cipher_hd
;
123 gnutls_datum_t key
, IV
, mac_secret
, state
;
124 uint8_t final
[MAC_SECRET_SIZE
];
125 time_t timestamp
= gnutls_time (0);
128 /* Check the integrity of ticket using HMAC-SHA-256. */
129 mac_secret
.data
= (void *) &priv
->key
[MAC_SECRET_POS
];
130 mac_secret
.size
= MAC_SECRET_SIZE
;
131 ret
= digest_ticket (&mac_secret
, ticket
, final
);
138 if (memcmp (ticket
->mac
, final
, MAC_SIZE
))
141 return GNUTLS_E_DECRYPTION_FAILED
;
144 /* Decrypt encrypted_state using 128-bit AES in CBC mode. */
145 key
.data
= (void *) &priv
->key
[KEY_POS
];
147 IV
.data
= ticket
->IV
;
150 _gnutls_cipher_init (&cipher_hd
, GNUTLS_CIPHER_AES_128_CBC
, &key
, &IV
, 0);
156 ret
= _gnutls_cipher_decrypt (&cipher_hd
, ticket
->encrypted_state
,
157 ticket
->encrypted_state_len
);
158 _gnutls_cipher_deinit (&cipher_hd
);
165 /* Unpack security parameters. */
166 state
.data
= ticket
->encrypted_state
;
167 state
.size
= ticket
->encrypted_state_len
;
168 ret
= _gnutls_session_unpack (session
, &state
);
175 if (timestamp
- session
->internals
.resumed_security_parameters
.timestamp
>
176 session
->internals
.expire_time
177 || session
->internals
.resumed_security_parameters
.timestamp
> timestamp
)
180 return GNUTLS_E_EXPIRED
;
183 session
->internals
.resumed
= RESUME_TRUE
;
189 encrypt_ticket (gnutls_session_t session
, session_ticket_ext_st
* priv
,
190 struct ticket
*ticket
)
192 cipher_hd_st cipher_hd
;
193 gnutls_datum_t key
, IV
, mac_secret
, state
, encrypted_state
;
197 /* Pack security parameters. */
198 ret
= _gnutls_session_pack (session
, &state
);
204 blocksize
= gnutls_cipher_get_block_size (GNUTLS_CIPHER_AES_128_CBC
);
206 encrypted_state
.size
=
207 ((state
.size
+ blocksize
- 1) / blocksize
) * blocksize
;
208 encrypted_state
.data
= gnutls_malloc (encrypted_state
.size
);
209 if (!encrypted_state
.data
)
212 _gnutls_free_datum (&state
);
213 return GNUTLS_E_MEMORY_ERROR
;
215 memset (encrypted_state
.data
, 0, encrypted_state
.size
);
216 memcpy (encrypted_state
.data
, state
.data
, state
.size
);
217 _gnutls_free_datum (&state
);
219 /* Encrypt state using 128-bit AES in CBC mode. */
220 key
.data
= (void *) &priv
->key
[KEY_POS
];
222 IV
.data
= priv
->session_ticket_IV
;
225 _gnutls_cipher_init (&cipher_hd
, GNUTLS_CIPHER_AES_128_CBC
, &key
, &IV
, 1);
229 _gnutls_free_datum (&encrypted_state
);
233 ret
= _gnutls_cipher_encrypt (&cipher_hd
, encrypted_state
.data
,
234 encrypted_state
.size
);
235 _gnutls_cipher_deinit (&cipher_hd
);
239 _gnutls_free_datum (&encrypted_state
);
243 /* Fill the ticket structure to compute MAC. */
244 memcpy (ticket
->key_name
, &priv
->key
[NAME_POS
], KEY_NAME_SIZE
);
245 memcpy (ticket
->IV
, IV
.data
, IV
.size
);
246 ticket
->encrypted_state_len
= encrypted_state
.size
;
247 ticket
->encrypted_state
= encrypted_state
.data
;
249 mac_secret
.data
= &priv
->key
[MAC_SECRET_POS
];
250 mac_secret
.size
= MAC_SECRET_SIZE
;
251 ret
= digest_ticket (&mac_secret
, ticket
, ticket
->mac
);
255 _gnutls_free_datum (&encrypted_state
);
263 session_ticket_recv_params (gnutls_session_t session
,
264 const uint8_t * data
, size_t _data_size
)
266 ssize_t data_size
= _data_size
;
267 session_ticket_ext_st
*priv
= NULL
;
268 extension_priv_data_t epriv
;
272 _gnutls_ext_get_session_data (session
, GNUTLS_EXTENSION_SESSION_TICKET
,
280 if (!priv
->session_ticket_enable
)
283 if (session
->security_parameters
.entity
== GNUTLS_SERVER
)
285 struct ticket ticket
;
286 const uint8_t *encrypted_state
;
289 /* The client requested a new session ticket. */
292 priv
->session_ticket_renew
= 1;
296 DECR_LEN (data_size
, KEY_NAME_SIZE
);
297 memcpy (ticket
.key_name
, data
, KEY_NAME_SIZE
);
298 data
+= KEY_NAME_SIZE
;
300 /* If the key name of the ticket does not match the one that we
301 hold, issue a new ticket. */
302 if (memcmp (ticket
.key_name
, &priv
->key
[NAME_POS
], KEY_NAME_SIZE
))
304 priv
->session_ticket_renew
= 1;
308 DECR_LEN (data_size
, IV_SIZE
);
309 memcpy (ticket
.IV
, data
, IV_SIZE
);
312 DECR_LEN (data_size
, 2);
313 ticket
.encrypted_state_len
= _gnutls_read_uint16 (data
);
316 encrypted_state
= data
;
318 DECR_LEN (data_size
, ticket
.encrypted_state_len
);
319 data
+= ticket
.encrypted_state_len
;
321 DECR_LEN (data_size
, MAC_SIZE
);
322 memcpy (ticket
.mac
, data
, MAC_SIZE
);
324 ticket
.encrypted_state
= gnutls_malloc (ticket
.encrypted_state_len
);
325 if (!ticket
.encrypted_state
)
328 return GNUTLS_E_MEMORY_ERROR
;
330 memcpy (ticket
.encrypted_state
, encrypted_state
,
331 ticket
.encrypted_state_len
);
333 ret
= decrypt_ticket (session
, priv
, &ticket
);
334 gnutls_free (ticket
.encrypted_state
);
337 priv
->session_ticket_renew
= 1;
345 priv
->session_ticket_renew
= 1;
353 /* returns a positive number if we send the extension data, (0) if we
354 do not want to send it, and a negative number on failure.
357 session_ticket_send_params (gnutls_session_t session
,
358 gnutls_buffer_st
* extdata
)
360 session_ticket_ext_st
*priv
= NULL
;
361 extension_priv_data_t epriv
;
365 _gnutls_ext_get_session_data (session
, GNUTLS_EXTENSION_SESSION_TICKET
,
370 if (priv
== NULL
|| !priv
->session_ticket_enable
)
373 if (session
->security_parameters
.entity
== GNUTLS_SERVER
)
375 if (priv
&& priv
->session_ticket_renew
)
377 return GNUTLS_E_INT_RET_0
;
383 _gnutls_ext_get_resumed_session_data (session
,
384 GNUTLS_EXTENSION_SESSION_TICKET
,
389 /* no previous data. Just advertize it */
391 return GNUTLS_E_INT_RET_0
;
393 /* previous data had session tickets disabled. Don't advertize. Ignore. */
394 if (!priv
->session_ticket_enable
)
397 if (priv
->session_ticket_len
> 0)
399 ret
= _gnutls_buffer_append_data( extdata
, priv
->session_ticket
, priv
->session_ticket_len
);
401 return gnutls_assert_val(ret
);
403 return priv
->session_ticket_len
;
411 session_ticket_deinit_data (extension_priv_data_t epriv
)
413 session_ticket_ext_st
*priv
= epriv
.ptr
;
415 gnutls_free (priv
->session_ticket
);
420 session_ticket_pack (extension_priv_data_t epriv
, gnutls_buffer_st
* ps
)
422 session_ticket_ext_st
*priv
= epriv
.ptr
;
425 BUFFER_APPEND_PFX4 (ps
, priv
->session_ticket
, priv
->session_ticket_len
);
426 BUFFER_APPEND_NUM (ps
, priv
->session_ticket_enable
);
432 session_ticket_unpack (gnutls_buffer_st
* ps
, extension_priv_data_t
* _priv
)
434 session_ticket_ext_st
*priv
= NULL
;
436 extension_priv_data_t epriv
;
437 gnutls_datum_t ticket
;
439 priv
= gnutls_calloc (1, sizeof (*priv
));
443 return GNUTLS_E_MEMORY_ERROR
;
446 BUFFER_POP_DATUM (ps
, &ticket
);
447 priv
->session_ticket
= ticket
.data
;
448 priv
->session_ticket_len
= ticket
.size
;
449 BUFFER_POP_NUM (ps
, priv
->session_ticket_enable
);
464 * gnutls_session_ticket_key_generate:
465 * @key: is a pointer to a #gnutls_datum_t which will contain a newly
468 * Generate a random key to encrypt security parameters within
471 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
477 gnutls_session_ticket_key_generate (gnutls_datum_t
* key
)
479 return gnutls_key_generate(key
, SESSION_KEY_SIZE
);
483 * gnutls_session_ticket_enable_client:
484 * @session: is a #gnutls_session_t structure.
486 * Request that the client should attempt session resumption using
489 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
495 gnutls_session_ticket_enable_client (gnutls_session_t session
)
497 session_ticket_ext_st
*priv
= NULL
;
498 extension_priv_data_t epriv
;
503 return GNUTLS_E_INVALID_REQUEST
;
506 priv
= gnutls_calloc (1, sizeof (*priv
));
510 return GNUTLS_E_MEMORY_ERROR
;
512 priv
->session_ticket_enable
= 1;
515 _gnutls_ext_set_session_data (session
,
516 GNUTLS_EXTENSION_SESSION_TICKET
, epriv
);
522 * gnutls_session_ticket_enable_server:
523 * @session: is a #gnutls_session_t structure.
524 * @key: key to encrypt session parameters.
526 * Request that the server should attempt session resumption using
527 * SessionTicket. @key must be initialized with
528 * gnutls_session_ticket_key_generate().
530 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
536 gnutls_session_ticket_enable_server (gnutls_session_t session
,
537 const gnutls_datum_t
* key
)
540 session_ticket_ext_st
*priv
= NULL
;
541 extension_priv_data_t epriv
;
544 || key
->size
!= SESSION_KEY_SIZE
)
547 return GNUTLS_E_INVALID_REQUEST
;
550 priv
= gnutls_calloc (1, sizeof (*priv
));
554 return GNUTLS_E_MEMORY_ERROR
;
558 ret
= _gnutls_rnd (GNUTLS_RND_NONCE
, priv
->session_ticket_IV
, IV_SIZE
);
565 memcpy (&priv
->key
, key
->data
, key
->size
);
566 priv
->session_ticket_enable
= 1;
568 _gnutls_ext_set_session_data (session
,
569 GNUTLS_EXTENSION_SESSION_TICKET
, epriv
);
575 _gnutls_send_new_session_ticket (gnutls_session_t session
, int again
)
577 mbuffer_st
*bufel
= NULL
;
578 uint8_t *data
= NULL
, *p
;
581 struct ticket ticket
;
583 session_ticket_ext_st
*priv
= NULL
;
584 extension_priv_data_t epriv
;
585 uint16_t epoch_saved
= session
->security_parameters
.epoch_write
;
590 _gnutls_ext_get_session_data (session
,
591 GNUTLS_EXTENSION_SESSION_TICKET
,
597 if (!priv
->session_ticket_renew
)
600 /* XXX: Temporarily set write algorithms to be used.
601 _gnutls_write_connection_state_init() does this job, but it also
602 triggers encryption, while NewSessionTicket should not be
603 encrypted in the record layer. */
605 _gnutls_epoch_set_keys (session
,
606 session
->security_parameters
.epoch_next
);
613 session
->security_parameters
.epoch_write
=
614 session
->security_parameters
.epoch_next
;
616 ret
= encrypt_ticket (session
, priv
, &ticket
);
617 session
->security_parameters
.epoch_write
= epoch_saved
;
624 ticket_len
= KEY_NAME_SIZE
+ IV_SIZE
+ 2 + ticket
.encrypted_state_len
627 bufel
= _gnutls_handshake_alloc (session
, 4 + 2 + ticket_len
, 4+2+ticket_len
);
631 gnutls_free (ticket
.encrypted_state
);
632 return GNUTLS_E_MEMORY_ERROR
;
635 data
= _mbuffer_get_udata_ptr (bufel
);
638 _gnutls_write_uint32 (session
->internals
.expire_time
, p
);
641 _gnutls_write_uint16 (ticket_len
, p
);
644 memcpy (p
, ticket
.key_name
, KEY_NAME_SIZE
);
647 memcpy (p
, ticket
.IV
, IV_SIZE
);
650 _gnutls_write_uint16 (ticket
.encrypted_state_len
, p
);
653 memcpy (p
, ticket
.encrypted_state
, ticket
.encrypted_state_len
);
654 gnutls_free (ticket
.encrypted_state
);
655 p
+= ticket
.encrypted_state_len
;
657 memcpy (p
, ticket
.mac
, MAC_SIZE
);
660 data_size
= p
- data
;
662 session
->internals
.ticket_sent
= 1;
664 return _gnutls_send_handshake (session
, data_size
? bufel
: NULL
,
665 GNUTLS_HANDSHAKE_NEW_SESSION_TICKET
);
669 _gnutls_recv_new_session_ticket (gnutls_session_t session
)
673 gnutls_buffer_st buf
;
676 session_ticket_ext_st
*priv
= NULL
;
677 extension_priv_data_t epriv
;
680 _gnutls_ext_get_session_data (session
, GNUTLS_EXTENSION_SESSION_TICKET
,
689 if (!priv
->session_ticket_renew
)
692 ret
= _gnutls_recv_handshake (session
,
693 GNUTLS_HANDSHAKE_NEW_SESSION_TICKET
,
696 return gnutls_assert_val_fatal(ret
);
699 data_size
= buf
.length
;
701 DECR_LENGTH_COM (data_size
, 4, ret
= GNUTLS_E_UNEXPECTED_PACKET_LENGTH
; goto error
);
702 /* skip over lifetime hint */
705 DECR_LENGTH_COM (data_size
, 2, ret
= GNUTLS_E_UNEXPECTED_PACKET_LENGTH
; goto error
);
706 ticket_len
= _gnutls_read_uint16 (p
);
709 DECR_LENGTH_COM (data_size
, ticket_len
, ret
= GNUTLS_E_UNEXPECTED_PACKET_LENGTH
; goto error
);
710 priv
->session_ticket
= gnutls_realloc (priv
->session_ticket
, ticket_len
);
711 if (!priv
->session_ticket
)
714 ret
= GNUTLS_E_MEMORY_ERROR
;
717 memcpy (priv
->session_ticket
, p
, ticket_len
);
718 priv
->session_ticket_len
= ticket_len
;
720 /* Discard the current session ID. (RFC5077 3.4) */
721 ret
= _gnutls_generate_session_id (session
->security_parameters
.session_id
,
723 security_parameters
.session_id_size
);
727 gnutls_free (priv
->session_ticket
);
728 priv
->session_ticket
= NULL
;
729 ret
= GNUTLS_E_INTERNAL_ERROR
;
735 _gnutls_buffer_clear (&buf
);