Coverage Report

Created: 2025-07-23 07:18

/src/gnutls/lib/algorithms/groups.c
Line
Count
Source (jump to first uncovered line)
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 "algorithms.h"
25
#include "errors.h"
26
#include "x509/common.h"
27
#include "pk.h"
28
#include "c-strcase.h"
29
30
/* Supported ECC curves
31
 */
32
33
static const gnutls_group_entry_st supported_groups[] = {
34
  {
35
    .name = "SECP192R1",
36
    .id = GNUTLS_GROUP_SECP192R1,
37
    .curve = GNUTLS_ECC_CURVE_SECP192R1,
38
    .tls_id = 19,
39
    .pk = GNUTLS_PK_ECDSA,
40
  },
41
  {
42
    .name = "SECP224R1",
43
    .id = GNUTLS_GROUP_SECP224R1,
44
    .curve = GNUTLS_ECC_CURVE_SECP224R1,
45
    .tls_id = 21,
46
    .pk = GNUTLS_PK_ECDSA,
47
  },
48
  {
49
    .name = "SECP256R1",
50
    .id = GNUTLS_GROUP_SECP256R1,
51
    .curve = GNUTLS_ECC_CURVE_SECP256R1,
52
    .tls_id = 23,
53
    .pk = GNUTLS_PK_ECDSA,
54
  },
55
  {
56
    .name = "SECP384R1",
57
    .id = GNUTLS_GROUP_SECP384R1,
58
    .curve = GNUTLS_ECC_CURVE_SECP384R1,
59
    .tls_id = 24,
60
    .pk = GNUTLS_PK_ECDSA,
61
  },
62
  {
63
    .name = "SECP521R1",
64
    .id = GNUTLS_GROUP_SECP521R1,
65
    .curve = GNUTLS_ECC_CURVE_SECP521R1,
66
    .tls_id = 25,
67
    .pk = GNUTLS_PK_ECDSA,
68
  },
69
  {
70
    .name = "X25519",
71
    .id = GNUTLS_GROUP_X25519,
72
    .curve = GNUTLS_ECC_CURVE_X25519,
73
    .tls_id = 29,
74
    .pk = GNUTLS_PK_ECDH_X25519,
75
  },
76
#ifdef ENABLE_GOST
77
  /* draft-smyshlyaev-tls12-gost-suites-06, Section 6 */
78
  {
79
    .name = "GC256A",
80
    .id = GNUTLS_GROUP_GC256A,
81
    .curve = GNUTLS_ECC_CURVE_GOST256A,
82
    .pk = GNUTLS_PK_GOST_12_256,
83
    .tls_id = 34,
84
  },
85
  {
86
    .name = "GC256B",
87
    .id = GNUTLS_GROUP_GC256B,
88
    .curve = GNUTLS_ECC_CURVE_GOST256B,
89
    .pk = GNUTLS_PK_GOST_12_256,
90
    .tls_id = 35,
91
  },
92
  {
93
    .name = "GC256C",
94
    .id = GNUTLS_GROUP_GC256C,
95
    .curve = GNUTLS_ECC_CURVE_GOST256C,
96
    .pk = GNUTLS_PK_GOST_12_256,
97
    .tls_id = 36,
98
  },
99
  {
100
    .name = "GC256D",
101
    .id = GNUTLS_GROUP_GC256D,
102
    .curve = GNUTLS_ECC_CURVE_GOST256D,
103
    .pk = GNUTLS_PK_GOST_12_256,
104
    .tls_id = 37,
105
  },
106
  {
107
    .name = "GC512A",
108
    .id = GNUTLS_GROUP_GC512A,
109
    .curve = GNUTLS_ECC_CURVE_GOST512A,
110
    .pk = GNUTLS_PK_GOST_12_512,
111
    .tls_id = 38,
112
  },
113
  {
114
    .name = "GC512B",
115
    .id = GNUTLS_GROUP_GC512B,
116
    .curve = GNUTLS_ECC_CURVE_GOST512B,
117
    .pk = GNUTLS_PK_GOST_12_512,
118
    .tls_id = 39,
119
  },
120
  {
121
    .name = "GC512C",
122
    .id = GNUTLS_GROUP_GC512C,
123
    .curve = GNUTLS_ECC_CURVE_GOST512C,
124
    .pk = GNUTLS_PK_GOST_12_512,
125
    .tls_id = 40,
126
  },
127
#endif
128
  { .name = "X448",
129
    .id = GNUTLS_GROUP_X448,
130
    .curve = GNUTLS_ECC_CURVE_X448,
131
    .tls_id = 30,
132
    .pk = GNUTLS_PK_ECDH_X448 },
133
#ifdef ENABLE_DHE
134
  { .name = "FFDHE2048",
135
    .id = GNUTLS_GROUP_FFDHE2048,
136
    .generator = &gnutls_ffdhe_2048_group_generator,
137
    .prime = &gnutls_ffdhe_2048_group_prime,
138
    .q = &gnutls_ffdhe_2048_group_q,
139
    .q_bits = &gnutls_ffdhe_2048_key_bits,
140
    .pk = GNUTLS_PK_DH,
141
    .tls_id = 0x100 },
142
  { .name = "FFDHE3072",
143
    .id = GNUTLS_GROUP_FFDHE3072,
144
    .generator = &gnutls_ffdhe_3072_group_generator,
145
    .prime = &gnutls_ffdhe_3072_group_prime,
146
    .q = &gnutls_ffdhe_3072_group_q,
147
    .q_bits = &gnutls_ffdhe_3072_key_bits,
148
    .pk = GNUTLS_PK_DH,
149
    .tls_id = 0x101 },
150
  { .name = "FFDHE4096",
151
    .id = GNUTLS_GROUP_FFDHE4096,
152
    .generator = &gnutls_ffdhe_4096_group_generator,
153
    .prime = &gnutls_ffdhe_4096_group_prime,
154
    .q = &gnutls_ffdhe_4096_group_q,
155
    .q_bits = &gnutls_ffdhe_4096_key_bits,
156
    .pk = GNUTLS_PK_DH,
157
    .tls_id = 0x102 },
158
  { .name = "FFDHE6144",
159
    .id = GNUTLS_GROUP_FFDHE6144,
160
    .generator = &gnutls_ffdhe_6144_group_generator,
161
    .prime = &gnutls_ffdhe_6144_group_prime,
162
    .q = &gnutls_ffdhe_6144_group_q,
163
    .q_bits = &gnutls_ffdhe_6144_key_bits,
164
    .pk = GNUTLS_PK_DH,
165
    .tls_id = 0x103 },
166
  { .name = "FFDHE8192",
167
    .id = GNUTLS_GROUP_FFDHE8192,
168
    .generator = &gnutls_ffdhe_8192_group_generator,
169
    .prime = &gnutls_ffdhe_8192_group_prime,
170
    .q = &gnutls_ffdhe_8192_group_q,
171
    .q_bits = &gnutls_ffdhe_8192_key_bits,
172
    .pk = GNUTLS_PK_DH,
173
    .tls_id = 0x104 },
174
#endif
175
#ifdef HAVE_LEANCRYPTO
176
  {
177
    .name = "MLKEM768",
178
    .id = GNUTLS_GROUP_EXP_MLKEM768,
179
    .pk = GNUTLS_PK_MLKEM768,
180
    .pubkey_size = MLKEM768_PUBKEY_SIZE,
181
    .ciphertext_size = MLKEM768_CIPHERTEXT_SIZE,
182
    /* absense of .tls_id means that this group alone cannot be used in TLS */
183
  },
184
  {
185
    .name = "MLKEM1024",
186
    .id = GNUTLS_GROUP_EXP_MLKEM1024,
187
    .pk = GNUTLS_PK_MLKEM1024,
188
    .pubkey_size = MLKEM1024_PUBKEY_SIZE,
189
    .ciphertext_size = MLKEM1024_CIPHERTEXT_SIZE,
190
    /* absense of .tls_id means that this group alone cannot be used in TLS */
191
  },
192
#endif
193
#ifdef HAVE_LEANCRYPTO
194
  { .name = "SECP256R1-MLKEM768",
195
    .id = GNUTLS_GROUP_EXP_SECP256R1_MLKEM768,
196
    .ids = { GNUTLS_GROUP_SECP256R1, GNUTLS_GROUP_EXP_MLKEM768,
197
       GNUTLS_GROUP_INVALID },
198
    .tls_id = 0x11EB },
199
  { .name = "SECP384R1-MLKEM1024",
200
    .id = GNUTLS_GROUP_EXP_SECP384R1_MLKEM1024,
201
    .ids = { GNUTLS_GROUP_SECP384R1, GNUTLS_GROUP_EXP_MLKEM1024,
202
       GNUTLS_GROUP_INVALID },
203
    .tls_id = 0x11ED },
204
  { .name = "X25519-MLKEM768",
205
    .id = GNUTLS_GROUP_EXP_X25519_MLKEM768,
206
    .ids = { GNUTLS_GROUP_EXP_MLKEM768, GNUTLS_GROUP_X25519,
207
       GNUTLS_GROUP_INVALID },
208
    .tls_id = 0x11EC },
209
#endif
210
  { 0, 0, 0 }
211
};
212
213
#define GNUTLS_GROUP_LOOP(b)                                       \
214
0
  {                                                          \
215
0
    const gnutls_group_entry_st *p;                    \
216
0
    for (p = supported_groups; p->name != NULL; p++) { \
217
0
      b;                                         \
218
0
    }                                                  \
219
0
  }
220
221
static inline const gnutls_group_entry_st *group_to_entry(gnutls_group_t group)
222
0
{
223
0
  if (group == 0)
224
0
    return NULL;
225
226
0
  GNUTLS_GROUP_LOOP(if (p->id == group) { return p; });
227
228
0
  return NULL;
229
0
}
230
231
static inline bool
232
group_is_supported_standalone(const gnutls_group_entry_st *group)
233
0
{
234
0
  return group->pk != 0 && _gnutls_pk_exists(group->pk) &&
235
0
         (group->curve == 0 ||
236
0
    _gnutls_ecc_curve_is_supported(group->curve));
237
0
}
238
239
static inline bool group_is_supported(const gnutls_group_entry_st *group)
240
0
{
241
0
  if (!IS_GROUP_HYBRID(group))
242
0
    return group_is_supported_standalone(group);
243
244
0
  for (size_t i = 0;
245
0
       i < MAX_HYBRID_GROUPS && group->ids[i] != GNUTLS_GROUP_INVALID;
246
0
       i++) {
247
0
    const gnutls_group_entry_st *p = group_to_entry(group->ids[i]);
248
0
    if (!p || !group_is_supported_standalone(p))
249
0
      return false;
250
0
  }
251
252
0
  return true;
253
0
}
254
255
/* Returns the TLS id of the given curve
256
 */
