-
Notifications
You must be signed in to change notification settings - Fork 9
/
signing.go
180 lines (130 loc) · 5.9 KB
/
signing.go
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
package signature
import (
"errors"
"fmt"
stewstrings "github.com/stretchr/stew/strings"
"github.com/stretchr/tracer"
"net/url"
"regexp"
"strings"
)
// FailedSignature is the string that will be used if signing fails.
const FailedSignature string = ":-("
// ErrNoSignatureFound is the error that is thrown when no signature could be found.
var ErrNoSignatureFound = errors.New("No signature was found.")
// SignatureRegex is the regex used to remove the signature from the URL string
var SignatureRegex = regexp.MustCompile("(.*)[&?](~|%7E)?sign=([0-9a-zA-Z]+)(.*)")
// trace writes some trace (if there is a Tracer set).
func trace(t *tracer.Tracer, format string, args ...interface{}) {
if t.Should(tracer.LevelDebug) {
// add the 'signature' prefix to trace
if len(format) > 0 {
format = stewstrings.MergeStrings("signature: ", format)
}
// trace this
t.Trace(tracer.LevelDebug, format, args...)
}
}
// GetSignature gets the signature of a request based on the given parameters.
func GetSignature(method, requestUrl, body, privateKey string) (string, error) {
return GetSignatureWithTrace(method, requestUrl, body, privateKey, Tracer)
}
// GetSignatureWithTrace gets the signature of a request based on the given parameters.
func GetSignatureWithTrace(method, requestUrl, body, privateKey string, tracer *tracer.Tracer) (string, error) {
trace(tracer, "GetSignature: method=%s", method)
trace(tracer, "GetSignature: requestUrl=%s", requestUrl)
trace(tracer, "GetSignature: body=%s", body)
trace(tracer, "GetSignature: privateKey=%s", privateKey)
// parse the URL
u, parseErr := url.ParseRequestURI(requestUrl)
if parseErr != nil {
trace(tracer, "GetSignature: FAILED to parse the URL: %s", parseErr)
return FailedSignature, parseErr
}
trace(tracer, "GetSignature: Parsed the URL as: %s", u.String())
// get the query values
values := u.Query()
// add the private key parameter
values.Set(PrivateKeyKey, privateKey)
trace(tracer, "GetSignature: Set the private key (%s): %s", PrivateKeyKey, privateKey)
if len(body) > 0 {
bodyHash := Hash(body)
trace(tracer, "GetSignature: Set the body hash (%s): %s", BodyHashKey, bodyHash)
values.Set(BodyHashKey, bodyHash)
} else {
trace(tracer, "GetSignature: Skipping body hash as there's no body (%s).", BodyHashKey)
}
// get the ordered params
orderedParams := OrderParams(values)
trace(tracer, "GetSignature: Ordered parameters: %s", orderedParams)
base := strings.Split(u.String(), "?")[0]
combined := stewstrings.MergeStrings(strings.ToUpper(method), "&", base, "?", orderedParams)
trace(tracer, "GetSignature: Base : %s", base)
trace(tracer, "GetSignature: Combined: %s", combined)
theHash := Hash(combined)
trace(tracer, "GetSignature: Output: %s", theHash)
return theHash, nil
}
// GetSignedURL gets the URL with the sign parameter added based on the given parameters.
func GetSignedURL(method, requestUrl, body, privateKey string) (string, error) {
return GetSignedURLWithTrace(method, requestUrl, body, privateKey, Tracer)
}
// GetSignedURL gets the URL with the sign parameter added based on the given parameters.
func GetSignedURLWithTrace(method, requestUrl, body, privateKey string, tracer *tracer.Tracer) (string, error) {
trace(tracer, "GetSignedURL: method=%s", method)
trace(tracer, "GetSignedURL: requestUrl=%s", requestUrl)
trace(tracer, "GetSignedURL: body=%s", body)
trace(tracer, "GetSignedURL: privateKey=%s", privateKey)
hash, hashErr := GetSignatureWithTrace(method, requestUrl, body, privateKey, tracer)
if hashErr != nil {
trace(tracer, "GetSignedURL: FAILED to get the signature: %s", hashErr)
return FailedSignature, hashErr
}
var signedUrl string
if strings.Contains(requestUrl, "?") {
signedUrl = stewstrings.MergeStrings(requestUrl, "&", url.QueryEscape(SignatureKey), "=", url.QueryEscape(hash))
} else {
signedUrl = stewstrings.MergeStrings(requestUrl, "?", url.QueryEscape(SignatureKey), "=", url.QueryEscape(hash))
}
trace(tracer, "GetSignedURL: Output: %s", signedUrl)
return signedUrl, nil
}
// ValidateSignature validates the signature in a URL to ensure it is correct based on
// the specified parameters.
func ValidateSignature(method, requestUrl, body, privateKey string) (bool, error) {
return ValidateSignatureWithTrace(method, requestUrl, body, privateKey, Tracer)
}
// ValidateSignature validates the signature in a URL to ensure it is correct based on
// the specified parameters.
func ValidateSignatureWithTrace(method, requestUrl, body, privateKey string, tracer *tracer.Tracer) (bool, error) {
trace(tracer, "ValidateSignature: method=%s", method)
trace(tracer, "ValidateSignature: requestUrl=%s", requestUrl)
trace(tracer, "ValidateSignature: body=%s", body)
trace(tracer, "ValidateSignature: privateKey=%s", privateKey)
if !strings.Contains(requestUrl, "?") {
trace(tracer, "ValidateSignature: FAILED because there was no signature found.")
return false, ErrNoSignatureFound
}
segments := strings.Split(requestUrl, SignatureKey)
if len(segments) < 2 {
trace(tracer, "ValidateSignature: Failed to get signature: %s", ErrNoSignatureFound)
return false, ErrNoSignatureFound
} else {
trace(tracer, "Segments: %s", segments)
}
modifiedURL := strings.TrimRight(segments[0], "&")
signature := strings.TrimLeft(segments[1], "=")
trace(tracer, "ValidateSignature: Modified URL (without signature): %s", modifiedURL)
expectedSignature, signErr := GetSignatureWithTrace(method, modifiedURL, body, privateKey, tracer)
if signErr != nil {
trace(tracer, "ValidateSignature: FAILED to GetSignature: %s", signErr)
return false, signErr
}
if signature != expectedSignature {
err := errors.New(fmt.Sprintf("Signature \"%s\" is incorrect when \"%s\" is expected.", signature, expectedSignature))
trace(tracer, "ValidateSignature: Signatures do not match: %s", err)
return false, err
}
trace(tracer, "ValidateSignature: Happy because the signatures match: %s", signature)
return true, nil
}