@@ -491,3 +491,109 @@ func TestSSHConfig_ValidKeyParsing(t *testing.T) {
491491 assert .Equal (t , "testuser" , config .User )
492492 assert .NotEmpty (t , config .Auth )
493493}
494+
495+ func TestWithHostKey_SetsExpectedHostKey (t * testing.T ) {
496+ t .Parallel ()
497+
498+ _ , pubKey , err := GenerateHostKeyPair ()
499+ require .NoError (t , err )
500+
501+ c := NewClient ("127.0.0.1" , 22 , "user" , "/tmp/key" , WithHostKey (pubKey ))
502+ assert .NotNil (t , c .expectedHostKey )
503+ assert .Equal (t , pubKey .Marshal (), c .expectedHostKey .Marshal ())
504+ }
505+
506+ func TestWithHostKey_NilFallback (t * testing.T ) {
507+ t .Parallel ()
508+
509+ // No options → expectedHostKey should be nil.
510+ c := NewClient ("127.0.0.1" , 22 , "user" , "/tmp/key" )
511+ assert .Nil (t , c .expectedHostKey )
512+ }
513+
514+ func TestSSHConfig_WithHostKey_AcceptsMatchingKey (t * testing.T ) {
515+ t .Parallel ()
516+
517+ keyDir := t .TempDir ()
518+ privKeyPath , _ , err := GenerateKeyPair (keyDir )
519+ require .NoError (t , err )
520+
521+ _ , hostPubKey , err := GenerateHostKeyPair ()
522+ require .NoError (t , err )
523+
524+ c := & Client {
525+ host : "testhost" ,
526+ port : 2222 ,
527+ user : "testuser" ,
528+ keyPath : privKeyPath ,
529+ readFile : os .ReadFile ,
530+ expectedHostKey : hostPubKey ,
531+ }
532+
533+ config , err := c .sshConfig ()
534+ require .NoError (t , err )
535+ require .NotNil (t , config .HostKeyCallback )
536+
537+ // Matching key should be accepted.
538+ err = config .HostKeyCallback ("testhost:2222" , nil , hostPubKey )
539+ assert .NoError (t , err , "matching host key should be accepted" )
540+ }
541+
542+ func TestSSHConfig_WithHostKey_RejectsMismatchedKey (t * testing.T ) {
543+ t .Parallel ()
544+
545+ keyDir := t .TempDir ()
546+ privKeyPath , _ , err := GenerateKeyPair (keyDir )
547+ require .NoError (t , err )
548+
549+ _ , hostPubKey , err := GenerateHostKeyPair ()
550+ require .NoError (t , err )
551+
552+ // Generate a different key to simulate an impersonator.
553+ _ , wrongPubKey , err := GenerateHostKeyPair ()
554+ require .NoError (t , err )
555+
556+ c := & Client {
557+ host : "testhost" ,
558+ port : 2222 ,
559+ user : "testuser" ,
560+ keyPath : privKeyPath ,
561+ readFile : os .ReadFile ,
562+ expectedHostKey : hostPubKey ,
563+ }
564+
565+ config , err := c .sshConfig ()
566+ require .NoError (t , err )
567+ require .NotNil (t , config .HostKeyCallback )
568+
569+ // Mismatched key should be rejected.
570+ err = config .HostKeyCallback ("testhost:2222" , nil , wrongPubKey )
571+ assert .Error (t , err , "mismatched host key should be rejected" )
572+ }
573+
574+ func TestSSHConfig_WithoutHostKey_AcceptsAnyKey (t * testing.T ) {
575+ t .Parallel ()
576+
577+ keyDir := t .TempDir ()
578+ privKeyPath , _ , err := GenerateKeyPair (keyDir )
579+ require .NoError (t , err )
580+
581+ c := & Client {
582+ host : "testhost" ,
583+ port : 2222 ,
584+ user : "testuser" ,
585+ keyPath : privKeyPath ,
586+ readFile : os .ReadFile ,
587+ }
588+
589+ config , err := c .sshConfig ()
590+ require .NoError (t , err )
591+ require .NotNil (t , config .HostKeyCallback )
592+
593+ // Without host key pinning, any key should be accepted.
594+ _ , anyPubKey , err := GenerateHostKeyPair ()
595+ require .NoError (t , err )
596+
597+ err = config .HostKeyCallback ("testhost:2222" , nil , anyPubKey )
598+ assert .NoError (t , err , "insecure callback should accept any key" )
599+ }
0 commit comments