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