tls-hkdf: Cleanups and refactorings
[strongswan.git] / src / libtls / tls_hkdf.c
1 /*
2 * Copyright (C) 2020 Tobias Brunner
3 * Copyright (C) 2020 Pascal Knecht
4 * Copyright (C) 2020 Méline Sieber
5 * HSR Hochschule fuer Technik Rapperswil
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
16 */
17
18 #include "tls_hkdf.h"
19
20 #include <bio/bio_writer.h>
21 #include <crypto/prf_plus.h>
22
23 typedef struct private_tls_hkdf_t private_tls_hkdf_t;
24
25 typedef enum hkdf_phase {
26 HKDF_PHASE_0,
27 HKDF_PHASE_1,
28 HKDF_PHASE_2,
29 HKDF_PHASE_3,
30 } hkdf_phase;
31
32 struct private_tls_hkdf_t {
33
34 /**
35 * Public tls_hkdf_t interface.
36 */
37 struct tls_hkdf_t public;
38
39 /**
40 * Phase we are in.
41 */
42 hkdf_phase phase;
43
44 /**
45 * Pseudorandom function used.
46 */
47 prf_t *prf;
48
49 /**
50 * Hasher used.
51 */
52 hasher_t *hasher;
53
54 /**
55 * (EC)DHE as IKM to switch from phase 1 to phase 2
56 */
57 chunk_t shared_secret;
58
59 /**
60 * PSK used.
61 */
62 chunk_t psk;
63
64 /**
65 * PRK used.
66 */
67 chunk_t prk;
68
69 /**
70 * OKM used.
71 */
72 chunk_t okm;
73
74 /**
75 * Current implementation needs a copy of derived secrets to calculate the
76 * proper finished key.
77 */
78 chunk_t client_traffic_secret;
79 chunk_t server_traffic_secret;
80 };
81
82 static char *hkdf_labels[] = {
83 "tls13 ext binder",
84 "tls13 res binder",
85 "tls13 c e traffic",
86 "tls13 e exp master",
87 "tls13 c hs traffic",
88 "tls13 s hs traffic",
89 "tls13 c ap traffic",
90 "tls13 s ap traffic",
91 "tls13 exp master",
92 "tls13 res master",
93 };
94
95 /**
96 * Step 1: Extract, as defined in RFC 5869, section 2.2:
97 * HKDF-Extract(salt, IKM) -> PRK
98 */
99 static bool extract(private_tls_hkdf_t *this, chunk_t salt, chunk_t ikm,
100 chunk_t *prk)
101 {
102 if (!this->prf->set_key(this->prf, salt))
103 {
104 DBG1(DBG_TLS, "unable to set PRF secret to salt");
105 return FALSE;
106 }
107 chunk_clear(prk);
108 if(!this->prf->allocate_bytes(this->prf, ikm, prk))
109 {
110 DBG1(DBG_TLS, "unable to allocate PRF result");
111 return FALSE;
112 }
113
114 DBG4(DBG_TLS, "PRK: %B", prk);
115
116 return TRUE;
117 }
118
119 /**
120 * Step 2: Expand as defined in RFC 5869, section 2.3:
121 * HKDF-Expand(PRK, info, L) -> OKM
122 */
123 static bool expand(private_tls_hkdf_t *this, chunk_t prk, chunk_t info,
124 size_t length, chunk_t *okm)
125 {
126 prf_plus_t *prf_plus;
127
128 if (!this->prf->set_key(this->prf, prk))
129 {
130 DBG1(DBG_TLS, "unable to set PRF secret to PRK");
131 return FALSE;
132 }
133 prf_plus = prf_plus_create(this->prf, TRUE, info);
134 chunk_clear(okm);
135 if (!prf_plus || !prf_plus->allocate_bytes(prf_plus, length, okm))
136 {
137 DBG1(DBG_TLS, "unable to allocate PRF+ result");
138 DESTROY_IF(prf_plus);
139 chunk_clear(okm);
140 return FALSE;
141 }
142 prf_plus->destroy(prf_plus);
143
144 DBG4(DBG_TLS, "OKM: %B", okm);
145
146 return TRUE;
147 }
148
149 /**
150 * Expand-Label as defined in RFC 8446, section 7.1:
151 * HKDF-Expand-Label(Secret, Label, Context, Length) -> OKM
152 */
153 static bool expand_label(private_tls_hkdf_t *this, chunk_t secret,
154 chunk_t label, chunk_t context, uint16_t length,
155 chunk_t *key)
156 {
157 bool success;
158
159 if (label.len < 7 || label.len > 255 || context.len > 255)
160 {
161 return FALSE;
162 }
163
164 /* HKDFLabel as defined in RFC 8446, section 7.1 */
165 bio_writer_t *writer = bio_writer_create(0);
166 writer->write_uint16(writer, length);
167 writer->write_data8(writer, label);
168 writer->write_data8(writer, context);
169
170 success = expand(this, secret, writer->get_buf(writer), length, key);
171 writer->destroy(writer);
172 return success;
173 }
174
175 /**
176 * Derive-Secret as defined in RFC 8446, section 7.1:
177 * Derive-Secret(Secret, Label, Message) -> OKM
178 */
179 static bool derive_secret(private_tls_hkdf_t *this, chunk_t label,
180 chunk_t messages)
181 {
182 chunk_t context;
183 bool success;
184
185 if (!this->hasher->allocate_hash(this->hasher, messages, &context))
186 {
187 return FALSE;
188 }
189
190 success = expand_label(this, this->prk, label, context,
191 this->hasher->get_hash_size(this->hasher),
192 &this->okm);
193 chunk_free(&context);
194 return success;
195 }
196
197 /**
198 * Move to phase 1 (Early Secret)
199 *
200 * 0
201 * |
202 * v
203 * PSK -> HKDF-Extract = Early Secret
204 * |
205 * +-----> Derive-Secret(., "ext binder" | "res binder", "")
206 * | = binder_key
207 * |
208 * +-----> Derive-Secret(., "c e traffic", ClientHello)
209 * | = client_early_traffic_secret
210 * |
211 * +-----> Derive-Secret(., "e exp master", ClientHello)
212 * | = early_exporter_master_secret
213 * v
214 */
215 static bool move_to_phase_1(private_tls_hkdf_t *this)
216 {
217 chunk_t salt_zero, psk = this->psk;
218
219 switch (this->phase)
220 {
221 case HKDF_PHASE_0:
222 salt_zero = chunk_alloca(this->hasher->get_hash_size(this->hasher));
223 chunk_copy_pad(salt_zero, chunk_empty, 0);
224 if (!psk.ptr)
225 {
226 psk = salt_zero;
227 }
228 if (!extract(this, salt_zero, psk, &this->prk))
229 {
230 DBG1(DBG_TLS, "unable to extract PRK");
231 return FALSE;
232 }
233 this->phase = HKDF_PHASE_1;
234 return TRUE;
235 case HKDF_PHASE_1:
236 return TRUE;
237 default:
238 DBG1(DBG_TLS, "invalid HKDF phase");
239 return FALSE;
240 }
241 }
242
243 /**
244 * Move to phase 2 (Handshake Secret)
245 *
246 * Derive-Secret(., "derived", "")
247 * |
248 * v
249 * (EC)DHE -> HKDF-Extract = Handshake Secret
250 * |
251 * +-----> Derive-Secret(., "c hs traffic",
252 * | ClientHello...ServerHello)
253 * | = client_handshake_traffic_secret
254 * |
255 * +-----> Derive-Secret(., "s hs traffic",
256 * | ClientHello...ServerHello)
257 * | = server_handshake_traffic_secret
258 * v
259 */
260 static bool move_to_phase_2(private_tls_hkdf_t *this)
261 {
262 chunk_t derived;
263
264 switch (this->phase)
265 {
266 case HKDF_PHASE_0:
267 if (!move_to_phase_1(this))
268 {
269 DBG1(DBG_TLS, "unable to move to phase 1");
270 return FALSE;
271 }
272 /* fall-through */
273 case HKDF_PHASE_1:
274 derived = chunk_from_str("tls13 derived");
275 if (!derive_secret(this, derived, chunk_empty))
276 {
277 DBG1(DBG_TLS, "unable to derive secret");
278 return FALSE;
279 }
280
281 if (!this->shared_secret.ptr)
282 {
283 DBG1(DBG_TLS, "no shared secret set");
284 return FALSE;
285 }
286
287 if (!extract(this, this->okm, this->shared_secret, &this->prk))
288 {
289 DBG1(DBG_TLS, "unable extract PRK");
290 return FALSE;
291 }
292 this->phase = HKDF_PHASE_2;
293 return TRUE;
294 case HKDF_PHASE_2:
295 return TRUE;
296 default:
297 DBG1(DBG_TLS, "invalid HKDF phase");
298 return FALSE;
299 }
300 }
301
302 /**
303 * Move to phase 3 (Master Secret)
304 *
305 * Derive-Secret(., "derived", "")
306 * |
307 * v
308 * 0 -> HKDF-Extract = Master Secret
309 * |
310 * +-----> Derive-Secret(., "c ap traffic",
311 * | ClientHello...server Finished)
312 * | = client_application_traffic_secret_0
313 * |
314 * +-----> Derive-Secret(., "s ap traffic",
315 * | ClientHello...server Finished)
316 * | = server_application_traffic_secret_0
317 * |
318 * +-----> Derive-Secret(., "exp master",
319 * | ClientHello...server Finished)
320 * | = exporter_master_secret
321 * |
322 * +-----> Derive-Secret(., "res master",
323 * ClientHello...client Finished)
324 * = resumption_master_secret
325 */
326 static bool move_to_phase_3(private_tls_hkdf_t *this)
327 {
328 chunk_t derived, ikm_zero;
329
330 switch (this->phase)
331 {
332 case HKDF_PHASE_0:
333 case HKDF_PHASE_1:
334 if (!move_to_phase_2(this))
335 {
336 DBG1(DBG_TLS, "unable to move to phase 2");
337 return FALSE;
338 }
339 /* fall-through */
340 case HKDF_PHASE_2:
341 /* prepare okm for next extract */
342 derived = chunk_from_str("tls13 derived");
343 if (!derive_secret(this, derived, chunk_empty))
344 {
345 DBG1(DBG_TLS, "unable to derive secret");
346 return FALSE;
347 }
348
349 ikm_zero = chunk_alloca(this->hasher->get_hash_size(this->hasher));
350 chunk_copy_pad(ikm_zero, chunk_empty, 0);
351 if (!extract(this, this->okm, ikm_zero, &this->prk))
352 {
353 DBG1(DBG_TLS, "unable extract PRK");
354 return FALSE;
355 }
356 this->phase = HKDF_PHASE_3;
357 return TRUE;
358 case HKDF_PHASE_3:
359 return TRUE;
360 default:
361 DBG1(DBG_TLS, "invalid HKDF phase");
362 return FALSE;
363 }
364 }
365
366 METHOD(tls_hkdf_t, set_shared_secret, void,
367 private_tls_hkdf_t *this, chunk_t shared_secret)
368 {
369 this->shared_secret = chunk_clone(shared_secret);
370 }
371
372 METHOD(tls_hkdf_t, generate_secret, bool,
373 private_tls_hkdf_t *this, tls_hkdf_label_t label, chunk_t messages,
374 chunk_t *secret)
375 {
376 switch (label)
377 {
378 case TLS_HKDF_EXT_BINDER:
379 case TLS_HKDF_RES_BINDER:
380 case TLS_HKDF_C_E_TRAFFIC:
381 case TLS_HKDF_E_EXP_MASTER:
382 if (!move_to_phase_1(this))
383 {
384 DBG1(DBG_TLS, "unable to move to phase 1");
385 return FALSE;
386 }
387 break;
388 case TLS_HKDF_C_HS_TRAFFIC:
389 case TLS_HKDF_S_HS_TRAFFIC:
390 if (!move_to_phase_2(this))
391 {
392 DBG1(DBG_TLS, "unable to move to phase 2");
393 return FALSE;
394 }
395 break;
396 case TLS_HKDF_C_AP_TRAFFIC:
397 case TLS_HKDF_S_AP_TRAFFIC:
398 case TLS_HKDF_EXP_MASTER:
399 case TLS_HKDF_RES_MASTER:
400 if (!move_to_phase_3(this))
401 {
402 DBG1(DBG_TLS, "unable to move to phase 3");
403 return FALSE;
404 }
405 break;
406 default:
407 DBG1(DBG_TLS, "invalid HKDF label");
408 return FALSE;
409 }
410
411 if (!derive_secret(this, chunk_from_str(hkdf_labels[label]), messages))
412 {
413 DBG1(DBG_TLS, "unable to derive secret");
414 return FALSE;
415 }
416
417 if (label == TLS_HKDF_C_HS_TRAFFIC || label == TLS_HKDF_C_AP_TRAFFIC)
418 {
419 chunk_clear(&this->client_traffic_secret);
420 this->client_traffic_secret = chunk_clone(this->okm);
421 }
422
423 if (label == TLS_HKDF_S_HS_TRAFFIC || label == TLS_HKDF_S_AP_TRAFFIC)
424 {
425 chunk_clear(&this->server_traffic_secret);
426 this->server_traffic_secret = chunk_clone(this->okm);
427 }
428
429 if (secret)
430 {
431 *secret = chunk_clone(this->okm);
432 }
433 return TRUE;
434 }
435
436 /**
437 * Derive keys/IVs from the current traffic secrets.
438 */
439 static bool get_shared_label_keys(private_tls_hkdf_t *this, chunk_t label,
440 bool is_server, size_t length, chunk_t *key)
441 {
442 chunk_t result = chunk_empty, secret;
443
444 secret = is_server ? this->server_traffic_secret
445 : this->client_traffic_secret;
446
447 if (!expand_label(this, secret, label, chunk_empty, length, &result))
448 {
449 DBG1(DBG_TLS, "unable to derive labeled secret");
450 chunk_clear(&result);
451 return FALSE;
452 }
453
454 if (key)
455 {
456 *key = result;
457 }
458 else
459 {
460 chunk_clear(&result);
461 }
462 return TRUE;
463 }
464
465 METHOD(tls_hkdf_t, derive_key, bool,
466 private_tls_hkdf_t *this, bool is_server, size_t length, chunk_t *key)
467 {
468 return get_shared_label_keys(this, chunk_from_str("tls13 key"), is_server,
469 length, key);
470 }
471
472 METHOD(tls_hkdf_t, derive_iv, bool,
473 private_tls_hkdf_t *this, bool is_server, size_t length, chunk_t *iv)
474 {
475 return get_shared_label_keys(this, chunk_from_str("tls13 iv"), is_server,
476 length, iv);
477 }
478
479 METHOD(tls_hkdf_t, derive_finished, bool,
480 private_tls_hkdf_t *this, bool is_server, chunk_t *finished)
481 {
482 return get_shared_label_keys(this, chunk_from_str("tls13 finished"),
483 is_server,
484 this->hasher->get_hash_size(this->hasher),
485 finished);
486 }
487
488 METHOD(tls_hkdf_t, destroy, void,
489 private_tls_hkdf_t *this)
490 {
491 chunk_clear(&this->psk);
492 chunk_clear(&this->prk);
493 chunk_clear(&this->shared_secret);
494 chunk_clear(&this->okm);
495 chunk_clear(&this->client_traffic_secret);
496 chunk_clear(&this->server_traffic_secret);
497 DESTROY_IF(this->prf);
498 DESTROY_IF(this->hasher);
499 free(this);
500 }
501
502 tls_hkdf_t *tls_hkdf_create(hash_algorithm_t hash_algorithm, chunk_t psk)
503 {
504 private_tls_hkdf_t *this;
505 pseudo_random_function_t prf_algorithm;
506
507 switch (hash_algorithm)
508 {
509 case HASH_SHA256:
510 prf_algorithm = PRF_HMAC_SHA2_256;
511 break;
512 case HASH_SHA384:
513 prf_algorithm = PRF_HMAC_SHA2_384;
514 break;
515 default:
516 DBG1(DBG_TLS, "unsupported hash algorithm %N", hash_algorithm_names,
517 hash_algorithm);
518 return NULL;
519 }
520
521 INIT(this,
522 .public = {
523 .set_shared_secret = _set_shared_secret,
524 .generate_secret = _generate_secret,
525 .derive_key = _derive_key,
526 .derive_iv = _derive_iv,
527 .derive_finished = _derive_finished,
528 .destroy = _destroy,
529 },
530 .phase = HKDF_PHASE_0,
531 .psk = psk.ptr ? chunk_clone(psk) : chunk_empty,
532 .prf = lib->crypto->create_prf(lib->crypto, prf_algorithm),
533 .hasher = lib->crypto->create_hasher(lib->crypto, hash_algorithm),
534 );
535
536 if (!this->prf || !this->hasher)
537 {
538 if (!this->prf)
539 {
540 DBG1(DBG_TLS, "%N not supported", pseudo_random_function_names,
541 prf_algorithm);
542 }
543 if (!this->hasher)
544 {
545 DBG1(DBG_TLS, "%N not supported", hash_algorithm_names,
546 hash_algorithm);
547 }
548 DBG1(DBG_TLS, "unable to initialise HKDF");
549 destroy(this);
550 return NULL;
551 }
552 return &this->public;
553 }