From f040e20ac42584c9bc5968ac71b94eb8c01931a3 Mon Sep 17 00:00:00 2001 From: Alessandro Ros Date: Thu, 22 Feb 2024 19:12:17 +0100 Subject: [PATCH] support authenticating with SHA-256 digest (#524) --- client_play_test.go | 12 ++- pkg/auth/auth_test.go | 10 ++- pkg/auth/nonce.go | 17 ++++ pkg/auth/sender.go | 79 +++++++++++-------- pkg/auth/validate.go | 75 +++++++----------- pkg/auth/validate_test.go | 2 +- pkg/auth/www_authenticate.go | 23 ++++++ pkg/headers/authenticate.go | 50 +++++++++--- pkg/headers/authenticate_test.go | 39 ++++++--- pkg/headers/authorization.go | 23 +++++- pkg/headers/authorization_test.go | 27 +++++-- .../cfd8b2b783bff8d8 | 2 + .../d3421c855dd22114 | 2 + 13 files changed, 241 insertions(+), 120 deletions(-) create mode 100644 pkg/auth/nonce.go create mode 100644 pkg/auth/www_authenticate.go create mode 100644 pkg/headers/testdata/fuzz/FuzzAuthenticateUnmarshal/cfd8b2b783bff8d8 create mode 100644 pkg/headers/testdata/fuzz/FuzzAuthorizationUnmarshal/d3421c855dd22114 diff --git a/client_play_test.go b/client_play_test.go index 889d49c6..ca740b79 100644 --- a/client_play_test.go +++ b/client_play_test.go @@ -1690,17 +1690,15 @@ func TestClientPlayRedirect(t *testing.T) { authNonce := "exampleNonce" authOpaque := "exampleOpaque" authStale := "FALSE" - authAlg := "MD5" err = conn.WriteResponse(&base.Response{ Header: base.Header{ "WWW-Authenticate": headers.Authenticate{ - Method: headers.AuthDigest, - Realm: authRealm, - Nonce: authNonce, - Opaque: &authOpaque, - Stale: &authStale, - Algorithm: &authAlg, + Method: headers.AuthDigestMD5, + Realm: authRealm, + Nonce: authNonce, + Opaque: &authOpaque, + Stale: &authStale, }.Marshal(), }, StatusCode: base.StatusUnauthorized, diff --git a/pkg/auth/auth_test.go b/pkg/auth/auth_test.go index 2b0beffd..6f064871 100644 --- a/pkg/auth/auth_test.go +++ b/pkg/auth/auth_test.go @@ -27,11 +27,15 @@ func TestAuth(t *testing.T) { []headers.AuthMethod{headers.AuthBasic}, }, { - "digest", - []headers.AuthMethod{headers.AuthDigest}, + "digest md5", + []headers.AuthMethod{headers.AuthDigestMD5}, }, { - "both", + "digest sha256", + []headers.AuthMethod{headers.AuthDigestSHA256}, + }, + { + "all", nil, }, } { diff --git a/pkg/auth/nonce.go b/pkg/auth/nonce.go new file mode 100644 index 00000000..5d41cb9e --- /dev/null +++ b/pkg/auth/nonce.go @@ -0,0 +1,17 @@ +package auth + +import ( + "crypto/rand" + "encoding/hex" +) + +// GenerateNonce generates a nonce that can be used in Validate(). +func GenerateNonce() (string, error) { + byts := make([]byte, 16) + _, err := rand.Read(byts) + if err != nil { + return "", err + } + + return hex.EncodeToString(byts), nil +} diff --git a/pkg/auth/sender.go b/pkg/auth/sender.go index 7a662b64..0fd14c88 100644 --- a/pkg/auth/sender.go +++ b/pkg/auth/sender.go @@ -2,19 +2,34 @@ package auth import ( "fmt" - "strings" "github.com/bluenviron/gortsplib/v4/pkg/base" "github.com/bluenviron/gortsplib/v4/pkg/headers" ) -func findHeader(v base.HeaderValue, prefix string) string { - for _, vi := range v { - if strings.HasPrefix(vi, prefix) { - return vi +func findAuthenticateHeader(auths []headers.Authenticate, method headers.AuthMethod) *headers.Authenticate { + for _, auth := range auths { + if auth.Method == method { + return &auth } } - return "" + return nil +} + +func pickAuthenticateHeader(auths []headers.Authenticate) (*headers.Authenticate, error) { + if auth := findAuthenticateHeader(auths, headers.AuthDigestSHA256); auth != nil { + return auth, nil + } + + if auth := findAuthenticateHeader(auths, headers.AuthDigestMD5); auth != nil { + return auth, nil + } + + if auth := findAuthenticateHeader(auths, headers.AuthBasic); auth != nil { + return auth, nil + } + + return nil, fmt.Errorf("no authentication methods available") } // Sender allows to send credentials. @@ -27,37 +42,29 @@ type Sender struct { // NewSender allocates a Sender. // It requires a WWW-Authenticate header (provided by the server) // and a set of credentials. -func NewSender(v base.HeaderValue, user string, pass string) (*Sender, error) { - // prefer digest - if v0 := findHeader(v, "Digest"); v0 != "" { +func NewSender(vals base.HeaderValue, user string, pass string) (*Sender, error) { + var auths []headers.Authenticate //nolint:prealloc + + for _, v := range vals { var auth headers.Authenticate - err := auth.Unmarshal(base.HeaderValue{v0}) + err := auth.Unmarshal(base.HeaderValue{v}) if err != nil { - return nil, err + continue // ignore unrecognized headers } - return &Sender{ - user: user, - pass: pass, - authenticateHeader: &auth, - }, nil + auths = append(auths, auth) } - if v0 := findHeader(v, "Basic"); v0 != "" { - var auth headers.Authenticate - err := auth.Unmarshal(base.HeaderValue{v0}) - if err != nil { - return nil, err - } - - return &Sender{ - user: user, - pass: pass, - authenticateHeader: &auth, - }, nil + auth, err := pickAuthenticateHeader(auths) + if err != nil { + return nil, err } - return nil, fmt.Errorf("no authentication methods available") + return &Sender{ + user: user, + pass: pass, + authenticateHeader: auth, + }, nil } // AddAuthorization adds the Authorization header to a Request. @@ -68,16 +75,26 @@ func (se *Sender) AddAuthorization(req *base.Request) { Method: se.authenticateHeader.Method, } - if se.authenticateHeader.Method == headers.AuthBasic { + switch se.authenticateHeader.Method { + case headers.AuthBasic: h.BasicUser = se.user h.BasicPass = se.pass - } else { // digest + + case headers.AuthDigestMD5: h.Username = se.user h.Realm = se.authenticateHeader.Realm h.Nonce = se.authenticateHeader.Nonce h.URI = urStr h.Response = md5Hex(md5Hex(se.user+":"+se.authenticateHeader.Realm+":"+se.pass) + ":" + se.authenticateHeader.Nonce + ":" + md5Hex(string(req.Method)+":"+urStr)) + + default: // digest SHA-256 + h.Username = se.user + h.Realm = se.authenticateHeader.Realm + h.Nonce = se.authenticateHeader.Nonce + h.URI = urStr + h.Response = sha256Hex(sha256Hex(se.user+":"+se.authenticateHeader.Realm+":"+se.pass) + ":" + + se.authenticateHeader.Nonce + ":" + sha256Hex(string(req.Method)+":"+urStr)) } if req.Header == nil { diff --git a/pkg/auth/validate.go b/pkg/auth/validate.go index 3e20cd27..50bac2f4 100644 --- a/pkg/auth/validate.go +++ b/pkg/auth/validate.go @@ -2,7 +2,7 @@ package auth import ( "crypto/md5" - "crypto/rand" + "crypto/sha256" "encoding/hex" "fmt" @@ -16,41 +16,10 @@ func md5Hex(in string) string { return hex.EncodeToString(h.Sum(nil)) } -// GenerateNonce generates a nonce that can be used in Validate(). -func GenerateNonce() (string, error) { - byts := make([]byte, 16) - _, err := rand.Read(byts) - if err != nil { - return "", err - } - - return hex.EncodeToString(byts), nil -} - -// GenerateWWWAuthenticate generates a WWW-Authenticate header. -func GenerateWWWAuthenticate(methods []headers.AuthMethod, realm string, nonce string) base.HeaderValue { - if methods == nil { - methods = []headers.AuthMethod{headers.AuthBasic, headers.AuthDigest} - } - - var ret base.HeaderValue - for _, m := range methods { - switch m { - case headers.AuthBasic: - ret = append(ret, (&headers.Authenticate{ - Method: headers.AuthBasic, - Realm: realm, - }).Marshal()...) - - case headers.AuthDigest: - ret = append(ret, headers.Authenticate{ - Method: headers.AuthDigest, - Realm: realm, - Nonce: nonce, - }.Marshal()...) - } - } - return ret +func sha256Hex(in string) string { + h := sha256.New() + h.Write([]byte(in)) + return hex.EncodeToString(h.Sum(nil)) } func contains(list []headers.AuthMethod, item headers.AuthMethod) bool { @@ -73,7 +42,7 @@ func Validate( nonce string, ) error { if methods == nil { - methods = []headers.AuthMethod{headers.AuthBasic, headers.AuthDigest} + methods = []headers.AuthMethod{headers.AuthDigestSHA256, headers.AuthDigestMD5, headers.AuthBasic} } var auth headers.Authorization @@ -83,15 +52,8 @@ func Validate( } switch { - case auth.Method == headers.AuthBasic && contains(methods, headers.AuthBasic): - if auth.BasicUser != user { - return fmt.Errorf("authentication failed") - } - - if auth.BasicPass != pass { - return fmt.Errorf("authentication failed") - } - case auth.Method == headers.AuthDigest && contains(methods, headers.AuthDigest): + case (auth.Method == headers.AuthDigestSHA256 && contains(methods, headers.AuthDigestSHA256)) || + (auth.Method == headers.AuthDigestMD5 && contains(methods, headers.AuthDigestMD5)): if auth.Nonce != nonce { return fmt.Errorf("wrong nonce") } @@ -119,12 +81,29 @@ func Validate( } } - response := md5Hex(md5Hex(user+":"+realm+":"+pass) + - ":" + nonce + ":" + md5Hex(string(req.Method)+":"+ur.String())) + var response string + + if auth.Method == headers.AuthDigestSHA256 { + response = sha256Hex(sha256Hex(user+":"+realm+":"+pass) + + ":" + nonce + ":" + sha256Hex(string(req.Method)+":"+ur.String())) + } else { + response = md5Hex(md5Hex(user+":"+realm+":"+pass) + + ":" + nonce + ":" + md5Hex(string(req.Method)+":"+ur.String())) + } if auth.Response != response { return fmt.Errorf("authentication failed") } + + case auth.Method == headers.AuthBasic && contains(methods, headers.AuthBasic): + if auth.BasicUser != user { + return fmt.Errorf("authentication failed") + } + + if auth.BasicPass != pass { + return fmt.Errorf("authentication failed") + } + default: return fmt.Errorf("no supported authentication methods found") } diff --git a/pkg/auth/validate_test.go b/pkg/auth/validate_test.go index 956fa5fb..f062e62b 100644 --- a/pkg/auth/validate_test.go +++ b/pkg/auth/validate_test.go @@ -49,7 +49,7 @@ func TestValidateAdditionalErrors(t *testing.T) { "myuser", "mypass", nil, - []headers.AuthMethod{headers.AuthDigest}, + []headers.AuthMethod{headers.AuthDigestMD5}, "IPCAM", "abcde", ) diff --git a/pkg/auth/www_authenticate.go b/pkg/auth/www_authenticate.go new file mode 100644 index 00000000..5ee0a904 --- /dev/null +++ b/pkg/auth/www_authenticate.go @@ -0,0 +1,23 @@ +package auth + +import ( + "github.com/bluenviron/gortsplib/v4/pkg/base" + "github.com/bluenviron/gortsplib/v4/pkg/headers" +) + +// GenerateWWWAuthenticate generates a WWW-Authenticate header. +func GenerateWWWAuthenticate(methods []headers.AuthMethod, realm string, nonce string) base.HeaderValue { + if methods == nil { + methods = []headers.AuthMethod{headers.AuthDigestSHA256, headers.AuthDigestMD5, headers.AuthBasic} + } + + var ret base.HeaderValue + for _, m := range methods { + ret = append(ret, headers.Authenticate{ + Method: m, + Realm: realm, + Nonce: nonce, // used only by digest + }.Marshal()...) + } + return ret +} diff --git a/pkg/headers/authenticate.go b/pkg/headers/authenticate.go index d4d37763..0b85c8cf 100644 --- a/pkg/headers/authenticate.go +++ b/pkg/headers/authenticate.go @@ -15,10 +15,33 @@ const ( // AuthBasic is the Basic authentication method AuthBasic AuthMethod = iota - // AuthDigest is the Digest authentication method - AuthDigest + // AuthDigestMD5 is the Digest authentication method with the MD5 hash + AuthDigestMD5 + + // AuthDigestSHA256 is the Digest authentication method with the SHA-256 hash + AuthDigestSHA256 ) +const ( + // AuthDigest is an alias for AuthDigestMD5 + // + // Deprecated: replaced by AuthDigestMD5 + AuthDigest = AuthDigestMD5 +) + +func algorithmToMethod(v *string) (AuthMethod, error) { + switch { + case v == nil, strings.ToLower(*v) == "md5": + return AuthDigestMD5, nil + + case strings.ToLower(*v) == "sha-256": + return AuthDigestSHA256, nil + + default: + return 0, fmt.Errorf("unrecognized algorithm: %v", *v) + } +} + // Authenticate is a WWW-Authenticate header. type Authenticate struct { // authentication method @@ -39,9 +62,6 @@ type Authenticate struct { // stale Stale *string - - // algorithm - Algorithm *string } // Unmarshal decodes a WWW-Authenticate header. @@ -62,18 +82,20 @@ func (h *Authenticate) Unmarshal(v base.HeaderValue) error { } method, v0 := v0[:i], v0[i+1:] + isDigest := false + switch method { case "Basic": h.Method = AuthBasic case "Digest": - h.Method = AuthDigest + isDigest = true default: return fmt.Errorf("invalid method (%s)", method) } - if h.Method == AuthBasic { + if !isDigest { kvs, err := keyValParse(v0, ',') if err != nil { return err @@ -101,6 +123,7 @@ func (h *Authenticate) Unmarshal(v base.HeaderValue) error { realmReceived := false nonceReceived := false + var algorithm *string for k, rv := range kvs { v := rv @@ -121,13 +144,18 @@ func (h *Authenticate) Unmarshal(v base.HeaderValue) error { h.Stale = &v case "algorithm": - h.Algorithm = &v + algorithm = &v } } if !realmReceived || !nonceReceived { return fmt.Errorf("one or more digest fields are missing") } + + h.Method, err = algorithmToMethod(algorithm) + if err != nil { + return err + } } return nil @@ -150,8 +178,10 @@ func (h Authenticate) Marshal() base.HeaderValue { ret += ", stale=\"" + *h.Stale + "\"" } - if h.Algorithm != nil { - ret += ", algorithm=\"" + *h.Algorithm + "\"" + if h.Method == AuthDigestMD5 { + ret += ", algorithm=\"MD5\"" + } else { + ret += ", algorithm=\"SHA-256\"" } return base.HeaderValue{ret} diff --git a/pkg/headers/authenticate_test.go b/pkg/headers/authenticate_test.go index 6d82eab2..6aa1c15b 100644 --- a/pkg/headers/authenticate_test.go +++ b/pkg/headers/authenticate_test.go @@ -30,9 +30,10 @@ var casesAuthenticate = []struct { { "digest 1", base.HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale="FALSE"`}, - base.HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale="FALSE"`}, + base.HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", ` + + `stale="FALSE", algorithm="MD5"`}, Authenticate{ - Method: AuthDigest, + Method: AuthDigestMD5, Realm: "4419b63f5e51", Nonce: "8b84a3b789283a8bea8da7fa7d41f08b", Stale: stringPtr("FALSE"), @@ -41,9 +42,10 @@ var casesAuthenticate = []struct { { "digest 2", base.HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale=FALSE`}, - base.HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale="FALSE"`}, + base.HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", ` + + `stale="FALSE", algorithm="MD5"`}, Authenticate{ - Method: AuthDigest, + Method: AuthDigestMD5, Realm: "4419b63f5e51", Nonce: "8b84a3b789283a8bea8da7fa7d41f08b", Stale: stringPtr("FALSE"), @@ -52,9 +54,10 @@ var casesAuthenticate = []struct { { "digest 3", base.HeaderValue{`Digest realm="4419b63f5e51",nonce="133767111917411116111311118211673010032", stale="FALSE"`}, - base.HeaderValue{`Digest realm="4419b63f5e51", nonce="133767111917411116111311118211673010032", stale="FALSE"`}, + base.HeaderValue{`Digest realm="4419b63f5e51", nonce="133767111917411116111311118211673010032", ` + + `stale="FALSE", algorithm="MD5"`}, Authenticate{ - Method: AuthDigest, + Method: AuthDigestMD5, Realm: "4419b63f5e51", Nonce: "133767111917411116111311118211673010032", Stale: stringPtr("FALSE"), @@ -67,12 +70,24 @@ var casesAuthenticate = []struct { base.HeaderValue{`Digest realm="Please log in with a valid username", ` + `nonce="752a62306daf32b401a41004555c7663", opaque="", stale="FALSE", algorithm="MD5"`}, Authenticate{ - Method: AuthDigest, - Realm: "Please log in with a valid username", - Nonce: "752a62306daf32b401a41004555c7663", - Opaque: stringPtr(""), - Stale: stringPtr("FALSE"), - Algorithm: stringPtr("MD5"), + Method: AuthDigestMD5, + Realm: "Please log in with a valid username", + Nonce: "752a62306daf32b401a41004555c7663", + Opaque: stringPtr(""), + Stale: stringPtr("FALSE"), + }, + }, + { + "digest sha256", + base.HeaderValue{`Digest realm="IP Camera(AB705)", ` + + `nonce="fcc86deace979a488b2bfb89f4d0812c", algorithm="SHA-256", stale="FALSE"`}, + base.HeaderValue{`Digest realm="IP Camera(AB705)", ` + + `nonce="fcc86deace979a488b2bfb89f4d0812c", stale="FALSE", algorithm="SHA-256"`}, + Authenticate{ + Method: AuthDigestSHA256, + Realm: "IP Camera(AB705)", + Nonce: "fcc86deace979a488b2bfb89f4d0812c", + Stale: stringPtr("FALSE"), }, }, } diff --git a/pkg/headers/authorization.go b/pkg/headers/authorization.go index 8fb18ccf..b65761bc 100644 --- a/pkg/headers/authorization.go +++ b/pkg/headers/authorization.go @@ -42,7 +42,7 @@ type Authorization struct { // response Response string - // response + // opaque Opaque *string } @@ -64,18 +64,20 @@ func (h *Authorization) Unmarshal(v base.HeaderValue) error { } method, v0 := v0[:i], v0[i+1:] + isDigest := false + switch method { case "Basic": h.Method = AuthBasic case "Digest": - h.Method = AuthDigest + isDigest = true default: return fmt.Errorf("invalid method (%s)", method) } - if h.Method == AuthBasic { + if !isDigest { tmp, err := base64.StdEncoding.DecodeString(v0) if err != nil { return fmt.Errorf("invalid value") @@ -98,6 +100,7 @@ func (h *Authorization) Unmarshal(v base.HeaderValue) error { nonceReceived := false uriReceived := false responseReceived := false + var algorithm *string for k, rv := range kvs { v := rv @@ -125,12 +128,20 @@ func (h *Authorization) Unmarshal(v base.HeaderValue) error { case "opaque": h.Opaque = &v + + case "algorithm": + algorithm = &v } } if !realmReceived || !usernameReceived || !nonceReceived || !uriReceived || !responseReceived { return fmt.Errorf("one or more digest fields are missing") } + + h.Method, err = algorithmToMethod(algorithm) + if err != nil { + return err + } } return nil @@ -151,5 +162,11 @@ func (h Authorization) Marshal() base.HeaderValue { ret += ", opaque=\"" + *h.Opaque + "\"" } + if h.Method == AuthDigestMD5 { + ret += ", algorithm=\"MD5\"" + } else { + ret += ", algorithm=\"SHA-256\"" + } + return base.HeaderValue{ret} } diff --git a/pkg/headers/authorization_test.go b/pkg/headers/authorization_test.go index e86ea8e0..f9a1a624 100644 --- a/pkg/headers/authorization_test.go +++ b/pkg/headers/authorization_test.go @@ -30,10 +30,10 @@ var casesAuthorization = []struct { `nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", ` + `uri="/dir/index.html", response="e966c932a9242554e42c8ee200cec7f6", opaque="5ccc069c403ebaf9f0171e9517f40e41"`}, base.HeaderValue{`Digest username="Mufasa", realm="testrealm@host.com", ` + - `nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", ` + - `uri="/dir/index.html", response="e966c932a9242554e42c8ee200cec7f6", opaque="5ccc069c403ebaf9f0171e9517f40e41"`}, + `nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", uri="/dir/index.html", ` + + `response="e966c932a9242554e42c8ee200cec7f6", opaque="5ccc069c403ebaf9f0171e9517f40e41", algorithm="MD5"`}, Authorization{ - Method: AuthDigest, + Method: AuthDigestMD5, Username: "Mufasa", Realm: "testrealm@host.com", Nonce: "dcd98b7102dd2f0e8b11d0f600bfb0c093", @@ -49,9 +49,9 @@ var casesAuthorization = []struct { `response="c072ae90eb4a27f4cdcb90d62266b2a1"`}, base.HeaderValue{`Digest username="", realm="IPCAM", ` + `nonce="5d17cd12b9fa8a85ac5ceef0926ea5a6", uri="rtsp://localhost:8554/mystream", ` + - `response="c072ae90eb4a27f4cdcb90d62266b2a1"`}, + `response="c072ae90eb4a27f4cdcb90d62266b2a1", algorithm="MD5"`}, Authorization{ - Method: AuthDigest, + Method: AuthDigestMD5, Username: "", Realm: "IPCAM", Nonce: "5d17cd12b9fa8a85ac5ceef0926ea5a6", @@ -59,6 +59,23 @@ var casesAuthorization = []struct { Response: "c072ae90eb4a27f4cdcb90d62266b2a1", }, }, + { + "digest sha256", + base.HeaderValue{`Digest username="admin", realm="IP Camera(AB705)", ` + + `nonce="1ad195c2b2ca5a03784e53f88e16f579", uri="rtsp://192.168.80.76/", ` + + `response="9e2324f104f3ce507d17e44a78fc1293001fe84805bde65d2aaa9be97a5a8913", algorithm="SHA-256"`}, + base.HeaderValue{`Digest username="admin", realm="IP Camera(AB705)", ` + + `nonce="1ad195c2b2ca5a03784e53f88e16f579", uri="rtsp://192.168.80.76/", ` + + `response="9e2324f104f3ce507d17e44a78fc1293001fe84805bde65d2aaa9be97a5a8913", algorithm="SHA-256"`}, + Authorization{ + Method: AuthDigestSHA256, + Username: "admin", + Realm: "IP Camera(AB705)", + Nonce: "1ad195c2b2ca5a03784e53f88e16f579", + URI: "rtsp://192.168.80.76/", + Response: "9e2324f104f3ce507d17e44a78fc1293001fe84805bde65d2aaa9be97a5a8913", + }, + }, } func TestAuthorizationUnmarshal(t *testing.T) { diff --git a/pkg/headers/testdata/fuzz/FuzzAuthenticateUnmarshal/cfd8b2b783bff8d8 b/pkg/headers/testdata/fuzz/FuzzAuthenticateUnmarshal/cfd8b2b783bff8d8 new file mode 100644 index 00000000..4d990ba4 --- /dev/null +++ b/pkg/headers/testdata/fuzz/FuzzAuthenticateUnmarshal/cfd8b2b783bff8d8 @@ -0,0 +1,2 @@ +go test fuzz v1 +string("Digest realm,nonce=\"\"algorithm") diff --git a/pkg/headers/testdata/fuzz/FuzzAuthorizationUnmarshal/d3421c855dd22114 b/pkg/headers/testdata/fuzz/FuzzAuthorizationUnmarshal/d3421c855dd22114 new file mode 100644 index 00000000..6f788a96 --- /dev/null +++ b/pkg/headers/testdata/fuzz/FuzzAuthorizationUnmarshal/d3421c855dd22114 @@ -0,0 +1,2 @@ +go test fuzz v1 +string("Digest username,realm,nonce,uri,response,algorithm")