From 89e5f25b78fc85037abe6dd256b9e08e2dff41e3 Mon Sep 17 00:00:00 2001 From: Dmitriy Panov Date: Sun, 23 Jun 2019 11:25:55 +0300 Subject: [PATCH 1/3] add tests --- authorization_test.go | 174 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 authorization_test.go diff --git a/authorization_test.go b/authorization_test.go new file mode 100644 index 0000000..2091334 --- /dev/null +++ b/authorization_test.go @@ -0,0 +1,174 @@ +package digest_auth_client + +import "testing" + +func TestHash(t *testing.T) { + testCases := []struct { + name string + algorithm string + expRes string + }{ + { + name: "empty algorithm", + algorithm: "", + expRes: "1a79a4d60de6718e8e5b326e338ae533", + }, + { + name: "MD5 algorithm", + algorithm: "MD5", + expRes: "1a79a4d60de6718e8e5b326e338ae533", + }, + { + name: "MD5-sess algorithm", + algorithm: "MD5", + expRes: "1a79a4d60de6718e8e5b326e338ae533", + }, + { + name: "SHA256 algorithm", + algorithm: "SHA-256", + expRes: "50d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c", + }, + { + name: "SHA256-sess algorithm", + algorithm: "SHA-256", + expRes: "50d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c", + }, + //{ + // name: "md5 algorithm will panic", + // algorithm: "md5", + // expRes: "1a79a4d60de6718e8e5b326e338ae533", + //}, + //{ + // name: "unknown algorithm will panic", + // algorithm: "unknown", + // expRes: "", + //}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + ah := &authorization{Algorithm: tc.algorithm} + res := ah.hash("example") + if res != tc.expRes { + t.Errorf("got: %q, want: %q", res, tc.expRes) + } + }) + } +} + +func TestComputeA1(t *testing.T) { + testCases := []struct { + name string + algorithm string + expRes string + }{ + { + name: "empty algorithm", + algorithm: "", + expRes: "username:realm:secret", + }, + { + name: "MD5 algorithm", + algorithm: "MD5", + expRes: "username:realm:secret", + }, + { + name: "MD5-sess algorithm", + algorithm: "MD5", + expRes: "username:realm:secret", + }, + { + name: "SHA256 algorithm", + algorithm: "SHA-256", + expRes: "username:realm:secret", + }, + { + name: "SHA256-sess algorithm", + algorithm: "SHA-256", + expRes: "username:realm:secret", + }, + { + name: "md5 algorithm", + algorithm: "md5", + expRes: "", + }, + { + name: "unknown algorithm", + algorithm: "unknown", + expRes: "", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + dr := &DigestRequest{Password: "secret"} + ah := &authorization{ + Algorithm: tc.algorithm, + Nonce: "nonce", + Cnonce: "cnonce", + Username: "username", + Realm: "realm", + } + res := ah.computeA1(dr) + if res != tc.expRes { + t.Errorf("got: %q, want: %q", res, tc.expRes) + } + }) + } +} + +func TestComputeA2(t *testing.T) { + testCases := []struct { + name string + qop string + expRes string + expAuthQop string + }{ + { + name: "empty qop", + qop: "", + expRes: "method:uri", + expAuthQop: "auth", + }, + { + name: "qop is auth", + qop: "auth", + expRes: "method:uri", + expAuthQop: "auth", + }, + { + name: "qop is auth-int", + qop: "qop is auth-int", + expRes: "method:uri:841a2d689ad86bd1611447453c22c6fc", + expAuthQop: "auth-int", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + dr := &DigestRequest{ + Method: "method", + Body: "body", + Wa: &wwwAuthenticate{ + Qop: tc.qop, + }, + } + ah := &authorization{ + Algorithm: "MD5", + Nonce: "nonce", + Cnonce: "cnonce", + Username: "username", + Realm: "realm", + URI: "uri", + Qop: tc.qop, + } + res := ah.computeA2(dr) + if res != tc.expRes { + t.Errorf("wrong result, got: %q, want: %q", res, tc.expRes) + } + if ah.Qop != tc.expAuthQop { + t.Errorf("wrong qop, got: %q, want: %q", ah.Qop, tc.expAuthQop) + } + }) + } +} From 12e4990b3e13b8390c0595b3b5128ee88d9c6801 Mon Sep 17 00:00:00 2001 From: Dmitriy Panov Date: Sun, 23 Jun 2019 11:39:40 +0300 Subject: [PATCH 2/3] case insensitive algorithm name --- authorization.go | 28 +++++++++++++++++++--------- authorization_test.go | 22 +++++++++++----------- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/authorization.go b/authorization.go index 9f8d8bc..e91b0a9 100644 --- a/authorization.go +++ b/authorization.go @@ -49,6 +49,13 @@ func newAuthorization(dr *DigestRequest) (*authorization, error) { return ah.refreshAuthorization(dr) } +const ( + algorithmMD5 = "MD5" + algorithmMD5Sess = "MD5-SESS" + algorithmSHA256 = "SHA-256" + algorithmSHA256Sess = "SHA-256-SESS" +) + func (ah *authorization) refreshAuthorization(dr *DigestRequest) (*authorization, error) { ah.Username = dr.Username @@ -82,11 +89,13 @@ func (ah *authorization) computeResponse(dr *DigestRequest) (s string) { func (ah *authorization) computeA1(dr *DigestRequest) string { - if ah.Algorithm == "" || ah.Algorithm == "MD5" || ah.Algorithm == "SHA-256" { + algorithm := strings.ToUpper(ah.Algorithm) + + if algorithm == "" || algorithm == algorithmMD5 || algorithm == algorithmSHA256 { return fmt.Sprintf("%s:%s:%s", ah.Username, ah.Realm, dr.Password) } - if ah.Algorithm == "MD5-sess" || ah.Algorithm == "SHA-256-sess" { + if algorithm == algorithmMD5Sess || algorithm == algorithmSHA256Sess { upHash := ah.hash(fmt.Sprintf("%s:%s:%s", ah.Username, ah.Realm, dr.Password)) return fmt.Sprintf("%s:%s:%s", upHash, ah.Nonce, ah.Cnonce) } @@ -109,20 +118,21 @@ func (ah *authorization) computeA2(dr *DigestRequest) string { return "" } -func (ah *authorization) hash(a string) (s string) { - +func (ah *authorization) hash(a string) string { var h hash.Hash + algorithm := strings.ToUpper(ah.Algorithm) - if ah.Algorithm == "" || ah.Algorithm == "MD5" || ah.Algorithm == "MD5-sess" { + if algorithm == "" || algorithm == algorithmMD5 || algorithm == algorithmMD5Sess { h = md5.New() - } else if ah.Algorithm == "SHA-256" || ah.Algorithm == "SHA-256-sess" { + } else if algorithm == algorithmSHA256 || algorithm == algorithmSHA256Sess { h = sha256.New() + } else { + // unknown algorithm + return "" } io.WriteString(h, a) - s = hex.EncodeToString(h.Sum(nil)) - - return + return hex.EncodeToString(h.Sum(nil)) } func (ah *authorization) toString() string { diff --git a/authorization_test.go b/authorization_test.go index 2091334..76cb721 100644 --- a/authorization_test.go +++ b/authorization_test.go @@ -33,16 +33,16 @@ func TestHash(t *testing.T) { algorithm: "SHA-256", expRes: "50d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c", }, - //{ - // name: "md5 algorithm will panic", - // algorithm: "md5", - // expRes: "1a79a4d60de6718e8e5b326e338ae533", - //}, - //{ - // name: "unknown algorithm will panic", - // algorithm: "unknown", - // expRes: "", - //}, + { + name: "md5 algorithm", + algorithm: "md5", + expRes: "1a79a4d60de6718e8e5b326e338ae533", + }, + { + name: "unknown algorithm", + algorithm: "unknown", + expRes: "", + }, } for _, tc := range testCases { @@ -90,7 +90,7 @@ func TestComputeA1(t *testing.T) { { name: "md5 algorithm", algorithm: "md5", - expRes: "", + expRes: "username:realm:secret", }, { name: "unknown algorithm", From 9d6d3110b8a8d7450857bebd17b19410ce3ccfe6 Mon Sep 17 00:00:00 2001 From: Dmitriy Panov Date: Sun, 23 Jun 2019 11:42:46 +0300 Subject: [PATCH 3/3] small optimization: using strings.Contains instead regexp.MatchString --- authorization.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/authorization.go b/authorization.go index e91b0a9..741fd65 100644 --- a/authorization.go +++ b/authorization.go @@ -9,7 +9,6 @@ import ( "hash" "io" "net/url" - "regexp" "strings" "time" ) @@ -105,7 +104,7 @@ func (ah *authorization) computeA1(dr *DigestRequest) string { func (ah *authorization) computeA2(dr *DigestRequest) string { - if matched, _ := regexp.MatchString("auth-int", dr.Wa.Qop); matched { + if strings.Contains(dr.Wa.Qop, "auth-int") { ah.Qop = "auth-int" return fmt.Sprintf("%s:%s:%s", dr.Method, ah.URI, ah.hash(dr.Body)) }