github.com/wayscript/goofys@v0.24.0/internal/v2signer.go (about) 1 // Copyright 2015 - 2017 Ka-Hing Cheung 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package internal 16 17 import ( 18 "crypto/hmac" 19 "crypto/sha1" 20 "encoding/base64" 21 "errors" 22 "fmt" 23 "net/http" 24 "net/url" 25 "sort" 26 "strings" 27 "time" 28 29 "github.com/aws/aws-sdk-go/aws" 30 "github.com/aws/aws-sdk-go/aws/credentials" 31 "github.com/aws/aws-sdk-go/aws/request" 32 "github.com/aws/aws-sdk-go/private/protocol/rest" 33 ) 34 35 var ( 36 errInvalidMethod = errors.New("v2 signer does not handle HTTP POST") 37 ) 38 39 const ( 40 signatureVersion = "2" 41 signatureMethod = "HmacSHA1" 42 timeFormat = "Mon, 2 Jan 2006 15:04:05 +0000" 43 ) 44 45 var subresources = []string{ 46 "acl", 47 "delete", 48 "lifecycle", 49 "location", 50 "logging", 51 "notification", 52 "partNumber", 53 "policy", 54 "requestPayment", 55 "torrent", 56 "uploadId", 57 "uploads", 58 "versionId", 59 "versioning", 60 "versions", 61 "website", 62 } 63 64 type signer struct { 65 // Values that must be populated from the request 66 Request *http.Request 67 Time time.Time 68 Credentials *credentials.Credentials 69 Debug aws.LogLevelType 70 Logger aws.Logger 71 pathStyle bool 72 bucket string 73 74 Query url.Values 75 stringToSign string 76 signature string 77 } 78 79 // Sign requests with signature version 2. 80 // 81 // Will sign the requests with the service config's Credentials object 82 // Signing is skipped if the credentials is the credentials.AnonymousCredentials 83 // object. 84 func SignV2(req *request.Request) { 85 // If the request does not need to be signed ignore the signing of the 86 // request if the AnonymousCredentials object is used. 87 if req.Config.Credentials == credentials.AnonymousCredentials { 88 return 89 } 90 91 v2 := signer{ 92 Request: req.HTTPRequest, 93 Time: req.Time, 94 Credentials: req.Config.Credentials, 95 Debug: req.Config.LogLevel.Value(), 96 Logger: req.Config.Logger, 97 pathStyle: aws.BoolValue(req.Config.S3ForcePathStyle), 98 } 99 100 req.Error = v2.Sign() 101 } 102 103 func (v2 *signer) Sign() error { 104 credValue, err := v2.Credentials.Get() 105 if err != nil { 106 return err 107 } 108 109 v2.Query = v2.Request.URL.Query() 110 111 contentMD5 := v2.Request.Header.Get("Content-MD5") 112 contentType := v2.Request.Header.Get("Content-Type") 113 date := v2.Time.UTC().Format(timeFormat) 114 v2.Request.Header.Set("x-amz-date", date) 115 116 if credValue.SessionToken != "" { 117 v2.Request.Header.Set("x-amz-security-token", credValue.SessionToken) 118 } 119 120 // in case this is a retry, ensure no signature present 121 v2.Request.Header.Del("Authorization") 122 123 method := v2.Request.Method 124 125 uri := v2.Request.URL.Opaque 126 if uri != "" { 127 if questionMark := strings.Index(uri, "?"); questionMark != -1 { 128 uri = uri[0:questionMark] 129 } 130 uri = "/" + strings.Join(strings.Split(uri, "/")[3:], "/") 131 } else { 132 uri = v2.Request.URL.Path 133 } 134 path := rest.EscapePath(uri, false) 135 if !v2.pathStyle { 136 host := strings.SplitN(v2.Request.URL.Host, ".", 2)[0] 137 path = "/" + host + uri 138 } 139 if path == "" { 140 path = "/" 141 } 142 143 // build URL-encoded query keys and values 144 queryKeysAndValues := []string{} 145 for _, key := range subresources { 146 if _, ok := v2.Query[key]; ok { 147 k := strings.Replace(url.QueryEscape(key), "+", "%20", -1) 148 v := strings.Replace(url.QueryEscape(v2.Query.Get(key)), "+", "%20", -1) 149 if v != "" { 150 v = "=" + v 151 } 152 queryKeysAndValues = append(queryKeysAndValues, k+v) 153 } 154 } 155 156 // join into one query string 157 query := strings.Join(queryKeysAndValues, "&") 158 159 if query != "" { 160 path += "?" + query 161 } 162 163 tmp := []string{ 164 method, 165 contentMD5, 166 contentType, 167 "", 168 } 169 170 var headers []string 171 for k := range v2.Request.Header { 172 k = strings.ToLower(k) 173 if strings.HasPrefix(k, "x-amz-") { 174 headers = append(headers, k) 175 } 176 } 177 sort.Strings(headers) 178 179 for _, k := range headers { 180 v := strings.Join(v2.Request.Header[http.CanonicalHeaderKey(k)], ",") 181 tmp = append(tmp, k+":"+v) 182 } 183 184 tmp = append(tmp, path) 185 186 // build the canonical string for the V2 signature 187 v2.stringToSign = strings.Join(tmp, "\n") 188 189 hash := hmac.New(sha1.New, []byte(credValue.SecretAccessKey)) 190 hash.Write([]byte(v2.stringToSign)) 191 v2.signature = base64.StdEncoding.EncodeToString(hash.Sum(nil)) 192 v2.Request.Header.Set("Authorization", 193 "AWS "+credValue.AccessKeyID+":"+v2.signature) 194 195 if v2.Debug.Matches(aws.LogDebugWithSigning) { 196 v2.logSigningInfo() 197 } 198 199 return nil 200 } 201 202 const logSignInfoMsg = `DEBUG: Request Signature: 203 ---[ STRING TO SIGN ]-------------------------------- 204 %s 205 ---[ SIGNATURE ]------------------------------------- 206 %s 207 -----------------------------------------------------` 208 209 func (v2 *signer) logSigningInfo() { 210 msg := fmt.Sprintf(logSignInfoMsg, v2.stringToSign, v2.Request.Header.Get("Authorization")) 211 v2.Logger.Log(msg) 212 }