Public beta — not for production use. Data may be wiped at any time. Questions? Contact us.
A
Axiom
/Marketplace/christiangeorgelucas/password-hash-tools
Sign in

christiangeorgelucas/password-hash-tools

v0.1.0Go

The password-hash-tools package provides a set of composable nodes for securely hashing and verifying passwords using various algorithms, including Argon2id, bcrypt, scrypt, and PBKDF2-HMAC. It is designed for developers looking to implement robust password storage solutions with features like constant-time verification and cost-upgrade checks.

Published by Christian George Lucas· MIT

password-hashingsecurityargon2idbcryptscryptpbkdf2gocrypto

Use cases

  • Implement secure user authentication in web applications
  • Migrate existing password hashes to stronger algorithms
  • Perform password strength checks during user registration
  • Upgrade password storage parameters without user intervention

Nodes (11)

HashArgon2idunary
HashArgon2IdInput·PasswordHash

The HashArgon2id node derives a password hash using the Argon2id algorithm, which is recommended by OWASP for secure password storage. It takes a password, a user-supplied salt, and configurable cost parameters to produce a deterministic, PHC-format hash string along with the raw derived key and the salt used.

View source
package nodes

import (
	"context"
	"encoding/hex"

	"christiangeorgelucas/password-hash-tools/axiom"
	gen "christiangeorgelucas/password-hash-tools/gen"

	"golang.org/x/crypto/argon2"
)

// HashArgon2id derives a password hash using Argon2id (RFC 9106) with a
// caller-supplied salt and cost parameters, returning a standard PHC-format
// string plus the raw derived key. Deterministic: the same password, salt,
// and parameters always produce the same output.
func HashArgon2id(ctx context.Context, ax axiom.Context, input *gen.HashArgon2IdInput) (*gen.PasswordHash, error) {
	salt, errTok := decodeSalt(input.GetSalt(), input.GetSaltEncoding())
	if errTok != "" {
		return &gen.PasswordHash{Error: errTok}, nil
	}
	if len(salt) == 0 {
		return &gen.PasswordHash{Error: "EMPTY_SALT"}, nil
	}
	if len(salt) < 8 {
		return &gen.PasswordHash{Error: "SALT_TOO_SHORT"}, nil
	}

	memKiB := input.GetMemoryKib()
	if memKiB == 0 {
		memKiB = 65536
	}
	iterations := input.GetIterations()
	if iterations == 0 {
		iterations = 3
	}
	parallelism := input.GetParallelism()
	if parallelism == 0 {
		parallelism = 4
	}
	if parallelism > 255 {
		return &gen.PasswordHash{Error: "INVALID_PARAMS"}, nil
	}
	keyLen := input.GetKeyLen()
	if keyLen == 0 {
		keyLen = 32
	}

	hash := argon2.IDKey([]byte(input.GetPassword()), salt, iterations, memKiB, uint8(parallelism), keyLen)

	return &gen.PasswordHash{
		Algorithm:  "argon2id",
		PhcHash:    encodeArgon2idPHC(memKiB, iterations, parallelism, salt, hash),
		HashHex:    hex.EncodeToString(hash),
		HashBase64: b64std(hash),
		SaltBase64: b64std(salt),
	}, nil
}
HashBcryptunary
HashBcryptInput·PasswordHash

The HashBcrypt node derives a password hash using the bcrypt algorithm, which includes an internal random salt generation to ensure unique hashes for identical passwords. It accepts a password and a cost factor, returning a bcrypt-compatible hash string.

View source
package nodes

import (
	"context"
	"errors"

	"christiangeorgelucas/password-hash-tools/axiom"
	gen "christiangeorgelucas/password-hash-tools/gen"

	"golang.org/x/crypto/bcrypt"
)

