password.go 2.21 KiB
// Package pwhash provides a simple interface to hashed passwords with
// support for multiple hashing algorithms.
//
// The format is the well-known dollar-separated field string,
// extended with optional algorithm-specific parameters:
//
// $id[$params...]$salt$encrypted
//
// We extend 'id' beyond the values supported by the libc crypt(3)
// function with the following hashing algorithms:
//
// Scrypt (id '$s$'), in which case the parameters are N, R and P.
//
// Argon2 (id '$a2$'), with parameters time, memory and threads. To
// tune Argon2 parameters, you can run the benchmarks in this package:
// the parameterized benchmarks are named with
// time/memory(MB)/threads. For nicer results:
//
// go test -bench=Argon2 -run=none . 2>&1 | \
// awk '/^Bench/ {ops=1000000000 / $3; print $1 " " ops " ops/sec"}'
//
package pwhash
import (
"crypto/rand"
"io"
"strings"
)
// PasswordHash is the interface for a password hashing algorithm
// implementation.
type PasswordHash interface {
// ComparePassword returns true if the given password matches
// the encrypted one.
ComparePassword(string, string) bool
// Encrypt the given password.
Encrypt(string) string
}
func getRandomBytes(n int) []byte {
b := make([]byte, n)
_, err := io.ReadFull(rand.Reader, b[:])
if err != nil {
panic(err)
}
return b
}
// A registry of default handlers for decoding passwords.
var prefixRegistry = map[string]PasswordHash{
"$1$": NewSystemCrypt(),
"$5$": NewSystemCrypt(),
"$6$": NewSystemCrypt(),
"$s$": NewScrypt(),
"$a2$": NewArgon2(),
"$argon2i$": NewArgon2Std(),
}
// ComparePassword returns true if the given password matches the
// encrypted one.
func ComparePassword(encrypted, password string) bool {
for pfx, h := range prefixRegistry {
if strings.HasPrefix(encrypted, pfx) {
return h.ComparePassword(encrypted, password)
}
}
return false
}
// DefaultEncryptAlgorithm is used by the Encrypt function to encrypt
// passwords.
var DefaultEncryptAlgorithm PasswordHash
func init() {
DefaultEncryptAlgorithm = NewArgon2()
}
// Encrypt will encrypt a password with the default algorithm.
func Encrypt(password string) string {
return DefaultEncryptAlgorithm.Encrypt(password)
}