2 * Copyright (C) 2009, 2010 Free Software Foundation, Inc.
6 * This file is part of GNUTLS.
8 * The GNUTLS library 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>
36 #ifdef ENABLE_SESSION_TICKET
38 #define KEY_NAME_SIZE SESSION_TICKET_KEY_NAME_SIZE
39 #define KEY_SIZE SESSION_TICKET_KEY_SIZE
40 #define IV_SIZE SESSION_TICKET_IV_SIZE
41 #define MAC_SECRET_SIZE SESSION_TICKET_MAC_SECRET_SIZE
47 opaque key_name
[KEY_NAME_SIZE
];
49 opaque
*encrypted_state
;
50 uint16_t encrypted_state_len
;
55 digest_ticket (const gnutls_datum_t
* key
, struct ticket
*ticket
,
58 digest_hd_st digest_hd
;
62 ret
= _gnutls_hmac_init (&digest_hd
, GNUTLS_MAC_SHA256
, key
->data
,
69 _gnutls_hmac (&digest_hd
, ticket
->key_name
, KEY_NAME_SIZE
);
70 _gnutls_hmac (&digest_hd
, ticket
->IV
, IV_SIZE
);
71 length16
= _gnutls_conv_uint16 (ticket
->encrypted_state_len
);
72 _gnutls_hmac (&digest_hd
, &length16
, 2);
73 _gnutls_hmac (&digest_hd
, ticket
->encrypted_state
,
74 ticket
->encrypted_state_len
);
75 _gnutls_hmac_deinit (&digest_hd
, digest
);
81 decrypt_ticket (gnutls_session_t session
, struct ticket
*ticket
)
83 cipher_hd_st cipher_hd
;
84 gnutls_datum_t key
, IV
, mac_secret
, state
;
86 time_t timestamp
= time (0);
89 /* Check the integrity of ticket using HMAC-SHA-256. */
90 mac_secret
.data
= (void*)
91 session
->internals
.session_ticket_key
->mac_secret
;
92 mac_secret
.size
= MAC_SECRET_SIZE
;
93 ret
= digest_ticket (&mac_secret
, ticket
, final
);
100 if (memcmp (ticket
->mac
, final
, MAC_SIZE
))
103 return GNUTLS_E_DECRYPTION_FAILED
;
106 /* Decrypt encrypted_state using 128-bit AES in CBC mode. */
107 key
.data
= (void*)session
->internals
.session_ticket_key
->key
;
109 IV
.data
= ticket
->IV
;
112 _gnutls_cipher_init (&cipher_hd
, GNUTLS_CIPHER_AES_128_CBC
, &key
, &IV
);
118 ret
= _gnutls_cipher_decrypt (&cipher_hd
, ticket
->encrypted_state
,
119 ticket
->encrypted_state_len
);
120 _gnutls_cipher_deinit (&cipher_hd
);
127 /* Unpack security parameters. */
128 state
.data
= ticket
->encrypted_state
;
129 state
.size
= ticket
->encrypted_state_len
;
130 ret
= _gnutls_session_unpack (session
, &state
);
137 if (timestamp
- session
->internals
.resumed_security_parameters
.timestamp
>
138 session
->internals
.expire_time
139 || session
->internals
.resumed_security_parameters
.timestamp
> timestamp
)
142 return GNUTLS_E_EXPIRED
;
145 session
->internals
.resumed
= RESUME_TRUE
;
151 encrypt_ticket (gnutls_session_t session
, struct ticket
*ticket
)
153 cipher_hd_st cipher_hd
;
154 gnutls_datum_t key
, IV
, mac_secret
, state
, encrypted_state
;
158 /* Pack security parameters. */
159 ret
= _gnutls_session_pack (session
, &state
);
165 blocksize
= gnutls_cipher_get_block_size (GNUTLS_CIPHER_AES_128_CBC
);
166 encrypted_state
.size
=
167 ((state
.size
+ blocksize
- 1) / blocksize
) * blocksize
;
168 encrypted_state
.data
= gnutls_malloc (encrypted_state
.size
);
169 if (!encrypted_state
.data
)
172 _gnutls_free_datum (&state
);
173 return GNUTLS_E_MEMORY_ERROR
;
175 memset (encrypted_state
.data
, 0, encrypted_state
.size
);
176 memcpy (encrypted_state
.data
, state
.data
, state
.size
);
177 _gnutls_free_datum (&state
);
179 /* Encrypt state using 128-bit AES in CBC mode. */
180 key
.data
= (void*)session
->internals
.session_ticket_key
->key
;
182 IV
.data
= session
->internals
.session_ticket_IV
;
185 _gnutls_cipher_init (&cipher_hd
, GNUTLS_CIPHER_AES_128_CBC
, &key
, &IV
);
189 _gnutls_free_datum (&encrypted_state
);
193 ret
= _gnutls_cipher_encrypt (&cipher_hd
, encrypted_state
.data
,
194 encrypted_state
.size
);
195 _gnutls_cipher_deinit (&cipher_hd
);
199 _gnutls_free_datum (&encrypted_state
);
203 /* Fill the ticket structure to compute MAC. */
204 memcpy (ticket
->key_name
,
205 session
->internals
.session_ticket_key
->key_name
, KEY_NAME_SIZE
);
206 memcpy (ticket
->IV
, IV
.data
, IV
.size
);
207 ticket
->encrypted_state_len
= encrypted_state
.size
;
208 ticket
->encrypted_state
= encrypted_state
.data
;
211 (void*)session
->internals
.session_ticket_key
->mac_secret
;
212 mac_secret
.size
= MAC_SECRET_SIZE
;
213 ret
= digest_ticket (&mac_secret
, ticket
, ticket
->mac
);
217 _gnutls_free_datum (&encrypted_state
);
225 _gnutls_session_ticket_recv_params (gnutls_session_t session
,
226 const opaque
* data
, size_t _data_size
)
228 ssize_t data_size
= _data_size
;
230 if (!session
->internals
.session_ticket_enable
)
233 if (session
->security_parameters
.entity
== GNUTLS_SERVER
)
235 struct ticket ticket
;
236 const opaque
*encrypted_state
;
239 /* The client requested a new session ticket. */
242 session
->internals
.session_ticket_renew
= 1;
246 DECR_LEN (data_size
, KEY_NAME_SIZE
);
247 memcpy (ticket
.key_name
, data
, KEY_NAME_SIZE
);
248 data
+= KEY_NAME_SIZE
;
250 /* If the key name of the ticket does not match the one that we
251 hold, issue a new ticket. */
252 if (memcmp (ticket
.key_name
,
253 session
->internals
.session_ticket_key
->key_name
, KEY_NAME_SIZE
))
255 session
->internals
.session_ticket_renew
= 1;
259 DECR_LEN (data_size
, IV_SIZE
);
260 memcpy (ticket
.IV
, data
, IV_SIZE
);
263 DECR_LEN (data_size
, 2);
264 ticket
.encrypted_state_len
= _gnutls_read_uint16 (data
);
267 encrypted_state
= data
;
269 DECR_LEN (data_size
, ticket
.encrypted_state_len
);
270 data
+= ticket
.encrypted_state_len
;
272 DECR_LEN (data_size
, MAC_SIZE
);
273 memcpy (ticket
.mac
, data
, MAC_SIZE
);
275 ticket
.encrypted_state
= gnutls_malloc (ticket
.encrypted_state_len
);
276 if (!ticket
.encrypted_state
)
279 return GNUTLS_E_MEMORY_ERROR
;
281 memcpy (ticket
.encrypted_state
, encrypted_state
,
282 ticket
.encrypted_state_len
);
284 ret
= decrypt_ticket (session
, &ticket
);
285 gnutls_free (ticket
.encrypted_state
);
288 session
->internals
.session_ticket_renew
= 1;
296 session
->internals
.session_ticket_renew
= 1;
304 /* returns a positive number if we send the extension data, zero if we
305 do not want to send it, and a negative number on failure.
308 _gnutls_session_ticket_send_params (gnutls_session_t session
,
309 opaque
* data
, size_t _data_size
)
311 ssize_t data_size
= _data_size
;
313 if (!session
->internals
.session_ticket_enable
)
316 if (session
->security_parameters
.entity
== GNUTLS_SERVER
)
318 if (session
->internals
.session_ticket_renew
)
320 return GNUTLS_E_INT_RET_0
;
325 if (session
->internals
.resumed_security_parameters
.extensions
.
326 session_ticket_len
> 0)
328 DECR_LENGTH_RET (data_size
,
329 session
->internals
.resumed_security_parameters
.
330 extensions
.session_ticket_len
,
331 GNUTLS_E_SHORT_MEMORY_BUFFER
);
333 session
->internals
.resumed_security_parameters
.extensions
.
335 session
->internals
.resumed_security_parameters
.extensions
.
338 return session
->internals
.resumed_security_parameters
.
339 extensions
.session_ticket_len
;
343 return GNUTLS_E_INT_RET_0
;
350 * gnutls_session_ticket_key_generate:
351 * @key: is a pointer to a #gnutls_datum_t which will contain a newly
354 * Generate a random key to encrypt security parameters within
357 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
363 gnutls_session_ticket_key_generate (gnutls_datum_t
* key
)
367 key
->size
= sizeof (struct gnutls_session_ticket_key_st
);
368 key
->data
= gnutls_malloc (key
->size
);
372 return GNUTLS_E_MEMORY_ERROR
;
375 ret
= _gnutls_rnd (GNUTLS_RND_RANDOM
, key
->data
, key
->size
);
379 _gnutls_free_datum (key
);
387 * gnutls_session_ticket_enable_client:
388 * @session: is a #gnutls_session_t structure.
390 * Request that the client should attempt session resumption using
393 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
399 gnutls_session_ticket_enable_client (gnutls_session_t session
)
404 return GNUTLS_E_INVALID_REQUEST
;
407 session
->internals
.session_ticket_enable
= 1;
412 * gnutls_session_ticket_enable_server:
413 * @session: is a #gnutls_session_t structure.
414 * @key: key to encrypt session parameters.
416 * Request that the server should attempt session resumption using
417 * SessionTicket. @key must be initialized with
418 * gnutls_session_ticket_key_generate().
420 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
426 gnutls_session_ticket_enable_server (gnutls_session_t session
,
427 const gnutls_datum_t
* key
)
432 || key
->size
!= sizeof (struct gnutls_session_ticket_key_st
))
435 return GNUTLS_E_INVALID_REQUEST
;
438 ret
= _gnutls_rnd (GNUTLS_RND_RANDOM
,
440 session_ticket_IV
, IV_SIZE
);
447 session
->internals
.session_ticket_key
=
448 (struct gnutls_session_ticket_key_st
*) key
->data
;
449 session
->internals
.session_ticket_enable
= 1;
454 _gnutls_send_new_session_ticket (gnutls_session_t session
, int again
)
456 uint8_t *data
= NULL
, *p
;
459 struct ticket ticket
;
461 gnutls_cipher_algorithm_t write_bulk_cipher_algorithm
;
462 gnutls_mac_algorithm_t write_mac_algorithm
;
463 gnutls_compression_method_t write_compression_algorithm
;
465 #define SAVE_WRITE_SECURITY_PARAMETERS \
468 write_bulk_cipher_algorithm = \
469 session->security_parameters.write_bulk_cipher_algorithm; \
470 write_mac_algorithm = \
471 session->security_parameters.write_mac_algorithm; \
472 write_compression_algorithm = \
473 session->security_parameters.write_compression_algorithm; \
477 #define RESTORE_WRITE_SECURITY_PARAMETERS \
480 session->security_parameters.write_bulk_cipher_algorithm = \
481 write_bulk_cipher_algorithm; \
482 session->security_parameters.write_mac_algorithm = \
483 write_mac_algorithm; \
484 session->security_parameters.write_compression_algorithm = \
485 write_compression_algorithm; \
491 /* XXX: Temporarily set write algorithms to be used.
492 _gnutls_write_connection_state_init() does this job, but it also
493 triggers encryption, while NewSessionTicket should not be
494 encrypted in the record layer. */
495 SAVE_WRITE_SECURITY_PARAMETERS
;
496 ret
= _gnutls_set_write_cipher (session
,
497 _gnutls_cipher_suite_get_cipher_algo
498 (&session
->security_parameters
.
499 current_cipher_suite
));
502 ret
= _gnutls_set_write_mac (session
,
503 _gnutls_cipher_suite_get_mac_algo
504 (&session
->security_parameters
.
505 current_cipher_suite
));
508 ret
= _gnutls_set_write_compression (session
,
514 ret
= encrypt_ticket (session
, &ticket
);
515 RESTORE_WRITE_SECURITY_PARAMETERS
;
522 ticket_len
= KEY_NAME_SIZE
+ IV_SIZE
+ 2 + ticket
.encrypted_state_len
525 data
= gnutls_malloc (4 + 2 + ticket_len
);
529 gnutls_free (ticket
.encrypted_state
);
530 return GNUTLS_E_MEMORY_ERROR
;
534 /* FIXME: ticket lifetime is fixed to 10 days, which should be
536 _gnutls_write_uint32 (864000, p
);
539 _gnutls_write_uint16 (ticket_len
, p
);
542 memcpy (p
, ticket
.key_name
, KEY_NAME_SIZE
);
545 memcpy (p
, ticket
.IV
, IV_SIZE
);
548 _gnutls_write_uint16 (ticket
.encrypted_state_len
, p
);
551 memcpy (p
, ticket
.encrypted_state
, ticket
.encrypted_state_len
);
552 gnutls_free (ticket
.encrypted_state
);
553 p
+= ticket
.encrypted_state_len
;
555 memcpy (p
, ticket
.mac
, MAC_SIZE
);
558 data_size
= p
- data
;
561 ret
= _gnutls_send_handshake (session
, data_size
? data
: NULL
, data_size
,
562 GNUTLS_HANDSHAKE_NEW_SESSION_TICKET
);
569 _gnutls_recv_new_session_ticket (gnutls_session_t session
)
571 uint8_t *data
= NULL
, *p
;
573 uint32_t lifetime_hint
;
577 ret
= _gnutls_recv_handshake (session
, &data
, &data_size
,
578 GNUTLS_HANDSHAKE_NEW_SESSION_TICKET
,
587 DECR_LENGTH_COM (data_size
, 4, goto error
);
588 lifetime_hint
= _gnutls_read_uint32 (p
);
591 DECR_LENGTH_COM (data_size
, 2, goto error
);
592 ticket_len
= _gnutls_read_uint16 (p
);
595 DECR_LENGTH_COM (data_size
, ticket_len
, goto error
);
596 session
->security_parameters
.extensions
.session_ticket
=
597 gnutls_realloc (session
->security_parameters
.extensions
.session_ticket
,
599 if (!session
->security_parameters
.extensions
.session_ticket
)
603 return GNUTLS_E_MEMORY_ERROR
;
605 memcpy (session
->security_parameters
.extensions
.session_ticket
,
608 session
->security_parameters
.extensions
.session_ticket_len
= ticket_len
;
610 /* Discard the current session ID. (RFC5077 3.4) */
611 ret
= _gnutls_generate_session_id (session
->security_parameters
.session_id
,
612 &session
->security_parameters
.
618 gnutls_free (session
->security_parameters
.extensions
.session_ticket
);
619 return GNUTLS_E_INTERNAL_ERROR
;
625 return GNUTLS_E_UNEXPECTED_PACKET_LENGTH
;