File 0711-ssl-Handle-encryption-state-stepping-properly.patch of Package erlang

From 715dec5e0471b65dfb26aab6f5bebc025862d0ba Mon Sep 17 00:00:00 2001
From: Ingela Anderton Andin <ingela@erlang.org>
Date: Wed, 26 Nov 2025 16:19:56 +0100
Subject: [PATCH 1/5] ssl: Handle encryption state stepping properly

---
 lib/ssl/src/ssl_logger.erl                |  63 +++++++++
 lib/ssl/src/tls_client_connection_1_3.erl |  24 ++--
 lib/ssl/src/tls_gen_connection_1_3.erl    |  50 ++++++-
 lib/ssl/src/tls_handshake_1_3.erl         | 152 ++++++++++++++++------
 lib/ssl/src/tls_server_connection_1_3.erl |  50 +++++--
 lib/ssl/test/ssl_session_ticket_SUITE.erl |  19 +++
 6 files changed, 288 insertions(+), 70 deletions(-)

diff --git a/lib/ssl/src/ssl_logger.erl b/lib/ssl/src/ssl_logger.erl
index e9131b28a5..af893c389c 100644
--- a/lib/ssl/src/ssl_logger.erl
+++ b/lib/ssl/src/ssl_logger.erl
@@ -25,6 +25,12 @@
          format/2,
          format/1]).
 
+-export([keylog/3,
+         keylog_early_data/3,
+         keylog_hs/4,
+         keylog_traffic_pre_1_3/2,
+         keylog_traffic_1_3/5]).
+
 -define(DEC2HEX(X),
         if ((X) >= 0) andalso ((X) =< 9) -> (X) + $0;
            ((X) >= 10) andalso ((X) =< 15) -> (X) + $a - 10
@@ -128,11 +134,68 @@ format(#{msg:= {report, Msg}}, _Config0) ->
         _Other ->
             []
     end.
+%%-------------------------------------------------------------------------
+%%  Keylog functions (not erlang logger related)
+%%-------------------------------------------------------------------------
+
+keylog(KeyLog, Random, Fun) ->
+    try
+        Fun(#{items => KeyLog, client_random => Random})
+    catch
+        _:function_clause:FunclauseST ->
+            [_| RestST] = FunclauseST,
+            %% Do not log arguments, our app should not cause any information leak.
+            %% What user fun does with the arguments is the users responsibility.
+            ?LOG_WARNING("user keylogging fun failed with function_clause: ~p ~n", [RestST]);
+        _:Reason:ST ->
+            ?LOG_WARNING("user keylogging fun failed : ~p ~p ~n", [Reason, ST])
+    end.
+
+keylog_early_data(ClientRand, Prf, EarlySecret) ->
+    ClientRandom = binary:decode_unsigned(ClientRand),
+    ClientEarlySecret = keylog_secret(EarlySecret, Prf),
+    [io_lib:format("CLIENT_EARLY_TRAFFIC_SECRET ~64.16.0B ",
+                   [ClientRandom]) ++ ClientEarlySecret].
+
+keylog_hs(ClientRand, Prf, ClientHSSecretBin, ServerHSSecretBin) ->
+    ClientRandom = binary:decode_unsigned(ClientRand),
+    ClientHSecret = keylog_secret(ClientHSSecretBin, Prf),
+    ServerHSecret = keylog_secret(ServerHSSecretBin, Prf),
+    [io_lib:format("CLIENT_HANDSHAKE_TRAFFIC_SECRET ~64.16.0B ",
+                   [ClientRandom]) ++ ClientHSecret,
+     io_lib:format("SERVER_HANDSHAKE_TRAFFIC_SECRET ~64.16.0B ",
+                   [ClientRandom]) ++ ServerHSecret].
+
+keylog_traffic_1_3(Role, ClientRandom, Prf, TrafficSecretBin, N) ->
+    ClientRandBin = binary:decode_unsigned(ClientRandom),
+    TrafficSecret = keylog_secret(TrafficSecretBin, Prf),
+    ClientRand = io_lib:format("~64.16.0B", [ClientRandBin]),
+    case Role of
+        client ->
+            ["CLIENT_TRAFFIC_SECRET_" ++ integer_to_list(N) ++ " "
+             ++ ClientRand ++ " " ++ TrafficSecret];
+        server ->
+            ["SERVER_TRAFFIC_SECRET_" ++ integer_to_list(N) ++ " "
+             ++ ClientRand ++ " " ++ TrafficSecret]
+    end.
+
+keylog_traffic_pre_1_3(ClientRandom, MasterSecret) ->
+    ClientRandBin = binary:decode_unsigned(ClientRandom),
+    MasterSecretBin = binary:decode_unsigned(MasterSecret),
+    [io_lib:format("CLIENT_RANDOM ~64.16.0B ~96.16.0B", [ClientRandBin, MasterSecretBin])].
+
 
 %%-------------------------------------------------------------------------
 %%  Internal functions
 %%-------------------------------------------------------------------------
 
+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)]).
+
 %%-------------------------------------------------------------------------
 %% Handshake Protocol
 %%-------------------------------------------------------------------------
