diff options
author | Ingela Anderton Andin <ingela@erlang.org> | 2019-11-08 09:46:25 +0100 |
---|---|---|
committer | Ingela Anderton Andin <ingela@erlang.org> | 2019-11-21 08:48:17 +0100 |
commit | 893f7f84989bc8ae558963f924f911505bb9ec4c (patch) | |
tree | ae31716c0848d8c16ecc487eb80f596a8084c8df /lib | |
parent | b51b7b883f2615869fde5923cc4aba9999eec470 (diff) | |
download | erlang-893f7f84989bc8ae558963f924f911505bb9ec4c.tar.gz |
ssl: Add statfull tickets
Diffstat (limited to 'lib')
-rw-r--r-- | lib/ssl/src/tls_handshake_1_3.erl | 60 | ||||
-rw-r--r-- | lib/ssl/src/tls_server_session_ticket.erl | 153 | ||||
-rw-r--r-- | lib/ssl/src/tls_socket.erl | 12 | ||||
-rw-r--r-- | lib/ssl/test/ssl_session_ticket_SUITE.erl | 40 |
4 files changed, 198 insertions, 67 deletions
diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl index aa74d11d69..aceb0349aa 100644 --- a/lib/ssl/src/tls_handshake_1_3.erl +++ b/lib/ssl/src/tls_handshake_1_3.erl @@ -54,6 +54,7 @@ maybe_add_binders/4, maybe_automatic_session_resumption/1]). +-export([is_valid_binder/4]). %% crypto:hash(sha256, "HelloRetryRequest"). -define(HELLO_RETRY_REQUEST_RANDOM, <<207,33,173,116,229,154,97,17, @@ -364,6 +365,18 @@ decode_handshake(?KEY_UPDATE, <<?BYTE(Update)>>) -> decode_handshake(Tag, HandshakeMsg) -> ssl_handshake:decode_handshake({3,4}, Tag, HandshakeMsg). +is_valid_binder(Binder, HHistory, PSK, Hash) -> + case HHistory of + [ClientHello2, HRR, MessageHash|_] -> + Truncated = truncate_client_hello(ClientHello2), + FinishedKey = calculate_finished_key(PSK, Hash), + Binder == calculate_binder(FinishedKey, Hash, [MessageHash, HRR, Truncated]); + [ClientHello1|_] -> + Truncated = truncate_client_hello(ClientHello1), + FinishedKey = calculate_finished_key(PSK, Hash), + Binder == calculate_binder(FinishedKey, Hash, Truncated) + end. + %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- @@ -551,9 +564,6 @@ do_start(#client_hello{cipher_suites = ClientCiphers, State1 = maybe_seed_session_tickets(OfferedPSKs, State0), - %% Exclude any incompatible PSKs. - PSK = Maybe(handle_pre_shared_key(State1, OfferedPSKs, Cipher)), - Groups = Maybe(select_common_groups(ServerGroups, ClientGroups)), Maybe(validate_client_key_share(ClientGroups, ClientShares)), @@ -589,9 +599,14 @@ do_start(#client_hello{cipher_suites = ClientCiphers, %% message if it is able to find an acceptable set of parameters but the %% ClientHello does not contain sufficient information to proceed with %% the handshake. - NextStateTuple = Maybe(send_hello_retry_request(State2, ClientPubKey, KeyShare, SessionId)), - Maybe(session_resumption(NextStateTuple, PSK)) - + case Maybe(send_hello_retry_request(State2, ClientPubKey, KeyShare, SessionId)) of + {_, start} = NextStateTuple -> + NextStateTuple; + {_, negotiated} = NextStateTuple -> + %% Exclude any incompatible PSKs. + PSK = Maybe(handle_pre_shared_key(State2, OfferedPSKs, Cipher)), + Maybe(session_resumption(NextStateTuple, PSK)) + end catch {Ref, {insufficient_security, no_suitable_groups}} -> ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_groups); @@ -1112,9 +1127,6 @@ send_hello_retry_request(State0, _, _, _) -> %% Suitable key found. {ok, {State0, negotiated}}. - -session_resumption({State, start}, _) -> - {ok, {State, start}}; session_resumption({#state{ssl_options = #{session_tickets := disabled}} = State, negotiated}, _) -> {ok, {State, negotiated}}; session_resumption({#state{ssl_options = #{session_tickets := Tickets}} = State, negotiated}, undefined) @@ -1172,9 +1184,14 @@ maybe_send_session_ticket(#state{ssl_options = #{session_tickets := SessionTicke when SessionTickets =/= disabled -> State; maybe_send_session_ticket(#state{ssl_options = #{session_tickets := stateful}, + connection_states = ConnectionStates, static_env = #static_env{trackers = Trackers}} = State0, N) -> Tracker = proplists:get_value(session_tickets_tracker, Trackers), - Ticket = tls_server_session_ticket:new(Tracker), + #{security_parameters := SecParamsR} = + ssl_record:current_connection_state(ConnectionStates, read), + #security_parameters{prf_algorithm = HKDF, + resumption_master_secret = RMS} = SecParamsR, + Ticket = tls_server_session_ticket:new(Tracker, HKDF, RMS), {State, _} = tls_connection:send_handshake(Ticket, State0), maybe_send_session_ticket(State, N - 1); maybe_send_session_ticket(#state{ @@ -2211,8 +2228,16 @@ handle_pre_shared_key(_, undefined, _) -> {ok, undefined}; handle_pre_shared_key(#state{ssl_options = #{session_tickets := disabled}}, _, _) -> {ok, undefined}; +handle_pre_shared_key(#state{ssl_options = #{session_tickets := stateful}, + handshake_env = #handshake_env{tls_handshake_history = {HHistory, _}}, + static_env = #static_env{trackers = Trackers}}, OfferedPreSharedKeys, Cipher) -> + Tracker = proplists:get_value(session_tickets_tracker, Trackers), + #{prf := CipherHash} = ssl_cipher_format:suite_bin_to_map(Cipher), + IndexAndPSK = tls_server_session_ticket:use(Tracker, OfferedPreSharedKeys, CipherHash, HHistory), + {ok, IndexAndPSK}; handle_pre_shared_key(#state{handshake_env = #handshake_env{ticket_seed = Seed}, - ssl_options = #{anti_replay := BloomFilter}} = State, PreSharedKeys, Cipher) -> + ssl_options = #{session_tickets := stateless, + anti_replay := BloomFilter}} = State, PreSharedKeys, Cipher) -> {IV, Shard} = case Seed of {_, {IV0, Shard0}} -> @@ -2243,18 +2268,7 @@ validate_binder(#state{handshake_env = #handshake_env{tls_handshake_history = {H static_env = #static_env{trackers = Trackers}}, {PSK, Index, Hash, Binder}, BloomFilter) -> Tracker = proplists:get_value(session_tickets_tracker, Trackers), - IsBinderValid = - case HHistory of - [ClientHello2, HRR, MessageHash|_] -> - Truncated = truncate_client_hello(ClientHello2), - FinishedKey = calculate_finished_key(PSK, Hash), - Binder == calculate_binder(FinishedKey, Hash, [MessageHash, HRR, Truncated]); - [ClientHello1|_] -> - Truncated = truncate_client_hello(ClientHello1), - FinishedKey = calculate_finished_key(PSK, Hash), - Binder == calculate_binder(FinishedKey, Hash, Truncated) - end, - case IsBinderValid of + case is_valid_binder(Binder, HHistory, PSK, Hash) of true -> case check_replay(Tracker, Binder, BloomFilter) of anti_replay_disabled -> diff --git a/lib/ssl/src/tls_server_session_ticket.erl b/lib/ssl/src/tls_server_session_ticket.erl index 3b5c36d203..94d9708320 100644 --- a/lib/ssl/src/tls_server_session_ticket.erl +++ b/lib/ssl/src/tls_server_session_ticket.erl @@ -31,7 +31,9 @@ %% API -export([start_link/3, new/1, + new/3, new_with_seed/1, + use/4, bloom_filter_add_elem/2, bloom_filter_contains/2]). @@ -44,6 +46,7 @@ -record(state, { stateless, stateful, + nonce, lifetime }). @@ -58,11 +61,17 @@ start_link(Mode, Lifetime, AntiReplay) -> gen_server:start_link(?MODULE, [Mode, Lifetime, AntiReplay], []). new(Pid) -> - gen_server:call(Pid, new_ticket, infinity). + gen_server:call(Pid, new_session_ticket, infinity). + +new(Pid, Prf, MasterSecret) -> + gen_server:call(Pid, {new_session_ticket, Prf, MasterSecret}, infinity). new_with_seed(Pid) -> gen_server:call(Pid, new_with_seed, infinity). +use(Pid, Identifiers, Prf, HH) -> + gen_server:call(Pid, {use_ticket, Identifiers, Prf, HH}, infinity). + bloom_filter_add_elem(Pid, Elem) -> gen_server:cast(Pid, {add_elem, Elem}). @@ -83,24 +92,29 @@ init(Args) -> -spec handle_call(Request :: term(), From :: {pid(), term()}, State :: term()) -> {reply, Reply :: term(), NewState :: term()} . -handle_call(new_ticket, _From, #state{stateless = #{nonce := Nonce} = Stateless} = State) -> - Ticket = new_ticket(Nonce, State#state.lifetime), - {reply, Ticket, State#state{stateless = Stateless#{nonce => Nonce + 1}}}; -handle_call(new_ticket, _From, State) -> - Ticket = new_ticket(State), - {reply, Ticket, State}; -handle_call(new_with_seed, _From, #state{stateless = #{nonce := Nonce, seed := Seed} = Stateless} = State) -> - Ticket = new_ticket(Nonce, State#state.lifetime), +handle_call({new_session_ticket, Prf, MasterSecret}, _From, #state{nonce = Nonce, + lifetime = LifeTime, + stateful = #{}} = State0) -> + Id = stateful_psk_id(), + PSK = tls_v1:pre_shared_key(MasterSecret, ticket_nonce(Nonce), Prf), + SessionTicket = new_session_ticket(Id, Nonce, LifeTime), + State = stateful_ticket_store(Id, SessionTicket, Prf, PSK, State0), + {reply, SessionTicket, State}; +handle_call(new_session_ticket, _From, #state{nonce = Nonce, stateless = #{}} = State) -> + BaseSessionTicket = new_session_ticket_base(State), + {reply, BaseSessionTicket, State#state{nonce = Nonce+1}}; +handle_call({use_ticket, Identifiers, Prf, HH}, _From, #state{stateful = #{}} = State0) -> + {IndexAndPSK, State} = stateful_use(Identifiers, Prf, HH, State0), + {reply, IndexAndPSK, State}; +handle_call(new_with_seed, _From, #state{nonce = Nonce, stateless = #{seed := Seed} = Stateless} = State) -> + Ticket = new_session_ticket_base(State), {reply, {Ticket, Seed}, State#state{stateless = Stateless#{nonce => Nonce + 1}}}; -handle_call(new_with_seed, _From, #state{} = State) -> - Ticket = new_ticket(State), - {reply, {Ticket, no_seed}, State}; handle_call({contains, Elem}, _From, #state{stateless = #{bloom_filter := BloomFilter}} = State) -> Reply = tls_bloom_filter:contains(BloomFilter, Elem), {reply, Reply, State}. -spec handle_cast(Request :: term(), State :: term()) -> - {noreply, NewState :: term()}. + {noreply, NewState :: term()}. handle_cast({add_elem, Elem}, #state{stateless = #{bloom_filter := BloomFilter0} = Stateless} = State) -> BloomFilter = tls_bloom_filter:add_elem(BloomFilter0, Elem), {noreply, State#state{stateless = Stateless#{bloom_filter => BloomFilter}}}; @@ -108,7 +122,7 @@ handle_cast(_Request, State) -> {noreply, State}. -spec handle_info(Info :: timeout() | term(), State :: term()) -> - {noreply, NewState :: term()}. + {noreply, NewState :: term()}. handle_info(rotate_bloom_filters, #state{stateless = #{bloom_filter := BloomFilter0, window := Window} = Stateless} = State) -> BloomFilter = tls_bloom_filter:rotate(BloomFilter0), @@ -125,7 +139,7 @@ terminate(_Reason, _State) -> -spec code_change(OldVsn :: term() | {down, term()}, State :: term(), Extra :: term()) -> {ok, NewState :: term()} | - {error, Reason :: term()}. + {error, Reason :: term()}. code_change(_OldVsn, State, _Extra) -> {ok, State}. @@ -138,26 +152,31 @@ format_status(_Opt, Status) -> %%% Internal functions %%%=================================================================== - inital_state([stateless, Lifetime, undefined]) -> - #state{stateless = #{nonce => 0, - seed => {crypto:strong_rand_bytes(16), + #state{nonce = 0, + stateless = #{seed => {crypto:strong_rand_bytes(16), crypto:strong_rand_bytes(32)}}, lifetime = Lifetime }; inital_state([stateless, Lifetime, {Window, K, M}]) -> erlang:send_after(Window * 1000, self(), rotate_bloom_filters), - #state{stateless = #{bloom_filter => tls_bloom_filter:new(K, M), - nonce => 0, + #state{nonce = 0, + stateless = #{bloom_filter => tls_bloom_filter:new(K, M), seed => {crypto:strong_rand_bytes(16), crypto:strong_rand_bytes(32)}, windows => Window}, lifetime = Lifetime }; -inital_state([stateful, Lifetime]) -> +inital_state([stateful, Lifetime|_]) -> + %% statfeful servers replay + %% protection is that it saves + %% all valid tickets #state{lifetime = Lifetime, - stateful = #{db => gb_trees:empty(), - max => 1000} + nonce = 0, + stateful = #{db => stateful_store(), + max => 1000, + ref_index => #{} + } }. ticket_age_add() -> @@ -175,14 +194,96 @@ ticket_age_add() -> ticket_nonce(I) -> <<?UINT64(I)>>. -new_ticket(Nonce, Lifetime) -> +new_session_ticket_base(#state{nonce = Nonce, + lifetime = Lifetime}) -> + new_session_ticket(undefined, Nonce, Lifetime). + +new_session_ticket(Id, Nonce, Lifetime) -> TicketAgeAdd = ticket_age_add(), #new_session_ticket{ + ticket = Id, ticket_lifetime = Lifetime, ticket_age_add = TicketAgeAdd, ticket_nonce = ticket_nonce(Nonce), extensions = #{} }. -new_ticket(_) -> - #new_session_ticket{}. +%%%=================================================================== +%%% Stateful store +%%%=================================================================== + +stateful_store() -> + gb_trees:empty(). + +stateful_ticket_store(Ref, NewSessionTicket, Hash, Psk, + #state{nonce = Nonce, + stateful = #{db := Tree0, + max := Max, + ref_index := Index0} = Stateful} = State0) -> + Id = erlang:monotonic_time(), + StatefulTicket = {NewSessionTicket, Hash, Psk}, + case gb_trees:size(Tree0) of + Max -> + %% Trow away oldes ticket + {_, {#new_session_ticket{ticket = OldRef},_,_}, Tree1} + = gb_trees:take_smallest(Tree0), + Tree = gb_trees:insert(Id, StatefulTicket, Tree1), + Index = maps:without([OldRef], Index0), + State0#state{nonce = Nonce+1, stateful = + Stateful#{db => Tree, ref_index => Index#{Ref => Id}}}; + _ -> + Tree = gb_trees:insert(Id, StatefulTicket, Tree0), + State0#state{nonce = Nonce+1, stateful = + Stateful#{db => Tree, ref_index => Index0#{Ref => Id}}} + end. + +stateful_use(#offered_psks{ + identities = Identities, + binders = Binders + }, Prf, HH, State) -> + stateful_use(Identities, Binders, Prf, HH, 0, State). + +stateful_use([], [], _, _, _, State) -> + {undefined, State}; +stateful_use([#psk_identity{identity = Ref} | Refs], [Binder | Binders], Prf, HH, + Index, #state{stateful = #{db := Tree0, ref_index := RefIndex0} = Stateful} = State) -> + try maps:get(Ref, RefIndex0) of + Key -> + case usable_ticket(Key, Prf, Binder, HH, Tree0) of + true -> + RefIndex = maps:without([Ref], RefIndex0), + {{_,_, PSK}, Tree} = gb_trees:take(Key, Tree0), + {{Index, PSK}, State#state{stateful = Stateful#{db => Tree, ref_index => RefIndex}}}; + false -> + stateful_use(Refs, Binders, Prf, HH, Index + 1, State) + end + catch + _:{badkey, Ref} -> + stateful_use(Refs, Binders, Prf, HH, Index + 1, State) + end. + +usable_ticket(Key, Prf, Binder, HH, Tree) -> + case gb_trees:lookup(Key, Tree) of + none -> + false; + {value, {NewSessionTicket, Prf, PSK}} -> + case living_ticket(Key, NewSessionTicket) of + true -> + tls_handshake_1_3:is_valid_binder(Binder, HH, PSK, Prf); + false -> + false + end; + _ -> + false + end. + +living_ticket(TimeStamp, #new_session_ticket{ticket_lifetime = LifeTime}) -> + Now = erlang:monotonic_time(), + Lived = erlang:convert_time_unit(Now-TimeStamp, native, seconds), + Lived < LifeTime. + + +stateful_psk_id() -> + term_to_binary(make_ref()). + + diff --git a/lib/ssl/src/tls_socket.erl b/lib/ssl/src/tls_socket.erl index ca97e91688..031665bfe8 100644 --- a/lib/ssl/src/tls_socket.erl +++ b/lib/ssl/src/tls_socket.erl @@ -78,7 +78,7 @@ listen(Transport, Port, #config{transport_info = {Transport, _, _, _, _}, {ok, ListenSocket} -> {ok, Tracker} = inherit_tracker(ListenSocket, EmOpts, SslOpts), %% TODO not hard code - {ok, SessionHandler} = session_tickets_tracker(stateless, 7200, SslOpts), + {ok, SessionHandler} = session_tickets_tracker(7200, SslOpts), Trackers = [{option_tracker, Tracker}, {session_tickets_tracker, SessionHandler}], Socket = #sslsocket{pid = {ListenSocket, Config#config{trackers = Trackers}}}, check_active_n(EmOpts, Socket), @@ -249,10 +249,14 @@ inherit_tracker(ListenSocket, EmOpts, #{erl_dist := false} = SslOpts) -> inherit_tracker(ListenSocket, EmOpts, #{erl_dist := true} = SslOpts) -> ssl_listen_tracker_sup:start_child_dist([ListenSocket, EmOpts, SslOpts]). -session_tickets_tracker(Mode, Lifetime, #{erl_dist := false, - anti_replay := AntiReplay}) -> +session_tickets_tracker(_, #{erl_dist := false, + session_tickets := disabled}) -> + {ok, disabled}; +session_tickets_tracker(Lifetime, #{erl_dist := false, + session_tickets := Mode, + anti_replay := AntiReplay}) -> tls_server_session_ticket_sup:start_child([Mode, Lifetime, AntiReplay]); -session_tickets_tracker(Mode, Lifetime, #{erl_dist := true}) -> +session_tickets_tracker(Lifetime, #{erl_dist := true, session_tickets := Mode}) -> tls_server_session_ticket_sup:start_child_dist([Mode, Lifetime]). diff --git a/lib/ssl/test/ssl_session_ticket_SUITE.erl b/lib/ssl/test/ssl_session_ticket_SUITE.erl index 78cae6fab3..83e8e3214d 100644 --- a/lib/ssl/test/ssl_session_ticket_SUITE.erl +++ b/lib/ssl/test/ssl_session_ticket_SUITE.erl @@ -40,17 +40,20 @@ all() -> ]. groups() -> - [{'tlsv1.3', [], session_tests()}]. + [{'tlsv1.3', [], [{group, stateful}, {group, stateless}, {group, openssl_server}]}, + {openssl_server, [], [erlang_client_openssl_server_basic, + erlang_client_openssl_server_hrr, + erlang_client_openssl_server_hrr_multiple_tickets + ]}, + {stateful, [], session_tests()}, + {stateless, [], session_tests()}]. session_tests() -> [erlang_client_erlang_server_basic, - erlang_client_openssl_server_basic, openssl_client_erlang_server_basic, erlang_client_erlang_server_hrr, - erlang_client_openssl_server_hrr, openssl_client_erlang_server_hrr, erlang_client_erlang_server_multiple_tickets, - erlang_client_openssl_server_hrr_multiple_tickets, erlang_client_erlang_server_multiple_tickets_2hash]. init_per_suite(Config0) -> @@ -58,8 +61,7 @@ init_per_suite(Config0) -> try crypto:start() of ok -> ssl_test_lib:clean_start(), - Config = ssl_test_lib:make_rsa_cert(Config0), - ssl_test_lib:make_dsa_cert(Config) + ssl_test_lib:make_rsa_cert(Config0) catch _:_ -> {skip, "Crypto did not start"} end. @@ -68,6 +70,10 @@ end_per_suite(_Config) -> ssl:stop(), application:stop(crypto). +init_per_group(stateful, Config) -> + [{server_ticket_mode, stateful} | proplists:delete(server_ticket_mode, Config)]; +init_per_group(stateless, Config) -> + [{server_ticket_mode, stateless} | proplists:delete(server_ticket_mode, Config)]; init_per_group(GroupName, Config) -> ssl_test_lib:clean_tls_version(Config), case ssl_test_lib:is_tls_version(GroupName) andalso ssl_test_lib:sufficient_crypto_support(GroupName) of @@ -112,11 +118,12 @@ erlang_client_erlang_server_basic(Config) when is_list(Config) -> ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + ServerTicketMode = proplists:get_value(server_ticket_mode, Config), %% Configure session tickets ClientOpts = [{session_tickets, auto}, {log_level, debug}, {versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0], - ServerOpts = [{session_tickets, stateless}, {log_level, debug}, + ServerOpts = [{session_tickets, ServerTicketMode}, {log_level, debug}, {versions, ['tlsv1.2','tlsv1.3']}|ServerOpts0], Server0 = @@ -221,11 +228,12 @@ openssl_client_erlang_server_basic(Config) when is_list(Config) -> {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), TicketFile0 = filename:join([proplists:get_value(priv_dir, Config), "session_ticket0"]), TicketFile1 = filename:join([proplists:get_value(priv_dir, Config), "session_ticket1"]), + ServerTicketMode = proplists:get_value(server_ticket_mode, Config), Data = "Hello world", %% Configure session tickets - ServerOpts = [{session_tickets, stateless}, {log_level, debug}, + ServerOpts = [{session_tickets, ServerTicketMode}, {log_level, debug}, {versions, ['tlsv1.2','tlsv1.3']}|ServerOpts0], Server0 = @@ -282,12 +290,13 @@ erlang_client_erlang_server_hrr(Config) when is_list(Config) -> ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - + ServerTicketMode = proplists:get_value(server_ticket_mode, Config), + %% Configure session tickets ClientOpts = [{session_tickets, auto}, {log_level, debug}, {versions, ['tlsv1.2','tlsv1.3']}, {supported_groups,[secp256r1, x25519]}|ClientOpts0], - ServerOpts = [{session_tickets, stateless}, {log_level, debug}, + ServerOpts = [{session_tickets, ServerTicketMode}, {log_level, debug}, {versions, ['tlsv1.2','tlsv1.3']}, {supported_groups, [x448, x25519]}|ServerOpts0], @@ -398,11 +407,12 @@ openssl_client_erlang_server_hrr(Config) when is_list(Config) -> {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), TicketFile0 = filename:join([proplists:get_value(priv_dir, Config), "session_ticket0"]), TicketFile1 = filename:join([proplists:get_value(priv_dir, Config), "session_ticket1"]), + ServerTicketMode = proplists:get_value(server_ticket_mode, Config), Data = "Hello world", %% Configure session tickets - ServerOpts = [{session_tickets, stateless}, {log_level, debug}, + ServerOpts = [{session_tickets, ServerTicketMode}, {log_level, debug}, {versions, ['tlsv1.2','tlsv1.3']}, {supported_groups,[x448, x25519]}|ServerOpts0], @@ -462,11 +472,12 @@ erlang_client_erlang_server_multiple_tickets(Config) when is_list(Config) -> ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + ServerTicketMode = proplists:get_value(server_ticket_mode, Config), %% Configure session tickets ClientOpts = [{session_tickets, enabled}, {log_level, debug}, {versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0], - ServerOpts = [{session_tickets, stateless}, {log_level, debug}, + ServerOpts = [{session_tickets, ServerTicketMode}, {log_level, debug}, {versions, ['tlsv1.2','tlsv1.3']}|ServerOpts0], Server0 = @@ -582,11 +593,12 @@ erlang_client_erlang_server_multiple_tickets_2hash(Config) when is_list(Config) ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - + ServerTicketMode = proplists:get_value(server_ticket_mode, Config), + %% Configure session tickets ClientOpts = [{session_tickets, enabled}, {log_level, debug}, {versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0], - ServerOpts = [{session_tickets, stateless}, {log_level, debug}, + ServerOpts = [{session_tickets, ServerTicketMode}, {log_level, debug}, {versions, ['tlsv1.2','tlsv1.3']}|ServerOpts0], Server0 = |