File 0715-ssl-Improve-maintainability-and-correct-keylogging.patch of Package erlang

From 23e79dcf78c67c0b3d396b092b0efcbbe6c1e6d4 Mon Sep 17 00:00:00 2001
From: Ingela Anderton Andin <ingela@erlang.org>
Date: Mon, 22 Dec 2025 09:38:06 +0100
Subject: [PATCH 5/5] ssl: Improve maintainability and correct keylogging

---
 lib/ssl/src/ssl.erl                       |  25 +-
 lib/ssl/src/ssl_gen_statem.erl            | 255 ++++++++++-----
 lib/ssl/src/ssl_logger.erl                |  10 +-
 lib/ssl/src/tls_client_connection_1_3.erl |  26 +-
 lib/ssl/src/tls_gen_connection.erl        |  25 +-
 lib/ssl/src/tls_gen_connection_1_3.erl    |   3 +-
 lib/ssl/src/tls_handshake_1_3.erl         | 379 +++++++++++-----------
 lib/ssl/src/tls_record.erl                |   2 +-
 lib/ssl/src/tls_server_connection_1_3.erl |  77 ++---
 lib/ssl/test/ssl_session_ticket_SUITE.erl |   9 -
 lib/ssl/test/tls_1_3_version_SUITE.erl    | 159 +++++++--
 11 files changed, 600 insertions(+), 370 deletions(-)

diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index a29047a410..cc47041b85 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -1757,7 +1757,7 @@ opt_protocol_versions(UserOpts, Opts, Env) ->
 
     {_, LL} = get_opt_of(log_level, LogLevels, DefaultLevel, UserOpts, Opts),
 
-    Opts1 = set_opt_bool(keep_secrets, false, UserOpts, Opts),
+    Opts1 = opt_keep_secrets(UserOpts, Opts),
 
     {DistW, Dist} = get_opt_bool(erl_dist, false, UserOpts, Opts1),
     option_incompatible(PRC =:= dtls andalso Dist, [{protocol, PRC}, {erl_dist, Dist}]),
@@ -2501,6 +2501,22 @@ opt_process(UserOpts, Opts0, _Env) ->
     %% Opts = Opts1#{receiver_spawn_opts => RSO, sender_spawn_opts => SSO},
     set_opt_int(hibernate_after, 0, infinity, infinity, UserOpts, Opts2).
 
+opt_keep_secrets(UserOpts, Opts) ->
+    case get_opt(keep_secrets, false, UserOpts, Opts) of
+        {new, Value} when is_boolean(Value) ->
+            Opts#{keep_secrets => Value};
+        {new, {FunType, Fun} = Value}
+          when FunType == keylog orelse FunType == keylog_hs,
+               is_function(Fun) ->
+            Opts#{keep_secrets => Value};
+        {old, _} ->
+            Opts;
+        {default, _} -> %% Keep default implicit
+            Opts;
+        {_, Value}  ->
+            option_error(keep_secrets, Value)
+    end.
+
 %%%%
 
 get_opt(Opt, Default, UserOpts, Opts) ->
@@ -2571,13 +2587,6 @@ get_opt_file(Opt, Default, UserOpts, Opts) ->
         Res -> Res
     end.
 
-set_opt_bool(Opt, Default, UserOpts, Opts) ->
-    case maps:get(Opt, UserOpts, Default) of
-        Default -> Opts;
-        Value when is_boolean(Value) -> Opts#{Opt => Value};
-        Value -> option_error(Opt, Value)
-    end.
-
 get_opt_map(Opt, Default, UserOpts, Opts) ->
     case get_opt(Opt, Default, UserOpts, Opts) of
         {new, Err} when not is_map(Err) -> option_error(Opt, Err);