257
const gnutls_group_entry_st *_gnutls_tls_id_to_group(unsigned num)
258
0
{
259
0
  GNUTLS_GROUP_LOOP(
260
0
    if (p->tls_id == num && group_is_supported(p)) { return p; });
261
262
0
  return NULL;
263
0
}
264
265
const gnutls_group_entry_st *_gnutls_id_to_group(unsigned id)
266
0
{
267
0
  if (id == 0)
268
0
    return NULL;
269
270
0
  GNUTLS_GROUP_LOOP(
271
0
    if (p->id == id && group_is_supported(p)) { return p; });
272
273
0
  return NULL;
274
0
}
275
276
/**
277
 * gnutls_group_list:
278
 *
279
 * Get the list of supported elliptic curves.
280
 *
281
 * This function is not thread safe.
282
 *
283
 * Returns: Return a (0)-terminated list of #gnutls_group_t
284
 *   integers indicating the available groups.
285
 *
286
 * Since: 3.6.0
287
 **/
288
const gnutls_group_t *gnutls_group_list(void)
289
0
{
290
0
  static gnutls_group_t groups[MAX_ALGOS + 1] = { 0 };
291
292
0
  if (groups[0] == 0) {
293
0
    size_t i = 0;
294
295
0
    for (const gnutls_group_entry_st *p = supported_groups;
296
0
         p->name != NULL; p++) {
297
0
      if (group_is_supported(p))
298
0
        groups[i++] = p->id;
299
0
    }
300
0
    groups[i++] = GNUTLS_GROUP_INVALID;
301
0
  }
302
303
0
  return groups;
304
0
}
305
306
/**
307
 * gnutls_group_get_id:
308
 * @name: is a group name
309
 *
310
 * The names are compared in a case insensitive way.
311
 *
312
 * Returns: return a #gnutls_group_t value corresponding to
313
 *   the specified group, or %GNUTLS_GROUP_INVALID on error.
314
 *
315
 * Since: 3.6.0
316
 **/
