diff options
| author | Shulhan <m.shulhan@gmail.com> | 2026-03-27 06:10:04 +0700 |
|---|---|---|
| committer | Shulhan <m.shulhan@gmail.com> | 2026-03-27 06:10:04 +0700 |
| commit | e6291d1382827c5f725dc056babd75974c74253e (patch) | |
| tree | d6b3cf23abef7ab13fd39c4d516fdbc70cd3fe17 | |
| parent | 045799b6f22dffa15d613442ca89999d240579b7 (diff) | |
| download | go-x-crypto-e6291d1382827c5f725dc056babd75974c74253e.tar.xz | |
ssh/knownhosts: implements DB interface
There is an issue with current SSH client implementation.
Given a single host public key in the known_hosts file,
host ssh-ed25519 key...
Calling ssh.Dial(`tcp`, "host", ...) will return an error
knownhosts: key mismatch
from [handshakeTransport.enterKeyExchange], because only key
"mlkem768x25519-sha256" is checked on the client side.
This changes add DB interface for knownhosts that have two methods:
- HostKeyAlgorithms: return the host key that matches in known_hosts
based on the "host" name or address for
[ssh.ClientConfig.HostKeyAlgorithms].
- HostKeyCallback: return the ssh.HostKeyCallback for
[ssh.ClientConfig.HostKeyCallback].
Author: Faye Salwin
Reference: https://go-review.googlesource.com/c/crypto/+/154458
| -rw-r--r-- | ssh/knownhosts/db.go | 68 |
1 files changed, 68 insertions, 0 deletions
diff --git a/ssh/knownhosts/db.go b/ssh/knownhosts/db.go new file mode 100644 index 0000000..9d81617 --- /dev/null +++ b/ssh/knownhosts/db.go @@ -0,0 +1,68 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package knownhosts + +import ( + "fmt" + "net" + "os" + + "golang.org/x/crypto/ssh" +) + +type DB interface { + // HostKeyAlgorithms takes an address and returns a list of matching key types. + HostKeyAlgorithms(address string) ([]string, error) + + // HostKeyCallback is knownhosts.New without the DB initialization. + HostKeyCallback() ssh.HostKeyCallback +} + +// NewDB creates a new known_hosts database from the files given and returns +// it. +func NewDB(files ...string) (DB, error) { + logp := `NewDB` + db := newHostKeyDB() + for _, fn := range files { + f, err := os.Open(fn) + if err != nil { + return nil, fmt.Errorf(`%s: %w`, logp, err) + } + defer f.Close() + err = db.Read(f, fn) + if err != nil { + return nil, fmt.Errorf(`%s: %w`, logp, err) + } + } + return db, nil +} + +// HostKeyAlgorithms returns a list of host key algorithms associated +// with the given address. +func (db *hostKeyDB) HostKeyAlgorithms(address string) (knownTypes []string, err error) { + logp := `HostKeyAlgorithms` + host, port, err := net.SplitHostPort(address) + if err != nil { + return nil, fmt.Errorf(`%s: %w`, logp, err) + } + + hostToCheck := addr{host, port} + for _, l := range db.lines { + if l.match(hostToCheck) { + knownTypes = append(knownTypes, l.knownKey.Key.Type()) + } + } + return knownTypes, nil +} + +// HostKeyCallback is the way to get the ssh.HostKeyCallback if you have used +// NewDB. +func (db *hostKeyDB) HostKeyCallback() ssh.HostKeyCallback { + var certChecker ssh.CertChecker + certChecker.IsHostAuthority = db.IsHostAuthority + certChecker.IsRevoked = db.IsRevoked + certChecker.HostKeyFallback = db.check + return certChecker.CheckHostKey +} |
