A package to provide hashing and different algorithms.
Designed to be extensible to other algorithms which use salting. Signed-off-by: Ethan Wellenreiter <ewellenreiter@gmail.com>
This commit is contained in:
parent
9cd57daf4b
commit
829349c2aa
@ -22,59 +22,104 @@ type Argon2id struct {
|
||||
Parallelism uint8
|
||||
}
|
||||
|
||||
func (a *Argon2id) encodeHashString(hash []byte, salt []byte) (string, error) {
|
||||
// Base64 encode the salt and hashed password.
|
||||
b64Salt := base64.RawStdEncoding.EncodeToString(salt)
|
||||
b64Hash := base64.RawStdEncoding.EncodeToString(hash)
|
||||
|
||||
// Return a string using the standard encoded hash representation.
|
||||
encoding := fmt.Sprintf("$%s$v=%d$m=%d,t=%d,p=%d$%s$%s", algorithmName, argon2.Version, a.Memory, a.Iterations, a.Parallelism, b64Salt, b64Hash)
|
||||
return encoding, nil
|
||||
func (a *Argon2id) getAlgoString() string {
|
||||
return fmt.Sprintf("$%s$v=%d$m=%d,t=%d,p=%d", algorithmName, argon2.Version, a.Memory, a.Iterations, a.Parallelism)
|
||||
}
|
||||
|
||||
func (a *Argon2id) decodeHashString(encodedString string) (algo Argon2id, hash []byte, salt []byte, err error) {
|
||||
func (a *Argon2id) decodeAlgoParams(encodedString string) (algo Argon2id, leftovers []string, err error) {
|
||||
algo = Argon2id{}
|
||||
|
||||
vals := strings.Split(encodedString, "$")
|
||||
if len(vals) != 6 {
|
||||
return algo, nil, nil, ErrInvalidHash
|
||||
if len(vals) > 4 {
|
||||
return algo, nil, ErrInvalidHash
|
||||
}
|
||||
|
||||
var name string
|
||||
if _, err = fmt.Sscanf(vals[1], "%s", &name); err != nil {
|
||||
return algo, nil, nil, err
|
||||
return algo, nil, err
|
||||
}
|
||||
if name != algorithmName {
|
||||
return algo, nil, nil, ErrWrongHashAlgo
|
||||
return algo, nil, ErrWrongHashAlgo
|
||||
}
|
||||
|
||||
var version int
|
||||
_, err = fmt.Sscanf(vals[2], "v=%d", &version)
|
||||
if err != nil {
|
||||
return algo, nil, nil, err
|
||||
return algo, nil, err
|
||||
}
|
||||
if version != argon2.Version {
|
||||
return algo, nil, nil, ErrIncompatibleVersion
|
||||
return algo, nil, ErrIncompatibleVersion
|
||||
}
|
||||
|
||||
_, err = fmt.Sscanf(vals[3], "m=%d,t=%d,p=%d", &algo.Memory, &algo.Iterations, &algo.Parallelism)
|
||||
if err != nil {
|
||||
return algo, nil, nil, err
|
||||
return algo, nil, err
|
||||
}
|
||||
|
||||
salt, err = base64.RawStdEncoding.Strict().DecodeString(vals[4])
|
||||
if err != nil {
|
||||
return algo, nil, nil, err
|
||||
}
|
||||
|
||||
hash, err = base64.RawStdEncoding.Strict().DecodeString(vals[5])
|
||||
if err != nil {
|
||||
return algo, nil, nil, err
|
||||
}
|
||||
|
||||
return algo, salt, hash, nil
|
||||
return algo, vals[4:], nil
|
||||
}
|
||||
|
||||
func (a *Argon2id) hashBytes(pass string, salt []byte, keyLen uint) ([]byte, error) {
|
||||
hash := argon2.IDKey([]byte(pass), salt, a.Iterations, a.Memory, a.Parallelism, uint32(keyLen))
|
||||
func (a *Argon2id) EncodeHashAndSalt(hash []byte, salt []byte) (string, error) {
|
||||
// Base64 encode the salt and hashed password.
|
||||
b64Hash := base64.RawStdEncoding.EncodeToString(hash)
|
||||
b64Salt := base64.RawStdEncoding.EncodeToString(salt)
|
||||
|
||||
// Return a string using the standard encoded hash representation.
|
||||
encoding := fmt.Sprintf("%s$%s$%s", a.getAlgoString(), b64Hash, b64Salt)
|
||||
return encoding, nil
|
||||
}
|
||||
|
||||
func (a *Argon2id) EncodeHash(hash []byte) (string, error) {
|
||||
// Base64 encode the salt and hashed password.
|
||||
b64Hash := base64.RawStdEncoding.EncodeToString(hash)
|
||||
|
||||
// Return a string using the standard encoded hash representation.
|
||||
encoding := fmt.Sprintf("%s$%s", a.getAlgoString(), b64Hash)
|
||||
return encoding, nil
|
||||
}
|
||||
|
||||
func (a *Argon2id) DecodeHashAndSalt(encodedString string) (algo Argon2id, hash []byte, salt []byte, err error) {
|
||||
|
||||
algo, values, err := a.decodeAlgoParams(encodedString)
|
||||
if err != nil {
|
||||
return algo, nil, nil, err
|
||||
}
|
||||
|
||||
if len(values) != 2 {
|
||||
return algo, nil, nil, ErrInvalidHash
|
||||
}
|
||||
|
||||
hash, err = base64.RawStdEncoding.Strict().DecodeString(values[0])
|
||||
if err != nil {
|
||||
return algo, nil, nil, err
|
||||
}
|
||||
|
||||
salt, err = base64.RawStdEncoding.Strict().DecodeString(values[1])
|
||||
if err != nil {
|
||||
return algo, nil, nil, err
|
||||
}
|
||||
|
||||
return algo, hash, salt, nil
|
||||
}
|
||||
|
||||
func (a *Argon2id) DecodeHash(encodedString string) (algo Argon2id, hash []byte, err error) {
|
||||
algo, values, err := a.decodeAlgoParams(encodedString)
|
||||
if err != nil {
|
||||
return algo, nil, err
|
||||
}
|
||||
|
||||
if len(values) != 1 {
|
||||
return algo, nil, ErrInvalidHash
|
||||
}
|
||||
|
||||
hash, err = base64.RawStdEncoding.Strict().DecodeString(values[0])
|
||||
if err != nil {
|
||||
return algo, nil, err
|
||||
}
|
||||
return algo, hash, nil
|
||||
}
|
||||
|
||||
func (a *Argon2id) HashString(in string, salt []byte, keyLen uint) ([]byte, error) {
|
||||
hash := argon2.IDKey([]byte(in), salt, a.Iterations, a.Memory, a.Parallelism, uint32(keyLen))
|
||||
return hash, nil
|
||||
}
|
||||
|
||||
@ -6,10 +6,11 @@ import (
|
||||
)
|
||||
|
||||
type HashAlgo interface {
|
||||
encodeHashString(hash []byte, salt []byte) (string, error)
|
||||
decodeHashString(encodedString string) (algo HashAlgo, hash []byte, salt []byte, err error)
|
||||
hashBytes(pass string, salt []byte, keyLength uint) ([]byte, error)
|
||||
// hashString(pass string) (string, error)
|
||||
EncodeHashAndSalt(hash []byte, salt []byte) (string, error)
|
||||
EncodeHash(hash []byte) (string, error)
|
||||
DecodeHashAndSalt(encodedString string) (algo Argon2id, hash []byte, salt []byte, err error)
|
||||
DecodeHash(encodedString string) (algo Argon2id, hash []byte, err error)
|
||||
HashString(in string, salt []byte, keyLen uint) ([]byte, error)
|
||||
}
|
||||
|
||||
type Hasher struct {
|
||||
@ -18,37 +19,70 @@ type Hasher struct {
|
||||
SaltLength uint
|
||||
}
|
||||
|
||||
func (h *Hasher) CheckPass(pass string, encoded string) (bool, error) {
|
||||
algo, oghash, salt, err := h.Algo.decodeHashString(encoded)
|
||||
func (h *Hasher) CheckString(pass string, encoded string) (bool, error) {
|
||||
algo, truehash, salt, err := h.Algo.DecodeHashAndSalt(encoded)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
newhash, err := algo.hashBytes(pass, salt, h.KeyLength)
|
||||
newhash, err := algo.HashString(pass, salt, h.KeyLength)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
equal, err := compareHash(oghash, newhash)
|
||||
equal, err := CompareHash(truehash, newhash)
|
||||
|
||||
return equal, err
|
||||
}
|
||||
|
||||
func (h *Hasher) Hash(password string) (string, error) {
|
||||
func (h *Hasher) CheckStringWithSalt(in string, salt []byte, encoded string) (bool, error) {
|
||||
algo, truehash, err := h.Algo.DecodeHash(encoded)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
newhash, err := algo.HashString(in, salt, h.KeyLength)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
equal, err := CompareHash(truehash, newhash)
|
||||
|
||||
return equal, err
|
||||
}
|
||||
|
||||
func (h *Hasher) HashString(in string) (string, error) {
|
||||
|
||||
salt, err := GenerateRandomBytes(h.SaltLength)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
hash, err := h.Algo.hashBytes(password, salt, h.KeyLength)
|
||||
hash, err := h.Algo.HashString(in, salt, h.KeyLength)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return h.Algo.encodeHashString(hash, salt)
|
||||
return h.Algo.EncodeHashAndSalt(hash, salt)
|
||||
}
|
||||
|
||||
func compareHash(h1 []byte, h2 []byte) (bool, error) {
|
||||
func (h *Hasher) HashStringWithSalt(in string, salt []byte) (string, error) {
|
||||
hash, err := h.Algo.HashString(in, salt, h.KeyLength)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return h.Algo.EncodeHash(hash)
|
||||
}
|
||||
|
||||
func (h *Hasher) Hash(in string) ([]byte, error) {
|
||||
salt, err := GenerateRandomBytes(h.SaltLength)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return h.Algo.HashString(in, salt, h.KeyLength)
|
||||
}
|
||||
|
||||
func CompareHash(h1 []byte, h2 []byte) (bool, error) {
|
||||
if subtle.ConstantTimeCompare(h1, h2) == 1 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user