-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathWebhook.go
More file actions
81 lines (70 loc) · 1.99 KB
/
Copy pathWebhook.go
File metadata and controls
81 lines (70 loc) · 1.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
package keymint
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"strconv"
"strings"
"time"
)
// VerifyWebhookSignature verifies a webhook payload signature received from Keymint.
// payload: The raw request body as string.
// header: The value of the "Keymint-Signature" header.
// secret: The webhook endpoint's signing secret.
// tolerance: Time tolerance duration (e.g. 5 * time.Minute) to prevent replay attacks. Set to 0 to use default (5 minutes).
// Returns nil if verification is successful, or an error if verification fails.
func VerifyWebhookSignature(payload string, header string, secret string, tolerance time.Duration) error {
if header == "" {
return fmt.Errorf("missing signature header")
}
if secret == "" {
return fmt.Errorf("missing signing secret")
}
// Parse header (e.g., t=1719374021,v1=signature)
var timestampStr string
var signature string
parts := strings.Split(header, ",")
for _, part := range parts {
kv := strings.SplitN(strings.TrimSpace(part), "=", 2)
if len(kv) == 2 {
switch kv[0] {
case "t":
timestampStr = kv[1]
case "v1":
signature = kv[1]
}
}
}
if timestampStr == "" || signature == "" {
return fmt.Errorf("invalid signature header format")
}
// Check timestamp validity
timestampInt, err := strconv.ParseInt(timestampStr, 10, 64)
if err != nil {
return fmt.Errorf("invalid timestamp format: %v", err)
}
if tolerance <= 0 {
tolerance = 5 * time.Minute
}
eventTime := time.Unix(timestampInt, 0)
diff := time.Since(eventTime)
if diff < 0 {
diff = -diff
}
if diff > tolerance {
return fmt.Errorf("timestamp is outside tolerance limit")
}
// Verify HMAC signature
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(timestampStr + "." + payload))
expectedMac := mac.Sum(nil)
sigBytes, err := hex.DecodeString(signature)
if err != nil {
return fmt.Errorf("invalid signature encoding")
}
if !hmac.Equal(sigBytes, expectedMac) {
return fmt.Errorf("signatures do not match")
}
return nil
}