1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2011-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_ocsp_SUITE). 22 23-include_lib("common_test/include/ct.hrl"). 24-include_lib("public_key/include/public_key.hrl"). 25 26%% Callback functions 27-export([all/0, 28 groups/0, 29 init_per_suite/1, 30 end_per_suite/1, 31 init_per_group/2, 32 end_per_group/2, 33 init_per_testcase/2, 34 end_per_testcase/2]). 35 36%% Testcases 37-export([ocsp_stapling_basic/0,ocsp_stapling_basic/1, 38 ocsp_stapling_with_nonce/0, ocsp_stapling_with_nonce/1, 39 ocsp_stapling_with_responder_cert/0,ocsp_stapling_with_responder_cert/1, 40 ocsp_stapling_revoked/0, ocsp_stapling_revoked/1, 41 ocsp_stapling_undetermined/0, ocsp_stapling_undetermined/1, 42 ocsp_stapling_no_staple/0, ocsp_stapling_no_staple/1 43 ]). 44 45%% spawn export 46-export([ocsp_responder_init/3]). 47 48 49%%-------------------------------------------------------------------- 50%% Common Test interface functions ----------------------------------- 51%%-------------------------------------------------------------------- 52all() -> 53 [{group, 'tlsv1.3'}, 54 {group, 'tlsv1.2'}, 55 {group, 'dtlsv1.2'}]. 56 57groups() -> 58 [{'tlsv1.3', [], ocsp_tests()}, 59 {'tlsv1.2', [], ocsp_tests()}, 60 {'dtlsv1.2', [], ocsp_tests()}]. 61 62ocsp_tests() -> 63 [ocsp_stapling_basic, 64 ocsp_stapling_with_nonce, 65 ocsp_stapling_with_responder_cert, 66 ocsp_stapling_revoked, 67 ocsp_stapling_undetermined, 68 ocsp_stapling_no_staple 69 ]. 70 71%%-------------------------------------------------------------------- 72init_per_suite(Config) -> 73 case ssl_test_lib:openssl_ocsp_support() of 74 true -> 75 do_init_per_suite(Config); 76 false -> 77 {skip, "OCSP not well supported in openSSL"} 78 end. 79 80do_init_per_suite(Config) -> 81 catch crypto:stop(), 82 try crypto:start() of 83 ok -> 84 ssl_test_lib:clean_start(), 85 DataDir = proplists:get_value(data_dir, Config), 86 PrivDir = proplists:get_value(priv_dir, Config), 87 88 %% Prepare certs 89 {ok, _} = make_certs:all(DataDir, PrivDir), 90 91 ResponderPort = get_free_port(), 92 Pid = start_ocsp_responder(ResponderPort, PrivDir), 93 94 NewConfig = 95 lists:merge( 96 [{responder_port, ResponderPort}, 97 {responder_pid, Pid} 98 ], Config), 99 100 ssl_test_lib:cert_options(NewConfig) 101 catch _:_ -> 102 {skip, "Crypto did not start"} 103 end. 104 105 106end_per_suite(Config) -> 107 ResponderPid = proplists:get_value(responder_pid, Config), 108 ssl_test_lib:close(ResponderPid), 109 ok = ssl:stop(), 110 application:stop(crypto). 111 112%%-------------------------------------------------------------------- 113init_per_group(GroupName, Config) -> 114 ssl_test_lib:init_per_group_openssl(GroupName, Config). 115 116end_per_group(GroupName, Config) -> 117 ssl_test_lib:end_per_group(GroupName, Config). 118 119%%-------------------------------------------------------------------- 120init_per_testcase(_TestCase, Config) -> 121 ssl_test_lib:ct_log_supported_protocol_versions(Config), 122 ct:timetrap({seconds, 10}), 123 Config. 124 125end_per_testcase(_TestCase, Config) -> 126 Config. 127 128%%-------------------------------------------------------------------- 129%% Test Cases -------------------------------------------------------- 130%%-------------------------------------------------------------------- 131 132ocsp_stapling_basic() -> 133 [{doc, "Verify OCSP stapling works without nonce " 134 "and responder certs."}]. 135ocsp_stapling_basic(Config) 136 when is_list(Config) -> 137 PrivDir = proplists:get_value(priv_dir, Config), 138 CACertsFile = filename:join(PrivDir, "a.server/cacerts.pem"), 139 140 Data = "ping", %% 4 bytes 141 GroupName = proplists:get_value(group, Config), 142 ServerOpts = [{log_level, debug}, 143 {group, GroupName}], 144 Server = ssl_test_lib:start_server(openssl_ocsp, 145 [{options, ServerOpts}], Config), 146 Port = ssl_test_lib:inet_port(Server), 147 148 ClientOpts = [{log_level, debug}, 149 {verify, verify_peer}, 150 {cacertfile, CACertsFile}, 151 {server_name_indication, disable}, 152 {ocsp_stapling, true}, 153 {ocsp_nonce, false}] ++ dtls_client_opt(GroupName), 154 Client = ssl_test_lib:start_client(erlang, 155 [{port, Port}, 156 {options, ClientOpts}], Config), 157 ssl_test_lib:send(Server, Data), 158 Data = ssl_test_lib:check_active_receive(Client, Data), 159 160 ssl_test_lib:close(Server), 161 ssl_test_lib:close(Client). 162%%-------------------------------------------------------------------- 163ocsp_stapling_with_nonce() -> 164 [{doc, "Verify OCSP stapling works with nonce."}]. 165ocsp_stapling_with_nonce(Config) 166 when is_list(Config) -> 167 PrivDir = proplists:get_value(priv_dir, Config), 168 CACertsFile = filename:join(PrivDir, "a.server/cacerts.pem"), 169 170 Data = "ping", %% 4 bytes 171 GroupName = proplists:get_value(group, Config), 172 ServerOpts = [{log_level, debug}, 173 {group, GroupName}], 174 Server = ssl_test_lib:start_server(openssl_ocsp, 175 [{options, ServerOpts}], Config), 176 Port = ssl_test_lib:inet_port(Server), 177 178 ClientOpts = [{log_level, debug}, 179 {verify, verify_peer}, 180 {cacertfile, CACertsFile}, 181 {server_name_indication, disable}, 182 {ocsp_stapling, true}, 183 {ocsp_nonce, true}] ++ dtls_client_opt(GroupName), 184 Client = ssl_test_lib:start_client(erlang, 185 [{port, Port}, 186 {options, ClientOpts}], Config), 187 ssl_test_lib:send(Server, Data), 188 Data = ssl_test_lib:check_active_receive(Client, Data), 189 190 ssl_test_lib:close(Server), 191 ssl_test_lib:close(Client). 192 193ocsp_stapling_with_responder_cert() -> 194 [{doc, "Verify OCSP stapling works with nonce " 195 "and responder certs."}]. 196ocsp_stapling_with_responder_cert(Config) 197 when is_list(Config) -> 198 PrivDir = proplists:get_value(priv_dir, Config), 199 CACertsFile = filename:join(PrivDir, "a.server/cacerts.pem"), 200 201 Data = "ping", %% 4 bytes 202 GroupName = proplists:get_value(group, Config), 203 ServerOpts = [{log_level, debug}, 204 {group, GroupName}], 205 Server = ssl_test_lib:start_server(openssl_ocsp, 206 [{options, ServerOpts}], Config), 207 Port = ssl_test_lib:inet_port(Server), 208 209 PrivDir = proplists:get_value(priv_dir, Config), 210 {ok, ResponderCert} = 211 file:read_file(filename:join(PrivDir, "b.server/cert.pem")), 212 [{'Certificate', Der, _IsEncrypted}] = 213 public_key:pem_decode(ResponderCert), 214 215 ClientOpts = [{log_level, debug}, 216 {verify, verify_peer}, 217 {cacertfile, CACertsFile}, 218 {server_name_indication, disable}, 219 {ocsp_stapling, true}, 220 {ocsp_nonce, true}, 221 {ocsp_responder_certs, [Der]}] ++ dtls_client_opt(GroupName), 222 Client = ssl_test_lib:start_client(erlang, 223 [{port, Port}, 224 {options, ClientOpts}], Config), 225 ssl_test_lib:send(Server, Data), 226 Data = ssl_test_lib:check_active_receive(Client, Data), 227 228 ssl_test_lib:close(Server), 229 ssl_test_lib:close(Client). 230%%-------------------------------------------------------------------- 231ocsp_stapling_revoked() -> 232 [{doc, "Verify OCSP stapling works with revoked certificate."}]. 233ocsp_stapling_revoked(Config) 234 when is_list(Config) -> 235 PrivDir = proplists:get_value(priv_dir, Config), 236 CACertsFile = filename:join(PrivDir, "revoked/cacerts.pem"), 237 238 GroupName = proplists:get_value(group, Config), 239 ServerOpts = [{log_level, debug}, 240 {group, GroupName}], 241 {ClientNode, _ServerNode, Hostname} = ssl_test_lib:run_where(Config), 242 243 Server = ssl_test_lib:start_server(openssl_ocsp_revoked, 244 [{options, ServerOpts}], Config), 245 Port = ssl_test_lib:inet_port(Server), 246 247 ClientOpts = [{log_level, debug}, 248 {verify, verify_peer}, 249 {server_name_indication, disable}, 250 {cacertfile, CACertsFile}, 251 {ocsp_stapling, true}, 252 {ocsp_nonce, true} 253 ] ++ dtls_client_opt(GroupName), 254 255 Client = ssl_test_lib:start_client_error([{node, ClientNode},{port, Port}, 256 {host, Hostname}, {from, self()}, 257 {options, ClientOpts}]), 258 259 ssl_test_lib:check_client_alert(Client, certificate_revoked). 260 261%%-------------------------------------------------------------------- 262ocsp_stapling_undetermined() -> 263 [{doc, "Verify OCSP stapling works with certificate with undetermined status."}]. 264ocsp_stapling_undetermined(Config) 265 when is_list(Config) -> 266 PrivDir = proplists:get_value(priv_dir, Config), 267 CACertsFile = filename:join(PrivDir, "undetermined/cacerts.pem"), 268 269 GroupName = proplists:get_value(group, Config), 270 ServerOpts = [{log_level, debug}, 271 {group, GroupName}], 272 {ClientNode, _ServerNode, Hostname} = ssl_test_lib:run_where(Config), 273 274 Server = ssl_test_lib:start_server(openssl_ocsp_undetermined, 275 [{options, ServerOpts}], Config), 276 Port = ssl_test_lib:inet_port(Server), 277 278 ClientOpts = [{log_level, debug}, 279 {verify, verify_peer}, 280 {server_name_indication, disable}, 281 {cacertfile, CACertsFile}, 282 {ocsp_stapling, true}, 283 {ocsp_nonce, true} 284 ] ++ dtls_client_opt(GroupName), 285 286 Client = ssl_test_lib:start_client_error([{node, ClientNode},{port, Port}, 287 {host, Hostname}, {from, self()}, 288 {options, ClientOpts}]), 289 290 ssl_test_lib:check_client_alert(Client, bad_certificate). 291 292%%-------------------------------------------------------------------- 293ocsp_stapling_no_staple() -> 294 [{doc, "Verify OCSP stapling works with a missing OCSP response."}]. 295ocsp_stapling_no_staple(Config) 296 when is_list(Config) -> 297 PrivDir = proplists:get_value(priv_dir, Config), 298 CACertsFile = filename:join(PrivDir, "a.server/cacerts.pem"), 299 300 GroupName = proplists:get_value(group, Config), 301 ServerOpts = [{log_level, debug}, 302 {group, GroupName}], 303 {ClientNode, _ServerNode, Hostname} = ssl_test_lib:run_where(Config), 304 305 %% Start a server that will not include an OCSP response. 306 Server = ssl_test_lib:start_server(openssl, 307 [{options, ServerOpts}], Config), 308 Port = ssl_test_lib:inet_port(Server), 309 310 ClientOpts = [{log_level, debug}, 311 {verify, verify_peer}, 312 {server_name_indication, disable}, 313 {cacertfile, CACertsFile}, 314 {ocsp_stapling, true}, 315 {ocsp_nonce, true} 316 ] ++ dtls_client_opt(GroupName), 317 318 Client = ssl_test_lib:start_client_error([{node, ClientNode},{port, Port}, 319 {host, Hostname}, {from, self()}, 320 {options, ClientOpts}]), 321 322 ssl_test_lib:check_client_alert(Client, bad_certificate). 323 324%%-------------------------------------------------------------------- 325%% Intrernal functions ----------------------------------------------- 326%%-------------------------------------------------------------------- 327start_ocsp_responder(ResponderPort, PrivDir) -> 328 Starter = self(), 329 Pid = erlang:spawn_link( 330 ?MODULE, ocsp_responder_init, [ResponderPort, PrivDir, Starter]), 331 receive 332 {started, Pid} -> 333 Pid; 334 {'EXIT', Pid, Reason} -> 335 throw({unable_to_start_ocsp_service, Reason}) 336 end. 337 338ocsp_responder_init(ResponderPort, PrivDir, Starter) -> 339 Index = filename:join(PrivDir, "otpCA/index.txt"), 340 CACerts = filename:join(PrivDir, "b.server/cacerts.pem"), 341 Cert = filename:join(PrivDir, "b.server/cert.pem"), 342 Key = filename:join(PrivDir, "b.server/key.pem"), 343 344 Args = ["ocsp", "-index", Index, "-CA", CACerts, "-rsigner", Cert, 345 "-rkey", Key, "-port", erlang:integer_to_list(ResponderPort)], 346 process_flag(trap_exit, true), 347 Port = ssl_test_lib:portable_open_port("openssl", Args), 348 ocsp_responder_loop(Port, {new, Starter}). 349 350ocsp_responder_loop(Port, {Status, Starter} = State) -> 351 receive 352 stop_ocsp_responder -> 353 ct:log("Shut down OCSP responder!~n"), 354 ok = ssl_test_lib:close_port(Port); 355 {_Port, closed} -> 356 ct:log("Port Closed~n"), 357 ok; 358 {'EXIT', _Port, Reason} -> 359 ct:log("Port Closed ~p~n",[Reason]), 360 ok; 361 {Port, {data, _Msg}} when Status == new -> 362 Starter ! {started, self()}, 363 ocsp_responder_loop(Port, {started, undefined}); 364 {Port, {data, Msg}} -> 365 ct:pal("Responder Msg ~p~n",[Msg]), 366 ocsp_responder_loop(Port, State) 367 after 1000 -> 368 case Status of 369 new -> 370 exit(no_ocsp_server); 371 _ -> 372 ocsp_responder_loop(Port, State) 373 end 374 end. 375 376stop_ocsp_responder(Pid) -> 377 Pid ! stop_ocsp_responder. 378 379get_free_port() -> 380 {ok, Listen} = gen_tcp:listen(0, [{reuseaddr, true}]), 381 {ok, Port} = inet:port(Listen), 382 ok = gen_tcp:close(Listen), 383 Port. 384 385dtls_client_opt('dtlsv1.2') -> 386 [{protocol, dtls}]; 387dtls_client_opt(_Other) -> 388 [].