package main import ( "crypto/rand" "crypto/subtle" "encoding/base64" "fmt" "strings" "golang.org/x/crypto/argon2" ) const ( memory = 64 * 1024 // 64 MB iterations = 3 parallelism = 2 saltLength = 16 keyLength = 32 ) func generateSalt() ([]byte, error) { salt := make([]byte, saltLength) _, err := rand.Read(salt) return salt, err } func verifyPassword(password, encodedHash string) bool { parts := strings.Split(encodedHash, "$") params := parts[3] salt := parts[4] hash := parts[5] var memory uint32 var iterations uint32 var parallelism uint8 fmt.Sscanf(params, "m=%d,t=%d,p=%d", &memory, &iterations, ¶llelism) saltBytes, _ := base64.RawStdEncoding.DecodeString(salt) hashBytes, _ := base64.RawStdEncoding.DecodeString(hash) comparisonHash := argon2.IDKey( []byte(password), saltBytes, iterations, memory, parallelism, uint32(len(hashBytes)), ) return subtle.ConstantTimeCompare(hashBytes, comparisonHash) == 1 } func hashPassword(password string, salt []byte) string { hash := argon2.IDKey( []byte(password), salt, iterations, memory, parallelism, keyLength, ) b64Salt := base64.RawStdEncoding.EncodeToString(salt) b64Hash := base64.RawStdEncoding.EncodeToString(hash) return fmt.Sprintf("$argon2id$v=19$m=%d,t=%d,p=%d$%s$%s", memory, iterations, parallelism, b64Salt, b64Hash) }