Cryptographic Dice?

This site allows you to secretly roll dice with your untrusted enemies in a way that should ensure that no set of the players can influence the outcome. Even if all of the other players are colluding, as long as there is one honest player, then the results should be fair. The server is also untrusted and not involved other than routing messages between the players, who establish a group session key using Diffie-Hellman so that they can have end-to-end encryption of the actual dice rolls. The players have four verification words that can be used out-of-band to verify that their actual public key was used to establish the session key, which protects against the server or a MITM attempting to sneak in a fake key for a player.

Flipping coins over the phone

To understand how it works, consider a thought experiment of how you would flip a coin with an unscrupulous person on the phone. You can't trust them to flip the coin and tell you the result. Likewise they don't trust you to flip the coin.

You could both flip a coin and have a rule that if the two coins are the same then it is a Heads, and if the two coins are different then the result is a Tails. This is fair, 50% of the time the coins will be the same and 50% of the time they will be different, except that now the issue is who says their result first? If you say you have a Heads, they can lie about their coin and effectively determine the result of the coin flip.

So what is necessary is a way to commit to the value of your coin flip, without revealing the actual value. One-way cryptographic hash functions are perfect for this, with a minor caveat -- if you were to publish just Hash("Heads") or Hash("Tails"), then your opponent can precompute both of them to see which you have sent. Instead of hashing just "Heads" or "Tails", each of you hash a large cryptographicly secure random number. And then the bottom bit of the random number is used to determine your coin flip: 0 is Tails, 1 is Heads. The bottom bit of your number and their number are XOR'ed and the result is used to determine the final coin flip. Now it doesn't matter who reveals first since the other party can't change their result.

(Another approach is that if you have exchanged public keys with your enemies, you could sign your coin flip (along with some random padding as before to ensure they can't guess it) and send just the signature first. Then both of you reveal the padding and the coin flip, and can verify each other's signatures, although this requires more math and key management than the simple XOR and hash)

The nice thing about this cryptographic coin flip techinque is that it generalizes to an arbitrary number of people on the phone call. Even if the rest of them collude to come up with random numbers that XOR to their desired value, they don't know if your random number will flip that result or not.

What if we want to keep the results secret?

With the XOR technique the server and anyone watching the clear-text traffic can learn the value of the coin flip. The actual traffic is encrypted with TLS, so it is harder to for anyone outside the server to see, but the server could still learn the result. Instead we we use Diffie-Hellman to generate a shared secret for all of the peers player and use it to protect all of the communication. This algorithm generalizes for arbitrary number of players, with some additional math and a way to agree on an ordering of the players. The worst case is the server can prevent key agreement, but that doesn't sacrifice the security of the system.

This way no outside party can determine what the result is for our coin flip, not even if they have code execution on the server. They can try to sneak a fake player into the round, but you can use an out-of-band channel (like your voice chat) to verify the public portion of each player's Diffie-Hellman key. If the four words don't match, then something has tried to MITM the group key establishment.

Now can we trust the server?

All 4-sided die faces in different orientations

If a dishonest player is colluding with the server, they could team up to send fake commitments for another player since the cheater can leak the shared secret and then the server can fake the source id. This is difficult since the honest player has no way to deny that they sent the messages.

Instead we've imported the public key management from secret.cards that generates an ephemeral signing key as part of joining the room and shares the key as part of setting up the shared secret. Then all messages from that player to the room are signed with that player's key and then encrypted with the shared secret. The other players can verify that the messgae is encrypted with the correct secret (using AES-GCM) and then verify that the signature on the message matches the claimed source that sent it.

How can player's verify the other players?

The hash of the shared secret is presented as a four-word verification phrase, and the public keys for each other player are also shown with a similar phrase. Players can verify these phrases out-of-band, such as on a separate audio chat, and then have some confidence that their session is secured with the correct key (and that no one is leaking it) and that the players are who they claim to be.

Adding new dice sets?

12-sided die faces

You can checkout the source code and add new dice to the docs/dice.json file. Eventually there should be a drag-and-drop way to add new images and sets.