Coverage Report

Created: 2026-08-14 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gnutls/lib/tls13/session_ticket.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2017-2018 Red Hat, Inc.
3
 *
4
 * Author: Nikos Mavrogiannopoulos, Ander Juaristi
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 <https://www.gnu.org/licenses/>
20
 *
21
 */
22
23
#include "gnutls_int.h"
24
#include "errors.h"
25
#include "extv.h"
26
#include "handshake.h"
27
#include "mbuffers.h"
28
#include "ext/pre_shared_key.h"
29
#include "ext/session_ticket.h"
30
#include "ext/early_data.h"
31
#include "auth/cert.h"
32
#include "tls13/session_ticket.h"
33
#include "session_pack.h"
34
#include "db.h"
35
36
static int pack_ticket(gnutls_session_t session, tls13_ticket_st *ticket,
37
           gnutls_datum_t *packed)
38
0
{
39
0
  uint8_t *p;
40
0
  gnutls_datum_t state;
41
0
  int ret;
42
43
0
  ret = _gnutls_session_pack(session, &state);
44
0
  if (ret < 0)
45
0
    return gnutls_assert_val(ret);
46
47
0
  packed->size = 2 + 4 + 4 + 1 + ticket->prf->output_size + 1 +
48
0
           ticket->nonce_size + 2 + state.size + 12;
49
50
0
  packed->data = gnutls_malloc(packed->size);
51
0
  if (!packed->data) {
52
0
    gnutls_assert();
53
0
    ret = GNUTLS_E_MEMORY_ERROR;
54
0
    goto cleanup;
55
0
  }
56
57
0
  p = packed->data;
58
59
0
  _gnutls_write_uint16(ticket->prf->id, p);
60
0
  p += 2;
61
0
  _gnutls_write_uint32(ticket->age_add, p);
62
0
  p += 4;
63
0
  _gnutls_write_uint32(ticket->lifetime, p);
64
0
  p += 4;
65
0
  *p = ticket->prf->output_size;
66
0
  p += 1;
67
0
  memcpy(p, ticket->resumption_master_secret, ticket->prf->output_size);
68
0
  p += ticket->prf->output_size;
69
0
  *p = ticket->nonce_size;
70
71
0
  p += 1;
72
0
  memcpy(p, ticket->nonce, ticket->nonce_size);
73
0
  p += ticket->nonce_size;
74
75
0
  _gnutls_write_uint16(state.size, p);
76
0
  p += 2;
77
78
0
  memcpy(p, state.data, state.size);
79
0
  p += state.size;
80
81
0
  _gnutls_write_uint32((uint64_t)ticket->creation_time.tv_sec >> 32, p);
82
0
  p += 4;
83
0
  _gnutls_write_uint32(ticket->creation_time.tv_sec & 0xFFFFFFFF, p);
84
0
  p += 4;
85
0
  _gnutls_write_uint32(ticket->creation_time.tv_nsec, p);
86
87
0
  ret = 0;
88
89
0
cleanup:
90
0
  gnutls_free(state.data);
91
0
  return ret;
92
0
}
93
94
static int unpack_ticket(gnutls_session_t session, gnutls_datum_t *packed,
95
       tls13_ticket_st *data)
