Skip to content

Commit 5c9e217

Browse files
committed
ssl: Correct TLS-1-3 session ticket handling
A documentation and option handling issue. Closes #10464
1 parent ab489c0 commit 5c9e217

File tree

5 files changed

+76
-36
lines changed

5 files changed

+76
-36
lines changed

lib/ssl/doc/guides/using_ssl.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -727,7 +727,7 @@ tickets sent by the server.
727727
_Step 10 (client):_ Receive a new session ticket:
728728

729729
```erlang
730-
Ticket = receive {ssl, session_ticket, {_, TicketData}} -> TicketData end.
730+
Ticket = receive {ssl, session_ticket, Ticket0} -> Ticket0 end.
731731
```
732732

733733
_Step 11 (server):_ Accept a new connection on the server:

lib/ssl/src/ssl.erl

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -162,11 +162,12 @@ Special Erlang node configuration for the application can be found in
162162
key/0,
163163
named_curve/0,
164164
old_cipher_suite/0,
165-
prf_random/0,
165+
prf_random/0,
166166
protocol_extensions/0,
167167
protocol_version/0,
168168
reason/0,
169169
session_id/0,
170+
session_ticket/0,
170171
sign_algo/0,
171172
sign_scheme/0,
172173
signature_algs/0,
@@ -1533,6 +1534,11 @@ different semantics for the client and server.
15331534
{customize_hostname_check, HostNameCheckOpts::list()} |
15341535
{certificate_authorities, boolean()} |
15351536
{stapling, Stapling:: staple | no_staple | map()}.
1537+
-doc(#{group => <<"Client Options">>}).
1538+
-doc """
1539+
1540+
""".
1541+
-nominal session_ticket() :: #{sni := inet:hostname()}.
15361542

15371543
-doc(#{group => <<"Client Options">>}).
15381544
-doc """
@@ -1545,11 +1551,14 @@ Options only relevant for TLS-1.3.
15451551
information to user process in a 3-tuple:
15461552

15471553
```erlang
1548-
{ssl, session_ticket, {SNI, TicketData}}
1554+
{ssl, session_ticket, `Ticket::`(`t:session_ticket/0`)}
15491555
```
15501556

1551-
where `SNI` is the ServerNameIndication and `TicketData` is the extended ticket
1552-
data that can be used in subsequent session resumptions.
1557+
where `Ticket` is a map with information about the created TLS-1.3 session ticket.
1558+
The only key that the user needs to consider is `sni` key to be able to
1559+
provide it as a value in the use_ticket option list of possible tickets
1560+
to use it to attempt session resumption to a server identified by the
1561+
server name indication in `manual` session ticket mode.
15531562

15541563
If it is set to `auto`, the client automatically handles received tickets and
15551564
tries to use them when making new TLS connections (session resumption with
@@ -1606,7 +1615,7 @@ Options only relevant for TLS-1.3.
16061615
""".
16071616
-type client_option_tls13() ::
16081617
{session_tickets, SessionTickets:: disabled | manual | auto} |
1609-
{use_ticket, Tickets::[binary()]} |
1618+
{use_ticket, Tickets::[session_ticket()]} |
16101619
{early_data, binary()} |
16111620
{middlebox_comp_mode, MiddleBoxMode::boolean()}.
16121621

lib/ssl/src/ssl_config.erl

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -590,9 +590,9 @@ process_options(UserSslOpts, SslOpts0, Env) ->
590590
SslOpts1 = opt_protocol_versions(UserSslOptsMap, SslOpts0, Env),
591591
SslOpts2 = opt_verification(UserSslOptsMap, SslOpts1, Env),
592592
SslOpts3 = opt_certs(UserSslOptsMap, SslOpts2, Env),
593-
SslOpts4 = opt_tickets(UserSslOptsMap, SslOpts3, Env),
594-
SslOpts5 = opt_stapling(UserSslOptsMap, SslOpts4, Env),
595-
SslOpts6 = opt_sni(UserSslOptsMap, SslOpts5, Env),
593+
SslOpts4 = opt_sni(UserSslOptsMap, SslOpts3, Env),
594+
SslOpts5 = opt_tickets(UserSslOptsMap, SslOpts4, Env),
595+
SslOpts6 = opt_stapling(UserSslOptsMap, SslOpts5, Env),
596596
SslOpts7 = opt_signature_algs(UserSslOptsMap, SslOpts6, Env),
597597
SslOpts8 = opt_alpn(UserSslOptsMap, SslOpts7, Env),
598598
SslOpts9 = opt_mitigation(UserSslOptsMap, SslOpts8, Env),
@@ -942,7 +942,8 @@ opt_cacerts(UserOpts, #{verify := Verify, log_level := LogLevel, versions := Ver
942942
{new, FileName} -> unambiguous_path(FileName);
943943
{_, FileName} -> FileName
944944
end,
945-
option_incompatible(CaCertFile =:= <<>> andalso CaCerts =:= undefined andalso Verify =:= verify_peer,
945+
option_incompatible(CaCertFile =:= <<>> andalso CaCerts =:= undefined andalso
946+
Verify =:= verify_peer,
946947
[{verify, verify_peer}, {cacerts, undefined}]),
947948

948949
{Where2, CA} = get_opt_bool(certificate_authorities, Role =:= server, UserOpts, Opts),
@@ -957,24 +958,33 @@ opt_cacerts(UserOpts, #{verify := Verify, log_level := LogLevel, versions := Ver
957958
Opts2 = set_opt_new(Where2, certificate_authorities, Role =:= server, CA, Opts1),
958959
Opts2#{cacerts => CaCerts}.
959960

960-
opt_tickets(UserOpts, #{versions := Versions} = Opts, #{role := client}) ->
961-
{_, SessionTickets} = get_opt_of(session_tickets, [disabled,manual,auto], disabled, UserOpts, Opts),
961+
opt_tickets(UserOpts, #{versions := Versions} = Opts,
962+
#{role := client}) ->
963+
{_, SessionTickets} = get_opt_of(session_tickets, [disabled,manual,auto], disabled,
964+
UserOpts, Opts),
962965
assert_version_dep(SessionTickets =/= disabled, session_tickets, Versions, ['tlsv1.3']),
963966

964-
{_, UseTicket} = get_opt_list(use_ticket, undefined, UserOpts, Opts),
965-
option_error(UseTicket =:= [], use_ticket, UseTicket),
966-
option_incompatible(UseTicket =/= undefined andalso SessionTickets =/= manual,
967-
[{use_ticket, UseTicket}, {session_tickets, SessionTickets}]),
967+
{_, UseTickets} = get_opt_list(use_ticket, undefined, UserOpts, Opts),
968+
case (SessionTickets == manual) andalso UseTickets =/= undefined of
969+
true ->
970+
verify_use_tickets(UseTickets, maps:get(server_name_indication, Opts));
971+
_ ->
972+
ok
973+
end,
974+
option_error(UseTickets =:= [], use_ticket, UseTickets),
975+
option_incompatible(UseTickets =/= undefined andalso SessionTickets =/= manual,
976+
[{use_ticket, UseTickets}, {session_tickets, SessionTickets}]),
968977

969978
{_, EarlyData} = get_opt_bin(early_data, undefined, UserOpts, Opts),
970979
option_incompatible(is_binary(EarlyData) andalso SessionTickets =:= disabled,
971980
[early_data, {session_tickets, disabled}]),
972-
option_incompatible(is_binary(EarlyData) andalso SessionTickets =:= manual andalso UseTicket =:= undefined,
981+
option_incompatible(is_binary(EarlyData) andalso SessionTickets =:= manual andalso
982+
UseTickets =:= undefined,
973983
[early_data, {session_tickets, manual}, {use_ticket, undefined}]),
974984

975985
assert_server_only(anti_replay, UserOpts),
976986
assert_server_only(stateless_tickets_seed, UserOpts),
977-
Opts#{session_tickets => SessionTickets, use_ticket => UseTicket, early_data => EarlyData};
987+
Opts#{session_tickets => SessionTickets, use_ticket => UseTickets, early_data => EarlyData};
978988
opt_tickets(UserOpts, #{versions := Versions} = Opts, #{role := server}) ->
979989
{_, SessionTickets} =
980990
get_opt_of(session_tickets,
@@ -995,8 +1005,10 @@ opt_tickets(UserOpts, #{versions := Versions} = Opts, #{role := server}) ->
9951005
{_, undefined} -> undefined;
9961006
{_,AR} when not Stateless ->
9971007
option_incompatible([{anti_replay, AR}, {session_tickets, SessionTickets}]);
998-
{_,'10k'} -> {10, 5, 72985}; %% n = 10000 p = 0.030003564 (1 in 33) m = 72985 (8.91KiB) k = 5
999-
{_,'100k'} -> {10, 5, 729845}; %% n = 10000 p = 0.03000428 (1 in 33) m = 729845 (89.09KiB) k = 5
1008+
%% n = 10000 p = 0.030003564 (1 in 33) m = 72985 (8.91KiB) k = 5
1009+
{_,'10k'} -> {10, 5, 72985};
1010+
%% n = 10000 p = 0.03000428 (1 in 33) m = 729845 (89.09KiB) k = 5
1011+
{_,'100k'} -> {10, 5, 729845};
10001012
{_, {_,_,_} = AR} -> AR;
10011013
{_, AR} -> option_error(anti_replay, AR)
10021014
end,
@@ -1009,6 +1021,13 @@ opt_tickets(UserOpts, #{versions := Versions} = Opts, #{role := server}) ->
10091021
Opts#{session_tickets => SessionTickets, early_data => EarlyData,
10101022
anti_replay => AntiReplay, stateless_tickets_seed => STS}.
10111023

1024+
verify_use_tickets([], _) ->
1025+
true;
1026+
verify_use_tickets([#{sni := SNI} | Tickests], SNI) ->
1027+
verify_use_tickets(Tickests, SNI);
1028+
verify_use_tickets([Ticket | _], SNI) ->
1029+
option_error(ticket_for_other_SNI, {Ticket, SNI}).
1030+
10121031
opt_stapling(UserOpts, #{versions := _Versions} = Opts, #{role := client}) ->
10131032
{Stapling, Nonce} =
10141033
case get_opt(stapling, ?DEFAULT_STAPLING_OPT, UserOpts, Opts) of
@@ -1111,7 +1130,8 @@ valid_signature_algs_cert(#{versions := Versions} = Opts, UserOpts, TlsVersion)
11111130
{_, Schemes} ->
11121131
Schemes
11131132
end.
1114-
valid_signature_algs(AlgCertSchemes0, #{versions := Versions} = Opts, UserOpts, [TlsVersion| _] = TlsVsns) ->
1133+
valid_signature_algs(AlgCertSchemes0, #{versions := Versions} = Opts, UserOpts,
1134+
[TlsVersion| _] = TlsVsns) ->
11151135
case get_opt_list(signature_algs, undefined, UserOpts, Opts) of
11161136
{default, undefined} ->
11171137
%% Smooth upgrade path allow rsa_pkcs1_sha1 for signatures_algs_cert

lib/ssl/test/ssl_api_SUITE.erl

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -986,7 +986,8 @@ handshake_continue_tls13_client(Config) when is_list(Config) ->
986986
{mfa, {ssl_test_lib, send_recv_result_active, []}},
987987
{options, ssl_test_lib:ssl_options([{handshake, hello},
988988
{session_tickets, manual},
989-
{use_ticket, [DummyTicket]},
989+
{use_ticket, [#{sni => net_adm:localhost(),
990+
ticket => DummyTicket}]},
990991
{versions, ['tlsv1.3',
991992
'tlsv1.2',
992993
'tlsv1.1',
@@ -2217,7 +2218,7 @@ customize_defaults(Opts, Role, Host) ->
22172218
-define(OK(EXP, Opts, Role), ?OK(EXP,Opts, Role, [])).
22182219
-define(OK(EXP, Opts, Role, ShouldBeMissing),
22192220
fun() ->
2220-
Host = "dummy.host.org",
2221+
Host = net_adm:localhost(),
22212222
{__DefOpts, __Opts} = customize_defaults(Opts, Role, Host),
22222223
try ssl_config:handle_options(__Opts, Role, Host) of
22232224
{ok, #config{ssl=EXP = __ALL}} ->
@@ -2252,7 +2253,7 @@ customize_defaults(Opts, Role, Host) ->
22522253

22532254
-define(ERR(EXP, Opts, Role),
22542255
fun() ->
2255-
Host = "dummy.host.org",
2256+
Host = net_adm:localhost(),
22562257
{__DefOpts, __Opts} = customize_defaults(Opts, Role, Host),
22572258
try ssl_config:handle_options(__Opts, Role, Host) of
22582259
Other ->
@@ -2286,7 +2287,7 @@ customize_defaults(Opts, Role, Host) ->
22862287

22872288
-define(ERR_UPD(EXP, Opts, Role),
22882289
fun() ->
2289-
Host = "dummy.host.org",
2290+
Host = net_adm:localhost(),
22902291
{__DefOpts, __Opts} = customize_defaults(Opts, Role, Host),
22912292
try ssl_config:handle_options(__Opts, Role, Host) of
22922293
{ok, #config{}} ->
@@ -2718,15 +2719,19 @@ options_dh(Config) -> %% dh dhfile
27182719
ok.
27192720

27202721
options_early_data(_Config) -> %% early_data, session_tickets and use_ticket
2722+
SNI = net_adm:localhost(),
27212723
?OK(#{early_data := undefined, session_tickets := disabled},
27222724
[], client),
27232725
?OK(#{early_data := disabled, session_tickets := disabled, stateless_tickets_seed := undefined},
27242726
[], server),
27252727

27262728
?OK(#{early_data := <<>>, session_tickets := auto},
27272729
[{early_data, <<>>}, {session_tickets, auto}], client),
2728-
?OK(#{early_data := <<>>, session_tickets := manual, use_ticket := [<<1>>]},
2729-
[{early_data, <<>>}, {session_tickets, manual}, {use_ticket, [<<1>>]}],
2730+
?OK(#{early_data := <<>>, session_tickets := manual,
2731+
use_ticket := [#{sni := SNI,
2732+
ticket := <<1>>}]},
2733+
[{early_data, <<>>}, {session_tickets, manual},
2734+
{use_ticket, [#{sni => SNI, ticket => <<1>>}]}],
27302735
client),
27312736

27322737
?OK(#{early_data := enabled, stateless_tickets_seed := <<"foo">>},
@@ -2795,7 +2800,8 @@ options_eccs(_Config) ->
27952800

27962801
options_verify(Config) -> %% fail_if_no_peer_cert, verify, verify_fun, partial_chain
27972802
Cert = proplists:get_value(cert, ssl_test_lib:ssl_options(server_rsa_der_opts, Config)),
2798-
{ok, #config{ssl = DefOpts = #{verify_fun := {DefVerify,_}}}} = ssl_config:handle_options([{verify, verify_none}], client, "dummy.host.org"),
2803+
{ok, #config{ssl = DefOpts = #{verify_fun := {DefVerify,_}}}} =
2804+
ssl_config:handle_options([{verify, verify_none}], client, net_adm:localhost()),
27992805

28002806
?OK(#{fail_if_no_peer_cert := false, verify := verify_none, verify_fun := {DefVerify, []}, partial_chain := _},
28012807
[], server),
@@ -3070,10 +3076,11 @@ options_reuse_session(_Config) ->
30703076
ok.
30713077

30723078
options_sni(_Config) -> %% server_name_indication
3073-
?OK(#{server_name_indication := "dummy.host.org"}, [], client),
3079+
SNI = net_adm:localhost(),
3080+
?OK(#{server_name_indication := SNI}, [], client),
30743081
?OK(#{}, [], server, [server_name_indication]),
30753082
?OK(#{server_name_indication := disable}, [{server_name_indication, disable}], client),
3076-
?OK(#{server_name_indication := "dummy.org"}, [{server_name_indication, "dummy.org"}], client),
3083+
?OK(#{server_name_indication := SNI}, [{server_name_indication, SNI}], client),
30773084

30783085
?OK(#{sni_fun := _}, [], server, [sni_hosts]),
30793086

@@ -3408,13 +3415,15 @@ client_options_negative_early_data(Config) when is_list(Config) ->
34083415
[early_data, {session_tickets, manual}, {use_ticket, undefined}]}),
34093416
start_client_negative(Config, [{versions, ['tlsv1.2', 'tlsv1.3']},
34103417
{session_tickets, manual},
3411-
{use_ticket, [<<"ticket">>]},
3418+
{use_ticket, [#{sni=> net_adm:localhost(),
3419+
ticket => <<"ticket">>}]},
34123420
{early_data, "test"}],
34133421
{options, {early_data, "test"}}),
34143422
%% All options are ok but there is no server
34153423
start_client_negative(Config, [{versions, ['tlsv1.2', 'tlsv1.3']},
34163424
{session_tickets, manual},
3417-
{use_ticket, [<<"ticket">>]},
3425+
{use_ticket, [#{sni=> net_adm:localhost(),
3426+
ticket => <<"ticket">>}]},
34183427
{early_data, <<"test">>}],
34193428
econnrefused),
34203429

lib/ssl/test/ssl_session_ticket_SUITE.erl

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -532,12 +532,13 @@ basic_stateless_stateful_anti_replay(Config) when is_list(Config) ->
532532
basic_stateful_stateless_faulty_ticket() ->
533533
[{doc,"Test session resumption with session tickets (erlang client - erlang server)"}].
534534
basic_stateful_stateless_faulty_ticket(Config) when is_list(Config) ->
535+
SNI = net_adm:localhost(),
535536
do_test_mixed(Config,
536537
[{session_tickets, auto},
537538
{versions, ['tlsv1.2','tlsv1.3']}],
538539
[{session_tickets, manual},
539-
{use_ticket, [<<131,100,0,12,"faultyticket">>,
540-
<<"faulty ticket">>]},
540+
{use_ticket, [#{sni => SNI,
541+
ticket => <<"faultyticket">>}]},
541542
{versions, ['tlsv1.2','tlsv1.3']}],
542543
[{session_tickets, stateless},
543544
{anti_replay, '10k'},
@@ -548,12 +549,13 @@ basic_stateful_stateless_faulty_ticket(Config) when is_list(Config) ->
548549
basic_stateless_stateful_faulty_ticket() ->
549550
[{doc,"Test session resumption with session tickets (erlang client - erlang server)"}].
550551
basic_stateless_stateful_faulty_ticket(Config) when is_list(Config) ->
552+
SNI = net_adm:localhost(),
551553
do_test_mixed(Config,
552554
[{session_tickets, auto},
553555
{versions, ['tlsv1.2','tlsv1.3']}],
554556
[{session_tickets, manual},
555-
{use_ticket, [<<"faulty ticket">>,
556-
<<131,100,0,12,"faultyticket">>]},
557+
{use_ticket, [#{sni => SNI,
558+
ticket => <<"faultyticket">>}]},
557559
{versions, ['tlsv1.2','tlsv1.3']}],
558560
[{session_tickets, stateless},
559561
{anti_replay, '10k'},

0 commit comments

Comments
 (0)