Add test condition for auth-scheme, fix bug in HMAC

このコミットが含まれているのは:
Cory Slep 2019-03-09 23:56:49 +01:00
コミット 2555707168
2個のファイルの変更153行の追加109行の削除

ファイルの表示

@ -206,6 +206,7 @@ func newSigner(algo Algorithm, headers []string, scheme SignatureScheme) (Signer
m: m, m: m,
headers: headers, headers: headers,
targetHeader: scheme, targetHeader: scheme,
prefix: scheme.authScheme(),
} }
return c, nil return c, nil
} }

ファイルの表示

@ -12,10 +12,11 @@ import (
) )
const ( const (
testUrl = "foo.net/bar/baz?q=test&r=ok" testUrl = "foo.net/bar/baz?q=test&r=ok"
testDate = "Tue, 07 Jun 2014 20:51:35 GMT" testUrlPath = "bar/baz"
testDigest = "SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=" testDate = "Tue, 07 Jun 2014 20:51:35 GMT"
testMethod = "GET" testDigest = "SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE="
testMethod = "GET"
) )
type httpsigTest struct { type httpsigTest struct {
@ -28,6 +29,7 @@ type httpsigTest struct {
pubKeyId string pubKeyId string
expectedAlgorithm Algorithm expectedAlgorithm Algorithm
expectErrorSigningResponse bool expectErrorSigningResponse bool
expectRequestPath bool
} }
var ( var (
@ -126,6 +128,7 @@ func init() {
pubKeyId: "pubKeyId", pubKeyId: "pubKeyId",
expectedAlgorithm: RSA_SHA512, expectedAlgorithm: RSA_SHA512,
expectErrorSigningResponse: true, 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) return fmt.Sprintf("%s%s%s%s%s", k, parameterKVSeparater, parameterValueDelimiter, v, parameterValueDelimiter)
} }
func TestNewSigner(t *testing.T) { func TestSignerRequest(t *testing.T) {
for _, test := range tests { testFn := func(t *testing.T, test httpsigTest) {
s, a, err := NewSigner(test.prefs, test.headers, test.scheme) s, a, err := NewSigner(test.prefs, test.headers, test.scheme)
if err != nil { if err != nil {
t.Fatalf("%q: %s", test.name, err) t.Fatalf("%s", err)
} }
if a != test.expectedAlgorithm { 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 // Test request signing
req, err := http.NewRequest(testMethod, testUrl, nil) req, err := http.NewRequest(testMethod, testUrl, nil)
if err != nil { if err != nil {
t.Fatalf("%q: %s", test.name, err) t.Fatalf("%s", err)
} }
req.Header.Set("Date", testDate) req.Header.Set("Date", testDate)
req.Header.Set("Digest", testDigest) req.Header.Set("Digest", testDigest)
err = s.SignRequest(test.privKey, test.pubKeyId, req) err = s.SignRequest(test.privKey, test.pubKeyId, req)
if err != nil { if err != nil {
t.Fatalf("%q: %s", test.name, err) t.Fatalf("%s", err)
} }
vals, ok := req.Header[string(test.scheme)] vals, ok := req.Header[string(test.scheme)]
if !ok { 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 { 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) { 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) { } 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) { } 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) { } 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 // Test response signing
resp := httptest.NewRecorder() resp := httptest.NewRecorder()
resp.HeaderMap.Set("Date", testDate) resp.HeaderMap.Set("Date", testDate)
@ -189,27 +209,38 @@ func TestNewSigner(t *testing.T) {
if test.expectErrorSigningResponse { if test.expectErrorSigningResponse {
if err != nil { if err != nil {
// Skip rest of testing // Skip rest of testing
continue return
} else { } 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 { 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 { 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) { 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) { } 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) { } 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) { } 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 { for _, test := range failingTests {
s, a, err := NewSigner(test.prefs, test.headers, test.scheme) t.Run(test.name, func(t *testing.T) {
if err != nil { test := test
t.Fatalf("%q: %s", test.name, err) s, a, err := NewSigner(test.prefs, test.headers, test.scheme)
} if err != nil {
if a != test.expectedAlgorithm { t.Fatalf("%s", err)
t.Fatalf("%q: got %s, want %s", test.name, a, test.expectedAlgorithm) }
} if a != test.expectedAlgorithm {
req, err := http.NewRequest(testMethod, testUrl, nil) t.Fatalf("got %s, want %s", a, test.expectedAlgorithm)
if err != nil { }
t.Fatalf("%q: %s", test.name, err) req, err := http.NewRequest(testMethod, testUrl, nil)
} if err != nil {
req.Header.Set("Date", testDate) t.Fatalf("%s", err)
err = s.SignRequest(test.privKey, test.pubKeyId, req) }
if err == nil { req.Header.Set("Date", testDate)
t.Fatalf("%q: expect error but got nil", test.name) 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 { for _, test := range failingTests {
s, a, err := NewSigner(test.prefs, test.headers, test.scheme) t.Run(test.name, func(t *testing.T) {
if err != nil { test := test
t.Fatalf("%q: %s", test.name, err) s, a, err := NewSigner(test.prefs, test.headers, test.scheme)
} if err != nil {
if a != test.expectedAlgorithm { t.Fatalf("%s", err)
t.Fatalf("%q: got %s, want %s", test.name, a, test.expectedAlgorithm) }
} if a != test.expectedAlgorithm {
resp := httptest.NewRecorder() t.Fatalf("got %s, want %s", a, test.expectedAlgorithm)
resp.HeaderMap.Set("Date", testDate) }
resp.HeaderMap.Set("Digest", testDigest) resp := httptest.NewRecorder()
err = s.SignResponse(test.privKey, test.pubKeyId, resp) resp.HeaderMap.Set("Date", testDate)
if err != nil { resp.HeaderMap.Set("Digest", testDigest)
t.Fatalf("%q: expected error, got nil", test.name) err = s.SignResponse(test.privKey, test.pubKeyId, resp)
} if err != nil {
t.Fatalf("expected error, got nil")
}
})
} }
} }
func TestNewVerifier(t *testing.T) { func TestNewVerifier(t *testing.T) {
for _, test := range tests { for _, test := range tests {
// Prepare t.Run(test.name, func(t *testing.T) {
req, err := http.NewRequest(testMethod, testUrl, nil) test := test
if err != nil { // Prepare
t.Fatalf("%q: %s", test.name, err) req, err := http.NewRequest(testMethod, testUrl, nil)
} if err != nil {
req.Header.Set("Date", testDate) t.Fatalf("%s", err)
req.Header.Set("Digest", testDigest) }
s, _, err := NewSigner(test.prefs, test.headers, test.scheme) req.Header.Set("Date", testDate)
if err != nil { req.Header.Set("Digest", testDigest)
t.Fatalf("%q: %s", test.name, err) s, _, err := NewSigner(test.prefs, test.headers, test.scheme)
} if err != nil {
err = s.SignRequest(test.privKey, test.pubKeyId, req) t.Fatalf("%s", err)
if err != nil { }
t.Fatalf("%q: %s", test.name, err) err = s.SignRequest(test.privKey, test.pubKeyId, req)
} if err != nil {
// Test verification t.Fatalf("%s", err)
v, err := NewVerifier(req) }
if err != nil { // Test verification
t.Fatalf("%q: %s", test.name, err) v, err := NewVerifier(req)
} if err != nil {
if v.KeyId() != test.pubKeyId { t.Fatalf("%s", err)
t.Fatalf("%q: got %s, want %s", test.name, v.KeyId(), test.pubKeyId) }
} if v.KeyId() != test.pubKeyId {
err = v.Verify(test.pubKey, test.expectedAlgorithm) t.Fatalf("got %s, want %s", v.KeyId(), test.pubKeyId)
if err != nil { }
t.Fatalf("%q: %s", test.name, err) err = v.Verify(test.pubKey, test.expectedAlgorithm)
} if err != nil {
t.Fatalf("%s", err)
}
})
} }
} }
func TestNewResponseVerifier(t *testing.T) { func TestNewResponseVerifier(t *testing.T) {
for _, test := range tests { for _, test := range tests {
if test.expectErrorSigningResponse { t.Run(test.name, func(t *testing.T) {
continue test := test
} if test.expectErrorSigningResponse {
// Prepare return
resp := httptest.NewRecorder() }
resp.HeaderMap.Set("Date", testDate) // Prepare
resp.HeaderMap.Set("Digest", testDigest) resp := httptest.NewRecorder()
s, _, err := NewSigner(test.prefs, test.headers, test.scheme) resp.HeaderMap.Set("Date", testDate)
if err != nil { resp.HeaderMap.Set("Digest", testDigest)
t.Fatalf("%q: %s", test.name, err) s, _, err := NewSigner(test.prefs, test.headers, test.scheme)
} if err != nil {
err = s.SignResponse(test.privKey, test.pubKeyId, resp) t.Fatalf("%s", err)
if err != nil { }
t.Fatalf("%q: %s", test.name, err) err = s.SignResponse(test.privKey, test.pubKeyId, resp)
} if err != nil {
// Test verification t.Fatalf("%s", err)
v, err := NewResponseVerifier(resp.Result()) }
if err != nil { // Test verification
t.Fatalf("%q: %s", test.name, err) v, err := NewResponseVerifier(resp.Result())
} if err != nil {
if v.KeyId() != test.pubKeyId { t.Fatalf("%s", err)
t.Fatalf("%q: got %s, want %s", test.name, v.KeyId(), test.pubKeyId) }
} if v.KeyId() != test.pubKeyId {
err = v.Verify(test.pubKey, test.expectedAlgorithm) t.Fatalf("got %s, want %s", v.KeyId(), test.pubKeyId)
if err != nil { }
t.Fatalf("%q: %s", test.name, err) err = v.Verify(test.pubKey, test.expectedAlgorithm)
} if err != nil {
t.Fatalf("%s", err)
}
})
} }
} }