{"record":{"id":"c153d2b98906ec7d","repo":"knadh/listmonk","slug":"error-asn1-unmarshal-of-signature-v","errorCode":null,"errorMessage":"error asn1 unmarshal of signature: %v","messagePattern":"error asn1 unmarshal of signature: (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/bounce/webhooks/sendgrid.go","lineNumber":102,"sourceCode":"\t}\n\n\treturn out, nil\n}\n\n// verifyNotif verifies the signature on a notification payload.\nfunc (s *Sendgrid) verifyNotif(sig, timestamp string, b []byte) error {\n\tsigB, err := base64.StdEncoding.DecodeString(sig)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tecdsaSig := struct {\n\t\tR *big.Int\n\t\tS *big.Int\n\t}{}\n\n\tif _, err := asn1.Unmarshal(sigB, &ecdsaSig); err != nil {\n\t\treturn fmt.Errorf(\"error asn1 unmarshal of signature: %v\", err)\n\t}\n\n\th := sha256.New()\n\th.Write([]byte(timestamp))\n\th.Write(b)\n\thash := h.Sum(nil)\n\n\tif !ecdsa.Verify(s.pubKey, hash, ecdsaSig.R, ecdsaSig.S) {\n\t\treturn errors.New(\"invalid signature\")\n\t}\n\n\treturn nil\n}\n","sourceCodeStart":84,"sourceCodeEnd":116,"githubUrl":"https://github.com/knadh/listmonk/blob/670c01717d48647093335cc23a6be6f4b79c3b6b/internal/bounce/webhooks/sendgrid.go#L84-L116","documentation":"SendGrid signs webhook payloads with ECDSA; the base64-decoded signature must be a DER/ASN.1-encoded ECDSA signature (R,S). verifyNotif asn1.Unmarshals the decoded bytes into an {R,S} struct, and returns 'error asn1 unmarshal of signature' when the bytes are not a valid ASN.1 ECDSA-Sig-Value structure.","triggerScenarios":"ProcessBounce receives an X-Webhook-Signature header whose base64-decoded bytes are not DER-encoded ECDSA (R,S): raw r||s concatenation, hex instead of base64, truncated signature, empty header, or a signature produced by a different scheme (e.g. HMAC).","commonSituations":"SendGrid webhook verification settings changed between signature schemes/versions; copying the verification key or signature incorrectly when testing; a proxy truncating the header; using the wrong SendGrid public key so the test signature was generated under a different format; SendGrid rotating their signing cert.","solutions":["Confirm SendGrid Event Webhook signature verification is enabled (ECDSA v1) so the header contains a base64 ASN.1 signature","Verify the signature header is passed through unmodified (base64, no truncation or added whitespace) by proxies","Regenerate/copy the exact signature from the actual request headers when testing manually","Ensure the ECDSA public key configured in NewSendgrid matches the one SendGrid currently signs with"],"exampleFix":"// before (test header with raw hex r||s)\nsig := hex.EncodeToString(sigRS)\n// after\nsig := base64.StdEncoding.EncodeToString(asn1EncodedEcdsaSig) // DER ECDSA-Sig-Value","handlingStrategy":"validation","validationCode":"func validEcdsaSigHeader(sig string) bool {\n    sigB, err := base64.StdEncoding.DecodeString(sig)\n    if err != nil { return false }\n    var ecdsaSig struct {\n        R *big.Int\n        S *big.Int\n    }\n    _, err = asn1.Unmarshal(sigB, &ecdsaSig)\n    return err == nil && ecdsaSig.R != nil && ecdsaSig.S != nil\n}\n// return 400 if !validEcdsaSigHeader(sigHeader)","typeGuard":null,"tryCatchPattern":"bounces, err := handler.ProcessBounce(sig, ts, body)\nif err != nil {\n    if strings.Contains(err.Error(), \"error asn1 unmarshal of signature\") {\n        log.Printf(\"bad ECDSA signature header (len=%d)\", len(sig))\n        http.Error(w, \"invalid signature\", http.StatusUnauthorized)\n        return\n    }\n    http.Error(w, \"webhook error\", http.StatusInternalServerError)\n}","preventionTips":["Ensure SendGrid signature verification (ECDSA) is enabled so signatures are ASN.1/DER, not raw r||s","Forward the signature header verbatim; base64 content must not be altered or truncated","Keep the SendGrid verification public key in sync with SendGrid's current signing cert","Never mix HMAC and ECDSA signature schemes in tests"],"tags":["webhook","signature-parsing","ecdsa","asn1","sendgrid","go"],"backgroundTag":"webhook-signature-invalid","analyzedSha":"670c01717d48647093335cc23a6be6f4b79c3b6b","analyzedAt":"2026-09-01T03:39:35.452Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}