Coverage Report

Created: 2023-03-26 08:33

/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 =
50
      GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_DTLS |
51
      GNUTLS_EXT_FLAG_CLIENT_HELLO | GNUTLS_EXT_FLAG_EE |
52
      GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO,
53
  .recv_func = _gnutls_supported_groups_recv_params,
54
  .send_func = _gnutls_supported_groups_send_params,
55
  .pack_func = NULL,
56
  .unpack_func = NULL,
57
  .deinit_func = NULL,
58
  .cannot_be_overriden = 1
59
};
60
61
static unsigned get_min_dh(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 =
69
0
      (gnutls_certificate_credentials_t) _gnutls_get_cred(session,
70
0
                GNUTLS_CRD_CERTIFICATE);
71
0
  psk_cred =
72
0
      (gnutls_psk_server_credentials_t) _gnutls_get_cred(session,
73
0
                     GNUTLS_CRD_PSK);
74
0
  anon_cred =
75
0
      (gnutls_anon_server_credentials_t) _gnutls_get_cred(session,
76
0
                GNUTLS_CRD_ANON);
77
78
0
  if (cert_cred) {
79
0
    level = cert_cred->dh_sec_param;
80
0
  } else if (psk_cred) {
81
0
    level = psk_cred->dh_sec_param;
82
0
  } else if (anon_cred) {
83
0
    level = anon_cred->dh_sec_param;
84
0
  }
85
86
0
  if (level)
87
0
    return gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, level);
88
89
0
  return 0;
90
0
}
91
92
/*
93
 * In case of a server: if a SUPPORTED_GROUPS extension type is received then it stores
94
 * into the session security parameters the new value. The server may use gnutls_session_certificate_type_get(),
95
 * to access it.
96
 *
97
 * In case of a client: If supported_eccs have been specified then we send the extension.
98
 *
99
 */
100
static int
101
_gnutls_supported_groups_recv_params(gnutls_session_t session,
102
             const uint8_t * data, size_t data_size)
