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>
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,
256
session->internals.session_ticket_renew = 1;
260
DECR_LEN (data_size, IV_SIZE);
261
memcpy (ticket.IV, data, IV_SIZE);
264
DECR_LEN (data_size, 2);
265
ticket.encrypted_state_len = _gnutls_read_uint16 (data);
268
encrypted_state = data;
270
DECR_LEN (data_size, ticket.encrypted_state_len);
271
data += ticket.encrypted_state_len;
273
DECR_LEN (data_size, MAC_SIZE);
274
memcpy (ticket.mac, data, MAC_SIZE);
276
ticket.encrypted_state = gnutls_malloc (ticket.encrypted_state_len);
277
if (!ticket.encrypted_state)
280
return GNUTLS_E_MEMORY_ERROR;
282
memcpy (ticket.encrypted_state, encrypted_state,
283
ticket.encrypted_state_len);
285
ret = decrypt_ticket (session, &ticket);
286
gnutls_free (ticket.encrypted_state);
289
session->internals.session_ticket_renew = 1;
297
session->internals.session_ticket_renew = 1;
305
/* returns a positive number if we send the extension data, zero if we
306
do not want to send it, and a negative number on failure.
309
_gnutls_session_ticket_send_params (gnutls_session_t session,
310
opaque * data, size_t _data_size)
312
ssize_t data_size = _data_size;
314
if (!session->internals.session_ticket_enable)
317
if (session->security_parameters.entity == GNUTLS_SERVER)
319
if (session->internals.session_ticket_renew)
321
return GNUTLS_E_INT_RET_0;
326
if (session->internals.resumed_security_parameters.
327
extensions.session_ticket_len > 0)
329
DECR_LENGTH_RET (data_size,
331
resumed_security_parameters.extensions.
332
session_ticket_len, GNUTLS_E_SHORT_MEMORY_BUFFER);
334
session->internals.resumed_security_parameters.
335
extensions.session_ticket,
336
session->internals.resumed_security_parameters.
337
extensions.session_ticket_len);
339
return session->internals.resumed_security_parameters.extensions.
344
return GNUTLS_E_INT_RET_0;
351
* gnutls_session_ticket_key_generate:
352
* @key: is a pointer to a #gnutls_datum_t which will contain a newly
355
* Generate a random key to encrypt security parameters within
358
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
364
gnutls_session_ticket_key_generate (gnutls_datum_t * key)
368
key->size = sizeof (struct gnutls_session_ticket_key_st);
369
key->data = gnutls_malloc (key->size);
373
return GNUTLS_E_MEMORY_ERROR;
376
ret = _gnutls_rnd (GNUTLS_RND_RANDOM, key->data, key->size);
380
_gnutls_free_datum (key);
388
* gnutls_session_ticket_enable_client:
389
* @session: is a #gnutls_session_t structure.
391
* Request that the client should attempt session resumption using
394
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
400
gnutls_session_ticket_enable_client (gnutls_session_t session)
405
return GNUTLS_E_INVALID_REQUEST;
408
session->internals.session_ticket_enable = 1;
413
* gnutls_session_ticket_enable_server:
414
* @session: is a #gnutls_session_t structure.
415
* @key: key to encrypt session parameters.
417
* Request that the server should attempt session resumption using
418
* SessionTicket. @key must be initialized with
419
* gnutls_session_ticket_key_generate().
421
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
427
gnutls_session_ticket_enable_server (gnutls_session_t session,
428
const gnutls_datum_t * key)
433
|| key->size != sizeof (struct gnutls_session_ticket_key_st))
436
return GNUTLS_E_INVALID_REQUEST;
439
ret = _gnutls_rnd (GNUTLS_RND_RANDOM,
440
session->internals.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
499
security_parameters.current_cipher_suite));
502
ret = _gnutls_set_write_mac (session,
503
_gnutls_cipher_suite_get_mac_algo
505
security_parameters.current_cipher_suite));
508
ret = _gnutls_set_write_compression (session,
510
internals.compression_method);
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,
613
security_parameters.session_id_size);
617
gnutls_free (session->security_parameters.extensions.session_ticket);
618
return GNUTLS_E_INTERNAL_ERROR;
624
return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;