147 lines
2.9 KiB
Go
147 lines
2.9 KiB
Go
package legacy
|
|
|
|
import (
|
|
"bytes"
|
|
"hash"
|
|
"crypto/sha1"
|
|
"crypto/sha512"
|
|
"encoding/base64"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// Credit: some of the SSHA code used from https://github.com/daknob/ssha/blob/master/ssha.go
|
|
|
|
// IsSSHA512 checks if a particular string is a SSHA hash or not
|
|
func IsSSHA512(hash string) bool {
|
|
/* Check if the string begins with {SSHA} */
|
|
if !strings.HasPrefix(hash, "{SSHA512}") {
|
|
return false
|
|
}
|
|
|
|
/* Check if the string has anything after {SSHA} and it has only one {SSHA} */
|
|
if len(strings.Split(hash, "{SSHA512}")) != 2 {
|
|
return false
|
|
}
|
|
|
|
/* Get the Base64-Encoded payload of the hash */
|
|
payload := strings.Split(hash, "{SSHA512}")[1]
|
|
|
|
/* Decode the payload */
|
|
decoded, err := base64.StdEncoding.DecodeString(payload)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
/* Check the payload length */
|
|
if len(decoded) < 64 {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
// IsSSHA checks if a particular string is a SSHA hash or not
|
|
func IsSSHA(hash string) bool {
|
|
/* Check if the string begins with {SSHA} */
|
|
if !strings.HasPrefix(hash, "{SSHA}") {
|
|
return false
|
|
}
|
|
|
|
/* Check if the string has anything after {SSHA} and it has only one {SSHA} */
|
|
if len(strings.Split(hash, "{SSHA}")) != 2 {
|
|
return false
|
|
}
|
|
|
|
/* Get the Base64-Encoded payload of the hash */
|
|
payload := strings.Split(hash, "{SSHA}")[1]
|
|
|
|
/* Decode the payload */
|
|
decoded, err := base64.StdEncoding.DecodeString(payload)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
/* Check the payload length */
|
|
if len(decoded) < 21 {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
type hashType uint8
|
|
|
|
const (
|
|
typeSSHA hashType = iota
|
|
typeSSHA512
|
|
typeInvalid
|
|
)
|
|
|
|
|
|
// GetHashParts returns the SSHA hash' Salt and SHA-1 hash
|
|
func GetHashParts(hash string) ([]byte, []byte, hashType, error) {
|
|
var sep string
|
|
var htype hashType
|
|
var hlen int
|
|
switch {
|
|
case IsSSHA(hash):
|
|
sep = "{SSHA}"
|
|
htype = typeSSHA
|
|
hlen = 20
|
|
case IsSSHA512(hash):
|
|
sep = "{SSHA512}"
|
|
htype = typeSSHA512
|
|
hlen = 64
|
|
default:
|
|
return nil, nil, typeInvalid, fmt.Errorf("hash is invalid")
|
|
}
|
|
|
|
decoded, err := base64.StdEncoding.DecodeString(strings.Split(hash, sep)[1])
|
|
|
|
if err != nil {
|
|
return nil, nil, typeInvalid, fmt.Errorf("failed to decode %s hash: %v", sep, err)
|
|
}
|
|
|
|
return decoded[0:hlen], decoded[hlen:], htype, nil
|
|
}
|
|
|
|
/*
|
|
VerifyHash accepts a SSHA string, as well as a plaintext password, and checks whether this
|
|
password is correct or not
|
|
*/
|
|
func VerifyHash(provided, password string) (bool, error) {
|
|
correct, salt, htype, err := GetHashParts(provided)
|
|
if err != nil {
|
|
return false, fmt.Errorf("failed to parse hash: %v", err)
|
|
}
|
|
if htype == typeInvalid {
|
|
return false, errors.New("failed to parse hash")
|
|
}
|
|
|
|
var phash hash.Hash
|
|
|
|
switch htype {
|
|
case typeSSHA:
|
|
phash = sha1.New()
|
|
phash.Write([]byte(password))
|
|
phash.Write(salt)
|
|
case typeSSHA512:
|
|
phash = sha512.New()
|
|
phash.Write([]byte(password))
|
|
phash.Write(salt)
|
|
default:
|
|
panic(htype)
|
|
}
|
|
|
|
if bytes.Equal(correct, phash.Sum(nil)) {
|
|
return true, nil
|
|
}
|
|
|
|
return false, nil
|
|
}
|