/src/gnutls/lib/tls13/key_update.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 "errors.h" |
25 | | #include "handshake.h" |
26 | | #include "tls13/key_update.h" |
27 | | #include "mem.h" |
28 | | #include "mbuffers.h" |
29 | | #include "secrets.h" |
30 | | #include "system/ktls.h" |
31 | | |
32 | 0 | #define KEY_UPDATES_WINDOW 1000 |
33 | | #define KEY_UPDATES_PER_WINDOW 8 |
34 | | |
35 | | /* |
36 | | * Sets kTLS keys if enabled. |
37 | | * If this operation fails with GNUTLS_E_INTERNAL_ERROR, KTLS is disabled |
38 | | * because KTLS most likely doesn't support key update. |
39 | | */ |
40 | | static inline int set_ktls_keys(gnutls_session_t session, |
41 | | gnutls_transport_ktls_enable_flags_t iface) |
42 | 0 | { |
43 | 0 | if (_gnutls_ktls_set_keys(session, iface) < 0) { |
44 | 0 | session->internals.ktls_enabled = 0; |
45 | 0 | session->internals.invalid_connection = true; |
46 | 0 | session->internals.resumable = false; |
47 | 0 | _gnutls_audit_log( |
48 | 0 | session, |
49 | 0 | "invalidating session: KTLS - couldn't update keys\n"); |
50 | 0 | return GNUTLS_E_INTERNAL_ERROR; |
51 | 0 | } |
52 | 0 | return 0; |
53 | 0 | } |
54 | | |
55 | | static int update_keys(gnutls_session_t session, hs_stage_t stage) |
56 | 0 | { |
57 | 0 | int ret; |
58 | |
|
59 | 0 | ret = _tls13_update_secret(session, |
60 | 0 | session->key.proto.tls13.temp_secret, |
61 | 0 | session->key.proto.tls13.temp_secret_size); |
62 | 0 | if (ret < 0) |
63 | 0 | return gnutls_assert_val(ret); |
64 | | |
65 | 0 | _gnutls_epoch_bump(session); |
66 | 0 | ret = _gnutls_epoch_dup(session, EPOCH_READ_CURRENT); |
67 | 0 | if (ret < 0) |
68 | 0 | return gnutls_assert_val(ret); |
69 | | |
70 | | /* If we send a key update during early start, only update our |
71 | | * write keys */ |
72 | 0 | if (session->internals.recv_state == RECV_STATE_EARLY_START) { |
73 | 0 | ret = _tls13_write_connection_state_init(session, stage); |
74 | 0 | if (ret < 0) |
75 | 0 | return gnutls_assert_val(ret); |
76 | | |
77 | 0 | if (IS_KTLS_ENABLED(session, GNUTLS_KTLS_SEND)) |
78 | 0 | ret = set_ktls_keys(session, GNUTLS_KTLS_SEND); |
79 | 0 | } else { |
80 | 0 | ret = _tls13_connection_state_init(session, stage); |
81 | 0 | if (ret < 0) |
82 | 0 | return gnutls_assert_val(ret); |
83 | | |
84 | 0 | if (IS_KTLS_ENABLED(session, GNUTLS_KTLS_SEND) && |
85 | 0 | stage == STAGE_UPD_OURS) |
86 | 0 | ret = set_ktls_keys(session, GNUTLS_KTLS_SEND); |
87 | 0 | else if (IS_KTLS_ENABLED(session, GNUTLS_KTLS_RECV) && |
88 | 0 | stage == STAGE_UPD_PEERS) |
89 | 0 | ret = set_ktls_keys(session, GNUTLS_KTLS_RECV); |
90 | 0 | } |
91 | 0 | if (ret < 0) |
92 | 0 | return gnutls_assert_val(ret); |
93 | | |
94 | 0 | return 0; |
95 | 0 | } |
96 | | |
97 | | int _gnutls13_recv_key_update(gnutls_session_t session, gnutls_buffer_st *buf) |
98 | 0 | { |
99 | 0 | int ret; |
100 | 0 | struct timespec now; |
101 | |
|
102 | 0 | if (buf->length != 1) |
103 | 0 | return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); |
104 | | |
105 | 0 | gnutls_gettime(&now); |
106 | | |
107 | | /* Roll over the counter if the time window has elapsed */ |
108 | 0 | if (session->internals.key_update_count == 0 || |
109 | 0 | timespec_sub_ms(&now, &session->internals.last_key_update) > |
110 | 0 | KEY_UPDATES_WINDOW) { |
111 | 0 | session->internals.last_key_update = now; |
112 | 0 | session->internals.key_update_count = 0; |
113 | 0 | } |
114 | |
|
115 | 0 | if (unlikely(++session->internals.key_update_count > |
116 | 0 | KEY_UPDATES_PER_WINDOW)) { |
117 | 0 | _gnutls_debug_log( |
118 | 0 | "reached maximum number of key updates per %d milliseconds (%d)\n", |
119 | 0 | KEY_UPDATES_WINDOW, KEY_UPDATES_PER_WINDOW); |
120 | 0 | return gnutls_assert_val(GNUTLS_E_TOO_MANY_HANDSHAKE_PACKETS); |
121 | 0 | } |
122 | | |
123 | 0 | _gnutls_epoch_gc(session); |
124 | |
|
125 | 0 | _gnutls_handshake_log("HSK[%p]: received TLS 1.3 key update (%u)\n", |
126 | 0 | session, (unsigned)buf->data[0]); |
127 | |
|
128 | 0 | switch (buf->data[0]) { |
129 | 0 | case 0: |
130 | | /* peer updated its key, not requested our key update */ |
131 | 0 | ret = update_keys(session, STAGE_UPD_PEERS); |
132 | 0 | if (ret < 0) |
133 | 0 | return gnutls_assert_val(ret); |
134 | | |
135 | 0 | break; |
136 | 0 | case 1: |
137 | 0 | if (session->internals.hsk_flags & HSK_KEY_UPDATE_ASKED) { |
138 | | /* if we had asked a key update we shouldn't get this |
139 | | * reply */ |
140 | 0 | return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER); |
141 | 0 | } |
142 | | |
143 | | /* peer updated its key, requested our key update */ |
144 | 0 | ret = update_keys(session, STAGE_UPD_PEERS); |
145 | 0 | if (ret < 0) |
146 | 0 | return gnutls_assert_val(ret); |
147 | | |
148 | | /* we mark that a key update is schedule, and it |
149 | | * will be performed prior to sending the next application |
150 | | * message. |
151 | | */ |
152 | 0 | if (session->internals.rsend_state == RECORD_SEND_NORMAL) |
153 | 0 | session->internals.rsend_state = |
154 | 0 | RECORD_SEND_KEY_UPDATE_1; |
155 | 0 | else if (session->internals.rsend_state == RECORD_SEND_CORKED) |
156 | 0 | session->internals.rsend_state = |
157 | 0 | RECORD_SEND_CORKED_TO_KU; |
158 | |
|
159 | 0 | break; |
160 | 0 | default: |
161 | 0 | return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); |
162 | 0 | } |
163 | | |
164 | 0 | session->internals.hsk_flags &= ~(unsigned)(HSK_KEY_UPDATE_ASKED); |
165 | |
|
166 | 0 | return 0; |
167 | 0 | } |
168 | | |
169 | | int _gnutls13_send_key_update(gnutls_session_t session, unsigned again, |
170 | | unsigned flags /* GNUTLS_KU_* */) |
171 | 0 | { |
172 | 0 | int ret; |
173 | 0 | mbuffer_st *bufel = NULL; |
174 | 0 | uint8_t val; |
175 | |
|
176 | 0 | if (again == 0) { |
177 | 0 | if (flags & GNUTLS_KU_PEER) { |
178 | | /* mark that we asked a key update to prevent an |
179 | | * infinite ping pong when receiving the reply */ |
180 | 0 | session->internals.hsk_flags |= HSK_KEY_UPDATE_ASKED; |
181 | 0 | val = 0x01; |
182 | 0 | } else { |
183 | 0 | val = 0x00; |
184 | 0 | } |
185 | |
|
186 | 0 | _gnutls_handshake_log("HSK[%p]: sending key update (%u)\n", |
187 | 0 | session, (unsigned)val); |
188 | |
|
189 | 0 | bufel = _gnutls_handshake_alloc(session, 1); |
190 | 0 | if (bufel == NULL) |
191 | 0 | return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); |
192 | | |
193 | 0 | _mbuffer_set_udata_size(bufel, 0); |
194 | 0 | ret = _mbuffer_append_data(bufel, &val, 1); |
195 | 0 | if (ret < 0) { |
196 | 0 | gnutls_assert(); |
197 | 0 | goto cleanup; |
198 | 0 | } |
199 | 0 | } |
200 | | |
201 | 0 | return _gnutls_send_handshake(session, bufel, |
202 | 0 | GNUTLS_HANDSHAKE_KEY_UPDATE); |
203 | | |
204 | 0 | cleanup: |
205 | 0 | _mbuffer_xfree(&bufel); |
206 | 0 | return ret; |
207 | 0 | } |
208 | | |
209 | | /** |
210 | | * gnutls_session_key_update: |
211 | | * @session: is a #gnutls_session_t type. |
212 | | * @flags: zero of %GNUTLS_KU_PEER |
213 | | * |
214 | | * This function will update/refresh the session keys when the |
215 | | * TLS protocol is 1.3 or better. The peer is notified of the |
216 | | * update by sending a message, so this function should be |
217 | | * treated similarly to gnutls_record_send() --i.e., it may |
218 | | * return %GNUTLS_E_AGAIN or %GNUTLS_E_INTERRUPTED. |
219 | | * |
220 | | * When this flag %GNUTLS_KU_PEER is specified, this function |
221 | | * in addition to updating the local keys, will ask the peer to |
222 | | * refresh its keys too. |
223 | | * |
224 | | * If the negotiated version is not TLS 1.3 or better this |
225 | | * function will return %GNUTLS_E_INVALID_REQUEST. |
226 | | * |
227 | | * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code. |
228 | | * |
229 | | * Since: 3.6.3 |
230 | | **/ |
231 | | int gnutls_session_key_update(gnutls_session_t session, unsigned flags) |
232 | 0 | { |
233 | 0 | int ret; |
234 | 0 | const version_entry_st *vers = get_version(session); |
235 | |
|
236 | 0 | if (!vers->tls13_sem) |
237 | 0 | return GNUTLS_E_INVALID_REQUEST; |
238 | | |
239 | 0 | ret = _gnutls13_send_key_update(session, AGAIN(STATE150), flags); |
240 | 0 | STATE = STATE150; |
241 | |
|
242 | 0 | if (ret < 0) { |
243 | 0 | gnutls_assert(); |
244 | 0 | return ret; |
245 | 0 | } |
246 | 0 | STATE = STATE0; |
247 | |
|
248 | 0 | _gnutls_epoch_gc(session); |
249 | | |
250 | | /* it was completely sent, update the keys */ |
251 | 0 | ret = update_keys(session, STAGE_UPD_OURS); |
252 | 0 | if (ret < 0) |
253 | 0 | return gnutls_assert_val(ret); |
254 | | |
255 | 0 | return 0; |
256 | 0 | } |