package auth import ( "bytes" "crypto/ed25519" "errors" "io" "net/http" "strings" "testing" ) type ed25519PublicKey = ed25519.PublicKey func TestSign_SetsHeaders(t *testing.T) { priv, _ := GenerateKey() s := &Signer{Key: priv} r, _ := http.NewRequest(http.MethodGet, "https://ingress.example:8443/api/v1/stream", nil) if err := s.Sign(r); err != nil { t.Fatalf("Sign: %v", err) } if got := r.Header.Get(HeaderKey); got != EncodePublicKey(priv.Public().(ed25519.PublicKey)) { t.Errorf("%s = %q", HeaderKey, got) } for _, h := range []string{HeaderTimestamp, HeaderNonce, HeaderSignature} { if r.Header.Get(h) == "" { t.Errorf("Header %s fehlt", h) } } } func TestSign_FreshNonceEachTime(t *testing.T) { priv, _ := GenerateKey() s := &Signer{Key: priv} seen := map[string]bool{} for range 100 { r, _ := http.NewRequest(http.MethodGet, "https://h/x", nil) if err := s.Sign(r); err != nil { t.Fatal(err) } n := r.Header.Get(HeaderNonce) if seen[n] { t.Fatalf("Nonce %q doppelt", n) } seen[n] = true } } func TestSign_PreservesBody(t *testing.T) { priv, _ := GenerateKey() r, _ := http.NewRequest(http.MethodPost, "https://h/api/v1/state", strings.NewReader(`{"a":1}`)) if err := (&Signer{Key: priv}).Sign(r); err != nil { t.Fatal(err) } body, _ := io.ReadAll(r.Body) if string(body) != `{"a":1}` { t.Errorf("Body nach Sign = %q", body) } if r.GetBody == nil { t.Fatal("GetBody fehlt – Redirects/Retries könnten den Body nicht erneut senden") } again, _ := r.GetBody() body, _ = io.ReadAll(again) if !bytes.Equal(body, []byte(`{"a":1}`)) { t.Errorf("GetBody liefert %q", body) } } // Kein Signieren für Klartext-Verbindungen: Signatur und Body dürfen nie // unverschlüsselt über die Leitung gehen. func TestSign_RefusesPlainHTTP(t *testing.T) { priv, _ := GenerateKey() r, _ := http.NewRequest(http.MethodGet, "http://ingress.example:8080/api/v1/stream", nil) if err := (&Signer{Key: priv}).Sign(r); !errors.Is(err, ErrInsecureTransport) { t.Errorf("erwartet ErrInsecureTransport, bekommen %v", err) } if r.Header.Get(HeaderSignature) != "" { t.Error("Signatur trotz Klartext gesetzt") } } func TestSign_NoKey(t *testing.T) { r, _ := http.NewRequest(http.MethodGet, "https://h/x", nil) if err := (&Signer{}).Sign(r); !errors.Is(err, ErrInvalidKey) { t.Errorf("erwartet ErrInvalidKey, bekommen %v", err) } }