trustdraw

package module
v0.0.0-...-e5415f8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 15, 2023 License: MIT Imports: 14 Imported by: 0

README

TrustDraw

A protocol for dealing and playing with a shuffled deck of cards in the open, using RSA, AES and Ed25519 encryption.

This protocol does not currently support returning cards to the deck, but will be extended to support it. It will require a further call to the dealer.

Try it out

# Install the TrustDraw CLI
$ go install github.com/jphastings/trustdraw@latest
go: downloading github.com/jphastings/trustdraw v1.0.0

# Deal a deck to play with
$ trustdraw deal standard52-fr test_data/dealer.pem test_data/player1.pub.pem test_data/player2.pub.pem > example.deal

# Verify that the deck was created by the dealer to prevent cheating
$ trustdraw verify example.deal test_data/dealer.pub.pem
✅ example.deal is a valid deck of 52 cards for 2 players

# As Player 2, get an allowKey, to allow Player 1 to draw a card
$ trustdraw allow-draw example.deal test_data/player2.pem 1
Creating example-game.player2.state to hold game state…
Your allowKey: BABFpJBzhiVJwMonZIDVDjk4

# As Player 1, use the allowKey given by Player 2 to draw a card
$ trustdraw draw example.deal test_data/player1.pem BABFpJBzhiVJwMonZIDVDjk4
Creating example-game.player1.state to hold game state…
You drew: 3♦️
Prove with: AACH+oA5nhR+JoulasCyHrmv

# As Player 2, when Player 1 plays 🃓, verify that they really drew that card
$ trustdraw verify-draw example.deal test_data/player2.pem 🃓 AACH+oA5nhR+JoulasCyHrmv
✅ This was a valid draw

# Demonstrate that a cheating draw is detectable
$ trustdraw verify-draw example.deal test_data/player2.pem 🂱 AACH+oA5nhR+JoulasCyHrmv
❌ This was not a valid draw

Protocol

Below is a walk-through of the deal and a draw of a two player game of Scrabble using this protocol. This also works for more players.

