github.com/ncw/rclone@v1.48.1-0.20190724201158-a35aa1360e3e/backend/s3/v2sign.go (about)

     1  // v2 signing
     2  
     3  package s3
     4  
     5  import (
     6  	"crypto/hmac"
     7  	"crypto/sha1"
     8  	"encoding/base64"
     9  	"net/http"
    10  	"sort"
    11  	"strings"
    12  	"time"
    13  )
    14  
    15  // URL parameters that need to be added to the signature
    16  var s3ParamsToSign = map[string]struct{}{
    17  	"acl":                          {},
    18  	"location":                     {},
    19  	"logging":                      {},
    20  	"notification":                 {},
    21  	"partNumber":                   {},
    22  	"policy":                       {},
    23  	"requestPayment":               {},
    24  	"torrent":                      {},
    25  	"uploadId":                     {},
    26  	"uploads":                      {},
    27  	"versionId":                    {},
    28  	"versioning":                   {},
    29  	"versions":                     {},
    30  	"response-content-type":        {},
    31  	"response-content-language":    {},
    32  	"response-expires":             {},
    33  	"response-cache-control":       {},
    34  	"response-content-disposition": {},
    35  	"response-content-encoding":    {},
    36  }
    37  
    38  // sign signs requests using v2 auth
    39  //
    40  // Cobbled together from goamz and aws-sdk-go
    41  func sign(AccessKey, SecretKey string, req *http.Request) {
    42  	// Set date
    43  	date := time.Now().UTC().Format(time.RFC1123)
    44  	req.Header.Set("Date", date)
    45  
    46  	// Sort out URI
    47  	uri := req.URL.EscapedPath()
    48  	if uri == "" {
    49  		uri = "/"
    50  	}
    51  
    52  	// Look through headers of interest
    53  	var md5 string
    54  	var contentType string
    55  	var headersToSign []string
    56  	for k, v := range req.Header {
    57  		k = strings.ToLower(k)
    58  		switch k {
    59  		case "content-md5":
    60  			md5 = v[0]
    61  		case "content-type":
    62  			contentType = v[0]
    63  		default:
    64  			if strings.HasPrefix(k, "x-amz-") {
    65  				vall := strings.Join(v, ",")
    66  				headersToSign = append(headersToSign, k+":"+vall)
    67  			}
    68  		}
    69  	}
    70  	// Make headers of interest into canonical string
    71  	var joinedHeadersToSign string
    72  	if len(headersToSign) > 0 {
    73  		sort.StringSlice(headersToSign).Sort()
    74  		joinedHeadersToSign = strings.Join(headersToSign, "\n") + "\n"
    75  	}
    76  
    77  	// Look for query parameters which need to be added to the signature
    78  	params := req.URL.Query()
    79  	var queriesToSign []string
    80  	for k, vs := range params {
    81  		if _, ok := s3ParamsToSign[k]; ok {
    82  			for _, v := range vs {
    83  				if v == "" {
    84  					queriesToSign = append(queriesToSign, k)
    85  				} else {
    86  					queriesToSign = append(queriesToSign, k+"="+v)
    87  				}
    88  			}
    89  		}
    90  	}
    91  	// Add query parameters to URI
    92  	if len(queriesToSign) > 0 {
    93  		sort.StringSlice(queriesToSign).Sort()
    94  		uri += "?" + strings.Join(queriesToSign, "&")
    95  	}
    96  
    97  	// Make signature
    98  	payload := req.Method + "\n" + md5 + "\n" + contentType + "\n" + date + "\n" + joinedHeadersToSign + uri
    99  	hash := hmac.New(sha1.New, []byte(SecretKey))
   100  	_, _ = hash.Write([]byte(payload))
   101  	signature := make([]byte, base64.StdEncoding.EncodedLen(hash.Size()))
   102  	base64.StdEncoding.Encode(signature, hash.Sum(nil))
   103  
   104  	// Set signature in request
   105  	req.Header.Set("Authorization", "AWS "+AccessKey+":"+string(signature))
   106  }