Skip to content

Mental poker library implementing Bayer-Groth 2012 shuffle proofs for multi-party card shuffling without a trusted dealer

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT
Notifications You must be signed in to change notification settings

v26-solutions/ziffle

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ziffle

crates.io docs.rs

Mental poker shuffle protocol using zero-knowledge proofs.

ziffle implements a mental poker protocol where multiple players can collaboratively shuffle a deck of cards without any player learning the order, and then selectively reveal individual cards.

The protocol uses Bayer-Groth 2012 shuffle proofs to ensure that each shuffle is performed correctly without revealing the permutation.

ziffle is a #[no_std] crate.

⚠️ Security Warning

This code has not been independently audited. It is experimental software provided as-is without any warranties. While the implementation follows the Bayer-Groth 2012 specification, it may contain bugs or vulnerabilities.

DO NOT use this library to play for non-trivial amounts of money or in any high-stakes scenario. Use at your own risk.

Main Entry Point

The Shuffle struct is the primary interface for using this library. Create an instance with Shuffle::<N>::default() where N is the number of cards in your deck.

Example: Three-Player Poker Game

use ziffle::{Shuffle, AggregatePublicKey, AggregateRevealToken};

// Create a standard 52-card deck
let shuffle = Shuffle::<52>::default();
let mut rng = ark_std::test_rng(); // DO NOT USE IN PRODUCTION
let ctx = b"poker_game_session_123";

// Three players generate their keys and prove ownership
let (alice_sk, alice_pk, alice_proof) = shuffle.keygen(&mut rng, ctx);
let (bob_sk, bob_pk, bob_proof) = shuffle.keygen(&mut rng, ctx);
let (carol_sk, carol_pk, carol_proof) = shuffle.keygen(&mut rng, ctx);

// Each player verifies others' key ownership proofs
let alice_vpk = alice_proof.verify(alice_pk, ctx).unwrap();
let bob_vpk = bob_proof.verify(bob_pk, ctx).unwrap();
let carol_vpk = carol_proof.verify(carol_pk, ctx).unwrap();

// Create aggregate public key from all verified keys
let apk = AggregatePublicKey::new(&[alice_vpk, bob_vpk, carol_vpk]);

// Alice performs the initial shuffle
let (alice_deck, alice_proof) = shuffle.shuffle_initial_deck(&mut rng, apk, ctx);
let alice_vdeck = shuffle
    .verify_initial_shuffle(apk, alice_deck, alice_proof, ctx)
    .expect("Alice's shuffle should be valid");

// Bob shuffles Alice's deck
let (bob_deck, bob_proof) = shuffle.shuffle_deck(&mut rng, apk, &alice_vdeck, ctx);
let bob_vdeck = shuffle
    .verify_shuffle(apk, &alice_vdeck, bob_deck, bob_proof, ctx)
    .expect("Bob's shuffle should be valid");

// Carol shuffles Bob's deck
let (final_deck, carol_proof) = shuffle.shuffle_deck(&mut rng, apk, &bob_vdeck, ctx);
let final_vdeck = shuffle
    .verify_shuffle(apk, &bob_vdeck, final_deck, carol_proof, ctx)
    .expect("Carol's shuffle should be valid");

// Now the deck is fully shuffled and encrypted. Let's reveal the first card.
let first_card = final_vdeck.get(0).unwrap();

// Each player creates a reveal token for the first card
let (alice_token, alice_token_proof) =
    first_card.reveal_token(&mut rng, &alice_sk, alice_pk, ctx);
let (bob_token, bob_token_proof) =
    first_card.reveal_token(&mut rng, &bob_sk, bob_pk, ctx);
let (carol_token, carol_token_proof) =
    first_card.reveal_token(&mut rng, &carol_sk, carol_pk, ctx);

// All players verify each other's reveal tokens
let alice_vtoken = alice_token_proof
    .verify(alice_vpk, alice_token, first_card, ctx)
    .expect("Alice's token should be valid");
let bob_vtoken = bob_token_proof
    .verify(bob_vpk, bob_token, first_card, ctx)
    .expect("Bob's token should be valid");
let carol_vtoken = carol_token_proof
    .verify(carol_vpk, carol_token, first_card, ctx)
    .expect("Carol's token should be valid");

// Aggregate the verified tokens to decrypt the card
let aggregate_token = AggregateRevealToken::new(&[alice_vtoken, bob_vtoken, carol_vtoken]);

// Reveal the card's index in the original deck (0-51)
let card_index = shuffle
    .reveal_card(aggregate_token, first_card)
    .expect("Card should be revealed successfully");

println!("First card is at index: {}", card_index);
assert!(card_index < 52);

Serialized Sizes

The following table shows the serialized sizes of types that need to be transmitted over the network in a typical mental poker protocol. All sizes use compressed canonical serialization from arkworks.

Type Size Notes
PublicKey 33 bytes One per player at setup
OwnershipProof 65 bytes One per player at setup
MaskedDeck<52> 3,432 bytes 66 bytes per card (33 × 2)
ShuffleProof<52> 5,547 bytes One per shuffle operation
RevealToken 33 bytes One per player per revealed card
RevealTokenProof 98 bytes One per player per revealed card

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

About

Mental poker library implementing Bayer-Groth 2012 shuffle proofs for multi-party card shuffling without a trusted dealer

Topics

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Packages

No packages published

Languages