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) } }