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 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 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>
36 #include <gnutls_dtls.h>
38 #ifdef ENABLE_SESSION_TICKETS
40 #define KEY_NAME_SIZE SESSION_TICKET_KEY_NAME_SIZE
41 #define KEY_SIZE SESSION_TICKET_KEY_SIZE
42 #define IV_SIZE 12 /* GCM */
44 #define TAG_SIZE 16 /* GCM */
46 static int session_ticket_recv_params(gnutls_session_t session,
49 static int session_ticket_send_params(gnutls_session_t session,
50 gnutls_buffer_st * extdata);
51 static int session_ticket_unpack(gnutls_buffer_st * ps,
52 extension_priv_data_t * _priv);
53 static int session_ticket_pack(extension_priv_data_t _priv,
54 gnutls_buffer_st * ps);
55 static void session_ticket_deinit_data(extension_priv_data_t priv);
57 extension_entry_st ext_mod_session_ticket = {
58 .name = "SESSION TICKET",
59 .type = GNUTLS_EXTENSION_SESSION_TICKET,
60 .parse_type = GNUTLS_EXT_MANDATORY,
62 .recv_func = session_ticket_recv_params,
63 .send_func = session_ticket_send_params,
64 .pack_func = session_ticket_pack,
65 .unpack_func = session_ticket_unpack,
66 .deinit_func = session_ticket_deinit_data,
69 #define SESSION_KEY_SIZE (SESSION_TICKET_KEY_NAME_SIZE+SESSION_TICKET_KEY_SIZE)
71 #define KEY_POS (SESSION_TICKET_KEY_NAME_SIZE)
74 int session_ticket_enable;
75 int session_ticket_renew;
77 uint8_t *session_ticket;
78 int session_ticket_len;
80 uint8_t key[SESSION_KEY_SIZE];
81 } session_ticket_ext_st;
84 uint8_t key_name[KEY_NAME_SIZE];
86 uint8_t *encrypted_state;
87 uint16_t encrypted_state_len;
88 uint8_t tag[TAG_SIZE];
92 decrypt_ticket(gnutls_session_t session, session_ticket_ext_st * priv,
93 struct ticket_st *ticket)
95 cipher_hd_st cipher_hd;
96 gnutls_datum_t key, IV, state;
97 uint8_t final[TAG_SIZE];
98 time_t timestamp = gnutls_time(0);
101 /* Decrypt encrypted_state using 128-bit AES in GCM mode. */
102 key.data = (void *) &priv->key[KEY_POS];
104 IV.data = ticket->IV;
107 _gnutls_cipher_init(&cipher_hd,
108 cipher_to_entry(GNUTLS_CIPHER_AES_128_GCM),
114 ret = _gnutls_cipher_decrypt(&cipher_hd, ticket->encrypted_state,
115 ticket->encrypted_state_len);
121 _gnutls_cipher_tag(&cipher_hd, final, TAG_SIZE);
122 if (gnutls_memcmp(ticket->tag, final, TAG_SIZE) != 0) {
124 ret = GNUTLS_E_DECRYPTION_FAILED;
128 /* Unpack security parameters. */
129 state.data = ticket->encrypted_state;
130 state.size = ticket->encrypted_state_len;
131 ret = _gnutls_session_unpack(session, &state);
138 session->internals.resumed_security_parameters.timestamp >
139 session->internals.expire_time
140 || session->internals.resumed_security_parameters.timestamp >
143 ret = GNUTLS_E_EXPIRED;
147 session->internals.resumed = RESUME_TRUE;
151 _gnutls_cipher_deinit(&cipher_hd);
158 encrypt_ticket(gnutls_session_t session, session_ticket_ext_st * priv,
159 struct ticket_st *ticket)
161 cipher_hd_st cipher_hd;
162 gnutls_datum_t key, IV;
163 gnutls_datum_t state = {NULL,0}, encrypted_state = {NULL,0};
168 /* Pack security parameters. */
169 ret = _gnutls_session_pack(session, &state);
175 encrypted_state.size = state.size;
176 encrypted_state.data = gnutls_malloc(encrypted_state.size);
177 if (!encrypted_state.data) {
179 ret = GNUTLS_E_MEMORY_ERROR;
182 memset(encrypted_state.data, 0, encrypted_state.size);
183 memcpy(encrypted_state.data, state.data, state.size);
185 /* Encrypt state using 128-bit AES in GCM mode. */
186 key.data = (void *) &priv->key[KEY_POS];
193 ret = _gnutls_rnd(GNUTLS_RND_NONCE, iv+4, IV_SIZE-4);
200 _gnutls_cipher_init(&cipher_hd,
201 cipher_to_entry(GNUTLS_CIPHER_AES_128_GCM),
208 ret = _gnutls_cipher_encrypt(&cipher_hd, encrypted_state.data,
209 encrypted_state.size);
215 _gnutls_cipher_tag(&cipher_hd, ticket->tag, TAG_SIZE);
217 /* Fill the ticket structure to compute MAC. */
218 memcpy(ticket->key_name, &priv->key[NAME_POS], KEY_NAME_SIZE);
219 memcpy(ticket->IV, IV.data, IV.size);
220 ticket->encrypted_state_len = encrypted_state.size;
221 ticket->encrypted_state = encrypted_state.data;
223 encrypted_state.data = NULL;
228 _gnutls_cipher_deinit(&cipher_hd);
231 _gnutls_free_datum(&state);
232 _gnutls_free_datum(&encrypted_state);
238 session_ticket_recv_params(gnutls_session_t session,
239 const uint8_t * data, size_t _data_size)
241 ssize_t data_size = _data_size;
242 session_ticket_ext_st *priv = NULL;
243 extension_priv_data_t epriv;
247 _gnutls_ext_get_session_data(session,
248 GNUTLS_EXTENSION_SESSION_TICKET,
255 if (!priv->session_ticket_enable)
258 if (session->security_parameters.entity == GNUTLS_SERVER) {
259 struct ticket_st ticket;
260 const uint8_t *encrypted_state;
262 /* The client requested a new session ticket. */
263 if (data_size == 0) {
264 priv->session_ticket_renew = 1;
268 DECR_LEN(data_size, KEY_NAME_SIZE);
269 memcpy(ticket.key_name, data, KEY_NAME_SIZE);
270 data += KEY_NAME_SIZE;
272 /* If the key name of the ticket does not match the one that we
273 hold, issue a new ticket. */
275 (ticket.key_name, &priv->key[NAME_POS],
277 priv->session_ticket_renew = 1;
281 DECR_LEN(data_size, IV_SIZE);
282 memcpy(ticket.IV, data, IV_SIZE);
285 DECR_LEN(data_size, 2);
286 ticket.encrypted_state_len = _gnutls_read_uint16(data);
289 encrypted_state = data;
291 DECR_LEN(data_size, ticket.encrypted_state_len);
292 data += ticket.encrypted_state_len;
294 DECR_LEN(data_size, TAG_SIZE);
295 memcpy(ticket.tag, data, TAG_SIZE);
297 ticket.encrypted_state =
298 gnutls_malloc(ticket.encrypted_state_len);
299 if (!ticket.encrypted_state) {
301 return GNUTLS_E_MEMORY_ERROR;
303 memcpy(ticket.encrypted_state, encrypted_state,
304 ticket.encrypted_state_len);
306 ret = decrypt_ticket(session, priv, &ticket);
307 gnutls_free(ticket.encrypted_state);
309 priv->session_ticket_renew = 1;
312 } else { /* Client */
314 if (data_size == 0) {
315 priv->session_ticket_renew = 1;
323 /* returns a positive number if we send the extension data, (0) if we
324 do not want to send it, and a negative number on failure.
327 session_ticket_send_params(gnutls_session_t session,
328 gnutls_buffer_st * extdata)
330 session_ticket_ext_st *priv = NULL;
331 extension_priv_data_t epriv;
335 _gnutls_ext_get_session_data(session,
336 GNUTLS_EXTENSION_SESSION_TICKET,
341 if (priv == NULL || !priv->session_ticket_enable)
344 if (session->security_parameters.entity == GNUTLS_SERVER) {
345 if (priv && priv->session_ticket_renew) {
346 return GNUTLS_E_INT_RET_0;
350 _gnutls_ext_get_resumed_session_data(session,
351 GNUTLS_EXTENSION_SESSION_TICKET,
356 /* no previous data. Just advertize it */
358 return GNUTLS_E_INT_RET_0;
360 /* previous data had session tickets disabled. Don't advertize. Ignore. */
361 if (!priv->session_ticket_enable)
364 if (priv->session_ticket_len > 0) {
366 _gnutls_buffer_append_data(extdata,
372 return gnutls_assert_val(ret);
374 return priv->session_ticket_len;
381 static void session_ticket_deinit_data(extension_priv_data_t epriv)
383 session_ticket_ext_st *priv = epriv;
385 gnutls_free(priv->session_ticket);
390 session_ticket_pack(extension_priv_data_t epriv, gnutls_buffer_st * ps)
392 session_ticket_ext_st *priv = epriv;
395 BUFFER_APPEND_PFX4(ps, priv->session_ticket,
396 priv->session_ticket_len);
397 BUFFER_APPEND_NUM(ps, priv->session_ticket_enable);
403 session_ticket_unpack(gnutls_buffer_st * ps, extension_priv_data_t * _priv)
405 session_ticket_ext_st *priv = NULL;
407 extension_priv_data_t epriv;
408 gnutls_datum_t ticket;
410 priv = gnutls_calloc(1, sizeof(*priv));
413 return GNUTLS_E_MEMORY_ERROR;
416 BUFFER_POP_DATUM(ps, &ticket);
417 priv->session_ticket = ticket.data;
418 priv->session_ticket_len = ticket.size;
419 BUFFER_POP_NUM(ps, priv->session_ticket_enable);
434 * gnutls_session_ticket_key_generate:
435 * @key: is a pointer to a #gnutls_datum_t which will contain a newly
438 * Generate a random key to encrypt security parameters within
441 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
446 int gnutls_session_ticket_key_generate(gnutls_datum_t * key)
448 return gnutls_key_generate(key, SESSION_KEY_SIZE);
452 * gnutls_session_ticket_enable_client:
453 * @session: is a #gnutls_session_t type.
455 * Request that the client should attempt session resumption using
458 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
463 int gnutls_session_ticket_enable_client(gnutls_session_t session)
465 session_ticket_ext_st *priv = NULL;
466 extension_priv_data_t epriv;
470 return GNUTLS_E_INVALID_REQUEST;
473 priv = gnutls_calloc(1, sizeof(*priv));
476 return GNUTLS_E_MEMORY_ERROR;
478 priv->session_ticket_enable = 1;
481 _gnutls_ext_set_session_data(session,
482 GNUTLS_EXTENSION_SESSION_TICKET,
489 * gnutls_session_ticket_enable_server:
490 * @session: is a #gnutls_session_t type.
491 * @key: key to encrypt session parameters.
493 * Request that the server should attempt session resumption using
494 * SessionTicket. @key must be initialized with
495 * gnutls_session_ticket_key_generate(), and should be overwritten
496 * using gnutls_memset() before being released.
498 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
504 gnutls_session_ticket_enable_server(gnutls_session_t session,
505 const gnutls_datum_t * key)
507 session_ticket_ext_st *priv = NULL;
508 extension_priv_data_t epriv;
510 if (!session || !key || key->size != SESSION_KEY_SIZE) {
512 return GNUTLS_E_INVALID_REQUEST;
515 priv = gnutls_calloc(1, sizeof(*priv));
518 return GNUTLS_E_MEMORY_ERROR;
522 memcpy(&priv->key, key->data, key->size);
523 priv->session_ticket_enable = 1;
525 _gnutls_ext_set_session_data(session,
526 GNUTLS_EXTENSION_SESSION_TICKET,
532 int _gnutls_send_new_session_ticket(gnutls_session_t session, int again)
534 mbuffer_st *bufel = NULL;
535 uint8_t *data = NULL, *p;
538 struct ticket_st ticket;
540 session_ticket_ext_st *priv = NULL;
541 extension_priv_data_t epriv;
542 uint16_t epoch_saved = session->security_parameters.epoch_write;
546 _gnutls_ext_get_session_data(session,
547 GNUTLS_EXTENSION_SESSION_TICKET,
553 if (!priv->session_ticket_renew)
556 /* XXX: Temporarily set write algorithms to be used.
557 _gnutls_write_connection_state_init() does this job, but it also
558 triggers encryption, while NewSessionTicket should not be
559 encrypted in the record layer. */
561 _gnutls_epoch_set_keys(session,
562 session->security_parameters.
569 session->security_parameters.epoch_write =
570 session->security_parameters.epoch_next;
572 ret = encrypt_ticket(session, priv, &ticket);
573 session->security_parameters.epoch_write = epoch_saved;
580 KEY_NAME_SIZE + IV_SIZE + 2 +
581 ticket.encrypted_state_len + TAG_SIZE;
584 _gnutls_handshake_alloc(session,
588 gnutls_free(ticket.encrypted_state);
589 return GNUTLS_E_MEMORY_ERROR;
592 data = _mbuffer_get_udata_ptr(bufel);
595 _gnutls_write_uint32(session->internals.expire_time, p);
598 _gnutls_write_uint16(ticket_len, p);
601 memcpy(p, ticket.key_name, KEY_NAME_SIZE);
604 memcpy(p, ticket.IV, IV_SIZE);
607 _gnutls_write_uint16(ticket.encrypted_state_len, p);
610 memcpy(p, ticket.encrypted_state, ticket.encrypted_state_len);
611 gnutls_free(ticket.encrypted_state);
612 p += ticket.encrypted_state_len;
614 memcpy(p, ticket.tag, TAG_SIZE);
617 data_size = p - data;
619 session->internals.ticket_sent = 1;
621 return _gnutls_send_handshake(session, data_size ? bufel : NULL,
622 GNUTLS_HANDSHAKE_NEW_SESSION_TICKET);
625 int _gnutls_recv_new_session_ticket(gnutls_session_t session)
629 gnutls_buffer_st buf;
632 session_ticket_ext_st *priv = NULL;
633 extension_priv_data_t epriv;
636 _gnutls_ext_get_session_data(session,
637 GNUTLS_EXTENSION_SESSION_TICKET,
645 if (!priv->session_ticket_renew)
648 /* This is the last flight and peer cannot be sure
649 * we have received it unless we notify him. So we
650 * wait for a message and retransmit if needed. */
651 if (IS_DTLS(session) && !_dtls_is_async(session) &&
652 (gnutls_record_check_pending(session) +
653 record_check_unprocessed(session)) == 0) {
654 ret = _dtls_wait_and_retransmit(session);
656 return gnutls_assert_val(ret);
659 ret = _gnutls_recv_handshake(session,
660 GNUTLS_HANDSHAKE_NEW_SESSION_TICKET,
663 return gnutls_assert_val_fatal(ret);
666 data_size = buf.length;
668 DECR_LENGTH_COM(data_size, 4, ret =
669 GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
671 /* skip over lifetime hint */
674 DECR_LENGTH_COM(data_size, 2, ret =
675 GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
677 ticket_len = _gnutls_read_uint16(p);
680 DECR_LENGTH_COM(data_size, ticket_len, ret =
681 GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
683 priv->session_ticket =
684 gnutls_realloc_fast(priv->session_ticket, ticket_len);
685 if (!priv->session_ticket) {
687 ret = GNUTLS_E_MEMORY_ERROR;
690 memcpy(priv->session_ticket, p, ticket_len);
691 priv->session_ticket_len = ticket_len;
693 /* Discard the current session ID. (RFC5077 3.4) */
695 _gnutls_generate_session_id(session->security_parameters.
697 &session->security_parameters.
701 gnutls_free(priv->session_ticket);
702 priv->session_ticket = NULL;
703 ret = GNUTLS_E_INTERNAL_ERROR;
709 _gnutls_buffer_clear(&buf);