diff --git a/lib/ssl/src/ssl_gen_statem.erl b/lib/ssl/src/ssl_gen_statem.erl
index 7eb9452a6f..4ade5e86d3 100644
--- a/lib/ssl/src/ssl_gen_statem.erl
+++ b/lib/ssl/src/ssl_gen_statem.erl
@@ -87,6 +87,7 @@
 
 %% Alert and close handling
 -export([send_alert/3,
+         opposite_role/1,
          handle_own_alert/3,
          handle_alert/3,
 	 handle_normal_shutdown/3,
@@ -1100,6 +1101,7 @@ handle_normal_shutdown(Alert, StateName, #state{static_env = #static_env{role =
                                                 handshake_env = #handshake_env{renegotiation = {false, first}},
                                                 start_or_recv_from = StartFrom} = State) ->
     Pids = Connection:pids(State),
+    maybe_keylog_hs_callback(Alert, StateName, State),
     alert_user(Pids, Transport, Trackers, Socket, StartFrom, Alert, Role, StateName, Connection);
 
 handle_normal_shutdown(Alert, StateName, #state{static_env = #static_env{role = Role,
@@ -1112,6 +1114,7 @@ handle_normal_shutdown(Alert, StateName, #state{static_env = #static_env{role =
                                                 socket_options = Opts,
 						start_or_recv_from = RecvFrom} = State) ->
     Pids = Connection:pids(State),
+    maybe_keylog_hs_callback(Alert, StateName, State),
     alert_user(Pids, Transport, Trackers, Socket, Type, Opts, Pid, RecvFrom, Alert, Role, StateName, Connection).
 
 handle_alert(#alert{level = ?FATAL} = Alert, StateName, State) ->
@@ -1227,6 +1230,7 @@ handle_fatal_alert(Alert0, StateName,
               StateName, Alert),
     Pids = Connection:pids(State),
     alert_user(Pids, Transport, Trackers, Socket, StateName, Opts, Pid, From, Alert, Role, StateName, Connection),
+    handle_normal_shutdown(Alert, StateName, State),
     {stop, {shutdown, normal}, State}.
 
 handle_trusted_certs_db(#state{ssl_options =#{cacerts := []} = Opts})
@@ -2208,79 +2212,6 @@ ssl_options_list([{ciphers = Key, Value}|T], Acc) ->
 ssl_options_list([{Key, Value}|T], Acc) ->
    ssl_options_list(T, [{Key, Value} | Acc]).
 
-%% Maybe add NSS keylog info according to
-%% https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format
-maybe_add_keylog(Info) ->
-    maybe_add_keylog(lists:keyfind(protocol, 1, Info), Info).
-
-maybe_add_keylog({_, 'tlsv1.3'}, Info) ->
-    try
-        {client_random, ClientRandomBin} = lists:keyfind(client_random, 1, Info),
-        %% after traffic key update current traffic secret
-        %% is stored in tls_sender process state
-        MaybeUpdateTrafficSecret =
-            fun({Direction, {Sender, TrafficSecret0}}) ->
-                    TrafficSecret =
-                        case call(Sender, get_application_traffic_secret) of
-                            {ok, SenderAppTrafSecretWrite} ->
-                                SenderAppTrafSecretWrite;
-                            _ ->
-                                TrafficSecret0
-                        end,
-                    {Direction, TrafficSecret};
-               (TrafficSecret0) ->
-                    TrafficSecret0
-            end,
-        {client_traffic_secret_0, ClientTrafficSecret0Bin} =
-            MaybeUpdateTrafficSecret(lists:keyfind(client_traffic_secret_0, 1, Info)),
-        {server_traffic_secret_0, ServerTrafficSecret0Bin} =
-            MaybeUpdateTrafficSecret(lists:keyfind(server_traffic_secret_0, 1, Info)),
-        {client_handshake_traffic_secret, ClientHSecretBin} = lists:keyfind(client_handshake_traffic_secret, 1, Info),
-        {server_handshake_traffic_secret, ServerHSecretBin} = lists:keyfind(server_handshake_traffic_secret, 1, Info),
-        {selected_cipher_suite, #{prf := Prf}} = lists:keyfind(selected_cipher_suite, 1, Info),
-        ClientRandom = binary:decode_unsigned(ClientRandomBin),
-        ClientTrafficSecret0 = keylog_secret(ClientTrafficSecret0Bin, Prf),
-        ServerTrafficSecret0 = keylog_secret(ServerTrafficSecret0Bin, Prf),
-        ClientHSecret = keylog_secret(ClientHSecretBin, Prf),
-        ServerHSecret = keylog_secret(ServerHSecretBin, Prf),
-        Keylog0 = [io_lib:format("CLIENT_HANDSHAKE_TRAFFIC_SECRET ~64.16.0B ", [ClientRandom]) ++ ClientHSecret,
-                   io_lib:format("SERVER_HANDSHAKE_TRAFFIC_SECRET ~64.16.0B ", [ClientRandom]) ++ ServerHSecret,
-                   io_lib:format("CLIENT_TRAFFIC_SECRET_0 ~64.16.0B ", [ClientRandom]) ++ ClientTrafficSecret0,
-                   io_lib:format("SERVER_TRAFFIC_SECRET_0 ~64.16.0B ", [ClientRandom]) ++ ServerTrafficSecret0],
-        Keylog = case lists:keyfind(client_early_data_secret, 1, Info) of
-                     {client_early_data_secret, EarlySecret} ->
-                         ClientEarlySecret = keylog_secret(EarlySecret, Prf),
-                         [io_lib:format("CLIENT_EARLY_TRAFFIC_SECRET ~64.16.0B ", [ClientRandom]) ++ ClientEarlySecret
-                          | Keylog0];
-                     _ ->
-                         Keylog0
-                 end,
-        Info ++ [{keylog,Keylog}]
-    catch
-        _Cxx:_Exx ->
-            Info
-    end;
-maybe_add_keylog({_, _}, Info) ->
-    try
-        {client_random, ClientRandomBin} = lists:keyfind(client_random, 1, Info),
-        {master_secret, MasterSecretBin} = lists:keyfind(master_secret, 1, Info),
-        ClientRandom = binary:decode_unsigned(ClientRandomBin),
-        MasterSecret = binary:decode_unsigned(MasterSecretBin),
-        Keylog = [io_lib:format("CLIENT_RANDOM ~64.16.0B ~96.16.0B", [ClientRandom, MasterSecret])],
-        Info ++ [{keylog,Keylog}]
-    catch
-        _Cxx:_Exx ->
-            Info
-    end;
-maybe_add_keylog(_, Info) ->
-    Info.
-
-keylog_secret(SecretBin, sha256) ->
-    io_lib:format("~64.16.0B", [binary:decode_unsigned(SecretBin)]);
-keylog_secret(SecretBin, sha384) ->
-    io_lib:format("~96.16.0B", [binary:decode_unsigned(SecretBin)]);
-keylog_secret(SecretBin, sha512) ->
-    io_lib:format("~128.16.0B", [binary:decode_unsigned(SecretBin)]).
 
 maybe_generate_client_shares(#{versions := [?TLS_1_3|_],
                                supported_groups :=
@@ -2291,6 +2222,184 @@ maybe_generate_client_shares(#{versions := [?TLS_1_3|_],
 maybe_generate_client_shares(_) ->
     undefined.
 
+%% Maybe add NSS keylog info according to
+%% https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format
+maybe_add_keylog(Info) ->
+    case lists:keyfind(keep_secrets, 1, Info) of
+        {keep_secrets, true} ->
+            case lists:keyfind(protocol, 1, Info) of
+                {protocol, 'tlsv1.3'} ->
+                    Info ++ [{keylog, keylog_1_3(Info)}];
+                {protocol, _} ->
+                    Info ++ [{keylog, keylog_pre_1_3(Info)}]
+            end;
+        {keep_secrets, false} ->
+            Info
+    end.
+
+maybe_keylog_hs_callback(#alert{level = ?FATAL}, StateName,
+                         #state{ssl_options = #{keep_secrets := {keylog_hs, Fun}}}
+                         = State) when is_function(Fun)  ->
+    case keylog_hs_alert(StateName, State) of
+        {[], _} ->
+            ok;
+        {KeyLog, Random} ->
+            ssl_logger:keylog(KeyLog, Random, Fun)
+    end;
+maybe_keylog_hs_callback(_, _, _) ->
+    ok.
+
+keylog_hs_alert(start, _) -> %% TLS 1.3: No secrets yet established
+    {[], undefined};
+keylog_hs_alert(wait_sh, _) -> %% TLS 1.3: No secrets yet established
+    {[], undefined};
+keylog_hs_alert(negotiated, #state{static_env = #static_env{role = server},
+                                   connection_env =
+                                       #connection_env{negotiated_version = TlsVersion},
+                                   connection_states = #{current_read := Read,
+                                                         current_write := Write
+                                                        }})
+  when ?TLS_GTE(TlsVersion, ?TLS_1_3) ->
+     #{server_handshake_traffic_secret := ServerHSSecret,
+      security_parameters :=
+          #security_parameters{client_random = ClientRandomBin,
+                               client_early_data_secret = EarlySecret,
+                               prf_algorithm = Prf
+                              }} = Write,
+    #{client_handshake_traffic_secret := ClientHSSecret} = Read,
+    {keylog_hs_1_3(ClientRandomBin, Prf, EarlySecret, ServerHSSecret, ClientHSSecret),
+     ClientRandomBin};
+keylog_hs_alert(wait_eoed, #state{static_env = #static_env{role = server},
+                                  connection_env =
+                                      #connection_env{negotiated_version = TlsVersion},
+                                  connection_states = #{pending_read := Read,
+                                                        current_write := Write
+                                                       }})
+  when ?TLS_GTE(TlsVersion, ?TLS_1_3) ->
+    keylog_1_3_client_finished(Read, Write);
+keylog_hs_alert(StateName, #state{static_env = #static_env{role = server},
+                                  connection_states =
+                                      #{current_read := Read,
+                                        current_write := Write
+                                       }}) when StateName == wait_cert;
+                                                StateName == wait_cv;
+                                                StateName == wait_finished ->
+    keylog_1_3_client_finished(Read, Write);
+keylog_hs_alert(connection, #state{static_env = #static_env{role = client},
+                                   connection_env =
+                                       #connection_env{negotiated_version = TlsVersion},
+                                   connection_states = #{current_read := Read,
+                                                         current_write := Write
+                                               }})
+  when ?TLS_GTE(TlsVersion, ?TLS_1_3) ->
+    #{server_handshake_traffic_secret := ServerHSSecret,
+      security_parameters :=
+          #security_parameters{client_random = ClientRandomBin,
+                               client_early_data_secret = EarlySecret,
+                               prf_algorithm = Prf,
+                               master_secret = {master_secret, STrafficSecret}
+                              }}
+        = Read,
+    #{client_handshake_traffic_secret := ClientHSSecret,
+       security_parameters :=
+          #security_parameters{master_secret = {master_secret, CTrafficSecret}
+                              }} = Write,
+
+    {keylog_hs_1_3(ClientRandomBin, Prf, EarlySecret, ClientHSSecret, ServerHSSecret) ++
+         ssl_logger:keylog_traffic_1_3(client, ClientRandomBin, Prf, CTrafficSecret, 0) ++
+         ssl_logger:keylog_traffic_1_3(server, ClientRandomBin, Prf, STrafficSecret, 0),
+     ClientRandomBin};
+keylog_hs_alert(_, #state{static_env = #static_env{role = client},
+                          connection_env =
+                              #connection_env{negotiated_version = TlsVersion},
+                          connection_states = #{current_read := Read,
+                                                current_write := Write
+                                               }})
+  when ?TLS_GTE(TlsVersion, ?TLS_1_3) ->
+   #{server_handshake_traffic_secret := ServerHSSecret,
+      security_parameters :=
+                   #security_parameters{client_random = ClientRandomBin,
+                                        client_early_data_secret = EarlySecret,
+                                        prf_algorithm = Prf
+                                       }}
+        = Write,
+    #{client_handshake_traffic_secret := ClientHSSecret} = Read,
+    {keylog_hs_1_3(ClientRandomBin, Prf, EarlySecret, ClientHSSecret, ServerHSSecret),
+     ClientRandomBin};
+keylog_hs_alert(_, _) -> % NOT Relevant pre TLS-1.3
+    {[], undefined}.
+
+keylog_hs_1_3(ClientRandomBin, Prf, EarlySecret, ClientSecret, ServerSecret) ->
+    HSItems =
+        ssl_logger:keylog_hs(ClientRandomBin, Prf, ClientSecret, ServerSecret),
+    case EarlySecret of
+        undefined ->
+            HSItems;
+        _  ->
+            [ssl_logger:keylog_early_data(ClientRandomBin, Prf, EarlySecret) | HSItems]
+    end.
+
+keylog_1_3_client_finished(Read, Write) ->
+    #{server_handshake_traffic_secret := ServerHSSecret,
+      security_parameters :=
+                   #security_parameters{client_random = ClientRandomBin,
+                                        client_early_data_secret = EarlySecret,
+                                        prf_algorithm = Prf,
+                                        master_secret = {master_secret, TrafficSecret}
+                                       }}
+        = Write,
+    #{client_handshake_traffic_secret := ClientHSSecret} = Read,
+    {keylog_hs_1_3(ClientRandomBin, Prf, EarlySecret, ServerHSSecret, ClientHSSecret) ++
+         ssl_logger:keylog_traffic_1_3(server, ClientRandomBin, Prf, TrafficSecret, 0),
+     ClientRandomBin}.
+
+keylog_1_3(Info) ->
+    {client_random, ClientRandomBin} = lists:keyfind(client_random, 1, Info),
+    {selected_cipher_suite, #{prf := Prf}} = lists:keyfind(selected_cipher_suite, 1, Info),
+    {Role, Sender} = case lists:keyfind(server, 1, Info) of
+                         false ->
+                             lists:keyfind(client, 1, Info);
+                         Result ->
+                             Result
+                     end,
+    keylog_1_3(ClientRandomBin, Prf, Role, Sender, Info).
+
+keylog_1_3(ClientRandom, Prf, Role, Sender, Info) ->
+    {ok, SecretWrite, NWrite} = call(Sender, get_application_traffic_secret),
+    EarlySecret = proplists:get_value(client_early_data_secret, Info, undefined),
+    case Role of
+        client ->
+            {server_traffic_secret, SecretRead, NRead} = lists:keyfind(server_traffic_secret, 1, Info),
+            hs_logs(NRead, ClientRandom, Prf, EarlySecret, Info) ++
+                ssl_logger:keylog_traffic_1_3(Role, ClientRandom, Prf, SecretRead, NRead) ++
+                ssl_logger:keylog_traffic_1_3(server, ClientRandom, Prf, SecretWrite, NWrite);
+        server ->
+            {client_traffic_secret, SecretRead, NRead} = lists:keyfind(client_traffic_secret, 1, Info),
+            hs_logs(NRead, ClientRandom, Prf, EarlySecret, Info) ++
+                ssl_logger:keylog_traffic_1_3(client, ClientRandom, Prf, SecretWrite, NWrite) ++
+                ssl_logger:keylog_traffic_1_3(Role, ClientRandom, Prf, SecretRead, NRead)
+    end.
+
+hs_logs(0, ClientRandom, Prf, EarlySecret, Info) ->
+    {client_handshake_traffic_secret, ClientHSecret} =
+        lists:keyfind(client_handshake_traffic_secret, 1, Info),
+    {server_handshake_traffic_secret, ServerHSecret} =
+        lists:keyfind(server_handshake_traffic_secret, 1, Info),
+    HSItems = ssl_logger:keylog_hs(ClientRandom, Prf, ClientHSecret, ServerHSecret),
+    case EarlySecret of
+        undefined ->
+            HSItems;
+        _  ->
+            [ssl_logger:keylog_early_data(ClientRandom, Prf, EarlySecret) | HSItems]
+    end;
+hs_logs(_,_,_,_, _) ->
+    [].
+
+keylog_pre_1_3(Info) ->
+    {client_random, ClientRandom} = lists:keyfind(client_random, 1, Info),
+    {master_secret, MasterSecret} = lists:keyfind(master_secret, 1, Info),
+    ssl_logger:keylog_traffic_pre_1_3(ClientRandom, MasterSecret).
+
 %%%################################################################
 %%%#
 %%%# Tracing
diff --git a/lib/ssl/src/ssl_logger.erl b/lib/ssl/src/ssl_logger.erl
index af893c389c..95a726d78f 100644
--- a/lib/ssl/src/ssl_logger.erl
+++ b/lib/ssl/src/ssl_logger.erl
@@ -51,6 +51,11 @@
 %% Internal API -- Stateful logging
 %%-------------------------------------------------------------------------
 
+
+%%---%%-------------------------------------------------------------------------
+%% Internal API -- Stateful logging
+%%-------------------------------------------------------------------------
+
 log(Level, LogLevel, ReportMap, Meta) ->
     case logger:compare_levels(LogLevel, Level) of
         lt ->
@@ -63,6 +68,8 @@ log(Level, LogLevel, ReportMap, Meta) ->
             ok
     end.
 
+debug(undefined, _Direction, _Protocol, _Message) ->
+    ok;
 debug(LogLevel, Direction, Protocol, Message)
   when (Direction =:= inbound orelse Direction =:= outbound) andalso
        (Protocol =:= 'record' orelse Protocol =:= 'handshake') ->
@@ -134,7 +141,8 @@ format(#{msg:= {report, Msg}}, _Config0) ->
         _Other ->
             []
     end.
-%%-------------------------------------------------------------------------
+
+%%----------------------------------------------------------------------
 %%  Keylog functions (not erlang logger related)
 %%-------------------------------------------------------------------------
 
diff --git a/lib/ssl/src/tls_client_connection_1_3.erl b/lib/ssl/src/tls_client_connection_1_3.erl
index 4c5a6cecf0..928360374a 100644
--- a/lib/ssl/src/tls_client_connection_1_3.erl
+++ b/lib/ssl/src/tls_client_connection_1_3.erl
@@ -459,12 +459,13 @@ wait_finished(internal,
         State4 = Connection:queue_handshake(Finished, State3),
         %% Send first flight
         {State5, _} = Connection:send_handshake_flight(State4),
-        State6 = tls_handshake_1_3:calculate_traffic_secrets(State5),
-        State7 = tls_handshake_1_3:maybe_calculate_resumption_master_secret(State6),
-        State8 = ssl_record:step_encryption_state(State7),
-        State9 = tls_handshake_1_3:prepare_connection(State8),
+        State6 = tls_handshake_1_3:calculate_write_traffic_secrets(State5),
+        State7 = tls_handshake_1_3:calculate_read_traffic_secrets(State6),
+        State8 = tls_handshake_1_3:maybe_calculate_resumption_master_secret(State7),
+        State9 = ssl_record:step_encryption_state(State8),
+        State10 = tls_handshake_1_3:prepare_connection(State9),
         {Record, State} =
-            ssl_gen_statem:prepare_connection(State9, tls_gen_connection),
+            ssl_gen_statem:prepare_connection(State10, tls_gen_connection),
         KeepSecrets = maps:get(keep_secrets, SSLOpts, false),
         tls_gen_connection_1_3:maybe_traffic_keylog_1_3(KeepSecrets, Role,
                                                         State#state.connection_states, 0),
@@ -483,6 +484,21 @@ wait_finished(Type, Msg, State) ->
                  term(), #state{}) ->
           gen_statem:state_function_result().
 %%--------------------------------------------------------------------
+connection(enter, _, #state{ssl_options = Opts} = State) ->
+    case maps:get(keep_secrets, Opts, undefined) of
+        {keylog_hs, _} ->
+            %% Mitigate consequences of keylog_hs being activated, as
+            %% this forces the client to remember secrets longer, that
+            %% is the client certification can fail after the client
+            %% reached connection state.
+            {next_state, ?FUNCTION_NAME, State, [{timeout, 1000, forget}]};
+        _ ->
+            {keep_state, State}
+    end;
+connection(timeout, forget, State) ->
+    {next_state, ?FUNCTION_NAME, tls_handshake_1_3:forget_master_secret(State)};
+connection(info, Msg, State) ->
+    tls_gen_connection:gen_info(Msg, connection, State);
 connection(Type, Msg, State) ->
     tls_gen_connection_1_3:connection(Type, Msg, State).
 
diff --git a/lib/ssl/src/tls_gen_connection.erl b/lib/ssl/src/tls_gen_connection.erl
index 36da0f0ec2..edaab26406 100644
--- a/lib/ssl/src/tls_gen_connection.erl
+++ b/lib/ssl/src/tls_gen_connection.erl
@@ -62,7 +62,8 @@
 -export([socket/4,
          setopts/3,
          getopts/3,
-         handle_info/3]).
+         handle_info/3,
+         gen_info/3]).
 
 %% Alert and close handling
 -export([send_alert/2,
@@ -288,6 +289,28 @@ setopts(Transport, Socket, Other) ->
 getopts(Transport, Socket, Tag) ->
     tls_socket:getopts(Transport, Socket, Tag).
 
+
+gen_info(Event, connection = StateName, State) ->
+    try
+        handle_info(Event, StateName, State)
+    catch
+        _:Reason:ST ->
+            ?SSL_LOG(info, internal_error, [{error, Reason}, {stacktrace, ST}]),
+	    ssl_gen_statem:handle_own_alert(?ALERT_REC(?FATAL, ?INTERNAL_ERROR,
+						       malformed_data),
+					    StateName, State)
+    end;
+gen_info(Event, StateName, State) ->
+    try
+        handle_info(Event, StateName, State)
+    catch
+        _:Reason:ST ->
+            ?SSL_LOG(info, handshake_error, [{error, Reason}, {stacktrace, ST}]),
+	    ssl_gen_statem:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE,
+						       malformed_handshake_data),
+					    StateName, State)
+    end.
+
 %% raw data from socket, upack records
 handle_info({Protocol, _, Data}, StateName,
             #state{static_env = #static_env{data_tag = Protocol}} = State0) ->
diff --git a/lib/ssl/src/tls_gen_connection_1_3.erl b/lib/ssl/src/tls_gen_connection_1_3.erl
index 5261d77a8a..5c9f14fb12 100644
--- a/lib/ssl/src/tls_gen_connection_1_3.erl
+++ b/lib/ssl/src/tls_gen_connection_1_3.erl
@@ -50,8 +50,7 @@
          maybe_forget_hs_secrets/2,
          do_maybe/0]).
 
-%%--------------------------------------------------------------------
-%% Internal API
+%%  Internal API
 %%--------------------------------------------------------------------
 initial_state(Role, Sender, Host, Port, Socket,
               {SSLOptions, SocketOptions, Trackers}, User,
diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl
index 63ac4ba177..e2bea6bfe2 100644
--- a/lib/ssl/src/tls_handshake_1_3.erl
+++ b/lib/ssl/src/tls_handshake_1_3.erl
@@ -79,7 +79,6 @@
          get_pre_shared_key/4,
          get_pre_shared_key_early_data/2,
          get_supported_groups/1,
-         calculate_traffic_secrets/1,
          calculate_client_early_traffic_secret/5,
          calculate_client_early_traffic_secret/2,
          calculate_read_traffic_secrets/1,
@@ -87,6 +86,7 @@
          maybe_calculate_resumption_master_secret/1,
          early_data_secret/1,
          hs_traffic_secrets/2,
+         forget_master_secret/1,
          encode_early_data/2,
          get_ticket_data/3,
          ciphers_for_early_data/1,
@@ -870,49 +870,136 @@ message_hash(ClientHello1, HKDFAlgo) ->
      0,0,ssl_cipher:hash_size(HKDFAlgo),
      crypto:hash(HKDFAlgo, ClientHello1)].
 
-prepare_connection(State0) ->
+prepare_connection(#state{static_env = #static_env{role = Role}, ssl_options = Opts} =State0) ->
     %% Handle different secrets on transition to the connection state
     State1 = #state{protocol_specific = PS} = maybe_calculate_resumption_master_secret(State0),
     ExporterSecret = calculate_exporter_master_secret(State1),
-    State =  State0#state{protocol_specific = PS#{exporter_master_secret => ExporterSecret}},
-    forget_master_secret(State).
+    State = State0#state{protocol_specific = PS#{exporter_master_secret => ExporterSecret}},
+    Keep = case maps:get(keep_secrets, Opts, undefined) of
+               {keylog_hs, _} ->
+                   true;
+               _ ->
+                   false
+           end,
+    case Role == client andalso Keep of
+        true ->
+            %% Remember
+            State;
+        false ->
+            forget_master_secret(State)
+    end.
 
-calculate_handshake_secrets(PublicKey, PrivateKey, SelectedGroup, PSK,
-                              #state{connection_states = ConnectionStates,
-                                     handshake_env =
-                                         #handshake_env{
-                                            tls_handshake_history = HHistory}} = State0) ->
-    #{security_parameters := SecParamsR} =
-        ssl_record:pending_connection_state(ConnectionStates, read),
-    #security_parameters{prf_algorithm = HKDFAlgo,
-                         cipher_suite = CipherSuite} = SecParamsR,
+calculate_handshake_secrets(PublicKey, PrivateKeyOrSecret, SelectedGroup, PSK,
+                              #state{connection_states = ConnectionStates0,
+                                    ssl_options = Opts} = State0) ->
+    PendingRead1 = calculate_read_handshake_secrets(PublicKey, PrivateKeyOrSecret,
+                                                  SelectedGroup, PSK, State0),
+    PendingWrite1 = calculate_write_handshake_secrets(PublicKey, PrivateKeyOrSecret,
+                                                   SelectedGroup, PSK, State0),
+    {PendingRead, PendingWrite} = maybe_handshake_keylog(PendingRead1, PendingWrite1, Opts),
+    ConnectionStates = ConnectionStates0#{pending_read => PendingRead,
+                                          pending_write => PendingWrite},
+    State0#state{connection_states = ConnectionStates}.
+
+calculate_read_handshake_secrets(PublicKey, PrivateKeyOrSecret, SelectedGroup, PSK,
+                                #state{ssl_options = Opts,
+                                       static_env = #static_env{role = Role},
+                                       connection_states = #{pending_read := PendingRead0},
+                                       handshake_env =
+                                           #handshake_env{
+                                              tls_handshake_history = HHistory}}) ->
+    #{security_parameters := SecParams} = PendingRead0,
+    #security_parameters{prf_algorithm = HKDFAlgo} = SecParams,
+    HandshakeSecret = calculate_handshake_master_secret(PublicKey, PrivateKeyOrSecret,
+                                                        SelectedGroup, PSK, HKDFAlgo),
+    Keep = maps:get(keep_secrets, Opts, false),
+    calculate_handshake_secret(HandshakeSecret, PendingRead0, read, Role, HHistory, Keep).
+
+calculate_write_handshake_secrets(PublicKey, PrivateKeyOrSecret, SelectedGroup, PSK,
+                                  #state{ssl_options = Opts,
+                                         static_env = #static_env{role = Role},
+                                         connection_states = #{pending_write := PendingWrite0},
+                                         handshake_env =
+                                             #handshake_env{
+                                                tls_handshake_history = HHistory}}) ->
+    #{security_parameters := SecParams} = PendingWrite0,
+    #security_parameters{prf_algorithm = HKDFAlgo} = SecParams,
+    HandshakeSecret = calculate_handshake_master_secret(PublicKey, PrivateKeyOrSecret,
+                                                        SelectedGroup, PSK, HKDFAlgo),
+    Keep = maps:get(keep_secrets, Opts, false),
+    calculate_handshake_secret(HandshakeSecret, PendingWrite0, write, Role, HHistory, Keep).
+
+calculate_handshake_master_secret(PublicKey, PrivateKeyOrSecret, SelectedGroup,
+                                  PSK, HKDFAlgo) ->
     EarlySecret = tls_v1:key_schedule(early_secret, HKDFAlgo , {psk, PSK}),