103
0
{
104
0
  int i;
105
0
  uint16_t len;
106
0
  const uint8_t *p = data;
107
0
  const gnutls_group_entry_st *group = NULL;
108
0
  unsigned have_ffdhe = 0;
109
0
  unsigned tls_id;
110
0
  unsigned min_dh;
111
0
  unsigned j;
112
0
  int serv_ec_idx, serv_dh_idx; /* index in server's priority listing */
113
0
  int cli_ec_pos, cli_dh_pos; /* position in listing sent by client */
114
115
0
  if (session->security_parameters.entity == GNUTLS_CLIENT) {
116
    /* A client shouldn't receive this extension in TLS1.2. It is
117
     * possible to read that message under TLS1.3 as an encrypted
118
     * extension. */
119
0
    return 0;
120
0
  } else {   /* SERVER SIDE - we must check if the sent supported ecc type is the right one
121
         */
122
0
    if (data_size < 2)
123
0
      return
124
0
          gnutls_assert_val
125
0
          (GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION);
126
127
0
    DECR_LEN(data_size, 2);
128
0
    len = _gnutls_read_uint16(p);
129
0
    p += 2;
130
131
0
    if (len % 2 != 0)
132
0
      return
133
0
          gnutls_assert_val
134
0
          (GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
135
136
0
    DECR_LEN(data_size, len);
137
138
    /* we figure what is the minimum DH allowed for this session, if any */
139
0
    min_dh = get_min_dh(session);
140
141
0
    serv_ec_idx = serv_dh_idx = -1;
142
0
    cli_ec_pos = cli_dh_pos = -1;
143
144
    /* This extension is being processed prior to a ciphersuite being selected,
145
     * so we cannot rely on ciphersuite information. */
146
0
    for (i = 0; i < len; i += 2) {
147
0
      if (have_ffdhe == 0 && p[i] == 0x01)
148
0
        have_ffdhe = 1;
149
150
0
      tls_id = _gnutls_read_uint16(&p[i]);
151
0
      group = _gnutls_tls_id_to_group(tls_id);
152
153
0
      _gnutls_handshake_log
154
0
          ("EXT[%p]: Received group %s (0x%x)\n", session,
155
0
           group ? group->name : "unknown", tls_id);
156
0
      if (group == NULL)
157
0
        continue;
158
159
0
      if (min_dh > 0 && group->prime
160
0
          && group->prime->size * 8 < min_dh)
161
0
        continue;
162
163
      /* we simulate _gnutls_session_supports_group, but we prioritize if
164
       * %SERVER_PRECEDENCE is given */
165
0
      for (j = 0;
166
0
           j < session->internals.priorities->groups.size;
167
0
           j++) {
168
0
        if (session->internals.priorities->
169
0
            groups.entry[j]->id == group->id) {
170
0
          if (session->internals.
171
0
              priorities->server_precedence) {
172
0
            if (group->pk == GNUTLS_PK_DH) {
173
0
              if (serv_dh_idx != -1
174
0
                  && (int)j >
175
0
                  serv_dh_idx)
176
0
                break;
177
0
              serv_dh_idx = j;
178
0
              cli_dh_pos = i;
179
0
            } else if (IS_EC(group->pk)) {
180
0
              if (serv_ec_idx != -1
181
0
                  && (int)j >
182
0
                  serv_ec_idx)
183
0
                break;
184
0
              serv_ec_idx = j;
185
0
              cli_ec_pos = i;
186
0
            }
187
0
          } else {
188
0
            if (group->pk == GNUTLS_PK_DH) {
189
0
              if (cli_dh_pos != -1)
190
0
                break;
191
0
              cli_dh_pos = i;
192
0
              serv_dh_idx = j;
193
0
            } else if (IS_EC(group->pk)) {
194
0
              if (cli_ec_pos != -1)
195
0
                break;
196
0
              cli_ec_pos = i;
197
0
              serv_ec_idx = j;
198
0
            }
199
0
          }
200
0
          break;
201
0
        }
202
0
      }
203
0
    }
204
205
    /* serv_dh/ec_pos contain the index of the groups we want to use.
206
     */
207
0
    if (serv_dh_idx != -1) {
208
0
      session->internals.cand_dh_group =
209
0
          session->internals.priorities->
210
0
          groups.entry[serv_dh_idx];
211
0
      session->internals.cand_group =
212
0
          session->internals.cand_dh_group;
213
0
    }
214
215
0
    if (serv_ec_idx != -1) {
216
0
      session->internals.cand_ec_group =
217
0
          session->internals.priorities->
218
0
          groups.entry[serv_ec_idx];
219
0
      if (session->internals.cand_group == NULL
220
0
          || (session->internals.priorities->server_precedence
221
0
        && serv_ec_idx < serv_dh_idx)
222
0
          || (!session->internals.
223
0
        priorities->server_precedence
224
0
        && cli_ec_pos < cli_dh_pos)) {
225
0
        session->internals.cand_group =
226
0
            session->internals.cand_ec_group;
227
0
      }
228
0
    }
229
230
0
    if (session->internals.cand_group)
231
0
      _gnutls_handshake_log("EXT[%p]: Selected group %s\n",
232
0
                session,
233
0
                session->internals.
234
0
                cand_group->name);
235
236
0
    if (have_ffdhe)
237
0
      session->internals.hsk_flags |= HSK_HAVE_FFDHE;
238
0
  }
239
240
0
  return 0;
241
0
}
242
243
/* returns data_size or a negative number on failure
244
 */
245
static int
246
_gnutls_supported_groups_send_params(gnutls_session_t session,
247
             gnutls_buffer_st * extdata)
248
0
{
249
0
  unsigned len, i;
250
0
  int ret;
251
0
  uint16_t p;
252
253
  /* this extension is only being sent on client side */
254
0
  if (session->security_parameters.entity == GNUTLS_CLIENT) {
255
256
0
    len = session->internals.priorities->groups.size;
257
0
    if (len > 0) {
258
0
      ret =
259
0
          _gnutls_buffer_append_prefix(extdata, 16, len * 2);
260
0
      if (ret < 0)
261
0
        return gnutls_assert_val(ret);
262
263
0
      for (i = 0; i < len; i++) {
264
0
        p = session->internals.priorities->
265
0
            groups.entry[i]->tls_id;
266
267
0
        _gnutls_handshake_log
268
0
            ("EXT[%p]: Sent group %s (0x%x)\n", session,
269
0
             session->internals.priorities->
270
0
             groups.entry[i]->name, (unsigned)p);
271
272
0
        ret =
273
0
            _gnutls_buffer_append_prefix(extdata,
274
0
                 16, p);
275
0
        if (ret < 0)
276
0
          return gnutls_assert_val(ret);
277
0
      }
278
0
      return (len + 1) * 2;
279
0
    }
280
281
0
  }
282
283
0
  return 0;
284
0
}
285
286
/* Returns 0 if the given ECC curve is allowed in the current
287
 * session. A negative error value is returned otherwise.
288
 */
289
int _gnutls_session_supports_group(gnutls_session_t session, unsigned int group)
290
0
{
291
0
  unsigned i;
292
293
0
  for (i = 0; i < session->internals.priorities->groups.size; i++) {
294
0
    if (session->internals.priorities->groups.entry[i]->id == group)
295
0
      return 0;
296
0
  }
297
298
0
  return GNUTLS_E_ECC_UNSUPPORTED_CURVE;
299
0
}