From 67e156b0472b06a04fd5b1b8ab830efc22e4466d Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Tue, 30 Jun 2015 22:33:27 +0200 Subject: ssh: Repair/add experimental diffie-hellman-group-exchange-sha1 support DO NOT USE IN PRODUCTION!!! This is a bug fixing of the previously partly impelmented kex algorithm. There are more things to do, for example genrate/select better g,p pair obeying the min||n||max request. It is not enabled by default, but may be enabled with the option {preferred_algorithms, [{kex, ['diffie-hellman-group-exchange-sha1']}]} --- lib/ssh/src/ssh_connection_handler.erl | 42 ++++-- lib/ssh/src/ssh_message.erl | 17 ++- lib/ssh/src/ssh_transport.erl | 247 ++++++++++++++++++++++----------- 3 files changed, 210 insertions(+), 96 deletions(-) (limited to 'lib/ssh/src') diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl index a9c60d0674..c059834b27 100644 --- a/lib/ssh/src/ssh_connection_handler.erl +++ b/lib/ssh/src/ssh_connection_handler.erl @@ -46,7 +46,9 @@ get_print_info/1]). %% gen_fsm callbacks --export([hello/2, kexinit/2, key_exchange/2, new_keys/2, +-export([hello/2, kexinit/2, key_exchange/2, + key_exchange_dh_gex_init/2, key_exchange_dh_gex_reply/2, + new_keys/2, userauth/2, connected/2, error/2]). @@ -417,26 +419,38 @@ key_exchange(#ssh_msg_kexdh_reply{} = Msg, send_msg(NewKeys, State), {next_state, new_keys, next_packet(State#state{ssh_params = Ssh})}; -key_exchange(#ssh_msg_kex_dh_gex_group{} = Msg, +key_exchange(#ssh_msg_kex_dh_gex_request{} = Msg, #state{ssh_params = #ssh{role = server} = Ssh0} = State) -> - {ok, NextKexMsg, Ssh1} = ssh_transport:handle_kex_dh_gex_group(Msg, Ssh0), - send_msg(NextKexMsg, State), - {ok, NewKeys, Ssh} = ssh_transport:new_keys_message(Ssh1), - send_msg(NewKeys, State), - {next_state, new_keys, next_packet(State#state{ssh_params = Ssh})}; + {ok, GexGroup, Ssh} = ssh_transport:handle_kex_dh_gex_request(Msg, Ssh0), + send_msg(GexGroup, State), + {next_state, key_exchange_dh_gex_init, next_packet(State#state{ssh_params = Ssh})}; -key_exchange(#ssh_msg_kex_dh_gex_request{} = Msg, +key_exchange(#ssh_msg_kex_dh_gex_group{} = Msg, #state{ssh_params = #ssh{role = client} = Ssh0} = State) -> - {ok, NextKexMsg, Ssh} = ssh_transport:handle_kex_dh_gex_request(Msg, Ssh0), - send_msg(NextKexMsg, State), - {next_state, new_keys, next_packet(State#state{ssh_params = Ssh})}; + {ok, KexGexInit, Ssh} = ssh_transport:handle_kex_dh_gex_group(Msg, Ssh0), + send_msg(KexGexInit, State), + {next_state, key_exchange_dh_gex_reply, next_packet(State#state{ssh_params = Ssh})}. -key_exchange(#ssh_msg_kex_dh_gex_reply{} = Msg, - #state{ssh_params = #ssh{role = client} = Ssh0} = State) -> - {ok, NewKeys, Ssh} = ssh_transport:handle_kex_dh_gex_reply(Msg, Ssh0), +%%-------------------------------------------------------------------- +-spec key_exchange_dh_gex_init(#ssh_msg_kex_dh_gex_init{}, #state{}) -> gen_fsm_state_return(). +%%-------------------------------------------------------------------- +key_exchange_dh_gex_init(#ssh_msg_kex_dh_gex_init{} = Msg, + #state{ssh_params = #ssh{role = server} = Ssh0} = State) -> + {ok, KexGexReply, Ssh1} = ssh_transport:handle_kex_dh_gex_init(Msg, Ssh0), + send_msg(KexGexReply, State), + {ok, NewKeys, Ssh} = ssh_transport:new_keys_message(Ssh1), send_msg(NewKeys, State), {next_state, new_keys, next_packet(State#state{ssh_params = Ssh})}. +%%-------------------------------------------------------------------- +-spec key_exchange_dh_gex_reply(#ssh_msg_kex_dh_gex_reply{}, #state{}) -> gen_fsm_state_return(). +%%-------------------------------------------------------------------- +key_exchange_dh_gex_reply(#ssh_msg_kex_dh_gex_reply{} = Msg, + #state{ssh_params = #ssh{role = client} = Ssh0} = State) -> + {ok, NewKeys, Ssh1} = ssh_transport:handle_kex_dh_gex_reply(Msg, Ssh0), + send_msg(NewKeys, State), + {next_state, new_keys, next_packet(State#state{ssh_params = Ssh1})}. + %%-------------------------------------------------------------------- -spec new_keys(#ssh_msg_newkeys{}, #state{}) -> gen_fsm_state_return(). %%-------------------------------------------------------------------- diff --git a/lib/ssh/src/ssh_message.erl b/lib/ssh/src/ssh_message.erl index 1f0f6fb15f..7b786b8fff 100644 --- a/lib/ssh/src/ssh_message.erl +++ b/lib/ssh/src/ssh_message.erl @@ -237,7 +237,7 @@ encode(#ssh_msg_kex_dh_gex_request{ max = Max }) -> ssh_bits:encode([?SSH_MSG_KEX_DH_GEX_REQUEST, Min, N, Max], - [byte, uint32, uint32, uint32, uint32]); + [byte, uint32, uint32, uint32]); encode(#ssh_msg_kex_dh_gex_request_old{n = N}) -> ssh_bits:encode([?SSH_MSG_KEX_DH_GEX_REQUEST_OLD, N], [byte, uint32]); @@ -257,7 +257,7 @@ encode(#ssh_msg_kex_dh_gex_reply{ }) -> EncKey = encode_host_key(Key), EncSign = encode_sign(Key, Signature), - ssh_bits:encode([?SSH_MSG_KEXDH_REPLY, EncKey, F, EncSign], [byte, binary, mpint, binary]); + ssh_bits:encode([?SSH_MSG_KEX_DH_GEX_REPLY, EncKey, F, EncSign], [byte, binary, mpint, binary]); encode(#ssh_msg_ignore{data = Data}) -> ssh_bits:encode([?SSH_MSG_IGNORE, Data], [byte, string]); @@ -442,6 +442,19 @@ decode(<>) -> + #ssh_msg_kex_dh_gex_init{ + e = E + }; +decode(<>) -> + #ssh_msg_kex_dh_gex_reply{ + public_host_key = decode_host_key(Key), + f = F, + h_sig = decode_sign(Hashsign) + }; decode(<>) -> diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index 2e7391e1f8..ce0762bf96 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -39,7 +39,7 @@ key_exchange_init_msg/1, key_init/3, new_keys_message/1, handle_kexinit_msg/3, handle_kexdh_init/2, - handle_kex_dh_gex_group/2, handle_kex_dh_gex_reply/2, + handle_kex_dh_gex_group/2, handle_kex_dh_gex_init/2, handle_kex_dh_gex_reply/2, handle_new_keys/2, handle_kex_dh_gex_request/2, handle_kexdh_reply/2, unpack/3, decompress/2, ssh_packet/2, pack/2, msg_data/1, @@ -66,6 +66,8 @@ algo_classes() -> [kex, public_key, cipher, mac, compression]. default_algorithms(compression) -> %% Do not announce 'zlib@openssh.com' because there seem to be problems supported_algorithms(compression, same(['zlib@openssh.com'])); +default_algorithms(kex) -> + supported_algorithms(kex, ['diffie-hellman-group-exchange-sha1']); default_algorithms(Alg) -> supported_algorithms(Alg). @@ -73,7 +75,8 @@ default_algorithms(Alg) -> supported_algorithms() -> [{K,supported_algorithms(K)} || K <- algo_classes()]. supported_algorithms(kex) -> - ['diffie-hellman-group1-sha1']; + ['diffie-hellman-group1-sha1', + 'diffie-hellman-group-exchange-sha1']; supported_algorithms(public_key) -> ssh_auth:default_public_key_algorithms(); supported_algorithms(cipher) -> @@ -135,7 +138,7 @@ ssh_vsn() -> _:_ -> "" end. -random_id(Nlo, Nup) -> +random_id(Nlo, Nup) -> [crypto:rand_uniform($a,$z+1) || _<- lists:duplicate(crypto:rand_uniform(Nlo,Nup+1),x) ]. hello_version_msg(Data) -> @@ -144,7 +147,7 @@ hello_version_msg(Data) -> next_seqnum(SeqNum) -> (SeqNum + 1) band 16#ffffffff. -decrypt_first_block(Bin, #ssh{decrypt_block_size = BlockSize} = Ssh0) -> +decrypt_first_block(Bin, #ssh{decrypt_block_size = BlockSize} = Ssh0) -> <> = Bin, {Ssh, <> = DecData} = decrypt(Ssh0, EncBlock), @@ -282,9 +285,14 @@ verify_algorithm(#alg{kex = 'diffie-hellman-group1-sha1'}) -> true; verify_algorithm(#alg{kex = 'diffie-hellman-group-exchange-sha1'}) -> true; verify_algorithm(_) -> false. +%%%---------------------------------------------------------------- +%%% +%%% Key exchange initialization +%%% key_exchange_first_msg('diffie-hellman-group1-sha1', Ssh0) -> {G, P} = dh_group1(), {Private, Public} = dh_gen_key(G, P, 1024), + %% Public = G^Private mod P (def) {SshPacket, Ssh1} = ssh_packet(#ssh_msg_kexdh_init{e = Public}, Ssh0), {ok, SshPacket, Ssh1#ssh{keyex_key = {{Private, Public}, {G, P}}}}; @@ -295,13 +303,18 @@ key_exchange_first_msg('diffie-hellman-group-exchange-sha1', Ssh0) -> Max = ?DEFAULT_DH_GROUP_MAX, {SshPacket, Ssh1} = ssh_packet(#ssh_msg_kex_dh_gex_request{min = Min, - n = NBits, max = Max}, + n = NBits, + max = Max}, Ssh0), {ok, SshPacket, Ssh1#ssh{keyex_info = {Min, Max, NBits}}}. - +%%%---------------------------------------------------------------- +%%% +%%% diffie-hellman-group1-sha1 +%%% handle_kexdh_init(#ssh_msg_kexdh_init{e = E}, Ssh0) -> + %% server {G, P} = dh_group1(), if 1= @@ -314,101 +327,176 @@ handle_kexdh_init(#ssh_msg_kexdh_init{e = E}, Ssh0) -> f = Public, h_sig = H_SIG }, Ssh0), - {ok, SshPacket, Ssh1#ssh{keyex_key = {{Private, Public}, {G, P}}, shared_secret = K, exchanged_hash = H, session_id = sid(Ssh1, H)}}; + true -> - Error = {error,bad_e_from_peer}, - Disconnect = #ssh_msg_disconnect{ - code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, - description = "Key exchange failed, 'f' out of bounds", - language = "en"}, - throw({Error, Disconnect}) + throw({{error,bad_e_from_peer}, + #ssh_msg_disconnect{ + code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, + description = "Key exchange failed, 'e' out of bounds", + language = ""} + }) end. -handle_kex_dh_gex_group(#ssh_msg_kex_dh_gex_group{p = P, g = G}, Ssh0) -> - {Private, Public} = dh_gen_key(G,P,1024), - {SshPacket, Ssh1} = - ssh_packet(#ssh_msg_kex_dh_gex_init{e = Public}, Ssh0), - {ok, SshPacket, - Ssh1#ssh{keyex_key = {{Private, Public}, {G, P}}}}. +handle_kexdh_reply(#ssh_msg_kexdh_reply{public_host_key = HostKey, + f = F, + h_sig = H_SIG}, + #ssh{keyex_key = {{Private, Public}, {_G, P}}} = Ssh0) -> + %% client + if + 1= + K = ssh_math:ipow(F, Private, P), + H = kex_h(Ssh0, HostKey, Public, F, K), + + case verify_host_key(Ssh0, HostKey, H, H_SIG) of + ok -> + {SshPacket, Ssh} = ssh_packet(#ssh_msg_newkeys{}, Ssh0), + {ok, SshPacket, Ssh#ssh{shared_secret = K, + exchanged_hash = H, + session_id = sid(Ssh, H)}}; + Error -> + throw({Error, + #ssh_msg_disconnect{ + code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, + description = "Key exchange failed", + language = "en"} + }) + end; -handle_new_keys(#ssh_msg_newkeys{}, Ssh0) -> - try install_alg(Ssh0) of - #ssh{} = Ssh -> - {ok, Ssh} - catch - error:_Error -> %% TODO: Throw earlier .... - throw(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR, - description = "Install alg failed", - language = "en"}) - end. + true -> + throw({{error,bad_f_from_peer}, + #ssh_msg_disconnect{ + code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, + description = "Key exchange failed, 'f' out of bounds", + language = ""} + }) + end. -%% %% Select algorithms -handle_kexdh_reply(#ssh_msg_kexdh_reply{public_host_key = HostKey, f = F, - h_sig = H_SIG}, - #ssh{keyex_key = {{Private, Public}, {_G, P}}} = Ssh0) when 1= - K = ssh_math:ipow(F, Private, P), - H = kex_h(Ssh0, HostKey, Public, F, K), - - case verify_host_key(Ssh0, HostKey, H, H_SIG) of - ok -> - {SshPacket, Ssh} = ssh_packet(#ssh_msg_newkeys{}, Ssh0), - {ok, SshPacket, Ssh#ssh{shared_secret = K, - exchanged_hash = H, - session_id = sid(Ssh, H)}}; - Error -> - Disconnect = #ssh_msg_disconnect{ - code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, - description = "Key exchange failed", - language = "en"}, - throw({Error, Disconnect}) - end; -handle_kexdh_reply(#ssh_msg_kexdh_reply{}, _SSH) -> - Error = {error,bad_f_from_peer}, - Disconnect = #ssh_msg_disconnect{ - code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, - description = "Key exchange failed, 'f' out of bounds", - language = "en"}, - throw({Error, Disconnect}). - - -handle_kex_dh_gex_request(#ssh_msg_kex_dh_gex_request{min = _Min, - n = _NBits, - max = _Max}, Ssh0) -> +%%%---------------------------------------------------------------- +%%% +%%% diffie-hellman-group-exchange-sha1 +%%% +handle_kex_dh_gex_request(#ssh_msg_kex_dh_gex_request{min = Min, + n = NBits, + max = Max}, Ssh0) -> + %% server {G,P} = dh_group1(), %% TODO real imp this seems to be a hack?! {Private, Public} = dh_gen_key(G, P, 1024), {SshPacket, Ssh} = ssh_packet(#ssh_msg_kex_dh_gex_group{p = P, g = G}, Ssh0), {ok, SshPacket, - Ssh#ssh{keyex_key = {{Private, Public}, {G, P}}}}. + Ssh#ssh{keyex_key = {{Private, Public}, {G, P}}, + keyex_info = {Min, Max, NBits} + }}. + +handle_kex_dh_gex_group(#ssh_msg_kex_dh_gex_group{p = P, g = G}, Ssh0) -> + %% client + {Private, Public} = dh_gen_key(G, P, 1024), + {SshPacket, Ssh1} = + ssh_packet(#ssh_msg_kex_dh_gex_init{e = Public}, Ssh0), % Pub = G^Priv mod P (def) + + {ok, SshPacket, + Ssh1#ssh{keyex_key = {{Private, Public}, {G, P}}}}. + +handle_kex_dh_gex_init(#ssh_msg_kex_dh_gex_init{e = E}, + #ssh{keyex_key = {{Private, Public}, {G, P}}, + keyex_info = {Min, Max, NBits}} = + Ssh0) -> + %% server + if + 1= + K = ssh_math:ipow(E, Private, P), + if + 1 + HostKey = get_host_key(Ssh0), + H = kex_h(Ssh0, HostKey, Min, NBits, Max, P, G, E, Public, K), + H_SIG = sign_host_key(Ssh0, HostKey, H), + {SshPacket, Ssh} = + ssh_packet(#ssh_msg_kex_dh_gex_reply{public_host_key = HostKey, + f = Public, + h_sig = H_SIG}, Ssh0), + {ok, SshPacket, Ssh#ssh{shared_secret = K, + exchanged_hash = H, + session_id = sid(Ssh, H) + }}; + true -> + throw({{error,bad_K}, + #ssh_msg_disconnect{ + code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, + description = "Key exchange failed, 'K' out of bounds", + language = ""} + }) + end; + true -> + throw({{error,bad_e_from_peer}, + #ssh_msg_disconnect{ + code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, + description = "Key exchange failed, 'e' out of bounds", + language = ""} + }) + end. handle_kex_dh_gex_reply(#ssh_msg_kex_dh_gex_reply{public_host_key = HostKey, f = F, h_sig = H_SIG}, #ssh{keyex_key = {{Private, Public}, {G, P}}, keyex_info = {Min, Max, NBits}} = - Ssh0) -> - K = ssh_math:ipow(F, Private, P), - H = kex_h(Ssh0, HostKey, Min, NBits, Max, P, G, Public, F, K), - - case verify_host_key(Ssh0, HostKey, H, H_SIG) of - ok -> - {SshPacket, Ssh} = ssh_packet(#ssh_msg_newkeys{}, Ssh0), - {ok, SshPacket, Ssh#ssh{shared_secret = K, - exchanged_hash = H, - session_id = sid(Ssh, H)}}; - _Error -> - Disconnect = #ssh_msg_disconnect{ - code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, - description = "Key exchange failed", - language = "en"}, - throw(Disconnect) + Ssh0) -> + %% client + if + 1= + K = ssh_math:ipow(F, Private, P), + if + 1 + H = kex_h(Ssh0, HostKey, Min, NBits, Max, P, G, Public, F, K), + + case verify_host_key(Ssh0, HostKey, H, H_SIG) of + ok -> + {SshPacket, Ssh} = ssh_packet(#ssh_msg_newkeys{}, Ssh0), + {ok, SshPacket, Ssh#ssh{shared_secret = K, + exchanged_hash = H, + session_id = sid(Ssh, H)}}; + _Error -> + throw(#ssh_msg_disconnect{ + code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, + description = "Key exchange failed", + language = ""} + ) + end; + + true -> + throw({{error,bad_K}, + #ssh_msg_disconnect{ + code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, + description = "Key exchange failed, 'K' out of bounds", + language = ""} + }) + end; + true -> + throw({{error,bad_f_from_peer}, + #ssh_msg_disconnect{ + code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, + description = "Key exchange failed, 'f' out of bounds", + language = ""} + }) end. +%%%---------------------------------------------------------------- +handle_new_keys(#ssh_msg_newkeys{}, Ssh0) -> + try install_alg(Ssh0) of + #ssh{} = Ssh -> + {ok, Ssh} + catch + error:_Error -> %% TODO: Throw earlier .... + throw(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR, + description = "Install alg failed", + language = "en"}) + end. + %% select session id sid(#ssh{session_id = undefined}, H) -> H; @@ -511,7 +599,6 @@ select_algorithm(Role, Client, Server) -> decompress = Decompression, c_lng = C_Lng, s_lng = S_Lng}, -%%ct:pal("~p~n Client=~p~n Server=~p~n Alg=~p~n",[Role,Client,Server,Alg]), {ok, Alg}. select_encrypt_decrypt(client, Client, Server) -> @@ -1001,7 +1088,7 @@ recv_mac_init(SSH) -> recv_mac_final(SSH) -> {ok, SSH#ssh { recv_mac = none, recv_mac_key = undefined }}. -mac(none, _ , _, _) -> +mac(none, _ , _, _) -> <<>>; mac('hmac-sha1', Key, SeqNum, Data) -> crypto:hmac(sha, Key, [<>, Data]); -- cgit v1.2.3 From 2a9f5054f89ca02c1a35dcb96c3ad747663afd51 Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Thu, 2 Jul 2015 12:49:17 +0200 Subject: ssh: Add experimental 'diffie-hellman-group-exchange-sha256 support DO NOT USE IN PRODUCTION!!! It is not enabled by default, but may be enabled with the option {preferred_algorithms, [{kex, ['diffie-hellman-group-exchange-sha256']}]} --- lib/ssh/src/ssh_transport.erl | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'lib/ssh/src') diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index ce0762bf96..a0714ac97c 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -67,7 +67,10 @@ default_algorithms(compression) -> %% Do not announce 'zlib@openssh.com' because there seem to be problems supported_algorithms(compression, same(['zlib@openssh.com'])); default_algorithms(kex) -> - supported_algorithms(kex, ['diffie-hellman-group-exchange-sha1']); + %% Do not announce the experimental 'diffie-hellman-group-exchange-sha*' yet + supported_algorithms(kex, ['diffie-hellman-group-exchange-sha1', + 'diffie-hellman-group-exchange-sha256' + ]); default_algorithms(Alg) -> supported_algorithms(Alg). @@ -76,7 +79,9 @@ supported_algorithms() -> [{K,supported_algorithms(K)} || K <- algo_classes()]. supported_algorithms(kex) -> ['diffie-hellman-group1-sha1', - 'diffie-hellman-group-exchange-sha1']; + 'diffie-hellman-group-exchange-sha1', + 'diffie-hellman-group-exchange-sha256' + ]; supported_algorithms(public_key) -> ssh_auth:default_public_key_algorithms(); supported_algorithms(cipher) -> @@ -283,6 +288,7 @@ verify_algorithm(#alg{decompress = undefined}) -> false; verify_algorithm(#alg{kex = 'diffie-hellman-group1-sha1'}) -> true; verify_algorithm(#alg{kex = 'diffie-hellman-group-exchange-sha1'}) -> true; +verify_algorithm(#alg{kex = 'diffie-hellman-group-exchange-sha256'}) -> true; verify_algorithm(_) -> false. %%%---------------------------------------------------------------- @@ -297,7 +303,8 @@ key_exchange_first_msg('diffie-hellman-group1-sha1', Ssh0) -> {ok, SshPacket, Ssh1#ssh{keyex_key = {{Private, Public}, {G, P}}}}; -key_exchange_first_msg('diffie-hellman-group-exchange-sha1', Ssh0) -> +key_exchange_first_msg(Kex, Ssh0) when Kex == 'diffie-hellman-group-exchange-sha1' ; + Kex == 'diffie-hellman-group-exchange-sha256' -> Min = ?DEFAULT_DH_GROUP_MIN, NBits = ?DEFAULT_DH_GROUP_NBITS, Max = ?DEFAULT_DH_GROUP_MAX, @@ -1109,6 +1116,8 @@ hash(SSH, Char, Bits) -> fun(Data) -> crypto:hash(sha, Data) end; 'diffie-hellman-group-exchange-sha1' -> fun(Data) -> crypto:hash(sha, Data) end; + 'diffie-hellman-group-exchange-sha256' -> + fun(Data) -> crypto:hash(sha256, Data) end; _ -> exit({bad_algorithm,SSH#ssh.kex}) end, @@ -1158,8 +1167,11 @@ kex_h(SSH, Key, Min, NBits, Max, Prime, Gen, E, F, K) -> ssh_message:encode_host_key(Key), Min, NBits, Max, Prime, Gen, E,F,K], Ts) end, - crypto:hash(sha,L). + crypto:hash(sha((SSH#ssh.algorithms)#alg.kex), L). +sha('diffie-hellman-group-exchange-sha1') -> sha; +sha('diffie-hellman-group-exchange-sha256') -> sha256. + mac_key_size('hmac-sha1') -> 20*8; mac_key_size('hmac-sha1-96') -> 20*8; mac_key_size('hmac-md5') -> 16*8; -- cgit v1.2.3 From d964a7bfb9b87bf69f8a131aea517f4f0bfdfdd1 Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Fri, 3 Jul 2015 12:47:19 +0200 Subject: ssh: filter algs unknown to crypto (except pk) --- lib/ssh/src/ssh_transport.erl | 44 ++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) (limited to 'lib/ssh/src') diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index a0714ac97c..a0a0217fe2 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -78,26 +78,28 @@ default_algorithms(Alg) -> supported_algorithms() -> [{K,supported_algorithms(K)} || K <- algo_classes()]. supported_algorithms(kex) -> - ['diffie-hellman-group1-sha1', - 'diffie-hellman-group-exchange-sha1', - 'diffie-hellman-group-exchange-sha256' - ]; + select_crypto_supported( + [{'diffie-hellman-group1-sha1', [{hashs,sha}]}, + {'diffie-hellman-group-exchange-sha1', [{hashs,sha}]}, + {'diffie-hellman-group-exchange-sha256', [{hashs,sha256}]} + ]); supported_algorithms(public_key) -> ssh_auth:default_public_key_algorithms(); supported_algorithms(cipher) -> - Supports = crypto:supports(), - CipherAlgos = [{aes_ctr, 'aes128-ctr'}, {aes_cbc128, 'aes128-cbc'}, {des3_cbc, '3des-cbc'}], - Algs = [SshAlgo || - {CryptoAlgo, SshAlgo} <- CipherAlgos, - lists:member(CryptoAlgo, proplists:get_value(ciphers, Supports, []))], - same(Algs); + same( + select_crypto_supported( + [{'aes128-ctr', [{ciphers,aes_ctr}]}, + {'aes128-cbc', [{ciphers,aes_cbc128}]}, + {'3des-cbc', [{ciphers,des3_cbc}]} + ] + )); supported_algorithms(mac) -> - Supports = crypto:supports(), - HashAlgos = [{sha256, 'hmac-sha2-256'}, {sha, 'hmac-sha1'}], - Algs = [SshAlgo || - {CryptoAlgo, SshAlgo} <- HashAlgos, - lists:member(CryptoAlgo, proplists:get_value(hashs, Supports, []))], - same(Algs); + same( + select_crypto_supported( + [{'hmac-sha2-256', [{hashs,sha256}]}, + {'hmac-sha1', [{hashs,sha}]} + ] + )); supported_algorithms(compression) -> same(['none','zlib','zlib@openssh.com']). @@ -108,7 +110,15 @@ supported_algorithms(Key, [{client2server,BL1},{server2client,BL2}]) -> supported_algorithms(Key, BlackList) -> supported_algorithms(Key) -- BlackList. - +select_crypto_supported(L) -> + Sup = crypto:supports(), + [Name || {Name,CryptoRequires} <- L, + crypto_supported(CryptoRequires, Sup)]. + +crypto_supported(Conditions, Supported) -> + lists:all(fun({Tag,CryptoName}) -> + lists:member(CryptoName, proplists:get_value(Tag,Supported,[])) + end, Conditions). same(Algs) -> [{client2server,Algs}, {server2client,Algs}]. -- cgit v1.2.3 From ec0da994d1c223cc7b1fde18061464a6c44cdf0b Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Mon, 3 Aug 2015 16:50:53 +0200 Subject: ssh: rm ssh_math and use crypto:compute_key instead --- lib/ssh/src/Makefile | 1 - lib/ssh/src/ssh.app.src | 1 - lib/ssh/src/ssh_math.erl | 42 ------------------------------------------ lib/ssh/src/ssh_transport.erl | 15 ++++++++++----- 4 files changed, 10 insertions(+), 49 deletions(-) delete mode 100644 lib/ssh/src/ssh_math.erl (limited to 'lib/ssh/src') diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile index 61d71d2cf7..98fb90d7c4 100644 --- a/lib/ssh/src/Makefile +++ b/lib/ssh/src/Makefile @@ -67,7 +67,6 @@ MODULES= \ ssh_file \ ssh_io \ ssh_info \ - ssh_math \ ssh_message \ ssh_no_io \ ssh_sftp \ diff --git a/lib/ssh/src/ssh.app.src b/lib/ssh/src/ssh.app.src index bc01c539e0..4a76fd9cd3 100644 --- a/lib/ssh/src/ssh.app.src +++ b/lib/ssh/src/ssh.app.src @@ -24,7 +24,6 @@ ssh_file, ssh_io, ssh_info, - ssh_math, ssh_no_io, ssh_server_key_api, ssh_sftp, diff --git a/lib/ssh/src/ssh_math.erl b/lib/ssh/src/ssh_math.erl deleted file mode 100644 index cace85bc93..0000000000 --- a/lib/ssh/src/ssh_math.erl +++ /dev/null @@ -1,42 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2013. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% -%% - -%% - -%%% Description: SSH math utilities - --module(ssh_math). - --export([ipow/3]). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% -%% INTEGER utils -%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -%% calculate A^B mod M -ipow(A, B, M) when M > 0, B >= 0 -> - crypto:bytes_to_integer(crypto:mod_pow(A, B, M)). - - - - - diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index a0a0217fe2..74e25e1641 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -336,7 +336,7 @@ handle_kexdh_init(#ssh_msg_kexdh_init{e = E}, Ssh0) -> if 1= {Private, Public} = dh_gen_key(G, P, 1024), - K = ssh_math:ipow(E, Private, P), + K = dh_compute_key(G, P, E, Private), Key = get_host_key(Ssh0), H = kex_h(Ssh0, Key, E, Public, K), H_SIG = sign_host_key(Ssh0, Key, H), @@ -361,11 +361,11 @@ handle_kexdh_init(#ssh_msg_kexdh_init{e = E}, Ssh0) -> handle_kexdh_reply(#ssh_msg_kexdh_reply{public_host_key = HostKey, f = F, h_sig = H_SIG}, - #ssh{keyex_key = {{Private, Public}, {_G, P}}} = Ssh0) -> + #ssh{keyex_key = {{Private, Public}, {G, P}}} = Ssh0) -> %% client if 1= - K = ssh_math:ipow(F, Private, P), + K = dh_compute_key(G, P, F, Private), H = kex_h(Ssh0, HostKey, Public, F, K), case verify_host_key(Ssh0, HostKey, H, H_SIG) of @@ -426,7 +426,7 @@ handle_kex_dh_gex_init(#ssh_msg_kex_dh_gex_init{e = E}, %% server if 1= - K = ssh_math:ipow(E, Private, P), + K = dh_compute_key(G, P, E, Private), if 1 HostKey = get_host_key(Ssh0), @@ -466,7 +466,7 @@ handle_kex_dh_gex_reply(#ssh_msg_kex_dh_gex_reply{public_host_key = HostKey, %% client if 1= - K = ssh_math:ipow(F, Private, P), + K = dh_compute_key(G, P, F, Private), if 1 H = kex_h(Ssh0, HostKey, Min, NBits, Max, P, G, Public, F, K), @@ -1212,6 +1212,11 @@ dh_gen_key(G, P, _) -> {Public, Private} = crypto:generate_key(dh, [P, G]), {crypto:bytes_to_integer(Private), crypto:bytes_to_integer(Public)}. +dh_compute_key(G, P, OthersPublic, MyPrivate) -> + crypto:bytes_to_integer( + crypto:compute_key(dh, OthersPublic, MyPrivate, [P,G]) + ). + trim_tail(Str) -> lists:reverse(trim_head(lists:reverse(Str))). -- cgit v1.2.3 From 481eb2c445eeb350cd370aa4880e7c72c5173cfc Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Mon, 3 Aug 2015 18:08:41 +0200 Subject: ssh: added more groups for gex --- lib/ssh/src/ssh_transport.erl | 41 ++++++++++++++++++++++++++++++++++++--- lib/ssh/src/ssh_transport.hrl | 45 ------------------------------------------- 2 files changed, 38 insertions(+), 48 deletions(-) (limited to 'lib/ssh/src') diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index 74e25e1641..9be8e45aed 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -399,16 +399,23 @@ handle_kexdh_reply(#ssh_msg_kexdh_reply{public_host_key = HostKey, %%% handle_kex_dh_gex_request(#ssh_msg_kex_dh_gex_request{min = Min, n = NBits, - max = Max}, Ssh0) -> + max = Max}, Ssh0) when Min= %% server - {G,P} = dh_group1(), %% TODO real imp this seems to be a hack?! + {G, P} = dh_gex_group(Min, NBits, Max), {Private, Public} = dh_gen_key(G, P, 1024), {SshPacket, Ssh} = ssh_packet(#ssh_msg_kex_dh_gex_group{p = P, g = G}, Ssh0), {ok, SshPacket, Ssh#ssh{keyex_key = {{Private, Public}, {G, P}}, keyex_info = {Min, Max, NBits} - }}. + }}; +handle_kex_dh_gex_request(_, _) -> + throw({{error,bad_ssh_msg_kex_dh_gex_request}, + #ssh_msg_disconnect{ + code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, + description = "Key exchange failed, bad values in ssh_msg_kex_dh_gex_request", + language = ""} + }). handle_kex_dh_gex_group(#ssh_msg_kex_dh_gex_group{p = P, g = G}, Ssh0) -> %% client @@ -1205,9 +1212,35 @@ peer_name({Host, _}) -> %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% rfc 2489, ch 6.2 dh_group1() -> {2, 16#FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF}. +%%% rfc 3526, ch3 +dh_group14() -> + {2, 16#FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF}. + +%%% rfc 3526, ch4 +dh_group15() -> + {2, 16#FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF}. + +%%% rfc 3526, ch5 +dh_group16() -> + {2, 16#FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF}. + + +%%% First try exact match: +dh_gex_group(_Min, N, _Max) when N==1024 -> dh_group1(); +dh_gex_group(_Min, N, _Max) when N==2048 -> dh_group14(); +dh_gex_group(_Min, N, _Max) when N==3072 -> dh_group15(); +dh_gex_group(_Min, N, _Max) when N==4096 -> dh_group16(); +%%% If not an exact match, select the largest possible: +dh_gex_group(Min, _N, Max) when Min=<4096, 4096= dh_group16(); +dh_gex_group(Min, _N, Max) when Min=<3072, 3072= dh_group15(); +dh_gex_group(Min, _N, Max) when Min=<2048, 2048= dh_group14(); +dh_gex_group(Min, _N, Max) when Min=<1024, 1024= dh_group1(). + + dh_gen_key(G, P, _) -> {Public, Private} = crypto:generate_key(dh, [P, G]), {crypto:bytes_to_integer(Private), crypto:bytes_to_integer(Public)}. @@ -1217,6 +1250,8 @@ dh_compute_key(G, P, OthersPublic, MyPrivate) -> crypto:compute_key(dh, OthersPublic, MyPrivate, [P,G]) ). + + trim_tail(Str) -> lists:reverse(trim_head(lists:reverse(Str))). diff --git a/lib/ssh/src/ssh_transport.hrl b/lib/ssh/src/ssh_transport.hrl index 2faf8a9316..ab59742b96 100644 --- a/lib/ssh/src/ssh_transport.hrl +++ b/lib/ssh/src/ssh_transport.hrl @@ -188,49 +188,4 @@ -define(SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE, 14). -define(SSH_DISCONNECT_ILLEGAL_USER_NAME, 15). - -%%%---------------------------------------------------------------------- -%%% # DH_14_xxx -%%% Description: Oakley group 14 prime numbers and generator. Used in -%%% diffie-hellman-group1-sha1 key exchange method. -%%%---------------------------------------------------------------------- -%%%---------------------------------------------------------------------- -%%% # DH_14_P -%%% Description: Prime for this group -%%%---------------------------------------------------------------------- - --define(DH_14_P, - <<000,000,000,129,000,255,255,255,255,255,255,255,255,201,015,218, - 162,033,104,194,052,196,198,098,139,128,220,028,209,041,002,078, - 008,138,103,204,116,002,011,190,166,059,019,155,034,081,074,008, - 121,142,052,004,221,239,149,025,179,205,058,067,027,048,043,010, - 109,242,095,020,055,079,225,053,109,109,081,194,069,228,133,181, - 118,098,094,126,198,244,076,066,233,166,055,237,107,011,255,092, - 182,244,006,183,237,238,056,107,251,090,137,159,165,174,159,036, - 017,124,075,031,230,073,040,102,081,236,230,083,129,255,255,255, - 255,255,255,255,255>>). - -%%%---------------------------------------------------------------------- -%%% # DH_14_G -%%% Description: Generator for DH_14_P. -%%%---------------------------------------------------------------------- - --define(DH_14_G, <<0,0,0,1,2>>). - -%%%---------------------------------------------------------------------- -%%% # DH_14_Q -%%% Description: Group order (DH_14_P - 1) / 2. -%%%---------------------------------------------------------------------- - --define(DH_14_Q, - <<000,000,000,128,127,255,255,255,255,255,255,255,228,135,237,081, - 016,180,097,026,098,099,049,069,192,110,014,104,148,129,039,004, - 069,051,230,058,001,005,223,083,029,137,205,145,040,165,004,060, - 199,026,002,110,247,202,140,217,230,157,033,141,152,021,133,054, - 249,047,138,027,167,240,154,182,182,168,225,034,242,066,218,187, - 049,047,063,099,122,038,033,116,211,027,246,181,133,255,174,091, - 122,003,091,246,247,028,053,253,173,068,207,210,215,079,146,008, - 190,037,143,243,036,148,051,040,246,115,041,192,255,255,255,255, - 255,255,255,255>>). - -endif. % -ifdef(ssh_transport). -- cgit v1.2.3