From 9711755641cd9b6de323676abeab6cc80ea13e4b Mon Sep 17 00:00:00 2001 From: Daiki Ueno Date: Sun, 2 Feb 2020 08:13:50 +0100 Subject: keylogfile: generalize with a callback This refactors the keylogfile mechanism by adding a callback to get notified when a new secret is derived and installed. That way, consumers can implement custom logging feature per session, which is particularly useful in QUIC implementation. Signed-off-by: Daiki Ueno --- .gitignore | 1 + NEWS | 2 + devel/libgnutls-latest-x86_64.abi | 1 + devel/symbols.last | 1 + doc/Makefile.am | 3 + doc/manpages/Makefile.am | 1 + lib/constate.c | 34 ++-- lib/ext/pre_shared_key.c | 16 +- lib/gnutls_int.h | 2 + lib/handshake-tls13.c | 8 +- lib/includes/gnutls/gnutls.h.in | 53 ++++++ lib/kx.c | 87 ++++++++- lib/kx.h | 7 + lib/libgnutls.map | 1 + lib/state.c | 5 + tests/Makefile.am | 2 +- tests/secret-hook.c | 389 ++++++++++++++++++++++++++++++++++++++ 17 files changed, 588 insertions(+), 25 deletions(-) create mode 100644 tests/secret-hook.c diff --git a/.gitignore b/.gitignore index 6c716933fa..de27a2fc9a 100644 --- a/.gitignore +++ b/.gitignore @@ -661,6 +661,7 @@ tests/safe-renegotiation/srn2 tests/safe-renegotiation/srn3 tests/sanity-cpp tests/sec-params +tests/secret-hook tests/send-client-cert tests/send-data-befor tests/send-data-before-handshake diff --git a/NEWS b/NEWS index 11c860e428..3e6e7fa83e 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,8 @@ See the end for copying conditions. gnutls_hkdf_extract: Added gnutls_hkdf_expand: Added gnutls_pbkdf2: Added +gnutls_handshake_secret_type_t: New enumeration +gnutls_handshake_set_secret_function: Added * Version 3.6.12 (released 2020-02-01) diff --git a/devel/libgnutls-latest-x86_64.abi b/devel/libgnutls-latest-x86_64.abi index 23d346aefa..3a9497697e 100644 --- a/devel/libgnutls-latest-x86_64.abi +++ b/devel/libgnutls-latest-x86_64.abi @@ -300,6 +300,7 @@ + diff --git a/devel/symbols.last b/devel/symbols.last index 5159447705..037741c562 100644 --- a/devel/symbols.last +++ b/devel/symbols.last @@ -267,6 +267,7 @@ gnutls_handshake_set_max_packet_length@GNUTLS_3_4 gnutls_handshake_set_post_client_hello_function@GNUTLS_3_4 gnutls_handshake_set_private_extensions@GNUTLS_3_4 gnutls_handshake_set_random@GNUTLS_3_4 +gnutls_handshake_set_secret_function@GNUTLS_3_6_13 gnutls_handshake_set_timeout@GNUTLS_3_4 gnutls_hash@GNUTLS_3_4 gnutls_hash_copy@GNUTLS_3_6_9 diff --git a/doc/Makefile.am b/doc/Makefile.am index 234cbf315d..ef3c40f76c 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -556,6 +556,7 @@ ENUMS += enums/gnutls_fips_mode_t ENUMS += enums/gnutls_gost_paramset_t ENUMS += enums/gnutls_group_t ENUMS += enums/gnutls_handshake_description_t +ENUMS += enums/gnutls_handshake_secret_type_t ENUMS += enums/gnutls_init_flags_t ENUMS += enums/gnutls_keygen_types_t ENUMS += enums/gnutls_keyid_flags_t @@ -1083,6 +1084,8 @@ FUNCS += functions/gnutls_handshake_set_private_extensions FUNCS += functions/gnutls_handshake_set_private_extensions.short FUNCS += functions/gnutls_handshake_set_random FUNCS += functions/gnutls_handshake_set_random.short +FUNCS += functions/gnutls_handshake_set_secret_function +FUNCS += functions/gnutls_handshake_set_secret_function.short FUNCS += functions/gnutls_handshake_set_timeout FUNCS += functions/gnutls_handshake_set_timeout.short FUNCS += functions/gnutls_hash diff --git a/doc/manpages/Makefile.am b/doc/manpages/Makefile.am index f11b070fe0..14e591e62f 100644 --- a/doc/manpages/Makefile.am +++ b/doc/manpages/Makefile.am @@ -343,6 +343,7 @@ APIMANS += gnutls_handshake_set_max_packet_length.3 APIMANS += gnutls_handshake_set_post_client_hello_function.3 APIMANS += gnutls_handshake_set_private_extensions.3 APIMANS += gnutls_handshake_set_random.3 +APIMANS += gnutls_handshake_set_secret_function.3 APIMANS += gnutls_handshake_set_timeout.3 APIMANS += gnutls_hash.3 APIMANS += gnutls_hash_copy.3 diff --git a/lib/constate.c b/lib/constate.c index 51943ede69..a11577d7ba 100644 --- a/lib/constate.c +++ b/lib/constate.c @@ -197,6 +197,7 @@ _tls13_update_keys(gnutls_session_t session, hs_stage_t stage, char buf[65]; record_state_st *upd_state; record_parameters_st *prev = NULL; + gnutls_handshake_secret_type_t secret_type; int ret; /* generate new keys for direction needed and copy old from previous epoch */ @@ -274,6 +275,7 @@ _tls13_update_keys(gnutls_session_t session, hs_stage_t stage, ret = _tls13_expand_secret(session, "iv", 2, NULL, 0, session->key.proto.tls13.ap_ckey, iv_size, iv_block); if (ret < 0) return gnutls_assert_val(ret); + secret_type = GNUTLS_SECRET_CLIENT_TRAFFIC_SECRET; } else { ret = _tls13_expand_secret(session, APPLICATION_TRAFFIC_UPDATE, sizeof(APPLICATION_TRAFFIC_UPDATE)-1, @@ -291,8 +293,14 @@ _tls13_update_keys(gnutls_session_t session, hs_stage_t stage, ret = _tls13_expand_secret(session, "iv", 2, NULL, 0, session->key.proto.tls13.ap_skey, iv_size, iv_block); if (ret < 0) return gnutls_assert_val(ret); + secret_type = GNUTLS_SECRET_SERVER_TRAFFIC_SECRET; } + ret = _gnutls_call_secret_func(session, secret_type, + key_block, key_size); + if (ret < 0) + return gnutls_assert_val(ret); + upd_state->mac_key_size = 0; assert(key_size <= sizeof(upd_state->key)); @@ -388,7 +396,7 @@ _tls13_set_keys(gnutls_session_t session, hs_stage_t stage, record_state_st *client_write, *server_write; const char *label; unsigned label_size, hsk_len; - const char *keylog_label; + gnutls_handshake_secret_type_t secret_type; void *ckey, *skey; int ret; @@ -404,13 +412,13 @@ _tls13_set_keys(gnutls_session_t session, hs_stage_t stage, label = HANDSHAKE_CLIENT_TRAFFIC_LABEL; label_size = sizeof(HANDSHAKE_CLIENT_TRAFFIC_LABEL)-1; hsk_len = session->internals.handshake_hash_buffer.length; - keylog_label = "CLIENT_HANDSHAKE_TRAFFIC_SECRET"; + secret_type = GNUTLS_SECRET_CLIENT_HANDSHAKE_TRAFFIC_SECRET; ckey = session->key.proto.tls13.hs_ckey; } else { label = APPLICATION_CLIENT_TRAFFIC_LABEL; label_size = sizeof(APPLICATION_CLIENT_TRAFFIC_LABEL)-1; hsk_len = session->internals.handshake_hash_buffer_server_finished_len; - keylog_label = "CLIENT_TRAFFIC_SECRET_0"; + secret_type = GNUTLS_SECRET_CLIENT_TRAFFIC_SECRET; ckey = session->key.proto.tls13.ap_ckey; } @@ -422,9 +430,11 @@ _tls13_set_keys(gnutls_session_t session, hs_stage_t stage, if (ret < 0) return gnutls_assert_val(ret); - _gnutls_nss_keylog_write(session, keylog_label, - ckey, - session->security_parameters.prf->output_size); + ret = _gnutls_call_secret_func(session, secret_type, + ckey, + session->security_parameters.prf->output_size); + if (ret < 0) + return gnutls_assert_val(ret); /* client keys */ ret = _tls13_expand_secret(session, "key", 3, NULL, 0, ckey, key_size, ckey_block); @@ -439,12 +449,12 @@ _tls13_set_keys(gnutls_session_t session, hs_stage_t stage, if (stage == STAGE_HS) { label = HANDSHAKE_SERVER_TRAFFIC_LABEL; label_size = sizeof(HANDSHAKE_SERVER_TRAFFIC_LABEL)-1; - keylog_label = "SERVER_HANDSHAKE_TRAFFIC_SECRET"; + secret_type = GNUTLS_SECRET_SERVER_HANDSHAKE_TRAFFIC_SECRET; skey = session->key.proto.tls13.hs_skey; } else { label = APPLICATION_SERVER_TRAFFIC_LABEL; label_size = sizeof(APPLICATION_SERVER_TRAFFIC_LABEL)-1; - keylog_label = "SERVER_TRAFFIC_SECRET_0"; + secret_type = GNUTLS_SECRET_SERVER_TRAFFIC_SECRET; skey = session->key.proto.tls13.ap_skey; } @@ -457,9 +467,11 @@ _tls13_set_keys(gnutls_session_t session, hs_stage_t stage, if (ret < 0) return gnutls_assert_val(ret); - _gnutls_nss_keylog_write(session, keylog_label, - skey, - session->security_parameters.prf->output_size); + ret = _gnutls_call_secret_func(session, secret_type, + skey, + session->security_parameters.prf->output_size); + if (ret < 0) + return gnutls_assert_val(ret); ret = _tls13_expand_secret(session, "key", 3, NULL, 0, skey, key_size, skey_block); if (ret < 0) diff --git a/lib/ext/pre_shared_key.c b/lib/ext/pre_shared_key.c index d344922910..eef84814d6 100644 --- a/lib/ext/pre_shared_key.c +++ b/lib/ext/pre_shared_key.c @@ -203,9 +203,11 @@ generate_early_secrets(gnutls_session_t session, if (ret < 0) return gnutls_assert_val(ret); - _gnutls_nss_keylog_write(session, "CLIENT_EARLY_TRAFFIC_SECRET", - session->key.proto.tls13.e_ckey, - prf->output_size); + ret = _gnutls_call_secret_func(session, GNUTLS_SECRET_CLIENT_EARLY_TRAFFIC_SECRET, + session->key.proto.tls13.e_ckey, + prf->output_size); + if (ret < 0) + return gnutls_assert_val(ret); ret = _tls13_derive_secret2(prf, EARLY_EXPORTER_MASTER_LABEL, sizeof(EARLY_EXPORTER_MASTER_LABEL)-1, session->internals.handshake_hash_buffer.data, @@ -215,9 +217,11 @@ generate_early_secrets(gnutls_session_t session, if (ret < 0) return gnutls_assert_val(ret); - _gnutls_nss_keylog_write(session, "EARLY_EXPORTER_SECRET", - session->key.proto.tls13.ap_expkey, - prf->output_size); + ret = _gnutls_call_secret_func(session, GNUTLS_SECRET_EARLY_EXPORTER_SECRET, + session->key.proto.tls13.ap_expkey, + prf->output_size); + if (ret < 0) + return gnutls_assert_val(ret); return 0; } diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index b48805190a..cd2adc103d 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -1243,6 +1243,8 @@ typedef struct { unsigned int h_type; /* the hooked type */ int16_t h_post; /* whether post-generation/receive */ + gnutls_handshake_secret_func secret_func; + /* holds the selected certificate and key. * use _gnutls_selected_certs_deinit() and _gnutls_selected_certs_set() * to change them. diff --git a/lib/handshake-tls13.c b/lib/handshake-tls13.c index 60f8030eb3..39d002bd04 100644 --- a/lib/handshake-tls13.c +++ b/lib/handshake-tls13.c @@ -292,9 +292,11 @@ static int generate_ap_traffic_keys(gnutls_session_t session) if (ret < 0) return gnutls_assert_val(ret); - _gnutls_nss_keylog_write(session, "EXPORTER_SECRET", - session->key.proto.tls13.ap_expkey, - session->security_parameters.prf->output_size); + ret = _gnutls_call_secret_func(session, GNUTLS_SECRET_EXPORTER_SECRET, + session->key.proto.tls13.ap_expkey, + session->security_parameters.prf->output_size); + if (ret < 0) + return gnutls_assert_val(ret); _gnutls_epoch_bump(session); ret = _gnutls_epoch_dup(session, EPOCH_READ_CURRENT); diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index d05ef8e5a9..13b6c35659 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -2292,6 +2292,59 @@ void gnutls_global_set_log_function(gnutls_log_func log_func); void gnutls_global_set_audit_log_function(gnutls_audit_log_func log_func); void gnutls_global_set_log_level(int level); +/** + * gnutls_handshake_secret_type_t: + * @GNUTLS_SECRET_CLIENT_RANDOM: 48 bytes for the master secret (for SSL 3.0, + * TLS 1.0, 1.1 and 1.2) + * @GNUTLS_SECRET_CLIENT_EARLY_TRAFFIC_SECRET: the early traffic secret for the + * client side (for TLS 1.3) + * @GNUTLS_SECRET_CLIENT_HANDSHAKE_TRAFFIC_SECRET: the handshake traffic secret + * for the client side (for TLS 1.3) + * @GNUTLS_SECRET_SERVER_HANDSHAKE_TRAFFIC_SECRET: the handshake traffic secret + * for the server side (for TLS 1.3) + * @GNUTLS_SECRET_CLIENT_TRAFFIC_SECRET: the application traffic secret for the + * client side (for TLS 1.3) + * @GNUTLS_SECRET_SERVER_TRAFFIC_SECRET: the application traffic secret for the + * server side (for TLS 1.3) + * @GNUTLS_SECRET_EARLY_EXPORTER_SECRET: the early exporter secret (for TLS 1.3, + * used for 0-RTT keys). + * @GNUTLS_SECRET_EXPORTER_SECRET: the exporter secret (for TLS 1.3, used for + * 1-RTT keys) + * + * Enumeration of different types of secrets derived during handshake. + * This is used by gnutls_handshake_set_secret_function(). + * + * Since: 3.6.13 + */ +typedef enum { + GNUTLS_SECRET_CLIENT_RANDOM, + GNUTLS_SECRET_CLIENT_EARLY_TRAFFIC_SECRET, + GNUTLS_SECRET_CLIENT_HANDSHAKE_TRAFFIC_SECRET, + GNUTLS_SECRET_SERVER_HANDSHAKE_TRAFFIC_SECRET, + GNUTLS_SECRET_CLIENT_TRAFFIC_SECRET, + GNUTLS_SECRET_SERVER_TRAFFIC_SECRET, + GNUTLS_SECRET_EARLY_EXPORTER_SECRET, + GNUTLS_SECRET_EXPORTER_SECRET +} gnutls_handshake_secret_type_t; + + /** + * gnutls_handshake_secret_function: + * @session: the current session + * @type: #gnutls_handshake_secret_type_t + * @secret: the (const) data of the derived secret. + * + * Function prototype for secret derivation hooks. It is set using + * gnutls_handshake_set_secret_function(). + * + * Returns: Non zero on error. + * Since: 3.6.13 + */ +typedef int (*gnutls_handshake_secret_func) (gnutls_session_t session, + gnutls_handshake_secret_type_t type, + const gnutls_datum_t *secret); +void gnutls_handshake_set_secret_function(gnutls_session_t session, + gnutls_handshake_secret_func func); + /* Diffie-Hellman parameter handling. */ int gnutls_dh_params_init(gnutls_dh_params_t * dh_params); diff --git a/lib/kx.c b/lib/kx.c index 69374908e4..43056d412a 100644 --- a/lib/kx.c +++ b/lib/kx.c @@ -70,6 +70,83 @@ int _gnutls_generate_master(gnutls_session_t session, int keep_premaster) return 0; } +/** + * gnutls_handshake_set_secret_function: + * @session: is #gnutls_session_t type + * @func: is the function to be called + * + * This function will set a callback to be called when a new secret is + * derived and installed during handshake. + * + * Since: 3.6.13 + */ +void +gnutls_handshake_set_secret_function(gnutls_session_t session, + gnutls_handshake_secret_func func) +{ + session->internals.secret_func = func; +} + +int +_gnutls_call_secret_func(gnutls_session_t session, + gnutls_handshake_secret_type_t type, + const uint8_t *data, + unsigned size) +{ + if (session->internals.secret_func) { + gnutls_datum_t secret = {(void*)data, size}; + return session->internals.secret_func(session, type, &secret); + } + return 0; +} + +static const char * +secret_type_to_nss_keylog_label(gnutls_handshake_secret_type_t type) +{ + switch (type) { + case GNUTLS_SECRET_CLIENT_RANDOM: + return "CLIENT_RANDOM"; + case GNUTLS_SECRET_CLIENT_EARLY_TRAFFIC_SECRET: + return "CLIENT_EARLY_TRAFFIC_SECRET"; + case GNUTLS_SECRET_CLIENT_HANDSHAKE_TRAFFIC_SECRET: + return "CLIENT_HANDSHAKE_TRAFFIC_SECRET"; + case GNUTLS_SECRET_SERVER_HANDSHAKE_TRAFFIC_SECRET: + return "SERVER_HANDSHAKE_TRAFFIC_SECRET"; + case GNUTLS_SECRET_CLIENT_TRAFFIC_SECRET: + return "CLIENT_TRAFFIC_SECRET_0"; + case GNUTLS_SECRET_SERVER_TRAFFIC_SECRET: + return "SERVER_TRAFFIC_SECRET_0"; + case GNUTLS_SECRET_EARLY_EXPORTER_SECRET: + return "EARLY_EXPORTER_SECRET"; + case GNUTLS_SECRET_EXPORTER_SECRET: + return "EXPORTER_SECRET"; + default: + gnutls_assert(); + return NULL; + } +} + +int +_gnutls_nss_keylog_secret_func(gnutls_session_t session, + gnutls_handshake_secret_type_t type, + const gnutls_datum_t *secret) +{ + const char *label; + + /* ignore subsequent traffic secrets that are calculated from + * the previous traffic secret + */ + if (!session->internals.handshake_in_progress) + return 0; + + label = secret_type_to_nss_keylog_label(type); + if (unlikely(label == NULL)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + _gnutls_nss_keylog_write(session, label, secret->data, secret->size); + return 0; +} + void _gnutls_nss_keylog_write(gnutls_session_t session, const char *label, const uint8_t *secret, size_t secret_size) @@ -182,16 +259,18 @@ generate_normal_master(gnutls_session_t session, gnutls_free(shash.data); } - _gnutls_nss_keylog_write(session, "CLIENT_RANDOM", - session->security_parameters.master_secret, - GNUTLS_MASTER_SIZE); - if (!keep_premaster) _gnutls_free_temp_key_datum(premaster); if (ret < 0) return ret; + ret = _gnutls_call_secret_func(session, GNUTLS_SECRET_CLIENT_RANDOM, + session->security_parameters.master_secret, + GNUTLS_MASTER_SIZE); + if (ret < 0) + return gnutls_assert_val(ret); + _gnutls_hard_log("INT: MASTER SECRET[%d]: %s\n", GNUTLS_MASTER_SIZE, _gnutls_bin2hex(session->security_parameters. diff --git a/lib/kx.h b/lib/kx.h index 2d98fda3cd..8d8d4225ef 100644 --- a/lib/kx.h +++ b/lib/kx.h @@ -38,8 +38,15 @@ int _gnutls_recv_server_crt_request(gnutls_session_t session); int _gnutls_send_server_crt_request(gnutls_session_t session, int again); int _gnutls_recv_client_certificate_verify_message(gnutls_session_t session); +int _gnutls_call_secret_func(gnutls_session_t session, + gnutls_handshake_secret_type_t type, + const uint8_t *data, + unsigned size); void _gnutls_nss_keylog_write(gnutls_session_t session, const char *label, const uint8_t *secret, size_t secret_size); +int _gnutls_nss_keylog_secret_func(gnutls_session_t session, + gnutls_handshake_secret_type_t type, + const gnutls_datum_t *secret); #endif /* GNUTLS_LIB_KX_H */ diff --git a/lib/libgnutls.map b/lib/libgnutls.map index bf8fff8bc3..c1aace905e 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -1315,6 +1315,7 @@ GNUTLS_3_6_13 gnutls_hkdf_extract; gnutls_hkdf_expand; gnutls_pbkdf2; + gnutls_handshake_set_secret_function; } GNUTLS_3_6_12; GNUTLS_FIPS140_3_4 { diff --git a/lib/state.c b/lib/state.c index dff7312a87..f33cd5a8bc 100644 --- a/lib/state.c +++ b/lib/state.c @@ -54,6 +54,7 @@ #include "tls13/session_ticket.h" #include "ext/cert_types.h" #include "locks.h" +#include "kx.h" /* to be used by supplemental data support to disable TLS1.3 * when supplemental data have been globally registered */ @@ -587,6 +588,10 @@ int gnutls_init(gnutls_session_t * session, unsigned int flags) if (_gnutls_disable_tls13 != 0) (*session)->internals.flags |= INT_FLAG_NO_TLS13; + /* Install the default secret function */ + gnutls_handshake_set_secret_function(*session, + _gnutls_nss_keylog_secret_func); + return 0; } diff --git a/tests/Makefile.am b/tests/Makefile.am index 764db8c33a..5b9fdb7168 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -217,7 +217,7 @@ ctests += mini-record-2 simple gnutls_hmac_fast set_pkcs12_cred cert certuniquei tls-record-size-limit-asym dh-compute ecdh-compute sign-verify-data-newapi \ sign-verify-newapi sign-verify-deterministic iov aead-cipher-vec \ tls13-without-timeout-func buffer status-request-revoked \ - set_x509_ocsp_multi_cli kdf-api + set_x509_ocsp_multi_cli kdf-api secret-hook if HAVE_SECCOMP_TESTS ctests += dtls-with-seccomp tls-with-seccomp dtls-client-with-seccomp tls-client-with-seccomp diff --git a/tests/secret-hook.c b/tests/secret-hook.c new file mode 100644 index 0000000000..f4523a6a46 --- /dev/null +++ b/tests/secret-hook.c @@ -0,0 +1,389 @@ +/* + * Copyright (C) 2019 Free Software Foundation, Inc. + * + * Author: Aniketh Girish + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#if !defined(__linux__) || !defined(__GNUC__) + +int main(int argc, char **argv) +{ + exit(77); +} + +#else + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cert-common.h" +#include "utils.h" + +/* This program tests whether a secret hook function is called upon a + * new traffic secret is installed. + */ + +static void terminate(void); + +static void server_log_func(int level, const char *str) +{ + fprintf(stderr, "server|<%d>| %s", level, str); +} + +static void client_log_func(int level, const char *str) +{ + fprintf(stderr, "client|<%d>| %s", level, str); +} + +const char *side = ""; + +/* These are global */ +static pid_t child; +#define MAX_BUF 1024 +#define MSG "Hello TLS" + +static const char * +secret_type_to_str(gnutls_handshake_secret_type_t type) +{ + switch (type) { + case GNUTLS_SECRET_CLIENT_RANDOM: + return "CLIENT_RANDOM"; + case GNUTLS_SECRET_CLIENT_EARLY_TRAFFIC_SECRET: + return "CLIENT_EARLY_TRAFFIC_SECRET"; + case GNUTLS_SECRET_CLIENT_HANDSHAKE_TRAFFIC_SECRET: + return "CLIENT_HANDSHAKE_TRAFFIC_SECRET"; + case GNUTLS_SECRET_SERVER_HANDSHAKE_TRAFFIC_SECRET: + return "SERVER_HANDSHAKE_TRAFFIC_SECRET"; + case GNUTLS_SECRET_CLIENT_TRAFFIC_SECRET: + return "CLIENT_TRAFFIC_SECRET"; + case GNUTLS_SECRET_SERVER_TRAFFIC_SECRET: + return "SERVER_TRAFFIC_SECRET"; + case GNUTLS_SECRET_EARLY_EXPORTER_SECRET: + return "EARLY_EXPORTER_SECRET"; + case GNUTLS_SECRET_EXPORTER_SECRET: + return "EXPORTER_SECRET"; + default: + return NULL; + } +} + +static int +secret_hook_func(gnutls_session_t session, + gnutls_handshake_secret_type_t type, + const gnutls_datum_t *secret) +{ + unsigned int *call_count = gnutls_session_get_ptr(session); + static const gnutls_handshake_secret_type_t exp_types[] = { + GNUTLS_SECRET_CLIENT_HANDSHAKE_TRAFFIC_SECRET, + GNUTLS_SECRET_SERVER_HANDSHAKE_TRAFFIC_SECRET, + GNUTLS_SECRET_EXPORTER_SECRET, + GNUTLS_SECRET_CLIENT_TRAFFIC_SECRET, + GNUTLS_SECRET_SERVER_TRAFFIC_SECRET, + GNUTLS_SECRET_CLIENT_TRAFFIC_SECRET, + GNUTLS_SECRET_SERVER_TRAFFIC_SECRET + }; + + if (*call_count >= sizeof(exp_types)/sizeof(exp_types[0])) + fail("unexpected secret at call count %u\n", + *call_count); + + if (type != exp_types[*call_count]) + fail("unexpected %s at call count %u\n", + secret_type_to_str(type), *call_count); + else if (debug) + success("received %s at call count %u\n", + secret_type_to_str(type), *call_count); + + (*call_count)++; + return 0; +} + +static void client(int fd, const char *prio, unsigned int exp_call_count) +{ + gnutls_session_t session; + char buffer[MAX_BUF + 1]; + unsigned int call_count = 0; + int ret, ii; + gnutls_certificate_credentials_t clientx509cred; + const char *err; + /* Need to enable anonymous KX specifically. */ + + global_init(); + + if (debug) { + gnutls_global_set_log_function(client_log_func); + gnutls_global_set_log_level(4711); + } + + gnutls_certificate_allocate_credentials(&clientx509cred); + + /* Initialize TLS session + */ + gnutls_init(&session, GNUTLS_CLIENT); + + gnutls_session_set_ptr(session, &call_count); + + /* Use default priorities */ + ret = gnutls_priority_set_direct(session, prio, &err); + if (ret < 0) { + fail("client: priority set failed (%s): %s\n", + gnutls_strerror(ret), err); + exit(1); + } + + ret = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, + clientx509cred); + if (ret < 0) + exit(1); + + gnutls_transport_set_int(session, fd); + + gnutls_handshake_set_secret_function(session, secret_hook_func); + + /* Perform the TLS handshake + */ + do { + ret = gnutls_handshake(session); + } + while (ret < 0 && gnutls_error_is_fatal(ret) == 0); + + if (ret < 0) + fail("client: Handshake failed: %s\n", gnutls_strerror(ret)); + else { + if (debug) + success("client: Handshake was completed\n"); + } + + if (debug) + success("client: TLS version is: %s\n", + gnutls_protocol_get_name + (gnutls_protocol_get_version(session))); + + /* Send key update */ + do { + ret = gnutls_session_key_update(session, GNUTLS_KU_PEER); + } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); + + if (ret < 0) + fail("error in key update: %s\n", gnutls_strerror(ret)); + else { + if (debug) + success("client: Sent key update\n"); + } + + gnutls_record_send(session, MSG, strlen(MSG)); + + do { + ret = gnutls_record_recv(session, buffer, MAX_BUF); + } while (ret == GNUTLS_E_AGAIN); + if (ret == 0) { + if (debug) + success + ("client: Peer has closed the TLS connection\n"); + } else if (ret < 0) { + fail("client: Error: %s\n", gnutls_strerror(ret)); + } + + if (debug) { + printf("- Received %d bytes: ", ret); + for (ii = 0; ii < ret; ii++) { + fputc(buffer[ii], stdout); + } + fputs("\n", stdout); + } + + if (call_count != exp_call_count) + fail("secret hook is not called %u times (%u)\n", + call_count, exp_call_count); + + gnutls_bye(session, GNUTLS_SHUT_WR); + + close(fd); + + gnutls_deinit(session); + + gnutls_certificate_free_credentials(clientx509cred); + + gnutls_global_deinit(); +} + +static void server(int fd, const char *prio, unsigned int exp_call_count) +{ + int ret; + char buffer[MAX_BUF + 1]; + gnutls_session_t session; + unsigned int call_count = 0; + gnutls_certificate_credentials_t serverx509cred; + + /* this must be called once in the program + */ + global_init(); + + if (debug) { + gnutls_global_set_log_function(server_log_func); + gnutls_global_set_log_level(4711); + } + + gnutls_certificate_allocate_credentials(&serverx509cred); + + gnutls_init(&session, GNUTLS_SERVER); + + gnutls_session_set_ptr(session, &call_count); + + /* avoid calling all the priority functions, since the defaults + * are adequate. + */ + ret = gnutls_priority_set_direct(session, + "NORMAL:-VERS-ALL:+VERS-TLS1.3:-KX-ALL:-SIGN-ALL:+SIGN-RSA-PSS-RSAE-SHA384:-GROUP-ALL:+GROUP-SECP256R1", NULL); + if (ret < 0) { + fail("server: priority set failed (%s)\n\n", + gnutls_strerror(ret)); + terminate(); + } + + gnutls_certificate_set_x509_key_mem(serverx509cred, + &server_cert, &server_key, + GNUTLS_X509_FMT_PEM); + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, + serverx509cred); + + gnutls_transport_set_int(session, fd); + + gnutls_handshake_set_secret_function(session, secret_hook_func); + + do { + ret = gnutls_handshake(session); + } + while (ret < 0 && gnutls_error_is_fatal(ret) == 0); + if (ret < 0) { + close(fd); + gnutls_deinit(session); + fail("server: Handshake has failed (%s)\n\n", + gnutls_strerror(ret)); + terminate(); + } + if (debug) { + success("server: Handshake was completed\n"); + } + if (debug) + success("server: TLS version is: %s\n", + gnutls_protocol_get_name + (gnutls_protocol_get_version(session))); + + memset(buffer, 0, MAX_BUF + 1); + + do { + ret = gnutls_record_recv(session, buffer, MAX_BUF); + } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); + + if (ret == 0) { + if (debug) + success("server: Peer has closed the GnuTLS connection\n"); + } else if (ret < 0) { + fail("server: Received corrupted data(%d). Closing...\n", ret); + } else if (ret > 0) { + /* echo data back to the client + */ + gnutls_record_send(session, buffer, + strlen(buffer)); + } + + if (call_count != exp_call_count) + fail("secret hook is not called %u times (%u)\n", + call_count, exp_call_count); + + /* do not wait for the peer to close the connection. + */ + gnutls_bye(session, GNUTLS_SHUT_WR); + + close(fd); + gnutls_deinit(session); + + gnutls_certificate_free_credentials(serverx509cred); + + gnutls_global_deinit(); + + if (debug) + success("server: finished\n"); +} + +static void terminate(void) +{ + int status = 0; + + kill(child, SIGTERM); + wait(&status); + exit(1); +} + +static void +run(const char *prio, unsigned int exp_call_count) +{ + int fd[2]; + int ret; + + signal(SIGPIPE, SIG_IGN); + + ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd); + if (ret < 0) { + perror("socketpair"); + exit(1); + } + + child = fork(); + if (child < 0) { + perror("fork"); + fail("fork"); + exit(1); + } + + if (child) { + int status = 0; + /* parent */ + + server(fd[0], prio, exp_call_count); + wait(&status); + check_wait_status(status); + } else { + close(fd[0]); + client(fd[1], prio, exp_call_count); + exit(0); + } +} + +void doit(void) +{ + run("NORMAL:-VERS-ALL:+VERS-TLS1.3", 7); +} + +#endif /* _WIN32 */ -- cgit v1.2.1