002d481f1e1abebb0f5a29db8b43526e4e922d12
[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 struct cached_secrets_t {
26 chunk_t client;
27 chunk_t server;
28 } cached_secrets_t;
29
30 typedef enum hkdf_phase {
31 HKDF_PHASE_0,
32 HKDF_PHASE_1,
33 HKDF_PHASE_2,
34 HKDF_PHASE_3,
35 } hkdf_phase;
36
37 struct private_tls_hkdf_t {
38
39 /**
40 * Public tls_hkdf_t interface.
41 */
42 struct tls_hkdf_t public;
43
44 /**
45 * Phase we are in.
46 */
47 hkdf_phase phase;
48
49 /**
50 * Pseudorandom function used.
51 */
52 prf_t *prf;
53
54 /**
55 * Hasher used.
56 */
57 hasher_t *hasher;
58
59 /**
60 * (EC)DHE as IKM to switch from phase 1 to phase 2
61 */
62 chunk_t shared_secret;
63
64 /**
65 * PSK used.
66 */
67 chunk_t psk;
68
69 /**
70 * PRK used.
71 */
72 chunk_t prk;
73
74 /**
75 * Handshake traffic secrets.
76 */
77 cached_secrets_t handshake_traffic_secrets;
78
79 /**
80 * Current traffic secrets.
81 */
82 cached_secrets_t traffic_secrets;
83 };
84
85 static char *hkdf_labels[] = {
86 "ext binder",
87 "res binder",
88 "c e traffic",
89 "e exp master",
90 "c hs traffic",
91 "s hs traffic",
92 "c ap traffic",
93 "s ap traffic",
94 "exp master",
95 "res master",
96 };
97
98 /**
99 * Step 1: Extract, as defined in RFC 5869, section 2.2:
100 * HKDF-Extract(salt, IKM) -> PRK
101 */
102 static bool extract(private_tls_hkdf_t *this, chunk_t salt, chunk_t ikm,
103 chunk_t *prk)
104 {
105 if (!this->prf->set_key(this->prf, salt))
106 {
107 DBG1(DBG_TLS, "unable to set PRF secret to salt");
108 return FALSE;
109 }
110 chunk_clear(prk);
111 if(!this->prf->allocate_bytes(this->prf, ikm, prk))
112 {
113 DBG1(DBG_TLS, "unable to allocate PRF result");
114 return FALSE;
115 }
116
117 DBG4(DBG_TLS, "PRK: %B", prk);
118
119 return TRUE;
120 }
121
122 /**
123 * Step 2: Expand as defined in RFC 5869, section 2.3:
124 * HKDF-Expand(PRK, info, L) -> OKM
125 */
126 static bool expand(private_tls_hkdf_t *this, chunk_t prk, chunk_t info,
127 size_t length, chunk_t *okm)
128 {
129 prf_plus_t *prf_plus;
130
131 if (!this->prf->set_key(this->prf, prk))
132 {
133 DBG1(DBG_TLS, "unable to set PRF secret to PRK");
134 return FALSE;
135 }
136 prf_plus = prf_plus_create(this->prf, TRUE, info);
137 if (!prf_plus || !prf_plus->allocate_bytes(prf_plus, length, okm))
138 {
139 DBG1(DBG_TLS, "unable to allocate PRF+ result");
140 DESTROY_IF(prf_plus);
141 return FALSE;
142 }
143 prf_plus->destroy(prf_plus);
144
145 DBG4(DBG_TLS, "OKM: %B", okm);
146
147 return TRUE;
148 }
149
150 /**
151 * Expand-Label as defined in RFC 8446, section 7.1:
152 * HKDF-Expand-Label(Secret, Label, Context, Length) -> OKM
153 */
154 static bool expand_label(private_tls_hkdf_t *this, chunk_t secret,
155 chunk_t label, chunk_t context, uint16_t length,
156 chunk_t *key)
157 {
158 bool success;
159
160 if (!label.len || label.len > 249 || context.len > 255)
161 {
162 return FALSE;
163 }
164
165 /* HKDFLabel as defined in RFC 8446, section 7.1 */
166 bio_writer_t *writer = bio_writer_create(0);
167 writer->write_uint16(writer, length);
168 label = chunk_cata("cc", chunk_from_str("tls13 "), label);
169 writer->write_data8(writer, label);
170 writer->write_data8(writer, context);
171
172 success = expand(this, secret, writer->get_buf(writer), length, key);
173 writer->destroy(writer);
174 return success;
175 }
176
177 /**
178 * Derive-Secret as defined in RFC 8446, section 7.1:
179 * Derive-Secret(Secret, Label, Message) -> OKM
180 */
181 static bool derive_secret(private_tls_hkdf_t *this, chunk_t secret,
182 chunk_t label, chunk_t messages, chunk_t *okm)
183 {
184 chunk_t context;
185 bool success;
186
187 if (!this->hasher->allocate_hash(this->hasher, messages, &context))
188 {
189 return FALSE;
190 }
191
192 success = expand_label(this, secret, label, context,
193 this->hasher->get_hash_size(this->hasher), okm);
194 chunk_free(&context);
195 return success;
196 }
197
198 /**
199 * Move to phase 1 (Early Secret)
200 *
201 * 0
202 * |
203 * v
204 * PSK -> HKDF-Extract = Early Secret
205 * |
206 * +-----> Derive-Secret(., "ext binder" | "res binder", "")
207 * | = binder_key
208 * |
209 * +-----> Derive-Secret(., "c e traffic", ClientHello)
210 * | = client_early_traffic_secret
211 * |
212 * +-----> Derive-Secret(., "e exp master", ClientHello)
213 * | = early_exporter_master_secret
214 * v
215 */
216 static bool move_to_phase_1(private_tls_hkdf_t *this)
217 {
218 chunk_t salt_zero, psk = this->psk;
219
220 switch (this->phase)
221 {
222 case HKDF_PHASE_0:
223 salt_zero = chunk_alloca(this->hasher->get_hash_size(this->hasher));
224 chunk_copy_pad(salt_zero, chunk_empty, 0);
225 if (!psk.ptr)
226 {
227 psk = salt_zero;
228 }
229 if (!extract(this, salt_zero, psk, &this->prk))
230 {
231 DBG1(DBG_TLS, "unable to extract PRK");
232 return FALSE;
233 }
234 this->phase = HKDF_PHASE_1;
235 return TRUE;
236 case HKDF_PHASE_1:
237 return TRUE;
238 default:
239 DBG1(DBG_TLS, "invalid HKDF phase");
240 return FALSE;
241 }
242 }
243
244 /**
245 * Move to phase 2 (Handshake Secret)
246 *
247 * Derive-Secret(., "derived", "")
248 * |
249 * v
250 * (EC)DHE -> HKDF-Extract = Handshake Secret
251 * |
252 * +-----> Derive-Secret(., "c hs traffic",
253 * | ClientHello...ServerHello)
254 * | = client_handshake_traffic_secret
255 * |
256 * +-----> Derive-Secret(., "s hs traffic",
257 * | ClientHello...ServerHello)
258 * | = server_handshake_traffic_secret
259 * v
260 */
261 static bool move_to_phase_2(private_tls_hkdf_t *this)
262 {
263 chunk_t okm;
264
265 switch (this->phase)
266 {
267 case HKDF_PHASE_0:
268 if (!move_to_phase_1(this))
269 {
270 DBG1(DBG_TLS, "unable to move to phase 1");
271 return FALSE;
272 }
273 /* fall-through */
274 case HKDF_PHASE_1:
275 if (!derive_secret(this, this->prk, chunk_from_str("derived"),
276 chunk_empty, &okm))
277 {
278 DBG1(DBG_TLS, "unable to derive secret");
279 return FALSE;
280 }
281
282 if (!this->shared_secret.ptr)
283 {
284 DBG1(DBG_TLS, "no shared secret set");
285 chunk_clear(&okm);
286 return FALSE;
287 }
288
289 if (!extract(this, okm, this->shared_secret, &this->prk))
290 {
291 DBG1(DBG_TLS, "unable extract PRK");
292 chunk_clear(&okm);
293 return FALSE;
294 }
295 chunk_clear(&okm);
296 this->phase = HKDF_PHASE_2;
297 return TRUE;
298 case HKDF_PHASE_2:
299 return TRUE;
300 default:
301 DBG1(DBG_TLS, "invalid HKDF phase");
302 return FALSE;
303 }
304 }
305
306 /**
307 * Move to phase 3 (Master Secret)
308 *
309 * Derive-Secret(., "derived", "")
310 * |
311 * v
312 * 0 -> HKDF-Extract = Master Secret
313 * |
314 * +-----> Derive-Secret(., "c ap traffic",
315 * | ClientHello...server Finished)
316 * | = client_application_traffic_secret_0
317 * |
318 * +-----> Derive-Secret(., "s ap traffic",
319 * | ClientHello...server Finished)
320 * | = server_application_traffic_secret_0
321 * |
322 * +-----> Derive-Secret(., "exp master",
323 * | ClientHello...server Finished)
324 * | = exporter_master_secret
325 * |
326 * +-----> Derive-Secret(., "res master",
327 * ClientHello...client Finished)
328 * = resumption_master_secret
329 */
330 static bool move_to_phase_3(private_tls_hkdf_t *this)
331 {
332 chunk_t okm, ikm_zero;
333
334 switch (this->phase)
335 {
336 case HKDF_PHASE_0:
337 case HKDF_PHASE_1:
338 if (!move_to_phase_2(this))
339 {
340 DBG1(DBG_TLS, "unable to move to phase 2");
341 return FALSE;
342 }
343 /* fall-through */
344 case HKDF_PHASE_2:
345 /* prepare okm for next extract */
346 if (!derive_secret(this, this->prk, chunk_from_str("derived"),
347 chunk_empty, &okm))
348 {
349 DBG1(DBG_TLS, "unable to derive secret");
350 return FALSE;
351 }
352
353 ikm_zero = chunk_alloca(this->hasher->get_hash_size(this->hasher));
354 chunk_copy_pad(ikm_zero, chunk_empty, 0);
355 if (!extract(this, okm, ikm_zero, &this->prk))
356 {
357 DBG1(DBG_TLS, "unable extract PRK");
358 chunk_clear(&okm);
359 return FALSE;
360 }
361 chunk_clear(&okm);
362 this->phase = HKDF_PHASE_3;
363 return TRUE;
364 case HKDF_PHASE_3:
365 return TRUE;
366 default:
367 DBG1(DBG_TLS, "invalid HKDF phase");
368 return FALSE;
369 }
370 }
371
372 METHOD(tls_hkdf_t, set_shared_secret, void,
373 private_tls_hkdf_t *this, chunk_t shared_secret)
374 {
375 this->shared_secret = chunk_clone(shared_secret);
376 }
377
378 METHOD(tls_hkdf_t, generate_secret, bool,
379 private_tls_hkdf_t *this, tls_hkdf_label_t label, chunk_t messages,
380 chunk_t *secret)
381 {
382 chunk_t okm;
383
384 switch (label)
385 {
386 case TLS_HKDF_EXT_BINDER:
387 case TLS_HKDF_RES_BINDER:
388 case TLS_HKDF_C_E_TRAFFIC:
389 case TLS_HKDF_E_EXP_MASTER:
390 if (!move_to_phase_1(this))
391 {
392 DBG1(DBG_TLS, "unable to move to phase 1");
393 return FALSE;
394 }
395 break;
396 case TLS_HKDF_C_HS_TRAFFIC:
397 case TLS_HKDF_S_HS_TRAFFIC:
398 if (!move_to_phase_2(this))
399 {
400 DBG1(DBG_TLS, "unable to move to phase 2");
401 return FALSE;
402 }
403 break;
404 case TLS_HKDF_C_AP_TRAFFIC:
405 case TLS_HKDF_S_AP_TRAFFIC:
406 case TLS_HKDF_EXP_MASTER:
407 case TLS_HKDF_RES_MASTER:
408 if (!move_to_phase_3(this))
409 {
410 DBG1(DBG_TLS, "unable to move to phase 3");
411 return FALSE;
412 }
413 break;
414 case TLS_HKDF_UPD_C_TRAFFIC:
415 case TLS_HKDF_UPD_S_TRAFFIC:
416 if (this->phase != HKDF_PHASE_3)
417 {
418 DBG1(DBG_TLS, "unable to update traffic keys");
419 return FALSE;
420 }
421 break;
422 default:
423 DBG1(DBG_TLS, "invalid HKDF label");
424 return FALSE;
425 }
426
427 if (label == TLS_HKDF_UPD_C_TRAFFIC || label == TLS_HKDF_UPD_S_TRAFFIC)
428 {
429 chunk_t previous = this->traffic_secrets.client;
430
431 if (label == TLS_HKDF_UPD_S_TRAFFIC)
432 {
433 previous = this->traffic_secrets.server;
434 }
435
436 if (!expand_label(this, previous, chunk_from_str("traffic upd"),
437 chunk_empty, this->hasher->get_hash_size(this->hasher),
438 &okm))
439 {
440 DBG1(DBG_TLS, "unable to update secret");
441 return FALSE;
442 }
443 }
444 else
445 {
446 if (!derive_secret(this, this->prk, chunk_from_str(hkdf_labels[label]),
447 messages, &okm))
448 {
449 DBG1(DBG_TLS, "unable to derive secret");
450 return FALSE;
451 }
452 }
453
454 switch (label)
455 {
456 case TLS_HKDF_C_HS_TRAFFIC:
457 chunk_clear(&this->handshake_traffic_secrets.client);
458 this->handshake_traffic_secrets.client = chunk_clone(okm);
459 /* fall-through */
460 case TLS_HKDF_C_AP_TRAFFIC:
461 case TLS_HKDF_UPD_C_TRAFFIC:
462 chunk_clear(&this->traffic_secrets.client);
463 this->traffic_secrets.client = chunk_clone(okm);
464 break;
465 case TLS_HKDF_S_HS_TRAFFIC:
466 chunk_clear(&this->handshake_traffic_secrets.server);
467 this->handshake_traffic_secrets.server = chunk_clone(okm);
468 /* fall-through */
469 case TLS_HKDF_S_AP_TRAFFIC:
470 case TLS_HKDF_UPD_S_TRAFFIC:
471 chunk_clear(&this->traffic_secrets.server);
472 this->traffic_secrets.server = chunk_clone(okm);
473 break;
474 default:
475 break;
476 }
477
478 if (secret)
479 {
480 *secret = okm;
481 }
482 else
483 {
484 chunk_clear(&okm);
485 }
486 return TRUE;
487 }
488
489 /**
490 * Derive keys/IVs from the current traffic secrets.
491 */
492 static bool get_shared_label_keys(private_tls_hkdf_t *this, chunk_t label,
493 cached_secrets_t *secrets,
494 bool server, size_t length, chunk_t *key)
495 {
496 chunk_t result = chunk_empty, secret;
497
498 secret = server ? secrets->server : secrets->client;
499
500 if (!expand_label(this, secret, label, chunk_empty, length, &result))
501 {
502 DBG1(DBG_TLS, "unable to derive labeled secret");
503 chunk_clear(&result);
504 return FALSE;
505 }
506
507 if (key)
508 {
509 *key = result;
510 }
511 else
512 {
513 chunk_clear(&result);
514 }
515 return TRUE;
516 }
517
518 METHOD(tls_hkdf_t, derive_key, bool,
519 private_tls_hkdf_t *this, bool is_server, size_t length, chunk_t *key)
520 {
521 return get_shared_label_keys(this, chunk_from_str("key"),
522 &this->traffic_secrets, is_server, length, key);
523 }
524
525 METHOD(tls_hkdf_t, derive_iv, bool,
526 private_tls_hkdf_t *this, bool is_server, size_t length, chunk_t *iv)
527 {
528 return get_shared_label_keys(this, chunk_from_str("iv"),
529 &this->traffic_secrets, is_server, length, iv);
530 }
531
532 METHOD(tls_hkdf_t, derive_finished, bool,
533 private_tls_hkdf_t *this, bool server, chunk_t *finished)
534 {
535 return get_shared_label_keys(this, chunk_from_str("finished"),
536 &this->handshake_traffic_secrets, server,
537 this->hasher->get_hash_size(this->hasher),
538 finished);
539 }
540
541 METHOD(tls_hkdf_t, export, bool,
542 private_tls_hkdf_t *this, char *label, chunk_t context,
543 chunk_t messages, size_t length, chunk_t *key)
544 {
545 chunk_t exporter_master, exporter, hash = chunk_empty;
546
547 if (this->phase != HKDF_PHASE_3)
548 {
549 DBG1(DBG_TLS, "unable to export key material");
550 return FALSE;
551 }
552
553 /**
554 * Export key material according to RFC 8446, section 7.5:
555 *
556 * TLS-Exporter(label, context_value, key_length) =
557 * HKDF-Expand-Label(Derive-Secret(Secret, label, ""),
558 * "exporter", Hash(context_value), key_length)
559 */
560 if (!generate_secret(this, TLS_HKDF_EXP_MASTER, messages, &exporter_master))
561 {
562 DBG1(DBG_TLS, "unable to derive exporter master secret");
563 return FALSE;
564 }
565
566 if (!derive_secret(this, exporter_master, chunk_from_str(label),
567 chunk_empty, &exporter))
568 {
569 DBG1(DBG_TLS, "unable to derive exporter secret");
570 chunk_clear(&exporter_master);
571 return FALSE;
572 }
573 chunk_clear(&exporter_master);
574
575 if (!this->hasher->allocate_hash(this->hasher, context, &hash) ||
576 !expand_label(this, exporter, chunk_from_str("exporter"), hash,
577 length, key))
578 {
579 DBG1(DBG_TLS, "unable to expand key material");
580 chunk_clear(&exporter);
581 chunk_free(&hash);
582 return FALSE;
583 }
584 chunk_clear(&exporter);
585 chunk_free(&hash);
586 return TRUE;
587 }
588
589 METHOD(tls_hkdf_t, allocate_bytes, bool,
590 private_tls_hkdf_t *this, chunk_t key, chunk_t seed,
591 chunk_t *out)
592 {
593 return this->prf->set_key(this->prf, key) &&
594 this->prf->allocate_bytes(this->prf, seed, out);
595 }
596
597 /**
598 * Clean up secrets
599 */
600 static void destroy_secrets(cached_secrets_t *secrets)
601 {
602 chunk_clear(&secrets->client);
603 chunk_clear(&secrets->server);
604 }
605
606 METHOD(tls_hkdf_t, destroy, void,
607 private_tls_hkdf_t *this)
608 {
609 chunk_clear(&this->psk);
610 chunk_clear(&this->prk);
611 chunk_clear(&this->shared_secret);
612 destroy_secrets(&this->handshake_traffic_secrets);
613 destroy_secrets(&this->traffic_secrets);
614 DESTROY_IF(this->prf);
615 DESTROY_IF(this->hasher);
616 free(this);
617 }
618
619 tls_hkdf_t *tls_hkdf_create(hash_algorithm_t hash_algorithm, chunk_t psk)
620 {
621 private_tls_hkdf_t *this;
622 pseudo_random_function_t prf_algorithm;
623
624 switch (hash_algorithm)
625 {
626 case HASH_SHA256:
627 prf_algorithm = PRF_HMAC_SHA2_256;
628 break;
629 case HASH_SHA384:
630 prf_algorithm = PRF_HMAC_SHA2_384;
631 break;
632 default:
633 DBG1(DBG_TLS, "unsupported hash algorithm %N", hash_algorithm_names,
634 hash_algorithm);
635 return NULL;
636 }
637
638 INIT(this,
639 .public = {
640 .set_shared_secret = _set_shared_secret,
641 .generate_secret = _generate_secret,
642 .derive_key = _derive_key,
643 .derive_iv = _derive_iv,
644 .derive_finished = _derive_finished,
645 .export = _export,
646 .allocate_bytes = _allocate_bytes,
647 .destroy = _destroy,
648 },
649 .phase = HKDF_PHASE_0,
650 .psk = psk.ptr ? chunk_clone(psk) : chunk_empty,
651 .prf = lib->crypto->create_prf(lib->crypto, prf_algorithm),
652 .hasher = lib->crypto->create_hasher(lib->crypto, hash_algorithm),
653 );
654
655 if (!this->prf || !this->hasher)
656 {
657 if (!this->prf)
658 {
659 DBG1(DBG_TLS, "%N not supported", pseudo_random_function_names,
660 prf_algorithm);
661 }
662 if (!this->hasher)
663 {
664 DBG1(DBG_TLS, "%N not supported", hash_algorithm_names,
665 hash_algorithm);
666 }
667 DBG1(DBG_TLS, "unable to initialise HKDF");
668 destroy(this);
669 return NULL;
670 }
671 return &this->public;
672 }