Coverage Report

Created: 2025-11-16 07:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gnutls/lib/tls13/key_update.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2017 Red Hat, Inc.
3
 *
4
 * Author: Nikos Mavrogiannopoulos
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 "handshake.h"
26
#include "tls13/key_update.h"
27
#include "mem.h"
28
#include "mbuffers.h"
29
#include "secrets.h"
30
#include "system/ktls.h"
31
32
0
#define KEY_UPDATES_WINDOW 1000
33
#define KEY_UPDATES_PER_WINDOW 8
34
35
/*
36
 * Sets kTLS keys if enabled.
37
 * If this operation fails with GNUTLS_E_INTERNAL_ERROR, KTLS is disabled
38
 * because KTLS most likely doesn't support key update.
39
 */
40
static inline int set_ktls_keys(gnutls_session_t session,
41
        gnutls_transport_ktls_enable_flags_t iface)
42
0
{
43
0
  if (_gnutls_ktls_set_keys(session, iface) < 0) {
44
0
    session->internals.ktls_enabled = 0;
45
0
    session->internals.invalid_connection = true;
46
0
    session->internals.resumable = false;
47
0
    _gnutls_audit_log(
48
0
      session,
49
0
      "invalidating session: KTLS - couldn't update keys\n");
50
0
    return GNUTLS_E_INTERNAL_ERROR;
51
0
  }
52
0
  return 0;
53
0
}
54
55
static int update_sending_key(gnutls_session_t session, hs_stage_t stage)
56
0
{
57
0
  int ret;
58
59
0
  _gnutls_epoch_bump(session);
60
0
  ret = _gnutls_epoch_dup(session, EPOCH_WRITE_CURRENT);
61
0
  if (ret < 0)
62
0
    return gnutls_assert_val(ret);
63
64
0
  ret = _tls13_write_connection_state_init(session, stage);
65
0
  if (ret < 0)
66
0
    return gnutls_assert_val(ret);
67
68
0
  if (IS_KTLS_ENABLED(session, GNUTLS_KTLS_SEND)) {
69
0
    ret = set_ktls_keys(session, GNUTLS_KTLS_SEND);
70
0
    if (ret < 0)
71
0
      return gnutls_assert_val(ret);
72
0
  }
73
74
0
  return 0;
75
0
}
76
77
static int update_receiving_key(gnutls_session_t session, hs_stage_t stage)
78
0
{
79
0
  int ret;
80
81
0
  _gnutls_epoch_bump(session);
82
0
  ret = _gnutls_epoch_dup(session, EPOCH_READ_CURRENT);
83
0
  if (ret < 0)
84
0
    return gnutls_assert_val(ret);
85
86
0
  ret = _tls13_read_connection_state_init(session, stage);
87
0
  if (ret < 0)
88
0
    return gnutls_assert_val(ret);
89
90
0
  if (IS_KTLS_ENABLED(session, GNUTLS_KTLS_RECV)) {
91
0
    ret = set_ktls_keys(session, GNUTLS_KTLS_RECV);
92
0
    if (ret < 0)
93
0
      return gnutls_assert_val(ret);
94
0
  }
95
96
0
  return 0;
97
0
}
98
99
int _gnutls13_recv_key_update(gnutls_session_t session, gnutls_buffer_st *buf)
100
0
{
101
0
  int ret;
102
0
  struct timespec now;
103
104
0
  if (buf->length != 1)
105
0
    return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
106
107
0
  gnutls_gettime(&now);
108
109
  /* Roll over the counter if the time window has elapsed */
110
0
  if (session->internals.key_update_count == 0 ||
111
0
      timespec_sub_ms(&now, &session->internals.last_key_update) >
112
0
        KEY_UPDATES_WINDOW) {
113
0
    session->internals.last_key_update = now;
114
0
    session->internals.key_update_count = 0;
115
0
  }
116
117
0
  if (unlikely(++session->internals.key_update_count >
118
0
         KEY_UPDATES_PER_WINDOW)) {
119
0
    _gnutls_debug_log(
120
0
      "reached maximum number of key updates per %d milliseconds (%d)\n",
121
0
      KEY_UPDATES_WINDOW, KEY_UPDATES_PER_WINDOW);
122
0
    return gnutls_assert_val(GNUTLS_E_TOO_MANY_HANDSHAKE_PACKETS);
123
0
  }
124
125
0
  _gnutls_epoch_gc(session);
126
127
0
  _gnutls_handshake_log("HSK[%p]: received TLS 1.3 key update (%u)\n",
128
0
            session, (unsigned)buf->data[0]);
129
130
0
  switch (buf->data[0]) {
131
0
  case 0:
132
    /* peer updated its key, not requested our key update */
133
0
    ret = _tls13_update_secret(
134
0
      session, session->key.proto.tls13.temp_secret,
135
0
      session->key.proto.tls13.temp_secret_size);
136
0
    if (ret < 0)
137
0
      return gnutls_assert_val(ret);
138
139
0
    ret = update_receiving_key(session, STAGE_UPD_PEERS);
140
0
    if (ret < 0)
141
0
      return gnutls_assert_val(ret);
142
143
0
    break;
144
0
  case 1:
145
0
    if (session->internals.hsk_flags & HSK_KEY_UPDATE_ASKED) {
146
      /* if we had asked a key update we shouldn't get this
147
       * reply */
148
0
      return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
149
0
    }
150
151
    /* peer updated its key, requested our key update */
152
0
    ret = _tls13_update_secret(
153
0
      session, session->key.proto.tls13.temp_secret,
154
0
      session->key.proto.tls13.temp_secret_size);
155
0
    if (ret < 0)
156
0
      return gnutls_assert_val(ret);
157
158
0
    ret = update_receiving_key(session, STAGE_UPD_PEERS);
159
0
    if (ret < 0)
160
0
      return gnutls_assert_val(ret);
161
162
    /* we mark that a key update is schedule, and it
163
     * will be performed prior to sending the next application
164
     * message.
165
     */
166
0
    if (session->internals.rsend_state == RECORD_SEND_NORMAL)
167
0
      session->internals.rsend_state =
168
0
        RECORD_SEND_KEY_UPDATE_1;
169
0
    else if (session->internals.rsend_state == RECORD_SEND_CORKED)
170
0
      session->internals.rsend_state =
171
0
        RECORD_SEND_CORKED_TO_KU;
172
173
0
    break;
174
0
  default:
175
0
    return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
176
0
  }
177
178
0
  session->internals.hsk_flags &= ~(unsigned)(HSK_KEY_UPDATE_ASKED);
179
180
0
  return 0;
181
0
}
182
183
int _gnutls13_send_key_update(gnutls_session_t session, unsigned again,
184
            unsigned flags /* GNUTLS_KU_* */)
185
0
{
186
0
  int ret;
187
0
  mbuffer_st *bufel = NULL;
188
0
  uint8_t val;
189
190
0
  if (again == 0) {
191
0
    if (flags & GNUTLS_KU_PEER) {
192
      /* mark that we asked a key update to prevent an
193
       * infinite ping pong when receiving the reply */
194
0
      session->internals.hsk_flags |= HSK_KEY_UPDATE_ASKED;
195
0
      val = 0x01;
196
0
    } else {
197
0
      val = 0x00;
198
0
    }
199
200
0
    _gnutls_handshake_log("HSK[%p]: sending key update (%u)\n",
201
0
              session, (unsigned)val);
202
203
0
    bufel = _gnutls_handshake_alloc(session, 1);
204
0
    if (bufel == NULL)
205
0
      return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
206
207
0
    _mbuffer_set_udata_size(bufel, 0);
208
0
    ret = _mbuffer_append_data(bufel, &val, 1);
209
0
    if (ret < 0) {
210
0
      gnutls_assert();
211
0
      goto cleanup;
212
0
    }
213
0
  }
214
215
0
  return _gnutls_send_handshake(session, bufel,
216
0
              GNUTLS_HANDSHAKE_KEY_UPDATE);
217
218
0
cleanup:
219
0
  _mbuffer_xfree(&bufel);
220
0
  return ret;
221
0
}
222
223
/**
224
 * gnutls_session_key_update:
225
 * @session: is a #gnutls_session_t type.
226
 * @flags: zero of %GNUTLS_KU_PEER
227
 *
228
 * This function will update/refresh the session keys when the
229
 * TLS protocol is 1.3 or better. The peer is notified of the
230
 * update by sending a message, so this function should be
231
 * treated similarly to gnutls_record_send() --i.e., it may
232
 * return %GNUTLS_E_AGAIN or %GNUTLS_E_INTERRUPTED.
233
 *
234
 * When this flag %GNUTLS_KU_PEER is specified, this function
235
 * in addition to updating the local keys, will ask the peer to
236
 * refresh its keys too.
237
 *
238
 * If the negotiated version is not TLS 1.3 or better this
239
 * function will return %GNUTLS_E_INVALID_REQUEST.
240
 *
241
 * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
242
 *
243
 * Since: 3.6.3
244
 **/