// HashBcrypt derives a password hash using bcrypt. Unlike this package's
// other three hashers, bcrypt generates its own random salt internally (the
// reference algorithm gives callers no salt-injection point), so repeated
// calls with the same password produce a different `phc_hash` every time —
// VerifyBcrypt against any of them still succeeds.
func HashBcrypt(ctx context.Context, ax axiom.Context, input *gen.HashBcryptInput) (*gen.PasswordHash, error) {
	cost := int(input.GetCost())
	if cost == 0 {
		cost = bcrypt.DefaultCost
	}
	if cost < bcrypt.MinCost || cost > bcrypt.MaxCost {
		return &gen.PasswordHash{Error: "INVALID_PARAMS"}, nil
	}

	hashBytes, err := bcrypt.GenerateFromPassword([]byte(input.GetPassword()), cost)
	if err != nil {
		if errors.Is(err, bcrypt.ErrPasswordTooLong) {
			return &gen.PasswordHash{Error: "PASSWORD_TOO_LONG"}, nil
		}
		return &gen.PasswordHash{Error: "INVALID_PARAMS"}, nil
	}

	return &gen.PasswordHash{
		Algorithm: "bcrypt",
		PhcHash:   string(hashBytes),
	}, nil
}
HashPbkdf2unary
HashPbkdf2Input·PasswordHash

The HashPbkdf2 node derives a password hash using the PBKDF2-HMAC algorithm as specified in RFC 8018. It takes a password, a non-empty salt, an iteration count, a hash algorithm, and an output key length to produce a deterministic hash output.

View source
package nodes

import (
	"context"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/hex"
	"hash"
	"strings"

	"christiangeorgelucas/password-hash-tools/axiom"
	gen "christiangeorgelucas/password-hash-tools/gen"

	"golang.org/x/crypto/pbkdf2"
)

// pbkdf2HashFunc resolves a caller-supplied hash algorithm name (case-
// insensitive; '-'/'_' equivalent) to its constructor and normalized name.
// Defaults to sha256 when empty.
func pbkdf2HashFunc(name string) (func() hash.Hash, string, bool) {
	norm := strings.ToLower(strings.NewReplacer("-", "", "_", "").Replace(name))
	switch norm {
	case "", "sha256":
		return sha256.New, "sha256", true
	case "sha1":
		return sha1.New, "sha1", true
	case "sha384":
		return sha512.New384, "sha384", true
	case "sha512":
		return sha512.New, "sha512", true
	default:
		return nil, "", false
	}
}

// HashPbkdf2 derives a password hash using PBKDF2-HMAC (RFC 8018) with a
// caller-supplied salt, iteration count, and underlying hash, returning this
// package's own documented PHC-style string plus the raw derived key.
// Deterministic: the same password, salt, and parameters always produce the
// same output.
func HashPbkdf2(ctx context.Context, ax axiom.Context, input *gen.HashPbkdf2Input) (*gen.PasswordHash, error) {
	salt, errTok := decodeSalt(input.GetSalt(), input.GetSaltEncoding())
	if errTok != "" {
		return &gen.PasswordHash{Error: errTok}, nil
	}
	if len(salt) == 0 {
		return &gen.PasswordHash{Error: "EMPTY_SALT"}, nil
	}

	hf, hfName, ok := pbkdf2HashFunc(input.GetHashAlgorithm())
	if !ok {
		return &gen.PasswordHash{Error: "UNKNOWN_HASH_ALGORITHM"}, nil
	}

	iterations := input.GetIterations()
	if iterations == 0 {
		iterations = 600000
	}
	keyLen := input.GetKeyLen()
	if keyLen == 0 {
		keyLen = 32
	}

	hashBytes := pbkdf2.Key([]byte(input.GetPassword()), salt, int(iterations), int(keyLen), hf)

	return &gen.PasswordHash{
		Algorithm:  "pbkdf2-" + hfName,
		PhcHash:    encodePbkdf2PHC(hfName, iterations, salt, hashBytes),
		HashHex:    hex.EncodeToString(hashBytes),
		HashBase64: b64std(hashBytes),
		SaltBase64: b64std(salt),
	}, nil
}
HashScryptunary
HashScryptInput·PasswordHash

The HashScrypt node derives a password hash using the scrypt algorithm as specified in RFC 7914, utilizing a user-defined salt and configurable cost parameters. It produces a deterministic output, ensuring that the same inputs will always yield the same hash.

View source
package nodes

import (
	"context"
	"encoding/hex"

	"christiangeorgelucas/password-hash-tools/axiom"
	gen "christiangeorgelucas/password-hash-tools/gen"

	"golang.org/x/crypto/scrypt"
)

