1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2007-2020. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21-module(openssl_session_ticket_SUITE).
22
23%% Callback functions
24-export([all/0,
25         groups/0,
26         init_per_suite/1,
27         end_per_suite/1,
28         init_per_group/2,
29         end_per_group/2,
30         init_per_testcase/2,
31         end_per_testcase/2]).
32
33%% Testcases
34-export([openssl_server_basic/0,
35         openssl_server_basic/1,
36         openssl_server_hrr/0,
37         openssl_server_hrr/1,
38         openssl_server_hrr_multiple_tickets/0,
39         openssl_server_hrr_multiple_tickets/1,
40         openssl_client_basic/0,
41         openssl_client_basic/1,
42         openssl_client_hrr/0,
43         openssl_client_hrr/1]).
44
45-include("tls_handshake.hrl").
46
47-include_lib("common_test/include/ct.hrl").
48
49-define(SLEEP, 500).
50
51%%--------------------------------------------------------------------
52%% Common Test interface functions -----------------------------------
53%%--------------------------------------------------------------------
54all() ->
55    [
56     {group, 'tlsv1.3'}
57    ].
58
59groups() ->
60    [{'tlsv1.3', [], [{group, stateful},
61                      {group, stateless},
62                      {group, openssl_server}]},
63     {openssl_server, [], [openssl_server_basic,
64                           openssl_server_hrr,
65                           openssl_server_hrr_multiple_tickets
66                          ]},
67     {stateful, [], session_tests()},
68     {stateless, [], session_tests()}].
69
70session_tests() ->
71    [openssl_client_basic,
72     openssl_client_hrr].
73
74init_per_suite(Config0) ->
75    catch crypto:stop(),
76    try crypto:start() of
77	ok ->
78	    ssl_test_lib:clean_start(),
79            ssl_test_lib:make_rsa_cert(Config0)
80    catch _:_ ->
81	    {skip, "Crypto did not start"}
82    end.
83
84end_per_suite(_Config) ->
85    ssl:stop(),
86    application:stop(crypto).
87
88init_per_group(stateful, Config) ->
89    [{server_ticket_mode, stateful} | proplists:delete(server_ticket_mode, Config)];
90init_per_group(stateless, Config) ->
91    [{server_ticket_mode, stateless} | proplists:delete(server_ticket_mode, Config)];
92init_per_group(GroupName, Config) ->
93    ssl_test_lib:init_per_group_openssl(GroupName, Config).
94
95end_per_group(GroupName, Config) ->
96  ssl_test_lib:end_per_group(GroupName, Config).
97
98init_per_testcase(_TestCase, Config) ->
99    ssl:stop(),
100    application:load(ssl),
101    ssl:start(),
102    ct:timetrap({seconds, 15}),
103    Config.
104
105end_per_testcase(_TestCase, Config) ->
106    Config.
107
108%%--------------------------------------------------------------------
109%% Test Cases --------------------------------------------------------
110%%--------------------------------------------------------------------
111
112openssl_server_basic() ->
113    [{doc,"Test session resumption with session tickets (erlang client - openssl server)"}].
114openssl_server_basic(Config) when is_list(Config) ->
115    process_flag(trap_exit, true),
116    ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
117    ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
118    {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
119
120    Version = 'tlsv1.3',
121    Port = ssl_test_lib:inet_port(node()),
122    CertFile = proplists:get_value(certfile, ServerOpts),
123    CACertFile = proplists:get_value(cacertfile, ServerOpts),
124    KeyFile = proplists:get_value(keyfile, ServerOpts),
125
126    %% Configure session tickets
127    ClientOpts = [{session_tickets, auto}, {log_level, debug},
128                  {versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0],
129
130    Exe = "openssl",
131    Args = ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version),
132            "-cert", CertFile,"-key", KeyFile, "-CAfile", CACertFile, "-msg", "-debug"],
133
134    OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
135
136    ssl_test_lib:wait_for_openssl_server(Port,  proplists:get_value(protocol, Config)),
137
138    %% Store ticket from first connection
139    Client0 = ssl_test_lib:start_client([{node, ClientNode},
140                                         {port, Port}, {host, Hostname},
141                                         {mfa, {ssl_test_lib,
142                                                verify_active_session_resumption,
143                                                [false, no_reply]}},
144                                         {from, self()},  {options, ClientOpts}]),
145    %% Wait for session ticket
146    ct:sleep(100),
147
148    %% Close previous connection as s_server can only handle one at a time
149    ssl_test_lib:close(Client0),
150
151    %% Use ticket
152    Client1 = ssl_test_lib:start_client([{node, ClientNode},
153                                         {port, Port}, {host, Hostname},
154                                         {mfa, {ssl_test_lib,
155                                                verify_active_session_resumption,
156                                                [true, no_reply]}},
157                                         {from, self()},
158                                         {options, ClientOpts}]),
159    process_flag(trap_exit, false),
160
161    %% Clean close down!   Server needs to be closed first !!
162    ssl_test_lib:close_port(OpensslPort),
163    ssl_test_lib:close(Client1).
164
165openssl_client_basic() ->
166    [{doc,"Test session resumption with session tickets (openssl client - erlang server)"}].
167openssl_client_basic(Config) when is_list(Config) ->
168    ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
169    {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
170    TicketFile0 = filename:join([proplists:get_value(priv_dir, Config), "session_ticket0"]),
171    TicketFile1 = filename:join([proplists:get_value(priv_dir, Config), "session_ticket1"]),
172    ServerTicketMode = proplists:get_value(server_ticket_mode, Config),
173
174    Data = "Hello world",
175
176    %% Configure session tickets
177    ServerOpts = [{session_tickets, ServerTicketMode}, {log_level, debug},
178                  {versions, ['tlsv1.2','tlsv1.3']}|ServerOpts0],
179
180    Server0 =
181	ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
182				   {from, self()},
183				   {mfa, {ssl_test_lib,
184                                          verify_active_session_resumption,
185                                          [false]}},
186				   {options, ServerOpts}]),
187
188    Version = 'tlsv1.3',
189    Port0 = ssl_test_lib:inet_port(Server0),
190
191    Exe = "openssl",
192    Args0 = ["s_client", "-connect", ssl_test_lib:hostname_format(Hostname)
193            ++ ":" ++ integer_to_list(Port0),
194            ssl_test_lib:version_flag(Version),
195            "-sess_out", TicketFile0],
196
197    OpenSslPort0 =  ssl_test_lib:portable_open_port(Exe, Args0),
198
199    true = port_command(OpenSslPort0, Data),
200
201    ssl_test_lib:check_result(Server0, ok),
202
203    Server0 ! {listen, {mfa, {ssl_test_lib,
204                              verify_active_session_resumption,
205                              [true]}}},
206
207    %% Wait for session ticket
208    ct:sleep(100),
209
210    Args1 = ["s_client", "-connect", ssl_test_lib:hostname_format(Hostname)
211            ++ ":" ++ integer_to_list(Port0),
212            ssl_test_lib:version_flag(Version),
213            "-sess_in", TicketFile0,
214            "-sess_out", TicketFile1],
215
216    OpenSslPort1 =  ssl_test_lib:portable_open_port(Exe, Args1),
217
218    true = port_command(OpenSslPort1, Data),
219
220    ssl_test_lib:check_result(Server0, ok),
221
222    %% Clean close down!   Server needs to be closed first !!
223    ssl_test_lib:close(Server0),
224    ssl_test_lib:close_port(OpenSslPort0),
225    ssl_test_lib:close_port(OpenSslPort1).
226
227openssl_server_hrr() ->
228    [{doc,"Test session resumption with session tickets and hello_retry_request (erlang client - openssl server)"}].
229openssl_server_hrr(Config) when is_list(Config) ->
230    process_flag(trap_exit, true),
231    ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
232    ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
233    {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
234
235    Version = 'tlsv1.3',
236    Port = ssl_test_lib:inet_port(node()),
237    CertFile = proplists:get_value(certfile, ServerOpts),
238    CACertFile = proplists:get_value(cacertfile, ServerOpts),
239    KeyFile = proplists:get_value(keyfile, ServerOpts),
240
241    %% Configure session tickets
242    ClientOpts = [{session_tickets, auto}, {log_level, debug},
243                  {versions, ['tlsv1.2','tlsv1.3']},
244                  {supported_groups,[secp256r1, x25519]}|ClientOpts0],
245
246    Exe = "openssl",
247    Args = ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version),
248            "-cert", CertFile,
249            "-key", KeyFile,
250            "-CAfile", CACertFile,
251            "-groups", "X448:X25519",
252            "-msg", "-debug"],
253
254    OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
255
256    ssl_test_lib:wait_for_openssl_server(Port,  proplists:get_value(protocol, Config)),
257
258    %% Store ticket from first connection
259    Client0 = ssl_test_lib:start_client([{node, ClientNode},
260                                         {port, Port}, {host, Hostname},
261                                         {mfa, {ssl_test_lib,
262                                                verify_active_session_resumption,
263                                                [false, no_reply]}},
264                                         {from, self()}, {options, ClientOpts}]),
265    %% Wait for session ticket
266    ct:sleep(100),
267
268    %% Close previous connection as s_server can only handle one at a time
269    ssl_test_lib:close(Client0),
270
271    %% Use ticket
272    Client1 = ssl_test_lib:start_client([{node, ClientNode},
273                                         {port, Port}, {host, Hostname},
274                                         {mfa, {ssl_test_lib,
275                                                verify_active_session_resumption,
276                                                [true, no_reply]}},
277                                         {from, self()},
278                                         {options, ClientOpts}]),
279    process_flag(trap_exit, false),
280
281    %% Clean close down!   Server needs to be closed first !!
282    ssl_test_lib:close_port(OpensslPort),
283    ssl_test_lib:close(Client1).
284
285openssl_client_hrr() ->
286    [{doc,"Test session resumption with session tickets and hello_retry_request (openssl client - erlang server)"}].
287openssl_client_hrr(Config) when is_list(Config) ->
288    ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
289    {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
290    TicketFile0 = filename:join([proplists:get_value(priv_dir, Config), "session_ticket0"]),
291    TicketFile1 = filename:join([proplists:get_value(priv_dir, Config), "session_ticket1"]),
292    ServerTicketMode = proplists:get_value(server_ticket_mode, Config),
293
294    Data = "Hello world",
295
296    %% Configure session tickets
297    ServerOpts = [{session_tickets, ServerTicketMode}, {log_level, debug},
298                  {versions, ['tlsv1.2','tlsv1.3']},
299                  {supported_groups,[x448, x25519]}|ServerOpts0],
300
301    Server0 =
302	ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
303				   {from, self()},
304				   {mfa, {ssl_test_lib,
305                                          verify_active_session_resumption,
306                                          [false]}},
307				   {options, ServerOpts}]),
308
309    Version = 'tlsv1.3',
310    Port0 = ssl_test_lib:inet_port(Server0),
311
312    Exe = "openssl",
313    Args0 = ["s_client", "-connect", ssl_test_lib:hostname_format(Hostname)
314            ++ ":" ++ integer_to_list(Port0),
315            ssl_test_lib:version_flag(Version),
316            "-groups", "P-256:X25519",
317            "-sess_out", TicketFile0],
318
319    OpenSslPort0 =  ssl_test_lib:portable_open_port(Exe, Args0),
320
321    true = port_command(OpenSslPort0, Data),
322
323    ssl_test_lib:check_result(Server0, ok),
324
325    Server0 ! {listen, {mfa, {ssl_test_lib,
326                              verify_active_session_resumption,
327                              [true]}}},
328
329    %% Wait for session ticket
330    ct:sleep(100),
331
332    Args1 = ["s_client", "-connect", ssl_test_lib:hostname_format(Hostname)
333            ++ ":" ++ integer_to_list(Port0),
334            ssl_test_lib:version_flag(Version),
335            "-groups", "P-256:X25519",
336            "-sess_in", TicketFile0,
337            "-sess_out", TicketFile1],
338
339    OpenSslPort1 =  ssl_test_lib:portable_open_port(Exe, Args1),
340
341    true = port_command(OpenSslPort1, Data),
342
343    ssl_test_lib:check_result(Server0, ok),
344
345    %% Clean close down!   Server needs to be closed first !!
346    ssl_test_lib:close(Server0),
347    ssl_test_lib:close_port(OpenSslPort0),
348    ssl_test_lib:close_port(OpenSslPort1).
349
350openssl_server_hrr_multiple_tickets() ->
351    [{doc,"Test session resumption with multiple session tickets and hello_retry_request (erlang client - openssl server)"}].
352openssl_server_hrr_multiple_tickets(Config) when is_list(Config) ->
353    process_flag(trap_exit, true),
354    ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
355    ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
356    {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
357
358    Version = 'tlsv1.3',
359    Port = ssl_test_lib:inet_port(node()),
360    CertFile = proplists:get_value(certfile, ServerOpts),
361    CACertFile = proplists:get_value(cacertfile, ServerOpts),
362    KeyFile = proplists:get_value(keyfile, ServerOpts),
363
364    %% Configure session tickets
365    ClientOpts = [{session_tickets, manual}, {log_level, debug},
366                  {versions, ['tlsv1.2','tlsv1.3']},
367                  {supported_groups,[secp256r1, x25519]}|ClientOpts0],
368
369    Exe = "openssl",
370    Args = ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version),
371            "-cert", CertFile,
372            "-key", KeyFile,
373            "-CAfile", CACertFile,
374            "-groups", "X448:X25519",
375            "-msg", "-debug"],
376
377    OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
378
379    ssl_test_lib:wait_for_openssl_server(Port,  proplists:get_value(protocol, Config)),
380
381    %% Store ticket from first connection
382    Client0 = ssl_test_lib:start_client([{node, ClientNode},
383                                         {port, Port}, {host, Hostname},
384                                         {mfa, {ssl_test_lib,
385                                                verify_active_session_resumption,
386                                                [false, no_reply, {tickets, 2}]}},
387                                         {from, self()},  {options, ClientOpts}]),
388
389    Tickets0 = ssl_test_lib:check_tickets(Client0),
390
391    ct:pal("Received tickets: ~p~n", [Tickets0]),
392
393    %% Close previous connection as s_server can only handle one at a time
394    ssl_test_lib:close(Client0),
395
396    %% Use tickets
397    Client1 = ssl_test_lib:start_client([{node, ClientNode},
398                                         {port, Port}, {host, Hostname},
399                                         {mfa, {ssl_test_lib,
400                                                verify_active_session_resumption,
401                                                [true, no_reply, no_tickets]}},
402                                         {from, self()},
403                                         {options, [{use_ticket, Tickets0}|ClientOpts]}]),
404
405    process_flag(trap_exit, false),
406
407    %% Clean close down!   Server needs to be closed first !!
408    ssl_test_lib:close_port(OpensslPort),
409    ssl_test_lib:close(Client1).
410