Provably fair coin flip system in Laravel I made a provably fair 1v1 coin flip game in Laravel and I want to be sure it's safe (cannot be hacked) and that the outcome is random (50/50).

At first Player 1 starts the bet and the hash will be created. The hash is a SHA256 of the $secret, which will be revealed to the players after the game. This hash ensures that the game outcome depends on the client and not on the server (provably fair). Does it matter which hash type I use? Are they all equally safe?

$secret = str_random(40);
$bet = new Bet;
$bet-&gt;user_id = Auth::id();
$bet-&gt;amount = $amount;
$bet-&gt;secret_key = $secret;
$bet-&gt;secret_hash = hash('sha256', $secret);
$bet-&gt;save();


So after that, player 2 will be able to join player 1's bet. When player 2 accepts the bet, this is what happens:

$(document).delegate('.flipcoin-btn', 'click', function() {
    // When player 2 clicks the button
    SubmitFlipCoin($(this).attr('id'), CreateClientKey());
});

function CreateClientKey()
{
    var outStr = "", newStr;
    while(outStr.length &lt; 20)
    {
        newStr = Math.random().toString(36).slice(2);
        outStr += newStr.slice(0, Math.min(newStr.length, (20 - outStr.length)));
    }
    console.log("Client string created: "+outStr.toUpperCase());
    return outStr.toUpperCase();
};

function SubmitFlipCoin(id, client)
{
    StopMoneyUpdate = 1;

    $.ajax({
        method: 'POST',
        url: flipcoin_link,
        data: {id: id, clientstr: client, _token: token},
        dataType: 'JSON',
    })
    .done(function(result) {
        if(result.hasOwnProperty("error")) {
            ShowBetError('&lt;b&gt;'+result.error+'&lt;/b&gt;');
        }
    })
    .fail(function(error) {
        ShowBetError('&lt;b&gt;An error occurred while flipping the coin, please try again.&lt;/b&gt;');
    });
}


As you can see, the client (player 2) generates a random string, which will be send to the server. The server will then make an HMAC hash of bet_id+clientstring with the $secret as key. The outcome of the game is determined by this HMAC hash. It takes the second number of the HMAC hash. If the result is 0 to 4 then player 1 wins, and if result is 5 to 9 player 2 will win.

$resulthash = hash_hmac('sha512', $request-&gt;clientstr."-".$bet-&gt;id, $bet-&gt;secret_key);
$resultnumber = filter_var($resulthash, FILTER_SANITIZE_NUMBER_INT);
if($resultnumber[1] &lt; 5) {
    $result = 0;
}
else {
    $result = 1;
}


EDIT: I did some testing to see if the outcome is really 50/50 and it seems pretty accurate.

$dealer = 0;
$player = 0;
$times = 1000000;

for($i = 0; $i &lt; $times; $i++) {
    $result = filter_var(hash('sha512', rand()), FILTER_SANITIZE_NUMBER_INT)[1];
    if($result &lt; 5) {
        $dealer++;
    }
    else {
        $player++;
    }
}

echo "Dealer: ".$dealer/$times * 100;
echo "&lt;/p&gt;Player: ".$player/$times * 100;


This was the outcome that I got:

Dealer: 49.9891
Player: 50.0109
So why are you saving both the $secret and the hash of the $secret in the DB? 

$bet-&gt;secret_key = $secret;
$bet-&gt;secret_hash = hash('sha256', $secret);


Also how does the hash of the $secret allow you to ensure that the outcome is fair? Do you ever use this hash anywhere?

Also carrying out any critical operations that you expect to remain secure on a client that allows someone to view and alter the code via a console cannot be considered secure. Basically the game should be run entirely on the server with input from the clients, such as the bet amount and bet acceptance, and then the results can be distributed to the clients for display.

Do you have some mathematical proof showing that the distribution of numbers in the second position of a string generated from a HMAC hash after non-number characters are removed is actually random? If not then you cannot assert that this method is truly random and would give you the 50/50 chance you desire.

Since PHP already has a built int cryptographically secure pseudo-random integer generator, int random_int ( int $min , int $max ), why don't your just use that to generate you numbers between 0 and 9?