Skip to content

Commit

Permalink
More refactoring; starting S3 signing
Browse files Browse the repository at this point in the history
  • Loading branch information
mholt committed Nov 5, 2013
1 parent 272f2a2 commit 22144d1
Show file tree
Hide file tree
Showing 8 changed files with 68 additions and 40 deletions.
9 changes: 8 additions & 1 deletion awsauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ func Sign(req *http.Request) *http.Request {
return Sign2(req)
case 4:
return Sign4(req)
case -1:
return SignS3(req)
}

return nil
Expand All @@ -41,7 +43,7 @@ func Sign4(req *http.Request) *http.Request {
signingKey := signingKeyV4(Keys.SecretAccessKey, meta.date, meta.region, meta.service)
signature := signatureV4(signingKey, stringToSign)

req.Header.Set("Authorization", buildAuthHeader(signature, meta))
req.Header.Set("Authorization", buildAuthHeaderV4(signature, meta))

return req
}
Expand All @@ -61,6 +63,11 @@ func Sign2(req *http.Request) *http.Request {
return req
}

func SignS3(req *http.Request) *http.Request {
// TODO
return req
}

type metadata struct {
algorithm string
credentialScope string
Expand Down
2 changes: 1 addition & 1 deletion awsauth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func TestIntegration(t *testing.T) {

Convey("A request to S3 should succeed", nil)

Convey("A request to EC2 should succeed", func() {
Convey(`A request to EC2 should succeed <!--<audio src="its-working.mp3" autoplay></audio>-->`, func() {
req := newRequest("GET", "https://ec2.amazonaws.com", url.Values{
"Action": []string{"DescribeInstances"},
})
Expand Down
8 changes: 8 additions & 0 deletions common.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"crypto/hmac"
"crypto/sha256"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
Expand Down Expand Up @@ -56,6 +57,13 @@ func hashSHA256(content string) string {
return fmt.Sprintf("%x", h.Sum(nil))
}

func readAndReplaceBody(req *http.Request) string {
rawPayload, _ := ioutil.ReadAll(req.Body)
payload := string(rawPayload)
req.Body = ioutil.NopCloser(strings.NewReader(payload))
return payload
}

func concat(delim string, str ...string) string {
return strings.Join(str, delim)
}
Expand Down
2 changes: 1 addition & 1 deletion common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func TestCommonFunctions(t *testing.T) {
input := "This is... Sparta!!"
actual := hashSHA256(input)

So(actual, ShouldEqual, expecting["Sha256Hash"])
So(actual, ShouldEqual, "5c81a4ef1172e89b1a9d575f4cd82f4ed20ea9137e61aa7f1ab936291d24e79a")
})

Convey("Given a key and contents", t, func() {
Expand Down
10 changes: 10 additions & 0 deletions s3.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package awsauth

import (
"fmt"
)

func signatureS3(stringToSign string) string {
fmt.Println("TODO")
return ""
}
14 changes: 14 additions & 0 deletions s3_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package awsauth

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

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

}
11 changes: 1 addition & 10 deletions sign4.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ package awsauth

import (
"encoding/hex"
"io/ioutil"
"net/http"
"strings"
)

func hashedCanonicalRequestV4(req *http.Request, meta *metadata) string {
Expand Down Expand Up @@ -68,14 +66,7 @@ func signingKeyV4(secretKey, date, region, service string) []byte {
return kSigning
}

func readAndReplaceBody(req *http.Request) string {
rawPayload, _ := ioutil.ReadAll(req.Body)
payload := string(rawPayload)
req.Body = ioutil.NopCloser(strings.NewReader(payload))
return payload
}

func buildAuthHeader(signature string, meta *metadata) string {
func buildAuthHeaderV4(signature string, meta *metadata) string {
credential := Keys.AccessKeyID + "/" + meta.credentialScope

return meta.algorithm +
Expand Down
52 changes: 25 additions & 27 deletions sign4_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import (

func TestVersion4RequestPreparer(t *testing.T) {
Convey("Given a plain request with no custom headers", t, func() {
req := plainRequest(false)
req := test_plainRequestV4(false)

expectedUnsigned := unsignedRequest(true)
expectedUnsigned := test_unsignedRequestV4(true)
expectedUnsigned.Header.Set("X-Amz-Date", timestampV4())

Convey("The necessary, default headers should be appended", func() {
Expand All @@ -26,14 +26,14 @@ func TestVersion4RequestPreparer(t *testing.T) {
})

Convey("And a set of credentials", func() {
Keys = testCred
Keys = testCredV4

Convey("It should be signed with an Authorization header", func() {
actualSigned := Sign4(req)
actual := actualSigned.Header.Get("Authorization")

So(actual, ShouldNotBeBlank)
So(actual, ShouldContainSubstring, "Credential="+testCred.AccessKeyID)
So(actual, ShouldContainSubstring, "Credential="+testCredV4.AccessKeyID)
So(actual, ShouldContainSubstring, "SignedHeaders=")
So(actual, ShouldContainSubstring, "Signature=")
So(actual, ShouldContainSubstring, "AWS4")
Expand All @@ -43,9 +43,9 @@ func TestVersion4RequestPreparer(t *testing.T) {

Convey("Given a request with custom, necessary headers", t, func() {
Convey("The custom, necessary headers must not be changed", func() {
req := unsignedRequest(true)
req := test_unsignedRequestV4(true)
prepareRequestV4(req)
So(req, ShouldResemble, unsignedRequest(true))
So(req, ShouldResemble, test_unsignedRequestV4(true))
})
})
}
Expand All @@ -54,37 +54,36 @@ func TestVersion4SigningTasks(t *testing.T) {
// http://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html

Convey("Given a bogus request and credentials from AWS documentation", t, func() {
req := unsignedRequest(true)
req := test_unsignedRequestV4(true)
meta := new(metadata)

Convey("(Task 1) The canonical request should be built correctly", func() {
hashedCanonReq := hashedCanonicalRequestV4(req, meta)

So(hashedCanonReq, ShouldEqual, expecting["CanonicalHash"])
So(hashedCanonReq, ShouldEqual, expectingV4["CanonicalHash"])
})

Convey("(Task 2) The string to sign should be built correctly", func() {
hashedCanonReq := hashedCanonicalRequestV4(req, meta)
stringToSign := stringToSignV4(req, hashedCanonReq, meta)

So(stringToSign, ShouldEqual, expecting["StringToSign"])
So(stringToSign, ShouldEqual, expectingV4["StringToSign"])
})

Convey("(Task 3) The version 4 signed signature should be correct", func() {
hashedCanonReq := hashedCanonicalRequestV4(req, meta)
stringToSign := stringToSignV4(req, hashedCanonReq, meta)
signature := signatureV4(testSigningKey(), stringToSign)
signature := signatureV4(test_signingKeyV4(), stringToSign)

So(signature, ShouldEqual, expecting["SignatureV4"])
So(signature, ShouldEqual, expectingV4["SignatureV4"])
})
})
}

func TestSignature4Helpers(t *testing.T) {

Convey("The signing key should be properly generated", t, func() {
expected := []byte{152, 241, 216, 137, 254, 196, 244, 66, 26, 220, 82, 43, 171, 12, 225, 248, 46, 105, 41, 194, 98, 237, 21, 229, 169, 76, 144, 239, 209, 227, 176, 231}
actual := testSigningKey()
actual := test_signingKeyV4()

So(actual, ShouldResemble, expected)
})
Expand All @@ -95,8 +94,8 @@ func TestSignature4Helpers(t *testing.T) {
credentialScope: "20110909/us-east-1/iam/aws4_request",
signedHeaders: "content-type;host;x-amz-date",
}
expected := expecting["AuthHeader"] + expecting["SignatureV4"]
actual := buildAuthHeader(expecting["SignatureV4"], meta)
expected := expectingV4["AuthHeader"] + expectingV4["SignatureV4"]
actual := buildAuthHeaderV4(expectingV4["SignatureV4"], meta)

So(actual, ShouldEqual, expected)
})
Expand All @@ -121,10 +120,10 @@ func TestSignature4Helpers(t *testing.T) {
})

Convey("Given any request with a body", t, func() {
req := plainRequest(false)
req := test_plainRequestV4(false)

Convey("Its body should be read and replaced without differences", func() {
expected := requestValues.Encode()
expected := requestValuesV4.Encode()

actual1 := readAndReplaceBody(req)
So(actual1, ShouldEqual, expected)
Expand All @@ -135,9 +134,9 @@ func TestSignature4Helpers(t *testing.T) {
})
}

func plainRequest(trailingSlash bool) *http.Request {
func test_plainRequestV4(trailingSlash bool) *http.Request {
url := "http://iam.amazonaws.com"
body := strings.NewReader(requestValues.Encode())
body := strings.NewReader(requestValuesV4.Encode())

if trailingSlash {
url += "/"
Expand All @@ -152,32 +151,31 @@ func plainRequest(trailingSlash bool) *http.Request {
return req
}

func unsignedRequest(trailingSlash bool) *http.Request {
req := plainRequest(trailingSlash)
func test_unsignedRequestV4(trailingSlash bool) *http.Request {
req := test_plainRequestV4(trailingSlash)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=utf-8")
req.Header.Set("X-Amz-Date", "20110909T233600Z")
return req
}

func testSigningKey() []byte {
return signingKeyV4(testCred.SecretAccessKey, "20110909", "us-east-1", "iam")
func test_signingKeyV4() []byte {
return signingKeyV4(testCredV4.SecretAccessKey, "20110909", "us-east-1", "iam")
}

var (
testCred = &Credentials{
testCredV4 = &Credentials{
"AKIDEXAMPLE",
"wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY",
}

expecting = map[string]string{
expectingV4 = map[string]string{
"CanonicalHash": "3511de7e95d28ecd39e9513b642aee07e54f4941150d8df8bf94b328ef7e55e2",
"StringToSign": "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/iam/aws4_request\n3511de7e95d28ecd39e9513b642aee07e54f4941150d8df8bf94b328ef7e55e2",
"SignatureV4": "ced6826de92d2bdeed8f846f0bf508e8559e98e4b0199114b84c54174deb456c",
"Sha256Hash": "5c81a4ef1172e89b1a9d575f4cd82f4ed20ea9137e61aa7f1ab936291d24e79a",
"AuthHeader": "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/iam/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=",
}

requestValues = &url.Values{
requestValuesV4 = &url.Values{
"Action": []string{"ListUsers"},
"Version": []string{"2010-05-08"},
}
Expand Down

0 comments on commit 22144d1

Please sign in to comment.