]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
keylogfile: generalize with a callback
authorDaiki Ueno <dueno@redhat.com>
Sun, 2 Feb 2020 07:13:50 +0000 (08:13 +0100)
committerDaiki Ueno <dueno@redhat.com>
Fri, 7 Feb 2020 17:03:45 +0000 (18:03 +0100)
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 <dueno@redhat.com>
17 files changed:
.gitignore
NEWS
devel/libgnutls-latest-x86_64.abi
devel/symbols.last
doc/Makefile.am
doc/manpages/Makefile.am
lib/constate.c
lib/ext/pre_shared_key.c
lib/gnutls_int.h
lib/handshake-tls13.c
lib/includes/gnutls/gnutls.h.in
lib/kx.c
lib/kx.h
lib/libgnutls.map
lib/state.c
tests/Makefile.am
tests/secret-hook.c [new file with mode: 0644]

index 6c716933fac3ea1f151185fac7b79fcdc0ae8e47..de27a2fc9a0f5cd39eeb9054573e382d95fface2 100644 (file)
@@ -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 11c860e428baf97d18bc7802aeb0e8ac0eb7834d..3e6e7fa83e3ba306ef0b866a08a14187839ba7da 100644 (file)
--- 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)
 
index 23d346aefab575de4275f790e4563f4554263bd6..3a9497697e8dc3f256a9725eba49cd83916aed15 100644 (file)
     <elf-symbol name='gnutls_handshake_set_post_client_hello_function' version='GNUTLS_3_4' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
     <elf-symbol name='gnutls_handshake_set_private_extensions' version='GNUTLS_3_4' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
     <elf-symbol name='gnutls_handshake_set_random' version='GNUTLS_3_4' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+    <elf-symbol name='gnutls_handshake_set_secret_function' version='GNUTLS_3_6_13' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
     <elf-symbol name='gnutls_handshake_set_timeout' version='GNUTLS_3_4' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
     <elf-symbol name='gnutls_hash' version='GNUTLS_3_4' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
     <elf-symbol name='gnutls_hash_copy' version='GNUTLS_3_6_9' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
index 51594477055c02b1983bc3f07534fd2fd32d857a..037741c562fac7834c206dd483a0917bfb310bb1 100644 (file)
@@ -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
index 234cbf315dfa04bea3a9b68c57a4d1df91dbb2b4..ef3c40f76c93392cc03790d6ee218fe4731069c7 100644 (file)
@@ -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
index f11b070fe0fc3213ddebcfca816f5ba7779acfb6..14e591e62f16a72c2d85233819709f03604dabd6 100644 (file)
@@ -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
index 51943ede69a70f16e458406ae68400ba5ae806b3..a11577d7ba5f811f9de596018c1204aaedf0a48a 100644 (file)
@@ -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)
index d3449229103b5137beda40501901c25457063fa1..eef84814d626ba1c224074e89256f248508d0c30 100644 (file)
@@ -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;
 }
index b48805190ac569729c6e79ad374f9e0f5bba56b9..cd2adc103df992a277119626262c74d07b077dd5 100644 (file)
@@ -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.
index 60f8030eb3b1dc7f704d21d1a0e1d0b7df4da667..39d002bd04f40fc5eaaedd024f0439bc2a9d4dde 100644 (file)
@@ -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);
index d05ef8e5a95dcf1fbbc7a02dbcd30b7b64a4d932..13b6c3565915c96d0061ea1987c4fad1eb5c9c8e 100644 (file)
@@ -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);
index 69374908e44f0069dcb9dec5bd72ca2176568d46..43056d412a605f521ba63f4fa85883adad211432 100644 (file)
--- 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.
index 2d98fda3cdff3d12d57e6704317b933477d4112d..8d8d4225ef4d625f4939fc5637364efa7753077e 100644 (file)
--- 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 */
index bf8fff8bc3def8ee4349fa32bd68acd85df4dfe9..c1aace905ef3806d773208f6876430f93d1d2d40 100644 (file)
@@ -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 {
index dff7312a87c8f0c8dd6f4d186c0a0fae3c9fc800..f33cd5a8bc01372d0fef156cc7a3aefc0bb9824d 100644 (file)
@@ -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;
 }
 
index 764db8c33a1d931b7cfacda76217023b508316b6..5b9fdb716821ab66e3265320e9fb5d41010b42c1 100644 (file)
@@ -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 (file)
index 0000000..f4523a6
--- /dev/null
@@ -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 <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if !defined(__linux__) || !defined(__GNUC__)
+
+int main(int argc, char **argv)
+{
+       exit(77);
+}
+
+#else
+
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+#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 */