// Copyright 2023 wanderer // 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() } }