aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--erts/aclocal.m414
-rw-r--r--erts/emulator/beam/external.c76
-rw-r--r--erts/emulator/test/map_SUITE.erl110
-rw-r--r--erts/include/internal/ethread.h3
-rw-r--r--erts/preloaded/ebin/init.beambin48768 -> 48812 bytes
-rw-r--r--erts/preloaded/src/init.erl2
-rw-r--r--lib/compiler/src/sys_core_fold.erl6
-rw-r--r--lib/compiler/test/bs_match_SUITE.erl22
-rw-r--r--lib/crypto/doc/src/crypto.xml7
-rw-r--r--lib/erl_interface/src/connect/ei_resolve.c14
-rw-r--r--lib/inets/src/http_client/httpc_handler.erl11
-rw-r--r--lib/inets/src/http_lib/http_chunk.erl2
-rw-r--r--lib/inets/test/http_format_SUITE.erl16
-rw-r--r--lib/inets/test/httpc_SUITE.erl21
-rw-r--r--lib/ssh/doc/src/ssh_app.xml70
-rw-r--r--lib/ssh/doc/src/using_ssh.xml2
-rw-r--r--lib/ssh/src/ssh.hrl1
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl129
-rw-r--r--lib/ssh/src/ssh_transport.erl494
-rw-r--r--lib/ssh/test/ssh_renegotiate_SUITE.erl22
-rw-r--r--lib/ssh/test/ssh_test_lib.erl10
-rw-r--r--lib/ssl/src/inet_tls_dist.erl2
-rw-r--r--lib/ssl/src/ssl_dist_sup.erl2
-rw-r--r--lib/ssl/src/tls_connection.erl3
-rw-r--r--lib/ssl/src/tls_record.erl62
-rw-r--r--lib/stdlib/doc/src/rand.xml2
-rw-r--r--lib/stdlib/doc/src/random.xml2
27 files changed, 772 insertions, 333 deletions
diff --git a/erts/aclocal.m4 b/erts/aclocal.m4
index 390d6cfc4d..3d52538933 100644
--- a/erts/aclocal.m4
+++ b/erts/aclocal.m4
@@ -142,18 +142,18 @@ MIXED_MSYS=no
AC_MSG_CHECKING(for mixed cygwin or msys and native VC++ environment)
if test "X$host" = "Xwin32" -a "x$GCC" != "xyes"; then
- if test -x /usr/bin/cygpath; then
- CFLAGS="-O2"
- MIXED_CYGWIN=yes
- AC_MSG_RESULT([Cygwin and VC])
- MIXED_CYGWIN_VC=yes
- CPPFLAGS="$CPPFLAGS -DERTS_MIXED_CYGWIN_VC"
- elif test -x /usr/bin/msysinfo; then
+ if test -x /usr/bin/msys-?.0.dll; then
CFLAGS="-O2"
MIXED_MSYS=yes
AC_MSG_RESULT([MSYS and VC])
MIXED_MSYS_VC=yes
CPPFLAGS="$CPPFLAGS -DERTS_MIXED_MSYS_VC"
+ elif test -x /usr/bin/cygpath; then
+ CFLAGS="-O2"
+ MIXED_CYGWIN=yes
+ AC_MSG_RESULT([Cygwin and VC])
+ MIXED_CYGWIN_VC=yes
+ CPPFLAGS="$CPPFLAGS -DERTS_MIXED_CYGWIN_VC"
else
AC_MSG_RESULT([undeterminable])
AC_MSG_ERROR(Seems to be mixed windows but not with cygwin, cannot handle this!)
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index c6d7e3fcc5..a85aa15403 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -1179,7 +1179,7 @@ typedef struct {
ErtsHeapFactory factory;
int remaining_n;
char* remaining_bytes;
- Eterm* maps_list;
+ ErtsWStack flat_maps;
ErtsPStack hamt_array;
} B2TDecodeContext;
@@ -1519,7 +1519,7 @@ static BIF_RETTYPE binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binar
ctx->u.dc.res = (Eterm) (UWord) NULL;
ctx->u.dc.next = &ctx->u.dc.res;
erts_factory_proc_prealloc_init(&ctx->u.dc.factory, p, ctx->heap_size);
- ctx->u.dc.maps_list = NULL;
+ ctx->u.dc.flat_maps.wstart = NULL;
ctx->u.dc.hamt_array.pstart = NULL;
ctx->state = B2TDecode;
/*fall through*/
@@ -2938,7 +2938,7 @@ dec_term(ErtsDistExternal *edep,
int n;
ErtsAtomEncoding char_enc;
register Eterm* hp; /* Please don't take the address of hp */
- Eterm *maps_list; /* for preprocessing of small maps */
+ DECLARE_WSTACK(flat_maps); /* for preprocessing of small maps */
Eterm* next;
SWord reds;
#ifdef DEBUG
@@ -2950,7 +2950,6 @@ dec_term(ErtsDistExternal *edep,
next = ctx->u.dc.next;
ep = ctx->u.dc.ep;
factory = &ctx->u.dc.factory;
- maps_list = ctx->u.dc.maps_list;
if (ctx->state != B2TDecode) {
int n_limit = reds;
@@ -3026,15 +3025,18 @@ dec_term(ErtsDistExternal *edep,
}
}
PSTACK_CHANGE_ALLOCATOR(hamt_array, ERTS_ALC_T_SAVED_ESTACK);
+ WSTACK_CHANGE_ALLOCATOR(flat_maps, ERTS_ALC_T_SAVED_ESTACK);
if (ctx->u.dc.hamt_array.pstart) {
PSTACK_RESTORE(hamt_array, &ctx->u.dc.hamt_array);
}
+ if (ctx->u.dc.flat_maps.wstart) {
+ WSTACK_RESTORE(flat_maps, &ctx->u.dc.flat_maps);
+ }
}
else {
reds = ERTS_SWORD_MAX;
next = objp;
*next = (Eterm) (UWord) NULL;
- maps_list = NULL;
}
hp = factory->hp;
@@ -3595,14 +3597,8 @@ dec_term_atom_common:
* vptr, last word for values
*/
- /*
- * Use thing_word to link through decoded maps.
- * The list of maps is for later validation.
- */
-
- mp->thing_word = (Eterm) COMPRESS_POINTER(maps_list);
- maps_list = (Eterm *) mp;
-
+ WSTACK_PUSH(flat_maps, (UWord)mp);
+ mp->thing_word = MAP_HEADER_FLATMAP;
mp->size = size;
mp->keys = keys;
*objp = make_flatmap(mp);
@@ -3851,7 +3847,9 @@ dec_term_atom_common:
ctx->u.dc.ep = ep;
ctx->u.dc.next = next;
ctx->u.dc.factory.hp = hp;
- ctx->u.dc.maps_list = maps_list;
+ if (!WSTACK_ISEMPTY(flat_maps)) {
+ WSTACK_SAVE(flat_maps, &ctx->u.dc.flat_maps);
+ }
if (!PSTACK_IS_EMPTY(hamt_array)) {
PSTACK_SAVE(hamt_array, &ctx->u.dc.hamt_array);
}
@@ -3865,18 +3863,6 @@ dec_term_atom_common:
}
}
- /* Iterate through all the maps and check for validity and sort keys
- * - done here for when we know it is complete.
- */
-
- while (maps_list) {
- next = (Eterm *)(EXPAND_POINTER(*maps_list));
- *maps_list = MAP_HEADER_FLATMAP;
- if (!erts_validate_and_sort_flatmap((flatmap_t*)maps_list))
- goto error;
- maps_list = next;
- }
-
ASSERT(hp <= factory->hp_end
|| (factory->mode == FACTORY_CLOSED && is_immed(*dbg_resultp)));
factory->hp = hp;
@@ -3885,20 +3871,31 @@ dec_term_atom_common:
*/
if (!PSTACK_IS_EMPTY(hamt_array)) {
- do {
- struct dec_term_hamt* hamt = PSTACK_TOP(hamt_array);
-
- *hamt->objp = erts_hashmap_from_array(factory,
- hamt->leaf_array,
- hamt->size,
- 1);
- if (is_non_value(*hamt->objp))
- goto error_hamt;
-
- (void) PSTACK_POP(hamt_array);
- } while (!PSTACK_IS_EMPTY(hamt_array));
- PSTACK_DESTROY(hamt_array);
+ do {
+ struct dec_term_hamt* hamt = PSTACK_TOP(hamt_array);
+
+ *hamt->objp = erts_hashmap_from_array(factory,
+ hamt->leaf_array,
+ hamt->size,
+ 1);
+ if (is_non_value(*hamt->objp))
+ goto error_hamt;
+
+ (void) PSTACK_POP(hamt_array);
+ } while (!PSTACK_IS_EMPTY(hamt_array));
+ PSTACK_DESTROY(hamt_array);
+ }
+
+ /* Iterate through all the (flat)maps and check for validity and sort keys
+ * - done here for when we know it is complete.
+ */
+
+ while(!WSTACK_ISEMPTY(flat_maps)) {
+ next = (Eterm *)WSTACK_POP(flat_maps);
+ if (!erts_validate_and_sort_flatmap((flatmap_t*)next))
+ goto error;
}
+ WSTACK_DESTROY(flat_maps);
ASSERT((Eterm*)EXPAND_POINTER(*dbg_resultp) != NULL);
@@ -3924,6 +3921,7 @@ error_hamt:
ctx->state = B2TDecodeFail;
ctx->reds = reds;
}
+ WSTACK_DESTROY(flat_maps);
return NULL;
}
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index 886ae7d516..62a94e5281 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -2181,7 +2181,9 @@ t_map_encode_decode(Config) when is_list(Config) ->
{<<>>, sc9}, {3.14158, sc10},
{[3.14158], sc11}, {more_atoms, sc12},
{{more_tuples}, sc13}, {self(), sc14},
- {{},{}},{[],[]}
+ {{},{}},{[],[]},
+ {map_s, #{a=>a, 2=>b, 3=>c}},
+ {map_l, maps:from_list([{I,I}||I <- lists:seq(1,74)])}
],
ok = map_encode_decode_and_match(Pairs,[],#{}),
@@ -2245,9 +2247,30 @@ t_map_encode_decode(Config) when is_list(Config) ->
%% bad size (too small) .. should fail just truncate it .. weird.
%% possibly change external format so truncated will be #{a:=1}
- #{ a:=b } =
- erlang:binary_to_term(<<131,116,0,0,0,1,100,0,1,97,100,0,1,98,97,1,97,1>>),
-
+ #{ a:=b } = erlang:binary_to_term(<<131,116,0,0,0,1,100,0,1,97,100,0,1,98,97,1,97,1>>),
+
+ %% specific fannerl (opensource app) binary_to_term error in 18.1
+
+ #{bias := {1,1,0},
+ bit_fail := 0,
+ connections := #{{2,9} := _,
+ {8,14} := _,
+ {2,12} := _,
+ {5,7} := _,
+ {11,16} := _,
+ {11,15} := _},
+ layers := {5,7,3},
+ network_type := fann_nettype_layer,
+ num_input := 5,
+ num_layers := 3,
+ num_output := 3,
+ rprop_delta_max := _,
+ rprop_delta_min := _,
+ total_connections := 66,
+ total_neurons := 17,
+ train_error_function := fann_errorfunc_tanh,
+ train_stop_function := fann_stopfunc_mse,
+ training_algorithm := fann_train_rprop} = erlang:binary_to_term(fannerl()),
ok.
map_encode_decode_and_match([{K,V}|Pairs], EncodedPairs, M0) ->
@@ -2966,3 +2989,82 @@ do_badmap_17(Config) ->
%% Use this function to avoid compile-time evaluation of an expression.
id(I) -> I.
+
+
+%% map external_format (fannerl).
+fannerl() ->
+ <<131,116,0,0,0,28,100,0,13,108,101,97,114,110,105,110,103,95,114,
+ 97,116,101,70,63,230,102,102,96,0,0,0,100,0,17,108,101,97,114,110,105,110,
+ 103,95,109,111,109,101,110,116,117,109,70,0,0,0,0,0,0,0,0,100,0,
+ 18,116,114,97,105,110,105,110,103,95,97,108,103,111,114,105,116,104,109,100,0,
+ 16,102,97,110,110,95,116,114,97,105,110,95,114,112,114,111,112,
+ 100,0,17,109,101,97,110,95,115,113,117,97,114,101,95,101,114,114,111,114,70,
+ 0,0,0,0,0,0,0,0,100,0,8,98,105,116,95,102,97,105,108,97,0,100,0,20,
+ 116,114,97,105,110,95,101,114,114,111,114,95,102,117,110,99,116,105,111,
+ 110,100,0,19,102,97,110,110,95,101,114,114,111,114,102,117,110,99,
+ 95,116,97,110,104,100,0,9,110,117,109,95,105,110,112,117,116,97,5,100,0,10,110,
+ 117,109,95,111,117,116,112,117,116,97,3,100,0,13,116,111,116,97,108,
+ 95,110,101,117,114,111,110,115,97,17,100,0,17,116,111,116,97,108,95,99,111,110,
+ 110,101,99,116,105,111,110,115,97,66,100,0,12,110,101,116,119,111,114,107,
+ 95,116,121,112,101,100,0,18,102,97,110,110,95,110,101,116,116,121,112,101,
+ 95,108,97,121,101,114,100,0,15,99,111,110,110,101,99,116,105,111,110,95,
+ 114,97,116,101,70,63,240,0,0,0,0,0,0,100,0,10,110,117,109,95,108,97,121,101,
+ 114,115,97,3,100,0,19,116,114,97,105,110,95,115,116,111,112,95,102,117,110,
+ 99,116,105,111,110,100,0,17,102,97,110,110,95,115,116,111,112,102,117,110,
+ 99,95,109,115,101,100,0,15,113,117,105,99,107,112,114,111,112,95,100,101,99,
+ 97,121,70,191,26,54,226,224,0,0,0,100,0,12,113,117,105,99,107,112,114,
+ 111,112,95,109,117,70,63,252,0,0,0,0,0,0,100,0,21,114,112,114,111,112,95,105,
+ 110,99,114,101,97,115,101,95,102,97,99,116,111,114,70,63,243,51,51,
+ 64,0,0,0,100,0,21,114,112,114,111,112,95,100,101,99,114,101,97,115,101,
+ 95,102,97,99,116,111,114,70,63,224,0,0,0,0,0,0,100,0,15,114,112,114,111,112,
+ 95,100,101,108,116,97,95,109,105,110,70,0,0,0,0,0,0,0,0,100,0,15,114,112,114,
+ 111,112,95,100,101,108,116,97,95,109,97,120,70,64,73,0,0,0,0,0,0,100,0,
+ 16,114,112,114,111,112,95,100,101,108,116,97,95,122,101,114,111,70,63,185,153,
+ 153,160,0,0,0,100,0,26,115,97,114,112,114,111,112,95,119,101,105,103,
+ 104,116,95,100,101,99,97,121,95,115,104,105,102,116,70,192,26,147,116,192,0,0,0,
+ 100,0,35,115,97,114,112,114,111,112,95,115,116,101,112,95,101,114,
+ 114,111,114,95,116,104,114,101,115,104,111,108,100,95,102,97,99,116,111,114,70,
+ 63,185,153,153,160,0,0,0,100,0,24,115,97,114,112,114,111,112,95,115,
+ 116,101,112,95,101,114,114,111,114,95,115,104,105,102,116,70,63,246,40,245,
+ 192,0,0,0,100,0,19,115,97,114,112,114,111,112,95,116,101,109,112,101,114,
+ 97,116,117,114,101,70,63,142,184,81,224,0,0,0,100,0,6,108,97,121,101,114,115,
+ 104,3,97,5,97,7,97,3,100,0,4,98,105,97,115,104,3,97,1,97,1,97,0,100,0,11,
+ 99,111,110,110,101,99,116,105,111,110,115,116,0,0,0,66,104,2,97,0,97,6,70,
+ 191,179,51,44,64,0,0,0,104,2,97,1,97,6,70,63,178,130,90,32,0,0,0,104,2,97,2,
+ 97,6,70,63,82,90,88,0,0,0,0,104,2,97,3,97,6,70,63,162,91,63,192,0,0,0,104,2,
+ 97,4,97,6,70,191,151,70,169,0,0,0,0,104,2,97,5,97,6,70,191,117,52,222,0,0,0,
+ 0,104,2,97,0,97,7,70,63,152,240,139,0,0,0,0,104,2,97,1,97,7,70,191,166,31,
+ 187,160,0,0,0,104,2,97,2,97,7,70,191,150,70,63,0,0,0,0,104,2,97,3,97,7,70,
+ 63,152,181,126,128,0,0,0,104,2,97,4,97,7,70,63,151,187,162,128,0,0,0,104,2,
+ 97,5,97,7,70,191,143,161,101,0,0,0,0,104,2,97,0,97,8,70,191,153,102,36,128,0,
+ 0,0,104,2,97,1,97,8,70,63,160,139,250,64,0,0,0,104,2,97,2,97,8,70,63,164,62,
+ 196,64,0,0,0,104,2,97,3,97,8,70,191,178,78,209,192,0,0,0,104,2,97,4,97,8,70,
+ 191,185,19,76,224,0,0,0,104,2,97,5,97,8,70,63,183,142,196,96,0,0,0,104,2,97,0,
+ 97,9,70,63,150,104,248,0,0,0,0,104,2,97,1,97,9,70,191,164,4,100,224,0,0,0,
+ 104,2,97,2,97,9,70,191,169,42,42,224,0,0,0,104,2,97,3,97,9,70,63,145,54,78,128,0,
+ 0,0,104,2,97,4,97,9,70,63,126,243,134,0,0,0,0,104,2,97,5,97,9,70,63,177,
+ 203,25,96,0,0,0,104,2,97,0,97,10,70,63,172,104,47,64,0,0,0,104,2,97,1,97,10,
+ 70,63,161,242,193,64,0,0,0,104,2,97,2,97,10,70,63,175,208,241,192,0,0,0,104,2,
+ 97,3,97,10,70,191,129,202,161,0,0,0,0,104,2,97,4,97,10,70,63,178,151,55,32,0,0,0,
+ 104,2,97,5,97,10,70,63,137,155,94,0,0,0,0,104,2,97,0,97,11,70,191,179,
+ 106,160,0,0,0,0,104,2,97,1,97,11,70,63,184,253,164,96,0,0,0,104,2,97,2,97,11,
+ 70,191,143,30,157,0,0,0,0,104,2,97,3,97,11,70,63,153,225,140,128,0,0,0,104,
+ 2,97,4,97,11,70,63,161,35,85,192,0,0,0,104,2,97,5,97,11,70,63,175,200,55,192,
+ 0,0,0,104,2,97,0,97,12,70,191,180,116,132,96,0,0,0,104,2,97,1,97,12,70,191,
+ 165,151,152,0,0,0,0,104,2,97,2,97,12,70,191,180,197,91,160,0,0,0,104,2,97,3,97,12,
+ 70,191,91,30,160,0,0,0,0,104,2,97,4,97,12,70,63,180,251,45,32,0,0,0,
+ 104,2,97,5,97,12,70,63,165,134,77,64,0,0,0,104,2,97,6,97,14,70,63,181,56,242,96,
+ 0,0,0,104,2,97,7,97,14,70,191,165,239,234,224,0,0,0,104,2,97,8,97,14,
+ 70,191,154,65,216,128,0,0,0,104,2,97,9,97,14,70,63,150,250,236,0,0,0,0,104,2,97,
+ 10,97,14,70,191,141,105,108,0,0,0,0,104,2,97,11,97,14,70,191,152,40,
+ 165,0,0,0,0,104,2,97,12,97,14,70,63,141,159,46,0,0,0,0,104,2,97,13,97,14,70,
+ 191,183,172,137,32,0,0,0,104,2,97,6,97,15,70,63,163,26,123,192,0,0,0,104,
+ 2,97,7,97,15,70,63,176,184,106,32,0,0,0,104,2,97,8,97,15,70,63,152,234,144,
+ 0,0,0,0,104,2,97,9,97,15,70,191,172,58,70,160,0,0,0,104,2,97,10,97,15,70,
+ 63,161,211,211,192,0,0,0,104,2,97,11,97,15,70,191,148,171,120,128,0,0,0,104,
+ 2,97,12,97,15,70,63,180,117,214,224,0,0,0,104,2,97,13,97,15,70,191,104,
+ 230,216,0,0,0,0,104,2,97,6,97,16,70,63,178,53,103,96,0,0,0,104,2,97,7,97,16,
+ 70,63,170,230,232,64,0,0,0,104,2,97,8,97,16,70,191,183,45,100,192,0,0,0,
+ 104,2,97,9,97,16,70,63,184,100,97,32,0,0,0,104,2,97,10,97,16,70,63,169,174,
+ 254,64,0,0,0,104,2,97,11,97,16,70,191,119,121,234,0,0,0,0,104,2,97,12,97,
+ 16,70,63,149,12,170,128,0,0,0,104,2,97,13,97,16,70,191,144,193,191,0,0,0,0>>.
diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h
index f9c203e97c..8964f95652 100644
--- a/erts/include/internal/ethread.h
+++ b/erts/include/internal/ethread.h
@@ -54,7 +54,8 @@
#endif
#if defined(ETHR_DEBUG) || !defined(ETHR_INLINE) || ETHR_XCHK \
- || (defined(__GNUC__) && defined(ERTS_MIXED_CYGWIN_VC))
+ || (defined(__GNUC__) && defined(ERTS_MIXED_CYGWIN_VC)) \
+ || (defined(__GNUC__) && defined(ERTS_MIXED_MSYS_VC))
# undef ETHR_INLINE
# define ETHR_INLINE
# undef ETHR_FORCE_INLINE
diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam
index 851513b2e9..73dfb3d351 100644
--- a/erts/preloaded/ebin/init.beam
+++ b/erts/preloaded/ebin/init.beam
Binary files differ
diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl
index c4e37b76f1..0ad5824ad1 100644
--- a/erts/preloaded/src/init.erl
+++ b/erts/preloaded/src/init.erl
@@ -167,7 +167,6 @@ stop(Status) -> init ! {stop,{stop,Status}}, ok.
boot(BootArgs) ->
register(init, self()),
process_flag(trap_exit, true),
- start_on_load_handler_process(),
{Start0,Flags,Args} = parse_boot_args(BootArgs),
Start = map(fun prepare_run_args/1, Start0),
Flags0 = flags_to_atoms_again(Flags),
@@ -225,6 +224,7 @@ code_path_choice() ->
end.
boot(Start,Flags,Args) ->
+ start_on_load_handler_process(),
BootPid = do_boot(Flags,Start),
State = #state{flags = Flags,
args = Args,
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index 27d023d067..65699ccda9 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -3091,12 +3091,12 @@ bsm_ensure_no_partition_2([#c_var{name=V}|Ps], N, G, Vstate, S) ->
bsm_ensure_no_partition_2([_|Ps], N, G, _, S) ->
bsm_ensure_no_partition_2(Ps, N-1, G, bin_argument_order, S).
-bsm_ensure_no_partition_after([#c_clause{pats=Ps}|Cs], Pos) ->
+bsm_ensure_no_partition_after([#c_clause{pats=Ps}=C|Cs], Pos) ->
case nth(Pos, Ps) of
#c_var{} ->
bsm_ensure_no_partition_after(Cs, Pos);
- P ->
- bsm_problem(P, bin_partition)
+ _ ->
+ bsm_problem(C, bin_partition)
end;
bsm_ensure_no_partition_after([], _) -> ok.
diff --git a/lib/compiler/test/bs_match_SUITE.erl b/lib/compiler/test/bs_match_SUITE.erl
index 6e138b0a43..b4601b0798 100644
--- a/lib/compiler/test/bs_match_SUITE.erl
+++ b/lib/compiler/test/bs_match_SUITE.erl
@@ -36,7 +36,7 @@
match_string/1,zero_width/1,bad_size/1,haystack/1,
cover_beam_bool/1,matched_out_size/1,follow_fail_branch/1,
no_partition/1,calling_a_binary/1,binary_in_map/1,
- match_string_opt/1]).
+ match_string_opt/1,map_and_binary/1]).
-export([coverage_id/1,coverage_external_ignore/2]).
@@ -62,7 +62,7 @@ groups() ->
otp_7498,match_string,zero_width,bad_size,haystack,
cover_beam_bool,matched_out_size,follow_fail_branch,
no_partition,calling_a_binary,binary_in_map,
- match_string_opt]}].
+ match_string_opt,map_and_binary]}].
init_per_suite(Config) ->
@@ -1225,6 +1225,24 @@ match_string_opt(Config) when is_list(Config) ->
do_match_string_opt({<<1>>,{v,V}}=T) ->
{x,V,T}.
+%% If 'bin_opt_info' was given the warning would lack filename
+%% and line number.
+
+map_and_binary(_Config) ->
+ {<<"10">>,<<"37">>,<<"am">>} = do_map_and_binary(<<"10:37am">>),
+ Map1 = #{time => "noon"},
+ {ok,Map1} = do_map_and_binary(Map1),
+ Map2 = #{hour => 8, min => 42},
+ {8,42,Map2} = do_map_and_binary(Map2),
+ ok.
+
+do_map_and_binary(<<Hour:2/bytes, $:, Min:2/bytes, Rest/binary>>) ->
+ {Hour, Min, Rest};
+do_map_and_binary(#{time := _} = T) ->
+ {ok, T};
+do_map_and_binary(#{hour := Hour, min := Min} = T) ->
+ {Hour, Min, T}.
+
check(F, R) ->
R = F().
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index 8d082bf3fe..563a090e98 100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -601,8 +601,11 @@
</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>
+ result in a binary. Uses the <c>crypto</c> library pseudo-random
+ number generator.</p>
+ <p>This function is not recommended for cryptographic purposes.
+ Please use <seealso marker="#strong_rand_bytes/1">
+ strong_rand_bytes/1</seealso> instead.</p>
</desc>
</func>
diff --git a/lib/erl_interface/src/connect/ei_resolve.c b/lib/erl_interface/src/connect/ei_resolve.c
index 3f1be2b17d..6381b02393 100644
--- a/lib/erl_interface/src/connect/ei_resolve.c
+++ b/lib/erl_interface/src/connect/ei_resolve.c
@@ -601,6 +601,16 @@ struct hostent *ei_gethostbyaddr(const char *addr, int len, int type)
return gethostbyaddr(addr, len, type);
}
+/*
+ * Imprecise way to select the actually available gethostbyname_r and
+ * gethostbyaddr_r.
+ *
+ * TODO: check this properly in configure.in
+ */
+#if (defined(__linux__) || (__FreeBSD_version >= 602000) || defined(__DragonFly__))
+ #define HAVE_GETHOSTBYADDR_R_8 1
+#endif
+
struct hostent *ei_gethostbyaddr_r(const char *addr,
int length,
int type,
@@ -616,7 +626,7 @@ struct hostent *ei_gethostbyaddr_r(const char *addr,
#ifndef HAVE_GETHOSTBYNAME_R
return my_gethostbyaddr_r(addr,length,type,hostp,buffer,buflen,h_errnop);
#else
-#if (defined(__GLIBC__) || (__FreeBSD_version >= 602000) || defined(__DragonFly__))
+#ifdef HAVE_GETHOSTBYADDR_R_8
struct hostent *result;
gethostbyaddr_r(addr, length, type, hostp, buffer, buflen, &result,
@@ -643,7 +653,7 @@ struct hostent *ei_gethostbyname_r(const char *name,
#ifndef HAVE_GETHOSTBYNAME_R
return my_gethostbyname_r(name,hostp,buffer,buflen,h_errnop);
#else
-#if (defined(__GLIBC__) || (__FreeBSD_version >= 602000) || defined(__DragonFly__) || defined(__ANDROID__))
+#ifdef HAVE_GETHOSTBYADDR_R_8
struct hostent *result;
gethostbyname_r(name, hostp, buffer, buflen, &result, h_errnop);
diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl
index 1044cffe6f..e6dcfee818 100644
--- a/lib/inets/src/http_client/httpc_handler.erl
+++ b/lib/inets/src/http_client/httpc_handler.erl
@@ -1113,8 +1113,8 @@ handle_http_body(Body, #state{headers = Headers,
case case_insensitive_header(TransferEnc) of
"chunked" ->
?hcrt("handle_http_body - chunked", []),
- case http_chunk:decode(Body, State#state.max_body_size,
- State#state.max_header_size) of
+ try http_chunk:decode(Body, State#state.max_body_size,
+ State#state.max_header_size) of
{Module, Function, Args} ->
?hcrt("handle_http_body - new mfa",
[{module, Module},
@@ -1139,6 +1139,13 @@ handle_http_body(Body, #state{headers = Headers,
handle_response(State#state{headers = NewHeaders,
body = NewBody2})
end
+ catch throw:{error, Reason} ->
+ NewState =
+ answer_request(Request,
+ httpc_response:error(Request,
+ Reason),
+ State),
+ {stop, normal, NewState}
end;
Enc when Enc =:= "identity"; Enc =:= undefined ->
?hcrt("handle_http_body - identity", []),
diff --git a/lib/inets/src/http_lib/http_chunk.erl b/lib/inets/src/http_lib/http_chunk.erl
index 2f8476a49d..9699856bf8 100644
--- a/lib/inets/src/http_lib/http_chunk.erl
+++ b/lib/inets/src/http_lib/http_chunk.erl
@@ -147,7 +147,7 @@ decode_size(Data = <<?CR, ?LF, ChunkRest/binary>>, HexList, AccHeaderSize,
{MaxBodySize, Body,
AccLength,
MaxHeaderSize}) ->
- try http_util:hexlist_to_integer(lists:reverse(HexList)) of
+ try http_util:hexlist_to_integer(lists:reverse(string:strip(HexList, left))) of
0 -> % Last chunk, there was no data
ignore_extensions(Data, remaing_size(MaxHeaderSize, AccHeaderSize), MaxHeaderSize,
{?MODULE, decode_trailer,
diff --git a/lib/inets/test/http_format_SUITE.erl b/lib/inets/test/http_format_SUITE.erl
index a927adc75e..e977bd1b9b 100644
--- a/lib/inets/test/http_format_SUITE.erl
+++ b/lib/inets/test/http_format_SUITE.erl
@@ -38,6 +38,7 @@ groups() ->
[chunk_decode, chunk_encode, chunk_extensions_otp_6005,
chunk_decode_otp_6264,
chunk_decode_empty_chunk_otp_6511,
+ chunk_whitespace_suffix,
chunk_decode_trailer, chunk_max_headersize, chunk_max_bodysize, chunk_not_hex]}].
init_per_suite(Config) ->
@@ -157,6 +158,21 @@ chunk_decode_empty_chunk_otp_6511(Config) when is_list(Config) ->
?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE).
%%-------------------------------------------------------------------------
+chunk_whitespace_suffix() ->
+ [{doc, "Test whitespace after chunked length header"}].
+chunk_whitespace_suffix(Config) when is_list(Config) ->
+ ChunkedBody = "1a ; ignore-stuff-here" ++ ?CRLF ++
+ "abcdefghijklmnopqrstuvwxyz" ++ ?CRLF ++ "10 " ++ ?CRLF
+ ++ "1234567890abcdef" ++ ?CRLF ++ "0 " ++ ?CRLF
+ ++ "some-footer:some-value" ++ ?CRLF
+ ++ "another-footer:another-value" ++ ?CRLF ++ ?CRLF,
+ {ok, {["content-length:42", "another-footer:another-value",
+ "some-footer:some-value", ""],
+ <<"abcdefghijklmnopqrstuvwxyz1234567890abcdef">>}} =
+ http_chunk:decode(list_to_binary(ChunkedBody),
+ ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE).
+
+%%-------------------------------------------------------------------------
chunk_decode_trailer() ->
[{doc,"Make sure trailers are handled correctly. Trailers should"
"become new headers"}].
diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl
index 989563cdbc..c6c59ab1af 100644
--- a/lib/inets/test/httpc_SUITE.erl
+++ b/lib/inets/test/httpc_SUITE.erl
@@ -106,6 +106,7 @@ only_simulated() ->
bad_response,
internal_server_error,
invalid_http,
+ invalid_chunk_size,
headers_dummy,
headers_with_obs_fold,
empty_response_header,
@@ -765,6 +766,22 @@ invalid_http(Config) when is_list(Config) ->
ct:print("Parse error: ~p ~n", [Reason]).
%%-------------------------------------------------------------------------
+
+invalid_chunk_size(doc) ->
+ ["Test parse error of HTTP chunk size"];
+invalid_chunk_size(suite) ->
+ [];
+invalid_chunk_size(Config) when is_list(Config) ->
+
+ URL = url(group_name(Config), "/invalid_chunk_size.html", Config),
+
+ {error, {chunk_size, _} = Reason} =
+ httpc:request(get, {URL, []}, [], []),
+
+ ct:print("Parse error: ~p ~n", [Reason]).
+
+%%-------------------------------------------------------------------------
+
emulate_lower_versions(doc) ->
[{doc, "Perform request as 0.9 and 1.0 clients."}];
emulate_lower_versions(Config) when is_list(Config) ->
@@ -1876,6 +1893,10 @@ handle_uri(_,"/invalid_http.html",_,_,_,_) ->
"HTTP/1.1 301\r\nDate:Sun, 09 Dec 2007 13:04:18 GMT\r\n" ++
"Transfer-Encoding:chunked\r\n\r\n";
+handle_uri(_,"/invalid_chunk_size.html",_,_,_,_) ->
+ "HTTP/1.1 200 ok\r\n" ++
+ "Transfer-Encoding:chunked\r\n\r\nåäö\r\n";
+
handle_uri(_,"/missing_reason_phrase.html",_,_,_,_) ->
"HTTP/1.1 200\r\n" ++
"Content-Length: 32\r\n\r\n"
diff --git a/lib/ssh/doc/src/ssh_app.xml b/lib/ssh/doc/src/ssh_app.xml
index 29cbbd79a2..79dd1e210e 100644
--- a/lib/ssh/doc/src/ssh_app.xml
+++ b/lib/ssh/doc/src/ssh_app.xml
@@ -137,6 +137,19 @@
<p>Supported algorithms are:</p>
<taglist>
+ <tag>Key exchange algorithms</tag>
+ <item>
+ <list type="bulleted">
+ <item>ecdh-sha2-nistp256</item>
+ <item>ecdh-sha2-nistp384</item>
+ <item>ecdh-sha2-nistp521</item>
+ <item>diffie-hellman-group-exchange-sha1</item>
+ <item>diffie-hellman-group-exchange-sha256</item>
+ <item>diffie-hellman-group14-sha1</item>
+ <item>diffie-hellman-group1-sha1</item>
+ </list>
+ </item>
+
<tag>Public key algorithms</tag>
<item>
<list type="bulleted">
@@ -157,30 +170,26 @@
</list>
</item>
- <tag>Encryption algorithms</tag>
+ <tag>Encryption algorithms (ciphers)</tag>
<item>
<list type="bulleted">
+ <item>[email protected] (AEAD_AES_128_GCM)</item>
+ <item>[email protected] (AEAD_AES_256_GCM)</item>
<item>aes128-ctr</item>
<item>aes192-ctr</item>
<item>aes256-ctr</item>
<item>aes128-cbc</item>
<item>3des-cbc</item>
</list>
+ <p>Following the internet de-facto standard, the cipher and mac algorithm AEAD_AES_128_GCM is selected when the
+ cipher [email protected] is negotiated. The cipher and mac algorithm AEAD_AES_256_GCM is selected when the
+ cipher [email protected] is negotiated.
+ </p>
+ <p>See the text at the description of <seealso marker="#rfc5647_note">the rfc 5647 further down</seealso>
+ for more information.
+ </p>
</item>
-
- <tag>Key exchange algorithms</tag>
- <item>
- <list type="bulleted">
- <item>ecdh-sha2-nistp256</item>
- <item>ecdh-sha2-nistp384</item>
- <item>ecdh-sha2-nistp521</item>
- <item>diffie-hellman-group-exchange-sha1</item>
- <item>diffie-hellman-group-exchange-sha256</item>
- <item>diffie-hellman-group14-sha1</item>
- <item>diffie-hellman-group1-sha1</item>
- </list>
- </item>
-
+
<tag>Compression algorithms</tag>
<item>
<list type="bulleted">
@@ -255,6 +264,30 @@
<p></p>
</item>
+ <item><url href="https://tools.ietf.org/html/rfc5647">RFC 5647</url>, AES Galois Counter Mode for
+ the Secure Shell Transport Layer Protocol.
+ <p><marker id="rfc5647_note"/>There is an ambiguity in the synchronized selection of cipher and mac algorithm.
+ This is resolved by OpenSSH in the ciphers [email protected] and [email protected] which are implemented.
+ If the explicit ciphers and macs AEAD_AES_128_GCM or AEAD_AES_256_GCM are needed,
+ they could be enabled with the option preferred_algorithms.
+ <warning>
+ If the client or the server is not Erlang/OTP, it is the users responsibility to check that
+ other implementation has the same interpretation of AEAD_AES_*_GCM as the Erlang/OTP SSH before
+ enabling them. The aes*[email protected] variants are always safe to use since they lack the
+ ambiguity.
+ </warning>
+ </p>
+ <p>The second paragraph in section 5.1 is resolved as:
+ <list type="ordered">
+ <item>If the negotiated cipher is AEAD_AES_128_GCM, the mac algorithm is set to AEAD_AES_128_GCM.</item>
+ <item>If the negotiated cipher is AEAD_AES_256_GCM, the mac algorithm is set to AEAD_AES_256_GCM.</item>
+ <item>If the mac algorithm is AEAD_AES_128_GCM, the cipher is set to AEAD_AES_128_GCM.</item>
+ <item>If the mac algorithm is AEAD_AES_256_GCM, the cipher is set to AEAD_AES_256_GCM.</item>
+ </list>
+ The first rule that matches when read in order from the top is applied
+ </p>
+ </item>
+
<item><url href="https://tools.ietf.org/html/rfc5656">RFC 5656</url>, Elliptic Curve Algorithm Integration in
the Secure Shell Transport Layer.
<p>Except
@@ -266,6 +299,13 @@
</list>
</p>
</item>
+
+ <item><url href="https://tools.ietf.org/html/rfc6668">RFC 6668</url>, SHA-2 Data Integrity Verification for
+ the Secure Shell (SSH) Transport Layer Protocol
+ <p>Comment: Defines hmac-sha2-256 and hmac-sha2-512
+ </p>
+ </item>
+
</list>
</section>
diff --git a/lib/ssh/doc/src/using_ssh.xml b/lib/ssh/doc/src/using_ssh.xml
index 2d045fdb60..6826f20fb3 100644
--- a/lib/ssh/doc/src/using_ssh.xml
+++ b/lib/ssh/doc/src/using_ssh.xml
@@ -252,7 +252,7 @@
<code type="erl">
%% First three parameters depending on which crypto type we select:
Key = &lt;&lt;"This is a 256 bit key. abcdefghi">>,
-Ivec0 = crypto:rand_bytes(16),
+Ivec0 = crypto:strong_rand_bytes(16),
DataSize = 1024, % DataSize rem 16 = 0 for aes_cbc
%% Initialization of the CryptoState, in this case it is the Ivector.
diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl
index 4ad936f742..8efc743b67 100644
--- a/lib/ssh/src/ssh.hrl
+++ b/lib/ssh/src/ssh.hrl
@@ -29,7 +29,6 @@
-define(SSH_DEFAULT_PORT, 22).
-define(SSH_MAX_PACKET_SIZE, (256*1024)).
--define(SSH_LENGHT_INDICATOR_SIZE, 4).
-define(REKEY_TIMOUT, 3600000).
-define(REKEY_DATA_TIMOUT, 60000).
-define(DEFAULT_PROFILE, default).
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index 8448218d91..505c6eb181 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -970,57 +970,39 @@ handle_info({Protocol, Socket, Info}, hello,
transport_protocol = Protocol} = State) ->
event({info_line, Info}, hello, State);
-handle_info({Protocol, Socket, Data}, Statename,
+handle_info({Protocol, Socket, Data}, StateName,
#state{socket = Socket,
transport_protocol = Protocol,
- ssh_params = #ssh{decrypt_block_size = BlockSize,
- recv_mac_size = MacSize} = Ssh0,
- decoded_data_buffer = <<>>,
- encoded_data_buffer = EncData0} = State0) ->
-
- %% Implementations SHOULD decrypt the length after receiving the
- %% first 8 (or cipher block size, whichever is larger) bytes of a
- %% packet. (RFC 4253: Section 6 - Binary Packet Protocol)
- case size(EncData0) + size(Data) >= erlang:max(8, BlockSize) of
- true ->
- {Ssh, SshPacketLen, DecData, EncData} =
-
- ssh_transport:decrypt_first_block(<<EncData0/binary,
- Data/binary>>, Ssh0),
- case SshPacketLen > ?SSH_MAX_PACKET_SIZE of
- true ->
- DisconnectMsg =
- #ssh_msg_disconnect{code =
- ?SSH_DISCONNECT_PROTOCOL_ERROR,
- description = "Bad packet length "
- ++ integer_to_list(SshPacketLen),
- language = "en"},
- handle_disconnect(DisconnectMsg, State0);
- false ->
- RemainingSshPacketLen =
- (SshPacketLen + ?SSH_LENGHT_INDICATOR_SIZE) -
- BlockSize + MacSize,
- State = State0#state{ssh_params = Ssh},
- handle_ssh_packet_data(RemainingSshPacketLen,
- DecData, EncData, Statename,
- State)
- end;
- false ->
- {next_state, Statename,
- next_packet(State0#state{encoded_data_buffer =
- <<EncData0/binary, Data/binary>>})}
+ ssh_params = Ssh0,
+ decoded_data_buffer = DecData0,
+ encoded_data_buffer = EncData0,
+ undecoded_packet_length = RemainingSshPacketLen0} = State0) ->
+ Encoded = <<EncData0/binary, Data/binary>>,
+ case ssh_transport:handle_packet_part(DecData0, Encoded, RemainingSshPacketLen0, Ssh0) of
+ {get_more, DecBytes, EncDataRest, RemainingSshPacketLen, Ssh1} ->
+ {next_state, StateName,
+ next_packet(State0#state{encoded_data_buffer = EncDataRest,
+ decoded_data_buffer = DecBytes,
+ undecoded_packet_length = RemainingSshPacketLen,
+ ssh_params = Ssh1})};
+ {decoded, MsgBytes, EncDataRest, Ssh1} ->
+ generate_event(MsgBytes, StateName,
+ State0#state{ssh_params = Ssh1,
+ %% Important to be set for
+ %% next_packet
+%%% FIXME: the following three seem to always be set in generate_event!
+ decoded_data_buffer = <<>>,
+ undecoded_packet_length = undefined,
+ encoded_data_buffer = EncDataRest},
+ EncDataRest);
+ {bad_mac, Ssh1} ->
+ DisconnectMsg =
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
+ description = "Bad mac",
+ language = ""},
+ handle_disconnect(DisconnectMsg, State0#state{ssh_params=Ssh1})
end;
-
-handle_info({Protocol, Socket, Data}, Statename,
- #state{socket = Socket,
- transport_protocol = Protocol,
- decoded_data_buffer = DecData,
- encoded_data_buffer = EncData,
- undecoded_packet_length = Len} =
- State) when is_integer(Len) ->
- handle_ssh_packet_data(Len, DecData, <<EncData/binary, Data/binary>>,
- Statename, State);
-
+
handle_info({CloseTag, _Socket}, _StateName,
#state{transport_close_tag = CloseTag,
ssh_params = #ssh{role = _Role, opts = _Opts}} = State) ->
@@ -1631,57 +1613,6 @@ after_new_keys_events({connection_reply, _Data} = Reply, {StateName, State}) ->
NewState = send_replies([Reply], State),
{next_state, StateName, NewState}.
-handle_ssh_packet_data(RemainingSshPacketLen, DecData, EncData, StateName,
- State) ->
- EncSize = size(EncData),
- case RemainingSshPacketLen > EncSize of
- true ->
- {next_state, StateName,
- next_packet(State#state{decoded_data_buffer = DecData,
- encoded_data_buffer = EncData,
- undecoded_packet_length =
- RemainingSshPacketLen})};
- false ->
- handle_ssh_packet(RemainingSshPacketLen, StateName,
- State#state{decoded_data_buffer = DecData,
- encoded_data_buffer = EncData})
-
- end.
-
-handle_ssh_packet(Length, StateName, #state{decoded_data_buffer = DecData0,
- encoded_data_buffer = EncData0,
- ssh_params = Ssh0,
- transport_protocol = _Protocol,
- socket = _Socket} = State0) ->
- try
- {Ssh1, DecData, EncData, Mac} =
- ssh_transport:unpack(EncData0, Length, Ssh0),
- SshPacket = <<DecData0/binary, DecData/binary>>,
- case ssh_transport:is_valid_mac(Mac, SshPacket, Ssh1) of
- true ->
- PacketData = ssh_transport:msg_data(SshPacket),
- {Ssh1, Msg} = ssh_transport:decompress(Ssh1, PacketData),
- generate_event(Msg, StateName,
- State0#state{ssh_params = Ssh1,
- %% Important to be set for
- %% next_packet
- decoded_data_buffer = <<>>},
- EncData);
- false ->
- DisconnectMsg =
- #ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
- description = "Bad mac",
- language = "en"},
- handle_disconnect(DisconnectMsg, State0)
- end
- catch _:_ ->
- Disconnect =
- #ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
- description = "Bad input",
- language = "en"},
- handle_disconnect(Disconnect, State0)
- end.
-
handle_disconnect(DisconnectMsg, State) ->
handle_disconnect(own, DisconnectMsg, State).
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index 0c999b96cc..041e86bae1 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -31,10 +31,10 @@
-include("ssh.hrl").
-export([versions/2, hello_version_msg/1]).
--export([next_seqnum/1, decrypt_first_block/2, decrypt_blocks/3,
+-export([next_seqnum/1,
supported_algorithms/0, supported_algorithms/1,
default_algorithms/0, default_algorithms/1,
- is_valid_mac/3,
+ handle_packet_part/4,
handle_hello_version/1,
key_exchange_init_msg/1,
key_init/3, new_keys_message/1,
@@ -45,9 +45,13 @@
handle_kex_ecdh_init/2,
handle_kex_ecdh_reply/2,
extract_public_key/1,
- unpack/3, decompress/2, ssh_packet/2, pack/2, pack/3, msg_data/1,
+ ssh_packet/2, pack/2,
sign/3, verify/4]).
+%%% For test suites
+-export([pack/3]).
+-export([decompress/2, decrypt_blocks/3, is_valid_mac/3 ]). % FIXME: remove
+
%%%----------------------------------------------------------------------------
%%%
%%% There is a difference between supported and default algorithms. The
@@ -66,10 +70,15 @@ default_algorithms() -> [{K,default_algorithms(K)} || K <- algo_classes()].
algo_classes() -> [kex, public_key, cipher, mac, compression].
-%% default_algorithms(kex) -> % Example of how to disable an algorithm
-%% supported_algorithms(kex, ['ecdh-sha2-nistp521']);
+
+default_algorithms(cipher) ->
+ supported_algorithms(cipher, same(['AEAD_AES_128_GCM',
+ 'AEAD_AES_256_GCM']));
+default_algorithms(mac) ->
+ supported_algorithms(mac, same(['AEAD_AES_128_GCM',
+ 'AEAD_AES_256_GCM']));
default_algorithms(Alg) ->
- supported_algorithms(Alg).
+ supported_algorithms(Alg, []).
supported_algorithms() -> [{K,supported_algorithms(K)} || K <- algo_classes()].
@@ -97,19 +106,25 @@ supported_algorithms(public_key) ->
supported_algorithms(cipher) ->
same(
select_crypto_supported(
- [{'aes256-ctr', [{ciphers,{aes_ctr,256}}]},
- {'aes192-ctr', [{ciphers,{aes_ctr,192}}]},
- {'aes128-ctr', [{ciphers,{aes_ctr,128}}]},
- {'aes128-cbc', [{ciphers,aes_cbc128}]},
- {'3des-cbc', [{ciphers,des3_cbc}]}
+ [{'aes256-ctr', [{ciphers,{aes_ctr,256}}]},
+ {'aes192-ctr', [{ciphers,{aes_ctr,192}}]},
+ {'aes128-ctr', [{ciphers,{aes_ctr,128}}]},
+ {'aes128-cbc', [{ciphers,aes_cbc128}]},
+ {'[email protected]', [{ciphers,{aes_gcm,128}}]},
+ {'[email protected]', [{ciphers,{aes_gcm,256}}]},
+ {'AEAD_AES_128_GCM', [{ciphers,{aes_gcm,128}}]},
+ {'AEAD_AES_256_GCM', [{ciphers,{aes_gcm,256}}]},
+ {'3des-cbc', [{ciphers,des3_cbc}]}
]
));
supported_algorithms(mac) ->
same(
select_crypto_supported(
- [{'hmac-sha2-256', [{hashs,sha256}]},
- {'hmac-sha2-512', [{hashs,sha512}]},
- {'hmac-sha1', [{hashs,sha}]}
+ [{'hmac-sha2-256', [{hashs,sha256}]},
+ {'hmac-sha2-512', [{hashs,sha512}]},
+ {'hmac-sha1', [{hashs,sha}]},
+ {'AEAD_AES_128_GCM', [{ciphers,{aes_gcm,128}}]},
+ {'AEAD_AES_256_GCM', [{ciphers,{aes_gcm,256}}]}
]
));
supported_algorithms(compression) ->
@@ -118,46 +133,6 @@ supported_algorithms(compression) ->
'zlib'
]).
-%% Dialyzer complains when not called...supported_algorithms(Key, [{client2server,BL1},{server2client,BL2}]) ->
-%% Dialyzer complains when not called... [{client2server,As1},{server2client,As2}] = supported_algorithms(Key),
-%% Dialyzer complains when not called... [{client2server,As1--BL1},{server2client,As2--BL2}];
-%% Dialyzer complains when not called...supported_algorithms(Key, BlackList) ->
-%% Dialyzer complains when not called... supported_algorithms(Key) -- BlackList.
-
-select_crypto_supported(L) ->
- Sup = [{ec_curve,crypto_supported_curves()} | crypto:supports()],
- [Name || {Name,CryptoRequires} <- L,
- crypto_supported(CryptoRequires, Sup)].
-
-crypto_supported_curves() ->
- try crypto:ec_curves()
- catch _:_ -> []
- end.
-
-crypto_supported(Conditions, Supported) ->
- lists:all( fun({Tag,CryptoName}) when is_atom(CryptoName) ->
- crypto_name_supported(Tag,CryptoName,Supported);
- ({Tag,{Name=aes_ctr,Len}}) when is_integer(Len) ->
- crypto_name_supported(Tag,Name,Supported) andalso
- ctr_len_supported(Name,Len)
- end, Conditions).
-
-crypto_name_supported(Tag, CryptoName, Supported) ->
- lists:member(CryptoName, proplists:get_value(Tag,Supported,[])).
-
-ctr_len_supported(Name, Len) ->
- try
- crypto:stream_encrypt(crypto:stream_init(Name, <<0:Len>>, <<0:128>>), <<"">>)
- of
- {_,X} -> is_binary(X)
- catch
- _:_ -> false
- end.
-
-
-same(Algs) -> [{client2server,Algs}, {server2client,Algs}].
-
-
%%%----------------------------------------------------------------------------
versions(client, Options)->
Vsn = proplists:get_value(vsn, Options, ?DEFAULT_CLIENT_VERSION),
@@ -196,12 +171,6 @@ hello_version_msg(Data) ->
next_seqnum(SeqNum) ->
(SeqNum + 1) band 16#ffffffff.
-decrypt_first_block(Bin, #ssh{decrypt_block_size = BlockSize} = Ssh0) ->
- <<EncBlock:BlockSize/binary, EncData/binary>> = Bin,
- {Ssh, <<?UINT32(PacketLen), _/binary>> = DecData} =
- decrypt(Ssh0, EncBlock),
- {Ssh, PacketLen, DecData, EncData}.
-
decrypt_blocks(Bin, Length, Ssh0) ->
<<EncBlocks:Length/binary, EncData/binary>> = Bin,
{Ssh, DecData} = decrypt(Ssh0, EncBlocks),
@@ -757,8 +726,12 @@ known_host_key(#ssh{opts = Opts, key_cb = Mod, peer = Peer} = Ssh,
%% The first algorithm in each list MUST be the preferred (guessed)
%% algorithm. Each string MUST contain at least one algorithm name.
select_algorithm(Role, Client, Server) ->
- {Encrypt, Decrypt} = select_encrypt_decrypt(Role, Client, Server),
- {SendMac, RecvMac} = select_send_recv_mac(Role, Client, Server),
+ {Encrypt0, Decrypt0} = select_encrypt_decrypt(Role, Client, Server),
+ {SendMac0, RecvMac0} = select_send_recv_mac(Role, Client, Server),
+
+ {Encrypt, SendMac} = aead_gcm_simultan(Encrypt0, SendMac0),
+ {Decrypt, RecvMac} = aead_gcm_simultan(Decrypt0, RecvMac0),
+
{Compression, Decompression} =
select_compression_decompression(Role, Client, Server),
@@ -789,6 +762,38 @@ select_algorithm(Role, Client, Server) ->
s_lng = S_Lng},
{ok, Alg}.
+
+%%% It is an agreed problem with RFC 5674 that if the selection is
+%%% Cipher = AEAD_AES_x_GCM and
+%%% Mac = AEAD_AES_y_GCM (where x =/= y)
+%%% then it is undefined what length should be selected.
+%%%
+%%% If only one of the two lengths (128,256) is available, I claim that
+%%% there is no such ambiguity.
+
+%%% From https://anongit.mindrot.org/openssh.git/plain/PROTOCOL
+%%% (read Nov 20, 2015)
+%%% 1.6 transport: AES-GCM
+%%%
+%%% OpenSSH supports the AES-GCM algorithm as specified in RFC 5647.
+%%% Because of problems with the specification of the key exchange
+%%% the behaviour of OpenSSH differs from the RFC as follows:
+%%%
+%%% AES-GCM is only negotiated as the cipher algorithms
+%%% "[email protected]" or "[email protected]" and never as
+%%% an MAC algorithm. Additionally, if AES-GCM is selected as the cipher
+%%% the exchanged MAC algorithms are ignored and there doesn't have to be
+%%% a matching MAC.
+
+aead_gcm_simultan('[email protected]', _) -> {'AEAD_AES_128_GCM', 'AEAD_AES_128_GCM'};
+aead_gcm_simultan('[email protected]', _) -> {'AEAD_AES_256_GCM', 'AEAD_AES_256_GCM'};
+aead_gcm_simultan('AEAD_AES_128_GCM', _) -> {'AEAD_AES_128_GCM', 'AEAD_AES_128_GCM'};
+aead_gcm_simultan('AEAD_AES_256_GCM', _) -> {'AEAD_AES_256_GCM', 'AEAD_AES_256_GCM'};
+aead_gcm_simultan(_, 'AEAD_AES_128_GCM') -> {'AEAD_AES_128_GCM', 'AEAD_AES_128_GCM'};
+aead_gcm_simultan(_, 'AEAD_AES_256_GCM') -> {'AEAD_AES_256_GCM', 'AEAD_AES_256_GCM'};
+aead_gcm_simultan(Cipher, Mac) -> {Cipher,Mac}.
+
+
select_encrypt_decrypt(client, Client, Server) ->
Encrypt =
select(Client#ssh_msg_kexinit.encryption_algorithms_client_to_server,
@@ -823,18 +828,18 @@ select_compression_decompression(client, Client, Server) ->
Compression =
select(Client#ssh_msg_kexinit.compression_algorithms_client_to_server,
Server#ssh_msg_kexinit.compression_algorithms_client_to_server),
- Decomprssion =
+ Decompression =
select(Client#ssh_msg_kexinit.compression_algorithms_server_to_client,
Server#ssh_msg_kexinit.compression_algorithms_server_to_client),
- {Compression, Decomprssion};
+ {Compression, Decompression};
select_compression_decompression(server, Client, Server) ->
- Decomprssion =
+ Decompression =
select(Client#ssh_msg_kexinit.compression_algorithms_client_to_server,
Server#ssh_msg_kexinit.compression_algorithms_client_to_server),
Compression =
select(Client#ssh_msg_kexinit.compression_algorithms_server_to_client,
Server#ssh_msg_kexinit.compression_algorithms_server_to_client),
- {Compression, Decomprssion}.
+ {Compression, Decompression}.
install_alg(SSH) ->
SSH1 = alg_final(SSH),
@@ -911,14 +916,39 @@ pack(Data, Ssh=#ssh{}) ->
%%% Note: pack/3 is only to be called from tests that wants
%%% to deliberetly send packets with wrong PacketLength!
%%% Use pack/2 for all other purposes!
-pack(Data0, #ssh{encrypt_block_size = BlockSize,
- send_sequence = SeqNum, send_mac = MacAlg,
- send_mac_key = MacKey,
- random_length_padding = RandomLengthPadding}
- = Ssh0,
- PacketLenDeviationForTests) when is_binary(Data0) ->
- {Ssh1, Data} = compress(Ssh0, Data0),
- PL = (BlockSize - ((4 + 1 + size(Data)) rem BlockSize)) rem BlockSize,
+pack(PlainText,
+ #ssh{send_sequence = SeqNum,
+ send_mac = MacAlg,
+ send_mac_key = MacKey,
+ encrypt = CryptoAlg} = Ssh0, PacketLenDeviationForTests) when is_binary(PlainText) ->
+
+ {Ssh1, CompressedPlainText} = compress(Ssh0, PlainText),
+ {EcryptedPacket, MAC, Ssh3} =
+ case pkt_type(CryptoAlg) of
+ common ->
+ PaddingLen = padding_length(4+1+size(CompressedPlainText), Ssh0),
+ Padding = ssh_bits:random(PaddingLen),
+ PlainPacketLen = 1 + PaddingLen + size(CompressedPlainText) + PacketLenDeviationForTests,
+ PlainPacketData = <<?UINT32(PlainPacketLen),?BYTE(PaddingLen), CompressedPlainText/binary, Padding/binary>>,
+ {Ssh2, EcryptedPacket0} = encrypt(Ssh1, PlainPacketData),
+ MAC0 = mac(MacAlg, MacKey, SeqNum, PlainPacketData),
+ {EcryptedPacket0, MAC0, Ssh2};
+ aead ->
+ PaddingLen = padding_length(1+size(CompressedPlainText), Ssh0),
+ Padding = ssh_bits:random(PaddingLen),
+ PlainPacketLen = 1 + PaddingLen + size(CompressedPlainText) + PacketLenDeviationForTests,
+ PlainPacketData = <<?BYTE(PaddingLen), CompressedPlainText/binary, Padding/binary>>,
+ {Ssh2, {EcryptedPacket0,MAC0}} = encrypt(Ssh1, {<<?UINT32(PlainPacketLen)>>,PlainPacketData}),
+ {<<?UINT32(PlainPacketLen),EcryptedPacket0/binary>>, MAC0, Ssh2}
+ end,
+ FinalPacket = [EcryptedPacket, MAC],
+ Ssh = Ssh3#ssh{send_sequence = (SeqNum+1) band 16#ffffffff},
+ {FinalPacket, Ssh}.
+
+
+padding_length(Size, #ssh{encrypt_block_size = BlockSize,
+ random_length_padding = RandomLengthPadding}) ->
+ PL = (BlockSize - (Size rem BlockSize)) rem BlockSize,
MinPaddingLen = if PL < 4 -> PL + BlockSize;
true -> PL
end,
@@ -927,45 +957,94 @@ pack(Data0, #ssh{encrypt_block_size = BlockSize,
ExtraPaddingLen = try crypto:rand_uniform(0,MaxExtraBlocks)*PadBlockSize
catch _:_ -> 0
end,
- PaddingLen = MinPaddingLen + ExtraPaddingLen,
- Padding = ssh_bits:random(PaddingLen),
- PacketLen = 1 + PaddingLen + size(Data) + PacketLenDeviationForTests,
- PacketData = <<?UINT32(PacketLen),?BYTE(PaddingLen),
- Data/binary, Padding/binary>>,
- {Ssh2, EncPacket} = encrypt(Ssh1, PacketData),
- MAC = mac(MacAlg, MacKey, SeqNum, PacketData),
- Packet = [EncPacket, MAC],
- Ssh = Ssh2#ssh{send_sequence = (SeqNum+1) band 16#ffffffff},
- {Packet, Ssh}.
-
-unpack(EncodedSoFar, ReminingLenght, #ssh{recv_mac_size = MacSize} = Ssh0) ->
- SshLength = ReminingLenght - MacSize,
- {NoMac, Mac, Rest} = case MacSize of
- 0 ->
- <<NoMac0:SshLength/binary,
- Rest0/binary>> = EncodedSoFar,
- {NoMac0, <<>>, Rest0};
- _ ->
- <<NoMac0:SshLength/binary,
- Mac0:MacSize/binary,
- Rest0/binary>> = EncodedSoFar,
- {NoMac0, Mac0, Rest0}
- end,
- {Ssh1, DecData, <<>>} =
- case SshLength of
- 0 ->
- {Ssh0, <<>>, <<>>};
- _ ->
- decrypt_blocks(NoMac, SshLength, Ssh0)
- end,
- {Ssh1, DecData, Rest, Mac}.
+ MinPaddingLen + ExtraPaddingLen.
+
+
+
+handle_packet_part(<<>>, Encrypted0, undefined, #ssh{decrypt = CryptoAlg} = Ssh0) ->
+ %% New ssh packet
+ case get_length(pkt_type(CryptoAlg), Encrypted0, Ssh0) of
+ get_more ->
+ %% too short to get the length
+ {get_more, <<>>, Encrypted0, undefined, Ssh0};
+
+ {ok, PacketLen, _, _, _} when PacketLen > ?SSH_MAX_PACKET_SIZE ->
+ %% far too long message than expected
+ throw(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
+ description = "Bad packet length "
+ ++ integer_to_list(PacketLen),
+ language = ""});
+
+ {ok, PacketLen, Decrypted, Encrypted1,
+ #ssh{recv_mac_size = MacSize} = Ssh1} ->
+ %% enough bytes so we got the length and can calculate how many
+ %% more bytes to expect for a full packet
+ TotalNeeded = (4 + PacketLen + MacSize),
+ handle_packet_part(Decrypted, Encrypted1, TotalNeeded, Ssh1)
+ end;
+
+handle_packet_part(DecryptedPfx, EncryptedBuffer, TotalNeeded, Ssh0)
+ when (size(DecryptedPfx)+size(EncryptedBuffer)) < TotalNeeded ->
+ %% need more bytes to finalize the packet
+ {get_more, DecryptedPfx, EncryptedBuffer, TotalNeeded, Ssh0};
+
+handle_packet_part(DecryptedPfx, EncryptedBuffer, TotalNeeded,
+ #ssh{recv_mac_size = MacSize,
+ decrypt = CryptoAlg} = Ssh0) ->
+ %% enough bytes to decode the packet.
+ DecryptLen = TotalNeeded - size(DecryptedPfx) - MacSize,
+ <<EncryptedSfx:DecryptLen/binary, Mac:MacSize/binary, NextPacketBytes/binary>> = EncryptedBuffer,
+ case pkt_type(CryptoAlg) of
+ common ->
+ {Ssh1, DecryptedSfx} = decrypt(Ssh0, EncryptedSfx),
+ DecryptedPacket = <<DecryptedPfx/binary, DecryptedSfx/binary>>,
+ case is_valid_mac(Mac, DecryptedPacket, Ssh1) of
+ false ->
+ {bad_mac, Ssh1};
+ true ->
+ {Ssh, DecompressedPayload} = decompress(Ssh1, payload(DecryptedPacket)),
+ {decoded, DecompressedPayload, NextPacketBytes, Ssh}
+ end;
+ aead ->
+ PacketLenBin = DecryptedPfx,
+ case decrypt(Ssh0, {PacketLenBin,EncryptedSfx,Mac}) of
+ {Ssh1, error} ->
+ {bad_mac, Ssh1};
+ {Ssh1, DecryptedSfx} ->
+ DecryptedPacket = <<DecryptedPfx/binary, DecryptedSfx/binary>>,
+ {Ssh, DecompressedPayload} = decompress(Ssh1, payload(DecryptedPacket)),
+ {decoded, DecompressedPayload, NextPacketBytes, Ssh}
+ end
+ end.
+
+
+get_length(common, EncryptedBuffer, #ssh{decrypt_block_size = BlockSize} = Ssh0) ->
+ case size(EncryptedBuffer) >= erlang:max(8, BlockSize) of
+ true ->
+ <<EncBlock:BlockSize/binary, EncryptedRest/binary>> = EncryptedBuffer,
+ {Ssh,
+ <<?UINT32(PacketLen),_/binary>> = Decrypted} = decrypt(Ssh0, EncBlock),
+ {ok, PacketLen, Decrypted, EncryptedRest, Ssh};
+ false ->
+ get_more
+ end;
+get_length(aead, EncryptedBuffer, Ssh) ->
+ case size(EncryptedBuffer) >= 4 of
+ true ->
+ <<?UINT32(PacketLen), EncryptedRest/binary>> = EncryptedBuffer,
+ {ok, PacketLen, <<?UINT32(PacketLen)>>, EncryptedRest, Ssh};
+ false ->
+ get_more
+ end.
+
+pkt_type('AEAD_AES_128_GCM') -> aead;
+pkt_type('AEAD_AES_256_GCM') -> aead;
+pkt_type(_) -> common.
-msg_data(PacketData) ->
- <<Len:32, PaddingLen:8, _/binary>> = PacketData,
- DataLen = Len - PaddingLen - 1,
- <<_:32, _:8, Data:DataLen/binary,
- _:PaddingLen/binary>> = PacketData,
- Data.
+payload(<<PacketLen:32, PaddingLen:8, PayloadAndPadding/binary>>) ->
+ PayloadLen = PacketLen - PaddingLen - 1,
+ <<Payload:PayloadLen/binary, _/binary>> = PayloadAndPadding,
+ Payload.
sign(SigData, Hash, #'DSAPrivateKey'{} = Key) ->
DerSignature = public_key:sign(SigData, Hash, Key),
@@ -991,6 +1070,7 @@ verify(PlainText, Hash, Sig, {#'ECPoint'{},_} = Key) ->
verify(PlainText, Hash, Sig, Key) ->
public_key:verify(PlainText, Hash, Sig, Key).
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Encryption
@@ -999,6 +1079,30 @@ verify(PlainText, Hash, Sig, Key) ->
encrypt_init(#ssh{encrypt = none} = Ssh) ->
{ok, Ssh};
+encrypt_init(#ssh{encrypt = 'AEAD_AES_128_GCM', role = client} = Ssh) ->
+ IV = hash(Ssh, "A", 12*8),
+ <<K:16/binary>> = hash(Ssh, "C", 128),
+ {ok, Ssh#ssh{encrypt_keys = K,
+ encrypt_block_size = 16,
+ encrypt_ctx = IV}};
+encrypt_init(#ssh{encrypt = 'AEAD_AES_128_GCM', role = server} = Ssh) ->
+ IV = hash(Ssh, "B", 12*8),
+ <<K:16/binary>> = hash(Ssh, "D", 128),
+ {ok, Ssh#ssh{encrypt_keys = K,
+ encrypt_block_size = 16,
+ encrypt_ctx = IV}};
+encrypt_init(#ssh{encrypt = 'AEAD_AES_256_GCM', role = client} = Ssh) ->
+ IV = hash(Ssh, "A", 12*8),
+ <<K:32/binary>> = hash(Ssh, "C", 256),
+ {ok, Ssh#ssh{encrypt_keys = K,
+ encrypt_block_size = 16,
+ encrypt_ctx = IV}};
+encrypt_init(#ssh{encrypt = 'AEAD_AES_256_GCM', role = server} = Ssh) ->
+ IV = hash(Ssh, "B", 12*8),
+ <<K:32/binary>> = hash(Ssh, "D", 256),
+ {ok, Ssh#ssh{encrypt_keys = K,
+ encrypt_block_size = 16,
+ encrypt_ctx = IV}};
encrypt_init(#ssh{encrypt = '3des-cbc', role = client} = Ssh) ->
IV = hash(Ssh, "A", 64),
<<K1:8/binary, K2:8/binary, K3:8/binary>> = hash(Ssh, "C", 192),
@@ -1075,6 +1179,18 @@ encrypt_final(Ssh) ->
encrypt(#ssh{encrypt = none} = Ssh, Data) ->
{Ssh, Data};
+encrypt(#ssh{encrypt = 'AEAD_AES_128_GCM',
+ encrypt_keys = K,
+ encrypt_ctx = IV0} = Ssh, Data={_AAD,_Ptext}) ->
+ Enc = {_Ctext,_Ctag} = crypto:block_encrypt(aes_gcm, K, IV0, Data),
+ IV = next_gcm_iv(IV0),
+ {Ssh#ssh{encrypt_ctx = IV}, Enc};
+encrypt(#ssh{encrypt = 'AEAD_AES_256_GCM',
+ encrypt_keys = K,
+ encrypt_ctx = IV0} = Ssh, Data={_AAD,_Ptext}) ->
+ Enc = {_Ctext,_Ctag} = crypto:block_encrypt(aes_gcm, K, IV0, Data),
+ IV = next_gcm_iv(IV0),
+ {Ssh#ssh{encrypt_ctx = IV}, Enc};
encrypt(#ssh{encrypt = '3des-cbc',
encrypt_keys = {K1,K2,K3},
encrypt_ctx = IV0} = Ssh, Data) ->
@@ -1107,6 +1223,30 @@ encrypt(#ssh{encrypt = 'aes256-ctr',
decrypt_init(#ssh{decrypt = none} = Ssh) ->
{ok, Ssh};
+decrypt_init(#ssh{decrypt = 'AEAD_AES_128_GCM', role = client} = Ssh) ->
+ IV = hash(Ssh, "B", 12*8),
+ <<K:16/binary>> = hash(Ssh, "D", 128),
+ {ok, Ssh#ssh{decrypt_keys = K,
+ decrypt_block_size = 16,
+ decrypt_ctx = IV}};
+decrypt_init(#ssh{decrypt = 'AEAD_AES_128_GCM', role = server} = Ssh) ->
+ IV = hash(Ssh, "A", 12*8),
+ <<K:16/binary>> = hash(Ssh, "C", 128),
+ {ok, Ssh#ssh{decrypt_keys = K,
+ decrypt_block_size = 16,
+ decrypt_ctx = IV}};
+decrypt_init(#ssh{decrypt = 'AEAD_AES_256_GCM', role = client} = Ssh) ->
+ IV = hash(Ssh, "B", 12*8),
+ <<K:32/binary>> = hash(Ssh, "D", 256),
+ {ok, Ssh#ssh{decrypt_keys = K,
+ decrypt_block_size = 16,
+ decrypt_ctx = IV}};
+decrypt_init(#ssh{decrypt = 'AEAD_AES_256_GCM', role = server} = Ssh) ->
+ IV = hash(Ssh, "A", 12*8),
+ <<K:32/binary>> = hash(Ssh, "C", 256),
+ {ok, Ssh#ssh{decrypt_keys = K,
+ decrypt_block_size = 16,
+ decrypt_ctx = IV}};
decrypt_init(#ssh{decrypt = '3des-cbc', role = client} = Ssh) ->
{IV, KD} = {hash(Ssh, "B", 64),
hash(Ssh, "D", 192)},
@@ -1181,8 +1321,22 @@ decrypt_final(Ssh) ->
decrypt_ctx = undefined,
decrypt_block_size = 8}}.
+decrypt(Ssh, <<>>) ->
+ {Ssh, <<>>};
decrypt(#ssh{decrypt = none} = Ssh, Data) ->
{Ssh, Data};
+decrypt(#ssh{decrypt = 'AEAD_AES_128_GCM',
+ decrypt_keys = K,
+ decrypt_ctx = IV0} = Ssh, Data = {_AAD,_Ctext,_Ctag}) ->
+ Dec = crypto:block_decrypt(aes_gcm, K, IV0, Data), % Dec = PlainText | error
+ IV = next_gcm_iv(IV0),
+ {Ssh#ssh{decrypt_ctx = IV}, Dec};
+decrypt(#ssh{decrypt = 'AEAD_AES_256_GCM',
+ decrypt_keys = K,
+ decrypt_ctx = IV0} = Ssh, Data = {_AAD,_Ctext,_Ctag}) ->
+ Dec = crypto:block_decrypt(aes_gcm, K, IV0, Data), % Dec = PlainText | error
+ IV = next_gcm_iv(IV0),
+ {Ssh#ssh{decrypt_ctx = IV}, Dec};
decrypt(#ssh{decrypt = '3des-cbc', decrypt_keys = Keys,
decrypt_ctx = IV0} = Ssh, Data) ->
{K1, K2, K3} = Keys,
@@ -1207,6 +1361,10 @@ decrypt(#ssh{decrypt = 'aes256-ctr',
{State, Enc} = crypto:stream_decrypt(State0,Data),
{Ssh#ssh{decrypt_ctx = State}, Enc}.
+
+next_gcm_iv(<<Fixed:32, InvCtr:64>>) -> <<Fixed:32, (InvCtr+1):64>>.
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Compression
%%
@@ -1295,28 +1453,42 @@ decompress(#ssh{decompress = '[email protected]', decompress_ctx = Context, authe
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
send_mac_init(SSH) ->
- case SSH#ssh.role of
- client ->
- KeySize =mac_key_size(SSH#ssh.send_mac),
- Key = hash(SSH, "E", KeySize),
- {ok, SSH#ssh { send_mac_key = Key }};
- server ->
- KeySize = mac_key_size(SSH#ssh.send_mac),
- Key = hash(SSH, "F", KeySize),
- {ok, SSH#ssh { send_mac_key = Key }}
+ case pkt_type(SSH#ssh.send_mac) of
+ common ->
+ case SSH#ssh.role of
+ client ->
+ KeySize = mac_key_size(SSH#ssh.send_mac),
+ Key = hash(SSH, "E", KeySize),
+ {ok, SSH#ssh { send_mac_key = Key }};
+ server ->
+ KeySize = mac_key_size(SSH#ssh.send_mac),
+ Key = hash(SSH, "F", KeySize),
+ {ok, SSH#ssh { send_mac_key = Key }}
+ end;
+ aead ->
+ %% Not applicable
+ {ok, SSH}
end.
send_mac_final(SSH) ->
- {ok, SSH#ssh { send_mac = none, send_mac_key = undefined }}.
+ {ok, SSH#ssh {send_mac = none,
+ send_mac_key = undefined }}.
+
recv_mac_init(SSH) ->
- case SSH#ssh.role of
- client ->
- Key = hash(SSH, "F", mac_key_size(SSH#ssh.recv_mac)),
- {ok, SSH#ssh { recv_mac_key = Key }};
- server ->
- Key = hash(SSH, "E", mac_key_size(SSH#ssh.recv_mac)),
- {ok, SSH#ssh { recv_mac_key = Key }}
+ case pkt_type(SSH#ssh.recv_mac) of
+ common ->
+ case SSH#ssh.role of
+ client ->
+ Key = hash(SSH, "F", mac_key_size(SSH#ssh.recv_mac)),
+ {ok, SSH#ssh { recv_mac_key = Key }};
+ server ->
+ Key = hash(SSH, "E", mac_key_size(SSH#ssh.recv_mac)),
+ {ok, SSH#ssh { recv_mac_key = Key }}
+ end;
+ aead ->
+ %% Not applicable
+ {ok, SSH}
end.
recv_mac_final(SSH) ->
@@ -1447,6 +1619,8 @@ mac_digest_size('hmac-md5') -> 20;
mac_digest_size('hmac-md5-96') -> 12;
mac_digest_size('hmac-sha2-256') -> 32;
mac_digest_size('hmac-sha2-512') -> 64;
+mac_digest_size('AEAD_AES_128_GCM') -> 16;
+mac_digest_size('AEAD_AES_256_GCM') -> 16;
mac_digest_size(none) -> 0.
peer_name({Host, _}) ->
@@ -1476,6 +1650,68 @@ ecdh_curve('ecdh-sha2-nistp256') -> secp256r1;
ecdh_curve('ecdh-sha2-nistp384') -> secp384r1;
ecdh_curve('ecdh-sha2-nistp521') -> secp521r1.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Utils for default_algorithms/1 and supported_algorithms/1
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+supported_algorithms(Key, [{client2server,BL1},{server2client,BL2}]) ->
+ [{client2server,As1},{server2client,As2}] = supported_algorithms(Key),
+ [{client2server,As1--BL1},{server2client,As2--BL2}];
+supported_algorithms(Key, BlackList) ->
+ supported_algorithms(Key) -- BlackList.
+
+
+select_crypto_supported(L) ->
+ Sup = [{ec_curve,crypto_supported_curves()} | crypto:supports()],
+ [Name || {Name,CryptoRequires} <- L,
+ crypto_supported(CryptoRequires, Sup)].
+
+crypto_supported_curves() ->
+ try crypto:ec_curves()
+ catch _:_ -> []
+ end.
+
+crypto_supported(Conditions, Supported) ->
+ lists:all( fun({Tag,CryptoName}) when is_atom(CryptoName) ->
+ crypto_name_supported(Tag,CryptoName,Supported);
+ ({Tag,{Name,Len}}) when is_integer(Len) ->
+ crypto_name_supported(Tag,Name,Supported) andalso
+ len_supported(Name,Len)
+ end, Conditions).
+
+crypto_name_supported(Tag, CryptoName, Supported) ->
+ lists:member(CryptoName, proplists:get_value(Tag,Supported,[])).
+
+len_supported(Name, Len) ->
+ try
+ case Name of
+ aes_ctr ->
+ {_, <<_/binary>>} =
+ %% Test encryption
+ crypto:stream_encrypt(crypto:stream_init(Name, <<0:Len>>, <<0:128>>), <<"">>);
+ aes_gcm ->
+ {<<_/binary>>, <<_/binary>>} =
+ crypto:block_encrypt(Name,
+ _Key = <<0:Len>>,
+ _IV = <<0:12/unsigned-unit:8>>,
+ {<<"AAD">>,"PT"})
+ end
+ of
+ _ -> true
+ catch
+ _:_ -> false
+ end.
+
+
+same(Algs) -> [{client2server,Algs}, {server2client,Algs}].
+
+
+%% default_algorithms(kex) -> % Example of how to disable an algorithm
+%% supported_algorithms(kex, ['ecdh-sha2-nistp521']);
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Other utils
diff --git a/lib/ssh/test/ssh_renegotiate_SUITE.erl b/lib/ssh/test/ssh_renegotiate_SUITE.erl
index ef631d54bd..227dfcddcd 100644
--- a/lib/ssh/test/ssh_renegotiate_SUITE.erl
+++ b/lib/ssh/test/ssh_renegotiate_SUITE.erl
@@ -32,9 +32,15 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
-all() -> [rekey, rekey_limit, renegotiate1, renegotiate2].
+all() -> [{group,default_algs},
+ {group,aes_gcm}
+ ].
-groups() -> [].
+groups() -> [{default_algs, [], tests()},
+ {aes_gcm, [], tests()}
+ ].
+
+tests() -> [rekey, rekey_limit, renegotiate1, renegotiate2].
%%--------------------------------------------------------------------
init_per_suite(Config) ->
@@ -50,6 +56,18 @@ end_per_suite(_Config) ->
crypto:stop().
%%--------------------------------------------------------------------
+init_per_group(aes_gcm, Config) ->
+ [{preferred_algorithms, [{cipher,[{client2server,['[email protected]']},
+ {server2client,['[email protected]']}]}]}
+ | Config];
+init_per_group(_, Config) ->
+ [{preferred_algorithms, ssh:default_algorithms()} | Config].
+
+
+end_per_group(_, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
init_per_testcase(_TestCase, Config) ->
ssh:start(),
Config.
diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl
index 5816b708f2..424afc76fe 100644
--- a/lib/ssh/test/ssh_test_lib.erl
+++ b/lib/ssh/test/ssh_test_lib.erl
@@ -296,7 +296,7 @@ setup_dsa(DataDir, UserDir) ->
file:make_dir(System),
file:copy(filename:join(DataDir, "ssh_host_dsa_key"), filename:join(System, "ssh_host_dsa_key")),
file:copy(filename:join(DataDir, "ssh_host_dsa_key.pub"), filename:join(System, "ssh_host_dsa_key.pub")),
-ct:pal("DataDir ~p:~n ~p~n~nSystDir ~p:~n ~p~n~nUserDir ~p:~n ~p",[DataDir, file:list_dir(DataDir), System, file:list_dir(System), UserDir, file:list_dir(UserDir)]),
+ct:log("DataDir ~p:~n ~p~n~nSystDir ~p:~n ~p~n~nUserDir ~p:~n ~p",[DataDir, file:list_dir(DataDir), System, file:list_dir(System), UserDir, file:list_dir(UserDir)]),
setup_dsa_known_host(DataDir, UserDir),
setup_dsa_auth_keys(DataDir, UserDir).
@@ -306,7 +306,7 @@ setup_rsa(DataDir, UserDir) ->
file:make_dir(System),
file:copy(filename:join(DataDir, "ssh_host_rsa_key"), filename:join(System, "ssh_host_rsa_key")),
file:copy(filename:join(DataDir, "ssh_host_rsa_key.pub"), filename:join(System, "ssh_host_rsa_key.pub")),
-ct:pal("DataDir ~p:~n ~p~n~nSystDir ~p:~n ~p~n~nUserDir ~p:~n ~p",[DataDir, file:list_dir(DataDir), System, file:list_dir(System), UserDir, file:list_dir(UserDir)]),
+ct:log("DataDir ~p:~n ~p~n~nSystDir ~p:~n ~p~n~nUserDir ~p:~n ~p",[DataDir, file:list_dir(DataDir), System, file:list_dir(System), UserDir, file:list_dir(UserDir)]),
setup_rsa_known_host(DataDir, UserDir),
setup_rsa_auth_keys(DataDir, UserDir).
@@ -316,7 +316,7 @@ setup_ecdsa(Size, DataDir, UserDir) ->
file:make_dir(System),
file:copy(filename:join(DataDir, "ssh_host_ecdsa_key"++Size), filename:join(System, "ssh_host_ecdsa_key")),
file:copy(filename:join(DataDir, "ssh_host_ecdsa_key"++Size++".pub"), filename:join(System, "ssh_host_ecdsa_key.pub")),
-ct:pal("DataDir ~p:~n ~p~n~nSystDir ~p:~n ~p~n~nUserDir ~p:~n ~p",[DataDir, file:list_dir(DataDir), System, file:list_dir(System), UserDir, file:list_dir(UserDir)]),
+ct:log("DataDir ~p:~n ~p~n~nSystDir ~p:~n ~p~n~nUserDir ~p:~n ~p",[DataDir, file:list_dir(DataDir), System, file:list_dir(System), UserDir, file:list_dir(UserDir)]),
setup_ecdsa_known_host(Size, System, UserDir),
setup_ecdsa_auth_keys(Size, UserDir, UserDir).
@@ -502,7 +502,7 @@ default_algorithms(sshd, Host, Port) ->
{user_interaction, false}]}]))
catch
_C:_E ->
- ct:pal("***~p:~p: ~p:~p",[?MODULE,?LINE,_C,_E]),
+ ct:log("***~p:~p: ~p:~p",[?MODULE,?LINE,_C,_E]),
[]
end.
@@ -522,7 +522,7 @@ default_algorithms(sshc, DaemonOptions) ->
InitialState))
catch
_C:_E ->
- ct:pal("***~p:~p: ~p:~p",[?MODULE,?LINE,_C,_E]),
+ ct:log("***~p:~p: ~p:~p",[?MODULE,?LINE,_C,_E]),
[]
end}
end),
diff --git a/lib/ssl/src/inet_tls_dist.erl b/lib/ssl/src/inet_tls_dist.erl
index b6e62a18c9..404ae93d20 100644
--- a/lib/ssl/src/inet_tls_dist.erl
+++ b/lib/ssl/src/inet_tls_dist.erl
@@ -30,7 +30,7 @@
childspecs() ->
{ok, [{ssl_dist_sup,{ssl_dist_sup, start_link, []},
- permanent, 2000, worker, [ssl_dist_sup]}]}.
+ permanent, infinity, supervisor, [ssl_dist_sup]}]}.
select(Node) ->
case split_node(atom_to_list(Node), $@, []) of
diff --git a/lib/ssl/src/ssl_dist_sup.erl b/lib/ssl/src/ssl_dist_sup.erl
index aa1fa57db8..435ad27a44 100644
--- a/lib/ssl/src/ssl_dist_sup.erl
+++ b/lib/ssl/src/ssl_dist_sup.erl
@@ -70,7 +70,7 @@ connection_manager_child_spec() ->
Name = ssl_connection_dist,
StartFunc = {tls_connection_sup, start_link_dist, []},
Restart = permanent,
- Shutdown = 4000,
+ Shutdown = infinity,
Modules = [tls_connection_sup],
Type = supervisor,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index 3093508f61..a468c131ce 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -168,9 +168,10 @@ hello(start, #state{host = Host, port = Port, role = client,
Cache, CacheCb, Renegotiation, Cert),
Version = Hello#client_hello.client_version,
+ HelloVersion = tls_record:lowest_protocol_version(SslOpts#ssl_options.versions),
Handshake0 = ssl_handshake:init_handshake_history(),
{BinMsg, ConnectionStates, Handshake} =
- encode_handshake(Hello, Version, ConnectionStates0, Handshake0),
+ encode_handshake(Hello, HelloVersion, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
State1 = State0#state{connection_states = ConnectionStates,
negotiated_version = Version, %% Requested version
diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl
index aa524f0225..1e266ed424 100644
--- a/lib/ssl/src/tls_record.erl
+++ b/lib/ssl/src/tls_record.erl
@@ -41,8 +41,9 @@
-export([encode_plain_text/4]).
%% Protocol version handling
--export([protocol_version/1, lowest_protocol_version/2,
- highest_protocol_version/1, is_higher/2, supported_protocol_versions/0,
+-export([protocol_version/1, lowest_protocol_version/1, lowest_protocol_version/2,
+ highest_protocol_version/1, highest_protocol_version/2,
+ is_higher/2, supported_protocol_versions/0,
is_acceptable_version/1, is_acceptable_version/2]).
-export_type([tls_version/0, tls_atom_version/0]).
@@ -257,6 +258,18 @@ lowest_protocol_version(Version = {M,_},
Version;
lowest_protocol_version(_,Version) ->
Version.
+
+%%--------------------------------------------------------------------
+-spec lowest_protocol_version([tls_version()]) -> tls_version().
+%%
+%% Description: Lowest protocol version present in a list
+%%--------------------------------------------------------------------
+lowest_protocol_version([]) ->
+ lowest_protocol_version();
+lowest_protocol_version(Versions) ->
+ [Ver | Vers] = Versions,
+ lowest_list_protocol_version(Ver, Vers).
+
%%--------------------------------------------------------------------
-spec highest_protocol_version([tls_version()]) -> tls_version().
%%
@@ -266,19 +279,29 @@ highest_protocol_version([]) ->
highest_protocol_version();
highest_protocol_version(Versions) ->
[Ver | Vers] = Versions,
- highest_protocol_version(Ver, Vers).
+ highest_list_protocol_version(Ver, Vers).
-highest_protocol_version(Version, []) ->
+%%--------------------------------------------------------------------
+-spec highest_protocol_version(tls_version(), tls_version()) -> tls_version().
+%%
+%% Description: Highest protocol version of two given versions
+%%--------------------------------------------------------------------
+highest_protocol_version(Version = {M, N}, {M, O}) when N > O ->
+ Version;
+highest_protocol_version({M, _},
+ Version = {M, _}) ->
Version;
-highest_protocol_version(Version = {N, M}, [{N, O} | Rest]) when M > O ->
- highest_protocol_version(Version, Rest);
-highest_protocol_version({M, _}, [Version = {M, _} | Rest]) ->
- highest_protocol_version(Version, Rest);
-highest_protocol_version(Version = {M,_}, [{N,_} | Rest]) when M > N ->
- highest_protocol_version(Version, Rest);
-highest_protocol_version(_, [Version | Rest]) ->
- highest_protocol_version(Version, Rest).
+highest_protocol_version(Version = {M,_},
+ {N, _}) when M > N ->
+ Version;
+highest_protocol_version(_,Version) ->
+ Version.
+%%--------------------------------------------------------------------
+-spec is_higher(V1 :: tls_version(), V2::tls_version()) -> tls_version().
+%%
+%% Description: Is V1 > V2
+%%--------------------------------------------------------------------
is_higher({M, N}, {M, O}) when N > O ->
true;
is_higher({M, _}, {N, _}) when M > N ->
@@ -352,6 +375,17 @@ is_acceptable_version(_,_) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+
+lowest_list_protocol_version(Ver, []) ->
+ Ver;
+lowest_list_protocol_version(Ver1, [Ver2 | Rest]) ->
+ lowest_list_protocol_version(lowest_protocol_version(Ver1, Ver2), Rest).
+
+highest_list_protocol_version(Ver, []) ->
+ Ver;
+highest_list_protocol_version(Ver1, [Ver2 | Rest]) ->
+ highest_list_protocol_version(highest_protocol_version(Ver1, Ver2), Rest).
+
encode_tls_cipher_text(Type, {MajVer, MinVer}, Fragment) ->
Length = erlang:iolist_size(Fragment),
[<<?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer), ?UINT16(Length)>>, Fragment].
@@ -370,6 +404,10 @@ mac_hash({3, N} = Version, MacAlg, MacSecret, SeqNo, Type, Length, Fragment)
highest_protocol_version() ->
highest_protocol_version(supported_protocol_versions()).
+lowest_protocol_version() ->
+ lowest_protocol_version(supported_protocol_versions()).
+
+
sufficient_tlsv1_2_crypto_support() ->
CryptoSupport = crypto:supports(),
proplists:get_bool(sha256, proplists:get_value(hashs, CryptoSupport)).
diff --git a/lib/stdlib/doc/src/rand.xml b/lib/stdlib/doc/src/rand.xml
index e7d4728ef7..50057259c6 100644
--- a/lib/stdlib/doc/src/rand.xml
+++ b/lib/stdlib/doc/src/rand.xml
@@ -104,7 +104,7 @@
strong. If a strong cryptographic random number generator is
needed, use one of functions in the
<seealso marker="crypto:crypto">crypto</seealso>
- module, for example <c>crypto:rand_bytes/1</c>.</p></note>
+ module, for example <c>crypto:strong_rand_bytes/1</c>.</p></note>
</description>
<datatypes>
<datatype>
diff --git a/lib/stdlib/doc/src/random.xml b/lib/stdlib/doc/src/random.xml
index d3d7c90c31..fc4f796863 100644
--- a/lib/stdlib/doc/src/random.xml
+++ b/lib/stdlib/doc/src/random.xml
@@ -48,7 +48,7 @@
tuple of three integers.</p>
<p>It should be noted that this random number generator is not cryptographically
strong. If a strong cryptographic random number generator is needed for
- example <c>crypto:rand_bytes/1</c> could be used instead.</p>
+ example <c>crypto:strong_rand_bytes/1</c> could be used instead.</p>
<note><p>The new and improved <seealso
marker="stdlib:rand">rand</seealso> module should be used
instead of this module.</p></note>