github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/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 "delete": {}, 18 "acl": {}, 19 "location": {}, 20 "logging": {}, 21 "notification": {}, 22 "partNumber": {}, 23 "policy": {}, 24 "requestPayment": {}, 25 "torrent": {}, 26 "uploadId": {}, 27 "uploads": {}, 28 "versionId": {}, 29 "versioning": {}, 30 "versions": {}, 31 "response-content-type": {}, 32 "response-content-language": {}, 33 "response-expires": {}, 34 "response-cache-control": {}, 35 "response-content-disposition": {}, 36 "response-content-encoding": {}, 37 } 38 39 // sign signs requests using v2 auth 40 // 41 // Cobbled together from goamz and aws-sdk-go 42 func sign(AccessKey, SecretKey string, req *http.Request) { 43 // Set date 44 date := time.Now().UTC().Format(time.RFC1123) 45 req.Header.Set("Date", date) 46 47 // Sort out URI 48 uri := req.URL.EscapedPath() 49 if uri == "" { 50 uri = "/" 51 } 52 53 // Look through headers of interest 54 var md5 string 55 var contentType string 56 var headersToSign []string 57 tmpHeadersToSign := make(map[string][]string) 58 for k, v := range req.Header { 59 k = strings.ToLower(k) 60 switch k { 61 case "content-md5": 62 md5 = v[0] 63 case "content-type": 64 contentType = v[0] 65 default: 66 if strings.HasPrefix(k, "x-amz-") { 67 tmpHeadersToSign[k] = v 68 } 69 } 70 } 71 var keys []string 72 for k := range tmpHeadersToSign { 73 keys = append(keys, k) 74 } 75 // https://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html 76 sort.Strings(keys) 77 78 for _, key := range keys { 79 vall := strings.Join(tmpHeadersToSign[key], ",") 80 headersToSign = append(headersToSign, key+":"+vall) 81 } 82 // Make headers of interest into canonical string 83 var joinedHeadersToSign string 84 if len(headersToSign) > 0 { 85 joinedHeadersToSign = strings.Join(headersToSign, "\n") + "\n" 86 } 87 88 // Look for query parameters which need to be added to the signature 89 params := req.URL.Query() 90 var queriesToSign []string 91 for k, vs := range params { 92 if _, ok := s3ParamsToSign[k]; ok { 93 for _, v := range vs { 94 if v == "" { 95 queriesToSign = append(queriesToSign, k) 96 } else { 97 queriesToSign = append(queriesToSign, k+"="+v) 98 } 99 } 100 } 101 } 102 // Add query parameters to URI 103 if len(queriesToSign) > 0 { 104 sort.StringSlice(queriesToSign).Sort() 105 uri += "?" + strings.Join(queriesToSign, "&") 106 } 107 108 // Make signature 109 payload := req.Method + "\n" + md5 + "\n" + contentType + "\n" + date + "\n" + joinedHeadersToSign + uri 110 hash := hmac.New(sha1.New, []byte(SecretKey)) 111 _, _ = hash.Write([]byte(payload)) 112 signature := make([]byte, base64.StdEncoding.EncodedLen(hash.Size())) 113 base64.StdEncoding.Encode(signature, hash.Sum(nil)) 114 115 // Set signature in request 116 req.Header.Set("Authorization", "AWS "+AccessKey+":"+string(signature)) 117 }