Three Poker Security Challenges: What the Source Actually Protects
How default Hold’em guards future cards, hidden hands and settlement, with post-hand privacy limits and offline tampering results.
Knowing the board early, reading an opponent’s hole cards and changing your hand to win test three different boundaries: randomness, information access and server settlement. BluffKing’s current security bounty offers RMB 10,000 for each reproduced and confirmed challenge.
This article examines the default Hold’em sealed-dealing path at source snapshot 78f381f46c8970f8478c903ed1f6e54ea8a71f0c, reviewed on October 26, 2026, and runs the repository’s offline verifier. We did not reproduce an attack against production or establish that its running binaries match this snapshot. These conclusions do not extend to every game, tournaments or advanced encrypted mode.
01 Predict community cards before reveal
Can a player know the exact community cards for a hand, including suits and ranks, before the server officially reveals them?
What does the source code do?
The separate dealer process generates a secret 32-byte server_seed using operating-system randomness, OsRng. Before dealing, it publishes a domain-separated SHA-256 commitment, keeping the seed itself secret.
Protocol-capable clients can contribute randomness by submitting commitments before revealing their seeds. The final deck seed combines the server seed, accepted client seeds in seat order and this hand’s hand_id. A client that times out may contribute nothing, so client entropy cannot be assumed for every hand.
The complete 256-bit seed initializes ChaCha20Rng, which drives a Fisher–Yates shuffle. Swap positions use rejection sampling to avoid modulo bias. Once the order is fixed, the game loop requests board cards from the dealer as play reaches each stage; client messages contain the board cards already revealed. See sources [1]–[4].
Is it possible?
With uncompromised secret seeds, a sound operating-system entropy source and secure cryptographic primitives, this review found no practical path through this implementation for an ordinary player to calculate the exact board in advance. Knowing the algorithm, hand ID, your client seed or previous outcomes still leaves a secret input missing. Equity estimates do not identify the actual suits and ranks.
A lucky guess is possible, but one correct guess does not establish access to hidden information. Evidence needs to bind a prediction to a specific hand and show that it was submitted before the official reveal.
Leaked seeds, a compromised host or a new authorization flaw could change that conclusion. The dealer holds the plaintext deck, so process isolation cannot establish that the operator can never know the cards. A seed commitment also does not independently rule out selective hand aborts.
03 Change your hand in server settlement
Can you make the server accept a modified hand and use it to determine the winner and settle the hand? Changing only the browser display does not qualify.
What does the source code do?
An ordinary action message submits fold, check, call, raise or all-in, together with an amount where applicable and the current decision number. It has no field allowing a player to choose their hole cards. Adding supposed replacement cards to a hostile message does not make the normal action handler replace the engine’s hand. [9]
The server also determines identity. The WebSocket handler injects sender_user_id from the authenticated connection; a same-named client field is not deserialized into that identity. Session logic checks that the sender owns the acting seat and that decision_epoch matches the current decision, preventing out-of-turn actions and stale action replay. [10], [11]
When default sealed dealing requires a showdown, the game service obtains contenders’ cards from the dealer and passes them to the engine for ranking. finish_blind() requires a completed hand and returns an error when a contested pot lacks required reveals; it does not invent cards. The service then validates the final deck commitment and checks that the board and showdown cards used in settlement match their positions in that deck. [4], [12]
Is it possible?
This review found no path through display changes or ordinary action requests that makes the server settle using forged hole cards. Achieving that would require an additional flaw that changes authoritative card state, bypasses the trusted card source or defeats settlement checks. Control over the server or host also changes the threat assumptions. This review does not prove that every endpoint and deployment configuration is free of such flaws.
What did offline record tampering show?
We ran the repository’s pf_demo_hand and pf_verify, making four modifications to the same deterministic example record: [13]
| Input | Offline result |
|---|---|
| Original example, checking 3 seats and 5 board cards | Accepted, exit code 0 |
| Replace the first board card, J♦, with A♠ | Rejected: board mismatch |
| Replace seat 0’s K♣ with A♠ | Rejected: hole-card mismatch |
| Replace the server seed but retain its original commitment | Rejected: commitment mismatch |
| Remove every hole card and board card | Rejected: no comparable cards |
These results show that the offline verifier detects these record changes. They did not exercise production actions or account settlement and cannot establish a live server-settlement test for challenge three. The example uses a fixed seed and does not establish production randomness quality. For checking a real hand, see how to verify a hand independently.
Source references
Paths refer to the reviewed repository. Links pin the matching published files to an immutable public-source snapshot; unlinked entries identify the server code reviewed.
server/src/pf_dealing.rs—os_random_32 / seed_commit / derive_deck_seedengine/src/rng.rs—PokerRng::from_seed_bytesengine/src/deck.rs—Deck::new / rng_rangeserver/src/sealed_dealer.rs—SealedHand / sync_tickets / deliver_holes / ActiveHand::finishserver/src/protocol/translation.rs—translate_for / snapshot_for_user_with_equities_fromserver/src/sealed_dealer/authorization.rs—guard / signserver/src/session/fair_dealing.rs—pf_emit_revealserver/src/handlers/hands_fairness.rs—get_hand_fairnessserver/src/protocol/client_commands.rs—ClientCmd::Actionserver/src/ws/socket.rs—sender_user_idserver/src/session/action_wait.rs—sender_valid / decision_epochengine/src/game/pots.rs—finish_blindmental-poker/src/bin—pf_demo_hand / pf_verify