+    IKM = calculate_shared_secret(PublicKey, PrivateKeyOrSecret, SelectedGroup),
+    tls_v1:key_schedule(handshake_secret, HKDFAlgo, IKM, EarlySecret).
 
-    IKM = calculate_shared_secret(PublicKey, PrivateKey, SelectedGroup),
-    HandshakeSecret = tls_v1:key_schedule(handshake_secret, HKDFAlgo, IKM, EarlySecret),
-
-    %% Calculate [sender]_handshake_traffic_secret
-    {Messages, _} =  HHistory,
-    ClientHSTrafficSecret =
-        tls_v1:client_handshake_traffic_secret(HKDFAlgo, HandshakeSecret, lists:reverse(Messages)),
-    ServerHSTrafficSecret =
-        tls_v1:server_handshake_traffic_secret(HKDFAlgo, HandshakeSecret, lists:reverse(Messages)),
+calculate_handshake_secret(HandshakeSecret, ConnectionState, ReadorWrite, Role, HHistory, Keep)  ->
+    #{security_parameters := SecParams} = ConnectionState,
+    #security_parameters{prf_algorithm = HKDFAlgo,
+                         bulk_cipher_algorithm = BulkCipherAlgo,
+                        cipher_suite = CipherSuite} = SecParams,
+    Messages = get_handshake_context(Role, HHistory),
+    {HsTrafficSecret, SecretType} =
+        case {Role, ReadorWrite} of
+            {server, write} ->
+                {tls_v1:server_handshake_traffic_secret(HKDFAlgo,
+                                                       HandshakeSecret, Messages), server} ;
+            {client, read} ->
+                {tls_v1:server_handshake_traffic_secret(HKDFAlgo,
+                                                       HandshakeSecret, Messages), server};
+            {client, write} ->
+                {tls_v1:client_handshake_traffic_secret(HKDFAlgo,
+                                                       HandshakeSecret, Messages), client};
+            {server, read} ->
+                {tls_v1:client_handshake_traffic_secret(HKDFAlgo,
+                                                       HandshakeSecret, Messages), client}
+        end,
 
