зеркало из https://github.com/github/s3gof3r.git
196 строки
4.3 KiB
Go
196 строки
4.3 KiB
Go
package s3gof3r
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/hmac"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
prefix = "AWS4-HMAC-SHA256"
|
|
isoFormat = "20060102T150405Z"
|
|
shortDate = "20060102"
|
|
)
|
|
|
|
var ignoredHeaders = map[string]bool{
|
|
"Authorization": true,
|
|
"Content-Type": true,
|
|
"Content-Length": true,
|
|
"User-Agent": true,
|
|
}
|
|
|
|
type signer struct {
|
|
Time time.Time
|
|
Request *http.Request
|
|
Region string
|
|
Keys Keys
|
|
|
|
credentialString string
|
|
signedHeaders string
|
|
signature string
|
|
|
|
canonicalHeaders string
|
|
canonicalString string
|
|
stringToSign string
|
|
}
|
|
|
|
// Sign signs the http.Request
|
|
func (b *Bucket) Sign(req *http.Request) {
|
|
if req.Header == nil {
|
|
req.Header = http.Header{}
|
|
}
|
|
if b.S3.Keys.SecurityToken != "" {
|
|
req.Header.Set("X-Amz-Security-Token", b.S3.Keys.SecurityToken)
|
|
}
|
|
req.Header.Set("User-Agent", "S3Gof3r")
|
|
s := &signer{
|
|
Time: time.Now(),
|
|
Request: req,
|
|
Region: b.S3.Region(),
|
|
Keys: b.S3.Keys,
|
|
}
|
|
s.sign()
|
|
}
|
|
|
|
func (s *signer) sign() {
|
|
s.buildTime()
|
|
s.buildCredentialString()
|
|
s.buildCanonicalHeaders()
|
|
s.buildCanonicalString()
|
|
s.buildStringToSign()
|
|
s.buildSignature()
|
|
parts := []string{
|
|
prefix + " Credential=" + s.Keys.AccessKey + "/" + s.credentialString,
|
|
"SignedHeaders=" + s.signedHeaders,
|
|
"Signature=" + s.signature,
|
|
}
|
|
s.Request.Header.Set("Authorization", strings.Join(parts, ","))
|
|
}
|
|
|
|
func (s *signer) buildTime() {
|
|
s.Request.Header.Set("X-Amz-Date", s.Time.UTC().Format(isoFormat))
|
|
}
|
|
|
|
func (s *signer) buildCredentialString() {
|
|
s.credentialString = strings.Join([]string{
|
|
s.Time.UTC().Format(shortDate),
|
|
s.Region,
|
|
"s3",
|
|
"aws4_request",
|
|
}, "/")
|
|
}
|
|
|
|
func (s *signer) buildCanonicalHeaders() {
|
|
var headers []string
|
|
headers = append(headers, "host")
|
|
for k := range s.Request.Header {
|
|
if _, ok := ignoredHeaders[http.CanonicalHeaderKey(k)]; ok {
|
|
continue
|
|
}
|
|
headers = append(headers, strings.ToLower(k))
|
|
}
|
|
sort.Strings(headers)
|
|
|
|
s.signedHeaders = strings.Join(headers, ";")
|
|
|
|
headerValues := make([]string, len(headers))
|
|
for i, k := range headers {
|
|
if k == "host" {
|
|
headerValues[i] = "host:" + s.Request.URL.Host
|
|
} else {
|
|
headerValues[i] = k + ":" +
|
|
strings.Join(s.Request.Header[http.CanonicalHeaderKey(k)], ",")
|
|
}
|
|
}
|
|
|
|
s.canonicalHeaders = strings.Join(headerValues, "\n")
|
|
}
|
|
|
|
func (s *signer) buildCanonicalString() {
|
|
s.Request.URL.RawQuery = strings.Replace(s.Request.URL.Query().Encode(), "+", "%20", -1)
|
|
uri := s.Request.URL.Opaque
|
|
if uri != "" {
|
|
uri = "/" + strings.Join(strings.Split(uri, "/")[3:], "/")
|
|
} else {
|
|
uri = s.Request.URL.EscapedPath()
|
|
}
|
|
if uri == "" {
|
|
uri = "/"
|
|
}
|
|
|
|
uri = strings.Replace(uri, "@", "%40", -1)
|
|
uri = strings.Replace(uri, ":", "%3A", -1)
|
|
|
|
s.canonicalString = strings.Join([]string{
|
|
s.Request.Method,
|
|
uri,
|
|
s.Request.URL.RawQuery,
|
|
s.canonicalHeaders + "\n",
|
|
s.signedHeaders,
|
|
s.bodyDigest(),
|
|
}, "\n")
|
|
}
|
|
|
|
func (s *signer) buildStringToSign() {
|
|
s.stringToSign = strings.Join([]string{
|
|
prefix,
|
|
s.Time.UTC().Format(isoFormat),
|
|
s.credentialString,
|
|
hex.EncodeToString(sha([]byte(s.canonicalString))),
|
|
}, "\n")
|
|
}
|
|
|
|
func (s *signer) buildSignature() {
|
|
secret := s.Keys.SecretKey
|
|
date := hmacSign([]byte("AWS4"+secret), []byte(s.Time.UTC().Format(shortDate)))
|
|
region := hmacSign(date, []byte(s.Region))
|
|
service := hmacSign(region, []byte("s3"))
|
|
credentials := hmacSign(service, []byte("aws4_request"))
|
|
signature := hmacSign(credentials, []byte(s.stringToSign))
|
|
s.signature = hex.EncodeToString(signature)
|
|
}
|
|
|
|
func (s *signer) bodyDigest() string {
|
|
hash := s.Request.Header.Get("X-Amz-Content-Sha256")
|
|
if hash == "" {
|
|
if s.Request.Body == nil {
|
|
hash = hex.EncodeToString(sha([]byte{}))
|
|
} else {
|
|
body, _ := ioutil.ReadAll(s.Request.Body)
|
|
s.Request.Body = ioutil.NopCloser(bytes.NewReader(body))
|
|
hash = hex.EncodeToString(sha(body))
|
|
}
|
|
s.Request.Header.Add("X-Amz-Content-Sha256", hash)
|
|
}
|
|
return hash
|
|
}
|
|
|
|
func hmacSign(key []byte, data []byte) []byte {
|
|
hash := hmac.New(sha256.New, key)
|
|
hash.Write(data)
|
|
return hash.Sum(nil)
|
|
}
|
|
|
|
func sha(data []byte) []byte {
|
|
hash := sha256.New()
|
|
hash.Write(data)
|
|
return hash.Sum(nil)
|
|
}
|
|
|
|
func shaReader(r io.ReadSeeker) string {
|
|
hash := sha256.New()
|
|
start, _ := r.Seek(0, 1)
|
|
defer r.Seek(start, 0)
|
|
|
|
io.Copy(hash, r)
|
|
sum := hash.Sum(nil)
|
|
return hex.EncodeToString(sum)
|
|
}
|