package player import ( "crypto/sha256" "encoding/base64" "fmt" "strings" "testing" ) func TestHashPasswordUsesArgon2idAndVerifies(t *testing.T) { hash, err := HashPassword("secret password") if err != nil { t.Fatal(err) } if !strings.HasPrefix(hash, "argon2id:") { t.Fatalf("hash format = %q, want argon2id", hash) } if !CheckPassword("secret password", hash) { t.Fatal("correct password rejected") } if CheckPassword("wrong password", hash) { t.Fatal("wrong password accepted") } // Salts must be unique per call: two hashes of the same password differ. other, _ := HashPassword("secret password") if other == hash { t.Fatal("two hashes of the same password are identical (salt reuse)") } } func TestCheckPasswordVerifiesLegacySHA256(t *testing.T) { salt := make([]byte, 16) for i := range salt { salt[i] = byte(i) } sum := sha256.Sum256(append(salt, []byte("legacy pw")...)) legacy := fmt.Sprintf("sha256:%s:%x", base64.RawStdEncoding.EncodeToString(salt), sum) if !CheckPassword("legacy pw", legacy) { t.Fatal("correct legacy password rejected") } if CheckPassword("wrong", legacy) { t.Fatal("wrong legacy password accepted") } if !NeedsRehash(legacy) { t.Fatal("legacy hash not flagged for rehash") } if NeedsRehash("argon2id:1:65536:2:c2FsdA:x") { t.Fatal("argon2id hash flagged for rehash") } } func TestCheckPasswordRejectsGarbage(t *testing.T) { for _, stored := range []string{"", "plain", "argon2id:1:2:3:no-salt:zz", "unknown:v1:salt:deadbeef"} { if CheckPassword("anything", stored) { t.Fatalf("garbage hash accepted: %q", stored) } } }