-    %% Calculate traffic keys
     KeyLength = tls_v1:key_length(CipherSuite),
-    {ReadKey, ReadIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, KeyLength, ClientHSTrafficSecret),
-    {WriteKey, WriteIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, KeyLength, ServerHSTrafficSecret),
-
-    %% Calculate Finished Keys
-    ReadFinishedKey = tls_v1:finished_key(ClientHSTrafficSecret, HKDFAlgo),
-    WriteFinishedKey = tls_v1:finished_key(ServerHSTrafficSecret, HKDFAlgo),
-
-    State1 = maybe_store_handshake_traffic_secret(State0, ClientHSTrafficSecret, ServerHSTrafficSecret),
+    {Key, IV} = tls_v1:calculate_traffic_keys(HKDFAlgo, KeyLength, HsTrafficSecret),
+
+    FinishedKey = tls_v1:finished_key(HsTrafficSecret, HKDFAlgo),
+
+    NewSecParams =
+        SecParams#security_parameters{master_secret = HandshakeSecret},
+
+    NewConnectionState = ConnectionState#{security_parameters => NewSecParams,
+                                         cipher_state =>
+                                             cipher_init(BulkCipherAlgo, Key, IV, FinishedKey)},
+    maybe_store_handshake_traffic_secret(SecretType, NewConnectionState, HsTrafficSecret, Keep).
+
+maybe_store_handshake_traffic_secret(client, ConnectionState, HsTrafficSecret, Keep)
+  when Keep =/=  false ->
+    ConnectionState#{client_handshake_traffic_secret => HsTrafficSecret};
+maybe_store_handshake_traffic_secret(server, ConnectionState, HsTrafficSecret, Keep)
+  when Keep =/=  false ->
+    ConnectionState#{server_handshake_traffic_secret => HsTrafficSecret};
+maybe_store_handshake_traffic_secret(_,ConnectionState, _,_) ->
+    ConnectionState.
+
+maybe_handshake_keylog(Read0, Write0, Opts) ->
+    Keep = maps:get(keep_secrets, Opts, undefined),
+    #{security_parameters := SecParams} = Read0,
+    #security_parameters{prf_algorithm = HKDFAlgo} = SecParams,
+    case Keep of
+        {keylog, Fun} ->
+            #{security_parameters := SecParams0} = Read0,
+            ClientRand = SecParams0#security_parameters.client_random,
+            {ClientHSTrafficSecret, ServerHSTrafficSecret} =
+                hs_secrets(Read0, Write0),
+            KeyLog = ssl_logger:keylog_hs(ClientRand, HKDFAlgo,
+                                          ClientHSTrafficSecret, ServerHSTrafficSecret),
+            ssl_logger:keylog(KeyLog, ClientRand, Fun),
+            {maps:without([client_handshake_traffic_secret, server_handshake_traffic_secret],
+                          Read0),
+             maps:without([client_handshake_traffic_secret, server_handshake_traffic_secret],
+                          Write0)};
+        _ ->
+            {Read0, Write0}
+    end.
 
-    update_pending_connection_states(State1, HandshakeSecret, undefined,
-                                     undefined, undefined,
-                                     ReadKey, ReadIV, ReadFinishedKey,
-                                     WriteKey, WriteIV, WriteFinishedKey).
 hs_secrets(Read, Write) ->
     {hs_traffic_secret(client_handshake_traffic_secret, Read, Write),
      hs_traffic_secret(server_handshake_traffic_secret, Read, Write)}.
@@ -944,7 +1031,8 @@ calculate_client_early_traffic_secret(#state{connection_states = ConnectionState
 %% Client
 calculate_client_early_traffic_secret(
   ClientHello, PSK, Cipher, HKDFAlgo,
-  #state{connection_states = ConnectionStates,
+  #state{connection_states = #{pending_write := PendingWrite0,
+                               pending_read := PendingRead0} = ConnectionStates,
          ssl_options = Opts,
          static_env = #static_env{role = Role}} = State0) ->
     EarlySecret = tls_v1:key_schedule(early_secret, HKDFAlgo , {psk, PSK}),
@@ -957,20 +1045,21 @@ calculate_client_early_traffic_secret(
     %% Update pending connection states
     case Role of
         client ->
-            PendingWrite0 = ssl_record:pending_connection_state(ConnectionStates, write),
-            PendingWrite1 = maybe_store_early_data_secret(Opts, ClientEarlyTrafficSecret,
-                                                          PendingWrite0),
-            PendingWrite = update_connection_state(PendingWrite1, undefined, undefined,
-                                                   undefined,
-                                                   Key, IV, undefined),
+            PendingWrite1 = #{security_parameters := SecParams} =
+                maybe_store_early_data_secret(Opts, ClientEarlyTrafficSecret,
+                                              PendingWrite0, HKDFAlgo),
+            BulkCipherAlgo = SecParams#security_parameters.bulk_cipher_algorithm,
+            PendingWrite = PendingWrite1#{cipher_state =>
+                               cipher_init(BulkCipherAlgo, Key, IV, undefined)},
+
             State0#state{connection_states = ConnectionStates#{pending_write => PendingWrite}};
         server ->
-            PendingRead0 = ssl_record:pending_connection_state(ConnectionStates, read),
-            PendingRead1 = maybe_store_early_data_secret(Opts, ClientEarlyTrafficSecret,
-                                                         PendingRead0),
-            PendingRead = update_connection_state(PendingRead1, undefined, undefined,
-                                                   undefined,
-                                                   Key, IV, undefined),
+            PendingRead1  = #{security_parameters := SecParams} =
+                maybe_store_early_data_secret(Opts, ClientEarlyTrafficSecret,
+                                              PendingRead0, HKDFAlgo),
+            BulkCipherAlgo = SecParams#security_parameters.bulk_cipher_algorithm,
+            PendingRead = PendingRead1#{cipher_state =>
+                                            cipher_init(BulkCipherAlgo, Key, IV, undefined)},
             State0#state{connection_states = ConnectionStates#{pending_read => PendingRead}}
     end.
 
