Package: v4

import "../ibm-cos-sdk-go/aws/signer/v4"

Overview

Package v4 implements signing for AWS V4 signer

Provides request signing for request that need to be signed with AWS V4 Signatures.

Standalone Signer

Generally using the signer outside of the SDK should not require any additional logic when using Go v1.5 or higher. The signer does this by taking advantage of the URL.EscapedPath method. If your request URI requires additional escaping you may need to use the URL.Opaque to define what the raw URI should be sent to the service as.

The signer will first check the URL.Opaque field, and use its value if set. The signer does require the URL.Opaque field to be set in the form of:

"///" // e.g. "//example.com/some/path"

The leading “//” and hostname are required or the URL.Opaque escaping will not work correctly.

If URL.Opaque is not set the signer will fallback to the URL.EscapedPath() method and using the returned value. If you're using Go v1.4 you must set URL.Opaque if the URI path needs escaping. If URL.Opaque is not set with Go v1.5 the signer will fallback to URL.Path.

AWS v4 signature validation requires that the canonical string's URI path element must be the URI escaped form of the HTTP request's path. docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html

The Go HTTP client will perform escaping automatically on the request. Some of these escaping may cause signature validation errors because the HTTP request differs from the URI path or query that the signature was generated. golang.org/pkg/net/url/#URL.EscapedPath

Because of this, it is recommended that when using the signer outside of the SDK that explicitly escaping the request prior to being signed is preferable, and will help prevent signature validation errors. This can be done by setting the URL.Opaque or URL.RawPath. The SDK will use URL.Opaque first and then call URL.EscapedPath() if Opaque is not set.

If signing a request intended for HTTP2 server, and you're using Go 1.6.2 through 1.7.4 you should use the URL.RawPath as the pre-escaped form of the request URL. github.com/golang/go/issues/16847 points to a bug in Go pre 1.8 that fails to make HTTP2 requests using absolute URL in the HTTP message. URL.Opaque generally will force Go to make requests with absolute URL. URL.RawPath does not do this, but RawPath must be a valid escaping of Path or url.EscapedPath will ignore the RawPath escaping.

Test TestStandaloneSign provides a complete example of using the signer outside of the SDK and pre-escaping the URI path.

Variables

var SignRequestHandler = writable

SignRequestHandler is a named request handler the SDK will use to sign service client request with using the V4 signature.

Value:

Type Summary collapse

Function Summary collapse

Function Details

func BuildNamedHandler(name string, opts ...func(*Signer)) request.NamedHandler

BuildNamedHandler will build a generic handler for signing.



433
434
435
436
437
438
439
440
// File 'aws/signer/v4/v4.go', line 433

func BuildNamedHandler(name string, opts ...func(*Signer)) request.NamedHandler { return request.NamedHandler{ Name: name, Fn: func(req *request.Request) { SignSDKRequestWithCurrentTime(req, time.Now, opts...) }, } }

func GetSignedRequestSignature(r *http.Request) ([]byte, error)

GetSignedRequestSignature attempts to extract the signature of the request. Returning an error if the request is unsigned, or unable to extract the signature.



555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
// File 'aws/signer/v4/v4.go', line 555

func GetSignedRequestSignature(r *http.Request) ([]byte, error) { if auth := r.Header.Get(authorizationHeader); len(auth) != 0 { ps := strings.Split(auth, ", ") for _, p := range ps { if idx := strings.Index(p, authHeaderSignatureElem); idx >= 0 { sig := p[len(authHeaderSignatureElem):] if len(sig) == 0 { return nil, fmt.Errorf("invalid request signature authorization header") } return hex.DecodeString(sig) } } } if sig := r.URL.Query().Get("X-Amz-Signature"); len(sig) != 0 { return hex.DecodeString(sig) } return nil, fmt.Errorf("request not signed") }

func NewSigner(credentials *credentials.Credentials, options ...func(*Signer)) *Signer

NewSigner returns a Signer pointer configured with the credentials and optional option values provided. If not options are provided the Signer will use its default configuration.



214
215
216
217
218
219
220
221
222
223
224
// File 'aws/signer/v4/v4.go', line 214

func NewSigner(credentials *credentials.Credentials, options ...func(*Signer)) *Signer { v4 := &Signer{ Credentials: credentials, } for _, option := range options { option(v4) } return v4 }

func SignSDKRequest(req *request.Request)

SignSDKRequest signs an AWS request with the V4 signature. This request handler should only be used with the SDK's built in service client's API operation requests.

This function should not be used on its own, but in conjunction with an AWS service client's API operation call. To sign a standalone request not created by a service client's API operation method use the “Sign” or “Presign” functions of the “Signer” type.

If the credentials of the request's config are set to credentials.AnonymousCredentials the request will not be signed.



428
429
430
// File 'aws/signer/v4/v4.go', line 428

func SignSDKRequest(req *request.Request) { SignSDKRequestWithCurrentTime(req, time.Now) }

func SignSDKRequestWithCurrentTime(req *request.Request, curTimeFn func() time.Time, opts ...func(*Signer))

SignSDKRequestWithCurrentTime will sign the SDK's request using the time function passed in. Behaves the same as SignSDKRequest with the exception the request is signed with the value returned by the current time function.



445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
// File 'aws/signer/v4/v4.go', line 445

func SignSDKRequestWithCurrentTime(req *request.Request, curTimeFn func() time.Time, opts ...func(*Signer)) { // If the request does not need to be signed ignore the signing of the // request if the AnonymousCredentials object is used. if req.Config.Credentials == credentials.AnonymousCredentials { return } region := req.ClientInfo.SigningRegion if region == "" { region = aws.StringValue(req.Config.Region) } name := req.ClientInfo.SigningName if name == "" { name = req.ClientInfo.ServiceName } v4 := NewSigner(req.Config.Credentials, func(v4 *Signer) { v4.Debug = req.Config.LogLevel.Value() v4.Logger = req.Config.Logger v4.DisableHeaderHoisting = req.NotHoist v4.currentTimeFn = curTimeFn if name == "s3" { // S3 service should not have any escaping applied v4.DisableURIPathEscaping = true } // Prevents setting the HTTPRequest's Body. Since the Body could be // wrapped in a custom io.Closer that we do not want to be stompped // on top of by the signer. v4.DisableRequestBodyOverwrite = true }) for _, opt := range opts { opt(v4) } curTime := curTimeFn() signedHeaders, err := v4.signWithBody(req.HTTPRequest, req.GetBody(), name, region, req.ExpireTime, req.ExpireTime > 0, curTime, ) if err != nil { req.Error = err req.SignedHeaderVals = nil return } req.SignedHeaderVals = signedHeaders req.LastSignedAt = curTime }

func WithUnsignedPayload(v4 *Signer)

WithUnsignedPayload will enable and set the UnsignedPayload field to true of the signer.



4
5
6
// File 'aws/signer/v4/options.go', line 4

func WithUnsignedPayload(v4 *Signer) { v4.UnsignedPayload = true }