From 3776c26a72aa8ab61b77f53528dc807990ff54bf Mon Sep 17 00:00:00 2001 From: Stanislav Paskalev Date: Sat, 16 Sep 2017 17:47:46 +0300 Subject: [PATCH] Initial support for SHa-256 and SHA-512/256 --- digest.go | 17 ++++++++++++----- misc.go | 28 ++++++++++++++++++++++++++-- misc_test.go | 4 ++-- 3 files changed, 40 insertions(+), 9 deletions(-) diff --git a/digest.go b/digest.go index 21b0933..e7c8467 100644 --- a/digest.go +++ b/digest.go @@ -20,6 +20,7 @@ type digest_client struct { } type DigestAuth struct { + Algorithm string Realm string Opaque string Secrets SecretProvider @@ -91,8 +92,8 @@ func (a *DigestAuth) RequireAuth(w http.ResponseWriter, r *http.Request) { a.clients[nonce] = &digest_client{nc: 0, last_seen: time.Now().UnixNano()} w.Header().Set(contentType, a.Headers.V().UnauthContentType) w.Header().Set(a.Headers.V().Authenticate, - fmt.Sprintf(`Digest realm="%s", nonce="%s", opaque="%s", algorithm="MD5", qop="auth"`, - a.Realm, nonce, a.Opaque)) + fmt.Sprintf(`Digest realm="%s", nonce="%s", opaque="%s", algorithm="%s", qop="auth"`, + a.Realm, nonce, a.Opaque, a.Algorithm)) w.WriteHeader(a.Headers.V().UnauthCode) w.Write([]byte(a.Headers.V().UnauthResponse)) } @@ -138,7 +139,12 @@ func (da *DigestAuth) CheckAuth(r *http.Request) (username string, authinfo *str if _, ok := auth["algorithm"]; !ok { auth["algorithm"] = "MD5" } - if da.Opaque != auth["opaque"] || auth["algorithm"] != "MD5" || auth["qop"] != "auth" { + if da.Opaque != auth["opaque"] || auth["qop"] != "auth" { + return "", nil + } + + H, ok := algorithms[strings.ToUpper(auth["algorithm"])] + if !ok { return "", nil } @@ -255,14 +261,15 @@ func (a *DigestAuth) NewContext(ctx context.Context, r *http.Request) context.Co nonce := RandomKey() a.clients[nonce] = &digest_client{nc: 0, last_seen: time.Now().UnixNano()} info.ResponseHeaders.Set(a.Headers.V().Authenticate, - fmt.Sprintf(`Digest realm="%s", nonce="%s", opaque="%s", algorithm="MD5", qop="auth"`, - a.Realm, nonce, a.Opaque)) + fmt.Sprintf(`Digest realm="%s", nonce="%s", opaque="%s", algorithm="%s", qop="auth"`, + a.Realm, nonce, a.Opaque, a.Algorithm)) } return context.WithValue(ctx, infoKey, info) } func NewDigestAuthenticator(realm string, secrets SecretProvider) *DigestAuth { da := &DigestAuth{ + Algorithm: "MD5", // NOT RECOMMENDED according to RFC 7616 Opaque: RandomKey(), Realm: realm, Secrets: secrets, diff --git a/misc.go b/misc.go index 4536ce6..179adfb 100644 --- a/misc.go +++ b/misc.go @@ -4,12 +4,22 @@ import ( "bytes" "crypto/md5" "crypto/rand" + "crypto/sha256" + "crypto/sha512" "encoding/base64" "fmt" "net/http" "strings" ) +var ( + algorithms = map[string]func(string) string{ + "MD5": MD5, + "SHA-256": SHA_256, + "SHA-512/256": SHA_512_256, + } +) + // RandomKey returns a random 16-byte base64 alphabet string func RandomKey() string { k := make([]byte, 12) @@ -23,13 +33,27 @@ func RandomKey() string { return base64.StdEncoding.EncodeToString(k) } -// H function for MD5 algorithm (returns a lower-case hex MD5 digest) -func H(data string) string { +// Hashes data using MD5 and returns a lower-case hex digest +func MD5(data string) string { digest := md5.New() digest.Write([]byte(data)) return fmt.Sprintf("%x", digest.Sum(nil)) } +// Hashes data using SHA-256 and returns a lower-case hex digest +func SHA_256(data string) string { + digest := sha256.New() + digest.Write([]byte(data)) + return fmt.Sprintf("%x", digest.Sum(nil)) +} + +// Hashes data using SHA-512/256 and returns a lower-case hex digest +func SHA_512_256(data string) string { + digest := sha512.New512_256() + digest.Write([]byte(data)) + return fmt.Sprintf("%x", digest.Sum(nil)) +} + // ParseList parses a comma-separated list of values as described by // RFC 2068 and returns list elements. // diff --git a/misc_test.go b/misc_test.go index 31a920f..c1b45a0 100644 --- a/misc_test.go +++ b/misc_test.go @@ -5,10 +5,10 @@ import ( "testing" ) -func TestH(t *testing.T) { +func TestMD5(t *testing.T) { const hello = "Hello, world!" const hello_md5 = "6cd3556deb0da54bca060b4c39479839" - h := H(hello) + h := MD5(hello) if h != hello_md5 { t.Fatal("Incorrect digest for test string:", h, "instead of", hello_md5) }