pcmt/modules/password/argon.go

79 lines
1.7 KiB
Go
Raw Permalink Normal View History

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