@@ -397,7 +397,7 @@ new_keys_message(Ssh0) ->
397397 {ok , SshPacket , Ssh }.
398398
399399
400- handle_kexinit_msg (# ssh_msg_kexinit {} = CounterPart , # ssh_msg_kexinit {} = Own ,
400+ handle_kexinit_msg (# ssh_msg_kexinit {first_kex_packet_follows = CounterGuess } = CounterPart , # ssh_msg_kexinit {} = Own ,
401401 # ssh {role = client } = Ssh , ReNeg ) ->
402402 try
403403 {ok , Algorithms } =
@@ -407,16 +407,17 @@ handle_kexinit_msg(#ssh_msg_kexinit{} = CounterPart, #ssh_msg_kexinit{} = Own,
407407 Algorithms
408408 of
409409 Algos ->
410+ IsGuessWrong = is_guess_wrong (CounterGuess , CounterPart , Own ),
410411 key_exchange_first_msg (Algos # alg .kex ,
411- Ssh # ssh {algorithms = Algos })
412+ Ssh # ssh {algorithms = Algos , ignore_next_kex_message = IsGuessWrong })
412413 catch
413414 Class :Reason0 ->
414415 Reason = ssh_lib :trim_reason (Reason0 ),
415416 Msg = kexinit_error (Class , Reason , client , Own , CounterPart , Ssh ),
416417 ? DISCONNECT (? SSH_DISCONNECT_KEY_EXCHANGE_FAILED , Msg )
417418 end ;
418419
419- handle_kexinit_msg (# ssh_msg_kexinit {} = CounterPart , # ssh_msg_kexinit {} = Own ,
420+ handle_kexinit_msg (# ssh_msg_kexinit {first_kex_packet_follows = CounterGuess } = CounterPart , # ssh_msg_kexinit {} = Own ,
420421 # ssh {role = server } = Ssh , ReNeg ) ->
421422 try
422423 {ok , Algorithms } =
@@ -426,14 +427,44 @@ handle_kexinit_msg(#ssh_msg_kexinit{} = CounterPart, #ssh_msg_kexinit{} = Own,
426427 Algorithms
427428 of
428429 Algos ->
429- {ok , Ssh # ssh {algorithms = Algos }}
430+ IsGuessWrong = is_guess_wrong (CounterGuess , CounterPart , Own ),
431+ {ok , Ssh # ssh {algorithms = Algos , ignore_next_kex_message = IsGuessWrong }}
430432 catch
431433 Class :Reason0 ->
432434 Reason = ssh_lib :trim_reason (Reason0 ),
433435 Msg = kexinit_error (Class , Reason , server , Own , CounterPart , Ssh ),
434436 ? DISCONNECT (? SSH_DISCONNECT_KEY_EXCHANGE_FAILED , Msg )
435437 end .
436438
439+ % % RFC 4253 section 7 check if guess is wrong
440+ is_guess_wrong (false , _ , _ ) ->
441+ false ;
442+ is_guess_wrong (true , CounterPart , Own ) ->
443+ CounterPreferredKexAlgo = get_preferred_kex_algorithm (CounterPart ),
444+ OwnPreferredKexAlgo = get_preferred_kex_algorithm (Own ),
445+ CounterPreferredHostKeyAlgo = get_preferred_host_key_algorithm (CounterPart ),
446+ OwnPreferredHostKeyAlgo = get_preferred_host_key_algorithm (Own ),
447+
448+ is_different_algorithm (CounterPreferredKexAlgo , OwnPreferredKexAlgo ) orelse
449+ is_different_algorithm (CounterPreferredHostKeyAlgo , OwnPreferredHostKeyAlgo ).
450+
451+ is_different_algorithm (none , none ) ->
452+ false ;
453+ is_different_algorithm (Same , Same ) ->
454+ false ;
455+ is_different_algorithm (_ , _ ) ->
456+ true .
457+
458+ get_preferred_kex_algorithm (# ssh_msg_kexinit {kex_algorithms = [Preferred | _ ]}) ->
459+ Preferred ;
460+ get_preferred_kex_algorithm (_ ) ->
461+ none .
462+
463+ get_preferred_host_key_algorithm (# ssh_msg_kexinit {server_host_key_algorithms = [Preferred | _ ]}) ->
464+ Preferred ;
465+ get_preferred_host_key_algorithm (_ ) ->
466+ none .
467+
437468kexinit_error (Class , Error , Role , Own , CounterPart , Ssh ) ->
438469 {Fmt ,Args } =
439470 case {Class ,Error } of
@@ -2211,7 +2242,11 @@ parallell_gen_key(Ssh = #ssh{keyex_key = {x, {G, P}},
22112242generate_key (ecdh , Args ) ->
22122243 crypto :generate_key (ecdh , Args );
22132244generate_key (dh , [P ,G ,Sz2 ]) ->
2214- {Public ,Private } = crypto :generate_key (dh , [P , G , max (Sz2 ,? MIN_DH_KEY_SIZE )] ),
2245+ BitSize = fun (N ) -> bit_size (binary :encode_unsigned (N )) end ,
2246+ {Public ,Private } =
2247+ crypto :generate_key (dh ,
2248+ [P , G , max (min (BitSize (P )- 1 , Sz2 ),
2249+ ? MIN_DH_KEY_SIZE )]),
22152250 {crypto :bytes_to_integer (Public ), crypto :bytes_to_integer (Private )}.
22162251
22172252
0 commit comments