-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathrequest.go
158 lines (142 loc) · 3.63 KB
/
request.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
package digestsig
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
"strings"
"time"
)
const defaultExpireIn = 30 * time.Second
// VerifySignature Check the signature from request, if signature is valid, not error returns
//
// Signature = URL-Encode( Base64( HMAC-SHA1( YourSecretAccessKey, UTF-8-Encoding-Of( StringToSign ) ) ) );
// StringToSign = HTTP-VERB + "\n" +
//
// Content-MD5 + "\n" +
// Content-Type + "\n" +
// Date + "\n" +
// Expires+ "\n" +
// HTTP-HOST
//
// func SignRequest(r *http.Request, secret, body []byte) (*http.Request, error) {
// if len(r.Host) == 0 {
// return r, ErrorEmptyHost
// }
// }
func VerifySignature(r *http.Request, secret []byte) error {
dh := r.Header[DigestHeaderKey]
hashB := []byte{}
if len(dh) > 0 {
digest := dh[0]
if !strings.HasPrefix(digest, DigestHeaderSHAPrefix) {
return ErrorIncorrectDigestHeader
}
hash := strings.TrimPrefix(digest, DigestHeaderSHAPrefix)
var err error
hash, err = url.QueryUnescape(hash)
if err != nil {
return ErrorIncorrectDigestHeader
}
hashB, err = base64.StdEncoding.DecodeString(hash)
if err != nil {
return ErrorIncorrectDigestHeader
}
} else if len(dh) > 0 {
return ErrorMissingDigestHeader
}
stringToSign, err := stringToSignFromRequest(r, "")
if err != nil {
return err
}
fmt.Println(stringToSign.String())
mac := hmac.New(sha256.New, secret)
mac.Write([]byte(stringToSign.String()))
equal := hmac.Equal(hashB, mac.Sum(nil))
if !equal {
return ErrorSignatureMismatch
}
return nil
}
func AddHeadersAndSignRequest(r *http.Request, secret []byte, bodyMD5 string) error {
if len(bodyMD5) == 0 {
bodyMD5 = GetBodyMD5(r)
}
if len(bodyMD5) > 0 {
r.Header[ContentMD5HeaderKey] = []string{bodyMD5}
}
r.Header[ExpiresHeaderKey] = []string{fmt.Sprintf("%d", time.Now().Add(defaultExpireIn).Unix())}
r.Header[DateHeaderKey] = []string{time.Now().Format(time.RFC3339)}
return SignRequest(r, secret, bodyMD5)
}
func SignRequest(r *http.Request, secret []byte, bodyMD5 string) error {
stringToSign, err := stringToSignFromRequest(r, bodyMD5)
if err != nil {
return err
}
signature := SignString(stringToSign.String(), secret)
r.Header[DigestHeaderKey] = []string{fmt.Sprintf("%s%s", DigestHeaderSHAPrefix, signature)}
return nil
}
func stringToSignFromRequest(r *http.Request, bodyMD5 string) (SigningData, error) {
data := SigningData{}
data.Method = r.Method
bmd5 := bodyMD5
if len(bodyMD5) == 0 {
bmd5 = GetBodyMD5(r)
}
md5 := r.Header[ContentMD5HeaderKey]
if len(md5) > 0 {
data.BodyMD5 = md5[0]
if bmd5 != md5[0] {
return data, ErrorIncorrectMD5Header
}
} else if len(bmd5) > 0 {
return data, ErrorMissingMD5Header
}
ct := r.Header[ContentTypeHeaderKey]
if len(ct) > 0 {
data.ContentType = ct[0]
} else {
return data, ErrorMissingContentTypeHeader
}
eh := r.Header[ExpiresHeaderKey]
if len(eh) > 0 {
exp, err := strconv.ParseInt(eh[0], 10, 0)
if err != nil {
return data, ErrorIncorrectExpireHeader
}
if time.Now().After(time.Unix(exp, 0)) {
return data, ErrorExpiredRequest
}
data.Expires = exp
} else {
return data, ErrorMissingExpiresHeader
}
dh := r.Header[DateHeaderKey]
if len(dh) > 0 {
data.Date = dh[0]
} else {
return data, ErrorMissingDateHeader
}
data.Host = r.Host
return data, nil
}
func GetBodyMD5(r *http.Request) string {
// Read the Body content
var bodyBytes []byte
if r.Body != nil {
bodyBytes, _ = io.ReadAll(r.Body)
}
// Restore the io.ReadCloser to its original state
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
if len(bodyBytes) == 0 {
return ""
}
return GetMD5(bodyBytes)
}