Argon2 Password Hashing
Hash passwords with Argon2id and a random salt, encode parameters into a PHC string, and verify in constant time.
Code
package main
import (
"crypto/rand"
"crypto/subtle"
"encoding/base64"
"fmt"
"strings"
"golang.org/x/crypto/argon2"
)
const (
argonTime = 1
argonMemory = 64 * 1024
argonThreads = 4
argonKeyLen = 32
argonSaltLen = 16
)
func HashPassword(password string) (string, error) {
salt := make([]byte, argonSaltLen)
if _, err := rand.Read(salt); err != nil {
return "", fmt.Errorf("read salt: %w", err)
}
key := argon2.IDKey([]byte(password), salt, argonTime, argonMemory, argonThreads, argonKeyLen)
return fmt.Sprintf(
"$argon2id$v=19$m=%d,t=%d,p=%d$%s$%s",
argonMemory,
argonTime,
argonThreads,
base64.RawStdEncoding.EncodeToString(salt),
base64.RawStdEncoding.EncodeToString(key),
), nil
}
func VerifyPassword(password, encoded string) (bool, error) {
parts := strings.Split(encoded, "$")
if len(parts) != 6 || parts[1] != "argon2id" {
return false, fmt.Errorf("unsupported hash format")
}
var memory uint32
var timeCost uint32
var threads uint8
if _, err := fmt.Sscanf(parts[3], "m=%d,t=%d,p=%d", &memory, &timeCost, &threads); err != nil {
return false, fmt.Errorf("parse parameters: %w", err)
}
salt, err := base64.RawStdEncoding.DecodeString(parts[4])
if err != nil {
return false, fmt.Errorf("decode salt: %w", err)
}
want, err := base64.RawStdEncoding.DecodeString(parts[5])
if err != nil {
return false, fmt.Errorf("decode hash: %w", err)
}
got := argon2.IDKey([]byte(password), salt, timeCost, memory, threads, uint32(len(want)))
return subtle.ConstantTimeCompare(got, want) == 1, nil
}
func main() {
encoded, err := HashPassword("correct horse battery staple")
if err != nil {
panic(err)
}
fmt.Println("encoded prefix:", encoded[:10])
match, err := VerifyPassword("correct horse battery staple", encoded)
if err != nil {
panic(err)
}
reject, err := VerifyPassword("wrong password", encoded)
if err != nil {
panic(err)
}
fmt.Println("match:", match, "reject:", reject)
}Report a problem with this exampleOpens a pre-filled issue on GitHub.