mirror of
https://github.com/aler9/gortsplib
synced 2025-09-27 03:25:52 +08:00
support authenticating with SHA-256 digest (#524)
This commit is contained in:
@@ -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,
|
||||
|
@@ -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,
|
||||
},
|
||||
} {
|
||||
|
17
pkg/auth/nonce.go
Normal file
17
pkg/auth/nonce.go
Normal file
@@ -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
|
||||
}
|
@@ -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 {
|
||||
|
@@ -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")
|
||||
}
|
||||
|
@@ -49,7 +49,7 @@ func TestValidateAdditionalErrors(t *testing.T) {
|
||||
"myuser",
|
||||
"mypass",
|
||||
nil,
|
||||
[]headers.AuthMethod{headers.AuthDigest},
|
||||
[]headers.AuthMethod{headers.AuthDigestMD5},
|
||||
"IPCAM",
|
||||
"abcde",
|
||||
)
|
||||
|
23
pkg/auth/www_authenticate.go
Normal file
23
pkg/auth/www_authenticate.go
Normal file
@@ -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
|
||||
}
|
@@ -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}
|
||||
|
@@ -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"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@@ -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}
|
||||
}
|
||||
|
@@ -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) {
|
||||
|
2
pkg/headers/testdata/fuzz/FuzzAuthenticateUnmarshal/cfd8b2b783bff8d8
vendored
Normal file
2
pkg/headers/testdata/fuzz/FuzzAuthenticateUnmarshal/cfd8b2b783bff8d8
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
string("Digest realm,nonce=\"\"algorithm")
|
2
pkg/headers/testdata/fuzz/FuzzAuthorizationUnmarshal/d3421c855dd22114
vendored
Normal file
2
pkg/headers/testdata/fuzz/FuzzAuthorizationUnmarshal/d3421c855dd22114
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
string("Digest username,realm,nonce,uri,response,algorithm")
|
Reference in New Issue
Block a user