rastrillo / abono Public

Clone
git clone https://amadan.net/rastrillo/abono

Plain git — no account needed to clone.

Download

Download this file

1package stripeapi
2
3import (
4 "crypto/hmac"
5 "crypto/sha256"
6 "encoding/hex"
7 "errors"
8 "strconv"
9 "strings"
10 "time"
11)
12
13// webhookTolerance is how old a signed delivery may be: Stripe's own
14// libraries' five minutes, which is what stops a captured one replaying.
15const webhookTolerance = 5 * time.Minute
16
17// ErrBadSignature is every way a delivery can fail the check: the header
18// is malformed, stale, or signed by none of the secrets.
19var ErrBadSignature = errors.New("stripeapi: webhook signature does not check")
20
21// VerifySignature checks a Stripe-Signature header ("t=…,v1=…[,v1=…]")
22// over body against any of secrets (more than one while rotating).
23func VerifySignature(header string, body []byte, secrets []string, now time.Time) error {
24 var ts string
25 var sigs [][]byte
26 for _, part := range strings.Split(header, ",") {
27 k, v, _ := strings.Cut(strings.TrimSpace(part), "=")
28 switch k {
29 case "t":
30 ts = v
31 case "v1":
32 if b, err := hex.DecodeString(v); err == nil {
33 sigs = append(sigs, b)
34 }
35 }
36 }
37 t, err := strconv.ParseInt(ts, 10, 64)
38 if err != nil || len(sigs) == 0 {
39 return ErrBadSignature
40 }
41 if d := now.Sub(time.Unix(t, 0)); d > webhookTolerance || d < -webhookTolerance {
42 return ErrBadSignature
43 }
44 for _, secret := range secrets {
45 mac := hmac.New(sha256.New, []byte(secret))
46 mac.Write([]byte(ts))
47 mac.Write([]byte("."))
48 mac.Write(body)
49 want := mac.Sum(nil)
50 for _, s := range sigs {
51 if hmac.Equal(s, want) {
52 return nil
53 }
54 }
55 }
56 return ErrBadSignature
57}
58