Line data Source code
1 : /*
2 : * Copyright (C) 2018 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 : #include "gnutls_int.h"
23 : #include "stek.h"
24 : #ifdef HAVE_VALGRIND_MEMCHECK_H
25 : #include <valgrind/memcheck.h>
26 : #endif
27 :
28 : #define NAME_POS (0)
29 : #define KEY_POS (TICKET_KEY_NAME_SIZE)
30 : #define MAC_SECRET_POS (TICKET_KEY_NAME_SIZE+TICKET_CIPHER_KEY_SIZE)
31 :
32 4617 : static int totp_sha3(gnutls_session_t session,
33 : uint64_t t,
34 : const gnutls_datum_t *secret,
35 : uint8_t out[TICKET_MASTER_KEY_SIZE])
36 : {
37 4617 : int retval;
38 4617 : uint8_t t_be[8];
39 4617 : digest_hd_st hd;
40 : /*
41 : * We choose SHA3-512 because it outputs 64 bytes,
42 : * just the same length as the ticket key.
43 : */
44 4617 : const gnutls_digest_algorithm_t algo = GNUTLS_DIG_SHA3_512;
45 : #if TICKET_MASTER_KEY_SIZE != 64
46 : #error "TICKET_MASTER_KEY_SIZE must be 64 bytes"
47 : #endif
48 :
49 4617 : if (unlikely(secret == NULL))
50 0 : return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
51 :
52 4617 : if ((retval = _gnutls_hash_init(&hd, hash_to_entry(algo))) < 0)
53 0 : return gnutls_assert_val(retval);
54 :
55 4617 : _gnutls_write_uint64(t, t_be);
56 :
57 4617 : if ((retval = _gnutls_hash(&hd, t_be, sizeof(t_be))) < 0)
58 0 : return gnutls_assert_val(retval);
59 9234 : if ((retval = _gnutls_hash(&hd, secret->data, secret->size)) < 0)
60 0 : return gnutls_assert_val(retval);
61 :
62 4617 : _gnutls_hash_deinit(&hd, out);
63 4617 : return GNUTLS_E_SUCCESS;
64 : }
65 :
66 8362 : static uint64_t T(gnutls_session_t session, time_t t)
67 : {
68 8362 : uint64_t numeral = t;
69 8362 : unsigned int x = session->internals.expire_time * STEK_ROTATION_PERIOD_PRODUCT;
70 :
71 8362 : if (numeral <= 0)
72 : return 0;
73 :
74 8362 : return (numeral / x);
75 : }
76 :
77 8362 : static int64_t totp_next(gnutls_session_t session)
78 : {
79 8362 : time_t t;
80 8362 : uint64_t result;
81 :
82 8362 : t = gnutls_time(NULL);
83 8362 : if (unlikely(t == (time_t) -1))
84 0 : return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
85 :
86 8362 : result = T(session, t);
87 8362 : if (result == 0)
88 : return 0;
89 :
90 8362 : if (result == session->key.totp.last_result)
91 : return 0;
92 :
93 4520 : return result;
94 : }
95 :
96 97 : static int64_t totp_previous(gnutls_session_t session)
97 : {
98 97 : uint64_t result;
99 :
100 97 : if (session->key.totp.last_result == 0)
101 0 : return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
102 97 : if (!session->key.totp.was_rotated)
103 0 : return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
104 :
105 97 : result = session->key.totp.last_result - 1;
106 97 : if (result == 0)
107 0 : return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
108 :
109 97 : return result;
110 : }
111 :
112 4520 : static void call_rotation_callback(gnutls_session_t session,
113 : uint8_t key[TICKET_MASTER_KEY_SIZE], uint64_t t)
114 : {
115 4520 : gnutls_datum_t prev_key, new_key;
116 :
117 4520 : if (session->key.totp.cb) {
118 21 : new_key.data = key;
119 21 : new_key.size = TICKET_MASTER_KEY_SIZE;
120 21 : prev_key.data = session->key.session_ticket_key;
121 21 : prev_key.size = TICKET_MASTER_KEY_SIZE;
122 :
123 21 : session->key.totp.cb(&prev_key, &new_key, t);
124 : }
125 4520 : }
126 :
127 8362 : static int rotate(gnutls_session_t session)
128 : {
129 8362 : int64_t t;
130 8362 : gnutls_datum_t secret;
131 8362 : uint8_t key[TICKET_MASTER_KEY_SIZE];
132 :
133 : /* Do we need to calculate new totp? */
134 8362 : t = totp_next(session);
135 8362 : if (t > 0) {
136 4520 : secret.data = session->key.initial_stek;
137 4520 : secret.size = TICKET_MASTER_KEY_SIZE;
138 :
139 : /* Generate next key */
140 4520 : if (totp_sha3(session, t, &secret, key) < 0) {
141 0 : gnutls_assert();
142 0 : return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
143 : }
144 :
145 : /* Replace old key with new one, and call callback if provided */
146 4520 : call_rotation_callback(session, key, t);
147 4520 : session->key.totp.last_result = t;
148 4520 : memcpy(session->key.session_ticket_key, key, sizeof(key));
149 : #ifdef HAVE_VALGRIND_MEMCHECK_H
150 : if (RUNNING_ON_VALGRIND)
151 : VALGRIND_MAKE_MEM_DEFINED(session->key.session_ticket_key,
152 : TICKET_MASTER_KEY_SIZE);
153 : #endif
154 :
155 4520 : session->key.totp.was_rotated = 1;
156 3842 : } else if (t < 0) {
157 0 : return gnutls_assert_val(t);
158 : }
159 :
160 : return GNUTLS_E_SUCCESS;
161 : }
162 :
163 97 : static int rotate_back_and_peek(gnutls_session_t session,
164 : uint8_t key[TICKET_MASTER_KEY_SIZE])
165 : {
166 97 : int64_t t;
167 97 : gnutls_datum_t secret;
168 :
169 : /* Get the previous TOTP */
170 97 : t = totp_previous(session);
171 97 : if (t < 0)
172 0 : return gnutls_assert_val(t);
173 :
174 97 : secret.data = session->key.initial_stek;
175 97 : secret.size = TICKET_MASTER_KEY_SIZE;
176 :
177 97 : if (totp_sha3(session, t, &secret, key) < 0)
178 0 : return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
179 :
180 : return 0;
181 : }
182 :
183 : /*
184 : * _gnutls_get_session_ticket_encryption_key:
185 : * @key_name: an empty datum that will receive the key name part of the STEK
186 : * @mac_key: an empty datum that will receive the MAC key part of the STEK
187 : * @enc_key: an empty datum that will receive the encryption key part of the STEK
188 : *
189 : * Get the currently active session ticket encryption key (STEK).
190 : *
191 : * The STEK is a 64-byte blob which is further divided in three parts,
192 : * and this function requires the caller to supply three separate datums for each one.
193 : * Though the caller might omit one or more of those if not interested in that part of the STEK.
194 : *
195 : * These are the three parts the STEK is divided in:
196 : *
197 : * - Key name: 16 bytes
198 : * - Encryption key: 32 bytes
199 : * - MAC key: 16 bytes
200 : *
201 : * This function will transparently rotate the key, if the time has come for that,
202 : * before returning it to the caller.
203 : */
204 7673 : int _gnutls_get_session_ticket_encryption_key(gnutls_session_t session,
205 : gnutls_datum_t *key_name,
206 : gnutls_datum_t *mac_key,
207 : gnutls_datum_t *enc_key)
208 : {
209 7673 : int retval;
210 :
211 7673 : if (unlikely(session == NULL)) {
212 0 : gnutls_assert();
213 0 : return GNUTLS_E_INTERNAL_ERROR;
214 : }
215 :
216 7673 : if ((retval = rotate(session)) < 0)
217 0 : return gnutls_assert_val(retval);
218 :
219 : /* Copy key parts to user-supplied datums (if provided) */
220 7673 : if (key_name) {
221 7673 : key_name->data = &session->key.session_ticket_key[NAME_POS];
222 7673 : key_name->size = TICKET_KEY_NAME_SIZE;
223 : }
224 7673 : if (mac_key) {
225 7673 : mac_key->data = &session->key.session_ticket_key[MAC_SECRET_POS];
226 7673 : mac_key->size = TICKET_MAC_SECRET_SIZE;
227 : }
228 7673 : if (enc_key) {
229 7673 : enc_key->data = &session->key.session_ticket_key[KEY_POS];
230 7673 : enc_key->size = TICKET_CIPHER_KEY_SIZE;
231 : }
232 :
233 : return retval;
234 : }
235 :
236 : /*
237 : * _gnutls_get_session_ticket_decryption_key:
238 : * @ticket_data: the bytes of a session ticket that must be decrypted
239 : * @key_name: an empty datum that will receive the key name part of the STEK
240 : * @mac_key: an empty datum that will receive the MAC key part of the STEK
241 : * @enc_key: an empty datum that will receive the encryption key part of the STEK
242 : *
243 : * Get the key (STEK) the given session ticket was encrypted with.
244 : *
245 : * As with its encryption counterpart (%_gnutls_get_session_ticket_encryption_key),
246 : * this function will also transparently rotate
247 : * the currently active STEK if time has come for that, and it also requires the different
248 : * parts of the STEK to be obtained in different datums.
249 : *
250 : * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or a negative error code, such as
251 : * %GNUTLS_E_REQUSTED_DATA_NOT_AVAILABLE if no key could be found for the supplied ticket.
252 : */
253 741 : int _gnutls_get_session_ticket_decryption_key(gnutls_session_t session,
254 : const gnutls_datum_t *ticket_data,
255 : gnutls_datum_t *key_name,
256 : gnutls_datum_t *mac_key,
257 : gnutls_datum_t *enc_key)
258 : {
259 741 : int retval;
260 741 : gnutls_datum_t key = {
261 741 : .data = session->key.session_ticket_key,
262 : .size = TICKET_MASTER_KEY_SIZE
263 : };
264 :
265 741 : if (unlikely(session == NULL || ticket_data == NULL || ticket_data->data == NULL))
266 0 : return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
267 :
268 741 : if (ticket_data->size < TICKET_KEY_NAME_SIZE)
269 59 : return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
270 :
271 689 : if ((retval = rotate(session)) < 0)
272 0 : return gnutls_assert_val(retval);
273 :
274 : /*
275 : * Is current key valid?
276 : * We compare the first 16 bytes --> The key_name field.
277 : */
278 689 : if (memcmp(ticket_data->data,
279 : &key.data[NAME_POS],
280 : TICKET_KEY_NAME_SIZE) == 0)
281 592 : goto key_found;
282 :
283 97 : key.size = TICKET_MASTER_KEY_SIZE;
284 97 : key.data = session->key.previous_ticket_key;
285 :
286 : /*
287 : * Current key is not valid.
288 : * Compute previous key and see if that matches.
289 : */
290 97 : if ((retval = rotate_back_and_peek(session, key.data)) < 0)
291 0 : return gnutls_assert_val(retval);
292 :
293 97 : if (memcmp(ticket_data->data,
294 : &key.data[NAME_POS],
295 : TICKET_KEY_NAME_SIZE) == 0)
296 87 : goto key_found;
297 :
298 : return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
299 :
300 679 : key_found:
301 679 : if (key_name) {
302 679 : key_name->data = &key.data[NAME_POS];
303 679 : key_name->size = TICKET_KEY_NAME_SIZE;
304 : }
305 679 : if (mac_key) {
306 679 : mac_key->data = &key.data[MAC_SECRET_POS];
307 679 : mac_key->size = TICKET_MAC_SECRET_SIZE;
308 : }
309 679 : if (enc_key) {
310 679 : enc_key->data = &key.data[KEY_POS];
311 679 : enc_key->size = TICKET_CIPHER_KEY_SIZE;
312 : }
313 :
314 : return GNUTLS_E_SUCCESS;
315 : }
316 :
317 : /*
318 : * _gnutls_initialize_session_ticket_key_rotation:
319 : * @key: Initial session ticket key
320 : *
321 : * Initialize the session ticket key rotation.
322 : *
323 : * This function will not enable session ticket keys on the server side. That is done
324 : * with the gnutls_session_ticket_enable_server() function. This function just initializes
325 : * the internal state to support periodical rotation of the session ticket encryption key.
326 : *
327 : * Returns: %GNUTLS_E_SUCCESS (0) on success, or %GNUTLS_E_INVALID_REQUEST on error.
328 : */
329 7555 : int _gnutls_initialize_session_ticket_key_rotation(gnutls_session_t session, const gnutls_datum_t *key)
330 : {
331 7555 : if (unlikely(session == NULL || key == NULL))
332 0 : return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
333 :
334 7555 : if (unlikely(session->key.totp.last_result != 0))
335 : return GNUTLS_E_INVALID_REQUEST;
336 :
337 7555 : memcpy(session->key.initial_stek, key->data, key->size);
338 :
339 7555 : session->key.totp.was_rotated = 0;
340 7555 : return 0;
341 : }
342 :
343 : /*
344 : * _gnutls_set_session_ticket_key_rotation_callback:
345 : * @cb: the callback function
346 : *
347 : * Set a callback function that will be invoked every time the session ticket key
348 : * is rotated.
349 : *
350 : * The function will take as arguments the previous key, the new key and the time
351 : * step value that caused the key to rotate.
352 : *
353 : */
354 21 : void _gnutls_set_session_ticket_key_rotation_callback(gnutls_session_t session, gnutls_stek_rotation_callback_t cb)
355 : {
356 21 : if (session)
357 21 : session->key.totp.cb = cb;
358 21 : }
|