diff options
Diffstat (limited to 'lib')
59 files changed, 3999 insertions, 629 deletions
diff --git a/lib/common_test/doc/src/ct_run.xml b/lib/common_test/doc/src/ct_run.xml index d871908952..c87c765ae7 100644 --- a/lib/common_test/doc/src/ct_run.xml +++ b/lib/common_test/doc/src/ct_run.xml @@ -113,9 +113,9 @@ [-muliply_timetraps Multiplier] [-scale_timetraps] [-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc] - [-repeat N [-force_stop]] | - [-duration HHMMSS [-force_stop]] | - [-until [YYMoMoDD]HHMMSS [-force_stop]] + [-repeat N] | + [-duration HHMMSS [-force_stop [skip_rest]]] | + [-until [YYMoMoDD]HHMMSS [-force_stop [skip_rest]]] [-basic_html] [-ct_hooks CTHModule1 CTHOpts1 and CTHModule2 CTHOpts2 and .. CTHModuleN CTHOptsN] @@ -149,9 +149,9 @@ [-muliply_timetraps Multiplier] [-scale_timetraps] [-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc] - [-repeat N [-force_stop]] | - [-duration HHMMSS [-force_stop]] | - [-until [YYMoMoDD]HHMMSS [-force_stop]] + [-repeat N] | + [-duration HHMMSS [-force_stop [skip_rest]]] | + [-until [YYMoMoDD]HHMMSS [-force_stop [skip_rest]]] [-basic_html] [-ct_hooks CTHModule1 CTHOpts1 and CTHModule2 CTHOpts2 and .. CTHModuleN CTHOptsN] diff --git a/lib/common_test/doc/src/run_test_chapter.xml b/lib/common_test/doc/src/run_test_chapter.xml index 35f89153d3..afaed29626 100644 --- a/lib/common_test/doc/src/run_test_chapter.xml +++ b/lib/common_test/doc/src/run_test_chapter.xml @@ -174,7 +174,7 @@ <item><c><![CDATA[-repeat <n>]]></c>, tells Common Test to repeat the tests n times (see below).</item> <item><c><![CDATA[-duration <time>]]></c>, tells Common Test to repeat the tests for duration of time (see below).</item> <item><c><![CDATA[-until <stop_time>]]></c>, tells Common Test to repeat the tests until stop_time (see below).</item> - <item><c>-force_stop</c>, on timeout, the test run will be aborted when current test job is finished (see below).</item> + <item><c>-force_stop [skip_rest]</c>, on timeout, the test run will be aborted when current test job is finished. If <c>skip_rest</c> is provided the rest of the test cases in the current test job will be skipped (see below).</item> <item><c><![CDATA[-decrypt_key <key>]]></c>, provides a decryption key for <seealso marker="config_file_chapter#encrypted_config_files">encrypted configuration files</seealso>.</item> <item><c><![CDATA[-decrypt_file <key_file>]]></c>, points out a file containing a decryption key for @@ -1273,6 +1273,7 @@ <item><c>-duration DurTime ({duration,DurTime})</c>, where <c>DurTime</c> is the duration, see below.</item> <item><c>-until StopTime ({until,StopTime})</c>, where <c>StopTime</c> is finish time, see below.</item> <item><c>-force_stop ({force_stop,true})</c></item> + <item><c>-force_stop skip_rest ({force_stop,skip_rest})</c></item> </list> <p>The duration time, <c>DurTime</c>, is specified as <c>HHMMSS</c>. Example: <c>-duration 012030</c> or <c>{duration,"012030"}</c>, means the tests will @@ -1283,12 +1284,16 @@ Example: <c>-until 071001120000</c> or <c>{until,"071001120000"}</c>, which means the tests will be executed and (if time allows) repeated, until 12 o'clock on the 1st of Oct 2007.</p> - <p>When timeout occurs, Common Test will never abort the test run immediately, since + <p>When timeout occurs, Common Test will never abort the ongoing test case, since this might leave the system under test in an undefined, and possibly bad, state. - Instead Common Test will finish the current test job, or the complete test - run, before stopping. The latter is the default behaviour. The <c>force_stop</c> - flag/option tells Common Test to stop as soon as the current test job is finished. - Note that since Common Test always finishes off the current test job or test session, + Instead Common Test will by default finish the current test + run before stopping. If the <c>force_stop</c> flag is + given, Common Test will stop as soon as the current test job + is finished, and if the <c>force_stop</c> flag is given with + <c>skip_rest</c> Common Test will only complete the current + test case and skip the rest of the tests in the test job. + Note that since Common Test always finishes off at least the + current test case, the time specified with <c>duration</c> or <c>until</c> is never definitive!</p> <p>Log files from every single repeated test run is saved in normal Common Test fashion (see above). @@ -1312,6 +1317,18 @@ <p>Example 2:</p> <pre> + $ ct_run -dir $TEST_ROOT/to1 $TEST_ROOT/to2 -duration 001000 -forces_stop skip_rest</pre> + <p>Here the same test run as in Example 1, but with the + <c>force_stop</c> flag set to <c>skip_rest</c>. If the timeout + occurs while executing tests in directory to1, the rest of the + test cases in to1 will be skipped and then the test will be + aborted without running the tests in to2 another time. If the + timeout occurs while executing tests in directory to2, then the + rest of the test cases in to2 will be skipped and then the test + will be aborted.</p> + + <p>Example 3:</p> + <pre> $ date Fri Sep 28 15:00:00 MEST 2007 @@ -1321,7 +1338,7 @@ Common Test will finish the entire test run before stopping (i.e. the to1 and to2 test will always both be executed in the same test run).</p> - <p>Example 3:</p> + <p>Example 4:</p> <pre> $ ct_run -dir $TEST_ROOT/to1 $TEST_ROOT/to2 -repeat 5</pre> <p>Here the test run, including both the to1 and the to2 test, will be repeated 5 times.</p> diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl index 04a95a53fa..e6732f7fc7 100644 --- a/lib/common_test/src/ct.erl +++ b/lib/common_test/src/ct.erl @@ -153,7 +153,7 @@ run(TestDirs) -> %%% {auto_compile,Bool} | {create_priv_dir,CreatePrivDir} | %%% {multiply_timetraps,M} | {scale_timetraps,Bool} | %%% {repeat,N} | {duration,DurTime} | {until,StopTime} | -%%% {force_stop,Bool} | {decrypt,DecryptKeyOrFile} | +%%% {force_stop,ForceStop} | {decrypt,DecryptKeyOrFile} | %%% {refresh_logs,LogDir} | {logopts,LogOpts} | %%% {verbosity,VLevels} | {basic_html,Bool} | %%% {ct_hooks, CTHs} | {enable_builtin_hooks,Bool} | @@ -184,6 +184,7 @@ run(TestDirs) -> %%% N = integer() %%% DurTime = string(HHMMSS) %%% StopTime = string(YYMoMoDDHHMMSS) | string(HHMMSS) +%%% ForceStop = skip_rest | Bool %%% DecryptKeyOrFile = {key,DecryptKey} | {file,DecryptFile} %%% DecryptKey = string() %%% DecryptFile = string() diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl index 5fe4eaf511..b92fe1555f 100644 --- a/lib/common_test/src/ct_framework.erl +++ b/lib/common_test/src/ct_framework.erl @@ -64,38 +64,46 @@ init_tc(Mod,Func,Config) -> ok end, - case ct_util:get_testdata(curr_tc) of - {Suite,{suite0_failed,{require,Reason}}} -> - {skip,{require_failed_in_suite0,Reason}}; - {Suite,{suite0_failed,_}=Failure} -> - {skip,Failure}; + case Func=/=end_per_suite + andalso Func=/=end_per_group + andalso ct_util:get_testdata(skip_rest) of + true -> + {skip,"Repeated test stopped by force_stop option"}; _ -> - ct_util:update_testdata(curr_tc, - fun(undefined) -> - [{Suite,Func}]; - (Running) -> - [{Suite,Func}|Running] - end, [create]), - case ct_util:read_suite_data({seq,Suite,Func}) of - undefined -> - init_tc1(Mod,Suite,Func,Config); - Seq when is_atom(Seq) -> - case ct_util:read_suite_data({seq,Suite,Seq}) of - [Func|TCs] -> % this is the 1st case in Seq - %% make sure no cases in this seq are - %% marked as failed from an earlier execution - %% in the same suite - lists:foreach( - fun(TC) -> - ct_util:save_suite_data({seq,Suite,TC}, - Seq) - end, TCs); - _ -> - ok - end, - init_tc1(Mod,Suite,Func,Config); - {failed,Seq,BadFunc} -> - {skip,{sequence_failed,Seq,BadFunc}} + case ct_util:get_testdata(curr_tc) of + {Suite,{suite0_failed,{require,Reason}}} -> + {skip,{require_failed_in_suite0,Reason}}; + {Suite,{suite0_failed,_}=Failure} -> + {skip,Failure}; + _ -> + ct_util:update_testdata(curr_tc, + fun(undefined) -> + [{Suite,Func}]; + (Running) -> + [{Suite,Func}|Running] + end, [create]), + case ct_util:read_suite_data({seq,Suite,Func}) of + undefined -> + init_tc1(Mod,Suite,Func,Config); + Seq when is_atom(Seq) -> + case ct_util:read_suite_data({seq,Suite,Seq}) of + [Func|TCs] -> % this is the 1st case in Seq + %% make sure no cases in this seq are + %% marked as failed from an earlier execution + %% in the same suite + lists:foreach( + fun(TC) -> + ct_util:save_suite_data( + {seq,Suite,TC}, + Seq) + end, TCs); + _ -> + ok + end, + init_tc1(Mod,Suite,Func,Config); + {failed,Seq,BadFunc} -> + {skip,{sequence_failed,Seq,BadFunc}} + end end end. diff --git a/lib/common_test/src/ct_repeat.erl b/lib/common_test/src/ct_repeat.erl index a47309c6ee..f4d9949776 100644 --- a/lib/common_test/src/ct_repeat.erl +++ b/lib/common_test/src/ct_repeat.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-2013. 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 @@ -23,7 +23,7 @@ %%% start flags (or equivalent ct:run_test/1 options) are supported: %%% -until <StopTime>, StopTime = YYMoMoDDHHMMSS | HHMMSS %%% -duration <DurTime>, DurTime = HHMMSS -%%% -force_stop +%%% -force_stop [skip_rest] %%% -repeat <N>, N = integer()</p> -module(ct_repeat). @@ -62,12 +62,15 @@ loop_test(If,Args) when is_list(Args) -> io:format("\nCommon Test: " "Will repeat tests for ~s.\n\n",[ts(Secs)]), TPid = - case lists:keymember(force_stop,1,Args) of - true -> + case proplists:get_value(force_stop,Args) of + False when False==false; False==undefined -> + undefined; + ForceStop -> CtrlPid = self(), - spawn(fun() -> stop_after(CtrlPid,Secs) end); - false -> - undefined + spawn( + fun() -> + stop_after(CtrlPid,Secs,ForceStop) + end) end, Args1 = [{loop_info,[{stop_time,Secs,StopTime,1}]} | Args], loop(If,stop_time,0,Secs,StopTime,Args1,TPid,[]) @@ -212,7 +215,7 @@ get_stop_time(until,[Y1,Y2,Mo1,Mo2,D1,D2,H1,H2,Mi1,Mi2,S1,S2]) -> list_to_integer([S1,S2])}, calendar:datetime_to_gregorian_seconds({Date,Time}); -get_stop_time(until,Time) -> +get_stop_time(until,Time=[_,_,_,_,_,_]) -> get_stop_time(until,"000000"++Time); get_stop_time(duration,[H1,H2,Mi1,Mi2,S1,S2]) -> @@ -227,10 +230,17 @@ cancel(Pid) -> %% After Secs, abort will make the test_server finish the current %% job, then empty the job queue and stop. -stop_after(_CtrlPid,Secs) -> +stop_after(_CtrlPid,Secs,ForceStop) -> timer:sleep(Secs*1000), + case ForceStop of + SkipRest when SkipRest==skip_rest; SkipRest==["skip_rest"] -> + ct_util:set_testdata({skip_rest,true}); + _ -> + ok + end, test_server_ctrl:abort(). + %% Callback from ct_run to print loop info to system log. log_loop_info(Args) -> case lists:keysearch(loop_info,1,Args) of @@ -259,11 +269,11 @@ log_loop_info(Args) -> io_lib:format("Test time remaining: ~w secs (~w%)\n", [Secs,trunc((Secs/Secs0)*100)]), LogStr4 = - case lists:keymember(force_stop,1,Args) of - true -> - io_lib:format("force_stop is enabled",[]); - _ -> - "" + case proplists:get_value(force_stop,Args) of + False when False==false; False==undefined -> + ""; + ForceStop -> + io_lib:format("force_stop is set to: ~w",[ForceStop]) end, ct_logs:log("Test loop info",LogStr1++LogStr2++LogStr3++LogStr4,[]) end. diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl index 49f00429ae..57cfab532e 100644 --- a/lib/common_test/src/ct_run.erl +++ b/lib/common_test/src/ct_run.erl @@ -771,9 +771,9 @@ script_usage() -> "\n\t[-scale_timetraps]" "\n\t[-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]" "\n\t[-basic_html]" - "\n\t[-repeat N [-force_stop]] |" - "\n\t[-duration HHMMSS [-force_stop]] |" - "\n\t[-until [YYMoMoDD]HHMMSS [-force_stop]]\n\n"), + "\n\t[-repeat N] |" + "\n\t[-duration HHMMSS [-force_stop [skip_rest]]] |" + "\n\t[-until [YYMoMoDD]HHMMSS [-force_stop [skip_rest]]]\n\n"), io:format("Run tests using test specification:\n\n" "\tct_run -spec TestSpec1 TestSpec2 .. TestSpecN" "\n\t[-config ConfigFile1 ConfigFile2 .. ConfigFileN]" @@ -795,9 +795,9 @@ script_usage() -> "\n\t[-scale_timetraps]" "\n\t[-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]" "\n\t[-basic_html]" - "\n\t[-repeat N [-force_stop]] |" - "\n\t[-duration HHMMSS [-force_stop]] |" - "\n\t[-until [YYMoMoDD]HHMMSS [-force_stop]]\n\n"), + "\n\t[-repeat N] |" + "\n\t[-duration HHMMSS [-force_stop [skip_rest]]] |" + "\n\t[-until [YYMoMoDD]HHMMSS [-force_stop [skip_rest]]]\n\n"), io:format("Refresh the HTML index files:\n\n" "\tct_run -refresh_logs [LogDir]" "[-logdir LogDir] " @@ -2933,6 +2933,8 @@ opts2args(EnvStartOpts) -> []; ({create_priv_dir,PD}) when is_atom(PD) -> [{create_priv_dir,[atom_to_list(PD)]}]; + ({force_stop,skip_rest}) -> + [{force_stop,["skip_rest"]}]; ({force_stop,true}) -> [{force_stop,[]}]; ({force_stop,false}) -> diff --git a/lib/common_test/test/Makefile b/lib/common_test/test/Makefile index a9ebd8f1d3..94569fa87f 100644 --- a/lib/common_test/test/Makefile +++ b/lib/common_test/test/Makefile @@ -38,6 +38,7 @@ MODULES= \ ct_groups_spec_SUITE \ ct_sequence_1_SUITE \ ct_repeat_1_SUITE \ + ct_repeat_testrun_SUITE \ ct_testspec_1_SUITE \ ct_testspec_2_SUITE \ ct_testspec_3_SUITE \ diff --git a/lib/common_test/test/ct_repeat_testrun_SUITE.erl b/lib/common_test/test/ct_repeat_testrun_SUITE.erl new file mode 100644 index 0000000000..7ec384c932 --- /dev/null +++ b/lib/common_test/test/ct_repeat_testrun_SUITE.erl @@ -0,0 +1,378 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2013. 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% +%% + +%%%------------------------------------------------------------------- +%%% File: ct_repeat_test_SUITE +%%% +%%% Description: +%%% Test different options for repeating test runs: +%%% -repeat N +%%% -duration T [-force_stop [skip_rest]] +%%% -until T [-force_stop [skip_rest]] +%%% +%%%------------------------------------------------------------------- +-module(ct_repeat_testrun_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("common_test/include/ct_event.hrl"). + +-define(eh, ct_test_support_eh). +-define(skip_reason, "Repeated test stopped by force_stop option"). +-define(skipped, {skipped, ?skip_reason}). + + +%% Timers used in this test. +%% Each test suite consists of +%% +%% [tc1,tc2,{group,g,[tc1,tc2]},tc2] +%% +%% In r1_SUITE tc1 has a sleep of 10 sec - all other test cases just +%% return ok. +%% +%% => One complete test run of two suites r1_SUITE + r2_SUITE is at +%% least 20 seconds (10 sec for each r1_SUITE:tc1) +%% +-define(t1,30). % time shall expire during second run of r1_SUITE +-define(t2,6). % time shall expire during first run of tc1 +-define(t3,16). % time shall expire during second run of tc1 + + +%%-------------------------------------------------------------------- +%% TEST SERVER CALLBACK FUNCTIONS +%%-------------------------------------------------------------------- + +%%-------------------------------------------------------------------- +%% Description: Since Common Test starts another Test Server +%% instance, the tests need to be performed on a separate node (or +%% there will be clashes with logging processes etc). +%%-------------------------------------------------------------------- +init_per_suite(Config0) -> + Config = ct_test_support:init_per_suite(Config0), + DataDir = ?config(data_dir, Config), + Suite1 = filename:join([DataDir,"a_test","r1_SUITE"]), + Suite2 = filename:join([DataDir,"b_test","r2_SUITE"]), + Opts0 = ct_test_support:get_opts(Config), + Opts1 = Opts0 ++ [{suite,Suite1},{testcase,tc2},{label,timing1}], + Opts2 = Opts0 ++ [{suite,Suite2},{testcase,tc2},{label,timing2}], + + %% Make sure both suites are compiled + {1,0,{0,0}} = ct_test_support:run(ct,run_test,[Opts1],Config), + {1,0,{0,0}} = ct_test_support:run(ct,run_test,[Opts2],Config), + + %% Time the shortest testcase to use for offset + {T0,{1,0,{0,0}}} = timer:tc(ct_test_support,run,[ct,run_test,[Opts1],Config]), + + %% -2 is to ensure we hit inside the target test case and not after +% T = round(T0/1000000)-2, + T=0, + [{offset,T}|Config]. + +end_per_suite(Config) -> + ct_test_support:end_per_suite(Config). + +init_per_testcase(TestCase, Config) -> + ct_test_support:init_per_testcase(TestCase, Config). + +end_per_testcase(TestCase, Config) -> + ct_test_support:end_per_testcase(TestCase, Config). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [ + repeat_n, + duration, + duration_force_stop, + duration_force_stop_skip_rest, + duration_force_stop_skip_rest_group, + until, + until_force_stop, + until_force_stop_skip_rest, + until_force_stop_skip_rest_group + ]. + +%%-------------------------------------------------------------------- +%% TEST CASES +%%-------------------------------------------------------------------- + +%%%----------------------------------------------------------------- +%%% +repeat_n(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + Dirs = filelib:wildcard(filename:join(DataDir,"*")), + {Opts,ERPid} = setup([{dir,Dirs}, + {label,repeat_n}, + {repeat,2}], + Config), + ok = execute(repeat_n, Opts, ERPid, Config). + +duration(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + Dirs = filelib:wildcard(filename:join(DataDir,"*")), + {Opts,ERPid} = setup([{dir,Dirs}, + {label,duration}, + {duration,duration_str(?t1,2,Config)}], + Config), + ok = execute(duration, Opts, ERPid, Config). + +duration_force_stop(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + Dirs = filelib:wildcard(filename:join(DataDir,"*")), + {Opts,ERPid} = setup([{dir,Dirs}, + {label,duration_force_stop}, + {duration,duration_str(?t1,2,Config)}, + {force_stop,true}], + Config), + ok = execute(duration_force_stop, Opts, ERPid, Config). + +duration_force_stop_skip_rest(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + Dirs = filelib:wildcard(filename:join(DataDir,"*")), + {Opts,ERPid} = setup([{dir,Dirs}, + {label,duration_force_stop_skip_rest}, + {duration,duration_str(?t2,1,Config)}, + {force_stop,skip_rest}], + Config), + ok = execute(duration_force_stop_skip_rest, Opts, ERPid, Config). + +duration_force_stop_skip_rest_group(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + Dirs = filelib:wildcard(filename:join(DataDir,"*")), + {Opts,ERPid} = setup([{dir,Dirs}, + {label,duration_force_stop_skip_rest_group}, + {duration,duration_str(?t3,1,Config)}, + {force_stop,skip_rest}], + Config), + ok = execute(duration_force_stop_skip_rest_group, Opts, ERPid, Config). + +until(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + Dirs = filelib:wildcard(filename:join(DataDir,"*")), + {Opts,ERPid} = setup([{dir,Dirs}, + {label,until}], + Config), + ExecuteFun = + fun() -> + [_,_] = ct_test_support:run_ct_run_test( + Opts++[{until,until_str(?t1,2,Config)}],Config), + 0 = ct_test_support:run_ct_script_start( + Opts++[{until,until_str(?t1,2,Config)}],Config) + end, + ok = execute(ExecuteFun, until, Opts, ERPid, Config). + +until_force_stop(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + Dirs = filelib:wildcard(filename:join(DataDir,"*")), + {Opts,ERPid} = setup([{dir,Dirs}, + {label,until_force_stop}, + {force_stop,true}], + Config), + ExecuteFun = + fun() -> + [_,_] = ct_test_support:run_ct_run_test( + Opts++[{until,until_str(?t1,2,Config)}],Config), + 0 = ct_test_support:run_ct_script_start( + Opts++[{until,until_str(?t1,2,Config)}],Config) + end, + ok = execute(ExecuteFun, until_force_stop, Opts, ERPid, Config). + +until_force_stop_skip_rest(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + Dirs = filelib:wildcard(filename:join(DataDir,"*")), + {Opts,ERPid} = setup([{dir,Dirs}, + {label,until_force_stop_skip_rest}, + {force_stop,skip_rest}], + Config), + ExecuteFun = + fun() -> + [_] = ct_test_support:run_ct_run_test( + Opts++[{until,until_str(?t2,1,Config)}],Config), + 1 = ct_test_support:run_ct_script_start( + Opts++[{until,until_str(?t2,1,Config)}],Config) + end, + ok = execute(ExecuteFun, until_force_stop_skip_rest, + Opts, ERPid, Config). + +until_force_stop_skip_rest_group(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + Dirs = filelib:wildcard(filename:join(DataDir,"*")), + {Opts,ERPid} = setup([{dir,Dirs}, + {label,until_force_stop_skip_rest_group}, + {force_stop,skip_rest}], + Config), + ExecuteFun = + fun() -> + [_] = ct_test_support:run_ct_run_test( + Opts++[{until,until_str(?t3,1,Config)}],Config), + 0 = ct_test_support:run_ct_script_start( + Opts++[{until,until_str(?t3,1,Config)}],Config) + end, + ok = execute(ExecuteFun, + until_force_stop_skip_rest_group, + Opts, ERPid, Config). + + +%%%----------------------------------------------------------------- +%%% HELP FUNCTIONS +%%%----------------------------------------------------------------- + +setup(Test, Config) -> + Opts0 = ct_test_support:get_opts(Config), + Level = ?config(trace_level, Config), + EvHArgs = [{cbm,ct_test_support},{trace_level,Level}], + Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}}|Test], + ERPid = ct_test_support:start_event_receiver(Config), + {Opts,ERPid}. + +%% Execute test, first with ct:run_test, then with ct:script_start +execute(Name, Opts, ERPid, Config) -> + ExecuteFun = fun() -> ok = ct_test_support:run(Opts, Config) end, + execute(ExecuteFun, Name, Opts, ERPid, Config). + +execute(ExecuteFun, Name, Opts, ERPid, Config) -> + ExecuteFun(), + Events = ct_test_support:get_events(ERPid, Config), + + ct_test_support:log_events(Name, + reformat(Events, ?eh), + ?config(priv_dir, Config), + Opts), + + TestEvents = events_to_check(Name), + ct_test_support:verify_events(TestEvents, Events, Config). + +reformat(Events, EH) -> + ct_test_support:reformat(Events, EH). + +%% N is the expected number of repeats +until_str(Secs0,N,Config) -> + Offset = ?config(offset,Config), + Secs = Secs0 + N*Offset, + Now = calendar:datetime_to_gregorian_seconds(calendar:local_time()), + {{Y,Mo,D},{H,M,S}} = calendar:gregorian_seconds_to_datetime(Now+Secs), + lists:flatten(io_lib:format("~2..0w~2..0w~2..0w~2..0w~2..0w~2..0w", + [Y rem 100, Mo, D, H, M, S])). + +%% N is the expected number of repeats +duration_str(Secs0,N,Config) -> + Offset = ?config(offset,Config), + Secs = Secs0 + N*Offset, + "0000" ++ lists:flatten(io_lib:format("~2..0w",[Secs])). + +%%%----------------------------------------------------------------- +%%% TEST EVENTS +%%%----------------------------------------------------------------- +%% 2 tests (ct:run_test + script_start) is default +events_to_check(C) when C==repeat_n; C==duration; C==until -> + dupl(4, start_logging() ++ all_succ() ++ stop_logging()); +events_to_check(C) when C==duration_force_stop; C==until_force_stop -> + dupl(2, start_logging() ++ + all_succ() ++ + stop_logging() ++ + start_logging() ++ + all_succ(r1_SUITE) ++ + stop_logging()); +events_to_check(C) when C==duration_force_stop_skip_rest; + C==until_force_stop_skip_rest -> + dupl(2, start_logging() ++ skip_first_tc1(r1_SUITE) ++ stop_logging()); +events_to_check(C) when C==duration_force_stop_skip_rest_group; + C==until_force_stop_skip_rest_group -> + dupl(2, start_logging() ++ skip_tc1_in_group(r1_SUITE) ++ stop_logging()). + +dupl(N,List) -> + lists:flatten(lists:duplicate(N,List)). + +start_logging() -> + [{?eh,start_logging,{'DEF','RUNDIR'}}]. +stop_logging() -> + [{?eh,stop_logging,[]}]. + + +all_succ() -> + all_succ(r1_SUITE) ++ all_succ(r2_SUITE). + +all_succ(Suite) -> + [{?eh,tc_start,{Suite,init_per_suite}}, + {?eh,tc_done,{Suite,init_per_suite,ok}}, + {?eh,tc_start,{Suite,tc1}}, + {?eh,tc_done,{Suite,tc1,ok}}, + {?eh,test_stats,{'_',0,{0,0}}}, + {?eh,tc_start,{Suite,tc2}}, + {?eh,tc_done,{Suite,tc2,ok}}, + {?eh,test_stats,{'_',0,{0,0}}}, + [{?eh,tc_start,{Suite,{init_per_group,g,[]}}}, + {?eh,tc_done,{Suite,{init_per_group,g,[]},ok}}, + {?eh,tc_start,{Suite,tc1}}, + {?eh,tc_done,{Suite,tc1,ok}}, + {?eh,test_stats,{'_',0,{0,0}}}, + {?eh,tc_start,{Suite,tc2}}, + {?eh,tc_done,{Suite,tc2,ok}}, + {?eh,test_stats,{'_',0,{0,0}}}, + {?eh,tc_start,{Suite,{end_per_group,g,[]}}}, + {?eh,tc_done,{Suite,{end_per_group,g,[]},ok}}], + {?eh,tc_start,{Suite,tc2}}, + {?eh,tc_done,{Suite,tc2,ok}}, + {?eh,test_stats,{'_',0,{0,0}}}, + {?eh,tc_start,{Suite,end_per_suite}}, + {?eh,tc_done,{Suite,end_per_suite,ok}}]. + +skip_first_tc1(Suite) -> + [{?eh,tc_start,{Suite,init_per_suite}}, + {?eh,tc_done,{Suite,init_per_suite,ok}}, + {?eh,tc_start,{Suite,tc1}}, + {?eh,tc_done,{Suite,tc1,ok}}, + {?eh,test_stats,{'_',0,{0,0}}}, + {?eh,tc_done,{Suite,tc2,?skipped}}, + {?eh,test_stats,{'_',0,{1,0}}}, + {?eh,tc_done,{Suite,{init_per_group,g,[]},?skipped}}, + {?eh,tc_auto_skip,{Suite,tc1,?skip_reason}}, + {?eh,test_stats,{'_',0,{1,1}}}, + {?eh,tc_auto_skip,{Suite,tc2,?skip_reason}}, + {?eh,test_stats,{'_',0,{1,2}}}, + {?eh,tc_auto_skip,{Suite,end_per_group,?skip_reason}}, + {?eh,tc_done,{Suite,tc2,?skipped}}, + {?eh,test_stats,{'_',0,{2,2}}}, + {?eh,tc_start,{Suite,end_per_suite}}, + {?eh,tc_done,{Suite,end_per_suite,ok}}]. + + +skip_tc1_in_group(Suite) -> + [{?eh,tc_start,{Suite,init_per_suite}}, + {?eh,tc_done,{Suite,init_per_suite,ok}}, + {?eh,tc_start,{Suite,tc1}}, + {?eh,tc_done,{Suite,tc1,ok}}, + {?eh,test_stats,{'_',0,{0,0}}}, + {?eh,tc_start,{Suite,tc2}}, + {?eh,tc_done,{Suite,tc2,ok}}, + {?eh,test_stats,{'_',0,{0,0}}}, + [{?eh,tc_start,{Suite,{init_per_group,g,[]}}}, + {?eh,tc_done,{Suite,{init_per_group,g,[]},ok}}, + {?eh,tc_start,{Suite,tc1}}, + {?eh,tc_done,{Suite,tc1,ok}}, + {?eh,test_stats,{'_',0,{0,0}}}, + {?eh,tc_done,{Suite,tc2,?skipped}}, + {?eh,test_stats,{'_',0,{1,0}}}, + {?eh,tc_start,{Suite,{end_per_group,g,[]}}}, + {?eh,tc_done,{Suite,{end_per_group,g,[]},ok}}], + {?eh,tc_done,{Suite,tc2,?skipped}}, + {?eh,test_stats,{'_',0,{2,0}}}, + {?eh,tc_start,{Suite,end_per_suite}}, + {?eh,tc_done,{Suite,end_per_suite,ok}}]. diff --git a/lib/common_test/test/ct_repeat_testrun_SUITE_data/a_test/r1_SUITE.erl b/lib/common_test/test/ct_repeat_testrun_SUITE_data/a_test/r1_SUITE.erl new file mode 100644 index 0000000000..3fd5943691 --- /dev/null +++ b/lib/common_test/test/ct_repeat_testrun_SUITE_data/a_test/r1_SUITE.erl @@ -0,0 +1,75 @@ +%%-------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2013. 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% +%% +%%---------------------------------------------------------------------- +%% File: r1_SUITE.erl +%% +%% Description: +%% +%% +%% @author Support +%% @doc +%% @end +%%---------------------------------------------------------------------- +%%---------------------------------------------------------------------- +-module(r1_SUITE). +-include_lib("common_test/include/ct.hrl"). + +-compile(export_all). + +%% Default timetrap timeout (set in init_per_testcase). +-define(default_timeout, ?t:seconds(30)). + +all() -> + testcases() ++ [{group,g}, tc2]. + +groups() -> + [{g,testcases()}]. + +testcases() -> + [tc1,tc2]. + +init_per_suite(Config) -> + Config. + +end_per_suite(Config) -> + Config. + +init_per_group(_, Config) -> + Config. + +end_per_group(_Group, Config) -> + Config. + +init_per_testcase(_Case, Config) -> + Dog = test_server:timetrap(?default_timeout), + [{watchdog, Dog}|Config]. + +end_per_testcase(_Case, Config) -> + Dog=?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + ok. + +%%%----------------------------------------------------------------- +%%% Test cases +tc1(_Config) -> + timer:sleep(10000), + ok. + +tc2(_Config) -> + ok. diff --git a/lib/common_test/test/ct_repeat_testrun_SUITE_data/b_test/r2_SUITE.erl b/lib/common_test/test/ct_repeat_testrun_SUITE_data/b_test/r2_SUITE.erl new file mode 100644 index 0000000000..dc9abc2863 --- /dev/null +++ b/lib/common_test/test/ct_repeat_testrun_SUITE_data/b_test/r2_SUITE.erl @@ -0,0 +1,75 @@ +%%-------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2013. 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% +%% +%%---------------------------------------------------------------------- +%% File: r2_SUITE.erl +%% +%% Description: +%% +%% +%% @author Support +%% @doc +%% @end +%%---------------------------------------------------------------------- +%%---------------------------------------------------------------------- +-module(r2_SUITE). +-include_lib("common_test/include/ct.hrl"). + +-compile(export_all). + +%% Default timetrap timeout (set in init_per_testcase). +-define(default_timeout, ?t:seconds(30)). + +all() -> + testcases() ++ [{group,g}, tc2]. + +groups() -> + [{g,testcases()}]. + +testcases() -> + [tc1,tc2]. + +init_per_suite(Config) -> + Config. + +end_per_suite(Config) -> + Config. + +init_per_group(_, Config) -> + Config. + +end_per_group(_Group, Config) -> + Config. + +init_per_testcase(_Case, Config) -> + Dog = test_server:timetrap(?default_timeout), + [{watchdog, Dog}|Config]. + +end_per_testcase(_Case, Config) -> + Dog=?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + ok. + +%%%----------------------------------------------------------------- +%%% Test cases +tc1(_Config) -> + %% timer:sleep(3000), + ok. + +tc2(_Config) -> + ok. diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl index 5e109e98e9..70dd087358 100644 --- a/lib/common_test/test/ct_test_support.erl +++ b/lib/common_test/test/ct_test_support.erl @@ -29,7 +29,8 @@ -export([init_per_suite/1, init_per_suite/2, end_per_suite/1, init_per_testcase/2, end_per_testcase/2, write_testspec/2, write_testspec/3, - run/2, run/3, run/4, get_opts/1, wait_for_ct_stop/1]). + run/2, run/3, run/4, run_ct_run_test/2, run_ct_script_start/2, + get_opts/1, wait_for_ct_stop/1]). -export([handle_event/2, start_event_receiver/1, get_events/2, verify_events/3, verify_events/4, reformat/2, log_events/4, @@ -224,9 +225,15 @@ get_opts(Config) -> %%%----------------------------------------------------------------- %%% run(Opts, Config) when is_list(Opts) -> + %% use ct interface + CtRunTestResult=run_ct_run_test(Opts,Config), + %% use run_test interface (simulated) + ExitStatus=run_ct_script_start(Opts,Config), + check_result(CtRunTestResult,ExitStatus,Opts). + +run_ct_run_test(Opts,Config) -> CTNode = proplists:get_value(ct_node, Config), Level = proplists:get_value(trace_level, Config), - %% use ct interface test_server:format(Level, "~n[RUN #1] Calling ct:run_test(~p) on ~p~n", [Opts, CTNode]), CtRunTestResult = rpc:call(CTNode, ct, run_test, [Opts]), @@ -242,7 +249,11 @@ run(Opts, Config) when is_list(Opts) -> timer:sleep(5000), undefined = rpc:call(CTNode, erlang, whereis, [ct_util_server]) end, - %% use run_test interface (simulated) + CtRunTestResult. + +run_ct_script_start(Opts, Config) -> + CTNode = proplists:get_value(ct_node, Config), + Level = proplists:get_value(trace_level, Config), Opts1 = [{halt_with,{?MODULE,ct_test_halt}} | Opts], test_server:format(Level, "Saving start opts on ~p: ~p~n", [CTNode, Opts1]), @@ -253,27 +264,38 @@ run(Opts, Config) when is_list(Opts) -> ExitStatus = rpc:call(CTNode, ct_run, script_start, []), test_server:format(Level, "[RUN #2] Got exit status value ~p~n", [ExitStatus]), - case {CtRunTestResult,ExitStatus} of - {{_Ok,Failed,{_UserSkipped,_AutoSkipped}},1} when Failed > 0 -> - ok; - {{_Ok,0,{_UserSkipped,AutoSkipped}},ExitStatus} when AutoSkipped > 0 -> - case proplists:get_value(exit_status, Opts1) of - ignore_config when ExitStatus == 1 -> - {error,{wrong_exit_status,ExitStatus}}; - _ -> - ok - end; - {{error,_}=Error,ExitStatus} -> - if ExitStatus /= 2 -> - {error,{wrong_exit_status,ExitStatus}}; - ExitStatus == 2 -> - Error - end; - {{_Ok,0,{_UserSkipped,_AutoSkipped}},0} -> - ok; - Unexpected -> - {error,{unexpected_return_value,Unexpected}} - end. + ExitStatus. + +check_result({_Ok,Failed,{_UserSkipped,_AutoSkipped}},1,_Opts) + when Failed > 0 -> + ok; +check_result({_Ok,0,{_UserSkipped,AutoSkipped}},ExitStatus,Opts) + when AutoSkipped > 0 -> + case proplists:get_value(exit_status, Opts) of + ignore_config when ExitStatus == 1 -> + {error,{wrong_exit_status,ExitStatus}}; + _ -> + ok + end; +check_result({error,_}=Error,2,_Opts) -> + Error; +check_result({error,_},ExitStatus,_Opts) -> + {error,{wrong_exit_status,ExitStatus}}; +check_result({_Ok,0,{_UserSkipped,_AutoSkipped}},0,_Opts) -> + ok; +check_result(CtRunTestResult,ExitStatus,Opts) + when is_list(CtRunTestResult) -> % repeated testruns + try check_result(sum_testruns(CtRunTestResult,0,0,0,0),ExitStatus,Opts) + catch _:_ -> + {error,{unexpected_return_value,{CtRunTestResult,ExitStatus}}} + end; +check_result(CtRunTestResult,ExitStatus,_Opts) -> + {error,{unexpected_return_value,{CtRunTestResult,ExitStatus}}}. + +sum_testruns([{O,F,{US,AS}}|T],Ok,Failed,UserSkipped,AutoSkipped) -> + sum_testruns(T,Ok+O,Failed+F,UserSkipped+US,AutoSkipped+AS); +sum_testruns([],Ok,Failed,UserSkipped,AutoSkipped) -> + {Ok,Failed,{UserSkipped,AutoSkipped}}. run(M, F, A, Config) -> run({M,F,A}, [], Config). diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl index 497af2b52c..745f1d5cf9 100644 --- a/lib/compiler/src/compile.erl +++ b/lib/compiler/src/compile.erl @@ -599,7 +599,8 @@ standard_passes() -> core_passes() -> %% Optimization and transforms of Core Erlang code. - [{delay, + [{iff,clint0,?pass(core_lint_module)}, + {delay, [{unless,no_copt, [{core_old_inliner,fun test_old_inliner/1,fun core_old_inliner/1}, {iff,doldinline,{listing,"oldinline"}}, diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index 01042cc56f..eea54b30a2 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -1187,9 +1187,9 @@ list_gen_pattern(P0, Line, St) -> bc_initial_size(E, Q, St0) -> try - {ElemSzExpr,ElemSzPre,St1} = bc_elem_size(E, St0), + {ElemSzExpr,ElemSzPre,EVs,St1} = bc_elem_size(E, St0), {V,St2} = new_var(St1), - {GenSzExpr,GenSzPre,St3} = bc_gen_size(Q, St2), + {GenSzExpr,GenSzPre,St3} = bc_gen_size(Q, EVs, St2), case ElemSzExpr of #c_literal{val=ElemSz} when ElemSz rem 8 =:= 0 -> NumBytesExpr = #c_literal{val=ElemSz div 8}, @@ -1214,11 +1214,13 @@ bc_initial_size(E, Q, St0) -> bc_elem_size({bin,_,El}, St0) -> case bc_elem_size_1(El, 0, []) of {Bits,[]} -> - {#c_literal{val=Bits},[],St0}; + {#c_literal{val=Bits},[],[],St0}; {Bits,Vars0} -> [{U,V0}|Pairs] = sort(Vars0), F = bc_elem_size_combine(Pairs, U, [V0], []), - bc_mul_pairs(F, #c_literal{val=Bits}, [], St0) + Vs = [V || {_,#c_var{name=V}} <- Vars0], + {E,Pre,St} = bc_mul_pairs(F, #c_literal{val=Bits}, [], St0), + {E,Pre,Vs,St} end. bc_elem_size_1([{bin_element,_,_,{integer,_,N},Flags}|Es], Bits, Vars) -> @@ -1260,11 +1262,11 @@ bc_add_list_1([H|T], Pre, E, St0) -> bc_add_list_1([], Pre, E, St) -> {E,reverse(Pre),St}. -bc_gen_size(Q, St) -> - bc_gen_size_1(Q, #c_literal{val=1}, [], St). +bc_gen_size(Q, EVs, St) -> + bc_gen_size_1(Q, EVs, #c_literal{val=1}, [], St). -bc_gen_size_1([{generate,L,El,Gen}|Qs], E0, Pre0, St0) -> - bc_verify_non_filtering(El), +bc_gen_size_1([{generate,L,El,Gen}|Qs], EVs, E0, Pre0, St0) -> + bc_verify_non_filtering(El, EVs), case Gen of {var,_,ListVar} -> Lanno = lineno_anno(L, St0), @@ -1275,16 +1277,16 @@ bc_gen_size_1([{generate,L,El,Gen}|Qs], E0, Pre0, St0) -> name=#c_literal{val=length}, args=[#c_var{name=ListVar}]}}, {E,Pre,St} = bc_gen_size_mul(E0, LenVar, [Set|Pre0], St1), - bc_gen_size_1(Qs, E, Pre, St); + bc_gen_size_1(Qs, EVs, E, Pre, St); _ -> %% The only expressions we handle is literal lists. Len = bc_list_length(Gen, 0), {E,Pre,St} = bc_gen_size_mul(E0, #c_literal{val=Len}, Pre0, St0), - bc_gen_size_1(Qs, E, Pre, St) + bc_gen_size_1(Qs, EVs, E, Pre, St) end; -bc_gen_size_1([{b_generate,_,El,Gen}|Qs], E0, Pre0, St0) -> - bc_verify_non_filtering(El), - {MatchSzExpr,Pre1,St1} = bc_elem_size(El, St0), +bc_gen_size_1([{b_generate,_,El,Gen}|Qs], EVs, E0, Pre0, St0) -> + bc_verify_non_filtering(El, EVs), + {MatchSzExpr,Pre1,_,St1} = bc_elem_size(El, St0), Pre2 = reverse(Pre1, Pre0), {ResVar,St2} = new_var(St1), {BitSizeExpr,Pre3,St3} = bc_gen_bit_size(Gen, Pre2, St2), @@ -1292,10 +1294,10 @@ bc_gen_size_1([{b_generate,_,El,Gen}|Qs], E0, Pre0, St0) -> MatchSzExpr)}, Pre4 = [Div|Pre3], {E,Pre,St} = bc_gen_size_mul(E0, ResVar, Pre4, St3), - bc_gen_size_1(Qs, E, Pre, St); -bc_gen_size_1([], E, Pre, St) -> + bc_gen_size_1(Qs, EVs, E, Pre, St); +bc_gen_size_1([], _, E, Pre, St) -> {E,reverse(Pre),St}; -bc_gen_size_1(_, _, _, _) -> +bc_gen_size_1(_, _, _, _, _) -> throw(impossible). bc_gen_bit_size({var,L,V}, Pre0, St0) -> @@ -1312,13 +1314,20 @@ bc_gen_bit_size({bin,_,_}=Bin, Pre, St) -> bc_gen_bit_size(_, _, _) -> throw(impossible). -bc_verify_non_filtering({bin,_,Els}) -> - foreach(fun({bin_element,_,{var,_,_},_,_}) -> ok; +bc_verify_non_filtering({bin,_,Els}, EVs) -> + foreach(fun({bin_element,_,{var,_,V},_,_}) -> + case member(V, EVs) of + true -> throw(impossible); + false -> ok + end; (_) -> throw(impossible) end, Els); -bc_verify_non_filtering({var,_,_}) -> - ok; -bc_verify_non_filtering(_) -> +bc_verify_non_filtering({var,_,V}, EVs) -> + case member(V, EVs) of + true -> throw(impossible); + false -> ok + end; +bc_verify_non_filtering(_, _) -> throw(impossible). bc_list_length({string,_,Str}, Len) -> diff --git a/lib/compiler/test/bs_bincomp_SUITE.erl b/lib/compiler/test/bs_bincomp_SUITE.erl index d39e340429..451a9b1e3b 100644 --- a/lib/compiler/test/bs_bincomp_SUITE.erl +++ b/lib/compiler/test/bs_bincomp_SUITE.erl @@ -282,6 +282,9 @@ sizes(Config) when is_list(Config) -> ?line <<1,2,3,0>> = Fun13(7), ?line <<1,2,3,0,0>> = Fun13(8), + <<0:3>> = cs_default(<< <<0:S>> || S <- [0,1,2] >>), + <<0:3>> = cs_default(<< <<0:S>> || <<S>> <= <<0,1,2>> >>), + ?line {'EXIT',_} = (catch << <<C:4>> || <<C:8>> <= {1,2,3} >>), ?line cs_end(), diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c index e77e5fb8f0..fac77308f6 100644 --- a/lib/crypto/c_src/crypto.c +++ b/lib/crypto/c_src/crypto.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2010-2012. All Rights Reserved. + * Copyright Ericsson AB 2010-2013. 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 @@ -136,6 +136,7 @@ static void unload(ErlNifEnv* env, void* priv_data); /* The NIFs: */ static ERL_NIF_TERM info_lib(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM md5(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM md5_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM md5_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); @@ -207,6 +208,9 @@ static ERL_NIF_TERM dh_generate_parameters_nif(ErlNifEnv* env, int argc, const E static ERL_NIF_TERM dh_check(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM dh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM srp_value_B_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM srp_client_secret_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM srp_server_secret_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM bf_cfb64_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM bf_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM bf_ecb_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); @@ -215,6 +219,7 @@ static ERL_NIF_TERM blowfish_ofb64_encrypt(ErlNifEnv* env, int argc, const ERL_N /* helpers */ +static void init_algorithms_types(void); static void init_digest_types(ErlNifEnv* env); static void hmac_md5(unsigned char *key, int klen, unsigned char *dbuf, int dlen, @@ -247,6 +252,7 @@ static int library_refc = 0; /* number of users of this dynamic library */ static ErlNifFunc nif_funcs[] = { {"info_lib", 0, info_lib}, + {"algorithms", 0, algorithms}, {"md5", 1, md5}, {"md5_init", 0, md5_init}, {"md5_update", 2, md5_update}, @@ -304,7 +310,7 @@ static ErlNifFunc nif_funcs[] = { {"rand_bytes", 3, rand_bytes_3}, {"strong_rand_mpint_nif", 3, strong_rand_mpint_nif}, {"rand_uniform_nif", 2, rand_uniform_nif}, - {"mod_exp_nif", 3, mod_exp_nif}, + {"mod_exp_nif", 4, mod_exp_nif}, {"dss_verify", 4, dss_verify}, {"rsa_verify_nif", 4, rsa_verify_nif}, {"aes_cbc_crypt", 4, aes_cbc_crypt}, @@ -321,6 +327,9 @@ static ErlNifFunc nif_funcs[] = { {"dh_check", 1, dh_check}, {"dh_generate_key_nif", 2, dh_generate_key_nif}, {"dh_compute_key_nif", 3, dh_compute_key_nif}, + {"srp_value_B_nif", 5, srp_value_B_nif}, + {"srp_client_secret_nif", 7, srp_client_secret_nif}, + {"srp_server_secret_nif", 5, srp_server_secret_nif}, {"bf_cfb64_crypt", 4, bf_cfb64_crypt}, {"bf_cbc_crypt", 4, bf_cbc_crypt}, {"bf_ecb_crypt", 3, bf_ecb_crypt}, @@ -458,6 +467,7 @@ static int init(ErlNifEnv* env, ERL_NIF_TERM load_info) atom_digest = enif_make_atom(env,"digest"); init_digest_types(env); + init_algorithms_types(); #ifdef HAVE_DYNAMIC_CRYPTO_LIB { @@ -538,6 +548,35 @@ static void unload(ErlNifEnv* env, void* priv_data) --library_refc; } +static int algos_cnt; +static ERL_NIF_TERM algos[7]; /* increase when extending the list */ + +static void init_algorithms_types(void) +{ + algos_cnt = 0; + + algos[algos_cnt++] = atom_md5; + algos[algos_cnt++] = atom_sha; + algos[algos_cnt++] = atom_ripemd160; +#ifdef HAVE_SHA224 + algos[algos_cnt++] = atom_sha224; +#endif +#ifdef HAVE_SHA256 + algos[algos_cnt++] = atom_sha256; +#endif +#ifdef HAVE_SHA384 + algos[algos_cnt++] = atom_sha384; +#endif +#ifdef HAVE_SHA512 + algos[algos_cnt++] = atom_sha512; +#endif +} + +static ERL_NIF_TERM algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + return enif_make_list_from_array(env, algos, algos_cnt); +} + static ERL_NIF_TERM info_lib(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { /* [{<<"OpenSSL">>,9470143,<<"OpenSSL 0.9.8k 25 Mar 2009">>}] */ @@ -1515,6 +1554,17 @@ static int get_bn_from_mpint(ErlNifEnv* env, ERL_NIF_TERM term, BIGNUM** bnp) return 1; } +static int get_bn_from_bin(ErlNifEnv* env, ERL_NIF_TERM term, BIGNUM** bnp) +{ + ErlNifBinary bin; + if (!enif_inspect_binary(env,term,&bin)) { + return 0; + } + ERL_VALGRIND_ASSERT_MEM_DEFINED(bin.data, bin.size); + *bnp = BN_bin2bn(bin.data, bin.size, NULL); + return 1; +} + static ERL_NIF_TERM rand_uniform_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Lo,Hi) */ BIGNUM *bn_from = NULL, *bn_to, *bn_rand; @@ -1543,16 +1593,19 @@ static ERL_NIF_TERM rand_uniform_nif(ErlNifEnv* env, int argc, const ERL_NIF_TER } static ERL_NIF_TERM mod_exp_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Base,Exponent,Modulo) */ +{/* (Base,Exponent,Modulo,bin_hdr) */ BIGNUM *bn_base=NULL, *bn_exponent=NULL, *bn_modulo, *bn_result; BN_CTX *bn_ctx; unsigned char* ptr; unsigned dlen; + unsigned bin_hdr; /* return type: 0=plain binary, 4: mpint */ + unsigned extra_byte; ERL_NIF_TERM ret; - if (!get_bn_from_mpint(env, argv[0], &bn_base) - || !get_bn_from_mpint(env, argv[1], &bn_exponent) - || !get_bn_from_mpint(env, argv[2], &bn_modulo)) { + if (!get_bn_from_bin(env, argv[0], &bn_base) + || !get_bn_from_bin(env, argv[1], &bn_exponent) + || !get_bn_from_bin(env, argv[2], &bn_modulo) + || !enif_get_uint(env,argv[3],&bin_hdr) || (bin_hdr & ~4)) { if (bn_base) BN_free(bn_base); if (bn_exponent) BN_free(bn_exponent); @@ -1562,9 +1615,14 @@ static ERL_NIF_TERM mod_exp_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg bn_ctx = BN_CTX_new(); BN_mod_exp(bn_result, bn_base, bn_exponent, bn_modulo, bn_ctx); dlen = BN_num_bytes(bn_result); - ptr = enif_make_new_binary(env, dlen+4, &ret); - put_int32(ptr, dlen); - BN_bn2bin(bn_result, ptr+4); + extra_byte = bin_hdr && BN_is_bit_set(bn_result, dlen*8-1); + ptr = enif_make_new_binary(env, bin_hdr+extra_byte+dlen, &ret); + if (bin_hdr) { + put_int32(ptr, extra_byte+dlen); + ptr[4] = 0; /* extra zeroed byte to ensure a positive mpint */ + ptr += bin_hdr + extra_byte; + } + BN_bn2bin(bn_result, ptr); BN_free(bn_result); BN_CTX_free(bn_ctx); BN_free(bn_modulo); @@ -2344,6 +2402,205 @@ static ERL_NIF_TERM dh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_T return ret; } +static ERL_NIF_TERM srp_value_B_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Multiplier, Verifier, Generator, Exponent, Prime) */ + BIGNUM *bn_verifier = NULL; + BIGNUM *bn_exponent, *bn_generator, *bn_prime, *bn_multiplier, *bn_result; + BN_CTX *bn_ctx; + unsigned char* ptr; + unsigned dlen; + ERL_NIF_TERM ret; + + if (!get_bn_from_bin(env, argv[0], &bn_multiplier) + || !get_bn_from_bin(env, argv[1], &bn_verifier) + || !get_bn_from_bin(env, argv[2], &bn_generator) + || !get_bn_from_bin(env, argv[3], &bn_exponent) + || !get_bn_from_bin(env, argv[4], &bn_prime)) { + if (bn_multiplier) BN_free(bn_multiplier); + if (bn_verifier) BN_free(bn_verifier); + if (bn_verifier) BN_free(bn_generator); + if (bn_verifier) BN_free(bn_exponent); + if (bn_verifier) BN_free(bn_prime); + return enif_make_badarg(env); + } + + bn_result = BN_new(); + bn_ctx = BN_CTX_new(); + + /* B = k*v + g^b % N */ + + /* k * v */ + BN_mod_mul(bn_multiplier, bn_multiplier, bn_verifier, bn_prime, bn_ctx); + + /* g^b % N */ + BN_mod_exp(bn_result, bn_generator, bn_exponent, bn_prime, bn_ctx); + + /* k*v + g^b % N */ + BN_mod_add(bn_result, bn_result, bn_multiplier, bn_prime, bn_ctx); + + /* check that B % N != 0, reuse bn_multiplier */ + BN_nnmod(bn_multiplier, bn_result, bn_prime, bn_ctx); + if (BN_is_zero(bn_multiplier)) { + ret = atom_error; + } else { + dlen = BN_num_bytes(bn_result); + ptr = enif_make_new_binary(env, dlen, &ret); + BN_bn2bin(bn_result, ptr); + } + BN_free(bn_result); + BN_CTX_free(bn_ctx); + BN_free(bn_prime); + BN_free(bn_generator); + BN_free(bn_multiplier); + BN_free(bn_exponent); + BN_free(bn_verifier); + return ret; +} + +static ERL_NIF_TERM srp_client_secret_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (a, u, B, Multiplier, Prime, Exponent, Generator) */ +/* + <premaster secret> = (B - (k * g^x)) ^ (a + (u * x)) % N +*/ + BIGNUM *bn_exponent = NULL, *bn_a = NULL; + BIGNUM *bn_u, *bn_multiplier, *bn_exp2, *bn_base, + *bn_prime, *bn_generator, *bn_B, *bn_result; + BN_CTX *bn_ctx; + unsigned char* ptr; + unsigned dlen; + ERL_NIF_TERM ret; + + if (!get_bn_from_bin(env, argv[0], &bn_a) + || !get_bn_from_bin(env, argv[1], &bn_u) + || !get_bn_from_bin(env, argv[2], &bn_B) + || !get_bn_from_bin(env, argv[3], &bn_multiplier) + || !get_bn_from_bin(env, argv[4], &bn_generator) + || !get_bn_from_bin(env, argv[5], &bn_exponent) + || !get_bn_from_bin(env, argv[6], &bn_prime)) + { + if (bn_exponent) BN_free(bn_exponent); + if (bn_a) BN_free(bn_a); + if (bn_u) BN_free(bn_u); + if (bn_B) BN_free(bn_B); + if (bn_multiplier) BN_free(bn_multiplier); + if (bn_generator) BN_free(bn_generator); + if (bn_prime) BN_free(bn_prime); + return enif_make_badarg(env); + } + + bn_ctx = BN_CTX_new(); + bn_result = BN_new(); + + /* check that B % N != 0 */ + BN_nnmod(bn_result, bn_B, bn_prime, bn_ctx); + if (BN_is_zero(bn_result)) { + BN_free(bn_exponent); + BN_free(bn_a); + BN_free(bn_generator); + BN_free(bn_prime); + BN_free(bn_u); + BN_free(bn_B); + BN_CTX_free(bn_ctx); + + return atom_error; + } + + /* (B - (k * g^x)) */ + bn_base = BN_new(); + BN_mod_exp(bn_result, bn_generator, bn_exponent, bn_prime, bn_ctx); + BN_mod_mul(bn_result, bn_multiplier, bn_result, bn_prime, bn_ctx); + BN_mod_sub(bn_base, bn_B, bn_result, bn_prime, bn_ctx); + + /* a + (u * x) */ + bn_exp2 = BN_new(); + BN_mod_mul(bn_result, bn_u, bn_exponent, bn_prime, bn_ctx); + BN_mod_add(bn_exp2, bn_a, bn_result, bn_prime, bn_ctx); + + /* (B - (k * g^x)) ^ (a + (u * x)) % N */ + BN_mod_exp(bn_result, bn_base, bn_exp2, bn_prime, bn_ctx); + + dlen = BN_num_bytes(bn_result); + ptr = enif_make_new_binary(env, dlen, &ret); + BN_bn2bin(bn_result, ptr); + BN_free(bn_result); + BN_CTX_free(bn_ctx); + + BN_free(bn_multiplier); + BN_free(bn_exp2); + BN_free(bn_u); + BN_free(bn_exponent); + BN_free(bn_a); + BN_free(bn_B); + BN_free(bn_base); + BN_free(bn_generator); + BN_free(bn_prime); + return ret; +} + +static ERL_NIF_TERM srp_server_secret_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Verifier, b, u, A, Prime) */ +/* + <premaster secret> = (A * v^u) ^ b % N +*/ + BIGNUM *bn_b = NULL, *bn_verifier = NULL; + BIGNUM *bn_prime, *bn_A, *bn_u, *bn_base, *bn_result; + BN_CTX *bn_ctx; + unsigned char* ptr; + unsigned dlen; + ERL_NIF_TERM ret; + + if (!get_bn_from_bin(env, argv[0], &bn_verifier) + || !get_bn_from_bin(env, argv[1], &bn_b) + || !get_bn_from_bin(env, argv[2], &bn_u) + || !get_bn_from_bin(env, argv[3], &bn_A) + || !get_bn_from_bin(env, argv[4], &bn_prime)) + { + if (bn_verifier) BN_free(bn_verifier); + if (bn_b) BN_free(bn_b); + if (bn_u) BN_free(bn_u); + if (bn_A) BN_free(bn_A); + if (bn_prime) BN_free(bn_prime); + return enif_make_badarg(env); + } + + bn_ctx = BN_CTX_new(); + bn_result = BN_new(); + + /* check that A % N != 0 */ + BN_nnmod(bn_result, bn_A, bn_prime, bn_ctx); + if (BN_is_zero(bn_result)) { + BN_free(bn_b); + BN_free(bn_verifier); + BN_free(bn_prime); + BN_free(bn_A); + BN_CTX_free(bn_ctx); + + return atom_error; + } + + /* (A * v^u) */ + bn_base = BN_new(); + BN_mod_exp(bn_base, bn_verifier, bn_u, bn_prime, bn_ctx); + BN_mod_mul(bn_base, bn_A, bn_base, bn_prime, bn_ctx); + + /* (A * v^u) ^ b % N */ + BN_mod_exp(bn_result, bn_base, bn_b, bn_prime, bn_ctx); + + dlen = BN_num_bytes(bn_result); + ptr = enif_make_new_binary(env, dlen, &ret); + BN_bn2bin(bn_result, ptr); + BN_free(bn_result); + BN_CTX_free(bn_ctx); + + BN_free(bn_u); + BN_free(bn_base); + BN_free(bn_verifier); + BN_free(bn_prime); + BN_free(bn_A); + BN_free(bn_b); + return ret; +} + static ERL_NIF_TERM bf_cfb64_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Key, Ivec, Data, IsEncrypt) */ ErlNifBinary key_bin, ivec_bin, data_bin; diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml index 6b9b2ef207..f30a058ca4 100755 --- a/lib/crypto/doc/src/crypto.xml +++ b/lib/crypto/doc/src/crypto.xml @@ -63,6 +63,11 @@ <item> <p>dss: Digital Signature Standard (FIPS 186-2)</p> </item> + <item> + <p>srp: Secure Remote Password Protocol (RFC 2945)</p> + </item> + + </list> <p>The above publications can be found at <url href="http://csrc.nist.gov/publications">NIST publications</url>, at <url href="http://www.ietf.org">IETF</url>. </p> @@ -99,6 +104,14 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> </desc> </func> <func> + <name>algorithms() -> [atom()]</name> + <fsummary>Provide a list of available crypto algorithms.</fsummary> + <desc> + <p>Provides the available crypto algorithms in terms of a list + of atoms.</p> + </desc> + </func> + <func> <name>info_lib() -> [{Name,VerNum,VerStr}]</name> <fsummary>Provides information about the libraries used by crypto.</fsummary> <type> @@ -973,7 +986,17 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> using the <c>crypto</c> library.</p> </desc> </func> - + <func> + <name>mod_exp_prime(N, P, M) -> Result</name> + <fsummary>Computes the function: N^P mod M</fsummary> + <type> + <v>N, P, M = binary()</v> + <v>Result = binary() | error</v> + </type> + <desc> + <p>Computes the function <c>N^P mod M</c>.</p> + </desc> + </func> <func> <name>rsa_sign(DataOrDigest, Key) -> Signature</name> <name>rsa_sign(DigestType, DataOrDigest, Key) -> Signature</name> @@ -1256,8 +1279,85 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> </desc> </func> + <func> + <name>srp_generate_key(Generator, Prime, Version) -> {PublicKey, PrivateKey} </name> + <name>srp_generate_key(Generator, Prime, Version, Private) -> {PublicKey, PrivateKey} </name> + <name>srp_generate_key(Verifier, Generator, Prime, Version) -> {PublicKey, PrivateKey} </name> + <name>srp_generate_key(Verifier, Generator, Prime, Version, Private) -> {PublicKey, PrivateKey} </name> + <fsummary>Generates SRP public keys</fsummary> + <type> + <v>Verifier = binary()</v> + <d>Parameter v from <seealso marker="http://srp.stanford.edu/design.html">SRP design</seealso> + </d> + <v>Generator = binary() </v> + <d>Parameter g from <seealso marker="http://srp.stanford.edu/design.html">SRP design</seealso> + </d> + <v>Prime = binary() </v> + <d>Parameter N from <seealso marker="http://srp.stanford.edu/design.html">SRP design</seealso> + </d> + <v>Version = '3' | '6' | '6a' </v> + <d>SRP version, TLS SRP cipher suites uses '6a'.</d> + <v>PublicKey = binary()</v> + <d> Parameter A or B from <seealso marker="http://srp.stanford.edu/design.html">SRP design</seealso></d> + <v>PrivateKey = binary() - generated if not supplied</v> + <d>Parameter a or b from <seealso marker="http://srp.stanford.edu/design.html">SRP design</seealso></d> + </type> + <desc> + <p>Generates SRP public keys</p> + </desc> + </func> <func> + <name>srp_compute_key(DerivedKey, Prime, Generator, + ClientPublic, ClientPrivate, ServerPublic, Version) -> SessionKey</name> + <name>srp_compute_key(DerivedKey, Prime, Generator, + ClientPublic, ClientPrivate, ServerPublic, Version, Scrambler) -> SessionKey</name> + <name>srp_compute_key(Verifier, Prime, + ClientPublic, ServerPublic, ServerPrivate, Version, Scrambler)-> SessionKey</name> + <name>srp_compute_key(Verifier, Prime, + ClientPublic, ServerPublic, ServerPrivate, Version) -> SessionKey</name> + + <fsummary>Computes SRP session key</fsummary> + <type> + <v>DerivedKey = binary()</v> + <d>Parameter x from <url href="http://srp.stanford.edu/design.html">SRP design</url> + </d> + <v>Verifier = binary()</v> + <d>Parameter v from <url href="http://srp.stanford.edu/design.html">SRP design</url> + </d> + <v>Prime = binary() </v> + <d>Parameter N from <url href="http://srp.stanford.edu/design.html">SRP design</url> + </d> + <v>Generator = binary() </v> + <d>Parameter g from <url href="http://srp.stanford.edu/design.html">SRP design</url> + </d> + <v>ClientPublic = binary() </v> + <d>Parameter A from <url href="http://srp.stanford.edu/design.html">SRP design</url> + </d> + <v>ClientPrivate = binary() </v> + <d>Parameter a from <url href="http://srp.stanford.edu/design.html">SRP design</url> + </d> + <v>ServerPublic = binary() </v> + <d>Parameter B from <url href="http://srp.stanford.edu/design.html">SRP design</url> + </d> + <v>ServerPrivate = binary() </v> + <d>Parameter b from <url href="http://srp.stanford.edu/design.html">SRP design</url> + </d> + <v>Version = '3' | '6' | '6a' </v> + <d>SRP version, TLS SRP cipher suites uses '6a'.</d> + <v>SessionKey = binary()</v> + <d>Result K from <url href="http://srp.stanford.edu/design.html">SRP design</url> + </d> + </type> + <desc> + <p> + Computes the SRP session key (shared secret). Also used + as premaster secret by TLS-SRP ciher suites. + </p> + </desc> + </func> + + <func> <name>exor(Data1, Data2) -> Result</name> <fsummary>XOR data</fsummary> <type> diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl index 1328a95e87..1d0a9943c3 100644 --- a/lib/crypto/src/crypto.erl +++ b/lib/crypto/src/crypto.erl @@ -21,7 +21,7 @@ -module(crypto). --export([start/0, stop/0, info/0, info_lib/0, version/0]). +-export([start/0, stop/0, info/0, info_lib/0, algorithms/0, version/0]). -export([hash/2, hash_init/1, hash_update/2, hash_final/1]). -export([md4/1, md4_init/0, md4_update/2, md4_final/1]). -export([md5/1, md5_init/0, md5_update/2, md5_final/1]). @@ -57,7 +57,10 @@ -export([dh_generate_key/1, dh_generate_key/2, dh_compute_key/3]). -export([rand_bytes/1, rand_bytes/3, rand_uniform/2]). -export([strong_rand_bytes/1, strong_rand_mpint/3]). --export([mod_exp/3, mpint/1, erlint/1]). +-export([mod_exp/3, mod_exp_prime/3, mpint/1, erlint/1]). +-export([srp_generate_key/4, srp_generate_key/3, + srp_generate_key/5, srp_compute_key/6, srp_compute_key/7, srp_compute_key/8]). + %% -export([idea_cbc_encrypt/3, idea_cbc_decrypt/3]). -export([aes_cbc_128_encrypt/3, aes_cbc_128_decrypt/3]). -export([aes_cbc_256_encrypt/3, aes_cbc_256_decrypt/3]). @@ -88,7 +91,7 @@ strong_rand_bytes, strong_rand_mpint, rand_uniform, - mod_exp, + mod_exp, mod_exp_prime, dss_verify,dss_sign, rsa_verify,rsa_sign, rsa_public_encrypt,rsa_private_decrypt, @@ -109,7 +112,8 @@ hash, hash_init, hash_update, hash_final, hmac, hmac_init, hmac_update, hmac_final, hmac_final_n, info, rc2_cbc_encrypt, rc2_cbc_decrypt, - info_lib]). + srp_generate_key, srp_compute_key, + info_lib, algorithms]). -type rsa_digest_type() :: 'md5' | 'sha' | 'sha224' | 'sha256' | 'sha384' | 'sha512'. -type dss_digest_type() :: 'none' | 'sha'. @@ -184,6 +188,8 @@ info() -> info_lib() -> ?nif_stub. +algorithms() -> ?nif_stub. + %% Crypto app version history: %% (no version): Driver implementation %% 2.0 : NIF implementation, requires OTP R14 @@ -783,21 +789,24 @@ rand_uniform_pos(_,_) -> rand_uniform_nif(_From,_To) -> ?nif_stub. %% -%% mod_exp - utility for rsa generation +%% mod_exp - utility for rsa generation and SRP %% mod_exp(Base, Exponent, Modulo) when is_integer(Base), is_integer(Exponent), is_integer(Modulo) -> - erlint(mod_exp(mpint(Base), mpint(Exponent), mpint(Modulo))); + bin_to_int(mod_exp_nif(int_to_bin(Base), int_to_bin(Exponent), int_to_bin(Modulo), 0)); mod_exp(Base, Exponent, Modulo) -> - case mod_exp_nif(Base,Exponent,Modulo) of - <<Len:32/integer, MSB, Rest/binary>> when MSB > 127 -> - <<(Len + 1):32/integer, 0, MSB, Rest/binary>>; - Whatever -> - Whatever + mod_exp_nif(mpint_to_bin(Base),mpint_to_bin(Exponent),mpint_to_bin(Modulo), 4). + +-spec mod_exp_prime(binary(), binary(), binary()) -> binary() | error. +mod_exp_prime(Base, Exponent, Prime) -> + case mod_exp_nif(Base, Exponent, Prime, 0) of + <<0>> -> error; + R -> R end. -mod_exp_nif(_Base,_Exp,_Mod) -> ?nif_stub. + +mod_exp_nif(_Base,_Exp,_Mod,_bin_hdr) -> ?nif_stub. %% %% DSS, RSA - verify @@ -1064,50 +1073,205 @@ dh_compute_key(OthersPublicKey, MyPrivateKey, DHParameters) -> dh_compute_key_nif(_OthersPublicKey, _MyPrivateKey, _DHParameters) -> ?nif_stub. + +%%% SRP +-spec srp_generate_key(binary(), binary(), atom() | binary(), atom() | binary() ) -> {Public::binary(), Private::binary()}. +srp_generate_key(Verifier, Generator, Prime, Version) when is_binary(Verifier), + is_binary(Generator), + is_binary(Prime), + is_atom(Version) -> + Private = random_bytes(32), + server_srp_gen_key(Private, Verifier, Generator, Prime, Version); + +srp_generate_key(Generator, Prime, Version, Private) when is_binary(Generator), + is_binary(Prime), + is_atom(Version), + is_binary(Private) -> + client_srp_gen_key(Private, Generator, Prime). + +-spec srp_generate_key(binary(), binary(), binary(), atom(), binary()) -> {Public::binary(), Private::binary()}. +srp_generate_key(Verifier, Generator, Prime, Version, Private) when is_binary(Verifier), + is_binary(Generator), + is_binary(Prime), + is_atom(Version), + is_binary(Private) + -> + server_srp_gen_key(Private, Verifier, Generator, Prime, Version). + +-spec srp_generate_key(binary(), binary(), atom()) -> {Public::binary(), Private::binary()}. +srp_generate_key(Generator, Prime, Version) when is_binary(Generator), + is_binary(Prime), + is_atom(Version) -> + Private = random_bytes(32), + client_srp_gen_key(Private, Generator, Prime). + +-spec srp_compute_key(binary(), binary(), binary(), binary(), binary(), atom()| binary(), atom() | binary() ) -> binary(). +srp_compute_key(DerivedKey, Prime, Generator, ClientPublic, ClientPrivate, ServerPublic, Version) when + is_binary(Prime), + is_binary(Generator), + is_binary(ClientPublic), + is_binary(ClientPrivate), + is_binary(ServerPublic), + is_atom(Version) -> + Multiplier = srp_multiplier(Version, Generator, Prime), + Scrambler = srp_scrambler(Version, ClientPublic, ServerPublic, Prime), + srp_client_secret_nif(ClientPrivate, Scrambler, ServerPublic, Multiplier, + Generator, DerivedKey, Prime); + +srp_compute_key(Verifier, Prime, ClientPublic, ServerPublic, ServerPrivate, Version, Scrambler) when + is_binary(Verifier), + is_binary(Prime), + is_binary(ClientPublic), + is_binary(ServerPublic), + is_binary(ServerPrivate), + is_atom(Version), + is_binary(Scrambler) -> + srp_server_secret_nif(Verifier, ServerPrivate, Scrambler, ClientPublic, Prime). + +-spec srp_compute_key(binary(), binary(), binary(), binary(), binary(), binary(), atom(), binary()) -> binary(). +srp_compute_key(DerivedKey, Prime, Generator, ClientPublic, ClientPrivate, + ServerPublic, Version, Scrambler) when is_binary(DerivedKey), + is_binary(Prime), + is_binary(Generator), + is_binary(ClientPublic), + is_binary(ClientPrivate), + is_binary(ServerPublic), + is_atom(Version), + is_binary(Scrambler) -> + Multiplier = srp_multiplier(Version, Generator, Prime), + srp_client_secret_nif(ClientPrivate, Scrambler, ServerPublic, Multiplier, + Generator, DerivedKey, Prime). + +-spec srp_compute_key(binary(), binary(), binary(), binary(), binary(), atom()) -> binary(). +srp_compute_key(Verifier, Prime, ClientPublic, ServerPublic, ServerPrivate, Version) when + is_binary(Verifier), + is_binary(Prime), + is_binary(ClientPublic), + is_binary(ServerPublic), + is_binary(ServerPrivate), + is_atom(Version) -> + Scrambler = srp_scrambler(Version, ClientPublic, ServerPublic, Prime), + srp_server_secret_nif(Verifier, ServerPrivate, Scrambler, ClientPublic, Prime). + %% %% LOCAL FUNCTIONS %% +client_srp_gen_key(Private, Generator, Prime) -> + case mod_exp_prime(Generator, Private, Prime) of + error -> + error; + Public -> + {Public, Private} + end. + +server_srp_gen_key(Private, Verifier, Generator, Prime, Version) -> + Multiplier = srp_multiplier(Version, Generator, Prime), + case srp_value_B_nif(Multiplier, Verifier, Generator, Private, Prime) of + error -> + error; + Public -> + {Public, Private} + end. + +srp_multiplier('6a', Generator, Prime) -> + %% k = SHA1(N | PAD(g)) from http://srp.stanford.edu/design.html + C0 = sha_init(), + C1 = sha_update(C0, Prime), + C2 = sha_update(C1, srp_pad_to(erlang:byte_size(Prime), Generator)), + sha_final(C2); +srp_multiplier('6', _, _) -> + <<3/integer>>; +srp_multiplier('3', _, _) -> + <<1/integer>>. + +srp_scrambler(Version, ClientPublic, ServerPublic, Prime) when Version == '6'; Version == '6a'-> + %% SHA1(PAD(A) | PAD(B)) from http://srp.stanford.edu/design.html + PadLength = erlang:byte_size(Prime), + C0 = sha_init(), + C1 = sha_update(C0, srp_pad_to(PadLength, ClientPublic)), + C2 = sha_update(C1, srp_pad_to(PadLength, ServerPublic)), + sha_final(C2); +srp_scrambler('3', _, ServerPublic, _Prime) -> + %% The parameter u is a 32-bit unsigned integer which takes its value + %% from the first 32 bits of the SHA1 hash of B, MSB first. + <<U:32/bits, _/binary>> = sha(ServerPublic), + U. + +srp_pad_length(Width, Length) -> + (Width - Length rem Width) rem Width. + +srp_pad_to(Width, Binary) -> + case srp_pad_length(Width, size(Binary)) of + 0 -> Binary; + N -> << 0:(N*8), Binary/binary>> + end. + +srp_server_secret_nif(_Verifier, _B, _U, _A, _Prime) -> ?nif_stub. + +srp_client_secret_nif(_A, _U, _B, _Multiplier, _Generator, _Exponent, _Prime) -> ?nif_stub. + +srp_value_B_nif(_Multiplier, _Verifier, _Generator, _Exponent, _Prime) -> ?nif_stub. %% large integer in a binary with 32bit length %% MP representaion (SSH2) -mpint(X) when X < 0 -> - case X of - -1 -> - <<0,0,0,1,16#ff>>; - _ -> - mpint_neg(X,0,[]) - end; -mpint(X) -> - case X of - 0 -> - <<0,0,0,0>>; - _ -> - mpint_pos(X,0,[]) - end. +mpint(X) when X < 0 -> mpint_neg(X); +mpint(X) -> mpint_pos(X). -define(UINT32(X), X:32/unsigned-big-integer). -mpint_neg(-1,I,Ds=[MSB|_]) -> - if MSB band 16#80 =/= 16#80 -> - <<?UINT32((I+1)), (list_to_binary([255|Ds]))/binary>>; - true -> - (<<?UINT32(I), (list_to_binary(Ds))/binary>>) - end; -mpint_neg(X,I,Ds) -> - mpint_neg(X bsr 8,I+1,[(X band 255)|Ds]). + +mpint_neg(X) -> + Bin = int_to_bin_neg(X, []), + Sz = byte_size(Bin), + <<?UINT32(Sz), Bin/binary>>. -mpint_pos(0,I,Ds=[MSB|_]) -> +mpint_pos(X) -> + Bin = int_to_bin_pos(X, []), + <<MSB,_/binary>> = Bin, + Sz = byte_size(Bin), if MSB band 16#80 == 16#80 -> - <<?UINT32((I+1)), (list_to_binary([0|Ds]))/binary>>; + <<?UINT32((Sz+1)), 0, Bin/binary>>; true -> - (<<?UINT32(I), (list_to_binary(Ds))/binary>>) - end; -mpint_pos(X,I,Ds) -> - mpint_pos(X bsr 8,I+1,[(X band 255)|Ds]). + <<?UINT32(Sz), Bin/binary>> + end. + +int_to_bin(X) when X < 0 -> int_to_bin_neg(X, []); +int_to_bin(X) -> int_to_bin_pos(X, []). + +%%int_to_bin_pos(X) when X >= 0 -> +%% int_to_bin_pos(X, []). + +int_to_bin_pos(0,Ds=[_|_]) -> + list_to_binary(Ds); +int_to_bin_pos(X,Ds) -> + int_to_bin_pos(X bsr 8, [(X band 255)|Ds]). + +int_to_bin_neg(-1, Ds=[MSB|_]) when MSB >= 16#80 -> + list_to_binary(Ds); +int_to_bin_neg(X,Ds) -> + int_to_bin_neg(X bsr 8, [(X band 255)|Ds]). + + +bin_to_int(Bin) -> + Bits = bit_size(Bin), + <<Integer:Bits/integer>> = Bin, + Integer. %% int from integer in a binary with 32bit length erlint(<<MPIntSize:32/integer,MPIntValue/binary>>) -> Bits= MPIntSize * 8, <<Integer:Bits/integer>> = MPIntValue, Integer. + +mpint_to_bin(<<Len:32, Bin:Len/binary>>) -> + Bin. + +random_bytes(N) -> + try strong_rand_bytes(N) of + RandBytes -> + RandBytes + catch + error:low_entropy -> + rand_bytes(N) + end. diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl index 6f2df0f07b..08ecad3233 100644 --- a/lib/crypto/test/crypto_SUITE.erl +++ b/lib/crypto/test/crypto_SUITE.erl @@ -39,7 +39,10 @@ hmac_update_md5_io/1, hmac_update_md5_n/1, hmac_rfc2202/1, - hmac_rfc4231/1, + hmac_rfc4231_sha224/1, + hmac_rfc4231_sha256/1, + hmac_rfc4231_sha384/1, + hmac_rfc4231_sha512/1, ripemd160/1, ripemd160_update/1, sha256/1, @@ -72,6 +75,7 @@ dsa_sign_hash_test/1, rsa_encrypt_decrypt/1, dh/1, + srp3/1, srp6/1, srp6a/1, exor_test/1, rc4_test/1, rc4_stream_test/1, @@ -93,14 +97,15 @@ groups() -> sha256, sha256_update, sha512, sha512_update, hmac_update_sha, hmac_update_sha_n, hmac_update_sha256, hmac_update_sha512, hmac_update_md5_n, hmac_update_md5_io, hmac_update_md5, - hmac_rfc2202, hmac_rfc4231, + hmac_rfc2202, hmac_rfc4231_sha224, hmac_rfc4231_sha256, + hmac_rfc4231_sha384, hmac_rfc4231_sha512, des_cbc, aes_cfb, aes_cbc, des_cfb, des_cfb_iter, des3_cbc, des3_cfb, rc2_cbc, aes_cbc_iter, aes_ctr, aes_ctr_stream, des_cbc_iter, des_ecb, rand_uniform_test, strong_rand_test, rsa_verify_test, dsa_verify_test, rsa_sign_test, rsa_sign_hash_test, dsa_sign_test, dsa_sign_hash_test, - rsa_encrypt_decrypt, dh, exor_test, + rsa_encrypt_decrypt, dh, srp3, srp6, srp6a, exor_test, rc4_test, rc4_stream_test, mod_exp_test, blowfish_cfb64, smp]}]. @@ -363,8 +368,7 @@ hmac_update_sha256(doc) -> hmac_update_sha256(suite) -> []; hmac_update_sha256(Config) when is_list(Config) -> - if_098(fun() -> hmac_update_sha256_do() end). - + if_supported(sha256, fun() -> hmac_update_sha256_do() end). hmac_update_sha256_do() -> ?line Key = hexstr2bin("00010203101112132021222330313233" @@ -386,7 +390,7 @@ hmac_update_sha512(doc) -> hmac_update_sha512(suite) -> []; hmac_update_sha512(Config) when is_list(Config) -> - if_098(fun() -> hmac_update_sha512_do() end). + if_supported(sha512, fun() -> hmac_update_sha512_do() end). hmac_update_sha512_do() -> ?line Key = hexstr2bin("00010203101112132021222330313233" @@ -582,366 +586,192 @@ hmac_rfc2202_sha() -> ?line m(Case7Exp, Case7Mac_1), ?line m(Case7Exp, Case7Mac_2). -hmac_rfc4231(doc) -> - ["Generate an HMAC using crypto:shaXXX_mac, hmac, and hmac_init, hmac_update, and hmac_final. " +hmac_rfc4231_sha224(doc) -> + ["Generate an HMAC using crypto:sha224_mac, hmac, and hmac_init, hmac_update, and hmac_final. " "Testvectors are take from RFC4231." ]; -hmac_rfc4231(suite) -> +hmac_rfc4231_sha224(suite) -> []; -hmac_rfc4231(Config) when is_list(Config) -> - if_098(fun() -> hmac_rfc4231_do() end). +hmac_rfc4231_sha224(Config) when is_list(Config) -> + if_supported(sha224, fun() -> hmac_rfc4231_sha224_do() end). -hmac_rfc4231_do() -> - %% Test Case 1 - Case1Key = binary:copy(<<16#0b>>, 20), - Case1Data = <<"Hi There">>, - Case1Exp224 = hexstr2bin("896fb1128abbdf196832107cd49df33f" - "47b4b1169912ba4f53684b22"), - Case1Exp256 = hexstr2bin("b0344c61d8db38535ca8afceaf0bf12b" - "881dc200c9833da726e9376c2e32cff7"), - Case1Exp384 = hexstr2bin("afd03944d84895626b0825f4ab46907f" - "15f9dadbe4101ec682aa034c7cebc59c" - "faea9ea9076ede7f4af152e8b2fa9cb6"), - Case1Exp512 = hexstr2bin("87aa7cdea5ef619d4ff0b4241a1d6cb0" - "2379f4e2ce4ec2787ad0b30545e17cde" - "daa833b7d6b8a702038b274eaea3f4e4" - "be9d914eeb61f1702e696c203a126854"), - - ?line Case1Ctx224 = crypto:hmac_init(sha224, Case1Key), - ?line Case1Ctx224_2 = crypto:hmac_update(Case1Ctx224, Case1Data), - ?line Case1Mac224_1 = crypto:hmac_final(Case1Ctx224_2), - ?line Case1Mac224_2 = crypto:sha224_mac(Case1Key, Case1Data), - ?line Case1Mac224_3 = crypto:hmac(sha224, Case1Key, Case1Data), - ?line m(Case1Exp224, Case1Mac224_1), - ?line m(Case1Exp224, Case1Mac224_2), - ?line m(Case1Exp224, Case1Mac224_3), - - ?line Case1Ctx256 = crypto:hmac_init(sha256, Case1Key), - ?line Case1Ctx256_2 = crypto:hmac_update(Case1Ctx256, Case1Data), - ?line Case1Mac256_1 = crypto:hmac_final(Case1Ctx256_2), - ?line Case1Mac256_2 = crypto:sha256_mac(Case1Key, Case1Data), - ?line Case1Mac256_3 = crypto:hmac(sha256, Case1Key, Case1Data), - ?line m(Case1Exp256, Case1Mac256_1), - ?line m(Case1Exp256, Case1Mac256_2), - ?line m(Case1Exp256, Case1Mac256_3), - - ?line Case1Ctx384 = crypto:hmac_init(sha384, Case1Key), - ?line Case1Ctx384_2 = crypto:hmac_update(Case1Ctx384, Case1Data), - ?line Case1Mac384_1 = crypto:hmac_final(Case1Ctx384_2), - ?line Case1Mac384_2 = crypto:sha384_mac(Case1Key, Case1Data), - ?line Case1Mac384_3 = crypto:hmac(sha384, Case1Key, Case1Data), - ?line m(Case1Exp384, Case1Mac384_1), - ?line m(Case1Exp384, Case1Mac384_2), - ?line m(Case1Exp384, Case1Mac384_3), - - ?line Case1Ctx512 = crypto:hmac_init(sha512, Case1Key), - ?line Case1Ctx512_2 = crypto:hmac_update(Case1Ctx512, Case1Data), - ?line Case1Mac512_1 = crypto:hmac_final(Case1Ctx512_2), - ?line Case1Mac512_2 = crypto:sha512_mac(Case1Key, Case1Data), - ?line Case1Mac512_3 = crypto:hmac(sha512, Case1Key, Case1Data), - ?line m(Case1Exp512, Case1Mac512_1), - ?line m(Case1Exp512, Case1Mac512_2), - ?line m(Case1Exp512, Case1Mac512_3), - - %% Test Case 2 - Case2Key = <<"Jefe">>, - Case2Data = <<"what do ya want for nothing?">>, - Case2Exp224 = hexstr2bin("a30e01098bc6dbbf45690f3a7e9e6d0f" - "8bbea2a39e6148008fd05e44"), - Case2Exp256 = hexstr2bin("5bdcc146bf60754e6a042426089575c7" - "5a003f089d2739839dec58b964ec3843"), - Case2Exp384 = hexstr2bin("af45d2e376484031617f78d2b58a6b1b" - "9c7ef464f5a01b47e42ec3736322445e" - "8e2240ca5e69e2c78b3239ecfab21649"), - Case2Exp512 = hexstr2bin("164b7a7bfcf819e2e395fbe73b56e0a3" - "87bd64222e831fd610270cd7ea250554" - "9758bf75c05a994a6d034f65f8f0e6fd" - "caeab1a34d4a6b4b636e070a38bce737"), - - ?line Case2Ctx224 = crypto:hmac_init(sha224, Case2Key), - ?line Case2Ctx224_2 = crypto:hmac_update(Case2Ctx224, Case2Data), - ?line Case2Mac224_1 = crypto:hmac_final(Case2Ctx224_2), - ?line Case2Mac224_2 = crypto:sha224_mac(Case2Key, Case2Data), - ?line Case2Mac224_3 = crypto:hmac(sha224, Case2Key, Case2Data), - ?line m(Case2Exp224, Case2Mac224_1), - ?line m(Case2Exp224, Case2Mac224_2), - ?line m(Case2Exp224, Case2Mac224_3), - - ?line Case2Ctx256 = crypto:hmac_init(sha256, Case2Key), - ?line Case2Ctx256_2 = crypto:hmac_update(Case2Ctx256, Case2Data), - ?line Case2Mac256_1 = crypto:hmac_final(Case2Ctx256_2), - ?line Case2Mac256_2 = crypto:sha256_mac(Case2Key, Case2Data), - ?line Case2Mac256_3 = crypto:hmac(sha256, Case2Key, Case2Data), - ?line m(Case2Exp256, Case2Mac256_1), - ?line m(Case2Exp256, Case2Mac256_2), - ?line m(Case2Exp256, Case2Mac256_3), - - ?line Case2Ctx384 = crypto:hmac_init(sha384, Case2Key), - ?line Case2Ctx384_2 = crypto:hmac_update(Case2Ctx384, Case2Data), - ?line Case2Mac384_1 = crypto:hmac_final(Case2Ctx384_2), - ?line Case2Mac384_2 = crypto:sha384_mac(Case2Key, Case2Data), - ?line Case2Mac384_3 = crypto:hmac(sha384, Case2Key, Case2Data), - ?line m(Case2Exp384, Case2Mac384_1), - ?line m(Case2Exp384, Case2Mac384_2), - ?line m(Case2Exp384, Case2Mac384_3), - - ?line Case2Ctx512 = crypto:hmac_init(sha512, Case2Key), - ?line Case2Ctx512_2 = crypto:hmac_update(Case2Ctx512, Case2Data), - ?line Case2Mac512_1 = crypto:hmac_final(Case2Ctx512_2), - ?line Case2Mac512_2 = crypto:sha512_mac(Case2Key, Case2Data), - ?line Case2Mac512_3 = crypto:hmac(sha512, Case2Key, Case2Data), - ?line m(Case2Exp512, Case2Mac512_1), - ?line m(Case2Exp512, Case2Mac512_2), - ?line m(Case2Exp512, Case2Mac512_3), - - %% Test Case 3 - Case3Key = binary:copy(<<16#aa>>, 20), - Case3Data = binary:copy(<<16#dd>>, 50), - Case3Exp224 = hexstr2bin("7fb3cb3588c6c1f6ffa9694d7d6ad264" - "9365b0c1f65d69d1ec8333ea"), - Case3Exp256 = hexstr2bin("773ea91e36800e46854db8ebd09181a7" - "2959098b3ef8c122d9635514ced565fe"), - Case3Exp384 = hexstr2bin("88062608d3e6ad8a0aa2ace014c8a86f" - "0aa635d947ac9febe83ef4e55966144b" - "2a5ab39dc13814b94e3ab6e101a34f27"), - Case3Exp512 = hexstr2bin("fa73b0089d56a284efb0f0756c890be9" - "b1b5dbdd8ee81a3655f83e33b2279d39" - "bf3e848279a722c806b485a47e67c807" - "b946a337bee8942674278859e13292fb"), - - ?line Case3Ctx224 = crypto:hmac_init(sha224, Case3Key), - ?line Case3Ctx224_2 = crypto:hmac_update(Case3Ctx224, Case3Data), - ?line Case3Mac224_1 = crypto:hmac_final(Case3Ctx224_2), - ?line Case3Mac224_2 = crypto:sha224_mac(Case3Key, Case3Data), - ?line Case3Mac224_3 = crypto:hmac(sha224, Case3Key, Case3Data), - ?line m(Case3Exp224, Case3Mac224_1), - ?line m(Case3Exp224, Case3Mac224_2), - ?line m(Case3Exp224, Case3Mac224_3), - - ?line Case3Ctx256 = crypto:hmac_init(sha256, Case3Key), - ?line Case3Ctx256_2 = crypto:hmac_update(Case3Ctx256, Case3Data), - ?line Case3Mac256_1 = crypto:hmac_final(Case3Ctx256_2), - ?line Case3Mac256_2 = crypto:sha256_mac(Case3Key, Case3Data), - ?line Case3Mac256_3 = crypto:hmac(sha256, Case3Key, Case3Data), - ?line m(Case3Exp256, Case3Mac256_1), - ?line m(Case3Exp256, Case3Mac256_2), - ?line m(Case3Exp256, Case3Mac256_3), - - ?line Case3Ctx384 = crypto:hmac_init(sha384, Case3Key), - ?line Case3Ctx384_2 = crypto:hmac_update(Case3Ctx384, Case3Data), - ?line Case3Mac384_1 = crypto:hmac_final(Case3Ctx384_2), - ?line Case3Mac384_2 = crypto:sha384_mac(Case3Key, Case3Data), - ?line Case3Mac384_3 = crypto:hmac(sha384, Case3Key, Case3Data), - ?line m(Case3Exp384, Case3Mac384_1), - ?line m(Case3Exp384, Case3Mac384_2), - ?line m(Case3Exp384, Case3Mac384_3), - - ?line Case3Ctx512 = crypto:hmac_init(sha512, Case3Key), - ?line Case3Ctx512_2 = crypto:hmac_update(Case3Ctx512, Case3Data), - ?line Case3Mac512_1 = crypto:hmac_final(Case3Ctx512_2), - ?line Case3Mac512_2 = crypto:sha512_mac(Case3Key, Case3Data), - ?line Case3Mac512_3 = crypto:hmac(sha512, Case3Key, Case3Data), - ?line m(Case3Exp512, Case3Mac512_1), - ?line m(Case3Exp512, Case3Mac512_2), - ?line m(Case3Exp512, Case3Mac512_3), - - %% Test Case 4 - Case4Key = list_to_binary(lists:seq(1, 16#19)), - Case4Data = binary:copy(<<16#cd>>, 50), - Case4Exp224 = hexstr2bin("6c11506874013cac6a2abc1bb382627c" - "ec6a90d86efc012de7afec5a"), - Case4Exp256 = hexstr2bin("82558a389a443c0ea4cc819899f2083a" - "85f0faa3e578f8077a2e3ff46729665b"), - Case4Exp384 = hexstr2bin("3e8a69b7783c25851933ab6290af6ca7" - "7a9981480850009cc5577c6e1f573b4e" - "6801dd23c4a7d679ccf8a386c674cffb"), - Case4Exp512 = hexstr2bin("b0ba465637458c6990e5a8c5f61d4af7" - "e576d97ff94b872de76f8050361ee3db" - "a91ca5c11aa25eb4d679275cc5788063" - "a5f19741120c4f2de2adebeb10a298dd"), - - ?line Case4Ctx224 = crypto:hmac_init(sha224, Case4Key), - ?line Case4Ctx224_2 = crypto:hmac_update(Case4Ctx224, Case4Data), - ?line Case4Mac224_1 = crypto:hmac_final(Case4Ctx224_2), - ?line Case4Mac224_2 = crypto:sha224_mac(Case4Key, Case4Data), - ?line Case4Mac224_3 = crypto:hmac(sha224, Case4Key, Case4Data), - ?line m(Case4Exp224, Case4Mac224_1), - ?line m(Case4Exp224, Case4Mac224_2), - ?line m(Case4Exp224, Case4Mac224_3), - - ?line Case4Ctx256 = crypto:hmac_init(sha256, Case4Key), - ?line Case4Ctx256_2 = crypto:hmac_update(Case4Ctx256, Case4Data), - ?line Case4Mac256_1 = crypto:hmac_final(Case4Ctx256_2), - ?line Case4Mac256_2 = crypto:sha256_mac(Case4Key, Case4Data), - ?line Case4Mac256_3 = crypto:hmac(sha256, Case4Key, Case4Data), - ?line m(Case4Exp256, Case4Mac256_1), - ?line m(Case4Exp256, Case4Mac256_2), - ?line m(Case4Exp256, Case4Mac256_3), - - ?line Case4Ctx384 = crypto:hmac_init(sha384, Case4Key), - ?line Case4Ctx384_2 = crypto:hmac_update(Case4Ctx384, Case4Data), - ?line Case4Mac384_1 = crypto:hmac_final(Case4Ctx384_2), - ?line Case4Mac384_2 = crypto:sha384_mac(Case4Key, Case4Data), - ?line Case4Mac384_3 = crypto:hmac(sha384, Case4Key, Case4Data), - ?line m(Case4Exp384, Case4Mac384_1), - ?line m(Case4Exp384, Case4Mac384_2), - ?line m(Case4Exp384, Case4Mac384_3), - - ?line Case4Ctx512 = crypto:hmac_init(sha512, Case4Key), - ?line Case4Ctx512_2 = crypto:hmac_update(Case4Ctx512, Case4Data), - ?line Case4Mac512_1 = crypto:hmac_final(Case4Ctx512_2), - ?line Case4Mac512_2 = crypto:sha512_mac(Case4Key, Case4Data), - ?line Case4Mac512_3 = crypto:hmac(sha512, Case4Key, Case4Data), - ?line m(Case4Exp512, Case4Mac512_1), - ?line m(Case4Exp512, Case4Mac512_2), - ?line m(Case4Exp512, Case4Mac512_3), - - %% Test Case 5 - Case5Key = binary:copy(<<16#0c>>, 20), - Case5Data = <<"Test With Truncation">>, - Case5Exp224 = hexstr2bin("0e2aea68a90c8d37c988bcdb9fca6fa8"), - Case5Exp256 = hexstr2bin("a3b6167473100ee06e0c796c2955552b"), - Case5Exp384 = hexstr2bin("3abf34c3503b2a23a46efc619baef897"), - Case5Exp512 = hexstr2bin("415fad6271580a531d4179bc891d87a6"), - - ?line Case5Ctx224 = crypto:hmac_init(sha224, Case5Key), - ?line Case5Ctx224_2 = crypto:hmac_update(Case5Ctx224, Case5Data), - ?line Case5Mac224_1 = crypto:hmac_final_n(Case5Ctx224_2, 16), - ?line Case5Mac224_2 = crypto:sha224_mac(Case5Key, Case5Data, 16), - ?line Case5Mac224_3 = crypto:hmac(sha224, Case5Key, Case5Data, 16), - ?line m(Case5Exp224, Case5Mac224_1), - ?line m(Case5Exp224, Case5Mac224_2), - ?line m(Case5Exp224, Case5Mac224_3), - - ?line Case5Ctx256 = crypto:hmac_init(sha256, Case5Key), - ?line Case5Ctx256_2 = crypto:hmac_update(Case5Ctx256, Case5Data), - ?line Case5Mac256_1 = crypto:hmac_final_n(Case5Ctx256_2, 16), - ?line Case5Mac256_2 = crypto:sha256_mac(Case5Key, Case5Data, 16), - ?line Case5Mac256_3 = crypto:hmac(sha256, Case5Key, Case5Data, 16), - ?line m(Case5Exp256, Case5Mac256_1), - ?line m(Case5Exp256, Case5Mac256_2), - ?line m(Case5Exp256, Case5Mac256_3), - - ?line Case5Ctx384 = crypto:hmac_init(sha384, Case5Key), - ?line Case5Ctx384_2 = crypto:hmac_update(Case5Ctx384, Case5Data), - ?line Case5Mac384_1 = crypto:hmac_final_n(Case5Ctx384_2, 16), - ?line Case5Mac384_2 = crypto:sha384_mac(Case5Key, Case5Data, 16), - ?line Case5Mac384_3 = crypto:hmac(sha384, Case5Key, Case5Data, 16), - ?line m(Case5Exp384, Case5Mac384_1), - ?line m(Case5Exp384, Case5Mac384_2), - ?line m(Case5Exp384, Case5Mac384_3), - - ?line Case5Ctx512 = crypto:hmac_init(sha512, Case5Key), - ?line Case5Ctx512_2 = crypto:hmac_update(Case5Ctx512, Case5Data), - ?line Case5Mac512_1 = crypto:hmac_final_n(Case5Ctx512_2, 16), - ?line Case5Mac512_2 = crypto:sha512_mac(Case5Key, Case5Data, 16), - ?line Case5Mac512_3 = crypto:hmac(sha512, Case5Key, Case5Data, 16), - ?line m(Case5Exp512, Case5Mac512_1), - ?line m(Case5Exp512, Case5Mac512_2), - ?line m(Case5Exp512, Case5Mac512_3), - - %% Test Case 6 - Case6Key = binary:copy(<<16#aa>>, 131), - Case6Data = <<"Test Using Larger Than Block-Size Key - Hash Key First">>, - Case6Exp224 = hexstr2bin("95e9a0db962095adaebe9b2d6f0dbce2" - "d499f112f2d2b7273fa6870e"), - Case6Exp256 = hexstr2bin("60e431591ee0b67f0d8a26aacbf5b77f" - "8e0bc6213728c5140546040f0ee37f54"), - Case6Exp384 = hexstr2bin("4ece084485813e9088d2c63a041bc5b4" - "4f9ef1012a2b588f3cd11f05033ac4c6" - "0c2ef6ab4030fe8296248df163f44952"), - Case6Exp512 = hexstr2bin("80b24263c7c1a3ebb71493c1dd7be8b4" - "9b46d1f41b4aeec1121b013783f8f352" - "6b56d037e05f2598bd0fd2215d6a1e52" - "95e64f73f63f0aec8b915a985d786598"), - - ?line Case6Ctx224 = crypto:hmac_init(sha224, Case6Key), - ?line Case6Ctx224_2 = crypto:hmac_update(Case6Ctx224, Case6Data), - ?line Case6Mac224_1 = crypto:hmac_final(Case6Ctx224_2), - ?line Case6Mac224_2 = crypto:sha224_mac(Case6Key, Case6Data), - ?line Case6Mac224_3 = crypto:hmac(sha224, Case6Key, Case6Data), - ?line m(Case6Exp224, Case6Mac224_1), - ?line m(Case6Exp224, Case6Mac224_2), - ?line m(Case6Exp224, Case6Mac224_3), - - ?line Case6Ctx256 = crypto:hmac_init(sha256, Case6Key), - ?line Case6Ctx256_2 = crypto:hmac_update(Case6Ctx256, Case6Data), - ?line Case6Mac256_1 = crypto:hmac_final(Case6Ctx256_2), - ?line Case6Mac256_2 = crypto:sha256_mac(Case6Key, Case6Data), - ?line Case6Mac256_3 = crypto:hmac(sha256, Case6Key, Case6Data), - ?line m(Case6Exp256, Case6Mac256_1), - ?line m(Case6Exp256, Case6Mac256_2), - ?line m(Case6Exp256, Case6Mac256_3), - - ?line Case6Ctx384 = crypto:hmac_init(sha384, Case6Key), - ?line Case6Ctx384_2 = crypto:hmac_update(Case6Ctx384, Case6Data), - ?line Case6Mac384_1 = crypto:hmac_final(Case6Ctx384_2), - ?line Case6Mac384_2 = crypto:sha384_mac(Case6Key, Case6Data), - ?line Case6Mac384_3 = crypto:hmac(sha384, Case6Key, Case6Data), - ?line m(Case6Exp384, Case6Mac384_1), - ?line m(Case6Exp384, Case6Mac384_2), - ?line m(Case6Exp384, Case6Mac384_3), - - ?line Case6Ctx512 = crypto:hmac_init(sha512, Case6Key), - ?line Case6Ctx512_2 = crypto:hmac_update(Case6Ctx512, Case6Data), - ?line Case6Mac512_1 = crypto:hmac_final(Case6Ctx512_2), - ?line Case6Mac512_2 = crypto:sha512_mac(Case6Key, Case6Data), - ?line Case6Mac512_3 = crypto:hmac(sha512, Case6Key, Case6Data), - ?line m(Case6Exp512, Case6Mac512_1), - ?line m(Case6Exp512, Case6Mac512_2), - ?line m(Case6Exp512, Case6Mac512_3), - +hmac_rfc4231_sha256(doc) -> + ["Generate an HMAC using crypto:sha256_mac, hmac, and hmac_init, hmac_update, and hmac_final. " + "Testvectors are take from RFC4231." ]; +hmac_rfc4231_sha256(suite) -> + []; +hmac_rfc4231_sha256(Config) when is_list(Config) -> + if_supported(sha256, fun() -> hmac_rfc4231_sha256_do() end). + +hmac_rfc4231_sha384(doc) -> + ["Generate an HMAC using crypto:sha384_mac, hmac, and hmac_init, hmac_update, and hmac_final. " + "Testvectors are take from RFC4231." ]; +hmac_rfc4231_sha384(suite) -> + []; +hmac_rfc4231_sha384(Config) when is_list(Config) -> + if_supported(sha384, fun() -> hmac_rfc4231_sha384_do() end). + +hmac_rfc4231_sha512(doc) -> + ["Generate an HMAC using crypto:sha512_mac, hmac, and hmac_init, hmac_update, and hmac_final. " + "Testvectors are take from RFC4231." ]; +hmac_rfc4231_sha512(suite) -> + []; +hmac_rfc4231_sha512(Config) when is_list(Config) -> + if_supported(sha512, fun() -> hmac_rfc4231_sha512_do() end). + +hmac_rfc4231_case(Hash, HashFun, case1, Exp) -> + %% Test 1 + Key = binary:copy(<<16#0b>>, 20), + Data = <<"Hi There">>, + hmac_rfc4231_case(Hash, HashFun, Key, Data, Exp); + +hmac_rfc4231_case(Hash, HashFun, case2, Exp) -> + %% Test 2 + Key = <<"Jefe">>, + Data = <<"what do ya want for nothing?">>, + hmac_rfc4231_case(Hash, HashFun, Key, Data, Exp); + +hmac_rfc4231_case(Hash, HashFun, case3, Exp) -> + %% Test 3 + Key = binary:copy(<<16#aa>>, 20), + Data = binary:copy(<<16#dd>>, 50), + hmac_rfc4231_case(Hash, HashFun, Key, Data, Exp); + +hmac_rfc4231_case(Hash, HashFun, case4, Exp) -> + %% Test 4 + Key = list_to_binary(lists:seq(1, 16#19)), + Data = binary:copy(<<16#cd>>, 50), + hmac_rfc4231_case(Hash, HashFun, Key, Data, Exp); + +hmac_rfc4231_case(Hash, HashFun, case5, Exp) -> + %% Test 5 + Key = binary:copy(<<16#0c>>, 20), + Data = <<"Test With Truncation">>, + hmac_rfc4231_case(Hash, HashFun, Key, Data, 16, Exp); + +hmac_rfc4231_case(Hash, HashFun, case6, Exp) -> + %% Test 6 + Key = binary:copy(<<16#aa>>, 131), + Data = <<"Test Using Larger Than Block-Size Key - Hash Key First">>, + hmac_rfc4231_case(Hash, HashFun, Key, Data, Exp); + +hmac_rfc4231_case(Hash, HashFun, case7, Exp) -> %% Test Case 7 - Case7Key = binary:copy(<<16#aa>>, 131), - Case7Data = <<"This is a test using a larger than block-size key and a larger t", - "han block-size data. The key needs to be hashed before being use", - "d by the HMAC algorithm.">>, - Case7Exp224 = hexstr2bin("3a854166ac5d9f023f54d517d0b39dbd" - "946770db9c2b95c9f6f565d1"), - Case7Exp256 = hexstr2bin("9b09ffa71b942fcb27635fbcd5b0e944" - "bfdc63644f0713938a7f51535c3a35e2"), - Case7Exp384 = hexstr2bin("6617178e941f020d351e2f254e8fd32c" - "602420feb0b8fb9adccebb82461e99c5" - "a678cc31e799176d3860e6110c46523e"), - Case7Exp512 = hexstr2bin("e37b6a775dc87dbaa4dfa9f96e5e3ffd" - "debd71f8867289865df5a32d20cdc944" - "b6022cac3c4982b10d5eeb55c3e4de15" - "134676fb6de0446065c97440fa8c6a58"), - - ?line Case7Ctx224 = crypto:hmac_init(sha224, Case7Key), - ?line Case7Ctx224_2 = crypto:hmac_update(Case7Ctx224, Case7Data), - ?line Case7Mac224_1 = crypto:hmac_final(Case7Ctx224_2), - ?line Case7Mac224_2 = crypto:sha224_mac(Case7Key, Case7Data), - ?line Case7Mac224_3 = crypto:hmac(sha224, Case7Key, Case7Data), - ?line m(Case7Exp224, Case7Mac224_1), - ?line m(Case7Exp224, Case7Mac224_2), - ?line m(Case7Exp224, Case7Mac224_3), - - ?line Case7Ctx256 = crypto:hmac_init(sha256, Case7Key), - ?line Case7Ctx256_2 = crypto:hmac_update(Case7Ctx256, Case7Data), - ?line Case7Mac256_1 = crypto:hmac_final(Case7Ctx256_2), - ?line Case7Mac256_2 = crypto:sha256_mac(Case7Key, Case7Data), - ?line Case7Mac256_3 = crypto:hmac(sha256, Case7Key, Case7Data), - ?line m(Case7Exp256, Case7Mac256_1), - ?line m(Case7Exp256, Case7Mac256_2), - ?line m(Case7Exp256, Case7Mac256_3), - - ?line Case7Ctx384 = crypto:hmac_init(sha384, Case7Key), - ?line Case7Ctx384_2 = crypto:hmac_update(Case7Ctx384, Case7Data), - ?line Case7Mac384_1 = crypto:hmac_final(Case7Ctx384_2), - ?line Case7Mac384_2 = crypto:sha384_mac(Case7Key, Case7Data), - ?line Case7Mac384_3 = crypto:hmac(sha384, Case7Key, Case7Data), - ?line m(Case7Exp384, Case7Mac384_1), - ?line m(Case7Exp384, Case7Mac384_2), - ?line m(Case7Exp384, Case7Mac384_3), - - ?line Case7Ctx512 = crypto:hmac_init(sha512, Case7Key), - ?line Case7Ctx512_2 = crypto:hmac_update(Case7Ctx512, Case7Data), - ?line Case7Mac512_1 = crypto:hmac_final(Case7Ctx512_2), - ?line Case7Mac512_2 = crypto:sha512_mac(Case7Key, Case7Data), - ?line Case7Mac512_3 = crypto:hmac(sha512, Case7Key, Case7Data), - ?line m(Case7Exp512, Case7Mac512_1), - ?line m(Case7Exp512, Case7Mac512_2), - ?line m(Case7Exp512, Case7Mac512_3). + Key = binary:copy(<<16#aa>>, 131), + Data = <<"This is a test using a larger than block-size key and a larger t", + "han block-size data. The key needs to be hashed before being use", + "d by the HMAC algorithm.">>, + hmac_rfc4231_case(Hash, HashFun, Key, Data, Exp). + +hmac_rfc4231_case(Hash, HashFun, Key, Data, Exp) -> + ?line Ctx = crypto:hmac_init(Hash, Key), + ?line Ctx2 = crypto:hmac_update(Ctx, Data), + ?line Mac1 = crypto:hmac_final(Ctx2), + ?line Mac2 = crypto:HashFun(Key, Data), + ?line Mac3 = crypto:hmac(Hash, Key, Data), + ?line m(Exp, Mac1), + ?line m(Exp, Mac2), + ?line m(Exp, Mac3). + +hmac_rfc4231_case(Hash, HashFun, Key, Data, Trunc, Exp) -> + ?line Ctx = crypto:hmac_init(Hash, Key), + ?line Ctx2 = crypto:hmac_update(Ctx, Data), + ?line Mac1 = crypto:hmac_final_n(Ctx2, Trunc), + ?line Mac2 = crypto:HashFun(Key, Data, Trunc), + ?line Mac3 = crypto:hmac(Hash, Key, Data, Trunc), + ?line m(Exp, Mac1), + ?line m(Exp, Mac2), + ?line m(Exp, Mac3). + +hmac_rfc4231_sha224_do() -> + Case1 = hexstr2bin("896fb1128abbdf196832107cd49df33f" + "47b4b1169912ba4f53684b22"), + Case2 = hexstr2bin("a30e01098bc6dbbf45690f3a7e9e6d0f" + "8bbea2a39e6148008fd05e44"), + Case3 = hexstr2bin("7fb3cb3588c6c1f6ffa9694d7d6ad264" + "9365b0c1f65d69d1ec8333ea"), + Case4 = hexstr2bin("6c11506874013cac6a2abc1bb382627c" + "ec6a90d86efc012de7afec5a"), + Case5 = hexstr2bin("0e2aea68a90c8d37c988bcdb9fca6fa8"), + Case6 = hexstr2bin("95e9a0db962095adaebe9b2d6f0dbce2" + "d499f112f2d2b7273fa6870e"), + Case7 = hexstr2bin("3a854166ac5d9f023f54d517d0b39dbd" + "946770db9c2b95c9f6f565d1"), + hmac_rfc4231_cases_do(sha224, sha224_mac, [Case1, Case2, Case3, Case4, Case5, Case6, Case7]). + +hmac_rfc4231_sha256_do() -> + Case1 = hexstr2bin("b0344c61d8db38535ca8afceaf0bf12b" + "881dc200c9833da726e9376c2e32cff7"), + Case2 = hexstr2bin("5bdcc146bf60754e6a042426089575c7" + "5a003f089d2739839dec58b964ec3843"), + Case3 = hexstr2bin("773ea91e36800e46854db8ebd09181a7" + "2959098b3ef8c122d9635514ced565fe"), + Case4 = hexstr2bin("82558a389a443c0ea4cc819899f2083a" + "85f0faa3e578f8077a2e3ff46729665b"), + Case5 = hexstr2bin("a3b6167473100ee06e0c796c2955552b"), + Case6 = hexstr2bin("60e431591ee0b67f0d8a26aacbf5b77f" + "8e0bc6213728c5140546040f0ee37f54"), + Case7 = hexstr2bin("9b09ffa71b942fcb27635fbcd5b0e944" + "bfdc63644f0713938a7f51535c3a35e2"), + hmac_rfc4231_cases_do(sha256, sha256_mac, [Case1, Case2, Case3, Case4, Case5, Case6, Case7]). + +hmac_rfc4231_sha384_do() -> + Case1 = hexstr2bin("afd03944d84895626b0825f4ab46907f" + "15f9dadbe4101ec682aa034c7cebc59c" + "faea9ea9076ede7f4af152e8b2fa9cb6"), + Case2 = hexstr2bin("af45d2e376484031617f78d2b58a6b1b" + "9c7ef464f5a01b47e42ec3736322445e" + "8e2240ca5e69e2c78b3239ecfab21649"), + Case3 = hexstr2bin("88062608d3e6ad8a0aa2ace014c8a86f" + "0aa635d947ac9febe83ef4e55966144b" + "2a5ab39dc13814b94e3ab6e101a34f27"), + Case4 = hexstr2bin("3e8a69b7783c25851933ab6290af6ca7" + "7a9981480850009cc5577c6e1f573b4e" + "6801dd23c4a7d679ccf8a386c674cffb"), + Case5 = hexstr2bin("3abf34c3503b2a23a46efc619baef897"), + Case6 = hexstr2bin("4ece084485813e9088d2c63a041bc5b4" + "4f9ef1012a2b588f3cd11f05033ac4c6" + "0c2ef6ab4030fe8296248df163f44952"), + Case7 = hexstr2bin("6617178e941f020d351e2f254e8fd32c" + "602420feb0b8fb9adccebb82461e99c5" + "a678cc31e799176d3860e6110c46523e"), + hmac_rfc4231_cases_do(sha384, sha384_mac, [Case1, Case2, Case3, Case4, Case5, Case6, Case7]). + +hmac_rfc4231_sha512_do() -> + Case1 = hexstr2bin("87aa7cdea5ef619d4ff0b4241a1d6cb0" + "2379f4e2ce4ec2787ad0b30545e17cde" + "daa833b7d6b8a702038b274eaea3f4e4" + "be9d914eeb61f1702e696c203a126854"), + Case2 = hexstr2bin("164b7a7bfcf819e2e395fbe73b56e0a3" + "87bd64222e831fd610270cd7ea250554" + "9758bf75c05a994a6d034f65f8f0e6fd" + "caeab1a34d4a6b4b636e070a38bce737"), + Case3 = hexstr2bin("fa73b0089d56a284efb0f0756c890be9" + "b1b5dbdd8ee81a3655f83e33b2279d39" + "bf3e848279a722c806b485a47e67c807" + "b946a337bee8942674278859e13292fb"), + Case4 = hexstr2bin("b0ba465637458c6990e5a8c5f61d4af7" + "e576d97ff94b872de76f8050361ee3db" + "a91ca5c11aa25eb4d679275cc5788063" + "a5f19741120c4f2de2adebeb10a298dd"), + Case5 = hexstr2bin("415fad6271580a531d4179bc891d87a6"), + Case6 = hexstr2bin("80b24263c7c1a3ebb71493c1dd7be8b4" + "9b46d1f41b4aeec1121b013783f8f352" + "6b56d037e05f2598bd0fd2215d6a1e52" + "95e64f73f63f0aec8b915a985d786598"), + Case7 = hexstr2bin("e37b6a775dc87dbaa4dfa9f96e5e3ffd" + "debd71f8867289865df5a32d20cdc944" + "b6022cac3c4982b10d5eeb55c3e4de15" + "134676fb6de0446065c97440fa8c6a58"), + hmac_rfc4231_cases_do(sha512, sha512_mac, [Case1, Case2, Case3, Case4, Case5, Case6, Case7]). + +hmac_rfc4231_cases_do(Hash, HashFun, CasesData) -> + hmac_rfc4231_cases_do(Hash, HashFun, [case1, case2, case3, case4, case5, case6, case7], CasesData). + +hmac_rfc4231_cases_do(_Hash, _HashFun, _, []) -> + ok; +hmac_rfc4231_cases_do(Hash, HashFun, [C|Cases], [D|CasesData]) -> + hmac_rfc4231_case(Hash, HashFun, C, D), + hmac_rfc4231_cases_do(Hash, HashFun, Cases, CasesData). hmac_update_md5_io(doc) -> ["Generate an MD5 HMAC using hmac_init, hmac_update, and hmac_final. " @@ -1025,7 +855,7 @@ sha256(doc) -> sha256(suite) -> []; sha256(Config) when is_list(Config) -> - if_098(fun() -> sha256_do() end). + if_supported(sha256, fun() -> sha256_do() end). sha256_do() -> ?line m(crypto:sha256("abc"), @@ -1044,7 +874,7 @@ sha256_update(doc) -> sha256_update(suite) -> []; sha256_update(Config) when is_list(Config) -> - if_098(fun() -> sha256_update_do() end). + if_supported(sha256, fun() -> sha256_update_do() end). sha256_update_do() -> ?line Ctx = crypto:sha256_init(), @@ -1063,7 +893,7 @@ sha512(doc) -> sha512(suite) -> []; sha512(Config) when is_list(Config) -> - if_098(fun() -> sha512_do() end). + if_supported(sha512, fun() -> sha512_do() end). sha512_do() -> ?line m(crypto:sha512("abc"), @@ -1084,7 +914,7 @@ sha512_update(doc) -> sha512_update(suite) -> []; sha512_update(Config) when is_list(Config) -> - if_098(fun() -> sha512_update_do() end). + if_supported(sha512, fun() -> sha512_update_do() end). sha512_update_do() -> ?line Ctx = crypto:sha512_init(), @@ -2017,6 +1847,161 @@ dh(Config) when is_list(Config) -> exit(Pid, kill) end. +srp3(doc) -> + ["SRP-3 test vectors generated by http://srp.stanford.edu/demo/demo.html"]; +srp3(suite) -> []; +srp3(Config) when is_list(Config) -> + Username = <<"alice">>, + Password = <<"password123">>, + Salt = hexstr2bin("2857827A19266A1F2BC6"), + Prime = hexstr2bin("EEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C" + "9C256576D674DF7496EA81D3383B4813D692C6E0E0D5D8E250B98BE4" + "8E495C1D6089DAD15DC7D7B46154D6B6CE8EF4AD69B15D4982559B29" + "7BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA9A" + "FD5138FE8376435B9FC61D2FC0EB06E3"), + Generator = <<2>>, + Version = '3', + Scrambler = hexstr2bin("02E2476A"), + + %% X = hexstr2bin("96E54AB0CD4C5123EDCFA4A1502918AAD3C9E2A8"), + Verifier = hexstr2bin("96EB5F13621D911AA1CA405DE9C64217D4108EEEECAFFE500034FE0E" + "C031E42C8714667C161BCE0E7996F7DDE1B63824C130D2D7286C08C0" + "49758420735961347112AE102A3F23B3F687F8FEE0DF2BFAF933C608" + "D6FE5B5EEE3116FE54016E065BF8E8C9FDBBC08719231AC215149140" + "519E8FDD9AA4F410C28A58AF42974D2D"), + ClientPrivate = hexstr2bin("6411DE75538BED8170677D577D0608F39112BC95B503C447EB6AC945" + "49C75C7B"), + ServerPrivate = hexstr2bin("85E44A6F694DBE676145DB245A045CD37C99F05C562C7840A31F270D" + "9AADCF8B"), + ClientPublic = hexstr2bin("B22B1FFA2244B8CB94F3A9080F419CAEAB0DBA93EA1965B5E84587EE" + "55C79E7A118865DC59B9D0353362C2A8261E7C1B0D221A0E233C2AD1" + "640DACBB8664CBC9733EAC392DA7800142860380C3FC573C3C064329" + "CF54063FD114C7210E9CB3A611EA8002B1844B698F930D95D143899B" + "948A090E0C25938E5F84067D1883DC63"), + ServerPublic = hexstr2bin("93A8C4D8B7F7395ADCFD4ABA37B015124513D3F37B3E85EB23064BE5" + "F53C0AE32FFB9D8C0AA0DCFFA74D632DD67DEBB5C35AAE9812286CC8" + "C43CC176ECBC6D3F447594D9554E995B2509127BF88FADDDA4982D03" + "8EC3001320712D3B1269308CE70F319B2295FA57674F03A2D993CFB1" + "F84C35B7D0C012FA73CD4C8F7D5A71C7"), + + SessionKey = hexstr2bin("C29A986C4D521BBC66428ED11D994CD7431574A6184B83CDCC345092" + "791E75748A1D38CAC4BD14760F0D2694B711236419240FF2F172454C" + "46ABF4FF39498DAFDD2C82924F7D7BD76CDFCE688C77D93F18A65409" + "9176A9192615DC0277AE7C12F1F6A7F6563FCA11675D809AF578BDE5" + "2B51E05D440B63099A017A0B45044801"), + UserPassHash = crypto:sha([Salt, crypto:sha([Username, <<$:>>, Password])]), + Verifier = crypto:mod_exp_prime(Generator, UserPassHash, Prime), + ClientPublic = crypto:mod_exp_prime(Generator, ClientPrivate, Prime), + + {ClientPublic, ClientPrivate} = crypto:srp_generate_key(Generator, Prime, Version, ClientPrivate), + {ServerPublic, ServerPrivate} = crypto:srp_generate_key(Verifier, Generator, Prime, Version, ServerPrivate), + SessionKey = crypto:srp_compute_key(UserPassHash, Prime, Generator, ClientPublic, + ClientPrivate, ServerPublic, Version, Scrambler), + SessionKey = crypto:srp_compute_key(Verifier, Prime, ClientPublic, + ServerPublic, ServerPrivate, Version, Scrambler). + +srp6(doc) -> + ["SRP-6 test vectors generated by http://srp.stanford.edu/demo/demo.html"]; +srp6(suite) -> []; +srp6(Config) when is_list(Config) -> + Username = <<"alice">>, + Password = <<"password123">>, + Salt = hexstr2bin("2857827A19266A1F2BC6"), + Prime = hexstr2bin("EEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C" + "9C256576D674DF7496EA81D3383B4813D692C6E0E0D5D8E250B98BE4" + "8E495C1D6089DAD15DC7D7B46154D6B6CE8EF4AD69B15D4982559B29" + "7BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA9A" + "FD5138FE8376435B9FC61D2FC0EB06E3"), + Generator = <<2>>, + Version = '6', + Scrambler = hexstr2bin("0A2534C0BF52A0DA9001EEC62CF2A546AB0908A7"), + Verifier = hexstr2bin("96EB5F13621D911AA1CA405DE9C64217D4108EEEECAFFE500034FE0E" + "C031E42C8714667C161BCE0E7996F7DDE1B63824C130D2D7286C08C0" + "49758420735961347112AE102A3F23B3F687F8FEE0DF2BFAF933C608" + "D6FE5B5EEE3116FE54016E065BF8E8C9FDBBC08719231AC215149140" + "519E8FDD9AA4F410C28A58AF42974D2D"), + ClientPrivate = hexstr2bin("6411DE75538BED8170677D577D0608F39112BC95B503C447EB6AC945" + "49C75C7B"), + ServerPrivate = hexstr2bin("85E44A6F694DBE676145DB245A045CD37C99F05C562C7840A31F270D" + "9AADCF8B"), + ClientPublic = hexstr2bin("B22B1FFA2244B8CB94F3A9080F419CAEAB0DBA93EA1965B5E84587EE" + "55C79E7A118865DC59B9D0353362C2A8261E7C1B0D221A0E233C2AD1" + "640DACBB8664CBC9733EAC392DA7800142860380C3FC573C3C064329" + "CF54063FD114C7210E9CB3A611EA8002B1844B698F930D95D143899B" + "948A090E0C25938E5F84067D1883DC63"), + ServerPublic = hexstr2bin("D2D07845CE7ECDB9845DD36B10ACD3598CC29049DE9F467F84CE16B6" + "D97A6DC567AF8B0F9FEDF74962400AD5C357951E64E67B641246F264" + "C8DE6D9A72E554D6C8D3194548780A0C438A0FCC509CA88A14AA1DEB" + "C0F09E4B37A965D1545DB4AD361346F3189B0EA569C06D326C4E4797" + "9E381C748293B7C0591BE0BE419E053E"), + + SessionKey = hexstr2bin("19D22C19612874EBF1F2581F8EFCFDC44C6FDA3B87B0A73823D7E962" + "554295D4E48D3A336523ADBDDD0EC8FB0F02687109E97E01C17C93CC" + "7216F9CD8A4AC39F0429857D8D1023066614BDFCBCB89F59A0FEB81C" + "72E992AAD89095A84B6A5FADA152369AB1E350A03693BEF044DF3EDF" + "0C34741F4696C30E9F675D09F58ACBEB"), + UserPassHash = crypto:sha([Salt, crypto:sha([Username, <<$:>>, Password])]), + Verifier = crypto:mod_exp_prime(Generator, UserPassHash, Prime), + ClientPublic = crypto:mod_exp_prime(Generator, ClientPrivate, Prime), + + {ClientPublic, ClientPrivate} = crypto:srp_generate_key(Generator, Prime, Version, ClientPrivate), + {ServerPublic, ServerPrivate} = crypto:srp_generate_key(Verifier, Generator, Prime, Version, ServerPrivate), + SessionKey = crypto:srp_compute_key(UserPassHash, Prime, Generator, ClientPublic, + ClientPrivate, ServerPublic, Version, Scrambler), + SessionKey = crypto:srp_compute_key(Verifier, Prime, ClientPublic, + ServerPublic, ServerPrivate, Version, Scrambler). + +srp6a(doc) -> + ["SRP-6a test vectors from RFC5054."]; +srp6a(suite) -> []; +srp6a(Config) when is_list(Config) -> + Username = <<"alice">>, + Password = <<"password123">>, + Salt = hexstr2bin("BEB25379D1A8581EB5A727673A2441EE"), + Prime = hexstr2bin("EEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C" + "9C256576D674DF7496EA81D3383B4813D692C6E0E0D5D8E250B98BE4" + "8E495C1D6089DAD15DC7D7B46154D6B6CE8EF4AD69B15D4982559B29" + "7BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA9A" + "FD5138FE8376435B9FC61D2FC0EB06E3"), + Generator = <<2>>, + Version = '6a', + Scrambler = hexstr2bin("CE38B9593487DA98554ED47D70A7AE5F462EF019"), + Verifier = hexstr2bin("7E273DE8696FFC4F4E337D05B4B375BEB0DDE1569E8FA00A9886D812" + "9BADA1F1822223CA1A605B530E379BA4729FDC59F105B4787E5186F5" + "C671085A1447B52A48CF1970B4FB6F8400BBF4CEBFBB168152E08AB5" + "EA53D15C1AFF87B2B9DA6E04E058AD51CC72BFC9033B564E26480D78" + "E955A5E29E7AB245DB2BE315E2099AFB"), + ClientPrivate = hexstr2bin("60975527035CF2AD1989806F0407210BC81EDC04E2762A56AFD529DD" + "DA2D4393"), + ServerPrivate = hexstr2bin("E487CB59D31AC550471E81F00F6928E01DDA08E974A004F49E61F5D1" + "05284D20"), + ClientPublic = hexstr2bin("61D5E490F6F1B79547B0704C436F523DD0E560F0C64115BB72557EC4" + "4352E8903211C04692272D8B2D1A5358A2CF1B6E0BFCF99F921530EC" + "8E39356179EAE45E42BA92AEACED825171E1E8B9AF6D9C03E1327F44" + "BE087EF06530E69F66615261EEF54073CA11CF5858F0EDFDFE15EFEA" + "B349EF5D76988A3672FAC47B0769447B"), + ServerPublic = hexstr2bin("BD0C61512C692C0CB6D041FA01BB152D4916A1E77AF46AE105393011" + "BAF38964DC46A0670DD125B95A981652236F99D9B681CBF87837EC99" + "6C6DA04453728610D0C6DDB58B318885D7D82C7F8DEB75CE7BD4FBAA" + "37089E6F9C6059F388838E7A00030B331EB76840910440B1B27AAEAE" + "EB4012B7D7665238A8E3FB004B117B58"), + + SessionKey = hexstr2bin("B0DC82BABCF30674AE450C0287745E7990A3381F63B387AAF271A10D" + "233861E359B48220F7C4693C9AE12B0A6F67809F0876E2D013800D6C" + "41BB59B6D5979B5C00A172B4A2A5903A0BDCAF8A709585EB2AFAFA8F" + "3499B200210DCC1F10EB33943CD67FC88A2F39A4BE5BEC4EC0A3212D" + "C346D7E474B29EDE8A469FFECA686E5A"), + UserPassHash = crypto:sha([Salt, crypto:sha([Username, <<$:>>, Password])]), + Verifier = crypto:mod_exp_prime(Generator, UserPassHash, Prime), + + {ClientPublic, ClientPrivate} = crypto:srp_generate_key(Generator, Prime, Version, ClientPrivate), + {ServerPublic, ServerPrivate} = crypto:srp_generate_key(Verifier, Generator, Prime, Version, ServerPrivate), + + SessionKey = crypto:srp_compute_key(UserPassHash, Prime, Generator, ClientPublic, + ClientPrivate, ServerPublic, Version, Scrambler), + SessionKey = crypto:srp_compute_key(Verifier, Prime, ClientPublic, + ServerPublic, ServerPrivate, Version, Scrambler). + %% %% exor_test(doc) -> @@ -2120,8 +2105,8 @@ worker_loop(N, Config) -> aes_cfb, aes_cbc, des_cbc_iter, rand_uniform_test, strong_rand_test, rsa_verify_test, exor_test, rc4_test, rc4_stream_test, mod_exp_test, hmac_update_md5, hmac_update_sha, hmac_update_sha256, hmac_update_sha512, - hmac_rfc2202, hmac_rfc4231, - aes_ctr_stream }, + hmac_rfc2202, hmac_rfc4231_sha224, hmac_rfc4231_sha256, hmac_rfc4231_sha384, + hmac_rfc4231_sha512, aes_ctr_stream }, F = element(random:uniform(size(Funcs)),Funcs), %%io:format("worker ~p calling ~p\n",[self(),F]), @@ -2256,10 +2241,10 @@ openssl_version() -> undefined end. -if_098(Fun) -> - case openssl_version() of - V when V < 16#908000 -> - {skipped,"OpenSSL version too old"}; +if_supported(Algorithm, Fun) -> + case proplists:get_bool(Algorithm, crypto:algorithms()) of + true -> + Fun(); _ -> - Fun() + {skipped, io:format("~s not spupported", [Algorithm])} end. diff --git a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl index 86618a4915..ca6b403ac9 100644 --- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl @@ -261,8 +261,8 @@ compile_and_store(Files, #analysis_state{codeserver = CServer, dict:new(), NewFiles), check_for_duplicate_modules(ModDict); false -> - Msg = io_lib:format("Could not scan the following file(s): ~p", - [lists:flatten(Failed)]), + Msg = io_lib:format("Could not scan the following file(s):~n~s", + [[Reason || {_Filename, Reason} <- Failed]]), exit({error, Msg}) end, {T2, _} = statistics(runtime), diff --git a/lib/odbc/c_src/odbcserver.c b/lib/odbc/c_src/odbcserver.c index a6b3de6e48..5730e20774 100644 --- a/lib/odbc/c_src/odbcserver.c +++ b/lib/odbc/c_src/odbcserver.c @@ -1222,7 +1222,7 @@ static db_result_msg encode_out_params(db_state *state, (column.type.strlen_or_indptr_array[j])); break; case SQL_C_SLONG: - ei_x_encode_long(&dynamic_buffer(state), ((long*)values)[j]); + ei_x_encode_long(&dynamic_buffer(state), ((SQLINTEGER*)values)[j]); break; case SQL_C_DOUBLE: ei_x_encode_double(&dynamic_buffer(state), diff --git a/lib/odbc/test/odbc_query_SUITE.erl b/lib/odbc/test/odbc_query_SUITE.erl index 062373afa0..56550bfaa6 100644 --- a/lib/odbc/test/odbc_query_SUITE.erl +++ b/lib/odbc/test/odbc_query_SUITE.erl @@ -43,7 +43,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> case odbc_test_lib:odbc_check() of ok -> - [sql_query, next, {group, scrollable_cursors}, select_count, + [stored_proc, sql_query, next, {group, scrollable_cursors}, select_count, select_next, select_relative, select_absolute, create_table_twice, delete_table_twice, duplicate_key, not_connection_owner, no_result_set, query_error, @@ -172,6 +172,26 @@ end_per_testcase(_Case, Config) -> %%------------------------------------------------------------------------- %% Test cases starts here. %%------------------------------------------------------------------------- +stored_proc(doc)-> + ["Test stored proc with OUT param"]; +stored_proc(suite) -> []; +stored_proc(Config) when is_list(Config) -> + case ?RDBMS of + X when X == oracle; X == postgres-> + Ref = ?config(connection_ref, Config), + {updated, _} = + odbc:sql_query(Ref, + ?RDBMS:stored_proc_integer_out()), + Result = ?RDBMS:query_result(), + Result = + ?RDBMS:param_query(Ref), + {updated, _} = + odbc:sql_query(Ref, ?RDBMS:drop_proc()), + ok; + _ -> + {skip, "stored proc not yet supported"} + end. + sql_query(doc)-> ["Test the common cases"]; sql_query(suite) -> []; diff --git a/lib/odbc/test/oracle.erl b/lib/odbc/test/oracle.erl index d74863d8c1..95cf7155dc 100644 --- a/lib/odbc/test/oracle.erl +++ b/lib/odbc/test/oracle.erl @@ -240,3 +240,30 @@ describe_floating() -> {ok,[{"F",sql_double},{"R",sql_double},{"D",sql_double}]}. describe_dec_num() -> {ok,[{"MYDEC",{sql_decimal,9,3}},{"MYNUM",{sql_decimal,9,2}}]}. + +%------------------------------------------------------------------------- +drop_proc() -> + "drop procedure test_proc1;". + +stored_proc_integer_out() -> + "create or replace PROCEDURE test_proc1(" ++ + "int_a OUT NUMBER, " ++ + "int_b OUT NUMBER) " ++ + "is " ++ + "begin " ++ + " int_a := 123; " ++ + " int_b := 456; " ++ + "exception " ++ + "WHEN NO_DATA_FOUND THEN " ++ + " int_a := 0; " ++ + " int_b := 0; " ++ + "end;". + +param_query(Ref) -> + odbc:param_query(Ref, "call test_proc1(?,?)", + [{sql_integer, out, [0]}, + {sql_integer, out, [0]}]). + + +query_result() -> + {executed, 2, [{123, 456}]}. diff --git a/lib/odbc/test/postgres.erl b/lib/odbc/test/postgres.erl index d564dbd5ff..0c1761b835 100644 --- a/lib/odbc/test/postgres.erl +++ b/lib/odbc/test/postgres.erl @@ -293,3 +293,42 @@ describe_dec_num() -> describe_timestamp() -> {ok, [{"field", sql_timestamp}]}. + +%------------------------------------------------------------------------- +drop_proc() -> + "drop function test_proc1(OUT integer, OUT integer);". + +stored_proc_integer_out() -> + "create or replace FUNCTION test_proc1(" ++ + "OUT int_a INTEGER, " ++ + "OUT int_b INTEGER) " ++ + "AS $$ " ++ + "BEGIN " ++ + " int_a := 123; " ++ + " int_b := 456; " ++ + "END " ++ + "$$ LANGUAGE plpgsql ". + +%% This does not test what you might think it is supposed to test. +%% Since the stored procedure has got 2 out parameters and no +%% in parameters it is of arity 0 as called below. +%% +%% The port program odbcserver.c will marshal these out parameters +%% and hand them to ODBC. The ODBC driver for postgres will +%% apparently not give a hoot about these out parameters and instead +%% return the result in a regular result select set. The port program +%% will assume it has the result in the out parameters and marshal +%% these as they are i.e as it itself had packed them, so they +%% come back unchanged. +%% +%% The real function result goes into the void but the code in odbcserver.c +%% that marshals out parameters returned from ODBC will be run +%% so that is what this test tests... +%% +param_query(Ref) -> + odbc:param_query(Ref, "select * from test_proc1()", + [{sql_integer, out, [111]}, + {sql_integer, out, [444]}]). + +query_result() -> + {executed, 2, [{111, 444}]}. diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl index 5e25f22a6f..97785ca7f8 100644 --- a/lib/reltool/src/reltool_server.erl +++ b/lib/reltool/src/reltool_server.erl @@ -973,7 +973,8 @@ refresh_app(#app{name = AppName, is_escript = IsEscript, active_dir = ActiveDir, label = OptLabel, - mods = Mods} = App, + mods = Mods, + status = AppStatus} = App, Force, Status) -> if @@ -993,6 +994,8 @@ refresh_app(#app{name = AppName, read_app_info(AppFile, AppFile, AppName, + ActiveDir, + AppStatus, DefaultVsn, Status), @@ -1064,9 +1067,11 @@ refresh_app(#app{name = AppName, missing_app_info(Vsn) -> #app_info{vsn = Vsn}. -read_app_info(_AppFileOrBin, _AppFile, erts, DefaultVsn, Status) -> +read_app_info(_AppFileOrBin, _AppFile, erts, _ActiveDir, _AppStatus, DefaultVsn, Status) -> {missing_app_info(DefaultVsn), Status}; -read_app_info(AppFileOrBin, AppFile, AppName, DefaultVsn, Status) -> +read_app_info(_AppFileOrBin, _AppFile, _AppName, undefined, missing, DefaultVsn, Status) -> + {missing_app_info(DefaultVsn), Status}; +read_app_info(AppFileOrBin, AppFile, AppName, _ActiveDir, _AppStatus, DefaultVsn, Status) -> EnoentText = file:format_error(enoent), case reltool_utils:prim_consult(AppFileOrBin) of {ok, [{application, AppName, Info}]} -> @@ -1080,9 +1085,9 @@ read_app_info(AppFileOrBin, AppFile, AppName, DefaultVsn, Status) -> Status)}; {error, Text} when Text =:= EnoentText -> {missing_app_info(DefaultVsn), - reltool_utils:add_warning("~w: Missing app file ~tp.", - [AppName,AppFile], - Status)}; + reltool_utils:add_warning("~w: Missing app file ~tp.", + [AppName,AppFile], + Status)}; {error, Text} -> {missing_app_info(DefaultVsn), reltool_utils:add_warning("~w: Cannot parse app file ~tp (~tp).", @@ -1773,13 +1778,15 @@ escripts_to_apps([Escript | Escripts], Apps, Status) -> get_vsn_from_dir(AppName,AppLabel), AppFileName = filename:join([Escript, FullName]), + Dir = filename:join([Escript, AppName]), {Info, StatusAcc2} = read_app_info(GetBin(), AppFileName, AppName, + Dir, + ok, DefaultVsn, Status), - Dir = filename:join([Escript, AppName]), {[{AppName, app, Dir, Info} | FileAcc], StatusAcc2}; E when E =:= Ext -> @@ -1979,20 +1986,27 @@ refresh_apps(ConfigApps, [New | NewApps], Acc, Force, Status) -> refresh_apps(_ConfigApps, [], Acc, _Force, Status) -> {lists:reverse(Acc), Status}. - ensure_app_info(#app{is_escript = IsEscript, active_dir = Dir, info = Info}, Status) when IsEscript=/=false -> %% Escript or application which is inlined in an escript {Info, Dir, Status}; -ensure_app_info(#app{name = Name, sorted_dirs = []}, _Status) -> - reltool_utils:throw_error("~w: : Missing application directory.",[Name]); +ensure_app_info(#app{name = Name, sorted_dirs = []} = App, Status) -> + Reason = "~w: Missing application directory.", + case App of + #app{incl_cond = exclude, status = missing, active_dir = Dir} -> + Status2 = reltool_utils:add_warning(Reason, [Name], Status), + {missing_app_info(""), Dir, Status2}; + _ -> + reltool_utils:throw_error(Reason, [Name]) + end; ensure_app_info(#app{name = Name, vsn = Vsn, use_selected_vsn = UseSelectedVsn, active_dir = ActiveDir, sorted_dirs = Dirs, - info = undefined}, + info = undefined, + status = AppStatus}, Status) -> ReadInfo = fun(Dir, StatusAcc) -> @@ -2000,7 +2014,8 @@ ensure_app_info(#app{name = Name, Ebin = filename:join([Dir, "ebin"]), DefaultVsn = get_vsn_from_dir(Name,Base), AppFile = filename:join([Ebin, atom_to_list(Name) ++ ".app"]), - read_app_info(AppFile, AppFile, Name, DefaultVsn, StatusAcc) + read_app_info(AppFile, AppFile, Name, ActiveDir, + AppStatus, DefaultVsn, StatusAcc) end, {AllInfo, Status2} = lists:mapfoldl(ReadInfo, Status, Dirs), AllVsns = [I#app_info.vsn || I <- AllInfo], diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl index 23338d9ecd..adea716e99 100644 --- a/lib/reltool/test/reltool_server_SUITE.erl +++ b/lib/reltool/test/reltool_server_SUITE.erl @@ -80,6 +80,8 @@ all() -> otp_9229_dupl_mod_exclude_app, otp_9229_dupl_mod_exclude_mod, dupl_mod_in_app_file, + include_non_existing_app, + exclude_non_existing_app, get_apps, get_mod, get_sys, @@ -1313,7 +1315,6 @@ otp_9229_dupl_mod_exclude_mod(Config) -> ok. - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Test that if a module is duplicated in a .app file, then a warning %% is produced, but target can still be created. @@ -1346,6 +1347,56 @@ dupl_mod_in_app_file(Config) -> ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Test that a reasonable error message is returned if an application +%% is missing +include_non_existing_app(_Config) -> + %% Configure the server + Sys = + {sys, + [ + {incl_cond,exclude}, + {app,foobar,[{incl_cond,include}]}, + {app,kernel,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]} + ]}, + + %% Generate target file + TargetDir = filename:join([?WORK_DIR, "target_include_non_existing_app"]), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, Sys}])]), + ?m({error,"foobar: Missing application directory."}, + reltool:get_status([{config, Sys}])), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Test that if a missing application is explicitly excluded a warning +%% should be issued. +exclude_non_existing_app(_Config) -> + %% Configure the server + Sys = + {sys, + [ + {incl_cond,exclude}, + {app,foobar,[{incl_cond,exclude}]}, + {app,kernel,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]} + ]}, + + %% Generate target file + TargetDir = filename:join([?WORK_DIR, "target_exclude_non_existing_app"]), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, Sys}])]), + ?m({ok,["foobar: Missing application directory."]}, + reltool:get_status([{config, Sys}])), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Test the interface used by the GUI: %% get_app %% get_apps diff --git a/lib/snmp/include/.gitignore b/lib/snmp/include/.gitignore new file mode 100644 index 0000000000..4db987f64a --- /dev/null +++ b/lib/snmp/include/.gitignore @@ -0,0 +1,4 @@ +*.hrl +!SNMPv2-TC.hrl +!snmp_tables.hrl +!snmp_types.hrl diff --git a/lib/snmp/priv/mibs/.gitignore b/lib/snmp/priv/mibs/.gitignore index e69de29bb2..a8a0dcec44 100644 --- a/lib/snmp/priv/mibs/.gitignore +++ b/lib/snmp/priv/mibs/.gitignore @@ -0,0 +1 @@ +*.bin diff --git a/lib/snmp/test/snmp_test_data/.gitignore b/lib/snmp/test/snmp_test_data/.gitignore new file mode 100644 index 0000000000..c7cffa7f6d --- /dev/null +++ b/lib/snmp/test/snmp_test_data/.gitignore @@ -0,0 +1,2 @@ +*.bin +*.hrl diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index e9858a3220..2501db858a 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -79,6 +79,7 @@ {keyfile, path()} | {password, string()} | {cacerts, [der_encoded()]} | {cacertfile, path()} | |{dh, der_encoded()} | {dhfile, path()} | {ciphers, ciphers()} | + {user_lookup_fun, {fun(), term()}}, {psk_identity, string()}, {srp_identity, {string(), string()}} | {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} | {reuse_session, fun()} {next_protocols_advertised, [binary()]} | {client_preferred_next_protocols, client | server, [binary()]} @@ -123,6 +124,7 @@ {key_exchange(), cipher(), hash()}</c></p> <p><c>key_exchange() = rsa | dhe_dss | dhe_rsa | dh_anon + | psk | dhe_psk | rsa_psk | srp_anon | srp_dss | srp_rsa </c></p> <p><c>cipher() = rc4_128 | des_cbc | '3des_ede_cbc' @@ -134,6 +136,9 @@ <p><c>prf_random() = client_random | server_random </c></p> + <p><c>srp_param_type() = srp_1024 | srp_1536 | srp_2048 | srp_3072 + | srp_4096 | srp_6144 | srp_8192</c></p> + </section> <section> @@ -179,13 +184,16 @@ <tag>{ciphers, ciphers()}</tag> <item>The cipher suites that should be supported. The function - <c>cipher_suites/0</c> can be used to find all available - ciphers. Additionally some anonymous cipher suites ({dh_anon, - rc4_128, md5}, {dh_anon, des_cbc, sha}, {dh_anon, - '3des_ede_cbc', sha}, {dh_anon, aes_128_cbc, sha}, {dh_anon, - aes_256_cbc, sha}) are supported for testing purposes and will - only work if explicitly enabled by this option and they are supported/enabled - by the peer also. + <c>cipher_suites/0</c> can be used to find all ciphers that are + supported by default. <c>cipher_suites(all)</c> may be called + to find all available cipher suites. + Pre-Shared Key (<url href="http://www.ietf.org/rfc/rfc4279.txt">RFC 4279</url> and + <url href="http://www.ietf.org/rfc/rfc5487.txt">RFC 5487</url>), + Secure Remote Password (<url href="http://www.ietf.org/rfc/rfc5054.txt">RFC 5054</url>) + and anonymous cipher suites only work if explicitly enabled by + this option and they are supported/enabled by the peer also. + Note that anonymous cipher suites are supported for testing purposes + only and should not be used when security matters. </item> <tag>{ssl_imp, new | old}</tag> @@ -195,10 +203,10 @@ <tag>{secure_renegotiate, boolean()}</tag> <item>Specifies if to reject renegotiation attempt that does - not live up to RFC 5746. By default secure_renegotiate is + not live up to <url href="http://www.ietf.org/rfc/rfc5746.txt">RFC 5746</url>. By default secure_renegotiate is set to false i.e. secure renegotiation will be used if possible but it will fallback to unsecure renegotiation if the peer - does not support RFC 5746. + does not support <url href="http://www.ietf.org/rfc/rfc5746.txt">RFC 5746</url>. </item> <tag>{depth, integer()}</tag> @@ -292,6 +300,32 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | <c>undefined</c> is specified (this is the default), the process will never go into hibernation. </item> + + <tag>{user_lookup_fun, {Lookupfun :: fun(), UserState :: term()}}</tag> + <item> + <p>The lookup fun should be defined as:</p> + <code> +fun(psk, PSKIdentity, UserState :: term()) -> + {ok, SharedSecret :: binary()} | error; +fun(srp, Username, UserState :: term()) -> + {ok, {SRPParams :: srp_param_type(), Salt :: binary(), DerivedKey :: binary()}} | error. + </code> + + <p>For Pre-Shared Key (PSK) cipher suites, the lookup fun will + be called by the client and server to determine the shared + secret. When called by the client, PSKIdentity will be set to the + hint presented by the server or undefined. When called by the + server, PSKIdentity is the identity presented by the client. + </p> + + <p>For Secure Remote Password (SRP), the fun will only be used by the server to obtain + parameters that it will use to generate its session keys. <c>DerivedKey</c> should be + derived according to <url href="http://tools.ietf.org/html/rfc2945#section-3"> RFC 2945</url> and + <url href="http://tools.ietf.org/html/rfc5054#section-2.4"> RFC 5054</url>: + <c>crypto:sha([Salt, crypto:sha([Username, <<$:>>, Password])]) </c> + </p> + </item> + </taglist> </section> @@ -334,6 +368,14 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | server does not support Next Protocol Negotiation the connection will be aborted if no default protocol is supplied.</p> </item> + + <tag>{psk_identity, string()}</tag> + <item>Specifies the identity the client presents to the server. The matching secret is + found by calling the user_look_fun. + </item> + <tag>{srp_identity, {Username :: string(), Password :: string()}</tag> + <item>Specifies the Username and Password to use to authenticate to the server. + </item> </taglist> </section> @@ -396,6 +438,10 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | using <c>negotiated_next_protocol/1</c> method. </item> + <tag>{psk_identity, string()}</tag> + <item>Specifies the server identity hint the server presents to the client. + </item> + </taglist> </section> @@ -427,13 +473,16 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | <name>cipher_suites(Type) -> ciphers()</name> <fsummary> Returns a list of supported cipher suites</fsummary> <type> - <v>Type = erlang | openssl</v> + <v>Type = erlang | openssl | all</v> </type> <desc><p>Returns a list of supported cipher suites. cipher_suites() is equivalent to cipher_suites(erlang). Type openssl is provided for backwards compatibility with - old ssl that used openssl. + old ssl that used openssl. cipher_suites(all) returns + all available cipher suites. The cipher suites not present + in cipher_suites(erlang) but in included in cipher_suites(all) + will not be used unless explicitly configured by the user. </p> </desc> </func> @@ -821,7 +870,6 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | </desc> </func> - </funcs> <section> diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile index e61f415c84..d3ba76d34e 100644 --- a/lib/ssl/src/Makefile +++ b/lib/ssl/src/Makefile @@ -50,6 +50,7 @@ MODULES= \ ssl_certificate\ ssl_certificate_db\ ssl_cipher \ + ssl_srp_primes \ ssl_connection \ ssl_connection_sup \ ssl_handshake \ @@ -65,7 +66,7 @@ MODULES= \ INTERNAL_HRL_FILES = \ ssl_alert.hrl ssl_cipher.hrl ssl_handshake.hrl ssl_internal.hrl \ - ssl_record.hrl + ssl_record.hrl ssl_srp.hrl ssl_srp_primes.hrl ERL_FILES= \ $(MODULES:%=%.erl) \ diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src index 897a097f73..5c34de905e 100644 --- a/lib/ssl/src/ssl.app.src +++ b/lib/ssl/src/ssl.app.src @@ -20,6 +20,7 @@ ssl_connection_sup, ssl_connection, ssl_cipher, + ssl_srp_primes, ssl_certificate_db, ssl_certificate, ssl_alert diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index fc06b5f1b0..70f3b4f050 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -37,6 +37,7 @@ -include("ssl_record.hrl"). -include("ssl_cipher.hrl"). -include("ssl_handshake.hrl"). +-include("ssl_srp_primes.hrl"). -include_lib("public_key/include/public_key.hrl"). @@ -65,6 +66,9 @@ {cert, Der::binary()} | {certfile, path()} | {key, Der::binary()} | {keyfile, path()} | {password, string()} | {cacerts, [Der::binary()]} | {cacertfile, path()} | {dh, Der::binary()} | {dhfile, path()} | + {user_lookup_fun, {fun(), InitialUserState::term()}} | + {psk_identity, string()} | + {srp_identity, {string(), string()}} | {ciphers, ciphers()} | {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} | {reuse_session, fun()} | {hibernate_after, integer()|undefined} | {next_protocols_advertised, list(binary())} | @@ -351,7 +355,7 @@ negotiated_next_protocol(#sslsocket{pid = Pid}) -> ssl_connection:negotiated_next_protocol(Pid). -spec cipher_suites() -> [erl_cipher_suite()]. --spec cipher_suites(erlang | openssl) -> [erl_cipher_suite()] | [string()]. +-spec cipher_suites(erlang | openssl | all ) -> [erl_cipher_suite()] | [string()]. %% Description: Returns all supported cipher suites. %%-------------------------------------------------------------------- @@ -364,8 +368,15 @@ cipher_suites(erlang) -> cipher_suites(openssl) -> Version = ssl_record:highest_protocol_version([]), - [ssl_cipher:openssl_suite_name(S) || S <- ssl_cipher:suites(Version)]. + [ssl_cipher:openssl_suite_name(S) || S <- ssl_cipher:suites(Version)]; +cipher_suites(all) -> + Version = ssl_record:highest_protocol_version([]), + Supported = ssl_cipher:suites(Version) + ++ ssl_cipher:anonymous_suites() + ++ ssl_cipher:psk_suites(Version) + ++ ssl_cipher:srp_suites(), + [suite_definition(S) || S <- Supported]. %%-------------------------------------------------------------------- -spec getopts(#sslsocket{}, [gen_tcp:option_name()]) -> {ok, [gen_tcp:option()]} | {error, reason()}. @@ -635,6 +646,9 @@ handle_options(Opts0, _Role) -> cacertfile = handle_option(cacertfile, Opts, CaCertDefault), dh = handle_option(dh, Opts, undefined), dhfile = handle_option(dhfile, Opts, undefined), + user_lookup_fun = handle_option(user_lookup_fun, Opts, undefined), + psk_identity = handle_option(psk_identity, Opts, undefined), + srp_identity = handle_option(srp_identity, Opts, undefined), ciphers = handle_option(ciphers, Opts, []), %% Server side option reuse_session = handle_option(reuse_session, Opts, ReuseSessionFun), @@ -654,7 +668,8 @@ handle_options(Opts0, _Role) -> SslOptions = [versions, verify, verify_fun, fail_if_no_peer_cert, verify_client_once, depth, cert, certfile, key, keyfile, - password, cacerts, cacertfile, dh, dhfile, ciphers, + password, cacerts, cacertfile, dh, dhfile, + user_lookup_fun, psk_identity, srp_identity, ciphers, reuse_session, reuse_sessions, ssl_imp, cb_info, renegotiate_at, secure_renegotiate, hibernate_after, erl_dist, next_protocols_advertised, @@ -756,6 +771,20 @@ validate_option(dhfile, Value) when is_binary(Value) -> Value; validate_option(dhfile, Value) when is_list(Value), Value =/= "" -> list_to_binary(Value); +validate_option(psk_identity, undefined) -> + undefined; +validate_option(psk_identity, Identity) + when is_list(Identity), Identity =/= "", length(Identity) =< 65535 -> + list_to_binary(Identity); +validate_option(user_lookup_fun, undefined) -> + undefined; +validate_option(user_lookup_fun, {Fun, _} = Value) when is_function(Fun, 3) -> + Value; +validate_option(srp_identity, undefined) -> + undefined; +validate_option(srp_identity, {Username, Password}) + when is_list(Username), is_list(Password), Username =/= "", length(Username) =< 255 -> + {list_to_binary(Username), list_to_binary(Password)}; validate_option(ciphers, Value) when is_list(Value) -> Version = ssl_record:highest_protocol_version([]), try cipher_suites(Version, Value) @@ -926,7 +955,10 @@ cipher_suites(Version, [{_,_,_}| _] = Ciphers0) -> Ciphers = [ssl_cipher:suite(C) || C <- Ciphers0], cipher_suites(Version, Ciphers); cipher_suites(Version, [Cipher0 | _] = Ciphers0) when is_binary(Cipher0) -> - Supported = ssl_cipher:suites(Version) ++ ssl_cipher:anonymous_suites(), + Supported = ssl_cipher:suites(Version) + ++ ssl_cipher:anonymous_suites() + ++ ssl_cipher:psk_suites(Version) + ++ ssl_cipher:srp_suites(), case [Cipher || Cipher <- Ciphers0, lists:member(Cipher, Supported)] of [] -> Supported; diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl index 94e95d3cd3..1810043dfb 100644 --- a/lib/ssl/src/ssl_alert.erl +++ b/lib/ssl/src/ssl_alert.erl @@ -112,4 +112,6 @@ description_txt(?INTERNAL_ERROR) -> description_txt(?USER_CANCELED) -> "user canceled"; description_txt(?NO_RENEGOTIATION) -> - "no renegotiation". + "no renegotiation"; +description_txt(?UNKNOWN_PSK_IDENTITY) -> + "unknown psk identity". diff --git a/lib/ssl/src/ssl_alert.hrl b/lib/ssl/src/ssl_alert.hrl index 92548edab7..2a8a91aefa 100644 --- a/lib/ssl/src/ssl_alert.hrl +++ b/lib/ssl/src/ssl_alert.hrl @@ -60,6 +60,7 @@ %% internal_error(80), %% user_canceled(90), %% no_renegotiation(100), +%% unknown_psk_identity(115), %% (255) %% } AlertDescription; @@ -87,6 +88,7 @@ -define(INTERNAL_ERROR, 80). -define(USER_CANCELED, 90). -define(NO_RENEGOTIATION, 100). +-define(UNKNOWN_PSK_IDENTITY, 115). -define(ALERT_REC(Level,Desc), #alert{level=Level,description=Desc,where={?FILE, ?LINE}}). diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl index d91e2a89a0..173c53709b 100644 --- a/lib/ssl/src/ssl_cipher.erl +++ b/lib/ssl/src/ssl_cipher.erl @@ -34,7 +34,7 @@ -export([security_parameters/3, suite_definition/1, decipher/5, cipher/5, - suite/1, suites/1, anonymous_suites/0, + suite/1, suites/1, anonymous_suites/0, psk_suites/1, srp_suites/0, openssl_suite/1, openssl_suite_name/1, filter/2, hash_algorithm/1, sign_algorithm/1]). @@ -215,6 +215,56 @@ anonymous_suites() -> ?TLS_DH_anon_WITH_AES_256_CBC_SHA256]. %%-------------------------------------------------------------------- +-spec psk_suites(tls_version()) -> [cipher_suite()]. +%% +%% Description: Returns a list of the PSK cipher suites, only supported +%% if explicitly set by user. +%%-------------------------------------------------------------------- +psk_suites({3, N}) -> + psk_suites(N); + +psk_suites(N) + when N >= 3 -> + psk_suites(0) ++ + [?TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, + ?TLS_RSA_PSK_WITH_AES_256_CBC_SHA384, + ?TLS_PSK_WITH_AES_256_CBC_SHA384, + ?TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, + ?TLS_RSA_PSK_WITH_AES_128_CBC_SHA256, + ?TLS_PSK_WITH_AES_128_CBC_SHA256]; + +psk_suites(_) -> + [?TLS_DHE_PSK_WITH_AES_256_CBC_SHA, + ?TLS_RSA_PSK_WITH_AES_256_CBC_SHA, + ?TLS_PSK_WITH_AES_256_CBC_SHA, + ?TLS_DHE_PSK_WITH_AES_128_CBC_SHA, + ?TLS_RSA_PSK_WITH_AES_128_CBC_SHA, + ?TLS_PSK_WITH_AES_128_CBC_SHA, + ?TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA, + ?TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA, + ?TLS_PSK_WITH_3DES_EDE_CBC_SHA, + ?TLS_DHE_PSK_WITH_RC4_128_SHA, + ?TLS_RSA_PSK_WITH_RC4_128_SHA, + ?TLS_PSK_WITH_RC4_128_SHA]. + +%%-------------------------------------------------------------------- +-spec srp_suites() -> [cipher_suite()]. +%% +%% Description: Returns a list of the SRP cipher suites, only supported +%% if explicitly set by user. +%%-------------------------------------------------------------------- +srp_suites() -> + [?TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA, + ?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA, + ?TLS_SRP_SHA_WITH_AES_128_CBC_SHA, + ?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA, + ?TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA, + ?TLS_SRP_SHA_WITH_AES_256_CBC_SHA, + ?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA, + ?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA]. + +%%-------------------------------------------------------------------- -spec suite_definition(cipher_suite()) -> int_cipher_suite(). %% %% Description: Return erlang cipher suite definition. @@ -297,7 +347,84 @@ suite_definition(?TLS_DH_anon_WITH_AES_256_CBC_SHA) -> suite_definition(?TLS_DH_anon_WITH_AES_128_CBC_SHA256) -> {dh_anon, aes_128_cbc, sha256, default_prf}; suite_definition(?TLS_DH_anon_WITH_AES_256_CBC_SHA256) -> - {dh_anon, aes_256_cbc, sha256, default_prf}. + {dh_anon, aes_256_cbc, sha256, default_prf}; + +%%% PSK Cipher Suites RFC 4279 + +suite_definition(?TLS_PSK_WITH_RC4_128_SHA) -> + {psk, rc4_128, sha, default_prf}; +suite_definition(?TLS_PSK_WITH_3DES_EDE_CBC_SHA) -> + {psk, '3des_ede_cbc', sha, default_prf}; +suite_definition(?TLS_PSK_WITH_AES_128_CBC_SHA) -> + {psk, aes_128_cbc, sha, default_prf}; +suite_definition(?TLS_PSK_WITH_AES_256_CBC_SHA) -> + {psk, aes_256_cbc, sha, default_prf}; +suite_definition(?TLS_DHE_PSK_WITH_RC4_128_SHA) -> + {dhe_psk, rc4_128, sha, default_prf}; +suite_definition(?TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA) -> + {dhe_psk, '3des_ede_cbc', sha, default_prf}; +suite_definition(?TLS_DHE_PSK_WITH_AES_128_CBC_SHA) -> + {dhe_psk, aes_128_cbc, sha, default_prf}; +suite_definition(?TLS_DHE_PSK_WITH_AES_256_CBC_SHA) -> + {dhe_psk, aes_256_cbc, sha, default_prf}; +suite_definition(?TLS_RSA_PSK_WITH_RC4_128_SHA) -> + {rsa_psk, rc4_128, sha, default_prf}; +suite_definition(?TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA) -> + {rsa_psk, '3des_ede_cbc', sha, default_prf}; +suite_definition(?TLS_RSA_PSK_WITH_AES_128_CBC_SHA) -> + {rsa_psk, aes_128_cbc, sha, default_prf}; +suite_definition(?TLS_RSA_PSK_WITH_AES_256_CBC_SHA) -> + {rsa_psk, aes_256_cbc, sha, default_prf}; + +%%% TLS 1.2 PSK Cipher Suites RFC 5487 + +suite_definition(?TLS_PSK_WITH_AES_128_CBC_SHA256) -> + {psk, aes_128_cbc, sha256, default_prf}; +suite_definition(?TLS_PSK_WITH_AES_256_CBC_SHA384) -> + {psk, aes_256_cbc, sha384, default_prf}; +suite_definition(?TLS_DHE_PSK_WITH_AES_128_CBC_SHA256) -> + {dhe_psk, aes_128_cbc, sha256, default_prf}; +suite_definition(?TLS_DHE_PSK_WITH_AES_256_CBC_SHA384) -> + {dhe_psk, aes_256_cbc, sha384, default_prf}; +suite_definition(?TLS_RSA_PSK_WITH_AES_128_CBC_SHA256) -> + {rsa_psk, aes_128_cbc, sha256, default_prf}; +suite_definition(?TLS_RSA_PSK_WITH_AES_256_CBC_SHA384) -> + {rsa_psk, aes_256_cbc, sha384, default_prf}; + +suite_definition(?TLS_PSK_WITH_NULL_SHA256) -> + {psk, null, sha256, default_prf}; +suite_definition(?TLS_PSK_WITH_NULL_SHA384) -> + {psk, null, sha384, default_prf}; +suite_definition(?TLS_DHE_PSK_WITH_NULL_SHA256) -> + {dhe_psk, null, sha256, default_prf}; +suite_definition(?TLS_DHE_PSK_WITH_NULL_SHA384) -> + {dhe_psk, null, sha384, default_prf}; +suite_definition(?TLS_RSA_PSK_WITH_NULL_SHA256) -> + {rsa_psk, null, sha256, default_prf}; +suite_definition(?TLS_RSA_PSK_WITH_NULL_SHA384) -> + {rsa_psk, null, sha384, default_prf}; + +%%% SRP Cipher Suites RFC 5054 + +suite_definition(?TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA) -> + {srp_anon, '3des_ede_cbc', sha, default_prf}; +suite_definition(?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA) -> + {srp_rsa, '3des_ede_cbc', sha, default_prf}; +suite_definition(?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA) -> + {srp_dss, '3des_ede_cbc', sha, default_prf}; +suite_definition(?TLS_SRP_SHA_WITH_AES_128_CBC_SHA) -> + {srp_anon, aes_128_cbc, sha, default_prf}; +suite_definition(?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA) -> + {srp_rsa, aes_128_cbc, sha, default_prf}; +suite_definition(?TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA) -> + {srp_dss, aes_128_cbc, sha, default_prf}; +suite_definition(?TLS_SRP_SHA_WITH_AES_256_CBC_SHA) -> + {srp_anon, aes_256_cbc, sha, default_prf}; +suite_definition(?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA) -> + {srp_rsa, aes_256_cbc, sha, default_prf}; +suite_definition(?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA) -> + {srp_dss, aes_256_cbc, sha, default_prf}. + %%-------------------------------------------------------------------- -spec suite(erl_cipher_suite()) -> cipher_suite(). @@ -370,7 +497,83 @@ suite({dhe_rsa, aes_256_cbc, sha256}) -> suite({dh_anon, aes_128_cbc, sha256}) -> ?TLS_DH_anon_WITH_AES_128_CBC_SHA256; suite({dh_anon, aes_256_cbc, sha256}) -> - ?TLS_DH_anon_WITH_AES_256_CBC_SHA256. + ?TLS_DH_anon_WITH_AES_256_CBC_SHA256; + +%%% PSK Cipher Suites RFC 4279 + +suite({psk, rc4_128,sha}) -> + ?TLS_PSK_WITH_RC4_128_SHA; +suite({psk, '3des_ede_cbc',sha}) -> + ?TLS_PSK_WITH_3DES_EDE_CBC_SHA; +suite({psk, aes_128_cbc,sha}) -> + ?TLS_PSK_WITH_AES_128_CBC_SHA; +suite({psk, aes_256_cbc,sha}) -> + ?TLS_PSK_WITH_AES_256_CBC_SHA; +suite({dhe_psk, rc4_128,sha}) -> + ?TLS_DHE_PSK_WITH_RC4_128_SHA; +suite({dhe_psk, '3des_ede_cbc',sha}) -> + ?TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA; +suite({dhe_psk, aes_128_cbc,sha}) -> + ?TLS_DHE_PSK_WITH_AES_128_CBC_SHA; +suite({dhe_psk, aes_256_cbc,sha}) -> + ?TLS_DHE_PSK_WITH_AES_256_CBC_SHA; +suite({rsa_psk, rc4_128,sha}) -> + ?TLS_RSA_PSK_WITH_RC4_128_SHA; +suite({rsa_psk, '3des_ede_cbc',sha}) -> + ?TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA; +suite({rsa_psk, aes_128_cbc,sha}) -> + ?TLS_RSA_PSK_WITH_AES_128_CBC_SHA; +suite({rsa_psk, aes_256_cbc,sha}) -> + ?TLS_RSA_PSK_WITH_AES_256_CBC_SHA; + +%%% TLS 1.2 PSK Cipher Suites RFC 5487 + +suite({psk, aes_128_cbc, sha256}) -> + ?TLS_PSK_WITH_AES_128_CBC_SHA256; +suite({psk, aes_256_cbc, sha384}) -> + ?TLS_PSK_WITH_AES_256_CBC_SHA384; +suite({dhe_psk, aes_128_cbc, sha256}) -> + ?TLS_DHE_PSK_WITH_AES_128_CBC_SHA256; +suite({dhe_psk, aes_256_cbc, sha384}) -> + ?TLS_DHE_PSK_WITH_AES_256_CBC_SHA384; +suite({rsa_psk, aes_128_cbc, sha256}) -> + ?TLS_RSA_PSK_WITH_AES_128_CBC_SHA256; +suite({rsa_psk, aes_256_cbc, sha384}) -> + ?TLS_RSA_PSK_WITH_AES_256_CBC_SHA384; + +suite({psk, null, sha256}) -> + ?TLS_PSK_WITH_NULL_SHA256; +suite({psk, null, sha384}) -> + ?TLS_PSK_WITH_NULL_SHA384; +suite({dhe_psk, null, sha256}) -> + ?TLS_DHE_PSK_WITH_NULL_SHA256; +suite({dhe_psk, null, sha384}) -> + ?TLS_DHE_PSK_WITH_NULL_SHA384; +suite({rsa_psk, null, sha256}) -> + ?TLS_RSA_PSK_WITH_NULL_SHA256; +suite({rsa_psk, null, sha384}) -> + ?TLS_RSA_PSK_WITH_NULL_SHA384; + +%%% SRP Cipher Suites RFC 5054 + +suite({srp_anon, '3des_ede_cbc', sha}) -> + ?TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA; +suite({srp_rsa, '3des_ede_cbc', sha}) -> + ?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA; +suite({srp_dss, '3des_ede_cbc', sha}) -> + ?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA; +suite({srp_anon, aes_128_cbc, sha}) -> + ?TLS_SRP_SHA_WITH_AES_128_CBC_SHA; +suite({srp_rsa, aes_128_cbc, sha}) -> + ?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA; +suite({srp_dss, aes_128_cbc, sha}) -> + ?TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA; +suite({srp_anon, aes_256_cbc, sha}) -> + ?TLS_SRP_SHA_WITH_AES_256_CBC_SHA; +suite({srp_rsa, aes_256_cbc, sha}) -> + ?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA; +suite({srp_dss, aes_256_cbc, sha}) -> + ?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA. %%-------------------------------------------------------------------- -spec openssl_suite(openssl_cipher_suite()) -> cipher_suite(). @@ -415,7 +618,24 @@ openssl_suite("RC4-MD5") -> openssl_suite("EDH-RSA-DES-CBC-SHA") -> ?TLS_DHE_RSA_WITH_DES_CBC_SHA; openssl_suite("DES-CBC-SHA") -> - ?TLS_RSA_WITH_DES_CBC_SHA. + ?TLS_RSA_WITH_DES_CBC_SHA; + +%%% SRP Cipher Suites RFC 5054 + +openssl_suite("SRP-DSS-AES-256-CBC-SHA") -> + ?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA; +openssl_suite("SRP-RSA-AES-256-CBC-SHA") -> + ?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA; +openssl_suite("SRP-DSS-3DES-EDE-CBC-SHA") -> + ?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA; +openssl_suite("SRP-RSA-3DES-EDE-CBC-SHA") -> + ?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA; +openssl_suite("SRP-DSS-AES-128-CBC-SHA") -> + ?TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA; +openssl_suite("SRP-RSA-AES-128-CBC-SHA") -> + ?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA. + + %%-------------------------------------------------------------------- -spec openssl_suite_name(cipher_suite()) -> openssl_cipher_suite(). %% @@ -469,6 +689,33 @@ openssl_suite_name(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256) -> "DHE-DSS-AES256-SHA256"; openssl_suite_name(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256) -> "DHE-RSA-AES256-SHA256"; + +%%% PSK Cipher Suites RFC 4279 + +openssl_suite_name(?TLS_PSK_WITH_AES_256_CBC_SHA) -> + "PSK-AES256-CBC-SHA"; +openssl_suite_name(?TLS_PSK_WITH_3DES_EDE_CBC_SHA) -> + "PSK-3DES-EDE-CBC-SHA"; +openssl_suite_name(?TLS_PSK_WITH_AES_128_CBC_SHA) -> + "PSK-AES128-CBC-SHA"; +openssl_suite_name(?TLS_PSK_WITH_RC4_128_SHA) -> + "PSK-RC4-SHA"; + +%%% SRP Cipher Suites RFC 5054 + +openssl_suite_name(?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA) -> + "SRP-RSA-3DES-EDE-CBC-SHA"; +openssl_suite_name(?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA) -> + "SRP-DSS-3DES-EDE-CBC-SHA"; +openssl_suite_name(?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA) -> + "SRP-RSA-AES-128-CBC-SHA"; +openssl_suite_name(?TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA) -> + "SRP-DSS-AES-128-CBC-SHA"; +openssl_suite_name(?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA) -> + "SRP-RSA-AES-256-CBC-SHA"; +openssl_suite_name(?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA) -> + "SRP-DSS-AES-256-CBC-SHA"; + %% No oppenssl name openssl_suite_name(Cipher) -> suite_definition(Cipher). @@ -605,14 +852,14 @@ hash_size(md5) -> 16; hash_size(sha) -> 20; +hash_size(sha224) -> + 28; hash_size(sha256) -> - 32. -%% Currently no supported cipher suites defaults to sha384 or sha512 -%% so these clauses are not needed at the moment. -%% hash_size(sha384) -> -%% 48; -%% hash_size(sha512) -> -%% 64. + 32; +hash_size(sha384) -> + 48; +hash_size(sha512) -> + 64. %% RFC 5246: 6.2.3.2. CBC Block Cipher %% @@ -702,7 +949,8 @@ next_iv(Bin, IV) -> NextIV. rsa_signed_suites() -> - dhe_rsa_suites() ++ rsa_suites(). + dhe_rsa_suites() ++ rsa_suites() ++ + psk_rsa_suites() ++ srp_rsa_suites(). dhe_rsa_suites() -> [?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, @@ -712,6 +960,19 @@ dhe_rsa_suites() -> ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA, ?TLS_DHE_RSA_WITH_DES_CBC_SHA]. +psk_rsa_suites() -> + [?TLS_RSA_PSK_WITH_AES_256_CBC_SHA384, + ?TLS_RSA_PSK_WITH_AES_128_CBC_SHA256, + ?TLS_RSA_PSK_WITH_AES_256_CBC_SHA, + ?TLS_RSA_PSK_WITH_AES_128_CBC_SHA, + ?TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA, + ?TLS_RSA_PSK_WITH_RC4_128_SHA]. + +srp_rsa_suites() -> + [?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA, + ?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA]. + rsa_suites() -> [?TLS_RSA_WITH_AES_256_CBC_SHA256, ?TLS_RSA_WITH_AES_256_CBC_SHA, @@ -723,7 +984,7 @@ rsa_suites() -> ?TLS_RSA_WITH_DES_CBC_SHA]. dsa_signed_suites() -> - dhe_dss_suites(). + dhe_dss_suites() ++ srp_dss_suites(). dhe_dss_suites() -> [?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, @@ -733,6 +994,11 @@ dhe_dss_suites() -> ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA, ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA]. +srp_dss_suites() -> + [?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA, + ?TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA, + ?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA]. + filter_rsa(OtpCert, RsaCiphers) -> TBSCert = OtpCert#'OTPCertificate'.tbsCertificate, TBSExtensions = TBSCert#'OTPTBSCertificate'.extensions, diff --git a/lib/ssl/src/ssl_cipher.hrl b/lib/ssl/src/ssl_cipher.hrl index 0f439f8ed5..90d3704efd 100644 --- a/lib/ssl/src/ssl_cipher.hrl +++ b/lib/ssl/src/ssl_cipher.hrl @@ -250,4 +250,109 @@ %% hello extension data as they should. -define(TLS_EMPTY_RENEGOTIATION_INFO_SCSV, <<?BYTE(16#00), ?BYTE(16#FF)>>). +%%% PSK Cipher Suites RFC 4279 + +%% TLS_PSK_WITH_RC4_128_SHA = { 0x00, 0x8A }; +-define(TLS_PSK_WITH_RC4_128_SHA, <<?BYTE(16#00), ?BYTE(16#8A)>>). + +%% TLS_PSK_WITH_3DES_EDE_CBC_SHA = { 0x00, 0x8B }; +-define(TLS_PSK_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#8B)>>). + +%% TLS_PSK_WITH_AES_128_CBC_SHA = { 0x00, 0x8C }; +-define(TLS_PSK_WITH_AES_128_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#8C)>>). + +%% TLS_PSK_WITH_AES_256_CBC_SHA = { 0x00, 0x8D }; +-define(TLS_PSK_WITH_AES_256_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#8D)>>). + +%% TLS_DHE_PSK_WITH_RC4_128_SHA = { 0x00, 0x8E }; +-define(TLS_DHE_PSK_WITH_RC4_128_SHA, <<?BYTE(16#00), ?BYTE(16#8E)>>). + +%% TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA = { 0x00, 0x8F }; +-define(TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#8F)>>). + +%% TLS_DHE_PSK_WITH_AES_128_CBC_SHA = { 0x00, 0x90 }; +-define(TLS_DHE_PSK_WITH_AES_128_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#90)>>). + +%% TLS_DHE_PSK_WITH_AES_256_CBC_SHA = { 0x00, 0x91 }; +-define(TLS_DHE_PSK_WITH_AES_256_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#91)>>). + +%% TLS_RSA_PSK_WITH_RC4_128_SHA = { 0x00, 0x92 }; +-define(TLS_RSA_PSK_WITH_RC4_128_SHA, <<?BYTE(16#00), ?BYTE(16#92)>>). + +%% TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA = { 0x00, 0x93 }; +-define(TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#93)>>). + +%% TLS_RSA_PSK_WITH_AES_128_CBC_SHA = { 0x00, 0x94 }; +-define(TLS_RSA_PSK_WITH_AES_128_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#94)>>). + +%% TLS_RSA_PSK_WITH_AES_256_CBC_SHA = { 0x00, 0x95 }; +-define(TLS_RSA_PSK_WITH_AES_256_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#95)>>). + +%%% TLS 1.2 PSK Cipher Suites RFC 5487 + +%% TLS_PSK_WITH_AES_128_CBC_SHA256 = {0x00,0xAE}; +-define(TLS_PSK_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#AE)>>). + +%% TLS_PSK_WITH_AES_256_CBC_SHA384 = {0x00,0xAF}; +-define(TLS_PSK_WITH_AES_256_CBC_SHA384, <<?BYTE(16#00), ?BYTE(16#AF)>>). + +%% TLS_PSK_WITH_NULL_SHA256 = {0x00,0xB0}; +-define(TLS_PSK_WITH_NULL_SHA256, <<?BYTE(16#00), ?BYTE(16#B0)>>). + +%% TLS_PSK_WITH_NULL_SHA384 = {0x00,0xB1}; +-define(TLS_PSK_WITH_NULL_SHA384, <<?BYTE(16#00), ?BYTE(16#B1)>>). + +%% TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 = {0x00,0xB2}; +-define(TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#B2)>>). + +%% TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 = {0x00,0xB3}; +-define(TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, <<?BYTE(16#00), ?BYTE(16#B3)>>). + +%% TLS_DHE_PSK_WITH_NULL_SHA256 = {0x00,0xB4}; +-define(TLS_DHE_PSK_WITH_NULL_SHA256, <<?BYTE(16#00), ?BYTE(16#B4)>>). + +%% TLS_DHE_PSK_WITH_NULL_SHA384 = {0x00,0xB5}; +-define(TLS_DHE_PSK_WITH_NULL_SHA384, <<?BYTE(16#00), ?BYTE(16#B5)>>). + +%% TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 = {0x00,0xB6}; +-define(TLS_RSA_PSK_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#B6)>>). + +%% TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 = {0x00,0xB7}; +-define(TLS_RSA_PSK_WITH_AES_256_CBC_SHA384, <<?BYTE(16#00), ?BYTE(16#B7)>>). + +%% TLS_RSA_PSK_WITH_NULL_SHA256 = {0x00,0xB8}; +-define(TLS_RSA_PSK_WITH_NULL_SHA256, <<?BYTE(16#00), ?BYTE(16#B8)>>). + +%% TLS_RSA_PSK_WITH_NULL_SHA384 = {0x00,0xB9}; +-define(TLS_RSA_PSK_WITH_NULL_SHA384, <<?BYTE(16#00), ?BYTE(16#B9)>>). + +%%% SRP Cipher Suites RFC 5054 + +%% TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA = { 0xC0,0x1A }; +-define(TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#1A)>>). + +%% TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA = { 0xC0,0x1B }; +-define(TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#1B)>>). + +%% TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA = { 0xC0,0x1C }; +-define(TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#1C)>>). + +%% TLS_SRP_SHA_WITH_AES_128_CBC_SHA = { 0xC0,0x1D }; +-define(TLS_SRP_SHA_WITH_AES_128_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#1D)>>). + +%% TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA = { 0xC0,0x1E }; +-define(TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#1E)>>). + +%% TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA = { 0xC0,0x1F }; +-define(TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#1F)>>). + +%% TLS_SRP_SHA_WITH_AES_256_CBC_SHA = { 0xC0,0x20 }; +-define(TLS_SRP_SHA_WITH_AES_256_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#20)>>). + +%% TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA = { 0xC0,0x21 }; +-define(TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#21)>>). + +%% TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA = { 0xC0,0x22 }; +-define(TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#22)>>). + -endif. % -ifdef(ssl_cipher). diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 4f241ecc0a..1843377582 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -34,6 +34,8 @@ -include("ssl_record.hrl"). -include("ssl_cipher.hrl"). -include("ssl_internal.hrl"). +-include("ssl_srp.hrl"). +-include("ssl_srp_primes.hrl"). -include_lib("public_key/include/public_key.hrl"). %% Internal application API @@ -80,6 +82,9 @@ private_key, % PKIX: #'RSAPrivateKey'{} diffie_hellman_params, % PKIX: #'DHParameter'{} relevant for server side diffie_hellman_keys, % {PublicKey, PrivateKey} + psk_identity, % binary() - server psk identity hint + srp_params, % #srp_user{} + srp_keys, % {PublicKey, PrivateKey} premaster_secret, % file_ref_db, % ets() cert_db_ref, % ref() @@ -528,7 +533,9 @@ certify(#certificate{} = Cert, certify(#server_key_exchange{} = KeyExchangeMsg, #state{role = client, negotiated_version = Version, key_algorithm = Alg} = State0) - when Alg == dhe_dss; Alg == dhe_rsa; Alg == dh_anon -> + when Alg == dhe_dss; Alg == dhe_rsa; Alg == dh_anon; + Alg == psk; Alg == dhe_psk; Alg == rsa_psk; + Alg == srp_dss; Alg == srp_rsa; Alg == srp_anon -> case handle_server_key(KeyExchangeMsg, State0) of #state{} = State1 -> {Record, State} = next_record(State1), @@ -545,6 +552,45 @@ certify(#certificate_request{}, State0) -> {Record, State} = next_record(State0#state{client_certificate_requested = true}), next_state(certify, certify, Record, State); +%% PSK and RSA_PSK might bypass the Server-Key-Exchange +certify(#server_hello_done{}, + #state{session = #session{master_secret = undefined}, + negotiated_version = Version, + psk_identity = PSKIdentity, + premaster_secret = undefined, + role = client, + key_algorithm = Alg} = State0) + when Alg == psk -> + case server_psk_master_secret(PSKIdentity, State0) of + #state{} = State -> + client_certify_and_key_exchange(State); + #alert{} = Alert -> + handle_own_alert(Alert, Version, certify, State0) + end; + +certify(#server_hello_done{}, + #state{session = #session{master_secret = undefined}, + ssl_options = SslOpts, + negotiated_version = Version, + psk_identity = PSKIdentity, + premaster_secret = undefined, + role = client, + key_algorithm = Alg} = State0) + when Alg == rsa_psk -> + case handle_psk_identity(PSKIdentity, SslOpts#ssl_options.user_lookup_fun) of + {ok, PSK} when is_binary(PSK) -> + PremasterSecret = make_premaster_secret(Version, rsa), + Len = byte_size(PSK), + RealPMS = <<?UINT16(48), PremasterSecret/binary, ?UINT16(Len), PSK/binary>>, + State1 = State0#state{premaster_secret = PremasterSecret}, + State = master_from_premaster_secret(RealPMS, State1), + client_certify_and_key_exchange(State); + #alert{} = Alert -> + Alert; + _ -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER) + end; + %% Master secret was determined with help of server-key exchange msg certify(#server_hello_done{}, #state{session = #session{master_secret = MasterSecret} = Session, @@ -631,6 +677,60 @@ certify_client_key_exchange(#client_diffie_hellman_public{dh_public = ClientPubl next_state(certify, cipher, Record, State); #alert{} = Alert -> handle_own_alert(Alert, Version, certify, State0) + end; + +certify_client_key_exchange(#client_psk_identity{identity = ClientPSKIdentity}, + #state{negotiated_version = Version} = State0) -> + case server_psk_master_secret(ClientPSKIdentity, State0) of + #state{} = State1 -> + {Record, State} = next_record(State1), + next_state(certify, cipher, Record, State); + #alert{} = Alert -> + handle_own_alert(Alert, Version, certify, State0) + end; + +certify_client_key_exchange(#client_dhe_psk_identity{ + identity = ClientPSKIdentity, + dh_public = ClientPublicDhKey}, + #state{negotiated_version = Version, + diffie_hellman_params = #'DHParameter'{prime = P, + base = G}, + diffie_hellman_keys = {_, ServerDhPrivateKey}} = State0) -> + case dhe_psk_master_secret(ClientPSKIdentity, crypto:mpint(P), crypto:mpint(G), ClientPublicDhKey, ServerDhPrivateKey, State0) of + #state{} = State1 -> + {Record, State} = next_record(State1), + next_state(certify, cipher, Record, State); + #alert{} = Alert -> + handle_own_alert(Alert, Version, certify, State0) + end; + +certify_client_key_exchange(#client_rsa_psk_identity{ + identity = PskIdentity, + exchange_keys = + #encrypted_premaster_secret{premaster_secret= EncPMS}}, + #state{negotiated_version = Version, + private_key = Key} = State0) -> + PremasterSecret = ssl_handshake:decrypt_premaster_secret(EncPMS, Key), + case server_rsa_psk_master_secret(PskIdentity, PremasterSecret, State0) of + #state{} = State1 -> + {Record, State} = next_record(State1), + next_state(certify, cipher, Record, State); + #alert{} = Alert -> + handle_own_alert(Alert, Version, certify, State0) + end; + +certify_client_key_exchange(#client_srp_public{srp_a = ClientPublicKey}, + #state{negotiated_version = Version, + srp_params = + #srp_user{prime = Prime, + verifier = Verifier} + } = State0) -> + case server_srp_master_secret(Verifier, Prime, ClientPublicKey, State0) of + #state{} = State1 -> + {Record, State} = next_record(State1), + next_state(certify, cipher, Record, State); + #alert{} = Alert -> + handle_own_alert(Alert, Version, certify, State0) end. %%-------------------------------------------------------------------- @@ -1446,7 +1546,8 @@ server_hello_done(#state{transport_cb = Transport, State#state{connection_states = ConnectionStates, tls_handshake_history = Handshake}. -certify_server(#state{key_algorithm = dh_anon} = State) -> +certify_server(#state{key_algorithm = Algo} = State) + when Algo == dh_anon; Algo == psk; Algo == dhe_psk -> State; certify_server(#state{transport_cb = Transport, @@ -1501,6 +1602,128 @@ key_exchange(#state{role = server, key_algorithm = Algo, diffie_hellman_keys = Keys, tls_handshake_history = Handshake}; +key_exchange(#state{role = server, key_algorithm = psk, + ssl_options = #ssl_options{psk_identity = undefined}} = State) -> + State; +key_exchange(#state{role = server, key_algorithm = psk, + ssl_options = #ssl_options{psk_identity = PskIdentityHint}, + hashsign_algorithm = HashSignAlgo, + private_key = PrivateKey, + connection_states = ConnectionStates0, + negotiated_version = Version, + tls_handshake_history = Handshake0, + socket = Socket, + transport_cb = Transport + } = State) -> + ConnectionState = + ssl_record:pending_connection_state(ConnectionStates0, read), + SecParams = ConnectionState#connection_state.security_parameters, + #security_parameters{client_random = ClientRandom, + server_random = ServerRandom} = SecParams, + Msg = ssl_handshake:key_exchange(server, Version, {psk, PskIdentityHint, + HashSignAlgo, ClientRandom, + ServerRandom, + PrivateKey}), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), + Transport:send(Socket, BinMsg), + State#state{connection_states = ConnectionStates, + tls_handshake_history = Handshake}; + +key_exchange(#state{role = server, key_algorithm = dhe_psk, + ssl_options = #ssl_options{psk_identity = PskIdentityHint}, + hashsign_algorithm = HashSignAlgo, + diffie_hellman_params = #'DHParameter'{prime = P, base = G} = Params, + private_key = PrivateKey, + connection_states = ConnectionStates0, + negotiated_version = Version, + tls_handshake_history = Handshake0, + socket = Socket, + transport_cb = Transport + } = State) -> + Keys = crypto:dh_generate_key([crypto:mpint(P), crypto:mpint(G)]), + ConnectionState = + ssl_record:pending_connection_state(ConnectionStates0, read), + SecParams = ConnectionState#connection_state.security_parameters, + #security_parameters{client_random = ClientRandom, + server_random = ServerRandom} = SecParams, + Msg = ssl_handshake:key_exchange(server, Version, {dhe_psk, PskIdentityHint, Keys, Params, + HashSignAlgo, ClientRandom, + ServerRandom, + PrivateKey}), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), + Transport:send(Socket, BinMsg), + State#state{connection_states = ConnectionStates, + diffie_hellman_keys = Keys, + tls_handshake_history = Handshake}; + +key_exchange(#state{role = server, key_algorithm = rsa_psk, + ssl_options = #ssl_options{psk_identity = undefined}} = State) -> + State; +key_exchange(#state{role = server, key_algorithm = rsa_psk, + ssl_options = #ssl_options{psk_identity = PskIdentityHint}, + hashsign_algorithm = HashSignAlgo, + private_key = PrivateKey, + connection_states = ConnectionStates0, + negotiated_version = Version, + tls_handshake_history = Handshake0, + socket = Socket, + transport_cb = Transport + } = State) -> + ConnectionState = + ssl_record:pending_connection_state(ConnectionStates0, read), + SecParams = ConnectionState#connection_state.security_parameters, + #security_parameters{client_random = ClientRandom, + server_random = ServerRandom} = SecParams, + Msg = ssl_handshake:key_exchange(server, Version, {psk, PskIdentityHint, + HashSignAlgo, ClientRandom, + ServerRandom, + PrivateKey}), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), + Transport:send(Socket, BinMsg), + State#state{connection_states = ConnectionStates, + tls_handshake_history = Handshake}; + +key_exchange(#state{role = server, key_algorithm = Algo, + ssl_options = #ssl_options{user_lookup_fun = LookupFun}, + hashsign_algorithm = HashSignAlgo, + session = #session{srp_username = Username}, + private_key = PrivateKey, + connection_states = ConnectionStates0, + negotiated_version = Version, + tls_handshake_history = Handshake0, + socket = Socket, + transport_cb = Transport + } = State) + when Algo == srp_dss; + Algo == srp_rsa; + Algo == srp_anon -> + SrpParams = handle_srp_identity(Username, LookupFun), + Keys = case generate_srp_server_keys(SrpParams, 0) of + Alert = #alert{} -> + throw(Alert); + Keys0 = {_,_} -> + Keys0 + end, + ConnectionState = + ssl_record:pending_connection_state(ConnectionStates0, read), + SecParams = ConnectionState#connection_state.security_parameters, + #security_parameters{client_random = ClientRandom, + server_random = ServerRandom} = SecParams, + Msg = ssl_handshake:key_exchange(server, Version, {srp, Keys, SrpParams, + HashSignAlgo, ClientRandom, + ServerRandom, + PrivateKey}), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), + Transport:send(Socket, BinMsg), + State#state{connection_states = ConnectionStates, + srp_params = SrpParams, + srp_keys = Keys, + tls_handshake_history = Handshake}; + key_exchange(#state{role = client, connection_states = ConnectionStates0, key_algorithm = rsa, @@ -1530,6 +1753,68 @@ key_exchange(#state{role = client, encode_handshake(Msg, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), State#state{connection_states = ConnectionStates, + tls_handshake_history = Handshake}; + +key_exchange(#state{role = client, + ssl_options = SslOpts, + connection_states = ConnectionStates0, + key_algorithm = psk, + negotiated_version = Version, + socket = Socket, transport_cb = Transport, + tls_handshake_history = Handshake0} = State) -> + Msg = ssl_handshake:key_exchange(client, Version, {psk, SslOpts#ssl_options.psk_identity}), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), + Transport:send(Socket, BinMsg), + State#state{connection_states = ConnectionStates, + tls_handshake_history = Handshake}; + +key_exchange(#state{role = client, + ssl_options = SslOpts, + connection_states = ConnectionStates0, + key_algorithm = dhe_psk, + negotiated_version = Version, + diffie_hellman_keys = {DhPubKey, _}, + socket = Socket, transport_cb = Transport, + tls_handshake_history = Handshake0} = State) -> + Msg = ssl_handshake:key_exchange(client, Version, {dhe_psk, SslOpts#ssl_options.psk_identity, DhPubKey}), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), + Transport:send(Socket, BinMsg), + State#state{connection_states = ConnectionStates, + tls_handshake_history = Handshake}; + +key_exchange(#state{role = client, + ssl_options = SslOpts, + connection_states = ConnectionStates0, + key_algorithm = rsa_psk, + public_key_info = PublicKeyInfo, + negotiated_version = Version, + premaster_secret = PremasterSecret, + socket = Socket, transport_cb = Transport, + tls_handshake_history = Handshake0} = State) -> + Msg = rsa_psk_key_exchange(Version, SslOpts#ssl_options.psk_identity, PremasterSecret, PublicKeyInfo), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), + Transport:send(Socket, BinMsg), + State#state{connection_states = ConnectionStates, + tls_handshake_history = Handshake}; + +key_exchange(#state{role = client, + connection_states = ConnectionStates0, + key_algorithm = Algorithm, + negotiated_version = Version, + srp_keys = {ClientPubKey, _}, + socket = Socket, transport_cb = Transport, + tls_handshake_history = Handshake0} = State) + when Algorithm == srp_dss; + Algorithm == srp_rsa; + Algorithm == srp_anon -> + Msg = ssl_handshake:key_exchange(client, Version, {srp, ClientPubKey}), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), + Transport:send(Socket, BinMsg), + State#state{connection_states = ConnectionStates, tls_handshake_history = Handshake}. rsa_key_exchange(Version, PremasterSecret, PublicKeyInfo = {Algorithm, _, _}) @@ -1548,6 +1833,22 @@ rsa_key_exchange(Version, PremasterSecret, PublicKeyInfo = {Algorithm, _, _}) rsa_key_exchange(_, _, _) -> throw (?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE)). +rsa_psk_key_exchange(Version, PskIdentity, PremasterSecret, PublicKeyInfo = {Algorithm, _, _}) + when Algorithm == ?rsaEncryption; + Algorithm == ?md2WithRSAEncryption; + Algorithm == ?md5WithRSAEncryption; + Algorithm == ?sha1WithRSAEncryption; + Algorithm == ?sha224WithRSAEncryption; + Algorithm == ?sha256WithRSAEncryption; + Algorithm == ?sha384WithRSAEncryption; + Algorithm == ?sha512WithRSAEncryption + -> + ssl_handshake:key_exchange(client, Version, + {psk_premaster_secret, PskIdentity, PremasterSecret, + PublicKeyInfo}); +rsa_psk_key_exchange(_, _, _, _) -> + throw (?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE)). + request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer}, connection_states = ConnectionStates0, cert_db = CertDbHandle, @@ -1666,7 +1967,23 @@ verify_server_key(#server_key_params{params = Params, server_master_secret(#server_dh_params{dh_p = P, dh_g = G, dh_y = ServerPublicDhKey}, State) -> - dh_master_secret(P, G, ServerPublicDhKey, undefined, State). + dh_master_secret(P, G, ServerPublicDhKey, undefined, State); + +server_master_secret(#server_psk_params{ + hint = IdentityHint}, + State) -> + %% store for later use + State#state{psk_identity = IdentityHint}; + +server_master_secret(#server_dhe_psk_params{ + hint = IdentityHint, + dh_params = #server_dh_params{dh_p = P, dh_g = G, dh_y = ServerPublicDhKey}}, + State) -> + dhe_psk_master_secret(IdentityHint, P, G, ServerPublicDhKey, undefined, State); + +server_master_secret(#server_srp_params{srp_n = N, srp_g = G, srp_s = S, srp_b = B}, + State) -> + client_srp_master_secret(G, N, S, B, undefined, State). master_from_premaster_secret(PremasterSecret, #state{session = Session, @@ -1696,6 +2013,131 @@ dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey, State) -> [PMpint, GMpint]), master_from_premaster_secret(PremasterSecret, State). +handle_psk_identity(_PSKIdentity, LookupFun) + when LookupFun == undefined -> + error; +handle_psk_identity(PSKIdentity, {Fun, UserState}) -> + Fun(psk, PSKIdentity, UserState). + +server_psk_master_secret(ClientPSKIdentity, + #state{ssl_options = SslOpts} = State) -> + case handle_psk_identity(ClientPSKIdentity, SslOpts#ssl_options.user_lookup_fun) of + {ok, PSK} when is_binary(PSK) -> + Len = byte_size(PSK), + PremasterSecret = <<?UINT16(Len), 0:(Len*8), ?UINT16(Len), PSK/binary>>, + master_from_premaster_secret(PremasterSecret, State); + #alert{} = Alert -> + Alert; + _ -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER) + end. + +dhe_psk_master_secret(PSKIdentity, Prime, Base, PublicDhKey, undefined, State) -> + PMpint = mpint_binary(Prime), + GMpint = mpint_binary(Base), + Keys = {_, PrivateDhKey} = + crypto:dh_generate_key([PMpint,GMpint]), + dhe_psk_master_secret(PSKIdentity, PMpint, GMpint, PublicDhKey, PrivateDhKey, + State#state{diffie_hellman_keys = Keys}); + +dhe_psk_master_secret(PSKIdentity, PMpint, GMpint, PublicDhKey, PrivateDhKey, + #state{ssl_options = SslOpts} = State) -> + case handle_psk_identity(PSKIdentity, SslOpts#ssl_options.user_lookup_fun) of + {ok, PSK} when is_binary(PSK) -> + DHSecret = + crypto:dh_compute_key(mpint_binary(PublicDhKey), PrivateDhKey, + [PMpint, GMpint]), + DHLen = erlang:byte_size(DHSecret), + Len = erlang:byte_size(PSK), + PremasterSecret = <<?UINT16(DHLen), DHSecret/binary, ?UINT16(Len), PSK/binary>>, + master_from_premaster_secret(PremasterSecret, State); + #alert{} = Alert -> + Alert; + _ -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER) + end. + +server_rsa_psk_master_secret(PskIdentity, PremasterSecret, + #state{ssl_options = SslOpts} = State) -> + case handle_psk_identity(PskIdentity, SslOpts#ssl_options.user_lookup_fun) of + {ok, PSK} when is_binary(PSK) -> + Len = byte_size(PSK), + RealPMS = <<?UINT16(48), PremasterSecret/binary, ?UINT16(Len), PSK/binary>>, + master_from_premaster_secret(RealPMS, State); + #alert{} = Alert -> + Alert; + _ -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER) + end. + +generate_srp_server_keys(_SrpParams, 10) -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); +generate_srp_server_keys(SrpParams = + #srp_user{generator = Generator, prime = Prime, + verifier = Verifier}, N) -> + case crypto:srp_generate_key(Verifier, Generator, Prime, '6a') of + error -> + generate_srp_server_keys(SrpParams, N+1); + Keys -> + Keys + end. + +generate_srp_client_keys(_Generator, _Prime, 10) -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); +generate_srp_client_keys(Generator, Prime, N) -> + + case crypto:srp_generate_key(Generator, Prime, '6a') of + error -> + generate_srp_client_keys(Generator, Prime, N+1); + Keys -> + Keys + end. + +handle_srp_identity(Username, {Fun, UserState}) -> + case Fun(srp, Username, UserState) of + {ok, {SRPParams, Salt, DerivedKey}} + when is_atom(SRPParams), is_binary(Salt), is_binary(DerivedKey) -> + {Generator, Prime} = ssl_srp_primes:get_srp_params(SRPParams), + Verifier = crypto:mod_exp_prime(Generator, DerivedKey, Prime), + #srp_user{generator = Generator, prime = Prime, + salt = Salt, verifier = Verifier}; + #alert{} = Alert -> + throw(Alert); + _ -> + throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)) + end. + +server_srp_master_secret(Verifier, Prime, ClientPub, State = #state{srp_keys = {ServerPub, ServerPriv}}) -> + case crypto:srp_compute_key(Verifier, Prime, ClientPub, ServerPub, ServerPriv, '6a') of + error -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); + PremasterSecret -> + master_from_premaster_secret(PremasterSecret, State) + end. + +client_srp_master_secret(_Generator, _Prime, _Salt, _ServerPub, #alert{} = Alert, _State) -> + Alert; +client_srp_master_secret(Generator, Prime, Salt, ServerPub, undefined, State) -> + Keys = generate_srp_client_keys(Generator, Prime, 0), + client_srp_master_secret(Generator, Prime, Salt, ServerPub, Keys, State#state{srp_keys = Keys}); + +client_srp_master_secret(Generator, Prime, Salt, ServerPub, {ClientPub, ClientPriv}, + #state{ssl_options = SslOpts} = State) -> + case ssl_srp_primes:check_srp_params(Generator, Prime) of + ok -> + {Username, Password} = SslOpts#ssl_options.srp_identity, + DerivedKey = crypto:sha([Salt, crypto:sha([Username, <<$:>>, Password])]), + + case crypto:srp_compute_key(DerivedKey, Prime, Generator, ClientPub, ClientPriv, ServerPub, '6a') of + error -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); + PremasterSecret -> + master_from_premaster_secret(PremasterSecret, State) + end; + _ -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER) + end. + cipher_role(client, Data, Session, #state{connection_states = ConnectionStates0} = State) -> ConnectionStates = ssl_record:set_server_verify_data(current_both, Data, ConnectionStates0), next_state_connection(cipher, ack_connection(State#state{session = Session, @@ -2495,19 +2937,26 @@ default_hashsign(_Version = {Major, Minor}, KeyExchange) when Major == 3 andalso Minor >= 3 andalso (KeyExchange == rsa orelse KeyExchange == dhe_rsa orelse - KeyExchange == dh_rsa) -> + KeyExchange == dh_rsa orelse + KeyExchange == srp_rsa) -> {sha, rsa}; default_hashsign(_Version, KeyExchange) when KeyExchange == rsa; KeyExchange == dhe_rsa; - KeyExchange == dh_rsa -> + KeyExchange == dh_rsa; + KeyExchange == srp_rsa -> {md5sha, rsa}; default_hashsign(_Version, KeyExchange) when KeyExchange == dhe_dss; - KeyExchange == dh_dss -> + KeyExchange == dh_dss; + KeyExchange == srp_dss -> {sha, dsa}; default_hashsign(_Version, KeyExchange) - when KeyExchange == dh_anon -> + when KeyExchange == dh_anon; + KeyExchange == psk; + KeyExchange == dhe_psk; + KeyExchange == rsa_psk; + KeyExchange == srp_anon -> {null, anon}. start_or_recv_cancel_timer(infinity, _RecvFrom) -> diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 889d310ca8..83c0092de2 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -28,6 +28,7 @@ -include("ssl_cipher.hrl"). -include("ssl_alert.hrl"). -include("ssl_internal.hrl"). +-include("ssl_srp.hrl"). -include_lib("public_key/include/public_key.hrl"). -export([master_secret/4, client_hello/8, server_hello/5, hello/4, @@ -65,6 +66,7 @@ client_hello(Host, Port, ConnectionStates, Pending = ssl_record:pending_connection_state(ConnectionStates, read), SecParams = Pending#connection_state.security_parameters, Ciphers = available_suites(UserSuites, Version), + SRP = srp_user(SslOpts), Id = ssl_session:client_id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert), @@ -76,6 +78,7 @@ client_hello(Host, Port, ConnectionStates, renegotiation_info = renegotiation_info(client, ConnectionStates, Renegotiation), + srp = SRP, hash_signs = default_hash_signs(), next_protocol_negotiation = encode_client_protocol_negotiation(SslOpts#ssl_options.next_protocol_selector, Renegotiation) @@ -162,7 +165,8 @@ hello(#server_hello{cipher_suite = CipherSuite, server_version = Version, hello(#client_hello{client_version = ClientVersion, random = Random, cipher_suites = CipherSuites, - renegotiation_info = Info} = Hello, + renegotiation_info = Info, + srp = SRP} = Hello, #ssl_options{versions = Versions, secure_renegotiate = SecureRenegotation} = SslOpts, {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert}, Renegotiation) -> @@ -171,13 +175,14 @@ hello(#client_hello{client_version = ClientVersion, random = Random, case ssl_record:is_acceptable_version(Version, Versions) of true -> {Type, #session{cipher_suite = CipherSuite, - compression_method = Compression} = Session} + compression_method = Compression} = Session1} = select_session(Hello, Port, Session0, Version, SslOpts, Cache, CacheCb, Cert), case CipherSuite of no_suite -> ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY); _ -> + Session = handle_srp_info(SRP, Session1), case handle_renegotiation_info(server, Info, ConnectionStates0, Renegotiation, SecureRenegotation, CipherSuites) of @@ -372,6 +377,10 @@ certificate_request(ConnectionStates, CertDbHandle, CertDbRef) -> {premaster_secret, binary(), public_key_info()} | {dh, binary()} | {dh, {binary(), binary()}, #'DHParameter'{}, {HashAlgo::atom(), SignAlgo::atom()}, + binary(), binary(), private_key()} | + {psk, binary()} | + {dhe_psk, binary(), binary()} | + {srp, {binary(), binary()}, #srp_user{}, {HashAlgo::atom(), SignAlgo::atom()}, binary(), binary(), private_key()}) -> #client_key_exchange{} | #server_key_exchange{}. %% @@ -388,6 +397,33 @@ key_exchange(client, _Version, {dh, <<?UINT32(Len), PublicKey:Len/binary>>}) -> dh_public = PublicKey} }; +key_exchange(client, _Version, {psk, Identity}) -> + #client_key_exchange{ + exchange_keys = #client_psk_identity{ + identity = Identity} + }; + +key_exchange(client, _Version, {dhe_psk, Identity, <<?UINT32(Len), PublicKey:Len/binary>>}) -> + #client_key_exchange{ + exchange_keys = #client_dhe_psk_identity{ + identity = Identity, + dh_public = PublicKey} + }; + +key_exchange(client, _Version, {psk_premaster_secret, PskIdentity, Secret, {_, PublicKey, _}}) -> + EncPremasterSecret = + encrypted_premaster_secret(Secret, PublicKey), + #client_key_exchange{ + exchange_keys = #client_rsa_psk_identity{ + identity = PskIdentity, + exchange_keys = EncPremasterSecret}}; + +key_exchange(client, _Version, {srp, PublicKey}) -> + #client_key_exchange{ + exchange_keys = #client_srp_public{ + srp_a = PublicKey} + }; + key_exchange(server, Version, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _}, #'DHParameter'{prime = P, base = G}, HashSign, ClientRandom, ServerRandom, PrivateKey}) -> @@ -396,6 +432,34 @@ key_exchange(server, Version, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _}, ServerDHParams = #server_dh_params{dh_p = PBin, dh_g = GBin, dh_y = PublicKey}, enc_server_key_exchange(Version, ServerDHParams, HashSign, + ClientRandom, ServerRandom, PrivateKey); + +key_exchange(server, Version, {psk, PskIdentityHint, + HashSign, ClientRandom, ServerRandom, PrivateKey}) -> + ServerPSKParams = #server_psk_params{hint = PskIdentityHint}, + enc_server_key_exchange(Version, ServerPSKParams, HashSign, + ClientRandom, ServerRandom, PrivateKey); + +key_exchange(server, Version, {dhe_psk, PskIdentityHint, {<<?UINT32(Len), PublicKey:Len/binary>>, _}, + #'DHParameter'{prime = P, base = G}, + HashSign, ClientRandom, ServerRandom, PrivateKey}) -> + <<?UINT32(_), PBin/binary>> = crypto:mpint(P), + <<?UINT32(_), GBin/binary>> = crypto:mpint(G), + ServerEDHPSKParams = #server_dhe_psk_params{ + hint = PskIdentityHint, + dh_params = #server_dh_params{dh_p = PBin, + dh_g = GBin, dh_y = PublicKey} + }, + enc_server_key_exchange(Version, ServerEDHPSKParams, + HashSign, ClientRandom, ServerRandom, PrivateKey); + +key_exchange(server, Version, {srp, {PublicKey, _}, + #srp_user{generator = Generator, prime = Prime, + salt = Salt}, + HashSign, ClientRandom, ServerRandom, PrivateKey}) -> + ServerSRPParams = #server_srp_params{srp_n = Prime, srp_g = Generator, + srp_s = Salt, srp_b = PublicKey}, + enc_server_key_exchange(Version, ServerSRPParams, HashSign, ClientRandom, ServerRandom, PrivateKey). enc_server_key_exchange(Version, Params, {HashAlgo, SignAlgo}, @@ -525,7 +589,12 @@ get_tls_handshake(Version, Data, Buffer) -> %%-------------------------------------------------------------------- -spec decode_client_key(binary(), key_algo(), tls_version()) -> - #encrypted_premaster_secret{} | #client_diffie_hellman_public{}. + #encrypted_premaster_secret{} + | #client_diffie_hellman_public{} + | #client_psk_identity{} + | #client_dhe_psk_identity{} + | #client_rsa_psk_identity{} + | #client_srp_public{}. %% %% Description: Decode client_key data and return appropriate type %%-------------------------------------------------------------------- @@ -677,6 +746,11 @@ cipher_suites(Suites, false) -> cipher_suites(Suites, true) -> Suites. +srp_user(#ssl_options{srp_identity = {UserName, _}}) -> + #srp{username = UserName}; +srp_user(_) -> + undefined. + renegotiation_info(client, _, false) -> #renegotiation_info{renegotiated_connection = undefined}; renegotiation_info(server, ConnectionStates, false) -> @@ -759,6 +833,11 @@ select_next_protocol(Protocols, NextProtocolSelector) -> Protocol end. +handle_srp_info(undefined, Session) -> + Session; +handle_srp_info(#srp{username = Username}, Session) -> + Session#session{srp_username = Username}. + handle_renegotiation_info(_, #renegotiation_info{renegotiated_connection = ?byte(0)}, ConnectionStates, false, _, _) -> {ok, ssl_record:set_renegotiation_flag(true, ConnectionStates)}; @@ -941,6 +1020,7 @@ dec_hs(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, DecodedExtensions = dec_hello_extensions(Extensions), RenegotiationInfo = proplists:get_value(renegotiation_info, DecodedExtensions, undefined), + SRP = proplists:get_value(srp, DecodedExtensions, undefined), HashSigns = proplists:get_value(hash_signs, DecodedExtensions, undefined), NextProtocolNegotiation = proplists:get_value(next_protocol_negotiation, DecodedExtensions, undefined), @@ -951,6 +1031,7 @@ dec_hs(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, cipher_suites = from_2bytes(CipherSuites), compression_methods = Comp_methods, renegotiation_info = RenegotiationInfo, + srp = SRP, hash_signs = HashSigns, next_protocol_negotiation = NextProtocolNegotiation }; @@ -1029,7 +1110,23 @@ dec_client_key(<<>>, ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) -> throw(?ALERT_REC(?FATAL, ?UNSUPPORTED_CERTIFICATE)); dec_client_key(<<?UINT16(DH_YLen), DH_Y:DH_YLen/binary>>, ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) -> - #client_diffie_hellman_public{dh_public = DH_Y}. + #client_diffie_hellman_public{dh_public = DH_Y}; +dec_client_key(<<?UINT16(Len), Id:Len/binary>>, + ?KEY_EXCHANGE_PSK, _) -> + #client_psk_identity{identity = Id}; +dec_client_key(<<?UINT16(Len), Id:Len/binary, + ?UINT16(DH_YLen), DH_Y:DH_YLen/binary>>, + ?KEY_EXCHANGE_DHE_PSK, _) -> + #client_dhe_psk_identity{identity = Id, dh_public = DH_Y}; +dec_client_key(<<?UINT16(Len), Id:Len/binary, PKEPMS/binary>>, + ?KEY_EXCHANGE_RSA_PSK, {3, 0}) -> + #client_rsa_psk_identity{identity = Id, exchange_keys = #encrypted_premaster_secret{premaster_secret = PKEPMS}}; +dec_client_key(<<?UINT16(Len), Id:Len/binary, ?UINT16(_), PKEPMS/binary>>, + ?KEY_EXCHANGE_RSA_PSK, _) -> + #client_rsa_psk_identity{identity = Id, exchange_keys = #encrypted_premaster_secret{premaster_secret = PKEPMS}}; +dec_client_key(<<?UINT16(ALen), A:ALen/binary>>, + ?KEY_EXCHANGE_SRP, _) -> + #client_srp_public{srp_a = A}. dec_ske_params(Len, Keys, Version) -> <<Params:Len/bytes, Signature/binary>> = Keys, @@ -1064,6 +1161,41 @@ dec_server_key(<<?UINT16(PLen), P:PLen/binary, params_bin = BinMsg, hashsign = HashSign, signature = Signature}; +dec_server_key(<<?UINT16(Len), PskIdentityHint:Len/binary>> = KeyStruct, + KeyExchange, Version) + when KeyExchange == ?KEY_EXCHANGE_PSK; KeyExchange == ?KEY_EXCHANGE_RSA_PSK -> + Params = #server_psk_params{ + hint = PskIdentityHint}, + {BinMsg, HashSign, Signature} = dec_ske_params(Len + 2, KeyStruct, Version), + #server_key_params{params = Params, + params_bin = BinMsg, + hashsign = HashSign, + signature = Signature}; +dec_server_key(<<?UINT16(Len), IdentityHint:Len/binary, + ?UINT16(PLen), P:PLen/binary, + ?UINT16(GLen), G:GLen/binary, + ?UINT16(YLen), Y:YLen/binary, _/binary>> = KeyStruct, + ?KEY_EXCHANGE_DHE_PSK, Version) -> + DHParams = #server_dh_params{dh_p = P, dh_g = G, dh_y = Y}, + Params = #server_dhe_psk_params{ + hint = IdentityHint, + dh_params = DHParams}, + {BinMsg, HashSign, Signature} = dec_ske_params(Len + PLen + GLen + YLen + 8, KeyStruct, Version), + #server_key_params{params = Params, + params_bin = BinMsg, + hashsign = HashSign, + signature = Signature}; +dec_server_key(<<?UINT16(NLen), N:NLen/binary, + ?UINT16(GLen), G:GLen/binary, + ?BYTE(SLen), S:SLen/binary, + ?UINT16(BLen), B:BLen/binary, _/binary>> = KeyStruct, + ?KEY_EXCHANGE_SRP, Version) -> + Params = #server_srp_params{srp_n = N, srp_g = G, srp_s = S, srp_b = B}, + {BinMsg, HashSign, Signature} = dec_ske_params(NLen + GLen + SLen + BLen + 7, KeyStruct, Version), + #server_key_params{params = Params, + params_bin = BinMsg, + hashsign = HashSign, + signature = Signature}; dec_server_key(_, _, _) -> throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)). @@ -1091,6 +1223,11 @@ dec_hello_extensions(<<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info:Len/binar dec_hello_extensions(Rest, [{renegotiation_info, #renegotiation_info{renegotiated_connection = RenegotiateInfo}} | Acc]); +dec_hello_extensions(<<?UINT16(?SRP_EXT), ?UINT16(Len), ?BYTE(SRPLen), SRP:SRPLen/binary, Rest/binary>>, Acc) + when Len == SRPLen + 2 -> + dec_hello_extensions(Rest, [{srp, + #srp{username = SRP}} | Acc]); + dec_hello_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len), ExtData:Len/binary, Rest/binary>>, Acc) -> SignAlgoListLen = Len - 2, @@ -1148,6 +1285,7 @@ enc_hs(#client_hello{client_version = {Major, Minor}, cipher_suites = CipherSuites, compression_methods = CompMethods, renegotiation_info = RenegotiationInfo, + srp = SRP, hash_signs = HashSigns, next_protocol_negotiation = NextProtocolNegotiation}, _Version) -> SIDLength = byte_size(SessionID), @@ -1155,7 +1293,7 @@ enc_hs(#client_hello{client_version = {Major, Minor}, CmLength = byte_size(BinCompMethods), BinCipherSuites = list_to_binary(CipherSuites), CsLength = byte_size(BinCipherSuites), - Extensions0 = hello_extensions(RenegotiationInfo, NextProtocolNegotiation), + Extensions0 = hello_extensions(RenegotiationInfo, SRP, NextProtocolNegotiation), Extensions1 = if Major == 3, Minor >=3 -> Extensions0 ++ hello_extensions(HashSigns); true -> Extensions0 @@ -1231,13 +1369,56 @@ enc_cke(#encrypted_premaster_secret{premaster_secret = PKEPMS}, _) -> <<?UINT16(PKEPMSLen), PKEPMS/binary>>; enc_cke(#client_diffie_hellman_public{dh_public = DHPublic}, _) -> Len = byte_size(DHPublic), - <<?UINT16(Len), DHPublic/binary>>. + <<?UINT16(Len), DHPublic/binary>>; +enc_cke(#client_psk_identity{identity = undefined}, _) -> + Id = <<"psk_identity">>, + Len = byte_size(Id), + <<?UINT16(Len), Id/binary>>; +enc_cke(#client_psk_identity{identity = Id}, _) -> + Len = byte_size(Id), + <<?UINT16(Len), Id/binary>>; +enc_cke(Identity = #client_dhe_psk_identity{identity = undefined}, Version) -> + enc_cke(Identity#client_dhe_psk_identity{identity = <<"psk_identity">>}, Version); +enc_cke(#client_dhe_psk_identity{identity = Id, dh_public = DHPublic}, _) -> + Len = byte_size(Id), + DHLen = byte_size(DHPublic), + <<?UINT16(Len), Id/binary, ?UINT16(DHLen), DHPublic/binary>>; +enc_cke(Identity = #client_rsa_psk_identity{identity = undefined}, Version) -> + enc_cke(Identity#client_rsa_psk_identity{identity = <<"psk_identity">>}, Version); +enc_cke(#client_rsa_psk_identity{identity = Id, exchange_keys = ExchangeKeys}, Version) -> + EncPMS = enc_cke(ExchangeKeys, Version), + Len = byte_size(Id), + <<?UINT16(Len), Id/binary, EncPMS/binary>>; +enc_cke(#client_srp_public{srp_a = A}, _) -> + Len = byte_size(A), + <<?UINT16(Len), A/binary>>. enc_server_key(#server_dh_params{dh_p = P, dh_g = G, dh_y = Y}) -> PLen = byte_size(P), GLen = byte_size(G), YLen = byte_size(Y), - <<?UINT16(PLen), P/binary, ?UINT16(GLen), G/binary, ?UINT16(YLen), Y/binary>>. + <<?UINT16(PLen), P/binary, ?UINT16(GLen), G/binary, ?UINT16(YLen), Y/binary>>; +enc_server_key(#server_psk_params{hint = PskIdentityHint}) -> + Len = byte_size(PskIdentityHint), + <<?UINT16(Len), PskIdentityHint/binary>>; +enc_server_key(Params = #server_dhe_psk_params{hint = undefined}) -> + enc_server_key(Params#server_dhe_psk_params{hint = <<>>}); +enc_server_key(#server_dhe_psk_params{ + hint = PskIdentityHint, + dh_params = #server_dh_params{dh_p = P, dh_g = G, dh_y = Y}}) -> + Len = byte_size(PskIdentityHint), + PLen = byte_size(P), + GLen = byte_size(G), + YLen = byte_size(Y), + <<?UINT16(Len), PskIdentityHint/binary, + ?UINT16(PLen), P/binary, ?UINT16(GLen), G/binary, ?UINT16(YLen), Y/binary>>; +enc_server_key(#server_srp_params{srp_n = N, srp_g = G, srp_s = S, srp_b = B}) -> + NLen = byte_size(N), + GLen = byte_size(G), + SLen = byte_size(S), + BLen = byte_size(B), + <<?UINT16(NLen), N/binary, ?UINT16(GLen), G/binary, + ?BYTE(SLen), S/binary, ?UINT16(BLen), B/binary>>. enc_sign({_, anon}, _Sign, _Version) -> <<>>; @@ -1253,13 +1434,20 @@ enc_sign(_HashSign, Sign, _Version) -> hello_extensions(RenegotiationInfo, NextProtocolNegotiation) -> hello_extensions(RenegotiationInfo) ++ next_protocol_extension(NextProtocolNegotiation). +hello_extensions(RenegotiationInfo, SRP, NextProtocolNegotiation) -> + hello_extensions(RenegotiationInfo) ++ hello_extensions(SRP) ++ next_protocol_extension(NextProtocolNegotiation). + %% Renegotiation info hello_extensions(#renegotiation_info{renegotiated_connection = undefined}) -> []; hello_extensions(#renegotiation_info{} = Info) -> [Info]; +hello_extensions(#srp{} = Info) -> + [Info]; hello_extensions(#hash_sign_algos{} = Info) -> - [Info]. + [Info]; +hello_extensions(undefined) -> + []. next_protocol_extension(undefined) -> []; @@ -1286,6 +1474,11 @@ enc_hello_extensions([#renegotiation_info{renegotiated_connection = Info} | Rest Len = InfoLen +1, enc_hello_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), ?BYTE(InfoLen), Info/binary, Acc/binary>>); +enc_hello_extensions([#srp{username = UserName} | Rest], Acc) -> + SRPLen = byte_size(UserName), + Len = SRPLen + 2, + enc_hello_extensions(Rest, <<?UINT16(?SRP_EXT), ?UINT16(Len), ?BYTE(SRPLen), UserName/binary, Acc/binary>>); + enc_hello_extensions([#hash_sign_algos{hash_sign_algos = HashSignAlgos} | Rest], Acc) -> SignAlgoList = << <<(ssl_cipher:hash_algorithm(Hash)):8, (ssl_cipher:sign_algorithm(Sign)):8>> || {Hash, Sign} <- HashSignAlgos >>, @@ -1395,6 +1588,15 @@ key_exchange_alg(rsa) -> key_exchange_alg(Alg) when Alg == dhe_rsa; Alg == dhe_dss; Alg == dh_dss; Alg == dh_rsa; Alg == dh_anon -> ?KEY_EXCHANGE_DIFFIE_HELLMAN; +key_exchange_alg(psk) -> + ?KEY_EXCHANGE_PSK; +key_exchange_alg(dhe_psk) -> + ?KEY_EXCHANGE_DHE_PSK; +key_exchange_alg(rsa_psk) -> + ?KEY_EXCHANGE_RSA_PSK; +key_exchange_alg(Alg) + when Alg == srp_rsa; Alg == srp_dss; Alg == srp_anon -> + ?KEY_EXCHANGE_SRP; key_exchange_alg(_) -> ?NULL. diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl index 2414d5b666..1fbb88f5f6 100644 --- a/lib/ssl/src/ssl_handshake.hrl +++ b/lib/ssl/src/ssl_handshake.hrl @@ -48,6 +48,7 @@ compression_method, cipher_suite, master_secret, + srp_username, is_resumable, time_stamp }). @@ -99,6 +100,7 @@ cipher_suites, % cipher_suites<2..2^16-1> compression_methods, % compression_methods<1..2^8-1>, renegotiation_info, + srp, % srp username to send hash_signs, % supported combinations of hashes/signature algos next_protocol_negotiation = undefined % [binary()] }). @@ -128,6 +130,10 @@ -define(KEY_EXCHANGE_RSA, 0). -define(KEY_EXCHANGE_DIFFIE_HELLMAN, 1). +-define(KEY_EXCHANGE_PSK, 2). +-define(KEY_EXCHANGE_DHE_PSK, 3). +-define(KEY_EXCHANGE_RSA_PSK, 4). +-define(KEY_EXCHANGE_SRP, 5). -record(server_rsa_params, { rsa_modulus, %% opaque RSA_modulus<1..2^16-1> @@ -139,7 +145,23 @@ dh_g, %% opaque DH_g<1..2^16-1> dh_y %% opaque DH_Ys<1..2^16-1> }). - + +-record(server_psk_params, { + hint + }). + +-record(server_dhe_psk_params, { + hint, + dh_params + }). + +-record(server_srp_params, { + srp_n, %% opaque srp_N<1..2^16-1> + srp_g, %% opaque srp_g<1..2^16-1> + srp_s, %% opaque srp_s<1..2^8-1> + srp_b %% opaque srp_B<1..2^16-1> + }). + -record(server_key_exchange, { exchange_keys }). @@ -209,6 +231,24 @@ dh_public }). +-record(client_psk_identity, { + identity + }). + +-record(client_dhe_psk_identity, { + identity, + dh_public + }). + +-record(client_rsa_psk_identity, { + identity, + exchange_keys + }). + +-record(client_srp_public, { + srp_a + }). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Certificate verify - RFC 4346 section 7.4.8 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -235,6 +275,15 @@ }). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% SRP RFC 5054 section 2.8.1. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-define(SRP_EXT, 12). + +-record(srp, { + username + }). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Signature Algorithms RFC 5746 section 7.4.1.4.1. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -define(SIGNATURE_ALGORITHMS_EXT, 13). diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index ed0dc34adf..96a1c8e1ce 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -90,6 +90,9 @@ cacertfile, % file() dh, % der_encoded() dhfile, % file() + user_lookup_fun, % server option, fun to lookup the user + psk_identity, % binary + srp_identity, % client option {User, Password} ciphers, % %% Local policy for the server if it want's to reuse the session %% or not. Defaluts to allways returning true. diff --git a/lib/ssl/src/ssl_srp.hrl b/lib/ssl/src/ssl_srp.hrl new file mode 100644 index 0000000000..ab2be33ab2 --- /dev/null +++ b/lib/ssl/src/ssl_srp.hrl @@ -0,0 +1,31 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2007-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% +%% + +%% +%%---------------------------------------------------------------------- +%% Purpose: Record definition for the TLS SRP protocol +%% see RFC 5054 +%%---------------------------------------------------------------------- + +-record(srp_user, { + generator :: binary(), + prime :: binary(), + salt :: binary(), + verifier :: binary() + }). diff --git a/lib/ssl/src/ssl_srp_primes.erl b/lib/ssl/src/ssl_srp_primes.erl new file mode 100644 index 0000000000..ca20a8d673 --- /dev/null +++ b/lib/ssl/src/ssl_srp_primes.erl @@ -0,0 +1,506 @@ +-module(ssl_srp_primes). + +-export([get_srp_params/1, check_srp_params/2]). + +-define(PRIME_1024, <<16#EE, 16#AF, 16#0A, 16#B9, 16#AD, 16#B3, 16#8D, + 16#D6, 16#9C, 16#33, 16#F8, 16#0A, 16#FA, 16#8F, + 16#C5, 16#E8, 16#60, 16#72, 16#61, 16#87, 16#75, + 16#FF, 16#3C, 16#0B, 16#9E, 16#A2, 16#31, 16#4C, + 16#9C, 16#25, 16#65, 16#76, 16#D6, 16#74, 16#DF, + 16#74, 16#96, 16#EA, 16#81, 16#D3, 16#38, 16#3B, + 16#48, 16#13, 16#D6, 16#92, 16#C6, 16#E0, 16#E0, + 16#D5, 16#D8, 16#E2, 16#50, 16#B9, 16#8B, 16#E4, + 16#8E, 16#49, 16#5C, 16#1D, 16#60, 16#89, 16#DA, + 16#D1, 16#5D, 16#C7, 16#D7, 16#B4, 16#61, 16#54, + 16#D6, 16#B6, 16#CE, 16#8E, 16#F4, 16#AD, 16#69, + 16#B1, 16#5D, 16#49, 16#82, 16#55, 16#9B, 16#29, + 16#7B, 16#CF, 16#18, 16#85, 16#C5, 16#29, 16#F5, + 16#66, 16#66, 16#0E, 16#57, 16#EC, 16#68, 16#ED, + 16#BC, 16#3C, 16#05, 16#72, 16#6C, 16#C0, 16#2F, + 16#D4, 16#CB, 16#F4, 16#97, 16#6E, 16#AA, 16#9A, + 16#FD, 16#51, 16#38, 16#FE, 16#83, 16#76, 16#43, + 16#5B, 16#9F, 16#C6, 16#1D, 16#2F, 16#C0, 16#EB, + 16#06, 16#E3>>). +-define(GENERATOR_1024, <<2>>). + + +-define(PRIME_1536, <<16#9D, 16#EF, 16#3C, 16#AF, 16#B9, 16#39, 16#27, + 16#7A, 16#B1, 16#F1, 16#2A, 16#86, 16#17, 16#A4, + 16#7B, 16#BB, 16#DB, 16#A5, 16#1D, 16#F4, 16#99, + 16#AC, 16#4C, 16#80, 16#BE, 16#EE, 16#A9, 16#61, + 16#4B, 16#19, 16#CC, 16#4D, 16#5F, 16#4F, 16#5F, + 16#55, 16#6E, 16#27, 16#CB, 16#DE, 16#51, 16#C6, + 16#A9, 16#4B, 16#E4, 16#60, 16#7A, 16#29, 16#15, + 16#58, 16#90, 16#3B, 16#A0, 16#D0, 16#F8, 16#43, + 16#80, 16#B6, 16#55, 16#BB, 16#9A, 16#22, 16#E8, + 16#DC, 16#DF, 16#02, 16#8A, 16#7C, 16#EC, 16#67, + 16#F0, 16#D0, 16#81, 16#34, 16#B1, 16#C8, 16#B9, + 16#79, 16#89, 16#14, 16#9B, 16#60, 16#9E, 16#0B, + 16#E3, 16#BA, 16#B6, 16#3D, 16#47, 16#54, 16#83, + 16#81, 16#DB, 16#C5, 16#B1, 16#FC, 16#76, 16#4E, + 16#3F, 16#4B, 16#53, 16#DD, 16#9D, 16#A1, 16#15, + 16#8B, 16#FD, 16#3E, 16#2B, 16#9C, 16#8C, 16#F5, + 16#6E, 16#DF, 16#01, 16#95, 16#39, 16#34, 16#96, + 16#27, 16#DB, 16#2F, 16#D5, 16#3D, 16#24, 16#B7, + 16#C4, 16#86, 16#65, 16#77, 16#2E, 16#43, 16#7D, + 16#6C, 16#7F, 16#8C, 16#E4, 16#42, 16#73, 16#4A, + 16#F7, 16#CC, 16#B7, 16#AE, 16#83, 16#7C, 16#26, + 16#4A, 16#E3, 16#A9, 16#BE, 16#B8, 16#7F, 16#8A, + 16#2F, 16#E9, 16#B8, 16#B5, 16#29, 16#2E, 16#5A, + 16#02, 16#1F, 16#FF, 16#5E, 16#91, 16#47, 16#9E, + 16#8C, 16#E7, 16#A2, 16#8C, 16#24, 16#42, 16#C6, + 16#F3, 16#15, 16#18, 16#0F, 16#93, 16#49, 16#9A, + 16#23, 16#4D, 16#CF, 16#76, 16#E3, 16#FE, 16#D1, + 16#35, 16#F9, 16#BB>>). +-define(GENERATOR_1536, <<2>>). + +-define(PRIME_2048, <<16#AC, 16#6B, 16#DB, 16#41, 16#32, 16#4A, 16#9A, + 16#9B, 16#F1, 16#66, 16#DE, 16#5E, 16#13, 16#89, + 16#58, 16#2F, 16#AF, 16#72, 16#B6, 16#65, 16#19, + 16#87, 16#EE, 16#07, 16#FC, 16#31, 16#92, 16#94, + 16#3D, 16#B5, 16#60, 16#50, 16#A3, 16#73, 16#29, + 16#CB, 16#B4, 16#A0, 16#99, 16#ED, 16#81, 16#93, + 16#E0, 16#75, 16#77, 16#67, 16#A1, 16#3D, 16#D5, + 16#23, 16#12, 16#AB, 16#4B, 16#03, 16#31, 16#0D, + 16#CD, 16#7F, 16#48, 16#A9, 16#DA, 16#04, 16#FD, + 16#50, 16#E8, 16#08, 16#39, 16#69, 16#ED, 16#B7, + 16#67, 16#B0, 16#CF, 16#60, 16#95, 16#17, 16#9A, + 16#16, 16#3A, 16#B3, 16#66, 16#1A, 16#05, 16#FB, + 16#D5, 16#FA, 16#AA, 16#E8, 16#29, 16#18, 16#A9, + 16#96, 16#2F, 16#0B, 16#93, 16#B8, 16#55, 16#F9, + 16#79, 16#93, 16#EC, 16#97, 16#5E, 16#EA, 16#A8, + 16#0D, 16#74, 16#0A, 16#DB, 16#F4, 16#FF, 16#74, + 16#73, 16#59, 16#D0, 16#41, 16#D5, 16#C3, 16#3E, + 16#A7, 16#1D, 16#28, 16#1E, 16#44, 16#6B, 16#14, + 16#77, 16#3B, 16#CA, 16#97, 16#B4, 16#3A, 16#23, + 16#FB, 16#80, 16#16, 16#76, 16#BD, 16#20, 16#7A, + 16#43, 16#6C, 16#64, 16#81, 16#F1, 16#D2, 16#B9, + 16#07, 16#87, 16#17, 16#46, 16#1A, 16#5B, 16#9D, + 16#32, 16#E6, 16#88, 16#F8, 16#77, 16#48, 16#54, + 16#45, 16#23, 16#B5, 16#24, 16#B0, 16#D5, 16#7D, + 16#5E, 16#A7, 16#7A, 16#27, 16#75, 16#D2, 16#EC, + 16#FA, 16#03, 16#2C, 16#FB, 16#DB, 16#F5, 16#2F, + 16#B3, 16#78, 16#61, 16#60, 16#27, 16#90, 16#04, + 16#E5, 16#7A, 16#E6, 16#AF, 16#87, 16#4E, 16#73, + 16#03, 16#CE, 16#53, 16#29, 16#9C, 16#CC, 16#04, + 16#1C, 16#7B, 16#C3, 16#08, 16#D8, 16#2A, 16#56, + 16#98, 16#F3, 16#A8, 16#D0, 16#C3, 16#82, 16#71, + 16#AE, 16#35, 16#F8, 16#E9, 16#DB, 16#FB, 16#B6, + 16#94, 16#B5, 16#C8, 16#03, 16#D8, 16#9F, 16#7A, + 16#E4, 16#35, 16#DE, 16#23, 16#6D, 16#52, 16#5F, + 16#54, 16#75, 16#9B, 16#65, 16#E3, 16#72, 16#FC, + 16#D6, 16#8E, 16#F2, 16#0F, 16#A7, 16#11, 16#1F, + 16#9E, 16#4A, 16#FF, 16#73>>). +-define(GENERATOR_2048, <<2>>). + +-define(PRIME_3072, <<16#FF, 16#FF, 16#FF, 16#FF, 16#FF, 16#FF, 16#FF, + 16#FF, 16#C9, 16#0F, 16#DA, 16#A2, 16#21, 16#68, + 16#C2, 16#34, 16#C4, 16#C6, 16#62, 16#8B, 16#80, + 16#DC, 16#1C, 16#D1, 16#29, 16#02, 16#4E, 16#08, + 16#8A, 16#67, 16#CC, 16#74, 16#02, 16#0B, 16#BE, + 16#A6, 16#3B, 16#13, 16#9B, 16#22, 16#51, 16#4A, + 16#08, 16#79, 16#8E, 16#34, 16#04, 16#DD, 16#EF, + 16#95, 16#19, 16#B3, 16#CD, 16#3A, 16#43, 16#1B, + 16#30, 16#2B, 16#0A, 16#6D, 16#F2, 16#5F, 16#14, + 16#37, 16#4F, 16#E1, 16#35, 16#6D, 16#6D, 16#51, + 16#C2, 16#45, 16#E4, 16#85, 16#B5, 16#76, 16#62, + 16#5E, 16#7E, 16#C6, 16#F4, 16#4C, 16#42, 16#E9, + 16#A6, 16#37, 16#ED, 16#6B, 16#0B, 16#FF, 16#5C, + 16#B6, 16#F4, 16#06, 16#B7, 16#ED, 16#EE, 16#38, + 16#6B, 16#FB, 16#5A, 16#89, 16#9F, 16#A5, 16#AE, + 16#9F, 16#24, 16#11, 16#7C, 16#4B, 16#1F, 16#E6, + 16#49, 16#28, 16#66, 16#51, 16#EC, 16#E4, 16#5B, + 16#3D, 16#C2, 16#00, 16#7C, 16#B8, 16#A1, 16#63, + 16#BF, 16#05, 16#98, 16#DA, 16#48, 16#36, 16#1C, + 16#55, 16#D3, 16#9A, 16#69, 16#16, 16#3F, 16#A8, + 16#FD, 16#24, 16#CF, 16#5F, 16#83, 16#65, 16#5D, + 16#23, 16#DC, 16#A3, 16#AD, 16#96, 16#1C, 16#62, + 16#F3, 16#56, 16#20, 16#85, 16#52, 16#BB, 16#9E, + 16#D5, 16#29, 16#07, 16#70, 16#96, 16#96, 16#6D, + 16#67, 16#0C, 16#35, 16#4E, 16#4A, 16#BC, 16#98, + 16#04, 16#F1, 16#74, 16#6C, 16#08, 16#CA, 16#18, + 16#21, 16#7C, 16#32, 16#90, 16#5E, 16#46, 16#2E, + 16#36, 16#CE, 16#3B, 16#E3, 16#9E, 16#77, 16#2C, + 16#18, 16#0E, 16#86, 16#03, 16#9B, 16#27, 16#83, + 16#A2, 16#EC, 16#07, 16#A2, 16#8F, 16#B5, 16#C5, + 16#5D, 16#F0, 16#6F, 16#4C, 16#52, 16#C9, 16#DE, + 16#2B, 16#CB, 16#F6, 16#95, 16#58, 16#17, 16#18, + 16#39, 16#95, 16#49, 16#7C, 16#EA, 16#95, 16#6A, + 16#E5, 16#15, 16#D2, 16#26, 16#18, 16#98, 16#FA, + 16#05, 16#10, 16#15, 16#72, 16#8E, 16#5A, 16#8A, + 16#AA, 16#C4, 16#2D, 16#AD, 16#33, 16#17, 16#0D, + 16#04, 16#50, 16#7A, 16#33, 16#A8, 16#55, 16#21, + 16#AB, 16#DF, 16#1C, 16#BA, 16#64, 16#EC, 16#FB, + 16#85, 16#04, 16#58, 16#DB, 16#EF, 16#0A, 16#8A, + 16#EA, 16#71, 16#57, 16#5D, 16#06, 16#0C, 16#7D, + 16#B3, 16#97, 16#0F, 16#85, 16#A6, 16#E1, 16#E4, + 16#C7, 16#AB, 16#F5, 16#AE, 16#8C, 16#DB, 16#09, + 16#33, 16#D7, 16#1E, 16#8C, 16#94, 16#E0, 16#4A, + 16#25, 16#61, 16#9D, 16#CE, 16#E3, 16#D2, 16#26, + 16#1A, 16#D2, 16#EE, 16#6B, 16#F1, 16#2F, 16#FA, + 16#06, 16#D9, 16#8A, 16#08, 16#64, 16#D8, 16#76, + 16#02, 16#73, 16#3E, 16#C8, 16#6A, 16#64, 16#52, + 16#1F, 16#2B, 16#18, 16#17, 16#7B, 16#20, 16#0C, + 16#BB, 16#E1, 16#17, 16#57, 16#7A, 16#61, 16#5D, + 16#6C, 16#77, 16#09, 16#88, 16#C0, 16#BA, 16#D9, + 16#46, 16#E2, 16#08, 16#E2, 16#4F, 16#A0, 16#74, + 16#E5, 16#AB, 16#31, 16#43, 16#DB, 16#5B, 16#FC, + 16#E0, 16#FD, 16#10, 16#8E, 16#4B, 16#82, 16#D1, + 16#20, 16#A9, 16#3A, 16#D2, 16#CA, 16#FF, 16#FF, + 16#FF, 16#FF, 16#FF, 16#FF, 16#FF, 16#FF>>). +-define(GENERATOR_3072, <<5>>). + +-define(PRIME_4096, <<16#FF, 16#FF, 16#FF, 16#FF, 16#FF, 16#FF, 16#FF, + 16#FF, 16#C9, 16#0F, 16#DA, 16#A2, 16#21, 16#68, + 16#C2, 16#34, 16#C4, 16#C6, 16#62, 16#8B, 16#80, + 16#DC, 16#1C, 16#D1, 16#29, 16#02, 16#4E, 16#08, + 16#8A, 16#67, 16#CC, 16#74, 16#02, 16#0B, 16#BE, + 16#A6, 16#3B, 16#13, 16#9B, 16#22, 16#51, 16#4A, + 16#08, 16#79, 16#8E, 16#34, 16#04, 16#DD, 16#EF, + 16#95, 16#19, 16#B3, 16#CD, 16#3A, 16#43, 16#1B, + 16#30, 16#2B, 16#0A, 16#6D, 16#F2, 16#5F, 16#14, + 16#37, 16#4F, 16#E1, 16#35, 16#6D, 16#6D, 16#51, + 16#C2, 16#45, 16#E4, 16#85, 16#B5, 16#76, 16#62, + 16#5E, 16#7E, 16#C6, 16#F4, 16#4C, 16#42, 16#E9, + 16#A6, 16#37, 16#ED, 16#6B, 16#0B, 16#FF, 16#5C, + 16#B6, 16#F4, 16#06, 16#B7, 16#ED, 16#EE, 16#38, + 16#6B, 16#FB, 16#5A, 16#89, 16#9F, 16#A5, 16#AE, + 16#9F, 16#24, 16#11, 16#7C, 16#4B, 16#1F, 16#E6, + 16#49, 16#28, 16#66, 16#51, 16#EC, 16#E4, 16#5B, + 16#3D, 16#C2, 16#00, 16#7C, 16#B8, 16#A1, 16#63, + 16#BF, 16#05, 16#98, 16#DA, 16#48, 16#36, 16#1C, + 16#55, 16#D3, 16#9A, 16#69, 16#16, 16#3F, 16#A8, + 16#FD, 16#24, 16#CF, 16#5F, 16#83, 16#65, 16#5D, + 16#23, 16#DC, 16#A3, 16#AD, 16#96, 16#1C, 16#62, + 16#F3, 16#56, 16#20, 16#85, 16#52, 16#BB, 16#9E, + 16#D5, 16#29, 16#07, 16#70, 16#96, 16#96, 16#6D, + 16#67, 16#0C, 16#35, 16#4E, 16#4A, 16#BC, 16#98, + 16#04, 16#F1, 16#74, 16#6C, 16#08, 16#CA, 16#18, + 16#21, 16#7C, 16#32, 16#90, 16#5E, 16#46, 16#2E, + 16#36, 16#CE, 16#3B, 16#E3, 16#9E, 16#77, 16#2C, + 16#18, 16#0E, 16#86, 16#03, 16#9B, 16#27, 16#83, + 16#A2, 16#EC, 16#07, 16#A2, 16#8F, 16#B5, 16#C5, + 16#5D, 16#F0, 16#6F, 16#4C, 16#52, 16#C9, 16#DE, + 16#2B, 16#CB, 16#F6, 16#95, 16#58, 16#17, 16#18, + 16#39, 16#95, 16#49, 16#7C, 16#EA, 16#95, 16#6A, + 16#E5, 16#15, 16#D2, 16#26, 16#18, 16#98, 16#FA, + 16#05, 16#10, 16#15, 16#72, 16#8E, 16#5A, 16#8A, + 16#AA, 16#C4, 16#2D, 16#AD, 16#33, 16#17, 16#0D, + 16#04, 16#50, 16#7A, 16#33, 16#A8, 16#55, 16#21, + 16#AB, 16#DF, 16#1C, 16#BA, 16#64, 16#EC, 16#FB, + 16#85, 16#04, 16#58, 16#DB, 16#EF, 16#0A, 16#8A, + 16#EA, 16#71, 16#57, 16#5D, 16#06, 16#0C, 16#7D, + 16#B3, 16#97, 16#0F, 16#85, 16#A6, 16#E1, 16#E4, + 16#C7, 16#AB, 16#F5, 16#AE, 16#8C, 16#DB, 16#09, + 16#33, 16#D7, 16#1E, 16#8C, 16#94, 16#E0, 16#4A, + 16#25, 16#61, 16#9D, 16#CE, 16#E3, 16#D2, 16#26, + 16#1A, 16#D2, 16#EE, 16#6B, 16#F1, 16#2F, 16#FA, + 16#06, 16#D9, 16#8A, 16#08, 16#64, 16#D8, 16#76, + 16#02, 16#73, 16#3E, 16#C8, 16#6A, 16#64, 16#52, + 16#1F, 16#2B, 16#18, 16#17, 16#7B, 16#20, 16#0C, + 16#BB, 16#E1, 16#17, 16#57, 16#7A, 16#61, 16#5D, + 16#6C, 16#77, 16#09, 16#88, 16#C0, 16#BA, 16#D9, + 16#46, 16#E2, 16#08, 16#E2, 16#4F, 16#A0, 16#74, + 16#E5, 16#AB, 16#31, 16#43, 16#DB, 16#5B, 16#FC, + 16#E0, 16#FD, 16#10, 16#8E, 16#4B, 16#82, 16#D1, + 16#20, 16#A9, 16#21, 16#08, 16#01, 16#1A, 16#72, + 16#3C, 16#12, 16#A7, 16#87, 16#E6, 16#D7, 16#88, + 16#71, 16#9A, 16#10, 16#BD, 16#BA, 16#5B, 16#26, + 16#99, 16#C3, 16#27, 16#18, 16#6A, 16#F4, 16#E2, + 16#3C, 16#1A, 16#94, 16#68, 16#34, 16#B6, 16#15, + 16#0B, 16#DA, 16#25, 16#83, 16#E9, 16#CA, 16#2A, + 16#D4, 16#4C, 16#E8, 16#DB, 16#BB, 16#C2, 16#DB, + 16#04, 16#DE, 16#8E, 16#F9, 16#2E, 16#8E, 16#FC, + 16#14, 16#1F, 16#BE, 16#CA, 16#A6, 16#28, 16#7C, + 16#59, 16#47, 16#4E, 16#6B, 16#C0, 16#5D, 16#99, + 16#B2, 16#96, 16#4F, 16#A0, 16#90, 16#C3, 16#A2, + 16#23, 16#3B, 16#A1, 16#86, 16#51, 16#5B, 16#E7, + 16#ED, 16#1F, 16#61, 16#29, 16#70, 16#CE, 16#E2, + 16#D7, 16#AF, 16#B8, 16#1B, 16#DD, 16#76, 16#21, + 16#70, 16#48, 16#1C, 16#D0, 16#06, 16#91, 16#27, + 16#D5, 16#B0, 16#5A, 16#A9, 16#93, 16#B4, 16#EA, + 16#98, 16#8D, 16#8F, 16#DD, 16#C1, 16#86, 16#FF, + 16#B7, 16#DC, 16#90, 16#A6, 16#C0, 16#8F, 16#4D, + 16#F4, 16#35, 16#C9, 16#34, 16#06, 16#31, 16#99, + 16#FF, 16#FF, 16#FF, 16#FF, 16#FF, 16#FF, 16#FF, + 16#FF>>). +-define(GENERATOR_4096, <<5>>). + +-define(PRIME_6144, <<16#FF, 16#FF, 16#FF, 16#FF, 16#FF, 16#FF, 16#FF, + 16#FF, 16#C9, 16#0F, 16#DA, 16#A2, 16#21, 16#68, + 16#C2, 16#34, 16#C4, 16#C6, 16#62, 16#8B, 16#80, + 16#DC, 16#1C, 16#D1, 16#29, 16#02, 16#4E, 16#08, + 16#8A, 16#67, 16#CC, 16#74, 16#02, 16#0B, 16#BE, + 16#A6, 16#3B, 16#13, 16#9B, 16#22, 16#51, 16#4A, + 16#08, 16#79, 16#8E, 16#34, 16#04, 16#DD, 16#EF, + 16#95, 16#19, 16#B3, 16#CD, 16#3A, 16#43, 16#1B, + 16#30, 16#2B, 16#0A, 16#6D, 16#F2, 16#5F, 16#14, + 16#37, 16#4F, 16#E1, 16#35, 16#6D, 16#6D, 16#51, + 16#C2, 16#45, 16#E4, 16#85, 16#B5, 16#76, 16#62, + 16#5E, 16#7E, 16#C6, 16#F4, 16#4C, 16#42, 16#E9, + 16#A6, 16#37, 16#ED, 16#6B, 16#0B, 16#FF, 16#5C, + 16#B6, 16#F4, 16#06, 16#B7, 16#ED, 16#EE, 16#38, + 16#6B, 16#FB, 16#5A, 16#89, 16#9F, 16#A5, 16#AE, + 16#9F, 16#24, 16#11, 16#7C, 16#4B, 16#1F, 16#E6, + 16#49, 16#28, 16#66, 16#51, 16#EC, 16#E4, 16#5B, + 16#3D, 16#C2, 16#00, 16#7C, 16#B8, 16#A1, 16#63, + 16#BF, 16#05, 16#98, 16#DA, 16#48, 16#36, 16#1C, + 16#55, 16#D3, 16#9A, 16#69, 16#16, 16#3F, 16#A8, + 16#FD, 16#24, 16#CF, 16#5F, 16#83, 16#65, 16#5D, + 16#23, 16#DC, 16#A3, 16#AD, 16#96, 16#1C, 16#62, + 16#F3, 16#56, 16#20, 16#85, 16#52, 16#BB, 16#9E, + 16#D5, 16#29, 16#07, 16#70, 16#96, 16#96, 16#6D, + 16#67, 16#0C, 16#35, 16#4E, 16#4A, 16#BC, 16#98, + 16#04, 16#F1, 16#74, 16#6C, 16#08, 16#CA, 16#18, + 16#21, 16#7C, 16#32, 16#90, 16#5E, 16#46, 16#2E, + 16#36, 16#CE, 16#3B, 16#E3, 16#9E, 16#77, 16#2C, + 16#18, 16#0E, 16#86, 16#03, 16#9B, 16#27, 16#83, + 16#A2, 16#EC, 16#07, 16#A2, 16#8F, 16#B5, 16#C5, + 16#5D, 16#F0, 16#6F, 16#4C, 16#52, 16#C9, 16#DE, + 16#2B, 16#CB, 16#F6, 16#95, 16#58, 16#17, 16#18, + 16#39, 16#95, 16#49, 16#7C, 16#EA, 16#95, 16#6A, + 16#E5, 16#15, 16#D2, 16#26, 16#18, 16#98, 16#FA, + 16#05, 16#10, 16#15, 16#72, 16#8E, 16#5A, 16#8A, + 16#AA, 16#C4, 16#2D, 16#AD, 16#33, 16#17, 16#0D, + 16#04, 16#50, 16#7A, 16#33, 16#A8, 16#55, 16#21, + 16#AB, 16#DF, 16#1C, 16#BA, 16#64, 16#EC, 16#FB, + 16#85, 16#04, 16#58, 16#DB, 16#EF, 16#0A, 16#8A, + 16#EA, 16#71, 16#57, 16#5D, 16#06, 16#0C, 16#7D, + 16#B3, 16#97, 16#0F, 16#85, 16#A6, 16#E1, 16#E4, + 16#C7, 16#AB, 16#F5, 16#AE, 16#8C, 16#DB, 16#09, + 16#33, 16#D7, 16#1E, 16#8C, 16#94, 16#E0, 16#4A, + 16#25, 16#61, 16#9D, 16#CE, 16#E3, 16#D2, 16#26, + 16#1A, 16#D2, 16#EE, 16#6B, 16#F1, 16#2F, 16#FA, + 16#06, 16#D9, 16#8A, 16#08, 16#64, 16#D8, 16#76, + 16#02, 16#73, 16#3E, 16#C8, 16#6A, 16#64, 16#52, + 16#1F, 16#2B, 16#18, 16#17, 16#7B, 16#20, 16#0C, + 16#BB, 16#E1, 16#17, 16#57, 16#7A, 16#61, 16#5D, + 16#6C, 16#77, 16#09, 16#88, 16#C0, 16#BA, 16#D9, + 16#46, 16#E2, 16#08, 16#E2, 16#4F, 16#A0, 16#74, + 16#E5, 16#AB, 16#31, 16#43, 16#DB, 16#5B, 16#FC, + 16#E0, 16#FD, 16#10, 16#8E, 16#4B, 16#82, 16#D1, + 16#20, 16#A9, 16#21, 16#08, 16#01, 16#1A, 16#72, + 16#3C, 16#12, 16#A7, 16#87, 16#E6, 16#D7, 16#88, + 16#71, 16#9A, 16#10, 16#BD, 16#BA, 16#5B, 16#26, + 16#99, 16#C3, 16#27, 16#18, 16#6A, 16#F4, 16#E2, + 16#3C, 16#1A, 16#94, 16#68, 16#34, 16#B6, 16#15, + 16#0B, 16#DA, 16#25, 16#83, 16#E9, 16#CA, 16#2A, + 16#D4, 16#4C, 16#E8, 16#DB, 16#BB, 16#C2, 16#DB, + 16#04, 16#DE, 16#8E, 16#F9, 16#2E, 16#8E, 16#FC, + 16#14, 16#1F, 16#BE, 16#CA, 16#A6, 16#28, 16#7C, + 16#59, 16#47, 16#4E, 16#6B, 16#C0, 16#5D, 16#99, + 16#B2, 16#96, 16#4F, 16#A0, 16#90, 16#C3, 16#A2, + 16#23, 16#3B, 16#A1, 16#86, 16#51, 16#5B, 16#E7, + 16#ED, 16#1F, 16#61, 16#29, 16#70, 16#CE, 16#E2, + 16#D7, 16#AF, 16#B8, 16#1B, 16#DD, 16#76, 16#21, + 16#70, 16#48, 16#1C, 16#D0, 16#06, 16#91, 16#27, + 16#D5, 16#B0, 16#5A, 16#A9, 16#93, 16#B4, 16#EA, + 16#98, 16#8D, 16#8F, 16#DD, 16#C1, 16#86, 16#FF, + 16#B7, 16#DC, 16#90, 16#A6, 16#C0, 16#8F, 16#4D, + 16#F4, 16#35, 16#C9, 16#34, 16#02, 16#84, 16#92, + 16#36, 16#C3, 16#FA, 16#B4, 16#D2, 16#7C, 16#70, + 16#26, 16#C1, 16#D4, 16#DC, 16#B2, 16#60, 16#26, + 16#46, 16#DE, 16#C9, 16#75, 16#1E, 16#76, 16#3D, + 16#BA, 16#37, 16#BD, 16#F8, 16#FF, 16#94, 16#06, + 16#AD, 16#9E, 16#53, 16#0E, 16#E5, 16#DB, 16#38, + 16#2F, 16#41, 16#30, 16#01, 16#AE, 16#B0, 16#6A, + 16#53, 16#ED, 16#90, 16#27, 16#D8, 16#31, 16#17, + 16#97, 16#27, 16#B0, 16#86, 16#5A, 16#89, 16#18, + 16#DA, 16#3E, 16#DB, 16#EB, 16#CF, 16#9B, 16#14, + 16#ED, 16#44, 16#CE, 16#6C, 16#BA, 16#CE, 16#D4, + 16#BB, 16#1B, 16#DB, 16#7F, 16#14, 16#47, 16#E6, + 16#CC, 16#25, 16#4B, 16#33, 16#20, 16#51, 16#51, + 16#2B, 16#D7, 16#AF, 16#42, 16#6F, 16#B8, 16#F4, + 16#01, 16#37, 16#8C, 16#D2, 16#BF, 16#59, 16#83, + 16#CA, 16#01, 16#C6, 16#4B, 16#92, 16#EC, 16#F0, + 16#32, 16#EA, 16#15, 16#D1, 16#72, 16#1D, 16#03, + 16#F4, 16#82, 16#D7, 16#CE, 16#6E, 16#74, 16#FE, + 16#F6, 16#D5, 16#5E, 16#70, 16#2F, 16#46, 16#98, + 16#0C, 16#82, 16#B5, 16#A8, 16#40, 16#31, 16#90, + 16#0B, 16#1C, 16#9E, 16#59, 16#E7, 16#C9, 16#7F, + 16#BE, 16#C7, 16#E8, 16#F3, 16#23, 16#A9, 16#7A, + 16#7E, 16#36, 16#CC, 16#88, 16#BE, 16#0F, 16#1D, + 16#45, 16#B7, 16#FF, 16#58, 16#5A, 16#C5, 16#4B, + 16#D4, 16#07, 16#B2, 16#2B, 16#41, 16#54, 16#AA, + 16#CC, 16#8F, 16#6D, 16#7E, 16#BF, 16#48, 16#E1, + 16#D8, 16#14, 16#CC, 16#5E, 16#D2, 16#0F, 16#80, + 16#37, 16#E0, 16#A7, 16#97, 16#15, 16#EE, 16#F2, + 16#9B, 16#E3, 16#28, 16#06, 16#A1, 16#D5, 16#8B, + 16#B7, 16#C5, 16#DA, 16#76, 16#F5, 16#50, 16#AA, + 16#3D, 16#8A, 16#1F, 16#BF, 16#F0, 16#EB, 16#19, + 16#CC, 16#B1, 16#A3, 16#13, 16#D5, 16#5C, 16#DA, + 16#56, 16#C9, 16#EC, 16#2E, 16#F2, 16#96, 16#32, + 16#38, 16#7F, 16#E8, 16#D7, 16#6E, 16#3C, 16#04, + 16#68, 16#04, 16#3E, 16#8F, 16#66, 16#3F, 16#48, + 16#60, 16#EE, 16#12, 16#BF, 16#2D, 16#5B, 16#0B, + 16#74, 16#74, 16#D6, 16#E6, 16#94, 16#F9, 16#1E, + 16#6D, 16#CC, 16#40, 16#24, 16#FF, 16#FF, 16#FF, + 16#FF, 16#FF, 16#FF, 16#FF, 16#FF>>). +-define(GENERATOR_6144, <<5>>). + +-define(PRIME_8192, <<16#FF, 16#FF, 16#FF, 16#FF, 16#FF, 16#FF, 16#FF, + 16#FF, 16#C9, 16#0F, 16#DA, 16#A2, 16#21, 16#68, + 16#C2, 16#34, 16#C4, 16#C6, 16#62, 16#8B, 16#80, + 16#DC, 16#1C, 16#D1, 16#29, 16#02, 16#4E, 16#08, + 16#8A, 16#67, 16#CC, 16#74, 16#02, 16#0B, 16#BE, + 16#A6, 16#3B, 16#13, 16#9B, 16#22, 16#51, 16#4A, + 16#08, 16#79, 16#8E, 16#34, 16#04, 16#DD, 16#EF, + 16#95, 16#19, 16#B3, 16#CD, 16#3A, 16#43, 16#1B, + 16#30, 16#2B, 16#0A, 16#6D, 16#F2, 16#5F, 16#14, + 16#37, 16#4F, 16#E1, 16#35, 16#6D, 16#6D, 16#51, + 16#C2, 16#45, 16#E4, 16#85, 16#B5, 16#76, 16#62, + 16#5E, 16#7E, 16#C6, 16#F4, 16#4C, 16#42, 16#E9, + 16#A6, 16#37, 16#ED, 16#6B, 16#0B, 16#FF, 16#5C, + 16#B6, 16#F4, 16#06, 16#B7, 16#ED, 16#EE, 16#38, + 16#6B, 16#FB, 16#5A, 16#89, 16#9F, 16#A5, 16#AE, + 16#9F, 16#24, 16#11, 16#7C, 16#4B, 16#1F, 16#E6, + 16#49, 16#28, 16#66, 16#51, 16#EC, 16#E4, 16#5B, + 16#3D, 16#C2, 16#00, 16#7C, 16#B8, 16#A1, 16#63, + 16#BF, 16#05, 16#98, 16#DA, 16#48, 16#36, 16#1C, + 16#55, 16#D3, 16#9A, 16#69, 16#16, 16#3F, 16#A8, + 16#FD, 16#24, 16#CF, 16#5F, 16#83, 16#65, 16#5D, + 16#23, 16#DC, 16#A3, 16#AD, 16#96, 16#1C, 16#62, + 16#F3, 16#56, 16#20, 16#85, 16#52, 16#BB, 16#9E, + 16#D5, 16#29, 16#07, 16#70, 16#96, 16#96, 16#6D, + 16#67, 16#0C, 16#35, 16#4E, 16#4A, 16#BC, 16#98, + 16#04, 16#F1, 16#74, 16#6C, 16#08, 16#CA, 16#18, + 16#21, 16#7C, 16#32, 16#90, 16#5E, 16#46, 16#2E, + 16#36, 16#CE, 16#3B, 16#E3, 16#9E, 16#77, 16#2C, + 16#18, 16#0E, 16#86, 16#03, 16#9B, 16#27, 16#83, + 16#A2, 16#EC, 16#07, 16#A2, 16#8F, 16#B5, 16#C5, + 16#5D, 16#F0, 16#6F, 16#4C, 16#52, 16#C9, 16#DE, + 16#2B, 16#CB, 16#F6, 16#95, 16#58, 16#17, 16#18, + 16#39, 16#95, 16#49, 16#7C, 16#EA, 16#95, 16#6A, + 16#E5, 16#15, 16#D2, 16#26, 16#18, 16#98, 16#FA, + 16#05, 16#10, 16#15, 16#72, 16#8E, 16#5A, 16#8A, + 16#AA, 16#C4, 16#2D, 16#AD, 16#33, 16#17, 16#0D, + 16#04, 16#50, 16#7A, 16#33, 16#A8, 16#55, 16#21, + 16#AB, 16#DF, 16#1C, 16#BA, 16#64, 16#EC, 16#FB, + 16#85, 16#04, 16#58, 16#DB, 16#EF, 16#0A, 16#8A, + 16#EA, 16#71, 16#57, 16#5D, 16#06, 16#0C, 16#7D, + 16#B3, 16#97, 16#0F, 16#85, 16#A6, 16#E1, 16#E4, + 16#C7, 16#AB, 16#F5, 16#AE, 16#8C, 16#DB, 16#09, + 16#33, 16#D7, 16#1E, 16#8C, 16#94, 16#E0, 16#4A, + 16#25, 16#61, 16#9D, 16#CE, 16#E3, 16#D2, 16#26, + 16#1A, 16#D2, 16#EE, 16#6B, 16#F1, 16#2F, 16#FA, + 16#06, 16#D9, 16#8A, 16#08, 16#64, 16#D8, 16#76, + 16#02, 16#73, 16#3E, 16#C8, 16#6A, 16#64, 16#52, + 16#1F, 16#2B, 16#18, 16#17, 16#7B, 16#20, 16#0C, + 16#BB, 16#E1, 16#17, 16#57, 16#7A, 16#61, 16#5D, + 16#6C, 16#77, 16#09, 16#88, 16#C0, 16#BA, 16#D9, + 16#46, 16#E2, 16#08, 16#E2, 16#4F, 16#A0, 16#74, + 16#E5, 16#AB, 16#31, 16#43, 16#DB, 16#5B, 16#FC, + 16#E0, 16#FD, 16#10, 16#8E, 16#4B, 16#82, 16#D1, + 16#20, 16#A9, 16#21, 16#08, 16#01, 16#1A, 16#72, + 16#3C, 16#12, 16#A7, 16#87, 16#E6, 16#D7, 16#88, + 16#71, 16#9A, 16#10, 16#BD, 16#BA, 16#5B, 16#26, + 16#99, 16#C3, 16#27, 16#18, 16#6A, 16#F4, 16#E2, + 16#3C, 16#1A, 16#94, 16#68, 16#34, 16#B6, 16#15, + 16#0B, 16#DA, 16#25, 16#83, 16#E9, 16#CA, 16#2A, + 16#D4, 16#4C, 16#E8, 16#DB, 16#BB, 16#C2, 16#DB, + 16#04, 16#DE, 16#8E, 16#F9, 16#2E, 16#8E, 16#FC, + 16#14, 16#1F, 16#BE, 16#CA, 16#A6, 16#28, 16#7C, + 16#59, 16#47, 16#4E, 16#6B, 16#C0, 16#5D, 16#99, + 16#B2, 16#96, 16#4F, 16#A0, 16#90, 16#C3, 16#A2, + 16#23, 16#3B, 16#A1, 16#86, 16#51, 16#5B, 16#E7, + 16#ED, 16#1F, 16#61, 16#29, 16#70, 16#CE, 16#E2, + 16#D7, 16#AF, 16#B8, 16#1B, 16#DD, 16#76, 16#21, + 16#70, 16#48, 16#1C, 16#D0, 16#06, 16#91, 16#27, + 16#D5, 16#B0, 16#5A, 16#A9, 16#93, 16#B4, 16#EA, + 16#98, 16#8D, 16#8F, 16#DD, 16#C1, 16#86, 16#FF, + 16#B7, 16#DC, 16#90, 16#A6, 16#C0, 16#8F, 16#4D, + 16#F4, 16#35, 16#C9, 16#34, 16#02, 16#84, 16#92, + 16#36, 16#C3, 16#FA, 16#B4, 16#D2, 16#7C, 16#70, + 16#26, 16#C1, 16#D4, 16#DC, 16#B2, 16#60, 16#26, + 16#46, 16#DE, 16#C9, 16#75, 16#1E, 16#76, 16#3D, + 16#BA, 16#37, 16#BD, 16#F8, 16#FF, 16#94, 16#06, + 16#AD, 16#9E, 16#53, 16#0E, 16#E5, 16#DB, 16#38, + 16#2F, 16#41, 16#30, 16#01, 16#AE, 16#B0, 16#6A, + 16#53, 16#ED, 16#90, 16#27, 16#D8, 16#31, 16#17, + 16#97, 16#27, 16#B0, 16#86, 16#5A, 16#89, 16#18, + 16#DA, 16#3E, 16#DB, 16#EB, 16#CF, 16#9B, 16#14, + 16#ED, 16#44, 16#CE, 16#6C, 16#BA, 16#CE, 16#D4, + 16#BB, 16#1B, 16#DB, 16#7F, 16#14, 16#47, 16#E6, + 16#CC, 16#25, 16#4B, 16#33, 16#20, 16#51, 16#51, + 16#2B, 16#D7, 16#AF, 16#42, 16#6F, 16#B8, 16#F4, + 16#01, 16#37, 16#8C, 16#D2, 16#BF, 16#59, 16#83, + 16#CA, 16#01, 16#C6, 16#4B, 16#92, 16#EC, 16#F0, + 16#32, 16#EA, 16#15, 16#D1, 16#72, 16#1D, 16#03, + 16#F4, 16#82, 16#D7, 16#CE, 16#6E, 16#74, 16#FE, + 16#F6, 16#D5, 16#5E, 16#70, 16#2F, 16#46, 16#98, + 16#0C, 16#82, 16#B5, 16#A8, 16#40, 16#31, 16#90, + 16#0B, 16#1C, 16#9E, 16#59, 16#E7, 16#C9, 16#7F, + 16#BE, 16#C7, 16#E8, 16#F3, 16#23, 16#A9, 16#7A, + 16#7E, 16#36, 16#CC, 16#88, 16#BE, 16#0F, 16#1D, + 16#45, 16#B7, 16#FF, 16#58, 16#5A, 16#C5, 16#4B, + 16#D4, 16#07, 16#B2, 16#2B, 16#41, 16#54, 16#AA, + 16#CC, 16#8F, 16#6D, 16#7E, 16#BF, 16#48, 16#E1, + 16#D8, 16#14, 16#CC, 16#5E, 16#D2, 16#0F, 16#80, + 16#37, 16#E0, 16#A7, 16#97, 16#15, 16#EE, 16#F2, + 16#9B, 16#E3, 16#28, 16#06, 16#A1, 16#D5, 16#8B, + 16#B7, 16#C5, 16#DA, 16#76, 16#F5, 16#50, 16#AA, + 16#3D, 16#8A, 16#1F, 16#BF, 16#F0, 16#EB, 16#19, + 16#CC, 16#B1, 16#A3, 16#13, 16#D5, 16#5C, 16#DA, + 16#56, 16#C9, 16#EC, 16#2E, 16#F2, 16#96, 16#32, + 16#38, 16#7F, 16#E8, 16#D7, 16#6E, 16#3C, 16#04, + 16#68, 16#04, 16#3E, 16#8F, 16#66, 16#3F, 16#48, + 16#60, 16#EE, 16#12, 16#BF, 16#2D, 16#5B, 16#0B, + 16#74, 16#74, 16#D6, 16#E6, 16#94, 16#F9, 16#1E, + 16#6D, 16#BE, 16#11, 16#59, 16#74, 16#A3, 16#92, + 16#6F, 16#12, 16#FE, 16#E5, 16#E4, 16#38, 16#77, + 16#7C, 16#B6, 16#A9, 16#32, 16#DF, 16#8C, 16#D8, + 16#BE, 16#C4, 16#D0, 16#73, 16#B9, 16#31, 16#BA, + 16#3B, 16#C8, 16#32, 16#B6, 16#8D, 16#9D, 16#D3, + 16#00, 16#74, 16#1F, 16#A7, 16#BF, 16#8A, 16#FC, + 16#47, 16#ED, 16#25, 16#76, 16#F6, 16#93, 16#6B, + 16#A4, 16#24, 16#66, 16#3A, 16#AB, 16#63, 16#9C, + 16#5A, 16#E4, 16#F5, 16#68, 16#34, 16#23, 16#B4, + 16#74, 16#2B, 16#F1, 16#C9, 16#78, 16#23, 16#8F, + 16#16, 16#CB, 16#E3, 16#9D, 16#65, 16#2D, 16#E3, + 16#FD, 16#B8, 16#BE, 16#FC, 16#84, 16#8A, 16#D9, + 16#22, 16#22, 16#2E, 16#04, 16#A4, 16#03, 16#7C, + 16#07, 16#13, 16#EB, 16#57, 16#A8, 16#1A, 16#23, + 16#F0, 16#C7, 16#34, 16#73, 16#FC, 16#64, 16#6C, + 16#EA, 16#30, 16#6B, 16#4B, 16#CB, 16#C8, 16#86, + 16#2F, 16#83, 16#85, 16#DD, 16#FA, 16#9D, 16#4B, + 16#7F, 16#A2, 16#C0, 16#87, 16#E8, 16#79, 16#68, + 16#33, 16#03, 16#ED, 16#5B, 16#DD, 16#3A, 16#06, + 16#2B, 16#3C, 16#F5, 16#B3, 16#A2, 16#78, 16#A6, + 16#6D, 16#2A, 16#13, 16#F8, 16#3F, 16#44, 16#F8, + 16#2D, 16#DF, 16#31, 16#0E, 16#E0, 16#74, 16#AB, + 16#6A, 16#36, 16#45, 16#97, 16#E8, 16#99, 16#A0, + 16#25, 16#5D, 16#C1, 16#64, 16#F3, 16#1C, 16#C5, + 16#08, 16#46, 16#85, 16#1D, 16#F9, 16#AB, 16#48, + 16#19, 16#5D, 16#ED, 16#7E, 16#A1, 16#B1, 16#D5, + 16#10, 16#BD, 16#7E, 16#E7, 16#4D, 16#73, 16#FA, + 16#F3, 16#6B, 16#C3, 16#1E, 16#CF, 16#A2, 16#68, + 16#35, 16#90, 16#46, 16#F4, 16#EB, 16#87, 16#9F, + 16#92, 16#40, 16#09, 16#43, 16#8B, 16#48, 16#1C, + 16#6C, 16#D7, 16#88, 16#9A, 16#00, 16#2E, 16#D5, + 16#EE, 16#38, 16#2B, 16#C9, 16#19, 16#0D, 16#A6, + 16#FC, 16#02, 16#6E, 16#47, 16#95, 16#58, 16#E4, + 16#47, 16#56, 16#77, 16#E9, 16#AA, 16#9E, 16#30, + 16#50, 16#E2, 16#76, 16#56, 16#94, 16#DF, 16#C8, + 16#1F, 16#56, 16#E8, 16#80, 16#B9, 16#6E, 16#71, + 16#60, 16#C9, 16#80, 16#DD, 16#98, 16#ED, 16#D3, + 16#DF, 16#FF, 16#FF, 16#FF, 16#FF, 16#FF, 16#FF, + 16#FF, 16#FF>>). +-define(GENERATOR_8192, <<19>>). + +get_srp_params(srp_1024) -> {?GENERATOR_1024, ?PRIME_1024}; +get_srp_params(srp_1536) -> {?GENERATOR_1536, ?PRIME_1536}; +get_srp_params(srp_2048) -> {?GENERATOR_2048, ?PRIME_2048}; +get_srp_params(srp_3072) -> {?GENERATOR_3072, ?PRIME_3072}; +get_srp_params(srp_4096) -> {?GENERATOR_4096, ?PRIME_4096}; +get_srp_params(srp_6144) -> {?GENERATOR_6144, ?PRIME_6144}; +get_srp_params(srp_8192) -> {?GENERATOR_8192, ?PRIME_8192}. + +check_srp_params(?GENERATOR_1024, ?PRIME_1024) -> ok; +check_srp_params(?GENERATOR_1536, ?PRIME_1536) -> ok; +check_srp_params(?GENERATOR_2048, ?PRIME_2048) -> ok; +check_srp_params(?GENERATOR_3072, ?PRIME_3072) -> ok; +check_srp_params(?GENERATOR_4096, ?PRIME_4096) -> ok; +check_srp_params(?GENERATOR_6144, ?PRIME_6144) -> ok; +check_srp_params(?GENERATOR_8192, ?PRIME_8192) -> ok; +check_srp_params(_Generator, _Prime) -> + not_accepted. diff --git a/lib/ssl/src/ssl_srp_primes.hrl b/lib/ssl/src/ssl_srp_primes.hrl new file mode 100644 index 0000000000..4bd534efbf --- /dev/null +++ b/lib/ssl/src/ssl_srp_primes.hrl @@ -0,0 +1 @@ +-type srp_parameters() :: srp_1024 | srp_1536 | srp_2048 | srp_3072 | srp_4096 | srp_6144 | srp_8192. diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index 6b8f226a77..5cedde5d27 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -154,6 +154,10 @@ cipher_tests() -> ciphers_dsa_signed_certs, ciphers_dsa_signed_certs_openssl_names, anonymous_cipher_suites, + psk_cipher_suites, + psk_with_hint_cipher_suites, + srp_cipher_suites, + srp_dsa_cipher_suites, default_reject_anonymous]. error_handling_tests()-> @@ -1575,7 +1579,34 @@ anonymous_cipher_suites(Config) when is_list(Config) -> Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), Ciphers = ssl_test_lib:anonymous_suites(), run_suites(Ciphers, Version, Config, anonymous). - +%%------------------------------------------------------------------- +psk_cipher_suites() -> + [{doc, "Test the PSK ciphersuites WITHOUT server supplied identity hint"}]. +psk_cipher_suites(Config) when is_list(Config) -> + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Ciphers = ssl_test_lib:psk_suites(), + run_suites(Ciphers, Version, Config, psk). +%%------------------------------------------------------------------- +psk_with_hint_cipher_suites()-> + [{doc, "Test the PSK ciphersuites WITH server supplied identity hint"}]. +psk_with_hint_cipher_suites(Config) when is_list(Config) -> + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Ciphers = ssl_test_lib:psk_suites(), + run_suites(Ciphers, Version, Config, psk_with_hint). +%%------------------------------------------------------------------- +srp_cipher_suites()-> + [{doc, "Test the SRP ciphersuites"}]. +srp_cipher_suites(Config) when is_list(Config) -> + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Ciphers = ssl_test_lib:srp_suites(), + run_suites(Ciphers, Version, Config, srp). +%%------------------------------------------------------------------- +srp_dsa_cipher_suites()-> + [{doc, "Test the SRP DSA ciphersuites"}]. +srp_dsa_cipher_suites(Config) when is_list(Config) -> + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Ciphers = ssl_test_lib:srp_dss_suites(), + run_suites(Ciphers, Version, Config, srp_dsa). %%-------------------------------------------------------------------- default_reject_anonymous()-> [{doc,"Test that by default anonymous cipher suites are rejected "}]. @@ -3113,7 +3144,19 @@ run_suites(Ciphers, Version, Config, Type) -> anonymous -> %% No certs in opts! {?config(client_opts, Config), - ?config(server_anon, Config)} + ?config(server_anon, Config)}; + psk -> + {?config(client_psk, Config), + ?config(server_psk, Config)}; + psk_with_hint -> + {?config(client_psk, Config), + ?config(server_psk_hint, Config)}; + srp -> + {?config(client_srp, Config), + ?config(server_srp, Config)}; + srp_dsa -> + {?config(client_srp_dsa, Config), + ?config(server_srp_dsa, Config)} end, Result = lists:map(fun(Cipher) -> diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index 76b302b1cb..d655d7659e 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -281,6 +281,13 @@ wait_for_result(Pid, Msg) -> %% Unexpected end. +user_lookup(psk, _Identity, UserState) -> + {ok, UserState}; +user_lookup(srp, Username, _UserState) -> + Salt = ssl:random_bytes(16), + UserPassHash = crypto:sha([Salt, crypto:sha([Username, <<$:>>, <<"secret">>])]), + {ok, {srp_1024, Salt, UserPassHash}}. + cert_options(Config) -> ClientCaCertFile = filename:join([?config(priv_dir, Config), "client", "cacerts.pem"]), @@ -307,6 +314,7 @@ cert_options(Config) -> "badcert.pem"]), BadKeyFile = filename:join([?config(priv_dir, Config), "badkey.pem"]), + PskSharedSecret = <<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15>>, [{client_opts, [{ssl_imp, new},{reuseaddr, true}]}, {client_verification_opts, [{cacertfile, ClientCaCertFile}, {certfile, ClientCertFile}, @@ -319,6 +327,24 @@ cert_options(Config) -> {server_opts, [{ssl_imp, new},{reuseaddr, true}, {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]}, {server_anon, [{ssl_imp, new},{reuseaddr, true}, {ciphers, anonymous_suites()}]}, + {client_psk, [{ssl_imp, new},{reuseaddr, true}, + {psk_identity, "Test-User"}, + {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}}]}, + {server_psk, [{ssl_imp, new},{reuseaddr, true}, + {certfile, ServerCertFile}, {keyfile, ServerKeyFile}, + {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}}, + {ciphers, psk_suites()}]}, + {server_psk_hint, [{ssl_imp, new},{reuseaddr, true}, + {certfile, ServerCertFile}, {keyfile, ServerKeyFile}, + {psk_identity, "HINT"}, + {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}}, + {ciphers, psk_suites()}]}, + {client_srp, [{ssl_imp, new},{reuseaddr, true}, + {srp_identity, {"Test-User", "secret"}}]}, + {server_srp, [{ssl_imp, new},{reuseaddr, true}, + {certfile, ServerCertFile}, {keyfile, ServerKeyFile}, + {user_lookup_fun, {fun user_lookup/3, undefined}}, + {ciphers, srp_suites()}]}, {server_verification_opts, [{ssl_imp, new},{reuseaddr, true}, {cacertfile, ServerCaCertFile}, {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]}, @@ -356,7 +382,16 @@ make_dsa_cert(Config) -> {verify, verify_peer}]}, {client_dsa_opts, [{ssl_imp, new},{reuseaddr, true}, {cacertfile, ClientCaCertFile}, - {certfile, ClientCertFile}, {keyfile, ClientKeyFile}]} + {certfile, ClientCertFile}, {keyfile, ClientKeyFile}]}, + {server_srp_dsa, [{ssl_imp, new},{reuseaddr, true}, + {cacertfile, ServerCaCertFile}, + {certfile, ServerCertFile}, {keyfile, ServerKeyFile}, + {user_lookup_fun, {fun user_lookup/3, undefined}}, + {ciphers, srp_dss_suites()}]}, + {client_srp_dsa, [{ssl_imp, new},{reuseaddr, true}, + {srp_identity, {"Test-User", "secret"}}, + {cacertfile, ClientCaCertFile}, + {certfile, ClientCertFile}, {keyfile, ClientKeyFile}]} | Config]. @@ -675,6 +710,33 @@ anonymous_suites() -> {dh_anon, aes_128_cbc, sha}, {dh_anon, aes_256_cbc, sha}]. +psk_suites() -> + [{psk, rc4_128, sha}, + {psk, '3des_ede_cbc', sha}, + {psk, aes_128_cbc, sha}, + {psk, aes_256_cbc, sha}, + {dhe_psk, rc4_128, sha}, + {dhe_psk, '3des_ede_cbc', sha}, + {dhe_psk, aes_128_cbc, sha}, + {dhe_psk, aes_256_cbc, sha}, + {rsa_psk, rc4_128, sha}, + {rsa_psk, '3des_ede_cbc', sha}, + {rsa_psk, aes_128_cbc, sha}, + {rsa_psk, aes_256_cbc, sha}]. + +srp_suites() -> + [{srp_anon, '3des_ede_cbc', sha}, + {srp_rsa, '3des_ede_cbc', sha}, + {srp_anon, aes_128_cbc, sha}, + {srp_rsa, aes_128_cbc, sha}, + {srp_anon, aes_256_cbc, sha}, + {srp_rsa, aes_256_cbc, sha}]. + +srp_dss_suites() -> + [{srp_dss, '3des_ede_cbc', sha}, + {srp_dss, aes_128_cbc, sha}, + {srp_dss, aes_256_cbc, sha}]. + pem_to_der(File) -> {ok, PemBin} = file:read_file(File), public_key:pem_decode(PemBin). diff --git a/lib/stdlib/doc/src/erl_parse.xml b/lib/stdlib/doc/src/erl_parse.xml index bafc2e0746..56a7131821 100644 --- a/lib/stdlib/doc/src/erl_parse.xml +++ b/lib/stdlib/doc/src/erl_parse.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2011</year> + <year>1996</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -167,6 +167,23 @@ <p>Converts the Erlang data structure <c><anno>Data</anno></c> into an abstract form of type <c><anno>AbsTerm</anno></c>. This is the inverse of <c>normalise/1</c>.</p> + <p><c>erl_parse:abstract(T)</c> is equivalent to + <c>erl_parse:abstract(T, 0)</c>.</p> + </desc> + </func> + <func> + <name name="abstract" arity="2"/> + <fsummary>Convert an Erlang term into an abstract form</fsummary> + <desc> + <p>Converts the Erlang data structure <c><anno>Data</anno></c> into an + abstract form of type <c><anno>AbsTerm</anno></c>.</p> + <p>The <c><anno>Line</anno></c> option is the line that will + be assigned to each node of the abstract form.</p> + <p>The <c><anno>Encoding</anno></c> option is used for + selecting which integer lists will be considered + as strings. The default is to use the encoding returned by + <seealso marker="epp#default_encoding/0"> + <c>epp:default_encoding/0</c></seealso></p> </desc> </func> </funcs> diff --git a/lib/stdlib/doc/src/sys.xml b/lib/stdlib/doc/src/sys.xml index 073faf2df2..a177b80739 100644 --- a/lib/stdlib/doc/src/sys.xml +++ b/lib/stdlib/doc/src/sys.xml @@ -211,18 +211,87 @@ <p>Gets the status of the process.</p> <p>The value of <c><anno>Misc</anno></c> varies for different types of processes. For example, a <c>gen_server</c> process returns - the callback module's state, and a <c>gen_fsm</c> process - returns information such as its current state name. Callback - modules for <c>gen_server</c> and <c>gen_fsm</c> can also - customise the value of <c><anno>Misc</anno></c> by exporting - a <c>format_status/2</c> function that contributes - module-specific information; - see <seealso marker="gen_server#Module:format_status/2">gen_server:format_status/2</seealso> - and <seealso marker="gen_fsm#Module:format_status/2">gen_fsm:format_status/2</seealso> + the callback module's state, a <c>gen_fsm</c> process + returns information such as its current state name and state data, + and a <c>gen_event</c> process returns information about each of its + registered handlers. Callback modules for <c>gen_server</c>, + <c>gen_fsm</c>, and <c>gen_event</c> can also customise the value + of <c><anno>Misc</anno></c> by exporting a <c>format_status/2</c> + function that contributes module-specific information; + see <seealso marker="gen_server#Module:format_status/2">gen_server:format_status/2</seealso>, + <seealso marker="gen_fsm#Module:format_status/2">gen_fsm:format_status/2</seealso>, and + <seealso marker="gen_event#Module:format_status/2">gen_event:format_status/2</seealso> for more details.</p> </desc> </func> <func> + <name name="get_state" arity="1"/> + <name name="get_state" arity="2"/> + <fsummary>Get the state of the process</fsummary> + <desc> + <p>Gets the state of the process.</p> + <note> + <p>These functions are intended only to help with debugging. They are provided for + convenience, allowing developers to avoid having to create their own state extraction + functions and also avoid having to interactively extract state from the return values of + <c><seealso marker="get_status/1">get_status/1</seealso></c> or + <c><seealso marker="get_status/2">get_status/2</seealso></c> while debugging.</p> + </note> + <p>The value of <c><anno>State</anno></c> varies for different types of + processes. For a <c>gen_server</c> process, the returned <c><anno>State</anno></c> + is simply the callback module's state. For a <c>gen_fsm</c> process, + <c><anno>State</anno></c> is the tuple <c>{CurrentStateName, CurrentStateData}</c>. + For a <c>gen_event</c> process, <c><anno>State</anno></c> a list of tuples, + where each tuple corresponds to an event handler registered in the process and contains + <c>{Module, Id, HandlerState}</c>, where <c>Module</c> is the event handler's module name, + <c>Id</c> is the handler's ID (which is the value <c>false</c> if it was registered without + an ID), and <c>HandlerState</c> is the handler's state.</p> + <p>To obtain more information about a process, including its state, see + <seealso marker="get_status/1">get_status/1</seealso> and + <seealso marker="get_status/2">get_status/2</seealso>.</p> + </desc> + </func> + <func> + <name name="replace_state" arity="2"/> + <name name="replace_state" arity="3"/> + <fsummary>Replace the state of the process</fsummary> + <desc> + <p>Replaces the state of the process, and returns the new state.</p> + <note> + <p>These functions are intended only to help with debugging, and they should not be + be called from normal code. They are provided for convenience, allowing developers + to avoid having to create their own custom state replacement functions.</p> + </note> + <p>The <c><anno>StateFun</anno></c> function provides a new state for the process. + The <c><anno>State</anno></c> argument and <c><anno>NewState</anno></c> return value + of <c><anno>StateFun</anno></c> vary for different types of processes. For a + <c>gen_server</c> process, <c><anno>State</anno></c> is simply the callback module's + state, and <c><anno>NewState</anno></c> is a new instance of that state. For a + <c>gen_fsm</c> process, <c><anno>State</anno></c> is the tuple + <c>{CurrentStateName, CurrentStateData}</c>, and <c><anno>NewState</anno></c> + is a similar tuple that may contain a new state name, new state data, or both. + For a <c>gen_event</c> process, <c><anno>State</anno></c> is the tuple + <c>{Module, Id, HandlerState}</c> where <c>Module</c> is the event handler's module name, + <c>Id</c> is the handler's ID (which is the value <c>false</c> if it was registered without + an ID), and <c>HandlerState</c> is the handler's state. <c><anno>NewState</anno></c> is a + similar tuple where <c>Module</c> and <c>Id</c> shall have the same values as in + <c><anno>State</anno></c> but the value of <c>HandlerState</c> may be different. Returning + a <c><anno>NewState</anno></c> whose <c>Module</c> or <c>Id</c> values differ from those of + <c><anno>State</anno></c> will result in the event handler's state remaining unchanged. For a + <c>gen_event</c> process, <c><anno>StateFun</anno></c> is called once for each event handler + registered in the <c>gen_event</c> process.</p> + <p>If a <c><anno>StateFun</anno></c> function decides not to effect any change in process + state, then regardless of process type, it may simply return its <c><anno>State</anno></c> + argument.</p> + <p>If a <c><anno>StateFun</anno></c> function crashes or throws an exception, then + for <c>gen_server</c> and <c>gen_fsm</c> processes, the original state of the process is + unchanged. For <c>gen_event</c> processes, a crashing or failing <c><anno>StateFun</anno></c> + function means that only the state of the particular event handler it was working on when it + failed or crashed is unchanged; it can still succeed in changing the states of other event + handlers registered in the same <c>gen_event</c> process.</p> + </desc> + </func> + <func> <name name="install" arity="2"/> <name name="install" arity="3"/> <fsummary>Install a debug function in the process</fsummary> diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl index 9ff25fcbc5..7145b0858f 100644 --- a/lib/stdlib/src/erl_parse.yrl +++ b/lib/stdlib/src/erl_parse.yrl @@ -887,6 +887,7 @@ abstract(T, Options) when is_list(Options) -> abstract(T, Line, Encoding). -define(UNICODE(C), + is_integer(C) andalso (C >= 0 andalso C < 16#D800 orelse C > 16#DFFF andalso C < 16#FFFE orelse C > 16#FFFF andalso C =< 16#10FFFF)). diff --git a/lib/stdlib/src/erl_scan.erl b/lib/stdlib/src/erl_scan.erl index 3651f608bc..d988a4d8c7 100644 --- a/lib/stdlib/src/erl_scan.erl +++ b/lib/stdlib/src/erl_scan.erl @@ -338,6 +338,7 @@ string_thing(_) -> "string". -define(DIGIT(C), C >= $0, C =< $9). -define(CHAR(C), is_integer(C), C >= 0). -define(UNICODE(C), + is_integer(C) andalso (C >= 0 andalso C < 16#D800 orelse C > 16#DFFF andalso C < 16#FFFE orelse C > 16#FFFF andalso C =< 16#10FFFF)). diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl index 2b8ba86909..bfebf29080 100644 --- a/lib/stdlib/src/gen_event.erl +++ b/lib/stdlib/src/gen_event.erl @@ -229,6 +229,24 @@ wake_hib(Parent, ServerName, MSL, Debug) -> fetch_msg(Parent, ServerName, MSL, Debug, Hib) -> receive + {system, From, get_state} -> + States = [{Mod,Id,State} || #handler{module=Mod, id=Id, state=State} <- MSL], + sys:handle_system_msg(get_state, From, Parent, ?MODULE, Debug, + {States, [ServerName, MSL, Hib]}, Hib); + {system, From, {replace_state, StateFun}} -> + {NMSL, NStates} = + lists:unzip([begin + Cur = {Mod,Id,State}, + try + NState = {Mod,Id,NS} = StateFun(Cur), + {HS#handler{state=NS}, NState} + catch + _:_ -> + {HS, Cur} + end + end || #handler{module=Mod, id=Id, state=State}=HS <- MSL]), + sys:handle_system_msg(replace_state, From, Parent, ?MODULE, Debug, + {NStates, [ServerName, NMSL, Hib]}, Hib); {system, From, Req} -> sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug, [ServerName, MSL, Hib],Hib); diff --git a/lib/stdlib/src/gen_fsm.erl b/lib/stdlib/src/gen_fsm.erl index e480e2ac11..d9411e58cf 100644 --- a/lib/stdlib/src/gen_fsm.erl +++ b/lib/stdlib/src/gen_fsm.erl @@ -422,6 +422,17 @@ wake_hib(Parent, Name, StateName, StateData, Mod, Debug) -> decode_msg(Msg,Parent, Name, StateName, StateData, Mod, Time, Debug, Hib) -> case Msg of + {system, From, get_state} -> + Misc = [Name, StateName, StateData, Mod, Time], + sys:handle_system_msg(get_state, From, Parent, ?MODULE, Debug, + {{StateName, StateData}, Misc}, Hib); + {system, From, {replace_state, StateFun}} -> + State = {StateName, StateData}, + NState = {NStateName, NStateData} = try StateFun(State) + catch _:_ -> State end, + NMisc = [Name, NStateName, NStateData, Mod, Time], + sys:handle_system_msg(replace_state, From, Parent, ?MODULE, Debug, + {NState, NMisc}, Hib); {system, From, Req} -> sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug, [Name, StateName, StateData, Mod, Time], Hib); diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl index 04308a51b7..9c4b95acf6 100644 --- a/lib/stdlib/src/gen_server.erl +++ b/lib/stdlib/src/gen_server.erl @@ -372,6 +372,13 @@ wake_hib(Parent, Name, State, Mod, Debug) -> decode_msg(Msg, Parent, Name, State, Mod, Time, Debug, Hib) -> case Msg of + {system, From, get_state} -> + sys:handle_system_msg(get_state, From, Parent, ?MODULE, Debug, + {State, [Name, State, Mod, Time]}, Hib); + {system, From, {replace_state, StateFun}} -> + NState = try StateFun(State) catch _:_ -> State end, + sys:handle_system_msg(replace_state, From, Parent, ?MODULE, Debug, + {NState, [Name, NState, Mod, Time]}, Hib); {system, From, Req} -> sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug, [Name, State, Mod, Time], Hib); diff --git a/lib/stdlib/src/sys.erl b/lib/stdlib/src/sys.erl index 2d6287814e..bffeb44179 100644 --- a/lib/stdlib/src/sys.erl +++ b/lib/stdlib/src/sys.erl @@ -21,6 +21,8 @@ %% External exports -export([suspend/1, suspend/2, resume/1, resume/2, get_status/1, get_status/2, + get_state/1, get_state/2, + replace_state/2, replace_state/3, change_code/4, change_code/5, log/2, log/3, trace/2, trace/3, statistics/2, statistics/3, log_to_file/2, log_to_file/3, no_debug/1, no_debug/2, @@ -97,6 +99,32 @@ get_status(Name) -> send_system_msg(Name, get_status). | (Misc :: term()). get_status(Name, Timeout) -> send_system_msg(Name, get_status, Timeout). +-spec get_state(Name) -> State when + Name :: name(), + State :: term(). +get_state(Name) -> send_system_msg(Name, get_state). + +-spec get_state(Name, Timeout) -> State when + Name :: name(), + Timeout :: timeout(), + State :: term(). +get_state(Name, Timeout) -> send_system_msg(Name, get_state, Timeout). + +-spec replace_state(Name, StateFun) -> NewState when + Name :: name(), + StateFun :: fun((State :: term()) -> NewState :: term()), + NewState :: term(). +replace_state(Name, StateFun) -> + send_system_msg(Name, {replace_state, StateFun}). + +-spec replace_state(Name, StateFun, Timeout) -> NewState when + Name :: name(), + StateFun :: fun((State :: term()) -> NewState :: term()), + Timeout :: timeout(), + NewState :: term(). +replace_state(Name, StateFun, Timeout) -> + send_system_msg(Name, {replace_state, StateFun}, Timeout). + -spec change_code(Name, Module, OldVsn, Extra) -> 'ok' | {error, Reason} when Name :: name(), Module :: module(), @@ -362,6 +390,10 @@ do_cmd(_, suspend, _Parent, _Mod, Debug, Misc) -> {suspended, ok, Debug, Misc}; do_cmd(_, resume, _Parent, _Mod, Debug, Misc) -> {running, ok, Debug, Misc}; +do_cmd(SysState, get_state, _Parent, _Mod, Debug, {State, Misc}) -> + {SysState, State, Debug, Misc}; +do_cmd(SysState, replace_state, _Parent, _Mod, Debug, {State, Misc}) -> + {SysState, State, Debug, Misc}; do_cmd(SysState, get_status, Parent, Mod, Debug, Misc) -> Res = get_status(SysState, Parent, Mod, Debug, Misc), {SysState, Res, Debug, Misc}; diff --git a/lib/stdlib/test/erl_scan_SUITE.erl b/lib/stdlib/test/erl_scan_SUITE.erl index ecd181e87c..361abbb771 100644 --- a/lib/stdlib/test/erl_scan_SUITE.erl +++ b/lib/stdlib/test/erl_scan_SUITE.erl @@ -21,7 +21,8 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2]). --export([ error_1/1, error_2/1, iso88591/1, otp_7810/1, otp_10302/1]). +-export([ error_1/1, error_2/1, iso88591/1, otp_7810/1, otp_10302/1, + otp_10990/1, otp_10992/1]). -import(lists, [nth/2,flatten/1]). -import(io_lib, [print/1]). @@ -60,7 +61,7 @@ end_per_testcase(_Case, Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [{group, error}, iso88591, otp_7810, otp_10302]. + [{group, error}, iso88591, otp_7810, otp_10302, otp_10990, otp_10992]. groups() -> [{error, [], [error_1, error_2]}]. @@ -1121,6 +1122,29 @@ otp_10302(Config) when is_list(Config) -> erl_parse:abstract("a"++[1024]++"c", [{encoding,latin1}]), ok. +otp_10990(doc) -> + "OTP-10990. Floating point number in input string."; +otp_10990(suite) -> + []; +otp_10990(Config) when is_list(Config) -> + {'EXIT',_} = (catch {foo, erl_scan:string([$",42.0,$"],1)}), + ok. + +otp_10992(doc) -> + "OTP-10992. List of floats to abstract format."; +otp_10992(suite) -> + []; +otp_10992(Config) when is_list(Config) -> + {cons,0,{float,0,42.0},{nil,0}} = + erl_parse:abstract([42.0], [{encoding,unicode}]), + {cons,0,{float,0,42.0},{nil,0}} = + erl_parse:abstract([42.0], [{encoding,utf8}]), + {cons,0,{integer,0,65},{cons,0,{float,0,42.0},{nil,0}}} = + erl_parse:abstract([$A,42.0], [{encoding,unicode}]), + {cons,0,{integer,0,65},{cons,0,{float,0,42.0},{nil,0}}} = + erl_parse:abstract([$A,42.0], [{encoding,utf8}]), + ok. + test_string(String, Expected) -> {ok, Expected, _End} = erl_scan:string(String), test(String). diff --git a/lib/stdlib/test/gen_event_SUITE.erl b/lib/stdlib/test/gen_event_SUITE.erl index 5c51e12e35..6be5a299b6 100644 --- a/lib/stdlib/test/gen_event_SUITE.erl +++ b/lib/stdlib/test/gen_event_SUITE.erl @@ -26,13 +26,14 @@ delete_handler/1, swap_handler/1, swap_sup_handler/1, notify/1, sync_notify/1, call/1, info/1, hibernate/1, call_format_status/1, call_format_status_anon/1, - error_format_status/1]). + error_format_status/1, get_state/1, replace_state/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [start, {group, test_all}, hibernate, - call_format_status, call_format_status_anon, error_format_status]. + call_format_status, call_format_status_anon, error_format_status, + get_state, replace_state]. groups() -> [{test_all, [], @@ -956,3 +957,45 @@ error_format_status(Config) when is_list(Config) -> ?line ok = gen_event:stop(Pid), process_flag(trap_exit, OldFl), ok. + +get_state(suite) -> + []; +get_state(doc) -> + ["Test that sys:get_state/1,2 return the gen_event state"]; +get_state(Config) when is_list(Config) -> + {ok, Pid} = gen_event:start({local, my_dummy_handler}), + State1 = self(), + ok = gen_event:add_handler(my_dummy_handler, dummy1_h, [State1]), + [{dummy1_h,false,State1}] = sys:get_state(Pid), + [{dummy1_h,false,State1}] = sys:get_state(Pid, 5000), + State2 = {?MODULE, self()}, + ok = gen_event:add_handler(my_dummy_handler, {dummy1_h,id}, [State2]), + Result1 = sys:get_state(Pid), + [{dummy1_h,false,State1},{dummy1_h,id,State2}] = lists:sort(Result1), + Result2 = sys:get_state(Pid, 5000), + [{dummy1_h,false,State1},{dummy1_h,id,State2}] = lists:sort(Result2), + ok = gen_event:stop(Pid), + ok. + +replace_state(suite) -> + []; +replace_state(doc) -> + ["Test that replace_state/2,3 replace the gen_event state"]; +replace_state(Config) when is_list(Config) -> + {ok, Pid} = gen_event:start({local, my_dummy_handler}), + State1 = self(), + ok = gen_event:add_handler(my_dummy_handler, dummy1_h, [State1]), + [{dummy1_h,false,State1}] = sys:get_state(Pid), + NState1 = "replaced", + Replace1 = fun({dummy1_h,false,_}=S) -> setelement(3,S,NState1) end, + [{dummy1_h,false,NState1}] = sys:replace_state(Pid, Replace1), + [{dummy1_h,false,NState1}] = sys:get_state(Pid), + NState2 = "replaced again", + Replace2 = fun({dummy1_h,false,_}=S) -> setelement(3,S,NState2) end, + [{dummy1_h,false,NState2}] = sys:replace_state(Pid, Replace2, 5000), + [{dummy1_h,false,NState2}] = sys:get_state(Pid), + %% verify no change in state if replace function crashes + Replace3 = fun(_) -> exit(fail) end, + [{dummy1_h,false,NState2}] = sys:replace_state(Pid, Replace3), + [{dummy1_h,false,NState2}] = sys:get_state(Pid), + ok. diff --git a/lib/stdlib/test/gen_fsm_SUITE.erl b/lib/stdlib/test/gen_fsm_SUITE.erl index a637a8543b..fd15838b7d 100644 --- a/lib/stdlib/test/gen_fsm_SUITE.erl +++ b/lib/stdlib/test/gen_fsm_SUITE.erl @@ -31,7 +31,7 @@ -export([shutdown/1]). --export([ sys1/1, call_format_status/1, error_format_status/1]). +-export([ sys1/1, call_format_status/1, error_format_status/1, get_state/1, replace_state/1]). -export([hibernate/1,hiber_idle/3,hiber_wakeup/3,hiber_idle/2,hiber_wakeup/2]). @@ -66,7 +66,7 @@ groups() -> start8, start9, start10, start11, start12]}, {abnormal, [], [abnormal1, abnormal2]}, {sys, [], - [sys1, call_format_status, error_format_status]}]. + [sys1, call_format_status, error_format_status, get_state, replace_state]}]. init_per_suite(Config) -> Config. @@ -413,6 +413,40 @@ error_format_status(Config) when is_list(Config) -> process_flag(trap_exit, OldFl), ok. +get_state(Config) when is_list(Config) -> + State = self(), + {ok, Pid} = gen_fsm:start(?MODULE, {state_data, State}, []), + {idle, State} = sys:get_state(Pid), + {idle, State} = sys:get_state(Pid, 5000), + stop_it(Pid), + + %% check that get_state can handle a name being an atom (pid is + %% already checked by the previous test) + {ok, Pid2} = gen_fsm:start({local, gfsm}, gen_fsm_SUITE, {state_data, State}, []), + {idle, State} = sys:get_state(gfsm), + {idle, State} = sys:get_state(gfsm, 5000), + stop_it(Pid2), + ok. + +replace_state(Config) when is_list(Config) -> + State = self(), + {ok, Pid} = gen_fsm:start(?MODULE, {state_data, State}, []), + {idle, State} = sys:get_state(Pid), + NState1 = "replaced", + Replace1 = fun({StateName, _}) -> {StateName, NState1} end, + {idle, NState1} = sys:replace_state(Pid, Replace1), + {idle, NState1} = sys:get_state(Pid), + NState2 = "replaced again", + Replace2 = fun({idle, _}) -> {state0, NState2} end, + {state0, NState2} = sys:replace_state(Pid, Replace2, 5000), + {state0, NState2} = sys:get_state(Pid), + %% verify no change in state if replace function crashes + Replace3 = fun(_) -> error(fail) end, + {state0, NState2} = sys:replace_state(Pid, Replace3), + {state0, NState2} = sys:get_state(Pid), + stop_it(Pid), + ok. + %% Hibernation hibernate(suite) -> []; hibernate(Config) when is_list(Config) -> diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl index dffeadb423..3b6a3f38bc 100644 --- a/lib/stdlib/test/gen_server_SUITE.erl +++ b/lib/stdlib/test/gen_server_SUITE.erl @@ -32,7 +32,7 @@ spec_init_local_registered_parent/1, spec_init_global_registered_parent/1, otp_5854/1, hibernate/1, otp_7669/1, call_format_status/1, - error_format_status/1, call_with_huge_message_queue/1 + error_format_status/1, get_state/1, replace_state/1, call_with_huge_message_queue/1 ]). % spawn export @@ -57,6 +57,7 @@ all() -> spec_init_local_registered_parent, spec_init_global_registered_parent, otp_5854, hibernate, otp_7669, call_format_status, error_format_status, + get_state, replace_state, call_with_huge_message_queue]. groups() -> @@ -1033,6 +1034,51 @@ error_format_status(Config) when is_list(Config) -> process_flag(trap_exit, OldFl), ok. +%% Verify that sys:get_state correctly returns gen_server state +%% +get_state(suite) -> + []; +get_state(doc) -> + ["Test that sys:get_state/1,2 return the gen_server state"]; +get_state(Config) when is_list(Config) -> + State = self(), + {ok, _Pid} = gen_server:start_link({local, get_state}, + ?MODULE, {state,State}, []), + State = sys:get_state(get_state), + State = sys:get_state(get_state, 5000), + {ok, Pid} = gen_server:start_link(?MODULE, {state,State}, []), + State = sys:get_state(Pid), + State = sys:get_state(Pid, 5000), + ok. + +%% Verify that sys:replace_state correctly replaces gen_server state +%% +replace_state(suite) -> + []; +replace_state(doc) -> + ["Test that sys:replace_state/1,2 replace the gen_server state"]; +replace_state(Config) when is_list(Config) -> + State = self(), + {ok, _Pid} = gen_server:start_link({local, replace_state}, + ?MODULE, {state,State}, []), + State = sys:get_state(replace_state), + NState1 = "replaced", + Replace1 = fun(_) -> NState1 end, + NState1 = sys:replace_state(replace_state, Replace1), + NState1 = sys:get_state(replace_state), + {ok, Pid} = gen_server:start_link(?MODULE, {state,NState1}, []), + NState1 = sys:get_state(Pid), + Suffix = " again", + NState2 = NState1 ++ Suffix, + Replace2 = fun(S) -> S ++ Suffix end, + NState2 = sys:replace_state(Pid, Replace2, 5000), + NState2 = sys:get_state(Pid, 5000), + %% verify no change in state if replace function crashes + Replace3 = fun(_) -> throw(fail) end, + NState2 = sys:replace_state(Pid, Replace3), + NState2 = sys:get_state(Pid, 5000), + ok. + %% Test that the time for a huge message queue is not %% significantly slower than with an empty message queue. call_with_huge_message_queue(Config) when is_list(Config) -> diff --git a/lib/stdlib/test/io_proto_SUITE.erl b/lib/stdlib/test/io_proto_SUITE.erl index e16ba55481..76a8109a8d 100644 --- a/lib/stdlib/test/io_proto_SUITE.erl +++ b/lib/stdlib/test/io_proto_SUITE.erl @@ -147,8 +147,7 @@ unicode_prompt(Config) when is_list(Config) -> %% And one with oldshell ?line rtnode([{putline,""}, {putline, "2."}, - {getline_re, ".*2."}, - {getline, "2"}, + {getline_re, ".*2$"}, {putline, "shell:prompt_func({io_proto_SUITE,uprompt})."}, {getline_re, ".*default"}, {putline, "io:get_line('')."}, @@ -263,8 +262,7 @@ setopts_getopts(Config) when is_list(Config) -> %% And one with oldshell ?line rtnode([{putline,""}, {putline, "2."}, - {getline_re, ".*2."}, - {getline, "2"}, + {getline_re, ".*2$"}, {putline, "lists:keyfind(binary,1,io:getopts())."}, {getline_re, ".*{binary,false}"}, {putline, "io:get_line('')."}, @@ -467,8 +465,7 @@ unicode_options(Config) when is_list(Config) -> end, ?line rtnode([{putline,""}, {putline, "2."}, - {getline_re, ".*2."}, - {getline, "2"}, + {getline_re, ".*2$"}, {putline, "lists:keyfind(encoding,1,io:getopts())."}, {getline_re, ".*{encoding,latin1}"}, {putline, "io:format(\"~ts~n\",[[1024]])."}, @@ -701,8 +698,7 @@ binary_options(Config) when is_list(Config) -> old -> ok; new -> - ?line rtnode([{putline,""}, - {putline, "2."}, + ?line rtnode([{putline, "2."}, {getline, "2"}, {putline, "lists:keyfind(binary,1,io:getopts())."}, {getline, "{binary,false}"}, @@ -720,10 +716,8 @@ binary_options(Config) when is_list(Config) -> ],[]) end, %% And one with oldshell - ?line rtnode([{putline,""}, - {putline, "2."}, - {getline_re, ".*2."}, - {getline, "2"}, + ?line rtnode([{putline, "2."}, + {getline_re, ".*2$"}, {putline, "lists:keyfind(binary,1,io:getopts())."}, {getline_re, ".*{binary,false}"}, {putline, "io:get_line('')."}, diff --git a/lib/tools/emacs/erlang-start.el b/lib/tools/emacs/erlang-start.el index bbcea3e46a..e1dc86621e 100644 --- a/lib/tools/emacs/erlang-start.el +++ b/lib/tools/emacs/erlang-start.el @@ -83,6 +83,7 @@ ;; Associate files extensions ".erl" and ".hrl" with Erlang mode. ;; +;;;###autoload (let ((a '("\\.erl\\'" . erlang-mode)) (b '("\\.hrl\\'" . erlang-mode))) (or (assoc (car a) auto-mode-alist) @@ -94,6 +95,7 @@ ;; Associate files using interpreter "escript" with Erlang mode. ;; +;;;###autoload (add-to-list 'interpreter-mode-alist (cons "escript" 'erlang-mode)) ;; @@ -101,6 +103,7 @@ ;; file completion. ;; +;;;###autoload (let ((erl-ext '(".jam" ".vee" ".beam"))) (while erl-ext (let ((cie completion-ignored-extensions)) |