diff options
-rw-r--r-- | erts/test/erl_print_SUITE.erl | 4 | ||||
-rw-r--r-- | erts/test/ethread_SUITE.erl | 4 | ||||
-rw-r--r-- | lib/common_test/test/ct_test_support.erl | 4 | ||||
-rw-r--r-- | lib/dialyzer/src/Makefile | 2 | ||||
-rw-r--r-- | lib/dialyzer/src/dialyzer_analysis_callgraph.erl | 6 | ||||
-rw-r--r-- | lib/dialyzer/src/dialyzer_dataflow.erl | 14 | ||||
-rw-r--r-- | lib/dialyzer/src/dialyzer_gui_wx.erl | 57 | ||||
-rw-r--r-- | lib/dialyzer/test/small_SUITE_data/src/maybe_servers.erl | 31 | ||||
-rw-r--r-- | lib/dialyzer/vsn.mk | 2 | ||||
-rw-r--r-- | lib/mnesia/src/mnesia_loader.erl | 2 | ||||
-rw-r--r-- | lib/runtime_tools/test/Makefile | 1 | ||||
-rw-r--r-- | lib/runtime_tools/test/dyntrace_SUITE.erl | 224 | ||||
-rw-r--r-- | lib/ssl/doc/src/ssl.xml | 23 | ||||
-rw-r--r-- | lib/ssl/src/ssl.erl | 15 | ||||
-rw-r--r-- | lib/ssl/src/ssl_connection.erl | 39 | ||||
-rw-r--r-- | lib/ssl/src/ssl_handshake.erl | 14 | ||||
-rw-r--r-- | lib/ssl/src/ssl_tls1.erl | 2 |
17 files changed, 397 insertions, 47 deletions
diff --git a/erts/test/erl_print_SUITE.erl b/erts/test/erl_print_SUITE.erl index adc353bd51..165e005960 100644 --- a/erts/test/erl_print_SUITE.erl +++ b/erts/test/erl_print_SUITE.erl @@ -34,7 +34,7 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, - init_per_testcase/2, fin_per_testcase/2]). + init_per_testcase/2, end_per_testcase/2]). -export([erlang_display/1, integer/1, float/1, string/1, character/1, snprintf/1, quote/1]). @@ -247,7 +247,7 @@ init_per_testcase(Case, Config) -> Dog = ?t:timetrap(?DEFAULT_TIMEOUT), [{testcase, Case}, {watchdog, Dog} |Config]. -fin_per_testcase(_Case, Config) -> +end_per_testcase(_Case, Config) -> Dog = ?config(watchdog, Config), ?t:timetrap_cancel(Dog), ok. diff --git a/erts/test/ethread_SUITE.erl b/erts/test/ethread_SUITE.erl index 5bb5aed3ed..84b08d93d5 100644 --- a/erts/test/ethread_SUITE.erl +++ b/erts/test/ethread_SUITE.erl @@ -33,7 +33,7 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, - init_per_testcase/2, fin_per_testcase/2]). + init_per_testcase/2, end_per_testcase/2]). -export([create_join_thread/1, equal_tids/1, @@ -250,7 +250,7 @@ init_per_testcase(_Case, Config) -> Dog = ?t:timetrap(?DEFAULT_TIMEOUT), [{watchdog, Dog}|Config]. -fin_per_testcase(_Case, Config) -> +end_per_testcase(_Case, Config) -> Dog = ?config(watchdog, Config), ?t:timetrap_cancel(Dog), ok. diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl index 62c167d78b..a712ab6cb9 100644 --- a/lib/common_test/test/ct_test_support.erl +++ b/lib/common_test/test/ct_test_support.erl @@ -1013,8 +1013,8 @@ result_match({SkipOrFail,{ErrorInd,{EMod,EFunc,{Why,'_'}}}}, true; result_match({failed,{timetrap_timeout,{'$approx',Num}}}, {failed,{timetrap_timeout,Value}}) -> - if Value >= trunc(Num-0.01*Num), - Value =< trunc(Num+0.01*Num) -> true; + if Value >= trunc(Num-0.02*Num), + Value =< trunc(Num+0.02*Num) -> true; true -> false end; result_match(Result, Result) -> diff --git a/lib/dialyzer/src/Makefile b/lib/dialyzer/src/Makefile index 810f86dc21..97c5469d39 100644 --- a/lib/dialyzer/src/Makefile +++ b/lib/dialyzer/src/Makefile @@ -86,7 +86,7 @@ APPUP_TARGET= $(EBIN)/$(APPUP_FILE) ifeq ($(NATIVE_LIBS_ENABLED),yes) ERL_COMPILE_FLAGS += +native endif -ERL_COMPILE_FLAGS += +warn_exported_vars +warn_unused_import +warn_untyped_record +warn_missing_spec +ERL_COMPILE_FLAGS += +warn_exported_vars +warn_unused_import +warn_untyped_record +warn_missing_spec +warnings_as_errors # ---------------------------------------------------- # Targets diff --git a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl index 5be870d78f..b6dbfdfacf 100644 --- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl @@ -463,8 +463,10 @@ default_includes(Dir) -> rcv_and_send_ext_types(Parent) -> Self = self(), Self ! {Self, done}, - ExtTypes = rcv_ext_types(Self, []), - Parent ! {Self, ext_types, ExtTypes}, + case rcv_ext_types(Self, []) of + [] -> ok; + ExtTypes -> Parent ! {Self, ext_types, ExtTypes} + end, ok. rcv_ext_types(Self, ExtTypes) -> diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl index aba13278ff..d1dd7e1c34 100644 --- a/lib/dialyzer/src/dialyzer_dataflow.erl +++ b/lib/dialyzer/src/dialyzer_dataflow.erl @@ -761,7 +761,13 @@ handle_apply_or_call([{TypeOfApply, {Fun, Sig, Contr, LocalRet}}|Left], true -> AccArgTypes; false -> [t_sup(X, Y) || {X, Y} <- lists:zip(NewArgTypes, AccArgTypes)] end, - NewAccRet = t_sup(AccRet, t_inf(RetWithoutLocal, LocalRet, opaque)), + TotalRet = + case t_is_none(LocalRet) andalso t_is_unit(RetWithoutLocal) of + true -> RetWithoutLocal; + false -> t_inf(RetWithoutLocal, LocalRet, opaque) + end, + NewAccRet = t_sup(AccRet, TotalRet), + ?debug("NewAccRet: ~s\n", [t_to_string(NewAccRet)]), handle_apply_or_call(Left, Args, ArgTypes, Map, Tree, State3, NewAccArgTypes, NewAccRet); handle_apply_or_call([], Args, _ArgTypes, Map, _Tree, State, @@ -3109,6 +3115,7 @@ init_fun_tab([Fun|Left], Dict, TreeMap, Callgraph, Plt, Opaques) -> NewDict = dict:store(Fun, FunEntry, Dict), init_fun_tab(Left, NewDict, TreeMap, Callgraph, Plt, Opaques); init_fun_tab([], Dict, _TreeMap, _Callgraph, _Plt, _Opaques) -> + ?debug("DICT:~p\n",[dict:to_list(Dict)]), Dict. state__update_fun_env(Tree, Map, #state{envs = Envs} = State) -> @@ -3140,7 +3147,9 @@ state__fun_type(Fun, #state{fun_tab = FunTab}) -> if is_integer(Fun) -> Fun; true -> get_label(Fun) end, - case dict:find(Label, FunTab) of + Entry = dict:find(Label, FunTab), + ?debug("FunType ~p:~p\n",[Label, Entry]), + case Entry of {ok, {not_handled, {A, R}}} -> t_fun(A, R); {ok, {A, R}} -> @@ -3248,6 +3257,7 @@ state__fun_info(Fun, #state{callgraph = CG, fun_tab = FunTab, plt = PLT}) -> {not_handled, {_Args, Ret}} -> Ret; {_Args, Ret} -> Ret end, + ?debug("LocalRet: ~s\n", [t_to_string(LocalRet)]), {Fun, Sig, Contract, LocalRet}. state__find_apply_return(Tree, #state{callgraph = Callgraph} = State) -> diff --git a/lib/dialyzer/src/dialyzer_gui_wx.erl b/lib/dialyzer/src/dialyzer_gui_wx.erl index 9ff32bd8b1..8f470dd650 100644 --- a/lib/dialyzer/src/dialyzer_gui_wx.erl +++ b/lib/dialyzer/src/dialyzer_gui_wx.erl @@ -40,39 +40,38 @@ %%------------------------------------------------------------------------ --type wx_object() :: any(). %% XXX: should be imported from wx - --record(menu, {file :: wx_object(), - warnings :: wx_object(), - plt :: wx_object(), - options :: wx_object(), - help :: wx_object()}). - --record(gui_state, {add :: wx_object(), - add_dir :: wx_object(), - add_rec :: wx_object(), - chosen_box :: wx_object(), +-record(menu, {file :: wx:wx_object(), + warnings :: wx:wx_object(), + plt :: wx:wx_object(), + options :: wx:wx_object(), + help :: wx:wx_object()}). +-type menu() :: #menu{}. + +-record(gui_state, {add :: wx:wx_object(), + add_dir :: wx:wx_object(), + add_rec :: wx:wx_object(), + chosen_box :: wx:wx_object(), analysis_pid :: pid(), - del_file :: wx_object(), + del_file :: wx:wx_object(), doc_plt :: dialyzer_plt:plt(), - clear_chosen :: wx_object(), - clear_log :: wx_object(), - explain_warn :: wx_object(), - clear_warn :: wx_object(), + clear_chosen :: wx:wx_object(), + clear_log :: wx:wx_object(), + explain_warn :: wx:wx_object(), + clear_warn :: wx:wx_object(), init_plt :: dialyzer_plt:plt(), - dir_entry :: wx_object(), - file_box :: wx_object(), + dir_entry :: wx:wx_object(), + file_box :: wx:wx_object(), files_to_analyze :: ordset(string()), - gui :: wx_object(), - log :: wx_object(), - menu :: #menu{}, - mode :: wx_object(), + gui :: wx:wx_object(), + log :: wx:wx_object(), + menu :: menu(), + mode :: wx:wx_object(), options :: #options{}, - run :: wx_object(), - stop :: wx_object(), - frame :: wx_object(), - warnings_box :: wx_object(), - explanation_box :: wx_object(), + run :: wx:wx_object(), + stop :: wx:wx_object(), + frame :: wx:wx_object(), + warnings_box :: wx:wx_object(), + explanation_box :: wx:wx_object(), wantedWarnings :: list(), rawWarnings :: list(), backend_pid :: pid(), @@ -824,7 +823,7 @@ build_analysis_record(#gui_state{mode = Mode, menu = Menu, options = Options, 1 -> src_code end, InitPlt = - case wxMenu:isChecked(Menu#menu.plt,?menuID_PLT_INIT_EMPTY) of + case wxMenu:isChecked(Menu#menu.plt, ?menuID_PLT_INIT_EMPTY) of true -> dialyzer_plt:new(); false -> InitPlt0 end, diff --git a/lib/dialyzer/test/small_SUITE_data/src/maybe_servers.erl b/lib/dialyzer/test/small_SUITE_data/src/maybe_servers.erl new file mode 100644 index 0000000000..237f43b1a6 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/maybe_servers.erl @@ -0,0 +1,31 @@ +-module(maybe_servers). + +-export([maybe_server/2, mirror_maybe_server/2]). + +maybe_server(O, I) -> + case O of + no -> + maybe_loop(fun(_) -> fin end, I); + yes -> + maybe_loop(fun(X) -> {ok, X} end, I) + end. + +maybe_loop(F, X)-> + case F(X) of + {ok, Y} -> maybe_loop(F, Y); + fin -> exit(n) + end. + +mirror_maybe_loop(F, X)-> + case F(X) of + {ok, Y} -> mirror_maybe_loop(F, Y); + fin -> exit(n) + end. + +mirror_maybe_server(O, I) -> + case O of + no -> + mirror_maybe_loop(fun(_) -> fin end, I); + yes -> + mirror_maybe_loop(fun(X) -> {ok, X} end, I) + end. diff --git a/lib/dialyzer/vsn.mk b/lib/dialyzer/vsn.mk index 622e51b859..edafcc4afb 100644 --- a/lib/dialyzer/vsn.mk +++ b/lib/dialyzer/vsn.mk @@ -1 +1 @@ -DIALYZER_VSN = 2.5 +DIALYZER_VSN = 2.5.1 diff --git a/lib/mnesia/src/mnesia_loader.erl b/lib/mnesia/src/mnesia_loader.erl index e443b54016..4ba400fbbf 100644 --- a/lib/mnesia/src/mnesia_loader.erl +++ b/lib/mnesia/src/mnesia_loader.erl @@ -57,7 +57,7 @@ disc_load_table(Tab, Reason) -> do_get_disc_copy2(Tab, _Reason, Storage, _Type) when Storage == unknown -> verbose("Local table copy of ~p has recently been deleted, ignored.~n", [Tab]), - {loaded, ok}; %% ? + {not_loaded, storage_unknown}; do_get_disc_copy2(Tab, Reason, Storage, Type) when Storage == disc_copies -> %% NOW we create the actual table Repair = mnesia_monitor:get_env(auto_repair), diff --git a/lib/runtime_tools/test/Makefile b/lib/runtime_tools/test/Makefile index cfaf420d65..3e50ba02ca 100644 --- a/lib/runtime_tools/test/Makefile +++ b/lib/runtime_tools/test/Makefile @@ -3,6 +3,7 @@ include $(ERL_TOP)/make/target.mk include $(ERL_TOP)/make/$(TARGET)/otp.mk MODULES = \ + dyntrace_SUITE \ runtime_tools_SUITE \ inviso_testmodule1_foo \ inviso_SUITE \ diff --git a/lib/runtime_tools/test/dyntrace_SUITE.erl b/lib/runtime_tools/test/dyntrace_SUITE.erl new file mode 100644 index 0000000000..0e4f369ed0 --- /dev/null +++ b/lib/runtime_tools/test/dyntrace_SUITE.erl @@ -0,0 +1,224 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2012. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(dyntrace_SUITE). +-include_lib("test_server/include/test_server.hrl"). + +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2]). +-export([init_per_testcase/2, end_per_testcase/2]). + +%% Test cases +-export([smoke/1,process/1]). + +%% Default timetrap timeout (set in init_per_testcase) +-define(default_timeout, ?t:minutes(1)). + +init_per_testcase(_Case, Config) -> + Dog = test_server:timetrap(?default_timeout), + [{watchdog,Dog}|Config]. + +end_per_testcase(_Case, Config) -> + Dog = ?config(watchdog, Config), + ?t:timetrap_cancel(Dog), + ok. + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + case erlang:system_info(dynamic_trace) of + none -> + {skip,"No dynamic trace in this run-time system"}; + dtrace -> + [{group,smoke}]; + systemtap -> + {skip,"SystemTap tests currently not supported"} + end. + +groups() -> + [{smoke,[sequence],[smoke,{group,rest}]}, + {rest,[], + [process]}]. + +init_per_suite(Config) -> + N = "beam" ++ + case erlang:system_info(debug_compiled) of + false -> ""; + true -> ".debug" + end ++ + case erlang:system_info(smp_support) of + false -> ""; + true -> ".smp" + end, + [{emu_name,N}|Config]. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +smoke(Config) -> + Emu = ?t:lookup_config(emu_name, Config), + BinEmu = list_to_binary(Emu), + case erlang:system_info(dynamic_trace) of + dtrace -> + Probes = os:cmd("sudo /usr/sbin/dtrace -l -m" ++ Emu), + io:put_chars(Probes), + [_|Lines] = re:split(Probes, "\n", [trim]), + [{_,_} = binary:match(L, BinEmu) || L <- Lines], + ok + end, + + %% Test that the framework for running dtrace/systemtap works + %% by executing an empty script. + {ok,[]} = dyntrace("", fun() -> ok end), + ok. + + +process(_Config) -> + Script = [{probe,"process-spawn"}, + {action,[{printf,["spawn %s %s\n",{arg,0},{arg,1}]}]}, + {probe,"process-scheduled"}, + {action,[{printf,["in %s\n",{arg,0}]}]}, + {probe,"process-unscheduled"}, + {action,[{printf,["out %s\n",{arg,0}]}]}, + {probe,"process-hibernate"}, + {action,[{printf,["hibernate %s %s\n",{arg,0},{arg,1}]}]}, + {probe,"process-exit"}, + {action,[{printf,["exit %s %s\n",{arg,0},{arg,1}]}]} + ], + F = fun() -> + {Pid,Ref} = spawn_monitor(fun my_process/0), + Pid ! hibernate, + Pid ! quit, + receive + {'DOWN',Ref,process,Pid,{terminated,Pid}} -> + ok + end, + Pid + end, + {Pid,Output0} = dyntrace(Script, F), + Output1 = [termify_line(L) || L <- Output0], + PidStr = pid_to_list(Pid), + Output = [L || L <- Output1, element(2, L) =:= PidStr], + Reason = "{terminated,"++PidStr++"}", + io:format("~p\n", [Output]), + [{spawn,PidStr,"erlang:apply/2"}, + {in,PidStr}, + {hibernate,PidStr,"erlang:apply/2"}, + {out,PidStr}, + {in,PidStr}, + {exit,PidStr,Reason}, + {out,PidStr}] = Output, + ok. + +termify_line(L) -> + [H|T] = re:split(L, " ", [{return,list}]), + list_to_tuple([list_to_atom(H)|T]). + +my_process() -> + receive + hibernate -> + erlang:hibernate(erlang, apply, [fun my_process/0,[]]); + quit -> + exit({terminated,self()}) + end. + +%%% +%%% Utility functions. +%%% + +dyntrace(Script0, Action) -> + Sudo = os:find_executable(sudo), + {Termination,Pid} = termination_probe(), + Script1 = Script0++Termination, + Script = translate_script(Script1), + io:format("~s\n", [Script]), + SrcFile = "test-dyntrace.d", + ok = file:write_file(SrcFile, Script), + Args = ["/usr/sbin/dtrace", "-q","-s",SrcFile], + Port = open_port({spawn_executable,Sudo}, + [{args,Args},stream,in,stderr_to_stdout,eof]), + receive + {Port,{data,Sofar}} -> + Res = Action(), + Pid ! quit, + {Res,get_data(Port, Sofar)} + end. + +get_data(Port, Sofar) -> + receive + {Port,{data,Bytes}} -> + get_data(Port, [Sofar|Bytes]); + {Port,eof} -> + port_close(Port), + [$\n|T] = lists:flatten(Sofar), + re:split(T, "\n", [{return,list},trim]) + end. + +termination_probe() -> + Pid = spawn(fun() -> + receive + _ -> + exit(done) + end + end), + S = [{'BEGIN',[{printf,["\n"]}]}, + {probe,"process-exit"}, + {pred,{'==',{arg,0},Pid}}, + {action,[{exit,[0]}]}], + {S,Pid}. + +translate_script(Script) -> + [dtrace_op(Op) || Op <- Script]. + +dtrace_op({probe,Function}) -> + OsPid = os:getpid(), + ["erlang",OsPid,":::",Function,$\n]; +dtrace_op({pred,Pred}) -> + ["/",dtrace_op(Pred),"/\n"]; +dtrace_op({action,List}) -> + ["{ ",action_list(List)," }\n\n"]; +dtrace_op({'BEGIN',List}) -> + ["BEGIN { ",action_list(List)," }\n\n"]; +dtrace_op({'==',Op1,Op2}) -> + [dtrace_op(Op1)," == ",dtrace_op(Op2)]; +dtrace_op({arg,N}) -> + ["copyinstr(arg",integer_to_list(N),")"]; +dtrace_op({Func,List}) when is_atom(Func), is_list(List) -> + [atom_to_list(Func),"(",comma_sep_ops(List),")"]; +dtrace_op(Pid) when is_pid(Pid) -> + ["\"",pid_to_list(Pid),"\""]; +dtrace_op(Str) when is_integer(hd(Str)) -> + io_lib:format("~p", [Str]); +dtrace_op(Int) when is_integer(Int) -> + integer_to_list(Int). + +comma_sep_ops([A,B|T]) -> + [dtrace_op(A),","|comma_sep_ops([B|T])]; +comma_sep_ops([H]) -> + dtrace_op(H); +comma_sep_ops([]) -> []. + +action_list([H|T]) -> + [dtrace_op(H),";"|action_list(T)]; +action_list([]) -> []. diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index 4910a6f1b8..62a79e15eb 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -122,6 +122,9 @@ <p> <c>hash() = md5 | sha </c></p> + <p><c>prf_random() = client_random | server_random + </c></p> + </section> <section> @@ -561,6 +564,26 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | </func> <func> + <name>prf(Socket, Secret, Label, Seed, WantedLength) -> {ok, binary()} | {error, reason()}</name> + <fsummary>Use a sessions pseudo random function to generate key material.</fsummary> + <type> + <v>Socket = sslsocket()</v> + <v>Secret = binary() | master_secret</v> + <v>Label = binary()</v> + <v>Seed = [binary() | prf_random()]</v> + <v>WantedLength = non_neg_integer()</v> + </type> + <desc> + <p>Use the pseudo random function (PRF) of a TLS session to generate + additional key material. It either takes user generated values for + <c>Secret</c> and <c>Seed</c> or atoms directing it use a specific + value from the session security parameters.</p> + <p>This function can only be used with TLS connections, <c>{error, undefined}</c> + is returned for SSLv3 connections.</p> + </desc> + </func> + + <func> <name>renegotiate(Socket) -> ok | {error, Reason}</name> <fsummary> Initiates a new handshake.</fsummary> <type> diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index d0693445e0..1048583eca 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -30,7 +30,7 @@ controlling_process/2, listen/2, pid/1, peername/1, peercert/1, recv/2, recv/3, send/2, getopts/2, setopts/2, sockname/1, versions/0, session_info/1, format_error/1, - renegotiate/1]). + renegotiate/1, prf/5]). -deprecated({pid, 1, next_major_release}). @@ -67,7 +67,7 @@ -type ssl_imp() :: new | old. -type transport_option() :: {cb_info, {CallbackModule::atom(), DataTag::atom(), ClosedTag::atom()}}. - +-type prf_random() :: client_random | server_random. %%-------------------------------------------------------------------- -spec start() -> ok | {error, reason()}. @@ -414,6 +414,17 @@ versions() -> renegotiate(#sslsocket{pid = Pid, fd = new_ssl}) -> ssl_connection:renegotiation(Pid). +%%-------------------------------------------------------------------- +-spec prf(#sslsocket{}, binary() | 'master_secret', binary(), + binary() | prf_random(), non_neg_integer()) -> + {ok, binary()} | {error, reason()}. +%% +%% Description: use a ssl sessions TLS PRF to generate key material +%%-------------------------------------------------------------------- +prf(#sslsocket{pid = Pid, fd = new_ssl}, + Secret, Label, Seed, WantedLength) -> + ssl_connection:prf(Pid, Secret, Label, Seed, WantedLength). + %%--------------------------------------------------------------- -spec format_error({error, term()}) -> list(). %% diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 28dd0c85d0..59133dccf0 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -40,7 +40,8 @@ -export([send/2, recv/3, connect/7, ssl_accept/6, handshake/2, socket_control/3, close/1, shutdown/2, new_user/2, get_opts/2, set_opts/2, info/1, session_info/1, - peer_certificate/1, sockname/1, peername/1, renegotiation/1]). + peer_certificate/1, sockname/1, peername/1, renegotiation/1, + prf/5]). %% Called by ssl_connection_sup -export([start_link/7]). @@ -273,6 +274,16 @@ peer_certificate(ConnectionPid) -> renegotiation(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, renegotiate). +%%-------------------------------------------------------------------- +-spec prf(pid(), binary() | 'master_secret', binary(), + binary() | ssl:prf_secret(), non_neg_integer()) -> + {ok, binary()} | {error, reason()} | {'EXIT', term()}. +%% +%% Description: use a ssl sessions TLS PRF to generate key material +%%-------------------------------------------------------------------- +prf(ConnectionPid, Secret, Label, Seed, WantedLength) -> + sync_send_all_state_event(ConnectionPid, {prf, Secret, Label, Seed, WantedLength}). + %%==================================================================== %% ssl_connection_sup API %%==================================================================== @@ -868,6 +879,32 @@ handle_sync_event(renegotiate, From, connection, State) -> handle_sync_event(renegotiate, _, StateName, State) -> {reply, {error, already_renegotiating}, StateName, State, get_timeout(State)}; +handle_sync_event({prf, Secret, Label, Seed, WantedLength}, _, StateName, + #state{connection_states = ConnectionStates, + negotiated_version = Version} = State) -> + ConnectionState = + ssl_record:current_connection_state(ConnectionStates, read), + SecParams = ConnectionState#connection_state.security_parameters, + #security_parameters{master_secret = MasterSecret, + client_random = ClientRandom, + server_random = ServerRandom} = SecParams, + Reply = try + SecretToUse = case Secret of + _ when is_binary(Secret) -> Secret; + master_secret -> MasterSecret + end, + SeedToUse = lists:reverse( + lists:foldl(fun(X, Acc) when is_binary(X) -> [X|Acc]; + (client_random, Acc) -> [ClientRandom|Acc]; + (server_random, Acc) -> [ServerRandom|Acc] + end, [], Seed)), + ssl_handshake:prf(Version, SecretToUse, Label, SeedToUse, WantedLength) + catch + exit:_ -> {error, badarg}; + error:Reason -> {error, Reason} + end, + {reply, Reply, StateName, State, get_timeout(State)}; + handle_sync_event(info, _, StateName, #state{negotiated_version = Version, session = #session{cipher_suite = Suite}} = State) -> diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 542033e6ce..ef60cac6df 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -37,7 +37,7 @@ finished/4, verify_connection/5, get_tls_handshake/2, decode_client_key/3, server_hello_done/0, encode_handshake/2, init_hashes/0, update_hashes/2, - decrypt_premaster_secret/2]). + decrypt_premaster_secret/2, prf/5]). -export([dec_hello_extensions/2]). @@ -543,6 +543,18 @@ server_key_exchange_hash(dhe_dss, Value) -> crypto:sha(Value). %%-------------------------------------------------------------------- +-spec prf(tls_version(), binary(), binary(), binary(), non_neg_integer()) -> + {ok, binary()} | {error, undefined}. +%% +%% Description: use the TLS PRF to generate key material +%%-------------------------------------------------------------------- +prf({3,0}, _, _, _, _) -> + {error, undefined}; +prf({3,N}, Secret, Label, Seed, WantedLength) + when N == 1; N == 2 -> + {ok, ssl_tls1:prf(Secret, Label, Seed, WantedLength)}. + +%%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- get_tls_handshake_aux(<<?BYTE(Type), ?UINT24(Length), diff --git a/lib/ssl/src/ssl_tls1.erl b/lib/ssl/src/ssl_tls1.erl index 5f9850c386..7351f34b61 100644 --- a/lib/ssl/src/ssl_tls1.erl +++ b/lib/ssl/src/ssl_tls1.erl @@ -29,7 +29,7 @@ -include("ssl_record.hrl"). -export([master_secret/3, finished/3, certificate_verify/2, mac_hash/7, - setup_keys/6, suites/0]). + setup_keys/6, suites/0, prf/4]). %%==================================================================== %% Internal application API |