diff --git a/knownhosts.go b/knownhosts.go index 15d25fa..c2fb516 100644 --- a/knownhosts.go +++ b/knownhosts.go @@ -69,8 +69,19 @@ func (hkcb HostKeyCallback) HostKeys(hostWithPort string) (keys []ssh.PublicKey) // known_hosts entries (for different key types), the result will be sorted by // known_hosts filename and line number. func (hkcb HostKeyCallback) HostKeyAlgorithms(hostWithPort string) (algos []string) { - for _, key := range hkcb.HostKeys(hostWithPort) { - algos = append(algos, key.Type()) + // We ensure that algos never contains duplicates. This is done for robustness + // even though currently golang.org/x/crypto/ssh/knownhosts never exposes + // multiple keys of the same type. This way our behavior here is unaffected + // even if https://github.com/golang/go/issues/28870 is implemented, for + // example by https://github.com/golang/crypto/pull/254. + hostKeys := hkcb.HostKeys(hostWithPort) + seen := make(map[string]struct{}, len(hostKeys)) + for _, key := range hostKeys { + typ := key.Type() + if _, already := seen[typ]; !already { + algos = append(algos, typ) + seen[typ] = struct{}{} + } } return algos } diff --git a/knownhosts_test.go b/knownhosts_test.go index 329a81b..3a56510 100644 --- a/knownhosts_test.go +++ b/knownhosts_test.go @@ -248,7 +248,7 @@ func getTestKnownHosts(t *testing.T) string { // writeTestKnownHosts generates the test known_hosts file and returns the // file path to it. The generated file contains several hosts with a mix of -// key types; each known host has between 1 and 3 different known host keys. +// key types; each known host has between 1 and 4 different known host keys. // If generating or writing the file fails, the test fails. func writeTestKnownHosts(t *testing.T) string { t.Helper() @@ -256,7 +256,7 @@ func writeTestKnownHosts(t *testing.T) string { "only-rsa.example.test:22": {generatePubKeyRSA(t)}, "only-ecdsa.example.test:22": {generatePubKeyECDSA(t)}, "only-ed25519.example.test:22": {generatePubKeyEd25519(t)}, - "multi.example.test:2233": {generatePubKeyRSA(t), generatePubKeyECDSA(t), generatePubKeyEd25519(t)}, + "multi.example.test:2233": {generatePubKeyRSA(t), generatePubKeyECDSA(t), generatePubKeyEd25519(t), generatePubKeyEd25519(t)}, "192.168.1.102:2222": {generatePubKeyECDSA(t), generatePubKeyEd25519(t)}, "[fe80::abc:abc:abcd:abcd]:22": {generatePubKeyEd25519(t), generatePubKeyRSA(t)}, }