ory/kratos · error · ErrHashParametersOutOfBounds

pbkdf2 i= not in [1, ]

Error message

pbkdf2 i=%d not in [1, %d]

What it means

This error means a PBKDF2 hash declares an iteration count i that is 0 or greater than 10,000,000. i=0 does no key-stretching; a huge i makes every password comparison take unbounded CPU time, so the package caps it at ~10x OWASP's strongest published recommendation. It wraps ErrHashParametersOutOfBounds from validatePbkdf2Params during hash decode.

Solutions

  1. Check the iteration count in the hash string; it must be in [1, 10000000]
  2. Re-hash with an OWASP 2023 recommendation (600k for SHA-256, 210k for SHA-512)
  3. For legacy users with higher counts, migrate via rehash-on-login after successful authentication

Example fix

// before (i=50000000, rejected)
$pbkdf2-sha256$i=50000000$...
// after (i=600000, accepted)
$pbkdf2-sha256$i=600000$...
Defensive patterns

Strategy: validation

Validate before calling

func pbkdf2IterationsOK(i uint32) bool { return i >= 1 && i <= 10_000_000 }
// Or pre-validate the whole hash: hash.ValidateImportedHash(hashed)

Try / catch

if err := hash.ValidateImportedHash(raw); errors.Is(err, hash.ErrHashParametersOutOfBounds) {
    return fmt.Errorf("pbkdf2 iterations out of range: %w", err)
}

Prevention

When it happens

Trigger: decodePbkdf2Hash parses a PBKDF2 hash (e.g. $pbkdf2-sha256$i=... or {PBKDF2}... format) whose iteration count is 0 or exceeds 10,000,000, via ValidateImportedHash or during password comparison.

Common situations: Importing hashes from systems configured with tens of millions of iterations (e.g. long-lived high-security deployments or FIPS-style configs); hand-edited hash strings; zeroed iterations from a corrupt export.

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/d2c62f13c9a37bbb. Report an issue: GitHub.

Appendix: source

Thrown at hash/hash_limits.go:120

	return nil
}

func validateArgon2Params(memoryKiB uint64, iterations uint32, parallelism uint8) error {
	if memoryKiB == 0 || memoryKiB > uint64(maxArgon2MemoryKiB) {
		return errors.Wrapf(ErrHashParametersOutOfBounds, "argon2 m=%d KiB not in [1, %d]", memoryKiB, maxArgon2MemoryKiB)
	}
	if iterations == 0 || iterations > maxArgon2Iterations {
		return errors.Wrapf(ErrHashParametersOutOfBounds, "argon2 t=%d not in [1, %d]", iterations, maxArgon2Iterations)
	}
	if parallelism == 0 || parallelism > maxArgon2Parallelism {
		return errors.Wrapf(ErrHashParametersOutOfBounds, "argon2 p=%d not in [1, %d]", parallelism, maxArgon2Parallelism)
	}
	return nil
}

func validatePbkdf2Params(iterations uint32) error {
	if iterations == 0 || iterations > maxPbkdf2Iterations {
		return errors.Wrapf(ErrHashParametersOutOfBounds, "pbkdf2 i=%d not in [1, %d]", iterations, maxPbkdf2Iterations)
	}
	return nil
}

func validateBcryptHashCost(hashed []byte) error {
	cost, err := bcrypt.Cost(hashed)
	if err != nil {
		return err
	}
	if cost > maxBcryptCost {
		return errors.Wrapf(ErrHashParametersOutOfBounds, "bcrypt cost=%d exceeds max %d", cost, maxBcryptCost)
	}
	return nil
}

// ValidateImportedHash performs cost-parameter bounds checking on a hash
// before it is persisted via the admin identity import API. It is the
// import-time counterpart to the bounds checks in the compare* paths and

View on GitHub (pinned to b86338da04)