96
0
{
97
0
  uint32_t age_add, lifetime;
98
0
  struct timespec creation_time;
99
0
  uint8_t resumption_master_secret[MAX_HASH_SIZE];
100
0
  size_t resumption_master_secret_size;
101
0
  uint8_t nonce[UINT8_MAX];
102
0
  size_t nonce_size;
103
0
  gnutls_datum_t state;
104
0
  gnutls_mac_algorithm_t kdf;
105
0
  const mac_entry_st *prf;
106
0
  uint8_t *p;
107
0
  size_t len;
108
0
  uint64_t v;
109
0
  int ret;
110
111
0
  if (unlikely(packed == NULL || data == NULL))
112
0
    return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
113
114
0
  memset(data, 0, sizeof(*data));
115
116
0
  p = packed->data;
117
0
  len = packed->size;
118
119
0
  DECR_LEN(len, 2);
120
0
  kdf = _gnutls_read_uint16(p);
121
0
  p += 2;
122
123
  /* Check if the MAC ID we got is valid */
124
0
  prf = _gnutls_mac_to_entry(kdf);
125
0
  if (prf == NULL)
126
0
    return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
127
128
  /* Read the ticket age add and the ticket lifetime */
129
0
  DECR_LEN(len, 4);
130
0
  age_add = _gnutls_read_uint32(p);
131
0
  p += 4;
132
133
0
  DECR_LEN(len, 4);
134
0
  lifetime = _gnutls_read_uint32(p);
135
0
  p += 4;
136
137
  /*
138
   * Check if the whole ticket is large enough,
139
   * and read the resumption master secret
140
   */
141
0
  DECR_LEN(len, 1);
142
0
  resumption_master_secret_size = *p;
143
0
  p += 1;
144
145
  /* Check if the size of resumption_master_secret matches the PRF */
146
0
  if (resumption_master_secret_size != prf->output_size)
147
0
    return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
148
149
0
  DECR_LEN(len, resumption_master_secret_size);
150
0
  memcpy(resumption_master_secret, p, resumption_master_secret_size);
151
0
  p += resumption_master_secret_size;
152
153
  /* Read the ticket nonce */
154
0
  DECR_LEN(len, 1);
155
0
  nonce_size = *p;
156
0
  p += 1;
157
158
0
  DECR_LEN(len, nonce_size);
159
0
  memcpy(nonce, p, nonce_size);
160
0
  p += nonce_size;
161
162
0
  DECR_LEN(len, 2);
163
0
  state.size = _gnutls_read_uint16(p);
164
0
  p += 2;
165
166
0
  DECR_LEN(len, state.size);
167
0
  state.data = p;
168
0
  p += state.size;
169
170
0
  DECR_LEN(len, 12);
171
0
  v = _gnutls_read_uint32(p);
172
0
  p += 4;
173
0
  creation_time.tv_sec = (v << 32) | _gnutls_read_uint32(p);
174
0
  p += 4;
175
0
  creation_time.tv_nsec = _gnutls_read_uint32(p);
176
177
0
  ret = _gnutls_session_unpack(session, &state);
178
0
  if (ret < 0)
179
0
    return gnutls_assert_val(ret);
180
181
  /* No errors - Now return all the data to the caller */
182
0
  data->prf = prf;
183
0
  memcpy(data->resumption_master_secret, resumption_master_secret,
184
0
         resumption_master_secret_size);
185
0
  memcpy(data->nonce, nonce, nonce_size);
186
0
  data->nonce_size = nonce_size;
187
0
  data->age_add = age_add;
188
0
  data->lifetime = lifetime;
189
0
  memcpy(&data->creation_time, &creation_time, sizeof(struct timespec));
190
191
0
  return 0;
192
0
}
193
194
static int generate_session_ticket(gnutls_session_t session,
195
           tls13_ticket_st *ticket)
