diff options
author | Kenneth Lakin <[email protected]> | 2016-04-30 20:17:03 -0700 |
---|---|---|
committer | Ingela Anderton Andin <[email protected]> | 2016-06-09 18:12:48 +0200 |
commit | 7001063d331e5702ba21bfec3b3d84c06a31fefb (patch) | |
tree | dd71b0cf069365497b241d73d7222c01c13b2773 /lib/ssl/test | |
parent | 1ab69efa960703b86a13ea6ba96f4fd56f1565f9 (diff) | |
download | otp-7001063d331e5702ba21bfec3b3d84c06a31fefb.tar.gz otp-7001063d331e5702ba21bfec3b3d84c06a31fefb.tar.bz2 otp-7001063d331e5702ba21bfec3b3d84c06a31fefb.zip |
ssl: Use cipher suite's PRF in prf/5
Use the negotiated cipher suite's PRF algorithm in calls to
ssl:prf/5, rather than a hard-coded one.
For TLS 1.0 the PRF algorithm was hard-coded to MD5/SHA1. This
was correct 100% of the time.
For TLS 1.1 and 1.2 the PRF algorithm was hard-coded to SHA256.
This was correct only some of the time for TLS 1.2 and none of the
time for TLS 1.1. Because the TLS handshake code calls tls_v1:prf/5
through another path, the handshaking process used the negotiated
PRF and did not encounter this bug.
A new test (prf) has been added to ssl_basic_SUITE to guard against future
breakage.
Diffstat (limited to 'lib/ssl/test')
-rw-r--r-- | lib/ssl/test/ssl_basic_SUITE.erl | 122 |
1 files changed, 121 insertions, 1 deletions
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index 89cdd40b4c..16211a54c1 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -145,7 +145,8 @@ api_tests() -> versions_option, server_name_indication_option, accept_pool, - new_options_in_accept + new_options_in_accept, + prf ]. session_tests() -> @@ -324,6 +325,31 @@ init_per_testcase(rizzo, Config) -> ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]), ct:timetrap({seconds, 40}), Config; +init_per_testcase(prf, Config) -> + ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]), + ct:timetrap({seconds, 40}), + case ?config(tc_group_path, Config) of + [] -> Prop = []; + [Prop] -> Prop + end, + case ?config(name, Prop) of + undefined -> TlsVersions = [sslv3, tlsv1, 'tlsv1.1', 'tlsv1.2']; + TlsVersion when is_atom(TlsVersion) -> + TlsVersions = [TlsVersion] + end, + PRFS=[md5, sha, sha256, sha384, sha512], + %All are the result of running tls_v1:prf(PrfAlgo, <<>>, <<>>, <<>>, 16) + %with the specified PRF algorithm + ExpectedPrfResults= + [{md5, <<96,139,180,171,236,210,13,10,28,32,2,23,88,224,235,199>>}, + {sha, <<95,3,183,114,33,169,197,187,231,243,19,242,220,228,70,151>>}, + {sha256, <<166,249,145,171,43,95,158,232,6,60,17,90,183,180,0,155>>}, + {sha384, <<153,182,217,96,186,130,105,85,65,103,123,247,146,91,47,106>>}, + {sha512, <<145,8,98,38,243,96,42,94,163,33,53,49,241,4,127,28>>}, + %TLS 1.0 and 1.1 PRF: + {md5sha, <<63,136,3,217,205,123,200,177,251,211,17,229,132,4,173,80>>}], + TestPlan = prf_create_plan(TlsVersions, PRFS, ExpectedPrfResults), + [{prf_test_plan, TestPlan} | Config]; init_per_testcase(TestCase, Config) when TestCase == ssl_accept_timeout; TestCase == client_closes_socket; @@ -429,6 +455,25 @@ new_options_in_accept(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- +prf() -> + [{doc,"Test that ssl:prf/5 uses the negotiated PRF."}]. +prf(Config) when is_list(Config) -> + TestPlan = ?config(prf_test_plan, Config), + case TestPlan of + [] -> ct:fail({error, empty_prf_test_plan}); + _ -> lists:foreach(fun(Suite) -> + lists:foreach( + fun(Test) -> + V = ?config(tls_ver, Test), + C = ?config(ciphers, Test), + E = ?config(expected, Test), + P = ?config(prf, Test), + prf_run_test(Config, V, C, E, P) + end, Suite) + end, TestPlan) + end. + +%%-------------------------------------------------------------------- connection_info() -> [{doc,"Test the API function ssl:connection_information/1"}]. @@ -3662,6 +3707,81 @@ basic_test(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). +prf_create_plan(TlsVersions, PRFs, Results) -> + lists:foldl(fun(Ver, Acc) -> + A = prf_ciphers_and_expected(Ver, PRFs, Results), + [A|Acc] + end, [], TlsVersions). +prf_ciphers_and_expected(TlsVer, PRFs, Results) -> + case TlsVer of + TlsVer when TlsVer == sslv3 orelse TlsVer == tlsv1 + orelse TlsVer == 'tlsv1.1' -> + Ciphers = ssl:cipher_suites(), + {_, Expected} = lists:keyfind(md5sha, 1, Results), + [[{tls_ver, TlsVer}, {ciphers, Ciphers}, {expected, Expected}, {prf, md5sha}]]; + 'tlsv1.2' -> + lists:foldl( + fun(PRF, Acc) -> + Ciphers = prf_get_ciphers(TlsVer, PRF), + case Ciphers of + [] -> + ct:log("No ciphers for PRF algorithm ~p. Skipping.", [PRF]), + Acc; + Ciphers -> + {_, Expected} = lists:keyfind(PRF, 1, Results), + [[{tls_ver, TlsVer}, {ciphers, Ciphers}, {expected, Expected}, + {prf, PRF}] | Acc] + end + end, [], PRFs) + end. +prf_get_ciphers(TlsVer, PRF) -> + case TlsVer of + 'tlsv1.2' -> + lists:filter( + fun(C) when tuple_size(C) == 4 andalso + element(4, C) == PRF -> + true; + (_) -> false + end, ssl:cipher_suites()) + end. +prf_run_test(_, TlsVer, [], _, Prf) -> + ct:fail({error, cipher_list_empty, TlsVer, Prf}); +prf_run_test(Config, TlsVer, Ciphers, Expected, Prf) -> + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + BaseOpts = [{active, true}, {versions, [TlsVer]}, {ciphers, Ciphers}], + ServerOpts = BaseOpts ++ ?config(server_opts, Config), + ClientOpts = BaseOpts ++ ?config(client_opts, Config), + Server = ssl_test_lib:start_server( + [{node, ServerNode}, {port, 0}, {from, self()}, + {mfa, {?MODULE, prf_verify_value, [TlsVer, Expected, Prf]}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client( + [{node, ClientNode}, {port, Port}, + {host, Hostname}, {from, self()}, + {mfa, {?MODULE, prf_verify_value, [TlsVer, Expected, Prf]}}, + {options, ClientOpts}]), + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). +prf_verify_value(Socket, TlsVer, Expected, Algo) -> + Ret = ssl:prf(Socket, <<>>, <<>>, [<<>>], 16), + case TlsVer of + sslv3 -> + case Ret of + {error, undefined} -> ok; + _ -> + {error, {expected, {error, undefined}, + got, Ret, tls_ver, TlsVer, prf_algorithm, Algo}} + end; + _ -> + case Ret of + {ok, Expected} -> ok; + {ok, Val} -> {error, {expected, Expected, got, Val, tls_ver, TlsVer, + prf_algorithm, Algo}} + end + end. + send_recv_result_timeout_client(Socket) -> {error, timeout} = ssl:recv(Socket, 11, 500), ssl:send(Socket, "Hello world"), |