Skip to content

Instantly share code, notes, and snippets.

Last active August 29, 2022 07:31
  • Star 22 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
Star You must be signed in to star a gist
What would you like to do?
Password hashing and verification with Argon2id
package main
import (
var (
ErrInvalidHash = errors.New("the encoded hash is not in the correct format")
ErrIncompatibleVersion = errors.New("incompatible version of argon2")
type params struct {
memory uint32
iterations uint32
parallelism uint8
saltLength uint32
keyLength uint32
func main() {
p := &params{
memory: 64 * 1024,
iterations: 3,
parallelism: 2,
saltLength: 16,
keyLength: 32,
encodedHash, err := generateFromPassword("password123", p)
if err != nil {
match, err := comparePasswordAndHash("pa$$word", encodedHash)
if err != nil {
fmt.Printf("Match: %v\n", match)
func generateFromPassword(password string, p *params) (encodedHash string, err error) {
// Generate a cryptographically secure random salt.
salt, err := generateRandomBytes(p.saltLength)
if err != nil {
return "", err
// Pass the plaintext password, salt and parameters to the argon2.IDKey
// function. This will generate a hash of the password using the Argon2id
// variant.
hash := argon2.IDKey([]byte(password), salt, p.iterations, p.memory, p.parallelism, p.keyLength)
// Base64 encode the salt and hashed password.
b64Salt := base64.RawStdEncoding.EncodeToString(salt)
b64Hash := base64.RawStdEncoding.EncodeToString(hash)
// Return a string using the standard encoded hash representation.
encodedHash = fmt.Sprintf("$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s", argon2.Version, p.memory, p.iterations, p.parallelism, b64Salt, b64Hash)
return encodedHash, nil
func generateRandomBytes(n uint32) ([]byte, error) {
b := make([]byte, n)
_, err := rand.Read(b)
if err != nil {
return nil, err
return b, nil
func comparePasswordAndHash(password, encodedHash string) (match bool, err error) {
// Extract the parameters, salt and derived key from the encoded password
// hash.
p, salt, hash, err := decodeHash(encodedHash)
if err != nil {
return false, err
// Derive the key from the other password using the same parameters.
otherHash := argon2.IDKey([]byte(password), salt, p.iterations, p.memory, p.parallelism, p.keyLength)
// Check that the contents of the hashed passwords are identical. Note
// that we are using the subtle.ConstantTimeCompare() function for this
// to help prevent timing attacks.
if subtle.ConstantTimeCompare(hash, otherHash) == 1 {
return true, nil
return false, nil
func decodeHash(encodedHash string) (p *params, salt, hash []byte, err error) {
vals := strings.Split(encodedHash, "$")
if len(vals) != 6 {
return nil, nil, nil, ErrInvalidHash
var version int
_, err = fmt.Sscanf(vals[2], "v=%d", &version)
if err != nil {
return nil, nil, nil, err
if version != argon2.Version {
return nil, nil, nil, ErrIncompatibleVersion
p = &params{}
_, err = fmt.Sscanf(vals[3], "m=%d,t=%d,p=%d", &p.memory, &p.iterations, &p.parallelism)
if err != nil {
return nil, nil, nil, err
salt, err = base64.RawStdEncoding.Strict().DecodeString(vals[4])
if err != nil {
return nil, nil, nil, err
p.saltLength = uint32(len(salt))
hash, err = base64.RawStdEncoding.Strict().DecodeString(vals[5])
if err != nil {
return nil, nil, nil, err
p.keyLength = uint32(len(hash))
return p, salt, hash, nil
Copy link

phifty commented Dec 11, 2018

Great code and blog post! I've implemented a similar thing in my Go version of crypt. Feel very welcome to do a code review or comment it in another constructive way.
Best regards

Copy link

Just a minor refactor but you could replace lines 96-99 with:

return subtle.ConstantTimeCompare(hash, otherHash) == 1, nil

Copy link

ke-bab commented Oct 19, 2020

How about to use argon2.Key() instead of argon2.IDKey()?
Seems like argon2i more suitable for server side password hashing and argon2id is more relevant to "proof-of-work" checks.

Copy link

@AleksandrMakarenkov my understanding is that 2i should be preferred to 2d for password hashing --- not necessarily preferred to 2id. Using 2id is arguably better than just 2i in most cases as it provides some resistance to TMTO attacks.

Copy link

ke-bab commented Nov 1, 2020

@alexedwards Hey!
You were right :D
After investigating some posts i realized that argon2id is recommended as "primary algo" for most cases.
Here the link,,
i hope it helps someone else!

Copy link

cbluth commented Apr 23, 2021

great blog post, thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment