35bad36f12cd060ac9f1cacf7ff8ca0bdcbf76d1
[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
37 #ifdef ENABLE_SESSION_TICKETS
38
39 #define KEY_NAME_SIZE SESSION_TICKET_KEY_NAME_SIZE
40 #define KEY_SIZE SESSION_TICKET_KEY_SIZE
41 #define IV_SIZE 12 /* GCM */
42
43 #define TAG_SIZE 16 /* GCM */
44
45 static int session_ticket_recv_params(gnutls_session_t session,
46                                       const uint8_t * data,
47                                       size_t data_size);
48 static int session_ticket_send_params(gnutls_session_t session,
49                                       gnutls_buffer_st * extdata);
50 static int session_ticket_unpack(gnutls_buffer_st * ps,
51                                  extension_priv_data_t * _priv);
52 static int session_ticket_pack(extension_priv_data_t _priv,
53                                gnutls_buffer_st * ps);
54 static void session_ticket_deinit_data(extension_priv_data_t priv);
55
56 extension_entry_st ext_mod_session_ticket = {
57         .name = "SESSION TICKET",
58         .type = GNUTLS_EXTENSION_SESSION_TICKET,
59         .parse_type = GNUTLS_EXT_MANDATORY,
60
61         .recv_func = session_ticket_recv_params,
62         .send_func = session_ticket_send_params,
63         .pack_func = session_ticket_pack,
64         .unpack_func = session_ticket_unpack,
65         .deinit_func = session_ticket_deinit_data,
66 };
67
68 #define SESSION_KEY_SIZE (SESSION_TICKET_KEY_NAME_SIZE+SESSION_TICKET_KEY_SIZE)
69 #define NAME_POS (0)
70 #define KEY_POS (SESSION_TICKET_KEY_NAME_SIZE)
71
72 typedef struct {
73         int session_ticket_enable;
74         int session_ticket_renew;
75
76         uint8_t *session_ticket;
77         int session_ticket_len;
78
79         uint8_t key[SESSION_KEY_SIZE];
80 } session_ticket_ext_st;
81
82 struct ticket_st {
83         uint8_t key_name[KEY_NAME_SIZE];
84         uint8_t IV[IV_SIZE];
85         uint8_t *encrypted_state;
86         uint16_t encrypted_state_len;
87         uint8_t tag[TAG_SIZE];
88 };
89
90 static int
91 decrypt_ticket(gnutls_session_t session, session_ticket_ext_st * priv,
92                struct ticket_st *ticket)
93 {
94         cipher_hd_st cipher_hd;
95         gnutls_datum_t key, IV, state;
96         uint8_t final[TAG_SIZE];
97         time_t timestamp = gnutls_time(0);
98         int ret;
99
100         /* Decrypt encrypted_state using 128-bit AES in CBC mode. */
101         key.data = (void *) &priv->key[KEY_POS];
102         key.size = KEY_SIZE;
103         IV.data = ticket->IV;
104         IV.size = IV_SIZE;
105         ret =
106             _gnutls_cipher_init(&cipher_hd,
107                                 cipher_to_entry(GNUTLS_CIPHER_AES_128_GCM),
108                                 &key, &IV, 0);
109         if (ret < 0) {
110                 gnutls_assert();
111                 return ret;
112         }
113         ret = _gnutls_cipher_decrypt(&cipher_hd, ticket->encrypted_state,
114                                      ticket->encrypted_state_len);
115         if (ret < 0) {
116                 gnutls_assert();
117                 goto cleanup;
118         }
119         
120         _gnutls_cipher_tag(&cipher_hd, final, TAG_SIZE);
121         if (memcmp(ticket->tag, final, TAG_SIZE) != 0) {
122                 gnutls_assert();
123                 ret = GNUTLS_E_DECRYPTION_FAILED;
124                 goto cleanup;
125         }
126
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) {
132                 gnutls_assert();
133                 goto cleanup;
134         }
135
136         if (timestamp -
137             session->internals.resumed_security_parameters.timestamp >
138             session->internals.expire_time
139             || session->internals.resumed_security_parameters.timestamp >
140             timestamp) {
141                 gnutls_assert();
142                 ret = GNUTLS_E_EXPIRED;
143                 goto cleanup;
144         }
145
146         session->internals.resumed = RESUME_TRUE;
147
148         ret = 0;
149 cleanup:
150         _gnutls_cipher_deinit(&cipher_hd);
151         
152         return ret;
153
154 }
155
156 static int
157 encrypt_ticket(gnutls_session_t session, session_ticket_ext_st * priv,
158                struct ticket_st *ticket)
159 {
160         cipher_hd_st cipher_hd;
161         gnutls_datum_t key, IV;
162         gnutls_datum_t state = {NULL,0}, encrypted_state = {NULL,0};
163         uint8_t iv[IV_SIZE];
164         uint32_t t;
165         int ret;
166
167         /* Pack security parameters. */
168         ret = _gnutls_session_pack(session, &state);
169         if (ret < 0) {
170                 gnutls_assert();
171                 return ret;
172         }
173
174         encrypted_state.size = state.size;
175         encrypted_state.data = gnutls_malloc(encrypted_state.size);
176         if (!encrypted_state.data) {
177                 gnutls_assert();
178                 ret = GNUTLS_E_MEMORY_ERROR;
179                 goto cleanup;
180         }
181         memset(encrypted_state.data, 0, encrypted_state.size);
182         memcpy(encrypted_state.data, state.data, state.size);
183
184         /* Encrypt state using 128-bit AES in GCM mode. */
185         key.data = (void *) &priv->key[KEY_POS];
186         key.size = KEY_SIZE;
187         IV.data = iv;
188         IV.size = IV_SIZE;
189         
190         t = gnutls_time(0);
191         memcpy(iv, &t, 4);
192         ret = _gnutls_rnd(GNUTLS_RND_NONCE, iv+4, IV_SIZE-4);
193         if (ret < 0) {
194                 gnutls_assert();
195                 goto cleanup;
196         }
197
198         ret =
199             _gnutls_cipher_init(&cipher_hd,
200                                 cipher_to_entry(GNUTLS_CIPHER_AES_128_GCM),
201                                 &key, &IV, 1);
202         if (ret < 0) {
203                 gnutls_assert();
204                 goto cleanup;
205         }
206
207         ret = _gnutls_cipher_encrypt(&cipher_hd, encrypted_state.data,
208                                      encrypted_state.size);
209         if (ret < 0) {
210                 gnutls_assert();
211                 goto cleanup2;
212         }
213         
214         _gnutls_cipher_tag(&cipher_hd, ticket->tag, TAG_SIZE);
215
216         /* Fill the ticket structure to compute MAC. */
217         memcpy(ticket->key_name, &priv->key[NAME_POS], KEY_NAME_SIZE);
218         memcpy(ticket->IV, IV.data, IV.size);
219         ticket->encrypted_state_len = encrypted_state.size;
220         ticket->encrypted_state = encrypted_state.data;
221         
222         encrypted_state.data = NULL;
223
224         ret = 0;
225
226 cleanup2:
227         _gnutls_cipher_deinit(&cipher_hd);
228
229 cleanup:
230         _gnutls_free_datum(&state);
231         _gnutls_free_datum(&encrypted_state);
232
233         return ret;
234 }
235
236 static int
237 session_ticket_recv_params(gnutls_session_t session,
238                            const uint8_t * data, size_t _data_size)
239 {
240         ssize_t data_size = _data_size;
241         session_ticket_ext_st *priv = NULL;
242         extension_priv_data_t epriv;
243         int ret;
244
245         ret =
246             _gnutls_ext_get_session_data(session,
247                                          GNUTLS_EXTENSION_SESSION_TICKET,
248                                          &epriv);
249         if (ret < 0) {
250                 return 0;
251         }
252         priv = epriv.ptr;
253
254         if (!priv->session_ticket_enable)
255                 return 0;
256
257         if (session->security_parameters.entity == GNUTLS_SERVER) {
258                 struct ticket_st ticket;
259                 const uint8_t *encrypted_state;
260                 int ret;
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.ptr;
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.ptr;
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.ptr;
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.ptr;
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.ptr = 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 structure.
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.ptr = 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 structure.
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().
496  *
497  * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
498  * error code.
499  *
500  * Since: 2.10.0
501  **/
502 int
503 gnutls_session_ticket_enable_server(gnutls_session_t session,
504                                     const gnutls_datum_t * key)
505 {
506         session_ticket_ext_st *priv = NULL;
507         extension_priv_data_t epriv;
508
509         if (!session || !key || key->size != SESSION_KEY_SIZE) {
510                 gnutls_assert();
511                 return GNUTLS_E_INVALID_REQUEST;
512         }
513
514         priv = gnutls_calloc(1, sizeof(*priv));
515         if (priv == NULL) {
516                 gnutls_assert();
517                 return GNUTLS_E_MEMORY_ERROR;
518         }
519         epriv.ptr = priv;
520
521         memcpy(&priv->key, key->data, key->size);
522         priv->session_ticket_enable = 1;
523
524         _gnutls_ext_set_session_data(session,
525                                      GNUTLS_EXTENSION_SESSION_TICKET,
526                                      epriv);
527
528         return 0;
529 }
530
531 int _gnutls_send_new_session_ticket(gnutls_session_t session, int again)
532 {
533         mbuffer_st *bufel = NULL;
534         uint8_t *data = NULL, *p;
535         int data_size = 0;
536         int ret;
537         struct ticket_st ticket;
538         uint16_t ticket_len;
539         session_ticket_ext_st *priv = NULL;
540         extension_priv_data_t epriv;
541         uint16_t epoch_saved = session->security_parameters.epoch_write;
542
543         if (again == 0) {
544                 ret =
545                     _gnutls_ext_get_session_data(session,
546                                                  GNUTLS_EXTENSION_SESSION_TICKET,
547                                                  &epriv);
548                 if (ret < 0)
549                         return 0;
550                 priv = epriv.ptr;
551
552                 if (!priv->session_ticket_renew)
553                         return 0;
554
555                 /* XXX: Temporarily set write algorithms to be used.
556                    _gnutls_write_connection_state_init() does this job, but it also
557                    triggers encryption, while NewSessionTicket should not be
558                    encrypted in the record layer. */
559                 ret =
560                     _gnutls_epoch_set_keys(session,
561                                            session->security_parameters.
562                                            epoch_next);
563                 if (ret < 0) {
564                         gnutls_assert();
565                         return ret;
566                 }
567
568                 session->security_parameters.epoch_write =
569                     session->security_parameters.epoch_next;
570
571                 ret = encrypt_ticket(session, priv, &ticket);
572                 session->security_parameters.epoch_write = epoch_saved;
573                 if (ret < 0) {
574                         gnutls_assert();
575                         return ret;
576                 }
577
578                 ticket_len =
579                     KEY_NAME_SIZE + IV_SIZE + 2 +
580                     ticket.encrypted_state_len + TAG_SIZE;
581
582                 bufel =
583                     _gnutls_handshake_alloc(session, 
584                                             4 + 2 + ticket_len);
585                 if (!bufel) {
586                         gnutls_assert();
587                         gnutls_free(ticket.encrypted_state);
588                         return GNUTLS_E_MEMORY_ERROR;
589                 }
590
591                 data = _mbuffer_get_udata_ptr(bufel);
592                 p = data;
593
594                 _gnutls_write_uint32(session->internals.expire_time, p);
595                 p += 4;
596
597                 _gnutls_write_uint16(ticket_len, p);
598                 p += 2;
599
600                 memcpy(p, ticket.key_name, KEY_NAME_SIZE);
601                 p += KEY_NAME_SIZE;
602
603                 memcpy(p, ticket.IV, IV_SIZE);
604                 p += IV_SIZE;
605
606                 _gnutls_write_uint16(ticket.encrypted_state_len, p);
607                 p += 2;
608
609                 memcpy(p, ticket.encrypted_state, ticket.encrypted_state_len);
610                 gnutls_free(ticket.encrypted_state);
611                 p += ticket.encrypted_state_len;
612
613                 memcpy(p, ticket.tag, TAG_SIZE);
614                 p += TAG_SIZE;
615
616                 data_size = p - data;
617
618                 session->internals.ticket_sent = 1;
619         }
620         return _gnutls_send_handshake(session, data_size ? bufel : NULL,
621                                       GNUTLS_HANDSHAKE_NEW_SESSION_TICKET);
622 }
623
624 int _gnutls_recv_new_session_ticket(gnutls_session_t session)
625 {
626         uint8_t *p;
627         int data_size;
628         gnutls_buffer_st buf;
629         uint16_t ticket_len;
630         int ret;
631         session_ticket_ext_st *priv = NULL;
632         extension_priv_data_t epriv;
633
634         ret =
635             _gnutls_ext_get_session_data(session,
636                                          GNUTLS_EXTENSION_SESSION_TICKET,
637                                          &epriv);
638         if (ret < 0) {
639                 gnutls_assert();
640                 return 0;
641         }
642         priv = epriv.ptr;
643
644         if (!priv->session_ticket_renew)
645                 return 0;
646
647         ret = _gnutls_recv_handshake(session,
648                                      GNUTLS_HANDSHAKE_NEW_SESSION_TICKET,
649                                      0, &buf);
650         if (ret < 0)
651                 return gnutls_assert_val_fatal(ret);
652
653         p = buf.data;
654         data_size = buf.length;
655
656         DECR_LENGTH_COM(data_size, 4, ret =
657                         GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
658                         goto error);
659         /* skip over lifetime hint */
660         p += 4;
661
662         DECR_LENGTH_COM(data_size, 2, ret =
663                         GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
664                         goto error);
665         ticket_len = _gnutls_read_uint16(p);
666         p += 2;
667
668         DECR_LENGTH_COM(data_size, ticket_len, ret =
669                         GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
670                         goto error);
671         priv->session_ticket =
672             gnutls_realloc_fast(priv->session_ticket, ticket_len);
673         if (!priv->session_ticket) {
674                 gnutls_assert();
675                 ret = GNUTLS_E_MEMORY_ERROR;
676                 goto error;
677         }
678         memcpy(priv->session_ticket, p, ticket_len);
679         priv->session_ticket_len = ticket_len;
680
681         /* Discard the current session ID.  (RFC5077 3.4) */
682         ret =
683             _gnutls_generate_session_id(session->security_parameters.
684                                         session_id,
685                                         &session->security_parameters.
686                                         session_id_size);
687         if (ret < 0) {
688                 gnutls_assert();
689                 gnutls_free(priv->session_ticket);
690                 priv->session_ticket = NULL;
691                 ret = GNUTLS_E_INTERNAL_ERROR;
692                 goto error;
693         }
694         ret = 0;
695
696       error:
697         _gnutls_buffer_clear(&buf);
698
699         return ret;
700 }
701
702 #endif