aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/minio/minio-go/v7/pkg/signer/request-signature-v2.go
diff options
context:
space:
mode:
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.go319
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
18package signer
19
20import (
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.
37const (
38 signV2Algorithm = "AWS"
39)
40
41// Encode input URL path to URL encoded path.
42func 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}.
60func 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.
106func 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).
130func 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;
171func 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.
183func 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;
199func 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.
211func 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.
219func 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.
250var 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"];
289func 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}