// HashScrypt derives a password hash using scrypt (RFC 7914) with a
// caller-supplied salt and cost parameters, returning this package's own
// documented PHC-style string plus the raw derived key. Deterministic: the
// same password, salt, and parameters always produce the same output.
func HashScrypt(ctx context.Context, ax axiom.Context, input *gen.HashScryptInput) (*gen.PasswordHash, error) {
	salt, errTok := decodeSalt(input.GetSalt(), input.GetSaltEncoding())
	if errTok != "" {
		return &gen.PasswordHash{Error: errTok}, nil
	}
	if len(salt) == 0 {
		return &gen.PasswordHash{Error: "EMPTY_SALT"}, nil
	}

	n := input.GetN()
	if n == 0 {
		n = 16384
	}
	r := input.GetR()
	if r == 0 {
		r = 8
	}
	p := input.GetP()
	if p == 0 {
		p = 1
	}
	keyLen := input.GetKeyLen()
	if keyLen == 0 {
		keyLen = 32
	}

	hash, err := scrypt.Key([]byte(input.GetPassword()), salt, int(n), int(r), int(p), int(keyLen))
	if err != nil {
		return &gen.PasswordHash{Error: "INVALID_PARAMS"}, nil
	}

	return &gen.PasswordHash{
		Algorithm:  "scrypt",
		PhcHash:    encodeScryptPHC(n, r, p, salt, hash),
		HashHex:    hex.EncodeToString(hash),
		HashBase64: b64std(hash),
		SaltBase64: b64std(salt),
	}, nil
}
NeedsRehashunary
NeedsRehashInput·NeedsRehashResult

The NeedsRehash node determines if a stored password hash's cost parameters are below a specified target, indicating that the hash should be rehashed on the next successful login. It evaluates the parameters of the stored hash against the provided target parameters and returns a decision along with the current parameters of the hash.

View source
package nodes

import (
	"context"
	"strconv"

	"christiangeorgelucas/password-hash-tools/axiom"
	gen "christiangeorgelucas/password-hash-tools/gen"
)

// NeedsRehash decides whether a stored hash's embedded cost parameters have
// fallen below a caller-chosen current target (e.g. after raising the
// site-wide Argon2id memory cost), so it should be re-hashed the next time
// the plaintext password is available (typically right after a successful
// login). It reuses ParsePhcString to identify the algorithm and parse the
// hash's own parameters, then compares each parameter present in both sets
// numerically; a target key with no numeric counterpart (or absent from the
// hash's own params) is skipped, not an error.
func NeedsRehash(ctx context.Context, ax axiom.Context, input *gen.NeedsRehashInput) (*gen.NeedsRehashResult, error) {
	info, err := ParsePhcString(ctx, ax, &gen.ParsePhcStringInput{PhcHash: input.GetPhcHash()})
	if err != nil {
		return nil, err
	}
	if info.GetError() != "" {
		return &gen.NeedsRehashResult{Algorithm: "unknown", Error: info.GetError()}, nil
	}

	needs := false
	for key, targetStr := range input.GetTargetParams() {
		curStr, ok := info.GetParams()[key]
		if !ok {
			continue
		}
		targetN, err1 := strconv.ParseFloat(targetStr, 64)
		curN, err2 := strconv.ParseFloat(curStr, 64)
		if err1 != nil || err2 != nil {
			continue // non-numeric params (e.g. pbkdf2's "hash" name) are skipped
		}
		if curN < targetN {
			needs = true
		}
	}

	return &gen.NeedsRehashResult{
		NeedsRehash:   needs,
		Algorithm:     info.GetAlgorithm(),
		CurrentParams: info.GetParams(),
	}, nil
}
ParsePhcStringunary
ParsePhcStringInput·PhcInfo

The ParsePhcString node inspects a stored hash string generated by various hashing algorithms and extracts its algorithm type, cost/tuning parameters, and salt without performing any verification. It returns 'unknown' for unrecognized or unparseable strings.

View source
package nodes

import (
	"context"
	"strconv"
	"strings"

	"christiangeorgelucas/password-hash-tools/axiom"
	gen "christiangeorgelucas/password-hash-tools/gen"
)

