585544b4ebf05e5786dde35bad59d12b085b2fdc
[platform/upstream/gnutls.git] / lib / ext / session_ticket.c
1 /*
2  * Copyright (C) 2009-2012 Free Software Foundation, Inc.
3  *
4  * Author: Daiki Ueno
5  *
6  * This file is part of GnuTLS.
7  *
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.
12  *
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.
17  *
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/>
20  *
21  */
22
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>
31 #include <random.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>
37
38 #ifdef ENABLE_SESSION_TICKETS
39
40 #define KEY_NAME_SIZE SESSION_TICKET_KEY_NAME_SIZE
41 #define KEY_SIZE SESSION_TICKET_KEY_SIZE
42 #define IV_SIZE 12 /* GCM */
43
44 #define TAG_SIZE 16 /* GCM */
45
46 static int session_ticket_recv_params(gnutls_session_t session,
47                                       const uint8_t * data,
48                                       size_t data_size);
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);
56
57 extension_entry_st ext_mod_session_ticket = {
58         .name = "SESSION TICKET",
59         .type = GNUTLS_EXTENSION_SESSION_TICKET,
60         .parse_type = GNUTLS_EXT_MANDATORY,
61
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,
67 };
68
69 #define SESSION_KEY_SIZE (SESSION_TICKET_KEY_NAME_SIZE+SESSION_TICKET_KEY_SIZE)
70 #define NAME_POS (0)
71 #define KEY_POS (SESSION_TICKET_KEY_NAME_SIZE)
72
73 typedef struct {
74         int session_ticket_enable;
75         int session_ticket_renew;
76
77         uint8_t *session_ticket;
78         int session_ticket_len;
79
80         uint8_t key[SESSION_KEY_SIZE];
81 } session_ticket_ext_st;
82
83 struct ticket_st {
84         uint8_t key_name[KEY_NAME_SIZE];
85         uint8_t IV[IV_SIZE];
86         uint8_t *encrypted_state;
87         uint16_t encrypted_state_len;
88         uint8_t tag[TAG_SIZE];
89 };
90
91 static int
92 decrypt_ticket(gnutls_session_t session, session_ticket_ext_st * priv,
93                struct ticket_st *ticket)
94 {
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);
99         int ret;
100
101         /* Decrypt encrypted_state using 128-bit AES in GCM mode. */
102         key.data = (void *) &priv->key[KEY_POS];
103         key.size = KEY_SIZE;
104         IV.data = ticket->IV;
105         IV.size = IV_SIZE;
106         ret =
107             _gnutls_cipher_init(&cipher_hd,
108                                 cipher_to_entry(GNUTLS_CIPHER_AES_128_GCM),
109                                 &key, &IV, 0);
110         if (ret < 0) {
111                 gnutls_assert();
112                 return ret;
113         }
114         ret = _gnutls_cipher_decrypt(&cipher_hd, ticket->encrypted_state,
115                                      ticket->encrypted_state_len);
116         if (ret < 0) {
117                 gnutls_assert();
118                 goto cleanup;
119         }
120         
121         _gnutls_cipher_tag(&cipher_hd, final, TAG_SIZE);
122         if (gnutls_memcmp(ticket->tag, final, TAG_SIZE) != 0) {
123                 gnutls_assert();
124                 ret = GNUTLS_E_DECRYPTION_FAILED;
125                 goto cleanup;
126         }
127
128         /* Unpack security parameters. */
129         state.data = ticket->encrypted_state;
130         state.size = ticket->encrypted_state_len;
131         ret = _gnutls_session_unpack(session, &state);
132         if (ret < 0) {
133                 gnutls_assert();
134                 goto cleanup;
135         }
136
137         if (timestamp -
138             session->internals.resumed_security_parameters.timestamp >
139             session->internals.expire_time
140             || session->internals.resumed_security_parameters.timestamp >
141             timestamp) {
142                 gnutls_assert();
143                 ret = GNUTLS_E_EXPIRED;
144                 goto cleanup;
145         }
146
147         session->internals.resumed = RESUME_TRUE;
148
149         ret = 0;
150 cleanup:
151         _gnutls_cipher_deinit(&cipher_hd);
152         
153         return ret;
154
155 }
156
157 static int
158 encrypt_ticket(gnutls_session_t session, session_ticket_ext_st * priv,
159                struct ticket_st *ticket)
160 {
161         cipher_hd_st cipher_hd;
162         gnutls_datum_t key, IV;
163         gnutls_datum_t state = {NULL,0}, encrypted_state = {NULL,0};
164         uint8_t iv[IV_SIZE];
165         uint32_t t;
166         int ret;
167
168         /* Pack security parameters. */
169         ret = _gnutls_session_pack(session, &state);
170         if (ret < 0) {
171                 gnutls_assert();
172                 return ret;
173         }
174
175         encrypted_state.size = state.size;
176         encrypted_state.data = gnutls_malloc(encrypted_state.size);
177         if (!encrypted_state.data) {
178                 gnutls_assert();
179                 ret = GNUTLS_E_MEMORY_ERROR;
180                 goto cleanup;
181         }
182         memset(encrypted_state.data, 0, encrypted_state.size);
183         memcpy(encrypted_state.data, state.data, state.size);
184
185         /* Encrypt state using 128-bit AES in GCM mode. */
186         key.data = (void *) &priv->key[KEY_POS];
187         key.size = KEY_SIZE;
188         IV.data = iv;
189         IV.size = IV_SIZE;
190         
191         t = gnutls_time(0);
192         memcpy(iv, &t, 4);
193         ret = _gnutls_rnd(GNUTLS_RND_NONCE, iv+4, IV_SIZE-4);
194         if (ret < 0) {
195                 gnutls_assert();
196                 goto cleanup;
197         }
198
199         ret =
200             _gnutls_cipher_init(&cipher_hd,
201                                 cipher_to_entry(GNUTLS_CIPHER_AES_128_GCM),
202                                 &key, &IV, 1);
203         if (ret < 0) {
204                 gnutls_assert();
205                 goto cleanup;
206         }
207
208         ret = _gnutls_cipher_encrypt(&cipher_hd, encrypted_state.data,
209                                      encrypted_state.size);
210         if (ret < 0) {
211                 gnutls_assert();
212                 goto cleanup2;
213         }
214         
215         _gnutls_cipher_tag(&cipher_hd, ticket->tag, TAG_SIZE);
216
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;
222         
223         encrypted_state.data = NULL;
224
225         ret = 0;
226
227 cleanup2:
228         _gnutls_cipher_deinit(&cipher_hd);
229
230 cleanup:
231         _gnutls_free_datum(&state);
232         _gnutls_free_datum(&encrypted_state);
233
234         return ret;
235 }
236
237 static int
238 session_ticket_recv_params(gnutls_session_t session,
239                            const uint8_t * data, size_t _data_size)
240 {
241         ssize_t data_size = _data_size;
242         session_ticket_ext_st *priv = NULL;
243         extension_priv_data_t epriv;
244         int ret;
245
246         ret =
247             _gnutls_ext_get_session_data(session,
248                                          GNUTLS_EXTENSION_SESSION_TICKET,
249                                          &epriv);
250         if (ret < 0) {
251                 return 0;
252         }
253         priv = epriv;
254
255         if (!priv->session_ticket_enable)
256                 return 0;
257
258         if (session->security_parameters.entity == GNUTLS_SERVER) {
259                 struct ticket_st ticket;
260                 const uint8_t *encrypted_state;
261
262                 /* The client requested a new session ticket. */
263                 if (data_size == 0) {
264                         priv->session_ticket_renew = 1;
265                         return 0;
266                 }
267
268                 DECR_LEN(data_size, KEY_NAME_SIZE);
269                 memcpy(ticket.key_name, data, KEY_NAME_SIZE);
270                 data += KEY_NAME_SIZE;
271
272                 /* If the key name of the ticket does not match the one that we
273                    hold, issue a new ticket. */
274                 if (memcmp
275                     (ticket.key_name, &priv->key[NAME_POS],
276                      KEY_NAME_SIZE)) {
277                         priv->session_ticket_renew = 1;
278                         return 0;
279                 }
280
281                 DECR_LEN(data_size, IV_SIZE);
282                 memcpy(ticket.IV, data, IV_SIZE);
283                 data += IV_SIZE;
284
285                 DECR_LEN(data_size, 2);
286                 ticket.encrypted_state_len = _gnutls_read_uint16(data);
287                 data += 2;
288
289                 encrypted_state = data;
290
291                 DECR_LEN(data_size, ticket.encrypted_state_len);
292                 data += ticket.encrypted_state_len;
293
294                 DECR_LEN(data_size, TAG_SIZE);
295                 memcpy(ticket.tag, data, TAG_SIZE);
296
297                 ticket.encrypted_state =
298                     gnutls_malloc(ticket.encrypted_state_len);
299                 if (!ticket.encrypted_state) {
300                         gnutls_assert();
301                         return GNUTLS_E_MEMORY_ERROR;
302                 }
303                 memcpy(ticket.encrypted_state, encrypted_state,
304                        ticket.encrypted_state_len);
305
306                 ret = decrypt_ticket(session, priv, &ticket);
307                 gnutls_free(ticket.encrypted_state);
308                 if (ret < 0) {
309                         priv->session_ticket_renew = 1;
310                         return 0;
311                 }
312         } else {                /* Client */
313
314                 if (data_size == 0) {
315                         priv->session_ticket_renew = 1;
316                         return 0;
317                 }
318         }
319
320         return 0;
321 }
322
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.
325  */
326 static int
327 session_ticket_send_params(gnutls_session_t session,
328                            gnutls_buffer_st * extdata)
329 {
330         session_ticket_ext_st *priv = NULL;
331         extension_priv_data_t epriv;
332         int ret;
333
334         ret =
335             _gnutls_ext_get_session_data(session,
336                                          GNUTLS_EXTENSION_SESSION_TICKET,
337                                          &epriv);
338         if (ret >= 0)
339                 priv = epriv;
340
341         if (priv == NULL || !priv->session_ticket_enable)
342                 return 0;
343
344         if (session->security_parameters.entity == GNUTLS_SERVER) {
345                 if (priv && priv->session_ticket_renew) {
346                         return GNUTLS_E_INT_RET_0;
347                 }
348         } else {
349                 ret =
350                     _gnutls_ext_get_resumed_session_data(session,
351                                                          GNUTLS_EXTENSION_SESSION_TICKET,
352                                                          &epriv);
353                 if (ret >= 0)
354                         priv = epriv;
355
356                 /* no previous data. Just advertize it */
357                 if (ret < 0)
358                         return GNUTLS_E_INT_RET_0;
359
360                 /* previous data had session tickets disabled. Don't advertize. Ignore. */
361                 if (!priv->session_ticket_enable)
362                         return 0;
363
364                 if (priv->session_ticket_len > 0) {
365                         ret =
366                             _gnutls_buffer_append_data(extdata,
367                                                        priv->
368                                                        session_ticket,
369                                                        priv->
370                                                        session_ticket_len);
371                         if (ret < 0)
372                                 return gnutls_assert_val(ret);
373
374                         return priv->session_ticket_len;
375                 }
376         }
377         return 0;
378 }
379
380
381 static void session_ticket_deinit_data(extension_priv_data_t epriv)
382 {
383         session_ticket_ext_st *priv = epriv;
384
385         gnutls_free(priv->session_ticket);
386         gnutls_free(priv);
387 }
388
389 static int
390 session_ticket_pack(extension_priv_data_t epriv, gnutls_buffer_st * ps)
391 {
392         session_ticket_ext_st *priv = epriv;
393         int ret;
394
395         BUFFER_APPEND_PFX4(ps, priv->session_ticket,
396                            priv->session_ticket_len);
397         BUFFER_APPEND_NUM(ps, priv->session_ticket_enable);
398
399         return 0;
400 }
401
402 static int
403 session_ticket_unpack(gnutls_buffer_st * ps, extension_priv_data_t * _priv)
404 {
405         session_ticket_ext_st *priv = NULL;
406         int ret;
407         extension_priv_data_t epriv;
408         gnutls_datum_t ticket;
409
410         priv = gnutls_calloc(1, sizeof(*priv));
411         if (priv == NULL) {
412                 gnutls_assert();
413                 return GNUTLS_E_MEMORY_ERROR;
414         }
415
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);
420
421         epriv = priv;
422         *_priv = epriv;
423
424         return 0;
425
426       error:
427         gnutls_free(priv);
428         return ret;
429 }
430
431
432
433 /**
434  * gnutls_session_ticket_key_generate:
435  * @key: is a pointer to a #gnutls_datum_t which will contain a newly
436  * created key.
437  *
438  * Generate a random key to encrypt security parameters within
439  * SessionTicket.
440  *
441  * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
442  * error code.
443  *
444  * Since: 2.10.0
445  **/
446 int gnutls_session_ticket_key_generate(gnutls_datum_t * key)
447 {
448         return gnutls_key_generate(key, SESSION_KEY_SIZE);
449 }
450
451 /**
452  * gnutls_session_ticket_enable_client:
453  * @session: is a #gnutls_session_t type.
454  *
455  * Request that the client should attempt session resumption using
456  * SessionTicket.
457  *
458  * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
459  * error code.
460  *
461  * Since: 2.10.0
462  **/
463 int gnutls_session_ticket_enable_client(gnutls_session_t session)
464 {
465         session_ticket_ext_st *priv = NULL;
466         extension_priv_data_t epriv;
467
468         if (!session) {
469                 gnutls_assert();
470                 return GNUTLS_E_INVALID_REQUEST;
471         }
472
473         priv = gnutls_calloc(1, sizeof(*priv));
474         if (priv == NULL) {
475                 gnutls_assert();
476                 return GNUTLS_E_MEMORY_ERROR;
477         }
478         priv->session_ticket_enable = 1;
479         epriv = priv;
480
481         _gnutls_ext_set_session_data(session,
482                                      GNUTLS_EXTENSION_SESSION_TICKET,
483                                      epriv);
484
485         return 0;
486 }
487
488 /**
489  * gnutls_session_ticket_enable_server:
490  * @session: is a #gnutls_session_t type.
491  * @key: key to encrypt session parameters.
492  *
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.
497  *
498  * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
499  * error code.
500  *
501  * Since: 2.10.0
502  **/
503 int
504 gnutls_session_ticket_enable_server(gnutls_session_t session,
505                                     const gnutls_datum_t * key)
506 {
507         session_ticket_ext_st *priv = NULL;
508         extension_priv_data_t epriv;
509
510         if (!session || !key || key->size != SESSION_KEY_SIZE) {
511                 gnutls_assert();
512                 return GNUTLS_E_INVALID_REQUEST;
513         }
514
515         priv = gnutls_calloc(1, sizeof(*priv));
516         if (priv == NULL) {
517                 gnutls_assert();
518                 return GNUTLS_E_MEMORY_ERROR;
519         }
520         epriv = priv;
521
522         memcpy(&priv->key, key->data, key->size);
523         priv->session_ticket_enable = 1;
524
525         _gnutls_ext_set_session_data(session,
526                                      GNUTLS_EXTENSION_SESSION_TICKET,
527                                      epriv);
528
529         return 0;
530 }
531
532 int _gnutls_send_new_session_ticket(gnutls_session_t session, int again)
533 {
534         mbuffer_st *bufel = NULL;
535         uint8_t *data = NULL, *p;
536         int data_size = 0;
537         int ret;
538         struct ticket_st ticket;
539         uint16_t ticket_len;
540         session_ticket_ext_st *priv = NULL;
541         extension_priv_data_t epriv;
542         uint16_t epoch_saved = session->security_parameters.epoch_write;
543
544         if (again == 0) {
545                 ret =
546                     _gnutls_ext_get_session_data(session,
547                                                  GNUTLS_EXTENSION_SESSION_TICKET,
548                                                  &epriv);
549                 if (ret < 0)
550                         return 0;
551                 priv = epriv;
552
553                 if (!priv->session_ticket_renew)
554                         return 0;
555
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. */
560                 ret =
561                     _gnutls_epoch_set_keys(session,
562                                            session->security_parameters.
563                                            epoch_next);
564                 if (ret < 0) {
565                         gnutls_assert();
566                         return ret;
567                 }
568
569                 session->security_parameters.epoch_write =
570                     session->security_parameters.epoch_next;
571
572                 ret = encrypt_ticket(session, priv, &ticket);
573                 session->security_parameters.epoch_write = epoch_saved;
574                 if (ret < 0) {
575                         gnutls_assert();
576                         return ret;
577                 }
578
579                 ticket_len =
580                     KEY_NAME_SIZE + IV_SIZE + 2 +
581                     ticket.encrypted_state_len + TAG_SIZE;
582
583                 bufel =
584                     _gnutls_handshake_alloc(session, 
585                                             4 + 2 + ticket_len);
586                 if (!bufel) {
587                         gnutls_assert();
588                         gnutls_free(ticket.encrypted_state);
589                         return GNUTLS_E_MEMORY_ERROR;
590                 }
591
592                 data = _mbuffer_get_udata_ptr(bufel);
593                 p = data;
594
595                 _gnutls_write_uint32(session->internals.expire_time, p);
596                 p += 4;
597
598                 _gnutls_write_uint16(ticket_len, p);
599                 p += 2;
600
601                 memcpy(p, ticket.key_name, KEY_NAME_SIZE);
602                 p += KEY_NAME_SIZE;
603
604                 memcpy(p, ticket.IV, IV_SIZE);
605                 p += IV_SIZE;
606
607                 _gnutls_write_uint16(ticket.encrypted_state_len, p);
608                 p += 2;
609
610                 memcpy(p, ticket.encrypted_state, ticket.encrypted_state_len);
611                 gnutls_free(ticket.encrypted_state);
612                 p += ticket.encrypted_state_len;
613
614                 memcpy(p, ticket.tag, TAG_SIZE);
615                 p += TAG_SIZE;
616
617                 data_size = p - data;
618
619                 session->internals.ticket_sent = 1;
620         }
621         return _gnutls_send_handshake(session, data_size ? bufel : NULL,
622                                       GNUTLS_HANDSHAKE_NEW_SESSION_TICKET);
623 }
624
625 int _gnutls_recv_new_session_ticket(gnutls_session_t session)
626 {
627         uint8_t *p;
628         int data_size;
629         gnutls_buffer_st buf;
630         uint16_t ticket_len;
631         int ret;
632         session_ticket_ext_st *priv = NULL;
633         extension_priv_data_t epriv;
634
635         ret =
636             _gnutls_ext_get_session_data(session,
637                                          GNUTLS_EXTENSION_SESSION_TICKET,
638                                          &epriv);
639         if (ret < 0) {
640                 gnutls_assert();
641                 return 0;
642         }
643         priv = epriv;
644
645         if (!priv->session_ticket_renew)
646                 return 0;
647
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);
655                 if (ret < 0)
656                         return gnutls_assert_val(ret);
657         }
658
659         ret = _gnutls_recv_handshake(session,
660                                      GNUTLS_HANDSHAKE_NEW_SESSION_TICKET,
661                                      0, &buf);
662         if (ret < 0)
663                 return gnutls_assert_val_fatal(ret);
664
665         p = buf.data;
666         data_size = buf.length;
667
668         DECR_LENGTH_COM(data_size, 4, ret =
669                         GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
670                         goto error);
671         /* skip over lifetime hint */
672         p += 4;
673
674         DECR_LENGTH_COM(data_size, 2, ret =
675                         GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
676                         goto error);
677         ticket_len = _gnutls_read_uint16(p);
678         p += 2;
679
680         DECR_LENGTH_COM(data_size, ticket_len, ret =
681                         GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
682                         goto error);
683         priv->session_ticket =
684             gnutls_realloc_fast(priv->session_ticket, ticket_len);
685         if (!priv->session_ticket) {
686                 gnutls_assert();
687                 ret = GNUTLS_E_MEMORY_ERROR;
688                 goto error;
689         }
690         memcpy(priv->session_ticket, p, ticket_len);
691         priv->session_ticket_len = ticket_len;
692
693         /* Discard the current session ID.  (RFC5077 3.4) */
694         ret =
695             _gnutls_generate_session_id(session->security_parameters.
696                                         session_id,
697                                         &session->security_parameters.
698                                         session_id_size);
699         if (ret < 0) {
700                 gnutls_assert();
701                 gnutls_free(priv->session_ticket);
702                 priv->session_ticket = NULL;
703                 ret = GNUTLS_E_INTERNAL_ERROR;
704                 goto error;
705         }
706         ret = 0;
707
708       error:
709         _gnutls_buffer_clear(&buf);
710
711         return ret;
712 }
713
714 #endif