196
0
{
197
0
  int ret;
198
0
  gnutls_datum_t packed = { NULL, 0 };
199
0
  struct timespec now;
200
0
  tls13_ticket_st ticket_data;
201
202
0
  gnutls_gettime(&now);
203
0
  if (session->internals.resumed) {
204
    /* If we are resuming ensure that we don't extend the lifetime
205
     * of the ticket past the original session expiration time */
206
0
    if (now.tv_sec >= session->security_parameters.timestamp +
207
0
            session->internals.expire_time)
208
0
      return GNUTLS_E_INT_RET_0; /* don't send ticket */
209
0
    else
210
0
      ticket->lifetime =
211
0
        session->security_parameters.timestamp +
212
0
        session->internals.expire_time - now.tv_sec;
213
0
  } else {
214
    /* Set ticket lifetime to the default expiration time */
215
0
    ticket->lifetime = session->internals.expire_time;
216
0
  }
217
218
  /* Generate a random 32-bit ticket nonce */
219
0
  ticket->nonce_size = 4;
220
221
0
  if ((ret = gnutls_rnd(GNUTLS_RND_NONCE, ticket->nonce,
222
0
            ticket->nonce_size)) < 0)
223
0
    return gnutls_assert_val(ret);
224
225
0
  if ((ret = gnutls_rnd(GNUTLS_RND_NONCE, &ticket->age_add,
226
0
            sizeof(uint32_t))) < 0)
227
0
    return gnutls_assert_val(ret);
228
  /* This is merely to produce the same binder value on
229
   * different endian architectures. */
230
#ifdef WORDS_BIGENDIAN
231
  ticket->age_add = bswap_32(ticket->age_add);
232
#endif
233
234
0
  ticket->prf = session->security_parameters.prf;
235
236
  /* Encrypt the ticket and place the result in ticket->ticket */
237
0
  ticket_data.lifetime = ticket->lifetime;
238
0
  ticket_data.age_add = ticket->age_add;
239
0
  memcpy(&ticket_data.creation_time, &now, sizeof(struct timespec));
240
0
  memcpy(ticket_data.nonce, ticket->nonce, ticket->nonce_size);
241
0
  ticket_data.nonce_size = ticket->nonce_size;
242
0
  ticket_data.prf = ticket->prf;
243
0
  memcpy(&ticket_data.resumption_master_secret,
244
0
         session->key.proto.tls13.ap_rms, ticket->prf->output_size);
245
246
0
  ret = pack_ticket(session, &ticket_data, &packed);
247
0
  if (ret < 0)
248
0
    return gnutls_assert_val(ret);
249
250
0
  ret = _gnutls_encrypt_session_ticket(session, &packed, &ticket->ticket);
251
0
  _gnutls_free_datum(&packed);
252
0
  if (ret < 0)
253
0
    return gnutls_assert_val(ret);
254
255
0
  return 0;
256
0
}
257
258
static int append_nst_extension(void *ctx, gnutls_buffer_st *buf)
259
0
{
260
0
  gnutls_session_t session = ctx;
261
0
  int ret;
262
263
0
  if (!(session->internals.flags & GNUTLS_ENABLE_EARLY_DATA))
264
0
    return 0;
265
266
0
  ret = _gnutls_buffer_append_uint32(
267
0
    buf, session->security_parameters.max_early_data_size);
268
0
  if (ret < 0)
269
0
    gnutls_assert();
270
271
0
  return ret;
272
0
}
273
274
int _gnutls13_send_session_ticket(gnutls_session_t session, unsigned nr,
275
          unsigned again)
