diff options
214 files changed, 7297 insertions, 3673 deletions
diff --git a/bootstrap/lib/compiler/ebin/beam_receive.beam b/bootstrap/lib/compiler/ebin/beam_receive.beam Binary files differindex afc5eaa333..2f1fcc9fe1 100644 --- a/bootstrap/lib/compiler/ebin/beam_receive.beam +++ b/bootstrap/lib/compiler/ebin/beam_receive.beam diff --git a/bootstrap/lib/kernel/ebin/application_master.beam b/bootstrap/lib/kernel/ebin/application_master.beam Binary files differindex e73de8b687..0af5dc0e00 100644 --- a/bootstrap/lib/kernel/ebin/application_master.beam +++ b/bootstrap/lib/kernel/ebin/application_master.beam diff --git a/bootstrap/lib/kernel/ebin/disk_log.beam b/bootstrap/lib/kernel/ebin/disk_log.beam Binary files differindex 96f63b364c..5ec7cfe5eb 100644 --- a/bootstrap/lib/kernel/ebin/disk_log.beam +++ b/bootstrap/lib/kernel/ebin/disk_log.beam diff --git a/bootstrap/lib/kernel/ebin/file.beam b/bootstrap/lib/kernel/ebin/file.beam Binary files differindex d74d356bfb..9906ddd1fe 100644 --- a/bootstrap/lib/kernel/ebin/file.beam +++ b/bootstrap/lib/kernel/ebin/file.beam diff --git a/bootstrap/lib/kernel/ebin/file_io_server.beam b/bootstrap/lib/kernel/ebin/file_io_server.beam Binary files differindex 2c617ae7e4..6916cdcc58 100644 --- a/bootstrap/lib/kernel/ebin/file_io_server.beam +++ b/bootstrap/lib/kernel/ebin/file_io_server.beam diff --git a/bootstrap/lib/kernel/ebin/file_server.beam b/bootstrap/lib/kernel/ebin/file_server.beam Binary files differindex 211b2df642..2ebded8ba5 100644 --- a/bootstrap/lib/kernel/ebin/file_server.beam +++ b/bootstrap/lib/kernel/ebin/file_server.beam diff --git a/bootstrap/lib/kernel/ebin/inet_gethost_native.beam b/bootstrap/lib/kernel/ebin/inet_gethost_native.beam Binary files differindex f1f3bdcd8d..03cb896d1c 100644 --- a/bootstrap/lib/kernel/ebin/inet_gethost_native.beam +++ b/bootstrap/lib/kernel/ebin/inet_gethost_native.beam diff --git a/bootstrap/lib/kernel/ebin/rpc.beam b/bootstrap/lib/kernel/ebin/rpc.beam Binary files differindex 305779539a..4782908361 100644 --- a/bootstrap/lib/kernel/ebin/rpc.beam +++ b/bootstrap/lib/kernel/ebin/rpc.beam diff --git a/bootstrap/lib/stdlib/ebin/dets.beam b/bootstrap/lib/stdlib/ebin/dets.beam Binary files differindex b9a5866d30..979a471a98 100644 --- a/bootstrap/lib/stdlib/ebin/dets.beam +++ b/bootstrap/lib/stdlib/ebin/dets.beam diff --git a/bootstrap/lib/stdlib/ebin/epp.beam b/bootstrap/lib/stdlib/ebin/epp.beam Binary files differindex 59e07c65c2..879370ef87 100644 --- a/bootstrap/lib/stdlib/ebin/epp.beam +++ b/bootstrap/lib/stdlib/ebin/epp.beam diff --git a/bootstrap/lib/stdlib/ebin/gen.beam b/bootstrap/lib/stdlib/ebin/gen.beam Binary files differindex 77d0bf806a..277a881630 100644 --- a/bootstrap/lib/stdlib/ebin/gen.beam +++ b/bootstrap/lib/stdlib/ebin/gen.beam diff --git a/bootstrap/lib/stdlib/ebin/gen_event.beam b/bootstrap/lib/stdlib/ebin/gen_event.beam Binary files differindex cee9abfd5d..313bd2dcce 100644 --- a/bootstrap/lib/stdlib/ebin/gen_event.beam +++ b/bootstrap/lib/stdlib/ebin/gen_event.beam diff --git a/bootstrap/lib/stdlib/ebin/gen_fsm.beam b/bootstrap/lib/stdlib/ebin/gen_fsm.beam Binary files differindex 1fcabac0d7..561b562115 100644 --- a/bootstrap/lib/stdlib/ebin/gen_fsm.beam +++ b/bootstrap/lib/stdlib/ebin/gen_fsm.beam diff --git a/bootstrap/lib/stdlib/ebin/gen_server.beam b/bootstrap/lib/stdlib/ebin/gen_server.beam Binary files differindex f0fa4f03a9..ac948974cc 100644 --- a/bootstrap/lib/stdlib/ebin/gen_server.beam +++ b/bootstrap/lib/stdlib/ebin/gen_server.beam diff --git a/bootstrap/lib/stdlib/ebin/io.beam b/bootstrap/lib/stdlib/ebin/io.beam Binary files differindex 73cf837dfb..19ae65b3bf 100644 --- a/bootstrap/lib/stdlib/ebin/io.beam +++ b/bootstrap/lib/stdlib/ebin/io.beam diff --git a/bootstrap/lib/stdlib/ebin/lists.beam b/bootstrap/lib/stdlib/ebin/lists.beam Binary files differindex cf189f2ae5..332a2088d0 100644 --- a/bootstrap/lib/stdlib/ebin/lists.beam +++ b/bootstrap/lib/stdlib/ebin/lists.beam diff --git a/bootstrap/lib/stdlib/ebin/supervisor.beam b/bootstrap/lib/stdlib/ebin/supervisor.beam Binary files differindex 9bf3439674..e68e59dacd 100644 --- a/bootstrap/lib/stdlib/ebin/supervisor.beam +++ b/bootstrap/lib/stdlib/ebin/supervisor.beam diff --git a/bootstrap/lib/stdlib/ebin/sys.beam b/bootstrap/lib/stdlib/ebin/sys.beam Binary files differindex 841874f1f5..7e2ba3c19b 100644 --- a/bootstrap/lib/stdlib/ebin/sys.beam +++ b/bootstrap/lib/stdlib/ebin/sys.beam diff --git a/bootstrap/lib/stdlib/ebin/timer.beam b/bootstrap/lib/stdlib/ebin/timer.beam Binary files differindex 5f8f714a78..4269f1ebec 100644 --- a/bootstrap/lib/stdlib/ebin/timer.beam +++ b/bootstrap/lib/stdlib/ebin/timer.beam diff --git a/erts/configure.in b/erts/configure.in index 2ee907b6e4..b9c9a76ef6 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -1342,6 +1342,17 @@ if test "x$TERMCAP_LIB" != "x"; then AC_DEFINE(HAVE_TERMCAP, 1, [Define if termcap functions exists]) fi +if test "X$host" != "Xwin32"; then + AC_MSG_CHECKING(for wcwidth) + AC_TRY_LINK([#include <wchar.h>], [wcwidth(0);], + have_wcwidth=yes, have_wcwidth=no) + if test $have_wcwidth = yes; then + AC_MSG_RESULT([yes]) + AC_DEFINE(HAVE_WCWIDTH, [1], + [Define to 1 if you have a `wcwidth' function.]) + fi +fi + dnl ------------- dnl zlib dnl ------------- @@ -3576,10 +3587,7 @@ DED_EXT=so case $host_os in win32) DED_EXT=dll;; darwin*) - DED_CFLAGS="$DED_CFLAGS -fno-common" - if test "X$STATIC_CFLAGS" = "X"; then - STATIC_CFLAGS="-mdynamic-no-pic" - fi;; + DED_CFLAGS="$DED_CFLAGS -fno-common";; *) ;; esac diff --git a/erts/doc/src/crash_dump.xml b/erts/doc/src/crash_dump.xml index b3c4671c3d..73212e6143 100644 --- a/erts/doc/src/crash_dump.xml +++ b/erts/doc/src/crash_dump.xml @@ -290,6 +290,10 @@ <em>Stack+heap</em>, <em>OldHeap</em>, <em>Heap unused</em> and <em>OldHeap unused</em> do not exist. Instead this field presents the size of the process' stack.</item> + <tag><em>Memory</em></tag> + <item>The total memory used by this process. This includes call stack, + heap and internal structures. Same as <seealso marker="erlang#process_info-2">erlang:process_info(Pid,memory)</seealso>. + </item> <tag><em>Program counter</em></tag> <item>The current instruction pointer. This is only interesting for runtime system developers. The function into which the program diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index 1f7c5b5a7f..a68e62d051 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -536,15 +536,35 @@ <tag><marker id="file_name_encoding"></marker><c><![CDATA[+fnl]]></c></tag> <item> <p>The VM works with file names as if they are encoded using the ISO-latin-1 encoding, disallowing Unicode characters with codepoints beyond 255. This is default on operating systems that have transparent file naming, i.e. all Unixes except MacOSX.</p> + <p>See <seealso marker="stdlib:unicode_usage#unicode_file_names">STDLIB User's Guide</seealso> for more infomation about unicode file names.</p> </item> - <tag><c><![CDATA[+fnu]]></c></tag> + <tag><c><![CDATA[+fnu[{w|i|e}]]]></c></tag> <item> <p>The VM works with file names as if they are encoded using UTF-8 (or some other system specific Unicode encoding). This is the default on operating systems that enforce Unicode encoding, i.e. Windows and MacOSX.</p> - <p>By enabling Unicode file name translation on systems where this is not default, you open up to the possibility that some file names can not be interpreted by the VM and therefore will be returned to the program as raw binaries. The option is therefore considered experimental.</p> - </item> - <tag><c><![CDATA[+fna]]></c></tag> - <item> - <p>Selection between <c>+fnl</c> and <c>+fnu</c> is done based on the current locale settings in the OS, meaning that if you have set your terminal for UTF-8 encoding, the filesystem is expected to use the same encoding for filenames (use with care).</p> + <p>The <c>+fnu</c> switch can be followed by <c>w</c>, + <c>i</c>, or <c>e</c> to control the way wrongly encoded file + names are to be reported. <c>w</c> means that a warning is + sent to the <c>error_logger</c> whenever a wrongly encoded + file name is "skipped" in directory listings, <c>i</c> means + that those wrongly encoded file names are silently ignored and + <c>e</c> means that the API function will return an error + whenever a wrongly encoded file (or directory) name is + encountered. <c>w</c> is the default. Note that + <c>file:read_link/1</c> will always return an error if the + link points to an invalid file name.</p> + <p>See <seealso marker="stdlib:unicode_usage#unicode_file_names">STDLIB User's Guide</seealso> for more infomation about unicode file names.</p> + </item> + <tag><c><![CDATA[+fna[{w|i|e}]]]></c></tag> + <item> + <p>Selection between <c>+fnl</c> and <c>+fnu</c> is done based on the current locale settings in the OS, meaning that if you have set your terminal for UTF-8 encoding, the filesystem is expected to use the same encoding for file names (use with care).</p> + <p>The <c>+fna</c> switch can be followed by <c>w</c>, + <c>i</c>, or <c>e</c>. This will have effect if the locale + settings cause the behavior of <c>+fnu</c> to be selected. + See the description of <c>+fnu</c> above. If the locale + settings cause the behavior of <c>+fnl</c> to be selected, + then <c>w</c>, <c>i</c>, or <c>e</c> will not have any + effect.</p> + <p>See <seealso marker="stdlib:unicode_usage#unicode_file_names">STDLIB User's Guide</seealso> for more infomation about unicode file names.</p> </item> <tag><c><![CDATA[+hms Size]]></c></tag> <item> @@ -571,9 +591,9 @@ </item> <tag><c><![CDATA[+L]]></c></tag> <item> - <p>Don't load information about source filenames and line numbers. + <p>Don't load information about source file names and line numbers. This will save some memory, but exceptions will not contain - information about the filenames and line numbers. + information about the file names and line numbers. </p> </item> <tag><marker id="erts_alloc"><c><![CDATA[+MFlag Value]]></c></marker></tag> diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index c9c46afa4b..3a2c644ff7 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -30,6 +30,29 @@ </header> <p>This document describes the changes made to the ERTS application.</p> +<section><title>Erts 5.10.1.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + A bug in the implementation of offline schedulers has + been fixed. The bug was introduced in OTP-R16A/ERTS-5.10, + and caused work-stealing between schedulers to fail. This + in turn, caused work to accumulate in some run-queues. + The bug was only triggered when there were offline + schedulers in the system, i.e., when the amount of online + schedulers was less than the total amount of schedulers. + The effect of the bug got more severe the larger amount + of offline schedulers the system had.</p> + <p> + Own Id: OTP-11022 Aux Id: OTP-9892 </p> + </item> + </list> + </section> + +</section> + <section><title>Erts 5.10.1.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 4f2f647742..2031ec3a4c 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -48,6 +48,7 @@ CREATE_DIRS= LDFLAGS=@LDFLAGS@ ARFLAGS=rc +OMIT_OMIT_FP=no ifeq ($(TYPE),debug) PURIFY = @@ -113,6 +114,13 @@ TYPEMARKER = .lcnt TYPE_FLAGS = @CFLAGS@ -DERTS_ENABLE_LOCK_COUNT else +ifeq ($(TYPE),frmptr) +PURIFY = +OMIT_OMIT_FP=yes +TYPEMARKER = .frmptr +TYPE_FLAGS = @CFLAGS@ -DERTS_FRMPTR +else + # If type isn't one of the above, it *is* opt type... override TYPE=opt PURIFY = @@ -126,6 +134,7 @@ endif endif endif endif +endif # # NOTE: When adding a new type update ERL_BUILD_TYPE_MARKER in sys/unix/sys.c @@ -219,8 +228,6 @@ LIB_PREFIX=lib LIB_SUFFIX=.a endif -OMIT_OMIT_FP=no - ifeq (@EMU_LOCK_CHECKING@,yes) NO_INLINE_FUNCTIONS=true endif @@ -238,7 +245,7 @@ ifeq ($(NO_INLINE_FUNCTIONS),true) GEN_OPT_FLGS = $(OPT_LEVEL) -fno-inline-functions else ifeq ($(OMIT_OMIT_FP),yes) -GEN_OPT_FLGS = $(OPT_LEVEL) +GEN_OPT_FLGS = $(OPT_LEVEL) -fno-omit-frame-pointer else GEN_OPT_FLGS = $(OPT_LEVEL) -fomit-frame-pointer endif diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index bd4e5a52d0..4193eb4f3f 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -2964,19 +2964,19 @@ gen_put_integer(LoaderState* stp, GenOpArg Fail, GenOpArg Size, NEW_GENOP(stp, op); NATIVE_ENDIAN(Flags); - if (Size.type == TAG_i && Size.val < 0) { - error: /* Negative size must fail */ - op->op = genop_badarg_1; - op->arity = 1; - op->a[0] = Fail; - } else if (Size.type == TAG_i) { + if (Size.type == TAG_i) { op->op = genop_i_new_bs_put_integer_imm_4; op->arity = 4; op->a[0] = Fail; op->a[1].type = TAG_u; if (!safe_mul(Size.val, Unit.val, &op->a[1].val)) { - goto error; + error: + op->op = genop_badarg_1; + op->arity = 1; + op->a[0] = Fail; + op->next = NULL; + return op; } op->a[1].val = Size.val * Unit.val; op->a[2].type = Flags.type; diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 8bc994c8c3..dc8e9101de 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -154,8 +154,10 @@ bif erts_internal:port_command/3 bif erts_internal:port_control/3 bif erts_internal:port_close/1 bif erts_internal:port_connect/2 -bif erts_internal:port_set_data/2 -bif erts_internal:port_get_data/1 + +# inet_db support +bif erlang:port_set_data/2 +bif erlang:port_get_data/1 # Tracing & debugging. bif erlang:trace_pattern/2 diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c index 9260c0c4b8..6f9b171224 100644 --- a/erts/emulator/beam/big.c +++ b/erts/emulator/beam/big.c @@ -2528,17 +2528,18 @@ const byte d_base_exp_lookup[] = { 31, 19, 15, 13, 11, 11, 10, 9, 9, 8, 8, 8, 8, * end * How much can the characters which fit in 31 bit represent */ -const Uint d_base_base_lookup[] = { 2147483648, 1162261467, 1073741824, - 1220703125, 362797056, 1977326743, 1073741824, 387420489, 1000000000, - 214358881, 429981696, 815730721, 1475789056, 170859375, 268435456, - 410338673, 612220032, 893871739, 1280000000, 1801088541, 113379904, - 148035889, 191102976, 244140625, 308915776, 387420489, 481890304, - 594823321, 729000000, 887503681, 1073741824, 1291467969, 1544804416, - 1838265625, 60466176, 69343957, 79235168, 90224199, 102400000, - 115856201, 130691232, 147008443, 164916224, 184528125, 205962976, - 229345007, 254803968, 282475249, 312500000, 345025251, 380204032, - 418195493, 459165024, 503284375, 550731776, 601692057, 656356768, - 714924299, 777600000, 844596301, 916132832, 992436543, 1073741824 }; +const Uint d_base_base_lookup[] = { 2147483648u, 1162261467u, 1073741824u, + 1220703125u, 362797056u, 1977326743u, 1073741824u, 387420489u, + 1000000000u, 214358881u, 429981696u, 815730721u, 1475789056u, + 170859375u, 268435456u, 410338673u, 612220032u, 893871739u, 1280000000u, + 1801088541u, 113379904u, 148035889u, 191102976u, 244140625u, 308915776u, + 387420489u, 481890304u, 594823321u, 729000000u, 887503681u, 1073741824u, + 1291467969u, 1544804416u, 1838265625u, 60466176u, 69343957u, 79235168u, + 90224199u, 102400000u, 115856201u, 130691232u, 147008443u, 164916224u, + 184528125u, 205962976u, 229345007u, 254803968u, 282475249u, 312500000u, + 345025251u, 380204032u, 418195493u, 459165024u, 503284375u, 550731776u, + 601692057u, 656356768u, 714924299u, 777600000u, 844596301u, 916132832u, + 992436543u, 1073741824u }; Eterm erts_chars_to_integer(Process *BIF_P, char *bytes, Uint size, const int base) { diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c index 9aa1e5f30d..e34fc8388c 100644 --- a/erts/emulator/beam/break.c +++ b/erts/emulator/beam/break.c @@ -331,6 +331,7 @@ print_process_info(int to, void *to_arg, Process *p) erts_print(to, to_arg, "Heap unused: %bpu\n", (p->hend - p->htop)); erts_print(to, to_arg, "OldHeap unused: %bpu\n", (OLD_HEAP(p) == NULL) ? 0 : (OLD_HEND(p) - OLD_HTOP(p)) ); + erts_print(to, to_arg, "Memory: %beu\n", erts_process_memory(p)); if (garbing) { print_garb_info(to, to_arg, p); diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index 0781665f05..44f4eb9d43 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -455,7 +455,7 @@ int erts_do_net_exits(DistEntry *dep, Eterm reason) Eterm nd_reason = (reason == am_no_network ? am_no_network : am_net_kernel_terminated); - erts_smp_rwmtx_rwlock(&erts_dist_table_rwmtx); + erts_smp_rwmtx_rlock(&erts_dist_table_rwmtx); for (tdep = erts_hidden_dist_entries; tdep; tdep = tdep->next) no_dist_port++; @@ -464,7 +464,7 @@ int erts_do_net_exits(DistEntry *dep, Eterm reason) /* KILL all port controllers */ if (no_dist_port == 0) - erts_smp_rwmtx_rwunlock(&erts_dist_table_rwmtx); + erts_smp_rwmtx_runlock(&erts_dist_table_rwmtx); else { Eterm def_buf[128]; int i = 0; @@ -483,7 +483,7 @@ int erts_do_net_exits(DistEntry *dep, Eterm reason) ASSERT(is_internal_port(tdep->cid)); dist_port[i++] = tdep->cid; } - erts_smp_rwmtx_rwunlock(&erts_dist_table_rwmtx); + erts_smp_rwmtx_runlock(&erts_dist_table_rwmtx); for (i = 0; i < no_dist_port; i++) { Port *prt = erts_port_lookup(dist_port[i], @@ -2560,9 +2560,9 @@ BIF_RETTYPE setnode_2(BIF_ALIST_2) erts_smp_proc_unlock(net_kernel, ERTS_PROC_LOCK_MAIN); #ifdef DEBUG - erts_smp_rwmtx_rwlock(&erts_dist_table_rwmtx); + erts_smp_rwmtx_rlock(&erts_dist_table_rwmtx); ASSERT(!erts_visible_dist_entries && !erts_hidden_dist_entries); - erts_smp_rwmtx_rwunlock(&erts_dist_table_rwmtx); + erts_smp_rwmtx_runlock(&erts_dist_table_rwmtx); #endif erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); @@ -2912,7 +2912,7 @@ BIF_RETTYPE nodes_1(BIF_ALIST_1) length = 0; - erts_smp_rwmtx_rwlock(&erts_dist_table_rwmtx); + erts_smp_rwmtx_rlock(&erts_dist_table_rwmtx); ASSERT(erts_no_of_not_connected_dist_entries >= 0); ASSERT(erts_no_of_hidden_dist_entries >= 0); @@ -2929,7 +2929,7 @@ BIF_RETTYPE nodes_1(BIF_ALIST_1) result = NIL; if (length == 0) { - erts_smp_rwmtx_rwunlock(&erts_dist_table_rwmtx); + erts_smp_rwmtx_runlock(&erts_dist_table_rwmtx); goto done; } @@ -2958,7 +2958,7 @@ BIF_RETTYPE nodes_1(BIF_ALIST_1) hp += 2; } ASSERT(endp == hp); - erts_smp_rwmtx_rwunlock(&erts_dist_table_rwmtx); + erts_smp_rwmtx_runlock(&erts_dist_table_rwmtx); done: UnUseTmpHeap(2,BIF_P); diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index d748f86d75..c19a2647e4 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -495,6 +495,38 @@ refuse_af_strategy(struct au_init *init) static void hdbg_init(void); #endif +static void adjust_fix_alloc_sizes(UWord extra_block_size) +{ + + if (extra_block_size && erts_allctrs_info[ERTS_ALC_A_FIXED_SIZE].enabled) { + int j; + +#ifdef ERTS_SMP + if (erts_allctrs_info[ERTS_ALC_A_FIXED_SIZE].thr_spec) { + int i; + ErtsAllocatorThrSpec_t* tspec; + + tspec = &erts_allctr_thr_spec[ERTS_ALC_A_FIXED_SIZE]; + ASSERT(tspec->enabled); + + for (i=0; i < tspec->size; i++) { + Allctr_t* allctr = tspec->allctr[i]; + for (j=0; j < ERTS_ALC_NO_FIXED_SIZES; ++j) { + allctr->fix[j].type_size += extra_block_size; + } + } + } + else +#endif + { + Allctr_t* allctr = erts_allctrs_info[ERTS_ALC_A_FIXED_SIZE].extra; + for (j=0; j < ERTS_ALC_NO_FIXED_SIZES; ++j) { + allctr->fix[j].type_size += extra_block_size; + } + } + } +} + void erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) { @@ -515,9 +547,9 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) = sizeof(Process); #if !HALFWORD_HEAP fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_MONITOR_SH)] - = ERTS_MONITOR_SH_SIZE; + = ERTS_MONITOR_SH_SIZE * sizeof(Uint); fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_NLINK_SH)] - = ERTS_LINK_SH_SIZE; + = ERTS_LINK_SH_SIZE * sizeof(Uint); #endif fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_DRV_EV_D_STATE)] = sizeof(ErtsDrvEventDataState); @@ -538,6 +570,8 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) if (ncpu < 1) ncpu = 1; + erts_tsd_key_create(&erts_allctr_prelock_tsd_key); + erts_sys_alloc_init(); erts_init_utils_mem(); @@ -768,7 +802,7 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) #ifdef DEBUG extra_block_size += install_debug_functions(); #endif - + adjust_fix_alloc_sizes(extra_block_size); } void @@ -3107,6 +3141,55 @@ erts_request_alloc_info(struct process *c_p, return 1; } +/* + * The allocator wrapper prelocking stuff below is about the locking order. + * It only affects wrappers (erl_mtrace.c and erl_instrument.c) that keep locks + * during alloc/realloc/free. + * + * Some query functions in erl_alloc_util.c lock the allocator mutex and then + * use erts_printf that in turn may call the sys allocator through the wrappers. + * To avoid breaking locking order these query functions first "pre-locks" all + * allocator wrappers. + */ +ErtsAllocatorWrapper_t *erts_allctr_wrappers; +int erts_allctr_wrapper_prelocked = 0; +erts_tsd_key_t erts_allctr_prelock_tsd_key; + +void erts_allctr_wrapper_prelock_init(ErtsAllocatorWrapper_t* wrapper) +{ + ASSERT(wrapper->lock && wrapper->unlock); + wrapper->next = erts_allctr_wrappers; + erts_allctr_wrappers = wrapper; +} + +void erts_allctr_wrapper_pre_lock(void) +{ + if (erts_allctr_wrappers) { + ErtsAllocatorWrapper_t* wrapper = erts_allctr_wrappers; + for ( ; wrapper; wrapper = wrapper->next) { + wrapper->lock(); + } + ASSERT(!erts_allctr_wrapper_prelocked); + erts_allctr_wrapper_prelocked = 1; + erts_tsd_set(erts_allctr_prelock_tsd_key, (void*)1); + } +} + +void erts_allctr_wrapper_pre_unlock(void) +{ + if (erts_allctr_wrappers) { + ErtsAllocatorWrapper_t* wrapper = erts_allctr_wrappers; + + erts_allctr_wrapper_prelocked = 0; + erts_tsd_set(erts_allctr_prelock_tsd_key, (void*)0); + for ( ; wrapper; wrapper = wrapper->next) { + wrapper->unlock(); + } + } +} + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * Deprecated functions * * * diff --git a/erts/emulator/beam/erl_alloc.h b/erts/emulator/beam/erl_alloc.h index ba5ec9c367..9cafd8ddc8 100644 --- a/erts/emulator/beam/erl_alloc.h +++ b/erts/emulator/beam/erl_alloc.h @@ -135,6 +135,18 @@ typedef struct { extern ErtsAllocatorThrSpec_t erts_allctr_thr_spec[ERTS_ALC_A_MAX+1]; +typedef struct ErtsAllocatorWrapper_t_ { + void (*lock)(void); + void (*unlock)(void); + struct ErtsAllocatorWrapper_t_* next; +}ErtsAllocatorWrapper_t; +ErtsAllocatorWrapper_t *erts_allctr_wrappers; +extern int erts_allctr_wrapper_prelocked; +extern erts_tsd_key_t erts_allctr_prelock_tsd_key; +void erts_allctr_wrapper_prelock_init(ErtsAllocatorWrapper_t* wrapper); +void erts_allctr_wrapper_pre_lock(void); +void erts_allctr_wrapper_pre_unlock(void); + void erts_alloc_register_scheduler(void *vesdp); #ifdef ERTS_SMP void erts_alloc_scheduler_handle_delayed_dealloc(void *vesdp, @@ -188,6 +200,7 @@ void *erts_realloc(ErtsAlcType_t type, void *ptr, Uint size); void erts_free(ErtsAlcType_t type, void *ptr); void *erts_alloc_fnf(ErtsAlcType_t type, Uint size); void *erts_realloc_fnf(ErtsAlcType_t type, void *ptr, Uint size); +int erts_is_allctr_wrapper_prelocked(void); #endif /* #if !ERTS_ALC_DO_INLINE */ @@ -258,6 +271,13 @@ void *erts_realloc_fnf(ErtsAlcType_t type, void *ptr, Uint size) size); } +ERTS_ALC_INLINE +int erts_is_allctr_wrapper_prelocked(void) +{ + return erts_allctr_wrapper_prelocked /* locked */ + && !!erts_tsd_get(erts_allctr_prelock_tsd_key); /* by me */ +} + #endif /* #if ERTS_ALC_DO_INLINE || defined(ERTS_ALC_INTERNAL__) */ #define ERTS_ALC_GET_THR_IX() ((int) erts_get_scheduler_id()) diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index 5e3615ccc2..5a92ab7f24 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -272,6 +272,7 @@ type CODE_IX_LOCK_Q SHORT_LIVED SYSTEM code_ix_lock_q type PROC_INTERVAL LONG_LIVED SYSTEM process_interval type BUSY_CALLER_TAB SHORT_LIVED SYSTEM busy_caller_table type BUSY_CALLER SHORT_LIVED SYSTEM busy_caller +type PORT_DATA_HEAP STANDARD SYSTEM port_data_heap +if threads_no_smp # Need thread safe allocs, but std_alloc and fix_alloc are not; diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index ac7f420708..cbeb2c1e3a 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -588,17 +588,21 @@ do { \ #ifdef DEBUG #ifdef USE_THREADS +# ifdef ERTS_SMP +# define IS_ACTUALLY_BLOCKING (erts_thr_progress_is_blocking()) +# else +# define IS_ACTUALLY_BLOCKING 0 +# endif #define ERTS_ALCU_DBG_CHK_THR_ACCESS(A) \ do { \ - if (!(A)->thread_safe) { \ - if (!(A)->debug.saved_tid) { \ + if (!(A)->thread_safe && !IS_ACTUALLY_BLOCKING) { \ + if (!(A)->debug.saved_tid) { \ (A)->debug.tid = erts_thr_self(); \ (A)->debug.saved_tid = 1; \ } \ else { \ ERTS_SMP_LC_ASSERT( \ - ethr_equal_tids((A)->debug.tid, erts_thr_self()) \ - || erts_thr_progress_is_blocking()); \ + ethr_equal_tids((A)->debug.tid, erts_thr_self())); \ } \ } \ } while (0) @@ -878,8 +882,10 @@ erts_alcu_fix_alloc_shrink(Allctr_t *allctr, erts_aint32_t flgs) #ifdef ERTS_SMP -#define ERTS_ALCU_DD_FIX_TYPE_OFFS \ - ((sizeof(ErtsAllctrDDBlock_t)-1)/sizeof(UWord) + 1) +typedef struct { + ErtsAllctrDDBlock_t ddblock__; /* must be first */ + ErtsAlcType_t fix_type; +}ErtsAllctrFixDDBlock_t; static ERTS_INLINE Allctr_t* @@ -1180,7 +1186,7 @@ handle_delayed_dealloc(Allctr_t *allctr, if (fix) { ErtsAlcType_t type; - type = (ErtsAlcType_t) ((UWord *) ptr)[ERTS_ALCU_DD_FIX_TYPE_OFFS]; + type = ((ErtsAllctrFixDDBlock_t*) ptr)->fix_type; ix = type - ERTS_ALC_N_MIN_A_FIXED_SIZE; ERTS_DBG_CHK_FIX_LIST(allctr, fix, ix, 1); fix[ix].used--; @@ -1242,7 +1248,7 @@ enqueue_dealloc_other_instance(ErtsAlcType_t type, int cinit) { if (allctr->fix) - ((UWord *) ptr)[ERTS_ALCU_DD_FIX_TYPE_OFFS] = (UWord) type; + ((ErtsAllctrFixDDBlock_t*) ptr)->fix_type = type; if (ddq_enqueue(type, &allctr->dd.q, ptr, cinit)) erts_alloc_notify_delayed_dealloc(allctr->ix); @@ -2508,12 +2514,6 @@ static erts_mtx_t init_atoms_mtx; static void init_atoms(Allctr_t *allctr) { - -#ifdef USE_THREADS - if (allctr && allctr->thread_safe) - erts_mtx_unlock(&allctr->mutex); -#endif - erts_mtx_lock(&init_atoms_mtx); if (!atoms_initialized) { @@ -2604,18 +2604,13 @@ init_atoms(Allctr_t *allctr) fix_type_atoms[ix] = am_atom_put(name, len); } } - - if (allctr) { + if (allctr && !allctr->atoms_initialized) { make_name_atoms(allctr); (*allctr->init_atoms)(); -#ifdef USE_THREADS - if (allctr->thread_safe) - erts_mtx_lock(&allctr->mutex); -#endif allctr->atoms_initialized = 1; } @@ -3243,17 +3238,21 @@ erts_alcu_info_options(Allctr_t *allctr, { Eterm res; + if (hpp || szp) + ensure_atoms_initialized(allctr); #ifdef USE_THREADS - if (allctr->thread_safe) + if (allctr->thread_safe) { + erts_allctr_wrapper_pre_lock(); erts_mtx_lock(&allctr->mutex); + } #endif - if (hpp || szp) - ensure_atoms_initialized(allctr); res = info_options(allctr, print_to_p, print_to_arg, hpp, szp); #ifdef USE_THREADS - if (allctr->thread_safe) + if (allctr->thread_safe) { erts_mtx_unlock(&allctr->mutex); + erts_allctr_wrapper_pre_unlock(); + } #endif return res; } @@ -3280,16 +3279,18 @@ erts_alcu_sz_info(Allctr_t *allctr, return am_false; } + if (hpp || szp) + ensure_atoms_initialized(allctr); + #ifdef USE_THREADS - if (allctr->thread_safe) + if (allctr->thread_safe) { + erts_allctr_wrapper_pre_lock(); erts_mtx_lock(&allctr->mutex); + } #endif ERTS_ALCU_DBG_CHK_THR_ACCESS(allctr); - if (hpp || szp) - ensure_atoms_initialized(allctr); - /* Update sbc values not continously updated */ allctr->sbcs.blocks.curr.no = allctr->sbcs.curr.norm.mseg.no + allctr->sbcs.curr.norm.sys_alloc.no; @@ -3325,13 +3326,16 @@ erts_alcu_sz_info(Allctr_t *allctr, #ifdef USE_THREADS - if (allctr->thread_safe) + if (allctr->thread_safe) { erts_mtx_unlock(&allctr->mutex); + erts_allctr_wrapper_pre_unlock(); + } #endif return res; } + Eterm erts_alcu_info(Allctr_t *allctr, int begin_max_period, @@ -3352,16 +3356,18 @@ erts_alcu_info(Allctr_t *allctr, return am_false; } + if (hpp || szp) + ensure_atoms_initialized(allctr); + #ifdef USE_THREADS - if (allctr->thread_safe) + if (allctr->thread_safe) { + erts_allctr_wrapper_pre_lock(); erts_mtx_lock(&allctr->mutex); + } #endif ERTS_ALCU_DBG_CHK_THR_ACCESS(allctr); - if (hpp || szp) - ensure_atoms_initialized(allctr); - /* Update sbc values not continously updated */ allctr->sbcs.blocks.curr.no = allctr->sbcs.curr.norm.mseg.no + allctr->sbcs.curr.norm.sys_alloc.no; @@ -3414,8 +3420,10 @@ erts_alcu_info(Allctr_t *allctr, #ifdef USE_THREADS - if (allctr->thread_safe) + if (allctr->thread_safe) { erts_mtx_unlock(&allctr->mutex); + erts_allctr_wrapper_pre_unlock(); + } #endif return res; @@ -3487,7 +3495,9 @@ do_erts_alcu_alloc(ErtsAlcType_t type, void *extra, Uint size) fix = allctr->fix; if (fix) { int ix = type - ERTS_ALC_N_MIN_A_FIXED_SIZE; + ASSERT((unsigned)ix < ERTS_ALC_NO_FIXED_SIZES); ERTS_DBG_CHK_FIX_LIST(allctr, fix, ix, 1); + ASSERT(size <= fix[ix].type_size); fix[ix].used++; res = fix[ix].list; if (res) { @@ -3508,8 +3518,7 @@ do_erts_alcu_alloc(ErtsAlcType_t type, void *extra, Uint size) ERTS_DBG_CHK_FIX_LIST(allctr, fix, ix, 0); return res; } - if (size < 2*sizeof(UWord)) - size += sizeof(UWord); + size = fix[ix].type_size; if (fix[ix].limit < fix[ix].used) fix[ix].limit = fix[ix].used; if (fix[ix].max_used < fix[ix].used) @@ -4198,9 +4207,8 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init) #if ERTS_SMP if (init->tpref) { Uint sz = ABLK_HDR_SZ; - sz += ERTS_ALCU_DD_FIX_TYPE_OFFS*sizeof(UWord); - if (init->fix) - sz += sizeof(UWord); + sz += (init->fix ? + sizeof(ErtsAllctrFixDDBlock_t) : sizeof(ErtsAllctrDDBlock_t)); sz = UNIT_CEILING(sz); if (sz > allctr->min_block_size) allctr->min_block_size = sz; @@ -4325,6 +4333,9 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init) allctr->fix[i].list = NULL; allctr->fix[i].allocated = 0; allctr->fix[i].used = 0; +#ifdef ERTS_SMP + ASSERT(allctr->fix[i].type_size >= sizeof(ErtsAllctrFixDDBlock_t)); +#endif } } diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 379d3eecc6..54eefe8d12 100755 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -114,6 +114,9 @@ static char erts_system_version[] = ("Erlang " ERLANG_OTP_RELEASE #ifdef VALGRIND " [valgrind-compiled]" #endif +#ifdef ERTS_FRMPTR + " [frame-pointer]" +#endif #ifdef USE_DTRACE " [dtrace]" #endif @@ -486,27 +489,6 @@ collect_one_suspend_monitor(ErtsSuspendMonitor *smon, void *vsmicp) } } - -static void one_link_size(ErtsLink *lnk, void *vpu) -{ - Uint *pu = vpu; - *pu += ERTS_LINK_SIZE*sizeof(Uint); - if(!IS_CONST(lnk->pid)) - *pu += NC_HEAP_SIZE(lnk->pid)*sizeof(Uint); - if (lnk->type != LINK_NODE && ERTS_LINK_ROOT(lnk) != NULL) { - erts_doforall_links(ERTS_LINK_ROOT(lnk),&one_link_size,vpu); - } -} -static void one_mon_size(ErtsMonitor *mon, void *vpu) -{ - Uint *pu = vpu; - *pu += ERTS_MONITOR_SIZE*sizeof(Uint); - if(!IS_CONST(mon->pid)) - *pu += NC_HEAP_SIZE(mon->pid)*sizeof(Uint); - if(!IS_CONST(mon->ref)) - *pu += NC_HEAP_SIZE(mon->ref)*sizeof(Uint); -} - /* * process_info/[1,2] */ @@ -1420,41 +1402,8 @@ process_info_aux(Process *BIF_P, } case am_memory: { /* Memory consumed in bytes */ - ErlMessage *mp; - Uint size = 0; Uint hsz = 3; - struct saved_calls *scb; - size += sizeof(Process); - - ERTS_SMP_MSGQ_MV_INQ2PRIVQ(rp); - - erts_doforall_links(ERTS_P_LINKS(rp), &one_link_size, &size); - erts_doforall_monitors(ERTS_P_MONITORS(rp), &one_mon_size, &size); - size += (rp->heap_sz + rp->mbuf_sz) * sizeof(Eterm); - if (rp->old_hend && rp->old_heap) - size += (rp->old_hend - rp->old_heap) * sizeof(Eterm); - - size += rp->msg.len * sizeof(ErlMessage); - - for (mp = rp->msg.first; mp; mp = mp->next) - if (mp->data.attached) - size += erts_msg_attached_data_size(mp)*sizeof(Eterm); - - if (rp->arg_reg != rp->def_arg_reg) { - size += rp->arity * sizeof(rp->arg_reg[0]); - } - - if (rp->psd) - size += sizeof(ErtsPSD); - - scb = ERTS_PROC_GET_SAVED_CALLS_BUF(rp); - if (scb) { - size += (sizeof(struct saved_calls) - + (scb->len-1) * sizeof(scb->ct[0])); - } - - size += erts_dicts_mem_size(rp); - + Uint size = erts_process_memory(rp); (void) erts_bld_uint(NULL, &hsz, size); hp = HAlloc(BIF_P, hsz); res = erts_bld_uint(&hp, NULL, size); @@ -2096,6 +2045,9 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) #elif defined(ERTS_ENABLE_LOCK_COUNT) ERTS_DECL_AM(lcnt); BIF_RET(AM_lcnt); +#elif defined(ERTS_FRMPTR) + ERTS_DECL_AM(frmptr); + BIF_RET(AM_frmptr); #else BIF_RET(am_opt); #endif @@ -2861,12 +2813,10 @@ erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt, Eterm ite included though). */ Uint size = 0; - ErlHeapFragment* bp; - erts_doforall_links(ERTS_P_LINKS(prt), &one_link_size, &size); + erts_doforall_links(ERTS_P_LINKS(prt), &erts_one_link_size, &size); - for (bp = prt->bp; bp; bp = bp->next) - size += sizeof(ErlHeapFragment) + (bp->alloc_size - 1)*sizeof(Eterm); + size += erts_port_data_size(prt); if (prt->linebuf) size += sizeof(LineBuf) + prt->linebuf->ovsiz; diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index 44fa41c7b6..109c54fd7f 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -423,57 +423,164 @@ BIF_RETTYPE erts_internal_port_info_2(BIF_ALIST_2) } } +/* + * The erlang:port_set_data()/erlang:port_get_data() operations should + * be viewed as operations on a table (inet_db) with data values + * associated with port identifier keys. That is, these operations are + * *not* signals to/from ports. + */ + +#if (TAG_PRIMARY_IMMED1 & 0x3) == 0 +# error "erlang:port_set_data()/erlang:port_get_data() needs to be rewritten!" +#endif + +typedef struct { + ErtsThrPrgrLaterOp later_op; + Uint hsize; + Eterm data; + ErlOffHeap off_heap; + Eterm heap[1]; +} ErtsPortDataHeap; -BIF_RETTYPE erts_internal_port_set_data_2(BIF_ALIST_2) +static void +free_port_data_heap(void *vpdhp) { - Eterm ref; + erts_cleanup_offheap(&((ErtsPortDataHeap *) vpdhp)->off_heap); + erts_free(ERTS_ALC_T_PORT_DATA_HEAP, vpdhp); +} + +static ERTS_INLINE void +cleanup_old_port_data(erts_aint_t data) +{ + if ((data & 0x3) != 0) { + ASSERT(is_immed((Eterm) data)); + } + else { +#ifdef ERTS_SMP + ErtsPortDataHeap *pdhp = (ErtsPortDataHeap *) data; + size_t size; + ERTS_SMP_DATA_DEPENDENCY_READ_MEMORY_BARRIER; + size = sizeof(ErtsPortDataHeap) + pdhp->hsize*(sizeof(Eterm) - 1); + erts_schedule_thr_prgr_later_cleanup_op(free_port_data_heap, + (void *) pdhp, + &pdhp->later_op, + size); +#else + free_port_data_heap((void *) data); +#endif + } +} + +void +erts_init_port_data(Port *prt) +{ + erts_smp_atomic_init_nob(&prt->data, (erts_aint_t) am_undefined); +} + +void +erts_cleanup_port_data(Port *prt) +{ + ASSERT(erts_atomic32_read_nob(&prt->state) & ERTS_PORT_SFLGS_INVALID_LOOKUP); + cleanup_old_port_data(erts_smp_atomic_read_nob(&prt->data)); + erts_smp_atomic_set_nob(&prt->data, (erts_aint_t) THE_NON_VALUE); +} + +Uint +erts_port_data_size(Port *prt) +{ + erts_aint_t data = erts_smp_atomic_read_ddrb(&prt->data); + + if ((data & 0x3) != 0) { + ASSERT(is_immed((Eterm) (UWord) data)); + return (Uint) 0; + } + else { + ErtsPortDataHeap *pdhp = (ErtsPortDataHeap *) data; + return (Uint) sizeof(ErtsPortDataHeap) + pdhp->hsize*(sizeof(Eterm)-1); + } +} + +ErlOffHeap * +erts_port_data_offheap(Port *prt) +{ + erts_aint_t data = erts_smp_atomic_read_ddrb(&prt->data); + + if ((data & 0x3) != 0) { + ASSERT(is_immed((Eterm) (UWord) data)); + return NULL; + } + else { + ErtsPortDataHeap *pdhp = (ErtsPortDataHeap *) data; + return &pdhp->off_heap; + } +} + +BIF_RETTYPE port_set_data_2(BIF_ALIST_2) +{ + /* + * This is not a signal. See comment above. + */ + erts_aint_t data; Port* prt; prt = lookup_port(BIF_P, BIF_ARG_1); if (!prt) - BIF_RET(am_badarg); + BIF_ERROR(BIF_P, BADARG); - switch (erts_port_set_data(BIF_P, prt, BIF_ARG_2, &ref)) { - case ERTS_PORT_OP_CALLER_EXIT: - case ERTS_PORT_OP_BADARG: - case ERTS_PORT_OP_DROPPED: - BIF_RET(am_badarg); - case ERTS_PORT_OP_SCHEDULED: - ASSERT(is_internal_ref(ref)); - BIF_RET(ref); - case ERTS_PORT_OP_DONE: - BIF_RET(am_true); - default: - ERTS_INTERNAL_ERROR("Unexpected erts_port_set_data() result"); - BIF_RET(am_internal_error); + if (is_immed(BIF_ARG_2)) { + data = (erts_aint_t) BIF_ARG_2; + ASSERT((data & 0x3) != 0); } + else { + ErtsPortDataHeap *pdhp; + Uint hsize; + Eterm *hp; + + hsize = size_object(BIF_ARG_2); + pdhp = erts_alloc(ERTS_ALC_T_PORT_DATA_HEAP, + sizeof(ErtsPortDataHeap) + hsize*(sizeof(Eterm)-1)); + hp = &pdhp->heap[0]; + pdhp->off_heap.first = NULL; + pdhp->off_heap.overhead = 0; + pdhp->data = copy_struct(BIF_ARG_2, hsize, &hp, &pdhp->off_heap); + data = (erts_aint_t) pdhp; + ASSERT((data & 0x3) == 0); + } + + data = erts_smp_atomic_xchg_wb(&prt->data, data); + + cleanup_old_port_data(data); + + BIF_RET(am_true); } -BIF_RETTYPE erts_internal_port_get_data_1(BIF_ALIST_1) +BIF_RETTYPE port_get_data_1(BIF_ALIST_1) { - Eterm retval; + /* + * This is not a signal. See comment above. + */ + Eterm res; + erts_aint_t data; Port* prt; prt = lookup_port(BIF_P, BIF_ARG_1); if (!prt) - BIF_RET(am_badarg); + BIF_ERROR(BIF_P, BADARG); - switch (erts_port_get_data(BIF_P, prt, &retval)) { - case ERTS_PORT_OP_CALLER_EXIT: - case ERTS_PORT_OP_BADARG: - case ERTS_PORT_OP_DROPPED: - BIF_RET(am_badarg); - case ERTS_PORT_OP_SCHEDULED: - ASSERT(is_internal_ref(retval)); - BIF_RET(retval); - case ERTS_PORT_OP_DONE: - ASSERT(is_not_internal_ref(retval)); - BIF_RET(retval); - default: - ERTS_INTERNAL_ERROR("Unexpected erts_port_get_data() result"); - BIF_RET(am_internal_error); + data = erts_smp_atomic_read_ddrb(&prt->data); + + if ((data & 0x3) != 0) { + res = (Eterm) (UWord) data; + ASSERT(is_immed(res)); + } + else { + ErtsPortDataHeap *pdhp = (ErtsPortDataHeap *) data; + Eterm *hp = HAlloc(BIF_P, pdhp->hsize); + res = copy_struct(pdhp->data, pdhp->hsize, &hp, &MSO(BIF_P)); } + + BIF_RET(res); } /* diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index 41c98bbffb..559cb3efa1 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -1224,9 +1224,7 @@ trace_info_func(Process* p, Eterm func_spec, Eterm key) match_spec_meta = NIL; } if (r & FUNC_TRACE_COUNT_TRACE) { - c = count < 0 ? - erts_make_integer(-count-1, p) : - erts_make_integer(count, p); + c = erts_make_integer(count, p); } if (r & FUNC_TRACE_TIME_TRACE) { ct = call_time; diff --git a/erts/emulator/beam/erl_instrument.c b/erts/emulator/beam/erl_instrument.c index b5b245288b..1aea3f65bc 100644 --- a/erts/emulator/beam/erl_instrument.c +++ b/erts/emulator/beam/erl_instrument.c @@ -271,6 +271,18 @@ stat_upd_realloc(ErtsAlcType_t n, Uint size, Uint old_size) * stat instrumentation callback functions */ +static void stat_pre_lock(void) +{ + erts_mtx_lock(&instr_mutex); +} + +static void stat_pre_unlock(void) +{ + erts_mtx_unlock(&instr_mutex); +} + +static ErtsAllocatorWrapper_t instr_wrapper; + static void * stat_alloc(ErtsAlcType_t n, void *extra, Uint size) { @@ -278,7 +290,9 @@ stat_alloc(ErtsAlcType_t n, void *extra, Uint size) Uint ssize; void *res; - erts_mtx_lock(&instr_mutex); + if (!erts_is_allctr_wrapper_prelocked()) { + erts_mtx_lock(&instr_mutex); + } ssize = size + STAT_BLOCK_HEADER_SIZE; res = (*real_af->alloc)(n, real_af->extra, ssize); @@ -293,7 +307,9 @@ stat_alloc(ErtsAlcType_t n, void *extra, Uint size) res = (void *) ((StatBlock_t *) res)->mem; } - erts_mtx_unlock(&instr_mutex); + if (!erts_is_allctr_wrapper_prelocked()) { + erts_mtx_unlock(&instr_mutex); + } return res; } @@ -307,7 +323,9 @@ stat_realloc(ErtsAlcType_t n, void *extra, void *ptr, Uint size) void *sptr; void *res; - erts_mtx_lock(&instr_mutex); + if (!erts_is_allctr_wrapper_prelocked()) { + erts_mtx_lock(&instr_mutex); + } if (ptr) { sptr = (void *) (((char *) ptr) - STAT_BLOCK_HEADER_SIZE); @@ -329,7 +347,9 @@ stat_realloc(ErtsAlcType_t n, void *extra, void *ptr, Uint size) res = (void *) ((StatBlock_t *) res)->mem; } - erts_mtx_unlock(&instr_mutex); + if (!erts_is_allctr_wrapper_prelocked()) { + erts_mtx_unlock(&instr_mutex); + } return res; } @@ -340,7 +360,9 @@ stat_free(ErtsAlcType_t n, void *extra, void *ptr) ErtsAllocatorFunctions_t *real_af = (ErtsAllocatorFunctions_t *) extra; void *sptr; - erts_mtx_lock(&instr_mutex); + if (!erts_is_allctr_wrapper_prelocked()) { + erts_mtx_lock(&instr_mutex); + } if (ptr) { sptr = (void *) (((char *) ptr) - STAT_BLOCK_HEADER_SIZE); @@ -352,7 +374,9 @@ stat_free(ErtsAlcType_t n, void *extra, void *ptr) (*real_af->free)(n, real_af->extra, sptr); - erts_mtx_unlock(&instr_mutex); + if (!erts_is_allctr_wrapper_prelocked()) { + erts_mtx_unlock(&instr_mutex); + } } @@ -360,6 +384,18 @@ stat_free(ErtsAlcType_t n, void *extra, void *ptr) * map stat instrumentation callback functions */ +static void map_stat_pre_lock(void) +{ + erts_mtx_lock(&instr_x_mutex); + erts_mtx_lock(&instr_mutex); +} + +static void map_stat_pre_unlock(void) +{ + erts_mtx_unlock(&instr_mutex); + erts_mtx_unlock(&instr_x_mutex); +} + static void * map_stat_alloc(ErtsAlcType_t n, void *extra, Uint size) { @@ -367,7 +403,9 @@ map_stat_alloc(ErtsAlcType_t n, void *extra, Uint size) Uint msize; void *res; - erts_mtx_lock(&instr_mutex); + if (!erts_is_allctr_wrapper_prelocked()) { + erts_mtx_lock(&instr_mutex); + } msize = size + MAP_STAT_BLOCK_HEADER_SIZE; res = (*real_af->alloc)(n, real_af->extra, msize); @@ -388,7 +426,9 @@ map_stat_alloc(ErtsAlcType_t n, void *extra, Uint size) res = (void *) mb->mem; } - erts_mtx_unlock(&instr_mutex); + if (!erts_is_allctr_wrapper_prelocked()) { + erts_mtx_unlock(&instr_mutex); + } return res; } @@ -402,8 +442,10 @@ map_stat_realloc(ErtsAlcType_t n, void *extra, void *ptr, Uint size) void *mptr; void *res; - erts_mtx_lock(&instr_x_mutex); - erts_mtx_lock(&instr_mutex); + if (!erts_is_allctr_wrapper_prelocked()) { + erts_mtx_lock(&instr_x_mutex); + erts_mtx_lock(&instr_mutex); + } if (ptr) { mptr = (void *) (((char *) ptr) - MAP_STAT_BLOCK_HEADER_SIZE); @@ -449,9 +491,10 @@ map_stat_realloc(ErtsAlcType_t n, void *extra, void *ptr, Uint size) res = (void *) mb->mem; } - - erts_mtx_unlock(&instr_mutex); - erts_mtx_unlock(&instr_x_mutex); + if (!erts_is_allctr_wrapper_prelocked()) { + erts_mtx_unlock(&instr_mutex); + erts_mtx_unlock(&instr_x_mutex); + } return res; } @@ -462,8 +505,10 @@ map_stat_free(ErtsAlcType_t n, void *extra, void *ptr) ErtsAllocatorFunctions_t *real_af = (ErtsAllocatorFunctions_t *) extra; void *mptr; - erts_mtx_lock(&instr_x_mutex); - erts_mtx_lock(&instr_mutex); + if (!erts_is_allctr_wrapper_prelocked()) { + erts_mtx_lock(&instr_x_mutex); + erts_mtx_lock(&instr_mutex); + } if (ptr) { MapStatBlock_t *mb; @@ -486,8 +531,10 @@ map_stat_free(ErtsAlcType_t n, void *extra, void *ptr) (*real_af->free)(n, real_af->extra, mptr); - erts_mtx_unlock(&instr_mutex); - erts_mtx_unlock(&instr_x_mutex); + if (!erts_is_allctr_wrapper_prelocked()) { + erts_mtx_unlock(&instr_mutex); + erts_mtx_unlock(&instr_x_mutex); + } } @@ -496,8 +543,10 @@ static void dump_memory_map_to_stream(FILE *fp) ErtsAlcType_t n; MapStatBlock_t *bp; int lock = !ERTS_IS_CRASH_DUMPING; - if (lock) + if (lock) { + ASSERT(!erts_is_allctr_wrapper_prelocked()); erts_mtx_lock(&instr_mutex); + } /* Write header */ @@ -1155,6 +1204,7 @@ erts_instr_get_type_info(Process *proc) Uint erts_instr_init(int stat, int map_stat) { + Uint extra_sz; int i; am_tot = NULL; @@ -1208,7 +1258,9 @@ erts_instr_init(int stat, int map_stat) erts_allctrs[i].free = map_stat_free; erts_allctrs[i].extra = (void *) &real_allctrs[i]; } - return MAP_STAT_BLOCK_HEADER_SIZE; + instr_wrapper.lock = map_stat_pre_lock; + instr_wrapper.unlock = map_stat_pre_unlock; + extra_sz = MAP_STAT_BLOCK_HEADER_SIZE; } else { erts_instr_stat = 1; @@ -1220,8 +1272,11 @@ erts_instr_init(int stat, int map_stat) erts_allctrs[i].free = stat_free; erts_allctrs[i].extra = (void *) &real_allctrs[i]; } - return STAT_BLOCK_HEADER_SIZE; + instr_wrapper.lock = stat_pre_lock; + instr_wrapper.unlock = stat_pre_unlock; + extra_sz = STAT_BLOCK_HEADER_SIZE; } - + erts_allctr_wrapper_prelock_init(&instr_wrapper); + return extra_sz; } diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index 69bb4be717..a1d270adba 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -143,6 +143,8 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "ptimer_pre_alloc_lock", "address", }, { "btm_pre_alloc_lock", NULL, }, { "dist_entry_out_queue", "address" }, + { "port_sched_lock", "port_id" }, + { "port_table", NULL }, #endif { "mtrace_op", NULL }, { "instr_x", NULL }, @@ -154,10 +156,8 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "pmmap", NULL }, #endif #ifdef ERTS_SMP - { "port_sched_lock", "port_id" }, { "port_task_pre_alloc_lock", "address" }, { "proclist_pre_alloc_lock", "address" }, - { "port_table", NULL }, { "xports_list_pre_alloc_lock", "address" }, { "inet_buffer_stack_lock", NULL }, { "gc_info", NULL }, diff --git a/erts/emulator/beam/erl_monitors.c b/erts/emulator/beam/erl_monitors.c index 70e592cc5f..244a2b26db 100644 --- a/erts/emulator/beam/erl_monitors.c +++ b/erts/emulator/beam/erl_monitors.c @@ -1024,3 +1024,23 @@ Eterm erts_debug_dump_links_1(BIF_ALIST_1) } } } + +void erts_one_link_size(ErtsLink *lnk, void *vpu) +{ + Uint *pu = vpu; + *pu += ERTS_LINK_SIZE*sizeof(Uint); + if(!IS_CONST(lnk->pid)) + *pu += NC_HEAP_SIZE(lnk->pid)*sizeof(Uint); + if (lnk->type != LINK_NODE && ERTS_LINK_ROOT(lnk) != NULL) { + erts_doforall_links(ERTS_LINK_ROOT(lnk),&erts_one_link_size,vpu); + } +} +void erts_one_mon_size(ErtsMonitor *mon, void *vpu) +{ + Uint *pu = vpu; + *pu += ERTS_MONITOR_SIZE*sizeof(Uint); + if(!IS_CONST(mon->pid)) + *pu += NC_HEAP_SIZE(mon->pid)*sizeof(Uint); + if(!IS_CONST(mon->ref)) + *pu += NC_HEAP_SIZE(mon->ref)*sizeof(Uint); +} diff --git a/erts/emulator/beam/erl_monitors.h b/erts/emulator/beam/erl_monitors.h index 6a360a2336..fb11dbbd22 100644 --- a/erts/emulator/beam/erl_monitors.h +++ b/erts/emulator/beam/erl_monitors.h @@ -170,6 +170,8 @@ ErtsSuspendMonitor *erts_lookup_suspend_monitor(ErtsSuspendMonitor *root, Eterm pid); void erts_delete_suspend_monitor(ErtsSuspendMonitor **root, Eterm pid); void erts_init_monitors(void); +void erts_one_link_size(ErtsLink *lnk, void *vpu); +void erts_one_mon_size(ErtsMonitor *mon, void *vpu); #define erts_doforall_monitors erts_sweep_monitors #define erts_doforall_links erts_sweep_links diff --git a/erts/emulator/beam/erl_mtrace.c b/erts/emulator/beam/erl_mtrace.c index e538ba30c2..c8bb126687 100644 --- a/erts/emulator/beam/erl_mtrace.c +++ b/erts/emulator/beam/erl_mtrace.c @@ -222,6 +222,8 @@ static byte *tracep; static byte *endp; static SysTimeval last_tv; +static ErtsAllocatorWrapper_t mtrace_wrapper; + #if ERTS_MTRACE_SEGMENT_ID >= ERTS_ALC_A_MIN || ERTS_MTRACE_SEGMENT_ID < 0 #error ERTS_MTRACE_SEGMENT_ID >= ERTS_ALC_A_MIN || ERTS_MTRACE_SEGMENT_ID < 0 #endif @@ -555,6 +557,8 @@ write_trace_header(char *nodename, char *pid, char *hostname) return 1; } +static void mtrace_pre_lock(void); +static void mtrace_pre_unlock(void); static void *mtrace_alloc(ErtsAlcType_t, void *, Uint); static void *mtrace_realloc(ErtsAlcType_t, void *, void *, Uint); static void mtrace_free(ErtsAlcType_t, void *, void *); @@ -635,12 +639,16 @@ erts_mtrace_install_wrapper_functions(void) erts_allctrs[i].free = mtrace_free; erts_allctrs[i].extra = (void *) &real_allctrs[i]; } + mtrace_wrapper.lock = mtrace_pre_lock; + mtrace_wrapper.unlock = mtrace_pre_unlock; + erts_allctr_wrapper_prelock_init(&mtrace_wrapper); } } void erts_mtrace_stop(void) { + ASSERT(!erts_is_allctr_wrapper_prelocked()); erts_mtx_lock(&mtrace_op_mutex); erts_mtx_lock(&mtrace_buf_mutex); if (erts_mtrace_enabled) { @@ -677,6 +685,7 @@ erts_mtrace_stop(void) void erts_mtrace_exit(Uint32 exit_value) { + ASSERT(!erts_is_allctr_wrapper_prelocked()); erts_mtx_lock(&mtrace_op_mutex); erts_mtx_lock(&mtrace_buf_mutex); if (erts_mtrace_enabled) { @@ -935,18 +944,33 @@ write_free_entry(byte tag, erts_mtx_unlock(&mtrace_buf_mutex); } +static void mtrace_pre_lock(void) +{ + erts_mtx_lock(&mtrace_op_mutex); +} + +static void mtrace_pre_unlock(void) +{ + erts_mtx_unlock(&mtrace_op_mutex); +} + + static void * mtrace_alloc(ErtsAlcType_t n, void *extra, Uint size) { ErtsAllocatorFunctions_t *real_af = (ErtsAllocatorFunctions_t *) extra; void *res; - erts_mtx_lock(&mtrace_op_mutex); + if (!erts_is_allctr_wrapper_prelocked()) { + erts_mtx_lock(&mtrace_op_mutex); + } res = (*real_af->alloc)(n, real_af->extra, size); write_alloc_entry(ERTS_MT_ALLOC_BDY_TAG, res, n, 0, size); - erts_mtx_unlock(&mtrace_op_mutex); + if (!erts_is_allctr_wrapper_prelocked()) { + erts_mtx_unlock(&mtrace_op_mutex); + } return res; } @@ -957,12 +981,16 @@ mtrace_realloc(ErtsAlcType_t n, void *extra, void *ptr, Uint size) ErtsAllocatorFunctions_t *real_af = (ErtsAllocatorFunctions_t *) extra; void *res; - erts_mtx_lock(&mtrace_op_mutex); + if (!erts_is_allctr_wrapper_prelocked()) { + erts_mtx_lock(&mtrace_op_mutex); + } res = (*real_af->realloc)(n, real_af->extra, ptr, size); write_realloc_entry(ERTS_MT_REALLOC_BDY_TAG, res, n, 0, ptr, size); - erts_mtx_unlock(&mtrace_op_mutex); + if (!erts_is_allctr_wrapper_prelocked()) { + erts_mtx_unlock(&mtrace_op_mutex); + } return res; @@ -973,10 +1001,14 @@ mtrace_free(ErtsAlcType_t n, void *extra, void *ptr) { ErtsAllocatorFunctions_t *real_af = (ErtsAllocatorFunctions_t *) extra; - erts_mtx_lock(&mtrace_op_mutex); + if (!erts_is_allctr_wrapper_prelocked()) { + erts_mtx_lock(&mtrace_op_mutex); + } (*real_af->free)(n, real_af->extra, ptr); - write_free_entry(ERTS_MT_FREE_BDY_TAG, n, 0, ptr); + if (!erts_is_allctr_wrapper_prelocked()) { + write_free_entry(ERTS_MT_FREE_BDY_TAG, n, 0, ptr); + } erts_mtx_unlock(&mtrace_op_mutex); } diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c index ebfba065d1..e688e55c88 100644 --- a/erts/emulator/beam/erl_node_tables.c +++ b/erts/emulator/beam/erl_node_tables.c @@ -1371,6 +1371,7 @@ setup_reference_table(void) /* Insert all ports */ max = erts_ptab_max(&erts_port); for (i = 0; i < max; i++) { + ErlOffHeap *ohp; erts_aint32_t state; Port *prt; @@ -1389,8 +1390,9 @@ setup_reference_table(void) if (ERTS_P_MONITORS(prt)) insert_monitors(ERTS_P_MONITORS(prt), prt->common.id); /* Insert port data */ - for(hfp = prt->bp; hfp; hfp = hfp->next) - insert_offheap(&(hfp->off_heap), HEAP_REF, prt->common.id); + ohp = erts_port_data_offheap(prt); + if (ohp) + insert_offheap(ohp, HEAP_REF, prt->common.id); /* Insert controller */ if (prt->dist_entry) insert_dist_entry(prt->dist_entry, diff --git a/erts/emulator/beam/erl_port.h b/erts/emulator/beam/erl_port.h index 377aa72ed5..ad3f104a68 100644 --- a/erts/emulator/beam/erl_port.h +++ b/erts/emulator/beam/erl_port.h @@ -167,8 +167,7 @@ struct _erl_drv_port { #endif erts_atomic_t connected; /* A connected process */ Eterm caller; /* Current caller. */ - Eterm data; /* Data associated with port. */ - ErlHeapFragment* bp; /* Heap fragment holding data (NULL if imm data). */ + erts_smp_atomic_t data; /* Data associated with port. */ Uint bytes_in; /* Number of bytes read */ Uint bytes_out; /* Number of bytes written */ @@ -189,6 +188,12 @@ struct _erl_drv_port { int reds; /* Only used while executing driver callbacks */ }; + +void erts_init_port_data(Port *); +void erts_cleanup_port_data(Port *); +Uint erts_port_data_size(Port *); +ErlOffHeap *erts_port_data_offheap(Port *); + #define ERTS_PORT_GET_CONNECTED(PRT) \ ((Eterm) erts_atomic_read_nob(&(PRT)->connected)) #define ERTS_PORT_SET_CONNECTED(PRT, PID) \ @@ -326,8 +331,6 @@ extern erts_smp_atomic_t erts_bytes_in; /* no bytes sent into the system */ #define ERTS_PORT_REDS_CONTROL (CONTEXT_REDS/100) #define ERTS_PORT_REDS_CALL (CONTEXT_REDS/50) #define ERTS_PORT_REDS_INFO (CONTEXT_REDS/100) -#define ERTS_PORT_REDS_SET_DATA (CONTEXT_REDS/100) -#define ERTS_PORT_REDS_GET_DATA (CONTEXT_REDS/100) #define ERTS_PORT_REDS_TERMINATE (CONTEXT_REDS/50) void print_port_info(Port *, int, void *); @@ -794,8 +797,6 @@ struct binary; #define ERTS_P2P_SIG_TYPE_INFO 7 #define ERTS_P2P_SIG_TYPE_LINK 8 #define ERTS_P2P_SIG_TYPE_UNLINK 9 -#define ERTS_P2P_SIG_TYPE_SET_DATA 10 -#define ERTS_P2P_SIG_TYPE_GET_DATA 11 #define ERTS_P2P_SIG_TYPE_BITS 4 #define ERTS_P2P_SIG_TYPE_MASK \ @@ -810,6 +811,7 @@ struct binary; #define ERTS_P2P_SIG_DATA_FLG_BAD_OUTPUT ERTS_P2P_SIG_DATA_FLG(4) #define ERTS_P2P_SIG_DATA_FLG_BROKEN_LINK ERTS_P2P_SIG_DATA_FLG(5) #define ERTS_P2P_SIG_DATA_FLG_SCHED ERTS_P2P_SIG_DATA_FLG(6) +#define ERTS_P2P_SIG_DATA_FLG_ASYNC ERTS_P2P_SIG_DATA_FLG(7) struct ErtsProc2PortSigData_ { int flags; @@ -856,10 +858,6 @@ struct ErtsProc2PortSigData_ { struct { Eterm from; } unlink; - struct { - ErlHeapFragment *bp; - Eterm data; - } set_data; } u; } ; @@ -919,6 +917,7 @@ erts_schedule_proc2port_signal(Process *, Eterm *, ErtsProc2PortSigData *, int, + ErtsPortTaskHandle *, ErtsProc2PortSigCallback); int erts_deliver_port_exit(Port *, Eterm, Eterm, int); @@ -932,6 +931,7 @@ int erts_deliver_port_exit(Port *, Eterm, Eterm, int); #define ERTS_PORT_SIG_FLG_BROKEN_LINK ERTS_P2P_SIG_DATA_FLG_BROKEN_LINK #define ERTS_PORT_SIG_FLG_BAD_OUTPUT ERTS_P2P_SIG_DATA_FLG_BAD_OUTPUT #define ERTS_PORT_SIG_FLG_FORCE_SCHED ERTS_P2P_SIG_DATA_FLG_SCHED +#define ERTS_PORT_SIG_FLG_ASYNC ERTS_P2P_SIG_DATA_FLG_ASYNC /* ERTS_PORT_SIG_FLG_FORCE_IMM_CALL only when crash dumping... */ #define ERTS_PORT_SIG_FLG_FORCE_IMM_CALL ERTS_P2P_SIG_DATA_FLG_BAD_OUTPUT @@ -953,7 +953,5 @@ ErtsPortOpResult erts_port_unlink(Process *, Port *, Eterm, Eterm *); ErtsPortOpResult erts_port_control(Process *, Port *, unsigned int, Eterm, Eterm *); ErtsPortOpResult erts_port_call(Process *, Port *, unsigned int, Eterm, Eterm *); ErtsPortOpResult erts_port_info(Process *, Port *, Eterm, Eterm *); -ErtsPortOpResult erts_port_set_data(Process *, Port *, Eterm, Eterm *); -ErtsPortOpResult erts_port_get_data(Process *, Port *, Eterm *); #endif diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c index 0ed08bee01..53cb01a8c6 100644 --- a/erts/emulator/beam/erl_port_task.c +++ b/erts/emulator/beam/erl_port_task.c @@ -552,6 +552,19 @@ set_handle(ErtsPortTask *ptp, ErtsPortTaskHandle *pthp) } } +static ERTS_INLINE void +set_tmp_handle(ErtsPortTask *ptp, ErtsPortTaskHandle *pthp) +{ + ptp->u.alive.handle = NULL; + if (pthp) { + /* + * IMPORTANT! Task either need to be aborted, or task handle + * need to be detached before thread progress has been made. + */ + erts_smp_atomic_set_relb(pthp, (erts_aint_t) ptp); + } +} + /* * Busy port queue management @@ -1182,6 +1195,13 @@ erl_drv_consume_timeslice(ErlDrvPort dprt, int percent) return 1; } +void +erts_port_task_tmp_handle_detach(ErtsPortTaskHandle *pthp) +{ + ERTS_SMP_LC_ASSERT(erts_thr_progress_lc_is_delaying()); + reset_port_task_handle(pthp); +} + /* * Abort a scheduled task. */ @@ -1206,7 +1226,7 @@ erts_port_task_abort(ErtsPortTaskHandle *pthp) ERTS_SMP_READ_MEMORY_BARRIER; old_state = erts_smp_atomic32_read_nob(&ptp->state); if (old_state == ERTS_PT_STATE_SCHEDULED) { - ASSERT(saved_pthp == pthp); + ASSERT(!saved_pthp || saved_pthp == pthp); } #endif @@ -1227,9 +1247,6 @@ erts_port_task_abort(ErtsPortTaskHandle *pthp) &erts_port_task_outstanding_io_tasks) > 0); erts_smp_atomic_dec_relb(&erts_port_task_outstanding_io_tasks); break; - case ERTS_PORT_TASK_PROC_SIG: - ERTS_INTERNAL_ERROR("Aborted process to port signal"); - break; default: break; } @@ -1404,7 +1421,6 @@ erts_port_task_schedule(Eterm id, } case ERTS_PORT_TASK_PROC_SIG: { va_list argp; - ASSERT(!pthp); va_start(argp, type); sigdp = va_arg(argp, ErtsProc2PortSigData *); ptp = p2p_sig_data_to_task(sigdp); @@ -1412,7 +1428,7 @@ erts_port_task_schedule(Eterm id, ptp->u.alive.flags |= va_arg(argp, int); va_end(argp); if (!(ptp->u.alive.flags & ERTS_PT_FLG_NOSUSPEND)) - set_handle(ptp, pthp); + set_tmp_handle(ptp, pthp); else { ns_pthlp = erts_alloc(ERTS_ALC_T_PT_HNDL_LIST, sizeof(ErtsPortTaskHandleList)); diff --git a/erts/emulator/beam/erl_port_task.h b/erts/emulator/beam/erl_port_task.h index ae6cd69ae2..d35a15c27b 100644 --- a/erts/emulator/beam/erl_port_task.h +++ b/erts/emulator/beam/erl_port_task.h @@ -243,7 +243,9 @@ int erts_port_task_execute(ErtsRunQueue *, Port **); void erts_port_task_init(void); #endif +void erts_port_task_tmp_handle_detach(ErtsPortTaskHandle *); int erts_port_task_abort(ErtsPortTaskHandle *); + void erts_port_task_abort_nosuspend_tasks(Port *); int erts_port_task_schedule(Eterm, diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 8bf481dbff..7415a5721f 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -2625,6 +2625,8 @@ set_no_active_runqs(int active) erts_aint32_t exp = erts_smp_atomic32_read_nob(&balance_info.no_runqs); while (1) { erts_aint32_t act, new; + if ((exp & ERTS_NO_RUNQS_MASK) == active) + break; new = exp & (ERTS_NO_RUNQS_MASK << ERTS_NO_USED_RUNQS_SHIFT); new |= active & ERTS_NO_RUNQS_MASK; act = erts_smp_atomic32_cmpxchg_nob(&balance_info.no_runqs, new, exp); @@ -3171,9 +3173,6 @@ evacuate_run_queue(ErtsRunQueue *rq, if (notify) smp_notify_inc_runq(to_rq); } - - if (ERTS_EMPTY_RUNQ(rq)) - empty_runq(rq); } static int @@ -4067,9 +4066,8 @@ static void change_no_used_runqs(int used) { ErtsMigrationPaths *new_mpaths, *old_mpaths; - int active, qix; + int qix; erts_smp_mtx_lock(&balance_info.update_mtx); - get_no_runqs(&active, NULL); set_no_used_runqs(used); old_mpaths = erts_get_migration_paths_managed(); @@ -4672,6 +4670,8 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) esdp->reductions = 0; init_sched_wall_time(&esdp->sched_wall_time); + + erts_port_task_handle_init(&esdp->nosuspend_port_task_handle); } init_misc_aux_work(); @@ -4697,7 +4697,7 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) schdlr_sspnd.msb.ongoing = 0; erts_smp_atomic32_init_nob(&schdlr_sspnd.active, no_schedulers); schdlr_sspnd.msb.procs = NULL; - init_no_runqs(no_schedulers, no_schedulers_online); + init_no_runqs(no_schedulers_online, no_schedulers_online); balance_info.last_active_runqs = no_schedulers; erts_smp_mtx_init(&balance_info.update_mtx, "migration_info_update"); balance_info.forced_check_balance = 0; diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 3d3579fa7e..5a1f6bbe8d 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -507,6 +507,7 @@ struct ErtsSchedulerData_ { Uint64 reductions; ErtsSchedWallTime sched_wall_time; + ErtsPortTaskHandle nosuspend_port_task_handle; #ifdef ERTS_DO_VERIFY_UNUSED_TEMP_ALLOC erts_alloc_verify_func_t verify_unused_temp_alloc; @@ -1421,6 +1422,8 @@ Eterm erts_debug_reader_groups_map(Process *c_p, int groups); Uint erts_debug_nbalance(void); int erts_debug_wait_deallocations(Process *c_p); +Uint erts_process_memory(Process *c_p); + #ifdef ERTS_SMP # define ERTS_GET_SCHEDULER_DATA_FROM_PROC(PROC) ((PROC)->scheduler_data) # define ERTS_PROC_GET_SCHDATA(PROC) ((PROC)->scheduler_data) diff --git a/erts/emulator/beam/erl_process_dump.c b/erts/emulator/beam/erl_process_dump.c index a93229c473..6cd0d23b97 100644 --- a/erts/emulator/beam/erl_process_dump.c +++ b/erts/emulator/beam/erl_process_dump.c @@ -76,6 +76,43 @@ erts_deep_process_dump(int to, void *to_arg) dump_binaries(to, to_arg, all_binaries); } +Uint erts_process_memory(Process *p) { + ErlMessage *mp; + Uint size = 0; + struct saved_calls *scb; + size += sizeof(Process); + + ERTS_SMP_MSGQ_MV_INQ2PRIVQ(p); + + erts_doforall_links(ERTS_P_LINKS(p), &erts_one_link_size, &size); + erts_doforall_monitors(ERTS_P_MONITORS(p), &erts_one_mon_size, &size); + size += (p->heap_sz + p->mbuf_sz) * sizeof(Eterm); + if (p->old_hend && p->old_heap) + size += (p->old_hend - p->old_heap) * sizeof(Eterm); + + size += p->msg.len * sizeof(ErlMessage); + + for (mp = p->msg.first; mp; mp = mp->next) + if (mp->data.attached) + size += erts_msg_attached_data_size(mp)*sizeof(Eterm); + + if (p->arg_reg != p->def_arg_reg) { + size += p->arity * sizeof(p->arg_reg[0]); + } + + if (p->psd) + size += sizeof(ErtsPSD); + + scb = ERTS_PROC_GET_SAVED_CALLS_BUF(p); + if (scb) { + size += (sizeof(struct saved_calls) + + (scb->len-1) * sizeof(scb->ct[0])); + } + + size += erts_dicts_mem_size(p); + return size; +} + static void dump_process_info(int to, void *to_arg, Process *p) { diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 5ce0d97c74..8420cfae24 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -3147,17 +3147,6 @@ encode_size_struct2(ErtsAtomCacheMap *acmp, Eterm obj, unsigned dflags) return result; } -static int is_valid_utf8_atom(byte* bytes, Uint nbytes) -{ - byte* err_pos; - Uint num_chars; - - /*SVERK Do we really need to validate correct utf8? */ - return nbytes <= MAX_ATOM_SZ_LIMIT - && erts_analyze_utf8(bytes, nbytes, &err_pos, &num_chars, NULL) == ERTS_UTF8_OK - && num_chars <= MAX_ATOM_CHARACTERS; -} - static Sint decoded_size(byte *ep, byte* endp, int internal_tags) { @@ -3235,7 +3224,7 @@ decoded_size(byte *ep, byte* endp, int internal_tags) CHKSIZE(2); n = get_int16(ep); ep += 2; - if (!is_valid_utf8_atom(ep, n)) { + if (n > MAX_ATOM_SZ_LIMIT) { return -1; } SKIP(n+atom_extra_skip); @@ -3254,7 +3243,7 @@ decoded_size(byte *ep, byte* endp, int internal_tags) CHKSIZE(1); n = get_int8(ep); ep++; - if (!is_valid_utf8_atom(ep, n)) { + if (n > MAX_ATOM_SZ_LIMIT) { return -1; } SKIP(n+atom_extra_skip); diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 7cadd4aaad..b6b7b47bd6 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -364,9 +364,8 @@ static Port *create_port(char *name, ERTS_P_LINKS(prt) = NULL; ERTS_P_MONITORS(prt) = NULL; prt->linebuf = NULL; - prt->bp = NULL; prt->suspended = NULL; - prt->data = am_undefined; + erts_init_port_data(prt); prt->port_data_lock = NULL; prt->control_flags = 0; prt->bytes_in = 0; @@ -1442,6 +1441,7 @@ erts_schedule_proc2port_signal(Process *c_p, Eterm *refp, ErtsProc2PortSigData *sigdp, int task_flags, + ErtsPortTaskHandle *pthp, ErtsProc2PortSigCallback callback) { int sched_res; @@ -1491,7 +1491,7 @@ erts_schedule_proc2port_signal(Process *c_p, /* Schedule port close call for later execution... */ sched_res = erts_port_task_schedule(prt->common.id, - NULL, + pthp, ERTS_PORT_TASK_PROC_SIG, sigdp, callback, @@ -1629,6 +1629,7 @@ bad_port_signal(Process *c_p, refp, sigdp, 0, + NULL, port_badsig); } @@ -1838,8 +1839,11 @@ erts_port_output(Process *c_p, ErlIOVec *evp = NULL; char *buf = NULL; int force_immediate_call = (flags & ERTS_PORT_SIG_FLG_FORCE_IMM_CALL); + int async_nosuspend; + ErtsPortTaskHandle *ns_pthp; ASSERT((flags & ~(ERTS_PORT_SIG_FLG_BANG_OP + | ERTS_PORT_SIG_FLG_ASYNC | ERTS_PORT_SIG_FLG_NOSUSPEND | ERTS_PORT_SIG_FLG_FORCE | ERTS_PORT_SIG_FLG_FORCE_IMM_CALL)) == 0); @@ -1861,6 +1865,12 @@ erts_port_output(Process *c_p, ? ERTS_PORT_OP_DROPPED : ERTS_PORT_OP_BUSY); + async_nosuspend = ((flags & (ERTS_PORT_SIG_FLG_ASYNC + | ERTS_PORT_SIG_FLG_NOSUSPEND + | ERTS_PORT_SIG_FLG_FORCE)) + == (ERTS_PORT_SIG_FLG_ASYNC + | ERTS_PORT_SIG_FLG_NOSUSPEND)); + try_call = (force_immediate_call /* crash dumping */ || !(sched_flags & (invalid_flags | ERTS_PTS_FLGS_FORCE_SCHEDULE_OP))); @@ -1995,6 +2005,15 @@ erts_port_output(Process *c_p, return ERTS_PORT_OP_DONE; case ERTS_TRY_IMM_DRV_CALL_INVALID_SCHED_FLAGS: sched_flags = try_call_state.sched_flags; + if (async_nosuspend + && (sched_flags & (busy_flgs|ERTS_PTS_FLG_EXIT))) { + driver_free_binary(cbin); + if (evp != &ev) + erts_free(ERTS_ALC_T_TMP, evp); + return ((sched_flags & ERTS_PTS_FLG_EXIT) + ? ERTS_PORT_OP_DROPPED + : ERTS_PORT_OP_BUSY); + } case ERTS_TRY_IMM_DRV_CALL_BUSY_LOCK: /* Schedule outputv() call instead... */ break; @@ -2142,6 +2161,13 @@ erts_port_output(Process *c_p, return ERTS_PORT_OP_DONE; case ERTS_TRY_IMM_DRV_CALL_INVALID_SCHED_FLAGS: sched_flags = try_call_state.sched_flags; + if (async_nosuspend + && (sched_flags & (busy_flgs|ERTS_PTS_FLG_EXIT))) { + erts_free(ERTS_ALC_T_TMP, buf); + return ((sched_flags & ERTS_PTS_FLG_EXIT) + ? ERTS_PORT_OP_DROPPED + : ERTS_PORT_OP_BUSY); + } case ERTS_TRY_IMM_DRV_CALL_BUSY_LOCK: /* Schedule outputv() call instead... */ break; @@ -2163,20 +2189,33 @@ erts_port_output(Process *c_p, task_flags = ERTS_PT_FLG_WAIT_BUSY; sigdp->flags |= flags; + ns_pthp = NULL; if (flags & (ERTS_P2P_SIG_DATA_FLG_FORCE|ERTS_P2P_SIG_DATA_FLG_NOSUSPEND)) { task_flags = 0; if (flags & ERTS_P2P_SIG_DATA_FLG_FORCE) sigdp->flags &= ~ERTS_P2P_SIG_DATA_FLG_NOSUSPEND; + else if (async_nosuspend) { + ErtsSchedulerData *esdp = (c_p + ? ERTS_PROC_GET_SCHDATA(c_p) + : erts_get_scheduler_data()); + ASSERT(esdp); + ns_pthp = &esdp->nosuspend_port_task_handle; + sigdp->flags &= ~ERTS_P2P_SIG_DATA_FLG_NOSUSPEND; + } else if (flags & ERTS_P2P_SIG_DATA_FLG_NOSUSPEND) task_flags = ERTS_PT_FLG_NOSUSPEND; } + ASSERT(ns_pthp || !async_nosuspend); + ASSERT(async_nosuspend || !ns_pthp); + res = erts_schedule_proc2port_signal(c_p, prt, c_p ? c_p->common.id : ERTS_INVALID_PID, refp, sigdp, task_flags, + ns_pthp, port_sig_callback); if (res != ERTS_PORT_OP_SCHEDULED) { @@ -2187,9 +2226,23 @@ erts_port_output(Process *c_p, return res; } - if (!(sched_flags & ERTS_PTS_FLG_EXIT) && (sched_flags & busy_flgs)) - return ERTS_PORT_OP_BUSY_SCHEDULED; - + if (!(flags & ERTS_PORT_SIG_FLG_FORCE)) { + sched_flags = erts_smp_atomic32_read_acqb(&prt->sched.flags); + if (!(sched_flags & ERTS_PTS_FLG_BUSY_PORT)) { + if (async_nosuspend) + erts_port_task_tmp_handle_detach(ns_pthp); + } + else { + if (!async_nosuspend) + return ERTS_PORT_OP_BUSY_SCHEDULED; + else { + if (erts_port_task_abort(ns_pthp) == 0) + return ERTS_PORT_OP_BUSY; + else + erts_port_task_tmp_handle_detach(ns_pthp); + } + } + } return res; bad_value: @@ -2285,6 +2338,7 @@ erts_port_exit(Process *c_p, ErlHeapFragment *bp = NULL; ASSERT((flags & ~(ERTS_PORT_SIG_FLG_BANG_OP + | ERTS_PORT_SIG_FLG_ASYNC | ERTS_PORT_SIG_FLG_BROKEN_LINK | ERTS_PORT_SIG_FLG_FORCE_SCHED)) == 0); @@ -2345,6 +2399,7 @@ erts_port_exit(Process *c_p, refp, sigdp, 0, + NULL, port_sig_exit); if (res == ERTS_PORT_OP_DROPPED) { @@ -2460,7 +2515,8 @@ erts_port_connect(Process *c_p, !refp, am_connect); - ASSERT((flags & ~ERTS_PORT_SIG_FLG_BANG_OP) == 0); + ASSERT((flags & ~(ERTS_PORT_SIG_FLG_BANG_OP + | ERTS_PORT_SIG_FLG_ASYNC)) == 0); if (is_not_internal_pid(connect)) connect_id = NIL; /* Fail in op (for signal order) */ @@ -2499,6 +2555,7 @@ erts_port_connect(Process *c_p, refp, sigdp, 0, + NULL, port_sig_connect); } @@ -2555,6 +2612,7 @@ erts_port_unlink(Process *c_p, Port *prt, Eterm from, Eterm *refp) refp, sigdp, 0, + NULL, port_sig_unlink); } @@ -2644,6 +2702,7 @@ erts_port_link(Process *c_p, Port *prt, Eterm to, Eterm *refp) refp, sigdp, 0, + NULL, port_sig_link); } @@ -3371,11 +3430,8 @@ terminate_port(Port *prt) erts_free(ERTS_ALC_T_LINEBUF, (void *) prt->linebuf); prt->linebuf = NULL; } - if (prt->bp != NULL) { - free_message_buffer(prt->bp); - prt->bp = NULL; - prt->data = am_undefined; - } + + erts_cleanup_port_data(prt); if (prt->psd) erts_free(ERTS_ALC_T_PRTSD, prt->psd); @@ -3625,6 +3681,10 @@ erts_port_command(Process *c_p, ASSERT(port); flags |= ERTS_PORT_SIG_FLG_BANG_OP; + if (!erts_port_synchronous_ops) { + flags |= ERTS_PORT_SIG_FLG_ASYNC; + refp = NULL; + } if (is_tuple_arity(command, 2)) { Eterm cntd; @@ -3632,21 +3692,14 @@ erts_port_command(Process *c_p, cntd = tp[1]; if (is_internal_pid(cntd)) { if (tp[2] == am_close) { - if (!erts_port_synchronous_ops) - refp = NULL; flags &= ~ERTS_PORT_SIG_FLG_NOSUSPEND; return erts_port_exit(c_p, flags, port, cntd, am_normal, refp); } else if (is_tuple_arity(tp[2], 2)) { tp = tuple_val(tp[2]); if (tp[1] == am_command) { - if (!(flags & ERTS_PORT_SIG_FLG_NOSUSPEND) - && !erts_port_synchronous_ops) - refp = NULL; return erts_port_output(c_p, flags, port, cntd, tp[2], refp); } else if (tp[1] == am_connect) { - if (!erts_port_synchronous_ops) - refp = NULL; flags &= ~ERTS_PORT_SIG_FLG_NOSUSPEND; return erts_port_connect(c_p, flags, port, cntd, tp[2], refp); } @@ -3655,8 +3708,6 @@ erts_port_command(Process *c_p, } /* badsig */ - if (!erts_port_synchronous_ops) - refp = NULL; flags &= ~ERTS_PORT_SIG_FLG_NOSUSPEND; return bad_port_signal(c_p, flags, port, c_p->common.id, refp, am_command); } @@ -4053,6 +4104,7 @@ erts_port_control(Process* c_p, retvalp, sigdp, 0, + NULL, port_sig_control); if (res != ERTS_PORT_OP_SCHEDULED) { cleanup_scheduled_control(binp, bufp); @@ -4147,7 +4199,7 @@ port_sig_call(Port *prt, ErlOffHeap *ohp; Process *rp; ErtsProcLocks rp_locks = 0; - Uint hsz; + Sint hsz; rp = erts_proc_lookup_raw(sigdp->caller); if (!rp) @@ -4264,7 +4316,7 @@ erts_port_call(Process* c_p, switch (try_call_res) { case ERTS_TRY_IMM_DRV_CALL_OK: { Eterm *hp, *hp_end; - Uint hsz; + Sint hsz; unsigned ret_flags = 0U; Eterm term; @@ -4333,6 +4385,7 @@ erts_port_call(Process* c_p, retvalp, sigdp, 0, + NULL, port_sig_call); if (res != ERTS_PORT_OP_SCHEDULED) { cleanup_scheduled_call(bufp); @@ -4499,228 +4552,10 @@ erts_port_info(Process* c_p, retvalp, sigdp, 0, + NULL, port_sig_info); } -static int -port_sig_set_data(Port *prt, - erts_aint32_t state, - int op, - ErtsProc2PortSigData *sigdp) -{ - ASSERT(sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY); - - if (op == ERTS_PROC2PORT_SIG_EXEC) { - if (prt->bp) - free_message_buffer(prt->bp); - prt->bp = sigdp->u.set_data.bp; - prt->data = sigdp->u.set_data.data; - port_sched_op_reply(sigdp->caller, sigdp->ref, am_true); - } - else { - if (sigdp->u.set_data.bp) - free_message_buffer(sigdp->u.set_data.bp); - port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg); - } - return ERTS_PORT_REDS_SET_DATA; -} - -ErtsPortOpResult -erts_port_set_data(Process* c_p, - Port *prt, - Eterm data, - Eterm *refp) -{ - ErtsPortOpResult res; - Eterm set_data; - ErlHeapFragment *bp; - ErtsProc2PortSigData *sigdp; - ErtsTryImmDrvCallResult try_call_res; - ErtsTryImmDrvCallState try_call_state - = ERTS_INIT_TRY_IMM_DRV_CALL_STATE( - c_p, - prt, - ERTS_PORT_SFLGS_INVALID_LOOKUP, - 0, - !refp, - am_set_data); - - if (is_immed(data)) { - set_data = data; - bp = NULL; - } - else { - Eterm *hp; - Uint sz = size_object(data); - bp = new_message_buffer(sz); - hp = bp->mem; - set_data = copy_struct(data, sz, &hp, &bp->off_heap); - } - - try_call_res = try_imm_drv_call(&try_call_state); - switch (try_call_res) { - case ERTS_TRY_IMM_DRV_CALL_OK: - if (prt->bp) - free_message_buffer(prt->bp); - prt->bp = bp; - prt->data = set_data; - finalize_imm_drv_call(&try_call_state); - BUMP_REDS(c_p, ERTS_PORT_REDS_SET_DATA); - return ERTS_PORT_OP_DONE; - case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT: - return ERTS_PORT_OP_DROPPED; - case ERTS_TRY_IMM_DRV_CALL_INVALID_SCHED_FLAGS: - case ERTS_TRY_IMM_DRV_CALL_BUSY_LOCK: - /* Schedule call instead... */ - break; - } - - sigdp = erts_port_task_alloc_p2p_sig_data(); - sigdp->flags = ERTS_P2P_SIG_TYPE_SET_DATA; - sigdp->u.set_data.data = set_data; - sigdp->u.set_data.bp = bp; - - res = erts_schedule_proc2port_signal(c_p, - prt, - c_p->common.id, - refp, - sigdp, - 0, - port_sig_set_data); - if (res != ERTS_PORT_OP_SCHEDULED && bp) - free_message_buffer(bp); - return res; -} - -static int -port_sig_get_data(Port *prt, - erts_aint32_t state, - int op, - ErtsProc2PortSigData *sigdp) -{ - ASSERT(sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY); - if (op != ERTS_PROC2PORT_SIG_EXEC) - port_sched_op_reply(sigdp->caller, sigdp->ref, am_badarg); - else { - Process *rp; - ErtsProcLocks rp_locks = 0; - - rp = erts_proc_lookup_raw(sigdp->caller); - if (rp) { - Uint hsz; - Eterm *hp, *hp_start; - Eterm data, msg; - ErlHeapFragment *bp; - ErlOffHeap *ohp; - - hsz = ERTS_QUEUE_PORT_SCHED_OP_REPLY_SIZE; - hsz += 3; - if (prt->bp) - hsz += prt->bp->used_size; - - hp_start = hp = erts_alloc_message_heap(hsz, - &bp, - &ohp, - rp, - &rp_locks); - - if (is_immed(prt->data)) - data = prt->data; - else - data = copy_struct(prt->data, - prt->bp->used_size, - &hp, - &bp->off_heap); - - - - msg = TUPLE2(hp, am_ok, data); - hp += 3; - - queue_port_sched_op_reply(rp, - &rp_locks, - hp_start, - hp, - hsz, - bp, - sigdp->ref, - msg); - if (rp_locks) - erts_smp_proc_unlock(rp, rp_locks); - } - } - return ERTS_PORT_REDS_GET_DATA; -} - -ErtsPortOpResult -erts_port_get_data(Process* c_p, - Port *prt, - Eterm *retvalp) -{ - ErtsProc2PortSigData *sigdp; - ErtsTryImmDrvCallResult try_call_res; - ErtsTryImmDrvCallState try_call_state - = ERTS_INIT_TRY_IMM_DRV_CALL_STATE( - c_p, - prt, - ERTS_PORT_SFLGS_INVALID_LOOKUP, - 0, - 0, - am_get_data); - - try_call_res = try_imm_drv_call(&try_call_state); - switch (try_call_res) { - case ERTS_TRY_IMM_DRV_CALL_OK: { - Eterm *hp; - Eterm data; - ErlHeapFragment *bp; - Uint sz; - if (is_immed(prt->data)) { - bp = NULL; - data = prt->data; - } - else { - bp = new_message_buffer(prt->bp->used_size); - data = copy_struct(prt->data, - prt->bp->used_size, - &hp, - &bp->off_heap); - } - finalize_imm_drv_call(&try_call_state); - if (is_immed(data)) - sz = 0; - else - sz = bp->used_size; - - hp = HAlloc(c_p, sz + 3); - if (is_not_immed(data)) { - data = copy_struct(data, bp->used_size, &hp, &MSO(c_p)); - free_message_buffer(bp); - } - *retvalp = TUPLE2(hp, am_ok, data); - BUMP_REDS(c_p, ERTS_PORT_REDS_GET_DATA); - return ERTS_PORT_OP_DONE; - } - case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT: - return ERTS_PORT_OP_DROPPED; - case ERTS_TRY_IMM_DRV_CALL_INVALID_SCHED_FLAGS: - case ERTS_TRY_IMM_DRV_CALL_BUSY_LOCK: - /* Schedule call instead... */ - break; - } - - sigdp = erts_port_task_alloc_p2p_sig_data(); - sigdp->flags = ERTS_P2P_SIG_TYPE_GET_DATA; - - return erts_schedule_proc2port_signal(c_p, - prt, - c_p->common.id, - retvalp, - sigdp, - 0, - port_sig_get_data); -} - typedef struct { int to; void *arg; @@ -5941,10 +5776,6 @@ int driver_outputv(ErlDrvPort ix, char* hbuf, ErlDrvSizeT hlen, return driver_output2(ix, hbuf, hlen, NULL, 0); size = vec->size - skip; /* Size of remaining bytes in vector */ - ASSERT(hlen >= 0); /* debug only */ - if (hlen < 0) - hlen = 0; - prt = erts_drvport2port_state(ix, &state); if (prt == ERTS_INVALID_ERL_DRV_PORT) return -1; diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 2451f41a82..301ce2d0e2 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -678,8 +678,8 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define INET_LOPT_UDP_READ_PACKETS 33 /* Number of packets to read */ #define INET_OPT_RAW 34 /* Raw socket options */ #define INET_LOPT_TCP_SEND_TIMEOUT_CLOSE 35 /* auto-close on send timeout or not */ -#define INET_LOPT_TCP_MSGQ_HIWTRMRK 36 /* set local high watermark */ -#define INET_LOPT_TCP_MSGQ_LOWTRMRK 37 /* set local low watermark */ +#define INET_LOPT_MSGQ_HIWTRMRK 36 /* set local msgq high watermark */ +#define INET_LOPT_MSGQ_LOWTRMRK 37 /* set local msgq low watermark */ /* SCTP options: a separate range, from 100: */ #define SCTP_OPT_RTOINFO 100 #define SCTP_OPT_ASSOCINFO 101 @@ -5476,27 +5476,25 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len) } continue; - case INET_LOPT_TCP_MSGQ_HIWTRMRK: - if (desc->stype == SOCK_STREAM) { - ErlDrvSizeT high; - if (ival < ERL_DRV_BUSY_MSGQ_LIM_MIN - || ERL_DRV_BUSY_MSGQ_LIM_MAX < ival) - return -1; - high = (ErlDrvSizeT) ival; - erl_drv_busy_msgq_limits(desc->port, NULL, &high); - } + case INET_LOPT_MSGQ_HIWTRMRK: { + ErlDrvSizeT high; + if (ival < ERL_DRV_BUSY_MSGQ_LIM_MIN + || ERL_DRV_BUSY_MSGQ_LIM_MAX < ival) + return -1; + high = (ErlDrvSizeT) ival; + erl_drv_busy_msgq_limits(desc->port, NULL, &high); continue; + } - case INET_LOPT_TCP_MSGQ_LOWTRMRK: - if (desc->stype == SOCK_STREAM) { - ErlDrvSizeT low; - if (ival < ERL_DRV_BUSY_MSGQ_LIM_MIN - || ERL_DRV_BUSY_MSGQ_LIM_MAX < ival) - return -1; - low = (ErlDrvSizeT) ival; - erl_drv_busy_msgq_limits(desc->port, &low, NULL); - } + case INET_LOPT_MSGQ_LOWTRMRK: { + ErlDrvSizeT low; + if (ival < ERL_DRV_BUSY_MSGQ_LIM_MIN + || ERL_DRV_BUSY_MSGQ_LIM_MAX < ival) + return -1; + low = (ErlDrvSizeT) ival; + erl_drv_busy_msgq_limits(desc->port, &low, NULL); continue; + } case INET_LOPT_TCP_SEND_TIMEOUT: if (desc->stype == SOCK_STREAM) { @@ -6398,31 +6396,23 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc, } continue; - case INET_LOPT_TCP_MSGQ_HIWTRMRK: - if (desc->stype == SOCK_STREAM) { - ErlDrvSizeT high = ERL_DRV_BUSY_MSGQ_READ_ONLY; - *ptr++ = opt; - erl_drv_busy_msgq_limits(desc->port, NULL, &high); - ival = high > INT_MAX ? INT_MAX : (int) high; - put_int32(ival, ptr); - } - else { - TRUNCATE_TO(0,ptr); - } + case INET_LOPT_MSGQ_HIWTRMRK: { + ErlDrvSizeT high = ERL_DRV_BUSY_MSGQ_READ_ONLY; + *ptr++ = opt; + erl_drv_busy_msgq_limits(desc->port, NULL, &high); + ival = high > INT_MAX ? INT_MAX : (int) high; + put_int32(ival, ptr); continue; + } - case INET_LOPT_TCP_MSGQ_LOWTRMRK: - if (desc->stype == SOCK_STREAM) { - ErlDrvSizeT low = ERL_DRV_BUSY_MSGQ_READ_ONLY; - *ptr++ = opt; - erl_drv_busy_msgq_limits(desc->port, &low, NULL); - ival = low > INT_MAX ? INT_MAX : (int) low; - put_int32(ival, ptr); - } - else { - TRUNCATE_TO(0,ptr); - } + case INET_LOPT_MSGQ_LOWTRMRK: { + ErlDrvSizeT low = ERL_DRV_BUSY_MSGQ_READ_ONLY; + *ptr++ = opt; + erl_drv_busy_msgq_limits(desc->port, &low, NULL); + ival = low > INT_MAX ? INT_MAX : (int) low; + put_int32(ival, ptr); continue; + } case INET_LOPT_TCP_SEND_TIMEOUT: if (desc->stype == SOCK_STREAM) { @@ -7471,6 +7461,20 @@ static void inet_stop(inet_descriptor* desc) FREE(desc); } +static void set_default_msgq_limits(ErlDrvPort port) +{ + ErlDrvSizeT q_high = INET_HIGH_MSGQ_WATERMARK; + ErlDrvSizeT q_low = INET_LOW_MSGQ_WATERMARK; + if (q_low < ERL_DRV_BUSY_MSGQ_LIM_MIN) + q_low = ERL_DRV_BUSY_MSGQ_LIM_MIN; + else if (q_low > ERL_DRV_BUSY_MSGQ_LIM_MAX) + q_low = ERL_DRV_BUSY_MSGQ_LIM_MAX; + if (q_high < ERL_DRV_BUSY_MSGQ_LIM_MIN) + q_high = ERL_DRV_BUSY_MSGQ_LIM_MIN; + else if (q_high > ERL_DRV_BUSY_MSGQ_LIM_MAX) + q_high = ERL_DRV_BUSY_MSGQ_LIM_MAX; + erl_drv_busy_msgq_limits(port, &q_low, &q_high); +} /* Allocate descriptor */ static ErlDrvData inet_start(ErlDrvPort port, int size, int protocol) @@ -8091,9 +8095,8 @@ static int tcp_inet_init(void) /* initialize the TCP descriptor */ -static ErlDrvData tcp_inet_start(ErlDrvPort port, char* args) +static ErlDrvData prep_tcp_inet_start(ErlDrvPort port, char* args) { - ErlDrvSizeT q_low, q_high; tcp_descriptor* desc; DEBUGF(("tcp_inet_start(%ld) {\r\n", (long)port)); @@ -8103,17 +8106,6 @@ static ErlDrvData tcp_inet_start(ErlDrvPort port, char* args) return ERL_DRV_ERROR_ERRNO; desc->high = INET_HIGH_WATERMARK; desc->low = INET_LOW_WATERMARK; - q_high = INET_HIGH_MSGQ_WATERMARK; - q_low = INET_LOW_MSGQ_WATERMARK; - if (q_low < ERL_DRV_BUSY_MSGQ_LIM_MIN) - q_low = ERL_DRV_BUSY_MSGQ_LIM_MIN; - else if (q_low > ERL_DRV_BUSY_MSGQ_LIM_MAX) - q_low = ERL_DRV_BUSY_MSGQ_LIM_MAX; - if (q_high < ERL_DRV_BUSY_MSGQ_LIM_MIN) - q_high = ERL_DRV_BUSY_MSGQ_LIM_MIN; - else if (q_high > ERL_DRV_BUSY_MSGQ_LIM_MAX) - q_high = ERL_DRV_BUSY_MSGQ_LIM_MAX; - erl_drv_busy_msgq_limits(port, &q_low, &q_high); desc->send_timeout = INET_INFINITY; desc->send_timeout_close = 0; desc->busy_on_send = 0; @@ -8130,6 +8122,12 @@ static ErlDrvData tcp_inet_start(ErlDrvPort port, char* args) return (ErlDrvData) desc; } +static ErlDrvData tcp_inet_start(ErlDrvPort port, char* args) +{ + ErlDrvData data = prep_tcp_inet_start(port, args); + set_default_msgq_limits(port); + return data; +} /* Copy a descriptor, by creating a new port with same settings * as the descriptor desc. * return NULL on error (SYSTEM_LIMIT no ports avail) @@ -8141,7 +8139,7 @@ static tcp_descriptor* tcp_inet_copy(tcp_descriptor* desc,SOCKET s, ErlDrvPort port = desc->inet.port; tcp_descriptor* copy_desc; - copy_desc = (tcp_descriptor*) tcp_inet_start(port, NULL); + copy_desc = (tcp_descriptor*) prep_tcp_inet_start(port, NULL); /* Setup event if needed */ if ((copy_desc->inet.s = s) != INVALID_SOCKET) { @@ -9864,12 +9862,15 @@ static int should_use_so_bsdcompat(void) * as the descriptor desc. * return NULL on error (ENFILE no ports avail) */ +static ErlDrvData packet_inet_start(ErlDrvPort port, char* args, int protocol); + static udp_descriptor* sctp_inet_copy(udp_descriptor* desc, SOCKET s, int* err) { + ErlDrvSizeT q_low, q_high; ErlDrvPort port = desc->inet.port; udp_descriptor* copy_desc; - copy_desc = (udp_descriptor*) sctp_inet_start(port, NULL); + copy_desc = (udp_descriptor*) packet_inet_start(port, NULL, IPPROTO_SCTP); /* Setup event if needed */ if ((copy_desc->inet.s = s) != INVALID_SOCKET) { @@ -9900,9 +9901,17 @@ static udp_descriptor* sctp_inet_copy(udp_descriptor* desc, SOCKET s, int* err) FREE(copy_desc); return NULL; } + + /* Read busy msgq limits of parent */ + q_low = q_high = ERL_DRV_BUSY_MSGQ_READ_ONLY; + erl_drv_busy_msgq_limits(desc->inet.port, &q_low, &q_high); + /* Write same busy msgq limits to child */ + erl_drv_busy_msgq_limits(port, &q_low, &q_high); + copy_desc->inet.port = port; copy_desc->inet.dport = driver_mk_port(port); *err = 0; + return copy_desc; } #endif @@ -9935,13 +9944,17 @@ static ErlDrvData packet_inet_start(ErlDrvPort port, char* args, int protocol) static ErlDrvData udp_inet_start(ErlDrvPort port, char *args) { - return packet_inet_start(port, args, IPPROTO_UDP); + ErlDrvData data = packet_inet_start(port, args, IPPROTO_UDP); + set_default_msgq_limits(port); + return data; } #ifdef HAVE_SCTP static ErlDrvData sctp_inet_start(ErlDrvPort port, char *args) { - return packet_inet_start(port, args, IPPROTO_SCTP); + ErlDrvData data = packet_inet_start(port, args, IPPROTO_SCTP); + set_default_msgq_limits(port); + return data; } #endif diff --git a/erts/emulator/drivers/unix/ttsl_drv.c b/erts/emulator/drivers/unix/ttsl_drv.c index 8912d148a5..d76401a790 100644 --- a/erts/emulator/drivers/unix/ttsl_drv.c +++ b/erts/emulator/drivers/unix/ttsl_drv.c @@ -42,6 +42,9 @@ static ErlDrvData ttysl_start(ErlDrvPort, char*); #include <locale.h> #include <unistd.h> #include <termios.h> +#ifdef HAVE_WCWIDTH +#include <wchar.h> +#endif #ifdef HAVE_FCNTL_H #include <fcntl.h> #endif @@ -95,6 +98,9 @@ static int lpos; /* The current "cursor position" in the line buf */ #define CONTROL_TAG 0x10000000U /* Control character, value in first position */ #define ESCAPED_TAG 0x01000000U /* Escaped character, value in first position */ +#ifdef HAVE_WCWIDTH +#define WIDE_TAG 0x02000000U /* Wide character, value in first position */ +#endif #define TAG_MASK 0xFF000000U #define MAXSIZE (1 << 16) @@ -597,12 +603,20 @@ static int check_buf_size(byte *s, int n) } if (utf8_mode) { /* That is, terminal is UTF8 compliant */ if (ch >= 128 || isprint(ch)) { - DEBUGLOG(("Printable(UTF-8:%d):%d",(pos - opos),ch)); - size++; /* Buffer contains wide characters... */ +#ifdef HAVE_WCWIDTH + int width; +#endif + DEBUGLOG(("Printable(UTF-8:%d):%d",pos,ch)); + size++; +#ifdef HAVE_WCWIDTH + if ((width = wcwidth(ch)) > 1) { + size += width - 1; + } +#endif } else if (ch == '\t') { size += 8; } else { - DEBUGLOG(("Magic(UTF-8:%d):%d",(pos - opos),ch)); + DEBUGLOG(("Magic(UTF-8:%d):%d",pos,ch)); size += 2; } } else { @@ -823,7 +837,7 @@ static int del_chars(int n) r = llen - lpos - l; /* Characters after deleted */ /* Fix up buffer and buffer pointers. */ if (r > 0) - memcpy(lbuf + lpos, lbuf + pos, r * sizeof(Uint32)); + memmove(lbuf + lpos, lbuf + pos, r * sizeof(Uint32)); llen -= l; /* Write out characters after, blank the tail and jump back to lpos. */ write_buf(lbuf + lpos, r); @@ -842,7 +856,7 @@ static int del_chars(int n) move_cursor(lpos, lpos-l); /* Move back */ /* Fix up buffer and buffer pointers. */ if (r > 0) - memcpy(lbuf + pos, lbuf + lpos, r * sizeof(Uint32)); + memmove(lbuf + pos, lbuf + lpos, r * sizeof(Uint32)); lpos -= l; llen -= l; /* Write out characters after, blank the tail and jump back to lpos. */ @@ -868,12 +882,22 @@ static int step_over_chars(int n) end = lbuf + llen; c = lbuf + lpos; for ( ; n > 0 && c < end; --n) { +#ifdef HAVE_WCWIDTH + while (*c & WIDE_TAG) { + c++; + } +#endif c++; while (c < end && (*c & TAG_MASK) && ((*c & ~TAG_MASK) == 0)) c++; } for ( ; n < 0 && c > beg; n++) { --c; +#ifdef HAVE_WCWIDTH + while (c > beg + 1 && (c[-1] & WIDE_TAG)) { + --c; + } +#endif while (c > beg && (*c & TAG_MASK) && ((*c & ~TAG_MASK) == 0)) --c; } @@ -899,6 +923,15 @@ static int insert_buf(byte *s, int n) ++pos; } if ((utf8_mode && (ch >= 128 || isprint(ch))) || (ch <= 255 && isprint(ch))) { +#ifdef HAVE_WCWIDTH + int width; + if ((width = wcwidth(ch)) > 1) { + while (--width) { + DEBUGLOG(("insert_buf: Wide(UTF-8):%d,%d",width,ch)); + lbuf[lpos++] = (WIDE_TAG | ((Uint32) ch)); + } + } +#endif DEBUGLOG(("insert_buf: Printable(UTF-8):%d",ch)); lbuf[lpos++] = (Uint32) ch; } else if (ch >= 128) { /* not utf8 mode */ @@ -1006,6 +1039,8 @@ static int write_buf(Uint32 *s, int n) if (octbuff != octtmp) { driver_free(octbuff); } + } else if (*s & WIDE_TAG) { + --n; s++; } else { DEBUGLOG(("Very unexpected character %d",(int) *s)); ++n; diff --git a/erts/emulator/drivers/win32/ttsl_drv.c b/erts/emulator/drivers/win32/ttsl_drv.c index 1a74d21e99..8b5e3eeefd 100644 --- a/erts/emulator/drivers/win32/ttsl_drv.c +++ b/erts/emulator/drivers/win32/ttsl_drv.c @@ -414,12 +414,12 @@ static int check_buf_size(byte *s, int n) } if (utf8_mode) { /* That is, terminal is UTF8 compliant */ if (ch >= 128 || isprint(ch)) { - DEBUGLOG(("Printable(UTF-8:%d):%d",(pos - opos),ch)); + DEBUGLOG(("Printable(UTF-8:%d):%d",pos,ch)); size++; /* Buffer contains wide characters... */ } else if (ch == '\t') { size += 8; } else { - DEBUGLOG(("Magic(UTF-8:%d):%d",(pos - opos),ch)); + DEBUGLOG(("Magic(UTF-8:%d):%d",pos,ch)); size += 2; } } else { diff --git a/erts/emulator/hipe/hipe_x86_signal.c b/erts/emulator/hipe/hipe_x86_signal.c index 64c0e0da3e..19fc448742 100644 --- a/erts/emulator/hipe/hipe_x86_signal.c +++ b/erts/emulator/hipe/hipe_x86_signal.c @@ -183,6 +183,7 @@ static void do_init(void) #include <dlfcn.h> static int (*__next_sigaction)(int, const struct sigaction*, struct sigaction*); #define init_done() (__next_sigaction != 0) +extern int _sigaction(int, const struct sigaction*, struct sigaction*); #define __SIGACTION _sigaction static void do_init(void) { diff --git a/erts/emulator/internal_doc/dec.erl b/erts/emulator/internal_doc/dec.erl index 0315f2a52d..255018abe0 100644 --- a/erts/emulator/internal_doc/dec.erl +++ b/erts/emulator/internal_doc/dec.erl @@ -1,3 +1,4 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% @@ -25,10 +26,10 @@ %% {RevList,Translation} %% Where 'RevList' is a reversed list of the denormalized repressentation of %% the character 'Translation'. An example would be the swedish character -%% '�', which would be represented in the file as: +%% 'ö', which would be represented in the file as: %% {[776,111],246}, as the denormalized representation of codepoint 246 %% is [111,776] (i.e an 'o' followed by the "double dot accent character 776), -%% while '�' instead is represented as {[776,97],228}, as the denormalized +%% while 'ä' instead is represented as {[776,97],228}, as the denormalized %% form would be [97,776] (same accent but an 'a' instead). %% The datafile is generated from the table on Apple's developer connection %% http://developer.apple.com/library/mac/#technotes/tn/tn1150table.html diff --git a/erts/emulator/sys/unix/sys_float.c b/erts/emulator/sys/unix/sys_float.c index 787f8d6728..689be98969 100644 --- a/erts/emulator/sys/unix/sys_float.c +++ b/erts/emulator/sys/unix/sys_float.c @@ -832,6 +832,8 @@ sys_chars_to_double(char* buf, double* fp) return 0; } +#ifdef USE_MATHERR + int matherr(struct exception *exc) { @@ -842,3 +844,5 @@ matherr(struct exception *exc) #endif return 1; } + +#endif diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c index e0422de026..922967c698 100755 --- a/erts/emulator/sys/win32/sys.c +++ b/erts/emulator/sys/win32/sys.c @@ -399,11 +399,12 @@ int* pBuild; /* Pointer to build number. */ * Definitions for driver flags. */ -#define DF_OVR_READY 1 /* Overlapped result is ready. */ -#define DF_EXIT_THREAD 2 /* The thread should exit. */ -#define DF_XLAT_CR 4 /* The thread should translate CRs. */ -#define DF_DROP_IF_INVH 8 /* Drop packages instead of crash if +#define DF_OVR_READY 1 /* Overlapped result is ready. */ +#define DF_EXIT_THREAD 2 /* The thread should exit. */ +#define DF_XLAT_CR 4 /* The thread should translate CRs. */ +#define DF_DROP_IF_INVH 8 /* Drop packages instead of crash if invalid handle (stderr) */ +#define DF_THREAD_FLUSHED 16 /* The thread should exit. */ #define OV_BUFFER_PTR(dp) ((LPVOID) ((dp)->ov.Internal)) #define OV_NUM_TO_READ(dp) ((dp)->ov.InternalHigh) @@ -2141,8 +2142,9 @@ threaded_writer(LPVOID param) for (;;) { handle = WaitForMultipleObjects(2, handles, FALSE, INFINITE); - if (aio->flags & DF_EXIT_THREAD) + if (aio->flags & DF_EXIT_THREAD) { break; + } buf = OV_BUFFER_PTR(aio); numToWrite = OV_NUM_TO_READ(aio); @@ -2150,6 +2152,7 @@ threaded_writer(LPVOID param) if (handle == (WAIT_OBJECT_0 + 1) && numToWrite == 0) { SetEvent(aio->flushReplyEvent); + aio->flags |= DF_THREAD_FLUSHED; continue; } @@ -2197,6 +2200,7 @@ threaded_writer(LPVOID param) if (aio->flags & DF_EXIT_THREAD) break; } + aio->flags |= DF_THREAD_FLUSHED; CloseHandle(aio->fd); aio->fd = INVALID_HANDLE_VALUE; unrefer_driver_data(aio->dp); @@ -2340,9 +2344,11 @@ static void fd_stop(ErlDrvData data) (void) driver_select(dp->port_num, (ErlDrvEvent)dp->out.ov.hEvent, ERL_DRV_WRITE, 0); - ASSERT(dp->out.flushEvent); - SetEvent(dp->out.flushEvent); - WaitForSingleObject(dp->out.flushReplyEvent, INFINITE); + do { + ASSERT(dp->out.flushEvent); + SetEvent(dp->out.flushEvent); + } while (WaitForSingleObject(dp->out.flushReplyEvent, 10) == WAIT_TIMEOUT + || !(dp->out.flags & DF_THREAD_FLUSHED)); } } @@ -2433,12 +2439,12 @@ threaded_exiter(LPVOID param) */ i = 0; if (dp->out.thread != (HANDLE) -1) { - dp->out.flags = DF_EXIT_THREAD; + dp->out.flags |= DF_EXIT_THREAD; SetEvent(dp->out.ioAllowed); handles[i++] = dp->out.thread; } if (dp->in.thread != (HANDLE) -1) { - dp->in.flags = DF_EXIT_THREAD; + dp->in.flags |= DF_EXIT_THREAD; SetEvent(dp->in.ioAllowed); handles[i++] = dp->in.thread; } diff --git a/erts/emulator/sys/win32/sys_float.c b/erts/emulator/sys/win32/sys_float.c index fb1ffc3089..e2a777d182 100644 --- a/erts/emulator/sys/win32/sys_float.c +++ b/erts/emulator/sys/win32/sys_float.c @@ -133,6 +133,8 @@ sys_double_to_chars_ext(double fp, char *buffer, size_t buffer_size, size_t deci return s-buffer; /* i.e strlen(buffer) */ } +#ifdef USE_MATHERR + int matherr(struct _exception *exc) { @@ -141,6 +143,8 @@ matherr(struct _exception *exc) return 1; } +#endif + static void fpe_exception(int sig) { diff --git a/erts/emulator/test/busy_port_SUITE.erl b/erts/emulator/test/busy_port_SUITE.erl index 2c63296b83..4b4af0babe 100644 --- a/erts/emulator/test/busy_port_SUITE.erl +++ b/erts/emulator/test/busy_port_SUITE.erl @@ -170,6 +170,7 @@ send_3(Config) when is_list(Config) -> ?line {Owner,Slave} = get_slave(), ?line ok = erlang:send(Slave, {Owner,{command,"set busy"}}, [nosuspend]), + receive after 100 -> ok end, % ensure command reached port ?line nosuspend = erlang:send(Slave, {Owner,{command,"busy"}}, [nosuspend]), ?line unlock_slave(), @@ -563,6 +564,7 @@ scheduling_delay_busy_nosuspend(Config) -> {2,{call,[{var,1},open_port]}}, {0,{cast,[{var,1},{command,1,100}]}}, {0,{cast,[{var,1},{busy,2}]}}, + {0,{timer,sleep,[200]}}, % ensure reached port {10,{call,[{var,1},{command,3,[nosuspend]}]}}, {0,{timer,sleep,[200]}}, {0,{erlang,port_command,[{var,2},<<$N>>,[force]]}}, diff --git a/erts/emulator/test/code_parallel_load_SUITE.erl b/erts/emulator/test/code_parallel_load_SUITE.erl index aa9e4c96c6..d2c80c1ca0 100644 --- a/erts/emulator/test/code_parallel_load_SUITE.erl +++ b/erts/emulator/test/code_parallel_load_SUITE.erl @@ -16,7 +16,6 @@ %% %% %CopyrightEnd% %% -%% Author: Björn-Egil Dahlberg -module(code_parallel_load_SUITE). -export([ diff --git a/erts/etc/unix/cerl.src b/erts/etc/unix/cerl.src index cc7d77fd9a..f99059cb72 100644 --- a/erts/etc/unix/cerl.src +++ b/erts/etc/unix/cerl.src @@ -171,6 +171,11 @@ while [ $# -gt 0 ]; do cargs="$cargs -debug" TYPE=.debug ;; + "-frmptr") + shift + cargs="$cargs -frmptr" + TYPE=.frmptr + ;; "-gdb") shift GDB=gdb diff --git a/erts/lib_src/Makefile.in b/erts/lib_src/Makefile.in index 0fc3ac6efc..4f0a5e5202 100644 --- a/erts/lib_src/Makefile.in +++ b/erts/lib_src/Makefile.in @@ -86,6 +86,12 @@ CFLAGS += -DERTS_ENABLE_LOCK_COUNT OMIT_FP=true PRE_LD= else +ifeq ($(TYPE),frmptr) +TYPE_SUFFIX = .frmptr +CFLAGS += -DERTS_FRMPTR +OMIT_OMIT_FP=yes +PRE_LD= +else override TYPE=opt OMIT_FP=true TYPE_SUFFIX= @@ -98,6 +104,7 @@ endif endif endif endif +endif OPSYS=@OPSYS@ sol2CFLAGS= @@ -110,6 +117,7 @@ ultrasparcCFLAGS=-Wa,-xarch=v8plusa ARCHCFLAGS=$($(ARCH)CFLAGS) ifeq ($(OMIT_OMIT_FP),yes) +CFLAGS += -fno-omit-frame-pointer OMIT_FP=false endif diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam Binary files differindex 29fb8aaebf..308cb99be5 100644 --- a/erts/preloaded/ebin/erlang.beam +++ b/erts/preloaded/ebin/erlang.beam diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam Binary files differindex c907ead38a..1b47509a53 100644 --- a/erts/preloaded/ebin/erts_internal.beam +++ b/erts/preloaded/ebin/erts_internal.beam diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam Binary files differindex 7b9da42e4f..43cbc17dbf 100644 --- a/erts/preloaded/ebin/prim_inet.beam +++ b/erts/preloaded/ebin/prim_inet.beam diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index fefe0f21e0..7106c0a4fb 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2086,7 +2086,7 @@ tuple_to_list(_Tuple) -> ({allocator_sizes, Alloc}) -> [_] when %% More or less anything Alloc :: atom(); (build_type) -> opt | debug | purify | quantify | purecov | - gcov | valgrind | gprof | lcnt; + gcov | valgrind | gprof | lcnt | frmptr; (c_compiler_used) -> {atom(), term()}; (check_io) -> [_]; (compat_rel) -> integer(); @@ -2705,26 +2705,14 @@ port_info(Port, Item) -> Port :: port() | atom(), Data :: term(). -port_set_data(Port, Data) -> - case case erts_internal:port_set_data(Port, Data) of - Ref when erlang:is_reference(Ref) -> receive {Ref, Res} -> Res end; - Res -> Res - end of - badarg -> erlang:error(badarg, [Port, Data]); - Result -> Result - end. +port_set_data(_Port, _Data) -> + erlang:nif_error(undefined). -spec erlang:port_get_data(Port) -> term() when Port :: port() | atom(). -port_get_data(Port) -> - case case erts_internal:port_get_data(Port) of - Ref when erlang:is_reference(Ref) -> receive {Ref, Res} -> Res end; - Res -> Res - end of - {ok, Data} -> Data; - Error -> erlang:error(Error, [Port]) - end. +port_get_data(_Port) -> + erlang:nif_error(undefined). %% %% If the emulator wants to perform a distributed command and diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index f1c83f4518..507bc3afb9 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -31,8 +31,7 @@ -export([await_port_send_result/3]). -export([port_command/3, port_connect/2, port_close/1, - port_control/3, port_call/3, port_set_data/2, port_get_data/1, - port_info/1, port_info/2]). + port_control/3, port_call/3, port_info/1, port_info/2]). %% %% Await result of send to port @@ -86,19 +85,6 @@ port_control(_Port, _Operation, _Data) -> port_call(_Port, _Operation, _Data) -> erlang:nif_error(undefined). --spec erts_internal:port_get_data(P1) -> Result when - P1 :: port() | atom(), - Result :: {ok, term()} | reference() | badarg. -port_get_data(_P1) -> - erlang:nif_error(undefined). - --spec erts_internal:port_set_data(P1, P2) -> Result when - P1 :: port() | atom(), - P2 :: term(), - Result :: true | reference() | badarg. -port_set_data(_P1, _P2) -> - erlang:nif_error(undefined). - -type port_info_1_result_item() :: {registered_name, RegName :: atom()} | {id, Index :: non_neg_integer()} | diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index 21d23159f0..d0e70aa95c 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -1072,8 +1072,8 @@ enc_opt(deliver) -> ?INET_LOPT_DELIVER; enc_opt(exit_on_close) -> ?INET_LOPT_EXITONCLOSE; enc_opt(high_watermark) -> ?INET_LOPT_TCP_HIWTRMRK; enc_opt(low_watermark) -> ?INET_LOPT_TCP_LOWTRMRK; -enc_opt(high_msgq_watermark) -> ?INET_LOPT_TCP_MSGQ_HIWTRMRK; -enc_opt(low_msgq_watermark) -> ?INET_LOPT_TCP_MSGQ_LOWTRMRK; +enc_opt(high_msgq_watermark) -> ?INET_LOPT_MSGQ_HIWTRMRK; +enc_opt(low_msgq_watermark) -> ?INET_LOPT_MSGQ_LOWTRMRK; enc_opt(send_timeout) -> ?INET_LOPT_TCP_SEND_TIMEOUT; enc_opt(send_timeout_close) -> ?INET_LOPT_TCP_SEND_TIMEOUT_CLOSE; enc_opt(delay_send) -> ?INET_LOPT_TCP_DELAY_SEND; @@ -1128,8 +1128,8 @@ dec_opt(?INET_LOPT_DELIVER) -> deliver; dec_opt(?INET_LOPT_EXITONCLOSE) -> exit_on_close; dec_opt(?INET_LOPT_TCP_HIWTRMRK) -> high_watermark; dec_opt(?INET_LOPT_TCP_LOWTRMRK) -> low_watermark; -dec_opt(?INET_LOPT_TCP_MSGQ_HIWTRMRK) -> high_msgq_watermark; -dec_opt(?INET_LOPT_TCP_MSGQ_LOWTRMRK) -> low_msgq_watermark; +dec_opt(?INET_LOPT_MSGQ_HIWTRMRK) -> high_msgq_watermark; +dec_opt(?INET_LOPT_MSGQ_LOWTRMRK) -> low_msgq_watermark; dec_opt(?INET_LOPT_TCP_SEND_TIMEOUT) -> send_timeout; dec_opt(?INET_LOPT_TCP_SEND_TIMEOUT_CLOSE) -> send_timeout_close; dec_opt(?INET_LOPT_TCP_DELAY_SEND) -> delay_send; diff --git a/lib/asn1/doc/src/notes.xml b/lib/asn1/doc/src/notes.xml index e619408591..76d605569d 100644 --- a/lib/asn1/doc/src/notes.xml +++ b/lib/asn1/doc/src/notes.xml @@ -31,6 +31,23 @@ <p>This document describes the changes made to the asn1 application.</p> +<section><title>Asn1 2.0.1.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + When an object set is an actual parameter, the extension + marker for the object set could get lost (which would + cause the decoding of unknown values to fail).</p> + <p> + Own Id: OTP-10995 Aux Id: seq12290 </p> + </item> + </list> + </section> + +</section> + <section><title>Asn1 2.0.1.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl index e1911d8170..0622998445 100644 --- a/lib/asn1/src/asn1ct_check.erl +++ b/lib/asn1/src/asn1ct_check.erl @@ -1025,8 +1025,8 @@ prepare_objset({{'SingleValue',Set},Ext}) -> %% {set,lists:append([Set,Ext]),true}; prepare_objset({Set,Ext}) when is_list(Set) -> {set,merge_sets(Set,Ext),true}; -prepare_objset({ObjDef={object,definedsyntax,_ObjFields},_Ext}) -> - {set,[ObjDef],true}; +prepare_objset({{object,definedsyntax,_ObjFields}=Set,Ext}) -> + {set,merge_sets(Set, Ext),true}; prepare_objset(ObjDef={object,definedsyntax,_ObjFields}) -> {set,[ObjDef],false}; prepare_objset({ObjDef=#type{},Ext}) when is_list(Ext) -> @@ -4034,8 +4034,8 @@ categorize(S,value,Type,Value) -> [#valuedef{type=Type,value=Value,module=S#state.mname}]. -parse_objectset({valueset,T=#type{}}) -> - [T]; +parse_objectset({valueset,#type{def=#'Externaltypereference'{}=Ref}}) -> + Ref; parse_objectset({valueset,Set}) -> Set; parse_objectset(#type{def=Ref}) when is_record(Ref,'Externaltypereference') -> diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl index 62418e554e..8deabece37 100644 --- a/lib/asn1/test/asn1_SUITE.erl +++ b/lib/asn1/test/asn1_SUITE.erl @@ -829,8 +829,9 @@ testInfObjectClass(Config, Rule, Opts) -> testParameterizedInfObj(Config) -> test(Config, fun testParameterizedInfObj/3). testParameterizedInfObj(Config, Rule, Opts) -> - asn1_test_lib:compile("Param", Config, [Rule|Opts]), - testParameterizedInfObj:main(Rule). + Files = ["Param","Param2"], + asn1_test_lib:compile_all(Files, Config, [Rule|Opts]), + testParameterizedInfObj:main(Config, Rule). testMergeCompile(Config) -> test(Config, fun testMergeCompile/3). testMergeCompile(Config, Rule, Opts) -> diff --git a/lib/asn1/test/asn1_SUITE_data/Param2.asn1 b/lib/asn1/test/asn1_SUITE_data/Param2.asn1 new file mode 100644 index 0000000000..09ccb367d8 --- /dev/null +++ b/lib/asn1/test/asn1_SUITE_data/Param2.asn1 @@ -0,0 +1,48 @@ +Param2 DEFINITIONS AUTOMATIC TAGS ::= +BEGIN + + S1AP-PROTOCOL-IES ::= CLASS { + &id INTEGER UNIQUE, + &Value + } + WITH SYNTAX { + ID &id + TYPE &Value + } + + ProtocolIE-Field {S1AP-PROTOCOL-IES : IEsSetParam} ::= SEQUENCE { + id S1AP-PROTOCOL-IES.&id ({IEsSetParam}), + value S1AP-PROTOCOL-IES.&Value ({IEsSetParam}{@id}) + } + + ProtocolIE-Container {S1AP-PROTOCOL-IES : IEsSetParam} ::= + SEQUENCE (SIZE (0..10)) OF ProtocolIE-Field {{IEsSetParam}} + + HandoverRequired ::= SEQUENCE { + protocolIEs ProtocolIE-Container { { HandoverRequiredIEs } }, + ... + } + + HandoverRequiredIEs S1AP-PROTOCOL-IES ::= { + { ID 1 TYPE OCTET STRING } | + { ID 2 TYPE INTEGER }, +--Delete-start + ..., + { ID 100 TYPE INTEGER (0..1023) } | + { ID 101 TYPE ENUMERATED {true,false} } +--Delete-end + } + + SingleRoot ::= SEQUENCE { + protocolIEs ProtocolIE-Container { { SingleRootIEs } }, + ... + } + + -- The extension was lost when there was a single root item. + SingleRootIEs S1AP-PROTOCOL-IES ::= { + { ID 1 TYPE OCTET STRING }, + ..., + { ID 2 TYPE INTEGER } + } + +END diff --git a/lib/asn1/test/testParameterizedInfObj.erl b/lib/asn1/test/testParameterizedInfObj.erl index 17108e285b..212df79fd4 100644 --- a/lib/asn1/test/testParameterizedInfObj.erl +++ b/lib/asn1/test/testParameterizedInfObj.erl @@ -20,7 +20,7 @@ -module(testParameterizedInfObj). --export([main/1,ranap/1]). +-export([main/2,ranap/1]). -include_lib("test_server/include/test_server.hrl"). @@ -31,7 +31,11 @@ -record('Iu-ReleaseCommand',{protocolIEs,protocolExtensions}). -main(Erule) -> +main(Config, Erule) -> + param(Erule), + param2(Config, Erule). + +param(Erule) -> PERVal = #'AllocationOrRetentionPriority' {priorityLevel = true, iE_Extensions = @@ -84,7 +88,6 @@ main(Erule) -> ok. - ranap(_Erule) -> PIEVal2 = [{'ProtocolIE-Field',4,ignore,{radioNetwork,'rab-pre-empted'}}], ?line Val2 = @@ -102,3 +105,51 @@ open_type(uper,Val) when is_list(Val) -> list_to_binary(Val); open_type(_,Val) -> Val. + +param2(Config, Erule) -> + roundtrip2('HandoverRequired', + {'HandoverRequired', + [{'ProtocolIE-Field',1,"ABC"}, + {'ProtocolIE-Field',2,577799}]}), + Enc = roundtrip2('HandoverRequired', + {'HandoverRequired', + [{'ProtocolIE-Field',1,"ABC"}, + {'ProtocolIE-Field',2,-42}, + {'ProtocolIE-Field',100,533}, + {'ProtocolIE-Field',101,true}]}), + + %% Now remove the data after the extension mark in the object set. + DataDir = ?config(data_dir, Config), + CaseDir = ?config(case_dir, Config), + Asn1SrcBase = "Param2.asn1", + Asn1SrcFile0 = filename:join(DataDir, Asn1SrcBase), + {ok,Src0} = file:read_file(Asn1SrcFile0), + Src = re:replace(Src0, "--Delete-start.*?--Delete-end", "...\n", + [dotall,global,{return,binary}]), + io:format("~s\n\n", [Src]), + + Asn1SrcFile = filename:join(CaseDir, Asn1SrcBase), + ok = file:write_file(Asn1SrcFile, Src), + ok = asn1ct:compile(Asn1SrcFile, + [{i,DataDir},{outdir,CaseDir},Erule]), + + %% Decompile extended data. + {ok,{'HandoverRequired',[{'ProtocolIE-Field',1,"ABC"}, + {'ProtocolIE-Field',2,-42}, + {'ProtocolIE-Field',100,Open100}, + {'ProtocolIE-Field',101,Open101}]}} = + asn1_wrapper:decode('Param2', 'HandoverRequired', Enc), + true = is_binary(Open100), + true = is_binary(Open101), + + %% Test single root. + roundtrip2('SingleRoot', + {'SingleRoot',[{'ProtocolIE-Field',1,"ABC"}, + {'ProtocolIE-Field',2,9999}]}), + ok. + + +roundtrip2(T, V) -> + {ok,Enc} = asn1_wrapper:encode('Param2', T, V), + {ok,V} = asn1_wrapper:decode('Param2', T, Enc), + Enc. diff --git a/lib/asn1/vsn.mk b/lib/asn1/vsn.mk index 3c4f3ff122..9245f83280 100644 --- a/lib/asn1/vsn.mk +++ b/lib/asn1/vsn.mk @@ -1,2 +1,2 @@ #next version number to use is 2.0 -ASN1_VSN = 2.0.1.1 +ASN1_VSN = 2.0.1.2 diff --git a/lib/common_test/priv/ct_default.css b/lib/common_test/priv/ct_default.css index 1188f8f676..ff48b4fdc0 100644 --- a/lib/common_test/priv/ct_default.css +++ b/lib/common_test/priv/ct_default.css @@ -96,6 +96,14 @@ div.ct_error_notify { margin: .2em 0 0 0; } +div.ct_error_notify a:link { + color: #D0D0D0; +} + +div.ct_error_notify a:visited { + color: #AAAAAA; +} + div.default { background: lightgreen; color: black; font-family: "Monaco", "Andale Mono", "Consolas", monospace; diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl index b92fe1555f..276f902b05 100644 --- a/lib/common_test/src/ct_framework.erl +++ b/lib/common_test/src/ct_framework.erl @@ -32,6 +32,7 @@ -export([error_in_suite/1, init_per_suite/1, end_per_suite/1, init_per_group/2, end_per_group/2]). +-include("ct.hrl"). -include("ct_event.hrl"). -include("ct_util.hrl"). @@ -806,8 +807,14 @@ error_notification(Mod,Func,_Args,{Error,Loc}) -> "- - - - - - - - - -~n", io:format(user, lists:concat([Div,ErrFormat,Div,"~n"]), ErrArgs), - ct_logs:tc_log(ct_error_notify, "CT Error Notification", - ErrFormat, ErrArgs) + Link = + "\n\n<a href=\"#end\">" + "Full error description and stacktrace" + "</a>", + ct_logs:tc_log(ct_error_notify, + ?MAX_IMPORTANCE, + "CT Error Notification", + ErrFormat++Link, ErrArgs) end, case Loc of [{?MODULE,error_in_suite}] -> diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl index 752033fdff..f5355bfefe 100644 --- a/lib/common_test/src/ct_logs.erl +++ b/lib/common_test/src/ct_logs.erl @@ -41,7 +41,8 @@ -export([uri/1]). %% Logging stuff directly from testcase --export([tc_log/3, tc_log/4, tc_log_async/3, tc_print/3, tc_print/4, +-export([tc_log/3, tc_log/4, tc_log/5, tc_log_async/3, tc_log_async/5, + tc_print/3, tc_print/4, tc_pal/3, tc_pal/4, ct_log/3, basic_html/0]). %% Simulate logger process for use without ct environment running @@ -59,6 +60,7 @@ -define(all_runs_name, "all_runs.html"). -define(index_name, "index.html"). -define(totals_name, "totals.info"). +-define(log_cache_name, "ct_log_cache"). -define(table_color1,"#ADD8E6"). -define(table_color2,"#E4F0FE"). @@ -68,6 +70,10 @@ -define(abs(Name), filename:absname(Name)). +-record(log_cache, {version, + all_runs = [], + tests = []}). + %%%----------------------------------------------------------------- %%% @spec init(Mode) -> Result %%% Mode = normal | interactive @@ -93,14 +99,25 @@ init(Mode, Verbosity) -> exit({could_not_start_process,?MODULE,Reason}) end. -make_dirname({{YY,MM,DD},{H,M,S}}) -> - io_lib:format(logdir_node_prefix()++".~w-~2.2.0w-~2.2.0w_~2.2.0w.~2.2.0w.~2.2.0w", - [YY,MM,DD,H,M,S]). - +date2str({{YY,MM,DD},{H,M,S}}) -> + lists:flatten(io_lib:format("~w-~2.2.0w-~2.2.0w_~2.2.0w.~2.2.0w.~2.2.0w", + [YY,MM,DD,H,M,S])). logdir_prefix() -> "ct_run". logdir_node_prefix() -> - logdir_prefix()++"."++atom_to_list(node()). + logdir_prefix() ++ "." ++ atom_to_list(node()). + +make_dirname(DateTime) -> + logdir_node_prefix() ++ "." ++ date2str(DateTime). + +datestr_from_dirname([Y1,Y2,Y3,Y4,$-,Mo1,Mo2,$-,D1,D2,$_, + H1,H2,$.,M1,M2,$.,S1,S2 | _]) -> + [Y1,Y2,Y3,Y4,$-,Mo1,Mo2,$-,D1,D2,$_, + H1,H2,$.,M1,M2,$.,S1,S2]; +datestr_from_dirname([_Ch | Rest]) -> + datestr_from_dirname(Rest); +datestr_from_dirname([]) -> + "". %%%----------------------------------------------------------------- %%% @spec close(Info, StartDir) -> ok @@ -108,8 +125,21 @@ logdir_node_prefix() -> %%% @doc Create index pages with test results and close the CT Log %%% (tool-internal use only). close(Info, StartDir) -> - make_last_run_index(), - + %% close executes on the ct_util process, not on the logger process + %% so we need to use a local copy of the log cache data + LogCacheBin = make_last_run_index(), + put(ct_log_cache,LogCacheBin), + Cache2File = fun() -> + case get(ct_log_cache) of + undefined -> + ok; + CacheBin -> + %% save final version of the log cache to file + file:write_file(?log_cache_name,CacheBin), + put(ct_log_cache,undefined) + end + end, + ct_event:notify(#event{name=stop_logging,node=node(),data=[]}), case whereis(?MODULE) of @@ -132,11 +162,13 @@ close(Info, StartDir) -> io:format("Warning! Cleanup failed: ~p~n", [Error]) end, make_all_suites_index(stop), - make_all_runs_index(stop); + make_all_runs_index(stop), + Cache2File(); true -> file:set_cwd(".."), make_all_suites_index(stop), make_all_runs_index(stop), + Cache2File(), case ct_util:get_profile_data(browser, StartDir) of undefined -> ok; @@ -168,12 +200,19 @@ clear_stylesheet(TC) -> %%%----------------------------------------------------------------- %%% @spec get_log_dir() -> {ok,Dir} | {error,Reason} get_log_dir() -> - call({get_log_dir,false}). + get_log_dir(false). %%%----------------------------------------------------------------- %%% @spec get_log_dir(ReturnAbsName) -> {ok,Dir} | {error,Reason} get_log_dir(ReturnAbsName) -> - call({get_log_dir,ReturnAbsName}). + case call({get_log_dir,ReturnAbsName}) of + {error,does_not_exist} when ReturnAbsName == true -> + {ok,filename:absname(".")}; + {error,does_not_exist} -> + {ok,"."}; + Result -> + Result + end. %%%----------------------------------------------------------------- %%% make_last_run_index() -> ok @@ -333,8 +372,15 @@ tc_log(Category,Format,Args) -> %%%----------------------------------------------------------------- %%% @spec tc_log(Category,Importance,Format,Args) -> ok +%%% @equiv tc_log(Category,Importance,"User",Format,Args) +tc_log(Category,Importance,Format,Args) -> + tc_log(Category,Importance,"User",Format,Args). + +%%%----------------------------------------------------------------- +%%% @spec tc_log(Category,Importance,Printer,Format,Args) -> ok %%% Category = atom() %%% Importance = integer() +%%% Printer = string() %%% Format = string() %%% Args = list() %%% @@ -343,9 +389,6 @@ tc_log(Category,Format,Args) -> %%% <p>This function is called by <code>ct</code> when logging %%% stuff directly from a testcase (i.e. not from within the CT %%% framework).</p> -tc_log(Category,Importance,Format,Args) -> - tc_log(Category,Importance,"User",Format,Args). - tc_log(Category,Importance,Printer,Format,Args) -> cast({log,sync,self(),group_leader(),Category,Importance, [{div_header(Category,Printer),[]}, @@ -355,14 +398,15 @@ tc_log(Category,Importance,Printer,Format,Args) -> %%%----------------------------------------------------------------- %%% @spec tc_log_async(Category,Format,Args) -> ok -%%% @equiv tc_log_async(Category,?STD_IMPORTANCE,Format,Args) +%%% @equiv tc_log_async(Category,?STD_IMPORTANCE,"User",Format,Args) tc_log_async(Category,Format,Args) -> - tc_log_async(Category,?STD_IMPORTANCE,Format,Args). + tc_log_async(Category,?STD_IMPORTANCE,"User",Format,Args). %%%----------------------------------------------------------------- %%% @spec tc_log_async(Category,Importance,Format,Args) -> ok %%% Category = atom() %%% Importance = integer() +%%% Printer = string() %%% Format = string() %%% Args = list() %%% @@ -373,9 +417,9 @@ tc_log_async(Category,Format,Args) -> %%% to avoid deadlocks when e.g. the hook that handles SASL printouts %%% prints to the test case log file at the same time test server %%% asks ct_logs for an html wrapper.</p> -tc_log_async(Category,Importance,Format,Args) -> +tc_log_async(Category,Importance,Printer,Format,Args) -> cast({log,async,self(),group_leader(),Category,Importance, - [{div_header(Category),[]}, + [{div_header(Category,Printer),[]}, {Format,Args}, {div_footer(),[]}]}), ok. @@ -515,7 +559,6 @@ log_timestamp({MS,S,US}) -> logger(Parent, Mode, Verbosity) -> register(?MODULE,self()), - %%! Below is a temporary workaround for the limitation of %%! max one test run per second. %%! ---> @@ -561,9 +604,10 @@ logger(Parent, Mode, Verbosity) -> ok -> case copy_priv_files(PrivFilesSrc, PrivFilesDestRun) of {error,Src2,Dest2,Reason2} -> - io:format(user, "ERROR! "++ - "Priv file ~p could not be copied to ~p. "++ - "Reason: ~p~n", + io:format(user, + "ERROR! "++ + "Priv file ~p could not be copied to ~p. " + ++"Reason: ~p~n", [Src2,Dest2,Reason2]), exit({priv_file_error,Dest2}); ok -> @@ -687,7 +731,7 @@ logger_loop(State) -> logger_loop(State); {make_last_run_index,From} -> make_last_run_index(State#logger_state.start_time), - return(From,filename:basename(State#logger_state.log_dir)), + return(From,get(ct_log_cache)), logger_loop(State); {set_stylesheet,_,SSFile} when State#logger_state.stylesheet == SSFile -> @@ -946,40 +990,37 @@ print_style_error(Fd,StyleSheet,Reason) -> print_style(Fd,undefined). close_ctlog(Fd) -> - io:format(Fd,"\n</pre>\n",[]), - io:format(Fd,footer(),[]), + io:format(Fd, "\n</pre>\n", []), + io:format(Fd, [xhtml("<br><br>\n", "<br /><br />\n") | footer()], []), file:close(Fd). - %%%----------------------------------------------------------------- %%% Make an index page for the last run make_last_run_index(StartTime) -> IndexName = ?index_name, AbsIndexName = ?abs(IndexName), - case catch make_last_run_index1(StartTime,IndexName) of - {'EXIT', Reason} -> - io:put_chars("CRASHED while updating " ++ AbsIndexName ++ "!\n"), - io:format("~p~n", [Reason]), - {error, Reason}; - {error, Reason} -> - io:put_chars("FAILED while updating " ++ AbsIndexName ++ "\n"), - io:format("~p~n", [Reason]), - {error, Reason}; - ok -> -% io:put_chars("done\n"), - ok; - Err -> - io:format("Unknown internal error while updating ~ts. " - "Please report.\n(Err: ~p, ID: 1)", - [AbsIndexName,Err]), - {error, Err} - end. + Result = + case catch make_last_run_index1(StartTime,IndexName) of + {'EXIT', Reason} -> + io:put_chars("CRASHED while updating " ++ AbsIndexName ++ "!\n"), + io:format("~p~n", [Reason]), + {error, Reason}; + {error, Reason} -> + io:put_chars("FAILED while updating " ++ AbsIndexName ++ "\n"), + io:format("~p~n", [Reason]), + {error, Reason}; + ok -> + ok; + Err -> + io:format("Unknown internal error while updating ~ts. " + "Please report.\n(Err: ~p, ID: 1)", + [AbsIndexName,Err]), + {error, Err} + end, + Result. make_last_run_index1(StartTime,IndexName) -> - %% this manoeuvre is to ensure the tests get logged - %% in correct order of time (the 1 sec resolution - %% of the dirnames may be too big) Logs1 = case filelib:wildcard([$*|?logdir_ext]) of [Log] -> % first test @@ -1007,7 +1048,8 @@ make_last_run_index1(StartTime,IndexName) -> 0, 0, 0, 0, 0, Missing), %% write current Totals to file, later to be used in all_runs log write_totals_file(?totals_name,Label,Logs1,Totals), - Index = [Index0|index_footer()], + Index = [Index0|last_run_index_footer()], + case force_write_file(IndexName, unicode:characters_to_binary(Index)) of ok -> ok; @@ -1046,22 +1088,26 @@ make_last_run_index([Name|Rest], Result, TotSucc, TotFail, TotNotBuilt1, Missing) end; -make_last_run_index([], Result, TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt, _) -> - {ok, [Result|total_row(TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt, false)], +make_last_run_index([], Result, TotSucc, TotFail, UserSkip, AutoSkip, + TotNotBuilt, _) -> + {ok, [Result|total_row(TotSucc, TotFail, UserSkip, AutoSkip, + TotNotBuilt, false)], {TotSucc,TotFail,UserSkip,AutoSkip,TotNotBuilt}}. make_last_run_index1(SuiteName, [LogDir | LogDirs], Result, TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt, Missing) -> - case make_one_index_entry(SuiteName, LogDir, "-", false, Missing) of - {Result1,Succ,Fail,USkip,ASkip,NotBuilt} -> + case make_one_index_entry(SuiteName, LogDir, "-", false, + Missing, undefined) of + {Result1,Succ,Fail,USkip,ASkip,NotBuilt,_URIs1} -> %% for backwards compatibility AutoSkip1 = case catch AutoSkip+ASkip of {'EXIT',_} -> undefined; Res -> Res end, - make_last_run_index1(SuiteName, LogDirs, [Result|Result1], TotSucc+Succ, - TotFail+Fail, UserSkip+USkip, AutoSkip1, - TotNotBuilt+NotBuilt, Missing); + make_last_run_index1(SuiteName, LogDirs, [Result|Result1], + TotSucc+Succ, + TotFail+Fail, UserSkip+USkip, AutoSkip1, + TotNotBuilt+NotBuilt, Missing); error -> make_last_run_index1(SuiteName, LogDirs, Result, TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt, Missing) @@ -1070,35 +1116,49 @@ make_last_run_index1(_, [], Result, TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt, _) -> {Result,TotSucc,TotFail,UserSkip,AutoSkip,TotNotBuilt}. -make_one_index_entry(SuiteName, LogDir, Label, All, Missing) -> +make_one_index_entry(SuiteName, LogDir, Label, All, Missing, URIs) -> case count_cases(LogDir) of {Succ,Fail,UserSkip,AutoSkip} -> NotBuilt = not_built(SuiteName, LogDir, All, Missing), - NewResult = make_one_index_entry1(SuiteName, LogDir, Label, Succ, Fail, - UserSkip, AutoSkip, NotBuilt, All, - normal), - {NewResult,Succ,Fail,UserSkip,AutoSkip,NotBuilt}; + {NewResult,URIs1} = make_one_index_entry1(SuiteName, LogDir, Label, + Succ, Fail, + UserSkip, AutoSkip, + NotBuilt, All, + normal, URIs), + {NewResult,Succ,Fail,UserSkip,AutoSkip,NotBuilt,URIs1}; error -> error end. make_one_index_entry1(SuiteName, Link, Label, Success, Fail, UserSkip, AutoSkip, - NotBuilt, All, Mode) -> + NotBuilt, All, Mode, URIs) -> LogFile = filename:join(Link, ?suitelog_name ++ ".html"), + CtRunDir = filename:dirname(filename:dirname(Link)), + CrashDumpName = SuiteName ++ "_erl_crash.dump", + + URIs1 = {CtRunLogURI,LogFileURI,CrashDumpURI} = + case URIs of + undefined -> + {uri(filename:join(CtRunDir,?ct_log_name)), + uri(LogFile), + uri(CrashDumpName)}; + _ -> + URIs + end, + CrashDumpLink = case Mode of - cached -> + temp -> ""; normal -> - CrashDumpName = SuiteName ++ "_erl_crash.dump", case filelib:is_file(CrashDumpName) of true -> - [" <a href=\"", uri(CrashDumpName), + [" <a href=\"", CrashDumpURI, "\">(CrashDump)</a>"]; false -> "" end end, - CtRunDir = filename:dirname(filename:dirname(Link)), + {Lbl,Timestamp,Node,AllInfo} = case All of {true,OldRuns} -> @@ -1107,7 +1167,9 @@ make_one_index_entry1(SuiteName, Link, Label, Success, Fail, UserSkip, AutoSkip, 0 -> "-"; _ -> NodeOrDate end, + TS = timestamp(CtRunDir), + N = xhtml(["<td align=right><font size=\"-1\">",Node1, "</font></td>\n"], ["<td align=right>",Node1,"</td>\n"]), @@ -1116,28 +1178,31 @@ make_one_index_entry1(SuiteName, Link, Label, Success, Fail, UserSkip, AutoSkip, ["<td align=center><b>",Label,"</b></td>\n"]), T = xhtml(["<td><font size=\"-1\">",TS,"</font></td>\n"], ["<td>",TS,"</td>\n"]), - CtLogFile = filename:join(CtRunDir,?ct_log_name), + OldRunsLink = case OldRuns of [] -> "none"; _ -> "<a href=\""++?all_runs_name++"\">Old Runs</a>" end, - A = xhtml(["<td><font size=\"-1\"><a href=\"",uri(CtLogFile), + + A = xhtml(["<td><font size=\"-1\"><a href=\"",CtRunLogURI, "\">CT Log</a></font></td>\n", - "<td><font size=\"-1\">",OldRunsLink,"</font></td>\n"], - ["<td><a href=\"",uri(CtLogFile),"\">CT Log</a></td>\n", + "<td><font size=\"-1\">",OldRunsLink, + "</font></td>\n"], + ["<td><a href=\"",CtRunLogURI, + "\">CT Log</a></td>\n", "<td>",OldRunsLink,"</td>\n"]), {L,T,N,A}; false -> {"","","",""} end, + NotBuiltStr = if NotBuilt == 0 -> ["<td align=right>",integer_to_list(NotBuilt),"</td>\n"]; true -> - ["<td align=right><a href=\"", - uri(filename:join(CtRunDir,?ct_log_name)),"\">", - integer_to_list(NotBuilt),"</a></td>\n"] + ["<td align=right><a href=\"",CtRunLogURI,"\">", + integer_to_list(NotBuilt),"</a></td>\n"] end, FailStr = if Fail > 0 -> @@ -1156,17 +1221,17 @@ make_one_index_entry1(SuiteName, Link, Label, Success, Fail, UserSkip, AutoSkip, end, {UserSkip+AutoSkip,integer_to_list(UserSkip),ASStr} end, - [xhtml("<tr valign=top>\n", - ["<tr class=\"",odd_or_even(),"\">\n"]), - xhtml("<td><font size=\"-1\"><a href=\"", "<td><a href=\""), - uri(LogFile),"\">",SuiteName,"</a>", CrashDumpLink, - xhtml("</font></td>\n", "</td>\n"), - Lbl, Timestamp, - "<td align=right>",integer_to_list(Success),"</td>\n", - "<td align=right>",FailStr,"</td>\n", - "<td align=right>",integer_to_list(AllSkip), - " (",UserSkipStr,"/",AutoSkipStr,")</td>\n", - NotBuiltStr, Node, AllInfo, "</tr>\n"]. + {[xhtml("<tr valign=top>\n", + ["<tr class=\"",odd_or_even(),"\">\n"]), + xhtml("<td><font size=\"-1\"><a href=\"", "<td><a href=\""), + LogFileURI,"\">",SuiteName,"</a>", CrashDumpLink, + xhtml("</font></td>\n", "</td>\n"), + Lbl, Timestamp, + "<td align=right>",integer_to_list(Success),"</td>\n", + "<td align=right>",FailStr,"</td>\n", + "<td align=right>",integer_to_list(AllSkip), + " (",UserSkipStr,"/",AutoSkipStr,")</td>\n", + NotBuiltStr, Node, AllInfo, "</tr>\n"], URIs1}. total_row(Success, Fail, UserSkip, AutoSkip, NotBuilt, All) -> {Label,TimestampCell,AllInfo} = @@ -1392,17 +1457,30 @@ header1(Title, SubTitle, TableCols) -> "</center>\n", SubTitleHTML,"\n"]. -index_footer() -> - ["</table>\n" +last_run_index_footer() -> + AllRuns = filename:join("../",?all_runs_name), + TestIndex = filename:join("../",?index_name), + ["</table>\n", + xhtml("<br><hr><p>\n", "<br /><hr /><p>\n"), + "<a href=\"", uri(AllRuns), + "\">Test run history\n</a> | ", + "<a href=\"", uri(TestIndex), + "\">Top level test index\n</a>\n</p>\n", "</center>\n" | footer()]. +all_suites_index_footer() -> + ["</table>\n", + "</center>\n", + xhtml("<br><br>\n", "<br /><br />\n") | footer()]. + all_runs_index_footer() -> - ["</tbody>\n</table>\n" - "</center>\n" | footer()]. + ["</tbody>\n</table>\n", + "</center>\n", + xhtml("<br><br>\n", "<br /><br />\n") | footer()]. footer() -> ["<center>\n", - xhtml("<br><br>\n<hr>\n", "<br /><br />\n"), + xhtml("<hr>\n", ""), xhtml("<p><font size=\"-1\">\n", "<div class=\"copyright\">"), "Copyright © ", year(), " <a href=\"http://www.erlang.org\">Open Telecom Platform</a>", @@ -1414,7 +1492,6 @@ footer() -> "</body>\n" "</html>\n"]. - body_tag() -> CTPath = code:lib_dir(common_test), TileFile = filename:join(filename:join(CTPath,"priv"),"tile1.jpg"), @@ -1580,35 +1657,169 @@ make_all_runs_index(When) -> if When == start -> ok; true -> io:put_chars("Updating " ++ AbsName ++ "... ") end, + + %% check if log cache should be used, and if it exists + UseCache = + if When == refresh -> + save_only; + true -> + case application:get_env(common_test, disable_log_cache) of + {ok,true} -> + disabled; + _ -> + case get(ct_log_cache) of + undefined -> + file:read_file(?log_cache_name); + LogCacheBin -> + {ok,LogCacheBin} + end + end + end, + Dirs = filelib:wildcard(logdir_prefix()++"*.*"), DirsSorted = (catch sort_all_runs(Dirs)), - Header = all_runs_header(), - Index = [runentry(Dir) || Dir <- DirsSorted], - Result = file:write_file(AbsName, - unicode:characters_to_binary( - Header++Index++all_runs_index_footer())), + + LogCacheInfo = get_cache_data(UseCache), + + Result = + case LogCacheInfo of + {ok,LogCache} -> + %% use the log cache file to generate the index + make_all_runs_from_cache(AbsName,DirsSorted,LogCache); + + _WhyNot -> + %% no cache file exists (or feature has been disabled) + Header = all_runs_header(), + GetLogResult = + fun(Dir,{RunData,LogTxt}) -> + {Tot,XHTML,IxLink} = runentry(Dir, + undefined, + undefined), + {[{Dir,Tot,IxLink}|RunData],[XHTML|LogTxt]} + end, + {AllRunsData,Index} = + lists:foldr(GetLogResult,{[],[]},DirsSorted), + + %% update cache with result unless the cache is disabled + if UseCache == disabled -> ok; + true -> update_all_runs_in_cache(AllRunsData) + end, + %% write all_runs log file + ok = file:write_file(AbsName, + unicode:characters_to_binary( + Header++Index++ + all_runs_index_footer())) + end, + notify_and_unlock_file(AbsName), if When == start -> ok; true -> io:put_chars("done\n") end, - notify_and_unlock_file(AbsName), Result. +make_all_runs_from_cache(AbsName, Dirs, LogCache) -> + Header = all_runs_header(), + + %% Note that both Dirs and the cache is sorted! + AllRunsDirs = dir_diff_all_runs(Dirs, LogCache), + + GetLogResult = + fun({Dir,no_test_data,IxLink},{RunData,LogTxt}) -> + {Tot,XHTML,_} = runentry(Dir,undefined,IxLink), + {[{Dir,Tot,IxLink}|RunData],[XHTML|LogTxt]}; + ({Dir,CachedTotals,IxLink},{RunData,LogTxt}) -> + %% create log entry using cached data + {Tot,XHTML,_} = runentry(Dir,CachedTotals,IxLink), + {[{Dir,Tot,IxLink}|RunData],[XHTML|LogTxt]}; + (Dir,{RunData,LogTxt}) -> + %% create log entry from scratch + {Tot,XHTML,IxLink} = runentry(Dir,undefined,undefined), + {[{Dir,Tot,IxLink}|RunData],[XHTML|LogTxt]} + end, + {AllRunsData,Index} = lists:foldr(GetLogResult,{[],[]},AllRunsDirs), + %% update cache with result + update_all_runs_in_cache(AllRunsData,LogCache), + %% write all_runs log file + ok = file:write_file(AbsName, + unicode:characters_to_binary( + Header++Index++ + all_runs_index_footer())). + +update_all_runs_in_cache(AllRunsData) -> + case get(ct_log_cache) of + undefined -> + LogCache = #log_cache{version = cache_vsn(), + all_runs = AllRunsData}, + case {self(),whereis(?MODULE)} of + {_Pid,_Pid} -> + %% save the cache in RAM so it doesn't have to be + %% read from file as long as this logger process is alive + put(ct_log_cache,term_to_binary(LogCache)); + _ -> + file:write_file(?log_cache_name,term_to_binary(LogCache)) + end; + SavedLogCache -> + update_all_runs_in_cache(AllRunsData,binary_to_term(SavedLogCache)) + end. + +update_all_runs_in_cache(AllRunsData, LogCache) -> + LogCache1 = LogCache#log_cache{all_runs = AllRunsData}, + case {self(),whereis(?MODULE)} of + {_Pid,_Pid} -> + %% save the cache in RAM so it doesn't have to be + %% read from file as long as this logger process is alive + put(ct_log_cache,term_to_binary(LogCache1)); + _ -> + file:write_file(?log_cache_name,term_to_binary(LogCache1)) + end. + sort_all_runs(Dirs) -> %% sort on time string, always last and on the format: %% "YYYY-MM-DD_HH.MM.SS" - KeyList = - lists:map(fun(Dir) -> - case lists:reverse(string:tokens(Dir,[$.,$_])) of - [SS,MM,HH,Date|_] -> - {{Date,HH,MM,SS},Dir}; - _Other -> - throw(Dirs) - end - end,Dirs), - lists:reverse(lists:map(fun({_,Dir}) -> - Dir - end,lists:keysort(1,KeyList))). + lists:sort(fun(Dir1,Dir2) -> + [SS1,MM1,HH1,Date1|_] = + lists:reverse(string:tokens(Dir1,[$.,$_])), + [SS2,MM2,HH2,Date2|_] = + lists:reverse(string:tokens(Dir2,[$.,$_])), + {Date1,HH1,MM1,SS1} > {Date2,HH2,MM2,SS2} + end, Dirs). + +dir_diff_all_runs(Dirs, LogCache) -> + case LogCache#log_cache.all_runs of + [] -> + Dirs; + Cached = [{CDir,_,_}|_] -> + AllRunsDirs = + dir_diff_all_runs(Dirs, Cached, datestr_from_dirname(CDir), []), + lists:reverse(AllRunsDirs) + end. + +dir_diff_all_runs(LogDirs=[Dir|Dirs], Cached=[CElem|CElems], + LatestInCache, AllRunsDirs) -> + DirDate = datestr_from_dirname(Dir), + if DirDate > LatestInCache -> + %% Dir is a new run entry + dir_diff_all_runs(Dirs, Cached, LatestInCache, + [Dir|AllRunsDirs]); + DirDate == LatestInCache, CElems /= [] -> + %% Dir is an existing run entry + dir_diff_all_runs(Dirs, CElems, + datestr_from_dirname(element(1,hd(CElems))), + [CElem|AllRunsDirs]); + DirDate == LatestInCache, CElems == [] -> + %% we're done, Dirs must all be new + lists:reverse(Dirs)++[CElem|AllRunsDirs]; + CElems /= [] -> % DirDate < LatestInCache + %% current CDir not in Dirs, update timestamp and check next + dir_diff_all_runs(LogDirs, CElems, + datestr_from_dirname(element(1,hd(CElems))), + AllRunsDirs); + CElems == [] -> + %% we're done, LogDirs must all be new + lists:reverse(LogDirs)++AllRunsDirs + end; +dir_diff_all_runs([], _Cached, _, AllRunsDirs) -> + AllRunsDirs. interactive_link() -> [Dir|_] = lists:reverse(filelib:wildcard(logdir_prefix()++"*.*")), @@ -1619,12 +1830,14 @@ interactive_link() -> "<html>\n"], ["<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n", "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n", - "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"]), + "<html xmlns=\"http://www.w3.org/1999/xhtml\" ", + "xml:lang=\"en\" lang=\"en\">\n"]), "<!-- autogenerated by '"++atom_to_list(?MODULE)++"' -->\n", "<head>\n", "<title>Last interactive run</title>\n", "<meta http-equiv=\"cache-control\" content=\"no-cache\">\n", - "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n", + "<meta http-equiv=\"content-type\" content=\"text/html; " + "charset=utf-8\">\n", "</head>\n", "<body>\n", "Log from last interactive run: <a href=\"",uri(CtLog),"\">", @@ -1637,98 +1850,120 @@ interactive_link() -> "Any CT activities will be logged here\n", [?abs("last_interactive.html")]). -runentry(Dir) -> +%% use if cache disabled or non-existing +runentry(Dir, undefined, _) -> TotalsFile = filename:join(Dir,?totals_name), - TotalsStr = - case read_totals_file(TotalsFile) of - {Node,Label,Logs,{TotSucc,TotFail,UserSkip,AutoSkip,NotBuilt}} -> - TotFailStr = - if TotFail > 0 -> - ["<font color=\"red\">", - integer_to_list(TotFail),"</font>"]; - true -> - integer_to_list(TotFail) - end, - {AllSkip,UserSkipStr,AutoSkipStr} = - if AutoSkip == undefined -> {UserSkip,"?","?"}; - true -> - ASStr = if AutoSkip > 0 -> - ["<font color=\"brown\">", - integer_to_list(AutoSkip),"</font>"]; - true -> integer_to_list(AutoSkip) - end, - {UserSkip+AutoSkip,integer_to_list(UserSkip),ASStr} - end, - NoOfTests = case length(Logs) of - 0 -> "-"; - N -> integer_to_list(N) - end, - StripExt = - fun(File) -> - string:sub_string(File,1, - length(File)- - length(?logdir_ext)) ++ ", " - end, - Polish = fun(S) -> case lists:reverse(S) of - [32,$,|Rev] -> lists:reverse(Rev); - [$,|Rev] -> lists:reverse(Rev); - _ -> S - end - end, - TestNames = Polish(lists:flatten(lists:map(StripExt,Logs))), - TestNamesTrunc = - if TestNames=="" -> - ""; - length(TestNames) < ?testname_width -> - TestNames; - true -> - Trunc = Polish(string:substr(TestNames,1,?testname_width-3)), - lists:flatten(io_lib:format("~ts...",[Trunc])) - end, - Total = TotSucc+TotFail+AllSkip, - A = xhtml(["<td align=center><font size=\"-1\">",Node, - "</font></td>\n", - "<td align=center><font size=\"-1\"><b>",Label, - "</b></font></td>\n", - "<td align=right>",NoOfTests,"</td>\n"], - ["<td align=center>",Node,"</td>\n", - "<td align=center><b>",Label,"</b></td>\n", - "<td align=right>",NoOfTests,"</td>\n"]), - B = xhtml(["<td align=center title='",TestNames,"'><font size=\"-1\"> ", - TestNamesTrunc,"</font></td>\n"], - ["<td align=center title='",TestNames,"'> ", - TestNamesTrunc,"</td>\n"]), - C = ["<td align=right>",integer_to_list(Total),"</td>\n", - "<td align=right>",integer_to_list(TotSucc),"</td>\n", - "<td align=right>",TotFailStr,"</td>\n", - "<td align=right>",integer_to_list(AllSkip), - " (",UserSkipStr,"/",AutoSkipStr,")</td>\n", - "<td align=right>",integer_to_list(NotBuilt),"</td>\n"], - A++B++C; - _ -> - A = xhtml(["<td align=center><font size=\"-1\" color=\"red\">" - "Test data missing or corrupt</font></td>\n", - "<td align=center><font size=\"-1\">?</font></td>\n", - "<td align=right>?</td>\n"], - ["<td align=center><font color=\"red\">" - "Test data missing or corrupt</font></td>\n", - "<td align=center>?</td>\n", - "<td align=right>?</td>\n"]), - B = xhtml(["<td align=center><font size=\"-1\">?</font></td>\n"], - ["<td align=center>?</td>\n"]), - C = ["<td align=right>?</td>\n", - "<td align=right>?</td>\n", - "<td align=right>?</td>\n", - "<td align=right>?</td>\n", - "<td align=right>?</td>\n"], - A++B++C - end, Index = uri(filename:join(Dir,?index_name)), - [xhtml("<tr>\n", ["<tr class=\"",odd_or_even(),"\">\n"]), - xhtml(["<td><font size=\"-1\"><a href=\"",Index,"\">",timestamp(Dir),"</a>", - TotalsStr,"</font></td>\n"], - ["<td><a href=\"",Index,"\">",timestamp(Dir),"</a>",TotalsStr,"</td>\n"]), - "</tr>\n"]. + runentry(Dir, read_totals_file(TotalsFile), Index); + +%% use cached data +runentry(Dir, Totals={Node,Label,Logs, + {TotSucc,TotFail,UserSkip,AutoSkip,NotBuilt}}, Index) -> + TotFailStr = + if TotFail > 0 -> + ["<font color=\"red\">", + integer_to_list(TotFail),"</font>"]; + true -> + integer_to_list(TotFail) + end, + {AllSkip,UserSkipStr,AutoSkipStr} = + if AutoSkip == undefined -> {UserSkip,"?","?"}; + true -> + ASStr = if AutoSkip > 0 -> + ["<font color=\"brown\">", + integer_to_list(AutoSkip), + "</font>"]; + true -> integer_to_list(AutoSkip) + end, + {UserSkip+AutoSkip,integer_to_list(UserSkip),ASStr} + end, + NoOfTests = case length(Logs) of + 0 -> "-"; + N -> integer_to_list(N) + end, + StripExt = + fun(File) -> + string:sub_string(File,1, + length(File)- + length(?logdir_ext)) ++ ", " + end, + Polish = fun(S) -> case lists:reverse(S) of + [32,$,|Rev] -> lists:reverse(Rev); + [$,|Rev] -> lists:reverse(Rev); + _ -> S + end + end, + TestNames = Polish(lists:flatten(lists:map(StripExt,Logs))), + TestNamesTrunc = + if TestNames=="" -> + ""; + length(TestNames) < ?testname_width -> + TestNames; + true -> + Trunc = Polish(string:substr(TestNames,1, + ?testname_width-3)), + lists:flatten(io_lib:format("~ts...",[Trunc])) + end, + Total = TotSucc+TotFail+AllSkip, + A = xhtml(["<td align=center><font size=\"-1\">",Node, + "</font></td>\n", + "<td align=center><font size=\"-1\"><b>",Label, + "</b></font></td>\n", + "<td align=right>",NoOfTests,"</td>\n"], + ["<td align=center>",Node,"</td>\n", + "<td align=center><b>",Label,"</b></td>\n", + "<td align=right>",NoOfTests,"</td>\n"]), + B = xhtml(["<td align=center title='",TestNames, + "'><font size=\"-1\"> ", + TestNamesTrunc,"</font></td>\n"], + ["<td align=center title='",TestNames,"'> ", + TestNamesTrunc,"</td>\n"]), + C = ["<td align=right>",integer_to_list(Total),"</td>\n", + "<td align=right>",integer_to_list(TotSucc),"</td>\n", + "<td align=right>",TotFailStr,"</td>\n", + "<td align=right>",integer_to_list(AllSkip), + " (",UserSkipStr,"/",AutoSkipStr,")</td>\n", + "<td align=right>",integer_to_list(NotBuilt),"</td>\n"], + TotalsStr = A++B++C, + + XHTML = [xhtml("<tr>\n", ["<tr class=\"",odd_or_even(),"\">\n"]), + xhtml(["<td><font size=\"-1\"><a href=\"",Index,"\">", + timestamp(Dir),"</a>", + TotalsStr,"</font></td>\n"], + ["<td><a href=\"",Index,"\">",timestamp(Dir),"</a>",TotalsStr, + "</td>\n"]), + "</tr>\n"], + {Totals,XHTML,Index}; + +%% handle missing or corrupt data (missing e.g. if the test is in progress) +runentry(Dir, _, _) -> + A = xhtml(["<td align=center><font size=\"-1\" color=\"red\">" + "Test data missing or corrupt</font></td>\n", + "<td align=center><font size=\"-1\">?</font></td>\n", + "<td align=right>?</td>\n"], + ["<td align=center><font color=\"red\">" + "Test data missing or corrupt</font></td>\n", + "<td align=center>?</td>\n", + "<td align=right>?</td>\n"]), + B = xhtml(["<td align=center><font size=\"-1\">?</font></td>\n"], + ["<td align=center>?</td>\n"]), + C = ["<td align=right>?</td>\n", + "<td align=right>?</td>\n", + "<td align=right>?</td>\n", + "<td align=right>?</td>\n", + "<td align=right>?</td>\n"], + TotalsStr = A++B++C, + + Index = uri(filename:join(Dir,?index_name)), + + XHTML = [xhtml("<tr>\n", ["<tr class=\"",odd_or_even(),"\">\n"]), + xhtml(["<td><font size=\"-1\"><a href=\"",Index,"\">", + timestamp(Dir),"</a>", + TotalsStr,"</font></td>\n"], + ["<td><a href=\"",Index,"\">",timestamp(Dir),"</a>",TotalsStr, + "</td>\n"]), + "</tr>\n"], + {no_test_data,XHTML,Index}. write_totals_file(Name,Label,Logs,Totals) -> AbsName = ?abs(Name), @@ -1755,17 +1990,19 @@ read_totals_file(Name) -> _ -> Label end, case Tot of - {_Ok,_Fail,_USkip,_ASkip,_NoBuild} -> % latest format + {_Ok,_Fail,_USkip,_ASkip,_NoBuild} -> % latest format {Node,Label1,Ls,Tot}; {TotSucc,TotFail,AllSkip,NotBuilt} -> - {Node,Label1,Ls,{TotSucc,TotFail,AllSkip,undefined,NotBuilt}} + {Node,Label1,Ls, + {TotSucc,TotFail,AllSkip,undefined,NotBuilt}} end; {Node,Ls,Tot} -> % no label found case Tot of - {_Ok,_Fail,_USkip,_ASkip,_NoBuild} -> % latest format + {_Ok,_Fail,_USkip,_ASkip,_NoBuild} -> % latest format {Node,"-",Ls,Tot}; {TotSucc,TotFail,AllSkip,NotBuilt} -> - {Node,"-",Ls,{TotSucc,TotFail,AllSkip,undefined,NotBuilt}} + {Node,"-",Ls, + {TotSucc,TotFail,AllSkip,undefined,NotBuilt}} end; %% for backwards compatibility {Ls,Tot} -> {"-",Ls,Tot}; @@ -1819,29 +2056,73 @@ timestamp(Dir) -> %% run will not show until after the final refresh. %% ------------------------------------------------------------------------- -%% Creates the top level index file. When == start | refresh. -%% A copy of the dir tree under logdir is cached as a result. +%% Creates the top level index file. When == start | stop | refresh. +%% A copy of the dir tree under logdir is saved temporarily as a result. make_all_suites_index(When) when is_atom(When) -> put(basic_html, basic_html()), AbsIndexName = ?abs(?index_name), notify_and_lock_file(AbsIndexName), + + %% check if log cache should be used, and if it exists + UseCache = + if When == refresh -> + save_only; + true -> + case application:get_env(common_test, disable_log_cache) of + {ok,true} -> + disabled; + _ -> + case get(ct_log_cache) of + undefined -> + file:read_file(?log_cache_name); + LogCacheBin -> + {ok,LogCacheBin} + end + end + end, + LogDirs = filelib:wildcard(logdir_prefix()++".*/*"++?logdir_ext), - Sorted = sort_logdirs(LogDirs, []), - Result = make_all_suites_index1(When, AbsIndexName, Sorted), - notify_and_unlock_file(AbsIndexName), - Result; -%% This updates the top level index file using cached data from -%% the initial index file creation. -make_all_suites_index(NewTestData = {_TestName,DirName}) -> + LogCacheInfo = get_cache_data(UseCache), + + Result = + case LogCacheInfo of + {ok,LogCache} -> + %% use the log cache file to generate the index + make_all_suites_index_from_cache(When,AbsIndexName, + LogDirs,LogCache); + _WhyNot -> + %% no cache file exists (or feature has been disabled) + Sorted = sort_and_filter_logdirs(LogDirs), + TempData = make_all_suites_index1(When,AbsIndexName,Sorted), + notify_and_unlock_file(AbsIndexName), + + %% save new cache file unless the feature is disabled + if UseCache == disabled -> ok; + true -> update_tests_in_cache(TempData) + end, + TempData + end, + + case Result of + Error = {error,_} -> Error; + _ -> ok + end; + +%% This updates the top level index file using data from the initial +%% index file creation, saved temporarily in a table. +make_all_suites_index(NewTestData = {_TestName,DirName}) -> put(basic_html, basic_html()), - %% AllLogDirs = [{TestName,Label,Missing,{LastLogDir,Summary},OldDirs}|...] + + %% AllLogDirs = [{TestName,Label,Missing, + %% {LastLogDir,Summary,URIs},OldDirs}|...] + {AbsIndexName,LogDirData} = ct_util:get_testdata(test_index), CtRunDirPos = length(filename:split(AbsIndexName)), CtRunDir = filename:join(lists:sublist(filename:split(DirName), CtRunDirPos)), - + Label = case read_totals_file(filename:join(CtRunDir, ?totals_name)) of {_,"-",_,_} -> "..."; {_,Lbl,_,_} -> Lbl; @@ -1849,10 +2130,10 @@ make_all_suites_index(NewTestData = {_TestName,DirName}) -> end, notify_and_lock_file(AbsIndexName), Result = - case catch make_all_suites_ix_cached(AbsIndexName, - NewTestData, - Label, - LogDirData) of + case catch make_all_suites_ix_temp(AbsIndexName, + NewTestData, + Label, + LogDirData) of {'EXIT',Reason} -> io:put_chars("CRASHED while updating " ++ AbsIndexName ++ "!\n"), io:format("~p~n", [Reason]), @@ -1869,46 +2150,219 @@ make_all_suites_index(NewTestData = {_TestName,DirName}) -> [AbsIndexName,Err]), {error, Err} end, - notify_and_unlock_file(AbsIndexName), + notify_and_unlock_file(AbsIndexName), Result. -sort_logdirs([Dir|Dirs],Groups) -> +make_all_suites_index_from_cache(When, AbsIndexName, LogDirs, LogCache) -> + + %% The structure of the cache: + %% + %% #log_cache{tests = {TestName,Label,Missing, + %% {LastLogDir,Summary,URIs},OldDirs} + %% } + %% Summary = {Succ,Fail,USkip,ASkip} | error + %% + + {NewAdded,OldTests} = dir_diff_tests(LogDirs,LogCache), + + LogCache1 = delete_tests_from_cache(OldTests,LogCache), + Sorted = sort_and_filter_logdirs(NewAdded, + LogCache1#log_cache.tests), + TempData = + if Sorted /= [] -> + make_all_suites_index1(When,AbsIndexName, + Sorted); + true -> + Data = LogCache1#log_cache.tests, + ct_util:set_testdata_async({test_index,{AbsIndexName, + Data}}), + Data + end, + + notify_and_unlock_file(AbsIndexName), + + update_tests_in_cache(TempData,LogCache1), + TempData. + +sort_and_filter_logdirs(NewDirs,CachedTests) when CachedTests /= [] -> + NewSorted = sort_and_filter_logdirs1(NewDirs,[]), + sort_and_filter_logdirs(NewSorted,CachedTests,[]); + +sort_and_filter_logdirs(NewDirs,_CachedTests) -> + sort_and_filter_logdirs(NewDirs). + +%% sort latest dirs found and combine them with cached entries +sort_and_filter_logdirs([{TestName,IxDirs}|Tests],CachedTests,Combined) -> + case lists:keysearch(TestName,1,CachedTests) of + {value,{TestName,_,_,{IxDir0,_,_},IxDirs0}} -> + Groups = sort_and_filter_logdirs2(TestName, + IxDirs++[IxDir0|IxDirs0], + []), + sort_and_filter_logdirs(Tests,CachedTests,Groups++Combined); + _ -> + IxDirs1 = lists:map(fun(Elem = {_,_}) -> + Elem; + (RunDir) -> + {filename:basename(RunDir),RunDir} + end, IxDirs), + sort_and_filter_logdirs(Tests,CachedTests, + [{TestName,IxDirs1}|Combined]) + end; +sort_and_filter_logdirs([],CachedTests,Combined) -> + Cached1 = lists:foldl(fun({TestName,_},Cached) -> + lists:keydelete(TestName,1,Cached) + end, CachedTests, Combined), + lists:keysort(1,sort_each_group(Combined)++Cached1). + +sort_and_filter_logdirs(Dirs) -> + sort_and_filter_logdirs1(Dirs, []). + +%% sort and filter directories (no cache) +sort_and_filter_logdirs1([Dir|Dirs],Groups) -> TestName = filename:rootname(filename:basename(Dir)), case filelib:wildcard(filename:join(Dir,"run.*")) of RunDirs = [_|_] -> - Groups1 = sort_logdirs1(TestName,RunDirs,Groups), - sort_logdirs(Dirs,Groups1); + Groups1 = sort_and_filter_logdirs2(TestName,RunDirs,Groups), + sort_and_filter_logdirs1(Dirs,Groups1); _ -> % ignore missing run directory - sort_logdirs(Dirs,Groups) + sort_and_filter_logdirs1(Dirs,Groups) end; -sort_logdirs([],Groups) -> +sort_and_filter_logdirs1([],Groups) -> lists:keysort(1,sort_each_group(Groups)). -sort_logdirs1(TestName,[RunDir|RunDirs],Groups) -> +sort_and_filter_logdirs2(TestName,[RunDir|RunDirs],Groups) -> Groups1 = insert_test(TestName,{filename:basename(RunDir),RunDir},Groups), - sort_logdirs1(TestName,RunDirs,Groups1); -sort_logdirs1(_,[],Groups) -> + sort_and_filter_logdirs2(TestName,RunDirs,Groups1); +sort_and_filter_logdirs2(_,[],Groups) -> Groups. +%% new rundir for Test found, add to (not sorted) list of prev rundirs insert_test(Test,IxDir,[{Test,IxDirs}|Groups]) -> [{Test,[IxDir|IxDirs]}|Groups]; +%% first occurance of Test insert_test(Test,IxDir,[]) -> [{Test,[IxDir]}]; insert_test(Test,IxDir,[TestDir|Groups]) -> [TestDir|insert_test(Test,IxDir,Groups)]. - + +%% sort the list of rundirs for each Test sort_each_group([{Test,IxDirs}|Groups]) -> Sorted = lists:reverse([Dir || {_,Dir} <- lists:keysort(1,IxDirs)]), - [{Test,Sorted}| sort_each_group(Groups)]; + [{Test,Sorted}|sort_each_group(Groups)]; sort_each_group([]) -> []. -make_all_suites_index1(When, AbsIndexName, AllLogDirs) -> +dir_diff_tests(LogDirs, #log_cache{tests = CachedTests}) -> + AllTestNames = + [TestName || {TestName,_,_,_,_} <- CachedTests], + dir_diff_tests(LogDirs, CachedTests, [], AllTestNames, [], []). + +dir_diff_tests([LogDir|LogDirs], CachedTests, NewAdded, DeletedTests, + ValidLast, InvalidLast) -> + TestName = filename:rootname(filename:basename(LogDir)), + Time = datestr_from_dirname(LogDir), + %% check if the test already exists in the cache + {New,DeletedTests1,ValidLast1,InvalidLast1} = + case lists:keysearch(TestName,1,CachedTests) of + {value,{_,_,_,{LastLogDir,_,_},_PrevLogDirs}} -> + LastLogTime = datestr_from_dirname(LastLogDir), + if Time > LastLogTime -> + %% this is a new test run, not in cache + {[LogDir|NewAdded], + lists:delete(TestName,DeletedTests), + ValidLast,[{TestName,LastLogDir}|InvalidLast]}; + Time == LastLogTime -> + %% this is the latest test run, already in cache + TDir = {TestName,LastLogDir}, + {NewAdded, + lists:delete(TestName,DeletedTests), + [TDir|ValidLast],InvalidLast}; + true -> + %% this is an old test run + {[], + lists:delete(TestName,DeletedTests), + ValidLast,[{TestName,LastLogDir}|InvalidLast]} + end; + _ -> + %% this is a test run for a new test, not in cache + {[LogDir|NewAdded], + DeletedTests,ValidLast,InvalidLast} + end, + dir_diff_tests(LogDirs, CachedTests, New, DeletedTests1, + ValidLast1,InvalidLast1); + +dir_diff_tests([], _CachedTests, NewAdded, DeletedTests, + ValidLast, InvalidLast) -> + %% We have to check if LastLogDir still exists or if it's been + %% deleted. InvalidLast contains all log dirs that should be deleted, + %% if not present in ValidLast. + InvalidLast1 = + lists:foldl(fun(TDir,IL) -> + case lists:member(TDir,ValidLast) of + true -> + [TD || TD <- IL, TD /= TDir]; + false -> + [TDir | [TD || TD <- IL, TD /= TDir]] + end + end, InvalidLast, InvalidLast), + + %% Collect all tests for which LastLogDir has been deleted. + DeletedTests1 = [T || {T,_} <- InvalidLast1] ++ DeletedTests, + + %% Make sure that directories for tests that are to be deleted are + %% saved in NewAdded so that tests don't disappear from the log if + %% older run dirs for them exist. + NewAdded1 = lists:map(fun({_TestName,RunDir}) -> + [TopDir,TestDir|_] = filename:split(RunDir), + filename:join(TopDir,TestDir) + end, InvalidLast1) ++ NewAdded, + + {NewAdded1,DeletedTests1}. + +delete_tests_from_cache(OldTests, LogCache=#log_cache{tests=Tests}) -> + Tests2 = lists:foldl(fun(T,Tests1) -> + lists:keydelete(T,1,Tests1) + end, Tests, OldTests), + LogCache#log_cache{tests = Tests2}. + +update_tests_in_cache(TempData) -> + case get(ct_log_cache) of + undefined -> + update_tests_in_cache(TempData,#log_cache{version = cache_vsn(), + tests=[]}); + SavedLogCache -> + update_tests_in_cache(TempData,binary_to_term(SavedLogCache)) + end. + +update_tests_in_cache(TempData,LogCache=#log_cache{tests=Tests}) -> + Cached1 = + if Tests == [] -> + []; + true -> + lists:foldl(fun({TestName,_,_,_,_},Cached) -> + lists:keydelete(TestName,1,Cached) + end, Tests, TempData) + end, + Tests1 = lists:keysort(1,TempData++Cached1), + CacheBin = term_to_binary(LogCache#log_cache{tests = Tests1}), + case {self(),whereis(?MODULE)} of + {_Pid,_Pid} -> + put(ct_log_cache,CacheBin); + _ -> + file:write_file(?log_cache_name,CacheBin) + end. + +%% +%% AllTestLogDirs = +%% [{TestName,[IxDir|IxDirs]} | ...] (non-cached), or +%% [{TestName,Label,Missing,{IxDir,Summary,URIs},IxDirs} | ...] (cached) +%% +make_all_suites_index1(When, AbsIndexName, AllTestLogDirs) -> IndexName = ?index_name, if When == start -> ok; true -> io:put_chars("Updating " ++ AbsIndexName ++ "... ") end, - case catch make_all_suites_index2(IndexName, AllLogDirs) of + case catch make_all_suites_index2(IndexName, AllTestLogDirs) of {'EXIT', Reason} -> io:put_chars("CRASHED while updating " ++ AbsIndexName ++ "!\n"), io:format("~p~n", [Reason]), @@ -1917,15 +2371,15 @@ make_all_suites_index1(When, AbsIndexName, AllLogDirs) -> io:put_chars("FAILED while updating " ++ AbsIndexName ++ "\n"), io:format("~p~n", [Reason]), {error, Reason}; - {ok,CacheData} -> + {ok,TempData} -> case When of start -> ct_util:set_testdata_async({test_index,{AbsIndexName, - CacheData}}), - ok; + TempData}}), + TempData; _ -> io:put_chars("done\n"), - ok + TempData end; Err -> io:format("Unknown internal error while updating ~ts. " @@ -1935,21 +2389,57 @@ make_all_suites_index1(When, AbsIndexName, AllLogDirs) -> end. make_all_suites_index2(IndexName, AllTestLogDirs) -> - {ok,Index0,_Totals,CacheData} = + {ok,Index0,_Totals,TempData} = make_all_suites_index3(AllTestLogDirs, all_suites_index_header(), 0, 0, 0, 0, 0, [], []), - Index = [Index0|index_footer()], + Index = [Index0|all_suites_index_footer()], case force_write_file(IndexName, unicode:characters_to_binary(Index)) of ok -> - {ok,CacheData}; + {ok,TempData}; {error, Reason} -> {error,{index_write_error, Reason}} end. +%% +%% AllTestLogDirs = [{TestName,Label,Missing,{LogDir,Summary,URIs},OldDirs}] +%% Summary = {Succ,Fail,UserSkip,AutoSkip} | error +%% URIs = {CtRunLogURI,LogFileURI,CrashDumpURI} | undefined +%% +%% this clause is for handling entries in the log cache +make_all_suites_index3([IxEntry = {TestName,Label,Missing, + {LastLogDir,Summary,URIs},OldDirs} | Rest], + Result, TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt, + Labels, TempData) -> + [EntryDir|_] = filename:split(LastLogDir), + Labels1 = [{EntryDir,Label}|Labels], + case Summary of + {Succ,Fail,USkip,ASkip} -> + All = {true,OldDirs}, + NotBuilt = not_built(TestName, LastLogDir, All, Missing), + + {Result1,_} = make_one_index_entry1(TestName, LastLogDir, Label, + Succ, Fail, USkip, ASkip, + NotBuilt, All, temp, URIs), + + AutoSkip1 = case catch AutoSkip+ASkip of + {'EXIT',_} -> undefined; + Res -> Res + end, + make_all_suites_index3(Rest, [Result|Result1], TotSucc+Succ, + TotFail+Fail, UserSkip+USkip, AutoSkip1, + TotNotBuilt+NotBuilt, Labels1, + [IxEntry|TempData]); + error -> + make_all_suites_index3(Rest, Result, TotSucc, TotFail, + UserSkip, AutoSkip, TotNotBuilt, Labels1, + [IxEntry|TempData]) + end; + +%% this clause is for handling non-cached directories make_all_suites_index3([{TestName,[LastLogDir|OldDirs]}|Rest], Result, TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt, - Labels, CacheData) -> + Labels, TempData) -> [EntryDir|_] = filename:split(LastLogDir), Missing = case file:read_file(filename:join(EntryDir, ?missing_suites_info)) of @@ -1966,38 +2456,50 @@ make_all_suites_index3([{TestName,[LastLogDir|OldDirs]}|Rest], Lbl -> {Lbl,Labels} end, - case make_one_index_entry(TestName, LastLogDir, Label, {true,OldDirs}, Missing) of - {Result1,Succ,Fail,USkip,ASkip,NotBuilt} -> + case make_one_index_entry(TestName, LastLogDir, Label, + {true,OldDirs}, Missing, undefined) of + {Result1,Succ,Fail,USkip,ASkip,NotBuilt,URIs} -> %% for backwards compatibility AutoSkip1 = case catch AutoSkip+ASkip of {'EXIT',_} -> undefined; Res -> Res end, IxEntry = {TestName,Label,Missing, - {LastLogDir,{Succ,Fail,USkip,ASkip}},OldDirs}, + {LastLogDir,{Succ,Fail,USkip,ASkip},URIs},OldDirs}, + make_all_suites_index3(Rest, [Result|Result1], TotSucc+Succ, TotFail+Fail, UserSkip+USkip, AutoSkip1, TotNotBuilt+NotBuilt, Labels1, - [IxEntry|CacheData]); + [IxEntry|TempData]); error -> - IxEntry = {TestName,Label,Missing,{LastLogDir,error},OldDirs}, + IxEntry = {TestName,Label,Missing, + {LastLogDir,error,undefined},OldDirs}, make_all_suites_index3(Rest, Result, TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt, Labels1, - [IxEntry|CacheData]) + [IxEntry|TempData]) end; + +%% something wrong with this test dir, ignore +make_all_suites_index3([_|Rest], Result, TotSucc, TotFail, UserSkip, AutoSkip, + TotNotBuilt, Labels, TempData) -> + make_all_suites_index3(Rest, Result, TotSucc, TotFail, + UserSkip, AutoSkip, TotNotBuilt, Labels, + TempData); + make_all_suites_index3([], Result, TotSucc, TotFail, UserSkip, AutoSkip, - TotNotBuilt, _, CacheData) -> - {ok, [Result|total_row(TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt,true)], - {TotSucc,TotFail,UserSkip,AutoSkip,TotNotBuilt}, lists:reverse(CacheData)}. + TotNotBuilt, _, TempData) -> + {ok, [Result|total_row(TotSucc, TotFail, UserSkip, AutoSkip, + TotNotBuilt,true)], + {TotSucc,TotFail,UserSkip,AutoSkip,TotNotBuilt}, lists:reverse(TempData)}. -make_all_suites_ix_cached(AbsIndexName, NewTestData, Label, AllTestLogDirs) -> +make_all_suites_ix_temp(AbsIndexName, NewTestData, Label, AllTestLogDirs) -> AllTestLogDirs1 = insert_new_test_data(NewTestData, Label, AllTestLogDirs), IndexDir = filename:dirname(AbsIndexName), - Index0 = make_all_suites_ix_cached1(AllTestLogDirs1, - all_suites_index_header(IndexDir), - 0, 0, 0, 0, 0), - Index = [Index0|index_footer()], + Index0 = make_all_suites_ix_temp1(AllTestLogDirs1, + all_suites_index_header(IndexDir), + 0, 0, 0, 0, 0), + Index = [Index0|all_suites_index_footer()], case force_write_file(AbsIndexName, unicode:characters_to_binary(Index)) of ok -> ok; @@ -2008,51 +2510,94 @@ make_all_suites_ix_cached(AbsIndexName, NewTestData, Label, AllTestLogDirs) -> insert_new_test_data({NewTestName,NewTestDir}, NewLabel, AllTestLogDirs) -> AllTestLogDirs1 = case lists:keysearch(NewTestName, 1, AllTestLogDirs) of - {value,{_,_,_,{LastLogDir,_},OldDirs}} -> - [{NewTestName,NewLabel,[],{NewTestDir,{0,0,0,0}}, + {value,{_,_,_,{LastLogDir,_,_},OldDirs}} -> + [{NewTestName,NewLabel,[],{NewTestDir,{0,0,0,0},undefined}, [LastLogDir|OldDirs]} | lists:keydelete(NewTestName, 1, AllTestLogDirs)]; false -> - [{NewTestName,NewLabel,[],{NewTestDir,{0,0,0,0}},[]} | + [{NewTestName,NewLabel,[],{NewTestDir,{0,0,0,0},undefined},[]} | AllTestLogDirs] end, lists:keysort(1, AllTestLogDirs1). -make_all_suites_ix_cached1([{TestName,Label,Missing,LastLogDirData,OldDirs}|Rest], - Result, TotSucc, TotFail, UserSkip, AutoSkip, - TotNotBuilt) -> - - case make_one_ix_entry_cached(TestName, LastLogDirData, - Label, {true,OldDirs}, Missing) of - {Result1,Succ,Fail,USkip,ASkip,NotBuilt} -> +make_all_suites_ix_temp1([{TestName,Label,Missing,LastLogDirData,OldDirs}|Rest], + Result, TotSucc, TotFail, UserSkip, AutoSkip, + TotNotBuilt) -> + case make_one_ix_entry_temp(TestName, LastLogDirData, + Label, {true,OldDirs}, Missing) of + {Result1,Succ,Fail,USkip,ASkip,NotBuilt,_URIs} -> %% for backwards compatibility AutoSkip1 = case catch AutoSkip+ASkip of {'EXIT',_} -> undefined; Res -> Res end, - make_all_suites_ix_cached1(Rest, [Result|Result1], TotSucc+Succ, - TotFail+Fail, UserSkip+USkip, AutoSkip1, - TotNotBuilt+NotBuilt); + make_all_suites_ix_temp1(Rest, [Result|Result1], TotSucc+Succ, + TotFail+Fail, UserSkip+USkip, AutoSkip1, + TotNotBuilt+NotBuilt); error -> - make_all_suites_ix_cached1(Rest, Result, TotSucc, TotFail, - UserSkip, AutoSkip, TotNotBuilt) + make_all_suites_ix_temp1(Rest, Result, TotSucc, TotFail, + UserSkip, AutoSkip, TotNotBuilt) end; -make_all_suites_ix_cached1([], Result, TotSucc, TotFail, UserSkip, AutoSkip, - TotNotBuilt) -> +make_all_suites_ix_temp1([], Result, TotSucc, TotFail, UserSkip, AutoSkip, + TotNotBuilt) -> [Result|total_row(TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt, true)]. -make_one_ix_entry_cached(TestName, {LogDir,Summary}, Label, All, Missing) -> +make_one_ix_entry_temp(TestName, {LogDir,Summary,URIs}, Label, All, Missing) -> case Summary of {Succ,Fail,UserSkip,AutoSkip} -> NotBuilt = not_built(TestName, LogDir, All, Missing), - NewResult = make_one_index_entry1(TestName, LogDir, Label, - Succ, Fail, UserSkip, AutoSkip, - NotBuilt, All, cached), - {NewResult,Succ,Fail,UserSkip,AutoSkip,NotBuilt}; + {NewResult,URIs1} = make_one_index_entry1(TestName, LogDir, Label, + Succ, Fail, + UserSkip, AutoSkip, + NotBuilt, All, temp, URIs), + {NewResult,Succ,Fail,UserSkip,AutoSkip,NotBuilt,URIs1}; error -> error end. +%%%----------------------------------------------------------------- +%%% +get_cache_data({ok,CacheBin}) -> + case binary_to_term(CacheBin) of + CacheRec when is_record(CacheRec,log_cache) -> + %% make sure we don't use a cache on old format + case is_correct_cache_vsn(CacheRec) of + true -> + {ok,CacheRec}; + false -> + file:delete(?log_cache_name), + {error,old_cache_file} + end; + _ -> + file:delete(?log_cache_name), + {error,invalid_cache_file} + end; +get_cache_data(NoCache) -> + NoCache. + +cache_vsn() -> + application:load(common_test), + case application:get_key(common_test,vsn) of + {ok,VSN} -> + VSN; + _ -> + EbinDir = filename:dirname(code:which(ct)), + VSNfile = filename:join([EbinDir,"..","vsn.mk"]), + case file:read_file(VSNfile) of + {ok,Bin} -> + [_,VSN] = string:tokens(binary_to_list(Bin),[$=,$\n,$ ]), + VSN; + _ -> + undefined + end + end. + +is_correct_cache_vsn(#log_cache{version = CVSN}) -> + case cache_vsn() of + CVSN -> true; + _ -> false + end. + %%----------------------------------------------------------------- %% Remove log files. %% Cwd should always be set to the root logdir when finished. diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl index 57cfab532e..41d53c7b43 100644 --- a/lib/common_test/src/ct_run.erl +++ b/lib/common_test/src/ct_run.erl @@ -329,6 +329,13 @@ script_start1(Parent, Args) -> application:set_env(common_test, basic_html, true), true end, + %% disable_log_cache - used by ct_logs + case proplists:get_value(disable_log_cache, Args) of + undefined -> + application:set_env(common_test, disable_log_cache, false); + _ -> + application:set_env(common_test, disable_log_cache, true) + end, Opts = #opts{label = Label, profile = Profile, vts = Vts, shell = Shell, @@ -1039,6 +1046,13 @@ run_test2(StartOpts) -> BasicHtmlBool end, + case proplists:get_value(disable_log_cache, StartOpts) of + undefined -> + application:set_env(common_test, disable_log_cache, false); + DisableCacheBool -> + application:set_env(common_test, disable_log_cache, DisableCacheBool) + end, + %% stepped execution Step = get_start_opt(step, value, StartOpts), diff --git a/lib/common_test/src/ct_telnet.erl b/lib/common_test/src/ct_telnet.erl index beb3c1d649..bd74991859 100644 --- a/lib/common_test/src/ct_telnet.erl +++ b/lib/common_test/src/ct_telnet.erl @@ -29,7 +29,9 @@ %%% Command timeout = 10 sec (time to wait for a command to return) %%% Max no of reconnection attempts = 3 %%% Reconnection interval = 5 sek (time to wait in between reconnection attempts) -%%% Keep alive = true (will send NOP to the server every 10 sec if connection is idle)</pre> +%%% Keep alive = true (will send NOP to the server every 10 sec if connection is idle) +%%% Wait for linebreak = true (Will expect answer from server to end with linebreak when +%%% using ct_telnet:expect)</pre> %%% <p>These parameters can be altered by the user with the following %%% configuration term:</p> %%% <pre> @@ -37,7 +39,8 @@ %%% {command_timeout,Millisec}, %%% {reconnection_attempts,N}, %%% {reconnection_interval,Millisec}, -%%% {keep_alive,Bool}]}.</pre> +%%% {keep_alive,Bool}, +%% {wait_for_linebreak, Bool}]}.</pre> %%% <p><code>Millisec = integer(), N = integer()</code></p> %%% <p>Enter the <code>telnet_settings</code> term in a configuration %%% file included in the test and ct_telnet will retrieve the information diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl index 02e58d0786..6a8b37bf3b 100644 --- a/lib/common_test/src/ct_util.erl +++ b/lib/common_test/src/ct_util.erl @@ -414,6 +414,8 @@ loop(Mode,TestData,StartDir) -> [#conn{address=A,callback=CB}] -> %% A connection crashed - remove the connection but don't die ct_logs:tc_log_async(ct_error_notify, + ?MAX_IMPORTANCE, + "CT Error Notification", "Connection process died: " "Pid: ~w, Address: ~p, Callback: ~w\n" "Reason: ~p\n\n", diff --git a/lib/common_test/src/cth_log_redirect.erl b/lib/common_test/src/cth_log_redirect.erl index 78ae70f37e..958b7a94c7 100644 --- a/lib/common_test/src/cth_log_redirect.erl +++ b/lib/common_test/src/cth_log_redirect.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011-2012. All Rights Reserved. +%% Copyright Ericsson AB 2011-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -33,6 +33,8 @@ handle_event/2, handle_call/2, handle_info/2, terminate/1]). +-include("ct.hrl"). + id(_Opts) -> ?MODULE. @@ -78,7 +80,7 @@ handle_event(Event, LogFunc) -> SReport = sasl_report:format_report(group_leader(), ErrLogType, tag_event(Event)), if is_list(SReport) -> - ct_logs:LogFunc(sasl, SReport, []); + ct_logs:LogFunc(sasl, ?STD_IMPORTANCE, "System", SReport, []); true -> %% Report is an atom if no logging is to be done ignore end @@ -86,7 +88,7 @@ handle_event(Event, LogFunc) -> EReport = error_logger_tty_h:write_event( tag_event(Event),io_lib), if is_list(EReport) -> - ct_logs:LogFunc(error_logger, EReport, []); + ct_logs:LogFunc(error_logger, ?STD_IMPORTANCE, "System", EReport, []); true -> %% Report is an atom if no logging is to be done ignore end, diff --git a/lib/common_test/test/ct_cover_SUITE_data/cover_SUITE.erl b/lib/common_test/test/ct_cover_SUITE_data/cover_SUITE.erl index d967590c72..83d368c53d 100644 --- a/lib/common_test/test/ct_cover_SUITE_data/cover_SUITE.erl +++ b/lib/common_test/test/ct_cover_SUITE_data/cover_SUITE.erl @@ -52,11 +52,10 @@ init_per_testcase(_Case, Config) -> [{watchdog, Dog}|Config]. end_per_testcase(Case, Config) -> - %% try apply(?MODULE,Case,[cleanup,Config]) - %% catch error:undef -> ok - %% end, + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, - kill_slaves(Case,nodes()), Dog=?config(watchdog, Config), test_server:timetrap_cancel(Dog), ok. @@ -67,12 +66,12 @@ break(_Config) -> test_server:break(""), ok. -default(Config) -> +default(_Config) -> cover_compiled = code:which(cover_test_mod), cover_test_mod:foo(), ok. -slave(Config) -> +slave(_Config) -> cover_compiled = code:which(cover_test_mod), cover_test_mod:foo(), N1 = nodename(slave,1), @@ -81,8 +80,10 @@ slave(Config) -> rpc:call(Node,cover_test_mod,foo,[]), {ok,Node} = ct_slave:stop(N1), ok. +slave(cleanup,_Config) -> + kill_slaves([nodename(slave,1)]). -slave_start_slave(Config) -> +slave_start_slave(_Config) -> cover_compiled = code:which(cover_test_mod), cover_test_mod:foo(), N1 = nodename(slave_start_slave,1), @@ -90,13 +91,16 @@ slave_start_slave(Config) -> {ok,Node} = start_slave(N1), cover_compiled = rpc:call(Node,code,which,[cover_test_mod]), rpc:call(Node,cover_test_mod,foo,[]), - {ok,Node2} = rpc:call(Node,ct_slave,start,[N2]), + {ok,Node2} = start_slave(Node,N2), % start slave N2 from node Node rpc:call(Node2,cover_test_mod,foo,[]), {ok,Node2} = rpc:call(Node,ct_slave,stop,[N2]), {ok,Node} = ct_slave:stop(N1), ok. +slave_start_slave(cleanup,_Config) -> + kill_slaves([nodename(slave_start_slave,1), + nodename(slave_start_slave,2)]). -cover_node_option(Config) -> +cover_node_option(_Config) -> cover_compiled = code:which(cover_test_mod), cover_test_mod:foo(), Node = fullname(existing_node_1), @@ -104,7 +108,7 @@ cover_node_option(Config) -> rpc:call(Node,cover_test_mod,foo,[]), ok. -ct_cover_add_remove_nodes(Config) -> +ct_cover_add_remove_nodes(_Config) -> cover_compiled = code:which(cover_test_mod), cover_test_mod:foo(), Node = fullname(existing_node_2), @@ -143,22 +147,20 @@ fullname(Name) -> {ok,Host} = inet:gethostname(), list_to_atom(atom_to_list(Name) ++ "@" ++ Host). -kill_slaves(Case, [Node|Nodes]) -> - Prefix = nodeprefix(Case), - case lists:prefix(Prefix,atom_to_list(Node)) of - true -> - rpc:call(Node,erlang,halt,[]); - _ -> - ok - end, - kill_slaves(Case,Nodes); -kill_slaves(_,[]) -> +kill_slaves([Name|Names]) -> + _ = rpc:call(fullname(Name),erlang,halt,[]), + kill_slaves(Names); +kill_slaves([]) -> ok. start_slave(Name) -> + start_slave(node(),Name). + +start_slave(FromNode,Name) -> {ok, HostStr}=inet:gethostname(), Host = list_to_atom(HostStr), - ct_slave:start(Host,Name, - [{boot_timeout,10}, % extending some timers for slow test hosts - {init_timeout,10}, - {startup_timeout,10}]). + rpc:call(FromNode,ct_slave,start, + [Host,Name, + [{boot_timeout,15}, % extending some timers for slow test hosts + {init_timeout,15}, + {startup_timeout,15}]]). diff --git a/lib/common_test/test/ct_repeat_testrun_SUITE.erl b/lib/common_test/test/ct_repeat_testrun_SUITE.erl index 7ec384c932..35d67a10f2 100644 --- a/lib/common_test/test/ct_repeat_testrun_SUITE.erl +++ b/lib/common_test/test/ct_repeat_testrun_SUITE.erl @@ -51,8 +51,8 @@ %% least 20 seconds (10 sec for each r1_SUITE:tc1) %% -define(t1,30). % time shall expire during second run of r1_SUITE --define(t2,6). % time shall expire during first run of tc1 --define(t3,16). % time shall expire during second run of tc1 +-define(t2,9). % time shall expire during first run of tc1 +-define(t3,19). % time shall expire during second run of tc1 %%-------------------------------------------------------------------- diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl index 70dd087358..6bcac12326 100644 --- a/lib/common_test/test/ct_test_support.erl +++ b/lib/common_test/test/ct_test_support.erl @@ -224,11 +224,38 @@ get_opts(Config) -> %%%----------------------------------------------------------------- %%% -run(Opts, Config) when is_list(Opts) -> +run(Opts0, Config) when is_list(Opts0) -> + Opts = + %% read (and override) opts from env variable, the form expected: + %% "[{some_key1,SomeVal2}, {some_key2,SomeVal2}]" + case os:getenv("CT_TEST_OPTS") of + false -> Opts0; + "" -> Opts0; + Terms -> + case erl_scan:string(Terms++".", 0) of + {ok,Tokens,_} -> + case erl_parse:parse_term(Tokens) of + {ok,OROpts} -> + Override = + fun(O={Key,_}, Os) -> + io:format(user, "ADDING START " + "OPTION: ~p~n", [O]), + [O | lists:keydelete(Key, 1, Os)] + end, + lists:foldl(Override, Opts0, OROpts); + _ -> + Opts0 + end; + _ -> + Opts0 + end + end, + %% use ct interface CtRunTestResult=run_ct_run_test(Opts,Config), %% use run_test interface (simulated) ExitStatus=run_ct_script_start(Opts,Config), + check_result(CtRunTestResult,ExitStatus,Opts). run_ct_run_test(Opts,Config) -> @@ -236,9 +263,10 @@ run_ct_run_test(Opts,Config) -> Level = proplists:get_value(trace_level, Config), test_server:format(Level, "~n[RUN #1] Calling ct:run_test(~p) on ~p~n", [Opts, CTNode]), + T0 = now(), CtRunTestResult = rpc:call(CTNode, ct, run_test, [Opts]), - test_server:format(Level, "~n[RUN #1] Got return value ~p~n", - [CtRunTestResult]), + test_server:format(Level, "~n[RUN #1] Got return value ~p after ~p ms~n", + [CtRunTestResult,trunc(timer:now_diff(now(), T0)/1000)]), case rpc:call(CTNode, erlang, whereis, [ct_util_server]) of undefined -> ok; @@ -261,9 +289,10 @@ run_ct_script_start(Opts, Config) -> [common_test, run_test_start_opts, Opts1]), test_server:format(Level, "[RUN #2] Calling ct_run:script_start() on ~p~n", [CTNode]), + T0 = now(), ExitStatus = rpc:call(CTNode, ct_run, script_start, []), - test_server:format(Level, "[RUN #2] Got exit status value ~p~n", - [ExitStatus]), + test_server:format(Level, "[RUN #2] Got exit status value ~p after ~p ms~n", + [ExitStatus,trunc(timer:now_diff(now(), T0)/1000)]), ExitStatus. check_result({_Ok,Failed,{_UserSkipped,_AutoSkipped}},1,_Opts) diff --git a/lib/compiler/src/beam_receive.erl b/lib/compiler/src/beam_receive.erl index 3dd5ed182e..97a9188ee7 100644 --- a/lib/compiler/src/beam_receive.erl +++ b/lib/compiler/src/beam_receive.erl @@ -151,20 +151,20 @@ opt_recv(Is, Regs, D) -> opt_recv([{label,L}=Lbl,{loop_rec,{f,Fail},_}=Loop|Is], D, R0, _, Acc) -> R = regs_kill_not_live(0, R0), - case regs_to_list(R) of - [{y,_}=RefReg] -> - %% We now have the new reference in the Y register RefReg + case regs_empty(R) of + false -> + %% We now have the new reference in Y registers %% and the current instruction is the beginning of a %% receive statement. We must now verify that only messages %% that contain the reference will be matched. - case opt_ref_used(Is, RefReg, Fail, D) of + case opt_ref_used(Is, R, Fail, D) of false -> no; true -> RecvSet = {recv_set,{f,L}}, {yes,reverse(Acc, [RecvSet,Lbl,Loop|Is]),L} end; - [] -> + true -> no end; opt_recv([I|Is], D, R0, L0, Acc) -> @@ -226,9 +226,9 @@ opt_update_regs_bl([{set,Ds,_,_}|Is], Regs0) -> opt_update_regs_bl(Is, Regs); opt_update_regs_bl([], Regs) -> Regs. -%% opt_ref_used([Instruction], RefRegister, FailLabel, LabelIndex) -> true|false +%% opt_ref_used([Instruction], RefRegs, FailLabel, LabelIndex) -> true|false %% Return 'true' if it is certain that only messages that contain the same -%% reference as in RefRegister can be matched out. Otherwise return 'false'. +%% reference as in RefRegs can be matched out. Otherwise return 'false'. %% %% Basically, we follow all possible paths through the receive statement. %% If all paths are safe, we return 'true'. @@ -236,7 +236,7 @@ opt_update_regs_bl([], Regs) -> Regs. %% A branch to FailLabel is safe, because it exits the receive statement %% and no further message may be matched out. %% -%% If a path hits an comparision between RefRegister and part of the message, +%% If a path hits an comparision between RefRegs and part of the message, %% that path is safe (any messages that may be matched further down the %% path is guaranteed to contain the reference). %% @@ -245,11 +245,11 @@ opt_update_regs_bl([], Regs) -> Regs. %% we hit an unrecognized instruction, we also give up and return %% 'false' (the optimization may be unsafe). -opt_ref_used(Is, RefReg, Fail, D) -> +opt_ref_used(Is, RefRegs, Fail, D) -> Done = gb_sets:singleton(Fail), Regs = regs_init_x0(), try - _ = opt_ref_used_1(Is, RefReg, D, Done, Regs), + _ = opt_ref_used_1(Is, RefRegs, D, Done, Regs), true catch throw:not_used -> @@ -258,37 +258,39 @@ opt_ref_used(Is, RefReg, Fail, D) -> %% This functions only returns if all paths through the receive %% statement are safe, and throws an 'not_used' term otherwise. -opt_ref_used_1([{block,Bl}|Is], RefReg, D, Done, Regs0) -> +opt_ref_used_1([{block,Bl}|Is], RefRegs, D, Done, Regs0) -> Regs = opt_ref_used_bl(Bl, Regs0), - opt_ref_used_1(Is, RefReg, D, Done, Regs); -opt_ref_used_1([{test,is_eq_exact,{f,Fail},Args}|Is], RefReg, D, Done0, Regs) -> - Done = opt_ref_used_at(Fail, RefReg, D, Done0, Regs), - case is_ref_msg_comparison(Args, RefReg, Regs) of + opt_ref_used_1(Is, RefRegs, D, Done, Regs); +opt_ref_used_1([{test,is_eq_exact,{f,Fail},Args}|Is], + RefRegs, D, Done0, Regs) -> + Done = opt_ref_used_at(Fail, RefRegs, D, Done0, Regs), + case is_ref_msg_comparison(Args, RefRegs, Regs) of false -> - opt_ref_used_1(Is, RefReg, D, Done, Regs); + opt_ref_used_1(Is, RefRegs, D, Done, Regs); true -> %% The instructions that follow (Is) can only be executed - %% if the message contains the same reference as in RefReg. + %% if the message contains the same reference as in RefRegs. Done end; -opt_ref_used_1([{test,is_ne_exact,{f,Fail},Args}|Is], RefReg, D, Done0, Regs) -> - Done = opt_ref_used_1(Is, RefReg, D, Done0, Regs), - case is_ref_msg_comparison(Args, RefReg, Regs) of +opt_ref_used_1([{test,is_ne_exact,{f,Fail},Args}|Is], + RefRegs, D, Done0, Regs) -> + Done = opt_ref_used_1(Is, RefRegs, D, Done0, Regs), + case is_ref_msg_comparison(Args, RefRegs, Regs) of false -> - opt_ref_used_at(Fail, RefReg, D, Done, Regs); + opt_ref_used_at(Fail, RefRegs, D, Done, Regs); true -> Done end; -opt_ref_used_1([{test,_,{f,Fail},_}|Is], RefReg, D, Done0, Regs) -> - Done = opt_ref_used_at(Fail, RefReg, D, Done0, Regs), - opt_ref_used_1(Is, RefReg, D, Done, Regs); -opt_ref_used_1([{select,_,_,{f,Fail},List}|_], RefReg, D, Done, Regs) -> +opt_ref_used_1([{test,_,{f,Fail},_}|Is], RefRegs, D, Done0, Regs) -> + Done = opt_ref_used_at(Fail, RefRegs, D, Done0, Regs), + opt_ref_used_1(Is, RefRegs, D, Done, Regs); +opt_ref_used_1([{select,_,_,{f,Fail},List}|_], RefRegs, D, Done, Regs) -> Lbls = [F || {f,F} <- List] ++ [Fail], - opt_ref_used_in_all(Lbls, RefReg, D, Done, Regs); -opt_ref_used_1([{label,Lbl}|Is], RefReg, D, Done, Regs) -> + opt_ref_used_in_all(Lbls, RefRegs, D, Done, Regs); +opt_ref_used_1([{label,Lbl}|Is], RefRegs, D, Done, Regs) -> case gb_sets:is_member(Lbl, Done) of true -> Done; - false -> opt_ref_used_1(Is, RefReg, D, Done, Regs) + false -> opt_ref_used_1(Is, RefRegs, D, Done, Regs) end; opt_ref_used_1([{loop_rec_end,_}|_], _, _, Done, _) -> Done; @@ -296,27 +298,25 @@ opt_ref_used_1([_I|_], _RefReg, _D, _Done, _Regs) -> %% The optimization may be unsafe. throw(not_used). -%% is_ref_msg_comparison(Args, RefReg, RegisterSet) -> true|false. +%% is_ref_msg_comparison(Args, RefRegs, RegisterSet) -> true|false. %% Return 'true' if Args denotes a comparison between the %% reference and message or part of the message. -is_ref_msg_comparison([R,RefReg], RefReg, Regs) -> - regs_is_member(R, Regs); -is_ref_msg_comparison([RefReg,R], RefReg, Regs) -> - regs_is_member(R, Regs); -is_ref_msg_comparison([_,_], _, _) -> false. - -opt_ref_used_in_all([L|Ls], RefReg, D, Done0, Regs) -> - Done = opt_ref_used_at(L, RefReg, D, Done0, Regs), - opt_ref_used_in_all(Ls, RefReg, D, Done, Regs); +is_ref_msg_comparison([R1,R2], RefRegs, Regs) -> + (regs_is_member(R2, RefRegs) andalso regs_is_member(R1, Regs)) orelse + (regs_is_member(R1, RefRegs) andalso regs_is_member(R2, Regs)). + +opt_ref_used_in_all([L|Ls], RefRegs, D, Done0, Regs) -> + Done = opt_ref_used_at(L, RefRegs, D, Done0, Regs), + opt_ref_used_in_all(Ls, RefRegs, D, Done, Regs); opt_ref_used_in_all([], _, _, Done, _) -> Done. -opt_ref_used_at(Fail, RefReg, D, Done0, Regs) -> +opt_ref_used_at(Fail, RefRegs, D, Done0, Regs) -> case gb_sets:is_member(Fail, Done0) of true -> Done0; false -> Is = beam_utils:code_at(Fail, D), - Done = opt_ref_used_1(Is, RefReg, D, Done0, Regs), + Done = opt_ref_used_1(Is, RefRegs, D, Done0, Regs), gb_sets:add(Fail, Done) end. @@ -408,15 +408,3 @@ regs_all_members([], _) -> true. regs_is_member({x,N}, {Regs,_}) -> Regs band (1 bsl N) =/= 0; regs_is_member({y,N}, {_,Regs}) -> Regs band (1 bsl N) =/= 0; regs_is_member(_, _) -> false. - -%% regs_to_list(RegisterSet) -> [Register] -%% Convert the register set to an explicit list of registers. -regs_to_list({Xregs,Yregs}) -> - regs_to_list_1(Xregs, 0, x, regs_to_list_1(Yregs, 0, y, [])). - -regs_to_list_1(0, _, _, Acc) -> - Acc; -regs_to_list_1(Regs, N, Tag, Acc) when (Regs band 1) =:= 1 -> - regs_to_list_1(Regs bsr 1, N+1, Tag, [{Tag,N}|Acc]); -regs_to_list_1(Regs, N, Tag, Acc) -> - regs_to_list_1(Regs bsr 1, N+1, Tag, Acc). diff --git a/lib/compiler/test/receive_SUITE.erl b/lib/compiler/test/receive_SUITE.erl index b91f2922fb..e60584d4ab 100644 --- a/lib/compiler/test/receive_SUITE.erl +++ b/lib/compiler/test/receive_SUITE.erl @@ -188,7 +188,7 @@ ref_opt(Config) when is_list(Config) -> ref_opt_1(Config) -> ?line DataDir = ?config(data_dir, Config), ?line PrivDir = ?config(priv_dir, Config), - ?line Sources = filelib:wildcard(filename:join([DataDir,"ref_opt","*.erl"])), + Sources = filelib:wildcard(filename:join([DataDir,"ref_opt","*.{erl,S}"])), ?line test_lib:p_run(fun(Src) -> do_ref_opt(Src, PrivDir) end, Sources), @@ -196,10 +196,15 @@ ref_opt_1(Config) -> do_ref_opt(Source, PrivDir) -> try - {ok,Mod} = c:c(Source, [{outdir,PrivDir}]), + Ext = filename:extension(Source), + {ok,Mod} = compile:file(Source, [report_errors,report_warnings, + {outdir,PrivDir}] ++ + [from_asm || Ext =:= ".S" ]), + Base = filename:rootname(filename:basename(Source), Ext), + code:purge(list_to_atom(Base)), + BeamFile = filename:join(PrivDir, Base), + code:load_abs(BeamFile), ok = Mod:Mod(), - Base = filename:rootname(filename:basename(Source), ".erl"), - BeamFile = filename:join(PrivDir, Base), {beam_file,Mod,_,_,_,Code} = beam_disasm:file(BeamFile), case Base of "no_"++_ -> diff --git a/lib/compiler/test/receive_SUITE_data/ref_opt/yes_14.S b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_14.S new file mode 100644 index 0000000000..fd14228135 --- /dev/null +++ b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_14.S @@ -0,0 +1,71 @@ +{module, yes_14}. %% version = 0 + +{exports, [{f,2},{module_info,0},{module_info,1},{yes_14,0}]}. + +{attributes, []}. + +{labels, 12}. + + +{function, yes_14, 0, 2}. + {label,1}. + {func_info,{atom,yes_14},{atom,yes_14},0}. + {label,2}. + {move,{atom,ok},{x,0}}. + return. + + +{function, f, 2, 4}. + {label,3}. + {func_info,{atom,yes_14},{atom,f},2}. + {label,4}. + {allocate_heap,2,3,2}. + {move,{x,0},{y,1}}. + {put_tuple,2,{y,0}}. + {put,{atom,data}}. + {put,{x,1}}. + {call_ext,0,{extfunc,erlang,make_ref,0}}. % Ref in [x0] + {test_heap,4,1}. + {put_tuple,3,{x,1}}. + {put,{atom,request}}. + {put,{x,0}}. + {put,{y,0}}. + {move,{x,0},{y,0}}. % Ref in [x0,y0] + {move,{y,1},{x,0}}. % Ref in [y0] + {kill,{y,1}}. + send. + {move,{y,0},{x,0}}. % Ref in [x0,y0] + {move,{x,0},{y,1}}. % Ref in [x0,y0,y1] + {label,5}. + {loop_rec,{f,7},{x,0}}. % Ref in [y0,y1] + {test,is_tuple,{f,6},[{x,0}]}. + {test,test_arity,{f,6},[{x,0},2]}. + {get_tuple_element,{x,0},0,{x,1}}. + {get_tuple_element,{x,0},1,{x,2}}. + {test,is_eq_exact,{f,6},[{x,1},{atom,reply}]}. + {test,is_eq_exact,{f,6},[{x,2},{y,1}]}. + remove_message. + {move,{atom,ok},{x,0}}. + {deallocate,2}. + return. + {label,6}. + {loop_rec_end,{f,5}}. + {label,7}. + {wait,{f,5}}. + + +{function, module_info, 0, 9}. + {label,8}. + {func_info,{atom,yes_14},{atom,module_info},0}. + {label,9}. + {move,{atom,yes_14},{x,0}}. + {call_ext_only,1,{extfunc,erlang,get_module_info,1}}. + + +{function, module_info, 1, 11}. + {label,10}. + {func_info,{atom,yes_14},{atom,module_info},1}. + {label,11}. + {move,{x,0},{x,1}}. + {move,{atom,yes_14},{x,0}}. + {call_ext_only,2,{extfunc,erlang,get_module_info,2}}. diff --git a/lib/compiler/test/receive_SUITE_data/ref_opt/yes_5.erl b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_5.erl index 3f02fba6a6..5070e3e546 100644 --- a/lib/compiler/test/receive_SUITE_data/ref_opt/yes_5.erl +++ b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_5.erl @@ -24,11 +24,7 @@ do_call(Process, Label, Request, Timeout) -> {'DOWN', Mref, _, _, Reason} -> exit(Reason) after Timeout -> - erlang:demonitor(Mref), - receive - {'DOWN', Mref, _, _, _} -> true - after 0 -> true - end, + erlang:demonitor(Mref, [flush]), exit(timeout) end catch diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c index fac77308f6..9d43a1d907 100644 --- a/lib/crypto/c_src/crypto.c +++ b/lib/crypto/c_src/crypto.c @@ -74,6 +74,19 @@ # define HAVE_DES_ede3_cfb_encrypt #endif +#if OPENSSL_VERSION_NUMBER >= 0x009080ffL \ + && !defined(OPENSSL_NO_EC) \ + && !defined(OPENSSL_NO_ECDH) \ + && !defined(OPENSSL_NO_ECDSA) +# define HAVE_EC +#endif + +#if defined(HAVE_EC) +#include <openssl/ec.h> +#include <openssl/ecdh.h> +#include <openssl/ecdsa.h> +#endif + #ifdef VALGRIND # include <valgrind/memcheck.h> @@ -192,7 +205,7 @@ static ERL_NIF_TERM rand_bytes_3(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar static ERL_NIF_TERM strong_rand_mpint_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM rand_uniform_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM mod_exp_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM dss_verify(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM dss_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM rsa_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM aes_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM exor(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); @@ -209,13 +222,19 @@ static ERL_NIF_TERM dh_check(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] static ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM dh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM srp_value_B_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM srp_client_secret_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM srp_server_secret_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM srp_user_secret_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM srp_host_secret_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM bf_cfb64_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM bf_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM bf_ecb_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM blowfish_ofb64_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM ec_key_to_term_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM term_to_ec_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM ec_key_generate(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM ecdsa_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM ecdsa_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM ecdh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); /* helpers */ @@ -247,6 +266,11 @@ static void hmac_sha512(unsigned char *key, int klen, unsigned char *dbuf, int dlen, unsigned char *hmacbuf); #endif +#ifdef HAVE_EC +static EC_KEY* ec_key_new(ErlNifEnv* env, ERL_NIF_TERM curve_arg); +static int term2point(ErlNifEnv* env, ERL_NIF_TERM term, + EC_GROUP *group, EC_POINT **pptr); +#endif static int library_refc = 0; /* number of users of this dynamic library */ @@ -311,7 +335,7 @@ static ErlNifFunc nif_funcs[] = { {"strong_rand_mpint_nif", 3, strong_rand_mpint_nif}, {"rand_uniform_nif", 2, rand_uniform_nif}, {"mod_exp_nif", 4, mod_exp_nif}, - {"dss_verify", 4, dss_verify}, + {"dss_verify_nif", 4, dss_verify_nif}, {"rsa_verify_nif", 4, rsa_verify_nif}, {"aes_cbc_crypt", 4, aes_cbc_crypt}, {"exor", 2, exor}, @@ -325,16 +349,120 @@ static ErlNifFunc nif_funcs[] = { {"rsa_private_crypt", 4, rsa_private_crypt}, {"dh_generate_parameters_nif", 2, dh_generate_parameters_nif}, {"dh_check", 1, dh_check}, - {"dh_generate_key_nif", 2, dh_generate_key_nif}, + {"dh_generate_key_nif", 3, dh_generate_key_nif}, {"dh_compute_key_nif", 3, dh_compute_key_nif}, {"srp_value_B_nif", 5, srp_value_B_nif}, - {"srp_client_secret_nif", 7, srp_client_secret_nif}, - {"srp_server_secret_nif", 5, srp_server_secret_nif}, + {"srp_user_secret_nif", 7, srp_user_secret_nif}, + {"srp_host_secret_nif", 5, srp_host_secret_nif}, {"bf_cfb64_crypt", 4, bf_cfb64_crypt}, {"bf_cbc_crypt", 4, bf_cbc_crypt}, {"bf_ecb_crypt", 3, bf_ecb_crypt}, - {"blowfish_ofb64_encrypt", 3, blowfish_ofb64_encrypt} + {"blowfish_ofb64_encrypt", 3, blowfish_ofb64_encrypt}, + + {"ec_key_to_term_nif", 1, ec_key_to_term_nif}, + {"term_to_ec_key_nif", 3, term_to_ec_key_nif}, + {"ec_key_generate", 1, ec_key_generate}, + {"ecdsa_sign_nif", 3, ecdsa_sign_nif}, + {"ecdsa_verify_nif", 4, ecdsa_verify_nif}, + {"ecdh_compute_key_nif", 2, ecdh_compute_key_nif} +}; + +#if defined(HAVE_EC) +struct nid_map { + char *name; + int nid; + ERL_NIF_TERM atom; +}; + +static struct nid_map ec_curves[] = { + /* prime field curves */ + /* secg curves */ + { "secp112r1", NID_secp112r1 }, + { "secp112r2", NID_secp112r2 }, + { "secp128r1", NID_secp128r1 }, + { "secp128r2", NID_secp128r2 }, + { "secp160k1", NID_secp160k1 }, + { "secp160r1", NID_secp160r1 }, + { "secp160r2", NID_secp160r2 }, + /* SECG secp192r1 is the same as X9.62 prime192v1 */ + { "secp192r1", NID_X9_62_prime192v1 }, + { "secp192k1", NID_secp192k1 }, + { "secp224k1", NID_secp224k1 }, + { "secp224r1", NID_secp224r1 }, + { "secp256k1", NID_secp256k1 }, + /* SECG secp256r1 is the same as X9.62 prime256v1 */ + { "secp256r1", NID_X9_62_prime256v1 }, + { "secp384r1", NID_secp384r1 }, + { "secp521r1", NID_secp521r1 }, + /* X9.62 curves */ + { "prime192v1", NID_X9_62_prime192v1 }, + { "prime192v2", NID_X9_62_prime192v2 }, + { "prime192v3", NID_X9_62_prime192v3 }, + { "prime239v1", NID_X9_62_prime239v1 }, + { "prime239v2", NID_X9_62_prime239v2 }, + { "prime239v3", NID_X9_62_prime239v3 }, + { "prime256v1", NID_X9_62_prime256v1 }, + /* characteristic two field curves */ + /* NIST/SECG curves */ + { "sect113r1", NID_sect113r1 }, + { "sect113r2", NID_sect113r2 }, + { "sect131r1", NID_sect131r1 }, + { "sect131r2", NID_sect131r2 }, + { "sect163k1", NID_sect163k1 }, + { "sect163r1", NID_sect163r1 }, + { "sect163r2", NID_sect163r2 }, + { "sect193r1", NID_sect193r1 }, + { "sect193r2", NID_sect193r2 }, + { "sect233k1", NID_sect233k1 }, + { "sect233r1", NID_sect233r1 }, + { "sect239k1", NID_sect239k1 }, + { "sect283k1", NID_sect283k1 }, + { "sect283r1", NID_sect283r1 }, + { "sect409k1", NID_sect409k1 }, + { "sect409r1", NID_sect409r1 }, + { "sect571k1", NID_sect571k1 }, + { "sect571r1", NID_sect571r1 }, + /* X9.62 curves */ + { "c2pnb163v1", NID_X9_62_c2pnb163v1 }, + { "c2pnb163v2", NID_X9_62_c2pnb163v2 }, + { "c2pnb163v3", NID_X9_62_c2pnb163v3 }, + { "c2pnb176v1", NID_X9_62_c2pnb176v1 }, + { "c2tnb191v1", NID_X9_62_c2tnb191v1 }, + { "c2tnb191v2", NID_X9_62_c2tnb191v2 }, + { "c2tnb191v3", NID_X9_62_c2tnb191v3 }, + { "c2pnb208w1", NID_X9_62_c2pnb208w1 }, + { "c2tnb239v1", NID_X9_62_c2tnb239v1 }, + { "c2tnb239v2", NID_X9_62_c2tnb239v2 }, + { "c2tnb239v3", NID_X9_62_c2tnb239v3 }, + { "c2pnb272w1", NID_X9_62_c2pnb272w1 }, + { "c2pnb304w1", NID_X9_62_c2pnb304w1 }, + { "c2tnb359v1", NID_X9_62_c2tnb359v1 }, + { "c2pnb368w1", NID_X9_62_c2pnb368w1 }, + { "c2tnb431r1", NID_X9_62_c2tnb431r1 }, + /* the WAP/WTLS curves + * [unlike SECG, spec has its own OIDs for curves from X9.62] */ + { "wtls1", NID_wap_wsg_idm_ecid_wtls1 }, + { "wtls3", NID_wap_wsg_idm_ecid_wtls3 }, + { "wtls4", NID_wap_wsg_idm_ecid_wtls4 }, + { "wtls5", NID_wap_wsg_idm_ecid_wtls5 }, + { "wtls6", NID_wap_wsg_idm_ecid_wtls6 }, + { "wtls7", NID_wap_wsg_idm_ecid_wtls7 }, + { "wtls8", NID_wap_wsg_idm_ecid_wtls8 }, + { "wtls9", NID_wap_wsg_idm_ecid_wtls9 }, + { "wtls10", NID_wap_wsg_idm_ecid_wtls10 }, + { "wtls11", NID_wap_wsg_idm_ecid_wtls11 }, + { "wtls12", NID_wap_wsg_idm_ecid_wtls12 }, + /* IPSec curves */ + { "ipsec3", NID_ipsec3 }, + { "ipsec4", NID_ipsec4 } +}; + +#define EC_CURVES_CNT (sizeof(ec_curves)/sizeof(struct nid_map)) + +struct nif_ec_key { + EC_KEY *key; }; +#endif ERL_NIF_INIT(crypto,nif_funcs,load,NULL,upgrade,unload) @@ -368,6 +496,7 @@ static ERL_NIF_TERM atom_sha256; static ERL_NIF_TERM atom_sha384; static ERL_NIF_TERM atom_sha512; static ERL_NIF_TERM atom_md5; +static ERL_NIF_TERM atom_md4; static ERL_NIF_TERM atom_ripemd160; static ERL_NIF_TERM atom_error; static ERL_NIF_TERM atom_rsa_pkcs1_padding; @@ -386,6 +515,19 @@ static ERL_NIF_TERM atom_none; static ERL_NIF_TERM atom_notsup; static ERL_NIF_TERM atom_digest; +static ERL_NIF_TERM atom_ec; + +#if defined(HAVE_EC) +static ERL_NIF_TERM atom_prime_field; +static ERL_NIF_TERM atom_characteristic_two_field; +static ERL_NIF_TERM atom_tpbasis; +static ERL_NIF_TERM atom_ppbasis; +static ERL_NIF_TERM atom_onbasis; + +static ErlNifResourceType* res_type_ec_key; +static void ec_key_dtor(ErlNifEnv* env, void* obj); +#endif + /* #define PRINTF_ERR0(FMT) enif_fprintf(stderr, FMT "\n") #define PRINTF_ERR1(FMT, A1) enif_fprintf(stderr, FMT "\n", A1) @@ -415,6 +557,7 @@ static void error_handler(void* null, const char* errstr) static int init(ErlNifEnv* env, ERL_NIF_TERM load_info) { + int i; ErlNifSysInfo sys_info; get_crypto_callbacks_t* funcp; struct crypto_callbacks* ccb; @@ -448,6 +591,7 @@ static int init(ErlNifEnv* env, ERL_NIF_TERM load_info) atom_sha256 = enif_make_atom(env,"sha256"); atom_sha384 = enif_make_atom(env,"sha384"); atom_sha512 = enif_make_atom(env,"sha512"); + atom_md4 = enif_make_atom(env,"md4"); atom_md5 = enif_make_atom(env,"md5"); atom_ripemd160 = enif_make_atom(env,"ripemd160"); atom_error = enif_make_atom(env,"error"); @@ -466,6 +610,23 @@ static int init(ErlNifEnv* env, ERL_NIF_TERM load_info) atom_notsup = enif_make_atom(env,"notsup"); atom_digest = enif_make_atom(env,"digest"); +#if defined(HAVE_EC) + atom_ec = enif_make_atom(env,"ec"); + atom_prime_field = enif_make_atom(env,"prime_field"); + atom_characteristic_two_field = enif_make_atom(env,"characteristic_two_field"); + atom_tpbasis = enif_make_atom(env,"tpbasis"); + atom_ppbasis = enif_make_atom(env,"ppbasis"); + atom_onbasis = enif_make_atom(env,"onbasis"); + + for (i = 0; i < EC_CURVES_CNT; i++) + ec_curves[i].atom = enif_make_atom(env,ec_curves[i].name); + + res_type_ec_key = enif_open_resource_type(env,NULL,"crypto.EC_KEY", + ec_key_dtor, + ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER, + NULL); +#endif + init_digest_types(env); init_algorithms_types(); @@ -549,12 +710,12 @@ static void unload(ErlNifEnv* env, void* priv_data) } static int algos_cnt; -static ERL_NIF_TERM algos[7]; /* increase when extending the list */ +static ERL_NIF_TERM algos[9]; /* increase when extending the list */ static void init_algorithms_types(void) { algos_cnt = 0; - + algos[algos_cnt++] = atom_md4; algos[algos_cnt++] = atom_md5; algos[algos_cnt++] = atom_sha; algos[algos_cnt++] = atom_ripemd160; @@ -570,6 +731,9 @@ static void init_algorithms_types(void) #ifdef HAVE_SHA512 algos[algos_cnt++] = atom_sha512; #endif +#if defined(HAVE_EC) + algos[algos_cnt++] = atom_ec; +#endif } static ERL_NIF_TERM algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) @@ -1631,13 +1795,7 @@ static ERL_NIF_TERM mod_exp_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg return ret; } -static int inspect_mpint(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifBinary* bin) -{ - return enif_inspect_binary(env, term, bin) && - bin->size >= 4 && get_int32(bin->data) == bin->size-4; -} - -static ERL_NIF_TERM dss_verify(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +static ERL_NIF_TERM dss_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (DigestType|none, Data|{digest,Digest}, Signature,Key=[P, Q, G, Y]) */ ErlNifBinary data_bin, sign_bin; BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL, *dsa_y = NULL; @@ -1660,10 +1818,10 @@ static ERL_NIF_TERM dss_verify(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv digest = data_bin.data; } else { - if (!inspect_mpint(env, argv[1], &data_bin)) { + if (!enif_inspect_binary(env, argv[1], &data_bin)) { return enif_make_badarg(env); } - SHA1(data_bin.data+4, data_bin.size-4, hmacbuf); + SHA1(data_bin.data, data_bin.size, hmacbuf); digest = hmacbuf; } } @@ -1675,15 +1833,15 @@ static ERL_NIF_TERM dss_verify(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv return enif_make_badarg(env); } - if (!inspect_mpint(env, argv[2], &sign_bin) + if (!enif_inspect_binary(env, argv[2], &sign_bin) || !enif_get_list_cell(env, argv[3], &head, &tail) - || !get_bn_from_mpint(env, head, &dsa_p) + || !get_bn_from_bin(env, head, &dsa_p) || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_mpint(env, head, &dsa_q) + || !get_bn_from_bin(env, head, &dsa_q) || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_mpint(env, head, &dsa_g) + || !get_bn_from_bin(env, head, &dsa_g) || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_mpint(env, head, &dsa_y) + || !get_bn_from_bin(env, head, &dsa_y) || !enif_is_empty_list(env,tail)) { if (dsa_p) BN_free(dsa_p); @@ -1700,7 +1858,7 @@ static ERL_NIF_TERM dss_verify(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv dsa->priv_key = NULL; dsa->pub_key = dsa_y; i = DSA_verify(0, digest, SHA_DIGEST_LENGTH, - sign_bin.data+4, sign_bin.size-4, dsa); + sign_bin.data, sign_bin.size, dsa); DSA_free(dsa); return(i > 0) ? atom_true : atom_false; } @@ -1826,11 +1984,11 @@ static ERL_NIF_TERM rsa_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM rsa = RSA_new(); - if (!inspect_mpint(env, argv[2], &sign_bin) + if (!enif_inspect_binary(env, argv[2], &sign_bin) || !enif_get_list_cell(env, argv[3], &head, &tail) - || !get_bn_from_mpint(env, head, &rsa->e) + || !get_bn_from_bin(env, head, &rsa->e) || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_mpint(env, head, &rsa->n) + || !get_bn_from_bin(env, head, &rsa->n) || !enif_is_empty_list(env, tail)) { ret = enif_make_badarg(env); @@ -1846,9 +2004,9 @@ static ERL_NIF_TERM rsa_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM } digest = data_bin.data; } - else if (inspect_mpint(env, argv[1], &data_bin)) { + else if (enif_inspect_binary(env, argv[1], &data_bin)) { digest = hmacbuf; - digp->funcp(data_bin.data+4, data_bin.size-4, digest); + digp->funcp(data_bin.data, data_bin.size, digest); } else { ret = enif_make_badarg(env); @@ -1856,7 +2014,7 @@ static ERL_NIF_TERM rsa_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM } i = RSA_verify(digp->NID_type, digest, digp->len, - sign_bin.data+4, sign_bin.size-4, rsa); + sign_bin.data, sign_bin.size, rsa); ret = (i==1 ? atom_true : atom_false); @@ -2001,22 +2159,22 @@ static int get_rsa_private_key(ErlNifEnv* env, ERL_NIF_TERM key, RSA *rsa) ERL_NIF_TERM head, tail; if (!enif_get_list_cell(env, key, &head, &tail) - || !get_bn_from_mpint(env, head, &rsa->e) + || !get_bn_from_bin(env, head, &rsa->e) || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_mpint(env, head, &rsa->n) + || !get_bn_from_bin(env, head, &rsa->n) || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_mpint(env, head, &rsa->d) + || !get_bn_from_bin(env, head, &rsa->d) || (!enif_is_empty_list(env, tail) && (!enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_mpint(env, head, &rsa->p) + || !get_bn_from_bin(env, head, &rsa->p) || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_mpint(env, head, &rsa->q) + || !get_bn_from_bin(env, head, &rsa->q) || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_mpint(env, head, &rsa->dmp1) + || !get_bn_from_bin(env, head, &rsa->dmp1) || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_mpint(env, head, &rsa->dmq1) + || !get_bn_from_bin(env, head, &rsa->dmq1) || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_mpint(env, head, &rsa->iqmp) + || !get_bn_from_bin(env, head, &rsa->iqmp) || !enif_is_empty_list(env, tail)))) { return 0; } @@ -2053,11 +2211,11 @@ static ERL_NIF_TERM rsa_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar digest = data_bin.data; } else { - if (!inspect_mpint(env,argv[1],&data_bin)) { + if (!enif_inspect_binary(env,argv[1],&data_bin)) { return enif_make_badarg(env); } digest = hmacbuf; - digp->funcp(data_bin.data+4, data_bin.size-4, digest); + digp->funcp(data_bin.data, data_bin.size, digest); } rsa = RSA_new(); @@ -2112,10 +2270,10 @@ static ERL_NIF_TERM dss_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar digest = data_bin.data; } else { - if (!inspect_mpint(env,argv[1],&data_bin)) { + if (!enif_inspect_binary(env,argv[1],&data_bin)) { return enif_make_badarg(env); } - SHA1(data_bin.data+4, data_bin.size-4, hmacbuf); + SHA1(data_bin.data, data_bin.size, hmacbuf); digest = hmacbuf; } } @@ -2133,13 +2291,13 @@ static ERL_NIF_TERM dss_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar dsa->pub_key = NULL; if (!enif_get_list_cell(env, argv[2], &head, &tail) - || !get_bn_from_mpint(env, head, &dsa->p) + || !get_bn_from_bin(env, head, &dsa->p) || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_mpint(env, head, &dsa->q) + || !get_bn_from_bin(env, head, &dsa->q) || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_mpint(env, head, &dsa->g) + || !get_bn_from_bin(env, head, &dsa->g) || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_mpint(env, head, &dsa->priv_key) + || !get_bn_from_bin(env, head, &dsa->priv_key) || !enif_is_empty_list(env,tail)) { DSA_free(dsa); return enif_make_badarg(env); @@ -2187,9 +2345,9 @@ static ERL_NIF_TERM rsa_public_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TER if (!enif_inspect_binary(env, argv[0], &data_bin) || !enif_get_list_cell(env, argv[1], &head, &tail) - || !get_bn_from_mpint(env, head, &rsa->e) + || !get_bn_from_bin(env, head, &rsa->e) || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_mpint(env, head, &rsa->n) + || !get_bn_from_bin(env, head, &rsa->n) || !enif_is_empty_list(env,tail) || !rsa_pad(argv[2], &padding)) { @@ -2286,14 +2444,12 @@ static ERL_NIF_TERM dh_generate_parameters_nif(ErlNifEnv* env, int argc, const E } p_len = BN_num_bytes(dh_params->p); g_len = BN_num_bytes(dh_params->g); - p_ptr = enif_make_new_binary(env, p_len+4, &ret_p); - g_ptr = enif_make_new_binary(env, g_len+4, &ret_g); - put_int32(p_ptr, p_len); - put_int32(g_ptr, g_len); - BN_bn2bin(dh_params->p, p_ptr+4); - BN_bn2bin(dh_params->g, g_ptr+4); - ERL_VALGRIND_MAKE_MEM_DEFINED(p_ptr+4, p_len); - ERL_VALGRIND_MAKE_MEM_DEFINED(g_ptr+4, g_len); + p_ptr = enif_make_new_binary(env, p_len, &ret_p); + g_ptr = enif_make_new_binary(env, g_len, &ret_g); + BN_bn2bin(dh_params->p, p_ptr); + BN_bn2bin(dh_params->g, g_ptr); + ERL_VALGRIND_MAKE_MEM_DEFINED(p_ptr, p_len); + ERL_VALGRIND_MAKE_MEM_DEFINED(g_ptr, g_len); DH_free(dh_params); return enif_make_list2(env, ret_p, ret_g); } @@ -2305,9 +2461,9 @@ static ERL_NIF_TERM dh_check(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] ERL_NIF_TERM ret, head, tail; if (!enif_get_list_cell(env, argv[0], &head, &tail) - || !get_bn_from_mpint(env, head, &dh_params->p) + || !get_bn_from_bin(env, head, &dh_params->p) || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_mpint(env, head, &dh_params->g) + || !get_bn_from_bin(env, head, &dh_params->g) || !enif_is_empty_list(env,tail)) { DH_free(dh_params); @@ -2329,19 +2485,21 @@ static ERL_NIF_TERM dh_check(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] } static ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (PrivKey, DHParams=[P,G]) */ +{/* (PrivKey, DHParams=[P,G], Mpint) */ DH* dh_params = DH_new(); int pub_len, prv_len; unsigned char *pub_ptr, *prv_ptr; ERL_NIF_TERM ret, ret_pub, ret_prv, head, tail; + int mpint; /* 0 or 4 */ - if (!(get_bn_from_mpint(env, argv[0], &dh_params->priv_key) + if (!(get_bn_from_bin(env, argv[0], &dh_params->priv_key) || argv[0] == atom_undefined) || !enif_get_list_cell(env, argv[1], &head, &tail) - || !get_bn_from_mpint(env, head, &dh_params->p) + || !get_bn_from_bin(env, head, &dh_params->p) || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_mpint(env, head, &dh_params->g) - || !enif_is_empty_list(env, tail)) { + || !get_bn_from_bin(env, head, &dh_params->g) + || !enif_is_empty_list(env, tail) + || !enif_get_int(env, argv[2], &mpint) || (mpint & ~4)) { DH_free(dh_params); return enif_make_badarg(env); } @@ -2349,14 +2507,16 @@ static ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_ if (DH_generate_key(dh_params)) { pub_len = BN_num_bytes(dh_params->pub_key); prv_len = BN_num_bytes(dh_params->priv_key); - pub_ptr = enif_make_new_binary(env, pub_len+4, &ret_pub); - prv_ptr = enif_make_new_binary(env, prv_len+4, &ret_prv); - put_int32(pub_ptr, pub_len); - put_int32(prv_ptr, prv_len); - BN_bn2bin(dh_params->pub_key, pub_ptr+4); - BN_bn2bin(dh_params->priv_key, prv_ptr+4); - ERL_VALGRIND_MAKE_MEM_DEFINED(pub_ptr+4, pub_len); - ERL_VALGRIND_MAKE_MEM_DEFINED(prv_ptr+4, prv_len); + pub_ptr = enif_make_new_binary(env, pub_len+mpint, &ret_pub); + prv_ptr = enif_make_new_binary(env, prv_len+mpint, &ret_prv); + if (mpint) { + put_int32(pub_ptr, pub_len); pub_ptr += 4; + put_int32(prv_ptr, prv_len); prv_ptr += 4; + } + BN_bn2bin(dh_params->pub_key, pub_ptr); + BN_bn2bin(dh_params->priv_key, prv_ptr); + ERL_VALGRIND_MAKE_MEM_DEFINED(pub_ptr, pub_len); + ERL_VALGRIND_MAKE_MEM_DEFINED(prv_ptr, prv_len); ret = enif_make_tuple2(env, ret_pub, ret_prv); } else { @@ -2374,12 +2534,12 @@ static ERL_NIF_TERM dh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_T ErlNifBinary ret_bin; ERL_NIF_TERM ret, head, tail; - if (!get_bn_from_mpint(env, argv[0], &pubkey) - || !get_bn_from_mpint(env, argv[1], &dh_params->priv_key) + if (!get_bn_from_bin(env, argv[0], &pubkey) + || !get_bn_from_bin(env, argv[1], &dh_params->priv_key) || !enif_get_list_cell(env, argv[2], &head, &tail) - || !get_bn_from_mpint(env, head, &dh_params->p) + || !get_bn_from_bin(env, head, &dh_params->p) || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_mpint(env, head, &dh_params->g) + || !get_bn_from_bin(env, head, &dh_params->g) || !enif_is_empty_list(env, tail)) { ret = enif_make_badarg(env); @@ -2457,7 +2617,7 @@ static ERL_NIF_TERM srp_value_B_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM return ret; } -static ERL_NIF_TERM srp_client_secret_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +static ERL_NIF_TERM srp_user_secret_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (a, u, B, Multiplier, Prime, Exponent, Generator) */ /* <premaster secret> = (B - (k * g^x)) ^ (a + (u * x)) % N @@ -2537,7 +2697,7 @@ static ERL_NIF_TERM srp_client_secret_nif(ErlNifEnv* env, int argc, const ERL_NI return ret; } -static ERL_NIF_TERM srp_server_secret_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +static ERL_NIF_TERM srp_host_secret_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Verifier, b, u, A, Prime) */ /* <premaster secret> = (A * v^u) ^ b % N @@ -2686,7 +2846,554 @@ static ERL_NIF_TERM blowfish_ofb64_encrypt(ErlNifEnv* env, int argc, const ERL_N return ret; } +#if defined(HAVE_EC) +static int term2curve_id(ERL_NIF_TERM nid) +{ + int i; + + for (i = 0; i < EC_CURVES_CNT; i++) + if (ec_curves[i].atom == nid) + return ec_curves[i].nid; + + return 0; +} +static EC_KEY* ec_key_new(ErlNifEnv* env, ERL_NIF_TERM curve_arg) +{ + EC_KEY *key = NULL; + int nid = 0; + int c_arity = -1; + const ERL_NIF_TERM* curve; + ErlNifBinary seed; + BIGNUM *p = NULL; + BIGNUM *a = NULL; + BIGNUM *b = NULL; + BIGNUM *bn_order = NULL; + BIGNUM *cofactor = NULL; + EC_GROUP *group = NULL; + EC_POINT *point = NULL; + + if (enif_is_atom(env, curve_arg)) { + nid = term2curve_id(curve_arg); + if (nid == 0) + return NULL; + key = EC_KEY_new_by_curve_name(nid); + } + else if (enif_is_tuple(env, curve_arg) + && enif_get_tuple(env,curve_arg,&c_arity,&curve) + && c_arity == 5 + && get_bn_from_bin(env, curve[3], &bn_order) + && (curve[4] != atom_none && get_bn_from_bin(env, curve[4], &cofactor))) { + //* {Field, Prime, Point, Order, CoFactor} = Curve */ + + int f_arity = -1; + const ERL_NIF_TERM* field; + int p_arity = -1; + const ERL_NIF_TERM* prime; + + long field_bits; + + /* {A, B, Seed} = Prime */ + if (!enif_get_tuple(env,curve[1],&p_arity,&prime) + || !get_bn_from_bin(env, prime[0], &a) + || !get_bn_from_bin(env, prime[1], &b)) + goto out_err; + + if (!enif_get_tuple(env,curve[0],&f_arity,&field)) + goto out_err; + + if (f_arity == 2 && field[0] == atom_prime_field) { + /* {prime_field, Prime} */ + + if (!get_bn_from_bin(env, field[1], &p)) + goto out_err; + + if (BN_is_negative(p) || BN_is_zero(p)) + goto out_err; + + field_bits = BN_num_bits(p); + if (field_bits > OPENSSL_ECC_MAX_FIELD_BITS) + goto out_err; + + /* create the EC_GROUP structure */ + group = EC_GROUP_new_curve_GFp(p, a, b, NULL); + + } else if (f_arity == 3 && field[0] == atom_characteristic_two_field) { + /* {characteristic_two_field, M, Basis} */ + + int b_arity = -1; + const ERL_NIF_TERM* basis; + unsigned int k1, k2, k3; + + if ((p = BN_new()) == NULL) + goto out_err; + + if (!enif_get_long(env, field[1], &field_bits) + || field_bits > OPENSSL_ECC_MAX_FIELD_BITS) + goto out_err; + + if (enif_get_tuple(env,field[2],&b_arity,&basis)) { + if (b_arity == 2 + && basis[0] == atom_tpbasis + && enif_get_uint(env, basis[1], &k1)) { + /* {tpbasis, k} = Basis */ + + if (!(field_bits > k1 && k1 > 0)) + goto out_err; + + /* create the polynomial */ + if (!BN_set_bit(p, (int)field_bits) + || !BN_set_bit(p, (int)k1) + || !BN_set_bit(p, 0)) + goto out_err; + + } else if (b_arity == 4 + && basis[0] == atom_ppbasis + && enif_get_uint(env, basis[1], &k1) + && enif_get_uint(env, basis[2], &k2) + && enif_get_uint(env, basis[3], &k3)) { + /* {ppbasis, k1, k2, k3} = Basis */ + + if (!(field_bits > k3 && k3 > k2 && k2 > k1 && k1 > 0)) + goto out_err; + + /* create the polynomial */ + if (!BN_set_bit(p, (int)field_bits) + || !BN_set_bit(p, (int)k1) + || !BN_set_bit(p, (int)k2) + || !BN_set_bit(p, (int)k3) + || !BN_set_bit(p, 0)) + goto out_err; + + } else + goto out_err; + } else if (field[2] == atom_onbasis) { + /* onbasis = Basis */ + /* no parameters */ + goto out_err; + + } else + goto out_err; + + group = EC_GROUP_new_curve_GF2m(p, a, b, NULL); + } else + goto out_err; + + if (enif_inspect_binary(env, prime[2], &seed)) { + EC_GROUP_set_seed(group, seed.data, seed.size); + } + + if (!term2point(env, curve[2], group, &point)) + goto out_err; + + if (BN_is_negative(bn_order) + || BN_is_zero(bn_order) + || BN_num_bits(bn_order) > (int)field_bits + 1) + goto out_err; + + if (!EC_GROUP_set_generator(group, point, bn_order, cofactor)) + goto out_err; + + EC_GROUP_set_asn1_flag(group, 0x0); + + key = EC_KEY_new(); + if (!key) + goto out_err; + EC_KEY_set_group(key, group); + } + else { + goto out_err; + } + + + goto out; + +out_err: + if (key) EC_KEY_free(key); + key = NULL; + +out: + /* some OpenSSL structures are mem-dup'ed into the key, + so we have to free our copies here */ + if (p) BN_free(p); + if (a) BN_free(a); + if (b) BN_free(b); + if (bn_order) BN_free(bn_order); + if (cofactor) BN_free(cofactor); + if (group) EC_GROUP_free(group); + + return key; +} + + +static ERL_NIF_TERM bn2term(ErlNifEnv* env, const BIGNUM *bn) +{ + unsigned dlen; + unsigned char* ptr; + ERL_NIF_TERM ret; + + if (!bn) + return atom_undefined; + + dlen = BN_num_bytes(bn); + ptr = enif_make_new_binary(env, dlen, &ret); + BN_bn2bin(bn, ptr); + + return ret; +} + +static ERL_NIF_TERM point2term(ErlNifEnv* env, + const EC_GROUP *group, + const EC_POINT *point, + point_conversion_form_t form) +{ + unsigned dlen; + ErlNifBinary bin; + + dlen = EC_POINT_point2oct(group, point, form, NULL, 0, NULL); + if (dlen == 0) + return atom_undefined; + + if (!enif_alloc_binary(dlen, &bin)) + return enif_make_badarg(env); + + if (!EC_POINT_point2oct(group, point, form, bin.data, bin.size, NULL)) { + enif_release_binary(&bin); + return enif_make_badarg(env); + } + + return enif_make_binary(env, &bin); +} +#endif + +static ERL_NIF_TERM ec_key_to_term_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ +#if defined(HAVE_EC) + struct nif_ec_key *obj; + const EC_GROUP *group; + const EC_POINT *public_key; + const BIGNUM *priv_key = NULL; + ERL_NIF_TERM pub_key = atom_undefined; + + if (!enif_get_resource(env, argv[0], res_type_ec_key, (void **)&obj)) + return enif_make_badarg(env); + + group = EC_KEY_get0_group(obj->key); + public_key = EC_KEY_get0_public_key(obj->key); + priv_key = EC_KEY_get0_private_key(obj->key); + + if (group) { + if (public_key) + pub_key = point2term(env, group, public_key, EC_KEY_get_conv_form(obj->key)); + } + + return enif_make_tuple2(env, bn2term(env, priv_key), pub_key); +#else + return atom_notsup; +#endif +} + +#if defined(HAVE_EC) +static int term2point(ErlNifEnv* env, ERL_NIF_TERM term, + EC_GROUP *group, EC_POINT **pptr) +{ + int ret = 0; + ErlNifBinary bin; + EC_POINT *point; + + if (!enif_inspect_binary(env,term,&bin)) { + return 0; + } + + if ((*pptr = point = EC_POINT_new(group)) == NULL) { + return 0; + } + + /* set the point conversion form */ + EC_GROUP_set_point_conversion_form(group, (point_conversion_form_t)(bin.data[0] & ~0x01)); + + /* extract the ec point */ + if (!EC_POINT_oct2point(group, point, bin.data, bin.size, NULL)) { + EC_POINT_free(point); + *pptr = NULL; + } else + ret = 1; + + return ret; +} +#endif + +static ERL_NIF_TERM term_to_ec_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ +#if defined(HAVE_EC) + ERL_NIF_TERM ret; + EC_KEY *key = NULL; + BIGNUM *priv_key = NULL; + EC_POINT *pub_key = NULL; + struct nif_ec_key *obj; + EC_GROUP *group = NULL; + + if (!(argv[1] == atom_undefined || get_bn_from_bin(env, argv[1], &priv_key)) + || !(argv[2] == atom_undefined || enif_is_binary(env, argv[2]))) { + goto out_err; + } + + key = ec_key_new(env, argv[0]); + + if (!key) { + goto out_err; + } + + if (!group) + group = EC_GROUP_dup(EC_KEY_get0_group(key)); + + if (term2point(env, argv[2], group, &pub_key)) { + if (!EC_KEY_set_public_key(key, pub_key)) { + goto out_err; + } + } + if (argv[1] != atom_undefined + && !BN_is_zero(priv_key)) { + if (!EC_KEY_set_private_key(key, priv_key)) + goto out_err; + + /* calculate public key (if necessary) */ + if (EC_KEY_get0_public_key(key) == NULL) + { + /* the public key was not included in the SEC1 private + * key => calculate the public key */ + pub_key = EC_POINT_new(group); + if (pub_key == NULL + || !EC_POINT_copy(pub_key, EC_GROUP_get0_generator(group)) + || !EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, NULL) + || !EC_KEY_set_public_key(key, pub_key)) + goto out_err; + } + } + + obj = enif_alloc_resource(res_type_ec_key, sizeof(struct nif_ec_key)); + if (!obj) + goto out_err; + + obj->key = key; + ret = enif_make_resource(env, obj); + enif_release_resource(obj); + + goto out; + +out_err: + if (key) EC_KEY_free(key); + ret = enif_make_badarg(env); + +out: + /* some OpenSSL structures are mem-dup'ed into the key, + so we have to free our copies here */ + if (priv_key) BN_clear_free(priv_key); + if (pub_key) EC_POINT_free(pub_key); + if (group) EC_GROUP_free(group); + return ret; +#else + return atom_notsup; +#endif +} + +static ERL_NIF_TERM ec_key_generate(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ +#if defined(HAVE_EC) + EC_KEY *key = ec_key_new(env, argv[0]); + + if (key && EC_KEY_generate_key(key)) { + ERL_NIF_TERM term; + struct nif_ec_key *obj = enif_alloc_resource(res_type_ec_key, sizeof(struct nif_ec_key)); + if (!obj) + return atom_error; + obj->key = key; + term = enif_make_resource(env, obj); + enif_release_resource(obj); + return term; + } + else + return enif_make_badarg(env); +#else + return atom_notsup; +#endif +} + +#if defined(HAVE_EC) +static void ec_key_dtor(ErlNifEnv* env, void* obj) +{ + struct nif_ec_key *key = (struct nif_ec_key*) obj; + EC_KEY_free(key->key); +} +#endif + +static ERL_NIF_TERM ecdsa_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Type, Data|{digest,Digest}, Key) */ +#if defined(HAVE_EC) + ErlNifBinary data_bin, ret_bin; + unsigned char hmacbuf[SHA_DIGEST_LENGTH]; + unsigned int dsa_s_len; + struct nif_ec_key *obj; + int i; + const ERL_NIF_TERM* tpl_terms; + int tpl_arity; + struct digest_type_t *digp; + unsigned char* digest; + + digp = get_digest_type(argv[0]); + if (!digp) { + return enif_make_badarg(env); + } + if (!digp->len) { + return atom_notsup; + } + + if (!enif_get_resource(env, argv[2], res_type_ec_key, (void **)&obj)) + return enif_make_badarg(env); + + if (enif_get_tuple(env, argv[1], &tpl_arity, &tpl_terms)) { + if (tpl_arity != 2 || tpl_terms[0] != atom_digest + || !enif_inspect_binary(env, tpl_terms[1], &data_bin) + || data_bin.size != digp->len) { + + return enif_make_badarg(env); + } + digest = data_bin.data; + } + else { + if (!enif_inspect_binary(env,argv[1],&data_bin)) { + return enif_make_badarg(env); + } + digest = hmacbuf; + digp->funcp(data_bin.data, data_bin.size, digest); + } + + enif_alloc_binary(ECDSA_size(obj->key), &ret_bin); + + i = ECDSA_sign(digp->NID_type, digest, digp->len, + ret_bin.data, &dsa_s_len, obj->key); + if (i) { + if (dsa_s_len != ret_bin.size) { + enif_realloc_binary(&ret_bin, dsa_s_len); + } + return enif_make_binary(env, &ret_bin); + } + else { + enif_release_binary(&ret_bin); + return atom_error; + } +#else + return atom_notsup; +#endif +} + +static ERL_NIF_TERM ecdsa_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Type, Data|{digest,Digest}, Signature, Key) */ +#if defined(HAVE_EC) + ErlNifBinary data_bin, sign_bin; + unsigned char hmacbuf[SHA512_LEN]; + int i; + struct nif_ec_key *obj; + const ERL_NIF_TERM type = argv[0]; + const ERL_NIF_TERM* tpl_terms; + int tpl_arity; + struct digest_type_t* digp = NULL; + unsigned char* digest = NULL; + + digp = get_digest_type(type); + if (!digp) { + return enif_make_badarg(env); + } + if (!digp->len) { + return atom_notsup; + } + + if (!enif_inspect_binary(env, argv[2], &sign_bin) + || !enif_get_resource(env, argv[3], res_type_ec_key, (void **)&obj)) + return enif_make_badarg(env); + + if (enif_get_tuple(env, argv[1], &tpl_arity, &tpl_terms)) { + if (tpl_arity != 2 || tpl_terms[0] != atom_digest + || !enif_inspect_binary(env, tpl_terms[1], &data_bin) + || data_bin.size != digp->len) { + + return enif_make_badarg(env); + } + digest = data_bin.data; + } + else if (enif_inspect_binary(env, argv[1], &data_bin)) { + digest = hmacbuf; + digp->funcp(data_bin.data, data_bin.size, digest); + } + else { + return enif_make_badarg(env); + } + + i = ECDSA_verify(digp->NID_type, digest, digp->len, + sign_bin.data, sign_bin.size, obj->key); + + return (i==1 ? atom_true : atom_false); +#else + return atom_notsup; +#endif +} + +/* + (_OthersPublicKey, _MyPrivateKey) + (_OthersPublicKey, _MyEC_Point) +*/ +static ERL_NIF_TERM ecdh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ +#if defined(HAVE_EC) + ERL_NIF_TERM ret; + unsigned char *p; + struct nif_ec_key *other_key; + int field_size = 0; + int i; + + EC_GROUP *group; + const BIGNUM *priv_key; + EC_POINT *my_ecpoint; + EC_KEY *other_ecdh = NULL; + + if (!enif_get_resource(env, argv[1], res_type_ec_key, (void **)&other_key)) + return enif_make_badarg(env); + + group = EC_GROUP_dup(EC_KEY_get0_group(other_key->key)); + priv_key = EC_KEY_get0_private_key(other_key->key); + + if (!term2point(env, argv[0], group, &my_ecpoint)) { + goto out_err; + } + + if ((other_ecdh = EC_KEY_new()) == NULL + || !EC_KEY_set_group(other_ecdh, group) + || !EC_KEY_set_private_key(other_ecdh, priv_key)) + goto out_err; + + field_size = EC_GROUP_get_degree(group); + if (field_size <= 0) + goto out_err; + + p = enif_make_new_binary(env, (field_size+7)/8, &ret); + i = ECDH_compute_key(p, (field_size+7)/8, my_ecpoint, other_ecdh, NULL); + + if (i < 0) + goto out_err; +out: + if (group) EC_GROUP_free(group); + if (my_ecpoint) EC_POINT_free(my_ecpoint); + if (other_ecdh) EC_KEY_free(other_ecdh); + + return ret; + +out_err: + ret = enif_make_badarg(env); + goto out; +#else + return atom_notsup; +#endif +} /* HMAC */ diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml index 29fc885152..df765ade87 100755..100644 --- a/lib/crypto/doc/src/crypto.xml +++ b/lib/crypto/doc/src/crypto.xml @@ -22,263 +22,222 @@ </legalnotice> <title>crypto</title> - <prepared>Peter Högfeldt</prepared> - <docno></docno> - <date>2000-06-20</date> - <rev>B</rev> </header> <module>crypto</module> <modulesummary>Crypto Functions</modulesummary> <description> <p>This module provides a set of cryptographic functions. </p> - <p>References:</p> <list type="bulleted"> <item> - <p>md4: The MD4 Message Digest Algorithm (RFC 1320)</p> - </item> - <item> - <p>md5: The MD5 Message Digest Algorithm (RFC 1321)</p> - </item> - <item> - <p>sha: Secure Hash Standard (FIPS 180-2)</p> - </item> - <item> - <p>hmac: Keyed-Hashing for Message Authentication (RFC 2104)</p> - </item> - <item> - <p>des: Data Encryption Standard (FIPS 46-3)</p> + <p>Hash functions - + <url href="http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf"> Secure Hash Standard</url>, + <url href="http://www.ietf.org/rfc/rfc1321.txt"> The MD5 Message Digest Algorithm (RFC 1321)</url> and + <url href="http://www.ietf.org/rfc/rfc1320.txt">The MD4 Message Digest Algorithm (RFC 1320)</url> + </p> </item> <item> - <p>aes: Advanced Encryption Standard (AES) (FIPS 197) </p> + <p>Hmac functions - <url href="http://www.ietf.org/rfc/rfc2104.txt"> Keyed-Hashing for Message Authentication (RFC 2104) </url></p> </item> <item> - <p>ecb, cbc, cfb, ofb, ctr: Recommendation for Block Cipher Modes - of Operation (NIST SP 800-38A).</p> + <p>Block ciphers - <url href="http://csrc.nist.gov/groups/ST/toolkit/block_ciphers.html"> </url> DES and AES in + Block Cipher Modes - <url href="http://csrc.nist.gov/groups/ST/toolkit/BCM/index.html"> ECB, CBC, CFB, OFB and CTR </url></p> </item> <item> - <p>rsa: Recommendation for Block Cipher Modes of Operation - (NIST 800-38A)</p> + <p><url href="http://www.ietf.org/rfc/rfc1321.txt"> RSA encryption RFC 1321 </url> </p> </item> <item> - <p>dss: Digital Signature Standard (FIPS 186-2)</p> + <p>Digital signatures <url href="http://csrc.nist.gov/publications/drafts/fips186-3/fips_186-3.pdf">Digital Signature Standard (DSS)</url> and<url href="http://csrc.nist.gov/groups/STM/cavp/documents/dss2/ecdsa2vs.pdf"> Elliptic Curve Digital + Signature Algorithm (ECDSA) </url> </p> </item> <item> - <p>srp: Secure Remote Password Protocol (RFC 2945)</p> + <p><url href="http://www.ietf.org/rfc/rfc2945.txt"> Secure Remote Password Protocol (SRP - RFC 2945) </url></p> </item> - - </list> - <p>The above publications can be found at <url href="http://csrc.nist.gov/publications">NIST publications</url>, at <url href="http://www.ietf.org">IETF</url>. - </p> - <p><em>Types</em></p> - <pre> -byte() = 0 ... 255 -ioelem() = byte() | binary() | iolist() -iolist() = [ioelem()] -Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> - </pre> - <p></p> </description> + + <section> + <title>DATA TYPES </title> + + <p><code>key_value() = integer() | binary() </code></p> + + <p><code>rsa_public() = [key_value()] = [E, N] </code></p> + <p> Where E is the public exponent and N is public modulus. </p> + + <p><code>rsa_private() = [key_value()] = [E, N, D] | [E, N, D, P1, P2, E1, E2, C] </code></p> + <p>Where E is the public exponent, N is public modulus and D is + the private exponent.The longer key format contains redundant + information that will make the calculation faster. P1,P2 are first + and second prime factors. E1,E2 are first and second exponents. C + is the CRT coefficient. Terminology is taken from <url href="http://www.ietf.org/rfc/rfc3477.txt"> RFC 3447</url>.</p> + + <p><code>dss_public() = [key_value()] = [P, Q, G, Y] </code></p> + <p>Where P, Q and G are the dss parameters and Y is the public key.</p> + + <p><code>dss_private() = [key_value()] = [P, Q, G, X] </code></p> + <p>Where P, Q and G are the dss parameters and X is the private key.</p> + + <p><code>dss_public() = [key_value()] =[P, Q, G, Y] </code></p> + + <p><code>srp_public() = key_value() </code></p> + <p>Where is <c>A</c> or <c>B</c> from <url href="http://srp.stanford.edu/design.html">SRP design</url></p> + + <p><code>srp_private() = key_value() </code></p> + <p>Where is <c>a</c> or <c>b</c> from <url href="http://srp.stanford.edu/design.html">SRP design</url></p> + + <p><code>srp_params() = {user, [Generator::binary(), Prime::binary(), Version::atom()]} | + {host, [Verifier::binary(), Generator::binary(), Prime::binary(), Version::atom()]} + | {user, [DerivedKey::binary(), Prime::binary(), Generator::binary(), Version::atom() | [Scrambler:binary()]]} + | {host,[Verifier::binary(), Prime::binary(), Version::atom() | [Scrambler::binary]]} </code></p> + + <p>Where Verifier is <c>v</c>, Generator is <c>g</c> and Prime is<c> N</c>, DerivedKey is <c>X</c>, and Scrambler is + <c>u</c> (optional will be genrated if not provided) from <url href="http://srp.stanford.edu/design.html">SRP design</url> + Version = '3' | '6' | '6a' + </p> + + <p><code>dh_public() = key_value() </code></p> + + <p><code>dh_private() = key_value() </code></p> + + <p><code>dh_params() = [key_value()] = [P, G] </code></p> + + <p><code>ecdh_public() = key_value() </code></p> + + <p><code>ecdh_private() = key_value() </code></p> + + <p><code>ecdh_params() = ec_named_curve() | + {ec_field(), Prime :: key_value(), Point :: key_value(), Order :: integer(), CoFactor :: none | integer()} </code></p> + + <p><code>ec_field() = {prime_field, Prime :: integer()} | + {characteristic_two_field, M :: integer(), Basis :: ec_basis()}</code></p> + + <p><code>ec_basis() = {tpbasis, K :: non_neg_integer()} | + {ppbasis, K1 :: non_neg_integer(), K2 :: non_neg_integer(), K3 :: non_neg_integer()} | + onbasis</code></p> + + <p><code>ec_named_curve() -> + sect571r1| sect571k1| sect409r1| sect409k1| secp521r1| secp384r1| secp224r1| secp224k1| + secp192k1| secp160r2| secp128r2| secp128r1| sect233r1| sect233k1| sect193r2| sect193r1| + sect131r2| sect131r1| sect283r1| sect283k1| sect163r2| secp256k1| secp160k1| secp160r1| + secp112r2| secp112r1| sect113r2| sect113r1| sect239k1| sect163r1| sect163k1| secp256r1| + secp192r1 </code></p> + + <p><code>stream_cipher() = rc4 | aes_ctr </code></p> + + <p><code>block_cipher() = aes_cbc128 | aes_cfb128 | blowfish_cbc | + blowfish_cfb64 | des_cbc | des_cfb | des3_cbc | des3_cbf + | des_ede3 | rc2_cbc </code></p> + + <p><code>stream_key() = aes_key() | rc4_key() </code></p> + + <p><code>block_key() = aes_key() | blowfish_key() | des_key()| des3_key() </code></p> + + <p><code>aes_key() = iodata() </code> Key length is 128, 192 or 256 bits</p> + + <p><code>rc4_key() = iodata() </code> Variable key length from 8 bits up to 2048 bits (usually between 40 and 256)</p> + + <p><code>blowfish_key() = iodata() </code> Variable key length from 32 bits up to 448 bits</p> + + <p><code>des_key() = iodata() </code> Key length is 64 bits (in CBC mode only 8 bits are used)</p> + + <p><code>des3_key() = [binary(), binary(), binary()] </code> Each key part is 64 bits (in CBC mode only 8 bits are used)</p> + + <p><code> message_digest_algorithms() = md5 | ripemd160 | sha | sha224 | sha256 | sha384 | sha512 </code> md4 is aslo supported for hash_init/1 and hash/2. + Note that both md4 and md5 are recommended only for compatibility with existing applications. + </p> + </section> + <funcs> <func> - <name>start() -> ok</name> - <fsummary>Start the crypto server.</fsummary> - <desc> - <p>Starts the crypto server.</p> - </desc> - </func> - <func> - <name>stop() -> ok</name> - <fsummary>Stop the crypto server.</fsummary> - <desc> - <p>Stops the crypto server.</p> - </desc> - </func> - <func> - <name>info() -> [atom()]</name> - <fsummary>Provide a list of available crypto functions.</fsummary> - <desc> - <p>Provides the available crypto functions in terms of a list - of atoms.</p> - </desc> - </func> - <func> - <name>algorithms() -> [atom()]</name> + <name>algorithms() -> [message_digest_algorithms() | md4 | ec]</name> <fsummary>Provide a list of available crypto algorithms.</fsummary> <desc> - <p>Provides the available crypto algorithms in terms of a list - of atoms.</p> - </desc> - </func> - <func> - <name>info_lib() -> [{Name,VerNum,VerStr}]</name> - <fsummary>Provides information about the libraries used by crypto.</fsummary> - <type> - <v>Name = binary()</v> - <v>VerNum = integer()</v> - <v>VerStr = binary()</v> - </type> - <desc> - <p>Provides the name and version of the libraries used by crypto.</p> - <p><c>Name</c> is the name of the library. <c>VerNum</c> is - the numeric version according to the library's own versioning - scheme. <c>VerStr</c> contains a text variant of the version.</p> - <pre> -> <input>info_lib().</input> -[{<<"OpenSSL">>,9469983,<<"OpenSSL 0.9.8a 11 Oct 2005">>}] - </pre> - <note><p> - From OTP R16 the <em>numeric version</em> represents the version of the OpenSSL - <em>header files</em> (<c>openssl/opensslv.h</c>) used when crypto was compiled. - The text variant represents the OpenSSL library used at runtime. - In earlier OTP versions both numeric and text was taken from the library. - </p></note> - </desc> - </func> - <func> - <name>md4(Data) -> Digest</name> - <fsummary>Compute an <c>MD4</c>message digest from <c>Data</c></fsummary> - <type> - <v>Data = iolist() | binary()</v> - <v>Digest = binary()</v> - </type> - <desc> - <p>Computes an <c>MD4</c> message digest from <c>Data</c>, where - the length of the digest is 128 bits (16 bytes).</p> - </desc> - </func> - <func> - <name>md4_init() -> Context</name> - <fsummary>Creates an MD4 context</fsummary> - <type> - <v>Context = binary()</v> - </type> - <desc> - <p>Creates an MD4 context, to be used in subsequent calls to - <c>md4_update/2</c>.</p> - </desc> - </func> - <func> - <name>md4_update(Context, Data) -> NewContext</name> - <fsummary>Update an MD4 <c>Context</c>with <c>Data</c>, and return a <c>NewContext</c></fsummary> - <type> - <v>Data = iolist() | binary()</v> - <v>Context = NewContext = binary()</v> - </type> - <desc> - <p>Updates an MD4 <c>Context</c> with <c>Data</c>, and returns - a <c>NewContext</c>.</p> - </desc> - </func> - <func> - <name>md4_final(Context) -> Digest</name> - <fsummary>Finish the update of an MD4 <c>Context</c>and return the computed <c>MD4</c>message digest</fsummary> - <type> - <v>Context = Digest = binary()</v> - </type> - <desc> - <p>Finishes the update of an MD4 <c>Context</c> and returns - the computed <c>MD4</c> message digest.</p> - </desc> - </func> - <func> - <name>md5(Data) -> Digest</name> - <fsummary>Compute an <c>MD5</c>message digest from <c>Data</c></fsummary> - <type> - <v>Data = iolist() | binary()</v> - <v>Digest = binary()</v> - </type> - <desc> - <p>Computes an <c>MD5</c> message digest from <c>Data</c>, where - the length of the digest is 128 bits (16 bytes).</p> + <p> Can be used to determine if the crypto library has support for elliptic curve (ec) and + which message digest algorithms that are supported.</p> </desc> </func> - <func> - <name>md5_init() -> Context</name> - <fsummary>Creates an MD5 context</fsummary> - <type> - <v>Context = binary()</v> - </type> - <desc> - <p>Creates an MD5 context, to be used in subsequent calls to - <c>md5_update/2</c>.</p> - </desc> - </func> - <func> - <name>md5_update(Context, Data) -> NewContext</name> - <fsummary>Update an MD5 <c>Context</c>with <c>Data</c>, and return a <c>NewContext</c></fsummary> - <type> - <v>Data = iolist() | binary()</v> - <v>Context = NewContext = binary()</v> - </type> - <desc> - <p>Updates an MD5 <c>Context</c> with <c>Data</c>, and returns - a <c>NewContext</c>.</p> - </desc> - </func> - <func> - <name>md5_final(Context) -> Digest</name> - <fsummary>Finish the update of an MD5 <c>Context</c>and return the computed <c>MD5</c>message digest</fsummary> + + <func> + <name>block_encrypt(Type, Key, Ivec, PlainText) -> CipherText</name> + <fsummary>Encrypt <c>PlainText</c>according to <c>Type</c> block cipher</fsummary> <type> - <v>Context = Digest = binary()</v> + <v>Key = block_key() </v> + <v>PlainText = iodata() </v> + <v>IVec = CipherText = binary()</v> </type> <desc> - <p>Finishes the update of an MD5 <c>Context</c> and returns - the computed <c>MD5</c> message digest.</p> + <p>Encrypt <c>PlainText</c>according to <c>Type</c> block cipher. + <c>IVec</c> is an arbitrary initializing vector. + </p> </desc> </func> + <func> - <name>sha(Data) -> Digest</name> - <fsummary>Compute an <c>SHA</c>message digest from <c>Data</c></fsummary> + <name>block_decrypt(Type, Key, Ivec, CipherText) -> PlainText</name> + <fsummary>Decrypt <c>CipherText</c>according to <c>Type</c> block cipher</fsummary> <type> - <v>Data = iolist() | binary()</v> - <v>Digest = binary()</v> + <v>Key = block_key() </v> + <v>PlainText = iodata() </v> + <v>IVec = CipherText = binary()</v> </type> <desc> - <p>Computes an <c>SHA</c> message digest from <c>Data</c>, where - the length of the digest is 160 bits (20 bytes).</p> + <p>Decrypt <c>CipherText</c>according to <c>Type</c> block cipher. + <c>IVec</c> is an arbitrary initializing vector. + </p> </desc> </func> + <func> - <name>sha_init() -> Context</name> - <fsummary>Create an SHA context</fsummary> + <name>compute_key(Type, OthersPublicKey, MyPrivateKey, Params) -> SharedSecret</name> + <fsummary>Computes the shared secret</fsummary> <type> - <v>Context = binary()</v> + <v> Type = dh | ecdh | srp </v> + <v>OthersPublicKey = dh_public() | ecdh_public() | srp_public() </v> + <v>MyPrivate = dh_private() | ecdh_private() | srp_private() </v> + <v>Params = dh_params() | edhc_params() | srp_params() </v> + <v>SharedSecret = binary()</v> </type> <desc> - <p>Creates an SHA context, to be used in subsequent calls to - <c>sha_update/2</c>.</p> + <p>Computes the shared secret from the private key and the other party's public key. + See also <seealso marker="public_key:public_key#compute_key/2">public_key:compute_key/2</seealso> + </p> </desc> </func> + <func> - <name>sha_update(Context, Data) -> NewContext</name> - <fsummary>Update an SHA context</fsummary> + <name>exor(Data1, Data2) -> Result</name> + <fsummary>XOR data</fsummary> <type> - <v>Data = iolist() | binary()</v> - <v>Context = NewContext = binary()</v> + <v>Data1, Data2 = iodata()</v> + <v>Result = binary()</v> </type> <desc> - <p>Updates an SHA <c>Context</c> with <c>Data</c>, and returns - a <c>NewContext</c>.</p> + <p>Performs bit-wise XOR (exclusive or) on the data supplied.</p> </desc> </func> - <func> - <name>sha_final(Context) -> Digest</name> - <fsummary>Finish the update of an SHA context</fsummary> + + <func> + <name>generate_key(Type, Params) -> {PublicKey, PrivateKey} </name> + <name>generate_key(Type, Params, PrivateKey) -> {PublicKey, PrivateKey} </name> + <fsummary>Generates a public keys of type <c>Type</c></fsummary> <type> - <v>Context = Digest = binary()</v> + <v> Type = dh | ecdh | srp </v> + <v>Params = dh_params() | edhc_params() | srp_params() </v> + <v>PublicKey = dh_public() | ecdh_public() | srp_public() </v> + <v>PrivateKey = dh_private() | ecdh_private() | srp_private() </v> </type> <desc> - <p>Finishes the update of an SHA <c>Context</c> and returns - the computed <c>SHA</c> message digest.</p> + <p>Generates public keys of type <c>Type</c>. + See also <seealso marker="public_key:public_key#generate_key/1">public_key:generate_key/1</seealso> + </p> </desc> </func> - <func> + + <func> <name>hash(Type, Data) -> Digest</name> <fsummary></fsummary> <type> - <v>Type = md4 | md5 | ripemd160 | sha | sha224 | sha256 | sha384 | sha512</v> + <v>Type = md4 | message_digest_algorithms()</v> <v>Data = iodata()</v> <v>Digest = binary()</v> </type> @@ -288,11 +247,12 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> is not supported by the underlying OpenSSL implementation.</p> </desc> </func> + <func> <name>hash_init(Type) -> Context</name> <fsummary></fsummary> <type> - <v>Type = md4 | md5 | ripemd160 | sha | sha224 | sha256 | sha384 | sha512</v> + <v>Type = md4 | message_digest_algorithms()</v> </type> <desc> <p>Initializes the context for streaming hash operations. <c>Type</c> determines @@ -302,6 +262,7 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> is not supported by the underlying OpenSSL implementation.</p> </desc> </func> + <func> <name>hash_update(Context, Data) -> NewContext</name> <fsummary></fsummary> @@ -329,38 +290,13 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> function used to generate it.</p> </desc> </func> - <func> - <name>md5_mac(Key, Data) -> Mac</name> - <fsummary>Compute an <c>MD5 MAC</c>message authentification code</fsummary> - <type> - <v>Key = Data = iolist() | binary()</v> - <v>Mac = binary()</v> - </type> - <desc> - <p>Computes an <c>MD5 MAC</c> message authentification code - from <c>Key</c> and <c>Data</c>, where the the length of the - Mac is 128 bits (16 bytes).</p> - </desc> - </func> - <func> - <name>md5_mac_96(Key, Data) -> Mac</name> - <fsummary>Compute an <c>MD5 MAC</c>message authentification code</fsummary> - <type> - <v>Key = Data = iolist() | binary()</v> - <v>Mac = binary()</v> - </type> - <desc> - <p>Computes an <c>MD5 MAC</c> message authentification code - from <c>Key</c> and <c>Data</c>, where the length of the Mac - is 96 bits (12 bytes).</p> - </desc> - </func> + <func> <name>hmac(Type, Key, Data) -> Mac</name> <name>hmac(Type, Key, Data, MacLength) -> Mac</name> <fsummary></fsummary> <type> - <v>Type = md5 | sha | sha224 | sha256 | sha384 | sha512</v> + <v>Type = message_digest_algorithms() </v> <v>Key = iodata()</v> <v>Data = iodata()</v> <v>MacLength = integer()</v> @@ -372,12 +308,13 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> will limit the size of the resultant <c>Mac</c>. </desc> </func> + <func> <name>hmac_init(Type, Key) -> Context</name> <fsummary></fsummary> <type> - <v>Type = md5 | ripemd160 | sha | sha224 | sha256 | sha384 | sha512</v> - <v>Key = iolist() | binary()</v> + <v>Type = message_digest_algorithms()</v> + <v>Key = iodata()</v> <v>Context = binary()</v> </type> <desc> @@ -386,20 +323,26 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> key. The key can be any length.</p> </desc> </func> + <func> <name>hmac_update(Context, Data) -> NewContext</name> <fsummary></fsummary> <type> <v>Context = NewContext = binary()</v> - <v>Data = iolist() | binary()</v> + <v>Data = iodata()</v> </type> <desc> <p>Updates the HMAC represented by <c>Context</c> using the given <c>Data</c>. <c>Context</c> must have been generated using an HMAC init function (such as <seealso marker="#hmac_init/2">hmac_init</seealso>). <c>Data</c> can be any length. <c>NewContext</c> - must be passed into the next call to <c>hmac_update</c>.</p> + must be passed into the next call to <c>hmac_update</c> + or to one of the functions <seealso marker="#hmac_final/1">hmac_final</seealso> and + <seealso marker="#hmac_final_n/1">hmac_final_n</seealso> + </p> + </desc> </func> + <func> <name>hmac_final(Context) -> Mac</name> <fsummary></fsummary> @@ -411,6 +354,7 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> determined by the type of hash function used to generate it.</p> </desc> </func> + <func> <name>hmac_final_n(Context, HashLen) -> Mac</name> <fsummary></fsummary> @@ -423,705 +367,88 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> zero. <c>Mac</c> will be a binary with at most <c>HashLen</c> bytes. Note that if HashLen is greater than the actual number of bytes returned from the underlying hash, the returned hash will have fewer than <c>HashLen</c> bytes.</p> </desc> </func> - <func> - <name>sha_mac(Key, Data) -> Mac</name> - <name>sha_mac(Key, Data, MacLength) -> Mac</name> - <fsummary>Compute an <c>MD5 MAC</c>message authentification code</fsummary> - <type> - <v>Key = Data = iolist() | binary()</v> - <v>Mac = binary()</v> - <v>MacLenength = integer() =< 20 </v> - </type> - <desc> - <p>Computes an <c>SHA MAC</c> message authentification code - from <c>Key</c> and <c>Data</c>, where the default length of the Mac - is 160 bits (20 bytes).</p> - </desc> - </func> - <func> - <name>sha_mac_96(Key, Data) -> Mac</name> - <fsummary>Compute an <c>SHA MAC</c>message authentification code</fsummary> - <type> - <v>Key = Data = iolist() | binary()</v> - <v>Mac = binary()</v> - </type> - <desc> - <p>Computes an <c>SHA MAC</c> message authentification code - from <c>Key</c> and <c>Data</c>, where the length of the Mac - is 96 bits (12 bytes).</p> - </desc> - </func> - <func> - <name>des_cbc_encrypt(Key, IVec, Text) -> Cipher</name> - <fsummary>Encrypt <c>Text</c>according to DES in CBC mode</fsummary> - <type> - <v>Key = Text = iolist() | binary()</v> - <v>IVec = Cipher = binary()</v> - </type> - <desc> - <p>Encrypts <c>Text</c> according to DES in CBC - mode. <c>Text</c> must be a multiple of 64 bits (8 - bytes). <c>Key</c> is the DES key, and <c>IVec</c> is an - arbitrary initializing vector. The lengths of <c>Key</c> and - <c>IVec</c> must be 64 bits (8 bytes).</p> - </desc> - </func> - <func> - <name>des_cbc_decrypt(Key, IVec, Cipher) -> Text</name> - <fsummary>Decrypt <c>Cipher</c>according to DES in CBC mode</fsummary> - <type> - <v>Key = Cipher = iolist() | binary()</v> - <v>IVec = Text = binary()</v> - </type> - <desc> - <p>Decrypts <c>Cipher</c> according to DES in CBC mode. - <c>Key</c> is the DES key, and <c>IVec</c> is an arbitrary - initializing vector. <c>Key</c> and <c>IVec</c> must have - the same values as those used when encrypting. <c>Cipher</c> - must be a multiple of 64 bits (8 bytes). The lengths of - <c>Key</c> and <c>IVec</c> must be 64 bits (8 bytes).</p> - </desc> - </func> - <func> - <name>des_cbc_ivec(Data) -> IVec</name> - <fsummary>Get <c>IVec</c> to be used in next iteration of - <c>des_cbc_[ecrypt|decrypt]</c></fsummary> - <type> - <v>Data = iolist() | binary()</v> - <v>IVec = binary()</v> - </type> - <desc> - <p>Returns the <c>IVec</c> to be used in a next iteration of - <c>des_cbc_[encrypt|decrypt]</c>. <c>Data</c> is the encrypted - data from the previous iteration step.</p> - </desc> - </func> - <func> - <name>des_cfb_encrypt(Key, IVec, Text) -> Cipher</name> - <fsummary>Encrypt <c>Text</c>according to DES in CFB mode</fsummary> - <type> - <v>Key = Text = iolist() | binary()</v> - <v>IVec = Cipher = binary()</v> - </type> - <desc> - <p>Encrypts <c>Text</c> according to DES in 8-bit CFB - mode. <c>Key</c> is the DES key, and <c>IVec</c> is an - arbitrary initializing vector. The lengths of <c>Key</c> and - <c>IVec</c> must be 64 bits (8 bytes).</p> - </desc> - </func> - <func> - <name>des_cfb_decrypt(Key, IVec, Cipher) -> Text</name> - <fsummary>Decrypt <c>Cipher</c>according to DES in CFB mode</fsummary> - <type> - <v>Key = Cipher = iolist() | binary()</v> - <v>IVec = Text = binary()</v> - </type> - <desc> - <p>Decrypts <c>Cipher</c> according to DES in 8-bit CFB mode. - <c>Key</c> is the DES key, and <c>IVec</c> is an arbitrary - initializing vector. <c>Key</c> and <c>IVec</c> must have - the same values as those used when encrypting. The lengths of - <c>Key</c> and <c>IVec</c> must be 64 bits (8 bytes).</p> - </desc> - </func> - <func> - <name>des_cfb_ivec(IVec, Data) -> NextIVec</name> - <fsummary>Get <c>IVec</c> to be used in next iteration of - <c>des_cfb_[ecrypt|decrypt]</c></fsummary> - <type> - <v>IVec = iolist() | binary()</v> - <v>Data = iolist() | binary()</v> - <v>NextIVec = binary()</v> - </type> - <desc> - <p>Returns the <c>IVec</c> to be used in a next iteration of - <c>des_cfb_[encrypt|decrypt]</c>. <c>IVec</c> is the vector - used in the previous iteration step. <c>Data</c> is the encrypted - data from the previous iteration step.</p> - </desc> - </func> - <func> - <name>des3_cbc_encrypt(Key1, Key2, Key3, IVec, Text) -> Cipher</name> - <fsummary>Encrypt <c>Text</c>according to DES3 in CBC mode</fsummary> - <type> - <v>Key1 =Key2 = Key3 Text = iolist() | binary()</v> - <v>IVec = Cipher = binary()</v> - </type> - <desc> - <p>Encrypts <c>Text</c> according to DES3 in CBC - mode. <c>Text</c> must be a multiple of 64 bits (8 - bytes). <c>Key1</c>, <c>Key2</c>, <c>Key3</c>, are the DES - keys, and <c>IVec</c> is an arbitrary initializing - vector. The lengths of each of <c>Key1</c>, <c>Key2</c>, - <c>Key3</c> and <c>IVec</c> must be 64 bits (8 bytes).</p> - </desc> - </func> - <func> - <name>des3_cbc_decrypt(Key1, Key2, Key3, IVec, Cipher) -> Text</name> - <fsummary>Decrypt <c>Cipher</c>according to DES3 in CBC mode</fsummary> - <type> - <v>Key1 = Key2 = Key3 = Cipher = iolist() | binary()</v> - <v>IVec = Text = binary()</v> - </type> - <desc> - <p>Decrypts <c>Cipher</c> according to DES3 in CBC mode. - <c>Key1</c>, <c>Key2</c>, <c>Key3</c> are the DES key, and - <c>IVec</c> is an arbitrary initializing vector. - <c>Key1</c>, <c>Key2</c>, <c>Key3</c> and <c>IVec</c> must - and <c>IVec</c> must have the same values as those used when - encrypting. <c>Cipher</c> must be a multiple of 64 bits (8 - bytes). The lengths of <c>Key1</c>, <c>Key2</c>, - <c>Key3</c>, and <c>IVec</c> must be 64 bits (8 bytes).</p> - </desc> - </func> - <func> - <name>des3_cfb_encrypt(Key1, Key2, Key3, IVec, Text) -> Cipher</name> - <fsummary>Encrypt <c>Text</c>according to DES3 in CFB mode</fsummary> - <type> - <v>Key1 =Key2 = Key3 Text = iolist() | binary()</v> - <v>IVec = Cipher = binary()</v> - </type> - <desc> - <p>Encrypts <c>Text</c> according to DES3 in 8-bit CFB - mode. <c>Key1</c>, <c>Key2</c>, <c>Key3</c>, are the DES - keys, and <c>IVec</c> is an arbitrary initializing - vector. The lengths of each of <c>Key1</c>, <c>Key2</c>, - <c>Key3</c> and <c>IVec</c> must be 64 bits (8 bytes).</p> - <p>May throw exception <c>notsup</c> for old OpenSSL - versions (0.9.7) that does not support this encryption mode.</p> - </desc> - </func> - <func> - <name>des3_cfb_decrypt(Key1, Key2, Key3, IVec, Cipher) -> Text</name> - <fsummary>Decrypt <c>Cipher</c>according to DES3 in CFB mode</fsummary> - <type> - <v>Key1 = Key2 = Key3 = Cipher = iolist() | binary()</v> - <v>IVec = Text = binary()</v> - </type> - <desc> - <p>Decrypts <c>Cipher</c> according to DES3 in 8-bit CFB mode. - <c>Key1</c>, <c>Key2</c>, <c>Key3</c> are the DES key, and - <c>IVec</c> is an arbitrary initializing vector. - <c>Key1</c>, <c>Key2</c>, <c>Key3</c> and <c>IVec</c> must - and <c>IVec</c> must have the same values as those used when - encrypting. The lengths of <c>Key1</c>, <c>Key2</c>, - <c>Key3</c>, and <c>IVec</c> must be 64 bits (8 bytes).</p> - <p>May throw exception <c>notsup</c> for old OpenSSL - versions (0.9.7) that does not support this encryption mode.</p> - </desc> - </func> - - <func> - <name>des_ecb_encrypt(Key, Text) -> Cipher</name> - <fsummary>Encrypt <c>Text</c>according to DES in ECB mode</fsummary> - <type> - <v>Key = Text = iolist() | binary()</v> - <v>Cipher = binary()</v> - </type> - <desc> - <p>Encrypts <c>Text</c> according to DES in ECB mode. - <c>Key</c> is the DES key. The lengths of <c>Key</c> and - <c>Text</c> must be 64 bits (8 bytes).</p> - </desc> - </func> - <func> - <name>des_ecb_decrypt(Key, Cipher) -> Text</name> - <fsummary>Decrypt <c>Cipher</c>according to DES in ECB mode</fsummary> - <type> - <v>Key = Cipher = iolist() | binary()</v> - <v>Text = binary()</v> - </type> - <desc> - <p>Decrypts <c>Cipher</c> according to DES in ECB mode. - <c>Key</c> is the DES key. The lengths of <c>Key</c> and - <c>Cipher</c> must be 64 bits (8 bytes).</p> - </desc> - </func> - - <func> - <name>blowfish_ecb_encrypt(Key, Text) -> Cipher</name> - <fsummary>Encrypt the first 64 bits of <c>Text</c> using Blowfish in ECB mode</fsummary> - <type> - <v>Key = Text = iolist() | binary()</v> - <v>Cipher = binary()</v> - </type> - <desc> - <p>Encrypts the first 64 bits of <c>Text</c> using Blowfish in ECB mode. <c>Key</c> is the Blowfish key. The length of <c>Text</c> must be at least 64 bits (8 bytes).</p> - </desc> - </func> - <func> - <name>blowfish_ecb_decrypt(Key, Text) -> Cipher</name> - <fsummary>Decrypt the first 64 bits of <c>Text</c> using Blowfish in ECB mode</fsummary> - <type> - <v>Key = Text = iolist() | binary()</v> - <v>Cipher = binary()</v> - </type> - <desc> - <p>Decrypts the first 64 bits of <c>Text</c> using Blowfish in ECB mode. <c>Key</c> is the Blowfish key. The length of <c>Text</c> must be at least 64 bits (8 bytes).</p> - </desc> - </func> - - <func> - <name>blowfish_cbc_encrypt(Key, IVec, Text) -> Cipher</name> - <fsummary>Encrypt <c>Text</c> using Blowfish in CBC mode</fsummary> - <type> - <v>Key = Text = iolist() | binary()</v> - <v>IVec = Cipher = binary()</v> - </type> - <desc> - <p>Encrypts <c>Text</c> using Blowfish in CBC mode. <c>Key</c> is the Blowfish key, and <c>IVec</c> is an - arbitrary initializing vector. The length of <c>IVec</c> - must be 64 bits (8 bytes). The length of <c>Text</c> must be a multiple of 64 bits (8 bytes).</p> - </desc> - </func> - <func> - <name>blowfish_cbc_decrypt(Key, IVec, Text) -> Cipher</name> - <fsummary>Decrypt <c>Text</c> using Blowfish in CBC mode</fsummary> - <type> - <v>Key = Text = iolist() | binary()</v> - <v>IVec = Cipher = binary()</v> - </type> - <desc> - <p>Decrypts <c>Text</c> using Blowfish in CBC mode. <c>Key</c> is the Blowfish key, and <c>IVec</c> is an - arbitrary initializing vector. The length of <c>IVec</c> - must be 64 bits (8 bytes). The length of <c>Text</c> must be a multiple 64 bits (8 bytes).</p> - </desc> - </func> - - <func> - <name>blowfish_cfb64_encrypt(Key, IVec, Text) -> Cipher</name> - <fsummary>Encrypt <c>Text</c>using Blowfish in CFB mode with 64 - bit feedback</fsummary> - <type> - <v>Key = Text = iolist() | binary()</v> - <v>IVec = Cipher = binary()</v> - </type> - <desc> - <p>Encrypts <c>Text</c> using Blowfish in CFB mode with 64 bit - feedback. <c>Key</c> is the Blowfish key, and <c>IVec</c> is an - arbitrary initializing vector. The length of <c>IVec</c> - must be 64 bits (8 bytes).</p> - </desc> - </func> - <func> - <name>blowfish_cfb64_decrypt(Key, IVec, Text) -> Cipher</name> - <fsummary>Decrypt <c>Text</c>using Blowfish in CFB mode with 64 - bit feedback</fsummary> - <type> - <v>Key = Text = iolist() | binary()</v> - <v>IVec = Cipher = binary()</v> - </type> - <desc> - <p>Decrypts <c>Text</c> using Blowfish in CFB mode with 64 bit - feedback. <c>Key</c> is the Blowfish key, and <c>IVec</c> is an - arbitrary initializing vector. The length of <c>IVec</c> - must be 64 bits (8 bytes).</p> - </desc> - </func> <func> - <name>blowfish_ofb64_encrypt(Key, IVec, Text) -> Cipher</name> - <fsummary>Encrypt <c>Text</c>using Blowfish in OFB mode with 64 - bit feedback</fsummary> + <name>info_lib() -> [{Name,VerNum,VerStr}]</name> + <fsummary>Provides information about the libraries used by crypto.</fsummary> <type> - <v>Key = Text = iolist() | binary()</v> - <v>IVec = Cipher = binary()</v> + <v>Name = binary()</v> + <v>VerNum = integer()</v> + <v>VerStr = binary()</v> </type> <desc> - <p>Encrypts <c>Text</c> using Blowfish in OFB mode with 64 bit - feedback. <c>Key</c> is the Blowfish key, and <c>IVec</c> is an - arbitrary initializing vector. The length of <c>IVec</c> - must be 64 bits (8 bytes).</p> + <p>Provides the name and version of the libraries used by crypto.</p> + <p><c>Name</c> is the name of the library. <c>VerNum</c> is + the numeric version according to the library's own versioning + scheme. <c>VerStr</c> contains a text variant of the version.</p> + <pre> +> <input>info_lib().</input> +[{<<"OpenSSL">>,9469983,<<"OpenSSL 0.9.8a 11 Oct 2005">>}] + </pre> + <note><p> + From OTP R16 the <em>numeric version</em> represents the version of the OpenSSL + <em>header files</em> (<c>openssl/opensslv.h</c>) used when crypto was compiled. + The text variant represents the OpenSSL library used at runtime. + In earlier OTP versions both numeric and text was taken from the library. + </p></note> </desc> </func> <func> - <name>aes_cfb_128_encrypt(Key, IVec, Text) -> Cipher</name> - <fsummary>Encrypt <c>Text</c>according to AES in Cipher Feedback mode</fsummary> - <type> - <v>Key = Text = iolist() | binary()</v> - <v>IVec = Cipher = binary()</v> - </type> - <desc> - <p>Encrypts <c>Text</c> according to AES in Cipher Feedback - mode (CFB). <c>Key</c> is the - AES key, and <c>IVec</c> is an arbitrary initializing vector. - The lengths of <c>Key</c> and <c>IVec</c> must be 128 bits - (16 bytes).</p> - </desc> - </func> - <func> - <name>aes_cfb_128_decrypt(Key, IVec, Cipher) -> Text</name> - <fsummary>Decrypt <c>Cipher</c>according to AES in Cipher Feedback mode</fsummary> - <type> - <v>Key = Cipher = iolist() | binary()</v> - <v>IVec = Text = binary()</v> - </type> - <desc> - <p>Decrypts <c>Cipher</c> according to AES in Cipher Feedback Mode (CFB). - <c>Key</c> is the AES key, and <c>IVec</c> is an arbitrary - initializing vector. <c>Key</c> and <c>IVec</c> must have - the same values as those used when encrypting. The lengths of - <c>Key</c> and <c>IVec</c> must be 128 bits (16 bytes).</p> - </desc> - </func> - <func> - <name>aes_cbc_128_encrypt(Key, IVec, Text) -> Cipher</name> - <fsummary>Encrypt <c>Text</c>according to AES in Cipher Block Chaining mode</fsummary> - <type> - <v>Key = Text = iolist() | binary()</v> - <v>IVec = Cipher = binary()</v> - </type> - <desc> - <p>Encrypts <c>Text</c> according to AES in Cipher Block Chaining - mode (CBC). <c>Text</c> - must be a multiple of 128 bits (16 bytes). <c>Key</c> is the - AES key, and <c>IVec</c> is an arbitrary initializing vector. - The lengths of <c>Key</c> and <c>IVec</c> must be 128 bits - (16 bytes).</p> - </desc> - </func> - <func> - <name>aes_cbc_128_decrypt(Key, IVec, Cipher) -> Text</name> - <fsummary>Decrypt <c>Cipher</c>according to AES in Cipher Block Chaining mode</fsummary> - <type> - <v>Key = Cipher = iolist() | binary()</v> - <v>IVec = Text = binary()</v> - </type> - <desc> - <p>Decrypts <c>Cipher</c> according to AES in Cipher Block - Chaining mode (CBC). - <c>Key</c> is the AES key, and <c>IVec</c> is an arbitrary - initializing vector. <c>Key</c> and <c>IVec</c> must have - the same values as those used when encrypting. <c>Cipher</c> - must be a multiple of 128 bits (16 bytes). The lengths of - <c>Key</c> and <c>IVec</c> must be 128 bits (16 bytes).</p> - </desc> - </func> - <func> - <name>aes_cbc_ivec(Data) -> IVec</name> - <fsummary>Get <c>IVec</c> to be used in next iteration of - <c>aes_cbc_*_[ecrypt|decrypt]</c></fsummary> - <type> - <v>Data = iolist() | binary()</v> - <v>IVec = binary()</v> - </type> - <desc> - <p>Returns the <c>IVec</c> to be used in a next iteration of - <c>aes_cbc_*_[encrypt|decrypt]</c>. <c>Data</c> is the encrypted - data from the previous iteration step.</p> - </desc> - </func> - <func> - <name>aes_ctr_encrypt(Key, IVec, Text) -> Cipher</name> - <fsummary>Encrypt <c>Text</c>according to AES in Counter mode</fsummary> - <type> - <v>Key = Text = iolist() | binary()</v> - <v>IVec = Cipher = binary()</v> - </type> - <desc> - <p>Encrypts <c>Text</c> according to AES in Counter mode (CTR). <c>Text</c> - can be any number of bytes. <c>Key</c> is the AES key and must be either - 128, 192 or 256 bits long. <c>IVec</c> is an arbitrary initializing vector of 128 bits - (16 bytes).</p> - </desc> - </func> - <func> - <name>aes_ctr_decrypt(Key, IVec, Cipher) -> Text</name> - <fsummary>Decrypt <c>Cipher</c>according to AES in Counter mode</fsummary> - <type> - <v>Key = Cipher = iolist() | binary()</v> - <v>IVec = Text = binary()</v> - </type> - <desc> - <p>Decrypts <c>Cipher</c> according to AES in Counter mode (CTR). <c>Cipher</c> - can be any number of bytes. <c>Key</c> is the AES key and must be either - 128, 192 or 256 bits long. <c>IVec</c> is an arbitrary initializing vector of 128 bits - (16 bytes).</p> - </desc> - </func> - <func> - <name>aes_ctr_stream_init(Key, IVec) -> State</name> - <fsummary></fsummary> - <type> - <v>State = { K, I, E, C }</v> - <v>Key = K = iolist()</v> - <v>IVec = I = E = binary()</v> - <v>C = integer()</v> - </type> - <desc> - <p>Initializes the state for use in streaming AES encryption using Counter mode (CTR). - <c>Key</c> is the AES key and must be either 128, 192, or 256 bts long. <c>IVec</c> is - an arbitrary initializing vector of 128 bits (16 bytes). This state is for use with - <seealso marker="#aes_ctr_stream_encrypt/2">aes_ctr_stream_encrypt</seealso> and - <seealso marker="#aes_ctr_stream_decrypt/2">aes_ctr_stream_decrypt</seealso>.</p> - </desc> - </func> - <func> - <name>aes_ctr_stream_encrypt(State, Text) -> { NewState, Cipher}</name> - <fsummary></fsummary> - <type> - <v>Text = iolist() | binary()</v> - <v>Cipher = binary()</v> - </type> - <desc> - <p>Encrypts <c>Text</c> according to AES in Counter mode (CTR). This function can be - used to encrypt a stream of text using a series of calls instead of requiring all - text to be in memory. <c>Text</c> can be any number of bytes. State is initialized using - <seealso marker="#aes_ctr_stream_init/2">aes_ctr_stream_init</seealso>. <c>NewState</c> is the new streaming - encryption state that must be passed to the next call to <c>aes_ctr_stream_encrypt</c>. - <c>Cipher</c> is the encrypted cipher text.</p> - </desc> - </func> - <func> - <name>aes_ctr_stream_decrypt(State, Cipher) -> { NewState, Text }</name> - <fsummary></fsummary> - <type> - <v>Cipher = iolist() | binary()</v> - <v>Text = binary()</v> - </type> - <desc> - <p>Decrypts <c>Cipher</c> according to AES in Counter mode (CTR). This function can be - used to decrypt a stream of ciphertext using a series of calls instead of requiring all - ciphertext to be in memory. <c>Cipher</c> can be any number of bytes. State is initialized using - <seealso marker="#aes_ctr_stream_init/2">aes_ctr_stream_init</seealso>. <c>NewState</c> is the new streaming - encryption state that must be passed to the next call to <c>aes_ctr_stream_encrypt</c>. - <c>Text</c> is the decrypted data.</p> - </desc> - </func> - <func> - <name>erlint(Mpint) -> N</name> - <name>mpint(N) -> Mpint</name> - <fsummary>Convert between binary multi-precision integer and erlang big integer</fsummary> - <type> - <v>Mpint = binary()</v> - <v>N = integer()</v> - </type> - <desc> - <p>Convert a binary multi-precision integer <c>Mpint</c> to and from - an erlang big integer. A multi-precision integer is a binary - with the following form: - <c><![CDATA[<<ByteLen:32/integer, Bytes:ByteLen/binary>>]]></c> where both - <c>ByteLen</c> and <c>Bytes</c> are big-endian. Mpints are used in - some of the functions in <c>crypto</c> and are not translated - in the API for performance reasons.</p> - </desc> - </func> - <func> - <name>rand_bytes(N) -> binary()</name> - <fsummary>Generate a binary of random bytes</fsummary> - <type> - <v>N = integer()</v> - </type> - <desc> - <p>Generates N bytes randomly uniform 0..255, and returns the - result in a binary. Uses the <c>crypto</c> library pseudo-random - number generator.</p> - </desc> - </func> - <func> - <name>strong_rand_bytes(N) -> binary()</name> - <fsummary>Generate a binary of random bytes</fsummary> - <type> - <v>N = integer()</v> - </type> - <desc> - <p>Generates N bytes randomly uniform 0..255, and returns the - result in a binary. Uses a cryptographically secure prng seeded and - periodically mixed with operating system provided entropy. By default - this is the <c>RAND_bytes</c> method from OpenSSL.</p> - <p>May throw exception <c>low_entropy</c> in case the random generator - failed due to lack of secure "randomness".</p> - </desc> - </func> - <func> - <name>rand_uniform(Lo, Hi) -> N</name> - <fsummary>Generate a random number</fsummary> - <type> - <v>Lo, Hi, N = Mpint | integer()</v> - <v>Mpint = binary()</v> - </type> - <desc> - <p>Generate a random number <c><![CDATA[N, Lo =< N < Hi.]]></c> Uses the - <c>crypto</c> library pseudo-random number generator. The - arguments (and result) can be either erlang integers or binary - multi-precision integers. <c>Hi</c> must be larger than <c>Lo</c>.</p> - </desc> - </func> - <func> - <name>strong_rand_mpint(N, Top, Bottom) -> Mpint</name> - <fsummary>Generate an N bit random number</fsummary> - <type> - <v>N = non_neg_integer()</v> - <v>Top = -1 | 0 | 1</v> - <v>Bottom = 0 | 1</v> - <v>Mpint = binary()</v> - </type> - <desc> - <p>Generate an N bit random number using OpenSSL's - cryptographically strong pseudo random number generator - <c>BN_rand</c>.</p> - <p>The parameter <c>Top</c> places constraints on the most - significant bits of the generated number. If <c>Top</c> is 1, then the - two most significant bits will be set to 1, if <c>Top</c> is 0, the - most significant bit will be 1, and if <c>Top</c> is -1 then no - constraints are applied and thus the generated number may be less than - N bits long.</p> - <p>If <c>Bottom</c> is 1, then the generated number is - constrained to be odd.</p> - <p>May throw exception <c>low_entropy</c> in case the random generator - failed due to lack of secure "randomness".</p> - </desc> - </func> - <func> - <name>mod_exp(N, P, M) -> Result</name> - <fsummary>Perform N ^ P mod M</fsummary> - <type> - <v>N, P, M, Result = Mpint</v> - <v>Mpint = binary()</v> - </type> - <desc> - <p>This function performs the exponentiation <c>N ^ P mod M</c>, - using the <c>crypto</c> library.</p> - </desc> - </func> - <func> - <name>mod_exp_prime(N, P, M) -> Result</name> + <name>mod_pow(N, P, M) -> Result</name> <fsummary>Computes the function: N^P mod M</fsummary> <type> - <v>N, P, M = binary()</v> + <v>N, P, M = binary() | integer()</v> <v>Result = binary() | error</v> </type> <desc> <p>Computes the function <c>N^P mod M</c>.</p> </desc> </func> - <func> - <name>rsa_sign(DataOrDigest, Key) -> Signature</name> - <name>rsa_sign(DigestType, DataOrDigest, Key) -> Signature</name> - <fsummary>Sign the data using rsa with the given key.</fsummary> - <type> - <v>DataOrDigest = Data | {digest,Digest}</v> - <v>Data = Mpint</v> - <v>Digest = binary()</v> - <v>Key = [E, N, D] | [E, N, D, P1, P2, E1, E2, C]</v> - <v>E, N, D = Mpint</v> - <d>Where <c>E</c> is the public exponent, <c>N</c> is public modulus and - <c>D</c> is the private exponent.</d> - <v>P1, P2, E1, E2, C = Mpint</v> - <d>The longer key format contains redundant information that will make - the calculation faster. <c>P1,P2</c> are first and second prime factors. - <c>E1,E2</c> are first and second exponents. <c>C</c> is the CRT coefficient. - Terminology is taken from RFC 3447.</d> - <v>DigestType = md5 | sha | sha224 | sha256 | sha384 | sha512</v> - <d>The default <c>DigestType</c> is sha.</d> - <v>Mpint = binary()</v> - <v>Signature = binary()</v> - </type> - <desc> - <p>Creates a RSA signature with the private key <c>Key</c> - of a digest. The digest is either calculated as a - <c>DigestType</c> digest of <c>Data</c> or a precalculated - binary <c>Digest</c>.</p> - </desc> - </func> <func> - <name>rsa_verify(DataOrDigest, Signature, Key) -> Verified</name> - <name>rsa_verify(DigestType, DataOrDigest, Signature, Key) -> Verified </name> - <fsummary>Verify the digest and signature using rsa with given public key.</fsummary> - <type> - <v>Verified = boolean()</v> - <v>DataOrDigest = Data | {digest|Digest}</v> - <v>Data, Signature = Mpint</v> - <v>Digest = binary()</v> - <v>Key = [E, N]</v> - <v>E, N = Mpint</v> - <d>Where <c>E</c> is the public exponent and <c>N</c> is public modulus.</d> - <v>DigestType = md5 | sha | sha224 | sha256 | sha384 | sha512</v> - <d>The default <c>DigestType</c> is sha.</d> - <v>Mpint = binary()</v> - </type> - <desc> - <p>Verifies that a digest matches the RSA signature using the - signer's public key <c>Key</c>. - The digest is either calculated as a <c>DigestType</c> - digest of <c>Data</c> or a precalculated binary <c>Digest</c>.</p> - <p>May throw exception <c>notsup</c> in case the chosen <c>DigestType</c> - is not supported by the underlying OpenSSL implementation.</p> - </desc> - </func> - - <func> - <name>rsa_public_encrypt(PlainText, PublicKey, Padding) -> ChipherText</name> - <fsummary>Encrypts Msg using the public Key.</fsummary> - <type> - <v>PlainText = binary()</v> - <v>PublicKey = [E, N]</v> - <v>E, N = Mpint</v> - <d>Where <c>E</c> is the public exponent and <c>N</c> is public modulus.</d> - <v>Padding = rsa_pkcs1_padding | rsa_pkcs1_oaep_padding | rsa_no_padding</v> - <v>ChipherText = binary()</v> - </type> - <desc> - <p>Encrypts the <c>PlainText</c> (usually a session key) using the <c>PublicKey</c> - and returns the cipher. The <c>Padding</c> decides what padding mode is used, - <c>rsa_pkcs1_padding</c> is PKCS #1 v1.5 currently the most - used mode and <c>rsa_pkcs1_oaep_padding</c> is EME-OAEP as - defined in PKCS #1 v2.0 with SHA-1, MGF1 and an empty encoding - parameter. This mode is recommended for all new applications. - The size of the <c>Msg</c> must be less - than <c>byte_size(N)-11</c> if - <c>rsa_pkcs1_padding</c> is used, <c>byte_size(N)-41</c> if - <c>rsa_pkcs1_oaep_padding</c> is used and <c>byte_size(N)</c> if <c>rsa_no_padding</c> - is used. - Where byte_size(N) is the size part of an <c>Mpint-1</c>. - </p> - </desc> + <name>next_iv(Type, Data) -> </name> + <fsummary></fsummary> + <type> + <v>Type = des_cbc | aes_cbc</v> + <v>Data = iodata()</v> + </type> + <desc> + <p>Returns the initialization vector to be used in the next + iteration of encrypt/decrypt of type <c>Type</c>. Data is the + encrypted data from the previous iteration step.</p> + </desc> </func> <func> - <name>rsa_private_decrypt(ChipherText, PrivateKey, Padding) -> PlainText</name> + <name>private_decrypt(Type, ChipherText, PrivateKey, Padding) -> PlainText</name> <fsummary>Decrypts ChipherText using the private Key.</fsummary> <type> + <v>Type = rsa</v> <v>ChipherText = binary()</v> - <v>PrivateKey = [E, N, D] | [E, N, D, P1, P2, E1, E2, C]</v> - <v>E, N, D = Mpint</v> - <d>Where <c>E</c> is the public exponent, <c>N</c> is public modulus and - <c>D</c> is the private exponent.</d> - <v>P1, P2, E1, E2, C = Mpint</v> - <d>The longer key format contains redundant information that will make - the calculation faster. <c>P1,P2</c> are first and second prime factors. - <c>E1,E2</c> are first and second exponents. <c>C</c> is the CRT coefficient. - Terminology is taken from RFC 3447.</d> + <v>PrivateKey = rsa_private()</v> <v>Padding = rsa_pkcs1_padding | rsa_pkcs1_oaep_padding | rsa_no_padding</v> <v>PlainText = binary()</v> </type> <desc> - <p>Decrypts the <c>ChipherText</c> (usually a session key encrypted with - <seealso marker="#rsa_public_encrypt/3">rsa_public_encrypt/3</seealso>) + <p>Decrypts the <c>ChipherText</c> (usually a session key encrypted with + <seealso marker="#public_encrypt/3">public_encrypt/3</seealso>) using the <c>PrivateKey</c> and returns the message. The <c>Padding</c> is the padding mode that was - used to encrypt the data, - see <seealso marker="#rsa_public_encrypt/3">rsa_public_encrypt/3</seealso>. + used to encrypt the data, + see <seealso marker="#public_encrypt/3">public_encrypt/3</seealso>. + See also <seealso marker="public_key:public_key#decrypt_private/2">public_key:decrypt_private/[2,3]</seealso> </p> </desc> </func> + <func> - <name>rsa_private_encrypt(PlainText, PrivateKey, Padding) -> ChipherText</name> + <name>private_encrypt(Type, PlainText, PrivateKey, Padding) -> ChipherText</name> <fsummary>Encrypts Msg using the private Key.</fsummary> <type> + <v>Type = rsa</v> <v>PlainText = binary()</v> - <v>PrivateKey = [E, N, D] | [E, N, D, P1, P2, E1, E2, C]</v> - <v>E, N, D = Mpint</v> - <d>Where <c>E</c> is the public exponent, <c>N</c> is public modulus and - <c>D</c> is the private exponent.</d> - <v>P1, P2, E1, E2, C = Mpint</v> - <d>The longer key format contains redundant information that will make - the calculation faster. <c>P1,P2</c> are first and second prime factors. - <c>E1,E2</c> are first and second exponents. <c>C</c> is the CRT coefficient. - Terminology is taken from RFC 3447.</d> + <v>PrivateKey = rsa_private()</v> <v>Padding = rsa_pkcs1_padding | rsa_no_padding</v> <v>ChipherText = binary()</v> </type> @@ -1131,316 +458,289 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> <c>rsa_pkcs1_padding</c> is PKCS #1 v1.5 currently the most used mode. The size of the <c>Msg</c> must be less than <c>byte_size(N)-11</c> if - <c>rsa_pkcs1_padding</c> is used, and <c>byte_size(N)</c> if <c>rsa_no_padding</c> - is used. Where byte_size(N) is the size part of an <c>Mpint-1</c>. + <c>rsa_pkcs1_padding</c> is used, and <c>byte_size(N)</c> if <c>rsa_no_padding</c> + is used. + See also <seealso marker="public_key:public_key#encrypt_private/2">public_key:encrypt_private/[2,3]</seealso> </p> </desc> </func> - <func> - <name>rsa_public_decrypt(ChipherText, PublicKey, Padding) -> PlainText</name> + <name>public_decrypt(Type, ChipherText, PublicKey, Padding) -> PlainText</name> <fsummary>Decrypts ChipherText using the public Key.</fsummary> <type> + <v>Type = rsa</v> <v>ChipherText = binary()</v> - <v>PublicKey = [E, N]</v> - <v>E, N = Mpint</v> - <d>Where <c>E</c> is the public exponent and <c>N</c> is public modulus</d> + <v>PublicKey = rsa_public() </v> <v>Padding = rsa_pkcs1_padding | rsa_no_padding</v> <v>PlainText = binary()</v> </type> <desc> - <p>Decrypts the <c>ChipherText</c> (encrypted with - <seealso marker="#rsa_private_encrypt/3">rsa_private_encrypt/3</seealso>) + <p>Decrypts the <c>ChipherText</c> (encrypted with + <seealso marker="#private_encrypt/3">private_encrypt/3</seealso>) using the <c>PrivateKey</c> and returns the message. The <c>Padding</c> is the padding mode that was - used to encrypt the data, - see <seealso marker="#rsa_private_encrypt/3">rsa_private_encrypt/3</seealso>. + used to encrypt the data, + see <seealso marker="#private_encrypt/3">private_encrypt/3</seealso>. + See also <seealso marker="public_key:public_key#decrypt_public/2">public_key:decrypt_public/[2,3]</seealso> </p> </desc> </func> - + <func> - <name>dss_sign(DataOrDigest, Key) -> Signature</name> - <name>dss_sign(DigestType, DataOrDigest, Key) -> Signature</name> - <fsummary>Sign the data using dsa with given private key.</fsummary> + <name>public_encrypt(Type, PlainText, PublicKey, Padding) -> ChipherText</name> + <fsummary>Encrypts Msg using the public Key.</fsummary> <type> - <v>DigestType = sha</v> - <v>DataOrDigest = Mpint | {digest,Digest}</v> - <v>Key = [P, Q, G, X]</v> - <v>P, Q, G, X = Mpint</v> - <d> Where <c>P</c>, <c>Q</c> and <c>G</c> are the dss - parameters and <c>X</c> is the private key.</d> - <v>Digest = binary() with length 20 bytes</v> - <v>Signature = binary()</v> + <v>Type = rsa</v> + <v>PlainText = binary()</v> + <v>PublicKey = rsa_public()</v> + <v>Padding = rsa_pkcs1_padding | rsa_pkcs1_oaep_padding | rsa_no_padding</v> + <v>ChipherText = binary()</v> </type> <desc> - <p>Creates a DSS signature with the private key <c>Key</c> of - a digest. The digest is either calculated as a SHA1 - digest of <c>Data</c> or a precalculated binary <c>Digest</c>.</p> - <p>A deprecated feature is having <c>DigestType = 'none'</c> - in which case <c>DataOrDigest</c> is a precalculated SHA1 - digest.</p> + <p>Encrypts the <c>PlainText</c> (usually a session key) using the <c>PublicKey</c> + and returns the <c>CipherText</c>. The <c>Padding</c> decides what padding mode is used, + <c>rsa_pkcs1_padding</c> is PKCS #1 v1.5 currently the most + used mode and <c>rsa_pkcs1_oaep_padding</c> is EME-OAEP as + defined in PKCS #1 v2.0 with SHA-1, MGF1 and an empty encoding + parameter. This mode is recommended for all new applications. + The size of the <c>Msg</c> must be less + than <c>byte_size(N)-11</c> if + <c>rsa_pkcs1_padding</c> is used, <c>byte_size(N)-41</c> if + <c>rsa_pkcs1_oaep_padding</c> is used and <c>byte_size(N)</c> if <c>rsa_no_padding</c> + is used. + See also <seealso marker="public_key:public_key#encrypt_public/2">public_key:encrypt_public/[2,3]</seealso> + </p> </desc> </func> <func> - <name>dss_verify(DataOrDigest, Signature, Key) -> Verified</name> - <name>dss_verify(DigestType, DataOrDigest, Signature, Key) -> Verified</name> - <fsummary>Verify the data and signature using dsa with given public key.</fsummary> + <name>rand_bytes(N) -> binary()</name> + <fsummary>Generate a binary of random bytes</fsummary> <type> - <v>Verified = boolean()</v> - <v>DigestType = sha</v> - <v>DataOrDigest = Mpint | {digest,Digest}</v> - <v>Data = Mpint | ShaDigest</v> - <v>Signature = Mpint</v> - <v>Key = [P, Q, G, Y]</v> - <v>P, Q, G, Y = Mpint</v> - <d> Where <c>P</c>, <c>Q</c> and <c>G</c> are the dss - parameters and <c>Y</c> is the public key.</d> - <v>Digest = binary() with length 20 bytes</v> + <v>N = integer()</v> </type> <desc> - <p>Verifies that a digest matches the DSS signature using the - public key <c>Key</c>. The digest is either calculated as a SHA1 - digest of <c>Data</c> or is a precalculated binary <c>Digest</c>.</p> - <p>A deprecated feature is having <c>DigestType = 'none'</c> - in which case <c>DataOrDigest</c> is a precalculated SHA1 - digest binary.</p> + <p>Generates N bytes randomly uniform 0..255, and returns the + result in a binary. Uses the <c>crypto</c> library pseudo-random + number generator.</p> </desc> </func> - <func> - <name>rc2_cbc_encrypt(Key, IVec, Text) -> Cipher</name> - <fsummary>Encrypt <c>Text</c>according to RC2 in CBC mode</fsummary> + <func> + <name>rand_uniform(Lo, Hi) -> N</name> + <fsummary>Generate a random number</fsummary> <type> - <v>Key = Text = iolist() | binary()</v> - <v>Ivec = Cipher = binary()</v> + <v>Lo, Hi, N = integer()</v> </type> <desc> - <p>Encrypts <c>Text</c> according to RC2 in CBC mode.</p> + <p>Generate a random number <c><![CDATA[N, Lo =< N < Hi.]]></c> Uses the + <c>crypto</c> library pseudo-random number generator. + <c>Hi</c> must be larger than <c>Lo</c>.</p> </desc> </func> <func> - <name>rc2_cbc_decrypt(Key, IVec, Cipher) -> Text</name> - <fsummary>Decrypts <c>Cipher</c>according to RC2 in CBC mode</fsummary> + <name>sign(Algorithm, DigestType, Msg, Key) -> binary()</name> + <fsummary> Create digital signature.</fsummary> <type> - <v>Key = Text = iolist() | binary()</v> - <v>Ivec = Cipher = binary()</v> + <v>Algorithm = rsa | dss | ecdsa </v> + <v>Msg = binary() | {digest,binary()}</v> + <d>The msg is either the binary "plain text" data to be + signed or it is the hashed value of "plain text" i.e. the + digest.</d> + <v>DigestType = digest_type()</v> + <v>Key = rsa_private_key() | dsa_private_key() | ec_private_key()</v> </type> <desc> - <p>Decrypts <c>Cipher</c> according to RC2 in CBC mode.</p> + <p> Creates a digital signature.</p> + See also <seealso marker="public_key:public_key#sign/3">public_key:sign/3</seealso> </desc> </func> - + <func> - <name>rc4_encrypt(Key, Data) -> Result</name> - <fsummary>Encrypt data using RC4</fsummary> + <name>start() -> ok</name> + <fsummary> Equivalent to application:start(crypto). </fsummary> + <desc> + <p> Equivalent to application:start(crypto).</p> + </desc> + </func> + <func> + <name>stop() -> ok</name> + <fsummary> Equivalent to application:stop(crypto).</fsummary> + <desc> + <p> Equivalent to application:stop(crypto).</p> + </desc> + </func> + + <func> + <name>strong_rand_bytes(N) -> binary()</name> + <fsummary>Generate a binary of random bytes</fsummary> <type> - <v>Key, Data = iolist() | binary()</v> - <v>Result = binary()</v> + <v>N = integer()</v> </type> <desc> - <p>Encrypts the data with RC4 symmetric stream encryption. - Since it is symmetric, the same function is used for - decryption.</p> + <p>Generates N bytes randomly uniform 0..255, and returns the + result in a binary. Uses a cryptographically secure prng seeded and + periodically mixed with operating system provided entropy. By default + this is the <c>RAND_bytes</c> method from OpenSSL.</p> + <p>May throw exception <c>low_entropy</c> in case the random generator + failed due to lack of secure "randomness".</p> </desc> </func> - <func> - <name>dh_generate_key(DHParams) -> {PublicKey,PrivateKey} </name> - <name>dh_generate_key(PrivateKey, DHParams) -> {PublicKey,PrivateKey} </name> - <fsummary>Generates a Diffie-Hellman public key</fsummary> + <name>stream_init(Type, Key) -> State</name> + <fsummary></fsummary> <type> - <v>DHParameters = [P, G]</v> - <v>P, G = Mpint</v> - <d> Where <c>P</c> is the shared prime number and <c>G</c> is the shared generator.</d> - <v>PublicKey, PrivateKey = Mpint()</v> + <v>Type rc4 </v> + <v>State = opaque() </v> + <v>Key = iodata()</v> + <v>IVec = binary()</v> </type> <desc> - <p>Generates a Diffie-Hellman <c>PublicKey</c> and <c>PrivateKey</c> (if not given). - </p> + <p>Initializes the state for use in RC4 stream encryption + <seealso marker="#stream_encrypt/2">stream_encrypt</seealso> and + <seealso marker="#stream_decrypt/2">stream_decrypt</seealso></p> </desc> </func> - <func> - <name>dh_compute_key(OthersPublicKey, MyPrivateKey, DHParams) -> SharedSecret</name> - <fsummary>Computes the shared secret</fsummary> + <func> + <name>stream_init(Type, Key, IVec) -> State</name> + <fsummary></fsummary> <type> - <v>DHParameters = [P, G]</v> - <v>P, G = Mpint</v> - <d> Where <c>P</c> is the shared prime number and <c>G</c> is the shared generator.</d> - <v>OthersPublicKey, MyPrivateKey = Mpint()</v> - <v>SharedSecret = binary()</v> + <v>Type aes_ctr </v> + <v>State = opaque() </v> + <v>Key = iodata()</v> + <v>IVec = binary()</v> </type> <desc> - <p>Computes the shared secret from the private key and the other party's public key. - </p> + <p>Initializes the state for use in streaming AES encryption using Counter mode (CTR). + <c>Key</c> is the AES key and must be either 128, 192, or 256 bts long. <c>IVec</c> is + an arbitrary initializing vector of 128 bits (16 bytes). This state is for use with + <seealso marker="#stream_encrypt/2">stream_encrypt</seealso> and + <seealso marker="#stream_decrypt/2">stream_decrypt</seealso>.</p> </desc> </func> - + <func> - <name>srp_generate_key(Generator, Prime, Version) -> {PublicKey, PrivateKey} </name> - <name>srp_generate_key(Generator, Prime, Version, Private) -> {PublicKey, PrivateKey} </name> - <name>srp_generate_key(Verifier, Generator, Prime, Version) -> {PublicKey, PrivateKey} </name> - <name>srp_generate_key(Verifier, Generator, Prime, Version, Private) -> {PublicKey, PrivateKey} </name> - <fsummary>Generates SRP public keys</fsummary> + <name>stream_encrypt(State, PlainText) -> { NewState, CipherText}</name> + <fsummary></fsummary> <type> - <v>Verifier = binary()</v> - <d>Parameter v from <url href="http://srp.stanford.edu/design.html">SRP design</url> - </d> - <v>Generator = binary() </v> - <d>Parameter g from <url href="http://srp.stanford.edu/design.html">SRP design</url> - </d> - <v>Prime = binary() </v> - <d>Parameter N from <url href="http://srp.stanford.edu/design.html">SRP design</url> - </d> - <v>Version = '3' | '6' | '6a' </v> - <d>SRP version, TLS SRP cipher suites uses '6a'.</d> - <v>PublicKey = binary()</v> - <d> Parameter A or B from <url href="http://srp.stanford.edu/design.html">SRP design</url></d> - <v>Private = PrivateKey = binary() - generated if not supplied</v> - <d>Parameter a or b from <url href="http://srp.stanford.edu/design.html">SRP design</url></d> + <v>Text = iodata()</v> + <v>CipherText = binary()</v> </type> <desc> - <p>Generates SRP public keys for the client side (first argument is Generator) - or for the server side (first argument is Verifier).</p> + <p>Encrypts <c>PlainText</c> according to the stream cipher <c>Type</c> specified in stream_init/3. + <c>Text</c> can be any number of bytes. The initial <c>State</c> is created using + <seealso marker="#stream_init/2">stream_init</seealso>. + <c>NewState</c> must be passed into the next call to <c>stream_encrypt</c>.</p> </desc> </func> <func> - <name>srp_compute_key(DerivedKey, Prime, Generator, - ClientPublic, ClientPrivate, ServerPublic, Version) -> SessionKey</name> - <name>srp_compute_key(DerivedKey, Prime, Generator, - ClientPublic, ClientPrivate, ServerPublic, Version, Scrambler) -> SessionKey</name> - <name>srp_compute_key(Verifier, Prime, - ClientPublic, ServerPublic, ServerPrivate, Version, Scrambler)-> SessionKey</name> - <name>srp_compute_key(Verifier, Prime, - ClientPublic, ServerPublic, ServerPrivate, Version) -> SessionKey</name> - - <fsummary>Computes SRP session key</fsummary> + <name>stream_decrypt(State, CipherText) -> { NewState, PlainText }</name> + <fsummary></fsummary> <type> - <v>DerivedKey = binary()</v> - <d>Parameter x from <url href="http://srp.stanford.edu/design.html">SRP design</url> - </d> - <v>Verifier = binary()</v> - <d>Parameter v from <url href="http://srp.stanford.edu/design.html">SRP design</url> - </d> - <v>Prime = binary() </v> - <d>Parameter N from <url href="http://srp.stanford.edu/design.html">SRP design</url> - </d> - <v>Generator = binary() </v> - <d>Parameter g from <url href="http://srp.stanford.edu/design.html">SRP design</url> - </d> - <v>ClientPublic = binary() </v> - <d>Parameter A from <url href="http://srp.stanford.edu/design.html">SRP design</url> - </d> - <v>ClientPrivate = binary() </v> - <d>Parameter a from <url href="http://srp.stanford.edu/design.html">SRP design</url> - </d> - <v>ServerPublic = binary() </v> - <d>Parameter B from <url href="http://srp.stanford.edu/design.html">SRP design</url> - </d> - <v>ServerPrivate = binary() </v> - <d>Parameter b from <url href="http://srp.stanford.edu/design.html">SRP design</url> - </d> - <v>Version = '3' | '6' | '6a' </v> - <d>SRP version, TLS SRP cipher suites uses '6a'.</d> - <v>SessionKey = binary()</v> - <d>Result K from <url href="http://srp.stanford.edu/design.html">SRP design</url> - </d> + <v>CipherText = iodata()</v> + <v>PlainText = binary()</v> </type> <desc> - <p> - Computes the SRP session key (shared secret) for the client side (first argument is DerivedKey) - or for the server side (first argument is Verifier). Also used - as premaster secret by TLS-SRP ciher suites. - </p> + <p>Decrypts <c>CipherText</c> according to the stream cipher <c>Type</c> specified in stream_init/3. + <c>PlainText</c> can be any number of bytes. The initial <c>State</c> is created using + <seealso marker="#stream_init/2">stream_init</seealso>. + <c>NewState</c> must be passed into the next call to <c>stream_encrypt</c>.</p> </desc> </func> - - <func> - <name>exor(Data1, Data2) -> Result</name> - <fsummary>XOR data</fsummary> + + <func> + <name>verify(Algorithm, DigestType, Msg, Signature, Key) -> boolean()</name> + <fsummary>Verifies a digital signature.</fsummary> <type> - <v>Data1, Data2 = iolist() | binary()</v> - <v>Result = binary()</v> + <v> Algorithm = rsa | dss | ecdsa </v> + <v>Msg = binary() | {digest,binary()}</v> + <d>The msg is either the binary "plain text" data + or it is the hashed value of "plain text" i.e. the digest.</d> + <v>DigestType = digest_type()</v> + <v>Signature = binary()</v> + <v>Key = rsa_public_key() | dsa_public_key() | ec_public_key()</v> </type> <desc> - <p>Performs bit-wise XOR (exclusive or) on the data supplied.</p> + <p>Verifies a digital signature</p> + See also <seealso marker="public_key:public_key#sign/3">public_key:verify/3</seealso> </desc> </func> - </funcs> - <section> - <title>DES in CBC mode</title> - <p>The Data Encryption Standard (DES) defines an algorithm for - encrypting and decrypting an 8 byte quantity using an 8 byte key - (actually only 56 bits of the key is used). - </p> - <p>When it comes to encrypting and decrypting blocks that are - multiples of 8 bytes various modes are defined (NIST SP - 800-38A). One of those modes is the Cipher Block Chaining (CBC) - mode, where the encryption of an 8 byte segment depend not only - of the contents of the segment itself, but also on the result of - encrypting the previous segment: the encryption of the previous - segment becomes the initializing vector of the encryption of the - current segment. - </p> - <p>Thus the encryption of every segment depends on the encryption - key (which is secret) and the encryption of the previous - segment, except the first segment which has to be provided with - an initial initializing vector. That vector could be chosen at - random, or be a counter of some kind. It does not have to be - secret. - </p> - <p>The following example is drawn from the old FIPS 81 standard - (replaced by NIST SP 800-38A), where both the plain text and the - resulting cipher text is settled. The following code fragment - returns `true'. - </p> - <pre><![CDATA[ - - Key = <<16#01,16#23,16#45,16#67,16#89,16#ab,16#cd,16#ef>>, - IVec = <<16#12,16#34,16#56,16#78,16#90,16#ab,16#cd,16#ef>>, - P = "Now is the time for all ", - C = crypto:des_cbc_encrypt(Key, IVec, P), - % Which is the same as - P1 = "Now is t", P2 = "he time ", P3 = "for all ", - C1 = crypto:des_cbc_encrypt(Key, IVec, P1), - C2 = crypto:des_cbc_encrypt(Key, C1, P2), - C3 = crypto:des_cbc_encrypt(Key, C2, P3), - - C = <<C1/binary, C2/binary, C3/binary>>, - C = <<16#e5,16#c7,16#cd,16#de,16#87,16#2b,16#f2,16#7c, - 16#43,16#e9,16#34,16#00,16#8c,16#38,16#9c,16#0f, - 16#68,16#37,16#88,16#49,16#9a,16#7c,16#05,16#f6>>, - <<"Now is the time for all ">> == - crypto:des_cbc_decrypt(Key, IVec, C). - ]]></pre> - <p>The following is true for the DES CBC mode. For all - decompositions <c>P1 ++ P2 = P</c> of a plain text message - <c>P</c> (where the length of all quantities are multiples of 8 - bytes), the encryption <c>C</c> of <c>P</c> is equal to <c>C1 ++ - C2</c>, where <c>C1</c> is obtained by encrypting <c>P1</c> with - <c>Key</c> and the initializing vector <c>IVec</c>, and where - <c>C2</c> is obtained by encrypting <c>P2</c> with <c>Key</c> - and the initializing vector <c>last8(C1)</c>, - where <c>last(Binary)</c> denotes the last 8 bytes of the - binary <c>Binary</c>. - </p> - <p>Similarly, for all decompositions <c>C1 ++ C2 = C</c> of a - cipher text message <c>C</c> (where the length of all quantities - are multiples of 8 bytes), the decryption <c>P</c> of <c>C</c> - is equal to <c>P1 ++ P2</c>, where <c>P1</c> is obtained by - decrypting <c>C1</c> with <c>Key</c> and the initializing vector - <c>IVec</c>, and where <c>P2</c> is obtained by decrypting - <c>C2</c> with <c>Key</c> and the initializing vector - <c>last8(C1)</c>, where <c>last8(Binary)</c> is as above. - </p> - <p>For DES3 (which uses three 64 bit keys) the situation is the - same. - </p> - </section> + </funcs> + + <!-- Maybe put this in the users guide --> + <!-- <section> --> + <!-- <title>DES in CBC mode</title> --> + <!-- <p>The Data Encryption Standard (DES) defines an algorithm for --> + <!-- encrypting and decrypting an 8 byte quantity using an 8 byte key --> + <!-- (actually only 56 bits of the key is used). --> + <!-- </p> --> + <!-- <p>When it comes to encrypting and decrypting blocks that are --> + <!-- multiples of 8 bytes various modes are defined (NIST SP --> + <!-- 800-38A). One of those modes is the Cipher Block Chaining (CBC) --> + <!-- mode, where the encryption of an 8 byte segment depend not only --> + <!-- of the contents of the segment itself, but also on the result of --> + <!-- encrypting the previous segment: the encryption of the previous --> + <!-- segment becomes the initializing vector of the encryption of the --> + <!-- current segment. --> + <!-- </p> --> + <!-- <p>Thus the encryption of every segment depends on the encryption --> + <!-- key (which is secret) and the encryption of the previous --> + <!-- segment, except the first segment which has to be provided with --> + <!-- an initial initializing vector. That vector could be chosen at --> + <!-- random, or be a counter of some kind. It does not have to be --> + <!-- secret. --> + <!-- </p> --> + <!-- <p>The following example is drawn from the old FIPS 81 standard --> + <!-- (replaced by NIST SP 800-38A), where both the plain text and the --> + <!-- resulting cipher text is settled. The following code fragment --> + <!-- returns `true'. --> + <!-- </p> --> + <!-- <pre><![CDATA[ --> + + <!-- Key = <<16#01,16#23,16#45,16#67,16#89,16#ab,16#cd,16#ef>>, --> + <!-- IVec = <<16#12,16#34,16#56,16#78,16#90,16#ab,16#cd,16#ef>>, --> + <!-- P = "Now is the time for all ", --> + <!-- C = crypto:des_cbc_encrypt(Key, IVec, P), --> + <!-- % Which is the same as --> + <!-- P1 = "Now is t", P2 = "he time ", P3 = "for all ", --> + <!-- C1 = crypto:des_cbc_encrypt(Key, IVec, P1), --> + <!-- C2 = crypto:des_cbc_encrypt(Key, C1, P2), --> + <!-- C3 = crypto:des_cbc_encrypt(Key, C2, P3), --> + + <!-- C = <<C1/binary, C2/binary, C3/binary>>, --> + <!-- C = <<16#e5,16#c7,16#cd,16#de,16#87,16#2b,16#f2,16#7c, --> + <!-- 16#43,16#e9,16#34,16#00,16#8c,16#38,16#9c,16#0f, --> + <!-- 16#68,16#37,16#88,16#49,16#9a,16#7c,16#05,16#f6>>, --> + <!-- <<"Now is the time for all ">> == --> + <!-- crypto:des_cbc_decrypt(Key, IVec, C). --> + <!-- ]]></pre> --> + <!-- <p>The following is true for the DES CBC mode. For all --> + <!-- decompositions <c>P1 ++ P2 = P</c> of a plain text message --> + <!-- <c>P</c> (where the length of all quantities are multiples of 8 --> + <!-- bytes), the encryption <c>C</c> of <c>P</c> is equal to <c>C1 ++ --> + <!-- C2</c>, where <c>C1</c> is obtained by encrypting <c>P1</c> with --> + <!-- <c>Key</c> and the initializing vector <c>IVec</c>, and where --> + <!-- <c>C2</c> is obtained by encrypting <c>P2</c> with <c>Key</c> --> + <!-- and the initializing vector <c>last8(C1)</c>, --> + <!-- where <c>last(Binary)</c> denotes the last 8 bytes of the --> + <!-- binary <c>Binary</c>. --> + <!-- </p> --> + <!-- <p>Similarly, for all decompositions <c>C1 ++ C2 = C</c> of a --> + <!-- cipher text message <c>C</c> (where the length of all quantities --> + <!-- are multiples of 8 bytes), the decryption <c>P</c> of <c>C</c> --> + <!-- is equal to <c>P1 ++ P2</c>, where <c>P1</c> is obtained by --> + <!-- decrypting <c>C1</c> with <c>Key</c> and the initializing vector --> + <!-- <c>IVec</c>, and where <c>P2</c> is obtained by decrypting --> + <!-- <c>C2</c> with <c>Key</c> and the initializing vector --> + <!-- <c>last8(C1)</c>, where <c>last8(Binary)</c> is as above. --> + <!-- </p> --> + <!-- <p>For DES3 (which uses three 64 bit keys) the situation is the --> + <!-- same. --> + <!-- </p> --> + <!-- </section> --> </erlref> diff --git a/lib/crypto/doc/src/crypto_app.xml b/lib/crypto/doc/src/crypto_app.xml index 8371db1ff2..6d26076c04 100644 --- a/lib/crypto/doc/src/crypto_app.xml +++ b/lib/crypto/doc/src/crypto_app.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE appref SYSTEM "appref.dtd"> <appref> @@ -24,81 +24,28 @@ </legalnotice> <title>crypto</title> - <prepared>Peter Högfeldt</prepared> - <responsible>Peter Högfeldt</responsible> - <docno></docno> - <approved>Peter Högfeldt</approved> - <checked>Peter Högfeldt</checked> - <date>2003-06-01</date> - <rev>B</rev> <file>crypto_app.sgml</file> </header> <app>crypto</app> <appsummary>The Crypto Application</appsummary> <description> - <p>The purpose of the Crypto application is to provide message - digest and DES encryption for SMNPv3. It provides computation of - message digests MD5 and SHA, and CBC-DES encryption and - decryption.</p> - <p></p> + <p>The purpose of the Crypto application is to provide an Erlang API + to cryptographic functions, see <seealso marker="crypto">crypto(3)</seealso>. + Note that the API is on a fairly low level and there are some + corresponding API functions available in <seealso marker="public_key:public_key">public_key(3)</seealso>, + on a higher abstraction level, that uses the crypto application in its implementation. + </p> </description> <section> - <title>Configuration</title> - <p>The following environment configuration parameters are defined - for the Crypto application. Refer to application(3) for more - information about configuration parameters. - </p> - <taglist> - <tag><c><![CDATA[debug = true | false <optional>]]></c></tag> - <item> - <p>Causes debug information to be written to standard - error or standard output. Default is <c>false</c>. - </p> - </item> - </taglist> - </section> + <title>DEPENDENCIES</title> - <section> - <title>OpenSSL libraries</title> - <p>The current implementation of the Erlang Crypto application is - based on the <em>OpenSSL</em> package version 0.9.8 or higher. - There are source and binary releases on the web. - </p> + <p>The current crypto implementation uses nifs to interface OpenSSLs crypto library + and requires <em>OpenSSL</em> package version 0.9.8 or higher.</p> <p>Source releases of OpenSSL can be downloaded from the <url href="http://www.openssl.org">OpenSSL</url> project home page, - or mirror sites listed there. - </p> - <p>The same URL also contains links to some compiled binaries and - libraries of OpenSSL (see the <c>Related/Binaries</c> menu) of - which the <url href="http://www.shininglightpro.com/search.php?searchname=Win32+OpenSSL">Shining Light Productions Win32 and OpenSSL</url> pages are of - interest for the Win32 user. - </p> - <p>For some Unix flavours there are binary packages available - on the net. - </p> - <p>If you cannot find a suitable binary OpenSSL package, you - have to fetch an OpenSSL source release and compile it. - </p> - <p>You then have to compile and install the library - <c>libcrypto.so</c> (Unix), or the library <c>libeay32.dll</c> - (Win32). - </p> - <p>For Unix The <c>crypto_drv</c> dynamic driver is delivered linked - to OpenSSL libraries in <c>/usr/local/lib</c>, but the default - dynamic linking will also accept libraries in <c>/lib</c> and - <c>/usr/lib</c>. - </p> - <p>If that is not applicable to the particular Unix operating - system used, the example <c>Makefile</c> in the Crypto - <c>priv/obj</c> directory, should be used as a basis for - relinking the final version of the port program. - </p> - <p>For <c>Win32</c> it is only required that the library can be - found from the <c>PATH</c> environment variable, or that they - reside in the appropriate <c>SYSTEM32</c> directory; hence no - particular relinking is need. Hence no example <c>Makefile</c> - for Win32 is provided.</p> - </section> + or mirror sites listed there. + </p> + </section> <section> <title>SEE ALSO</title> diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl index 1d0a9943c3..f4e157198c 100644 --- a/lib/crypto/src/crypto.erl +++ b/lib/crypto/src/crypto.erl @@ -21,104 +21,220 @@ -module(crypto). --export([start/0, stop/0, info/0, info_lib/0, algorithms/0, version/0]). +-export([start/0, stop/0, info_lib/0, algorithms/0, version/0]). -export([hash/2, hash_init/1, hash_update/2, hash_final/1]). +-export([sign/4, verify/5]). +-export([generate_key/2, generate_key/3, compute_key/4]). +-export([hmac/3, hmac/4, hmac_init/2, hmac_update/2, hmac_final/1, hmac_final_n/2]). +-export([exor/2, strong_rand_bytes/1, mod_pow/3]). +-export([rand_bytes/1, rand_bytes/3, rand_uniform/2]). +-export([block_encrypt/3, block_decrypt/3, block_encrypt/4, block_decrypt/4]). +-export([next_iv/2, next_iv/3]). +-export([stream_init/2, stream_init/3, stream_encrypt/2, stream_decrypt/2]). +-export([public_encrypt/4, private_decrypt/4]). +-export([private_encrypt/4, public_decrypt/4]). + +-export([dh_generate_parameters/2, dh_check/1]). %% Testing see + +%% DEPRECATED +%% Replaced by hash_* -export([md4/1, md4_init/0, md4_update/2, md4_final/1]). -export([md5/1, md5_init/0, md5_update/2, md5_final/1]). -export([sha/1, sha_init/0, sha_update/2, sha_final/1]). --export([sha224/1, sha224_init/0, sha224_update/2, sha224_final/1]). --export([sha256/1, sha256_init/0, sha256_update/2, sha256_final/1]). --export([sha384/1, sha384_init/0, sha384_update/2, sha384_final/1]). --export([sha512/1, sha512_init/0, sha512_update/2, sha512_final/1]). +-deprecated({md4, 1, next_major_release}). +-deprecated({md5, 1, next_major_release}). +-deprecated({sha, 1, next_major_release}). +-deprecated({md4_init, 0, next_major_release}). +-deprecated({md5_init, 0, next_major_release}). +-deprecated({sha_init, 0, next_major_release}). +-deprecated({md4_update, 2, next_major_release}). +-deprecated({md5_update, 2, next_major_release}). +-deprecated({sha_update, 2, next_major_release}). +-deprecated({md4_final, 1, next_major_release}). +-deprecated({md5_final, 1, next_major_release}). +-deprecated({sha_final, 1, next_major_release}). + +%% Replaced by hmac_* -export([md5_mac/2, md5_mac_96/2, sha_mac/2, sha_mac/3, sha_mac_96/2]). --export([sha224_mac/2, sha224_mac/3]). --export([sha256_mac/2, sha256_mac/3]). --export([sha384_mac/2, sha384_mac/3]). --export([sha512_mac/2, sha512_mac/3]). --export([hmac/3, hmac/4, hmac_init/2, hmac_update/2, hmac_final/1, hmac_final_n/2]). +-deprecated({md5_mac, 2, next_major_release}). +-deprecated({md5_mac_96, 2, next_major_release}). +-deprecated({sha_mac, 2, next_major_release}). +-deprecated({sha_mac, 3, next_major_release}). +-deprecated({sha_mac_96, 2, next_major_release}). + +%% Replaced by sign/verify +-export([dss_verify/3, dss_verify/4, rsa_verify/3, rsa_verify/4]). +-export([dss_sign/2, dss_sign/3, rsa_sign/2, rsa_sign/3]). +-deprecated({dss_verify, 3, next_major_release}). +-deprecated({dss_verify, 4, next_major_release}). +-deprecated({rsa_verify, 3, next_major_release}). +-deprecated({rsa_verify, 4, next_major_release}). +-deprecated({dss_sign, 2, next_major_release}). +-deprecated({dss_sign, 3, next_major_release}). +-deprecated({rsa_sign, 2, next_major_release}). +-deprecated({rsa_sign, 3, next_major_release}). + +%% Replaced by generate_key +-export([dh_generate_key/1, dh_generate_key/2, dh_compute_key/3]). +-deprecated({dh_generate_key, 1, next_major_release}). +-deprecated({dh_generate_key, 2, next_major_release}). +-deprecated({dh_compute_key, 3, next_major_release}). + +%% Replaced by mod_exp_prim and no longer needed +-export([mod_exp/3, mpint/1, erlint/1, strong_rand_mpint/3]). +-deprecated({mod_exp, 3, next_major_release}). +-deprecated({mpint, 1, next_major_release}). +-deprecated({erlint, 1, next_major_release}). +-deprecated({strong_rand_mpint, 3, next_major_release}). + +%% Replaced by block_* -export([des_cbc_encrypt/3, des_cbc_decrypt/3, des_cbc_ivec/1]). +-export([des3_cbc_encrypt/5, des3_cbc_decrypt/5]). -export([des_ecb_encrypt/2, des_ecb_decrypt/2]). +-export([des_ede3_cbc_encrypt/5, des_ede3_cbc_decrypt/5]). -export([des_cfb_encrypt/3, des_cfb_decrypt/3, des_cfb_ivec/2]). --export([des3_cbc_encrypt/5, des3_cbc_decrypt/5]). -export([des3_cfb_encrypt/5, des3_cfb_decrypt/5]). +-deprecated({des_cbc_encrypt, 3, next_major_release}). +-deprecated({des_cbc_decrypt, 3, next_major_release}). +-deprecated({des_cbc_ivec, 1, next_major_release}). +-deprecated({des3_cbc_encrypt, 5, next_major_release}). +-deprecated({des3_cbc_decrypt, 5, next_major_release}). +-deprecated({des_ecb_encrypt, 2, next_major_release}). +-deprecated({des_ecb_decrypt, 2, next_major_release}). +-deprecated({des_ede3_cbc_encrypt, 5, next_major_release}). +-deprecated({des_ede3_cbc_decrypt, 5, next_major_release}). +-deprecated({des_cfb_encrypt, 3, next_major_release}). +-deprecated({des_cfb_decrypt, 3, next_major_release}). +-deprecated({des_cfb_ivec, 2, next_major_release}). +-deprecated({des3_cfb_encrypt, 5, next_major_release}). +-deprecated({des3_cfb_decrypt, 5, next_major_release}). -export([blowfish_ecb_encrypt/2, blowfish_ecb_decrypt/2]). -export([blowfish_cbc_encrypt/3, blowfish_cbc_decrypt/3]). -export([blowfish_cfb64_encrypt/3, blowfish_cfb64_decrypt/3]). -export([blowfish_ofb64_encrypt/3]). --export([des_ede3_cbc_encrypt/5, des_ede3_cbc_decrypt/5]). +-deprecated({blowfish_ecb_encrypt, 2, next_major_release}). +-deprecated({blowfish_ecb_decrypt, 2, next_major_release}). +-deprecated({blowfish_cbc_encrypt, 3, next_major_release}). +-deprecated({blowfish_cbc_decrypt, 3, next_major_release}). +-deprecated({blowfish_cfb64_encrypt, 3, next_major_release}). +-deprecated({blowfish_cfb64_decrypt, 3, next_major_release}). +-deprecated({blowfish_ofb64_encrypt, 3, next_major_release}). -export([aes_cfb_128_encrypt/3, aes_cfb_128_decrypt/3]). --export([exor/2]). --export([rc4_encrypt/2, rc4_set_key/1, rc4_encrypt_with_state/2]). --export([rc2_cbc_encrypt/3, rc2_cbc_decrypt/3, rc2_40_cbc_encrypt/3, rc2_40_cbc_decrypt/3]). --export([dss_verify/3, dss_verify/4, rsa_verify/3, rsa_verify/4]). --export([dss_sign/2, dss_sign/3, rsa_sign/2, rsa_sign/3]). --export([rsa_public_encrypt/3, rsa_private_decrypt/3]). --export([rsa_private_encrypt/3, rsa_public_decrypt/3]). --export([dh_generate_key/1, dh_generate_key/2, dh_compute_key/3]). --export([rand_bytes/1, rand_bytes/3, rand_uniform/2]). --export([strong_rand_bytes/1, strong_rand_mpint/3]). --export([mod_exp/3, mod_exp_prime/3, mpint/1, erlint/1]). --export([srp_generate_key/4, srp_generate_key/3, - srp_generate_key/5, srp_compute_key/6, srp_compute_key/7, srp_compute_key/8]). - -%% -export([idea_cbc_encrypt/3, idea_cbc_decrypt/3]). -export([aes_cbc_128_encrypt/3, aes_cbc_128_decrypt/3]). -export([aes_cbc_256_encrypt/3, aes_cbc_256_decrypt/3]). -export([aes_cbc_ivec/1]). --export([aes_ctr_encrypt/3, aes_ctr_decrypt/3]). +-deprecated({aes_cfb_128_encrypt, 3, next_major_release}). +-deprecated({aes_cfb_128_decrypt, 3, next_major_release}). +-deprecated({aes_cbc_128_encrypt, 3, next_major_release}). +-deprecated({aes_cbc_128_decrypt, 3, next_major_release}). +-deprecated({aes_cbc_256_encrypt, 3, next_major_release}). +-deprecated({aes_cbc_256_decrypt, 3, next_major_release}). +-deprecated({aes_cbc_ivec, 1, next_major_release}). +-export([rc2_cbc_encrypt/3, rc2_cbc_decrypt/3]). +-export([rc2_40_cbc_encrypt/3, rc2_40_cbc_decrypt/3]). +-deprecated({rc2_cbc_encrypt, 3, next_major_release}). +-deprecated({rc2_cbc_decrypt, 3, next_major_release}). +%% allready replaced by above! +-deprecated({rc2_40_cbc_encrypt, 3, next_major_release}). +-deprecated({rc2_40_cbc_decrypt, 3, next_major_release}). + +%% Replaced by stream_* -export([aes_ctr_stream_init/2, aes_ctr_stream_encrypt/2, aes_ctr_stream_decrypt/2]). - --export([dh_generate_parameters/2, dh_check/1]). %% Testing see below - - --define(FUNC_LIST, [md4, md4_init, md4_update, md4_final, +-export([rc4_set_key/1, rc4_encrypt_with_state/2]). +-deprecated({aes_ctr_stream_init, 2, next_major_release}). +-deprecated({aes_ctr_stream_encrypt, 2, next_major_release}). +-deprecated({aes_ctr_stream_decrypt, 2, next_major_release}). +-deprecated({rc4_set_key, 1, next_major_release}). +-deprecated({rc4_encrypt_with_state, 2, next_major_release}). + +%% Not needed special case of stream_* +-export([aes_ctr_encrypt/3, aes_ctr_decrypt/3, rc4_encrypt/2]). +-deprecated({aes_ctr_encrypt, 3, next_major_release}). +-deprecated({aes_ctr_decrypt, 3, next_major_release}). +-deprecated({rc4_encrypt, 2, next_major_release}). + +%% Replace by public/private_encrypt/decrypt +-export([rsa_public_encrypt/3, rsa_private_decrypt/3]). +-export([rsa_private_encrypt/3, rsa_public_decrypt/3]). +-deprecated({rsa_public_encrypt, 3, next_major_release}). +-deprecated({rsa_private_decrypt, 3, next_major_release}). +-deprecated({rsa_public_decrypt, 3, next_major_release}). +-deprecated({rsa_private_encrypt, 3, next_major_release}). + +%% Replaced by crypto:module_info() +-export([info/0]). +-deprecated({info, 0, next_major_release}). + +-define(FUNC_LIST, [hash, hash_init, hash_update, hash_final, + hmac, hmac_init, hmac_update, hmac_final, hmac_final_n, + %% deprecated + md4, md4_init, md4_update, md4_final, md5, md5_init, md5_update, md5_final, sha, sha_init, sha_update, sha_final, - sha224, sha224_init, sha224_update, sha224_final, - sha256, sha256_init, sha256_update, sha256_final, - sha384, sha384_init, sha384_update, sha384_final, - sha512, sha512_init, sha512_update, sha512_final, md5_mac, md5_mac_96, sha_mac, sha_mac_96, - sha224_mac, sha256_mac, sha384_mac, sha512_mac, + %% + block_encrypt, block_decrypt, + %% deprecated des_cbc_encrypt, des_cbc_decrypt, des_cfb_encrypt, des_cfb_decrypt, des_ecb_encrypt, des_ecb_decrypt, des3_cbc_encrypt, des3_cbc_decrypt, des3_cfb_encrypt, des3_cfb_decrypt, aes_cfb_128_encrypt, aes_cfb_128_decrypt, + rc2_cbc_encrypt, rc2_cbc_decrypt, + rc2_40_cbc_encrypt, rc2_40_cbc_decrypt, + aes_cbc_128_encrypt, aes_cbc_128_decrypt, + aes_cbc_256_encrypt, aes_cbc_256_decrypt, + blowfish_cbc_encrypt, blowfish_cbc_decrypt, + blowfish_cfb64_encrypt, blowfish_cfb64_decrypt, + blowfish_ecb_encrypt, blowfish_ecb_decrypt, blowfish_ofb64_encrypt, + %% rand_bytes, strong_rand_bytes, - strong_rand_mpint, rand_uniform, - mod_exp, mod_exp_prime, + mod_pow, + exor, + %% deprecated + mod_exp,strong_rand_mpint,erlint, mpint, + %% + sign, verify, generate_key, compute_key, + %% deprecated dss_verify,dss_sign, rsa_verify,rsa_sign, rsa_public_encrypt,rsa_private_decrypt, rsa_private_encrypt,rsa_public_decrypt, dh_generate_key, dh_compute_key, - aes_cbc_128_encrypt, aes_cbc_128_decrypt, - exor, + %% + stream_init, stream_encrypt, stream_decrypt, + %% deprecated rc4_encrypt, rc4_set_key, rc4_encrypt_with_state, - rc2_40_cbc_encrypt, rc2_40_cbc_decrypt, - %% idea_cbc_encrypt, idea_cbc_decrypt, - aes_cbc_256_encrypt, aes_cbc_256_decrypt, aes_ctr_encrypt, aes_ctr_decrypt, aes_ctr_stream_init, aes_ctr_stream_encrypt, aes_ctr_stream_decrypt, - aes_cbc_ivec, blowfish_cbc_encrypt, blowfish_cbc_decrypt, - blowfish_cfb64_encrypt, blowfish_cfb64_decrypt, - blowfish_ecb_encrypt, blowfish_ecb_decrypt, blowfish_ofb64_encrypt, - des_cbc_ivec, des_cfb_ivec, erlint, mpint, - hash, hash_init, hash_update, hash_final, - hmac, hmac_init, hmac_update, hmac_final, hmac_final_n, info, - rc2_cbc_encrypt, rc2_cbc_decrypt, - srp_generate_key, srp_compute_key, + %% + next_iv, + %% deprecated + aes_cbc_ivec, + des_cbc_ivec, des_cfb_ivec, + info, + %% info_lib, algorithms]). +-type mpint() :: binary(). -type rsa_digest_type() :: 'md5' | 'sha' | 'sha224' | 'sha256' | 'sha384' | 'sha512'. -type dss_digest_type() :: 'none' | 'sha'. +%%-type ecdsa_digest_type() :: 'md5' | 'sha' | 'sha256' | 'sha384' | 'sha512'. -type data_or_digest() :: binary() | {digest, binary()}. -type crypto_integer() :: binary() | integer(). +-type ec_key_res() :: any(). %% nif resource +-type ec_named_curve() :: atom(). +-type ec_point() :: crypto_integer(). +-type ec_basis() :: {tpbasis, K :: non_neg_integer()} | {ppbasis, K1 :: non_neg_integer(), K2 :: non_neg_integer(), K3 :: non_neg_integer()} | onbasis. +-type ec_field() :: {prime_field, Prime :: integer()} | {characteristic_two_field, M :: integer(), Basis :: ec_basis()}. +-type ec_prime() :: {A :: crypto_integer(), B :: crypto_integer(), Seed :: binary() | none}. +-type ec_curve_spec() :: {Field :: ec_field(), Prime :: ec_prime(), Point :: crypto_integer(), Order :: integer(), CoFactor :: none | integer()}. +-type ec_curve() :: ec_named_curve() | ec_curve_spec(). +-type ec_key() :: {Curve :: ec_curve(), PrivKey :: binary() | undefined, PubKey :: ec_point() | undefined}. -define(nif_stub,nif_stub_error(?LINE)). @@ -565,6 +681,110 @@ sha512_mac(Key, Data, MacSz) -> sha512_mac_nif(_Key,_Data,_MacSz) -> ?nif_stub. + +%% Ecrypt/decrypt %%% + +-spec block_encrypt(des_cbc | des_cfb | des3_cbc | des3_cbf | des_ede3 | blowfish_cbc | + blowfish_cfb64 | aes_cbc128 | aes_cfb128 | aes_cbc256 | rc2_cbc, + Key::iodata(), Ivec::binary(), Data::iodata()) -> binary(). + +block_encrypt(des_cbc, Key, Ivec, Data) -> + des_cbc_encrypt(Key, Ivec, Data); +block_encrypt(des_cfb, Key, Ivec, Data) -> + des_cfb_encrypt(Key, Ivec, Data); +block_encrypt(des3_cbc, [Key1, Key2, Key3], Ivec, Data) -> + des3_cbc_encrypt(Key1, Key2, Key3, Ivec, Data); +block_encrypt(des3_cbf, [Key1, Key2, Key3], Ivec, Data) -> + des3_cfb_encrypt(Key1, Key2, Key3, Ivec, Data); +block_encrypt(des_ede3, [Key1, Key2, Key3], Ivec, Data) -> + des_ede3_cbc_encrypt(Key1, Key2, Key3, Ivec, Data); +block_encrypt(blowfish_cbc, Key, Ivec, Data) -> + blowfish_cbc_encrypt(Key, Ivec, Data); +block_encrypt(blowfish_cfb64, Key, Ivec, Data) -> + blowfish_cfb64_encrypt(Key, Ivec, Data); +block_encrypt(blowfish_ofb64, Key, Ivec, Data) -> + blowfish_ofb64_encrypt(Key, Ivec, Data); +block_encrypt(aes_cbc128, Key, Ivec, Data) -> + aes_cbc_128_encrypt(Key, Ivec, Data); +block_encrypt(aes_cbc256, Key, Ivec, Data) -> + aes_cbc_256_encrypt(Key, Ivec, Data); +block_encrypt(aes_cfb128, Key, Ivec, Data) -> + aes_cfb_128_encrypt(Key, Ivec, Data); +block_encrypt(rc2_cbc, Key, Ivec, Data) -> + rc2_cbc_encrypt(Key, Ivec, Data). + +-spec block_decrypt(des_cbc | des_cfb | des3_cbc | des3_cbf | des_ede3 | blowfish_cbc | + blowfish_cfb64 | blowfish_ofb64 | aes_cbc128 | aes_cbc256 | aes_cfb128 | rc2_cbc, + Key::iodata(), Ivec::binary(), Data::iodata()) -> binary(). + +block_decrypt(des_cbc, Key, Ivec, Data) -> + des_cbc_decrypt(Key, Ivec, Data); +block_decrypt(des_cfb, Key, Ivec, Data) -> + des_cfb_decrypt(Key, Ivec, Data); +block_decrypt(des3_cbc, [Key1, Key2, Key3], Ivec, Data) -> + des3_cbc_decrypt(Key1, Key2, Key3, Ivec, Data); +block_decrypt(des3_cbf, [Key1, Key2, Key3], Ivec, Data) -> + des3_cfb_decrypt(Key1, Key2, Key3, Ivec, Data); +block_decrypt(des_ede3, [Key1, Key2, Key3], Ivec, Data) -> + des_ede3_cbc_decrypt(Key1, Key2, Key3, Ivec, Data); +block_decrypt(blowfish_cbc, Key, Ivec, Data) -> + blowfish_cbc_decrypt(Key, Ivec, Data); +block_decrypt(blowfish_cfb64, Key, Ivec, Data) -> + blowfish_cfb64_decrypt(Key, Ivec, Data); +block_decrypt(blowfish_ofb, Key, Ivec, Data) -> + blowfish_ofb64_decrypt(Key, Ivec, Data); +block_decrypt(aes_cbc128, Key, Ivec, Data) -> + aes_cbc_128_decrypt(Key, Ivec, Data); +block_decrypt(aes_cbc256, Key, Ivec, Data) -> + aes_cbc_256_decrypt(Key, Ivec, Data); +block_decrypt(aes_cfb128, Key, Ivec, Data) -> + aes_cfb_128_decrypt(Key, Ivec, Data); +block_decrypt(rc2_cbc, Key, Ivec, Data) -> + rc2_cbc_decrypt(Key, Ivec, Data). + +-spec block_encrypt(des_ecb | blowfish_ecb, Key::iodata(), Data::iodata()) -> binary(). + +block_encrypt(des_ecb, Key, Data) -> + des_ecb_encrypt(Key, Data); +block_encrypt(blowfish_ecb, Key, Data) -> + blowfish_ecb_encrypt(Key, Data). + +-spec block_decrypt(des_ecb | blowfish_ecb, Key::iodata(), Data::iodata()) -> binary(). + +block_decrypt(des_ecb, Key, Data) -> + des_ecb_decrypt(Key, Data); +block_decrypt(blowfish_ecb, Key, Data) -> + blowfish_ecb_decrypt(Key, Data). + +-spec next_iv(des_cbc | aes_cbc, Data::iodata()) -> binary(). + +next_iv(des_cbc, Data) -> + des_cbc_ivec(Data); +next_iv(aes_cbc, Data) -> + aes_cbc_ivec(Data). + +-spec next_iv(des_cbf, Ivec::binary(), Data::iodata()) -> binary(). + +next_iv(des_cbf, Ivec, Data) -> + des_cfb_ivec(Ivec, Data). + +stream_init(aes_ctr, Key, Ivec) -> + {aes_ctr, aes_ctr_stream_init(Key, Ivec)}. +stream_init(rc4, Key) -> + {rc4, rc4_set_key(Key)}. +stream_encrypt({aes_ctr, State}, Data) -> + {State, Cipher} = aes_ctr_stream_encrypt(State, Data), + {{aes_ctr, State}, Cipher}; +stream_encrypt({rc4, State0}, Data) -> + {State, Cipher} = rc4_encrypt_with_state(State0, Data), + {{rc4, State}, Cipher}. +stream_decrypt({aes_ctr, State0}, Data) -> + {State, Text} = aes_ctr_stream_decrypt(State0, Data), + {{aes_ctr, State}, Text}; +stream_decrypt({rc4, State0}, Data) -> + {State, Text} = rc4_encrypt_with_state (State0, Data), + {{rc4, State}, Text}. + %% %% CRYPTO FUNCTIONS %% @@ -713,8 +933,12 @@ blowfish_cfb64_decrypt(Key, IVec, Data) -> bf_cfb64_crypt(_Key, _IVec, _Data, _IsEncrypt) -> ?nif_stub. +blowfish_ofb64_decrypt(Key, Ivec, Data) -> + blowfish_ofb64_encrypt(Key, Ivec, Data). + blowfish_ofb64_encrypt(_Key, _IVec, _Data) -> ?nif_stub. + %% %% AES in cipher feedback mode (CFB) %% @@ -798,9 +1022,9 @@ mod_exp(Base, Exponent, Modulo) mod_exp(Base, Exponent, Modulo) -> mod_exp_nif(mpint_to_bin(Base),mpint_to_bin(Exponent),mpint_to_bin(Modulo), 4). --spec mod_exp_prime(binary(), binary(), binary()) -> binary() | error. -mod_exp_prime(Base, Exponent, Prime) -> - case mod_exp_nif(Base, Exponent, Prime, 0) of +-spec mod_pow(binary()|integer(), binary()|integer(), binary()|integer()) -> binary() | error. +mod_pow(Base, Exponent, Prime) -> + case mod_exp_nif(ensure_int_as_bin(Base), ensure_int_as_bin(Exponent), ensure_int_as_bin(Prime), 0) of <<0>> -> error; R -> R end. @@ -819,19 +1043,40 @@ mod_exp_nif(_Base,_Exp,_Mod,_bin_hdr) -> ?nif_stub. %% Key = [P,Q,G,Y] P,Q,G=DSSParams Y=PublicKey dss_verify(Data,Signature,Key) -> - dss_verify(sha, Data, Signature, Key). -dss_verify(_Type,_Data,_Signature,_Key) -> ?nif_stub. + dss_verify(sha, Data, Signature, Key). + +dss_verify(Type,Data,Signature,Key) when is_binary(Data), Type=/=none -> + verify(dss,Type,mpint_to_bin(Data),mpint_to_bin(Signature),map_mpint_to_bin(Key)); +dss_verify(Type,Digest,Signature,Key) -> + verify(dss,Type,Digest,mpint_to_bin(Signature),map_mpint_to_bin(Key)). % Key = [E,N] E=PublicExponent N=PublicModulus rsa_verify(Data,Signature,Key) -> - rsa_verify_nif(sha, Data,Signature,Key). -rsa_verify(Type, DataOrDigest, Signature, Key) -> - case rsa_verify_nif(Type, DataOrDigest, Signature, Key) of + rsa_verify(sha, Data,Signature,Key). +rsa_verify(Type, Data, Signature, Key) when is_binary(Data) -> + verify(rsa, Type, mpint_to_bin(Data), mpint_to_bin(Signature), map_mpint_to_bin(Key)); +rsa_verify(Type, Digest, Signature, Key) -> + verify(rsa, Type, Digest, mpint_to_bin(Signature), map_mpint_to_bin(Key)). + + +verify(dss, Type, Data, Signature, Key) -> + dss_verify_nif(Type, Data, Signature, map_ensure_int_as_bin(Key)); + +verify(rsa, Type, DataOrDigest, Signature, Key) -> + case rsa_verify_nif(Type, DataOrDigest, Signature, map_ensure_int_as_bin(Key)) of notsup -> erlang:error(notsup); Bool -> Bool + end; +verify(ecdsa, Type, DataOrDigest, Signature, [Key, Curve]) -> + case ecdsa_verify_nif(Type, DataOrDigest, Signature, term_to_ec_key({Curve, undefined, Key})) of + notsup -> erlang:error(notsup); + Bool -> Bool end. + +dss_verify_nif(_Type, _Data, _Signature, _Key) -> ?nif_stub. rsa_verify_nif(_Type, _Data, _Signature, _Key) -> ?nif_stub. +ecdsa_verify_nif(_Type, _DataOrDigest, _Signature, _Key) -> ?nif_stub. %% @@ -845,24 +1090,103 @@ rsa_verify_nif(_Type, _Data, _Signature, _Key) -> ?nif_stub. dss_sign(DataOrDigest,Key) -> dss_sign(sha,DataOrDigest,Key). -dss_sign(Type, DataOrDigest, Key) -> - case dss_sign_nif(Type,DataOrDigest,Key) of - error -> erlang:error(badkey, [DataOrDigest, Key]); - Sign -> Sign - end. +dss_sign(Type, Data, Key) when is_binary(Data), Type=/=none -> + sign(dss, Type, mpint_to_bin(Data), map_mpint_to_bin(Key)); +dss_sign(Type, Digest, Key) -> + sign(dss, Type, Digest, map_mpint_to_bin(Key)). -dss_sign_nif(_Type,_Data,_Key) -> ?nif_stub. %% Key = [E,N,D] E=PublicExponent N=PublicModulus D=PrivateExponent rsa_sign(DataOrDigest,Key) -> rsa_sign(sha, DataOrDigest, Key). -rsa_sign(Type, DataOrDigest, Key) -> - case rsa_sign_nif(Type,DataOrDigest,Key) of + +rsa_sign(Type, Data, Key) when is_binary(Data) -> + sign(rsa, Type, mpint_to_bin(Data), map_mpint_to_bin(Key)); +rsa_sign(Type, Digest, Key) -> + sign(rsa, Type, Digest, map_mpint_to_bin(Key)). + +map_mpint_to_bin(List) -> + lists:map(fun(E) -> mpint_to_bin(E) end, List ). + +map_ensure_int_as_bin([H|_]=List) when is_integer(H) -> + lists:map(fun(E) -> int_to_bin(E) end, List); +map_ensure_int_as_bin(List) -> + List. + +ensure_int_as_bin(Int) when is_integer(Int) -> + int_to_bin(Int); +ensure_int_as_bin(Bin) -> + Bin. + +map_to_norm_bin([H|_]=List) when is_integer(H) -> + lists:map(fun(E) -> int_to_bin(E) end, List); +map_to_norm_bin(List) -> + lists:map(fun(E) -> mpint_to_bin(E) end, List). + + +sign(rsa, Type, DataOrDigest, Key) -> + case rsa_sign_nif(Type, DataOrDigest, map_ensure_int_as_bin(Key)) of + error -> erlang:error(badkey, [Type,DataOrDigest,Key]); + Sign -> Sign + end; +sign(dss, Type, DataOrDigest, Key) -> + case dss_sign_nif(Type, DataOrDigest, map_ensure_int_as_bin(Key)) of + error -> erlang:error(badkey, [DataOrDigest, Key]); + Sign -> Sign + end; +sign(ecdsa, Type, DataOrDigest, [Key, Curve]) -> + case ecdsa_sign_nif(Type, DataOrDigest, term_to_ec_key({Curve, Key, undefined})) of error -> erlang:error(badkey, [Type,DataOrDigest,Key]); Sign -> Sign end. rsa_sign_nif(_Type,_Data,_Key) -> ?nif_stub. +dss_sign_nif(_Type,_Data,_Key) -> ?nif_stub. +ecdsa_sign_nif(_Type, _DataOrDigest, _Key) -> ?nif_stub. + + + + +-spec public_encrypt(rsa, binary(), [binary()], rsa_padding()) -> + binary(). +-spec public_decrypt(rsa, binary(), [integer() | binary()], rsa_padding()) -> + binary(). +-spec private_encrypt(rsa, binary(), [integer() | binary()], rsa_padding()) -> + binary(). +-spec private_decrypt(rsa, binary(), [integer() | binary()], rsa_padding()) -> + binary(). + +public_encrypt(rsa, BinMesg, Key, Padding) -> + case rsa_public_crypt(BinMesg, map_ensure_int_as_bin(Key), Padding, true) of + error -> + erlang:error(encrypt_failed, [BinMesg,Key, Padding]); + Sign -> Sign + end. + +%% Binary, Key = [E,N,D] +private_decrypt(rsa, BinMesg, Key, Padding) -> + case rsa_private_crypt(BinMesg, map_ensure_int_as_bin(Key), Padding, false) of + error -> + erlang:error(decrypt_failed, [BinMesg,Key, Padding]); + Sign -> Sign + end. + + +%% Binary, Key = [E,N,D] +private_encrypt(rsa, BinMesg, Key, Padding) -> + case rsa_private_crypt(BinMesg, map_ensure_int_as_bin(Key), Padding, true) of + error -> + erlang:error(encrypt_failed, [BinMesg,Key, Padding]); + Sign -> Sign + end. + +%% Binary, Key = [E,N] +public_decrypt(rsa, BinMesg, Key, Padding) -> + case rsa_public_crypt(BinMesg, map_ensure_int_as_bin(Key), Padding, false) of + error -> + erlang:error(decrypt_failed, [BinMesg,Key, Padding]); + Sign -> Sign + end. %% @@ -872,16 +1196,16 @@ rsa_sign_nif(_Type,_Data,_Key) -> ?nif_stub. -spec rsa_public_encrypt(binary(), [binary()], rsa_padding()) -> binary(). --spec rsa_public_decrypt(binary(), [binary()], rsa_padding()) -> +-spec rsa_public_decrypt(binary(), [integer() | mpint()], rsa_padding()) -> binary(). --spec rsa_private_encrypt(binary(), [binary()], rsa_padding()) -> +-spec rsa_private_encrypt(binary(), [integer() | mpint()], rsa_padding()) -> binary(). --spec rsa_private_decrypt(binary(), [binary()], rsa_padding()) -> +-spec rsa_private_decrypt(binary(), [integer() | mpint()], rsa_padding()) -> binary(). %% Binary, Key = [E,N] rsa_public_encrypt(BinMesg, Key, Padding) -> - case rsa_public_crypt(BinMesg, Key, Padding, true) of + case rsa_public_crypt(BinMesg, map_to_norm_bin(Key), Padding, true) of error -> erlang:error(encrypt_failed, [BinMesg,Key, Padding]); Sign -> Sign @@ -891,7 +1215,7 @@ rsa_public_crypt(_BinMsg, _Key, _Padding, _IsEncrypt) -> ?nif_stub. %% Binary, Key = [E,N,D] rsa_private_decrypt(BinMesg, Key, Padding) -> - case rsa_private_crypt(BinMesg, Key, Padding, false) of + case rsa_private_crypt(BinMesg, map_to_norm_bin(Key), Padding, false) of error -> erlang:error(decrypt_failed, [BinMesg,Key, Padding]); Sign -> Sign @@ -902,7 +1226,7 @@ rsa_private_crypt(_BinMsg, _Key, _Padding, _IsEncrypt) -> ?nif_stub. %% Binary, Key = [E,N,D] rsa_private_encrypt(BinMesg, Key, Padding) -> - case rsa_private_crypt(BinMesg, Key, Padding, true) of + case rsa_private_crypt(BinMesg, map_to_norm_bin(Key), Padding, true) of error -> erlang:error(encrypt_failed, [BinMesg,Key, Padding]); Sign -> Sign @@ -910,7 +1234,7 @@ rsa_private_encrypt(BinMesg, Key, Padding) -> %% Binary, Key = [E,N] rsa_public_decrypt(BinMesg, Key, Padding) -> - case rsa_public_crypt(BinMesg, Key, Padding, false) of + case rsa_public_crypt(BinMesg, map_to_norm_bin(Key), Padding, false) of error -> erlang:error(decrypt_failed, [BinMesg,Key, Padding]); Sign -> Sign @@ -1052,120 +1376,142 @@ dh_check([_Prime,_Gen]) -> ?nif_stub. {binary(),binary()}. dh_generate_key(DHParameters) -> - dh_generate_key(undefined, DHParameters). + dh_generate_key_nif(undefined, map_mpint_to_bin(DHParameters), 4). dh_generate_key(PrivateKey, DHParameters) -> - case dh_generate_key_nif(PrivateKey, DHParameters) of - error -> erlang:error(generation_failed, [PrivateKey,DHParameters]); - Res -> Res - end. + dh_generate_key_nif(mpint_to_bin(PrivateKey), map_mpint_to_bin(DHParameters), 4). -dh_generate_key_nif(_PrivateKey, _DHParameters) -> ?nif_stub. +dh_generate_key_nif(_PrivateKey, _DHParameters, _Mpint) -> ?nif_stub. %% DHParameters = [P (Prime)= mpint(), G(Generator) = mpint()] -%% MyPrivKey, OthersPublicKey = mpint() +%% MyPrivKey, OthersPublicKey = mpint() -spec dh_compute_key(binary(), binary(), [binary()]) -> binary(). dh_compute_key(OthersPublicKey, MyPrivateKey, DHParameters) -> - case dh_compute_key_nif(OthersPublicKey,MyPrivateKey,DHParameters) of - error -> erlang:error(computation_failed, [OthersPublicKey,MyPrivateKey,DHParameters]); - Ret -> Ret - end. + compute_key(dh, mpint_to_bin(OthersPublicKey), mpint_to_bin(MyPrivateKey), + map_mpint_to_bin(DHParameters)). + dh_compute_key_nif(_OthersPublicKey, _MyPrivateKey, _DHParameters) -> ?nif_stub. +generate_key(Type, Params) -> + generate_key(Type, Params, undefined). + +generate_key(dh, DHParameters, PrivateKey) -> + dh_generate_key_nif(PrivateKey, map_ensure_int_as_bin(DHParameters), 0); + +generate_key(srp, {host, [Verifier, Generator, Prime, Version]}, PrivArg) + when is_binary(Verifier), is_binary(Generator), is_binary(Prime), is_atom(Version) -> + Private = case PrivArg of + undefined -> random_bytes(32); + _ -> PrivArg + end, + host_srp_gen_key(Private, Verifier, Generator, Prime, Version); + +generate_key(srp, {user, [Generator, Prime, Version]}, PrivateArg) + when is_binary(Generator), is_binary(Prime), is_atom(Version) -> + Private = case PrivateArg of + undefined -> random_bytes(32); + _ -> PrivateArg + end, + user_srp_gen_key(Private, Generator, Prime); -%%% SRP --spec srp_generate_key(binary(), binary(), atom() | binary(), atom() | binary() ) -> {Public::binary(), Private::binary()}. -srp_generate_key(Verifier, Generator, Prime, Version) when is_binary(Verifier), - is_binary(Generator), - is_binary(Prime), - is_atom(Version) -> - Private = random_bytes(32), - server_srp_gen_key(Private, Verifier, Generator, Prime, Version); - -srp_generate_key(Generator, Prime, Version, Private) when is_binary(Generator), - is_binary(Prime), - is_atom(Version), - is_binary(Private) -> - client_srp_gen_key(Private, Generator, Prime). - --spec srp_generate_key(binary(), binary(), binary(), atom(), binary()) -> {Public::binary(), Private::binary()}. -srp_generate_key(Verifier, Generator, Prime, Version, Private) when is_binary(Verifier), - is_binary(Generator), - is_binary(Prime), - is_atom(Version), - is_binary(Private) - -> - server_srp_gen_key(Private, Verifier, Generator, Prime, Version). - --spec srp_generate_key(binary(), binary(), atom()) -> {Public::binary(), Private::binary()}. -srp_generate_key(Generator, Prime, Version) when is_binary(Generator), - is_binary(Prime), - is_atom(Version) -> - Private = random_bytes(32), - client_srp_gen_key(Private, Generator, Prime). - --spec srp_compute_key(binary(), binary(), binary(), binary(), binary(), atom()| binary(), atom() | binary() ) -> binary(). -srp_compute_key(DerivedKey, Prime, Generator, ClientPublic, ClientPrivate, ServerPublic, Version) when - is_binary(Prime), +generate_key(ecdh, Curve, undefined) -> + ec_key_to_term(ec_key_generate(Curve)). + + +ec_key_generate(_Key) -> ?nif_stub. + + +compute_key(dh, OthersPublicKey, MyPrivateKey, DHParameters) -> + case dh_compute_key_nif(OthersPublicKey,MyPrivateKey, map_ensure_int_as_bin(DHParameters)) of + error -> erlang:error(computation_failed, + [OthersPublicKey,MyPrivateKey,DHParameters]); + Ret -> Ret + end; + +compute_key(srp, HostPublic, {UserPublic, UserPrivate}, + {user, [DerivedKey, Prime, Generator, Version | ScramblerArg]}) when + is_binary(Prime), is_binary(Generator), - is_binary(ClientPublic), - is_binary(ClientPrivate), - is_binary(ServerPublic), + is_binary(UserPublic), + is_binary(UserPrivate), + is_binary(HostPublic), is_atom(Version) -> Multiplier = srp_multiplier(Version, Generator, Prime), - Scrambler = srp_scrambler(Version, ClientPublic, ServerPublic, Prime), - srp_client_secret_nif(ClientPrivate, Scrambler, ServerPublic, Multiplier, - Generator, DerivedKey, Prime); - -srp_compute_key(Verifier, Prime, ClientPublic, ServerPublic, ServerPrivate, Version, Scrambler) when + Scrambler = case ScramblerArg of + [] -> srp_scrambler(Version, UserPublic, HostPublic, Prime); + [S] -> S + end, + srp_user_secret_nif(UserPrivate, Scrambler, HostPublic, Multiplier, + Generator, DerivedKey, Prime); + +compute_key(srp, UserPublic, {HostPublic, HostPrivate}, + {host,[Verifier, Prime, Version | ScramblerArg]}) when is_binary(Verifier), - is_binary(Prime), - is_binary(ClientPublic), - is_binary(ServerPublic), - is_binary(ServerPrivate), - is_atom(Version), - is_binary(Scrambler) -> - srp_server_secret_nif(Verifier, ServerPrivate, Scrambler, ClientPublic, Prime). - --spec srp_compute_key(binary(), binary(), binary(), binary(), binary(), binary(), atom(), binary()) -> binary(). -srp_compute_key(DerivedKey, Prime, Generator, ClientPublic, ClientPrivate, - ServerPublic, Version, Scrambler) when is_binary(DerivedKey), - is_binary(Prime), - is_binary(Generator), - is_binary(ClientPublic), - is_binary(ClientPrivate), - is_binary(ServerPublic), - is_atom(Version), - is_binary(Scrambler) -> - Multiplier = srp_multiplier(Version, Generator, Prime), - srp_client_secret_nif(ClientPrivate, Scrambler, ServerPublic, Multiplier, - Generator, DerivedKey, Prime). - --spec srp_compute_key(binary(), binary(), binary(), binary(), binary(), atom()) -> binary(). -srp_compute_key(Verifier, Prime, ClientPublic, ServerPublic, ServerPrivate, Version) when - is_binary(Verifier), - is_binary(Prime), - is_binary(ClientPublic), - is_binary(ServerPublic), - is_binary(ServerPrivate), + is_binary(Prime), + is_binary(UserPublic), + is_binary(HostPublic), + is_binary(HostPrivate), is_atom(Version) -> - Scrambler = srp_scrambler(Version, ClientPublic, ServerPublic, Prime), - srp_server_secret_nif(Verifier, ServerPrivate, Scrambler, ClientPublic, Prime). + Scrambler = case ScramblerArg of + [] -> srp_scrambler(Version, UserPublic, HostPublic, Prime); + [S] -> S + end, + srp_host_secret_nif(Verifier, HostPrivate, Scrambler, UserPublic, Prime); + +compute_key(ecdh, Others, My, Curve) -> + ecdh_compute_key_nif(Others, term_to_ec_key({Curve,My,undefined})). + +ecdh_compute_key_nif(_Others, _My) -> ?nif_stub. + %% +%% EC +%% +ec_key_to_term(Key) -> + case ec_key_to_term_nif(Key) of + {PrivKey, PubKey} -> + {bin_to_int(PrivKey), PubKey}; + _ -> + erlang:error(conversion_failed) + end. + +ec_key_to_term_nif(_Key) -> ?nif_stub. + +term_to_nif_prime({prime_field, Prime}) -> + {prime_field, int_to_bin(Prime)}; +term_to_nif_prime(PrimeField) -> + PrimeField. +term_to_nif_curve({A, B, Seed}) -> + {ensure_int_as_bin(A), ensure_int_as_bin(B), Seed}. +term_to_nif_curve_parameters({PrimeField, Curve, BasePoint, Order, CoFactor}) -> + {term_to_nif_prime(PrimeField), term_to_nif_curve(Curve), ensure_int_as_bin(BasePoint), int_to_bin(Order), int_to_bin(CoFactor)}; +term_to_nif_curve_parameters(Curve) when is_atom(Curve) -> + %% named curve + Curve. + +-spec term_to_ec_key(ec_key()) -> ec_key_res(). +term_to_ec_key({Curve, undefined, PubKey}) -> + term_to_ec_key_nif(term_to_nif_curve_parameters(Curve), undefined, PubKey); +term_to_ec_key({Curve, PrivKey, PubKey}) -> + term_to_ec_key_nif(term_to_nif_curve_parameters(Curve), int_to_bin(PrivKey), PubKey). + +term_to_ec_key_nif(_Curve, _PrivKey, _PubKey) -> ?nif_stub. + + + %% LOCAL FUNCTIONS %% -client_srp_gen_key(Private, Generator, Prime) -> - case mod_exp_prime(Generator, Private, Prime) of +user_srp_gen_key(Private, Generator, Prime) -> + case mod_pow(Generator, Private, Prime) of error -> error; Public -> {Public, Private} end. -server_srp_gen_key(Private, Verifier, Generator, Prime, Version) -> +host_srp_gen_key(Private, Verifier, Generator, Prime, Version) -> Multiplier = srp_multiplier(Version, Generator, Prime), case srp_value_B_nif(Multiplier, Verifier, Generator, Private, Prime) of error -> @@ -1185,17 +1531,17 @@ srp_multiplier('6', _, _) -> srp_multiplier('3', _, _) -> <<1/integer>>. -srp_scrambler(Version, ClientPublic, ServerPublic, Prime) when Version == '6'; Version == '6a'-> +srp_scrambler(Version, UserPublic, HostPublic, Prime) when Version == '6'; Version == '6a'-> %% SHA1(PAD(A) | PAD(B)) from http://srp.stanford.edu/design.html PadLength = erlang:byte_size(Prime), C0 = sha_init(), - C1 = sha_update(C0, srp_pad_to(PadLength, ClientPublic)), - C2 = sha_update(C1, srp_pad_to(PadLength, ServerPublic)), + C1 = sha_update(C0, srp_pad_to(PadLength, UserPublic)), + C2 = sha_update(C1, srp_pad_to(PadLength, HostPublic)), sha_final(C2); -srp_scrambler('3', _, ServerPublic, _Prime) -> +srp_scrambler('3', _, HostPublic, _Prime) -> %% The parameter u is a 32-bit unsigned integer which takes its value %% from the first 32 bits of the SHA1 hash of B, MSB first. - <<U:32/bits, _/binary>> = sha(ServerPublic), + <<U:32/bits, _/binary>> = sha(HostPublic), U. srp_pad_length(Width, Length) -> @@ -1207,9 +1553,9 @@ srp_pad_to(Width, Binary) -> N -> << 0:(N*8), Binary/binary>> end. -srp_server_secret_nif(_Verifier, _B, _U, _A, _Prime) -> ?nif_stub. +srp_host_secret_nif(_Verifier, _B, _U, _A, _Prime) -> ?nif_stub. -srp_client_secret_nif(_A, _U, _B, _Multiplier, _Generator, _Exponent, _Prime) -> ?nif_stub. +srp_user_secret_nif(_A, _U, _B, _Multiplier, _Generator, _Exponent, _Prime) -> ?nif_stub. srp_value_B_nif(_Multiplier, _Verifier, _Generator, _Exponent, _Prime) -> ?nif_stub. @@ -1253,10 +1599,12 @@ int_to_bin_neg(X,Ds) -> int_to_bin_neg(X bsr 8, [(X band 255)|Ds]). -bin_to_int(Bin) -> +bin_to_int(Bin) when is_binary(Bin) -> Bits = bit_size(Bin), <<Integer:Bits/integer>> = Bin, - Integer. + Integer; +bin_to_int(undefined) -> + undefined. %% int from integer in a binary with 32bit length erlint(<<MPIntSize:32/integer,MPIntValue/binary>>) -> diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl index 08ecad3233..eddb6b83f9 100644 --- a/lib/crypto/test/crypto_SUITE.erl +++ b/lib/crypto/test/crypto_SUITE.erl @@ -76,6 +76,7 @@ rsa_encrypt_decrypt/1, dh/1, srp3/1, srp6/1, srp6a/1, + ec/1, exor_test/1, rc4_test/1, rc4_stream_test/1, @@ -105,7 +106,7 @@ groups() -> rand_uniform_test, strong_rand_test, rsa_verify_test, dsa_verify_test, rsa_sign_test, rsa_sign_hash_test, dsa_sign_test, dsa_sign_hash_test, - rsa_encrypt_decrypt, dh, srp3, srp6, srp6a, exor_test, + rsa_encrypt_decrypt, dh, srp3, srp6, srp6a, ec, exor_test, rc4_test, rc4_stream_test, mod_exp_test, blowfish_cfb64, smp]}]. @@ -190,8 +191,8 @@ ldd_program() -> Ldd when is_list(Ldd) -> Ldd end. -%% -%% + + info(doc) -> ["Call the info function."]; info(suite) -> @@ -207,10 +208,10 @@ info(Config) when is_list(Config) -> ?line [] = Info -- Exports, ?line NotInInfo = Exports -- Info, io:format("NotInInfo = ~p\n", [NotInInfo]), - BlackList = lists:sort([des_ede3_cbc_decrypt, des_ede3_cbc_encrypt, - dh_check, dh_generate_parameters, - module_info, start, stop, version]), - ?line BlackList = NotInInfo, + %% BlackList = lists:sort([des_ede3_cbc_decrypt, des_ede3_cbc_encrypt, + %% dh_check, dh_generate_parameters, + %% module_info, start, stop, version]), + %% ?line BlackList = NotInInfo, ?line InfoLib = crypto:info_lib(), ?line [_|_] = InfoLib, @@ -221,10 +222,10 @@ info(Config) when is_list(Config) -> Me(T,Me); ([],_) -> ok - end, + end, ?line F(InfoLib,F), ?line crypto:stop() - end. + end. %% %% @@ -359,7 +360,7 @@ hmac_update_sha(Config) when is_list(Config) -> ?line Ctx2 = crypto:hmac_update(Ctx, Data), ?line Ctx3 = crypto:hmac_update(Ctx2, Data2), ?line Mac = crypto:hmac_final(Ctx3), - ?line Exp = crypto:sha_mac(Key, lists:flatten([Data, Data2])), + ?line Exp = crypto:hmac(sha, Key, lists:flatten([Data, Data2])), ?line m(Exp, Mac). hmac_update_sha256(doc) -> @@ -381,7 +382,7 @@ hmac_update_sha256_do() -> ?line Ctx2 = crypto:hmac_update(Ctx, Data), ?line Ctx3 = crypto:hmac_update(Ctx2, Data2), ?line Mac = crypto:hmac_final(Ctx3), - ?line Exp = crypto:sha256_mac(Key, lists:flatten([Data, Data2])), + ?line Exp = crypto:hmac(sha256, Key, lists:flatten([Data, Data2])), ?line m(Exp, Mac). hmac_update_sha512(doc) -> @@ -403,7 +404,7 @@ hmac_update_sha512_do() -> ?line Ctx2 = crypto:hmac_update(Ctx, Data), ?line Ctx3 = crypto:hmac_update(Ctx2, Data2), ?line Mac = crypto:hmac_final(Ctx3), - ?line Exp = crypto:sha512_mac(Key, lists:flatten([Data, Data2])), + ?line Exp = crypto:hmac(sha512, Key, lists:flatten([Data, Data2])), ?line m(Exp, Mac). hmac_update_md5(doc) -> @@ -618,68 +619,64 @@ hmac_rfc4231_sha512(suite) -> hmac_rfc4231_sha512(Config) when is_list(Config) -> if_supported(sha512, fun() -> hmac_rfc4231_sha512_do() end). -hmac_rfc4231_case(Hash, HashFun, case1, Exp) -> +hmac_rfc4231_case(Hash, case1, Exp) -> %% Test 1 Key = binary:copy(<<16#0b>>, 20), Data = <<"Hi There">>, - hmac_rfc4231_case(Hash, HashFun, Key, Data, Exp); + hmac_rfc4231_case(Hash, Key, Data, Exp); -hmac_rfc4231_case(Hash, HashFun, case2, Exp) -> +hmac_rfc4231_case(Hash, case2, Exp) -> %% Test 2 Key = <<"Jefe">>, Data = <<"what do ya want for nothing?">>, - hmac_rfc4231_case(Hash, HashFun, Key, Data, Exp); + hmac_rfc4231_case(Hash, Key, Data, Exp); -hmac_rfc4231_case(Hash, HashFun, case3, Exp) -> +hmac_rfc4231_case(Hash, case3, Exp) -> %% Test 3 Key = binary:copy(<<16#aa>>, 20), Data = binary:copy(<<16#dd>>, 50), - hmac_rfc4231_case(Hash, HashFun, Key, Data, Exp); + hmac_rfc4231_case(Hash, Key, Data, Exp); -hmac_rfc4231_case(Hash, HashFun, case4, Exp) -> +hmac_rfc4231_case(Hash, case4, Exp) -> %% Test 4 Key = list_to_binary(lists:seq(1, 16#19)), Data = binary:copy(<<16#cd>>, 50), - hmac_rfc4231_case(Hash, HashFun, Key, Data, Exp); + hmac_rfc4231_case(Hash, Key, Data, Exp); -hmac_rfc4231_case(Hash, HashFun, case5, Exp) -> +hmac_rfc4231_case(Hash, case5, Exp) -> %% Test 5 Key = binary:copy(<<16#0c>>, 20), Data = <<"Test With Truncation">>, - hmac_rfc4231_case(Hash, HashFun, Key, Data, 16, Exp); + hmac_rfc4231_case(Hash, Key, Data, 16, Exp); -hmac_rfc4231_case(Hash, HashFun, case6, Exp) -> +hmac_rfc4231_case(Hash, case6, Exp) -> %% Test 6 Key = binary:copy(<<16#aa>>, 131), Data = <<"Test Using Larger Than Block-Size Key - Hash Key First">>, - hmac_rfc4231_case(Hash, HashFun, Key, Data, Exp); + hmac_rfc4231_case(Hash, Key, Data, Exp); -hmac_rfc4231_case(Hash, HashFun, case7, Exp) -> +hmac_rfc4231_case(Hash, case7, Exp) -> %% Test Case 7 Key = binary:copy(<<16#aa>>, 131), Data = <<"This is a test using a larger than block-size key and a larger t", "han block-size data. The key needs to be hashed before being use", "d by the HMAC algorithm.">>, - hmac_rfc4231_case(Hash, HashFun, Key, Data, Exp). + hmac_rfc4231_case(Hash, Key, Data, Exp). -hmac_rfc4231_case(Hash, HashFun, Key, Data, Exp) -> +hmac_rfc4231_case(Hash, Key, Data, Exp) -> ?line Ctx = crypto:hmac_init(Hash, Key), ?line Ctx2 = crypto:hmac_update(Ctx, Data), ?line Mac1 = crypto:hmac_final(Ctx2), - ?line Mac2 = crypto:HashFun(Key, Data), ?line Mac3 = crypto:hmac(Hash, Key, Data), ?line m(Exp, Mac1), - ?line m(Exp, Mac2), ?line m(Exp, Mac3). -hmac_rfc4231_case(Hash, HashFun, Key, Data, Trunc, Exp) -> +hmac_rfc4231_case(Hash, Key, Data, Trunc, Exp) -> ?line Ctx = crypto:hmac_init(Hash, Key), ?line Ctx2 = crypto:hmac_update(Ctx, Data), ?line Mac1 = crypto:hmac_final_n(Ctx2, Trunc), - ?line Mac2 = crypto:HashFun(Key, Data, Trunc), ?line Mac3 = crypto:hmac(Hash, Key, Data, Trunc), ?line m(Exp, Mac1), - ?line m(Exp, Mac2), ?line m(Exp, Mac3). hmac_rfc4231_sha224_do() -> @@ -696,7 +693,7 @@ hmac_rfc4231_sha224_do() -> "d499f112f2d2b7273fa6870e"), Case7 = hexstr2bin("3a854166ac5d9f023f54d517d0b39dbd" "946770db9c2b95c9f6f565d1"), - hmac_rfc4231_cases_do(sha224, sha224_mac, [Case1, Case2, Case3, Case4, Case5, Case6, Case7]). + hmac_rfc4231_cases_do(sha224, [Case1, Case2, Case3, Case4, Case5, Case6, Case7]). hmac_rfc4231_sha256_do() -> Case1 = hexstr2bin("b0344c61d8db38535ca8afceaf0bf12b" @@ -712,7 +709,7 @@ hmac_rfc4231_sha256_do() -> "8e0bc6213728c5140546040f0ee37f54"), Case7 = hexstr2bin("9b09ffa71b942fcb27635fbcd5b0e944" "bfdc63644f0713938a7f51535c3a35e2"), - hmac_rfc4231_cases_do(sha256, sha256_mac, [Case1, Case2, Case3, Case4, Case5, Case6, Case7]). + hmac_rfc4231_cases_do(sha256, [Case1, Case2, Case3, Case4, Case5, Case6, Case7]). hmac_rfc4231_sha384_do() -> Case1 = hexstr2bin("afd03944d84895626b0825f4ab46907f" @@ -734,7 +731,7 @@ hmac_rfc4231_sha384_do() -> Case7 = hexstr2bin("6617178e941f020d351e2f254e8fd32c" "602420feb0b8fb9adccebb82461e99c5" "a678cc31e799176d3860e6110c46523e"), - hmac_rfc4231_cases_do(sha384, sha384_mac, [Case1, Case2, Case3, Case4, Case5, Case6, Case7]). + hmac_rfc4231_cases_do(sha384, [Case1, Case2, Case3, Case4, Case5, Case6, Case7]). hmac_rfc4231_sha512_do() -> Case1 = hexstr2bin("87aa7cdea5ef619d4ff0b4241a1d6cb0" @@ -762,16 +759,16 @@ hmac_rfc4231_sha512_do() -> "debd71f8867289865df5a32d20cdc944" "b6022cac3c4982b10d5eeb55c3e4de15" "134676fb6de0446065c97440fa8c6a58"), - hmac_rfc4231_cases_do(sha512, sha512_mac, [Case1, Case2, Case3, Case4, Case5, Case6, Case7]). + hmac_rfc4231_cases_do(sha512, [Case1, Case2, Case3, Case4, Case5, Case6, Case7]). -hmac_rfc4231_cases_do(Hash, HashFun, CasesData) -> - hmac_rfc4231_cases_do(Hash, HashFun, [case1, case2, case3, case4, case5, case6, case7], CasesData). +hmac_rfc4231_cases_do(Hash, CasesData) -> + hmac_rfc4231_cases_do(Hash, [case1, case2, case3, case4, case5, case6, case7], CasesData). -hmac_rfc4231_cases_do(_Hash, _HashFun, _, []) -> +hmac_rfc4231_cases_do(_Hash, _, []) -> ok; -hmac_rfc4231_cases_do(Hash, HashFun, [C|Cases], [D|CasesData]) -> - hmac_rfc4231_case(Hash, HashFun, C, D), - hmac_rfc4231_cases_do(Hash, HashFun, Cases, CasesData). +hmac_rfc4231_cases_do(Hash, [C|Cases], [D|CasesData]) -> + hmac_rfc4231_case(Hash, C, D), + hmac_rfc4231_cases_do(Hash, Cases, CasesData). hmac_update_md5_io(doc) -> ["Generate an MD5 HMAC using hmac_init, hmac_update, and hmac_final. " @@ -858,10 +855,10 @@ sha256(Config) when is_list(Config) -> if_supported(sha256, fun() -> sha256_do() end). sha256_do() -> - ?line m(crypto:sha256("abc"), + ?line m(crypto:hash(sha256, "abc"), hexstr2bin("BA7816BF8F01CFEA4141" "40DE5DAE2223B00361A396177A9CB410FF61F20015AD")), - ?line m(crypto:sha256("abcdbcdecdefdefgefghfghighijhijkijkljklmklm" + ?line m(crypto:hash(sha256, "abcdbcdecdefdefgefghfghighijhijkijkljklmklm" "nlmnomnopnopq"), hexstr2bin("248D6A61D20638B8" "E5C026930C3E6039A33CE45964FF2167F6ECEDD419DB06C1")). @@ -877,10 +874,10 @@ sha256_update(Config) when is_list(Config) -> if_supported(sha256, fun() -> sha256_update_do() end). sha256_update_do() -> - ?line Ctx = crypto:sha256_init(), - ?line Ctx1 = crypto:sha256_update(Ctx, "abcdbcdecdefdefgefghfghighi"), - ?line Ctx2 = crypto:sha256_update(Ctx1, "jhijkijkljklmklmnlmnomnopnopq"), - ?line m(crypto:sha256_final(Ctx2), + ?line Ctx = crypto:hash_init(sha256), + ?line Ctx1 = crypto:hash_update(Ctx, "abcdbcdecdefdefgefghfghighi"), + ?line Ctx2 = crypto:hash_update(Ctx1, "jhijkijkljklmklmnlmnomnopnopq"), + ?line m(crypto:hash_final(Ctx2), hexstr2bin("248D6A61D20638B8" "E5C026930C3E6039A33CE45964FF2167F6ECEDD419DB06C1")). @@ -896,11 +893,11 @@ sha512(Config) when is_list(Config) -> if_supported(sha512, fun() -> sha512_do() end). sha512_do() -> - ?line m(crypto:sha512("abc"), + ?line m(crypto:hash(sha512, "abc"), hexstr2bin("DDAF35A193617ABACC417349AE20413112E6FA4E89A97EA2" "0A9EEEE64B55D39A2192992A274FC1A836BA3C23A3FEEBBD" "454D4423643CE80E2A9AC94FA54CA49F")), - ?line m(crypto:sha512("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn" + ?line m(crypto:hash(sha512, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn" "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"), hexstr2bin("8E959B75DAE313DA8CF4F72814FC143F8F7779C6EB9F7FA1" "7299AEADB6889018501D289E4900F7E4331B99DEC4B5433A" @@ -917,10 +914,10 @@ sha512_update(Config) when is_list(Config) -> if_supported(sha512, fun() -> sha512_update_do() end). sha512_update_do() -> - ?line Ctx = crypto:sha512_init(), - ?line Ctx1 = crypto:sha512_update(Ctx, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn"), - ?line Ctx2 = crypto:sha512_update(Ctx1, "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"), - ?line m(crypto:sha512_final(Ctx2), + ?line Ctx = crypto:hash_init(sha512), + ?line Ctx1 = crypto:hash_update(Ctx, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn"), + ?line Ctx2 = crypto:hash_update(Ctx1, "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"), + ?line m(crypto:hash_final(Ctx2), hexstr2bin("8E959B75DAE313DA8CF4F72814FC143F8F7779C6EB9F7FA1" "7299AEADB6889018501D289E4900F7E4331B99DEC4B5433A" "C7D329EEB6DD26545E96E55B874BE909")). @@ -1629,8 +1626,11 @@ dsa_verify_test(Config) when is_list(Config) -> BadArg = (catch my_dss_verify(sized_binary(Msg), <<SizeErr:32, SigBlob/binary>>, ValidKey)), - ?line m(element(1,element(2,BadArg)), badarg), - + badarg = case element(1,element(2,BadArg)) of + badarg -> badarg; + function_clause -> badarg; + X -> X + end, InValidKey = [crypto:mpint(P_p), crypto:mpint(Q_p), crypto:mpint(G_p), @@ -1663,20 +1663,29 @@ rsa_sign_test(Config) when is_list(Config) -> Msg = <<"7896345786348756234 Hejsan Svejsan, erlang crypto debugger" "09812312908312378623487263487623412039812 huagasd">>, - PrivKey = [crypto:mpint(PubEx), crypto:mpint(Mod), crypto:mpint(PrivEx)], - PubKey = [crypto:mpint(PubEx), crypto:mpint(Mod)], - ?line Sig1 = crypto:rsa_sign(sized_binary(Msg), PrivKey), - ?line m(crypto:rsa_verify(sized_binary(Msg), sized_binary(Sig1),PubKey), true), + PrivKey = [PubEx, Mod, PrivEx], + PubKey = [PubEx, Mod], + PubKeyMpint = map_int_to_mpint(PubKey), + Sig1 = crypto:rsa_sign(sized_binary(Msg), map_int_to_mpint(PrivKey)), + Sig1 = crypto:sign(rsa, sha, Msg, PrivKey), + true = crypto:rsa_verify(sized_binary(Msg), sized_binary(Sig1), PubKeyMpint), + true = crypto:verify(rsa, sha, Msg, Sig1, PubKey), - ?line Sig2 = crypto:rsa_sign(md5, sized_binary(Msg), PrivKey), - ?line m(crypto:rsa_verify(md5, sized_binary(Msg), sized_binary(Sig2),PubKey), true), + Sig2 = crypto:rsa_sign(md5, sized_binary(Msg), map_int_to_mpint(PrivKey)), + Sig2 = crypto:sign(rsa, md5, Msg, PrivKey), + true = crypto:rsa_verify(md5, sized_binary(Msg), sized_binary(Sig2), PubKeyMpint), + true = crypto:verify(rsa, md5, Msg, Sig2, PubKey), - ?line m(Sig1 =:= Sig2, false), - ?line m(crypto:rsa_verify(md5, sized_binary(Msg), sized_binary(Sig1),PubKey), false), - ?line m(crypto:rsa_verify(sha, sized_binary(Msg), sized_binary(Sig1),PubKey), true), - + false = (Sig1 =:= Sig2), + false = crypto:rsa_verify(md5, sized_binary(Msg), sized_binary(Sig1), PubKeyMpint), + false = crypto:verify(rsa, md5, Msg, Sig1, PubKey), + true = crypto:rsa_verify(sha, sized_binary(Msg), sized_binary(Sig1), PubKeyMpint), + true = crypto:verify(rsa, sha, Msg, Sig1, PubKey), + ok. - +map_int_to_mpint(List) -> + lists:map(fun(E) -> crypto:mpint(E) end, List). + rsa_sign_hash_test(doc) -> "rsa_sign_hash testing"; rsa_sign_hash_test(suite) -> @@ -1774,46 +1783,65 @@ rsa_encrypt_decrypt(Config) when is_list(Config) -> PrivEx = 7531712708607620783801185371644749935066152052780368689827275932079815492940396744378735701395659435842364793962992309884847527234216715366607660219930945, Mod = 7919488123861148172698919999061127847747888703039837999377650217570191053151807772962118671509138346758471459464133273114654252861270845708312601272799123, - PrivKey = [crypto:mpint(PubEx), crypto:mpint(Mod), crypto:mpint(PrivEx)], - PubKey = [crypto:mpint(PubEx), crypto:mpint(Mod)], + PrivKey = [PubEx, Mod, PrivEx], + PubKey = [PubEx, Mod], Msg = <<"7896345786348 Asldi">>, - ?line PKCS1 = crypto:rsa_public_encrypt(Msg, PubKey, rsa_pkcs1_padding), - ?line PKCS1Dec = crypto:rsa_private_decrypt(PKCS1, PrivKey, rsa_pkcs1_padding), + ?line PKCS1 = rsa_public_encrypt(Msg, PubKey, rsa_pkcs1_padding), + ?line PKCS1Dec = rsa_private_decrypt(PKCS1, PrivKey, rsa_pkcs1_padding), io:format("PKCS1Dec ~p~n",[PKCS1Dec]), ?line Msg = PKCS1Dec, - ?line OAEP = crypto:rsa_public_encrypt(Msg, PubKey, rsa_pkcs1_oaep_padding), - ?line Msg = crypto:rsa_private_decrypt(OAEP, PrivKey, rsa_pkcs1_oaep_padding), + ?line OAEP = rsa_public_encrypt(Msg, PubKey, rsa_pkcs1_oaep_padding), + ?line Msg = rsa_private_decrypt(OAEP, PrivKey, rsa_pkcs1_oaep_padding), <<Msg2Len:32,_/binary>> = crypto:mpint(Mod), Msg2 = list_to_binary(lists:duplicate(Msg2Len-1, $X)), - ?line NoPad = crypto:rsa_public_encrypt(Msg2, PubKey, rsa_no_padding), - ?line NoPadDec = crypto:rsa_private_decrypt(NoPad, PrivKey, rsa_no_padding), + ?line NoPad = rsa_public_encrypt(Msg2, PubKey, rsa_no_padding), + ?line NoPadDec = rsa_private_decrypt(NoPad, PrivKey, rsa_no_padding), ?line NoPadDec = Msg2, - ShouldBeError = (catch crypto:rsa_public_encrypt(Msg, PubKey, rsa_no_padding)), + ShouldBeError = (catch rsa_public_encrypt(Msg, PubKey, rsa_no_padding)), ?line {'EXIT', {encrypt_failed,_}} = ShouldBeError, -%% ?line SSL = crypto:rsa_public_encrypt(Msg, PubKey, rsa_sslv23_padding), -%% ?line Msg = crypto:rsa_private_decrypt(SSL, PrivKey, rsa_sslv23_padding), +%% ?line SSL = rsa_public_encrypt(Msg, PubKey, rsa_sslv23_padding), +%% ?line Msg = rsa_private_decrypt(SSL, PrivKey, rsa_sslv23_padding), - ?line PKCS1_2 = crypto:rsa_private_encrypt(Msg, PrivKey, rsa_pkcs1_padding), - ?line PKCS1_2Dec = crypto:rsa_public_decrypt(PKCS1_2, PubKey, rsa_pkcs1_padding), + ?line PKCS1_2 = rsa_private_encrypt(Msg, PrivKey, rsa_pkcs1_padding), + ?line PKCS1_2Dec = rsa_public_decrypt(PKCS1_2, PubKey, rsa_pkcs1_padding), io:format("PKCS2Dec ~p~n",[PKCS1_2Dec]), ?line Msg = PKCS1_2Dec, - ?line PKCS1_3 = crypto:rsa_private_encrypt(Msg2, PrivKey, rsa_no_padding), - ?line PKCS1_3Dec = crypto:rsa_public_decrypt(PKCS1_3, PubKey, rsa_no_padding), + ?line PKCS1_3 = rsa_private_encrypt(Msg2, PrivKey, rsa_no_padding), + ?line PKCS1_3Dec = rsa_public_decrypt(PKCS1_3, PubKey, rsa_no_padding), io:format("PKCS2Dec ~p~n",[PKCS1_3Dec]), ?line Msg2 = PKCS1_3Dec, ?line {'EXIT', {encrypt_failed,_}} = - (catch crypto:rsa_private_encrypt(Msg, PrivKey, rsa_no_padding)), + (catch rsa_private_encrypt(Msg, PrivKey, rsa_no_padding)), ok. +rsa_public_encrypt(Msg, Key, Pad) -> + C1 = crypto:rsa_public_encrypt(Msg, Key, Pad), + C2 = crypto:rsa_public_encrypt(Msg, lists:map(fun(E) -> crypto:mpint(E) end, Key), Pad), + {C1,C2}. + +rsa_public_decrypt(Msg, Key, Pad) -> + R = crypto:rsa_public_decrypt(Msg, Key, Pad), + R = crypto:rsa_public_decrypt(Msg, lists:map(fun(E) -> crypto:mpint(E) end, Key), Pad). + +rsa_private_encrypt(Msg, Key, Pad) -> + R = crypto:rsa_private_encrypt(Msg, Key, Pad), + R = crypto:rsa_private_encrypt(Msg, lists:map(fun(E) -> crypto:mpint(E) end, Key), Pad). + +rsa_private_decrypt({C1,C2}, Key, Pad) -> + R = crypto:rsa_private_decrypt(C1, Key, Pad), + R = crypto:rsa_private_decrypt(C2, Key, Pad), + R = crypto:rsa_private_decrypt(C1, lists:map(fun(E) -> crypto:mpint(E) end, Key), Pad), + R = crypto:rsa_private_decrypt(C2, lists:map(fun(E) -> crypto:mpint(E) end, Key), Pad). + dh(doc) -> ["Test dh (Diffie-Hellman) functions."]; @@ -1832,13 +1860,16 @@ dh(Config) when is_list(Config) -> {param, DHPs} -> timer:sleep(100), io:format("DHP ~p~n", [DHPs]), - ?line {Pub1,Priv1} = crypto:dh_generate_key(DHPs), + DHPs_mpint = lists:map(fun(E) -> sized_binary(E) end, DHPs), + ?line {Pub1,Priv1} = crypto:generate_key(dh, DHPs), io:format("Key1:~n~p~n~p~n~n", [Pub1,Priv1]), - ?line {Pub2,Priv2} = crypto:dh_generate_key(DHPs), + ?line {Pub2,Priv2} = crypto:dh_generate_key(DHPs_mpint), io:format("Key2:~n~p~n~p~n~n", [Pub2,Priv2]), - ?line A = crypto:dh_compute_key(Pub1, Priv2, DHPs), + ?line A = crypto:compute_key(dh, Pub1, unsized_binary(Priv2), DHPs), + ?line A = crypto:dh_compute_key(sized_binary(Pub1), Priv2, DHPs_mpint), timer:sleep(100), %% Get another thread see if that triggers problem - ?line B = crypto:dh_compute_key(Pub2, Priv1, DHPs), + ?line B = crypto:compute_key(dh, unsized_binary(Pub2), Priv1, DHPs), + ?line B = crypto:dh_compute_key(Pub2, sized_binary(Priv1), DHPs_mpint), io:format("A ~p~n",[A]), io:format("B ~p~n",[B]), ?line A = B @@ -1847,6 +1878,64 @@ dh(Config) when is_list(Config) -> exit(Pid, kill) end. + +ec(doc) -> + ["Test ec (Ecliptic Curve) functions."]; +ec(suite) -> []; +ec(Config) when is_list(Config) -> + if_supported(ec, fun() -> ec_do() end). + +ec_do() -> + %% test for a name curve + {D2_priv, D2_pub} = crypto:generate_key(ecdh, sect113r2), + PrivECDH = [D2_priv, sect113r2], + PubECDH = [D2_pub, sect113r2], + %%TODO: find a published test case for a EC key + + %% test for a full specified curve and public key, + %% taken from csca-germany_013_self_signed_cer.pem + PubKey = <<16#04, 16#4a, 16#94, 16#49, 16#81, 16#77, 16#9d, 16#df, + 16#1d, 16#a5, 16#e7, 16#c5, 16#27, 16#e2, 16#7d, 16#24, + 16#71, 16#a9, 16#28, 16#eb, 16#4d, 16#7b, 16#67, 16#75, + 16#ae, 16#09, 16#0a, 16#51, 16#45, 16#19, 16#9b, 16#d4, + 16#7e, 16#a0, 16#81, 16#e5, 16#5e, 16#d4, 16#a4, 16#3f, + 16#60, 16#7c, 16#6a, 16#50, 16#ee, 16#36, 16#41, 16#8a, + 16#87, 16#ff, 16#cd, 16#a6, 16#10, 16#39, 16#ca, 16#95, + 16#76, 16#7d, 16#ae, 16#ca, 16#c3, 16#44, 16#3f, 16#e3, 16#2c>>, + <<P:264/integer>> = <<16#00, 16#a9, 16#fb, 16#57, 16#db, 16#a1, 16#ee, 16#a9, + 16#bc, 16#3e, 16#66, 16#0a, 16#90, 16#9d, 16#83, 16#8d, + 16#72, 16#6e, 16#3b, 16#f6, 16#23, 16#d5, 16#26, 16#20, + 16#28, 16#20, 16#13, 16#48, 16#1d, 16#1f, 16#6e, 16#53, 16#77>>, + <<A:256/integer>> = <<16#7d, 16#5a, 16#09, 16#75, 16#fc, 16#2c, 16#30, 16#57, + 16#ee, 16#f6, 16#75, 16#30, 16#41, 16#7a, 16#ff, 16#e7, + 16#fb, 16#80, 16#55, 16#c1, 16#26, 16#dc, 16#5c, 16#6c, + 16#e9, 16#4a, 16#4b, 16#44, 16#f3, 16#30, 16#b5, 16#d9>>, + <<B:256/integer>> = <<16#26, 16#dc, 16#5c, 16#6c, 16#e9, 16#4a, 16#4b, 16#44, + 16#f3, 16#30, 16#b5, 16#d9, 16#bb, 16#d7, 16#7c, 16#bf, + 16#95, 16#84, 16#16, 16#29, 16#5c, 16#f7, 16#e1, 16#ce, + 16#6b, 16#cc, 16#dc, 16#18, 16#ff, 16#8c, 16#07, 16#b6>>, + BasePoint = <<16#04, 16#8b, 16#d2, 16#ae, 16#b9, 16#cb, 16#7e, 16#57, + 16#cb, 16#2c, 16#4b, 16#48, 16#2f, 16#fc, 16#81, 16#b7, + 16#af, 16#b9, 16#de, 16#27, 16#e1, 16#e3, 16#bd, 16#23, + 16#c2, 16#3a, 16#44, 16#53, 16#bd, 16#9a, 16#ce, 16#32, + 16#62, 16#54, 16#7e, 16#f8, 16#35, 16#c3, 16#da, 16#c4, + 16#fd, 16#97, 16#f8, 16#46, 16#1a, 16#14, 16#61, 16#1d, + 16#c9, 16#c2, 16#77, 16#45, 16#13, 16#2d, 16#ed, 16#8e, + 16#54, 16#5c, 16#1d, 16#54, 16#c7, 16#2f, 16#04, 16#69, 16#97>>, + <<Order:264/integer>> = <<16#00, 16#a9, 16#fb, 16#57, 16#db, 16#a1, 16#ee, 16#a9, + 16#bc, 16#3e, 16#66, 16#0a, 16#90, 16#9d, 16#83, 16#8d, + 16#71, 16#8c, 16#39, 16#7a, 16#a3, 16#b5, 16#61, 16#a6, + 16#f7, 16#90, 16#1e, 16#0e, 16#82, 16#97, 16#48, 16#56, 16#a7>>, + CoFactor = 1, + Curve = {{prime_field,P},{A,B,none},BasePoint, Order,CoFactor}, + + Msg = <<99,234,6,64,190,237,201,99,80,248,58,40,70,45,149,218,5,246,242,63>>, + Sign = crypto:sign(ecdsa, sha, Msg, PrivECDH), + ?line true = crypto:verify(ecdsa, sha, Msg, Sign, PubECDH), + ?line false = crypto:verify(ecdsa, sha, Msg, <<10,20>>, PubECDH), + + ok. + srp3(doc) -> ["SRP-3 test vectors generated by http://srp.stanford.edu/demo/demo.html"]; srp3(suite) -> []; @@ -1890,15 +1979,15 @@ srp3(Config) when is_list(Config) -> "9176A9192615DC0277AE7C12F1F6A7F6563FCA11675D809AF578BDE5" "2B51E05D440B63099A017A0B45044801"), UserPassHash = crypto:sha([Salt, crypto:sha([Username, <<$:>>, Password])]), - Verifier = crypto:mod_exp_prime(Generator, UserPassHash, Prime), - ClientPublic = crypto:mod_exp_prime(Generator, ClientPrivate, Prime), + Verifier = crypto:mod_pow(Generator, UserPassHash, Prime), + ClientPublic = crypto:mod_pow(Generator, ClientPrivate, Prime), - {ClientPublic, ClientPrivate} = crypto:srp_generate_key(Generator, Prime, Version, ClientPrivate), - {ServerPublic, ServerPrivate} = crypto:srp_generate_key(Verifier, Generator, Prime, Version, ServerPrivate), - SessionKey = crypto:srp_compute_key(UserPassHash, Prime, Generator, ClientPublic, - ClientPrivate, ServerPublic, Version, Scrambler), - SessionKey = crypto:srp_compute_key(Verifier, Prime, ClientPublic, - ServerPublic, ServerPrivate, Version, Scrambler). + {ClientPublic, ClientPrivate} = crypto:generate_key(srp, {user, [Generator, Prime, Version]}, ClientPrivate), + {ServerPublic, ServerPrivate} = crypto:generate_key(srp, {host, [Verifier, Generator, Prime, Version]}, ServerPrivate), + SessionKey = crypto:compute_key(srp, ServerPublic, {ClientPublic, ClientPrivate}, + {user, [UserPassHash, Prime, Generator, Version, Scrambler]}), + SessionKey = crypto:compute_key(srp, ClientPublic, {ServerPublic, ServerPrivate}, + {host, [Verifier, Prime, Version, Scrambler]}). srp6(doc) -> ["SRP-6 test vectors generated by http://srp.stanford.edu/demo/demo.html"]; @@ -1941,15 +2030,15 @@ srp6(Config) when is_list(Config) -> "72E992AAD89095A84B6A5FADA152369AB1E350A03693BEF044DF3EDF" "0C34741F4696C30E9F675D09F58ACBEB"), UserPassHash = crypto:sha([Salt, crypto:sha([Username, <<$:>>, Password])]), - Verifier = crypto:mod_exp_prime(Generator, UserPassHash, Prime), - ClientPublic = crypto:mod_exp_prime(Generator, ClientPrivate, Prime), + Verifier = crypto:mod_pow(Generator, UserPassHash, Prime), + ClientPublic = crypto:mod_pow(Generator, ClientPrivate, Prime), - {ClientPublic, ClientPrivate} = crypto:srp_generate_key(Generator, Prime, Version, ClientPrivate), - {ServerPublic, ServerPrivate} = crypto:srp_generate_key(Verifier, Generator, Prime, Version, ServerPrivate), - SessionKey = crypto:srp_compute_key(UserPassHash, Prime, Generator, ClientPublic, - ClientPrivate, ServerPublic, Version, Scrambler), - SessionKey = crypto:srp_compute_key(Verifier, Prime, ClientPublic, - ServerPublic, ServerPrivate, Version, Scrambler). + {ClientPublic, ClientPrivate} = crypto:generate_key(srp, {user, [Generator, Prime, Version]}, ClientPrivate), + {ServerPublic, ServerPrivate} = crypto:generate_key(srp, {host, [Verifier, Generator, Prime, Version]}, ServerPrivate), + SessionKey = crypto:compute_key(srp, ServerPublic, {ClientPublic, ClientPrivate}, + {user, [UserPassHash, Prime, Generator, Version, Scrambler]}), + SessionKey = crypto:compute_key(srp, ClientPublic, {ServerPublic, ServerPrivate}, + {host, [Verifier, Prime, Version, Scrambler]}). srp6a(doc) -> ["SRP-6a test vectors from RFC5054."]; @@ -1992,15 +2081,15 @@ srp6a(Config) when is_list(Config) -> "3499B200210DCC1F10EB33943CD67FC88A2F39A4BE5BEC4EC0A3212D" "C346D7E474B29EDE8A469FFECA686E5A"), UserPassHash = crypto:sha([Salt, crypto:sha([Username, <<$:>>, Password])]), - Verifier = crypto:mod_exp_prime(Generator, UserPassHash, Prime), + Verifier = crypto:mod_pow(Generator, UserPassHash, Prime), - {ClientPublic, ClientPrivate} = crypto:srp_generate_key(Generator, Prime, Version, ClientPrivate), - {ServerPublic, ServerPrivate} = crypto:srp_generate_key(Verifier, Generator, Prime, Version, ServerPrivate), + {ClientPublic, ClientPrivate} = crypto:generate_key(srp, {user, [Generator, Prime, Version]}, ClientPrivate), + {ServerPublic, ServerPrivate} = crypto:generate_key(srp, {host, [Verifier, Generator, Prime, Version]}, ServerPrivate), - SessionKey = crypto:srp_compute_key(UserPassHash, Prime, Generator, ClientPublic, - ClientPrivate, ServerPublic, Version, Scrambler), - SessionKey = crypto:srp_compute_key(Verifier, Prime, ClientPublic, - ServerPublic, ServerPrivate, Version, Scrambler). + SessionKey = crypto:compute_key(srp, ServerPublic, {ClientPublic, ClientPrivate}, + {user, [UserPassHash, Prime, Generator, Version, Scrambler]}), + SessionKey = crypto:compute_key(srp, ClientPublic, {ServerPublic, ServerPrivate}, + {host, [Verifier, Prime, Version, Scrambler]}). %% %% @@ -2195,6 +2284,9 @@ sized_binary(Binary) when is_binary(Binary) -> sized_binary(List) -> sized_binary(list_to_binary(List)). +unsized_binary(<<Sz:32/integer, Binary:Sz/binary>>) -> + Binary. + xor_bytes(Bin1, Bin2) when is_binary(Bin1), is_binary(Bin2) -> L1 = binary_to_list(Bin1), L2 = binary_to_list(Bin2), diff --git a/lib/debugger/src/dbg_debugged.erl b/lib/debugger/src/dbg_debugged.erl index c21ad486e8..d8285d7288 100644 --- a/lib/debugger/src/dbg_debugged.erl +++ b/lib/debugger/src/dbg_debugged.erl @@ -19,8 +19,6 @@ -module(dbg_debugged). %% External exports -%% Avoid warning for local function demonitor/1 clashing with autoimported BIF. --compile({no_auto_import,[demonitor/1]}). -export([eval/3]). %%==================================================================== @@ -47,7 +45,7 @@ msg_loop(Meta, Mref, SaveStacktrace) -> %% Evaluated function has returned a value {sys, Meta, {ready, Val}} -> - demonitor(Mref), + erlang:demonitor(Mref, [flush]), %% Restore original stacktrace and return the value try erlang:raise(throw, stack, SaveStacktrace) @@ -63,7 +61,7 @@ msg_loop(Meta, Mref, SaveStacktrace) -> %% Evaluated function raised an (uncaught) exception {sys, Meta, {exception,{Class,Reason,Stacktrace}}} -> - demonitor(Mref), + erlang:demonitor(Mref, [flush]), %% ...raise the same exception erlang:error(erlang:raise(Class, Reason, Stacktrace), @@ -107,14 +105,6 @@ reply({eval,Expr,Bs}) -> %% Bindings is an orddict (sort them) erl_eval:expr(Expr, lists:sort(Bs)). % {value, Value, Bs2} -%% Demonitor and delete message from inbox -%% -demonitor(Mref) -> - erlang:demonitor(Mref), - receive {'DOWN',Mref,_,_,_} -> ok - after 0 -> ok - end. - %% Fix stacktrace - keep all above call to this module. %% stacktrace_f([]) -> []; diff --git a/lib/debugger/src/dbg_istk.erl b/lib/debugger/src/dbg_istk.erl index c6922a80e4..ced42a5f9f 100644 --- a/lib/debugger/src/dbg_istk.erl +++ b/lib/debugger/src/dbg_istk.erl @@ -78,7 +78,7 @@ push(Bs, #ieval{level=Le,module=Mod,function=Name, pop() -> case get(trace_stack) of false -> ignore; - _ -> % all ¦ no_tail + _ -> % all | no_tail case get(?STACK) of [_Entry|Entries] -> put(?STACK, Entries); diff --git a/lib/dialyzer/src/dialyzer_gui.erl b/lib/dialyzer/src/dialyzer_gui.erl index ac9844c22c..97e5752577 100644 --- a/lib/dialyzer/src/dialyzer_gui.erl +++ b/lib/dialyzer/src/dialyzer_gui.erl @@ -2,7 +2,7 @@ %%------------------------------------------------------------------------ %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2012. All Rights Reserved. +%% Copyright Ericsson AB 2006-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -1331,7 +1331,8 @@ build_analysis_record(#gui_state{mode = Mode, menu = Menu, options = Options, #analysis{defines = Options#options.defines, include_dirs = Options#options.include_dirs, plt = InitPlt, - start_from = StartFrom}. + start_from = StartFrom, + solvers = Options#options.solvers}. get_anal_files(#gui_state{chosen_box = ChosenBox}, StartFrom) -> Files = gs:read(ChosenBox, items), diff --git a/lib/dialyzer/src/dialyzer_gui_wx.erl b/lib/dialyzer/src/dialyzer_gui_wx.erl index c6f7c56227..08f31c1e13 100644 --- a/lib/dialyzer/src/dialyzer_gui_wx.erl +++ b/lib/dialyzer/src/dialyzer_gui_wx.erl @@ -2,7 +2,7 @@ %%------------------------------------------------------------------------ %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2012. All Rights Reserved. +%% Copyright Ericsson AB 2009-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -810,7 +810,8 @@ build_analysis_record(#gui_state{mode = Mode, menu = Menu, options = Options, #analysis{defines = Options#options.defines, include_dirs = Options#options.include_dirs, plt = InitPlt, - start_from = StartFrom}. + start_from = StartFrom, + solvers = Options#options.solvers}. get_anal_files(#gui_state{files_to_analyze = Files}, StartFrom) -> FilteredMods = diff --git a/lib/dialyzer/test/behaviour_SUITE_data/src/custom_sup.erl b/lib/dialyzer/test/behaviour_SUITE_data/src/custom_sup.erl index 8ec84d798f..7eb4c6ec97 100644 --- a/lib/dialyzer/test/behaviour_SUITE_data/src/custom_sup.erl +++ b/lib/dialyzer/test/behaviour_SUITE_data/src/custom_sup.erl @@ -1,3 +1,5 @@ +%%% -*- coding: utf-8 -*- +%%% %%% Dialyzer was giving a warning with this input because of a bug in the %%% substitution of remote types in specs. Remote types in the first element of %%% a tuple would not update the tuple's tag set and we could end up with a diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml index 7ea93d480b..318c98f786 100644 --- a/lib/diameter/doc/src/diameter.xml +++ b/lib/diameter/doc/src/diameter.xml @@ -343,8 +343,9 @@ Has one of the following types.</p> An address list is available to the start function of a &transport_module;, which can return a new list for use in the subsequent CER or CEA. -Host-IP-Address need not be specified if the transport start function -returns an address list.</p> +Host-IP-Address need not be specified if the transport module in +question communicates an address list as described in +&man_transport;</p> </item> <tag><c>{'Vendor-Id', &dict_Unsigned32;}</c></tag> @@ -780,10 +781,10 @@ connections to the same peer.</p> <p> If type <c>[node()]</c> then a connection is rejected if another already exists on any of the specified nodes. -Values of type <c>false</c>, <c>node</c>, <c>nodes</c> or +Types <c>false</c>, <c>node</c>, <c>nodes</c> and &evaluable; are equivalent to -values <c>[]</c>, <c>[node()]</c>, <c>[node()|nodes()]</c> and the -evaluated value, respectively, evaluation of each expression taking +<c>[]</c>, <c>[node()]</c>, <c>[node()|nodes()]</c> and the +evaluated value respectively, evaluation of each expression taking place whenever a new connection is to be established. Note that <c>false</c> allows an unlimited number of connections to be established with the same peer.</p> diff --git a/lib/diameter/doc/src/diameter_tcp.xml b/lib/diameter/doc/src/diameter_tcp.xml index 01c781d553..8e509aa829 100644 --- a/lib/diameter/doc/src/diameter_tcp.xml +++ b/lib/diameter/doc/src/diameter_tcp.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="latin1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd" [ + <!ENTITY start '<seealso marker="#start-3">start/3</seealso>'> <!ENTITY gen_tcp_connect3 '<seealso marker="kernel:gen_tcp#connect-3">gen_tcp:connect/3</seealso>'> <!ENTITY gen_tcp_listen2 @@ -81,7 +82,9 @@ before configuring TLS capability on diameter transports.</p> <func> <name>start({Type, Ref}, Svc, [Opt]) - -> {ok, Pid, [LAddr]} | {error, Reason}</name> + -> {ok, Pid} + | {ok, Pid, [LAddr]} + | {error, Reason}</name> <fsummary>Start a transport process.</fsummary> <type> <v>Type = connect | accept</v> @@ -153,13 +156,14 @@ that will not be forthcoming, which will eventually cause the RFC 3539 watchdog to take down the connection.</p> <p> -If the <c>#diameter_service{}</c> record has more than one -<c>Host-IP-Address</c> and option <c>ip</c> is unspecified then the -first of the these addresses is used as the local address.</p> - -<p> -The returned local address list has length one.</p> - +If an <c>ip</c> option is not specified then the first element of a +non-empty <c>Host-IP-Address</c> list in <c>Svc</c> provides the local +IP address. +If neither is specified then the default address selected by &gen_tcp; +is used. +In all cases, the selected address is either returned from +&start; or passed in a <c>connected</c> message over the transport +interface.</p> </desc> </func> diff --git a/lib/diameter/doc/src/diameter_transport.xml b/lib/diameter/doc/src/diameter_transport.xml index 55b531155f..8bccf6521e 100644 --- a/lib/diameter/doc/src/diameter_transport.xml +++ b/lib/diameter/doc/src/diameter_transport.xml @@ -1,6 +1,8 @@ <?xml version="1.0" encoding="latin1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd" [ <!ENTITY message '<seealso marker="#message">message()</seealso>'> + <!ENTITY MESSAGES '<seealso marker="#MESSAGES">MESSAGES</seealso>'> + <!ENTITY start '<seealso marker="#Mod:start-3">start/3</seealso>'> <!ENTITY ip_address '<seealso marker="kernel:inet#type-ip_address">inet:ip_address()</seealso>'> <!ENTITY % also SYSTEM "seealso.ent" > @@ -12,7 +14,7 @@ <erlref> <header> <copyright> -<year>2011</year><year>2012</year> +<year>2011</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -125,7 +127,7 @@ Ref is the value that was returned from the call to &mod_add_transport; that has lead to starting of a transport process.</p> <p> -<c>Svc</c> contains the capabilities passed to &mod_start_service; and +<c>Svc</c> contains capabilities passed to &mod_start_service; and &mod_add_transport;, values passed to the latter overriding those passed to the former.</p> @@ -134,13 +136,16 @@ passed to the former.</p> &mod_transport_opt; list passed to &mod_add_transport;.</p> <p> -The start function should use the <c>Host-IP-Address</c> list and/or -<c>Config</c> to select an appropriate list of local IP addresses, -and should return this list if different from the -<c>#diameter_service{}</c> addresses. -The returned list is used to populate <c>Host-IP-Address</c> AVPs in -outgoing capabilities exchange messages, the -<c>#diameter_service{}</c> addresses being used otherwise.</p> +The start function should use the <c>Host-IP-Address</c> list in +<c>Svc</c> and/or <c>Config</c> to select an appropriate list of local +IP addresses, and should return this list if different from the +<c>Svc</c> addresses. +In the connecting case, the local address list can instead be +communicated in a <c>connected</c> message (see &MESSAGES; below) +following connection establishment. +In either case, the local address list is used to populate +<c>Host-IP-Address</c> AVPs in outgoing capabilities exchange +messages.</p> <p> A transport process must implement the message interface documented below. @@ -230,13 +235,16 @@ Not sent if the transport process has <c>Type=connect</c>.</p> </item> <tag><c>{diameter, {self(), connected, Remote}}</c></tag> +<tag><c>{diameter, {self(), connected, Remote, [LocalAddr]}}</c></tag> <item> <p> Inform the parent that the transport process with <c>Type=connect</c> has established a connection with a peer. -Not sent if the transport process has <c>Type=accept</c>. +Not sent if the transport process has <c>Type=accept</c>. <c>Remote</c> is an arbitrary term that uniquely identifies the remote -endpoint to which the transport has connected.</p> +endpoint to which the transport has connected. +A <c>LocalAddr</c> list has the same semantics as one returned from +&start;.</p> </item> <tag><c>{diameter, {recv, &message;}}</c></tag> diff --git a/lib/diameter/examples/code/peer.erl b/lib/diameter/examples/code/peer.erl index 8fdeba57bf..b4ee17e4b7 100644 --- a/lib/diameter/examples/code/peer.erl +++ b/lib/diameter/examples/code/peer.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2012. All Rights Reserved. +%% Copyright Ericsson AB 2010-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -51,7 +51,6 @@ | {protocol(), ip_address(), non_neg_integer()} | {protocol(), ip_address(), ip_address(), non_neg_integer()}. --define(DEFAULT_ADDR, {127,0,0,1}). -define(DEFAULT_PORT, 3868). %% --------------------------------------------------------------------------- @@ -111,7 +110,7 @@ server({T, Addr, Port}) -> {port, Port}]}]; server(T) -> - server({T, ?DEFAULT_ADDR, ?DEFAULT_PORT}). + server({T, loopback, ?DEFAULT_PORT}). %% client/1 %% @@ -124,21 +123,28 @@ client({all, LA, RA, RP}) -> client({T, LA, RA, RP}) -> [{transport_module, tmod(T)}, - {transport_config, [{ip, addr(LA)}, - {raddr, addr(RA)}, + {transport_config, [{raddr, addr(RA)}, {rport, RP}, - {reuseaddr, true}]}]; + {reuseaddr, true} + | ip(LA)]}]; -client({T, LA, RP}) -> - client({T, LA, LA, RP}); +client({T, RA, RP}) -> + client({T, default, RA, RP}); client(T) -> - client({T, ?DEFAULT_ADDR, ?DEFAULT_ADDR, ?DEFAULT_PORT}). + client({T, loopback, loopback, ?DEFAULT_PORT}). tmod(tcp) -> diameter_tcp; tmod(sctp) -> diameter_sctp. -addr(default) -> - ?DEFAULT_ADDR; +ip(default) -> + []; +ip(loopback) -> + [{ip, {127,0,0,1}}]; +ip(Addr) -> + [{ip, Addr}]. + +addr(loopback) -> + {127,0,0,1}; addr(A) -> A. diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile index df10c33268..c0cf418f06 100644 --- a/lib/diameter/src/Makefile +++ b/lib/diameter/src/Makefile @@ -230,7 +230,7 @@ include $(ERL_TOP)/make/otp_release_targets.mk # Can't $(INSTALL_DIR) more than one directory at a time on Solaris. release_spec: opt - for d in bin ebin include src/dict; do \ + for d in bin ebin examples include src/dict; do \ $(INSTALL_DIR) "$(RELSYSDIR)/$$d"; \ done $(INSTALL_SCRIPT) $(BINS:%=../bin/%) "$(RELSYSDIR)/bin" diff --git a/lib/diameter/src/base/diameter_peer.erl b/lib/diameter/src/base/diameter_peer.erl index dfc76eb76e..0d2efd4d1f 100644 --- a/lib/diameter/src/base/diameter_peer.erl +++ b/lib/diameter/src/base/diameter_peer.erl @@ -24,7 +24,8 @@ %% Interface towards transport modules ... -export([recv/2, up/1, - up/2]). + up/2, + up/3]). %% ... and the stack. -export([start/1, @@ -180,7 +181,7 @@ start(Mod, Args) -> apply(Mod, start, Args). %%% --------------------------------------------------------------------------- -%%% # up/[12] +%%% # up/1-3 %%% --------------------------------------------------------------------------- up(Pid) -> %% accepting transport @@ -189,6 +190,9 @@ up(Pid) -> %% accepting transport up(Pid, Remote) -> %% connecting transport ifc_send(Pid, {self(), connected, Remote}). +up(Pid, Remote, LAddrs) -> %% connecting transport + ifc_send(Pid, {self(), connected, Remote, LAddrs}). + %%% --------------------------------------------------------------------------- %%% # recv/2 %%% --------------------------------------------------------------------------- diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl index bee3e507fd..6be4259510 100644 --- a/lib/diameter/src/base/diameter_peer_fsm.erl +++ b/lib/diameter/src/base/diameter_peer_fsm.erl @@ -351,10 +351,17 @@ transition({diameter, {TPid, connected, Remote}}, mode = M} = S) -> {'Wait-Conn-Ack', _} = PS, %% assert - connect = M, %% + connect = M, %% keep_transport(TPid), send_CER(S#state{mode = {M, Remote}}); +transition({diameter, {TPid, connected, Remote, LAddrs}}, + #state{transport = TPid, + service = Svc} + = S) -> + transition({diameter, {TPid, connected, Remote}}, + S#state{service = readdr(Svc, LAddrs)}); + %% Connection from peer. transition({diameter, {TPid, connected}}, #state{transport = TPid, @@ -363,7 +370,7 @@ transition({diameter, {TPid, connected}}, parent = Pid} = S) -> {'Wait-Conn-Ack', Tmo} = PS, %% assert - accept = M, %% + accept = M, %% keep_transport(TPid), Pid ! {accepted, self()}, start_timer(Tmo, S#state{state = recv_CER}); @@ -376,6 +383,8 @@ transition({diameter, {_, connected}}, _) -> {stop, connection_timeout}; transition({diameter, {_, connected, _}}, _) -> {stop, connection_timeout}; +transition({diameter, {_, connected, _, _}}, _) -> + {stop, connection_timeout}; %% Connection has timed out: start an alternate. transition({connection_timeout = T, TPid}, diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index e4d1c60727..112e83476d 100644 --- a/lib/diameter/src/base/diameter_service.erl +++ b/lib/diameter/src/base/diameter_service.erl @@ -861,17 +861,21 @@ watchdog(TPid, [], ?WD_SUSPECT, ?WD_OKAY, Wd, State) -> %% Watchdog has an unresponsive connection. watchdog(TPid, [], ?WD_OKAY, ?WD_SUSPECT = To, Wd, State) -> #watchdog{peer = TPid} = Wd, %% assert - connection_down(Wd, To, State); + watchdog_down(Wd, To, State); %% Watchdog has lost its connection. watchdog(TPid, [], _, ?WD_DOWN = To, Wd, #state{peerT = PeerT} = S) -> close(Wd, S), - connection_down(Wd, To, S), + watchdog_down(Wd, To, S), ets:delete(PeerT, TPid); watchdog(_, [], _, _, _, _) -> ok. +watchdog_down(Wd, To, #state{watchdogT = WatchdogT} = S) -> + insert(WatchdogT, Wd#watchdog{state = To}), + connection_down(Wd, To, S). + %% --------------------------------------------------------------------------- %% # connection_up/3 %% --------------------------------------------------------------------------- @@ -1029,21 +1033,17 @@ connection_down(#watchdog{state = ?WD_OKAY, remove_local_peer(SApps, {{TPid, Caps}, {SvcName, Apps}}, LDict), diameter_traffic:peer_down(TPid); -connection_down(#watchdog{}, #peer{}, _) -> - ok; - -connection_down(#watchdog{state = WS, +connection_down(#watchdog{state = ?WD_OKAY, peer = TPid} = Wd, To, - #state{watchdogT = WatchdogT, - peerT = PeerT} + #state{peerT = PeerT} = S) when is_atom(To) -> - insert(WatchdogT, Wd#watchdog{state = To}), - ?WD_OKAY == WS - andalso - connection_down(Wd, fetch(PeerT, TPid), S). + connection_down(Wd, #peer{} = fetch(PeerT, TPid), S); + +connection_down(#watchdog{}, _, _) -> + ok. remove_local_peer(SApps, T, LDict) -> lists:foldl(fun(A,D) -> rlp(A, T, D) end, LDict, SApps). diff --git a/lib/diameter/src/transport/diameter_tcp.erl b/lib/diameter/src/transport/diameter_tcp.erl index 132088b514..cbbba714ac 100644 --- a/lib/diameter/src/transport/diameter_tcp.erl +++ b/lib/diameter/src/transport/diameter_tcp.erl @@ -100,6 +100,18 @@ %% # start/3 %% --------------------------------------------------------------------------- +-spec start({accept, Ref}, Svc, [Opt]) + -> {ok, pid(), [inet:ip_address()]} + when Ref :: diameter:transport_ref(), + Svc :: #diameter_service{}, + Opt :: diameter:transport_opt(); + ({connect, Ref}, Svc, [Opt]) + -> {ok, pid(), [inet:ip_address()]} + | {ok, pid()} + when Ref :: diameter:transport_ref(), + Svc :: #diameter_service{}, + Opt :: diameter:transport_opt(). + start({T, Ref}, #diameter_service{capabilities = Caps}, Opts) -> diameter_tcp_sup:start(), %% start tcp supervisors on demand {Mod, Rest} = split(Opts), @@ -172,7 +184,7 @@ i({T, Ref, Mod, Pid, Opts, Addrs}) OwnOpts, ?DEFAULT_FRAGMENT_TIMEOUT), ?IS_TIMEOUT(Tmo) orelse ?ERROR({fragment_timer, Tmo}), - Sock = i(T, Ref, Mod, Pid, SslOpts, Rest, Addrs), + Sock = init(T, Ref, Mod, Pid, SslOpts, Rest, Addrs), MPid ! {stop, self()}, %% tell the monitor to die M = if SslOpts -> ssl; true -> Mod end, setopts(M, Sock), @@ -199,14 +211,21 @@ i(#monitor{parent = Pid, transport = TPid} = S) -> i({listen, LRef, APid, {Mod, Opts, Addrs}}) -> {[LA, LP], Rest} = proplists:split(Opts, [ip, port]), - LAddr = get_addr(LA, Addrs), + LAddrOpt = get_addr(LA, Addrs), LPort = get_port(LP), - {ok, LSock} = Mod:listen(LPort, gen_opts(LAddr, Rest)), + {ok, LSock} = Mod:listen(LPort, gen_opts(LAddrOpt, Rest)), + LAddr = laddr(LAddrOpt, Mod, LSock), true = diameter_reg:add_new({?MODULE, listener, {LRef, {LAddr, LSock}}}), proc_lib:init_ack({ok, self(), {LAddr, LSock}}), erlang:monitor(process, APid), start_timer(#listener{socket = LSock}). +laddr([], Mod, Sock) -> + {ok, {Addr, _Port}} = sockname(Mod, Sock), + Addr; +laddr([{ip, Addr}], _, _) -> + Addr. + own(Opts) -> {Own, Rest} = proplists:split(Opts, [fragment_timer]), {lists:append(Own), Rest}. @@ -225,17 +244,19 @@ ssl_opts([{ssl_options, Opts}]) ssl_opts(L) -> ?ERROR({ssl_options, L}). -%% i/7 +%% init/7 %% Establish a TLS connection before capabilities exchange ... -i(Type, Ref, Mod, Pid, true, Opts, Addrs) -> - i(Type, Ref, ssl, Pid, [{cb_info, ?TCP_CB(Mod)} | Opts], Addrs); +init(Type, Ref, Mod, Pid, true, Opts, Addrs) -> + init(Type, Ref, ssl, Pid, [{cb_info, ?TCP_CB(Mod)} | Opts], Addrs); %% ... or not. -i(Type, Ref, Mod, Pid, _, Opts, Addrs) -> - i(Type, Ref, Mod, Pid, Opts, Addrs). +init(Type, Ref, Mod, Pid, _, Opts, Addrs) -> + init(Type, Ref, Mod, Pid, Opts, Addrs). + +%% init/6 -i(accept = T, Ref, Mod, Pid, Opts, Addrs) -> +init(accept = T, Ref, Mod, Pid, Opts, Addrs) -> {LAddr, LSock} = listener(Ref, {Mod, Opts, Addrs}), proc_lib:init_ack({ok, self(), [LAddr]}), Sock = ok(accept(Mod, LSock)), @@ -243,17 +264,28 @@ i(accept = T, Ref, Mod, Pid, Opts, Addrs) -> diameter_peer:up(Pid), Sock; -i(connect = T, Ref, Mod, Pid, Opts, Addrs) -> +init(connect = T, Ref, Mod, Pid, Opts, Addrs) -> {[LA, RA, RP], Rest} = proplists:split(Opts, [ip, raddr, rport]), - LAddr = get_addr(LA, Addrs), - RAddr = get_addr(RA, []), + LAddrOpt = get_addr(LA, Addrs), + RAddr = get_addr(RA), RPort = get_port(RP), - proc_lib:init_ack({ok, self(), [LAddr]}), - Sock = ok(connect(Mod, RAddr, RPort, gen_opts(LAddr, Rest))), + proc_lib:init_ack(init_rc(LAddrOpt)), + Sock = ok(connect(Mod, RAddr, RPort, gen_opts(LAddrOpt, Rest))), publish(Mod, T, Ref, Sock), - diameter_peer:up(Pid, {RAddr, RPort}), + up(Pid, {RAddr, RPort}, LAddrOpt, Mod, Sock), Sock. +init_rc([{ip, Addr}]) -> + {ok, self(), [Addr]}; +init_rc([]) -> + {ok, self()}. + +up(Pid, Remote, [{ip, _Addr}], _, _) -> + diameter_peer:up(Pid, Remote); +up(Pid, Remote, [], Mod, Sock) -> + {Addr, _Port} = ok(sockname(Mod, Sock)), + diameter_peer:up(Pid, Remote, [Addr]). + publish(Mod, T, Ref, Sock) -> true = diameter_reg:add_new({?MODULE, T, {Ref, Sock}}), putr(?INFO_KEY, {Mod, Sock}). %% for info/1 @@ -281,10 +313,17 @@ l([], LRef, T) -> {ok, _, AS} = diameter_tcp_sup:start_child({listen, LRef, self(), T}), AS. +%% get_addr/1 + +get_addr(As) -> + diameter_lib:ipaddr(addr(As, [])). + %% get_addr/2 +get_addr([], []) -> + []; get_addr(As, Def) -> - diameter_lib:ipaddr(addr(As, Def)). + [{ip, diameter_lib:ipaddr(addr(As, Def))}]. %% Take the first address from the service if several are unspecified. addr([], [Addr | _]) -> @@ -305,14 +344,10 @@ get_port(Ps) -> %% gen_opts/2 -gen_opts(LAddr, Opts) -> +gen_opts(LAddrOpt, Opts) -> {L,_} = proplists:split(Opts, [binary, packet, active]), [[],[],[]] == L orelse ?ERROR({reserved_options, Opts}), - [binary, - {packet, 0}, - {active, once}, - {ip, LAddr} - | Opts]. + [binary, {packet, 0}, {active, once}] ++ LAddrOpt ++ Opts. %% --------------------------------------------------------------------------- %% # ports/1 diff --git a/lib/diameter/test/diameter_capx_SUITE.erl b/lib/diameter/test/diameter_capx_SUITE.erl index 9e6619ecdd..8c9bb67e61 100644 --- a/lib/diameter/test/diameter_capx_SUITE.erl +++ b/lib/diameter/test/diameter_capx_SUITE.erl @@ -444,7 +444,7 @@ connect(Config, T, Opts) -> {CRef, LRef}. connect(LRef, Opts) -> - [PortNr] = ?util:lport(tcp, LRef, 20), + [PortNr] = ?util:lport(tcp, LRef), {ok, CRef} = diameter:add_transport(?CLIENT, {connect, opts(PortNr, Opts)}), CRef. diff --git a/lib/diameter/test/diameter_event_SUITE.erl b/lib/diameter/test/diameter_event_SUITE.erl index 18bdcb1f54..94b4967921 100644 --- a/lib/diameter/test/diameter_event_SUITE.erl +++ b/lib/diameter/test/diameter_event_SUITE.erl @@ -100,7 +100,7 @@ start_server(Config) -> ok = diameter:start_service(?SERVER, ?SERVICE(?SERVER, [?DICT_COMMON])), LRef = ?util:listen(?SERVER, tcp, [{capabilities_cb, fun capx_cb/2}, {capx_timeout, ?SERVER_CAPX_TMO}]), - [PortNr] = ?util:lport(tcp, LRef, 20), + [PortNr] = ?util:lport(tcp, LRef), ?util:write_priv(Config, portnr, PortNr), start = event(?SERVER). diff --git a/lib/diameter/test/diameter_examples_SUITE.erl b/lib/diameter/test/diameter_examples_SUITE.erl index 6d797f6911..585fc9d3b8 100644 --- a/lib/diameter/test/diameter_examples_SUITE.erl +++ b/lib/diameter/test/diameter_examples_SUITE.erl @@ -283,7 +283,7 @@ start(server) -> ok = diameter:start(), ok = server:start(), {ok, Ref} = server:listen(tcp), - [_] = ?util:lport(tcp, Ref, 20), + [_] = ?util:lport(tcp, Ref), ok; start(client) -> diff --git a/lib/diameter/test/diameter_tls_SUITE.erl b/lib/diameter/test/diameter_tls_SUITE.erl index 77194a0f56..5a79c63d36 100644 --- a/lib/diameter/test/diameter_tls_SUITE.erl +++ b/lib/diameter/test/diameter_tls_SUITE.erl @@ -343,7 +343,7 @@ join(Strs) -> server(Host, {Caps, Opts}) -> ok = diameter:start_service(Host, ?SERVICE(Host, ?DICT_COMMON)), {ok, LRef} = diameter:add_transport(Host, ?LISTEN(Caps, Opts)), - {LRef, hd([_] = ?util:lport(tcp, LRef, 20))}. + {LRef, hd([_] = ?util:lport(tcp, LRef))}. sopts(?SERVER1, Dir) -> {inband_security([?TLS]), diff --git a/lib/diameter/test/diameter_transport_SUITE.erl b/lib/diameter/test/diameter_transport_SUITE.erl index 893b7ba2f9..97f4cec11f 100644 --- a/lib/diameter/test/diameter_transport_SUITE.erl +++ b/lib/diameter/test/diameter_transport_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -36,6 +36,7 @@ tcp_connect/1, sctp_accept/1, sctp_connect/1, + reconnect/1, reconnect/0, stop/1]). -export([accept/1, @@ -54,9 +55,6 @@ -define(RECV(Pat, Ret), receive Pat -> Ret end). -define(RECV(Pat), ?RECV(Pat, now())). -%% Or not. --define(WAIT(Ms), receive after Ms -> now() end). - %% Sockets are opened on the loopback address. -define(ADDR, {127,0,0,1}). @@ -102,7 +100,8 @@ tc() -> [tcp_accept, tcp_connect, sctp_accept, - sctp_connect]. + sctp_connect, + reconnect]. init_per_suite(Config) -> [{sctp, have_sctp()} | Config]. @@ -165,6 +164,90 @@ connect(Prot) -> [] = ?util:run([{?MODULE, [init, X, T]} || X <- [gen_accept, connect]]). %% =========================================================================== +%% reconnect/1 +%% +%% Exercise reconnection behaviour: that a connecting transport +%% doesn't try to establish a new connection until the old one is +%% broken. + +reconnect() -> + [{timetrap, {minutes, 4}}]. + +reconnect({listen, Ref}) -> + SvcName = make_ref(), + ok = start_service(SvcName), + LRef = ?util:listen(SvcName, tcp, [{watchdog_timer, 6000}]), + [_] = diameter_reg:wait({diameter_tcp, listener, {LRef, '_'}}), + true = diameter_reg:add_new({?MODULE, Ref, LRef}), + + %% Wait for partner to request transport death: kill to force the + %% peer to reconnect. + TPid = abort(SvcName, LRef, Ref), + + exit(TPid, kill), + + abort(SvcName, LRef, Ref); + +reconnect({connect, Ref}) -> + SvcName = make_ref(), + true = diameter:subscribe(SvcName), + ok = start_service(SvcName), + [{{_, _, LRef}, Pid}] = diameter_reg:wait({?MODULE, Ref, '_'}), + CRef = ?util:connect(SvcName, tcp, LRef, [{reconnect_timer, 2000}, + {watchdog_timer, 6000}]), + + %% Tell partner to kill transport after seeing that there are no + %% reconnection attempts. + abort(SvcName, Pid, Ref), + + %% Transport does down and is reestablished. + ?RECV(#diameter_event{service = SvcName, info = {down, CRef, _, _}}), + ?RECV(#diameter_event{service = SvcName, info = {reconnect, CRef, _}}), + ?RECV(#diameter_event{service = SvcName, info = {up, CRef, _, _, _}}), + + %% Kill again. + abort(SvcName, Pid, Ref), + + %% Wait for partner to die. + MRef = erlang:monitor(process, Pid), + ?RECV({'DOWN', MRef, process, _, _}); + +reconnect(_) -> + Ref = make_ref(), + [] = ?util:run([{?MODULE, [reconnect, {T, Ref}]} + || T <- [listen, connect]]). + +start_service(SvcName) -> + OH = io_lib:format("~p-~p-~p", tuple_to_list(now())), + Opts = [{application, [{dictionary, diameter_gen_base_rfc6733}, + {module, diameter_callback}]}, + {'Origin-Host', OH}, + {'Origin-Realm', OH ++ ".org"}, + {'Vendor-Id', 0}, + {'Product-Name', "x"}, + {'Auth-Application-Id', [0]}], + diameter:start_service(SvcName, Opts). + +abort(SvcName, Pid, Ref) + when is_pid(Pid) -> + receive + #diameter_event{service = SvcName, info = {reconnect, _, _}} = E -> + erlang:error(E) + after 45000 -> + ok + end, + Pid ! {abort, Ref}; + +abort(SvcName, LRef, Ref) + when is_reference(LRef) -> + ?RECV({abort, Ref}), + [[{ref, LRef}, {type, listen}, {options, _}, {accept, [_,_] = Ts} | _]] + %% assert on two accepting + = diameter:service_info(SvcName, transport), + [TPid] = [P || [{watchdog, {_,_,okay}}, {peer, {P,_}} | _] <- Ts], + TPid. + +%% =========================================================================== %% =========================================================================== %% have_sctp/0 @@ -209,7 +292,7 @@ init(accept, {Prot, Ref}) -> init(gen_connect, {Prot, Ref}) -> %% Lookup the peer's listening socket. - [PortNr] = ?util:lport(Prot, Ref, 20), + [PortNr] = ?util:lport(Prot, Ref), %% Connect, send a message and receive it back. {ok, Sock} = gen_connect(Prot, PortNr), @@ -230,7 +313,8 @@ init(gen_accept, {Prot, Ref}) -> init(connect, {Prot, Ref}) -> %% Lookup the peer's listening socket. - [{?TEST_LISTENER(_, PortNr), _}] = match(?TEST_LISTENER(Ref, '_')), + [{?TEST_LISTENER(_, PortNr), _}] + = diameter_reg:wait(?TEST_LISTENER(Ref, '_')), %% Start a connecting transport and receive notification of %% the connection. @@ -246,18 +330,6 @@ init(connect, {Prot, Ref}) -> MRef = erlang:monitor(process, TPid), ?RECV({'DOWN', MRef, process, _, _}). -match(Pat) -> - match(Pat, 20). - -match(Pat, T) -> - L = diameter_reg:match(Pat), - if [] /= L orelse 1 == T -> - L; - true -> - ?WAIT(50), - match(Pat, T-1) - end. - bin(sctp, #diameter_packet{bin = Bin}) -> Bin; bin(tcp, Bin) -> @@ -310,22 +382,18 @@ start_connect(tcp, T, Svc, Opts) -> %% start_accept/2 %% %% Start transports sequentially by having each wait for a message -%% from a job in a queue before commencing. Only one transport with -%% a pending accept is started at a time since diameter_sctp currently -%% assumes (and diameter currently implements) this. +%% from a job in a queue before commencing. Only one transport with a +%% pending accept is started at a time since diameter_{tcp,sctp} +%% currently assume (and diameter currently implements) this. start_accept(Prot, Ref) -> Pid = sync(accept, Ref), - - %% Configure the same port number for transports on the same - %% reference. - [PortNr | _] = ?util:lport(Prot, Ref) ++ [0], {Mod, Opts} = tmod(Prot), try {ok, TPid, [?ADDR]} = Mod:start({accept, Ref}, ?SVC([?ADDR]), - [{port, PortNr} | Opts]), + [{port, 0} | Opts]), ?RECV(?TMSG({TPid, connected})), TPid after diff --git a/lib/diameter/test/diameter_util.erl b/lib/diameter/test/diameter_util.erl index a9872f32e1..aa489fef5f 100644 --- a/lib/diameter/test/diameter_util.erl +++ b/lib/diameter/test/diameter_util.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2012. All Rights Reserved. +%% Copyright Ericsson AB 2010-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -33,7 +33,6 @@ %% diameter-specific -export([lport/2, - lport/3, listen/2, listen/3, connect/3, connect/4, disconnect/4, @@ -251,27 +250,17 @@ path(Config, Name) -> filename:join([Dir, Name]). %% --------------------------------------------------------------------------- -%% lport/2-3 +%% lport/2 %% %% Lookup the port number of a tcp/sctp listening transport. -lport(M, Ref) -> - lport(M, Ref, 1). +lport(M, {Node, Ref}) -> + rpc:call(Node, ?MODULE, lport, [M, Ref]); -lport(M, {Node, Ref}, Tries) -> - rpc:call(Node, ?MODULE, lport, [M, Ref, Tries]); - -lport(M, Ref, Tries) -> - lp(tmod(M), Ref, Tries). - -lp(M, Ref, T) -> - L = [N || {listen, N, _} <- M:ports(Ref)], - if [] /= L orelse T =< 1 -> - L; - true -> - receive after 50 -> ok end, - lp(M, Ref, T-1) - end. +lport(Prot, Ref) -> + Mod = tmod(Prot), + [_] = diameter_reg:wait({'_', listener, {Ref, '_'}}), + [N || {listen, N, _} <- Mod:ports(Ref)]. %% --------------------------------------------------------------------------- %% listen/2-3 @@ -297,7 +286,7 @@ connect(Client, Prot, LRef) -> connect(Client, Prot, LRef, []). connect(Client, Prot, LRef, Opts) -> - [PortNr] = lport(Prot, LRef, 20), + [PortNr] = lport(Prot, LRef), Client = diameter:service_info(Client, name), %% assert true = diameter:subscribe(Client), Ref = add_transport(Client, {connect, opts(Prot, PortNr) ++ Opts}), diff --git a/lib/diameter/test/diameter_watchdog_SUITE.erl b/lib/diameter/test/diameter_watchdog_SUITE.erl index 704bf110c7..b6e8730ec2 100644 --- a/lib/diameter/test/diameter_watchdog_SUITE.erl +++ b/lib/diameter/test/diameter_watchdog_SUITE.erl @@ -524,7 +524,7 @@ cfg(listen, _) -> cfg(connect, Ref) -> [{{_, _, SvcName}, _Pid}] = diameter_reg:wait({listen, Ref, '_'}), [[{ref, LRef} | _]] = diameter:service_info(SvcName, transport), - [LP] = ?util:lport(tcp, LRef, 20), + [LP] = ?util:lport(tcp, LRef), [{raddr, ?ADDR}, {rport, LP}]. %% =========================================================================== diff --git a/lib/erl_interface/src/connect/ei_connect.c b/lib/erl_interface/src/connect/ei_connect.c index 3ab86bb340..8f1f231b82 100644 --- a/lib/erl_interface/src/connect/ei_connect.c +++ b/lib/erl_interface/src/connect/ei_connect.c @@ -830,7 +830,8 @@ int ei_accept_tmo(ei_cnode* ec, int lfd, ErlConnect *conp, unsigned ms) error: EI_TRACE_ERR0("ei_accept","<- ACCEPT failed"); - closesocket(fd); + if (fd>=0) + closesocket(fd); return ERL_ERROR; } /* ei_accept */ diff --git a/lib/et/doc/src/et_desc.xmlsrc b/lib/et/doc/src/et_desc.xmlsrc index c02517ae01..36274f6316 100644 --- a/lib/et/doc/src/et_desc.xmlsrc +++ b/lib/et/doc/src/et_desc.xmlsrc @@ -150,7 +150,7 @@ NewEvent = #event{}]]></code> <p>The interface of the filter function is the same as the the - filter functions for the good old <c>lists:zf/2</c>. If the filter + filter functions for the good old <c>lists:filtermap/2</c>. If the filter returns <c>false</c> it means that the trace data should silently be dropped. <c>true</c> means that the trace data data already is an <c>Event Record</c> and that it should be kept as it is. diff --git a/lib/et/src/et_collector.erl b/lib/et/src/et_collector.erl index a63d15fb4c..ce8cf6d4e0 100644 --- a/lib/et/src/et_collector.erl +++ b/lib/et/src/et_collector.erl @@ -654,13 +654,8 @@ start_trace_client(CollectorPid, Type, FileName) when Type =:= file -> Ref = erlang:monitor(process, Pid), receive WaitFor -> - erlang:demonitor(Ref), - receive - {'DOWN', Ref, _, _, _} -> - file_loaded - after 0 -> - file_loaded - end; + erlang:demonitor(Ref, [flush]), + file_loaded; {'DOWN', Ref, _, _, Reason} -> exit(Reason) end; diff --git a/lib/ic/java_src/com/ericsson/otp/ic/Environment.java b/lib/ic/java_src/com/ericsson/otp/ic/Environment.java index fff854a5f8..4b321fbce7 100644 --- a/lib/ic/java_src/com/ericsson/otp/ic/Environment.java +++ b/lib/ic/java_src/com/ericsson/otp/ic/Environment.java @@ -135,8 +135,8 @@ public class Environment { if (connection == null) connection = self.connect(peer); - clientP = new com.ericsson.otp.erlang.OtpErlangPid(self); /* This is not perfect */ - send_ref = new com.ericsson.otp.erlang.OtpErlangRef(self); + clientP = self.createPid(); /* This is not perfect */ + send_ref = self.createRef(); } diff --git a/lib/ic/src/icparse.yrl b/lib/ic/src/icparse.yrl index d0dd6cde4c..b0286d57f4 100644 --- a/lib/ic/src/icparse.yrl +++ b/lib/ic/src/icparse.yrl @@ -230,6 +230,9 @@ Terminals Rootsymbol '<specification>'. +Expect 9. + + %%------------------------------------------------------------ %% Clauses %% diff --git a/lib/inets/src/http_server/httpd_manager.erl b/lib/inets/src/http_server/httpd_manager.erl index 672a70a394..c83d06a158 100644 --- a/lib/inets/src/http_server/httpd_manager.erl +++ b/lib/inets/src/http_server/httpd_manager.erl @@ -691,11 +691,11 @@ handle_unblock(S, FromA) -> handle_unblock(S, _FromA, unblocked) -> {ok,S}; handle_unblock(S, FromA, _AdminState) -> - stop_block_tmr(S#state.blocking_tmr), case S#state.blocking_tmr of - {_Tmr,FromB,Ref} -> + {Tmr,FromB,Ref} -> %% Another process is trying to unblock %% Inform the blocker + stop_block_tmr(Tmr), FromB ! {block_reply, {error,{unblocked,FromA}},Ref}; _ -> ok diff --git a/lib/jinterface/test/jinterface_SUITE.erl b/lib/jinterface/test/jinterface_SUITE.erl index b438da12d0..de8d611efc 100644 --- a/lib/jinterface/test/jinterface_SUITE.erl +++ b/lib/jinterface/test/jinterface_SUITE.erl @@ -180,7 +180,7 @@ init_per_testcase(Case, _Config) Case =:= kill_mbox_from_erlang -> {skip, "Not yet implemented"}; init_per_testcase(_Case,Config) -> - Dog = ?t:timetrap({seconds,10}), + Dog = ?t:timetrap({seconds,30}), [{watch_dog,Dog}|Config]. end_per_testcase(_Case,Config) -> @@ -188,6 +188,7 @@ end_per_testcase(_Case,Config) -> undefined -> ok; Pid -> exit(Pid,kill) end, + jitu:kill_all_jnodes(), ?t:timetrap_cancel(?config(watch_dog,Config)), ok. diff --git a/lib/jinterface/test/jitu.erl b/lib/jinterface/test/jitu.erl index 0e1af0ff22..a029c063bc 100644 --- a/lib/jinterface/test/jitu.erl +++ b/lib/jinterface/test/jitu.erl @@ -27,7 +27,10 @@ java/4, java/5, init_all/1, - finish_all/1]). + finish_all/1, + kill_all_jnodes/0]). + +-include("ct.hrl"). %% %% Lots of stuff here are originating from java_client_erl_server_SUITE.erl @@ -51,10 +54,33 @@ java(Java, Dir, Class, Args, Props) -> init_all(Config) when is_list(Config) -> case find_executable(["java"]) of false -> {skip,"Found no Java VM"}; - Path -> [{java,Path}|Config] + Path -> + Pid = spawn(fun() -> + ets:new(jitu_tab,[set,named_table,public]), + receive stop -> ets:delete(jitu_tab) end + end), + [{java,Path},{tab_proc,Pid}|Config] end. -finish_all(Config) -> Config. +finish_all(Config) -> + kill_all_jnodes(), + ?config(tab_proc,Config) ! stop, + Config. + +kill_all_jnodes() -> + Jnodes = ets:tab2list(jitu_tab), + [begin +% ct:pal("Killing OsPid=~w started with ~p",[OsPid,_Cmd]), + kill_os_process(os:type(),integer_to_list(OsPid)) + end || {OsPid,_Cmd} <- Jnodes], + ets:delete_all_objects(jitu_tab), + ok. + +kill_os_process({win32,_},OsPid) -> + os:cmd("taskkill /PID " ++ OsPid); +kill_os_process(_,OsPid) -> + os:cmd("kill " ++ OsPid). + %% %% Internal stuff... @@ -110,6 +136,12 @@ cmd(Cmd) -> io:format("cmd: ~s~n", [Cmd]), case catch open_port({spawn,Cmd}, PortOpts) of Port when is_port(Port) -> + case erlang:port_info(Port,os_pid) of + {os_pid,OsPid} -> + ets:insert(jitu_tab,{OsPid,Cmd}); + _ -> + ok + end, Result = cmd_loop(Port, []), io:format("cmd res: ~w~n", [Result]), case Result of diff --git a/lib/jinterface/test/nc_SUITE.erl b/lib/jinterface/test/nc_SUITE.erl index 63c78ebdaa..f1493a3cc9 100644 --- a/lib/jinterface/test/nc_SUITE.erl +++ b/lib/jinterface/test/nc_SUITE.erl @@ -105,12 +105,13 @@ end_per_suite(Config) -> init_per_testcase(Case, Config) -> T = case atom_to_list(Case) of "unicode"++_ -> 240; - _ -> 30 + _ -> 120 end, WatchDog = test_server:timetrap(test_server:seconds(T)), [{watchdog, WatchDog}| Config]. end_per_testcase(_Case, Config) -> + jitu:kill_all_jnodes(), WatchDog = ?config(watchdog, Config), test_server:timetrap_cancel(WatchDog). @@ -695,15 +696,18 @@ run_server(Server, Config, Action, ExtraArgs) -> true = register(Name, self()), JName = make_name(), spawn_link(fun () -> + %% Setting max memory to 256. This is due to + %% echo_server sometimes failing with + %% OutOfMemoryException one some test machines. ok = jitu:java(?config(java, Config), ?config(data_dir, Config), atom_to_list(Server), [JName, erlang:get_cookie(), node(), - Name]++ExtraArgs - ), - %,"-DOtpConnection.trace=3"), + Name]++ExtraArgs, + " -Xmx256m"), + %% " -Xmx256m -DOtpConnection.trace=3"), Name ! {done, JName} end), receive diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index 541500a300..7cd98914d1 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -559,7 +559,7 @@ fe80::204:acff:fe17:bf38 <c>[Byte1,Byte2|Binary]</c>.</p> </item> - <tag><c>{high_msgq_watermark, Size}</c> (TCP/IP sockets)</tag> + <tag><c>{high_msgq_watermark, Size}</c></tag> <item> <p>The socket message queue will be set into a busy state when the amount of data queued on the message @@ -674,7 +674,7 @@ fe80::204:acff:fe17:bf38 the flushing time-out in seconds.</p> </item> - <tag><c>{low_msgq_watermark, Size}</c> (TCP/IP sockets)</tag> + <tag><c>{low_msgq_watermark, Size}</c></tag> <item> <p>If the socket message queue is in a busy state, the socket message queue will be set in a not busy state when diff --git a/lib/kernel/src/application_master.erl b/lib/kernel/src/application_master.erl index 679fefaed9..34a3efcaf2 100644 --- a/lib/kernel/src/application_master.erl +++ b/lib/kernel/src/application_master.erl @@ -76,13 +76,8 @@ call(AppMaster, Req) -> {'DOWN', Ref, process, _, _Info} -> ok; {Tag, Res} -> - erlang:demonitor(Ref), - receive - {'DOWN', Ref, process, _, _Info} -> - Res - after 0 -> - Res - end + erlang:demonitor(Ref, [flush]), + Res end. %%%----------------------------------------------------------------- diff --git a/lib/kernel/src/disk_log.erl b/lib/kernel/src/disk_log.erl index 0c5af2857e..c238eff12f 100644 --- a/lib/kernel/src/disk_log.erl +++ b/lib/kernel/src/disk_log.erl @@ -1914,13 +1914,8 @@ multi_req(Msg, Pids) -> {'DOWN', Ref, process, Pid, _Info} -> Reply; {disk_log, Pid, _Reply} -> - erlang:demonitor(Ref), - receive - {'DOWN', Ref, process, Pid, _Reason} -> - ok - after 0 -> - ok - end + erlang:demonitor(Ref, [flush]), + ok end end, {error, nonode}, Refs). @@ -1965,13 +1960,8 @@ monitor_request(Pid, Req) -> {error, no_such_log}; {disk_log, Pid, Reply} when not is_tuple(Reply) orelse element(2, Reply) =/= disk_log_stopped -> - erlang:demonitor(Ref), - receive - {'DOWN', Ref, process, Pid, _Reason} -> - Reply - after 0 -> - Reply - end + erlang:demonitor(Ref, [flush]), + Reply end. req2(Pid, R) -> diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl index a4c56b346f..36289053eb 100644 --- a/lib/kernel/src/file.erl +++ b/lib/kernel/src/file.erl @@ -480,8 +480,7 @@ open(Item, Mode) -> Reason :: posix() | badarg | terminated. close(File) when is_pid(File) -> - R = file_request(File, close), - case wait_file_reply(File, R) of + case file_request(File, close) of {error, terminated} -> ok; Other -> @@ -503,8 +502,7 @@ close(_) -> Reason :: posix() | badarg. advise(File, Offset, Length, Advise) when is_pid(File) -> - R = file_request(File, {advise, Offset, Length, Advise}), - wait_file_reply(File, R); + file_request(File, {advise, Offset, Length, Advise}); advise(#file_descriptor{module = Module} = Handle, Offset, Length, Advise) -> Module:advise(Handle, Offset, Length, Advise); advise(_, _, _, _) -> @@ -517,8 +515,7 @@ advise(_, _, _, _) -> Length :: non_neg_integer(). allocate(File, Offset, Length) when is_pid(File) -> - R = file_request(File, {allocate, Offset, Length}), - wait_file_reply(File, R); + file_request(File, {allocate, Offset, Length}); allocate(#file_descriptor{module = Module} = Handle, Offset, Length) -> Module:allocate(Handle, Offset, Length). @@ -601,8 +598,7 @@ pread_int(_, _, _) -> Reason :: posix() | badarg | terminated. pread(File, At, Sz) when is_pid(File), is_integer(Sz), Sz >= 0 -> - R = file_request(File, {pread, At, Sz}), - wait_file_reply(File, R); + file_request(File, {pread, At, Sz}); pread(#file_descriptor{module = Module} = Handle, Offs, Sz) when is_integer(Sz), Sz >= 0 -> Module:pread(Handle, Offs, Sz); @@ -658,8 +654,7 @@ pwrite_int(_, _, _) -> Reason :: posix() | badarg | terminated. pwrite(File, At, Bytes) when is_pid(File) -> - R = file_request(File, {pwrite, At, Bytes}), - wait_file_reply(File, R); + file_request(File, {pwrite, At, Bytes}); pwrite(#file_descriptor{module = Module} = Handle, Offs, Bytes) -> Module:pwrite(Handle, Offs, Bytes); pwrite(_, _, _) -> @@ -670,8 +665,7 @@ pwrite(_, _, _) -> Reason :: posix() | badarg | terminated. datasync(File) when is_pid(File) -> - R = file_request(File, datasync), - wait_file_reply(File, R); + file_request(File, datasync); datasync(#file_descriptor{module = Module} = Handle) -> Module:datasync(Handle); datasync(_) -> @@ -682,8 +676,7 @@ datasync(_) -> Reason :: posix() | badarg | terminated. sync(File) when is_pid(File) -> - R = file_request(File, sync), - wait_file_reply(File, R); + file_request(File, sync); sync(#file_descriptor{module = Module} = Handle) -> Module:sync(Handle); sync(_) -> @@ -696,8 +689,7 @@ sync(_) -> Reason :: posix() | badarg | terminated. position(File, At) when is_pid(File) -> - R = file_request(File, {position,At}), - wait_file_reply(File, R); + file_request(File, {position,At}); position(#file_descriptor{module = Module} = Handle, At) -> Module:position(Handle, At); position(_, _) -> @@ -708,8 +700,7 @@ position(_, _) -> Reason :: posix() | badarg | terminated. truncate(File) when is_pid(File) -> - R = file_request(File, truncate), - wait_file_reply(File, R); + file_request(File, truncate); truncate(#file_descriptor{module = Module} = Handle) -> Module:truncate(Handle); truncate(_) -> @@ -1497,25 +1488,19 @@ check_args([]) -> ok. %%----------------------------------------------------------------- -%% Functions for communicating with a file io server. +%% Function for communicating with a file io server. %% The messages sent have the following formats: %% %% {file_request,From,ReplyAs,Request} %% {file_reply,ReplyAs,Reply} file_request(Io, Request) -> - R = erlang:monitor(process, Io), - Io ! {file_request,self(),Io,Request}, - R. - -wait_file_reply(From, Ref) -> + Ref = erlang:monitor(process, Io), + Io ! {file_request,self(),Ref,Request}, receive - {file_reply,From,Reply} -> - erlang:demonitor(Ref), - receive {'DOWN', Ref, _, _, _} -> ok after 0 -> ok end, - %% receive {'EXIT', From, _} -> ok after 0 -> ok end, + {file_reply,Ref,Reply} -> + erlang:demonitor(Ref, [flush]), Reply; {'DOWN', Ref, _, _, _} -> - %% receive {'EXIT', From, _} -> ok after 0 -> ok end, {error, terminated} end. diff --git a/lib/kernel/src/file_io_server.erl b/lib/kernel/src/file_io_server.erl index fad2ed7fb3..07fb55f390 100644 --- a/lib/kernel/src/file_io_server.erl +++ b/lib/kernel/src/file_io_server.erl @@ -92,8 +92,7 @@ do_start(Spawn, Owner, FileName, ModeList) -> Mref = erlang:monitor(process, Pid), receive {Ref, {error, _Reason} = Error} -> - erlang:demonitor(Mref), - receive {'DOWN', Mref, _, _, _} -> ok after 0 -> ok end, + erlang:demonitor(Mref, [flush]), Error; {Ref, ok} -> erlang:demonitor(Mref), diff --git a/lib/kernel/src/file_server.erl b/lib/kernel/src/file_server.erl index 49ec6f96cc..d036dbb516 100644 --- a/lib/kernel/src/file_server.erl +++ b/lib/kernel/src/file_server.erl @@ -317,8 +317,7 @@ do_start_slave(start, Filer, Name) -> SlaveMonitor = erlang:monitor(process, Slave), receive {started, Token} -> - erlang:demonitor(SlaveMonitor), - receive {'DOWN', SlaveMonitor, _, _, _} -> ok after 0 -> ok end, + erlang:demonitor(SlaveMonitor, [flush]), {ok, Slave}; {'DOWN', SlaveMonitor, _, _, Reason} -> exit(Reason) diff --git a/lib/kernel/src/gen_sctp.erl b/lib/kernel/src/gen_sctp.erl index 74ad192802..acc116df84 100644 --- a/lib/kernel/src/gen_sctp.erl +++ b/lib/kernel/src/gen_sctp.erl @@ -39,7 +39,9 @@ {active, true | false | once} | {buffer, non_neg_integer()} | {dontroute, boolean()} | + {high_msgq_watermark, pos_integer()} | {linger, {boolean(), non_neg_integer()}} | + {low_msgq_watermark, pos_integer()} | {mode, list | binary} | list | binary | {priority, non_neg_integer()} | {recbuf, non_neg_integer()} | @@ -68,7 +70,9 @@ active | buffer | dontroute | + high_msgq_watermark | linger | + low_msgq_watermark | mode | priority | recbuf | diff --git a/lib/kernel/src/gen_udp.erl b/lib/kernel/src/gen_udp.erl index c5a1173575..fb5737f82e 100644 --- a/lib/kernel/src/gen_udp.erl +++ b/lib/kernel/src/gen_udp.erl @@ -34,6 +34,8 @@ {dontroute, boolean()} | {drop_membership, {inet:ip_address(), inet:ip_address()}} | {header, non_neg_integer()} | + {high_msgq_watermark, pos_integer()} | + {low_msgq_watermark, pos_integer()} | {mode, list | binary} | list | binary | {multicast_if, inet:ip_address()} | {multicast_loop, boolean()} | @@ -56,6 +58,8 @@ deliver | dontroute | header | + high_msgq_watermark | + low_msgq_watermark | mode | multicast_if | multicast_loop | diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index 9670271b2e..aada1252ad 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -712,7 +712,8 @@ udp_options() -> [tos, priority, reuseaddr, sndbuf, recbuf, header, active, buffer, mode, deliver, ipv6_v6only, broadcast, dontroute, multicast_if, multicast_ttl, multicast_loop, - add_membership, drop_membership, read_packets,raw]. + add_membership, drop_membership, read_packets,raw, + high_msgq_watermark, low_msgq_watermark]. udp_options(Opts, Family) -> @@ -766,7 +767,7 @@ udp_add(Name, Val, R, Opts, As) -> sctp_options() -> [ % The following are generic inet options supported for SCTP sockets: mode, active, buffer, tos, priority, dontroute, reuseaddr, linger, sndbuf, - recbuf, ipv6_v6only, + recbuf, ipv6_v6only, high_msgq_watermark, low_msgq_watermark, % Other options are SCTP-specific (though they may be similar to their % TCP and UDP counter-parts): diff --git a/lib/kernel/src/inet_gethost_native.erl b/lib/kernel/src/inet_gethost_native.erl index db3e44ce6f..df866660b4 100644 --- a/lib/kernel/src/inet_gethost_native.erl +++ b/lib/kernel/src/inet_gethost_native.erl @@ -503,8 +503,7 @@ getit(Req, DefaultName) -> Pid, Reason} -> {error, Reason} end, - catch erlang:demonitor(Ref2), - receive {'DOWN',Ref2,_,_,_} -> ok after 0 -> ok end, + catch erlang:demonitor(Ref2, [flush]), Res2 end. diff --git a/lib/kernel/src/inet_int.hrl b/lib/kernel/src/inet_int.hrl index 000119bc74..a01c733d81 100644 --- a/lib/kernel/src/inet_int.hrl +++ b/lib/kernel/src/inet_int.hrl @@ -141,8 +141,8 @@ -define(INET_LOPT_READ_PACKETS, 33). -define(INET_OPT_RAW, 34). -define(INET_LOPT_TCP_SEND_TIMEOUT_CLOSE, 35). --define(INET_LOPT_TCP_MSGQ_HIWTRMRK, 36). --define(INET_LOPT_TCP_MSGQ_LOWTRMRK, 37). +-define(INET_LOPT_MSGQ_HIWTRMRK, 36). +-define(INET_LOPT_MSGQ_LOWTRMRK, 37). % Specific SCTP options: separate range: -define(SCTP_OPT_RTOINFO, 100). -define(SCTP_OPT_ASSOCINFO, 101). diff --git a/lib/kernel/src/rpc.erl b/lib/kernel/src/rpc.erl index 7c965ca384..83e0b59cc2 100644 --- a/lib/kernel/src/rpc.erl +++ b/lib/kernel/src/rpc.erl @@ -388,13 +388,8 @@ server_call(Node, Name, ReplyWrapper, Msg) {'DOWN', Ref, _, _, _} -> {error, nodedown}; {ReplyWrapper, Node, Reply} -> - erlang:demonitor(Ref), - receive - {'DOWN', Ref, _, _, _} -> - Reply - after 0 -> - Reply - end + erlang:demonitor(Ref, [flush]), + Reply end end. @@ -501,17 +496,6 @@ start_monitor(Node, Name) -> {Node,erlang:monitor(process, {Name, Node})} end. -%% Cancels a monitor started with Ref=erlang:monitor(_, _), -%% i.e return value {Node, Ref} from start_monitor/2 above. -unmonitor(Ref) when is_reference(Ref) -> - erlang:demonitor(Ref), - receive - {'DOWN', Ref, _, _, _} -> - true - after 0 -> - true - end. - %% Call apply(M,F,A) on all nodes in parallel -spec multicall(Module, Function, Args) -> {ResL, BadNodes} when @@ -635,10 +619,10 @@ rec_nodes(Name, [{N,R} | Tail], Badnodes, Replies) -> rec_nodes(Name, Tail, [N|Badnodes], Replies); {?NAME, N, {nonexisting_name, _}} -> %% used by sbcast() - unmonitor(R), + erlang:demonitor(R, [flush]), rec_nodes(Name, Tail, [N|Badnodes], Replies); {Name, N, Reply} -> %% Name is bound !!! - unmonitor(R), + erlang:demonitor(R, [flush]), rec_nodes(Name, Tail, Badnodes, [Reply|Replies]) end. diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index c604e7073f..4218cfa646 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -91,6 +91,8 @@ -export([standard_io/1,mini_server/1]). +-export([old_io_protocol/1]). + %% Debug exports -export([create_file_slow/2, create_file/2, create_bin/2]). -export([verify_file/2, verify_bin/3]). @@ -114,7 +116,7 @@ all() -> delayed_write, read_ahead, segment_read, segment_write, ipread, pid2name, interleaved_read_write, otp_5814, otp_10852, large_file, large_write, read_line_1, read_line_2, read_line_3, - read_line_4, standard_io]. + read_line_4, standard_io, old_io_protocol]. groups() -> [{dirs, [], [make_del_dir, cur_dir_0, cur_dir_1, @@ -310,6 +312,31 @@ standard_io(Config) when is_list(Config) -> Pid ! die, receive after 1000 -> ok end. +old_io_protocol(suite) -> + []; +old_io_protocol(doc) -> + ["Test that the old file IO protocol =< R16B still works"]; +old_io_protocol(Config) when is_list(Config) -> + Dog = test_server:timetrap(test_server:seconds(5)), + RootDir = ?config(priv_dir,Config), + Name = filename:join(RootDir, + atom_to_list(?MODULE) + ++"old_io_protocol.fil"), + MyData = "0123456789abcdefghijklmnopqrstuvxyz", + ok = ?FILE_MODULE:write_file(Name, MyData), + {ok, Fd} = ?FILE_MODULE:open(Name, write), + Fd ! {file_request,self(),Fd,truncate}, + receive + {file_reply,Fd,ok} -> ok + end, + ok = ?FILE_MODULE:close(Fd), + {ok, <<>>} = ?FILE_MODULE:read_file(Name), + test_server:timetrap_cancel(Dog), + [] = flush(), + ok. + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% read_write_file(suite) -> []; diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl index 2a886b2efc..c5d8becfd3 100644 --- a/lib/kernel/test/gen_sctp_SUITE.erl +++ b/lib/kernel/test/gen_sctp_SUITE.erl @@ -1399,8 +1399,7 @@ s_req(S, Req) -> {'DOWN',Mref,_,_,Error} -> exit(Error); {S,Mref,Reply} -> - erlang:demonitor(Mref), - receive {'DOWN',Mref,_,_,_} -> ok after 0 -> ok end, + erlang:demonitor(Mref, [flush]), Reply end. diff --git a/lib/kernel/test/global_SUITE_data/global_trace.erl b/lib/kernel/test/global_SUITE_data/global_trace.erl index 4f253baac4..ddbe608c0a 100644 --- a/lib/kernel/test/global_SUITE_data/global_trace.erl +++ b/lib/kernel/test/global_SUITE_data/global_trace.erl @@ -1,3 +1,4 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% @@ -122,12 +123,12 @@ state(Else) -> %%% {ops,Ops}] %%% NewKnown = Known ++ AddedNodes %%% AddedNodes = NewNodes -- Known -%%% NewNodes �r h�r den man f�rhandlat med plus de noder den k�nner till. +%%% NewNodes är här den man förhandlat med plus de noder den känner till. %%% {added, AddedNodes}, Extra = [{ops,Ops}] %%% NewKnown = Known ++ AddedNodes -%%% Den (passiva) noden f�r Nodes som �r NewNodes -%%% hos den f�rhandlande. Sedan: AddedNodes = (Nodes -- Known) -- [node()]. -%%% Det �r som hos f�rhandlaren. +%%% Den (passiva) noden får Nodes som är NewNodes +%%% hos den förhandlande. Sedan: AddedNodes = (Nodes -- Known) -- [node()]. +%%% Det är som hos förhandlaren. %%% {nodes_changed, {New,Old}} %%% Every now and then the list [node() | nodes()] is checked for updates. %%% New are the nodes that global does not know of (yet). diff --git a/lib/kernel/test/inet_sockopt_SUITE.erl b/lib/kernel/test/inet_sockopt_SUITE.erl index 75496ce745..185751fead 100644 --- a/lib/kernel/test/inet_sockopt_SUITE.erl +++ b/lib/kernel/test/inet_sockopt_SUITE.erl @@ -772,8 +772,10 @@ all_listen_options() -> {mode,list,binary,true,true}, {deliver,term,port,true,true}, {exit_on_close, true, false, true, true}, - %{high_watermark,4096,8192,true,true}, - %{low_watermark,2048,4096,true,true}, + {high_watermark,4096,8192,true,true}, + {low_watermark,2048,4096,true,true}, + {high_msgq_watermark,4096,8192,true,true}, + {low_msgq_watermark,2048,4096,true,true}, {send_timeout,infinity,1000,true,true}, {send_timeout_close,false,true,true,true}, {delay_send,false,true,true,true}, @@ -797,6 +799,8 @@ all_connect_options() -> {exit_on_close, true, false, true, true}, {high_watermark,4096,8192,false,true}, {low_watermark,2048,4096,false,true}, + {high_msgq_watermark,4096,8192,true,true}, + {low_msgq_watermark,2048,4096,true,true}, {send_timeout,infinity,1000,true,true}, {send_timeout_close,false,true,true,true}, {delay_send,false,true,true,true}, diff --git a/lib/mnesia/src/mnesia_loader.erl b/lib/mnesia/src/mnesia_loader.erl index 4ba400fbbf..4afbea1cc2 100644 --- a/lib/mnesia/src/mnesia_loader.erl +++ b/lib/mnesia/src/mnesia_loader.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2011. All Rights Reserved. +%% Copyright Ericsson AB 1998-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -487,7 +487,8 @@ finish_copy(Storage,Tab,Cs,SenderPid,DatBin,OrigTabRec) -> subscr_receiver(TabRef = {_, Tab}, RecName) -> receive - {mnesia_table_event, {Op, Val, _Tid}} -> + {mnesia_table_event, {Op, Val, _Tid}} + when element(1, Val) =:= Tab -> if Tab == RecName -> handle_event(TabRef, Op, Val); @@ -496,6 +497,15 @@ subscr_receiver(TabRef = {_, Tab}, RecName) -> end, subscr_receiver(TabRef, RecName); + {mnesia_table_event, {Op, Val, _Tid}} when element(1, Val) =:= schema -> + %% clear_table is faked via two schema events + %% a schema record delete and a write + case Op of + delete -> handle_event(TabRef, clear_table, {Tab, all}); + _ -> ok + end, + subscr_receiver(TabRef, RecName); + {'EXIT', Pid, Reason} -> handle_exit(Pid, Reason), subscr_receiver(TabRef, RecName) diff --git a/lib/mnesia/test/mnesia_evil_coverage_test.erl b/lib/mnesia/test/mnesia_evil_coverage_test.erl index 64b61288ef..0df245b75d 100644 --- a/lib/mnesia/test/mnesia_evil_coverage_test.erl +++ b/lib/mnesia/test/mnesia_evil_coverage_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -39,7 +39,7 @@ all() -> db_node_lifecycle, evil_delete_db_node, start_and_stop, checkpoint, table_lifecycle, storage_options, add_copy_conflict, - add_copy_when_going_down, replica_management, + add_copy_when_going_down, replica_management, clear_table_during_load, schema_availability, local_content, {group, table_access_modifications}, replica_location, {group, table_sync}, user_properties, unsupp_user_props, @@ -569,7 +569,50 @@ storage_options(Config) when is_list(Config) -> ?verify_mnesia(Nodes, []). +clear_table_during_load(suite) -> []; +clear_table_during_load(doc) -> + ["Clear table caused during load caused a schema entry in the actual tab"]; +clear_table_during_load(Config) when is_list(Config) -> + Nodes = [_, Node2] = ?acquire_nodes(2, Config ++ [{tc_timeout, timer:minutes(2)}]), + ?match({atomic,ok}, mnesia:create_table(cleartab, [{ram_copies, Nodes}])), + Tester = self(), + Bin = <<"Testingasdasd", 0:32000>>, + Fill = fun() -> [mnesia:write({cleartab, N, Bin}) || N <- lists:seq(1, 3000)], ok end, + ?match({atomic, ok}, mnesia:sync_transaction(Fill)), + + StopAndStart = fun() -> + stopped = mnesia:stop(), + Tester ! {self(), stopped}, + receive start_node -> ok end, + ok = mnesia:start(), + ok = mnesia:wait_for_tables([cleartab], 2000), + lists:foreach(fun({cleartab,_,_}) -> ok; + (What) -> Tester ! {failed, What}, + unlink(Tester), + exit(foo) + end, + ets:tab2list(cleartab)), + Tester ! {self(), ok}, + normal + end, + Test = fun(N) -> + Pid = spawn_link(Node2, StopAndStart), + receive {Pid, stopped} -> ok end, + Pid ! start_node, + timer:sleep(N*10), + {atomic, ok} = mnesia:clear_table(cleartab), + receive + {Pid, ok} -> ok; + {failed, What} -> + io:format("Failed in ~p tries, with ~p~n",[N, What]), + exit({error, What}); + {'EXIT', Pid, Reason} -> + exit({died, Reason}) + end + end, + [Test(N) || N <- lists:seq(1, 10)], + ?verify_mnesia(Nodes, []). add_copy_conflict(suite) -> []; @@ -599,7 +642,7 @@ add_copy_conflict(Config) when is_list(Config) -> mnesia_controller:unblock_controller(), ?match_receive({test, {atomic,ok}}), - + ?match(ok, mnesia:wait_for_tables([a,b], 3000)), ?verify_mnesia(Nodes, []), ?cleanup(1, Config). @@ -635,7 +678,7 @@ add_copy_when_going_down(Config) -> end, _Lock = spawn(fun() -> mnesia:transaction(WriteAndWait) end), Tester = self(), - spawn_link(fun() -> Res = rpc:call(Node2,mnesia, add_table_copy, + spawn_link(fun() -> Res = rpc:call(Node2, mnesia, add_table_copy, [a, Node2, ram_copies]), Tester ! {test, Res} end), diff --git a/lib/orber/src/orber_iiop_outproxy.erl b/lib/orber/src/orber_iiop_outproxy.erl index 879af8222d..9c4e603753 100644 --- a/lib/orber/src/orber_iiop_outproxy.erl +++ b/lib/orber/src/orber_iiop_outproxy.erl @@ -69,13 +69,8 @@ request(Pid, true, Timeout, Msg, RequestId) -> gen_server:cast(Pid, {request, Timeout, Msg, RequestId, self(), MRef}), receive {MRef, Reply} -> - erlang:demonitor(MRef), - receive - {'DOWN', MRef, _, _, _} -> - Reply - after 0 -> - Reply - end; + erlang:demonitor(MRef, [flush]), + Reply; {'DOWN', MRef, _, Pid, _Reason} when is_pid(Pid) -> receive %% Clear EXIT message from queue @@ -444,13 +439,7 @@ collect_fragments(GIOPHdr1, InBuffer, Bytes, Proxy, RequestId, MRef) -> %% the reply and send it to the client. {fragment, #giop_message{byte_order = ByteOrder, message = Message} = GIOPHdr2, RequestId, MRef} -> - erlang:demonitor(MRef), - receive - {'DOWN', MRef, _, _, _} -> - ok - after 0 -> - ok - end, + erlang:demonitor(MRef, [flush]), case catch cdr_decode:dec_message_header(null, GIOPHdr2, Message) of {_, #fragment_header{}, FragBody, _, _} -> %% This buffer is all the fragments concatenated. @@ -484,13 +473,8 @@ Unable to decode Reply or LocateReply header",[?LINE, NewGIOP, Error], ?DEBUG_LE {MRef, {'EXCEPTION', E}} -> orber:dbg("[~p] orber_iiop:collect_fragments(~p);", [?LINE, E], ?DEBUG_LEVEL), - erlang:demonitor(MRef), - receive - {'DOWN', MRef, _, _, _} -> - corba:raise(E) - after 0 -> - corba:raise(E) - end; + erlang:demonitor(MRef, [flush]), + corba:raise(E); {'DOWN', MRef, _, Proxy, Reason} when is_pid(Proxy) -> orber:dbg("[~p] orber_iiop:collect_fragments(~p);~n" "Monitor generated a DOWN message.", @@ -511,13 +495,8 @@ clear_queue(Proxy, RequestId, MRef) -> {MRef, RequestId, cancelled} -> %% This is the last message that the proxy will send %% after we've cancelled the request. - erlang:demonitor(MRef), - receive - {'DOWN', MRef, _, _, _} -> - ok - after 0 -> - ok - end; + erlang:demonitor(MRef, [flush]), + ok; {'DOWN', MRef, _, Proxy, _Reason} -> %% The proxy terminated. Clear EXIT message from queue receive diff --git a/lib/public_key/asn1/ECPrivateKey.asn1 b/lib/public_key/asn1/ECPrivateKey.asn1 new file mode 100644 index 0000000000..a20fa4009c --- /dev/null +++ b/lib/public_key/asn1/ECPrivateKey.asn1 @@ -0,0 +1,24 @@ +ECPrivateKey { iso(1) identified-organization(3) dod(6) + internet(1) security(5) mechanisms(5) pkix(7) id-mod(0) + id-mod-ecprivateKey(65) } + +DEFINITIONS EXPLICIT TAGS ::= + +BEGIN + +-- EXPORTS ALL; + +IMPORTS + +-- FROM New PKIX ASN.1 [RFC5912] + +EcpkParameters FROM PKIX1Algorithms88; + +ECPrivateKey ::= SEQUENCE { + version INTEGER, + privateKey OCTET STRING, + parameters [0] EcpkParameters OPTIONAL, + publicKey [1] BIT STRING OPTIONAL +} + +END diff --git a/lib/public_key/asn1/OTP-PKIX.asn1 b/lib/public_key/asn1/OTP-PKIX.asn1 index a90fe2840c..911a156d6c 100644 --- a/lib/public_key/asn1/OTP-PKIX.asn1 +++ b/lib/public_key/asn1/OTP-PKIX.asn1 @@ -103,15 +103,16 @@ IMPORTS md5WithRSAEncryption, sha1WithRSAEncryption, rsaEncryption, RSAPublicKey, - dhpublicnumber, DomainParameters, DHPublicKey, + dhpublicnumber, DomainParameters, DHPublicKey, id-keyExchangeAlgorithm, KEA-Parms-Id, --KEA-PublicKey, - ecdsa-with-SHA1, + ecdsa-with-SHA1, ecdsa-with-SHA224, + ecdsa-with-SHA256, ecdsa-with-SHA384, ecdsa-with-SHA512, prime-field, Prime-p, characteristic-two-field, --Characteristic-two, gnBasis, tpBasis, Trinomial, ppBasis, Pentanomial, - id-ecPublicKey, EcpkParameters, ECPoint + id-ecPublicKey, EcpkParameters, ECParameters, ECPoint FROM PKIX1Algorithms88 { iso(1) identified-organization(3) dod(6) internet(1) security(5) mechanisms(5) pkix(7) id-mod(0) id-mod-pkix1-algorithms(17) } @@ -321,7 +322,11 @@ SupportedSignatureAlgorithms SIGNATURE-ALGORITHM-CLASS ::= { sha256-with-rsa-encryption | sha384-with-rsa-encryption | sha512-with-rsa-encryption | - ecdsa-with-sha1 } + ecdsa-with-sha1 | + ecdsa-with-sha224 | + ecdsa-with-sha256 | + ecdsa-with-sha384 | + ecdsa-with-sha512 } SupportedPublicKeyAlgorithms PUBLIC-KEY-ALGORITHM-CLASS ::= { dsa | rsa-encryption | dh | kea | ec-public-key } @@ -439,6 +444,22 @@ SupportedPublicKeyAlgorithms PUBLIC-KEY-ALGORITHM-CLASS ::= { ID ecdsa-with-SHA1 TYPE NULL } -- XXX Must be empty and not NULL + ecdsa-with-sha224 SIGNATURE-ALGORITHM-CLASS ::= { + ID ecdsa-with-SHA224 + TYPE NULL } -- XXX Must be empty and not NULL + + ecdsa-with-sha256 SIGNATURE-ALGORITHM-CLASS ::= { + ID ecdsa-with-SHA256 + TYPE NULL } -- XXX Must be empty and not NULL + + ecdsa-with-sha384 SIGNATURE-ALGORITHM-CLASS ::= { + ID ecdsa-with-SHA384 + TYPE NULL } -- XXX Must be empty and not NULL + + ecdsa-with-sha512 SIGNATURE-ALGORITHM-CLASS ::= { + ID ecdsa-with-SHA512 + TYPE NULL } -- XXX Must be empty and not NULL + FIELD-ID-CLASS ::= CLASS { &id OBJECT IDENTIFIER UNIQUE, &Type } @@ -489,6 +510,7 @@ SupportedPublicKeyAlgorithms PUBLIC-KEY-ALGORITHM-CLASS ::= { ID ppBasis TYPE Pentanomial } + -- SubjectPublicKeyInfo.algorithm ec-public-key PUBLIC-KEY-ALGORITHM-CLASS ::= { diff --git a/lib/public_key/asn1/OTP-PUB-KEY.set.asn b/lib/public_key/asn1/OTP-PUB-KEY.set.asn index f8fb318c93..e94f428e4b 100644 --- a/lib/public_key/asn1/OTP-PUB-KEY.set.asn +++ b/lib/public_key/asn1/OTP-PUB-KEY.set.asn @@ -6,5 +6,6 @@ PKIX1Algorithms88.asn1 PKCS-1.asn1 PKCS-3.asn1 DSS.asn1 +ECPrivateKey.asn1 PKCS-7.asn1 PKCS-10.asn1 diff --git a/lib/public_key/asn1/PKCS-1.asn1 b/lib/public_key/asn1/PKCS-1.asn1 index b5754790e7..117eacd8ad 100644 --- a/lib/public_key/asn1/PKCS-1.asn1 +++ b/lib/public_key/asn1/PKCS-1.asn1 @@ -52,8 +52,40 @@ id-md5 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 5 } +id-hmacWithSHA224 OBJECT IDENTIFIER ::= { + iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 8 +} + +id-hmacWithSHA256 OBJECT IDENTIFIER ::= { + iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 9 +} + +id-hmacWithSHA384 OBJECT IDENTIFIER ::= { + iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 10 +} + +id-hmacWithSHA512 OBJECT IDENTIFIER ::= { + iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 11 +} + id-mgf1 OBJECT IDENTIFIER ::= { pkcs-1 8 } +id-sha224 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) + country(16) us(840) organization(1) gov(101) csor(3) + nistalgorithm(4) hashalgs(2) 4 } + +id-sha256 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) + country(16) us(840) organization(1) gov(101) csor(3) + nistalgorithm(4) hashalgs(2) 1 } + +id-sha384 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) + country(16) us(840) organization(1) gov(101) csor(3) + nistalgorithm(4) hashalgs(2) 2 } + +id-sha512 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) + country(16) us(840) organization(1) gov(101) csor(3) + nistalgorithm(4) hashalgs(2) 3 } + RSAPublicKey ::= SEQUENCE { modulus INTEGER, -- n diff --git a/lib/public_key/asn1/PKIX1Algorithms88.asn1 b/lib/public_key/asn1/PKIX1Algorithms88.asn1 index 74225747d3..6cc6745af6 100644 --- a/lib/public_key/asn1/PKIX1Algorithms88.asn1 +++ b/lib/public_key/asn1/PKIX1Algorithms88.asn1 @@ -98,6 +98,11 @@ -- OID for ECDSA signatures with SHA-1 ecdsa-with-SHA1 OBJECT IDENTIFIER ::= { id-ecSigType 1 } + ecdsa-with-SHA2 OBJECT IDENTIFIER ::= { id-ecSigType 3 } + ecdsa-with-SHA224 OBJECT IDENTIFIER ::= { ecdsa-with-SHA2 1 } + ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { ecdsa-with-SHA2 2 } + ecdsa-with-SHA384 OBJECT IDENTIFIER ::= { ecdsa-with-SHA2 3 } + ecdsa-with-SHA512 OBJECT IDENTIFIER ::= { ecdsa-with-SHA2 4 } -- OID for an elliptic curve signature -- format for the value of an ECDSA signature value @@ -199,40 +204,83 @@ -- Named Elliptic Curves in ANSI X9.62. - ellipticCurve OBJECT IDENTIFIER ::= { ansi-X9-62 curves(3) } - - c-TwoCurve OBJECT IDENTIFIER ::= { - ellipticCurve characteristicTwo(0) } - - c2pnb163v1 OBJECT IDENTIFIER ::= { c-TwoCurve 1 } - c2pnb163v2 OBJECT IDENTIFIER ::= { c-TwoCurve 2 } - c2pnb163v3 OBJECT IDENTIFIER ::= { c-TwoCurve 3 } - c2pnb176w1 OBJECT IDENTIFIER ::= { c-TwoCurve 4 } - c2tnb191v1 OBJECT IDENTIFIER ::= { c-TwoCurve 5 } - c2tnb191v2 OBJECT IDENTIFIER ::= { c-TwoCurve 6 } - c2tnb191v3 OBJECT IDENTIFIER ::= { c-TwoCurve 7 } - c2onb191v4 OBJECT IDENTIFIER ::= { c-TwoCurve 8 } - c2onb191v5 OBJECT IDENTIFIER ::= { c-TwoCurve 9 } - c2pnb208w1 OBJECT IDENTIFIER ::= { c-TwoCurve 10 } - c2tnb239v1 OBJECT IDENTIFIER ::= { c-TwoCurve 11 } - c2tnb239v2 OBJECT IDENTIFIER ::= { c-TwoCurve 12 } - c2tnb239v3 OBJECT IDENTIFIER ::= { c-TwoCurve 13 } - c2onb239v4 OBJECT IDENTIFIER ::= { c-TwoCurve 14 } - c2onb239v5 OBJECT IDENTIFIER ::= { c-TwoCurve 15 } - c2pnb272w1 OBJECT IDENTIFIER ::= { c-TwoCurve 16 } - c2pnb304w1 OBJECT IDENTIFIER ::= { c-TwoCurve 17 } - c2tnb359v1 OBJECT IDENTIFIER ::= { c-TwoCurve 18 } - c2pnb368w1 OBJECT IDENTIFIER ::= { c-TwoCurve 19 } - c2tnb431r1 OBJECT IDENTIFIER ::= { c-TwoCurve 20 } - - primeCurve OBJECT IDENTIFIER ::= { ellipticCurve prime(1) } - - prime192v1 OBJECT IDENTIFIER ::= { primeCurve 1 } - prime192v2 OBJECT IDENTIFIER ::= { primeCurve 2 } - prime192v3 OBJECT IDENTIFIER ::= { primeCurve 3 } - prime239v1 OBJECT IDENTIFIER ::= { primeCurve 4 } - prime239v2 OBJECT IDENTIFIER ::= { primeCurve 5 } - prime239v3 OBJECT IDENTIFIER ::= { primeCurve 6 } - prime256v1 OBJECT IDENTIFIER ::= { primeCurve 7 } + -- ellipticCurve OBJECT IDENTIFIER ::= { ansi-X9-62 curves(3) } + + -- c-TwoCurve OBJECT IDENTIFIER ::= { + -- ansi-ellipticCurve characteristicTwo(0) } + + -- c2pnb163v1 OBJECT IDENTIFIER ::= { c-TwoCurve 1 } + -- c2pnb163v2 OBJECT IDENTIFIER ::= { c-TwoCurve 2 } + -- c2pnb163v3 OBJECT IDENTIFIER ::= { c-TwoCurve 3 } + -- c2pnb176w1 OBJECT IDENTIFIER ::= { c-TwoCurve 4 } + -- c2tnb191v1 OBJECT IDENTIFIER ::= { c-TwoCurve 5 } + -- c2tnb191v2 OBJECT IDENTIFIER ::= { c-TwoCurve 6 } + -- c2tnb191v3 OBJECT IDENTIFIER ::= { c-TwoCurve 7 } + -- c2onb191v4 OBJECT IDENTIFIER ::= { c-TwoCurve 8 } + -- c2onb191v5 OBJECT IDENTIFIER ::= { c-TwoCurve 9 } + -- c2pnb208w1 OBJECT IDENTIFIER ::= { c-TwoCurve 10 } + -- c2tnb239v1 OBJECT IDENTIFIER ::= { c-TwoCurve 11 } + -- c2tnb239v2 OBJECT IDENTIFIER ::= { c-TwoCurve 12 } + -- c2tnb239v3 OBJECT IDENTIFIER ::= { c-TwoCurve 13 } + -- c2onb239v4 OBJECT IDENTIFIER ::= { c-TwoCurve 14 } + -- c2onb239v5 OBJECT IDENTIFIER ::= { c-TwoCurve 15 } + -- c2pnb272w1 OBJECT IDENTIFIER ::= { c-TwoCurve 16 } + -- c2pnb304w1 OBJECT IDENTIFIER ::= { c-TwoCurve 17 } + -- c2tnb359v1 OBJECT IDENTIFIER ::= { c-TwoCurve 18 } + -- c2pnb368w1 OBJECT IDENTIFIER ::= { c-TwoCurve 19 } + -- c2tnb431r1 OBJECT IDENTIFIER ::= { c-TwoCurve 20 } + + -- primeCurve OBJECT IDENTIFIER ::= { ansi-ellipticCurve prime(1) } + + -- prime192v1 OBJECT IDENTIFIER ::= { primeCurve 1 } + -- prime192v2 OBJECT IDENTIFIER ::= { primeCurve 2 } + -- prime192v3 OBJECT IDENTIFIER ::= { primeCurve 3 } + -- prime239v1 OBJECT IDENTIFIER ::= { primeCurve 4 } + -- prime239v2 OBJECT IDENTIFIER ::= { primeCurve 5 } + -- prime239v3 OBJECT IDENTIFIER ::= { primeCurve 6 } + -- prime256v1 OBJECT IDENTIFIER ::= { primeCurve 7 } + + certicom-arc OBJECT IDENTIFIER ::= { + iso(1) identified-organization(3) certicom(132) + } + + ellipticCurve OBJECT IDENTIFIER ::= { + iso(1) identified-organization(3) certicom(132) curve(0) + } + + secp192r1 OBJECT IDENTIFIER ::= { ansi-X9-62 curves(3) prime(1) 1 } + secp256r1 OBJECT IDENTIFIER ::= { ansi-X9-62 curves(3) prime(1) 7 } + + sect163k1 OBJECT IDENTIFIER ::= { ellipticCurve 1 } + sect163r1 OBJECT IDENTIFIER ::= { ellipticCurve 2 } + sect239k1 OBJECT IDENTIFIER ::= { ellipticCurve 3 } + sect113r1 OBJECT IDENTIFIER ::= { ellipticCurve 4 } + sect113r2 OBJECT IDENTIFIER ::= { ellipticCurve 5 } + secp112r1 OBJECT IDENTIFIER ::= { ellipticCurve 6 } + secp112r2 OBJECT IDENTIFIER ::= { ellipticCurve 7 } + secp160r1 OBJECT IDENTIFIER ::= { ellipticCurve 8 } + secp160k1 OBJECT IDENTIFIER ::= { ellipticCurve 9 } + secp256k1 OBJECT IDENTIFIER ::= { ellipticCurve 10 } + sect163r2 OBJECT IDENTIFIER ::= { ellipticCurve 15 } + sect283k1 OBJECT IDENTIFIER ::= { ellipticCurve 16 } + sect283r1 OBJECT IDENTIFIER ::= { ellipticCurve 17 } + sect131r1 OBJECT IDENTIFIER ::= { ellipticCurve 22 } + sect131r2 OBJECT IDENTIFIER ::= { ellipticCurve 23 } + sect193r1 OBJECT IDENTIFIER ::= { ellipticCurve 24 } + sect193r2 OBJECT IDENTIFIER ::= { ellipticCurve 25 } + sect233k1 OBJECT IDENTIFIER ::= { ellipticCurve 26 } + sect233r1 OBJECT IDENTIFIER ::= { ellipticCurve 27 } + secp128r1 OBJECT IDENTIFIER ::= { ellipticCurve 28 } + secp128r2 OBJECT IDENTIFIER ::= { ellipticCurve 29 } + secp160r2 OBJECT IDENTIFIER ::= { ellipticCurve 30 } + secp192k1 OBJECT IDENTIFIER ::= { ellipticCurve 31 } + secp224k1 OBJECT IDENTIFIER ::= { ellipticCurve 32 } + secp224r1 OBJECT IDENTIFIER ::= { ellipticCurve 33 } + secp384r1 OBJECT IDENTIFIER ::= { ellipticCurve 34 } + secp521r1 OBJECT IDENTIFIER ::= { ellipticCurve 35 } + sect409k1 OBJECT IDENTIFIER ::= { ellipticCurve 36 } + sect409r1 OBJECT IDENTIFIER ::= { ellipticCurve 37 } + sect571k1 OBJECT IDENTIFIER ::= { ellipticCurve 38 } + sect571r1 OBJECT IDENTIFIER ::= { ellipticCurve 39 } END diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml index 84300f6e65..10c95a39ac 100644 --- a/lib/public_key/doc/src/public_key.xml +++ b/lib/public_key/doc/src/public_key.xml @@ -84,7 +84,8 @@ <p><code>pki_asn1_type() = 'Certificate' | 'RSAPrivateKey'| 'RSAPublicKey' | 'DSAPrivateKey' | 'DSAPublicKey' | 'DHParameter' | 'SubjectPublicKeyInfo' | - 'PrivateKeyInfo' | 'CertificationRequest'</code></p> + 'PrivateKeyInfo' | 'CertificationRequest' | 'ECPrivateKey'| + 'EcpkParameters'</code></p> <p><code>pem_entry () = {pki_asn1_type(), binary(), %% DER or encrypted DER not_encrypted | cipher_info()} </code></p> @@ -99,7 +100,11 @@ <p><code>dsa_public_key() = {integer(), #'Dss-Parms'{}} </code></p> <p><code>dsa_private_key() = #'DSAPrivateKey'{}</code></p> + + <p><code>ec_public_key() = {#'ECPoint'{}, #'EcpkParameters'{} | {namedCurve, oid()}} </code></p> + <p><code>ec_private_key() = #'ECPrivateKey'{}</code></p> + <p><code> public_crypt_options() = [{rsa_pad, rsa_padding()}]. </code></p> <p><code> rsa_padding() = 'rsa_pkcs1_padding' | 'rsa_pkcs1_oaep_padding' @@ -109,6 +114,8 @@ <p><code> dss_digest_type() = 'sha' </code></p> + <p><code> ecdsa_digest_type() = 'sha'| 'sha224' | 'sha256' | 'sha384' | 'sha512' </code></p> + <p><code> crl_reason() = unspecified | keyCompromise | cACompromise | affiliationChanged | superseded | cessationOfOperation | certificateHold | privilegeWithdrawn | aACompromise </code></p> @@ -147,6 +154,19 @@ <funcs> <func> + <name> compute_key(OthersKey, MyKey)-></name> + <name> compute_key(OthersKey, MyKey, Params)-></name> + <fsummary> Compute shared secret</fsummary> + <type> + <v>OthersKey = #'ECPoint'{} | binary(), MyKey = #'ECPrivateKey'{} | binary()</v> + <v>Params = #'DHParameter'{}</v> + </type> + <desc> + <p> Compute shared secret </p> + </desc> + </func> + + <func> <name>decrypt_private(CipherText, Key) -> binary()</name> <name>decrypt_private(CipherText, Key, Options) -> binary()</name> <fsummary>Public key decryption.</fsummary> @@ -204,6 +224,17 @@ </func> <func> + <name>generate_key(Params) -> {Public::binary(), Private::binary()} | #'ECPrivateKey'{} </name> + <fsummary>Generates a new keypair</fsummary> + <type> + <v> Params = #'DHParameter'{} | {namedCurve, oid()} | #'ECParameters'{} </v> + </type> + <desc> + <p>Generates a new keypair</p> + </desc> + </func> + + <func> <name>pem_decode(PemBin) -> [pem_entry()]</name> <fsummary>Decode PEM binary data and return entries as ASN.1 DER encoded entities. </fsummary> @@ -528,8 +559,8 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | <d>The msg is either the binary "plain text" data to be signed or it is the hashed value of "plain text" i.e. the digest.</d> - <v>DigestType = rsa_digest_type() | dss_digest_type()</v> - <v>Key = rsa_private_key() | dsa_private_key()</v> + <v>DigestType = rsa_digest_type() | dss_digest_type() | ecdsa_digest_type()</v> + <v>Key = rsa_private_key() | dsa_private_key() | ec_private_key()</v> </type> <desc> <p> Creates a digital signature.</p> @@ -592,12 +623,12 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | <v>Msg = binary() | {digest,binary()}</v> <d>The msg is either the binary "plain text" data or it is the hashed value of "plain text" i.e. the digest.</d> - <v>DigestType = rsa_digest_type() | dss_digest_type()</v> + <v>DigestType = rsa_digest_type() | dss_digest_type() | ecdsa_digest_type()</v> <v>Signature = binary()</v> - <v>Key = rsa_public_key() | dsa_public_key()</v> - </type> - <desc> - <p>Verifies a digital signature</p> + <v>Key = rsa_public_key() | dsa_public_key() | ec_public_key()</v> + </type> + <desc> + <p>Verifies a digital signature</p> </desc> </func> diff --git a/lib/public_key/include/public_key.hrl b/lib/public_key/include/public_key.hrl index 4d1d510f29..1e882e76ee 100644 --- a/lib/public_key/include/public_key.hrl +++ b/lib/public_key/include/public_key.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2012. All Rights Reserved. +%% Copyright Ericsson AB 2008-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -72,6 +72,10 @@ valid_ext }). +-record('ECPoint', { + point + }). + -define(unspecified, 0). -define(keyCompromise, 1). @@ -89,6 +93,8 @@ -type rsa_private_key() :: #'RSAPrivateKey'{}. -type dsa_private_key() :: #'DSAPrivateKey'{}. -type dsa_public_key() :: {integer(), #'Dss-Parms'{}}. +-type ec_public_key() :: {#'ECPoint'{},{namedCurve, Oid::tuple()} | #'ECParameters'{}}. +-type ec_private_key() :: #'ECPrivateKey'{}. -type der_encoded() :: binary(). -type decrypt_der() :: binary(). -type pki_asn1_type() :: 'Certificate' | 'RSAPrivateKey' | 'RSAPublicKey' diff --git a/lib/public_key/src/pubkey_cert_records.erl b/lib/public_key/src/pubkey_cert_records.erl index 98004c71a3..0449129809 100644 --- a/lib/public_key/src/pubkey_cert_records.erl +++ b/lib/public_key/src/pubkey_cert_records.erl @@ -23,7 +23,8 @@ -include("public_key.hrl"). --export([decode_cert/1, transform/2, supportedPublicKeyAlgorithms/1]). +-export([decode_cert/1, transform/2, supportedPublicKeyAlgorithms/1, + supportedCurvesTypes/1, namedCurves/1]). %%==================================================================== %% Internal application API @@ -101,6 +102,77 @@ supportedPublicKeyAlgorithms(?'dhpublicnumber') -> 'DHPublicKey'; supportedPublicKeyAlgorithms(?'id-keyExchangeAlgorithm') -> 'KEA-PublicKey'; supportedPublicKeyAlgorithms(?'id-ecPublicKey') -> 'ECPoint'. +supportedCurvesTypes(?'characteristic-two-field') -> characteristic_two_field; +supportedCurvesTypes(?'prime-field') -> prime_field. + +namedCurves(?'sect571r1') -> sect571r1; +namedCurves(?'sect571k1') -> sect571k1; +namedCurves(?'sect409r1') -> sect409r1; +namedCurves(?'sect409k1') -> sect409k1; +namedCurves(?'secp521r1') -> secp521r1; +namedCurves(?'secp384r1') -> secp384r1; +namedCurves(?'secp224r1') -> secp224r1; +namedCurves(?'secp224k1') -> secp224k1; +namedCurves(?'secp192k1') -> secp192k1; +namedCurves(?'secp160r2') -> secp160r2; +namedCurves(?'secp128r2') -> secp128r2; +namedCurves(?'secp128r1') -> secp128r1; +namedCurves(?'sect233r1') -> sect233r1; +namedCurves(?'sect233k1') -> sect233k1; +namedCurves(?'sect193r2') -> sect193r2; +namedCurves(?'sect193r1') -> sect193r1; +namedCurves(?'sect131r2') -> sect131r2; +namedCurves(?'sect131r1') -> sect131r1; +namedCurves(?'sect283r1') -> sect283r1; +namedCurves(?'sect283k1') -> sect283k1; +namedCurves(?'sect163r2') -> sect163r2; +namedCurves(?'secp256k1') -> secp256k1; +namedCurves(?'secp160k1') -> secp160k1; +namedCurves(?'secp160r1') -> secp160r1; +namedCurves(?'secp112r2') -> secp112r2; +namedCurves(?'secp112r1') -> secp112r1; +namedCurves(?'sect113r2') -> sect113r2; +namedCurves(?'sect113r1') -> sect113r1; +namedCurves(?'sect239k1') -> sect239k1; +namedCurves(?'sect163r1') -> sect163r1; +namedCurves(?'sect163k1') -> sect163k1; +namedCurves(?'secp256r1') -> secp256r1; +namedCurves(?'secp192r1') -> secp192r1; + +namedCurves(sect571r1) -> ?'sect571r1'; +namedCurves(sect571k1) -> ?'sect571k1'; +namedCurves(sect409r1) -> ?'sect409r1'; +namedCurves(sect409k1) -> ?'sect409k1'; +namedCurves(secp521r1) -> ?'secp521r1'; +namedCurves(secp384r1) -> ?'secp384r1'; +namedCurves(secp224r1) -> ?'secp224r1'; +namedCurves(secp224k1) -> ?'secp224k1'; +namedCurves(secp192k1) -> ?'secp192k1'; +namedCurves(secp160r2) -> ?'secp160r2'; +namedCurves(secp128r2) -> ?'secp128r2'; +namedCurves(secp128r1) -> ?'secp128r1'; +namedCurves(sect233r1) -> ?'sect233r1'; +namedCurves(sect233k1) -> ?'sect233k1'; +namedCurves(sect193r2) -> ?'sect193r2'; +namedCurves(sect193r1) -> ?'sect193r1'; +namedCurves(sect131r2) -> ?'sect131r2'; +namedCurves(sect131r1) -> ?'sect131r1'; +namedCurves(sect283r1) -> ?'sect283r1'; +namedCurves(sect283k1) -> ?'sect283k1'; +namedCurves(sect163r2) -> ?'sect163r2'; +namedCurves(secp256k1) -> ?'secp256k1'; +namedCurves(secp160k1) -> ?'secp160k1'; +namedCurves(secp160r1) -> ?'secp160r1'; +namedCurves(secp112r2) -> ?'secp112r2'; +namedCurves(secp112r1) -> ?'secp112r1'; +namedCurves(sect113r2) -> ?'sect113r2'; +namedCurves(sect113r1) -> ?'sect113r1'; +namedCurves(sect239k1) -> ?'sect239k1'; +namedCurves(sect163r1) -> ?'sect163r1'; +namedCurves(sect163k1) -> ?'sect163k1'; +namedCurves(secp256r1) -> ?'secp256r1'; +namedCurves(secp192r1) -> ?'secp192r1'. + %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- @@ -111,14 +183,24 @@ decode_supportedPublicKey(#'OTPSubjectPublicKeyInfo'{algorithm= PA = #'PublicKeyAlgorithm'{algorithm=Algo}, subjectPublicKey = {0,SPK0}}) -> Type = supportedPublicKeyAlgorithms(Algo), - {ok, SPK} = 'OTP-PUB-KEY':decode(Type, SPK0), + SPK = case Type of + 'ECPoint' -> #'ECPoint'{point = SPK0}; + _ -> {ok, SPK1} = 'OTP-PUB-KEY':decode(Type, SPK0), + SPK1 + end, #'OTPSubjectPublicKeyInfo'{subjectPublicKey = SPK, algorithm=PA}. encode_supportedPublicKey(#'OTPSubjectPublicKeyInfo'{algorithm= PA = #'PublicKeyAlgorithm'{algorithm=Algo}, subjectPublicKey = SPK0}) -> Type = supportedPublicKeyAlgorithms(Algo), - {ok, SPK} = 'OTP-PUB-KEY':encode(Type, SPK0), + SPK = case Type of + 'ECPoint' -> + SPK0#'ECPoint'.point; + _ -> + {ok, SPK1} = 'OTP-PUB-KEY':encode(Type, SPK0), + SPK1 + end, #'OTPSubjectPublicKeyInfo'{subjectPublicKey = {0,SPK}, algorithm=PA}. %%% Extensions diff --git a/lib/public_key/src/pubkey_pem.erl b/lib/public_key/src/pubkey_pem.erl index 6bdc35fb79..746d142ec3 100644 --- a/lib/public_key/src/pubkey_pem.erl +++ b/lib/public_key/src/pubkey_pem.erl @@ -202,7 +202,11 @@ pem_start('CertificationRequest') -> pem_start('ContentInfo') -> <<"-----BEGIN PKCS7-----">>; pem_start('CertificateList') -> - <<"-----BEGIN X509 CRL-----">>. + <<"-----BEGIN X509 CRL-----">>; +pem_start('OTPEcpkParameters') -> + <<"-----BEGIN EC PARAMETERS-----">>; +pem_start('ECPrivateKey') -> + <<"-----BEGIN EC PRIVATE KEY-----">>. pem_end(<<"-----BEGIN CERTIFICATE-----">>) -> <<"-----END CERTIFICATE-----">>; @@ -226,6 +230,10 @@ pem_end(<<"-----BEGIN PKCS7-----">>) -> <<"-----END PKCS7-----">>; pem_end(<<"-----BEGIN X509 CRL-----">>) -> <<"-----END X509 CRL-----">>; +pem_end(<<"-----BEGIN EC PARAMETERS-----">>) -> + <<"-----END EC PARAMETERS-----">>; +pem_end(<<"-----BEGIN EC PRIVATE KEY-----">>) -> + <<"-----END EC PRIVATE KEY-----">>; pem_end(_) -> undefined. @@ -250,7 +258,11 @@ asn1_type(<<"-----BEGIN CERTIFICATE REQUEST-----">>) -> asn1_type(<<"-----BEGIN PKCS7-----">>) -> 'ContentInfo'; asn1_type(<<"-----BEGIN X509 CRL-----">>) -> - 'CertificateList'. + 'CertificateList'; +asn1_type(<<"-----BEGIN EC PARAMETERS-----">>) -> + 'OTPEcpkParameters'; +asn1_type(<<"-----BEGIN EC PRIVATE KEY-----">>) -> + 'ECPrivateKey'. pem_decrypt() -> <<"Proc-Type: 4,ENCRYPTED">>. diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl index 736c18cdd4..648dba3d5a 100644 --- a/lib/public_key/src/public_key.erl +++ b/lib/public_key/src/public_key.erl @@ -35,6 +35,8 @@ encrypt_public/2, encrypt_public/3, decrypt_public/2, decrypt_public/3, sign/3, verify/4, + generate_key/1, + compute_key/2, compute_key/3, pkix_sign/2, pkix_verify/2, pkix_sign_types/1, pkix_is_self_signed/1, @@ -52,6 +54,7 @@ -type public_crypt_options() :: [{rsa_pad, rsa_padding()}]. -type rsa_digest_type() :: 'md5' | 'sha'| 'sha224' | 'sha256' | 'sha384' | 'sha512'. -type dss_digest_type() :: 'none' | 'sha'. %% None is for backwards compatibility +-type ecdsa_digest_type() :: 'sha'| 'sha224' | 'sha256' | 'sha384' | 'sha512'. -type crl_reason() :: unspecified | keyCompromise | cACompromise | affiliationChanged | superseded | cessationOfOperation | certificateHold | privilegeWithdrawn | aACompromise. -type oid() :: tuple(). @@ -94,7 +97,9 @@ pem_entry_decode({'SubjectPublicKeyInfo', Der, _}) -> der_decode(KeyType, Key0); 'DSAPublicKey' -> {params, DssParams} = der_decode('DSAParams', Params), - {der_decode(KeyType, Key0), DssParams} + {der_decode(KeyType, Key0), DssParams}; + 'ECPoint' -> + der_decode(KeyType, Key0) end; pem_entry_decode({Asn1Type, Der, not_encrypted}) when is_atom(Asn1Type), is_binary(Der) -> @@ -251,10 +256,9 @@ decrypt_private(CipherText, privateExponent = D} = Key, Options) when is_binary(CipherText), - is_integer(N), is_integer(E), is_integer(D), is_list(Options) -> Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_padding), - crypto:rsa_private_decrypt(CipherText, format_rsa_private_key(Key), Padding). + crypto:private_decrypt(rsa, CipherText, format_rsa_private_key(Key), Padding). %%-------------------------------------------------------------------- -spec decrypt_public(CipherText :: binary(), rsa_public_key() | rsa_private_key()) -> @@ -318,30 +322,41 @@ encrypt_private(PlainText, Options) when is_binary(PlainText), is_integer(N), is_integer(E), is_integer(D), - is_list(Options) -> + is_list(Options) -> Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_padding), - crypto:rsa_private_encrypt(PlainText, format_rsa_private_key(Key), Padding). + crypto:private_encrypt(rsa, PlainText, format_rsa_private_key(Key), Padding). +%%-------------------------------------------------------------------- +-spec generate_key(#'DHParameter'{} | {namedCurve, Name ::atom()} | + #'ECParameters'{}) -> {Public::binary(), Private::binary()} | + #'ECPrivateKey'{}. +%% Description: Generates a new keypair +%%-------------------------------------------------------------------- +generate_key(#'DHParameter'{prime = P, base = G}) -> + crypto:generate_key(dh, [P, G]); +generate_key({namedCurve, _} = Params) -> + ec_generate_key(Params); +generate_key(#'ECParameters'{} = Params) -> + ec_generate_key(Params). -format_rsa_private_key(#'RSAPrivateKey'{modulus = N, publicExponent = E, - privateExponent = D, - prime1 = P1, prime2 = P2, - exponent1 = E1, exponent2 = E2, - coefficient = C}) - when is_integer(P1), is_integer(P2), - is_integer(E1), is_integer(E2), is_integer(C) -> - [crypto:mpint(K) || K <- [E, N, D, P1, P2, E1, E2, C]]; +%%-------------------------------------------------------------------- +-spec compute_key(#'ECPoint'{} , #'ECPrivateKey'{}) -> binary(). +-spec compute_key(OthersKey ::binary(), MyKey::binary(), #'DHParameter'{}) -> binary(). +%% Description: Compute shared secret +%%-------------------------------------------------------------------- +compute_key(#'ECPoint'{point = Point}, #'ECPrivateKey'{privateKey = PrivKey, + parameters = Param}) -> + ECCurve = ec_curve_spec(Param), + crypto:compute_key(ecdh, Point, list2int(PrivKey), ECCurve). -format_rsa_private_key(#'RSAPrivateKey'{modulus = N, publicExponent = E, - privateExponent = D}) -> - [crypto:mpint(K) || K <- [E, N, D]]. +compute_key(PubKey, PrivKey, #'DHParameter'{prime = P, base = G}) -> + crypto:compute_key(dh, PubKey, PrivKey, [P, G]). %%-------------------------------------------------------------------- - -spec pkix_sign_types(SignatureAlg::oid()) -> %% Relevant dsa digest type is subpart of rsa digest type { DigestType :: rsa_digest_type(), - SignatureType :: rsa | dsa + SignatureType :: rsa | dsa | ecdsa }. %% Description: %%-------------------------------------------------------------------- @@ -362,68 +377,60 @@ pkix_sign_types(?md5WithRSAEncryption) -> pkix_sign_types(?'id-dsa-with-sha1') -> {sha, dsa}; pkix_sign_types(?'id-dsaWithSHA1') -> - {sha, dsa}. + {sha, dsa}; +pkix_sign_types(?'ecdsa-with-SHA1') -> + {sha, ecdsa}; +pkix_sign_types(?'ecdsa-with-SHA256') -> + {sha256, ecdsa}; +pkix_sign_types(?'ecdsa-with-SHA384') -> + {sha384, ecdsa}; +pkix_sign_types(?'ecdsa-with-SHA512') -> + {sha512, ecdsa}. %%-------------------------------------------------------------------- --spec sign(binary() | {digest, binary()}, rsa_digest_type() | dss_digest_type(), +-spec sign(binary() | {digest, binary()}, rsa_digest_type() | dss_digest_type() | ecdsa_digest_type(), rsa_private_key() | - dsa_private_key()) -> Signature :: binary(). + dsa_private_key() | ec_private_key()) -> Signature :: binary(). %% Description: Create digital signature. %%-------------------------------------------------------------------- -sign({digest,_}=Digest, DigestType, Key = #'RSAPrivateKey'{}) -> - crypto:rsa_sign(DigestType, Digest, format_rsa_private_key(Key)); +sign(DigestOrPlainText, DigestType, Key = #'RSAPrivateKey'{}) -> + crypto:sign(rsa, DigestType, DigestOrPlainText, format_rsa_private_key(Key)); -sign(PlainText, DigestType, Key = #'RSAPrivateKey'{}) -> - crypto:rsa_sign(DigestType, sized_binary(PlainText), format_rsa_private_key(Key)); +sign(DigestOrPlainText, sha, #'DSAPrivateKey'{p = P, q = Q, g = G, x = X}) -> + crypto:sign(dss, sha, DigestOrPlainText, [P, Q, G, X]); -sign({digest,_}=Digest, sha, #'DSAPrivateKey'{p = P, q = Q, g = G, x = X}) -> - crypto:dss_sign(Digest, - [crypto:mpint(P), crypto:mpint(Q), - crypto:mpint(G), crypto:mpint(X)]); - -sign(PlainText, sha, #'DSAPrivateKey'{p = P, q = Q, g = G, x = X}) -> - crypto:dss_sign(sized_binary(PlainText), - [crypto:mpint(P), crypto:mpint(Q), - crypto:mpint(G), crypto:mpint(X)]); +sign(DigestOrPlainText, DigestType, #'ECPrivateKey'{privateKey = PrivKey, + parameters = Param}) -> + ECCurve = ec_curve_spec(Param), + crypto:sign(ecdsa, DigestType, DigestOrPlainText, [list2int(PrivKey), ECCurve]); %% Backwards compatible sign(Digest, none, #'DSAPrivateKey'{} = Key) -> sign({digest,Digest}, sha, Key). %%-------------------------------------------------------------------- --spec verify(binary() | {digest, binary()}, rsa_digest_type() | dss_digest_type(), +-spec verify(binary() | {digest, binary()}, rsa_digest_type() | dss_digest_type() | ecdsa_digest_type(), Signature :: binary(), rsa_public_key() - | dsa_public_key()) -> boolean(). + | dsa_public_key() | ec_public_key()) -> boolean(). %% Description: Verifies a digital signature. %%-------------------------------------------------------------------- -verify({digest,_}=Digest, DigestType, Signature, +verify(DigestOrPlainText, DigestType, Signature, #'RSAPublicKey'{modulus = Mod, publicExponent = Exp}) -> - crypto:rsa_verify(DigestType, Digest, - sized_binary(Signature), - [crypto:mpint(Exp), crypto:mpint(Mod)]); + crypto:verify(rsa, DigestType, DigestOrPlainText, Signature, + [Exp, Mod]); -verify(PlainText, DigestType, Signature, - #'RSAPublicKey'{modulus = Mod, publicExponent = Exp}) -> - crypto:rsa_verify(DigestType, - sized_binary(PlainText), - sized_binary(Signature), - [crypto:mpint(Exp), crypto:mpint(Mod)]); +verify(DigestOrPlaintext, DigestType, Signature, {#'ECPoint'{point = Point}, Param}) -> + ECCurve = ec_curve_spec(Param), + crypto:verify(ecdsa, DigestType, DigestOrPlaintext, Signature, [Point, ECCurve]); -verify({digest,_}=Digest, sha, Signature, {Key, #'Dss-Parms'{p = P, q = Q, g = G}}) - when is_integer(Key), is_binary(Signature) -> - crypto:dss_verify(Digest, sized_binary(Signature), - [crypto:mpint(P), crypto:mpint(Q), - crypto:mpint(G), crypto:mpint(Key)]); %% Backwards compatibility verify(Digest, none, Signature, {_, #'Dss-Parms'{}} = Key ) -> verify({digest,Digest}, sha, Signature, Key); -verify(PlainText, sha, Signature, {Key, #'Dss-Parms'{p = P, q = Q, g = G}}) - when is_integer(Key), is_binary(PlainText), is_binary(Signature) -> - crypto:dss_verify(sized_binary(PlainText), - sized_binary(Signature), - [crypto:mpint(P), crypto:mpint(Q), - crypto:mpint(G), crypto:mpint(Key)]). +verify(DigestOrPlainText, sha = DigestType, Signature, {Key, #'Dss-Parms'{p = P, q = Q, g = G}}) + when is_integer(Key), is_binary(Signature) -> + crypto:verify(dss, DigestType, DigestOrPlainText, Signature, [P, Q, G, Key]). + %%-------------------------------------------------------------------- -spec pkix_sign(#'OTPTBSCertificate'{}, rsa_private_key() | dsa_private_key()) -> Der::binary(). @@ -446,7 +453,7 @@ pkix_sign(#'OTPTBSCertificate'{signature = %%-------------------------------------------------------------------- -spec pkix_verify(Cert::binary(), rsa_public_key()| - dsa_public_key()) -> boolean(). + dsa_public_key() | ec_public_key()) -> boolean(). %% %% Description: Verify pkix x.509 certificate signature. %%-------------------------------------------------------------------- @@ -458,7 +465,12 @@ pkix_verify(DerCert, {Key, #'Dss-Parms'{}} = DSAKey) pkix_verify(DerCert, #'RSAPublicKey'{} = RSAKey) when is_binary(DerCert) -> {DigestType, PlainText, Signature} = pubkey_cert:verify_data(DerCert), - verify(PlainText, DigestType, Signature, RSAKey). + verify(PlainText, DigestType, Signature, RSAKey); + +pkix_verify(DerCert, Key = {#'ECPoint'{}, _}) + when is_binary(DerCert) -> + {DigestType, PlainText, Signature} = pubkey_cert:verify_data(DerCert), + verify(PlainText, DigestType, Signature, Key). %%-------------------------------------------------------------------- -spec pkix_is_issuer(Cert :: der_encoded()| #'OTPCertificate'{} | #'CertificateList'{}, @@ -640,13 +652,11 @@ do_pem_entry_decode({Asn1Type,_, _} = PemEntry, Password) -> encrypt_public(PlainText, N, E, Options)-> Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_padding), - crypto:rsa_public_encrypt(PlainText, [crypto:mpint(E),crypto:mpint(N)], - Padding). + crypto:public_encrypt(rsa, PlainText, [E,N], Padding). -decrypt_public(CipherText, N,E, Options) -> +decrypt_public(CipherText, N,E, Options) -> Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_padding), - crypto:rsa_public_decrypt(CipherText,[crypto:mpint(E), crypto:mpint(N)], - Padding). + crypto:public_decrypt(rsa, CipherText,[E, N], Padding). path_validation([], #path_validation_state{working_public_key_algorithm = Algorithm, @@ -732,10 +742,6 @@ validate(Cert, #path_validation_state{working_issuer_name = Issuer, pubkey_cert:prepare_for_next_cert(OtpCert, ValidationState). -sized_binary(Binary) -> - Size = size(Binary), - <<?UINT32(Size), Binary/binary>>. - otp_cert(Der) when is_binary(Der) -> pkix_decode_cert(Der, otp); otp_cert(#'OTPCertificate'{} =Cert) -> @@ -842,3 +848,46 @@ combine(CRL, DeltaCRLs) -> end, lists:foldl(Fun, hd(Deltas), tl(Deltas)) end. + +format_rsa_private_key(#'RSAPrivateKey'{modulus = N, publicExponent = E, + privateExponent = D, + prime1 = P1, prime2 = P2, + exponent1 = E1, exponent2 = E2, + coefficient = C}) + when is_integer(N), is_integer(E), is_integer(D), + is_integer(P1), is_integer(P2), + is_integer(E1), is_integer(E2), is_integer(C) -> + [E, N, D, P1, P2, E1, E2, C]; + +format_rsa_private_key(#'RSAPrivateKey'{modulus = N, publicExponent = E, + privateExponent = D}) when is_integer(N), + is_integer(E), + is_integer(D) -> + [E, N, D]. + +ec_generate_key(Params) -> + Curve = ec_curve_spec(Params), + Term = crypto:generate_key(ecdh, Curve), + ec_key(Term, Params). + +ec_curve_spec( #'ECParameters'{fieldID = FieldId, curve = PCurve, base = Base, order = Order, cofactor = CoFactor }) -> + Field = {pubkey_cert_records:supportedCurvesTypes(FieldId#'FieldID'.fieldType), + FieldId#'FieldID'.parameters}, + Curve = {erlang:list_to_binary(PCurve#'Curve'.a), erlang:list_to_binary(PCurve#'Curve'.b), none}, + {Field, Curve, erlang:list_to_binary(Base), Order, CoFactor}; +ec_curve_spec({namedCurve, OID}) -> + pubkey_cert_records:namedCurves(OID). + +ec_key({PrivateKey, PubKey}, Params) -> + #'ECPrivateKey'{version = 1, + privateKey = int2list(PrivateKey), + parameters = Params, + publicKey = {0, PubKey}}. + +list2int(L) -> + S = length(L) * 8, + <<R:S/integer>> = erlang:iolist_to_binary(L), + R. +int2list(I) -> + L = (length(integer_to_list(I, 16)) + 1) div 2, + binary_to_list(<<I:(L*8)>>). diff --git a/lib/public_key/test/erl_make_certs.erl b/lib/public_key/test/erl_make_certs.erl index 95e288cd71..14efbcc7e0 100644 --- a/lib/public_key/test/erl_make_certs.erl +++ b/lib/public_key/test/erl_make_certs.erl @@ -45,7 +45,7 @@ %% {dnQualifer, DnQ} %% issuer = {Issuer, IssuerKey} true (i.e. a ca cert is created) %% (obs IssuerKey migth be {Key, Password} -%% key = KeyFile|KeyBin|rsa|dsa Subject PublicKey rsa or dsa generates key +%% key = KeyFile|KeyBin|rsa|dsa|ec Subject PublicKey rsa, dsa or ec generates key %% %% %% (OBS: The generated keys are for testing only) @@ -91,6 +91,16 @@ gen_dsa(LSize,NSize) when is_integer(LSize), is_integer(NSize) -> {Key, encode_key(Key)}. %%-------------------------------------------------------------------- +%% @doc Creates a ec key (OBS: for testing only) +%% the sizes are in bytes +%% @spec (::integer()) -> {::atom(), ::binary(), ::opaque()} +%% @end +%%-------------------------------------------------------------------- +gen_ec(Curve) when is_atom(Curve) -> + Key = gen_ec2(Curve), + {Key, encode_key(Key)}. + +%%-------------------------------------------------------------------- %% @doc Verifies cert signatures %% @spec (::binary(), ::tuple()) -> ::boolean() %% @end @@ -102,7 +112,10 @@ verify_signature(DerEncodedCert, DerKey, _KeyParams) -> public_key:pkix_verify(DerEncodedCert, #'RSAPublicKey'{modulus=Mod, publicExponent=Exp}); #'DSAPrivateKey'{p=P, q=Q, g=G, y=Y} -> - public_key:pkix_verify(DerEncodedCert, {Y, #'Dss-Parms'{p=P, q=Q, g=G}}) + public_key:pkix_verify(DerEncodedCert, {Y, #'Dss-Parms'{p=P, q=Q, g=G}}); + #'ECPrivateKey'{version = _Version, privateKey = _PrivKey, + parameters = _Params, publicKey = _PubKey} -> + public_key:pkix_verify(DerEncodedCert, Key) end. %%%%%%%%%%%%%%%%%%%%%%%%% Implementation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -112,6 +125,7 @@ get_key(Opts) -> undefined -> make_key(rsa, Opts); rsa -> make_key(rsa, Opts); dsa -> make_key(dsa, Opts); + ec -> make_key(ec, Opts); Key -> Password = proplists:get_value(password, Opts, no_passwd), decode_key(Key, Password) @@ -129,6 +143,8 @@ decode_key(#'RSAPrivateKey'{} = Key,_) -> Key; decode_key(#'DSAPrivateKey'{} = Key,_) -> Key; +decode_key(#'ECPrivateKey'{} = Key,_) -> + Key; decode_key(PemEntry = {_,_,_}, Pw) -> public_key:pem_entry_decode(PemEntry, Pw); decode_key(PemBin, Pw) -> @@ -140,7 +156,10 @@ encode_key(Key = #'RSAPrivateKey'{}) -> {'RSAPrivateKey', Der, not_encrypted}; encode_key(Key = #'DSAPrivateKey'{}) -> {ok, Der} = 'OTP-PUB-KEY':encode('DSAPrivateKey', Key), - {'DSAPrivateKey', Der, not_encrypted}. + {'DSAPrivateKey', Der, not_encrypted}; +encode_key(Key = #'ECPrivateKey'{}) -> + {ok, Der} = 'OTP-PUB-KEY':encode('ECPrivateKey', Key), + {'ECPrivateKey', Der, not_encrypted}. make_tbs(SubjectKey, Opts) -> Version = list_to_atom("v"++integer_to_list(proplists:get_value(version, Opts, 3))), @@ -282,7 +301,14 @@ publickey(#'RSAPrivateKey'{modulus=N, publicExponent=E}) -> publickey(#'DSAPrivateKey'{p=P, q=Q, g=G, y=Y}) -> Algo = #'PublicKeyAlgorithm'{algorithm= ?'id-dsa', parameters={params, #'Dss-Parms'{p=P, q=Q, g=G}}}, - #'OTPSubjectPublicKeyInfo'{algorithm = Algo, subjectPublicKey = Y}. + #'OTPSubjectPublicKeyInfo'{algorithm = Algo, subjectPublicKey = Y}; +publickey(#'ECPrivateKey'{version = _Version, + privateKey = _PrivKey, + parameters = Params, + publicKey = {0, PubKey}}) -> + Algo = #'PublicKeyAlgorithm'{algorithm= ?'id-ecPublicKey', parameters=Params}, + #'OTPSubjectPublicKeyInfo'{algorithm = Algo, + subjectPublicKey = #'ECPoint'{point = PubKey}}. validity(Opts) -> DefFrom0 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())-1), @@ -303,13 +329,24 @@ sign_algorithm(#'RSAPrivateKey'{}, Opts) -> end, {Type, 'NULL'}; sign_algorithm(#'DSAPrivateKey'{p=P, q=Q, g=G}, _Opts) -> - {?'id-dsa-with-sha1', {params,#'Dss-Parms'{p=P, q=Q, g=G}}}. + {?'id-dsa-with-sha1', {params,#'Dss-Parms'{p=P, q=Q, g=G}}}; +sign_algorithm(#'ECPrivateKey'{}, Opts) -> + Type = case proplists:get_value(digest, Opts, sha1) of + sha1 -> ?'ecdsa-with-SHA1'; + sha512 -> ?'ecdsa-with-SHA512'; + sha384 -> ?'ecdsa-with-SHA384'; + sha256 -> ?'ecdsa-with-SHA256' + end, + {Type, 'NULL'}. make_key(rsa, _Opts) -> %% (OBS: for testing only) gen_rsa2(64); make_key(dsa, _Opts) -> - gen_dsa2(128, 20). %% Bytes i.e. {1024, 160} + gen_dsa2(128, 20); %% Bytes i.e. {1024, 160} +make_key(ec, _Opts) -> + %% (OBS: for testing only) + gen_ec2(secp256k1). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% RSA key generation (OBS: for testing only) @@ -354,13 +391,13 @@ gen_dsa2(LSize, NSize) -> X0 = prime(LSize), P0 = prime((LSize div 2) +1), - %% Choose L-bit prime modulus P such that p–1 is a multiple of q. + %% Choose L-bit prime modulus P such that p-1 is a multiple of q. case dsa_search(X0 div (2*Q*P0), P0, Q, 1000) of error -> gen_dsa2(LSize, NSize); P -> G = crypto:mod_exp(2, (P-1) div Q, P), % Choose G a number whose multiplicative order modulo p is q. - %% such that This may be done by setting g = h^(p–1)/q mod p, commonly h=2 is used. + %% such that This may be done by setting g = h^(p-1)/q mod p, commonly h=2 is used. X = prime(20), %% Choose x by some random method, where 0 < x < q. Y = crypto:mod_exp(G, X, P), %% Calculate y = g^x mod p. @@ -368,6 +405,24 @@ gen_dsa2(LSize, NSize) -> #'DSAPrivateKey'{version=0, p=P, q=Q, g=G, y=Y, x=X} end. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EC key generation (OBS: for testing only) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +int2list(I) -> + L = (length(integer_to_list(I, 16)) + 1) div 2, + binary_to_list(<<I:(L*8)>>). + +gen_ec2(CurveId) -> + Key = crypto:ec_key_new(CurveId), + crypto:ec_key_generate(Key), + {_Curve, PrivKey, PubKey} = crypto:ec_key_to_term(Key), + + #'ECPrivateKey'{version = 1, + privateKey = int2list(PrivKey), + parameters = {namedCurve, pubkey_cert_records:namedCurves(CurveId)}, + publicKey = {0, PubKey}}. + %% See fips_186-3.pdf dsa_search(T, P0, Q, Iter) when Iter > 0 -> P = 2*T*Q*P0 + 1, diff --git a/lib/public_key/test/pkits_SUITE.erl b/lib/public_key/test/pkits_SUITE.erl index d901adaadd..8cdf0aaae3 100644 --- a/lib/public_key/test/pkits_SUITE.erl +++ b/lib/public_key/test/pkits_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2012. All Rights Reserved. +%% Copyright Ericsson AB 2008-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -758,11 +758,10 @@ warning(Format, Args, File0, Line) -> io:format("~s(~p): Warning "++Format, [File,Line|Args]). crypto_support_check(Config) -> - try crypto:sha256(<<"Test">>) of - _ -> - Config - catch error:notsup -> - crypto:stop(), + case proplists:get_bool(sha256, crypto:algorithms()) of + true -> + Config; + false -> {skip, "To old version of openssl"} end. diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl index 0de80edeac..5a64140c67 100644 --- a/lib/public_key/test/public_key_SUITE.erl +++ b/lib/public_key/test/public_key_SUITE.erl @@ -551,7 +551,7 @@ dsa_sign_verify(Config) when is_list(Config) -> false = public_key:verify(Msg, sha, <<1:8, DSASign/binary>>, {DSAPublicKey, DSAParams}), - Digest = crypto:sha(Msg), + Digest = crypto:hash(sha,Msg), DigestSign = public_key:sign(Digest, none, DSAPrivateKey), true = public_key:verify(Digest, none, DigestSign, {DSAPublicKey, DSAParams}), <<_:8, RestDigest/binary>> = Digest, diff --git a/lib/reltool/src/reltool_utils.erl b/lib/reltool/src/reltool_utils.erl index edccb889b1..9af8f6bae8 100644 --- a/lib/reltool/src/reltool_utils.erl +++ b/lib/reltool/src/reltool_utils.erl @@ -572,7 +572,7 @@ copy_file(From, To) -> FromMode = FromInfo#file_info.mode, ToMode = ToInfo#file_info.mode, ToMode2 = FromMode bor ToMode, - FileInfo = FromInfo#file_info{mode = ToMode2}, + FileInfo = ToInfo#file_info{mode = ToMode2}, write_file_info(To, FileInfo), ok; {error, Reason} -> diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl index b1e1787f18..752037042d 100644 --- a/lib/reltool/test/reltool_server_SUITE.erl +++ b/lib/reltool/test/reltool_server_SUITE.erl @@ -2595,8 +2595,8 @@ start_node(Name, ErlPath, Args0) -> %io:format("open_port({spawn_executable, ~p}, [{args,~p}])~n",[ErlPath,Args]), case open_port({spawn_executable, ErlPath}, [{args,Args}]) of Port when is_port(Port) -> - unlink(Port), - erlang:port_close(Port), + %% no need to close port since node is detached (see + %% mk_node_args) so port will be closed anyway. case ping_node(FullName, 50) of ok -> {ok, FullName}; Other -> exit({failed_to_start_node, FullName, Other}) @@ -2629,7 +2629,7 @@ mk_node_args(Name, Args) -> end, {ok, Pwd} = file:get_cwd(), NameStr = atom_to_list(Name), - ["-detached", "-noinput", + ["-detached", NameSw, NameStr, "-pa", Pa, "-env", "ERL_CRASH_DUMP", Pwd ++ "/erl_crash_dump." ++ NameStr, diff --git a/lib/runtime_tools/src/dbg.erl b/lib/runtime_tools/src/dbg.erl index 6e3bfe31c6..6b2fb0460f 100644 --- a/lib/runtime_tools/src/dbg.erl +++ b/lib/runtime_tools/src/dbg.erl @@ -551,8 +551,7 @@ c(M, F, A, Flags) -> stop_clear(), {error, Reason}; {Pid, Res} -> - erlang:demonitor(Mref), - receive {'DOWN', Mref, _, _, _} -> ok after 0 -> ok end, + erlang:demonitor(Mref, [flush]), %% 'sleep' prevents the tracer (recv_all_traces) from %% receiving garbage {'EXIT',...} when dbg i stopped. timer:sleep(1), @@ -592,8 +591,7 @@ req(R) -> {'DOWN', Mref, _, _, _} -> % If server died exit(dbg_server_crash); {dbg, Reply} -> - erlang:demonitor(Mref), - receive {'DOWN', Mref, _, _, _} -> ok after 0 -> ok end, + erlang:demonitor(Mref, [flush]), Reply end. diff --git a/lib/runtime_tools/src/ttb_autostart.erl b/lib/runtime_tools/src/ttb_autostart.erl index 4c6971c119..5339507cec 100644 --- a/lib/runtime_tools/src/ttb_autostart.erl +++ b/lib/runtime_tools/src/ttb_autostart.erl @@ -1,3 +1,4 @@ +%%%-*- coding: utf-8 -*- %%%------------------------------------------------------------------- %%% File : ttb_autostart.erl %%% Author : Bartłomiej Puzoń <[email protected]> diff --git a/lib/sasl/test/release_handler_SUITE.erl b/lib/sasl/test/release_handler_SUITE.erl index 97ba70c9bd..a56924d5ca 100644 --- a/lib/sasl/test/release_handler_SUITE.erl +++ b/lib/sasl/test/release_handler_SUITE.erl @@ -1112,10 +1112,10 @@ otp_9395_update_many_mods(Conf) when is_list(Conf) -> true = rpc:call(Node,erlang,check_old_code,[m10]), %% Run check_install_release with purge before install this time - {TCheck,{ok, _RelVsn1, []}} = + {_TCheck,{ok, _RelVsn1, []}} = timer:tc(rpc,call,[Node, release_handler, check_install_release, [RelVsn2,[purge]]]), -% ct:log("check_install_release with purge: ~.2f",[TCheck/1000000]), +% ct:log("check_install_release with purge: ~.2f",[_TCheck/1000000]), %% Finally install release after check and purge, and check that %% this install was faster than the first. @@ -1209,10 +1209,10 @@ otp_9395_rm_many_mods(Conf) when is_list(Conf) -> true = rpc:call(Node,erlang,check_old_code,[m10]), %% Run check_install_release with purge before install this time - {TCheck,{ok, _RelVsn1, []}} = + {_TCheck,{ok, _RelVsn1, []}} = timer:tc(rpc,call,[Node, release_handler, check_install_release, [RelVsn2,[purge]]]), -% ct:log("check_install_release with purge: ~.2f",[TCheck/1000000]), +% ct:log("check_install_release with purge: ~.2f",[_TCheck/1000000]), %% Finally install release after check and purge, and check that %% this install was faster than the first. diff --git a/lib/snmp/src/agent/snmpa_target_cache.erl b/lib/snmp/src/agent/snmpa_target_cache.erl index 2aa35aa46a..1fcaf82373 100644 --- a/lib/snmp/src/agent/snmpa_target_cache.erl +++ b/lib/snmp/src/agent/snmpa_target_cache.erl @@ -21,8 +21,6 @@ -behaviour(gen_server). %% External exports -%% Avoid warning for local function demonitor/1 clashing with autoimported BIF. --compile({no_auto_import,[demonitor/1]}). -export([start_link/2, stop/0, verbosity/1]). -export([ @@ -213,21 +211,6 @@ do_init(Prio, Opts) -> %% requests will have to wait. %% -monitor(Pid) -> erlang:monitor(process, Pid). --ifdef(SNMP_R10). -demonitor(Ref) -> - erlang:demonitor(Ref), - receive - {_, Ref, _, _, _} -> - true - after 0 -> - true - end. --else. -demonitor(Ref) -> - erlang:demonitor(Ref, [flush]). --endif. - %% (1) No write_lock active or waiting handle_call({lock, read = Type, infinity}, {Pid, _} = From, @@ -236,7 +219,7 @@ handle_call({lock, read = Type, infinity}, {Pid, _} = From, "entry when no waiting or active writer with" "~n Pid: ~p" "~n Cnt: ~p", [Pid, Cnt]), - MonRef = monitor(Pid), + MonRef = erlang:monitor(process, Pid), Locker = #locker{pid = Pid, from = From, mon_ref = MonRef, @@ -252,7 +235,7 @@ handle_call({lock, read = Type, infinity}, {Pid, _} = From, State) -> ?vlog("lock(read, infinity) -> " "entry when active or waiting write locks with" "~n Pid: ~p", [Pid]), - MonRef = monitor(Pid), + MonRef = erlang:monitor(process, Pid), Locker = #locker{pid = Pid, from = From, mon_ref = MonRef, @@ -273,7 +256,7 @@ handle_call({lock, write = Type, infinity}, {Pid, _} = From, ?vlog("lock(write, infinity) -> " "entry when no active lockers with" "~n Pid: ~p", [Pid]), - MonRef = monitor(Pid), + MonRef = erlang:monitor(process, Pid), Locker = #locker{pid = Pid, from = From, mon_ref = MonRef, @@ -290,7 +273,7 @@ handle_call({lock, write = Type, infinity}, {Pid, _} = From, ?vlog("lock(write, infinity) -> " "entry when active lockers with" "~n Pid: ~p", [Pid]), - MonRef = monitor(Pid), + MonRef = erlang:monitor(process, Pid), Locker = #locker{pid = Pid, from = From, mon_ref = MonRef, @@ -307,7 +290,7 @@ handle_call({lock, write = Type, infinity}, {Pid, _} = From, #state{writer = true} = State) -> ?vlog("lock(write, infinity) -> entry with" "~n Pid: ~p", [Pid]), - MonRef = monitor(Pid), + MonRef = erlang:monitor(process, Pid), Locker = #locker{pid = Pid, from = From, mon_ref = MonRef, @@ -429,7 +412,7 @@ handle_cast({unlock, Pid}, [#locker{mon_ref = MonRef, type = read}] -> ?vdebug("unlock -> found read locker" "~n MonRef: ~p", [MonRef]), - demonitor(MonRef), + erlang:demonitor(MonRef, [flush]), ets:delete(?LOCKER_TAB, Pid), %% ?vtrace("unlock -> done when" %% "~n Lockers: ~p", [ets:tab2list(?LOCKER_TAB)]), @@ -437,7 +420,7 @@ handle_cast({unlock, Pid}, [#locker{mon_ref = MonRef, type = write}] -> ?vdebug("unlock -> found write locker" "~n MonRef: ~p", [MonRef]), - demonitor(MonRef), + erlang:demonitor(MonRef, [flush]), ets:delete(?LOCKER_TAB, Pid), %% ?vtrace("unlock -> done when" %% "~n Lockers: ~p", [ets:tab2list(?LOCKER_TAB)]), @@ -459,7 +442,7 @@ handle_cast({unlock, Pid}, [#locker{mon_ref = MonRef, type = read}] when (Cnt == 1) -> ?vdebug("unlock -> found read locker" "~n MonRef: ~p", [MonRef]), - demonitor(MonRef), + erlang:demonitor(MonRef, [flush]), ets:delete(?LOCKER_TAB, Pid), case active_waiting_writer(Waiting) of {true, StillWaiting} -> @@ -482,7 +465,7 @@ handle_cast({unlock, Pid}, [#locker{mon_ref = MonRef, type = read}] -> ?vdebug("unlock -> found read locker" "~n MonRef: ~p", [MonRef]), - demonitor(MonRef), + erlang:demonitor(MonRef, [flush]), ets:delete(?LOCKER_TAB, Pid), %% ?vtrace("unlock -> done when" %% "~n Lockers: ~p", [ets:tab2list(?LOCKER_TAB)]), @@ -492,7 +475,7 @@ handle_cast({unlock, Pid}, %% Release the hord (maybe) ?vdebug("unlock -> found write locker" "~n MonRef: ~p", [MonRef]), - demonitor(MonRef), + erlang:demonitor(MonRef, [flush]), ets:delete(?LOCKER_TAB, Pid), {Active, StillWaiting, Writer} = activate_waiting_readers_or_maybe_writer(Waiting), diff --git a/lib/ssh/src/ssh_cli.erl b/lib/ssh/src/ssh_cli.erl index 0531ad7830..69b1ab186f 100644 --- a/lib/ssh/src/ssh_cli.erl +++ b/lib/ssh/src/ssh_cli.erl @@ -230,11 +230,11 @@ io_request({window_change, OldTty}, Buf, Tty) -> io_request({put_chars, Cs}, Buf, Tty) -> put_chars(bin_to_list(Cs), Buf, Tty); io_request({put_chars, unicode, Cs}, Buf, Tty) -> - put_chars([Ch || Ch <- unicode:characters_to_list(Cs,unicode), Ch =< 255], Buf, Tty); + put_chars(unicode:characters_to_list(Cs,unicode), Buf, Tty); io_request({insert_chars, Cs}, Buf, Tty) -> insert_chars(bin_to_list(Cs), Buf, Tty); io_request({insert_chars, unicode, Cs}, Buf, Tty) -> - insert_chars([Ch || Ch <- unicode:characters_to_list(Cs,unicode), Ch =< 255], Buf, Tty); + insert_chars(unicode:characters_to_list(Cs,unicode), Buf, Tty); io_request({move_rel, N}, Buf, Tty) -> move_rel(N, Buf, Tty); io_request({delete_chars,N}, Buf, Tty) -> diff --git a/lib/ssh/src/ssh_xfer.erl b/lib/ssh/src/ssh_xfer.erl index 4dfd9ed8b0..93f9e20663 100644 --- a/lib/ssh/src/ssh_xfer.erl +++ b/lib/ssh/src/ssh_xfer.erl @@ -72,7 +72,7 @@ protocol_version_request(XF) -> open(XF, ReqID, FileName, Access, Flags, Attrs) -> Vsn = XF#ssh_xfer.vsn, - FileName1 = list_to_binary(FileName), + FileName1 = unicode:characters_to_binary(FileName), MBits = if Vsn >= 5 -> M = encode_ace_mask(Access), ?uint32(M); @@ -115,7 +115,7 @@ write(XF,ReqID, Handle, Offset, Data) -> is_binary(Data) -> Data; is_list(Data) -> - list_to_binary(Data) + unicode:characters_to_binary(Data) end, xf_request(XF,?SSH_FXP_WRITE, [?uint32(ReqID), @@ -132,8 +132,8 @@ remove(XF, ReqID, File) -> %% Rename a file/directory rename(XF, ReqID, Old, New, Flags) -> Vsn = XF#ssh_xfer.vsn, - OldPath = list_to_binary(Old), - NewPath = list_to_binary(New), + OldPath = unicode:characters_to_binary(Old), + NewPath = unicode:characters_to_binary(New), FlagBits = if Vsn >= 5 -> F0 = encode_rename_flags(Flags), @@ -151,7 +151,7 @@ rename(XF, ReqID, Old, New, Flags) -> %% Create directory mkdir(XF, ReqID, Path, Attrs) -> - Path1 = list_to_binary(Path), + Path1 = unicode:characters_to_binary(Path), xf_request(XF, ?SSH_FXP_MKDIR, [?uint32(ReqID), ?binary(Path1), @@ -159,14 +159,14 @@ mkdir(XF, ReqID, Path, Attrs) -> %% Remove a directory rmdir(XF, ReqID, Dir) -> - Dir1 = list_to_binary(Dir), + Dir1 = unicode:characters_to_binary(Dir), xf_request(XF, ?SSH_FXP_RMDIR, [?uint32(ReqID), ?binary(Dir1)]). %% Stat file stat(XF, ReqID, Path, Flags) -> - Path1 = list_to_binary(Path), + Path1 = unicode:characters_to_binary(Path), Vsn = XF#ssh_xfer.vsn, AttrFlags = if Vsn >= 5 -> F = encode_attr_flags(Vsn, Flags), @@ -182,7 +182,7 @@ stat(XF, ReqID, Path, Flags) -> %% Stat file - follow symbolic links lstat(XF, ReqID, Path, Flags) -> - Path1 = list_to_binary(Path), + Path1 = unicode:characters_to_binary(Path), Vsn = XF#ssh_xfer.vsn, AttrFlags = if Vsn >= 5 -> F = encode_attr_flags(Vsn, Flags), @@ -211,7 +211,7 @@ fstat(XF, ReqID, Handle, Flags) -> %% Modify file attributes setstat(XF, ReqID, Path, Attrs) -> - Path1 = list_to_binary(Path), + Path1 = unicode:characters_to_binary(Path), xf_request(XF, ?SSH_FXP_SETSTAT, [?uint32(ReqID), ?binary(Path1), @@ -227,7 +227,7 @@ fsetstat(XF, ReqID, Handle, Attrs) -> %% Read a symbolic link readlink(XF, ReqID, Path) -> - Path1 = list_to_binary(Path), + Path1 = unicode:characters_to_binary(Path), xf_request(XF, ?SSH_FXP_READLINK, [?uint32(ReqID), ?binary(Path1)]). @@ -235,8 +235,8 @@ readlink(XF, ReqID, Path) -> %% Create a symbolic link symlink(XF, ReqID, LinkPath, TargetPath) -> - LinkPath1 = list_to_binary(LinkPath), - TargetPath1 = list_to_binary(TargetPath), + LinkPath1 = unicode:characters_to_binary(LinkPath), + TargetPath1 = unicode:characters_to_binary(TargetPath), xf_request(XF, ?SSH_FXP_SYMLINK, [?uint32(ReqID), ?binary(LinkPath1), @@ -244,7 +244,7 @@ symlink(XF, ReqID, LinkPath, TargetPath) -> %% Convert a path into a 'canonical' form realpath(XF, ReqID, Path) -> - Path1 = list_to_binary(Path), + Path1 = unicode:characters_to_binary(Path), xf_request(XF, ?SSH_FXP_REALPATH, [?uint32(ReqID), ?binary(Path1)]). diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index d5615fecfc..1645eb15f3 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -37,17 +37,21 @@ <list type="bulleted"> <item>ssl requires the crypto and public_key applications.</item> <item>Supported SSL/TLS-versions are SSL-3.0, TLS-1.0, - TLS-1.1 and TLS-1.2 (no support for elliptic curve cipher suites yet).</item> + TLS-1.1 and TLS-1.2.</item> <item>For security reasons sslv2 is not supported.</item> <item>Ephemeral Diffie-Hellman cipher suites are supported but not Diffie Hellman Certificates cipher suites.</item> + <item>Elliptic Curve cipher suites are supported if crypto + supports it and named curves are used. + </item> <item>Export cipher suites are not supported as the U.S. lifted its export restrictions in early 2000.</item> <item>IDEA cipher suites are not supported as they have become deprecated by the latest TLS spec so there is not any real motivation to implement them.</item> - <item>CRL and policy certificate - extensions are not supported yet. </item> + <item>CRL and policy certificate extensions are not supported + yet. However CRL verification is supported by public_key, only not integrated + in ssl yet. </item> </list> </section> @@ -75,7 +79,7 @@ {fail_if_no_peer_cert, boolean()} {depth, integer()} | {cert, der_encoded()}| {certfile, path()} | - {key, {'RSAPrivateKey'| 'DSAPrivateKey' | 'PrivateKeyInfo', der_encoded()}} | + {key, {'RSAPrivateKey'| 'DSAPrivateKey' | 'ECPrivateKey' |'PrivateKeyInfo', der_encoded()}} | {keyfile, path()} | {password, string()} | {cacerts, [der_encoded()]} | {cacertfile, path()} | |{dh, der_encoded()} | {dhfile, path()} | {ciphers, ciphers()} | @@ -125,6 +129,7 @@ <p><c>key_exchange() = rsa | dhe_dss | dhe_rsa | dh_anon | psk | dhe_psk | rsa_psk | srp_anon | srp_dss | srp_rsa + | ecdh_anon | ecdh_ecdsa | ecdhe_ecdsa | ecdh_rsa | ecdhe_rsa </c></p> <p><c>cipher() = rc4_128 | des_cbc | '3des_ede_cbc' @@ -157,7 +162,7 @@ <tag>{certfile, path()}</tag> <item>Path to a file containing the user's certificate.</item> - <tag>{key, {'RSAPrivateKey'| 'DSAPrivateKey' | 'PrivateKeyInfo', der_encoded()}}</tag> + <tag>{key, {'RSAPrivateKey'| 'DSAPrivateKey' | 'ECPrivateKey' |'PrivateKeyInfo', der_encoded()}}</tag> <item> The DER encoded users private key. If this option is supplied it will override the keyfile option.</item> diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 70f3b4f050..f52862729a 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -364,11 +364,11 @@ cipher_suites() -> cipher_suites(erlang) -> Version = ssl_record:highest_protocol_version([]), - [suite_definition(S) || S <- ssl_cipher:suites(Version)]; + [suite_definition(S) || S <- cipher_suites(Version, [])]; cipher_suites(openssl) -> Version = ssl_record:highest_protocol_version([]), - [ssl_cipher:openssl_suite_name(S) || S <- ssl_cipher:suites(Version)]; + [ssl_cipher:openssl_suite_name(S) || S <- cipher_suites(Version, [])]; cipher_suites(all) -> Version = ssl_record:highest_protocol_version([]), @@ -739,6 +739,7 @@ validate_option(key, {KeyType, Value}) when is_binary(Value), KeyType == dsa; %% Backwards compatibility KeyType == 'RSAPrivateKey'; KeyType == 'DSAPrivateKey'; + KeyType == 'ECPrivateKey'; KeyType == 'PrivateKeyInfo' -> {KeyType, Value}; @@ -947,21 +948,22 @@ emulated_options([], Inet,Emulated) -> {Inet, Emulated}. cipher_suites(Version, []) -> - ssl_cipher:suites(Version); + ssl_cipher:filter_suites(ssl_cipher:suites(Version)); cipher_suites(Version, [{_,_,_,_}| _] = Ciphers0) -> %% Backwards compatibility Ciphers = [{KeyExchange, Cipher, Hash} || {KeyExchange, Cipher, Hash, _} <- Ciphers0], - cipher_suites(Version, Ciphers); + ssl_cipher:filter_suites(cipher_suites(Version, Ciphers)); cipher_suites(Version, [{_,_,_}| _] = Ciphers0) -> Ciphers = [ssl_cipher:suite(C) || C <- Ciphers0], - cipher_suites(Version, Ciphers); + ssl_cipher:filter_suites(cipher_suites(Version, Ciphers)); cipher_suites(Version, [Cipher0 | _] = Ciphers0) when is_binary(Cipher0) -> - Supported = ssl_cipher:suites(Version) + Supported0 = ssl_cipher:suites(Version) ++ ssl_cipher:anonymous_suites() ++ ssl_cipher:psk_suites(Version) ++ ssl_cipher:srp_suites(), - case [Cipher || Cipher <- Ciphers0, lists:member(Cipher, Supported)] of + Supported1 = ssl_cipher:filter_suites(Supported0), + case [Cipher || Cipher <- Ciphers0, lists:member(Cipher, Supported1)] of [] -> - Supported; + Supported1; Ciphers -> Ciphers end; diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl index 01a7cd93b5..9e1c3a09bf 100644 --- a/lib/ssl/src/ssl_certificate.erl +++ b/lib/ssl/src/ssl_certificate.erl @@ -37,7 +37,8 @@ is_valid_extkey_usage/2, is_valid_key_usage/2, select_extension/2, - extensions_list/1 + extensions_list/1, + public_key_type/1 ]). %%==================================================================== @@ -166,6 +167,18 @@ extensions_list(Extensions) -> Extensions. %%-------------------------------------------------------------------- +-spec public_key_type(term()) -> rsa | dsa | ec. +%% +%% Description: +%%-------------------------------------------------------------------- +public_key_type(?'rsaEncryption') -> + rsa; +public_key_type(?'id-dsa') -> + dsa; +public_key_type(?'id-ecPublicKey') -> + ec. + +%%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- certificate_chain(OtpCert, _Cert, CertDbHandle, CertsDbRef, Chain) -> diff --git a/lib/ssl/src/ssl_certificate_db.erl b/lib/ssl/src/ssl_certificate_db.erl index ff36b5ee26..cdff73336e 100644 --- a/lib/ssl/src/ssl_certificate_db.erl +++ b/lib/ssl/src/ssl_certificate_db.erl @@ -100,7 +100,7 @@ add_trusted_certs(_Pid, {der, DerList}, [CerDb, _,_]) -> {ok, NewRef}; add_trusted_certs(_Pid, File, [CertsDb, RefDb, PemChache] = Db) -> - MD5 = crypto:md5(File), + MD5 = crypto:hash(md5, File), case lookup_cached_pem(Db, MD5) of [{_Content, Ref}] -> ref_count(Ref, RefDb, 1), diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl index 173c53709b..dc413d6dfc 100644 --- a/lib/ssl/src/ssl_cipher.erl +++ b/lib/ssl/src/ssl_cipher.erl @@ -35,7 +35,7 @@ -export([security_parameters/3, suite_definition/1, decipher/5, cipher/5, suite/1, suites/1, anonymous_suites/0, psk_suites/1, srp_suites/0, - openssl_suite/1, openssl_suite_name/1, filter/2, + openssl_suite/1, openssl_suite_name/1, filter/2, filter_suites/1, hash_algorithm/1, sign_algorithm/1]). -compile(inline). @@ -73,25 +73,25 @@ cipher(?NULL, CipherState, <<>>, Fragment, _Version) -> {GenStreamCipherList, CipherState}; cipher(?RC4, CipherState, Mac, Fragment, _Version) -> State0 = case CipherState#cipher_state.state of - undefined -> crypto:rc4_set_key(CipherState#cipher_state.key); + undefined -> crypto:stream_init(rc4, CipherState#cipher_state.key); S -> S end, GenStreamCipherList = [Fragment, Mac], - {State1, T} = crypto:rc4_encrypt_with_state(State0, GenStreamCipherList), + {State1, T} = crypto:stream_encrypt(State0, GenStreamCipherList), {T, CipherState#cipher_state{state = State1}}; cipher(?DES, CipherState, Mac, Fragment, Version) -> block_cipher(fun(Key, IV, T) -> - crypto:des_cbc_encrypt(Key, IV, T) + crypto:block_encrypt(des_cbc, Key, IV, T) end, block_size(des_cbc), CipherState, Mac, Fragment, Version); cipher(?'3DES', CipherState, Mac, Fragment, Version) -> block_cipher(fun(<<K1:8/binary, K2:8/binary, K3:8/binary>>, IV, T) -> - crypto:des3_cbc_encrypt(K1, K2, K3, IV, T) + crypto:block_encrypt(des3_cbc, [K1, K2, K3], IV, T) end, block_size(des_cbc), CipherState, Mac, Fragment, Version); cipher(?AES, CipherState, Mac, Fragment, Version) -> block_cipher(fun(Key, IV, T) when byte_size(Key) =:= 16 -> - crypto:aes_cbc_128_encrypt(Key, IV, T); + crypto:block_encrypt(aes_cbc128, Key, IV, T); (Key, IV, T) when byte_size(Key) =:= 32 -> - crypto:aes_cbc_256_encrypt(Key, IV, T) + crypto:block_encrypt(aes_cbc256, Key, IV, T) end, block_size(aes_128_cbc), CipherState, Mac, Fragment, Version). build_cipher_block(BlockSz, Mac, Fragment) -> @@ -127,10 +127,10 @@ decipher(?NULL, _HashSz, CipherState, Fragment, _) -> {Fragment, <<>>, CipherState}; decipher(?RC4, HashSz, CipherState, Fragment, _) -> State0 = case CipherState#cipher_state.state of - undefined -> crypto:rc4_set_key(CipherState#cipher_state.key); + undefined -> crypto:stream_init(rc4, CipherState#cipher_state.key); S -> S end, - try crypto:rc4_encrypt_with_state(State0, Fragment) of + try crypto:stream_decrypt(State0, Fragment) of {State, Text} -> GSC = generic_stream_cipher_from_bin(Text, HashSz), #generic_stream_cipher{content = Content, mac = Mac} = GSC, @@ -147,17 +147,17 @@ decipher(?RC4, HashSz, CipherState, Fragment, _) -> decipher(?DES, HashSz, CipherState, Fragment, Version) -> block_decipher(fun(Key, IV, T) -> - crypto:des_cbc_decrypt(Key, IV, T) + crypto:block_decrypt(des_cbc, Key, IV, T) end, CipherState, HashSz, Fragment, Version); decipher(?'3DES', HashSz, CipherState, Fragment, Version) -> block_decipher(fun(<<K1:8/binary, K2:8/binary, K3:8/binary>>, IV, T) -> - crypto:des3_cbc_decrypt(K1, K2, K3, IV, T) + crypto:block_decrypt(des3_cbc, [K1, K2, K3], IV, T) end, CipherState, HashSz, Fragment, Version); decipher(?AES, HashSz, CipherState, Fragment, Version) -> block_decipher(fun(Key, IV, T) when byte_size(Key) =:= 16 -> - crypto:aes_cbc_128_decrypt(Key, IV, T); + crypto:block_decrypt(aes_cbc128, Key, IV, T); (Key, IV, T) when byte_size(Key) =:= 32 -> - crypto:aes_cbc_256_decrypt(Key, IV, T) + crypto:block_decrypt(aes_cbc256, Key, IV, T) end, CipherState, HashSz, Fragment, Version). block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0, @@ -212,10 +212,14 @@ anonymous_suites() -> ?TLS_DH_anon_WITH_AES_128_CBC_SHA, ?TLS_DH_anon_WITH_AES_256_CBC_SHA, ?TLS_DH_anon_WITH_AES_128_CBC_SHA256, - ?TLS_DH_anon_WITH_AES_256_CBC_SHA256]. + ?TLS_DH_anon_WITH_AES_256_CBC_SHA256, + ?TLS_ECDH_anon_WITH_RC4_128_SHA, + ?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA, + ?TLS_ECDH_anon_WITH_AES_128_CBC_SHA, + ?TLS_ECDH_anon_WITH_AES_256_CBC_SHA]. %%-------------------------------------------------------------------- --spec psk_suites(tls_version()) -> [cipher_suite()]. +-spec psk_suites(tls_version() | integer()) -> [cipher_suite()]. %% %% Description: Returns a list of the PSK cipher suites, only supported %% if explicitly set by user. @@ -274,6 +278,11 @@ srp_suites() -> %% TLS v1.1 suites suite_definition(?TLS_NULL_WITH_NULL_NULL) -> {null, null, null, null}; +%% RFC 5746 - Not a real cipher suite used to signal empty "renegotiation_info" extension +%% to avoid handshake failure from old servers that do not ignore +%% hello extension data as they should. +suite_definition(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV) -> + {null, null, null, null}; %% suite_definition(?TLS_RSA_WITH_NULL_MD5) -> %% {rsa, null, md5, default_prf}; %% suite_definition(?TLS_RSA_WITH_NULL_SHA) -> @@ -423,8 +432,81 @@ suite_definition(?TLS_SRP_SHA_WITH_AES_256_CBC_SHA) -> suite_definition(?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA) -> {srp_rsa, aes_256_cbc, sha, default_prf}; suite_definition(?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA) -> - {srp_dss, aes_256_cbc, sha, default_prf}. - + {srp_dss, aes_256_cbc, sha, default_prf}; + +%% RFC 4492 EC TLS suites +suite_definition(?TLS_ECDH_ECDSA_WITH_NULL_SHA) -> + {ecdh_ecdsa, null, sha, default_prf}; +suite_definition(?TLS_ECDH_ECDSA_WITH_RC4_128_SHA) -> + {ecdh_ecdsa, rc4_128, sha, default_prf}; +suite_definition(?TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA) -> + {ecdh_ecdsa, '3des_ede_cbc', sha, default_prf}; +suite_definition(?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA) -> + {ecdh_ecdsa, aes_128_cbc, sha, default_prf}; +suite_definition(?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA) -> + {ecdh_ecdsa, aes_256_cbc, sha, default_prf}; + +suite_definition(?TLS_ECDHE_ECDSA_WITH_NULL_SHA) -> + {ecdhe_ecdsa, null, sha, default_prf}; +suite_definition(?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA) -> + {ecdhe_ecdsa, rc4_128, sha, default_prf}; +suite_definition(?TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA) -> + {ecdhe_ecdsa, '3des_ede_cbc', sha, default_prf}; +suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA) -> + {ecdhe_ecdsa, aes_128_cbc, sha, default_prf}; +suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA) -> + {ecdhe_ecdsa, aes_256_cbc, sha, default_prf}; + +suite_definition(?TLS_ECDH_RSA_WITH_NULL_SHA) -> + {ecdh_rsa, null, sha, default_prf}; +suite_definition(?TLS_ECDH_RSA_WITH_RC4_128_SHA) -> + {ecdh_rsa, rc4_128, sha, default_prf}; +suite_definition(?TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA) -> + {ecdh_rsa, '3des_ede_cbc', sha, default_prf}; +suite_definition(?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA) -> + {ecdh_rsa, aes_128_cbc, sha, default_prf}; +suite_definition(?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA) -> + {ecdh_rsa, aes_256_cbc, sha, default_prf}; + +suite_definition(?TLS_ECDHE_RSA_WITH_NULL_SHA) -> + {ecdhe_rsa, null, sha, default_prf}; +suite_definition(?TLS_ECDHE_RSA_WITH_RC4_128_SHA) -> + {ecdhe_rsa, rc4_128, sha, default_prf}; +suite_definition(?TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA) -> + {ecdhe_rsa, '3des_ede_cbc', sha, default_prf}; +suite_definition(?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA) -> + {ecdhe_rsa, aes_128_cbc, sha, default_prf}; +suite_definition(?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA) -> + {ecdhe_rsa, aes_256_cbc, sha, default_prf}; + +suite_definition(?TLS_ECDH_anon_WITH_NULL_SHA) -> + {ecdh_anon, null, sha, default_prf}; +suite_definition(?TLS_ECDH_anon_WITH_RC4_128_SHA) -> + {ecdh_anon, rc4_128, sha, default_prf}; +suite_definition(?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA) -> + {ecdh_anon, '3des_ede_cbc', sha, default_prf}; +suite_definition(?TLS_ECDH_anon_WITH_AES_128_CBC_SHA) -> + {ecdh_anon, aes_128_cbc, sha, default_prf}; +suite_definition(?TLS_ECDH_anon_WITH_AES_256_CBC_SHA) -> + {ecdh_anon, aes_256_cbc, sha, default_prf}; + +%% RFC 5289 EC TLS suites +suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256) -> + {ecdhe_ecdsa, aes_128_cbc, sha256, sha256}; +suite_definition(?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384) -> + {ecdhe_ecdsa, aes_256_cbc, sha384, sha384}; +suite_definition(?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256) -> + {ecdh_ecdsa, aes_128_cbc, sha256, sha256}; +suite_definition(?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384) -> + {ecdh_ecdsa, aes_256_cbc, sha384, sha384}; +suite_definition(?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) -> + {ecdhe_rsa, aes_128_cbc, sha256, sha256}; +suite_definition(?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384) -> + {ecdhe_rsa, aes_256_cbc, sha384, sha384}; +suite_definition(?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256) -> + {ecdh_rsa, aes_128_cbc, sha256, sha256}; +suite_definition(?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384) -> + {ecdh_rsa, aes_256_cbc, sha384, sha384}. %%-------------------------------------------------------------------- -spec suite(erl_cipher_suite()) -> cipher_suite(). @@ -573,7 +655,81 @@ suite({srp_anon, aes_256_cbc, sha}) -> suite({srp_rsa, aes_256_cbc, sha}) -> ?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA; suite({srp_dss, aes_256_cbc, sha}) -> - ?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA. + ?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA; + +%%% RFC 4492 EC TLS suites +suite({ecdh_ecdsa, null, sha}) -> + ?TLS_ECDH_ECDSA_WITH_NULL_SHA; +suite({ecdh_ecdsa, rc4_128, sha}) -> + ?TLS_ECDH_ECDSA_WITH_RC4_128_SHA; +suite({ecdh_ecdsa, '3des_ede_cbc', sha}) -> + ?TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA; +suite({ecdh_ecdsa, aes_128_cbc, sha}) -> + ?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA; +suite({ecdh_ecdsa, aes_256_cbc, sha}) -> + ?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA; + +suite({ecdhe_ecdsa, null, sha}) -> + ?TLS_ECDHE_ECDSA_WITH_NULL_SHA; +suite({ecdhe_ecdsa, rc4_128, sha}) -> + ?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA; +suite({ecdhe_ecdsa, '3des_ede_cbc', sha}) -> + ?TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA; +suite({ecdhe_ecdsa, aes_128_cbc, sha}) -> + ?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA; +suite({ecdhe_ecdsa, aes_256_cbc, sha}) -> + ?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA; + +suite({ecdh_rsa, null, sha}) -> + ?TLS_ECDH_RSA_WITH_NULL_SHA; +suite({ecdh_rsa, rc4_128, sha}) -> + ?TLS_ECDH_RSA_WITH_RC4_128_SHA; +suite({ecdh_rsa, '3des_ede_cbc', sha}) -> + ?TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA; +suite({ecdh_rsa, aes_128_cbc, sha}) -> + ?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA; +suite({ecdh_rsa, aes_256_cbc, sha}) -> + ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA; + +suite({ecdhe_rsa, null, sha}) -> + ?TLS_ECDHE_RSA_WITH_NULL_SHA; +suite({ecdhe_rsa, rc4_128, sha}) -> + ?TLS_ECDHE_RSA_WITH_RC4_128_SHA; +suite({ecdhe_rsa, '3des_ede_cbc', sha}) -> + ?TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA; +suite({ecdhe_rsa, aes_128_cbc, sha}) -> + ?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA; +suite({ecdhe_rsa, aes_256_cbc, sha}) -> + ?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA; + +suite({ecdh_anon, null, sha}) -> + ?TLS_ECDH_anon_WITH_NULL_SHA; +suite({ecdh_anon, rc4_128, sha}) -> + ?TLS_ECDH_anon_WITH_RC4_128_SHA; +suite({ecdh_anon, '3des_ede_cbc', sha}) -> + ?TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA; +suite({ecdh_anon, aes_128_cbc, sha}) -> + ?TLS_ECDH_anon_WITH_AES_128_CBC_SHA; +suite({ecdh_anon, aes_256_cbc, sha}) -> + ?TLS_ECDH_anon_WITH_AES_256_CBC_SHA; + +%%% RFC 5289 EC TLS suites +suite({ecdhe_ecdsa, aes_128_cbc, sha256}) -> + ?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256; +suite({ecdhe_ecdsa, aes_256_cbc, sha384}) -> + ?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384; +suite({ecdh_ecdsa, aes_128_cbc, sha256}) -> + ?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256; +suite({ecdh_ecdsa, aes_256_cbc, sha384}) -> + ?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384; +suite({ecdhe_rsa, aes_128_cbc, sha256}) -> + ?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256; +suite({ecdhe_rsa, aes_256_cbc, sha384}) -> + ?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384; +suite({ecdh_rsa, aes_128_cbc, sha256}) -> + ?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256; +suite({ecdh_rsa, aes_256_cbc, sha384}) -> + ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384. %%-------------------------------------------------------------------- -spec openssl_suite(openssl_cipher_suite()) -> cipher_suite(). @@ -633,8 +789,62 @@ openssl_suite("SRP-RSA-3DES-EDE-CBC-SHA") -> openssl_suite("SRP-DSS-AES-128-CBC-SHA") -> ?TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA; openssl_suite("SRP-RSA-AES-128-CBC-SHA") -> - ?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA. + ?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA; +%% RFC 4492 EC TLS suites +openssl_suite("ECDH-ECDSA-RC4-SHA") -> + ?TLS_ECDH_ECDSA_WITH_RC4_128_SHA; +openssl_suite("ECDH-ECDSA-DES-CBC3-SHA") -> + ?TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA; +openssl_suite("ECDH-ECDSA-AES128-SHA") -> + ?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA; +openssl_suite("ECDH-ECDSA-AES256-SHA") -> + ?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA; + +openssl_suite("ECDHE-ECDSA-RC4-SHA") -> + ?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA; +openssl_suite("ECDHE-ECDSA-DES-CBC3-SHA") -> + ?TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA; +openssl_suite("ECDHE-ECDSA-AES128-SHA") -> + ?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA; +openssl_suite("ECDHE-ECDSA-AES256-SHA") -> + ?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA; + +openssl_suite("ECDHE-RSA-RC4-SHA") -> + ?TLS_ECDHE_RSA_WITH_RC4_128_SHA; +openssl_suite("ECDHE-RSA-DES-CBC3-SHA") -> + ?TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA; +openssl_suite("ECDHE-RSA-AES128-SHA") -> + ?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA; +openssl_suite("ECDHE-RSA-AES256-SHA") -> + ?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA; + +openssl_suite("ECDH-RSA-RC4-SHA") -> + ?TLS_ECDH_RSA_WITH_RC4_128_SHA; +openssl_suite("ECDH-RSA-DES-CBC3-SHA") -> + ?TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA; +openssl_suite("ECDH-RSA-AES128-SHA") -> + ?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA; +openssl_suite("ECDH-RSA-AES256-SHA") -> + ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA; + +%% RFC 5289 EC TLS suites +openssl_suite("ECDHE-ECDSA-AES128-SHA256") -> + ?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256; +openssl_suite("ECDHE-ECDSA-AES256-SHA384") -> + ?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384; +openssl_suite("ECDH-ECDSA-AES128-SHA256") -> + ?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256; +openssl_suite("ECDH-ECDSA-AES256-SHA384") -> + ?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384; +openssl_suite("ECDHE-RSA-AES128-SHA256") -> + ?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256; +openssl_suite("ECDHE-RSA-AES256-SHA384") -> + ?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384; +openssl_suite("ECDH-RSA-AES128-SHA256") -> + ?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256; +openssl_suite("ECDH-RSA-AES256-SHA384") -> + ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384. %%-------------------------------------------------------------------- -spec openssl_suite_name(cipher_suite()) -> openssl_cipher_suite(). @@ -716,6 +926,61 @@ openssl_suite_name(?TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA) -> openssl_suite_name(?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA) -> "SRP-DSS-AES-256-CBC-SHA"; +%% RFC 4492 EC TLS suites +openssl_suite_name(?TLS_ECDH_ECDSA_WITH_RC4_128_SHA) -> + "ECDH-ECDSA-RC4-SHA"; +openssl_suite_name(?TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA) -> + "ECDH-ECDSA-DES-CBC3-SHA"; +openssl_suite_name(?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA) -> + "ECDH-ECDSA-AES128-SHA"; +openssl_suite_name(?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA) -> + "ECDH-ECDSA-AES256-SHA"; + +openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA) -> + "ECDHE-ECDSA-RC4-SHA"; +openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA) -> + "ECDHE-ECDSA-DES-CBC3-SHA"; +openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA) -> + "ECDHE-ECDSA-AES128-SHA"; +openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA) -> + "ECDHE-ECDSA-AES256-SHA"; + +openssl_suite_name(?TLS_ECDH_RSA_WITH_RC4_128_SHA) -> + "ECDH-RSA-RC4-SHA"; +openssl_suite_name(?TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA) -> + "ECDH-RSA-DES-CBC3-SHA"; +openssl_suite_name(?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA) -> + "ECDH-RSA-AES128-SHA"; +openssl_suite_name(?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA) -> + "ECDH-RSA-AES256-SHA"; + +openssl_suite_name(?TLS_ECDHE_RSA_WITH_RC4_128_SHA) -> + "ECDHE-RSA-RC4-SHA"; +openssl_suite_name(?TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA) -> + "ECDHE-RSA-DES-CBC3-SHA"; +openssl_suite_name(?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA) -> + "ECDHE-RSA-AES128-SHA"; +openssl_suite_name(?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA) -> + "ECDHE-RSA-AES256-SHA"; + +%% RFC 5289 EC TLS suites +openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256) -> + "ECDHE-ECDSA-AES128-SHA256"; +openssl_suite_name(?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384) -> + "ECDHE-ECDSA-AES256-SHA384"; +openssl_suite_name(?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256) -> + "ECDH-ECDSA-AES128-SHA256"; +openssl_suite_name(?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384) -> + "ECDH-ECDSA-AES256-SHA384"; +openssl_suite_name(?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) -> + "ECDHE-RSA-AES128-SHA256"; +openssl_suite_name(?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384) -> + "ECDHE-RSA-AES256-SHA384"; +openssl_suite_name(?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256) -> + "ECDH-RSA-AES128-SHA256"; +openssl_suite_name(?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384) -> + "ECDH-RSA-AES256-SHA384"; + %% No oppenssl name openssl_suite_name(Cipher) -> suite_definition(Cipher). @@ -730,14 +995,85 @@ filter(undefined, Ciphers) -> filter(DerCert, Ciphers) -> OtpCert = public_key:pkix_decode_cert(DerCert, otp), SigAlg = OtpCert#'OTPCertificate'.signatureAlgorithm, + PubKeyInfo = OtpCert#'OTPCertificate'.tbsCertificate#'OTPTBSCertificate'.subjectPublicKeyInfo, + PubKeyAlg = PubKeyInfo#'OTPSubjectPublicKeyInfo'.algorithm, + + Ciphers1 = + case ssl_certificate:public_key_type(PubKeyAlg#'PublicKeyAlgorithm'.algorithm) of + rsa -> + filter_keyuse(OtpCert, ((Ciphers -- dsa_signed_suites()) -- ec_keyed_suites()) -- ecdh_suites(), + rsa_suites(), dhe_rsa_suites() ++ ecdhe_rsa_suites()); + dsa -> + (Ciphers -- rsa_keyed_suites()) -- ec_keyed_suites(); + ec -> + filter_keyuse(OtpCert, (Ciphers -- rsa_keyed_suites()) -- dsa_signed_suites(), + [], ecdhe_ecdsa_suites()) + end, case public_key:pkix_sign_types(SigAlg#'SignatureAlgorithm'.algorithm) of {_, rsa} -> - filter_rsa(OtpCert, Ciphers -- dsa_signed_suites()); + Ciphers1 -- ecdsa_signed_suites(); {_, dsa} -> - Ciphers -- rsa_signed_suites() + Ciphers1; + {_, ecdsa} -> + Ciphers1 -- rsa_signed_suites() end. %%-------------------------------------------------------------------- +-spec filter_suites([cipher_suite()]) -> [cipher_suite()]. +%% +%% Description: filter suites for algorithms +%%------------------------------------------------------------------- +filter_suites(Suites = [{_,_,_}|_]) -> + Algos = crypto:algorithms(), + lists:filter(fun({KeyExchange, Cipher, Hash}) -> + is_acceptable_keyexchange(KeyExchange, Algos) andalso + is_acceptable_cipher(Cipher, Algos) andalso + is_acceptable_hash(Hash, Algos) + end, Suites); + +filter_suites(Suites = [{_,_,_,_}|_]) -> + Algos = crypto:algorithms(), + lists:filter(fun({KeyExchange, Cipher, Hash, Prf}) -> + is_acceptable_keyexchange(KeyExchange, Algos) andalso + is_acceptable_cipher(Cipher, Algos) andalso + is_acceptable_hash(Hash, Algos) andalso + is_acceptable_prf(Prf, Algos) + end, Suites); + +filter_suites(Suites) -> + Algos = crypto:algorithms(), + lists:filter(fun(Suite) -> + {KeyExchange, Cipher, Hash, Prf} = ssl_cipher:suite_definition(Suite), + is_acceptable_keyexchange(KeyExchange, Algos) andalso + is_acceptable_cipher(Cipher, Algos) andalso + is_acceptable_hash(Hash, Algos) andalso + is_acceptable_prf(Prf, Algos) + end, Suites). + +is_acceptable_keyexchange(KeyExchange, Algos) + when KeyExchange == ecdh_ecdsa; + KeyExchange == ecdhe_ecdsa; + KeyExchange == ecdh_rsa; + KeyExchange == ecdhe_rsa; + KeyExchange == ecdh_anon -> + proplists:get_bool(ec, Algos); +is_acceptable_keyexchange(_, _) -> + true. + +is_acceptable_cipher(_, _) -> + true. + +is_acceptable_hash(null, _Algos) -> + true; +is_acceptable_hash(Hash, Algos) -> + proplists:get_bool(Hash, Algos). + +is_acceptable_prf(default_prf, _) -> + true; +is_acceptable_prf(Prf, Algos) -> + proplists:get_bool(Prf, Algos). + +%%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- @@ -950,7 +1286,13 @@ next_iv(Bin, IV) -> rsa_signed_suites() -> dhe_rsa_suites() ++ rsa_suites() ++ - psk_rsa_suites() ++ srp_rsa_suites(). + psk_rsa_suites() ++ srp_rsa_suites() ++ + ecdh_rsa_suites(). + +rsa_keyed_suites() -> + dhe_rsa_suites() ++ rsa_suites() ++ + psk_rsa_suites() ++ srp_rsa_suites() ++ + ecdhe_rsa_suites(). dhe_rsa_suites() -> [?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, @@ -982,7 +1324,25 @@ rsa_suites() -> ?TLS_RSA_WITH_RC4_128_SHA, ?TLS_RSA_WITH_RC4_128_MD5, ?TLS_RSA_WITH_DES_CBC_SHA]. - + +ecdh_rsa_suites() -> + [?TLS_ECDH_RSA_WITH_NULL_SHA, + ?TLS_ECDH_RSA_WITH_RC4_128_SHA, + ?TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, + ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, + ?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, + ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384]. + +ecdhe_rsa_suites() -> + [?TLS_ECDHE_RSA_WITH_NULL_SHA, + ?TLS_ECDHE_RSA_WITH_RC4_128_SHA, + ?TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + ?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + ?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + ?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384]. + dsa_signed_suites() -> dhe_dss_suites() ++ srp_dss_suites(). @@ -999,24 +1359,52 @@ srp_dss_suites() -> ?TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA, ?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA]. -filter_rsa(OtpCert, RsaCiphers) -> +ec_keyed_suites() -> + ecdh_ecdsa_suites() ++ ecdhe_ecdsa_suites() + ++ ecdh_rsa_suites(). + +ecdsa_signed_suites() -> + ecdh_ecdsa_suites() ++ ecdhe_ecdsa_suites(). + +ecdh_suites() -> + ecdh_rsa_suites() ++ ecdh_ecdsa_suites(). + +ecdh_ecdsa_suites() -> + [?TLS_ECDH_ECDSA_WITH_NULL_SHA, + ?TLS_ECDH_ECDSA_WITH_RC4_128_SHA, + ?TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, + ?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, + ?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, + ?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384]. + +ecdhe_ecdsa_suites() -> + [?TLS_ECDHE_ECDSA_WITH_NULL_SHA, + ?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, + ?TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + ?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + ?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + ?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384]. + +filter_keyuse(OtpCert, Ciphers, Suites, SignSuites) -> TBSCert = OtpCert#'OTPCertificate'.tbsCertificate, TBSExtensions = TBSCert#'OTPTBSCertificate'.extensions, Extensions = ssl_certificate:extensions_list(TBSExtensions), case ssl_certificate:select_extension(?'id-ce-keyUsage', Extensions) of undefined -> - RsaCiphers; + Ciphers; #'Extension'{extnValue = KeyUse} -> - Result = filter_rsa_suites(keyEncipherment, - KeyUse, RsaCiphers, rsa_suites()), - filter_rsa_suites(digitalSignature, - KeyUse, Result, dhe_rsa_suites()) + Result = filter_keyuse_suites(keyEncipherment, + KeyUse, Ciphers, Suites), + filter_keyuse_suites(digitalSignature, + KeyUse, Result, SignSuites) end. -filter_rsa_suites(Use, KeyUse, CipherSuits, RsaSuites) -> +filter_keyuse_suites(Use, KeyUse, CipherSuits, Suites) -> case ssl_certificate:is_valid_key_usage(KeyUse, Use) of true -> CipherSuits; false -> - CipherSuits -- RsaSuites + CipherSuits -- Suites end. diff --git a/lib/ssl/src/ssl_cipher.hrl b/lib/ssl/src/ssl_cipher.hrl index 90d3704efd..c7c71ee1a7 100644 --- a/lib/ssl/src/ssl_cipher.hrl +++ b/lib/ssl/src/ssl_cipher.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -28,9 +28,9 @@ -type cipher() :: null |rc4_128 | idea_cbc | des40_cbc | des_cbc | '3des_ede_cbc' | aes_128_cbc | aes_256_cbc. --type hash() :: null | sha | md5 | sha256 | sha384 | sha512. +-type hash() :: null | sha | md5 | ssh224 | sha256 | sha384 | sha512. -type erl_cipher_suite() :: {key_algo(), cipher(), hash()}. --type int_cipher_suite() :: {key_algo(), cipher(), hash(), hash()}. +-type int_cipher_suite() :: {key_algo(), cipher(), hash(), hash() | default_prf}. -type cipher_suite() :: binary(). -type cipher_enum() :: integer(). -type openssl_cipher_suite() :: string(). @@ -219,6 +219,120 @@ %% TLS_DH_anon_WITH_AES_256_CBC_SHA256 = { 0x00,0x6D }; -define(TLS_DH_anon_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#6D)>>). +%% RFC 4492 EC TLS suites + +%% ECDH_ECDSA + +%% TLS_ECDH_ECDSA_WITH_NULL_SHA = { 0xC0, 0x01 } +-define(TLS_ECDH_ECDSA_WITH_NULL_SHA, <<?BYTE(16#C0), ?BYTE(16#01)>>). + +%% TLS_ECDH_ECDSA_WITH_RC4_128_SHA = { 0xC0, 0x02 } +-define(TLS_ECDH_ECDSA_WITH_RC4_128_SHA, <<?BYTE(16#C0), ?BYTE(16#02)>>). + +%% TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA = { 0xC0, 0x03 } +-define(TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#03)>>). + +%% TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA = { 0xC0, 0x04 } +-define(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#04)>>). + +%% TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA = { 0xC0, 0x05 } +-define(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#05)>>). + +%% ECDHE_ECDSA + +%% TLS_ECDHE_ECDSA_WITH_NULL_SHA = { 0xC0, 0x06 } +-define(TLS_ECDHE_ECDSA_WITH_NULL_SHA, <<?BYTE(16#C0), ?BYTE(16#06)>>). + +%% TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = { 0xC0, 0x07 } +-define(TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, <<?BYTE(16#C0), ?BYTE(16#07)>>). + +%% TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = { 0xC0, 0x08 } +-define(TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#08)>>). + +%% TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = { 0xC0, 0x09 } +-define(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#09)>>). + +%% TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = { 0xC0, 0x0A } +-define(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#0A)>>). + +%% ECDH_RSA + +%% TLS_ECDH_RSA_WITH_NULL_SHA = { 0xC0, 0x0B } +-define(TLS_ECDH_RSA_WITH_NULL_SHA, <<?BYTE(16#C0), ?BYTE(16#0B)>>). + +%% TLS_ECDH_RSA_WITH_RC4_128_SHA = { 0xC0, 0x0C } +-define(TLS_ECDH_RSA_WITH_RC4_128_SHA, <<?BYTE(16#C0), ?BYTE(16#0C)>>). + +%% TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA = { 0xC0, 0x0D } +-define(TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#0D)>>). + +%% TLS_ECDH_RSA_WITH_AES_128_CBC_SHA = { 0xC0, 0x0E } +-define(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#0E)>>). + +%% TLS_ECDH_RSA_WITH_AES_256_CBC_SHA = { 0xC0, 0x0F } +-define(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#0F)>>). + +%% ECDHE_RSA + +%% TLS_ECDHE_RSA_WITH_NULL_SHA = { 0xC0, 0x10 } +-define(TLS_ECDHE_RSA_WITH_NULL_SHA, <<?BYTE(16#C0), ?BYTE(16#10)>>). + +%% TLS_ECDHE_RSA_WITH_RC4_128_SHA = { 0xC0, 0x11 } +-define(TLS_ECDHE_RSA_WITH_RC4_128_SHA, <<?BYTE(16#C0), ?BYTE(16#11)>>). + +%% TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = { 0xC0, 0x12 } +-define(TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#12)>>). + +%% TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = { 0xC0, 0x13 } +-define(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#13)>>). + +%% TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = { 0xC0, 0x14 } +-define(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#14)>>). + +%% ECDH_anon + +%% TLS_ECDH_anon_WITH_NULL_SHA = { 0xC0, 0x15 } +-define(TLS_ECDH_anon_WITH_NULL_SHA, <<?BYTE(16#C0), ?BYTE(16#15)>>). + +%% TLS_ECDH_anon_WITH_RC4_128_SHA = { 0xC0, 0x16 } +-define(TLS_ECDH_anon_WITH_RC4_128_SHA, <<?BYTE(16#C0), ?BYTE(16#16)>>). + +%% TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA = { 0xC0, 0x17 } +-define(TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#17)>>). + +%% TLS_ECDH_anon_WITH_AES_128_CBC_SHA = { 0xC0, 0x18 } +-define(TLS_ECDH_anon_WITH_AES_128_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#18)>>). + +%% TLS_ECDH_anon_WITH_AES_256_CBC_SHA = { 0xC0, 0x19 } +-define(TLS_ECDH_anon_WITH_AES_256_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#19)>>). + + +%% RFC 5289 EC TLS suites + +%% TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = {0xC0,0x23}; +-define(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, <<?BYTE(16#C0), ?BYTE(16#23)>>). + +%% TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = {0xC0,0x24}; +-define(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, <<?BYTE(16#C0), ?BYTE(16#24)>>). + +%% TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 = {0xC0,0x25}; +-define(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, <<?BYTE(16#C0), ?BYTE(16#25)>>). + +%% TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 = {0xC0,0x26}; +-define(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, <<?BYTE(16#C0), ?BYTE(16#26)>>). + +%% TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = {0xC0,0x27}; +-define(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, <<?BYTE(16#C0), ?BYTE(16#27)>>). + +%% TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = {0xC0,0x28}; +-define(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, <<?BYTE(16#C0), ?BYTE(16#28)>>). + +%% TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 = {0xC0,0x29}; +-define(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, <<?BYTE(16#C0), ?BYTE(16#29)>>). + +%% TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 = {0xC0,0x2A}; +-define(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, <<?BYTE(16#C0), ?BYTE(16#2A)>>). + %%% Kerberos Cipher Suites %% TLS_KRB5_WITH_DES_CBC_SHA = { 0x00,0x1E }; diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 1843377582..54eed03d3c 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -98,7 +98,8 @@ terminated = false, % allow_renegotiate = true, expecting_next_protocol_negotiation = false :: boolean(), - next_protocol = undefined :: undefined | binary() + next_protocol = undefined :: undefined | binary(), + client_ecc % {Curves, PointFmt} }). -define(DEFAULT_DIFFIE_HELLMAN_PARAMS, @@ -416,11 +417,14 @@ hello(Hello = #client_hello{client_version = ClientVersion}, ssl_options = SslOpts}) -> case ssl_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert}, Renegotiation) of - {Version, {Type, Session}, ConnectionStates, ProtocolsToAdvertise} -> - do_server_hello(Type, ProtocolsToAdvertise, State#state{connection_states = - ConnectionStates, - negotiated_version = Version, - session = Session}); + {Version, {Type, Session}, ConnectionStates, ProtocolsToAdvertise, + EcPointFormats, EllipticCurves} -> + do_server_hello(Type, ProtocolsToAdvertise, + EcPointFormats, EllipticCurves, + State#state{connection_states = ConnectionStates, + negotiated_version = Version, + session = Session, + client_ecc = {EllipticCurves, EcPointFormats}}); #alert{} = Alert -> handle_own_alert(Alert, ClientVersion, hello, State) end; @@ -533,7 +537,9 @@ certify(#certificate{} = Cert, certify(#server_key_exchange{} = KeyExchangeMsg, #state{role = client, negotiated_version = Version, key_algorithm = Alg} = State0) - when Alg == dhe_dss; Alg == dhe_rsa; Alg == dh_anon; + when Alg == dhe_dss; Alg == dhe_rsa; + Alg == ecdhe_rsa; Alg == ecdhe_ecdsa; + Alg == dh_anon; Alg == ecdh_anon; Alg == psk; Alg == dhe_psk; Alg == rsa_psk; Alg == srp_dss; Alg == srp_rsa; Alg == srp_anon -> case handle_server_key(KeyExchangeMsg, State0) of @@ -669,9 +675,20 @@ certify_client_key_exchange(#encrypted_premaster_secret{premaster_secret= EncPMS certify_client_key_exchange(#client_diffie_hellman_public{dh_public = ClientPublicDhKey}, #state{negotiated_version = Version, diffie_hellman_params = #'DHParameter'{prime = P, - base = G}, + base = G} = Params, diffie_hellman_keys = {_, ServerDhPrivateKey}} = State0) -> - case dh_master_secret(crypto:mpint(P), crypto:mpint(G), ClientPublicDhKey, ServerDhPrivateKey, State0) of + case dh_master_secret(Params, ClientPublicDhKey, ServerDhPrivateKey, State0) of + #state{} = State1 -> + {Record, State} = next_record(State1), + next_state(certify, cipher, Record, State); + #alert{} = Alert -> + handle_own_alert(Alert, Version, certify, State0) + end; + +certify_client_key_exchange(#client_ec_diffie_hellman_public{dh_public = ClientPublicEcDhPoint}, + #state{negotiated_version = Version, + diffie_hellman_keys = ECDHKey} = State0) -> + case ec_dh_master_secret(ECDHKey, #'ECPoint'{point = ClientPublicEcDhPoint}, State0) of #state{} = State1 -> {Record, State} = next_record(State1), next_state(certify, cipher, Record, State); @@ -696,7 +713,7 @@ certify_client_key_exchange(#client_dhe_psk_identity{ diffie_hellman_params = #'DHParameter'{prime = P, base = G}, diffie_hellman_keys = {_, ServerDhPrivateKey}} = State0) -> - case dhe_psk_master_secret(ClientPSKIdentity, crypto:mpint(P), crypto:mpint(G), ClientPublicDhKey, ServerDhPrivateKey, State0) of + case dhe_psk_master_secret(ClientPSKIdentity, P, G, ClientPublicDhKey, ServerDhPrivateKey, State0) of #state{} = State1 -> {Record, State} = next_record(State1), next_state(certify, cipher, Record, State); @@ -1278,6 +1295,7 @@ init_private_key(DbHandle, undefined, KeyFile, Password, _) -> [PemEntry] = [PemEntry || PemEntry = {PKey, _ , _} <- List, PKey =:= 'RSAPrivateKey' orelse PKey =:= 'DSAPrivateKey' orelse + PKey =:= 'ECPrivateKey' orelse PKey =:= 'PrivateKeyInfo' ], private_key(public_key:pem_entry_decode(PemEntry, Password)) @@ -1291,6 +1309,8 @@ init_private_key(_,{rsa, PrivateKey}, _, _,_) -> init_private_key('RSAPrivateKey', PrivateKey); init_private_key(_,{dsa, PrivateKey},_,_,_) -> init_private_key('DSAPrivateKey', PrivateKey); +init_private_key(_,{ec, PrivateKey},_,_,_) -> + init_private_key('ECPrivateKey', PrivateKey); init_private_key(_,{Asn1Type, PrivateKey},_,_,_) -> private_key(init_private_key(Asn1Type, PrivateKey)). @@ -1306,6 +1326,7 @@ private_key(#'PrivateKeyInfo'{privateKeyAlgorithm = #'PrivateKeyInfo_privateKeyAlgorithm'{algorithm = ?'id-dsa'}, privateKey = Key}) -> public_key:der_decode('DSAPrivateKey', iolist_to_binary(Key)); + private_key(Key) -> Key. @@ -1357,7 +1378,15 @@ handle_peer_cert(PeerCert, PublicKeyInfo, State1 = State0#state{session = Session#session{peer_certificate = PeerCert}, public_key_info = PublicKeyInfo}, - {Record, State} = next_record(State1), + State2 = case PublicKeyInfo of + {?'id-ecPublicKey', #'ECPoint'{point = _ECPoint} = PublicKey, PublicKeyParams} -> + ECDHKey = public_key:generate_key(PublicKeyParams), + State3 = State1#state{diffie_hellman_keys = ECDHKey}, + ec_dh_master_secret(ECDHKey, PublicKey, State3); + + _ -> State1 + end, + {Record, State} = next_record(State2), next_state(certify, certify, Record, State). certify_client(#state{client_certificate_requested = true, role = client, @@ -1407,15 +1436,18 @@ verify_client_cert(#state{client_certificate_requested = true, role = client, verify_client_cert(#state{client_certificate_requested = false} = State) -> State. -do_server_hello(Type, NextProtocolsToSend, #state{negotiated_version = Version, - session = #session{session_id = SessId}, - connection_states = ConnectionStates0, - renegotiation = {Renegotiation, _}} +do_server_hello(Type, NextProtocolsToSend, + EcPointFormats, EllipticCurves, + #state{negotiated_version = Version, + session = #session{session_id = SessId}, + connection_states = ConnectionStates0, + renegotiation = {Renegotiation, _}} = State0) when is_atom(Type) -> ServerHello = ssl_handshake:server_hello(SessId, Version, - ConnectionStates0, Renegotiation, NextProtocolsToSend), + ConnectionStates0, Renegotiation, + NextProtocolsToSend, EcPointFormats, EllipticCurves), State = server_hello(ServerHello, State0#state{expecting_next_protocol_negotiation = NextProtocolsToSend =/= undefined}), @@ -1547,7 +1579,7 @@ server_hello_done(#state{transport_cb = Transport, tls_handshake_history = Handshake}. certify_server(#state{key_algorithm = Algo} = State) - when Algo == dh_anon; Algo == psk; Algo == dhe_psk -> + when Algo == dh_anon; Algo == ecdh_anon; Algo == psk; Algo == dhe_psk; Algo == srp_anon -> State; certify_server(#state{transport_cb = Transport, @@ -1574,7 +1606,7 @@ key_exchange(#state{role = server, key_algorithm = rsa} = State) -> State; key_exchange(#state{role = server, key_algorithm = Algo, hashsign_algorithm = HashSignAlgo, - diffie_hellman_params = #'DHParameter'{prime = P, base = G} = Params, + diffie_hellman_params = #'DHParameter'{} = Params, private_key = PrivateKey, connection_states = ConnectionStates0, negotiated_version = Version, @@ -1585,13 +1617,13 @@ key_exchange(#state{role = server, key_algorithm = Algo, when Algo == dhe_dss; Algo == dhe_rsa; Algo == dh_anon -> - Keys = crypto:dh_generate_key([crypto:mpint(P), crypto:mpint(G)]), + DHKeys = public_key:generate_key(Params), ConnectionState = ssl_record:pending_connection_state(ConnectionStates0, read), SecParams = ConnectionState#connection_state.security_parameters, #security_parameters{client_random = ClientRandom, server_random = ServerRandom} = SecParams, - Msg = ssl_handshake:key_exchange(server, Version, {dh, Keys, Params, + Msg = ssl_handshake:key_exchange(server, Version, {dh, DHKeys, Params, HashSignAlgo, ClientRandom, ServerRandom, PrivateKey}), @@ -1599,9 +1631,41 @@ key_exchange(#state{role = server, key_algorithm = Algo, encode_handshake(Msg, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), State#state{connection_states = ConnectionStates, - diffie_hellman_keys = Keys, + diffie_hellman_keys = DHKeys, tls_handshake_history = Handshake}; +key_exchange(#state{role = server, private_key = Key, key_algorithm = Algo} = State) + when Algo == ecdh_ecdsa; Algo == ecdh_rsa -> + State#state{diffie_hellman_keys = Key}; +key_exchange(#state{role = server, key_algorithm = Algo, + hashsign_algorithm = HashSignAlgo, + private_key = PrivateKey, + connection_states = ConnectionStates0, + negotiated_version = Version, + tls_handshake_history = Handshake0, + socket = Socket, + transport_cb = Transport + } = State) + when Algo == ecdhe_ecdsa; Algo == ecdhe_rsa; + Algo == ecdh_anon -> + + ECDHKeys = public_key:generate_key(select_curve(State)), + ConnectionState = + ssl_record:pending_connection_state(ConnectionStates0, read), + SecParams = ConnectionState#connection_state.security_parameters, + #security_parameters{client_random = ClientRandom, + server_random = ServerRandom} = SecParams, + Msg = ssl_handshake:key_exchange(server, Version, {ecdh, ECDHKeys, + HashSignAlgo, ClientRandom, + ServerRandom, + PrivateKey}), + {BinMsg, ConnectionStates, Handshake1} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), + Transport:send(Socket, BinMsg), + State#state{connection_states = ConnectionStates, + diffie_hellman_keys = ECDHKeys, + tls_handshake_history = Handshake1}; + key_exchange(#state{role = server, key_algorithm = psk, ssl_options = #ssl_options{psk_identity = undefined}} = State) -> State; @@ -1633,7 +1697,7 @@ key_exchange(#state{role = server, key_algorithm = psk, key_exchange(#state{role = server, key_algorithm = dhe_psk, ssl_options = #ssl_options{psk_identity = PskIdentityHint}, hashsign_algorithm = HashSignAlgo, - diffie_hellman_params = #'DHParameter'{prime = P, base = G} = Params, + diffie_hellman_params = #'DHParameter'{} = Params, private_key = PrivateKey, connection_states = ConnectionStates0, negotiated_version = Version, @@ -1641,13 +1705,13 @@ key_exchange(#state{role = server, key_algorithm = dhe_psk, socket = Socket, transport_cb = Transport } = State) -> - Keys = crypto:dh_generate_key([crypto:mpint(P), crypto:mpint(G)]), + DHKeys = public_key:generate_key(Params), ConnectionState = ssl_record:pending_connection_state(ConnectionStates0, read), SecParams = ConnectionState#connection_state.security_parameters, #security_parameters{client_random = ClientRandom, server_random = ServerRandom} = SecParams, - Msg = ssl_handshake:key_exchange(server, Version, {dhe_psk, PskIdentityHint, Keys, Params, + Msg = ssl_handshake:key_exchange(server, Version, {dhe_psk, PskIdentityHint, DHKeys, Params, HashSignAlgo, ClientRandom, ServerRandom, PrivateKey}), @@ -1655,7 +1719,7 @@ key_exchange(#state{role = server, key_algorithm = dhe_psk, encode_handshake(Msg, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), State#state{connection_states = ConnectionStates, - diffie_hellman_keys = Keys, + diffie_hellman_keys = DHKeys, tls_handshake_history = Handshake}; key_exchange(#state{role = server, key_algorithm = rsa_psk, @@ -1756,6 +1820,23 @@ key_exchange(#state{role = client, tls_handshake_history = Handshake}; key_exchange(#state{role = client, + connection_states = ConnectionStates0, + key_algorithm = Algorithm, + negotiated_version = Version, + diffie_hellman_keys = Keys, + socket = Socket, transport_cb = Transport, + tls_handshake_history = Handshake0} = State) + when Algorithm == ecdhe_ecdsa; Algorithm == ecdhe_rsa; + Algorithm == ecdh_ecdsa; Algorithm == ecdh_rsa; + Algorithm == ecdh_anon -> + Msg = ssl_handshake:key_exchange(client, Version, {ecdh, Keys}), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), + Transport:send(Socket, BinMsg), + State#state{connection_states = ConnectionStates, + tls_handshake_history = Handshake}; + +key_exchange(#state{role = client, ssl_options = SslOpts, connection_states = ConnectionStates0, key_algorithm = psk, @@ -1936,7 +2017,7 @@ handle_server_key(#server_key_exchange{exchange_keys = Keys}, Params = ssl_handshake:decode_server_key(Keys, KeyAlg, Version), HashSign = connection_hashsign(Params#server_key_params.hashsign, State), case HashSign of - {_, anon} -> + {_, SignAlgo} when SignAlgo == anon; SignAlgo == ecdh_anon -> server_master_secret(Params#server_key_params.params, State); _ -> verify_server_key(Params, HashSign, State) @@ -1969,6 +2050,11 @@ server_master_secret(#server_dh_params{dh_p = P, dh_g = G, dh_y = ServerPublicDh State) -> dh_master_secret(P, G, ServerPublicDhKey, undefined, State); +server_master_secret(#server_ecdh_params{curve = ECCurve, public = ECServerPubKey}, + State) -> + ECDHKeys = public_key:generate_key(ECCurve), + ec_dh_master_secret(ECDHKeys, #'ECPoint'{point = ECServerPubKey}, State#state{diffie_hellman_keys = ECDHKeys}); + server_master_secret(#server_psk_params{ hint = IdentityHint}, State) -> @@ -2000,17 +2086,23 @@ master_from_premaster_secret(PremasterSecret, Alert end. +dh_master_secret(#'DHParameter'{} = Params, OtherPublicDhKey, MyPrivateKey, State) -> + PremasterSecret = + public_key:compute_key(OtherPublicDhKey, MyPrivateKey, Params), + master_from_premaster_secret(PremasterSecret, State). + dh_master_secret(Prime, Base, PublicDhKey, undefined, State) -> - PMpint = mpint_binary(Prime), - GMpint = mpint_binary(Base), - Keys = {_, PrivateDhKey} = - crypto:dh_generate_key([PMpint,GMpint]), - dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey, State#state{diffie_hellman_keys = Keys}); + Keys = {_, PrivateDhKey} = crypto:generate_key(dh, [Prime, Base]), + dh_master_secret(Prime, Base, PublicDhKey, PrivateDhKey, State#state{diffie_hellman_keys = Keys}); + +dh_master_secret(Prime, Base, PublicDhKey, PrivateDhKey, State) -> + PremasterSecret = + crypto:compute_key(dh, PublicDhKey, PrivateDhKey, [Prime, Base]), + master_from_premaster_secret(PremasterSecret, State). -dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey, State) -> +ec_dh_master_secret(ECDHKeys, ECPoint, State) -> PremasterSecret = - crypto:dh_compute_key(mpint_binary(PublicDhKey), PrivateDhKey, - [PMpint, GMpint]), + public_key:compute_key(ECPoint, ECDHKeys), master_from_premaster_secret(PremasterSecret, State). handle_psk_identity(_PSKIdentity, LookupFun) @@ -2033,20 +2125,18 @@ server_psk_master_secret(ClientPSKIdentity, end. dhe_psk_master_secret(PSKIdentity, Prime, Base, PublicDhKey, undefined, State) -> - PMpint = mpint_binary(Prime), - GMpint = mpint_binary(Base), Keys = {_, PrivateDhKey} = - crypto:dh_generate_key([PMpint,GMpint]), - dhe_psk_master_secret(PSKIdentity, PMpint, GMpint, PublicDhKey, PrivateDhKey, + crypto:generate_key(dh, [Prime, Base]), + dhe_psk_master_secret(PSKIdentity, Prime, Base, PublicDhKey, PrivateDhKey, State#state{diffie_hellman_keys = Keys}); -dhe_psk_master_secret(PSKIdentity, PMpint, GMpint, PublicDhKey, PrivateDhKey, +dhe_psk_master_secret(PSKIdentity, Prime, Base, PublicDhKey, PrivateDhKey, #state{ssl_options = SslOpts} = State) -> case handle_psk_identity(PSKIdentity, SslOpts#ssl_options.user_lookup_fun) of {ok, PSK} when is_binary(PSK) -> DHSecret = - crypto:dh_compute_key(mpint_binary(PublicDhKey), PrivateDhKey, - [PMpint, GMpint]), + crypto:compute_key(dh, PublicDhKey, PrivateDhKey, + [Prime, Base]), DHLen = erlang:byte_size(DHSecret), Len = erlang:byte_size(PSK), PremasterSecret = <<?UINT16(DHLen), DHSecret/binary, ?UINT16(Len), PSK/binary>>, @@ -2075,7 +2165,7 @@ generate_srp_server_keys(_SrpParams, 10) -> generate_srp_server_keys(SrpParams = #srp_user{generator = Generator, prime = Prime, verifier = Verifier}, N) -> - case crypto:srp_generate_key(Verifier, Generator, Prime, '6a') of + case crypto:generate_key(srp, {host, [Verifier, Generator, Prime, '6a']}) of error -> generate_srp_server_keys(SrpParams, N+1); Keys -> @@ -2086,7 +2176,7 @@ generate_srp_client_keys(_Generator, _Prime, 10) -> ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); generate_srp_client_keys(Generator, Prime, N) -> - case crypto:srp_generate_key(Generator, Prime, '6a') of + case crypto:generate_key(srp, {user, [Generator, Prime, '6a']}) of error -> generate_srp_client_keys(Generator, Prime, N+1); Keys -> @@ -2098,7 +2188,7 @@ handle_srp_identity(Username, {Fun, UserState}) -> {ok, {SRPParams, Salt, DerivedKey}} when is_atom(SRPParams), is_binary(Salt), is_binary(DerivedKey) -> {Generator, Prime} = ssl_srp_primes:get_srp_params(SRPParams), - Verifier = crypto:mod_exp_prime(Generator, DerivedKey, Prime), + Verifier = crypto:mod_pow(Generator, DerivedKey, Prime), #srp_user{generator = Generator, prime = Prime, salt = Salt, verifier = Verifier}; #alert{} = Alert -> @@ -2107,8 +2197,8 @@ handle_srp_identity(Username, {Fun, UserState}) -> throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)) end. -server_srp_master_secret(Verifier, Prime, ClientPub, State = #state{srp_keys = {ServerPub, ServerPriv}}) -> - case crypto:srp_compute_key(Verifier, Prime, ClientPub, ServerPub, ServerPriv, '6a') of +server_srp_master_secret(Verifier, Prime, ClientPub, State = #state{srp_keys = ServerKeys}) -> + case crypto:compute_key(srp, ClientPub, ServerKeys, {host, [Verifier, Prime, '6a']}) of error -> ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); PremasterSecret -> @@ -2121,14 +2211,13 @@ client_srp_master_secret(Generator, Prime, Salt, ServerPub, undefined, State) -> Keys = generate_srp_client_keys(Generator, Prime, 0), client_srp_master_secret(Generator, Prime, Salt, ServerPub, Keys, State#state{srp_keys = Keys}); -client_srp_master_secret(Generator, Prime, Salt, ServerPub, {ClientPub, ClientPriv}, - #state{ssl_options = SslOpts} = State) -> +client_srp_master_secret(Generator, Prime, Salt, ServerPub, ClientKeys, + #state{ssl_options = SslOpts} = State) -> case ssl_srp_primes:check_srp_params(Generator, Prime) of ok -> {Username, Password} = SslOpts#ssl_options.srp_identity, DerivedKey = crypto:sha([Salt, crypto:sha([Username, <<$:>>, Password])]), - - case crypto:srp_compute_key(DerivedKey, Prime, Generator, ClientPub, ClientPriv, ServerPub, '6a') of + case crypto:compute_key(srp, ServerPub, ClientKeys, {user, [DerivedKey, Prime, Generator, '6a']}) of error -> ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); PremasterSecret -> @@ -2798,11 +2887,6 @@ make_premaster_secret({MajVer, MinVer}, rsa) -> make_premaster_secret(_, _) -> undefined. -mpint_binary(Binary) -> - Size = erlang:byte_size(Binary), - <<?UINT32(Size), Binary/binary>>. - - ack_connection(#state{renegotiation = {true, Initiater}} = State) when Initiater == internal; Initiater == peer -> @@ -2938,21 +3022,29 @@ default_hashsign(_Version = {Major, Minor}, KeyExchange) (KeyExchange == rsa orelse KeyExchange == dhe_rsa orelse KeyExchange == dh_rsa orelse + KeyExchange == ecdhe_rsa orelse KeyExchange == srp_rsa) -> {sha, rsa}; default_hashsign(_Version, KeyExchange) when KeyExchange == rsa; KeyExchange == dhe_rsa; KeyExchange == dh_rsa; + KeyExchange == ecdhe_rsa; KeyExchange == srp_rsa -> {md5sha, rsa}; default_hashsign(_Version, KeyExchange) + when KeyExchange == ecdhe_ecdsa; + KeyExchange == ecdh_ecdsa; + KeyExchange == ecdh_rsa -> + {sha, ecdsa}; +default_hashsign(_Version, KeyExchange) when KeyExchange == dhe_dss; KeyExchange == dh_dss; KeyExchange == srp_dss -> {sha, dsa}; default_hashsign(_Version, KeyExchange) when KeyExchange == dh_anon; + KeyExchange == ecdh_anon; KeyExchange == psk; KeyExchange == dhe_psk; KeyExchange == rsa_psk; @@ -2987,3 +3079,8 @@ handle_close_alert(Data, StateName, State0) -> _ -> ok end. + +select_curve(#state{client_ecc = {[Curve|_], _}}) -> + {namedCurve, Curve}; +select_curve(_) -> + {namedCurve, ?secp256k1}. diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 83c0092de2..e358cbe9bb 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -31,7 +31,7 @@ -include("ssl_srp.hrl"). -include_lib("public_key/include/public_key.hrl"). --export([master_secret/4, client_hello/8, server_hello/5, hello/4, +-export([master_secret/4, client_hello/8, server_hello/7, hello/4, hello_request/0, certify/7, certificate/4, client_certificate_verify/6, certificate_verify/6, verify_signature/5, certificate_request/3, key_exchange/3, server_key_exchange_hash/2, @@ -47,6 +47,8 @@ #client_key_exchange{} | #finished{} | #certificate_verify{} | #hello_request{} | #next_protocol{}. +-define(NAMED_CURVE_TYPE, 3). + %%==================================================================== %% Internal application API %%==================================================================== @@ -67,6 +69,7 @@ client_hello(Host, Port, ConnectionStates, SecParams = Pending#connection_state.security_parameters, Ciphers = available_suites(UserSuites, Version), SRP = srp_user(SslOpts), + {EcPointFormats, EllipticCurves} = default_ecc_extensions(Version), Id = ssl_session:client_id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert), @@ -80,6 +83,8 @@ client_hello(Host, Port, ConnectionStates, renegotiation_info(client, ConnectionStates, Renegotiation), srp = SRP, hash_signs = default_hash_signs(), + ec_point_formats = EcPointFormats, + elliptic_curves = EllipticCurves, next_protocol_negotiation = encode_client_protocol_negotiation(SslOpts#ssl_options.next_protocol_selector, Renegotiation) }. @@ -96,11 +101,14 @@ encode_protocols_advertised_on_server(Protocols) -> %%-------------------------------------------------------------------- -spec server_hello(session_id(), tls_version(), #connection_states{}, - boolean(), [binary()] | undefined) -> #server_hello{}. + boolean(), [binary()] | undefined, + #ec_point_formats{} | undefined, + #elliptic_curves{} | undefined) -> #server_hello{}. %% %% Description: Creates a server hello message. %%-------------------------------------------------------------------- -server_hello(SessionId, Version, ConnectionStates, Renegotiation, ProtocolsAdvertisedOnServer) -> +server_hello(SessionId, Version, ConnectionStates, Renegotiation, + ProtocolsAdvertisedOnServer, EcPointFormats, EllipticCurves) -> Pending = ssl_record:pending_connection_state(ConnectionStates, read), SecParams = Pending#connection_state.security_parameters, #server_hello{server_version = Version, @@ -111,6 +119,8 @@ server_hello(SessionId, Version, ConnectionStates, Renegotiation, ProtocolsAdver session_id = SessionId, renegotiation_info = renegotiation_info(server, ConnectionStates, Renegotiation), + ec_point_formats = EcPointFormats, + elliptic_curves = EllipticCurves, next_protocol_negotiation = encode_protocols_advertised_on_server(ProtocolsAdvertisedOnServer) }. @@ -129,7 +139,8 @@ hello_request() -> atom(), #connection_states{}, binary()}, boolean()) -> {tls_version(), session_id(), #connection_states{}, binary() | undefined}| - {tls_version(), {resumed | new, #session{}}, #connection_states{}, list(binary()) | undefined} | + {tls_version(), {resumed | new, #session{}}, #connection_states{}, [binary()] | undefined, + [oid()] | undefined, [oid()] | undefined} | #alert{}. %% %% Description: Handles a recieved hello message @@ -163,44 +174,27 @@ hello(#server_hello{cipher_suite = CipherSuite, server_version = Version, ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION) end; -hello(#client_hello{client_version = ClientVersion, random = Random, - cipher_suites = CipherSuites, - renegotiation_info = Info, - srp = SRP} = Hello, - #ssl_options{versions = Versions, - secure_renegotiate = SecureRenegotation} = SslOpts, +hello(#client_hello{client_version = ClientVersion} = Hello, + #ssl_options{versions = Versions} = SslOpts, {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert}, Renegotiation) -> -%% TODO: select hash and signature algorithm + %% TODO: select hash and signature algorithm Version = select_version(ClientVersion, Versions), case ssl_record:is_acceptable_version(Version, Versions) of true -> - {Type, #session{cipher_suite = CipherSuite, - compression_method = Compression} = Session1} + %% TODO: need to take supported Curves into Account when selecting the CipherSuite.... + %% if whe have an ECDSA cert with an unsupported curve, we need to drop ECDSA ciphers + {Type, #session{cipher_suite = CipherSuite} = Session1} = select_session(Hello, Port, Session0, Version, SslOpts, Cache, CacheCb, Cert), case CipherSuite of no_suite -> ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY); _ -> - Session = handle_srp_info(SRP, Session1), - case handle_renegotiation_info(server, Info, ConnectionStates0, - Renegotiation, SecureRenegotation, - CipherSuites) of - {ok, ConnectionStates1} -> - ConnectionStates = - hello_pending_connection_states(server, - Version, - CipherSuite, - Random, - Compression, - ConnectionStates1), - case handle_next_protocol_on_server(Hello, Renegotiation, SslOpts) of - #alert{} = Alert -> - Alert; - ProtocolsToAdvertise -> - {Version, {Type, Session}, ConnectionStates, ProtocolsToAdvertise} - end; - #alert{} = Alert -> + try handle_hello_extensions(Hello, Version, SslOpts, Session1, ConnectionStates0, Renegotiation) of + {Session, ConnectionStates, ProtocolsToAdvertise, ECPointFormats, EllipticCurves} -> + {Version, {Type, Session}, ConnectionStates, + ProtocolsToAdvertise, ECPointFormats, EllipticCurves} + catch throw:Alert -> Alert end end; @@ -350,9 +344,10 @@ verify_signature(_Version, Hash, _HashAlgo, Signature, {?rsaEncryption, PubKey, _ -> false end; verify_signature(_Version, Hash, {HashAlgo, dsa}, Signature, {?'id-dsa', PublicKey, PublicKeyParams}) -> + public_key:verify({digest, Hash}, HashAlgo, Signature, {PublicKey, PublicKeyParams}); +verify_signature(_Version, Hash, {HashAlgo, ecdsa}, Signature, {?'id-ecPublicKey', PublicKey, PublicKeyParams}) -> public_key:verify({digest, Hash}, HashAlgo, Signature, {PublicKey, PublicKeyParams}). - %%-------------------------------------------------------------------- -spec certificate_request(#connection_states{}, db_handle(), certdb_ref()) -> #certificate_request{}. @@ -378,6 +373,7 @@ certificate_request(ConnectionStates, CertDbHandle, CertDbRef) -> {dh, binary()} | {dh, {binary(), binary()}, #'DHParameter'{}, {HashAlgo::atom(), SignAlgo::atom()}, binary(), binary(), private_key()} | + {ecdh, #'ECPrivateKey'{}} | {psk, binary()} | {dhe_psk, binary(), binary()} | {srp, {binary(), binary()}, #srp_user{}, {HashAlgo::atom(), SignAlgo::atom()}, @@ -391,19 +387,25 @@ key_exchange(client, _Version, {premaster_secret, Secret, {_, PublicKey, _}}) -> encrypted_premaster_secret(Secret, PublicKey), #client_key_exchange{exchange_keys = EncPremasterSecret}; -key_exchange(client, _Version, {dh, <<?UINT32(Len), PublicKey:Len/binary>>}) -> +key_exchange(client, _Version, {dh, PublicKey}) -> #client_key_exchange{ exchange_keys = #client_diffie_hellman_public{ dh_public = PublicKey} }; +key_exchange(client, _Version, {ecdh, #'ECPrivateKey'{publicKey = {0, ECPublicKey}}}) -> + #client_key_exchange{ + exchange_keys = #client_ec_diffie_hellman_public{ + dh_public = ECPublicKey} + }; + key_exchange(client, _Version, {psk, Identity}) -> #client_key_exchange{ exchange_keys = #client_psk_identity{ identity = Identity} }; -key_exchange(client, _Version, {dhe_psk, Identity, <<?UINT32(Len), PublicKey:Len/binary>>}) -> +key_exchange(client, _Version, {dhe_psk, Identity, PublicKey}) -> #client_key_exchange{ exchange_keys = #client_dhe_psk_identity{ identity = Identity, @@ -415,7 +417,7 @@ key_exchange(client, _Version, {psk_premaster_secret, PskIdentity, Secret, {_, P encrypted_premaster_secret(Secret, PublicKey), #client_key_exchange{ exchange_keys = #client_rsa_psk_identity{ - identity = PskIdentity, + identity = PskIdentity, exchange_keys = EncPremasterSecret}}; key_exchange(client, _Version, {srp, PublicKey}) -> @@ -424,31 +426,34 @@ key_exchange(client, _Version, {srp, PublicKey}) -> srp_a = PublicKey} }; -key_exchange(server, Version, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _}, - #'DHParameter'{prime = P, base = G}, - HashSign, ClientRandom, ServerRandom, PrivateKey}) -> - <<?UINT32(_), PBin/binary>> = crypto:mpint(P), - <<?UINT32(_), GBin/binary>> = crypto:mpint(G), - ServerDHParams = #server_dh_params{dh_p = PBin, - dh_g = GBin, dh_y = PublicKey}, +key_exchange(server, Version, {dh, {PublicKey, _}, + #'DHParameter'{prime = P, base = G}, + HashSign, ClientRandom, ServerRandom, PrivateKey}) -> + ServerDHParams = #server_dh_params{dh_p = int_to_bin(P), + dh_g = int_to_bin(G), dh_y = PublicKey}, enc_server_key_exchange(Version, ServerDHParams, HashSign, ClientRandom, ServerRandom, PrivateKey); +key_exchange(server, Version, {ecdh, #'ECPrivateKey'{publicKey = {0, ECPublicKey}, + parameters = ECCurve}, HashSign, ClientRandom, ServerRandom, + PrivateKey}) -> + ServerECParams = #server_ecdh_params{curve = ECCurve, public = ECPublicKey}, + enc_server_key_exchange(Version, ServerECParams, HashSign, + ClientRandom, ServerRandom, PrivateKey); + key_exchange(server, Version, {psk, PskIdentityHint, HashSign, ClientRandom, ServerRandom, PrivateKey}) -> ServerPSKParams = #server_psk_params{hint = PskIdentityHint}, enc_server_key_exchange(Version, ServerPSKParams, HashSign, ClientRandom, ServerRandom, PrivateKey); -key_exchange(server, Version, {dhe_psk, PskIdentityHint, {<<?UINT32(Len), PublicKey:Len/binary>>, _}, +key_exchange(server, Version, {dhe_psk, PskIdentityHint, {PublicKey, _}, #'DHParameter'{prime = P, base = G}, HashSign, ClientRandom, ServerRandom, PrivateKey}) -> - <<?UINT32(_), PBin/binary>> = crypto:mpint(P), - <<?UINT32(_), GBin/binary>> = crypto:mpint(G), ServerEDHPSKParams = #server_dhe_psk_params{ hint = PskIdentityHint, - dh_params = #server_dh_params{dh_p = PBin, - dh_g = GBin, dh_y = PublicKey} + dh_params = #server_dh_params{dh_p = int_to_bin(P), + dh_g = int_to_bin(G), dh_y = PublicKey} }, enc_server_key_exchange(Version, ServerEDHPSKParams, HashSign, ClientRandom, ServerRandom, PrivateKey); @@ -591,6 +596,7 @@ get_tls_handshake(Version, Data, Buffer) -> -spec decode_client_key(binary(), key_algo(), tls_version()) -> #encrypted_premaster_secret{} | #client_diffie_hellman_public{} + | #client_ec_diffie_hellman_public{} | #client_psk_identity{} | #client_dhe_psk_identity{} | #client_rsa_psk_identity{} @@ -661,8 +667,8 @@ decrypt_premaster_secret(Secret, RSAPrivateKey) -> %% Description: Calculate server key exchange hash %%-------------------------------------------------------------------- server_key_exchange_hash(md5sha, Value) -> - MD5 = crypto:md5(Value), - SHA = crypto:sha(Value), + MD5 = crypto:hash(md5, Value), + SHA = crypto:hash(sha, Value), <<MD5/binary, SHA/binary>>; server_key_exchange_hash(Hash, Value) -> @@ -833,10 +839,35 @@ select_next_protocol(Protocols, NextProtocolSelector) -> Protocol end. -handle_srp_info(undefined, Session) -> - Session; -handle_srp_info(#srp{username = Username}, Session) -> - Session#session{srp_username = Username}. +default_ecc_extensions(Version) -> + case proplists:get_bool(ec, crypto:algorithms()) of + true -> + EcPointFormats = #ec_point_formats{ec_point_format_list = [?ECPOINT_UNCOMPRESSED]}, + EllipticCurves = #elliptic_curves{elliptic_curve_list = ssl_tls1:ecc_curves(Version)}, + {EcPointFormats, EllipticCurves}; + _ -> + {undefined, undefined} + end. + +handle_ecc_extensions(Version, EcPointFormats0, EllipticCurves0) -> + case proplists:get_bool(ec, crypto:algorithms()) of + true -> + EcPointFormats1 = handle_ecc_point_fmt_extension(EcPointFormats0), + EllipticCurves1 = handle_ecc_curves_extension(Version, EllipticCurves0), + {EcPointFormats1, EllipticCurves1}; + _ -> + {undefined, undefined} + end. + +handle_ecc_point_fmt_extension(undefined) -> + undefined; +handle_ecc_point_fmt_extension(_) -> + #ec_point_formats{ec_point_format_list = [?ECPOINT_UNCOMPRESSED]}. + +handle_ecc_curves_extension(Version, undefined) -> + undefined; +handle_ecc_curves_extension(Version, _) -> + #elliptic_curves{elliptic_curve_list = ssl_tls1:ecc_curves(Version)}. handle_renegotiation_info(_, #renegotiation_info{renegotiated_connection = ?byte(0)}, ConnectionStates, false, _, _) -> @@ -1022,6 +1053,8 @@ dec_hs(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, RenegotiationInfo = proplists:get_value(renegotiation_info, DecodedExtensions, undefined), SRP = proplists:get_value(srp, DecodedExtensions, undefined), HashSigns = proplists:get_value(hash_signs, DecodedExtensions, undefined), + EllipticCurves = proplists:get_value(elliptic_curves, DecodedExtensions, + undefined), NextProtocolNegotiation = proplists:get_value(next_protocol_negotiation, DecodedExtensions, undefined), #client_hello{ @@ -1033,6 +1066,7 @@ dec_hs(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, renegotiation_info = RenegotiationInfo, srp = SRP, hash_signs = HashSigns, + elliptic_curves = EllipticCurves, next_protocol_negotiation = NextProtocolNegotiation }; @@ -1046,7 +1080,8 @@ dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, cipher_suite = Cipher_suite, compression_method = Comp_method, renegotiation_info = undefined, - hash_signs = undefined}; + hash_signs = undefined, + elliptic_curves = undefined}; dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SID_length), Session_ID:SID_length/binary, @@ -1058,6 +1093,8 @@ dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, undefined), HashSigns = proplists:get_value(hash_signs, HelloExtensions, undefined), + EllipticCurves = proplists:get_value(elliptic_curves, HelloExtensions, + undefined), NextProtocolNegotiation = proplists:get_value(next_protocol_negotiation, HelloExtensions, undefined), #server_hello{ @@ -1068,6 +1105,7 @@ dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, compression_method = Comp_method, renegotiation_info = RenegotiationInfo, hash_signs = HashSigns, + elliptic_curves = EllipticCurves, next_protocol_negotiation = NextProtocolNegotiation}; dec_hs(_Version, ?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>) -> #certificate{asn1_certificates = certs_to_list(ASN1Certs)}; @@ -1111,6 +1149,11 @@ dec_client_key(<<>>, ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) -> dec_client_key(<<?UINT16(DH_YLen), DH_Y:DH_YLen/binary>>, ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) -> #client_diffie_hellman_public{dh_public = DH_Y}; +dec_client_key(<<>>, ?KEY_EXCHANGE_EC_DIFFIE_HELLMAN, _) -> + throw(?ALERT_REC(?FATAL, ?UNSUPPORTED_CERTIFICATE)); +dec_client_key(<<?BYTE(DH_YLen), DH_Y:DH_YLen/binary>>, + ?KEY_EXCHANGE_EC_DIFFIE_HELLMAN, _) -> + #client_ec_diffie_hellman_public{dh_public = DH_Y}; dec_client_key(<<?UINT16(Len), Id:Len/binary>>, ?KEY_EXCHANGE_PSK, _) -> #client_psk_identity{identity = Id}; @@ -1161,6 +1204,19 @@ dec_server_key(<<?UINT16(PLen), P:PLen/binary, params_bin = BinMsg, hashsign = HashSign, signature = Signature}; +%% ECParameters with named_curve +%% TODO: explicit curve +dec_server_key(<<?BYTE(?NAMED_CURVE), ?UINT16(CurveID), + ?BYTE(PointLen), ECPoint:PointLen/binary, + _/binary>> = KeyStruct, + ?KEY_EXCHANGE_EC_DIFFIE_HELLMAN, Version) -> + Params = #server_ecdh_params{curve = {namedCurve, ssl_tls1:enum_to_oid(CurveID)}, + public = ECPoint}, + {BinMsg, HashSign, Signature} = dec_ske_params(PointLen + 4, KeyStruct, Version), + #server_key_params{params = Params, + params_bin = BinMsg, + hashsign = HashSign, + signature = Signature}; dec_server_key(<<?UINT16(Len), PskIdentityHint:Len/binary>> = KeyStruct, KeyExchange, Version) when KeyExchange == ?KEY_EXCHANGE_PSK; KeyExchange == ?KEY_EXCHANGE_RSA_PSK -> @@ -1237,6 +1293,22 @@ dec_hello_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len), dec_hello_extensions(Rest, [{hash_signs, #hash_sign_algos{hash_sign_algos = HashSignAlgos}} | Acc]); +dec_hello_extensions(<<?UINT16(?ELLIPTIC_CURVES_EXT), ?UINT16(Len), + ExtData:Len/binary, Rest/binary>>, Acc) -> + EllipticCurveListLen = Len - 2, + <<?UINT16(EllipticCurveListLen), EllipticCurveList/binary>> = ExtData, + EllipticCurves = [ssl_tls1:enum_to_oid(X) || <<X:16>> <= EllipticCurveList], + dec_hello_extensions(Rest, [{elliptic_curves, + #elliptic_curves{elliptic_curve_list = EllipticCurves}} | Acc]); + +dec_hello_extensions(<<?UINT16(?EC_POINT_FORMATS_EXT), ?UINT16(Len), + ExtData:Len/binary, Rest/binary>>, Acc) -> + ECPointFormatListLen = Len - 1, + <<?BYTE(ECPointFormatListLen), ECPointFormatList/binary>> = ExtData, + ECPointFormats = binary_to_list(ECPointFormatList), + dec_hello_extensions(Rest, [{ec_point_formats, + #ec_point_formats{ec_point_format_list = ECPointFormats}} | Acc]); + %% Ignore data following the ClientHello (i.e., %% extensions) if not understood. @@ -1287,13 +1359,17 @@ enc_hs(#client_hello{client_version = {Major, Minor}, renegotiation_info = RenegotiationInfo, srp = SRP, hash_signs = HashSigns, + ec_point_formats = EcPointFormats, + elliptic_curves = EllipticCurves, next_protocol_negotiation = NextProtocolNegotiation}, _Version) -> SIDLength = byte_size(SessionID), BinCompMethods = list_to_binary(CompMethods), CmLength = byte_size(BinCompMethods), BinCipherSuites = list_to_binary(CipherSuites), CsLength = byte_size(BinCipherSuites), - Extensions0 = hello_extensions(RenegotiationInfo, SRP, NextProtocolNegotiation), + Extensions0 = hello_extensions(RenegotiationInfo, SRP, NextProtocolNegotiation) + ++ ec_hello_extensions(lists:map(fun ssl_cipher:suite_definition/1, CipherSuites), EcPointFormats) + ++ ec_hello_extensions(lists:map(fun ssl_cipher:suite_definition/1, CipherSuites), EllipticCurves), Extensions1 = if Major == 3, Minor >=3 -> Extensions0 ++ hello_extensions(HashSigns); true -> Extensions0 @@ -1308,16 +1384,21 @@ enc_hs(#client_hello{client_version = {Major, Minor}, enc_hs(#server_hello{server_version = {Major, Minor}, random = Random, session_id = Session_ID, - cipher_suite = Cipher_suite, + cipher_suite = CipherSuite, compression_method = Comp_method, renegotiation_info = RenegotiationInfo, + ec_point_formats = EcPointFormats, + elliptic_curves = EllipticCurves, next_protocol_negotiation = NextProtocolNegotiation}, _Version) -> SID_length = byte_size(Session_ID), - Extensions = hello_extensions(RenegotiationInfo, NextProtocolNegotiation), + CipherSuites = [ssl_cipher:suite_definition(CipherSuite)], + Extensions = hello_extensions(RenegotiationInfo, NextProtocolNegotiation) + ++ ec_hello_extensions(CipherSuites, EcPointFormats) + ++ ec_hello_extensions(CipherSuites, EllipticCurves), ExtensionsBin = enc_hello_extensions(Extensions), {?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SID_length), Session_ID/binary, - Cipher_suite/binary, ?BYTE(Comp_method), ExtensionsBin/binary>>}; + CipherSuite/binary, ?BYTE(Comp_method), ExtensionsBin/binary>>}; enc_hs(#certificate{asn1_certificates = ASN1CertList}, _Version) -> ASN1Certs = certs_from_list(ASN1CertList), ACLen = erlang:iolist_size(ASN1Certs), @@ -1370,6 +1451,9 @@ enc_cke(#encrypted_premaster_secret{premaster_secret = PKEPMS}, _) -> enc_cke(#client_diffie_hellman_public{dh_public = DHPublic}, _) -> Len = byte_size(DHPublic), <<?UINT16(Len), DHPublic/binary>>; +enc_cke(#client_ec_diffie_hellman_public{dh_public = DHPublic}, _) -> + Len = byte_size(DHPublic), + <<?BYTE(Len), DHPublic/binary>>; enc_cke(#client_psk_identity{identity = undefined}, _) -> Id = <<"psk_identity">>, Len = byte_size(Id), @@ -1398,6 +1482,11 @@ enc_server_key(#server_dh_params{dh_p = P, dh_g = G, dh_y = Y}) -> GLen = byte_size(G), YLen = byte_size(Y), <<?UINT16(PLen), P/binary, ?UINT16(GLen), G/binary, ?UINT16(YLen), Y/binary>>; +enc_server_key(#server_ecdh_params{curve = {namedCurve, ECCurve}, public = ECPubKey}) -> + %%TODO: support arbitrary keys + KLen = size(ECPubKey), + <<?BYTE(?NAMED_CURVE_TYPE), ?UINT16((ssl_tls1:oid_to_enum(ECCurve))), + ?BYTE(KLen), ECPubKey/binary>>; enc_server_key(#server_psk_params{hint = PskIdentityHint}) -> Len = byte_size(PskIdentityHint), <<?UINT16(Len), PskIdentityHint/binary>>; @@ -1431,11 +1520,46 @@ enc_sign(_HashSign, Sign, _Version) -> SignLen = byte_size(Sign), <<?UINT16(SignLen), Sign/binary>>. + +ec_hello_extensions(CipherSuites, #elliptic_curves{} = Info) -> + case advertises_ec_ciphers(CipherSuites) of + true -> + [Info]; + false -> + [] + end; +ec_hello_extensions(CipherSuites, #ec_point_formats{} = Info) -> + case advertises_ec_ciphers(CipherSuites) of + true -> + [Info]; + false -> + [] + end; +ec_hello_extensions(_, undefined) -> + []. + hello_extensions(RenegotiationInfo, NextProtocolNegotiation) -> hello_extensions(RenegotiationInfo) ++ next_protocol_extension(NextProtocolNegotiation). hello_extensions(RenegotiationInfo, SRP, NextProtocolNegotiation) -> - hello_extensions(RenegotiationInfo) ++ hello_extensions(SRP) ++ next_protocol_extension(NextProtocolNegotiation). + hello_extensions(RenegotiationInfo) + ++ hello_extensions(SRP) + ++ next_protocol_extension(NextProtocolNegotiation). + +advertises_ec_ciphers([]) -> + false; +advertises_ec_ciphers([{ecdh_ecdsa, _,_,_} | _]) -> + true; +advertises_ec_ciphers([{ecdhe_ecdsa, _,_,_} | _]) -> + true; +advertises_ec_ciphers([{ecdh_rsa, _,_,_} | _]) -> + true; +advertises_ec_ciphers([{ecdhe_rsa, _,_,_} | _]) -> + true; +advertises_ec_ciphers([{ecdh_anon, _,_,_} | _]) -> + true; +advertises_ec_ciphers([_| Rest]) -> + advertises_ec_ciphers(Rest). %% Renegotiation info hello_extensions(#renegotiation_info{renegotiated_connection = undefined}) -> @@ -1473,12 +1597,22 @@ enc_hello_extensions([#renegotiation_info{renegotiated_connection = Info} | Rest InfoLen = byte_size(Info), Len = InfoLen +1, enc_hello_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), ?BYTE(InfoLen), Info/binary, Acc/binary>>); - +enc_hello_extensions([#elliptic_curves{elliptic_curve_list = EllipticCurves} | Rest], Acc) -> + EllipticCurveList = << <<(ssl_tls1:oid_to_enum(X)):16>> || X <- EllipticCurves>>, + ListLen = byte_size(EllipticCurveList), + Len = ListLen + 2, + enc_hello_extensions(Rest, <<?UINT16(?ELLIPTIC_CURVES_EXT), + ?UINT16(Len), ?UINT16(ListLen), EllipticCurveList/binary, Acc/binary>>); +enc_hello_extensions([#ec_point_formats{ec_point_format_list = ECPointFormats} | Rest], Acc) -> + ECPointFormatList = list_to_binary(ECPointFormats), + ListLen = byte_size(ECPointFormatList), + Len = ListLen + 1, + enc_hello_extensions(Rest, <<?UINT16(?EC_POINT_FORMATS_EXT), + ?UINT16(Len), ?BYTE(ListLen), ECPointFormatList/binary, Acc/binary>>); enc_hello_extensions([#srp{username = UserName} | Rest], Acc) -> SRPLen = byte_size(UserName), Len = SRPLen + 2, enc_hello_extensions(Rest, <<?UINT16(?SRP_EXT), ?UINT16(Len), ?BYTE(SRPLen), UserName/binary, Acc/binary>>); - enc_hello_extensions([#hash_sign_algos{hash_sign_algos = HashSignAlgos} | Rest], Acc) -> SignAlgoList = << <<(ssl_cipher:hash_algorithm(Hash)):8, (ssl_cipher:sign_algorithm(Sign)):8>> || {Hash, Sign} <- HashSignAlgos >>, @@ -1513,9 +1647,15 @@ from_2bytes(<<?UINT16(N), Rest/binary>>, Acc) -> certificate_types({KeyExchange, _, _, _}) when KeyExchange == rsa; KeyExchange == dhe_dss; - KeyExchange == dhe_rsa -> + KeyExchange == dhe_rsa; + KeyExchange == ecdhe_rsa -> <<?BYTE(?RSA_SIGN), ?BYTE(?DSS_SIGN)>>; +certificate_types({KeyExchange, _, _, _}) + when KeyExchange == dh_ecdsa; + KeyExchange == dhe_ecdsa -> + <<?BYTE(?ECDSA_SIGN)>>; + certificate_types(_) -> <<?BYTE(?RSA_SIGN)>>. @@ -1532,9 +1672,6 @@ certificate_authorities(CertDbHandle, CertDbRef) -> Enc = fun(#'OTPCertificate'{tbsCertificate=TBSCert}) -> OTPSubj = TBSCert#'OTPTBSCertificate'.subject, DNEncodedBin = public_key:pkix_encode('Name', OTPSubj, otp), - %%Subj = public_key:pkix_transform(OTPSubj, encode), - %% {ok, DNEncoded} = 'OTP-PUB-KEY':encode('Name', Subj), - %% DNEncodedBin = iolist_to_binary(DNEncoded), DNEncodedLen = byte_size(DNEncodedBin), <<?UINT16(DNEncodedLen), DNEncodedBin/binary>> end, @@ -1555,7 +1692,9 @@ digitally_signed(_Version, Hash, HashAlgo, #'DSAPrivateKey'{} = Key) -> public_key:sign({digest, Hash}, HashAlgo, Key); digitally_signed(_Version, Hash, _HashAlgo, #'RSAPrivateKey'{} = Key) -> public_key:encrypt_private(Hash, Key, - [{rsa_pad, rsa_pkcs1_padding}]). + [{rsa_pad, rsa_pkcs1_padding}]); +digitally_signed(_Version, Hash, HashAlgo, Key) -> + public_key:sign({digest, Hash}, HashAlgo, Key). calc_master_secret({3,0}, _PrfAlgo, PremasterSecret, ClientRandom, ServerRandom) -> ssl_ssl3:master_secret(PremasterSecret, ClientRandom, ServerRandom); @@ -1588,6 +1727,10 @@ key_exchange_alg(rsa) -> key_exchange_alg(Alg) when Alg == dhe_rsa; Alg == dhe_dss; Alg == dh_dss; Alg == dh_rsa; Alg == dh_anon -> ?KEY_EXCHANGE_DIFFIE_HELLMAN; +key_exchange_alg(Alg) when Alg == ecdhe_rsa; Alg == ecdh_rsa; + Alg == ecdhe_ecdsa; Alg == ecdh_ecdsa; + Alg == ecdh_anon -> + ?KEY_EXCHANGE_EC_DIFFIE_HELLMAN; key_exchange_alg(psk) -> ?KEY_EXCHANGE_PSK; key_exchange_alg(dhe_psk) -> @@ -1612,15 +1755,70 @@ apply_user_fun(Fun, OtpCert, ExtensionOrError, UserState0, SslState) -> -define(TLSEXT_SIGALG_RSA(MD), {MD, rsa}). -define(TLSEXT_SIGALG_DSA(MD), {MD, dsa}). +-define(TLSEXT_SIGALG_ECDSA(MD), {MD, ecdsa}). --define(TLSEXT_SIGALG(MD), ?TLSEXT_SIGALG_RSA(MD)). +-define(TLSEXT_SIGALG(MD), ?TLSEXT_SIGALG_ECDSA(MD), ?TLSEXT_SIGALG_RSA(MD)). default_hash_signs() -> + HashSigns = [?TLSEXT_SIGALG(sha512), + ?TLSEXT_SIGALG(sha384), + ?TLSEXT_SIGALG(sha256), + ?TLSEXT_SIGALG(sha224), + ?TLSEXT_SIGALG(sha), + ?TLSEXT_SIGALG_DSA(sha), + ?TLSEXT_SIGALG_RSA(md5)], + HasECC = proplists:get_bool(ec, crypto:algorithms()), #hash_sign_algos{hash_sign_algos = - [?TLSEXT_SIGALG(sha512), - ?TLSEXT_SIGALG(sha384), - ?TLSEXT_SIGALG(sha256), - ?TLSEXT_SIGALG(sha224), - ?TLSEXT_SIGALG(sha), - ?TLSEXT_SIGALG_DSA(sha), - ?TLSEXT_SIGALG_RSA(md5)]}. + lists:filter(fun({_, ecdsa}) -> HasECC; + (_) -> true end, HashSigns)}. + +handle_hello_extensions(#client_hello{random = Random, + cipher_suites = CipherSuites, + renegotiation_info = Info, + srp = SRP, + ec_point_formats = EcPointFormats0, + elliptic_curves = EllipticCurves0} = Hello, Version, + #ssl_options{secure_renegotiate = SecureRenegotation} = Opts, + Session0, ConnectionStates0, Renegotiation) -> + Session = handle_srp_extension(SRP, Session0), + ConnectionStates = handle_renegotiation_extension(Version, Info, Random, Session, ConnectionStates0, + Renegotiation, SecureRenegotation, CipherSuites), + ProtocolsToAdvertise = handle_next_protocol_extension(Hello, Renegotiation, Opts), + {EcPointFormats, EllipticCurves} = handle_ecc_extensions(Version, EcPointFormats0, EllipticCurves0), + %%TODO make extensions compund data structure + {Session, ConnectionStates, ProtocolsToAdvertise, EcPointFormats, EllipticCurves}. + + +handle_renegotiation_extension(Version, Info, Random, #session{cipher_suite = CipherSuite, + compression_method = Compression}, + ConnectionStates0, Renegotiation, SecureRenegotation, CipherSuites) -> + case handle_renegotiation_info(server, Info, ConnectionStates0, + Renegotiation, SecureRenegotation, + CipherSuites) of + {ok, ConnectionStates1} -> + hello_pending_connection_states(server, + Version, + CipherSuite, + Random, + Compression, + ConnectionStates1); + #alert{} = Alert -> + throw(Alert) + end. + +handle_next_protocol_extension(Hello, Renegotiation, SslOpts)-> + case handle_next_protocol_on_server(Hello, Renegotiation, SslOpts) of + #alert{} = Alert -> + throw(Alert); + ProtocolsToAdvertise -> + ProtocolsToAdvertise + end. + +handle_srp_extension(undefined, Session) -> + Session; +handle_srp_extension(#srp{username = Username}, Session) -> + Session#session{srp_username = Username}. + +int_to_bin(I) -> + L = (length(integer_to_list(I, 16)) + 1) div 2, + <<I:(L*8)>>. diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl index 1fbb88f5f6..b2387a0ee7 100644 --- a/lib/ssl/src/ssl_handshake.hrl +++ b/lib/ssl/src/ssl_handshake.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -28,9 +28,9 @@ -include_lib("public_key/include/public_key.hrl"). --type algo_oid() :: ?'rsaEncryption' | ?'id-dsa'. --type public_key_params() :: #'Dss-Parms'{} | term(). --type public_key_info() :: {algo_oid(), #'RSAPublicKey'{} | integer() , public_key_params()}. +-type oid() :: tuple(). +-type public_key_params() :: #'Dss-Parms'{} | {namedCurve, oid()} | #'ECParameters'{} | term(). +-type public_key_info() :: {oid(), #'RSAPublicKey'{} | integer() | #'ECPoint'{}, public_key_params()}. -type tls_handshake_history() :: {[binary()], [binary()]}. -define(NO_PROTOCOL, <<>>). @@ -102,6 +102,8 @@ renegotiation_info, srp, % srp username to send hash_signs, % supported combinations of hashes/signature algos + ec_point_formats, % supported ec point formats + elliptic_curves, % supported elliptic curver next_protocol_negotiation = undefined % [binary()] }). @@ -113,6 +115,8 @@ compression_method, % compression_method renegotiation_info, hash_signs, % supported combinations of hashes/signature algos + ec_point_formats, % supported ec point formats + elliptic_curves, % supported elliptic curver next_protocol_negotiation = undefined % [binary()] }). @@ -130,6 +134,7 @@ -define(KEY_EXCHANGE_RSA, 0). -define(KEY_EXCHANGE_DIFFIE_HELLMAN, 1). +-define(KEY_EXCHANGE_EC_DIFFIE_HELLMAN, 6). -define(KEY_EXCHANGE_PSK, 2). -define(KEY_EXCHANGE_DHE_PSK, 3). -define(KEY_EXCHANGE_RSA_PSK, 4). @@ -146,6 +151,11 @@ dh_y %% opaque DH_Ys<1..2^16-1> }). +-record(server_ecdh_params, { + curve, + public %% opaque encoded ECpoint + }). + -record(server_psk_params, { hint }). @@ -195,6 +205,9 @@ -define(DSS_SIGN, 2). -define(RSA_FIXED_DH, 3). -define(DSS_FIXED_DH, 4). +-define(ECDSA_SIGN, 64). +-define(RSA_FIXED_ECDH, 65). +-define(ECDSA_FIXED_ECDH, 66). % opaque DistinguishedName<1..2^16-1>; @@ -231,6 +244,10 @@ dh_public }). +-record(client_ec_diffie_hellman_public, { + dh_public + }). + -record(client_psk_identity, { identity }). @@ -304,6 +321,33 @@ -record(next_protocol, {selected_protocol}). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% ECC Extensions RFC 4492 section 4 and 5 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-define(ELLIPTIC_CURVES_EXT, 10). +-define(EC_POINT_FORMATS_EXT, 11). + +-record(elliptic_curves, { + elliptic_curve_list + }). + +-record(ec_point_formats, { + ec_point_format_list + }). + +-define(ECPOINT_UNCOMPRESSED, 0). +-define(ECPOINT_ANSIX962_COMPRESSED_PRIME, 1). +-define(ECPOINT_ANSIX962_COMPRESSED_CHAR2, 2). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% ECC RFC 4492 Handshake Messages, Section 5 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-define(EXPLICIT_PRIME, 1). +-define(EXPLICIT_CHAR2, 2). +-define(NAMED_CURVE, 3). + -endif. % -ifdef(ssl_handshake). diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index 96a1c8e1ce..14db4a6067 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -37,9 +37,9 @@ -type tls_atom_version() :: sslv3 | tlsv1 | 'tlsv1.1' | 'tlsv1.2'. -type certdb_ref() :: reference(). -type db_handle() :: term(). --type key_algo() :: null | rsa | dhe_rsa | dhe_dss | dh_anon. +-type key_algo() :: null | rsa | dhe_rsa | dhe_dss | ecdhe_ecdsa| ecdh_ecdsa | ecdh_rsa| srp_rsa| srp_dss | psk | dhe_psk | rsa_psk | dh_anon | ecdh_anon | srp_anon. -type der_cert() :: binary(). --type private_key() :: #'RSAPrivateKey'{} | #'DSAPrivateKey'{}. +-type private_key() :: #'RSAPrivateKey'{} | #'DSAPrivateKey'{} | #'ECPrivateKey'{}. -type issuer() :: tuple(). -type serialnumber() :: integer(). -type cert_key() :: {reference(), integer(), issuer()}. diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index aa9da65bb8..caea528a08 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -103,7 +103,7 @@ connection_init(Trustedcerts, Role) -> %% Description: Cach a pem file and return its content. %%-------------------------------------------------------------------- cache_pem_file(File, DbHandle) -> - MD5 = crypto:md5(File), + MD5 = crypto:hash(md5, File), case ssl_certificate_db:lookup_cached_pem(DbHandle, MD5) of [{Content,_}] -> {ok, Content}; @@ -468,7 +468,7 @@ new_id(Port, Tries, Cache, CacheCb) -> clean_cert_db(Ref, CertDb, RefDb, PemCache, File) -> case ssl_certificate_db:ref_count(Ref, RefDb, 0) of 0 -> - MD5 = crypto:md5(File), + MD5 = crypto:hash(md5, File), case ssl_certificate_db:lookup_cached_pem(PemCache, MD5) of [{Content, Ref}] -> ssl_certificate_db:insert(MD5, Content, PemCache); diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl index 26aca56739..50b1b2cda9 100644 --- a/lib/ssl/src/ssl_record.erl +++ b/lib/ssl/src/ssl_record.erl @@ -712,12 +712,4 @@ mac_hash({3, N} = Version, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) Length, Fragment). sufficient_tlsv1_2_crypto_support() -> - Data = "Sampl", - Data2 = "e #1", - Key = <<0,1,2,3,16,17,18,19,32,33,34,35,48,49,50,51,4,5,6,7,20,21,22,23,36,37,38,39, - 52,53,54,55,8,9,10,11,24,25,26,27,40,41,42,43,56,57,58,59>>, - try - crypto:sha256_mac(Key, lists:flatten([Data, Data2])), - true - catch _:_ -> false - end. + proplists:get_bool(sha256, crypto:algorithms()). diff --git a/lib/ssl/src/ssl_ssl3.erl b/lib/ssl/src/ssl_ssl3.erl index a11c5b8c0c..013c27ebb5 100644 --- a/lib/ssl/src/ssl_ssl3.erl +++ b/lib/ssl/src/ssl_ssl3.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -154,9 +154,9 @@ suites() -> %%-------------------------------------------------------------------- hash(?MD5, Data) -> - crypto:md5(Data); + crypto:hash(md5, Data); hash(?SHA, Data) -> - crypto:sha(Data). + crypto:hash(sha, Data). %%pad_1(?NULL) -> %% ""; @@ -198,6 +198,6 @@ gen(_Secret, _All, Wanted, Len, _C, _N, Acc) when Wanted =< Len -> Block; gen(Secret, All, Wanted, Len, C, N, Acc) -> Prefix = lists:duplicate(N, C), - SHA = crypto:sha([Prefix, All]), - MD5 = crypto:md5([Secret, SHA]), + SHA = crypto:hash(sha, [Prefix, All]), + MD5 = crypto:hash(md5, [Secret, SHA]), gen(Secret, All, Wanted, Len + 16, C+1, N+1, [MD5 | Acc]). diff --git a/lib/ssl/src/ssl_tls1.erl b/lib/ssl/src/ssl_tls1.erl index 41dc1bf0dc..f8fd9efd07 100644 --- a/lib/ssl/src/ssl_tls1.erl +++ b/lib/ssl/src/ssl_tls1.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -29,7 +29,8 @@ -include("ssl_record.hrl"). -export([master_secret/4, finished/5, certificate_verify/3, mac_hash/7, - setup_keys/8, suites/1, prf/5]). + setup_keys/8, suites/1, prf/5, + ecc_curves/1, oid_to_enum/1, enum_to_oid/1]). %%==================================================================== %% Internal application API @@ -57,8 +58,8 @@ finished(Role, Version, PrfAlgo, MasterSecret, Handshake) %% verify_data %% PRF(master_secret, finished_label, MD5(handshake_messages) + %% SHA-1(handshake_messages)) [0..11]; - MD5 = crypto:md5(Handshake), - SHA = crypto:sha(Handshake), + MD5 = crypto:hash(md5, Handshake), + SHA = crypto:hash(sha, Handshake), prf(?MD5SHA, MasterSecret, finished_label(Role), [MD5, SHA], 12); finished(Role, Version, PrfAlgo, MasterSecret, Handshake) @@ -76,8 +77,8 @@ finished(Role, Version, PrfAlgo, MasterSecret, Handshake) -spec certificate_verify(md5sha | sha, integer(), [binary()]) -> binary(). certificate_verify(md5sha, _Version, Handshake) -> - MD5 = crypto:md5(Handshake), - SHA = crypto:sha(Handshake), + MD5 = crypto:hash(md5, Handshake), + SHA = crypto:hash(sha, Handshake), <<MD5/binary, SHA/binary>>; certificate_verify(HashAlgo, _Version, Handshake) -> @@ -184,27 +185,56 @@ mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor}, suites(Minor) when Minor == 1; Minor == 2-> [ + ?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + ?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA, ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA, + ?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, + ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, ?TLS_RSA_WITH_AES_256_CBC_SHA, + + ?TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, + ?TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, ?TLS_RSA_WITH_3DES_EDE_CBC_SHA, + + ?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + ?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA, ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA, + ?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, + ?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, ?TLS_RSA_WITH_AES_128_CBC_SHA, %%?TLS_RSA_WITH_IDEA_CBC_SHA, + ?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, + ?TLS_ECDHE_RSA_WITH_RC4_128_SHA, ?TLS_RSA_WITH_RC4_128_SHA, ?TLS_RSA_WITH_RC4_128_MD5, ?TLS_DHE_RSA_WITH_DES_CBC_SHA, + ?TLS_ECDH_ECDSA_WITH_RC4_128_SHA, + ?TLS_ECDH_RSA_WITH_RC4_128_SHA, ?TLS_RSA_WITH_DES_CBC_SHA ]; suites(Minor) when Minor == 3 -> [ + ?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, + ?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + ?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, + ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, + ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, ?TLS_RSA_WITH_AES_256_CBC_SHA256, + + ?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + ?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + ?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, + ?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, + ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, ?TLS_RSA_WITH_AES_128_CBC_SHA256 @@ -218,16 +248,8 @@ suites(Minor) when Minor == 3 -> %%%% HMAC and the Pseudorandom Functions RFC 2246 & 4346 - 5.%%%% hmac_hash(?NULL, _, _) -> <<>>; -hmac_hash(?MD5, Key, Value) -> - crypto:md5_mac(Key, Value); -hmac_hash(?SHA, Key, Value) -> - crypto:sha_mac(Key, Value); -hmac_hash(?SHA256, Key, Value) -> - crypto:sha256_mac(Key, Value); -hmac_hash(?SHA384, Key, Value) -> - crypto:sha384_mac(Key, Value); -hmac_hash(?SHA512, Key, Value) -> - crypto:sha512_mac(Key, Value). +hmac_hash(Alg, Key, Value) -> + crypto:hmac(mac_algo(Alg), Key, Value). mac_algo(?MD5) -> md5; mac_algo(?SHA) -> sha; @@ -303,3 +325,64 @@ finished_label(client) -> <<"client finished">>; finished_label(server) -> <<"server finished">>. + +%% list ECC curves in prefered order +ecc_curves(_Minor) -> + [?sect571r1,?sect571k1,?secp521r1,?sect409k1,?sect409r1, + ?secp384r1,?sect283k1,?sect283r1,?secp256k1,?secp256r1, + ?sect239k1,?sect233k1,?sect233r1,?secp224k1,?secp224r1, + ?sect193r1,?sect193r2,?secp192k1,?secp192r1,?sect163k1, + ?sect163r1,?sect163r2,?secp160k1,?secp160r1,?secp160r2]. + +%% ECC curves from draft-ietf-tls-ecc-12.txt (Oct. 17, 2005) +oid_to_enum(?sect163k1) -> 1; +oid_to_enum(?sect163r1) -> 2; +oid_to_enum(?sect163r2) -> 3; +oid_to_enum(?sect193r1) -> 4; +oid_to_enum(?sect193r2) -> 5; +oid_to_enum(?sect233k1) -> 6; +oid_to_enum(?sect233r1) -> 7; +oid_to_enum(?sect239k1) -> 8; +oid_to_enum(?sect283k1) -> 9; +oid_to_enum(?sect283r1) -> 10; +oid_to_enum(?sect409k1) -> 11; +oid_to_enum(?sect409r1) -> 12; +oid_to_enum(?sect571k1) -> 13; +oid_to_enum(?sect571r1) -> 14; +oid_to_enum(?secp160k1) -> 15; +oid_to_enum(?secp160r1) -> 16; +oid_to_enum(?secp160r2) -> 17; +oid_to_enum(?secp192k1) -> 18; +oid_to_enum(?secp192r1) -> 19; +oid_to_enum(?secp224k1) -> 20; +oid_to_enum(?secp224r1) -> 21; +oid_to_enum(?secp256k1) -> 22; +oid_to_enum(?secp256r1) -> 23; +oid_to_enum(?secp384r1) -> 24; +oid_to_enum(?secp521r1) -> 25. + +enum_to_oid(1) -> ?sect163k1; +enum_to_oid(2) -> ?sect163r1; +enum_to_oid(3) -> ?sect163r2; +enum_to_oid(4) -> ?sect193r1; +enum_to_oid(5) -> ?sect193r2; +enum_to_oid(6) -> ?sect233k1; +enum_to_oid(7) -> ?sect233r1; +enum_to_oid(8) -> ?sect239k1; +enum_to_oid(9) -> ?sect283k1; +enum_to_oid(10) -> ?sect283r1; +enum_to_oid(11) -> ?sect409k1; +enum_to_oid(12) -> ?sect409r1; +enum_to_oid(13) -> ?sect571k1; +enum_to_oid(14) -> ?sect571r1; +enum_to_oid(15) -> ?secp160k1; +enum_to_oid(16) -> ?secp160r1; +enum_to_oid(17) -> ?secp160r2; +enum_to_oid(18) -> ?secp192k1; +enum_to_oid(19) -> ?secp192r1; +enum_to_oid(20) -> ?secp224k1; +enum_to_oid(21) -> ?secp224r1; +enum_to_oid(22) -> ?secp256k1; +enum_to_oid(23) -> ?secp256r1; +enum_to_oid(24) -> ?secp384r1; +enum_to_oid(25) -> ?secp521r1. diff --git a/lib/ssl/test/erl_make_certs.erl b/lib/ssl/test/erl_make_certs.erl index 5b92e551a5..723ccf4496 100644 --- a/lib/ssl/test/erl_make_certs.erl +++ b/lib/ssl/test/erl_make_certs.erl @@ -45,7 +45,7 @@ %% {dnQualifer, DnQ} %% issuer = {Issuer, IssuerKey} true (i.e. a ca cert is created) %% (obs IssuerKey migth be {Key, Password} -%% key = KeyFile|KeyBin|rsa|dsa Subject PublicKey rsa or dsa generates key +%% key = KeyFile|KeyBin|rsa|dsa|ec Subject PublicKey rsa, dsa or ec generates key %% %% %% (OBS: The generated keys are for testing only) @@ -91,6 +91,16 @@ gen_dsa(LSize,NSize) when is_integer(LSize), is_integer(NSize) -> {Key, encode_key(Key)}. %%-------------------------------------------------------------------- +%% @doc Creates a ec key (OBS: for testing only) +%% the sizes are in bytes +%% @spec (::integer()) -> {::atom(), ::binary(), ::opaque()} +%% @end +%%-------------------------------------------------------------------- +gen_ec(Curve) when is_atom(Curve) -> + Key = gen_ec2(Curve), + {Key, encode_key(Key)}. + +%%-------------------------------------------------------------------- %% @doc Verifies cert signatures %% @spec (::binary(), ::tuple()) -> ::boolean() %% @end @@ -102,7 +112,10 @@ verify_signature(DerEncodedCert, DerKey, _KeyParams) -> public_key:pkix_verify(DerEncodedCert, #'RSAPublicKey'{modulus=Mod, publicExponent=Exp}); #'DSAPrivateKey'{p=P, q=Q, g=G, y=Y} -> - public_key:pkix_verify(DerEncodedCert, {Y, #'Dss-Parms'{p=P, q=Q, g=G}}) + public_key:pkix_verify(DerEncodedCert, {Y, #'Dss-Parms'{p=P, q=Q, g=G}}); + #'ECPrivateKey'{version = _Version, privateKey = _PrivKey, + parameters = Params, publicKey = {0, PubKey}} -> + public_key:pkix_verify(DerEncodedCert, {#'ECPoint'{point = PubKey}, Params}) end. %%%%%%%%%%%%%%%%%%%%%%%%% Implementation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -112,6 +125,7 @@ get_key(Opts) -> undefined -> make_key(rsa, Opts); rsa -> make_key(rsa, Opts); dsa -> make_key(dsa, Opts); + ec -> make_key(ec, Opts); Key -> Password = proplists:get_value(password, Opts, no_passwd), decode_key(Key, Password) @@ -129,6 +143,8 @@ decode_key(#'RSAPrivateKey'{} = Key,_) -> Key; decode_key(#'DSAPrivateKey'{} = Key,_) -> Key; +decode_key(#'ECPrivateKey'{} = Key,_) -> + Key; decode_key(PemEntry = {_,_,_}, Pw) -> public_key:pem_entry_decode(PemEntry, Pw); decode_key(PemBin, Pw) -> @@ -140,7 +156,10 @@ encode_key(Key = #'RSAPrivateKey'{}) -> {'RSAPrivateKey', Der, not_encrypted}; encode_key(Key = #'DSAPrivateKey'{}) -> {ok, Der} = 'OTP-PUB-KEY':encode('DSAPrivateKey', Key), - {'DSAPrivateKey', Der, not_encrypted}. + {'DSAPrivateKey', Der, not_encrypted}; +encode_key(Key = #'ECPrivateKey'{}) -> + {ok, Der} = 'OTP-PUB-KEY':encode('ECPrivateKey', Key), + {'ECPrivateKey', Der, not_encrypted}. make_tbs(SubjectKey, Opts) -> Version = list_to_atom("v"++integer_to_list(proplists:get_value(version, Opts, 3))), @@ -277,7 +296,14 @@ publickey(#'RSAPrivateKey'{modulus=N, publicExponent=E}) -> publickey(#'DSAPrivateKey'{p=P, q=Q, g=G, y=Y}) -> Algo = #'PublicKeyAlgorithm'{algorithm= ?'id-dsa', parameters={params, #'Dss-Parms'{p=P, q=Q, g=G}}}, - #'OTPSubjectPublicKeyInfo'{algorithm = Algo, subjectPublicKey = Y}. + #'OTPSubjectPublicKeyInfo'{algorithm = Algo, subjectPublicKey = Y}; +publickey(#'ECPrivateKey'{version = _Version, + privateKey = _PrivKey, + parameters = Params, + publicKey = {0, PubKey}}) -> + Algo = #'PublicKeyAlgorithm'{algorithm= ?'id-ecPublicKey', parameters=Params}, + #'OTPSubjectPublicKeyInfo'{algorithm = Algo, + subjectPublicKey = #'ECPoint'{point = PubKey}}. validity(Opts) -> DefFrom0 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())-1), @@ -298,13 +324,24 @@ sign_algorithm(#'RSAPrivateKey'{}, Opts) -> end, {Type, 'NULL'}; sign_algorithm(#'DSAPrivateKey'{p=P, q=Q, g=G}, _Opts) -> - {?'id-dsa-with-sha1', {params,#'Dss-Parms'{p=P, q=Q, g=G}}}. + {?'id-dsa-with-sha1', {params,#'Dss-Parms'{p=P, q=Q, g=G}}}; +sign_algorithm(#'ECPrivateKey'{}, Opts) -> + Type = case proplists:get_value(digest, Opts, sha1) of + sha1 -> ?'ecdsa-with-SHA1'; + sha512 -> ?'ecdsa-with-SHA512'; + sha384 -> ?'ecdsa-with-SHA384'; + sha256 -> ?'ecdsa-with-SHA256' + end, + {Type, 'NULL'}. make_key(rsa, _Opts) -> %% (OBS: for testing only) gen_rsa2(64); make_key(dsa, _Opts) -> - gen_dsa2(128, 20). %% Bytes i.e. {1024, 160} + gen_dsa2(128, 20); %% Bytes i.e. {1024, 160} +make_key(ec, _Opts) -> + %% (OBS: for testing only) + gen_ec2(secp256k1). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% RSA key generation (OBS: for testing only) @@ -349,13 +386,13 @@ gen_dsa2(LSize, NSize) -> X0 = prime(LSize), P0 = prime((LSize div 2) +1), - %% Choose L-bit prime modulus P such that p–1 is a multiple of q. + %% Choose L-bit prime modulus P such that p-1 is a multiple of q. case dsa_search(X0 div (2*Q*P0), P0, Q, 1000) of error -> gen_dsa2(LSize, NSize); P -> G = crypto:mod_exp(2, (P-1) div Q, P), % Choose G a number whose multiplicative order modulo p is q. - %% such that This may be done by setting g = h^(p–1)/q mod p, commonly h=2 is used. + %% such that This may be done by setting g = h^(p-1)/q mod p, commonly h=2 is used. X = prime(20), %% Choose x by some random method, where 0 < x < q. Y = crypto:mod_exp(G, X, P), %% Calculate y = g^x mod p. @@ -363,6 +400,22 @@ gen_dsa2(LSize, NSize) -> #'DSAPrivateKey'{version=0, p=P, q=Q, g=G, y=Y, x=X} end. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EC key generation (OBS: for testing only) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +int2list(I) -> + L = (length(integer_to_list(I, 16)) + 1) div 2, + binary_to_list(<<I:(L*8)>>). + +gen_ec2(CurveId) -> + {PrivKey, PubKey} = crypto:generate_key(ecdh, CurveId), + + #'ECPrivateKey'{version = 1, + privateKey = int2list(PrivKey), + parameters = {namedCurve, pubkey_cert_records:namedCurves(CurveId)}, + publicKey = {0, PubKey}}. + %% See fips_186-3.pdf dsa_search(T, P0, Q, Iter) when Iter > 0 -> P = 2*T*Q*P0 + 1, diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index 5cedde5d27..165a8a5fcc 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -69,6 +69,7 @@ groups() -> {session, [], session_tests()}, {renegotiate, [], renegotiate_tests()}, {ciphers, [], cipher_tests()}, + {ciphers_ec, [], cipher_tests_ec()}, {error_handling_tests, [], error_handling_tests()} ]. @@ -76,6 +77,7 @@ all_versions_groups ()-> [{group, api}, {group, renegotiate}, {group, ciphers}, + {group, ciphers_ec}, {group, error_handling_tests}]. @@ -156,10 +158,19 @@ cipher_tests() -> anonymous_cipher_suites, psk_cipher_suites, psk_with_hint_cipher_suites, + psk_anon_cipher_suites, + psk_anon_with_hint_cipher_suites, srp_cipher_suites, + srp_anon_cipher_suites, srp_dsa_cipher_suites, default_reject_anonymous]. +cipher_tests_ec() -> + [ciphers_ecdsa_signed_certs, + ciphers_ecdsa_signed_certs_openssl_names, + ciphers_ecdh_rsa_signed_certs, + ciphers_ecdh_rsa_signed_certs_openssl_names]. + error_handling_tests()-> [controller_dies, client_closes_socket, @@ -185,10 +196,12 @@ init_per_suite(Config0) -> Result = (catch make_certs:all(?config(data_dir, Config0), ?config(priv_dir, Config0))), - ct:print("Make certs ~p~n", [Result]), + ct:log("Make certs ~p~n", [Result]), Config1 = ssl_test_lib:make_dsa_cert(Config0), - Config = ssl_test_lib:cert_options(Config1), + Config2 = ssl_test_lib:make_ecdsa_cert(Config1), + Config3 = ssl_test_lib:make_ecdh_rsa_cert(Config2), + Config = ssl_test_lib:cert_options(Config3), [{watchdog, Dog} | Config] catch _:_ -> {skip, "Crypto did not start"} @@ -253,7 +266,7 @@ init_per_testcase(empty_protocol_versions, Config) -> %% ssl_test_lib:make_mix_cert(Config0); init_per_testcase(_TestCase, Config0) -> - ct:print("TLS/SSL version ~p~n ", [ssl_record:supported_protocol_versions()]), + ct:log("TLS/SSL version ~p~n ", [ssl_record:supported_protocol_versions()]), Config = lists:keydelete(watchdog, 1, Config0), Dog = ct:timetrap(?TIMEOUT), [{watchdog, Dog} | Config]. @@ -316,7 +329,7 @@ connection_info(Config) when is_list(Config) -> [{ciphers,[{rsa,rc4_128,sha,no_export}]} | ClientOpts]}]), - ct:print("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), Version = @@ -369,7 +382,7 @@ controlling_process(Config) when is_list(Config) -> ClientMsg]}}, {options, ClientOpts}]), - ct:print("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), receive @@ -419,7 +432,7 @@ controller_dies(Config) when is_list(Config) -> ClientMsg]}}, {options, ClientOpts}]), - ct:print("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ct:sleep(?SLEEP), %% so that they are connected process_flag(trap_exit, true), @@ -457,12 +470,12 @@ controller_dies(Config) when is_list(Config) -> Client3 ! die_nice end, - ct:print("Wating on exit ~p~n",[Client3]), + ct:log("Wating on exit ~p~n",[Client3]), receive {'EXIT', Client3, normal} -> ok end, receive %% Client3 is dead but that doesn't matter, socket should not be closed. Unexpected -> - ct:print("Unexpected ~p~n",[Unexpected]), + ct:log("Unexpected ~p~n",[Unexpected]), ct:fail({line, ?LINE-1}) after 1000 -> ok @@ -593,7 +606,7 @@ peername(Config) when is_list(Config) -> ServerMsg = {ok, {ClientIp, ClientPort}}, ClientMsg = {ok, {ServerIp, Port}}, - ct:print("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), @@ -626,7 +639,7 @@ peercert(Config) when is_list(Config) -> ServerMsg = {error, no_peercert}, ClientMsg = {ok, BinCert}, - ct:print("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), @@ -664,7 +677,7 @@ peercert_with_client_cert(Config) when is_list(Config) -> ServerMsg = {ok, ClientBinCert}, ClientMsg = {ok, ServerBinCert}, - ct:print("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), @@ -696,7 +709,7 @@ sockname(Config) when is_list(Config) -> ServerMsg = {ok, {ServerIp, Port}}, ClientMsg = {ok, {ClientIp, ClientPort}}, - ct:print("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), @@ -769,7 +782,7 @@ socket_options_result(Socket, Options, DefaultValues, NewOptions, NewValues) -> ssl:setopts(Socket, [{nodelay, true}]), {ok,[{nodelay, true}]} = ssl:getopts(Socket, [nodelay]), {ok, All} = ssl:getopts(Socket, []), - ct:print("All opts ~p~n", [All]), + ct:log("All opts ~p~n", [All]), ok. @@ -792,7 +805,7 @@ invalid_inet_get_option(Config) when is_list(Config) -> {mfa, {ssl_test_lib, no_result, []}}, {options, ClientOpts}]), - ct:print("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok), @@ -818,7 +831,7 @@ invalid_inet_get_option_not_list(Config) when is_list(Config) -> {mfa, {ssl_test_lib, no_result, []}}, {options, ClientOpts}]), - ct:print("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok), @@ -850,7 +863,7 @@ invalid_inet_get_option_improper_list(Config) when is_list(Config) -> {mfa, {ssl_test_lib, no_result, []}}, {options, ClientOpts}]), - ct:print("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok), @@ -881,7 +894,7 @@ invalid_inet_set_option(Config) when is_list(Config) -> {mfa, {ssl_test_lib, no_result, []}}, {options, ClientOpts}]), - ct:print("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok), @@ -913,7 +926,7 @@ invalid_inet_set_option_not_list(Config) when is_list(Config) -> {mfa, {ssl_test_lib, no_result, []}}, {options, ClientOpts}]), - ct:print("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok), @@ -945,7 +958,7 @@ invalid_inet_set_option_improper_list(Config) when is_list(Config) -> {mfa, {ssl_test_lib, no_result, []}}, {options, ClientOpts}]), - ct:print("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok), @@ -986,7 +999,7 @@ misc_ssl_options(Config) when is_list(Config) -> {mfa, {ssl_test_lib, send_recv_result_active, []}}, {options, TestOpts ++ ClientOpts}]), - ct:print("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok, Client, ok), @@ -999,7 +1012,7 @@ versions() -> versions(Config) when is_list(Config) -> [_|_] = Versions = ssl:versions(), - ct:print("~p~n", [Versions]). + ct:log("~p~n", [Versions]). %%-------------------------------------------------------------------- send_recv() -> @@ -1021,7 +1034,7 @@ send_recv(Config) when is_list(Config) -> {mfa, {ssl_test_lib, send_recv_result, []}}, {options, [{active, false} | ClientOpts]}]), - ct:print("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok, Client, ok), @@ -1047,7 +1060,7 @@ send_close(Config) when is_list(Config) -> {ok, SslS} = rpc:call(ClientNode, ssl, connect, [TcpS,[{active, false}|ClientOpts]]), - ct:print("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), self(), Server]), ok = ssl:send(SslS, "Hello world"), {ok,<<"Hello world">>} = ssl:recv(SslS, 11), @@ -1132,7 +1145,7 @@ upgrade(Config) when is_list(Config) -> {tcp_options, TcpOpts}, {ssl_options, ClientOpts}]), - ct:print("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok, Client, ok), @@ -1182,7 +1195,7 @@ upgrade_with_timeout(Config) when is_list(Config) -> {tcp_options, TcpOpts}, {ssl_options, ClientOpts}]), - ct:print("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok, Client, ok), @@ -1208,14 +1221,14 @@ tcp_connect(Config) when is_list(Config) -> Port = ssl_test_lib:inet_port(Server), {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {packet, 0}]), - ct:print("Testcase ~p connected to Server ~p ~n", [self(), Server]), + ct:log("Testcase ~p connected to Server ~p ~n", [self(), Server]), gen_tcp:send(Socket, "<SOME GARBLED NON SSL MESSAGE>"), receive {tcp_closed, Socket} -> receive {Server, {error, Error}} -> - ct:print("Error ~p", [Error]) + ct:log("Error ~p", [Error]) end end. %%-------------------------------------------------------------------- @@ -1236,7 +1249,7 @@ tcp_connect_big(Config) when is_list(Config) -> Port = ssl_test_lib:inet_port(Server), {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {packet, 0}]), - ct:print("Testcase ~p connected to Server ~p ~n", [self(), Server]), + ct:log("Testcase ~p connected to Server ~p ~n", [self(), Server]), Rand = crypto:rand_bytes(?MAX_CIPHER_TEXT_LENGTH+1), gen_tcp:send(Socket, <<?BYTE(0), @@ -1248,7 +1261,7 @@ tcp_connect_big(Config) when is_list(Config) -> {Server, {error, timeout}} -> ct:fail("hangs"); {Server, {error, Error}} -> - ct:print("Error ~p", [Error]) + ct:log("Error ~p", [Error]) end end. @@ -1278,7 +1291,7 @@ ipv6(Config) when is_list(Config) -> {options, [inet6, {active, false} | ClientOpts]}]), - ct:print("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok, Client, ok), @@ -1536,8 +1549,8 @@ ciphers_rsa_signed_certs(Config) when is_list(Config) -> Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), - Ciphers = ssl_test_lib:rsa_suites(), - ct:print("~p erlang cipher suites ~p~n", [Version, Ciphers]), + Ciphers = ssl_test_lib:rsa_suites(erlang), + ct:log("~p erlang cipher suites ~p~n", [Version, Ciphers]), run_suites(Ciphers, Version, Config, rsa). %%------------------------------------------------------------------- ciphers_rsa_signed_certs_openssl_names() -> @@ -1547,7 +1560,7 @@ ciphers_rsa_signed_certs_openssl_names(Config) when is_list(Config) -> Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), Ciphers = ssl_test_lib:openssl_rsa_suites(), - ct:print("tls1 openssl cipher suites ~p~n", [Ciphers]), + ct:log("tls1 openssl cipher suites ~p~n", [Ciphers]), run_suites(Ciphers, Version, Config, rsa). %%------------------------------------------------------------------- @@ -1559,7 +1572,7 @@ ciphers_dsa_signed_certs(Config) when is_list(Config) -> ssl_record:protocol_version(ssl_record:highest_protocol_version([])), Ciphers = ssl_test_lib:dsa_suites(), - ct:print("~p erlang cipher suites ~p~n", [Version, Ciphers]), + ct:log("~p erlang cipher suites ~p~n", [Version, Ciphers]), run_suites(Ciphers, Version, Config, dsa). %%------------------------------------------------------------------- ciphers_dsa_signed_certs_openssl_names() -> @@ -1570,7 +1583,7 @@ ciphers_dsa_signed_certs_openssl_names(Config) when is_list(Config) -> ssl_record:protocol_version(ssl_record:highest_protocol_version([])), Ciphers = ssl_test_lib:openssl_dsa_suites(), - ct:print("tls1 openssl cipher suites ~p~n", [Ciphers]), + ct:log("tls1 openssl cipher suites ~p~n", [Ciphers]), run_suites(Ciphers, Version, Config, dsa). %%------------------------------------------------------------------- anonymous_cipher_suites()-> @@ -1594,6 +1607,20 @@ psk_with_hint_cipher_suites(Config) when is_list(Config) -> Ciphers = ssl_test_lib:psk_suites(), run_suites(Ciphers, Version, Config, psk_with_hint). %%------------------------------------------------------------------- +psk_anon_cipher_suites() -> + [{doc, "Test the anonymous PSK ciphersuites WITHOUT server supplied identity hint"}]. +psk_anon_cipher_suites(Config) when is_list(Config) -> + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Ciphers = ssl_test_lib:psk_anon_suites(), + run_suites(Ciphers, Version, Config, psk_anon). +%%------------------------------------------------------------------- +psk_anon_with_hint_cipher_suites()-> + [{doc, "Test the anonymous PSK ciphersuites WITH server supplied identity hint"}]. +psk_anon_with_hint_cipher_suites(Config) when is_list(Config) -> + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Ciphers = ssl_test_lib:psk_anon_suites(), + run_suites(Ciphers, Version, Config, psk_anon_with_hint). +%%------------------------------------------------------------------- srp_cipher_suites()-> [{doc, "Test the SRP ciphersuites"}]. srp_cipher_suites(Config) when is_list(Config) -> @@ -1601,6 +1628,13 @@ srp_cipher_suites(Config) when is_list(Config) -> Ciphers = ssl_test_lib:srp_suites(), run_suites(Ciphers, Version, Config, srp). %%------------------------------------------------------------------- +srp_anon_cipher_suites()-> + [{doc, "Test the anonymous SRP ciphersuites"}]. +srp_anon_cipher_suites(Config) when is_list(Config) -> + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Ciphers = ssl_test_lib:srp_anon_suites(), + run_suites(Ciphers, Version, Config, srp_anon). +%%------------------------------------------------------------------- srp_dsa_cipher_suites()-> [{doc, "Test the SRP DSA ciphersuites"}]. srp_dsa_cipher_suites(Config) when is_list(Config) -> @@ -1632,6 +1666,48 @@ default_reject_anonymous(Config) when is_list(Config) -> Client, {error, {tls_alert, "insufficient security"}}). %%-------------------------------------------------------------------- +ciphers_ecdsa_signed_certs() -> + [{doc, "Test all ecdsa ssl cipher suites in highest support ssl/tls version"}]. + +ciphers_ecdsa_signed_certs(Config) when is_list(Config) -> + Version = + ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + + Ciphers = ssl_test_lib:ecdsa_suites(), + ct:log("~p erlang cipher suites ~p~n", [Version, Ciphers]), + run_suites(Ciphers, Version, Config, ecdsa). +%%-------------------------------------------------------------------- +ciphers_ecdsa_signed_certs_openssl_names() -> + [{doc, "Test all ecdsa ssl cipher suites in highest support ssl/tls version"}]. + +ciphers_ecdsa_signed_certs_openssl_names(Config) when is_list(Config) -> + Version = + ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Ciphers = ssl_test_lib:openssl_ecdsa_suites(), + ct:log("tls1 openssl cipher suites ~p~n", [Ciphers]), + run_suites(Ciphers, Version, Config, ecdsa). +%%-------------------------------------------------------------------- +ciphers_ecdh_rsa_signed_certs() -> + [{doc, "Test all ecdh_rsa ssl cipher suites in highest support ssl/tls version"}]. + +ciphers_ecdh_rsa_signed_certs(Config) when is_list(Config) -> + Version = + ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + + Ciphers = ssl_test_lib:ecdh_rsa_suites(), + ct:log("~p erlang cipher suites ~p~n", [Version, Ciphers]), + run_suites(Ciphers, Version, Config, ecdh_rsa). +%%-------------------------------------------------------------------- +ciphers_ecdh_rsa_signed_certs_openssl_names() -> + [{doc, "Test all ecdh_rsa ssl cipher suites in highest support ssl/tls version"}]. + +ciphers_ecdh_rsa_signed_certs_openssl_names(Config) when is_list(Config) -> + Version = + ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Ciphers = ssl_test_lib:openssl_ecdh_rsa_suites(), + ct:log("tls1 openssl cipher suites ~p~n", [Ciphers]), + run_suites(Ciphers, Version, Config, ecdh_rsa). +%%-------------------------------------------------------------------- reuse_session() -> [{doc,"Test reuse of sessions (short handshake)"}]. reuse_session(Config) when is_list(Config) -> @@ -1670,7 +1746,7 @@ reuse_session(Config) when is_list(Config) -> {Client1, SessionInfo} -> ok; {Client1, Other} -> - ct:print("Expected: ~p, Unexpected: ~p~n", + ct:log("Expected: ~p, Unexpected: ~p~n", [SessionInfo, Other]), ct:fail(session_not_reused) end, @@ -1728,7 +1804,7 @@ reuse_session(Config) when is_list(Config) -> ct:fail( session_reused_when_session_reuse_disabled_by_server); {Client4, _Other} -> - ct:print("OTHER: ~p ~n", [_Other]), + ct:log("OTHER: ~p ~n", [_Other]), ok end, @@ -1778,7 +1854,7 @@ reuse_session_expired(Config) when is_list(Config) -> {Client1, SessionInfo} -> ok; {Client1, Other} -> - ct:print("Expected: ~p, Unexpected: ~p~n", + ct:log("Expected: ~p, Unexpected: ~p~n", [SessionInfo, Other]), ct:fail(session_not_reused) end, @@ -2481,7 +2557,7 @@ connect_twice(Config) when is_list(Config) -> {options, [{keepalive, true},{active, false} | ClientOpts]}]), - ct:print("Testcase ~p, Client ~p Server ~p ~n", + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok, Client, ok), @@ -2827,9 +2903,9 @@ result_ok(_Socket) -> ok. renegotiate(Socket, Data) -> - ct:print("Renegotiating ~n", []), + ct:log("Renegotiating ~n", []), Result = ssl:renegotiate(Socket), - ct:print("Result ~p~n", [Result]), + ct:log("Result ~p~n", [Result]), ssl:send(Socket, Data), case Result of ok -> @@ -2858,7 +2934,7 @@ renegotiate_immediately(Socket) -> {error, renegotiation_rejected} = ssl:renegotiate(Socket), ct:sleep(?RENEGOTIATION_DISABLE_TIME +1), ok = ssl:renegotiate(Socket), - ct:print("Renegotiated again"), + ct:log("Renegotiated again"), ssl:send(Socket, "Hello world"), ok. @@ -2877,7 +2953,7 @@ new_config(PrivDir, ServerOpts0) -> ServerOpts = proplists:delete(keyfile, ServerOpts2), {ok, PEM} = file:read_file(NewCaCertFile), - ct:print("CA file content: ~p~n", [public_key:pem_decode(PEM)]), + ct:log("CA file content: ~p~n", [public_key:pem_decode(PEM)]), [{cacertfile, NewCaCertFile}, {certfile, NewCertFile}, {keyfile, NewKeyFile} | ServerOpts]. @@ -3073,15 +3149,15 @@ get_close(Pid, Where) -> {'EXIT', Pid, _Reason} -> receive {_, {ssl_closed, Socket}} -> - ct:print("Socket closed ~p~n",[Socket]); + ct:log("Socket closed ~p~n",[Socket]); Unexpected -> - ct:print("Unexpected ~p~n",[Unexpected]), + ct:log("Unexpected ~p~n",[Unexpected]), ct:fail({line, ?LINE-1}) after 5000 -> ct:fail({timeout, {line, ?LINE, Where}}) end; Unexpected -> - ct:print("Unexpected ~p~n",[Unexpected]), + ct:log("Unexpected ~p~n",[Unexpected]), ct:fail({line, ?LINE-1}) after 5000 -> ct:fail({timeout, {line, ?LINE, Where}}) @@ -3095,7 +3171,7 @@ run_send_recv_rizzo(Ciphers, Config, Version, Mfa) -> [] -> ok; Error -> - ct:print("Cipher suite errors: ~p~n", [Error]), + ct:log("Cipher suite errors: ~p~n", [Error]), ct:fail(cipher_suite_failed_see_test_case_log) end. @@ -3125,12 +3201,21 @@ rizzo_test(Cipher, Config, Version, Mfa) -> [{Cipher, Error}] end. -client_server_opts({KeyAlgo,_,_}, Config) when KeyAlgo == rsa orelse KeyAlgo == dhe_rsa -> +client_server_opts({KeyAlgo,_,_}, Config) + when KeyAlgo == rsa orelse + KeyAlgo == dhe_rsa orelse + KeyAlgo == ecdhe_rsa -> {?config(client_opts, Config), ?config(server_opts, Config)}; client_server_opts({KeyAlgo,_,_}, Config) when KeyAlgo == dss orelse KeyAlgo == dhe_dss -> {?config(client_dsa_opts, Config), - ?config(server_dsa_opts, Config)}. + ?config(server_dsa_opts, Config)}; +client_server_opts({KeyAlgo,_,_}, Config) when KeyAlgo == ecdh_ecdsa orelse KeyAlgo == ecdhe_ecdsa -> + {?config(client_opts, Config), + ?config(server_ecdsa_opts, Config)}; +client_server_opts({KeyAlgo,_,_}, Config) when KeyAlgo == ecdh_rsa -> + {?config(client_opts, Config), + ?config(server_ecdh_rsa_opts, Config)}. run_suites(Ciphers, Version, Config, Type) -> {ClientOpts, ServerOpts} = @@ -3151,12 +3236,27 @@ run_suites(Ciphers, Version, Config, Type) -> psk_with_hint -> {?config(client_psk, Config), ?config(server_psk_hint, Config)}; + psk_anon -> + {?config(client_psk, Config), + ?config(server_psk_anon, Config)}; + psk_anon_with_hint -> + {?config(client_psk, Config), + ?config(server_psk_anon_hint, Config)}; srp -> {?config(client_srp, Config), ?config(server_srp, Config)}; + srp_anon -> + {?config(client_srp, Config), + ?config(server_srp_anon, Config)}; srp_dsa -> {?config(client_srp_dsa, Config), - ?config(server_srp_dsa, Config)} + ?config(server_srp_dsa, Config)}; + ecdsa -> + {?config(client_opts, Config), + ?config(server_ecdsa_opts, Config)}; + ecdh_rsa -> + {?config(client_opts, Config), + ?config(server_ecdh_rsa_opts, Config)} end, Result = lists:map(fun(Cipher) -> @@ -3166,7 +3266,7 @@ run_suites(Ciphers, Version, Config, Type) -> [] -> ok; Error -> - ct:print("Cipher suite errors: ~p~n", [Error]), + ct:log("Cipher suite errors: ~p~n", [Error]), ct:fail(cipher_suite_failed_see_test_case_log) end. @@ -3177,7 +3277,7 @@ erlang_cipher_suite(Suite) -> cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> %% process_flag(trap_exit, true), - ct:print("Testing CipherSuite ~p~n", [CipherSuite]), + ct:log("Testing CipherSuite ~p~n", [CipherSuite]), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), ErlangCipherSuite = erlang_cipher_suite(CipherSuite), diff --git a/lib/ssl/test/ssl_certificate_verify_SUITE.erl b/lib/ssl/test/ssl_certificate_verify_SUITE.erl index 26938bda50..2703d2d79c 100644 --- a/lib/ssl/test/ssl_certificate_verify_SUITE.erl +++ b/lib/ssl/test/ssl_certificate_verify_SUITE.erl @@ -86,7 +86,7 @@ init_per_suite(Config0) -> Result = (catch make_certs:all(?config(data_dir, Config0), ?config(priv_dir, Config0))), - ct:print("Make certs ~p~n", [Result]), + ct:log("Make certs ~p~n", [Result]), Config1 = ssl_test_lib:make_dsa_cert(Config0), Config = ssl_test_lib:cert_options(Config1), @@ -297,7 +297,7 @@ verify_fun_always_run_client(Config) when is_list(Config) -> %% this is not a bug it is a circumstance of how tcp works! receive {Server, ServerError} -> - ct:print("Server Error ~p~n", [ServerError]) + ct:log("Server Error ~p~n", [ServerError]) end, ssl_test_lib:check_result(Client, {error, {tls_alert, "handshake failure"}}). @@ -346,7 +346,7 @@ verify_fun_always_run_server(Config) when is_list(Config) -> %% this is not a bug it is a circumstance of how tcp works! receive {Client, ClientError} -> - ct:print("Client Error ~p~n", [ClientError]) + ct:log("Client Error ~p~n", [ClientError]) end, ssl_test_lib:check_result(Server, {error, {tls_alert, "handshake failure"}}). @@ -413,7 +413,7 @@ cert_expired(Config) when is_list(Config) -> two_digits_str(Sec)])), NewValidity = {'Validity', {generalTime, NotBeforeStr}, {generalTime, NotAfterStr}}, - ct:print("Validity: ~p ~n NewValidity: ~p ~n", + ct:log("Validity: ~p ~n NewValidity: ~p ~n", [OTPTbsCert#'OTPTBSCertificate'.validity, NewValidity]), NewOTPTbsCert = OTPTbsCert#'OTPTBSCertificate'{validity = NewValidity}, @@ -644,7 +644,7 @@ no_authority_key_identifier(Config) when is_list(Config) -> NewExtensions = delete_authority_key_extension(Extensions, []), NewOTPTbsCert = OTPTbsCert#'OTPTBSCertificate'{extensions = NewExtensions}, - ct:print("Extensions ~p~n, NewExtensions: ~p~n", [Extensions, NewExtensions]), + ct:log("Extensions ~p~n, NewExtensions: ~p~n", [Extensions, NewExtensions]), NewDerCert = public_key:pkix_sign(NewOTPTbsCert, Key), ssl_test_lib:der_to_pem(NewCertFile, [{'Certificate', NewDerCert, not_encrypted}]), @@ -955,10 +955,10 @@ client_msg(Client, ClientMsg) -> {Client, ClientMsg} -> ok; {Client, {error,closed}} -> - ct:print("client got close"), + ct:log("client got close"), ok; {Client, {error, Reason}} -> - ct:print("client got econnaborted: ~p", [Reason]), + ct:log("client got econnaborted: ~p", [Reason]), ok; Unexpected -> ct:fail(Unexpected) @@ -968,10 +968,10 @@ server_msg(Server, ServerMsg) -> {Server, ServerMsg} -> ok; {Server, {error,closed}} -> - ct:print("server got close"), + ct:log("server got close"), ok; {Server, {error, Reason}} -> - ct:print("server got econnaborted: ~p", [Reason]), + ct:log("server got econnaborted: ~p", [Reason]), ok; Unexpected -> ct:fail(Unexpected) diff --git a/lib/ssl/test/ssl_npn_handshake_SUITE.erl b/lib/ssl/test/ssl_npn_handshake_SUITE.erl index 8c1b22cf5e..7b271c4d5d 100644 --- a/lib/ssl/test/ssl_npn_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_npn_handshake_SUITE.erl @@ -74,7 +74,7 @@ init_per_suite(Config) -> Result = (catch make_certs:all(?config(data_dir, Config), ?config(priv_dir, Config))), - ct:print("Make certs ~p~n", [Result]), + ct:log("Make certs ~p~n", [Result]), ssl_test_lib:cert_options(Config) catch _:_ -> {skip, "Crypto did not start"} @@ -311,13 +311,13 @@ run_npn_handshake(Config, ClientExtraOpts, ServerExtraOpts, ExpectedProtocol) -> assert_npn(Socket, Protocol) -> - ct:print("Negotiated Protocol ~p, Expecting: ~p ~n", + ct:log("Negotiated Protocol ~p, Expecting: ~p ~n", [ssl:negotiated_next_protocol(Socket), Protocol]), Protocol = ssl:negotiated_next_protocol(Socket). assert_npn_and_renegotiate_and_send_data(Socket, Protocol, Data) -> assert_npn(Socket, Protocol), - ct:print("Renegotiating ~n", []), + ct:log("Renegotiating ~n", []), ok = ssl:renegotiate(Socket), ssl:send(Socket, Data), assert_npn(Socket, Protocol), @@ -332,7 +332,7 @@ ssl_receive_and_assert_npn(Socket, Protocol, Data) -> ssl_receive(Socket, Data). ssl_send(Socket, Data) -> - ct:print("Connection info: ~p~n", + ct:log("Connection info: ~p~n", [ssl:connection_info(Socket)]), ssl:send(Socket, Data). @@ -340,11 +340,11 @@ ssl_receive(Socket, Data) -> ssl_receive(Socket, Data, []). ssl_receive(Socket, Data, Buffer) -> - ct:print("Connection info: ~p~n", + ct:log("Connection info: ~p~n", [ssl:connection_info(Socket)]), receive {ssl, Socket, MoreData} -> - ct:print("Received ~p~n",[MoreData]), + ct:log("Received ~p~n",[MoreData]), NewBuffer = Buffer ++ MoreData, case NewBuffer of Data -> diff --git a/lib/ssl/test/ssl_npn_hello_SUITE.erl b/lib/ssl/test/ssl_npn_hello_SUITE.erl index 72768bcb55..43fa72ea28 100644 --- a/lib/ssl/test/ssl_npn_hello_SUITE.erl +++ b/lib/ssl/test/ssl_npn_hello_SUITE.erl @@ -25,6 +25,8 @@ -compile(export_all). -include("ssl_handshake.hrl"). -include("ssl_record.hrl"). +-include("ssl_cipher.hrl"). +-include("ssl_internal.hrl"). -include_lib("common_test/include/ct.hrl"). %%-------------------------------------------------------------------- @@ -75,17 +77,19 @@ encode_and_decode_npn_server_hello_test(_Config) -> {[{DecodedHandshakeMessage, _Raw}], _} = ssl_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>), NextProtocolNegotiation = DecodedHandshakeMessage#server_hello.next_protocol_negotiation, - ct:print("~p ~n", [NextProtocolNegotiation]), + ct:log("~p ~n", [NextProtocolNegotiation]), NextProtocolNegotiation = #next_protocol_negotiation{extension_data = <<6, "spdy/2">>}. %%-------------------------------------------------------------------- create_server_hello_with_no_advertised_protocols_test(_Config) -> - Hello = ssl_handshake:server_hello(<<>>, {3, 0}, create_connection_states(), false, undefined), + Hello = ssl_handshake:server_hello(<<>>, {3, 0}, create_connection_states(), false, + undefined, undefined, undefined), undefined = Hello#server_hello.next_protocol_negotiation. %%-------------------------------------------------------------------- create_server_hello_with_advertised_protocols_test(_Config) -> Hello = ssl_handshake:server_hello(<<>>, {3, 0}, create_connection_states(), - false, [<<"spdy/1">>, <<"http/1.0">>, <<"http/1.1">>]), + false, [<<"spdy/1">>, <<"http/1.0">>, <<"http/1.1">>], + undefined, undefined), #next_protocol_negotiation{extension_data = <<6, "spdy/1", 8, "http/1.0", 8, "http/1.1">>} = Hello#server_hello.next_protocol_negotiation. %%-------------------------------------------------------------------- @@ -96,7 +100,7 @@ create_client_handshake(Npn) -> client_version = {1, 2}, random = <<1:256>>, session_id = <<>>, - cipher_suites = "", + cipher_suites = [?TLS_DHE_DSS_WITH_DES_CBC_SHA], compression_methods = "", next_protocol_negotiation = Npn, renegotiation_info = #renegotiation_info{} @@ -107,7 +111,7 @@ create_server_handshake(Npn) -> server_version = {1, 2}, random = <<1:256>>, session_id = <<>>, - cipher_suite = <<1,2>>, + cipher_suite = ?TLS_DHE_DSS_WITH_DES_CBC_SHA, compression_method = 1, next_protocol_negotiation = Npn, renegotiation_info = #renegotiation_info{} @@ -119,7 +123,7 @@ create_connection_states() -> security_parameters = #security_parameters{ server_random = <<1:256>>, compression_algorithm = 1, - cipher_suite = <<1, 2>> + cipher_suite = ?TLS_DHE_DSS_WITH_DES_CBC_SHA } }, diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl index 4116bb39d1..5a374e234d 100644 --- a/lib/ssl/test/ssl_packet_SUITE.erl +++ b/lib/ssl/test/ssl_packet_SUITE.erl @@ -143,7 +143,7 @@ init_per_suite(Config) -> Result = (catch make_certs:all(?config(data_dir, Config), ?config(priv_dir, Config))), - ct:print("Make certs ~p~n", [Result]), + ct:log("Make certs ~p~n", [Result]), ssl_test_lib:cert_options(Config) catch _:_ -> {skip, "Crypto did not start"} @@ -2069,7 +2069,7 @@ client_packet_decode(Socket, [Head | Tail] = Packet) -> client_packet_decode(Socket, [Head], Tail, Packet). client_packet_decode(Socket, P1, P2, Packet) -> - ct:print("Packet: ~p ~n", [Packet]), + ct:log("Packet: ~p ~n", [Packet]), ok = ssl:send(Socket, P1), ok = ssl:send(Socket, P2), receive diff --git a/lib/ssl/test/ssl_payload_SUITE.erl b/lib/ssl/test/ssl_payload_SUITE.erl index 77ad546420..5f5166391f 100644 --- a/lib/ssl/test/ssl_payload_SUITE.erl +++ b/lib/ssl/test/ssl_payload_SUITE.erl @@ -556,33 +556,33 @@ send(Socket, Data, Size, Repeate,F) -> sender(Socket, Data, Size) -> ok = send(Socket, Data, Size, 100, fun() -> do_recv(Socket, Data, Size, <<>>, false) end), - ct:print("Sender recv: ~p~n", [ssl:getopts(Socket, [active])]), + ct:log("Sender recv: ~p~n", [ssl:getopts(Socket, [active])]), ok. sender_once(Socket, Data, Size) -> send(Socket, Data, Size, 100, fun() -> do_active_once(Socket, Data, Size, <<>>, false) end), - ct:print("Sender active once: ~p~n", + ct:log("Sender active once: ~p~n", [ssl:getopts(Socket, [active])]), ok. sender_active(Socket, Data, Size) -> F = fun() -> do_active(Socket, Data, Size, <<>>, false) end, send(Socket, Data, Size, 100, F), - ct:print("Sender active: ~p~n", [ssl:getopts(Socket, [active])]), + ct:log("Sender active: ~p~n", [ssl:getopts(Socket, [active])]), ok. echoer(Socket, Data, Size) -> - ct:print("Echoer recv: ~p~n", [ssl:getopts(Socket, [active])]), + ct:log("Echoer recv: ~p~n", [ssl:getopts(Socket, [active])]), echo(fun() -> do_recv(Socket, Data, Size, <<>>, true) end, 100). echoer_once(Socket, Data, Size) -> - ct:print("Echoer active once: ~p ~n", + ct:log("Echoer active once: ~p ~n", [ssl:getopts(Socket, [active])]), echo(fun() -> do_active_once(Socket, Data, Size, <<>>, true) end, 100). echoer_active(Socket, Data, Size) -> - ct:print("Echoer active: ~p~n", [ssl:getopts(Socket, [active])]), + ct:log("Echoer active: ~p~n", [ssl:getopts(Socket, [active])]), echo(fun() -> do_active(Socket, Data, Size, <<>>, true) end, 100). echo(_Fun, 0) -> ok; diff --git a/lib/ssl/test/ssl_session_cache_SUITE.erl b/lib/ssl/test/ssl_session_cache_SUITE.erl index fd9a0a594c..6cc6c4bdb2 100644 --- a/lib/ssl/test/ssl_session_cache_SUITE.erl +++ b/lib/ssl/test/ssl_session_cache_SUITE.erl @@ -63,7 +63,7 @@ init_per_suite(Config0) -> Result = (catch make_certs:all(?config(data_dir, Config0), ?config(priv_dir, Config0))), - ct:print("Make certs ~p~n", [Result]), + ct:log("Make certs ~p~n", [Result]), Config1 = ssl_test_lib:make_dsa_cert(Config0), Config = ssl_test_lib:cert_options(Config1), diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index d655d7659e..a8ff5187b6 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -59,7 +59,7 @@ run_server(Opts) -> Options = proplists:get_value(options, Opts), Pid = proplists:get_value(from, Opts), Transport = proplists:get_value(transport, Opts, ssl), - ct:print("ssl:listen(~p, ~p)~n", [Port, Options]), + ct:log("ssl:listen(~p, ~p)~n", [Port, Options]), {ok, ListenSocket} = rpc:call(Node, Transport, listen, [Port, Options]), Pid ! {listen, up}, send_selected_port(Pid, Port, ListenSocket), @@ -77,13 +77,13 @@ do_run_server(ListenSocket, AcceptSocket, Opts) -> Pid = proplists:get_value(from, Opts), Transport = proplists:get_value(transport, Opts, ssl), {Module, Function, Args} = proplists:get_value(mfa, Opts), - ct:print("Server: apply(~p,~p,~p)~n", + ct:log("Server: apply(~p,~p,~p)~n", [Module, Function, [AcceptSocket | Args]]), case rpc:call(Node, Module, Function, [AcceptSocket | Args]) of no_result_msg -> ok; Msg -> - ct:print("Server Msg: ~p ~n", [Msg]), + ct:log("Server Msg: ~p ~n", [Msg]), Pid ! {self(), Msg} end, receive @@ -92,10 +92,10 @@ do_run_server(ListenSocket, AcceptSocket, Opts) -> {listen, MFA} -> run_server(ListenSocket, [MFA | proplists:delete(mfa, Opts)]); close -> - ct:print("Server closing ~p ~n", [self()]), + ct:log("Server closing ~p ~n", [self()]), Result = rpc:call(Node, Transport, close, [AcceptSocket], 500), Result1 = rpc:call(Node, Transport, close, [ListenSocket], 500), - ct:print("Result ~p : ~p ~n", [Result, Result1]); + ct:log("Result ~p : ~p ~n", [Result, Result1]); {ssl_closed, _} -> ok end. @@ -115,7 +115,7 @@ connect(#sslsocket{} = ListenSocket, Opts) -> end; connect(ListenSocket, Opts) -> Node = proplists:get_value(node, Opts), - ct:print("gen_tcp:accept(~p)~n", [ListenSocket]), + ct:log("gen_tcp:accept(~p)~n", [ListenSocket]), {ok, AcceptSocket} = rpc:call(Node, gen_tcp, accept, [ListenSocket]), AcceptSocket. @@ -123,10 +123,10 @@ connect(ListenSocket, Opts) -> connect(_, _, 0, AcceptSocket, _) -> AcceptSocket; connect(ListenSocket, Node, N, _, Timeout) -> - ct:print("ssl:transport_accept(~p)~n", [ListenSocket]), + ct:log("ssl:transport_accept(~p)~n", [ListenSocket]), {ok, AcceptSocket} = rpc:call(Node, ssl, transport_accept, [ListenSocket]), - ct:print("ssl:ssl_accept(~p, ~p)~n", [AcceptSocket, Timeout]), + ct:log("ssl:ssl_accept(~p, ~p)~n", [AcceptSocket, Timeout]), case rpc:call(Node, ssl, ssl_accept, [AcceptSocket, Timeout]) of ok -> @@ -161,27 +161,27 @@ run_client(Opts) -> Pid = proplists:get_value(from, Opts), Transport = proplists:get_value(transport, Opts, ssl), Options = proplists:get_value(options, Opts), - ct:print("ssl:connect(~p, ~p, ~p)~n", [Host, Port, Options]), + ct:log("ssl:connect(~p, ~p, ~p)~n", [Host, Port, Options]), case rpc:call(Node, Transport, connect, [Host, Port, Options]) of {ok, Socket} -> Pid ! { connected, Socket }, - ct:print("Client: connected~n", []), + ct:log("Client: connected~n", []), %% In special cases we want to know the client port, it will %% be indicated by sending {port, 0} in options list! send_selected_port(Pid, proplists:get_value(port, Options), Socket), {Module, Function, Args} = proplists:get_value(mfa, Opts), - ct:print("Client: apply(~p,~p,~p)~n", + ct:log("Client: apply(~p,~p,~p)~n", [Module, Function, [Socket | Args]]), case rpc:call(Node, Module, Function, [Socket | Args]) of no_result_msg -> ok; Msg -> - ct:print("Client Msg: ~p ~n", [Msg]), + ct:log("Client Msg: ~p ~n", [Msg]), Pid ! {self(), Msg} end, receive close -> - ct:print("Client closing~n", []), + ct:log("Client closing~n", []), rpc:call(Node, Transport, close, [Socket]); {ssl_closed, Socket} -> ok; @@ -189,18 +189,18 @@ run_client(Opts) -> ok end; {error, Reason} -> - ct:print("Client: connection failed: ~p ~n", [Reason]), + ct:log("Client: connection failed: ~p ~n", [Reason]), Pid ! {self(), {error, Reason}} end. close(Pid) -> - ct:print("Close ~p ~n", [Pid]), + ct:log("Close ~p ~n", [Pid]), Monitor = erlang:monitor(process, Pid), Pid ! close, receive {'DOWN', Monitor, process, Pid, Reason} -> erlang:demonitor(Monitor), - ct:print("Pid: ~p down due to:~p ~n", [Pid, Reason]) + ct:log("Pid: ~p down due to:~p ~n", [Pid, Reason]) end. check_result(Server, ServerMsg, Client, ClientMsg) -> @@ -339,12 +339,22 @@ cert_options(Config) -> {psk_identity, "HINT"}, {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}}, {ciphers, psk_suites()}]}, + {server_psk_anon, [{ssl_imp, new},{reuseaddr, true}, + {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}}, + {ciphers, psk_anon_suites()}]}, + {server_psk_anon_hint, [{ssl_imp, new},{reuseaddr, true}, + {psk_identity, "HINT"}, + {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}}, + {ciphers, psk_anon_suites()}]}, {client_srp, [{ssl_imp, new},{reuseaddr, true}, {srp_identity, {"Test-User", "secret"}}]}, {server_srp, [{ssl_imp, new},{reuseaddr, true}, {certfile, ServerCertFile}, {keyfile, ServerKeyFile}, {user_lookup_fun, {fun user_lookup/3, undefined}}, {ciphers, srp_suites()}]}, + {server_srp_anon, [{ssl_imp, new},{reuseaddr, true}, + {user_lookup_fun, {fun user_lookup/3, undefined}}, + {ciphers, srp_anon_suites()}]}, {server_verification_opts, [{ssl_imp, new},{reuseaddr, true}, {cacertfile, ServerCaCertFile}, {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]}, @@ -394,6 +404,49 @@ make_dsa_cert(Config) -> {certfile, ClientCertFile}, {keyfile, ClientKeyFile}]} | Config]. +make_ecdsa_cert(Config) -> + case proplists:get_bool(ec, crypto:algorithms()) of + true -> + {ServerCaCertFile, ServerCertFile, ServerKeyFile} = make_cert_files("server", Config, ec, ec, ""), + {ClientCaCertFile, ClientCertFile, ClientKeyFile} = make_cert_files("client", Config, ec, ec, ""), + [{server_ecdsa_opts, [{ssl_imp, new},{reuseaddr, true}, + {cacertfile, ServerCaCertFile}, + {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]}, + {server_ecdsa_verify_opts, [{ssl_imp, new},{reuseaddr, true}, + {cacertfile, ClientCaCertFile}, + {certfile, ServerCertFile}, {keyfile, ServerKeyFile}, + {verify, verify_peer}]}, + {client_ecdsa_opts, [{ssl_imp, new},{reuseaddr, true}, + {cacertfile, ClientCaCertFile}, + {certfile, ClientCertFile}, {keyfile, ClientKeyFile}]} + | Config]; + _ -> + Config + end. + +%% RFC 4492, Sect. 2.3. ECDH_RSA +%% +%% This key exchange algorithm is the same as ECDH_ECDSA except that the +%% server's certificate MUST be signed with RSA rather than ECDSA. +make_ecdh_rsa_cert(Config) -> + case proplists:get_bool(ec, crypto:algorithms()) of + true -> + {ServerCaCertFile, ServerCertFile, ServerKeyFile} = make_cert_files("server", Config, rsa, ec, "rsa_"), + {ClientCaCertFile, ClientCertFile, ClientKeyFile} = make_cert_files("client", Config, rsa, ec, "rsa_"), + [{server_ecdh_rsa_opts, [{ssl_imp, new},{reuseaddr, true}, + {cacertfile, ServerCaCertFile}, + {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]}, + {server_ecdh_rsa_verify_opts, [{ssl_imp, new},{reuseaddr, true}, + {cacertfile, ClientCaCertFile}, + {certfile, ServerCertFile}, {keyfile, ServerKeyFile}, + {verify, verify_peer}]}, + {client_ecdh_rsa_opts, [{ssl_imp, new},{reuseaddr, true}, + {cacertfile, ClientCaCertFile}, + {certfile, ClientCertFile}, {keyfile, ClientKeyFile}]} + | Config]; + _ -> + Config + end. make_mix_cert(Config) -> {ServerCaCertFile, ServerCertFile, ServerKeyFile} = make_cert_files("server", Config, dsa, @@ -445,33 +498,33 @@ run_upgrade_server(Opts) -> SslOptions = proplists:get_value(ssl_options, Opts), Pid = proplists:get_value(from, Opts), - ct:print("gen_tcp:listen(~p, ~p)~n", [Port, TcpOptions]), + ct:log("gen_tcp:listen(~p, ~p)~n", [Port, TcpOptions]), {ok, ListenSocket} = rpc:call(Node, gen_tcp, listen, [Port, TcpOptions]), Pid ! {listen, up}, send_selected_port(Pid, Port, ListenSocket), - ct:print("gen_tcp:accept(~p)~n", [ListenSocket]), + ct:log("gen_tcp:accept(~p)~n", [ListenSocket]), {ok, AcceptSocket} = rpc:call(Node, gen_tcp, accept, [ListenSocket]), try {ok, SslAcceptSocket} = case TimeOut of infinity -> - ct:print("ssl:ssl_accept(~p, ~p)~n", + ct:log("ssl:ssl_accept(~p, ~p)~n", [AcceptSocket, SslOptions]), rpc:call(Node, ssl, ssl_accept, [AcceptSocket, SslOptions]); _ -> - ct:print("ssl:ssl_accept(~p, ~p, ~p)~n", + ct:log("ssl:ssl_accept(~p, ~p, ~p)~n", [AcceptSocket, SslOptions, TimeOut]), rpc:call(Node, ssl, ssl_accept, [AcceptSocket, SslOptions, TimeOut]) end, {Module, Function, Args} = proplists:get_value(mfa, Opts), Msg = rpc:call(Node, Module, Function, [SslAcceptSocket | Args]), - ct:print("Upgrade Server Msg: ~p ~n", [Msg]), + ct:log("Upgrade Server Msg: ~p ~n", [Msg]), Pid ! {self(), Msg}, receive close -> - ct:print("Upgrade Server closing~n", []), + ct:log("Upgrade Server closing~n", []), rpc:call(Node, ssl, close, [SslAcceptSocket]) end catch error:{badmatch, Error} -> @@ -489,24 +542,24 @@ run_upgrade_client(Opts) -> TcpOptions = proplists:get_value(tcp_options, Opts), SslOptions = proplists:get_value(ssl_options, Opts), - ct:print("gen_tcp:connect(~p, ~p, ~p)~n", + ct:log("gen_tcp:connect(~p, ~p, ~p)~n", [Host, Port, TcpOptions]), {ok, Socket} = rpc:call(Node, gen_tcp, connect, [Host, Port, TcpOptions]), send_selected_port(Pid, Port, Socket), - ct:print("ssl:connect(~p, ~p)~n", [Socket, SslOptions]), + ct:log("ssl:connect(~p, ~p)~n", [Socket, SslOptions]), {ok, SslSocket} = rpc:call(Node, ssl, connect, [Socket, SslOptions]), {Module, Function, Args} = proplists:get_value(mfa, Opts), - ct:print("apply(~p, ~p, ~p)~n", + ct:log("apply(~p, ~p, ~p)~n", [Module, Function, [SslSocket | Args]]), Msg = rpc:call(Node, Module, Function, [SslSocket | Args]), - ct:print("Upgrade Client Msg: ~p ~n", [Msg]), + ct:log("Upgrade Client Msg: ~p ~n", [Msg]), Pid ! {self(), Msg}, receive close -> - ct:print("Upgrade Client closing~n", []), + ct:log("Upgrade Client closing~n", []), rpc:call(Node, ssl, close, [SslSocket]) end. @@ -525,20 +578,20 @@ run_upgrade_server_error(Opts) -> SslOptions = proplists:get_value(ssl_options, Opts), Pid = proplists:get_value(from, Opts), - ct:print("gen_tcp:listen(~p, ~p)~n", [Port, TcpOptions]), + ct:log("gen_tcp:listen(~p, ~p)~n", [Port, TcpOptions]), {ok, ListenSocket} = rpc:call(Node, gen_tcp, listen, [Port, TcpOptions]), Pid ! {listen, up}, send_selected_port(Pid, Port, ListenSocket), - ct:print("gen_tcp:accept(~p)~n", [ListenSocket]), + ct:log("gen_tcp:accept(~p)~n", [ListenSocket]), {ok, AcceptSocket} = rpc:call(Node, gen_tcp, accept, [ListenSocket]), Error = case TimeOut of infinity -> - ct:print("ssl:ssl_accept(~p, ~p)~n", + ct:log("ssl:ssl_accept(~p, ~p)~n", [AcceptSocket, SslOptions]), rpc:call(Node, ssl, ssl_accept, [AcceptSocket, SslOptions]); _ -> - ct:print("ssl:ssl_accept(~p, ~p, ~p)~n", + ct:log("ssl:ssl_accept(~p, ~p, ~p)~n", [AcceptSocket, SslOptions, TimeOut]), rpc:call(Node, ssl, ssl_accept, [AcceptSocket, SslOptions, TimeOut]) @@ -558,26 +611,26 @@ run_server_error(Opts) -> Options = proplists:get_value(options, Opts), Pid = proplists:get_value(from, Opts), Transport = proplists:get_value(transport, Opts, ssl), - ct:print("ssl:listen(~p, ~p)~n", [Port, Options]), + ct:log("ssl:listen(~p, ~p)~n", [Port, Options]), case rpc:call(Node, Transport, listen, [Port, Options]) of {ok, #sslsocket{} = ListenSocket} -> %% To make sure error_client will %% get {error, closed} and not {error, connection_refused} Pid ! {listen, up}, send_selected_port(Pid, Port, ListenSocket), - ct:print("ssl:transport_accept(~p)~n", [ListenSocket]), + ct:log("ssl:transport_accept(~p)~n", [ListenSocket]), case rpc:call(Node, Transport, transport_accept, [ListenSocket]) of {error, _} = Error -> Pid ! {self(), Error}; {ok, AcceptSocket} -> - ct:print("ssl:ssl_accept(~p)~n", [AcceptSocket]), + ct:log("ssl:ssl_accept(~p)~n", [AcceptSocket]), Error = rpc:call(Node, ssl, ssl_accept, [AcceptSocket]), Pid ! {self(), Error} end; {ok, ListenSocket} -> Pid ! {listen, up}, send_selected_port(Pid, Port, ListenSocket), - ct:print("~p:accept(~p)~n", [Transport, ListenSocket]), + ct:log("~p:accept(~p)~n", [Transport, ListenSocket]), case rpc:call(Node, Transport, accept, [ListenSocket]) of {error, _} = Error -> Pid ! {self(), Error} @@ -599,7 +652,7 @@ run_client_error(Opts) -> Pid = proplists:get_value(from, Opts), Transport = proplists:get_value(transport, Opts, ssl), Options = proplists:get_value(options, Opts), - ct:print("ssl:connect(~p, ~p, ~p)~n", [Host, Port, Options]), + ct:log("ssl:connect(~p, ~p, ~p)~n", [Host, Port, Options]), Error = rpc:call(Node, Transport, connect, [Host, Port, Options]), Pid ! {self(), Error}. @@ -656,11 +709,16 @@ send_selected_port(Pid, 0, Socket) -> send_selected_port(_,_,_) -> ok. -rsa_suites() -> - lists:filter(fun({dhe_dss, _, _}) -> - false; +rsa_suites(CounterPart) -> + ECC = is_sane_ecc(CounterPart), + lists:filter(fun({rsa, _, _}) -> + true; + ({dhe_rsa, _, _}) -> + true; + ({ecdhe_rsa, _, _}) when ECC == true -> + true; (_) -> - true + false end, ssl:cipher_suites()). @@ -680,17 +738,32 @@ dsa_suites() -> end, ssl:cipher_suites()). +ecdsa_suites() -> + lists:filter(fun({ecdhe_ecdsa, _, _}) -> + true; + (_) -> + false + end, + ssl:cipher_suites()). + +ecdh_rsa_suites() -> + lists:filter(fun({ecdh_rsa, _, _}) -> + true; + (_) -> + false + end, + ssl:cipher_suites()). openssl_rsa_suites() -> Ciphers = ssl:cipher_suites(openssl), lists:filter(fun(Str) -> - case re:run(Str,"DSS",[]) of + case re:run(Str,"DSS|ECDH-RSA|ECDSA",[]) of nomatch -> true; _ -> false end - end, Ciphers). + end, Ciphers). openssl_dsa_suites() -> Ciphers = ssl:cipher_suites(openssl), @@ -703,14 +776,58 @@ openssl_dsa_suites() -> end end, Ciphers). +openssl_ecdsa_suites() -> + Ciphers = ssl:cipher_suites(openssl), + lists:filter(fun(Str) -> + case re:run(Str,"ECDHE-ECDSA",[]) of + nomatch -> + false; + _ -> + true + end + end, Ciphers). + +openssl_ecdh_rsa_suites() -> + Ciphers = ssl:cipher_suites(openssl), + lists:filter(fun(Str) -> + case re:run(Str,"ECDH-RSA",[]) of + nomatch -> + false; + _ -> + true + end + end, Ciphers). + anonymous_suites() -> - [{dh_anon, rc4_128, md5}, - {dh_anon, des_cbc, sha}, - {dh_anon, '3des_ede_cbc', sha}, - {dh_anon, aes_128_cbc, sha}, - {dh_anon, aes_256_cbc, sha}]. + Suites = + [{dh_anon, rc4_128, md5}, + {dh_anon, des_cbc, sha}, + {dh_anon, '3des_ede_cbc', sha}, + {dh_anon, aes_128_cbc, sha}, + {dh_anon, aes_256_cbc, sha}, + {ecdh_anon,rc4_128,sha}, + {ecdh_anon,'3des_ede_cbc',sha}, + {ecdh_anon,aes_128_cbc,sha}, + {ecdh_anon,aes_256_cbc,sha}], + ssl_cipher:filter_suites(Suites). psk_suites() -> + Suites = + [{psk, rc4_128, sha}, + {psk, '3des_ede_cbc', sha}, + {psk, aes_128_cbc, sha}, + {psk, aes_256_cbc, sha}, + {dhe_psk, rc4_128, sha}, + {dhe_psk, '3des_ede_cbc', sha}, + {dhe_psk, aes_128_cbc, sha}, + {dhe_psk, aes_256_cbc, sha}, + {rsa_psk, rc4_128, sha}, + {rsa_psk, '3des_ede_cbc', sha}, + {rsa_psk, aes_128_cbc, sha}, + {rsa_psk, aes_256_cbc, sha}], + ssl_cipher:filter_suites(Suites). + +psk_anon_suites() -> [{psk, rc4_128, sha}, {psk, '3des_ede_cbc', sha}, {psk, aes_128_cbc, sha}, @@ -718,24 +835,29 @@ psk_suites() -> {dhe_psk, rc4_128, sha}, {dhe_psk, '3des_ede_cbc', sha}, {dhe_psk, aes_128_cbc, sha}, - {dhe_psk, aes_256_cbc, sha}, - {rsa_psk, rc4_128, sha}, - {rsa_psk, '3des_ede_cbc', sha}, - {rsa_psk, aes_128_cbc, sha}, - {rsa_psk, aes_256_cbc, sha}]. + {dhe_psk, aes_256_cbc, sha}]. srp_suites() -> + Suites = + [{srp_anon, '3des_ede_cbc', sha}, + {srp_rsa, '3des_ede_cbc', sha}, + {srp_anon, aes_128_cbc, sha}, + {srp_rsa, aes_128_cbc, sha}, + {srp_anon, aes_256_cbc, sha}, + {srp_rsa, aes_256_cbc, sha}], + ssl_cipher:filter_suites(Suites). + +srp_anon_suites() -> [{srp_anon, '3des_ede_cbc', sha}, - {srp_rsa, '3des_ede_cbc', sha}, {srp_anon, aes_128_cbc, sha}, - {srp_rsa, aes_128_cbc, sha}, - {srp_anon, aes_256_cbc, sha}, - {srp_rsa, aes_256_cbc, sha}]. + {srp_anon, aes_256_cbc, sha}]. srp_dss_suites() -> - [{srp_dss, '3des_ede_cbc', sha}, - {srp_dss, aes_128_cbc, sha}, - {srp_dss, aes_256_cbc, sha}]. + Suites = + [{srp_dss, '3des_ede_cbc', sha}, + {srp_dss, aes_128_cbc, sha}, + {srp_dss, aes_256_cbc, sha}], + ssl_cipher:filter_suites(Suites). pem_to_der(File) -> {ok, PemBin} = file:read_file(File), @@ -747,7 +869,7 @@ der_to_pem(File, Entries) -> cipher_result(Socket, Result) -> Result = ssl:connection_info(Socket), - ct:print("Successfull connect: ~p~n", [Result]), + ct:log("Successfull connect: ~p~n", [Result]), %% Importante to send two packets here %% to properly test "cipher state" handling ssl:send(Socket, "Hello\n"), @@ -817,15 +939,9 @@ init_tls_version(Version) -> ssl:start(). sufficient_crypto_support('tlsv1.2') -> - Data = "Sampl", - Data2 = "e #1", - Key = <<0,1,2,3,16,17,18,19,32,33,34,35,48,49,50,51,4,5,6,7,20,21,22,23,36,37,38,39, - 52,53,54,55,8,9,10,11,24,25,26,27,40,41,42,43,56,57,58,59>>, - try - crypto:sha256_mac(Key, lists:flatten([Data, Data2])), - true - catch _:_ -> false - end; + proplists:get_bool(sha256, crypto:algorithms()); +sufficient_crypto_support(ciphers_ec) -> + proplists:get_bool(ec, crypto:algorithms()); sufficient_crypto_support(_) -> true. @@ -858,3 +974,35 @@ send_recv_result_active_once(Socket) -> {ssl, Socket, "Hello world"} -> ok end. + +is_sane_ecc(openssl) -> + case os:cmd("openssl version") of + "OpenSSL 1.0.0a" ++ _ -> % Known bug in openssl + %% manifests as SSL_CHECK_SERVERHELLO_TLSEXT:tls invalid ecpointformat list + false; + "OpenSSL 1.0.0" ++ _ -> % Known bug in openssl + %% manifests as SSL_CHECK_SERVERHELLO_TLSEXT:tls invalid ecpointformat list + false; + "OpenSSL 0.9.8" ++ _ -> % Does not support ECC + false; + "OpenSSL 0.9.7" ++ _ -> % Does not support ECC + false; + _ -> + true + end; +is_sane_ecc(_) -> + true. + +cipher_restriction(Config0) -> + case is_sane_ecc(openssl) of + false -> + Opts = proplists:get_value(server_opts, Config0), + Config1 = proplists:delete(server_opts, Config0), + VerOpts = proplists:get_value(server_verification_opts, Config1), + Config = proplists:delete(server_verification_opts, Config1), + Restricted0 = ssl:cipher_suites() -- ecdsa_suites(), + Restricted = Restricted0 -- ecdh_rsa_suites(), + [{server_opts, [{ciphers, Restricted} | Opts]}, {server_verification_opts, [{ciphers, Restricted} | VerOpts] } | Config]; + true -> + Config0 + end. diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl index a3d382f837..075b4b1ec4 100644 --- a/lib/ssl/test/ssl_to_openssl_SUITE.erl +++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl @@ -104,10 +104,11 @@ init_per_suite(Config0) -> Result = (catch make_certs:all(?config(data_dir, Config0), ?config(priv_dir, Config0))), - ct:print("Make certs ~p~n", [Result]), + ct:log("Make certs ~p~n", [Result]), Config1 = ssl_test_lib:make_dsa_cert(Config0), Config = ssl_test_lib:cert_options(Config1), - [{watchdog, Dog} | Config] + NewConfig = [{watchdog, Dog} | Config], + ssl_test_lib:cipher_restriction(NewConfig) catch _:_ -> {skip, "Crypto did not start"} end @@ -200,7 +201,7 @@ basic_erlang_client_openssl_server(Config) when is_list(Config) -> Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile, - ct:print("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -212,7 +213,7 @@ basic_erlang_client_openssl_server(Config) when is_list(Config) -> {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, {options, ClientOpts}]), - port_command(OpensslPort, Data), + true = port_command(OpensslPort, Data), ssl_test_lib:check_result(Client, ok), @@ -241,10 +242,10 @@ basic_erlang_server_openssl_client(Config) when is_list(Config) -> Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ " -host localhost", - ct:print("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - port_command(OpenSslPort, Data), + true = port_command(OpenSslPort, Data), ssl_test_lib:check_result(Server, ok), @@ -272,7 +273,7 @@ erlang_client_openssl_server(Config) when is_list(Config) -> Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile, - ct:print("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -284,7 +285,7 @@ erlang_client_openssl_server(Config) when is_list(Config) -> {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, {options, ClientOpts}]), - port_command(OpensslPort, Data), + true = port_command(OpensslPort, Data), ssl_test_lib:check_result(Client, ok), @@ -314,10 +315,10 @@ erlang_server_openssl_client(Config) when is_list(Config) -> Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -host localhost", - ct:print("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - port_command(OpenSslPort, Data), + true = port_command(OpenSslPort, Data), ssl_test_lib:check_result(Server, ok), @@ -349,7 +350,7 @@ erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) -> " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile ++ " -key " ++ KeyFile ++ " -Verify 2 -msg", - ct:print("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -362,7 +363,7 @@ erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) -> erlang_ssl_receive, [Data]}}, {options, ClientOpts}]), - port_command(OpensslPort, Data), + true = port_command(OpensslPort, Data), ssl_test_lib:check_result(Client, ok), @@ -396,10 +397,10 @@ erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) -> " -host localhost " ++ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile ++ " -key " ++ KeyFile ++ " -msg", - ct:print("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - port_command(OpenSslPort, Data), + true = port_command(OpenSslPort, Data), ssl_test_lib:check_result(Server, ok), @@ -431,11 +432,11 @@ erlang_server_openssl_client_reuse_session(Config) when is_list(Config) -> Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -host localhost -reconnect", - ct:print("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - port_command(OpenSslPort, Data), + true = port_command(OpenSslPort, Data), ssl_test_lib:check_result(Server, ok), @@ -467,7 +468,7 @@ erlang_client_openssl_server_renegotiate(Config) when is_list(Config) -> Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -msg", - ct:print("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -480,9 +481,9 @@ erlang_client_openssl_server_renegotiate(Config) when is_list(Config) -> delayed_send, [[ErlData, OpenSslData]]}}, {options, ClientOpts}]), - port_command(OpensslPort, ?OPENSSL_RENEGOTIATE), + true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE), ct:sleep(?SLEEP), - port_command(OpensslPort, OpenSslData), + true = port_command(OpensslPort, OpenSslData), ssl_test_lib:check_result(Client, ok), @@ -516,7 +517,7 @@ erlang_client_openssl_server_nowrap_seqnum(Config) when is_list(Config) -> Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -msg", - ct:print("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -562,11 +563,11 @@ erlang_server_openssl_client_nowrap_seqnum(Config) when is_list(Config) -> Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -host localhost -msg", - ct:print("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - port_command(OpenSslPort, Data), + true = port_command(OpenSslPort, Data), ssl_test_lib:check_result(Server, ok), @@ -597,7 +598,7 @@ erlang_client_openssl_server_no_server_ca_cert(Config) when is_list(Config) -> Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -msg", - ct:print("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -610,7 +611,7 @@ erlang_client_openssl_server_no_server_ca_cert(Config) when is_list(Config) -> erlang_ssl_receive, [Data]}}, {options, ClientOpts}]), - port_command(OpensslPort, Data), + true = port_command(OpensslPort, Data), ssl_test_lib:check_result(Client, ok), @@ -640,7 +641,7 @@ erlang_client_openssl_server_client_cert(Config) when is_list(Config) -> " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile ++ " -key " ++ KeyFile ++ " -Verify 2", - ct:print("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -652,7 +653,7 @@ erlang_client_openssl_server_client_cert(Config) when is_list(Config) -> {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, {options, ClientOpts}]), - port_command(OpensslPort, Data), + true = port_command(OpensslPort, Data), ssl_test_lib:check_result(Client, ok), @@ -691,10 +692,10 @@ erlang_server_openssl_client_client_cert(Config) when is_list(Config) -> ++ " -key " ++ KeyFile ++ " -port " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -host localhost", - ct:print("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - port_command(OpenSslPort, Data), + true = port_command(OpenSslPort, Data), ssl_test_lib:check_result(Server, ok), @@ -749,7 +750,7 @@ ciphers_rsa_signed_certs(Config) when is_list(Config) -> Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), - Ciphers = ssl_test_lib:rsa_suites(), + Ciphers = ssl_test_lib:rsa_suites(openssl), run_suites(Ciphers, Version, Config, rsa). %%-------------------------------------------------------------------- @@ -779,7 +780,7 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) -> Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "", - ct:print("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -793,7 +794,7 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) -> [{versions, [Version]} | ClientOpts]}]), %% Send garbage - port_command(OpensslPort, ?OPENSSL_GARBAGE), + true = port_command(OpensslPort, ?OPENSSL_GARBAGE), ct:sleep(?SLEEP), @@ -835,7 +836,7 @@ expired_session(Config) when is_list(Config) -> Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "", - ct:print("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -893,10 +894,10 @@ ssl2_erlang_server_openssl_client(Config) when is_list(Config) -> Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ " -host localhost -ssl2 -msg", - ct:print("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - port_command(OpenSslPort, Data), + true = port_command(OpenSslPort, Data), receive {'EXIT', OpenSslPort, _} -> ok @@ -912,7 +913,7 @@ erlang_client_openssl_server_npn() -> erlang_client_openssl_server_npn(Config) when is_list(Config) -> Data = "From openssl to erlang", start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, fun(Client, OpensslPort) -> - port_command(OpensslPort, Data), + true = port_command(OpensslPort, Data), ssl_test_lib:check_result(Client, ok) end), @@ -925,9 +926,9 @@ erlang_client_openssl_server_npn_renegotiate() -> erlang_client_openssl_server_npn_renegotiate(Config) when is_list(Config) -> Data = "From openssl to erlang", start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, fun(Client, OpensslPort) -> - port_command(OpensslPort, ?OPENSSL_RENEGOTIATE), + true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE), ct:sleep(?SLEEP), - port_command(OpensslPort, Data), + true = port_command(OpensslPort, Data), ssl_test_lib:check_result(Client, ok) end), ok. @@ -939,7 +940,7 @@ erlang_server_openssl_client_npn(Config) when is_list(Config) -> Data = "From openssl to erlang", start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, fun(Server, OpensslPort) -> - port_command(OpensslPort, Data), + true = port_command(OpensslPort, Data), ssl_test_lib:check_result(Server, ok) end), ok. @@ -951,9 +952,9 @@ erlang_server_openssl_client_npn_renegotiate() -> erlang_server_openssl_client_npn_renegotiate(Config) when is_list(Config) -> Data = "From openssl to erlang", start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, fun(Server, OpensslPort) -> - port_command(OpensslPort, ?OPENSSL_RENEGOTIATE), + true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE), ct:sleep(?SLEEP), - port_command(OpensslPort, Data), + true = port_command(OpensslPort, Data), ssl_test_lib:check_result(Server, ok) end), ok. @@ -962,7 +963,7 @@ erlang_client_openssl_server_npn_only_server(Config) when is_list(Config) -> Data = "From openssl to erlang", start_erlang_client_and_openssl_server_with_opts(Config, [], "-nextprotoneg spdy/2", Data, fun(Server, OpensslPort) -> - port_command(OpensslPort, Data), + true = port_command(OpensslPort, Data), ssl_test_lib:check_result(Server, ok) end), ok. @@ -975,7 +976,7 @@ erlang_client_openssl_server_npn_only_client(Config) when is_list(Config) -> [{client_preferred_next_protocols, {client, [<<"spdy/2">>], <<"http/1.1">>}}], "", Data, fun(Server, OpensslPort) -> - port_command(OpensslPort, Data), + true = port_command(OpensslPort, Data), ssl_test_lib:check_result(Server, ok) end), ok. @@ -985,7 +986,7 @@ erlang_server_openssl_client_npn_only_server(Config) when is_list(Config) -> Data = "From openssl to erlang", start_erlang_server_and_openssl_client_with_opts(Config, [{next_protocols_advertised, [<<"spdy/2">>]}], "", Data, fun(Server, OpensslPort) -> - port_command(OpensslPort, Data), + true = port_command(OpensslPort, Data), ssl_test_lib:check_result(Server, ok) end), ok. @@ -994,7 +995,7 @@ erlang_server_openssl_client_npn_only_client(Config) when is_list(Config) -> Data = "From openssl to erlang", start_erlang_server_and_openssl_client_with_opts(Config, [], "-nextprotoneg spdy/2", Data, fun(Server, OpensslPort) -> - port_command(OpensslPort, Data), + true = port_command(OpensslPort, Data), ssl_test_lib:check_result(Server, ok) end), ok. @@ -1020,13 +1021,13 @@ run_suites(Ciphers, Version, Config, Type) -> [] -> ok; Error -> - ct:print("Cipher suite errors: ~p~n", [Error]), + ct:log("Cipher suite errors: ~p~n", [Error]), ct:fail(cipher_suite_failed_see_test_case_log) end. cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> process_flag(trap_exit, true), - ct:print("Testing CipherSuite ~p~n", [CipherSuite]), + ct:log("Testing CipherSuite ~p~n", [CipherSuite]), {ClientNode, _ServerNode, Hostname} = ssl_test_lib:run_where(Config), Port = ssl_test_lib:inet_port(node()), @@ -1036,7 +1037,7 @@ cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "", - ct:print("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -1052,19 +1053,19 @@ cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> [{ciphers,[CipherSuite]} | ClientOpts]}]), - port_command(OpenSslPort, "Hello\n"), + true = port_command(OpenSslPort, "Hello\n"), receive {Port, {data, _}} when is_port(Port) -> ok after 500 -> - ct:print("Time out on openssl port, check that" + ct:log("Time out on openssl port, check that" " the messages Hello and world are received" " during close of port" , []), ok end, - port_command(OpenSslPort, " world\n"), + true = port_command(OpenSslPort, " world\n"), Result = ssl_test_lib:wait_for_result(Client, ok), @@ -1100,7 +1101,7 @@ start_erlang_client_and_openssl_server_with_opts(Config, ErlangClientOpts, Opens integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile, - ct:print("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -1139,7 +1140,7 @@ start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, Callbac Cmd = "openssl s_server -msg -nextprotoneg http/1.1,spdy/2 -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile, - ct:print("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -1177,7 +1178,7 @@ start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, Callbac Cmd = "openssl s_client -nextprotoneg http/1.0,spdy/2 -msg -port " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -host localhost", - ct:print("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -1206,7 +1207,7 @@ start_erlang_server_and_openssl_client_with_opts(Config, ErlangServerOpts, OpenS Cmd = "openssl s_client " ++ OpenSSLClientOpts ++ " -msg -port " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -host localhost", - ct:print("openssl cmd: ~p~n", [Cmd]), + ct:log("openssl cmd: ~p~n", [Cmd]), OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -1225,7 +1226,7 @@ erlang_ssl_receive_and_assert_npn(Socket, Protocol, Data) -> ok. erlang_ssl_receive(Socket, Data) -> - ct:print("Connection info: ~p~n", + ct:log("Connection info: ~p~n", [ssl:connection_info(Socket)]), receive {ssl, Socket, Data} -> @@ -1247,7 +1248,7 @@ erlang_ssl_receive(Socket, Data) -> connection_info(Socket, Version) -> case ssl:connection_info(Socket) of {ok, {Version, _} = Info} -> - ct:print("Connection info: ~p~n", [Info]), + ct:log("Connection info: ~p~n", [Info]), ok; {ok, {OtherVersion, _}} -> {wrong_version, OtherVersion} @@ -1269,28 +1270,28 @@ close_port(Port) -> close_loop(Port, Time, SentClose) -> receive {Port, {data,Debug}} when is_port(Port) -> - io:format("openssl ~s~n",[Debug]), + ct:log("openssl ~s~n",[Debug]), close_loop(Port, Time, SentClose); {ssl,_,Msg} -> - io:format("ssl Msg ~s~n",[Msg]), + ct:log("ssl Msg ~s~n",[Msg]), close_loop(Port, Time, SentClose); {Port, closed} -> - io:format("Port Closed~n",[]), + ct:log("Port Closed~n",[]), ok; {'EXIT', Port, Reason} -> - io:format("Port Closed ~p~n",[Reason]), + ct:log("Port Closed ~p~n",[Reason]), ok; Msg -> - io:format("Port Msg ~p~n",[Msg]), + ct:log("Port Msg ~p~n",[Msg]), close_loop(Port, Time, SentClose) after Time -> case SentClose of false -> - io:format("Closing port ~n",[]), + ct:log("Closing port ~n",[]), catch erlang:port_close(Port), close_loop(Port, Time, true); true -> - io:format("Timeout~n",[]) + ct:log("Timeout~n",[]) end end. @@ -1305,7 +1306,7 @@ server_sent_garbage(Socket) -> wait_for_openssl_server() -> receive {Port, {data, Debug}} when is_port(Port) -> - io:format("openssl ~s~n",[Debug]), + ct:log("openssl ~s~n",[Debug]), %% openssl has started make sure %% it will be in accept. Parsing %% output is too error prone. (Even diff --git a/lib/stdlib/doc/src/lists.xml b/lib/stdlib/doc/src/lists.xml index b6c0fa4e05..1aff78f4fc 100644 --- a/lib/stdlib/doc/src/lists.xml +++ b/lib/stdlib/doc/src/lists.xml @@ -152,6 +152,31 @@ </desc> </func> <func> + <name name="filtermap" arity="2"/> + <fsummary>Filter and map elements which satisfy a function</fsummary> + <desc> + <p>Calls <c><anno>Fun</anno>(<anno>Elem</anno>)</c> on successive elements <c>Elem</c> + of <c><anno>List1</anno></c>. <c><anno>Fun</anno>/2</c> must return either a boolean + or a tuple <c>{true, <anno>Value</anno>}</c>. The function returns the list of elements + for which <c><anno>Fun</anno></c> returns a new value, where a value of <c>true</c> + is synonymous with <c>{true, <anno>Elem</anno>}</c>.</p> + <p>That is, <c>filtermap</c> behaves as if it had been defined as follows:</p> + <code type="none"> +filtermap(Fun, List1) -> + lists:foldr(fun(Elem, Acc) -> + case Fun(Elem) of + false -> Acc; + true -> [Elem|Acc]; + {true,Value} -> [Value|Acc] + end, + end, [], List1).</code> + <p>Example:</p> + <pre> +> <input>lists:filtermap(fun(X) -> case X rem 2 of 0 -> {true, X div 2}; _ -> false end end, [1,2,3,4,5]).</input> +[1,2]</pre> + </desc> + </func> + <func> <name name="flatlength" arity="1"/> <fsummary>Length of flattened deep list</fsummary> <desc> diff --git a/lib/stdlib/doc/src/unicode_usage.xml b/lib/stdlib/doc/src/unicode_usage.xml index c5d476e54b..1f64b38554 100644 --- a/lib/stdlib/doc/src/unicode_usage.xml +++ b/lib/stdlib/doc/src/unicode_usage.xml @@ -625,6 +625,7 @@ Eshell V5.10.1 (abort with ^G) </section> <section> <title>Unicode File Names</title> + <marker id="unicode_file_names"/> <p>Most modern operating systems support Unicode file names in some way or another. There are several different ways to do this and Erlang by default treats the different approaches differently:</p> diff --git a/lib/stdlib/src/dets.erl b/lib/stdlib/src/dets.erl index 45aef0ea80..50812cc532 100644 --- a/lib/stdlib/src/dets.erl +++ b/lib/stdlib/src/dets.erl @@ -1246,13 +1246,8 @@ req(Proc, R) -> {'DOWN', Ref, process, Proc, _Info} -> badarg; {Proc, Reply} -> - erlang:demonitor(Ref), - receive - {'DOWN', Ref, process, Proc, _Reason} -> - Reply - after 0 -> - Reply - end + erlang:demonitor(Ref, [flush]), + Reply end. %% Inlined. diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl index 0a1caa7178..d1d060ebc8 100644 --- a/lib/stdlib/src/epp.erl +++ b/lib/stdlib/src/epp.erl @@ -601,7 +601,7 @@ enter_file2(NewF, Pname, From, St0, AtLocation) -> %% file will depend on the order of file inclusions in the parent files Path = [filename:dirname(Pname) | tl(St0#epp.path)], _ = set_encoding(NewF), - #epp{file=NewF,location=Loc,name=Pname,delta=0, + #epp{file=NewF,location=Loc,name=Pname,name2=Pname,delta=0, sstk=[St0|St0#epp.sstk],path=Path,macs=Ms}. enter_file_reply(From, Name, Location, AtLocation) -> @@ -1339,8 +1339,7 @@ epp_reply(From, Rep) -> wait_epp_reply(Epp, Mref) -> receive {epp_reply,Epp,Rep} -> - erlang:demonitor(Mref), - receive {'DOWN',Mref,_,_,_} -> ok after 0 -> ok end, + erlang:demonitor(Mref, [flush]), Rep; {'DOWN',Mref,_,_,E} -> receive {epp_reply,Epp,Rep} -> Rep diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl index 7c7566e4ec..c0596e5ba6 100644 --- a/lib/stdlib/src/erl_pp.erl +++ b/lib/stdlib/src/erl_pp.erl @@ -35,7 +35,7 @@ | fun((Expr :: erl_parse:abstract_expr(), CurrentIndentation :: integer(), CurrentPrecedence :: non_neg_integer(), - HookFunction :: hook_function()) -> + Options :: options()) -> io_lib:chars())). -type(option() :: {hook, hook_function()} @@ -225,7 +225,7 @@ lattribute(module, {M,Vs}, _Opts, _State) -> lattribute(module, M, _Opts, _State) -> attr("module", [{var,0,pname(M)}]); lattribute(export, Falist, _Opts, _State) -> - call({var,0,"-export"}, [falist(Falist)], 0, none); + call({var,0,"-export"}, [falist(Falist)], 0, options(none)); lattribute(import, Name, _Opts, _State) when is_list(Name) -> attr("import", [{var,0,pname(Name)}]); lattribute(import, {From,Falist}, _Opts, _State) -> @@ -240,10 +240,10 @@ lattribute(Name, Arg, #options{encoding = Encoding}, _State) -> typeattr(Tag, {TypeName,Type,Args}, _Opts) -> {first,leaf("-"++atom_to_list(Tag)++" "), - typed(call({atom,0,TypeName}, Args, 0, none), Type)}. + typed(call({atom,0,TypeName}, Args, 0, options(none)), Type)}. ltype({ann_type,_Line,[V,T]}) -> - typed(lexpr(V, none), T); + typed(lexpr(V, options(none)), T); ltype({paren_type,_Line,[T]}) -> [$(,ltype(T),$)]; ltype({type,_Line,union,Ts}) -> @@ -253,7 +253,7 @@ ltype({type,_Line,list,[T]}) -> ltype({type,_Line,nonempty_list,[T]}) -> {seq,$[,$],[$,],[ltype(T),leaf("...")]}; ltype({type,Line,nil,[]}) -> - lexpr({nil,Line}, 0, none); + lexpr({nil,Line}, 0, options(none)); ltype({type,Line,tuple,any}) -> simple_type({atom,Line,tuple}, []); ltype({type,_Line,tuple,Ts}) -> @@ -261,7 +261,7 @@ ltype({type,_Line,tuple,Ts}) -> ltype({type,_Line,record,[{atom,_,N}|Fs]}) -> record_type(N, Fs); ltype({type,_Line,range,[_I1,_I2]=Es}) -> - expr_list(Es, '..', fun lexpr/2, none); + expr_list(Es, '..', fun lexpr/2, options(none)); ltype({type,_Line,binary,[I1,I2]}) -> binary_type(I1, I2); % except binary() ltype({type,_Line,'fun',[]}) -> @@ -277,14 +277,14 @@ ltype({remote_type,Line,[M,F,Ts]}) -> ltype({atom,_,T}) -> leaf(write(T)); ltype(E) -> - lexpr(E, 0, none). + lexpr(E, 0, options(none)). binary_type(I1, I2) -> B = [[] || {integer,_,0} <- [I1]] =:= [], U = [[] || {integer,_,0} <- [I2]] =:= [], P = max_prec(), - E1 = [[leaf("_:"),lexpr(I1, P, none)] || B], - E2 = [[leaf("_:_*"),lexpr(I2, P, none)] || U], + E1 = [[leaf("_:"),lexpr(I1, P, options(none))] || B], + E2 = [[leaf("_:_*"),lexpr(I2, P, options(none))] || U], {seq,'<<','>>',[$,],E1++E2}. record_type(Name, Fields) -> @@ -294,7 +294,7 @@ field_types(Fs) -> tuple_type(Fs, fun field_type/1). field_type({type,_Line,field_type,[Name,Type]}) -> - typed(lexpr(Name, none), Type). + typed(lexpr(Name, options(none)), Type). typed(B, {type,_,union,Ts}) -> %% Special layout for :: followed by union. @@ -330,7 +330,8 @@ sig_type(FunType) -> fun_type([], FunType). guard_type(Before, Gs) -> - Gl = {list,[{step,'when',expr_list(Gs, [$,], fun constraint/2, none)}]}, + Opts = options(none), + Gl = {list,[{step,'when',expr_list(Gs, [$,], fun constraint/2, Opts)}]}, {list,[{step,Before,Gl}]}. constraint({type,_Line,constraint,[Tag,As]}, _Opts) -> @@ -345,7 +346,7 @@ type_args({type,_line,product,Ts}) -> targs(Ts). simple_type(Tag, Types) -> - {first,lexpr(Tag, 0, none),targs(Types)}. + {first,lexpr(Tag, 0, options(none)),targs(Types)}. targs(Ts) -> {seq,$(,$),[$,],ltypes(Ts)}. @@ -357,7 +358,7 @@ ltypes(Ts, F) -> [F(T) || T <- Ts]. attr(Name, Args) -> - call({var,0,format("-~s", [Name])}, Args, 0, none). + call({var,0,format("-~s", [Name])}, Args, 0, options(none)). pname(['' | As]) -> [$. | pname(As)]; @@ -632,11 +633,11 @@ bit_elem_types([T | Rest]) -> [bit_elem_type(T), $-|bit_elem_types(Rest)]. bit_elem_type({A,B}) -> - [lexpr(erl_parse:abstract(A), none), + [lexpr(erl_parse:abstract(A), options(none)), $:, - lexpr(erl_parse:abstract(B), none)]; + lexpr(erl_parse:abstract(B), options(none))]; bit_elem_type(T) -> - lexpr(erl_parse:abstract(T), none). + lexpr(erl_parse:abstract(T), options(none)). %% end of BITS diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl index 9c4b95acf6..30a81ade49 100644 --- a/lib/stdlib/src/gen_server.erl +++ b/lib/stdlib/src/gen_server.erl @@ -472,11 +472,11 @@ rec_nodes(Tag, [{N,R}|Tail], Name, Badnodes, Replies, Time, TimerId ) -> {'DOWN', R, _, _, _} -> rec_nodes(Tag, Tail, Name, [N|Badnodes], Replies, Time, TimerId); {{Tag, N}, Reply} -> %% Tag is bound !!! - unmonitor(R), + erlang:demonitor(R, [flush]), rec_nodes(Tag, Tail, Name, Badnodes, [{N,Reply}|Replies], Time, TimerId); {timeout, TimerId, _} -> - unmonitor(R), + erlang:demonitor(R, [flush]), %% Collect all replies that already have arrived rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies) end; @@ -527,10 +527,10 @@ rec_nodes_rest(Tag, [{N,R}|Tail], Name, Badnodes, Replies) -> {'DOWN', R, _, _, _} -> rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies); {{Tag, N}, Reply} -> %% Tag is bound !!! - unmonitor(R), + erlang:demonitor(R, [flush]), rec_nodes_rest(Tag, Tail, Name, Badnodes, [{N,Reply}|Replies]) after 0 -> - unmonitor(R), + erlang:demonitor(R, [flush]), rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies) end; rec_nodes_rest(Tag, [N|Tail], Name, Badnodes, Replies) -> @@ -572,16 +572,6 @@ start_monitor(Node, Name) when is_atom(Node), is_atom(Name) -> end end. -%% Cancels a monitor started with Ref=erlang:monitor(_, _). -unmonitor(Ref) when is_reference(Ref) -> - erlang:demonitor(Ref), - receive - {'DOWN', Ref, _, _, _} -> - true - after 0 -> - true - end. - %%% --------------------------------------------------- %%% Message handling functions %%% --------------------------------------------------- diff --git a/lib/stdlib/src/io.erl b/lib/stdlib/src/io.erl index c92e9e3ade..53728237ca 100644 --- a/lib/stdlib/src/io.erl +++ b/lib/stdlib/src/io.erl @@ -598,11 +598,7 @@ default_output() -> wait_io_mon_reply(From, Mref) -> receive {io_reply, From, Reply} -> - erlang:demonitor(Mref), - receive - {'DOWN', Mref, _, _, _} -> true - after 0 -> true - end, + erlang:demonitor(Mref, [flush]), Reply; {'EXIT', From, _What} -> receive diff --git a/lib/stdlib/src/lists.erl b/lib/stdlib/src/lists.erl index 961c060019..0c033acd88 100644 --- a/lib/stdlib/src/lists.erl +++ b/lib/stdlib/src/lists.erl @@ -36,7 +36,7 @@ -export([merge/3, rmerge/3, sort/2, umerge/3, rumerge/3, usort/2]). -export([all/2,any/2,map/2,flatmap/2,foldl/3,foldr/3,filter/2, - partition/2,zf/2, + partition/2,zf/2,filtermap/2, mapfoldl/3,mapfoldr/3,foreach/2,takewhile/2,dropwhile/2,splitwith/2, split/2]). @@ -1291,18 +1291,28 @@ partition(Pred, [H | T], As, Bs) -> partition(Pred, [], As, Bs) when is_function(Pred, 1) -> {reverse(As), reverse(Bs)}. --spec zf(fun((T) -> boolean() | {'true', X}), [T]) -> [(T | X)]. +-spec filtermap(Fun, List1) -> List2 when + Fun :: fun((Elem) -> boolean() | {'true', Value}), + List1 :: [Elem], + List2 :: [Elem | Value], + Elem :: term(), + Value :: term(). -zf(F, [Hd|Tail]) -> +filtermap(F, [Hd|Tail]) -> case F(Hd) of true -> - [Hd|zf(F, Tail)]; + [Hd|filtermap(F, Tail)]; {true,Val} -> - [Val|zf(F, Tail)]; + [Val|filtermap(F, Tail)]; false -> - zf(F, Tail) + filtermap(F, Tail) end; -zf(F, []) when is_function(F, 1) -> []. +filtermap(F, []) when is_function(F, 1) -> []. + +-spec zf(fun((T) -> boolean() | {'true', X}), [T]) -> [(T | X)]. + +zf(F, L) -> + filtermap(F, L). -spec foreach(Fun, List) -> ok when Fun :: fun((Elem :: T) -> term()), diff --git a/lib/stdlib/src/otp_internal.erl b/lib/stdlib/src/otp_internal.erl index a4f4035c79..f2849e50ec 100644 --- a/lib/stdlib/src/otp_internal.erl +++ b/lib/stdlib/src/otp_internal.erl @@ -66,6 +66,139 @@ obsolete_1(rpc, safe_multi_server_call, A) when A =:= 2; A =:= 3 -> {deprecated, {rpc, multi_server_call, A}}; +%% *** CRYPTO add in R16B01 *** + +obsolete_1(crypto, md4, 1) -> + {deprecated, {crypto, hash, 2}}; +obsolete_1(crypto, md5, 1) -> + {deprecated, {crypto, hash, 2}}; +obsolete_1(crypto, sha, 1) -> + {deprecated, {crypto, hash, 2}}; + +obsolete_1(crypto, md4_init, 1) -> + {deprecated, {crypto, hash_init, 2}}; +obsolete_1(crypto, md5_init, 1) -> + {deprecated, {crypto, hash_init, 2}}; +obsolete_1(crypto, sha_init, 1) -> + {deprecated, {crypto, hash_init, 2}}; + +obsolete_1(crypto, md4_update, 2) -> + {deprecated, {crypto, hash_update, 3}}; +obsolete_1(crypto, md5_update, 2) -> + {deprecated, {crypto, hash_update, 3}}; +obsolete_1(crypto, sah_update, 2) -> + {deprecated, {crypto, hash_update, 3}}; + +obsolete_1(crypto, md4_final, 1) -> + {deprecated, {crypto, hash_final, 2}}; +obsolete_1(crypto, md5_final, 1) -> + {deprecated, {crypto, hash_final, 2}}; +obsolete_1(crypto, sha_final, 1) -> + {deprecated, {crypto, hash_final, 2}}; + +obsolete_1(crypto, md5_mac, 2) -> + {deprecated, {crypto, hmac, 3}}; +obsolete_1(crypto, sha_mac, 2) -> + {deprecated, {crypto, hmac, 3}}; + +obsolete_1(crypto, sha_mac_96, 2) -> + {deprecated, {crypto, hmac_n, 3}}; +obsolete_1(crypto, md5_mac_96, 2) -> + {deprecated, {crypto, hmac_n, 3}}; + +obsolete_1(crypto, rsa_sign, 3) -> + {deprecated, {crypto, sign, 4}}; +obsolete_1(crypto, rsa_verify, 3) -> + {deprecated, {crypto, verify, 4}}; + +obsolete_1(crypto, dss_sign, 2) -> + {deprecated, {crypto, sign, 4}}; +obsolete_1(crypto, dss_sign, 3) -> + {deprecated, {crypto, sign, 4}}; + +obsolete_1(crypto, dss_verify, 3) -> + {deprecated, {crypto, verify, 4}}; +obsolete_1(crypto, dss_verify, 4) -> + {deprecated, {crypto, verify, 4}}; + +obsolete_1(crypto, mod_exp, 3) -> + {deprecated, {crypto, mod_pow, 3}}; + +obsolete_1(crypto, dh_compute_key, 3) -> + {deprecated, {crypto, compute_key, 4}}; +obsolete_1(crypto, dh_generate_key, 1) -> + {deprecated, {crypto, generate_key, 3}}; +obsolete_1(crypto, dh_generate_key, 2) -> + {deprecated, {crypto, generate_key, 3}}; + +obsolete_1(crypto, des_cbc_encrypt, 3) -> + {deprecated, {crypto, block_encrypt, 4}}; +obsolete_1(crypto, des3_cbc_encrypt, 5) -> + {deprecated, {crypto, block_encrypt, 4}}; +obsolete_1(crypto, des_ecb_encrypt, 3) -> + {deprecated, {crypto, block_encrypt, 4}}; +obsolete_1(crypto, des_ede3_cbc_encrypt, 5) -> + {deprecated, {crypto, block_encrypt, 4}}; +obsolete_1(crypto, des_cfb_encrypt, 3) -> + {deprecated, {crypto, block_encrypt, 4}}; +obsolete_1(crypto, des3_cfb_encrypt, 5) -> + {deprecated, {crypto, block_encrypt, 4}}; +obsolete_1(crypto, blowfish_ecb_encrypt, 2) -> + {deprecated, {crypto, block_encrypt, 3}}; +obsolete_1(crypto, blowfish_cbc_encrypt, 3) -> + {deprecated, {crypto, block_encrypt, 4}}; +obsolete_1(crypto, blowfish_cfb64_encrypt, 3) -> + {deprecated, {crypto, block_encrypt, 4}}; +obsolete_1(crypto, blowfish_ofb64_encrypt, 3) -> + {deprecated, {crypto, block_encrypt, 4}}; +obsolete_1(crypto, aes_cfb_128_encrypt, 3) -> + {deprecated, {crypto, block_encrypt, 4}}; +obsolete_1(crypto, aes_cbc_256_encrypt, 3) -> + {deprecated, {crypto, block_encrypt, 4}}; +obsolete_1(crypto,rc2_cbc_encrypt, 3) -> + {deprecated, {crypto, block_encrypt, 4}}; +obsolete_1(crypto,rc2_40_cbc_encrypt, 3) -> + {deprecated, {crypto, block_encrypt, 4}}; + +obsolete_1(crypto, des_cbc_decrypt, 3) -> + {deprecated, {crypto, block_decrypt, 4}}; +obsolete_1(crypto, des3_cbc_decrypt, 5) -> + {deprecated, {crypto, block_decrypt, 4}}; +obsolete_1(crypto, des_ecb_decrypt, 3) -> + {deprecated, {crypto, block_decrypt, 4}}; +obsolete_1(crypto, des_ede3_cbc_decrypt, 5) -> + {deprecated, {crypto, block_decrypt, 4}}; +obsolete_1(crypto, des_cfb_decrypt, 3) -> + {deprecated, {crypto, block_decrypt, 4}}; +obsolete_1(crypto, des3_cfb_decrypt, 5) -> + {deprecated, {crypto, block_decrypt, 4}}; +obsolete_1(crypto, blowfish_ecb_decrypt, 2) -> + {deprecated, {crypto, block_decrypt, 3}}; +obsolete_1(crypto, blowfish_cbc_decrypt, 3) -> + {deprecated, {crypto, block_decrypt, 4}}; +obsolete_1(crypto, blowfish_cfb64_decrypt, 3) -> + {deprecated, {crypto, block_decrypt, 4}}; +obsolete_1(crypto, blowfish_ofb64_decrypt, 3) -> + {deprecated, {crypto, block_decrypt, 4}}; +obsolete_1(crypto, aes_cfb_128_decrypt, 3) -> + {deprecated, {crypto, block_decrypt, 4}}; +obsolete_1(crypto, aes_cbc_256_decrypt, 3) -> + {deprecated, {crypto, block_decrypt, 4}}; +obsolete_1(crypto,rc2_cbc_decrypt, 3) -> + {deprecated, {crypto, block_decrypt, 4}}; +obsolete_1(crypto,rc2_40_cbc_decrypt, 3) -> + {deprecated, {crypto, block_decrypt, 4}}; + +obsolete_1(crypto,info, 0) -> + {deprecated, {crypto, module_info, 0}}; + +obsolete_1(crypto, strong_rand_mpint, 3) -> + {deprecated, "needed only by deprecated functions"}; +obsolete_1(crypto, erlint, 3) -> + {deprecated, "needed only by deprecated functions"}; +obsolete_1(crypto, mpint, 3) -> + {deprecated, "needed only by deprecated functions"}; + %% *** SNMP *** obsolete_1(snmp, N, A) -> diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl index 9f93747c3e..54328cd9ff 100644 --- a/lib/stdlib/src/supervisor.erl +++ b/lib/stdlib/src/supervisor.erl @@ -63,7 +63,9 @@ %%-------------------------------------------------------------------------- -record(child, {% pid is undefined when child is not running - pid = undefined :: child() | {restarting,pid()} | [pid()], + pid = undefined :: child() + | {restarting, pid() | undefined} + | [pid()], name :: child_id(), mfargs :: mfargs(), restart_type :: restart(), @@ -752,6 +754,9 @@ restart(Child, State) -> end, timer:apply_after(0,?MODULE,try_again_restart,[self(),Id]), {ok,NState2}; + {try_again, NState2, #child{name=ChName}} -> + timer:apply_after(0,?MODULE,try_again_restart,[self(),ChName]), + {ok,NState2}; Other -> Other end; @@ -798,10 +803,16 @@ restart(rest_for_one, Child, State) -> case start_children(ChAfter2, State#state.name) of {ok, ChAfter3} -> {ok, State#state{children = ChAfter3 ++ ChBefore}}; - {error, ChAfter3, _Reason} -> + {error, ChAfter3, {failed_to_start_child, ChName, _Reason}} + when ChName =:= Child#child.name -> NChild = Child#child{pid=restarting(Child#child.pid)}, NState = State#state{children = ChAfter3 ++ ChBefore}, - {try_again, replace_child(NChild,NState)} + {try_again, replace_child(NChild,NState)}; + {error, ChAfter3, {failed_to_start_child, ChName, _Reason}} -> + NChild = lists:keyfind(ChName, #child.name, ChAfter3), + NChild2 = NChild#child{pid=?restarting(undefined)}, + NState = State#state{children = ChAfter3 ++ ChBefore}, + {try_again, replace_child(NChild2,NState), NChild2} end; restart(one_for_all, Child, State) -> Children1 = del_child(Child#child.pid, State#state.children), @@ -809,10 +820,16 @@ restart(one_for_all, Child, State) -> case start_children(Children2, State#state.name) of {ok, NChs} -> {ok, State#state{children = NChs}}; - {error, NChs, _Reason} -> + {error, NChs, {failed_to_start_child, ChName, _Reason}} + when ChName =:= Child#child.name -> NChild = Child#child{pid=restarting(Child#child.pid)}, NState = State#state{children = NChs}, - {try_again, replace_child(NChild,NState)} + {try_again, replace_child(NChild,NState)}; + {error, NChs, {failed_to_start_child, ChName, _Reason}} -> + NChild = lists:keyfind(ChName, #child.name, NChs), + NChild2 = NChild#child{pid=?restarting(undefined)}, + NState = State#state{children = NChs}, + {try_again, replace_child(NChild2,NState), NChild2} end. restarting(Pid) when is_pid(Pid) -> ?restarting(Pid); diff --git a/lib/stdlib/src/timer.erl b/lib/stdlib/src/timer.erl index 689e42051f..e11fb046e9 100644 --- a/lib/stdlib/src/timer.erl +++ b/lib/stdlib/src/timer.erl @@ -354,7 +354,7 @@ timer_timeout(SysTime) -> '$end_of_table' -> infinity; {Time, _Ref} when Time > SysTime -> - Timeout = (Time - SysTime) div 1000, + Timeout = (Time - SysTime + 999) div 1000, %% Returned timeout must fit in a small int erlang:min(Timeout, ?MAX_TIMEOUT); Key -> @@ -414,7 +414,7 @@ next_timeout() -> '$end_of_table' -> infinity; {Time, _} -> - erlang:min(positive((Time - system_time()) div 1000), ?MAX_TIMEOUT) + erlang:min(positive((Time - system_time() + 999) div 1000), ?MAX_TIMEOUT) end. %% Help functions diff --git a/lib/stdlib/test/Makefile b/lib/stdlib/test/Makefile index 6aa09d7bd0..af82f22b21 100644 --- a/lib/stdlib/test/Makefile +++ b/lib/stdlib/test/Makefile @@ -66,6 +66,7 @@ MODULES= \ string_SUITE \ supervisor_1 \ supervisor_2 \ + supervisor_3 \ supervisor_deadlock \ naughty_child \ shell_SUITE \ diff --git a/lib/stdlib/test/epp_SUITE.erl b/lib/stdlib/test/epp_SUITE.erl index b2f1aa955a..0cbdf76270 100644 --- a/lib/stdlib/test/epp_SUITE.erl +++ b/lib/stdlib/test/epp_SUITE.erl @@ -104,6 +104,8 @@ include_local(suite) -> include_local(Config) when is_list(Config) -> ?line DataDir = ?config(data_dir, Config), ?line File = filename:join(DataDir, "include_local.erl"), + FooHrl = filename:join([DataDir,"include","foo.hrl"]), + BarHrl = filename:join([DataDir,"include","bar.hrl"]), %% include_local.erl includes include/foo.hrl which %% includes bar.hrl (also in include/) without requiring %% any additional include path, and overriding any file @@ -111,6 +113,8 @@ include_local(Config) when is_list(Config) -> ?line {ok, List} = epp:parse_file(File, [DataDir], []), ?line {value, {attribute,_,a,{true,true}}} = lists:keysearch(a,3,List), + [{File,1},{FooHrl,1},{BarHrl,1},{FooHrl,5},{File,5}] = + [ FileLine || {attribute,_,file,FileLine} <- List ], ok. %%% Here is a little reimplementation of epp:parse_file, which times out diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl index 7ff4c81ea6..18ec17a4bf 100644 --- a/lib/stdlib/test/erl_eval_SUITE.erl +++ b/lib/stdlib/test/erl_eval_SUITE.erl @@ -1,3 +1,4 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% @@ -992,7 +993,7 @@ otp_10622(Config) when is_list(Config) -> <<0>>), check(fun() -> <<"\x{aa}ff"/utf8>> = <<"\x{aa}ff"/utf8>> end, "<<\"\\x{aa}ff\"/utf8>> = <<\"\\x{aa}ff\"/utf8>>. ", - <<"�\xaaff">>), + <<"Â\xaaff">>), %% The same bug as last example: check(fun() -> case <<"foo"/utf8>> of <<"foo"/utf8>> -> true diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl index 9c0a43abcc..ff3470349e 100644 --- a/lib/stdlib/test/erl_pp_SUITE.erl +++ b/lib/stdlib/test/erl_pp_SUITE.erl @@ -49,7 +49,7 @@ otp_6321/1, otp_6911/1, otp_6914/1, otp_8150/1, otp_8238/1, otp_8473/1, otp_8522/1, otp_8567/1, otp_8664/1, otp_9147/1, - otp_10302/1, otp_10820/1]). + otp_10302/1, otp_10820/1, otp_11100/1]). %% Internal export. -export([ehook/6]). @@ -81,7 +81,7 @@ groups() -> {tickets, [], [otp_6321, otp_6911, otp_6914, otp_8150, otp_8238, otp_8473, otp_8522, otp_8567, otp_8664, otp_9147, - otp_10302, otp_10820]}]. + otp_10302, otp_10820, otp_11100]}]. init_per_suite(Config) -> Config. @@ -1103,6 +1103,45 @@ file_attr_is_string("-file(\"" ++ _) -> true; file_attr_is_string([_ | L]) -> file_attr_is_string(L). +otp_11100(doc) -> + "OTP-11100. Fix printing of invalid forms."; +otp_11100(suite) -> []; +otp_11100(Config) when is_list(Config) -> + %% There are a few places where the added code ("options(none)") + %% doesn't make a difference (pp:bit_elem_type/1 is an example). + + %% Cannot trigger the use of the hook function with export/import. + "-export([{fy,a}/b]).\n" = + pf({attribute,1,export,[{{fy,a},b}]}), + "-type foo() :: integer(INVALID-FORM:{foo,bar}:).\n" = + pf({attribute,1,type,{foo,{type,1,integer,[{foo,bar}]},[]}}), + pf({attribute,1,type, + {a,{type,1,range,[{integer,1,1},{foo,bar}]},[]}}), + "-type foo(INVALID-FORM:{foo,bar}:) :: A.\n" = + pf({attribute,1,type,{foo,{var,1,'A'},[{foo,bar}]}}), + "-type foo() :: (INVALID-FORM:{foo,bar}: :: []).\n" = + pf({attribute,1,type, + {foo,{paren_type,1, + [{ann_type,1,[{foo,bar},{type,1,nil,[]}]}]}, + []}}), + "-type foo() :: <<_:INVALID-FORM:{foo,bar}:>>.\n" = + pf({attribute,1,type, + {foo,{type,1,binary,[{foo,bar},{integer,1,0}]},[]}}), + "-type foo() :: <<_:10, _:_*INVALID-FORM:{foo,bar}:>>.\n" = + pf({attribute,1,type, + {foo,{type,1,binary,[{integer,1,10},{foo,bar}]},[]}}), + "-type foo() :: #r{INVALID-FORM:{foo,bar}: :: integer()}.\n" = + pf({attribute,1,type, + {foo,{type,1,record, + [{atom,1,r}, + {type,1,field_type, + [{foo,bar},{type,1,integer,[]}]}]}, + []}}), + ok. + +pf(Form) -> + lists:flatten(erl_pp:form(Form,none)). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% compile(Config, Tests) -> diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl index af5d5a8f21..bd69019892 100644 --- a/lib/stdlib/test/ets_SUITE.erl +++ b/lib/stdlib/test/ets_SUITE.erl @@ -75,6 +75,7 @@ -export([otp_9932/1]). -export([otp_9423/1]). -export([otp_10182/1]). +-export([memory_check_summary/1]). -export([init_per_testcase/2, end_per_testcase/2]). %% Convenience for manual testing @@ -149,7 +150,9 @@ all() -> give_away, setopts, bad_table, types, otp_10182, otp_9932, - otp_9423]. + otp_9423, + + memory_check_summary]. % MUST BE LAST groups() -> [{new, [], @@ -185,7 +188,8 @@ init_per_suite(Config) -> end_per_suite(_Config) -> stop_spawn_logger(), - catch erts_debug:set_internal_state(available_internal_state, false). + catch erts_debug:set_internal_state(available_internal_state, false), + ok. init_per_group(_GroupName, Config) -> Config. @@ -193,6 +197,26 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. +%% Test that we did not have "too many" failed verify_etsmem()'s +%% in the test suite. +%% verify_etsmem() may give a low number of false positives +%% as concurrent activities, such as lingering processes +%% from earlier test suites, may do unrelated ets (de)allocations. +memory_check_summary(_Config) -> + case whereis(ets_test_spawn_logger) of + undefined -> + ?t:fail("No spawn logger exist"); + _ -> + ets_test_spawn_logger ! {self(), get_failed_memchecks}, + receive {get_failed_memchecks, FailedMemchecks} -> ok end, + io:format("Failed memchecks: ~p\n",[FailedMemchecks]), + if FailedMemchecks > 3 -> + ct:fail("Too many failed (~p) memchecks", [FailedMemchecks]); + true -> + ok + end + end. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -5635,7 +5659,8 @@ verify_etsmem({MemInfo,AllTabs}) -> io:format("Actual: ~p", [MemInfo2]), io:format("Changed tables before: ~p\n",[AllTabs -- AllTabs2]), io:format("Changed tables after: ~p\n", [AllTabs2 -- AllTabs]), - ?t:fail() + ets_test_spawn_logger ! failed_memcheck, + {comment, "Failed memory check"} end. @@ -5657,10 +5682,10 @@ stop_loopers(Loopers) -> looper(Fun, State) -> looper(Fun, Fun(State)). -spawn_logger(Procs) -> +spawn_logger(Procs, FailedMemchecks) -> receive {new_test_proc, Proc} -> - spawn_logger([Proc|Procs]); + spawn_logger([Proc|Procs], FailedMemchecks); {sync_test_procs, Kill, From} -> lists:foreach(fun (Proc) when From == Proc -> ok; @@ -5683,7 +5708,14 @@ spawn_logger(Procs) -> end end, Procs), From ! test_procs_synced, - spawn_logger([From]) + spawn_logger([From], FailedMemchecks); + + failed_memcheck -> + spawn_logger(Procs, FailedMemchecks+1); + + {Pid, get_failed_memchecks} -> + Pid ! {get_failed_memchecks, FailedMemchecks}, + spawn_logger(Procs, FailedMemchecks) end. pid_status(Pid) -> @@ -5699,7 +5731,7 @@ start_spawn_logger() -> case whereis(ets_test_spawn_logger) of Pid when is_pid(Pid) -> true; _ -> register(ets_test_spawn_logger, - spawn_opt(fun () -> spawn_logger([]) end, + spawn_opt(fun () -> spawn_logger([], 0) end, [{priority, max}])) end. @@ -5710,8 +5742,7 @@ start_spawn_logger() -> stop_spawn_logger() -> Mon = erlang:monitor(process, ets_test_spawn_logger), (catch exit(whereis(ets_test_spawn_logger), kill)), - receive {'DOWN', Mon, _, _, _} -> ok end, - ok. + receive {'DOWN', Mon, _, _, _} -> ok end. wait_for_test_procs() -> wait_for_test_procs(false). @@ -5811,7 +5842,7 @@ spawn_monitor_with_pid(Pid, Fun, N) -> end) of Pid -> {Pid, erlang:monitor(process, Pid)}; - Other -> + _Other -> spawn_monitor_with_pid(Pid,Fun,N-1) end. @@ -6117,11 +6148,18 @@ repeat_for_opts(F, OptGenList) -> repeat_for_opts(F, OptGenList, []). repeat_for_opts(F, [], Acc) -> - lists:map(fun(Opts) -> - OptList = lists:filter(fun(E) -> E =/= void end, Opts), - io:format("Calling with options ~p\n",[OptList]), - F(OptList) - end, Acc); + lists:foldl(fun(Opts, RV_Acc) -> + OptList = lists:filter(fun(E) -> E =/= void end, Opts), + io:format("Calling with options ~p\n",[OptList]), + RV = F(OptList), + case RV_Acc of + {comment,_} -> RV_Acc; + _ -> case RV of + {comment,_} -> RV; + _ -> [RV | RV_Acc] + end + end + end, [], Acc); repeat_for_opts(F, [OptList | Tail], []) when is_list(OptList) -> repeat_for_opts(F, Tail, [[Opt] || Opt <- OptList]); repeat_for_opts(F, [OptList | Tail], AccList) when is_list(OptList) -> diff --git a/lib/stdlib/test/lists_SUITE.erl b/lib/stdlib/test/lists_SUITE.erl index b56f0b39d8..cd7210f8ec 100644 --- a/lib/stdlib/test/lists_SUITE.erl +++ b/lib/stdlib/test/lists_SUITE.erl @@ -2532,8 +2532,8 @@ otp_5939(Config) when is_list(Config) -> ?line [] = lists:filter(Pred, []), ?line {'EXIT', _} = (catch lists:partition(func, [])), ?line {[],[]} = lists:partition(Pred, []), - ?line {'EXIT', _} = (catch lists:zf(func, [])), - ?line [] = lists:zf(Fun1, []), + ?line {'EXIT', _} = (catch lists:filtermap(func, [])), + ?line [] = lists:filtermap(Fun1, []), ?line {'EXIT', _} = (catch lists:foreach(func, [])), ?line ok = lists:foreach(Fun1, []), ?line {'EXIT', _} = (catch lists:mapfoldl(func, [], [])), diff --git a/lib/stdlib/test/qlc_SUITE_data/join_info_compat.erl b/lib/stdlib/test/qlc_SUITE_data/join_info_compat.erl index e0db132c47..b93b907392 100644 --- a/lib/stdlib/test/qlc_SUITE_data/join_info_compat.erl +++ b/lib/stdlib/test/qlc_SUITE_data/join_info_compat.erl @@ -1,3 +1,4 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% @@ -515,7 +516,7 @@ create_handle() -> $.:8/integer-unit:1-unsigned-big, $x:8/integer-unit:1-unsigned-big, $\234:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ë:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, $N:8/integer-unit:1-unsigned-big, $a:8/integer-unit:1-unsigned-big, @@ -523,16 +524,16 @@ create_handle() -> $-:8/integer-unit:1-unsigned-big, $):8/integer-unit:1-unsigned-big, $-:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $È:8/integer-unit:1-unsigned-big, $I:8/integer-unit:1-unsigned-big, $M:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $ä:8/integer-unit:1-unsigned-big, + $Ê:8/integer-unit:1-unsigned-big, $a:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ê:8/integer-unit:1-unsigned-big, $\000:8/integer-unit:1-unsigned-big, $\n:8/integer-unit:1-unsigned-big, $0:8/integer-unit:1-unsigned-big, @@ -541,19 +542,19 @@ create_handle() -> $\026:8/integer-unit:1-unsigned-big, $%:8/integer-unit:1-unsigned-big, $r:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $¥:8/integer-unit:1-unsigned-big, $0:8/integer-unit:1-unsigned-big, $0:8/integer-unit:1-unsigned-big, $F:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $ :8/integer-unit:1-unsigned-big, + $ð:8/integer-unit:1-unsigned-big, $":8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $³:8/integer-unit:1-unsigned-big, $\000:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $þ:8/integer-unit:1-unsigned-big, + $Ì:8/integer-unit:1-unsigned-big, $\n:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big>>, + $É:8/integer-unit:1-unsigned-big>>, <<$\203:8/integer-unit:1-unsigned-big, $P:8/integer-unit:1-unsigned-big, $\000:8/integer-unit:1-unsigned-big, @@ -562,7 +563,7 @@ create_handle() -> $<:8/integer-unit:1-unsigned-big, $x:8/integer-unit:1-unsigned-big, $\234:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ë:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, $N:8/integer-unit:1-unsigned-big, $a:8/integer-unit:1-unsigned-big, @@ -570,16 +571,16 @@ create_handle() -> $-:8/integer-unit:1-unsigned-big, $):8/integer-unit:1-unsigned-big, $-:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $È:8/integer-unit:1-unsigned-big, $I:8/integer-unit:1-unsigned-big, $M:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $ä:8/integer-unit:1-unsigned-big, + $Ê:8/integer-unit:1-unsigned-big, $a:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Î:8/integer-unit:1-unsigned-big, $\000:8/integer-unit:1-unsigned-big, $\n:8/integer-unit:1-unsigned-big, $0:8/integer-unit:1-unsigned-big, @@ -588,22 +589,22 @@ create_handle() -> $\026:8/integer-unit:1-unsigned-big, $%:8/integer-unit:1-unsigned-big, $r:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $¥:8/integer-unit:1-unsigned-big, $0:8/integer-unit:1-unsigned-big, $0:8/integer-unit:1-unsigned-big, $::8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $¡:8/integer-unit:1-unsigned-big, + $ð:8/integer-unit:1-unsigned-big, $":8/integer-unit:1-unsigned-big, $P:8/integer-unit:1-unsigned-big, $x:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $ñ:8/integer-unit:1-unsigned-big, $Y:8/integer-unit:1-unsigned-big, $\000:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $ª:8/integer-unit:1-unsigned-big, $9:8/integer-unit:1-unsigned-big, $\r:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big>>, + $ý:8/integer-unit:1-unsigned-big>>, <<$\203:8/integer-unit:1-unsigned-big, $P:8/integer-unit:1-unsigned-big, $\000:8/integer-unit:1-unsigned-big, @@ -612,51 +613,51 @@ create_handle() -> $I:8/integer-unit:1-unsigned-big, $x:8/integer-unit:1-unsigned-big, $\234:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ë:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, $M:8/integer-unit:1-unsigned-big, $a:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ê:8/integer-unit:1-unsigned-big, $/:8/integer-unit:1-unsigned-big, $H:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $ä:8/integer-unit:1-unsigned-big, $N:8/integer-unit:1-unsigned-big, $a:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $¶:8/integer-unit:1-unsigned-big, + $µ:8/integer-unit:1-unsigned-big, + $²:8/integer-unit:1-unsigned-big, + $Í:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, $\006:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ò:8/integer-unit:1-unsigned-big, $e:8/integer-unit:1-unsigned-big, $\211:8/integer-unit:1-unsigned-big, $E:8/integer-unit:1-unsigned-big, $\s:8/integer-unit:1-unsigned-big, $>:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $£:8/integer-unit:1-unsigned-big, $\023:8/integer-unit:1-unsigned-big, $\210:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ç:8/integer-unit:1-unsigned-big, $\232:8/integer-unit:1-unsigned-big, $\226:8/integer-unit:1-unsigned-big, $\223:8/integer-unit:1-unsigned-big, $\237:8/integer-unit:1-unsigned-big, $X:8/integer-unit:1-unsigned-big, $\222:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $È:8/integer-unit:1-unsigned-big, $\235:8/integer-unit:1-unsigned-big, $l:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $¨:8/integer-unit:1-unsigned-big, $g:8/integer-unit:1-unsigned-big, $i:8/integer-unit:1-unsigned-big, $d:8/integer-unit:1-unsigned-big, $\200:8/integer-unit:1-unsigned-big, $\001:8/integer-unit:1-unsigned-big, $R:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $µ:8/integer-unit:1-unsigned-big, $\r:8/integer-unit:1-unsigned-big, $\214:8/integer-unit:1-unsigned-big, $\030:8/integer-unit:1-unsigned-big, @@ -664,7 +665,7 @@ create_handle() -> $\000:8/integer-unit:1-unsigned-big, $\000:8/integer-unit:1-unsigned-big, $c:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ö:8/integer-unit:1-unsigned-big, $\017:8/integer-unit:1-unsigned-big, $=:8/integer-unit:1-unsigned-big>>, <<$\203:8/integer-unit:1-unsigned-big, @@ -708,24 +709,24 @@ create_handle() -> $*:8/integer-unit:1-unsigned-big, $x:8/integer-unit:1-unsigned-big, $\234:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ë:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, $M:8/integer-unit:1-unsigned-big, $a:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ê:8/integer-unit:1-unsigned-big, $/:8/integer-unit:1-unsigned-big, $H:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $ä:8/integer-unit:1-unsigned-big, $\005:8/integer-unit:1-unsigned-big, $R:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $¶:8/integer-unit:1-unsigned-big, + $¶:8/integer-unit:1-unsigned-big, $\031:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ì:8/integer-unit:1-unsigned-big, $):8/integer-unit:1-unsigned-big, $\f:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ì:8/integer-unit:1-unsigned-big, $e:8/integer-unit:1-unsigned-big, $\211:8/integer-unit:1-unsigned-big, $E:8/integer-unit:1-unsigned-big, @@ -737,7 +738,7 @@ create_handle() -> $/:8/integer-unit:1-unsigned-big, $\022:8/integer-unit:1-unsigned-big, $\000:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ì:8/integer-unit:1-unsigned-big, $\205:8/integer-unit:1-unsigned-big, $\t:8/integer-unit:1-unsigned-big, $\216:8/integer-unit:1-unsigned-big>>, @@ -749,33 +750,33 @@ create_handle() -> $j:8/integer-unit:1-unsigned-big, $x:8/integer-unit:1-unsigned-big, $\234:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ë:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, $I:8/integer-unit:1-unsigned-big, $a:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, $I:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Î:8/integer-unit:1-unsigned-big, + $Ï:8/integer-unit:1-unsigned-big, $+:8/integer-unit:1-unsigned-big, $N:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $ú:8/integer-unit:1-unsigned-big, + $ÿ:8/integer-unit:1-unsigned-big, + $ÿ:8/integer-unit:1-unsigned-big, + $ÿ:8/integer-unit:1-unsigned-big, + $·:8/integer-unit:1-unsigned-big, $\f:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $æ:8/integer-unit:1-unsigned-big, $\024:8/integer-unit:1-unsigned-big, $\006:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ö:8/integer-unit:1-unsigned-big, $\222:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ò:8/integer-unit:1-unsigned-big, $\202:8/integer-unit:1-unsigned-big, $\234:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ô:8/integer-unit:1-unsigned-big, $D:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $®:8/integer-unit:1-unsigned-big, $\034:8/integer-unit:1-unsigned-big, $\006:8/integer-unit:1-unsigned-big, $\006:8/integer-unit:1-unsigned-big, @@ -791,7 +792,7 @@ create_handle() -> $W:8/integer-unit:1-unsigned-big, $\n:8/integer-unit:1-unsigned-big, $\003:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $£:8/integer-unit:1-unsigned-big, $\023:8/integer-unit:1-unsigned-big, $\n:8/integer-unit:1-unsigned-big, $/:8/integer-unit:1-unsigned-big, @@ -800,18 +801,18 @@ create_handle() -> $\027:8/integer-unit:1-unsigned-big, $\237:8/integer-unit:1-unsigned-big, $\205:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $¤:8/integer-unit:1-unsigned-big, $\227:8/integer-unit:1-unsigned-big, $\007:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $¤:8/integer-unit:1-unsigned-big, $\227:8/integer-unit:1-unsigned-big, $\021:8/integer-unit:1-unsigned-big, $.:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ï:8/integer-unit:1-unsigned-big, $\003:8/integer-unit:1-unsigned-big, $\224:8/integer-unit:1-unsigned-big, $\217:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ì:8/integer-unit:1-unsigned-big, $\002:8/integer-unit:1-unsigned-big, $\000:8/integer-unit:1-unsigned-big, $\203:8/integer-unit:1-unsigned-big, @@ -1398,7 +1399,7 @@ lookup_handle() -> $.:8/integer-unit:1-unsigned-big, $x:8/integer-unit:1-unsigned-big, $\234:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ë:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, $N:8/integer-unit:1-unsigned-big, $a:8/integer-unit:1-unsigned-big, @@ -1406,16 +1407,16 @@ lookup_handle() -> $-:8/integer-unit:1-unsigned-big, $):8/integer-unit:1-unsigned-big, $-:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $È:8/integer-unit:1-unsigned-big, $I:8/integer-unit:1-unsigned-big, $M:8/integer-unit:1-unsigned-big, $\024:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ì:8/integer-unit:1-unsigned-big, $a:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ê:8/integer-unit:1-unsigned-big, $\000:8/integer-unit:1-unsigned-big, $\n:8/integer-unit:1-unsigned-big, $0:8/integer-unit:1-unsigned-big, @@ -1424,19 +1425,19 @@ lookup_handle() -> $\026:8/integer-unit:1-unsigned-big, $%:8/integer-unit:1-unsigned-big, $\n:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $¦:8/integer-unit:1-unsigned-big, $0:8/integer-unit:1-unsigned-big, $0:8/integer-unit:1-unsigned-big, $F:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $ :8/integer-unit:1-unsigned-big, + $ð:8/integer-unit:1-unsigned-big, $":8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $³:8/integer-unit:1-unsigned-big, $\000:8/integer-unit:1-unsigned-big, $\000:8/integer-unit:1-unsigned-big, $\206:8/integer-unit:1-unsigned-big, $\n:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big>>, + $Þ:8/integer-unit:1-unsigned-big>>, <<$\203:8/integer-unit:1-unsigned-big, $P:8/integer-unit:1-unsigned-big, $\000:8/integer-unit:1-unsigned-big, @@ -1445,7 +1446,7 @@ lookup_handle() -> $.:8/integer-unit:1-unsigned-big, $x:8/integer-unit:1-unsigned-big, $\234:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ë:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, $N:8/integer-unit:1-unsigned-big, $a:8/integer-unit:1-unsigned-big, @@ -1453,16 +1454,16 @@ lookup_handle() -> $-:8/integer-unit:1-unsigned-big, $):8/integer-unit:1-unsigned-big, $-:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $È:8/integer-unit:1-unsigned-big, $I:8/integer-unit:1-unsigned-big, $M:8/integer-unit:1-unsigned-big, $\024:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ì:8/integer-unit:1-unsigned-big, $a:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ê:8/integer-unit:1-unsigned-big, $\000:8/integer-unit:1-unsigned-big, $\n:8/integer-unit:1-unsigned-big, $0:8/integer-unit:1-unsigned-big, @@ -1471,19 +1472,19 @@ lookup_handle() -> $\026:8/integer-unit:1-unsigned-big, $%:8/integer-unit:1-unsigned-big, $\n:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $¦:8/integer-unit:1-unsigned-big, $0:8/integer-unit:1-unsigned-big, $0:8/integer-unit:1-unsigned-big, $F:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $ :8/integer-unit:1-unsigned-big, + $ð:8/integer-unit:1-unsigned-big, + $â:8/integer-unit:1-unsigned-big, + $³:8/integer-unit:1-unsigned-big, $\000:8/integer-unit:1-unsigned-big, $\000:8/integer-unit:1-unsigned-big, $\222:8/integer-unit:1-unsigned-big, $\n:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big>>, + $ä:8/integer-unit:1-unsigned-big>>, <<$\203:8/integer-unit:1-unsigned-big, $h:8/integer-unit:1-unsigned-big, $\003:8/integer-unit:1-unsigned-big, @@ -1525,25 +1526,25 @@ lookup_handle() -> $+:8/integer-unit:1-unsigned-big, $x:8/integer-unit:1-unsigned-big, $\234:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ë:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, $M:8/integer-unit:1-unsigned-big, $a:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ê:8/integer-unit:1-unsigned-big, $/:8/integer-unit:1-unsigned-big, $H:8/integer-unit:1-unsigned-big, $\024:8/integer-unit:1-unsigned-big, $N:8/integer-unit:1-unsigned-big, $a:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $¶:8/integer-unit:1-unsigned-big, + $µ:8/integer-unit:1-unsigned-big, + $²:8/integer-unit:1-unsigned-big, + $Í:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, $\006:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ò:8/integer-unit:1-unsigned-big, $e:8/integer-unit:1-unsigned-big, $\211:8/integer-unit:1-unsigned-big, $E:8/integer-unit:1-unsigned-big, @@ -1555,10 +1556,10 @@ lookup_handle() -> $/:8/integer-unit:1-unsigned-big, $\022:8/integer-unit:1-unsigned-big, $\000:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $×:8/integer-unit:1-unsigned-big, $\227:8/integer-unit:1-unsigned-big, $\t:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big>>, + $Û:8/integer-unit:1-unsigned-big>>, <<$\203:8/integer-unit:1-unsigned-big, $P:8/integer-unit:1-unsigned-big, $\000:8/integer-unit:1-unsigned-big, @@ -1567,33 +1568,33 @@ lookup_handle() -> $\\:8/integer-unit:1-unsigned-big, $x:8/integer-unit:1-unsigned-big, $\234:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ë:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, $I:8/integer-unit:1-unsigned-big, $a:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, $I:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Î:8/integer-unit:1-unsigned-big, + $Ï:8/integer-unit:1-unsigned-big, $+:8/integer-unit:1-unsigned-big, $N:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $ú:8/integer-unit:1-unsigned-big, + $ÿ:8/integer-unit:1-unsigned-big, + $ÿ:8/integer-unit:1-unsigned-big, + $ÿ:8/integer-unit:1-unsigned-big, + $û:8/integer-unit:1-unsigned-big, $\f:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $æ:8/integer-unit:1-unsigned-big, $\024:8/integer-unit:1-unsigned-big, $\006:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ö:8/integer-unit:1-unsigned-big, $\222:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ò:8/integer-unit:1-unsigned-big, $\202:8/integer-unit:1-unsigned-big, $\234:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ô:8/integer-unit:1-unsigned-big, $D:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Á:8/integer-unit:1-unsigned-big, $\034:8/integer-unit:1-unsigned-big, $\006:8/integer-unit:1-unsigned-big, $\006:8/integer-unit:1-unsigned-big, @@ -1605,7 +1606,7 @@ lookup_handle() -> $Y:8/integer-unit:1-unsigned-big, $b:8/integer-unit:1-unsigned-big, $Q:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $¢:8/integer-unit:1-unsigned-big, $`:8/integer-unit:1-unsigned-big, $\n:8/integer-unit:1-unsigned-big, $\003:8/integer-unit:1-unsigned-big, @@ -1616,7 +1617,7 @@ lookup_handle() -> $>:8/integer-unit:1-unsigned-big, $\v:8/integer-unit:1-unsigned-big, $I:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $µ:8/integer-unit:1-unsigned-big, $\020:8/integer-unit:1-unsigned-big, $H:8/integer-unit:1-unsigned-big, $5:8/integer-unit:1-unsigned-big, @@ -1630,7 +1631,7 @@ lookup_handle() -> $\005:8/integer-unit:1-unsigned-big, $\000:8/integer-unit:1-unsigned-big, $\024:8/integer-unit:1-unsigned-big, - $�:8/integer-unit:1-unsigned-big, + $Ù:8/integer-unit:1-unsigned-big, $\031:8/integer-unit:1-unsigned-big, $M:8/integer-unit:1-unsigned-big>>} end, diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl index 681c154463..3c49aaa103 100644 --- a/lib/stdlib/test/shell_SUITE.erl +++ b/lib/stdlib/test/shell_SUITE.erl @@ -1,3 +1,4 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% @@ -2820,7 +2821,7 @@ otp_10302(Config) when is_list(Config) -> "ok.\n** exception error: an error occurred when evaluating" " an arithmetic expression\n in operator '/'/2\n" - " called as <<\"�\">> / <<\"�\">>.\n" = t({Node,Test7}), + " called as <<\"ª\">> / <<\"ª\">>.\n" = t({Node,Test7}), Test8 = <<"begin A = [1089], diff --git a/lib/stdlib/test/supervisor_3.erl b/lib/stdlib/test/supervisor_3.erl new file mode 100644 index 0000000000..31b3037d6f --- /dev/null +++ b/lib/stdlib/test/supervisor_3.erl @@ -0,0 +1,45 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% Description: Simulates the behaviour that a child process may have. +%% Is used by the supervisor_SUITE test suite. +-module(supervisor_3). + +-export([start_child/2, init/1]). + +-export([handle_call/3, handle_info/2, terminate/2]). + +start_child(Name, Caller) -> + gen_server:start_link(?MODULE, [Name, Caller], []). + +init([Name, Caller]) -> + Caller ! {Name, self()}, + receive + {Result, Caller} -> + Result + end. + +handle_call(Req, _From, State) -> + {reply, Req, State}. + +handle_info(_, State) -> + {noreply, State}. + +terminate(_Reason, Time) -> + timer:sleep(Time), + ok. diff --git a/lib/stdlib/test/supervisor_SUITE.erl b/lib/stdlib/test/supervisor_SUITE.erl index 569c66959e..ff5be6bb95 100644 --- a/lib/stdlib/test/supervisor_SUITE.erl +++ b/lib/stdlib/test/supervisor_SUITE.erl @@ -53,9 +53,10 @@ %% Restart strategy tests -export([ one_for_one/1, one_for_one_escalation/1, one_for_all/1, - one_for_all_escalation/1, + one_for_all_escalation/1, one_for_all_other_child_fails_restart/1, simple_one_for_one/1, simple_one_for_one_escalation/1, rest_for_one/1, rest_for_one_escalation/1, + rest_for_one_other_child_fails_restart/1, simple_one_for_one_extra/1, simple_one_for_one_shutdown/1]). %% Misc tests @@ -107,12 +108,14 @@ groups() -> {restart_one_for_one, [], [one_for_one, one_for_one_escalation]}, {restart_one_for_all, [], - [one_for_all, one_for_all_escalation]}, + [one_for_all, one_for_all_escalation, + one_for_all_other_child_fails_restart]}, {restart_simple_one_for_one, [], [simple_one_for_one, simple_one_for_one_shutdown, simple_one_for_one_extra, simple_one_for_one_escalation]}, {restart_rest_for_one, [], - [rest_for_one, rest_for_one_escalation]}]. + [rest_for_one, rest_for_one_escalation, + rest_for_one_other_child_fails_restart]}]. init_per_suite(Config) -> Config. @@ -879,6 +882,57 @@ one_for_all_escalation(Config) when is_list(Config) -> %%------------------------------------------------------------------------- +%% Test that the supervisor terminates a restarted child when a different +%% child fails to start. +one_for_all_other_child_fails_restart(Config) when is_list(Config) -> + process_flag(trap_exit, true), + Self = self(), + Child1 = {child1, {supervisor_3, start_child, [child1, Self]}, + permanent, 1000, worker, []}, + Child2 = {child2, {supervisor_3, start_child, [child2, Self]}, + permanent, 1000, worker, []}, + Children = [Child1, Child2], + StarterFun = fun() -> + {ok, SupPid} = start_link({ok, {{one_for_all, 3, 3600}, Children}}), + Self ! {sup_pid, SupPid}, + receive {stop, Self} -> ok end + end, + StarterPid = spawn_link(StarterFun), + Ok = {{ok, undefined}, Self}, + %% Let the children start. + Child1Pid = receive {child1, Pid1} -> Pid1 end, + Child1Pid ! Ok, + Child2Pid = receive {child2, Pid2} -> Pid2 end, + Child2Pid ! Ok, + %% Supervisor started. + SupPid = receive {sup_pid, Pid} -> Pid end, + link(SupPid), + exit(Child1Pid, die), + %% Let child1 restart but don't let child2. + Child1Pid2 = receive {child1, Pid3} -> Pid3 end, + Child1Pid2Ref = erlang:monitor(process, Child1Pid2), + Child1Pid2 ! Ok, + Child2Pid2 = receive {child2, Pid4} -> Pid4 end, + Child2Pid2 ! {{stop, normal}, Self}, + %% Check child1 is terminated. + receive + {'DOWN', Child1Pid2Ref, _, _, shutdown} -> + ok; + {_childName, _Pid} -> + exit(SupPid, kill), + check_exit([StarterPid, SupPid]), + test_server:fail({restarting_child_not_terminated, Child1Pid2}) + end, + %% Let the restart complete. + Child1Pid3 = receive {child1, Pid5} -> Pid5 end, + Child1Pid3 ! Ok, + Child2Pid3 = receive {child2, Pid6} -> Pid6 end, + Child2Pid3 ! Ok, + StarterPid ! {stop, Self}, + check_exit([StarterPid, SupPid]). + + +%%------------------------------------------------------------------------- %% Test the simple_one_for_one base case. simple_one_for_one(Config) when is_list(Config) -> process_flag(trap_exit, true), @@ -1044,6 +1098,52 @@ rest_for_one_escalation(Config) when is_list(Config) -> terminate(SupPid, CPid1, child1, abnormal), check_exit([CPid2, SupPid]). + +%%------------------------------------------------------------------------- +%% Test that the supervisor terminates a restarted child when a different +%% child fails to start. +rest_for_one_other_child_fails_restart(Config) when is_list(Config) -> + process_flag(trap_exit, true), + Self = self(), + Child1 = {child1, {supervisor_3, start_child, [child1, Self]}, + permanent, 1000, worker, []}, + Child2 = {child2, {supervisor_3, start_child, [child2, Self]}, + permanent, 1000, worker, []}, + Children = [Child1, Child2], + StarterFun = fun() -> + {ok, SupPid} = start_link({ok, {{rest_for_one, 3, 3600}, Children}}), + Self ! {sup_pid, SupPid}, + receive {stop, Self} -> ok end + end, + StarterPid = spawn_link(StarterFun), + Ok = {{ok, undefined}, Self}, + %% Let the children start. + Child1Pid = receive {child1, Pid1} -> Pid1 end, + Child1Pid ! Ok, + Child2Pid = receive {child2, Pid2} -> Pid2 end, + Child2Pid ! Ok, + %% Supervisor started. + SupPid = receive {sup_pid, Pid} -> Pid end, + link(SupPid), + exit(Child1Pid, die), + %% Let child1 restart but don't let child2. + Child1Pid2 = receive {child1, Pid3} -> Pid3 end, + Child1Pid2 ! Ok, + Child2Pid2 = receive {child2, Pid4} -> Pid4 end, + Child2Pid2 ! {{stop, normal}, Self}, + %% Let child2 restart. + receive + {child2, Child2Pid3} -> + Child2Pid3 ! Ok; + {child1, _Child1Pid3} -> + exit(SupPid, kill), + check_exit([StarterPid, SupPid]), + test_server:fail({restarting_started_child, Child1Pid2}) + end, + StarterPid ! {stop, Self}, + check_exit([StarterPid, SupPid]). + + %%------------------------------------------------------------------------- %% Test that the supervisor does not hang forever if the child unliks %% and then is terminated by the supervisor. diff --git a/lib/test_server/src/erl2html2.erl b/lib/test_server/src/erl2html2.erl index 5584c1e50c..d0f40c47a7 100644 --- a/lib/test_server/src/erl2html2.erl +++ b/lib/test_server/src/erl2html2.erl @@ -1,3 +1,4 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% @@ -214,7 +215,7 @@ html_encoding(utf8) -> %%% from the source. %%% %%% Example: if the encoding of the file is utf8, and we have a string -%%% containing "�" = [229], then we need to convert this to [195,165] +%%% containing "å" = [229], then we need to convert this to [195,165] %%% before writing. Note that this conversion is only necessary %%% because the destination file is not (necessarily) opened with utf8 %%% encoding - it is opened with default encoding in order to allow diff --git a/lib/test_server/src/test_server_ctrl.erl b/lib/test_server/src/test_server_ctrl.erl index 21c10adccb..ffa21d054c 100644 --- a/lib/test_server/src/test_server_ctrl.erl +++ b/lib/test_server/src/test_server_ctrl.erl @@ -1183,7 +1183,7 @@ init_tester(Mod, Func, Args, Dir, Name, {_,_,MinLev}=Levels, "<td>~.3fs</td><td><b>~ts</b></td><td>~w Ok, ~w Failed~ts of ~w</td></tr>\n" "</tfoot>\n", [Time,SuccessStr,OkN,FailedN,SkipStr,OkN+FailedN+SkippedN]), - test_server_io:stop(). + test_server_io:stop([major,html,unexpected_io]). report_severe_error(Reason) -> test_server_sup:framework_call(report, [severe_error,Reason]). @@ -1588,7 +1588,7 @@ do_test_cases(TopCases, SkipCases, print(major, "=started ~s", [lists:flatten(timestamp_get(""))]), - put(test_server_html_footer, Footer), + test_server_io:set_footer(Footer), run_test_cases(TestSpec, Config, TimetrapData) end; diff --git a/lib/test_server/src/test_server_io.erl b/lib/test_server/src/test_server_io.erl index a979deffc3..73d4468bda 100644 --- a/lib/test_server/src/test_server_io.erl +++ b/lib/test_server/src/test_server_io.erl @@ -29,7 +29,7 @@ %% -module(test_server_io). --export([start_link/0,stop/0,get_gl/1,set_fd/2, +-export([start_link/0,stop/1,get_gl/1,set_fd/2, start_transaction/0,end_transaction/0, print_buffered/1,print/3,print_unexpected/1, set_footer/1,set_job_name/1,set_gl_props/1]). @@ -55,10 +55,10 @@ start_link() -> Other end. -stop() -> +stop(FilesToClose) -> OldGL = group_leader(), group_leader(self(), self()), - req(stop), + req({stop,FilesToClose}), group_leader(OldGL, self()), ok. @@ -213,12 +213,21 @@ handle_call({set_job_name,Name}, _From, St) -> handle_call({set_gl_props,Props}, _From, #st{shared_gl=Shared}=St) -> test_server_gl:set_props(Shared, Props), {reply,ok,St#st{gl_props=Props}}; -handle_call(stop, From, #st{shared_gl=SGL,gls=Gls0}=St0) -> +handle_call({stop,FdTags}, From, #st{fds=Fds,shared_gl=SGL,gls=Gls0}=St0) -> St = St0#st{gls=gb_sets:insert(SGL, Gls0),stopping=From}, gc(St), %% Give the users of the surviving group leaders some %% time to finish. erlang:send_after(2000, self(), stop_group_leaders), + %% close open log files + lists:foreach(fun(Tag) -> + case gb_trees:lookup(Tag, Fds) of + none -> + ok; + {value,Fd} -> + file:close(Fd) + end + end, FdTags), {noreply,St}. handle_info({'EXIT',Pid,normal}, #st{gls=Gls0,stopping=From}=St) -> diff --git a/lib/test_server/test/test_server_SUITE.erl b/lib/test_server/test/test_server_SUITE.erl index 3db2f5f9f1..8ad5fcfb5c 100644 --- a/lib/test_server/test/test_server_SUITE.erl +++ b/lib/test_server/test/test_server_SUITE.erl @@ -1,3 +1,4 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% @@ -187,7 +188,7 @@ test_server_unicode(Config) -> %% Create and run two test suites - one with filename and content %% in latin1 (if the default filename mode is latin1) and one with %% filename and content in utf8. Both have name and content - %% including letters ���. Check that all logs are generated with + %% including letters äöå. Check that all logs are generated with %% utf8 encoded filenames. case file:native_name_encoding() of utf8 -> @@ -348,7 +349,7 @@ generate_and_run_unicode_test(Config0,Encoding) -> SuiteHtml = translate_filename(LowerModStr++".src.html",Encoding), true = filelib:is_regular(filename:join(RunDir,SuiteHtml)), - TCLog = translate_filename(LowerModStr++".tc_���.html",Encoding), + TCLog = translate_filename(LowerModStr++".tc_äöå.html",Encoding), true = filelib:is_regular(filename:join(RunDir,TCLog)), ok. @@ -370,7 +371,7 @@ start_node(Config,Name,Args) -> end. create_unicode_test_suite(Dir,Encoding) -> - ModStr = "test_server_"++atom_to_list(Encoding)++"_���_SUITE", + ModStr = "test_server_"++atom_to_list(Encoding)++"_äöå_SUITE", File = filename:join(Dir,ModStr++".erl"), Suite = ["%% -*- ",epp:encoding_to_string(Encoding)," -*-\n", @@ -378,12 +379,12 @@ create_unicode_test_suite(Dir,Encoding) -> "\n" "-export([all/1, init_per_suite/1, end_per_suite/1]).\n" "-export([init_per_testcase/2, end_per_testcase/2]).\n" - "-export([tc_���/1]).\n" + "-export([tc_äöå/1]).\n" "\n" "-include_lib(\"test_server/include/test_server.hrl\").\n" "\n" "all(suite) ->\n" - " [tc_���].\n" + " [tc_äöå].\n" "\n" "init_per_suite(Config) ->\n" " Config.\n" @@ -406,7 +407,7 @@ create_unicode_test_suite(Dir,Encoding) -> " ?t:timetrap_cancel(Dog),\n" " ok.\n" "\n" - "tc_���(Config) when is_list(Config) ->\n" + "tc_äöå(Config) when is_list(Config) ->\n" " true = filelib:is_dir(?config(priv_dir,Config)),\n" " ok.\n"], {ok,Fd} = file:open(raw_filename(File,Encoding),[write,{encoding,Encoding}]), diff --git a/lib/tools/src/fprof.erl b/lib/tools/src/fprof.erl index 4cbb910f11..877218bda0 100644 --- a/lib/tools/src/fprof.erl +++ b/lib/tools/src/fprof.erl @@ -1226,10 +1226,7 @@ spawn_3step(Spawn, FunPrelude, FunAck, FunBody) MRef = erlang:monitor(process, Parent), receive {Parent, Ref, Go} -> - erlang:demonitor(MRef), - receive {'DOWN', MRef, _, _, _} -> ok - after 0 -> ok - end, + erlang:demonitor(MRef, [flush]), FunBody(Go); {'DOWN', MRef, _, _, _} -> ok @@ -1238,8 +1235,7 @@ spawn_3step(Spawn, FunPrelude, FunAck, FunBody) MRef = erlang:monitor(process, Child), receive {Child, Ref, Ack} -> - erlang:demonitor(MRef), - receive {'DOWN', MRef, _, _, _} -> ok after 0 -> ok end, + erlang:demonitor(MRef, [flush]), try FunAck(Ack) of {Result, Go} -> catch Child ! {Parent, Ref, Go}, diff --git a/make/run_make.mk b/make/run_make.mk index 1b4213107f..bb0da6743c 100644 --- a/make/run_make.mk +++ b/make/run_make.mk @@ -30,7 +30,7 @@ include $(ERL_TOP)/make/target.mk .PHONY: valgrind -opt debug purify quantify purecov valgrind gcov gprof lcnt: +opt debug purify quantify purecov valgrind gcov gprof lcnt frmptr: $(make_verbose)$(MAKE) -f $(TARGET)/Makefile TYPE=$@ plain smp frag smp_frag: |