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