276
0
{
277
0
  int ret = 0;
278
0
  mbuffer_st *bufel = NULL;
279
0
  gnutls_buffer_st buf;
280
0
  tls13_ticket_st ticket;
281
0
  unsigned i;
282
283
  /* Client does not send a NewSessionTicket */
284
0
  if (unlikely(session->security_parameters.entity == GNUTLS_CLIENT))
285
0
    return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
286
287
  /* Session resumption has not been enabled */
288
0
  if (session->internals.flags & GNUTLS_NO_TICKETS)
289
0
    return gnutls_assert_val(0);
290
291
  /* If we received the psk_key_exchange_modes extension which
292
   * does not have overlap with the server configuration, don't
293
   * send a session ticket */
294
0
  if (session->internals.hsk_flags & HSK_PSK_KE_MODE_INVALID)
295
0
    return gnutls_assert_val(0);
296
297
0
  if (again == 0) {
298
0
    for (i = 0; i < nr; i++) {
299
0
      unsigned init_pos;
300
301
0
      memset(&ticket, 0, sizeof(tls13_ticket_st));
302
0
      bufel = NULL;
303
304
0
      ret = _gnutls_buffer_init_handshake_mbuffer(&buf);
305
0
      if (ret < 0)
306
0
        return gnutls_assert_val(ret);
307
308
0
      ret = generate_session_ticket(session, &ticket);
309
0
      if (ret < 0) {
310
0
        if (ret == GNUTLS_E_INT_RET_0) {
311
0
          ret = gnutls_assert_val(0);
312
0
          goto cleanup;
313
0
        }
314
0
        gnutls_assert();
315
0
        goto cleanup;
316
0
      }
317
318
0
      ret = _gnutls_buffer_append_uint32(&buf,
319
0
                 ticket.lifetime);
320
0
      if (ret < 0) {
321
0
        gnutls_assert();
322
0
        goto cleanup;
323
0
      }
324
325
0
      ret = _gnutls_buffer_append_uint32(&buf,
326
0
                 ticket.age_add);
327
0
      if (ret < 0) {
328
0
        gnutls_assert();
329
0
        goto cleanup;
330
0
      }
331
332
      /* append ticket_nonce */
333
0
      ret = _gnutls_buffer_append_data_prefix8(
334
0
        &buf, ticket.nonce, ticket.nonce_size);
335
0
      if (ret < 0) {
336
0
        gnutls_assert();
337
0
        goto cleanup;
338
0
      }
339
340
      /* append ticket */
341
0
      ret = _gnutls_buffer_append_data_prefix16(
342
0
        &buf, ticket.ticket.data, ticket.ticket.size);
343
0
      if (ret < 0) {
344
0
        gnutls_assert();
345
0
        goto cleanup;
346
0
      }
347
348
0
      _gnutls_free_datum(&ticket.ticket);
349
350
      /* append extensions */
351
0
      ret = _gnutls_extv_append_init(&buf);
352
0
      if (ret < 0) {
353
0
        gnutls_assert();
354
0
        goto cleanup;
355
0
      }
356
0
      init_pos = ret;
357
358
0
      ret = _gnutls_extv_append(
359
0
        &buf, ext_mod_early_data.tls_id, session,
360
0
        (extv_append_func)append_nst_extension);
361
0
      if (ret < 0) {
362
0
        gnutls_assert();
363
0
        goto cleanup;
364
0
      }
365
366
0
      ret = _gnutls_extv_append_final(&buf, init_pos, 0);
367
0
      if (ret < 0) {
368
0
        gnutls_assert();
369
0
        goto cleanup;
370
0
      }
371
372
0
      bufel = _gnutls_buffer_to_mbuffer(&buf);
373
374
0
      ret = _gnutls_send_handshake2(
375
0
        session, bufel,
376
0
        GNUTLS_HANDSHAKE_NEW_SESSION_TICKET, 1);
377
0
      if (ret < 0) {
378
0
        gnutls_assert();
379
0
        goto cleanup;
380
0
      }
381
382
0
      session->internals.hsk_flags |= HSK_TLS13_TICKET_SENT;
383
0
    }
384
0
  }
385
386
0
  ret = _gnutls_handshake_io_write_flush(session);
387
388
0
  return ret;
389
390
0
cleanup:
391
0
  _gnutls_free_datum(&ticket.ticket);
392
0
  _mbuffer_xfree(&bufel);
393
0
  _gnutls_buffer_clear(&buf);
394
395
0
  return ret;
396
0
}
397
398
static int parse_nst_extension(void *ctx, unsigned tls_id,
399
             const unsigned char *data, unsigned data_size)
