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