79 lines
1.7 KiB
Go
79 lines
1.7 KiB
Go
// Copyright 2023 wanderer <a_mirre at utb dot cz>
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
package password
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/matthewhartstonge/argon2"
|
|
"golang.org/x/exp/rand"
|
|
)
|
|
|
|
var (
|
|
// argon provides the package with current config. the one currently set on
|
|
// init is the memory-hard argon2.RecommendedConfig().
|
|
argon argon2.Config
|
|
// r stores a seeded *rand.Rand so that the generated salts are more close
|
|
// to non-deterministic.
|
|
r = rand.New(rand.NewSource(uint64(time.Now().UnixNano())))
|
|
)
|
|
|
|
// Argon accepts a password string and returns hash and salt byte slices, or an
|
|
// error.
|
|
func Argon(password string) (*argon2.Raw, error) {
|
|
salt := make([]byte, argon.SaltLength)
|
|
|
|
err := genSalt(salt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
hash, err := argon.Hash([]byte(password), salt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &hash, nil
|
|
}
|
|
|
|
// ArgonDecode attempts to decode the given byte slice into an argon raw
|
|
// struct.
|
|
func ArgonDecode(digest []byte) (*argon2.Raw, error) {
|
|
hash, err := argon2.Decode(digest)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &hash, nil
|
|
}
|
|
|
|
// ArgonVerify checks that the provided password matches the one used to create
|
|
// the provided digest, returns a bool and an error.
|
|
func ArgonVerify(password string, digest []byte) (bool, error) {
|
|
return argon2.VerifyEncoded([]byte(password), digest)
|
|
}
|
|
|
|
// genSalt fills slice b with len(b) random bytes.
|
|
func genSalt(b []byte) error {
|
|
_, err := r.Read(b)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func init() {
|
|
// TODO: hide this behind a config flag someday.
|
|
noop := false
|
|
|
|
if noop {
|
|
// 64MiB peak memory usage.
|
|
argon = argon2.DefaultConfig()
|
|
} else {
|
|
// 2GiB peak memory usage.
|
|
argon = argon2.RecommendedDefaults()
|
|
}
|
|
}
|