From 153acb7f644d755c45533b7c5311e3ffa47e8973 Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Fri, 18 Sep 2026 05:31:09 -0400 Subject: fix: harden engine across casino, economy, concurrency, security, and content --- internal/player/hardening_password_params_test.go | 62 +++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 internal/player/hardening_password_params_test.go (limited to 'internal/player/hardening_password_params_test.go') diff --git a/internal/player/hardening_password_params_test.go b/internal/player/hardening_password_params_test.go new file mode 100644 index 0000000..4c171f1 --- /dev/null +++ b/internal/player/hardening_password_params_test.go @@ -0,0 +1,62 @@ +package player + +// Regression: Argon2id parameters parsed from a stored hash must be bounded +// before they reach argon2.IDKey. A hand-edited or corrupted account file +// (e.g. argon2id:1:4294967295:...) must fail the check, not request a +// multi-TiB allocation that kills the process. + +import ( + "crypto/sha256" + "encoding/base64" + "encoding/hex" + "testing" +) + +func TestCheckPasswordRejectsOutOfRangeArgonParams(t *testing.T) { + // Enormous memory cost (0xFFFFFFFF KiB) with an otherwise well-formed + // hash: must be rejected before hashing. + huge := "argon2id:1:4294967295:2:AAAAAAAAAAAAAAAAAAAAAA:0011223344556677889900112233445566778899001122334455667788990011" + if CheckPassword("pw", huge) { + t.Fatal("out-of-range Argon2 memory cost was accepted") + } + + // Negative values would wrap through uint32 under a naive conversion. + negative := "argon2id:-1:-1:-1:AAAAAAAAAAAAAAAAAAAAAA:0011223344556677889900112233445566778899001122334455667788990011" + if CheckPassword("pw", negative) { + t.Fatal("negative Argon2 parameters were accepted") + } + + // Absurd time cost is bounded too. + slow := "argon2id:4294967295:65536:2:AAAAAAAAAAAAAAAAAAAAAA:0011223344556677889900112233445566778899001122334455667788990011" + if CheckPassword("pw", slow) { + t.Fatal("out-of-range Argon2 time cost was accepted") + } +} + +func TestCheckPasswordAcceptsNormalAndLegacyHashes(t *testing.T) { + hashed, err := HashPassword("s3cret") + if err != nil { + t.Fatal(err) + } + if !CheckPassword("s3cret", hashed) { + t.Fatal("valid Argon2id hash rejected") + } + if CheckPassword("wrong", hashed) { + t.Fatal("wrong password accepted") + } + + // Legacy single-pass sha256 hashes must keep verifying so old accounts + // can still log in and be upgraded. + salt := []byte("sixteen-byte-slt") + sum := sha256.Sum256(append(append([]byte{}, salt...), []byte("pw")...)) + legacy := "sha256:" + base64.RawStdEncoding.EncodeToString(salt) + ":" + hex.EncodeToString(sum[:]) + if !CheckPassword("pw", legacy) { + t.Fatal("legacy sha256 hash rejected") + } + if CheckPassword("nope", legacy) { + t.Fatal("wrong password accepted against legacy sha256 hash") + } + if !NeedsRehash(legacy) { + t.Fatal("legacy sha256 hash not flagged for rehash") + } +} -- cgit v1.2.3