diff --git a/lib/ssl/src/tls_client_connection_1_3.erl b/lib/ssl/src/tls_client_connection_1_3.erl
index d7f78fc851..4c5a6cecf0 100644
--- a/lib/ssl/src/tls_client_connection_1_3.erl
+++ b/lib/ssl/src/tls_client_connection_1_3.erl
@@ -440,7 +440,9 @@ wait_finished(internal = Type, #change_cipher_spec{} = Msg, State) ->
                                                      ?FUNCTION_NAME, State);
 wait_finished(internal,
               #finished{verify_data = VerifyData},
-              #state{static_env = #static_env{protocol_cb = Connection}}
+              #state{static_env = #static_env{protocol_cb = Connection,
+                                              role = Role},
+                     ssl_options = SSLOpts}
               = State0) ->
     {Ref,Maybe} = tls_gen_connection_1_3:do_maybe(),
     try
@@ -458,19 +460,15 @@ wait_finished(internal,
         %% 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),
-        ExporterMasterSecret = tls_handshake_1_3:calculate_exporter_master_secret(State7),
-        State8 = tls_handshake_1_3:forget_master_secret(State7),
-        %% Configure traffic keys
-        State9 = ssl_record:step_encryption_state(State8),
-        {Record, #state{protocol_specific = PS} = State} =
+        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),
+        {Record, State} =
             ssl_gen_statem:prepare_connection(State9, tls_gen_connection),
-
-        tls_gen_connection:next_event(connection, Record,
-                                      State#state{protocol_specific =
-                                                      PS#{exporter_master_secret =>
-                                                              ExporterMasterSecret}},
+        KeepSecrets = maps:get(keep_secrets, SSLOpts, false),
+        tls_gen_connection_1_3:maybe_traffic_keylog_1_3(KeepSecrets, Role,
+                                                        State#state.connection_states, 0),
+        tls_gen_connection:next_event(connection, Record, State,
                                       [{{timeout, handshake}, cancel}])
     catch
         {Ref, #alert{} = Alert} ->
diff --git a/lib/ssl/src/tls_gen_connection_1_3.erl b/lib/ssl/src/tls_gen_connection_1_3.erl
index db1038de1c..7fa76401c1 100644
--- a/lib/ssl/src/tls_gen_connection_1_3.erl
+++ b/lib/ssl/src/tls_gen_connection_1_3.erl
@@ -46,6 +46,8 @@
          handle_resumption/2,
          send_key_update/2,
          update_cipher_key/2,
+         maybe_traffic_keylog_1_3/4,
+         maybe_forget_hs_secrets/2,
          do_maybe/0]).
 
 %%--------------------------------------------------------------------
