Remove.
[gnutls.git] / lib / ext_session_ticket.c
blobd659a83f420e9a7ed55c71203e09889580163c45
1 /*
2 * Copyright (C) 2009, 2010 Free Software Foundation, Inc.
4 * Author: Daiki Ueno
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,
21 * USA
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>
33 #include <random.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
43 #define MAC_SIZE 32
45 struct ticket
47 opaque key_name[KEY_NAME_SIZE];
48 opaque IV[IV_SIZE];
49 opaque *encrypted_state;
50 uint16_t encrypted_state_len;
51 opaque mac[MAC_SIZE];
54 static int
55 digest_ticket (const gnutls_datum_t * key, struct ticket *ticket,
56 opaque * digest)
58 digest_hd_st digest_hd;
59 uint16_t length16;
60 int ret;
62 ret = _gnutls_hmac_init (&digest_hd, GNUTLS_MAC_SHA256, key->data,
63 key->size);
64 if (ret < 0)
66 gnutls_assert ();
67 return ret;
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);
77 return 0;
80 static int
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;
85 opaque final[32];
86 time_t timestamp = time (0);
87 int ret;
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);
94 if (ret < 0)
96 gnutls_assert ();
97 return ret;
100 if (memcmp (ticket->mac, final, MAC_SIZE))
102 gnutls_assert ();
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;
108 key.size = KEY_SIZE;
109 IV.data = ticket->IV;
110 IV.size = IV_SIZE;
111 ret =
112 _gnutls_cipher_init (&cipher_hd, GNUTLS_CIPHER_AES_128_CBC, &key, &IV);
113 if (ret < 0)
115 gnutls_assert ();
116 return ret;
118 ret = _gnutls_cipher_decrypt (&cipher_hd, ticket->encrypted_state,
119 ticket->encrypted_state_len);
120 _gnutls_cipher_deinit (&cipher_hd);
121 if (ret < 0)
123 gnutls_assert ();
124 return ret;
127 /* Unpack security parameters. */
128 state.data = ticket->encrypted_state;
129 state.size = ticket->encrypted_state_len;
130 ret = _gnutls_session_unpack (session, &state);
131 if (ret < 0)
133 gnutls_assert ();
134 return ret;
137 if (timestamp - session->internals.resumed_security_parameters.timestamp >
138 session->internals.expire_time
139 || session->internals.resumed_security_parameters.timestamp > timestamp)
141 gnutls_assert ();
142 return GNUTLS_E_EXPIRED;
145 session->internals.resumed = RESUME_TRUE;
147 return 0;
150 static int
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;
155 int blocksize;
156 int ret;
158 /* Pack security parameters. */
159 ret = _gnutls_session_pack (session, &state);
160 if (ret < 0)
162 gnutls_assert ();
163 return ret;
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)
171 gnutls_assert ();
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;
181 key.size = KEY_SIZE;
182 IV.data = session->internals.session_ticket_IV;
183 IV.size = IV_SIZE;
184 ret =
185 _gnutls_cipher_init (&cipher_hd, GNUTLS_CIPHER_AES_128_CBC, &key, &IV);
186 if (ret < 0)
188 gnutls_assert ();
189 _gnutls_free_datum (&encrypted_state);
190 return ret;
193 ret = _gnutls_cipher_encrypt (&cipher_hd, encrypted_state.data,
194 encrypted_state.size);
195 _gnutls_cipher_deinit (&cipher_hd);
196 if (ret < 0)
198 gnutls_assert ();
199 _gnutls_free_datum (&encrypted_state);
200 return ret;
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;
210 mac_secret.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);
214 if (ret < 0)
216 gnutls_assert ();
217 _gnutls_free_datum (&encrypted_state);
218 return ret;
221 return 0;
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)
231 return 0;
233 if (session->security_parameters.entity == GNUTLS_SERVER)
235 struct ticket ticket;
236 const opaque *encrypted_state;
237 int ret;
239 /* The client requested a new session ticket. */
240 if (data_size == 0)
242 session->internals.session_ticket_renew = 1;
243 return 0;
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;
256 return 0;
259 DECR_LEN (data_size, IV_SIZE);
260 memcpy (ticket.IV, data, IV_SIZE);
261 data += IV_SIZE;
263 DECR_LEN (data_size, 2);
264 ticket.encrypted_state_len = _gnutls_read_uint16 (data);
265 data += 2;
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)
278 gnutls_assert ();
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);
286 if (ret < 0)
288 session->internals.session_ticket_renew = 1;
289 return 0;
292 else
294 if (data_size == 0)
296 session->internals.session_ticket_renew = 1;
297 return 0;
301 return 0;
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)
314 return 0;
316 if (session->security_parameters.entity == GNUTLS_SERVER)
318 if (session->internals.session_ticket_renew)
320 return GNUTLS_E_INT_RET_0;
323 else
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);
332 memcpy (data,
333 session->internals.resumed_security_parameters.extensions.
334 session_ticket,
335 session->internals.resumed_security_parameters.extensions.
336 session_ticket_len);
338 return session->internals.resumed_security_parameters.
339 extensions.session_ticket_len;
341 else
343 return GNUTLS_E_INT_RET_0;
346 return 0;
350 * gnutls_session_ticket_key_generate:
351 * @key: is a pointer to a #gnutls_datum_t which will contain a newly
352 * created key.
354 * Generate a random key to encrypt security parameters within
355 * SessionTicket.
357 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
358 * error code.
360 * Since: 2.10.0
363 gnutls_session_ticket_key_generate (gnutls_datum_t * key)
365 int ret;
367 key->size = sizeof (struct gnutls_session_ticket_key_st);
368 key->data = gnutls_malloc (key->size);
369 if (!key->data)
371 gnutls_assert ();
372 return GNUTLS_E_MEMORY_ERROR;
375 ret = _gnutls_rnd (GNUTLS_RND_RANDOM, key->data, key->size);
376 if (ret < 0)
378 gnutls_assert ();
379 _gnutls_free_datum (key);
380 return ret;
383 return 0;
387 * gnutls_session_ticket_enable_client:
388 * @session: is a #gnutls_session_t structure.
390 * Request that the client should attempt session resumption using
391 * SessionTicket.
393 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
394 * error code.
396 * Since: 2.10.0
399 gnutls_session_ticket_enable_client (gnutls_session_t session)
401 if (!session)
403 gnutls_assert ();
404 return GNUTLS_E_INVALID_REQUEST;
407 session->internals.session_ticket_enable = 1;
408 return 0;
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
421 * error code.
423 * Since: 2.10.0
426 gnutls_session_ticket_enable_server (gnutls_session_t session,
427 const gnutls_datum_t * key)
429 int ret;
431 if (!session || !key
432 || key->size != sizeof (struct gnutls_session_ticket_key_st))
434 gnutls_assert ();
435 return GNUTLS_E_INVALID_REQUEST;
438 ret = _gnutls_rnd (GNUTLS_RND_RANDOM,
439 session->internals.
440 session_ticket_IV, IV_SIZE);
441 if (ret < 0)
443 gnutls_assert ();
444 return ret;
447 session->internals.session_ticket_key =
448 (struct gnutls_session_ticket_key_st *) key->data;
449 session->internals.session_ticket_enable = 1;
450 return 0;
454 _gnutls_send_new_session_ticket (gnutls_session_t session, int again)
456 uint8_t *data = NULL, *p;
457 int data_size = 0;
458 int ret;
459 struct ticket ticket;
460 uint16_t ticket_len;
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 \
466 do \
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; \
475 while (0)
477 #define RESTORE_WRITE_SECURITY_PARAMETERS \
478 do \
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; \
487 while (0)
489 if (again == 0)
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));
500 if (ret < 0)
501 return ret;
502 ret = _gnutls_set_write_mac (session,
503 _gnutls_cipher_suite_get_mac_algo
504 (&session->security_parameters.
505 current_cipher_suite));
506 if (ret < 0)
507 return ret;
508 ret = _gnutls_set_write_compression (session,
509 session->internals.
510 compression_method);
511 if (ret < 0)
512 return ret;
514 ret = encrypt_ticket (session, &ticket);
515 RESTORE_WRITE_SECURITY_PARAMETERS;
516 if (ret < 0)
518 gnutls_assert ();
519 return ret;
522 ticket_len = KEY_NAME_SIZE + IV_SIZE + 2 + ticket.encrypted_state_len
523 + MAC_SIZE;
525 data = gnutls_malloc (4 + 2 + ticket_len);
526 if (!data)
528 gnutls_assert ();
529 gnutls_free (ticket.encrypted_state);
530 return GNUTLS_E_MEMORY_ERROR;
533 p = data;
534 /* FIXME: ticket lifetime is fixed to 10 days, which should be
535 customizable. */
536 _gnutls_write_uint32 (864000, p);
537 p += 4;
539 _gnutls_write_uint16 (ticket_len, p);
540 p += 2;
542 memcpy (p, ticket.key_name, KEY_NAME_SIZE);
543 p += KEY_NAME_SIZE;
545 memcpy (p, ticket.IV, IV_SIZE);
546 p += IV_SIZE;
548 _gnutls_write_uint16 (ticket.encrypted_state_len, p);
549 p += 2;
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);
556 p += 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);
563 gnutls_free (data);
565 return ret;
569 _gnutls_recv_new_session_ticket (gnutls_session_t session)
571 uint8_t *data = NULL, *p;
572 int data_size;
573 uint32_t lifetime_hint;
574 uint16_t ticket_len;
575 int ret;
577 ret = _gnutls_recv_handshake (session, &data, &data_size,
578 GNUTLS_HANDSHAKE_NEW_SESSION_TICKET,
579 MANDATORY_PACKET);
580 if (ret < 0)
582 gnutls_assert ();
583 return ret;
586 p = data;
587 DECR_LENGTH_COM (data_size, 4, goto error);
588 lifetime_hint = _gnutls_read_uint32 (p);
589 p += 4;
591 DECR_LENGTH_COM (data_size, 2, goto error);
592 ticket_len = _gnutls_read_uint16 (p);
593 p += 2;
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,
598 ticket_len);
599 if (!session->security_parameters.extensions.session_ticket)
601 gnutls_assert ();
602 gnutls_free (data);
603 return GNUTLS_E_MEMORY_ERROR;
605 memcpy (session->security_parameters.extensions.session_ticket,
606 p, ticket_len);
607 gnutls_free (data);
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.
613 session_id_size);
614 if (ret < 0)
616 gnutls_assert ();
617 gnutls_free (data);
618 gnutls_free (session->security_parameters.extensions.session_ticket);
619 return GNUTLS_E_INTERNAL_ERROR;
621 return 0;
623 error:
624 gnutls_free (data);
625 return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
628 #endif