400
0
{
401
0
  gnutls_session_t session = ctx;
402
0
  if (tls_id == ext_mod_early_data.tls_id) {
403
0
    if (data_size < 4)
404
0
      return gnutls_assert_val(
405
0
        GNUTLS_E_TLS_PACKET_DECODING_ERROR);
406
0
    session->security_parameters.max_early_data_size =
407
0
      _gnutls_read_uint32(data);
408
0
  }
409
0
  return 0;
410
0
}
411
412
int _gnutls13_recv_session_ticket(gnutls_session_t session,
413
          gnutls_buffer_st *buf)
414
0
{
415
0
  int ret;
416
0
  tls13_ticket_st *ticket = &session->internals.tls13_ticket;
417
0
  gnutls_datum_t t;
418
419
0
  if (unlikely(buf == NULL))
420
0
    return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
421
422
0
  _gnutls_free_datum(&ticket->ticket);
423
0
  memset(ticket, 0, sizeof(tls13_ticket_st));
424
425
0
  _gnutls_handshake_log("HSK[%p]: parsing session ticket message\n",
426
0
            session);
427
428
  /* ticket_lifetime */
429
0
  ret = _gnutls_buffer_pop_uint32(buf, &ticket->lifetime);
430
0
  if (ret < 0)
431
0
    return gnutls_assert_val(ret);
432
433
  /* ticket_age_add */
434
0
  ret = _gnutls_buffer_pop_uint32(buf, &ticket->age_add);
435
0
  if (ret < 0)
436
0
    return gnutls_assert_val(ret);
437
438
  /* ticket_nonce */
439
0
  ret = _gnutls_buffer_pop_uint8(buf, &ticket->nonce_size);
440
0
  if (ret < 0)
441
0
    return gnutls_assert_val(ret);
442
443
0
  ret = _gnutls_buffer_pop_data(buf, ticket->nonce, ticket->nonce_size);
444
0
  if (ret < 0)
445
0
    return gnutls_assert_val(ret);
446
447
  /* ticket */
448
0
  ret = _gnutls_buffer_pop_datum_prefix16(buf, &t);
449
0
  if (ret < 0)
450
0
    return gnutls_assert_val(ret);
451
452
0
  gnutls_free(ticket->ticket.data);
453
0
  ret = _gnutls_set_datum(&ticket->ticket, t.data, t.size);
454
0
  if (ret < 0)
455
0
    return gnutls_assert_val(ret);
456
457
  /* Extensions */
458
0
  ret = _gnutls_extv_parse(session, parse_nst_extension, buf->data,
459
0
         buf->length);
460
0
  if (ret < 0)
461
0
    return gnutls_assert_val(ret);
462
463
  /* Record the ticket arrival time */
464
0
  gnutls_gettime(&ticket->arrival_time);
465
466
0
  return 0;
467
0
}
468
469
/*
470
 * Parse the ticket in 'data' and return the resumption master secret
471
 * and the KDF ID associated to it.
472
 */
473
int _gnutls13_unpack_session_ticket(gnutls_session_t session,
474
            gnutls_datum_t *data,
475
            tls13_ticket_st *ticket_data)
476
0
{
477
0
  int ret;
478
0
  gnutls_datum_t decrypted = { NULL, 0 };
479
480
0
  if (unlikely(data == NULL || ticket_data == NULL))
481
0
    return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
482
483
0
  if (!session->key.stek_initialized) {
484
0
    return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
485
0
  }
486
487
  /* Check MAC and decrypt ticket */
488
0
  ret = _gnutls_decrypt_session_ticket(session, data, &decrypted);
489
0
  if (ret < 0)
490
0
    return gnutls_assert_val(ret);
491
492
  /* Return ticket parameters */
493
0
  ret = unpack_ticket(session, &decrypted, ticket_data);
494
0
  _gnutls_free_datum(&decrypted);
495
0
  if (ret < 0)
496
0
    return ret;
497
498
0
  ret = _gnutls_check_resumed_params(session);
499
0
  if (ret < 0)
500
0
    return gnutls_assert_val(ret);
501
502
0
  return 0;
503
0
}