@@ -988,13 +1077,21 @@ hs_traffic_secrets(Read, Write) ->
              {server_handshake_traffic_secret, ServerHSTrafficSecret}]
     end.
 
-maybe_store_early_data_secret(#{keep_secrets := true}, EarlySecret, State) ->
-    #{security_parameters := SecParams0} = State,
-    SecParams = SecParams0#security_parameters{client_early_data_secret = EarlySecret},
-    State#{security_parameters := SecParams};
-maybe_store_early_data_secret(_, _, State) ->
-    State.
-
+maybe_store_early_data_secret(#{keep_secrets := Keep}, EarlySecret,
+                              #{security_parameters := SecParams0} = CSState,
+                              Prf) when Keep =/= false ->
+    case Keep of
+        {keylog, Fun} ->
+            ClientRand = SecParams0#security_parameters.client_random,
+            KeyLog = ssl_logger:keylog_early_data(ClientRand, Prf, EarlySecret),
+            ssl_logger:keylog(KeyLog, ClientRand, Fun),
+            CSState;
+        _ ->
+            SecParams = SecParams0#security_parameters{client_early_data_secret = EarlySecret},
+            CSState#{security_parameters := SecParams}
+    end;
+maybe_store_early_data_secret(_, _, CSState, _) ->
+    CSState.
 %% Server
 %% get_pre_shared_key(undefined, HKDFAlgo) ->
 %%     binary:copy(<<0>>, ssl_cipher:hash_size(HKDFAlgo));
@@ -1071,42 +1168,6 @@ choose_psk([#ticket_data{
 choose_psk([_|T], SelectedIdentity) ->
     choose_psk(T, SelectedIdentity).
 
-
-calculate_traffic_secrets(#state{
-                             static_env = #static_env{role = Role},
-                             connection_states = ConnectionStates,
-                             handshake_env =
-                                 #handshake_env{
-                                    tls_handshake_history = HHistory}} = State0) ->
-    #{security_parameters := SecParamsR} =
-        ssl_record:pending_connection_state(ConnectionStates, read),
-    #security_parameters{prf_algorithm = HKDFAlgo,
-                         cipher_suite = CipherSuite,
-                         master_secret = HandshakeSecret} = SecParamsR,
-
-    MasterSecret =
-        tls_v1:key_schedule(master_secret, HKDFAlgo, HandshakeSecret),
-
-    %% Get the correct list messages for the handshake context.
-    Messages = get_handshake_context(Role, HHistory),
-
-    %% Calculate [sender]_application_traffic_secret_0
-    ClientAppTrafficSecret0 =
-        tls_v1:client_application_traffic_secret_0(HKDFAlgo, MasterSecret, Messages),
-    ServerAppTrafficSecret0 =
-        tls_v1:server_application_traffic_secret_0(HKDFAlgo, MasterSecret, Messages),
-
-    %% Calculate traffic keys
-    KeyLength = tls_v1:key_length(CipherSuite),
-    {ReadKey, ReadIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, KeyLength, ClientAppTrafficSecret0),
-    {WriteKey, WriteIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, KeyLength, ServerAppTrafficSecret0),
-
-    update_pending_connection_states(State0, MasterSecret, undefined,
-                                     ClientAppTrafficSecret0, ServerAppTrafficSecret0,
-                                     ReadKey, ReadIV, undefined,
-                                     WriteKey, WriteIV, undefined).
-
-
 calculate_read_traffic_secrets(#state{
                              static_env = #static_env{role = Role},
                                   connection_states = #{pending_read := PendingRead0} =
@@ -1114,29 +1175,7 @@ calculate_read_traffic_secrets(#state{
                                   handshake_env =
                                       #handshake_env{
                                          tls_handshake_history = HHistory}} = State0) ->
-    #{security_parameters := SecParamsR,
-      cipher_state := #cipher_state{finished_key = FinishedKey}} =
-        ssl_record:pending_connection_state(ConnectionStates, read),
-    #security_parameters{prf_algorithm = HKDFAlgo,
-                         cipher_suite = CipherSuite,
-                         master_secret = HandshakeSecret} = SecParamsR,
-
-    MasterSecret =
-        tls_v1:key_schedule(master_secret, HKDFAlgo, HandshakeSecret),
-
-    %% Get the correct list messages for the handshake context.
-    Messages = get_handshake_context(Role, HHistory),
-
-    %% Calculate [sender]_application_traffic_secret_0
-    ClientAppTrafficSecret0 =
-        tls_v1:client_application_traffic_secret_0(HKDFAlgo, MasterSecret, lists:reverse(Messages)),
-
-    %% Calculate traffic keys
-    KeyLength = tls_v1:key_length(CipherSuite),
-    {ReadKey, ReadIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, KeyLength, ClientAppTrafficSecret0),
-    PendingRead = update_connection_state(PendingRead0, MasterSecret, undefined,
-                                          ClientAppTrafficSecret0,
-                                          ReadKey, ReadIV, FinishedKey),
+    PendingRead = calculate_traffic_secrets(PendingRead0, read, Role, HHistory),
     State0#state{connection_states = ConnectionStates#{pending_read => PendingRead}}.
 
 calculate_write_traffic_secrets(#state{
@@ -1146,31 +1185,47 @@ calculate_write_traffic_secrets(#state{
                              handshake_env =
                                  #handshake_env{
                                     tls_handshake_history = HHistory}} = State0) ->
-    #{security_parameters := SecParamsR,
-      cipher_state := #cipher_state{finished_key = FinishedKey}} =
-        ssl_record:pending_connection_state(ConnectionStates, write),
+    PendingWrite = calculate_traffic_secrets(PendingWrite0, write, Role, HHistory),
+    State0#state{connection_states = ConnectionStates#{pending_write => PendingWrite}}.
+
+
+calculate_traffic_secrets(ConnectionState, ReadorWrite, Role, HHistory)  ->
+    #{security_parameters := SecParams,
+      cipher_state := #cipher_state{finished_key = FinishedKey}} = ConnectionState,
     #security_parameters{prf_algorithm = HKDFAlgo,
                          cipher_suite = CipherSuite,
-                         master_secret = HandshakeSecret} = SecParamsR,
-
+                         master_secret = HandshakeSecret,
+                         bulk_cipher_algorithm = BulkCipherAlgo} = SecParams,
     MasterSecret =
         tls_v1:key_schedule(master_secret, HKDFAlgo, HandshakeSecret),
-
-    %% Get the correct list messages for the handshake context.
     Messages = get_handshake_context(Role, HHistory),
 
-    %% Calculate [sender]_application_traffic_secret_0
-    ServerAppTrafficSecret0 =
-        tls_v1:server_application_traffic_secret_0(HKDFAlgo, MasterSecret, lists:reverse(Messages)),
+    AppTrafficSecret0 =
+        case {Role, ReadorWrite} of
+            {server, write} ->
+                tls_v1:server_application_traffic_secret_0(HKDFAlgo,
+                                                           MasterSecret, Messages);
+            {client, read} ->
+                tls_v1:server_application_traffic_secret_0(HKDFAlgo,
+                                                            MasterSecret, Messages);
+            {client, write} ->
+                tls_v1:client_application_traffic_secret_0(HKDFAlgo,
+                                                           MasterSecret, Messages);
+            {server, read} ->
+                tls_v1:client_application_traffic_secret_0(HKDFAlgo,
+                                                           MasterSecret, Messages)
+        end,
 
-    %% Calculate traffic keys
     KeyLength = tls_v1:key_length(CipherSuite),
-    {WriteKey, WriteIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, KeyLength, ServerAppTrafficSecret0),
-    PendingWrite = update_connection_state(PendingWrite0, MasterSecret, undefined,
-                                          ServerAppTrafficSecret0,
-                                          WriteKey, WriteIV, FinishedKey),
-    State0#state{connection_states = ConnectionStates#{pending_write => PendingWrite}}.
+    {Key, IV} = tls_v1:calculate_traffic_keys(HKDFAlgo, KeyLength, AppTrafficSecret0),
+
+    NewSecParams =
+        SecParams#security_parameters{master_secret = MasterSecret,
+                                      application_traffic_secret = AppTrafficSecret0},
 
+    ConnectionState#{security_parameters => NewSecParams,
+                     cipher_state =>
+                         cipher_init(BulkCipherAlgo, Key, IV, FinishedKey)}.
 
 %% X25519, X448
 calculate_shared_secret(OthersKey, MyKey, Group)
