]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/aws/aws-sdk-go/aws/signer/v4/v4.go
Merge branch 'master' of /home/ubuntu/terraform-vendor
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / aws / aws-sdk-go / aws / signer / v4 / v4.go
1 // Package v4 implements signing for AWS V4 signer
2 //
3 // Provides request signing for request that need to be signed with
4 // AWS V4 Signatures.
5 //
6 // Standalone Signer
7 //
8 // Generally using the signer outside of the SDK should not require any additional
9 // logic when using Go v1.5 or higher. The signer does this by taking advantage
10 // of the URL.EscapedPath method. If your request URI requires additional escaping
11 // you many need to use the URL.Opaque to define what the raw URI should be sent
12 // to the service as.
13 //
14 // The signer will first check the URL.Opaque field, and use its value if set.
15 // The signer does require the URL.Opaque field to be set in the form of:
16 //
17 // "//<hostname>/<path>"
18 //
19 // // e.g.
20 // "//example.com/some/path"
21 //
22 // The leading "//" and hostname are required or the URL.Opaque escaping will
23 // not work correctly.
24 //
25 // If URL.Opaque is not set the signer will fallback to the URL.EscapedPath()
26 // method and using the returned value. If you're using Go v1.4 you must set
27 // URL.Opaque if the URI path needs escaping. If URL.Opaque is not set with
28 // Go v1.5 the signer will fallback to URL.Path.
29 //
30 // AWS v4 signature validation requires that the canonical string's URI path
31 // element must be the URI escaped form of the HTTP request's path.
32 // http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
33 //
34 // The Go HTTP client will perform escaping automatically on the request. Some
35 // of these escaping may cause signature validation errors because the HTTP
36 // request differs from the URI path or query that the signature was generated.
37 // https://golang.org/pkg/net/url/#URL.EscapedPath
38 //
39 // Because of this, it is recommended that when using the signer outside of the
40 // SDK that explicitly escaping the request prior to being signed is preferable,
41 // and will help prevent signature validation errors. This can be done by setting
42 // the URL.Opaque or URL.RawPath. The SDK will use URL.Opaque first and then
43 // call URL.EscapedPath() if Opaque is not set.
44 //
45 // If signing a request intended for HTTP2 server, and you're using Go 1.6.2
46 // through 1.7.4 you should use the URL.RawPath as the pre-escaped form of the
47 // request URL. https://github.com/golang/go/issues/16847 points to a bug in
48 // Go pre 1.8 that failes to make HTTP2 requests using absolute URL in the HTTP
49 // message. URL.Opaque generally will force Go to make requests with absolute URL.
50 // URL.RawPath does not do this, but RawPath must be a valid escaping of Path
51 // or url.EscapedPath will ignore the RawPath escaping.
52 //
53 // Test `TestStandaloneSign` provides a complete example of using the signer
54 // outside of the SDK and pre-escaping the URI path.
55 package v4
56
57 import (
58 "bytes"
59 "crypto/hmac"
60 "crypto/sha256"
61 "encoding/hex"
62 "fmt"
63 "io"
64 "io/ioutil"
65 "net/http"
66 "net/url"
67 "sort"
68 "strconv"
69 "strings"
70 "time"
71
72 "github.com/aws/aws-sdk-go/aws"
73 "github.com/aws/aws-sdk-go/aws/credentials"
74 "github.com/aws/aws-sdk-go/aws/request"
75 "github.com/aws/aws-sdk-go/private/protocol/rest"
76 )
77
78 const (
79 authHeaderPrefix = "AWS4-HMAC-SHA256"
80 timeFormat = "20060102T150405Z"
81 shortTimeFormat = "20060102"
82
83 // emptyStringSHA256 is a SHA256 of an empty string
84 emptyStringSHA256 = `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`
85 )
86
87 var ignoredHeaders = rules{
88 blacklist{
89 mapRule{
90 "Authorization": struct{}{},
91 "User-Agent": struct{}{},
92 "X-Amzn-Trace-Id": struct{}{},
93 },
94 },
95 }
96
97 // requiredSignedHeaders is a whitelist for build canonical headers.
98 var requiredSignedHeaders = rules{
99 whitelist{
100 mapRule{
101 "Cache-Control": struct{}{},
102 "Content-Disposition": struct{}{},
103 "Content-Encoding": struct{}{},
104 "Content-Language": struct{}{},
105 "Content-Md5": struct{}{},
106 "Content-Type": struct{}{},
107 "Expires": struct{}{},
108 "If-Match": struct{}{},
109 "If-Modified-Since": struct{}{},
110 "If-None-Match": struct{}{},
111 "If-Unmodified-Since": struct{}{},
112 "Range": struct{}{},
113 "X-Amz-Acl": struct{}{},
114 "X-Amz-Copy-Source": struct{}{},
115 "X-Amz-Copy-Source-If-Match": struct{}{},
116 "X-Amz-Copy-Source-If-Modified-Since": struct{}{},
117 "X-Amz-Copy-Source-If-None-Match": struct{}{},
118 "X-Amz-Copy-Source-If-Unmodified-Since": struct{}{},
119 "X-Amz-Copy-Source-Range": struct{}{},
120 "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm": struct{}{},
121 "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key": struct{}{},
122 "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5": struct{}{},
123 "X-Amz-Grant-Full-control": struct{}{},
124 "X-Amz-Grant-Read": struct{}{},
125 "X-Amz-Grant-Read-Acp": struct{}{},
126 "X-Amz-Grant-Write": struct{}{},
127 "X-Amz-Grant-Write-Acp": struct{}{},
128 "X-Amz-Metadata-Directive": struct{}{},
129 "X-Amz-Mfa": struct{}{},
130 "X-Amz-Request-Payer": struct{}{},
131 "X-Amz-Server-Side-Encryption": struct{}{},
132 "X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id": struct{}{},
133 "X-Amz-Server-Side-Encryption-Customer-Algorithm": struct{}{},
134 "X-Amz-Server-Side-Encryption-Customer-Key": struct{}{},
135 "X-Amz-Server-Side-Encryption-Customer-Key-Md5": struct{}{},
136 "X-Amz-Storage-Class": struct{}{},
137 "X-Amz-Website-Redirect-Location": struct{}{},
138 },
139 },
140 patterns{"X-Amz-Meta-"},
141 }
142
143 // allowedHoisting is a whitelist for build query headers. The boolean value
144 // represents whether or not it is a pattern.
145 var allowedQueryHoisting = inclusiveRules{
146 blacklist{requiredSignedHeaders},
147 patterns{"X-Amz-"},
148 }
149
150 // Signer applies AWS v4 signing to given request. Use this to sign requests
151 // that need to be signed with AWS V4 Signatures.
152 type Signer struct {
153 // The authentication credentials the request will be signed against.
154 // This value must be set to sign requests.
155 Credentials *credentials.Credentials
156
157 // Sets the log level the signer should use when reporting information to
158 // the logger. If the logger is nil nothing will be logged. See
159 // aws.LogLevelType for more information on available logging levels
160 //
161 // By default nothing will be logged.
162 Debug aws.LogLevelType
163
164 // The logger loging information will be written to. If there the logger
165 // is nil, nothing will be logged.
166 Logger aws.Logger
167
168 // Disables the Signer's moving HTTP header key/value pairs from the HTTP
169 // request header to the request's query string. This is most commonly used
170 // with pre-signed requests preventing headers from being added to the
171 // request's query string.
172 DisableHeaderHoisting bool
173
174 // Disables the automatic escaping of the URI path of the request for the
175 // siganture's canonical string's path. For services that do not need additional
176 // escaping then use this to disable the signer escaping the path.
177 //
178 // S3 is an example of a service that does not need additional escaping.
179 //
180 // http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
181 DisableURIPathEscaping bool
182
183 // Disales the automatical setting of the HTTP request's Body field with the
184 // io.ReadSeeker passed in to the signer. This is useful if you're using a
185 // custom wrapper around the body for the io.ReadSeeker and want to preserve
186 // the Body value on the Request.Body.
187 //
188 // This does run the risk of signing a request with a body that will not be
189 // sent in the request. Need to ensure that the underlying data of the Body
190 // values are the same.
191 DisableRequestBodyOverwrite bool
192
193 // currentTimeFn returns the time value which represents the current time.
194 // This value should only be used for testing. If it is nil the default
195 // time.Now will be used.
196 currentTimeFn func() time.Time
197
198 // UnsignedPayload will prevent signing of the payload. This will only
199 // work for services that have support for this.
200 UnsignedPayload bool
201 }
202
203 // NewSigner returns a Signer pointer configured with the credentials and optional
204 // option values provided. If not options are provided the Signer will use its
205 // default configuration.
206 func NewSigner(credentials *credentials.Credentials, options ...func(*Signer)) *Signer {
207 v4 := &Signer{
208 Credentials: credentials,
209 }
210
211 for _, option := range options {
212 option(v4)
213 }
214
215 return v4
216 }
217
218 type signingCtx struct {
219 ServiceName string
220 Region string
221 Request *http.Request
222 Body io.ReadSeeker
223 Query url.Values
224 Time time.Time
225 ExpireTime time.Duration
226 SignedHeaderVals http.Header
227
228 DisableURIPathEscaping bool
229
230 credValues credentials.Value
231 isPresign bool
232 formattedTime string
233 formattedShortTime string
234 unsignedPayload bool
235
236 bodyDigest string
237 signedHeaders string
238 canonicalHeaders string
239 canonicalString string
240 credentialString string
241 stringToSign string
242 signature string
243 authorization string
244 }
245
246 // Sign signs AWS v4 requests with the provided body, service name, region the
247 // request is made to, and time the request is signed at. The signTime allows
248 // you to specify that a request is signed for the future, and cannot be
249 // used until then.
250 //
251 // Returns a list of HTTP headers that were included in the signature or an
252 // error if signing the request failed. Generally for signed requests this value
253 // is not needed as the full request context will be captured by the http.Request
254 // value. It is included for reference though.
255 //
256 // Sign will set the request's Body to be the `body` parameter passed in. If
257 // the body is not already an io.ReadCloser, it will be wrapped within one. If
258 // a `nil` body parameter passed to Sign, the request's Body field will be
259 // also set to nil. Its important to note that this functionality will not
260 // change the request's ContentLength of the request.
261 //
262 // Sign differs from Presign in that it will sign the request using HTTP
263 // header values. This type of signing is intended for http.Request values that
264 // will not be shared, or are shared in a way the header values on the request
265 // will not be lost.
266 //
267 // The requests body is an io.ReadSeeker so the SHA256 of the body can be
268 // generated. To bypass the signer computing the hash you can set the
269 // "X-Amz-Content-Sha256" header with a precomputed value. The signer will
270 // only compute the hash if the request header value is empty.
271 func (v4 Signer) Sign(r *http.Request, body io.ReadSeeker, service, region string, signTime time.Time) (http.Header, error) {
272 return v4.signWithBody(r, body, service, region, 0, signTime)
273 }
274
275 // Presign signs AWS v4 requests with the provided body, service name, region
276 // the request is made to, and time the request is signed at. The signTime
277 // allows you to specify that a request is signed for the future, and cannot
278 // be used until then.
279 //
280 // Returns a list of HTTP headers that were included in the signature or an
281 // error if signing the request failed. For presigned requests these headers
282 // and their values must be included on the HTTP request when it is made. This
283 // is helpful to know what header values need to be shared with the party the
284 // presigned request will be distributed to.
285 //
286 // Presign differs from Sign in that it will sign the request using query string
287 // instead of header values. This allows you to share the Presigned Request's
288 // URL with third parties, or distribute it throughout your system with minimal
289 // dependencies.
290 //
291 // Presign also takes an exp value which is the duration the
292 // signed request will be valid after the signing time. This is allows you to
293 // set when the request will expire.
294 //
295 // The requests body is an io.ReadSeeker so the SHA256 of the body can be
296 // generated. To bypass the signer computing the hash you can set the
297 // "X-Amz-Content-Sha256" header with a precomputed value. The signer will
298 // only compute the hash if the request header value is empty.
299 //
300 // Presigning a S3 request will not compute the body's SHA256 hash by default.
301 // This is done due to the general use case for S3 presigned URLs is to share
302 // PUT/GET capabilities. If you would like to include the body's SHA256 in the
303 // presigned request's signature you can set the "X-Amz-Content-Sha256"
304 // HTTP header and that will be included in the request's signature.
305 func (v4 Signer) Presign(r *http.Request, body io.ReadSeeker, service, region string, exp time.Duration, signTime time.Time) (http.Header, error) {
306 return v4.signWithBody(r, body, service, region, exp, signTime)
307 }
308
309 func (v4 Signer) signWithBody(r *http.Request, body io.ReadSeeker, service, region string, exp time.Duration, signTime time.Time) (http.Header, error) {
310 currentTimeFn := v4.currentTimeFn
311 if currentTimeFn == nil {
312 currentTimeFn = time.Now
313 }
314
315 ctx := &signingCtx{
316 Request: r,
317 Body: body,
318 Query: r.URL.Query(),
319 Time: signTime,
320 ExpireTime: exp,
321 isPresign: exp != 0,
322 ServiceName: service,
323 Region: region,
324 DisableURIPathEscaping: v4.DisableURIPathEscaping,
325 unsignedPayload: v4.UnsignedPayload,
326 }
327
328 for key := range ctx.Query {
329 sort.Strings(ctx.Query[key])
330 }
331
332 if ctx.isRequestSigned() {
333 ctx.Time = currentTimeFn()
334 ctx.handlePresignRemoval()
335 }
336
337 var err error
338 ctx.credValues, err = v4.Credentials.Get()
339 if err != nil {
340 return http.Header{}, err
341 }
342
343 ctx.assignAmzQueryValues()
344 ctx.build(v4.DisableHeaderHoisting)
345
346 // If the request is not presigned the body should be attached to it. This
347 // prevents the confusion of wanting to send a signed request without
348 // the body the request was signed for attached.
349 if !(v4.DisableRequestBodyOverwrite || ctx.isPresign) {
350 var reader io.ReadCloser
351 if body != nil {
352 var ok bool
353 if reader, ok = body.(io.ReadCloser); !ok {
354 reader = ioutil.NopCloser(body)
355 }
356 }
357 r.Body = reader
358 }
359
360 if v4.Debug.Matches(aws.LogDebugWithSigning) {
361 v4.logSigningInfo(ctx)
362 }
363
364 return ctx.SignedHeaderVals, nil
365 }
366
367 func (ctx *signingCtx) handlePresignRemoval() {
368 if !ctx.isPresign {
369 return
370 }
371
372 // The credentials have expired for this request. The current signing
373 // is invalid, and needs to be request because the request will fail.
374 ctx.removePresign()
375
376 // Update the request's query string to ensure the values stays in
377 // sync in the case retrieving the new credentials fails.
378 ctx.Request.URL.RawQuery = ctx.Query.Encode()
379 }
380
381 func (ctx *signingCtx) assignAmzQueryValues() {
382 if ctx.isPresign {
383 ctx.Query.Set("X-Amz-Algorithm", authHeaderPrefix)
384 if ctx.credValues.SessionToken != "" {
385 ctx.Query.Set("X-Amz-Security-Token", ctx.credValues.SessionToken)
386 } else {
387 ctx.Query.Del("X-Amz-Security-Token")
388 }
389
390 return
391 }
392
393 if ctx.credValues.SessionToken != "" {
394 ctx.Request.Header.Set("X-Amz-Security-Token", ctx.credValues.SessionToken)
395 }
396 }
397
398 // SignRequestHandler is a named request handler the SDK will use to sign
399 // service client request with using the V4 signature.
400 var SignRequestHandler = request.NamedHandler{
401 Name: "v4.SignRequestHandler", Fn: SignSDKRequest,
402 }
403
404 // SignSDKRequest signs an AWS request with the V4 signature. This
405 // request handler is bested used only with the SDK's built in service client's
406 // API operation requests.
407 //
408 // This function should not be used on its on its own, but in conjunction with
409 // an AWS service client's API operation call. To sign a standalone request
410 // not created by a service client's API operation method use the "Sign" or
411 // "Presign" functions of the "Signer" type.
412 //
413 // If the credentials of the request's config are set to
414 // credentials.AnonymousCredentials the request will not be signed.
415 func SignSDKRequest(req *request.Request) {
416 signSDKRequestWithCurrTime(req, time.Now)
417 }
418
419 // BuildNamedHandler will build a generic handler for signing.
420 func BuildNamedHandler(name string, opts ...func(*Signer)) request.NamedHandler {
421 return request.NamedHandler{
422 Name: name,
423 Fn: func(req *request.Request) {
424 signSDKRequestWithCurrTime(req, time.Now, opts...)
425 },
426 }
427 }
428
429 func signSDKRequestWithCurrTime(req *request.Request, curTimeFn func() time.Time, opts ...func(*Signer)) {
430 // If the request does not need to be signed ignore the signing of the
431 // request if the AnonymousCredentials object is used.
432 if req.Config.Credentials == credentials.AnonymousCredentials {
433 return
434 }
435
436 region := req.ClientInfo.SigningRegion
437 if region == "" {
438 region = aws.StringValue(req.Config.Region)
439 }
440
441 name := req.ClientInfo.SigningName
442 if name == "" {
443 name = req.ClientInfo.ServiceName
444 }
445
446 v4 := NewSigner(req.Config.Credentials, func(v4 *Signer) {
447 v4.Debug = req.Config.LogLevel.Value()
448 v4.Logger = req.Config.Logger
449 v4.DisableHeaderHoisting = req.NotHoist
450 v4.currentTimeFn = curTimeFn
451 if name == "s3" {
452 // S3 service should not have any escaping applied
453 v4.DisableURIPathEscaping = true
454 }
455 // Prevents setting the HTTPRequest's Body. Since the Body could be
456 // wrapped in a custom io.Closer that we do not want to be stompped
457 // on top of by the signer.
458 v4.DisableRequestBodyOverwrite = true
459 })
460
461 for _, opt := range opts {
462 opt(v4)
463 }
464
465 signingTime := req.Time
466 if !req.LastSignedAt.IsZero() {
467 signingTime = req.LastSignedAt
468 }
469
470 signedHeaders, err := v4.signWithBody(req.HTTPRequest, req.GetBody(),
471 name, region, req.ExpireTime, signingTime,
472 )
473 if err != nil {
474 req.Error = err
475 req.SignedHeaderVals = nil
476 return
477 }
478
479 req.SignedHeaderVals = signedHeaders
480 req.LastSignedAt = curTimeFn()
481 }
482
483 const logSignInfoMsg = `DEBUG: Request Signature:
484 ---[ CANONICAL STRING ]-----------------------------
485 %s
486 ---[ STRING TO SIGN ]--------------------------------
487 %s%s
488 -----------------------------------------------------`
489 const logSignedURLMsg = `
490 ---[ SIGNED URL ]------------------------------------
491 %s`
492
493 func (v4 *Signer) logSigningInfo(ctx *signingCtx) {
494 signedURLMsg := ""
495 if ctx.isPresign {
496 signedURLMsg = fmt.Sprintf(logSignedURLMsg, ctx.Request.URL.String())
497 }
498 msg := fmt.Sprintf(logSignInfoMsg, ctx.canonicalString, ctx.stringToSign, signedURLMsg)
499 v4.Logger.Log(msg)
500 }
501
502 func (ctx *signingCtx) build(disableHeaderHoisting bool) {
503 ctx.buildTime() // no depends
504 ctx.buildCredentialString() // no depends
505
506 unsignedHeaders := ctx.Request.Header
507 if ctx.isPresign {
508 if !disableHeaderHoisting {
509 urlValues := url.Values{}
510 urlValues, unsignedHeaders = buildQuery(allowedQueryHoisting, unsignedHeaders) // no depends
511 for k := range urlValues {
512 ctx.Query[k] = urlValues[k]
513 }
514 }
515 }
516
517 ctx.buildBodyDigest()
518 ctx.buildCanonicalHeaders(ignoredHeaders, unsignedHeaders)
519 ctx.buildCanonicalString() // depends on canon headers / signed headers
520 ctx.buildStringToSign() // depends on canon string
521 ctx.buildSignature() // depends on string to sign
522
523 if ctx.isPresign {
524 ctx.Request.URL.RawQuery += "&X-Amz-Signature=" + ctx.signature
525 } else {
526 parts := []string{
527 authHeaderPrefix + " Credential=" + ctx.credValues.AccessKeyID + "/" + ctx.credentialString,
528 "SignedHeaders=" + ctx.signedHeaders,
529 "Signature=" + ctx.signature,
530 }
531 ctx.Request.Header.Set("Authorization", strings.Join(parts, ", "))
532 }
533 }
534
535 func (ctx *signingCtx) buildTime() {
536 ctx.formattedTime = ctx.Time.UTC().Format(timeFormat)
537 ctx.formattedShortTime = ctx.Time.UTC().Format(shortTimeFormat)
538
539 if ctx.isPresign {
540 duration := int64(ctx.ExpireTime / time.Second)
541 ctx.Query.Set("X-Amz-Date", ctx.formattedTime)
542 ctx.Query.Set("X-Amz-Expires", strconv.FormatInt(duration, 10))
543 } else {
544 ctx.Request.Header.Set("X-Amz-Date", ctx.formattedTime)
545 }
546 }
547
548 func (ctx *signingCtx) buildCredentialString() {
549 ctx.credentialString = strings.Join([]string{
550 ctx.formattedShortTime,
551 ctx.Region,
552 ctx.ServiceName,
553 "aws4_request",
554 }, "/")
555
556 if ctx.isPresign {
557 ctx.Query.Set("X-Amz-Credential", ctx.credValues.AccessKeyID+"/"+ctx.credentialString)
558 }
559 }
560
561 func buildQuery(r rule, header http.Header) (url.Values, http.Header) {
562 query := url.Values{}
563 unsignedHeaders := http.Header{}
564 for k, h := range header {
565 if r.IsValid(k) {
566 query[k] = h
567 } else {
568 unsignedHeaders[k] = h
569 }
570 }
571
572 return query, unsignedHeaders
573 }
574 func (ctx *signingCtx) buildCanonicalHeaders(r rule, header http.Header) {
575 var headers []string
576 headers = append(headers, "host")
577 for k, v := range header {
578 canonicalKey := http.CanonicalHeaderKey(k)
579 if !r.IsValid(canonicalKey) {
580 continue // ignored header
581 }
582 if ctx.SignedHeaderVals == nil {
583 ctx.SignedHeaderVals = make(http.Header)
584 }
585
586 lowerCaseKey := strings.ToLower(k)
587 if _, ok := ctx.SignedHeaderVals[lowerCaseKey]; ok {
588 // include additional values
589 ctx.SignedHeaderVals[lowerCaseKey] = append(ctx.SignedHeaderVals[lowerCaseKey], v...)
590 continue
591 }
592
593 headers = append(headers, lowerCaseKey)
594 ctx.SignedHeaderVals[lowerCaseKey] = v
595 }
596 sort.Strings(headers)
597
598 ctx.signedHeaders = strings.Join(headers, ";")
599
600 if ctx.isPresign {
601 ctx.Query.Set("X-Amz-SignedHeaders", ctx.signedHeaders)
602 }
603
604 headerValues := make([]string, len(headers))
605 for i, k := range headers {
606 if k == "host" {
607 headerValues[i] = "host:" + ctx.Request.URL.Host
608 } else {
609 headerValues[i] = k + ":" +
610 strings.Join(ctx.SignedHeaderVals[k], ",")
611 }
612 }
613
614 ctx.canonicalHeaders = strings.Join(stripExcessSpaces(headerValues), "\n")
615 }
616
617 func (ctx *signingCtx) buildCanonicalString() {
618 ctx.Request.URL.RawQuery = strings.Replace(ctx.Query.Encode(), "+", "%20", -1)
619
620 uri := getURIPath(ctx.Request.URL)
621
622 if !ctx.DisableURIPathEscaping {
623 uri = rest.EscapePath(uri, false)
624 }
625
626 ctx.canonicalString = strings.Join([]string{
627 ctx.Request.Method,
628 uri,
629 ctx.Request.URL.RawQuery,
630 ctx.canonicalHeaders + "\n",
631 ctx.signedHeaders,
632 ctx.bodyDigest,
633 }, "\n")
634 }
635
636 func (ctx *signingCtx) buildStringToSign() {
637 ctx.stringToSign = strings.Join([]string{
638 authHeaderPrefix,
639 ctx.formattedTime,
640 ctx.credentialString,
641 hex.EncodeToString(makeSha256([]byte(ctx.canonicalString))),
642 }, "\n")
643 }
644
645 func (ctx *signingCtx) buildSignature() {
646 secret := ctx.credValues.SecretAccessKey
647 date := makeHmac([]byte("AWS4"+secret), []byte(ctx.formattedShortTime))
648 region := makeHmac(date, []byte(ctx.Region))
649 service := makeHmac(region, []byte(ctx.ServiceName))
650 credentials := makeHmac(service, []byte("aws4_request"))
651 signature := makeHmac(credentials, []byte(ctx.stringToSign))
652 ctx.signature = hex.EncodeToString(signature)
653 }
654
655 func (ctx *signingCtx) buildBodyDigest() {
656 hash := ctx.Request.Header.Get("X-Amz-Content-Sha256")
657 if hash == "" {
658 if ctx.unsignedPayload || (ctx.isPresign && ctx.ServiceName == "s3") {
659 hash = "UNSIGNED-PAYLOAD"
660 } else if ctx.Body == nil {
661 hash = emptyStringSHA256
662 } else {
663 hash = hex.EncodeToString(makeSha256Reader(ctx.Body))
664 }
665 if ctx.unsignedPayload || ctx.ServiceName == "s3" || ctx.ServiceName == "glacier" {
666 ctx.Request.Header.Set("X-Amz-Content-Sha256", hash)
667 }
668 }
669 ctx.bodyDigest = hash
670 }
671
672 // isRequestSigned returns if the request is currently signed or presigned
673 func (ctx *signingCtx) isRequestSigned() bool {
674 if ctx.isPresign && ctx.Query.Get("X-Amz-Signature") != "" {
675 return true
676 }
677 if ctx.Request.Header.Get("Authorization") != "" {
678 return true
679 }
680
681 return false
682 }
683
684 // unsign removes signing flags for both signed and presigned requests.
685 func (ctx *signingCtx) removePresign() {
686 ctx.Query.Del("X-Amz-Algorithm")
687 ctx.Query.Del("X-Amz-Signature")
688 ctx.Query.Del("X-Amz-Security-Token")
689 ctx.Query.Del("X-Amz-Date")
690 ctx.Query.Del("X-Amz-Expires")
691 ctx.Query.Del("X-Amz-Credential")
692 ctx.Query.Del("X-Amz-SignedHeaders")
693 }
694
695 func makeHmac(key []byte, data []byte) []byte {
696 hash := hmac.New(sha256.New, key)
697 hash.Write(data)
698 return hash.Sum(nil)
699 }
700
701 func makeSha256(data []byte) []byte {
702 hash := sha256.New()
703 hash.Write(data)
704 return hash.Sum(nil)
705 }
706
707 func makeSha256Reader(reader io.ReadSeeker) []byte {
708 hash := sha256.New()
709 start, _ := reader.Seek(0, 1)
710 defer reader.Seek(start, 0)
711
712 io.Copy(hash, reader)
713 return hash.Sum(nil)
714 }
715
716 const doubleSpaces = " "
717
718 var doubleSpaceBytes = []byte(doubleSpaces)
719
720 func stripExcessSpaces(headerVals []string) []string {
721 vals := make([]string, len(headerVals))
722 for i, str := range headerVals {
723 // Trim leading and trailing spaces
724 trimmed := strings.TrimSpace(str)
725
726 idx := strings.Index(trimmed, doubleSpaces)
727 var buf []byte
728 for idx > -1 {
729 // Multiple adjacent spaces found
730 if buf == nil {
731 // first time create the buffer
732 buf = []byte(trimmed)
733 }
734
735 stripToIdx := -1
736 for j := idx + 1; j < len(buf); j++ {
737 if buf[j] != ' ' {
738 buf = append(buf[:idx+1], buf[j:]...)
739 stripToIdx = j
740 break
741 }
742 }
743
744 if stripToIdx >= 0 {
745 idx = bytes.Index(buf[stripToIdx:], doubleSpaceBytes)
746 if idx >= 0 {
747 idx += stripToIdx
748 }
749 } else {
750 idx = -1
751 }
752 }
753
754 if buf != nil {
755 vals[i] = string(buf)
756 } else {
757 vals[i] = trimmed
758 }
759 }
760 return vals
761 }