feat: extend certificate pinning to all TLS protocols and add SSH
host-key pinning
This commit is contained in:
@@ -4,11 +4,14 @@
|
||||
package sftp
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
func TestHostKeyCallbackInsecure(t *testing.T) {
|
||||
cb, err := hostKeyCallback("", true)
|
||||
cb, err := hostKeyCallback("", true, "")
|
||||
if err != nil {
|
||||
t.Fatalf("hostKeyCallback: %v", err)
|
||||
}
|
||||
@@ -18,8 +21,61 @@ func TestHostKeyCallbackInsecure(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHostKeyCallbackMissingFile(t *testing.T) {
|
||||
_, err := hostKeyCallback("/nonexistent/known_hosts", false)
|
||||
_, err := hostKeyCallback("/nonexistent/known_hosts", false, "")
|
||||
if err == nil {
|
||||
t.Fatal("expected error for missing known_hosts")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHostKeyCallbackPinned(t *testing.T) {
|
||||
// Pinning takes precedence over insecure and known_hosts: the
|
||||
// callback is returned without touching the filesystem.
|
||||
cb, err := hostKeyCallback("/nonexistent/known_hosts", false, "SHA256:abcdefghijklmnopqrstuvwxyz0123456789+/=")
|
||||
if err != nil {
|
||||
t.Fatalf("hostKeyCallback with pin: %v", err)
|
||||
}
|
||||
if cb == nil {
|
||||
t.Fatal("expected callback for pinned host key")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizePinnedHostKey(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{"empty", "", ""},
|
||||
{"whitespace only", " ", ""},
|
||||
{"openssh form preserved", "SHA256:abc123", "SHA256:abc123"},
|
||||
{"hex converted to base64", "00", "SHA256:AA=="},
|
||||
{"invalid hex falls through", "nothex", "nothex"},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
if got := normalizePinnedHostKey(c.in); got != c.want {
|
||||
t.Errorf("normalizePinnedHostKey(%q) = %q, want %q", c.in, got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSSHFingerprintSHA256(t *testing.T) {
|
||||
// Use a fixed test key so the fingerprint is deterministic.
|
||||
// Generated with ssh-keygen -t ed25519 -f /tmp/test_key -N ""
|
||||
// and parsed here. We use ssh.ParseAuthorizedKey on a known-good
|
||||
// public key line.
|
||||
pubLine := "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE+HHi0MtRj4VPl8mdP8gniGZDRb0SZTdI2TPxRyCm1H test@example.com"
|
||||
key, _, _, _, err := ssh.ParseAuthorizedKey([]byte(pubLine))
|
||||
if err != nil {
|
||||
t.Fatalf("ParseAuthorizedKey: %v", err)
|
||||
}
|
||||
fp := sshFingerprintSHA256(key)
|
||||
if !strings.HasPrefix(fp, "SHA256:") {
|
||||
t.Errorf("fingerprint should start with SHA256:, got %q", fp)
|
||||
}
|
||||
// The fingerprint must be deterministic for the same key.
|
||||
if fp != sshFingerprintSHA256(key) {
|
||||
t.Error("fingerprint should be deterministic")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user