@@ -12,6 +12,7 @@ import (
12
12
"encoding/json"
13
13
"errors"
14
14
"fmt"
15
+ "hash"
15
16
"io/ioutil"
16
17
"log"
17
18
"math"
@@ -48,13 +49,19 @@ const (
48
49
49
50
// SignatureError describes an invalid payload signature passed to Hook.
50
51
type SignatureError struct {
51
- Signature string
52
+ Signature string
53
+ Signatures []string
52
54
}
53
55
54
56
func (e * SignatureError ) Error () string {
55
57
if e == nil {
56
58
return "<nil>"
57
59
}
60
+
61
+ if e .Signatures != nil {
62
+ return fmt .Sprintf ("invalid payload signatures %s" , e .Signatures )
63
+ }
64
+
58
65
return fmt .Sprintf ("invalid payload signature %s" , e .Signature )
59
66
}
60
67
@@ -94,46 +101,80 @@ func (e *ParseError) Error() string {
94
101
return e .Err .Error ()
95
102
}
96
103
97
- // CheckPayloadSignature calculates and verifies SHA1 signature of the given payload
98
- func CheckPayloadSignature (payload []byte , secret string , signature string ) (string , error ) {
99
- if secret == "" {
100
- return "" , errors .New ("signature validation secret can not be empty" )
104
+ // ExtractCommaSeparatedValues will extract the values matching the key.
105
+ func ExtractCommaSeparatedValues (source , prefix string ) []string {
106
+ parts := strings .Split (source , "," )
107
+ values := make ([]string , 0 )
108
+ for _ , part := range parts {
109
+ if strings .HasPrefix (part , prefix ) {
110
+ values = append (values , strings .TrimPrefix (part , prefix ))
111
+ }
101
112
}
102
113
103
- signature = strings .TrimPrefix (signature , "sha1=" )
114
+ return values
115
+ }
116
+
117
+ // ExtractSignatures will extract all the signatures from the source.
118
+ func ExtractSignatures (source , prefix string ) []string {
119
+ // If there are multiple possible matches, let the comma seperated extractor
120
+ // do it's work.
121
+ if strings .Contains (source , "," ) {
122
+ return ExtractCommaSeparatedValues (source , prefix )
123
+ }
104
124
105
- mac := hmac .New (sha1 .New , []byte (secret ))
125
+ // There were no commas, so just trim the prefix (if it even exists) and
126
+ // pass it back.
127
+ return []string {
128
+ strings .TrimPrefix (source , prefix ),
129
+ }
130
+ }
131
+
132
+ // ValidateMAC will verify that the expected mac for the given hash will match
133
+ // the one provided.
134
+ func ValidateMAC (payload []byte , mac hash.Hash , signatures []string ) (string , error ) {
135
+ // Write the payload to the provided hash.
106
136
_ , err := mac .Write (payload )
107
137
if err != nil {
108
138
return "" , err
109
139
}
140
+
110
141
expectedMAC := hex .EncodeToString (mac .Sum (nil ))
111
142
112
- if ! hmac .Equal ([]byte (signature ), []byte (expectedMAC )) {
113
- return expectedMAC , & SignatureError {signature }
143
+ for _ , signature := range signatures {
144
+ if hmac .Equal ([]byte (signature ), []byte (expectedMAC )) {
145
+ return expectedMAC , err
146
+ }
147
+ }
148
+
149
+ return expectedMAC , & SignatureError {
150
+ Signatures : signatures ,
114
151
}
115
- return expectedMAC , err
116
152
}
117
153
118
- // CheckPayloadSignature256 calculates and verifies SHA256 signature of the given payload
119
- func CheckPayloadSignature256 (payload []byte , secret string , signature string ) (string , error ) {
154
+ // CheckPayloadSignature calculates and verifies SHA1 signature of the given payload
155
+ func CheckPayloadSignature (payload []byte , secret string , signature string ) (string , error ) {
120
156
if secret == "" {
121
157
return "" , errors .New ("signature validation secret can not be empty" )
122
158
}
123
159
124
- signature = strings .TrimPrefix (signature , "sha256=" )
160
+ // Extract the signatures.
161
+ signatures := ExtractSignatures (signature , "sha1=" )
125
162
126
- mac := hmac .New (sha256 .New , []byte (secret ))
127
- _ , err := mac .Write (payload )
128
- if err != nil {
129
- return "" , err
130
- }
131
- expectedMAC := hex .EncodeToString (mac .Sum (nil ))
163
+ // Validate the MAC.
164
+ return ValidateMAC (payload , hmac .New (sha1 .New , []byte (secret )), signatures )
165
+ }
132
166
133
- if ! hmac .Equal ([]byte (signature ), []byte (expectedMAC )) {
134
- return expectedMAC , & SignatureError {signature }
167
+ // CheckPayloadSignature256 calculates and verifies SHA256 signature of the given payload
168
+ func CheckPayloadSignature256 (payload []byte , secret string , signature string ) (string , error ) {
169
+ if secret == "" {
170
+ return "" , errors .New ("signature validation secret can not be empty" )
135
171
}
136
- return expectedMAC , err
172
+
173
+ // Extract the signatures.
174
+ signatures := ExtractSignatures (signature , "sha256=" )
175
+
176
+ // Validate the MAC.
177
+ return ValidateMAC (payload , hmac .New (sha256 .New , []byte (secret )), signatures )
137
178
}
138
179
139
180
// CheckPayloadSignature512 calculates and verifies SHA512 signature of the given payload
@@ -142,19 +183,11 @@ func CheckPayloadSignature512(payload []byte, secret string, signature string) (
142
183
return "" , errors .New ("signature validation secret can not be empty" )
143
184
}
144
185
145
- signature = strings .TrimPrefix (signature , "sha512=" )
146
-
147
- mac := hmac .New (sha512 .New , []byte (secret ))
148
- _ , err := mac .Write (payload )
149
- if err != nil {
150
- return "" , err
151
- }
152
- expectedMAC := hex .EncodeToString (mac .Sum (nil ))
186
+ // Extract the signatures.
187
+ signatures := ExtractSignatures (signature , "sha512=" )
153
188
154
- if ! hmac .Equal ([]byte (signature ), []byte (expectedMAC )) {
155
- return expectedMAC , & SignatureError {signature }
156
- }
157
- return expectedMAC , err
189
+ // Validate the MAC.
190
+ return ValidateMAC (payload , hmac .New (sha512 .New , []byte (secret )), signatures )
158
191
}
159
192
160
193
func CheckScalrSignature (headers map [string ]interface {}, body []byte , signingKey string , checkDate bool ) (bool , error ) {
@@ -177,7 +210,7 @@ func CheckScalrSignature(headers map[string]interface{}, body []byte, signingKey
177
210
expectedSignature := hex .EncodeToString (mac .Sum (nil ))
178
211
179
212
if ! hmac .Equal ([]byte (providedSignature ), []byte (expectedSignature )) {
180
- return false , & SignatureError {providedSignature }
213
+ return false , & SignatureError {Signature : providedSignature }
181
214
}
182
215
183
216
if ! checkDate {
@@ -192,7 +225,7 @@ func CheckScalrSignature(headers map[string]interface{}, body []byte, signingKey
192
225
delta := math .Abs (now .Sub (date ).Seconds ())
193
226
194
227
if delta > 300 {
195
- return false , & SignatureError {"outdated" }
228
+ return false , & SignatureError {Signature : "outdated" }
196
229
}
197
230
return true , nil
198
231
}
0 commit comments