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-v4.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-v4.go')
-rw-r--r-- | vendor/github.com/minio/minio-go/v7/pkg/signer/request-signature-v4.go | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/vendor/github.com/minio/minio-go/v7/pkg/signer/request-signature-v4.go b/vendor/github.com/minio/minio-go/v7/pkg/signer/request-signature-v4.go new file mode 100644 index 0000000..ffd2514 --- /dev/null +++ b/vendor/github.com/minio/minio-go/v7/pkg/signer/request-signature-v4.go | |||
@@ -0,0 +1,351 @@ | |||
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 | "encoding/hex" | ||
23 | "net/http" | ||
24 | "sort" | ||
25 | "strconv" | ||
26 | "strings" | ||
27 | "time" | ||
28 | |||
29 | "github.com/minio/minio-go/v7/pkg/s3utils" | ||
30 | ) | ||
31 | |||
32 | // Signature and API related constants. | ||
33 | const ( | ||
34 | signV4Algorithm = "AWS4-HMAC-SHA256" | ||
35 | iso8601DateFormat = "20060102T150405Z" | ||
36 | yyyymmdd = "20060102" | ||
37 | ) | ||
38 | |||
39 | // Different service types | ||
40 | const ( | ||
41 | ServiceTypeS3 = "s3" | ||
42 | ServiceTypeSTS = "sts" | ||
43 | ) | ||
44 | |||
45 | // Excerpts from @lsegal - | ||
46 | // https:/github.com/aws/aws-sdk-js/issues/659#issuecomment-120477258. | ||
47 | // | ||
48 | // * User-Agent | ||
49 | // This is ignored from signing because signing this causes problems with generating pre-signed | ||
50 | // URLs (that are executed by other agents) or when customers pass requests through proxies, which | ||
51 | // may modify the user-agent. | ||
52 | // | ||
53 | // * Authorization | ||
54 | // Is skipped for obvious reasons. | ||
55 | // | ||
56 | // * Accept-Encoding | ||
57 | // Some S3 servers like Hitachi Content Platform do not honor this header for signature | ||
58 | // calculation. | ||
59 | var v4IgnoredHeaders = map[string]bool{ | ||
60 | "Accept-Encoding": true, | ||
61 | "Authorization": true, | ||
62 | "User-Agent": true, | ||
63 | } | ||
64 | |||
65 | // getSigningKey hmac seed to calculate final signature. | ||
66 | func getSigningKey(secret, loc string, t time.Time, serviceType string) []byte { | ||
67 | date := sumHMAC([]byte("AWS4"+secret), []byte(t.Format(yyyymmdd))) | ||
68 | location := sumHMAC(date, []byte(loc)) | ||
69 | service := sumHMAC(location, []byte(serviceType)) | ||
70 | signingKey := sumHMAC(service, []byte("aws4_request")) | ||
71 | return signingKey | ||
72 | } | ||
73 | |||
74 | // getSignature final signature in hexadecimal form. | ||
75 | func getSignature(signingKey []byte, stringToSign string) string { | ||
76 | return hex.EncodeToString(sumHMAC(signingKey, []byte(stringToSign))) | ||
77 | } | ||
78 | |||
79 | // getScope generate a string of a specific date, an AWS region, and a | ||
80 | // service. | ||
81 | func getScope(location string, t time.Time, serviceType string) string { | ||
82 | scope := strings.Join([]string{ | ||
83 | t.Format(yyyymmdd), | ||
84 | location, | ||
85 | serviceType, | ||
86 | "aws4_request", | ||
87 | }, "/") | ||
88 | return scope | ||
89 | } | ||
90 | |||
91 | // GetCredential generate a credential string. | ||
92 | func GetCredential(accessKeyID, location string, t time.Time, serviceType string) string { | ||
93 | scope := getScope(location, t, serviceType) | ||
94 | return accessKeyID + "/" + scope | ||
95 | } | ||
96 | |||
97 | // getHashedPayload get the hexadecimal value of the SHA256 hash of | ||
98 | // the request payload. | ||
99 | func getHashedPayload(req http.Request) string { | ||
100 | hashedPayload := req.Header.Get("X-Amz-Content-Sha256") | ||
101 | if hashedPayload == "" { | ||
102 | // Presign does not have a payload, use S3 recommended value. | ||
103 | hashedPayload = unsignedPayload | ||
104 | } | ||
105 | return hashedPayload | ||
106 | } | ||
107 | |||
108 | // getCanonicalHeaders generate a list of request headers for | ||
109 | // signature. | ||
110 | func getCanonicalHeaders(req http.Request, ignoredHeaders map[string]bool) string { | ||
111 | var headers []string | ||
112 | vals := make(map[string][]string) | ||
113 | for k, vv := range req.Header { | ||
114 | if _, ok := ignoredHeaders[http.CanonicalHeaderKey(k)]; ok { | ||
115 | continue // ignored header | ||
116 | } | ||
117 | headers = append(headers, strings.ToLower(k)) | ||
118 | vals[strings.ToLower(k)] = vv | ||
119 | } | ||
120 | if !headerExists("host", headers) { | ||
121 | headers = append(headers, "host") | ||
122 | } | ||
123 | sort.Strings(headers) | ||
124 | |||
125 | var buf bytes.Buffer | ||
126 | // Save all the headers in canonical form <header>:<value> newline | ||
127 | // separated for each header. | ||
128 | for _, k := range headers { | ||
129 | buf.WriteString(k) | ||
130 | buf.WriteByte(':') | ||
131 | switch { | ||
132 | case k == "host": | ||
133 | buf.WriteString(getHostAddr(&req)) | ||
134 | buf.WriteByte('\n') | ||
135 | default: | ||
136 | for idx, v := range vals[k] { | ||
137 | if idx > 0 { | ||
138 | buf.WriteByte(',') | ||
139 | } | ||
140 | buf.WriteString(signV4TrimAll(v)) | ||
141 | } | ||
142 | buf.WriteByte('\n') | ||
143 | } | ||
144 | } | ||
145 | return buf.String() | ||
146 | } | ||
147 | |||
148 | func headerExists(key string, headers []string) bool { | ||
149 | for _, k := range headers { | ||
150 | if k == key { | ||
151 | return true | ||
152 | } | ||
153 | } | ||
154 | return false | ||
155 | } | ||
156 | |||
157 | // getSignedHeaders generate all signed request headers. | ||
158 | // i.e lexically sorted, semicolon-separated list of lowercase | ||
159 | // request header names. | ||
160 | func getSignedHeaders(req http.Request, ignoredHeaders map[string]bool) string { | ||
161 | var headers []string | ||
162 | for k := range req.Header { | ||
163 | if _, ok := ignoredHeaders[http.CanonicalHeaderKey(k)]; ok { | ||
164 | continue // Ignored header found continue. | ||
165 | } | ||
166 | headers = append(headers, strings.ToLower(k)) | ||
167 | } | ||
168 | if !headerExists("host", headers) { | ||
169 | headers = append(headers, "host") | ||
170 | } | ||
171 | sort.Strings(headers) | ||
172 | return strings.Join(headers, ";") | ||
173 | } | ||
174 | |||
175 | // getCanonicalRequest generate a canonical request of style. | ||
176 | // | ||
177 | // canonicalRequest = | ||
178 | // | ||
179 | // <HTTPMethod>\n | ||
180 | // <CanonicalURI>\n | ||
181 | // <CanonicalQueryString>\n | ||
182 | // <CanonicalHeaders>\n | ||
183 | // <SignedHeaders>\n | ||
184 | // <HashedPayload> | ||
185 | func getCanonicalRequest(req http.Request, ignoredHeaders map[string]bool, hashedPayload string) string { | ||
186 | req.URL.RawQuery = strings.ReplaceAll(req.URL.Query().Encode(), "+", "%20") | ||
187 | canonicalRequest := strings.Join([]string{ | ||
188 | req.Method, | ||
189 | s3utils.EncodePath(req.URL.Path), | ||
190 | req.URL.RawQuery, | ||
191 | getCanonicalHeaders(req, ignoredHeaders), | ||
192 | getSignedHeaders(req, ignoredHeaders), | ||
193 | hashedPayload, | ||
194 | }, "\n") | ||
195 | return canonicalRequest | ||
196 | } | ||
197 | |||
198 | // getStringToSign a string based on selected query values. | ||
199 | func getStringToSignV4(t time.Time, location, canonicalRequest, serviceType string) string { | ||
200 | stringToSign := signV4Algorithm + "\n" + t.Format(iso8601DateFormat) + "\n" | ||
201 | stringToSign = stringToSign + getScope(location, t, serviceType) + "\n" | ||
202 | stringToSign += hex.EncodeToString(sum256([]byte(canonicalRequest))) | ||
203 | return stringToSign | ||
204 | } | ||
205 | |||
206 | // PreSignV4 presign the request, in accordance with | ||
207 | // http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html. | ||
208 | func PreSignV4(req http.Request, accessKeyID, secretAccessKey, sessionToken, location string, expires int64) *http.Request { | ||
209 | // Presign is not needed for anonymous credentials. | ||
210 | if accessKeyID == "" || secretAccessKey == "" { | ||
211 | return &req | ||
212 | } | ||
213 | |||
214 | // Initial time. | ||
215 | t := time.Now().UTC() | ||
216 | |||
217 | // Get credential string. | ||
218 | credential := GetCredential(accessKeyID, location, t, ServiceTypeS3) | ||
219 | |||
220 | // Get all signed headers. | ||
221 | signedHeaders := getSignedHeaders(req, v4IgnoredHeaders) | ||
222 | |||
223 | // Set URL query. | ||
224 | query := req.URL.Query() | ||
225 | query.Set("X-Amz-Algorithm", signV4Algorithm) | ||
226 | query.Set("X-Amz-Date", t.Format(iso8601DateFormat)) | ||
227 | query.Set("X-Amz-Expires", strconv.FormatInt(expires, 10)) | ||
228 | query.Set("X-Amz-SignedHeaders", signedHeaders) | ||
229 | query.Set("X-Amz-Credential", credential) | ||
230 | // Set session token if available. | ||
231 | if sessionToken != "" { | ||
232 | query.Set("X-Amz-Security-Token", sessionToken) | ||
233 | } | ||
234 | req.URL.RawQuery = query.Encode() | ||
235 | |||
236 | // Get canonical request. | ||
237 | canonicalRequest := getCanonicalRequest(req, v4IgnoredHeaders, getHashedPayload(req)) | ||
238 | |||
239 | // Get string to sign from canonical request. | ||
240 | stringToSign := getStringToSignV4(t, location, canonicalRequest, ServiceTypeS3) | ||
241 | |||
242 | // Gext hmac signing key. | ||
243 | signingKey := getSigningKey(secretAccessKey, location, t, ServiceTypeS3) | ||
244 | |||
245 | // Calculate signature. | ||
246 | signature := getSignature(signingKey, stringToSign) | ||
247 | |||
248 | // Add signature header to RawQuery. | ||
249 | req.URL.RawQuery += "&X-Amz-Signature=" + signature | ||
250 | |||
251 | return &req | ||
252 | } | ||
253 | |||
254 | // PostPresignSignatureV4 - presigned signature for PostPolicy | ||
255 | // requests. | ||
256 | func PostPresignSignatureV4(policyBase64 string, t time.Time, secretAccessKey, location string) string { | ||
257 | // Get signining key. | ||
258 | signingkey := getSigningKey(secretAccessKey, location, t, ServiceTypeS3) | ||
259 | // Calculate signature. | ||
260 | signature := getSignature(signingkey, policyBase64) | ||
261 | return signature | ||
262 | } | ||
263 | |||
264 | // SignV4STS - signature v4 for STS request. | ||
265 | func SignV4STS(req http.Request, accessKeyID, secretAccessKey, location string) *http.Request { | ||
266 | return signV4(req, accessKeyID, secretAccessKey, "", location, ServiceTypeSTS, nil) | ||
267 | } | ||
268 | |||
269 | // Internal function called for different service types. | ||
270 | func signV4(req http.Request, accessKeyID, secretAccessKey, sessionToken, location, serviceType string, trailer http.Header) *http.Request { | ||
271 | // Signature calculation is not needed for anonymous credentials. | ||
272 | if accessKeyID == "" || secretAccessKey == "" { | ||
273 | return &req | ||
274 | } | ||
275 | |||
276 | // Initial time. | ||
277 | t := time.Now().UTC() | ||
278 | |||
279 | // Set x-amz-date. | ||
280 | req.Header.Set("X-Amz-Date", t.Format(iso8601DateFormat)) | ||
281 | |||
282 | // Set session token if available. | ||
283 | if sessionToken != "" { | ||
284 | req.Header.Set("X-Amz-Security-Token", sessionToken) | ||
285 | } | ||
286 | |||
287 | if len(trailer) > 0 { | ||
288 | for k := range trailer { | ||
289 | req.Header.Add("X-Amz-Trailer", strings.ToLower(k)) | ||
290 | } | ||
291 | |||
292 | req.Header.Set("Content-Encoding", "aws-chunked") | ||
293 | req.Header.Set("x-amz-decoded-content-length", strconv.FormatInt(req.ContentLength, 10)) | ||
294 | } | ||
295 | |||
296 | hashedPayload := getHashedPayload(req) | ||
297 | if serviceType == ServiceTypeSTS { | ||
298 | // Content sha256 header is not sent with the request | ||
299 | // but it is expected to have sha256 of payload for signature | ||
300 | // in STS service type request. | ||
301 | req.Header.Del("X-Amz-Content-Sha256") | ||
302 | } | ||
303 | |||
304 | // Get canonical request. | ||
305 | canonicalRequest := getCanonicalRequest(req, v4IgnoredHeaders, hashedPayload) | ||
306 | |||
307 | // Get string to sign from canonical request. | ||
308 | stringToSign := getStringToSignV4(t, location, canonicalRequest, serviceType) | ||
309 | |||
310 | // Get hmac signing key. | ||
311 | signingKey := getSigningKey(secretAccessKey, location, t, serviceType) | ||
312 | |||
313 | // Get credential string. | ||
314 | credential := GetCredential(accessKeyID, location, t, serviceType) | ||
315 | |||
316 | // Get all signed headers. | ||
317 | signedHeaders := getSignedHeaders(req, v4IgnoredHeaders) | ||
318 | |||
319 | // Calculate signature. | ||
320 | signature := getSignature(signingKey, stringToSign) | ||
321 | |||
322 | // If regular request, construct the final authorization header. | ||
323 | parts := []string{ | ||
324 | signV4Algorithm + " Credential=" + credential, | ||
325 | "SignedHeaders=" + signedHeaders, | ||
326 | "Signature=" + signature, | ||
327 | } | ||
328 | |||
329 | // Set authorization header. | ||
330 | auth := strings.Join(parts, ", ") | ||
331 | req.Header.Set("Authorization", auth) | ||
332 | |||
333 | if len(trailer) > 0 { | ||
334 | // Use custom chunked encoding. | ||
335 | req.Trailer = trailer | ||
336 | return StreamingUnsignedV4(&req, sessionToken, req.ContentLength, time.Now().UTC()) | ||
337 | } | ||
338 | return &req | ||
339 | } | ||
340 | |||
341 | // SignV4 sign the request before Do(), in accordance with | ||
342 | // http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html. | ||
343 | func SignV4(req http.Request, accessKeyID, secretAccessKey, sessionToken, location string) *http.Request { | ||
344 | return signV4(req, accessKeyID, secretAccessKey, sessionToken, location, ServiceTypeS3, nil) | ||
345 | } | ||
346 | |||
347 | // SignV4Trailer sign the request before Do(), in accordance with | ||
348 | // http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html | ||
349 | func SignV4Trailer(req http.Request, accessKeyID, secretAccessKey, sessionToken, location string, trailer http.Header) *http.Request { | ||
350 | return signV4(req, accessKeyID, secretAccessKey, sessionToken, location, ServiceTypeS3, trailer) | ||
351 | } | ||