Files
br c8df9d7651 erste module drin.
zwischenstand: Architektur aktualisiert, TOFU implementiert,
2026-10-01 22:22:13 +02:00

140 lines
4.3 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package crypto
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/tls"
"crypto/x509"
"errors"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"
"time"
)
func TestLoadOrCreateServerCert_Creates(t *testing.T) {
dir := t.TempDir()
sc, err := LoadOrCreateServerCert(dir)
if err != nil {
t.Fatalf("LoadOrCreateServerCert: %v", err)
}
for name, wantPerm := range map[string]os.FileMode{serverKeyFile: 0o600, serverCertFile: 0o644} {
info, err := os.Stat(filepath.Join(dir, name))
if err != nil {
t.Fatalf("%s fehlt: %v", name, err)
}
if info.Mode().Perm() != wantPerm {
t.Errorf("%s hat %o, erwartet %o", name, info.Mode().Perm(), wantPerm)
}
}
leaf := sc.Leaf()
key, ok := leaf.PublicKey.(*ecdsa.PublicKey)
if !ok || key.Curve != elliptic.P256() {
t.Errorf("Schlüssel ist kein ECDSA P-256: %T", leaf.PublicKey)
}
// Selbstsigniert, aber bewusst keine CA (könnte sonst weitere
// Zertifikate ausstellen) – daher nicht CheckSignatureFrom.
if err := leaf.CheckSignature(leaf.SignatureAlgorithm, leaf.RawTBSCertificate, leaf.Signature); err != nil ||
leaf.Issuer.String() != leaf.Subject.String() {
t.Errorf("nicht selbstsigniert: %v", err)
}
if leaf.IsCA {
t.Error("Server-Zertifikat darf keine CA sein")
}
if len(leaf.ExtKeyUsage) != 1 || leaf.ExtKeyUsage[0] != x509.ExtKeyUsageServerAuth {
t.Errorf("ExtKeyUsage = %v, erwartet ServerAuth", leaf.ExtKeyUsage)
}
if leaf.NotAfter.Before(time.Now().AddDate(9, 0, 0)) {
t.Errorf("gültig nur bis %v – ein Ablauf würde alle Pins brechen", leaf.NotAfter)
}
if sc.Fingerprint != FingerprintOf(leaf) {
t.Error("Fingerprint passt nicht zum Zertifikat")
}
}
func TestLoadOrCreateServerCert_StableAcrossRestarts(t *testing.T) {
dir := t.TempDir()
first, err := LoadOrCreateServerCert(dir)
if err != nil {
t.Fatal(err)
}
second, err := LoadOrCreateServerCert(dir)
if err != nil {
t.Fatalf("erneutes Laden: %v", err)
}
if first.Fingerprint != second.Fingerprint {
t.Error("Fingerprint hat sich beim Neustart geändert – alle Agents würden die Verbindung verweigern")
}
}
func TestLoadOrCreateServerCert_Errors(t *testing.T) {
t.Run("nur Zertifikat vorhanden", func(t *testing.T) {
dir := t.TempDir()
LoadOrCreateServerCert(dir)
os.Remove(filepath.Join(dir, serverKeyFile))
if _, err := LoadOrCreateServerCert(dir); !errors.Is(err, ErrIncompleteCert) {
t.Errorf("erwartet ErrIncompleteCert, bekommen %v", err)
}
if _, err := os.Stat(filepath.Join(dir, serverKeyFile)); err == nil {
t.Error("fehlender Schlüssel wurde stillschweigend neu erzeugt")
}
})
t.Run("nur Schlüssel vorhanden", func(t *testing.T) {
dir := t.TempDir()
LoadOrCreateServerCert(dir)
os.Remove(filepath.Join(dir, serverCertFile))
if _, err := LoadOrCreateServerCert(dir); !errors.Is(err, ErrIncompleteCert) {
t.Errorf("erwartet ErrIncompleteCert, bekommen %v", err)
}
})
t.Run("Schlüssel zu offen", func(t *testing.T) {
dir := t.TempDir()
LoadOrCreateServerCert(dir)
os.Chmod(filepath.Join(dir, serverKeyFile), 0o644)
if _, err := LoadOrCreateServerCert(dir); !errors.Is(err, ErrInsecureKeyFile) {
t.Errorf("erwartet ErrInsecureKeyFile, bekommen %v", err)
}
})
t.Run("Schlüssel passt nicht zum Zertifikat", func(t *testing.T) {
a, b := t.TempDir(), t.TempDir()
LoadOrCreateServerCert(a)
LoadOrCreateServerCert(b)
data, _ := os.ReadFile(filepath.Join(b, serverCertFile))
os.WriteFile(filepath.Join(a, serverCertFile), data, 0o644)
if _, err := LoadOrCreateServerCert(a); err == nil {
t.Error("erwartet Fehler bei fremdem Zertifikat")
}
})
t.Run("Verzeichnis fehlt", func(t *testing.T) {
if _, err := LoadOrCreateServerCert(filepath.Join(t.TempDir(), "fehlt")); err == nil {
t.Error("erwartet Fehler")
}
})
}
func TestServerTLSConfig_OnlyTLS13(t *testing.T) {
sc, err := LoadOrCreateServerCert(t.TempDir())
if err != nil {
t.Fatal(err)
}
cfg := ServerTLSConfig(sc)
if cfg.MinVersion != tls.VersionTLS13 {
t.Errorf("MinVersion = %x, erwartet TLS 1.3", cfg.MinVersion)
}
srv := httptest.NewUnstartedServer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}))
srv.TLS = cfg
srv.StartTLS()
defer srv.Close()
old := &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{
InsecureSkipVerify: true, MaxVersion: tls.VersionTLS12,
}}}
if _, err := old.Get(srv.URL); err == nil {
t.Error("TLS-1.2-Client wurde akzeptiert")
}
}