ory/kratos · error · ErrHashParametersOutOfBounds

firescrypt ln= exceeds max

Error message

firescrypt ln=%d exceeds max %d

What it means

This error means a Firebase scrypt password hash declares a ln (log2 of N) parameter above the package ceiling of 17. The limit exists because firebase-scrypt allocates 128*N*r bytes of memory, so an attacker-controlled hash with an enormous ln could cause an OOM or multi-second CPU burn every time the comparator runs at login. It wraps ErrHashParametersOutOfBounds and is raised by validateFirebaseScryptParams when a stored or imported hash is decoded.

Solutions

  1. Check the ln field in the hash string (format ...|ln=NN|...) and confirm it is <= 17
  2. Re-hash affected users' passwords on next login instead of importing the high-cost hash directly
  3. If legitimate high costs are needed, lower the hash to ln<=17 (e.g. re-encode with ln=14, Firebase's strongest documented profile)
  4. File an issue / fork to raise maxScryptLogN if your deployment genuinely requires higher costs

Example fix

// before (hash with ln=20, rejected)
$firescrypt$ln=20$r=8$p=1$...
// after (re-encoded with ln=14, accepted)
$firescrypt$ln=14$r=8$p=1$...
Defensive patterns

Strategy: validation

Validate before calling

// Before importing a $firescrypt hash, check its ln parameter.
func firebaseScryptLnOK(ln uint32) bool { return ln <= 17 }
// Or pre-validate the full hash without persisting it:
// if err := hash.ValidateImportedHash(hashedPassword); err != nil {
//     if errors.Is(err, hash.ErrHashParametersOutOfBounds) { /* reject */ }
// }

Try / catch

if _, _, _, _, _, err := hash.ValidateImportedHash(raw); errors.Is(err, hash.ErrHashParametersOutOfBounds) {
    return fmt.Errorf("import rejected: %w", err)
}

Prevention

When it happens

Trigger: Calling ValidateImportedHash (or the admin identity import API) with a $firescrypt hash whose ln parameter exceeds 17, or comparing passwords against a stored hash with such a ln during decodeFirebaseScryptHash.

Common situations: Migrating users from Firebase Auth with hashes generated under a very high scrypt cost; hand-edited or forged hash strings; importing hashes exported from a system tuned for higher security than this package allows.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of ory/kratos@b86338da04 (2026-09-07). Data as JSON: /api/errors/6ec2c5052df6ba60. Report an issue: GitHub.

Appendix: source

Thrown at hash/hash_limits.go:81

	// up to 8-core hosts.
	maxArgon2Parallelism uint8 = 16

	// maxPbkdf2Iterations bounds PBKDF2 i. OWASP 2023 recommends 600k
	// (SHA-256) / 210k (SHA-512). 10M ≈ 10× the strongest published
	// recommendation and bounds CPU to a few seconds on modern hardware.
	maxPbkdf2Iterations uint32 = 10_000_000

	// maxBcryptCost bounds bcrypt cost. The format spec allows 4–31, but
	// cost grows exponentially: cost 12 (Kratos, PHP, Django default) is
	// ~250 ms; cost 14 (high-security guidance) is ~1 s; cost 15 (practical
	// max for interactive use) is ~2 s; cost 17 is ~8 s. No mainstream
	// platform defaults above cost 12.
	maxBcryptCost = 15
)

func validateFirebaseScryptParams(logN, r, p uint32) error {
	if logN > maxScryptLogN {
		return errors.Wrapf(ErrHashParametersOutOfBounds, "firescrypt ln=%d exceeds max %d", logN, maxScryptLogN)
	}
	if r == 0 || r > maxScryptR {
		return errors.Wrapf(ErrHashParametersOutOfBounds, "firescrypt r=%d not in [1, %d]", r, maxScryptR)
	}
	if p == 0 || p > maxScryptP {
		return errors.Wrapf(ErrHashParametersOutOfBounds, "firescrypt p=%d not in [1, %d]", p, maxScryptP)
	}
	return nil
}

func validateScryptParams(n, r, p uint32) error {
	if n == 0 || n > maxScryptN {
		return errors.Wrapf(ErrHashParametersOutOfBounds, "scrypt N=%d not in [1, %d]", n, maxScryptN)
	}
	if r == 0 || r > maxScryptR {
		return errors.Wrapf(ErrHashParametersOutOfBounds, "scrypt r=%d not in [1, %d]", r, maxScryptR)
	}
	if p == 0 || p > maxScryptP {

View on GitHub (pinned to b86338da04)