]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/aws/aws-sdk-go/aws/signer/v4/v4.go
update vendor and go.mod
[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 fails 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 "crypto/hmac"
59 "crypto/sha256"
60 "encoding/hex"
61 "fmt"
62 "io"
63 "io/ioutil"
64 "net/http"
65 "net/url"
66 "sort"
67 "strconv"
68 "strings"
69 "time"
70
71 "github.com/aws/aws-sdk-go/aws"
72 "github.com/aws/aws-sdk-go/aws/credentials"
73 "github.com/aws/aws-sdk-go/aws/request"
74 "github.com/aws/aws-sdk-go/internal/sdkio"
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-Tagging": struct{}{},
138 "X-Amz-Website-Redirect-Location": struct{}{},
139 "X-Amz-Content-Sha256": struct{}{},
140 },
141 },
142 patterns{"X-Amz-Meta-"},
143 }
144
145 // allowedHoisting is a whitelist for build query headers. The boolean value
146 // represents whether or not it is a pattern.
147 var allowedQueryHoisting = inclusiveRules{
148 blacklist{requiredSignedHeaders},
149 patterns{"X-Amz-"},
150 }
151
152 // Signer applies AWS v4 signing to given request. Use this to sign requests
153 // that need to be signed with AWS V4 Signatures.
154 type Signer struct {
155 // The authentication credentials the request will be signed against.
156 // This value must be set to sign requests.
157 Credentials *credentials.Credentials
158
159 // Sets the log level the signer should use when reporting information to
160 // the logger. If the logger is nil nothing will be logged. See
161 // aws.LogLevelType for more information on available logging levels
162 //
163 // By default nothing will be logged.
164 Debug aws.LogLevelType
165
166 // The logger loging information will be written to. If there the logger
167 // is nil, nothing will be logged.
168 Logger aws.Logger
169
170 // Disables the Signer's moving HTTP header key/value pairs from the HTTP
171 // request header to the request's query string. This is most commonly used
172 // with pre-signed requests preventing headers from being added to the
173 // request's query string.
174 DisableHeaderHoisting bool
175
176 // Disables the automatic escaping of the URI path of the request for the
177 // siganture's canonical string's path. For services that do not need additional
178 // escaping then use this to disable the signer escaping the path.
179 //
180 // S3 is an example of a service that does not need additional escaping.
181 //
182 // http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
183 DisableURIPathEscaping bool
184
185 // Disables the automatical setting of the HTTP request's Body field with the
186 // io.ReadSeeker passed in to the signer. This is useful if you're using a
187 // custom wrapper around the body for the io.ReadSeeker and want to preserve
188 // the Body value on the Request.Body.
189 //
190 // This does run the risk of signing a request with a body that will not be
191 // sent in the request. Need to ensure that the underlying data of the Body
192 // values are the same.
193 DisableRequestBodyOverwrite bool
194
195 // currentTimeFn returns the time value which represents the current time.
196 // This value should only be used for testing. If it is nil the default
197 // time.Now will be used.
198 currentTimeFn func() time.Time
199
200 // UnsignedPayload will prevent signing of the payload. This will only
201 // work for services that have support for this.
202 UnsignedPayload bool
203 }
204
205 // NewSigner returns a Signer pointer configured with the credentials and optional
206 // option values provided. If not options are provided the Signer will use its
207 // default configuration.
208 func NewSigner(credentials *credentials.Credentials, options ...func(*Signer)) *Signer {
209 v4 := &Signer{
210 Credentials: credentials,
211 }
212
213 for _, option := range options {
214 option(v4)
215 }
216
217 return v4
218 }
219
220 type signingCtx struct {
221 ServiceName string
222 Region string
223 Request *http.Request
224 Body io.ReadSeeker
225 Query url.Values
226 Time time.Time
227 ExpireTime time.Duration
228 SignedHeaderVals http.Header
229
230 DisableURIPathEscaping bool
231
232 credValues credentials.Value
233 isPresign bool
234 formattedTime string
235 formattedShortTime string
236 unsignedPayload bool
237
238 bodyDigest string
239 signedHeaders string
240 canonicalHeaders string
241 canonicalString string
242 credentialString string
243 stringToSign string
244 signature string
245 authorization string
246 }
247
248 // Sign signs AWS v4 requests with the provided body, service name, region the
249 // request is made to, and time the request is signed at. The signTime allows
250 // you to specify that a request is signed for the future, and cannot be
251 // used until then.
252 //
253 // Returns a list of HTTP headers that were included in the signature or an
254 // error if signing the request failed. Generally for signed requests this value
255 // is not needed as the full request context will be captured by the http.Request
256 // value. It is included for reference though.
257 //
258 // Sign will set the request's Body to be the `body` parameter passed in. If
259 // the body is not already an io.ReadCloser, it will be wrapped within one. If
260 // a `nil` body parameter passed to Sign, the request's Body field will be
261 // also set to nil. Its important to note that this functionality will not
262 // change the request's ContentLength of the request.
263 //
264 // Sign differs from Presign in that it will sign the request using HTTP
265 // header values. This type of signing is intended for http.Request values that
266 // will not be shared, or are shared in a way the header values on the request
267 // will not be lost.
268 //
269 // The requests body is an io.ReadSeeker so the SHA256 of the body can be
270 // generated. To bypass the signer computing the hash you can set the
271 // "X-Amz-Content-Sha256" header with a precomputed value. The signer will
272 // only compute the hash if the request header value is empty.
273 func (v4 Signer) Sign(r *http.Request, body io.ReadSeeker, service, region string, signTime time.Time) (http.Header, error) {
274 return v4.signWithBody(r, body, service, region, 0, false, signTime)
275 }
276
277 // Presign signs AWS v4 requests with the provided body, service name, region
278 // the request is made to, and time the request is signed at. The signTime
279 // allows you to specify that a request is signed for the future, and cannot
280 // be used until then.
281 //
282 // Returns a list of HTTP headers that were included in the signature or an
283 // error if signing the request failed. For presigned requests these headers
284 // and their values must be included on the HTTP request when it is made. This
285 // is helpful to know what header values need to be shared with the party the
286 // presigned request will be distributed to.
287 //
288 // Presign differs from Sign in that it will sign the request using query string
289 // instead of header values. This allows you to share the Presigned Request's
290 // URL with third parties, or distribute it throughout your system with minimal
291 // dependencies.
292 //
293 // Presign also takes an exp value which is the duration the
294 // signed request will be valid after the signing time. This is allows you to
295 // set when the request will expire.
296 //
297 // The requests body is an io.ReadSeeker so the SHA256 of the body can be
298 // generated. To bypass the signer computing the hash you can set the
299 // "X-Amz-Content-Sha256" header with a precomputed value. The signer will
300 // only compute the hash if the request header value is empty.
301 //
302 // Presigning a S3 request will not compute the body's SHA256 hash by default.
303 // This is done due to the general use case for S3 presigned URLs is to share
304 // PUT/GET capabilities. If you would like to include the body's SHA256 in the
305 // presigned request's signature you can set the "X-Amz-Content-Sha256"
306 // HTTP header and that will be included in the request's signature.
307 func (v4 Signer) Presign(r *http.Request, body io.ReadSeeker, service, region string, exp time.Duration, signTime time.Time) (http.Header, error) {
308 return v4.signWithBody(r, body, service, region, exp, true, signTime)
309 }
310
311 func (v4 Signer) signWithBody(r *http.Request, body io.ReadSeeker, service, region string, exp time.Duration, isPresign bool, signTime time.Time) (http.Header, error) {
312 currentTimeFn := v4.currentTimeFn
313 if currentTimeFn == nil {
314 currentTimeFn = time.Now
315 }
316
317 ctx := &signingCtx{
318 Request: r,
319 Body: body,
320 Query: r.URL.Query(),
321 Time: signTime,
322 ExpireTime: exp,
323 isPresign: isPresign,
324 ServiceName: service,
325 Region: region,
326 DisableURIPathEscaping: v4.DisableURIPathEscaping,
327 unsignedPayload: v4.UnsignedPayload,
328 }
329
330 for key := range ctx.Query {
331 sort.Strings(ctx.Query[key])
332 }
333
334 if ctx.isRequestSigned() {
335 ctx.Time = currentTimeFn()
336 ctx.handlePresignRemoval()
337 }
338
339 var err error
340 ctx.credValues, err = v4.Credentials.Get()
341 if err != nil {
342 return http.Header{}, err
343 }
344
345 ctx.sanitizeHostForHeader()
346 ctx.assignAmzQueryValues()
347 if err := ctx.build(v4.DisableHeaderHoisting); err != nil {
348 return nil, err
349 }
350
351 // If the request is not presigned the body should be attached to it. This
352 // prevents the confusion of wanting to send a signed request without
353 // the body the request was signed for attached.
354 if !(v4.DisableRequestBodyOverwrite || ctx.isPresign) {
355 var reader io.ReadCloser
356 if body != nil {
357 var ok bool
358 if reader, ok = body.(io.ReadCloser); !ok {
359 reader = ioutil.NopCloser(body)
360 }
361 }
362 r.Body = reader
363 }
364
365 if v4.Debug.Matches(aws.LogDebugWithSigning) {
366 v4.logSigningInfo(ctx)
367 }
368
369 return ctx.SignedHeaderVals, nil
370 }
371
372 func (ctx *signingCtx) sanitizeHostForHeader() {
373 request.SanitizeHostForHeader(ctx.Request)
374 }
375
376 func (ctx *signingCtx) handlePresignRemoval() {
377 if !ctx.isPresign {
378 return
379 }
380
381 // The credentials have expired for this request. The current signing
382 // is invalid, and needs to be request because the request will fail.
383 ctx.removePresign()
384
385 // Update the request's query string to ensure the values stays in
386 // sync in the case retrieving the new credentials fails.
387 ctx.Request.URL.RawQuery = ctx.Query.Encode()
388 }
389
390 func (ctx *signingCtx) assignAmzQueryValues() {
391 if ctx.isPresign {
392 ctx.Query.Set("X-Amz-Algorithm", authHeaderPrefix)
393 if ctx.credValues.SessionToken != "" {
394 ctx.Query.Set("X-Amz-Security-Token", ctx.credValues.SessionToken)
395 } else {
396 ctx.Query.Del("X-Amz-Security-Token")
397 }
398
399 return
400 }
401
402 if ctx.credValues.SessionToken != "" {
403 ctx.Request.Header.Set("X-Amz-Security-Token", ctx.credValues.SessionToken)
404 }
405 }
406
407 // SignRequestHandler is a named request handler the SDK will use to sign
408 // service client request with using the V4 signature.
409 var SignRequestHandler = request.NamedHandler{
410 Name: "v4.SignRequestHandler", Fn: SignSDKRequest,
411 }
412
413 // SignSDKRequest signs an AWS request with the V4 signature. This
414 // request handler should only be used with the SDK's built in service client's
415 // API operation requests.
416 //
417 // This function should not be used on its on its own, but in conjunction with
418 // an AWS service client's API operation call. To sign a standalone request
419 // not created by a service client's API operation method use the "Sign" or
420 // "Presign" functions of the "Signer" type.
421 //
422 // If the credentials of the request's config are set to
423 // credentials.AnonymousCredentials the request will not be signed.
424 func SignSDKRequest(req *request.Request) {
425 SignSDKRequestWithCurrentTime(req, time.Now)
426 }
427
428 // BuildNamedHandler will build a generic handler for signing.
429 func BuildNamedHandler(name string, opts ...func(*Signer)) request.NamedHandler {
430 return request.NamedHandler{
431 Name: name,
432 Fn: func(req *request.Request) {
433 SignSDKRequestWithCurrentTime(req, time.Now, opts...)
434 },
435 }
436 }
437
438 // SignSDKRequestWithCurrentTime will sign the SDK's request using the time
439 // function passed in. Behaves the same as SignSDKRequest with the exception
440 // the request is signed with the value returned by the current time function.
441 func SignSDKRequestWithCurrentTime(req *request.Request, curTimeFn func() time.Time, opts ...func(*Signer)) {
442 // If the request does not need to be signed ignore the signing of the
443 // request if the AnonymousCredentials object is used.
444 if req.Config.Credentials == credentials.AnonymousCredentials {
445 return
446 }
447
448 region := req.ClientInfo.SigningRegion
449 if region == "" {
450 region = aws.StringValue(req.Config.Region)
451 }
452
453 name := req.ClientInfo.SigningName
454 if name == "" {
455 name = req.ClientInfo.ServiceName
456 }
457
458 v4 := NewSigner(req.Config.Credentials, func(v4 *Signer) {
459 v4.Debug = req.Config.LogLevel.Value()
460 v4.Logger = req.Config.Logger
461 v4.DisableHeaderHoisting = req.NotHoist
462 v4.currentTimeFn = curTimeFn
463 if name == "s3" {
464 // S3 service should not have any escaping applied
465 v4.DisableURIPathEscaping = true
466 }
467 // Prevents setting the HTTPRequest's Body. Since the Body could be
468 // wrapped in a custom io.Closer that we do not want to be stompped
469 // on top of by the signer.
470 v4.DisableRequestBodyOverwrite = true
471 })
472
473 for _, opt := range opts {
474 opt(v4)
475 }
476
477 curTime := curTimeFn()
478 signedHeaders, err := v4.signWithBody(req.HTTPRequest, req.GetBody(),
479 name, region, req.ExpireTime, req.ExpireTime > 0, curTime,
480 )
481 if err != nil {
482 req.Error = err
483 req.SignedHeaderVals = nil
484 return
485 }
486
487 req.SignedHeaderVals = signedHeaders
488 req.LastSignedAt = curTime
489 }
490
491 const logSignInfoMsg = `DEBUG: Request Signature:
492 ---[ CANONICAL STRING ]-----------------------------
493 %s
494 ---[ STRING TO SIGN ]--------------------------------
495 %s%s
496 -----------------------------------------------------`
497 const logSignedURLMsg = `
498 ---[ SIGNED URL ]------------------------------------
499 %s`
500
501 func (v4 *Signer) logSigningInfo(ctx *signingCtx) {
502 signedURLMsg := ""
503 if ctx.isPresign {
504 signedURLMsg = fmt.Sprintf(logSignedURLMsg, ctx.Request.URL.String())
505 }
506 msg := fmt.Sprintf(logSignInfoMsg, ctx.canonicalString, ctx.stringToSign, signedURLMsg)
507 v4.Logger.Log(msg)
508 }
509
510 func (ctx *signingCtx) build(disableHeaderHoisting bool) error {
511 ctx.buildTime() // no depends
512 ctx.buildCredentialString() // no depends
513
514 if err := ctx.buildBodyDigest(); err != nil {
515 return err
516 }
517
518 unsignedHeaders := ctx.Request.Header
519 if ctx.isPresign {
520 if !disableHeaderHoisting {
521 urlValues := url.Values{}
522 urlValues, unsignedHeaders = buildQuery(allowedQueryHoisting, unsignedHeaders) // no depends
523 for k := range urlValues {
524 ctx.Query[k] = urlValues[k]
525 }
526 }
527 }
528
529 ctx.buildCanonicalHeaders(ignoredHeaders, unsignedHeaders)
530 ctx.buildCanonicalString() // depends on canon headers / signed headers
531 ctx.buildStringToSign() // depends on canon string
532 ctx.buildSignature() // depends on string to sign
533
534 if ctx.isPresign {
535 ctx.Request.URL.RawQuery += "&X-Amz-Signature=" + ctx.signature
536 } else {
537 parts := []string{
538 authHeaderPrefix + " Credential=" + ctx.credValues.AccessKeyID + "/" + ctx.credentialString,
539 "SignedHeaders=" + ctx.signedHeaders,
540 "Signature=" + ctx.signature,
541 }
542 ctx.Request.Header.Set("Authorization", strings.Join(parts, ", "))
543 }
544
545 return nil
546 }
547
548 func (ctx *signingCtx) buildTime() {
549 ctx.formattedTime = ctx.Time.UTC().Format(timeFormat)
550 ctx.formattedShortTime = ctx.Time.UTC().Format(shortTimeFormat)
551
552 if ctx.isPresign {
553 duration := int64(ctx.ExpireTime / time.Second)
554 ctx.Query.Set("X-Amz-Date", ctx.formattedTime)
555 ctx.Query.Set("X-Amz-Expires", strconv.FormatInt(duration, 10))
556 } else {
557 ctx.Request.Header.Set("X-Amz-Date", ctx.formattedTime)
558 }
559 }
560
561 func (ctx *signingCtx) buildCredentialString() {
562 ctx.credentialString = strings.Join([]string{
563 ctx.formattedShortTime,
564 ctx.Region,
565 ctx.ServiceName,
566 "aws4_request",
567 }, "/")
568
569 if ctx.isPresign {
570 ctx.Query.Set("X-Amz-Credential", ctx.credValues.AccessKeyID+"/"+ctx.credentialString)
571 }
572 }
573
574 func buildQuery(r rule, header http.Header) (url.Values, http.Header) {
575 query := url.Values{}
576 unsignedHeaders := http.Header{}
577 for k, h := range header {
578 if r.IsValid(k) {
579 query[k] = h
580 } else {
581 unsignedHeaders[k] = h
582 }
583 }
584
585 return query, unsignedHeaders
586 }
587 func (ctx *signingCtx) buildCanonicalHeaders(r rule, header http.Header) {
588 var headers []string
589 headers = append(headers, "host")
590 for k, v := range header {
591 canonicalKey := http.CanonicalHeaderKey(k)
592 if !r.IsValid(canonicalKey) {
593 continue // ignored header
594 }
595 if ctx.SignedHeaderVals == nil {
596 ctx.SignedHeaderVals = make(http.Header)
597 }
598
599 lowerCaseKey := strings.ToLower(k)
600 if _, ok := ctx.SignedHeaderVals[lowerCaseKey]; ok {
601 // include additional values
602 ctx.SignedHeaderVals[lowerCaseKey] = append(ctx.SignedHeaderVals[lowerCaseKey], v...)
603 continue
604 }
605
606 headers = append(headers, lowerCaseKey)
607 ctx.SignedHeaderVals[lowerCaseKey] = v
608 }
609 sort.Strings(headers)
610
611 ctx.signedHeaders = strings.Join(headers, ";")
612
613 if ctx.isPresign {
614 ctx.Query.Set("X-Amz-SignedHeaders", ctx.signedHeaders)
615 }
616
617 headerValues := make([]string, len(headers))
618 for i, k := range headers {
619 if k == "host" {
620 if ctx.Request.Host != "" {
621 headerValues[i] = "host:" + ctx.Request.Host
622 } else {
623 headerValues[i] = "host:" + ctx.Request.URL.Host
624 }
625 } else {
626 headerValues[i] = k + ":" +
627 strings.Join(ctx.SignedHeaderVals[k], ",")
628 }
629 }
630 stripExcessSpaces(headerValues)
631 ctx.canonicalHeaders = strings.Join(headerValues, "\n")
632 }
633
634 func (ctx *signingCtx) buildCanonicalString() {
635 ctx.Request.URL.RawQuery = strings.Replace(ctx.Query.Encode(), "+", "%20", -1)
636
637 uri := getURIPath(ctx.Request.URL)
638
639 if !ctx.DisableURIPathEscaping {
640 uri = rest.EscapePath(uri, false)
641 }
642
643 ctx.canonicalString = strings.Join([]string{
644 ctx.Request.Method,
645 uri,
646 ctx.Request.URL.RawQuery,
647 ctx.canonicalHeaders + "\n",
648 ctx.signedHeaders,
649 ctx.bodyDigest,
650 }, "\n")
651 }
652
653 func (ctx *signingCtx) buildStringToSign() {
654 ctx.stringToSign = strings.Join([]string{
655 authHeaderPrefix,
656 ctx.formattedTime,
657 ctx.credentialString,
658 hex.EncodeToString(makeSha256([]byte(ctx.canonicalString))),
659 }, "\n")
660 }
661
662 func (ctx *signingCtx) buildSignature() {
663 secret := ctx.credValues.SecretAccessKey
664 date := makeHmac([]byte("AWS4"+secret), []byte(ctx.formattedShortTime))
665 region := makeHmac(date, []byte(ctx.Region))
666 service := makeHmac(region, []byte(ctx.ServiceName))
667 credentials := makeHmac(service, []byte("aws4_request"))
668 signature := makeHmac(credentials, []byte(ctx.stringToSign))
669 ctx.signature = hex.EncodeToString(signature)
670 }
671
672 func (ctx *signingCtx) buildBodyDigest() error {
673 hash := ctx.Request.Header.Get("X-Amz-Content-Sha256")
674 if hash == "" {
675 includeSHA256Header := ctx.unsignedPayload ||
676 ctx.ServiceName == "s3" ||
677 ctx.ServiceName == "glacier"
678
679 s3Presign := ctx.isPresign && ctx.ServiceName == "s3"
680
681 if ctx.unsignedPayload || s3Presign {
682 hash = "UNSIGNED-PAYLOAD"
683 includeSHA256Header = !s3Presign
684 } else if ctx.Body == nil {
685 hash = emptyStringSHA256
686 } else {
687 if !aws.IsReaderSeekable(ctx.Body) {
688 return fmt.Errorf("cannot use unseekable request body %T, for signed request with body", ctx.Body)
689 }
690 hashBytes, err := makeSha256Reader(ctx.Body)
691 if err != nil {
692 return err
693 }
694 hash = hex.EncodeToString(hashBytes)
695 }
696
697 if includeSHA256Header {
698 ctx.Request.Header.Set("X-Amz-Content-Sha256", hash)
699 }
700 }
701 ctx.bodyDigest = hash
702
703 return nil
704 }
705
706 // isRequestSigned returns if the request is currently signed or presigned
707 func (ctx *signingCtx) isRequestSigned() bool {
708 if ctx.isPresign && ctx.Query.Get("X-Amz-Signature") != "" {
709 return true
710 }
711 if ctx.Request.Header.Get("Authorization") != "" {
712 return true
713 }
714
715 return false
716 }
717
718 // unsign removes signing flags for both signed and presigned requests.
719 func (ctx *signingCtx) removePresign() {
720 ctx.Query.Del("X-Amz-Algorithm")
721 ctx.Query.Del("X-Amz-Signature")
722 ctx.Query.Del("X-Amz-Security-Token")
723 ctx.Query.Del("X-Amz-Date")
724 ctx.Query.Del("X-Amz-Expires")
725 ctx.Query.Del("X-Amz-Credential")
726 ctx.Query.Del("X-Amz-SignedHeaders")
727 }
728
729 func makeHmac(key []byte, data []byte) []byte {
730 hash := hmac.New(sha256.New, key)
731 hash.Write(data)
732 return hash.Sum(nil)
733 }
734
735 func makeSha256(data []byte) []byte {
736 hash := sha256.New()
737 hash.Write(data)
738 return hash.Sum(nil)
739 }
740
741 func makeSha256Reader(reader io.ReadSeeker) (hashBytes []byte, err error) {
742 hash := sha256.New()
743 start, err := reader.Seek(0, sdkio.SeekCurrent)
744 if err != nil {
745 return nil, err
746 }
747 defer func() {
748 // ensure error is return if unable to seek back to start of payload.
749 _, err = reader.Seek(start, sdkio.SeekStart)
750 }()
751
752 // Use CopyN to avoid allocating the 32KB buffer in io.Copy for bodies
753 // smaller than 32KB. Fall back to io.Copy if we fail to determine the size.
754 size, err := aws.SeekerLen(reader)
755 if err != nil {
756 io.Copy(hash, reader)
757 } else {
758 io.CopyN(hash, reader, size)
759 }
760
761 return hash.Sum(nil), nil
762 }
763
764 const doubleSpace = " "
765
766 // stripExcessSpaces will rewrite the passed in slice's string values to not
767 // contain multiple side-by-side spaces.
768 func stripExcessSpaces(vals []string) {
769 var j, k, l, m, spaces int
770 for i, str := range vals {
771 // Trim trailing spaces
772 for j = len(str) - 1; j >= 0 && str[j] == ' '; j-- {
773 }
774
775 // Trim leading spaces
776 for k = 0; k < j && str[k] == ' '; k++ {
777 }
778 str = str[k : j+1]
779
780 // Strip multiple spaces.
781 j = strings.Index(str, doubleSpace)
782 if j < 0 {
783 vals[i] = str
784 continue
785 }
786
787 buf := []byte(str)
788 for k, m, l = j, j, len(buf); k < l; k++ {
789 if buf[k] == ' ' {
790 if spaces == 0 {
791 // First space.
792 buf[m] = buf[k]
793 m++
794 }
795 spaces++
796 } else {
797 // End of multiple spaces.
798 spaces = 0
799 buf[m] = buf[k]
800 m++
801 }
802 }
803
804 vals[i] = string(buf[:m])
805 }
806 }