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