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