@@ -1252,70 +1307,6 @@ overwrite_client_random(ConnectionState = #{security_parameters := SecurityParam
     SecurityParameters = SecurityParameters0#security_parameters{client_random = ClientRandom},
     ConnectionState#{security_parameters => SecurityParameters}.
 
-
-maybe_store_handshake_traffic_secret(#state{connection_states =
-                                                #{pending_read := PendingRead} = CS,
-                                            ssl_options = #{keep_secrets := true}} = State,
-                                     ClientHSTrafficSecret, ServerHSTrafficSecret) ->
-    PendingRead1 = store_handshake_traffic_secret(PendingRead, ClientHSTrafficSecret, ServerHSTrafficSecret),
-    State#state{connection_states = CS#{pending_read => PendingRead1}};
-maybe_store_handshake_traffic_secret(State, _, _) ->
-    State.
-
-store_handshake_traffic_secret(ConnectionState, ClientHSTrafficSecret, ServerHSTrafficSecret) ->
-    ConnectionState#{client_handshake_traffic_secret => ClientHSTrafficSecret,
-                     server_handshake_traffic_secret => ServerHSTrafficSecret}.
-
-
-update_pending_connection_states(#state{
-                                    static_env = #static_env{role = server},
-                                    connection_states =
-                                        CS = #{pending_read := PendingRead0,
-                                               pending_write := PendingWrite0}} = State,
-                                 HandshakeSecret, ResumptionMasterSecret,
-                                 ClientAppTrafficSecret, ServerAppTrafficSecret,
-                                 ReadKey, ReadIV, ReadFinishedKey,
-                                 WriteKey, WriteIV, WriteFinishedKey) ->
-    PendingRead = update_connection_state(PendingRead0, HandshakeSecret, ResumptionMasterSecret,
-                                          ClientAppTrafficSecret,
-                                          ReadKey, ReadIV, ReadFinishedKey),
-    PendingWrite = update_connection_state(PendingWrite0, HandshakeSecret, ResumptionMasterSecret,
-                                           ServerAppTrafficSecret,
-                                           WriteKey, WriteIV, WriteFinishedKey),
-    State#state{connection_states = CS#{pending_read => PendingRead,
-                                        pending_write => PendingWrite}};
-update_pending_connection_states(#state{
-                                    static_env = #static_env{role = client},
-                                    connection_states =
-                                        CS = #{pending_read := PendingRead0,
-                                               pending_write := PendingWrite0}} = State,
-                                 HandshakeSecret, ResumptionMasterSecret,
-                                 ClientAppTrafficSecret, ServerAppTrafficSecret,
-                                 ReadKey, ReadIV, ReadFinishedKey,
-                                 WriteKey, WriteIV, WriteFinishedKey) ->
-    PendingRead = update_connection_state(PendingRead0, HandshakeSecret, ResumptionMasterSecret,
-                                          ServerAppTrafficSecret,
-                                          WriteKey, WriteIV, WriteFinishedKey),
-    PendingWrite = update_connection_state(PendingWrite0, HandshakeSecret, ResumptionMasterSecret,
-                                           ClientAppTrafficSecret,
-                                           ReadKey, ReadIV, ReadFinishedKey),
-    State#state{connection_states = CS#{pending_read => PendingRead,
-                                        pending_write => PendingWrite}}.
-
-
-update_connection_state(ConnectionState = #{security_parameters := SecurityParameters0},
-                        HandshakeSecret, ResumptionMasterSecret,
-                        ApplicationTrafficSecret, Key, IV, FinishedKey) ->
-    %% Store secret
-    SecurityParameters = SecurityParameters0#security_parameters{
-                           master_secret = HandshakeSecret,
-                           resumption_master_secret = ResumptionMasterSecret,
-                           application_traffic_secret = ApplicationTrafficSecret},
-    BulkCipherAlgo = SecurityParameters#security_parameters.bulk_cipher_algorithm,
-    ConnectionState#{security_parameters => SecurityParameters,
-                     cipher_state => cipher_init(BulkCipherAlgo, Key, IV, FinishedKey)}.
-
-
 update_start_state(State, Map) ->
     Cipher = maps:get(cipher, Map, undefined),
     KeyShare = maps:get(key_share, Map, undefined),
diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl
index 6bd4f060ab..82c4db86c4 100644
--- a/lib/ssl/src/tls_record.erl
+++ b/lib/ssl/src/tls_record.erl
@@ -488,7 +488,7 @@ initial_connection_state(ConnectionEnd, BeastMitigation, MaxEarlyDataSize) ->
       pending_early_data_size => MaxEarlyDataSize,
       max_fragment_length => undefined,
       trial_decryption => false,
-      early_data_expected => false
+      early_data_accepted => false
      }.
 
 %% Used by logging to recreate the received bytes
diff --git a/lib/ssl/src/tls_server_connection_1_3.erl b/lib/ssl/src/tls_server_connection_1_3.erl
index 75babb99b4..b844e6ab83 100644
--- a/lib/ssl/src/tls_server_connection_1_3.erl
+++ b/lib/ssl/src/tls_server_connection_1_3.erl
@@ -256,15 +256,14 @@ negotiated(internal = Type, #change_cipher_spec{} = Msg, State) ->
 negotiated(internal, {start_handshake, _} = Message, State0) ->
     case send_hello_flight(Message, State0) of
         #alert{} = Alert ->
-            ssl_gen_statem:handle_own_alert(Alert, negotiated, State0);
+            ssl_gen_statem:handle_own_alert(Alert, ?FUNCTION_NAME, State0);
         {State1, NextState} ->
             State2 = tls_handshake_1_3:calculate_write_traffic_secrets(State1),
             State = ssl_record:step_encryption_state_write(State2),
-            maybe_keylog(State),
             {next_state, NextState, State, []}
     end;
 negotiated(info, Msg, State) ->
-    tls_connection:gen_info(Msg, negotiated, State);
+    tls_connection:gen_info(Msg, ?FUNCTION_NAME, State);
 negotiated(Type, Msg, State) ->
     ssl_gen_statem:handle_common_event(Type, Msg, ?FUNCTION_NAME, State).
 
@@ -476,10 +475,10 @@ do_handle_client_hello(#client_hello{cipher_suites = ClientCiphers,
 
         Opts = State2#state.ssl_options,
         State3 = case maps:get(keep_secrets, Opts, false) of
-                     true ->
-                         tls_handshake_1_3:set_client_random(State2, Hello#client_hello.random);
                      false ->
-                         State2
+                         State2;
+                     _ ->
+                         tls_handshake_1_3:set_client_random(State2, Hello#client_hello.random)
                  end,
 
         State4 = tls_handshake_1_3:update_start_state(State3,
@@ -554,14 +553,7 @@ send_hello_flight({start_handshake, PSK0},
                 true ->
                     ssl_record:step_encryption_state_write(State3);
                 false ->
-                    %% Read state is overwritten when handshake secrets are set.
-                    %% Trial_decryption and early_data_accepted must be set here!
-                    update_current_read(
-                      ssl_record:step_encryption_state(State3),
-                      true,   %% trial_decryption
-                      false   %% early_data_accepted
-                    )
-
+                    ssl_record:step_encryption_state(State3)
             end,
 
         %% Create EncryptedExtensions
@@ -617,32 +609,41 @@ validate_cookie(#cookie{cookie = Cookie0}, #state{ssl_options = #{cookie := true
 validate_cookie(_,_) ->
     {error, ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)}.
 
-session_resumption({#state{ssl_options = #{session_tickets := disabled}} = State, negotiated}, _) ->
+session_resumption({#state{ssl_options = #{session_tickets := disabled}} = State,
+                    negotiated}, _) ->
     {ok, {State, negotiated, undefined}}; % Resumption prohibited
-session_resumption({#state{ssl_options = #{session_tickets := Tickets}} = State, negotiated}, undefined = PSK)
+session_resumption({#state{ssl_options = #{session_tickets := Tickets}} = State,
+                    negotiated}, undefined = PSK)
   when Tickets =/= disabled ->
     {ok, {State, negotiated, PSK}}; % No resumption
 session_resumption({#state{ssl_options = #{session_tickets := Tickets},
                            handshake_env = #handshake_env{
-                                              early_data_accepted = false}} = State0, negotiated}, PSKInfo)
+                                              early_data_accepted = false}} = State0,
+                    negotiated}, PSKInfo)
   when Tickets =/= disabled -> % Resumption but early data prohibited
     State1 = tls_gen_connection_1_3:handle_resumption(State0, ok),
     {Index, PSK, PeerCert} = PSKInfo,
-    State = maybe_store_peer_cert(State1, PeerCert),
-    State = maybe_store_peer_cert(State1, PeerCert),
-    {ok, {State, negotiated, {Index, PSK}}};
+    State = #state{connection_states = #{pending_read := Read0} = CS} =
+        maybe_store_peer_cert(State1, PeerCert),
+    Read = Read0#{trial_decryption => true},
+    {ok, {State#state{connection_states = CS#{pending_read => Read}}, negotiated, {Index, PSK}}};
 session_resumption({#state{ssl_options = #{session_tickets := Tickets},
                            handshake_env = #handshake_env{
-                                              early_data_accepted = true}} = State0, negotiated}, PSKInfo)
+                                              early_data_accepted = true}} = State0,
+                    negotiated}, PSKInfo)
   when Tickets =/= disabled -> % Resumption with early data allowed
     State1 = tls_gen_connection_1_3:handle_resumption(State0, ok),
     %% TODO Refactor PSK-tuple {Index, PSK}, index might not be needed.
     {Index, PSK, PeerCert} = PSKInfo,