// ParsePhcString inspects a stored hash string — as produced by any of this
// package's four Hash* nodes — and reports its algorithm, cost/tuning
// parameters, and salt, without verifying anything against it.
func ParsePhcString(ctx context.Context, ax axiom.Context, input *gen.ParsePhcStringInput) (*gen.PhcInfo, error) {
	phc := input.GetPhcHash()
	if phc == "" {
		return &gen.PhcInfo{Algorithm: "unknown", Error: "EMPTY_INPUT"}, nil
	}

	switch detectAlgorithmFamily(phc) {
	case "argon2id":
		p, ok := parseArgon2idPHC(phc)
		if !ok {
			return &gen.PhcInfo{Algorithm: "unknown", Error: "UNKNOWN_HASH_FORMAT"}, nil
		}
		return &gen.PhcInfo{
			Algorithm: "argon2id",
			Params: map[string]string{
				"v": strconv.Itoa(int(p.version)),
				"m": strconv.Itoa(int(p.memKiB)),
				"t": strconv.Itoa(int(p.iterations)),
				"p": strconv.Itoa(int(p.parallelism)),
			},
			SaltBase64: b64std(p.salt),
		}, nil

	case "bcrypt":
		parts := strings.Split(phc, "$")
		// ["", "2a"/"2b"/"2y", cost, saltandhash]
		if len(parts) != 4 {
			return &gen.PhcInfo{Algorithm: "unknown", Error: "UNKNOWN_HASH_FORMAT"}, nil
		}
		if _, err := strconv.Atoi(parts[2]); err != nil {
			return &gen.PhcInfo{Algorithm: "unknown", Error: "UNKNOWN_HASH_FORMAT"}, nil
		}
		return &gen.PhcInfo{
			Algorithm: "bcrypt",
			Params:    map[string]string{"cost": parts[2]},
		}, nil

	case "scrypt":
		p, ok := parseScryptPHC(phc)
		if !ok {
			return &gen.PhcInfo{Algorithm: "unknown", Error: "UNKNOWN_HASH_FORMAT"}, nil
		}
		return &gen.PhcInfo{
			Algorithm: "scrypt",
			Params: map[string]string{
				"n": strconv.Itoa(int(p.n)),
				"r": strconv.Itoa(int(p.r)),
				"p": strconv.Itoa(int(p.p)),
			},
			SaltBase64: b64std(p.salt),
		}, nil

	case "pbkdf2":
		p, ok := parsePbkdf2PHC(phc)
		if !ok {
			return &gen.PhcInfo{Algorithm: "unknown", Error: "UNKNOWN_HASH_FORMAT"}, nil
		}
		return &gen.PhcInfo{
			Algorithm: "pbkdf2-" + p.hashName,
			Params: map[string]string{
				"i":    strconv.Itoa(int(p.iterations)),
				"hash": p.hashName,
			},
			SaltBase64: b64std(p.salt),
		}, nil

	default:
		return &gen.PhcInfo{Algorithm: "unknown", Error: "UNKNOWN_HASH_FORMAT"}, nil
	}
}
Verifyunary
VerifyInput·VerifyResult

The Verify node checks a candidate password against a stored hash string by auto-detecting the hashing algorithm used (argon2id, bcrypt, scrypt, or pbkdf2) based on the hash's prefix. It allows for seamless verification of passwords from multiple hashing algorithms without requiring the caller to track which algorithm was used for each hash.

View source
package nodes

import (
	"context"

	"christiangeorgelucas/password-hash-tools/axiom"
	gen "christiangeorgelucas/password-hash-tools/gen"
)

