/src/gnutls/lib/ext/psk_ke_modes.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2017 Free Software Foundation, Inc. |
3 | | * |
4 | | * Author: Ander Juaristi |
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 "ext/psk_ke_modes.h" |
25 | | #include "ext/pre_shared_key.h" |
26 | | #include <assert.h> |
27 | | |
28 | 0 | #define PSK_KE 0 |
29 | 0 | #define PSK_DHE_KE 1 |
30 | | |
31 | | static int psk_ke_modes_send_params(gnutls_session_t session, |
32 | | gnutls_buffer_t extdata) |
33 | 0 | { |
34 | 0 | int ret; |
35 | 0 | const version_entry_st *vers; |
36 | 0 | uint8_t data[2]; |
37 | 0 | unsigned pos, i; |
38 | 0 | unsigned have_dhpsk = 0; |
39 | 0 | unsigned have_psk = 0; |
40 | | |
41 | | /* Server doesn't send psk_key_exchange_modes */ |
42 | 0 | if (session->security_parameters.entity == GNUTLS_SERVER) |
43 | 0 | return 0; |
44 | | |
45 | | /* If session ticket is disabled and no PSK key exchange is |
46 | | * enabled, don't send the extension */ |
47 | 0 | if ((session->internals.flags & GNUTLS_NO_TICKETS) && |
48 | 0 | !session->internals.priorities->have_psk) |
49 | 0 | return 0; |
50 | | |
51 | 0 | vers = _gnutls_version_max(session); |
52 | 0 | if (!vers || !vers->tls13_sem) |
53 | 0 | return 0; |
54 | | |
55 | | /* We send the list prioritized according to our preferences as a convention |
56 | | * (used throughout the protocol), even if the protocol doesn't mandate that |
57 | | * for this particular message. That way we can keep the TLS 1.2 semantics/ |
58 | | * prioritization when negotiating PSK or DHE-PSK. Receiving servers would |
59 | | * very likely respect our prioritization if they parse the message serially. */ |
60 | 0 | pos = 0; |
61 | 0 | for (i = 0; i < session->internals.priorities->_kx.num_priorities; |
62 | 0 | i++) { |
63 | 0 | if (session->internals.priorities->_kx.priorities[i] == |
64 | 0 | GNUTLS_KX_PSK && |
65 | 0 | !have_psk) { |
66 | 0 | assert(pos <= 1); |
67 | 0 | data[pos++] = PSK_KE; |
68 | 0 | session->internals.hsk_flags |= HSK_PSK_KE_MODE_PSK; |
69 | 0 | have_psk = 1; |
70 | 0 | } else if ((session->internals.priorities->_kx.priorities[i] == |
71 | 0 | GNUTLS_KX_DHE_PSK || |
72 | 0 | session->internals.priorities->_kx.priorities[i] == |
73 | 0 | GNUTLS_KX_ECDHE_PSK) && |
74 | 0 | !have_dhpsk) { |
75 | 0 | assert(pos <= 1); |
76 | 0 | data[pos++] = PSK_DHE_KE; |
77 | 0 | session->internals.hsk_flags |= HSK_PSK_KE_MODE_DHE_PSK; |
78 | 0 | have_dhpsk = 1; |
79 | 0 | } |
80 | | |
81 | 0 | if (have_psk && have_dhpsk) |
82 | 0 | break; |
83 | 0 | } |
84 | | |
85 | | /* For session resumption we need to send at least one */ |
86 | 0 | if (pos == 0) { |
87 | 0 | if (session->internals.flags & GNUTLS_NO_TICKETS) |
88 | 0 | return 0; |
89 | | |
90 | 0 | data[pos++] = PSK_DHE_KE; |
91 | 0 | data[pos++] = PSK_KE; |
92 | 0 | session->internals.hsk_flags |= HSK_PSK_KE_MODE_DHE_PSK; |
93 | 0 | session->internals.hsk_flags |= HSK_PSK_KE_MODE_PSK; |
94 | 0 | } |
95 | | |
96 | 0 | ret = _gnutls_buffer_append_data_prefix(extdata, 8, data, pos); |
97 | 0 | if (ret < 0) |
98 | 0 | return gnutls_assert_val(ret); |
99 | | |
100 | 0 | session->internals.hsk_flags |= HSK_PSK_KE_MODES_SENT; |
101 | |
|
102 | 0 | return 0; |
103 | 0 | } |
104 | | |
105 | 0 | #define MAX_POS INT_MAX |
106 | | |
107 | | /* |
108 | | * Since we only support ECDHE-authenticated PSKs, the server |
109 | | * just verifies that a "psk_key_exchange_modes" extension was received, |
110 | | * and that it contains the value one. |
111 | | */ |
112 | | static int psk_ke_modes_recv_params(gnutls_session_t session, |
113 | | const unsigned char *data, size_t len) |
114 | 0 | { |
115 | 0 | uint8_t ke_modes_len; |
116 | 0 | const version_entry_st *vers = get_version(session); |
117 | 0 | gnutls_psk_server_credentials_t cred; |
118 | 0 | int dhpsk_pos = MAX_POS; |
119 | 0 | int psk_pos = MAX_POS; |
120 | 0 | int cli_psk_pos = MAX_POS; |
121 | 0 | int cli_dhpsk_pos = MAX_POS; |
122 | 0 | unsigned i; |
123 | | |
124 | | /* Client doesn't receive psk_key_exchange_modes */ |
125 | 0 | if (session->security_parameters.entity == GNUTLS_CLIENT) |
126 | 0 | return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION); |
127 | | |
128 | | /* we set hsk_flags to HSK_PSK_KE_MODE_INVALID on failure to ensure that |
129 | | * when we parse the pre-shared key extension we detect PSK_KE_MODES as |
130 | | * received. */ |
131 | 0 | if (!vers || !vers->tls13_sem) { |
132 | 0 | session->internals.hsk_flags |= HSK_PSK_KE_MODE_INVALID; |
133 | 0 | return gnutls_assert_val(0); |
134 | 0 | } |
135 | | |
136 | 0 | cred = (gnutls_psk_server_credentials_t)_gnutls_get_cred( |
137 | 0 | session, GNUTLS_CRD_PSK); |
138 | 0 | if (cred == NULL && (session->internals.flags & GNUTLS_NO_TICKETS)) { |
139 | 0 | session->internals.hsk_flags |= HSK_PSK_KE_MODE_INVALID; |
140 | 0 | return gnutls_assert_val(0); |
141 | 0 | } |
142 | | |
143 | 0 | DECR_LEN(len, 1); |
144 | 0 | ke_modes_len = *(data++); |
145 | |
|
146 | 0 | for (i = 0; i < session->internals.priorities->_kx.num_priorities; |
147 | 0 | i++) { |
148 | 0 | if (session->internals.priorities->_kx.priorities[i] == |
149 | 0 | GNUTLS_KX_PSK && |
150 | 0 | psk_pos == MAX_POS) { |
151 | 0 | psk_pos = i; |
152 | 0 | } else if ((session->internals.priorities->_kx.priorities[i] == |
153 | 0 | GNUTLS_KX_DHE_PSK || |
154 | 0 | session->internals.priorities->_kx.priorities[i] == |
155 | 0 | GNUTLS_KX_ECDHE_PSK) && |
156 | 0 | dhpsk_pos == MAX_POS) { |
157 | 0 | dhpsk_pos = i; |
158 | 0 | } |
159 | |
|
160 | 0 | if (dhpsk_pos != MAX_POS && psk_pos != MAX_POS) |
161 | 0 | break; |
162 | 0 | } |
163 | |
|
164 | 0 | if (psk_pos == MAX_POS && dhpsk_pos == MAX_POS) { |
165 | 0 | if (!(session->internals.flags & GNUTLS_NO_TICKETS)) |
166 | 0 | dhpsk_pos = 0; |
167 | 0 | else if (session->internals.priorities->groups.size == 0) |
168 | 0 | return gnutls_assert_val(0); |
169 | 0 | } |
170 | | |
171 | 0 | for (i = 0; i < ke_modes_len; i++) { |
172 | 0 | DECR_LEN(len, 1); |
173 | 0 | if (data[i] == PSK_DHE_KE) |
174 | 0 | cli_dhpsk_pos = i; |
175 | 0 | else if (data[i] == PSK_KE) |
176 | 0 | cli_psk_pos = i; |
177 | |
|
178 | 0 | _gnutls_handshake_log("EXT[%p]: PSK KE mode %.2x received\n", |
179 | 0 | session, (unsigned)data[i]); |
180 | 0 | if (cli_psk_pos != MAX_POS && cli_dhpsk_pos != MAX_POS) |
181 | 0 | break; |
182 | 0 | } |
183 | | |
184 | 0 | if (session->internals.priorities->server_precedence) { |
185 | 0 | if (dhpsk_pos != MAX_POS && cli_dhpsk_pos != MAX_POS && |
186 | 0 | (dhpsk_pos < psk_pos || cli_psk_pos == MAX_POS)) |
187 | 0 | session->internals.hsk_flags |= HSK_PSK_KE_MODE_DHE_PSK; |
188 | 0 | else if (psk_pos != MAX_POS && cli_psk_pos != MAX_POS && |
189 | 0 | (psk_pos < dhpsk_pos || cli_dhpsk_pos == MAX_POS)) |
190 | 0 | session->internals.hsk_flags |= HSK_PSK_KE_MODE_PSK; |
191 | 0 | } else { |
192 | 0 | if (dhpsk_pos != MAX_POS && cli_dhpsk_pos != MAX_POS && |
193 | 0 | (cli_dhpsk_pos < cli_psk_pos || psk_pos == MAX_POS)) |
194 | 0 | session->internals.hsk_flags |= HSK_PSK_KE_MODE_DHE_PSK; |
195 | 0 | else if (psk_pos != MAX_POS && cli_psk_pos != MAX_POS && |
196 | 0 | (cli_psk_pos < cli_dhpsk_pos || dhpsk_pos == MAX_POS)) |
197 | 0 | session->internals.hsk_flags |= HSK_PSK_KE_MODE_PSK; |
198 | 0 | } |
199 | |
|
200 | 0 | if ((session->internals.hsk_flags & HSK_PSK_KE_MODE_PSK) || |
201 | 0 | (session->internals.hsk_flags & HSK_PSK_KE_MODE_DHE_PSK)) { |
202 | 0 | return 0; |
203 | 0 | } else { |
204 | 0 | session->internals.hsk_flags |= HSK_PSK_KE_MODE_INVALID; |
205 | 0 | return gnutls_assert_val(0); |
206 | 0 | } |
207 | 0 | } |
208 | | |
209 | | const hello_ext_entry_st ext_mod_psk_ke_modes = { |
210 | | .name = "PSK Key Exchange Modes", |
211 | | .tls_id = 45, |
212 | | .gid = GNUTLS_EXTENSION_PSK_KE_MODES, |
213 | | .client_parse_point = GNUTLS_EXT_TLS, |
214 | | .server_parse_point = GNUTLS_EXT_TLS, |
215 | | .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_CLIENT_HELLO | |
216 | | GNUTLS_EXT_FLAG_TLS13_SERVER_HELLO, |
217 | | .send_func = psk_ke_modes_send_params, |
218 | | .recv_func = psk_ke_modes_recv_params |
219 | | }; |