-    State2 = tls_handshake_1_3:calculate_client_early_traffic_secret(State1, PSK),
+    State2 = maybe_store_peer_cert(State1, PeerCert),
+    State3 =
+        tls_handshake_1_3:calculate_client_early_traffic_secret(State2, PSK),
     %% Set 0-RTT traffic keys for reading early_data
-    State3 = ssl_record:step_encryption_state_read(State2),
-    State4 = maybe_store_peer_cert(State3, PeerCert),
-    State = update_current_read(State4, true, true),
+    State4 = #state{connection_states = #{current_read := Read0} = CS}
+        = ssl_record:step_encryption_state_read(State3),
+    Read = Read0#{trial_decryption => true,
+                  early_data_accepted => true},
+    State = State4#state{connection_states = CS#{current_read => Read}},
     {ok, {State, negotiated, {Index, PSK}}}.
 
 maybe_store_peer_cert(State, undefined) ->
@@ -799,12 +800,6 @@ send_hello_retry_request(State0, _, _, _) ->
     %% Suitable key found.
     {ok, {State0, negotiated}}.
 
-update_current_read(#state{connection_states = CS} = State, TrialDecryption, EarlyDataExpected) ->
-    Read0 = ssl_record:current_connection_state(CS, read),
-    Read = Read0#{trial_decryption => TrialDecryption,
-                  early_data_accepted => EarlyDataExpected},
-    State#state{connection_states = CS#{current_read => Read}}.
-
 handle_early_data(State, enabled, #early_data_indication{}) ->
     %% Accept early data
     HsEnv = (State#state.handshake_env)#handshake_env{early_data_accepted = true},
@@ -925,23 +920,3 @@ handle_alpn([ServerProtocol|T], ClientProtocols) ->
         false ->
             handle_alpn(T, ClientProtocols)
     end.
-
-maybe_keylog(#state{ssl_options = Options,
-                    connection_states = ConnectionStates,
-                    protocol_specific = PS})->
-    KeylogFun = maps:get(keep_secrets, Options, undefined),
-    maybe_keylog(KeylogFun, PS, ConnectionStates).
-
-maybe_keylog({Keylog, Fun}, ProtocolSpecific, ConnectionStates) when Keylog == keylog_hs;
-                                                                     Keylog == keylog ->
-    N = maps:get(num_key_updates, ProtocolSpecific, 0),
-    #{security_parameters := #security_parameters{client_random = ClientRandom,
-                                                  prf_algorithm = Prf,
-                                                  application_traffic_secret = TrafficSecret}}
-        = ssl_record:current_connection_state(ConnectionStates, write),
-    TrafficKeyLog = ssl_logger:keylog_traffic_1_3(server, ClientRandom,
-                                                  Prf, TrafficSecret, N),
-
-    ssl_logger:keylog(TrafficKeyLog, ClientRandom, Fun);
-maybe_keylog(_,_,_) ->
-    ok.
diff --git a/lib/ssl/test/ssl_session_ticket_SUITE.erl b/lib/ssl/test/ssl_session_ticket_SUITE.erl
index a6295d7818..42159708bd 100644
--- a/lib/ssl/test/ssl_session_ticket_SUITE.erl
+++ b/lib/ssl/test/ssl_session_ticket_SUITE.erl
@@ -1215,7 +1215,6 @@ early_data_basic(Config) when is_list(Config) ->
                                                 verify_active_session_resumption,
                                                 [false]}},
                                          {from, self()}, {options, ClientOpts1}]),
-    skip_keylogs(4), %% HS and two traffic secrets
 
     ssl_test_lib:check_result(Server0, ok, Client0, ok),
 
@@ -1235,14 +1234,6 @@ early_data_basic(Config) when is_list(Config) ->
                                                 verify_active_session_resumption,
                                                 [true]}},
                                          {from, self()}, {options, ClientOpts2}]),
-    %% Check that we get the EARLY DATA keylog event
-    receive
-        {keylog, #{items := EarlyKeylog}} ->
-            ["CLIENT_EARLY_TRAFFIC_SECRET" ++ _| _] = EarlyKeylog
-    end,
-    skip_keylogs(4), %% HS and two traffic secrets so they do not end up
-                     %% in check_result
-
 
     ssl_test_lib:check_result(Server0, ok, Client1, ok),
 
diff --git a/lib/ssl/test/tls_1_3_version_SUITE.erl b/lib/ssl/test/tls_1_3_version_SUITE.erl
index db708bb364..245c88397c 100644
--- a/lib/ssl/test/tls_1_3_version_SUITE.erl
+++ b/lib/ssl/test/tls_1_3_version_SUITE.erl
@@ -29,6 +29,8 @@
          groups/0,
          init_per_suite/1,
          init_per_group/2,
+         init_per_testcase/2,
+         end_per_testcase/2,
          end_per_suite/1,
          end_per_group/2
         ]).
@@ -79,7 +81,9 @@
          client_cert_fail_alert_active/0,
          client_cert_fail_alert_active/1,
          client_cert_fail_alert_passive/0,
-         client_cert_fail_alert_passive/1
+         client_cert_fail_alert_passive/1,
+         keylog_on_alert/0,
+         keylog_on_alert/1
         ]).
 
 
@@ -89,7 +93,7 @@
 %%--------------------------------------------------------------------
 %% Common Test interface functions -----------------------------------
 %%--------------------------------------------------------------------
-all() -> 
+all() ->
     [
      cert_groups()
     ].
@@ -117,7 +121,8 @@ tls_1_3_1_2_tests() ->
      middle_box_client_tls_v2_session_reused,
      renegotiate_error,
      client_cert_fail_alert_active,
-     client_cert_fail_alert_passive
+     client_cert_fail_alert_passive,
+     keylog_on_alert
     ].
 legacy_tests() ->
     [tls_client_tls10_server,
@@ -137,10 +142,10 @@ init_per_suite(Config) ->
     catch crypto:stop(),
     try crypto:start() of
 	ok ->
-            case ssl_test_lib:sufficient_crypto_support('tlsv1.3') of                
+            case ssl_test_lib:sufficient_crypto_support('tlsv1.3') of
                 true ->
                     ssl_test_lib:clean_start(),
-                    [{client_type, erlang}, {server_type, erlang} | 
+                    [{client_type, erlang}, {server_type, erlang} |
                      Config];
                 false ->
                     {skip, "Insufficient crypto support for TLS-1.3"}
@@ -158,18 +163,18 @@ init_per_group(rsa, Config0) ->
     COpts = proplists:get_value(client_rsa_opts, Config),
     SOpts = proplists:get_value(server_rsa_opts, Config),
     [{client_type, erlang},
-     {server_type, erlang},{client_cert_opts, COpts}, {server_cert_opts, SOpts} | 
+     {server_type, erlang},{client_cert_opts, COpts}, {server_cert_opts, SOpts} |
      lists:delete(server_cert_opts, lists:delete(client_cert_opts, Config))];
 init_per_group(ecdsa, Config0) ->
     PKAlg = crypto:supports(public_keys),
-    case lists:member(ecdsa, PKAlg) andalso 
+    case lists:member(ecdsa, PKAlg) andalso
         (lists:member(ecdh, PKAlg) orelse lists:member(dh, PKAlg)) of
         true ->
             Config = ssl_test_lib:make_ecdsa_cert(Config0),
             COpts = proplists:get_value(client_ecdsa_opts, Config),
             SOpts = proplists:get_value(server_ecdsa_opts, Config),
             [{client_type, erlang},
-             {server_type, erlang},{client_cert_opts, COpts}, {server_cert_opts, SOpts} | 
+             {server_type, erlang},{client_cert_opts, COpts}, {server_cert_opts, SOpts} |
              lists:delete(server_cert_opts, lists:delete(client_cert_opts, Config))];
         false ->
             {skip, "Missing EC crypto support"}
@@ -177,6 +182,15 @@ init_per_group(ecdsa, Config0) ->
 
 end_per_group(GroupName, Config) ->
     ssl_test_lib:end_per_group(GroupName, Config).
+
+init_per_testcase(_TestCase, Config) ->
+    ssl_test_lib:ct_log_supported_protocol_versions(Config),
+    ct:timetrap({seconds, 20}),
+    Config.
+
+end_per_testcase(_TestCase, Config) ->
+    Config.
+
 %%--------------------------------------------------------------------
 %% Test Cases --------------------------------------------------------
 %%--------------------------------------------------------------------
@@ -193,9 +207,8 @@ tls13_client_tls12_server(Config) when is_list(Config) ->
     ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
 
 
-    
 tls13_client_with_ext_tls12_server() ->
-     [{doc,"Test basic connection between TLS 1.2 server and TLS 1.3 client when " 
+     [{doc,"Test basic connection between TLS 1.2 server and TLS 1.3 client when "
        "client has TLS 1.3 specific extensions"}].
 
 tls13_client_with_ext_tls12_server(Config) ->
@@ -215,11 +228,11 @@ tls13_client_with_ext_tls12_server(Config) ->
                                          rsa_pkcs1_sha256,
                                          ecdsa_sha1]}|ClientOpts0],
     ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
-   
+
 tls12_client_tls13_server() ->
     [{doc,"Test that a TLS 1.2 client can connect to a TLS 1.3 server."}].
 
-tls12_client_tls13_server(Config) when is_list(Config) ->    
+tls12_client_tls13_server(Config) when is_list(Config) ->
     ClientOpts = [{versions, ['tlsv1.1', 'tlsv1.2']} |
                   ssl_test_lib:ssl_options(client_cert_opts, Config)],
     ServerOpts =  [{versions, ['tlsv1.3', 'tlsv1.2']},
@@ -234,7 +247,7 @@ tls_client_tls10_server(Config) when is_list(Config) ->
                                         [{key_exchange, fun(srp_rsa)  -> false;
                                                            (srp_anon) -> false;
                                                            (srp_dss) -> false;
-                                                           (_) -> true end}]),        
+                                                           (_) -> true end}]),
     ClientOpts = [{versions, ['tlsv1', 'tlsv1.1', 'tlsv1.2', 'tlsv1.3']},
                   {ciphers, CCiphers} |
                   ssl_test_lib:ssl_options(client_cert_opts, Config)],
