summaryrefslogtreecommitdiffstats
path: root/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/signer/v4/v4.go
diff options
context:
space:
mode:
Diffstat (limited to 'Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/signer/v4/v4.go')
-rw-r--r--Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/signer/v4/v4.go296
1 files changed, 296 insertions, 0 deletions
diff --git a/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/signer/v4/v4.go b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/signer/v4/v4.go
new file mode 100644
index 000000000..d331475f4
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/signer/v4/v4.go
@@ -0,0 +1,296 @@
+package v4
+
+import (
+ "crypto/hmac"
+ "crypto/sha256"
+ "encoding/hex"
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/awslabs/aws-sdk-go/aws"
+)
+
+const (
+ authHeaderPrefix = "AWS4-HMAC-SHA256"
+ timeFormat = "20060102T150405Z"
+ shortTimeFormat = "20060102"
+)
+
+var ignoredHeaders = map[string]bool{
+ "Authorization": true,
+ "Content-Type": true,
+ "Content-Length": true,
+ "User-Agent": true,
+}
+
+type signer struct {
+ Request *http.Request
+ Time time.Time
+ ExpireTime time.Duration
+ ServiceName string
+ Region string
+ AccessKeyID string
+ SecretAccessKey string
+ SessionToken string
+ Query url.Values
+ Body io.ReadSeeker
+ Debug uint
+ Logger io.Writer
+
+ isPresign bool
+ formattedTime string
+ formattedShortTime string
+
+ signedHeaders string
+ canonicalHeaders string
+ canonicalString string
+ credentialString string
+ stringToSign string
+ signature string
+ authorization string
+}
+
+// Sign requests with signature version 4.
+func Sign(req *aws.Request) {
+ creds, err := req.Service.Config.Credentials.Credentials()
+ if err != nil {
+ req.Error = err
+ return
+ }
+
+ s := signer{
+ Request: req.HTTPRequest,
+ Time: req.Time,
+ ExpireTime: req.ExpireTime,
+ Query: req.HTTPRequest.URL.Query(),
+ Body: req.Body,
+ ServiceName: req.Service.ServiceName,
+ Region: req.Service.Config.Region,
+ AccessKeyID: creds.AccessKeyID,
+ SecretAccessKey: creds.SecretAccessKey,
+ SessionToken: creds.SessionToken,
+ Debug: req.Service.Config.LogLevel,
+ Logger: req.Service.Config.Logger,
+ }
+ s.sign()
+ return
+}
+
+func (v4 *signer) sign() {
+ if v4.ExpireTime != 0 {
+ v4.isPresign = true
+ }
+
+ if v4.isPresign {
+ v4.Query.Set("X-Amz-Algorithm", authHeaderPrefix)
+ if v4.SessionToken != "" {
+ v4.Query.Set("X-Amz-Security-Token", v4.SessionToken)
+ } else {
+ v4.Query.Del("X-Amz-Security-Token")
+ }
+ } else if v4.SessionToken != "" {
+ v4.Request.Header.Set("X-Amz-Security-Token", v4.SessionToken)
+ }
+
+ v4.build()
+
+ if v4.Debug > 0 {
+ out := v4.Logger
+ fmt.Fprintf(out, "---[ CANONICAL STRING ]-----------------------------\n")
+ fmt.Fprintln(out, v4.canonicalString)
+ fmt.Fprintf(out, "---[ STRING TO SIGN ]--------------------------------\n")
+ fmt.Fprintln(out, v4.stringToSign)
+ fmt.Fprintf(out, "---[ SIGNED URL ]--------------------------------\n")
+ fmt.Fprintln(out, v4.Request.URL)
+ fmt.Fprintf(out, "-----------------------------------------------------\n")
+ }
+}
+
+func (v4 *signer) build() {
+ v4.buildTime() // no depends
+ v4.buildCredentialString() // no depends
+ if v4.isPresign {
+ v4.buildQuery() // no depends
+ }
+ v4.buildCanonicalHeaders() // depends on cred string
+ v4.buildCanonicalString() // depends on canon headers / signed headers
+ v4.buildStringToSign() // depends on canon string
+ v4.buildSignature() // depends on string to sign
+
+ if v4.isPresign {
+ v4.Request.URL.RawQuery += "&X-Amz-Signature=" + v4.signature
+ } else {
+ parts := []string{
+ authHeaderPrefix + " Credential=" + v4.AccessKeyID + "/" + v4.credentialString,
+ "SignedHeaders=" + v4.signedHeaders,
+ "Signature=" + v4.signature,
+ }
+ v4.Request.Header.Set("Authorization", strings.Join(parts, ", "))
+ }
+}
+
+func (v4 *signer) buildTime() {
+ v4.formattedTime = v4.Time.UTC().Format(timeFormat)
+ v4.formattedShortTime = v4.Time.UTC().Format(shortTimeFormat)
+
+ if v4.isPresign {
+ duration := int64(v4.ExpireTime / time.Second)
+ v4.Query.Set("X-Amz-Date", v4.formattedTime)
+ v4.Query.Set("X-Amz-Expires", strconv.FormatInt(duration, 10))
+ } else {
+ v4.Request.Header.Set("X-Amz-Date", v4.formattedTime)
+ }
+}
+
+func (v4 *signer) buildCredentialString() {
+ v4.credentialString = strings.Join([]string{
+ v4.formattedShortTime,
+ v4.Region,
+ v4.ServiceName,
+ "aws4_request",
+ }, "/")
+
+ if v4.isPresign {
+ v4.Query.Set("X-Amz-Credential", v4.AccessKeyID+"/"+v4.credentialString)
+ }
+}
+
+func (v4 *signer) buildQuery() {
+ for k, h := range v4.Request.Header {
+ if strings.HasPrefix(http.CanonicalHeaderKey(k), "X-Amz-") {
+ continue // never hoist x-amz-* headers, they must be signed
+ }
+ if _, ok := ignoredHeaders[http.CanonicalHeaderKey(k)]; ok {
+ continue // never hoist ignored headers
+ }
+
+ v4.Request.Header.Del(k)
+ v4.Query.Del(k)
+ for _, v := range h {
+ v4.Query.Add(k, v)
+ }
+ }
+}
+
+func (v4 *signer) buildCanonicalHeaders() {
+ headers := make([]string, 0)
+ headers = append(headers, "host")
+ for k, _ := range v4.Request.Header {
+ if _, ok := ignoredHeaders[http.CanonicalHeaderKey(k)]; ok {
+ continue // ignored header
+ }
+ headers = append(headers, strings.ToLower(k))
+ }
+ sort.Strings(headers)
+
+ v4.signedHeaders = strings.Join(headers, ";")
+
+ if v4.isPresign {
+ v4.Query.Set("X-Amz-SignedHeaders", v4.signedHeaders)
+ }
+
+ headerValues := make([]string, len(headers))
+ for i, k := range headers {
+ if k == "host" {
+ headerValues[i] = "host:" + v4.Request.URL.Host
+ } else {
+ headerValues[i] = k + ":" +
+ strings.Join(v4.Request.Header[http.CanonicalHeaderKey(k)], ",")
+ }
+ }
+
+ v4.canonicalHeaders = strings.Join(headerValues, "\n")
+}
+
+func (v4 *signer) buildCanonicalString() {
+ v4.Request.URL.RawQuery = v4.Query.Encode()
+ uri := v4.Request.URL.Opaque
+ if uri != "" {
+ uri = "/" + strings.Join(strings.Split(uri, "/")[3:], "/")
+ } else {
+ uri = v4.Request.URL.Path
+ }
+ if uri == "" {
+ uri = "/"
+ }
+
+ v4.canonicalString = strings.Join([]string{
+ v4.Request.Method,
+ uri,
+ v4.Request.URL.RawQuery,
+ v4.canonicalHeaders + "\n",
+ v4.signedHeaders,
+ v4.bodyDigest(),
+ }, "\n")
+}
+
+func (v4 *signer) buildStringToSign() {
+ v4.stringToSign = strings.Join([]string{
+ authHeaderPrefix,
+ v4.formattedTime,
+ v4.credentialString,
+ hex.EncodeToString(makeSha256([]byte(v4.canonicalString))),
+ }, "\n")
+}
+
+func (v4 *signer) buildSignature() {
+ secret := v4.SecretAccessKey
+ date := makeHmac([]byte("AWS4"+secret), []byte(v4.formattedShortTime))
+ region := makeHmac(date, []byte(v4.Region))
+ service := makeHmac(region, []byte(v4.ServiceName))
+ credentials := makeHmac(service, []byte("aws4_request"))
+ signature := makeHmac(credentials, []byte(v4.stringToSign))
+ v4.signature = hex.EncodeToString(signature)
+}
+
+func (v4 *signer) bodyDigest() string {
+ hash := v4.Request.Header.Get("X-Amz-Content-Sha256")
+ if hash == "" {
+ if v4.isPresign && v4.ServiceName == "s3" {
+ hash = "UNSIGNED-PAYLOAD"
+ } else if v4.Body == nil {
+ hash = hex.EncodeToString(makeSha256([]byte{}))
+ } else {
+ hash = hex.EncodeToString(makeSha256Reader(v4.Body))
+ }
+ v4.Request.Header.Add("X-Amz-Content-Sha256", hash)
+ }
+ return hash
+}
+
+func makeHmac(key []byte, data []byte) []byte {
+ hash := hmac.New(sha256.New, key)
+ hash.Write(data)
+ return hash.Sum(nil)
+}
+
+func makeSha256(data []byte) []byte {
+ hash := sha256.New()
+ hash.Write(data)
+ return hash.Sum(nil)
+}
+
+func makeSha256Reader(reader io.ReadSeeker) []byte {
+ packet := make([]byte, 4096)
+ hash := sha256.New()
+
+ reader.Seek(0, 0)
+ for {
+ n, err := reader.Read(packet)
+ if n > 0 {
+ hash.Write(packet[0:n])
+ }
+ if err == io.EOF || n == 0 {
+ break
+ }
+ }
+ reader.Seek(0, 0)
+
+ return hash.Sum(nil)
+}