Files
br ca6cdcca63 software steht so weit.
jetzt build + docker build und testen
2026-10-02 13:25:34 +02:00

93 lines
2.7 KiB
Go

package crypto
import (
"crypto/tls"
"errors"
"io"
"log"
"net/http"
"net/http/httptest"
"sync/atomic"
"testing"
)
// tlsServer startet einen Test-Server mit dem Zertifikat aus einem eigenen
// Datenverzeichnis und zählt, ob ein Request ihn erreicht.
func tlsServer(t *testing.T) (*httptest.Server, ServerCert, *atomic.Int32) {
t.Helper()
sc, err := LoadOrCreateServerCert(t.TempDir())
if err != nil {
t.Fatal(err)
}
var hits atomic.Int32
srv := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
hits.Add(1)
}))
srv.TLS = ServerTLSConfig(sc)
srv.Config.ErrorLog = log.New(io.Discard, "", 0) // abgewiesene Handshakes sind hier gewollt
srv.StartTLS()
t.Cleanup(srv.Close)
return srv, sc, &hits
}
func get(t *testing.T, url string, cfg *tls.Config) error {
t.Helper()
c := &http.Client{Transport: &http.Transport{TLSClientConfig: cfg}}
resp, err := c.Get(url)
if err == nil {
resp.Body.Close()
}
return err
}
func TestPinnedTLS_Matches(t *testing.T) {
srv, sc, hits := tlsServer(t)
if err := get(t, srv.URL, PinnedTLSConfig(sc.Fingerprint)); err != nil {
t.Fatalf("passender Fingerprint: %v", err)
}
if hits.Load() != 1 {
t.Error("Request kam nicht an")
}
}
// Der Pin hängt am Server, nicht an der Adresse: derselbe Server unter einer
// anderen Adresse (hier localhost statt 127.0.0.1) ist weiterhin vertrauenswürdig.
func TestPinnedTLS_AddressIndependent(t *testing.T) {
srv, sc, _ := tlsServer(t)
port := srv.Listener.Addr().(interface{ String() string }).String()
port = port[len("127.0.0.1"):]
if err := get(t, "https://localhost"+port, PinnedTLSConfig(sc.Fingerprint)); err != nil {
t.Errorf("andere Adresse, gleicher Server: %v", err)
}
}
// Angreifer mit eigenem Zertifikat: Abbruch im Handshake, kein Request
// (also auch kein Token, keine Signatur) erreicht ihn.
func TestPinnedTLS_Mismatch(t *testing.T) {
mitm, _, hits := tlsServer(t)
real, _ := LoadOrCreateServerCert(t.TempDir())
if err := get(t, mitm.URL, PinnedTLSConfig(real.Fingerprint)); !errors.Is(err, ErrFingerprintMismatch) {
t.Errorf("erwartet ErrFingerprintMismatch, bekommen %v", err)
}
if hits.Load() != 0 {
t.Error("Request hat den Angreifer erreicht")
}
}
// Kein blindes Vertrauen: ohne erwarteten Fingerprint keine Verbindung.
func TestPinnedTLS_NoFingerprint(t *testing.T) {
srv, _, hits := tlsServer(t)
if err := get(t, srv.URL, PinnedTLSConfig("")); !errors.Is(err, ErrUnknownServer) {
t.Errorf("erwartet ErrUnknownServer, bekommen %v", err)
}
if hits.Load() != 0 {
t.Error("Request erreichte den Server")
}
}
func TestPinnedTLSConfig_OnlyTLS13(t *testing.T) {
if cfg := PinnedTLSConfig("x"); cfg.MinVersion != tls.VersionTLS13 {
t.Errorf("MinVersion = %x", cfg.MinVersion)
}
}