71 lines
1.2 KiB
Go
71 lines
1.2 KiB
Go
package auth
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/rand"
|
|
"errors"
|
|
|
|
"golang.org/x/crypto/argon2"
|
|
)
|
|
|
|
type HashSalt struct {
|
|
Hash, Salt []byte
|
|
}
|
|
|
|
type Argon2IdHash struct {
|
|
time uint32
|
|
memory uint32
|
|
threads uint8
|
|
keyLen uint32
|
|
saltLen uint32
|
|
}
|
|
|
|
var (
|
|
ErrNoMatch error = errors.New("hash doesn't match")
|
|
)
|
|
|
|
func NewArgon2IdHash(time, saltLen uint32, memory uint32, threads uint8, keyLen uint32) *Argon2IdHash {
|
|
return &Argon2IdHash{
|
|
time: time,
|
|
saltLen: saltLen,
|
|
memory: memory,
|
|
threads: threads,
|
|
keyLen: keyLen,
|
|
}
|
|
}
|
|
|
|
func (a *Argon2IdHash) GenerateHash(password, salt []byte) (*HashSalt, error) {
|
|
var err error
|
|
if len(salt) == 0 {
|
|
salt, err = randomSecret(a.saltLen)
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
hash := argon2.IDKey(password, salt, a.time, a.memory, a.threads, a.keyLen)
|
|
return &HashSalt{Hash: hash, Salt: salt}, nil
|
|
}
|
|
|
|
func (a *Argon2IdHash) Compare(hash, salt, password []byte) error {
|
|
hashSalt, err := a.GenerateHash(password, salt)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !bytes.Equal(hash, hashSalt.Hash) {
|
|
return ErrNoMatch
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func randomSecret(length uint32) ([]byte, error) {
|
|
secret := make([]byte, length)
|
|
|
|
_, err := rand.Read(secret)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return secret, nil
|
|
}
|