aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl
diff options
context:
space:
mode:
authorKenneth Lakin <[email protected]>2016-04-30 20:17:03 -0700
committerIngela Anderton Andin <[email protected]>2016-06-09 18:12:48 +0200
commit7001063d331e5702ba21bfec3b3d84c06a31fefb (patch)
treedd71b0cf069365497b241d73d7222c01c13b2773 /lib/ssl
parent1ab69efa960703b86a13ea6ba96f4fd56f1565f9 (diff)
downloadotp-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')
-rw-r--r--lib/ssl/src/ssl_connection.erl5
-rw-r--r--lib/ssl/src/ssl_handshake.erl12
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl122
3 files changed, 129 insertions, 10 deletions
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index f774873269..0f0072ba34 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -821,7 +821,8 @@ handle_sync_event({prf, Secret, Label, Seed, WantedLength}, _, StateName,
SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{master_secret = MasterSecret,
client_random = ClientRandom,
- server_random = ServerRandom} = SecParams,
+ server_random = ServerRandom,
+ prf_algorithm = PRFAlgorithm} = SecParams,
Reply = try
SecretToUse = case Secret of
_ when is_binary(Secret) -> Secret;
@@ -832,7 +833,7 @@ handle_sync_event({prf, Secret, Label, Seed, WantedLength}, _, StateName,
(client_random, Acc) -> [ClientRandom|Acc];
(server_random, Acc) -> [ServerRandom|Acc]
end, [], Seed)),
- ssl_handshake:prf(Version, SecretToUse, Label, SeedToUse, WantedLength)
+ ssl_handshake:prf(Version, PRFAlgorithm, SecretToUse, Label, SeedToUse, WantedLength)
catch
exit:_ -> {error, badarg};
error:Reason -> {error, Reason}
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 644903cf4b..235d6efbb6 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -74,7 +74,7 @@
]).
%% MISC
--export([select_version/3, prf/5, select_hashsign/5,
+-export([select_version/3, prf/6, select_hashsign/5,
select_hashsign_algs/3,
premaster_secret/2, premaster_secret/3, premaster_secret/4]).
@@ -564,17 +564,15 @@ server_key_exchange_hash(md5sha, Value) ->
server_key_exchange_hash(Hash, Value) ->
crypto:hash(Hash, Value).
%%--------------------------------------------------------------------
--spec prf(ssl_record:ssl_version(), binary(), binary(), [binary()], non_neg_integer()) ->
+-spec prf(ssl_record:ssl_version(), non_neg_integer(), binary(), binary(), [binary()], non_neg_integer()) ->
{ok, binary()} | {error, undefined}.
%%
%% Description: use the TLS PRF to generate key material
%%--------------------------------------------------------------------
-prf({3,0}, _, _, _, _) ->
+prf({3,0}, _, _, _, _, _) ->
{error, undefined};
-prf({3,1}, Secret, Label, Seed, WantedLength) ->
- {ok, tls_v1:prf(?MD5SHA, Secret, Label, Seed, WantedLength)};
-prf({3,_N}, Secret, Label, Seed, WantedLength) ->
- {ok, tls_v1:prf(?SHA256, Secret, Label, Seed, WantedLength)}.
+prf({3,_N}, PRFAlgo, Secret, Label, Seed, WantedLength) ->
+ {ok, tls_v1:prf(PRFAlgo, Secret, Label, Seed, WantedLength)}.
%%--------------------------------------------------------------------
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"),