// Verify checks a candidate password against a stored hash string,
// auto-detecting the algorithm (argon2id, bcrypt, scrypt, or pbkdf2) from
// the string's own prefix and dispatching to the matching per-algorithm
// verifier — for a caller that stores hashes from more than one algorithm
// (e.g. after migrating cost parameters) and doesn't want to track which
// produced which. A wrong password is a clean non-match (matches=false,
// error empty), never an error.
func Verify(ctx context.Context, ax axiom.Context, input *gen.VerifyInput) (*gen.VerifyResult, error) {
	phc := input.GetPhcHash()
	if phc == "" {
		return &gen.VerifyResult{Error: "EMPTY_INPUT"}, nil
	}
	switch detectAlgorithmFamily(phc) {
	case "argon2id":
		return VerifyArgon2id(ctx, ax, input)
	case "bcrypt":
		return VerifyBcrypt(ctx, ax, input)
	case "scrypt":
		return VerifyScrypt(ctx, ax, input)
	case "pbkdf2":
		return VerifyPbkdf2(ctx, ax, input)
	default:
		return &gen.VerifyResult{Error: "UNKNOWN_HASH_FORMAT"}, nil
	}
}
VerifyArgon2idunary
VerifyInput·VerifyResult

The VerifyArgon2id node checks a candidate password against a stored Argon2id PHC hash string, recomputing the hash using the embedded parameters and performing a constant-time comparison. It returns a result indicating whether the password matches the hash, along with any relevant error messages for incorrect formats or algorithms.

View source
package nodes

import (
	"context"
	"crypto/subtle"

	"christiangeorgelucas/password-hash-tools/axiom"
	gen "christiangeorgelucas/password-hash-tools/gen"

	"golang.org/x/crypto/argon2"
)

// VerifyArgon2id checks a candidate password against a stored Argon2id PHC
// string, recomputing the hash under the string's own embedded parameters
// and comparing in constant time. A wrong password is a clean non-match
// (matches=false, error empty), never an error.
func VerifyArgon2id(ctx context.Context, ax axiom.Context, input *gen.VerifyInput) (*gen.VerifyResult, error) {
	phc := input.GetPhcHash()
	if phc == "" {
		return &gen.VerifyResult{Error: "EMPTY_INPUT"}, nil
	}
	fam := detectAlgorithmFamily(phc)
	if fam != "argon2id" {
		if fam == "" {
			return &gen.VerifyResult{Error: "UNKNOWN_HASH_FORMAT"}, nil
		}
		return &gen.VerifyResult{Algorithm: fam, Error: "WRONG_ALGORITHM"}, nil
	}
	p, ok := parseArgon2idPHC(phc)
	if !ok {
		return &gen.VerifyResult{Error: "UNKNOWN_HASH_FORMAT"}, nil
	}
	computed := argon2.IDKey([]byte(input.GetPassword()), p.salt, p.iterations, p.memKiB, uint8(p.parallelism), uint32(len(p.hash)))
	matches := subtle.ConstantTimeCompare(computed, p.hash) == 1
	return &gen.VerifyResult{Matches: matches, Algorithm: "argon2id"}, nil
}
VerifyBcryptunary
VerifyInput·VerifyResult

The VerifyBcrypt node checks a candidate password against a stored bcrypt hash string using bcrypt's constant-time comparison method. It returns whether the password matches the hash, along with any relevant error messages for incorrect formats or algorithms.

View source
package nodes

import (
	"context"
	"errors"

	"christiangeorgelucas/password-hash-tools/axiom"
	gen "christiangeorgelucas/password-hash-tools/gen"

	"golang.org/x/crypto/bcrypt"
)

// VerifyBcrypt checks a candidate password against a stored bcrypt hash
// string ($2a$/$2b$/$2y$). bcrypt.CompareHashAndPassword performs the
// comparison in constant time internally. A wrong password is a clean
// non-match (matches=false, error empty), never an error.
func VerifyBcrypt(ctx context.Context, ax axiom.Context, input *gen.VerifyInput) (*gen.VerifyResult, error) {
	phc := input.GetPhcHash()
	if phc == "" {
		return &gen.VerifyResult{Error: "EMPTY_INPUT"}, nil
	}
	fam := detectAlgorithmFamily(phc)
	if fam != "bcrypt" {
		if fam == "" {
			return &gen.VerifyResult{Error: "UNKNOWN_HASH_FORMAT"}, nil
		}
		return &gen.VerifyResult{Algorithm: fam, Error: "WRONG_ALGORITHM"}, nil
	}

	err := bcrypt.CompareHashAndPassword([]byte(phc), []byte(input.GetPassword()))
	switch {
	case err == nil:
		return &gen.VerifyResult{Matches: true, Algorithm: "bcrypt"}, nil
	case errors.Is(err, bcrypt.ErrMismatchedHashAndPassword):
		return &gen.VerifyResult{Matches: false, Algorithm: "bcrypt"}, nil
	default:
		return &gen.VerifyResult{Algorithm: "bcrypt", Error: "UNKNOWN_HASH_FORMAT"}, nil
	}
}
VerifyPbkdf2unary
VerifyInput·VerifyResult