@@ -251,14 +264,14 @@ tls_client_tls11_server(Config) when is_list(Config) ->
                                         [{key_exchange, fun(srp_rsa)  -> false;
                                                            (srp_anon) -> false;
                                                            (srp_dss) -> false;
-                                                           (_) -> true end}]),    
+                                                           (_) -> true end}]),
     ClientOpts = [{versions,
                    ['tlsv1', 'tlsv1.1', 'tlsv1.2', 'tlsv1.3']},
                   {ciphers, CCiphers} |
                   ssl_test_lib:ssl_options(client_cert_opts, Config)],
     ServerOpts =  [{versions,['tlsv1.1']},
                    {verify, verify_peer}, {fail_if_no_peer_cert, true},
-                   {ciphers, ssl:cipher_suites(all, 'tlsv1.1')}  
+                   {ciphers, ssl:cipher_suites(all, 'tlsv1.1')}
                   | ssl_test_lib:ssl_options(server_cert_opts, Config)],
     ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
 
@@ -280,7 +293,7 @@ tls10_client_tls_server(Config) when is_list(Config) ->
                                         [{key_exchange, fun(srp_rsa)  -> false;
                                                            (srp_anon) -> false;
                                                            (srp_dss) -> false;
-                                                           (_) -> true end}]),    
+                                                           (_) -> true end}]),
     ClientOpts = [{versions, ['tlsv1']},
                   {ciphers, ssl:cipher_suites(all, 'tlsv1')} |
                   ssl_test_lib:ssl_options(client_cert_opts, Config)],
@@ -298,7 +311,6 @@ tls11_client_tls_server(Config) when is_list(Config) ->
                                                            (srp_anon) -> false;
                                                            (srp_dss) -> false;
                                                            (_) -> true end}]),
-    
     ClientOpts = [{versions, ['tlsv1.1']},
                   {ciphers, ssl:cipher_suites(all, 'tlsv1.1')} |
                   ssl_test_lib:ssl_options(client_cert_opts, Config)],
@@ -339,7 +351,7 @@ legacy_tls12_server_tls_client(Config) when is_list(Config) ->
     SHA = sha384,
     Prop = proplists:get_value(tc_group_properties, Config),
     Alg = proplists:get_value(name, Prop),
-    #{client_config := ClientOpts0, 
+    #{client_config := ClientOpts0,
       server_config := ServerOpts0} = ssl_test_lib:make_cert_chains_der(Alg, [{server_chain,
                                                                                [[{digest, SHA}],
                                                                                 [{digest, SHA}],
@@ -580,9 +592,9 @@ client_cert_fail_alert_passive(Config) when is_list(Config) ->
                   {certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts0)],
     ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}| ServerOpts0],
     alert_passive(ServerOpts, ClientOpts, recv,
-                  ServerNode, Hostname),
+                  ServerNode, Hostname, unknown_ca),
     alert_passive(ServerOpts, ClientOpts, setopts,
-                  ServerNode, Hostname).
+                  ServerNode, Hostname, unknown_ca).
 
 tls13_client_tls11_server() ->
     [{doc,"Test that a TLS 1.3 client gets old server alert from TLS 1.0 server."}].
@@ -591,6 +603,83 @@ tls13_client_tls11_server(Config) when is_list(Config) ->
     ServerOpts =  [{versions, ['tlsv1']} | ssl_test_lib:ssl_options(server_cert_opts, Config)],
     ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, insufficient_security).
 
+keylog_on_alert() ->
+    [{doc,"Test that keep_secrets keylog_hs callback, if specified, "
+      "is called with keylog info when handshake alert is raised"}].
+
+keylog_on_alert(Config) when is_list(Config) ->
+    ssl:clear_pem_cache(),
+    {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+    ClientOpts0 = ssl_test_lib:ssl_options(extra_client, client_cert_opts, Config),
+    ServerOpts0 = ssl_test_lib:ssl_options(extra_server, server_cert_opts, Config),
+    PrivDir = proplists:get_value(priv_dir, Config),
+    NewClientCertFile = filename:join(PrivDir, "client_invalid_cert.pem"),
+    create_bad_client_certfile(NewClientCertFile, ClientOpts0),
+    ClientOpts = [{versions, ['tlsv1.3']}, {active, false}, {certfile, NewClientCertFile}|
+                  proplists:delete(certfile, ClientOpts0)],
+    ServerOpts =  [{versions, ['tlsv1.3']},
+                   {verify, verify_peer}, {fail_if_no_peer_cert, true}
+                  | ServerOpts0],
+
+    Me = self(),
+    Fun = fun(AlertInfo) ->
+                  Me ! {alert_info, AlertInfo}
+          end,
+    keylog_alert_passive([{keep_secrets, {keylog_hs, Fun}} | ServerOpts], ClientOpts, recv,
+                         ServerNode, Hostname),
+
+    receive_server_keylog_for_cert_alert(),
+
+    keylog_alert_passive(ServerOpts,
+                         [{keep_secrets, {keylog_hs, Fun}} | ClientOpts], recv,
+                         ServerNode, Hostname),
+
+    receive_client_keylog_for_cert_alert(),
+
+    ClientNoCert = proplists:delete(keyfile, proplists:delete(certfile, ClientOpts0)),
+    keylog_alert_passive([{keep_secrets, {keylog_hs, Fun}} | ServerOpts],
+                         [{active, false} | ClientNoCert],
+                         recv, ServerNode, Hostname),
+    receive_server_keylog_for_cert_alert().
+
+receive_server_keylog_for_cert_alert() ->
+    %% This alert will be decrypted with application secrets
+    %% as client is already in connection
+    receive
+        {alert_info, #{items := SKeyLog}} ->
+            case keylog_prefixes(["CLIENT_HANDSHAKE_TRAFFIC_SECRET",
+                                  "SERVER_HANDSHAKE_TRAFFIC_SECRET",
+                                  "SERVER_TRAFFIC_SECRET_0"], SKeyLog) of
+                true ->
+                    ok;
+                false ->
+                    ct:fail({server_received, SKeyLog})
+            end
+    end.
+
+receive_client_keylog_for_cert_alert() ->
+    receive
+        {alert_info, #{items := CKeyLog}} ->
+            case keylog_prefixes(["CLIENT_HANDSHAKE_TRAFFIC_SECRET",
+                                  "SERVER_HANDSHAKE_TRAFFIC_SECRET",
+                                  "CLIENT_TRAFFIC_SECRET_0",
+                                  "SERVER_TRAFFIC_SECRET_0"], CKeyLog) of
+                true ->
+                    ok;
+                false ->
+                    ct:fail({client_received, CKeyLog})
+            end
+    end.
+keylog_prefixes([], []) ->
+    true;
+keylog_prefixes([Prefix | Prefixes], [Secret | Secrets]) ->
+    case lists:prefix(Prefix, Secret) of
+        true  ->
+            keylog_prefixes(Prefixes, Secrets);
+        false ->
+            false
+    end.
+
 %%--------------------------------------------------------------------
 %% Internal functions and callbacks -----------------------------------
 %%--------------------------------------------------------------------
@@ -622,7 +711,7 @@ check_session_id(Socket, Expected) ->
     end.
 
 alert_passive(ServerOpts, ClientOpts, Function,
-              ServerNode, Hostname) ->
+              ServerNode, Hostname, AlertAtom) ->
     Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
                                         {from, self()},
                                         {mfa, {ssl_test_lib, no_result, []}},
@@ -632,16 +721,36 @@ alert_passive(ServerOpts, ClientOpts, Function,
     ct:sleep(500),
     case Function of
         recv ->
-            {error, {tls_alert, {unknown_ca,_}}} = ssl:recv(Socket, 0);
+            {error, {tls_alert, {AlertAtom,_}}} = ssl:recv(Socket, 0);
         setopts ->
             {error, {tls_alert, {unknown_ca,_}}} = ssl:setopts(Socket, [{active, once}])
     end.
 
-create_bad_client_certfile(NewClientCertFile, ClientOpts0) ->
-    KeyFile =  proplists:get_value(keyfile, ClientOpts0),
+keylog_alert_passive(ServerOpts, ClientOpts, Function,
+                     ServerNode, Hostname) ->
+    Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+                                        {from, self()},
+                                        {mfa, {ssl_test_lib, no_result, []}},
+                                        {options, ServerOpts}]),
+    Port = ssl_test_lib:inet_port(Server),
+
+    Fun = fun() ->
+                  {ok, Socket} = ssl:connect(Hostname, Port, ClientOpts),
+                  case Function of
+                      recv ->
+                          ssl:recv(Socket, 0);
+                      setopts ->
+                          ssl:setopts(Socket, [{active, once}])
+                  end
+          end,
+    %% Execute in other process and let test case detect key-log message.
+    spawn_link(Fun).
+
+create_bad_client_certfile(NewClientCertFile, ClientOpts) ->
+    KeyFile =  proplists:get_value(keyfile, ClientOpts),
     [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile),
     Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)),
-    ClientCertFile = proplists:get_value(certfile, ClientOpts0),
+    ClientCertFile = proplists:get_value(certfile, ClientOpts),
 
     [{'Certificate', ClientDerCert, _}] = ssl_test_lib:pem_to_der(ClientCertFile),
     ClientOTPCert = public_key:pkix_decode_cert(ClientDerCert, otp),
-- 
2.51.0

openSUSE Build Service is sponsored by