@@ -140,13 +142,17 @@ connection(enter, _, State) ->
 connection(internal, #new_session_ticket{} = NewSessionTicket, State) ->
     handle_new_session_ticket(NewSessionTicket, State),
     tls_gen_connection:next_event(?FUNCTION_NAME, no_record, State);
-
-connection(internal, #key_update{} = KeyUpdate, State0) ->
+connection(internal, #key_update{} = KeyUpdate, #state{static_env = #static_env{role = Role},
+                                                       ssl_options = SslOpts} = State0) ->
     case handle_key_update(KeyUpdate, State0) of
-        {ok, State} ->
-            tls_gen_connection:next_event(?FUNCTION_NAME, no_record, State);
+        {ok, #state{connection_states = ConnectionStates, protocol_specific = PS} = State} ->
+            Keep = maps:get(keep_secrets, SslOpts, false),
+            N = maps:get(num_key_updates, PS, 0),
+            maybe_traffic_keylog_1_3(Keep, Role, ConnectionStates, N),
+            tls_gen_connection:next_event(?FUNCTION_NAME, no_record,
+                                          maybe_forget_hs_secrets(Role, N, Keep, State));
         {error, State, Alert} ->
-            ssl_gen_statem:handle_own_alert(Alert, connection, State),
+            ssl_gen_statem:handle_own_alert(Alert, ?FUNCTION_NAME, State),
             tls_gen_connection:next_event(?FUNCTION_NAME, no_record, State)
     end;
 connection({call, From}, negotiated_protocol,
@@ -225,6 +231,19 @@ handle_resumption(#state{handshake_env = HSEnv0} = State, _) ->
     HSEnv = HSEnv0#handshake_env{resumption = true},
     State#state{handshake_env = HSEnv}.
 
+maybe_forget_hs_secrets({keylog_hs, _}, #state{connection_states =
+                                                   #{current_read := Read0,
+                                                     current_write := Write0} = CS} = State) ->
+    %% Server finishes the handshake last and  is able to immediately forget
+    %% hs secrets used for handshake alert logging.
+    #{security_parameters := SecParams} = Read0,
+    Read1 = Read0#{security_parameters =>
+                       SecParams#security_parameters{client_early_data_secret = undefined}},
+    Read = maps:without([client_handshake_traffic_secret], Read1),
+    Write = maps:without([server_handshake_traffic_secret], Write0),
+    State#state{connection_states = CS#{current_read => Read, current_write => Write}};
+maybe_forget_hs_secrets(_, State) ->
+    State.
 do_maybe() ->
     Ref = erlang:make_ref(),
     Ok = fun(ok) -> ok;
@@ -300,6 +319,18 @@ update_cipher_key(ConnStateName, CS0) ->
                             sequence_number => 0},
     CS0#{ConnStateName => ConnState}.
 
+maybe_traffic_keylog_1_3({keylog, Fun}, Role, ConnectionStates, N) ->
+    #{security_parameters := #security_parameters{client_random = ClientRandom,
+                                                  prf_algorithm = Prf,
+                                                  application_traffic_secret = TrafficSecret}}
+        = ssl_record:current_connection_state(ConnectionStates, read),
+    KeyLog = ssl_logger:keylog_traffic_1_3(ssl_gen_statem:opposite_role(Role), ClientRandom,
+                                           Prf, TrafficSecret, N),
+    ssl_logger:keylog(KeyLog, ClientRandom, Fun);
+maybe_traffic_keylog_1_3(_,_,_,_) ->
+    ok.
+
+
 %%--------------------------------------------------------------------
 %% Internal functions
 %%--------------------------------------------------------------------
@@ -431,3 +462,12 @@ exporter(ExporterSecret, Context0, WantedLength, PRFAlgorithm) ->
     HashContext = tls_v1:transcript_hash(Context, PRFAlgorithm),
     tls_v1:hkdf_expand_label(ExporterSecret, <<"exporter">>, HashContext,
                              WantedLength, PRFAlgorithm).
+%% If a client has key_logging for failing handshakes we now know that
+%% it is now safe to clear them. Could be cleared earlier but as this
+%% is debugging functionality we choose not to make complicated
+%% timeout handling to save a little memory for the client.
+%% This is not a problem on the server side.
+maybe_forget_hs_secrets(client, 0, KeepSecrets, State) ->
+    maybe_forget_hs_secrets(KeepSecrets, State);
+maybe_forget_hs_secrets(_,_,_, State) ->
+    State.
diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl
index 7da02e7c22..d7339d4a6f 100644
--- a/lib/ssl/src/tls_handshake_1_3.erl
+++ b/lib/ssl/src/tls_handshake_1_3.erl
@@ -61,11 +61,10 @@
          calculate_exporter_master_secret/1,
          verify_certificate_verify/2,
          validate_finished/2,
-         maybe_calculate_resumption_master_secret/1,
          replace_ch1_with_message_hash/1,
          select_common_groups/2,
          verify_signature_algorithm/2,
