File 1664-ssl-Improve-maintainability-and-correct-keylogging.patch of Package erlang
From b29d8afbf185cd49e78998b58ababdbde4c39126 Mon Sep 17 00:00:00 2001
From: Ingela Anderton Andin <ingela@erlang.org>
Date: Wed, 10 Dec 2025 13:40:34 +0100
Subject: [PATCH 4/4] ssl: Improve maintainability and correct keylogging
Remove confusing update function and adjust key logging functionality.
Also add timeout to clear secrets on client side if connection
apparently succeeds and keylog_hs is specified.
The adjustment also means that skipping of key log messages in
ssl_session_SUITE test can be changed back as correction of
connection steping also requires a change in the key
logging functionality.
---
lib/ssl/src/ssl_gen_statem.erl | 148 +++++---
lib/ssl/src/tls_client_connection_1_3.erl | 26 +-
lib/ssl/src/tls_gen_connection_1_3.erl | 16 +-
lib/ssl/src/tls_handshake_1_3.erl | 396 +++++++++++-----------
lib/ssl/src/tls_record.erl | 2 +-
lib/ssl/src/tls_server_connection_1_3.erl | 57 +---
lib/ssl/test/ssl_session_ticket_SUITE.erl | 4 +-
lib/ssl/test/tls_1_3_version_SUITE.erl | 123 ++++---
8 files changed, 423 insertions(+), 349 deletions(-)
diff --git a/lib/ssl/src/ssl_gen_statem.erl b/lib/ssl/src/ssl_gen_statem.erl
index e9994be201..a345ecebb2 100644
--- a/lib/ssl/src/ssl_gen_statem.erl
+++ b/lib/ssl/src/ssl_gen_statem.erl
@@ -1976,16 +1976,16 @@ connection_info(#state{handshake_env = #handshake_env{sni_hostname = SNIHostname
{sni_hostname, SNIHostname},
{srp_username, SrpUsername} | CurveInfo] ++ MFLInfo ++ ssl_options_list(Opts).
-security_info(#state{connection_states = ConnectionStates,
+security_info(#state{connection_states = #{current_read := Read,
+ current_write := Write},
static_env = #static_env{role = Role},
connection_env = #connection_env{negotiated_version = Version},
ssl_options = Opts,
protocol_specific = ProtocolSpecific}) ->
- ReadState = ssl_record:current_connection_state(ConnectionStates, read),
#{security_parameters :=
#security_parameters{client_random = ClientRand,
server_random = ServerRand,
- master_secret = MasterSecret}} = ReadState,
+ master_secret = MasterSecret}} = Read,
SecInfo = [{client_random, ClientRand},
{server_random, ServerRand}, {master_secret, MasterSecret}],
KeepSecrets = maps:get(keep_secrets, Opts, false),
@@ -1995,30 +1995,31 @@ security_info(#state{connection_states = ConnectionStates,
%% to be able to run in user process context
[{keep_secrets, false} | SecInfo];
_ -> %% true or keylog fun tuple
- maybe_security_info_1_3(Version, ReadState, ProtocolSpecific, Role)
+ maybe_security_info_1_3(Version, Read, Write, ProtocolSpecific, Role)
++ SecInfo
end.
-maybe_security_info_1_3(Version, #{security_parameters :=
- #security_parameters{application_traffic_secret = AppTrafSecretRead,
- client_early_data_secret = EarlyData
- }} = ReadState,
- ProtocolSpecific, Role) when ?TLS_GTE(Version, ?TLS_1_3)->
+maybe_security_info_1_3(Version,
+ #{security_parameters :=
+ #security_parameters{application_traffic_secret = AppTrafSecretRead,
+ client_early_data_secret = EarlyData
+ }} = Read, Write,
+ ProtocolSpecific, Role) when ?TLS_GTE(Version, ?TLS_1_3)->
N = maps:get(num_key_updates, ProtocolSpecific, 0),
Sender = maps:get(sender, ProtocolSpecific, undefined),
case Role of
server ->
tls_handshake_1_3:early_data_secret(EarlyData) ++
- tls_handshake_1_3:hs_traffic_secrets(ReadState) ++
+ tls_handshake_1_3:hs_traffic_secrets(Read, Write) ++
[{client_traffic_secret, AppTrafSecretRead, N},
{Role, Sender}];
client ->
tls_handshake_1_3:early_data_secret(EarlyData) ++
- tls_handshake_1_3:hs_traffic_secrets(ReadState) ++
+ tls_handshake_1_3:hs_traffic_secrets(Read, Write) ++
[{server_traffic_secret, AppTrafSecretRead, N},
{Role, Sender}]
end;
-maybe_security_info_1_3(_,_,_,_) ->
+maybe_security_info_1_3(_,_,_,_,_) ->
[].
record_cb(tls) ->
@@ -2244,56 +2245,111 @@ maybe_keylog_hs_callback(#alert{level = ?FATAL}, StateName,
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};
-%% Server alert for certificate validation can happen when client is in connection state already.
-keylog_hs_alert(connection, #state{static_env = #static_env{role = client},
- connection_env =
- #connection_env{negotiated_version = TlsVersion},
- connection_states = ConnectionStates,
- protocol_specific = #{sender := Sender} = PS})
+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) ->
- CSRead = #{security_parameters :=
- #security_parameters{application_traffic_secret = SecretRead,
- client_random = ClientRandom,
- prf_algorithm = Prf}}
- = ssl_record:current_connection_state(ConnectionStates, read),
- NRead = maps:get(num_key_updates, PS, 0),
- {ok, SecretWrite, NWrite} = call(Sender, get_application_traffic_secret),
- {keylog_hs_1_3(CSRead) ++
- ssl_logger:keylog_traffic_1_3(client, ClientRandom, Prf, SecretWrite, NWrite) ++
- ssl_logger:keylog_traffic_1_3(server, ClientRandom, Prf, SecretRead, NRead), ClientRandom};
-keylog_hs_alert(_, #state{connection_env =
+ #{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 = ConnectionStates})
+ connection_states = #{current_read := Read,
+ current_write := Write
+ }})
when ?TLS_GTE(TlsVersion, ?TLS_1_3) ->
- CSRead = #{security_parameters :=
- #security_parameters{client_random = ClientRandom}}
- = ssl_record:pending_connection_state(ConnectionStates, read),
- {keylog_hs_1_3(CSRead), ClientRandom};
-keylog_hs_alert(_,_) -> % NOT Relevant pre 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(#{client_handshake_traffic_secret := ClientHSTrafficSecret,
- server_handshake_traffic_secret := ServerHSTrafficSecret,
- security_parameters := #security_parameters{client_random = ClientRandomBin,
- prf_algorithm = Prf,
- client_early_data_secret = EarlySecret}}) ->
+keylog_hs_1_3(ClientRandomBin, Prf, EarlySecret, ClientSecret, ServerSecret) ->
HSItems =
- ssl_logger:keylog_hs(ClientRandomBin, Prf, ClientHSTrafficSecret, ServerHSTrafficSecret),
+ ssl_logger:keylog_hs(ClientRandomBin, Prf, ClientSecret, ServerSecret),
case EarlySecret of
undefined ->
HSItems;
_ ->
[ssl_logger:keylog_early_data(ClientRandomBin, Prf, EarlySecret) | HSItems]
- end;
-keylog_hs_1_3(_) ->
- [].
+ 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}.
-
%%%################################################################
%%%#
%%%# Tracing
diff --git a/lib/ssl/src/tls_client_connection_1_3.erl b/lib/ssl/src/tls_client_connection_1_3.erl
index 7870b6b603..834bc8b17d 100644
--- a/lib/ssl/src/tls_client_connection_1_3.erl
+++ b/lib/ssl/src/tls_client_connection_1_3.erl
@@ -458,12 +458,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),
@@ -482,6 +483,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, ?STATE(connection), State, [{timeout, 1000, forget}]};
+ _ ->
+ {keep_state, State}
+ end;
+connection(timeout, forget, State) ->
+ {next_state, ?STATE(connection), 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_1_3.erl b/lib/ssl/src/tls_gen_connection_1_3.erl
index f42f6a4072..6e52b9f991 100644
--- a/lib/ssl/src/tls_gen_connection_1_3.erl
+++ b/lib/ssl/src/tls_gen_connection_1_3.erl
@@ -52,7 +52,7 @@
do_maybe/0]).
%%--------------------------------------------------------------------
-%% Internal API
+%% Internal API
%%--------------------------------------------------------------------
initial_state(Role, Sender, Host, Port, Socket,
{SSLOptions, SocketOptions, Trackers}, User,
@@ -247,13 +247,17 @@ 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 = CS} = State) ->
+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.
- Read0 = #{security_parameters := SecParams} = ssl_record:current_connection_state(CS, read),
- Read1 = Read0#{security_parameters => SecParams#security_parameters{client_early_data_secret = undefined}},
- Read = maps:without([client_handshake_traffic_secret, server_handshake_traffic_secret], Read1),
- State#state{connection_states = CS#{current_read => Read}};
+ #{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.
diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl
index 3a789a5e2c..aa3638fa9d 100644
--- a/lib/ssl/src/tls_handshake_1_3.erl
+++ b/lib/ssl/src/tls_handshake_1_3.erl
@@ -80,14 +80,14 @@
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,
calculate_write_traffic_secrets/1,
maybe_calculate_resumption_master_secret/1,
early_data_secret/1,
- hs_traffic_secrets/1,
+ hs_traffic_secrets/2,
+ forget_master_secret/1,
encode_early_data/2,
get_ticket_data/3,
ciphers_for_early_data/1,
@@ -873,49 +873,149 @@ 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, HKDFAlgo, ClientHSTrafficSecret, ServerHSTrafficSecret),
-
- update_pending_connection_states(State1, HandshakeSecret, undefined,
- undefined, undefined,
- ReadKey, ReadIV, ReadFinishedKey,
- WriteKey, WriteIV, WriteFinishedKey).
+ {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.
+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,
@@ -933,7 +1033,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}),
@@ -946,20 +1047,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, HKDFAlgo),
- 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, HKDFAlgo),
- 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.
@@ -968,12 +1070,14 @@ early_data_secret(undefined) ->
early_data_secret(Secret) ->
[{client_early_data_secret, Secret}].
-hs_traffic_secrets(#{client_handshake_traffic_secret := ClientHSTrafficSecret,
- server_handshake_traffic_secret := ServerHSTrafficSecret}) ->
- [{client_handshake_traffic_secret, ClientHSTrafficSecret},
- {server_handshake_traffic_secret, ServerHSTrafficSecret}];
-hs_traffic_secrets(_) ->
- [].
+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 := Keep}, EarlySecret,
#{security_parameters := SecParams0} = CSState,
@@ -1067,42 +1171,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} =
@@ -1110,29 +1178,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{
@@ -1142,31 +1188,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)
@@ -1249,78 +1311,6 @@ overwrite_client_random(ConnectionState = #{security_parameters := SecurityParam
ConnectionState#{security_parameters => SecurityParameters}.
-maybe_store_handshake_traffic_secret(#state{connection_states =
- #{pending_read := PendingRead0}= CS,
- ssl_options = #{keep_secrets := Keep}} = State, Prf,
- ClientHSTrafficSecret, ServerHSTrafficSecret) when Keep =/= false ->
- case Keep of
- {keylog, Fun} ->
- #{security_parameters := SecParams0} = PendingRead0,
- ClientRand = SecParams0#security_parameters.client_random,
- KeyLog = ssl_logger:keylog_hs(ClientRand, Prf, ClientHSTrafficSecret, ServerHSTrafficSecret),
- ssl_logger:keylog(KeyLog, ClientRand, Fun),
- State;
- _ ->
- PendingRead = store_handshake_traffic_secret(PendingRead0, ClientHSTrafficSecret, ServerHSTrafficSecret),
- State#state{connection_states = CS#{pending_read => PendingRead}}
- end;
-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 420089f4d1..ca32f81267 100644
--- a/lib/ssl/src/tls_record.erl
+++ b/lib/ssl/src/tls_record.erl
@@ -459,7 +459,7 @@ initial_connection_state(ConnectionEnd, MaxEarlyDataSize) ->
mac_secret => undefined,
early_data => #{pending_early_data_size => MaxEarlyDataSize,
trial_decryption => false,
- early_data_expected => false
+ early_data_accepted => false
},
reneg => #{secure_renegotiation => undefined,
client_verify_data => undefined,
diff --git a/lib/ssl/src/tls_server_connection_1_3.erl b/lib/ssl/src/tls_server_connection_1_3.erl
index e907258084..3630ac70fe 100644
--- a/lib/ssl/src/tls_server_connection_1_3.erl
+++ b/lib/ssl/src/tls_server_connection_1_3.erl
@@ -258,7 +258,6 @@ negotiated(internal, {start_handshake, _} = Message, 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) ->
@@ -552,14 +551,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
@@ -629,8 +621,11 @@ session_resumption({#state{ssl_options = #{session_tickets := Tickets},
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),
- {ok, {State, negotiated, {Index, PSK}}};
+ State = #state{connection_states = #{pending_read := Read0} = CS} =
+ maybe_store_peer_cert(State1, PeerCert),
+ #{early_data := EarlyData0} = Read0,
+ Read = Read0#{early_data => EarlyData0#{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,
@@ -639,11 +634,16 @@ session_resumption({#state{ssl_options = #{session_tickets := Tickets},
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),
+ #{early_data := EarlyData0} = Read0,
+ Read = Read0#{early_data => EarlyData0#{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) ->
@@ -803,13 +803,6 @@ send_hello_retry_request(State0, _, _, _) ->
%% Suitable key found.
{ok, {State0, negotiated}}.
-update_current_read(#state{connection_states = CS} = State, TrialDecryption, EarlyDataExpected) ->
- #{early_data := EarlyData0} = Read0 = ssl_record:current_connection_state(CS, read),
- EarlyData = EarlyData0#{trial_decryption => TrialDecryption,
- early_data_accepted => EarlyDataExpected},
- Read = Read0#{early_data := EarlyData},
- 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},
@@ -932,23 +925,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 1ce8d29e53..c721f3f010 100644
--- a/lib/ssl/test/ssl_session_ticket_SUITE.erl
+++ b/lib/ssl/test/ssl_session_ticket_SUITE.erl
@@ -1222,7 +1222,7 @@ 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
+ skip_keylogs(3), %% HS and two traffic secrets
ssl_test_lib:check_result(Server0, ok, Client0, ok),
@@ -1247,7 +1247,7 @@ early_data_basic(Config) when is_list(Config) ->
{keylog, #{items := EarlyKeylog}} ->
["CLIENT_EARLY_TRAFFIC_SECRET" ++ _| _] = EarlyKeylog
end,
- skip_keylogs(4), %% HS and two traffic secrets so they do not end up
+ skip_keylogs(3), %% 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 913b7eafff..83001fe5e2 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
]).
@@ -91,7 +93,7 @@
%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
-all() ->
+all() ->
[
cert_groups()
].
@@ -139,10 +141,10 @@ legacy_tests() ->
init_per_suite(Config) ->
case application:ensure_started(crypto) 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"}
@@ -160,18 +162,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"}
@@ -179,6 +181,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 --------------------------------------------------------
%%--------------------------------------------------------------------
@@ -195,9 +206,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) ->
@@ -217,11 +227,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']},
@@ -236,7 +246,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)],
@@ -253,14 +263,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).
@@ -282,7 +292,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)],
@@ -300,7 +310,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)],
@@ -341,7 +350,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}],
@@ -608,55 +617,61 @@ keylog_on_alert(Config) when is_list(Config) ->
Fun = fun(AlertInfo) ->
Me ! {alert_info, AlertInfo}
end,
- alert_passive([{keep_secrets, {keylog_hs, Fun}} | ServerOpts], ClientOpts, recv,
- ServerNode, Hostname, unknown_ca),
+ keylog_alert_passive([{keep_secrets, {keylog_hs, Fun}} | ServerOpts], ClientOpts, recv,
+ ServerNode, Hostname),
- receive_server_keylog_for_client_cert_alert(),
+ receive_server_keylog_for_server_cert_alert(),
- alert_passive(ServerOpts, [{keep_secrets, {keylog_hs, Fun}} | ClientOpts], recv,
- ServerNode, Hostname, unknown_ca),
+ keylog_alert_passive(ServerOpts,
+ [{keep_secrets, {keylog_hs, Fun}} | ClientOpts], recv,
+ ServerNode, Hostname),
receive_client_keylog_for_client_cert_alert(),
ClientNoCert = proplists:delete(keyfile, proplists:delete(certfile, ClientOpts0)),
- alert_passive([{keep_secrets, {keylog_hs, Fun}} | ServerOpts], [{active, false} | ClientNoCert], recv,
- ServerNode, Hostname, certificate_required),
+ keylog_alert_passive([{keep_secrets, {keylog_hs, Fun}} | ServerOpts],
+ [{active, false} | ClientNoCert],
+ recv, ServerNode, Hostname),
+ receive_server_keylog_for_server_cert_alert().
- receive_server_keylog_for_client_cert_alert().
-
-receive_server_keylog_for_client_cert_alert() ->
+receive_server_keylog_for_server_cert_alert() ->
%% This alert will be decrypted with application secrets
%% as client is already in connection
- receive
- {alert_info, #{items := SKeyLog1}} ->
- case SKeyLog1 of
- ["SERVER_TRAFFIC_SECRET_0"++_] ->
- ok;
- S1Other ->
- ct:fail({server_received, S1Other})
- end
- end,
-
receive
{alert_info, #{items := SKeyLog}} ->
- case SKeyLog of
- ["CLIENT_HANDSHAKE_TRAFFIC_SECRET"++_,_|_] ->
+ case keylog_prefixes(["CLIENT_HANDSHAKE_TRAFFIC_SECRET",
+ "SERVER_HANDSHAKE_TRAFFIC_SECRET",
+ "SERVER_TRAFFIC_SECRET_0"], SKeyLog) of
+ true ->
ok;
- SOther ->
- ct:fail({server_received, SOther})
+ false ->
+ ct:fail({server_received, SKeyLog})
end
end.
receive_client_keylog_for_client_cert_alert() ->
receive
{alert_info, #{items := CKeyLog}} ->
- case CKeyLog of
- ["CLIENT_HANDSHAKE_TRAFFIC_SECRET"++_,_,_,_|_] ->
+ case keylog_prefixes(["CLIENT_HANDSHAKE_TRAFFIC_SECRET",
+ "SERVER_HANDSHAKE_TRAFFIC_SECRET",
+ "CLIENT_TRAFFIC_SECRET_0",
+ "SERVER_TRAFFIC_SECRET_0"], CKeyLog) of
+ true ->
ok;
- COther ->
- ct:fail({client_received, COther})
+ 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 -----------------------------------
%%--------------------------------------------------------------------
@@ -703,6 +718,26 @@ alert_passive(ServerOpts, ClientOpts, Function,
{error, {tls_alert, {unknown_ca,_}}} = ssl:setopts(Socket, [{active, once}])
end.
+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),
--
2.51.0