terms used
• client seed: a string chosen by the player (or automatically generated if you don't choose one)
• server seed: 64-character hex string generated using crypto.randomBytes(32) and hashed with SHA-256
• nonce: the number of round you're on, increases by 1 for every round across all house games
• cursor: internal number representing a nonce within the round, in beef there are 19 cursor values for each round and the cursor increases for each "shuffle" of the 0-19 sequence
• HMAC: cryptographic function that mixes unhashed server seed, client seed, nonce and cursor to produce a result
here's a 3rd party HMAC tool to verify each step for yourself: https://cryptii.com/pipes/hmac - (or any other one you prefer)
basics
beef uses the "Fisher-Yates" shuffling method to shuffle numbers from 0-19, each corresponding to a "death point"
these are shuffled one by one for every round, with the "cursor" increasing for each shuffle, death points are already determined before your beef takes the first step, the outcome isn't influenced by how good you are at dodging cars
how HMAC is used to determine shuffles, example round to demonstrate
unhashed server seed: cb8bd577e2b1f62e91c0fe5296c145330bc8ae4307729a372aa4fe639a5af24c
client seed: 62OUyVeUGCRnTpU6
nonce: 9837
cursor: 0 (for the first shuffle)
here's what the "message" part of the HMAC looks like for calculating the first shuffle: <clientSeed:nonce:cursor> = 62OUyVeUGCRnTpU6:9837:0 (second shuffle would be 62OUyVeUGCRnTpU6:9837:1)
the "key" part is your unhashed server seed
if you're using the 3rd party site above to follow along it would look like this:
These values result in the following hexadecimal string:
225ddb7e98db55b0b5e30daaf4a20cd2a2c96bbf12b962a6e884f208789caa24
the first 4 bytes (8 hex characters) are used for shuffling, in this case "225ddb7e" - this value is converted from hexadecimal → decimal (225ddb7e → 576576382) then compared against the maximum fairness range, essentially so that 576576382 < 4,294,967,280 (largest of possible 32-bit values divisible by 20 to eliminate very small bias)
how that equates to a cow getting hit by a car
the decimal value from above is used to calculate which number to shuffle first: 576576382 % 20 = 2 (meaning if you divide x by 20, what number is left over)
the grid sequence looks like this before shuffling:
wat shuffling
wat shuffling
shuffling works by swapping a number from a specific position in the sequence with the last number which hasn't been swapped yet, in this case it'd be swapping 2 (3rd number in the sequence) with 19 (20th number in the sequence) which means for this example the last number in the sequence is now locked in as 2.
the grid sequence looks like this after the first shuffle:
the next shuffle uses 62OUyVeUGCRnTpU6:9837:1 as the "message" part, which gives 3205935593 as the decimal number
since the 20th slot is already decided, the calculation is 3205935593 % 19 = 5 ( the process continues like this, meaning the next shuffle would be x % 18 etc.)
grid sequence after second shuffle:
this is then done 17 more times, which results in the final sequence:
the final sequence is then compared to death positions by difficulty level
(Extreme: 10, Hard: 5, Medium: 3, Easy: 1)
the round used as example had this set to "Extreme" which means 10 of the first numbers are arranged in ascending order and used as the final death positions
this gives [6, 7, 8, 9, 10, 11, 13, 15, 17, 18], meaning the cow died on lane 6
so how would i know if it WAS rigged?
1. if your hashed server seed wasn't shown before playing
2. if your hashed server seed was shown to you only after committing to a client seed
3. if the algorithm was changed to use a different part of the hash (you can verify that this isn't the case with steps above)
4. if your hashed server seed didn't match the unhashed seed when checking with a SHA-256 tool
TL;DR
• beef provably fair works by combining your client seed, server seed, nonce and cursor to shuffle numbers corresponding to death positions in a random and unpredictable way
• death positions are predetermined, your cow being run over is purely visual
• "Fisher-Yates" shuffling method is used for the final death positions - meaning that for each round, the 0-19 sequence is shuffled 19 times, and the resulting sequence of numbers dictates where your cow dies
• since you're able to set a client seed (after the hashed server seed is revealed), there's nothing Duel can do to skew the odds without players immediately noticing.