Coverage Report

Created: 2026-06-30 06:42

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
static unsigned get_min_dh(gnutls_session_t session)
61
2.22k
{
62
2.22k
  gnutls_certificate_credentials_t cert_cred;
63
2.22k
  gnutls_psk_server_credentials_t psk_cred;
64
2.22k
  gnutls_anon_server_credentials_t anon_cred;
65
2.22k
  unsigned level = 0;
66
67
2.22k
  cert_cred = (gnutls_certificate_credentials_t)_gnutls_get_cred(
68
2.22k
    session, GNUTLS_CRD_CERTIFICATE);
69
2.22k
  psk_cred = (gnutls_psk_server_credentials_t)_gnutls_get_cred(
70
2.22k
    session, GNUTLS_CRD_PSK);
71
2.22k
  anon_cred = (gnutls_anon_server_credentials_t)_gnutls_get_cred(
72
2.22k
    session, GNUTLS_CRD_ANON);
73
74
2.22k
  if (cert_cred) {
75
2.22k
    level = cert_cred->dh_sec_param;
76
2.22k
  } else if (psk_cred) {
77
0
    level = psk_cred->dh_sec_param;
78
0
  } else if (anon_cred) {
79
0
    level = anon_cred->dh_sec_param;
80
0
  }
81
82
2.22k
  if (level)
83
2.22k
    return gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, level);
84
85
0
  return 0;
86
2.22k
}
87
88
/*
89
 * In case of a server: if a SUPPORTED_GROUPS extension type is received then it stores
90
 * into the session security parameters the new value. The server may use gnutls_session_certificate_type_get(),
91
 * to access it.
92
 *
93
 * In case of a client: If supported_eccs have been specified then we send the extension.
94
 *
95
 */
96
static int _gnutls_supported_groups_recv_params(gnutls_session_t session,
97
            const uint8_t *data,
98
            size_t data_size)
