*DISCLAIMER: The method described in this article for generating random secrets **IS FLAWED AND NOT SECURE. *I’m leaving the content up as a learning exercise, but generating random numbers from signatures is not good practice. An attacker could control k, the secure random variable used in generating a signature and create a valid signature until finding one that generates a favorable roll.
Many games involve elements of chance such as rolling dice or drawing cards. Often times, the result of this random event is to be held secret by a participant in the game until it is time to reveal the whole thing or a partial component of it. For example, in card games you are dealt a hand and you keep it secret from the opponents until it is time to show your hand or a card of your hand.
There are tools for generating randomness on chain using oracles such as Chainlink VRF, but keeping the result of this randomness secret to a specific participant could be potentially challenging. In this article I will propose a method for generating random secrets that accomplish the following goals.
I believe the following proposal fits these criteria, but I am looking for feedback on the security and viability of this method. It is possible this contains a massive flaw, so if you are stumbling upon this at some later date, there is no guarantee of security.
Imagine a game where two players, Alice and Bob, each roll a dice under a cup and keep the result secret from each other. They then take some action (wagering, bluffing, whatever game mechanics) about their two numbers. They reveal them to each other, and the round is over.
On the blockchain, we could program the game as such:
function generateRandomness() external {
...
randomSeed[msg.sender] = randomness;
}
...
player1RandomSeed = await contract.randomSeed(player1.address);
player1Sig = await player1.signMessage(ethers.utils.arrayify(player1RandomSeed);
p1RandomNum = sha256(player1Sig) % 6 + 1
When it is time to reveal the randomness, we can use Open Zeppelin’s ECDSA library and the mathematics of ECC to verify the signature on chain and set the number/do whatever is necessary for our game mechanics.
function revealAndVerify(bytes calldata _sig) external {
bytes32 signedHash = ECDSA.toEthSignedMessageHash(randomSeed[msg.sender]);
require(ECDSA.recover(signedHash, _sig) == msg.sender);
revealedNum = uint256(sha256(_sig))%6+1;
}
An extremely barebones implementation of this is on my Github:
Thanks for reading and appreciate any and all comments or feedback!