Skip to content

Commit

Permalink
Trucking along with S3 signing
Browse files Browse the repository at this point in the history
  • Loading branch information
mholt committed Nov 5, 2013
1 parent 22144d1 commit 788b42d
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 8 deletions.
24 changes: 22 additions & 2 deletions common.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package awsauth

import (
"crypto/hmac"
"crypto/sha1"
"crypto/sha256"
"fmt"
"io/ioutil"
Expand All @@ -15,12 +16,25 @@ import (
func serviceAndRegion(host string) (string, string) {
var region, service string
parts := strings.Split(host, ".")

service = parts[0]

if len(parts) >= 4 {
region = parts[1]
if parts[1] == "s3" {
region = parts[0]
service = parts[1]
} else {
region = parts[1]
}
} else {
region = "us-east-1" // default. http://docs.aws.amazon.com/general/latest/gr/rande.html
if strings.HasPrefix(parts[0], "s3-") {
service = parts[0][:2]
region = parts[0][3:]
} else {
region = "us-east-1" // default. http://docs.aws.amazon.com/general/latest/gr/rande.html
}
}

return service, region
}

Expand Down Expand Up @@ -51,6 +65,12 @@ func hmacSHA256(key []byte, content string) []byte {
return mac.Sum(nil)
}

func hmacSHA1(key []byte, content string) []byte {
mac := hmac.New(sha1.New, key)
mac.Write([]byte(content))
return mac.Sum(nil)
}

func hashSHA256(content string) string {
h := sha256.New()
h.Write([]byte(content))
Expand Down
19 changes: 19 additions & 0 deletions common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@ func TestCommonFunctions(t *testing.T) {
s2, r2 := serviceAndRegion("iam.amazonaws.com")
So(s2, ShouldEqual, "iam")
So(r2, ShouldEqual, "us-east-1")

s3, r3 := serviceAndRegion("bucketname.s3.amazonaws.com")
So(s3, ShouldEqual, "s3")
So(r3, ShouldEqual, "bucketname")

s4, r4 := serviceAndRegion("s3.amazonaws.com")
So(s4, ShouldEqual, "s3")
So(r4, ShouldEqual, "us-east-1")

s5, r5 := serviceAndRegion("s3-us-west-1.amazonaws.com")
So(s5, ShouldEqual, "s3")
So(r5, ShouldEqual, "us-west-1")
})

Convey("SHA-256 hashes should be properly hex-encoded (base 16)", t, func() {
Expand All @@ -33,6 +45,13 @@ func TestCommonFunctions(t *testing.T) {

So(actual, ShouldResemble, expected)
})

Convey("HMAC-SHA1 should be properly computed", func() {
expected := []byte{164, 77, 252, 0, 87, 109, 207, 110, 163, 75, 228, 122, 83, 255, 233, 237, 125, 206, 85, 70}
actual := hmacSHA1(key, contents)

So(actual, ShouldResemble, expected)
})
})

Convey("Strings should be properly concatenated with a delimiter", t, func() {
Expand Down
64 changes: 64 additions & 0 deletions s3.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,73 @@ package awsauth

import (
"fmt"
"net/http"
"sort"
"strings"
"time"
)

func signatureS3(stringToSign string) string {
fmt.Println("TODO")
return ""
}

func canonicalAmzHeadersS3(req *http.Request) string {
var headers []string

for header := range req.Header {
standardized := strings.ToLower(strings.TrimSpace(header))
if strings.HasPrefix(standardized, "x-amz") {
headers = append(headers, standardized)
}
}

sort.Strings(headers)

for i, header := range headers {
headers[i] = header + ":" + strings.Replace(req.Header.Get(header), "\n", " ", -1)
}

return strings.Join(headers, "\n") + "\n"
}

func canonicalResourceS3(req *http.Request) string {
res := ""

if isS3VirtualHostedStyle(req) {
_, bucketname := serviceAndRegion(req.Host)
res += "/" + bucketname
}

res += req.URL.Path

for _, subres := range strings.Split(subresourcesS3, ",") {
if strings.HasPrefix(req.URL.RawQuery, subres) {
res += "?" + subres
}
}

return res
}

func prepareRequestS3(req *http.Request) *http.Request {
// TODO: test
if req.URL.Path == "" {
req.URL.Path += "/"
}
return req
}

func isS3VirtualHostedStyle(req *http.Request) bool {
service, _ := serviceAndRegion(req.Host)
return service == "s3" && strings.Count(req.Host, ".") == 3
}

func timestampS3() string {
return now().Format(timeFormatS3)
}

const (
timeFormatS3 = time.RFC1123Z
subresourcesS3 = "acl,lifecycle,location,logging,notification,partNumber,policy,requestPayment,torrent,uploadId,uploads,versionId,versioning,versions,website"
)
28 changes: 24 additions & 4 deletions s3_test.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,34 @@
package awsauth

import (
//. "github.com/smartystreets/goconvey/convey"
//"net/http"
//"net/url"
. "github.com/smartystreets/goconvey/convey"
"net/http"
"testing"
//"time"
)

func TestSignatureS3(t *testing.T) {
// http://docs.aws.amazon.com/AmazonS3/2006-03-01/dev/RESTAuthentication.html

Convey("Given a GET request to Amazon S3", t, func() {
req, _ := http.NewRequest("GET", "https://bucketname.s3.amazonaws.com/photos/puppy.jpg", nil)
req.Header.Set("X-Amz-Meta-Something", "more foobar")
req.Header.Set("X-Amz-Date", "foobar")
req.Header.Set("X-Foobar", "nanoo-nanoo")

Convey("The CanonicalizedAmzHeaders should be built properly", func() {
actual := canonicalAmzHeadersS3(req)
So(actual, ShouldEqual, expectedCanonAmzHeadersS3)
})

Convey("The CanonicalizedResource should be built properly", func() {
actual := canonicalResourceS3(req)
So(actual, ShouldEqual, expectedCanonResourceS3)
})
})
}

var (
testCredS3 = &Credentials{"AKIAIOSFODNN7EXAMPLE", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"}
expectedCanonAmzHeadersS3 = "x-amz-date:foobar\nx-amz-meta-something:more foobar\n"
expectedCanonResourceS3 = "/bucketname/photos/puppy.jpg"
)
3 changes: 1 addition & 2 deletions sign4.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,7 @@ func buildAuthHeaderV4(signature string, meta *metadata) string {
}

func timestampV4() string {
t := now().Format(timeFormatV4)
return t
return now().Format(timeFormatV4)
}

func tsDateV4(timestamp string) string {
Expand Down

0 comments on commit 788b42d

Please sign in to comment.