99
2.55k
{
100
2.55k
  int i;
101
2.55k
  uint16_t len;
102
2.55k
  const uint8_t *p = data;
103
2.55k
  const gnutls_group_entry_st *group = NULL;
104
2.55k
  unsigned have_ffdhe = 0;
105
2.55k
  unsigned tls_id;
106
2.55k
  unsigned min_dh;
107
2.55k
  unsigned j;
108
2.55k
  int serv_ec_idx, serv_dh_idx,
109
2.55k
    serv_hybrid_idx; /* index in server's priority listing */
110
2.55k
  int cli_ec_pos, cli_dh_pos,
111
2.55k
    cli_hybrid_pos; /* position in listing sent by client */
112
113
2.55k
  if (session->security_parameters.entity == GNUTLS_CLIENT) {
114
    /* A client shouldn't receive this extension in TLS1.2. It is
115
     * possible to read that message under TLS1.3 as an encrypted
116
     * extension. */
117
297
    return 0;
118
2.25k
  } else { /* SERVER SIDE - we must check if the sent supported ecc type is the right one
119
         */
120
2.25k
    if (data_size < 2)
121
8
      return gnutls_assert_val(
122
2.25k
        GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION);
123
124
2.24k
    DECR_LEN(data_size, 2);
125
2.24k
    len = _gnutls_read_uint16(p);
126
2.24k
    p += 2;
127
128
2.24k
    if (len % 2 != 0)
129
4
      return gnutls_assert_val(
130
2.24k
        GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
131
132
2.24k
    DECR_LEN(data_size, len);
133
134
    /* we figure what is the minimum DH allowed for this session, if any */
135
2.22k
    min_dh = get_min_dh(session);
136
137
2.22k
    serv_ec_idx = serv_dh_idx = serv_hybrid_idx = -1;
138
2.22k
    cli_ec_pos = cli_dh_pos = cli_hybrid_pos = -1;
139
140
    /* This extension is being processed prior to a ciphersuite being selected,
141
     * so we cannot rely on ciphersuite information. */
142
32.1k
    for (i = 0; i < len; i += 2) {
143
29.8k
      if (have_ffdhe == 0 && p[i] == 0x01)
144
1.42k
        have_ffdhe = 1;
145
146
29.8k
      tls_id = _gnutls_read_uint16(&p[i]);
147
29.8k
      group = _gnutls_tls_id_to_group(tls_id);
148
149
29.8k
      _gnutls_handshake_log(
150
29.8k
        "EXT[%p]: Received group %s (0x%x)\n", session,
151
29.8k
        group ? group->name : "unknown", tls_id);
152
29.8k
      if (group == NULL)
153
19.7k
        continue;
154
155
10.0k
      if (min_dh > 0 && group->prime &&
156
5.13k
          group->prime->size * 8 < min_dh)
157
0
        continue;
158
159
      /* we simulate _gnutls_session_supports_group, but we prioritize if
160
       * %SERVER_PRECEDENCE is given */
161
10.0k
      for (j = 0;
162
59.0k
           j < session->internals.priorities->groups.size;
163
58.1k
           j++) {
164
58.1k
        if (session->internals.priorities->groups
165
58.1k
              .entry[j]
166
58.1k
              ->id == group->id) {
167
9.12k
          if (session->internals.priorities
168
9.12k
                ->server_precedence) {
169
0
            if (group->pk == GNUTLS_PK_DH) {
170
0
              if (serv_dh_idx != -1 &&
171
0
                  (int)j >
172
0
                    serv_dh_idx)
173
0
                break;
174
0
              serv_dh_idx = j;
175
0
              cli_dh_pos = i;
176
0
            } else if (IS_EC(group->pk)) {
177
0
              if (serv_ec_idx != -1 &&
178
0
                  (int)j >
179
0
                    serv_ec_idx)
180
0
                break;
181
0
              serv_ec_idx = j;
182
0
              cli_ec_pos = i;
183
0
            } else if (IS_GROUP_HYBRID(
184
0
                   group)) {
185
0
              if (serv_hybrid_idx !=
186
0
                    -1 &&
187
0
                  (int)j >
188
0
                    serv_hybrid_idx)
189
0
                break;
190
0
              serv_hybrid_idx = j;
191
0
              cli_hybrid_pos = i;
192
0
            }
193
9.12k
          } else {
194
9.12k
            if (group->pk == GNUTLS_PK_DH) {
195
5.13k
              if (cli_dh_pos != -1)
196
4.08k
                break;
197
1.05k
              cli_dh_pos = i;
198
1.05k
              serv_dh_idx = j;
199
3.98k
            } else if (IS_EC(group->pk)) {
200
3.98k
              if (cli_ec_pos != -1)
201
2.01k
                break;
202
1.97k
              cli_ec_pos = i;
203
1.97k
              serv_ec_idx = j;
204
1.97k
            } else if (IS_GROUP_HYBRID(
205
0
                   group)) {
206
0
              if (cli_hybrid_pos !=
207
0
                  -1)
208
0
                break;
209
0
              cli_hybrid_pos = i;
210
0
              serv_hybrid_idx = j;
211
0
            }
212
9.12k
          }
213
3.02k
          break;
214
9.12k
        }
215
58.1k
      }
216
10.0k
    }
217
218
    /* serv_{dh,ec,hybrid}_idx contain the index of the groups we want to use.
219
     */
220
2.22k
    if (serv_dh_idx != -1) {
221
1.05k
      session->internals.cand_dh_group =
222
1.05k
        session->internals.priorities->groups
223
1.05k
          .entry[serv_dh_idx];
224
1.05k
      session->internals.cand_group =
225
1.05k
        session->internals.cand_dh_group;
226
1.05k
    }
227
228
2.22k
    if (serv_ec_idx != -1) {
229
1.97k
      session->internals.cand_ec_group =
230
1.97k
        session->internals.priorities->groups
231
1.97k
          .entry[serv_ec_idx];
232
1.97k
      if (session->internals.cand_group == NULL ||
233
920
          (session->internals.priorities->server_precedence &&
234
0
           serv_ec_idx < serv_dh_idx) ||
235
920
          (!session->internals.priorities->server_precedence &&
236
1.92k
           cli_ec_pos < cli_dh_pos)) {
237
1.92k
        session->internals.cand_group =
238
1.92k
          session->internals.cand_ec_group;
239
1.92k
      }
240
1.97k
    }
241
242
    /* PQC hybrid key exchange groups can only be used in
243
     * TLS 1.3, where no distinction between ECDH and DH
244
     * in the group definitions, and thus only cand_group
245
     * is set here.
246
     */
247
2.22k
    if (serv_hybrid_idx != -1) {
248
0
      if (session->internals.cand_group == NULL ||
249
0
          (session->internals.priorities->server_precedence &&
250
0
           (serv_dh_idx == -1 ||
251
0
            serv_hybrid_idx < serv_dh_idx) &&
252
0
           (serv_ec_idx == -1 ||
253
0
            serv_hybrid_idx < serv_ec_idx)) ||
254
0
          (!session->internals.priorities->server_precedence &&
255
0
           (cli_dh_pos == -1 ||
256
0
            cli_hybrid_pos < cli_dh_pos) &&
257
0
           (cli_ec_pos == -1 ||
258
0
            cli_hybrid_pos < cli_ec_pos))) {
259
0
        session->internals.cand_group =
260
0
          session->internals.priorities->groups
261
0
            .entry[serv_hybrid_idx];
262
0
      }
263
0
    }
264
265
2.22k
    if (session->internals.cand_group)
266
2.22k
      _gnutls_handshake_log(
267
2.22k
        "EXT[%p]: Selected group %s\n", session,
268
2.22k
        session->internals.cand_group->name);
269
270
2.22k
    if (have_ffdhe)
271
1.42k
      session->internals.hsk_flags |= HSK_HAVE_FFDHE;
272
2.22k
  }
273
274
2.22k
  return 0;
275
2.55k
}
276
277
/* returns data_size or a negative number on failure
278
 */
279
static int _gnutls_supported_groups_send_params(gnutls_session_t session,
280
            gnutls_buffer_st *extdata)
281
32.3k
{
282
32.3k
  unsigned len, i;
283
32.3k
  int ret;
284
32.3k
  uint16_t p;
285
286
  /* this extension is only being sent on client side */
287
32.3k
  if (session->security_parameters.entity == GNUTLS_CLIENT) {
288
31.3k
    len = session->internals.priorities->groups.size;
289
31.3k
    if (len > 0) {
290
31.3k
      ret = _gnutls_buffer_append_prefix(extdata, 16,
291
31.3k
                 len * 2);
292
31.3k
      if (ret < 0)
293
0
        return gnutls_assert_val(ret);
294
295
344k
      for (i = 0; i < len; i++) {
296
313k
        p = session->internals.priorities->groups
297
313k
              .entry[i]
298
313k
              ->tls_id;
299
300
313k
        _gnutls_handshake_log(
301
313k
          "EXT[%p]: Sent group %s (0x%x)\n",
302
313k
          session,
303
313k
          session->internals.priorities->groups
304
313k
            .entry[i]
305
313k
            ->name,
306
313k
          (unsigned)p);
307
308
313k
        ret = _gnutls_buffer_append_prefix(extdata, 16,
309
313k
                   p);
310
313k
        if (ret < 0)
311
0
          return gnutls_assert_val(ret);
312
313k
      }
313
31.3k
      return (len + 1) * 2;
314
31.3k
    }
315
31.3k
  }
316
317
1.00k
  return 0;
318
32.3k
}
319
320
/* Returns 0 if the given ECC curve is allowed in the current
321
 * session. A negative error value is returned otherwise.
322
 */
323
bool _gnutls_session_supports_group(gnutls_session_t session,
324
            unsigned int group)
325
12.9k
{
326
12.9k
  unsigned i;
327
328
34.5k
  for (i = 0; i < session->internals.priorities->groups.size; i++) {
329
34.5k
    if (session->internals.priorities->groups.entry[i]->id == group)
330
12.9k
      return true;
331
34.5k
  }
332
333
25
  return false;
334
12.9k
}