-         forget_master_secret/1,
+         prepare_connection/1,
          set_client_random/2,
          handle_pre_shared_key/3,
          update_start_state/2,
@@ -83,6 +82,11 @@
          calculate_traffic_secrets/1,
          calculate_client_early_traffic_secret/5,
          calculate_client_early_traffic_secret/2,
+         calculate_read_traffic_secrets/1,
+         calculate_write_traffic_secrets/1,
+         maybe_calculate_resumption_master_secret/1,
+         early_data_secret/1,
+         hs_traffic_secrets/2,
          encode_early_data/2,
          get_ticket_data/3,
          ciphers_for_early_data/1,
@@ -435,14 +439,9 @@ process_certificate(#certificate_1_3{
 process_certificate(#certificate_1_3{
                        certificate_request_context = <<>>,
                        certificate_list = []},
-                    #state{ssl_options =
-                               #{fail_if_no_peer_cert := true}} = State0) ->
-    %% At this point the client believes that the connection is up and starts using
-    %% its traffic secrets. In order to be able send an proper Alert to the client
-    %% the server should also change its connection state and use the traffic
-    %% secrets.
-    State1 = calculate_traffic_secrets(State0),
-    State = ssl_record:step_encryption_state(State1),
+                    #state{static_env = #static_env{role = server},
+                           ssl_options =
+                               #{fail_if_no_peer_cert := true}} = State) ->
     {error, {?ALERT_REC(?FATAL, ?CERTIFICATE_REQUIRED, certificate_required), State}};
 process_certificate(#certificate_1_3{certificate_list = CertEntries},
                     #state{ssl_options = SslOptions,
@@ -459,8 +458,7 @@ process_certificate(#certificate_1_3{certificate_list = CertEntries},
     case validate_certificate_chain(CertEntries, CertDbHandle, CertDbRef,
                                     SslOptions, CRLDbHandle, Role, Host, OcspState) of
         #alert{} = Alert ->
-            State = update_encryption_state(Role, State0),
-            {error, {Alert, State}};
+            {error, {Alert, State0}};
         {PeerCert, PublicKeyInfo} ->
             State = store_peer_cert(State0, PeerCert, PublicKeyInfo),
             {ok, {State, wait_cv}}
@@ -471,7 +469,7 @@ verify_certificate_verify(#state{static_env = #static_env{role = Role},
                                  handshake_env =
                                      #handshake_env{
                                         public_key_info = PublicKeyInfo,
-                                        tls_handshake_history = HHistory}} = State0,
+                                        tls_handshake_history = HHistory}} = State,
                           #certificate_verify_1_3{
                              algorithm = SignatureScheme,
                              signature = Signature}) ->
@@ -495,15 +493,11 @@ verify_certificate_verify(#state{static_env = #static_env{role = Role},
     %% scheme.
     case verify(THash, ContextString, HashAlgo, SignAlg, Signature, PublicKeyInfo) of
         {ok, true} ->
-            {ok, {State0, wait_finished}};
+            {ok, {State, wait_finished}};
         {ok, false} ->
-            State1 = calculate_traffic_secrets(State0),
-            State = ssl_record:step_encryption_state(State1),
             {error, {?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE,
                                 "Failed to verify CertificateVerify"), State}};
         {error, #alert{} = Alert} ->
-            State1 = calculate_traffic_secrets(State0),
-            State = ssl_record:step_encryption_state(State1),
             {error, {Alert, State}}
     end.
 
@@ -798,18 +792,6 @@ build_content(Context, THash) ->
     Prefix = binary:copy(<<32>>, 64),
     <<Prefix/binary,Context/binary,?BYTE(0),THash/binary>>.
 
-
-%% Sets correct encryption state when sending Alerts in shared states that use different secrets.
-%% - If client: use handshake secrets.
-%% - If server: use traffic secrets as by this time the client's state machine
-%%              already stepped into the 'connection' state.
-update_encryption_state(server, State0) ->
-    State1 = calculate_traffic_secrets(State0),
-    ssl_record:step_encryption_state(State1);
-update_encryption_state(client, State) ->
-    State.
-
-
 validate_certificate_chain(CertEntries, CertDbHandle, CertDbRef,
                            SslOptions, CRLDbHandle, Role, Host, OcspState0) ->
     {Certs, CertExt, OcspState} = split_cert_entries(CertEntries, OcspState0),
@@ -888,6 +870,12 @@ message_hash(ClientHello1, HKDFAlgo) ->
      0,0,ssl_cipher:hash_size(HKDFAlgo),
      crypto:hash(HKDFAlgo, ClientHello1)].
 
