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