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 }