sequenceDiagram
    participant A as Alice
    participant B as Bob
    participant D as Dealer

    A->>D: Share public key, Tileset to use
    activate D
    B->>D: Share public key
    Note over D: Shuffles tiles & publishes Deal file
    deactivate D

    D->>B: Retrieve Deal file
    activate B
    B-->>B: Validate Deal file
    deactivate B
    D->>A: Retrieve Deal file
    activate A
    A-->>A: Validate Deal file
    deactivate A

    activate B
    Note over B: Allow 8 draws by Alice<br/>(Record first 8 tiles as Alice's)
    B->>A: 8× allowKeys for Alice
    deactivate B
    
    activate A
    Note over A: Draw 8 tiles with provided allowKeys<br/>(Record first 8 tiles as Alice's)
    Note over A: Allow 8 draws by Bob<br/>(Record tiles 9-16 as Bob's)
    A->>B: 8× allowKeys for Bob
    deactivate A

    loop Until game has winner
        activate B
        Note over B: Draw 8 tiles with provided allowKeys<br/>(Record tiles 9-16 as Bob's)
        Note over B: Decide on play<br/>eg. JOKED 8D 50
        B->>A: Scrabble notation for the play<br/>Bob's  allowKeys for each tile used
        deactivate B
        
        activate A
        Note over A: Validate Bob's turn:<br/>Do given allowKeys decrpt used tiles?<br/>Are used tiles recorded as Bob's?
        Note over A: Allow 5 replacement draws by Bob<br/>(Record tiles 17-21 as Bob's)
        Note over A: Decide on play<br/>eg. REV(O)TInG E5 94
        A->>B: Scrabble notation for move<br/>Alice's allowKeys for each tile used<br/>5× allowKeys for Bob
        deactivate A
    end

To deal the tiles:

  1. Both players send their public RSA keys to the dealer.
  2. Dealer generates 100 AES keys for Alice, and 100 for Bob. (As English Scrabble has 100 tiles)
  3. Dealer pairs off the keys made for Alice and Bob, and XORs them to make 100 combined keys.
  4. Dealer pairs off each of the (shuffled) cards ("E(1)", "J(8)", "S(1)", etc) with each of the combined keys, and symmetrically encrypts the card with the key — this is the "shuffled deck". (AES-128-GCM)
  5. Dealer encrypts all Alice's keys (in order, the "key stack"), for Alice's eyes only, using Alice's public RSA key. (AES-128-CTR preceeded by RSA(key))
  6. …and does the same for Bob.
  7. Dealer publishes the shuffled deck and these two encrypted blocks, all signed with a dealer's key (Ed25519), to demonstrate authenticity, as the "deal file".

To verify a deal:

  1. The contents of the deal file are compared with the provided signature

To allow a tile draw, to draw a tile, to play a tile, and to verify a drawn tile:

  1. Alice and Bob both independently retrieve & decrypted the deal file with their private keys
  2. Allowing a draw:
    1. Bob finds the top-most unused AES key from their key stack (recording it as "dealt to Alice") and shares it, combined with the tile number in the deck, with Alice as an "allowKey".
  3. Drawing a tile:
    1. Alice breaks apart the allowKey into the tile number, and Bob's AES key for it.
    2. Alice finds the AES key for that tile from their own key stack, recording it as used by themselves.
    3. Alice XORs their key and the one received from Bob to make the combined key.
    4. Alice uses this combined key to decrypt the relevant card from the "shuffled deck", and now has drawn a tile!
  4. Playing a tile:
    1. Alice shares the tile as part of play, sharing their associated allowKey along side it, for validation.
  5. Verifying a drawn tile:
    1. Bob breaks apart the allowKey provided by Alice during the play into a tile number, and Alice's AES key for it.
    2. Bob ensures that the tile number is recorded as having been given to Alice.
    3. Bob XORs their key and the one received from Alice to make the combined key.
    4. Bob uses this combined key to decrypt the relevant tile from the "shuffled deck"
    5. Bob knows the play was legitimate if the locally decrypted tile is the same as the one played by Alice.

Documentation

Index

Constants

View Source
const Version = "1.0"

Variables

View Source
var ErrNoCardsLeft = errors.New("no cards left to draw")

Functions

func Deal

func Deal(deck io.Writer, cards []string, dealerPrv ed25519.PrivateKey, playerPubs ...*rsa.PublicKey) error

Deal shuffles a set of 'cards', writing the deal file to the given deck io.Writer. It will contain all the information needed for the players to draw cards as part of a turn-based game without needing any further trust.

func VerifyDeal

func VerifyDeal(dealFile io.Reader, dealerPub ed25519.PublicKey) (int, int, error)

Types

type Game

type Game struct {
	Players int
	// contains filtered or unexported fields
}

func OpenGame

func OpenGame(dealFile io.Reader, playerPrv *rsa.PrivateKey, state string) (*Game, error)

OpenGame opens a deal file, returning a Deal that can be used to draw cards. Make sure you have Verified the deck before using it.

func (*Game) AllowDraw

func (g *Game) AllowDraw(intended PlayerNumber) (string, error)

AllowDraw retrieves the allowKey for that will allow the specified player to draw a card. An allowKey contains 2 bytes of card ID, followed by 16 bytes of the card's AES key.

func (*Game) Draw

func (g *Game) Draw(allowKeys ...string) (card string, allowKey string, alreadyDrawn bool, error error)

Draw uses the allowKeys shared by other players to draw the relevant card.

func (*Game) LoadState

func (g *Game) LoadState(states string) error

LoadState loads the game state from a string encoded with State().

func (*Game) State

func (g *Game) State() string

State produces a base64 encoded string that represents the current state of the game.

func (*Game) VerifyDraw

func (g *Game) VerifyDraw(testCard string, allowKeys ...string) (bool, error)

type PlayerNumber

type PlayerNumber int

PlayerNumber is 1-indexed (The first player is 1).

Directories

Path Synopsis
cmd
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL