Coverage Report

Created: 2026-08-14 07:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gnutls/lib/ext/supported_groups.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2011-2012 Free Software Foundation, Inc.
3
 * Copyright (C) 2017 Red Hat, Inc.
4
 *
5
 * Author: Nikos Mavrogiannopoulos
6
 *
7
 * This file is part of GnuTLS.
8
 *
9
 * The GnuTLS is free software; you can redistribute it and/or
10
 * modify it under the terms of the GNU Lesser General Public License
11
 * as published by the Free Software Foundation; either version 2.1 of
12
 * the License, or (at your option) any later version.
13
 *
14
 * This library is distributed in the hope that it will be useful, but
15
 * WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17
 * Lesser General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Lesser General Public License
20
 * along with this program.  If not, see <https://www.gnu.org/licenses/>
21
 *
22
 */
23
24
/* This file contains the code for the Supported Groups extension (rfc7919).
25
 * This extension was previously named Supported Elliptic Curves under TLS 1.2.
26
 */
27
28
#include "ext/supported_groups.h"
29
#include "str.h"
30
#include "num.h"
31
#include "auth/psk.h"
32
#include "auth/cert.h"
33
#include "auth/anon.h"
34
#include "algorithms.h"
35
#include <gnutls/gnutls.h>
36
37
static int _gnutls_supported_groups_recv_params(gnutls_session_t session,
38
            const uint8_t *data,
39
            size_t data_size);
40
static int _gnutls_supported_groups_send_params(gnutls_session_t session,
41
            gnutls_buffer_st *extdata);
42
43
const hello_ext_entry_st ext_mod_supported_groups = {
44
  .name = "Supported Groups",
45
  .tls_id = 10,
46
  .gid = GNUTLS_EXTENSION_SUPPORTED_GROUPS,
47
  .client_parse_point = GNUTLS_EXT_TLS,
48
  .server_parse_point = GNUTLS_EXT_TLS,
49
  .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_DTLS |
50
        GNUTLS_EXT_FLAG_CLIENT_HELLO | GNUTLS_EXT_FLAG_EE |
51
        GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO,
52
  .recv_func = _gnutls_supported_groups_recv_params,
53
  .send_func = _gnutls_supported_groups_send_params,
54
  .pack_func = NULL,
55
  .unpack_func = NULL,
56
  .deinit_func = NULL,
57
  .cannot_be_overriden = 1
58
};
59
60
#ifdef ENABLE_DHE
61
static unsigned get_min_dh_bits(gnutls_session_t session)
62
0
{
63
0
  gnutls_certificate_credentials_t cert_cred;
64
0
  gnutls_psk_server_credentials_t psk_cred;
65
0
  gnutls_anon_server_credentials_t anon_cred;
66
0
  unsigned level = 0;
67
68
0
  cert_cred = (gnutls_certificate_credentials_t)_gnutls_get_cred(
69
0
    session, GNUTLS_CRD_CERTIFICATE);
70
0
  psk_cred = (gnutls_psk_server_credentials_t)_gnutls_get_cred(
71
0
    session, GNUTLS_CRD_PSK);
72
0
  anon_cred = (gnutls_anon_server_credentials_t)_gnutls_get_cred(
73
0
    session, GNUTLS_CRD_ANON);
74
75
0
  if (cert_cred) {
76
0
    level = cert_cred->dh_sec_param;
77
0
  } else if (psk_cred) {
78
0
    level = psk_cred->dh_sec_param;
79
0
  } else if (anon_cred) {
80
0
    level = anon_cred->dh_sec_param;
81
0
  }
82
83
0
  if (level)
84
0
    return gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, level);
85
86
0
  return 0;
87
0
}
88
#endif
89
90
enum group_class_t {
91
  GROUP_CLASS_UNKNOWN,
92
  GROUP_CLASS_DH,
93
  GROUP_CLASS_EC,
94
  GROUP_CLASS_HYBRID,
95
  GROUP_CLASS_MIN = GROUP_CLASS_DH,
96
  GROUP_CLASS_MAX = GROUP_CLASS_HYBRID
97
};
98
99
static enum group_class_t classify_group(const gnutls_group_entry_st *group)
100
0
{
101
0
  if (group->pk == GNUTLS_PK_DH) {
102
0
    return GROUP_CLASS_DH;
103
0
  } else if (IS_EC(group->pk)) {
104
0
    return GROUP_CLASS_EC;
105
0
  } else if (IS_GROUP_HYBRID(group)) {
106
0
    return GROUP_CLASS_HYBRID;
107
0
  }
108
0
  return GROUP_CLASS_UNKNOWN;
109
0
}
110
111
0
#define NOT_FOUND SIZE_MAX
112
113
static size_t find_group(const group_list_st *groups,
114
       const gnutls_group_entry_st *group)
115
0
{
116
0
  for (size_t i = 0; i < groups->size; i++) {
117
0
    if (groups->entry[i]->id == group->id)
118
0
      return i;
119
0
  }
120
0
  return NOT_FOUND;
121
0
}
122
123
static int server_recv_params(gnutls_session_t session, const uint8_t *data,
124
            size_t data_size)
125
0
{
126
0
  if (data_size < 2)
127
0
    return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION);
128
129
0
  DECR_LEN(data_size, 2);
130
0
  uint16_t len = _gnutls_read_uint16(data);
131
0
  data += 2;
132
133
0
  if (len != data_size)
134
0
    return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
135
136
0
#ifdef ENABLE_DHE
137
  /* Figure the minimum DH allowed for this session, if any */
138
0
  unsigned min_dh_bits = get_min_dh_bits(session);
139
0
#endif
140
141
0
  size_t cpos_by_class[GROUP_CLASS_MAX + 1];
142
0
  size_t spos_by_class[GROUP_CLASS_MAX + 1];
143
144
0
  for (enum group_class_t c = GROUP_CLASS_MIN; c <= GROUP_CLASS_MAX;
145
0
       c++) {
146
0
    cpos_by_class[c] = NOT_FOUND;
147
0
    spos_by_class[c] = NOT_FOUND;
148
0
  }
149
150
0
  size_t cpos, spos;
151
152
  /* This extension is being processed prior to a ciphersuite
153
   * being selected, so we cannot rely on ciphersuite
154
   * information. */
155
0
  for (cpos = 0; data_size > 0; cpos++) {
156
0
    DECR_LEN(data_size, 2);
157
0
    uint16_t tls_id = _gnutls_read_uint16(data);
158
0
    data += 2;
159
160
    /* Check if tls_id is in the FFDH range, even if it is
161
     * unknown to the server. This must be done before
162
     * group resolution to fail ciphersuite selection. */
163
0
    if (256 <= tls_id && tls_id <= 511)
164
0
      session->internals.hsk_flags |= HSK_HAVE_FFDHE;
165
166
0
    const gnutls_group_entry_st *group =
167
0
      _gnutls_tls_id_to_group(tls_id);
168
169
0
    if (group == NULL) {
170
0
      _gnutls_handshake_log(
171
0
        "EXT[%p]: Received unknown group (0x%x)\n",
172
0
        session, tls_id);
173
0
      continue;
174
0
    } else {
175
0
      _gnutls_handshake_log(
176
0
        "EXT[%p]: Received group %s (0x%x)\n", session,
177
0
        group->name, tls_id);
178
0
    }
179
180
0
#ifdef ENABLE_DHE
181
0
    if (group->pk == GNUTLS_PK_DH) {
182
0
      if (min_dh_bits > 0 &&
183
0
          group->prime->size * 8 < min_dh_bits)
184
0
        continue;
185
0
    }
186
0
#endif
187
188
0
    enum group_class_t c = classify_group(group);
189
0
    if (c == GROUP_CLASS_UNKNOWN) {
190
0
      _gnutls_debug_log(
191
0
        "EXT[%p]: Cannot classify group: %s\n", session,
192
0
        group->name);
193
0
      continue;
194
0
    }
195
196
0
    spos = find_group(&session->internals.priorities->groups,
197
0
          group);
198
0
    if (spos == NOT_FOUND) {
199
0
      _gnutls_debug_log(
200
0
        "EXT[%p]: Skipping group %s not found in server priorities\n",
201
0
        session, group->name);
202
0
      continue;
203
0
    }
204
205
    /* %SERVER_PRECEDENCE is set and the previous entry
206
     * has a lower priority, or it is the first time this
207
     * group class appears in the client advertisement. */
208
0
    if (session->internals.priorities->server_precedence ?
209
0
          spos < spos_by_class[c] :
210
0
          cpos_by_class[c] == NOT_FOUND) {
211
0
      cpos_by_class[c] = cpos;
212
0
      spos_by_class[c] = spos;
213
0
    }
214
0
  }
215
216
  /* If there is any FFDH or EC group, record it for later
217
   * fallback after ciphersuite selection. */
218
0
  if (spos_by_class[GROUP_CLASS_DH] != NOT_FOUND) {
219
0
    session->internals.cand_dh_group =
220
0
      session->internals.priorities->groups
221
0
        .entry[spos_by_class[GROUP_CLASS_DH]];
222
0
  }
223
224
0
  if (spos_by_class[GROUP_CLASS_EC] != NOT_FOUND) {
225
0
    session->internals.cand_ec_group =
226
0
      session->internals.priorities->groups
227
0
        .entry[spos_by_class[GROUP_CLASS_EC]];
228
0
  }
229
230
  /* Now pick the group with the highest priority. */
231
0
  cpos = spos = NOT_FOUND;
232
0
  if (session->internals.priorities->server_precedence) {
233
0
    for (enum group_class_t c = GROUP_CLASS_MIN;
234
0
         c <= GROUP_CLASS_MAX; c++) {
235
0
      if (spos_by_class[c] < spos)
236
0
        spos = spos_by_class[c];
237
0
    }
238
0
  } else {
239
0
    for (enum group_class_t c = GROUP_CLASS_MIN;
240
0
         c <= GROUP_CLASS_MAX; c++) {
241
0
      if (cpos_by_class[c] < cpos) {
242
0
        cpos = cpos_by_class[c];
243
0
        spos = spos_by_class[c];
244
0
      }
245
0
    }
246
0
  }
247
0
  if (spos != NOT_FOUND) {
248
0
    session->internals.cand_group =
249
0
      session->internals.priorities->groups.entry[spos];
250
0
    _gnutls_handshake_log("EXT[%p]: Selected group %s\n", session,
251
0
              session->internals.cand_group->name);
252
0
  } else {
253
0
    _gnutls_handshake_log("EXT[%p]: No group selected\n", session);
254
0
  }
255
256
0
  return 0;
257
0
}
258
259
static int _gnutls_supported_groups_recv_params(gnutls_session_t session,
260
            const uint8_t *data,
261
            size_t data_size)
262
0
{
263
0
  if (session->security_parameters.entity == GNUTLS_CLIENT) {
264
    /* A client shouldn't receive this extension in TLS
265
     * 1.2. It is possible to read that message under
266
     * TLS 1.3 as an encrypted extension. */
267
0
    return 0;
268
0
  } else {
269
0
    return server_recv_params(session, data, data_size);
270
0
  }
271
0
}
272
273
static int client_send_params(gnutls_session_t session,
274
            gnutls_buffer_st *extdata)
275
0
{
276
0
  const group_list_st *groups = &session->internals.priorities->groups;
277
278
0
  if (groups->size == 0)
279
0
    return 0;
280
281
0
  size_t spos = extdata->length;
282
283
0
  int ret = _gnutls_buffer_append_uint16(extdata, groups->size * 2);
284
0
  if (ret < 0)
285
0
    return gnutls_assert_val(ret);
286
287
0
  for (size_t i = 0; i < groups->size; i++) {
288
0
    const gnutls_group_entry_st *group = groups->entry[i];
289
290
0
    _gnutls_handshake_log("EXT[%p]: Sent group %s (0x%x)\n",
291
0
              session, group->name, group->tls_id);
292
293
0
    ret = _gnutls_buffer_append_uint16(extdata, group->tls_id);
294
0
    if (ret < 0)
295
0
      return gnutls_assert_val(ret);
296
0
  }
297
298
0
  return extdata->length - spos;
299
0
}
300
301
/* returns data_size or a negative number on failure
302
 */
303
static int _gnutls_supported_groups_send_params(gnutls_session_t session,
304
            gnutls_buffer_st *extdata)
305
0
{
306
0
  if (session->security_parameters.entity == GNUTLS_CLIENT) {
307
    /* this extension is only being sent on client side */
308
0
    return client_send_params(session, extdata);
309
0
  } else {
310
0
    return gnutls_assert_val(0);
311
0
  }
312
0
}
313
314
/* Returns true if the given group is allowed in the current session;
315
 * false otherwise.
316
 */
317
bool _gnutls_session_supports_group(gnutls_session_t session,
318
            const gnutls_group_entry_st *group)
319
0
{
320
0
  return find_group(&session->internals.priorities->groups, group) !=
321
         NOT_FOUND;
322
0
}