panel/shared/hash.go
2024-11-03 15:33:08 -05:00

79 lines
1.4 KiB
Go

package shared
import (
"bytes"
"crypto/rand"
"encoding/base64"
"errors"
"golang.org/x/crypto/argon2"
"runtime"
"strings"
)
type Hash struct {
Hash []byte
Salt []byte
}
func (h *Hash) String() string {
return base64.StdEncoding.EncodeToString(h.Salt) + ";" + base64.StdEncoding.EncodeToString(h.Hash)
}
func NewHash(pass string, salt []byte) (hash *Hash, err error) {
if salt == nil {
salt = make([]byte, 16)
_, err = rand.Read(salt)
if err != nil {
return nil, err
}
}
var parallelism uint8
if runtime.NumCPU() > 255 {
parallelism = 255
} else {
parallelism = uint8(runtime.NumCPU()) // #nosec G115 -- False positive, if this does happen, blame radiation.
}
argonHash := argon2.IDKey([]byte(pass), salt, 6, 64*1024, parallelism, 45)
return &Hash{
Salt: salt,
Hash: argonHash,
}, nil
}
var ErrInvalidHashStr = errors.New("invalid hash string")
func CompareHash(hash string, pass string) (matches bool, err error) {
hashSplit := strings.Split(hash, ";")
if len(hashSplit) != 2 {
return false, ErrInvalidHashStr
}
salt, err := base64.StdEncoding.DecodeString(hashSplit[0])
if err != nil {
return false, err
}
decodedHash, err := base64.StdEncoding.DecodeString(hashSplit[1])
if err != nil {
return false, err
}
hashInfo, err := NewHash(pass, salt)
if err != nil {
return false, err
}
if !bytes.Equal(decodedHash, hashInfo.Hash) {
return false, nil
}
return true, nil
}