diff options
author | Rutger Broekhoff | 2023-12-29 21:31:53 +0100 |
---|---|---|
committer | Rutger Broekhoff | 2023-12-29 21:31:53 +0100 |
commit | 404aeae4545d2426c089a5f8d5e82dae56f5212b (patch) | |
tree | 2d84e00af272b39fc04f3795ae06bc48970e57b5 /vendor/github.com/minio/minio-go/v7/pkg/signer/request-signature-v2.go | |
parent | 209d8b0187ed025dec9ac149ebcced3462877bff (diff) | |
download | gitolfs3-404aeae4545d2426c089a5f8d5e82dae56f5212b.tar.gz gitolfs3-404aeae4545d2426c089a5f8d5e82dae56f5212b.zip |
Make Nix builds work
Diffstat (limited to 'vendor/github.com/minio/minio-go/v7/pkg/signer/request-signature-v2.go')
-rw-r--r-- | vendor/github.com/minio/minio-go/v7/pkg/signer/request-signature-v2.go | 319 |
1 files changed, 319 insertions, 0 deletions
diff --git a/vendor/github.com/minio/minio-go/v7/pkg/signer/request-signature-v2.go b/vendor/github.com/minio/minio-go/v7/pkg/signer/request-signature-v2.go new file mode 100644 index 0000000..fa4f8c9 --- /dev/null +++ b/vendor/github.com/minio/minio-go/v7/pkg/signer/request-signature-v2.go | |||
@@ -0,0 +1,319 @@ | |||
1 | /* | ||
2 | * MinIO Go Library for Amazon S3 Compatible Cloud Storage | ||
3 | * Copyright 2015-2017 MinIO, Inc. | ||
4 | * | ||
5 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | * you may not use this file except in compliance with the License. | ||
7 | * You may obtain a copy of the License at | ||
8 | * | ||
9 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | * | ||
11 | * Unless required by applicable law or agreed to in writing, software | ||
12 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | * See the License for the specific language governing permissions and | ||
15 | * limitations under the License. | ||
16 | */ | ||
17 | |||
18 | package signer | ||
19 | |||
20 | import ( | ||
21 | "bytes" | ||
22 | "crypto/hmac" | ||
23 | "crypto/sha1" | ||
24 | "encoding/base64" | ||
25 | "fmt" | ||
26 | "net/http" | ||
27 | "net/url" | ||
28 | "sort" | ||
29 | "strconv" | ||
30 | "strings" | ||
31 | "time" | ||
32 | |||
33 | "github.com/minio/minio-go/v7/pkg/s3utils" | ||
34 | ) | ||
35 | |||
36 | // Signature and API related constants. | ||
37 | const ( | ||
38 | signV2Algorithm = "AWS" | ||
39 | ) | ||
40 | |||
41 | // Encode input URL path to URL encoded path. | ||
42 | func encodeURL2Path(req *http.Request, virtualHost bool) (path string) { | ||
43 | if virtualHost { | ||
44 | reqHost := getHostAddr(req) | ||
45 | dotPos := strings.Index(reqHost, ".") | ||
46 | if dotPos > -1 { | ||
47 | bucketName := reqHost[:dotPos] | ||
48 | path = "/" + bucketName | ||
49 | path += req.URL.Path | ||
50 | path = s3utils.EncodePath(path) | ||
51 | return | ||
52 | } | ||
53 | } | ||
54 | path = s3utils.EncodePath(req.URL.Path) | ||
55 | return | ||
56 | } | ||
57 | |||
58 | // PreSignV2 - presign the request in following style. | ||
59 | // https://${S3_BUCKET}.s3.amazonaws.com/${S3_OBJECT}?AWSAccessKeyId=${S3_ACCESS_KEY}&Expires=${TIMESTAMP}&Signature=${SIGNATURE}. | ||
60 | func PreSignV2(req http.Request, accessKeyID, secretAccessKey string, expires int64, virtualHost bool) *http.Request { | ||
61 | // Presign is not needed for anonymous credentials. | ||
62 | if accessKeyID == "" || secretAccessKey == "" { | ||
63 | return &req | ||
64 | } | ||
65 | |||
66 | d := time.Now().UTC() | ||
67 | // Find epoch expires when the request will expire. | ||
68 | epochExpires := d.Unix() + expires | ||
69 | |||
70 | // Add expires header if not present. | ||
71 | if expiresStr := req.Header.Get("Expires"); expiresStr == "" { | ||
72 | req.Header.Set("Expires", strconv.FormatInt(epochExpires, 10)) | ||
73 | } | ||
74 | |||
75 | // Get presigned string to sign. | ||
76 | stringToSign := preStringToSignV2(req, virtualHost) | ||
77 | hm := hmac.New(sha1.New, []byte(secretAccessKey)) | ||
78 | hm.Write([]byte(stringToSign)) | ||
79 | |||
80 | // Calculate signature. | ||
81 | signature := base64.StdEncoding.EncodeToString(hm.Sum(nil)) | ||
82 | |||
83 | query := req.URL.Query() | ||
84 | // Handle specially for Google Cloud Storage. | ||
85 | if strings.Contains(getHostAddr(&req), ".storage.googleapis.com") { | ||
86 | query.Set("GoogleAccessId", accessKeyID) | ||
87 | } else { | ||
88 | query.Set("AWSAccessKeyId", accessKeyID) | ||
89 | } | ||
90 | |||
91 | // Fill in Expires for presigned query. | ||
92 | query.Set("Expires", strconv.FormatInt(epochExpires, 10)) | ||
93 | |||
94 | // Encode query and save. | ||
95 | req.URL.RawQuery = s3utils.QueryEncode(query) | ||
96 | |||
97 | // Save signature finally. | ||
98 | req.URL.RawQuery += "&Signature=" + s3utils.EncodePath(signature) | ||
99 | |||
100 | // Return. | ||
101 | return &req | ||
102 | } | ||
103 | |||
104 | // PostPresignSignatureV2 - presigned signature for PostPolicy | ||
105 | // request. | ||
106 | func PostPresignSignatureV2(policyBase64, secretAccessKey string) string { | ||
107 | hm := hmac.New(sha1.New, []byte(secretAccessKey)) | ||
108 | hm.Write([]byte(policyBase64)) | ||
109 | signature := base64.StdEncoding.EncodeToString(hm.Sum(nil)) | ||
110 | return signature | ||
111 | } | ||
112 | |||
113 | // Authorization = "AWS" + " " + AWSAccessKeyId + ":" + Signature; | ||
114 | // Signature = Base64( HMAC-SHA1( YourSecretAccessKeyID, UTF-8-Encoding-Of( StringToSign ) ) ); | ||
115 | // | ||
116 | // StringToSign = HTTP-Verb + "\n" + | ||
117 | // Content-Md5 + "\n" + | ||
118 | // Content-Type + "\n" + | ||
119 | // Date + "\n" + | ||
120 | // CanonicalizedProtocolHeaders + | ||
121 | // CanonicalizedResource; | ||
122 | // | ||
123 | // CanonicalizedResource = [ "/" + Bucket ] + | ||
124 | // <HTTP-Request-URI, from the protocol name up to the query string> + | ||
125 | // [ subresource, if present. For example "?acl", "?location", "?logging", or "?torrent"]; | ||
126 | // | ||
127 | // CanonicalizedProtocolHeaders = <described below> | ||
128 | |||
129 | // SignV2 sign the request before Do() (AWS Signature Version 2). | ||
130 | func SignV2(req http.Request, accessKeyID, secretAccessKey string, virtualHost bool) *http.Request { | ||
131 | // Signature calculation is not needed for anonymous credentials. | ||
132 | if accessKeyID == "" || secretAccessKey == "" { | ||
133 | return &req | ||
134 | } | ||
135 | |||
136 | // Initial time. | ||
137 | d := time.Now().UTC() | ||
138 | |||
139 | // Add date if not present. | ||
140 | if date := req.Header.Get("Date"); date == "" { | ||
141 | req.Header.Set("Date", d.Format(http.TimeFormat)) | ||
142 | } | ||
143 | |||
144 | // Calculate HMAC for secretAccessKey. | ||
145 | stringToSign := stringToSignV2(req, virtualHost) | ||
146 | hm := hmac.New(sha1.New, []byte(secretAccessKey)) | ||
147 | hm.Write([]byte(stringToSign)) | ||
148 | |||
149 | // Prepare auth header. | ||
150 | authHeader := new(bytes.Buffer) | ||
151 | authHeader.WriteString(fmt.Sprintf("%s %s:", signV2Algorithm, accessKeyID)) | ||
152 | encoder := base64.NewEncoder(base64.StdEncoding, authHeader) | ||
153 | encoder.Write(hm.Sum(nil)) | ||
154 | encoder.Close() | ||
155 | |||
156 | // Set Authorization header. | ||
157 | req.Header.Set("Authorization", authHeader.String()) | ||
158 | |||
159 | return &req | ||
160 | } | ||
161 | |||
162 | // From the Amazon docs: | ||
163 | // | ||
164 | // StringToSign = HTTP-Verb + "\n" + | ||
165 | // | ||
166 | // Content-Md5 + "\n" + | ||
167 | // Content-Type + "\n" + | ||
168 | // Expires + "\n" + | ||
169 | // CanonicalizedProtocolHeaders + | ||
170 | // CanonicalizedResource; | ||
171 | func preStringToSignV2(req http.Request, virtualHost bool) string { | ||
172 | buf := new(bytes.Buffer) | ||
173 | // Write standard headers. | ||
174 | writePreSignV2Headers(buf, req) | ||
175 | // Write canonicalized protocol headers if any. | ||
176 | writeCanonicalizedHeaders(buf, req) | ||
177 | // Write canonicalized Query resources if any. | ||
178 | writeCanonicalizedResource(buf, req, virtualHost) | ||
179 | return buf.String() | ||
180 | } | ||
181 | |||
182 | // writePreSignV2Headers - write preSign v2 required headers. | ||
183 | func writePreSignV2Headers(buf *bytes.Buffer, req http.Request) { | ||
184 | buf.WriteString(req.Method + "\n") | ||
185 | buf.WriteString(req.Header.Get("Content-Md5") + "\n") | ||
186 | buf.WriteString(req.Header.Get("Content-Type") + "\n") | ||
187 | buf.WriteString(req.Header.Get("Expires") + "\n") | ||
188 | } | ||
189 | |||
190 | // From the Amazon docs: | ||
191 | // | ||
192 | // StringToSign = HTTP-Verb + "\n" + | ||
193 | // | ||
194 | // Content-Md5 + "\n" + | ||
195 | // Content-Type + "\n" + | ||
196 | // Date + "\n" + | ||
197 | // CanonicalizedProtocolHeaders + | ||
198 | // CanonicalizedResource; | ||
199 | func stringToSignV2(req http.Request, virtualHost bool) string { | ||
200 | buf := new(bytes.Buffer) | ||
201 | // Write standard headers. | ||
202 | writeSignV2Headers(buf, req) | ||
203 | // Write canonicalized protocol headers if any. | ||
204 | writeCanonicalizedHeaders(buf, req) | ||
205 | // Write canonicalized Query resources if any. | ||
206 | writeCanonicalizedResource(buf, req, virtualHost) | ||
207 | return buf.String() | ||
208 | } | ||
209 | |||
210 | // writeSignV2Headers - write signV2 required headers. | ||
211 | func writeSignV2Headers(buf *bytes.Buffer, req http.Request) { | ||
212 | buf.WriteString(req.Method + "\n") | ||
213 | buf.WriteString(req.Header.Get("Content-Md5") + "\n") | ||
214 | buf.WriteString(req.Header.Get("Content-Type") + "\n") | ||
215 | buf.WriteString(req.Header.Get("Date") + "\n") | ||
216 | } | ||
217 | |||
218 | // writeCanonicalizedHeaders - write canonicalized headers. | ||
219 | func writeCanonicalizedHeaders(buf *bytes.Buffer, req http.Request) { | ||
220 | var protoHeaders []string | ||
221 | vals := make(map[string][]string) | ||
222 | for k, vv := range req.Header { | ||
223 | // All the AMZ headers should be lowercase | ||
224 | lk := strings.ToLower(k) | ||
225 | if strings.HasPrefix(lk, "x-amz") { | ||
226 | protoHeaders = append(protoHeaders, lk) | ||
227 | vals[lk] = vv | ||
228 | } | ||
229 | } | ||
230 | sort.Strings(protoHeaders) | ||
231 | for _, k := range protoHeaders { | ||
232 | buf.WriteString(k) | ||
233 | buf.WriteByte(':') | ||
234 | for idx, v := range vals[k] { | ||
235 | if idx > 0 { | ||
236 | buf.WriteByte(',') | ||
237 | } | ||
238 | buf.WriteString(v) | ||
239 | } | ||
240 | buf.WriteByte('\n') | ||
241 | } | ||
242 | } | ||
243 | |||
244 | // AWS S3 Signature V2 calculation rule is give here: | ||
245 | // http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html#RESTAuthenticationStringToSign | ||
246 | |||
247 | // Whitelist resource list that will be used in query string for signature-V2 calculation. | ||
248 | // | ||
249 | // This list should be kept alphabetically sorted, do not hastily edit. | ||
250 | var resourceList = []string{ | ||
251 | "acl", | ||
252 | "cors", | ||
253 | "delete", | ||
254 | "encryption", | ||
255 | "legal-hold", | ||
256 | "lifecycle", | ||
257 | "location", | ||
258 | "logging", | ||
259 | "notification", | ||
260 | "partNumber", | ||
261 | "policy", | ||
262 | "replication", | ||
263 | "requestPayment", | ||
264 | "response-cache-control", | ||
265 | "response-content-disposition", | ||
266 | "response-content-encoding", | ||
267 | "response-content-language", | ||
268 | "response-content-type", | ||
269 | "response-expires", | ||
270 | "retention", | ||
271 | "select", | ||
272 | "select-type", | ||
273 | "tagging", | ||
274 | "torrent", | ||
275 | "uploadId", | ||
276 | "uploads", | ||
277 | "versionId", | ||
278 | "versioning", | ||
279 | "versions", | ||
280 | "website", | ||
281 | } | ||
282 | |||
283 | // From the Amazon docs: | ||
284 | // | ||
285 | // CanonicalizedResource = [ "/" + Bucket ] + | ||
286 | // | ||
287 | // <HTTP-Request-URI, from the protocol name up to the query string> + | ||
288 | // [ sub-resource, if present. For example "?acl", "?location", "?logging", or "?torrent"]; | ||
289 | func writeCanonicalizedResource(buf *bytes.Buffer, req http.Request, virtualHost bool) { | ||
290 | // Save request URL. | ||
291 | requestURL := req.URL | ||
292 | // Get encoded URL path. | ||
293 | buf.WriteString(encodeURL2Path(&req, virtualHost)) | ||
294 | if requestURL.RawQuery != "" { | ||
295 | var n int | ||
296 | vals, _ := url.ParseQuery(requestURL.RawQuery) | ||
297 | // Verify if any sub resource queries are present, if yes | ||
298 | // canonicallize them. | ||
299 | for _, resource := range resourceList { | ||
300 | if vv, ok := vals[resource]; ok && len(vv) > 0 { | ||
301 | n++ | ||
302 | // First element | ||
303 | switch n { | ||
304 | case 1: | ||
305 | buf.WriteByte('?') | ||
306 | // The rest | ||
307 | default: | ||
308 | buf.WriteByte('&') | ||
309 | } | ||
310 | buf.WriteString(resource) | ||
311 | // Request parameters | ||
312 | if len(vv[0]) > 0 { | ||
313 | buf.WriteByte('=') | ||
314 | buf.WriteString(vv[0]) | ||
315 | } | ||
316 | } | ||
317 | } | ||
318 | } | ||
319 | } | ||