+prepare_connection(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).
 
 calculate_handshake_secrets(PublicKey, PrivateKey, SelectedGroup, PSK,
                               #state{connection_states = ConnectionStates,
@@ -925,6 +913,20 @@ calculate_handshake_secrets(PublicKey, PrivateKey, SelectedGroup, PSK,
                                      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)}.
+
+hs_traffic_secret(SecretType, Read, Write) ->
+    %% Depending on if caller is a client or server temporary saved
+    %% handshake secrets for key-logging will be saved in either the
+    %% Read or The Write connection state.
+    case maps:get(SecretType, Read, undefined) of
+        undefined ->
+            maps:get(SecretType, Write, undefined);
+        Secret ->
+            Secret
+    end.
 
 %% Server
 calculate_client_early_traffic_secret(#state{connection_states = ConnectionStates,
@@ -972,7 +974,19 @@ calculate_client_early_traffic_secret(
             State0#state{connection_states = ConnectionStates#{pending_read => PendingRead}}
     end.
 
-
+early_data_secret(undefined) ->
+    [];
+early_data_secret(Secret) ->
+    [{client_early_data_secret, Secret}].
+
+hs_traffic_secrets(Read, Write) ->
+    case hs_secrets(Read, Write) of
+        {undefined, undefined} ->
+            [];
+        {ClientHSTrafficSecret, ServerHSTrafficSecret} ->
+            [{client_handshake_traffic_secret, ClientHSTrafficSecret},
+             {server_handshake_traffic_secret, ServerHSTrafficSecret}]
+    end.
 
 maybe_store_early_data_secret(#{keep_secrets := true}, EarlySecret, State) ->
     #{security_parameters := SecParams0} = State,
@@ -1092,6 +1106,72 @@ calculate_traffic_secrets(#state{
                                      ReadKey, ReadIV, undefined,
                                      WriteKey, WriteIV, undefined).
 
+
+calculate_read_traffic_secrets(#state{
+                             static_env = #static_env{role = Role},
+                                  connection_states = #{pending_read := PendingRead0} =
+                                      ConnectionStates,
+                                  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),
+    State0#state{connection_states = ConnectionStates#{pending_read => PendingRead}}.
+
+calculate_write_traffic_secrets(#state{
+                                   static_env = #static_env{role = Role},
+                                   connection_states = #{pending_write := PendingWrite0} =
+                                       ConnectionStates,
+                             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),
+    #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
+    ServerAppTrafficSecret0 =
+        tls_v1:server_application_traffic_secret_0(HKDFAlgo, MasterSecret, lists:reverse(Messages)),
+
+    %% 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}}.
+
+
 %% X25519, X448
 calculate_shared_secret(OthersKey, MyKey, Group)
   when is_binary(OthersKey) andalso is_binary(MyKey) andalso
@@ -1391,14 +1471,12 @@ get_handshake_context_client(L) ->
 %% CertificateRequest message.
 verify_signature_algorithm(#state{
                               static_env = #static_env{role = Role},
-                              ssl_options = #{signature_algs := LocalSignAlgs}} = State0,
+                              ssl_options = #{signature_algs := LocalSignAlgs}} = State,
                            #certificate_verify_1_3{algorithm = PeerSignAlg}) ->
     case lists:member(PeerSignAlg, filter_tls13_algs(LocalSignAlgs)) of
         true ->
-            {ok, maybe_update_selected_sign_alg(State0, PeerSignAlg, Role)};
+            {ok, maybe_update_selected_sign_alg(State, PeerSignAlg, Role)};
         false ->
-            State1 = calculate_traffic_secrets(State0),
-            State = ssl_record:step_encryption_state(State1),
             {error, {?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE,
                                 "CertificateVerify uses unsupported signature algorithm"), State}}
     end.
@@ -1409,10 +1487,6 @@ maybe_update_selected_sign_alg(#state{session = Session} = State, SignAlg, clien
 maybe_update_selected_sign_alg(State, _, _) ->
     State.
 
-
-
-
-
 context_string(server) ->
     <<"TLS 1.3, server CertificateVerify">>;
 context_string(client) ->
diff --git a/lib/ssl/src/tls_server_connection_1_3.erl b/lib/ssl/src/tls_server_connection_1_3.erl
index 6a4f666879..1ec2bac1a5 100644
--- a/lib/ssl/src/tls_server_connection_1_3.erl
+++ b/lib/ssl/src/tls_server_connection_1_3.erl
@@ -304,24 +304,25 @@ wait_finished(enter, _, State0) ->
     {next_state, ?FUNCTION_NAME, State,[]};
 wait_finished(internal = Type, #change_cipher_spec{} = Msg, State) ->
     tls_gen_connection_1_3:handle_change_cipher_spec(Type, Msg, ?FUNCTION_NAME, State);
-wait_finished(internal,
-             #finished{verify_data = VerifyData}, State0) ->
+wait_finished(internal,  #finished{verify_data = VerifyData},
+              #state{static_env = #static_env{role = Role},
+                     ssl_options = SSLOpts} = State0) ->
+
     {Ref,Maybe} = tls_gen_connection_1_3:do_maybe(),
     try
         Maybe(tls_handshake_1_3:validate_finished(State0, VerifyData)),
-
-        State1 = tls_handshake_1_3:calculate_traffic_secrets(State0),
+        State1 = tls_handshake_1_3:calculate_read_traffic_secrets(State0),
         State2 = tls_handshake_1_3:maybe_calculate_resumption_master_secret(State1),
-        ExporterSecret = tls_handshake_1_3:calculate_exporter_master_secret(State2),
-        State3 = tls_handshake_1_3:forget_master_secret(State2),
-        %% Configure traffic keys
-        State4 = ssl_record:step_encryption_state(State3),
-
+        State3 = ssl_record:step_encryption_state_read(State2),
+        State4 = tls_handshake_1_3:prepare_connection(State3),
         State5 = maybe_send_session_ticket(State4),
-
-        {Record, #state{protocol_specific = PS} = State} = ssl_gen_statem:prepare_connection(State5, tls_gen_connection),
+        {Record, State} = ssl_gen_statem:prepare_connection(State5, 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),
         tls_gen_connection:next_event(connection, Record,
-                                      State#state{protocol_specific = PS#{exporter_master_secret => ExporterSecret}},
+                                      tls_gen_connection_1_3:maybe_forget_hs_secrets(KeepSecrets,
+                                                                                     State),
                                       [{{timeout, handshake}, cancel}])
     catch
         {Ref, #alert{} = Alert} ->
@@ -582,8 +583,11 @@ send_hello_flight({start_handshake, PSK0},
         State9 = Connection:queue_handshake(Finished, State8),
 
         %% Send first flight
-        {State, _} = Connection:send_handshake_flight(State9),
+        {State10, _} = Connection:send_handshake_flight(State9),
 
+        State11 = tls_handshake_1_3:calculate_write_traffic_secrets(State10),
+        State = ssl_record:step_encryption_state_write(State11),
+        maybe_keylog(State),
         {State, NextState}
 
     catch
@@ -922,3 +926,23 @@ 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 dcebd5f0c3..a6295d7818 100644
--- a/lib/ssl/test/ssl_session_ticket_SUITE.erl
+++ b/lib/ssl/test/ssl_session_ticket_SUITE.erl
@@ -1215,6 +1215,8 @@ 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),
 
     Server0 ! {listen, {mfa, {ssl_test_lib,
@@ -1233,6 +1235,15 @@ 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),
 
     process_flag(trap_exit, false),
@@ -1432,3 +1443,11 @@ do_test_mixed(Config, COpts1, COpts2, SOpts1, SOpts2) when is_list(Config) ->
     process_flag(trap_exit, false),
     ssl_test_lib:close(Server1),
     ssl_test_lib:close(Client1).
+
+skip_keylogs(0) ->
+    ok;
+skip_keylogs(N) ->
+    receive
+        {keylog, _} ->
+            skip_keylogs(N-1)
+    end.
-- 
2.51.0

openSUSE Build Service is sponsored by