diff options
Diffstat (limited to 'lib')
177 files changed, 11359 insertions, 7639 deletions
diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl index 7e98e6395f..506147474f 100644 --- a/lib/common_test/src/ct_framework.erl +++ b/lib/common_test/src/ct_framework.erl @@ -314,8 +314,8 @@ add_defaults(Mod,Func, GroupPath) -> ErrStr = io_lib:format("~n*** ERROR *** " "~w:suite/0 failed: ~tp~n", [Suite,Reason]), - io:format(ErrStr, []), - io:format(?def_gl, ErrStr, []), + io:format("~ts", [ErrStr]), + io:format(?def_gl, "~ts", [ErrStr]), {suite0_failed,{exited,Reason}}; SuiteInfo when is_list(SuiteInfo) -> case lists:all(fun(E) when is_tuple(E) -> true; @@ -337,16 +337,16 @@ add_defaults(Mod,Func, GroupPath) -> "Invalid return value from " "~w:suite/0: ~tp~n", [Suite,SuiteInfo]), - io:format(ErrStr, []), - io:format(?def_gl, ErrStr, []), + io:format("~ts", [ErrStr]), + io:format(?def_gl, "~ts", [ErrStr]), {suite0_failed,bad_return_value} end; SuiteInfo -> ErrStr = io_lib:format("~n*** ERROR *** " "Invalid return value from " "~w:suite/0: ~tp~n", [Suite,SuiteInfo]), - io:format(ErrStr, []), - io:format(?def_gl, ErrStr, []), + io:format("~ts", [ErrStr]), + io:format(?def_gl, "~ts", [ErrStr]), {suite0_failed,bad_return_value} end. @@ -373,8 +373,8 @@ add_defaults1(Mod,Func, GroupPath, SuiteInfo) -> "Invalid return value from " "~w:group(~tw): ~tp~n", [Mod,GrName,BadGr0Val]), - io:format(Gr0ErrStr, []), - io:format(?def_gl, Gr0ErrStr, []), + io:format("~ts", [Gr0ErrStr]), + io:format(?def_gl, "~ts", [Gr0ErrStr]), {group0_failed,bad_return_value}; _ -> Args = if Func == init_per_group ; Func == end_per_group -> @@ -395,8 +395,8 @@ add_defaults1(Mod,Func, GroupPath, SuiteInfo) -> "Invalid return value from " "~w:~tw/0: ~tp~n", [Mod,Func,BadTC0Val]), - io:format(TC0ErrStr, []), - io:format(?def_gl, TC0ErrStr, []), + io:format("~ts", [TC0ErrStr]), + io:format(?def_gl, "~ts", [TC0ErrStr]), {testcase0_failed,bad_return_value}; _ -> %% let test case info (also for all config funcs) override @@ -972,11 +972,10 @@ error_notification(Mod,Func,_Args,{Error,Loc}) -> end, PrintError = fun(ErrorFormat, ErrorArgs) -> - Div = "~n- - - - - - - - - - - - - - - - - - - " - "- - - - - - - - - - - - - - - - - - - - -~n", + Div = "\n- - - - - - - - - - - - - - - - - - - " + "- - - - - - - - - - - - - - - - - - - - -\n", ErrorStr2 = io_lib:format(ErrorFormat, ErrorArgs), - io:format(?def_gl, lists:concat([Div,ErrorStr2,Div,"~n"]), - []), + io:format(?def_gl, "~ts~n", [lists:concat([Div,ErrorStr2,Div])]), Link = "\n\n<a href=\"#end\">" "Full error description and stacktrace" @@ -985,7 +984,8 @@ error_notification(Mod,Func,_Args,{Error,Loc}) -> ct_logs:tc_log(ct_error_notify, ?MAX_IMPORTANCE, "CT Error Notification", - ErrorHtml2++Link, [], []) + "~ts", [ErrorHtml2++Link], + []) end, case Loc of [{?MODULE,error_in_suite}] -> @@ -1181,7 +1181,7 @@ get_all(Mod, ConfTests) -> ErrStr = io_lib:format("~n*** ERROR *** " "~w:all/0 failed: ~tp~n", [Mod,ExitReason]), - io:format(?def_gl, ErrStr, []), + io:format(?def_gl, "~ts", [ErrStr]), %% save the error info so it doesn't get printed twice ct_util:set_testdata_async({{error_in_suite,Mod}, ExitReason}); diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl index 07a1693d5d..814b80b8bd 100644 --- a/lib/common_test/src/ct_logs.erl +++ b/lib/common_test/src/ct_logs.erl @@ -542,7 +542,7 @@ tc_print(Category,Importance,Format,Args,Opts) -> undefined -> atom_to_list(Category); Hd -> Hd end, - Str = lists:concat([get_header(Heading),Format,"\n\n"]), + Str = lists:flatten([get_header(Heading),Format,"\n\n"]), try io:format(?def_gl, Str, Args) catch @@ -935,7 +935,7 @@ create_io_fun(FromPid, CtLogFd, EscChars) -> {_HdOrFt,S,A} -> {false,S,A}; {S,A} -> {true,S,A} end, - try io_lib:format(Str, Args) of + try io_lib:format(lists:flatten(Str), Args) of IoStr when Escapable, EscChars, IoList == [] -> escape_chars(IoStr); IoStr when Escapable, EscChars -> @@ -1138,10 +1138,10 @@ set_evmgr_gl(GL) -> open_ctlog(MiscIoName) -> {ok,Fd} = file:open(?ct_log_name,[write,{encoding,utf8}]), - io:format(Fd, header("Common Test Framework Log", {[],[1,2],[]}), []), + io:format(Fd, "~ts", [header("Common Test Framework Log", {[],[1,2],[]})]), case file:consult(ct_run:variables_file_name("../")) of {ok,Vars} -> - io:format(Fd, config_table(Vars), []); + io:format(Fd, "~ts", [config_table(Vars)]); {error,Reason} -> {ok,Cwd} = file:get_cwd(), Dir = filename:dirname(Cwd), @@ -1213,7 +1213,7 @@ print_style_error(Fd, IoFormat, StyleSheet, Reason) -> close_ctlog(Fd) -> io:format(Fd, "\n</pre>\n", []), - io:format(Fd, [xhtml("<br><br>\n", "<br /><br />\n") | footer()], []), + io:format(Fd, "~ts", [[xhtml("<br><br>\n", "<br /><br />\n") | footer()]]), ok = file:close(Fd). %%%----------------------------------------------------------------- diff --git a/lib/common_test/src/ct_master.erl b/lib/common_test/src/ct_master.erl index fd33ee2280..9fc169789c 100644 --- a/lib/common_test/src/ct_master.erl +++ b/lib/common_test/src/ct_master.erl @@ -350,7 +350,7 @@ master_loop(#state{node_ctrl_pids=[], io_lib:format("~-40.40.*ts~tp\n", [$_,atom_to_list(Node),Result]) end,lists:reverse(Finished)), - log(all,"TEST RESULTS",Str,[]), + log(all,"TEST RESULTS","~ts", [Str]), log(all,"Info","Updating log files",[]), refresh_logs(LogDirs,[]), @@ -574,7 +574,7 @@ refresh_logs([],Refreshed) -> io_lib:format("Refreshing logs in ~tp... ~tp", [D,Result]) end,Refreshed), - log(all,"Info",Str,[]). + log(all,"Info","~ts", [Str]). %%%----------------------------------------------------------------- %%% NODE CONTROLLER, runs and controls tests on a test node. diff --git a/lib/common_test/src/ct_repeat.erl b/lib/common_test/src/ct_repeat.erl index 8b1c7d47bb..b97c6e59e7 100644 --- a/lib/common_test/src/ct_repeat.erl +++ b/lib/common_test/src/ct_repeat.erl @@ -278,7 +278,7 @@ log_loop_info(Args) -> ForceStop -> io_lib:format("force_stop is set to: ~w",[ForceStop]) end, - ct_logs:log("Test loop info",LogStr1++LogStr2++LogStr3++LogStr4,[]) + ct_logs:log("Test loop info","~ts", [LogStr1++LogStr2++LogStr3++LogStr4]) end. ts(Secs) -> diff --git a/lib/common_test/src/cth_log_redirect.erl b/lib/common_test/src/cth_log_redirect.erl index 86081369b9..fe869a4373 100644 --- a/lib/common_test/src/cth_log_redirect.erl +++ b/lib/common_test/src/cth_log_redirect.erl @@ -194,10 +194,10 @@ handle_call({log, case LogFunc of tc_log -> ct_logs:tc_log(Category, ?STD_IMPORTANCE, - Header, String, [], []); + Header, "~ts", [String], []); tc_log_async -> ct_logs:tc_log_async(sasl, ?STD_IMPORTANCE, - Header, String, []) + Header, "~ts", [String]) end, {reply,ok,State}; @@ -261,34 +261,34 @@ handle_remote_events(Bool) -> format_header(#eh_state{curr_suite = undefined, curr_group = undefined, curr_func = undefined}) -> - io_lib:format("System report", []); + lists:flatten(io_lib:format("System report", [])); format_header(#eh_state{curr_suite = Suite, curr_group = undefined, curr_func = undefined}) -> - io_lib:format("System report during ~w", [Suite]); + lists:flatten(io_lib:format("System report during ~w", [Suite])); format_header(#eh_state{curr_suite = Suite, curr_group = undefined, curr_func = TcOrConf}) -> - io_lib:format("System report during ~w:~tw/1", - [Suite,TcOrConf]); + lists:flatten(io_lib:format("System report during ~w:~tw/1", + [Suite,TcOrConf])); format_header(#eh_state{curr_suite = Suite, curr_group = Group, curr_func = Conf}) when Conf == init_per_group; Conf == end_per_group -> - io_lib:format("System report during ~w:~w/2 for ~tw", - [Suite,Conf,Group]); + lists:flatten(io_lib:format("System report during ~w:~w/2 for ~tw", + [Suite,Conf,Group])); format_header(#eh_state{curr_suite = Suite, curr_group = Group, parallel_tcs = true}) -> - io_lib:format("System report during ~tw in ~w", - [Group,Suite]); + lists:flatten(io_lib:format("System report during ~tw in ~w", + [Group,Suite])); format_header(#eh_state{curr_suite = Suite, curr_group = Group, curr_func = TC}) -> - io_lib:format("System report during ~w:~tw/1 in ~tw", - [Suite,TC,Group]). + lists:flatten(io_lib:format("System report during ~w:~tw/1 in ~tw", + [Suite,TC,Group])). diff --git a/lib/common_test/src/cth_surefire.erl b/lib/common_test/src/cth_surefire.erl index b0742717ae..c9b4cb10c6 100644 --- a/lib/common_test/src/cth_surefire.erl +++ b/lib/common_test/src/cth_surefire.erl @@ -235,7 +235,7 @@ close_suite(#state{ test_cases = TCs, url_base = UrlBase } = State) -> terminate(State = #state{ test_cases = [] }) -> {ok,D} = file:open(State#state.filepath,[write,{encoding,utf8}]), io:format(D, "<?xml version=\"1.0\" encoding= \"UTF-8\" ?>", []), - io:format(D, to_xml(State), []), + io:format(D, "~ts", [to_xml(State)]), catch file:sync(D), catch file:close(D); terminate(State) -> diff --git a/lib/common_test/src/test_server_ctrl.erl b/lib/common_test/src/test_server_ctrl.erl index 34f2feb33c..1518c6e8d6 100644 --- a/lib/common_test/src/test_server_ctrl.erl +++ b/lib/common_test/src/test_server_ctrl.erl @@ -1558,7 +1558,7 @@ do_test_cases(TopCases, SkipCases, Html1} end, - print(html, Header), + print(html, "~ts", [Header]), print(html, xhtml("<p>", "<h4>")), print_timestamp(html, "Test started at "), @@ -1605,10 +1605,10 @@ do_test_cases(TopCases, SkipCases, [?suitelog_name,CoverLog,?unexpected_io_log]), print(html, "<p>~ts</p>\n" ++ - xhtml(["<table bgcolor=\"white\" border=\"3\" cellpadding=\"5\">\n", - "<thead>\n"], - ["<table id=\"",?sortable_table_name,"\">\n", - "<thead>\n"]) ++ + xhtml("<table bgcolor=\"white\" border=\"3\" cellpadding=\"5\">\n" ++ + "<thead>\n", + "<table id=\"" ++ ?sortable_table_name ++ "\">\n" ++ + "<thead>\n") ++ "<tr><th>Num</th><th>Module</th><th>Group</th>" ++ "<th>Case</th><th>Log</th><th>Time</th><th>Result</th>" ++ "<th>Comment</th></tr>\n</thead>\n<tbody>\n", @@ -3306,7 +3306,8 @@ skip_case1(Type, CaseNum, Mod, Func, Comment, Mode) -> true -> print(2,"*** Skipping test case #~w ~tw ***", [CaseNum,{Mod,Func}]) end, - TR = xhtml("<tr valign=\"top\">", ["<tr class=\"",odd_or_even(),"\">"]), + TR = xhtml("<tr valign=\"top\">", + "<tr class=\"" ++ odd_or_even() ++ "\">"), GroupName = case get_name(Mode) of undefined -> ""; Name -> cast_to_list(Name) @@ -3796,8 +3797,8 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, end, print(minor, - escape_chars(io_lib:format("Config value:\n\n ~tp\n", [Args2Print])), - []), + "~ts", + [escape_chars(io_lib:format("Config value:\n\n ~tp\n", [Args2Print]))]), print(minor, "Current directory is ~tp\n", [Cwd]), GrNameStr = case GrName of @@ -3806,7 +3807,7 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, end, print(major, "=started ~s", [lists:flatten(timestamp_get(""))]), {{Col0,Col1},Style} = get_font_style((RunInit==run_init), Mode), - TR = xhtml("<tr valign=\"top\">", ["<tr class=\"",odd_or_even(),"\">"]), + TR = xhtml("<tr valign=\"top\">", "<tr class=\"" ++ odd_or_even() ++ "\">"), EncMinorBase = uri_encode(MinorBase), print(html, TR ++ "<td>" ++ Col0 ++ "~ts" ++ Col1 ++ "</td>" "<td>" ++ Col0 ++ "~w" ++ Col1 ++ "</td>" @@ -3831,7 +3832,7 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, print(minor, "<a name=\"end\"></a>", [], internal_raw), print(minor, "\n", [], internal_raw), print_timestamp(minor, "Ended at "), - print(major, "=ended ~s", [lists:flatten(timestamp_get(""))]), + print(major, "=ended ~s", [timestamp_get("")]), do_unless_parallel(Main, fun() -> file:set_cwd(filename:dirname(TSDir)) end), @@ -4075,9 +4076,9 @@ progress(failed, CaseNum, Mod, Func, GrName, Loc, {testcase_aborted,Reason}, _T, FormatLoc = test_server_sup:format_loc(Loc), print(minor, "=== Location: ~ts", [FormatLoc]), print(minor, - escape_chars(io_lib:format("=== Reason: {testcase_aborted,~tp}", - [Reason])), - []), + "~ts", + [escape_chars(io_lib:format("=== Reason: {testcase_aborted,~tp}", + [Reason]))]), failed; progress(failed, CaseNum, Mod, Func, GrName, unknown, Reason, Time, @@ -4115,8 +4116,8 @@ progress(failed, CaseNum, Mod, Func, GrName, unknown, Reason, Time, print(minor, "=== Location: ~w", [unknown]), {FStr,FormattedReason} = format_exception(Reason), print(minor, - escape_chars(io_lib:format("=== Reason: " ++ FStr, [FormattedReason])), - []), + "~ts", + [escape_chars(io_lib:format("=== Reason: " ++ FStr, [FormattedReason]))]), failed; progress(failed, CaseNum, Mod, Func, GrName, Loc, Reason, Time, @@ -4150,8 +4151,9 @@ progress(failed, CaseNum, Mod, Func, GrName, Loc, Reason, Time, FormatLoc = test_server_sup:format_loc(LocMin), print(minor, "=== Location: ~ts", [FormatLoc]), {FStr,FormattedReason} = format_exception(Reason), - print(minor, "=== Reason: " ++ - escape_chars(io_lib:format(FStr, [FormattedReason])), []), + print(minor, "~ts", + ["=== Reason: " ++ + escape_chars(io_lib:format(FStr, [FormattedReason]))]), failed; progress(ok, _CaseNum, Mod, Func, GrName, _Loc, RetVal, Time, @@ -4184,8 +4186,8 @@ progress(ok, _CaseNum, Mod, Func, GrName, _Loc, RetVal, Time, "~ts</tr>\n", [TimeStr,Comment]), print(minor, - escape_chars(io_lib:format("=== Returned value: ~tp", [RetVal])), - []), + "~ts", + [escape_chars(io_lib:format("=== Returned value: ~tp", [RetVal]))]), ok. %%-------------------------------------------------------------------- @@ -4542,7 +4544,7 @@ timestamp_get(Leader) -> timestamp_get_internal(Leader, Format) -> {YY,MM,DD,H,M,S} = time_get(), - io_lib:format(Format, [Leader,YY,MM,DD,H,M,S]). + lists:flatten(io_lib:format(Format, [Leader,YY,MM,DD,H,M,S])). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% time_get() -> {YY,MM,DD,H,M,S} diff --git a/lib/common_test/test_server/ts_benchmark.erl b/lib/common_test/test_server/ts_benchmark.erl index e4e06b54c2..a9486262b3 100644 --- a/lib/common_test/test_server/ts_benchmark.erl +++ b/lib/common_test/test_server/ts_benchmark.erl @@ -45,7 +45,7 @@ run(Specs, Opts, Vars) -> || Spec <- Specs], file:delete(filename:join(Cwd,"latest_benchmark")), {ok,D} = file:open(filename:join(Cwd,"latest_benchmark"),[write]), - io:format(D,BDir,[]), + io:format(D,"~ts", [BDir]), file:close(D). diff --git a/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml index 78128980f0..5219ba0f5d 100644 --- a/lib/compiler/doc/src/compile.xml +++ b/lib/compiler/doc/src/compile.xml @@ -695,12 +695,13 @@ module.beam: module.erl \ </note> <note> - <p>The options <c>{nowarn_unused_function, FAs}</c>, - <c>{nowarn_bif_clash, FAs}</c>, and - <c>{nowarn_deprecated_function, MFAs}</c> are only - recognized when given in files. They are not affected by - options <c>warn_unused_function</c>, <c>warn_bif_clash</c>, or - <c>warn_deprecated_function</c>.</p> + <p>Before OTP 22, the option <c>{nowarn_deprecated_function, + MFAs}</c> was only recognized when given in the file with + attribute <c>-compile()</c>. (The option + <c>{nowarn_unused_function,FAs}</c> was incorrectly documented + to only work in a file, but it also worked when given in the + option list.) Starting from OTP 22, all options that can be + given in the file can also be given in the option list.</p> </note> <p>For debugging of the compiler, or for pure curiosity, diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml index 7addadf82c..02e6203137 100644 --- a/lib/compiler/doc/src/notes.xml +++ b/lib/compiler/doc/src/notes.xml @@ -32,6 +32,26 @@ <p>This document describes the changes made to the Compiler application.</p> +<section><title>Compiler 7.3.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>An optimization that avoided allocation of a stack + frame for some <c>case</c> expressions was introduced in + OTP 21. (ERL-504/OTP-14808) It turns out that in rare + circumstances, this optimization is not safe. Therefore, + this optimization has been disabled.</p> + <p>A similar optimization will be included in OTP 22 in a + safe way.</p> + <p> + Own Id: OTP-15501 Aux Id: ERL-807, ERL-514, OTP-14808 </p> + </item> + </list> + </section> + +</section> + <section><title>Compiler 7.3</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/compiler/src/Makefile b/lib/compiler/src/Makefile index 961dacc6c9..074d9b881b 100644 --- a/lib/compiler/src/Makefile +++ b/lib/compiler/src/Makefile @@ -49,7 +49,6 @@ MODULES = \ beam_a \ beam_asm \ beam_block \ - beam_bs \ beam_clean \ beam_dict \ beam_disasm \ diff --git a/lib/compiler/src/beam_a.erl b/lib/compiler/src/beam_a.erl index dd2537a699..1ac892a8f1 100644 --- a/lib/compiler/src/beam_a.erl +++ b/lib/compiler/src/beam_a.erl @@ -100,8 +100,12 @@ rename_instr({bs_put_utf16=I,F,Fl,Src}) -> {bs_put,F,{I,Fl},[Src]}; rename_instr({bs_put_utf32=I,F,Fl,Src}) -> {bs_put,F,{I,Fl},[Src]}; -rename_instr({bs_put_string,_,_}=I) -> - {bs_put,{f,0},I,[]}; +rename_instr({bs_put_string,_,{string,String}}) -> + %% Only happens when compiling from .S files. In old + %% .S files, String is a list. In .S in OTP 22 and later, + %% String is a binary. + {bs_put,{f,0},{bs_put_binary,8,{field_flags,[unsigned,big]}}, + [{atom,all},{literal,iolist_to_binary([String])}]}; rename_instr({bs_add=I,F,[Src1,Src2,U],Dst}) when is_integer(U) -> {bif,I,F,[Src1,Src2,{integer,U}],Dst}; rename_instr({bs_utf8_size=I,F,Src,Dst}) -> @@ -118,8 +122,8 @@ rename_instr({bs_private_append=I,F,Sz,U,Src,Flags,Dst}) -> {bs_init,F,{I,U,Flags},none,[Sz,Src],Dst}; rename_instr(bs_init_writable=I) -> {bs_init,{f,0},I,1,[{x,0}],{x,0}}; -rename_instr({test,Op,F,[Ctx,Bits,{string,Str}]}) -> - %% When compiling from a .S file. +rename_instr({test,bs_match_string=Op,F,[Ctx,Bits,{string,Str}]}) when is_list(Str) -> + %% When compiling from an old .S file. Starting from OTP 22, Str is a binary. <<Bs:Bits/bits,_/bits>> = list_to_binary(Str), {test,Op,F,[Ctx,Bs]}; rename_instr({put_map_assoc,Fail,S,D,R,L}) -> diff --git a/lib/compiler/src/beam_asm.erl b/lib/compiler/src/beam_asm.erl index df0321e85a..bc1290f6fd 100644 --- a/lib/compiler/src/beam_asm.erl +++ b/lib/compiler/src/beam_asm.erl @@ -424,8 +424,8 @@ encode_arg({f, W}, Dict) -> {encode(?tag_f, W), Dict}; %% encode_arg({'char', C}, Dict) -> %% {encode(?tag_h, C), Dict}; -encode_arg({string, String}, Dict0) -> - {Offset, Dict} = beam_dict:string(String, Dict0), +encode_arg({string, BinString}, Dict0) when is_binary(BinString) -> + {Offset, Dict} = beam_dict:string(BinString, Dict0), {encode(?tag_u, Offset), Dict}; encode_arg({extfunc, M, F, A}, Dict0) -> {Index, Dict} = beam_dict:import(M, F, A, Dict0), diff --git a/lib/compiler/src/beam_bs.erl b/lib/compiler/src/beam_bs.erl deleted file mode 100644 index 15d8d687fc..0000000000 --- a/lib/compiler/src/beam_bs.erl +++ /dev/null @@ -1,183 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1999-2018. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% -%% -%% Purpose: Peephole optimization of binary syntax instructions. - --module(beam_bs). - --export([module/2]). --import(lists, [reverse/1]). - --spec module(beam_utils:module_code(), [compile:option()]) -> - {'ok',beam_utils:module_code()}. - -module({Mod,Exp,Attr,Fs0,Lc}, _Opt) -> - Fs = [function(F) || F <- Fs0], - {ok,{Mod,Exp,Attr,Fs,Lc}}. - -function({function,Name,Arity,CLabel,Is0}) -> - try - Is = bs_opt(Is0), - {function,Name,Arity,CLabel,Is} - catch - Class:Error:Stack -> - io:fwrite("Function: ~w/~w\n", [Name,Arity]), - erlang:raise(Class, Error, Stack) - end. - -%%% -%%% Evaluate construction of constant bit fields. -%%% Combine bs_skip_bits2 and bs_test_tail2 instructions. -%%% - -bs_opt([{bs_put,_,_,_}=I|Is0]) -> - {BsPuts0,Is} = collect_bs_puts(Is0, [I]), - BsPuts = opt_bs_puts(BsPuts0), - BsPuts ++ bs_opt(Is); -bs_opt([{test,bs_skip_bits2,F,[Ctx,{integer,I},Unit,_Flags]}, - {test,bs_test_tail2,F,[Ctx,Bits]}|Is]) -> - [{test,bs_test_tail2,F,[Ctx,Bits+I*Unit]}|bs_opt(Is)]; -bs_opt([{test,bs_skip_bits2,F,[Ctx,{integer,I1},Unit1,Flags]}, - {test,bs_skip_bits2,F,[Ctx,{integer,I2},Unit2,_]}|Is]) -> - I = {test,bs_skip_bits2,F, - [Ctx,{integer,I1*Unit1+I2*Unit2},1,Flags]}, - bs_opt([I|Is]); -bs_opt([I|Is]) -> - [I|bs_opt(Is)]; -bs_opt([]) -> []. - -collect_bs_puts([{bs_put,_,_,_}=I|Is], Acc) -> - collect_bs_puts(Is, [I|Acc]); -collect_bs_puts([_|_]=Is, Acc) -> - {reverse(Acc),Is}. - -opt_bs_puts(Is) -> - opt_bs_1(Is, []). - -opt_bs_1([{bs_put,Fail, - {bs_put_float,1,Flags0},[{integer,Sz},Src]}=I0|Is], Acc) -> - try eval_put_float(Src, Sz, Flags0) of - <<Int:Sz>> -> - Flags = force_big(Flags0), - I = {bs_put,Fail,{bs_put_integer,1,Flags}, - [{integer,Sz},{integer,Int}]}, - opt_bs_1([I|Is], Acc) - catch - error:_ -> - opt_bs_1(Is, [I0|Acc]) - end; -opt_bs_1([{bs_put,_,{bs_put_integer,1,_},[{integer,8},{integer,_}]}|_]=IsAll, - Acc0) -> - {Is,Acc} = bs_collect_string(IsAll, Acc0), - opt_bs_1(Is, Acc); -opt_bs_1([{bs_put,Fail,{bs_put_integer,1,F},[{integer,Sz},{integer,N}]}=I|Is0], - Acc) when Sz > 8 -> - case field_endian(F) of - big -> - %% We can do this optimization for any field size without - %% risk for code explosion. - case bs_split_int(N, Sz, Fail, Is0) of - no_split -> opt_bs_1(Is0, [I|Acc]); - Is -> opt_bs_1(Is, Acc) - end; - little when Sz < 128 -> - %% We only try to optimize relatively small fields, to - %% avoid an explosion in code size. - <<Int:Sz>> = <<N:Sz/little>>, - Flags = force_big(F), - Is = [{bs_put,Fail,{bs_put_integer,1,Flags}, - [{integer,Sz},{integer,Int}]}|Is0], - opt_bs_1(Is, Acc); - _ -> %native or too wide little field - opt_bs_1(Is0, [I|Acc]) - end; -opt_bs_1([{bs_put,Fail,{Op,U,F},[{integer,Sz},Src]}|Is], Acc) when U > 1 -> - opt_bs_1([{bs_put,Fail,{Op,1,F},[{integer,U*Sz},Src]}|Is], Acc); -opt_bs_1([I|Is], Acc) -> - opt_bs_1(Is, [I|Acc]); -opt_bs_1([], Acc) -> reverse(Acc). - -eval_put_float(Src, Sz, Flags) when Sz =< 256 -> - %%Only evaluate if Sz is reasonable. - Val = value(Src), - case field_endian(Flags) of - little -> <<Val:Sz/little-float-unit:1>>; - big -> <<Val:Sz/big-float-unit:1>> - %% native intentionally not handled here - we can't optimize - %% it. - end. - -value({integer,I}) -> I; -value({float,F}) -> F. - -bs_collect_string(Is, [{bs_put,_,{bs_put_string,Len,{string,Str}},[]}|Acc]) -> - bs_coll_str_1(Is, Len, reverse(Str), Acc); -bs_collect_string(Is, Acc) -> - bs_coll_str_1(Is, 0, [], Acc). - -bs_coll_str_1([{bs_put,_,{bs_put_integer,U,_},[{integer,Sz},{integer,V}]}|Is], - Len, StrAcc, IsAcc) when U*Sz =:= 8 -> - Byte = V band 16#FF, - bs_coll_str_1(Is, Len+1, [Byte|StrAcc], IsAcc); -bs_coll_str_1(Is, Len, StrAcc, IsAcc) -> - {Is,[{bs_put,{f,0},{bs_put_string,Len,{string,reverse(StrAcc)}},[]}|IsAcc]}. - -field_endian({field_flags,F}) -> field_endian_1(F). - -field_endian_1([big=E|_]) -> E; -field_endian_1([little=E|_]) -> E; -field_endian_1([native=E|_]) -> E; -field_endian_1([_|Fs]) -> field_endian_1(Fs). - -force_big({field_flags,F}) -> - {field_flags,force_big_1(F)}. - -force_big_1([big|_]=Fs) -> Fs; -force_big_1([little|Fs]) -> [big|Fs]; -force_big_1([F|Fs]) -> [F|force_big_1(Fs)]. - -bs_split_int(0, Sz, _, _) when Sz > 64 -> - %% We don't want to split in this case because the - %% string will consist of only zeroes. - no_split; -bs_split_int(-1, Sz, _, _) when Sz > 64 -> - %% We don't want to split in this case because the - %% string will consist of only 255 bytes. - no_split; -bs_split_int(N, Sz, Fail, Acc) -> - FirstByteSz = case Sz rem 8 of - 0 -> 8; - Rem -> Rem - end, - bs_split_int_1(N, FirstByteSz, Sz, Fail, Acc). - -bs_split_int_1(-1, _, Sz, Fail, Acc) when Sz > 64 -> - I = {bs_put,Fail,{bs_put_integer,1,{field_flags,[big]}}, - [{integer,Sz},{integer,-1}]}, - [I|Acc]; -bs_split_int_1(0, _, Sz, Fail, Acc) when Sz > 64 -> - I = {bs_put,Fail,{bs_put_integer,1,{field_flags,[big]}}, - [{integer,Sz},{integer,0}]}, - [I|Acc]; -bs_split_int_1(N, ByteSz, Sz, Fail, Acc) when Sz > 0 -> - Mask = (1 bsl ByteSz) - 1, - I = {bs_put,Fail,{bs_put_integer,1,{field_flags,[big]}}, - [{integer,ByteSz},{integer,N band Mask}]}, - bs_split_int_1(N bsr ByteSz, 8, Sz-ByteSz, Fail, [I|Acc]); -bs_split_int_1(_, _, _, _, Acc) -> Acc. diff --git a/lib/compiler/src/beam_dict.erl b/lib/compiler/src/beam_dict.erl index 990e86062a..b2056332e6 100644 --- a/lib/compiler/src/beam_dict.erl +++ b/lib/compiler/src/beam_dict.erl @@ -126,18 +126,17 @@ import(Mod0, Name0, Arity, #asm{imports=Imp0,next_import=NextIndex}=D0) {NextIndex,D2#asm{imports=Imp,next_import=NextIndex+1}} end. -%% Returns the index for a string in the string table (adding the string to the -%% table if necessary). +%% Returns the index for a binary string in the string table (adding +%% the string to the table if necessary). %% string(String, Dict) -> {Offset, Dict'} --spec string(string(), bdict()) -> {non_neg_integer(), bdict()}. +-spec string(binary(), bdict()) -> {non_neg_integer(), bdict()}. -string(Str, Dict) when is_list(Str) -> +string(BinString, Dict) when is_binary(BinString) -> #asm{strings=Strings,string_offset=NextOffset} = Dict, - StrBin = list_to_binary(Str), - case old_string(StrBin, Strings) of + case old_string(BinString, Strings) of none -> - NewDict = Dict#asm{strings = <<Strings/binary,StrBin/binary>>, - string_offset=NextOffset+byte_size(StrBin)}, + NewDict = Dict#asm{strings = <<Strings/binary,BinString/binary>>, + string_offset=NextOffset+byte_size(BinString)}, {NextOffset,NewDict}; Offset when is_integer(Offset) -> {NextOffset-Offset,Dict} diff --git a/lib/compiler/src/beam_ssa_codegen.erl b/lib/compiler/src/beam_ssa_codegen.erl index d3facc5911..fe1a0c8480 100644 --- a/lib/compiler/src/beam_ssa_codegen.erl +++ b/lib/compiler/src/beam_ssa_codegen.erl @@ -1071,8 +1071,8 @@ cg_block([#cg_set{op={bif,Name},dst=Dst0,args=Args0}]=Is0, {Dst0,Fail}, St0) -> {z,_} -> %% The result of the BIF call will only be used once. Convert to %% a test instruction. - Test = bif_to_test(Name, Args, ensure_label(Fail, St0)), - {Test,St0}; + {Test,St1} = bif_to_test(Name, Args, ensure_label(Fail, St0), St0), + {Test,St1}; _ -> %% Must explicitly call the BIF since the result will be used %% more than once. @@ -1269,16 +1269,17 @@ cg_copy_1([], _St) -> []. element(1, Val) =:= atom orelse element(1, Val) =:= literal)). +bif_to_test('or', [V1,V2], {f,Lbl}=Fail, St0) when Lbl =/= 0 -> + {SuccLabel,St} = new_label(St0), + {[{test,is_eq_exact,{f,SuccLabel},[V1,{atom,false}]}, + {test,is_eq_exact,Fail,[V2,{atom,true}]}, + {label,SuccLabel}],St}; +bif_to_test(Op, Args, Fail, St) -> + {bif_to_test(Op, Args, Fail),St}. + bif_to_test('and', [V1,V2], Fail) -> [{test,is_eq_exact,Fail,[V1,{atom,true}]}, {test,is_eq_exact,Fail,[V2,{atom,true}]}]; -bif_to_test('or', [V1,V2], {f,Lbl}=Fail) when Lbl =/= 0 -> - %% Labels are spaced 2 apart. We can create a new - %% label by incrementing the Fail label. - SuccLabel = Lbl + 1, - [{test,is_eq_exact,{f,SuccLabel},[V1,{atom,false}]}, - {test,is_eq_exact,Fail,[V2,{atom,true}]}, - {label,SuccLabel}]; bif_to_test('not', [Var], Fail) -> [{test,is_eq_exact,Fail,[Var,{atom,false}]}]; bif_to_test(Name, Args, Fail) -> @@ -2017,9 +2018,7 @@ is_gc_bif(Bif, Args) -> %% new_label(St) -> {L,St}. new_label(#cg{lcount=Next}=St) -> - %% Advance the label counter by 2 to allow us to create - %% a label for 'or' by incrementing an existing label. - {Next,St#cg{lcount=Next+2}}. + {Next,St#cg{lcount=Next+1}}. %% call_line(tail|body, Func, Anno) -> [] | [{line,...}]. %% Produce a line instruction if it will be needed by the diff --git a/lib/compiler/src/beam_ssa_opt.erl b/lib/compiler/src/beam_ssa_opt.erl index 2dda67eac6..6f7044f006 100644 --- a/lib/compiler/src/beam_ssa_opt.erl +++ b/lib/compiler/src/beam_ssa_opt.erl @@ -22,7 +22,7 @@ -export([module/2]). -include("beam_ssa.hrl"). --import(lists, [all/2,append/1,foldl/3,keyfind/3,member/2, +-import(lists, [append/1,foldl/3,keyfind/3,member/2, reverse/1,reverse/2, splitwith/2,takewhile/2,unzip/1]). @@ -52,25 +52,30 @@ passes(Opts0) -> ?PASS(ssa_opt_coalesce_phis), ?PASS(ssa_opt_element), ?PASS(ssa_opt_linearize), + ?PASS(ssa_opt_tuple_size), ?PASS(ssa_opt_record), %% Run ssa_opt_cse twice, because it will help ssa_opt_dead, - %% and ssa_opt_dead will help ssa_opt_cse. Run ssa_opt_live - %% twice, because it will help ssa_opt_dead and ssa_opt_dead - %% will help ssa_opt_live. + %% and ssa_opt_dead will help ssa_opt_cse. + %% + %% Run ssa_opt_live twice, because it will help ssa_opt_dead + %% and ssa_opt_dead will help ssa_opt_live. + %% + %% Run beam_ssa_type twice, because there will be more + %% opportunities for optimizations after running beam_ssa_dead. ?PASS(ssa_opt_cse), ?PASS(ssa_opt_type), ?PASS(ssa_opt_live), + ?PASS(ssa_opt_bs_puts), ?PASS(ssa_opt_dead), ?PASS(ssa_opt_cse), %Second time. ?PASS(ssa_opt_float), + ?PASS(ssa_opt_type), %Second time. ?PASS(ssa_opt_live), %Second time. ?PASS(ssa_opt_bsm), ?PASS(ssa_opt_bsm_units), ?PASS(ssa_opt_bsm_shortcut), - ?PASS(ssa_opt_misc), - ?PASS(ssa_opt_tuple_size), ?PASS(ssa_opt_sw), ?PASS(ssa_opt_blockify), ?PASS(ssa_opt_sink), @@ -855,14 +860,9 @@ live_opt_is([#b_set{op=succeeded,dst=SuccDst=SuccDstVar, #b_set{dst=Dst}=I|Is], Live0, Acc) -> case gb_sets:is_member(Dst, Live0) of true -> - case gb_sets:is_member(SuccDst, Live0) of - true -> - Live1 = gb_sets:add(Dst, Live0), - Live = gb_sets:delete_any(SuccDst, Live1), - live_opt_is([I|Is], Live, [SuccI|Acc]); - false -> - live_opt_is([I|Is], Live0, Acc) - end; + Live1 = gb_sets:add(Dst, Live0), + Live = gb_sets:delete_any(SuccDst, Live1), + live_opt_is([I|Is], Live, [SuccI|Acc]); false -> case live_opt_unused(I) of {replace,NewI0} -> @@ -902,7 +902,13 @@ live_opt_unused(#b_set{op=get_map_element}=Set) -> live_opt_unused(_) -> keep. %%% -%%% Optimize binary matching instructions. +%%% Optimize binary matching. +%%% +%%% * If the value of segment is never extracted, rewrite +%%% to a bs_skip instruction. +%%% +%%% * Coalesce adjacent bs_skip instructions and skip instructions +%%% with bs_test_tail. %%% ssa_opt_bsm(#st{ssa=Linear}=St) -> @@ -910,9 +916,10 @@ ssa_opt_bsm(#st{ssa=Linear}=St) -> Extracted = cerl_sets:from_list(Extracted0), St#st{ssa=bsm_skip(Linear, Extracted)}. -bsm_skip([{L,#b_blk{is=Is0}=Blk}|Bs], Extracted) -> +bsm_skip([{L,#b_blk{is=Is0}=Blk}|Bs0], Extracted) -> + Bs = bsm_skip(Bs0, Extracted), Is = bsm_skip_is(Is0, Extracted), - [{L,Blk#b_blk{is=Is}}|bsm_skip(Bs, Extracted)]; + coalesce_skips({L,Blk#b_blk{is=Is}}, Bs); bsm_skip([], _) -> []. bsm_skip_is([I0|Is], Extracted) -> @@ -943,6 +950,63 @@ bsm_extracted([{_,#b_blk{is=Is}}|Bs]) -> end; bsm_extracted([]) -> []. +coalesce_skips({L,#b_blk{is=[#b_set{op=bs_extract}=Extract|Is0], + last=Last0}=Blk0}, Bs0) -> + case coalesce_skips_is(Is0, Last0, Bs0) of + not_possible -> + [{L,Blk0}|Bs0]; + {Is,Last,Bs} -> + Blk = Blk0#b_blk{is=[Extract|Is],last=Last}, + [{L,Blk}|Bs] + end; +coalesce_skips({L,#b_blk{is=Is0,last=Last0}=Blk0}, Bs0) -> + case coalesce_skips_is(Is0, Last0, Bs0) of + not_possible -> + [{L,Blk0}|Bs0]; + {Is,Last,Bs} -> + Blk = Blk0#b_blk{is=Is,last=Last}, + [{L,Blk}|Bs] + end. + +coalesce_skips_is([#b_set{op=bs_match, + args=[#b_literal{val=skip}, + Ctx0,Type,Flags, + #b_literal{val=Size0}, + #b_literal{val=Unit0}]}=Skip0, + #b_set{op=succeeded}], + #b_br{succ=L2,fail=Fail}=Br0, + Bs0) when is_integer(Size0) -> + case Bs0 of + [{L2,#b_blk{is=[#b_set{op=bs_match, + dst=SkipDst, + args=[#b_literal{val=skip},_,_,_, + #b_literal{val=Size1}, + #b_literal{val=Unit1}]}, + #b_set{op=succeeded}=Succeeded], + last=#b_br{fail=Fail}=Br}}|Bs] when is_integer(Size1) -> + SkipBits = Size0 * Unit0 + Size1 * Unit1, + Skip = Skip0#b_set{dst=SkipDst, + args=[#b_literal{val=skip},Ctx0, + Type,Flags, + #b_literal{val=SkipBits}, + #b_literal{val=1}]}, + Is = [Skip,Succeeded], + {Is,Br,Bs}; + [{L2,#b_blk{is=[#b_set{op=bs_test_tail, + args=[_Ctx,#b_literal{val=TailSkip}]}], + last=#b_br{succ=NextSucc,fail=Fail}}}|Bs] -> + SkipBits = Size0 * Unit0, + TestTail = Skip0#b_set{op=bs_test_tail, + args=[Ctx0,#b_literal{val=SkipBits+TailSkip}]}, + Br = Br0#b_br{bool=TestTail#b_set.dst,succ=NextSucc}, + Is = [TestTail], + {Is,Br,Bs}; + _ -> + not_possible + end; +coalesce_skips_is(_, _, _) -> + not_possible. + %%% %%% Short-cutting binary matching instructions. %%% @@ -1117,91 +1181,172 @@ bsm_units_join_1([], _MapA, Right) -> Right. %%% -%%% Miscellanous optimizations in execution order. +%%% Optimize binary construction. +%%% +%%% If an integer segment or a float segment has a literal size and +%%% a literal value, convert to a binary segment. Coalesce adjacent +%%% literal binary segments. Literal binary segments will be converted +%%% to bs_put_string instructions in later pass. %%% -ssa_opt_misc(#st{ssa=Linear}=St) -> - St#st{ssa=misc_opt(Linear, #{})}. +ssa_opt_bs_puts(#st{ssa=Linear0,cnt=Count0}=St) -> + {Linear,Count} = opt_bs_puts(Linear0, Count0, []), + St#st{ssa=Linear,cnt=Count}. -misc_opt([{L,#b_blk{is=Is0,last=Last0}=Blk0}|Bs], Sub0) -> - {Is,Sub} = misc_opt_is(Is0, Sub0, []), - Last = sub(Last0, Sub), - Blk = Blk0#b_blk{is=Is,last=Last}, - [{L,Blk}|misc_opt(Bs, Sub)]; -misc_opt([], _) -> []. +opt_bs_puts([{L,#b_blk{is=Is}=Blk0}|Bs], Count0, Acc0) -> + case Is of + [#b_set{op=bs_put}=I0] -> + case opt_bs_put(L, I0, Blk0, Count0, Acc0) of + not_possible -> + opt_bs_puts(Bs, Count0, [{L,Blk0}|Acc0]); + {Count,Acc1} -> + Acc = opt_bs_puts_merge(Acc1), + opt_bs_puts(Bs, Count, Acc) + end; + _ -> + opt_bs_puts(Bs, Count0, [{L,Blk0}|Acc0]) + end; +opt_bs_puts([], Count, Acc) -> + {reverse(Acc),Count}. + +opt_bs_puts_merge([{L1,#b_blk{is=Is}=Blk0},{L2,#b_blk{is=AccIs}}=BAcc|Acc]) -> + case {AccIs,Is} of + {[#b_set{op=bs_put, + args=[#b_literal{val=binary}, + #b_literal{}, + #b_literal{val=Bin0}, + #b_literal{val=all}, + #b_literal{val=1}]}], + [#b_set{op=bs_put, + args=[#b_literal{val=binary}, + #b_literal{}, + #b_literal{val=Bin1}, + #b_literal{val=all}, + #b_literal{val=1}]}=I0]} -> + %% Coalesce the two segments to one. + Bin = <<Bin0/bitstring,Bin1/bitstring>>, + I = I0#b_set{args=bs_put_args(binary, Bin, all)}, + Blk = Blk0#b_blk{is=[I]}, + [{L2,Blk}|Acc]; + {_,_} -> + [{L1,Blk0},BAcc|Acc] + end. -misc_opt_is([#b_set{op=phi}=I0|Is], Sub0, Acc) -> - #b_set{dst=Dst,args=Args} = I = sub(I0, Sub0), - case all_same(Args) of +opt_bs_put(L, I0, #b_blk{last=Br0}=Blk0, Count0, Acc) -> + case opt_bs_put(I0) of + [Bin] when is_bitstring(Bin) -> + Args = bs_put_args(binary, Bin, all), + I = I0#b_set{args=Args}, + Blk = Blk0#b_blk{is=[I]}, + {Count0,[{L,Blk}|Acc]}; + [{int,Int,Size},Bin] when is_bitstring(Bin) -> + %% Construct a bs_put_integer instruction following + %% by a bs_put_binary instruction. + IntArgs = bs_put_args(integer, Int, Size), + BinArgs = bs_put_args(binary, Bin, all), + {BinL,BinVarNum} = {Count0,Count0+1}, + Count = Count0 + 2, + BinVar = #b_var{name={'@ssa_bool',BinVarNum}}, + BinI = I0#b_set{dst=BinVar,args=BinArgs}, + BinBlk = Blk0#b_blk{is=[BinI],last=Br0#b_br{bool=BinVar}}, + IntI = I0#b_set{args=IntArgs}, + IntBlk = Blk0#b_blk{is=[IntI],last=Br0#b_br{succ=BinL}}, + {Count,[{BinL,BinBlk},{L,IntBlk}|Acc]}; + not_possible -> + not_possible + end. + +opt_bs_put(#b_set{args=[#b_literal{val=binary},_,#b_literal{val=Val}, + #b_literal{val=all},#b_literal{val=Unit}]}) + when is_bitstring(Val) -> + if + bit_size(Val) rem Unit =:= 0 -> + [Val]; true -> - %% Eliminate the phi node if there is just one source - %% value or if the values are identical. - [{Val,_}|_] = Args, - Sub = Sub0#{Dst=>Val}, - misc_opt_is(Is, Sub, Acc); - false -> - misc_opt_is(Is, Sub0, [I|Acc]) - end; -misc_opt_is([#b_set{op={bif,'and'}}=I0], Sub, Acc) -> - #b_set{dst=Dst,args=Args} = I = sub(I0, Sub), - case eval_and(Args) of - error -> - misc_opt_is([], Sub, [I|Acc]); - Val -> - misc_opt_is([], Sub#{Dst=>Val}, Acc) + not_possible end; -misc_opt_is([#b_set{op={bif,'or'}}=I0], Sub, Acc) -> - #b_set{dst=Dst,args=Args} = I = sub(I0, Sub), - case eval_or(Args) of - error -> - misc_opt_is([], Sub, [I|Acc]); - Val -> - misc_opt_is([], Sub#{Dst=>Val}, Acc) - end; -misc_opt_is([#b_set{}=I0|Is], Sub, Acc) -> - #b_set{op=Op,dst=Dst,args=Args} = I = sub(I0, Sub), - case make_literal(Op, Args) of - #b_literal{}=Literal -> - misc_opt_is(Is, Sub#{Dst=>Literal}, Acc); - error -> - misc_opt_is(Is, Sub, [I|Acc]) +opt_bs_put(#b_set{args=[#b_literal{val=Type},#b_literal{val=Flags}, + #b_literal{val=Val},#b_literal{val=Size}, + #b_literal{val=Unit}]}=I0) when is_integer(Size) -> + EffectiveSize = Size * Unit, + if + EffectiveSize > 0 -> + case {Type,opt_bs_put_endian(Flags)} of + {integer,big} when is_integer(Val) -> + if + EffectiveSize < 64 -> + [<<Val:EffectiveSize>>]; + true -> + opt_bs_put_split_int(Val, EffectiveSize) + end; + {integer,little} when is_integer(Val), EffectiveSize < 128 -> + %% To avoid an explosion in code size, we only try + %% to optimize relatively small fields. + <<Int:EffectiveSize>> = <<Val:EffectiveSize/little>>, + Args = bs_put_args(Type, Int, EffectiveSize), + I = I0#b_set{args=Args}, + opt_bs_put(I); + {binary,_} when is_bitstring(Val) -> + <<Bitstring:EffectiveSize/bits,_/bits>> = Val, + [Bitstring]; + {float,Endian} -> + try + [opt_bs_put_float(Val, EffectiveSize, Endian)] + catch error:_ -> + not_possible + end; + {_,_} -> + not_possible + end; + true -> + not_possible end; -misc_opt_is([], Sub, Acc) -> - {reverse(Acc),Sub}. +opt_bs_put(#b_set{}) -> not_possible. -all_same([{H,_}|T]) -> - all(fun({E,_}) -> E =:= H end, T). - -make_literal(put_tuple, Args) -> - case make_literal_list(Args, []) of - error -> - error; - List -> - #b_literal{val=list_to_tuple(List)} - end; -make_literal(put_list, [#b_literal{val=H},#b_literal{val=T}]) -> - #b_literal{val=[H|T]}; -make_literal(_, _) -> error. +opt_bs_put_float(N, Sz, Endian) -> + case Endian of + big -> <<N:Sz/big-float-unit:1>>; + little -> <<N:Sz/little-float-unit:1>> + end. -make_literal_list([#b_literal{val=H}|T], Acc) -> - make_literal_list(T, [H|Acc]); -make_literal_list([_|_], _) -> - error; -make_literal_list([], Acc) -> - reverse(Acc). - -eval_and(Args) -> - case Args of - [_,#b_literal{val=false}=Res] -> Res; - [Res,#b_literal{val=true}] -> Res; - [_,_] -> error +bs_put_args(Type, Val, Size) -> + [#b_literal{val=Type}, + #b_literal{val=[unsigned,big]}, + #b_literal{val=Val}, + #b_literal{val=Size}, + #b_literal{val=1}]. + +opt_bs_put_endian([big=E|_]) -> E; +opt_bs_put_endian([little=E|_]) -> E; +opt_bs_put_endian([native=E|_]) -> E; +opt_bs_put_endian([_|Fs]) -> opt_bs_put_endian(Fs). + +opt_bs_put_split_int(Int, Size) -> + Pos = opt_bs_put_split_int_1(Int, 0, Size - 1), + UpperSize = Size - Pos, + if + Pos =:= 0 -> + %% Value is 0 or -1 -- keep the original instruction. + not_possible; + UpperSize < 64 -> + %% No or few leading zeroes or ones. + [<<Int:Size>>]; + true -> + %% There are 64 or more leading ones or zeroes in + %% the resulting binary. Split into two separate + %% segments to avoid an explosion in code size. + [{int,Int bsr Pos,UpperSize},<<Int:Pos>>] end. -eval_or(Args) -> - case Args of - [Res,#b_literal{val=false}] -> Res; - [_,#b_literal{val=true}=Res] -> Res; - [_,_] -> error +opt_bs_put_split_int_1(_Int, L, R) when L > R -> + 8 * ((L + 7) div 8); +opt_bs_put_split_int_1(Int, L, R) -> + Mid = (L + R) div 2, + case Int bsr Mid of + Upper when Upper =:= 0; Upper =:= -1 -> + opt_bs_put_split_int_1(Int, L, Mid - 1); + _ -> + opt_bs_put_split_int_1(Int, Mid + 1, R) end. %%% diff --git a/lib/compiler/src/beam_ssa_pre_codegen.erl b/lib/compiler/src/beam_ssa_pre_codegen.erl index 56fe9b4793..fa1b7bb71e 100644 --- a/lib/compiler/src/beam_ssa_pre_codegen.erl +++ b/lib/compiler/src/beam_ssa_pre_codegen.erl @@ -1996,18 +1996,26 @@ reserve_zregs(Blocks, Intervals, Res) -> reserve_zreg([#b_set{op={bif,tuple_size},dst=Dst}, #b_set{op={bif,'=:='},args=[Dst,Val]}], Last, ShortLived, A0) -> case {Val,Last} of - {#b_literal{val=Arity},#b_br{}} when Arity bsr 32 =:= 0 -> + {#b_literal{val=Arity},#b_br{bool=#b_var{}}} when Arity bsr 32 =:= 0 -> %% These two instructions can be combined to a test_arity %% instruction provided that the arity variable is short-lived. reserve_zreg_1(Dst, ShortLived, A0); {_,_} -> - %% Either the arity is too big, or the boolean value from - %% '=:=' will be returned. + %% Either the arity is too big, or the boolean value is not + %% used in a conditional branch. A0 end; reserve_zreg([#b_set{op={bif,tuple_size},dst=Dst}], #b_switch{}, ShortLived, A) -> reserve_zreg_1(Dst, ShortLived, A); +reserve_zreg([#b_set{op={bif,'xor'}}], _Last, _ShortLived, A) -> + %% There is no short, easy way to rewrite 'xor' to a series of + %% test instructions. + A; +reserve_zreg([#b_set{op={bif,is_record}}], _Last, _ShortLived, A) -> + %% There is no short, easy way to rewrite is_record/2 to a series of + %% test instructions. + A; reserve_zreg([#b_set{op=Op,dst=Dst}|Is], Last, ShortLived, A0) -> IsZReg = case Op of bs_match_string -> true; diff --git a/lib/compiler/src/beam_ssa_type.erl b/lib/compiler/src/beam_ssa_type.erl index 95fc3bb0e9..ede57875e2 100644 --- a/lib/compiler/src/beam_ssa_type.erl +++ b/lib/compiler/src/beam_ssa_type.erl @@ -23,7 +23,7 @@ -include("beam_ssa.hrl"). -import(lists, [all/2,any/2,droplast/1,foldl/3,last/1,member/2, - reverse/1,sort/1]). + partition/2,reverse/1,sort/1]). -define(UNICODE_INT, #t_integer{elements={0,16#10FFFF}}). @@ -139,6 +139,19 @@ opt_is([#b_set{op=phi,dst=Dst,args=Args0}=I0|Is], Ds = Ds0#{Dst=>I}, opt_is(Is, Ts, Ds, Ls, Sub0, [I|Acc]) end; +opt_is([#b_set{op=succeeded,args=Args0,dst=Dst}=I], + Ts0, Ds0, Ls, Sub0, Acc) -> + Args = simplify_args(Args0, Sub0, Ts0), + Type = type(succeeded, Args, Ts0, Ds0), + case get_literal_from_type(Type) of + #b_literal{}=Lit -> + Sub = Sub0#{Dst=>Lit}, + opt_is([], Ts0, Ds0, Ls, Sub, Acc); + none -> + Ts = update_types(I, Ts0, Ds0), + Ds = Ds0#{Dst=>I}, + opt_is([], Ts, Ds, Ls, Sub0, [I|Acc]) + end; opt_is([#b_set{args=Args0,dst=Dst}=I0|Is], Ts0, Ds0, Ls, Sub0, Acc) -> Args = simplify_args(Args0, Sub0, Ts0), @@ -153,8 +166,15 @@ opt_is([#b_set{args=Args0,dst=Dst}=I0|Is], Sub = Sub0#{Dst=>Lit}, opt_is(Is, Ts0, Ds0, Ls, Sub, Acc); #b_var{}=Var -> - Sub = Sub0#{Dst=>Var}, - opt_is(Is, Ts0, Ds0, Ls, Sub, Acc) + case Is of + [#b_set{op=succeeded,dst=SuccDst,args=[Dst]}] -> + %% We must remove this 'succeeded' instruction. + Sub = Sub0#{Dst=>Var,SuccDst=>#b_literal{val=true}}, + opt_is([], Ts0, Ds0, Ls, Sub, Acc); + _ -> + Sub = Sub0#{Dst=>Var}, + opt_is(Is, Ts0, Ds0, Ls, Sub, Acc) + end end; opt_is([], Ts, Ds, _Ls, Sub, Acc) -> {reverse(Acc),Ts,Ds,Sub}. @@ -289,8 +309,6 @@ simplify(#b_set{op=put_tuple,args=Args}=I, _Ts) -> none -> I; List -> #b_literal{val=list_to_tuple(List)} end; -simplify(#b_set{op=succeeded,args=[#b_literal{}]}, _Ts) -> - #b_literal{val=true}; simplify(#b_set{op=wait_timeout,args=[#b_literal{val=infinity}]}=I, _Ts) -> I#b_set{op=wait,args=[]}; simplify(I, _Ts) -> I. @@ -436,12 +454,12 @@ update_successors(#b_br{bool=#b_var{}=Bool,succ=Succ,fail=Fail}, Ts0, D0) -> %% no need to include the type database passed on to the %% successors of this block. Ts = maps:remove(Bool, Ts0), - D = update_successor(Fail, Ts, D0), - SuccTs = infer_types(Bool, Ts, D0), + {SuccTs,FailTs} = infer_types(Bool, Ts, D0), + D = update_successor(Fail, FailTs, D0), update_successor(Succ, SuccTs, D); false -> - D = update_successor_bool(Bool, false, Fail, Ts0, D0), - SuccTs = infer_types(Bool, Ts0, D0), + {SuccTs,FailTs} = infer_types(Bool, Ts0, D0), + D = update_successor_bool(Bool, false, Fail, FailTs, D0), update_successor_bool(Bool, true, Succ, SuccTs, D) end; update_successors(#b_switch{arg=#b_var{}=V,fail=Fail,list=List}, Ts0, D0) -> @@ -458,7 +476,8 @@ update_successors(#b_switch{arg=#b_var{}=V,fail=Fail,list=List}, Ts0, D0) -> end, foldl(F, D, List); false -> - D = update_successor(Fail, Ts0, D0), + FailTs = subtract_types([{V,join_sw_list(List, Ts0, none)}], Ts0), + D = update_successor(Fail, FailTs, D0), F = fun({Val,S}, A) -> T = get_type(Val, Ts0), update_successor(S, Ts0#{V=>T}, A) @@ -467,6 +486,10 @@ update_successors(#b_switch{arg=#b_var{}=V,fail=Fail,list=List}, Ts0, D0) -> end; update_successors(#b_ret{}, _Ts, D) -> D. +join_sw_list([{Val,_}|T], Ts, Type) -> + join_sw_list(T, Ts, join(Type, get_type(Val, Ts))); +join_sw_list([], _, Type) -> Type. + update_successor_bool(#b_var{}=Var, BoolValue, S, Ts, D) -> case t_is_boolean(get_type(Var, Ts)) of true -> @@ -542,6 +565,14 @@ type(call, [#b_remote{mod=#b_literal{val=Mod}, {_,_} -> #t_tuple{} end; + {erlang,'++',[List1,List2]} -> + case get_type(List1, Ts) =:= cons orelse + get_type(List2, Ts) =:= cons of + true -> cons; + false -> list + end; + {erlang,'--',[_,_]} -> + list; {math,_,_} -> case is_math_bif(Name, length(Args)) of false -> any; @@ -607,6 +638,8 @@ type(succeeded, [#b_var{}=Src], Ts, Ds) -> #b_set{} -> t_boolean() end; +type(succeeded, [#b_literal{}], _Ts, _Ds) -> + t_atom(true); type(_, _, _, _) -> any. arith_op_type(Args, Ts) -> @@ -873,10 +906,84 @@ get_type(#b_literal{val=Val}, _Ts) -> any end. -infer_types(#b_var{}=V, Ts, #d{ds=Ds}) -> +%% infer_types(Var, Types, #d{}) -> {SuccTypes,FailTypes} +%% Looking at the expression that defines the variable Var, infer +%% the types for the variables in the arguments. Return the updated +%% type database for the case that the expression evaluates to +%% true, and and for the case that it evaluates to false. +%% +%% Here is an example. The variable being asked about is +%% the variable Bool, which is defined like this: +%% +%% Bool = is_nonempty_list L +%% +%% If 'is_nonempty_list L' evaluates to 'true', L must +%% must be cons. The meet of the previously known type of L and 'cons' +%% will be added to SuccTypes. +%% +%% On the other hand, if 'is_nonempty_list L' evaluates to false, L +%% is not cons and cons can be subtracted from the previously known +%% type for L. For example, if L was known to be 'list', subtracting +%% 'cons' would give 'nil' as the only possible type. The result of the +%% subtraction for L will be added to FailTypes. +%% +%% Here is another example, asking about the variable Bool: +%% +%% Head = bif:hd L +%% Bool = succeeded Head +%% +%% 'succeeded Head' will evaluate to 'true' if the instrution that +%% defined Head succeeded. In this case, it is the 'bif:hd L' +%% instruction, which will succeed if L is 'cons'. Thus, the meet of +%% the previous type for L and 'cons' will be added to SuccTypes. +%% +%% If 'succeeded Head' evaluates to 'false', it means that 'bif:hd L' +%% failed and that L is not 'cons'. 'cons' can be subtracted from the +%% previously known type for L and the result put in FailTypes. + +infer_types(#b_var{}=V, Ts, #d{ds=Ds,once=Once}) -> #{V:=#b_set{op=Op,args=Args}} = Ds, - Types = infer_type(Op, Args, Ds), - meet_types(Types, Ts). + Types0 = infer_type(Op, Args, Ds), + + %% We must be careful with types inferred from '=:='. + %% + %% If we have seen L =:= [a], we know that L is 'cons' if the + %% comparison succeeds. However, if the comparison fails, L could + %% still be 'cons'. Therefore, we must not subtract 'cons' from the + %% previous type of L. + %% + %% However, it is safe to subtract a type inferred from '=:=' if + %% it is single-valued, e.g. if it is [] or the atom 'true'. + EqTypes0 = infer_eq_type(Op, Args, Ts, Ds), + {Types1,EqTypes} = partition(fun({_,T}) -> + is_singleton_type(T) + end, EqTypes0), + + %% Don't bother updating the types for variables that + %% are never used again. + Types2 = Types1 ++ Types0, + Types = [P || {InfV,_}=P <- Types2, not cerl_sets:is_element(InfV, Once)], + + {meet_types(EqTypes++Types, Ts),subtract_types(Types, Ts)}. + +infer_eq_type({bif,'=:='}, [#b_var{}=Src,#b_literal{}=Lit], Ts, Ds) -> + Def = maps:get(Src, Ds), + Type = get_type(Lit, Ts), + [{Src,Type}|infer_tuple_size(Def, Lit) ++ + infer_first_element(Def, Lit)]; +infer_eq_type({bif,'=:='}, [#b_var{}=Arg0,#b_var{}=Arg1], Ts, _Ds) -> + %% As an example, assume that L1 is known to be 'list', and L2 is + %% known to be 'cons'. Then if 'L1 =:= L2' evaluates to 'true', it can + %% be inferred that L1 is 'cons' (the meet of 'cons' and 'list'). + Type0 = get_type(Arg0, Ts), + Type1 = get_type(Arg1, Ts), + Type = meet(Type0, Type1), + [{V,MeetType} || + {V,OrigType,MeetType} <- + [{Arg0,Type0,Type},{Arg1,Type1,Type}], + OrigType =/= MeetType]; +infer_eq_type(_Op, _Args, _Ts, _Ds) -> + []. infer_type({bif,element}, [#b_literal{val=Pos},#b_var{}=Tuple], _Ds) -> if @@ -885,20 +992,27 @@ infer_type({bif,element}, [#b_literal{val=Pos},#b_var{}=Tuple], _Ds) -> true -> [] end; -infer_type({bif,'=:='}, [#b_var{}=Src,#b_literal{}=Lit], Ds) -> - Def = maps:get(Src, Ds), - Type = get_type(Lit, #{}), - [{Src,Type}|infer_tuple_size(Def, Lit) ++ - infer_first_element(Def, Lit)]; +infer_type({bif,element}, [#b_var{}=Position,#b_var{}=Tuple], _Ds) -> + [{Position,t_integer()},{Tuple,#t_tuple{}}]; infer_type({bif,Bif}, [#b_var{}=Src]=Args, _Ds) -> case inferred_bif_type(Bif, Args) of any -> []; T -> [{Src,T}] end; +infer_type({bif,binary_part}, [#b_var{}=Src,_], _Ds) -> + [{Src,{binary,8}}]; infer_type({bif,is_map_key}, [_,#b_var{}=Src], _Ds) -> [{Src,map}]; infer_type({bif,map_get}, [_,#b_var{}=Src], _Ds) -> [{Src,map}]; +infer_type({bif,Bif}, [_,_]=Args, _Ds) -> + case inferred_bif_type(Bif, Args) of + any -> []; + T -> [{A,T} || #b_var{}=A <- Args] + end; +infer_type({bif,binary_part}, [#b_var{}=Src,Pos,Len], _Ds) -> + [{Src,{binary,8}}| + [{V,t_integer()} || #b_var{}=V <- [Pos,Len]]]; infer_type(bs_start_match, [#b_var{}=Bin], _Ds) -> [{Bin,{binary,1}}]; infer_type(is_nonempty_list, [#b_var{}=Src], _Ds) -> @@ -969,6 +1083,7 @@ inferred_bif_type(is_number, [_]) -> number; inferred_bif_type(is_tuple, [_]) -> #t_tuple{}; inferred_bif_type(abs, [_]) -> number; inferred_bif_type(bit_size, [_]) -> {binary,1}; +inferred_bif_type('bnot', [_]) -> t_integer(); inferred_bif_type(byte_size, [_]) -> {binary,1}; inferred_bif_type(ceil, [_]) -> number; inferred_bif_type(float, [_]) -> number; @@ -976,10 +1091,25 @@ inferred_bif_type(floor, [_]) -> number; inferred_bif_type(hd, [_]) -> cons; inferred_bif_type(length, [_]) -> list; inferred_bif_type(map_size, [_]) -> map; +inferred_bif_type('not', [_]) -> t_boolean(); inferred_bif_type(round, [_]) -> number; inferred_bif_type(trunc, [_]) -> number; inferred_bif_type(tl, [_]) -> cons; inferred_bif_type(tuple_size, [_]) -> #t_tuple{}; +inferred_bif_type('and', [_,_]) -> t_boolean(); +inferred_bif_type('or', [_,_]) -> t_boolean(); +inferred_bif_type('xor', [_,_]) -> t_boolean(); +inferred_bif_type('band', [_,_]) -> t_integer(); +inferred_bif_type('bor', [_,_]) -> t_integer(); +inferred_bif_type('bsl', [_,_]) -> t_integer(); +inferred_bif_type('bsr', [_,_]) -> t_integer(); +inferred_bif_type('bxor', [_,_]) -> t_integer(); +inferred_bif_type('div', [_,_]) -> t_integer(); +inferred_bif_type('rem', [_,_]) -> t_integer(); +inferred_bif_type('+', [_,_]) -> number; +inferred_bif_type('-', [_,_]) -> number; +inferred_bif_type('*', [_,_]) -> number; +inferred_bif_type('/', [_,_]) -> number; inferred_bif_type(_, _) -> any. infer_tuple_size(#b_set{op={bif,tuple_size},args=[#b_var{}=Tuple]}, @@ -1088,6 +1218,9 @@ t_tuple_size(#t_tuple{size=Size,exact=true}) -> t_tuple_size(_) -> none. +is_singleton_type(Type) -> + get_literal_from_type(Type) =/= none. + %% join(Type1, Type2) -> Type %% Return the "join" of Type1 and Type2. The join is a more general %% type than Type1 and Type2. For example: @@ -1152,14 +1285,40 @@ gcd(A, B) -> meet_types([{V,T0}|Vs], Ts) -> #{V:=T1} = Ts, - T = meet(T0, T1), - meet_types(Vs, Ts#{V:=T}); + case meet(T0, T1) of + T1 -> meet_types(Vs, Ts); + T -> meet_types(Vs, Ts#{V:=T}) + end; meet_types([], Ts) -> Ts. meet([T1,T2|Ts]) -> meet([meet(T1, T2)|Ts]); meet([T]) -> T. +subtract_types([{V,T0}|Vs], Ts) -> + #{V:=T1} = Ts, + case subtract(T1, T0) of + T1 -> subtract_types(Vs, Ts); + T -> subtract_types(Vs, Ts#{V:=T}) + end; +subtract_types([], Ts) -> Ts. + +%% subtract(Type1, Type2) -> Type. +%% Subtract Type2 from Type1. Example: +%% +%% subtract(list, cons) -> nil + +subtract(#t_atom{elements=[_|_]=Set0}, #t_atom{elements=[_|_]=Set1}) -> + case ordsets:subtract(Set0, Set1) of + [] -> none; + [_|_]=Set -> #t_atom{elements=Set} + end; +subtract(number, float) -> #t_integer{}; +subtract(number, #t_integer{elements=any}) -> float; +subtract(list, cons) -> nil; +subtract(list, nil) -> cons; +subtract(T, _) -> T. + %% meet(Type1, Type2) -> Type %% Return the "meet" of Type1 and Type2. The meet is a narrower %% type than Type1 and Type2. For example: diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index 1945faba7f..3d53054f69 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -809,6 +809,14 @@ valfun_4({test,has_map_fields,{f,Lbl},Src,{list,List}}, Vst) -> assert_type(map, Src, Vst), assert_unique_map_keys(List), branch_state(Lbl, Vst); +valfun_4({test,is_list,{f,Lbl},[Src]}, Vst) -> + validate_src([Src], Vst), + Type = case get_term_type(Src, Vst) of + cons -> cons; + nil -> nil; + _ -> list + end, + set_aliased_type(Type, Src, branch_state(Lbl, Vst)); valfun_4({test,is_map,{f,Lbl},[Src]}, Vst0) -> Vst = branch_state(Lbl, Vst0), case Src of @@ -820,20 +828,28 @@ valfun_4({test,is_map,{f,Lbl},[Src]}, Vst0) -> _ -> kill_state(Vst0) end; +valfun_4({test,is_nil,{f,Lbl},[Src]}, Vst0) -> + Vst = case get_term_type(Src, Vst0) of + list -> + branch_state(Lbl, set_type_reg(cons, Src, Vst0)); + _ -> + branch_state(Lbl, Vst0) + end, + set_aliased_type(nil, Src, Vst); valfun_4({test,is_eq_exact,{f,Lbl},[Src,Val]=Ss}, Vst0) -> validate_src(Ss, Vst0), Infer = infer_types(Src, Vst0), Vst1 = Infer(Val, Vst0), - Vst = branch_state(Lbl, Vst1), - case Val of - {literal,Tuple} when is_tuple(Tuple) -> - Type0 = get_term_type(Val, Vst), - Type = upgrade_tuple_type({tuple,tuple_size(Tuple)}, - Type0), - set_aliased_type(Type, Src, Vst); - _ -> - Vst - end; + Vst2 = upgrade_ne_types(Src, Val, Vst1), + Vst3 = branch_state(Lbl, Vst2), + Vst = Vst3#vst{current=Vst1#vst.current}, + upgrade_eq_types(Src, Val, Vst); +valfun_4({test,is_ne_exact,{f,Lbl},[Src,Val]=Ss}, Vst0) -> + validate_src(Ss, Vst0), + Vst1 = upgrade_eq_types(Src, Val, Vst0), + Vst2 = branch_state(Lbl, Vst1), + Vst = Vst2#vst{current=Vst0#vst.current}, + upgrade_ne_types(Src, Val, Vst); valfun_4({test,_Op,{f,Lbl},Src}, Vst) -> validate_src(Src, Vst), branch_state(Lbl, Vst); @@ -920,6 +936,25 @@ valfun_4({get_map_elements,{f,Fail},Src,{list,List}}, Vst) -> valfun_4(_, _) -> error(unknown_instruction). +upgrade_ne_types(Src1, Src2, Vst0) -> + T1 = get_durable_term_type(Src1, Vst0), + T2 = get_durable_term_type(Src2, Vst0), + Type = subtract(T1, T2), + set_aliased_type(Type, Src1, Vst0). + +upgrade_eq_types(Src1, Src2, Vst0) -> + T1 = get_durable_term_type(Src1, Vst0), + T2 = get_durable_term_type(Src2, Vst0), + Meet = meet(T1, T2), + Vst = case T1 =/= Meet of + true -> set_aliased_type(Meet, Src1, Vst0); + false -> Vst0 + end, + case T2 =/= Meet of + true -> set_aliased_type(Meet, Src2, Vst); + false -> Vst + end. + verify_get_map(Fail, Src, List, Vst0) -> assert_not_literal(Src), %OTP 22. assert_type(map, Src, Vst0), @@ -1509,12 +1544,16 @@ assert_not_literal(Literal) -> error({literal_not_allowed,Literal}). %% %% term Any valid Erlang (but not of the special types above). %% +%% binary Binary or bitstring. +%% %% bool The atom 'true' or the atom 'false'. %% %% cons Cons cell: [_|_] %% %% nil Empty list: [] %% +%% list List: [] or [_|_] +%% %% {tuple,[Sz]} Tuple. An element has been accessed using %% element/2 or setelement/3 so that it is known that %% the type is a tuple of size at least Sz. @@ -1535,7 +1574,7 @@ assert_not_literal(Literal) -> error({literal_not_allowed,Literal}). %% %% map Map. %% -%% +%% none A conflict in types. There will be an exception at runtime. %% %% FRAGILITY %% --------- @@ -1548,6 +1587,47 @@ assert_not_literal(Literal) -> error({literal_not_allowed,Literal}). %% Such terms are wrapped in a {fragile,Type} tuple, where Type is one %% of the types described above. +%% meet(Type1, Type2) -> Type +%% Return the meet of two types. The meet is a more specific type. +%% It will be 'none' if the types are in conflict. + +meet(Same, Same) -> + Same; +meet(term, Other) -> + Other; +meet(Other, term) -> + Other; +meet(T1, T2) -> + case {erlang:min(T1, T2),erlang:max(T1, T2)} of + {{atom,_}=A,{atom,[]}} -> A; + {bool,{atom,B}=Atom} when is_boolean(B) -> Atom; + {bool,{atom,[]}} -> bool; + {cons,list} -> cons; + {{float,_}=T,{float,[]}} -> T; + {{integer,_}=T,{integer,[]}} -> T; + {list,nil} -> nil; + {number,{integer,_}=T} -> T; + {number,{float,_}=T} -> T; + {{tuple,Size1},{tuple,Size2}} -> + case {Size1,Size2} of + {[Sz1],[Sz2]} -> + {tuple,[erlang:max(Sz1, Sz2)]}; + {Sz1,[Sz2]} when Sz2 =< Sz1 -> + {tuple,Sz1}; + {_,_} -> + none + end; + {_,_} -> none + end. + +%% subtract(Type1, Type2) -> Type +%% Subtract Type2 from Type2. Example: +%% subtract(list, nil) -> cons + +subtract(list, nil) -> cons; +subtract(list, cons) -> nil; +subtract(Type, _) -> Type. + assert_type(WantedType, Term, Vst) -> case get_term_type(Term, Vst) of {fragile,Type} -> @@ -1581,25 +1661,27 @@ assert_type(Needed, Actual) -> %% be executed at run-time. upgrade_tuple_type(NewType, {fragile,OldType}) -> - make_fragile(upgrade_tuple_type_1(NewType, OldType)); + Type = upgrade_tuple_type_1(NewType, OldType), + make_fragile(Type); upgrade_tuple_type(NewType, OldType) -> upgrade_tuple_type_1(NewType, OldType). -upgrade_tuple_type_1({tuple,[Sz]}, {tuple,[OldSz]}=T) when Sz < OldSz -> - %% The old type has a higher value for the least tuple size. - T; -upgrade_tuple_type_1({tuple,[Sz]}, {tuple,OldSz}=T) - when is_integer(Sz), is_integer(OldSz), Sz =< OldSz -> - %% The old size is exact, and the new size is smaller than the old size. - T; -upgrade_tuple_type_1({tuple,_}=T, _) -> - %% The new type information is exact or has a higher value for - %% the least tuple size. - %% Note that inconsistencies are also handled in this - %% clause, e.g. if the old type was an integer or a tuple accessed - %% outside its size; inconsistences will generally cause an exception - %% at run-time but are safe from our point of view. - T. +upgrade_tuple_type_1(NewType, OldType) -> + case meet(NewType, OldType) of + none -> + %% Unoptimized code may look like this: + %% + %% {test,is_list,Fail,[Reg]}. + %% {test,is_tuple,Fail,[Reg]}. + %% {test,test_arity,Fail,[Reg,5]}. + %% + %% Note that the test_arity instruction can never be reached. + %% To make sure it's not rejected, set the type of Reg to + %% NewType instead of 'none'. + NewType; + Type -> + Type + end. get_tuple_size({integer,[]}) -> 0; get_tuple_size({integer,Sz}) -> Sz; @@ -1608,6 +1690,17 @@ get_tuple_size(_) -> 0. validate_src(Ss, Vst) when is_list(Ss) -> foreach(fun(S) -> get_term_type(S, Vst) end, Ss). +%% get_durable_term_type(Src, ValidatorState) -> Type +%% Get the type of the source Src. The returned type Type will be +%% a standard Erlang type (no catch/try tags or match contexts). +%% Fragility will be stripped. + +get_durable_term_type(Src, Vst) -> + case get_term_type(Src, Vst) of + {fragile,Type} -> Type; + Type -> Type + end. + %% get_move_term_type(Src, ValidatorState) -> Type %% Get the type of the source Src. The returned type Type will be %% a standard Erlang type (no catch/try tags). Match contexts are OK. @@ -1641,6 +1734,8 @@ get_term_type_1(nil=T, _) -> T; get_term_type_1({atom,A}=T, _) when is_atom(A) -> T; get_term_type_1({float,F}=T, _) when is_float(F) -> T; get_term_type_1({integer,I}=T, _) when is_integer(I) -> T; +get_term_type_1({literal,[_|_]}, _) -> cons; +get_term_type_1({literal,Bitstring}, _) when is_bitstring(Bitstring) -> binary; get_term_type_1({literal,Map}, _) when is_map(Map) -> map; get_term_type_1({literal,Tuple}, _) when is_tuple(Tuple) -> {tuple,tuple_size(Tuple)}; @@ -1809,6 +1904,10 @@ merge_types({atom,A}, bool) -> merge_bool(A); merge_types(cons, {literal,[_|_]}) -> cons; +merge_types(cons, nil) -> + list; +merge_types(nil, cons) -> + list; merge_types({literal,[_|_]}, cons) -> cons; merge_types({literal,[_|_]}, {literal,[_|_]}) -> @@ -2041,6 +2140,14 @@ return_type_1(erlang, setelement, 3, Vst) -> {integer,I} -> upgrade_tuple_type({tuple,[I]}, TupleType); _ -> TupleType end; +return_type_1(erlang, '++', 2, Vst) -> + case get_term_type({x,0}, Vst) =:= cons orelse + get_term_type({x,1}, Vst) =:= cons of + true -> cons; + false -> list + end; +return_type_1(erlang, '--', 2, _Vst) -> + list; return_type_1(erlang, F, A, _) -> return_type_erl(F, A); return_type_1(math, F, A, _) -> diff --git a/lib/compiler/src/beam_z.erl b/lib/compiler/src/beam_z.erl index 677094b3cd..415b579240 100644 --- a/lib/compiler/src/beam_z.erl +++ b/lib/compiler/src/beam_z.erl @@ -71,6 +71,31 @@ undo_renames([{get_hd,Src,Dst1},{get_tl,Src,Dst2}|Is]) -> [{get_list,Src,Dst1,Dst2}|undo_renames(Is)]; undo_renames([{get_tl,Src,Dst2},{get_hd,Src,Dst1}|Is]) -> [{get_list,Src,Dst1,Dst2}|undo_renames(Is)]; +undo_renames([{bs_put,_,{bs_put_binary,1,_}, + [{atom,all},{literal,<<>>}]}|Is]) -> + undo_renames(Is); +undo_renames([{bs_put,Fail,{bs_put_binary,1,_Flags}, + [{atom,all},{literal,BinString}]}|Is0]) -> + Bits = bit_size(BinString), + Bytes = Bits div 8, + case Bits rem 8 of + 0 -> + I = {bs_put_string,byte_size(BinString), + {string,BinString}}, + [undo_rename(I)|undo_renames(Is0)]; + Rem -> + <<Binary:Bytes/bytes,Int:Rem>> = BinString, + PutInt = {bs_put_integer,Fail,{integer,Rem},1, + {field_flags,[unsigned,big]},{integer,Int}}, + Is = [PutInt|undo_renames(Is0)], + case Binary of + <<>> -> + Is; + _ -> + [{bs_put_string,byte_size(Binary), + {string,Binary}}|Is] + end + end; undo_renames([I|Is]) -> [undo_rename(I)|undo_renames(Is)]; undo_renames([]) -> []. @@ -79,8 +104,6 @@ undo_rename({bs_put,F,{I,U,Fl},[Sz,Src]}) -> {I,F,Sz,U,Fl,Src}; undo_rename({bs_put,F,{I,Fl},[Src]}) -> {I,F,Fl,Src}; -undo_rename({bs_put,{f,0},{bs_put_string,_,_}=I,[]}) -> - I; undo_rename({bif,bs_add=I,F,[Src1,Src2,{integer,U}],Dst}) -> {I,F,[Src1,Src2,U],Dst}; undo_rename({bif,bs_utf8_size=I,F,[Src],Dst}) -> @@ -101,7 +124,7 @@ undo_rename({test,bs_match_string=Op,F,[Ctx,Bin0]}) -> 0 -> Bin0; Rem -> <<Bin0/bitstring,0:(8-Rem)>> end, - {test,Op,F,[Ctx,Bits,{string,binary_to_list(Bin)}]}; + {test,Op,F,[Ctx,Bits,{string,Bin}]}; undo_rename({put_map,Fail,assoc,S,D,R,L}) -> {put_map_assoc,Fail,S,D,R,L}; undo_rename({put_map,Fail,exact,S,D,R,L}) -> diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl index 14c8c5b4ab..73c66e6efc 100644 --- a/lib/compiler/src/compile.erl +++ b/lib/compiler/src/compile.erl @@ -855,8 +855,6 @@ asm_passes() -> {iff,dblk,{listing,"block"}}, {unless,no_except,{pass,beam_except}}, {iff,dexcept,{listing,"except"}}, - {unless,no_bs_opt,{pass,beam_bs}}, - {iff,dbs,{listing,"bs"}}, {unless,no_jopt,{pass,beam_jump}}, {iff,djmp,{listing,"jump"}}, {unless,no_peep_opt,{pass,beam_peep}}, @@ -2084,7 +2082,6 @@ pre_load() -> L = [beam_a, beam_asm, beam_block, - beam_bs, beam_clean, beam_dict, beam_except, diff --git a/lib/compiler/src/compiler.app.src b/lib/compiler/src/compiler.app.src index 1472e3fde1..108a0ca100 100644 --- a/lib/compiler/src/compiler.app.src +++ b/lib/compiler/src/compiler.app.src @@ -24,7 +24,6 @@ beam_a, beam_asm, beam_block, - beam_bs, beam_clean, beam_dict, beam_disasm, diff --git a/lib/compiler/src/erl_bifs.erl b/lib/compiler/src/erl_bifs.erl index ce9762899e..d925decce6 100644 --- a/lib/compiler/src/erl_bifs.erl +++ b/lib/compiler/src/erl_bifs.erl @@ -195,6 +195,7 @@ is_safe(erlang, is_float, 1) -> true; is_safe(erlang, is_function, 1) -> true; is_safe(erlang, is_integer, 1) -> true; is_safe(erlang, is_list, 1) -> true; +is_safe(erlang, is_map, 1) -> true; is_safe(erlang, is_number, 1) -> true; is_safe(erlang, is_pid, 1) -> true; is_safe(erlang, is_port, 1) -> true; diff --git a/lib/compiler/src/sys_core_inline.erl b/lib/compiler/src/sys_core_inline.erl index 5a6cc45e4a..3380e3f1bd 100644 --- a/lib/compiler/src/sys_core_inline.erl +++ b/lib/compiler/src/sys_core_inline.erl @@ -195,6 +195,9 @@ kill_id_anns(Body) -> cerl_trees:map(fun(#c_fun{anno=A0}=CFun) -> A = kill_id_anns_1(A0), CFun#c_fun{anno=A}; + (#c_var{anno=A0}=Var) -> + A = kill_id_anns_1(A0), + Var#c_var{anno=A}; (Expr) -> %% Mark everything as compiler generated to %% suppress bogus warnings. diff --git a/lib/compiler/test/beam_type_SUITE.erl b/lib/compiler/test/beam_type_SUITE.erl index 918e45ff6f..6efa98de44 100644 --- a/lib/compiler/test/beam_type_SUITE.erl +++ b/lib/compiler/test/beam_type_SUITE.erl @@ -213,6 +213,10 @@ coverage(Config) -> [_|_] -> ok end, + + %% Cover beam_type:verified_type(none). + {'EXIT',{badarith,_}} = (catch (id(2) / id(1)) band 16#ff), + ok. booleans(_Config) -> diff --git a/lib/compiler/test/beam_validator_SUITE.erl b/lib/compiler/test/beam_validator_SUITE.erl index 661b48a080..c9df066958 100644 --- a/lib/compiler/test/beam_validator_SUITE.erl +++ b/lib/compiler/test/beam_validator_SUITE.erl @@ -579,14 +579,23 @@ receive_stacked(Config) -> ok. +aliased_types(Config) -> + Seq = lists:seq(1, 5), + 1 = aliased_types_1(Seq, Config), + + {1,1} = aliased_types_2(Seq), + {42,none} = aliased_types_2([]), + + ok. + %% ERL-735: validator failed to track types on aliased registers, rejecting %% legitimate optimizations. %% %% move x0 y0 %% bif hd L1 x0 %% get_hd y0 %% The validator failed to see that y0 was a list -aliased_types(Config) when is_list(Config) -> - Bug = lists:seq(1, 5), +%% +aliased_types_1(Bug, Config) -> if Config =/= [gurka, gaffel] -> %% Pointless branch. _ = hd(Bug), @@ -594,6 +603,17 @@ aliased_types(Config) when is_list(Config) -> hd(Bug) end. +%% ERL-832: validator failed to realize that a Y register was a cons. +aliased_types_2(Bug) -> + Res = case Bug of + [] -> id(42); + _ -> hd(Bug) + end, + {Res,case Bug of + [] -> none; + _ -> hd(Bug) + end}. + %%%------------------------------------------------------------------------- transform_remove(Remove, Module) -> @@ -652,3 +672,6 @@ night(Turned) -> ok. participating(_, _, _, _) -> ok. + +id(I) -> + I. diff --git a/lib/compiler/test/bs_construct_SUITE.erl b/lib/compiler/test/bs_construct_SUITE.erl index ccc49df005..69017d87e7 100644 --- a/lib/compiler/test/bs_construct_SUITE.erl +++ b/lib/compiler/test/bs_construct_SUITE.erl @@ -153,6 +153,8 @@ l(I_13, I_big1, I_16, Bin) -> [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 16#77,16#FF,16#FF,16#FF,16#FF,16#FF,16#FF,16#FF,16#FF,16#FF,16#FF, 16#FF,16#FF,16#FF,16#FF,16#FF,16#FF]), + ?T(<< (<<"abc",7:3>>):3/binary >>, + [$a,$b,$c]), %% Mix different units. ?T(<<37558955:(I_16-12)/unit:8,1:1>>, @@ -311,6 +313,9 @@ fail(Config) when is_list(Config) -> {'EXIT',{badarg,_}} = (catch <<0:(-(1 bsl 100))>>), {'EXIT',{badarg,_}} = (catch <<Bin/binary,0:(-(1 bsl 100))>>), + %% Unaligned sizes with literal binaries. + {'EXIT',{badarg,_}} = (catch <<0,(<<7777:17>>)/binary>>), + ok. float_bin(Config) when is_list(Config) -> diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl index 6eae7b1404..c17d63cd60 100644 --- a/lib/compiler/test/compile_SUITE.erl +++ b/lib/compiler/test/compile_SUITE.erl @@ -391,7 +391,6 @@ do_file_listings(DataDir, PrivDir, [File|Files]) -> do_listing(Simple, TargetDir, dcg, ".codegen"), do_listing(Simple, TargetDir, dblk, ".block"), do_listing(Simple, TargetDir, dexcept, ".except"), - do_listing(Simple, TargetDir, dbs, ".bs"), do_listing(Simple, TargetDir, djmp, ".jump"), do_listing(Simple, TargetDir, dclean, ".clean"), do_listing(Simple, TargetDir, dpeep, ".peep"), diff --git a/lib/compiler/test/match_SUITE.erl b/lib/compiler/test/match_SUITE.erl index 8393dced06..60ab969929 100644 --- a/lib/compiler/test/match_SUITE.erl +++ b/lib/compiler/test/match_SUITE.erl @@ -25,7 +25,7 @@ match_in_call/1,untuplify/1,shortcut_boolean/1,letify_guard/1, selectify/1,deselectify/1,underscore/1,match_map/1,map_vars_used/1, coverage/1,grab_bag/1,literal_binary/1, - unary_op/1]). + unary_op/1,eq_types/1]). -include_lib("common_test/include/ct.hrl"). @@ -40,7 +40,7 @@ groups() -> match_in_call,untuplify, shortcut_boolean,letify_guard,selectify,deselectify, underscore,match_map,map_vars_used,coverage, - grab_bag,literal_binary,unary_op]}]. + grab_bag,literal_binary,unary_op,eq_types]}]. init_per_suite(Config) -> @@ -390,6 +390,13 @@ untuplify(Config) when is_list(Config) -> %% We do this to cover sys_core_fold:unalias_pat/1. {1,2,3,4,alias,{[1,2],{3,4},alias}} = untuplify_1([1,2], {3,4}, alias), error = untuplify_1([1,2], {3,4}, 42), + + %% Test that a previous bug in v3_codegen is gone. (The sinking of + %% stack frames into only the case arms that needed them was not always + %% safe.) + [33, -1, -33, 1] = untuplify_2(32, 65), + {33, 1, -33, -1} = untuplify_2(65, 32), + ok. untuplify_1(A, B, C) -> @@ -402,6 +409,21 @@ untuplify_1(A, B, C) -> error end. +untuplify_2(V1, V2) -> + {D1,D2,D3,D4} = + if V1 > V2 -> + %% The 1 value was overwritten by the value of V2-V1. + {V1-V2, 1, V2-V1, -1}; + true -> + {V2-V1, -1, V1-V2, 1} + end, + if + D2 > D4 -> + {D1, D2, D3, D4}; + true -> + [D1, D2, D3, D4] + end. + %% Coverage of beam_dead:shortcut_boolean_label/4. shortcut_boolean(Config) when is_list(Config) -> false = shortcut_boolean_1([0]), @@ -848,5 +870,25 @@ unary_op_1(Vop@1) -> end end. +eq_types(_Config) -> + Ref = make_ref(), + Ref = eq_types(Ref, any), + ok. + +eq_types(A, B) -> + %% {put_tuple2,{y,0},{list,[{x,0},{x,1}]}}. + Term0 = {A, B}, + Term = id(Term0), + + %% {test,is_eq_exact,{f,3},[{y,0},{x,0}]}. + %% Here beam_validator must infer that {x,0} has the + %% same type as {y,0}. + Term = Term0, + + %% {get_tuple_element,{x,0},0,{x,0}}. + {Ref22,_} = Term, + + Ref22. + id(I) -> I. diff --git a/lib/compiler/test/misc_SUITE.erl b/lib/compiler/test/misc_SUITE.erl index 2a6303ece8..e999c8ffae 100644 --- a/lib/compiler/test/misc_SUITE.erl +++ b/lib/compiler/test/misc_SUITE.erl @@ -223,10 +223,6 @@ silly_coverage(Config) when is_list(Config) -> {label,2}|non_proper_list]}],99}, expect_error(fun() -> beam_block:module(BlockInput, []) end), - %% beam_bs - BsInput = BlockInput, - expect_error(fun() -> beam_bs:module(BsInput, []) end), - %% beam_except ExceptInput = {?MODULE,[{foo,0}],[], [{function,foo,0,2, diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk index 97179b7fc4..efedb414ad 100644 --- a/lib/compiler/vsn.mk +++ b/lib/compiler/vsn.mk @@ -1 +1 @@ -COMPILER_VSN = 7.3 +COMPILER_VSN = 7.3.1 diff --git a/lib/crypto/c_src/Makefile.in b/lib/crypto/c_src/Makefile.in index cd0e5442e9..508e1c40ee 100644 --- a/lib/crypto/c_src/Makefile.in +++ b/lib/crypto/c_src/Makefile.in @@ -71,7 +71,35 @@ PRIVDIR = ../priv OBJDIR = $(PRIVDIR)/obj/$(TARGET) LIBDIR = $(PRIVDIR)/lib/$(TARGET) -CRYPTO_OBJS = $(OBJDIR)/crypto$(TYPEMARKER).o +CRYPTO_OBJS = $(OBJDIR)/crypto$(TYPEMARKER).o \ + $(OBJDIR)/aead$(TYPEMARKER).o \ + $(OBJDIR)/aes$(TYPEMARKER).o \ + $(OBJDIR)/algorithms$(TYPEMARKER).o \ + $(OBJDIR)/atoms$(TYPEMARKER).o \ + $(OBJDIR)/block$(TYPEMARKER).o \ + $(OBJDIR)/bn$(TYPEMARKER).o \ + $(OBJDIR)/chacha20$(TYPEMARKER).o \ + $(OBJDIR)/cipher$(TYPEMARKER).o \ + $(OBJDIR)/cmac$(TYPEMARKER).o \ + $(OBJDIR)/dh$(TYPEMARKER).o \ + $(OBJDIR)/digest$(TYPEMARKER).o \ + $(OBJDIR)/dss$(TYPEMARKER).o \ + $(OBJDIR)/ec$(TYPEMARKER).o \ + $(OBJDIR)/ecdh$(TYPEMARKER).o \ + $(OBJDIR)/eddsa$(TYPEMARKER).o \ + $(OBJDIR)/engine$(TYPEMARKER).o \ + $(OBJDIR)/evp$(TYPEMARKER).o \ + $(OBJDIR)/fips$(TYPEMARKER).o \ + $(OBJDIR)/hash$(TYPEMARKER).o \ + $(OBJDIR)/hmac$(TYPEMARKER).o \ + $(OBJDIR)/info$(TYPEMARKER).o \ + $(OBJDIR)/math$(TYPEMARKER).o \ + $(OBJDIR)/pkey$(TYPEMARKER).o \ + $(OBJDIR)/poly1305$(TYPEMARKER).o \ + $(OBJDIR)/rand$(TYPEMARKER).o \ + $(OBJDIR)/rc4$(TYPEMARKER).o \ + $(OBJDIR)/rsa$(TYPEMARKER).o \ + $(OBJDIR)/srp$(TYPEMARKER).o CALLBACK_OBJS = $(OBJDIR)/crypto_callback$(TYPEMARKER).o NIF_MAKEFILE = $(PRIVDIR)/Makefile CRYPTO_STATIC_OBJS = $(OBJDIR)/crypto_static$(TYPEMARKER).o\ @@ -172,24 +200,21 @@ $(LIBDIR)/crypto_callback$(TYPEMARKER).dll: $(CALLBACK_OBJS) endif -clean: - rm -f $(LIBDIR)/crypto.@DED_EXT@ - rm -f $(LIBDIR)/crypto.debug.@DED_EXT@ - rm -f $(LIBDIR)/crypto.valgrind.@DED_EXT@ - rm -f $(LIBDIR)/crypto_callback.@DED_EXT@ - rm -f $(LIBDIR)/crypto_callback.debug.@DED_EXT@ - rm -f $(LIBDIR)/crypto_callback.valgrind.@DED_EXT@ - rm -f $(LIBDIR)/otp_test_engine.@DED_EXT@ - rm -f $(OBJDIR)/crypto.o - rm -f $(OBJDIR)/crypto_static.o - rm -f $(OBJDIR)/crypto.debug.o - rm -f $(OBJDIR)/crypto_static.debug.o - rm -f $(OBJDIR)/crypto.valgrind.o - rm -f $(OBJDIR)/crypto_static.valgrind.o - rm -f $(OBJDIR)/crypto_callback.o - rm -f $(OBJDIR)/crypto_callback.debug.o - rm -f $(OBJDIR)/crypto_callback.valgrind.o - rm -f $(OBJDIR)/otp_test_engine.o +CLEAN_OBJS_RAW = $(CRYPTO_OBJS) $(CALLBACK_OBJS) $(CRYPTO_STATIC_OBJS) $(TEST_ENGINE_OBJS) +CLEAN_OBJS_O = $(patsubst %.debug.o,%.o,$(CLEAN_OBJS_RAW:.valgrind.o=.o)) + +CLEAN_LIBS_RAW = $(NIF_LIB) $(CALLBACK_LIB) $(TEST_ENGINE_LIB) +CLEAN_LIBS_SO = $(patsubst %.debug.@DED_EXT@,%.@DED_EXT@,$(CLEAN_LIBS_RAW:.valgrind.@DED_EXT@=.@DED_EXT@)) + +clean_dynamic_libs: + rm -f $(CLEAN_LIBS_SO) + rm -f $(foreach T,.valgrind.@DED_EXT@ .debug.@DED_EXT@,$(CLEAN_LIBS_SO:.@DED_EXT@=$T)) + +clean_objs: + rm -f $(CLEAN_OBJS_O) + rm -f $(foreach T,.valgrind.o .debug.o,$(CLEAN_OBJS_O:.o=$T)) + +clean: clean_objs clean_dynamic_libs rm -f core *~ docs: diff --git a/lib/crypto/c_src/aead.c b/lib/crypto/c_src/aead.c new file mode 100644 index 0000000000..b7ed06e3bc --- /dev/null +++ b/lib/crypto/c_src/aead.c @@ -0,0 +1,233 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "aead.h" +#include "aes.h" + +ERL_NIF_TERM aead_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Type,Key,Iv,AAD,In) */ +#if defined(HAVE_AEAD) + EVP_CIPHER_CTX *ctx; + const EVP_CIPHER *cipher = NULL; + ErlNifBinary key, iv, aad, in; + unsigned int tag_len; + unsigned char *outp, *tagp; + ERL_NIF_TERM type, out, out_tag; + int len, ctx_ctrl_set_ivlen, ctx_ctrl_get_tag; + + type = argv[0]; + + if (!enif_is_atom(env, type) + || !enif_inspect_iolist_as_binary(env, argv[1], &key) + || !enif_inspect_binary(env, argv[2], &iv) + || !enif_inspect_iolist_as_binary(env, argv[3], &aad) + || !enif_inspect_iolist_as_binary(env, argv[4], &in) + || !enif_get_uint(env, argv[5], &tag_len)) { + return enif_make_badarg(env); + } + + /* Use cipher_type some day. Must check block_encrypt|decrypt first */ +#if defined(HAVE_GCM) + if (type == atom_aes_gcm) { + if ((iv.size > 0) + && (1 <= tag_len && tag_len <= 16)) { + ctx_ctrl_set_ivlen = EVP_CTRL_GCM_SET_IVLEN; + ctx_ctrl_get_tag = EVP_CTRL_GCM_GET_TAG; + if (key.size == 16) cipher = EVP_aes_128_gcm(); + else if (key.size == 24) cipher = EVP_aes_192_gcm(); + else if (key.size == 32) cipher = EVP_aes_256_gcm(); + else enif_make_badarg(env); + } else + enif_make_badarg(env); + } else +#endif +#if defined(HAVE_CCM) + if (type == atom_aes_ccm) { + if ((7 <= iv.size && iv.size <= 13) + && (4 <= tag_len && tag_len <= 16) + && ((tag_len & 1) == 0) + ) { + ctx_ctrl_set_ivlen = EVP_CTRL_CCM_SET_IVLEN; + ctx_ctrl_get_tag = EVP_CTRL_CCM_GET_TAG; + if (key.size == 16) cipher = EVP_aes_128_ccm(); + else if (key.size == 24) cipher = EVP_aes_192_ccm(); + else if (key.size == 32) cipher = EVP_aes_256_ccm(); + else enif_make_badarg(env); + } else + enif_make_badarg(env); + } else +#endif +#if defined(HAVE_CHACHA20_POLY1305) + if (type == atom_chacha20_poly1305) { + if ((key.size == 32) + && (1 <= iv.size && iv.size <= 16) + && (tag_len == 16) + ) { + ctx_ctrl_set_ivlen = EVP_CTRL_AEAD_SET_IVLEN; + ctx_ctrl_get_tag = EVP_CTRL_AEAD_GET_TAG, + cipher = EVP_chacha20_poly1305(); + } else enif_make_badarg(env); + } else +#endif + return enif_raise_exception(env, atom_notsup); + + ctx = EVP_CIPHER_CTX_new(); + if (EVP_EncryptInit_ex(ctx, cipher, NULL, NULL, NULL) != 1) goto out_err; + if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_set_ivlen, iv.size, NULL) != 1) goto out_err; + +#if defined(HAVE_CCM) + if (type == atom_aes_ccm) { + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, tag_len, NULL) != 1) goto out_err; + if (EVP_EncryptInit_ex(ctx, NULL, NULL, key.data, iv.data) != 1) goto out_err; + if (EVP_EncryptUpdate(ctx, NULL, &len, NULL, in.size) != 1) goto out_err; + } else +#endif + if (EVP_EncryptInit_ex(ctx, NULL, NULL, key.data, iv.data) != 1) goto out_err; + + if (EVP_EncryptUpdate(ctx, NULL, &len, aad.data, aad.size) != 1) goto out_err; + + outp = enif_make_new_binary(env, in.size, &out); + + if (EVP_EncryptUpdate(ctx, outp, &len, in.data, in.size) != 1) goto out_err; + if (EVP_EncryptFinal_ex(ctx, outp/*+len*/, &len) != 1) goto out_err; + + tagp = enif_make_new_binary(env, tag_len, &out_tag); + + if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_get_tag, tag_len, tagp) != 1) goto out_err; + + EVP_CIPHER_CTX_free(ctx); + CONSUME_REDS(env, in); + return enif_make_tuple2(env, out, out_tag); + +out_err: + EVP_CIPHER_CTX_free(ctx); + return atom_error; + +#else + return enif_raise_exception(env, atom_notsup); +#endif +} + +ERL_NIF_TERM aead_decrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Type,Key,Iv,AAD,In,Tag) */ +#if defined(HAVE_AEAD) + EVP_CIPHER_CTX *ctx; + const EVP_CIPHER *cipher = NULL; + ErlNifBinary key, iv, aad, in, tag; + unsigned char *outp; + ERL_NIF_TERM type, out; + int len, ctx_ctrl_set_ivlen, ctx_ctrl_set_tag; + + type = argv[0]; +#if defined(HAVE_GCM_EVP_DECRYPT_BUG) + if (type == atom_aes_gcm) + return aes_gcm_decrypt_NO_EVP(env, argc, argv); +#endif + + if (!enif_is_atom(env, type) + || !enif_inspect_iolist_as_binary(env, argv[1], &key) + || !enif_inspect_binary(env, argv[2], &iv) + || !enif_inspect_iolist_as_binary(env, argv[3], &aad) + || !enif_inspect_iolist_as_binary(env, argv[4], &in) + || !enif_inspect_iolist_as_binary(env, argv[5], &tag)) { + return enif_make_badarg(env); + } + + /* Use cipher_type some day. Must check block_encrypt|decrypt first */ +#if defined(HAVE_GCM) + if (type == atom_aes_gcm) { + if (iv.size > 0) { + ctx_ctrl_set_ivlen = EVP_CTRL_GCM_SET_IVLEN; + ctx_ctrl_set_tag = EVP_CTRL_GCM_SET_TAG; + if (key.size == 16) cipher = EVP_aes_128_gcm(); + else if (key.size == 24) cipher = EVP_aes_192_gcm(); + else if (key.size == 32) cipher = EVP_aes_256_gcm(); + else enif_make_badarg(env); + } else + enif_make_badarg(env); + } else +#endif +#if defined(HAVE_CCM) + if (type == atom_aes_ccm) { + if (iv.size > 0) { + ctx_ctrl_set_ivlen = EVP_CTRL_CCM_SET_IVLEN; + if (key.size == 16) cipher = EVP_aes_128_ccm(); + else if (key.size == 24) cipher = EVP_aes_192_ccm(); + else if (key.size == 32) cipher = EVP_aes_256_ccm(); + else enif_make_badarg(env); + } else + enif_make_badarg(env); + } else +#endif +#if defined(HAVE_CHACHA20_POLY1305) + if (type == atom_chacha20_poly1305) { + if ((key.size == 32) + && (1 <= iv.size && iv.size <= 16) + && tag.size == 16 + ) { + ctx_ctrl_set_ivlen = EVP_CTRL_AEAD_SET_IVLEN; + ctx_ctrl_set_tag = EVP_CTRL_AEAD_SET_TAG; + cipher = EVP_chacha20_poly1305(); + } else enif_make_badarg(env); + } else +#endif + return enif_raise_exception(env, atom_notsup); + + outp = enif_make_new_binary(env, in.size, &out); + + ctx = EVP_CIPHER_CTX_new(); + if (EVP_DecryptInit_ex(ctx, cipher, NULL, NULL, NULL) != 1) goto out_err; + if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_set_ivlen, iv.size, NULL) != 1) goto out_err; + +#if defined(HAVE_CCM) + if (type == atom_aes_ccm) { + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, tag.size, tag.data) != 1) goto out_err; + } +#endif + + if (EVP_DecryptInit_ex(ctx, NULL, NULL, key.data, iv.data) != 1) goto out_err; + +#if defined(HAVE_CCM) + if (type == atom_aes_ccm) { + if (1 != EVP_DecryptUpdate(ctx, NULL, &len, NULL, in.size)) goto out_err; + } +#endif + + if (EVP_DecryptUpdate(ctx, NULL, &len, aad.data, aad.size) != 1) goto out_err; + if (EVP_DecryptUpdate(ctx, outp, &len, in.data, in.size) != 1) goto out_err; + +#if defined(HAVE_GCM) || defined(HAVE_CHACHA20_POLY1305) + if (type == atom_aes_gcm) { + if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_set_tag, tag.size, tag.data) != 1) goto out_err; + if (EVP_DecryptFinal_ex(ctx, outp+len, &len) != 1) goto out_err; + } +#endif + EVP_CIPHER_CTX_free(ctx); + + CONSUME_REDS(env, in); + return out; + +out_err: + EVP_CIPHER_CTX_free(ctx); + return atom_error; +#else + return enif_raise_exception(env, atom_notsup); +#endif +} diff --git a/lib/crypto/c_src/aead.h b/lib/crypto/c_src/aead.h new file mode 100644 index 0000000000..54c0711535 --- /dev/null +++ b/lib/crypto/c_src/aead.h @@ -0,0 +1,29 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_AEAD_H__ +#define E_AEAD_H__ 1 + +#include "common.h" + +ERL_NIF_TERM aead_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM aead_decrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_AEAD_H__ */ diff --git a/lib/crypto/c_src/aes.c b/lib/crypto/c_src/aes.c new file mode 100644 index 0000000000..36cd02933f --- /dev/null +++ b/lib/crypto/c_src/aes.c @@ -0,0 +1,282 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "aes.h" +#include "cipher.h" + +ERL_NIF_TERM aes_cfb_8_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, IVec, Data, IsEncrypt) */ + ErlNifBinary key, ivec, text; + AES_KEY aes_key; + unsigned char ivec_clone[16]; /* writable copy */ + int new_ivlen = 0; + ERL_NIF_TERM ret; + + CHECK_NO_FIPS_MODE(); + + if (!enif_inspect_iolist_as_binary(env, argv[0], &key) + || !(key.size == 16 || key.size == 24 || key.size == 32) + || !enif_inspect_binary(env, argv[1], &ivec) || ivec.size != 16 + || !enif_inspect_iolist_as_binary(env, argv[2], &text)) { + return enif_make_badarg(env); + } + + memcpy(ivec_clone, ivec.data, 16); + AES_set_encrypt_key(key.data, key.size * 8, &aes_key); + AES_cfb8_encrypt((unsigned char *) text.data, + enif_make_new_binary(env, text.size, &ret), + text.size, &aes_key, ivec_clone, &new_ivlen, + (argv[3] == atom_true)); + CONSUME_REDS(env,text); + return ret; +} + +ERL_NIF_TERM aes_cfb_128_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, IVec, Data, IsEncrypt) */ + ErlNifBinary key, ivec, text; + AES_KEY aes_key; + unsigned char ivec_clone[16]; /* writable copy */ + int new_ivlen = 0; + ERL_NIF_TERM ret; + + if (!enif_inspect_iolist_as_binary(env, argv[0], &key) + || !(key.size == 16 || key.size == 24 || key.size == 32) + || !enif_inspect_binary(env, argv[1], &ivec) || ivec.size != 16 + || !enif_inspect_iolist_as_binary(env, argv[2], &text)) { + return enif_make_badarg(env); + } + + memcpy(ivec_clone, ivec.data, 16); + AES_set_encrypt_key(key.data, key.size * 8, &aes_key); + AES_cfb128_encrypt((unsigned char *) text.data, + enif_make_new_binary(env, text.size, &ret), + text.size, &aes_key, ivec_clone, &new_ivlen, + (argv[3] == atom_true)); + CONSUME_REDS(env,text); + return ret; +} + +ERL_NIF_TERM aes_ige_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, IVec, Data, IsEncrypt) */ +#ifdef HAVE_AES_IGE + ErlNifBinary key_bin, ivec_bin, data_bin; + AES_KEY aes_key; + unsigned char ivec[32]; + int i; + unsigned char* ret_ptr; + ERL_NIF_TERM ret; + + CHECK_NO_FIPS_MODE(); + + if (!enif_inspect_iolist_as_binary(env, argv[0], &key_bin) + || (key_bin.size != 16 && key_bin.size != 32) + || !enif_inspect_binary(env, argv[1], &ivec_bin) + || ivec_bin.size != 32 + || !enif_inspect_iolist_as_binary(env, argv[2], &data_bin) + || data_bin.size % 16 != 0) { + + return enif_make_badarg(env); + } + + if (argv[3] == atom_true) { + i = AES_ENCRYPT; + AES_set_encrypt_key(key_bin.data, key_bin.size*8, &aes_key); + } + else { + i = AES_DECRYPT; + AES_set_decrypt_key(key_bin.data, key_bin.size*8, &aes_key); + } + + ret_ptr = enif_make_new_binary(env, data_bin.size, &ret); + memcpy(ivec, ivec_bin.data, 32); /* writable copy */ + AES_ige_encrypt(data_bin.data, ret_ptr, data_bin.size, &aes_key, ivec, i); + CONSUME_REDS(env,data_bin); + return ret; +#else + return atom_notsup; +#endif +} + + +/* Initializes state for ctr streaming (de)encryption +*/ +#ifdef HAVE_EVP_AES_CTR +ERL_NIF_TERM aes_ctr_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, IVec) */ + ErlNifBinary key_bin, ivec_bin; + struct evp_cipher_ctx *ctx; + const EVP_CIPHER *cipher; + ERL_NIF_TERM ret; + + if (!enif_inspect_iolist_as_binary(env, argv[0], &key_bin) + || !enif_inspect_binary(env, argv[1], &ivec_bin) + || ivec_bin.size != 16) { + return enif_make_badarg(env); + } + + switch (key_bin.size) + { + case 16: cipher = EVP_aes_128_ctr(); break; + case 24: cipher = EVP_aes_192_ctr(); break; + case 32: cipher = EVP_aes_256_ctr(); break; + default: return enif_make_badarg(env); + } + + ctx = enif_alloc_resource(evp_cipher_ctx_rtype, sizeof(struct evp_cipher_ctx)); + ctx->ctx = EVP_CIPHER_CTX_new(); + EVP_CipherInit_ex(ctx->ctx, cipher, NULL, + key_bin.data, ivec_bin.data, 1); + EVP_CIPHER_CTX_set_padding(ctx->ctx, 0); + ret = enif_make_resource(env, ctx); + enif_release_resource(ctx); + return ret; +} + +ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context, Data) */ + struct evp_cipher_ctx *ctx, *new_ctx; + ErlNifBinary data_bin; + ERL_NIF_TERM ret, cipher_term; + unsigned char *out; + int outl = 0; + + if (!enif_get_resource(env, argv[0], evp_cipher_ctx_rtype, (void**)&ctx) + || !enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) { + return enif_make_badarg(env); + } + new_ctx = enif_alloc_resource(evp_cipher_ctx_rtype, sizeof(struct evp_cipher_ctx)); + new_ctx->ctx = EVP_CIPHER_CTX_new(); + EVP_CIPHER_CTX_copy(new_ctx->ctx, ctx->ctx); + out = enif_make_new_binary(env, data_bin.size, &cipher_term); + EVP_CipherUpdate(new_ctx->ctx, out, &outl, data_bin.data, data_bin.size); + ASSERT(outl == data_bin.size); + + ret = enif_make_tuple2(env, enif_make_resource(env, new_ctx), cipher_term); + enif_release_resource(new_ctx); + CONSUME_REDS(env,data_bin); + return ret; +} + +#else /* if not HAVE_EVP_AES_CTR */ + +ERL_NIF_TERM aes_ctr_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, IVec) */ + ErlNifBinary key_bin, ivec_bin; + ERL_NIF_TERM ecount_bin; + + if (!enif_inspect_iolist_as_binary(env, argv[0], &key_bin) + || !enif_inspect_binary(env, argv[1], &ivec_bin) + || !(key_bin.size == 16 || key_bin.size == 24 || key_bin.size ==32) + || ivec_bin.size != 16) { + return enif_make_badarg(env); + } + + memset(enif_make_new_binary(env, AES_BLOCK_SIZE, &ecount_bin), + 0, AES_BLOCK_SIZE); + return enif_make_tuple4(env, argv[0], argv[1], ecount_bin, enif_make_int(env, 0)); +} + +ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* ({Key, IVec, ECount, Num}, Data) */ + ErlNifBinary key_bin, ivec_bin, text_bin, ecount_bin; + AES_KEY aes_key; + unsigned int num; + ERL_NIF_TERM ret, num2_term, cipher_term, ivec2_term, ecount2_term, new_state_term; + int state_arity; + const ERL_NIF_TERM *state_term; + unsigned char * ivec2_buf; + unsigned char * ecount2_buf; + + if (!enif_get_tuple(env, argv[0], &state_arity, &state_term) + || state_arity != 4 + || !enif_inspect_iolist_as_binary(env, state_term[0], &key_bin) + || AES_set_encrypt_key(key_bin.data, key_bin.size*8, &aes_key) != 0 + || !enif_inspect_binary(env, state_term[1], &ivec_bin) || ivec_bin.size != 16 + || !enif_inspect_binary(env, state_term[2], &ecount_bin) || ecount_bin.size != AES_BLOCK_SIZE + || !enif_get_uint(env, state_term[3], &num) + || !enif_inspect_iolist_as_binary(env, argv[1], &text_bin)) { + return enif_make_badarg(env); + } + + ivec2_buf = enif_make_new_binary(env, ivec_bin.size, &ivec2_term); + ecount2_buf = enif_make_new_binary(env, ecount_bin.size, &ecount2_term); + + memcpy(ivec2_buf, ivec_bin.data, 16); + memcpy(ecount2_buf, ecount_bin.data, ecount_bin.size); + + AES_ctr128_encrypt((unsigned char *) text_bin.data, + enif_make_new_binary(env, text_bin.size, &cipher_term), + text_bin.size, &aes_key, ivec2_buf, ecount2_buf, &num); + + num2_term = enif_make_uint(env, num); + new_state_term = enif_make_tuple4(env, state_term[0], ivec2_term, ecount2_term, num2_term); + ret = enif_make_tuple2(env, new_state_term, cipher_term); + CONSUME_REDS(env,text_bin); + return ret; +} +#endif /* !HAVE_EVP_AES_CTR */ + +#ifdef HAVE_GCM_EVP_DECRYPT_BUG +ERL_NIF_TERM aes_gcm_decrypt_NO_EVP(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Type,Key,Iv,AAD,In,Tag) */ + GCM128_CONTEXT *ctx; + ErlNifBinary key, iv, aad, in, tag; + AES_KEY aes_key; + unsigned char *outp; + ERL_NIF_TERM out; + + if (!enif_inspect_iolist_as_binary(env, argv[1], &key) + || AES_set_encrypt_key(key.data, key.size*8, &aes_key) != 0 + || !enif_inspect_binary(env, argv[2], &iv) || iv.size == 0 + || !enif_inspect_iolist_as_binary(env, argv[3], &aad) + || !enif_inspect_iolist_as_binary(env, argv[4], &in) + || !enif_inspect_iolist_as_binary(env, argv[5], &tag)) { + return enif_make_badarg(env); + } + + if (!(ctx = CRYPTO_gcm128_new(&aes_key, (block128_f)AES_encrypt))) + return atom_error; + + CRYPTO_gcm128_setiv(ctx, iv.data, iv.size); + + if (CRYPTO_gcm128_aad(ctx, aad.data, aad.size)) + goto out_err; + + outp = enif_make_new_binary(env, in.size, &out); + + /* decrypt */ + if (CRYPTO_gcm128_decrypt(ctx, in.data, outp, in.size)) + goto out_err; + + /* calculate and check the tag */ + if (CRYPTO_gcm128_finish(ctx, tag.data, tag.size)) + goto out_err; + + CRYPTO_gcm128_release(ctx); + CONSUME_REDS(env, in); + + return out; + +out_err: + CRYPTO_gcm128_release(ctx); + return atom_error; +} +#endif /* HAVE_GCM_EVP_DECRYPT_BUG */ + diff --git a/lib/crypto/c_src/aes.h b/lib/crypto/c_src/aes.h new file mode 100644 index 0000000000..09c984f84a --- /dev/null +++ b/lib/crypto/c_src/aes.h @@ -0,0 +1,36 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_AES_H__ +#define E_AES_H__ 1 + +#include "common.h" + +ERL_NIF_TERM aes_cfb_8_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM aes_cfb_128_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM aes_ige_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM aes_ctr_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#ifdef HAVE_GCM_EVP_DECRYPT_BUG +ERL_NIF_TERM aes_gcm_decrypt_NO_EVP(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +#endif + +#endif /* E_AES_H__ */ diff --git a/lib/crypto/c_src/algorithms.c b/lib/crypto/c_src/algorithms.c new file mode 100644 index 0000000000..a6e61cc9b2 --- /dev/null +++ b/lib/crypto/c_src/algorithms.c @@ -0,0 +1,320 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "algorithms.h" + +static int algo_hash_cnt, algo_hash_fips_cnt; +static ERL_NIF_TERM algo_hash[12]; /* increase when extending the list */ +static int algo_pubkey_cnt, algo_pubkey_fips_cnt; +static ERL_NIF_TERM algo_pubkey[12]; /* increase when extending the list */ +static int algo_cipher_cnt, algo_cipher_fips_cnt; +static ERL_NIF_TERM algo_cipher[25]; /* increase when extending the list */ +static int algo_mac_cnt, algo_mac_fips_cnt; +static ERL_NIF_TERM algo_mac[3]; /* increase when extending the list */ +static int algo_curve_cnt, algo_curve_fips_cnt; +static ERL_NIF_TERM algo_curve[89]; /* increase when extending the list */ +static int algo_rsa_opts_cnt, algo_rsa_opts_fips_cnt; +static ERL_NIF_TERM algo_rsa_opts[11]; /* increase when extending the list */ + +void init_algorithms_types(ErlNifEnv* env) +{ + // Validated algorithms first + algo_hash_cnt = 0; + algo_hash[algo_hash_cnt++] = atom_sha; +#ifdef HAVE_SHA224 + algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha224"); +#endif +#ifdef HAVE_SHA256 + algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha256"); +#endif +#ifdef HAVE_SHA384 + algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha384"); +#endif +#ifdef HAVE_SHA512 + algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha512"); +#endif +#ifdef HAVE_SHA3_224 + algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha3_224"); +#endif +#ifdef HAVE_SHA3_256 + algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha3_256"); +#endif +#ifdef HAVE_SHA3_384 + algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha3_384"); +#endif +#ifdef HAVE_SHA3_512 + algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha3_512"); +#endif + // Non-validated algorithms follow + algo_hash_fips_cnt = algo_hash_cnt; + algo_hash[algo_hash_cnt++] = enif_make_atom(env, "md4"); + algo_hash[algo_hash_cnt++] = enif_make_atom(env, "md5"); + algo_hash[algo_hash_cnt++] = enif_make_atom(env, "ripemd160"); + + algo_pubkey_cnt = 0; + algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "rsa"); + algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "dss"); + algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "dh"); +#if defined(HAVE_EC) +#if !defined(OPENSSL_NO_EC2M) + algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "ec_gf2m"); +#endif + algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "ecdsa"); + algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "ecdh"); +#endif + // Non-validated algorithms follow + algo_pubkey_fips_cnt = algo_pubkey_cnt; + // Don't know if Edward curves are fips validated +#if defined(HAVE_EDDSA) + algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "eddsa"); +#endif + algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "srp"); + + // Validated algorithms first + algo_cipher_cnt = 0; +#ifndef OPENSSL_NO_DES + algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "des3_cbc"); + algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "des_ede3"); +#ifdef HAVE_DES_ede3_cfb_encrypt + algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "des3_cbf"); + algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "des3_cfb"); +#endif +#endif + algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cbc"); + algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cbc128"); + algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cfb8"); + algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cfb128"); + algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cbc256"); + algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_ctr"); + algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_ecb"); +#if defined(HAVE_GCM) + algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"aes_gcm"); +#endif +#if defined(HAVE_CCM) + algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"aes_ccm"); +#endif + // Non-validated algorithms follow + algo_cipher_fips_cnt = algo_cipher_cnt; +#ifdef HAVE_AES_IGE + algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"aes_ige256"); +#endif +#ifndef OPENSSL_NO_DES + algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"des_cbc"); + algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"des_cfb"); + algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"des_ecb"); +#endif + algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"blowfish_cbc"); + algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"blowfish_cfb64"); + algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"blowfish_ofb64"); + algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"blowfish_ecb"); +#ifndef OPENSSL_NO_RC2 + algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"rc2_cbc"); +#endif +#ifndef OPENSSL_NO_RC4 + algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"rc4"); +#endif +#if defined(HAVE_CHACHA20_POLY1305) + algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"chacha20_poly1305"); +#endif +#if defined(HAVE_CHACHA20) + algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"chacha20"); +#endif + + // Validated algorithms first + algo_mac_cnt = 0; + algo_mac[algo_mac_cnt++] = enif_make_atom(env,"hmac"); +#ifdef HAVE_CMAC + algo_mac[algo_mac_cnt++] = enif_make_atom(env,"cmac"); +#endif +#ifdef HAVE_POLY1305 + algo_mac[algo_mac_cnt++] = enif_make_atom(env,"poly1305"); +#endif + // Non-validated algorithms follow + algo_mac_fips_cnt = algo_mac_cnt; + + // Validated algorithms first + algo_curve_cnt = 0; +#if defined(HAVE_EC) + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp160k1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp160r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp160r2"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp192r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp192k1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp224k1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp224r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp256k1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp256r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp384r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp521r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"prime192v1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"prime192v2"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"prime192v3"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"prime239v1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"prime239v2"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"prime239v3"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"prime256v1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls7"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls9"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls12"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP160r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP160t1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP192r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP192t1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP224r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP224t1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP256r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP256t1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP320r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP320t1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP384r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP384t1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP512r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP512t1"); +#if !defined(OPENSSL_NO_EC2M) + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect163k1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect163r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect163r2"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect193r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect193r2"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect233k1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect233r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect239k1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect283k1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect283r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect409k1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect409r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect571k1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect571r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb163v1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb163v2"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb163v3"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb176v1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb191v1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb191v2"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb191v3"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb208w1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb239v1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb239v2"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb239v3"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb272w1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb304w1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb359v1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb368w1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb431r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls3"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls5"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls10"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls11"); +#endif +#endif + // Non-validated algorithms follow + algo_curve_fips_cnt = algo_curve_cnt; +#if defined(HAVE_EC) + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp112r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp112r2"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp128r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp128r2"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls6"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls8"); +#if !defined(OPENSSL_NO_EC2M) + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect113r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect113r2"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect131r1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect131r2"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls1"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls4"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"ipsec3"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"ipsec4"); +#endif +#endif + //-- +#ifdef HAVE_EDDSA + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"ed25519"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"ed448"); +#endif +#ifdef HAVE_ED_CURVE_DH + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"x25519"); + algo_curve[algo_curve_cnt++] = enif_make_atom(env,"x448"); +#endif + + // Validated algorithms first + algo_rsa_opts_cnt = 0; +#ifdef HAS_EVP_PKEY_CTX +# ifdef HAVE_RSA_PKCS1_PSS_PADDING + algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_pkcs1_pss_padding"); + algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_pss_saltlen"); +# endif +# ifdef HAVE_RSA_MGF1_MD + algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_mgf1_md"); +# endif +# ifdef HAVE_RSA_OAEP_PADDING + algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_pkcs1_oaep_padding"); +# endif +# ifdef HAVE_RSA_OAEP_MD + algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_oaep_label"); + algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_oaep_md"); +# endif + algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"signature_md"); +#endif + algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_pkcs1_padding"); + algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_x931_padding"); +#ifdef HAVE_RSA_SSLV23_PADDING + algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_sslv23_padding"); +#endif + algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_no_padding"); + algo_rsa_opts_fips_cnt = algo_rsa_opts_cnt; + + + // Check that the max number of algos is updated + ASSERT(algo_hash_cnt <= sizeof(algo_hash)/sizeof(ERL_NIF_TERM)); + ASSERT(algo_pubkey_cnt <= sizeof(algo_pubkey)/sizeof(ERL_NIF_TERM)); + ASSERT(algo_cipher_cnt <= sizeof(algo_cipher)/sizeof(ERL_NIF_TERM)); + ASSERT(algo_mac_cnt <= sizeof(algo_mac)/sizeof(ERL_NIF_TERM)); + ASSERT(algo_curve_cnt <= sizeof(algo_curve)/sizeof(ERL_NIF_TERM)); + ASSERT(algo_rsa_opts_cnt <= sizeof(algo_rsa_opts)/sizeof(ERL_NIF_TERM)); +} + +ERL_NIF_TERM algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ +#ifdef FIPS_SUPPORT + int fips_mode = FIPS_mode(); + int hash_cnt = fips_mode ? algo_hash_fips_cnt : algo_hash_cnt; + int pubkey_cnt = fips_mode ? algo_pubkey_fips_cnt : algo_pubkey_cnt; + int cipher_cnt = fips_mode ? algo_cipher_fips_cnt : algo_cipher_cnt; + int mac_cnt = fips_mode ? algo_mac_fips_cnt : algo_mac_cnt; + int curve_cnt = fips_mode ? algo_curve_fips_cnt : algo_curve_cnt; + int rsa_opts_cnt = fips_mode ? algo_rsa_opts_fips_cnt : algo_rsa_opts_cnt; +#else + int hash_cnt = algo_hash_cnt; + int pubkey_cnt = algo_pubkey_cnt; + int cipher_cnt = algo_cipher_cnt; + int mac_cnt = algo_mac_cnt; + int curve_cnt = algo_curve_cnt; + int rsa_opts_cnt = algo_rsa_opts_cnt; +#endif + return enif_make_tuple6(env, + enif_make_list_from_array(env, algo_hash, hash_cnt), + enif_make_list_from_array(env, algo_pubkey, pubkey_cnt), + enif_make_list_from_array(env, algo_cipher, cipher_cnt), + enif_make_list_from_array(env, algo_mac, mac_cnt), + enif_make_list_from_array(env, algo_curve, curve_cnt), + enif_make_list_from_array(env, algo_rsa_opts, rsa_opts_cnt) + ); +} diff --git a/lib/crypto/c_src/algorithms.h b/lib/crypto/c_src/algorithms.h new file mode 100644 index 0000000000..068fb661ec --- /dev/null +++ b/lib/crypto/c_src/algorithms.h @@ -0,0 +1,30 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_ALGORITHMS_H__ +#define E_ALGORITHMS_H__ 1 + +#include "common.h" + +void init_algorithms_types(ErlNifEnv* env); + +ERL_NIF_TERM algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_ALGORITHMS_H__ */ diff --git a/lib/crypto/c_src/atoms.c b/lib/crypto/c_src/atoms.c new file mode 100644 index 0000000000..3a028b9a67 --- /dev/null +++ b/lib/crypto/c_src/atoms.c @@ -0,0 +1,271 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "atoms.h" + +ERL_NIF_TERM atom_true; +ERL_NIF_TERM atom_false; +ERL_NIF_TERM atom_sha; +ERL_NIF_TERM atom_error; +ERL_NIF_TERM atom_rsa_pkcs1_padding; +ERL_NIF_TERM atom_rsa_pkcs1_oaep_padding; +ERL_NIF_TERM atom_rsa_no_padding; +ERL_NIF_TERM atom_signature_md; +ERL_NIF_TERM atom_undefined; + +ERL_NIF_TERM atom_ok; +ERL_NIF_TERM atom_not_prime; +ERL_NIF_TERM atom_not_strong_prime; +ERL_NIF_TERM atom_unable_to_check_generator; +ERL_NIF_TERM atom_not_suitable_generator; +ERL_NIF_TERM atom_check_failed; +ERL_NIF_TERM atom_unknown; +ERL_NIF_TERM atom_none; +ERL_NIF_TERM atom_notsup; +ERL_NIF_TERM atom_digest; +#ifdef FIPS_SUPPORT +ERL_NIF_TERM atom_enabled; +ERL_NIF_TERM atom_not_enabled; +#else +ERL_NIF_TERM atom_not_supported; +#endif + +#if defined(HAVE_EC) +ERL_NIF_TERM atom_ec; +ERL_NIF_TERM atom_prime_field; +ERL_NIF_TERM atom_characteristic_two_field; +ERL_NIF_TERM atom_tpbasis; +ERL_NIF_TERM atom_ppbasis; +ERL_NIF_TERM atom_onbasis; +#endif + +ERL_NIF_TERM atom_aes_cfb8; +ERL_NIF_TERM atom_aes_cfb128; +#ifdef HAVE_GCM +ERL_NIF_TERM atom_aes_gcm; +#endif +#ifdef HAVE_CCM +ERL_NIF_TERM atom_aes_ccm; +#endif +#ifdef HAVE_CHACHA20_POLY1305 +ERL_NIF_TERM atom_chacha20_poly1305; +#endif +#ifdef HAVE_ECB_IVEC_BUG +ERL_NIF_TERM atom_aes_ecb; +ERL_NIF_TERM atom_des_ecb; +ERL_NIF_TERM atom_blowfish_ecb; +#endif + +ERL_NIF_TERM atom_rsa; +ERL_NIF_TERM atom_dss; +ERL_NIF_TERM atom_ecdsa; + +#ifdef HAVE_ED_CURVE_DH +ERL_NIF_TERM atom_x25519; +ERL_NIF_TERM atom_x448; +#endif + +ERL_NIF_TERM atom_eddsa; +#ifdef HAVE_EDDSA +ERL_NIF_TERM atom_ed25519; +ERL_NIF_TERM atom_ed448; +#endif + +ERL_NIF_TERM atom_rsa_mgf1_md; +ERL_NIF_TERM atom_rsa_oaep_label; +ERL_NIF_TERM atom_rsa_oaep_md; +ERL_NIF_TERM atom_rsa_pad; /* backwards compatibility */ +ERL_NIF_TERM atom_rsa_padding; +ERL_NIF_TERM atom_rsa_pkcs1_pss_padding; +#ifdef HAVE_RSA_SSLV23_PADDING +ERL_NIF_TERM atom_rsa_sslv23_padding; +#endif +ERL_NIF_TERM atom_rsa_x931_padding; +ERL_NIF_TERM atom_rsa_pss_saltlen; +ERL_NIF_TERM atom_sha224; +ERL_NIF_TERM atom_sha256; +ERL_NIF_TERM atom_sha384; +ERL_NIF_TERM atom_sha512; +ERL_NIF_TERM atom_sha3_224; +ERL_NIF_TERM atom_sha3_256; +ERL_NIF_TERM atom_sha3_384; +ERL_NIF_TERM atom_sha3_512; +ERL_NIF_TERM atom_md5; +ERL_NIF_TERM atom_ripemd160; + +#ifdef HAS_ENGINE_SUPPORT +ERL_NIF_TERM atom_bad_engine_method; +ERL_NIF_TERM atom_bad_engine_id; +ERL_NIF_TERM atom_ctrl_cmd_failed; +ERL_NIF_TERM atom_engine_init_failed; +ERL_NIF_TERM atom_register_engine_failed; +ERL_NIF_TERM atom_add_engine_failed; +ERL_NIF_TERM atom_remove_engine_failed; +ERL_NIF_TERM atom_engine_method_not_supported; + +ERL_NIF_TERM atom_engine_method_rsa; +ERL_NIF_TERM atom_engine_method_dsa; +ERL_NIF_TERM atom_engine_method_dh; +ERL_NIF_TERM atom_engine_method_rand; +ERL_NIF_TERM atom_engine_method_ecdh; +ERL_NIF_TERM atom_engine_method_ecdsa; +ERL_NIF_TERM atom_engine_method_ciphers; +ERL_NIF_TERM atom_engine_method_digests; +ERL_NIF_TERM atom_engine_method_store; +ERL_NIF_TERM atom_engine_method_pkey_meths; +ERL_NIF_TERM atom_engine_method_pkey_asn1_meths; +ERL_NIF_TERM atom_engine_method_ec; + +ERL_NIF_TERM atom_engine; +ERL_NIF_TERM atom_key_id; +ERL_NIF_TERM atom_password; +#endif + +int init_atoms(ErlNifEnv *env, const ERL_NIF_TERM fips_mode, const ERL_NIF_TERM load_info) { + atom_true = enif_make_atom(env,"true"); + atom_false = enif_make_atom(env,"false"); + /* Enter FIPS mode */ + if (fips_mode == atom_true) { +#ifdef FIPS_SUPPORT + if (!FIPS_mode_set(1)) { +#else + { +#endif + PRINTF_ERR0("CRYPTO: Could not setup FIPS mode"); + return 0; + } + } else if (fips_mode != atom_false) { + PRINTF_ERR1("CRYPTO: Invalid load_info '%T'", load_info); + return 0; + } + + atom_sha = enif_make_atom(env,"sha"); + atom_error = enif_make_atom(env,"error"); + atom_rsa_pkcs1_padding = enif_make_atom(env,"rsa_pkcs1_padding"); + atom_rsa_pkcs1_oaep_padding = enif_make_atom(env,"rsa_pkcs1_oaep_padding"); + atom_rsa_no_padding = enif_make_atom(env,"rsa_no_padding"); + atom_signature_md = enif_make_atom(env,"signature_md"); + atom_undefined = enif_make_atom(env,"undefined"); + atom_ok = enif_make_atom(env,"ok"); + atom_not_prime = enif_make_atom(env,"not_prime"); + atom_not_strong_prime = enif_make_atom(env,"not_strong_prime"); + atom_unable_to_check_generator = enif_make_atom(env,"unable_to_check_generator"); + atom_not_suitable_generator = enif_make_atom(env,"not_suitable_generator"); + atom_check_failed = enif_make_atom(env,"check_failed"); + atom_unknown = enif_make_atom(env,"unknown"); + atom_none = enif_make_atom(env,"none"); + atom_notsup = enif_make_atom(env,"notsup"); + atom_digest = enif_make_atom(env,"digest"); + +#if defined(HAVE_EC) + atom_ec = enif_make_atom(env,"ec"); + atom_prime_field = enif_make_atom(env,"prime_field"); + atom_characteristic_two_field = enif_make_atom(env,"characteristic_two_field"); + atom_tpbasis = enif_make_atom(env,"tpbasis"); + atom_ppbasis = enif_make_atom(env,"ppbasis"); + atom_onbasis = enif_make_atom(env,"onbasis"); +#endif + + atom_aes_cfb8 = enif_make_atom(env, "aes_cfb8"); + atom_aes_cfb128 = enif_make_atom(env, "aes_cfb128"); +#ifdef HAVE_GCM + atom_aes_gcm = enif_make_atom(env, "aes_gcm"); +#endif +#ifdef HAVE_CCM + atom_aes_ccm = enif_make_atom(env, "aes_ccm"); +#endif +#ifdef HAVE_CHACHA20_POLY1305 + atom_chacha20_poly1305 = enif_make_atom(env,"chacha20_poly1305"); +#endif +#ifdef HAVE_ECB_IVEC_BUG + atom_aes_ecb = enif_make_atom(env, "aes_ecb"); + atom_des_ecb = enif_make_atom(env, "des_ecb"); + atom_blowfish_ecb = enif_make_atom(env, "blowfish_ecb"); +#endif + +#ifdef FIPS_SUPPORT + atom_enabled = enif_make_atom(env,"enabled"); + atom_not_enabled = enif_make_atom(env,"not_enabled"); +#else + atom_not_supported = enif_make_atom(env,"not_supported"); +#endif + atom_rsa = enif_make_atom(env,"rsa"); + atom_dss = enif_make_atom(env,"dss"); + atom_ecdsa = enif_make_atom(env,"ecdsa"); +#ifdef HAVE_ED_CURVE_DH + atom_x25519 = enif_make_atom(env,"x25519"); + atom_x448 = enif_make_atom(env,"x448"); +#endif + atom_eddsa = enif_make_atom(env,"eddsa"); +#ifdef HAVE_EDDSA + atom_ed25519 = enif_make_atom(env,"ed25519"); + atom_ed448 = enif_make_atom(env,"ed448"); +#endif + atom_rsa_mgf1_md = enif_make_atom(env,"rsa_mgf1_md"); + atom_rsa_oaep_label = enif_make_atom(env,"rsa_oaep_label"); + atom_rsa_oaep_md = enif_make_atom(env,"rsa_oaep_md"); + atom_rsa_pad = enif_make_atom(env,"rsa_pad"); /* backwards compatibility */ + atom_rsa_padding = enif_make_atom(env,"rsa_padding"); + atom_rsa_pkcs1_pss_padding = enif_make_atom(env,"rsa_pkcs1_pss_padding"); +#ifdef HAVE_RSA_SSLV23_PADDING + atom_rsa_sslv23_padding = enif_make_atom(env,"rsa_sslv23_padding"); +#endif + atom_rsa_x931_padding = enif_make_atom(env,"rsa_x931_padding"); + atom_rsa_pss_saltlen = enif_make_atom(env,"rsa_pss_saltlen"); + atom_sha224 = enif_make_atom(env,"sha224"); + atom_sha256 = enif_make_atom(env,"sha256"); + atom_sha384 = enif_make_atom(env,"sha384"); + atom_sha512 = enif_make_atom(env,"sha512"); + atom_sha3_224 = enif_make_atom(env,"sha3_224"); + atom_sha3_256 = enif_make_atom(env,"sha3_256"); + atom_sha3_384 = enif_make_atom(env,"sha3_384"); + atom_sha3_512 = enif_make_atom(env,"sha3_512"); + atom_md5 = enif_make_atom(env,"md5"); + atom_ripemd160 = enif_make_atom(env,"ripemd160"); + +#ifdef HAS_ENGINE_SUPPORT + atom_bad_engine_method = enif_make_atom(env,"bad_engine_method"); + atom_bad_engine_id = enif_make_atom(env,"bad_engine_id"); + atom_ctrl_cmd_failed = enif_make_atom(env,"ctrl_cmd_failed"); + atom_engine_init_failed = enif_make_atom(env,"engine_init_failed"); + atom_engine_method_not_supported = enif_make_atom(env,"engine_method_not_supported"); + atom_add_engine_failed = enif_make_atom(env,"add_engine_failed"); + atom_remove_engine_failed = enif_make_atom(env,"remove_engine_failed"); + + atom_engine_method_rsa = enif_make_atom(env,"engine_method_rsa"); + atom_engine_method_dsa = enif_make_atom(env,"engine_method_dsa"); + atom_engine_method_dh = enif_make_atom(env,"engine_method_dh"); + atom_engine_method_rand = enif_make_atom(env,"engine_method_rand"); + atom_engine_method_ecdh = enif_make_atom(env,"engine_method_ecdh"); + atom_engine_method_ecdsa = enif_make_atom(env,"engine_method_ecdsa"); + atom_engine_method_store = enif_make_atom(env,"engine_method_store"); + atom_engine_method_ciphers = enif_make_atom(env,"engine_method_ciphers"); + atom_engine_method_digests = enif_make_atom(env,"engine_method_digests"); + atom_engine_method_pkey_meths = enif_make_atom(env,"engine_method_pkey_meths"); + atom_engine_method_pkey_asn1_meths = enif_make_atom(env,"engine_method_pkey_asn1_meths"); + atom_engine_method_ec = enif_make_atom(env,"engine_method_ec"); + + atom_engine = enif_make_atom(env,"engine"); + atom_key_id = enif_make_atom(env,"key_id"); + atom_password = enif_make_atom(env,"password"); +#endif + + return 1; +} diff --git a/lib/crypto/c_src/atoms.h b/lib/crypto/c_src/atoms.h new file mode 100644 index 0000000000..9ddf0131ac --- /dev/null +++ b/lib/crypto/c_src/atoms.h @@ -0,0 +1,147 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_ATOMS_H__ +#define E_ATOMS_H__ 1 + +#include <erl_nif.h> +#include "openssl_config.h" + +extern ERL_NIF_TERM atom_true; +extern ERL_NIF_TERM atom_false; +extern ERL_NIF_TERM atom_sha; +extern ERL_NIF_TERM atom_error; +extern ERL_NIF_TERM atom_rsa_pkcs1_padding; +extern ERL_NIF_TERM atom_rsa_pkcs1_oaep_padding; +extern ERL_NIF_TERM atom_rsa_no_padding; +extern ERL_NIF_TERM atom_signature_md; +extern ERL_NIF_TERM atom_undefined; + +extern ERL_NIF_TERM atom_ok; +extern ERL_NIF_TERM atom_not_prime; +extern ERL_NIF_TERM atom_not_strong_prime; +extern ERL_NIF_TERM atom_unable_to_check_generator; +extern ERL_NIF_TERM atom_not_suitable_generator; +extern ERL_NIF_TERM atom_check_failed; +extern ERL_NIF_TERM atom_unknown; +extern ERL_NIF_TERM atom_none; +extern ERL_NIF_TERM atom_notsup; +extern ERL_NIF_TERM atom_digest; +#ifdef FIPS_SUPPORT +extern ERL_NIF_TERM atom_enabled; +extern ERL_NIF_TERM atom_not_enabled; +#else +extern ERL_NIF_TERM atom_not_supported; +#endif + +#if defined(HAVE_EC) +extern ERL_NIF_TERM atom_ec; +extern ERL_NIF_TERM atom_prime_field; +extern ERL_NIF_TERM atom_characteristic_two_field; +extern ERL_NIF_TERM atom_tpbasis; +extern ERL_NIF_TERM atom_ppbasis; +extern ERL_NIF_TERM atom_onbasis; +#endif + +extern ERL_NIF_TERM atom_aes_cfb8; +extern ERL_NIF_TERM atom_aes_cfb128; +#ifdef HAVE_GCM +extern ERL_NIF_TERM atom_aes_gcm; +#endif +#ifdef HAVE_CCM +extern ERL_NIF_TERM atom_aes_ccm; +#endif +#ifdef HAVE_CHACHA20_POLY1305 +extern ERL_NIF_TERM atom_chacha20_poly1305; +#endif +#ifdef HAVE_ECB_IVEC_BUG +extern ERL_NIF_TERM atom_aes_ecb; +extern ERL_NIF_TERM atom_des_ecb; +extern ERL_NIF_TERM atom_blowfish_ecb; +#endif + +extern ERL_NIF_TERM atom_rsa; +extern ERL_NIF_TERM atom_dss; +extern ERL_NIF_TERM atom_ecdsa; + +#ifdef HAVE_ED_CURVE_DH +extern ERL_NIF_TERM atom_x25519; +extern ERL_NIF_TERM atom_x448; +#endif + +extern ERL_NIF_TERM atom_eddsa; +#ifdef HAVE_EDDSA +extern ERL_NIF_TERM atom_ed25519; +extern ERL_NIF_TERM atom_ed448; +#endif + +extern ERL_NIF_TERM atom_rsa_mgf1_md; +extern ERL_NIF_TERM atom_rsa_oaep_label; +extern ERL_NIF_TERM atom_rsa_oaep_md; +extern ERL_NIF_TERM atom_rsa_pad; /* backwards compatibility */ +extern ERL_NIF_TERM atom_rsa_padding; +extern ERL_NIF_TERM atom_rsa_pkcs1_pss_padding; +#ifdef HAVE_RSA_SSLV23_PADDING +extern ERL_NIF_TERM atom_rsa_sslv23_padding; +#endif +extern ERL_NIF_TERM atom_rsa_x931_padding; +extern ERL_NIF_TERM atom_rsa_pss_saltlen; +extern ERL_NIF_TERM atom_sha224; +extern ERL_NIF_TERM atom_sha256; +extern ERL_NIF_TERM atom_sha384; +extern ERL_NIF_TERM atom_sha512; +extern ERL_NIF_TERM atom_sha3_224; +extern ERL_NIF_TERM atom_sha3_256; +extern ERL_NIF_TERM atom_sha3_384; +extern ERL_NIF_TERM atom_sha3_512; +extern ERL_NIF_TERM atom_md5; +extern ERL_NIF_TERM atom_ripemd160; + +#ifdef HAS_ENGINE_SUPPORT +extern ERL_NIF_TERM atom_bad_engine_method; +extern ERL_NIF_TERM atom_bad_engine_id; +extern ERL_NIF_TERM atom_ctrl_cmd_failed; +extern ERL_NIF_TERM atom_engine_init_failed; +extern ERL_NIF_TERM atom_register_engine_failed; +extern ERL_NIF_TERM atom_add_engine_failed; +extern ERL_NIF_TERM atom_remove_engine_failed; +extern ERL_NIF_TERM atom_engine_method_not_supported; + +extern ERL_NIF_TERM atom_engine_method_rsa; +extern ERL_NIF_TERM atom_engine_method_dsa; +extern ERL_NIF_TERM atom_engine_method_dh; +extern ERL_NIF_TERM atom_engine_method_rand; +extern ERL_NIF_TERM atom_engine_method_ecdh; +extern ERL_NIF_TERM atom_engine_method_ecdsa; +extern ERL_NIF_TERM atom_engine_method_ciphers; +extern ERL_NIF_TERM atom_engine_method_digests; +extern ERL_NIF_TERM atom_engine_method_store; +extern ERL_NIF_TERM atom_engine_method_pkey_meths; +extern ERL_NIF_TERM atom_engine_method_pkey_asn1_meths; +extern ERL_NIF_TERM atom_engine_method_ec; + +extern ERL_NIF_TERM atom_engine; +extern ERL_NIF_TERM atom_key_id; +extern ERL_NIF_TERM atom_password; +#endif + +int init_atoms(ErlNifEnv *env, const ERL_NIF_TERM fips_mode, const ERL_NIF_TERM load_info); + +#endif /* E_ATOMS_H__ */ diff --git a/lib/crypto/c_src/block.c b/lib/crypto/c_src/block.c new file mode 100644 index 0000000000..2ba3290e9f --- /dev/null +++ b/lib/crypto/c_src/block.c @@ -0,0 +1,105 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "block.h" +#include "aes.h" +#include "cipher.h" + +ERL_NIF_TERM block_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Type, Key, Ivec, Text, IsEncrypt) or (Type, Key, Text, IsEncrypt) */ + struct cipher_type_t *cipherp = NULL; + const EVP_CIPHER *cipher; + ErlNifBinary key, ivec, text; + EVP_CIPHER_CTX* ctx; + ERL_NIF_TERM ret; + unsigned char *out; + int ivec_size, out_size = 0; + + if (!enif_inspect_iolist_as_binary(env, argv[1], &key) + || !(cipherp = get_cipher_type(argv[0], key.size)) + || !enif_inspect_iolist_as_binary(env, argv[argc - 2], &text)) { + return enif_make_badarg(env); + } + cipher = cipherp->cipher.p; + if (!cipher) { + return enif_raise_exception(env, atom_notsup); + } + + if (argv[0] == atom_aes_cfb8 + && (key.size == 24 || key.size == 32)) { + /* Why do EVP_CIPHER_CTX_set_key_length() fail on these key sizes? + * Fall back on low level API + */ + return aes_cfb_8_crypt(env, argc-1, argv+1); + } + else if (argv[0] == atom_aes_cfb128 + && (key.size == 24 || key.size == 32)) { + /* Why do EVP_CIPHER_CTX_set_key_length() fail on these key sizes? + * Fall back on low level API + */ + return aes_cfb_128_crypt_nif(env, argc-1, argv+1); + } + + ivec_size = EVP_CIPHER_iv_length(cipher); + +#ifdef HAVE_ECB_IVEC_BUG + if (argv[0] == atom_aes_ecb || argv[0] == atom_blowfish_ecb || + argv[0] == atom_des_ecb) + ivec_size = 0; /* 0.9.8l returns faulty ivec_size */ +#endif + + if (text.size % EVP_CIPHER_block_size(cipher) != 0 || + (ivec_size == 0 ? argc != 4 + : (argc != 5 || + !enif_inspect_iolist_as_binary(env, argv[2], &ivec) || + ivec.size != ivec_size))) { + return enif_make_badarg(env); + } + + out = enif_make_new_binary(env, text.size, &ret); + + ctx = EVP_CIPHER_CTX_new(); + if (!EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, + (argv[argc - 1] == atom_true)) || + !EVP_CIPHER_CTX_set_key_length(ctx, key.size) || + !(EVP_CIPHER_type(cipher) != NID_rc2_cbc || + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_SET_RC2_KEY_BITS, key.size * 8, NULL)) || + !EVP_CipherInit_ex(ctx, NULL, NULL, + key.data, ivec_size ? ivec.data : NULL, -1) || + !EVP_CIPHER_CTX_set_padding(ctx, 0)) { + + EVP_CIPHER_CTX_free(ctx); + return enif_raise_exception(env, atom_notsup); + } + + if (text.size > 0 && /* OpenSSL 0.9.8h asserts text.size > 0 */ + (!EVP_CipherUpdate(ctx, out, &out_size, text.data, text.size) + || (ASSERT(out_size == text.size), 0) + || !EVP_CipherFinal_ex(ctx, out + out_size, &out_size))) { + + EVP_CIPHER_CTX_free(ctx); + return enif_raise_exception(env, atom_notsup); + } + ASSERT(out_size == 0); + EVP_CIPHER_CTX_free(ctx); + CONSUME_REDS(env, text); + + return ret; +} diff --git a/lib/crypto/c_src/block.h b/lib/crypto/c_src/block.h new file mode 100644 index 0000000000..cc5e78ce12 --- /dev/null +++ b/lib/crypto/c_src/block.h @@ -0,0 +1,28 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_BLOCK_H__ +#define E_BLOCK_H__ 1 + +#include "common.h" + +ERL_NIF_TERM block_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_BLOCK_H__ */ diff --git a/lib/crypto/c_src/bn.c b/lib/crypto/c_src/bn.c new file mode 100644 index 0000000000..b576c46e1e --- /dev/null +++ b/lib/crypto/c_src/bn.c @@ -0,0 +1,121 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "bn.h" + + +int get_bn_from_mpint(ErlNifEnv* env, ERL_NIF_TERM term, BIGNUM** bnp) +{ + ErlNifBinary bin; + int sz; + if (!enif_inspect_binary(env,term,&bin)) { + return 0; + } + ERL_VALGRIND_ASSERT_MEM_DEFINED(bin.data, bin.size); + sz = bin.size - 4; + if (sz < 0 || get_int32(bin.data) != sz) { + return 0; + } + *bnp = BN_bin2bn(bin.data+4, sz, NULL); + return 1; +} + +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; +} + +ERL_NIF_TERM bin_from_bn(ErlNifEnv* env, const BIGNUM *bn) +{ + int bn_len; + unsigned char *bin_ptr; + ERL_NIF_TERM term; + + /* Copy the bignum into an erlang binary. */ + bn_len = BN_num_bytes(bn); + bin_ptr = enif_make_new_binary(env, bn_len, &term); + BN_bn2bin(bn, bin_ptr); + + return term; +} + +ERL_NIF_TERM mod_exp_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Base,Exponent,Modulo,bin_hdr) */ + BIGNUM *bn_base=NULL, *bn_exponent=NULL, *bn_modulo=NULL, *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_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); + if (bn_modulo) BN_free(bn_modulo); + return enif_make_badarg(env); + } + bn_result = BN_new(); + bn_ctx = BN_CTX_new(); + BN_mod_exp(bn_result, bn_base, bn_exponent, bn_modulo, bn_ctx); + dlen = BN_num_bytes(bn_result); + 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); + BN_free(bn_exponent); + BN_free(bn_base); + return ret; +} + +#ifdef HAVE_EC +ERL_NIF_TERM bn2term(ErlNifEnv* env, const BIGNUM *bn) +{ + unsigned dlen; + unsigned char* ptr; + ERL_NIF_TERM ret; + + if (!bn) + return atom_undefined; + + dlen = BN_num_bytes(bn); + ptr = enif_make_new_binary(env, dlen, &ret); + BN_bn2bin(bn, ptr); + ERL_VALGRIND_MAKE_MEM_DEFINED(ptr, dlen); + return ret; +} +#endif diff --git a/lib/crypto/c_src/bn.h b/lib/crypto/c_src/bn.h new file mode 100644 index 0000000000..332b06e79d --- /dev/null +++ b/lib/crypto/c_src/bn.h @@ -0,0 +1,36 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_BN_H__ +#define E_BN_H__ 1 + +#include "common.h" + +ERL_NIF_TERM bin_from_bn(ErlNifEnv* env, const BIGNUM *bn); +ERL_NIF_TERM mod_exp_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#ifdef HAVE_EC +ERL_NIF_TERM bn2term(ErlNifEnv* env, const BIGNUM *bn); +#endif + +int get_bn_from_mpint(ErlNifEnv* env, ERL_NIF_TERM term, BIGNUM** bnp); +int get_bn_from_bin(ErlNifEnv* env, ERL_NIF_TERM term, BIGNUM** bnp); + +#endif /* E_BN_H__ */ diff --git a/lib/crypto/c_src/chacha20.c b/lib/crypto/c_src/chacha20.c new file mode 100644 index 0000000000..8b21a0c7af --- /dev/null +++ b/lib/crypto/c_src/chacha20.c @@ -0,0 +1,83 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "chacha20.h" +#include "cipher.h" + +ERL_NIF_TERM chacha20_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, IV) */ +#if defined(HAVE_CHACHA20) + ErlNifBinary key_bin, ivec_bin; + struct evp_cipher_ctx *ctx; + const EVP_CIPHER *cipher; + ERL_NIF_TERM ret; + + if (!enif_inspect_iolist_as_binary(env, argv[0], &key_bin) + || !enif_inspect_binary(env, argv[1], &ivec_bin) + || key_bin.size != 32 + || ivec_bin.size != 16) { + return enif_make_badarg(env); + } + + cipher = EVP_chacha20(); + + ctx = enif_alloc_resource(evp_cipher_ctx_rtype, sizeof(struct evp_cipher_ctx)); + ctx->ctx = EVP_CIPHER_CTX_new(); + + + EVP_CipherInit_ex(ctx->ctx, cipher, NULL, + key_bin.data, ivec_bin.data, 1); + EVP_CIPHER_CTX_set_padding(ctx->ctx, 0); + ret = enif_make_resource(env, ctx); + enif_release_resource(ctx); + return ret; +#else + return enif_raise_exception(env, atom_notsup); +#endif +}; + +ERL_NIF_TERM chacha20_stream_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (State, Data) */ +#if defined(HAVE_CHACHA20) + struct evp_cipher_ctx *ctx, *new_ctx; + ErlNifBinary data_bin; + ERL_NIF_TERM ret, cipher_term; + unsigned char *out; + int outl = 0; + + if (!enif_get_resource(env, argv[0], evp_cipher_ctx_rtype, (void**)&ctx) + || !enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) { + return enif_make_badarg(env); + } + new_ctx = enif_alloc_resource(evp_cipher_ctx_rtype, sizeof(struct evp_cipher_ctx)); + new_ctx->ctx = EVP_CIPHER_CTX_new(); + EVP_CIPHER_CTX_copy(new_ctx->ctx, ctx->ctx); + out = enif_make_new_binary(env, data_bin.size, &cipher_term); + EVP_CipherUpdate(new_ctx->ctx, out, &outl, data_bin.data, data_bin.size); + ASSERT(outl == data_bin.size); + + ret = enif_make_tuple2(env, enif_make_resource(env, new_ctx), cipher_term); + enif_release_resource(new_ctx); + CONSUME_REDS(env,data_bin); + return ret; +#else + return enif_raise_exception(env, atom_notsup); +#endif +}; diff --git a/lib/crypto/c_src/chacha20.h b/lib/crypto/c_src/chacha20.h new file mode 100644 index 0000000000..7e2ccae2bb --- /dev/null +++ b/lib/crypto/c_src/chacha20.h @@ -0,0 +1,29 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_CHACHA20_H__ +#define E_CHACHA20_H__ 1 + +#include "common.h" + +ERL_NIF_TERM chacha20_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM chacha20_stream_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_CHACHA20_H__ */ diff --git a/lib/crypto/c_src/cipher.c b/lib/crypto/c_src/cipher.c new file mode 100644 index 0000000000..6580cb183f --- /dev/null +++ b/lib/crypto/c_src/cipher.c @@ -0,0 +1,117 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "cipher.h" + +#ifdef OPENSSL_NO_DES +#define COND_NO_DES_PTR(Ptr) (NULL) +#else +#define COND_NO_DES_PTR(Ptr) (Ptr) +#endif + +static struct cipher_type_t cipher_types[] = +{ + {{"rc2_cbc"}, +#ifndef OPENSSL_NO_RC2 + {&EVP_rc2_cbc} +#else + {NULL} +#endif + }, + {{"des_cbc"}, {COND_NO_DES_PTR(&EVP_des_cbc)}}, + {{"des_cfb"}, {COND_NO_DES_PTR(&EVP_des_cfb8)}}, + {{"des_ecb"}, {COND_NO_DES_PTR(&EVP_des_ecb)}}, + {{"des_ede3_cbc"}, {COND_NO_DES_PTR(&EVP_des_ede3_cbc)}}, + {{"des_ede3_cbf"}, /* Misspelled, retained */ +#ifdef HAVE_DES_ede3_cfb_encrypt + {COND_NO_DES_PTR(&EVP_des_ede3_cfb8)} +#else + {NULL} +#endif + }, + {{"des_ede3_cfb"}, +#ifdef HAVE_DES_ede3_cfb_encrypt + {COND_NO_DES_PTR(&EVP_des_ede3_cfb8)} +#else + {NULL} +#endif + }, + {{"blowfish_cbc"}, {&EVP_bf_cbc}}, + {{"blowfish_cfb64"}, {&EVP_bf_cfb64}}, + {{"blowfish_ofb64"}, {&EVP_bf_ofb}}, + {{"blowfish_ecb"}, {&EVP_bf_ecb}}, + {{"aes_cbc"}, {&EVP_aes_128_cbc}, 16}, + {{"aes_cbc"}, {&EVP_aes_192_cbc}, 24}, + {{"aes_cbc"}, {&EVP_aes_256_cbc}, 32}, + {{"aes_cbc128"}, {&EVP_aes_128_cbc}}, + {{"aes_cbc256"}, {&EVP_aes_256_cbc}}, + {{"aes_cfb8"}, {&EVP_aes_128_cfb8}}, + {{"aes_cfb128"}, {&EVP_aes_128_cfb128}}, + {{"aes_ecb"}, {&EVP_aes_128_ecb}, 16}, + {{"aes_ecb"}, {&EVP_aes_192_ecb}, 24}, + {{"aes_ecb"}, {&EVP_aes_256_ecb}, 32}, + {{NULL}} +}; + +#ifdef HAVE_EVP_AES_CTR +ErlNifResourceType* evp_cipher_ctx_rtype; + +static void evp_cipher_ctx_dtor(ErlNifEnv* env, struct evp_cipher_ctx* ctx) { + EVP_CIPHER_CTX_free(ctx->ctx); +} +#endif + +int init_cipher_ctx(ErlNifEnv *env) { +#ifdef HAVE_EVP_AES_CTR + evp_cipher_ctx_rtype = enif_open_resource_type(env, NULL, "EVP_CIPHER_CTX", + (ErlNifResourceDtor*) evp_cipher_ctx_dtor, + ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER, + NULL); + if (evp_cipher_ctx_rtype == NULL) { + PRINTF_ERR0("CRYPTO: Could not open resource type 'EVP_CIPHER_CTX'"); + return 0; + } +#endif + + return 1; +} + +void init_cipher_types(ErlNifEnv* env) +{ + struct cipher_type_t* p = cipher_types; + + for (p = cipher_types; p->type.str; p++) { + p->type.atom = enif_make_atom(env, p->type.str); + if (p->cipher.funcp) + p->cipher.p = p->cipher.funcp(); + } + p->type.atom = atom_false; /* end marker */ +} + +struct cipher_type_t* get_cipher_type(ERL_NIF_TERM type, size_t key_len) +{ + struct cipher_type_t* p = NULL; + for (p = cipher_types; p->type.atom != atom_false; p++) { + if (type == p->type.atom && (!p->key_len || key_len == p->key_len)) { + return p; + } + } + return NULL; +} diff --git a/lib/crypto/c_src/cipher.h b/lib/crypto/c_src/cipher.h new file mode 100644 index 0000000000..3fb27f0ba3 --- /dev/null +++ b/lib/crypto/c_src/cipher.h @@ -0,0 +1,50 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_CIPHER_H__ +#define E_CIPHER_H__ 1 + +#include "common.h" + +struct cipher_type_t { + union { + const char* str; /* before init */ + ERL_NIF_TERM atom; /* after init */ + }type; + union { + const EVP_CIPHER* (*funcp)(void); /* before init, NULL if notsup */ + const EVP_CIPHER* p; /* after init, NULL if notsup */ + }cipher; + const size_t key_len; /* != 0 to also match on key_len */ +}; + +#ifdef HAVE_EVP_AES_CTR +extern ErlNifResourceType* evp_cipher_ctx_rtype; +struct evp_cipher_ctx { + EVP_CIPHER_CTX* ctx; +}; +#endif + +int init_cipher_ctx(ErlNifEnv *env); + +void init_cipher_types(ErlNifEnv* env); +struct cipher_type_t* get_cipher_type(ERL_NIF_TERM type, size_t key_len); + +#endif /* E_CIPHER_H__ */ diff --git a/lib/crypto/c_src/cmac.c b/lib/crypto/c_src/cmac.c new file mode 100644 index 0000000000..526de11a01 --- /dev/null +++ b/lib/crypto/c_src/cmac.c @@ -0,0 +1,70 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "cmac.h" +#include "cipher.h" + +ERL_NIF_TERM cmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Type, Key, Data) */ +#if defined(HAVE_CMAC) + struct cipher_type_t *cipherp = NULL; + const EVP_CIPHER *cipher; + CMAC_CTX *ctx; + ErlNifBinary key; + ErlNifBinary data; + ERL_NIF_TERM ret; + size_t ret_size; + + if (!enif_inspect_iolist_as_binary(env, argv[1], &key) + || !(cipherp = get_cipher_type(argv[0], key.size)) + || !enif_inspect_iolist_as_binary(env, argv[2], &data)) { + return enif_make_badarg(env); + } + cipher = cipherp->cipher.p; + if (!cipher) { + return enif_raise_exception(env, atom_notsup); + } + + ctx = CMAC_CTX_new(); + if (!CMAC_Init(ctx, key.data, key.size, cipher, NULL)) { + CMAC_CTX_free(ctx); + return atom_notsup; + } + + if (!CMAC_Update(ctx, data.data, data.size) || + !CMAC_Final(ctx, + enif_make_new_binary(env, EVP_CIPHER_block_size(cipher), &ret), + &ret_size)) { + CMAC_CTX_free(ctx); + return atom_notsup; + } + ASSERT(ret_size == (unsigned)EVP_CIPHER_block_size(cipher)); + + CMAC_CTX_free(ctx); + CONSUME_REDS(env, data); + return ret; +#else + /* The CMAC functionality was introduced in OpenSSL 1.0.1 + * Although OTP requires at least version 0.9.8, the versions 0.9.8 and 1.0.0 are + * no longer maintained. */ + return atom_notsup; +#endif +} + diff --git a/lib/crypto/c_src/cmac.h b/lib/crypto/c_src/cmac.h new file mode 100644 index 0000000000..14488def58 --- /dev/null +++ b/lib/crypto/c_src/cmac.h @@ -0,0 +1,28 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_CMAC_H__ +#define E_CMAC_H__ 1 + +#include "common.h" + +ERL_NIF_TERM cmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_CMAC_H__ */ diff --git a/lib/crypto/c_src/common.h b/lib/crypto/c_src/common.h new file mode 100644 index 0000000000..1259ba1f36 --- /dev/null +++ b/lib/crypto/c_src/common.h @@ -0,0 +1,36 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_COMMON_H__ +#define E_COMMON_H__ 1 + +#ifdef __WIN32__ +# include <windows.h> +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <erl_nif.h> +#include "openssl_config.h" +#include "atoms.h" + +#endif /* E_COMMON_H__ */ diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c index df607732bf..fde3d99fa8 100644 --- a/lib/crypto/c_src/crypto.c +++ b/lib/crypto/c_src/crypto.c @@ -23,580 +23,41 @@ * Based on OpenSSL. */ -#ifdef __WIN32__ - #include <windows.h> -#endif - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> - -#include <erl_nif.h> - -#define OPENSSL_THREAD_DEFINES -#include <openssl/opensslconf.h> - -#include <openssl/crypto.h> -#ifndef OPENSSL_NO_DES -#include <openssl/des.h> -#endif /* #ifndef OPENSSL_NO_DES */ -/* #include <openssl/idea.h> This is not supported on the openssl OTP requires */ -#include <openssl/dsa.h> -#include <openssl/rsa.h> -#include <openssl/aes.h> -#include <openssl/md5.h> -#include <openssl/md4.h> -#include <openssl/sha.h> -#include <openssl/ripemd.h> -#include <openssl/bn.h> -#include <openssl/objects.h> -#ifndef OPENSSL_NO_RC4 - #include <openssl/rc4.h> -#endif /* OPENSSL_NO_RC4 */ -#ifndef OPENSSL_NO_RC2 - #include <openssl/rc2.h> -#endif -#include <openssl/blowfish.h> -#include <openssl/rand.h> -#include <openssl/evp.h> -#include <openssl/hmac.h> -#include <openssl/err.h> - -/* Helper macro to construct a OPENSSL_VERSION_NUMBER. - * See openssl/opensslv.h - */ -#define PACKED_OPENSSL_VERSION(MAJ, MIN, FIX, P) \ - ((((((((MAJ << 8) | MIN) << 8 ) | FIX) << 8) | (P-'a'+1)) << 4) | 0xf) - -#define PACKED_OPENSSL_VERSION_PLAIN(MAJ, MIN, FIX) \ - PACKED_OPENSSL_VERSION(MAJ,MIN,FIX,('a'-1)) - - -/* LibreSSL was cloned from OpenSSL 1.0.1g and claims to be API and BPI compatible - * with 1.0.1. - * - * LibreSSL has the same names on include files and symbols as OpenSSL, but defines - * the OPENSSL_VERSION_NUMBER to be >= 2.0.0 - * - * Therefor works tests like this as intendend: - * OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) - * (The test is for example "2.4.2" >= "1.0.0" although the test - * with the cloned OpenSSL test would be "1.0.1" >= "1.0.0") - * - * But tests like this gives wrong result: - * OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(1,1,0) - * (The test is false since "2.4.2" < "1.1.0". It should have been - * true because the LibreSSL API version is "1.0.1") - * - */ - -#ifdef LIBRESSL_VERSION_NUMBER -/* A macro to test on in this file */ -#define HAS_LIBRESSL -#endif - -#ifdef HAS_LIBRESSL -/* LibreSSL dislikes FIPS */ -# ifdef FIPS_SUPPORT -# undef FIPS_SUPPORT -# endif - -# if LIBRESSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(2,7,0) -/* LibreSSL wants the 1.0.1 API */ -# define NEED_EVP_COMPATIBILITY_FUNCTIONS -# endif -#endif - - -#if OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(1,1,0) -# define NEED_EVP_COMPATIBILITY_FUNCTIONS -#endif - - -#ifndef HAS_LIBRESSL -# if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) -# define HAS_EVP_PKEY_CTX -# endif -#endif - - -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) -#include <openssl/modes.h> -#endif - -#include "crypto_callback.h" - -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(0,9,8) \ - && !defined(OPENSSL_NO_SHA224) && defined(NID_sha224) \ - && !defined(OPENSSL_NO_SHA256) /* disabled like this in my sha.h (?) */ -# define HAVE_SHA224 -#endif -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(0,9,8) \ - && !defined(OPENSSL_NO_SHA256) && defined(NID_sha256) -# define HAVE_SHA256 -#endif -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(0,9,8) \ - && !defined(OPENSSL_NO_SHA384) && defined(NID_sha384)\ - && !defined(OPENSSL_NO_SHA512) /* disabled like this in my sha.h (?) */ -# define HAVE_SHA384 -#endif -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(0,9,8) \ - && !defined(OPENSSL_NO_SHA512) && defined(NID_sha512) -# define HAVE_SHA512 -#endif -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION(0,9,7,'e') -# define HAVE_DES_ede3_cfb_encrypt -#endif - -// SHA3: -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,1,1) -// An error in beta releases of 1.1.1 fixed in production release -# ifdef NID_sha3_224 -# define HAVE_SHA3_224 -# endif -# ifdef NID_sha3_256 -# define HAVE_SHA3_256 -# endif -#endif -# ifdef NID_sha3_384 -# define HAVE_SHA3_384 -# endif -# ifdef NID_sha3_512 -# define HAVE_SHA3_512 -# endif - -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION(0,9,8,'o') \ - && !defined(OPENSSL_NO_EC) \ - && !defined(OPENSSL_NO_ECDH) \ - && !defined(OPENSSL_NO_ECDSA) -# define HAVE_EC -#endif - -// (test for >= 1.1.1pre8) -#if OPENSSL_VERSION_NUMBER >= (PACKED_OPENSSL_VERSION_PLAIN(1,1,1) -7) \ - && !defined(HAS_LIBRESSL) \ - && defined(HAVE_EC) -# define HAVE_ED_CURVE_DH -# if OPENSSL_VERSION_NUMBER >= (PACKED_OPENSSL_VERSION_PLAIN(1,1,1)) -# define HAVE_EDDSA -# endif -#endif - -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION(0,9,8,'c') -# define HAVE_AES_IGE -#endif - -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,1) -# define HAVE_EVP_AES_CTR -# define HAVE_AEAD -# define HAVE_GCM -# define HAVE_CCM -# define HAVE_CMAC -# if defined(RSA_PKCS1_OAEP_PADDING) -# define HAVE_RSA_OAEP_PADDING -# endif -# define HAVE_RSA_MGF1_MD -# if OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION(1,0,1,'d') -# define HAVE_GCM_EVP_DECRYPT_BUG -# endif -#endif - -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,1,0) -# ifndef HAS_LIBRESSL -# define HAVE_CHACHA20 -# define HAVE_CHACHA20_POLY1305 -# define HAVE_RSA_OAEP_MD -# endif -#endif - -// OPENSSL_VERSION_NUMBER >= 1.1.1-pre8 -#if OPENSSL_VERSION_NUMBER >= (PACKED_OPENSSL_VERSION_PLAIN(1,1,1)-7) -# ifndef HAS_LIBRESSL -# define HAVE_POLY1305 -# endif -#endif - -#if OPENSSL_VERSION_NUMBER <= PACKED_OPENSSL_VERSION(0,9,8,'l') -# define HAVE_ECB_IVEC_BUG -#endif - -#ifndef HAS_LIBRESSL -# ifdef RSA_SSLV23_PADDING -# define HAVE_RSA_SSLV23_PADDING -# endif -#endif - -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) -# ifdef RSA_PKCS1_PSS_PADDING -# define HAVE_RSA_PKCS1_PSS_PADDING -# endif -#endif - -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION(0,9,8,'h') \ - && defined(HAVE_EC) -/* If OPENSSL_NO_EC is set, there will be an error in ec.h included from engine.h - So if EC is disabled, you can't use Engine either.... -*/ -# define HAS_ENGINE_SUPPORT -#endif - - -#if defined(HAS_ENGINE_SUPPORT) -# include <openssl/engine.h> -#endif - -#if defined(HAVE_CMAC) -#include <openssl/cmac.h> -#endif - -#if defined(HAVE_EC) -#include <openssl/ec.h> -#include <openssl/ecdh.h> -#include <openssl/ecdsa.h> -#endif - -#ifdef VALGRIND - # include <valgrind/memcheck.h> - -/* libcrypto mixes supplied buffer contents into its entropy pool, - which makes valgrind complain about the use of uninitialized data. - We use this valgrind "request" to make sure that no such seemingly - undefined data is returned. -*/ - # define ERL_VALGRIND_MAKE_MEM_DEFINED(ptr,size) \ - VALGRIND_MAKE_MEM_DEFINED(ptr,size) - - # define ERL_VALGRIND_ASSERT_MEM_DEFINED(Ptr,Size) \ - do { \ - int __erl_valgrind_mem_defined = VALGRIND_CHECK_MEM_IS_DEFINED((Ptr),(Size)); \ - if (__erl_valgrind_mem_defined != 0) { \ - fprintf(stderr,"\r\n####### VALGRIND_ASSSERT(%p,%ld) failed at %s:%d\r\n", \ - (Ptr),(long)(Size), __FILE__, __LINE__); \ - abort(); \ - } \ - } while (0) - -#else - # define ERL_VALGRIND_MAKE_MEM_DEFINED(ptr,size) - # define ERL_VALGRIND_ASSERT_MEM_DEFINED(ptr,size) -#endif - -#ifdef DEBUG - # define ASSERT(e) \ - ((void) ((e) ? 1 : (fprintf(stderr,"Assert '%s' failed at %s:%d\n",\ - #e, __FILE__, __LINE__), abort(), 0))) -#else - # define ASSERT(e) ((void) 1) -#endif - -#ifdef __GNUC__ - # define INLINE __inline__ -#elif defined(__WIN32__) - # define INLINE __forceinline -#else - # define INLINE -#endif - - -#define get_int32(s) ((((unsigned char*) (s))[0] << 24) | \ - (((unsigned char*) (s))[1] << 16) | \ - (((unsigned char*) (s))[2] << 8) | \ - (((unsigned char*) (s))[3])) - -#define put_int32(s,i) \ -{ (s)[0] = (char)(((i) >> 24) & 0xff);\ - (s)[1] = (char)(((i) >> 16) & 0xff);\ - (s)[2] = (char)(((i) >> 8) & 0xff);\ - (s)[3] = (char)((i) & 0xff);\ -} - -/* This shall correspond to the similar macro in crypto.erl */ -/* Current value is: erlang:system_info(context_reductions) * 10 */ -#define MAX_BYTES_TO_NIF 20000 - -#define CONSUME_REDS(NifEnv, Ibin) \ -do { \ - int _cost = ((Ibin).size * 100) / MAX_BYTES_TO_NIF;\ - if (_cost) { \ - (void) enif_consume_timeslice((NifEnv), \ - (_cost > 100) ? 100 : _cost); \ - } \ - } while (0) - - -#ifdef NEED_EVP_COMPATIBILITY_FUNCTIONS -/* - * In OpenSSL 1.1.0, most structs are opaque. That means that - * the structs cannot be allocated as automatic variables on the - * C stack (because the size is unknown) and that it is necessary - * to use access functions. - * - * For backward compatibility to previous versions of OpenSSL, define - * on our versions of the new functions defined in 1.1.0 here, so that - * we don't have to sprinkle ifdefs throughout the code. - */ - -static HMAC_CTX *HMAC_CTX_new(void); -static void HMAC_CTX_free(HMAC_CTX *ctx); - -static HMAC_CTX *HMAC_CTX_new() -{ - HMAC_CTX *ctx = CRYPTO_malloc(sizeof(HMAC_CTX), __FILE__, __LINE__); - HMAC_CTX_init(ctx); - return ctx; -} - -static void HMAC_CTX_free(HMAC_CTX *ctx) -{ - HMAC_CTX_cleanup(ctx); - CRYPTO_free(ctx); -} - -#define EVP_MD_CTX_new() EVP_MD_CTX_create() -#define EVP_MD_CTX_free(ctx) EVP_MD_CTX_destroy(ctx) - -static INLINE void *BN_GENCB_get_arg(BN_GENCB *cb); - -static INLINE void *BN_GENCB_get_arg(BN_GENCB *cb) -{ - return cb->arg; -} - -static INLINE int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d); -static INLINE void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d); -static INLINE int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q); -static INLINE void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q); -static INLINE int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp); -static INLINE void RSA_get0_crt_params(const RSA *r, const BIGNUM **dmp1, const BIGNUM **dmq1, const BIGNUM **iqmp); - -static INLINE int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) -{ - r->n = n; - r->e = e; - r->d = d; - return 1; -} - -static INLINE void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d) -{ - *n = r->n; - *e = r->e; - *d = r->d; -} - -static INLINE int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q) -{ - r->p = p; - r->q = q; - return 1; -} - -static INLINE void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q) -{ - *p = r->p; - *q = r->q; -} - -static INLINE int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp) -{ - r->dmp1 = dmp1; - r->dmq1 = dmq1; - r->iqmp = iqmp; - return 1; -} - -static INLINE void RSA_get0_crt_params(const RSA *r, const BIGNUM **dmp1, const BIGNUM **dmq1, const BIGNUM **iqmp) -{ - *dmp1 = r->dmp1; - *dmq1 = r->dmq1; - *iqmp = r->iqmp; -} - -static INLINE int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key); -static INLINE int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g); -static INLINE void DSA_get0_pqg(const DSA *dsa, - const BIGNUM **p, const BIGNUM **q, const BIGNUM **g); -static INLINE void DSA_get0_key(const DSA *dsa, - const BIGNUM **pub_key, const BIGNUM **priv_key); - -static INLINE int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key) -{ - d->pub_key = pub_key; - d->priv_key = priv_key; - return 1; -} - -static INLINE int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g) -{ - d->p = p; - d->q = q; - d->g = g; - return 1; -} - -static INLINE void -DSA_get0_pqg(const DSA *dsa, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) -{ - *p = dsa->p; - *q = dsa->q; - *g = dsa->g; -} - -static INLINE void -DSA_get0_key(const DSA *dsa, const BIGNUM **pub_key, const BIGNUM **priv_key) -{ - if (pub_key) *pub_key = dsa->pub_key; - if (priv_key) *priv_key = dsa->priv_key; -} - - - -static INLINE int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key); -static INLINE int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g); -static INLINE int DH_set_length(DH *dh, long length); -static INLINE void DH_get0_pqg(const DH *dh, - const BIGNUM **p, const BIGNUM **q, const BIGNUM **g); -static INLINE void DH_get0_key(const DH *dh, - const BIGNUM **pub_key, const BIGNUM **priv_key); - -static INLINE int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key) -{ - dh->pub_key = pub_key; - dh->priv_key = priv_key; - return 1; -} - -static INLINE int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g) -{ - dh->p = p; - dh->q = q; - dh->g = g; - return 1; -} - -static INLINE int DH_set_length(DH *dh, long length) -{ - dh->length = length; - return 1; -} - - - -static INLINE void -DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) -{ - *p = dh->p; - *q = dh->q; - *g = dh->g; -} - -static INLINE void -DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key) -{ - if (pub_key) *pub_key = dh->pub_key; - if (priv_key) *priv_key = dh->priv_key; -} - -#else /* End of compatibility definitions. */ - -#define HAVE_OPAQUE_BN_GENCB - -#endif +#include "common.h" + +#include "aead.h" +#include "aes.h" +#include "algorithms.h" +#include "block.h" +#include "bn.h" +#include "chacha20.h" +#include "cipher.h" +#include "cmac.h" +#include "dh.h" +#include "digest.h" +#include "dss.h" +#include "ec.h" +#include "ecdh.h" +#include "eddsa.h" +#include "engine.h" +#include "evp.h" +#include "fips.h" +#include "hash.h" +#include "hmac.h" +#include "info.h" +#include "math.h" +#include "pkey.h" +#include "poly1305.h" +#include "rand.h" +#include "rc4.h" +#include "rsa.h" +#include "srp.h" /* NIF interface declarations */ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info); static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info); 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 info_fips(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM enable_fips_mode(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 hash_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM hash_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM hash_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM hash_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM hmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM hmac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM hmac_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM hmac_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM cmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM block_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM aes_cfb_8_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM aes_cfb_128_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM aes_ige_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM aes_ctr_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM strong_rand_bytes_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM strong_rand_range_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM rand_uniform_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM mod_exp_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM do_exor(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM rc4_set_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM rc4_encrypt_with_state(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM pkey_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM pkey_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM pkey_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM rsa_generate_key_nif(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 privkey_to_pubkey_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_user_secret_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM srp_host_secret_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); - -static ERL_NIF_TERM ec_key_generate(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM ecdh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); - -static ERL_NIF_TERM evp_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM evp_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); - -static ERL_NIF_TERM rand_seed_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); - -static ERL_NIF_TERM aead_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM aead_decrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -#ifdef HAVE_GCM_EVP_DECRYPT_BUG -static ERL_NIF_TERM aes_gcm_decrypt_NO_EVP(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -#endif - -static ERL_NIF_TERM chacha20_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM chacha20_stream_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); - -static ERL_NIF_TERM poly1305_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); - -static ERL_NIF_TERM engine_by_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM engine_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM engine_finish_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM engine_free_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM engine_load_dynamic_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM engine_ctrl_cmd_strings_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM engine_register_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM engine_unregister_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM engine_add_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM engine_remove_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM engine_get_first_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM engine_get_next_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM engine_get_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM engine_get_name_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM engine_get_all_methods_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); - -/* helpers */ -static void init_algorithms_types(ErlNifEnv*); -static void init_digest_types(ErlNifEnv* env); -static void init_cipher_types(ErlNifEnv* env); -#ifdef HAVE_EC -static EC_KEY* ec_key_new(ErlNifEnv* env, ERL_NIF_TERM curve_arg); -static int term2point(ErlNifEnv* env, ERL_NIF_TERM term, - EC_GROUP *group, EC_POINT **pptr); -#endif -static ERL_NIF_TERM bin_from_bn(ErlNifEnv* env, const BIGNUM *bn); - -#ifdef HAS_ENGINE_SUPPORT -static int get_engine_load_cmd_list(ErlNifEnv* env, const ERL_NIF_TERM term, char **cmds, int i); -static int zero_terminate(ErlNifBinary bin, char **buf); -#endif - static int library_refc = 0; /* number of users of this dynamic library */ static int library_initialized = 0; @@ -676,329 +137,6 @@ static ErlNifFunc nif_funcs[] = { ERL_NIF_INIT(crypto,nif_funcs,load,NULL,upgrade,unload) -#define MD5_CTX_LEN (sizeof(MD5_CTX)) -#define MD4_CTX_LEN (sizeof(MD4_CTX)) -#define RIPEMD160_CTX_LEN (sizeof(RIPEMD160_CTX)) - - -static ERL_NIF_TERM atom_true; -static ERL_NIF_TERM atom_false; -static ERL_NIF_TERM atom_sha; -static ERL_NIF_TERM atom_error; -static ERL_NIF_TERM atom_rsa_pkcs1_padding; -static ERL_NIF_TERM atom_rsa_pkcs1_oaep_padding; -static ERL_NIF_TERM atom_rsa_no_padding; -static ERL_NIF_TERM atom_signature_md; -static ERL_NIF_TERM atom_undefined; - -static ERL_NIF_TERM atom_ok; -static ERL_NIF_TERM atom_not_prime; -static ERL_NIF_TERM atom_not_strong_prime; -static ERL_NIF_TERM atom_unable_to_check_generator; -static ERL_NIF_TERM atom_not_suitable_generator; -static ERL_NIF_TERM atom_check_failed; -static ERL_NIF_TERM atom_unknown; -static ERL_NIF_TERM atom_none; -static ERL_NIF_TERM atom_notsup; -static ERL_NIF_TERM atom_digest; -#ifdef FIPS_SUPPORT -static ERL_NIF_TERM atom_enabled; -static ERL_NIF_TERM atom_not_enabled; -#else -static ERL_NIF_TERM atom_not_supported; -#endif - -#if defined(HAVE_EC) -static ERL_NIF_TERM atom_ec; -static ERL_NIF_TERM atom_prime_field; -static ERL_NIF_TERM atom_characteristic_two_field; -static ERL_NIF_TERM atom_tpbasis; -static ERL_NIF_TERM atom_ppbasis; -static ERL_NIF_TERM atom_onbasis; -#endif - -static ERL_NIF_TERM atom_aes_cfb8; -static ERL_NIF_TERM atom_aes_cfb128; -#ifdef HAVE_GCM -static ERL_NIF_TERM atom_aes_gcm; -#endif -#ifdef HAVE_CCM -static ERL_NIF_TERM atom_aes_ccm; -#endif -#ifdef HAVE_CHACHA20_POLY1305 -static ERL_NIF_TERM atom_chacha20_poly1305; -#endif -#ifdef HAVE_ECB_IVEC_BUG -static ERL_NIF_TERM atom_aes_ecb; -static ERL_NIF_TERM atom_des_ecb; -static ERL_NIF_TERM atom_blowfish_ecb; -#endif - -static ERL_NIF_TERM atom_rsa; -static ERL_NIF_TERM atom_dss; -static ERL_NIF_TERM atom_ecdsa; - -#ifdef HAVE_ED_CURVE_DH -static ERL_NIF_TERM atom_x25519; -static ERL_NIF_TERM atom_x448; -#endif - -static ERL_NIF_TERM atom_eddsa; -#ifdef HAVE_EDDSA -static ERL_NIF_TERM atom_ed25519; -static ERL_NIF_TERM atom_ed448; -#endif - -static ERL_NIF_TERM atom_rsa_mgf1_md; -static ERL_NIF_TERM atom_rsa_oaep_label; -static ERL_NIF_TERM atom_rsa_oaep_md; -static ERL_NIF_TERM atom_rsa_pad; /* backwards compatibility */ -static ERL_NIF_TERM atom_rsa_padding; -static ERL_NIF_TERM atom_rsa_pkcs1_pss_padding; -#ifdef HAVE_RSA_SSLV23_PADDING -static ERL_NIF_TERM atom_rsa_sslv23_padding; -#endif -static ERL_NIF_TERM atom_rsa_x931_padding; -static ERL_NIF_TERM atom_rsa_pss_saltlen; -static ERL_NIF_TERM atom_sha224; -static ERL_NIF_TERM atom_sha256; -static ERL_NIF_TERM atom_sha384; -static ERL_NIF_TERM atom_sha512; -static ERL_NIF_TERM atom_sha3_224; -static ERL_NIF_TERM atom_sha3_256; -static ERL_NIF_TERM atom_sha3_384; -static ERL_NIF_TERM atom_sha3_512; -static ERL_NIF_TERM atom_md5; -static ERL_NIF_TERM atom_ripemd160; - -#ifdef HAS_ENGINE_SUPPORT -static ERL_NIF_TERM atom_bad_engine_method; -static ERL_NIF_TERM atom_bad_engine_id; -static ERL_NIF_TERM atom_ctrl_cmd_failed; -static ERL_NIF_TERM atom_engine_init_failed; -static ERL_NIF_TERM atom_register_engine_failed; -static ERL_NIF_TERM atom_add_engine_failed; -static ERL_NIF_TERM atom_remove_engine_failed; -static ERL_NIF_TERM atom_engine_method_not_supported; - -static ERL_NIF_TERM atom_engine_method_rsa; -static ERL_NIF_TERM atom_engine_method_dsa; -static ERL_NIF_TERM atom_engine_method_dh; -static ERL_NIF_TERM atom_engine_method_rand; -static ERL_NIF_TERM atom_engine_method_ecdh; -static ERL_NIF_TERM atom_engine_method_ecdsa; -static ERL_NIF_TERM atom_engine_method_ciphers; -static ERL_NIF_TERM atom_engine_method_digests; -static ERL_NIF_TERM atom_engine_method_store; -static ERL_NIF_TERM atom_engine_method_pkey_meths; -static ERL_NIF_TERM atom_engine_method_pkey_asn1_meths; -static ERL_NIF_TERM atom_engine_method_ec; - -static ERL_NIF_TERM atom_engine; -static ERL_NIF_TERM atom_key_id; -static ERL_NIF_TERM atom_password; -#endif - -static ErlNifResourceType* hmac_context_rtype; -struct hmac_context -{ - ErlNifMutex* mtx; - int alive; - HMAC_CTX* ctx; -}; -static void hmac_context_dtor(ErlNifEnv* env, struct hmac_context*); - -struct digest_type_t { - union { - const char* str; /* before init, NULL for end-of-table */ - ERL_NIF_TERM atom; /* after init, 'false' for end-of-table */ - }type; - union { - const EVP_MD* (*funcp)(void); /* before init, NULL if notsup */ - const EVP_MD* p; /* after init, NULL if notsup */ - }md; -}; - -static struct digest_type_t digest_types[] = -{ - {{"md4"}, {&EVP_md4}}, - {{"md5"}, {&EVP_md5}}, - {{"ripemd160"}, {&EVP_ripemd160}}, - {{"sha"}, {&EVP_sha1}}, - {{"sha224"}, -#ifdef HAVE_SHA224 - {&EVP_sha224} -#else - {NULL} -#endif - }, - {{"sha256"}, -#ifdef HAVE_SHA256 - {&EVP_sha256} -#else - {NULL} -#endif - }, - {{"sha384"}, -#ifdef HAVE_SHA384 - {&EVP_sha384} -#else - {NULL} -#endif - }, - {{"sha512"}, -#ifdef HAVE_SHA512 - {&EVP_sha512} -#else - {NULL} -#endif - }, - {{"sha3_224"}, -#ifdef HAVE_SHA3_224 - {&EVP_sha3_224} -#else - {NULL} -#endif - }, - {{"sha3_256"}, -#ifdef HAVE_SHA3_256 - {&EVP_sha3_256} -#else - {NULL} -#endif - }, - {{"sha3_384"}, -#ifdef HAVE_SHA3_384 - {&EVP_sha3_384} -#else - {NULL} -#endif - }, - {{"sha3_512"}, -#ifdef HAVE_SHA3_512 - {&EVP_sha3_512} -#else - {NULL} -#endif - }, - - {{NULL}} -}; - -static struct digest_type_t* get_digest_type(ERL_NIF_TERM type); - -struct cipher_type_t { - union { - const char* str; /* before init */ - ERL_NIF_TERM atom; /* after init */ - }type; - union { - const EVP_CIPHER* (*funcp)(void); /* before init, NULL if notsup */ - const EVP_CIPHER* p; /* after init, NULL if notsup */ - }cipher; - const size_t key_len; /* != 0 to also match on key_len */ -}; - -#ifdef OPENSSL_NO_DES -#define COND_NO_DES_PTR(Ptr) (NULL) -#else -#define COND_NO_DES_PTR(Ptr) (Ptr) -#endif - -static struct cipher_type_t cipher_types[] = -{ - {{"rc2_cbc"}, -#ifndef OPENSSL_NO_RC2 - {&EVP_rc2_cbc} -#else - {NULL} -#endif - }, - {{"des_cbc"}, {COND_NO_DES_PTR(&EVP_des_cbc)}}, - {{"des_cfb"}, {COND_NO_DES_PTR(&EVP_des_cfb8)}}, - {{"des_ecb"}, {COND_NO_DES_PTR(&EVP_des_ecb)}}, - {{"des_ede3_cbc"}, {COND_NO_DES_PTR(&EVP_des_ede3_cbc)}}, - {{"des_ede3_cbf"}, /* Misspelled, retained */ -#ifdef HAVE_DES_ede3_cfb_encrypt - {COND_NO_DES_PTR(&EVP_des_ede3_cfb8)} -#else - {NULL} -#endif - }, - {{"des_ede3_cfb"}, -#ifdef HAVE_DES_ede3_cfb_encrypt - {COND_NO_DES_PTR(&EVP_des_ede3_cfb8)} -#else - {NULL} -#endif - }, - {{"blowfish_cbc"}, {&EVP_bf_cbc}}, - {{"blowfish_cfb64"}, {&EVP_bf_cfb64}}, - {{"blowfish_ofb64"}, {&EVP_bf_ofb}}, - {{"blowfish_ecb"}, {&EVP_bf_ecb}}, - {{"aes_cbc"}, {&EVP_aes_128_cbc}, 16}, - {{"aes_cbc"}, {&EVP_aes_192_cbc}, 24}, - {{"aes_cbc"}, {&EVP_aes_256_cbc}, 32}, - {{"aes_cbc128"}, {&EVP_aes_128_cbc}}, - {{"aes_cbc256"}, {&EVP_aes_256_cbc}}, - {{"aes_cfb8"}, {&EVP_aes_128_cfb8}}, - {{"aes_cfb128"}, {&EVP_aes_128_cfb128}}, - {{"aes_ecb"}, {&EVP_aes_128_ecb}, 16}, - {{"aes_ecb"}, {&EVP_aes_192_ecb}, 24}, - {{"aes_ecb"}, {&EVP_aes_256_ecb}, 32}, - {{NULL}} -}; - -static struct cipher_type_t* get_cipher_type(ERL_NIF_TERM type, size_t key_len); - - -/* -#define PRINTF_ERR0(FMT) enif_fprintf(stderr, FMT "\n") -#define PRINTF_ERR1(FMT, A1) enif_fprintf(stderr, FMT "\n", A1) -#define PRINTF_ERR2(FMT, A1, A2) enif_fprintf(stderr, FMT "\n", A1, A2) -*/ - -#define PRINTF_ERR0(FMT) -#define PRINTF_ERR1(FMT,A1) -#define PRINTF_ERR2(FMT,A1,A2) - -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) -/* Define resource types for OpenSSL context structures. */ -static ErlNifResourceType* evp_md_ctx_rtype; -struct evp_md_ctx { - EVP_MD_CTX* ctx; -}; -static void evp_md_ctx_dtor(ErlNifEnv* env, struct evp_md_ctx *ctx) { - EVP_MD_CTX_free(ctx->ctx); -} -#endif - -#ifdef HAVE_EVP_AES_CTR -static ErlNifResourceType* evp_cipher_ctx_rtype; -struct evp_cipher_ctx { - EVP_CIPHER_CTX* ctx; -}; -static void evp_cipher_ctx_dtor(ErlNifEnv* env, struct evp_cipher_ctx* ctx) { - EVP_CIPHER_CTX_free(ctx->ctx); -} -#endif - -// Engine -#ifdef HAS_ENGINE_SUPPORT -static ErlNifResourceType* engine_ctx_rtype; -struct engine_ctx { - ENGINE *engine; - char *id; -}; -static void engine_ctx_dtor(ErlNifEnv* env, struct engine_ctx* ctx) { - PRINTF_ERR0("engine_ctx_dtor"); - if(ctx->id) { - PRINTF_ERR1(" non empty ctx->id=%s", ctx->id); - enif_free(ctx->id); - } else - PRINTF_ERR0(" empty ctx->id=NULL"); -} -#endif static int verify_lib_version(void) { @@ -1015,46 +153,6 @@ static int verify_lib_version(void) return 1; } -#ifdef FIPS_SUPPORT -/* In FIPS mode non-FIPS algorithms are disabled and return badarg. */ -#define CHECK_NO_FIPS_MODE() { if (FIPS_mode()) return atom_notsup; } -#else -#define CHECK_NO_FIPS_MODE() -#endif - -#ifdef HAVE_DYNAMIC_CRYPTO_LIB - -# if defined(DEBUG) -static char crypto_callback_name[] = "crypto_callback.debug"; -# elif defined(VALGRIND) -static char crypto_callback_name[] = "crypto_callback.valgrind"; -# else -static char crypto_callback_name[] = "crypto_callback"; -# endif - -static int change_basename(ErlNifBinary* bin, char* buf, int bufsz, const char* newfile) -{ - int i; - - for (i = bin->size; i > 0; i--) { - if (bin->data[i-1] == '/') - break; - } - if (i + strlen(newfile) >= bufsz) { - PRINTF_ERR0("CRYPTO: lib name too long"); - return 0; - } - memcpy(buf, bin->data, i); - strcpy(buf+i, newfile); - return 1; -} - -static void error_handler(void* null, const char* errstr) -{ - PRINTF_ERR1("CRYPTO LOADING ERROR: '%s'", errstr); -} -#endif /* HAVE_DYNAMIC_CRYPTO_LIB */ - static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info) { #ifdef OPENSSL_THREADS @@ -1083,44 +181,18 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info) return __LINE__; } - hmac_context_rtype = enif_open_resource_type(env, NULL, "hmac_context", - (ErlNifResourceDtor*) hmac_context_dtor, - ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER, - NULL); - if (!hmac_context_rtype) { - PRINTF_ERR0("CRYPTO: Could not open resource type 'hmac_context'"); + if (!init_hmac_ctx(env)) { return __LINE__; } -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) - evp_md_ctx_rtype = enif_open_resource_type(env, NULL, "EVP_MD_CTX", - (ErlNifResourceDtor*) evp_md_ctx_dtor, - ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER, - NULL); - if (!evp_md_ctx_rtype) { - PRINTF_ERR0("CRYPTO: Could not open resource type 'EVP_MD_CTX'"); + if (!init_hash_ctx(env)) { return __LINE__; } -#endif -#ifdef HAVE_EVP_AES_CTR - evp_cipher_ctx_rtype = enif_open_resource_type(env, NULL, "EVP_CIPHER_CTX", - (ErlNifResourceDtor*) evp_cipher_ctx_dtor, - ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER, - NULL); - if (!evp_cipher_ctx_rtype) { - PRINTF_ERR0("CRYPTO: Could not open resource type 'EVP_CIPHER_CTX'"); + if (!init_cipher_ctx(env)) { return __LINE__; } -#endif -#ifdef HAS_ENGINE_SUPPORT - engine_ctx_rtype = enif_open_resource_type(env, NULL, "ENGINE_CTX", - (ErlNifResourceDtor*) engine_ctx_dtor, - ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER, - NULL); - if (!engine_ctx_rtype) { - PRINTF_ERR0("CRYPTO: Could not open resource type 'ENGINE_CTX'"); + if (!init_engine_ctx(env)) { return __LINE__; } -#endif if (library_initialized) { /* Repeated loading of this library (module upgrade). @@ -1129,135 +201,10 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info) return 0; } - atom_true = enif_make_atom(env,"true"); - atom_false = enif_make_atom(env,"false"); - /* Enter FIPS mode */ - if (tpl_array[2] == atom_true) { -#ifdef FIPS_SUPPORT - if (!FIPS_mode_set(1)) { -#else - { -#endif - PRINTF_ERR0("CRYPTO: Could not setup FIPS mode"); - return 0; - } - } else if (tpl_array[2] != atom_false) { - PRINTF_ERR1("CRYPTO: Invalid load_info '%T'", load_info); - return 0; + if (!init_atoms(env, tpl_array[2], load_info)) { + return __LINE__; } - atom_sha = enif_make_atom(env,"sha"); - atom_error = enif_make_atom(env,"error"); - atom_rsa_pkcs1_padding = enif_make_atom(env,"rsa_pkcs1_padding"); - atom_rsa_pkcs1_oaep_padding = enif_make_atom(env,"rsa_pkcs1_oaep_padding"); - atom_rsa_no_padding = enif_make_atom(env,"rsa_no_padding"); - atom_signature_md = enif_make_atom(env,"signature_md"); - atom_undefined = enif_make_atom(env,"undefined"); - atom_ok = enif_make_atom(env,"ok"); - atom_not_prime = enif_make_atom(env,"not_prime"); - atom_not_strong_prime = enif_make_atom(env,"not_strong_prime"); - atom_unable_to_check_generator = enif_make_atom(env,"unable_to_check_generator"); - atom_not_suitable_generator = enif_make_atom(env,"not_suitable_generator"); - atom_check_failed = enif_make_atom(env,"check_failed"); - atom_unknown = enif_make_atom(env,"unknown"); - atom_none = enif_make_atom(env,"none"); - atom_notsup = enif_make_atom(env,"notsup"); - atom_digest = enif_make_atom(env,"digest"); - -#if defined(HAVE_EC) - atom_ec = enif_make_atom(env,"ec"); - atom_prime_field = enif_make_atom(env,"prime_field"); - atom_characteristic_two_field = enif_make_atom(env,"characteristic_two_field"); - atom_tpbasis = enif_make_atom(env,"tpbasis"); - atom_ppbasis = enif_make_atom(env,"ppbasis"); - atom_onbasis = enif_make_atom(env,"onbasis"); -#endif - - atom_aes_cfb8 = enif_make_atom(env, "aes_cfb8"); - atom_aes_cfb128 = enif_make_atom(env, "aes_cfb128"); -#ifdef HAVE_GCM - atom_aes_gcm = enif_make_atom(env, "aes_gcm"); -#endif -#ifdef HAVE_CCM - atom_aes_ccm = enif_make_atom(env, "aes_ccm"); -#endif -#ifdef HAVE_CHACHA20_POLY1305 - atom_chacha20_poly1305 = enif_make_atom(env,"chacha20_poly1305"); -#endif -#ifdef HAVE_ECB_IVEC_BUG - atom_aes_ecb = enif_make_atom(env, "aes_ecb"); - atom_des_ecb = enif_make_atom(env, "des_ecb"); - atom_blowfish_ecb = enif_make_atom(env, "blowfish_ecb"); -#endif - -#ifdef FIPS_SUPPORT - atom_enabled = enif_make_atom(env,"enabled"); - atom_not_enabled = enif_make_atom(env,"not_enabled"); -#else - atom_not_supported = enif_make_atom(env,"not_supported"); -#endif - atom_rsa = enif_make_atom(env,"rsa"); - atom_dss = enif_make_atom(env,"dss"); - atom_ecdsa = enif_make_atom(env,"ecdsa"); -#ifdef HAVE_ED_CURVE_DH - atom_x25519 = enif_make_atom(env,"x25519"); - atom_x448 = enif_make_atom(env,"x448"); -#endif - atom_eddsa = enif_make_atom(env,"eddsa"); -#ifdef HAVE_EDDSA - atom_ed25519 = enif_make_atom(env,"ed25519"); - atom_ed448 = enif_make_atom(env,"ed448"); -#endif - atom_rsa_mgf1_md = enif_make_atom(env,"rsa_mgf1_md"); - atom_rsa_oaep_label = enif_make_atom(env,"rsa_oaep_label"); - atom_rsa_oaep_md = enif_make_atom(env,"rsa_oaep_md"); - atom_rsa_pad = enif_make_atom(env,"rsa_pad"); /* backwards compatibility */ - atom_rsa_padding = enif_make_atom(env,"rsa_padding"); - atom_rsa_pkcs1_pss_padding = enif_make_atom(env,"rsa_pkcs1_pss_padding"); -#ifdef HAVE_RSA_SSLV23_PADDING - atom_rsa_sslv23_padding = enif_make_atom(env,"rsa_sslv23_padding"); -#endif - atom_rsa_x931_padding = enif_make_atom(env,"rsa_x931_padding"); - atom_rsa_pss_saltlen = enif_make_atom(env,"rsa_pss_saltlen"); - atom_sha224 = enif_make_atom(env,"sha224"); - atom_sha256 = enif_make_atom(env,"sha256"); - atom_sha384 = enif_make_atom(env,"sha384"); - atom_sha512 = enif_make_atom(env,"sha512"); - atom_sha3_224 = enif_make_atom(env,"sha3_224"); - atom_sha3_256 = enif_make_atom(env,"sha3_256"); - atom_sha3_384 = enif_make_atom(env,"sha3_384"); - atom_sha3_512 = enif_make_atom(env,"sha3_512"); - atom_md5 = enif_make_atom(env,"md5"); - atom_ripemd160 = enif_make_atom(env,"ripemd160"); - -#ifdef HAS_ENGINE_SUPPORT - atom_bad_engine_method = enif_make_atom(env,"bad_engine_method"); - atom_bad_engine_id = enif_make_atom(env,"bad_engine_id"); - atom_ctrl_cmd_failed = enif_make_atom(env,"ctrl_cmd_failed"); - atom_engine_init_failed = enif_make_atom(env,"engine_init_failed"); - atom_engine_method_not_supported = enif_make_atom(env,"engine_method_not_supported"); - atom_add_engine_failed = enif_make_atom(env,"add_engine_failed"); - atom_remove_engine_failed = enif_make_atom(env,"remove_engine_failed"); - - atom_engine_method_rsa = enif_make_atom(env,"engine_method_rsa"); - atom_engine_method_dsa = enif_make_atom(env,"engine_method_dsa"); - atom_engine_method_dh = enif_make_atom(env,"engine_method_dh"); - atom_engine_method_rand = enif_make_atom(env,"engine_method_rand"); - atom_engine_method_ecdh = enif_make_atom(env,"engine_method_ecdh"); - atom_engine_method_ecdsa = enif_make_atom(env,"engine_method_ecdsa"); - atom_engine_method_store = enif_make_atom(env,"engine_method_store"); - atom_engine_method_ciphers = enif_make_atom(env,"engine_method_ciphers"); - atom_engine_method_digests = enif_make_atom(env,"engine_method_digests"); - atom_engine_method_pkey_meths = enif_make_atom(env,"engine_method_pkey_meths"); - atom_engine_method_pkey_asn1_meths = enif_make_atom(env,"engine_method_pkey_asn1_meths"); - atom_engine_method_ec = enif_make_atom(env,"engine_method_ec"); - - atom_engine = enif_make_atom(env,"engine"); - atom_key_id = enif_make_atom(env,"key_id"); - atom_password = enif_make_atom(env,"password"); -#endif - - #ifdef HAVE_DYNAMIC_CRYPTO_LIB { void* handle; @@ -1345,4806 +292,3 @@ static void unload(ErlNifEnv* env, void* priv_data) { --library_refc; } - -static int algo_hash_cnt, algo_hash_fips_cnt; -static ERL_NIF_TERM algo_hash[12]; /* increase when extending the list */ -static int algo_pubkey_cnt, algo_pubkey_fips_cnt; -static ERL_NIF_TERM algo_pubkey[12]; /* increase when extending the list */ -static int algo_cipher_cnt, algo_cipher_fips_cnt; -static ERL_NIF_TERM algo_cipher[25]; /* increase when extending the list */ -static int algo_mac_cnt, algo_mac_fips_cnt; -static ERL_NIF_TERM algo_mac[3]; /* increase when extending the list */ -static int algo_curve_cnt, algo_curve_fips_cnt; -static ERL_NIF_TERM algo_curve[89]; /* increase when extending the list */ -static int algo_rsa_opts_cnt, algo_rsa_opts_fips_cnt; -static ERL_NIF_TERM algo_rsa_opts[11]; /* increase when extending the list */ - -static void init_algorithms_types(ErlNifEnv* env) -{ - // Validated algorithms first - algo_hash_cnt = 0; - algo_hash[algo_hash_cnt++] = atom_sha; -#ifdef HAVE_SHA224 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha224"); -#endif -#ifdef HAVE_SHA256 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha256"); -#endif -#ifdef HAVE_SHA384 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha384"); -#endif -#ifdef HAVE_SHA512 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha512"); -#endif -#ifdef HAVE_SHA3_224 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha3_224"); -#endif -#ifdef HAVE_SHA3_256 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha3_256"); -#endif -#ifdef HAVE_SHA3_384 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha3_384"); -#endif -#ifdef HAVE_SHA3_512 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha3_512"); -#endif - // Non-validated algorithms follow - algo_hash_fips_cnt = algo_hash_cnt; - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "md4"); - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "md5"); - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "ripemd160"); - - algo_pubkey_cnt = 0; - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "rsa"); - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "dss"); - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "dh"); -#if defined(HAVE_EC) -#if !defined(OPENSSL_NO_EC2M) - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "ec_gf2m"); -#endif - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "ecdsa"); - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "ecdh"); -#endif - // Non-validated algorithms follow - algo_pubkey_fips_cnt = algo_pubkey_cnt; - // Don't know if Edward curves are fips validated -#if defined(HAVE_EDDSA) - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "eddsa"); -#endif - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "srp"); - - // Validated algorithms first - algo_cipher_cnt = 0; -#ifndef OPENSSL_NO_DES - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "des3_cbc"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "des_ede3"); -#ifdef HAVE_DES_ede3_cfb_encrypt - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "des3_cbf"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "des3_cfb"); -#endif -#endif - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cbc"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cbc128"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cfb8"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cfb128"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_cbc256"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_ctr"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env, "aes_ecb"); -#if defined(HAVE_GCM) - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"aes_gcm"); -#endif -#if defined(HAVE_CCM) - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"aes_ccm"); -#endif - // Non-validated algorithms follow - algo_cipher_fips_cnt = algo_cipher_cnt; -#ifdef HAVE_AES_IGE - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"aes_ige256"); -#endif -#ifndef OPENSSL_NO_DES - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"des_cbc"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"des_cfb"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"des_ecb"); -#endif - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"blowfish_cbc"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"blowfish_cfb64"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"blowfish_ofb64"); - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"blowfish_ecb"); -#ifndef OPENSSL_NO_RC2 - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"rc2_cbc"); -#endif -#ifndef OPENSSL_NO_RC4 - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"rc4"); -#endif -#if defined(HAVE_CHACHA20_POLY1305) - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"chacha20_poly1305"); -#endif -#if defined(HAVE_CHACHA20) - algo_cipher[algo_cipher_cnt++] = enif_make_atom(env,"chacha20"); -#endif - - // Validated algorithms first - algo_mac_cnt = 0; - algo_mac[algo_mac_cnt++] = enif_make_atom(env,"hmac"); -#ifdef HAVE_CMAC - algo_mac[algo_mac_cnt++] = enif_make_atom(env,"cmac"); -#endif -#ifdef HAVE_POLY1305 - algo_mac[algo_mac_cnt++] = enif_make_atom(env,"poly1305"); -#endif - // Non-validated algorithms follow - algo_mac_fips_cnt = algo_mac_cnt; - - // Validated algorithms first - algo_curve_cnt = 0; -#if defined(HAVE_EC) - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp160k1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp160r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp160r2"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp192r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp192k1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp224k1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp224r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp256k1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp256r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp384r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp521r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"prime192v1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"prime192v2"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"prime192v3"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"prime239v1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"prime239v2"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"prime239v3"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"prime256v1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls7"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls9"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls12"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP160r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP160t1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP192r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP192t1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP224r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP224t1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP256r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP256t1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP320r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP320t1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP384r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP384t1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP512r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"brainpoolP512t1"); -#if !defined(OPENSSL_NO_EC2M) - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect163k1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect163r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect163r2"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect193r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect193r2"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect233k1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect233r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect239k1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect283k1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect283r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect409k1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect409r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect571k1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect571r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb163v1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb163v2"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb163v3"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb176v1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb191v1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb191v2"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb191v3"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb208w1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb239v1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb239v2"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb239v3"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb272w1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb304w1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb359v1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2pnb368w1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"c2tnb431r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls3"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls5"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls10"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls11"); -#endif -#endif - // Non-validated algorithms follow - algo_curve_fips_cnt = algo_curve_cnt; -#if defined(HAVE_EC) - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp112r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp112r2"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp128r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"secp128r2"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls6"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls8"); -#if !defined(OPENSSL_NO_EC2M) - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect113r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect113r2"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect131r1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"sect131r2"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls1"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"wtls4"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"ipsec3"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"ipsec4"); -#endif -#endif - //-- -#ifdef HAVE_EDDSA - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"ed25519"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"ed448"); -#endif -#ifdef HAVE_ED_CURVE_DH - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"x25519"); - algo_curve[algo_curve_cnt++] = enif_make_atom(env,"x448"); -#endif - - // Validated algorithms first - algo_rsa_opts_cnt = 0; -#ifdef HAS_EVP_PKEY_CTX -# ifdef HAVE_RSA_PKCS1_PSS_PADDING - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_pkcs1_pss_padding"); - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_pss_saltlen"); -# endif -# ifdef HAVE_RSA_MGF1_MD - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_mgf1_md"); -# endif -# ifdef HAVE_RSA_OAEP_PADDING - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_pkcs1_oaep_padding"); -# endif -# ifdef HAVE_RSA_OAEP_MD - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_oaep_label"); - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_oaep_md"); -# endif - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"signature_md"); -#endif - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_pkcs1_padding"); - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_x931_padding"); -#ifdef HAVE_RSA_SSLV23_PADDING - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_sslv23_padding"); -#endif - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_no_padding"); - algo_rsa_opts_fips_cnt = algo_rsa_opts_cnt; - - - // Check that the max number of algos is updated - ASSERT(algo_hash_cnt <= sizeof(algo_hash)/sizeof(ERL_NIF_TERM)); - ASSERT(algo_pubkey_cnt <= sizeof(algo_pubkey)/sizeof(ERL_NIF_TERM)); - ASSERT(algo_cipher_cnt <= sizeof(algo_cipher)/sizeof(ERL_NIF_TERM)); - ASSERT(algo_mac_cnt <= sizeof(algo_mac)/sizeof(ERL_NIF_TERM)); - ASSERT(algo_curve_cnt <= sizeof(algo_curve)/sizeof(ERL_NIF_TERM)); - ASSERT(algo_rsa_opts_cnt <= sizeof(algo_rsa_opts)/sizeof(ERL_NIF_TERM)); -} - -static ERL_NIF_TERM algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ -#ifdef FIPS_SUPPORT - int fips_mode = FIPS_mode(); - int hash_cnt = fips_mode ? algo_hash_fips_cnt : algo_hash_cnt; - int pubkey_cnt = fips_mode ? algo_pubkey_fips_cnt : algo_pubkey_cnt; - int cipher_cnt = fips_mode ? algo_cipher_fips_cnt : algo_cipher_cnt; - int mac_cnt = fips_mode ? algo_mac_fips_cnt : algo_mac_cnt; - int curve_cnt = fips_mode ? algo_curve_fips_cnt : algo_curve_cnt; - int rsa_opts_cnt = fips_mode ? algo_rsa_opts_fips_cnt : algo_rsa_opts_cnt; -#else - int hash_cnt = algo_hash_cnt; - int pubkey_cnt = algo_pubkey_cnt; - int cipher_cnt = algo_cipher_cnt; - int mac_cnt = algo_mac_cnt; - int curve_cnt = algo_curve_cnt; - int rsa_opts_cnt = algo_rsa_opts_cnt; -#endif - return enif_make_tuple6(env, - enif_make_list_from_array(env, algo_hash, hash_cnt), - enif_make_list_from_array(env, algo_pubkey, pubkey_cnt), - enif_make_list_from_array(env, algo_cipher, cipher_cnt), - enif_make_list_from_array(env, algo_mac, mac_cnt), - enif_make_list_from_array(env, algo_curve, curve_cnt), - enif_make_list_from_array(env, algo_rsa_opts, rsa_opts_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">>}] */ - - static const char libname[] = "OpenSSL"; - unsigned name_sz = strlen(libname); - const char* ver = SSLeay_version(SSLEAY_VERSION); - unsigned ver_sz = strlen(ver); - ERL_NIF_TERM name_term, ver_term; - int ver_num = OPENSSL_VERSION_NUMBER; - /* R16: - * Ignore library version number from SSLeay() and instead show header - * version. Otherwise user might try to call a function that is implemented - * by a newer library but not supported by the headers used at compile time. - * Example: DES_ede3_cfb_encrypt in 0.9.7i but not in 0.9.7d. - * - * Version string is still from library though. - */ - - memcpy(enif_make_new_binary(env, name_sz, &name_term), libname, name_sz); - memcpy(enif_make_new_binary(env, ver_sz, &ver_term), ver, ver_sz); - - return enif_make_list1(env, enif_make_tuple3(env, name_term, - enif_make_int(env, ver_num), - ver_term)); -} - -static ERL_NIF_TERM info_fips(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ -#ifdef FIPS_SUPPORT - return FIPS_mode() ? atom_enabled : atom_not_enabled; -#else - return atom_not_supported; -#endif -} - -static ERL_NIF_TERM enable_fips_mode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Boolean) */ - if (argv[0] == atom_true) { -#ifdef FIPS_SUPPORT - if (FIPS_mode_set(1)) { - return atom_true; - } -#endif - PRINTF_ERR0("CRYPTO: Could not setup FIPS mode"); - return atom_false; - } else if (argv[0] == atom_false) { -#ifdef FIPS_SUPPORT - if (!FIPS_mode_set(0)) { - return atom_false; - } -#endif - return atom_true; - } else { - return enif_make_badarg(env); - } -} - - -#if defined(HAVE_EC) -static ERL_NIF_TERM make_badarg_maybe(ErlNifEnv* env) -{ - ERL_NIF_TERM reason; - if (enif_has_pending_exception(env, &reason)) - return reason; /* dummy return value ignored */ - else - return enif_make_badarg(env); -} -#endif - -static ERL_NIF_TERM hash_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type, Data) */ - struct digest_type_t *digp = NULL; - const EVP_MD *md; - ErlNifBinary data; - ERL_NIF_TERM ret; - unsigned ret_size; - - digp = get_digest_type(argv[0]); - if (!digp || - !enif_inspect_iolist_as_binary(env, argv[1], &data)) { - return enif_make_badarg(env); - } - md = digp->md.p; - if (!md) { - return atom_notsup; - } - - ret_size = (unsigned)EVP_MD_size(md); - ASSERT(0 < ret_size && ret_size <= EVP_MAX_MD_SIZE); - if (!EVP_Digest(data.data, data.size, - enif_make_new_binary(env, ret_size, &ret), &ret_size, - md, NULL)) { - return atom_notsup; - } - ASSERT(ret_size == (unsigned)EVP_MD_size(md)); - - CONSUME_REDS(env, data); - return ret; -} - -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) - -static ERL_NIF_TERM hash_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type) */ - struct digest_type_t *digp = NULL; - struct evp_md_ctx *ctx; - ERL_NIF_TERM ret; - - digp = get_digest_type(argv[0]); - if (!digp) { - return enif_make_badarg(env); - } - if (!digp->md.p) { - return atom_notsup; - } - - ctx = enif_alloc_resource(evp_md_ctx_rtype, sizeof(struct evp_md_ctx)); - ctx->ctx = EVP_MD_CTX_new(); - if (!EVP_DigestInit(ctx->ctx, digp->md.p)) { - enif_release_resource(ctx); - return atom_notsup; - } - ret = enif_make_resource(env, ctx); - enif_release_resource(ctx); - return ret; -} -static ERL_NIF_TERM hash_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Context, Data) */ - struct evp_md_ctx *ctx, *new_ctx; - ErlNifBinary data; - ERL_NIF_TERM ret; - - if (!enif_get_resource(env, argv[0], evp_md_ctx_rtype, (void**)&ctx) || - !enif_inspect_iolist_as_binary(env, argv[1], &data)) { - return enif_make_badarg(env); - } - - new_ctx = enif_alloc_resource(evp_md_ctx_rtype, sizeof(struct evp_md_ctx)); - new_ctx->ctx = EVP_MD_CTX_new(); - if (!EVP_MD_CTX_copy(new_ctx->ctx, ctx->ctx) || - !EVP_DigestUpdate(new_ctx->ctx, data.data, data.size)) { - enif_release_resource(new_ctx); - return atom_notsup; - } - - ret = enif_make_resource(env, new_ctx); - enif_release_resource(new_ctx); - CONSUME_REDS(env, data); - return ret; -} -static ERL_NIF_TERM hash_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Context) */ - struct evp_md_ctx *ctx; - EVP_MD_CTX *new_ctx; - ERL_NIF_TERM ret; - unsigned ret_size; - - if (!enif_get_resource(env, argv[0], evp_md_ctx_rtype, (void**)&ctx)) { - return enif_make_badarg(env); - } - - ret_size = (unsigned)EVP_MD_CTX_size(ctx->ctx); - ASSERT(0 < ret_size && ret_size <= EVP_MAX_MD_SIZE); - - new_ctx = EVP_MD_CTX_new(); - if (!EVP_MD_CTX_copy(new_ctx, ctx->ctx) || - !EVP_DigestFinal(new_ctx, - enif_make_new_binary(env, ret_size, &ret), - &ret_size)) { - EVP_MD_CTX_free(new_ctx); - return atom_notsup; - } - EVP_MD_CTX_free(new_ctx); - ASSERT(ret_size == (unsigned)EVP_MD_CTX_size(ctx->ctx)); - - return ret; -} - -#else /* if OPENSSL_VERSION_NUMBER < 1.0 */ - -static ERL_NIF_TERM hash_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type) */ - typedef int (*init_fun)(unsigned char*); - struct digest_type_t *digp = NULL; - ERL_NIF_TERM ctx; - size_t ctx_size = 0; - init_fun ctx_init = 0; - - digp = get_digest_type(argv[0]); - if (!digp) { - return enif_make_badarg(env); - } - if (!digp->md.p) { - return atom_notsup; - } - - switch (EVP_MD_type(digp->md.p)) - { - case NID_md4: - ctx_size = MD4_CTX_LEN; - ctx_init = (init_fun)(&MD4_Init); - break; - case NID_md5: - ctx_size = MD5_CTX_LEN; - ctx_init = (init_fun)(&MD5_Init); - break; - case NID_ripemd160: - ctx_size = RIPEMD160_CTX_LEN; - ctx_init = (init_fun)(&RIPEMD160_Init); - break; - case NID_sha1: - ctx_size = sizeof(SHA_CTX); - ctx_init = (init_fun)(&SHA1_Init); - break; -#ifdef HAVE_SHA224 - case NID_sha224: - ctx_size = sizeof(SHA256_CTX); - ctx_init = (init_fun)(&SHA224_Init); - break; -#endif -#ifdef HAVE_SHA256 - case NID_sha256: - ctx_size = sizeof(SHA256_CTX); - ctx_init = (init_fun)(&SHA256_Init); - break; -#endif -#ifdef HAVE_SHA384 - case NID_sha384: - ctx_size = sizeof(SHA512_CTX); - ctx_init = (init_fun)(&SHA384_Init); - break; -#endif -#ifdef HAVE_SHA512 - case NID_sha512: - ctx_size = sizeof(SHA512_CTX); - ctx_init = (init_fun)(&SHA512_Init); - break; -#endif - default: - return atom_notsup; - } - ASSERT(ctx_size); - ASSERT(ctx_init); - - ctx_init(enif_make_new_binary(env, ctx_size, &ctx)); - return enif_make_tuple2(env, argv[0], ctx); -} -static ERL_NIF_TERM hash_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* ({Type, Context}, Data) */ - typedef int (*update_fun)(unsigned char*, const unsigned char*, size_t); - ERL_NIF_TERM new_ctx; - ErlNifBinary ctx, data; - const ERL_NIF_TERM *tuple; - int arity; - struct digest_type_t *digp = NULL; - unsigned char *ctx_buff; - size_t ctx_size = 0; - update_fun ctx_update = 0; - - if (!enif_get_tuple(env, argv[0], &arity, &tuple) || - arity != 2 || - !(digp = get_digest_type(tuple[0])) || - !enif_inspect_binary(env, tuple[1], &ctx) || - !enif_inspect_iolist_as_binary(env, argv[1], &data)) { - return enif_make_badarg(env); - } - if (!digp->md.p) { - return atom_notsup; - } - - switch (EVP_MD_type(digp->md.p)) - { - case NID_md4: - ctx_size = MD4_CTX_LEN; - ctx_update = (update_fun)(&MD4_Update); - break; - case NID_md5: - ctx_size = MD5_CTX_LEN; - ctx_update = (update_fun)(&MD5_Update); - break; - case NID_ripemd160: - ctx_size = RIPEMD160_CTX_LEN; - ctx_update = (update_fun)(&RIPEMD160_Update); - break; - case NID_sha1: - ctx_size = sizeof(SHA_CTX); - ctx_update = (update_fun)(&SHA1_Update); - break; -#ifdef HAVE_SHA224 - case NID_sha224: - ctx_size = sizeof(SHA256_CTX); - ctx_update = (update_fun)(&SHA224_Update); - break; -#endif -#ifdef HAVE_SHA256 - case NID_sha256: - ctx_size = sizeof(SHA256_CTX); - ctx_update = (update_fun)(&SHA256_Update); - break; -#endif -#ifdef HAVE_SHA384 - case NID_sha384: - ctx_size = sizeof(SHA512_CTX); - ctx_update = (update_fun)(&SHA384_Update); - break; -#endif -#ifdef HAVE_SHA512 - case NID_sha512: - ctx_size = sizeof(SHA512_CTX); - ctx_update = (update_fun)(&SHA512_Update); - break; -#endif - default: - return atom_notsup; - } - ASSERT(ctx_size); - ASSERT(ctx_update); - - if (ctx.size != ctx_size) { - return enif_make_badarg(env); - } - - ctx_buff = enif_make_new_binary(env, ctx_size, &new_ctx); - memcpy(ctx_buff, ctx.data, ctx_size); - ctx_update(ctx_buff, data.data, data.size); - - CONSUME_REDS(env, data); - return enif_make_tuple2(env, tuple[0], new_ctx); -} -static ERL_NIF_TERM hash_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* ({Type, Context}) */ - typedef int (*final_fun)(unsigned char*, void*); - ERL_NIF_TERM ret; - ErlNifBinary ctx; - const ERL_NIF_TERM *tuple; - int arity; - struct digest_type_t *digp = NULL; - const EVP_MD *md; - void *new_ctx; - size_t ctx_size = 0; - final_fun ctx_final = 0; - - if (!enif_get_tuple(env, argv[0], &arity, &tuple) || - arity != 2 || - !(digp = get_digest_type(tuple[0])) || - !enif_inspect_binary(env, tuple[1], &ctx)) { - return enif_make_badarg(env); - } - md = digp->md.p; - if (!md) { - return atom_notsup; - } - - - switch (EVP_MD_type(md)) - { - case NID_md4: - ctx_size = MD4_CTX_LEN; - ctx_final = (final_fun)(&MD4_Final); - break; - case NID_md5: - ctx_size = MD5_CTX_LEN; - ctx_final = (final_fun)(&MD5_Final); - break; - case NID_ripemd160: - ctx_size = RIPEMD160_CTX_LEN; - ctx_final = (final_fun)(&RIPEMD160_Final); - break; - case NID_sha1: - ctx_size = sizeof(SHA_CTX); - ctx_final = (final_fun)(&SHA1_Final); - break; -#ifdef HAVE_SHA224 - case NID_sha224: - ctx_size = sizeof(SHA256_CTX); - ctx_final = (final_fun)(&SHA224_Final); - break; -#endif -#ifdef HAVE_SHA256 - case NID_sha256: - ctx_size = sizeof(SHA256_CTX); - ctx_final = (final_fun)(&SHA256_Final); - break; -#endif -#ifdef HAVE_SHA384 - case NID_sha384: - ctx_size = sizeof(SHA512_CTX); - ctx_final = (final_fun)(&SHA384_Final); - break; -#endif -#ifdef HAVE_SHA512 - case NID_sha512: - ctx_size = sizeof(SHA512_CTX); - ctx_final = (final_fun)(&SHA512_Final); - break; -#endif - default: - return atom_notsup; - } - ASSERT(ctx_size); - ASSERT(ctx_final); - - if (ctx.size != ctx_size) { - return enif_make_badarg(env); - } - - new_ctx = enif_alloc(ctx_size); - memcpy(new_ctx, ctx.data, ctx_size); - ctx_final(enif_make_new_binary(env, (size_t)EVP_MD_size(md), &ret), - new_ctx); - enif_free(new_ctx); - - return ret; -} -#endif /* OPENSSL_VERSION_NUMBER < 1.0 */ - - -static ERL_NIF_TERM hmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type, Key, Data) or (Type, Key, Data, MacSize) */ - struct digest_type_t *digp = NULL; - ErlNifBinary key, data; - unsigned char buff[EVP_MAX_MD_SIZE]; - unsigned size = 0, req_size = 0; - ERL_NIF_TERM ret; - - digp = get_digest_type(argv[0]); - if (!digp || - !enif_inspect_iolist_as_binary(env, argv[1], &key) || - !enif_inspect_iolist_as_binary(env, argv[2], &data) || - (argc == 4 && !enif_get_uint(env, argv[3], &req_size))) { - return enif_make_badarg(env); - } - - if (!digp->md.p || - !HMAC(digp->md.p, - key.data, key.size, - data.data, data.size, - buff, &size)) { - return atom_notsup; - } - ASSERT(0 < size && size <= EVP_MAX_MD_SIZE); - CONSUME_REDS(env, data); - - if (argc == 4) { - if (req_size <= size) { - size = req_size; - } - else { - return enif_make_badarg(env); - } - } - memcpy(enif_make_new_binary(env, size, &ret), buff, size); - return ret; -} - -static void hmac_context_dtor(ErlNifEnv* env, struct hmac_context *obj) -{ - if (obj->alive) { - HMAC_CTX_free(obj->ctx); - obj->alive = 0; - } - enif_mutex_destroy(obj->mtx); -} - -static ERL_NIF_TERM hmac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type, Key) */ - struct digest_type_t *digp = NULL; - ErlNifBinary key; - ERL_NIF_TERM ret; - struct hmac_context *obj; - - digp = get_digest_type(argv[0]); - if (!digp || - !enif_inspect_iolist_as_binary(env, argv[1], &key)) { - return enif_make_badarg(env); - } - if (!digp->md.p) { - return atom_notsup; - } - - obj = enif_alloc_resource(hmac_context_rtype, sizeof(struct hmac_context)); - obj->mtx = enif_mutex_create("crypto.hmac"); - obj->alive = 1; - obj->ctx = HMAC_CTX_new(); -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) - // Check the return value of HMAC_Init: it may fail in FIPS mode - // for disabled algorithms - if (!HMAC_Init_ex(obj->ctx, key.data, key.size, digp->md.p, NULL)) { - enif_release_resource(obj); - return atom_notsup; - } -#else - HMAC_Init_ex(obj->ctx, key.data, key.size, digp->md.p, NULL); -#endif - - ret = enif_make_resource(env, obj); - enif_release_resource(obj); - return ret; -} - -static ERL_NIF_TERM hmac_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Context, Data) */ - ErlNifBinary data; - struct hmac_context* obj; - - if (!enif_get_resource(env, argv[0], hmac_context_rtype, (void**)&obj) - || !enif_inspect_iolist_as_binary(env, argv[1], &data)) { - return enif_make_badarg(env); - } - enif_mutex_lock(obj->mtx); - if (!obj->alive) { - enif_mutex_unlock(obj->mtx); - return enif_make_badarg(env); - } - HMAC_Update(obj->ctx, data.data, data.size); - enif_mutex_unlock(obj->mtx); - - CONSUME_REDS(env,data); - return argv[0]; -} - -static ERL_NIF_TERM hmac_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Context) or (Context, HashLen) */ - ERL_NIF_TERM ret; - struct hmac_context* obj; - unsigned char mac_buf[EVP_MAX_MD_SIZE]; - unsigned char * mac_bin; - unsigned int req_len = 0; - unsigned int mac_len; - - if (!enif_get_resource(env,argv[0],hmac_context_rtype, (void**)&obj) - || (argc == 2 && !enif_get_uint(env, argv[1], &req_len))) { - return enif_make_badarg(env); - } - - enif_mutex_lock(obj->mtx); - if (!obj->alive) { - enif_mutex_unlock(obj->mtx); - return enif_make_badarg(env); - } - - HMAC_Final(obj->ctx, mac_buf, &mac_len); - HMAC_CTX_free(obj->ctx); - obj->alive = 0; - enif_mutex_unlock(obj->mtx); - - if (argc == 2 && req_len < mac_len) { - /* Only truncate to req_len bytes if asked. */ - mac_len = req_len; - } - mac_bin = enif_make_new_binary(env, mac_len, &ret); - memcpy(mac_bin, mac_buf, mac_len); - - return ret; -} - -static ERL_NIF_TERM cmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type, Key, Data) */ -#if defined(HAVE_CMAC) - struct cipher_type_t *cipherp = NULL; - const EVP_CIPHER *cipher; - CMAC_CTX *ctx; - ErlNifBinary key; - ErlNifBinary data; - ERL_NIF_TERM ret; - size_t ret_size; - - if (!enif_inspect_iolist_as_binary(env, argv[1], &key) - || !(cipherp = get_cipher_type(argv[0], key.size)) - || !enif_inspect_iolist_as_binary(env, argv[2], &data)) { - return enif_make_badarg(env); - } - cipher = cipherp->cipher.p; - if (!cipher) { - return enif_raise_exception(env, atom_notsup); - } - - ctx = CMAC_CTX_new(); - if (!CMAC_Init(ctx, key.data, key.size, cipher, NULL)) { - CMAC_CTX_free(ctx); - return atom_notsup; - } - - if (!CMAC_Update(ctx, data.data, data.size) || - !CMAC_Final(ctx, - enif_make_new_binary(env, EVP_CIPHER_block_size(cipher), &ret), - &ret_size)) { - CMAC_CTX_free(ctx); - return atom_notsup; - } - ASSERT(ret_size == (unsigned)EVP_CIPHER_block_size(cipher)); - - CMAC_CTX_free(ctx); - CONSUME_REDS(env, data); - return ret; -#else - /* The CMAC functionality was introduced in OpenSSL 1.0.1 - * Although OTP requires at least version 0.9.8, the versions 0.9.8 and 1.0.0 are - * no longer maintained. */ - return atom_notsup; -#endif -} - -/* For OpenSSL >= 1.1.1 the hmac_nif and cmac_nif could be integrated into poly1305 (with 'type' as parameter) */ -static ERL_NIF_TERM poly1305_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Key, Text) */ -#ifdef HAVE_POLY1305 - ErlNifBinary key_bin, text, ret_bin; - ERL_NIF_TERM ret = atom_error; - EVP_PKEY *key = NULL; - EVP_MD_CTX *mctx = NULL; - EVP_PKEY_CTX *pctx = NULL; - const EVP_MD *md = NULL; - size_t size; - int type; - - type = EVP_PKEY_POLY1305; - - if (!enif_inspect_binary(env, argv[0], &key_bin) || - !(key_bin.size == 32) ) { - return enif_make_badarg(env); - } - - if (!enif_inspect_binary(env, argv[1], &text) ) { - return enif_make_badarg(env); - } - - key = EVP_PKEY_new_raw_private_key(type, /*engine*/ NULL, key_bin.data, key_bin.size); - - if (!key || - !(mctx = EVP_MD_CTX_new()) || - !EVP_DigestSignInit(mctx, &pctx, md, /*engine*/ NULL, key) || - !EVP_DigestSignUpdate(mctx, text.data, text.size)) { - goto err; - } - - if (!EVP_DigestSignFinal(mctx, NULL, &size) || - !enif_alloc_binary(size, &ret_bin) || - !EVP_DigestSignFinal(mctx, ret_bin.data, &size)) { - goto err; - } - - if ((size != ret_bin.size) && - !enif_realloc_binary(&ret_bin, size)) { - goto err; - } - - ret = enif_make_binary(env, &ret_bin); - - err: - EVP_MD_CTX_free(mctx); - EVP_PKEY_free(key); - return ret; - -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM block_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type, Key, Ivec, Text, IsEncrypt) or (Type, Key, Text, IsEncrypt) */ - struct cipher_type_t *cipherp = NULL; - const EVP_CIPHER *cipher; - ErlNifBinary key, ivec, text; - EVP_CIPHER_CTX* ctx; - ERL_NIF_TERM ret; - unsigned char *out; - int ivec_size, out_size = 0; - - if (!enif_inspect_iolist_as_binary(env, argv[1], &key) - || !(cipherp = get_cipher_type(argv[0], key.size)) - || !enif_inspect_iolist_as_binary(env, argv[argc - 2], &text)) { - return enif_make_badarg(env); - } - cipher = cipherp->cipher.p; - if (!cipher) { - return enif_raise_exception(env, atom_notsup); - } - - if (argv[0] == atom_aes_cfb8 - && (key.size == 24 || key.size == 32)) { - /* Why do EVP_CIPHER_CTX_set_key_length() fail on these key sizes? - * Fall back on low level API - */ - return aes_cfb_8_crypt(env, argc-1, argv+1); - } - else if (argv[0] == atom_aes_cfb128 - && (key.size == 24 || key.size == 32)) { - /* Why do EVP_CIPHER_CTX_set_key_length() fail on these key sizes? - * Fall back on low level API - */ - return aes_cfb_128_crypt_nif(env, argc-1, argv+1); - } - - ivec_size = EVP_CIPHER_iv_length(cipher); - -#ifdef HAVE_ECB_IVEC_BUG - if (argv[0] == atom_aes_ecb || argv[0] == atom_blowfish_ecb || - argv[0] == atom_des_ecb) - ivec_size = 0; /* 0.9.8l returns faulty ivec_size */ -#endif - - if (text.size % EVP_CIPHER_block_size(cipher) != 0 || - (ivec_size == 0 ? argc != 4 - : (argc != 5 || - !enif_inspect_iolist_as_binary(env, argv[2], &ivec) || - ivec.size != ivec_size))) { - return enif_make_badarg(env); - } - - out = enif_make_new_binary(env, text.size, &ret); - - ctx = EVP_CIPHER_CTX_new(); - if (!EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, - (argv[argc - 1] == atom_true)) || - !EVP_CIPHER_CTX_set_key_length(ctx, key.size) || - !(EVP_CIPHER_type(cipher) != NID_rc2_cbc || - EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_SET_RC2_KEY_BITS, key.size * 8, NULL)) || - !EVP_CipherInit_ex(ctx, NULL, NULL, - key.data, ivec_size ? ivec.data : NULL, -1) || - !EVP_CIPHER_CTX_set_padding(ctx, 0)) { - - EVP_CIPHER_CTX_free(ctx); - return enif_raise_exception(env, atom_notsup); - } - - if (text.size > 0 && /* OpenSSL 0.9.8h asserts text.size > 0 */ - (!EVP_CipherUpdate(ctx, out, &out_size, text.data, text.size) - || (ASSERT(out_size == text.size), 0) - || !EVP_CipherFinal_ex(ctx, out + out_size, &out_size))) { - - EVP_CIPHER_CTX_free(ctx); - return enif_raise_exception(env, atom_notsup); - } - ASSERT(out_size == 0); - EVP_CIPHER_CTX_free(ctx); - CONSUME_REDS(env, text); - - return ret; -} - -static ERL_NIF_TERM aes_cfb_8_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Key, IVec, Data, IsEncrypt) */ - ErlNifBinary key, ivec, text; - AES_KEY aes_key; - unsigned char ivec_clone[16]; /* writable copy */ - int new_ivlen = 0; - ERL_NIF_TERM ret; - - CHECK_NO_FIPS_MODE(); - - if (!enif_inspect_iolist_as_binary(env, argv[0], &key) - || !(key.size == 16 || key.size == 24 || key.size == 32) - || !enif_inspect_binary(env, argv[1], &ivec) || ivec.size != 16 - || !enif_inspect_iolist_as_binary(env, argv[2], &text)) { - return enif_make_badarg(env); - } - - memcpy(ivec_clone, ivec.data, 16); - AES_set_encrypt_key(key.data, key.size * 8, &aes_key); - AES_cfb8_encrypt((unsigned char *) text.data, - enif_make_new_binary(env, text.size, &ret), - text.size, &aes_key, ivec_clone, &new_ivlen, - (argv[3] == atom_true)); - CONSUME_REDS(env,text); - return ret; -} - -static ERL_NIF_TERM aes_cfb_128_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Key, IVec, Data, IsEncrypt) */ - ErlNifBinary key, ivec, text; - AES_KEY aes_key; - unsigned char ivec_clone[16]; /* writable copy */ - int new_ivlen = 0; - ERL_NIF_TERM ret; - - if (!enif_inspect_iolist_as_binary(env, argv[0], &key) - || !(key.size == 16 || key.size == 24 || key.size == 32) - || !enif_inspect_binary(env, argv[1], &ivec) || ivec.size != 16 - || !enif_inspect_iolist_as_binary(env, argv[2], &text)) { - return enif_make_badarg(env); - } - - memcpy(ivec_clone, ivec.data, 16); - AES_set_encrypt_key(key.data, key.size * 8, &aes_key); - AES_cfb128_encrypt((unsigned char *) text.data, - enif_make_new_binary(env, text.size, &ret), - text.size, &aes_key, ivec_clone, &new_ivlen, - (argv[3] == atom_true)); - CONSUME_REDS(env,text); - return ret; -} - -static ERL_NIF_TERM aes_ige_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Key, IVec, Data, IsEncrypt) */ -#ifdef HAVE_AES_IGE - ErlNifBinary key_bin, ivec_bin, data_bin; - AES_KEY aes_key; - unsigned char ivec[32]; - int i; - unsigned char* ret_ptr; - ERL_NIF_TERM ret; - - CHECK_NO_FIPS_MODE(); - - if (!enif_inspect_iolist_as_binary(env, argv[0], &key_bin) - || (key_bin.size != 16 && key_bin.size != 32) - || !enif_inspect_binary(env, argv[1], &ivec_bin) - || ivec_bin.size != 32 - || !enif_inspect_iolist_as_binary(env, argv[2], &data_bin) - || data_bin.size % 16 != 0) { - - return enif_make_badarg(env); - } - - if (argv[3] == atom_true) { - i = AES_ENCRYPT; - AES_set_encrypt_key(key_bin.data, key_bin.size*8, &aes_key); - } - else { - i = AES_DECRYPT; - AES_set_decrypt_key(key_bin.data, key_bin.size*8, &aes_key); - } - - ret_ptr = enif_make_new_binary(env, data_bin.size, &ret); - memcpy(ivec, ivec_bin.data, 32); /* writable copy */ - AES_ige_encrypt(data_bin.data, ret_ptr, data_bin.size, &aes_key, ivec, i); - CONSUME_REDS(env,data_bin); - return ret; -#else - return atom_notsup; -#endif -} - - -/* Initializes state for ctr streaming (de)encryption -*/ -#ifdef HAVE_EVP_AES_CTR -static ERL_NIF_TERM aes_ctr_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Key, IVec) */ - ErlNifBinary key_bin, ivec_bin; - struct evp_cipher_ctx *ctx; - const EVP_CIPHER *cipher; - ERL_NIF_TERM ret; - - if (!enif_inspect_iolist_as_binary(env, argv[0], &key_bin) - || !enif_inspect_binary(env, argv[1], &ivec_bin) - || ivec_bin.size != 16) { - return enif_make_badarg(env); - } - - switch (key_bin.size) - { - case 16: cipher = EVP_aes_128_ctr(); break; - case 24: cipher = EVP_aes_192_ctr(); break; - case 32: cipher = EVP_aes_256_ctr(); break; - default: return enif_make_badarg(env); - } - - ctx = enif_alloc_resource(evp_cipher_ctx_rtype, sizeof(struct evp_cipher_ctx)); - ctx->ctx = EVP_CIPHER_CTX_new(); - EVP_CipherInit_ex(ctx->ctx, cipher, NULL, - key_bin.data, ivec_bin.data, 1); - EVP_CIPHER_CTX_set_padding(ctx->ctx, 0); - ret = enif_make_resource(env, ctx); - enif_release_resource(ctx); - return ret; -} -static ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Context, Data) */ - struct evp_cipher_ctx *ctx, *new_ctx; - ErlNifBinary data_bin; - ERL_NIF_TERM ret, cipher_term; - unsigned char *out; - int outl = 0; - - if (!enif_get_resource(env, argv[0], evp_cipher_ctx_rtype, (void**)&ctx) - || !enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) { - return enif_make_badarg(env); - } - new_ctx = enif_alloc_resource(evp_cipher_ctx_rtype, sizeof(struct evp_cipher_ctx)); - new_ctx->ctx = EVP_CIPHER_CTX_new(); - EVP_CIPHER_CTX_copy(new_ctx->ctx, ctx->ctx); - out = enif_make_new_binary(env, data_bin.size, &cipher_term); - EVP_CipherUpdate(new_ctx->ctx, out, &outl, data_bin.data, data_bin.size); - ASSERT(outl == data_bin.size); - - ret = enif_make_tuple2(env, enif_make_resource(env, new_ctx), cipher_term); - enif_release_resource(new_ctx); - CONSUME_REDS(env,data_bin); - return ret; -} - -#else /* if not HAVE_EVP_AES_CTR */ - -static ERL_NIF_TERM aes_ctr_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Key, IVec) */ - ErlNifBinary key_bin, ivec_bin; - ERL_NIF_TERM ecount_bin; - - if (!enif_inspect_iolist_as_binary(env, argv[0], &key_bin) - || !enif_inspect_binary(env, argv[1], &ivec_bin) - || !(key_bin.size == 16 || key_bin.size == 24 || key_bin.size ==32) - || ivec_bin.size != 16) { - return enif_make_badarg(env); - } - - memset(enif_make_new_binary(env, AES_BLOCK_SIZE, &ecount_bin), - 0, AES_BLOCK_SIZE); - return enif_make_tuple4(env, argv[0], argv[1], ecount_bin, enif_make_int(env, 0)); -} - -static ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* ({Key, IVec, ECount, Num}, Data) */ - ErlNifBinary key_bin, ivec_bin, text_bin, ecount_bin; - AES_KEY aes_key; - unsigned int num; - ERL_NIF_TERM ret, num2_term, cipher_term, ivec2_term, ecount2_term, new_state_term; - int state_arity; - const ERL_NIF_TERM *state_term; - unsigned char * ivec2_buf; - unsigned char * ecount2_buf; - - if (!enif_get_tuple(env, argv[0], &state_arity, &state_term) - || state_arity != 4 - || !enif_inspect_iolist_as_binary(env, state_term[0], &key_bin) - || AES_set_encrypt_key(key_bin.data, key_bin.size*8, &aes_key) != 0 - || !enif_inspect_binary(env, state_term[1], &ivec_bin) || ivec_bin.size != 16 - || !enif_inspect_binary(env, state_term[2], &ecount_bin) || ecount_bin.size != AES_BLOCK_SIZE - || !enif_get_uint(env, state_term[3], &num) - || !enif_inspect_iolist_as_binary(env, argv[1], &text_bin)) { - return enif_make_badarg(env); - } - - ivec2_buf = enif_make_new_binary(env, ivec_bin.size, &ivec2_term); - ecount2_buf = enif_make_new_binary(env, ecount_bin.size, &ecount2_term); - - memcpy(ivec2_buf, ivec_bin.data, 16); - memcpy(ecount2_buf, ecount_bin.data, ecount_bin.size); - - AES_ctr128_encrypt((unsigned char *) text_bin.data, - enif_make_new_binary(env, text_bin.size, &cipher_term), - text_bin.size, &aes_key, ivec2_buf, ecount2_buf, &num); - - num2_term = enif_make_uint(env, num); - new_state_term = enif_make_tuple4(env, state_term[0], ivec2_term, ecount2_term, num2_term); - ret = enif_make_tuple2(env, new_state_term, cipher_term); - CONSUME_REDS(env,text_bin); - return ret; -} -#endif /* !HAVE_EVP_AES_CTR */ - -static ERL_NIF_TERM aead_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type,Key,Iv,AAD,In) */ -#if defined(HAVE_AEAD) - EVP_CIPHER_CTX *ctx; - const EVP_CIPHER *cipher = NULL; - ErlNifBinary key, iv, aad, in; - unsigned int tag_len; - unsigned char *outp, *tagp; - ERL_NIF_TERM type, out, out_tag; - int len, ctx_ctrl_set_ivlen, ctx_ctrl_get_tag; - - type = argv[0]; - - if (!enif_is_atom(env, type) - || !enif_inspect_iolist_as_binary(env, argv[1], &key) - || !enif_inspect_binary(env, argv[2], &iv) - || !enif_inspect_iolist_as_binary(env, argv[3], &aad) - || !enif_inspect_iolist_as_binary(env, argv[4], &in) - || !enif_get_uint(env, argv[5], &tag_len)) { - return enif_make_badarg(env); - } - - /* Use cipher_type some day. Must check block_encrypt|decrypt first */ -#if defined(HAVE_GCM) - if (type == atom_aes_gcm) { - if ((iv.size > 0) - && (1 <= tag_len && tag_len <= 16)) { - ctx_ctrl_set_ivlen = EVP_CTRL_GCM_SET_IVLEN; - ctx_ctrl_get_tag = EVP_CTRL_GCM_GET_TAG; - if (key.size == 16) cipher = EVP_aes_128_gcm(); - else if (key.size == 24) cipher = EVP_aes_192_gcm(); - else if (key.size == 32) cipher = EVP_aes_256_gcm(); - else enif_make_badarg(env); - } else - enif_make_badarg(env); - } else -#endif -#if defined(HAVE_CCM) - if (type == atom_aes_ccm) { - if ((7 <= iv.size && iv.size <= 13) - && (4 <= tag_len && tag_len <= 16) - && ((tag_len & 1) == 0) - ) { - ctx_ctrl_set_ivlen = EVP_CTRL_CCM_SET_IVLEN; - ctx_ctrl_get_tag = EVP_CTRL_CCM_GET_TAG; - if (key.size == 16) cipher = EVP_aes_128_ccm(); - else if (key.size == 24) cipher = EVP_aes_192_ccm(); - else if (key.size == 32) cipher = EVP_aes_256_ccm(); - else enif_make_badarg(env); - } else - enif_make_badarg(env); - } else -#endif -#if defined(HAVE_CHACHA20_POLY1305) - if (type == atom_chacha20_poly1305) { - if ((key.size == 32) - && (1 <= iv.size && iv.size <= 16) - && (tag_len == 16) - ) { - ctx_ctrl_set_ivlen = EVP_CTRL_AEAD_SET_IVLEN; - ctx_ctrl_get_tag = EVP_CTRL_AEAD_GET_TAG, - cipher = EVP_chacha20_poly1305(); - } else enif_make_badarg(env); - } else -#endif - return enif_raise_exception(env, atom_notsup); - - ctx = EVP_CIPHER_CTX_new(); - if (EVP_EncryptInit_ex(ctx, cipher, NULL, NULL, NULL) != 1) goto out_err; - if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_set_ivlen, iv.size, NULL) != 1) goto out_err; - -#if defined(HAVE_CCM) - if (type == atom_aes_ccm) { - if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, tag_len, NULL) != 1) goto out_err; - if (EVP_EncryptInit_ex(ctx, NULL, NULL, key.data, iv.data) != 1) goto out_err; - if (EVP_EncryptUpdate(ctx, NULL, &len, NULL, in.size) != 1) goto out_err; - } else -#endif - if (EVP_EncryptInit_ex(ctx, NULL, NULL, key.data, iv.data) != 1) goto out_err; - - if (EVP_EncryptUpdate(ctx, NULL, &len, aad.data, aad.size) != 1) goto out_err; - - outp = enif_make_new_binary(env, in.size, &out); - - if (EVP_EncryptUpdate(ctx, outp, &len, in.data, in.size) != 1) goto out_err; - if (EVP_EncryptFinal_ex(ctx, outp/*+len*/, &len) != 1) goto out_err; - - tagp = enif_make_new_binary(env, tag_len, &out_tag); - - if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_get_tag, tag_len, tagp) != 1) goto out_err; - - EVP_CIPHER_CTX_free(ctx); - CONSUME_REDS(env, in); - return enif_make_tuple2(env, out, out_tag); - -out_err: - EVP_CIPHER_CTX_free(ctx); - return atom_error; - -#else - return enif_raise_exception(env, atom_notsup); -#endif -} - -static ERL_NIF_TERM aead_decrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type,Key,Iv,AAD,In,Tag) */ -#if defined(HAVE_AEAD) - EVP_CIPHER_CTX *ctx; - const EVP_CIPHER *cipher = NULL; - ErlNifBinary key, iv, aad, in, tag; - unsigned char *outp; - ERL_NIF_TERM type, out; - int len, ctx_ctrl_set_ivlen, ctx_ctrl_set_tag; - - type = argv[0]; -#if defined(HAVE_GCM_EVP_DECRYPT_BUG) - if (type == atom_aes_gcm) - return aes_gcm_decrypt_NO_EVP(env, argc, argv); -#endif - - if (!enif_is_atom(env, type) - || !enif_inspect_iolist_as_binary(env, argv[1], &key) - || !enif_inspect_binary(env, argv[2], &iv) - || !enif_inspect_iolist_as_binary(env, argv[3], &aad) - || !enif_inspect_iolist_as_binary(env, argv[4], &in) - || !enif_inspect_iolist_as_binary(env, argv[5], &tag)) { - return enif_make_badarg(env); - } - - /* Use cipher_type some day. Must check block_encrypt|decrypt first */ -#if defined(HAVE_GCM) - if (type == atom_aes_gcm) { - if (iv.size > 0) { - ctx_ctrl_set_ivlen = EVP_CTRL_GCM_SET_IVLEN; - ctx_ctrl_set_tag = EVP_CTRL_GCM_SET_TAG; - if (key.size == 16) cipher = EVP_aes_128_gcm(); - else if (key.size == 24) cipher = EVP_aes_192_gcm(); - else if (key.size == 32) cipher = EVP_aes_256_gcm(); - else enif_make_badarg(env); - } else - enif_make_badarg(env); - } else -#endif -#if defined(HAVE_CCM) - if (type == atom_aes_ccm) { - if (iv.size > 0) { - ctx_ctrl_set_ivlen = EVP_CTRL_CCM_SET_IVLEN; - if (key.size == 16) cipher = EVP_aes_128_ccm(); - else if (key.size == 24) cipher = EVP_aes_192_ccm(); - else if (key.size == 32) cipher = EVP_aes_256_ccm(); - else enif_make_badarg(env); - } else - enif_make_badarg(env); - } else -#endif -#if defined(HAVE_CHACHA20_POLY1305) - if (type == atom_chacha20_poly1305) { - if ((key.size == 32) - && (1 <= iv.size && iv.size <= 16) - && tag.size == 16 - ) { - ctx_ctrl_set_ivlen = EVP_CTRL_AEAD_SET_IVLEN; - ctx_ctrl_set_tag = EVP_CTRL_AEAD_SET_TAG; - cipher = EVP_chacha20_poly1305(); - } else enif_make_badarg(env); - } else -#endif - return enif_raise_exception(env, atom_notsup); - - outp = enif_make_new_binary(env, in.size, &out); - - ctx = EVP_CIPHER_CTX_new(); - if (EVP_DecryptInit_ex(ctx, cipher, NULL, NULL, NULL) != 1) goto out_err; - if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_set_ivlen, iv.size, NULL) != 1) goto out_err; - -#if defined(HAVE_CCM) - if (type == atom_aes_ccm) { - if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, tag.size, tag.data) != 1) goto out_err; - } -#endif - - if (EVP_DecryptInit_ex(ctx, NULL, NULL, key.data, iv.data) != 1) goto out_err; - -#if defined(HAVE_CCM) - if (type == atom_aes_ccm) { - if (1 != EVP_DecryptUpdate(ctx, NULL, &len, NULL, in.size)) goto out_err; - } -#endif - - if (EVP_DecryptUpdate(ctx, NULL, &len, aad.data, aad.size) != 1) goto out_err; - if (EVP_DecryptUpdate(ctx, outp, &len, in.data, in.size) != 1) goto out_err; - -#if defined(HAVE_GCM) || defined(HAVE_CHACHA20_POLY1305) - if (type == atom_aes_gcm) { - if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_set_tag, tag.size, tag.data) != 1) goto out_err; - if (EVP_DecryptFinal_ex(ctx, outp+len, &len) != 1) goto out_err; - } -#endif - EVP_CIPHER_CTX_free(ctx); - - CONSUME_REDS(env, in); - return out; - -out_err: - EVP_CIPHER_CTX_free(ctx); - return atom_error; -#else - return enif_raise_exception(env, atom_notsup); -#endif -} - -#ifdef HAVE_GCM_EVP_DECRYPT_BUG -static ERL_NIF_TERM aes_gcm_decrypt_NO_EVP(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type,Key,Iv,AAD,In,Tag) */ - GCM128_CONTEXT *ctx; - ErlNifBinary key, iv, aad, in, tag; - AES_KEY aes_key; - unsigned char *outp; - ERL_NIF_TERM out; - - if (!enif_inspect_iolist_as_binary(env, argv[1], &key) - || AES_set_encrypt_key(key.data, key.size*8, &aes_key) != 0 - || !enif_inspect_binary(env, argv[2], &iv) || iv.size == 0 - || !enif_inspect_iolist_as_binary(env, argv[3], &aad) - || !enif_inspect_iolist_as_binary(env, argv[4], &in) - || !enif_inspect_iolist_as_binary(env, argv[5], &tag)) { - return enif_make_badarg(env); - } - - if (!(ctx = CRYPTO_gcm128_new(&aes_key, (block128_f)AES_encrypt))) - return atom_error; - - CRYPTO_gcm128_setiv(ctx, iv.data, iv.size); - - if (CRYPTO_gcm128_aad(ctx, aad.data, aad.size)) - goto out_err; - - outp = enif_make_new_binary(env, in.size, &out); - - /* decrypt */ - if (CRYPTO_gcm128_decrypt(ctx, in.data, outp, in.size)) - goto out_err; - - /* calculate and check the tag */ - if (CRYPTO_gcm128_finish(ctx, tag.data, tag.size)) - goto out_err; - - CRYPTO_gcm128_release(ctx); - CONSUME_REDS(env, in); - - return out; - -out_err: - CRYPTO_gcm128_release(ctx); - return atom_error; -} -#endif /* HAVE_GCM_EVP_DECRYPT_BUG */ - - -static ERL_NIF_TERM chacha20_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Key, IV) */ -#if defined(HAVE_CHACHA20) - ErlNifBinary key_bin, ivec_bin; - struct evp_cipher_ctx *ctx; - const EVP_CIPHER *cipher; - ERL_NIF_TERM ret; - - if (!enif_inspect_iolist_as_binary(env, argv[0], &key_bin) - || !enif_inspect_binary(env, argv[1], &ivec_bin) - || key_bin.size != 32 - || ivec_bin.size != 16) { - return enif_make_badarg(env); - } - - cipher = EVP_chacha20(); - - ctx = enif_alloc_resource(evp_cipher_ctx_rtype, sizeof(struct evp_cipher_ctx)); - ctx->ctx = EVP_CIPHER_CTX_new(); - - - EVP_CipherInit_ex(ctx->ctx, cipher, NULL, - key_bin.data, ivec_bin.data, 1); - EVP_CIPHER_CTX_set_padding(ctx->ctx, 0); - ret = enif_make_resource(env, ctx); - enif_release_resource(ctx); - return ret; -#else - return enif_raise_exception(env, atom_notsup); -#endif -}; - -static ERL_NIF_TERM chacha20_stream_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (State, Data) */ -#if defined(HAVE_CHACHA20) - struct evp_cipher_ctx *ctx, *new_ctx; - ErlNifBinary data_bin; - ERL_NIF_TERM ret, cipher_term; - unsigned char *out; - int outl = 0; - - if (!enif_get_resource(env, argv[0], evp_cipher_ctx_rtype, (void**)&ctx) - || !enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) { - return enif_make_badarg(env); - } - new_ctx = enif_alloc_resource(evp_cipher_ctx_rtype, sizeof(struct evp_cipher_ctx)); - new_ctx->ctx = EVP_CIPHER_CTX_new(); - EVP_CIPHER_CTX_copy(new_ctx->ctx, ctx->ctx); - out = enif_make_new_binary(env, data_bin.size, &cipher_term); - EVP_CipherUpdate(new_ctx->ctx, out, &outl, data_bin.data, data_bin.size); - ASSERT(outl == data_bin.size); - - ret = enif_make_tuple2(env, enif_make_resource(env, new_ctx), cipher_term); - enif_release_resource(new_ctx); - CONSUME_REDS(env,data_bin); - return ret; -#else - return enif_raise_exception(env, atom_notsup); -#endif -}; - - -static ERL_NIF_TERM strong_rand_bytes_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Bytes) */ - unsigned bytes; - unsigned char* data; - ERL_NIF_TERM ret; - - if (!enif_get_uint(env, argv[0], &bytes)) { - return enif_make_badarg(env); - } - data = enif_make_new_binary(env, bytes, &ret); - if ( RAND_bytes(data, bytes) != 1) { - return atom_false; - } - ERL_VALGRIND_MAKE_MEM_DEFINED(data, bytes); - return ret; -} - - -static int get_bn_from_mpint(ErlNifEnv* env, ERL_NIF_TERM term, BIGNUM** bnp) -{ - ErlNifBinary bin; - int sz; - if (!enif_inspect_binary(env,term,&bin)) { - return 0; - } - ERL_VALGRIND_ASSERT_MEM_DEFINED(bin.data, bin.size); - sz = bin.size - 4; - if (sz < 0 || get_int32(bin.data) != sz) { - return 0; - } - *bnp = BN_bin2bn(bin.data+4, sz, NULL); - 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 bin_from_bn(ErlNifEnv* env, const BIGNUM *bn) -{ - int bn_len; - unsigned char *bin_ptr; - ERL_NIF_TERM term; - - /* Copy the bignum into an erlang binary. */ - bn_len = BN_num_bytes(bn); - bin_ptr = enif_make_new_binary(env, bn_len, &term); - BN_bn2bin(bn, bin_ptr); - - return term; -} - -static ERL_NIF_TERM strong_rand_range_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Range) */ - BIGNUM *bn_range, *bn_rand; - ERL_NIF_TERM ret; - - if(!get_bn_from_bin(env, argv[0], &bn_range)) { - return enif_make_badarg(env); - } - - bn_rand = BN_new(); - if (BN_rand_range(bn_rand, bn_range) != 1) { - ret = atom_false; - } - else { - ret = bin_from_bn(env, bn_rand); - } - BN_free(bn_rand); - BN_free(bn_range); - return ret; -} - -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; - unsigned char* data; - unsigned dlen; - ERL_NIF_TERM ret; - - if (!get_bn_from_mpint(env, argv[0], &bn_from) - || !get_bn_from_mpint(env, argv[1], &bn_rand)) { - if (bn_from) BN_free(bn_from); - return enif_make_badarg(env); - } - - bn_to = BN_new(); - BN_sub(bn_to, bn_rand, bn_from); - BN_pseudo_rand_range(bn_rand, bn_to); - BN_add(bn_rand, bn_rand, bn_from); - dlen = BN_num_bytes(bn_rand); - data = enif_make_new_binary(env, dlen+4, &ret); - put_int32(data, dlen); - BN_bn2bin(bn_rand, data+4); - ERL_VALGRIND_MAKE_MEM_DEFINED(data+4, dlen); - BN_free(bn_rand); - BN_free(bn_from); - BN_free(bn_to); - return ret; -} - -static ERL_NIF_TERM mod_exp_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Base,Exponent,Modulo,bin_hdr) */ - BIGNUM *bn_base=NULL, *bn_exponent=NULL, *bn_modulo=NULL, *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_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); - if (bn_modulo) BN_free(bn_modulo); - return enif_make_badarg(env); - } - bn_result = BN_new(); - bn_ctx = BN_CTX_new(); - BN_mod_exp(bn_result, bn_base, bn_exponent, bn_modulo, bn_ctx); - dlen = BN_num_bytes(bn_result); - 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); - BN_free(bn_exponent); - BN_free(bn_base); - return ret; -} - -static void init_digest_types(ErlNifEnv* env) -{ - struct digest_type_t* p = digest_types; - - for (p = digest_types; p->type.str; p++) { - p->type.atom = enif_make_atom(env, p->type.str); - if (p->md.funcp) - p->md.p = p->md.funcp(); - } - p->type.atom = atom_false; /* end marker */ -} - -static void init_cipher_types(ErlNifEnv* env) -{ - struct cipher_type_t* p = cipher_types; - - for (p = cipher_types; p->type.str; p++) { - p->type.atom = enif_make_atom(env, p->type.str); - if (p->cipher.funcp) - p->cipher.p = p->cipher.funcp(); - } - p->type.atom = atom_false; /* end marker */ -} - -static struct digest_type_t* get_digest_type(ERL_NIF_TERM type) -{ - struct digest_type_t* p = NULL; - for (p = digest_types; p->type.atom != atom_false; p++) { - if (type == p->type.atom) { - return p; - } - } - return NULL; -} - -static struct cipher_type_t* get_cipher_type(ERL_NIF_TERM type, size_t key_len) -{ - struct cipher_type_t* p = NULL; - for (p = cipher_types; p->type.atom != atom_false; p++) { - if (type == p->type.atom && (!p->key_len || key_len == p->key_len)) { - return p; - } - } - return NULL; -} - - -static ERL_NIF_TERM do_exor(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Data1, Data2) */ - ErlNifBinary d1, d2; - unsigned char* ret_ptr; - int i; - ERL_NIF_TERM ret; - - if (!enif_inspect_iolist_as_binary(env,argv[0], &d1) - || !enif_inspect_iolist_as_binary(env,argv[1], &d2) - || d1.size != d2.size) { - return enif_make_badarg(env); - } - ret_ptr = enif_make_new_binary(env, d1.size, &ret); - - for (i=0; i<d1.size; i++) { - ret_ptr[i] = d1.data[i] ^ d2.data[i]; - } - CONSUME_REDS(env,d1); - return ret; -} - -static ERL_NIF_TERM rc4_set_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Key) */ -#ifndef OPENSSL_NO_RC4 - ErlNifBinary key; - ERL_NIF_TERM ret; - - CHECK_NO_FIPS_MODE(); - - if (!enif_inspect_iolist_as_binary(env,argv[0], &key)) { - return enif_make_badarg(env); - } - RC4_set_key((RC4_KEY*)enif_make_new_binary(env, sizeof(RC4_KEY), &ret), - key.size, key.data); - return ret; -#else - return enif_raise_exception(env, atom_notsup); -#endif -} - -static ERL_NIF_TERM rc4_encrypt_with_state(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (State, Data) */ -#ifndef OPENSSL_NO_RC4 - ErlNifBinary state, data; - RC4_KEY* rc4_key; - ERL_NIF_TERM new_state, new_data; - - CHECK_NO_FIPS_MODE(); - - if (!enif_inspect_iolist_as_binary(env,argv[0], &state) - || state.size != sizeof(RC4_KEY) - || !enif_inspect_iolist_as_binary(env,argv[1], &data)) { - return enif_make_badarg(env); - } - rc4_key = (RC4_KEY*)enif_make_new_binary(env, sizeof(RC4_KEY), &new_state); - memcpy(rc4_key, state.data, sizeof(RC4_KEY)); - RC4(rc4_key, data.size, data.data, - enif_make_new_binary(env, data.size, &new_data)); - CONSUME_REDS(env,data); - return enif_make_tuple2(env,new_state,new_data); -#else - return enif_raise_exception(env, atom_notsup); -#endif -} - -static int get_rsa_private_key(ErlNifEnv* env, ERL_NIF_TERM key, RSA *rsa) -{ - /* key=[E,N,D]|[E,N,D,P1,P2,E1,E2,C] */ - ERL_NIF_TERM head, tail; - BIGNUM *e, *n, *d; - BIGNUM *p, *q; - BIGNUM *dmp1, *dmq1, *iqmp; - - if (!enif_get_list_cell(env, key, &head, &tail) - || !get_bn_from_bin(env, head, &e) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &n) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &d)) { - return 0; - } - (void) RSA_set0_key(rsa, n, e, d); - if (enif_is_empty_list(env, tail)) { - return 1; - } - if (!enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &p) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &q) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &dmp1) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &dmq1) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &iqmp) - || !enif_is_empty_list(env, tail)) { - return 0; - } - (void) RSA_set0_factors(rsa, p, q); - (void) RSA_set0_crt_params(rsa, dmp1, dmq1, iqmp); - return 1; -} - - -static int get_rsa_public_key(ErlNifEnv* env, ERL_NIF_TERM key, RSA *rsa) -{ - /* key=[E,N] */ - ERL_NIF_TERM head, tail; - BIGNUM *e, *n; - - if (!enif_get_list_cell(env, key, &head, &tail) - || !get_bn_from_bin(env, head, &e) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &n) - || !enif_is_empty_list(env, tail)) { - return 0; - } - - (void) RSA_set0_key(rsa, n, e, NULL); - return 1; -} - -#ifdef HAVE_EDDSA - static int get_eddsa_key(ErlNifEnv* env, int public, ERL_NIF_TERM key, EVP_PKEY **pkey) -{ - /* key=[K] */ - ERL_NIF_TERM head, tail, tail2, algo; - ErlNifBinary bin; - int type; - - if (!enif_get_list_cell(env, key, &head, &tail) - || !enif_inspect_binary(env, head, &bin) - || !enif_get_list_cell(env, tail, &algo, &tail2) - || !enif_is_empty_list(env, tail2)) { - return 0; - } - if (algo == atom_ed25519) type = EVP_PKEY_ED25519; - else if (algo == atom_ed448) type = EVP_PKEY_ED448; - else - return 0; - - if (public) - *pkey = EVP_PKEY_new_raw_public_key(type, NULL, bin.data, bin.size); - else - *pkey = EVP_PKEY_new_raw_private_key(type, NULL, bin.data, bin.size); - - if (!pkey) - return 0; - return 1; -} -#endif - -static int get_dss_private_key(ErlNifEnv* env, ERL_NIF_TERM key, DSA *dsa) -{ - /* key=[P,Q,G,KEY] */ - ERL_NIF_TERM head, tail; - BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL; - BIGNUM *dummy_pub_key, *priv_key = NULL; - - if (!enif_get_list_cell(env, key, &head, &tail) - || !get_bn_from_bin(env, head, &dsa_p) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &dsa_q) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &dsa_g) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &priv_key) - || !enif_is_empty_list(env,tail)) { - if (dsa_p) BN_free(dsa_p); - if (dsa_q) BN_free(dsa_q); - if (dsa_g) BN_free(dsa_g); - if (priv_key) BN_free(priv_key); - return 0; - } - - /* Note: DSA_set0_key() does not allow setting only the - * private key, although DSA_sign() does not use the - * public key. Work around this limitation by setting - * the public key to a copy of the private key. - */ - dummy_pub_key = BN_dup(priv_key); - - DSA_set0_pqg(dsa, dsa_p, dsa_q, dsa_g); - DSA_set0_key(dsa, dummy_pub_key, priv_key); - return 1; -} - - -static int get_dss_public_key(ErlNifEnv* env, ERL_NIF_TERM key, DSA *dsa) -{ - /* key=[P, Q, G, Y] */ - ERL_NIF_TERM head, tail; - BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL, *dsa_y = NULL; - - if (!enif_get_list_cell(env, key, &head, &tail) - || !get_bn_from_bin(env, head, &dsa_p) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &dsa_q) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &dsa_g) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &dsa_y) - || !enif_is_empty_list(env,tail)) { - if (dsa_p) BN_free(dsa_p); - if (dsa_q) BN_free(dsa_q); - if (dsa_g) BN_free(dsa_g); - if (dsa_y) BN_free(dsa_y); - return 0; - } - - DSA_set0_pqg(dsa, dsa_p, dsa_q, dsa_g); - DSA_set0_key(dsa, dsa_y, NULL); - return 1; -} - -/* Creates a term which can be parsed by get_rsa_private_key(). This is a list of plain integer binaries (not mpints). */ -static ERL_NIF_TERM put_rsa_private_key(ErlNifEnv* env, const RSA *rsa) -{ - ERL_NIF_TERM result[8]; - const BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp; - - /* Return at least [E,N,D] */ - n = NULL; e = NULL; d = NULL; - RSA_get0_key(rsa, &n, &e, &d); - - result[0] = bin_from_bn(env, e); // Exponent E - result[1] = bin_from_bn(env, n); // Modulus N = p*q - result[2] = bin_from_bn(env, d); // Exponent D - - /* Check whether the optional additional parameters are available */ - p = NULL; q = NULL; - RSA_get0_factors(rsa, &p, &q); - dmp1 = NULL; dmq1 = NULL; iqmp = NULL; - RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp); - - if (p && q && dmp1 && dmq1 && iqmp) { - result[3] = bin_from_bn(env, p); // Factor p - result[4] = bin_from_bn(env, q); // Factor q - result[5] = bin_from_bn(env, dmp1); // D mod (p-1) - result[6] = bin_from_bn(env, dmq1); // D mod (q-1) - result[7] = bin_from_bn(env, iqmp); // (1/q) mod p - - return enif_make_list_from_array(env, result, 8); - } else { - return enif_make_list_from_array(env, result, 3); - } -} - -static int check_erlang_interrupt(int maj, int min, BN_GENCB *ctxt) -{ - ErlNifEnv *env = BN_GENCB_get_arg(ctxt); - - if (!enif_is_current_process_alive(env)) { - return 0; - } else { - return 1; - } -} - -static ERL_NIF_TERM rsa_generate_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (ModulusSize, PublicExponent) */ - int modulus_bits; - BIGNUM *pub_exp, *three; - RSA *rsa; - int success; - ERL_NIF_TERM result; - BN_GENCB *intr_cb; -#ifndef HAVE_OPAQUE_BN_GENCB - BN_GENCB intr_cb_buf; -#endif - - if (!enif_get_int(env, argv[0], &modulus_bits) || modulus_bits < 256) { - return enif_make_badarg(env); - } - - if (!get_bn_from_bin(env, argv[1], &pub_exp)) { - return enif_make_badarg(env); - } - - /* Make sure the public exponent is large enough (at least 3). - * Without this, RSA_generate_key_ex() can run forever. */ - three = BN_new(); - BN_set_word(three, 3); - success = BN_cmp(pub_exp, three); - BN_free(three); - if (success < 0) { - BN_free(pub_exp); - return enif_make_badarg(env); - } - - /* For large keys, prime generation can take many seconds. Set up - * the callback which we use to test whether the process has been - * interrupted. */ -#ifdef HAVE_OPAQUE_BN_GENCB - intr_cb = BN_GENCB_new(); -#else - intr_cb = &intr_cb_buf; -#endif - BN_GENCB_set(intr_cb, check_erlang_interrupt, env); - - rsa = RSA_new(); - success = RSA_generate_key_ex(rsa, modulus_bits, pub_exp, intr_cb); - BN_free(pub_exp); - -#ifdef HAVE_OPAQUE_BN_GENCB - BN_GENCB_free(intr_cb); -#endif - - if (!success) { - RSA_free(rsa); - return atom_error; - } - - result = put_rsa_private_key(env, rsa); - RSA_free(rsa); - - return result; -} - -static ERL_NIF_TERM rsa_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - /* RSA key generation can take a long time (>1 sec for a large - * modulus), so schedule it as a CPU-bound operation. */ - return enif_schedule_nif(env, "rsa_generate_key", - ERL_NIF_DIRTY_JOB_CPU_BOUND, - rsa_generate_key, argc, argv); -} - -static ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (PrivKey|undefined, DHParams=[P,G], Mpint, Len|0) */ - DH *dh_params = NULL; - int mpint; /* 0 or 4 */ - - { - ERL_NIF_TERM head, tail; - BIGNUM - *dh_p = NULL, - *dh_g = NULL, - *priv_key_in = NULL; - unsigned long - len = 0; - - if (!(get_bn_from_bin(env, argv[0], &priv_key_in) - || argv[0] == atom_undefined) - || !enif_get_list_cell(env, argv[1], &head, &tail) - || !get_bn_from_bin(env, head, &dh_p) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &dh_g) - || !enif_is_empty_list(env, tail) - || !enif_get_int(env, argv[2], &mpint) || (mpint & ~4) - || !enif_get_ulong(env, argv[3], &len) - - /* Load dh_params with values to use by the generator. - Mem mgmnt transfered from dh_p etc to dh_params */ - || !(dh_params = DH_new()) - || (priv_key_in && !DH_set0_key(dh_params, NULL, priv_key_in)) - || !DH_set0_pqg(dh_params, dh_p, NULL, dh_g) - ) { - if (priv_key_in) BN_free(priv_key_in); - if (dh_p) BN_free(dh_p); - if (dh_g) BN_free(dh_g); - if (dh_params) DH_free(dh_params); - return enif_make_badarg(env); - } - - if (len) { - if (len < BN_num_bits(dh_p)) - DH_set_length(dh_params, len); - else { - if (priv_key_in) BN_free(priv_key_in); - if (dh_p) BN_free(dh_p); - if (dh_g) BN_free(dh_g); - if (dh_params) DH_free(dh_params); - return enif_make_badarg(env); - } - } - } - -#ifdef HAS_EVP_PKEY_CTX - { - EVP_PKEY_CTX *ctx; - EVP_PKEY *dhkey, *params; - int success; - - params = EVP_PKEY_new(); - success = EVP_PKEY_set1_DH(params, dh_params); /* set the key referenced by params to dh_params... */ - DH_free(dh_params); /* ...dh_params (and params) must be freed */ - if (!success) return atom_error; - - ctx = EVP_PKEY_CTX_new(params, NULL); - EVP_PKEY_free(params); - if (!ctx) { - return atom_error; - } - - if (!EVP_PKEY_keygen_init(ctx)) { - /* EVP_PKEY_CTX_free(ctx); */ - return atom_error; - } - - dhkey = EVP_PKEY_new(); - if (!EVP_PKEY_keygen(ctx, &dhkey)) { /* "performs a key generation operation, the ... */ - /*... generated key is written to ppkey." (=last arg) */ - /* EVP_PKEY_CTX_free(ctx); */ - /* EVP_PKEY_free(dhkey); */ - return atom_error; - } - - dh_params = EVP_PKEY_get1_DH(dhkey); /* return the referenced key. dh_params and dhkey must be freed */ - EVP_PKEY_free(dhkey); - if (!dh_params) { - /* EVP_PKEY_CTX_free(ctx); */ - return atom_error; - } - EVP_PKEY_CTX_free(ctx); - } -#else - if (!DH_generate_key(dh_params)) return atom_error; -#endif - { - unsigned char *pub_ptr, *prv_ptr; - int pub_len, prv_len; - ERL_NIF_TERM ret_pub, ret_prv; - const BIGNUM *pub_key_gen, *priv_key_gen; - - DH_get0_key(dh_params, - &pub_key_gen, &priv_key_gen); /* Get pub_key_gen and priv_key_gen. - "The values point to the internal representation of - the public key and private key values. This memory - should not be freed directly." says man */ - pub_len = BN_num_bytes(pub_key_gen); - prv_len = BN_num_bytes(priv_key_gen); - pub_ptr = enif_make_new_binary(env, pub_len+mpint, &ret_pub); - prv_ptr = enif_make_new_binary(env, prv_len+mpint, &ret_prv); - if (mpint) { - put_int32(pub_ptr, pub_len); pub_ptr += 4; - put_int32(prv_ptr, prv_len); prv_ptr += 4; - } - BN_bn2bin(pub_key_gen, pub_ptr); - BN_bn2bin(priv_key_gen, prv_ptr); - ERL_VALGRIND_MAKE_MEM_DEFINED(pub_ptr, pub_len); - ERL_VALGRIND_MAKE_MEM_DEFINED(prv_ptr, prv_len); - - DH_free(dh_params); - - return enif_make_tuple2(env, ret_pub, ret_prv); - } -} - -static ERL_NIF_TERM dh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (OthersPublicKey, MyPrivateKey, DHParams=[P,G]) */ - BIGNUM *other_pub_key = NULL, - *dh_p = NULL, - *dh_g = NULL; - DH *dh_priv = DH_new(); - - /* Check the arguments and get - my private key (dh_priv), - the peer's public key (other_pub_key), - the parameters p & q - */ - - { - BIGNUM *dummy_pub_key = NULL, - *priv_key = NULL; - ERL_NIF_TERM head, tail; - - if (!get_bn_from_bin(env, argv[0], &other_pub_key) - || !get_bn_from_bin(env, argv[1], &priv_key) - || !enif_get_list_cell(env, argv[2], &head, &tail) - || !get_bn_from_bin(env, head, &dh_p) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_bin(env, head, &dh_g) - || !enif_is_empty_list(env, tail) - - /* Note: DH_set0_key() does not allow setting only the - * private key, although DH_compute_key() does not use the - * public key. Work around this limitation by setting - * the public key to a copy of the private key. - */ - || !(dummy_pub_key = BN_dup(priv_key)) - || !DH_set0_key(dh_priv, dummy_pub_key, priv_key) - || !DH_set0_pqg(dh_priv, dh_p, NULL, dh_g) - ) { - if (dh_p) BN_free(dh_p); - if (dh_g) BN_free(dh_g); - if (other_pub_key) BN_free(other_pub_key); - if (dummy_pub_key) BN_free(dummy_pub_key); - if (priv_key) BN_free(priv_key); - return enif_make_badarg(env); - } - } - { - ErlNifBinary ret_bin; - int size; - - enif_alloc_binary(DH_size(dh_priv), &ret_bin); - size = DH_compute_key(ret_bin.data, other_pub_key, dh_priv); - BN_free(other_pub_key); - DH_free(dh_priv); - if (size<=0) { - enif_release_binary(&ret_bin); - return atom_error; - } - - if (size != ret_bin.size) enif_realloc_binary(&ret_bin, size); - return enif_make_binary(env, &ret_bin); - } -} - - -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 = NULL, *bn_generator = NULL, *bn_prime = NULL, *bn_multiplier = NULL, *bn_result; - BN_CTX *bn_ctx; - unsigned char* ptr; - unsigned dlen; - ERL_NIF_TERM ret; - - CHECK_NO_FIPS_MODE(); - - 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_generator) BN_free(bn_generator); - if (bn_exponent) BN_free(bn_exponent); - if (bn_prime) 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_user_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 = NULL, *bn_multiplier = NULL, *bn_exp2, - *bn_base, *bn_prime = NULL, *bn_generator = NULL, - *bn_B = NULL, *bn_result; - BN_CTX *bn_ctx; - unsigned char* ptr; - unsigned dlen; - ERL_NIF_TERM ret; - - CHECK_NO_FIPS_MODE(); - - 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_mul(bn_result, bn_u, bn_exponent, bn_ctx); - BN_add(bn_exp2, bn_a, bn_result); - - /* (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_host_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 = NULL, *bn_A = NULL, *bn_u = NULL, *bn_base, *bn_result; - BN_CTX *bn_ctx; - unsigned char* ptr; - unsigned dlen; - ERL_NIF_TERM ret; - - CHECK_NO_FIPS_MODE(); - - 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; -} - -#if defined(HAVE_EC) -static EC_KEY* ec_key_new(ErlNifEnv* env, ERL_NIF_TERM curve_arg) -{ - EC_KEY *key = NULL; - int c_arity = -1; - const ERL_NIF_TERM* curve; - ErlNifBinary seed; - BIGNUM *p = NULL; - BIGNUM *a = NULL; - BIGNUM *b = NULL; - BIGNUM *bn_order = NULL; - BIGNUM *cofactor = NULL; - EC_GROUP *group = NULL; - EC_POINT *point = NULL; - - /* {Field, Prime, Point, Order, CoFactor} = Curve */ - if (enif_get_tuple(env,curve_arg,&c_arity,&curve) - && c_arity == 5 - && get_bn_from_bin(env, curve[3], &bn_order) - && (curve[4] != atom_none && get_bn_from_bin(env, curve[4], &cofactor))) { - - int f_arity = -1; - const ERL_NIF_TERM* field; - int p_arity = -1; - const ERL_NIF_TERM* prime; - - long field_bits; - - /* {A, B, Seed} = Prime */ - if (!enif_get_tuple(env,curve[1],&p_arity,&prime) - || !get_bn_from_bin(env, prime[0], &a) - || !get_bn_from_bin(env, prime[1], &b)) - goto out_err; - - if (!enif_get_tuple(env,curve[0],&f_arity,&field)) - goto out_err; - - if (f_arity == 2 && field[0] == atom_prime_field) { - /* {prime_field, Prime} */ - - if (!get_bn_from_bin(env, field[1], &p)) - goto out_err; - - if (BN_is_negative(p) || BN_is_zero(p)) - goto out_err; - - field_bits = BN_num_bits(p); - if (field_bits > OPENSSL_ECC_MAX_FIELD_BITS) - goto out_err; - - /* create the EC_GROUP structure */ - group = EC_GROUP_new_curve_GFp(p, a, b, NULL); - - } else if (f_arity == 3 && field[0] == atom_characteristic_two_field) { -#if defined(OPENSSL_NO_EC2M) - enif_raise_exception(env, atom_notsup); - goto out_err; -#else - /* {characteristic_two_field, M, Basis} */ - - int b_arity = -1; - const ERL_NIF_TERM* basis; - unsigned int k1, k2, k3; - - if ((p = BN_new()) == NULL) - goto out_err; - - if (!enif_get_long(env, field[1], &field_bits) - || field_bits > OPENSSL_ECC_MAX_FIELD_BITS) - goto out_err; - - if (enif_get_tuple(env,field[2],&b_arity,&basis)) { - if (b_arity == 2 - && basis[0] == atom_tpbasis - && enif_get_uint(env, basis[1], &k1)) { - /* {tpbasis, k} = Basis */ - - if (!(field_bits > k1 && k1 > 0)) - goto out_err; - - /* create the polynomial */ - if (!BN_set_bit(p, (int)field_bits) - || !BN_set_bit(p, (int)k1) - || !BN_set_bit(p, 0)) - goto out_err; - - } else if (b_arity == 4 - && basis[0] == atom_ppbasis - && enif_get_uint(env, basis[1], &k1) - && enif_get_uint(env, basis[2], &k2) - && enif_get_uint(env, basis[3], &k3)) { - /* {ppbasis, k1, k2, k3} = Basis */ - - if (!(field_bits > k3 && k3 > k2 && k2 > k1 && k1 > 0)) - goto out_err; - - /* create the polynomial */ - if (!BN_set_bit(p, (int)field_bits) - || !BN_set_bit(p, (int)k1) - || !BN_set_bit(p, (int)k2) - || !BN_set_bit(p, (int)k3) - || !BN_set_bit(p, 0)) - goto out_err; - - } else - goto out_err; - } else if (field[2] == atom_onbasis) { - /* onbasis = Basis */ - /* no parameters */ - goto out_err; - - } else - goto out_err; - - group = EC_GROUP_new_curve_GF2m(p, a, b, NULL); -#endif - } else - goto out_err; - - if (!group) - goto out_err; - - if (enif_inspect_binary(env, prime[2], &seed)) { - EC_GROUP_set_seed(group, seed.data, seed.size); - } - - if (!term2point(env, curve[2], group, &point)) - goto out_err; - - if (BN_is_negative(bn_order) - || BN_is_zero(bn_order) - || BN_num_bits(bn_order) > (int)field_bits + 1) - goto out_err; - - if (!EC_GROUP_set_generator(group, point, bn_order, cofactor)) - goto out_err; - - EC_GROUP_set_asn1_flag(group, 0x0); - - key = EC_KEY_new(); - if (!key) - goto out_err; - EC_KEY_set_group(key, group); - } - else { - goto out_err; - } - - - goto out; - -out_err: - if (key) EC_KEY_free(key); - key = NULL; - -out: - /* some OpenSSL structures are mem-dup'ed into the key, - so we have to free our copies here */ - if (p) BN_free(p); - if (a) BN_free(a); - if (b) BN_free(b); - if (bn_order) BN_free(bn_order); - if (cofactor) BN_free(cofactor); - if (group) EC_GROUP_free(group); - if (point) EC_POINT_free(point); - - return key; -} - - -static ERL_NIF_TERM bn2term(ErlNifEnv* env, const BIGNUM *bn) -{ - unsigned dlen; - unsigned char* ptr; - ERL_NIF_TERM ret; - - if (!bn) - return atom_undefined; - - dlen = BN_num_bytes(bn); - ptr = enif_make_new_binary(env, dlen, &ret); - BN_bn2bin(bn, ptr); - ERL_VALGRIND_MAKE_MEM_DEFINED(ptr, dlen); - return ret; -} - -static ERL_NIF_TERM point2term(ErlNifEnv* env, - const EC_GROUP *group, - const EC_POINT *point, - point_conversion_form_t form) -{ - unsigned dlen; - ErlNifBinary bin; - - dlen = EC_POINT_point2oct(group, point, form, NULL, 0, NULL); - if (dlen == 0) - return atom_undefined; - - if (!enif_alloc_binary(dlen, &bin)) - return enif_make_badarg(env); - - if (!EC_POINT_point2oct(group, point, form, bin.data, bin.size, NULL)) { - enif_release_binary(&bin); - return enif_make_badarg(env); - } - ERL_VALGRIND_MAKE_MEM_DEFINED(bin.data, bin.size); - return enif_make_binary(env, &bin); -} - -static int term2point(ErlNifEnv* env, ERL_NIF_TERM term, - EC_GROUP *group, EC_POINT **pptr) -{ - int ret = 0; - ErlNifBinary bin; - EC_POINT *point; - - if (!enif_inspect_binary(env,term,&bin)) { - return 0; - } - - if ((*pptr = point = EC_POINT_new(group)) == NULL) { - return 0; - } - - /* set the point conversion form */ - EC_GROUP_set_point_conversion_form(group, (point_conversion_form_t)(bin.data[0] & ~0x01)); - - /* extract the ec point */ - if (!EC_POINT_oct2point(group, point, bin.data, bin.size, NULL)) { - EC_POINT_free(point); - *pptr = NULL; - } else - ret = 1; - - return ret; -} - -static int get_ec_key(ErlNifEnv* env, - ERL_NIF_TERM curve, ERL_NIF_TERM priv, ERL_NIF_TERM pub, - EC_KEY** res) -{ - EC_KEY *key = NULL; - BIGNUM *priv_key = NULL; - EC_POINT *pub_key = NULL; - EC_GROUP *group = NULL; - - if (!(priv == atom_undefined || get_bn_from_bin(env, priv, &priv_key)) - || !(pub == atom_undefined || enif_is_binary(env, pub))) { - goto out_err; - } - - key = ec_key_new(env, curve); - - if (!key) { - goto out_err; - } - - if (!group) - group = EC_GROUP_dup(EC_KEY_get0_group(key)); - - if (term2point(env, pub, group, &pub_key)) { - if (!EC_KEY_set_public_key(key, pub_key)) { - goto out_err; - } - } - if (priv != atom_undefined - && !BN_is_zero(priv_key)) { - if (!EC_KEY_set_private_key(key, priv_key)) - goto out_err; - - /* calculate public key (if necessary) */ - if (EC_KEY_get0_public_key(key) == NULL) - { - /* the public key was not included in the SEC1 private - * key => calculate the public key */ - pub_key = EC_POINT_new(group); - if (pub_key == NULL - || !EC_POINT_copy(pub_key, EC_GROUP_get0_generator(group)) - || !EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, NULL) - || !EC_KEY_set_public_key(key, pub_key)) - goto out_err; - } - } - - goto out; - -out_err: - if (key) EC_KEY_free(key); - key = NULL; - -out: - /* some OpenSSL structures are mem-dup'ed into the key, - so we have to free our copies here */ - if (priv_key) BN_clear_free(priv_key); - if (pub_key) EC_POINT_free(pub_key); - if (group) EC_GROUP_free(group); - if (!key) - return 0; - *res = key; - return 1; -} -#endif /* HAVE_EC */ - -static ERL_NIF_TERM ec_key_generate(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ -#if defined(HAVE_EC) - EC_KEY *key = NULL; - const EC_GROUP *group; - const EC_POINT *public_key; - ERL_NIF_TERM priv_key; - ERL_NIF_TERM pub_key = atom_undefined; - - if (!get_ec_key(env, argv[0], argv[1], atom_undefined, &key)) - goto badarg; - - if (argv[1] == atom_undefined) { - if (!EC_KEY_generate_key(key)) - goto badarg; - } - - group = EC_KEY_get0_group(key); - public_key = EC_KEY_get0_public_key(key); - - if (group && public_key) { - pub_key = point2term(env, group, public_key, - EC_KEY_get_conv_form(key)); - } - priv_key = bn2term(env, EC_KEY_get0_private_key(key)); - EC_KEY_free(key); - return enif_make_tuple2(env, pub_key, priv_key); - -badarg: - if (key) - EC_KEY_free(key); - return make_badarg_maybe(env); -#else - return atom_notsup; -#endif -} - -/* - (_OthersPublicKey, _MyPrivateKey) - (_OthersPublicKey, _MyEC_Point) -*/ -static ERL_NIF_TERM ecdh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -/* (OtherPublicKey, Curve, My) */ -{ -#if defined(HAVE_EC) - ERL_NIF_TERM ret; - unsigned char *p; - EC_KEY* key = NULL; - int field_size = 0; - int i; - EC_GROUP *group; - const BIGNUM *priv_key; - EC_POINT *my_ecpoint = NULL; - EC_KEY *other_ecdh = NULL; - - if (!get_ec_key(env, argv[1], argv[2], atom_undefined, &key)) - return make_badarg_maybe(env); - - group = EC_GROUP_dup(EC_KEY_get0_group(key)); - priv_key = EC_KEY_get0_private_key(key); - - if (!term2point(env, argv[0], group, &my_ecpoint)) { - goto out_err; - } - - if ((other_ecdh = EC_KEY_new()) == NULL - || !EC_KEY_set_group(other_ecdh, group) - || !EC_KEY_set_private_key(other_ecdh, priv_key)) - goto out_err; - - field_size = EC_GROUP_get_degree(group); - if (field_size <= 0) - goto out_err; - - p = enif_make_new_binary(env, (field_size+7)/8, &ret); - i = ECDH_compute_key(p, (field_size+7)/8, my_ecpoint, other_ecdh, NULL); - - if (i < 0) - goto out_err; -out: - if (group) EC_GROUP_free(group); - if (my_ecpoint) EC_POINT_free(my_ecpoint); - if (other_ecdh) EC_KEY_free(other_ecdh); - if (key) EC_KEY_free(key); - - return ret; - -out_err: - ret = enif_make_badarg(env); - goto out; -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM evp_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) - /* (Curve, PeerBin, MyBin) */ -{ -#ifdef HAVE_ED_CURVE_DH - int type; - EVP_PKEY_CTX *ctx = NULL; - ErlNifBinary peer_bin, my_bin, key_bin; - EVP_PKEY *peer_key = NULL, *my_key = NULL; - size_t max_size; - - if (argv[0] == atom_x25519) type = EVP_PKEY_X25519; - else if (argv[0] == atom_x448) type = EVP_PKEY_X448; - else return enif_make_badarg(env); - - if (!enif_inspect_binary(env, argv[1], &peer_bin) || - !enif_inspect_binary(env, argv[2], &my_bin)) - goto return_badarg; - - if (!(my_key = EVP_PKEY_new_raw_private_key(type, NULL, my_bin.data, my_bin.size)) || - !(ctx = EVP_PKEY_CTX_new(my_key, NULL))) - goto return_badarg; - - if (!EVP_PKEY_derive_init(ctx)) - goto return_badarg; - - if (!(peer_key = EVP_PKEY_new_raw_public_key(type, NULL, peer_bin.data, peer_bin.size)) || - !EVP_PKEY_derive_set_peer(ctx, peer_key)) - goto return_badarg; - - if (!EVP_PKEY_derive(ctx, NULL, &max_size)) - goto return_badarg; - - if (!enif_alloc_binary(max_size, &key_bin) || - !EVP_PKEY_derive(ctx, key_bin.data, &key_bin.size)) - goto return_badarg; - - if (key_bin.size < max_size) { - size_t actual_size = key_bin.size; - if (!enif_realloc_binary(&key_bin, actual_size)) - goto return_badarg; - } - - EVP_PKEY_free(my_key); - EVP_PKEY_free(peer_key); - EVP_PKEY_CTX_free(ctx); - return enif_make_binary(env, &key_bin); - -return_badarg: - if (my_key) EVP_PKEY_free(my_key); - if (peer_key) EVP_PKEY_free(peer_key); - if (ctx) EVP_PKEY_CTX_free(ctx); - return enif_make_badarg(env); -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM evp_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -/* (Curve) */ -{ -#ifdef HAVE_ED_CURVE_DH - int type; - EVP_PKEY_CTX *ctx = NULL; - EVP_PKEY *pkey = NULL; - ERL_NIF_TERM ret_pub, ret_prv; - size_t key_len; - - if (argv[0] == atom_x25519) type = EVP_PKEY_X25519; - else if (argv[0] == atom_x448) type = EVP_PKEY_X448; - else return enif_make_badarg(env); - - if (!(ctx = EVP_PKEY_CTX_new_id(type, NULL))) return enif_make_badarg(env); - - if (!EVP_PKEY_keygen_init(ctx)) goto return_error; - if (!EVP_PKEY_keygen(ctx, &pkey)) goto return_error; - - if (!EVP_PKEY_get_raw_public_key(pkey, NULL, &key_len)) goto return_error; - if (!EVP_PKEY_get_raw_public_key(pkey, - enif_make_new_binary(env, key_len, &ret_pub), - &key_len)) - goto return_error; - - if (!EVP_PKEY_get_raw_private_key(pkey, NULL, &key_len)) goto return_error; - if (!EVP_PKEY_get_raw_private_key(pkey, - enif_make_new_binary(env, key_len, &ret_prv), - &key_len)) - goto return_error; - - EVP_PKEY_free(pkey); - EVP_PKEY_CTX_free(ctx); - return enif_make_tuple2(env, ret_pub, ret_prv); - -return_error: - if (pkey) EVP_PKEY_free(pkey); - if (ctx) EVP_PKEY_CTX_free(ctx); - return atom_error; - -#else - return atom_notsup; -#endif -} - -/*================================================================*/ -#define PKEY_BADARG -1 -#define PKEY_NOTSUP 0 -#define PKEY_OK 1 - -typedef struct PKeyCryptOptions { - const EVP_MD *rsa_mgf1_md; - ErlNifBinary rsa_oaep_label; - const EVP_MD *rsa_oaep_md; - int rsa_padding; - const EVP_MD *signature_md; -} PKeyCryptOptions; - -typedef struct PKeySignOptions { - const EVP_MD *rsa_mgf1_md; - int rsa_padding; - int rsa_pss_saltlen; -} PKeySignOptions; - -static int get_pkey_digest_type(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM type, - const EVP_MD **md) -{ - struct digest_type_t *digp = NULL; - *md = NULL; - - if (type == atom_none && algorithm == atom_rsa) return PKEY_OK; -#ifdef HAVE_EDDSA - if (algorithm == atom_eddsa) return PKEY_OK; -#endif - digp = get_digest_type(type); - if (!digp) return PKEY_BADARG; - if (!digp->md.p) return PKEY_NOTSUP; - - *md = digp->md.p; - return PKEY_OK; -} - - -static int get_pkey_sign_digest(ErlNifEnv *env, ERL_NIF_TERM algorithm, - ERL_NIF_TERM type, ERL_NIF_TERM data, - unsigned char *md_value, const EVP_MD **mdp, - unsigned char **tbsp, size_t *tbslenp) -{ - int i; - const ERL_NIF_TERM *tpl_terms; - int tpl_arity; - ErlNifBinary tbs_bin; - EVP_MD_CTX *mdctx; - const EVP_MD *md = *mdp; - unsigned char *tbs = *tbsp; - size_t tbslen = *tbslenp; - unsigned int tbsleni; - - if ((i = get_pkey_digest_type(env, algorithm, type, &md)) != PKEY_OK) { - return i; - } - if (enif_get_tuple(env, data, &tpl_arity, &tpl_terms)) { - if (tpl_arity != 2 || tpl_terms[0] != atom_digest - || !enif_inspect_binary(env, tpl_terms[1], &tbs_bin) - || (md != NULL && tbs_bin.size != EVP_MD_size(md))) { - return PKEY_BADARG; - } - /* We have a digest (= hashed text) in tbs_bin */ - tbs = tbs_bin.data; - tbslen = tbs_bin.size; - } else if (md == NULL) { - if (!enif_inspect_binary(env, data, &tbs_bin)) { - return PKEY_BADARG; - } - /* md == NULL, that is no hashing because DigestType argument was atom_none */ - tbs = tbs_bin.data; - tbslen = tbs_bin.size; - } else { - if (!enif_inspect_binary(env, data, &tbs_bin)) { - return PKEY_BADARG; - } - /* We have the cleartext in tbs_bin and the hash algo info in md */ - tbs = md_value; - mdctx = EVP_MD_CTX_create(); - if (!mdctx) { - return PKEY_BADARG; - } - /* Looks well, now hash the plain text into a digest according to md */ - if (EVP_DigestInit_ex(mdctx, md, NULL) <= 0) { - EVP_MD_CTX_destroy(mdctx); - return PKEY_BADARG; - } - if (EVP_DigestUpdate(mdctx, tbs_bin.data, tbs_bin.size) <= 0) { - EVP_MD_CTX_destroy(mdctx); - return PKEY_BADARG; - } - if (EVP_DigestFinal_ex(mdctx, tbs, &tbsleni) <= 0) { - EVP_MD_CTX_destroy(mdctx); - return PKEY_BADARG; - } - tbslen = (size_t)(tbsleni); - EVP_MD_CTX_destroy(mdctx); - } - - *mdp = md; - *tbsp = tbs; - *tbslenp = tbslen; - - return PKEY_OK; -} - - -static int get_pkey_sign_options(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM options, - const EVP_MD *md, PKeySignOptions *opt) -{ - ERL_NIF_TERM head, tail; - const ERL_NIF_TERM *tpl_terms; - int tpl_arity; - const EVP_MD *opt_md; - int i; - - if (!enif_is_list(env, options)) { - return PKEY_BADARG; - } - - /* defaults */ - if (algorithm == atom_rsa) { - opt->rsa_mgf1_md = NULL; - opt->rsa_padding = RSA_PKCS1_PADDING; - opt->rsa_pss_saltlen = -2; - } - - if (enif_is_empty_list(env, options)) { - return PKEY_OK; - } - - if (algorithm == atom_rsa) { - tail = options; - while (enif_get_list_cell(env, tail, &head, &tail)) { - if (enif_get_tuple(env, head, &tpl_arity, &tpl_terms) && tpl_arity == 2) { - if (tpl_terms[0] == atom_rsa_mgf1_md && enif_is_atom(env, tpl_terms[1])) { - i = get_pkey_digest_type(env, algorithm, tpl_terms[1], &opt_md); - if (i != PKEY_OK) { - return i; - } - opt->rsa_mgf1_md = opt_md; - } else if (tpl_terms[0] == atom_rsa_padding) { - if (tpl_terms[1] == atom_rsa_pkcs1_padding) { - opt->rsa_padding = RSA_PKCS1_PADDING; - } else if (tpl_terms[1] == atom_rsa_pkcs1_pss_padding) { -#ifdef HAVE_RSA_PKCS1_PSS_PADDING - opt->rsa_padding = RSA_PKCS1_PSS_PADDING; - if (opt->rsa_mgf1_md == NULL) { - opt->rsa_mgf1_md = md; - } -#else - return PKEY_NOTSUP; -#endif - } else if (tpl_terms[1] == atom_rsa_x931_padding) { - opt->rsa_padding = RSA_X931_PADDING; - } else if (tpl_terms[1] == atom_rsa_no_padding) { - opt->rsa_padding = RSA_NO_PADDING; - } else { - return PKEY_BADARG; - } - } else if (tpl_terms[0] == atom_rsa_pss_saltlen) { - if (!enif_get_int(env, tpl_terms[1], &(opt->rsa_pss_saltlen)) - || opt->rsa_pss_saltlen < -2) { - return PKEY_BADARG; - } - } else { - return PKEY_BADARG; - } - } else { - return PKEY_BADARG; - } - } - } else { - return PKEY_BADARG; - } - - return PKEY_OK; -} - - -#ifdef HAS_ENGINE_SUPPORT -static int get_engine_and_key_id(ErlNifEnv *env, ERL_NIF_TERM key, char ** id, ENGINE **e) -{ - ERL_NIF_TERM engine_res, key_id_term; - struct engine_ctx *ctx; - ErlNifBinary key_id_bin; - - if (!enif_get_map_value(env, key, atom_engine, &engine_res) || - !enif_get_resource(env, engine_res, engine_ctx_rtype, (void**)&ctx) || - !enif_get_map_value(env, key, atom_key_id, &key_id_term) || - !enif_inspect_binary(env, key_id_term, &key_id_bin)) { - return 0; - } - else { - *e = ctx->engine; - return zero_terminate(key_id_bin, id); - } -} - - -static char *get_key_password(ErlNifEnv *env, ERL_NIF_TERM key) { - ERL_NIF_TERM tmp_term; - ErlNifBinary pwd_bin; - char *pwd = NULL; - if (enif_get_map_value(env, key, atom_password, &tmp_term) && - enif_inspect_binary(env, tmp_term, &pwd_bin) && - zero_terminate(pwd_bin, &pwd) - ) return pwd; - - return NULL; -} - -static int zero_terminate(ErlNifBinary bin, char **buf) { - *buf = enif_alloc(bin.size+1); - if (!*buf) - return 0; - memcpy(*buf, bin.data, bin.size); - *(*buf+bin.size) = 0; - return 1; -} -#endif - -static int get_pkey_private_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM key, EVP_PKEY **pkey) -{ - if (enif_is_map(env, key)) { -#ifdef HAS_ENGINE_SUPPORT - /* Use key stored in engine */ - ENGINE *e; - char *id = NULL; - char *password; - - if (!get_engine_and_key_id(env, key, &id, &e)) - return PKEY_BADARG; - password = get_key_password(env, key); - *pkey = ENGINE_load_private_key(e, id, NULL, password); - if (password) enif_free(password); - enif_free(id); - if (!*pkey) - return PKEY_BADARG; -#else - return PKEY_BADARG; -#endif - } - else if (algorithm == atom_rsa) { - RSA *rsa = RSA_new(); - - if (!get_rsa_private_key(env, key, rsa)) { - RSA_free(rsa); - return PKEY_BADARG; - } - - *pkey = EVP_PKEY_new(); - if (!EVP_PKEY_assign_RSA(*pkey, rsa)) { - EVP_PKEY_free(*pkey); - RSA_free(rsa); - return PKEY_BADARG; - } - } else if (algorithm == atom_ecdsa) { -#if defined(HAVE_EC) - EC_KEY *ec = NULL; - const ERL_NIF_TERM *tpl_terms; - int tpl_arity; - - if (enif_get_tuple(env, key, &tpl_arity, &tpl_terms) && tpl_arity == 2 - && enif_is_tuple(env, tpl_terms[0]) && enif_is_binary(env, tpl_terms[1]) - && get_ec_key(env, tpl_terms[0], tpl_terms[1], atom_undefined, &ec)) { - - *pkey = EVP_PKEY_new(); - if (!EVP_PKEY_assign_EC_KEY(*pkey, ec)) { - EVP_PKEY_free(*pkey); - EC_KEY_free(ec); - return PKEY_BADARG; - } - } else { - return PKEY_BADARG; - } -#else - return PKEY_NOTSUP; -#endif - } else if (algorithm == atom_eddsa) { -#if defined(HAVE_EDDSA) - if (!get_eddsa_key(env, 0, key, pkey)) { - return PKEY_BADARG; - } -#else - return PKEY_NOTSUP; -#endif - } else if (algorithm == atom_dss) { - DSA *dsa = DSA_new(); - - if (!get_dss_private_key(env, key, dsa)) { - DSA_free(dsa); - return PKEY_BADARG; - } - - *pkey = EVP_PKEY_new(); - if (!EVP_PKEY_assign_DSA(*pkey, dsa)) { - EVP_PKEY_free(*pkey); - DSA_free(dsa); - return PKEY_BADARG; - } - } else { - return PKEY_BADARG; - } - - return PKEY_OK; -} - - -static int get_pkey_public_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM key, - EVP_PKEY **pkey) -{ - if (enif_is_map(env, key)) { -#ifdef HAS_ENGINE_SUPPORT - /* Use key stored in engine */ - ENGINE *e; - char *id = NULL; - char *password; - - if (!get_engine_and_key_id(env, key, &id, &e)) - return PKEY_BADARG; - password = get_key_password(env, key); - *pkey = ENGINE_load_public_key(e, id, NULL, password); - if (password) enif_free(password); - enif_free(id); - if (!pkey) - return PKEY_BADARG; -#else - return PKEY_BADARG; -#endif - } else if (algorithm == atom_rsa) { - RSA *rsa = RSA_new(); - - if (!get_rsa_public_key(env, key, rsa)) { - RSA_free(rsa); - return PKEY_BADARG; - } - - *pkey = EVP_PKEY_new(); - if (!EVP_PKEY_assign_RSA(*pkey, rsa)) { - EVP_PKEY_free(*pkey); - RSA_free(rsa); - return PKEY_BADARG; - } - } else if (algorithm == atom_ecdsa) { -#if defined(HAVE_EC) - EC_KEY *ec = NULL; - const ERL_NIF_TERM *tpl_terms; - int tpl_arity; - - if (enif_get_tuple(env, key, &tpl_arity, &tpl_terms) && tpl_arity == 2 - && enif_is_tuple(env, tpl_terms[0]) && enif_is_binary(env, tpl_terms[1]) - && get_ec_key(env, tpl_terms[0], atom_undefined, tpl_terms[1], &ec)) { - - *pkey = EVP_PKEY_new(); - if (!EVP_PKEY_assign_EC_KEY(*pkey, ec)) { - EVP_PKEY_free(*pkey); - EC_KEY_free(ec); - return PKEY_BADARG; - } - } else { - return PKEY_BADARG; - } -#else - return PKEY_NOTSUP; -#endif - } else if (algorithm == atom_eddsa) { -#if defined(HAVE_EDDSA) - if (!get_eddsa_key(env, 1, key, pkey)) { - return PKEY_BADARG; - } -#else - return PKEY_NOTSUP; -#endif - } else if (algorithm == atom_dss) { - DSA *dsa = DSA_new(); - - if (!get_dss_public_key(env, key, dsa)) { - DSA_free(dsa); - return PKEY_BADARG; - } - - *pkey = EVP_PKEY_new(); - if (!EVP_PKEY_assign_DSA(*pkey, dsa)) { - EVP_PKEY_free(*pkey); - DSA_free(dsa); - return PKEY_BADARG; - } - } else { - return PKEY_BADARG; - } - - return PKEY_OK; -} - -static ERL_NIF_TERM pkey_sign_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) -{/* (Algorithm, Type, Data|{digest,Digest}, Key|#{}, Options) */ - int i; - const EVP_MD *md = NULL; - unsigned char md_value[EVP_MAX_MD_SIZE]; - EVP_PKEY *pkey; -#ifdef HAS_EVP_PKEY_CTX - EVP_PKEY_CTX *ctx; - size_t siglen; -#else - unsigned len, siglen; -#endif - PKeySignOptions sig_opt; - ErlNifBinary sig_bin; /* signature */ - unsigned char *tbs; /* data to be signed */ - size_t tbslen; -/*char buf[1024]; -enif_get_atom(env,argv[0],buf,1024,ERL_NIF_LATIN1); printf("algo=%s ",buf); -enif_get_atom(env,argv[1],buf,1024,ERL_NIF_LATIN1); printf("hash=%s ",buf); -printf("\r\n"); -*/ - -#ifndef HAS_ENGINE_SUPPORT - if (enif_is_map(env, argv[3])) { - return atom_notsup; - } -#endif - - i = get_pkey_sign_digest(env, argv[0], argv[1], argv[2], md_value, &md, &tbs, &tbslen); - if (i != PKEY_OK) { - if (i == PKEY_NOTSUP) - return atom_notsup; - else - return enif_make_badarg(env); - } - - i = get_pkey_sign_options(env, argv[0], argv[4], md, &sig_opt); - if (i != PKEY_OK) { - if (i == PKEY_NOTSUP) - return atom_notsup; - else - return enif_make_badarg(env); - } - - if (get_pkey_private_key(env, argv[0], argv[3], &pkey) != PKEY_OK) { - return enif_make_badarg(env); - } - -#ifdef HAS_EVP_PKEY_CTX - ctx = EVP_PKEY_CTX_new(pkey, NULL); - if (!ctx) goto badarg; - - if (argv[0] != atom_eddsa) { - if (EVP_PKEY_sign_init(ctx) <= 0) goto badarg; - if (md != NULL && EVP_PKEY_CTX_set_signature_md(ctx, md) <= 0) goto badarg; - } - - if (argv[0] == atom_rsa) { - if (EVP_PKEY_CTX_set_rsa_padding(ctx, sig_opt.rsa_padding) <= 0) goto badarg; -# ifdef HAVE_RSA_PKCS1_PSS_PADDING - if (sig_opt.rsa_padding == RSA_PKCS1_PSS_PADDING) { - if (sig_opt.rsa_mgf1_md != NULL) { -# ifdef HAVE_RSA_MGF1_MD - if (EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, sig_opt.rsa_mgf1_md) <= 0) goto badarg; -# else - EVP_PKEY_CTX_free(ctx); - EVP_PKEY_free(pkey); - return atom_notsup; -# endif - } - if (sig_opt.rsa_pss_saltlen > -2 - && EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, sig_opt.rsa_pss_saltlen) <= 0) - goto badarg; - } -#endif - } - - if (argv[0] == atom_eddsa) { -#ifdef HAVE_EDDSA - EVP_MD_CTX* mdctx = EVP_MD_CTX_new(); - if (!EVP_DigestSignInit(mdctx, NULL, NULL, NULL, pkey)) { - if (mdctx) EVP_MD_CTX_free(mdctx); - goto badarg; - } - - if (!EVP_DigestSign(mdctx, NULL, &siglen, tbs, tbslen)) { - EVP_MD_CTX_free(mdctx); - goto badarg; - } - enif_alloc_binary(siglen, &sig_bin); - - if (!EVP_DigestSign(mdctx, sig_bin.data, &siglen, tbs, tbslen)) { - EVP_MD_CTX_free(mdctx); - goto badarg; - } - EVP_MD_CTX_free(mdctx); -#else - goto badarg; -#endif - } - else - { - if (EVP_PKEY_sign(ctx, NULL, &siglen, tbs, tbslen) <= 0) goto badarg; - enif_alloc_binary(siglen, &sig_bin); - - if (md != NULL) { - ERL_VALGRIND_ASSERT_MEM_DEFINED(tbs, EVP_MD_size(md)); - } - i = EVP_PKEY_sign(ctx, sig_bin.data, &siglen, tbs, tbslen); - } - - EVP_PKEY_CTX_free(ctx); -#else -/*printf("Old interface\r\n"); - */ - if (argv[0] == atom_rsa) { - RSA *rsa = EVP_PKEY_get1_RSA(pkey); - enif_alloc_binary(RSA_size(rsa), &sig_bin); - len = EVP_MD_size(md); - ERL_VALGRIND_ASSERT_MEM_DEFINED(tbs, len); - i = RSA_sign(md->type, tbs, len, sig_bin.data, &siglen, rsa); - RSA_free(rsa); - } else if (argv[0] == atom_dss) { - DSA *dsa = EVP_PKEY_get1_DSA(pkey); - enif_alloc_binary(DSA_size(dsa), &sig_bin); - len = EVP_MD_size(md); - ERL_VALGRIND_ASSERT_MEM_DEFINED(tbs, len); - i = DSA_sign(md->type, tbs, len, sig_bin.data, &siglen, dsa); - DSA_free(dsa); - } else if (argv[0] == atom_ecdsa) { -#if defined(HAVE_EC) - EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pkey); - enif_alloc_binary(ECDSA_size(ec), &sig_bin); - len = EVP_MD_size(md); - ERL_VALGRIND_ASSERT_MEM_DEFINED(tbs, len); - i = ECDSA_sign(md->type, tbs, len, sig_bin.data, &siglen, ec); - EC_KEY_free(ec); -#else - EVP_PKEY_free(pkey); - return atom_notsup; -#endif - } else { - goto badarg; - } -#endif - - EVP_PKEY_free(pkey); - if (i == 1) { - ERL_VALGRIND_MAKE_MEM_DEFINED(sig_bin.data, siglen); - if (siglen != sig_bin.size) { - enif_realloc_binary(&sig_bin, siglen); - ERL_VALGRIND_ASSERT_MEM_DEFINED(sig_bin.data, siglen); - } - return enif_make_binary(env, &sig_bin); - } else { - enif_release_binary(&sig_bin); - return atom_error; - } - - badarg: -#ifdef HAS_EVP_PKEY_CTX - EVP_PKEY_CTX_free(ctx); -#endif - EVP_PKEY_free(pkey); - return enif_make_badarg(env); -} - - -static ERL_NIF_TERM pkey_verify_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) -{/* (Algorithm, Type, Data|{digest,Digest}, Signature, Key, Options) */ - int i; - const EVP_MD *md = NULL; - unsigned char md_value[EVP_MAX_MD_SIZE]; - EVP_PKEY *pkey; -#ifdef HAS_EVP_PKEY_CTX - EVP_PKEY_CTX *ctx; -#else -#endif - PKeySignOptions sig_opt; - ErlNifBinary sig_bin; /* signature */ - unsigned char *tbs; /* data to be signed */ - size_t tbslen; - -#ifndef HAS_ENGINE_SUPPORT - if (enif_is_map(env, argv[4])) { - return atom_notsup; - } -#endif - - if (!enif_inspect_binary(env, argv[3], &sig_bin)) { - return enif_make_badarg(env); - } - - i = get_pkey_sign_digest(env, argv[0], argv[1], argv[2], md_value, &md, &tbs, &tbslen); - if (i != PKEY_OK) { - if (i == PKEY_NOTSUP) - return atom_notsup; - else - return enif_make_badarg(env); - } - - i = get_pkey_sign_options(env, argv[0], argv[5], md, &sig_opt); - if (i != PKEY_OK) { - if (i == PKEY_NOTSUP) - return atom_notsup; - else - return enif_make_badarg(env); - } - - if (get_pkey_public_key(env, argv[0], argv[4], &pkey) != PKEY_OK) { - return enif_make_badarg(env); - } - -#ifdef HAS_EVP_PKEY_CTX -/* printf("EVP interface\r\n"); - */ - ctx = EVP_PKEY_CTX_new(pkey, NULL); - if (!ctx) goto badarg; - - if (argv[0] != atom_eddsa) { - if (EVP_PKEY_verify_init(ctx) <= 0) goto badarg; - if (md != NULL && EVP_PKEY_CTX_set_signature_md(ctx, md) <= 0) goto badarg; - } - - if (argv[0] == atom_rsa) { - if (EVP_PKEY_CTX_set_rsa_padding(ctx, sig_opt.rsa_padding) <= 0) goto badarg; - if (sig_opt.rsa_padding == RSA_PKCS1_PSS_PADDING) { - if (sig_opt.rsa_mgf1_md != NULL) { -# ifdef HAVE_RSA_MGF1_MD - if (EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, sig_opt.rsa_mgf1_md) <= 0) goto badarg; -# else - EVP_PKEY_CTX_free(ctx); - EVP_PKEY_free(pkey); - return atom_notsup; -# endif - } - if (sig_opt.rsa_pss_saltlen > -2 - && EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, sig_opt.rsa_pss_saltlen) <= 0) - goto badarg; - } - } - - if (argv[0] == atom_eddsa) { -#ifdef HAVE_EDDSA - EVP_MD_CTX* mdctx = EVP_MD_CTX_create(); - - if (!EVP_DigestVerifyInit(mdctx, NULL, NULL, NULL, pkey)) { - if (mdctx) EVP_MD_CTX_destroy(mdctx); - goto badarg; - } - - i = EVP_DigestVerify(mdctx, sig_bin.data, sig_bin.size, tbs, tbslen); - EVP_MD_CTX_destroy(mdctx); -#else - goto badarg; -#endif - } - else - { - if (md != NULL) { - ERL_VALGRIND_ASSERT_MEM_DEFINED(tbs, EVP_MD_size(md)); - } - i = EVP_PKEY_verify(ctx, sig_bin.data, sig_bin.size, tbs, tbslen); - } - - EVP_PKEY_CTX_free(ctx); -#else -/*printf("Old interface\r\n"); -*/ - if (argv[0] == atom_rsa) { - RSA *rsa = EVP_PKEY_get1_RSA(pkey); - i = RSA_verify(md->type, tbs, tbslen, sig_bin.data, sig_bin.size, rsa); - RSA_free(rsa); - } else if (argv[0] == atom_dss) { - DSA *dsa = EVP_PKEY_get1_DSA(pkey); - i = DSA_verify(0, tbs, tbslen, sig_bin.data, sig_bin.size, dsa); - DSA_free(dsa); - } else if (argv[0] == atom_ecdsa) { -#if defined(HAVE_EC) - EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pkey); - i = ECDSA_verify(EVP_MD_type(md), tbs, tbslen, sig_bin.data, sig_bin.size, ec); - EC_KEY_free(ec); -#else - EVP_PKEY_free(pkey); - return atom_notsup; -#endif - } else { - goto badarg; - } -#endif - - EVP_PKEY_free(pkey); - if (i == 1) { - return atom_true; - } else { - return atom_false; - } - - badarg: -#ifdef HAS_EVP_PKEY_CTX - EVP_PKEY_CTX_free(ctx); -#endif - EVP_PKEY_free(pkey); - return enif_make_badarg(env); -} - - -/*--------------------------------*/ - -static int get_pkey_crypt_options(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM options, - PKeyCryptOptions *opt) -{ - ERL_NIF_TERM head, tail; - const ERL_NIF_TERM *tpl_terms; - int tpl_arity; - const EVP_MD *opt_md; - int i; - - if (!enif_is_list(env, options)) { - return PKEY_BADARG; - } - - /* defaults */ - if (algorithm == atom_rsa) { - opt->rsa_mgf1_md = NULL; - opt->rsa_oaep_label.data = NULL; - opt->rsa_oaep_label.size = 0; - opt->rsa_oaep_md = NULL; - opt->rsa_padding = RSA_PKCS1_PADDING; - opt->signature_md = NULL; - } - - if (enif_is_empty_list(env, options)) { - return PKEY_OK; - } - - if (algorithm == atom_rsa) { - tail = options; - while (enif_get_list_cell(env, tail, &head, &tail)) { - if (enif_get_tuple(env, head, &tpl_arity, &tpl_terms) && tpl_arity == 2) { - if (tpl_terms[0] == atom_rsa_padding - || tpl_terms[0] == atom_rsa_pad /* Compatibility */ - ) { - if (tpl_terms[1] == atom_rsa_pkcs1_padding) { - opt->rsa_padding = RSA_PKCS1_PADDING; -#ifdef HAVE_RSA_OAEP_PADDING - } else if (tpl_terms[1] == atom_rsa_pkcs1_oaep_padding) { - opt->rsa_padding = RSA_PKCS1_OAEP_PADDING; -#endif -#ifdef HAVE_RSA_SSLV23_PADDING - } else if (tpl_terms[1] == atom_rsa_sslv23_padding) { - opt->rsa_padding = RSA_SSLV23_PADDING; -#endif - } else if (tpl_terms[1] == atom_rsa_x931_padding) { - opt->rsa_padding = RSA_X931_PADDING; - } else if (tpl_terms[1] == atom_rsa_no_padding) { - opt->rsa_padding = RSA_NO_PADDING; - } else { - return PKEY_BADARG; - } - } else if (tpl_terms[0] == atom_signature_md && enif_is_atom(env, tpl_terms[1])) { - i = get_pkey_digest_type(env, algorithm, tpl_terms[1], &opt_md); - if (i != PKEY_OK) { - return i; - } - opt->signature_md = opt_md; - } else if (tpl_terms[0] == atom_rsa_mgf1_md && enif_is_atom(env, tpl_terms[1])) { -#ifndef HAVE_RSA_MGF1_MD - if (tpl_terms[1] != atom_sha) - return PKEY_NOTSUP; -#endif - i = get_pkey_digest_type(env, algorithm, tpl_terms[1], &opt_md); - if (i != PKEY_OK) { - return i; - } - opt->rsa_mgf1_md = opt_md; - } else if (tpl_terms[0] == atom_rsa_oaep_label - && enif_inspect_binary(env, tpl_terms[1], &(opt->rsa_oaep_label))) { -#ifdef HAVE_RSA_OAEP_MD - continue; -#else - return PKEY_NOTSUP; -#endif - } else if (tpl_terms[0] == atom_rsa_oaep_md && enif_is_atom(env, tpl_terms[1])) { -#ifndef HAVE_RSA_OAEP_MD - if (tpl_terms[1] != atom_sha) - return PKEY_NOTSUP; -#endif - i = get_pkey_digest_type(env, algorithm, tpl_terms[1], &opt_md); - if (i != PKEY_OK) { - return i; - } - opt->rsa_oaep_md = opt_md; - } else { - return PKEY_BADARG; - } - } else { - return PKEY_BADARG; - } - } - } else { - return PKEY_BADARG; - } - - return PKEY_OK; -} - -static size_t size_of_RSA(EVP_PKEY *pkey) { - size_t tmplen; - RSA *rsa = EVP_PKEY_get1_RSA(pkey); - if (rsa == NULL) return 0; - tmplen = RSA_size(rsa); - RSA_free(rsa); - return tmplen; -} - -static ERL_NIF_TERM pkey_crypt_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) -{/* (Algorithm, Data, PublKey=[E,N]|[E,N,D]|[E,N,D,P1,P2,E1,E2,C], Options, IsPrivate, IsEncrypt) */ - int i; - EVP_PKEY *pkey; -#ifdef HAS_EVP_PKEY_CTX - EVP_PKEY_CTX *ctx; -#else - RSA *rsa; -#endif - PKeyCryptOptions crypt_opt; - ErlNifBinary in_bin, out_bin, tmp_bin; - size_t outlen; -#ifdef HAVE_RSA_SSLV23_PADDING - size_t tmplen; -#endif - int is_private = (argv[4] == atom_true), - is_encrypt = (argv[5] == atom_true); - int algo_init = 0; - -/* char algo[1024]; */ - -#ifndef HAS_ENGINE_SUPPORT - if (enif_is_map(env, argv[2])) { - return atom_notsup; - } -#endif - - if (!enif_inspect_binary(env, argv[1], &in_bin)) { - return enif_make_badarg(env); - } - - i = get_pkey_crypt_options(env, argv[0], argv[3], &crypt_opt); - if (i != PKEY_OK) { - if (i == PKEY_NOTSUP) - return atom_notsup; - else - return enif_make_badarg(env); - } - - if (is_private) { - if (get_pkey_private_key(env, argv[0], argv[2], &pkey) != PKEY_OK) { - return enif_make_badarg(env); - } - } else { - if (get_pkey_public_key(env, argv[0], argv[2], &pkey) != PKEY_OK) { - return enif_make_badarg(env); - } - } - - out_bin.data = NULL; - out_bin.size = 0; - tmp_bin.data = NULL; - tmp_bin.size = 0; - -#ifdef HAS_EVP_PKEY_CTX - ctx = EVP_PKEY_CTX_new(pkey, NULL); - if (!ctx) goto badarg; - -/* enif_get_atom(env,argv[0],algo,1024,ERL_NIF_LATIN1); */ - - if (is_private) { - if (is_encrypt) { - /* private encrypt */ - if ((algo_init=EVP_PKEY_sign_init(ctx)) <= 0) { - /* fprintf(stderr,"BADARG %s private encrypt algo_init=%d %s:%d\r\n", algo, algo_init, __FILE__, __LINE__); */ - goto badarg; - } - } else { - /* private decrypt */ - if ((algo_init=EVP_PKEY_decrypt_init(ctx)) <= 0) { - /* fprintf(stderr,"BADARG %s private decrypt algo_init=%d %s:%d\r\n", algo, algo_init, __FILE__, __LINE__); */ - goto badarg; - } - } - } else { - if (is_encrypt) { - /* public encrypt */ - if ((algo_init=EVP_PKEY_encrypt_init(ctx)) <= 0) { - /* fprintf(stderr,"BADARG %s public encrypt algo_init=%d %s:%d\r\n", algo,algo_init,__FILE__, __LINE__); */ - goto badarg; - } - } else { - /* public decrypt */ - if ((algo_init=EVP_PKEY_verify_recover_init(ctx)) <= 0) { - /* fprintf(stderr,"BADARG %s public decrypt algo_init=%d %s:%d\r\n", algo,algo_init,__FILE__, __LINE__); */ - goto badarg; - } - } - } - - if (argv[0] == atom_rsa) { - if (crypt_opt.signature_md != NULL - && EVP_PKEY_CTX_set_signature_md(ctx, crypt_opt.signature_md) <= 0) - goto badarg; -#ifdef HAVE_RSA_SSLV23_PADDING - if (crypt_opt.rsa_padding == RSA_SSLV23_PADDING) { - if (is_encrypt) { - tmplen = size_of_RSA(pkey); - if (tmplen == 0) goto badarg; - if (!enif_alloc_binary(tmplen, &tmp_bin)) goto badarg; - if (RSA_padding_add_SSLv23(tmp_bin.data, tmplen, in_bin.data, in_bin.size) <= 0) - goto badarg; - in_bin = tmp_bin; - } - if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_NO_PADDING) <= 0) goto badarg; - } else -#endif - { - if (EVP_PKEY_CTX_set_rsa_padding(ctx, crypt_opt.rsa_padding) <= 0) goto badarg; - } -#ifdef HAVE_RSA_OAEP_MD - if (crypt_opt.rsa_padding == RSA_PKCS1_OAEP_PADDING) { - if (crypt_opt.rsa_oaep_md != NULL - && EVP_PKEY_CTX_set_rsa_oaep_md(ctx, crypt_opt.rsa_oaep_md) <= 0) - goto badarg; - if (crypt_opt.rsa_mgf1_md != NULL - && EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, crypt_opt.rsa_mgf1_md) <= 0) goto badarg; - if (crypt_opt.rsa_oaep_label.data != NULL && crypt_opt.rsa_oaep_label.size > 0) { - unsigned char *label_copy = NULL; - label_copy = OPENSSL_malloc(crypt_opt.rsa_oaep_label.size); - if (label_copy == NULL) goto badarg; - memcpy((void *)(label_copy), (const void *)(crypt_opt.rsa_oaep_label.data), - crypt_opt.rsa_oaep_label.size); - if (EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, label_copy, - crypt_opt.rsa_oaep_label.size) <= 0) { - OPENSSL_free(label_copy); - label_copy = NULL; - goto badarg; - } - } - } -#endif - } - - if (is_private) { - if (is_encrypt) { - /* private_encrypt */ - i = EVP_PKEY_sign(ctx, NULL, &outlen, in_bin.data, in_bin.size); - } else { - /* private_decrypt */ - i = EVP_PKEY_decrypt(ctx, NULL, &outlen, in_bin.data, in_bin.size); - } - } else { - if (is_encrypt) { - /* public_encrypt */ - i = EVP_PKEY_encrypt(ctx, NULL, &outlen, in_bin.data, in_bin.size); - } else { - /* public_decrypt */ - i = EVP_PKEY_verify_recover(ctx, NULL, &outlen, in_bin.data, in_bin.size); - } - } - /* fprintf(stderr,"i = %d %s:%d\r\n", i, __FILE__, __LINE__); */ - - if (i != 1) goto badarg; - - enif_alloc_binary(outlen, &out_bin); - - if (is_private) { - if (is_encrypt) { - /* private_encrypt */ - i = EVP_PKEY_sign(ctx, out_bin.data, &outlen, in_bin.data, in_bin.size); - } else { - /* private_decrypt */ - i = EVP_PKEY_decrypt(ctx, out_bin.data, &outlen, in_bin.data, in_bin.size); - } - } else { - if (is_encrypt) { - /* public_encrypt */ - i = EVP_PKEY_encrypt(ctx, out_bin.data, &outlen, in_bin.data, in_bin.size); - } else { - /* public_decrypt */ - i = EVP_PKEY_verify_recover(ctx, out_bin.data, &outlen, in_bin.data, in_bin.size); - } - } - -#else - /* Non-EVP cryptolib. Only support RSA */ - - if (argv[0] != atom_rsa) { - algo_init = -2; /* exitcode: notsup */ - goto badarg; - } - rsa = EVP_PKEY_get1_RSA(pkey); - enif_alloc_binary(RSA_size(rsa), &out_bin); - - if (is_private) { - if (is_encrypt) { - /* non-evp rsa private encrypt */ - ERL_VALGRIND_ASSERT_MEM_DEFINED(in_bin.data,in_bin.size); - i = RSA_private_encrypt(in_bin.size, in_bin.data, - out_bin.data, rsa, crypt_opt.rsa_padding); - if (i > 0) { - ERL_VALGRIND_MAKE_MEM_DEFINED(out_bin.data, i); - } - } else { - /* non-evp rsa private decrypt */ - i = RSA_private_decrypt(in_bin.size, in_bin.data, - out_bin.data, rsa, crypt_opt.rsa_padding); - if (i > 0) { - ERL_VALGRIND_MAKE_MEM_DEFINED(out_bin.data, i); - enif_realloc_binary(&out_bin, i); - } - } - } else { - if (is_encrypt) { - /* non-evp rsa public encrypt */ - ERL_VALGRIND_ASSERT_MEM_DEFINED(in_bin.data,in_bin.size); - i = RSA_public_encrypt(in_bin.size, in_bin.data, - out_bin.data, rsa, crypt_opt.rsa_padding); - if (i > 0) { - ERL_VALGRIND_MAKE_MEM_DEFINED(out_bin.data, i); - } - } else { - /* non-evp rsa public decrypt */ - i = RSA_public_decrypt(in_bin.size, in_bin.data, - out_bin.data, rsa, crypt_opt.rsa_padding); - if (i > 0) { - ERL_VALGRIND_MAKE_MEM_DEFINED(out_bin.data, i); - enif_realloc_binary(&out_bin, i); - } - } - } - - outlen = i; - RSA_free(rsa); -#endif - - if ((i > 0) && argv[0] == atom_rsa && !is_encrypt) { -#ifdef HAVE_RSA_SSLV23_PADDING - if (crypt_opt.rsa_padding == RSA_SSLV23_PADDING) { - unsigned char *p; - tmplen = size_of_RSA(pkey); - if (tmplen == 0) goto badarg; - if (!enif_alloc_binary(tmplen, &tmp_bin)) - goto badarg; - p = out_bin.data; - p++; - i = RSA_padding_check_SSLv23(tmp_bin.data, tmplen, p, out_bin.size - 1, tmplen); - if (i >= 0) { - outlen = i; - in_bin = out_bin; - out_bin = tmp_bin; - tmp_bin = in_bin; - i = 1; - } - } -#endif - } - - if (tmp_bin.data != NULL) { - enif_release_binary(&tmp_bin); - } - -#ifdef HAS_EVP_PKEY_CTX - EVP_PKEY_CTX_free(ctx); -#else -#endif - EVP_PKEY_free(pkey); - if (i > 0) { - ERL_VALGRIND_MAKE_MEM_DEFINED(out_bin.data, outlen); - if (outlen != out_bin.size) { - enif_realloc_binary(&out_bin, outlen); - ERL_VALGRIND_ASSERT_MEM_DEFINED(out_bin.data, outlen); - } - return enif_make_binary(env, &out_bin); - } else { - enif_release_binary(&out_bin); - return atom_error; - } - - badarg: - if (out_bin.data != NULL) { - enif_release_binary(&out_bin); - } - if (tmp_bin.data != NULL) { - enif_release_binary(&tmp_bin); - } -#ifdef HAS_EVP_PKEY_CTX - EVP_PKEY_CTX_free(ctx); -#else -#endif - EVP_PKEY_free(pkey); - if (algo_init == -2) - return atom_notsup; - else - return enif_make_badarg(env); -} - - - -/*--------------------------------*/ -static ERL_NIF_TERM privkey_to_pubkey_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ /* (Algorithm, PrivKey | KeyMap) */ - EVP_PKEY *pkey; - ERL_NIF_TERM alg = argv[0]; - ERL_NIF_TERM result[8]; - if (get_pkey_private_key(env, alg, argv[1], &pkey) != PKEY_OK) { - return enif_make_badarg(env); - } - - if (alg == atom_rsa) { - const BIGNUM *n = NULL, *e = NULL, *d = NULL; - RSA *rsa = EVP_PKEY_get1_RSA(pkey); - if (rsa) { - RSA_get0_key(rsa, &n, &e, &d); - result[0] = bin_from_bn(env, e); // Exponent E - result[1] = bin_from_bn(env, n); // Modulus N = p*q - RSA_free(rsa); - EVP_PKEY_free(pkey); - return enif_make_list_from_array(env, result, 2); - } - - } else if (argv[0] == atom_dss) { - const BIGNUM *p = NULL, *q = NULL, *g = NULL, *pub_key = NULL; - DSA *dsa = EVP_PKEY_get1_DSA(pkey); - if (dsa) { - DSA_get0_pqg(dsa, &p, &q, &g); - DSA_get0_key(dsa, &pub_key, NULL); - result[0] = bin_from_bn(env, p); - result[1] = bin_from_bn(env, q); - result[2] = bin_from_bn(env, g); - result[3] = bin_from_bn(env, pub_key); - DSA_free(dsa); - EVP_PKEY_free(pkey); - return enif_make_list_from_array(env, result, 4); - } - - } else if (argv[0] == atom_ecdsa) { -#if defined(HAVE_EC) - /* not yet implemented - EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pkey); - if (ec) { - / * Example of result: - { - Curve = {Field, Prime, Point, Order, CoFactor} = - { - Field = {prime_field,<<255,...,255>>}, - Prime = {<<255,...,252>>, - <<90,...,75>>, - <<196,...,144>> - }, - Point = <<4,...,245>>, - Order = <<255,...,81>>, - CoFactor = <<1>> - }, - Key = <<151,...,62>> - } - or - { - Curve = - {characteristic_two_field, - M, - Basis = {tpbasis, _} - | {ppbasis, k1, k2, k3} - }, - Key - } - * / - EVP_PKEY_free(pkey); - return enif_make_list_from_array(env, ..., ...); - */ -#endif - } - - if (pkey) EVP_PKEY_free(pkey); - return enif_make_badarg(env); -} - -/*================================================================*/ - -static ERL_NIF_TERM rand_seed_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - ErlNifBinary seed_bin; - - if (!enif_inspect_binary(env, argv[0], &seed_bin)) - return enif_make_badarg(env); - RAND_seed(seed_bin.data,seed_bin.size); - return atom_ok; -} - -/*================================================================*/ -/* Engine */ -/*================================================================*/ -static ERL_NIF_TERM engine_by_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (EngineId) */ -#ifdef HAS_ENGINE_SUPPORT - ERL_NIF_TERM ret; - ErlNifBinary engine_id_bin; - char *engine_id; - ENGINE *engine; - struct engine_ctx *ctx; - - // Get Engine Id - if(!enif_inspect_binary(env, argv[0], &engine_id_bin)) { - PRINTF_ERR0("engine_by_id_nif Leaved: badarg"); - return enif_make_badarg(env); - } else { - engine_id = enif_alloc(engine_id_bin.size+1); - (void) memcpy(engine_id, engine_id_bin.data, engine_id_bin.size); - engine_id[engine_id_bin.size] = '\0'; - } - - engine = ENGINE_by_id(engine_id); - if(!engine) { - enif_free(engine_id); - PRINTF_ERR0("engine_by_id_nif Leaved: {error, bad_engine_id}"); - return enif_make_tuple2(env, atom_error, atom_bad_engine_id); - } - - ctx = enif_alloc_resource(engine_ctx_rtype, sizeof(struct engine_ctx)); - ctx->engine = engine; - ctx->id = engine_id; - - ret = enif_make_resource(env, ctx); - enif_release_resource(ctx); - - return enif_make_tuple2(env, atom_ok, ret); -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM engine_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Engine) */ -#ifdef HAS_ENGINE_SUPPORT - ERL_NIF_TERM ret = atom_ok; - struct engine_ctx *ctx; - - // Get Engine - if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { - PRINTF_ERR0("engine_init_nif Leaved: Parameter not an engine resource object"); - return enif_make_badarg(env); - } - if (!ENGINE_init(ctx->engine)) { - //ERR_print_errors_fp(stderr); - PRINTF_ERR0("engine_init_nif Leaved: {error, engine_init_failed}"); - return enif_make_tuple2(env, atom_error, atom_engine_init_failed); - } - - return ret; -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM engine_free_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Engine) */ -#ifdef HAS_ENGINE_SUPPORT - struct engine_ctx *ctx; - - // Get Engine - if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { - PRINTF_ERR0("engine_free_nif Leaved: Parameter not an engine resource object"); - return enif_make_badarg(env); - } - - ENGINE_free(ctx->engine); - return atom_ok; -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM engine_finish_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Engine) */ -#ifdef HAS_ENGINE_SUPPORT - struct engine_ctx *ctx; - - // Get Engine - if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { - PRINTF_ERR0("engine_finish_nif Leaved: Parameter not an engine resource object"); - return enif_make_badarg(env); - } - - ENGINE_finish(ctx->engine); - return atom_ok; -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM engine_load_dynamic_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* () */ -#ifdef HAS_ENGINE_SUPPORT - ENGINE_load_dynamic(); - return atom_ok; -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM engine_ctrl_cmd_strings_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Engine, Commands) */ -#ifdef HAS_ENGINE_SUPPORT - ERL_NIF_TERM ret = atom_ok; - unsigned int cmds_len = 0; - char **cmds = NULL; - struct engine_ctx *ctx; - int i, optional = 0; - - // Get Engine - if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { - PRINTF_ERR0("engine_ctrl_cmd_strings_nif Leaved: Parameter not an engine resource object"); - return enif_make_badarg(env); - } - - PRINTF_ERR1("Engine Id: %s\r\n", ENGINE_get_id(ctx->engine)); - - // Get Command List - if(!enif_get_list_length(env, argv[1], &cmds_len)) { - PRINTF_ERR0("engine_ctrl_cmd_strings_nif Leaved: Bad Command List"); - return enif_make_badarg(env); - } else { - cmds_len *= 2; // Key-Value list from erlang - cmds = enif_alloc((cmds_len+1)*sizeof(char*)); - if(get_engine_load_cmd_list(env, argv[1], cmds, 0)) { - PRINTF_ERR0("engine_ctrl_cmd_strings_nif Leaved: Couldn't read Command List"); - ret = enif_make_badarg(env); - goto error; - } - } - - if(!enif_get_int(env, argv[2], &optional)) { - PRINTF_ERR0("engine_ctrl_cmd_strings_nif Leaved: Parameter optional not an integer"); - return enif_make_badarg(env); - } - - for(i = 0; i < cmds_len; i+=2) { - PRINTF_ERR2("Cmd: %s:%s\r\n", - cmds[i] ? cmds[i] : "(NULL)", - cmds[i+1] ? cmds[i+1] : "(NULL)"); - if(!ENGINE_ctrl_cmd_string(ctx->engine, cmds[i], cmds[i+1], optional)) { - PRINTF_ERR2("Command failed: %s:%s\r\n", - cmds[i] ? cmds[i] : "(NULL)", - cmds[i+1] ? cmds[i+1] : "(NULL)"); - //ENGINE_free(ctx->engine); - ret = enif_make_tuple2(env, atom_error, atom_ctrl_cmd_failed); - PRINTF_ERR0("engine_ctrl_cmd_strings_nif Leaved: {error, ctrl_cmd_failed}"); - goto error; - } - } - - error: - for(i = 0; cmds != NULL && cmds[i] != NULL; i++) - enif_free(cmds[i]); - enif_free(cmds); - return ret; -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM engine_add_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Engine) */ -#ifdef HAS_ENGINE_SUPPORT - struct engine_ctx *ctx; - - // Get Engine - if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { - PRINTF_ERR0("engine_add_nif Leaved: Parameter not an engine resource object"); - return enif_make_badarg(env); - } - - if (!ENGINE_add(ctx->engine)) { - PRINTF_ERR0("engine_add_nif Leaved: {error, add_engine_failed}"); - return enif_make_tuple2(env, atom_error, atom_add_engine_failed); - } - return atom_ok; -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM engine_remove_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Engine) */ -#ifdef HAS_ENGINE_SUPPORT - struct engine_ctx *ctx; - - // Get Engine - if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { - PRINTF_ERR0("engine_remove_nif Leaved: Parameter not an engine resource object"); - return enif_make_badarg(env); - } - - if (!ENGINE_remove(ctx->engine)) { - PRINTF_ERR0("engine_remove_nif Leaved: {error, remove_engine_failed}"); - return enif_make_tuple2(env, atom_error, atom_remove_engine_failed); - } - return atom_ok; -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM engine_register_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Engine, EngineMethod) */ -#ifdef HAS_ENGINE_SUPPORT - struct engine_ctx *ctx; - unsigned int method; - - // Get Engine - if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { - PRINTF_ERR0("engine_register_nif Leaved: Parameter not an engine resource object"); - return enif_make_badarg(env); - } - // Get Method - if (!enif_get_uint(env, argv[1], &method)) { - PRINTF_ERR0("engine_register_nif Leaved: Parameter Method not an uint"); - return enif_make_badarg(env); - } - - switch(method) - { -#ifdef ENGINE_METHOD_RSA - case ENGINE_METHOD_RSA: - if (!ENGINE_register_RSA(ctx->engine)) - return enif_make_tuple2(env, atom_error, atom_register_engine_failed); - break; -#endif -#ifdef ENGINE_METHOD_DSA - case ENGINE_METHOD_DSA: - if (!ENGINE_register_DSA(ctx->engine)) - return enif_make_tuple2(env, atom_error, atom_register_engine_failed); - break; -#endif -#ifdef ENGINE_METHOD_DH - case ENGINE_METHOD_DH: - if (!ENGINE_register_DH(ctx->engine)) - return enif_make_tuple2(env, atom_error, atom_register_engine_failed); - break; -#endif -#ifdef ENGINE_METHOD_RAND - case ENGINE_METHOD_RAND: - if (!ENGINE_register_RAND(ctx->engine)) - return enif_make_tuple2(env, atom_error, atom_register_engine_failed); - break; -#endif -#ifdef ENGINE_METHOD_ECDH - case ENGINE_METHOD_ECDH: - if (!ENGINE_register_ECDH(ctx->engine)) - return enif_make_tuple2(env, atom_error, atom_register_engine_failed); - break; -#endif -#ifdef ENGINE_METHOD_ECDSA - case ENGINE_METHOD_ECDSA: - if (!ENGINE_register_ECDSA(ctx->engine)) - return enif_make_tuple2(env, atom_error, atom_register_engine_failed); - break; -#endif -#ifdef ENGINE_METHOD_STORE - case ENGINE_METHOD_STORE: - if (!ENGINE_register_STORE(ctx->engine)) - return enif_make_tuple2(env, atom_error, atom_register_engine_failed); - break; -#endif -#ifdef ENGINE_METHOD_CIPHERS - case ENGINE_METHOD_CIPHERS: - if (!ENGINE_register_ciphers(ctx->engine)) - return enif_make_tuple2(env, atom_error, atom_register_engine_failed); - break; -#endif -#ifdef ENGINE_METHOD_DIGESTS - case ENGINE_METHOD_DIGESTS: - if (!ENGINE_register_digests(ctx->engine)) - return enif_make_tuple2(env, atom_error, atom_register_engine_failed); - break; -#endif -#ifdef ENGINE_METHOD_PKEY_METHS - case ENGINE_METHOD_PKEY_METHS: - if (!ENGINE_register_pkey_meths(ctx->engine)) - return enif_make_tuple2(env, atom_error, atom_register_engine_failed); - break; -#endif -#ifdef ENGINE_METHOD_PKEY_ASN1_METHS - case ENGINE_METHOD_PKEY_ASN1_METHS: - if (!ENGINE_register_pkey_asn1_meths(ctx->engine)) - return enif_make_tuple2(env, atom_error, atom_register_engine_failed); - break; -#endif -#ifdef ENGINE_METHOD_EC - case ENGINE_METHOD_EC: - if (!ENGINE_register_EC(ctx->engine)) - return enif_make_tuple2(env, atom_error, atom_register_engine_failed); - break; -#endif - default: - return enif_make_tuple2(env, atom_error, atom_engine_method_not_supported); - break; - } - return atom_ok; -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM engine_unregister_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Engine, EngineMethod) */ -#ifdef HAS_ENGINE_SUPPORT - struct engine_ctx *ctx; - unsigned int method; - - // Get Engine - if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { - PRINTF_ERR0("engine_unregister_nif Leaved: Parameter not an engine resource object"); - return enif_make_badarg(env); - } - // Get Method - if (!enif_get_uint(env, argv[1], &method)) { - PRINTF_ERR0("engine_unregister_nif Leaved: Parameter Method not an uint"); - return enif_make_badarg(env); - } - - switch(method) - { -#ifdef ENGINE_METHOD_RSA - case ENGINE_METHOD_RSA: - ENGINE_unregister_RSA(ctx->engine); - break; -#endif -#ifdef ENGINE_METHOD_DSA - case ENGINE_METHOD_DSA: - ENGINE_unregister_DSA(ctx->engine); - break; -#endif -#ifdef ENGINE_METHOD_DH - case ENGINE_METHOD_DH: - ENGINE_unregister_DH(ctx->engine); - break; -#endif -#ifdef ENGINE_METHOD_RAND - case ENGINE_METHOD_RAND: - ENGINE_unregister_RAND(ctx->engine); - break; -#endif -#ifdef ENGINE_METHOD_ECDH - case ENGINE_METHOD_ECDH: - ENGINE_unregister_ECDH(ctx->engine); - break; -#endif -#ifdef ENGINE_METHOD_ECDSA - case ENGINE_METHOD_ECDSA: - ENGINE_unregister_ECDSA(ctx->engine); - break; -#endif -#ifdef ENGINE_METHOD_STORE - case ENGINE_METHOD_STORE: - ENGINE_unregister_STORE(ctx->engine); - break; -#endif -#ifdef ENGINE_METHOD_CIPHERS - case ENGINE_METHOD_CIPHERS: - ENGINE_unregister_ciphers(ctx->engine); - break; -#endif -#ifdef ENGINE_METHOD_DIGESTS - case ENGINE_METHOD_DIGESTS: - ENGINE_unregister_digests(ctx->engine); - break; -#endif -#ifdef ENGINE_METHOD_PKEY_METHS - case ENGINE_METHOD_PKEY_METHS: - ENGINE_unregister_pkey_meths(ctx->engine); - break; -#endif -#ifdef ENGINE_METHOD_PKEY_ASN1_METHS - case ENGINE_METHOD_PKEY_ASN1_METHS: - ENGINE_unregister_pkey_asn1_meths(ctx->engine); - break; -#endif -#ifdef ENGINE_METHOD_EC - case ENGINE_METHOD_EC: - ENGINE_unregister_EC(ctx->engine); - break; -#endif - default: - break; - } - return atom_ok; -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM engine_get_first_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Engine) */ -#ifdef HAS_ENGINE_SUPPORT - ERL_NIF_TERM ret; - ENGINE *engine; - ErlNifBinary engine_bin; - struct engine_ctx *ctx; - - engine = ENGINE_get_first(); - if(!engine) { - enif_alloc_binary(0, &engine_bin); - engine_bin.size = 0; - return enif_make_tuple2(env, atom_ok, enif_make_binary(env, &engine_bin)); - } - - ctx = enif_alloc_resource(engine_ctx_rtype, sizeof(struct engine_ctx)); - ctx->engine = engine; - ctx->id = NULL; - - ret = enif_make_resource(env, ctx); - enif_release_resource(ctx); - - return enif_make_tuple2(env, atom_ok, ret); -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM engine_get_next_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Engine) */ -#ifdef HAS_ENGINE_SUPPORT - ERL_NIF_TERM ret; - ENGINE *engine; - ErlNifBinary engine_bin; - struct engine_ctx *ctx, *next_ctx; - - // Get Engine - if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { - PRINTF_ERR0("engine_get_next_nif Leaved: Parameter not an engine resource object"); - return enif_make_badarg(env); - } - engine = ENGINE_get_next(ctx->engine); - if (!engine) { - enif_alloc_binary(0, &engine_bin); - engine_bin.size = 0; - return enif_make_tuple2(env, atom_ok, enif_make_binary(env, &engine_bin)); - } - - next_ctx = enif_alloc_resource(engine_ctx_rtype, sizeof(struct engine_ctx)); - next_ctx->engine = engine; - next_ctx->id = NULL; - - ret = enif_make_resource(env, next_ctx); - enif_release_resource(next_ctx); - - return enif_make_tuple2(env, atom_ok, ret); -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM engine_get_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Engine) */ -#ifdef HAS_ENGINE_SUPPORT - ErlNifBinary engine_id_bin; - const char *engine_id; - int size; - struct engine_ctx *ctx; - - // Get Engine - if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { - PRINTF_ERR0("engine_get_id_nif Leaved: Parameter not an engine resource object"); - return enif_make_badarg(env); - } - - engine_id = ENGINE_get_id(ctx->engine); - if (!engine_id) { - enif_alloc_binary(0, &engine_id_bin); - engine_id_bin.size = 0; - return enif_make_binary(env, &engine_id_bin); - } - - size = strlen(engine_id); - enif_alloc_binary(size, &engine_id_bin); - engine_id_bin.size = size; - memcpy(engine_id_bin.data, engine_id, size); - - return enif_make_binary(env, &engine_id_bin); -#else - return atom_notsup; -#endif -} - -static ERL_NIF_TERM engine_get_name_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Engine) */ -#ifdef HAS_ENGINE_SUPPORT - ErlNifBinary engine_name_bin; - const char *engine_name; - int size; - struct engine_ctx *ctx; - - // Get Engine - if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { - PRINTF_ERR0("engine_get_id_nif Leaved: Parameter not an engine resource object"); - return enif_make_badarg(env); - } - - engine_name = ENGINE_get_name(ctx->engine); - if (!engine_name) { - enif_alloc_binary(0, &engine_name_bin); - engine_name_bin.size = 0; - return enif_make_binary(env, &engine_name_bin); - } - - size = strlen(engine_name); - enif_alloc_binary(size, &engine_name_bin); - engine_name_bin.size = size; - memcpy(engine_name_bin.data, engine_name, size); - - return enif_make_binary(env, &engine_name_bin); -#else - return atom_notsup; -#endif -} - -#ifdef HAS_ENGINE_SUPPORT -static int get_engine_load_cmd_list(ErlNifEnv* env, const ERL_NIF_TERM term, char **cmds, int i) -{ - ERL_NIF_TERM head, tail; - const ERL_NIF_TERM *tmp_tuple; - ErlNifBinary tmpbin; - int arity; - char* tmpstr; - - if(!enif_is_empty_list(env, term)) { - if(!enif_get_list_cell(env, term, &head, &tail)) { - cmds[i] = NULL; - return -1; - } else { - if(!enif_get_tuple(env, head, &arity, &tmp_tuple) || arity != 2) { - cmds[i] = NULL; - return -1; - } else { - if(!enif_inspect_binary(env, tmp_tuple[0], &tmpbin)) { - cmds[i] = NULL; - return -1; - } else { - tmpstr = enif_alloc(tmpbin.size+1); - (void) memcpy(tmpstr, tmpbin.data, tmpbin.size); - tmpstr[tmpbin.size] = '\0'; - cmds[i++] = tmpstr; - } - if(!enif_inspect_binary(env, tmp_tuple[1], &tmpbin)) { - cmds[i] = NULL; - return -1; - } else { - if(tmpbin.size == 0) - cmds[i++] = NULL; - else { - tmpstr = enif_alloc(tmpbin.size+1); - (void) memcpy(tmpstr, tmpbin.data, tmpbin.size); - tmpstr[tmpbin.size] = '\0'; - cmds[i++] = tmpstr; - } - } - return get_engine_load_cmd_list(env, tail, cmds, i); - } - } - } else { - cmds[i] = NULL; - return 0; - } -} -#endif - -static ERL_NIF_TERM engine_get_all_methods_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* () */ -#ifdef HAS_ENGINE_SUPPORT - ERL_NIF_TERM method_array[12]; - int i = 0; - -#ifdef ENGINE_METHOD_RSA - method_array[i++] = atom_engine_method_rsa; -#endif -#ifdef ENGINE_METHOD_DSA - method_array[i++] = atom_engine_method_dsa; -#endif -#ifdef ENGINE_METHOD_DH - method_array[i++] = atom_engine_method_dh; -#endif -#ifdef ENGINE_METHOD_RAND - method_array[i++] = atom_engine_method_rand; -#endif -#ifdef ENGINE_METHOD_ECDH - method_array[i++] = atom_engine_method_ecdh; -#endif -#ifdef ENGINE_METHOD_ECDSA - method_array[i++] = atom_engine_method_ecdsa; -#endif -#ifdef ENGINE_METHOD_STORE - method_array[i++] = atom_engine_method_store; -#endif -#ifdef ENGINE_METHOD_CIPHERS - method_array[i++] = atom_engine_method_ciphers; -#endif -#ifdef ENGINE_METHOD_DIGESTS - method_array[i++] = atom_engine_method_digests; -#endif -#ifdef ENGINE_METHOD_PKEY_METHS - method_array[i++] = atom_engine_method_pkey_meths; -#endif -#ifdef ENGINE_METHOD_PKEY_ASN1_METHS - method_array[i++] = atom_engine_method_pkey_asn1_meths; -#endif -#ifdef ENGINE_METHOD_EC - method_array[i++] = atom_engine_method_ec; -#endif - - return enif_make_list_from_array(env, method_array, i); -#else - return atom_notsup; -#endif -} diff --git a/lib/crypto/c_src/dh.c b/lib/crypto/c_src/dh.c new file mode 100644 index 0000000000..0c18ad7a3f --- /dev/null +++ b/lib/crypto/c_src/dh.c @@ -0,0 +1,204 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "dh.h" +#include "bn.h" + +ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (PrivKey|undefined, DHParams=[P,G], Mpint, Len|0) */ + DH *dh_params = NULL; + int mpint; /* 0 or 4 */ + + { + ERL_NIF_TERM head, tail; + BIGNUM + *dh_p = NULL, + *dh_g = NULL, + *priv_key_in = NULL; + unsigned long + len = 0; + + if (!(get_bn_from_bin(env, argv[0], &priv_key_in) + || argv[0] == atom_undefined) + || !enif_get_list_cell(env, argv[1], &head, &tail) + || !get_bn_from_bin(env, head, &dh_p) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_bin(env, head, &dh_g) + || !enif_is_empty_list(env, tail) + || !enif_get_int(env, argv[2], &mpint) || (mpint & ~4) + || !enif_get_ulong(env, argv[3], &len) + + /* Load dh_params with values to use by the generator. + Mem mgmnt transfered from dh_p etc to dh_params */ + || !(dh_params = DH_new()) + || (priv_key_in && !DH_set0_key(dh_params, NULL, priv_key_in)) + || !DH_set0_pqg(dh_params, dh_p, NULL, dh_g) + ) { + if (priv_key_in) BN_free(priv_key_in); + if (dh_p) BN_free(dh_p); + if (dh_g) BN_free(dh_g); + if (dh_params) DH_free(dh_params); + return enif_make_badarg(env); + } + + if (len) { + if (len < BN_num_bits(dh_p)) + DH_set_length(dh_params, len); + else { + if (priv_key_in) BN_free(priv_key_in); + if (dh_p) BN_free(dh_p); + if (dh_g) BN_free(dh_g); + if (dh_params) DH_free(dh_params); + return enif_make_badarg(env); + } + } + } + +#ifdef HAS_EVP_PKEY_CTX + { + EVP_PKEY_CTX *ctx; + EVP_PKEY *dhkey, *params; + int success; + + params = EVP_PKEY_new(); + success = EVP_PKEY_set1_DH(params, dh_params); /* set the key referenced by params to dh_params... */ + DH_free(dh_params); /* ...dh_params (and params) must be freed */ + if (!success) return atom_error; + + ctx = EVP_PKEY_CTX_new(params, NULL); + EVP_PKEY_free(params); + if (!ctx) { + return atom_error; + } + + if (!EVP_PKEY_keygen_init(ctx)) { + /* EVP_PKEY_CTX_free(ctx); */ + return atom_error; + } + + dhkey = EVP_PKEY_new(); + if (!EVP_PKEY_keygen(ctx, &dhkey)) { /* "performs a key generation operation, the ... */ + /*... generated key is written to ppkey." (=last arg) */ + /* EVP_PKEY_CTX_free(ctx); */ + /* EVP_PKEY_free(dhkey); */ + return atom_error; + } + + dh_params = EVP_PKEY_get1_DH(dhkey); /* return the referenced key. dh_params and dhkey must be freed */ + EVP_PKEY_free(dhkey); + if (!dh_params) { + /* EVP_PKEY_CTX_free(ctx); */ + return atom_error; + } + EVP_PKEY_CTX_free(ctx); + } +#else + if (!DH_generate_key(dh_params)) return atom_error; +#endif + { + unsigned char *pub_ptr, *prv_ptr; + int pub_len, prv_len; + ERL_NIF_TERM ret_pub, ret_prv; + const BIGNUM *pub_key_gen, *priv_key_gen; + + DH_get0_key(dh_params, + &pub_key_gen, &priv_key_gen); /* Get pub_key_gen and priv_key_gen. + "The values point to the internal representation of + the public key and private key values. This memory + should not be freed directly." says man */ + pub_len = BN_num_bytes(pub_key_gen); + prv_len = BN_num_bytes(priv_key_gen); + pub_ptr = enif_make_new_binary(env, pub_len+mpint, &ret_pub); + prv_ptr = enif_make_new_binary(env, prv_len+mpint, &ret_prv); + if (mpint) { + put_int32(pub_ptr, pub_len); pub_ptr += 4; + put_int32(prv_ptr, prv_len); prv_ptr += 4; + } + BN_bn2bin(pub_key_gen, pub_ptr); + BN_bn2bin(priv_key_gen, prv_ptr); + ERL_VALGRIND_MAKE_MEM_DEFINED(pub_ptr, pub_len); + ERL_VALGRIND_MAKE_MEM_DEFINED(prv_ptr, prv_len); + + DH_free(dh_params); + + return enif_make_tuple2(env, ret_pub, ret_prv); + } +} + +ERL_NIF_TERM dh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (OthersPublicKey, MyPrivateKey, DHParams=[P,G]) */ + BIGNUM *other_pub_key = NULL, + *dh_p = NULL, + *dh_g = NULL; + DH *dh_priv = DH_new(); + + /* Check the arguments and get + my private key (dh_priv), + the peer's public key (other_pub_key), + the parameters p & q + */ + + { + BIGNUM *dummy_pub_key = NULL, + *priv_key = NULL; + ERL_NIF_TERM head, tail; + + if (!get_bn_from_bin(env, argv[0], &other_pub_key) + || !get_bn_from_bin(env, argv[1], &priv_key) + || !enif_get_list_cell(env, argv[2], &head, &tail) + || !get_bn_from_bin(env, head, &dh_p) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_bin(env, head, &dh_g) + || !enif_is_empty_list(env, tail) + + /* Note: DH_set0_key() does not allow setting only the + * private key, although DH_compute_key() does not use the + * public key. Work around this limitation by setting + * the public key to a copy of the private key. + */ + || !(dummy_pub_key = BN_dup(priv_key)) + || !DH_set0_key(dh_priv, dummy_pub_key, priv_key) + || !DH_set0_pqg(dh_priv, dh_p, NULL, dh_g) + ) { + if (dh_p) BN_free(dh_p); + if (dh_g) BN_free(dh_g); + if (other_pub_key) BN_free(other_pub_key); + if (dummy_pub_key) BN_free(dummy_pub_key); + if (priv_key) BN_free(priv_key); + return enif_make_badarg(env); + } + } + { + ErlNifBinary ret_bin; + int size; + + enif_alloc_binary(DH_size(dh_priv), &ret_bin); + size = DH_compute_key(ret_bin.data, other_pub_key, dh_priv); + BN_free(other_pub_key); + DH_free(dh_priv); + if (size<=0) { + enif_release_binary(&ret_bin); + return atom_error; + } + + if (size != ret_bin.size) enif_realloc_binary(&ret_bin, size); + return enif_make_binary(env, &ret_bin); + } +} diff --git a/lib/crypto/c_src/dh.h b/lib/crypto/c_src/dh.h new file mode 100644 index 0000000000..a996b0ea28 --- /dev/null +++ b/lib/crypto/c_src/dh.h @@ -0,0 +1,29 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_DH_H__ +#define E_DH_H__ 1 + +#include "common.h" + +ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM dh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_DH_H__ */ diff --git a/lib/crypto/c_src/digest.c b/lib/crypto/c_src/digest.c new file mode 100644 index 0000000000..9e6199030d --- /dev/null +++ b/lib/crypto/c_src/digest.c @@ -0,0 +1,111 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "digest.h" + +static struct digest_type_t digest_types[] = +{ + {{"md4"}, {&EVP_md4}}, + {{"md5"}, {&EVP_md5}}, + {{"ripemd160"}, {&EVP_ripemd160}}, + {{"sha"}, {&EVP_sha1}}, + {{"sha224"}, +#ifdef HAVE_SHA224 + {&EVP_sha224} +#else + {NULL} +#endif + }, + {{"sha256"}, +#ifdef HAVE_SHA256 + {&EVP_sha256} +#else + {NULL} +#endif + }, + {{"sha384"}, +#ifdef HAVE_SHA384 + {&EVP_sha384} +#else + {NULL} +#endif + }, + {{"sha512"}, +#ifdef HAVE_SHA512 + {&EVP_sha512} +#else + {NULL} +#endif + }, + {{"sha3_224"}, +#ifdef HAVE_SHA3_224 + {&EVP_sha3_224} +#else + {NULL} +#endif + }, + {{"sha3_256"}, +#ifdef HAVE_SHA3_256 + {&EVP_sha3_256} +#else + {NULL} +#endif + }, + {{"sha3_384"}, +#ifdef HAVE_SHA3_384 + {&EVP_sha3_384} +#else + {NULL} +#endif + }, + {{"sha3_512"}, +#ifdef HAVE_SHA3_512 + {&EVP_sha3_512} +#else + {NULL} +#endif + }, + + {{NULL}} +}; + +void init_digest_types(ErlNifEnv* env) +{ + struct digest_type_t* p = digest_types; + + for (p = digest_types; p->type.str; p++) { + p->type.atom = enif_make_atom(env, p->type.str); + if (p->md.funcp) + p->md.p = p->md.funcp(); + } + p->type.atom = atom_false; /* end marker */ +} + +struct digest_type_t* get_digest_type(ERL_NIF_TERM type) +{ + struct digest_type_t* p = NULL; + for (p = digest_types; p->type.atom != atom_false; p++) { + if (type == p->type.atom) { + return p; + } + } + return NULL; +} + diff --git a/lib/crypto/c_src/digest.h b/lib/crypto/c_src/digest.h new file mode 100644 index 0000000000..06852416cf --- /dev/null +++ b/lib/crypto/c_src/digest.h @@ -0,0 +1,40 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_DIGEST_H__ +#define E_DIGEST_H__ 1 + +#include "common.h" + +struct digest_type_t { + union { + const char* str; /* before init, NULL for end-of-table */ + ERL_NIF_TERM atom; /* after init, 'false' for end-of-table */ + }type; + union { + const EVP_MD* (*funcp)(void); /* before init, NULL if notsup */ + const EVP_MD* p; /* after init, NULL if notsup */ + }md; +}; + +void init_digest_types(ErlNifEnv* env); +struct digest_type_t* get_digest_type(ERL_NIF_TERM type); + +#endif /* E_DIGEST_H__ */ diff --git a/lib/crypto/c_src/dss.c b/lib/crypto/c_src/dss.c new file mode 100644 index 0000000000..9d39241382 --- /dev/null +++ b/lib/crypto/c_src/dss.c @@ -0,0 +1,85 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "dss.h" +#include "bn.h" + +int get_dss_private_key(ErlNifEnv* env, ERL_NIF_TERM key, DSA *dsa) +{ + /* key=[P,Q,G,KEY] */ + ERL_NIF_TERM head, tail; + BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL; + BIGNUM *dummy_pub_key, *priv_key = NULL; + + if (!enif_get_list_cell(env, key, &head, &tail) + || !get_bn_from_bin(env, head, &dsa_p) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_bin(env, head, &dsa_q) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_bin(env, head, &dsa_g) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_bin(env, head, &priv_key) + || !enif_is_empty_list(env,tail)) { + if (dsa_p) BN_free(dsa_p); + if (dsa_q) BN_free(dsa_q); + if (dsa_g) BN_free(dsa_g); + if (priv_key) BN_free(priv_key); + return 0; + } + + /* Note: DSA_set0_key() does not allow setting only the + * private key, although DSA_sign() does not use the + * public key. Work around this limitation by setting + * the public key to a copy of the private key. + */ + dummy_pub_key = BN_dup(priv_key); + + DSA_set0_pqg(dsa, dsa_p, dsa_q, dsa_g); + DSA_set0_key(dsa, dummy_pub_key, priv_key); + return 1; +} + + +int get_dss_public_key(ErlNifEnv* env, ERL_NIF_TERM key, DSA *dsa) +{ + /* key=[P, Q, G, Y] */ + ERL_NIF_TERM head, tail; + BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL, *dsa_y = NULL; + + if (!enif_get_list_cell(env, key, &head, &tail) + || !get_bn_from_bin(env, head, &dsa_p) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_bin(env, head, &dsa_q) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_bin(env, head, &dsa_g) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_bin(env, head, &dsa_y) + || !enif_is_empty_list(env,tail)) { + if (dsa_p) BN_free(dsa_p); + if (dsa_q) BN_free(dsa_q); + if (dsa_g) BN_free(dsa_g); + if (dsa_y) BN_free(dsa_y); + return 0; + } + + DSA_set0_pqg(dsa, dsa_p, dsa_q, dsa_g); + DSA_set0_key(dsa, dsa_y, NULL); + return 1; +} diff --git a/lib/crypto/c_src/dss.h b/lib/crypto/c_src/dss.h new file mode 100644 index 0000000000..3275657e98 --- /dev/null +++ b/lib/crypto/c_src/dss.h @@ -0,0 +1,29 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_DSS_H__ +#define E_DSS_H__ 1 + +#include "common.h" + +int get_dss_private_key(ErlNifEnv* env, ERL_NIF_TERM key, DSA *dsa); +int get_dss_public_key(ErlNifEnv* env, ERL_NIF_TERM key, DSA *dsa); + +#endif /* E_DSS_H__ */ diff --git a/lib/crypto/c_src/ec.c b/lib/crypto/c_src/ec.c new file mode 100644 index 0000000000..6d831ec9d2 --- /dev/null +++ b/lib/crypto/c_src/ec.c @@ -0,0 +1,360 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "ec.h" +#include "bn.h" + +#ifdef HAVE_EC +static EC_KEY* ec_key_new(ErlNifEnv* env, ERL_NIF_TERM curve_arg); +static ERL_NIF_TERM point2term(ErlNifEnv* env, + const EC_GROUP *group, + const EC_POINT *point, + point_conversion_form_t form); + +ERL_NIF_TERM make_badarg_maybe(ErlNifEnv* env) +{ + ERL_NIF_TERM reason; + if (enif_has_pending_exception(env, &reason)) + return reason; /* dummy return value ignored */ + else + return enif_make_badarg(env); +} + +static EC_KEY* ec_key_new(ErlNifEnv* env, ERL_NIF_TERM curve_arg) +{ + EC_KEY *key = NULL; + int c_arity = -1; + const ERL_NIF_TERM* curve; + ErlNifBinary seed; + BIGNUM *p = NULL; + BIGNUM *a = NULL; + BIGNUM *b = NULL; + BIGNUM *bn_order = NULL; + BIGNUM *cofactor = NULL; + EC_GROUP *group = NULL; + EC_POINT *point = NULL; + + /* {Field, Prime, Point, Order, CoFactor} = Curve */ + if (enif_get_tuple(env,curve_arg,&c_arity,&curve) + && c_arity == 5 + && get_bn_from_bin(env, curve[3], &bn_order) + && (curve[4] != atom_none && get_bn_from_bin(env, curve[4], &cofactor))) { + + int f_arity = -1; + const ERL_NIF_TERM* field; + int p_arity = -1; + const ERL_NIF_TERM* prime; + + long field_bits; + + /* {A, B, Seed} = Prime */ + if (!enif_get_tuple(env,curve[1],&p_arity,&prime) + || !get_bn_from_bin(env, prime[0], &a) + || !get_bn_from_bin(env, prime[1], &b)) + goto out_err; + + if (!enif_get_tuple(env,curve[0],&f_arity,&field)) + goto out_err; + + if (f_arity == 2 && field[0] == atom_prime_field) { + /* {prime_field, Prime} */ + + if (!get_bn_from_bin(env, field[1], &p)) + goto out_err; + + if (BN_is_negative(p) || BN_is_zero(p)) + goto out_err; + + field_bits = BN_num_bits(p); + if (field_bits > OPENSSL_ECC_MAX_FIELD_BITS) + goto out_err; + + /* create the EC_GROUP structure */ + group = EC_GROUP_new_curve_GFp(p, a, b, NULL); + + } else if (f_arity == 3 && field[0] == atom_characteristic_two_field) { +#if defined(OPENSSL_NO_EC2M) + enif_raise_exception(env, atom_notsup); + goto out_err; +#else + /* {characteristic_two_field, M, Basis} */ + + int b_arity = -1; + const ERL_NIF_TERM* basis; + unsigned int k1, k2, k3; + + if ((p = BN_new()) == NULL) + goto out_err; + + if (!enif_get_long(env, field[1], &field_bits) + || field_bits > OPENSSL_ECC_MAX_FIELD_BITS) + goto out_err; + + if (enif_get_tuple(env,field[2],&b_arity,&basis)) { + if (b_arity == 2 + && basis[0] == atom_tpbasis + && enif_get_uint(env, basis[1], &k1)) { + /* {tpbasis, k} = Basis */ + + if (!(field_bits > k1 && k1 > 0)) + goto out_err; + + /* create the polynomial */ + if (!BN_set_bit(p, (int)field_bits) + || !BN_set_bit(p, (int)k1) + || !BN_set_bit(p, 0)) + goto out_err; + + } else if (b_arity == 4 + && basis[0] == atom_ppbasis + && enif_get_uint(env, basis[1], &k1) + && enif_get_uint(env, basis[2], &k2) + && enif_get_uint(env, basis[3], &k3)) { + /* {ppbasis, k1, k2, k3} = Basis */ + + if (!(field_bits > k3 && k3 > k2 && k2 > k1 && k1 > 0)) + goto out_err; + + /* create the polynomial */ + if (!BN_set_bit(p, (int)field_bits) + || !BN_set_bit(p, (int)k1) + || !BN_set_bit(p, (int)k2) + || !BN_set_bit(p, (int)k3) + || !BN_set_bit(p, 0)) + goto out_err; + + } else + goto out_err; + } else if (field[2] == atom_onbasis) { + /* onbasis = Basis */ + /* no parameters */ + goto out_err; + + } else + goto out_err; + + group = EC_GROUP_new_curve_GF2m(p, a, b, NULL); +#endif + } else + goto out_err; + + if (!group) + goto out_err; + + if (enif_inspect_binary(env, prime[2], &seed)) { + EC_GROUP_set_seed(group, seed.data, seed.size); + } + + if (!term2point(env, curve[2], group, &point)) + goto out_err; + + if (BN_is_negative(bn_order) + || BN_is_zero(bn_order) + || BN_num_bits(bn_order) > (int)field_bits + 1) + goto out_err; + + if (!EC_GROUP_set_generator(group, point, bn_order, cofactor)) + goto out_err; + + EC_GROUP_set_asn1_flag(group, 0x0); + + key = EC_KEY_new(); + if (!key) + goto out_err; + EC_KEY_set_group(key, group); + } + else { + goto out_err; + } + + + goto out; + +out_err: + if (key) EC_KEY_free(key); + key = NULL; + +out: + /* some OpenSSL structures are mem-dup'ed into the key, + so we have to free our copies here */ + if (p) BN_free(p); + if (a) BN_free(a); + if (b) BN_free(b); + if (bn_order) BN_free(bn_order); + if (cofactor) BN_free(cofactor); + if (group) EC_GROUP_free(group); + if (point) EC_POINT_free(point); + + return key; +} + +static ERL_NIF_TERM point2term(ErlNifEnv* env, + const EC_GROUP *group, + const EC_POINT *point, + point_conversion_form_t form) +{ + unsigned dlen; + ErlNifBinary bin; + + dlen = EC_POINT_point2oct(group, point, form, NULL, 0, NULL); + if (dlen == 0) + return atom_undefined; + + if (!enif_alloc_binary(dlen, &bin)) + return enif_make_badarg(env); + + if (!EC_POINT_point2oct(group, point, form, bin.data, bin.size, NULL)) { + enif_release_binary(&bin); + return enif_make_badarg(env); + } + ERL_VALGRIND_MAKE_MEM_DEFINED(bin.data, bin.size); + return enif_make_binary(env, &bin); +} + +int term2point(ErlNifEnv* env, ERL_NIF_TERM term, EC_GROUP *group, EC_POINT **pptr) +{ + int ret = 0; + ErlNifBinary bin; + EC_POINT *point; + + if (!enif_inspect_binary(env,term,&bin)) { + return 0; + } + + if ((*pptr = point = EC_POINT_new(group)) == NULL) { + return 0; + } + + /* set the point conversion form */ + EC_GROUP_set_point_conversion_form(group, (point_conversion_form_t)(bin.data[0] & ~0x01)); + + /* extract the ec point */ + if (!EC_POINT_oct2point(group, point, bin.data, bin.size, NULL)) { + EC_POINT_free(point); + *pptr = NULL; + } else + ret = 1; + + return ret; +} + +int get_ec_key(ErlNifEnv* env, + ERL_NIF_TERM curve, ERL_NIF_TERM priv, ERL_NIF_TERM pub, + EC_KEY** res) +{ + EC_KEY *key = NULL; + BIGNUM *priv_key = NULL; + EC_POINT *pub_key = NULL; + EC_GROUP *group = NULL; + + if (!(priv == atom_undefined || get_bn_from_bin(env, priv, &priv_key)) + || !(pub == atom_undefined || enif_is_binary(env, pub))) { + goto out_err; + } + + key = ec_key_new(env, curve); + + if (!key) { + goto out_err; + } + + if (!group) + group = EC_GROUP_dup(EC_KEY_get0_group(key)); + + if (term2point(env, pub, group, &pub_key)) { + if (!EC_KEY_set_public_key(key, pub_key)) { + goto out_err; + } + } + if (priv != atom_undefined + && !BN_is_zero(priv_key)) { + if (!EC_KEY_set_private_key(key, priv_key)) + goto out_err; + + /* calculate public key (if necessary) */ + if (EC_KEY_get0_public_key(key) == NULL) + { + /* the public key was not included in the SEC1 private + * key => calculate the public key */ + pub_key = EC_POINT_new(group); + if (pub_key == NULL + || !EC_POINT_copy(pub_key, EC_GROUP_get0_generator(group)) + || !EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, NULL) + || !EC_KEY_set_public_key(key, pub_key)) + goto out_err; + } + } + + goto out; + +out_err: + if (key) EC_KEY_free(key); + key = NULL; + +out: + /* some OpenSSL structures are mem-dup'ed into the key, + so we have to free our copies here */ + if (priv_key) BN_clear_free(priv_key); + if (pub_key) EC_POINT_free(pub_key); + if (group) EC_GROUP_free(group); + if (!key) + return 0; + *res = key; + return 1; +} + +#endif /* HAVE_EC */ + +ERL_NIF_TERM ec_key_generate(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ +#if defined(HAVE_EC) + EC_KEY *key = NULL; + const EC_GROUP *group; + const EC_POINT *public_key; + ERL_NIF_TERM priv_key; + ERL_NIF_TERM pub_key = atom_undefined; + + if (!get_ec_key(env, argv[0], argv[1], atom_undefined, &key)) + goto badarg; + + if (argv[1] == atom_undefined) { + if (!EC_KEY_generate_key(key)) + goto badarg; + } + + group = EC_KEY_get0_group(key); + public_key = EC_KEY_get0_public_key(key); + + if (group && public_key) { + pub_key = point2term(env, group, public_key, + EC_KEY_get_conv_form(key)); + } + priv_key = bn2term(env, EC_KEY_get0_private_key(key)); + EC_KEY_free(key); + return enif_make_tuple2(env, pub_key, priv_key); + +badarg: + if (key) + EC_KEY_free(key); + return make_badarg_maybe(env); +#else + return atom_notsup; +#endif +} diff --git a/lib/crypto/c_src/ec.h b/lib/crypto/c_src/ec.h new file mode 100644 index 0000000000..b7e1cc5a46 --- /dev/null +++ b/lib/crypto/c_src/ec.h @@ -0,0 +1,35 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_EC_H__ +#define E_EC_H__ 1 + +#include "common.h" + +#if defined(HAVE_EC) +int get_ec_key(ErlNifEnv* env, ERL_NIF_TERM curve, ERL_NIF_TERM priv, ERL_NIF_TERM pub, + EC_KEY** res); +int term2point(ErlNifEnv* env, ERL_NIF_TERM term, EC_GROUP *group, EC_POINT **pptr); +ERL_NIF_TERM make_badarg_maybe(ErlNifEnv* env); +#endif + +ERL_NIF_TERM ec_key_generate(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_EC_H__ */ diff --git a/lib/crypto/c_src/ecdh.c b/lib/crypto/c_src/ecdh.c new file mode 100644 index 0000000000..d458f3c48e --- /dev/null +++ b/lib/crypto/c_src/ecdh.c @@ -0,0 +1,80 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "ecdh.h" +#include "ec.h" + +/* + (_OthersPublicKey, _MyPrivateKey) + (_OthersPublicKey, _MyEC_Point) +*/ +ERL_NIF_TERM ecdh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +/* (OtherPublicKey, Curve, My) */ +{ +#if defined(HAVE_EC) + ERL_NIF_TERM ret; + unsigned char *p; + EC_KEY* key = NULL; + int field_size = 0; + int i; + EC_GROUP *group; + const BIGNUM *priv_key; + EC_POINT *my_ecpoint = NULL; + EC_KEY *other_ecdh = NULL; + + if (!get_ec_key(env, argv[1], argv[2], atom_undefined, &key)) + return make_badarg_maybe(env); + + group = EC_GROUP_dup(EC_KEY_get0_group(key)); + priv_key = EC_KEY_get0_private_key(key); + + if (!term2point(env, argv[0], group, &my_ecpoint)) { + goto out_err; + } + + if ((other_ecdh = EC_KEY_new()) == NULL + || !EC_KEY_set_group(other_ecdh, group) + || !EC_KEY_set_private_key(other_ecdh, priv_key)) + goto out_err; + + field_size = EC_GROUP_get_degree(group); + if (field_size <= 0) + goto out_err; + + p = enif_make_new_binary(env, (field_size+7)/8, &ret); + i = ECDH_compute_key(p, (field_size+7)/8, my_ecpoint, other_ecdh, NULL); + + if (i < 0) + goto out_err; +out: + if (group) EC_GROUP_free(group); + if (my_ecpoint) EC_POINT_free(my_ecpoint); + if (other_ecdh) EC_KEY_free(other_ecdh); + if (key) EC_KEY_free(key); + + return ret; + +out_err: + ret = enif_make_badarg(env); + goto out; +#else + return atom_notsup; +#endif +} diff --git a/lib/crypto/c_src/ecdh.h b/lib/crypto/c_src/ecdh.h new file mode 100644 index 0000000000..5ed331e676 --- /dev/null +++ b/lib/crypto/c_src/ecdh.h @@ -0,0 +1,28 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_ECDH_H__ +#define E_ECDH_H__ 1 + +#include "common.h" + +ERL_NIF_TERM ecdh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_ECDH_H__ */ diff --git a/lib/crypto/c_src/eddsa.c b/lib/crypto/c_src/eddsa.c new file mode 100644 index 0000000000..0fdada9677 --- /dev/null +++ b/lib/crypto/c_src/eddsa.c @@ -0,0 +1,51 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "eddsa.h" + +#ifdef HAVE_EDDSA +int get_eddsa_key(ErlNifEnv* env, int public, ERL_NIF_TERM key, EVP_PKEY **pkey) +{ + /* key=[K] */ + ERL_NIF_TERM head, tail, tail2, algo; + ErlNifBinary bin; + int type; + + if (!enif_get_list_cell(env, key, &head, &tail) + || !enif_inspect_binary(env, head, &bin) + || !enif_get_list_cell(env, tail, &algo, &tail2) + || !enif_is_empty_list(env, tail2)) { + return 0; + } + if (algo == atom_ed25519) type = EVP_PKEY_ED25519; + else if (algo == atom_ed448) type = EVP_PKEY_ED448; + else + return 0; + + if (public) + *pkey = EVP_PKEY_new_raw_public_key(type, NULL, bin.data, bin.size); + else + *pkey = EVP_PKEY_new_raw_private_key(type, NULL, bin.data, bin.size); + + if (!pkey) + return 0; + return 1; +} +#endif diff --git a/lib/crypto/c_src/eddsa.h b/lib/crypto/c_src/eddsa.h new file mode 100644 index 0000000000..4b30247cab --- /dev/null +++ b/lib/crypto/c_src/eddsa.h @@ -0,0 +1,30 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_EDDSA_H__ +#define E_EDDSA_H__ 1 + +#include "common.h" + +#ifdef HAVE_EDDSA +int get_eddsa_key(ErlNifEnv* env, int public, ERL_NIF_TERM key, EVP_PKEY **pkey); +#endif + +#endif /* E_EDDSA_H__ */ diff --git a/lib/crypto/c_src/engine.c b/lib/crypto/c_src/engine.c new file mode 100644 index 0000000000..dc8e1828ce --- /dev/null +++ b/lib/crypto/c_src/engine.c @@ -0,0 +1,720 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "engine.h" + +#ifdef HAS_ENGINE_SUPPORT +struct engine_ctx { + ENGINE *engine; + char *id; +}; + +static ErlNifResourceType* engine_ctx_rtype; + +static int get_engine_load_cmd_list(ErlNifEnv* env, const ERL_NIF_TERM term, char **cmds, int i); +static int zero_terminate(ErlNifBinary bin, char **buf); + +static void engine_ctx_dtor(ErlNifEnv* env, struct engine_ctx* ctx) { + PRINTF_ERR0("engine_ctx_dtor"); + if(ctx->id) { + PRINTF_ERR1(" non empty ctx->id=%s", ctx->id); + enif_free(ctx->id); + } else + PRINTF_ERR0(" empty ctx->id=NULL"); +} + +int get_engine_and_key_id(ErlNifEnv *env, ERL_NIF_TERM key, char ** id, ENGINE **e) +{ + ERL_NIF_TERM engine_res, key_id_term; + struct engine_ctx *ctx; + ErlNifBinary key_id_bin; + + if (!enif_get_map_value(env, key, atom_engine, &engine_res) || + !enif_get_resource(env, engine_res, engine_ctx_rtype, (void**)&ctx) || + !enif_get_map_value(env, key, atom_key_id, &key_id_term) || + !enif_inspect_binary(env, key_id_term, &key_id_bin)) { + return 0; + } + else { + *e = ctx->engine; + return zero_terminate(key_id_bin, id); + } +} + +char *get_key_password(ErlNifEnv *env, ERL_NIF_TERM key) { + ERL_NIF_TERM tmp_term; + ErlNifBinary pwd_bin; + char *pwd = NULL; + if (enif_get_map_value(env, key, atom_password, &tmp_term) && + enif_inspect_binary(env, tmp_term, &pwd_bin) && + zero_terminate(pwd_bin, &pwd) + ) return pwd; + + return NULL; +} + +static int zero_terminate(ErlNifBinary bin, char **buf) { + *buf = enif_alloc(bin.size+1); + if (!*buf) + return 0; + memcpy(*buf, bin.data, bin.size); + *(*buf+bin.size) = 0; + return 1; +} +#endif /* HAS_ENGINE_SUPPORT */ + +int init_engine_ctx(ErlNifEnv *env) { +#ifdef HAS_ENGINE_SUPPORT + engine_ctx_rtype = enif_open_resource_type(env, NULL, "ENGINE_CTX", + (ErlNifResourceDtor*) engine_ctx_dtor, + ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER, + NULL); + if (engine_ctx_rtype == NULL) { + PRINTF_ERR0("CRYPTO: Could not open resource type 'ENGINE_CTX'"); + return 0; + } +#endif + + return 1; +} + +ERL_NIF_TERM engine_by_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (EngineId) */ +#ifdef HAS_ENGINE_SUPPORT + ERL_NIF_TERM ret; + ErlNifBinary engine_id_bin; + char *engine_id; + ENGINE *engine; + struct engine_ctx *ctx; + + // Get Engine Id + if(!enif_inspect_binary(env, argv[0], &engine_id_bin)) { + PRINTF_ERR0("engine_by_id_nif Leaved: badarg"); + return enif_make_badarg(env); + } else { + engine_id = enif_alloc(engine_id_bin.size+1); + (void) memcpy(engine_id, engine_id_bin.data, engine_id_bin.size); + engine_id[engine_id_bin.size] = '\0'; + } + + engine = ENGINE_by_id(engine_id); + if(!engine) { + enif_free(engine_id); + PRINTF_ERR0("engine_by_id_nif Leaved: {error, bad_engine_id}"); + return enif_make_tuple2(env, atom_error, atom_bad_engine_id); + } + + ctx = enif_alloc_resource(engine_ctx_rtype, sizeof(struct engine_ctx)); + ctx->engine = engine; + ctx->id = engine_id; + + ret = enif_make_resource(env, ctx); + enif_release_resource(ctx); + + return enif_make_tuple2(env, atom_ok, ret); +#else + return atom_notsup; +#endif +} + +ERL_NIF_TERM engine_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Engine) */ +#ifdef HAS_ENGINE_SUPPORT + ERL_NIF_TERM ret = atom_ok; + struct engine_ctx *ctx; + + // Get Engine + if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { + PRINTF_ERR0("engine_init_nif Leaved: Parameter not an engine resource object"); + return enif_make_badarg(env); + } + if (!ENGINE_init(ctx->engine)) { + //ERR_print_errors_fp(stderr); + PRINTF_ERR0("engine_init_nif Leaved: {error, engine_init_failed}"); + return enif_make_tuple2(env, atom_error, atom_engine_init_failed); + } + + return ret; +#else + return atom_notsup; +#endif +} + +ERL_NIF_TERM engine_free_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Engine) */ +#ifdef HAS_ENGINE_SUPPORT + struct engine_ctx *ctx; + + // Get Engine + if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { + PRINTF_ERR0("engine_free_nif Leaved: Parameter not an engine resource object"); + return enif_make_badarg(env); + } + + ENGINE_free(ctx->engine); + return atom_ok; +#else + return atom_notsup; +#endif +} + +ERL_NIF_TERM engine_finish_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Engine) */ +#ifdef HAS_ENGINE_SUPPORT + struct engine_ctx *ctx; + + // Get Engine + if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { + PRINTF_ERR0("engine_finish_nif Leaved: Parameter not an engine resource object"); + return enif_make_badarg(env); + } + + ENGINE_finish(ctx->engine); + return atom_ok; +#else + return atom_notsup; +#endif +} + +ERL_NIF_TERM engine_load_dynamic_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* () */ +#ifdef HAS_ENGINE_SUPPORT + ENGINE_load_dynamic(); + return atom_ok; +#else + return atom_notsup; +#endif +} + +ERL_NIF_TERM engine_ctrl_cmd_strings_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Engine, Commands) */ +#ifdef HAS_ENGINE_SUPPORT + ERL_NIF_TERM ret = atom_ok; + unsigned int cmds_len = 0; + char **cmds = NULL; + struct engine_ctx *ctx; + int i, optional = 0; + + // Get Engine + if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { + PRINTF_ERR0("engine_ctrl_cmd_strings_nif Leaved: Parameter not an engine resource object"); + return enif_make_badarg(env); + } + + PRINTF_ERR1("Engine Id: %s\r\n", ENGINE_get_id(ctx->engine)); + + // Get Command List + if(!enif_get_list_length(env, argv[1], &cmds_len)) { + PRINTF_ERR0("engine_ctrl_cmd_strings_nif Leaved: Bad Command List"); + return enif_make_badarg(env); + } else { + cmds_len *= 2; // Key-Value list from erlang + cmds = enif_alloc((cmds_len+1)*sizeof(char*)); + if(get_engine_load_cmd_list(env, argv[1], cmds, 0)) { + PRINTF_ERR0("engine_ctrl_cmd_strings_nif Leaved: Couldn't read Command List"); + ret = enif_make_badarg(env); + goto error; + } + } + + if(!enif_get_int(env, argv[2], &optional)) { + PRINTF_ERR0("engine_ctrl_cmd_strings_nif Leaved: Parameter optional not an integer"); + return enif_make_badarg(env); + } + + for(i = 0; i < cmds_len; i+=2) { + PRINTF_ERR2("Cmd: %s:%s\r\n", + cmds[i] ? cmds[i] : "(NULL)", + cmds[i+1] ? cmds[i+1] : "(NULL)"); + if(!ENGINE_ctrl_cmd_string(ctx->engine, cmds[i], cmds[i+1], optional)) { + PRINTF_ERR2("Command failed: %s:%s\r\n", + cmds[i] ? cmds[i] : "(NULL)", + cmds[i+1] ? cmds[i+1] : "(NULL)"); + //ENGINE_free(ctx->engine); + ret = enif_make_tuple2(env, atom_error, atom_ctrl_cmd_failed); + PRINTF_ERR0("engine_ctrl_cmd_strings_nif Leaved: {error, ctrl_cmd_failed}"); + goto error; + } + } + + error: + for(i = 0; cmds != NULL && cmds[i] != NULL; i++) + enif_free(cmds[i]); + enif_free(cmds); + return ret; +#else + return atom_notsup; +#endif +} + +ERL_NIF_TERM engine_add_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Engine) */ +#ifdef HAS_ENGINE_SUPPORT + struct engine_ctx *ctx; + + // Get Engine + if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { + PRINTF_ERR0("engine_add_nif Leaved: Parameter not an engine resource object"); + return enif_make_badarg(env); + } + + if (!ENGINE_add(ctx->engine)) { + PRINTF_ERR0("engine_add_nif Leaved: {error, add_engine_failed}"); + return enif_make_tuple2(env, atom_error, atom_add_engine_failed); + } + return atom_ok; +#else + return atom_notsup; +#endif +} + +ERL_NIF_TERM engine_remove_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Engine) */ +#ifdef HAS_ENGINE_SUPPORT + struct engine_ctx *ctx; + + // Get Engine + if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { + PRINTF_ERR0("engine_remove_nif Leaved: Parameter not an engine resource object"); + return enif_make_badarg(env); + } + + if (!ENGINE_remove(ctx->engine)) { + PRINTF_ERR0("engine_remove_nif Leaved: {error, remove_engine_failed}"); + return enif_make_tuple2(env, atom_error, atom_remove_engine_failed); + } + return atom_ok; +#else + return atom_notsup; +#endif +} + +ERL_NIF_TERM engine_register_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Engine, EngineMethod) */ +#ifdef HAS_ENGINE_SUPPORT + struct engine_ctx *ctx; + unsigned int method; + + // Get Engine + if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { + PRINTF_ERR0("engine_register_nif Leaved: Parameter not an engine resource object"); + return enif_make_badarg(env); + } + // Get Method + if (!enif_get_uint(env, argv[1], &method)) { + PRINTF_ERR0("engine_register_nif Leaved: Parameter Method not an uint"); + return enif_make_badarg(env); + } + + switch(method) + { +#ifdef ENGINE_METHOD_RSA + case ENGINE_METHOD_RSA: + if (!ENGINE_register_RSA(ctx->engine)) + return enif_make_tuple2(env, atom_error, atom_register_engine_failed); + break; +#endif +#ifdef ENGINE_METHOD_DSA + case ENGINE_METHOD_DSA: + if (!ENGINE_register_DSA(ctx->engine)) + return enif_make_tuple2(env, atom_error, atom_register_engine_failed); + break; +#endif +#ifdef ENGINE_METHOD_DH + case ENGINE_METHOD_DH: + if (!ENGINE_register_DH(ctx->engine)) + return enif_make_tuple2(env, atom_error, atom_register_engine_failed); + break; +#endif +#ifdef ENGINE_METHOD_RAND + case ENGINE_METHOD_RAND: + if (!ENGINE_register_RAND(ctx->engine)) + return enif_make_tuple2(env, atom_error, atom_register_engine_failed); + break; +#endif +#ifdef ENGINE_METHOD_ECDH + case ENGINE_METHOD_ECDH: + if (!ENGINE_register_ECDH(ctx->engine)) + return enif_make_tuple2(env, atom_error, atom_register_engine_failed); + break; +#endif +#ifdef ENGINE_METHOD_ECDSA + case ENGINE_METHOD_ECDSA: + if (!ENGINE_register_ECDSA(ctx->engine)) + return enif_make_tuple2(env, atom_error, atom_register_engine_failed); + break; +#endif +#ifdef ENGINE_METHOD_STORE + case ENGINE_METHOD_STORE: + if (!ENGINE_register_STORE(ctx->engine)) + return enif_make_tuple2(env, atom_error, atom_register_engine_failed); + break; +#endif +#ifdef ENGINE_METHOD_CIPHERS + case ENGINE_METHOD_CIPHERS: + if (!ENGINE_register_ciphers(ctx->engine)) + return enif_make_tuple2(env, atom_error, atom_register_engine_failed); + break; +#endif +#ifdef ENGINE_METHOD_DIGESTS + case ENGINE_METHOD_DIGESTS: + if (!ENGINE_register_digests(ctx->engine)) + return enif_make_tuple2(env, atom_error, atom_register_engine_failed); + break; +#endif +#ifdef ENGINE_METHOD_PKEY_METHS + case ENGINE_METHOD_PKEY_METHS: + if (!ENGINE_register_pkey_meths(ctx->engine)) + return enif_make_tuple2(env, atom_error, atom_register_engine_failed); + break; +#endif +#ifdef ENGINE_METHOD_PKEY_ASN1_METHS + case ENGINE_METHOD_PKEY_ASN1_METHS: + if (!ENGINE_register_pkey_asn1_meths(ctx->engine)) + return enif_make_tuple2(env, atom_error, atom_register_engine_failed); + break; +#endif +#ifdef ENGINE_METHOD_EC + case ENGINE_METHOD_EC: + if (!ENGINE_register_EC(ctx->engine)) + return enif_make_tuple2(env, atom_error, atom_register_engine_failed); + break; +#endif + default: + return enif_make_tuple2(env, atom_error, atom_engine_method_not_supported); + break; + } + return atom_ok; +#else + return atom_notsup; +#endif +} + +ERL_NIF_TERM engine_unregister_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Engine, EngineMethod) */ +#ifdef HAS_ENGINE_SUPPORT + struct engine_ctx *ctx; + unsigned int method; + + // Get Engine + if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { + PRINTF_ERR0("engine_unregister_nif Leaved: Parameter not an engine resource object"); + return enif_make_badarg(env); + } + // Get Method + if (!enif_get_uint(env, argv[1], &method)) { + PRINTF_ERR0("engine_unregister_nif Leaved: Parameter Method not an uint"); + return enif_make_badarg(env); + } + + switch(method) + { +#ifdef ENGINE_METHOD_RSA + case ENGINE_METHOD_RSA: + ENGINE_unregister_RSA(ctx->engine); + break; +#endif +#ifdef ENGINE_METHOD_DSA + case ENGINE_METHOD_DSA: + ENGINE_unregister_DSA(ctx->engine); + break; +#endif +#ifdef ENGINE_METHOD_DH + case ENGINE_METHOD_DH: + ENGINE_unregister_DH(ctx->engine); + break; +#endif +#ifdef ENGINE_METHOD_RAND + case ENGINE_METHOD_RAND: + ENGINE_unregister_RAND(ctx->engine); + break; +#endif +#ifdef ENGINE_METHOD_ECDH + case ENGINE_METHOD_ECDH: + ENGINE_unregister_ECDH(ctx->engine); + break; +#endif +#ifdef ENGINE_METHOD_ECDSA + case ENGINE_METHOD_ECDSA: + ENGINE_unregister_ECDSA(ctx->engine); + break; +#endif +#ifdef ENGINE_METHOD_STORE + case ENGINE_METHOD_STORE: + ENGINE_unregister_STORE(ctx->engine); + break; +#endif +#ifdef ENGINE_METHOD_CIPHERS + case ENGINE_METHOD_CIPHERS: + ENGINE_unregister_ciphers(ctx->engine); + break; +#endif +#ifdef ENGINE_METHOD_DIGESTS + case ENGINE_METHOD_DIGESTS: + ENGINE_unregister_digests(ctx->engine); + break; +#endif +#ifdef ENGINE_METHOD_PKEY_METHS + case ENGINE_METHOD_PKEY_METHS: + ENGINE_unregister_pkey_meths(ctx->engine); + break; +#endif +#ifdef ENGINE_METHOD_PKEY_ASN1_METHS + case ENGINE_METHOD_PKEY_ASN1_METHS: + ENGINE_unregister_pkey_asn1_meths(ctx->engine); + break; +#endif +#ifdef ENGINE_METHOD_EC + case ENGINE_METHOD_EC: + ENGINE_unregister_EC(ctx->engine); + break; +#endif + default: + break; + } + return atom_ok; +#else + return atom_notsup; +#endif +} + +ERL_NIF_TERM engine_get_first_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Engine) */ +#ifdef HAS_ENGINE_SUPPORT + ERL_NIF_TERM ret; + ENGINE *engine; + ErlNifBinary engine_bin; + struct engine_ctx *ctx; + + engine = ENGINE_get_first(); + if(!engine) { + enif_alloc_binary(0, &engine_bin); + engine_bin.size = 0; + return enif_make_tuple2(env, atom_ok, enif_make_binary(env, &engine_bin)); + } + + ctx = enif_alloc_resource(engine_ctx_rtype, sizeof(struct engine_ctx)); + ctx->engine = engine; + ctx->id = NULL; + + ret = enif_make_resource(env, ctx); + enif_release_resource(ctx); + + return enif_make_tuple2(env, atom_ok, ret); +#else + return atom_notsup; +#endif +} + +ERL_NIF_TERM engine_get_next_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Engine) */ +#ifdef HAS_ENGINE_SUPPORT + ERL_NIF_TERM ret; + ENGINE *engine; + ErlNifBinary engine_bin; + struct engine_ctx *ctx, *next_ctx; + + // Get Engine + if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { + PRINTF_ERR0("engine_get_next_nif Leaved: Parameter not an engine resource object"); + return enif_make_badarg(env); + } + engine = ENGINE_get_next(ctx->engine); + if (!engine) { + enif_alloc_binary(0, &engine_bin); + engine_bin.size = 0; + return enif_make_tuple2(env, atom_ok, enif_make_binary(env, &engine_bin)); + } + + next_ctx = enif_alloc_resource(engine_ctx_rtype, sizeof(struct engine_ctx)); + next_ctx->engine = engine; + next_ctx->id = NULL; + + ret = enif_make_resource(env, next_ctx); + enif_release_resource(next_ctx); + + return enif_make_tuple2(env, atom_ok, ret); +#else + return atom_notsup; +#endif +} + +ERL_NIF_TERM engine_get_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Engine) */ +#ifdef HAS_ENGINE_SUPPORT + ErlNifBinary engine_id_bin; + const char *engine_id; + int size; + struct engine_ctx *ctx; + + // Get Engine + if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { + PRINTF_ERR0("engine_get_id_nif Leaved: Parameter not an engine resource object"); + return enif_make_badarg(env); + } + + engine_id = ENGINE_get_id(ctx->engine); + if (!engine_id) { + enif_alloc_binary(0, &engine_id_bin); + engine_id_bin.size = 0; + return enif_make_binary(env, &engine_id_bin); + } + + size = strlen(engine_id); + enif_alloc_binary(size, &engine_id_bin); + engine_id_bin.size = size; + memcpy(engine_id_bin.data, engine_id, size); + + return enif_make_binary(env, &engine_id_bin); +#else + return atom_notsup; +#endif +} + +ERL_NIF_TERM engine_get_name_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Engine) */ +#ifdef HAS_ENGINE_SUPPORT + ErlNifBinary engine_name_bin; + const char *engine_name; + int size; + struct engine_ctx *ctx; + + // Get Engine + if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) { + PRINTF_ERR0("engine_get_id_nif Leaved: Parameter not an engine resource object"); + return enif_make_badarg(env); + } + + engine_name = ENGINE_get_name(ctx->engine); + if (!engine_name) { + enif_alloc_binary(0, &engine_name_bin); + engine_name_bin.size = 0; + return enif_make_binary(env, &engine_name_bin); + } + + size = strlen(engine_name); + enif_alloc_binary(size, &engine_name_bin); + engine_name_bin.size = size; + memcpy(engine_name_bin.data, engine_name, size); + + return enif_make_binary(env, &engine_name_bin); +#else + return atom_notsup; +#endif +} + +#ifdef HAS_ENGINE_SUPPORT +static int get_engine_load_cmd_list(ErlNifEnv* env, const ERL_NIF_TERM term, char **cmds, int i) +{ + ERL_NIF_TERM head, tail; + const ERL_NIF_TERM *tmp_tuple; + ErlNifBinary tmpbin; + int arity; + char* tmpstr; + + if(!enif_is_empty_list(env, term)) { + if(!enif_get_list_cell(env, term, &head, &tail)) { + cmds[i] = NULL; + return -1; + } else { + if(!enif_get_tuple(env, head, &arity, &tmp_tuple) || arity != 2) { + cmds[i] = NULL; + return -1; + } else { + if(!enif_inspect_binary(env, tmp_tuple[0], &tmpbin)) { + cmds[i] = NULL; + return -1; + } else { + tmpstr = enif_alloc(tmpbin.size+1); + (void) memcpy(tmpstr, tmpbin.data, tmpbin.size); + tmpstr[tmpbin.size] = '\0'; + cmds[i++] = tmpstr; + } + if(!enif_inspect_binary(env, tmp_tuple[1], &tmpbin)) { + cmds[i] = NULL; + return -1; + } else { + if(tmpbin.size == 0) + cmds[i++] = NULL; + else { + tmpstr = enif_alloc(tmpbin.size+1); + (void) memcpy(tmpstr, tmpbin.data, tmpbin.size); + tmpstr[tmpbin.size] = '\0'; + cmds[i++] = tmpstr; + } + } + return get_engine_load_cmd_list(env, tail, cmds, i); + } + } + } else { + cmds[i] = NULL; + return 0; + } +} +#endif /* HAS_ENGINE_SUPPORT */ + +ERL_NIF_TERM engine_get_all_methods_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* () */ +#ifdef HAS_ENGINE_SUPPORT + ERL_NIF_TERM method_array[12]; + int i = 0; + +#ifdef ENGINE_METHOD_RSA + method_array[i++] = atom_engine_method_rsa; +#endif +#ifdef ENGINE_METHOD_DSA + method_array[i++] = atom_engine_method_dsa; +#endif +#ifdef ENGINE_METHOD_DH + method_array[i++] = atom_engine_method_dh; +#endif +#ifdef ENGINE_METHOD_RAND + method_array[i++] = atom_engine_method_rand; +#endif +#ifdef ENGINE_METHOD_ECDH + method_array[i++] = atom_engine_method_ecdh; +#endif +#ifdef ENGINE_METHOD_ECDSA + method_array[i++] = atom_engine_method_ecdsa; +#endif +#ifdef ENGINE_METHOD_STORE + method_array[i++] = atom_engine_method_store; +#endif +#ifdef ENGINE_METHOD_CIPHERS + method_array[i++] = atom_engine_method_ciphers; +#endif +#ifdef ENGINE_METHOD_DIGESTS + method_array[i++] = atom_engine_method_digests; +#endif +#ifdef ENGINE_METHOD_PKEY_METHS + method_array[i++] = atom_engine_method_pkey_meths; +#endif +#ifdef ENGINE_METHOD_PKEY_ASN1_METHS + method_array[i++] = atom_engine_method_pkey_asn1_meths; +#endif +#ifdef ENGINE_METHOD_EC + method_array[i++] = atom_engine_method_ec; +#endif + + return enif_make_list_from_array(env, method_array, i); +#else + return atom_notsup; +#endif +} diff --git a/lib/crypto/c_src/engine.h b/lib/crypto/c_src/engine.h new file mode 100644 index 0000000000..4a2eed9672 --- /dev/null +++ b/lib/crypto/c_src/engine.h @@ -0,0 +1,49 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_ENGINE_H__ +#define E_ENGINE_H__ 1 + +#include "common.h" + +#ifdef HAS_ENGINE_SUPPORT +int get_engine_and_key_id(ErlNifEnv *env, ERL_NIF_TERM key, char ** id, ENGINE **e); +char *get_key_password(ErlNifEnv *env, ERL_NIF_TERM key); +#endif /* HAS_ENGINE_SUPPORT */ + +int init_engine_ctx(ErlNifEnv *env); + +ERL_NIF_TERM engine_by_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM engine_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM engine_finish_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM engine_free_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM engine_load_dynamic_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM engine_ctrl_cmd_strings_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM engine_register_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM engine_unregister_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM engine_add_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM engine_remove_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM engine_get_first_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM engine_get_next_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM engine_get_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM engine_get_name_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM engine_get_all_methods_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_ENGINE_H__ */ diff --git a/lib/crypto/c_src/evp.c b/lib/crypto/c_src/evp.c new file mode 100644 index 0000000000..3c55ab630b --- /dev/null +++ b/lib/crypto/c_src/evp.c @@ -0,0 +1,124 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "evp.h" + +ERL_NIF_TERM evp_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) + /* (Curve, PeerBin, MyBin) */ +{ +#ifdef HAVE_ED_CURVE_DH + int type; + EVP_PKEY_CTX *ctx = NULL; + ErlNifBinary peer_bin, my_bin, key_bin; + EVP_PKEY *peer_key = NULL, *my_key = NULL; + size_t max_size; + + if (argv[0] == atom_x25519) type = EVP_PKEY_X25519; + else if (argv[0] == atom_x448) type = EVP_PKEY_X448; + else return enif_make_badarg(env); + + if (!enif_inspect_binary(env, argv[1], &peer_bin) || + !enif_inspect_binary(env, argv[2], &my_bin)) + goto return_badarg; + + if (!(my_key = EVP_PKEY_new_raw_private_key(type, NULL, my_bin.data, my_bin.size)) || + !(ctx = EVP_PKEY_CTX_new(my_key, NULL))) + goto return_badarg; + + if (!EVP_PKEY_derive_init(ctx)) + goto return_badarg; + + if (!(peer_key = EVP_PKEY_new_raw_public_key(type, NULL, peer_bin.data, peer_bin.size)) || + !EVP_PKEY_derive_set_peer(ctx, peer_key)) + goto return_badarg; + + if (!EVP_PKEY_derive(ctx, NULL, &max_size)) + goto return_badarg; + + if (!enif_alloc_binary(max_size, &key_bin) || + !EVP_PKEY_derive(ctx, key_bin.data, &key_bin.size)) + goto return_badarg; + + if (key_bin.size < max_size) { + size_t actual_size = key_bin.size; + if (!enif_realloc_binary(&key_bin, actual_size)) + goto return_badarg; + } + + EVP_PKEY_free(my_key); + EVP_PKEY_free(peer_key); + EVP_PKEY_CTX_free(ctx); + return enif_make_binary(env, &key_bin); + +return_badarg: + if (my_key) EVP_PKEY_free(my_key); + if (peer_key) EVP_PKEY_free(peer_key); + if (ctx) EVP_PKEY_CTX_free(ctx); + return enif_make_badarg(env); +#else + return atom_notsup; +#endif +} + +ERL_NIF_TERM evp_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +/* (Curve) */ +{ +#ifdef HAVE_ED_CURVE_DH + int type; + EVP_PKEY_CTX *ctx = NULL; + EVP_PKEY *pkey = NULL; + ERL_NIF_TERM ret_pub, ret_prv; + size_t key_len; + + if (argv[0] == atom_x25519) type = EVP_PKEY_X25519; + else if (argv[0] == atom_x448) type = EVP_PKEY_X448; + else return enif_make_badarg(env); + + if (!(ctx = EVP_PKEY_CTX_new_id(type, NULL))) return enif_make_badarg(env); + + if (!EVP_PKEY_keygen_init(ctx)) goto return_error; + if (!EVP_PKEY_keygen(ctx, &pkey)) goto return_error; + + if (!EVP_PKEY_get_raw_public_key(pkey, NULL, &key_len)) goto return_error; + if (!EVP_PKEY_get_raw_public_key(pkey, + enif_make_new_binary(env, key_len, &ret_pub), + &key_len)) + goto return_error; + + if (!EVP_PKEY_get_raw_private_key(pkey, NULL, &key_len)) goto return_error; + if (!EVP_PKEY_get_raw_private_key(pkey, + enif_make_new_binary(env, key_len, &ret_prv), + &key_len)) + goto return_error; + + EVP_PKEY_free(pkey); + EVP_PKEY_CTX_free(ctx); + return enif_make_tuple2(env, ret_pub, ret_prv); + +return_error: + if (pkey) EVP_PKEY_free(pkey); + if (ctx) EVP_PKEY_CTX_free(ctx); + return atom_error; + +#else + return atom_notsup; +#endif +} + diff --git a/lib/crypto/c_src/evp.h b/lib/crypto/c_src/evp.h new file mode 100644 index 0000000000..d767260262 --- /dev/null +++ b/lib/crypto/c_src/evp.h @@ -0,0 +1,29 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_EVP_H__ +#define E_EVP_H__ 1 + +#include "common.h" + +ERL_NIF_TERM evp_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM evp_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_EVP_H__ */ diff --git a/lib/crypto/c_src/evp_compat.h b/lib/crypto/c_src/evp_compat.h new file mode 100644 index 0000000000..98c861c45e --- /dev/null +++ b/lib/crypto/c_src/evp_compat.h @@ -0,0 +1,196 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_EVP_COMPAT_H__ +#define E_EVP_COMPAT_H__ 1 + +/* + * In OpenSSL 1.1.0, most structs are opaque. That means that + * the structs cannot be allocated as automatic variables on the + * C stack (because the size is unknown) and that it is necessary + * to use access functions. + * + * For backward compatibility to previous versions of OpenSSL, define + * on our versions of the new functions defined in 1.1.0 here, so that + * we don't have to sprinkle ifdefs throughout the code. + */ + +static INLINE HMAC_CTX *HMAC_CTX_new(void); +static INLINE void HMAC_CTX_free(HMAC_CTX *ctx); + +static INLINE HMAC_CTX *HMAC_CTX_new() +{ + HMAC_CTX *ctx = CRYPTO_malloc(sizeof(HMAC_CTX), __FILE__, __LINE__); + HMAC_CTX_init(ctx); + return ctx; +} + +static INLINE void HMAC_CTX_free(HMAC_CTX *ctx) +{ + HMAC_CTX_cleanup(ctx); + CRYPTO_free(ctx); +} + +#define EVP_MD_CTX_new() EVP_MD_CTX_create() +#define EVP_MD_CTX_free(ctx) EVP_MD_CTX_destroy(ctx) + +static INLINE void *BN_GENCB_get_arg(BN_GENCB *cb); + +static INLINE void *BN_GENCB_get_arg(BN_GENCB *cb) +{ + return cb->arg; +} + +static INLINE int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d); +static INLINE void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d); +static INLINE int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q); +static INLINE void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q); +static INLINE int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp); +static INLINE void RSA_get0_crt_params(const RSA *r, const BIGNUM **dmp1, const BIGNUM **dmq1, const BIGNUM **iqmp); + +static INLINE int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) +{ + r->n = n; + r->e = e; + r->d = d; + return 1; +} + +static INLINE void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d) +{ + *n = r->n; + *e = r->e; + *d = r->d; +} + +static INLINE int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q) +{ + r->p = p; + r->q = q; + return 1; +} + +static INLINE void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q) +{ + *p = r->p; + *q = r->q; +} + +static INLINE int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp) +{ + r->dmp1 = dmp1; + r->dmq1 = dmq1; + r->iqmp = iqmp; + return 1; +} + +static INLINE void RSA_get0_crt_params(const RSA *r, const BIGNUM **dmp1, const BIGNUM **dmq1, const BIGNUM **iqmp) +{ + *dmp1 = r->dmp1; + *dmq1 = r->dmq1; + *iqmp = r->iqmp; +} + +static INLINE int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key); +static INLINE int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g); +static INLINE void DSA_get0_pqg(const DSA *dsa, + const BIGNUM **p, const BIGNUM **q, const BIGNUM **g); +static INLINE void DSA_get0_key(const DSA *dsa, + const BIGNUM **pub_key, const BIGNUM **priv_key); + +static INLINE int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key) +{ + d->pub_key = pub_key; + d->priv_key = priv_key; + return 1; +} + +static INLINE int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g) +{ + d->p = p; + d->q = q; + d->g = g; + return 1; +} + +static INLINE void +DSA_get0_pqg(const DSA *dsa, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) +{ + *p = dsa->p; + *q = dsa->q; + *g = dsa->g; +} + +static INLINE void +DSA_get0_key(const DSA *dsa, const BIGNUM **pub_key, const BIGNUM **priv_key) +{ + if (pub_key) *pub_key = dsa->pub_key; + if (priv_key) *priv_key = dsa->priv_key; +} + + + +static INLINE int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key); +static INLINE int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g); +static INLINE int DH_set_length(DH *dh, long length); +static INLINE void DH_get0_pqg(const DH *dh, + const BIGNUM **p, const BIGNUM **q, const BIGNUM **g); +static INLINE void DH_get0_key(const DH *dh, + const BIGNUM **pub_key, const BIGNUM **priv_key); + +static INLINE int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key) +{ + dh->pub_key = pub_key; + dh->priv_key = priv_key; + return 1; +} + +static INLINE int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g) +{ + dh->p = p; + dh->q = q; + dh->g = g; + return 1; +} + +static INLINE int DH_set_length(DH *dh, long length) +{ + dh->length = length; + return 1; +} + + + +static INLINE void +DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) +{ + *p = dh->p; + *q = dh->q; + *g = dh->g; +} + +static INLINE void +DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key) +{ + if (pub_key) *pub_key = dh->pub_key; + if (priv_key) *priv_key = dh->priv_key; +} + +#endif /* E_EVP_COMPAT_H__ */ diff --git a/lib/crypto/c_src/fips.c b/lib/crypto/c_src/fips.c new file mode 100644 index 0000000000..b2d892d00b --- /dev/null +++ b/lib/crypto/c_src/fips.c @@ -0,0 +1,52 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "fips.h" + +ERL_NIF_TERM info_fips(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ +#ifdef FIPS_SUPPORT + return FIPS_mode() ? atom_enabled : atom_not_enabled; +#else + return atom_not_supported; +#endif +} + +ERL_NIF_TERM enable_fips_mode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Boolean) */ + if (argv[0] == atom_true) { +#ifdef FIPS_SUPPORT + if (FIPS_mode_set(1)) { + return atom_true; + } +#endif + PRINTF_ERR0("CRYPTO: Could not setup FIPS mode"); + return atom_false; + } else if (argv[0] == atom_false) { +#ifdef FIPS_SUPPORT + if (!FIPS_mode_set(0)) { + return atom_false; + } +#endif + return atom_true; + } else { + return enif_make_badarg(env); + } +} diff --git a/lib/crypto/c_src/fips.h b/lib/crypto/c_src/fips.h new file mode 100644 index 0000000000..9a436bd202 --- /dev/null +++ b/lib/crypto/c_src/fips.h @@ -0,0 +1,29 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_FIPS_H__ +#define E_FIPS_H__ 1 + +#include "common.h" + +ERL_NIF_TERM info_fips(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM enable_fips_mode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_FIPS_H__ */ diff --git a/lib/crypto/c_src/hash.c b/lib/crypto/c_src/hash.c new file mode 100644 index 0000000000..52748dc933 --- /dev/null +++ b/lib/crypto/c_src/hash.c @@ -0,0 +1,405 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "hash.h" +#include "digest.h" + +#define MD5_CTX_LEN (sizeof(MD5_CTX)) +#define MD4_CTX_LEN (sizeof(MD4_CTX)) +#define RIPEMD160_CTX_LEN (sizeof(RIPEMD160_CTX)) + +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) +struct evp_md_ctx { + EVP_MD_CTX* ctx; +}; + +/* Define resource types for OpenSSL context structures. */ +static ErlNifResourceType* evp_md_ctx_rtype; + +static void evp_md_ctx_dtor(ErlNifEnv* env, struct evp_md_ctx *ctx) { + EVP_MD_CTX_free(ctx->ctx); +} +#endif + +int init_hash_ctx(ErlNifEnv* env) { +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) + evp_md_ctx_rtype = enif_open_resource_type(env, NULL, "EVP_MD_CTX", + (ErlNifResourceDtor*) evp_md_ctx_dtor, + ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER, + NULL); + if (evp_md_ctx_rtype == NULL) { + PRINTF_ERR0("CRYPTO: Could not open resource type 'EVP_MD_CTX'"); + return 0; + } +#endif + + return 1; +} + +ERL_NIF_TERM hash_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Type, Data) */ + struct digest_type_t *digp = NULL; + const EVP_MD *md; + ErlNifBinary data; + ERL_NIF_TERM ret; + unsigned ret_size; + + digp = get_digest_type(argv[0]); + if (!digp || + !enif_inspect_iolist_as_binary(env, argv[1], &data)) { + return enif_make_badarg(env); + } + md = digp->md.p; + if (!md) { + return atom_notsup; + } + + ret_size = (unsigned)EVP_MD_size(md); + ASSERT(0 < ret_size && ret_size <= EVP_MAX_MD_SIZE); + if (!EVP_Digest(data.data, data.size, + enif_make_new_binary(env, ret_size, &ret), &ret_size, + md, NULL)) { + return atom_notsup; + } + ASSERT(ret_size == (unsigned)EVP_MD_size(md)); + + CONSUME_REDS(env, data); + return ret; +} + +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) + +ERL_NIF_TERM hash_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Type) */ + struct digest_type_t *digp = NULL; + struct evp_md_ctx *ctx; + ERL_NIF_TERM ret; + + digp = get_digest_type(argv[0]); + if (!digp) { + return enif_make_badarg(env); + } + if (!digp->md.p) { + return atom_notsup; + } + + ctx = enif_alloc_resource(evp_md_ctx_rtype, sizeof(struct evp_md_ctx)); + ctx->ctx = EVP_MD_CTX_new(); + if (!EVP_DigestInit(ctx->ctx, digp->md.p)) { + enif_release_resource(ctx); + return atom_notsup; + } + ret = enif_make_resource(env, ctx); + enif_release_resource(ctx); + return ret; +} + +ERL_NIF_TERM hash_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context, Data) */ + struct evp_md_ctx *ctx, *new_ctx; + ErlNifBinary data; + ERL_NIF_TERM ret; + + if (!enif_get_resource(env, argv[0], evp_md_ctx_rtype, (void**)&ctx) || + !enif_inspect_iolist_as_binary(env, argv[1], &data)) { + return enif_make_badarg(env); + } + + new_ctx = enif_alloc_resource(evp_md_ctx_rtype, sizeof(struct evp_md_ctx)); + new_ctx->ctx = EVP_MD_CTX_new(); + if (!EVP_MD_CTX_copy(new_ctx->ctx, ctx->ctx) || + !EVP_DigestUpdate(new_ctx->ctx, data.data, data.size)) { + enif_release_resource(new_ctx); + return atom_notsup; + } + + ret = enif_make_resource(env, new_ctx); + enif_release_resource(new_ctx); + CONSUME_REDS(env, data); + return ret; +} + +ERL_NIF_TERM hash_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context) */ + struct evp_md_ctx *ctx; + EVP_MD_CTX *new_ctx; + ERL_NIF_TERM ret; + unsigned ret_size; + + if (!enif_get_resource(env, argv[0], evp_md_ctx_rtype, (void**)&ctx)) { + return enif_make_badarg(env); + } + + ret_size = (unsigned)EVP_MD_CTX_size(ctx->ctx); + ASSERT(0 < ret_size && ret_size <= EVP_MAX_MD_SIZE); + + new_ctx = EVP_MD_CTX_new(); + if (!EVP_MD_CTX_copy(new_ctx, ctx->ctx) || + !EVP_DigestFinal(new_ctx, + enif_make_new_binary(env, ret_size, &ret), + &ret_size)) { + EVP_MD_CTX_free(new_ctx); + return atom_notsup; + } + EVP_MD_CTX_free(new_ctx); + ASSERT(ret_size == (unsigned)EVP_MD_CTX_size(ctx->ctx)); + + return ret; +} + +#else /* if OPENSSL_VERSION_NUMBER < 1.0 */ + +ERL_NIF_TERM hash_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Type) */ + typedef int (*init_fun)(unsigned char*); + struct digest_type_t *digp = NULL; + ERL_NIF_TERM ctx; + size_t ctx_size = 0; + init_fun ctx_init = 0; + + digp = get_digest_type(argv[0]); + if (!digp) { + return enif_make_badarg(env); + } + if (!digp->md.p) { + return atom_notsup; + } + + switch (EVP_MD_type(digp->md.p)) + { + case NID_md4: + ctx_size = MD4_CTX_LEN; + ctx_init = (init_fun)(&MD4_Init); + break; + case NID_md5: + ctx_size = MD5_CTX_LEN; + ctx_init = (init_fun)(&MD5_Init); + break; + case NID_ripemd160: + ctx_size = RIPEMD160_CTX_LEN; + ctx_init = (init_fun)(&RIPEMD160_Init); + break; + case NID_sha1: + ctx_size = sizeof(SHA_CTX); + ctx_init = (init_fun)(&SHA1_Init); + break; +#ifdef HAVE_SHA224 + case NID_sha224: + ctx_size = sizeof(SHA256_CTX); + ctx_init = (init_fun)(&SHA224_Init); + break; +#endif +#ifdef HAVE_SHA256 + case NID_sha256: + ctx_size = sizeof(SHA256_CTX); + ctx_init = (init_fun)(&SHA256_Init); + break; +#endif +#ifdef HAVE_SHA384 + case NID_sha384: + ctx_size = sizeof(SHA512_CTX); + ctx_init = (init_fun)(&SHA384_Init); + break; +#endif +#ifdef HAVE_SHA512 + case NID_sha512: + ctx_size = sizeof(SHA512_CTX); + ctx_init = (init_fun)(&SHA512_Init); + break; +#endif + default: + return atom_notsup; + } + ASSERT(ctx_size); + ASSERT(ctx_init); + + ctx_init(enif_make_new_binary(env, ctx_size, &ctx)); + return enif_make_tuple2(env, argv[0], ctx); +} + +ERL_NIF_TERM hash_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* ({Type, Context}, Data) */ + typedef int (*update_fun)(unsigned char*, const unsigned char*, size_t); + ERL_NIF_TERM new_ctx; + ErlNifBinary ctx, data; + const ERL_NIF_TERM *tuple; + int arity; + struct digest_type_t *digp = NULL; + unsigned char *ctx_buff; + size_t ctx_size = 0; + update_fun ctx_update = 0; + + if (!enif_get_tuple(env, argv[0], &arity, &tuple) || + arity != 2 || + !(digp = get_digest_type(tuple[0])) || + !enif_inspect_binary(env, tuple[1], &ctx) || + !enif_inspect_iolist_as_binary(env, argv[1], &data)) { + return enif_make_badarg(env); + } + if (!digp->md.p) { + return atom_notsup; + } + + switch (EVP_MD_type(digp->md.p)) + { + case NID_md4: + ctx_size = MD4_CTX_LEN; + ctx_update = (update_fun)(&MD4_Update); + break; + case NID_md5: + ctx_size = MD5_CTX_LEN; + ctx_update = (update_fun)(&MD5_Update); + break; + case NID_ripemd160: + ctx_size = RIPEMD160_CTX_LEN; + ctx_update = (update_fun)(&RIPEMD160_Update); + break; + case NID_sha1: + ctx_size = sizeof(SHA_CTX); + ctx_update = (update_fun)(&SHA1_Update); + break; +#ifdef HAVE_SHA224 + case NID_sha224: + ctx_size = sizeof(SHA256_CTX); + ctx_update = (update_fun)(&SHA224_Update); + break; +#endif +#ifdef HAVE_SHA256 + case NID_sha256: + ctx_size = sizeof(SHA256_CTX); + ctx_update = (update_fun)(&SHA256_Update); + break; +#endif +#ifdef HAVE_SHA384 + case NID_sha384: + ctx_size = sizeof(SHA512_CTX); + ctx_update = (update_fun)(&SHA384_Update); + break; +#endif +#ifdef HAVE_SHA512 + case NID_sha512: + ctx_size = sizeof(SHA512_CTX); + ctx_update = (update_fun)(&SHA512_Update); + break; +#endif + default: + return atom_notsup; + } + ASSERT(ctx_size); + ASSERT(ctx_update); + + if (ctx.size != ctx_size) { + return enif_make_badarg(env); + } + + ctx_buff = enif_make_new_binary(env, ctx_size, &new_ctx); + memcpy(ctx_buff, ctx.data, ctx_size); + ctx_update(ctx_buff, data.data, data.size); + + CONSUME_REDS(env, data); + return enif_make_tuple2(env, tuple[0], new_ctx); +} + +ERL_NIF_TERM hash_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* ({Type, Context}) */ + typedef int (*final_fun)(unsigned char*, void*); + ERL_NIF_TERM ret; + ErlNifBinary ctx; + const ERL_NIF_TERM *tuple; + int arity; + struct digest_type_t *digp = NULL; + const EVP_MD *md; + void *new_ctx; + size_t ctx_size = 0; + final_fun ctx_final = 0; + + if (!enif_get_tuple(env, argv[0], &arity, &tuple) || + arity != 2 || + !(digp = get_digest_type(tuple[0])) || + !enif_inspect_binary(env, tuple[1], &ctx)) { + return enif_make_badarg(env); + } + md = digp->md.p; + if (!md) { + return atom_notsup; + } + + switch (EVP_MD_type(md)) + { + case NID_md4: + ctx_size = MD4_CTX_LEN; + ctx_final = (final_fun)(&MD4_Final); + break; + case NID_md5: + ctx_size = MD5_CTX_LEN; + ctx_final = (final_fun)(&MD5_Final); + break; + case NID_ripemd160: + ctx_size = RIPEMD160_CTX_LEN; + ctx_final = (final_fun)(&RIPEMD160_Final); + break; + case NID_sha1: + ctx_size = sizeof(SHA_CTX); + ctx_final = (final_fun)(&SHA1_Final); + break; +#ifdef HAVE_SHA224 + case NID_sha224: + ctx_size = sizeof(SHA256_CTX); + ctx_final = (final_fun)(&SHA224_Final); + break; +#endif +#ifdef HAVE_SHA256 + case NID_sha256: + ctx_size = sizeof(SHA256_CTX); + ctx_final = (final_fun)(&SHA256_Final); + break; +#endif +#ifdef HAVE_SHA384 + case NID_sha384: + ctx_size = sizeof(SHA512_CTX); + ctx_final = (final_fun)(&SHA384_Final); + break; +#endif +#ifdef HAVE_SHA512 + case NID_sha512: + ctx_size = sizeof(SHA512_CTX); + ctx_final = (final_fun)(&SHA512_Final); + break; +#endif + default: + return atom_notsup; + } + ASSERT(ctx_size); + ASSERT(ctx_final); + + if (ctx.size != ctx_size) { + return enif_make_badarg(env); + } + + new_ctx = enif_alloc(ctx_size); + memcpy(new_ctx, ctx.data, ctx_size); + ctx_final(enif_make_new_binary(env, (size_t)EVP_MD_size(md), &ret), + new_ctx); + enif_free(new_ctx); + + return ret; +} + +#endif /* OPENSSL_VERSION_NUMBER < 1.0 */ diff --git a/lib/crypto/c_src/hash.h b/lib/crypto/c_src/hash.h new file mode 100644 index 0000000000..8bae07f39a --- /dev/null +++ b/lib/crypto/c_src/hash.h @@ -0,0 +1,33 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_HASH_H__ +#define E_HASH_H__ 1 + +#include "common.h" + +int init_hash_ctx(ErlNifEnv *env); + +ERL_NIF_TERM hash_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM hash_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM hash_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM hash_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_HASH_H__ */ diff --git a/lib/crypto/c_src/hmac.c b/lib/crypto/c_src/hmac.c new file mode 100644 index 0000000000..143cde90e1 --- /dev/null +++ b/lib/crypto/c_src/hmac.c @@ -0,0 +1,185 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "hmac.h" +#include "digest.h" + +struct hmac_context +{ + ErlNifMutex* mtx; + int alive; + HMAC_CTX* ctx; +}; + +static ErlNifResourceType* hmac_context_rtype; + +static void hmac_context_dtor(ErlNifEnv* env, struct hmac_context*); + +int init_hmac_ctx(ErlNifEnv *env) { + hmac_context_rtype = enif_open_resource_type(env, NULL, "hmac_context", + (ErlNifResourceDtor*) hmac_context_dtor, + ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER, + NULL); + if (hmac_context_rtype == NULL) { + PRINTF_ERR0("CRYPTO: Could not open resource type 'hmac_context'"); + return 0; + } + return 1; +} + +ERL_NIF_TERM hmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Type, Key, Data) or (Type, Key, Data, MacSize) */ + struct digest_type_t *digp = NULL; + ErlNifBinary key, data; + unsigned char buff[EVP_MAX_MD_SIZE]; + unsigned size = 0, req_size = 0; + ERL_NIF_TERM ret; + + digp = get_digest_type(argv[0]); + if (!digp || + !enif_inspect_iolist_as_binary(env, argv[1], &key) || + !enif_inspect_iolist_as_binary(env, argv[2], &data) || + (argc == 4 && !enif_get_uint(env, argv[3], &req_size))) { + return enif_make_badarg(env); + } + + if (!digp->md.p || + !HMAC(digp->md.p, + key.data, key.size, + data.data, data.size, + buff, &size)) { + return atom_notsup; + } + ASSERT(0 < size && size <= EVP_MAX_MD_SIZE); + CONSUME_REDS(env, data); + + if (argc == 4) { + if (req_size <= size) { + size = req_size; + } + else { + return enif_make_badarg(env); + } + } + memcpy(enif_make_new_binary(env, size, &ret), buff, size); + return ret; +} + +static void hmac_context_dtor(ErlNifEnv* env, struct hmac_context *obj) +{ + if (obj->alive) { + HMAC_CTX_free(obj->ctx); + obj->alive = 0; + } + enif_mutex_destroy(obj->mtx); +} + +ERL_NIF_TERM hmac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Type, Key) */ + struct digest_type_t *digp = NULL; + ErlNifBinary key; + ERL_NIF_TERM ret; + struct hmac_context *obj; + + digp = get_digest_type(argv[0]); + if (!digp || + !enif_inspect_iolist_as_binary(env, argv[1], &key)) { + return enif_make_badarg(env); + } + if (!digp->md.p) { + return atom_notsup; + } + + obj = enif_alloc_resource(hmac_context_rtype, sizeof(struct hmac_context)); + obj->mtx = enif_mutex_create("crypto.hmac"); + obj->alive = 1; + obj->ctx = HMAC_CTX_new(); +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) + // Check the return value of HMAC_Init: it may fail in FIPS mode + // for disabled algorithms + if (!HMAC_Init_ex(obj->ctx, key.data, key.size, digp->md.p, NULL)) { + enif_release_resource(obj); + return atom_notsup; + } +#else + HMAC_Init_ex(obj->ctx, key.data, key.size, digp->md.p, NULL); +#endif + + ret = enif_make_resource(env, obj); + enif_release_resource(obj); + return ret; +} + +ERL_NIF_TERM hmac_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context, Data) */ + ErlNifBinary data; + struct hmac_context* obj; + + if (!enif_get_resource(env, argv[0], hmac_context_rtype, (void**)&obj) + || !enif_inspect_iolist_as_binary(env, argv[1], &data)) { + return enif_make_badarg(env); + } + enif_mutex_lock(obj->mtx); + if (!obj->alive) { + enif_mutex_unlock(obj->mtx); + return enif_make_badarg(env); + } + HMAC_Update(obj->ctx, data.data, data.size); + enif_mutex_unlock(obj->mtx); + + CONSUME_REDS(env,data); + return argv[0]; +} + +ERL_NIF_TERM hmac_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context) or (Context, HashLen) */ + ERL_NIF_TERM ret; + struct hmac_context* obj; + unsigned char mac_buf[EVP_MAX_MD_SIZE]; + unsigned char * mac_bin; + unsigned int req_len = 0; + unsigned int mac_len; + + if (!enif_get_resource(env,argv[0],hmac_context_rtype, (void**)&obj) + || (argc == 2 && !enif_get_uint(env, argv[1], &req_len))) { + return enif_make_badarg(env); + } + + enif_mutex_lock(obj->mtx); + if (!obj->alive) { + enif_mutex_unlock(obj->mtx); + return enif_make_badarg(env); + } + + HMAC_Final(obj->ctx, mac_buf, &mac_len); + HMAC_CTX_free(obj->ctx); + obj->alive = 0; + enif_mutex_unlock(obj->mtx); + + if (argc == 2 && req_len < mac_len) { + /* Only truncate to req_len bytes if asked. */ + mac_len = req_len; + } + mac_bin = enif_make_new_binary(env, mac_len, &ret); + memcpy(mac_bin, mac_buf, mac_len); + + return ret; +} + diff --git a/lib/crypto/c_src/hmac.h b/lib/crypto/c_src/hmac.h new file mode 100644 index 0000000000..1f0e0ca632 --- /dev/null +++ b/lib/crypto/c_src/hmac.h @@ -0,0 +1,33 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_HMAC_H__ +#define E_HMAC_H__ 1 + +#include "common.h" + +int init_hmac_ctx(ErlNifEnv *env); + +ERL_NIF_TERM hmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM hmac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM hmac_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM hmac_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_HMAC_H__ */ diff --git a/lib/crypto/c_src/info.c b/lib/crypto/c_src/info.c new file mode 100644 index 0000000000..3f3194081d --- /dev/null +++ b/lib/crypto/c_src/info.c @@ -0,0 +1,81 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "info.h" + +#ifdef HAVE_DYNAMIC_CRYPTO_LIB + +# if defined(DEBUG) +char *crypto_callback_name = "crypto_callback.debug"; +# elif defined(VALGRIND) +char *crypto_callback_name = "crypto_callback.valgrind"; +# else +char *crypto_callback_name = "crypto_callback"; +# endif + +int change_basename(ErlNifBinary* bin, char* buf, int bufsz, const char* newfile) +{ + int i; + + for (i = bin->size; i > 0; i--) { + if (bin->data[i-1] == '/') + break; + } + if (i + strlen(newfile) >= bufsz) { + PRINTF_ERR0("CRYPTO: lib name too long"); + return 0; + } + memcpy(buf, bin->data, i); + strcpy(buf+i, newfile); + return 1; +} + +void error_handler(void* null, const char* errstr) +{ + PRINTF_ERR1("CRYPTO LOADING ERROR: '%s'", errstr); +} +#endif /* HAVE_DYNAMIC_CRYPTO_LIB */ + +ERL_NIF_TERM info_lib(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + /* [{<<"OpenSSL">>,9470143,<<"OpenSSL 0.9.8k 25 Mar 2009">>}] */ + + static const char libname[] = "OpenSSL"; + unsigned name_sz = strlen(libname); + const char* ver = SSLeay_version(SSLEAY_VERSION); + unsigned ver_sz = strlen(ver); + ERL_NIF_TERM name_term, ver_term; + int ver_num = OPENSSL_VERSION_NUMBER; + /* R16: + * Ignore library version number from SSLeay() and instead show header + * version. Otherwise user might try to call a function that is implemented + * by a newer library but not supported by the headers used at compile time. + * Example: DES_ede3_cfb_encrypt in 0.9.7i but not in 0.9.7d. + * + * Version string is still from library though. + */ + + memcpy(enif_make_new_binary(env, name_sz, &name_term), libname, name_sz); + memcpy(enif_make_new_binary(env, ver_sz, &ver_term), ver, ver_sz); + + return enif_make_list1(env, enif_make_tuple3(env, name_term, + enif_make_int(env, ver_num), + ver_term)); +} diff --git a/lib/crypto/c_src/info.h b/lib/crypto/c_src/info.h new file mode 100644 index 0000000000..4f8822ddd7 --- /dev/null +++ b/lib/crypto/c_src/info.h @@ -0,0 +1,35 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_INFO_H__ +#define E_INFO_H__ 1 + +#include "common.h" + +#ifdef HAVE_DYNAMIC_CRYPTO_LIB +extern char *crypto_callback_name; + +int change_basename(ErlNifBinary* bin, char* buf, int bufsz, const char* newfile); +void error_handler(void* null, const char* errstr); +#endif + +ERL_NIF_TERM info_lib(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_INFO_H__ */ diff --git a/lib/crypto/c_src/math.c b/lib/crypto/c_src/math.c new file mode 100644 index 0000000000..7d7d146ca9 --- /dev/null +++ b/lib/crypto/c_src/math.c @@ -0,0 +1,43 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "math.h" + +ERL_NIF_TERM do_exor(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Data1, Data2) */ + ErlNifBinary d1, d2; + unsigned char* ret_ptr; + int i; + ERL_NIF_TERM ret; + + if (!enif_inspect_iolist_as_binary(env,argv[0], &d1) + || !enif_inspect_iolist_as_binary(env,argv[1], &d2) + || d1.size != d2.size) { + return enif_make_badarg(env); + } + ret_ptr = enif_make_new_binary(env, d1.size, &ret); + + for (i=0; i<d1.size; i++) { + ret_ptr[i] = d1.data[i] ^ d2.data[i]; + } + CONSUME_REDS(env,d1); + return ret; +} + diff --git a/lib/crypto/c_src/math.h b/lib/crypto/c_src/math.h new file mode 100644 index 0000000000..b8d68ea654 --- /dev/null +++ b/lib/crypto/c_src/math.h @@ -0,0 +1,28 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_MATH_H__ +#define E_MATH_H__ 1 + +#include "common.h" + +ERL_NIF_TERM do_exor(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_MATH_H__ */ diff --git a/lib/crypto/c_src/openssl_config.h b/lib/crypto/c_src/openssl_config.h new file mode 100644 index 0000000000..2e5f5b22c1 --- /dev/null +++ b/lib/crypto/c_src/openssl_config.h @@ -0,0 +1,337 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_OPENSSL_CONFIG_H__ +#define E_OPENSSL_CONFIG_H__ 1 + +#define OPENSSL_THREAD_DEFINES +#include <openssl/opensslconf.h> + +#include <openssl/crypto.h> +#ifndef OPENSSL_NO_DES +#include <openssl/des.h> +#endif /* #ifndef OPENSSL_NO_DES */ +/* #include <openssl/idea.h> This is not supported on the openssl OTP requires */ +#include <openssl/dsa.h> +#include <openssl/rsa.h> +#include <openssl/aes.h> +#include <openssl/md5.h> +#include <openssl/md4.h> +#include <openssl/sha.h> +#include <openssl/ripemd.h> +#include <openssl/bn.h> +#include <openssl/objects.h> +#ifndef OPENSSL_NO_RC4 + #include <openssl/rc4.h> +#endif /* OPENSSL_NO_RC4 */ +#ifndef OPENSSL_NO_RC2 + #include <openssl/rc2.h> +#endif +#include <openssl/blowfish.h> +#include <openssl/rand.h> +#include <openssl/evp.h> +#include <openssl/hmac.h> +#include <openssl/err.h> + +/* Helper macro to construct a OPENSSL_VERSION_NUMBER. + * See openssl/opensslv.h + */ +#define PACKED_OPENSSL_VERSION(MAJ, MIN, FIX, P) \ + ((((((((MAJ << 8) | MIN) << 8 ) | FIX) << 8) | (P-'a'+1)) << 4) | 0xf) + +#define PACKED_OPENSSL_VERSION_PLAIN(MAJ, MIN, FIX) \ + PACKED_OPENSSL_VERSION(MAJ,MIN,FIX,('a'-1)) + + +/* LibreSSL was cloned from OpenSSL 1.0.1g and claims to be API and BPI compatible + * with 1.0.1. + * + * LibreSSL has the same names on include files and symbols as OpenSSL, but defines + * the OPENSSL_VERSION_NUMBER to be >= 2.0.0 + * + * Therefor works tests like this as intendend: + * OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) + * (The test is for example "2.4.2" >= "1.0.0" although the test + * with the cloned OpenSSL test would be "1.0.1" >= "1.0.0") + * + * But tests like this gives wrong result: + * OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(1,1,0) + * (The test is false since "2.4.2" < "1.1.0". It should have been + * true because the LibreSSL API version is "1.0.1") + * + */ + +#ifdef LIBRESSL_VERSION_NUMBER +/* A macro to test on in this file */ +#define HAS_LIBRESSL +#endif + +#ifdef HAS_LIBRESSL +/* LibreSSL dislikes FIPS */ +# ifdef FIPS_SUPPORT +# undef FIPS_SUPPORT +# endif + +# if LIBRESSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(2,7,0) +/* LibreSSL wants the 1.0.1 API */ +# define NEED_EVP_COMPATIBILITY_FUNCTIONS +# endif +#endif + + +#if OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(1,1,0) +# define NEED_EVP_COMPATIBILITY_FUNCTIONS +#endif + + +#ifndef HAS_LIBRESSL +# if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) +# define HAS_EVP_PKEY_CTX +# endif +#endif + + +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) +#include <openssl/modes.h> +#endif + +#include "crypto_callback.h" + +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(0,9,8) \ + && !defined(OPENSSL_NO_SHA224) && defined(NID_sha224) \ + && !defined(OPENSSL_NO_SHA256) /* disabled like this in my sha.h (?) */ +# define HAVE_SHA224 +#endif +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(0,9,8) \ + && !defined(OPENSSL_NO_SHA256) && defined(NID_sha256) +# define HAVE_SHA256 +#endif +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(0,9,8) \ + && !defined(OPENSSL_NO_SHA384) && defined(NID_sha384)\ + && !defined(OPENSSL_NO_SHA512) /* disabled like this in my sha.h (?) */ +# define HAVE_SHA384 +#endif +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(0,9,8) \ + && !defined(OPENSSL_NO_SHA512) && defined(NID_sha512) +# define HAVE_SHA512 +#endif +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION(0,9,7,'e') +# define HAVE_DES_ede3_cfb_encrypt +#endif + +// SHA3: +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,1,1) +// An error in beta releases of 1.1.1 fixed in production release +# ifdef NID_sha3_224 +# define HAVE_SHA3_224 +# endif +# ifdef NID_sha3_256 +# define HAVE_SHA3_256 +# endif +#endif +# ifdef NID_sha3_384 +# define HAVE_SHA3_384 +# endif +# ifdef NID_sha3_512 +# define HAVE_SHA3_512 +# endif + +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION(0,9,8,'o') \ + && !defined(OPENSSL_NO_EC) \ + && !defined(OPENSSL_NO_ECDH) \ + && !defined(OPENSSL_NO_ECDSA) +# define HAVE_EC +#endif + +// (test for >= 1.1.1pre8) +#if OPENSSL_VERSION_NUMBER >= (PACKED_OPENSSL_VERSION_PLAIN(1,1,1) -7) \ + && !defined(HAS_LIBRESSL) \ + && defined(HAVE_EC) +# define HAVE_ED_CURVE_DH +# if OPENSSL_VERSION_NUMBER >= (PACKED_OPENSSL_VERSION_PLAIN(1,1,1)) +# define HAVE_EDDSA +# endif +#endif + +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION(0,9,8,'c') +# define HAVE_AES_IGE +#endif + +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,1) +# define HAVE_EVP_AES_CTR +# define HAVE_AEAD +# define HAVE_GCM +# define HAVE_CCM +# define HAVE_CMAC +# if defined(RSA_PKCS1_OAEP_PADDING) +# define HAVE_RSA_OAEP_PADDING +# endif +# define HAVE_RSA_MGF1_MD +# if OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION(1,0,1,'d') +# define HAVE_GCM_EVP_DECRYPT_BUG +# endif +#endif + +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,1,0) +# ifndef HAS_LIBRESSL +# define HAVE_CHACHA20 +# define HAVE_CHACHA20_POLY1305 +# define HAVE_RSA_OAEP_MD +# endif +#endif + +// OPENSSL_VERSION_NUMBER >= 1.1.1-pre8 +#if OPENSSL_VERSION_NUMBER >= (PACKED_OPENSSL_VERSION_PLAIN(1,1,1)-7) +# ifndef HAS_LIBRESSL +# define HAVE_POLY1305 +# endif +#endif + +#if OPENSSL_VERSION_NUMBER <= PACKED_OPENSSL_VERSION(0,9,8,'l') +# define HAVE_ECB_IVEC_BUG +#endif + +#ifndef HAS_LIBRESSL +# ifdef RSA_SSLV23_PADDING +# define HAVE_RSA_SSLV23_PADDING +# endif +#endif + +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) +# ifdef RSA_PKCS1_PSS_PADDING +# define HAVE_RSA_PKCS1_PSS_PADDING +# endif +#endif + +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION(0,9,8,'h') \ + && defined(HAVE_EC) +/* If OPENSSL_NO_EC is set, there will be an error in ec.h included from engine.h + So if EC is disabled, you can't use Engine either.... +*/ +# define HAS_ENGINE_SUPPORT +#endif + + +#if defined(HAS_ENGINE_SUPPORT) +# include <openssl/engine.h> +#endif + +#if defined(HAVE_CMAC) +#include <openssl/cmac.h> +#endif + +#if defined(HAVE_EC) +#include <openssl/ec.h> +#include <openssl/ecdh.h> +#include <openssl/ecdsa.h> +#endif + +#ifdef VALGRIND + # include <valgrind/memcheck.h> + +/* libcrypto mixes supplied buffer contents into its entropy pool, + which makes valgrind complain about the use of uninitialized data. + We use this valgrind "request" to make sure that no such seemingly + undefined data is returned. +*/ + # define ERL_VALGRIND_MAKE_MEM_DEFINED(ptr,size) \ + VALGRIND_MAKE_MEM_DEFINED(ptr,size) + + # define ERL_VALGRIND_ASSERT_MEM_DEFINED(Ptr,Size) \ + do { \ + int __erl_valgrind_mem_defined = VALGRIND_CHECK_MEM_IS_DEFINED((Ptr),(Size)); \ + if (__erl_valgrind_mem_defined != 0) { \ + fprintf(stderr,"\r\n####### VALGRIND_ASSSERT(%p,%ld) failed at %s:%d\r\n", \ + (Ptr),(long)(Size), __FILE__, __LINE__); \ + abort(); \ + } \ + } while (0) + +#else + # define ERL_VALGRIND_MAKE_MEM_DEFINED(ptr,size) + # define ERL_VALGRIND_ASSERT_MEM_DEFINED(ptr,size) +#endif + +#ifdef DEBUG + # define ASSERT(e) \ + ((void) ((e) ? 1 : (fprintf(stderr,"Assert '%s' failed at %s:%d\n",\ + #e, __FILE__, __LINE__), abort(), 0))) +#else + # define ASSERT(e) ((void) 1) +#endif + +#ifdef __GNUC__ + # define INLINE __inline__ +#elif defined(__WIN32__) + # define INLINE __forceinline +#else + # define INLINE +#endif + + +#define get_int32(s) ((((unsigned char*) (s))[0] << 24) | \ + (((unsigned char*) (s))[1] << 16) | \ + (((unsigned char*) (s))[2] << 8) | \ + (((unsigned char*) (s))[3])) + +#define put_int32(s,i) \ +{ (s)[0] = (char)(((i) >> 24) & 0xff);\ + (s)[1] = (char)(((i) >> 16) & 0xff);\ + (s)[2] = (char)(((i) >> 8) & 0xff);\ + (s)[3] = (char)((i) & 0xff);\ +} + +/* This shall correspond to the similar macro in crypto.erl */ +/* Current value is: erlang:system_info(context_reductions) * 10 */ +#define MAX_BYTES_TO_NIF 20000 + +#define CONSUME_REDS(NifEnv, Ibin) \ +do { \ + int _cost = ((Ibin).size * 100) / MAX_BYTES_TO_NIF;\ + if (_cost) { \ + (void) enif_consume_timeslice((NifEnv), \ + (_cost > 100) ? 100 : _cost); \ + } \ + } while (0) + +#ifdef NEED_EVP_COMPATIBILITY_FUNCTIONS +# include "evp_compat.h" +#else +# define HAVE_OPAQUE_BN_GENCB +#endif + +/* +#define PRINTF_ERR0(FMT) enif_fprintf(stderr, FMT "\n") +#define PRINTF_ERR1(FMT, A1) enif_fprintf(stderr, FMT "\n", A1) +#define PRINTF_ERR2(FMT, A1, A2) enif_fprintf(stderr, FMT "\n", A1, A2) +*/ + +#define PRINTF_ERR0(FMT) +#define PRINTF_ERR1(FMT,A1) +#define PRINTF_ERR2(FMT,A1,A2) + +#ifdef FIPS_SUPPORT +/* In FIPS mode non-FIPS algorithms are disabled and return badarg. */ +#define CHECK_NO_FIPS_MODE() { if (FIPS_mode()) return atom_notsup; } +#else +#define CHECK_NO_FIPS_MODE() +#endif + +#endif /* E_OPENSSL_CONFIG_H__ */ diff --git a/lib/crypto/c_src/pkey.c b/lib/crypto/c_src/pkey.c new file mode 100644 index 0000000000..bd56b2d977 --- /dev/null +++ b/lib/crypto/c_src/pkey.c @@ -0,0 +1,1170 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "pkey.h" +#include "bn.h" +#include "digest.h" +#include "dss.h" +#include "ec.h" +#include "eddsa.h" +#include "engine.h" +#include "rsa.h" + +#define PKEY_BADARG -1 +#define PKEY_NOTSUP 0 +#define PKEY_OK 1 + +typedef struct PKeyCryptOptions { + const EVP_MD *rsa_mgf1_md; + ErlNifBinary rsa_oaep_label; + const EVP_MD *rsa_oaep_md; + int rsa_padding; + const EVP_MD *signature_md; +} PKeyCryptOptions; + +typedef struct PKeySignOptions { + const EVP_MD *rsa_mgf1_md; + int rsa_padding; + int rsa_pss_saltlen; +} PKeySignOptions; + + +static int get_pkey_digest_type(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM type, + const EVP_MD **md); +static int get_pkey_sign_digest(ErlNifEnv *env, ERL_NIF_TERM algorithm, + ERL_NIF_TERM type, ERL_NIF_TERM data, + unsigned char *md_value, const EVP_MD **mdp, + unsigned char **tbsp, size_t *tbslenp); +static int get_pkey_sign_options(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM options, + const EVP_MD *md, PKeySignOptions *opt); +static int get_pkey_private_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM key, EVP_PKEY **pkey); +static int get_pkey_public_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM key, + EVP_PKEY **pkey); +static int get_pkey_crypt_options(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM options, + PKeyCryptOptions *opt); +static size_t size_of_RSA(EVP_PKEY *pkey); + + +static int get_pkey_digest_type(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM type, + const EVP_MD **md) +{ + struct digest_type_t *digp = NULL; + *md = NULL; + + if (type == atom_none && algorithm == atom_rsa) return PKEY_OK; +#ifdef HAVE_EDDSA + if (algorithm == atom_eddsa) return PKEY_OK; +#endif + digp = get_digest_type(type); + if (!digp) return PKEY_BADARG; + if (!digp->md.p) return PKEY_NOTSUP; + + *md = digp->md.p; + return PKEY_OK; +} + +static int get_pkey_sign_digest(ErlNifEnv *env, ERL_NIF_TERM algorithm, + ERL_NIF_TERM type, ERL_NIF_TERM data, + unsigned char *md_value, const EVP_MD **mdp, + unsigned char **tbsp, size_t *tbslenp) +{ + int i; + const ERL_NIF_TERM *tpl_terms; + int tpl_arity; + ErlNifBinary tbs_bin; + EVP_MD_CTX *mdctx; + const EVP_MD *md = *mdp; + unsigned char *tbs = *tbsp; + size_t tbslen = *tbslenp; + unsigned int tbsleni; + + if ((i = get_pkey_digest_type(env, algorithm, type, &md)) != PKEY_OK) { + return i; + } + if (enif_get_tuple(env, data, &tpl_arity, &tpl_terms)) { + if (tpl_arity != 2 || tpl_terms[0] != atom_digest + || !enif_inspect_binary(env, tpl_terms[1], &tbs_bin) + || (md != NULL && tbs_bin.size != EVP_MD_size(md))) { + return PKEY_BADARG; + } + /* We have a digest (= hashed text) in tbs_bin */ + tbs = tbs_bin.data; + tbslen = tbs_bin.size; + } else if (md == NULL) { + if (!enif_inspect_binary(env, data, &tbs_bin)) { + return PKEY_BADARG; + } + /* md == NULL, that is no hashing because DigestType argument was atom_none */ + tbs = tbs_bin.data; + tbslen = tbs_bin.size; + } else { + if (!enif_inspect_binary(env, data, &tbs_bin)) { + return PKEY_BADARG; + } + /* We have the cleartext in tbs_bin and the hash algo info in md */ + tbs = md_value; + mdctx = EVP_MD_CTX_create(); + if (!mdctx) { + return PKEY_BADARG; + } + /* Looks well, now hash the plain text into a digest according to md */ + if (EVP_DigestInit_ex(mdctx, md, NULL) <= 0) { + EVP_MD_CTX_destroy(mdctx); + return PKEY_BADARG; + } + if (EVP_DigestUpdate(mdctx, tbs_bin.data, tbs_bin.size) <= 0) { + EVP_MD_CTX_destroy(mdctx); + return PKEY_BADARG; + } + if (EVP_DigestFinal_ex(mdctx, tbs, &tbsleni) <= 0) { + EVP_MD_CTX_destroy(mdctx); + return PKEY_BADARG; + } + tbslen = (size_t)(tbsleni); + EVP_MD_CTX_destroy(mdctx); + } + + *mdp = md; + *tbsp = tbs; + *tbslenp = tbslen; + + return PKEY_OK; +} + +static int get_pkey_sign_options(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM options, + const EVP_MD *md, PKeySignOptions *opt) +{ + ERL_NIF_TERM head, tail; + const ERL_NIF_TERM *tpl_terms; + int tpl_arity; + const EVP_MD *opt_md; + int i; + + if (!enif_is_list(env, options)) { + return PKEY_BADARG; + } + + /* defaults */ + if (algorithm == atom_rsa) { + opt->rsa_mgf1_md = NULL; + opt->rsa_padding = RSA_PKCS1_PADDING; + opt->rsa_pss_saltlen = -2; + } + + if (enif_is_empty_list(env, options)) { + return PKEY_OK; + } + + if (algorithm == atom_rsa) { + tail = options; + while (enif_get_list_cell(env, tail, &head, &tail)) { + if (enif_get_tuple(env, head, &tpl_arity, &tpl_terms) && tpl_arity == 2) { + if (tpl_terms[0] == atom_rsa_mgf1_md && enif_is_atom(env, tpl_terms[1])) { + i = get_pkey_digest_type(env, algorithm, tpl_terms[1], &opt_md); + if (i != PKEY_OK) { + return i; + } + opt->rsa_mgf1_md = opt_md; + } else if (tpl_terms[0] == atom_rsa_padding) { + if (tpl_terms[1] == atom_rsa_pkcs1_padding) { + opt->rsa_padding = RSA_PKCS1_PADDING; + } else if (tpl_terms[1] == atom_rsa_pkcs1_pss_padding) { +#ifdef HAVE_RSA_PKCS1_PSS_PADDING + opt->rsa_padding = RSA_PKCS1_PSS_PADDING; + if (opt->rsa_mgf1_md == NULL) { + opt->rsa_mgf1_md = md; + } +#else + return PKEY_NOTSUP; +#endif + } else if (tpl_terms[1] == atom_rsa_x931_padding) { + opt->rsa_padding = RSA_X931_PADDING; + } else if (tpl_terms[1] == atom_rsa_no_padding) { + opt->rsa_padding = RSA_NO_PADDING; + } else { + return PKEY_BADARG; + } + } else if (tpl_terms[0] == atom_rsa_pss_saltlen) { + if (!enif_get_int(env, tpl_terms[1], &(opt->rsa_pss_saltlen)) + || opt->rsa_pss_saltlen < -2) { + return PKEY_BADARG; + } + } else { + return PKEY_BADARG; + } + } else { + return PKEY_BADARG; + } + } + } else { + return PKEY_BADARG; + } + + return PKEY_OK; +} + +static int get_pkey_private_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM key, EVP_PKEY **pkey) +{ + if (enif_is_map(env, key)) { +#ifdef HAS_ENGINE_SUPPORT + /* Use key stored in engine */ + ENGINE *e; + char *id = NULL; + char *password; + + if (!get_engine_and_key_id(env, key, &id, &e)) + return PKEY_BADARG; + password = get_key_password(env, key); + *pkey = ENGINE_load_private_key(e, id, NULL, password); + if (password) enif_free(password); + enif_free(id); + if (!*pkey) + return PKEY_BADARG; +#else + return PKEY_BADARG; +#endif + } + else if (algorithm == atom_rsa) { + RSA *rsa = RSA_new(); + + if (!get_rsa_private_key(env, key, rsa)) { + RSA_free(rsa); + return PKEY_BADARG; + } + + *pkey = EVP_PKEY_new(); + if (!EVP_PKEY_assign_RSA(*pkey, rsa)) { + EVP_PKEY_free(*pkey); + RSA_free(rsa); + return PKEY_BADARG; + } + } else if (algorithm == atom_ecdsa) { +#if defined(HAVE_EC) + EC_KEY *ec = NULL; + const ERL_NIF_TERM *tpl_terms; + int tpl_arity; + + if (enif_get_tuple(env, key, &tpl_arity, &tpl_terms) && tpl_arity == 2 + && enif_is_tuple(env, tpl_terms[0]) && enif_is_binary(env, tpl_terms[1]) + && get_ec_key(env, tpl_terms[0], tpl_terms[1], atom_undefined, &ec)) { + + *pkey = EVP_PKEY_new(); + if (!EVP_PKEY_assign_EC_KEY(*pkey, ec)) { + EVP_PKEY_free(*pkey); + EC_KEY_free(ec); + return PKEY_BADARG; + } + } else { + return PKEY_BADARG; + } +#else + return PKEY_NOTSUP; +#endif + } else if (algorithm == atom_eddsa) { +#if defined(HAVE_EDDSA) + if (!get_eddsa_key(env, 0, key, pkey)) { + return PKEY_BADARG; + } +#else + return PKEY_NOTSUP; +#endif + } else if (algorithm == atom_dss) { + DSA *dsa = DSA_new(); + + if (!get_dss_private_key(env, key, dsa)) { + DSA_free(dsa); + return PKEY_BADARG; + } + + *pkey = EVP_PKEY_new(); + if (!EVP_PKEY_assign_DSA(*pkey, dsa)) { + EVP_PKEY_free(*pkey); + DSA_free(dsa); + return PKEY_BADARG; + } + } else { + return PKEY_BADARG; + } + + return PKEY_OK; +} + +static int get_pkey_public_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM key, + EVP_PKEY **pkey) +{ + if (enif_is_map(env, key)) { +#ifdef HAS_ENGINE_SUPPORT + /* Use key stored in engine */ + ENGINE *e; + char *id = NULL; + char *password; + + if (!get_engine_and_key_id(env, key, &id, &e)) + return PKEY_BADARG; + password = get_key_password(env, key); + *pkey = ENGINE_load_public_key(e, id, NULL, password); + if (password) enif_free(password); + enif_free(id); + if (!pkey) + return PKEY_BADARG; +#else + return PKEY_BADARG; +#endif + } else if (algorithm == atom_rsa) { + RSA *rsa = RSA_new(); + + if (!get_rsa_public_key(env, key, rsa)) { + RSA_free(rsa); + return PKEY_BADARG; + } + + *pkey = EVP_PKEY_new(); + if (!EVP_PKEY_assign_RSA(*pkey, rsa)) { + EVP_PKEY_free(*pkey); + RSA_free(rsa); + return PKEY_BADARG; + } + } else if (algorithm == atom_ecdsa) { +#if defined(HAVE_EC) + EC_KEY *ec = NULL; + const ERL_NIF_TERM *tpl_terms; + int tpl_arity; + + if (enif_get_tuple(env, key, &tpl_arity, &tpl_terms) && tpl_arity == 2 + && enif_is_tuple(env, tpl_terms[0]) && enif_is_binary(env, tpl_terms[1]) + && get_ec_key(env, tpl_terms[0], atom_undefined, tpl_terms[1], &ec)) { + + *pkey = EVP_PKEY_new(); + if (!EVP_PKEY_assign_EC_KEY(*pkey, ec)) { + EVP_PKEY_free(*pkey); + EC_KEY_free(ec); + return PKEY_BADARG; + } + } else { + return PKEY_BADARG; + } +#else + return PKEY_NOTSUP; +#endif + } else if (algorithm == atom_eddsa) { +#if defined(HAVE_EDDSA) + if (!get_eddsa_key(env, 1, key, pkey)) { + return PKEY_BADARG; + } +#else + return PKEY_NOTSUP; +#endif + } else if (algorithm == atom_dss) { + DSA *dsa = DSA_new(); + + if (!get_dss_public_key(env, key, dsa)) { + DSA_free(dsa); + return PKEY_BADARG; + } + + *pkey = EVP_PKEY_new(); + if (!EVP_PKEY_assign_DSA(*pkey, dsa)) { + EVP_PKEY_free(*pkey); + DSA_free(dsa); + return PKEY_BADARG; + } + } else { + return PKEY_BADARG; + } + + return PKEY_OK; +} + +ERL_NIF_TERM pkey_sign_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +{/* (Algorithm, Type, Data|{digest,Digest}, Key|#{}, Options) */ + int i; + const EVP_MD *md = NULL; + unsigned char md_value[EVP_MAX_MD_SIZE]; + EVP_PKEY *pkey; +#ifdef HAS_EVP_PKEY_CTX + EVP_PKEY_CTX *ctx; + size_t siglen; +#else + unsigned len, siglen; +#endif + PKeySignOptions sig_opt; + ErlNifBinary sig_bin; /* signature */ + unsigned char *tbs; /* data to be signed */ + size_t tbslen; +/*char buf[1024]; +enif_get_atom(env,argv[0],buf,1024,ERL_NIF_LATIN1); printf("algo=%s ",buf); +enif_get_atom(env,argv[1],buf,1024,ERL_NIF_LATIN1); printf("hash=%s ",buf); +printf("\r\n"); +*/ + +#ifndef HAS_ENGINE_SUPPORT + if (enif_is_map(env, argv[3])) { + return atom_notsup; + } +#endif + + i = get_pkey_sign_digest(env, argv[0], argv[1], argv[2], md_value, &md, &tbs, &tbslen); + if (i != PKEY_OK) { + if (i == PKEY_NOTSUP) + return atom_notsup; + else + return enif_make_badarg(env); + } + + i = get_pkey_sign_options(env, argv[0], argv[4], md, &sig_opt); + if (i != PKEY_OK) { + if (i == PKEY_NOTSUP) + return atom_notsup; + else + return enif_make_badarg(env); + } + + if (get_pkey_private_key(env, argv[0], argv[3], &pkey) != PKEY_OK) { + return enif_make_badarg(env); + } + +#ifdef HAS_EVP_PKEY_CTX + ctx = EVP_PKEY_CTX_new(pkey, NULL); + if (!ctx) goto badarg; + + if (argv[0] != atom_eddsa) { + if (EVP_PKEY_sign_init(ctx) <= 0) goto badarg; + if (md != NULL && EVP_PKEY_CTX_set_signature_md(ctx, md) <= 0) goto badarg; + } + + if (argv[0] == atom_rsa) { + if (EVP_PKEY_CTX_set_rsa_padding(ctx, sig_opt.rsa_padding) <= 0) goto badarg; +# ifdef HAVE_RSA_PKCS1_PSS_PADDING + if (sig_opt.rsa_padding == RSA_PKCS1_PSS_PADDING) { + if (sig_opt.rsa_mgf1_md != NULL) { +# ifdef HAVE_RSA_MGF1_MD + if (EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, sig_opt.rsa_mgf1_md) <= 0) goto badarg; +# else + EVP_PKEY_CTX_free(ctx); + EVP_PKEY_free(pkey); + return atom_notsup; +# endif + } + if (sig_opt.rsa_pss_saltlen > -2 + && EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, sig_opt.rsa_pss_saltlen) <= 0) + goto badarg; + } +#endif + } + + if (argv[0] == atom_eddsa) { +#ifdef HAVE_EDDSA + EVP_MD_CTX* mdctx = EVP_MD_CTX_new(); + if (!EVP_DigestSignInit(mdctx, NULL, NULL, NULL, pkey)) { + if (mdctx) EVP_MD_CTX_free(mdctx); + goto badarg; + } + + if (!EVP_DigestSign(mdctx, NULL, &siglen, tbs, tbslen)) { + EVP_MD_CTX_free(mdctx); + goto badarg; + } + enif_alloc_binary(siglen, &sig_bin); + + if (!EVP_DigestSign(mdctx, sig_bin.data, &siglen, tbs, tbslen)) { + EVP_MD_CTX_free(mdctx); + goto badarg; + } + EVP_MD_CTX_free(mdctx); +#else + goto badarg; +#endif + } + else + { + if (EVP_PKEY_sign(ctx, NULL, &siglen, tbs, tbslen) <= 0) goto badarg; + enif_alloc_binary(siglen, &sig_bin); + + if (md != NULL) { + ERL_VALGRIND_ASSERT_MEM_DEFINED(tbs, EVP_MD_size(md)); + } + i = EVP_PKEY_sign(ctx, sig_bin.data, &siglen, tbs, tbslen); + } + + EVP_PKEY_CTX_free(ctx); +#else +/*printf("Old interface\r\n"); + */ + if (argv[0] == atom_rsa) { + RSA *rsa = EVP_PKEY_get1_RSA(pkey); + enif_alloc_binary(RSA_size(rsa), &sig_bin); + len = EVP_MD_size(md); + ERL_VALGRIND_ASSERT_MEM_DEFINED(tbs, len); + i = RSA_sign(md->type, tbs, len, sig_bin.data, &siglen, rsa); + RSA_free(rsa); + } else if (argv[0] == atom_dss) { + DSA *dsa = EVP_PKEY_get1_DSA(pkey); + enif_alloc_binary(DSA_size(dsa), &sig_bin); + len = EVP_MD_size(md); + ERL_VALGRIND_ASSERT_MEM_DEFINED(tbs, len); + i = DSA_sign(md->type, tbs, len, sig_bin.data, &siglen, dsa); + DSA_free(dsa); + } else if (argv[0] == atom_ecdsa) { +#if defined(HAVE_EC) + EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pkey); + enif_alloc_binary(ECDSA_size(ec), &sig_bin); + len = EVP_MD_size(md); + ERL_VALGRIND_ASSERT_MEM_DEFINED(tbs, len); + i = ECDSA_sign(md->type, tbs, len, sig_bin.data, &siglen, ec); + EC_KEY_free(ec); +#else + EVP_PKEY_free(pkey); + return atom_notsup; +#endif + } else { + goto badarg; + } +#endif + + EVP_PKEY_free(pkey); + if (i == 1) { + ERL_VALGRIND_MAKE_MEM_DEFINED(sig_bin.data, siglen); + if (siglen != sig_bin.size) { + enif_realloc_binary(&sig_bin, siglen); + ERL_VALGRIND_ASSERT_MEM_DEFINED(sig_bin.data, siglen); + } + return enif_make_binary(env, &sig_bin); + } else { + enif_release_binary(&sig_bin); + return atom_error; + } + + badarg: +#ifdef HAS_EVP_PKEY_CTX + EVP_PKEY_CTX_free(ctx); +#endif + EVP_PKEY_free(pkey); + return enif_make_badarg(env); +} + +ERL_NIF_TERM pkey_verify_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +{/* (Algorithm, Type, Data|{digest,Digest}, Signature, Key, Options) */ + int i; + const EVP_MD *md = NULL; + unsigned char md_value[EVP_MAX_MD_SIZE]; + EVP_PKEY *pkey; +#ifdef HAS_EVP_PKEY_CTX + EVP_PKEY_CTX *ctx; +#else +#endif + PKeySignOptions sig_opt; + ErlNifBinary sig_bin; /* signature */ + unsigned char *tbs; /* data to be signed */ + size_t tbslen; + +#ifndef HAS_ENGINE_SUPPORT + if (enif_is_map(env, argv[4])) { + return atom_notsup; + } +#endif + + if (!enif_inspect_binary(env, argv[3], &sig_bin)) { + return enif_make_badarg(env); + } + + i = get_pkey_sign_digest(env, argv[0], argv[1], argv[2], md_value, &md, &tbs, &tbslen); + if (i != PKEY_OK) { + if (i == PKEY_NOTSUP) + return atom_notsup; + else + return enif_make_badarg(env); + } + + i = get_pkey_sign_options(env, argv[0], argv[5], md, &sig_opt); + if (i != PKEY_OK) { + if (i == PKEY_NOTSUP) + return atom_notsup; + else + return enif_make_badarg(env); + } + + if (get_pkey_public_key(env, argv[0], argv[4], &pkey) != PKEY_OK) { + return enif_make_badarg(env); + } + +#ifdef HAS_EVP_PKEY_CTX +/* printf("EVP interface\r\n"); + */ + ctx = EVP_PKEY_CTX_new(pkey, NULL); + if (!ctx) goto badarg; + + if (argv[0] != atom_eddsa) { + if (EVP_PKEY_verify_init(ctx) <= 0) goto badarg; + if (md != NULL && EVP_PKEY_CTX_set_signature_md(ctx, md) <= 0) goto badarg; + } + + if (argv[0] == atom_rsa) { + if (EVP_PKEY_CTX_set_rsa_padding(ctx, sig_opt.rsa_padding) <= 0) goto badarg; + if (sig_opt.rsa_padding == RSA_PKCS1_PSS_PADDING) { + if (sig_opt.rsa_mgf1_md != NULL) { +# ifdef HAVE_RSA_MGF1_MD + if (EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, sig_opt.rsa_mgf1_md) <= 0) goto badarg; +# else + EVP_PKEY_CTX_free(ctx); + EVP_PKEY_free(pkey); + return atom_notsup; +# endif + } + if (sig_opt.rsa_pss_saltlen > -2 + && EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, sig_opt.rsa_pss_saltlen) <= 0) + goto badarg; + } + } + + if (argv[0] == atom_eddsa) { +#ifdef HAVE_EDDSA + EVP_MD_CTX* mdctx = EVP_MD_CTX_create(); + + if (!EVP_DigestVerifyInit(mdctx, NULL, NULL, NULL, pkey)) { + if (mdctx) EVP_MD_CTX_destroy(mdctx); + goto badarg; + } + + i = EVP_DigestVerify(mdctx, sig_bin.data, sig_bin.size, tbs, tbslen); + EVP_MD_CTX_destroy(mdctx); +#else + goto badarg; +#endif + } + else + { + if (md != NULL) { + ERL_VALGRIND_ASSERT_MEM_DEFINED(tbs, EVP_MD_size(md)); + } + i = EVP_PKEY_verify(ctx, sig_bin.data, sig_bin.size, tbs, tbslen); + } + + EVP_PKEY_CTX_free(ctx); +#else +/*printf("Old interface\r\n"); +*/ + if (argv[0] == atom_rsa) { + RSA *rsa = EVP_PKEY_get1_RSA(pkey); + i = RSA_verify(md->type, tbs, tbslen, sig_bin.data, sig_bin.size, rsa); + RSA_free(rsa); + } else if (argv[0] == atom_dss) { + DSA *dsa = EVP_PKEY_get1_DSA(pkey); + i = DSA_verify(0, tbs, tbslen, sig_bin.data, sig_bin.size, dsa); + DSA_free(dsa); + } else if (argv[0] == atom_ecdsa) { +#if defined(HAVE_EC) + EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pkey); + i = ECDSA_verify(EVP_MD_type(md), tbs, tbslen, sig_bin.data, sig_bin.size, ec); + EC_KEY_free(ec); +#else + EVP_PKEY_free(pkey); + return atom_notsup; +#endif + } else { + goto badarg; + } +#endif + + EVP_PKEY_free(pkey); + if (i == 1) { + return atom_true; + } else { + return atom_false; + } + + badarg: +#ifdef HAS_EVP_PKEY_CTX + EVP_PKEY_CTX_free(ctx); +#endif + EVP_PKEY_free(pkey); + return enif_make_badarg(env); +} + +static int get_pkey_crypt_options(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM options, + PKeyCryptOptions *opt) +{ + ERL_NIF_TERM head, tail; + const ERL_NIF_TERM *tpl_terms; + int tpl_arity; + const EVP_MD *opt_md; + int i; + + if (!enif_is_list(env, options)) { + return PKEY_BADARG; + } + + /* defaults */ + if (algorithm == atom_rsa) { + opt->rsa_mgf1_md = NULL; + opt->rsa_oaep_label.data = NULL; + opt->rsa_oaep_label.size = 0; + opt->rsa_oaep_md = NULL; + opt->rsa_padding = RSA_PKCS1_PADDING; + opt->signature_md = NULL; + } + + if (enif_is_empty_list(env, options)) { + return PKEY_OK; + } + + if (algorithm == atom_rsa) { + tail = options; + while (enif_get_list_cell(env, tail, &head, &tail)) { + if (enif_get_tuple(env, head, &tpl_arity, &tpl_terms) && tpl_arity == 2) { + if (tpl_terms[0] == atom_rsa_padding + || tpl_terms[0] == atom_rsa_pad /* Compatibility */ + ) { + if (tpl_terms[1] == atom_rsa_pkcs1_padding) { + opt->rsa_padding = RSA_PKCS1_PADDING; +#ifdef HAVE_RSA_OAEP_PADDING + } else if (tpl_terms[1] == atom_rsa_pkcs1_oaep_padding) { + opt->rsa_padding = RSA_PKCS1_OAEP_PADDING; +#endif +#ifdef HAVE_RSA_SSLV23_PADDING + } else if (tpl_terms[1] == atom_rsa_sslv23_padding) { + opt->rsa_padding = RSA_SSLV23_PADDING; +#endif + } else if (tpl_terms[1] == atom_rsa_x931_padding) { + opt->rsa_padding = RSA_X931_PADDING; + } else if (tpl_terms[1] == atom_rsa_no_padding) { + opt->rsa_padding = RSA_NO_PADDING; + } else { + return PKEY_BADARG; + } + } else if (tpl_terms[0] == atom_signature_md && enif_is_atom(env, tpl_terms[1])) { + i = get_pkey_digest_type(env, algorithm, tpl_terms[1], &opt_md); + if (i != PKEY_OK) { + return i; + } + opt->signature_md = opt_md; + } else if (tpl_terms[0] == atom_rsa_mgf1_md && enif_is_atom(env, tpl_terms[1])) { +#ifndef HAVE_RSA_MGF1_MD + if (tpl_terms[1] != atom_sha) + return PKEY_NOTSUP; +#endif + i = get_pkey_digest_type(env, algorithm, tpl_terms[1], &opt_md); + if (i != PKEY_OK) { + return i; + } + opt->rsa_mgf1_md = opt_md; + } else if (tpl_terms[0] == atom_rsa_oaep_label + && enif_inspect_binary(env, tpl_terms[1], &(opt->rsa_oaep_label))) { +#ifdef HAVE_RSA_OAEP_MD + continue; +#else + return PKEY_NOTSUP; +#endif + } else if (tpl_terms[0] == atom_rsa_oaep_md && enif_is_atom(env, tpl_terms[1])) { +#ifndef HAVE_RSA_OAEP_MD + if (tpl_terms[1] != atom_sha) + return PKEY_NOTSUP; +#endif + i = get_pkey_digest_type(env, algorithm, tpl_terms[1], &opt_md); + if (i != PKEY_OK) { + return i; + } + opt->rsa_oaep_md = opt_md; + } else { + return PKEY_BADARG; + } + } else { + return PKEY_BADARG; + } + } + } else { + return PKEY_BADARG; + } + + return PKEY_OK; +} + +static size_t size_of_RSA(EVP_PKEY *pkey) { + size_t tmplen; + RSA *rsa = EVP_PKEY_get1_RSA(pkey); + if (rsa == NULL) return 0; + tmplen = RSA_size(rsa); + RSA_free(rsa); + return tmplen; +} + +ERL_NIF_TERM pkey_crypt_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +{/* (Algorithm, Data, PublKey=[E,N]|[E,N,D]|[E,N,D,P1,P2,E1,E2,C], Options, IsPrivate, IsEncrypt) */ + int i; + EVP_PKEY *pkey; +#ifdef HAS_EVP_PKEY_CTX + EVP_PKEY_CTX *ctx; +#else + RSA *rsa; +#endif + PKeyCryptOptions crypt_opt; + ErlNifBinary in_bin, out_bin, tmp_bin; + size_t outlen; +#ifdef HAVE_RSA_SSLV23_PADDING + size_t tmplen; +#endif + int is_private = (argv[4] == atom_true), + is_encrypt = (argv[5] == atom_true); + int algo_init = 0; + +/* char algo[1024]; */ + +#ifndef HAS_ENGINE_SUPPORT + if (enif_is_map(env, argv[2])) { + return atom_notsup; + } +#endif + + if (!enif_inspect_binary(env, argv[1], &in_bin)) { + return enif_make_badarg(env); + } + + i = get_pkey_crypt_options(env, argv[0], argv[3], &crypt_opt); + if (i != PKEY_OK) { + if (i == PKEY_NOTSUP) + return atom_notsup; + else + return enif_make_badarg(env); + } + + if (is_private) { + if (get_pkey_private_key(env, argv[0], argv[2], &pkey) != PKEY_OK) { + return enif_make_badarg(env); + } + } else { + if (get_pkey_public_key(env, argv[0], argv[2], &pkey) != PKEY_OK) { + return enif_make_badarg(env); + } + } + + out_bin.data = NULL; + out_bin.size = 0; + tmp_bin.data = NULL; + tmp_bin.size = 0; + +#ifdef HAS_EVP_PKEY_CTX + ctx = EVP_PKEY_CTX_new(pkey, NULL); + if (!ctx) goto badarg; + +/* enif_get_atom(env,argv[0],algo,1024,ERL_NIF_LATIN1); */ + + if (is_private) { + if (is_encrypt) { + /* private encrypt */ + if ((algo_init=EVP_PKEY_sign_init(ctx)) <= 0) { + /* fprintf(stderr,"BADARG %s private encrypt algo_init=%d %s:%d\r\n", algo, algo_init, __FILE__, __LINE__); */ + goto badarg; + } + } else { + /* private decrypt */ + if ((algo_init=EVP_PKEY_decrypt_init(ctx)) <= 0) { + /* fprintf(stderr,"BADARG %s private decrypt algo_init=%d %s:%d\r\n", algo, algo_init, __FILE__, __LINE__); */ + goto badarg; + } + } + } else { + if (is_encrypt) { + /* public encrypt */ + if ((algo_init=EVP_PKEY_encrypt_init(ctx)) <= 0) { + /* fprintf(stderr,"BADARG %s public encrypt algo_init=%d %s:%d\r\n", algo,algo_init,__FILE__, __LINE__); */ + goto badarg; + } + } else { + /* public decrypt */ + if ((algo_init=EVP_PKEY_verify_recover_init(ctx)) <= 0) { + /* fprintf(stderr,"BADARG %s public decrypt algo_init=%d %s:%d\r\n", algo,algo_init,__FILE__, __LINE__); */ + goto badarg; + } + } + } + + if (argv[0] == atom_rsa) { + if (crypt_opt.signature_md != NULL + && EVP_PKEY_CTX_set_signature_md(ctx, crypt_opt.signature_md) <= 0) + goto badarg; +#ifdef HAVE_RSA_SSLV23_PADDING + if (crypt_opt.rsa_padding == RSA_SSLV23_PADDING) { + if (is_encrypt) { + tmplen = size_of_RSA(pkey); + if (tmplen == 0) goto badarg; + if (!enif_alloc_binary(tmplen, &tmp_bin)) goto badarg; + if (RSA_padding_add_SSLv23(tmp_bin.data, tmplen, in_bin.data, in_bin.size) <= 0) + goto badarg; + in_bin = tmp_bin; + } + if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_NO_PADDING) <= 0) goto badarg; + } else +#endif + { + if (EVP_PKEY_CTX_set_rsa_padding(ctx, crypt_opt.rsa_padding) <= 0) goto badarg; + } +#ifdef HAVE_RSA_OAEP_MD + if (crypt_opt.rsa_padding == RSA_PKCS1_OAEP_PADDING) { + if (crypt_opt.rsa_oaep_md != NULL + && EVP_PKEY_CTX_set_rsa_oaep_md(ctx, crypt_opt.rsa_oaep_md) <= 0) + goto badarg; + if (crypt_opt.rsa_mgf1_md != NULL + && EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, crypt_opt.rsa_mgf1_md) <= 0) goto badarg; + if (crypt_opt.rsa_oaep_label.data != NULL && crypt_opt.rsa_oaep_label.size > 0) { + unsigned char *label_copy = NULL; + label_copy = OPENSSL_malloc(crypt_opt.rsa_oaep_label.size); + if (label_copy == NULL) goto badarg; + memcpy((void *)(label_copy), (const void *)(crypt_opt.rsa_oaep_label.data), + crypt_opt.rsa_oaep_label.size); + if (EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, label_copy, + crypt_opt.rsa_oaep_label.size) <= 0) { + OPENSSL_free(label_copy); + label_copy = NULL; + goto badarg; + } + } + } +#endif + } + + if (is_private) { + if (is_encrypt) { + /* private_encrypt */ + i = EVP_PKEY_sign(ctx, NULL, &outlen, in_bin.data, in_bin.size); + } else { + /* private_decrypt */ + i = EVP_PKEY_decrypt(ctx, NULL, &outlen, in_bin.data, in_bin.size); + } + } else { + if (is_encrypt) { + /* public_encrypt */ + i = EVP_PKEY_encrypt(ctx, NULL, &outlen, in_bin.data, in_bin.size); + } else { + /* public_decrypt */ + i = EVP_PKEY_verify_recover(ctx, NULL, &outlen, in_bin.data, in_bin.size); + } + } + /* fprintf(stderr,"i = %d %s:%d\r\n", i, __FILE__, __LINE__); */ + + if (i != 1) goto badarg; + + enif_alloc_binary(outlen, &out_bin); + + if (is_private) { + if (is_encrypt) { + /* private_encrypt */ + i = EVP_PKEY_sign(ctx, out_bin.data, &outlen, in_bin.data, in_bin.size); + } else { + /* private_decrypt */ + i = EVP_PKEY_decrypt(ctx, out_bin.data, &outlen, in_bin.data, in_bin.size); + } + } else { + if (is_encrypt) { + /* public_encrypt */ + i = EVP_PKEY_encrypt(ctx, out_bin.data, &outlen, in_bin.data, in_bin.size); + } else { + /* public_decrypt */ + i = EVP_PKEY_verify_recover(ctx, out_bin.data, &outlen, in_bin.data, in_bin.size); + } + } + +#else + /* Non-EVP cryptolib. Only support RSA */ + + if (argv[0] != atom_rsa) { + algo_init = -2; /* exitcode: notsup */ + goto badarg; + } + rsa = EVP_PKEY_get1_RSA(pkey); + enif_alloc_binary(RSA_size(rsa), &out_bin); + + if (is_private) { + if (is_encrypt) { + /* non-evp rsa private encrypt */ + ERL_VALGRIND_ASSERT_MEM_DEFINED(in_bin.data,in_bin.size); + i = RSA_private_encrypt(in_bin.size, in_bin.data, + out_bin.data, rsa, crypt_opt.rsa_padding); + if (i > 0) { + ERL_VALGRIND_MAKE_MEM_DEFINED(out_bin.data, i); + } + } else { + /* non-evp rsa private decrypt */ + i = RSA_private_decrypt(in_bin.size, in_bin.data, + out_bin.data, rsa, crypt_opt.rsa_padding); + if (i > 0) { + ERL_VALGRIND_MAKE_MEM_DEFINED(out_bin.data, i); + enif_realloc_binary(&out_bin, i); + } + } + } else { + if (is_encrypt) { + /* non-evp rsa public encrypt */ + ERL_VALGRIND_ASSERT_MEM_DEFINED(in_bin.data,in_bin.size); + i = RSA_public_encrypt(in_bin.size, in_bin.data, + out_bin.data, rsa, crypt_opt.rsa_padding); + if (i > 0) { + ERL_VALGRIND_MAKE_MEM_DEFINED(out_bin.data, i); + } + } else { + /* non-evp rsa public decrypt */ + i = RSA_public_decrypt(in_bin.size, in_bin.data, + out_bin.data, rsa, crypt_opt.rsa_padding); + if (i > 0) { + ERL_VALGRIND_MAKE_MEM_DEFINED(out_bin.data, i); + enif_realloc_binary(&out_bin, i); + } + } + } + + outlen = i; + RSA_free(rsa); +#endif + + if ((i > 0) && argv[0] == atom_rsa && !is_encrypt) { +#ifdef HAVE_RSA_SSLV23_PADDING + if (crypt_opt.rsa_padding == RSA_SSLV23_PADDING) { + unsigned char *p; + tmplen = size_of_RSA(pkey); + if (tmplen == 0) goto badarg; + if (!enif_alloc_binary(tmplen, &tmp_bin)) + goto badarg; + p = out_bin.data; + p++; + i = RSA_padding_check_SSLv23(tmp_bin.data, tmplen, p, out_bin.size - 1, tmplen); + if (i >= 0) { + outlen = i; + in_bin = out_bin; + out_bin = tmp_bin; + tmp_bin = in_bin; + i = 1; + } + } +#endif + } + + if (tmp_bin.data != NULL) { + enif_release_binary(&tmp_bin); + } + +#ifdef HAS_EVP_PKEY_CTX + EVP_PKEY_CTX_free(ctx); +#else +#endif + EVP_PKEY_free(pkey); + if (i > 0) { + ERL_VALGRIND_MAKE_MEM_DEFINED(out_bin.data, outlen); + if (outlen != out_bin.size) { + enif_realloc_binary(&out_bin, outlen); + ERL_VALGRIND_ASSERT_MEM_DEFINED(out_bin.data, outlen); + } + return enif_make_binary(env, &out_bin); + } else { + enif_release_binary(&out_bin); + return atom_error; + } + + badarg: + if (out_bin.data != NULL) { + enif_release_binary(&out_bin); + } + if (tmp_bin.data != NULL) { + enif_release_binary(&tmp_bin); + } +#ifdef HAS_EVP_PKEY_CTX + EVP_PKEY_CTX_free(ctx); +#else +#endif + EVP_PKEY_free(pkey); + if (algo_init == -2) + return atom_notsup; + else + return enif_make_badarg(env); +} + +ERL_NIF_TERM privkey_to_pubkey_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ /* (Algorithm, PrivKey | KeyMap) */ + EVP_PKEY *pkey; + ERL_NIF_TERM alg = argv[0]; + ERL_NIF_TERM result[8]; + if (get_pkey_private_key(env, alg, argv[1], &pkey) != PKEY_OK) { + return enif_make_badarg(env); + } + + if (alg == atom_rsa) { + const BIGNUM *n = NULL, *e = NULL, *d = NULL; + RSA *rsa = EVP_PKEY_get1_RSA(pkey); + if (rsa) { + RSA_get0_key(rsa, &n, &e, &d); + result[0] = bin_from_bn(env, e); // Exponent E + result[1] = bin_from_bn(env, n); // Modulus N = p*q + RSA_free(rsa); + EVP_PKEY_free(pkey); + return enif_make_list_from_array(env, result, 2); + } + + } else if (argv[0] == atom_dss) { + const BIGNUM *p = NULL, *q = NULL, *g = NULL, *pub_key = NULL; + DSA *dsa = EVP_PKEY_get1_DSA(pkey); + if (dsa) { + DSA_get0_pqg(dsa, &p, &q, &g); + DSA_get0_key(dsa, &pub_key, NULL); + result[0] = bin_from_bn(env, p); + result[1] = bin_from_bn(env, q); + result[2] = bin_from_bn(env, g); + result[3] = bin_from_bn(env, pub_key); + DSA_free(dsa); + EVP_PKEY_free(pkey); + return enif_make_list_from_array(env, result, 4); + } + + } else if (argv[0] == atom_ecdsa) { +#if defined(HAVE_EC) + /* not yet implemented + EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pkey); + if (ec) { + / * Example of result: + { + Curve = {Field, Prime, Point, Order, CoFactor} = + { + Field = {prime_field,<<255,...,255>>}, + Prime = {<<255,...,252>>, + <<90,...,75>>, + <<196,...,144>> + }, + Point = <<4,...,245>>, + Order = <<255,...,81>>, + CoFactor = <<1>> + }, + Key = <<151,...,62>> + } + or + { + Curve = + {characteristic_two_field, + M, + Basis = {tpbasis, _} + | {ppbasis, k1, k2, k3} + }, + Key + } + * / + EVP_PKEY_free(pkey); + return enif_make_list_from_array(env, ..., ...); + */ +#endif + } + + if (pkey) EVP_PKEY_free(pkey); + return enif_make_badarg(env); +} diff --git a/lib/crypto/c_src/pkey.h b/lib/crypto/c_src/pkey.h new file mode 100644 index 0000000000..f647a4a160 --- /dev/null +++ b/lib/crypto/c_src/pkey.h @@ -0,0 +1,31 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_PKEY_H__ +#define E_PKEY_H__ 1 + +#include "common.h" + +ERL_NIF_TERM pkey_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM pkey_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM pkey_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM privkey_to_pubkey_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_PKEY_H__ */ diff --git a/lib/crypto/c_src/poly1305.c b/lib/crypto/c_src/poly1305.c new file mode 100644 index 0000000000..3e2bcfa60e --- /dev/null +++ b/lib/crypto/c_src/poly1305.c @@ -0,0 +1,78 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "poly1305.h" + +/* For OpenSSL >= 1.1.1 the hmac_nif and cmac_nif could be integrated into poly1305 (with 'type' as parameter) */ +ERL_NIF_TERM poly1305_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, Text) */ +#ifdef HAVE_POLY1305 + ErlNifBinary key_bin, text, ret_bin; + ERL_NIF_TERM ret = atom_error; + EVP_PKEY *key = NULL; + EVP_MD_CTX *mctx = NULL; + EVP_PKEY_CTX *pctx = NULL; + const EVP_MD *md = NULL; + size_t size; + int type; + + type = EVP_PKEY_POLY1305; + + if (!enif_inspect_binary(env, argv[0], &key_bin) || + !(key_bin.size == 32) ) { + return enif_make_badarg(env); + } + + if (!enif_inspect_binary(env, argv[1], &text) ) { + return enif_make_badarg(env); + } + + key = EVP_PKEY_new_raw_private_key(type, /*engine*/ NULL, key_bin.data, key_bin.size); + + if (!key || + !(mctx = EVP_MD_CTX_new()) || + !EVP_DigestSignInit(mctx, &pctx, md, /*engine*/ NULL, key) || + !EVP_DigestSignUpdate(mctx, text.data, text.size)) { + goto err; + } + + if (!EVP_DigestSignFinal(mctx, NULL, &size) || + !enif_alloc_binary(size, &ret_bin) || + !EVP_DigestSignFinal(mctx, ret_bin.data, &size)) { + goto err; + } + + if ((size != ret_bin.size) && + !enif_realloc_binary(&ret_bin, size)) { + goto err; + } + + ret = enif_make_binary(env, &ret_bin); + + err: + EVP_MD_CTX_free(mctx); + EVP_PKEY_free(key); + return ret; + +#else + return atom_notsup; +#endif +} + diff --git a/lib/crypto/c_src/poly1305.h b/lib/crypto/c_src/poly1305.h new file mode 100644 index 0000000000..4bf45e6218 --- /dev/null +++ b/lib/crypto/c_src/poly1305.h @@ -0,0 +1,28 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_POLY1305_H__ +#define E_POLY1305_H__ 1 + +#include "common.h" + +ERL_NIF_TERM poly1305_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_POLY1305_H__ */ diff --git a/lib/crypto/c_src/rand.c b/lib/crypto/c_src/rand.c new file mode 100644 index 0000000000..e71e202f36 --- /dev/null +++ b/lib/crypto/c_src/rand.c @@ -0,0 +1,99 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "rand.h" +#include "bn.h" + +ERL_NIF_TERM strong_rand_bytes_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Bytes) */ + unsigned bytes; + unsigned char* data; + ERL_NIF_TERM ret; + + if (!enif_get_uint(env, argv[0], &bytes)) { + return enif_make_badarg(env); + } + data = enif_make_new_binary(env, bytes, &ret); + if ( RAND_bytes(data, bytes) != 1) { + return atom_false; + } + ERL_VALGRIND_MAKE_MEM_DEFINED(data, bytes); + return ret; +} + +ERL_NIF_TERM strong_rand_range_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Range) */ + BIGNUM *bn_range, *bn_rand; + ERL_NIF_TERM ret; + + if(!get_bn_from_bin(env, argv[0], &bn_range)) { + return enif_make_badarg(env); + } + + bn_rand = BN_new(); + if (BN_rand_range(bn_rand, bn_range) != 1) { + ret = atom_false; + } + else { + ret = bin_from_bn(env, bn_rand); + } + BN_free(bn_rand); + BN_free(bn_range); + return ret; +} + +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; + unsigned char* data; + unsigned dlen; + ERL_NIF_TERM ret; + + if (!get_bn_from_mpint(env, argv[0], &bn_from) + || !get_bn_from_mpint(env, argv[1], &bn_rand)) { + if (bn_from) BN_free(bn_from); + return enif_make_badarg(env); + } + + bn_to = BN_new(); + BN_sub(bn_to, bn_rand, bn_from); + BN_pseudo_rand_range(bn_rand, bn_to); + BN_add(bn_rand, bn_rand, bn_from); + dlen = BN_num_bytes(bn_rand); + data = enif_make_new_binary(env, dlen+4, &ret); + put_int32(data, dlen); + BN_bn2bin(bn_rand, data+4); + ERL_VALGRIND_MAKE_MEM_DEFINED(data+4, dlen); + BN_free(bn_rand); + BN_free(bn_from); + BN_free(bn_to); + return ret; +} + +ERL_NIF_TERM rand_seed_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ErlNifBinary seed_bin; + + if (!enif_inspect_binary(env, argv[0], &seed_bin)) + return enif_make_badarg(env); + RAND_seed(seed_bin.data,seed_bin.size); + return atom_ok; +} + diff --git a/lib/crypto/c_src/rand.h b/lib/crypto/c_src/rand.h new file mode 100644 index 0000000000..9c23d343ec --- /dev/null +++ b/lib/crypto/c_src/rand.h @@ -0,0 +1,31 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_RAND_H__ +#define E_RAND_H__ 1 + +#include "common.h" + +ERL_NIF_TERM strong_rand_bytes_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM strong_rand_range_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM rand_uniform_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM rand_seed_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_RAND_H__ */ diff --git a/lib/crypto/c_src/rc4.c b/lib/crypto/c_src/rc4.c new file mode 100644 index 0000000000..483c87b04b --- /dev/null +++ b/lib/crypto/c_src/rc4.c @@ -0,0 +1,66 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "rc4.h" + +ERL_NIF_TERM rc4_set_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key) */ +#ifndef OPENSSL_NO_RC4 + ErlNifBinary key; + ERL_NIF_TERM ret; + + CHECK_NO_FIPS_MODE(); + + if (!enif_inspect_iolist_as_binary(env,argv[0], &key)) { + return enif_make_badarg(env); + } + RC4_set_key((RC4_KEY*)enif_make_new_binary(env, sizeof(RC4_KEY), &ret), + key.size, key.data); + return ret; +#else + return enif_raise_exception(env, atom_notsup); +#endif +} + +ERL_NIF_TERM rc4_encrypt_with_state(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (State, Data) */ +#ifndef OPENSSL_NO_RC4 + ErlNifBinary state, data; + RC4_KEY* rc4_key; + ERL_NIF_TERM new_state, new_data; + + CHECK_NO_FIPS_MODE(); + + if (!enif_inspect_iolist_as_binary(env,argv[0], &state) + || state.size != sizeof(RC4_KEY) + || !enif_inspect_iolist_as_binary(env,argv[1], &data)) { + return enif_make_badarg(env); + } + rc4_key = (RC4_KEY*)enif_make_new_binary(env, sizeof(RC4_KEY), &new_state); + memcpy(rc4_key, state.data, sizeof(RC4_KEY)); + RC4(rc4_key, data.size, data.data, + enif_make_new_binary(env, data.size, &new_data)); + CONSUME_REDS(env,data); + return enif_make_tuple2(env,new_state,new_data); +#else + return enif_raise_exception(env, atom_notsup); +#endif +} + diff --git a/lib/crypto/c_src/rc4.h b/lib/crypto/c_src/rc4.h new file mode 100644 index 0000000000..28bf674253 --- /dev/null +++ b/lib/crypto/c_src/rc4.h @@ -0,0 +1,29 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_RC4_H__ +#define E_RC4_H__ 1 + +#include "common.h" + +ERL_NIF_TERM rc4_set_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM rc4_encrypt_with_state(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_RC4_H__ */ diff --git a/lib/crypto/c_src/rsa.c b/lib/crypto/c_src/rsa.c new file mode 100644 index 0000000000..92867671fb --- /dev/null +++ b/lib/crypto/c_src/rsa.c @@ -0,0 +1,195 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "rsa.h" +#include "bn.h" + +static ERL_NIF_TERM rsa_generate_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM put_rsa_private_key(ErlNifEnv* env, const RSA *rsa); +static int check_erlang_interrupt(int maj, int min, BN_GENCB *ctxt); + +int get_rsa_private_key(ErlNifEnv* env, ERL_NIF_TERM key, RSA *rsa) +{ + /* key=[E,N,D]|[E,N,D,P1,P2,E1,E2,C] */ + ERL_NIF_TERM head, tail; + BIGNUM *e, *n, *d; + BIGNUM *p, *q; + BIGNUM *dmp1, *dmq1, *iqmp; + + if (!enif_get_list_cell(env, key, &head, &tail) + || !get_bn_from_bin(env, head, &e) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_bin(env, head, &n) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_bin(env, head, &d)) { + return 0; + } + (void) RSA_set0_key(rsa, n, e, d); + if (enif_is_empty_list(env, tail)) { + return 1; + } + if (!enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_bin(env, head, &p) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_bin(env, head, &q) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_bin(env, head, &dmp1) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_bin(env, head, &dmq1) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_bin(env, head, &iqmp) + || !enif_is_empty_list(env, tail)) { + return 0; + } + (void) RSA_set0_factors(rsa, p, q); + (void) RSA_set0_crt_params(rsa, dmp1, dmq1, iqmp); + return 1; +} + +int get_rsa_public_key(ErlNifEnv* env, ERL_NIF_TERM key, RSA *rsa) +{ + /* key=[E,N] */ + ERL_NIF_TERM head, tail; + BIGNUM *e, *n; + + if (!enif_get_list_cell(env, key, &head, &tail) + || !get_bn_from_bin(env, head, &e) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_bin(env, head, &n) + || !enif_is_empty_list(env, tail)) { + return 0; + } + + (void) RSA_set0_key(rsa, n, e, NULL); + return 1; +} + +/* Creates a term which can be parsed by get_rsa_private_key(). This is a list of plain integer binaries (not mpints). */ +static ERL_NIF_TERM put_rsa_private_key(ErlNifEnv* env, const RSA *rsa) +{ + ERL_NIF_TERM result[8]; + const BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp; + + /* Return at least [E,N,D] */ + n = NULL; e = NULL; d = NULL; + RSA_get0_key(rsa, &n, &e, &d); + + result[0] = bin_from_bn(env, e); // Exponent E + result[1] = bin_from_bn(env, n); // Modulus N = p*q + result[2] = bin_from_bn(env, d); // Exponent D + + /* Check whether the optional additional parameters are available */ + p = NULL; q = NULL; + RSA_get0_factors(rsa, &p, &q); + dmp1 = NULL; dmq1 = NULL; iqmp = NULL; + RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp); + + if (p && q && dmp1 && dmq1 && iqmp) { + result[3] = bin_from_bn(env, p); // Factor p + result[4] = bin_from_bn(env, q); // Factor q + result[5] = bin_from_bn(env, dmp1); // D mod (p-1) + result[6] = bin_from_bn(env, dmq1); // D mod (q-1) + result[7] = bin_from_bn(env, iqmp); // (1/q) mod p + + return enif_make_list_from_array(env, result, 8); + } else { + return enif_make_list_from_array(env, result, 3); + } +} + +static int check_erlang_interrupt(int maj, int min, BN_GENCB *ctxt) +{ + ErlNifEnv *env = BN_GENCB_get_arg(ctxt); + + if (!enif_is_current_process_alive(env)) { + return 0; + } else { + return 1; + } +} + +static ERL_NIF_TERM rsa_generate_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (ModulusSize, PublicExponent) */ + int modulus_bits; + BIGNUM *pub_exp, *three; + RSA *rsa; + int success; + ERL_NIF_TERM result; + BN_GENCB *intr_cb; +#ifndef HAVE_OPAQUE_BN_GENCB + BN_GENCB intr_cb_buf; +#endif + + if (!enif_get_int(env, argv[0], &modulus_bits) || modulus_bits < 256) { + return enif_make_badarg(env); + } + + if (!get_bn_from_bin(env, argv[1], &pub_exp)) { + return enif_make_badarg(env); + } + + /* Make sure the public exponent is large enough (at least 3). + * Without this, RSA_generate_key_ex() can run forever. */ + three = BN_new(); + BN_set_word(three, 3); + success = BN_cmp(pub_exp, three); + BN_free(three); + if (success < 0) { + BN_free(pub_exp); + return enif_make_badarg(env); + } + + /* For large keys, prime generation can take many seconds. Set up + * the callback which we use to test whether the process has been + * interrupted. */ +#ifdef HAVE_OPAQUE_BN_GENCB + intr_cb = BN_GENCB_new(); +#else + intr_cb = &intr_cb_buf; +#endif + BN_GENCB_set(intr_cb, check_erlang_interrupt, env); + + rsa = RSA_new(); + success = RSA_generate_key_ex(rsa, modulus_bits, pub_exp, intr_cb); + BN_free(pub_exp); + +#ifdef HAVE_OPAQUE_BN_GENCB + BN_GENCB_free(intr_cb); +#endif + + if (!success) { + RSA_free(rsa); + return atom_error; + } + + result = put_rsa_private_key(env, rsa); + RSA_free(rsa); + + return result; +} + +ERL_NIF_TERM rsa_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + /* RSA key generation can take a long time (>1 sec for a large + * modulus), so schedule it as a CPU-bound operation. */ + return enif_schedule_nif(env, "rsa_generate_key", + ERL_NIF_DIRTY_JOB_CPU_BOUND, + rsa_generate_key, argc, argv); +} diff --git a/lib/crypto/c_src/rsa.h b/lib/crypto/c_src/rsa.h new file mode 100644 index 0000000000..69c02aa2cb --- /dev/null +++ b/lib/crypto/c_src/rsa.h @@ -0,0 +1,31 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_RSA_H__ +#define E_RSA_H__ 1 + +#include "common.h" + +int get_rsa_public_key(ErlNifEnv* env, ERL_NIF_TERM key, RSA *rsa); +int get_rsa_private_key(ErlNifEnv* env, ERL_NIF_TERM key, RSA *rsa); + +ERL_NIF_TERM rsa_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_RSA_H__ */ diff --git a/lib/crypto/c_src/srp.c b/lib/crypto/c_src/srp.c new file mode 100644 index 0000000000..1552bc8cc1 --- /dev/null +++ b/lib/crypto/c_src/srp.c @@ -0,0 +1,229 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "srp.h" +#include "bn.h" + +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 = NULL, *bn_generator = NULL, *bn_prime = NULL, *bn_multiplier = NULL, *bn_result; + BN_CTX *bn_ctx; + unsigned char* ptr; + unsigned dlen; + ERL_NIF_TERM ret; + + CHECK_NO_FIPS_MODE(); + + 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_generator) BN_free(bn_generator); + if (bn_exponent) BN_free(bn_exponent); + if (bn_prime) 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; +} + +ERL_NIF_TERM srp_user_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 = NULL, *bn_multiplier = NULL, *bn_exp2, + *bn_base, *bn_prime = NULL, *bn_generator = NULL, + *bn_B = NULL, *bn_result; + BN_CTX *bn_ctx; + unsigned char* ptr; + unsigned dlen; + ERL_NIF_TERM ret; + + CHECK_NO_FIPS_MODE(); + + 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_mul(bn_result, bn_u, bn_exponent, bn_ctx); + BN_add(bn_exp2, bn_a, bn_result); + + /* (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; +} + +ERL_NIF_TERM srp_host_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 = NULL, *bn_A = NULL, *bn_u = NULL, *bn_base, *bn_result; + BN_CTX *bn_ctx; + unsigned char* ptr; + unsigned dlen; + ERL_NIF_TERM ret; + + CHECK_NO_FIPS_MODE(); + + 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; +} + diff --git a/lib/crypto/c_src/srp.h b/lib/crypto/c_src/srp.h new file mode 100644 index 0000000000..c356690470 --- /dev/null +++ b/lib/crypto/c_src/srp.h @@ -0,0 +1,30 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_SRP_H__ +#define E_SRP_H__ 1 + +#include "common.h" + +ERL_NIF_TERM srp_value_B_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM srp_user_secret_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM srp_host_secret_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_SRP_H__ */ diff --git a/lib/crypto/test/Makefile b/lib/crypto/test/Makefile index e046a25338..8b320e01a9 100644 --- a/lib/crypto/test/Makefile +++ b/lib/crypto/test/Makefile @@ -6,6 +6,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk # ---------------------------------------------------- MODULES = \ + crypto_bench_SUITE \ blowfish_SUITE \ crypto_SUITE \ engine_SUITE @@ -77,7 +78,7 @@ release_spec: release_tests_spec: $(TEST_TARGET) $(INSTALL_DIR) "$(RELSYSDIR)" - $(INSTALL_DATA) crypto.spec crypto.cover $(RELTEST_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) crypto.spec crypto_bench.spec crypto.cover $(RELTEST_FILES) "$(RELSYSDIR)" @tar cfh - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) chmod -R u+w "$(RELSYSDIR)" diff --git a/lib/crypto/test/crypto.spec b/lib/crypto/test/crypto.spec index cc09970cb3..4a95275687 100644 --- a/lib/crypto/test/crypto.spec +++ b/lib/crypto/test/crypto.spec @@ -1 +1,6 @@ {suites,"../crypto_test",all}. + +{skip_suites, "../crypto_test", [crypto_bench_SUITE + ], + "Benchmarks run separately"}. + diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl index 82d098ebd1..003e0c58b1 100644 --- a/lib/crypto/test/crypto_SUITE.erl +++ b/lib/crypto/test/crypto_SUITE.erl @@ -156,7 +156,7 @@ groups() -> ]}, {dh, [], [generate_compute, compute_bug]}, - {ecdh, [], [generate_all_supported, compute, generate]}, + {ecdh, [], [use_all_elliptic_curves, compute, generate]}, {srp, [], [generate_compute]}, {des_cbc, [], [block]}, {des_cfb, [], [block]}, @@ -563,32 +563,43 @@ compute(Config) when is_list(Config) -> Gen = proplists:get_value(compute, Config), lists:foreach(fun do_compute/1, Gen). %%-------------------------------------------------------------------- -generate_all_supported() -> - [{doc, " Test that all curves from crypto:ec_curves/0 returns two binaries"}]. -generate_all_supported(_Config) -> +use_all_elliptic_curves() -> + [{doc, " Test that all curves from crypto:ec_curves/0"}]. +use_all_elliptic_curves(_Config) -> + Msg = <<"hello world!">>, + Sups = crypto:supports(), + Curves = proplists:get_value(curves, Sups), + Hashs = proplists:get_value(hashs, Sups), + ct:log("Lib: ~p~nFIPS: ~p~nCurves:~n~p~nHashs: ~p", [crypto:info_lib(), + crypto:info_fips(), + Curves, + Hashs]), Results = - [try - crypto:generate_key(ecdh, C) - of - {B1,B2} when is_binary(B1) and is_binary(B2) -> - %% That is, seems like it works as expected. - {ok,C}; - Err -> - ct:log("ERROR: Curve ~p generated ~p", [C,Err]), - {error,{C,Err}} - catch - Cls:Err:Stack -> - ct:log("ERROR: Curve ~p exception ~p:~p~n~p", [C,Cls,Err,Stack]), - {error,{C,{Cls,Err}}} - end - || C <- crypto:ec_curves(), - not lists:member(C, [ed25519, ed448]) + [{{Curve,Hash}, + try + {Pub,Priv} = crypto:generate_key(ecdh, Curve), + true = is_binary(Pub), + true = is_binary(Priv), + Sig = crypto:sign(ecdsa, Hash, Msg, [Priv, Curve]), + crypto:verify(ecdsa, Hash, Msg, Sig, [Pub, Curve]) + catch + C:E -> + {C,E} + end} + || Curve <- Curves -- [ed25519, ed448, x25519, x448, ipsec3, ipsec4], + Hash <- Hashs -- [md4, md5, ripemd160, sha3_224, sha3_256, sha3_384, sha3_512] ], - OK = [C || {ok,C} <- Results], - ct:log("Ok (len=~p): ~p", [length(OK), OK]), - false = lists:any(fun({error,_}) -> true; - (_) -> false - end, Results). + Fails = + lists:filter(fun({_,true}) -> false; + (_) -> true + end, Results), + case Fails of + [] -> + ok; + _ -> + ct:log("Fails:~n~p",[Fails]), + ct:fail("Bad curve(s)",[]) + end. %%-------------------------------------------------------------------- generate() -> diff --git a/lib/crypto/test/crypto_bench.spec b/lib/crypto/test/crypto_bench.spec new file mode 100644 index 0000000000..b9a26d94db --- /dev/null +++ b/lib/crypto/test/crypto_bench.spec @@ -0,0 +1,3 @@ +{suites, "../crypto_test", [ + crypto_bench_SUITE + ]}. diff --git a/lib/crypto/test/crypto_bench_SUITE.erl b/lib/crypto/test/crypto_bench_SUITE.erl new file mode 100644 index 0000000000..e1fd0a63e5 --- /dev/null +++ b/lib/crypto/test/crypto_bench_SUITE.erl @@ -0,0 +1,387 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009-2018. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +%% +-module(crypto_bench_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include_lib("common_test/include/ct_event.hrl"). +-include_lib("common_test/include/ct.hrl"). + +suite() -> [%%{ct_hooks,[{ts_install_cth,[{nodenames,2}]}]}, + {timetrap,{minutes,2}} + ]. + +all() -> + [ + {group, ciphers_128} + ]. + +groups() -> + [ + {ciphers_128, [{repeat, 3}], [{group,textblock_256} + ]}, + + {textblock_256, [{repeat,2}], [ + block, + stream + ]} + ]. + +%%%---------------------------------------------------------------- +%%% +init_per_suite(Config) -> + try crypto:start() of + _ -> + [{_,_,Info}] = crypto:info_lib(), + ct:comment("~s",[Info]), + ct:pal("Crypto version: ~p~n~n~p",[Info,crypto:supports()]), + [{sec_goal,5} | Config] + catch _:_ -> + {fail, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + application:stop(crypto). + +%%%---------------------------------------------------------------- +%%% +init_per_group(Group, Config0) -> + ct:pal("~p(~p,..)",[?FUNCTION_NAME,Group]), + + Config = calibrate(Config0), + case atom_to_list(Group) of + "ciphers_"++KeySizeStr -> + KeySize = list_to_integer(KeySizeStr), + [{key_size,KeySize} + | measure_openssl_aes_cbc(KeySize, Config)]; + + "textblock_"++BlockSizeStr -> + BlockSize = list_to_integer(BlockSizeStr), + [{block_size,BlockSize} | Config]; + + _ -> + Config + end. + +end_per_group(_Group, Config) -> + Config. + + +measure_openssl_aes_cbc(KeySize, Config) -> + BLno_acc = [baseline(aes_cbc, KeySize, false)], + ct:pal("Non-accelerated baseline encryption time [µs/block]:~n~p", [BLno_acc]), + BLacc = [baseline(aes_cbc, KeySize, true)], + ct:pal("Possibly accelerated baseline encryption time [µs/block]:~n~p", [BLacc]), + [{acc,BLacc}, + {no_acc,BLno_acc} | Config]. + +calibrate(Config) -> + Secs = proplists:get_value(sec_goal, Config, 5), + {_,Empty} = data(empty, 0, 0), + {Ne,Te} = run1(Secs*2000, Empty), + [{overhead,Te/Ne} | Config]. + +%%%================================================================ +%%% +%%% +block(Config) -> + run_cryptos([aes_cbc, aes_gcm, aes_ccm, chacha20_poly1305], + Config). + +stream(Config) -> + run_cryptos([aes_ctr, chacha20], + Config). + +%%%================================================================ +%%% +%%% + +run_cryptos(Cryptos, Config) -> + run_cryptos(Cryptos, 1, Config). + +run_cryptos(Cryptos, Factor, Config) -> + KeySize = proplists:get_value(key_size, Config), + BlockSize = proplists:get_value(block_size, Config), + MilliSecGoal = 1000*proplists:get_value(sec_goal,Config), + OverHead = proplists:get_value(overhead, Config, 0), + [try + Factor*run(Crypto,KeySize,BlockSize,MilliSecGoal) - OverHead + of + TimePerOp -> % µs + %% First, Report speed of encrypting blocks of 1000. [blocks/sec] + ReportUnit = 1000, + Label = [fmt(Crypto)," key:",KeySize," block:",BlockSize], + report(Label, + (BlockSize/ReportUnit)*1000000/TimePerOp + ), + + EffCrypto = case Crypto of + X -> X + end, + %% Percent of accelerated speed + case find_value([acc,{EffCrypto,KeySize},BlockSize], Config) of + undefined -> + ok; + TimePerOpBaseAcc -> + report(["Percent of acc OpenSSL "|Label], + 100*TimePerOpBaseAcc/TimePerOp % Percent of base *speed* + ) + end, + + %% Percent of non-accelerated speed + case find_value([no_acc,{EffCrypto,KeySize},BlockSize], Config) of + undefined -> + ok; + TimePerOpBaseNoAcc -> + report(["Percent of noacc OpenSSL "|Label], + 100*TimePerOpBaseNoAcc/TimePerOp % Percent of base *speed* + ) + end + catch + _:_ -> + ct:pal("~p unsupported",[{Crypto,KeySize,BlockSize}]) + end + || Crypto <- Cryptos, + supported(Crypto) + ]. + + +run(Crypto, KeySize, BlockSize, MilliSecGoal) -> + {_Type, Funs} = data(Crypto, KeySize, BlockSize), + {Nc,Tc} = run1(MilliSecGoal, Funs), + Tc/Nc. + +fmt(X) -> X. + + +find_value(KeyPath, PropList, Default) -> + try find_value(KeyPath, PropList) + of + undefined -> Default + catch + error:function_clause -> Default + end. + +find_value(KeyPath, PropList) -> + lists:foldl(fun(K, L) when is_list(L) -> proplists:get_value(K,L); + (_, _) -> undefined + end, PropList, KeyPath). + +%%%================================================================ +%%% +%%% +funs({block, {Type, Key, IV, Block}}) -> + {fun() -> ok end, + fun(_) -> crypto:block_encrypt(Type, Key, IV, Block) end, + fun(_) -> ok end}; + +funs({stream, {Type, Key, IV, Block}}) -> + {fun() -> {crypto:stream_init(Type, Key, IV),ok} end, + fun({Ctx,_}) -> crypto:stream_encrypt(Ctx, Block) end, + fun(_) -> ok end}. + + +data(aes_cbc, KeySize, BlockSize) -> + Type = case KeySize of + 128 -> aes_cbc128; + 256 -> aes_cbc256 + end, + Key = mk_bin(KeySize div 8), + IV = mk_bin(16), + Block = mk_bin(BlockSize), + {Type, funs({block, {Type, Key, IV, Block}})}; + +data(aes_gcm, KeySize, BlockSize) -> + Type = aes_gcm, + Key = mk_bin(KeySize div 8), + IV = mk_bin(12), + Block = mk_bin(BlockSize), + AAD = <<01,02,03,04>>, + {Type, funs({block, {Type, Key, IV, {AAD,Block,16}}})}; + +data(aes_ccm, KeySize, BlockSize) -> + Type = aes_ccm, + Key = mk_bin(KeySize div 8), + IV = mk_bin(12), + Block = mk_bin(BlockSize), + AAD = <<01,02,03,04>>, + {Type, funs({block, {Type, Key, IV, {AAD,Block,12}}})}; + +data(aes_ctr, KeySize, BlockSize) -> + Type = aes_ctr, + Key = mk_bin(KeySize div 8), + IV = mk_bin(16), + Block = mk_bin(BlockSize), + {Type, funs({stream, {Type, Key, IV, Block}})}; + +data(chacha20_poly1305, 256=KeySize, BlockSize) -> + Type = chacha20_poly1305, + Key = mk_bin(KeySize div 8), + IV = mk_bin(16), + AAD = <<01,02,03,04>>, + Block = mk_bin(BlockSize), + {Type, funs({block, {Type, Key, IV, {AAD,Block}}})}; + +data(chacha20, 256=KeySize, BlockSize) -> + Type = chacha20, + Key = mk_bin(KeySize div 8), + IV = mk_bin(16), + Block = mk_bin(BlockSize), + {Type, funs({stream, {Type, Key, IV, Block}})}; + +data(empty, 0, 0) -> + {undefined, + {fun() -> ok end, + fun(X) -> X end, + fun(_) -> ok end}}. + +%%%================================================================ +%%% +%%% +run1(MilliSecGoal, Funs) -> + Parent = self(), + Pid = spawn(fun() -> + {Fi,Fu,Ff} = Funs, + Ctx0 = Fi(), + T0 = start_time(), + {N,Ctx} = loop(Fu, Ctx0, 0), + T = elapsed_time(T0), + Ff(Ctx), + Parent ! {result,N,microseconds(T)} + end), + Pid ! go, + receive + after MilliSecGoal -> + Pid ! stop + end, + receive + {result,N,MicroSecs} -> + {N,MicroSecs} + end. + + +loop(F, Ctx, N) -> + receive + stop -> + {N, Ctx} + after 0 -> + loop(F, F(Ctx), N+1) + end. + +%%%---------------------------------------------------------------- +report(LabelList, Value) -> + Label = report_chars(lists:concat(LabelList)), + ct:pal("ct_event:notify ~p: ~p", [Label, Value]), + ct_event:notify( + #event{name = benchmark_data, + data = [{name, Label}, + {value,Value}]}). + +report_chars(Cs) -> + [case C of + $- -> $_; + _ -> C + end || C <- Cs]. + +%%%---------------------------------------------------------------- +supported(Algorithm) -> + lists:member(Algorithm, + [A || {_,As} <- crypto:supports(), A <- As] + ). + +%%%---------------------------------------------------------------- +start_time() -> + erlang:system_time(). + +elapsed_time(StartTime) -> + erlang:system_time() - StartTime. + +microseconds(Time) -> + erlang:convert_time_unit(Time, native, microsecond). + +%%%---------------------------------------------------------------- + +%% Example output: +%% +DT:aes-128-cbc:3:16 +%% +R:135704772:aes-128-cbc:2.980000 +%% +DT:aes-128-cbc:3:64 +%% +R:36835089:aes-128-cbc:3.000000 +%% +DT:aes-128-cbc:3:256 +%% +R:9398616:aes-128-cbc:3.000000 +%% +DT:aes-128-cbc:3:1024 +%% +R:2355683:aes-128-cbc:2.990000 +%% +DT:aes-128-cbc:3:8192 +%% +R:294508:aes-128-cbc:2.990000 +%% +H:16:64:256:1024:8192 +%% +F:22:aes-128-cbc:728616225.50:785815232.00:802015232.00:806762338.46:806892821.40 + +baseline(Crypto, KeySize, EVP) -> + Spec= + case {Crypto,KeySize} of + {aes_cbc, 128} -> "aes-128-cbc"; + {aes_cbc, 256} -> "aes-256-cbc" + end, + {{Crypto,KeySize}, baseline(Spec, EVP)}. + +baseline(Spec, EVP) -> + Cmd = + case EVP of + true -> "openssl speed -mr -evp " ++ Spec; + false-> "openssl speed -mr " ++ Spec + end, + get_base_values(string:tokens(os:cmd(Cmd),"\n"), Spec, []). + + +get_base_values(["+DT:"++Sdt, + "+R:"++Sr + |T], Crypto, Acc) -> + [Crypto0,_GoalSecs0,BlockSize0] = string:tokens(Sdt, ":"), + [Nblocks0,Crypto0,RealSecs0] = string:tokens(Sr, ":"), + Crypto = fix_possible_space_bug(Crypto0), + RealSecs = list_to_float(RealSecs0), + BlockSize = list_to_integer(BlockSize0), + Nblocks = list_to_integer(Nblocks0), + get_base_values(T, Crypto, [{BlockSize, 1000000*RealSecs/Nblocks} | Acc]); + +get_base_values([_|T], Crypto, Acc) -> + get_base_values(T, Crypto, Acc); + +get_base_values([], _, Acc) -> + lists:sort(Acc). + +fix_possible_space_bug(S) -> lists:concat(lists:join("-",string:tokens(S,"- "))). + +%%%---------------------------------------------------------------- +mk_bin(Size) when Size =< 256 -> + list_to_binary(lists:seq(0,Size-1)); + +mk_bin(Size) when 1024 =< Size -> + B = mk_bin(Size div 4), + Brest = mk_bin(Size rem 4), + <<B/binary, B/binary, B/binary, B/binary, Brest/binary>>; + +mk_bin(Size) when 256 < Size -> + B = mk_bin(Size div 2), + Brest = mk_bin(Size rem 2), + <<B/binary, B/binary, Brest/binary>>. + diff --git a/lib/inets/doc/src/httpd_util.xml b/lib/inets/doc/src/httpd_util.xml index 29971ba8ae..e0f947f860 100644 --- a/lib/inets/doc/src/httpd_util.xml +++ b/lib/inets/doc/src/httpd_util.xml @@ -45,8 +45,7 @@ <fsummary>Converts the date to the Erlang date format.</fsummary> <type> <v>DateString = string()</v> - <v>ErlDate = {{Year,Month,Date},{Hour,Min,Sec}}</v> - <v>Year = Month = Date = Hour = Min = Sec = integer()</v> + <v>ErlDate = calendar:datetime() </v> </type> <desc> <p><c>convert_request_date/1</c> converts <c>DateString</c> to @@ -281,10 +280,10 @@ <func> <name since="">rfc1123_date() -> RFC1123Date</name> - <name since="">rfc1123_date({{YYYY,MM,DD},{Hour,Min,Sec}}) -> RFC1123Date</name> + <name since="">rfc1123_date(Date) -> RFC1123Date</name> <fsummary>Returns the current date in RFC 1123 format.</fsummary> <type> - <v>YYYY = MM = DD = Hour = Min = Sec = integer()</v> + <v> Date = calendar:datetime()</v> <v>RFC1123Date = string()</v> </type> <desc> diff --git a/lib/inets/test/httpd_bench_SUITE.erl b/lib/inets/test/httpd_bench_SUITE.erl index 4b549dcb5b..087516f56c 100644 --- a/lib/inets/test/httpd_bench_SUITE.erl +++ b/lib/inets/test/httpd_bench_SUITE.erl @@ -37,7 +37,8 @@ %% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- suite() -> - [{ct_hooks,[{ts_install_cth,[{nodenames,2}]}]}]. + [{timetrap, {minutes, 1}}, + {ct_hooks,[{ts_install_cth,[{nodenames,2}]}]}]. all() -> [ @@ -499,8 +500,8 @@ start_dummy("http"= Protocol, Config) -> Conf = [ %%{big, filename:join(DataDir, "1M_file")}, %%{small, filename:join(DataDir, "1k_file")}, - {big, {gen, crypto:rand_bytes(1000000)}}, - {small, {gen, crypto:rand_bytes(1000)}}, + {big, {gen, crypto:strong_rand_bytes(1000000)}}, + {small, {gen, crypto:strong_rand_bytes(1000)}}, {http_version, HTTPVersion}, {keep_alive, ?config(keep_alive, Config)} ], @@ -519,8 +520,8 @@ start_dummy("https" = Protocol, Config) -> Opts = [{active, true}, {nodelay, true}, {reuseaddr, true} | SSLOpts], Conf = [%%{big, filename:join(DataDir, "1M_file")}, %%{small, filename:join(DataDir, "1k_file")}, - {big, {gen, crypto:rand_bytes(1000000)}}, - {small, {gen, crypto:rand_bytes(1000)}}, + {big, {gen, crypto:strong_rand_bytes(1000000)}}, + {small, {gen, crypto:strong_rand_bytes(1000)}}, {http_version, HTTPVersion}, {keep_alive, ?config(keep_alive, Config)} ], diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index 104c698591..709ba8e8fd 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -1007,13 +1007,34 @@ get_tcpi_sacked(Sock) -> <marker id="option-linger"></marker> </item> <tag><c>{linger, {true|false, Seconds}}</c></tag> - <item> + <item> <p>Determines the time-out, in seconds, for flushing unsent data - in the <c>close/1</c> socket call. If the first component of - the value tuple is <c>false</c>, the second is ignored. This - means that <c>close/1</c> returns immediately, not waiting - for data to be flushed. Otherwise, the second component is - the flushing time-out, in seconds.</p> + in the <c>close/1</c> socket call. </p> + <p>The first component is if linger is enabled, the second component + is the flushing time-out, in seconds. There are 3 alternatives:</p> + <taglist> + <tag><c>{false, _}</c></tag> + <item> + <p>close/1 or shutdown/2 returns immediately, + not waiting for data to be flushed, with closing + happening in the background.</p> + </item> + <tag><c>{true, 0}</c></tag> + <item> + <p>Aborts the connection when it is closed. + Discards any data still remaining in the send buffers + and sends RST to the peer.</p> + <p>This avoids TCP's TIME_WAIT state, but leaves open + the possibility that another "incarnation" of this connection + being created.</p> + </item> + <tag><c>{true, Time} when Time > 0</c></tag> + <item> + <p>close/1 or shutdown/2 will not return until + all queued messages for the socket have been successfully + sent or the linger timeout (Time) has been reached.</p> + </item> + </taglist> </item> <tag><c>{low_msgq_watermark, Size}</c></tag> <item> diff --git a/lib/kernel/src/seq_trace.erl b/lib/kernel/src/seq_trace.erl index 14fe21e9de..4f9d7b3e5c 100644 --- a/lib/kernel/src/seq_trace.erl +++ b/lib/kernel/src/seq_trace.erl @@ -98,7 +98,7 @@ print(Label, Term) -> -spec reset_trace() -> 'true'. reset_trace() -> - erlang:system_flag(1, 0). + erlang:system_flag(reset_seq_trace, true). %% reset_trace(Pid) -> % this might be a useful function too diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl index 7828cc4716..a0154b2694 100644 --- a/lib/kernel/test/init_SUITE.erl +++ b/lib/kernel/test/init_SUITE.erl @@ -295,7 +295,7 @@ is_real_system(KernelVsn, StdlibVsn) -> %% before restart. %% ------------------------------------------------ many_restarts() -> - [{timetrap,{minutes,8}}]. + [{timetrap,{minutes,16}}]. many_restarts(Config) when is_list(Config) -> {ok, Node} = loose_node:start(init_test, "", ?DEFAULT_TIMEOUT_SEC), @@ -315,7 +315,7 @@ loop_restart(N,Node,EHPid) -> loose_node:stop(Node), ct:fail(not_stopping) end, - ok = wait_for(30, Node, EHPid), + ok = wait_for(60, Node, EHPid), loop_restart(N-1,Node,rpc:call(Node,erlang,whereis,[logger])). wait_for(0,Node,_) -> @@ -367,7 +367,8 @@ restart(Config) when is_list(Config) -> SysProcs0 = rpc:call(Node, ?MODULE, find_system_processes, []), io:format("SysProcs0=~p~n", [SysProcs0]), [InitPid, PurgerPid, LitCollectorPid, - DirtySigNPid, DirtySigHPid, DirtySigMPid] = SysProcs0, + DirtySigNPid, DirtySigHPid, DirtySigMPid, + PrimFilePid] = SysProcs0, InitPid = rpc:call(Node, erlang, whereis, [init]), PurgerPid = rpc:call(Node, erlang, whereis, [erts_code_purger]), Procs = rpc:call(Node, erlang, processes, []), @@ -385,7 +386,8 @@ restart(Config) when is_list(Config) -> SysProcs1 = rpc:call(Node, ?MODULE, find_system_processes, []), io:format("SysProcs1=~p~n", [SysProcs1]), [InitPid1, PurgerPid1, LitCollectorPid1, - DirtySigNPid1, DirtySigHPid1, DirtySigMPid1] = SysProcs1, + DirtySigNPid1, DirtySigHPid1, DirtySigMPid1, + PrimFilePid1] = SysProcs1, %% Still the same init process! InitPid1 = rpc:call(Node, erlang, whereis, [init]), @@ -411,6 +413,10 @@ restart(Config) when is_list(Config) -> DirtySigMP = pid_to_list(DirtySigMPid), DirtySigMP = pid_to_list(DirtySigMPid1), + %% and same prim_file helper process! + PrimFileP = pid_to_list(PrimFilePid), + PrimFileP = pid_to_list(PrimFilePid1), + NewProcs0 = rpc:call(Node, erlang, processes, []), NewProcs = NewProcs0 -- SysProcs1, case check_processes(NewProcs, MaxPid) of @@ -437,7 +443,8 @@ restart(Config) when is_list(Config) -> literal_collector, dirty_sig_handler_normal, dirty_sig_handler_high, - dirty_sig_handler_max}). + dirty_sig_handler_max, + prim_file}). find_system_processes() -> find_system_procs(processes(), #sys_procs{}). @@ -448,7 +455,8 @@ find_system_procs([], SysProcs) -> SysProcs#sys_procs.literal_collector, SysProcs#sys_procs.dirty_sig_handler_normal, SysProcs#sys_procs.dirty_sig_handler_high, - SysProcs#sys_procs.dirty_sig_handler_max]; + SysProcs#sys_procs.dirty_sig_handler_max, + SysProcs#sys_procs.prim_file]; find_system_procs([P|Ps], SysProcs) -> case process_info(P, [initial_call, priority]) of [{initial_call,{erl_init,start,2}},_] -> @@ -472,6 +480,9 @@ find_system_procs([P|Ps], SysProcs) -> {priority,max}] -> undefined = SysProcs#sys_procs.dirty_sig_handler_max, find_system_procs(Ps, SysProcs#sys_procs{dirty_sig_handler_max = P}); + [{initial_call,{prim_file,start,0}},_] -> + undefined = SysProcs#sys_procs.prim_file, + find_system_procs(Ps, SysProcs#sys_procs{prim_file = P}); _ -> find_system_procs(Ps, SysProcs) end. diff --git a/lib/observer/src/cdv_html_wx.erl b/lib/observer/src/cdv_html_wx.erl index ffef83227c..8956173c93 100644 --- a/lib/observer/src/cdv_html_wx.erl +++ b/lib/observer/src/cdv_html_wx.erl @@ -79,14 +79,14 @@ handle_info(active, #state{panel=HtmlWin,delayed_fetch=Callback}=State) observer_lib:sync_destroy_progress_dialog(), wx_misc:beginBusyCursor(), wxHtmlWindow:setPage(HtmlWin,HtmlText), - cdv_wx:set_status(TW), + cdv_wx_set_status(State, TW), wx_misc:endBusyCursor(), {noreply, State#state{expand_table=Tab, delayed_fetch=undefined, trunc_warn=TW}}; handle_info(active, State) -> - cdv_wx:set_status(State#state.trunc_warn), + cdv_wx_set_status(State, State#state.trunc_warn), {noreply, State}; handle_info(Info, State) -> @@ -164,3 +164,10 @@ expand(Id,Callback,#state{expand_wins=Opened0, app=App}=State) -> Opened0 end, State#state{expand_wins=Opened}. + +cdv_wx_set_status(#state{app = cdv}, Status) -> + %% this module is used by the observer when cdw_wx isn't started + %% only try to set status when used by cdv + cdv_wx:set_status(Status); +cdv_wx_set_status(_, _) -> + ok. diff --git a/lib/observer/test/crashdump_helper.erl b/lib/observer/test/crashdump_helper.erl index a71e8fc29c..d6b5eff9b5 100644 --- a/lib/observer/test/crashdump_helper.erl +++ b/lib/observer/test/crashdump_helper.erl @@ -204,4 +204,4 @@ dump_persistent_terms() -> create_persistent_terms() -> persistent_term:put({?MODULE,first}, {pid,42.0}), persistent_term:put({?MODULE,second}, [1,2,3]), - persistent_term:get(). + {persistent_term:get({?MODULE,first}),persistent_term:get({?MODULE,second})}. diff --git a/lib/observer/test/crashdump_viewer_SUITE.erl b/lib/observer/test/crashdump_viewer_SUITE.erl index 8c5e618f4a..31cf7011d4 100644 --- a/lib/observer/test/crashdump_viewer_SUITE.erl +++ b/lib/observer/test/crashdump_viewer_SUITE.erl @@ -615,9 +615,8 @@ special(File,Procs) -> #proc{dict=Dict} = ProcDetails, %% io:format("~p\n", [Dict]), - Pts1 = crashdump_helper:create_persistent_terms(), - Pts2 = proplists:get_value(pts,Dict), - true = lists:sort(Pts1) =:= lists:sort(Pts2), + Pts = crashdump_helper:create_persistent_terms(), + Pts = proplists:get_value(pts,Dict), io:format(" persistent terms ok",[]), ok; _ -> diff --git a/lib/odbc/c_src/odbcserver.c b/lib/odbc/c_src/odbcserver.c index 8c799f6ff1..fb4f61417e 100644 --- a/lib/odbc/c_src/odbcserver.c +++ b/lib/odbc/c_src/odbcserver.c @@ -2749,6 +2749,11 @@ static diagnos get_diagnos(SQLSMALLINT handleType, SQLHANDLE handle, Boolean ext errmsg_buffer_size = errmsg_buffer_size - errmsg_size; acc_errmsg_size = acc_errmsg_size + errmsg_size; current_errmsg_pos = current_errmsg_pos + errmsg_size; + } else if(result == SQL_SUCCESS_WITH_INFO && errmsg_size >= errmsg_buffer_size) { + memcpy(diagnos.sqlState, current_sql_state, SQL_STATE_SIZE); + diagnos.nativeError = nativeError; + acc_errmsg_size = errmsg_buffer_size; + break; } else { break; } diff --git a/lib/runtime_tools/examples/dist.systemtap b/lib/runtime_tools/examples/dist.systemtap index bb20d617e1..4102a5243c 100644 --- a/lib/runtime_tools/examples/dist.systemtap +++ b/lib/runtime_tools/examples/dist.systemtap @@ -19,18 +19,18 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ -probe process("beam").mark("dist-monitor") +probe process("beam.smp").mark("dist-monitor") { printf("monitor: pid %d, who %s, what %s, node %s, type %s, reason %s\n", pid(), @@ -38,38 +38,38 @@ probe process("beam").mark("dist-monitor") user_string($arg5)); } -probe process("beam").mark("dist-port_busy") +probe process("beam.smp").mark("dist-port_busy") { printf("dist port_busy: node %s, port %s, remote_node %s, blocked pid %s\n", user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); - blocked_procs[user_string($arg4)] = timestamp; + blocked_procs[user_string($arg4)] = local_clock_ns(); } -probe process("beam").mark("dist-port_busy") +probe process("beam.smp").mark("dist-port_busy") { printf("dist port_busy: node %s, port %s, remote_node %s, blocked pid %s\n", user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); - blocked_procs[user_string($arg4)] = timestamp; + blocked_procs[user_string($arg4)] = local_clock_ns(); } -probe process("beam").mark("dist-output") +probe process("beam.smp").mark("dist-output") { printf("dist output: node %s, port %s, remote_node %s bytes %d\n", user_string($arg1), user_string($arg2), user_string($arg3), $arg4); } -probe process("beam").mark("dist-outputv") +probe process("beam.smp").mark("dist-outputv") { printf("port outputv: node %s, port %s, remote_node %s bytes %d\n", user_string($arg1), user_string($arg2), user_string($arg3), $arg4); } -probe process("beam").mark("process-scheduled") +probe process("beam.smp").mark("process-scheduled") { pidstr = user_string($arg1); if (pidstr in blocked_procs) { printf("blocked pid %s scheduled now, waited %d microseconds\n", - pidstr, (timestamp - blocked_procs[pidstr]) / 1000); + pidstr, (local_clock_ns() - blocked_procs[pidstr]) / 1000); delete blocked_procs[pidstr]; } } diff --git a/lib/runtime_tools/examples/driver1.systemtap b/lib/runtime_tools/examples/driver1.systemtap index e1ee8ecffc..f5bc28b42d 100644 --- a/lib/runtime_tools/examples/driver1.systemtap +++ b/lib/runtime_tools/examples/driver1.systemtap @@ -19,108 +19,102 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ -probe process("beam").mark("driver-init") +probe process("beam.smp").mark("driver__init") { printf("driver init name %s major %d minor %d flags %d\n", user_string($arg1), $arg2, $arg3, $arg4); } -probe process("beam").mark("driver-start") +probe process("beam.smp").mark("driver__start") { printf("driver start pid %s driver name %s port %s\n", user_string($arg1), user_string($arg2), user_string($arg3)); } -probe process("beam").mark("driver-stop") +probe process("beam.smp").mark("driver__stop") { printf("driver stop pid %s driver name %s port %s\n", user_string($arg1), user_string($arg2), user_string($arg3)); } -probe process("beam").mark("driver-finish") +probe process("beam.smp").mark("driver__finish") { printf("driver finish driver name %s\n", user_string($arg1)); } -probe process("beam").mark("driver-flush") +probe process("beam.smp").mark("driver__flush") { printf("driver flush pid %s port %s port name %s\n", user_string($arg1), user_string($arg2), user_string($arg3)); } -probe process("beam").mark("driver-output") +probe process("beam.smp").mark("driver__output") { printf("driver output pid %s port %s port name %s bytes %d\n", user_string($arg1), user_string($arg2), user_string($arg3), $arg4); } -probe process("beam").mark("driver-outputv") +probe process("beam.smp").mark("driver__outputv") { printf("driver outputv pid %s port %s port name %s bytes %d\n", user_string($arg1), user_string($arg2), user_string($arg3), $arg4); } -probe process("beam").mark("driver-control") +probe process("beam.smp").mark("driver__control") { printf("driver control pid %s port %s port name %s command %d bytes %d\n", user_string($arg1), user_string($arg2), user_string($arg3), $arg4, $arg5); } -probe process("beam").mark("driver-call") +probe process("beam.smp").mark("driver__call") { printf("driver call pid %s port %s port name %s command %d bytes %d\n", user_string($arg1), user_string($arg2), user_string($arg3), $arg4, $arg5); } -probe process("beam").mark("driver-event") -{ - printf("driver event pid %s port %s port name %s\n", - user_string($arg1), user_string($arg2), user_string($arg3)); -} - -probe process("beam").mark("driver-ready_input") +probe process("beam.smp").mark("driver__ready_input") { printf("driver ready_input pid %s port %s port name %s\n", user_string($arg1), user_string($arg2), user_string($arg3)); } -probe process("beam").mark("driver-ready_output") +probe process("beam.smp").mark("driver__ready_output") { printf("driver ready_output pid %s port %s port name %s\n", user_string($arg1), user_string($arg2), user_string($arg3)); } -probe process("beam").mark("driver-timeout") +probe process("beam.smp").mark("driver__timeout") { printf("driver timeout pid %s port %s port name %s\n", user_string($arg1), user_string($arg2), user_string($arg3)); } -probe process("beam").mark("driver-ready_async") +probe process("beam.smp").mark("driver__ready_async") { printf("driver ready_async pid %s port %s port name %s\n", user_string($arg1), user_string($arg2), user_string($arg3)); } -probe process("beam").mark("driver-process_exit") +probe process("beam.smp").mark("driver__process_exit") { printf("driver process_exit pid %s port %s port name %s\n", user_string($arg1), user_string($arg2), user_string($arg3)); } -probe process("beam").mark("driver-stop_select") +probe process("beam.smp").mark("driver__stop_select") { printf("driver stop_select driver name %s\n", user_string($arg1)); } diff --git a/lib/runtime_tools/examples/function-calls.systemtap b/lib/runtime_tools/examples/function-calls.systemtap index 9c44b2d014..6bb173b3ec 100644 --- a/lib/runtime_tools/examples/function-calls.systemtap +++ b/lib/runtime_tools/examples/function-calls.systemtap @@ -18,51 +18,51 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ -probe process("beam").mark("local-function-entry") +probe process("beam.smp").mark("local-function-entry") { printf("pid %s enter (local) %s depth %d\n", user_string($arg1), user_string($arg2), $arg3); } -probe process("beam").mark("global-function-entry") +probe process("beam.smp").mark("global-function-entry") { printf("pid %s enter (global) %s depth %d\n", user_string($arg1), user_string($arg2), $arg3); } -probe process("beam").mark("function-return") +probe process("beam.smp").mark("function-return") { printf("pid %s return %s depth %d\n", user_string($arg1), user_string($arg2), $arg3); } -probe process("beam").mark("bif-entry") +probe process("beam.smp").mark("bif-entry") { printf("pid %s BIF entry mfa %s\n", user_string($arg1), user_string($arg2)); } -probe process("beam").mark("bif-return") +probe process("beam.smp").mark("bif-return") { printf("pid %s BIF return mfa %s\n", user_string($arg1), user_string($arg2)); } -probe process("beam").mark("nif-entry") +probe process("beam.smp").mark("nif-entry") { printf("pid %s NIF entry mfa %s\n", user_string($arg1), user_string($arg2)); } -probe process("beam").mark("nif-return") +probe process("beam.smp").mark("nif-return") { printf("pid %s NIF return mfa %s\n", user_string($arg1), user_string($arg2)); } diff --git a/lib/runtime_tools/examples/garbage-collection.systemtap b/lib/runtime_tools/examples/garbage-collection.systemtap index e414eea821..14f0d6851c 100644 --- a/lib/runtime_tools/examples/garbage-collection.systemtap +++ b/lib/runtime_tools/examples/garbage-collection.systemtap @@ -18,33 +18,33 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ -probe process("beam").mark("gc_major-start") +probe process("beam.smp").mark("gc_major-start") { printf("GC major start pid %s need %d words\n", user_string($arg1), $arg2); } -probe process("beam").mark("gc_minor-start") +probe process("beam.smp").mark("gc_minor-start") { printf("GC minor start pid %s need %d words\n", user_string($arg1), $arg2); } -probe process("beam").mark("gc_major-end") +probe process("beam.smp").mark("gc_major-end") { printf("GC major end pid %s reclaimed %d words\n", user_string($arg1), $arg2); } -probe process("beam").mark("gc_minor-start") +probe process("beam.smp").mark("gc_minor-start") { printf("GC minor end pid %s reclaimed %d words\n", user_string($arg1), $arg2); } diff --git a/lib/runtime_tools/examples/memory1.systemtap b/lib/runtime_tools/examples/memory1.systemtap index 04df4d64c4..2fdc5a796c 100644 --- a/lib/runtime_tools/examples/memory1.systemtap +++ b/lib/runtime_tools/examples/memory1.systemtap @@ -18,34 +18,34 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ -probe process("beam").mark("copy-struct") +probe process("beam.smp").mark("copy-struct") { printf("copy_struct %d bytes\n", $arg1); } -probe process("beam").mark("copy-object") +probe process("beam.smp").mark("copy-object") { printf("copy_object pid %s %d bytes\n", user_string($arg1), $arg2); } -probe process("beam").mark("process-heap_grow") +probe process("beam.smp").mark("process-heap_grow") { printf("proc heap grow pid %s %d -> %d bytes\n", user_string($arg1), $arg2, $arg3); } -probe process("beam").mark("process-heap_shrink") +probe process("beam.smp").mark("process-heap_shrink") { printf("proc heap shrink pid %s %d -> %d bytes\n", user_string($arg1), $arg2, $arg3); diff --git a/lib/runtime_tools/examples/messages.systemtap b/lib/runtime_tools/examples/messages.systemtap index f2ef56a22b..49b7f46d69 100644 --- a/lib/runtime_tools/examples/messages.systemtap +++ b/lib/runtime_tools/examples/messages.systemtap @@ -18,15 +18,15 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ probe begin @@ -38,7 +38,7 @@ probe begin printf("\n"); } -probe process("beam").mark("message-send") +probe process("beam.smp").mark("message-send") { if ($arg4 == 0 && $arg5 == 0 && $arg6 == 0) { printf("send: %s -> %s: %d words\n", @@ -51,7 +51,7 @@ probe process("beam").mark("message-send") } } -probe process("beam").mark("message-send-remote") +probe process("beam.smp").mark("message-send-remote") { if ($arg5 == 0 && $arg6 == 0 && $arg7 == 0) { printf("send : %s -> %s %s: %d words\n", @@ -64,7 +64,7 @@ probe process("beam").mark("message-send-remote") } } -probe process("beam").mark("message-queued") +probe process("beam.smp").mark("message-queued") { if ($arg4 == 0 && $arg5 == 0 && $arg6 == 0) { printf("queued: %s: %d words, queue len %d\n", user_string($arg1), $arg2, $arg3); @@ -75,7 +75,7 @@ probe process("beam").mark("message-queued") } } -probe process("beam").mark("message-receive") +probe process("beam.smp").mark("message-receive") { if ($arg4 == 0 && $arg5 == 0 && $arg6 == 0) { printf("receive: %s: %d words, queue len %d\n", diff --git a/lib/runtime_tools/examples/port1.systemtap b/lib/runtime_tools/examples/port1.systemtap index f7ce03a65e..235581b0b1 100644 --- a/lib/runtime_tools/examples/port1.systemtap +++ b/lib/runtime_tools/examples/port1.systemtap @@ -18,15 +18,15 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ probe begin @@ -96,19 +96,19 @@ probe begin driver_map["udp_inet", 62] = "BINDX"; } -probe process("beam").mark("port-open") +probe process("beam.smp").mark("port-open") { printf("port open pid %s port name %s port %s\n", user_string($arg1), user_string($arg2), user_string($arg3)); } -probe process("beam").mark("port-command") +probe process("beam.smp").mark("port-command") { printf("port command pid %s port %s port name %s command type %s\n", user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); } -probe process("beam").mark("port-control") +probe process("beam.smp").mark("port-control") { cmd = driver_map[user_string($arg3), $arg4]; cmd_str = (cmd == "") ? "unknown" : cmd; @@ -118,36 +118,36 @@ probe process("beam").mark("port-control") /* port-exit is fired as a result of port_close() or exit signal */ -probe process("beam").mark("port-exit") +probe process("beam.smp").mark("port-exit") { printf("port exit pid %s port %s port name %s reason %s\n", user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); } -probe process("beam").mark("port-connect") +probe process("beam.smp").mark("port-connect") { printf("port connect pid %s port %s port name %s new pid %s\n", user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); } -probe process("beam").mark("port-busy") +probe process("beam.smp").mark("port-busy") { printf("port busy %s\n", user_string($arg1)); } -probe process("beam").mark("port-not_busy") +probe process("beam.smp").mark("port-not_busy") { printf("port not busy %s\n", user_string($arg1)); } -probe process("beam").mark("aio_pool-add") +probe process("beam.smp").mark("aio_pool-add") { printf("async I/O pool add thread %d queue len %d\n", $arg1, $arg2); } -probe process("beam").mark("aio_pool-get") +probe process("beam.smp").mark("aio_pool-get") { printf("async I/O pool get thread %d queue len %d\n", $arg1, $arg2); } -global driver_map;
\ No newline at end of file +global driver_map; diff --git a/lib/runtime_tools/examples/process-scheduling.systemtap b/lib/runtime_tools/examples/process-scheduling.systemtap index b0b74257b3..231c589f64 100644 --- a/lib/runtime_tools/examples/process-scheduling.systemtap +++ b/lib/runtime_tools/examples/process-scheduling.systemtap @@ -18,28 +18,28 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ -probe process("beam").mark("process-scheduled") +probe process("beam.smp").mark("process-scheduled") { printf(" Schedule pid %s mfa %s\n", user_string($arg1), user_string($arg2)); } -probe process("beam").mark("process-unscheduled") +probe process("beam.smp").mark("process-unscheduled") { printf("Unschedule pid %s\n", user_string($arg1)); } -probe process("beam").mark("process-hibernate") +probe process("beam.smp").mark("process-hibernate") { printf(" Hibernate pid %s resume mfa %s\n", user_string($arg1), user_string($arg2)); diff --git a/lib/runtime_tools/examples/spawn-exit.systemtap b/lib/runtime_tools/examples/spawn-exit.systemtap index 89bca14496..a7b4a0a3ea 100644 --- a/lib/runtime_tools/examples/spawn-exit.systemtap +++ b/lib/runtime_tools/examples/spawn-exit.systemtap @@ -18,34 +18,34 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ -probe process("beam").mark("process-spawn") +probe process("beam.smp").mark("process-spawn") { printf("pid %s mfa %s\n", user_string($arg1), user_string($arg2)); } -probe process("beam").mark("process-exit") +probe process("beam.smp").mark("process-exit") { printf("pid %s reason %s\n", user_string($arg1), user_string($arg2)); } -probe process("beam").mark("process-exit_signal") +probe process("beam.smp").mark("process-exit_signal") { printf("sender %s -> pid %s reason %s\n", user_string($arg1), user_string($arg2), user_string($arg3)); } -probe process("beam").mark("process-exit_signal-remote") +probe process("beam.smp").mark("process-exit_signal-remote") { printf("sender %s -> node %s pid %s reason %s\n", user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); diff --git a/lib/runtime_tools/examples/user-probe-n.systemtap b/lib/runtime_tools/examples/user-probe-n.systemtap index 25f7503283..8a0a89c931 100644 --- a/lib/runtime_tools/examples/user-probe-n.systemtap +++ b/lib/runtime_tools/examples/user-probe-n.systemtap @@ -18,18 +18,19 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ -probe process("beam").mark("user_trace-n0") + +probe process("beam.smp").mark("user_trace-n0") { printf("probe n0: %s %s %d %d %d %d '%s' '%s' '%s' '%s'\n", user_string($arg1), @@ -41,7 +42,7 @@ probe process("beam").mark("user_trace-n0") $arg9 == NULL ? "" : user_string($arg9)); } -probe process("beam").mark("user_trace-n1") +probe process("beam.smp").mark("user_trace-n1") { printf("probe n1: %s %s %d %d %d %d '%s' '%s' '%s' '%s'\n", user_string($arg1), diff --git a/lib/runtime_tools/examples/user-probe.systemtap b/lib/runtime_tools/examples/user-probe.systemtap index 1777476e54..ce9dde30f8 100644 --- a/lib/runtime_tools/examples/user-probe.systemtap +++ b/lib/runtime_tools/examples/user-probe.systemtap @@ -18,23 +18,23 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ -probe process("beam").mark("user_trace-s1") +probe process("beam.smp").mark("user_trace-s1") { printf("%s\n", user_string($arg1)); } -probe process("beam").mark("user_trace-i4s4") +probe process("beam.smp").mark("user_trace-i4s4") { printf("%s %s %d %d %d %d '%s' '%s' '%s' '%s'\n", user_string($arg1), diff --git a/lib/sasl/src/systools.erl b/lib/sasl/src/systools.erl index dd1a58c3c1..34eca6679f 100644 --- a/lib/sasl/src/systools.erl +++ b/lib/sasl/src/systools.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -73,7 +73,7 @@ make_tar(RelName, Opt) -> script2boot(File) -> case systools_lib:file_term2binary(File ++ ".script", File ++ ".boot") of {error,Error} -> - io:format(systools_make:format_error(Error)), + io:format("~ts", [systools_make:format_error(Error)]), error; _ -> ok @@ -84,7 +84,7 @@ script2boot(File, Output0, _Opt) -> Output = Output0++".boot", case systools_lib:file_term2binary(Input, Output) of {error,Error} -> - io:format(systools_make:format_error(Error)), + io:format("~ts", [systools_make:format_error(Error)]), error; _ -> ok diff --git a/lib/sasl/src/systools_relup.erl b/lib/sasl/src/systools_relup.erl index e836d57670..5f1176ec69 100644 --- a/lib/sasl/src/systools_relup.erl +++ b/lib/sasl/src/systools_relup.erl @@ -587,7 +587,7 @@ default(warnings_as_errors) -> false. print_error({error, Mod, Error}) -> S = apply(Mod, format_error, [Error]), - io:format(S, []); + io:format("~ts", [S]); print_error(Other) -> io:format("Error: ~tp~n", [Other]). diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml index 370cf4c3a0..0bc4baf5eb 100644 --- a/lib/ssh/doc/src/notes.xml +++ b/lib/ssh/doc/src/notes.xml @@ -30,6 +30,21 @@ <file>notes.xml</file> </header> +<section><title>Ssh 4.7.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed port leakage if a ssh:daemon call failed.</p> + <p> + Own Id: OTP-15397 Aux Id: ERL-801 </p> + </item> + </list> + </section> + +</section> + <section><title>Ssh 4.7.2</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -293,6 +308,21 @@ </section> </section> +<section><title>Ssh 4.6.9.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed port leakage if a ssh:daemon call failed.</p> + <p> + Own Id: OTP-15397 Aux Id: ERL-801 </p> + </item> + </list> + </section> + +</section> + <section><title>Ssh 4.6.9.2</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl index 086fa6e5f8..9281bf84a7 100644 --- a/lib/ssh/src/ssh.erl +++ b/lib/ssh/src/ssh.erl @@ -270,25 +270,38 @@ daemon(Host0, Port0, UserOptions0) when 0 =< Port0, Port0 =< 65535, try {Host1, UserOptions} = handle_daemon_args(Host0, UserOptions0), #{} = Options0 = ssh_options:handle_options(server, UserOptions), - - {{Host,Port}, ListenSocket} = - open_listen_socket(Host1, Port0, Options0), - - %% Now Host,Port is what to use for the supervisor to register its name, - %% and ListenSocket is for listening on connections. But it is still owned - %% by self()... - - finalize_start(Host, Port, ?GET_OPT(profile, Options0), - ?PUT_INTERNAL_OPT({lsocket,{ListenSocket,self()}}, Options0), - fun(Opts, Result) -> - {_, Callback, _} = ?GET_OPT(transport, Opts), - receive - {request_control, ListenSocket, ReqPid} -> - ok = Callback:controlling_process(ListenSocket, ReqPid), - ReqPid ! {its_yours,ListenSocket}, - Result - end - end) + {open_listen_socket(Host1, Port0, Options0), Options0} + of + {{{Host,Port}, ListenSocket}, Options1} -> + try + %% Now Host,Port is what to use for the supervisor to register its name, + %% and ListenSocket is for listening on connections. But it is still owned + %% by self()... + finalize_start(Host, Port, ?GET_OPT(profile, Options1), + ?PUT_INTERNAL_OPT({lsocket,{ListenSocket,self()}}, Options1), + fun(Opts, Result) -> + {_, Callback, _} = ?GET_OPT(transport, Opts), + receive + {request_control, ListenSocket, ReqPid} -> + ok = Callback:controlling_process(ListenSocket, ReqPid), + ReqPid ! {its_yours,ListenSocket}, + Result + end + end) + of + {error,Err} -> + close_listen_socket(ListenSocket, Options1), + {error,Err}; + OK -> + OK + catch + error:Error -> + close_listen_socket(ListenSocket, Options1), + error(Error); + exit:Exit -> + close_listen_socket(ListenSocket, Options1), + exit(Exit) + end catch throw:bad_fd -> {error,bad_fd}; @@ -524,6 +537,15 @@ open_listen_socket(_Host0, Port0, Options0) -> {{LHost,LPort}, LSock}. %%%---------------------------------------------------------------- +close_listen_socket(ListenSocket, Options) -> + try + {_, Callback, _} = ?GET_OPT(transport, Options), + Callback:close(ListenSocket) + catch + _C:_E -> ok + end. + +%%%---------------------------------------------------------------- finalize_start(Host, Port, Profile, Options0, F) -> try %% throws error:Error if no usable hostkey is found diff --git a/lib/ssh/src/ssh_sftpd.erl b/lib/ssh/src/ssh_sftpd.erl index 278f6a9780..aa9ba0f9bb 100644 --- a/lib/ssh/src/ssh_sftpd.erl +++ b/lib/ssh/src/ssh_sftpd.erl @@ -508,11 +508,8 @@ close_our_file({_,Fd}, FileMod, FS0) -> FS1. %%% stat: do the stat -stat(Vsn, ReqId, Data, State, F) when Vsn =< 3-> - <<?UINT32(BLen), BPath:BLen/binary>> = Data, - stat(ReqId, unicode:characters_to_list(BPath), State, F); -stat(Vsn, ReqId, Data, State, F) when Vsn >= 4-> - <<?UINT32(BLen), BPath:BLen/binary, ?UINT32(_Flags)>> = Data, +stat(Vsn, ReqId, Data, State, F) -> + <<?UINT32(BLen), BPath:BLen/binary, _/binary>> = Data, stat(ReqId, unicode:characters_to_list(BPath), State, F). fstat(Vsn, ReqId, Data, State) when Vsn =< 3-> diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl index da94b5722f..5de6d52092 100644 --- a/lib/ssh/test/ssh_basic_SUITE.erl +++ b/lib/ssh/test/ssh_basic_SUITE.erl @@ -43,7 +43,9 @@ suite() -> {timetrap,{seconds,40}}]. all() -> - [{group, all_tests}]. + [{group, all_tests}, + daemon_already_started + ]. groups() -> [{all_tests, [parallel], [{group, ssh_renegotiate_SUITE}, @@ -801,6 +803,24 @@ daemon_already_started(Config) when is_list(Config) -> ssh:stop_daemon(Pid). %%-------------------------------------------------------------------- +%%% Test that a failed daemon start does not leave the port open +daemon_error_closes_port(Config) -> + GoodSystemDir = proplists:get_value(data_dir, Config), + Port = ssh_test_lib:inet_port(), + {error,_} = ssh_test_lib:daemon(Port, []), % No system dir + case ssh_test_lib:daemon(Port, [{system_dir, GoodSystemDir}]) of + {error,eaddrinuse} -> + {fail, "Port leakage"}; + {error,Error} -> + ct:log("Strange error: ~p",[Error]), + {fail, "Strange error"}; + {Pid, _Host, Port} -> + %% Ok + ssh:stop_daemon(Pid) + end. + + +%%-------------------------------------------------------------------- %%% check that known_hosts is updated correctly known_hosts(Config) when is_list(Config) -> SystemDir = proplists:get_value(data_dir, Config), diff --git a/lib/ssh/test/ssh_compat_SUITE.erl b/lib/ssh/test/ssh_compat_SUITE.erl index f4eef2dc77..8e82527c6e 100644 --- a/lib/ssh/test/ssh_compat_SUITE.erl +++ b/lib/ssh/test/ssh_compat_SUITE.erl @@ -1126,7 +1126,24 @@ prepare_local_directory(ServerRootDir) -> "chmod 222 unreadable_file", "exit"]. + check_local_directory(ServerRootDir) -> + TimesToTry = 3, % sleep 0.5, 1, 2 and then 4 secs (7.5s in total) + check_local_directory(ServerRootDir, 500, TimesToTry-1). + +check_local_directory(ServerRootDir, SleepTime, N) -> + case do_check_local_directory(ServerRootDir) of + {error,Error} when N>0 -> + %% Could be that the erlang side is faster and the docker's operations + %% are not yet finalized. + %% Sleep for a while and retry a few times: + timer:sleep(SleepTime), + check_local_directory(ServerRootDir, 2*SleepTime, N-1); + Other -> + Other + end. + +do_check_local_directory(ServerRootDir) -> case lists:sort(ok(file:list_dir(ServerRootDir)) -- [".",".."]) of ["ex_tst1","mydir","tst2"] -> {ok,Expect} = file:read_file(filename:join(ServerRootDir,"ex_tst1")), @@ -1161,6 +1178,7 @@ check_local_directory(ServerRootDir) -> {error,{bad_dir_contents,"/"}} end. + call_sftp_in_docker(Config, ServerIP, ServerPort, Cmnds, UserDir) -> {DockerIP,DockerPort} = ip_port(Config), {ok,C} = ssh:connect(DockerIP, DockerPort, diff --git a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl index 778e4a5fc8..6aa587dc7f 100644 --- a/lib/ssh/test/ssh_connection_SUITE.erl +++ b/lib/ssh/test/ssh_connection_SUITE.erl @@ -1124,12 +1124,12 @@ start_our_shell(_User, _Peer) -> ssh_exec_echo(Cmd) -> spawn(fun() -> - io:format("echo "++Cmd ++ "\n") + io:format("echo ~s\n", [Cmd]) end). ssh_exec_echo(Cmd, User) -> spawn(fun() -> - io:format(io_lib:format("echo ~s ~s\n",[User,Cmd])) + io:format("echo ~s ~s\n",[User,Cmd]) end). ssh_exec_echo(Cmd, User, _PeerAddr) -> ssh_exec_echo(Cmd,User). diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk index 3ba1e177be..2890d7fe5b 100644 --- a/lib/ssh/vsn.mk +++ b/lib/ssh/vsn.mk @@ -1,4 +1,4 @@ #-*-makefile-*- ; force emacs to enter makefile-mode -SSH_VSN = 4.7.2 +SSH_VSN = 4.7.3 APP_VSN = "ssh-$(SSH_VSN)" diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml index fa85b5b8cc..82eb8ff700 100644 --- a/lib/ssl/doc/src/notes.xml +++ b/lib/ssl/doc/src/notes.xml @@ -27,6 +27,58 @@ </header> <p>This document describes the changes made to the SSL application.</p> +<section><title>SSL 9.1.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fix encoding of the SRP extension length field in ssl. + The old encoding of the SRP extension length could cause + interoperability problems with third party SSL + implementations when SRP was used.</p> + <p> + Own Id: OTP-15477 Aux Id: ERL-790 </p> + </item> + <item> + <p> + Guarantee active once data delivery, handling TCP stream + properly.</p> + <p> + Own Id: OTP-15504 Aux Id: ERL-371 </p> + </item> + <item> + <p> + Correct gen_statem returns for some error cases</p> + <p> + Own Id: OTP-15505</p> + </item> + </list> + </section> + +</section> + +<section><title>SSL 9.1.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed renegotiation bug. Client did not handle server + initiated renegotiation correctly after rewrite to two + connection processes, due to ERL-622 commit + d87ac1c55188f5ba5cdf72384125d94d42118c18. This could + manifest it self as a " bad_record_mac" alert.</p> + <p> + Also included are some optimizations</p> + <p> + Own Id: OTP-15489 Aux Id: ERL-308 </p> + </item> + </list> + </section> + +</section> + <section><title>SSL 9.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index 2da70131e1..200fb89a4d 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -83,8 +83,9 @@ <p><c>| {ciphers, ciphers()}</c></p> <p><c>| {user_lookup_fun, {fun(), term()}}, {psk_identity, string()}, {srp_identity, {string(), string()}}</c></p> - <p><c>| {reuse_sessions, boolean()}</c></p> - <p><c>| {reuse_session, fun()} {next_protocols_advertised, [binary()]}</c></p> + <p><c>| {reuse_sessions, boolean() | save()}</c></p> + <p><c>| {reuse_session, fun() | binary()} </c></p> + <p><c>| {next_protocols_advertised, [binary()]}</c></p> <p><c>| {client_preferred_next_protocols, {client | server, [binary()]} | {client | server, [binary()], binary()}}</c></p> <p><c>| {log_alert, boolean()}</c></p> @@ -593,11 +594,23 @@ fun(srp, Username :: string(), UserState :: term()) -> <item><p>In mode <c>verify_none</c> the default behavior is to allow all x509-path validation errors. See also option <c>verify_fun</c>.</p> </item> + + <tag><marker id="client_reuse_session"/><c>{reuse_session, binary()}</c></tag> + <item><p>Reuses a specific session earlier saved with the option + <c>{reuse_sessions, save} since ssl-9.2</c> + </p></item> - <tag><c>{reuse_sessions, boolean()}</c></tag> - <item><p>Specifies if the client is to try to reuse sessions - when possible.</p></item> - + <tag><c>{reuse_sessions, boolean() | save}</c></tag> + <item><p>When <c>save</c> is specified a new connection will be negotiated + and saved for later reuse. The session ID can be fetched with + <seealso marker="#connection_information">connection_information/2</seealso> + and used with the client option <seealso marker="#client_reuse_session">reuse_session</seealso> + The boolean value true specifies that if possible, automatized session reuse will + be performed. If a new session is created, and is unique in regard + to previous stored sessions, it will be saved for possible later reuse. + Value <c>save</c> since ssl-9.2 + </p></item> + <tag><c>{cacerts, [public_key:der_encoded()]}</c></tag> <item><p>The DER-encoded trusted certificates. If this option is supplied it overrides option <c>cacertfile</c>.</p></item> @@ -796,11 +809,14 @@ fun(srp, Username :: string(), UserState :: term()) -> </item> <tag><c>{reuse_sessions, boolean()}</c></tag> - <item><p>Specifies if the server is to agree to reuse sessions - when requested by the clients. See also option <c>reuse_session</c>. + <item><p>The boolean value true specifies that the server will + agree to reuse sessions. Setting it to false will result in an empty + session table, that is no sessions will be reused. + See also option <seealso marker="#server_reuse_session">reuse_session</seealso> </p></item> - <tag><c>{reuse_session, fun(SuggestedSessionId, + <tag><marker id="server_reuse_session"/> + <c>{reuse_session, fun(SuggestedSessionId, PeerCert, Compression, CipherSuite) -> boolean()}</c></tag> <item><p>Enables the TLS/DTLS server to have a local policy for deciding if a session is to be reused or not. diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl index 3dbda2c91b..eb0f742e70 100644 --- a/lib/ssl/src/dtls_handshake.erl +++ b/lib/ssl/src/dtls_handshake.erl @@ -214,8 +214,6 @@ handle_client_hello_extensions(Version, Type, Random, CipherSuites, HelloExt, dtls_v1:corresponding_tls_version(Version), SslOpts, Session0, ConnectionStates0, Renegotiation) of - #alert{} = Alert -> - Alert; {Session, ConnectionStates, Protocol, ServerHelloExt} -> {Version, {Type, Session}, ConnectionStates, Protocol, ServerHelloExt, HashSign} catch throw:Alert -> @@ -224,17 +222,16 @@ handle_client_hello_extensions(Version, Type, Random, CipherSuites, handle_server_hello_extensions(Version, SessionId, Random, CipherSuite, Compression, HelloExt, SslOpt, ConnectionStates0, Renegotiation) -> - case ssl_handshake:handle_server_hello_extensions(dtls_record, Random, CipherSuite, - Compression, HelloExt, - dtls_v1:corresponding_tls_version(Version), - SslOpt, ConnectionStates0, Renegotiation) of - #alert{} = Alert -> - Alert; + try ssl_handshake:handle_server_hello_extensions(dtls_record, Random, CipherSuite, + Compression, HelloExt, + dtls_v1:corresponding_tls_version(Version), + SslOpt, ConnectionStates0, Renegotiation) of {ConnectionStates, ProtoExt, Protocol} -> {Version, SessionId, ConnectionStates, ProtoExt, Protocol} + catch throw:Alert -> + Alert end. - %%-------------------------------------------------------------------- enc_handshake(#hello_verify_request{protocol_version = {Major, Minor}, @@ -343,8 +340,9 @@ decode_handshake(Version, ?CLIENT_HELLO, <<?UINT24(_), ?UINT16(_), ?BYTE(Cm_length), Comp_methods:Cm_length/binary, Extensions/binary>>) -> TLSVersion = dtls_v1:corresponding_tls_version(Version), + LegacyVersion = dtls_v1:corresponding_tls_version({Major, Minor}), Exts = ssl_handshake:decode_vector(Extensions), - DecodedExtensions = ssl_handshake:decode_hello_extensions(Exts, TLSVersion, client), + DecodedExtensions = ssl_handshake:decode_hello_extensions(Exts, TLSVersion, LegacyVersion, client), #client_hello{ client_version = {Major,Minor}, diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 2c3f8bc20f..616e9e26e7 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -942,8 +942,6 @@ handle_options(Opts0, Role, Host) -> {list, [{mode, list}]}], Opts0), assert_proplist(Opts), RecordCb = record_cb(Opts), - - ReuseSessionFun = fun(_, _, _, _) -> true end, CaCerts = handle_option(cacerts, Opts, undefined), {Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun, PartialChainHanlder, VerifyClientOnce} = @@ -1014,9 +1012,8 @@ handle_options(Opts0, Role, Host) -> Opts, undefined), %% Do not send by default tls_version(HighestVersion)), - %% Server side option - reuse_session = handle_option(reuse_session, Opts, ReuseSessionFun), - reuse_sessions = handle_option(reuse_sessions, Opts, true), + reuse_sessions = handle_reuse_sessions_option(reuse_sessions, Opts, Role), + reuse_session = handle_reuse_session_option(reuse_session, Opts, Role), secure_renegotiate = handle_option(secure_renegotiate, Opts, true), client_renegotiation = handle_option(client_renegotiation, Opts, default_option_role(server, true, Role), @@ -1211,11 +1208,16 @@ validate_option(srp_identity, {Username, Password}) {unicode:characters_to_binary(Username), unicode:characters_to_binary(Password)}; +validate_option(reuse_session, undefined) -> + undefined; validate_option(reuse_session, Value) when is_function(Value) -> Value; +validate_option(reuse_session, Value) when is_binary(Value) -> + Value; validate_option(reuse_sessions, Value) when is_boolean(Value) -> Value; - +validate_option(reuse_sessions, save = Value) -> + Value; validate_option(secure_renegotiate, Value) when is_boolean(Value) -> Value; validate_option(client_renegotiation, Value) when is_boolean(Value) -> @@ -1374,6 +1376,26 @@ handle_signature_algorithms_option(Value, Version) when is_list(Value) handle_signature_algorithms_option(_, _Version) -> undefined. +handle_reuse_sessions_option(Key, Opts, client) -> + Value = proplists:get_value(Key, Opts, true), + validate_option(Key, Value), + Value; +handle_reuse_sessions_option(Key, Opts0, server) -> + Opts = proplists:delete({Key, save}, Opts0), + Value = proplists:get_value(Key, Opts, true), + validate_option(Key, Value), + Value. + +handle_reuse_session_option(Key, Opts, client) -> + Value = proplists:get_value(Key, Opts, undefined), + validate_option(Key, Value), + Value; +handle_reuse_session_option(Key, Opts, server) -> + ReuseSessionFun = fun(_, _, _, _) -> true end, + Value = proplists:get_value(Key, Opts, ReuseSessionFun), + validate_option(Key, Value), + Value. + validate_options([]) -> []; validate_options([{Opt, Value} | Tail]) -> diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl index 1b6072dbcc..d08b2cc7ad 100644 --- a/lib/ssl/src/ssl_cipher.erl +++ b/lib/ssl/src/ssl_cipher.erl @@ -34,7 +34,7 @@ -include("tls_handshake_1_3.hrl"). -include_lib("public_key/include/public_key.hrl"). --export([security_parameters/2, security_parameters/3, security_parameters_1_3/3, +-export([security_parameters/2, security_parameters/3, security_parameters_1_3/2, cipher_init/3, nonce_seed/2, decipher/6, cipher/5, aead_encrypt/5, aead_decrypt/6, suites/1, all_suites/1, crypto_support_filters/0, chacha_suites/1, anonymous_suites/1, psk_suites/1, psk_suites_anon/1, @@ -44,10 +44,11 @@ hash_algorithm/1, sign_algorithm/1, is_acceptable_hash/2, is_fallback/1, random_bytes/1, calc_mac_hash/4, is_stream_ciphersuite/1, signature_scheme/1, - scheme_to_components/1, hash_size/1]). + scheme_to_components/1, hash_size/1, effective_key_bits/1, + key_material/1]). %% RFC 8446 TLS 1.3 --export([generate_client_shares/1, generate_server_share/1]). +-export([generate_client_shares/1, generate_server_share/1, add_zero_padding/2]). -compile(inline). @@ -88,23 +89,14 @@ security_parameters(Version, CipherSuite, SecParams) -> prf_algorithm = prf_algorithm(PrfHashAlg, Version), hash_size = hash_size(Hash)}. -security_parameters_1_3(SecParams, ClientRandom, CipherSuite) -> - #{cipher := Cipher, - mac := Hash, - prf := PrfHashAlg} = ssl_cipher_format:suite_definition(CipherSuite), +security_parameters_1_3(SecParams, CipherSuite) -> + #{cipher := Cipher, prf := PrfHashAlg} = + ssl_cipher_format:suite_definition(CipherSuite), SecParams#security_parameters{ - client_random = ClientRandom, cipher_suite = CipherSuite, bulk_cipher_algorithm = bulk_cipher_algorithm(Cipher), - cipher_type = type(Cipher), - key_size = effective_key_bits(Cipher), - expanded_key_material_length = expanded_key_material(Cipher), - key_material_length = key_material(Cipher), - iv_size = iv_size(Cipher), - mac_algorithm = mac_algorithm(Hash), - prf_algorithm =prf_algorithm(PrfHashAlg, {3,4}), - hash_size = hash_size(Hash), - compression_algorithm = 0}. + prf_algorithm = PrfHashAlg, %% HKDF hash algorithm + cipher_type = ?AEAD}. %%-------------------------------------------------------------------- -spec cipher_init(cipher_enum(), binary(), binary()) -> #cipher_state{}. @@ -578,7 +570,8 @@ crypto_support_filters() -> end]}. is_acceptable_keyexchange(KeyExchange, _Algos) when KeyExchange == psk; - KeyExchange == null -> + KeyExchange == null; + KeyExchange == any -> true; is_acceptable_keyexchange(KeyExchange, Algos) when KeyExchange == dh_anon; KeyExchange == dhe_psk -> @@ -621,7 +614,7 @@ is_acceptable_cipher(rc4_128, Algos) -> is_acceptable_cipher(des_cbc, Algos) -> proplists:get_bool(des_cbc, Algos); is_acceptable_cipher('3des_ede_cbc', Algos) -> - proplists:get_bool(des3_cbc, Algos); + proplists:get_bool(des_ede3, Algos); is_acceptable_cipher(aes_128_cbc, Algos) -> proplists:get_bool(aes_cbc128, Algos); is_acceptable_cipher(aes_256_cbc, Algos) -> @@ -690,10 +683,9 @@ hash_size(sha) -> hash_size(sha256) -> 32; hash_size(sha384) -> - 48. -%% Uncomment when adding cipher suite that needs it -%hash_size(sha512) -> -% 64. + 48; +hash_size(sha512) -> + 64. %%-------------------------------------------------------------------- %%% Internal functions @@ -897,8 +889,8 @@ scheme_to_components(ecdsa_secp521r1_sha512) -> {sha512, ecdsa, secp521r1}; scheme_to_components(rsa_pss_rsae_sha256) -> {sha256, rsa_pss_rsae, undefined}; scheme_to_components(rsa_pss_rsae_sha384) -> {sha384, rsa_pss_rsae, undefined}; scheme_to_components(rsa_pss_rsae_sha512) -> {sha512, rsa_pss_rsae, undefined}; -%% scheme_to_components(ed25519) -> {undefined, undefined, undefined}; -%% scheme_to_components(ed448) -> {undefined, undefined, undefined}; +scheme_to_components(ed25519) -> {undefined, undefined, undefined}; +scheme_to_components(ed448) -> {undefined, undefined, undefined}; scheme_to_components(rsa_pss_pss_sha256) -> {sha256, rsa_pss_pss, undefined}; scheme_to_components(rsa_pss_pss_sha384) -> {sha384, rsa_pss_pss, undefined}; scheme_to_components(rsa_pss_pss_sha512) -> {sha512, rsa_pss_pss, undefined}; @@ -1240,5 +1232,24 @@ generate_key_exchange(secp384r1) -> public_key:generate_key({namedCurve, secp384r1}); generate_key_exchange(secp521r1) -> public_key:generate_key({namedCurve, secp521r1}); +generate_key_exchange(x25519) -> + crypto:generate_key(ecdh, x25519); +generate_key_exchange(x448) -> + crypto:generate_key(ecdh, x448); generate_key_exchange(FFDHE) -> public_key:generate_key(ssl_dh_groups:dh_params(FFDHE)). + + +%% TODO: Move this functionality to crypto! +%% 7.4.1. Finite Field Diffie-Hellman +%% +%% For finite field groups, a conventional Diffie-Hellman [DH76] +%% computation is performed. The negotiated key (Z) is converted to a +%% byte string by encoding in big-endian form and left-padded with zeros +%% up to the size of the prime. This byte string is used as the shared +%% secret in the key schedule as specified above. +add_zero_padding(Bin, PrimeSize) + when byte_size (Bin) =:= PrimeSize -> + Bin; +add_zero_padding(Bin, PrimeSize) -> + add_zero_padding(<<0, Bin/binary>>, PrimeSize). diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index afb2ab6020..a95a96f644 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -1050,7 +1050,7 @@ cipher(internal, #finished{verify_data = Data} = Finished, get_current_prf(ConnectionStates0, read), MasterSecret, Handshake0) of verified -> - Session = register_session(Role, host_id(Role, Host, SslOpts), Port, Session0), + Session = handle_session(Role, SslOpts, Host, Port, Session0), cipher_role(Role, Data, Session, State#state{expecting_finished = false}, Connection); #alert{} = Alert -> @@ -1153,7 +1153,7 @@ handle_common_event(internal, {handshake, {#hello_request{} = Handshake, _}}, co handle_common_event(internal, {handshake, {#hello_request{}, _}}, StateName, #state{static_env = #static_env{role = client}}, _) when StateName =/= connection -> - {keep_state_and_data}; + keep_state_and_data; handle_common_event(internal, {handshake, {Handshake, Raw}}, StateName, #state{tls_handshake_history = Hs0} = State0, Connection) -> @@ -1199,7 +1199,7 @@ handle_call({shutdown, read_write = How}, From, StateName, ok -> {next_state, StateName, State#state{terminated = true}, [{reply, From, ok}]}; Error -> - {stop, StateName, State#state{terminated = true}, [{reply, From, Error}]} + {stop_and_reply, {shutdown, normal}, {reply, From, Error}, State#state{terminated = true}} end catch throw:Return -> @@ -1212,7 +1212,7 @@ handle_call({shutdown, How0}, From, StateName, ok -> {next_state, StateName, State, [{reply, From, ok}]}; Error -> - {stop, StateName, State, [{reply, From, Error}]} + {stop_and_reply, {shutdown, normal}, {reply, From, Error}, State} end; handle_call({recv, _N, _Timeout}, From, _, #state{socket_options = @@ -2455,15 +2455,35 @@ session_handle_params(#server_ecdh_params{curve = ECCurve}, Session) -> session_handle_params(_, Session) -> Session. -register_session(client, Host, Port, #session{is_resumable = new} = Session0) -> +handle_session(Role = server, #ssl_options{reuse_sessions = true} = SslOpts, + Host, Port, Session0) -> + register_session(Role, host_id(Role, Host, SslOpts), Port, Session0, true); +handle_session(Role = client, #ssl_options{verify = verify_peer, + reuse_sessions = Reuse} = SslOpts, + Host, Port, Session0) when Reuse =/= false -> + register_session(Role, host_id(Role, Host, SslOpts), Port, Session0, reg_type(Reuse)); +handle_session(server, _, Host, Port, Session) -> + %% Remove "session of type new" entry from session DB + ssl_manager:invalidate_session(Host, Port, Session), + Session; +handle_session(client, _,_,_, Session) -> + %% In client case there is no entry yet, so nothing to remove + Session. + +reg_type(save) -> + true; +reg_type(true) -> + unique. + +register_session(client, Host, Port, #session{is_resumable = new} = Session0, Save) -> Session = Session0#session{is_resumable = true}, - ssl_manager:register_session(Host, Port, Session), + ssl_manager:register_session(Host, Port, Session, Save), Session; -register_session(server, _, Port, #session{is_resumable = new} = Session0) -> +register_session(server, _, Port, #session{is_resumable = new} = Session0, _) -> Session = Session0#session{is_resumable = true}, ssl_manager:register_session(Port, Session), Session; -register_session(_, _, _, Session) -> +register_session(_, _, _, Session, _) -> Session. %% Already registered host_id(client, _Host, #ssl_options{server_name_indication = Hostname}) when is_list(Hostname) -> diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl index 6e08445798..ffd99a06ba 100644 --- a/lib/ssl/src/ssl_connection.hrl +++ b/lib/ssl/src/ssl_connection.hrl @@ -111,4 +111,61 @@ base = ?DEFAULT_DIFFIE_HELLMAN_GENERATOR}). -define(WAIT_TO_ALLOW_RENEGOTIATION, 12000). + +%%---------------------------------------------------------------------- +%% TLS 1.3 +%%---------------------------------------------------------------------- + +%% TLS 1.3 uses the same state record with the following differences: +%% +%% state :: record() +%% +%% session_cache - not implemented +%% session_cache_cb - not implemented +%% crl_db - not implemented +%% client_hello_version - Bleichenbacher mitigation in TLS 1.2 +%% client_certificate_requested - Built into TLS 1.3 state machine +%% key_algorithm - not used +%% diffie_hellman_params - used in TLS 1.2 ECDH key exchange +%% diffie_hellman_keys - used in TLS 1.2 ECDH key exchange +%% psk_identity - not used +%% srp_params - not used, no srp extension in TLS 1.3 +%% srp_keys - not used, no srp extension in TLS 1.3 +%% premaster_secret - not used +%% renegotiation - TLS 1.3 forbids renegotiation +%% hello - used in user_hello, handshake continue +%% allow_renegotiate - TLS 1.3 forbids renegotiation +%% expecting_next_protocol_negotiation - ALPN replaced NPN, depricated in TLS 1.3 +%% expecting_finished - not implemented, used by abbreviated +%% next_protocol - ALPN replaced NPN, depricated in TLS 1.3 +%% +%% connection_state :: map() +%% +%% compression_state - not used +%% mac_secret - not used +%% sequence_number - not used +%% secure_renegotiation - not used, no renegotiation_info in TLS 1.3 +%% client_verify_data - not used, no renegotiation_info in TLS 1.3 +%% server_verify_data - not used, no renegotiation_info in TLS 1.3 +%% beast_mitigation - not used +%% +%% security_parameters :: map() +%% +%% cipher_type - TLS 1.3 uses only AEAD ciphers +%% iv_size - not used +%% key_size - not used +%% key_material_length - not used +%% expanded_key_material_length - used in SSL 3.0 +%% mac_algorithm - not used +%% prf_algorithm - not used +%% hash_size - not used +%% compression_algorithm - not used +%% master_secret - used for multiple secret types in TLS 1.3 +%% client_random - not used +%% server_random - not used +%% exportable - not used +%% +%% cipher_state :: record() +%% nonce - used for sequence_number + -endif. % -ifdef(ssl_connection). diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 417e5d9eb6..5e3c767c2c 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -61,7 +61,7 @@ -export([encode_handshake/2, encode_hello_extensions/1, encode_extensions/1, encode_extensions/2, encode_client_protocol_negotiation/2, encode_protocols_advertised_on_server/1]). %% Decode --export([decode_handshake/3, decode_vector/1, decode_hello_extensions/3, decode_extensions/3, +-export([decode_handshake/3, decode_vector/1, decode_hello_extensions/4, decode_extensions/3, decode_server_key/3, decode_client_key/3, decode_suites/2 ]). @@ -639,7 +639,7 @@ encode_extensions([#ec_point_formats{ec_point_format_list = ECPointFormats} | Re ?UINT16(Len), ?BYTE(ListLen), ECPointFormatList/binary, Acc/binary>>); encode_extensions([#srp{username = UserName} | Rest], Acc) -> SRPLen = byte_size(UserName), - Len = SRPLen + 2, + Len = SRPLen + 1, encode_extensions(Rest, <<?UINT16(?SRP_EXT), ?UINT16(Len), ?BYTE(SRPLen), UserName/binary, Acc/binary>>); encode_extensions([#hash_sign_algos{hash_sign_algos = HashSignAlgos} | Rest], Acc) -> @@ -680,9 +680,9 @@ encode_extensions([#sni{hostname = Hostname} | Rest], Acc) -> encode_extensions([#client_hello_versions{versions = Versions0} | Rest], Acc) -> Versions = encode_versions(Versions0), VerLen = byte_size(Versions), - Len = VerLen + 2, + Len = VerLen + 1, encode_extensions(Rest, <<?UINT16(?SUPPORTED_VERSIONS_EXT), - ?UINT16(Len), ?UINT16(VerLen), Versions/binary, Acc/binary>>); + ?UINT16(Len), ?BYTE(VerLen), Versions/binary, Acc/binary>>); encode_extensions([#server_hello_selected_version{selected_version = Version0} | Rest], Acc) -> Version = encode_versions([Version0]), Len = byte_size(Version), %% 2 @@ -745,8 +745,7 @@ decode_handshake(Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32 ?BYTE(SID_length), Session_ID:SID_length/binary, Cipher_suite:2/binary, ?BYTE(Comp_method), ?UINT16(ExtLen), Extensions:ExtLen/binary>>) -> - - HelloExtensions = decode_hello_extensions(Extensions, Version, server_hello), + HelloExtensions = decode_hello_extensions(Extensions, Version, {Major, Minor}, server_hello), #server_hello{ server_version = {Major,Minor}, @@ -803,11 +802,12 @@ decode_vector(<<?UINT16(Len), Vector:Len/binary>>) -> Vector. %%-------------------------------------------------------------------- --spec decode_hello_extensions(binary(), ssl_record:ssl_version(), atom()) -> map(). +-spec decode_hello_extensions(binary(), ssl_record:ssl_version(), + ssl_record:ssl_version(), atom()) -> map(). %% %% Description: Decodes TLS hello extensions %%-------------------------------------------------------------------- -decode_hello_extensions(Extensions, Version, MessageType0) -> +decode_hello_extensions(Extensions, LocalVersion, LegacyVersion, MessageType0) -> %% Convert legacy atoms MessageType = case MessageType0 of @@ -815,6 +815,13 @@ decode_hello_extensions(Extensions, Version, MessageType0) -> server -> server_hello; T -> T end, + %% RFC 8446 - 4.2.1 + %% Servers MUST be prepared to receive ClientHellos that include this extension but + %% do not include 0x0304 in the list of versions. + %% Clients MUST check for this extension prior to processing the rest of the + %% ServerHello (although they will have to parse the ServerHello in order to read + %% the extension). + Version = process_supported_versions_extension(Extensions, LocalVersion, LegacyVersion), decode_extensions(Extensions, Version, MessageType, empty_extensions(Version, MessageType)). %%-------------------------------------------------------------------- @@ -1167,7 +1174,12 @@ kse_remove_private_key(#key_share_entry{ signature_algs_ext(undefined) -> undefined; -signature_algs_ext(SignatureSchemes) -> +signature_algs_ext(SignatureSchemes0) -> + %% The SSL option signature_algs contains both hash-sign algorithms (tuples) and + %% signature schemes (atoms) if TLS 1.3 is configured. + %% Filter out all hash-sign tuples when creating the signature_algs extension. + %% (TLS 1.3 specific record type) + SignatureSchemes = lists:filter(fun is_atom/1, SignatureSchemes0), #signature_algorithms{signature_scheme_list = SignatureSchemes}. signature_algs_cert(undefined) -> @@ -1191,7 +1203,8 @@ handle_client_hello_extensions(RecordCB, Random, ClientCipherSuites, Empty = empty_extensions(Version, server_hello), ServerHelloExtensions = Empty#{renegotiation_info => renegotiation_info(RecordCB, server, ConnectionStates, Renegotiation), - ec_point_formats => server_ecc_extension(Version, maps:get(ec_point_formats, Exts, undefined)) + ec_point_formats => server_ecc_extension(Version, + maps:get(ec_point_formats, Exts, undefined)) }, %% If we receive an ALPN extension and have ALPN configured for this connection, @@ -1199,13 +1212,9 @@ handle_client_hello_extensions(RecordCB, Random, ClientCipherSuites, ALPN = maps:get(alpn, Exts, undefined), if ALPN =/= undefined, ALPNPreferredProtocols =/= undefined -> - case handle_alpn_extension(ALPNPreferredProtocols, decode_alpn(ALPN)) of - #alert{} = Alert -> - Alert; - Protocol -> - {Session, ConnectionStates, Protocol, - ServerHelloExtensions#{alpn => encode_alpn([Protocol], Renegotiation)}} - end; + Protocol = handle_alpn_extension(ALPNPreferredProtocols, decode_alpn(ALPN)), + {Session, ConnectionStates, Protocol, + ServerHelloExtensions#{alpn => encode_alpn([Protocol], Renegotiation)}}; true -> NextProtocolNegotiation = maps:get(next_protocol_negotiation, Exts, undefined), ProtocolsToAdvertise = handle_next_protocol_extension(NextProtocolNegotiation, Renegotiation, Opts), @@ -1219,7 +1228,8 @@ handle_server_hello_extensions(RecordCB, Random, CipherSuite, Compression, #ssl_options{secure_renegotiate = SecureRenegotation, next_protocol_selector = NextProtoSelector}, ConnectionStates0, Renegotiation) -> - ConnectionStates = handle_renegotiation_extension(client, RecordCB, Version, maps:get(renegotiation_info, Exts, undefined), Random, + ConnectionStates = handle_renegotiation_extension(client, RecordCB, Version, + maps:get(renegotiation_info, Exts, undefined), Random, CipherSuite, undefined, Compression, ConnectionStates0, Renegotiation, SecureRenegotation), @@ -1234,12 +1244,8 @@ handle_server_hello_extensions(RecordCB, Random, CipherSuite, Compression, {ConnectionStates, alpn, Protocol}; undefined -> NextProtocolNegotiation = maps:get(next_protocol_negotiation, Exts, undefined), - case handle_next_protocol(NextProtocolNegotiation, NextProtoSelector, Renegotiation) of - #alert{} = Alert -> - Alert; - Protocol -> - {ConnectionStates, npn, Protocol} - end; + Protocol = handle_next_protocol(NextProtocolNegotiation, NextProtoSelector, Renegotiation), + {ConnectionStates, npn, Protocol}; {error, Reason} -> ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason); [] -> @@ -2201,6 +2207,47 @@ dec_server_key_signature(Params, <<?UINT16(Len), Signature:Len/binary>>, _) -> dec_server_key_signature(_, _, _) -> throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, failed_to_decrypt_server_key_sign)). +%% Processes a ClientHello/ServerHello message and returns the version to be used +%% in the decoding functions. The following rules apply: +%% - IF supported_versions extension is absent: +%% RETURN the lowest of (LocalVersion and LegacyVersion) +%% - IF supported_versions estension is present: +%% RETURN the lowest of (LocalVersion and first element of supported versions) +process_supported_versions_extension(<<>>, LocalVersion, LegacyVersion) + when LegacyVersion =< LocalVersion -> + LegacyVersion; +process_supported_versions_extension(<<>>, LocalVersion, _LegacyVersion) -> + LocalVersion; +process_supported_versions_extension(<<?UINT16(?SUPPORTED_VERSIONS_EXT), ?UINT16(Len), + ExtData:Len/binary, _Rest/binary>>, + LocalVersion, _LegacyVersion) when Len > 2 -> + <<?BYTE(_),Versions0/binary>> = ExtData, + [Highest|_] = decode_versions(Versions0), + if Highest =< LocalVersion -> + Highest; + true -> + LocalVersion + end; +process_supported_versions_extension(<<?UINT16(?SUPPORTED_VERSIONS_EXT), ?UINT16(Len), + ?BYTE(Major),?BYTE(Minor), _Rest/binary>>, + LocalVersion, _LegacyVersion) when Len =:= 2 -> + SelectedVersion = {Major, Minor}, + if SelectedVersion =< LocalVersion -> + SelectedVersion; + true -> + LocalVersion + end; +process_supported_versions_extension(<<?UINT16(_), ?UINT16(Len), + _ExtData:Len/binary, Rest/binary>>, + LocalVersion, LegacyVersion) -> + process_supported_versions_extension(Rest, LocalVersion, LegacyVersion); +%% Tolerate protocol encoding errors and skip parsing the rest of the extension. +process_supported_versions_extension(_, LocalVersion, LegacyVersion) + when LegacyVersion =< LocalVersion -> + LegacyVersion; +process_supported_versions_extension(_, LocalVersion, _) -> + LocalVersion. + decode_extensions(<<>>, _Version, _MessageType, Acc) -> Acc; decode_extensions(<<?UINT16(?ALPN_EXT), ?UINT16(ExtLen), ?UINT16(Len), @@ -2229,7 +2276,7 @@ decode_extensions(<<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), decode_extensions(<<?UINT16(?SRP_EXT), ?UINT16(Len), ?BYTE(SRPLen), SRP:SRPLen/binary, Rest/binary>>, Version, MessageType, Acc) - when Len == SRPLen + 2 -> + when Len == SRPLen + 1 -> decode_extensions(Rest, Version, MessageType, Acc#{srp => #srp{username = SRP}}); decode_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len), @@ -2327,7 +2374,7 @@ decode_extensions(<<?UINT16(?SNI_EXT), ?UINT16(Len), decode_extensions(<<?UINT16(?SUPPORTED_VERSIONS_EXT), ?UINT16(Len), ExtData:Len/binary, Rest/binary>>, Version, MessageType, Acc) when Len > 2 -> - <<?UINT16(_),Versions/binary>> = ExtData, + <<?BYTE(_),Versions/binary>> = ExtData, decode_extensions(Rest, Version, MessageType, Acc#{client_hello_versions => #client_hello_versions{ @@ -2596,30 +2643,26 @@ filter_unavailable_ecc_suites(_, Suites) -> handle_renegotiation_extension(Role, RecordCB, Version, Info, Random, NegotiatedCipherSuite, ClientCipherSuites, Compression, ConnectionStates0, Renegotiation, SecureRenegotation) -> - case handle_renegotiation_info(RecordCB, Role, Info, ConnectionStates0, - Renegotiation, SecureRenegotation, - ClientCipherSuites) of - {ok, ConnectionStates} -> - hello_pending_connection_states(RecordCB, Role, - Version, - NegotiatedCipherSuite, - Random, - Compression, - ConnectionStates); - #alert{} = Alert -> - throw(Alert) - end. + {ok, ConnectionStates} = handle_renegotiation_info(RecordCB, Role, Info, ConnectionStates0, + Renegotiation, SecureRenegotation, + ClientCipherSuites), + hello_pending_connection_states(RecordCB, Role, + Version, + NegotiatedCipherSuite, + Random, + Compression, + ConnectionStates). %% Receive protocols, choose one from the list, return it. handle_alpn_extension(_, {error, Reason}) -> - ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason); + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason)); handle_alpn_extension([], _) -> - ?ALERT_REC(?FATAL, ?NO_APPLICATION_PROTOCOL); + throw(?ALERT_REC(?FATAL, ?NO_APPLICATION_PROTOCOL)); handle_alpn_extension([ServerProtocol|Tail], ClientProtocols) -> - case lists:member(ServerProtocol, ClientProtocols) of - true -> ServerProtocol; - false -> handle_alpn_extension(Tail, ClientProtocols) - end. + case lists:member(ServerProtocol, ClientProtocols) of + true -> ServerProtocol; + false -> handle_alpn_extension(Tail, ClientProtocols) + end. handle_next_protocol(undefined, _NextProtocolSelector, _Renegotiating) -> @@ -2632,14 +2675,14 @@ handle_next_protocol(#next_protocol_negotiation{} = NextProtocols, true -> select_next_protocol(decode_next_protocols(NextProtocols), NextProtocolSelector); false -> - ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, unexpected_next_protocol_extension) + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, unexpected_next_protocol_extension)) end. handle_next_protocol_extension(NextProtocolNegotiation, Renegotiation, SslOpts)-> case handle_next_protocol_on_server(NextProtocolNegotiation, Renegotiation, SslOpts) of #alert{} = Alert -> - Alert; + throw(Alert); ProtocolsToAdvertise -> ProtocolsToAdvertise end. @@ -2894,14 +2937,14 @@ handle_renegotiation_info(_RecordCB, client, #renegotiation_info{renegotiated_co true -> {ok, ConnectionStates}; false -> - ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, client_renegotiation) + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, client_renegotiation)) end; handle_renegotiation_info(_RecordCB, server, #renegotiation_info{renegotiated_connection = ClientVerify}, ConnectionStates, true, _, CipherSuites) -> case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of true -> - ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {server_renegotiation, empty_renegotiation_info_scsv}); + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {server_renegotiation, empty_renegotiation_info_scsv})); false -> ConnectionState = ssl_record:current_connection_state(ConnectionStates, read), Data = maps:get(client_verify_data, ConnectionState), @@ -2909,7 +2952,7 @@ handle_renegotiation_info(_RecordCB, server, #renegotiation_info{renegotiated_co true -> {ok, ConnectionStates}; false -> - ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, server_renegotiation) + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, server_renegotiation)) end end; @@ -2919,7 +2962,7 @@ handle_renegotiation_info(RecordCB, client, undefined, ConnectionStates, true, S handle_renegotiation_info(RecordCB, server, undefined, ConnectionStates, true, SecureRenegotation, CipherSuites) -> case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of true -> - ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {server_renegotiation, empty_renegotiation_info_scsv}); + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {server_renegotiation, empty_renegotiation_info_scsv})); false -> handle_renegotiation_info(RecordCB, ConnectionStates, SecureRenegotation) end. @@ -2928,9 +2971,9 @@ handle_renegotiation_info(_RecordCB, ConnectionStates, SecureRenegotation) -> ConnectionState = ssl_record:current_connection_state(ConnectionStates, read), case {SecureRenegotation, maps:get(secure_renegotiation, ConnectionState)} of {_, true} -> - ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, already_secure); + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, already_secure)); {true, false} -> - ?ALERT_REC(?FATAL, ?NO_RENEGOTIATION); + throw(?ALERT_REC(?FATAL, ?NO_RENEGOTIATION)); {false, false} -> {ok, ConnectionStates} end. diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index a079c6a796..57b72366d3 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -137,10 +137,10 @@ %% Local policy for the server if it want's to reuse the session %% or not. Defaluts to allways returning true. %% fun(SessionId, PeerCert, Compression, CipherSuite) -> boolean() - reuse_session, + reuse_session :: fun() | binary() | undefined, %% Server side is a fun() %% If false sessions will never be reused, if true they %% will be reused if possible. - reuse_sessions :: boolean(), + reuse_sessions :: boolean() | save, %% Only client side can use value save renegotiate_at, secure_renegotiate, client_renegotiation, @@ -176,6 +176,8 @@ max_handshake_size :: integer(), handshake, customize_hostname_check + %% , + %% save_session :: boolean() }). -record(socket_options, diff --git a/lib/ssl/src/ssl_logger.erl b/lib/ssl/src/ssl_logger.erl index 35c8dcfd48..ce8225bf72 100644 --- a/lib/ssl/src/ssl_logger.erl +++ b/lib/ssl/src/ssl_logger.erl @@ -32,6 +32,7 @@ -define(rec_info(T,R),lists:zip(record_info(fields,T),tl(tuple_to_list(R)))). -include("tls_record.hrl"). +-include("ssl_cipher.hrl"). -include("ssl_internal.hrl"). -include("tls_handshake.hrl"). -include_lib("kernel/include/logger.hrl"). @@ -87,20 +88,32 @@ format_handshake(Direction, BinMsg) -> parse_handshake(Direction, #client_hello{ - client_version = Version + client_version = Version0, + cipher_suites = CipherSuites0, + extensions = Extensions } = ClientHello) -> + Version = get_client_version(Version0, Extensions), Header = io_lib:format("~s ~s Handshake, ClientHello", [header_prefix(Direction), version(Version)]), - Message = io_lib:format("~p", [?rec_info(client_hello, ClientHello)]), + CipherSuites = parse_cipher_suites(CipherSuites0), + Message = io_lib:format("~p", + [?rec_info(client_hello, + ClientHello#client_hello{cipher_suites = CipherSuites})]), {Header, Message}; parse_handshake(Direction, #server_hello{ - server_version = Version + server_version = Version0, + cipher_suite = CipherSuite0, + extensions = Extensions } = ServerHello) -> + Version = get_server_version(Version0, Extensions), Header = io_lib:format("~s ~s Handshake, ServerHello", [header_prefix(Direction), version(Version)]), - Message = io_lib:format("~p", [?rec_info(server_hello, ServerHello)]), + CipherSuite = format_cipher(CipherSuite0), + Message = io_lib:format("~p", + [?rec_info(server_hello, + ServerHello#server_hello{cipher_suite = CipherSuite})]), {Header, Message}; parse_handshake(Direction, #certificate{} = Certificate) -> Header = io_lib:format("~s Handshake, Certificate", @@ -148,7 +161,34 @@ parse_handshake(Direction, #hello_request{} = HelloRequest) -> Message = io_lib:format("~p", [?rec_info(hello_request, HelloRequest)]), {Header, Message}. +parse_cipher_suites([_|_] = Ciphers) -> + [format_cipher(C) || C <- Ciphers]. + +format_cipher(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV) -> + 'TLS_EMPTY_RENEGOTIATION_INFO_SCSV'; +format_cipher(C0) -> + list_to_atom(ssl_cipher_format:openssl_suite_name(C0)). + +get_client_version(Version, Extensions) -> + CHVersions = maps:get(client_hello_versions, Extensions, undefined), + case CHVersions of + #client_hello_versions{versions = [Highest|_]} -> + Highest; + undefined -> + Version + end. + +get_server_version(Version, Extensions) -> + SHVersion = maps:get(server_hello_selected_version, Extensions, undefined), + case SHVersion of + #server_hello_selected_version{selected_version = SelectedVersion} -> + SelectedVersion; + undefined -> + Version + end. +version({3,4}) -> + "TLS 1.3"; version({3,3}) -> "TLS 1.2"; version({3,2}) -> diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index c938772bc1..b1f080b0fe 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -30,7 +30,7 @@ connection_init/3, cache_pem_file/2, lookup_trusted_cert/4, new_session_id/1, clean_cert_db/2, - register_session/2, register_session/3, invalidate_session/2, + register_session/2, register_session/4, invalidate_session/2, insert_crls/2, insert_crls/3, delete_crls/1, delete_crls/2, invalidate_session/3, name/1]). @@ -170,9 +170,11 @@ clean_cert_db(Ref, File) -> %% %% Description: Make the session available for reuse. %%-------------------------------------------------------------------- --spec register_session(host(), inet:port_number(), #session{}) -> ok. -register_session(Host, Port, Session) -> - cast({register_session, Host, Port, Session}). +-spec register_session(host(), inet:port_number(), #session{}, unique | true) -> ok. +register_session(Host, Port, Session, true) -> + call({register_session, Host, Port, Session}); +register_session(Host, Port, Session, unique = Save) -> + cast({register_session, Host, Port, Session, Save}). -spec register_session(inet:port_number(), #session{}) -> ok. register_session(Port, Session) -> @@ -301,7 +303,10 @@ handle_call({{new_session_id, Port}, _}, _, #state{session_cache_cb = CacheCb, session_cache_server = Cache} = State) -> Id = new_id(Port, ?GEN_UNIQUE_ID_MAX_TRIES, Cache, CacheCb), - {reply, Id, State}. + {reply, Id, State}; +handle_call({{register_session, Host, Port, Session},_}, _, State0) -> + State = client_register_session(Host, Port, Session, State0), + {reply, ok, State}. %%-------------------------------------------------------------------- -spec handle_cast(msg(), #state{}) -> {noreply, #state{}}. @@ -311,8 +316,12 @@ handle_call({{new_session_id, Port}, _}, %% %% Description: Handling cast messages %%-------------------------------------------------------------------- -handle_cast({register_session, Host, Port, Session}, State0) -> - State = ssl_client_register_session(Host, Port, Session, State0), +handle_cast({register_session, Host, Port, Session, unique}, State0) -> + State = client_register_unique_session(Host, Port, Session, State0), + {noreply, State}; + +handle_cast({register_session, Host, Port, Session, true}, State0) -> + State = client_register_session(Host, Port, Session, State0), {noreply, State}; handle_cast({register_session, Port, Session}, State0) -> @@ -540,10 +549,10 @@ clean_cert_db(Ref, CertDb, RefDb, FileMapDb, File) -> ok end. -ssl_client_register_session(Host, Port, Session, #state{session_cache_client = Cache, - session_cache_cb = CacheCb, - session_cache_client_max = Max, - session_client_invalidator = Pid0} = State) -> +client_register_unique_session(Host, Port, Session, #state{session_cache_client = Cache, + session_cache_cb = CacheCb, + session_cache_client_max = Max, + session_client_invalidator = Pid0} = State) -> TimeStamp = erlang:monotonic_time(), NewSession = Session#session{time_stamp = TimeStamp}, @@ -557,6 +566,17 @@ ssl_client_register_session(Host, Port, Session, #state{session_cache_client = C register_unique_session(Sessions, NewSession, {Host, Port}, State) end. +client_register_session(Host, Port, Session, #state{session_cache_client = Cache, + session_cache_cb = CacheCb, + session_cache_client_max = Max, + session_client_invalidator = Pid0} = State) -> + TimeStamp = erlang:monotonic_time(), + NewSession = Session#session{time_stamp = TimeStamp}, + Pid = do_register_session({{Host, Port}, + NewSession#session.session_id}, + NewSession, Max, Pid0, Cache, CacheCb), + State#state{session_client_invalidator = Pid}. + server_register_session(Port, Session, #state{session_cache_server_max = Max, session_cache_server = Cache, session_cache_cb = CacheCb, diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl index ddc83821b4..499ba108f2 100644 --- a/lib/ssl/src/ssl_record.erl +++ b/lib/ssl/src/ssl_record.erl @@ -39,7 +39,8 @@ set_renegotiation_flag/2, set_client_verify_data/3, set_server_verify_data/3, - empty_connection_state/2, initial_connection_state/2, record_protocol_role/1]). + empty_connection_state/2, initial_connection_state/2, record_protocol_role/1, + step_encryption_state/1]). %% Compression -export([compress/3, uncompress/3, compressions/0]). @@ -118,6 +119,20 @@ activate_pending_connection_state(#{current_write := Current, }. %%-------------------------------------------------------------------- +-spec step_encryption_state(connection_states()) -> connection_states(). +%% +%% Description: Activates the next encyrption state (e.g. handshake +%% encryption). +%%-------------------------------------------------------------------- +step_encryption_state(#{pending_read := PendingRead, + pending_write := PendingWrite} = States) -> + NewRead = PendingRead#{sequence_number => 0}, + NewWrite = PendingWrite#{sequence_number => 0}, + States#{current_read => NewRead, + current_write => NewWrite}. + + +%%-------------------------------------------------------------------- -spec set_security_params(#security_parameters{}, #security_parameters{}, connection_states()) -> connection_states(). %% diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl index c9607489e9..a9759c9b43 100644 --- a/lib/ssl/src/ssl_session.erl +++ b/lib/ssl/src/ssl_session.erl @@ -53,6 +53,13 @@ is_new(_ClientSuggestion, _ServerDecision) -> %% Description: Should be called by the client side to get an id %% for the client hello message. %%-------------------------------------------------------------------- +client_id({Host, Port, #ssl_options{reuse_session = SessionId}}, Cache, CacheCb, _) when is_binary(SessionId)-> + case CacheCb:lookup(Cache, {{Host, Port}, SessionId}) of + undefined -> + <<>>; + #session{} -> + SessionId + end; client_id(ClientInfo, Cache, CacheCb, OwnCert) -> case select_session(ClientInfo, Cache, CacheCb, OwnCert) of no_session -> @@ -91,7 +98,8 @@ server_id(Port, SuggestedId, Options, Cert, Cache, CacheCb) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -select_session({_, _, #ssl_options{reuse_sessions=false}}, _Cache, _CacheCb, _OwnCert) -> +select_session({_, _, #ssl_options{reuse_sessions = Reuse}}, _Cache, _CacheCb, _OwnCert) when Reuse =/= true -> + %% If reuse_sessions == true | save a new session should be created no_session; select_session({HostIP, Port, SslOpts}, Cache, CacheCb, OwnCert) -> Sessions = CacheCb:select_session(Cache, {HostIP, Port}), @@ -132,7 +140,7 @@ is_resumable(SuggestedSessionId, Port, #ssl_options{reuse_session = ReuseFun} = false -> {false, undefined} end; undefined -> - {false, undefined} + {false, undefined} end. resumable(new) -> diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl index ea56aba34f..41542c65c1 100644 --- a/lib/ssl/src/tls_connection.erl +++ b/lib/ssl/src/tls_connection.erl @@ -683,12 +683,35 @@ connection(internal, #hello_request{}, port = Port, session_cache = Cache, session_cache_cb = CacheCb}, + renegotiation = {Renegotiation, peer}, + session = #session{own_certificate = Cert} = Session0, + ssl_options = SslOpts, + protocol_specific = #{sender := Pid}, + connection_states = ConnectionStates} = State0) -> + try tls_sender:peer_renegotiate(Pid) of + {ok, Write} -> + Hello = tls_handshake:client_hello(Host, Port, ConnectionStates, SslOpts, + Cache, CacheCb, Renegotiation, Cert, undefined), + {State, Actions} = send_handshake(Hello, State0#state{connection_states = ConnectionStates#{current_write => Write}}), + next_event(hello, no_record, State#state{session = Session0#session{session_id + = Hello#client_hello.session_id}}, Actions) + catch + _:_ -> + {stop, {shutdown, sender_blocked}, State0} + end; +connection(internal, #hello_request{}, + #state{static_env = #static_env{role = client, + host = Host, + port = Port, + session_cache = Cache, + session_cache_cb = CacheCb}, renegotiation = {Renegotiation, _}, session = #session{own_certificate = Cert} = Session0, - ssl_options = SslOpts, + ssl_options = SslOpts, connection_states = ConnectionStates} = State0) -> Hello = tls_handshake:client_hello(Host, Port, ConnectionStates, SslOpts, Cache, CacheCb, Renegotiation, Cert, undefined), + {State, Actions} = send_handshake(Hello, State0), next_event(hello, no_record, State#state{session = Session0#session{session_id = Hello#client_hello.session_id}}, Actions); @@ -718,6 +741,7 @@ connection(internal, #client_hello{}, send_alert_in_connection(Alert, State0), State = Connection:reinit_handshake_data(State0), next_event(?FUNCTION_NAME, no_record, State); + connection(Type, Event, State) -> ssl_connection:?FUNCTION_NAME(Type, Event, State, ?MODULE). @@ -1049,6 +1073,12 @@ handle_alerts([], Result) -> Result; handle_alerts(_, {stop, _, _} = Stop) -> Stop; +handle_alerts([#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} | _Alerts], + {next_state, connection = StateName, #state{user_data_buffer = Buffer, + protocol_buffers = #protocol_buffers{tls_cipher_texts = CTs}} = + State}) when (Buffer =/= <<>>) orelse + (CTs =/= []) -> + {next_state, StateName, State#state{terminated = true}}; handle_alerts([Alert | Alerts], {next_state, StateName, State}) -> handle_alerts(Alerts, ssl_connection:handle_alert(Alert, StateName, State)); handle_alerts([Alert | Alerts], {next_state, StateName, State, _Actions}) -> diff --git a/lib/ssl/src/tls_connection_1_3.erl b/lib/ssl/src/tls_connection_1_3.erl index 9ff84c703b..f5f91cedd7 100644 --- a/lib/ssl/src/tls_connection_1_3.erl +++ b/lib/ssl/src/tls_connection_1_3.erl @@ -134,67 +134,57 @@ start(internal, end. -%% TODO: move these functions + +negotiated(internal, + Map, + #state{connection_states = ConnectionStates0, + session = #session{session_id = SessionId, + own_certificate = OwnCert}, + ssl_options = #ssl_options{} = SslOpts, + key_share = KeyShare, + tls_handshake_history = HHistory0, + private_key = CertPrivateKey, + static_env = #static_env{ + cert_db = CertDbHandle, + cert_db_ref = CertDbRef, + socket = Socket, + transport_cb = Transport}} = State0, _Module) -> + Env = #{connection_states => ConnectionStates0, + session_id => SessionId, + own_certificate => OwnCert, + cert_db => CertDbHandle, + cert_db_ref => CertDbRef, + ssl_options => SslOpts, + key_share => KeyShare, + tls_handshake_history => HHistory0, + transport_cb => Transport, + socket => Socket, + private_key => CertPrivateKey}, + case tls_handshake_1_3:do_negotiated(Map, Env) of + #alert{} = Alert -> + ssl_connection:handle_own_alert(Alert, {3,4}, negotiated, State0); + M -> + %% TODO: implement update_state + %% State = update_state(State0, M), + {next_state, wait_flight2, State0, [{next_event, internal, M}]} + + end. + + update_state(#state{connection_states = ConnectionStates0, session = Session} = State, - #{client_random := ClientRandom, - cipher := Cipher, + #{cipher := Cipher, key_share := KeyShare, session_id := SessionId}) -> #{security_parameters := SecParamsR0} = PendingRead = maps:get(pending_read, ConnectionStates0), #{security_parameters := SecParamsW0} = PendingWrite = maps:get(pending_write, ConnectionStates0), - SecParamsR = ssl_cipher:security_parameters_1_3(SecParamsR0, ClientRandom, Cipher), - SecParamsW = ssl_cipher:security_parameters_1_3(SecParamsW0, ClientRandom, Cipher), + SecParamsR = ssl_cipher:security_parameters_1_3(SecParamsR0, Cipher), + SecParamsW = ssl_cipher:security_parameters_1_3(SecParamsW0, Cipher), ConnectionStates = ConnectionStates0#{pending_read => PendingRead#{security_parameters => SecParamsR}, pending_write => PendingWrite#{security_parameters => SecParamsW}}, State#state{connection_states = ConnectionStates, key_share = KeyShare, session = Session#session{session_id = SessionId}}. - - -negotiated(internal, - Map, - #state{connection_states = ConnectionStates0, - session = #session{session_id = SessionId}, - ssl_options = #ssl_options{} = SslOpts, - key_share = KeyShare, - tls_handshake_history = HHistory0, - static_env = #static_env{socket = Socket, - transport_cb = Transport}}, _Module) -> - - %% Create server_hello - %% Extensions: supported_versions, key_share, (pre_shared_key) - ServerHello = tls_handshake_1_3:server_hello(SessionId, KeyShare, - ConnectionStates0, Map), - - %% Update handshake_history (done in encode!) - %% Encode handshake - {BinMsg, _ConnectionStates, _HHistory} = - tls_connection:encode_handshake(ServerHello, {3,4}, ConnectionStates0, HHistory0), - %% Send server_hello - tls_connection:send(Transport, Socket, BinMsg), - Report = #{direction => outbound, - protocol => 'tls_record', - message => BinMsg}, - Msg = #{direction => outbound, - protocol => 'handshake', - message => ServerHello}, - ssl_logger:debug(SslOpts#ssl_options.log_level, Msg, #{domain => [otp,ssl,handshake]}), - ssl_logger:debug(SslOpts#ssl_options.log_level, Report, #{domain => [otp,ssl,tls_record]}), - ok. - - %% K_send = handshake ??? - %% (Send EncryptedExtensions) - %% ([Send CertificateRequest]) - %% [Send Certificate + CertificateVerify] - %% Send Finished - %% K_send = application ??? - - %% Will be called implicitly - %% {Record, State} = Connection:next_record(State2#state{session = Session}), - %% Connection:next_event(wait_flight2, Record, State, Actions), - %% OR - %% Connection:next_event(WAIT_EOED, Record, State, Actions) diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl index 5aca4bf8c8..f0bbd0f94f 100644 --- a/lib/ssl/src/tls_handshake.erl +++ b/lib/ssl/src/tls_handshake.erl @@ -232,7 +232,8 @@ hello(#client_hello{client_version = ClientVersion, %%-------------------------------------------------------------------- %%-------------------------------------------------------------------- --spec encode_handshake(tls_handshake(), tls_record:tls_version()) -> iolist(). +-spec encode_handshake(tls_handshake() | tls_handshake_1_3:tls_handshake_1_3(), + tls_record:tls_version()) -> iolist(). %% %% Description: Encode a handshake packet %%-------------------------------------------------------------------- @@ -318,8 +319,6 @@ handle_client_hello_extensions(Version, Type, Random, CipherSuites, HelloExt, Version, SslOpts, Session0, ConnectionStates0, Renegotiation) of - #alert{} = Alert -> - Alert; {Session, ConnectionStates, Protocol, ServerHelloExt} -> {Version, {Type, Session}, ConnectionStates, Protocol, ServerHelloExt, HashSign} @@ -330,14 +329,14 @@ handle_client_hello_extensions(Version, Type, Random, CipherSuites, handle_server_hello_extensions(Version, SessionId, Random, CipherSuite, Compression, HelloExt, SslOpt, ConnectionStates0, Renegotiation) -> - case ssl_handshake:handle_server_hello_extensions(tls_record, Random, CipherSuite, + try ssl_handshake:handle_server_hello_extensions(tls_record, Random, CipherSuite, Compression, HelloExt, Version, SslOpt, ConnectionStates0, - Renegotiation) of - #alert{} = Alert -> - Alert; + Renegotiation) of {ConnectionStates, ProtoExt, Protocol} -> {Version, SessionId, ConnectionStates, ProtoExt, Protocol} + catch throw:Alert -> + Alert end. @@ -403,14 +402,15 @@ get_tls_handshake_aux(_Version, Data, _, Acc) -> decode_handshake({3, N}, ?HELLO_REQUEST, <<>>) when N < 4 -> #hello_request{}; -decode_handshake(Version, ?CLIENT_HELLO, +decode_handshake(Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SID_length), Session_ID:SID_length/binary, ?UINT16(Cs_length), CipherSuites:Cs_length/binary, ?BYTE(Cm_length), Comp_methods:Cm_length/binary, Extensions/binary>>) -> Exts = ssl_handshake:decode_vector(Extensions), - DecodedExtensions = ssl_handshake:decode_hello_extensions(Exts, Version, client_hello), + DecodedExtensions = ssl_handshake:decode_hello_extensions(Exts, Version, {Major, Minor}, + client_hello), #client_hello{ client_version = {Major,Minor}, random = Random, diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl index f381e038cf..670c4d424d 100644 --- a/lib/ssl/src/tls_handshake_1_3.erl +++ b/lib/ssl/src/tls_handshake_1_3.erl @@ -27,6 +27,7 @@ -include("tls_handshake_1_3.hrl"). -include("ssl_alert.hrl"). +-include("ssl_cipher.hrl"). -include("ssl_internal.hrl"). -include("ssl_record.hrl"). -include_lib("public_key/include/public_key.hrl"). @@ -38,7 +39,11 @@ -export([handle_client_hello/3]). %% Create handshake messages --export([server_hello/4]). +-export([certificate/5, + certificate_verify/5, + server_hello/4]). + +-export([do_negotiated/2]). %%==================================================================== %% Create handshake messages @@ -50,8 +55,7 @@ server_hello(SessionId, KeyShare, ConnectionStates, _Map) -> Extensions = server_hello_extensions(KeyShare), #server_hello{server_version = {3,3}, %% legacy_version cipher_suite = SecParams#security_parameters.cipher_suite, - compression_method = - SecParams#security_parameters.compression_algorithm, + compression_method = 0, %% legacy attribute random = SecParams#security_parameters.server_random, session_id = SessionId, extensions = Extensions @@ -63,6 +67,37 @@ server_hello_extensions(KeyShare) -> ssl_handshake:add_server_share(Extensions, KeyShare). +%% TODO: use maybe monad for error handling! +certificate(OwnCert, CertDbHandle, CertDbRef, _CRContext, server) -> + case ssl_certificate:certificate_chain(OwnCert, CertDbHandle, CertDbRef) of + {ok, _, Chain} -> + CertList = chain_to_cert_list(Chain), + %% If this message is in response to a CertificateRequest, the value of + %% certificate_request_context in that message. Otherwise (in the case + %%of server authentication), this field SHALL be zero length. + #certificate_1_3{ + certificate_request_context = <<>>, + certificate_list = CertList}; + {error, Error} -> + ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {server_has_no_suitable_certificates, Error}) + end. + +%% TODO: use maybe monad for error handling! +certificate_verify(OwnCert, PrivateKey, SignatureScheme, Messages, server) -> + {HashAlgo, _, _} = + ssl_cipher:scheme_to_components(SignatureScheme), + + %% Transcript-Hash(Handshake Context, Certificate) + Context = [Messages, OwnCert], + THash = tls_v1:transcript_hash(Context, HashAlgo), + + Signature = digitally_sign(THash, <<"TLS 1.3, server CertificateVerify">>, + HashAlgo, PrivateKey), + + #certificate_verify_1_3{ + algorithm = SignatureScheme, + signature = Signature + }. %%==================================================================== %% Encode handshake @@ -76,7 +111,7 @@ encode_handshake(#certificate_request_1_3{ {?CERTIFICATE_REQUEST, <<EncContext/binary, BinExts/binary>>}; encode_handshake(#certificate_1_3{ certificate_request_context = Context, - entries = Entries}) -> + certificate_list = Entries}) -> EncContext = encode_cert_req_context(Context), EncEntries = encode_cert_entries(Entries), {?CERTIFICATE, <<EncContext/binary, EncEntries/binary>>}; @@ -120,14 +155,14 @@ decode_handshake(?CERTIFICATE, <<?BYTE(0), ?UINT24(Size), Certs:Size/binary>>) - CertList = decode_cert_entries(Certs), #certificate_1_3{ certificate_request_context = <<>>, - entries = CertList + certificate_list = CertList }; decode_handshake(?CERTIFICATE, <<?BYTE(CSize), Context:CSize/binary, ?UINT24(Size), Certs:Size/binary>>) -> CertList = decode_cert_entries(Certs), #certificate_1_3{ certificate_request_context = Context, - entries = CertList + certificate_list = CertList }; decode_handshake(?ENCRYPTED_EXTENSIONS, <<?UINT16(Size), EncExts:Size/binary>>) -> #encrypted_extensions{ @@ -193,12 +228,60 @@ extensions_list(HelloExtensions) -> [Ext || {_, Ext} <- maps:to_list(HelloExtensions)]. +%% TODO: add extensions! +chain_to_cert_list(L) -> + chain_to_cert_list(L, []). +%% +chain_to_cert_list([], Acc) -> + lists:reverse(Acc); +chain_to_cert_list([H|T], Acc) -> + chain_to_cert_list(T, [certificate_entry(H)|Acc]). + + +certificate_entry(DER) -> + #certificate_entry{ + data = DER, + extensions = #{} %% Extensions not supported. + }. + +%% The digital signature is then computed over the concatenation of: +%% - A string that consists of octet 32 (0x20) repeated 64 times +%% - The context string +%% - A single 0 byte which serves as the separator +%% - The content to be signed +%% +%% For example, if the transcript hash was 32 bytes of 01 (this length +%% would make sense for SHA-256), the content covered by the digital +%% signature for a server CertificateVerify would be: +%% +%% 2020202020202020202020202020202020202020202020202020202020202020 +%% 2020202020202020202020202020202020202020202020202020202020202020 +%% 544c5320312e332c207365727665722043657274696669636174655665726966 +%% 79 +%% 00 +%% 0101010101010101010101010101010101010101010101010101010101010101 +digitally_sign(THash, Context, HashAlgo, PrivateKey = #'RSAPrivateKey'{}) -> + Content = build_content(Context, THash), + + %% The length of the Salt MUST be equal to the length of the output + %% of the digest algorithm. + PadLen = ssl_cipher:hash_size(HashAlgo), + + public_key:sign(Content, HashAlgo, PrivateKey, + [{rsa_padding, rsa_pkcs1_pss_padding}, + {rsa_pss_saltlen, PadLen}]). + + +build_content(Context, THash) -> + <<" ", + " ", + Context/binary,?BYTE(0),THash/binary>>. + %%==================================================================== %% Handle handshake messages %%==================================================================== handle_client_hello(#client_hello{cipher_suites = ClientCiphers, - random = Random, session_id = SessionId, extensions = Extensions} = _Hello, #ssl_options{ciphers = ServerCiphers, @@ -233,26 +316,24 @@ handle_client_hello(#client_hello{cipher_suites = ClientCiphers, Cipher = Maybe(select_cipher_suite(ClientCiphers, ServerCiphers)), Group = Maybe(select_server_group(ServerGroups, ClientGroups)), Maybe(validate_key_share(ClientGroups, ClientShares)), - _ClientPubKey = Maybe(get_client_public_key(Group, ClientShares)), - %% Handle certificate - {PublicKeyAlgo, SignAlgo} = get_certificate_params(Cert), + ClientPubKey = Maybe(get_client_public_key(Group, ClientShares)), + + {PublicKeyAlgo, SignAlgo, SignHash} = get_certificate_params(Cert), %% Check if client supports signature algorithm of server certificate - Maybe(check_cert_sign_algo(SignAlgo, ClientSignAlgs, ClientSignAlgsCert)), + Maybe(check_cert_sign_algo(SignAlgo, SignHash, ClientSignAlgs, ClientSignAlgsCert)), - %% Check if server supports + %% Select signature algorithm (used in CertificateVerify message). SelectedSignAlg = Maybe(select_sign_algo(PublicKeyAlgo, ClientSignAlgs, ServerSignAlgs)), %% Generate server_share KeyShare = ssl_cipher:generate_server_share(Group), - _Ret = #{cipher => Cipher, group => Group, sign_alg => SelectedSignAlg, - %% client_share => ClientPubKey, + client_share => ClientPubKey, key_share => KeyShare, - client_random => Random, session_id => SessionId} %% TODO: @@ -265,9 +346,9 @@ handle_client_hello(#client_hello{cipher_suites = ClientCiphers, ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_groups); {Ref, illegal_parameter} -> ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); - {Ref, {client_hello_retry_request, _Group0}} -> + {Ref, {hello_retry_request, _Group0}} -> %% TODO - exit({client_hello_retry_request, not_implemented}); + ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, "hello_retry_request not implemented"); {Ref, no_suitable_cipher} -> ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_cipher); {Ref, {insufficient_security, no_suitable_signature_algorithm}} -> @@ -277,6 +358,197 @@ handle_client_hello(#client_hello{cipher_suites = ClientCiphers, end. +do_negotiated(#{client_share := ClientKey, + group := SelectedGroup, + sign_alg := SignatureScheme + } = Map, + #{connection_states := ConnectionStates0, + session_id := SessionId, + own_certificate := OwnCert, + cert_db := CertDbHandle, + cert_db_ref := CertDbRef, + ssl_options := SslOpts, + key_share := KeyShare, + tls_handshake_history := HHistory0, + transport_cb := Transport, + socket := Socket, + private_key := CertPrivateKey}) -> + {Ref,Maybe} = maybe(), + + try + %% Create server_hello + %% Extensions: supported_versions, key_share, (pre_shared_key) + ServerHello = server_hello(SessionId, KeyShare, ConnectionStates0, Map), + + %% Update handshake_history (done in encode!) + %% Encode handshake + {BinMsg, ConnectionStates1, HHistory1} = + tls_connection:encode_handshake(ServerHello, {3,4}, ConnectionStates0, HHistory0), + %% Send server_hello + tls_connection:send(Transport, Socket, BinMsg), + log_handshake(SslOpts, ServerHello), + log_tls_record(SslOpts, BinMsg), + + %% ConnectionStates2 = calculate_security_parameters(ClientKey, SelectedGroup, KeyShare, + %% HHistory1, ConnectionStates1), + {HandshakeSecret, ReadKey, ReadIV, WriteKey, WriteIV} = + calculate_security_parameters(ClientKey, SelectedGroup, KeyShare, + HHistory1, ConnectionStates1), + ConnectionStates2 = + update_pending_connection_states(ConnectionStates1, HandshakeSecret, + ReadKey, ReadIV, WriteKey, WriteIV), + ConnectionStates3 = + ssl_record:step_encryption_state(ConnectionStates2), + + %% Create Certificate + Certificate = certificate(OwnCert, CertDbHandle, CertDbRef, <<>>, server), + + %% Encode Certificate + {_, _ConnectionStates4, HHistory2} = + tls_connection:encode_handshake(Certificate, {3,4}, ConnectionStates3, HHistory1), + %% log_handshake(SslOpts, Certificate), + + %% Create CertificateVerify + {Messages, _} = HHistory2, + + %% Use selected signature_alg from here, HKDF only used for key_schedule + CertificateVerify = + tls_handshake_1_3:certificate_verify(OwnCert, CertPrivateKey, SignatureScheme, + Messages, server), + io:format("### CertificateVerify: ~p~n", [CertificateVerify]), + + %% Encode CertificateVerify + + %% Send Certificate, CertifricateVerify + + %% Send finished + + %% Next record/Next event + + Maybe(not_implemented(negotiated)) + + + catch + {Ref, {state_not_implemented, State}} -> + %% TODO + ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {state_not_implemented, State}) + end. + + +%% TODO: Remove this function! +not_implemented(State) -> + {error, {state_not_implemented, State}}. + + +log_handshake(SslOpts, Message) -> + Msg = #{direction => outbound, + protocol => 'handshake', + message => Message}, + ssl_logger:debug(SslOpts#ssl_options.log_level, Msg, #{domain => [otp,ssl,handshake]}). + + +log_tls_record(SslOpts, BinMsg) -> + Report = #{direction => outbound, + protocol => 'tls_record', + message => BinMsg}, + ssl_logger:debug(SslOpts#ssl_options.log_level, Report, #{domain => [otp,ssl,tls_record]}). + + +calculate_security_parameters(ClientKey, SelectedGroup, KeyShare, HHistory, ConnectionStates) -> + #{security_parameters := SecParamsR} = + ssl_record:pending_connection_state(ConnectionStates, read), + #security_parameters{prf_algorithm = HKDFAlgo, + cipher_suite = CipherSuite} = SecParamsR, + + %% Calculate handshake_secret + EarlySecret = tls_v1:key_schedule(early_secret, HKDFAlgo , {psk, <<>>}), + PrivateKey = get_server_private_key(KeyShare), %% #'ECPrivateKey'{} + + IKM = calculate_shared_secret(ClientKey, PrivateKey, SelectedGroup), + HandshakeSecret = tls_v1:key_schedule(handshake_secret, HKDFAlgo, IKM, EarlySecret), + + %% Calculate [sender]_handshake_traffic_secret + {Messages, _} = HHistory, + ClientHSTrafficSecret = + tls_v1:client_handshake_traffic_secret(HKDFAlgo, HandshakeSecret, lists:reverse(Messages)), + ServerHSTrafficSecret = + tls_v1:server_handshake_traffic_secret(HKDFAlgo, HandshakeSecret, lists:reverse(Messages)), + + %% Calculate traffic keys + #{cipher := Cipher} = ssl_cipher_format:suite_definition(CipherSuite), + {ReadKey, ReadIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, ClientHSTrafficSecret), + {WriteKey, WriteIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, ServerHSTrafficSecret), + + {HandshakeSecret, ReadKey, ReadIV, WriteKey, WriteIV}. + + %% %% Update pending connection state + %% PendingRead0 = ssl_record:pending_connection_state(ConnectionStates, read), + %% PendingWrite0 = ssl_record:pending_connection_state(ConnectionStates, write), + + %% PendingRead = update_conn_state(PendingRead0, HandshakeSecret, ReadKey, ReadIV), + %% PendingWrite = update_conn_state(PendingWrite0, HandshakeSecret, WriteKey, WriteIV), + + %% %% Update pending and copy to current (activate) + %% %% All subsequent handshake messages are encrypted + %% %% ([sender]_handshake_traffic_secret) + %% #{current_read => PendingRead, + %% current_write => PendingWrite, + %% pending_read => PendingRead, + %% pending_write => PendingWrite}. + + +get_server_private_key(#key_share_server_hello{server_share = ServerShare}) -> + get_private_key(ServerShare). + +get_private_key(#key_share_entry{ + key_exchange = #'ECPrivateKey'{} = PrivateKey}) -> + PrivateKey; +get_private_key(#key_share_entry{ + key_exchange = + {_, PrivateKey}}) -> + PrivateKey. + +%% X25519, X448 +calculate_shared_secret(OthersKey, MyKey, Group) + when is_binary(OthersKey) andalso is_binary(MyKey) andalso + (Group =:= x25519 orelse Group =:= x448)-> + crypto:compute_key(ecdh, OthersKey, MyKey, Group); +%% FFDHE +calculate_shared_secret(OthersKey, MyKey, Group) + when is_binary(OthersKey) andalso is_binary(MyKey) -> + Params = #'DHParameter'{prime = P} = ssl_dh_groups:dh_params(Group), + S = public_key:compute_key(OthersKey, MyKey, Params), + Size = byte_size(binary:encode_unsigned(P)), + ssl_cipher:add_zero_padding(S, Size); +%% ECDHE +calculate_shared_secret(OthersKey, MyKey = #'ECPrivateKey'{}, _Group) + when is_binary(OthersKey) -> + Point = #'ECPoint'{point = OthersKey}, + public_key:compute_key(Point, MyKey). + + +update_pending_connection_states(CS = #{pending_read := PendingRead0, + pending_write := PendingWrite0}, + HandshakeSecret, ReadKey, ReadIV, WriteKey, WriteIV) -> + PendingRead = update_connection_state(PendingRead0, HandshakeSecret, ReadKey, ReadIV), + PendingWrite = update_connection_state(PendingWrite0, HandshakeSecret, WriteKey, WriteIV), + CS#{pending_read => PendingRead, + pending_write => PendingWrite}. + +update_connection_state(ConnectionState = #{security_parameters := SecurityParameters0}, + HandshakeSecret, Key, IV) -> + %% Store secret + SecurityParameters = SecurityParameters0#security_parameters{ + master_secret = HandshakeSecret}, + ConnectionState#{security_parameters => SecurityParameters, + cipher_state => cipher_init(Key, IV)}. + + + +cipher_init(Key, IV) -> + #cipher_state{key = Key, iv = IV, tag_len = 16}. + + %% If there is no overlap between the received %% "supported_groups" and the groups supported by the server, then the %% server MUST abort the handshake with a "handshake_failure" or an @@ -324,14 +596,20 @@ get_client_public_key(Group, ClientShares) -> {value, {_, _, ClientPublicKey}} -> {ok, ClientPublicKey}; false -> - %% ClientHelloRetryRequest - {error, {client_hello_retry_request, Group}} + %% 4.1.4. Hello Retry Request + %% + %% The server will send this message in response to a ClientHello + %% message if it is able to find an acceptable set of parameters but the + %% ClientHello does not contain sufficient information to proceed with + %% the handshake. + {error, {hello_retry_request, Group}} end. select_cipher_suite([], _) -> {error, no_suitable_cipher}; select_cipher_suite([Cipher|ClientCiphers], ServerCiphers) -> - case lists:member(Cipher, ServerCiphers) of + case lists:member(Cipher, tls_v1:suites('TLS_v1.3')) andalso + lists:member(Cipher, ServerCiphers) of true -> {ok, Cipher}; false -> @@ -349,22 +627,28 @@ select_cipher_suite([Cipher|ClientCiphers], ServerCiphers) -> %% If no "signature_algorithms_cert" extension is %% present, then the "signature_algorithms" extension also applies to %% signatures appearing in certificates. -check_cert_sign_algo(SignAlgo, ClientSignAlgs, undefined) -> - maybe_lists_member(SignAlgo, ClientSignAlgs, - {insufficient_security, no_suitable_signature_algorithm}); -check_cert_sign_algo(SignAlgo, _, ClientSignAlgsCert) -> - maybe_lists_member(SignAlgo, ClientSignAlgsCert, - {insufficient_security, no_suitable_signature_algorithm}). + +%% Check if the signature algorithm of the server certificate is supported +%% by the client. +check_cert_sign_algo(SignAlgo, SignHash, ClientSignAlgs, undefined) -> + do_check_cert_sign_algo(SignAlgo, SignHash, ClientSignAlgs); +check_cert_sign_algo(SignAlgo, SignHash, _, ClientSignAlgsCert) -> + do_check_cert_sign_algo(SignAlgo, SignHash, ClientSignAlgsCert). %% DSA keys are not supported by TLS 1.3 select_sign_algo(dsa, _ClientSignAlgs, _ServerSignAlgs) -> {error, {insufficient_security, no_suitable_public_key}}; -%% TODO: Implement check for ellipctic curves! +%% TODO: Implement support for ECDSA keys! +select_sign_algo(_, [], _) -> + {error, {insufficient_security, no_suitable_signature_algorithm}}; select_sign_algo(PublicKeyAlgo, [C|ClientSignAlgs], ServerSignAlgs) -> {_, S, _} = ssl_cipher:scheme_to_components(C), - case PublicKeyAlgo =:= rsa andalso - ((S =:= rsa_pkcs1) orelse (S =:= rsa_pss_rsae) orelse (S =:= rsa_pss_pss)) andalso + %% RSASSA-PKCS1-v1_5 and Legacy algorithms are not defined for use in signed + %% TLS handshake messages: filter sha-1 and rsa_pkcs1. + case ((PublicKeyAlgo =:= rsa andalso S =:= rsa_pss_rsae) + orelse (PublicKeyAlgo =:= rsa_pss andalso S =:= rsa_pss_rsae)) + andalso lists:member(C, ServerSignAlgs) of true -> {ok, C}; @@ -373,51 +657,51 @@ select_sign_algo(PublicKeyAlgo, [C|ClientSignAlgs], ServerSignAlgs) -> end. -maybe_lists_member(Elem, List, Error) -> - case lists:member(Elem, List) of +do_check_cert_sign_algo(_, _, []) -> + {error, {insufficient_security, no_suitable_signature_algorithm}}; +do_check_cert_sign_algo(SignAlgo, SignHash, [Scheme|T]) -> + {Hash, Sign, _Curve} = ssl_cipher:scheme_to_components(Scheme), + case compare_sign_algos(SignAlgo, SignHash, Sign, Hash) of true -> ok; - false -> - {error, Error} + _Else -> + do_check_cert_sign_algo(SignAlgo, SignHash, T) end. -%% TODO: test with ecdsa, rsa_pss_rsae, rsa_pss_pss + +%% id-RSASSA-PSS (rsa_pss) indicates that the key may only be used for PSS signatures. +%% TODO: Uncomment when rsa_pss signatures are supported in certificates +%% compare_sign_algos(rsa_pss, Hash, Algo, Hash) +%% when Algo =:= rsa_pss_pss -> +%% true; +%% rsaEncryption (rsa) allows the key to be used for any of the standard encryption or +%% signature schemes. +compare_sign_algos(rsa, Hash, Algo, Hash) + when Algo =:= rsa_pss_rsae orelse + Algo =:= rsa_pkcs1 -> + true; +compare_sign_algos(Algo, Hash, Algo, Hash) -> + true; +compare_sign_algos(_, _, _, _) -> + false. + + get_certificate_params(Cert) -> {SignAlgo0, _Param, PublicKeyAlgo0} = ssl_handshake:get_cert_params(Cert), - SignAlgo = public_key:pkix_sign_types(SignAlgo0), + {SignHash0, SignAlgo} = public_key:pkix_sign_types(SignAlgo0), + %% Convert hash to new format + SignHash = case SignHash0 of + sha -> + sha1; + H -> H + end, PublicKeyAlgo = public_key_algo(PublicKeyAlgo0), - Scheme = sign_algo_to_scheme(SignAlgo), - {PublicKeyAlgo, Scheme}. - -sign_algo_to_scheme({Hash0, Sign0}) -> - SupportedSchemes = tls_v1:default_signature_schemes({3,4}), - Hash = case Hash0 of - sha -> - sha1; - H -> - H - end, - Sign = case Sign0 of - rsa -> - rsa_pkcs1; - S -> - S - end, - sign_algo_to_scheme(Hash, Sign, SupportedSchemes). -%% -sign_algo_to_scheme(_, _, []) -> - not_found; -sign_algo_to_scheme(H, S, [Scheme|T]) -> - {Hash, Sign, _Curve} = ssl_cipher:scheme_to_components(Scheme), - case H =:= Hash andalso S =:= Sign of - true -> - Scheme; - false -> - sign_algo_to_scheme(H, S, T) - end. + {PublicKeyAlgo, SignAlgo, SignHash}. %% Note: copied from ssl_handshake +public_key_algo(?'id-RSASSA-PSS') -> + rsa_pss; public_key_algo(?rsaEncryption) -> rsa; public_key_algo(?'id-ecPublicKey') -> diff --git a/lib/ssl/src/tls_handshake_1_3.hrl b/lib/ssl/src/tls_handshake_1_3.hrl index 6ef5364399..7ae1b93e1c 100644 --- a/lib/ssl/src/tls_handshake_1_3.hrl +++ b/lib/ssl/src/tls_handshake_1_3.hrl @@ -191,7 +191,7 @@ %% case RawPublicKey: %% /* From RFC 7250 ASN.1_subjectPublicKeyInfo */ %% opaque ASN1_subjectPublicKeyInfo<1..2^24-1>; - + %% %% case X509: %% opaque cert_data<1..2^24-1>; %% }; @@ -200,9 +200,14 @@ -record(certificate_1_3, { certificate_request_context, % opaque certificate_request_context<0..2^8-1>; - entries % CertificateEntry certificate_list<0..2^24-1>; + certificate_list % CertificateEntry certificate_list<0..2^24-1>; }). +-record(certificate_verify_1_3, { + algorithm, % SignatureScheme + signature % signature<0..2^16-1> + }). + %% RFC 8446 B.3.4. Ticket Establishment -record(new_session_ticket, { ticket_lifetime, %unit32 @@ -223,4 +228,11 @@ request_update }). +-type tls_handshake_1_3() :: #encrypted_extensions{} | + #certificate_request_1_3{} | + #certificate_1_3{} | + #certificate_verify_1_3{}. + +-export_type([tls_handshake_1_3/0]). + -endif. % -ifdef(tls_handshake_1_3). diff --git a/lib/ssl/src/tls_record_1_3.erl b/lib/ssl/src/tls_record_1_3.erl index d424336187..1681babed9 100644 --- a/lib/ssl/src/tls_record_1_3.erl +++ b/lib/ssl/src/tls_record_1_3.erl @@ -76,8 +76,8 @@ encode_data(Frag, ConnectionStates) -> encode_plain_text(Type, Data0, #{current_write := Write0} = ConnectionStates) -> PadLen = 0, %% TODO where to specify PadLen? Data = inner_plaintext(Type, Data0, PadLen), - {CipherFragment, Write1} = encode_plain_text(Data, Write0), - {CipherText, Write} = encode_tls_cipher_text(CipherFragment, Write1), + CipherFragment = encode_plain_text(Data, Write0), + {CipherText, Write} = encode_tls_cipher_text(CipherFragment, Write0), {CipherText, ConnectionStates#{current_write => Write}}. encode_iolist(Type, Data, ConnectionStates0) -> @@ -105,24 +105,23 @@ decode_cipher_text(#ssl_tls{type = ?OPAQUE_TYPE, fragment = CipherFragment}, #{current_read := #{sequence_number := Seq, - cipher_state := CipherS0, + cipher_state := #cipher_state{key = Key, + iv = IV, + tag_len = TagLen}, security_parameters := #security_parameters{ cipher_type = ?AEAD, bulk_cipher_algorithm = BulkCipherAlgo} } = ReadState0} = ConnectionStates0) -> - AAD = start_additional_data(), - CipherS1 = ssl_cipher:nonce_seed(<<?UINT64(Seq)>>, CipherS0), - case decipher_aead(BulkCipherAlgo, CipherS1, AAD, CipherFragment) of - {PlainFragment, CipherS1} -> + case decipher_aead(CipherFragment, BulkCipherAlgo, Key, Seq, IV, TagLen) of + #alert{} = Alert -> + Alert; + PlainFragment -> ConnectionStates = ConnectionStates0#{current_read => - ReadState0#{cipher_state => CipherS1, - sequence_number => Seq + 1}}, - decode_inner_plaintext(PlainFragment, ConnectionStates); - #alert{} = Alert -> - Alert + ReadState0#{sequence_number => Seq + 1}}, + {decode_inner_plaintext(PlainFragment), ConnectionStates} end; decode_cipher_text(#ssl_tls{type = Type, version = ?LEGACY_VERSION, @@ -137,7 +136,7 @@ decode_cipher_text(#ssl_tls{type = Type, fragment = CipherFragment}, ConnnectionStates0}; decode_cipher_text(#ssl_tls{type = Type}, _) -> %% Version mismatch is already asserted - ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC, {record_typ_mismatch, Type}). + ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC, {record_type_mismatch, Type}). %%-------------------------------------------------------------------- %%% Internal functions @@ -170,62 +169,61 @@ encode_plain_text(#inner_plaintext{ content = Data, type = Type, zeros = Zeros - }, #{cipher_state := CipherS0, + }, #{cipher_state := #cipher_state{key= Key, + iv = IV, + tag_len = TagLen}, sequence_number := Seq, security_parameters := #security_parameters{ - cipher_type = ?AEAD} - } = WriteState0) -> - PlainText = <<Data/binary, ?BYTE(Type), Zeros/binary>>, - AAD = start_additional_data(), - CipherS1 = ssl_cipher:nonce_seed(<<?UINT64(Seq)>>, CipherS0), - {Encoded, WriteState} = cipher_aead(PlainText, WriteState0#{cipher_state => CipherS1}, AAD), - {#tls_cipher_text{opaque_type = Type, - legacy_version = {3,3}, - encoded_record = Encoded}, WriteState}; + cipher_type = ?AEAD, + bulk_cipher_algorithm = BulkCipherAlgo} + }) -> + PlainText = [Data, Type, Zeros], + Encoded = cipher_aead(PlainText, BulkCipherAlgo, Key, Seq, IV, TagLen), + #tls_cipher_text{opaque_type = 23, %% 23 (application_data) for outward compatibility + legacy_version = {3,3}, + encoded_record = Encoded}; encode_plain_text(#inner_plaintext{ content = Data, type = Type }, #{security_parameters := #security_parameters{ cipher_suite = ?TLS_NULL_WITH_NULL_NULL} - } = WriteState0) -> + }) -> %% RFC8446 - 5.1. Record Layer %% When record protection has not yet been engaged, TLSPlaintext %% structures are written directly onto the wire. - {#tls_cipher_text{opaque_type = Type, + #tls_cipher_text{opaque_type = Type, legacy_version = {3,3}, - encoded_record = Data}, WriteState0}; + encoded_record = Data}; encode_plain_text(_, CS) -> exit({cs, CS}). -start_additional_data() -> - {MajVer, MinVer} = ?LEGACY_VERSION, - <<?BYTE(?OPAQUE_TYPE), ?BYTE(MajVer), ?BYTE(MinVer)>>. - -end_additional_data(AAD, Len) -> - <<AAD/binary, ?UINT16(Len)>>. - -nonce(#cipher_state{nonce = Nonce, iv = IV}) -> - Len = size(IV), - crypto:exor(<<Nonce:Len/bytes>>, IV). +additional_data(Length) -> + <<?BYTE(?OPAQUE_TYPE), ?BYTE(3), ?BYTE(3),?UINT16(Length)>>. -cipher_aead(Fragment, - #{cipher_state := CipherS0, - security_parameters := - #security_parameters{bulk_cipher_algorithm = - BulkCipherAlgo} - } = WriteState0, AAD) -> - {CipherFragment, CipherS1} = - cipher_aead(BulkCipherAlgo, CipherS0, AAD, Fragment), - {CipherFragment, WriteState0#{cipher_state => CipherS1}}. +%% The per-record nonce for the AEAD construction is formed as +%% follows: +%% +%% 1. The 64-bit record sequence number is encoded in network byte +%% order and padded to the left with zeros to iv_length. +%% +%% 2. The padded sequence number is XORed with either the static +%% client_write_iv or server_write_iv (depending on the role). +%% +%% The resulting quantity (of length iv_length) is used as the +%% per-record nonce. +nonce(Seq, IV) -> + Padding = binary:copy(<<0>>, byte_size(IV) - 8), + crypto:exor(<<Padding/binary,?UINT64(Seq)>>, IV). -cipher_aead(Type, #cipher_state{key=Key} = CipherState, AAD0, Fragment) -> - AAD = end_additional_data(AAD0, erlang:iolist_size(Fragment)), - Nonce = nonce(CipherState), - {Content, CipherTag} = ssl_cipher:aead_encrypt(Type, Key, Nonce, Fragment, AAD), - {<<Content/binary, CipherTag/binary>>, CipherState}. +cipher_aead(Fragment, BulkCipherAlgo, Key, Seq, IV, TagLen) -> + AAD = additional_data(erlang:iolist_size(Fragment) + TagLen), + Nonce = nonce(Seq, IV), + {Content, CipherTag} = + ssl_cipher:aead_encrypt(BulkCipherAlgo, Key, Nonce, Fragment, AAD), + <<Content/binary, CipherTag/binary>>. encode_tls_cipher_text(#tls_cipher_text{opaque_type = Type, legacy_version = {MajVer, MinVer}, @@ -234,13 +232,14 @@ encode_tls_cipher_text(#tls_cipher_text{opaque_type = Type, {[<<?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer), ?UINT16(Length)>>, Encoded], Write#{sequence_number => Seq +1}}. -decipher_aead(Type, #cipher_state{key = Key} = CipherState, AAD0, CipherFragment) -> +decipher_aead(CipherFragment, BulkCipherAlgo, Key, Seq, IV, TagLen) -> try - Nonce = nonce(CipherState), - {AAD, CipherText, CipherTag} = aead_ciphertext_split(CipherState, CipherFragment, AAD0), - case ssl_cipher:aead_decrypt(Type, Key, Nonce, CipherText, CipherTag, AAD) of + AAD = additional_data(erlang:iolist_size(CipherFragment)), + Nonce = nonce(Seq, IV), + {CipherText, CipherTag} = aead_ciphertext_split(CipherFragment, TagLen), + case ssl_cipher:aead_decrypt(BulkCipherAlgo, Key, Nonce, CipherText, CipherTag, AAD) of Content when is_binary(Content) -> - {Content, CipherState}; + Content; _ -> ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC, decryption_failed) end @@ -249,39 +248,34 @@ decipher_aead(Type, #cipher_state{key = Key} = CipherState, AAD0, CipherFragment ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC, decryption_failed) end. -aead_ciphertext_split(#cipher_state{tag_len = Len}, CipherTextFragment, AAD) -> - CipherLen = size(CipherTextFragment) - Len, - <<CipherText:CipherLen/bytes, CipherTag:Len/bytes>> = CipherTextFragment, - {end_additional_data(AAD, CipherLen), CipherText, CipherTag}. -decode_inner_plaintext(PlainText, ConnnectionStates) -> - case remove_padding(PlainText) of - #alert{} = Alert -> - Alert; - {Data, Type} -> - {#ssl_tls{type = Type, - version = {3,4}, %% Internally use real version - fragment = Data}, ConnnectionStates} - end. +aead_ciphertext_split(CipherTextFragment, TagLen) + when is_binary(CipherTextFragment) -> + CipherLen = erlang:byte_size(CipherTextFragment) - TagLen, + <<CipherText:CipherLen/bytes, CipherTag:TagLen/bytes>> = CipherTextFragment, + {CipherText, CipherTag}; +aead_ciphertext_split(CipherTextFragment, TagLen) + when is_list(CipherTextFragment) -> + CipherLen = erlang:iolist_size(CipherTextFragment) - TagLen, + <<CipherText:CipherLen/bytes, CipherTag:TagLen/bytes>> = + erlang:iolist_to_binary(CipherTextFragment), + {CipherText, CipherTag}. -remove_padding(PlainText)-> - case binary:split(PlainText, <<0>>, [global, trim]) of - [] -> - ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE, padding_error); - [Content] -> - Type = binary:last(Content), - split_content(Type, Content, erlang:byte_size(Content) - 1) +decode_inner_plaintext(PlainText) -> + case binary:last(PlainText) of + 0 -> + decode_inner_plaintext(init_binary(PlainText)); + Type when Type =:= ?APPLICATION_DATA orelse + Type =:= ?HANDSHAKE orelse + Type =:= ?ALERT -> + #ssl_tls{type = Type, + version = {3,4}, %% Internally use real version + fragment = init_binary(PlainText)}; + _Else -> + ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE, empty_alert) end. -split_content(?HANDSHAKE, _, 0) -> - ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE, empty_handshake); -split_content(?ALERT, _, 0) -> - ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE, empty_alert); -%% For special middlebox compatible case! -split_content(?CHANGE_CIPHER_SPEC, _, 0) -> - ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE, empty_change_cipher_spec); -split_content(?APPLICATION_DATA = Type, _, 0) -> - {Type, <<>>}; -split_content(Type, Content, N) -> - <<Data:N/bytes, ?BYTE(Type)>> = Content, - {Type, Data}. +init_binary(B) -> + {Init, _} = + split_binary(B, byte_size(B) - 1), + Init. diff --git a/lib/ssl/src/tls_sender.erl b/lib/ssl/src/tls_sender.erl index 4399999221..1559fcbb37 100644 --- a/lib/ssl/src/tls_sender.erl +++ b/lib/ssl/src/tls_sender.erl @@ -29,7 +29,7 @@ %% API -export([start/0, start/1, initialize/2, send_data/2, send_alert/2, - send_and_ack_alert/2, setopts/2, renegotiate/1, downgrade/2, + send_and_ack_alert/2, setopts/2, renegotiate/1, peer_renegotiate/1, downgrade/2, update_connection_state/3, dist_tls_socket/1, dist_handshake_complete/3]). %% gen_statem callbacks @@ -119,6 +119,15 @@ setopts(Pid, Opts) -> renegotiate(Pid) -> %% Needs error handling for external API call(Pid, renegotiate). + +%%-------------------------------------------------------------------- +-spec peer_renegotiate(pid()) -> {ok, WriteState::map()} | {error, term()}. +%% Description: So TLS connection process can synchronize the +%% encryption state to be used when handshaking. +%%-------------------------------------------------------------------- +peer_renegotiate(Pid) -> + gen_statem:call(Pid, renegotiate, ?DEFAULT_TIMEOUT). + %%-------------------------------------------------------------------- -spec update_connection_state(pid(), WriteState::map(), tls_record:tls_version()) -> ok. %% Description: So TLS connection process can synchronize the diff --git a/lib/ssl/src/tls_v1.erl b/lib/ssl/src/tls_v1.erl index 83dd7585dd..df2a421bce 100644 --- a/lib/ssl/src/tls_v1.erl +++ b/lib/ssl/src/tls_v1.erl @@ -36,7 +36,15 @@ default_signature_schemes/1, signature_schemes/2, groups/1, groups/2, group_to_enum/1, enum_to_group/1, default_groups/1]). --export([derive_secret/4, hkdf_expand_label/5, hkdf_extract/3, hkdf_expand/4]). +-export([derive_secret/4, hkdf_expand_label/5, hkdf_extract/3, hkdf_expand/4, + key_schedule/3, key_schedule/4, + external_binder_key/2, resumption_binder_key/2, + client_early_traffic_secret/3, early_exporter_master_secret/3, + client_handshake_traffic_secret/3, server_handshake_traffic_secret/3, + client_application_traffic_secret_0/3, server_application_traffic_secret_0/3, + exporter_master_secret/3, resumption_master_secret/3, + update_traffic_secret/2, calculate_traffic_keys/3, + transcript_hash/2]). -type named_curve() :: sect571r1 | sect571k1 | secp521r1 | brainpoolP512r1 | sect409k1 | sect409r1 | brainpoolP384r1 | secp384r1 | @@ -56,7 +64,7 @@ %% TLS 1.3 --------------------------------------------------- -spec derive_secret(Secret::binary(), Label::binary(), - Messages::binary(), Algo::ssl_cipher_format:hash()) -> Key::binary(). + Messages::iodata(), Algo::ssl_cipher_format:hash()) -> Key::binary(). derive_secret(Secret, Label, Messages, Algo) -> Hash = crypto:hash(mac_algo(Algo), Messages), hkdf_expand_label(Secret, Label, @@ -71,11 +79,14 @@ hkdf_expand_label(Secret, Label0, Context, Length, Algo) -> %% opaque label<7..255> = "tls13 " + Label; %% opaque context<0..255> = Context; %% } HkdfLabel; - Content = << <<"tls13">>/binary, Label0/binary, Context/binary>>, + Label1 = << <<"tls13 ">>/binary, Label0/binary>>, + LLen = size(Label1), + Label = <<?BYTE(LLen), Label1/binary>>, + Content = <<Label/binary, Context/binary>>, Len = size(Content), HkdfLabel = <<?UINT16(Len), Content/binary>>, hkdf_expand(Secret, HkdfLabel, Length, Algo). - + -spec hkdf_extract(MacAlg::ssl_cipher_format:hash(), Salt::binary(), KeyingMaterial::binary()) -> PseudoRandKey::binary(). @@ -89,6 +100,12 @@ hkdf_extract(MacAlg, Salt, KeyingMaterial) -> hkdf_expand(PseudoRandKey, ContextInfo, Length, Algo) -> Iterations = erlang:ceil(Length / ssl_cipher:hash_size(Algo)), hkdf_expand(Algo, PseudoRandKey, ContextInfo, Length, 1, Iterations, <<>>, <<>>). + + +-spec transcript_hash(Messages::iodata(), Algo::ssl_cipher_format:hash()) -> Hash::binary(). + +transcript_hash(Messages, Algo) -> + crypto:hash(mac_algo(Algo), Messages). %% TLS 1.3 --------------------------------------------------- %% TLS 1.0 -1.2 --------------------------------------------------- @@ -235,6 +252,153 @@ setup_keys(Version, PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize, ServerWriteKey, ClientIV, ServerIV}. %% TLS v1.2 --------------------------------------------------- +%% TLS v1.3 --------------------------------------------------- +%% RFC 8446 - 7.1. Key Schedule +%% +%% 0 +%% | +%% v +%% PSK -> HKDF-Extract = Early Secret +%% | +%% +-----> Derive-Secret(., "ext binder" | "res binder", "") +%% | = binder_key +%% | +%% +-----> Derive-Secret(., "c e traffic", ClientHello) +%% | = client_early_traffic_secret +%% | +%% +-----> Derive-Secret(., "e exp master", ClientHello) +%% | = early_exporter_master_secret +%% v +%% Derive-Secret(., "derived", "") +%% | +%% v +%% (EC)DHE -> HKDF-Extract = Handshake Secret +%% | +%% +-----> Derive-Secret(., "c hs traffic", +%% | ClientHello...ServerHello) +%% | = client_handshake_traffic_secret +%% | +%% +-----> Derive-Secret(., "s hs traffic", +%% | ClientHello...ServerHello) +%% | = server_handshake_traffic_secret +%% v +%% Derive-Secret(., "derived", "") +%% | +%% v +%% 0 -> HKDF-Extract = Master Secret +%% | +%% +-----> Derive-Secret(., "c ap traffic", +%% | ClientHello...server Finished) +%% | = client_application_traffic_secret_0 +%% | +%% +-----> Derive-Secret(., "s ap traffic", +%% | ClientHello...server Finished) +%% | = server_application_traffic_secret_0 +%% | +%% +-----> Derive-Secret(., "exp master", +%% | ClientHello...server Finished) +%% | = exporter_master_secret +%% | +%% +-----> Derive-Secret(., "res master", +%% ClientHello...client Finished) +%% = resumption_master_secret +-spec key_schedule(early_secret | handshake_secret | master_secret, + atom(), {psk | early_secret | handshake_secret, binary()}) -> + {early_secret | handshake_secret | master_secret, binary()}. + +key_schedule(early_secret, Algo, {psk, PSK}) -> + Len = ssl_cipher:hash_size(Algo), + Salt = binary:copy(<<?BYTE(0)>>, Len), + {early_secret, hkdf_extract(Algo, Salt, PSK)}; +key_schedule(master_secret, Algo, {handshake_secret, Secret}) -> + Len = ssl_cipher:hash_size(Algo), + IKM = binary:copy(<<?BYTE(0)>>, Len), + Salt = derive_secret(Secret, <<"derived">>, <<>>, Algo), + {master_secret, hkdf_extract(Algo, Salt, IKM)}. +%% +key_schedule(handshake_secret, Algo, IKM, {early_secret, Secret}) -> + Salt = derive_secret(Secret, <<"derived">>, <<>>, Algo), + {handshake_secret, hkdf_extract(Algo, Salt, IKM)}. + +-spec external_binder_key(atom(), {early_secret, binary()}) -> binary(). +external_binder_key(Algo, {early_secret, Secret}) -> + derive_secret(Secret, <<"ext binder">>, <<>>, Algo). + +-spec resumption_binder_key(atom(), {early_secret, binary()}) -> binary(). +resumption_binder_key(Algo, {early_secret, Secret}) -> + derive_secret(Secret, <<"res binder">>, <<>>, Algo). + +-spec client_early_traffic_secret(atom(), {early_secret, binary()}, iodata()) -> binary(). +%% M = ClientHello +client_early_traffic_secret(Algo, {early_secret, Secret}, M) -> + derive_secret(Secret, <<"c e traffic">>, M, Algo). + +-spec early_exporter_master_secret(atom(), {early_secret, binary()}, iodata()) -> binary(). +%% M = ClientHello +early_exporter_master_secret(Algo, {early_secret, Secret}, M) -> + derive_secret(Secret, <<"e exp master">>, M, Algo). + +-spec client_handshake_traffic_secret(atom(), {handshake_secret, binary()}, iodata()) -> binary(). +%% M = ClientHello...ServerHello +client_handshake_traffic_secret(Algo, {handshake_secret, Secret}, M) -> + derive_secret(Secret, <<"c hs traffic">>, M, Algo). + +-spec server_handshake_traffic_secret(atom(), {handshake_secret, binary()}, iodata()) -> binary(). +%% M = ClientHello...ServerHello +server_handshake_traffic_secret(Algo, {handshake_secret, Secret}, M) -> + derive_secret(Secret, <<"s hs traffic">>, M, Algo). + +-spec client_application_traffic_secret_0(atom(), {master_secret, binary()}, iodata()) -> binary(). +%% M = ClientHello...server Finished +client_application_traffic_secret_0(Algo, {master_secret, Secret}, M) -> + derive_secret(Secret, <<"c ap traffic">>, M, Algo). + +-spec server_application_traffic_secret_0(atom(), {master_secret, binary()}, iodata()) -> binary(). +%% M = ClientHello...server Finished +server_application_traffic_secret_0(Algo, {master_secret, Secret}, M) -> + derive_secret(Secret, <<"s ap traffic">>, M, Algo). + +-spec exporter_master_secret(atom(), {master_secret, binary()}, iodata()) -> binary(). +%% M = ClientHello...server Finished +exporter_master_secret(Algo, {master_secret, Secret}, M) -> + derive_secret(Secret, <<"exp master">>, M, Algo). + +-spec resumption_master_secret(atom(), {master_secret, binary()}, iodata()) -> binary(). +%% M = ClientHello...client Finished +resumption_master_secret(Algo, {master_secret, Secret}, M) -> + derive_secret(Secret, <<"res master">>, M, Algo). + +%% The next-generation application_traffic_secret is computed as: +%% +%% application_traffic_secret_N+1 = +%% HKDF-Expand-Label(application_traffic_secret_N, +%% "traffic upd", "", Hash.length) +-spec update_traffic_secret(atom(), binary()) -> binary(). +update_traffic_secret(Algo, Secret) -> + hkdf_expand_label(Secret, <<"traffic upd">>, <<>>, ssl_cipher:hash_size(Algo), Algo). + +%% The traffic keying material is generated from the following input +%% values: +%% +%% - A secret value +%% +%% - A purpose value indicating the specific value being generated +%% +%% - The length of the key being generated +%% +%% The traffic keying material is generated from an input traffic secret +%% value using: +%% +%% [sender]_write_key = HKDF-Expand-Label(Secret, "key", "", key_length) +%% [sender]_write_iv = HKDF-Expand-Label(Secret, "iv", "", iv_length) +-spec calculate_traffic_keys(atom(), atom(), binary()) -> {binary(), binary()}. +calculate_traffic_keys(HKDFAlgo, Cipher, Secret) -> + Key = hkdf_expand_label(Secret, <<"key">>, <<>>, ssl_cipher:key_material(Cipher), HKDFAlgo), + IV = hkdf_expand_label(Secret, <<"iv">>, <<>>, ssl_cipher:key_material(Cipher), HKDFAlgo), + {Key, IV}. + +%% TLS v1.3 --------------------------------------------------- + %% TLS 1.0 -1.2 --------------------------------------------------- -spec mac_hash(integer() | atom(), binary(), integer(), integer(), tls_record:tls_version(), integer(), binary()) -> binary(). @@ -254,7 +418,7 @@ mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor}, %% TODO 1.3 same as above? --spec suites(1|2|3|4) -> [ssl_cipher_format:cipher_suite()]. +-spec suites(1|2|3|4|'TLS_v1.3') -> [ssl_cipher_format:cipher_suite()]. suites(Minor) when Minor == 1; Minor == 2 -> [ @@ -315,7 +479,17 @@ suites(4) -> %% Not supported %% ?TLS_AES_128_CCM_SHA256, %% ?TLS_AES_128_CCM_8_SHA256 - ] ++ suites(3). + ] ++ suites(3); + +suites('TLS_v1.3') -> + [?TLS_AES_256_GCM_SHA384, + ?TLS_AES_128_GCM_SHA256, + ?TLS_CHACHA20_POLY1305_SHA256 + %% Not supported + %% ?TLS_AES_128_CCM_SHA256, + %% ?TLS_AES_128_CCM_8_SHA256 + ]. + signature_algs({3, 4}, HashSigns) -> signature_algs({3, 3}, HashSigns); @@ -347,7 +521,9 @@ signature_algs({3, 3}, HashSigns) -> lists:reverse(Supported). default_signature_algs({3, 4} = Version) -> - default_signature_schemes(Version); + %% TLS 1.3 servers shall be prepared to process TLS 1.2 ClientHellos + %% containing legacy hash-sign tuples. + default_signature_schemes(Version) ++ default_signature_algs({3,3}); default_signature_algs({3, 3} = Version) -> Default = [%% SHA2 {sha512, ecdsa}, @@ -373,15 +549,23 @@ signature_schemes(Version, SignatureSchemes) when is_tuple(Version) Hashes = proplists:get_value(hashs, CryptoSupports), PubKeys = proplists:get_value(public_keys, CryptoSupports), Curves = proplists:get_value(curves, CryptoSupports), - Fun = fun (Scheme, Acc) -> + RSAPSSSupported = lists:member(rsa_pkcs1_pss_padding, + proplists:get_value(rsa_opts, CryptoSupports)), + Fun = fun (Scheme, Acc) when is_atom(Scheme) -> {Hash0, Sign0, Curve} = ssl_cipher:scheme_to_components(Scheme), Sign = case Sign0 of - rsa_pkcs1 -> rsa; + rsa_pkcs1 -> + rsa; + rsa_pss_rsae when RSAPSSSupported -> + rsa; + rsa_pss_pss when RSAPSSSupported -> + rsa; S -> S end, Hash = case Hash0 of - sha1 -> sha; + sha1 -> + sha; H -> H end, case proplists:get_bool(Sign, PubKeys) @@ -394,7 +578,10 @@ signature_schemes(Version, SignatureSchemes) when is_tuple(Version) [Scheme | Acc]; false -> Acc - end + end; + %% Special clause for filtering out the legacy hash-sign tuples. + (_ , Acc) -> + Acc end, Supported = lists:foldl(Fun, [], SignatureSchemes), lists:reverse(Supported); @@ -403,22 +590,29 @@ signature_schemes(_, _) -> default_signature_schemes(Version) -> Default = [ - rsa_pkcs1_sha256, - rsa_pkcs1_sha384, - rsa_pkcs1_sha512, - ecdsa_secp256r1_sha256, - ecdsa_secp384r1_sha384, ecdsa_secp521r1_sha512, - rsa_pss_rsae_sha256, - rsa_pss_rsae_sha384, + ecdsa_secp384r1_sha384, + ecdsa_secp256r1_sha256, + rsa_pss_pss_sha512, + rsa_pss_pss_sha384, + rsa_pss_pss_sha256, rsa_pss_rsae_sha512, + rsa_pss_rsae_sha384, + rsa_pss_rsae_sha256, %% ed25519, %% ed448, - rsa_pss_pss_sha256, - rsa_pss_pss_sha384, - rsa_pss_pss_sha512, - rsa_pkcs1_sha1, - ecdsa_sha1 + + %% These values refer solely to signatures + %% which appear in certificates (see Section 4.4.2.2) and are not + %% defined for use in signed TLS handshake messages, although they + %% MAY appear in "signature_algorithms" and + %% "signature_algorithms_cert" for backward compatibility with + %% TLS 1.2. + rsa_pkcs1_sha512, + rsa_pkcs1_sha384, + rsa_pkcs1_sha256, + ecdsa_sha1, + rsa_pkcs1_sha1 ], signature_schemes(Version, Default). @@ -553,7 +747,9 @@ ecc_curves(_Minor, TLSCurves) -> -spec groups(4 | all | default) -> [group()]. groups(all) -> - [secp256r1, + [x25519, + x448, + secp256r1, secp384r1, secp521r1, ffdhe2048, @@ -562,27 +758,33 @@ groups(all) -> ffdhe6144, ffdhe8192]; groups(default) -> - [secp256r1, - secp384r1, - secp521r1, - ffdhe2048]; + [x25519, + x448, + secp256r1, + secp384r1]; groups(Minor) -> TLSGroups = groups(all), groups(Minor, TLSGroups). %% -spec groups(4, [group()]) -> [group()]. groups(_Minor, TLSGroups) -> - %% TODO: Adding FFDHE groups to crypto? - CryptoGroups = crypto:ec_curves() ++ [ffdhe2048,ffdhe3072,ffdhe4096,ffdhe6144,ffdhe8192], + CryptoGroups = supported_groups(), lists:filter(fun(Group) -> proplists:get_bool(Group, CryptoGroups) end, TLSGroups). default_groups(Minor) -> TLSGroups = groups(default), groups(Minor, TLSGroups). +supported_groups() -> + %% TODO: Add new function to crypto? + proplists:get_value(curves, crypto:supports()) ++ + [ffdhe2048,ffdhe3072,ffdhe4096,ffdhe6144,ffdhe8192]. + group_to_enum(secp256r1) -> 23; group_to_enum(secp384r1) -> 24; group_to_enum(secp521r1) -> 25; +group_to_enum(x25519) -> 29; +group_to_enum(x448) -> 30; group_to_enum(ffdhe2048) -> 256; group_to_enum(ffdhe3072) -> 257; group_to_enum(ffdhe4096) -> 258; @@ -592,6 +794,8 @@ group_to_enum(ffdhe8192) -> 260. enum_to_group(23) -> secp256r1; enum_to_group(24) -> secp384r1; enum_to_group(25) -> secp521r1; +enum_to_group(29) -> x25519; +enum_to_group(30) -> x448; enum_to_group(256) -> ffdhe2048; enum_to_group(257) -> ffdhe3072; enum_to_group(258) -> ffdhe4096; diff --git a/lib/ssl/test/make_certs.erl b/lib/ssl/test/make_certs.erl index 578f6a731a..76bf0fa895 100644 --- a/lib/ssl/test/make_certs.erl +++ b/lib/ssl/test/make_certs.erl @@ -189,6 +189,18 @@ gencrl(Root, CA, C, CrlHours) -> Env = [{"ROOTDIR", filename:absname(Root)}], cmd(Cmd, Env). +%% This function sets the number of seconds until the next CRL is due. +gencrl_sec(Root, CA, C, CrlSecs) -> + CACnfFile = filename:join([Root, CA, "ca.cnf"]), + CACRLFile = filename:join([Root, CA, "crl.pem"]), + Cmd = [C#config.openssl_cmd, " ca" + " -gencrl ", + " -crlsec ", integer_to_list(CrlSecs), + " -out ", CACRLFile, + " -config ", CACnfFile], + Env = [{"ROOTDIR", filename:absname(Root)}], + cmd(Cmd, Env). + can_generate_expired_crls(C) -> %% OpenSSL can generate CRLs with an expiration date in the past, %% if we pass a negative number for -crlhours. However, LibreSSL diff --git a/lib/ssl/test/property_test/ssl_eqc_handshake.erl b/lib/ssl/test/property_test/ssl_eqc_handshake.erl index 6ffb6d311f..e4c4c89021 100644 --- a/lib/ssl/test/property_test/ssl_eqc_handshake.erl +++ b/lib/ssl/test/property_test/ssl_eqc_handshake.erl @@ -160,7 +160,7 @@ certificate_1_3() -> ?LET(Certs, certificate_chain(), #certificate_1_3{ certificate_request_context = certificate_request_context(), - entries = certificate_entries(Certs, []) + certificate_list = certificate_entries(Certs, []) }). finished() -> @@ -326,7 +326,7 @@ extensions(?'TLS_v1.3' = Version, client_hello) -> %% oneof([psk_key_exchange_modes(), undefined]), %% oneof([early_data(), undefined]), %% oneof([cookie(), undefined]), - oneof([client_hello_versions(Version), undefined]), + oneof([client_hello_versions(Version)]), %% oneof([cert_authorities(), undefined]), %% oneof([post_handshake_auth(), undefined]), oneof([signature_algs_cert(), undefined]) @@ -403,7 +403,7 @@ extensions(?'TLS_v1.3' = Version, server_hello) -> { oneof([key_share(server_hello), undefined]), %% oneof([pre_shared_keys(), undefined]), - oneof([server_hello_selected_version(), undefined]) + oneof([server_hello_selected_version()]) }, maps:filter(fun(_, undefined) -> false; @@ -514,7 +514,9 @@ sig_scheme_list() -> client_hello_versions(?'TLS_v1.3') -> ?LET(SupportedVersions, oneof([[{3,4}], - [{3,3},{3,4}], + %% This list breaks the property but can be used for negative tests + %% [{3,3},{3,4}], + [{3,4},{3,3}], [{3,4},{3,3},{3,2},{3,1},{3,0}] ]), #client_hello_versions{versions = SupportedVersions}); @@ -543,7 +545,10 @@ choose_certificate_chain(#{server_config := ServerConf, oneof([certificate_chain(ServerConf), certificate_chain(ClientConf)]). certificate_request_context() -> - <<>>. + oneof([<<>>, + <<1>>, + <<"foobar">> + ]). certificate_entries([], Acc) -> lists:reverse(Acc); certificate_entries([Cert | Rest], Acc) -> diff --git a/lib/ssl/test/ssl.spec b/lib/ssl/test/ssl.spec index 5e65bfcfe6..24272327c3 100644 --- a/lib/ssl/test/ssl.spec +++ b/lib/ssl/test/ssl.spec @@ -3,6 +3,7 @@ {suites,dir,all}. {skip_groups,dir,ssl_bench_SUITE,setup,"Benchmarks run separately"}. +{skip_groups,dir,ssl_bench_SUITE,payload,"Benchmarks run separately"}. {skip_groups,dir,ssl_bench_SUITE,pem_cache,"Benchmarks run separately"}. {skip_groups,dir,ssl_dist_bench_SUITE,setup,"Benchmarks run separately"}. {skip_groups,dir,ssl_dist_bench_SUITE,throughput,"Benchmarks run separately"}. diff --git a/lib/ssl/test/ssl_alpn_handshake_SUITE.erl b/lib/ssl/test/ssl_alpn_handshake_SUITE.erl index 04c4b257d9..7f7c3da5ab 100644 --- a/lib/ssl/test/ssl_alpn_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_alpn_handshake_SUITE.erl @@ -262,52 +262,12 @@ client_renegotiate(Config) when is_list(Config) -> %-------------------------------------------------------------------------------- session_reused(Config) when is_list(Config)-> - ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), ClientOpts = [{alpn_advertised_protocols, [<<"http/1.0">>]}] ++ ClientOpts0, ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), ServerOpts = [{alpn_preferred_protocols, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}] ++ ServerOpts0, - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {options, ServerOpts}]), - - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, no_result_msg, []}}, - {options, ClientOpts}]), - - SessionInfo = - receive - {Server, Info} -> - Info - end, - - Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, - - %% Make sure session is registered - ct:sleep(?SLEEP), - - Client1 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {from, self()}, {options, ClientOpts}]), - - receive - {Client1, SessionInfo} -> - ok; - {Client1, Other} -> - ct:fail(Other) - end, - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - ssl_test_lib:close(Client1). - + ssl_test_lib:reuse_session(ClientOpts, ServerOpts, Config). %-------------------------------------------------------------------------------- alpn_not_supported_client(Config) when is_list(Config) -> diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index 8e82757553..b8a8ef1d73 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -53,7 +53,8 @@ all() -> {group, options_tls}, {group, session}, {group, 'dtlsv1.2'}, - {group, 'dtlsv1'}, + {group, 'dtlsv1'}, + {group, 'tlsv1.3'}, {group, 'tlsv1.2'}, {group, 'tlsv1.1'}, {group, 'tlsv1'}, @@ -67,6 +68,7 @@ groups() -> {options_tls, [], options_tests_tls()}, {'dtlsv1.2', [], all_versions_groups()}, {'dtlsv1', [], all_versions_groups()}, + {'tlsv1.3', [], tls13_test_group()}, {'tlsv1.2', [], all_versions_groups() ++ tls_versions_groups() ++ [conf_signature_algs, no_common_signature_algs]}, {'tlsv1.1', [], all_versions_groups() ++ tls_versions_groups()}, {'tlsv1', [], all_versions_groups() ++ tls_versions_groups() ++ rizzo_tests()}, @@ -266,6 +268,12 @@ rizzo_tests() -> rizzo_zero_n, rizzo_disabled]. +%% For testing TLS 1.3 features and possible regressions +tls13_test_group() -> + [tls13_enable_client_side, + tls13_enable_server_side, + tls_record_1_3_encode_decode]. + %%-------------------------------------------------------------------- init_per_suite(Config0) -> catch crypto:stop(), @@ -295,7 +303,8 @@ init_per_group(GroupName, Config) when GroupName == basic_tls; GroupName == options; GroupName == basic; GroupName == session; - GroupName == error_handling_tests_tls + GroupName == error_handling_tests_tls; + GroupName == tls13_test_group -> ssl_test_lib:clean_tls_version(Config); init_per_group(GroupName, Config) -> @@ -654,8 +663,8 @@ new_options_in_accept(Config) when is_list(Config) -> handshake_continue() -> [{doc, "Test API function ssl:handshake_continue/3"}]. handshake_continue(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, @@ -714,7 +723,7 @@ hello_client_cancel(Config) when is_list(Config) -> hello_server_cancel() -> [{doc, "Test API function ssl:handshake_cancel/1 on the server side"}]. hello_server_cancel(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, @@ -756,8 +765,8 @@ prf(Config) when is_list(Config) -> secret_connection_info() -> [{doc,"Test the API function ssl:connection_information/2"}]. secret_connection_info(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, @@ -838,42 +847,30 @@ controlling_process(Config) when is_list(Config) -> ClientMsg = "Server hello", ServerMsg = "Client hello", - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, - controlling_process_result, [self(), - ServerMsg]}}, - {options, ServerOpts}]), + Server = ssl_test_lib:start_server([ + {node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, + controlling_process_result, [self(), + ServerMsg]}}, + {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, + {Client, CSocket} = ssl_test_lib:start_client([return_socket, + {node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, {mfa, {?MODULE, controlling_process_result, [self(), ClientMsg]}}, {options, ClientOpts}]), - + ct:log("Testcase ~p, Client ~p Server ~p ~n", - [self(), Client, Server]), + [self(), Client, Server]), - receive - {ssl, _, "S"} -> - receive_s_rizzo_duong_beast(); - {ssl, _, ServerMsg} -> - receive - {ssl, _, ClientMsg} -> - ok - end; - {ssl, _, "C"} -> - receive_c_rizzo_duong_beast(); - {ssl, _, ClientMsg} -> - receive - {ssl, _, ServerMsg} -> - ok - end; - Unexpected -> - ct:fail(Unexpected) - end, + ServerMsg = ssl_test_lib:active_recv(CSocket, length(ServerMsg)), + %% We do not have the TLS server socket but all messages form the client + %% socket are now read, so ramining are form the server socket + ClientMsg = ssl_active_recv(length(ClientMsg)), ssl_test_lib:close(Server), ssl_test_lib:close(Client). @@ -1458,8 +1455,8 @@ cipher_suites_mix() -> cipher_suites_mix(Config) when is_list(Config) -> CipherSuites = [{dhe_rsa,aes_128_cbc,sha256,sha256}, {dhe_rsa,aes_128_cbc,sha}], - ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -2370,8 +2367,8 @@ invalid_options() -> [{doc,"Test what happens when we give invalid options"}]. invalid_options(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Check = fun(Client, Server, {versions, [sslv2, sslv3]} = Option) -> @@ -2386,27 +2383,28 @@ invalid_options(Config) when is_list(Config) -> {error, {options, Option}}) end, - TestOpts = [{versions, [sslv2, sslv3]}, - {verify, 4}, - {verify_fun, function}, - {fail_if_no_peer_cert, 0}, - {verify_client_once, 1}, - {depth, four}, - {certfile, 'cert.pem'}, - {keyfile,'key.pem' }, - {password, foo}, - {cacertfile, ""}, - {dhfile,'dh.pem' }, - {ciphers, [{foo, bar, sha, ignore}]}, - {reuse_session, foo}, - {reuse_sessions, 0}, - {renegotiate_at, "10"}, - {mode, depech}, - {packet, 8.0}, - {packet_size, "2"}, - {header, a}, - {active, trice}, - {key, 'key.pem' }], + TestOpts = + [{versions, [sslv2, sslv3]}, + {verify, 4}, + {verify_fun, function}, + {fail_if_no_peer_cert, 0}, + {verify_client_once, 1}, + {depth, four}, + {certfile, 'cert.pem'}, + {keyfile,'key.pem' }, + {password, foo}, + {cacertfile, ""}, + {dhfile,'dh.pem' }, + {ciphers, [{foo, bar, sha, ignore}]}, + {reuse_session, foo}, + {reuse_sessions, 0}, + {renegotiate_at, "10"}, + {mode, depech}, + {packet, 8.0}, + {packet_size, "2"}, + {header, a}, + {active, trice}, + {key, 'key.pem' }], [begin Server = @@ -2699,175 +2697,69 @@ ciphers_ecdh_rsa_signed_certs_openssl_names(Config) when is_list(Config) -> reuse_session() -> [{doc,"Test reuse of sessions (short handshake)"}]. reuse_session(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client0 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, no_result, []}}, - {from, self()}, {options, ClientOpts}]), - SessionInfo = - receive - {Server, Info} -> - Info - end, - - Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), - %% Make sure session is registered - ct:sleep(?SLEEP), - - Client1 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {from, self()}, {options, ClientOpts}]), - receive - {Client1, SessionInfo} -> - ok; - {Client1, Other} -> - ct:log("Expected: ~p, Unexpected: ~p~n", - [SessionInfo, Other]), - ct:fail(session_not_reused) - end, - - Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, - - Client2 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {from, self()}, {options, [{reuse_sessions, false} - | ClientOpts]}]), - receive - {Client2, SessionInfo} -> - ct:fail( - session_reused_when_session_reuse_disabled_by_client); - {Client2, _} -> - ok - end, - - ssl_test_lib:close(Server), - - Server1 = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {options, [{reuse_sessions, false} | ServerOpts]}]), - - Port1 = ssl_test_lib:inet_port(Server1), - Client3 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port1}, {host, Hostname}, - {mfa, {ssl_test_lib, no_result, []}}, - {from, self()}, {options, ClientOpts}]), - - SessionInfo1 = - receive - {Server1, Info1} -> - Info1 - end, - - Server1 ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, - - %% Make sure session is registered - ct:sleep(?SLEEP), - - Client4 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port1}, {host, Hostname}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {from, self()}, {options, ClientOpts}]), - - receive - {Client4, SessionInfo1} -> - ct:fail( - session_reused_when_session_reuse_disabled_by_server); - {Client4, _Other} -> - ct:log("OTHER: ~p ~n", [_Other]), - ok - end, - - ssl_test_lib:close(Server1), - ssl_test_lib:close(Client0), - ssl_test_lib:close(Client1), - ssl_test_lib:close(Client2), - ssl_test_lib:close(Client3), - ssl_test_lib:close(Client4). - + ssl_test_lib:reuse_session(ClientOpts, ServerOpts, Config). %%-------------------------------------------------------------------- reuse_session_expired() -> [{doc,"Test sessions is not reused when it has expired"}]. reuse_session_expired(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + + Server0 = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client0 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, no_result, []}}, - {from, self()}, {options, ClientOpts}]), - SessionInfo = - receive - {Server, Info} -> - Info - end, - - Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, + {mfa, {ssl_test_lib, no_result, []}}, + {tcp_options, [{active, false}]}, + {options, ServerOpts}]), + Port0 = ssl_test_lib:inet_port(Server0), - %% Make sure session is registered - ct:sleep(?SLEEP), - - Client1 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {from, self()}, {options, ClientOpts}]), + Client0 = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port0}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, [{reuse_sessions, save} | ClientOpts]}]), + Server0 ! listen, + + Client1 = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port0}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, ClientOpts}]), + + SID = receive + {Client0, Id0} -> + Id0 + end, + receive - {Client1, SessionInfo} -> - ok; - {Client1, Other} -> - ct:log("Expected: ~p, Unexpected: ~p~n", - [SessionInfo, Other]), - ct:fail(session_not_reused) + {Client1, SID} -> + ok + after ?SLEEP -> + ct:fail(session_not_reused) end, - Server ! listen, - + Server0 ! listen, + %% Make sure session is unregistered due to expiration - ct:sleep((?EXPIRE+1)), - [{session_id, Id} |_] = SessionInfo, + ct:sleep((?EXPIRE*2)), - make_sure_expired(Hostname, Port, Id), + make_sure_expired(Hostname, Port0, SID), Client2 = ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, session_info_result, []}}, + {port, Port0}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, {from, self()}, {options, ClientOpts}]), receive - {Client2, SessionInfo} -> + {Client2, SID} -> ct:fail(session_reused_when_session_expired); {Client2, _} -> ok end, process_flag(trap_exit, false), - ssl_test_lib:close(Server), + ssl_test_lib:close(Server0), ssl_test_lib:close(Client0), ssl_test_lib:close(Client1), ssl_test_lib:close(Client2). @@ -2876,16 +2768,16 @@ make_sure_expired(Host, Port, Id) -> {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), [_, _,_, _, Prop] = StatusInfo, State = ssl_test_lib:state(Prop), - Cache = element(2, State), + ClientCache = element(2, State), - case ssl_session_cache:lookup(Cache, {{Host, Port}, Id}) of + case ssl_session_cache:lookup(ClientCache, {{Host, Port}, Id}) of undefined -> - ok; + ok; #session{is_resumable = false} -> - ok; + ok; _ -> ct:sleep(?SLEEP), - make_sure_expired(Host, Port, Id) + make_sure_expired(Host, Port, Id) end. %%-------------------------------------------------------------------- @@ -4137,6 +4029,8 @@ rizzo(Config) when is_list(Config) -> {cipher, fun(rc4_128) -> false; + (chacha20_poly1305) -> + false; (_) -> true end}]), @@ -4482,6 +4376,163 @@ accept_pool(Config) when is_list(Config) -> ssl_test_lib:close(Client1), ssl_test_lib:close(Client2). +%%-------------------------------------------------------------------- +%% TLS 1.3 +%%-------------------------------------------------------------------- + +tls13_enable_client_side() -> + [{doc,"Test that a TLS 1.3 client can connect to a TLS 1.2 server."}]. + +tls13_enable_client_side(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, protocol_info_result, []}}, + {options, [{versions, + ['tlsv1.1', 'tlsv1.2']} | ServerOpts] }]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, protocol_info_result, []}}, + {options, [{versions, + ['tlsv1.2', 'tlsv1.3']} | ClientOpts]}]), + + ServerMsg = ClientMsg = {ok, 'tlsv1.2'}, + ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg). + +tls13_enable_server_side() -> + [{doc,"Test that a TLS 1.2 client can connect to a TLS 1.3 server."}]. + +tls13_enable_server_side(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, protocol_info_result, []}}, + {options, [{versions, + ['tlsv1.2', 'tlsv1.3']} | ServerOpts] }]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, protocol_info_result, []}}, + {options, [{versions, + ['tlsv1.2', 'tlsv1.1']} | ClientOpts]}]), + + ServerMsg = ClientMsg = {ok, 'tlsv1.2'}, + ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg). + +tls_record_1_3_encode_decode() -> + [{doc,"Test TLS 1.3 record encode/decode functions"}]. + +tls_record_1_3_encode_decode(_Config) -> + ConnectionStates = + #{current_read => + #{beast_mitigation => one_n_minus_one, + cipher_state => + {cipher_state, + <<14,172,111,243,199,170,242,203,126,205,34,93,122,115,226,14, + 15,117,155,48,24,112,61,15,113,208,127,51,179,227,194,232>>, + <<197,54,168,218,54,91,157,58,30,201,197,142,51,58,53,231,228, + 131,57,122,170,78,82,196,30,48,23,16,95,255,185,236>>, + undefined,undefined,16}, + client_verify_data => undefined,compression_state => undefined, + mac_secret => undefined,secure_renegotiation => undefined, + security_parameters => + {security_parameters, + <<19,2>>, + 0,8,2,undefined,undefined,undefined,undefined,undefined, + sha384,undefined,undefined, + {handshake_secret, + <<128,229,186,211,62,127,182,20,62,166,233,23,135,64,121, + 3,104,251,214,161,253,31,3,2,232,37,8,221,189,72,64,218, + 121,41,112,148,254,34,68,164,228,60,161,201,132,55,56, + 157>>}, + undefined, + <<92,24,205,75,244,60,136,212,250,32,214,20,37,3,213,87,61,207, + 147,61,168,145,177,118,160,153,33,53,48,108,191,174>>, + undefined}, + sequence_number => 0,server_verify_data => undefined}, + current_write => + #{beast_mitigation => one_n_minus_one, + cipher_state => + {cipher_state, + <<14,172,111,243,199,170,242,203,126,205,34,93,122,115,226,14, + 15,117,155,48,24,112,61,15,113,208,127,51,179,227,194,232>>, + <<197,54,168,218,54,91,157,58,30,201,197,142,51,58,53,231,228, + 131,57,122,170,78,82,196,30,48,23,16,95,255,185,236>>, + undefined,undefined,16}, + client_verify_data => undefined,compression_state => undefined, + mac_secret => undefined,secure_renegotiation => undefined, + security_parameters => + {security_parameters, + <<19,2>>, + 0,8,2,undefined,undefined,undefined,undefined,undefined, + sha384,undefined,undefined, + {handshake_secret, + <<128,229,186,211,62,127,182,20,62,166,233,23,135,64,121, + 3,104,251,214,161,253,31,3,2,232,37,8,221,189,72,64,218, + 121,41,112,148,254,34,68,164,228,60,161,201,132,55,56, + 157>>}, + undefined, + <<92,24,205,75,244,60,136,212,250,32,214,20,37,3,213,87,61,207, + 147,61,168,145,177,118,160,153,33,53,48,108,191,174>>, + undefined}, + sequence_number => 0,server_verify_data => undefined}}, + + PlainText = [11, + <<0,2,175>>, + <<0,0,2,171,0,2,166,48,130,2,162,48,130,1,138,2,9,0,186,57,220,137,88,255, + 191,235,48,13,6,9,42,134,72,134,247,13,1,1,11,5,0,48,18,49,16,48,14,6,3,85, + 4,3,12,7,84,101,115,116,32,67,65,48,30,23,13,49,56,48,53,48,52,49,52,49,50, + 51,56,90,23,13,50,56,48,50,48,52,49,52,49,50,51,56,90,48,20,49,18,48,16,6, + 3,85,4,3,12,9,108,111,99,97,108,104,111,115,116,48,130,1,34,48,13,6,9,42, + 134,72,134,247,13,1,1,1,5,0,3,130,1,15,0,48,130,1,10,2,130,1,1,0,169,40, + 144,176,121,63,134,97,144,126,243,183,225,157,37,131,183,225,87,243,23,88, + 230,70,9,134,32,147,7,27,167,98,51,81,224,75,199,12,229,251,195,207,75,179, + 181,78,128,3,255,44,58,39,43,172,142,45,186,58,51,65,187,199,154,153,245, + 70,133,137,1,27,87,42,116,65,251,129,109,145,233,97,171,71,54,213,185,74, + 209,166,11,218,189,119,206,86,170,60,212,213,85,189,30,50,215,23,185,53, + 132,238,132,176,198,250,139,251,198,221,225,128,109,113,23,220,39,143,71, + 30,59,189,51,244,61,158,214,146,180,196,103,169,189,221,136,78,129,216,148, + 2,9,8,65,37,224,215,233,13,209,21,235,20,143,33,74,59,53,208,90,152,94,251, + 54,114,171,39,88,230,227,158,211,135,37,182,67,205,161,59,20,138,58,253,15, + 53,48,8,157,9,95,197,9,177,116,21,54,9,125,78,109,182,83,20,16,234,223,116, + 41,155,123,87,77,17,120,153,246,239,124,130,105,219,166,146,242,151,66,198, + 75,72,63,28,246,86,16,244,223,22,36,50,15,247,222,98,6,152,136,154,72,150, + 73,127,2,3,1,0,1,48,13,6,9,42,134,72,134,247,13,1,1,11,5,0,3,130,1,1,0,76, + 33,54,160,229,219,219,193,150,116,245,252,18,39,235,145,86,12,167,171,52, + 117,166,30,83,5,216,245,177,217,247,95,1,136,94,246,212,108,248,230,111, + 225,202,189,6,129,8,70,128,245,18,204,215,87,82,129,253,227,122,66,182,184, + 189,30,193,169,144,218,216,109,105,110,215,144,60,104,162,178,101,164,218, + 122,60,37,41,143,57,150,52,59,51,112,238,113,239,168,114,69,183,143,154,73, + 61,58,80,247,172,95,251,55,28,186,28,200,206,230,118,243,92,202,189,49,76, + 124,252,76,0,247,112,85,194,69,59,222,163,228,103,49,110,104,109,251,155, + 138,9,37,167,49,189,48,134,52,158,185,129,24,96,153,196,251,90,206,76,239, + 175,119,174,165,133,108,222,125,237,125,187,149,152,83,190,16,202,94,202, + 201,40,218,22,254,63,189,41,174,97,140,203,70,18,196,118,237,175,134,79,78, + 246,2,61,54,77,186,112,32,17,193,192,188,217,252,215,200,7,245,180,179,132, + 183,212,229,155,15,152,206,135,56,81,88,3,123,244,149,110,182,72,109,70,62, + 146,152,146,151,107,126,216,210,9,93,0,0>>], + + {[_Header|Encoded], _} = tls_record_1_3:encode_plain_text(22, PlainText, ConnectionStates), + CipherText = #ssl_tls{type = 23, version = {3,3}, fragment = Encoded}, + + {#ssl_tls{type = 22, version = {3,4}, fragment = DecodedText}, _} = + tls_record_1_3:decode_cipher_text(CipherText, ConnectionStates), + + DecodedText = iolist_to_binary(PlainText), + ct:log("Decoded: ~p ~n", [DecodedText]), + ok. + %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ @@ -4496,8 +4547,8 @@ tcp_send_recv_result(Socket) -> ok. basic_verify_test_no_close(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -4640,19 +4691,24 @@ recv_close(Socket) -> send_recv_result_active_rizzo(Socket) -> ssl:send(Socket, "Hello world"), - receive - {ssl, Socket, "H"} -> - receive - {ssl, Socket, "ello world"} -> - ok - end - end. + "Hello world" = ssl_test_lib:active_recv(Socket, 11), + ok. send_recv_result_active_no_rizzo(Socket) -> ssl:send(Socket, "Hello world"), + "Hello world" = ssl_test_lib:active_recv(Socket, 11), + ok. + + +ssl_active_recv(N) -> + ssl_active_recv(N, []). + +ssl_active_recv(0, Acc) -> + Acc; +ssl_active_recv(N, Acc) -> receive - {ssl, Socket, "Hello world"} -> - ok + {ssl, _, Bytes} -> + ssl_active_recv(N-length(Bytes), Acc ++ Bytes) end. result_ok(_Socket) -> @@ -4676,16 +4732,7 @@ renegotiate_reuse_session(Socket, Data) -> renegotiate(Socket, Data). renegotiate_immediately(Socket) -> - receive - {ssl, Socket, "Hello world"} -> - ok; - %% Handle 1/n-1 splitting countermeasure Rizzo/Duong-Beast - {ssl, Socket, "H"} -> - receive - {ssl, Socket, "ello world"} -> - ok - end - end, + _ = ssl_test_lib:active_recv(Socket, 11), ok = ssl:renegotiate(Socket), {error, renegotiation_rejected} = ssl:renegotiate(Socket), ct:sleep(?RENEGOTIATION_DISABLE_TIME + ?SLEEP), @@ -4695,17 +4742,7 @@ renegotiate_immediately(Socket) -> ok. renegotiate_rejected(Socket) -> - receive - {ssl, Socket, "Hello world"} -> - ok; - %% Handle 1/n-1 splitting countermeasure Rizzo/Duong-Beast - {ssl, Socket, "H"} -> - - receive - {ssl, Socket, "ello world"} -> - ok - end - end, + _ = ssl_test_lib:active_recv(Socket, 11), {error, renegotiation_rejected} = ssl:renegotiate(Socket), {error, renegotiation_rejected} = ssl:renegotiate(Socket), ct:sleep(?RENEGOTIATION_DISABLE_TIME +1), @@ -4880,17 +4917,11 @@ session_loop(Sess) -> erlang_ssl_receive(Socket, Data) -> - receive - {ssl, Socket, Data} -> - io:format("Received ~p~n",[Data]), - ok; - {ssl, Socket, Byte} when length(Byte) == 1 -> %% Handle 1/n-1 splitting countermeasure Rizzo/Duong-Beast - io:format("Received ~p~n",[Byte]), - erlang_ssl_receive(Socket, tl(Data)); - Other -> - ct:fail({unexpected_message, Other}) - after timer:seconds(?SEC_RENEGOTIATION_TIMEOUT) * test_server:timetrap_scale_factor() -> - ct:fail({did_not_get, Data}) + case ssl_test_lib:active_recv(Socket, length(Data)) of + Data -> + ok; + Other -> + ct:fail({{expected, Data}, {got, Other}}) end. receive_msg(_) -> @@ -4907,28 +4938,6 @@ controlling_process_result(Socket, Pid, Msg) -> ssl:send(Socket, Msg), no_result_msg. -receive_s_rizzo_duong_beast() -> - receive - {ssl, _, "erver hello"} -> - receive - {ssl, _, "C"} -> - receive - {ssl, _, "lient hello"} -> - ok - end - end - end. -receive_c_rizzo_duong_beast() -> - receive - {ssl, _, "lient hello"} -> - receive - {ssl, _, "S"} -> - receive - {ssl, _, "erver hello"} -> - ok - end - end - end. controller_dies_result(_Socket, _Pid, _Msg) -> receive Result -> Result end. @@ -5014,16 +5023,16 @@ run_suites(Ciphers, Config, Type) -> {ClientOpts, ServerOpts} = case Type of rsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_verification_opts, Config)]}; + ssl_test_lib:ssl_options(server_rsa_opts, Config)]}; dsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_dsa_verify_opts, Config), [{ciphers, Ciphers} | ssl_test_lib:ssl_options(server_dsa_opts, Config)]}; anonymous -> %% No certs in opts! - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), [{ciphers, Ciphers} | ssl_test_lib:ssl_options([], Config)]}; psk -> @@ -5053,38 +5062,38 @@ run_suites(Ciphers, Config, Type) -> {ssl_test_lib:ssl_options(client_srp_dsa, Config), ssl_test_lib:ssl_options(server_srp_dsa, Config)}; ecdsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_ecdsa_opts, Config), [{ciphers, Ciphers} | ssl_test_lib:ssl_options(server_ecdsa_opts, Config)]}; ecdh_rsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_ecdh_rsa_opts, Config), ssl_test_lib:ssl_options(server_ecdh_rsa_opts, Config)}; rc4_rsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_verification_opts, Config)]}; + ssl_test_lib:ssl_options(server_rsa_verify_opts, Config)]}; rc4_ecdh_rsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_ecdh_rsa_opts, Config), [{ciphers, Ciphers} | ssl_test_lib:ssl_options(server_ecdh_rsa_opts, Config)]}; rc4_ecdsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), [{ciphers, Ciphers} | ssl_test_lib:ssl_options(server_ecdsa_opts, Config)]}; des_dhe_rsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), [{ciphers, Ciphers} | ssl_test_lib:ssl_options(server_verification_opts, Config)]}; des_rsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_verification_opts, Config)]}; + ssl_test_lib:ssl_options(server_rsa_verify_opts, Config)]}; chacha_rsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_verification_opts, Config)]}; + ssl_test_lib:ssl_options(server_rsa_verify_opts, Config)]}; chacha_ecdsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_ecdsa_opts, Config), [{ciphers, Ciphers} | ssl_test_lib:ssl_options(server_ecdsa_opts, Config)]} end, diff --git a/lib/ssl/test/ssl_bench.spec b/lib/ssl/test/ssl_bench.spec index 8b746c5ca9..217cc6fc83 100644 --- a/lib/ssl/test/ssl_bench.spec +++ b/lib/ssl/test/ssl_bench.spec @@ -1 +1,2 @@ {suites,"../ssl_test",[ssl_bench_SUITE, ssl_dist_bench_SUITE]}. +{skip_groups,"../ssl_test",ssl_bench_SUITE,basic,"Benchmarks run separately"}. diff --git a/lib/ssl/test/ssl_bench_SUITE.erl b/lib/ssl/test/ssl_bench_SUITE.erl index 13097b08b6..0b2011a627 100644 --- a/lib/ssl/test/ssl_bench_SUITE.erl +++ b/lib/ssl/test/ssl_bench_SUITE.erl @@ -25,10 +25,11 @@ suite() -> [{ct_hooks,[{ts_install_cth,[{nodenames,2}]}]}]. -all() -> [{group, setup}, {group, payload}, {group, pem_cache}]. +all() -> [{group, basic}, {group, setup}, {group, payload}, {group, pem_cache}]. groups() -> - [{setup, [{repeat, 3}], [setup_sequential, setup_concurrent]}, + [{basic, [], [basic_pem_cache]}, + {setup, [{repeat, 3}], [setup_sequential, setup_concurrent]}, {payload, [{repeat, 3}], [payload_simple]}, {pem_cache, [{repeat, 3}], [use_pem_cache, bypass_pem_cache]} ]. @@ -51,29 +52,21 @@ init_per_suite(Config) -> end_per_suite(_Config) -> ok. -init_per_testcase(use_pem_cache, Conf) -> +init_per_testcase(TC, Conf) when TC =:= use_pem_cache; + TC =:= bypass_pem_cache; + TC =:= basic_pem_cache -> case bypass_pem_cache_supported() of false -> {skipped, "PEM cache bypass support required"}; true -> application:set_env(ssl, bypass_pem_cache, false), Conf end; -init_per_testcase(bypass_pem_cache, Conf) -> - case bypass_pem_cache_supported() of - false -> {skipped, "PEM cache bypass support required"}; - true -> - application:set_env(ssl, bypass_pem_cache, true), - Conf - end; init_per_testcase(_Func, Conf) -> Conf. -end_per_testcase(use_pem_cache, _Config) -> - case bypass_pem_cache_supported() of - false -> ok; - true -> application:set_env(ssl, bypass_pem_cache, false) - end; -end_per_testcase(bypass_pem_cache, _Config) -> +end_per_testcase(TC, _Config) when TC =:= use_pem_cache; + TC =:= bypass_pem_cache; + TC =:= basic_pem_cache -> case bypass_pem_cache_supported() of false -> ok; true -> application:set_env(ssl, bypass_pem_cache, false) @@ -119,6 +112,9 @@ payload_simple(Config) -> {suite, "ssl"}, {name, "Payload simple"}]}), ok. +basic_pem_cache(_Config) -> + do_test(ssl, pem_cache, 10, 5, node()). + use_pem_cache(_Config) -> {ok, Result} = do_test(ssl, pem_cache, 100, 500, node()), ct_event:notify(#event{name = benchmark_data, diff --git a/lib/ssl/test/ssl_crl_SUITE.erl b/lib/ssl/test/ssl_crl_SUITE.erl index 23c5eaf84d..c61039b5da 100644 --- a/lib/ssl/test/ssl_crl_SUITE.erl +++ b/lib/ssl/test/ssl_crl_SUITE.erl @@ -383,8 +383,11 @@ crl_hash_dir_expired(Config) when is_list(Config) -> {verify, verify_peer}], {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - %% First make a CRL that expired yesterday. - make_certs:gencrl(PrivDir, CA, CertsConfig, -24), + %% First make a CRL that will expire in one second. + make_certs:gencrl_sec(PrivDir, CA, CertsConfig, 1), + %% Sleep until the next CRL is due + ct:sleep({seconds, 1}), + CrlDir = filename:join(PrivDir, "crls"), populate_crl_hash_dir(PrivDir, CrlDir, [{CA, "1627b4b0"}], diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl index e39313e5cd..1b432970b6 100644 --- a/lib/ssl/test/ssl_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_handshake_SUITE.erl @@ -26,6 +26,7 @@ -include_lib("common_test/include/ct.hrl"). -include("ssl_alert.hrl"). +-include("ssl_handshake.hrl"). -include("ssl_internal.hrl"). -include("tls_handshake.hrl"). -include_lib("public_key/include/public_key.hrl"). @@ -42,7 +43,8 @@ all() -> [decode_hello_handshake, decode_empty_server_sni_correctly, select_proper_tls_1_2_rsa_default_hashsign, ignore_hassign_extension_pre_tls_1_2, - unorded_chain, signature_algorithms]. + unorded_chain, signature_algorithms, + encode_decode_srp]. %%-------------------------------------------------------------------- init_per_suite(Config) -> @@ -124,13 +126,13 @@ decode_supported_elliptic_curves_hello_extension_correctly(_Config) -> Len = ListLen + 2, Extension = <<?UINT16(?ELLIPTIC_CURVES_EXT), ?UINT16(Len), ?UINT16(ListLen), EllipticCurveList/binary>>, % after decoding we should see only valid curves - Extensions = ssl_handshake:decode_hello_extensions(Extension, {3,2}, client), + Extensions = ssl_handshake:decode_hello_extensions(Extension, {3,2}, {3,2}, client), #{elliptic_curves := #elliptic_curves{elliptic_curve_list = [?sect233k1, ?sect193r2]}} = Extensions. decode_unknown_hello_extension_correctly(_Config) -> FourByteUnknown = <<16#CA,16#FE, ?UINT16(4), 3, 0, 1, 2>>, Renegotiation = <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(1), 0>>, - Extensions = ssl_handshake:decode_hello_extensions(<<FourByteUnknown/binary, Renegotiation/binary>>, {3,2}, client), + Extensions = ssl_handshake:decode_hello_extensions(<<FourByteUnknown/binary, Renegotiation/binary>>, {3,2}, {3,2}, client), #{renegotiation_info := #renegotiation_info{renegotiated_connection = <<0>>}} = Extensions. @@ -145,12 +147,12 @@ encode_single_hello_sni_extension_correctly(_Config) -> decode_single_hello_sni_extension_correctly(_Config) -> SNI = <<16#00, 16#00, 16#00, 16#0d, 16#00, 16#0b, 16#00, 16#00, 16#08, $t, $e, $s, $t, $., $c, $o, $m>>, - Decoded = ssl_handshake:decode_hello_extensions(SNI, {3,3}, client), + Decoded = ssl_handshake:decode_hello_extensions(SNI, {3,3}, {3,3}, client), #{sni := #sni{hostname = "test.com"}} = Decoded. decode_empty_server_sni_correctly(_Config) -> SNI = <<?UINT16(?SNI_EXT),?UINT16(0)>>, - Decoded = ssl_handshake:decode_hello_extensions(SNI, {3,3}, server), + Decoded = ssl_handshake:decode_hello_extensions(SNI, {3,3}, {3,3}, server), #{sni := #sni{hostname = ""}} = Decoded. @@ -190,6 +192,30 @@ unorded_chain(Config) when is_list(Config) -> {ok, _, OrderedChain} = ssl_certificate:certificate_chain(PeerCert, ets:new(foo, []), ExtractedCerts, UnordedChain). +encode_decode_srp(_Config) -> + Exts = #{srp => #srp{username = <<"foo">>}, + sni => #sni{hostname = "bar"}, + renegotiation_info => undefined, + signature_algs => undefined, + alpn => undefined, + next_protocol_negotiation => undefined, + ec_point_formats => undefined, + elliptic_curves => undefined + }, + EncodedExts0 = <<0,20, % Length + 0,12, % SRP extension + 0,4, % Length + 3, % srp_I length + 102,111,111, % username = "foo" + 0,0, % SNI extension + 0,8, % Length + 0,6, % ServerNameLength + 0, % NameType (host_name) + 0,3, % HostNameLength + 98,97,114>>, % hostname = "bar" + EncodedExts0 = <<?UINT16(_),EncodedExts/binary>> = + ssl_handshake:encode_hello_extensions(Exts), + Exts = ssl_handshake:decode_hello_extensions(EncodedExts, {3,3}, {3,3}, client). signature_algorithms(Config) -> Opts = proplists:get_value(server_opts, Config), diff --git a/lib/ssl/test/ssl_npn_handshake_SUITE.erl b/lib/ssl/test/ssl_npn_handshake_SUITE.erl index 1c7d6b5f9f..878e983bb9 100644 --- a/lib/ssl/test/ssl_npn_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_npn_handshake_SUITE.erl @@ -64,13 +64,12 @@ next_protocol_not_supported() -> npn_not_supported_server ]. -init_per_suite(Config) -> +init_per_suite(Config0) -> catch crypto:stop(), try crypto:start() of ok -> ssl_test_lib:clean_start(), - {ok, _} = make_certs:all(proplists:get_value(data_dir, Config), - proplists:get_value(priv_dir, Config)), + Config = ssl_test_lib:make_rsa_cert(Config0), ssl_test_lib:cert_options(Config) catch _:_ -> {skip, "Crypto did not start"} @@ -196,10 +195,10 @@ client_negotiate_server_does_not_support(Config) when is_list(Config) -> renegotiate_from_client_after_npn_handshake(Config) when is_list(Config) -> Data = "hello world", - ClientOpts0 = ssl_test_lib:ssl_options(client_opts, Config), + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), ClientOpts = [{client_preferred_next_protocols, {client, [<<"http/1.0">>], <<"http/1.1">>}}] ++ ClientOpts0, - ServerOpts0 = ssl_test_lib:ssl_options(server_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), ServerOpts = [{next_protocols_advertised, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}] ++ ServerOpts0, ExpectedProtocol = {ok, <<"http/1.0">>}, @@ -221,7 +220,7 @@ renegotiate_from_client_after_npn_handshake(Config) when is_list(Config) -> %-------------------------------------------------------------------------------- npn_not_supported_client(Config) when is_list(Config) -> - ClientOpts0 = ssl_test_lib:ssl_options(client_opts, Config), + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), PrefProtocols = {client_preferred_next_protocols, {client, [<<"http/1.0">>], <<"http/1.1">>}}, ClientOpts = [PrefProtocols] ++ ClientOpts0, @@ -236,7 +235,7 @@ npn_not_supported_client(Config) when is_list(Config) -> %-------------------------------------------------------------------------------- npn_not_supported_server(Config) when is_list(Config)-> - ServerOpts0 = ssl_test_lib:ssl_options(server_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), AdvProtocols = {next_protocols_advertised, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}, ServerOpts = [AdvProtocols] ++ ServerOpts0, @@ -244,63 +243,24 @@ npn_not_supported_server(Config) when is_list(Config)-> %-------------------------------------------------------------------------------- npn_handshake_session_reused(Config) when is_list(Config)-> - ClientOpts0 = ssl_test_lib:ssl_options(client_opts, Config), + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), ClientOpts = [{client_preferred_next_protocols, {client, [<<"http/1.0">>], <<"http/1.1">>}}] ++ ClientOpts0, - ServerOpts0 = ssl_test_lib:ssl_options(server_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), ServerOpts =[{next_protocols_advertised, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}] ++ ServerOpts0, - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {options, ServerOpts}]), - - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, no_result_msg, []}}, - {options, ClientOpts}]), - - SessionInfo = - receive - {Server, Info} -> - Info - end, - - Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, - - %% Make sure session is registered - ct:sleep(?SLEEP), - - Client1 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {from, self()}, {options, ClientOpts}]), - - receive - {Client1, SessionInfo} -> - ok; - {Client1, Other} -> - ct:fail(Other) - end, + ssl_test_lib:reuse_session(ClientOpts, ServerOpts, Config). - ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - ssl_test_lib:close(Client1). - %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- run_npn_handshake(Config, ClientExtraOpts, ServerExtraOpts, ExpectedProtocol) -> Data = "hello world", - ClientOpts0 = ssl_test_lib:ssl_options(client_opts, Config), + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), ClientOpts = ClientExtraOpts ++ ClientOpts0, - ServerOpts0 = ssl_test_lib:ssl_options(server_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), ServerOpts = ServerExtraOpts ++ ServerOpts0, {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), diff --git a/lib/ssl/test/ssl_payload_SUITE.erl b/lib/ssl/test/ssl_payload_SUITE.erl index 1f9b6a5772..27b9c258a0 100644 --- a/lib/ssl/test/ssl_payload_SUITE.erl +++ b/lib/ssl/test/ssl_payload_SUITE.erl @@ -64,7 +64,8 @@ payload_tests() -> server_echos_active_huge, client_echos_passive_huge, client_echos_active_once_huge, - client_echos_active_huge]. + client_echos_active_huge, + client_active_once_server_close]. init_per_suite(Config) -> catch crypto:stop(), @@ -397,6 +398,23 @@ client_echos_active_huge(Config) when is_list(Config) -> client_echos_active( Data, ClientOpts, ServerOpts, ClientNode, ServerNode, Hostname). + +%%-------------------------------------------------------------------- +client_active_once_server_close() -> + [{doc, "Server sends 500000 bytes and immediately after closes the connection" + "Make sure client recives all data if possible"}]. + +client_active_once_server_close(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + %% + Data = binary:copy(<<"1234567890">>, 50000), + client_active_once_server_close( + Data, ClientOpts, ServerOpts, ClientNode, ServerNode, Hostname). + + + %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- @@ -541,42 +559,57 @@ client_echos_active( ssl_test_lib:close(Server), ssl_test_lib:close(Client). +client_active_once_server_close( + Data, ClientOpts, ServerOpts, ClientNode, ServerNode, Hostname) -> + Length = byte_size(Data), + Server = + ssl_test_lib:start_server( + [{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, send_close, [Data]}}, + {options, [{active, once}, {mode, binary} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = + ssl_test_lib:start_client( + [{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, active_once_recv, [Length]}}, + {options,[{active, once}, {mode, binary} | ClientOpts]}]), + %% + ssl_test_lib:check_result(Server, ok, Client, ok). + +send(_Socket, _Data, 0, _) -> + ok; +send(Socket, Data, Count, RecvEcho) -> + ok = ssl:send(Socket, Data), + RecvEcho(), + send(Socket, Data, Count - 1, RecvEcho). -send(Socket, Data, Count, Verify) -> - send(Socket, Data, Count, <<>>, Verify). -%% -send(_Socket, _Data, 0, Acc, _Verify) -> - Acc; -send(Socket, Data, Count, Acc, Verify) -> +send_close(Socket, Data) -> ok = ssl:send(Socket, Data), - NewAcc = Verify(Acc), - send(Socket, Data, Count - 1, NewAcc, Verify). + ssl:close(Socket). - sender(Socket, Data) -> ct:log("Sender recv: ~p~n", [ssl:getopts(Socket, [active])]), - <<>> = - send( - Socket, Data, 100, - fun(Acc) -> verify_recv(Socket, Data, Acc) end), - ok. + send(Socket, Data, 100, + fun() -> + ssl_test_lib:recv_disregard(Socket, byte_size(Data)) + end). sender_active_once(Socket, Data) -> ct:log("Sender active once: ~p~n", [ssl:getopts(Socket, [active])]), - <<>> = - send( - Socket, Data, 100, - fun(Acc) -> verify_active_once(Socket, Data, Acc) end), - ok. + send(Socket, Data, 100, + fun() -> + ssl_test_lib:active_once_disregard(Socket, byte_size(Data)) + end). sender_active(Socket, Data) -> ct:log("Sender active: ~p~n", [ssl:getopts(Socket, [active])]), - <<>> = - send( - Socket, Data, 100, - fun(Acc) -> verify_active(Socket, Data, Acc) end), - ok. - + send(Socket, Data, 100, + fun() -> + ssl_test_lib:active_disregard(Socket, byte_size(Data)) + end). echoer(Socket, Size) -> ct:log("Echoer recv: ~p~n", [ssl:getopts(Socket, [active])]), @@ -592,99 +625,32 @@ echoer_active(Socket, Size) -> %% Receive Size bytes +echo_recv(_Socket, 0) -> + ok; echo_recv(Socket, Size) -> {ok, Data} = ssl:recv(Socket, 0), ok = ssl:send(Socket, Data), - NewSize = Size - byte_size(Data), - if - 0 < NewSize -> - echo_recv(Socket, NewSize); - 0 == NewSize -> - ok - end. - -%% Verify that received data is SentData, return any superflous data -verify_recv(Socket, SentData, Acc) -> - {ok, NewData} = ssl:recv(Socket, 0), - SentSize = byte_size(SentData), - NewAcc = <<Acc/binary, NewData/binary>>, - NewSize = byte_size(NewAcc), - if - SentSize < NewSize -> - {SentData,Rest} = split_binary(NewAcc, SentSize), - Rest; - NewSize < SentSize -> - verify_recv(Socket, SentData, NewAcc); - true -> - SentData = NewAcc, - <<>> - end. + echo_recv(Socket, Size - byte_size(Data)). %% Receive Size bytes +echo_active_once(_Socket, 0) -> + ok; echo_active_once(Socket, Size) -> receive {ssl, Socket, Data} -> ok = ssl:send(Socket, Data), NewSize = Size - byte_size(Data), ssl:setopts(Socket, [{active, once}]), - if - 0 < NewSize -> - echo_active_once(Socket, NewSize); - 0 == NewSize -> - ok - end + echo_active_once(Socket, NewSize) end. -%% Verify that received data is SentData, return any superflous data -verify_active_once(Socket, SentData, Acc) -> - receive - {ssl, Socket, Data} -> - SentSize = byte_size(SentData), - NewAcc = <<Acc/binary, Data/binary>>, - NewSize = byte_size(NewAcc), - ssl:setopts(Socket, [{active, once}]), - if - SentSize < NewSize -> - {SentData,Rest} = split_binary(NewAcc, SentSize), - Rest; - NewSize < SentSize -> - verify_active_once(Socket, SentData, NewAcc); - true -> - SentData = NewAcc, - <<>> - end - end. - - %% Receive Size bytes +echo_active(_Socket, 0) -> + ok; echo_active(Socket, Size) -> receive {ssl, Socket, Data} -> ok = ssl:send(Socket, Data), - NewSize = Size - byte_size(Data), - if - 0 < NewSize -> - echo_active(Socket, NewSize); - 0 == NewSize -> - ok - end - end. - -%% Verify that received data is SentData, return any superflous data -verify_active(Socket, SentData, Acc) -> - receive - {ssl, Socket, Data} -> - SentSize = byte_size(SentData), - NewAcc = <<Acc/binary, Data/binary>>, - NewSize = byte_size(NewAcc), - if - SentSize < NewSize -> - {SentData,Rest} = split_binary(NewAcc, SentSize), - Rest; - NewSize < SentSize -> - verify_active(Socket, SentData, NewAcc); - true -> - SentData = NewAcc, - <<>> - end - end. + echo_active(Socket, Size - byte_size(Data)) + end. + diff --git a/lib/ssl/test/ssl_pem_cache_SUITE.erl b/lib/ssl/test/ssl_pem_cache_SUITE.erl index 25d2cb300d..6f11e2bbe8 100644 --- a/lib/ssl/test/ssl_pem_cache_SUITE.erl +++ b/lib/ssl/test/ssl_pem_cache_SUITE.erl @@ -44,11 +44,8 @@ init_per_suite(Config0) -> try crypto:start() of ok -> ssl_test_lib:clean_start(), - %% make rsa certs using oppenssl - {ok, _} = make_certs:all(proplists:get_value(data_dir, Config0), - proplists:get_value(priv_dir, Config0)), - Config1 = ssl_test_lib:make_dsa_cert(Config0), - ssl_test_lib:cert_options(Config1) + %% make rsa certs + ssl_test_lib:make_rsa_cert(Config0) catch _:_ -> {skip, "Crypto did not start"} end. @@ -86,8 +83,8 @@ pem_cleanup() -> [{doc, "Test pem cache invalidate mechanism"}]. pem_cleanup(Config)when is_list(Config) -> process_flag(trap_exit, true), - ClientOpts = proplists:get_value(client_verification_opts, Config), - ServerOpts = proplists:get_value(server_verification_opts, Config), + ClientOpts = proplists:get_value(client_rsa_verify_opts, Config), + ServerOpts = proplists:get_value(server_rsa_verify_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = @@ -118,8 +115,8 @@ invalid_insert() -> invalid_insert(Config)when is_list(Config) -> process_flag(trap_exit, true), - ClientOpts = proplists:get_value(client_verification_opts, Config), - ServerOpts = proplists:get_value(server_verification_opts, Config), + ClientOpts = proplists:get_value(client_rsa_verify_opts, Config), + ServerOpts = proplists:get_value(server_rsa_verify_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), BadClientOpts = [{cacertfile, "tmp/does_not_exist.pem"} | proplists:delete(cacertfile, ClientOpts)], Server = diff --git a/lib/ssl/test/ssl_session_cache_SUITE.erl b/lib/ssl/test/ssl_session_cache_SUITE.erl index a0fab58b9d..7f33fe3204 100644 --- a/lib/ssl/test/ssl_session_cache_SUITE.erl +++ b/lib/ssl/test/ssl_session_cache_SUITE.erl @@ -48,7 +48,8 @@ all() -> session_cache_process_list, session_cache_process_mnesia, client_unique_session, - max_table_size + max_table_size, + save_specific_session ]. groups() -> @@ -60,10 +61,7 @@ init_per_suite(Config0) -> ok -> ssl_test_lib:clean_start(), %% make rsa certs using - {ok, _} = make_certs:all(proplists:get_value(data_dir, Config0), - proplists:get_value(priv_dir, Config0)), - Config = ssl_test_lib:make_dsa_cert(Config0), - ssl_test_lib:cert_options(Config) + ssl_test_lib:make_rsa_cert(Config0) catch _:_ -> {skip, "Crypto did not start"} end. @@ -97,7 +95,10 @@ init_per_testcase(session_cleanup, Config) -> init_per_testcase(client_unique_session, Config) -> ct:timetrap({seconds, 40}), Config; - +init_per_testcase(save_specific_session, Config) -> + ssl_test_lib:clean_start(), + ct:timetrap({seconds, 5}), + Config; init_per_testcase(max_table_size, Config) -> ssl:stop(), application:load(ssl), @@ -141,7 +142,7 @@ end_per_testcase(max_table_size, Config) -> end_per_testcase(default_action, Config); end_per_testcase(Case, Config) when Case == session_cache_process_list; Case == session_cache_process_mnesia -> - ets:delete(ssl_test), + catch ets:delete(ssl_test), Config; end_per_testcase(_, Config) -> Config. @@ -154,8 +155,8 @@ client_unique_session() -> "sets up many connections"}]. client_unique_session(Config) when is_list(Config) -> process_flag(trap_exit, true), - ClientOpts = proplists:get_value(client_opts, Config), - ServerOpts = proplists:get_value(server_opts, Config), + ClientOpts = proplists:get_value(client_rsa_verify_opts, Config), + ServerOpts = proplists:get_value(server_rsa_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, @@ -164,8 +165,7 @@ client_unique_session(Config) when is_list(Config) -> {tcp_options, [{active, false}]}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - LastClient = clients_start(Server, - ClientNode, Hostname, Port, ClientOpts, client_unique_session, 20), + LastClient = clients_start(Server, ClientNode, Hostname, Port, ClientOpts, 20), receive {LastClient, {ok, _}} -> ok @@ -185,8 +185,8 @@ session_cleanup() -> "does not grow and grow ..."}]. session_cleanup(Config) when is_list(Config) -> process_flag(trap_exit, true), - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = @@ -254,13 +254,75 @@ session_cache_process_mnesia(Config) when is_list(Config) -> session_cache_process(mnesia,Config). %%-------------------------------------------------------------------- +save_specific_session() -> + [{doc, "Test that we can save a specific client session" + }]. +save_specific_session(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ClientOpts = proplists:get_value(client_rsa_verify_opts, Config), + ServerOpts = proplists:get_value(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {tcp_options, [{active, false}]}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client1 = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, ClientOpts}]), + Server ! listen, + + Client2 = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, [{reuse_sessions, save} | ClientOpts]}]), + SessionID1 = + receive + {Client1, S1} -> + S1 + end, + + SessionID2 = + receive + {Client2, S2} -> + S2 + end, + + true = SessionID1 =/= SessionID2, + + {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), + [_, _,_, _, Prop] = StatusInfo, + State = ssl_test_lib:state(Prop), + ClientCache = element(2, State), + 2 = ssl_session_cache:size(ClientCache), + + Server ! listen, + + Client3 = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, [{reuse_session, SessionID2} | ClientOpts]}]), + receive + {Client3, SessionID2} -> + ok; + {Client3, SessionID3}-> + ct:fail({got, SessionID3, expected, SessionID2}); + Other -> + ct:fail({got,Other}) + end. + +%%-------------------------------------------------------------------- max_table_size() -> [{doc,"Test max limit on session table"}]. max_table_size(Config) when is_list(Config) -> process_flag(trap_exit, true), - ClientOpts = proplists:get_value(client_verification_opts, Config), - ServerOpts = proplists:get_value(server_verification_opts, Config), + ClientOpts = proplists:get_value(client_rsa_verify_opts, Config), + ServerOpts = proplists:get_value(server_rsa_verify_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, @@ -270,7 +332,7 @@ max_table_size(Config) when is_list(Config) -> {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), LastClient = clients_start(Server, - ClientNode, Hostname, Port, ClientOpts, max_table_size, 20), + ClientNode, Hostname, Port, ClientOpts, 20), receive {LastClient, {ok, _}} -> ok @@ -426,25 +488,27 @@ session_loop(Sess) -> %%-------------------------------------------------------------------- session_cache_process(_Type,Config) when is_list(Config) -> - ssl_basic_SUITE:reuse_session(Config). + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + ssl_test_lib:reuse_session(ClientOpts, ServerOpts, Config). -clients_start(_Server, ClientNode, Hostname, Port, ClientOpts, Test, 0) -> +clients_start(_Server, ClientNode, Hostname, Port, ClientOpts, 0) -> %% Make sure session is registered ct:sleep(?SLEEP * 2), ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {mfa, {?MODULE, connection_info_result, []}}, - {from, self()}, {options, test_copts(Test, 0, ClientOpts)}]); -clients_start(Server, ClientNode, Hostname, Port, ClientOpts, Test, N) -> + {from, self()}, {options, ClientOpts}]); +clients_start(Server, ClientNode, Hostname, Port, ClientOpts, N) -> spawn_link(ssl_test_lib, start_client, [[{node, ClientNode}, {port, Port}, {host, Hostname}, {mfa, {ssl_test_lib, no_result, []}}, - {from, self()}, {options, test_copts(Test, N, ClientOpts)}]]), + {from, self()}, {options, ClientOpts}]]), Server ! listen, wait_for_server(), - clients_start(Server, ClientNode, Hostname, Port, ClientOpts, Test, N-1). + clients_start(Server, ClientNode, Hostname, Port, ClientOpts, N-1). connection_info_result(Socket) -> ssl:connection_information(Socket, [protocol, cipher_suite]). @@ -481,21 +545,3 @@ get_delay_timers() -> wait_for_server() -> ct:sleep(100). - - -test_copts(_, 0, ClientOpts) -> - ClientOpts; -test_copts(max_table_size, N, ClientOpts) -> - Version = tls_record:highest_protocol_version([]), - CipherSuites = %%lists:map(fun(X) -> ssl_cipher_format:suite_definition(X) end, ssl_cipher:filter_suites(ssl_cipher:suites(Version))), -[ Y|| Y = {Alg,_, _, _} <- lists:map(fun(X) -> ssl_cipher_format:suite_definition(X) end, ssl_cipher:filter_suites(ssl_cipher:suites(Version))), Alg =/= ecdhe_ecdsa, Alg =/= ecdh_ecdsa, Alg =/= ecdh_rsa, Alg =/= ecdhe_rsa, Alg =/= dhe_dss, Alg =/= dss], - case length(CipherSuites) of - M when M >= N -> - Cipher = lists:nth(N, CipherSuites), - ct:pal("~p",[Cipher]), - [{ciphers, [Cipher]} | ClientOpts]; - _ -> - ClientOpts - end; -test_copts(_, _, ClientOpts) -> - ClientOpts. diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index a8d62d6c4e..0173b98e1a 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -523,7 +523,7 @@ cert_options(Config) -> {client_verification_opts, [{cacertfile, ServerCaCertFile}, {certfile, ClientCertFile}, {keyfile, ClientKeyFile}, - {ssl_imp, new}]}, + {verify, verify_peer}]}, {client_verification_opts_digital_signature_only, [{cacertfile, ServerCaCertFile}, {certfile, ClientCertFileDigitalSignatureOnly}, {keyfile, ClientKeyFile}, @@ -1461,19 +1461,10 @@ cipher_result(Socket, Result) -> %% Importante to send two packets here %% to properly test "cipher state" handling ssl:send(Socket, "Hello\n"), - receive - {ssl, Socket, "H"} -> - ssl:send(Socket, " world\n"), - receive_rizzo_duong_beast(); - {ssl, Socket, "Hello\n"} -> - ssl:send(Socket, " world\n"), - receive - {ssl, Socket, " world\n"} -> - ok - end; - Other -> - {unexpected, Other} - end. + "Hello\n" = active_recv(Socket, length( "Hello\n")), + ssl:send(Socket, " world\n"), + " world\n" = active_recv(Socket, length(" world\n")), + ok. session_info_result(Socket) -> {ok, Info} = ssl:connection_information(Socket, [session_id, cipher_suite]), @@ -1546,7 +1537,12 @@ init_tls_version(Version, Config) -> clean_tls_version(Config) -> proplists:delete(protocol_opts, proplists:delete(protocol, Config)). - + +sufficient_crypto_support(Version) + when Version == 'tlsv1.3' -> + CryptoSupport = crypto:supports(), + lists:member(rsa_pkcs1_pss_padding, proplists:get_value(rsa_opts, CryptoSupport)) andalso + lists:member(x448, proplists:get_value(curves, CryptoSupport)); sufficient_crypto_support(Version) when Version == 'tlsv1.2'; Version == 'dtlsv1.2' -> CryptoSupport = crypto:supports(), @@ -1592,36 +1588,72 @@ v_1_2_check(ecdh_rsa, ecdh_ecdsa) -> v_1_2_check(_, _) -> false. -send_recv_result_active(Socket) -> - ssl:send(Socket, "Hello world"), - receive - {ssl, Socket, "H"} -> - receive - {ssl, Socket, "ello world"} -> - ok - end; - {ssl, Socket, "Hello world"} -> - ok - end. - send_recv_result(Socket) -> - ssl:send(Socket, "Hello world"), - {ok,"Hello world"} = ssl:recv(Socket, 11), + Data = "Hello world", + ssl:send(Socket, Data), + {ok, Data} = ssl:recv(Socket, length(Data)), + ok. + +send_recv_result_active(Socket) -> + Data = "Hello world", + ssl:send(Socket, Data), + Data = active_recv(Socket, length(Data)), ok. send_recv_result_active_once(Socket) -> - ssl:send(Socket, "Hello world"), - receive - {ssl, Socket, "H"} -> - ssl:setopts(Socket, [{active, once}]), - receive - {ssl, Socket, "ello world"} -> - ok - end; - {ssl, Socket, "Hello world"} -> - ok + Data = "Hello world", + ssl:send(Socket, Data), + active_once_recv_list(Socket, length(Data)). + +active_recv(Socket, N) -> + active_recv(Socket, N, []). + +active_recv(_Socket, 0, Acc) -> + Acc; +active_recv(Socket, N, Acc) -> + receive + {ssl, Socket, Bytes} -> + active_recv(Socket, N-length(Bytes), Acc ++ Bytes) + end. + +active_once_recv(_Socket, 0) -> + ok; +active_once_recv(Socket, N) -> + receive + {ssl, Socket, Bytes} -> + ssl:setopts(Socket, [{active, once}]), + active_once_recv(Socket, N-byte_size(Bytes)) end. +active_once_recv_list(_Socket, 0) -> + ok; +active_once_recv_list(Socket, N) -> + receive + {ssl, Socket, Bytes} -> + ssl:setopts(Socket, [{active, once}]), + active_once_recv_list(Socket, N-length(Bytes)) + end. +recv_disregard(_Socket, 0) -> + ok; +recv_disregard(Socket, N) -> + {ok, Bytes} = ssl:recv(Socket, 0), + recv_disregard(Socket, N-byte_size(Bytes)). + +active_disregard(_Socket, 0) -> + ok; +active_disregard(Socket, N) -> + receive + {ssl, Socket, Bytes} -> + active_disregard(Socket, N-byte_size(Bytes)) + end. +active_once_disregard(_Socket, 0) -> + ok; +active_once_disregard(Socket, N) -> + receive + {ssl, Socket, Bytes} -> + ssl:setopts(Socket, [{active, once}]), + active_once_disregard(Socket, N-byte_size(Bytes)) + end. is_sane_ecc(openssl) -> case os:cmd("openssl version") of "OpenSSL 1.0.0a" ++ _ -> % Known bug in openssl @@ -2159,3 +2191,98 @@ server_msg(Server, ServerMsg) -> Unexpected -> ct:fail(Unexpected) end. + +session_id(Socket) -> + {ok, [{session_id, ID}]} = ssl:connection_information(Socket, [session_id]), + ID. + +reuse_session(ClientOpts, ServerOpts, Config) -> + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server0 = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {tcp_options, [{active, false}]}, + {options, ServerOpts}]), + Port0 = ssl_test_lib:inet_port(Server0), + + Client0 = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port0}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, [{reuse_sessions, save} | ClientOpts]}]), + Server0 ! listen, + + Client1 = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port0}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, ClientOpts}]), + + SID = receive + {Client0, Id0} -> + Id0 + end, + + receive + {Client1, SID} -> + ok + after ?SLEEP -> + ct:fail(session_not_reused) + end, + + Server0 ! listen, + + Client2 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port0}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, [{reuse_sessions, false} + | ClientOpts]}]), + receive + {Client2, SID} -> + ct:fail(session_reused_when_session_reuse_disabled_by_client); + {Client2, _} -> + ok + end, + + ssl_test_lib:close(Server0), + ssl_test_lib:close(Client0), + ssl_test_lib:close(Client1), + ssl_test_lib:close(Client2), + + Server1 = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {tcp_options, [{active, false}]}, + {options, [{reuse_sessions, false} |ServerOpts]}]), + Port1 = ssl_test_lib:inet_port(Server1), + + Client3 = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port1}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, [{reuse_sessions, save} | ClientOpts]}]), + SID1 = receive + {Client3, Id3} -> + Id3 + end, + + Server1 ! listen, + + Client4 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port1}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, ClientOpts}]), + + receive + {Client4, SID1} -> + ct:fail(session_reused_when_session_reuse_disabled_by_server); + {Client4, _} -> + ok + end, + + ssl_test_lib:close(Server1), + ssl_test_lib:close(Client3), + ssl_test_lib:close(Client4). + diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl index 5a38f5f9c1..d180021439 100644 --- a/lib/ssl/test/ssl_to_openssl_SUITE.erl +++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl @@ -91,6 +91,7 @@ all_versions_tests() -> erlang_server_openssl_client_anon_with_cert, erlang_server_openssl_client_reuse_session, erlang_client_openssl_server_renegotiate, + erlang_client_openssl_server_renegotiate_after_client_data, erlang_client_openssl_server_nowrap_seqnum, erlang_server_openssl_client_nowrap_seqnum, erlang_client_openssl_server_no_server_ca_cert, @@ -259,8 +260,9 @@ special_init(TestCase, Config) when Config; special_init(TestCase, Config) when TestCase == erlang_client_openssl_server_renegotiate; - TestCase == erlang_client_openssl_server_nowrap_seqnum; - TestCase == erlang_server_openssl_client_nowrap_seqnum + TestCase == erlang_client_openssl_server_nowrap_seqnum; + TestCase == erlang_server_openssl_client_nowrap_seqnum; + TestCase == erlang_client_openssl_server_renegotiate_after_client_data -> {ok, Version} = application:get_env(ssl, protocol_version), check_sane_openssl_renegotaite(Config, Version); @@ -760,8 +762,8 @@ erlang_client_openssl_server_renegotiate() -> [{doc,"Test erlang client when openssl server issuses a renegotiate"}]. erlang_client_openssl_server_renegotiate(Config) when is_list(Config) -> process_flag(trap_exit, true), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), - ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), @@ -770,12 +772,14 @@ erlang_client_openssl_server_renegotiate(Config) when is_list(Config) -> Port = ssl_test_lib:inet_port(node()), CertFile = proplists:get_value(certfile, ServerOpts), + CaCertFile = proplists:get_value(cacertfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), Version = ssl_test_lib:protocol_version(Config), Exe = "openssl", Args = ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version), + "-CAfile", CaCertFile, "-cert", CertFile, "-key", KeyFile, "-msg"], OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), @@ -800,6 +804,53 @@ erlang_client_openssl_server_renegotiate(Config) when is_list(Config) -> ssl_test_lib:close(Client), process_flag(trap_exit, false), ok. +%%-------------------------------------------------------------------- +erlang_client_openssl_server_renegotiate_after_client_data() -> + [{doc,"Test erlang client when openssl server issuses a renegotiate after reading client data"}]. +erlang_client_openssl_server_renegotiate_after_client_data(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + + {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), + + ErlData = "From erlang to openssl", + OpenSslData = "From openssl to erlang", + + Port = ssl_test_lib:inet_port(node()), + CaCertFile = proplists:get_value(cacertfile, ServerOpts), + CertFile = proplists:get_value(certfile, ServerOpts), + KeyFile = proplists:get_value(keyfile, ServerOpts), + Version = ssl_test_lib:protocol_version(Config), + + Exe = "openssl", + Args = ["s_server", "-accept", integer_to_list(Port), + ssl_test_lib:version_flag(Version), + "-CAfile", CaCertFile, + "-cert", CertFile, "-key", KeyFile, "-msg"], + + OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), + + ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + send_wait_send, [[ErlData, OpenSslData]]}}, + {options, ClientOpts}]), + + true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE), + ct:sleep(?SLEEP), + true = port_command(OpensslPort, OpenSslData), + + ssl_test_lib:check_result(Client, ok), + + %% Clean close down! Server needs to be closed first !! + ssl_test_lib:close_port(OpensslPort), + ssl_test_lib:close(Client), + process_flag(trap_exit, false), + ok. %%-------------------------------------------------------------------- @@ -810,7 +861,7 @@ erlang_client_openssl_server_nowrap_seqnum() -> " to lower treashold substantially."}]. erlang_client_openssl_server_nowrap_seqnum(Config) when is_list(Config) -> process_flag(trap_exit, true), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), @@ -819,12 +870,14 @@ erlang_client_openssl_server_nowrap_seqnum(Config) when is_list(Config) -> N = 10, Port = ssl_test_lib:inet_port(node()), + CaCertFile = proplists:get_value(cacertfile, ServerOpts), CertFile = proplists:get_value(certfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), Version = ssl_test_lib:protocol_version(Config), Exe = "openssl", Args = ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version), + "-CAfile", CaCertFile, "-cert", CertFile, "-key", KeyFile, "-msg"], OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), @@ -853,7 +906,7 @@ erlang_server_openssl_client_nowrap_seqnum() -> " to lower treashold substantially."}]. erlang_server_openssl_client_nowrap_seqnum(Config) when is_list(Config) -> process_flag(trap_exit, true), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -1602,8 +1655,8 @@ cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> start_erlang_client_and_openssl_server_with_opts(Config, ErlangClientOpts, OpensslServerOpts, Data, Callback) -> process_flag(trap_exit, true), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), - ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), ClientOpts = ErlangClientOpts ++ ClientOpts0, {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), @@ -1611,6 +1664,7 @@ start_erlang_client_and_openssl_server_with_opts(Config, ErlangClientOpts, Opens Data = "From openssl to erlang", Port = ssl_test_lib:inet_port(node()), + CaCertFile = proplists:get_value(cacertfile, ServerOpts), CertFile = proplists:get_value(certfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), Version = ssl_test_lib:protocol_version(Config), @@ -1620,10 +1674,12 @@ start_erlang_client_and_openssl_server_with_opts(Config, ErlangClientOpts, Opens [] -> ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version), + "-CAfile", CaCertFile, "-cert", CertFile,"-key", KeyFile]; [Opt, Value] -> ["s_server", Opt, Value, "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version), + "-CAfile", CaCertFile, "-cert", CertFile,"-key", KeyFile] end, @@ -1648,8 +1704,8 @@ start_erlang_client_and_openssl_server_with_opts(Config, ErlangClientOpts, Opens start_erlang_client_and_openssl_server_for_alpn_negotiation(Config, Data, Callback) -> process_flag(trap_exit, true), - ServerOpts = proplists:get_value(server_rsa_opts, Config), - ClientOpts0 = proplists:get_value(client_rsa_opts, Config), + ServerOpts = proplists:get_value(server_rsa_verify_opts, Config), + ClientOpts0 = proplists:get_value(client_rsa_verify_opts, Config), ClientOpts = [{alpn_advertised_protocols, [<<"spdy/2">>]} | ClientOpts0], {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), @@ -1657,12 +1713,14 @@ start_erlang_client_and_openssl_server_for_alpn_negotiation(Config, Data, Callba Data = "From openssl to erlang", Port = ssl_test_lib:inet_port(node()), + CaCertFile = proplists:get_value(cacertfile, ServerOpts), CertFile = proplists:get_value(certfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), Version = ssl_test_lib:protocol_version(Config), Exe = "openssl", Args = ["s_server", "-msg", "-alpn", "http/1.1,spdy/2", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version), + "-CAfile", CaCertFile, "-cert", CertFile, "-key", KeyFile], OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), @@ -1780,8 +1838,8 @@ start_erlang_server_and_openssl_client_for_alpn_npn_negotiation(Config, Data, Ca start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, Callback) -> process_flag(trap_exit, true), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), - ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), ClientOpts = [{client_preferred_next_protocols, {client, [<<"spdy/2">>], <<"http/1.1">>}} | ClientOpts0], {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), @@ -1789,6 +1847,7 @@ start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, Callbac Data = "From openssl to erlang", Port = ssl_test_lib:inet_port(node()), + CaCertFile = proplists:get_value(cacertfile, ServerOpts), CertFile = proplists:get_value(certfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), Version = ssl_test_lib:protocol_version(Config), @@ -1796,6 +1855,7 @@ start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, Callbac Exe = "openssl", Args = ["s_server", "-msg", "-nextprotoneg", "http/1.1,spdy/2", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version), + "-CAfile", CaCertFile, "-cert", CertFile, "-key", KeyFile], OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), @@ -1924,6 +1984,12 @@ server_sent_garbage(Socket) -> {error, closed} == ssl:send(Socket, "data") end. + +send_wait_send(Socket, [ErlData, OpenSslData]) -> + ssl:send(Socket, ErlData), + ct:sleep(?SLEEP), + ssl:send(Socket, ErlData), + erlang_ssl_receive(Socket, OpenSslData). check_openssl_sni_support(Config) -> HelpText = os:cmd("openssl s_client --help"), diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk index 75d959accf..3527062a8a 100644 --- a/lib/ssl/vsn.mk +++ b/lib/ssl/vsn.mk @@ -1 +1 @@ -SSL_VSN = 9.1 +SSL_VSN = 9.1.2 diff --git a/lib/stdlib/doc/src/lists.xml b/lib/stdlib/doc/src/lists.xml index 66146e9258..2755fb3dce 100644 --- a/lib/stdlib/doc/src/lists.xml +++ b/lib/stdlib/doc/src/lists.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2018</year> + <year>1996</year><year>2019</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -689,6 +689,18 @@ c</pre> </func> <func> + <name name="search" arity="2" since="OTP 21.0"/> + <fsummary>Find the first element that satisfies a predicate.</fsummary> + <desc> + <p>If there is a <c><anno>Value</anno></c> in <c><anno>List</anno></c> + such that <c><anno>Pred</anno>(<anno>Value</anno>)</c> returns + <c>true</c>, returns <c>{value, <anno>Value</anno>}</c> + for the first such <c><anno>Value</anno></c>, + otherwise returns <c>false</c>.</p> + </desc> + </func> + + <func> <name name="seq" arity="2" since=""/> <name name="seq" arity="3" since=""/> <fsummary>Generate a sequence of integers.</fsummary> @@ -771,18 +783,6 @@ length(lists:seq(From, To, Incr)) =:= (To - From + Incr) div Incr</code> </func> <func> - <name name="search" arity="2" since="OTP 21.0"/> - <fsummary>Find the first element that satisfies a predicate.</fsummary> - <desc> - <p>If there is a <c><anno>Value</anno></c> in <c><anno>List</anno></c> - such that <c><anno>Pred</anno>(<anno>Value</anno>)</c> returns - <c>true</c>, returns <c>{value, <anno>Value</anno>}</c> - for the first such <c><anno>Value</anno></c>, - otherwise returns <c>false</c>.</p> - </desc> - </func> - - <func> <name name="splitwith" arity="2" since=""/> <fsummary>Split a list into two lists based on a predicate.</fsummary> <desc> diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index e0cd68617b..3ec78a2667 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -831,13 +831,15 @@ bif_clashes(Forms, #lint{nowarn_bif_clash=Nowarn} = St) -> %% not_deprecated(Forms, State0) -> State -not_deprecated(Forms, St0) -> +not_deprecated(Forms, #lint{compile=Opts}=St0) -> %% There are no line numbers in St0#lint.compile. MFAsL = [{MFA,L} || {attribute, L, compile, Args} <- Forms, {nowarn_deprecated_function, MFAs0} <- lists:flatten([Args]), MFA <- lists:flatten([MFAs0])], - Nowarn = [MFA || {MFA,_L} <- MFAsL], + Nowarn = [MFA || + {nowarn_deprecated_function, MFAs0} <- Opts, + MFA <- lists:flatten([MFAs0])], ML = [{M,L} || {{M,_F,_A},L} <- MFAsL, is_atom(M)], St1 = foldl(fun ({M,L}, St2) -> check_module_name(M, L, St2) diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl index 9602f0bcd9..4ad94f2507 100644 --- a/lib/stdlib/src/erl_parse.yrl +++ b/lib/stdlib/src/erl_parse.yrl @@ -841,7 +841,7 @@ Erlang code. -type af_record_field(T) :: {'record_field', anno(), af_field_name(), T}. -type af_map_pattern() :: - {'map', anno(), [af_assoc_exact(abstract_expr)]}. + {'map', anno(), [af_assoc_exact(abstract_expr())]}. -type abstract_type() :: af_annotated_type() | af_atom() @@ -872,7 +872,7 @@ Erlang code. -type af_fun_type() :: {'type', anno(), 'fun', []} | {'type', anno(), 'fun', [{'type', anno(), 'any'} | abstract_type()]} - | {'type', anno(), 'fun', af_function_type()}. + | af_function_type(). -type af_integer_range_type() :: {'type', anno(), 'range', [af_singleton_integer_type()]}. @@ -924,10 +924,11 @@ Erlang code. -type af_function_constraint() :: [af_constraint()]. -type af_constraint() :: {'type', anno(), 'constraint', - af_lit_atom('is_subtype'), - [af_type_variable() | abstract_type()]}. % [V, T] + [af_lit_atom('is_subtype') | + [af_type_variable() | abstract_type()]]}. % [IsSubtype, [V, T]] -type af_singleton_integer_type() :: af_integer() + | af_character() | af_unary_op(af_singleton_integer_type()) | af_binary_op(af_singleton_integer_type()). diff --git a/lib/stdlib/src/ms_transform.erl b/lib/stdlib/src/ms_transform.erl index 6d243e1bec..97ec785c62 100644 --- a/lib/stdlib/src/ms_transform.erl +++ b/lib/stdlib/src/ms_transform.erl @@ -556,8 +556,8 @@ tg({call, Line, {remote,_,{atom,_,erlang},{atom, Line2, FunName}},ParaList}, FunName,length(ParaList)}}) end; tg({call, Line, {remote,_,{atom,_,ModuleName}, - {atom, _, FunName}},_ParaList},B) -> - throw({error,Line,{?ERR_GENREMOTECALL+B#tgd.eb,ModuleName,FunName}}); + {atom, _, FunName}},ParaList},B) -> + throw({error,Line,{?ERR_GENREMOTECALL+B#tgd.eb,ModuleName,FunName,length(ParaList)}}); tg({cons,Line, H, T},B) -> {cons, Line, tg(H,B), tg(T,B)}; tg({nil, Line},_B) -> diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index e6ed55bf2d..e791da48cf 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -68,7 +68,7 @@ non_latin1_module/1, otp_14323/1, stacktrace_syntax/1, otp_14285/1, otp_14378/1, - external_funs/1]). + external_funs/1,otp_15456/1]). suite() -> [{ct_hooks,[ts_install_cth]}, @@ -89,7 +89,8 @@ all() -> maps, maps_type, maps_parallel_match, otp_11851, otp_11879, otp_13230, record_errors, otp_11879_cont, non_latin1_module, otp_14323, - stacktrace_syntax, otp_14285, otp_14378, external_funs]. + stacktrace_syntax, otp_14285, otp_14378, external_funs, + otp_15456]. groups() -> [{unused_vars_warn, [], @@ -2119,6 +2120,61 @@ otp_5362(Config) when is_list(Config) -> [] = run(Config, Ts), ok. +%% OTP-15456. All compiler options can now be given in the option list +%% (as opposed to only in files). +otp_15456(Config) when is_list(Config) -> + Ts = [ + %% {nowarn_deprecated_function,[{M,F,A}]} can now be given + %% in the option list as well as in an attribute. + %% Wherever it occurs, it is not affected by + %% warn_deprecated_function. + {otp_15456_1, + <<"-compile({nowarn_deprecated_function,{erlang,now,0}}). + -export([foo/0]). + + foo() -> + {erlang:now(), random:seed0(), random:seed(1, 2, 3), + random:uniform(), random:uniform(42)}. + ">>, + {[{nowarn_deprecated_function,{random,seed0,0}}, + {nowarn_deprecated_function,[{random,uniform,0}, + {random,uniform,1}]}, + %% There should be no warnings when attempting to + %% turn of warnings for functions that are not + %% deprecated or not used in the module. + {nowarn_deprecated_function,{random,uniform_s,1}}, + {nowarn_deprecated_function,{erlang,abs,1}}, + warn_deprecated_function]}, + {warnings,[{5,erl_lint, + {deprecated,{random,seed,3}, + "the 'random' module is deprecated; " + "use the 'rand' module instead"}}]}}, + + %% {nowarn_unused_function,[{M,F,A}]} can be given + %% in the option list as well as in an attribute. + %% It was incorrectly documented to only work when + %% given in an attribute. + {otp_15456_2, + <<"-compile({nowarn_unused_function,foo/0}). + foo() -> ok. + bar() -> ok. + foobar() -> ok. + barf(_) -> ok. + other() -> ok. + ">>, + {[{nowarn_unused_function,[{bar,0},{foobar,0}]}, + {nowarn_unused_function,{barf,1}}, + %% There should be no warnings when attempting to + %% turn of warnings for unused functions that are not + %% defined in the module. + {nowarn_unused_function,{not_defined_in_module,1}}, + warn_unused_function]}, + {warnings,[{6,erl_lint,{unused_function,{other,0}}}]} + }], + + [] = run(Config, Ts), + ok. + %% OTP-5371. Aliases for bit syntax expressions are no longer allowed. otp_5371(Config) when is_list(Config) -> Ts = [{otp_5371_1, diff --git a/lib/syntax_tools/src/erl_prettypr.erl b/lib/syntax_tools/src/erl_prettypr.erl index 6906ef1553..6ad9bec2e6 100644 --- a/lib/syntax_tools/src/erl_prettypr.erl +++ b/lib/syntax_tools/src/erl_prettypr.erl @@ -1101,8 +1101,9 @@ lay_2(Node, Ctxt) -> Ctxt1 = reset_prec(Ctxt), D1 = lay(erl_syntax:constrained_function_type_body(Node), Ctxt1), + Ctxt2 = Ctxt1#ctxt{clause = undefined}, D2 = lay(erl_syntax:constrained_function_type_argument(Node), - Ctxt1), + Ctxt2), beside(D1, beside(floating(text(" when ")), D2)); @@ -1113,7 +1114,7 @@ lay_2(Node, Ctxt) -> _ -> {"fun(", ")"} end, - Ctxt1 = reset_prec(Ctxt), + Ctxt1 = (reset_prec(Ctxt))#ctxt{clause = undefined}, D1 = case erl_syntax:function_type_arguments(Node) of any_arity -> text("(...)"); diff --git a/lib/syntax_tools/test/syntax_tools_SUITE.erl b/lib/syntax_tools/test/syntax_tools_SUITE.erl index 9dbd0e302a..6b42f7a0a1 100644 --- a/lib/syntax_tools/test/syntax_tools_SUITE.erl +++ b/lib/syntax_tools/test/syntax_tools_SUITE.erl @@ -26,14 +26,14 @@ -export([app_test/1,appup_test/1,smoke_test/1,revert/1,revert_map/1, revert_map_type/1, t_abstract_type/1,t_erl_parse_type/1,t_type/1, t_epp_dodger/1, - t_comment_scan/1,t_igor/1,t_erl_tidy/1]). + t_comment_scan/1,t_igor/1,t_erl_tidy/1,t_prettypr/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [app_test,appup_test,smoke_test,revert,revert_map,revert_map_type, t_abstract_type,t_erl_parse_type,t_type,t_epp_dodger, - t_comment_scan,t_igor,t_erl_tidy]. + t_comment_scan,t_igor,t_erl_tidy,t_prettypr]. groups() -> []. @@ -300,6 +300,14 @@ t_comment_scan(Config) when is_list(Config) -> ok = test_comment_scan(Filenames,DataDir), ok. +t_prettypr(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), + Filenames = ["type_specs.erl", + "specs_and_funs.erl"], + ok = test_prettypr(Filenames,DataDir,PrivDir), + ok. + test_files(Config) -> DataDir = ?config(data_dir, Config), [ filename:join(DataDir,Filename) || Filename <- test_files() ]. @@ -307,7 +315,8 @@ test_files(Config) -> test_files() -> ["syntax_tools_SUITE_test_module.erl", "syntax_tools_test.erl", - "type_specs.erl"]. + "type_specs.erl", + "specs_and_funs.erl"]. t_igor(Config) when is_list(Config) -> DataDir = ?config(data_dir, Config), @@ -359,6 +368,27 @@ test_comment_scan([File|Files],DataDir) -> test_comment_scan(Files,DataDir). +test_prettypr([],_,_) -> ok; +test_prettypr([File|Files],DataDir,PrivDir) -> + Filename = filename:join(DataDir,File), + io:format("Parsing ~p~n", [Filename]), + {ok, Fs0} = epp:parse_file(Filename, [], []), + Fs = erl_syntax:form_list(Fs0), + PP = erl_prettypr:format(Fs, [{paper, 120}, {ribbon, 110}]), + io:put_chars(PP), + OutFile = filename:join(PrivDir, File), + ok = file:write_file(OutFile,iolist_to_binary(PP)), + io:format("Parsing OutFile: ~s~n", [OutFile]), + {ok, Fs2} = epp:parse_file(OutFile, [], []), + case [Error || {error, _} = Error <- Fs2] of + [] -> + ok; + Errors -> + ?t:fail(Errors) + end, + test_prettypr(Files,DataDir,PrivDir). + + test_epp_dodger([], _, _) -> ok; test_epp_dodger([Filename|Files],DataDir,PrivDir) -> io:format("Parsing ~p~n", [Filename]), diff --git a/lib/syntax_tools/test/syntax_tools_SUITE_data/specs_and_funs.erl b/lib/syntax_tools/test/syntax_tools_SUITE_data/specs_and_funs.erl new file mode 100644 index 0000000000..8dfeaf5a6b --- /dev/null +++ b/lib/syntax_tools/test/syntax_tools_SUITE_data/specs_and_funs.erl @@ -0,0 +1,18 @@ +-module(specs_and_funs). + +-export([my_apply/3, two/1]). + +%% OTP-15519, ERL-815 + +-spec my_apply(Fun, Arg, fun((A) -> A)) -> Result when + Fun :: fun((Arg) -> Result), + Arg :: any(), + Result :: any(). + +my_apply(Fun, Arg, _) -> + Fun(Arg). + +-spec two(fun((A) -> A)) -> fun((B) -> B). + +two(F) -> + F(fun(X) -> X end). diff --git a/lib/tools/src/tools.app.src b/lib/tools/src/tools.app.src index f8c6aa22cb..f0e0fc4bec 100644 --- a/lib/tools/src/tools.app.src +++ b/lib/tools/src/tools.app.src @@ -21,11 +21,13 @@ [{description, "DEVTOOLS CXC 138 16"}, {vsn, "%VSN%"}, {modules, [cover, + cprof, eprof, fprof, instrument, lcnt, make, + tags, xref, xref_base, xref_compiler, diff --git a/lib/xmerl/doc/src/notes.xml b/lib/xmerl/doc/src/notes.xml index a97036127e..7f6874e36b 100644 --- a/lib/xmerl/doc/src/notes.xml +++ b/lib/xmerl/doc/src/notes.xml @@ -32,6 +32,21 @@ <p>This document describes the changes made to the Xmerl application.</p> +<section><title>Xmerl 1.3.19</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>The charset detection parsing crash in some cases when + the XML directive is not syntactic correct.</p> + <p> + Own Id: OTP-15492 Aux Id: ERIERL-283 </p> + </item> + </list> + </section> + +</section> + <section><title>Xmerl 1.3.18</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -62,6 +77,21 @@ </section> +<section><title>Xmerl 1.3.16.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>The charset detection parsing crash in some cases when + the XML directive is not syntactic correct.</p> + <p> + Own Id: OTP-15492 Aux Id: ERIERL-283 </p> + </item> + </list> + </section> + +</section> + <section><title>Xmerl 1.3.16</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -1412,4 +1442,3 @@ </section> </section> </chapter> - diff --git a/lib/xmerl/src/xmerl_sax_parser.erl b/lib/xmerl/src/xmerl_sax_parser.erl index e383c4c349..fe836fd8cd 100644 --- a/lib/xmerl/src/xmerl_sax_parser.erl +++ b/lib/xmerl/src/xmerl_sax_parser.erl @@ -1,8 +1,8 @@ %%-------------------------------------------------------------------- %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2008-2017. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2008-2018. All Rights Reserved. +%% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at @@ -14,13 +14,13 @@ %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. -%% +%% %% %CopyrightEnd% %%---------------------------------------------------------------------- %% File : xmerl_sax_parser.erl %% Description : XML SAX parse API module. %% -%% Created : 4 Jun 2008 +%% Created : 4 Jun 2008 %%---------------------------------------------------------------------- -module(xmerl_sax_parser). @@ -72,9 +72,9 @@ file(Name,Options) -> CL = filename:absname(Dir), File = filename:basename(Name), ContinuationFun = fun default_continuation_cb/1, - Res = stream(<<>>, + Res = stream(<<>>, [{continuation_fun, ContinuationFun}, - {continuation_state, FD}, + {continuation_state, FD}, {current_location, CL}, {entity, File} |Options], @@ -101,39 +101,39 @@ stream(Xml, Options, InputType) when is_list(Xml), is_list(Options) -> State = parse_options(Options, initial_state()), case State#xmerl_sax_parser_state.file_type of dtd -> - xmerl_sax_parser_list:parse_dtd(Xml, + xmerl_sax_parser_list:parse_dtd(Xml, State#xmerl_sax_parser_state{encoding = list, input_type = InputType}); normal -> - xmerl_sax_parser_list:parse(Xml, + xmerl_sax_parser_list:parse(Xml, State#xmerl_sax_parser_state{encoding = list, input_type = InputType}) end; stream(Xml, Options, InputType) when is_binary(Xml), is_list(Options) -> - case parse_options(Options, initial_state()) of + case parse_options(Options, initial_state()) of {error, Reason} -> {error, Reason}; State -> - ParseFunction = + ParseFunction = case State#xmerl_sax_parser_state.file_type of dtd -> parse_dtd; normal -> parse end, - try + try {Xml1, State1} = detect_charset(Xml, State), parse_binary(Xml1, State1#xmerl_sax_parser_state{input_type = InputType}, ParseFunction) catch throw:{fatal_error, {State2, Reason}} -> - {fatal_error, + {fatal_error, { State2#xmerl_sax_parser_state.current_location, - State2#xmerl_sax_parser_state.entity, + State2#xmerl_sax_parser_state.entity, 1 }, - Reason, [], + Reason, [], State2#xmerl_sax_parser_state.event_state} end end. @@ -157,7 +157,7 @@ parse_binary(Xml, #xmerl_sax_parser_state{encoding={utf16,big}}=State, F) -> xmerl_sax_parser_utf16be:F(Xml, State); parse_binary(Xml, #xmerl_sax_parser_state{encoding=latin1}=State, F) -> xmerl_sax_parser_latin1:F(Xml, State); -parse_binary(_, #xmerl_sax_parser_state{encoding=Enc}, State) -> +parse_binary(_, #xmerl_sax_parser_state{encoding=Enc}, State) -> ?fatal_error(State, lists:flatten(io_lib:format("Charcter set ~p not supported", [Enc]))). %%---------------------------------------------------------------------- @@ -177,9 +177,9 @@ initial_state() -> %%---------------------------------------------------------------------- %% Function: parse_options(Options, State) %% Input: Options = [Option] -%% Option = {event_state, term()} | {event_fun, fun()} | +%% Option = {event_state, term()} | {event_fun, fun()} | %% {continuation_state, term()} | {continuation_fun, fun()} | -%% {encoding, Encoding} | {file_type, FT} +%% {encoding, Encoding} | {file_type, FT} %% FT = normal | dtd %% Encoding = utf8 | utf16le | utf16be | list | iso8859 %% State = #xmerl_sax_parser_state{} @@ -200,7 +200,7 @@ parse_options([{file_type, FT} |Options], State) when FT==normal; FT==dtd -> parse_options(Options, State#xmerl_sax_parser_state{file_type = FT}); parse_options([{encoding, E} |Options], State) -> case check_encoding_option(E) of - {error, Reason} -> + {error, Reason} -> {error, Reason}; Enc -> parse_options(Options, State#xmerl_sax_parser_state{encoding = Enc}) @@ -231,7 +231,7 @@ check_encoding_option(E) -> %% Description: Detects which character set is used in a binary stream. %%---------------------------------------------------------------------- detect_charset(<<>>, #xmerl_sax_parser_state{continuation_fun = undefined} = State) -> - ?fatal_error(State, "Can't detect character encoding due to lack of indata"); + ?fatal_error(State, "Can't detect character encoding due to lack of indata"); detect_charset(<<>>, State) -> cf(<<>>, State, fun detect_charset/2); detect_charset(Bytes, State) -> @@ -269,22 +269,14 @@ detect_charset_1(<<16#3C, 16#3F, 16#78, 16#6D>> = Xml, State) -> cf(Xml, State, fun detect_charset_1/2); detect_charset_1(<<16#3C, 16#3F, 16#78, 16#6D, 16#6C, Xml2/binary>>, State) -> {Xml3, State1} = read_until_end_of_xml_directive(Xml2, State), - case parse_xml_directive(Xml3) of - {error, Reason} -> - ?fatal_error(State, Reason); - AttrList -> - case lists:keysearch("encoding", 1, AttrList) of - {value, {_, E}} -> - case convert_encoding(E) of - {error, Reason} -> - ?fatal_error(State, Reason); - Enc -> - {<<16#3C, 16#3F, 16#78, 16#6D, 16#6C, Xml3/binary>>, - State1#xmerl_sax_parser_state{encoding=Enc}} - end; - _ -> - {<<16#3C, 16#3F, 16#78, 16#6D, 16#6C, Xml3/binary>>, State1} - end + AttrList = parse_xml_directive(Xml3, State), + case lists:keysearch("encoding", 1, AttrList) of + {value, {_, E}} -> + Enc = convert_encoding(E, State), + {<<16#3C, 16#3F, 16#78, 16#6D, 16#6C, Xml3/binary>>, + State1#xmerl_sax_parser_state{encoding=Enc}}; + _ -> + {<<16#3C, 16#3F, 16#78, 16#6D, 16#6C, Xml3/binary>>, State1} end; detect_charset_1(Xml, State) -> {Xml, State}. @@ -295,7 +287,7 @@ detect_charset_1(Xml, State) -> %% Output: utf8 | iso8859 %% Description: Converting 7,8 bit and utf8 encoding strings to internal format. %%---------------------------------------------------------------------- -convert_encoding(Enc) -> %% Just for 7,8 bit + utf8 +convert_encoding(Enc, State) -> %% Just for 7,8 bit + utf8 case string:to_lower(Enc) of "utf-8" -> utf8; "us-ascii" -> utf8; @@ -309,19 +301,19 @@ convert_encoding(Enc) -> %% Just for 7,8 bit + utf8 "iso-8859-7" -> latin1; "iso-8859-8" -> latin1; "iso-8859-9" -> latin1; - _ -> {error, "Unknown encoding: " ++ Enc} + _ -> ?fatal_error(State, "Unknown encoding: " ++ Enc) end. %%---------------------------------------------------------------------- %% Function: parse_xml_directive(Xml) %% Input: Xml = binary() %% Acc = list() -%% Output: +%% Output: %% Description: Parsing the xml declaration from the input stream. %%---------------------------------------------------------------------- -parse_xml_directive(<<C, Rest/binary>>) when ?is_whitespace(C) -> - parse_xml_directive_1(Rest, []). - +parse_xml_directive(<<C, Rest/binary>>, State) when ?is_whitespace(C) -> + parse_xml_directive_1(Rest, [], State). + %%---------------------------------------------------------------------- %% Function: parse_xml_directive_1(Xml, Acc) -> [{Name, Value}] %% Input: Xml = binary() @@ -331,20 +323,20 @@ parse_xml_directive(<<C, Rest/binary>>) when ?is_whitespace(C) -> %% Output: see above %% Description: Parsing the xml declaration from the input stream. %%---------------------------------------------------------------------- -parse_xml_directive_1(<<C, Rest/binary>>, Acc) when ?is_whitespace(C) -> - parse_xml_directive_1(Rest, Acc); -parse_xml_directive_1(<<"?>", _/binary>>, Acc) -> +parse_xml_directive_1(<<C, Rest/binary>>, Acc, State) when ?is_whitespace(C) -> + parse_xml_directive_1(Rest, Acc, State); +parse_xml_directive_1(<<"?>", _/binary>>, Acc, _State) -> Acc; -parse_xml_directive_1(<<C, Rest/binary>>, Acc) when 97 =< C, C =< 122 -> +parse_xml_directive_1(<<C, Rest/binary>>, Acc, State) when 97 =< C, C =< 122 -> {Name, Rest1} = parse_name(Rest, [C]), - Rest2 = parse_eq(Rest1), - {Value, Rest3} = parse_value(Rest2), - parse_xml_directive_1(Rest3, [{Name, Value} |Acc]); -parse_xml_directive_1(_, _) -> - {error, "Unknown attribute in xml directive"}. + Rest2 = parse_eq(Rest1, State), + {Value, Rest3} = parse_value(Rest2, State), + parse_xml_directive_1(Rest3, [{Name, Value} |Acc], State); +parse_xml_directive_1(_, _, State) -> + ?fatal_error(State, "Unknown attribute in xml directive"). %%---------------------------------------------------------------------- -%% Function: parse_xml_directive_1(Xml, Acc) -> Name +%% Function: parse_name(Xml, Acc) -> Name %% Input: Xml = binary() %% Acc = string() %% Output: Name = string() @@ -361,10 +353,12 @@ parse_name(Rest, Acc) -> %% Output: Rest = binary() %% Description: Reads an '=' from the stream. %%---------------------------------------------------------------------- -parse_eq(<<C, Rest/binary>>) when ?is_whitespace(C) -> - parse_eq(Rest); -parse_eq(<<"=", Rest/binary>>) -> - Rest. +parse_eq(<<C, Rest/binary>>, State) when ?is_whitespace(C) -> + parse_eq(Rest, State); +parse_eq(<<"=", Rest/binary>>, _State) -> + Rest; +parse_eq(_, State) -> + ?fatal_error(State, "expecting = or whitespace"). %%---------------------------------------------------------------------- %% Function: parse_value(Xml) -> {Value, Rest} @@ -373,10 +367,12 @@ parse_eq(<<"=", Rest/binary>>) -> %% Rest = binary() %% Description: Parsing an attribute value from the stream. %%---------------------------------------------------------------------- -parse_value(<<C, Rest/binary>>) when ?is_whitespace(C) -> - parse_value(Rest); -parse_value(<<C, Rest/binary>>) when C == $'; C == $" -> - parse_value_1(Rest, C, []). +parse_value(<<C, Rest/binary>>, State) when ?is_whitespace(C) -> + parse_value(Rest, State); +parse_value(<<C, Rest/binary>>, _State) when C == $'; C == $" -> + parse_value_1(Rest, C, []); +parse_value(_, State) -> + ?fatal_error(State, "\', \" or whitespace expected"). %%---------------------------------------------------------------------- %% Function: parse_value_1(Xml, Stop, Acc) -> {Value, Rest} @@ -431,7 +427,7 @@ read_until_end_of_xml_directive(Rest, State) -> nomatch -> case cf(Rest, State) of {<<>>, _} -> - ?fatal_error(State, "Can't detect character encoding due to lack of indata"); + ?fatal_error(State, "Can't detect character encoding due to lack of indata"); {NewBytes, NewState} -> read_until_end_of_xml_directive(NewBytes, NewState) end; @@ -450,9 +446,9 @@ read_until_end_of_xml_directive(Rest, State) -> %% input stream and calls the fun in NextCall. %%---------------------------------------------------------------------- cf(_Rest, #xmerl_sax_parser_state{continuation_fun = undefined} = State) -> - ?fatal_error(State, "Continuation function undefined"); + ?fatal_error(State, "Continuation function undefined"); cf(Rest, #xmerl_sax_parser_state{continuation_fun = CFun, continuation_state = CState} = State) -> - Result = + Result = try CFun(CState) catch @@ -463,9 +459,9 @@ cf(Rest, #xmerl_sax_parser_state{continuation_fun = CFun, continuation_state = C end, case Result of {<<>>, _} -> - ?fatal_error(State, "Can't detect character encoding due to lack of indata"); + ?fatal_error(State, "Can't detect character encoding due to lack of indata"); {NewBytes, NewContState} -> - {<<Rest/binary, NewBytes/binary>>, + {<<Rest/binary, NewBytes/binary>>, State#xmerl_sax_parser_state{continuation_state = NewContState}} end. @@ -479,10 +475,10 @@ cf(Rest, #xmerl_sax_parser_state{continuation_fun = CFun, continuation_state = C %% input stream and calls the fun in NextCall. %%---------------------------------------------------------------------- cf(_Rest, #xmerl_sax_parser_state{continuation_fun = undefined} = State, _) -> - ?fatal_error(State, "Continuation function undefined"); -cf(Rest, #xmerl_sax_parser_state{continuation_fun = CFun, continuation_state = CState} = State, + ?fatal_error(State, "Continuation function undefined"); +cf(Rest, #xmerl_sax_parser_state{continuation_fun = CFun, continuation_state = CState} = State, NextCall) -> - Result = + Result = try CFun(CState) catch @@ -493,8 +489,8 @@ cf(Rest, #xmerl_sax_parser_state{continuation_fun = CFun, continuation_state = C end, case Result of {<<>>, _} -> - ?fatal_error(State, "Can't detect character encoding due to lack of indata"); + ?fatal_error(State, "Can't detect character encoding due to lack of indata"); {NewBytes, NewContState} -> - NextCall(<<Rest/binary, NewBytes/binary>>, + NextCall(<<Rest/binary, NewBytes/binary>>, State#xmerl_sax_parser_state{continuation_state = NewContState}) end. diff --git a/lib/xmerl/test/xmerl_xsd_lib.erl b/lib/xmerl/test/xmerl_xsd_lib.erl index 6006cf500f..39300cd6cb 100644 --- a/lib/xmerl/test/xmerl_xsd_lib.erl +++ b/lib/xmerl/test/xmerl_xsd_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2016. All Rights Reserved. +%% Copyright Ericsson AB 2006-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -94,8 +94,9 @@ return_results2({NewFail, NewSuccess, NewMal, NewNotMal}, NumSucc, SkippedN, Tot _ -> io_lib:format("These ~p skipped tests were malicious, but succeeds now: ~p~n", [length(NewNotMal), NewNotMal]) end, - ct:comment(io_lib:format("~p successful tests, ~p skipped tests of totally ~p test cases. ~n" ++ - NFComm ++ NSComm ++ NMComm ++ NNMComm, [NumSucc, SkippedN, TotN])), + ct:comment(io_lib:format("~p successful tests, ~p skipped tests of totally ~p test cases. ~n~ts", + [NumSucc, SkippedN, TotN, + NFComm ++ NSComm ++ NMComm ++ NNMComm])), [] = NewFail. %% return_results2(Diff,{STErrs,STOther},{ITErrs,ITOther},TotN) -> diff --git a/lib/xmerl/vsn.mk b/lib/xmerl/vsn.mk index 3a266a56bd..b6486681c2 100644 --- a/lib/xmerl/vsn.mk +++ b/lib/xmerl/vsn.mk @@ -1 +1 @@ -XMERL_VSN = 1.3.18 +XMERL_VSN = 1.3.19 |