erste module drin.

zwischenstand: Architektur aktualisiert, TOFU implementiert,
This commit is contained in:
br committed 2026-10-01 22:22:13 +02:00
1 parent 9d5789c5d7
commit c8df9d7651
41 files changed
+6455 -36

No files matched your search

+139
View File
@@ -0,0 +1,139 @@
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")
}
}