317
gnutls_group_t gnutls_group_get_id(const char *name)
318
0
{
319
0
  gnutls_group_t ret = GNUTLS_GROUP_INVALID;
320
321
0
  GNUTLS_GROUP_LOOP(if (c_strcasecmp(p->name, name) == 0 &&
322
0
            (p->curve == 0 ||
323
0
             _gnutls_ecc_curve_is_supported(p->curve))) {
324
0
    ret = p->id;
325
0
    break;
326
0
  });
327
328
0
  return ret;
329
0
}
330
331
/* Similar to gnutls_group_get_id, except that it does not check if
332
 * the curve is supported.
333
 */
334
gnutls_group_t _gnutls_group_get_id(const char *name)
335
0
{
336
0
  gnutls_group_t ret = GNUTLS_GROUP_INVALID;
337
338
0
  GNUTLS_GROUP_LOOP(if (c_strcasecmp(p->name, name) == 0) {
339
0
    ret = p->id;
340
0
    break;
341
0
  });
342
343
0
  return ret;
344
0
}
345
346
/**
347
 * gnutls_group_get_name:
348
 * @group: is an element from %gnutls_group_t
349
 *
350
 * Convert a #gnutls_group_t value to a string.
351
 *
352
 * Returns: a string that contains the name of the specified
353
 *   group or %NULL.
354
 *
355
 * Since: 3.6.0
356
 **/
357
const char *gnutls_group_get_name(gnutls_group_t group)
358
0
{
359
0
  GNUTLS_GROUP_LOOP(if (p->id == group) { return p->name; });
360
361
0
  return NULL;
362
0
}
363
364
/* Expand GROUP into hybrid SUBGROUPS if any, otherwise an array
365
 * containing the GROUP itself. The result will be written to
366
 * SUBGROUPS, which will be NUL-terminated.
367
 */
368
int _gnutls_group_expand(
369
  const gnutls_group_entry_st *group,
370
  const gnutls_group_entry_st *subgroups[MAX_HYBRID_GROUPS + 1])
371
0
{
372
0
  size_t pos = 0;
373
374
0
  if (IS_GROUP_HYBRID(group)) {
375
0
    for (size_t i = 0; i < MAX_HYBRID_GROUPS &&
376
0
           group->ids[i] != GNUTLS_GROUP_INVALID;
377
0
         i++) {
378
0
      const gnutls_group_entry_st *p =
379
0
        group_to_entry(group->ids[i]);
380
      /* This shouldn't happen, as GROUP is assumed
381
       * to be supported before calling this
382
       * function. */
383
0
      if (unlikely(!p))
384
0
        return gnutls_assert_val(
385
0
          GNUTLS_E_INTERNAL_ERROR);
386
0
      subgroups[pos++] = p;
387
0
    }
388
0
  } else {
389
0
    subgroups[pos++] = group;
390
0
  }
391
0
  subgroups[pos] = NULL;
392
0
  return 0;
393
0
}