From 2555707168f018a8d68061077712b5f20d06029b Mon Sep 17 00:00:00 2001 From: Cory Slep Date: Sat, 9 Mar 2019 23:56:49 +0100 Subject: [PATCH] Add test condition for auth-scheme, fix bug in HMAC --- httpsig.go | 1 + httpsig_test.go | 261 ++++++++++++++++++++++++++++-------------------- 2 files changed, 153 insertions(+), 109 deletions(-) diff --git a/httpsig.go b/httpsig.go index 3dc99a7..efe18be 100644 --- a/httpsig.go +++ b/httpsig.go @@ -206,6 +206,7 @@ func newSigner(algo Algorithm, headers []string, scheme SignatureScheme) (Signer m: m, headers: headers, targetHeader: scheme, + prefix: scheme.authScheme(), } return c, nil } diff --git a/httpsig_test.go b/httpsig_test.go index d4c5978..3f6cdde 100644 --- a/httpsig_test.go +++ b/httpsig_test.go @@ -12,10 +12,11 @@ import ( ) const ( - testUrl = "foo.net/bar/baz?q=test&r=ok" - testDate = "Tue, 07 Jun 2014 20:51:35 GMT" - testDigest = "SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=" - testMethod = "GET" + testUrl = "foo.net/bar/baz?q=test&r=ok" + testUrlPath = "bar/baz" + testDate = "Tue, 07 Jun 2014 20:51:35 GMT" + testDigest = "SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=" + testMethod = "GET" ) type httpsigTest struct { @@ -28,6 +29,7 @@ type httpsigTest struct { pubKeyId string expectedAlgorithm Algorithm expectErrorSigningResponse bool + expectRequestPath bool } var ( @@ -126,6 +128,7 @@ func init() { pubKeyId: "pubKeyId", expectedAlgorithm: RSA_SHA512, expectErrorSigningResponse: true, + expectRequestPath: true, }, } @@ -145,42 +148,59 @@ func toHeaderSignatureParameters(k string, vals []string) string { return fmt.Sprintf("%s%s%s%s%s", k, parameterKVSeparater, parameterValueDelimiter, v, parameterValueDelimiter) } -func TestNewSigner(t *testing.T) { - for _, test := range tests { +func TestSignerRequest(t *testing.T) { + testFn := func(t *testing.T, test httpsigTest) { s, a, err := NewSigner(test.prefs, test.headers, test.scheme) if err != nil { - t.Fatalf("%q: %s", test.name, err) + t.Fatalf("%s", err) } if a != test.expectedAlgorithm { - t.Fatalf("%q: got %s, want %s", test.name, a, test.expectedAlgorithm) + t.Fatalf("got %s, want %s", a, test.expectedAlgorithm) } // Test request signing req, err := http.NewRequest(testMethod, testUrl, nil) if err != nil { - t.Fatalf("%q: %s", test.name, err) + t.Fatalf("%s", err) } req.Header.Set("Date", testDate) req.Header.Set("Digest", testDigest) err = s.SignRequest(test.privKey, test.pubKeyId, req) if err != nil { - t.Fatalf("%q: %s", test.name, err) + t.Fatalf("%s", err) } vals, ok := req.Header[string(test.scheme)] if !ok { - t.Fatalf("%q: not in header %s", test.name, test.scheme) + t.Fatalf("not in header %s", test.scheme) } if len(vals) != 1 { - t.Fatalf("%q: too many in header %s: %d", test.name, test.scheme, len(vals)) + t.Fatalf("too many in header %s: %d", test.scheme, len(vals)) } if p := toSignatureParameter(keyIdParameter, test.pubKeyId); !strings.Contains(vals[0], p) { - t.Fatalf("%q: %s\ndoes not contain\n%s", test.name, vals[0], p) + t.Fatalf("%s\ndoes not contain\n%s", vals[0], p) } else if p := toSignatureParameter(algorithmParameter, string(test.expectedAlgorithm)); !strings.Contains(vals[0], p) { - t.Fatalf("%q: %s\ndoes not contain\n%s", test.name, vals[0], p) + t.Fatalf("%s\ndoes not contain\n%s", vals[0], p) } else if p := toHeaderSignatureParameters(headersParameter, test.headers); !strings.Contains(vals[0], p) { - t.Fatalf("%q: %s\ndoes not contain\n%s", test.name, vals[0], p) + t.Fatalf("%s\ndoes not contain\n%s", vals[0], p) } else if !strings.Contains(vals[0], signatureParameter) { - t.Fatalf("%q: %s\ndoes not contain\n%s", test.name, vals[0], signatureParameter) + t.Fatalf("%s\ndoes not contain\n%s", vals[0], signatureParameter) } + // For schemes with an authScheme, enforce its is present and at the beginning + if len(test.scheme.authScheme()) > 0 { + if !strings.HasPrefix(vals[0], test.scheme.authScheme()) { + t.Fatalf("%s\ndoes not start with\n%s", vals[0], test.scheme.authScheme()) + } + } + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + testFn(t, test) + }) + } +} + +func TestSignerResponse(t *testing.T) { + testFn := func(t *testing.T, test httpsigTest) { + s, _, err := NewSigner(test.prefs, test.headers, test.scheme) // Test response signing resp := httptest.NewRecorder() resp.HeaderMap.Set("Date", testDate) @@ -189,27 +209,38 @@ func TestNewSigner(t *testing.T) { if test.expectErrorSigningResponse { if err != nil { // Skip rest of testing - continue + return } else { - t.Fatalf("%q: expected error, got nil", test.name) + t.Fatalf("expected error, got nil") } } - vals, ok = resp.HeaderMap[string(test.scheme)] + vals, ok := resp.HeaderMap[string(test.scheme)] if !ok { - t.Fatalf("%q: not in header %s", test.name, test.scheme) + t.Fatalf("not in header %s", test.scheme) } if len(vals) != 1 { - t.Fatalf("%q: too many in header %s: %d", test.name, test.scheme, len(vals)) + t.Fatalf("too many in header %s: %d", test.scheme, len(vals)) } if p := toSignatureParameter(keyIdParameter, test.pubKeyId); !strings.Contains(vals[0], p) { - t.Fatalf("%q: %s\ndoes not contain\n%s", test.name, vals[0], p) + t.Fatalf("%s\ndoes not contain\n%s", vals[0], p) } else if p := toSignatureParameter(algorithmParameter, string(test.expectedAlgorithm)); !strings.Contains(vals[0], p) { - t.Fatalf("%q: %s\ndoes not contain\n%s", test.name, vals[0], p) + t.Fatalf("%s\ndoes not contain\n%s", vals[0], p) } else if p := toHeaderSignatureParameters(headersParameter, test.headers); !strings.Contains(vals[0], p) { - t.Fatalf("%q: %s\ndoes not contain\n%s", test.name, vals[0], p) + t.Fatalf("%s\ndoes not contain\n%s", vals[0], p) } else if !strings.Contains(vals[0], signatureParameter) { - t.Fatalf("%q: %s\ndoes not contain\n%s", test.name, vals[0], signatureParameter) + t.Fatalf("%s\ndoes not contain\n%s", vals[0], signatureParameter) } + // For schemes with an authScheme, enforce its is present and at the beginning + if len(test.scheme.authScheme()) > 0 { + if !strings.HasPrefix(vals[0], test.scheme.authScheme()) { + t.Fatalf("%s\ndoes not start with\n%s", vals[0], test.scheme.authScheme()) + } + } + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + testFn(t, test) + }) } } @@ -234,22 +265,25 @@ func TestNewSignerRequestMissingHeaders(t *testing.T) { }, } for _, test := range failingTests { - s, a, err := NewSigner(test.prefs, test.headers, test.scheme) - if err != nil { - t.Fatalf("%q: %s", test.name, err) - } - if a != test.expectedAlgorithm { - t.Fatalf("%q: got %s, want %s", test.name, a, test.expectedAlgorithm) - } - req, err := http.NewRequest(testMethod, testUrl, nil) - if err != nil { - t.Fatalf("%q: %s", test.name, err) - } - req.Header.Set("Date", testDate) - err = s.SignRequest(test.privKey, test.pubKeyId, req) - if err == nil { - t.Fatalf("%q: expect error but got nil", test.name) - } + t.Run(test.name, func(t *testing.T) { + test := test + s, a, err := NewSigner(test.prefs, test.headers, test.scheme) + if err != nil { + t.Fatalf("%s", err) + } + if a != test.expectedAlgorithm { + t.Fatalf("got %s, want %s", a, test.expectedAlgorithm) + } + req, err := http.NewRequest(testMethod, testUrl, nil) + if err != nil { + t.Fatalf("%s", err) + } + req.Header.Set("Date", testDate) + err = s.SignRequest(test.privKey, test.pubKeyId, req) + if err == nil { + t.Fatalf("expect error but got nil") + } + }) } } @@ -275,83 +309,92 @@ func TestNewSignerResponseMissingHeaders(t *testing.T) { }, } for _, test := range failingTests { - s, a, err := NewSigner(test.prefs, test.headers, test.scheme) - if err != nil { - t.Fatalf("%q: %s", test.name, err) - } - if a != test.expectedAlgorithm { - t.Fatalf("%q: got %s, want %s", test.name, a, test.expectedAlgorithm) - } - resp := httptest.NewRecorder() - resp.HeaderMap.Set("Date", testDate) - resp.HeaderMap.Set("Digest", testDigest) - err = s.SignResponse(test.privKey, test.pubKeyId, resp) - if err != nil { - t.Fatalf("%q: expected error, got nil", test.name) - } + t.Run(test.name, func(t *testing.T) { + test := test + s, a, err := NewSigner(test.prefs, test.headers, test.scheme) + if err != nil { + t.Fatalf("%s", err) + } + if a != test.expectedAlgorithm { + t.Fatalf("got %s, want %s", a, test.expectedAlgorithm) + } + resp := httptest.NewRecorder() + resp.HeaderMap.Set("Date", testDate) + resp.HeaderMap.Set("Digest", testDigest) + err = s.SignResponse(test.privKey, test.pubKeyId, resp) + if err != nil { + t.Fatalf("expected error, got nil") + } + }) } } func TestNewVerifier(t *testing.T) { for _, test := range tests { - // Prepare - req, err := http.NewRequest(testMethod, testUrl, nil) - if err != nil { - t.Fatalf("%q: %s", test.name, err) - } - req.Header.Set("Date", testDate) - req.Header.Set("Digest", testDigest) - s, _, err := NewSigner(test.prefs, test.headers, test.scheme) - if err != nil { - t.Fatalf("%q: %s", test.name, err) - } - err = s.SignRequest(test.privKey, test.pubKeyId, req) - if err != nil { - t.Fatalf("%q: %s", test.name, err) - } - // Test verification - v, err := NewVerifier(req) - if err != nil { - t.Fatalf("%q: %s", test.name, err) - } - if v.KeyId() != test.pubKeyId { - t.Fatalf("%q: got %s, want %s", test.name, v.KeyId(), test.pubKeyId) - } - err = v.Verify(test.pubKey, test.expectedAlgorithm) - if err != nil { - t.Fatalf("%q: %s", test.name, err) - } + t.Run(test.name, func(t *testing.T) { + test := test + // Prepare + req, err := http.NewRequest(testMethod, testUrl, nil) + if err != nil { + t.Fatalf("%s", err) + } + req.Header.Set("Date", testDate) + req.Header.Set("Digest", testDigest) + s, _, err := NewSigner(test.prefs, test.headers, test.scheme) + if err != nil { + t.Fatalf("%s", err) + } + err = s.SignRequest(test.privKey, test.pubKeyId, req) + if err != nil { + t.Fatalf("%s", err) + } + // Test verification + v, err := NewVerifier(req) + if err != nil { + t.Fatalf("%s", err) + } + if v.KeyId() != test.pubKeyId { + t.Fatalf("got %s, want %s", v.KeyId(), test.pubKeyId) + } + err = v.Verify(test.pubKey, test.expectedAlgorithm) + if err != nil { + t.Fatalf("%s", err) + } + }) } } func TestNewResponseVerifier(t *testing.T) { for _, test := range tests { - if test.expectErrorSigningResponse { - continue - } - // Prepare - resp := httptest.NewRecorder() - resp.HeaderMap.Set("Date", testDate) - resp.HeaderMap.Set("Digest", testDigest) - s, _, err := NewSigner(test.prefs, test.headers, test.scheme) - if err != nil { - t.Fatalf("%q: %s", test.name, err) - } - err = s.SignResponse(test.privKey, test.pubKeyId, resp) - if err != nil { - t.Fatalf("%q: %s", test.name, err) - } - // Test verification - v, err := NewResponseVerifier(resp.Result()) - if err != nil { - t.Fatalf("%q: %s", test.name, err) - } - if v.KeyId() != test.pubKeyId { - t.Fatalf("%q: got %s, want %s", test.name, v.KeyId(), test.pubKeyId) - } - err = v.Verify(test.pubKey, test.expectedAlgorithm) - if err != nil { - t.Fatalf("%q: %s", test.name, err) - } + t.Run(test.name, func(t *testing.T) { + test := test + if test.expectErrorSigningResponse { + return + } + // Prepare + resp := httptest.NewRecorder() + resp.HeaderMap.Set("Date", testDate) + resp.HeaderMap.Set("Digest", testDigest) + s, _, err := NewSigner(test.prefs, test.headers, test.scheme) + if err != nil { + t.Fatalf("%s", err) + } + err = s.SignResponse(test.privKey, test.pubKeyId, resp) + if err != nil { + t.Fatalf("%s", err) + } + // Test verification + v, err := NewResponseVerifier(resp.Result()) + if err != nil { + t.Fatalf("%s", err) + } + if v.KeyId() != test.pubKeyId { + t.Fatalf("got %s, want %s", v.KeyId(), test.pubKeyId) + } + err = v.Verify(test.pubKey, test.expectedAlgorithm) + if err != nil { + t.Fatalf("%s", err) + } + }) } }