{"record":{"id":"6ec2c5052df6ba60","repo":"ory/kratos","slug":"firescrypt-ln-d-exceeds-max-d","errorCode":null,"errorMessage":"firescrypt ln=%d exceeds max %d","messagePattern":"firescrypt ln=(.+?) exceeds max (.+?)","errorType":"validation","errorClass":"ErrHashParametersOutOfBounds","httpStatus":null,"severity":"error","filePath":"hash/hash_limits.go","lineNumber":81,"sourceCode":"\t// up to 8-core hosts.\n\tmaxArgon2Parallelism uint8 = 16\n\n\t// maxPbkdf2Iterations bounds PBKDF2 i. OWASP 2023 recommends 600k\n\t// (SHA-256) / 210k (SHA-512). 10M ≈ 10× the strongest published\n\t// recommendation and bounds CPU to a few seconds on modern hardware.\n\tmaxPbkdf2Iterations uint32 = 10_000_000\n\n\t// maxBcryptCost bounds bcrypt cost. The format spec allows 4–31, but\n\t// cost grows exponentially: cost 12 (Kratos, PHP, Django default) is\n\t// ~250 ms; cost 14 (high-security guidance) is ~1 s; cost 15 (practical\n\t// max for interactive use) is ~2 s; cost 17 is ~8 s. No mainstream\n\t// platform defaults above cost 12.\n\tmaxBcryptCost = 15\n)\n\nfunc validateFirebaseScryptParams(logN, r, p uint32) error {\n\tif logN > maxScryptLogN {\n\t\treturn errors.Wrapf(ErrHashParametersOutOfBounds, \"firescrypt ln=%d exceeds max %d\", logN, maxScryptLogN)\n\t}\n\tif r == 0 || r > maxScryptR {\n\t\treturn errors.Wrapf(ErrHashParametersOutOfBounds, \"firescrypt r=%d not in [1, %d]\", r, maxScryptR)\n\t}\n\tif p == 0 || p > maxScryptP {\n\t\treturn errors.Wrapf(ErrHashParametersOutOfBounds, \"firescrypt p=%d not in [1, %d]\", p, maxScryptP)\n\t}\n\treturn nil\n}\n\nfunc validateScryptParams(n, r, p uint32) error {\n\tif n == 0 || n > maxScryptN {\n\t\treturn errors.Wrapf(ErrHashParametersOutOfBounds, \"scrypt N=%d not in [1, %d]\", n, maxScryptN)\n\t}\n\tif r == 0 || r > maxScryptR {\n\t\treturn errors.Wrapf(ErrHashParametersOutOfBounds, \"scrypt r=%d not in [1, %d]\", r, maxScryptR)\n\t}\n\tif p == 0 || p > maxScryptP {","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/ory/kratos/blob/b86338da04a040247a07f46100a86dcfb3875909/hash/hash_limits.go#L63-L99","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check the ln field in the hash string (format ...|ln=NN|...) and confirm it is <= 17","Re-hash affected users' passwords on next login instead of importing the high-cost hash directly","If legitimate high costs are needed, lower the hash to ln<=17 (e.g. re-encode with ln=14, Firebase's strongest documented profile)","File an issue / fork to raise maxScryptLogN if your deployment genuinely requires higher costs"],"exampleFix":"// before (hash with ln=20, rejected)\n$firescrypt$ln=20$r=8$p=1$...\n// after (re-encoded with ln=14, accepted)\n$firescrypt$ln=14$r=8$p=1$...","handlingStrategy":"validation","validationCode":"// Before importing a $firescrypt hash, check its ln parameter.\nfunc firebaseScryptLnOK(ln uint32) bool { return ln <= 17 }\n// Or pre-validate the full hash without persisting it:\n// if err := hash.ValidateImportedHash(hashedPassword); err != nil {\n//     if errors.Is(err, hash.ErrHashParametersOutOfBounds) { /* reject */ }\n// }","typeGuard":null,"tryCatchPattern":"if _, _, _, _, _, err := hash.ValidateImportedHash(raw); errors.Is(err, hash.ErrHashParametersOutOfBounds) {\n    return fmt.Errorf(\"import rejected: %w\", err)\n}","preventionTips":["Run hash.ValidateImportedHash on every hash before persisting via the admin import API","Keep Firebase migration profiles within ln<=14 (Firebase's strongest documented setting)","Alert on any stored hash whose parameters approach the ceilings","Reject user- or admin-supplied hash strings at intake rather than at login time"],"tags":["hashing","scrypt","firebase","security"],"backgroundTag":"value-out-of-range","analyzedSha":"b86338da04a040247a07f46100a86dcfb3875909","analyzedAt":"2026-09-07T15:58:15.934Z","contentChangedAt":"2026-09-07T15:58:15.934Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}