aboutsummaryrefslogtreecommitdiff
path: root/internal/player/password.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-09-18 05:31:09 -0400
committerhistoria <[not public]>2026-09-18 05:31:09 -0400
commit153acb7f644d755c45533b7c5311e3ffa47e8973 (patch)
tree7645e6f7b65bc3830b078e3b9336d2e6b199d584 /internal/player/password.go
parent87d08f0151e643951769e323995f1c5ed89ee200 (diff)
downloadthehouseoficarus-153acb7f644d755c45533b7c5311e3ffa47e8973.tar.gz
fix: harden engine across casino, economy, concurrency, security, and content
Diffstat (limited to 'internal/player/password.go')
-rw-r--r--internal/player/password.go25
1 files changed, 25 insertions, 0 deletions
diff --git a/internal/player/password.go b/internal/player/password.go
index 9be39fb..5a43e1b 100644
--- a/internal/player/password.go
+++ b/internal/player/password.go
@@ -31,6 +31,23 @@ const (
argonSaltLen = 16
)
+// Sanity bounds applied to Argon2id parameters parsed from a stored hash.
+// HashPassword always writes the constants above, but a hand-edited or
+// corrupted account file can carry arbitrary numbers; without these bounds a
+// value like 4294967295 (or a negative that wraps through uint32) makes
+// argon2.IDKey request a multi-TiB allocation and kills the process instead of
+// failing the login. Anything outside these ranges is rejected before hashing.
+const (
+ argonMinTime = 1
+ argonMaxTime = 8
+ argonMinMemory = 8 * 1024 // KiB (8 MiB)
+ argonMaxMemory = 256 * 1024 // KiB (256 MiB)
+ argonMinThreads = 1
+ argonMaxThreads = 16
+ argonMinKeyLen = 16
+ argonMaxKeyLen = 64
+)
+
// hashLimiter bounds concurrent Argon2id computations. Each hash costs
// ~64 MiB and tens of milliseconds, so an unbounded burst of logins (or
// login attempts) would spike memory and CPU in parallel. Password work
@@ -81,6 +98,14 @@ func CheckPassword(password, stored string) bool {
if errT != nil || errM != nil || errP != nil || errSalt != nil || errKey != nil {
return false
}
+ if t < argonMinTime || t > argonMaxTime ||
+ m < argonMinMemory || m > argonMaxMemory ||
+ p < argonMinThreads || p > argonMaxThreads ||
+ len(expected) < argonMinKeyLen || len(expected) > argonMaxKeyLen {
+ // Out-of-range parameters: fail the check instead of trusting the
+ // stored values and requesting an enormous allocation.
+ return false
+ }
acquireHashSlot()
key := argon2.IDKey([]byte(password), salt, uint32(t), uint32(m), uint8(p), uint32(len(expected)))
releaseHashSlot()