diff --git a/src/libcharon/Android.mk b/src/libcharon/Android.mk index 885b2e751..0fd8b57f3 100644 --- a/src/libcharon/Android.mk +++ b/src/libcharon/Android.mk @@ -221,9 +221,9 @@ ifneq ($(or $(call plugin_enabled, eap-tls), $(call plugin_enabled, eap-ttls), \ LOCAL_C_INCLUDES += $(LOCAL_PATH)/../libtls/ LOCAL_SRC_FILES += $(addprefix ../libtls/, \ tls_protection.c tls_compression.c tls_fragmentation.c tls_alert.c \ - tls_crypto.c tls_prf.c tls_socket.c tls_eap.c tls_cache.c tls_peer.c \ + tls_crypto.c tls_prf.c tls_hkdf.c tls_socket.c tls_eap.c tls_cache.c \ tls_aead_expl.c tls_aead_impl.c tls_aead_null.c tls_aead_seq.c tls_aead.c \ - tls_server.c tls.c \ + tls_peer.c tls_server.c tls.c \ ) endif diff --git a/src/libtls/Makefile.am b/src/libtls/Makefile.am index 2aa3b7f10..db31b8042 100644 --- a/src/libtls/Makefile.am +++ b/src/libtls/Makefile.am @@ -10,9 +10,9 @@ AM_LDFLAGS = \ ipseclib_LTLIBRARIES = libtls.la libtls_la_SOURCES = \ tls_protection.c tls_compression.c tls_fragmentation.c tls_alert.c \ - tls_crypto.c tls_prf.c tls_socket.c tls_eap.c tls_cache.c tls_peer.c \ + tls_crypto.c tls_prf.c tls_hkdf.c tls_socket.c tls_eap.c tls_cache.c \ tls_aead_expl.c tls_aead_impl.c tls_aead_null.c tls_aead_seq.c tls_aead.c \ - tls_server.c tls.c + tls_peer.c tls_server.c tls.c libtls_la_LIBADD = \ $(top_builddir)/src/libstrongswan/libstrongswan.la @@ -25,8 +25,8 @@ if USE_DEV_HEADERS tls_includedir = ${dev_headers}/tls nobase_tls_include_HEADERS = \ tls_protection.h tls_compression.h tls_fragmentation.h tls_alert.h \ - tls_crypto.h tls_prf.h tls_socket.h tls_eap.h tls_cache.h tls_peer.h \ - tls_server.h tls_handshake.h tls_application.h tls_aead.h tls.h + tls_crypto.h tls_prf.h tls_hkdf.h tls_socket.h tls_eap.h tls_cache.h \ + tls_peer.h tls_server.h tls_handshake.h tls_application.h tls_aead.h tls.h endif SUBDIRS = . tests diff --git a/src/libtls/tests/Makefile.am b/src/libtls/tests/Makefile.am index 456383f02..2220ff4a6 100644 --- a/src/libtls/tests/Makefile.am +++ b/src/libtls/tests/Makefile.am @@ -3,6 +3,7 @@ TESTS = tls_tests check_PROGRAMS = $(TESTS) tls_tests_SOURCES = \ + suites/test_hkdf.c \ suites/test_socket.c \ suites/test_suites.c \ tls_tests.h tls_tests.c diff --git a/src/libtls/tests/suites/test_hkdf.c b/src/libtls/tests/suites/test_hkdf.c new file mode 100644 index 000000000..529c6d540 --- /dev/null +++ b/src/libtls/tests/suites/test_hkdf.c @@ -0,0 +1,439 @@ +/* + * Copyright (C) 2020 Pascal Knecht + * Copyright (C) 2020 Méline Sieber + * HSR Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include + +#include "tls_hkdf.h" + +START_TEST(test_ulfheim_handshake) +{ + chunk_t handshake = chunk_from_chars( + /* Client Hello */ + 0x01, 0x00, 0x00, 0xc6, 0x03, 0x03, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0xe0, + 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, + 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0x00, + 0x06, 0x13, 0x01, 0x13, 0x02, 0x13, 0x03, 0x01, + 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x18, 0x00, + 0x16, 0x00, 0x00, 0x13, 0x65, 0x78, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x2e, 0x75, 0x6c, 0x66, 0x68, + 0x65, 0x69, 0x6d, 0x2e, 0x6e, 0x65, 0x74, 0x00, + 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x1d, 0x00, + 0x17, 0x00, 0x18, 0x00, 0x0d, 0x00, 0x14, 0x00, + 0x12, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01, 0x05, + 0x03, 0x08, 0x05, 0x05, 0x01, 0x08, 0x06, 0x06, + 0x01, 0x02, 0x01, 0x00, 0x33, 0x00, 0x26, 0x00, + 0x24, 0x00, 0x1d, 0x00, 0x20, 0x35, 0x80, 0x72, + 0xd6, 0x36, 0x58, 0x80, 0xd1, 0xae, 0xea, 0x32, + 0x9a, 0xdf, 0x91, 0x21, 0x38, 0x38, 0x51, 0xed, + 0x21, 0xa2, 0x8e, 0x3b, 0x75, 0xe9, 0x65, 0xd0, + 0xd2, 0xcd, 0x16, 0x62, 0x54, 0x00, 0x2d, 0x00, + 0x02, 0x01, 0x01, 0x00, 0x2b, 0x00, 0x03, 0x02, + 0x03, 0x04, + /* Server Hello */ + 0x02, 0x00, 0x00, 0x76, 0x03, 0x03, 0x70, 0x71, + 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, + 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x20, 0xe0, + 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, + 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0x13, + 0x01, 0x00, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x24, + 0x00, 0x1d, 0x00, 0x20, 0x9f, 0xd7, 0xad, 0x6d, + 0xcf, 0xf4, 0x29, 0x8d, 0xd3, 0xf9, 0x6d, 0x5b, + 0x1b, 0x2a, 0xf9, 0x10, 0xa0, 0x53, 0x5b, 0x14, + 0x88, 0xd7, 0xf8, 0xfa, 0xbb, 0x34, 0x9a, 0x98, + 0x28, 0x80, 0xb6, 0x15, 0x00, 0x2b, 0x00, 0x02, + 0x03, 0x04, + ); + + chunk_t ecdhe = chunk_from_chars( + 0xdf, 0x4a, 0x29, 0x1b, 0xaa, 0x1e, 0xb7, 0xcf, + 0xa6, 0x93, 0x4b, 0x29, 0xb4, 0x74, 0xba, 0xad, + 0x26, 0x97, 0xe2, 0x9f, 0x1f, 0x92, 0x0d, 0xcc, + 0x77, 0xc8, 0xa0, 0xa0, 0x88, 0x44, 0x76, 0x24, + ); + + chunk_t expected_client_handshake_traffic_secret = chunk_from_chars( + 0xff, 0x0e, 0x5b, 0x96, 0x52, 0x91, 0xc6, 0x08, + 0xc1, 0xe8, 0xcd, 0x26, 0x7e, 0xef, 0xc0, 0xaf, + 0xcc, 0x5e, 0x98, 0xa2, 0x78, 0x63, 0x73, 0xf0, + 0xdb, 0x47, 0xb0, 0x47, 0x86, 0xd7, 0x2a, 0xea, + ); + + chunk_t expected_server_handshake_traffic_secret = chunk_from_chars( + 0xa2, 0x06, 0x72, 0x65, 0xe7, 0xf0, 0x65, 0x2a, + 0x92, 0x3d, 0x5d, 0x72, 0xab, 0x04, 0x67, 0xc4, + 0x61, 0x32, 0xee, 0xb9, 0x68, 0xb6, 0xa3, 0x2d, + 0x31, 0x1c, 0x80, 0x58, 0x68, 0x54, 0x88, 0x14, + ); + + chunk_t expected_client_handshake_key = chunk_from_chars( + 0x71, 0x54, 0xf3, 0x14, 0xe6, 0xbe, 0x7d, 0xc0, + 0x08, 0xdf, 0x2c, 0x83, 0x2b, 0xaa, 0x1d, 0x39, + ); + + chunk_t expected_client_handshake_iv = chunk_from_chars( + 0x71, 0xab, 0xc2, 0xca, 0xe4, 0xc6, 0x99, 0xd4, + 0x7c, 0x60, 0x02, 0x68, + ); + + chunk_t expected_server_handshake_key = chunk_from_chars( + 0x84, 0x47, 0x80, 0xa7, 0xac, 0xad, 0x9f, 0x98, + 0x0f, 0xa2, 0x5c, 0x11, 0x4e, 0x43, 0x40, 0x2a, + ); + + chunk_t expected_server_handshake_iv = chunk_from_chars( + 0x4c, 0x04, 0x2d, 0xdc, 0x12, 0x0a, 0x38, 0xd1, + 0x41, 0x7f, 0xc8, 0x15, + ); + + chunk_t expected_client_finished_key = chunk_from_chars( + 0x7c, 0x60, 0xf8, 0xd6, 0x34, 0x6f, 0x4a, 0x96, + 0x91, 0xd2, 0xae, 0x64, 0x5a, 0x78, 0x85, 0xe0, + 0x10, 0x4a, 0xdf, 0xf9, 0x8e, 0xba, 0x98, 0x1c, + 0xa2, 0xf9, 0x9e, 0xf6, 0x2b, 0xdd, 0x8f, 0xaa, + ); + + chunk_t expected_server_finished_key = chunk_from_chars( + 0xea, 0x84, 0xab, 0xd2, 0xad, 0xa0, 0xb5, 0xc6, + 0x4c, 0x08, 0x07, 0xa3, 0x26, 0xb6, 0xfd, 0x94, + 0xa9, 0x59, 0x7e, 0x39, 0xca, 0x62, 0x10, 0x60, + 0x7c, 0x0d, 0x3c, 0x8c, 0x76, 0x68, 0x65, 0x71, + ); + + chunk_t c_secret, c_key, c_iv, s_secret, s_key, s_iv, c_finished, + s_finished; + + tls_hkdf_t *hkdf = tls_hkdf_create(HASH_SHA256, chunk_empty); + hkdf->set_shared_secret(hkdf, ecdhe); + + /* Generate client handshake traffic secret */ + ck_assert(hkdf->generate_secret(hkdf, TLS_HKDF_C_HS_TRAFFIC, handshake, + &c_secret)); + ck_assert_chunk_eq(expected_client_handshake_traffic_secret, c_secret); + + ck_assert(hkdf->derive_key(hkdf, FALSE, 16, &c_key)); + ck_assert_chunk_eq(expected_client_handshake_key, c_key); + + ck_assert(hkdf->derive_iv(hkdf, FALSE, 12, &c_iv)); + ck_assert_chunk_eq(expected_client_handshake_iv, c_iv); + + ck_assert(hkdf->derive_finished(hkdf, TRUE, &c_finished)); + ck_assert_chunk_eq(expected_client_finished_key, c_finished); + + /* Generate server handshake traffic secret */ + ck_assert(hkdf->generate_secret(hkdf, TLS_HKDF_S_HS_TRAFFIC, handshake, + &s_secret)); + ck_assert_chunk_eq(expected_server_handshake_traffic_secret, s_secret); + + ck_assert(hkdf->derive_key(hkdf, TRUE, 16, &s_key)); + ck_assert_chunk_eq(expected_server_handshake_key, s_key); + + ck_assert(hkdf->derive_iv(hkdf, TRUE, 12, &s_iv)); + ck_assert_chunk_eq(expected_server_handshake_iv, s_iv); + + ck_assert(hkdf->derive_finished(hkdf, TRUE, &s_finished)); + ck_assert_chunk_eq(expected_server_finished_key, s_finished); + + hkdf->destroy(hkdf); + + chunk_free(&c_secret); + chunk_free(&c_key); + chunk_free(&c_iv); + chunk_free(&s_secret); + chunk_free(&s_key); + chunk_free(&s_iv); + chunk_free(&c_finished); + chunk_free(&s_finished); +} +END_TEST + +START_TEST(test_ulfheim_traffic) +{ + chunk_t handshake = chunk_from_chars( + /* Client Hello */ + 0x01, 0x00, 0x00, 0xc6, 0x03, 0x03, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0xe0, + 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, + 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0x00, + 0x06, 0x13, 0x01, 0x13, 0x02, 0x13, 0x03, 0x01, + 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x18, 0x00, + 0x16, 0x00, 0x00, 0x13, 0x65, 0x78, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x2e, 0x75, 0x6c, 0x66, 0x68, + 0x65, 0x69, 0x6d, 0x2e, 0x6e, 0x65, 0x74, 0x00, + 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x1d, 0x00, + 0x17, 0x00, 0x18, 0x00, 0x0d, 0x00, 0x14, 0x00, + 0x12, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01, 0x05, + 0x03, 0x08, 0x05, 0x05, 0x01, 0x08, 0x06, 0x06, + 0x01, 0x02, 0x01, 0x00, 0x33, 0x00, 0x26, 0x00, + 0x24, 0x00, 0x1d, 0x00, 0x20, 0x35, 0x80, 0x72, + 0xd6, 0x36, 0x58, 0x80, 0xd1, 0xae, 0xea, 0x32, + 0x9a, 0xdf, 0x91, 0x21, 0x38, 0x38, 0x51, 0xed, + 0x21, 0xa2, 0x8e, 0x3b, 0x75, 0xe9, 0x65, 0xd0, + 0xd2, 0xcd, 0x16, 0x62, 0x54, 0x00, 0x2d, 0x00, + 0x02, 0x01, 0x01, 0x00, 0x2b, 0x00, 0x03, 0x02, + 0x03, 0x04, + /* Server Hello */ + 0x02, 0x00, 0x00, 0x76, 0x03, 0x03, 0x70, 0x71, + 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, + 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x20, 0xe0, + 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, + 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0x13, + 0x01, 0x00, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x24, + 0x00, 0x1d, 0x00, 0x20, 0x9f, 0xd7, 0xad, 0x6d, + 0xcf, 0xf4, 0x29, 0x8d, 0xd3, 0xf9, 0x6d, 0x5b, + 0x1b, 0x2a, 0xf9, 0x10, 0xa0, 0x53, 0x5b, 0x14, + 0x88, 0xd7, 0xf8, 0xfa, 0xbb, 0x34, 0x9a, 0x98, + 0x28, 0x80, 0xb6, 0x15, 0x00, 0x2b, 0x00, 0x02, + 0x03, 0x04, + /* Server Encrypted Extension */ + 0x08, 0x00, 0x00, 0x02, 0x00, 0x00, + /* Server Certificate */ + 0x0b, 0x00, 0x03, 0x2e, 0x00, 0x00, 0x03, 0x2a, + 0x00, 0x03, 0x25, 0x30, 0x82, 0x03, 0x21, 0x30, + 0x82, 0x02, 0x09, 0xa0, 0x03, 0x02, 0x01, 0x02, + 0x02, 0x08, 0x15, 0x5a, 0x92, 0xad, 0xc2, 0x04, + 0x8f, 0x90, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, + 0x00, 0x30, 0x22, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x0a, 0x45, 0x78, 0x61, 0x6d, 0x70, + 0x6c, 0x65, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, + 0x0d, 0x31, 0x38, 0x31, 0x30, 0x30, 0x35, 0x30, + 0x31, 0x33, 0x38, 0x31, 0x37, 0x5a, 0x17, 0x0d, + 0x31, 0x39, 0x31, 0x30, 0x30, 0x35, 0x30, 0x31, + 0x33, 0x38, 0x31, 0x37, 0x5a, 0x30, 0x2b, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x1c, 0x30, 0x1a, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x13, 0x65, + 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x75, + 0x6c, 0x66, 0x68, 0x65, 0x69, 0x6d, 0x2e, 0x6e, + 0x65, 0x74, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, + 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, + 0x01, 0x01, 0x00, 0xc4, 0x80, 0x36, 0x06, 0xba, + 0xe7, 0x47, 0x6b, 0x08, 0x94, 0x04, 0xec, 0xa7, + 0xb6, 0x91, 0x04, 0x3f, 0xf7, 0x92, 0xbc, 0x19, + 0xee, 0xfb, 0x7d, 0x74, 0xd7, 0xa8, 0x0d, 0x00, + 0x1e, 0x7b, 0x4b, 0x3a, 0x4a, 0xe6, 0x0f, 0xe8, + 0xc0, 0x71, 0xfc, 0x73, 0xe7, 0x02, 0x4c, 0x0d, + 0xbc, 0xf4, 0xbd, 0xd1, 0x1d, 0x39, 0x6b, 0xba, + 0x70, 0x46, 0x4a, 0x13, 0xe9, 0x4a, 0xf8, 0x3d, + 0xf3, 0xe1, 0x09, 0x59, 0x54, 0x7b, 0xc9, 0x55, + 0xfb, 0x41, 0x2d, 0xa3, 0x76, 0x52, 0x11, 0xe1, + 0xf3, 0xdc, 0x77, 0x6c, 0xaa, 0x53, 0x37, 0x6e, + 0xca, 0x3a, 0xec, 0xbe, 0xc3, 0xaa, 0xb7, 0x3b, + 0x31, 0xd5, 0x6c, 0xb6, 0x52, 0x9c, 0x80, 0x98, + 0xbc, 0xc9, 0xe0, 0x28, 0x18, 0xe2, 0x0b, 0xf7, + 0xf8, 0xa0, 0x3a, 0xfd, 0x17, 0x04, 0x50, 0x9e, + 0xce, 0x79, 0xbd, 0x9f, 0x39, 0xf1, 0xea, 0x69, + 0xec, 0x47, 0x97, 0x2e, 0x83, 0x0f, 0xb5, 0xca, + 0x95, 0xde, 0x95, 0xa1, 0xe6, 0x04, 0x22, 0xd5, + 0xee, 0xbe, 0x52, 0x79, 0x54, 0xa1, 0xe7, 0xbf, + 0x8a, 0x86, 0xf6, 0x46, 0x6d, 0x0d, 0x9f, 0x16, + 0x95, 0x1a, 0x4c, 0xf7, 0xa0, 0x46, 0x92, 0x59, + 0x5c, 0x13, 0x52, 0xf2, 0x54, 0x9e, 0x5a, 0xfb, + 0x4e, 0xbf, 0xd7, 0x7a, 0x37, 0x95, 0x01, 0x44, + 0xe4, 0xc0, 0x26, 0x87, 0x4c, 0x65, 0x3e, 0x40, + 0x7d, 0x7d, 0x23, 0x07, 0x44, 0x01, 0xf4, 0x84, + 0xff, 0xd0, 0x8f, 0x7a, 0x1f, 0xa0, 0x52, 0x10, + 0xd1, 0xf4, 0xf0, 0xd5, 0xce, 0x79, 0x70, 0x29, + 0x32, 0xe2, 0xca, 0xbe, 0x70, 0x1f, 0xdf, 0xad, + 0x6b, 0x4b, 0xb7, 0x11, 0x01, 0xf4, 0x4b, 0xad, + 0x66, 0x6a, 0x11, 0x13, 0x0f, 0xe2, 0xee, 0x82, + 0x9e, 0x4d, 0x02, 0x9d, 0xc9, 0x1c, 0xdd, 0x67, + 0x16, 0xdb, 0xb9, 0x06, 0x18, 0x86, 0xed, 0xc1, + 0xba, 0x94, 0x21, 0x02, 0x03, 0x01, 0x00, 0x01, + 0xa3, 0x52, 0x30, 0x50, 0x30, 0x0e, 0x06, 0x03, + 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, + 0x03, 0x02, 0x05, 0xa0, 0x30, 0x1d, 0x06, 0x03, + 0x55, 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, + 0x02, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x03, 0x01, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, + 0x89, 0x4f, 0xde, 0x5b, 0xcc, 0x69, 0xe2, 0x52, + 0xcf, 0x3e, 0xa3, 0x00, 0xdf, 0xb1, 0x97, 0xb8, + 0x1d, 0xe1, 0xc1, 0x46, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, + 0x59, 0x16, 0x45, 0xa6, 0x9a, 0x2e, 0x37, 0x79, + 0xe4, 0xf6, 0xdd, 0x27, 0x1a, 0xba, 0x1c, 0x0b, + 0xfd, 0x6c, 0xd7, 0x55, 0x99, 0xb5, 0xe7, 0xc3, + 0x6e, 0x53, 0x3e, 0xff, 0x36, 0x59, 0x08, 0x43, + 0x24, 0xc9, 0xe7, 0xa5, 0x04, 0x07, 0x9d, 0x39, + 0xe0, 0xd4, 0x29, 0x87, 0xff, 0xe3, 0xeb, 0xdd, + 0x09, 0xc1, 0xcf, 0x1d, 0x91, 0x44, 0x55, 0x87, + 0x0b, 0x57, 0x1d, 0xd1, 0x9b, 0xdf, 0x1d, 0x24, + 0xf8, 0xbb, 0x9a, 0x11, 0xfe, 0x80, 0xfd, 0x59, + 0x2b, 0xa0, 0x39, 0x8c, 0xde, 0x11, 0xe2, 0x65, + 0x1e, 0x61, 0x8c, 0xe5, 0x98, 0xfa, 0x96, 0xe5, + 0x37, 0x2e, 0xef, 0x3d, 0x24, 0x8a, 0xfd, 0xe1, + 0x74, 0x63, 0xeb, 0xbf, 0xab, 0xb8, 0xe4, 0xd1, + 0xab, 0x50, 0x2a, 0x54, 0xec, 0x00, 0x64, 0xe9, + 0x2f, 0x78, 0x19, 0x66, 0x0d, 0x3f, 0x27, 0xcf, + 0x20, 0x9e, 0x66, 0x7f, 0xce, 0x5a, 0xe2, 0xe4, + 0xac, 0x99, 0xc7, 0xc9, 0x38, 0x18, 0xf8, 0xb2, + 0x51, 0x07, 0x22, 0xdf, 0xed, 0x97, 0xf3, 0x2e, + 0x3e, 0x93, 0x49, 0xd4, 0xc6, 0x6c, 0x9e, 0xa6, + 0x39, 0x6d, 0x74, 0x44, 0x62, 0xa0, 0x6b, 0x42, + 0xc6, 0xd5, 0xba, 0x68, 0x8e, 0xac, 0x3a, 0x01, + 0x7b, 0xdd, 0xfc, 0x8e, 0x2c, 0xfc, 0xad, 0x27, + 0xcb, 0x69, 0xd3, 0xcc, 0xdc, 0xa2, 0x80, 0x41, + 0x44, 0x65, 0xd3, 0xae, 0x34, 0x8c, 0xe0, 0xf3, + 0x4a, 0xb2, 0xfb, 0x9c, 0x61, 0x83, 0x71, 0x31, + 0x2b, 0x19, 0x10, 0x41, 0x64, 0x1c, 0x23, 0x7f, + 0x11, 0xa5, 0xd6, 0x5c, 0x84, 0x4f, 0x04, 0x04, + 0x84, 0x99, 0x38, 0x71, 0x2b, 0x95, 0x9e, 0xd6, + 0x85, 0xbc, 0x5c, 0x5d, 0xd6, 0x45, 0xed, 0x19, + 0x90, 0x94, 0x73, 0x40, 0x29, 0x26, 0xdc, 0xb4, + 0x0e, 0x34, 0x69, 0xa1, 0x59, 0x41, 0xe8, 0xe2, + 0xcc, 0xa8, 0x4b, 0xb6, 0x08, 0x46, 0x36, 0xa0, + 0x00, 0x00, + /* Server Certificate Verify */ + 0x0f, 0x00, 0x01, 0x04, 0x08, 0x04, 0x01, 0x00, + 0x17, 0xfe, 0xb5, 0x33, 0xca, 0x6d, 0x00, 0x7d, + 0x00, 0x58, 0x25, 0x79, 0x68, 0x42, 0x4b, 0xbc, + 0x3a, 0xa6, 0x90, 0x9e, 0x9d, 0x49, 0x55, 0x75, + 0x76, 0xa5, 0x20, 0xe0, 0x4a, 0x5e, 0xf0, 0x5f, + 0x0e, 0x86, 0xd2, 0x4f, 0xf4, 0x3f, 0x8e, 0xb8, + 0x61, 0xee, 0xf5, 0x95, 0x22, 0x8d, 0x70, 0x32, + 0xaa, 0x36, 0x0f, 0x71, 0x4e, 0x66, 0x74, 0x13, + 0x92, 0x6e, 0xf4, 0xf8, 0xb5, 0x80, 0x3b, 0x69, + 0xe3, 0x55, 0x19, 0xe3, 0xb2, 0x3f, 0x43, 0x73, + 0xdf, 0xac, 0x67, 0x87, 0x06, 0x6d, 0xcb, 0x47, + 0x56, 0xb5, 0x45, 0x60, 0xe0, 0x88, 0x6e, 0x9b, + 0x96, 0x2c, 0x4a, 0xd2, 0x8d, 0xab, 0x26, 0xba, + 0xd1, 0xab, 0xc2, 0x59, 0x16, 0xb0, 0x9a, 0xf2, + 0x86, 0x53, 0x7f, 0x68, 0x4f, 0x80, 0x8a, 0xef, + 0xee, 0x73, 0x04, 0x6c, 0xb7, 0xdf, 0x0a, 0x84, + 0xfb, 0xb5, 0x96, 0x7a, 0xca, 0x13, 0x1f, 0x4b, + 0x1c, 0xf3, 0x89, 0x79, 0x94, 0x03, 0xa3, 0x0c, + 0x02, 0xd2, 0x9c, 0xbd, 0xad, 0xb7, 0x25, 0x12, + 0xdb, 0x9c, 0xec, 0x2e, 0x5e, 0x1d, 0x00, 0xe5, + 0x0c, 0xaf, 0xcf, 0x6f, 0x21, 0x09, 0x1e, 0xbc, + 0x4f, 0x25, 0x3c, 0x5e, 0xab, 0x01, 0xa6, 0x79, + 0xba, 0xea, 0xbe, 0xed, 0xb9, 0xc9, 0x61, 0x8f, + 0x66, 0x00, 0x6b, 0x82, 0x44, 0xd6, 0x62, 0x2a, + 0xaa, 0x56, 0x88, 0x7c, 0xcf, 0xc6, 0x6a, 0x0f, + 0x38, 0x51, 0xdf, 0xa1, 0x3a, 0x78, 0xcf, 0xf7, + 0x99, 0x1e, 0x03, 0xcb, 0x2c, 0x3a, 0x0e, 0xd8, + 0x7d, 0x73, 0x67, 0x36, 0x2e, 0xb7, 0x80, 0x5b, + 0x00, 0xb2, 0x52, 0x4f, 0xf2, 0x98, 0xa4, 0xda, + 0x48, 0x7c, 0xac, 0xde, 0xaf, 0x8a, 0x23, 0x36, + 0xc5, 0x63, 0x1b, 0x3e, 0xfa, 0x93, 0x5b, 0xb4, + 0x11, 0xe7, 0x53, 0xca, 0x13, 0xb0, 0x15, 0xfe, + 0xc7, 0xe4, 0xa7, 0x30, 0xf1, 0x36, 0x9f, 0x9e, + /* Server Handshake Finish */ + 0x14, 0x00, 0x00, 0x20, 0xea, 0x6e, 0xe1, 0x76, + 0xdc, 0xcc, 0x4a, 0xf1, 0x85, 0x9e, 0x9e, 0x4e, + 0x93, 0xf7, 0x97, 0xea, 0xc9, 0xa7, 0x8c, 0xe4, + 0x39, 0x30, 0x1e, 0x35, 0x27, 0x5a, 0xd4, 0x3f, + 0x3c, 0xdd, 0xbd, 0xe3, + ); + + chunk_t ecdhe = chunk_from_chars( + 0xdf, 0x4a, 0x29, 0x1b, 0xaa, 0x1e, 0xb7, 0xcf, + 0xa6, 0x93, 0x4b, 0x29, 0xb4, 0x74, 0xba, 0xad, + 0x26, 0x97, 0xe2, 0x9f, 0x1f, 0x92, 0x0d, 0xcc, + 0x77, 0xc8, 0xa0, 0xa0, 0x88, 0x44, 0x76, 0x24, + ); + + chunk_t expected_client_application_key = chunk_from_chars( + 0x49, 0x13, 0x4b, 0x95, 0x32, 0x8f, 0x27, 0x9f, + 0x01, 0x83, 0x86, 0x05, 0x89, 0xac, 0x67, 0x07, + ); + + chunk_t expected_client_application_iv = chunk_from_chars( + 0xbc, 0x4d, 0xd5, 0xf7, 0xb9, 0x8a, 0xcf, 0xf8, + 0x54, 0x66, 0x26, 0x1d, + ); + + chunk_t expected_server_application_key = chunk_from_chars( + 0x0b, 0x6d, 0x22, 0xc8, 0xff, 0x68, 0x09, 0x7e, + 0xa8, 0x71, 0xc6, 0x72, 0x07, 0x37, 0x73, 0xbf, + ); + + chunk_t expected_server_application_iv = chunk_from_chars( + 0x1b, 0x13, 0xdd, 0x9f, 0x8d, 0x8f, 0x17, 0x09, + 0x1d, 0x34, 0xb3, 0x49, + ); + + chunk_t c_key, c_iv, s_key, s_iv; + + tls_hkdf_t *hkdf = tls_hkdf_create(HASH_SHA256, chunk_empty); + hkdf->set_shared_secret(hkdf, ecdhe); + + /* Generate client application traffic secret */ + ck_assert(hkdf->generate_secret(hkdf, TLS_HKDF_C_AP_TRAFFIC, handshake, NULL)); + + ck_assert(hkdf->derive_key(hkdf, FALSE, 16, &c_key)); + ck_assert_chunk_eq(expected_client_application_key, c_key); + + ck_assert(hkdf->derive_iv(hkdf, FALSE, 12, &c_iv)); + ck_assert_chunk_eq(expected_client_application_iv, c_iv); + + /* Generate server application traffic secret */ + ck_assert(hkdf->generate_secret(hkdf, TLS_HKDF_S_AP_TRAFFIC, handshake, NULL)); + + ck_assert(hkdf->derive_key(hkdf, TRUE, 16, &s_key)); + ck_assert_chunk_eq(expected_server_application_key, s_key); + + ck_assert(hkdf->derive_iv(hkdf, TRUE, 12, &s_iv)); + ck_assert_chunk_eq(expected_server_application_iv, s_iv); + + hkdf->destroy(hkdf); + + chunk_free(&c_key); + chunk_free(&c_iv); + chunk_free(&s_key); + chunk_free(&s_iv); +} +END_TEST + +Suite *hkdf_suite_create() +{ + Suite *s; + TCase *tc; + + s = suite_create("HKDF TLS 1.3"); + + tc = tcase_create("Ulfheim Keys"); + tcase_add_test(tc, test_ulfheim_handshake); + tcase_add_test(tc, test_ulfheim_traffic); + suite_add_tcase(s, tc); + + return s; +} diff --git a/src/libtls/tests/tls_tests.h b/src/libtls/tests/tls_tests.h index 489b2ddb1..b0d0d57a5 100644 --- a/src/libtls/tests/tls_tests.h +++ b/src/libtls/tests/tls_tests.h @@ -13,5 +13,6 @@ * for more details. */ +TEST_SUITE(hkdf_suite_create) TEST_SUITE(socket_suite_create) TEST_SUITE(suites_suite_create) diff --git a/src/libtls/tls_hkdf.c b/src/libtls/tls_hkdf.c new file mode 100644 index 000000000..d0c44f558 --- /dev/null +++ b/src/libtls/tls_hkdf.c @@ -0,0 +1,511 @@ +/* + * Copyright (C) 2020 Pascal Knecht + * Copyright (C) 2020 Méline Sieber + * HSR Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "tls_hkdf.h" + +#include +#include + +typedef struct private_tls_hkdf_t private_tls_hkdf_t; + +typedef enum hkdf_phase { + HKDF_PHASE_0, + HKDF_PHASE_1, + HKDF_PHASE_2, + HKDF_PHASE_3, +} hkdf_phase; + +struct private_tls_hkdf_t { + + /** + * Public tls_hkdf_t interface. + */ + struct tls_hkdf_t public; + + /** + * Phase we are in. + */ + hkdf_phase phase; + + /** + * Hash algorithm used. + */ + hash_algorithm_t hash_algorithm; + + /** + * Pseudorandom function used. + */ + prf_t *prf; + + /** + * Hasher used. + */ + hasher_t *hasher; + + /** + * (EC)DHE as IKM to switch from phase 1 to phase 2 + */ + chunk_t shared_secret; + + /** + * IKM used. + */ + chunk_t ikm; + + /** + * PRK used. + */ + chunk_t prk; + + /** + * OKM used. + */ + chunk_t okm; + + /** + * Current implementation needs a copy of derived secrets to calculate the + * proper finished key. + */ + chunk_t client_traffic_secret; + chunk_t server_traffic_secret; +}; + +static char *hkdf_labels[] = { + "tls13 ext binder", + "tls13 res binder", + "tls13 c e traffic", + "tls13 e exp master", + "tls13 c hs traffic", + "tls13 s hs traffic", + "tls13 c ap traffic", + "tls13 s ap traffic", + "tls13 exp master", + "tls13 res master", +}; + +/** + * Step 1: Extract, as defined in RFC 5869, section 2.2: + * HKDF-Extract(salt, IKM) -> PRK + */ +static bool extract(private_tls_hkdf_t *this, chunk_t salt, chunk_t ikm, + chunk_t *prk) +{ + if (!this->prf->set_key(this->prf, salt)) + { + DBG1(DBG_TLS, "unable to set PRF salt"); + return FALSE; + } + chunk_clear(prk); + if(!this->prf->allocate_bytes(this->prf, ikm, prk)) + { + DBG1(DBG_TLS, "unable to allocate PRF space"); + return FALSE; + } + + DBG4(DBG_TLS, "PRK: %B", prk); + + return TRUE; +} + +/** + * Step 2: Expand as defined in RFC 5869, section 2.3: + * HKDF-Expand(PRK, info, L) -> OKM + */ +static bool expand(private_tls_hkdf_t *this, chunk_t prk, chunk_t info, + size_t length, chunk_t *okm) +{ + if (!this->prf->set_key(this->prf, prk)) + { + DBG1(DBG_TLS, "unable to set PRF PRK"); + return FALSE; + } + prf_plus_t *prf_plus = prf_plus_create(this->prf, TRUE, info); + chunk_clear(&this->okm); + if (!prf_plus || !prf_plus->allocate_bytes(prf_plus, length, okm)) + { + DBG1(DBG_TLS, "unable to allocate PRF plus space"); + DESTROY_IF(prf_plus); + chunk_clear(okm); + return FALSE; + } + DESTROY_IF(prf_plus); + + DBG4(DBG_TLS, "OKM: %B", okm); + + return TRUE; +} + +/** + * Expand-Label as defined in RFC 8446, section 7.1: + * HKDF-Expand-Label(Secret, Label, Context, Length) -> OKM + */ +static bool expand_label(private_tls_hkdf_t *this, chunk_t secret, + chunk_t label, chunk_t context, uint16_t length, + chunk_t *key) +{ + bool success; + + if (label.len < 7 || label.len > 255 || context.len > 255) + { + return FALSE; + } + + /* HKDFLabel as defined in RFC 8446, section 7.1 */ + bio_writer_t *writer = bio_writer_create(0); + writer->write_uint16(writer, length); + writer->write_data8(writer, label); + writer->write_data8(writer, context); + + success = expand(this, secret, writer->get_buf(writer), length, key); + writer->destroy(writer); + return success; +} + +/** + * Derive-Secret as defined in RFC 8446, section 7.1: + * Derive-Secret(Secret, Label, Message) -> OKM + */ +static bool derive_secret(private_tls_hkdf_t *this, chunk_t label, + chunk_t messages) +{ + bool success; + chunk_t context; + + if (!this->hasher || + !this->hasher->allocate_hash(this->hasher, messages, &context)) + { + DBG1(DBG_TLS, "%N not supported", hash_algorithm_names, + this->hash_algorithm); + return FALSE; + } + + success = expand_label(this, this->prk, label, context, + this->hasher->get_hash_size(this->hasher), + &this->okm); + chunk_free(&context); + return success; +} + +static bool move_to_phase_1(private_tls_hkdf_t *this) +{ + chunk_t salt_zero; + + switch (this->phase) + { + case HKDF_PHASE_0: + salt_zero = chunk_alloca(this->hasher->get_hash_size(this->hasher)); + chunk_copy_pad(salt_zero, chunk_empty, 0); + if (!extract(this, salt_zero, this->ikm, &this->prk)) + { + DBG1(DBG_TLS, "unable to extract PRK"); + return FALSE; + } + this->phase = HKDF_PHASE_1; + return TRUE; + case HKDF_PHASE_1: + return TRUE; + default: + DBG1(DBG_TLS, "invalid HKDF phase"); + return FALSE; + } +} + +static bool move_to_phase_2(private_tls_hkdf_t *this) +{ + chunk_t derived; + + switch (this->phase) + { + case HKDF_PHASE_0: + if (!move_to_phase_1(this)) + { + DBG1(DBG_TLS, "unable to move to phase 1"); + return FALSE; + } + /* fall-through */ + case HKDF_PHASE_1: + derived = chunk_from_str("tls13 derived"); + if (!derive_secret(this, derived, chunk_empty)) + { + DBG1(DBG_TLS, "unable to derive secret"); + return FALSE; + } + + if (!this->shared_secret.ptr) + { + DBG1(DBG_TLS, "no shared secret set"); + return FALSE; + } + else + { + chunk_clear(&this->ikm); + this->ikm = chunk_clone(this->shared_secret); + } + + if (!extract(this, this->okm, this->ikm, &this->prk)) + { + DBG1(DBG_TLS, "unable extract PRK"); + return FALSE; + } + this->phase = HKDF_PHASE_2; + return TRUE; + case HKDF_PHASE_2: + return TRUE; + default: + DBG1(DBG_TLS, "invalid HKDF phase"); + return FALSE; + } +} + +static bool move_to_phase_3(private_tls_hkdf_t *this) +{ + chunk_t derived, ikm_zero; + + switch (this->phase) + { + case HKDF_PHASE_0: + case HKDF_PHASE_1: + if (!move_to_phase_2(this)) + { + DBG1(DBG_TLS, "unable to move to phase 2"); + return FALSE; + } + /* fall-through */ + case HKDF_PHASE_2: + /* prepare okm for next extract */ + derived = chunk_from_str("tls13 derived"); + if (!derive_secret(this, derived, chunk_empty)) + { + DBG1(DBG_TLS, "unable to derive secret"); + return FALSE; + } + + ikm_zero = chunk_alloca(this->hasher->get_hash_size(this->hasher)); + chunk_copy_pad(ikm_zero, chunk_empty, 0); + if (!extract(this, this->okm, ikm_zero, &this->prk)) + { + DBG1(DBG_TLS, "unable extract PRK"); + return FALSE; + } + this->phase = HKDF_PHASE_3; + return TRUE; + case HKDF_PHASE_3: + return TRUE; + default: + DBG1(DBG_TLS, "invalid HKDF phase"); + return FALSE; + } +} + +static void return_secret(private_tls_hkdf_t *this, chunk_t key, + chunk_t *secret) +{ + *secret = chunk_alloc(key.len); + chunk_copy_pad(*secret, key, 0); +} + +static bool get_shared_label_keys(private_tls_hkdf_t *this, chunk_t label, + bool is_server, size_t length, chunk_t *key) +{ + chunk_t result, secret; + + if (is_server) + { + secret = chunk_clone(this->server_traffic_secret); + } + else + { + secret = chunk_clone(this->client_traffic_secret); + } + + if (!expand_label(this, secret, label, chunk_empty, length, &result)) + { + DBG1(DBG_TLS, "unable to derive secret"); + chunk_clear(&secret); + chunk_clear(&result); + return FALSE; + } + + if (key) + { + return_secret(this, result, key); + } + + chunk_clear(&secret); + chunk_clear(&result); + return TRUE; +} + +METHOD(tls_hkdf_t, set_shared_secret, void, + private_tls_hkdf_t *this, chunk_t shared_secret) +{ + this->shared_secret = chunk_clone(shared_secret); +} + +METHOD(tls_hkdf_t, generate_secret, bool, + private_tls_hkdf_t *this, enum tls_hkdf_labels_t label, chunk_t messages, + chunk_t *secret) +{ + switch (label) + { + case TLS_HKDF_EXT_BINDER: + case TLS_HKDF_RES_BINDER: + case TLS_HKDF_C_E_TRAFFIC: + case TLS_HKDF_E_EXP_MASTER: + if (!move_to_phase_1(this)) + { + DBG1(DBG_TLS, "unable to move to phase 1"); + return FALSE; + } + break; + case TLS_HKDF_C_HS_TRAFFIC: + case TLS_HKDF_S_HS_TRAFFIC: + if (!move_to_phase_2(this)) + { + DBG1(DBG_TLS, "unable to move to phase 2"); + return FALSE; + } + break; + case TLS_HKDF_C_AP_TRAFFIC: + case TLS_HKDF_S_AP_TRAFFIC: + case TLS_HKDF_EXP_MASTER: + case TLS_HKDF_RES_MASTER: + if (!move_to_phase_3(this)) + { + DBG1(DBG_TLS, "unable to move to phase 3"); + return FALSE; + } + break; + default: + DBG1(DBG_TLS, "invalid HKDF label"); + return FALSE; + } + + if (!derive_secret(this, chunk_from_str(hkdf_labels[label]), messages)) + { + DBG1(DBG_TLS, "unable to derive secret"); + return FALSE; + } + + if (label == TLS_HKDF_C_HS_TRAFFIC || label == TLS_HKDF_C_AP_TRAFFIC) + { + chunk_clear(&this->client_traffic_secret); + this->client_traffic_secret = chunk_clone(this->okm); + } + + if (label == TLS_HKDF_S_HS_TRAFFIC || label == TLS_HKDF_S_AP_TRAFFIC) + { + chunk_clear(&this->server_traffic_secret); + this->server_traffic_secret = chunk_clone(this->okm); + } + + if (secret) + { + return_secret(this, this->okm, secret); + } + + return TRUE; +} + +METHOD(tls_hkdf_t, derive_key, bool, + private_tls_hkdf_t *this, bool is_server, size_t length, chunk_t *key) +{ + return get_shared_label_keys(this, chunk_from_str("tls13 key"), is_server, + length, key); +} + +METHOD(tls_hkdf_t, derive_iv, bool, + private_tls_hkdf_t *this, bool is_server, size_t length, chunk_t *iv) +{ + return get_shared_label_keys(this, chunk_from_str("tls13 iv"), is_server, + length, iv); +} + +METHOD(tls_hkdf_t, derive_finished, bool, + private_tls_hkdf_t *this, bool is_server, chunk_t *finished) +{ + return get_shared_label_keys(this, chunk_from_str("tls13 finished"), + is_server, + this->hasher->get_hash_size(this->hasher), + finished); +} + +METHOD(tls_hkdf_t, destroy, void, + private_tls_hkdf_t *this) +{ + chunk_free(&this->ikm); + chunk_clear(&this->prk); + chunk_clear(&this->shared_secret); + chunk_clear(&this->okm); + chunk_clear(&this->client_traffic_secret); + chunk_clear(&this->server_traffic_secret); + DESTROY_IF(this->prf); + DESTROY_IF(this->hasher); + free(this); +} + +tls_hkdf_t *tls_hkdf_create(hash_algorithm_t hash_algorithm, chunk_t psk) +{ + private_tls_hkdf_t *this; + pseudo_random_function_t prf_algorithm; + + switch (hash_algorithm) + { + case HASH_SHA256: + prf_algorithm = PRF_HMAC_SHA2_256; + break; + case HASH_SHA384: + prf_algorithm = PRF_HMAC_SHA2_384; + break; + default: + DBG1(DBG_TLS, "not supported hash algorithm"); + return NULL; + } + + INIT(this, + .public = { + .set_shared_secret = _set_shared_secret, + .generate_secret = _generate_secret, + .derive_key = _derive_key, + .derive_iv = _derive_iv, + .derive_finished = _derive_finished, + .destroy = _destroy, + }, + .phase = HKDF_PHASE_0, + .hash_algorithm = hash_algorithm, + .prf = lib->crypto->create_prf(lib->crypto, prf_algorithm), + .hasher = lib->crypto->create_hasher(lib->crypto, hash_algorithm), + ); + + if (!psk.ptr) + { + this->ikm = chunk_alloc(this->hasher->get_hash_size(this->hasher)); + chunk_copy_pad(this->ikm, chunk_empty, 0); + } + else + { + this->ikm = chunk_clone(psk); + } + + if (!this->prf || !this->hasher) + { + DBG1(DBG_TLS, "unable to initialise HKDF"); + destroy(this); + return NULL; + } + + return &this->public; +} diff --git a/src/libtls/tls_hkdf.h b/src/libtls/tls_hkdf.h new file mode 100644 index 000000000..a820b0298 --- /dev/null +++ b/src/libtls/tls_hkdf.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2020 Pascal Knecht + * Copyright (C) 2020 Méline Sieber + * HSR Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup tls_hkdf tls_hkdf + * @{ @ingroup libtls + */ + +#ifndef TLS_HKDF_H_ +#define TLS_HKDF_H_ + +#include +#include + +typedef struct tls_hkdf_t tls_hkdf_t; + +/** + * TLS HKDF labels + */ +enum tls_hkdf_labels_t { + TLS_HKDF_EXT_BINDER, + TLS_HKDF_RES_BINDER, + TLS_HKDF_C_E_TRAFFIC, + TLS_HKDF_E_EXP_MASTER, + TLS_HKDF_C_HS_TRAFFIC, + TLS_HKDF_S_HS_TRAFFIC, + TLS_HKDF_C_AP_TRAFFIC, + TLS_HKDF_S_AP_TRAFFIC, + TLS_HKDF_EXP_MASTER, + TLS_HKDF_RES_MASTER, +}; + +/** + * TLS HKDF helper functions. + */ +struct tls_hkdf_t { + + /** + * Set the (EC)DHE shared secret of this connection. + * + * @param shared_secret input key material to use + */ + void (*set_shared_secret)(tls_hkdf_t *this, chunk_t shared_secret); + + /** + * Allocate secret of the requested label. + * + * Space for returned secret is allocated and must be freed by the caller. + * + * @param label HKDF label of requested secret + * @param messages handshake messages + * @param secret secret will be written into this chunk, if used + * @return TRUE if secrets derived successfully + */ + bool (*generate_secret)(tls_hkdf_t *this, enum tls_hkdf_labels_t label, + chunk_t messages, chunk_t *secret); + + /** + * Allocate traffic encryption key bytes. + * + * Key used to encrypt traffic data as defined in RFC 8446, section 7.3. + * Space for returned secret is allocated and must be freed by the caller. + * + * @param is_server TRUE if server, FALSE if client derives secret + * @param length key length, in bytes + * @param key secret will be written into this chunk + * @return TRUE if secrets derived successfully + */ + bool (*derive_key)(tls_hkdf_t *this, bool is_server, size_t length, + chunk_t *key); + + /** + * Allocate traffic IV bytes. + * + * IV used to encrypt traffic data as defined in RFC 8446, section 7.3. + * Space for returned secret is allocated and must be freed by the caller. + * + * @param is_server TRUE if server, FALSE if client derives secret + * @param length key length, in bytes + * @param iv IV will be written into this chunk + * @return TRUE if secrets derived successfully + */ + bool (*derive_iv)(tls_hkdf_t *this, bool is_server, size_t length, + chunk_t *iv); + + /** + * Allocate finished key bytes. + * + * Key used to compute Finished messages as defined in RFC 8446, + * section 4.4.4. Space for returned secret is allocated and must be freed + * by the caller. + * + * @param is_server TRUE if server, FALSE if client derives secret + * @param finished key will be written into this chunk + * @return TRUE if secrets derived successfully + */ + bool (*derive_finished)(tls_hkdf_t *this, bool is_server, + chunk_t *finished); + + /** + * Destroy a tls_hkdf_t + */ + void (*destroy)(tls_hkdf_t *this); +}; + +/** + * Create a tls_hkdf instance. + * + * @param hash_algorithm hash algorithm to use + * @param psk Pre shared key if available otherwise NULL + * @return TLS HKDF helper + */ +tls_hkdf_t *tls_hkdf_create(hash_algorithm_t hash_algorithm, chunk_t psk); + +#endif /** TLS_HKDF_H_ @}*/