Coverage Report

Created: 2025-03-06 07:58

/src/gnutls/lib/ext/supported_groups.c
Line
Count
Source (jump to first uncovered line)
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
0
{
62
0
  gnutls_certificate_credentials_t cert_cred;
63
0
  gnutls_psk_server_credentials_t psk_cred;
64
0
  gnutls_anon_server_credentials_t anon_cred;
65
0
  unsigned level = 0;
66
67
0
  cert_cred = (gnutls_certificate_credentials_t)_gnutls_get_cred(
68
0
    session, GNUTLS_CRD_CERTIFICATE);
69
0
  psk_cred = (gnutls_psk_server_credentials_t)_gnutls_get_cred(
70
0
    session, GNUTLS_CRD_PSK);
71
0
  anon_cred = (gnutls_anon_server_credentials_t)_gnutls_get_cred(
72
0
    session, GNUTLS_CRD_ANON);
73
74
0
  if (cert_cred) {
75
0
    level = cert_cred->dh_sec_param;
76
0
  } 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
0
  if (level)
83
0
    return gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, level);
84
85
0
  return 0;
86
0
}
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
0
{
100
0
  int i;
101
0
  uint16_t len;
102
0
  const uint8_t *p = data;
103
0
  const gnutls_group_entry_st *group = NULL;
104
0
  unsigned have_ffdhe = 0;
105
0
  unsigned tls_id;
106
0
  unsigned min_dh;
107
0
  unsigned j;
108
0
  int serv_ec_idx, serv_dh_idx,
109
0
    serv_hybrid_idx; /* index in server's priority listing */
110
0
  int cli_ec_pos, cli_dh_pos,
111
0
    cli_hybrid_pos; /* position in listing sent by client */
112
113
0
  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
0
    return 0;
118
0
  } else { /* SERVER SIDE - we must check if the sent supported ecc type is the right one
119
         */
120
0
    if (data_size < 2)
121
0
      return gnutls_assert_val(
122
0
        GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION);
123
124
0
    DECR_LEN(data_size, 2);
125
0
    len = _gnutls_read_uint16(p);
126
0
    p += 2;
127
128
0
    if (len % 2 != 0)
129
0
      return gnutls_assert_val(
130
0
        GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
131
132
0
    DECR_LEN(data_size, len);
133
134
    /* we figure what is the minimum DH allowed for this session, if any */
135
0
    min_dh = get_min_dh(session);
136
137
0
    serv_ec_idx = serv_dh_idx = serv_hybrid_idx = -1;
138
0
    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
0
    for (i = 0; i < len; i += 2) {
143
0
      if (have_ffdhe == 0 && p[i] == 0x01)
144
0
        have_ffdhe = 1;
145
146
0
      tls_id = _gnutls_read_uint16(&p[i]);
147
0
      group = _gnutls_tls_id_to_group(tls_id);
148
149
0
      _gnutls_handshake_log(
150
0
        "EXT[%p]: Received group %s (0x%x)\n", session,
151
0
        group ? group->name : "unknown", tls_id);
152
0
      if (group == NULL)
153
0
        continue;
154
155
0
      if (min_dh > 0 && group->prime &&
156
0
          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
0
      for (j = 0;
162
0
           j < session->internals.priorities->groups.size;
163
0
           j++) {
164
0
        if (session->internals.priorities->groups
165
0
              .entry[j]
166
0
              ->id == group->id) {
167
0
          if (session->internals.priorities
168
0
                ->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
0
          } else {
194
0
            if (group->pk == GNUTLS_PK_DH) {
195
0
              if (cli_dh_pos != -1)
196
0
                break;
197
0
              cli_dh_pos = i;
198
0
              serv_dh_idx = j;
199
0
            } else if (IS_EC(group->pk)) {
200
0
              if (cli_ec_pos != -1)
201
0
                break;
202
0
              cli_ec_pos = i;
203
0
              serv_ec_idx = j;
204
0
            } 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
0
          }
213
0
          break;
214
0
        }
215
0
      }
216
0
    }
217
218
    /* serv_{dh,ec,hybrid}_idx contain the index of the groups we want to use.
219
     */
220
0
    if (serv_dh_idx != -1) {
221
0
      session->internals.cand_dh_group =
222
0
        session->internals.priorities->groups
223
0
          .entry[serv_dh_idx];
224
0
      session->internals.cand_group =
225
0
        session->internals.cand_dh_group;
226
0
    }
227
228
0
    if (serv_ec_idx != -1) {
229
0
      session->internals.cand_ec_group =
230
0
        session->internals.priorities->groups
231
0
          .entry[serv_ec_idx];
232
0
      if (session->internals.cand_group == NULL ||
233
0
          (session->internals.priorities->server_precedence &&
234
0
           serv_ec_idx < serv_dh_idx) ||
235
0
          (!session->internals.priorities->server_precedence &&
236
0
           cli_ec_pos < cli_dh_pos)) {
237
0
        session->internals.cand_group =
238
0
          session->internals.cand_ec_group;
239
0
      }
240
0
    }
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
0
    if (serv_hybrid_idx != -1) {
248
0
      if (session->internals.cand_group == NULL ||
249
0
          (session->internals.priorities->server_precedence &&
250
0
           serv_hybrid_idx < MIN(serv_ec_idx, serv_dh_idx)) ||
251
0
          (!session->internals.priorities->server_precedence &&
252
0
           cli_hybrid_pos < MIN(cli_ec_pos, cli_dh_pos))) {
253
0
        session->internals.cand_group =
254
0
          session->internals.priorities->groups
255
0
            .entry[serv_hybrid_idx];
256
0
      }
257
0
    }
258
259
0
    if (session->internals.cand_group)
260
0
      _gnutls_handshake_log(
261
0
        "EXT[%p]: Selected group %s\n", session,
262
0
        session->internals.cand_group->name);
263
264
0
    if (have_ffdhe)
265
0
      session->internals.hsk_flags |= HSK_HAVE_FFDHE;
266
0
  }
267
268
0
  return 0;
269
0
}
270
271
/* returns data_size or a negative number on failure
272
 */
273
static int _gnutls_supported_groups_send_params(gnutls_session_t session,
274
            gnutls_buffer_st *extdata)
275
0
{
276
0
  unsigned len, i;
277
0
  int ret;
278
0
  uint16_t p;
279
280
  /* this extension is only being sent on client side */
281
0
  if (session->security_parameters.entity == GNUTLS_CLIENT) {
282
0
    len = session->internals.priorities->groups.size;
283
0
    if (len > 0) {
284
0
      ret = _gnutls_buffer_append_prefix(extdata, 16,
285
0
                 len * 2);
286
0
      if (ret < 0)
287
0
        return gnutls_assert_val(ret);
288
289
0
      for (i = 0; i < len; i++) {
290
0
        p = session->internals.priorities->groups
291
0
              .entry[i]
292
0
              ->tls_id;
293
294
0
        _gnutls_handshake_log(
295
0
          "EXT[%p]: Sent group %s (0x%x)\n",
296
0
          session,
297
0
          session->internals.priorities->groups
298
0
            .entry[i]
299
0
            ->name,
300
0
          (unsigned)p);
301
302
0
        ret = _gnutls_buffer_append_prefix(extdata, 16,
303
0
                   p);
304
0
        if (ret < 0)
305
0
          return gnutls_assert_val(ret);
306
0
      }
307
0
      return (len + 1) * 2;
308
0
    }
309
0
  }
310
311
0
  return 0;
312
0
}
313
314
/* Returns 0 if the given ECC curve is allowed in the current
315
 * session. A negative error value is returned otherwise.
316
 */
317
bool _gnutls_session_supports_group(gnutls_session_t session,
318
            unsigned int group)
319
0
{
320
0
  unsigned i;
321
322
0
  for (i = 0; i < session->internals.priorities->groups.size; i++) {
323
0
    if (session->internals.priorities->groups.entry[i]->id == group)
324
0
      return true;
325
0
  }
326
327
0
  return false;
328
0
}