The VerifyPbkdf2 node checks a candidate password against a stored PBKDF2-HMAC hash string, recomputing the hash with the parameters embedded in the hash string and performing a constant-time comparison. It returns a result indicating whether the password matches, along with any errors related to the hash format or algorithm used.

View source
package nodes

import (
	"context"
	"crypto/subtle"

	"christiangeorgelucas/password-hash-tools/axiom"
	gen "christiangeorgelucas/password-hash-tools/gen"

	"golang.org/x/crypto/pbkdf2"
)

// VerifyPbkdf2 checks a candidate password against a stored PBKDF2-HMAC hash
// string, recomputing the hash under the string's own embedded parameters
// and comparing in constant time. A wrong password is a clean non-match
// (matches=false, error empty), never an error.
func VerifyPbkdf2(ctx context.Context, ax axiom.Context, input *gen.VerifyInput) (*gen.VerifyResult, error) {
	phc := input.GetPhcHash()
	if phc == "" {
		return &gen.VerifyResult{Error: "EMPTY_INPUT"}, nil
	}
	fam := detectAlgorithmFamily(phc)
	if fam != "pbkdf2" {
		if fam == "" {
			return &gen.VerifyResult{Error: "UNKNOWN_HASH_FORMAT"}, nil
		}
		return &gen.VerifyResult{Algorithm: fam, Error: "WRONG_ALGORITHM"}, nil
	}
	p, ok := parsePbkdf2PHC(phc)
	if !ok {
		return &gen.VerifyResult{Error: "UNKNOWN_HASH_FORMAT"}, nil
	}
	hf, _, ok := pbkdf2HashFunc(p.hashName)
	if !ok {
		return &gen.VerifyResult{Algorithm: "pbkdf2", Error: "UNKNOWN_HASH_FORMAT"}, nil
	}
	computed := pbkdf2.Key([]byte(input.GetPassword()), p.salt, int(p.iterations), len(p.hash), hf)
	matches := subtle.ConstantTimeCompare(computed, p.hash) == 1
	return &gen.VerifyResult{Matches: matches, Algorithm: "pbkdf2-" + p.hashName}, nil
}
VerifyScryptunary
VerifyInput·VerifyResult

The VerifyScrypt node checks a candidate password against a stored scrypt hash string, recomputing the hash with the embedded parameters and ensuring a constant time comparison to prevent timing attacks. It returns a result indicating whether the password matches the hash, along with any relevant error messages for incorrect formats or algorithms.

View source
package nodes

import (
	"context"
	"crypto/subtle"

	"christiangeorgelucas/password-hash-tools/axiom"
	gen "christiangeorgelucas/password-hash-tools/gen"

	"golang.org/x/crypto/scrypt"
)

// VerifyScrypt checks a candidate password against a stored scrypt hash
// string, recomputing the hash under the string's own embedded parameters
// and comparing in constant time. A wrong password is a clean non-match
// (matches=false, error empty), never an error.
func VerifyScrypt(ctx context.Context, ax axiom.Context, input *gen.VerifyInput) (*gen.VerifyResult, error) {
	phc := input.GetPhcHash()
	if phc == "" {
		return &gen.VerifyResult{Error: "EMPTY_INPUT"}, nil
	}
	fam := detectAlgorithmFamily(phc)
	if fam != "scrypt" {
		if fam == "" {
			return &gen.VerifyResult{Error: "UNKNOWN_HASH_FORMAT"}, nil
		}
		return &gen.VerifyResult{Algorithm: fam, Error: "WRONG_ALGORITHM"}, nil
	}
	p, ok := parseScryptPHC(phc)
	if !ok {
		return &gen.VerifyResult{Error: "UNKNOWN_HASH_FORMAT"}, nil
	}
	computed, err := scrypt.Key([]byte(input.GetPassword()), p.salt, int(p.n), int(p.r), int(p.p), len(p.hash))
	if err != nil {
		return &gen.VerifyResult{Algorithm: "scrypt", Error: "UNKNOWN_HASH_FORMAT"}, nil
	}
	matches := subtle.ConstantTimeCompare(computed, p.hash) == 1
	return &gen.VerifyResult{Matches: matches, Algorithm: "scrypt"}, nil
}

