You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
180 lines
3.8 KiB
180 lines
3.8 KiB
package digest_auth_client
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/md5"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"hash"
|
|
"io"
|
|
"net/url"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type authorization struct {
|
|
Algorithm string // unquoted
|
|
Cnonce string // quoted
|
|
Nc int // unquoted
|
|
Nonce string // quoted
|
|
Opaque string // quoted
|
|
Qop string // unquoted
|
|
Realm string // quoted
|
|
Response string // quoted
|
|
Uri string // quoted
|
|
Userhash bool // quoted
|
|
Username string // quoted
|
|
Username_ string // quoted
|
|
}
|
|
|
|
func newAuthorization(dr *DigestRequest) (*authorization, error) {
|
|
|
|
ah := authorization{
|
|
Algorithm: dr.Wa.Algorithm,
|
|
Cnonce: "",
|
|
Nc: 0,
|
|
Nonce: dr.Wa.Nonce,
|
|
Opaque: dr.Wa.Opaque,
|
|
Qop: "",
|
|
Realm: dr.Wa.Realm,
|
|
Response: "",
|
|
Uri: "",
|
|
Userhash: dr.Wa.Userhash,
|
|
Username: "",
|
|
Username_: "", // TODO
|
|
}
|
|
|
|
return ah.refreshAuthorization(dr)
|
|
}
|
|
|
|
func (ah *authorization) refreshAuthorization(dr *DigestRequest) (*authorization, error) {
|
|
|
|
ah.Username = dr.Username
|
|
|
|
if ah.Userhash {
|
|
ah.Username = ah.hash(fmt.Sprintf("%s:%s", ah.Username, ah.Realm))
|
|
}
|
|
|
|
ah.Nc++
|
|
|
|
ah.Cnonce = ah.hash(fmt.Sprintf("%d:%s:my_value", time.Now().UnixNano(), dr.Username))
|
|
|
|
url, err := url.Parse(dr.Uri)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ah.Uri = url.RequestURI()
|
|
ah.Response = ah.computeResponse(dr)
|
|
|
|
return ah, nil
|
|
}
|
|
|
|
func (ah *authorization) computeResponse(dr *DigestRequest) (s string) {
|
|
|
|
kdSecret := ah.hash(ah.computeA1(dr))
|
|
kdData := fmt.Sprintf("%s:%08x:%s:%s:%s", ah.Nonce, ah.Nc, ah.Cnonce, ah.Qop, ah.hash(ah.computeA2(dr)))
|
|
|
|
return ah.hash(fmt.Sprintf("%s:%s", kdSecret, kdData))
|
|
}
|
|
|
|
func (ah *authorization) computeA1(dr *DigestRequest) string {
|
|
|
|
if ah.Algorithm == "" || ah.Algorithm == "MD5" || ah.Algorithm == "SHA-256" {
|
|
return fmt.Sprintf("%s:%s:%s", ah.Username, ah.Realm, dr.Password)
|
|
}
|
|
|
|
if ah.Algorithm == "MD5-sess" || ah.Algorithm == "SHA-256-sess" {
|
|
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)
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
func (ah *authorization) computeA2(dr *DigestRequest) string {
|
|
|
|
if matched, _ := regexp.MatchString("auth-int", dr.Wa.Qop); matched {
|
|
ah.Qop = "auth-int"
|
|
return fmt.Sprintf("%s:%s:%s", dr.Method, ah.Uri, ah.hash(dr.Body))
|
|
}
|
|
|
|
if dr.Wa.Qop == "auth" || dr.Wa.Qop == "" {
|
|
ah.Qop = "auth"
|
|
return fmt.Sprintf("%s:%s", dr.Method, ah.Uri)
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
func (ah *authorization) hash(a string) (s string) {
|
|
|
|
var h hash.Hash
|
|
|
|
if ah.Algorithm == "" || ah.Algorithm == "MD5" || ah.Algorithm == "MD5-sess" {
|
|
h = md5.New()
|
|
} else if ah.Algorithm == "SHA-256" || ah.Algorithm == "SHA-256-sess" {
|
|
h = sha256.New()
|
|
}
|
|
|
|
io.WriteString(h, a)
|
|
s = hex.EncodeToString(h.Sum(nil))
|
|
|
|
return
|
|
}
|
|
|
|
func (ah *authorization) toString() string {
|
|
var buffer bytes.Buffer
|
|
|
|
buffer.WriteString("Digest ")
|
|
|
|
if ah.Algorithm != "" {
|
|
buffer.WriteString(fmt.Sprintf("algorithm=%s, ", ah.Algorithm))
|
|
}
|
|
|
|
if ah.Cnonce != "" {
|
|
buffer.WriteString(fmt.Sprintf("cnonce=\"%s\", ", ah.Cnonce))
|
|
}
|
|
|
|
if ah.Nc != 0 {
|
|
buffer.WriteString(fmt.Sprintf("nc=%08x, ", ah.Nc))
|
|
}
|
|
|
|
if ah.Opaque != "" {
|
|
buffer.WriteString(fmt.Sprintf("opaque=\"%s\", ", ah.Opaque))
|
|
}
|
|
|
|
if ah.Nonce != "" {
|
|
buffer.WriteString(fmt.Sprintf("nonce=\"%s\", ", ah.Nonce))
|
|
}
|
|
|
|
if ah.Qop != "" {
|
|
buffer.WriteString(fmt.Sprintf("qop=%s, ", ah.Qop))
|
|
}
|
|
|
|
if ah.Realm != "" {
|
|
buffer.WriteString(fmt.Sprintf("realm=\"%s\", ", ah.Realm))
|
|
}
|
|
|
|
if ah.Response != "" {
|
|
buffer.WriteString(fmt.Sprintf("response=\"%s\", ", ah.Response))
|
|
}
|
|
|
|
if ah.Uri != "" {
|
|
buffer.WriteString(fmt.Sprintf("uri=\"%s\", ", ah.Uri))
|
|
}
|
|
|
|
if ah.Userhash {
|
|
buffer.WriteString("userhash=true, ")
|
|
}
|
|
|
|
if ah.Username != "" {
|
|
buffer.WriteString(fmt.Sprintf("username=\"%s\", ", ah.Username))
|
|
}
|
|
|
|
s := buffer.String()
|
|
|
|
return strings.TrimSuffix(s, ", ")
|
|
}
|
|
|