v4.go 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796
  1. // Package v4 implements signing for AWS V4 signer
  2. //
  3. // Provides request signing for request that need to be signed with
  4. // AWS V4 Signatures.
  5. //
  6. // Standalone Signer
  7. //
  8. // Generally using the signer outside of the SDK should not require any additional
  9. // logic when using Go v1.5 or higher. The signer does this by taking advantage
  10. // of the URL.EscapedPath method. If your request URI requires additional escaping
  11. // you many need to use the URL.Opaque to define what the raw URI should be sent
  12. // to the service as.
  13. //
  14. // The signer will first check the URL.Opaque field, and use its value if set.
  15. // The signer does require the URL.Opaque field to be set in the form of:
  16. //
  17. // "//<hostname>/<path>"
  18. //
  19. // // e.g.
  20. // "//example.com/some/path"
  21. //
  22. // The leading "//" and hostname are required or the URL.Opaque escaping will
  23. // not work correctly.
  24. //
  25. // If URL.Opaque is not set the signer will fallback to the URL.EscapedPath()
  26. // method and using the returned value. If you're using Go v1.4 you must set
  27. // URL.Opaque if the URI path needs escaping. If URL.Opaque is not set with
  28. // Go v1.5 the signer will fallback to URL.Path.
  29. //
  30. // AWS v4 signature validation requires that the canonical string's URI path
  31. // element must be the URI escaped form of the HTTP request's path.
  32. // http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
  33. //
  34. // The Go HTTP client will perform escaping automatically on the request. Some
  35. // of these escaping may cause signature validation errors because the HTTP
  36. // request differs from the URI path or query that the signature was generated.
  37. // https://golang.org/pkg/net/url/#URL.EscapedPath
  38. //
  39. // Because of this, it is recommended that when using the signer outside of the
  40. // SDK that explicitly escaping the request prior to being signed is preferable,
  41. // and will help prevent signature validation errors. This can be done by setting
  42. // the URL.Opaque or URL.RawPath. The SDK will use URL.Opaque first and then
  43. // call URL.EscapedPath() if Opaque is not set.
  44. //
  45. // If signing a request intended for HTTP2 server, and you're using Go 1.6.2
  46. // through 1.7.4 you should use the URL.RawPath as the pre-escaped form of the
  47. // request URL. https://github.com/golang/go/issues/16847 points to a bug in
  48. // Go pre 1.8 that fails to make HTTP2 requests using absolute URL in the HTTP
  49. // message. URL.Opaque generally will force Go to make requests with absolute URL.
  50. // URL.RawPath does not do this, but RawPath must be a valid escaping of Path
  51. // or url.EscapedPath will ignore the RawPath escaping.
  52. //
  53. // Test `TestStandaloneSign` provides a complete example of using the signer
  54. // outside of the SDK and pre-escaping the URI path.
  55. package v4
  56. import (
  57. "crypto/hmac"
  58. "crypto/sha256"
  59. "encoding/hex"
  60. "fmt"
  61. "io"
  62. "io/ioutil"
  63. "net/http"
  64. "net/url"
  65. "sort"
  66. "strconv"
  67. "strings"
  68. "time"
  69. "github.com/aws/aws-sdk-go/aws"
  70. "github.com/aws/aws-sdk-go/aws/credentials"
  71. "github.com/aws/aws-sdk-go/aws/request"
  72. "github.com/aws/aws-sdk-go/internal/sdkio"
  73. "github.com/aws/aws-sdk-go/private/protocol/rest"
  74. )
  75. const (
  76. authHeaderPrefix = "AWS4-HMAC-SHA256"
  77. timeFormat = "20060102T150405Z"
  78. shortTimeFormat = "20060102"
  79. // emptyStringSHA256 is a SHA256 of an empty string
  80. emptyStringSHA256 = `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`
  81. )
  82. var ignoredHeaders = rules{
  83. blacklist{
  84. mapRule{
  85. "Authorization": struct{}{},
  86. "User-Agent": struct{}{},
  87. "X-Amzn-Trace-Id": struct{}{},
  88. },
  89. },
  90. }
  91. // requiredSignedHeaders is a whitelist for build canonical headers.
  92. var requiredSignedHeaders = rules{
  93. whitelist{
  94. mapRule{
  95. "Cache-Control": struct{}{},
  96. "Content-Disposition": struct{}{},
  97. "Content-Encoding": struct{}{},
  98. "Content-Language": struct{}{},
  99. "Content-Md5": struct{}{},
  100. "Content-Type": struct{}{},
  101. "Expires": struct{}{},
  102. "If-Match": struct{}{},
  103. "If-Modified-Since": struct{}{},
  104. "If-None-Match": struct{}{},
  105. "If-Unmodified-Since": struct{}{},
  106. "Range": struct{}{},
  107. "X-Amz-Acl": struct{}{},
  108. "X-Amz-Copy-Source": struct{}{},
  109. "X-Amz-Copy-Source-If-Match": struct{}{},
  110. "X-Amz-Copy-Source-If-Modified-Since": struct{}{},
  111. "X-Amz-Copy-Source-If-None-Match": struct{}{},
  112. "X-Amz-Copy-Source-If-Unmodified-Since": struct{}{},
  113. "X-Amz-Copy-Source-Range": struct{}{},
  114. "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm": struct{}{},
  115. "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key": struct{}{},
  116. "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5": struct{}{},
  117. "X-Amz-Grant-Full-control": struct{}{},
  118. "X-Amz-Grant-Read": struct{}{},
  119. "X-Amz-Grant-Read-Acp": struct{}{},
  120. "X-Amz-Grant-Write": struct{}{},
  121. "X-Amz-Grant-Write-Acp": struct{}{},
  122. "X-Amz-Metadata-Directive": struct{}{},
  123. "X-Amz-Mfa": struct{}{},
  124. "X-Amz-Request-Payer": struct{}{},
  125. "X-Amz-Server-Side-Encryption": struct{}{},
  126. "X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id": struct{}{},
  127. "X-Amz-Server-Side-Encryption-Customer-Algorithm": struct{}{},
  128. "X-Amz-Server-Side-Encryption-Customer-Key": struct{}{},
  129. "X-Amz-Server-Side-Encryption-Customer-Key-Md5": struct{}{},
  130. "X-Amz-Storage-Class": struct{}{},
  131. "X-Amz-Tagging": struct{}{},
  132. "X-Amz-Website-Redirect-Location": struct{}{},
  133. "X-Amz-Content-Sha256": struct{}{},
  134. },
  135. },
  136. patterns{"X-Amz-Meta-"},
  137. }
  138. // allowedHoisting is a whitelist for build query headers. The boolean value
  139. // represents whether or not it is a pattern.
  140. var allowedQueryHoisting = inclusiveRules{
  141. blacklist{requiredSignedHeaders},
  142. patterns{"X-Amz-"},
  143. }
  144. // Signer applies AWS v4 signing to given request. Use this to sign requests
  145. // that need to be signed with AWS V4 Signatures.
  146. type Signer struct {
  147. // The authentication credentials the request will be signed against.
  148. // This value must be set to sign requests.
  149. Credentials *credentials.Credentials
  150. // Sets the log level the signer should use when reporting information to
  151. // the logger. If the logger is nil nothing will be logged. See
  152. // aws.LogLevelType for more information on available logging levels
  153. //
  154. // By default nothing will be logged.
  155. Debug aws.LogLevelType
  156. // The logger loging information will be written to. If there the logger
  157. // is nil, nothing will be logged.
  158. Logger aws.Logger
  159. // Disables the Signer's moving HTTP header key/value pairs from the HTTP
  160. // request header to the request's query string. This is most commonly used
  161. // with pre-signed requests preventing headers from being added to the
  162. // request's query string.
  163. DisableHeaderHoisting bool
  164. // Disables the automatic escaping of the URI path of the request for the
  165. // siganture's canonical string's path. For services that do not need additional
  166. // escaping then use this to disable the signer escaping the path.
  167. //
  168. // S3 is an example of a service that does not need additional escaping.
  169. //
  170. // http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
  171. DisableURIPathEscaping bool
  172. // Disales the automatical setting of the HTTP request's Body field with the
  173. // io.ReadSeeker passed in to the signer. This is useful if you're using a
  174. // custom wrapper around the body for the io.ReadSeeker and want to preserve
  175. // the Body value on the Request.Body.
  176. //
  177. // This does run the risk of signing a request with a body that will not be
  178. // sent in the request. Need to ensure that the underlying data of the Body
  179. // values are the same.
  180. DisableRequestBodyOverwrite bool
  181. // currentTimeFn returns the time value which represents the current time.
  182. // This value should only be used for testing. If it is nil the default
  183. // time.Now will be used.
  184. currentTimeFn func() time.Time
  185. // UnsignedPayload will prevent signing of the payload. This will only
  186. // work for services that have support for this.
  187. UnsignedPayload bool
  188. }
  189. // NewSigner returns a Signer pointer configured with the credentials and optional
  190. // option values provided. If not options are provided the Signer will use its
  191. // default configuration.
  192. func NewSigner(credentials *credentials.Credentials, options ...func(*Signer)) *Signer {
  193. v4 := &Signer{
  194. Credentials: credentials,
  195. }
  196. for _, option := range options {
  197. option(v4)
  198. }
  199. return v4
  200. }
  201. type signingCtx struct {
  202. ServiceName string
  203. Region string
  204. Request *http.Request
  205. Body io.ReadSeeker
  206. Query url.Values
  207. Time time.Time
  208. ExpireTime time.Duration
  209. SignedHeaderVals http.Header
  210. DisableURIPathEscaping bool
  211. credValues credentials.Value
  212. isPresign bool
  213. formattedTime string
  214. formattedShortTime string
  215. unsignedPayload bool
  216. bodyDigest string
  217. signedHeaders string
  218. canonicalHeaders string
  219. canonicalString string
  220. credentialString string
  221. stringToSign string
  222. signature string
  223. authorization string
  224. }
  225. // Sign signs AWS v4 requests with the provided body, service name, region the
  226. // request is made to, and time the request is signed at. The signTime allows
  227. // you to specify that a request is signed for the future, and cannot be
  228. // used until then.
  229. //
  230. // Returns a list of HTTP headers that were included in the signature or an
  231. // error if signing the request failed. Generally for signed requests this value
  232. // is not needed as the full request context will be captured by the http.Request
  233. // value. It is included for reference though.
  234. //
  235. // Sign will set the request's Body to be the `body` parameter passed in. If
  236. // the body is not already an io.ReadCloser, it will be wrapped within one. If
  237. // a `nil` body parameter passed to Sign, the request's Body field will be
  238. // also set to nil. Its important to note that this functionality will not
  239. // change the request's ContentLength of the request.
  240. //
  241. // Sign differs from Presign in that it will sign the request using HTTP
  242. // header values. This type of signing is intended for http.Request values that
  243. // will not be shared, or are shared in a way the header values on the request
  244. // will not be lost.
  245. //
  246. // The requests body is an io.ReadSeeker so the SHA256 of the body can be
  247. // generated. To bypass the signer computing the hash you can set the
  248. // "X-Amz-Content-Sha256" header with a precomputed value. The signer will
  249. // only compute the hash if the request header value is empty.
  250. func (v4 Signer) Sign(r *http.Request, body io.ReadSeeker, service, region string, signTime time.Time) (http.Header, error) {
  251. return v4.signWithBody(r, body, service, region, 0, false, signTime)
  252. }
  253. // Presign signs AWS v4 requests with the provided body, service name, region
  254. // the request is made to, and time the request is signed at. The signTime
  255. // allows you to specify that a request is signed for the future, and cannot
  256. // be used until then.
  257. //
  258. // Returns a list of HTTP headers that were included in the signature or an
  259. // error if signing the request failed. For presigned requests these headers
  260. // and their values must be included on the HTTP request when it is made. This
  261. // is helpful to know what header values need to be shared with the party the
  262. // presigned request will be distributed to.
  263. //
  264. // Presign differs from Sign in that it will sign the request using query string
  265. // instead of header values. This allows you to share the Presigned Request's
  266. // URL with third parties, or distribute it throughout your system with minimal
  267. // dependencies.
  268. //
  269. // Presign also takes an exp value which is the duration the
  270. // signed request will be valid after the signing time. This is allows you to
  271. // set when the request will expire.
  272. //
  273. // The requests body is an io.ReadSeeker so the SHA256 of the body can be
  274. // generated. To bypass the signer computing the hash you can set the
  275. // "X-Amz-Content-Sha256" header with a precomputed value. The signer will
  276. // only compute the hash if the request header value is empty.
  277. //
  278. // Presigning a S3 request will not compute the body's SHA256 hash by default.
  279. // This is done due to the general use case for S3 presigned URLs is to share
  280. // PUT/GET capabilities. If you would like to include the body's SHA256 in the
  281. // presigned request's signature you can set the "X-Amz-Content-Sha256"
  282. // HTTP header and that will be included in the request's signature.
  283. func (v4 Signer) Presign(r *http.Request, body io.ReadSeeker, service, region string, exp time.Duration, signTime time.Time) (http.Header, error) {
  284. return v4.signWithBody(r, body, service, region, exp, true, signTime)
  285. }
  286. func (v4 Signer) signWithBody(r *http.Request, body io.ReadSeeker, service, region string, exp time.Duration, isPresign bool, signTime time.Time) (http.Header, error) {
  287. currentTimeFn := v4.currentTimeFn
  288. if currentTimeFn == nil {
  289. currentTimeFn = time.Now
  290. }
  291. ctx := &signingCtx{
  292. Request: r,
  293. Body: body,
  294. Query: r.URL.Query(),
  295. Time: signTime,
  296. ExpireTime: exp,
  297. isPresign: isPresign,
  298. ServiceName: service,
  299. Region: region,
  300. DisableURIPathEscaping: v4.DisableURIPathEscaping,
  301. unsignedPayload: v4.UnsignedPayload,
  302. }
  303. for key := range ctx.Query {
  304. sort.Strings(ctx.Query[key])
  305. }
  306. if ctx.isRequestSigned() {
  307. ctx.Time = currentTimeFn()
  308. ctx.handlePresignRemoval()
  309. }
  310. var err error
  311. ctx.credValues, err = v4.Credentials.Get()
  312. if err != nil {
  313. return http.Header{}, err
  314. }
  315. ctx.sanitizeHostForHeader()
  316. ctx.assignAmzQueryValues()
  317. if err := ctx.build(v4.DisableHeaderHoisting); err != nil {
  318. return nil, err
  319. }
  320. // If the request is not presigned the body should be attached to it. This
  321. // prevents the confusion of wanting to send a signed request without
  322. // the body the request was signed for attached.
  323. if !(v4.DisableRequestBodyOverwrite || ctx.isPresign) {
  324. var reader io.ReadCloser
  325. if body != nil {
  326. var ok bool
  327. if reader, ok = body.(io.ReadCloser); !ok {
  328. reader = ioutil.NopCloser(body)
  329. }
  330. }
  331. r.Body = reader
  332. }
  333. if v4.Debug.Matches(aws.LogDebugWithSigning) {
  334. v4.logSigningInfo(ctx)
  335. }
  336. return ctx.SignedHeaderVals, nil
  337. }
  338. func (ctx *signingCtx) sanitizeHostForHeader() {
  339. request.SanitizeHostForHeader(ctx.Request)
  340. }
  341. func (ctx *signingCtx) handlePresignRemoval() {
  342. if !ctx.isPresign {
  343. return
  344. }
  345. // The credentials have expired for this request. The current signing
  346. // is invalid, and needs to be request because the request will fail.
  347. ctx.removePresign()
  348. // Update the request's query string to ensure the values stays in
  349. // sync in the case retrieving the new credentials fails.
  350. ctx.Request.URL.RawQuery = ctx.Query.Encode()
  351. }
  352. func (ctx *signingCtx) assignAmzQueryValues() {
  353. if ctx.isPresign {
  354. ctx.Query.Set("X-Amz-Algorithm", authHeaderPrefix)
  355. if ctx.credValues.SessionToken != "" {
  356. ctx.Query.Set("X-Amz-Security-Token", ctx.credValues.SessionToken)
  357. } else {
  358. ctx.Query.Del("X-Amz-Security-Token")
  359. }
  360. return
  361. }
  362. if ctx.credValues.SessionToken != "" {
  363. ctx.Request.Header.Set("X-Amz-Security-Token", ctx.credValues.SessionToken)
  364. }
  365. }
  366. // SignRequestHandler is a named request handler the SDK will use to sign
  367. // service client request with using the V4 signature.
  368. var SignRequestHandler = request.NamedHandler{
  369. Name: "v4.SignRequestHandler", Fn: SignSDKRequest,
  370. }
  371. // SignSDKRequest signs an AWS request with the V4 signature. This
  372. // request handler should only be used with the SDK's built in service client's
  373. // API operation requests.
  374. //
  375. // This function should not be used on its on its own, but in conjunction with
  376. // an AWS service client's API operation call. To sign a standalone request
  377. // not created by a service client's API operation method use the "Sign" or
  378. // "Presign" functions of the "Signer" type.
  379. //
  380. // If the credentials of the request's config are set to
  381. // credentials.AnonymousCredentials the request will not be signed.
  382. func SignSDKRequest(req *request.Request) {
  383. SignSDKRequestWithCurrentTime(req, time.Now)
  384. }
  385. // BuildNamedHandler will build a generic handler for signing.
  386. func BuildNamedHandler(name string, opts ...func(*Signer)) request.NamedHandler {
  387. return request.NamedHandler{
  388. Name: name,
  389. Fn: func(req *request.Request) {
  390. SignSDKRequestWithCurrentTime(req, time.Now, opts...)
  391. },
  392. }
  393. }
  394. // SignSDKRequestWithCurrentTime will sign the SDK's request using the time
  395. // function passed in. Behaves the same as SignSDKRequest with the exception
  396. // the request is signed with the value returned by the current time function.
  397. func SignSDKRequestWithCurrentTime(req *request.Request, curTimeFn func() time.Time, opts ...func(*Signer)) {
  398. // If the request does not need to be signed ignore the signing of the
  399. // request if the AnonymousCredentials object is used.
  400. if req.Config.Credentials == credentials.AnonymousCredentials {
  401. return
  402. }
  403. region := req.ClientInfo.SigningRegion
  404. if region == "" {
  405. region = aws.StringValue(req.Config.Region)
  406. }
  407. name := req.ClientInfo.SigningName
  408. if name == "" {
  409. name = req.ClientInfo.ServiceName
  410. }
  411. v4 := NewSigner(req.Config.Credentials, func(v4 *Signer) {
  412. v4.Debug = req.Config.LogLevel.Value()
  413. v4.Logger = req.Config.Logger
  414. v4.DisableHeaderHoisting = req.NotHoist
  415. v4.currentTimeFn = curTimeFn
  416. if name == "s3" {
  417. // S3 service should not have any escaping applied
  418. v4.DisableURIPathEscaping = true
  419. }
  420. // Prevents setting the HTTPRequest's Body. Since the Body could be
  421. // wrapped in a custom io.Closer that we do not want to be stompped
  422. // on top of by the signer.
  423. v4.DisableRequestBodyOverwrite = true
  424. })
  425. for _, opt := range opts {
  426. opt(v4)
  427. }
  428. curTime := curTimeFn()
  429. signedHeaders, err := v4.signWithBody(req.HTTPRequest, req.GetBody(),
  430. name, region, req.ExpireTime, req.ExpireTime > 0, curTime,
  431. )
  432. if err != nil {
  433. req.Error = err
  434. req.SignedHeaderVals = nil
  435. return
  436. }
  437. req.SignedHeaderVals = signedHeaders
  438. req.LastSignedAt = curTime
  439. }
  440. const logSignInfoMsg = `DEBUG: Request Signature:
  441. ---[ CANONICAL STRING ]-----------------------------
  442. %s
  443. ---[ STRING TO SIGN ]--------------------------------
  444. %s%s
  445. -----------------------------------------------------`
  446. const logSignedURLMsg = `
  447. ---[ SIGNED URL ]------------------------------------
  448. %s`
  449. func (v4 *Signer) logSigningInfo(ctx *signingCtx) {
  450. signedURLMsg := ""
  451. if ctx.isPresign {
  452. signedURLMsg = fmt.Sprintf(logSignedURLMsg, ctx.Request.URL.String())
  453. }
  454. msg := fmt.Sprintf(logSignInfoMsg, ctx.canonicalString, ctx.stringToSign, signedURLMsg)
  455. v4.Logger.Log(msg)
  456. }
  457. func (ctx *signingCtx) build(disableHeaderHoisting bool) error {
  458. ctx.buildTime() // no depends
  459. ctx.buildCredentialString() // no depends
  460. if err := ctx.buildBodyDigest(); err != nil {
  461. return err
  462. }
  463. unsignedHeaders := ctx.Request.Header
  464. if ctx.isPresign {
  465. if !disableHeaderHoisting {
  466. urlValues := url.Values{}
  467. urlValues, unsignedHeaders = buildQuery(allowedQueryHoisting, unsignedHeaders) // no depends
  468. for k := range urlValues {
  469. ctx.Query[k] = urlValues[k]
  470. }
  471. }
  472. }
  473. ctx.buildCanonicalHeaders(ignoredHeaders, unsignedHeaders)
  474. ctx.buildCanonicalString() // depends on canon headers / signed headers
  475. ctx.buildStringToSign() // depends on canon string
  476. ctx.buildSignature() // depends on string to sign
  477. if ctx.isPresign {
  478. ctx.Request.URL.RawQuery += "&X-Amz-Signature=" + ctx.signature
  479. } else {
  480. parts := []string{
  481. authHeaderPrefix + " Credential=" + ctx.credValues.AccessKeyID + "/" + ctx.credentialString,
  482. "SignedHeaders=" + ctx.signedHeaders,
  483. "Signature=" + ctx.signature,
  484. }
  485. ctx.Request.Header.Set("Authorization", strings.Join(parts, ", "))
  486. }
  487. return nil
  488. }
  489. func (ctx *signingCtx) buildTime() {
  490. ctx.formattedTime = ctx.Time.UTC().Format(timeFormat)
  491. ctx.formattedShortTime = ctx.Time.UTC().Format(shortTimeFormat)
  492. if ctx.isPresign {
  493. duration := int64(ctx.ExpireTime / time.Second)
  494. ctx.Query.Set("X-Amz-Date", ctx.formattedTime)
  495. ctx.Query.Set("X-Amz-Expires", strconv.FormatInt(duration, 10))
  496. } else {
  497. ctx.Request.Header.Set("X-Amz-Date", ctx.formattedTime)
  498. }
  499. }
  500. func (ctx *signingCtx) buildCredentialString() {
  501. ctx.credentialString = strings.Join([]string{
  502. ctx.formattedShortTime,
  503. ctx.Region,
  504. ctx.ServiceName,
  505. "aws4_request",
  506. }, "/")
  507. if ctx.isPresign {
  508. ctx.Query.Set("X-Amz-Credential", ctx.credValues.AccessKeyID+"/"+ctx.credentialString)
  509. }
  510. }
  511. func buildQuery(r rule, header http.Header) (url.Values, http.Header) {
  512. query := url.Values{}
  513. unsignedHeaders := http.Header{}
  514. for k, h := range header {
  515. if r.IsValid(k) {
  516. query[k] = h
  517. } else {
  518. unsignedHeaders[k] = h
  519. }
  520. }
  521. return query, unsignedHeaders
  522. }
  523. func (ctx *signingCtx) buildCanonicalHeaders(r rule, header http.Header) {
  524. var headers []string
  525. headers = append(headers, "host")
  526. for k, v := range header {
  527. canonicalKey := http.CanonicalHeaderKey(k)
  528. if !r.IsValid(canonicalKey) {
  529. continue // ignored header
  530. }
  531. if ctx.SignedHeaderVals == nil {
  532. ctx.SignedHeaderVals = make(http.Header)
  533. }
  534. lowerCaseKey := strings.ToLower(k)
  535. if _, ok := ctx.SignedHeaderVals[lowerCaseKey]; ok {
  536. // include additional values
  537. ctx.SignedHeaderVals[lowerCaseKey] = append(ctx.SignedHeaderVals[lowerCaseKey], v...)
  538. continue
  539. }
  540. headers = append(headers, lowerCaseKey)
  541. ctx.SignedHeaderVals[lowerCaseKey] = v
  542. }
  543. sort.Strings(headers)
  544. ctx.signedHeaders = strings.Join(headers, ";")
  545. if ctx.isPresign {
  546. ctx.Query.Set("X-Amz-SignedHeaders", ctx.signedHeaders)
  547. }
  548. headerValues := make([]string, len(headers))
  549. for i, k := range headers {
  550. if k == "host" {
  551. if ctx.Request.Host != "" {
  552. headerValues[i] = "host:" + ctx.Request.Host
  553. } else {
  554. headerValues[i] = "host:" + ctx.Request.URL.Host
  555. }
  556. } else {
  557. headerValues[i] = k + ":" +
  558. strings.Join(ctx.SignedHeaderVals[k], ",")
  559. }
  560. }
  561. stripExcessSpaces(headerValues)
  562. ctx.canonicalHeaders = strings.Join(headerValues, "\n")
  563. }
  564. func (ctx *signingCtx) buildCanonicalString() {
  565. ctx.Request.URL.RawQuery = strings.Replace(ctx.Query.Encode(), "+", "%20", -1)
  566. uri := getURIPath(ctx.Request.URL)
  567. if !ctx.DisableURIPathEscaping {
  568. uri = rest.EscapePath(uri, false)
  569. }
  570. ctx.canonicalString = strings.Join([]string{
  571. ctx.Request.Method,
  572. uri,
  573. ctx.Request.URL.RawQuery,
  574. ctx.canonicalHeaders + "\n",
  575. ctx.signedHeaders,
  576. ctx.bodyDigest,
  577. }, "\n")
  578. }
  579. func (ctx *signingCtx) buildStringToSign() {
  580. ctx.stringToSign = strings.Join([]string{
  581. authHeaderPrefix,
  582. ctx.formattedTime,
  583. ctx.credentialString,
  584. hex.EncodeToString(makeSha256([]byte(ctx.canonicalString))),
  585. }, "\n")
  586. }
  587. func (ctx *signingCtx) buildSignature() {
  588. secret := ctx.credValues.SecretAccessKey
  589. date := makeHmac([]byte("AWS4"+secret), []byte(ctx.formattedShortTime))
  590. region := makeHmac(date, []byte(ctx.Region))
  591. service := makeHmac(region, []byte(ctx.ServiceName))
  592. credentials := makeHmac(service, []byte("aws4_request"))
  593. signature := makeHmac(credentials, []byte(ctx.stringToSign))
  594. ctx.signature = hex.EncodeToString(signature)
  595. }
  596. func (ctx *signingCtx) buildBodyDigest() error {
  597. hash := ctx.Request.Header.Get("X-Amz-Content-Sha256")
  598. if hash == "" {
  599. includeSHA256Header := ctx.unsignedPayload ||
  600. ctx.ServiceName == "s3" ||
  601. ctx.ServiceName == "glacier"
  602. s3Presign := ctx.isPresign && ctx.ServiceName == "s3"
  603. if ctx.unsignedPayload || s3Presign {
  604. hash = "UNSIGNED-PAYLOAD"
  605. includeSHA256Header = !s3Presign
  606. } else if ctx.Body == nil {
  607. hash = emptyStringSHA256
  608. } else {
  609. if !aws.IsReaderSeekable(ctx.Body) {
  610. return fmt.Errorf("cannot use unseekable request body %T, for signed request with body", ctx.Body)
  611. }
  612. hash = hex.EncodeToString(makeSha256Reader(ctx.Body))
  613. }
  614. if includeSHA256Header {
  615. ctx.Request.Header.Set("X-Amz-Content-Sha256", hash)
  616. }
  617. }
  618. ctx.bodyDigest = hash
  619. return nil
  620. }
  621. // isRequestSigned returns if the request is currently signed or presigned
  622. func (ctx *signingCtx) isRequestSigned() bool {
  623. if ctx.isPresign && ctx.Query.Get("X-Amz-Signature") != "" {
  624. return true
  625. }
  626. if ctx.Request.Header.Get("Authorization") != "" {
  627. return true
  628. }
  629. return false
  630. }
  631. // unsign removes signing flags for both signed and presigned requests.
  632. func (ctx *signingCtx) removePresign() {
  633. ctx.Query.Del("X-Amz-Algorithm")
  634. ctx.Query.Del("X-Amz-Signature")
  635. ctx.Query.Del("X-Amz-Security-Token")
  636. ctx.Query.Del("X-Amz-Date")
  637. ctx.Query.Del("X-Amz-Expires")
  638. ctx.Query.Del("X-Amz-Credential")
  639. ctx.Query.Del("X-Amz-SignedHeaders")
  640. }
  641. func makeHmac(key []byte, data []byte) []byte {
  642. hash := hmac.New(sha256.New, key)
  643. hash.Write(data)
  644. return hash.Sum(nil)
  645. }
  646. func makeSha256(data []byte) []byte {
  647. hash := sha256.New()
  648. hash.Write(data)
  649. return hash.Sum(nil)
  650. }
  651. func makeSha256Reader(reader io.ReadSeeker) []byte {
  652. hash := sha256.New()
  653. start, _ := reader.Seek(0, sdkio.SeekCurrent)
  654. defer reader.Seek(start, sdkio.SeekStart)
  655. // Use CopyN to avoid allocating the 32KB buffer in io.Copy for bodies
  656. // smaller than 32KB. Fall back to io.Copy if we fail to determine the size.
  657. size, err := aws.SeekerLen(reader)
  658. if err != nil {
  659. io.Copy(hash, reader)
  660. } else {
  661. io.CopyN(hash, reader, size)
  662. }
  663. return hash.Sum(nil)
  664. }
  665. const doubleSpace = " "
  666. // stripExcessSpaces will rewrite the passed in slice's string values to not
  667. // contain multiple side-by-side spaces.
  668. func stripExcessSpaces(vals []string) {
  669. var j, k, l, m, spaces int
  670. for i, str := range vals {
  671. // Trim trailing spaces
  672. for j = len(str) - 1; j >= 0 && str[j] == ' '; j-- {
  673. }
  674. // Trim leading spaces
  675. for k = 0; k < j && str[k] == ' '; k++ {
  676. }
  677. str = str[k : j+1]
  678. // Strip multiple spaces.
  679. j = strings.Index(str, doubleSpace)
  680. if j < 0 {
  681. vals[i] = str
  682. continue
  683. }
  684. buf := []byte(str)
  685. for k, m, l = j, j, len(buf); k < l; k++ {
  686. if buf[k] == ' ' {
  687. if spaces == 0 {
  688. // First space.
  689. buf[m] = buf[k]
  690. m++
  691. }
  692. spaces++
  693. } else {
  694. // End of multiple spaces.
  695. spaces = 0
  696. buf[m] = buf[k]
  697. m++
  698. }
  699. }
  700. vals[i] = string(buf[:m])
  701. }
  702. }