Messages (11)

Download .proto
HashArgon2IdInput
password:string

The plaintext password that needs to be hashed.

salt:string

A caller-supplied salt used in the hashing process, which must be at least 8 bytes long.

salt_encoding:string

The encoding format of the salt, which can be base64, base64url, hex, or utf8.

memory_kib:uint32

The memory cost in KiB for the hashing process, defaulting to 64 MiB if set to 0.

iterations:uint32

The number of iterations for the hashing process, defaulting to 3 if set to 0.

parallelism:uint32

The number of parallel threads to use during hashing, defaulting to 4 if set to 0, with a maximum of 255.

key_len:uint32

The desired length of the derived key in bytes, defaulting to 32 bytes if set to 0.

HashBcryptInput
password:string

The plaintext password to be hashed, which is limited to the first 72 bytes.

cost:uint32

The cost factor for the bcrypt algorithm, which ranges from 4 to 31, defaulting to 10 when set to 0.

HashPbkdf2Input
password:string

The plaintext password to be hashed.

salt:string

A non-empty salt value used in the hashing process.

salt_encoding:string

The encoding format of the salt (e.g., base64).

iterations:uint32

The number of iterations for the PBKDF2 algorithm, defaulting to 600000 if set to 0.

hash_algorithm:string

The underlying HMAC hash algorithm to use (sha1, sha256, sha384, sha512), defaulting to sha256 if not recognized.

key_len:uint32

The desired length of the output key in bytes, defaulting to 32 if set to 0.

HashScryptInput
password:string

The plaintext password that needs to be hashed.

salt:string

A non-empty salt value used in the hashing process, which must be decoded according to the specified salt encoding.

salt_encoding:string

The encoding format for the salt, which determines how the salt is interpreted.

n:uint32
r:uint32

The block size parameter that affects memory usage; defaults to 8 if set to 0.

p:uint32

The parallelization factor that determines the number of parallel computations; defaults to 1 if set to 0.

key_len:uint32
NeedsRehashInput
phc_hash:string

The stored hash string that needs to be evaluated.

NeedsRehashResult
needs_rehash:bool

A boolean indicating whether the stored hash needs to be rehashed based on the comparison of parameters.

algorithm:string

The algorithm used for the stored hash, which is identified during the evaluation.

error:string

An error message indicating if the hash format is unrecognized or unparseable.

ParsePhcStringInput
phc_hash:string
PasswordHash
algorithm:string
phc_hash:string
hash_hex:string
hash_base64:string
salt_base64:string
error:string
PhcInfo
algorithm:string

The hashing algorithm used to generate the hash (e.g., argon2id, bcrypt, scrypt, pbkdf2).

VerifyInput
password:string

The candidate password that needs to be verified against the stored hash.

phc_hash:string
VerifyResult
matches:bool

Indicates whether the candidate password matches the stored hash.

algorithm:string

The algorithm used for hashing, which should be 'bcrypt' for this node.

error:string

Any error message that indicates issues with the input or hashing process.

Use as a tool in any AI agent

christiangeorgelucas/password-hash-tools is callable as a tool from any MCP client (Claude, Cursor, or your own agent) via Axiom's hosted MCP server — no install, no download. Add the server, then your agent can search and invoke it, or pin it as a typed tool.

claude mcp add --transport http axiom https://api.axiomide.com/mcp --header "Authorization: Bearer YOUR_AXIOM_API_KEY"

Replace YOUR_AXIOM_API_KEY with a key you create in Console → API Keys.

MCP server setup & other clients →

Use this package

Sign in to import christiangeorgelucas/password-hash-tools into the graph editor — drop it into a flow and hit run. Or call it directly via the API Reference with your API key.

Sign in to useOpen editor

Stats

Error rate (1h)0.0%
Back to marketplace