Some checks failed
Build & Sign Wraith / Build Windows + Sign (push) Has been cancelled
Go + Wails v3 + Vue 3 + SQLite + FreeRDP3 (purego) 183 tests, 76 source files, 9,910 lines of code Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
90 lines
2.8 KiB
Go
90 lines
2.8 KiB
Go
package ssh
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
)
|
|
|
|
// HostKeyResult represents the result of a host key verification.
|
|
type HostKeyResult int
|
|
|
|
const (
|
|
HostKeyNew HostKeyResult = iota // never seen this host before
|
|
HostKeyMatch // fingerprint matches stored
|
|
HostKeyChanged // fingerprint CHANGED — possible MITM
|
|
)
|
|
|
|
// HostKeyStore stores and verifies SSH host key fingerprints in the host_keys SQLite table.
|
|
type HostKeyStore struct {
|
|
db *sql.DB
|
|
}
|
|
|
|
// NewHostKeyStore creates a new HostKeyStore backed by the given database.
|
|
func NewHostKeyStore(db *sql.DB) *HostKeyStore {
|
|
return &HostKeyStore{db: db}
|
|
}
|
|
|
|
// Verify checks whether the given fingerprint matches any stored host key for the
|
|
// specified hostname, port, and key type. It returns HostKeyNew if no key is stored,
|
|
// HostKeyMatch if the fingerprint matches, or HostKeyChanged if it differs.
|
|
func (s *HostKeyStore) Verify(hostname string, port int, keyType string, fingerprint string) (HostKeyResult, error) {
|
|
var storedFingerprint string
|
|
err := s.db.QueryRow(
|
|
"SELECT fingerprint FROM host_keys WHERE hostname = ? AND port = ? AND key_type = ?",
|
|
hostname, port, keyType,
|
|
).Scan(&storedFingerprint)
|
|
|
|
if err == sql.ErrNoRows {
|
|
return HostKeyNew, nil
|
|
}
|
|
if err != nil {
|
|
return 0, fmt.Errorf("query host key: %w", err)
|
|
}
|
|
|
|
if storedFingerprint == fingerprint {
|
|
return HostKeyMatch, nil
|
|
}
|
|
return HostKeyChanged, nil
|
|
}
|
|
|
|
// Store inserts or replaces a host key fingerprint for the given hostname, port, and key type.
|
|
func (s *HostKeyStore) Store(hostname string, port int, keyType string, fingerprint string, rawKey string) error {
|
|
_, err := s.db.Exec(
|
|
`INSERT INTO host_keys (hostname, port, key_type, fingerprint, raw_key)
|
|
VALUES (?, ?, ?, ?, ?)
|
|
ON CONFLICT (hostname, port, key_type)
|
|
DO UPDATE SET fingerprint = excluded.fingerprint, raw_key = excluded.raw_key`,
|
|
hostname, port, keyType, fingerprint, rawKey,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("store host key: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Delete removes all stored host keys for the given hostname and port.
|
|
func (s *HostKeyStore) Delete(hostname string, port int) error {
|
|
_, err := s.db.Exec(
|
|
"DELETE FROM host_keys WHERE hostname = ? AND port = ?",
|
|
hostname, port,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("delete host key: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetFingerprint returns the stored fingerprint for the given hostname and port.
|
|
// It returns an empty string and sql.ErrNoRows if no key is stored.
|
|
func (s *HostKeyStore) GetFingerprint(hostname string, port int) (string, error) {
|
|
var fingerprint string
|
|
err := s.db.QueryRow(
|
|
"SELECT fingerprint FROM host_keys WHERE hostname = ? AND port = ?",
|
|
hostname, port,
|
|
).Scan(&fingerprint)
|
|
if err != nil {
|
|
return "", fmt.Errorf("get fingerprint: %w", err)
|
|
}
|
|
return fingerprint, nil
|
|
}
|