1package knotserver
2
3import (
4 "crypto/hmac"
5 "crypto/sha256"
6 "encoding/hex"
7 "net/http"
8 "time"
9)
10
11func (h *Handle) VerifySignature(next http.Handler) http.Handler {
12 if h.c.Server.Dev {
13 return next
14 }
15 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
16 signature := r.Header.Get("X-Signature")
17 if signature == "" || !h.verifyHMAC(signature, r) {
18 writeError(w, "signature verification failed", http.StatusForbidden)
19 return
20 }
21 next.ServeHTTP(w, r)
22 })
23}
24
25func (h *Handle) verifyHMAC(signature string, r *http.Request) bool {
26 secret := h.c.Server.Secret
27 timestamp := r.Header.Get("X-Timestamp")
28 if timestamp == "" {
29 return false
30 }
31
32 // Verify that the timestamp is not older than a minute
33 reqTime, err := time.Parse(time.RFC3339, timestamp)
34 if err != nil {
35 return false
36 }
37 if time.Since(reqTime) > time.Minute {
38 return false
39 }
40
41 message := r.Method + r.URL.Path + timestamp
42
43 mac := hmac.New(sha256.New, []byte(secret))
44 mac.Write([]byte(message))
45 expectedMAC := mac.Sum(nil)
46
47 signatureBytes, err := hex.DecodeString(signature)
48 if err != nil {
49 return false
50 }
51
52 return hmac.Equal(signatureBytes, expectedMAC)
53}