93 lines
2.7 KiB
Go
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)
|
|
}
|
|
}
|