245
int gnutls_session_key_update(gnutls_session_t session, unsigned flags)
246
0
{
247
0
  int ret;
248
0
  const version_entry_st *vers = get_version(session);
249
250
0
  if (!vers->tls13_sem)
251
0
    return GNUTLS_E_INVALID_REQUEST;
252
253
0
  ret = _gnutls13_send_key_update(session, AGAIN(STATE150), flags);
254
0
  STATE = STATE150;
255
256
0
  if (ret < 0) {
257
0
    gnutls_assert();
258
0
    return ret;
259
0
  }
260
0
  STATE = STATE0;
261
262
0
  _gnutls_epoch_gc(session);
263
264
  /* it was completely sent, update the keys */
265
0
  ret = _tls13_update_secret(session,
266
0
           session->key.proto.tls13.temp_secret,
267
0
           session->key.proto.tls13.temp_secret_size);
268
0
  if (ret < 0)
269
0
    return gnutls_assert_val(ret);
270
271
0
  ret = update_sending_key(session, STAGE_UPD_OURS);
272
0
  if (ret < 0)
273
0
    return gnutls_assert_val(ret);
274
275
0
  return 0;
276
0
}
277
278
/**
279
 * gnutls_handshake_update_receiving_key:
280
 * @session: is a #gnutls_session_t type.
281
 *
282
 * This function will update/refresh the session receiving keys when
283
 * the TLS protocol is 1.3 or better. Unlike gnutls_session_key_update()
284
 * this function does not notify the peer, it will only update
285
 * the local keys.
286
 *
287
 * If the negotiated version is not TLS 1.3 or better this
288
 * function will return %GNUTLS_E_INVALID_REQUEST.
289
 *
290
 * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
291
 *
292
 * Since: 3.8.11
293
 **/
294
int gnutls_handshake_update_receiving_key(gnutls_session_t session)
295
0
{
296
0
  int ret;
297
0
  const version_entry_st *vers = get_version(session);
298
299
0
  if (!vers->tls13_sem)
300
0
    return GNUTLS_E_INVALID_REQUEST;
301
302
0
  _gnutls_epoch_gc(session);
303
304
0
  ret = _tls13_update_secret(session,
305
0
           session->key.proto.tls13.temp_secret,
306
0
           session->key.proto.tls13.temp_secret_size);
307
0
  if (ret < 0)
308
0
    return gnutls_assert_val(ret);
309
310
0
  ret = update_receiving_key(session, STAGE_UPD_PEERS);
311
0
  if (ret < 0)
312
0
    return gnutls_assert_val(ret);
313
314
0
  return 0;
315
0
}