diff options
Diffstat (limited to 'lib')
55 files changed, 1325 insertions, 809 deletions
diff --git a/lib/common_test/test_server/configure.in b/lib/common_test/test_server/configure.in index 0511d126b4..e07bd4c2aa 100644 --- a/lib/common_test/test_server/configure.in +++ b/lib/common_test/test_server/configure.in @@ -459,11 +459,11 @@ dnl Freely inspired by AC_TRY_LINK. (Maybe better to create a dnl AC_LANG_JAVA instead...) AC_DEFUN(ERL_TRY_LINK_JAVA, [java_link='$JAVAC conftest.java 1>&AC_FD_CC' -changequote(�, �)dnl +changequote(, )dnl cat > conftest.java <<EOF -�$1� +$1 class conftest { public static void main(String[] args) { - �$2� + $2 ; return; }} EOF changequote([, ])dnl diff --git a/lib/compiler/src/beam_except.erl b/lib/compiler/src/beam_except.erl index 09925b2872..28c89782c9 100644 --- a/lib/compiler/src/beam_except.erl +++ b/lib/compiler/src/beam_except.erl @@ -225,7 +225,11 @@ moves_from_stack(nil, I, Acc) -> {reverse(Acc),I}; moves_from_stack({literal,[H|T]}, I, Acc) -> Cons = {cons,tag_literal(H),tag_literal(T)}, - moves_from_stack(Cons, I, Acc). + moves_from_stack(Cons, I, Acc); +moves_from_stack(_, _, _) -> + %% Not understood. Give up. + {[],-1}. + get_reg(R, Regs) -> case Regs of diff --git a/lib/compiler/src/beam_ssa_opt.erl b/lib/compiler/src/beam_ssa_opt.erl index f8e19d0aa7..6e548dd529 100644 --- a/lib/compiler/src/beam_ssa_opt.erl +++ b/lib/compiler/src/beam_ssa_opt.erl @@ -849,6 +849,7 @@ cse_expr(#b_set{op=Op,args=Args}=I) -> cse_suitable(#b_set{op=get_hd}) -> true; cse_suitable(#b_set{op=get_tl}) -> true; cse_suitable(#b_set{op=put_list}) -> true; +cse_suitable(#b_set{op=get_tuple_element}) -> true; cse_suitable(#b_set{op=put_tuple}) -> true; cse_suitable(#b_set{op={bif,tuple_size}}) -> %% Doing CSE for tuple_size/1 can prevent the diff --git a/lib/compiler/src/beam_ssa_type.erl b/lib/compiler/src/beam_ssa_type.erl index 5fbb679c6f..aa4720d222 100644 --- a/lib/compiler/src/beam_ssa_type.erl +++ b/lib/compiler/src/beam_ssa_type.erl @@ -24,7 +24,7 @@ -include("beam_ssa_opt.hrl"). -import(lists, [all/2,any/2,droplast/1,foldl/3,last/1,member/2, keyfind/3,partition/2,reverse/1,reverse/2, - seq/2,sort/1]). + seq/2,sort/1,split/2]). -define(UNICODE_INT, #t_integer{elements={0,16#10FFFF}}). @@ -358,6 +358,17 @@ simplify_call(I) -> I. %% Simplify a remote call to a pure BIF. simplify_remote_call(erlang, '++', [#b_literal{val=[]},Tl], _I) -> Tl; +simplify_remote_call(erlang, setelement, + [#b_literal{val=Pos}, + #b_literal{val=Tuple}, + #b_var{}=Value], I) + when is_integer(Pos), 1 =< Pos, Pos =< tuple_size(Tuple) -> + %% Position is a literal integer and the shape of the + %% tuple is known. + Els0 = [#b_literal{val=El} || El <- tuple_to_list(Tuple)], + {Bef,[_|Aft]} = split(Pos - 1, Els0), + Els = Bef ++ [Value|Aft], + I#b_set{op=put_tuple,args=Els}; simplify_remote_call(Mod, Name, Args0, I) -> case make_literal_list(Args0) of none -> @@ -873,6 +884,9 @@ type(call, [#b_remote{mod=#b_literal{val=Mod}, end; {erlang,'--',[_,_]} -> list; + {lists,F,Args} -> + Types = get_types(Args, Ts), + lists_function_type(F, Types); {math,_,_} -> case is_math_bif(Name, length(Args)) of false -> any; @@ -964,6 +978,70 @@ arith_op_type(Args, Ts) -> (_, _) -> none end, unknown, Types). +lists_function_type(F, Types) -> + case {F,Types} of + %% Functions that return booleans. + {all,[_,_]} -> + t_boolean(); + {any,[_,_]} -> + t_boolean(); + {keymember,[_,_,_]} -> + t_boolean(); + {member,[_,_]} -> + t_boolean(); + {prefix,[_,_]} -> + t_boolean(); + {suffix,[_,_]} -> + t_boolean(); + + %% Functions that return lists. + {dropwhile,[_,_]} -> + list; + {duplicate,[_,_]} -> + list; + {filter,[_,_]} -> + list; + {flatten,[_]} -> + list; + {map,[_Fun,List]} -> + same_length_type(List); + {MapFold,[_Fun,_Acc,List]} when MapFold =:= mapfoldl; + MapFold =:= mapfoldr -> + #t_tuple{size=2,exact=true, + elements=#{1=>same_length_type(List)}}; + {partition,[_,_]} -> + t_two_tuple(list, list); + {reverse,[List]} -> + same_length_type(List); + {sort,[List]} -> + same_length_type(List); + {splitwith,[_,_]} -> + t_two_tuple(list, list); + {takewhile,[_,_]} -> + list; + {unzip,[List]} -> + ListType = same_length_type(List), + t_two_tuple(ListType, ListType); + {usort,[List]} -> + same_length_type(List); + {zip,[_,_]} -> + list; + {zipwith,[_,_,_]} -> + list; + {_,_} -> + any + end. + +%% For a lists function that return a list of the same +%% length as the input list, return the type of the list. +same_length_type(cons) -> cons; +same_length_type(nil) -> nil; +same_length_type(_) -> list. + +t_two_tuple(Type1, Type2) -> + #t_tuple{size=2,exact=true, + elements=#{1=>Type1,2=>Type2}}. + %% will_succeed(TestOperation, Type) -> yes|no|maybe. %% Test whether TestOperation applied to an argument of type Type %% will succeed. Return yes, no, or maybe. diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index 8ca90870c4..5175be3ad5 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -2502,6 +2502,8 @@ call_return_type_1(erlang, '--', 2, _Vst) -> list; call_return_type_1(erlang, F, A, _) -> erlang_mod_return_type(F, A); +call_return_type_1(lists, F, A, Vst) -> + lists_mod_return_type(F, A, Vst); call_return_type_1(math, F, A, _) -> math_mod_return_type(F, A); call_return_type_1(M, F, A, _) when is_atom(M), is_atom(F), is_integer(A), A >= 0 -> @@ -2540,6 +2542,64 @@ math_mod_return_type(fmod, 2) -> {float,[]}; math_mod_return_type(pi, 0) -> {float,[]}; math_mod_return_type(F, A) when is_atom(F), is_integer(A), A >= 0 -> term. +lists_mod_return_type(dropwhile, 2, _Vst) -> + list; +lists_mod_return_type(duplicate, 2, _Vst) -> + list; +lists_mod_return_type(filter, 2, _Vst) -> + list; +lists_mod_return_type(flatten, 2, _Vst) -> + list; +lists_mod_return_type(map, 2, Vst) -> + same_length_type({x,1}, Vst); +lists_mod_return_type(MF, 3, Vst) when MF =:= mapfoldl; MF =:= mapfoldr -> + ListType = same_length_type({x,2}, Vst), + {tuple,2,#{1=>ListType}}; +lists_mod_return_type(partition, 2, _Vst) -> + two_tuple(list, list); +lists_mod_return_type(reverse, 1, Vst) -> + same_length_type({x,0}, Vst); +lists_mod_return_type(seq, 2, _Vst) -> + list; +lists_mod_return_type(seq, 3, _Vst) -> + list; +lists_mod_return_type(sort, 1, Vst) -> + same_length_type({x,0}, Vst); +lists_mod_return_type(sort, 2, Vst) -> + same_length_type({x,1}, Vst); +lists_mod_return_type(splitwith, 2, _Vst) -> + two_tuple(list, list); +lists_mod_return_type(takewhile, 2, _Vst) -> + list; +lists_mod_return_type(unzip, 1, Vst) -> + ListType = same_length_type({x,0}, Vst), + two_tuple(ListType, ListType); +lists_mod_return_type(usort, 1, Vst) -> + same_length_type({x,0}, Vst); +lists_mod_return_type(usort, 2, Vst) -> + same_length_type({x,1}, Vst); +lists_mod_return_type(zip, 2, _Vst) -> + list; +lists_mod_return_type(zip3, 3, _Vst) -> + list; +lists_mod_return_type(zipwith, 3, _Vst) -> + list; +lists_mod_return_type(zipwith3, 4, _Vst) -> + list; +lists_mod_return_type(_, _, _) -> + term. + +two_tuple(Type1, Type2) -> + {tuple,2,#{1=>Type1,2=>Type2}}. + +same_length_type(Reg, Vst) -> + case get_term_type(Reg, Vst) of + {literal,[_|_]} -> cons; + cons -> cons; + nil -> nil; + _ -> list + end. + check_limit({x,X}) when is_integer(X), X < 1023 -> %% Note: x(1023) is reserved for use by the BEAM loader. ok; diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl index 43c99be982..7e219da0af 100644 --- a/lib/compiler/src/sys_core_fold.erl +++ b/lib/compiler/src/sys_core_fold.erl @@ -961,18 +961,12 @@ fold_lit_args(Call, Module, Name, Args0) -> %% fold_non_lit_args(Call, erlang, is_boolean, [Arg], Sub) -> eval_is_boolean(Call, Arg, Sub); -fold_non_lit_args(Call, erlang, element, [Arg1,Arg2], Sub) -> - eval_element(Call, Arg1, Arg2, Sub); fold_non_lit_args(Call, erlang, length, [Arg], _) -> eval_length(Call, Arg); fold_non_lit_args(Call, erlang, '++', [Arg1,Arg2], _) -> eval_append(Call, Arg1, Arg2); fold_non_lit_args(Call, lists, append, [Arg1,Arg2], _) -> eval_append(Call, Arg1, Arg2); -fold_non_lit_args(Call, erlang, setelement, [Arg1,Arg2,Arg3], _) -> - eval_setelement(Call, Arg1, Arg2, Arg3); -fold_non_lit_args(Call, erlang, is_record, [Arg1,Arg2,Arg3], Sub) -> - eval_is_record(Call, Arg1, Arg2, Arg3, Sub); fold_non_lit_args(Call, erlang, is_function, [Arg1], Sub) -> eval_is_function_1(Call, Arg1, Sub); fold_non_lit_args(Call, erlang, is_function, [Arg1,Arg2], Sub) -> @@ -1141,96 +1135,6 @@ eval_append(Call, #c_cons{anno=Anno,hd=H,tl=T}, List) -> eval_append(Call, X, Y) -> Call#c_call{args=[X,Y]}. %Rebuild call arguments. -%% eval_element(Call, Pos, Tuple, Types) -> Val. -%% Evaluates element/2 if the position Pos is a literal and -%% the shape of the tuple Tuple is known. -%% -eval_element(Call, #c_literal{val=Pos}, Tuple, Types) - when is_integer(Pos) -> - case get_type(Tuple, Types) of - none -> - Call; - Type -> - Es = case cerl:is_c_tuple(Type) of - false -> []; - true -> cerl:tuple_es(Type) - end, - if - 1 =< Pos, Pos =< length(Es) -> - El = lists:nth(Pos, Es), - try - cerl:set_ann(pat_to_expr(El), [compiler_generated]) - catch - throw:impossible -> - Call - end; - true -> - %% Index outside tuple or not a tuple. - eval_failure(Call, badarg) - end - end; -eval_element(Call, Pos, Tuple, Sub) -> - case is_int_type(Pos, Sub) =:= no orelse - is_tuple_type(Tuple, Sub) =:= no of - true -> - eval_failure(Call, badarg); - false -> - Call - end. - -%% eval_is_record(Call, Var, Tag, Size, Types) -> Val. -%% Evaluates is_record/3 using type information. -%% -eval_is_record(Call, Term, #c_literal{val=NeededTag}, - #c_literal{val=Size}, Types) -> - case get_type(Term, Types) of - none -> - Call; - Type -> - Es = case cerl:is_c_tuple(Type) of - false -> []; - true -> cerl:tuple_es(Type) - end, - case Es of - [#c_literal{val=Tag}|_] -> - Bool = Tag =:= NeededTag andalso - length(Es) =:= Size, - #c_literal{val=Bool}; - _ -> - #c_literal{val=false} - end - end; -eval_is_record(Call, _, _, _, _) -> Call. - -%% eval_setelement(Call, Pos, Tuple, NewVal) -> Core. -%% Evaluates setelement/3 if position Pos is an integer -%% and the shape of the tuple Tuple is known. -%% -eval_setelement(Call, #c_literal{val=Pos}, Tuple, NewVal) - when is_integer(Pos) -> - case cerl:is_data(Tuple) of - false -> - Call; - true -> - Es0 = case cerl:is_c_tuple(Tuple) of - false -> []; - true -> cerl:tuple_es(Tuple) - end, - if - 1 =< Pos, Pos =< length(Es0) -> - Es = eval_setelement_1(Pos, Es0, NewVal), - cerl:update_c_tuple(Tuple, Es); - true -> - eval_failure(Call, badarg) - end - end; -eval_setelement(Call, _, _, _) -> Call. - -eval_setelement_1(1, [_|T], NewVal) -> - [NewVal|T]; -eval_setelement_1(Pos, [H|T], NewVal) when Pos > 1 -> - [H|eval_setelement_1(Pos-1, T, NewVal)]. - %% eval_failure(Call, Reason) -> Core. %% Warn for a call that will fail and replace the call with %% a call to erlang:error(Reason). @@ -1290,16 +1194,15 @@ clause(#c_clause{pats=Ps0}=Cl, Cexpr, Ctxt, Sub0) -> end. clause_1(#c_clause{guard=G0,body=B0}=Cl, Ps1, Cexpr, Ctxt, Sub1) -> - Sub2 = update_types(Cexpr, Ps1, Sub1), GSub = case {Cexpr,Ps1,G0} of {_,_,#c_literal{}} -> %% No need for substitution tricks when the guard %% does not contain any variables. - Sub2; + Sub1; {#c_var{name='_'},_,_} -> %% In a 'receive', Cexpr is the variable '_', which represents the %% message being matched. We must NOT do any extra substiutions. - Sub2; + Sub1; {#c_var{},[#c_var{}=Var],_} -> %% The idea here is to optimize expressions such as %% @@ -1321,16 +1224,16 @@ clause_1(#c_clause{guard=G0,body=B0}=Cl, Ps1, Cexpr, Ctxt, Sub1) -> %% case cerl:is_c_fname(Cexpr) of false -> - sub_set_var(Var, Cexpr, Sub2); + sub_set_var(Var, Cexpr, Sub1); true -> %% We must not copy funs, and especially not into guards. - Sub2 + Sub1 end; _ -> - Sub2 + Sub1 end, G1 = guard(G0, GSub), - B1 = body(B0, Ctxt, Sub2), + B1 = body(B0, Ctxt, Sub1), Cl#c_clause{pats=Ps1,guard=G1,body=B1}. %% let_substs(LetVars, LetArg, Sub) -> {[Var],[Val],Sub}. @@ -1414,8 +1317,7 @@ pattern(#c_binary{segments=V0}=Pat, Isub, Osub0) -> {Pat#c_binary{segments=V1},Osub1}; pattern(#c_alias{var=V0,pat=P0}=Pat, Isub, Osub0) -> {V1,Osub1} = pattern(V0, Isub, Osub0), - {P1,Osub2} = pattern(P0, Isub, Osub1), - Osub = update_types(V1, [P1], Osub2), + {P1,Osub} = pattern(P0, Isub, Osub1), {Pat#c_alias{var=V1,pat=P1},Osub}. map_pair_pattern_list(Ps0, Isub, Osub0) -> @@ -2137,14 +2039,9 @@ case_expand_var(E, #sub{t=Tdb}) -> %% encountered. coerce_to_data(C) -> - case cerl:is_c_alias(C) of - false -> - case cerl:is_data(C) orelse cerl:is_c_var(C) of - true -> C; - false -> throw(impossible) - end; - true -> - coerce_to_data(cerl:alias_pat(C)) + case cerl:is_data(C) orelse cerl:is_c_var(C) of + true -> C; + false -> throw(impossible) end. %% case_opt_nomatch(E, Clauses, LitExpr) -> Clauses' @@ -3140,14 +3037,6 @@ is_int_type(Var, Sub) -> C -> yes_no(cerl:is_c_int(C)) end. --spec is_tuple_type(cerl:cerl(), sub()) -> yes_no_maybe(). - -is_tuple_type(Var, Sub) -> - case get_type(Var, Sub) of - none -> maybe; - C -> yes_no(cerl:is_c_tuple(C)) - end. - yes_no(true) -> yes; yes_no(false) -> no. @@ -3209,27 +3098,23 @@ returns_integer(_, _) -> false. %% update_types(Expr, Pattern, Sub) -> Sub' %% Update the type database. --spec update_types(cerl:cerl(), [type_info()], sub()) -> sub(). +-spec update_types(cerl:c_var(), [type_info()], sub()) -> sub(). -update_types(Expr, Pat, #sub{t=Tdb0}=Sub) -> - Tdb = update_types_1(Expr, Pat, Tdb0), +update_types(#c_var{name=V}, Pat, #sub{t=Tdb0}=Sub) -> + Tdb = update_types_1(V, Pat, Tdb0), Sub#sub{t=Tdb}. -update_types_1(#c_var{name=V}, Pat, Types) -> - update_types_2(V, Pat, Types); -update_types_1(_, _, Types) -> Types. - -update_types_2(V, [#c_tuple{}=P], Types) -> +update_types_1(V, [#c_tuple{}=P], Types) -> Types#{V=>P}; -update_types_2(V, [#c_literal{val=Bool}], Types) when is_boolean(Bool) -> +update_types_1(V, [#c_literal{val=Bool}], Types) when is_boolean(Bool) -> Types#{V=>bool}; -update_types_2(V, [#c_fun{vars=Vars}], Types) -> +update_types_1(V, [#c_fun{vars=Vars}], Types) -> Types#{V=>{'fun',length(Vars)}}; -update_types_2(V, [#c_var{name={_,Arity}}], Types) -> +update_types_1(V, [#c_var{name={_,Arity}}], Types) -> Types#{V=>{'fun',Arity}}; -update_types_2(V, [Type], Types) when is_atom(Type) -> +update_types_1(V, [Type], Types) when is_atom(Type) -> Types#{V=>Type}; -update_types_2(_, _, Types) -> Types. +update_types_1(_, _, Types) -> Types. %% kill_types(V, Tdb) -> Tdb' %% Kill any entries that references the variable, diff --git a/lib/compiler/test/warnings_SUITE.erl b/lib/compiler/test/warnings_SUITE.erl index c5d0bf8420..70b7100451 100644 --- a/lib/compiler/test/warnings_SUITE.erl +++ b/lib/compiler/test/warnings_SUITE.erl @@ -240,19 +240,7 @@ guard(Config) when is_list(Config) -> {4,sys_core_fold,nomatch_guard}, {6,sys_core_fold,no_clause_match}, {6,sys_core_fold,nomatch_guard}, - {6,sys_core_fold,{eval_failure,badarg}}, - {8,sys_core_fold,no_clause_match}, - {8,sys_core_fold,nomatch_guard}, - {8,sys_core_fold,{eval_failure,badarg}}, - {9,sys_core_fold,no_clause_match}, - {9,sys_core_fold,nomatch_guard}, - {9,sys_core_fold,{eval_failure,badarg}}, - {10,sys_core_fold,no_clause_match}, - {10,sys_core_fold,nomatch_guard}, - {10,sys_core_fold,{eval_failure,badarg}}, - {11,sys_core_fold,no_clause_match}, - {11,sys_core_fold,nomatch_guard}, - {11,sys_core_fold,{eval_failure,badarg}} + {6,sys_core_fold,{eval_failure,badarg}} ]}}], [] = run(Config, Ts), diff --git a/lib/crypto/c_src/Makefile.in b/lib/crypto/c_src/Makefile.in index 508e1c40ee..e1e7f71538 100644 --- a/lib/crypto/c_src/Makefile.in +++ b/lib/crypto/c_src/Makefile.in @@ -75,6 +75,7 @@ CRYPTO_OBJS = $(OBJDIR)/crypto$(TYPEMARKER).o \ $(OBJDIR)/aead$(TYPEMARKER).o \ $(OBJDIR)/aes$(TYPEMARKER).o \ $(OBJDIR)/algorithms$(TYPEMARKER).o \ + $(OBJDIR)/api_ng$(TYPEMARKER).o \ $(OBJDIR)/atoms$(TYPEMARKER).o \ $(OBJDIR)/block$(TYPEMARKER).o \ $(OBJDIR)/bn$(TYPEMARKER).o \ diff --git a/lib/crypto/c_src/aead.c b/lib/crypto/c_src/aead.c index c6f4cf52b1..3ee04f1be9 100644 --- a/lib/crypto/c_src/aead.c +++ b/lib/crypto/c_src/aead.c @@ -20,17 +20,19 @@ #include "aead.h" #include "aes.h" +#include "cipher.h" ERL_NIF_TERM aead_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Type,Key,Iv,AAD,In) */ #if defined(HAVE_AEAD) + const struct cipher_type_t *cipherp; EVP_CIPHER_CTX *ctx = NULL; 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, ret; - int len, ctx_ctrl_set_ivlen, ctx_ctrl_get_tag; + int len, ctx_ctrl_set_ivlen, ctx_ctrl_get_tag, ctx_ctrl_set_tag; type = argv[0]; @@ -55,77 +57,19 @@ ERL_NIF_TERM aead_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) || aad.size > INT_MAX) goto bad_arg; - /* Use cipher_type some day. Must check block_encrypt|decrypt first */ -#if defined(HAVE_GCM) - if (type == atom_aes_gcm) { - if (iv.size == 0) - goto bad_arg; - if (tag_len < 1 || tag_len > 16) - goto bad_arg; - - ctx_ctrl_set_ivlen = EVP_CTRL_GCM_SET_IVLEN; - ctx_ctrl_get_tag = EVP_CTRL_GCM_GET_TAG; - - switch (key.size) { - case 16: - cipher = EVP_aes_128_gcm(); - break; - case 24: - cipher = EVP_aes_192_gcm(); - break; - case 32: - cipher = EVP_aes_256_gcm(); - break; - default: - goto bad_arg; - } - } else -#endif -#if defined(HAVE_CCM) - if (type == atom_aes_ccm) { - if (iv.size < 7 || iv.size > 13) - goto bad_arg; - if (tag_len < 4 || tag_len > 16) - goto bad_arg; - if ((tag_len & 1) != 0) - goto bad_arg; - - ctx_ctrl_set_ivlen = EVP_CTRL_CCM_SET_IVLEN; - ctx_ctrl_get_tag = EVP_CTRL_CCM_GET_TAG; - - switch (key.size) { - case 16: - cipher = EVP_aes_128_ccm(); - break; - case 24: - cipher = EVP_aes_192_ccm(); - break; - case 32: - cipher = EVP_aes_256_ccm(); - break; - default: - goto bad_arg; - } - } else -#endif -#if defined(HAVE_CHACHA20_POLY1305) - if (type == atom_chacha20_poly1305) { - if (key.size != 32) - goto bad_arg; - if (iv.size < 1 || iv.size > 16) - goto bad_arg; - if (tag_len != 16) - goto bad_arg; - - ctx_ctrl_set_ivlen = EVP_CTRL_AEAD_SET_IVLEN; - ctx_ctrl_get_tag = EVP_CTRL_AEAD_GET_TAG; - - cipher = EVP_chacha20_poly1305(); - - } else -#endif + if ((cipherp = get_cipher_type(type, key.size)) == NULL) + goto bad_arg; + if (cipherp->flags & NON_EVP_CIPHER) + goto bad_arg; + if (! (cipherp->flags & AEAD_CIPHER) ) + goto bad_arg; + if ((cipher = cipherp->cipher.p) == NULL) return enif_raise_exception(env, atom_notsup); - + + ctx_ctrl_set_ivlen = cipherp->extra.aead.ctx_ctrl_set_ivlen; + ctx_ctrl_get_tag = cipherp->extra.aead.ctx_ctrl_get_tag; + ctx_ctrl_set_tag = cipherp->extra.aead.ctx_ctrl_set_tag; + if ((ctx = EVP_CIPHER_CTX_new()) == NULL) goto err; @@ -136,7 +80,7 @@ ERL_NIF_TERM aead_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) #if defined(HAVE_CCM) if (type == atom_aes_ccm) { - if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, (int)tag_len, NULL) != 1) + if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_set_tag, (int)tag_len, NULL) != 1) goto err; if (EVP_EncryptInit_ex(ctx, NULL, NULL, key.data, iv.data) != 1) goto err; @@ -144,10 +88,10 @@ ERL_NIF_TERM aead_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) goto err; } else #endif - { - if (EVP_EncryptInit_ex(ctx, NULL, NULL, key.data, iv.data) != 1) - goto err; - } + { + if (EVP_EncryptInit_ex(ctx, NULL, NULL, key.data, iv.data) != 1) + goto err; + } if (EVP_EncryptUpdate(ctx, NULL, &len, aad.data, (int)aad.size) != 1) goto err; @@ -190,6 +134,7 @@ 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[]) {/* (Type,Key,Iv,AAD,In,Tag) */ #if defined(HAVE_AEAD) + const struct cipher_type_t *cipherp; EVP_CIPHER_CTX *ctx = NULL; const EVP_CIPHER *cipher = NULL; ErlNifBinary key, iv, aad, in, tag; @@ -225,70 +170,18 @@ ERL_NIF_TERM aead_decrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) || aad.size > INT_MAX) goto bad_arg; - /* Use cipher_type some day. Must check block_encrypt|decrypt first */ -#if defined(HAVE_GCM) - if (type == atom_aes_gcm) { - if (iv.size == 0) - goto bad_arg; - - ctx_ctrl_set_ivlen = EVP_CTRL_GCM_SET_IVLEN; - ctx_ctrl_set_tag = EVP_CTRL_GCM_SET_TAG; - - switch (key.size) { - case 16: - cipher = EVP_aes_128_gcm(); - break; - case 24: - cipher = EVP_aes_192_gcm(); - break; - case 32: - cipher = EVP_aes_256_gcm(); - break; - default: - goto bad_arg; - } - } else -#endif -#if defined(HAVE_CCM) - if (type == atom_aes_ccm) { - if (iv.size == 0) - goto bad_arg; - - ctx_ctrl_set_ivlen = EVP_CTRL_CCM_SET_IVLEN; - ctx_ctrl_set_tag = EVP_CTRL_CCM_SET_TAG; - - switch (key.size) { - case 16: - cipher = EVP_aes_128_ccm(); - break; - case 24: - cipher = EVP_aes_192_ccm(); - break; - case 32: - cipher = EVP_aes_256_ccm(); - break; - default: - goto bad_arg; - } - } else -#endif -#if defined(HAVE_CHACHA20_POLY1305) - if (type == atom_chacha20_poly1305) { - if (key.size != 32) - goto bad_arg; - if (iv.size < 1 || iv.size > 16) - goto bad_arg; - if (tag.size != 16) - goto bad_arg; - - ctx_ctrl_set_ivlen = EVP_CTRL_AEAD_SET_IVLEN; - ctx_ctrl_set_tag = EVP_CTRL_AEAD_SET_TAG; - - cipher = EVP_chacha20_poly1305(); - } else -#endif + if ((cipherp = get_cipher_type(type, key.size)) == NULL) + goto bad_arg; + if (cipherp->flags & NON_EVP_CIPHER) + goto bad_arg; + if ( !(cipherp->flags & AEAD_CIPHER) ) + goto bad_arg; + if ((cipher = cipherp->cipher.p) == NULL) return enif_raise_exception(env, atom_notsup); + ctx_ctrl_set_ivlen = cipherp->extra.aead.ctx_ctrl_set_ivlen; + ctx_ctrl_set_tag = cipherp->extra.aead.ctx_ctrl_set_tag; + if ((outp = enif_make_new_binary(env, in.size, &out)) == NULL) goto err; @@ -301,27 +194,26 @@ ERL_NIF_TERM aead_decrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) #if defined(HAVE_CCM) if (type == atom_aes_ccm) { - if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, (int)tag.size, tag.data) != 1) + if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_set_tag, (int)tag.size, tag.data) != 1) + goto err; + if (EVP_DecryptInit_ex(ctx, NULL, NULL, key.data, iv.data) != 1) goto err; - } -#endif - - if (EVP_DecryptInit_ex(ctx, NULL, NULL, key.data, iv.data) != 1) - goto err; - -#if defined(HAVE_CCM) - if (type == atom_aes_ccm) { if (EVP_DecryptUpdate(ctx, NULL, &len, NULL, (int)in.size) != 1) goto err; } + else #endif + { + if (EVP_DecryptInit_ex(ctx, NULL, NULL, key.data, iv.data) != 1) + goto err; + } if (EVP_DecryptUpdate(ctx, NULL, &len, aad.data, (int)aad.size) != 1) goto err; if (EVP_DecryptUpdate(ctx, outp, &len, in.data, (int)in.size) != 1) goto err; -#if defined(HAVE_GCM) || defined(HAVE_CHACHA20_POLY1305) +#if defined(HAVE_GCM) if (type == atom_aes_gcm) { if (EVP_CIPHER_CTX_ctrl(ctx, ctx_ctrl_set_tag, (int)tag.size, tag.data) != 1) goto err; diff --git a/lib/crypto/c_src/aes.c b/lib/crypto/c_src/aes.c index 2f30ec8a58..ee2bb70fb7 100644 --- a/lib/crypto/c_src/aes.c +++ b/lib/crypto/c_src/aes.c @@ -166,8 +166,6 @@ ERL_NIF_TERM aes_ige_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv } -/* 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) */ @@ -279,27 +277,31 @@ ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM ERL_NIF_TERM aes_ctr_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Key, IVec) */ + ASSERT(argc == 2); + + return aes_ctr_stream_init_compat(env, argv[0], argv[1]); +} + + +ERL_NIF_TERM aes_ctr_stream_init_compat(ErlNifEnv* env, const ERL_NIF_TERM key_term, const ERL_NIF_TERM iv_term) +{ ErlNifBinary key_bin, ivec_bin; ERL_NIF_TERM ecount_bin; unsigned char *outp; - - ASSERT(argc == 2); - - if (!enif_inspect_iolist_as_binary(env, argv[0], &key_bin)) + + if (!enif_inspect_iolist_as_binary(env, key_term, &key_bin)) goto bad_arg; if (key_bin.size != 16 && key_bin.size != 24 && key_bin.size != 32) goto bad_arg; - if (!enif_inspect_binary(env, argv[1], &ivec_bin)) + if (!enif_inspect_binary(env, iv_term, &ivec_bin)) goto bad_arg; if (ivec_bin.size != 16) goto bad_arg; - if ((outp = enif_make_new_binary(env, AES_BLOCK_SIZE, &ecount_bin)) == NULL) goto err; - memset(outp, 0, AES_BLOCK_SIZE); - return enif_make_tuple4(env, argv[0], argv[1], ecount_bin, enif_make_int(env, 0)); + return enif_make_tuple4(env, key_term, iv_term, ecount_bin, enif_make_int(env, 0)); bad_arg: err: @@ -307,6 +309,14 @@ ERL_NIF_TERM aes_ctr_stream_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar } ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ASSERT(argc == 2); + + return aes_ctr_stream_encrypt_compat(env, argv[0], argv[1]); +} + + +ERL_NIF_TERM aes_ctr_stream_encrypt_compat(ErlNifEnv* env, const ERL_NIF_TERM state_arg, const ERL_NIF_TERM data_arg) {/* ({Key, IVec, ECount, Num}, Data) */ ErlNifBinary key_bin, ivec_bin, text_bin, ecount_bin; AES_KEY aes_key; @@ -318,9 +328,7 @@ ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM unsigned char * ecount2_buf; unsigned char *outp; - ASSERT(argc == 2); - - if (!enif_get_tuple(env, argv[0], &state_arity, &state_term)) + if (!enif_get_tuple(env, state_arg, &state_arity, &state_term)) goto bad_arg; if (state_arity != 4) goto bad_arg; @@ -338,7 +346,7 @@ ERL_NIF_TERM aes_ctr_stream_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM goto bad_arg; if (!enif_get_uint(env, state_term[3], &num)) goto bad_arg; - if (!enif_inspect_iolist_as_binary(env, argv[1], &text_bin)) + if (!enif_inspect_iolist_as_binary(env, data_arg, &text_bin)) goto bad_arg; /* NOTE: This function returns 0 on success unlike most OpenSSL functions */ diff --git a/lib/crypto/c_src/aes.h b/lib/crypto/c_src/aes.h index 09c984f84a..527d041410 100644 --- a/lib/crypto/c_src/aes.h +++ b/lib/crypto/c_src/aes.h @@ -26,8 +26,13 @@ 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[]); +#if !defined(HAVE_EVP_AES_CTR) +ERL_NIF_TERM aes_ctr_stream_init_compat(ErlNifEnv* env, const ERL_NIF_TERM key_term, const ERL_NIF_TERM iv_term); +ERL_NIF_TERM aes_ctr_stream_encrypt_compat(ErlNifEnv* env, const ERL_NIF_TERM state_arg, const ERL_NIF_TERM data_arg); +#endif #ifdef HAVE_GCM_EVP_DECRYPT_BUG ERL_NIF_TERM aes_gcm_decrypt_NO_EVP(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); diff --git a/lib/crypto/c_src/algorithms.c b/lib/crypto/c_src/algorithms.c index a5bf248ea0..06cd109fc1 100644 --- a/lib/crypto/c_src/algorithms.c +++ b/lib/crypto/c_src/algorithms.c @@ -19,13 +19,12 @@ */ #include "algorithms.h" +#include "cipher.h" static unsigned int algo_hash_cnt, algo_hash_fips_cnt; static ERL_NIF_TERM algo_hash[14]; /* increase when extending the list */ static unsigned int algo_pubkey_cnt, algo_pubkey_fips_cnt; static ERL_NIF_TERM algo_pubkey[12]; /* increase when extending the list */ -static unsigned int algo_cipher_cnt, algo_cipher_fips_cnt; -static ERL_NIF_TERM algo_cipher[25]; /* increase when extending the list */ static unsigned int algo_mac_cnt, algo_mac_fips_cnt; static ERL_NIF_TERM algo_mac[3]; /* increase when extending the list */ static unsigned int algo_curve_cnt, algo_curve_fips_cnt; @@ -92,56 +91,7 @@ void init_algorithms_types(ErlNifEnv* env) #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"); @@ -290,7 +240,6 @@ void init_algorithms_types(ErlNifEnv* env) // 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)); @@ -303,14 +252,12 @@ ERL_NIF_TERM algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) unsigned int hash_cnt = fips_mode ? algo_hash_fips_cnt : algo_hash_cnt; unsigned int pubkey_cnt = fips_mode ? algo_pubkey_fips_cnt : algo_pubkey_cnt; - unsigned int cipher_cnt = fips_mode ? algo_cipher_fips_cnt : algo_cipher_cnt; unsigned int mac_cnt = fips_mode ? algo_mac_fips_cnt : algo_mac_cnt; unsigned int curve_cnt = fips_mode ? algo_curve_fips_cnt : algo_curve_cnt; unsigned int rsa_opts_cnt = fips_mode ? algo_rsa_opts_fips_cnt : algo_rsa_opts_cnt; #else unsigned int hash_cnt = algo_hash_cnt; unsigned int pubkey_cnt = algo_pubkey_cnt; - unsigned int cipher_cnt = algo_cipher_cnt; unsigned int mac_cnt = algo_mac_cnt; unsigned int curve_cnt = algo_curve_cnt; unsigned int rsa_opts_cnt = algo_rsa_opts_cnt; @@ -318,7 +265,7 @@ ERL_NIF_TERM algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 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), + cipher_types_as_list(env), 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/api_ng.c b/lib/crypto/c_src/api_ng.c new file mode 100644 index 0000000000..c4114d1626 --- /dev/null +++ b/lib/crypto/c_src/api_ng.c @@ -0,0 +1,223 @@ +/* + * %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 "api_ng.h" +#include "aes.h" +#include "cipher.h" + +/* + * A unified set of functions for encryption/decryption. + * + * EXPERIMENTAL!! + * + */ +ERL_NIF_TERM ng_crypto_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + + + +/* Try better error messages in new functions */ +#define ERROR_Term(Env, ReasonTerm) enif_make_tuple2((Env), atom_error, (ReasonTerm)) +#define ERROR_Str(Env, ReasonString) ERROR_Term((Env), enif_make_string((Env),(ReasonString),(ERL_NIF_LATIN1))) + +/* Initializes state for (de)encryption + */ +ERL_NIF_TERM ng_crypto_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Cipher, Key, IVec, Encrypt) % if no IV for the Cipher, set IVec = <<>> + */ + ErlNifBinary key_bin, ivec_bin; + unsigned char *iv = NULL; + struct evp_cipher_ctx *ctx; + const struct cipher_type_t *cipherp; + const EVP_CIPHER *cipher; + ERL_NIF_TERM enc_flg_arg, ret; + int enc; + unsigned iv_len; + + enc_flg_arg = argv[argc-1]; + if (enc_flg_arg == atom_true) + enc = 1; + else if (enc_flg_arg == atom_false) + enc = 0; + else if (enc_flg_arg == atom_undefined) + /* For compat funcs in crypto.erl */ + enc = -1; + else + return ERROR_Str(env, "Bad enc flag"); + + if (!enif_inspect_binary(env, argv[1], &key_bin)) + return ERROR_Str(env, "Bad key"); + + if (!(cipherp = get_cipher_type(argv[0], key_bin.size))) + return ERROR_Str(env, "Unknown cipher or bad key size"); + + if (FORBIDDEN_IN_FIPS(cipherp)) + return enif_raise_exception(env, atom_notsup); + + if (enc == -1) + return atom_undefined; + + if (!(cipher = cipherp->cipher.p)) { +#if !defined(HAVE_EVP_AES_CTR) + if (cipherp->flags & AES_CTR_COMPAT) + return aes_ctr_stream_init_compat(env, argv[1], argv[2]); + else +#endif + return enif_raise_exception(env, atom_notsup); + } + +#ifdef HAVE_ECB_IVEC_BUG + if (cipherp->flags & ECB_BUG_0_9_8L) + iv_len = 0; /* <= 0.9.8l returns faulty ivec length */ + else +#endif + iv_len = EVP_CIPHER_iv_length(cipher); + + if (iv_len) { + if (!enif_inspect_binary(env, argv[2], &ivec_bin)) + return ERROR_Str(env, "Bad iv type"); + + if (iv_len != ivec_bin.size) + return ERROR_Str(env, "Bad iv size"); + + iv = ivec_bin.data; + } + + if ((ctx = enif_alloc_resource(evp_cipher_ctx_rtype, sizeof(struct evp_cipher_ctx))) == NULL) + return ERROR_Str(env, "Can't allocate resource"); + + ctx->ctx = EVP_CIPHER_CTX_new(); + if (! ctx->ctx) + return ERROR_Str(env, "Can't allocate context"); + + if (!EVP_CipherInit_ex(ctx->ctx, cipher, NULL, NULL, NULL, enc)) { + enif_release_resource(ctx); + return ERROR_Str(env, "Can't initialize context, step 1"); + } + + if (!EVP_CIPHER_CTX_set_key_length(ctx->ctx, (int)key_bin.size)) { + enif_release_resource(ctx); + return ERROR_Str(env, "Can't initialize context, key_length"); + } + + if (EVP_CIPHER_type(cipher) == NID_rc2_cbc) { + if (key_bin.size > INT_MAX / 8) { + enif_release_resource(ctx); + return ERROR_Str(env, "To large rc2_cbc key"); + } + if (!EVP_CIPHER_CTX_ctrl(ctx->ctx, EVP_CTRL_SET_RC2_KEY_BITS, (int)key_bin.size * 8, NULL)) { + enif_release_resource(ctx); + return ERROR_Str(env, "ctrl rc2_cbc key"); + } + } + + if (!EVP_CipherInit_ex(ctx->ctx, NULL, NULL, key_bin.data, iv, enc)) { + enif_release_resource(ctx); + return ERROR_Str(env, "Can't initialize key and/or iv"); + } + + EVP_CIPHER_CTX_set_padding(ctx->ctx, 0); + + ret = enif_make_resource(env, ctx); + enif_release_resource(ctx); + return ret; +} + +ERL_NIF_TERM ng_crypto_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context, Data) + (Context, Data, IV) */ + struct evp_cipher_ctx *ctx; + ErlNifBinary in_data_bin, ivec_bin, out_data_bin; + int out_len, block_size; + +#if !defined(HAVE_EVP_AES_CTR) + const ERL_NIF_TERM *state_term; + int state_arity; + + if (enif_get_tuple(env, argv[0], &state_arity, &state_term) && (state_arity == 4)) { + return aes_ctr_stream_encrypt_compat(env, argv[0], argv[1]); + } +#endif + + if (!enif_get_resource(env, argv[0], evp_cipher_ctx_rtype, (void**)&ctx)) + return ERROR_Str(env, "Bad 1:st arg"); + + if (!enif_inspect_binary(env, argv[1], &in_data_bin) ) + return ERROR_Str(env, "Bad 2:nd arg"); + + /* arg[1] was checked by the caller */ + ASSERT(in_data_bin.size =< INT_MAX); + + block_size = EVP_CIPHER_CTX_block_size(ctx->ctx); + if (in_data_bin.size % (size_t)block_size != 0) + return ERROR_Str(env, "Data not a multiple of block size"); + + if (argc==3) { + if (!enif_inspect_iolist_as_binary(env, argv[2], &ivec_bin)) + return ERROR_Str(env, "Not binary IV"); + + if (ivec_bin.size > INT_MAX) + return ERROR_Str(env, "Too big IV"); + + if (!EVP_CipherInit_ex(ctx->ctx, NULL, NULL, NULL, ivec_bin.data, -1)) + return ERROR_Str(env, "Can't set IV"); + } + + if (!enif_alloc_binary((size_t)in_data_bin.size+block_size, &out_data_bin)) + return ERROR_Str(env, "Can't allocate outdata"); + + if (!EVP_CipherUpdate(ctx->ctx, out_data_bin.data, &out_len, in_data_bin.data, in_data_bin.size)) + return ERROR_Str(env, "Can't update"); + + if (!enif_realloc_binary(&out_data_bin, (size_t)out_len)) + return ERROR_Str(env, "Can't reallocate"); + + CONSUME_REDS(env, in_data_bin); + return enif_make_binary(env, &out_data_bin); +} + + +ERL_NIF_TERM ng_crypto_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context, Data) + (Context, Data, IV) */ + int i; + ErlNifBinary data_bin; + ERL_NIF_TERM new_argv[3]; + + ASSERT(argc =< 3); + + if (!enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) + return ERROR_Str(env, "iodata expected as data"); + + if (data_bin.size > INT_MAX) + return ERROR_Str(env, "to long data"); + + for (i=0; i<argc; i++) new_argv[i] = argv[i]; + new_argv[1] = enif_make_binary(env, &data_bin); + + /* Run long jobs on a dirty scheduler to not block the current emulator thread */ + if (data_bin.size > MAX_BYTES_TO_NIF) { + return enif_schedule_nif(env, "ng_crypto_update", + ERL_NIF_DIRTY_JOB_CPU_BOUND, + ng_crypto_update, argc, new_argv); + } + + return ng_crypto_update(env, argc, new_argv); +} + diff --git a/lib/crypto/c_src/api_ng.h b/lib/crypto/c_src/api_ng.h new file mode 100644 index 0000000000..a3b40fe7fc --- /dev/null +++ b/lib/crypto/c_src/api_ng.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_API_NG_H__ +#define E_API_NG_H__ 1 + +#include "common.h" + +ERL_NIF_TERM ng_crypto_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM ng_crypto_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_AES_H__ */ diff --git a/lib/crypto/c_src/atoms.c b/lib/crypto/c_src/atoms.c index 5f19327197..2e417da7f4 100644 --- a/lib/crypto/c_src/atoms.c +++ b/lib/crypto/c_src/atoms.c @@ -31,12 +31,6 @@ 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; @@ -48,7 +42,6 @@ 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; @@ -64,14 +57,6 @@ ERL_NIF_TERM atom_aes_gcm; #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; @@ -99,16 +84,6 @@ 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 HAVE_BLAKE2 ERL_NIF_TERM atom_blake2b; @@ -116,14 +91,6 @@ ERL_NIF_TERM atom_blake2s; #endif #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; @@ -169,18 +136,11 @@ int init_atoms(ErlNifEnv *env, const ERL_NIF_TERM fips_mode, const ERL_NIF_TERM 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"); @@ -196,14 +156,6 @@ int init_atoms(ErlNifEnv *env, const ERL_NIF_TERM fips_mode, const ERL_NIF_TERM #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"); @@ -214,6 +166,7 @@ int init_atoms(ErlNifEnv *env, const ERL_NIF_TERM fips_mode, const ERL_NIF_TERM 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"); @@ -234,29 +187,8 @@ int init_atoms(ErlNifEnv *env, const ERL_NIF_TERM fips_mode, const ERL_NIF_TERM #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 HAVE_BLAKE2 - atom_blake2b = enif_make_atom(env,"blake2b"); - atom_blake2s = enif_make_atom(env,"blake2s"); -#endif #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"); diff --git a/lib/crypto/c_src/atoms.h b/lib/crypto/c_src/atoms.h index 32f5ec856c..f15523d865 100644 --- a/lib/crypto/c_src/atoms.h +++ b/lib/crypto/c_src/atoms.h @@ -35,12 +35,6 @@ 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; @@ -52,7 +46,6 @@ 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; @@ -68,14 +61,6 @@ extern ERL_NIF_TERM atom_aes_gcm; #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; @@ -103,30 +88,8 @@ 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 HAVE_BLAKE2 -extern ERL_NIF_TERM atom_blake2b; -extern ERL_NIF_TERM atom_blake2s; -#endif #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; diff --git a/lib/crypto/c_src/block.c b/lib/crypto/c_src/block.c index d88ee8dba7..0a4fd72623 100644 --- a/lib/crypto/c_src/block.c +++ b/lib/crypto/c_src/block.c @@ -24,7 +24,7 @@ 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 struct cipher_type_t *cipherp; const EVP_CIPHER *cipher; ErlNifBinary key, ivec, text; EVP_CIPHER_CTX *ctx = NULL; @@ -41,36 +41,42 @@ ERL_NIF_TERM block_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] goto bad_arg; if ((cipherp = get_cipher_type(argv[0], key.size)) == NULL) goto bad_arg; + if (cipherp->flags & (NON_EVP_CIPHER | AEAD_CIPHER)) + goto bad_arg; if (!enif_inspect_iolist_as_binary(env, argv[argc - 2], &text)) goto bad_arg; if (text.size > INT_MAX) goto bad_arg; + if (FORBIDDEN_IN_FIPS(cipherp)) + return enif_raise_exception(env, atom_notsup); if ((cipher = cipherp->cipher.p) == NULL) 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); + if (cipherp->flags & AES_CFBx) { + 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); + } } - 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 */ + if (cipherp->flags & ECB_BUG_0_9_8L) + ivec_size = 0; /* 0.9.8l returns faulty ivec_size */ #endif + if (ivec_size < 0) goto bad_arg; diff --git a/lib/crypto/c_src/cipher.c b/lib/crypto/c_src/cipher.c index 449e636037..f8e44b228a 100644 --- a/lib/crypto/c_src/cipher.c +++ b/lib/crypto/c_src/cipher.c @@ -28,51 +28,122 @@ static struct cipher_type_t cipher_types[] = { - {{"rc2_cbc"}, #ifndef OPENSSL_NO_RC2 - {&EVP_rc2_cbc} + {{"rc2_cbc"}, {&EVP_rc2_cbc}, 0, NO_FIPS_CIPHER}, #else - {NULL} + {{"rc2_cbc"}, {NULL}, 0, NO_FIPS_CIPHER}, #endif - ,0}, - {{"des_cbc"}, {COND_NO_DES_PTR(&EVP_des_cbc)}, 0}, - {{"des_cfb"}, {COND_NO_DES_PTR(&EVP_des_cfb8)}, 0}, - {{"des_ecb"}, {COND_NO_DES_PTR(&EVP_des_ecb)}, 0}, - {{"des_ede3_cbc"}, {COND_NO_DES_PTR(&EVP_des_ede3_cbc)}, 0}, - {{"des_ede3_cbf"}, /* Misspelled, retained */ -#ifdef HAVE_DES_ede3_cfb_encrypt - {COND_NO_DES_PTR(&EVP_des_ede3_cfb8)} +#ifndef OPENSSL_NO_RC4 + {{"rc4"}, {&EVP_rc4}, 0, NO_FIPS_CIPHER}, #else - {NULL} + {{"rc4"}, {NULL}, 0, NO_FIPS_CIPHER}, #endif - ,0}, - {{"des_ede3_cfb"}, + {{"des_cbc"}, {COND_NO_DES_PTR(&EVP_des_cbc)}, 0, NO_FIPS_CIPHER}, + {{"des_cfb"}, {COND_NO_DES_PTR(&EVP_des_cfb8)}, 0, NO_FIPS_CIPHER}, + {{"des_ecb"}, {COND_NO_DES_PTR(&EVP_des_ecb)}, 0, NO_FIPS_CIPHER | ECB_BUG_0_9_8L}, + + {{"des_ede3_cbc"}, {COND_NO_DES_PTR(&EVP_des_ede3_cbc)}, 0, 0}, + #ifdef HAVE_DES_ede3_cfb_encrypt - {COND_NO_DES_PTR(&EVP_des_ede3_cfb8)} + {{"des_ede3_cfb"}, {COND_NO_DES_PTR(&EVP_des_ede3_cfb8)}, 0, 0}, +#else + {{"des_ede3_cfb"}, {NULL}, 0, 0}, +#endif + + {{"blowfish_cbc"}, {&EVP_bf_cbc}, 0, NO_FIPS_CIPHER}, + {{"blowfish_cfb64"}, {&EVP_bf_cfb64}, 0, NO_FIPS_CIPHER}, + {{"blowfish_ofb64"}, {&EVP_bf_ofb}, 0, NO_FIPS_CIPHER}, + {{"blowfish_ecb"}, {&EVP_bf_ecb}, 0, NO_FIPS_CIPHER | ECB_BUG_0_9_8L}, + + {{"aes_cbc"}, {&EVP_aes_128_cbc}, 16, 0}, + {{"aes_cbc"}, {&EVP_aes_192_cbc}, 24, 0}, + {{"aes_cbc"}, {&EVP_aes_256_cbc}, 32, 0}, + + {{"aes_128_cbc"}, {&EVP_aes_128_cbc}, 16, 0}, + {{"aes_192_cbc"}, {&EVP_aes_192_cbc}, 24, 0}, + {{"aes_256_cbc"}, {&EVP_aes_256_cbc}, 32, 0}, + + {{"aes_cfb8"}, {&EVP_aes_128_cfb8}, 16, NO_FIPS_CIPHER | AES_CFBx}, + {{"aes_cfb8"}, {&EVP_aes_192_cfb8}, 24, NO_FIPS_CIPHER | AES_CFBx}, + {{"aes_cfb8"}, {&EVP_aes_256_cfb8}, 32, NO_FIPS_CIPHER | AES_CFBx}, + + {{"aes_cfb128"}, {&EVP_aes_128_cfb128}, 16, NO_FIPS_CIPHER | AES_CFBx}, + {{"aes_cfb128"}, {&EVP_aes_192_cfb128}, 24, NO_FIPS_CIPHER | AES_CFBx}, + {{"aes_cfb128"}, {&EVP_aes_256_cfb128}, 32, NO_FIPS_CIPHER | AES_CFBx}, + + {{"aes_ecb"}, {&EVP_aes_128_ecb}, 16, ECB_BUG_0_9_8L}, + {{"aes_ecb"}, {&EVP_aes_192_ecb}, 24, ECB_BUG_0_9_8L}, + {{"aes_ecb"}, {&EVP_aes_256_ecb}, 32, ECB_BUG_0_9_8L}, + +#if defined(HAVE_EVP_AES_CTR) + {{"aes_128_ctr"}, {&EVP_aes_128_ctr}, 16, 0}, + {{"aes_192_ctr"}, {&EVP_aes_192_ctr}, 24, 0}, + {{"aes_256_ctr"}, {&EVP_aes_256_ctr}, 32, 0}, + {{"aes_ctr"}, {&EVP_aes_128_ctr}, 16, 0}, + {{"aes_ctr"}, {&EVP_aes_192_ctr}, 24, 0}, + {{"aes_ctr"}, {&EVP_aes_256_ctr}, 32, 0}, +#else + {{"aes_128_ctr"}, {NULL}, 16, AES_CTR_COMPAT}, + {{"aes_192_ctr"}, {NULL}, 24, AES_CTR_COMPAT}, + {{"aes_256_ctr"}, {NULL}, 32, AES_CTR_COMPAT}, + {{"aes_ctr"}, {NULL}, 0, AES_CTR_COMPAT}, +#endif + +#if defined(HAVE_CHACHA20) + {{"chacha20"}, {&EVP_chacha20}, 32, NO_FIPS_CIPHER}, +#else + {{"chacha20"}, {NULL}, 0, NO_FIPS_CIPHER}, +#endif + + /*==== AEAD ciphers ====*/ +#if defined(HAVE_CHACHA20_POLY1305) + {{"chacha20_poly1305"}, {&EVP_chacha20_poly1305}, 0, NO_FIPS_CIPHER | AEAD_CIPHER, {{EVP_CTRL_AEAD_SET_IVLEN,EVP_CTRL_AEAD_GET_TAG,EVP_CTRL_AEAD_SET_TAG}}}, +#else + {{"chacha20_poly1305"}, {NULL}, 0, NO_FIPS_CIPHER | AEAD_CIPHER, {{0,0,0}}}, +#endif + +#if defined(HAVE_GCM) + {{"aes_gcm"}, {&EVP_aes_128_gcm}, 16, AEAD_CIPHER, {{EVP_CTRL_GCM_SET_IVLEN,EVP_CTRL_GCM_GET_TAG,EVP_CTRL_GCM_SET_TAG}}}, + {{"aes_gcm"}, {&EVP_aes_192_gcm}, 24, AEAD_CIPHER, {{EVP_CTRL_GCM_SET_IVLEN,EVP_CTRL_GCM_GET_TAG,EVP_CTRL_GCM_SET_TAG}}}, + {{"aes_gcm"}, {&EVP_aes_256_gcm}, 32, AEAD_CIPHER, {{EVP_CTRL_GCM_SET_IVLEN,EVP_CTRL_GCM_GET_TAG,EVP_CTRL_GCM_SET_TAG}}}, + {{"aes_128_gcm"}, {&EVP_aes_128_gcm}, 16, AEAD_CIPHER, {{EVP_CTRL_GCM_SET_IVLEN,EVP_CTRL_GCM_GET_TAG,EVP_CTRL_GCM_SET_TAG}}}, + {{"aes_192_gcm"}, {&EVP_aes_192_gcm}, 24, AEAD_CIPHER, {{EVP_CTRL_GCM_SET_IVLEN,EVP_CTRL_GCM_GET_TAG,EVP_CTRL_GCM_SET_TAG}}}, + {{"aes_256_gcm"}, {&EVP_aes_256_gcm}, 32, AEAD_CIPHER, {{EVP_CTRL_GCM_SET_IVLEN,EVP_CTRL_GCM_GET_TAG,EVP_CTRL_GCM_SET_TAG}}}, +#else + {{"aes_gcm"}, {NULL}, 0, AEAD_CIPHER, {{0,0,0}}}, + {{"aes_128_gcm"}, {NULL}, 16, AEAD_CIPHER, {{0,0,0}}}, + {{"aes_192_gcm"}, {NULL}, 24, AEAD_CIPHER, {{0,0,0}}}, + {{"aes_256_gcm"}, {NULL}, 32, AEAD_CIPHER, {{0,0,0}}}, +#endif + +#if defined(HAVE_CCM) + {{"aes_ccm"}, {&EVP_aes_128_ccm}, 16, AEAD_CIPHER, {{EVP_CTRL_CCM_SET_IVLEN,EVP_CTRL_CCM_GET_TAG,EVP_CTRL_CCM_SET_TAG}}}, + {{"aes_ccm"}, {&EVP_aes_192_ccm}, 24, AEAD_CIPHER, {{EVP_CTRL_CCM_SET_IVLEN,EVP_CTRL_CCM_GET_TAG,EVP_CTRL_CCM_SET_TAG}}}, + {{"aes_ccm"}, {&EVP_aes_256_ccm}, 32, AEAD_CIPHER, {{EVP_CTRL_CCM_SET_IVLEN,EVP_CTRL_CCM_GET_TAG,EVP_CTRL_CCM_SET_TAG}}}, + {{"aes_128_ccm"}, {&EVP_aes_128_ccm}, 16, AEAD_CIPHER, {{EVP_CTRL_CCM_SET_IVLEN,EVP_CTRL_CCM_GET_TAG,EVP_CTRL_CCM_SET_TAG}}}, + {{"aes_192_ccm"}, {&EVP_aes_192_ccm}, 24, AEAD_CIPHER, {{EVP_CTRL_CCM_SET_IVLEN,EVP_CTRL_CCM_GET_TAG,EVP_CTRL_CCM_SET_TAG}}}, + {{"aes_256_ccm"}, {&EVP_aes_256_ccm}, 32, AEAD_CIPHER, {{EVP_CTRL_CCM_SET_IVLEN,EVP_CTRL_CCM_GET_TAG,EVP_CTRL_CCM_SET_TAG}}}, #else - {NULL} + {{"aes_ccm"}, {NULL}, 0, AEAD_CIPHER, {{0,0,0}}}, + {{"aes_128_ccm"}, {NULL}, 16, AEAD_CIPHER, {{0,0,0}}}, + {{"aes_192_ccm"}, {NULL}, 24, AEAD_CIPHER, {{0,0,0}}}, + {{"aes_256_ccm"}, {NULL}, 32, AEAD_CIPHER, {{0,0,0}}}, +#endif + + /*==== Specialy handled ciphers, only for inclusion in algorithm's list ====*/ +#ifdef HAVE_AES_IGE + {{"aes_ige256"}, {NULL}, 0, NO_FIPS_CIPHER | NON_EVP_CIPHER}, #endif - ,0}, - {{"blowfish_cbc"}, {&EVP_bf_cbc}, 0}, - {{"blowfish_cfb64"}, {&EVP_bf_cfb64}, 0}, - {{"blowfish_ofb64"}, {&EVP_bf_ofb}, 0}, - {{"blowfish_ecb"}, {&EVP_bf_ecb}, 0}, - {{"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}, 0}, - {{"aes_cbc256"}, {&EVP_aes_256_cbc}, 0}, - {{"aes_cfb8"}, {&EVP_aes_128_cfb8}, 0}, - {{"aes_cfb128"}, {&EVP_aes_128_cfb128}, 0}, - {{"aes_ecb"}, {&EVP_aes_128_ecb}, 16}, - {{"aes_ecb"}, {&EVP_aes_192_ecb}, 24}, - {{"aes_ecb"}, {&EVP_aes_256_ecb}, 32}, - {{NULL},{NULL},0} + + /*==== End of list ==== */ + + {{NULL},{NULL},0,0} }; -#ifdef HAVE_EVP_AES_CTR ErlNifResourceType* evp_cipher_ctx_rtype; +static size_t num_cipher_types = 0; + static void evp_cipher_ctx_dtor(ErlNifEnv* env, struct evp_cipher_ctx* ctx) { if (ctx == NULL) return; @@ -80,46 +151,79 @@ static void evp_cipher_ctx_dtor(ErlNifEnv* env, struct evp_cipher_ctx* ctx) { if (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) goto err; -#endif return 1; -#ifdef HAVE_EVP_AES_CTR err: PRINTF_ERR0("CRYPTO: Could not open resource type 'EVP_CIPHER_CTX'"); return 0; -#endif } void init_cipher_types(ErlNifEnv* env) { struct cipher_type_t* p = cipher_types; + num_cipher_types = 0; for (p = cipher_types; p->type.str; p++) { + num_cipher_types++; 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 */ + + qsort(cipher_types, num_cipher_types, sizeof(cipher_types[0]), cmp_cipher_types); } -struct cipher_type_t* get_cipher_type(ERL_NIF_TERM type, size_t key_len) +const 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; - } + struct cipher_type_t key; + + key.type.atom = type; + key.key_len = key_len; + + return bsearch(&key, cipher_types, num_cipher_types, sizeof(cipher_types[0]), cmp_cipher_types); +} + + +int cmp_cipher_types(const void *keyp, const void *elemp) { + const struct cipher_type_t *key = keyp; + const struct cipher_type_t *elem = elemp; + + if (key->type.atom < elem->type.atom) return -1; + else if (key->type.atom > elem->type.atom) return 1; + else /* key->type.atom == elem->type.atom */ + if (!elem->key_len || key->key_len == elem->key_len) return 0; + else if (key->key_len < elem->key_len) return -1; + else return 1; +} + + +ERL_NIF_TERM cipher_types_as_list(ErlNifEnv* env) +{ + struct cipher_type_t* p; + ERL_NIF_TERM prev, hd; + + hd = enif_make_list(env, 0); + prev = atom_undefined; + + for (p = cipher_types; (p->type.atom & (p->type.atom != atom_false)); p++) { + if ((prev != p->type.atom) && + ((p->cipher.p != NULL) || + (p->flags & (NON_EVP_CIPHER|AES_CTR_COMPAT)) ) && /* Special handling. Bad indeed... */ + ! FORBIDDEN_IN_FIPS(p) + ) + hd = enif_make_list_cell(env, p->type.atom, hd); + prev = p->type.atom; } - return NULL; + + return hd; } diff --git a/lib/crypto/c_src/cipher.h b/lib/crypto/c_src/cipher.h index 3fb27f0ba3..6b43afea99 100644 --- a/lib/crypto/c_src/cipher.h +++ b/lib/crypto/c_src/cipher.h @@ -32,19 +32,42 @@ struct cipher_type_t { 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 */ + size_t key_len; /* != 0 to also match on key_len */ + unsigned flags; + union { + struct aead_ctrl {int ctx_ctrl_set_ivlen, ctx_ctrl_get_tag, ctx_ctrl_set_tag;} aead; + } extra; }; -#ifdef HAVE_EVP_AES_CTR +/* masks in the flags field if cipher_type_t */ +#define NO_FIPS_CIPHER 1 +#define AES_CFBx 2 +#define ECB_BUG_0_9_8L 4 +#define AEAD_CIPHER 8 +#define NON_EVP_CIPHER 16 +#define AES_CTR_COMPAT 32 + + +#ifdef FIPS_SUPPORT +/* May have FIPS support, must check dynamically if it is enabled */ +# define FORBIDDEN_IN_FIPS(P) (((P)->flags & NO_FIPS_CIPHER) && FIPS_mode()) +#else +/* No FIPS support since the symbol FIPS_SUPPORT is undefined */ +# define FORBIDDEN_IN_FIPS(P) 0 +#endif + 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); +const struct cipher_type_t* get_cipher_type(ERL_NIF_TERM type, size_t key_len); + +int cmp_cipher_types(const void *keyp, const void *elemp); + +ERL_NIF_TERM cipher_types_as_list(ErlNifEnv* env); #endif /* E_CIPHER_H__ */ diff --git a/lib/crypto/c_src/cmac.c b/lib/crypto/c_src/cmac.c index 196b7476e3..49e67ccf29 100644 --- a/lib/crypto/c_src/cmac.c +++ b/lib/crypto/c_src/cmac.c @@ -24,7 +24,7 @@ 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 struct cipher_type_t *cipherp; const EVP_CIPHER *cipher; CMAC_CTX *ctx = NULL; ErlNifBinary key; @@ -40,9 +40,13 @@ ERL_NIF_TERM cmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) goto bad_arg; if ((cipherp = get_cipher_type(argv[0], key.size)) == NULL) goto bad_arg; + if (cipherp->flags & (NON_EVP_CIPHER | AEAD_CIPHER)) + goto bad_arg; if (!enif_inspect_iolist_as_binary(env, argv[2], &data)) goto bad_arg; + if (FORBIDDEN_IN_FIPS(cipherp)) + return enif_raise_exception(env, atom_notsup); if ((cipher = cipherp->cipher.p) == NULL) return enif_raise_exception(env, atom_notsup); diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c index 03f11c9059..06439c34b2 100644 --- a/lib/crypto/c_src/crypto.c +++ b/lib/crypto/c_src/crypto.c @@ -28,6 +28,7 @@ #include "aead.h" #include "aes.h" #include "algorithms.h" +#include "api_ng.h" #include "block.h" #include "bn.h" #include "chacha20.h" @@ -83,6 +84,9 @@ static ErlNifFunc nif_funcs[] = { {"aes_ctr_stream_init", 2, aes_ctr_stream_init, 0}, {"aes_ctr_stream_encrypt", 2, aes_ctr_stream_encrypt, 0}, {"aes_ctr_stream_decrypt", 2, aes_ctr_stream_encrypt, 0}, + {"ng_crypto_init_nif", 4, ng_crypto_init_nif, 0}, + {"ng_crypto_update_nif", 2, ng_crypto_update_nif, 0}, + {"ng_crypto_update_nif", 3, ng_crypto_update_nif, 0}, {"strong_rand_bytes_nif", 1, strong_rand_bytes_nif, 0}, {"strong_rand_range_nif", 1, strong_rand_range_nif, 0}, {"rand_uniform_nif", 2, rand_uniform_nif, 0}, @@ -132,7 +136,6 @@ static ErlNifFunc nif_funcs[] = { {"engine_get_id_nif", 1, engine_get_id_nif, 0}, {"engine_get_name_nif", 1, engine_get_name_nif, 0}, {"engine_get_all_methods_nif", 0, engine_get_all_methods_nif, 0} - }; ERL_NIF_INIT(crypto,nif_funcs,load,NULL,upgrade,unload) diff --git a/lib/crypto/c_src/engine.c b/lib/crypto/c_src/engine.c index 6692ccd734..7ffbb9e70d 100644 --- a/lib/crypto/c_src/engine.c +++ b/lib/crypto/c_src/engine.c @@ -26,6 +26,9 @@ struct engine_ctx { char *id; }; +#define ERROR_Term(Env, ReasonTerm) enif_make_tuple2((Env), atom_error, (ReasonTerm)) +#define ERROR_Atom(Env, ReasonString) ERROR_Term((Env), enif_make_atom((Env),(ReasonString))) + static ErlNifResourceType* engine_ctx_rtype; static int get_engine_load_cmd_list(ErlNifEnv* env, const ERL_NIF_TERM term, char **cmds, int i); @@ -136,7 +139,7 @@ ERL_NIF_TERM engine_by_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ if ((engine = ENGINE_by_id(engine_id)) == NULL) { PRINTF_ERR0("engine_by_id_nif Leaved: {error, bad_engine_id}"); - ret = enif_make_tuple2(env, atom_error, atom_bad_engine_id); + ret = ERROR_Atom(env, "bad_engine_id"); goto done; } @@ -179,7 +182,7 @@ ERL_NIF_TERM engine_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] goto bad_arg; if (!ENGINE_init(ctx->engine)) - return enif_make_tuple2(env, atom_error, atom_engine_init_failed); + return ERROR_Atom(env, "engine_init_failed"); return atom_ok; @@ -306,7 +309,7 @@ ERL_NIF_TERM engine_ctrl_cmd_strings_nif(ErlNifEnv* env, int argc, const ERL_NIF goto done; cmd_failed: - ret = enif_make_tuple2(env, atom_error, atom_ctrl_cmd_failed); + ret = ERROR_Atom(env, "ctrl_cmd_failed"); done: if (cmds_loaded) { @@ -344,7 +347,7 @@ ERL_NIF_TERM engine_add_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) return enif_make_badarg(env); failed: - return enif_make_tuple2(env, atom_error, atom_add_engine_failed); + return ERROR_Atom(env, "add_engine_failed"); #else return atom_notsup; @@ -371,7 +374,7 @@ ERL_NIF_TERM engine_remove_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv return enif_make_badarg(env); failed: - return enif_make_tuple2(env, atom_error, atom_remove_engine_failed); + return ERROR_Atom(env, "remove_engine_failed"); #else return atom_notsup; #endif @@ -466,7 +469,7 @@ ERL_NIF_TERM engine_register_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar break; #endif default: - return enif_make_tuple2(env, atom_error, atom_engine_method_not_supported); + return ERROR_Atom(env, "engine_method_not_supported"); } return atom_ok; @@ -475,7 +478,7 @@ ERL_NIF_TERM engine_register_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar return enif_make_badarg(env); failed: - return enif_make_tuple2(env, atom_error, atom_register_engine_failed); + return ERROR_Atom(env, "register_engine_failed"); #else return atom_notsup; diff --git a/lib/crypto/c_src/pkey.c b/lib/crypto/c_src/pkey.c index 4e76f817bc..567e8df08a 100644 --- a/lib/crypto/c_src/pkey.c +++ b/lib/crypto/c_src/pkey.c @@ -110,7 +110,7 @@ static int get_pkey_sign_digest(ErlNifEnv *env, ERL_NIF_TERM algorithm, goto bad_arg; if (tpl_terms[0] != atom_digest) goto bad_arg; - if (!enif_inspect_binary(env, tpl_terms[1], &tbs_bin)) + if (!enif_inspect_iolist_as_binary(env, tpl_terms[1], &tbs_bin)) goto bad_arg; if (tbs_bin.size > INT_MAX) goto bad_arg; @@ -123,14 +123,14 @@ static int get_pkey_sign_digest(ErlNifEnv *env, ERL_NIF_TERM algorithm, tbs = tbs_bin.data; tbslen = tbs_bin.size; } else if (md == NULL) { - if (!enif_inspect_binary(env, data, &tbs_bin)) + if (!enif_inspect_iolist_as_binary(env, data, &tbs_bin)) goto bad_arg; /* 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)) + if (!enif_inspect_iolist_as_binary(env, data, &tbs_bin)) goto bad_arg; /* We have the cleartext in tbs_bin and the hash algo info in md */ diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml index e0794a080e..83e10c4c78 100644 --- a/lib/crypto/doc/src/crypto.xml +++ b/lib/crypto/doc/src/crypto.xml @@ -193,10 +193,12 @@ <datatype_title>Ciphers</datatype_title> <datatype> <name name="stream_cipher"/> + <name name="stream_cipher_iv"/> + <name name="stream_cipher_no_iv"/> <desc> <p>Stream ciphers for - <seealso marker="#stream_encrypt-2">stream_encrypt/2</seealso> and - <seealso marker="#stream_decrypt-2">stream_decrypt/2</seealso> . + <seealso marker="#stream_init-3">stream_init/3</seealso> and + <seealso marker="#stream_init-2">stream_init/2</seealso> . </p> </desc> </datatype> @@ -214,6 +216,18 @@ </datatype> <datatype> + <name name="alias_cfb"/> + <name name="alias_cbc"/> + <desc> + <p>Names that are replaced by more common names. They may deprecated in futer releases.</p> + <p><c>des3_cbc</c> and <c>des_ede3</c> should be replaced by <c>des_ede3_cbc</c></p> + <p><c>des_ede3_cbf</c>, <c>des3_cbf</c> and <c>des3_cfb</c> should be replaced by <c>des_ede3_cfb</c>.</p> + <p><c>aes_cbc128</c> should be replaced by <c>aes_128_cbc</c>.</p> + <p><c>aes_cbc256</c> should be replaced by <c>aes_256_cbc</c>.</p> + </desc> + </datatype> + + <datatype> <name name="block_cipher_without_iv"/> <name name="ecb_cipher"/> <desc> diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl index de8cfac9a2..fe8390c5b8 100644 --- a/lib/crypto/src/crypto.erl +++ b/lib/crypto/src/crypto.erl @@ -47,6 +47,19 @@ -export([privkey_to_pubkey/2]). -export([ec_curve/1, ec_curves/0]). -export([rand_seed/1]). + +%% Experiment +-export([crypto_init/4, + crypto_update/2, crypto_update/3, + %% Emulates old api: + crypto_stream_init/2, crypto_stream_init/3, + crypto_stream_encrypt/2, + crypto_stream_decrypt/2, + crypto_block_encrypt/3, crypto_block_encrypt/4, + crypto_block_decrypt/3, crypto_block_decrypt/4 + ]). + + %% Engine -export([ engine_get_all_methods/0, @@ -89,7 +102,8 @@ -export_type([ stream_state/0, hmac_state/0, - hash_state/0 + hash_state/0, + crypto_state/0 ]). %% Private. For tests. @@ -261,17 +275,36 @@ %%% -type block_cipher_with_iv() :: cbc_cipher() | cfb_cipher() - | aes_cbc128 - | aes_cbc256 | aes_ige256 | blowfish_ofb64 - | des3_cbf % cfb misspelled - | des_ede3 | rc2_cbc . --type cbc_cipher() :: des_cbc | des3_cbc | aes_cbc | blowfish_cbc . --type aead_cipher() :: aes_gcm | aes_ccm | chacha20_poly1305 . --type cfb_cipher() :: aes_cfb128 | aes_cfb8 | blowfish_cfb64 | des3_cfb | des_cfb . +-type cbc_cipher() :: des_cbc | des_ede3_cbc + | blowfish_cbc + | aes_cbc | aes_128_cbc | aes_192_cbc | aes_256_cbc + | alias_cbc() . +-type alias_cbc() :: des3_cbc | des_ede3 + | aes_cbc128 | aes_cbc256 . + +-type aead_cipher() :: aes_gcm + | aes_128_gcm + | aes_192_gcm + | aes_256_gcm + | aes_ccm + | aes_128_ccm + | aes_192_ccm + | aes_256_ccm + | chacha20_poly1305 . + +-type cfb_cipher() :: aes_cfb8 + | aes_cfb128 + | blowfish_cfb64 + | des_cfb + | des_ede3_cfb + | alias_cfb() . +-type alias_cfb() :: des_ede3_cbf | des3_cbf + | des3_cfb . + -type block_cipher_without_iv() :: ecb_cipher() . -type ecb_cipher() :: des_ecb | blowfish_ecb | aes_ecb . @@ -342,7 +375,7 @@ stop() -> supports()-> {Hashs, PubKeys, Ciphers, Macs, Curves, RsaOpts} = algorithms(), [{hashs, Hashs}, - {ciphers, Ciphers}, + {ciphers, prepend_cipher_aliases(Ciphers)}, {public_keys, PubKeys}, {macs, Macs}, {curves, Curves}, @@ -473,7 +506,7 @@ hmac_final_n(Context, HashLen) -> Data :: iodata(), Mac :: binary(). cmac(Type, Key, Data) -> - notsup_to_error(cmac_nif(Type, Key, Data)). + notsup_to_error(cmac_nif(alias(Type), Key, Data)). -spec cmac(Type, Key, Data, MacLength) -> Mac when Type :: ?CMAC_CIPHER_ALGORITHM, @@ -481,8 +514,9 @@ cmac(Type, Key, Data) -> Data :: iodata(), MacLength :: integer(), Mac :: binary(). + cmac(Type, Key, Data, MacLength) -> - erlang:binary_part(cmac(Type, Key, Data), 0, MacLength). + erlang:binary_part(cmac(alias(Type), Key, Data), 0, MacLength). %%%---- POLY1305 @@ -499,91 +533,80 @@ poly1305(Key, Data) -> %%%---- Block ciphers +%%%---------------------------------------------------------------- -spec block_encrypt(Type::block_cipher_with_iv(), Key::key()|des3_key(), Ivec::binary(), PlainText::iodata()) -> binary(); (Type::aead_cipher(), Key::iodata(), Ivec::binary(), {AAD::binary(), PlainText::iodata()}) -> {binary(), binary()}; (aes_gcm | aes_ccm, Key::iodata(), Ivec::binary(), {AAD::binary(), PlainText::iodata(), TagLength::1..16}) -> {binary(), binary()}. -block_encrypt(Type, Key, Ivec, PlainText) when Type =:= des_cbc; - Type =:= des_cfb; - Type =:= blowfish_cbc; - Type =:= blowfish_cfb64; - Type =:= blowfish_ofb64; - Type =:= aes_cbc128; - Type =:= aes_cfb8; - Type =:= aes_cfb128; - Type =:= aes_cbc256; - Type =:= aes_cbc; - Type =:= rc2_cbc -> - block_crypt_nif(Type, Key, Ivec, PlainText, true); -block_encrypt(Type, Key0, Ivec, PlainText) when Type =:= des3_cbc; - Type =:= des_ede3 -> - Key = check_des3_key(Key0), - block_crypt_nif(des_ede3_cbc, Key, Ivec, PlainText, true); -block_encrypt(des3_cbf, Key0, Ivec, PlainText) -> % cfb misspelled - Key = check_des3_key(Key0), - block_crypt_nif(des_ede3_cbf, Key, Ivec, PlainText, true); -block_encrypt(des3_cfb, Key0, Ivec, PlainText) -> + +block_encrypt(Type, Key, Ivec, Data) -> + do_block_encrypt(alias(Type), Key, Ivec, Data). + +do_block_encrypt(Type, Key0, Ivec, Data) when Type =:= des_ede3_cbc; + Type =:= des_ede3_cfb -> Key = check_des3_key(Key0), - block_crypt_nif(des_ede3_cfb, Key, Ivec, PlainText, true); -block_encrypt(aes_ige256, Key, Ivec, PlainText) -> + block_crypt_nif(Type, Key, Ivec, Data, true); + +do_block_encrypt(Type, Key, Ivec, PlainText) when Type =:= aes_ige256 -> notsup_to_error(aes_ige_crypt_nif(Key, Ivec, PlainText, true)); -block_encrypt(Type, Key, Ivec, {AAD, PlainText}) when Type =:= aes_gcm; - Type =:= aes_ccm -> - aead_encrypt(Type, Key, Ivec, AAD, PlainText); -block_encrypt(Type, Key, Ivec, {AAD, PlainText, TagLength}) when Type =:= aes_gcm; - Type =:= aes_ccm -> - aead_encrypt(Type, Key, Ivec, AAD, PlainText, TagLength); -block_encrypt(chacha20_poly1305=Type, Key, Ivec, {AAD, PlainText}) -> - aead_encrypt(Type, Key, Ivec, AAD, PlainText, 16). +do_block_encrypt(Type, Key, Ivec, {AAD, PlainText}) when Type =:= chacha20_poly1305 -> + aead_encrypt(Type, Key, Ivec, AAD, PlainText, 16); +do_block_encrypt(Type, Key, Ivec, Data) when Type =:= aes_gcm; + Type =:= aes_ccm -> + case Data of + {AAD, PlainText} -> + aead_encrypt(Type, Key, Ivec, AAD, PlainText); + {AAD, PlainText, TagLength} -> + aead_encrypt(Type, Key, Ivec, AAD, PlainText, TagLength) + end; + +do_block_encrypt(Type, Key, Ivec, PlainText) -> + block_crypt_nif(Type, Key, Ivec, PlainText, true). + + + +-spec block_encrypt(Type::block_cipher_without_iv(), Key::key(), PlainText::iodata()) -> binary(). + +block_encrypt(Type, Key, PlainText) -> + block_crypt_nif(alias(Type), Key, PlainText, true). + +%%%---------------------------------------------------------------- +%%%---------------------------------------------------------------- -spec block_decrypt(Type::block_cipher_with_iv(), Key::key()|des3_key(), Ivec::binary(), Data::iodata()) -> binary(); (Type::aead_cipher(), Key::iodata(), Ivec::binary(), {AAD::binary(), Data::iodata(), Tag::binary()}) -> binary() | error. -block_decrypt(Type, Key, Ivec, Data) when Type =:= des_cbc; - Type =:= des_cfb; - Type =:= blowfish_cbc; - Type =:= blowfish_cfb64; - Type =:= blowfish_ofb64; - Type =:= aes_cbc; - Type =:= aes_cbc128; - Type =:= aes_cfb8; - Type =:= aes_cfb128; - Type =:= aes_cbc256; - Type =:= rc2_cbc -> - block_crypt_nif(Type, Key, Ivec, Data, false); -block_decrypt(Type, Key0, Ivec, Data) when Type =:= des3_cbc; - Type =:= des_ede3 -> - Key = check_des3_key(Key0), - block_crypt_nif(des_ede3_cbc, Key, Ivec, Data, false); -block_decrypt(des3_cbf, Key0, Ivec, Data) -> % cfb misspelled - Key = check_des3_key(Key0), - block_crypt_nif(des_ede3_cbf, Key, Ivec, Data, false); -block_decrypt(des3_cfb, Key0, Ivec, Data) -> + +block_decrypt(Type, Key, Ivec, Data) -> + do_block_decrypt(alias(Type), Key, Ivec, Data). + +do_block_decrypt(Type, Key0, Ivec, Data) when Type =:= des_ede3_cbc; + Type =:= des_ede3_cfb -> Key = check_des3_key(Key0), - block_crypt_nif(des_ede3_cfb, Key, Ivec, Data, false); -block_decrypt(aes_ige256, Key, Ivec, Data) -> + block_crypt_nif(Type, Key, Ivec, Data, false); + +do_block_decrypt(aes_ige256, Key, Ivec, Data) -> notsup_to_error(aes_ige_crypt_nif(Key, Ivec, Data, false)); -block_decrypt(Type, Key, Ivec, {AAD, Data, Tag}) when Type =:= aes_gcm; + +do_block_decrypt(Type, Key, Ivec, {AAD, Data, Tag}) when Type =:= aes_gcm; Type =:= aes_ccm; Type =:= chacha20_poly1305 -> - aead_decrypt(Type, Key, Ivec, AAD, Data, Tag). - + aead_decrypt(Type, Key, Ivec, AAD, Data, Tag); --spec block_encrypt(Type::block_cipher_without_iv(), Key::key(), PlainText::iodata()) -> binary(). +do_block_decrypt(Type, Key, Ivec, Data) -> + block_crypt_nif(Type, Key, Ivec, Data, false). -block_encrypt(Type, Key, PlainText) -> - block_crypt_nif(Type, Key, PlainText, true). -spec block_decrypt(Type::block_cipher_without_iv(), Key::key(), Data::iodata()) -> binary(). block_decrypt(Type, Key, Data) -> - block_crypt_nif(Type, Key, Data, false). - + block_crypt_nif(alias(Type), Key, Data, false). +%%%---------------------------------------------------------------- -spec next_iv(Type:: cbc_cipher(), Data) -> NextIVec when % Type :: cbc_cipher(), %des_cbc | des3_cbc | aes_cbc | aes_ige, Data :: iodata(), NextIVec :: binary(). @@ -614,18 +637,30 @@ next_iv(Type, Data, _Ivec) -> -opaque stream_state() :: {stream_cipher(), reference()}. --type stream_cipher() :: rc4 | aes_ctr | chacha20 . +-type stream_cipher() :: stream_cipher_iv() | stream_cipher_no_iv() . +-type stream_cipher_no_iv() :: rc4 . +-type stream_cipher_iv() :: aes_ctr + | aes_128_ctr + | aes_192_ctr + | aes_256_ctr + | chacha20 . --spec stream_init(Type, Key, IVec) -> State when Type :: aes_ctr | chacha20, +-spec stream_init(Type, Key, IVec) -> State when Type :: stream_cipher_iv(), Key :: iodata(), IVec :: binary(), State :: stream_state() . stream_init(aes_ctr, Key, Ivec) -> {aes_ctr, aes_ctr_stream_init(Key, Ivec)}; +stream_init(aes_128_ctr, Key, Ivec) -> + {aes_ctr, aes_ctr_stream_init(Key, Ivec)}; +stream_init(aes_192_ctr, Key, Ivec) -> + {aes_ctr, aes_ctr_stream_init(Key, Ivec)}; +stream_init(aes_256_ctr, Key, Ivec) -> + {aes_ctr, aes_ctr_stream_init(Key, Ivec)}; stream_init(chacha20, Key, Ivec) -> {chacha20, chacha20_stream_init(Key,Ivec)}. --spec stream_init(Type, Key) -> State when Type :: rc4, +-spec stream_init(Type, Key) -> State when Type :: stream_cipher_no_iv(), Key :: iodata(), State :: stream_state() . stream_init(rc4, Key) -> @@ -932,7 +967,7 @@ rand_seed_nif(_Seed) -> ?nif_stub. DigestType :: rsa_digest_type() | dss_digest_type() | ecdsa_digest_type(), - Msg :: binary() | {digest,binary()}, + Msg :: iodata() | {digest,iodata()}, Key :: rsa_private() | dss_private() | [ecdsa_private() | ecdsa_params()] @@ -951,7 +986,7 @@ sign(Algorithm, Type, Data, Key) -> | dss_digest_type() | ecdsa_digest_type() | none, - Msg :: binary() | {digest,binary()}, + Msg :: iodata() | {digest,iodata()}, Key :: rsa_private() | dss_private() | [ecdsa_private() | ecdsa_params()] @@ -980,7 +1015,7 @@ pkey_sign_nif(_Algorithm, _Type, _Digest, _Key, _Options) -> ?nif_stub. | dss_digest_type() | ecdsa_digest_type() | none, - Msg :: binary() | {digest,binary()}, + Msg :: iodata() | {digest,iodata()}, Signature :: binary(), Key :: rsa_public() | dss_public() @@ -998,7 +1033,7 @@ verify(Algorithm, Type, Data, Signature, Key) -> DigestType :: rsa_digest_type() | dss_digest_type() | ecdsa_digest_type(), - Msg :: binary() | {digest,binary()}, + Msg :: iodata() | {digest,iodata()}, Signature :: binary(), Key :: rsa_public() | dss_public() @@ -2180,3 +2215,178 @@ check_otp_test_engine(LibDir) -> {error, notexist} end end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% +%%% Experimental NG +%%% + +%%% -> {ok,State::ref()} | {error,Reason} + +-opaque crypto_state() :: reference() | {any(),any(),any(),any()}. + + +%%%---------------------------------------------------------------- +%%% +%%% Create and initialize a new state for encryption or decryption +%%% + +-spec crypto_init(Cipher, Key, IV, EncryptFlag) -> {ok,State} | {error,term()} | undefined + when Cipher :: stream_cipher() + | block_cipher_with_iv() + | block_cipher_without_iv() , + Key :: iodata(), + IV :: binary(), + EncryptFlag :: boolean() | undefined, + State :: crypto_state() . + +crypto_init(Cipher, Key, IV, EncryptFlag) when is_atom(Cipher), + is_binary(Key), + is_binary(IV), + is_atom(EncryptFlag) -> + case ng_crypto_init_nif(alias(Cipher), Key, IV, EncryptFlag) of + {error,Error} -> + {error,Error}; + undefined -> % For compatibility function crypto_stream_init/3 + undefined; + Ref when is_reference(Ref) -> + {ok,Ref}; + State when is_tuple(State), + size(State)==4 -> + {ok,State} % compatibility with old cryptolibs < 1.0.1 + end. + + +%%%---------------------------------------------------------------- +%%% +%%% Encrypt/decrypt a sequence of bytes. The sum of the sizes +%%% of all blocks must be an integer multiple of the crypto's +%%% blocksize. +%%% + +-spec crypto_update(State, Data) -> {ok,Result} | {error,term()} + when State :: crypto_state(), + Data :: iodata(), + Result :: binary() | {crypto_state(),binary()}. +crypto_update(State, Data) -> + mk_ret(ng_crypto_update_nif(State, Data)). + +%%%---------------------------------------------------------------- +%%% +%%% Encrypt/decrypt a sequence of bytes but change the IV first. +%%% Not applicable for all modes. +%%% + +-spec crypto_update(State, Data, IV) -> {ok,Result} | {error,term()} + when State :: crypto_state(), + Data :: iodata(), + IV :: binary(), + Result :: binary() | {crypto_state(),binary()}. +crypto_update(State, Data, IV) -> + mk_ret(ng_crypto_update_nif(State, Data, IV)). + +%%%---------------------------------------------------------------- +%%% Helpers +mk_ret(R) -> mk_ret(R, []). + +mk_ret({error,Error}, _) -> + {error,Error}; +mk_ret(Bin, Acc) when is_binary(Bin) -> + {ok, iolist_to_binary(lists:reverse([Bin|Acc]))}; +mk_ret({State1,Bin}, Acc) when is_tuple(State1), + size(State1) == 4, + is_binary(Bin) -> + %% compatibility with old cryptolibs < 1.0.1 + {ok, {State1, iolist_to_binary(lists:reverse([Bin|Acc]))}}. + +%%%---------------------------------------------------------------- +%%% NIFs +ng_crypto_init_nif(_Cipher, _Key, _IVec, _EncryptFlg) -> ?nif_stub. +ng_crypto_update_nif(_State, _Data) -> ?nif_stub. +ng_crypto_update_nif(_State, _Data, _IV) -> ?nif_stub. + +%%%================================================================ +%%% Compatibility functions to be called by "old" api functions. + +%%%-------------------------------- +%%%---- block encrypt/decrypt +crypto_block_encrypt(Cipher, Key, Data) -> crypto_block_encrypt(Cipher, Key, <<>>, Data). +crypto_block_decrypt(Cipher, Key, Data) -> crypto_block_decrypt(Cipher, Key, <<>>, Data). + +crypto_block_encrypt(Cipher, Key, Ivec, Data) -> crypto_block(Cipher, Key, Ivec, Data, true). +crypto_block_decrypt(Cipher, Key, Ivec, Data) -> crypto_block(Cipher, Key, Ivec, Data, false). + +%% AEAD: use old funcs + +%%%---- helper +crypto_block(Cipher, Key, IV, Data, EncryptFlag) -> + case crypto_init(Cipher, iolist_to_binary(Key), iolist_to_binary(IV), EncryptFlag) of + {ok, Ref} -> + case crypto_update(Ref, Data) of + {ok, {_,Bin}} when is_binary(Bin) -> Bin; + {ok, Bin} when is_binary(Bin) -> Bin; + {error,_} -> error(badarg) + end; + + {error,_} -> error(badarg) + end. + +%%%-------------------------------- +%%%---- stream init, encrypt/decrypt + +crypto_stream_init(Cipher, Key) -> + crypto_stream_init(Cipher, Key, <<>>). + +crypto_stream_init(Cipher, Key0, IV0) -> + Key = iolist_to_binary(Key0), + IV = iolist_to_binary(IV0), + %% First check the argumensts: + case crypto_init(Cipher, Key, IV, undefined) of + undefined -> + {Cipher, {Key, IV}}; + {error,_} -> + {error,badarg} + end. + +crypto_stream_encrypt(State, PlainText) -> + crypto_stream_emulate(State, PlainText, true). + +crypto_stream_decrypt(State, CryptoText) -> + crypto_stream_emulate(State, CryptoText, false). + + +%%%---- helper +crypto_stream_emulate({Cipher,{Key,IV}}, Data, EncryptFlag) -> + case crypto_init(Cipher, Key, IV, EncryptFlag) of + {ok,State} -> + crypto_stream_emulate({Cipher,State}, Data, EncryptFlag); + {error,_} -> + error(badarg) + end; +crypto_stream_emulate({Cipher,State}, Data, _) -> + case crypto_update(State, Data) of + {ok, {State1,Bin}} when is_binary(Bin) -> {{Cipher,State1},Bin}; + {ok,Bin} when is_binary(Bin) -> {{Cipher,State},Bin}; + {error,_} -> error(badarg) + end. + + +%%%================================================================ + +prepend_cipher_aliases(L) -> + [des3_cbc, des_ede3, des_ede3_cbf, des3_cbf, des3_cfb, aes_cbc128, aes_cbc256 | L]. + + +%%%---- des_ede3_cbc +alias(des3_cbc) -> des_ede3_cbc; +alias(des_ede3) -> des_ede3_cbc; +%%%---- des_ede3_cfb +alias(des_ede3_cbf) -> des_ede3_cfb; +alias(des3_cbf) -> des_ede3_cfb; +alias(des3_cfb) -> des_ede3_cfb; +%%%---- aes_*_cbc +alias(aes_cbc128) -> aes_128_cbc; +alias(aes_cbc256) -> aes_256_cbc; + +alias(Alg) -> Alg. diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/para b/lib/dialyzer/test/opaque_SUITE_data/results/para index 37b5b7b44e..eca445315c 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/results/para +++ b/lib/dialyzer/test/opaque_SUITE_data/results/para @@ -29,5 +29,6 @@ para4.erl:74: Attempt to test for equality between a term of type para4_adt:int( para4.erl:79: Attempt to test for equality between a term of type para4_adt:int(2 | 3 | 4) and a term of opaque type para4_adt:int(5 | 6 | 7) para4.erl:84: Attempt to test for equality between a term of type para4_adt:un(3 | 4) and a term of opaque type para4_adt:un(1 | 2) para4.erl:89: Attempt to test for equality between a term of type para4_adt:tup({_,_}) and a term of opaque type para4_adt:tup(tuple()) +para4.erl:94: Attempt to test for equality between a term of type para4_adt:t(#{1=>'a'}) and a term of opaque type para4_adt:t(#{2=>'b'}) para5.erl:13: Attempt to test for inequality between a term of type para5_adt:dd(atom()) and a term of opaque type para5_adt:d() para5.erl:8: The test para5_adt:d() =:= para5_adt:d() can never evaluate to 'true' diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/para/para4.erl b/lib/dialyzer/test/opaque_SUITE_data/src/para/para4.erl index b9794672a9..8cd049169d 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/src/para/para4.erl +++ b/lib/dialyzer/test/opaque_SUITE_data/src/para/para4.erl @@ -88,6 +88,11 @@ adt_tt13() -> I2 = adt_tup2(), I1 =:= I2. % opaque attempt +adt_tt14() -> + I1 = adt_map(), + I2 = adt_map2(), + I1 =:= I2. + y3() -> {a, 3}. @@ -132,3 +137,9 @@ adt_tup() -> adt_tup2() -> para4_adt:tup2(). + +adt_map() -> + para4_adt:map(). + +adt_map2() -> + para4_adt:map2(). diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/para/para4_adt.erl b/lib/dialyzer/test/opaque_SUITE_data/src/para/para4_adt.erl index 407dd198a7..06a6c22677 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/src/para/para4_adt.erl +++ b/lib/dialyzer/test/opaque_SUITE_data/src/para/para4_adt.erl @@ -8,6 +8,8 @@ -export([tup/0, tup2/0]). +-export([map/0, map2/0]). + -export_type([t/1, y/1, int/1, tup/1, un/1]). -type ai() :: atom() | integer(). @@ -106,3 +108,13 @@ tup() -> tup2() -> foo:tup2(). + +-spec map() -> t(#{2 => b}). + +map() -> + foo:map(). + +-spec map2() -> t(#{1 => a}). + +map2() -> + foo:map2(). diff --git a/lib/dialyzer/test/small_SUITE_data/src/lists_key_bug.erl b/lib/dialyzer/test/small_SUITE_data/src/lists_key_bug.erl index d7cbc27a4d..ad5cf3c503 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/lists_key_bug.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/lists_key_bug.erl @@ -2,10 +2,11 @@ %% OTP-15570 --export([t/1]). +-export([is_1/1, is_2/1, i/1, t1/0, t2/0, im/0]). -t(V) -> - K = key(V), +%% int_set([3]) +is_1(V) -> + K = ikey(V), case lists:keyfind(K, 1, [{<<"foo">>, bar}]) of false -> a; @@ -13,7 +14,62 @@ t(V) -> b end. -key(1) -> +ikey(1) -> 3; -key(2) -> +ikey(2) -> <<"foo">>. + +%% int_set([3, 5]) +is_2(V) -> + K = iskey(V), + case lists:keyfind(K, 1, [{<<"foo">>, bar}]) of + false -> + a; + {_, _} -> + b + end. + +iskey(1) -> + 12; +iskey(2) -> + 14; +iskey(3) -> + <<"foo">>. + +%% integer() +i(V) -> + K = intkey(V), + case lists:keyfind(K, 1, [{9.0, foo}]) of + false -> + a; + {_, _} -> + b + end. + +intkey(K) when is_integer(K) -> + K + 9999. + +t1() -> + case lists:keyfind({17}, 1, [{{17.0}, true}]) of + false -> + a; + {_, _} -> + b + end. + +t2() -> + case lists:keyfind({17.0}, 1, [{{17}, true}]) of + false -> + a; + {_, _} -> + b + end. + +%% Note: #{1.0 => a} =/= #{1 => a}. +im() -> + case lists:keyfind(#{1.0 => a}, 1, [{#{1 => a}, foo}]) of + false -> + a; + {_, _} -> + b + end. diff --git a/lib/erl_interface/src/Makefile.in b/lib/erl_interface/src/Makefile.in index 69b2cbc281..1b19a85f1e 100644 --- a/lib/erl_interface/src/Makefile.in +++ b/lib/erl_interface/src/Makefile.in @@ -31,6 +31,8 @@ .PHONY : debug opt release clean distclean depend +include $(ERL_TOP)/make/target.mk + # ---------------------------------------------------- # Application version and release dir specification # ---------------------------------------------------- diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl index 799957dfdc..8ae1cd4ab7 100644 --- a/lib/hipe/cerl/erl_bif_types.erl +++ b/lib/hipe/cerl/erl_bif_types.erl @@ -2224,7 +2224,8 @@ type_order() -> [t_number(), t_atom(), t_reference(), t_fun(), t_port(), t_pid(), t_tuple(), t_map(), t_list(), t_bitstr()]. -key_comparisons_fail(X, KeyPos, TupleList, Opaques) -> +key_comparisons_fail(X0, KeyPos, TupleList, Opaques) -> + X = erl_types:t_widen_to_number(X0), lists:all(fun(Tuple) -> Key = type(erlang, element, 2, [KeyPos, Tuple]), t_is_none(t_inf(Key, X, Opaques)) diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl index 9abb4d31d9..d61cd8664c 100644 --- a/lib/hipe/cerl/erl_types.erl +++ b/lib/hipe/cerl/erl_types.erl @@ -66,7 +66,6 @@ t_find_opaque_mismatch/3, t_find_unknown_opaque/3, t_fixnum/0, - t_map/2, t_non_neg_fixnum/0, t_pos_fixnum/0, t_float/0, @@ -205,6 +204,7 @@ t_unopaque/1, t_unopaque/2, t_var/1, t_var_name/1, + t_widen_to_number/1, %% t_assign_variables_to_subtype/2, type_is_defined/4, record_field_diffs_to_string/2, @@ -1594,6 +1594,50 @@ lift_list_to_pos_empty(?nil) -> ?nil; lift_list_to_pos_empty(?list(Content, Termination, _)) -> ?list(Content, Termination, ?unknown_qual). +-spec t_widen_to_number(erl_type()) -> erl_type(). + +%% Widens integers and floats to t_number(). +%% Used by erl_bif_types:key_comparison_fail(). + +t_widen_to_number(?any) -> ?any; +t_widen_to_number(?none) -> ?none; +t_widen_to_number(?unit) -> ?unit; +t_widen_to_number(?atom(_Set) = T) -> T; +t_widen_to_number(?bitstr(_Unit, _Base) = T) -> T; +t_widen_to_number(?float) -> t_number(); +t_widen_to_number(?function(Domain, Range)) -> + ?function(t_widen_to_number(Domain), t_widen_to_number(Range)); +t_widen_to_number(?identifier(_Types) = T) -> T; +t_widen_to_number(?int_range(_From, _To)) -> t_number(); +t_widen_to_number(?int_set(_Set)) -> t_number(); +t_widen_to_number(?integer(_Types)) -> t_number(); +t_widen_to_number(?list(Type, Tail, Size)) -> + ?list(t_widen_to_number(Type), t_widen_to_number(Tail), Size); +t_widen_to_number(?map(Pairs, DefK, DefV)) -> + L = [{t_widen_to_number(K), MNess, t_widen_to_number(V)} || + {K, MNess, V} <- Pairs], + t_map(L, t_widen_to_number(DefK), t_widen_to_number(DefV)); +t_widen_to_number(?matchstate(_P, _Slots) = T) -> T; +t_widen_to_number(?nil) -> ?nil; +t_widen_to_number(?number(_Set, _Tag)) -> t_number(); +t_widen_to_number(?opaque(Set)) -> + L = [Opaque#opaque{struct = t_widen_to_number(S)} || + #opaque{struct = S} = Opaque <- set_to_list(Set)], + ?opaque(ordsets:from_list(L)); +t_widen_to_number(?product(Types)) -> + ?product(list_widen_to_number(Types)); +t_widen_to_number(?tuple(?any, _, _) = T) -> T; +t_widen_to_number(?tuple(Types, Arity, Tag)) -> + ?tuple(list_widen_to_number(Types), Arity, Tag); +t_widen_to_number(?tuple_set(_) = Tuples) -> + t_sup([t_widen_to_number(T) || T <- t_tuple_subtypes(Tuples)]); +t_widen_to_number(?union(List)) -> + ?union(list_widen_to_number(List)); +t_widen_to_number(?var(_Id)= T) -> T. + +list_widen_to_number(List) -> + [t_widen_to_number(E) || E <- List]. + %%----------------------------------------------------------------------------- %% Maps %% @@ -3104,9 +3148,18 @@ is_compat_arg(?list(Contents1, Termination1, Size1), is_compat_arg(?product(Types1), ?product(Types2)) -> is_compat_list(Types1, Types2); is_compat_arg(?map(Pairs1, DefK1, DefV1), ?map(Pairs2, DefK2, DefV2)) -> - (is_compat_list(Pairs1, Pairs2) andalso - is_compat_arg(DefK1, DefK2) andalso - is_compat_arg(DefV1, DefV2)); + {Ks1, _, Vs1} = lists:unzip3(Pairs1), + {Ks2, _, Vs2} = lists:unzip3(Pairs2), + Key1 = t_sup([DefK1 | Ks1]), + Key2 = t_sup([DefK2 | Ks2]), + case is_compat_arg(Key1, Key2) of + true -> + Value1 = t_sup([DefV1 | Vs1]), + Value2 = t_sup([DefV2 | Vs2]), + is_compat_arg(Value1, Value2); + false -> + false + end; is_compat_arg(?tuple(?any, ?any, ?any), ?tuple(_, _, _)) -> false; is_compat_arg(?tuple(_, _, _), ?tuple(?any, ?any, ?any)) -> false; is_compat_arg(?tuple(Elements1, Arity, _), @@ -4156,39 +4209,6 @@ t_abstract_records(?opaque(_)=Type, RecDict) -> t_abstract_records(T, _RecDict) -> T. -%% Map over types. Depth first. Used by the contract checker. ?list is -%% not fully implemented so take care when changing the type in Termination. - --spec t_map(fun((erl_type()) -> erl_type()), erl_type()) -> erl_type(). - -t_map(Fun, ?list(Contents, Termination, Size)) -> - Fun(?list(t_map(Fun, Contents), t_map(Fun, Termination), Size)); -t_map(Fun, ?function(Domain, Range)) -> - Fun(?function(t_map(Fun, Domain), t_map(Fun, Range))); -t_map(Fun, ?product(Types)) -> - Fun(?product([t_map(Fun, T) || T <- Types])); -t_map(Fun, ?union(Types)) -> - Fun(t_sup([t_map(Fun, T) || T <- Types])); -t_map(Fun, ?tuple(?any, ?any, ?any) = T) -> - Fun(T); -t_map(Fun, ?tuple(Elements, _Arity, _Tag)) -> - Fun(t_tuple([t_map(Fun, E) || E <- Elements])); -t_map(Fun, ?tuple_set(_) = Tuples) -> - Fun(t_sup([t_map(Fun, T) || T <- t_tuple_subtypes(Tuples)])); -t_map(Fun, ?opaque(Set)) -> - L = [Opaque#opaque{struct = NewS} || - #opaque{struct = S} = Opaque <- set_to_list(Set), - not t_is_none(NewS = t_map(Fun, S))], - Fun(case L of - [] -> ?none; - _ -> ?opaque(ordsets:from_list(L)) - end); -t_map(Fun, ?map(Pairs,DefK,DefV)) -> - %% TODO: - Fun(t_map(Pairs, Fun(DefK), Fun(DefV))); -t_map(Fun, T) -> - Fun(T). - %%============================================================================= %% %% Prettyprinter diff --git a/lib/kernel/include/dist.hrl b/lib/kernel/include/dist.hrl index 003852f1b0..f06fc328d7 100644 --- a/lib/kernel/include/dist.hrl +++ b/lib/kernel/include/dist.hrl @@ -42,6 +42,9 @@ -define(DFLAG_BIG_CREATION, 16#40000). -define(DFLAG_SEND_SENDER, 16#80000). -define(DFLAG_BIG_SEQTRACE_LABELS, 16#100000). +%% -define(DFLAG_NO_MAGIC, 16#200000). %% Used internally only +-define(DFLAG_EXIT_PAYLOAD, 16#400000). +-define(DFLAG_FRAGMENTS, 16#800000). %% Also update dflag2str() in ../src/dist_util.erl %% when adding flags... diff --git a/lib/kernel/src/Makefile b/lib/kernel/src/Makefile index 3d1506ea08..43b776f37e 100644 --- a/lib/kernel/src/Makefile +++ b/lib/kernel/src/Makefile @@ -123,7 +123,6 @@ MODULES = \ logger_server \ logger_simple_h \ logger_sup \ - net \ net_adm \ net_kernel \ os \ diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl index ecc022b28d..09ed31f10c 100644 --- a/lib/kernel/src/dist_util.erl +++ b/lib/kernel/src/dist_util.erl @@ -116,6 +116,10 @@ dflag2str(?DFLAG_SEND_SENDER) -> "SEND_SENDER"; dflag2str(?DFLAG_BIG_SEQTRACE_LABELS) -> "BIG_SEQTRACE_LABELS"; +dflag2str(?DFLAG_EXIT_PAYLOAD) -> + "EXIT_PAYLOAD"; +dflag2str(?DFLAG_FRAGMENTS) -> + "FRAGMENTS"; dflag2str(_) -> "UNKNOWN". diff --git a/lib/kernel/src/gen_tcp.erl b/lib/kernel/src/gen_tcp.erl index 7f7833ec23..5d4764f8ff 100644 --- a/lib/kernel/src/gen_tcp.erl +++ b/lib/kernel/src/gen_tcp.erl @@ -156,7 +156,7 @@ connect(Address, Port, Opts) -> Options :: [connect_option()], Timeout :: timeout(), Socket :: socket(), - Reason :: inet:posix(). + Reason :: timeout | inet:posix(). connect(Address, Port, Opts, Time) -> Timer = inet:start_timer(Time), @@ -220,7 +220,7 @@ listen(Port, Opts0) -> -spec accept(ListenSocket) -> {ok, Socket} | {error, Reason} when ListenSocket :: socket(), Socket :: socket(), - Reason :: closed | timeout | system_limit | inet:posix(). + Reason :: closed | system_limit | inet:posix(). accept(S) -> case inet_db:lookup_socket(S) of @@ -312,7 +312,7 @@ recv(S, Length) when is_port(S) -> Length :: non_neg_integer(), Timeout :: timeout(), Packet :: string() | binary() | HttpPacket, - Reason :: closed | inet:posix(), + Reason :: closed | timeout | inet:posix(), HttpPacket :: term(). recv(S, Length, Time) when is_port(S) -> diff --git a/lib/kernel/src/gen_udp.erl b/lib/kernel/src/gen_udp.erl index d6e8652e77..fad7b2f887 100644 --- a/lib/kernel/src/gen_udp.erl +++ b/lib/kernel/src/gen_udp.erl @@ -95,7 +95,7 @@ -spec open(Port) -> {ok, Socket} | {error, Reason} when Port :: inet:port_number(), Socket :: socket(), - Reason :: inet:posix(). + Reason :: system_limit | inet:posix(). open(Port) -> open(Port, []). @@ -112,7 +112,7 @@ open(Port) -> | {bind_to_device, binary()} | option(), Socket :: socket(), - Reason :: inet:posix(). + Reason :: system_limit | inet:posix(). open(Port, Opts0) -> {Mod, Opts} = inet:udp_module(Opts0), @@ -186,7 +186,7 @@ recv(S,Len) when is_port(S), is_integer(Len) -> Port :: inet:port_number(), AncData :: inet:ancillary_data(), Packet :: string() | binary(), - Reason :: not_owner | inet:posix(). + Reason :: not_owner | timeout | inet:posix(). recv(S,Len,Time) when is_port(S) -> case inet_db:lookup_socket(S) of diff --git a/lib/kernel/src/inet_tcp_dist.erl b/lib/kernel/src/inet_tcp_dist.erl index c37212b0f9..c5a114a9ef 100644 --- a/lib/kernel/src/inet_tcp_dist.erl +++ b/lib/kernel/src/inet_tcp_dist.erl @@ -212,6 +212,7 @@ do_accept(Driver, Kernel, AcceptPid, Socket, MyNode, Allowed, SetupTime) -> [{active, true}, {deliver, port}, {packet, 4}, + binary, nodelay()]) end, f_getll = fun(S) -> diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index a1d9e8e215..4b48f6cd1d 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2018. 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. @@ -74,7 +74,6 @@ logger_simple_h, logger_std_h, logger_sup, - net, net_adm, net_kernel, os, diff --git a/lib/kernel/src/net.erl b/lib/kernel/src/net.erl deleted file mode 100644 index 2d0ae2ed0c..0000000000 --- a/lib/kernel/src/net.erl +++ /dev/null @@ -1,40 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2016. 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(net). - -%% Various network functions, kept here for compatibility - --export([call/4, - cast/4, - broadcast/3, - ping/1, - relay/1, - sleep/1]). - --deprecated(module). - -call(N,M,F,A) -> rpc:call(N,M,F,A). -cast(N,M,F,A) -> rpc:cast(N,M,F,A). -broadcast(M,F,A) -> rpc:eval_everywhere(M,F,A). -ping(Node) -> net_adm:ping(Node). -sleep(T) -> receive after T -> ok end. -relay(X) -> slave:relay(X). - - diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl index 4915193196..83d3b4b5e1 100644 --- a/lib/kernel/src/net_kernel.erl +++ b/lib/kernel/src/net_kernel.erl @@ -1126,14 +1126,22 @@ do_disconnect(Node, State) -> {false, State} end. - disconnect_pid(Pid, State) -> exit(Pid, disconnect), + + %% This code used to only use exit + recv 'EXIT' to sync, + %% but since OTP-22 links are no longer broken atomically + %% so the exit message below can arrive before any remaining + %% exit messages have killed the distribution port + Ref = erlang:monitor(process, Pid), %% Sync wait for connection to die!!! receive - {'EXIT',Pid,Reason} -> - {_,State1} = handle_exit(Pid, Reason, State), - {true, State1} + {'DOWN',Ref,_,_,_} -> + receive + {'EXIT',Pid,Reason} -> + {_,State1} = handle_exit(Pid, Reason, State), + {true, State1} + end end. %% diff --git a/lib/kernel/test/Makefile b/lib/kernel/test/Makefile index 8a6ffe7e72..d203597fc2 100644 --- a/lib/kernel/test/Makefile +++ b/lib/kernel/test/Makefile @@ -130,6 +130,9 @@ ERL_COMPILE_FLAGS += EBIN = . +TARGETS = $(MODULES:%=$(EBIN)/%.$(EMULATOR)) + + # ---------------------------------------------------- # Targets # ---------------------------------------------------- @@ -150,6 +153,9 @@ clean: docs: +targets: $(TARGETS) + + # ---------------------------------------------------- # Release Target # ---------------------------------------------------- diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index bd963e8148..a2dd1f29b7 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -641,8 +641,8 @@ fun(srp, Username :: string(), UserState :: term()) -> <datatype> <name name="logging_level"/> <desc><p>Specifies the log level for TLS/DTLS. At verbosity level <c>notice</c> and above error reports are - displayed in TLS. The level <c>debug</c> triggers verbose logging of TLS protocol - messages and logging of ignored alerts in DTLS.</p> + displayed in TLS/DTLS. The level <c>debug</c> triggers verbose logging of TLS/DTLS protocol + messages.</p> </desc> </datatype> diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 6b5a311efc..ed47980a69 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -250,10 +250,11 @@ handle_protocol_record(#ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, Stat handle_protocol_record(#ssl_tls{type = ?HANDSHAKE, fragment = Data}, StateName, - #state{protocol_buffers = Buffers0, - connection_env = #connection_env{negotiated_version = Version}} = State) -> + #state{protocol_buffers = Buffers0, + connection_env = #connection_env{negotiated_version = Version}, + ssl_options = Options} = State) -> try - case dtls_handshake:get_dtls_handshake(Version, Data, Buffers0) of + case dtls_handshake:get_dtls_handshake(Version, Data, Buffers0, Options) of {[], Buffers} -> next_event(StateName, no_record, State#state{protocol_buffers = Buffers}); {Packets, Buffers} -> @@ -308,9 +309,12 @@ queue_handshake(Handshake0, #state{handshake_env = #handshake_env{tls_handshake_ connection_env = #connection_env{negotiated_version = Version}, flight_buffer = #{handshakes := HsBuffer0, change_cipher_spec := undefined, - next_sequence := Seq} = Flight0} = State) -> + next_sequence := Seq} = Flight0, + ssl_options = SslOpts} = State) -> Handshake = dtls_handshake:encode_handshake(Handshake0, Version, Seq), Hist = update_handshake_history(Handshake0, Handshake, Hist0), + ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'handshake', Handshake0), + State#state{flight_buffer = Flight0#{handshakes => [Handshake | HsBuffer0], next_sequence => Seq +1}, handshake_env = HsEnv#handshake_env{tls_handshake_history = Hist}}; @@ -318,9 +322,12 @@ queue_handshake(Handshake0, #state{handshake_env = #handshake_env{tls_handshake_ queue_handshake(Handshake0, #state{handshake_env = #handshake_env{tls_handshake_history = Hist0} = HsEnv, connection_env = #connection_env{negotiated_version = Version}, flight_buffer = #{handshakes_after_change_cipher_spec := Buffer0, - next_sequence := Seq} = Flight0} = State) -> + next_sequence := Seq} = Flight0, + ssl_options = SslOpts} = State) -> Handshake = dtls_handshake:encode_handshake(Handshake0, Version, Seq), Hist = update_handshake_history(Handshake0, Handshake, Hist0), + ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'handshake', Handshake0), + State#state{flight_buffer = Flight0#{handshakes_after_change_cipher_spec => [Handshake | Buffer0], next_sequence => Seq +1}, handshake_env = HsEnv#handshake_env{tls_handshake_history = Hist}}. @@ -328,7 +335,7 @@ queue_handshake(Handshake0, #state{handshake_env = #handshake_env{tls_handshake_ queue_change_cipher(ChangeCipher, #state{flight_buffer = Flight, connection_states = ConnectionStates0} = State) -> ConnectionStates = - dtls_record:next_epoch(ConnectionStates0, write), + dtls_record:next_epoch(ConnectionStates0, write), State#state{flight_buffer = Flight#{change_cipher_spec => ChangeCipher}, connection_states = ConnectionStates}. @@ -368,11 +375,14 @@ encode_alert(#alert{} = Alert, Version, ConnectionStates) -> send_alert(Alert, #state{static_env = #static_env{socket = Socket, transport_cb = Transport}, + connection_env = #connection_env{negotiated_version = Version}, - connection_states = ConnectionStates0} = State0) -> + connection_states = ConnectionStates0, + ssl_options = SslOpts} = State0) -> {BinMsg, ConnectionStates} = encode_alert(Alert, Version, ConnectionStates0), send(Transport, Socket, BinMsg), + ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'record', BinMsg), State0#state{connection_states = ConnectionStates}. send_alert_in_connection(Alert, State) -> @@ -815,10 +825,11 @@ initial_flight_state(_) -> next_dtls_record(Data, StateName, #state{protocol_buffers = #protocol_buffers{ dtls_record_buffer = Buf0, - dtls_cipher_texts = CT0} = Buffers} = State0) -> + dtls_cipher_texts = CT0} = Buffers, + ssl_options = SslOpts} = State0) -> case dtls_record:get_dtls_records(Data, acceptable_record_versions(StateName, State0), - Buf0) of + Buf0, SslOpts) of {Records, Buf1} -> CT1 = CT0 ++ Records, next_record(State0#state{protocol_buffers = @@ -1074,11 +1085,14 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket, connection_env = #connection_env{negotiated_version = Version}, flight_buffer = #{handshakes := Flight, change_cipher_spec := undefined}, - connection_states = ConnectionStates0} = State0, Epoch) -> + connection_states = ConnectionStates0, + ssl_options = #ssl_options{log_level = LogLevel}} = State0, + Epoch) -> %% TODO remove hardcoded Max size {Encoded, ConnectionStates} = encode_handshake_flight(lists:reverse(Flight), Version, 1400, Epoch, ConnectionStates0), - send(Transport, Socket, Encoded), + send(Transport, Socket, Encoded), + ssl_logger:debug(LogLevel, outbound, 'record', Encoded), {State0#state{connection_states = ConnectionStates}, []}; send_handshake_flight(#state{static_env = #static_env{socket = Socket, @@ -1087,12 +1101,16 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket, flight_buffer = #{handshakes := [_|_] = Flight0, change_cipher_spec := ChangeCipher, handshakes_after_change_cipher_spec := []}, - connection_states = ConnectionStates0} = State0, Epoch) -> + connection_states = ConnectionStates0, + ssl_options = #ssl_options{log_level = LogLevel}} = State0, + Epoch) -> {HsBefore, ConnectionStates1} = encode_handshake_flight(lists:reverse(Flight0), Version, 1400, Epoch, ConnectionStates0), {EncChangeCipher, ConnectionStates} = encode_change_cipher(ChangeCipher, Version, Epoch, ConnectionStates1), send(Transport, Socket, [HsBefore, EncChangeCipher]), + ssl_logger:debug(LogLevel, outbound, 'record', [HsBefore]), + ssl_logger:debug(LogLevel, outbound, 'record', [EncChangeCipher]), {State0#state{connection_states = ConnectionStates}, []}; send_handshake_flight(#state{static_env = #static_env{socket = Socket, @@ -1101,7 +1119,9 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket, flight_buffer = #{handshakes := [_|_] = Flight0, change_cipher_spec := ChangeCipher, handshakes_after_change_cipher_spec := Flight1}, - connection_states = ConnectionStates0} = State0, Epoch) -> + connection_states = ConnectionStates0, + ssl_options = #ssl_options{log_level = LogLevel}} = State0, + Epoch) -> {HsBefore, ConnectionStates1} = encode_handshake_flight(lists:reverse(Flight0), Version, 1400, Epoch-1, ConnectionStates0), {EncChangeCipher, ConnectionStates2} = @@ -1109,6 +1129,9 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket, {HsAfter, ConnectionStates} = encode_handshake_flight(lists:reverse(Flight1), Version, 1400, Epoch, ConnectionStates2), send(Transport, Socket, [HsBefore, EncChangeCipher, HsAfter]), + ssl_logger:debug(LogLevel, outbound, 'record', [HsBefore]), + ssl_logger:debug(LogLevel, outbound, 'record', [EncChangeCipher]), + ssl_logger:debug(LogLevel, outbound, 'record', [HsAfter]), {State0#state{connection_states = ConnectionStates}, []}; send_handshake_flight(#state{static_env = #static_env{socket = Socket, @@ -1117,12 +1140,16 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket, flight_buffer = #{handshakes := [], change_cipher_spec := ChangeCipher, handshakes_after_change_cipher_spec := Flight1}, - connection_states = ConnectionStates0} = State0, Epoch) -> + connection_states = ConnectionStates0, + ssl_options = #ssl_options{log_level = LogLevel}} = State0, + Epoch) -> {EncChangeCipher, ConnectionStates1} = encode_change_cipher(ChangeCipher, Version, Epoch-1, ConnectionStates0), {HsAfter, ConnectionStates} = encode_handshake_flight(lists:reverse(Flight1), Version, 1400, Epoch, ConnectionStates1), send(Transport, Socket, [EncChangeCipher, HsAfter]), + ssl_logger:debug(LogLevel, outbound, 'record', [EncChangeCipher]), + ssl_logger:debug(LogLevel, outbound, 'record', [HsAfter]), {State0#state{connection_states = ConnectionStates}, []}. retransmit_epoch(_StateName, #state{connection_states = ConnectionStates}) -> @@ -1174,7 +1201,8 @@ send_application_data(Data, From, _StateName, connection_env = #connection_env{negotiated_version = Version}, handshake_env = HsEnv, connection_states = ConnectionStates0, - ssl_options = #ssl_options{renegotiate_at = RenegotiateAt}} = State0) -> + ssl_options = #ssl_options{renegotiate_at = RenegotiateAt, + log_level = LogLevel}} = State0) -> case time_to_renegotiate(Data, ConnectionStates0, RenegotiateAt) of true -> @@ -1186,6 +1214,7 @@ send_application_data(Data, From, _StateName, State = State0#state{connection_states = ConnectionStates}, case send(Transport, Socket, Msgs) of ok -> + ssl_logger:debug(LogLevel, outbound, 'record', Msgs), ssl_connection:hibernate_after(connection, State, [{reply, From, ok}]); Result -> ssl_connection:hibernate_after(connection, State, [{reply, From, Result}]) diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl index 8e749e65b8..46e8348ce0 100644 --- a/lib/ssl/src/dtls_handshake.erl +++ b/lib/ssl/src/dtls_handshake.erl @@ -37,7 +37,7 @@ -export([fragment_handshake/2, encode_handshake/3]). %% Handshake decodeing --export([get_dtls_handshake/3]). +-export([get_dtls_handshake/4]). -type dtls_handshake() :: #client_hello{} | #hello_verify_request{} | ssl_handshake:ssl_handshake(). @@ -151,15 +151,15 @@ encode_handshake(Handshake, Version, Seq) -> %%-------------------------------------------------------------------- %%-------------------------------------------------------------------- --spec get_dtls_handshake(ssl_record:ssl_version(), binary(), #protocol_buffers{}) -> +-spec get_dtls_handshake(ssl_record:ssl_version(), binary(), #protocol_buffers{}, #ssl_options{}) -> {[dtls_handshake()], #protocol_buffers{}}. %% %% Description: Given buffered and new data from dtls_record, collects %% and returns it as a list of handshake messages, also returns %% possible leftover data in the new "protocol_buffers". %%-------------------------------------------------------------------- -get_dtls_handshake(Version, Fragment, ProtocolBuffers) -> - handle_fragments(Version, Fragment, ProtocolBuffers, []). +get_dtls_handshake(Version, Fragment, ProtocolBuffers, Options) -> + handle_fragments(Version, Fragment, ProtocolBuffers, Options, []). %%-------------------------------------------------------------------- %%% Internal functions @@ -310,20 +310,21 @@ address_to_bin({A,B,C,D,E,F,G,H}, Port) -> %%-------------------------------------------------------------------- -handle_fragments(Version, FragmentData, Buffers0, Acc) -> +handle_fragments(Version, FragmentData, Buffers0, Options, Acc) -> Fragments = decode_handshake_fragments(FragmentData), - do_handle_fragments(Version, Fragments, Buffers0, Acc). + do_handle_fragments(Version, Fragments, Buffers0, Options, Acc). -do_handle_fragments(_, [], Buffers, Acc) -> +do_handle_fragments(_, [], Buffers, _Options, Acc) -> {lists:reverse(Acc), Buffers}; -do_handle_fragments(Version, [Fragment | Fragments], Buffers0, Acc) -> +do_handle_fragments(Version, [Fragment | Fragments], Buffers0, Options, Acc) -> case reassemble(Version, Fragment, Buffers0) of {more_data, Buffers} when Fragments == [] -> {lists:reverse(Acc), Buffers}; {more_data, Buffers} -> - do_handle_fragments(Version, Fragments, Buffers, Acc); - {HsPacket, Buffers} -> - do_handle_fragments(Version, Fragments, Buffers, [HsPacket | Acc]) + do_handle_fragments(Version, Fragments, Buffers, Options, Acc); + {{Handshake, _} = HsPacket, Buffers} -> + ssl_logger:debug(Options#ssl_options.log_level, inbound, 'handshake', Handshake), + do_handle_fragments(Version, Fragments, Buffers, Options, [HsPacket | Acc]) end. decode_handshake(Version, <<?BYTE(Type), Bin/binary>>) -> @@ -363,9 +364,9 @@ decode_handshake(_Version, ?HELLO_VERIFY_REQUEST, <<?UINT24(_), ?UINT16(_), decode_handshake(Version, Tag, <<?UINT24(_), ?UINT16(_), ?UINT24(_), ?UINT24(_), Msg/binary>>) -> %% DTLS specifics stripped - decode_tls_thandshake(Version, Tag, Msg). + decode_tls_handshake(Version, Tag, Msg). -decode_tls_thandshake(Version, Tag, Msg) -> +decode_tls_handshake(Version, Tag, Msg) -> TLSVersion = dtls_v1:corresponding_tls_version(Version), ssl_handshake:decode_handshake(TLSVersion, Tag, Msg). diff --git a/lib/ssl/src/dtls_handshake.hrl b/lib/ssl/src/dtls_handshake.hrl index dab4038762..de2be1daeb 100644 --- a/lib/ssl/src/dtls_handshake.hrl +++ b/lib/ssl/src/dtls_handshake.hrl @@ -26,23 +26,13 @@ -ifndef(dtls_handshake). -define(dtls_handshake, true). +-include("tls_handshake.hrl"). %% Common TLS and DTLS records and Constantes -include("ssl_handshake.hrl"). %% Common TLS and DTLS records and Constantes -include("ssl_api.hrl"). -define(HELLO_VERIFY_REQUEST, 3). -define(HELLO_VERIFY_REQUEST_VERSION, {254, 255}). --record(client_hello, { - client_version, - random, - session_id, % opaque SessionID<0..32> - cookie, % opaque<2..2^16-1> - cipher_suites, % cipher_suites<2..2^16-1> - compression_methods, % compression_methods<1..2^8-1>, - %% Extensions - extensions - }). - -record(hello_verify_request, { protocol_version, cookie diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl index 2fe875da31..a4846f42c5 100644 --- a/lib/ssl/src/dtls_record.erl +++ b/lib/ssl/src/dtls_record.erl @@ -30,7 +30,7 @@ -include("ssl_cipher.hrl"). %% Handling of incoming data --export([get_dtls_records/3, init_connection_states/2, empty_connection_state/1]). +-export([get_dtls_records/4, init_connection_states/2, empty_connection_state/1]). -export([save_current_connection_state/2, next_epoch/2, get_connection_state_by_epoch/3, replay_detect/2, init_connection_state_seq/2, current_connection_state_epoch/2]). @@ -162,24 +162,25 @@ current_connection_state_epoch(#{current_write := #{epoch := Epoch}}, Epoch. %%-------------------------------------------------------------------- --spec get_dtls_records(binary(), [ssl_record:ssl_version()], binary()) -> {[binary()], binary()} | #alert{}. +-spec get_dtls_records(binary(), [ssl_record:ssl_version()], binary(), + #ssl_options{}) -> {[binary()], binary()} | #alert{}. %% %% Description: Given old buffer and new data from UDP/SCTP, packs up a records %% and returns it as a list of tls_compressed binaries also returns leftover %% data %%-------------------------------------------------------------------- -get_dtls_records(Data, Versions, Buffer) -> +get_dtls_records(Data, Versions, Buffer, SslOpts) -> BinData = list_to_binary([Buffer, Data]), case erlang:byte_size(BinData) of N when N >= 3 -> case assert_version(BinData, Versions) of true -> - get_dtls_records_aux(BinData, []); + get_dtls_records_aux(BinData, [], SslOpts); false -> ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) end; _ -> - get_dtls_records_aux(BinData, []) + get_dtls_records_aux(BinData, [], SslOpts) end. %%==================================================================== @@ -409,42 +410,47 @@ assert_version(<<?BYTE(_), ?BYTE(MajVer), ?BYTE(MinVer), _/binary>>, Versions) - get_dtls_records_aux(<<?BYTE(?APPLICATION_DATA),?BYTE(MajVer),?BYTE(MinVer), ?UINT16(Epoch), ?UINT48(SequenceNumber), - ?UINT16(Length), Data:Length/binary, Rest/binary>>, - Acc) -> + ?UINT16(Length), Data:Length/binary, Rest/binary>> = RawDTLSRecord, + Acc, SslOpts) -> + ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', [RawDTLSRecord]), get_dtls_records_aux(Rest, [#ssl_tls{type = ?APPLICATION_DATA, version = {MajVer, MinVer}, epoch = Epoch, sequence_number = SequenceNumber, - fragment = Data} | Acc]); + fragment = Data} | Acc], SslOpts); get_dtls_records_aux(<<?BYTE(?HANDSHAKE),?BYTE(MajVer),?BYTE(MinVer), ?UINT16(Epoch), ?UINT48(SequenceNumber), ?UINT16(Length), - Data:Length/binary, Rest/binary>>, Acc) when MajVer >= 128 -> + Data:Length/binary, Rest/binary>> = RawDTLSRecord, + Acc, SslOpts) when MajVer >= 128 -> + ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', [RawDTLSRecord]), get_dtls_records_aux(Rest, [#ssl_tls{type = ?HANDSHAKE, version = {MajVer, MinVer}, epoch = Epoch, sequence_number = SequenceNumber, - fragment = Data} | Acc]); + fragment = Data} | Acc], SslOpts); get_dtls_records_aux(<<?BYTE(?ALERT),?BYTE(MajVer),?BYTE(MinVer), ?UINT16(Epoch), ?UINT48(SequenceNumber), ?UINT16(Length), Data:Length/binary, - Rest/binary>>, Acc) -> + Rest/binary>> = RawDTLSRecord, Acc, SslOpts) -> + ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', [RawDTLSRecord]), get_dtls_records_aux(Rest, [#ssl_tls{type = ?ALERT, version = {MajVer, MinVer}, epoch = Epoch, sequence_number = SequenceNumber, - fragment = Data} | Acc]); + fragment = Data} | Acc], SslOpts); get_dtls_records_aux(<<?BYTE(?CHANGE_CIPHER_SPEC),?BYTE(MajVer),?BYTE(MinVer), ?UINT16(Epoch), ?UINT48(SequenceNumber), - ?UINT16(Length), Data:Length/binary, Rest/binary>>, - Acc) -> + ?UINT16(Length), Data:Length/binary, Rest/binary>> = RawDTLSRecord, + Acc, SslOpts) -> + ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', [RawDTLSRecord]), get_dtls_records_aux(Rest, [#ssl_tls{type = ?CHANGE_CIPHER_SPEC, version = {MajVer, MinVer}, epoch = Epoch, sequence_number = SequenceNumber, - fragment = Data} | Acc]); + fragment = Data} | Acc], SslOpts); get_dtls_records_aux(<<?BYTE(_), ?BYTE(_MajVer), ?BYTE(_MinVer), ?UINT16(Length), _/binary>>, - _Acc) when Length > ?MAX_CIPHER_TEXT_LENGTH -> + _Acc, _) when Length > ?MAX_CIPHER_TEXT_LENGTH -> ?ALERT_REC(?FATAL, ?RECORD_OVERFLOW); -get_dtls_records_aux(Data, Acc) -> +get_dtls_records_aux(Data, Acc, _) -> case size(Data) =< ?MAX_CIPHER_TEXT_LENGTH + ?INITIAL_BYTES of true -> {lists:reverse(Acc), Data}; diff --git a/lib/ssl/src/ssl_logger.erl b/lib/ssl/src/ssl_logger.erl index d59a0dfda2..b82b3937a1 100644 --- a/lib/ssl/src/ssl_logger.erl +++ b/lib/ssl/src/ssl_logger.erl @@ -35,6 +35,7 @@ -include("ssl_cipher.hrl"). -include("ssl_internal.hrl"). -include("tls_handshake.hrl"). +-include("dtls_handshake.hrl"). -include("tls_handshake_1_3.hrl"). -include_lib("kernel/include/logger.hrl"). @@ -48,7 +49,7 @@ format(#{level:= _Level, msg:= {report, Msg}, meta:= _Meta}, _Config0) -> protocol := Protocol, message := Content} = Msg, case Protocol of - 'tls_record' -> + 'record' -> BinMsg = case Content of #ssl_tls{} -> @@ -66,7 +67,7 @@ format(#{level:= _Level, msg:= {report, Msg}, meta:= _Meta}, _Config0) -> %% Stateful logging debug(Level, Direction, Protocol, Message) when (Direction =:= inbound orelse Direction =:= outbound) andalso - (Protocol =:= 'tls_record' orelse Protocol =:= 'handshake') -> + (Protocol =:= 'record' orelse Protocol =:= 'handshake') -> case logger:compare_levels(Level, debug) of lt -> ?LOG_DEBUG(#{direction => Direction, @@ -130,6 +131,11 @@ parse_handshake(Direction, #server_hello{ [?rec_info(server_hello, ServerHello#server_hello{cipher_suite = CipherSuite})]), {Header, Message}; +parse_handshake(Direction, #hello_verify_request{} = HelloVerifyRequest) -> + Header = io_lib:format("~s Handshake, HelloVerifyRequest", + [header_prefix(Direction)]), + Message = io_lib:format("~p", [?rec_info(hello_verify_request, HelloVerifyRequest)]), + {Header, Message}; parse_handshake(Direction, #certificate{} = Certificate) -> Header = io_lib:format("~s Handshake, Certificate", [header_prefix(Direction)]), @@ -228,9 +234,12 @@ version({3,1}) -> "TLS 1.0"; version({3,0}) -> "SSL 3.0"; +version({254,253}) -> + "DTLS 1.2"; +version({254,255}) -> + "DTLS 1.0"; version({M,N}) -> - io_lib:format("TLS [0x0~B0~B]", [M,N]). - + io_lib:format("TLS/DTLS [0x0~B0~B]", [M,N]). header_prefix(inbound) -> "<<<"; @@ -264,8 +273,12 @@ tls_record_version([<<?BYTE(B),?BYTE(3),?BYTE(1),_/binary>>|_]) -> io_lib:format("TLS 1.0 Record Protocol, ~s", [msg_type(B)]); tls_record_version([<<?BYTE(B),?BYTE(3),?BYTE(0),_/binary>>|_]) -> io_lib:format("SSL 3.0 Record Protocol, ~s", [msg_type(B)]); +tls_record_version([<<?BYTE(B),?BYTE(254),?BYTE(253),_/binary>>|_]) -> + io_lib:format("DTLS 1.2 Record Protocol, ~s", [msg_type(B)]); +tls_record_version([<<?BYTE(B),?BYTE(254),?BYTE(255),_/binary>>|_]) -> + io_lib:format("DTLS 1.0 Record Protocol, ~s", [msg_type(B)]); tls_record_version([<<?BYTE(B),?BYTE(M),?BYTE(N),_/binary>>|_]) -> - io_lib:format("TLS [0x0~B0~B] Record Protocol, ~s", [M, N, msg_type(B)]). + io_lib:format("TLS/DTLS [0x0~B0~B] Record Protocol, ~s", [M, N, msg_type(B)]). msg_type(20) -> "change_cipher_spec"; @@ -346,12 +359,12 @@ convert_to_hex(P, [H|T], Row, Acc, C) when is_integer(H) -> C + 1). -row_prefix(tls_record, N) -> +row_prefix(_ , N) -> S = string:pad(string:to_lower(erlang:integer_to_list(N, 16)),4,leading,$0), lists:reverse(lists:flatten(S ++ " - ")). -end_row(tls_record, Row) -> +end_row(_, Row) -> Row ++ " ". diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl index 9a896971ef..8eb9e56375 100644 --- a/lib/ssl/src/tls_connection.erl +++ b/lib/ssl/src/tls_connection.erl @@ -362,7 +362,7 @@ queue_handshake(Handshake, #state{handshake_env = #handshake_env{tls_handshake_h {BinHandshake, ConnectionStates, Hist} = encode_handshake(Handshake, Version, ConnectionStates0, Hist0), ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'handshake', Handshake), - ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'tls_record', BinHandshake), + ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'record', BinHandshake), State0#state{connection_states = ConnectionStates, handshake_env = HsEnv#handshake_env{tls_handshake_history = Hist}, @@ -381,7 +381,7 @@ queue_change_cipher(Msg, #state{connection_env = #connection_env{negotiated_vers connection_states = ConnectionStates0} = State0) -> {BinChangeCipher, ConnectionStates} = encode_change_cipher(Msg, Version, ConnectionStates0), - ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'tls_record', BinChangeCipher), + ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'record', BinChangeCipher), State0#state{connection_states = ConnectionStates, flight_buffer = Flight0 ++ [BinChangeCipher]}. @@ -430,7 +430,7 @@ send_alert(Alert, #state{static_env = #static_env{socket = Socket, {BinMsg, ConnectionStates} = encode_alert(Alert, Version, ConnectionStates0), tls_socket:send(Transport, Socket, BinMsg), - ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'tls_record', BinMsg), + ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'record', BinMsg), StateData0#state{connection_states = ConnectionStates}. %% If an ALERT sent in the connection state, should cause the TLS @@ -527,7 +527,7 @@ init({call, From}, {start, Timeout}, encode_handshake(Hello, HelloVersion, ConnectionStates0, Handshake0), tls_socket:send(Transport, Socket, BinMsg), ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'handshake', Hello), - ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'tls_record', BinMsg), + ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'record', BinMsg), State = State0#state{connection_states = ConnectionStates, connection_env = CEnv#connection_env{negotiated_version = HelloVersion}, %% Requested version diff --git a/lib/ssl/src/tls_handshake.hrl b/lib/ssl/src/tls_handshake.hrl index f6644f64af..fc67bb61fd 100644 --- a/lib/ssl/src/tls_handshake.hrl +++ b/lib/ssl/src/tls_handshake.hrl @@ -32,6 +32,7 @@ client_version, random, session_id, % opaque SessionID<0..32> + cookie, % opaque<2..2^16-1> cipher_suites, % cipher_suites<2..2^16-1> compression_methods, % compression_methods<1..2^8-1>, %% Extensions diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl index 94506b8edc..9f0c588cb6 100644 --- a/lib/ssl/src/tls_record.erl +++ b/lib/ssl/src/tls_record.erl @@ -503,7 +503,7 @@ validate_tls_record_length(Versions, {_,Size0,_} = Q0, SslOpts, Acc, Type, Versi %% Complete record {Fragment, Q} = binary_from_front(Length, Q0), Record = #ssl_tls{type = Type, version = Version, fragment = Fragment}, - ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'tls_record', Record), + ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', Record), decode_tls_records(Versions, Q, SslOpts, [Record|Acc], undefined, undefined, undefined); true -> {lists:reverse(Acc), diff --git a/lib/ssl/src/tls_sender.erl b/lib/ssl/src/tls_sender.erl index ba73eddf0b..d0604565e3 100644 --- a/lib/ssl/src/tls_sender.erl +++ b/lib/ssl/src/tls_sender.erl @@ -415,7 +415,7 @@ send_tls_alert(#alert{} = Alert, {BinMsg, ConnectionStates} = tls_record:encode_alert_record(Alert, Version, ConnectionStates0), tls_socket:send(Transport, Socket, BinMsg), - ssl_logger:debug(LogLevel, outbound, 'tls_record', BinMsg), + ssl_logger:debug(LogLevel, outbound, 'record', BinMsg), StateData0#data{connection_states = ConnectionStates}. send_application_data(Data, From, StateName, @@ -437,12 +437,12 @@ send_application_data(Data, From, StateName, StateData = StateData0#data{connection_states = ConnectionStates}, case tls_socket:send(Transport, Socket, Msgs) of ok when DistHandle =/= undefined -> - ssl_logger:debug(LogLevel, outbound, 'tls_record', Msgs), + ssl_logger:debug(LogLevel, outbound, 'record', Msgs), {next_state, StateName, StateData, []}; Reason when DistHandle =/= undefined -> {next_state, death_row, StateData, [{state_timeout, 5000, Reason}]}; ok -> - ssl_logger:debug(LogLevel, outbound, 'tls_record', Msgs), + ssl_logger:debug(LogLevel, outbound, 'record', Msgs), {next_state, StateName, StateData, [{reply, From, ok}]}; Result -> {next_state, StateName, StateData, [{reply, From, Result}]} diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml index ff088de8ab..d2ac6a75b1 100644 --- a/lib/stdlib/doc/src/ets.xml +++ b/lib/stdlib/doc/src/ets.xml @@ -2038,9 +2038,8 @@ true</pre> <p>This function provides an efficient way to update one or more counters, without the trouble of having to look up an object, update the object by incrementing an element, and insert the resulting - object into the table again. (The update is done atomically, - that is, no process - can access the ETS table in the middle of the operation.)</p> + object into the table again. The operation is guaranteed to be + <seealso marker="#concurrency">atomic and isolated</seealso>.</p> <p>This function destructively update the object with key <c><anno>Key</anno></c> in table <c><anno>Tab</anno></c> by adding <c><anno>Incr</anno></c> to the element at position diff --git a/lib/stdlib/doc/src/filename.xml b/lib/stdlib/doc/src/filename.xml index ae42846c6b..60b7eb3436 100644 --- a/lib/stdlib/doc/src/filename.xml +++ b/lib/stdlib/doc/src/filename.xml @@ -546,7 +546,7 @@ true <p><em>Examples:</em></p> <pre> 20> <input>filename:rootname("/beam.src/kalle").</input> -/beam.src/kalle" +"/beam.src/kalle" 21> <input>filename:rootname("/beam.src/foo.erl").</input> "/beam.src/foo" 22> <input>filename:rootname("/beam.src/foo.erl", ".erl").</input> diff --git a/lib/stdlib/src/otp_internal.erl b/lib/stdlib/src/otp_internal.erl index f2b50c354a..b95cb8f525 100644 --- a/lib/stdlib/src/otp_internal.erl +++ b/lib/stdlib/src/otp_internal.erl @@ -44,8 +44,19 @@ obsolete(Module, Name, Arity) -> no end. -obsolete_1(net, _, _) -> - {deprecated, "module 'net' obsolete; use 'net_adm'"}; +obsolete_1(net, call, 4) -> + {deprecated, {rpc, call, 4}}; +obsolete_1(net, cast, 4) -> + {deprecated, {rpc, cast, 4}}; +obsolete_1(net, broadcast, 3) -> + {deprecated, {rpc, eval_everywhere, 3}}; +obsolete_1(net, ping, 1) -> + {deprecated, {net_adm, ping, 1}}; +obsolete_1(net, sleep, 1) -> + {deprecated, "Use 'receive after T -> ok end' instead"}; +obsolete_1(net, relay, 1) -> + {deprecated, {slave, relay, 1}}; + obsolete_1(erlang, now, 0) -> {deprecated, |