3232-include (" ssh_auth.hrl" ).
3333-include (" ssh_test_lib.hrl" ).
3434
35+ % % RFC 3526 Group 14 — 2048-bit MODP prime (generator = 2).
36+ -define (RFC3526_GROUP14_PRIME ,
37+ 16#FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF ).
38+
3539-export ([
3640 suite /0 ,
3741 all /0 ,
8286 gex_client_init_option_groups_moduli_file /1 ,
8387 gex_client_old_request_exact /1 ,
8488 gex_client_old_request_noexact /1 ,
89+ gex_client_rejects_small_group /1 ,
90+ gex_client_rejects_bad_generator /1 ,
8591 gex_server_gex_limit /1 ,
8692 lib_match /1 ,
8793 lib_no_match /1 ,
@@ -209,6 +215,8 @@ groups() ->
209215 gex_client_init_option_groups_file ,
210216 gex_client_old_request_exact ,
211217 gex_client_old_request_noexact ,
218+ gex_client_rejects_small_group ,
219+ gex_client_rejects_bad_generator ,
212220 kex_strict_negotiated ,
213221 kex_strict_violation_key_exchange ,
214222 kex_strict_violation_new_keys ,
@@ -301,6 +309,9 @@ init_per_testcase(TC, Config) when TC == kex_strict_negotiated;
301309 Level = ssh_test_lib :get_log_level (),
302310 ssh_test_lib :set_log_level (debug ),
303311 [{saved_log_level , Level } | Config ];
312+ init_per_testcase (TC , Config ) when TC == gex_client_rejects_small_group ;
313+ TC == gex_client_rejects_bad_generator ->
314+ Config ;
304315init_per_testcase (TC , Config ) when TC == gex_client_init_option_groups ;
305316 TC == gex_client_init_option_groups_moduli_file ;
306317 TC == gex_client_init_option_groups_file ;
@@ -309,10 +320,8 @@ init_per_testcase(TC, Config) when TC == gex_client_init_option_groups ;
309320 TC == gex_client_old_request_noexact ->
310321 Opts = case TC of
311322 gex_client_init_option_groups ->
312- [{dh_gex_groups ,
313- [{1023 , 5 ,
314- 16#D9277DAA27DB131C03B108D41A76B4DA8ACEECCCAE73D2E48CEDAAA70B09EF9F04FB020DCF36C51B8E485B26FABE0337E24232BE4F4E693548310244937433FB1A5758195DC73B84ADEF8237472C46747D79DC0A2CF8A57CE8DBD8F466A20F8551E7B1B824B2E4987A8816D9BC0741C2798F3EBAD3ADEBCC78FCE6A770E2EC9F
315- }]}];
323+ [{dh_gex_groups ,
324+ [{2048 , 2 , ? RFC3526_GROUP14_PRIME }]}];
316325 gex_client_init_option_groups_file ->
317326 DataDir = proplists :get_value (data_dir , Config ),
318327 F = filename :join (DataDir , " dh_group_test" ),
@@ -663,9 +672,8 @@ no_common_alg_client_disconnects(Config) ->
663672
664673% %%--------------------------------------------------------------------
665674gex_client_init_option_groups (Config ) ->
666- do_gex_client_init (Config , {512 , 2048 , 4000 },
667- {5 ,16#D9277DAA27DB131C03B108D41A76B4DA8ACEECCCAE73D2E48CEDAAA70B09EF9F04FB020DCF36C51B8E485B26FABE0337E24232BE4F4E693548310244937433FB1A5758195DC73B84ADEF8237472C46747D79DC0A2CF8A57CE8DBD8F466A20F8551E7B1B824B2E4987A8816D9BC0741C2798F3EBAD3ADEBCC78FCE6A770E2EC9F }
668- ).
675+ do_gex_client_init (Config , {2048 , 2048 , 4000 },
676+ {2 , ? RFC3526_GROUP14_PRIME }).
669677
670678gex_client_init_option_groups_file (Config ) ->
671679 do_gex_client_init (Config , {2000 , 2048 , 4000 },
@@ -742,6 +750,67 @@ do_gex_client_init_old(Config, N, {G,P}) ->
742750 ]
743751 ).
744752
753+ % %%--------------------------------------------------------------------
754+ % %% Client rejects a DH GEX group with too-small prime (512 bits).
755+ % %% The test lib acts as server and sends a bad GEX_GROUP; the real
756+ % %% OTP client must disconnect with KEY_EXCHANGE_FAILED.
757+ gex_client_rejects_small_group (Config ) ->
758+ % % 512-bit number (not even prime — doesn't matter, size check rejects first)
759+ SmallP = 16#D4BCD52406F2C926B7E8BE5FF5D2B2E3B956F79441CE5B2E35 ,
760+ gex_client_rejects_group (Config , SmallP , 2 ).
761+
762+ % %% Client rejects a DH GEX group with invalid generator (G=1).
763+ gex_client_rejects_bad_generator (Config ) ->
764+ % % Valid 2048-bit prime (RFC 3526 group 14), but generator = 1
765+ gex_client_rejects_group (Config , ? RFC3526_GROUP14_PRIME , 1 ).
766+
767+ gex_client_rejects_group (Config , P , G ) ->
768+ {ok , InitialState } = ssh_trpt_test_lib :exec (listen ),
769+ HostPort = ssh_trpt_test_lib :server_host_port (InitialState ),
770+ Parent = self (),
771+
772+ % % Server side: accept, negotiate DH-GEX, send bad group
773+ Pid =
774+ spawn_link (
775+ fun () ->
776+ Parent !
777+ {result , self (),
778+ ssh_trpt_test_lib :exec (
779+ [{set_options , [print_ops , print_seqnums , print_messages ]},
780+ {accept , [{system_dir , ssh_test_lib :system_dir (Config )},
781+ {user_dir , ssh_test_lib :user_dir (Config )}]},
782+ receive_hello ,
783+ {send , hello },
784+ {send , ssh_msg_kexinit },
785+ {match , # ssh_msg_kexinit {_ = '_' }, receive_msg },
786+ {match , # ssh_msg_kex_dh_gex_request {_ = '_' }, receive_msg },
787+ {send , # ssh_msg_kex_dh_gex_group {p = P , g = G }},
788+ {match , disconnect (? SSH_DISCONNECT_KEY_EXCHANGE_FAILED ),
789+ receive_msg }
790+ ],
791+ InitialState )}
792+ end ),
793+
794+ % % Client side: connect forcing DH-GEX
795+ Result = std_connect (HostPort , Config ,
796+ [{preferred_algorithms ,
797+ [{kex , ['diffie-hellman-group-exchange-sha256' ]},
798+ {cipher , ? DEFAULT_CIPHERS }]}]),
799+ ct :log (" Client connect result: ~p " , [Result ]),
800+
801+ receive
802+ {result , Pid , {ok , _ }} ->
803+ ok ;
804+ {result , Pid , {error , {Op , ExecResult , S }}} ->
805+ ct :log (" ERROR!~n Op = ~p~n ExecResult = ~p~n State =~n~s " ,
806+ [Op , ExecResult , ssh_trpt_test_lib :format_msg (S )]),
807+ {fail , ExecResult };
808+ {result , Pid , X } ->
809+ ct :fail (X )
810+ after
811+ 30000 -> ct :fail (" timeout ~p :~p " , [? MODULE , ? LINE ])
812+ end .
813+
745814% %%--------------------------------------------------------------------
746815bad_service_name (Config ) ->
747816 bad_service_name (Config , " kfglkjf" ).
0 commit comments