@@ -13,6 +13,10 @@ import (
13
13
"time"
14
14
)
15
15
16
+ // ValidityPeriod is the time in hours after which a request is descarded
17
+ type ValidityPeriod * float64
18
+
19
+ // StringToTime converts from Unicod Epoch enconded timestamps to time.Time Go objects
16
20
func StringToTime (s string ) (time.Time , error ) {
17
21
sec , err := strconv .ParseInt (s , 10 , 64 )
18
22
if err != nil {
@@ -21,31 +25,53 @@ func StringToTime(s string) (time.Time, error) {
21
25
return time .Unix (sec , 0 ), nil
22
26
}
23
27
24
- func HMACSHA256 (message , signingKey []byte ) ([]byte , error ) {
25
- mac := hmac .New (sha256 .New , []byte (signingKey ))
28
+ // HMACSHA256 generates HMACS enconded hashes using the provided Key and SHA256
29
+ // encoding for the message
30
+ func HMACSHA256 (message , key []byte ) ([]byte , error ) {
31
+ mac := hmac .New (sha256 .New , []byte (key ))
26
32
if _ , err := mac .Write (message ); err != nil {
27
33
return nil , err
28
34
}
29
35
return mac .Sum (nil ), nil
30
36
}
31
37
38
+ // Validator type represents a MessageBird signature validator
32
39
type Validator struct {
33
- SigningKey string
34
- Period float64
40
+ SigningKey string // Signing Key provided by MessageBird
41
+ Period ValidityPeriod // Period in hours for a message to be accepted as real, set to nil to bypass the timestamp validator
35
42
Log * log.Logger
36
- LogMesssage string
43
+ LogMesssage * string
44
+ }
45
+
46
+ // NewValidator returns a signature validator object
47
+ func NewValidator (signingKey string , period float64 , log * log.Logger , message * string ) * Validator {
48
+ return & Validator {
49
+ SigningKey : signingKey ,
50
+ Period : & period ,
51
+ Log : log ,
52
+ LogMesssage : message ,
53
+ }
37
54
}
38
55
39
- func (v * Validator ) ValidTimestamp (ts time.Time ) bool {
40
- if v .Period != 0 {
56
+ // ValidTimestamp validates if the MessageBird-Request-Timestamp is a valid
57
+ // date and if the request is older than the validator Period.
58
+ func (v * Validator ) ValidTimestamp (ts string ) bool {
59
+ if v .Period != nil {
41
60
now := time .Now ()
42
- if diff := now .Sub (ts ); diff .Hours () > v .Period {
61
+ t , err := StringToTime (ts )
62
+ diff := now .Sub (t )
63
+ if err != nil || diff .Hours () > * v .Period {
43
64
return false
44
65
}
45
66
}
46
67
return true
47
68
}
48
69
70
+ // CalculateSignature calculates the MessageBird-Signature using HMAC_SHA_256
71
+ // encoding and the timestamp, query params and body from the request:
72
+ // signature = HMAC_SHA_256(
73
+ // TIMESTAMP + \n + QUERY_PARAMS + \n + SHA_256_SUM(BODY),
74
+ // signing_key)
49
75
func (v * Validator ) CalculateSignature (ts , qp string , b []byte ) ([]byte , error ) {
50
76
var m bytes.Buffer
51
77
bh := sha256 .Sum256 (b )
@@ -57,24 +83,38 @@ func (v *Validator) CalculateSignature(ts, qp string, b []byte) ([]byte, error)
57
83
return s , nil
58
84
}
59
85
86
+ // ValidSignature takes the timestamp, query params and body from the request,
87
+ // calculates the expected signature and compares it to the one sent by MessageBird.
60
88
func (v * Validator ) ValidSignature (ts , qp , rs string , b []byte ) bool {
61
89
es , _ := v .CalculateSignature (ts , qp , b )
62
90
drs , _ := base64 .StdEncoding .DecodeString (rs )
63
91
return hmac .Equal (drs , es )
64
92
}
65
93
66
- func (v * Validator ) Wrapper (h http.Handler ) http.Handler {
94
+ func (v * Validator ) Error (w http.ResponseWriter , r * http.Request ) {
95
+ if v .Log != nil {
96
+ v .Log .Println (v .LogMesssage , r .Host )
97
+ }
98
+ http .Error (w , "Request not allowed" , http .StatusUnauthorized )
99
+ return
100
+ }
101
+
102
+ // Validate is a handler wrapper that takes care of the signature validation of
103
+ // incoming requests and rejects them if invalid or pass them on to your handler
104
+ // otherwise.
105
+ // To use just wrappe your handler with it:
106
+ // http.Handle("/path", signature.Validate(handleThing))
107
+ func (v * Validator ) Validate (h http.Handler ) http.Handler {
67
108
return http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
68
109
ts := r .Header .Get ("MessageBird-Request-Timestamp" )
69
110
rs := r .Header .Get ("MessageBird-Request-Signature" )
70
111
if ts == "" || rs == "" {
71
- http .Error (w , "Request not allowed" , http . StatusUnauthorized )
112
+ v .Error (w , r )
72
113
return
73
114
}
74
- t , _ := StringToTime (ts )
75
115
b , _ := ioutil .ReadAll (r .Body )
76
- if v .ValidTimestamp (t ) == false || v .ValidSignature (ts , r .URL .RawQuery , rs , b ) == false {
77
- http .Error (w , "Request not allowed" , http . StatusUnauthorized )
116
+ if v .ValidTimestamp (ts ) == false || v .ValidSignature (ts , r .URL .RawQuery , rs , b ) == false {
117
+ v .Error (w , r )
78
118
return
79
119
}
80
120
h .ServeHTTP (w , r )
0 commit comments