|
|
@ -5,6 +5,8 @@ import ( |
|
|
|
"bytes" |
|
|
|
"bytes" |
|
|
|
"crypto/rand" |
|
|
|
"crypto/rand" |
|
|
|
"crypto/sha1" |
|
|
|
"crypto/sha1" |
|
|
|
|
|
|
|
"crypto/sha256" |
|
|
|
|
|
|
|
"crypto/sha512" |
|
|
|
"encoding/base64" |
|
|
|
"encoding/base64" |
|
|
|
"errors" |
|
|
|
"errors" |
|
|
|
"fmt" |
|
|
|
"fmt" |
|
|
@ -30,6 +32,10 @@ const ( |
|
|
|
HashSHA HashAlgorithm = "sha" |
|
|
|
HashSHA HashAlgorithm = "sha" |
|
|
|
// HashSSHA Salted SHA
|
|
|
|
// HashSSHA Salted SHA
|
|
|
|
HashSSHA HashAlgorithm = "ssha" |
|
|
|
HashSSHA HashAlgorithm = "ssha" |
|
|
|
|
|
|
|
// HashSHA256 256 variant of SHA
|
|
|
|
|
|
|
|
HashSHA256 HashAlgorithm = "sha256" |
|
|
|
|
|
|
|
// HashSHA512 512 variant of SHA
|
|
|
|
|
|
|
|
HashSHA512 HashAlgorithm = "sha512" |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
// ParseHtpasswdFile parses a .htpasswd file
|
|
|
|
// ParseHtpasswdFile parses a .htpasswd file
|
|
|
@ -174,6 +180,10 @@ func verifyPass(pass, hash string, alg HashAlgorithm) error { |
|
|
|
return verifySHA(pass, hash) |
|
|
|
return verifySHA(pass, hash) |
|
|
|
case HashSSHA: |
|
|
|
case HashSSHA: |
|
|
|
return verifySSHA(pass, hash) |
|
|
|
return verifySSHA(pass, hash) |
|
|
|
|
|
|
|
case HashSHA256: |
|
|
|
|
|
|
|
return verifySHA256(pass, hash) |
|
|
|
|
|
|
|
case HashSHA512: |
|
|
|
|
|
|
|
return verifySHA512(pass, hash) |
|
|
|
} |
|
|
|
} |
|
|
|
return fmt.Errorf("unsupported hash algorithm %v", alg) |
|
|
|
return fmt.Errorf("unsupported hash algorithm %v", alg) |
|
|
|
} |
|
|
|
} |
|
|
@ -235,6 +245,53 @@ func verifySSHA(password, hashedPassword string) error { |
|
|
|
|
|
|
|
|
|
|
|
return nil |
|
|
|
return nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func verifySHA256(password, hashedPassword string) error { |
|
|
|
|
|
|
|
eppS := hashedPassword[3:] |
|
|
|
|
|
|
|
hash, err := base64.StdEncoding.DecodeString(eppS) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return fmt.Errorf("cannot base64 decode") |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sha := sha256.New() |
|
|
|
|
|
|
|
_, err = sha.Write([]byte(password)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sum := sha.Sum(nil) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if !bytes.Equal(sum, hash) { |
|
|
|
|
|
|
|
return fmt.Errorf("wrong password") |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func verifySHA512(password, hashedPassword string) error { |
|
|
|
|
|
|
|
eppS := hashedPassword[3:] |
|
|
|
|
|
|
|
hash, err := base64.StdEncoding.DecodeString(eppS) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return fmt.Errorf("cannot base64 decode") |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sha := sha512.New() |
|
|
|
|
|
|
|
_, err = sha.Write([]byte(password)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sum := sha.Sum(nil) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if !bytes.Equal(sum, hash) { |
|
|
|
|
|
|
|
return fmt.Errorf("wrong password") |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (pp Passwds) toByte() []byte { |
|
|
|
func (pp Passwds) toByte() []byte { |
|
|
|
pass := []byte{} |
|
|
|
pass := []byte{} |
|
|
|
for name, hash := range pp { |
|
|
|
for name, hash := range pp { |
|
|
@ -254,6 +311,10 @@ func identifyHash(h string) (HashAlgorithm, error) { |
|
|
|
return HashSHA, nil |
|
|
|
return HashSHA, nil |
|
|
|
case strings.HasPrefix(h, "{SSHA}"): |
|
|
|
case strings.HasPrefix(h, "{SSHA}"): |
|
|
|
return HashSSHA, nil |
|
|
|
return HashSSHA, nil |
|
|
|
|
|
|
|
case strings.HasPrefix(h, "$5$"): |
|
|
|
|
|
|
|
return HashSHA256, nil |
|
|
|
|
|
|
|
case strings.HasPrefix(h, "$6$"): |
|
|
|
|
|
|
|
return HashSHA512, nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return "", fmt.Errorf("unsupported hash algorithm") |
|
|
|
return "", fmt.Errorf("unsupported hash algorithm") |
|
|
@ -274,6 +335,12 @@ func createHash(passwd string, algo HashAlgorithm) (string, error) { |
|
|
|
case HashSSHA: |
|
|
|
case HashSSHA: |
|
|
|
prefix = "{SSHA}" |
|
|
|
prefix = "{SSHA}" |
|
|
|
hash, err = hashSSha(passwd) |
|
|
|
hash, err = hashSSha(passwd) |
|
|
|
|
|
|
|
case HashSHA256: |
|
|
|
|
|
|
|
prefix = "$5$" |
|
|
|
|
|
|
|
hash, err = hashSha256(passwd) |
|
|
|
|
|
|
|
case HashSHA512: |
|
|
|
|
|
|
|
prefix = "$6$" |
|
|
|
|
|
|
|
hash, err = hashSha512(passwd) |
|
|
|
} |
|
|
|
} |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return "", err |
|
|
|
return "", err |
|
|
@ -303,6 +370,27 @@ func hashSha(passwd string) (string, error) { |
|
|
|
passwordSum := []byte(s.Sum(nil)) |
|
|
|
passwordSum := []byte(s.Sum(nil)) |
|
|
|
return base64.StdEncoding.EncodeToString(passwordSum), nil |
|
|
|
return base64.StdEncoding.EncodeToString(passwordSum), nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func hashSha256(passwd string) (string, error) { |
|
|
|
|
|
|
|
s := sha256.New() |
|
|
|
|
|
|
|
_, err := s.Write([]byte(passwd)) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return "", err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
passwordSum := []byte(s.Sum(nil)) |
|
|
|
|
|
|
|
return base64.StdEncoding.EncodeToString(passwordSum), nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func hashSha512(passwd string) (string, error) { |
|
|
|
|
|
|
|
s := sha512.New() |
|
|
|
|
|
|
|
_, err := s.Write([]byte(passwd)) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return "", err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
passwordSum := []byte(s.Sum(nil)) |
|
|
|
|
|
|
|
return base64.StdEncoding.EncodeToString(passwordSum), nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func hashSSha(passwd string) (string, error) { |
|
|
|
func hashSSha(passwd string) (string, error) { |
|
|
|
s := sha1.New() |
|
|
|
s := sha1.New() |
|
|
|
_, err := s.Write([]byte(passwd)) |
|
|
|
_, err := s.Write([]byte(passwd)) |
|
|
|