package main import ( "crypto/hmac" "crypto/sha1" "crypto/sha256" "encoding/hex" "errors" "fmt" "hash" "net/http" "strings" ) type Verifier interface { Verify(body []byte, headers http.Header, secret string, signatureHeader string) error } func NewVerifier(method string) (Verifier, error) { switch method { case "hmac-sha256": return &hmacVerifier{prefix: "sha256=", newHash: sha256.New}, nil case "hmac-sha1": return &hmacVerifier{prefix: "sha1=", newHash: sha1.New}, nil default: return nil, fmt.Errorf("unknown verification method: %s", method) } } type hmacVerifier struct { prefix string newHash func() hash.Hash } func (v *hmacVerifier) Verify(body []byte, headers http.Header, secret string, signatureHeader string) error { sig := headers.Get(signatureHeader) if sig == "" { return errors.New("missing signature header") } sigHex, ok := strings.CutPrefix(sig, v.prefix) if !ok { return fmt.Errorf("signature missing expected prefix %q", v.prefix) } sigBytes, err := hex.DecodeString(sigHex) if err != nil { return fmt.Errorf("invalid hex in signature: %w", err) } mac := hmac.New(v.newHash, []byte(secret)) mac.Write(body) expected := mac.Sum(nil) if !hmac.Equal(sigBytes, expected) { return errors.New("signature mismatch") } return nil }