Skip to main content

ziffle/
lib.rs

1//! Mental poker shuffle protocol using zero-knowledge proofs.
2//!
3//! `ziffle` implements a mental poker protocol where multiple players can
4//! collaboratively shuffle a deck of cards without any player learning the order,
5//! and then selectively reveal individual cards.
6//!
7//! The protocol uses [Bayer-Groth 2012](http://www0.cs.ucl.ac.uk/staff/J.Groth/MinimalShuffle.pdf)
8//! shuffle proofs to ensure that each shuffle is performed correctly without revealing
9//! the permutation.
10//!
11//! `ziffle` is a `#[no_std]` crate.
12//!
13//! # ⚠️ Security Warning
14//!
15//! **This code has not been independently audited.** It is experimental software provided
16//! as-is without any warranties. While the implementation follows the Bayer-Groth 2012
17//! specification, it may contain bugs or vulnerabilities.
18//!
19//! **DO NOT use this library to play for non-trivial amounts of money or in any
20//! high-stakes scenario.** Use at your own risk.
21//!
22//! # Main Entry Point
23//!
24//! The [`Shuffle`] struct is the primary interface for using this library. Create an
25//! instance with `Shuffle::<N>::default()` where `N` is the number of cards in your deck.
26//!
27//! # Example: Three-Player Poker Game
28//!
29//! ```
30//! use ziffle::{Shuffle, AggregatePublicKey, AggregateRevealToken};
31//!
32//! // Create a standard 52-card deck
33//! let shuffle = Shuffle::<52>::default();
34//! let mut rng = ark_std::test_rng(); // DO NOT USE IN PRODUCTION
35//! let ctx = b"poker_game_session_123";
36//!
37//! // Three players generate their keys and prove ownership
38//! let (alice_sk, alice_pk, alice_proof) = shuffle.keygen(&mut rng, ctx);
39//! let (bob_sk, bob_pk, bob_proof) = shuffle.keygen(&mut rng, ctx);
40//! let (carol_sk, carol_pk, carol_proof) = shuffle.keygen(&mut rng, ctx);
41//!
42//! // Each player verifies others' key ownership proofs
43//! let alice_vpk = alice_proof.verify(alice_pk, ctx).unwrap();
44//! let bob_vpk = bob_proof.verify(bob_pk, ctx).unwrap();
45//! let carol_vpk = carol_proof.verify(carol_pk, ctx).unwrap();
46//!
47//! // Create aggregate public key from all verified keys
48//! let apk = AggregatePublicKey::new(&[alice_vpk, bob_vpk, carol_vpk]);
49//!
50//! // Alice performs the initial shuffle
51//! let (alice_deck, alice_proof) = shuffle.shuffle_initial_deck(&mut rng, apk, ctx);
52//! let alice_vdeck = shuffle
53//!     .verify_initial_shuffle(apk, alice_deck, alice_proof, ctx)
54//!     .expect("Alice's shuffle should be valid");
55//!
56//! // Bob shuffles Alice's deck
57//! let (bob_deck, bob_proof) = shuffle.shuffle_deck(&mut rng, apk, &alice_vdeck, ctx);
58//! let bob_vdeck = shuffle
59//!     .verify_shuffle(apk, &alice_vdeck, bob_deck, bob_proof, ctx)
60//!     .expect("Bob's shuffle should be valid");
61//!
62//! // Carol shuffles Bob's deck
63//! let (final_deck, carol_proof) = shuffle.shuffle_deck(&mut rng, apk, &bob_vdeck, ctx);
64//! let final_vdeck = shuffle
65//!     .verify_shuffle(apk, &bob_vdeck, final_deck, carol_proof, ctx)
66//!     .expect("Carol's shuffle should be valid");
67//!
68//! // Now the deck is fully shuffled and encrypted. Let's reveal the first card.
69//! let first_card = final_vdeck.get(0).unwrap();
70//!
71//! // Each player creates a reveal token for the first card
72//! let (alice_token, alice_token_proof) =
73//!     first_card.reveal_token(&mut rng, &alice_sk, alice_pk, ctx);
74//! let (bob_token, bob_token_proof) =
75//!     first_card.reveal_token(&mut rng, &bob_sk, bob_pk, ctx);
76//! let (carol_token, carol_token_proof) =
77//!     first_card.reveal_token(&mut rng, &carol_sk, carol_pk, ctx);
78//!
79//! // All players verify each other's reveal tokens
80//! let alice_vtoken = alice_token_proof
81//!     .verify(alice_vpk, alice_token, first_card, ctx)
82//!     .expect("Alice's token should be valid");
83//! let bob_vtoken = bob_token_proof
84//!     .verify(bob_vpk, bob_token, first_card, ctx)
85//!     .expect("Bob's token should be valid");
86//! let carol_vtoken = carol_token_proof
87//!     .verify(carol_vpk, carol_token, first_card, ctx)
88//!     .expect("Carol's token should be valid");
89//!
90//! // Aggregate the verified tokens to decrypt the card
91//! let aggregate_token = AggregateRevealToken::new(&[alice_vtoken, bob_vtoken, carol_vtoken]);
92//!
93//! // Reveal the card's index in the original deck (0-51)
94//! let card_index = shuffle
95//!     .reveal_card(aggregate_token, first_card)
96//!     .expect("Card should be revealed successfully");
97//!
98//! println!("First card is at index: {}", card_index);
99//! assert!(card_index < 52);
100//! ```
101//!
102//! # Serialized Sizes
103//!
104//! The following table shows the serialized sizes of types that need to be transmitted
105//! over the network in a typical mental poker protocol. All sizes use compressed canonical
106//! serialization from [arkworks](https://docs.rs/ark-serialize/0.5.0/ark_serialize/index.html).
107//!
108//! | Type | Size | Notes |
109//! |------|------|-------|
110//! | `PublicKey` | 33 bytes | One per player at setup |
111//! | `OwnershipProof` | 65 bytes | One per player at setup |
112//! | `MaskedDeck<52>` | 3,432 bytes | 66 bytes per card (33 × 2) |
113//! | `ShuffleProof<52>` | 5,547 bytes | One per shuffle operation |
114//! | `RevealToken` | 33 bytes | One per player per revealed card |
115//! | `RevealTokenProof` | 98 bytes | One per player per revealed card |
116#![no_std]
117#![forbid(clippy::all)]
118
119use core::array;
120
121use ark_ec::{AffineRepr, CurveConfig, CurveGroup, short_weierstrass::SWCurveConfig};
122use ark_ff::{
123    Field, UniformRand, Zero,
124    field_hashers::{DefaultFieldHasher, HashToField},
125};
126use ark_serialize::CanonicalSerialize;
127use ark_std::rand::{RngCore as Rng, SeedableRng, rngs::StdRng, seq::SliceRandom};
128use sha2::{Digest, Sha256};
129use zeroize::{Zeroize, ZeroizeOnDrop};
130
131type Curve = ark_secp256k1::Config;
132type CurveAffine = ark_secp256k1::Affine;
133type CurveProj = <CurveAffine as AffineRepr>::Group;
134type Scalar = <Curve as CurveConfig>::ScalarField;
135type Ciphertext = (CurveAffine, CurveAffine);
136
137const GENERATOR: CurveAffine = <Curve as SWCurveConfig>::GENERATOR;
138
139// Deterministic PRNG Seeds
140const OPEN_CARD_PRNG_SEED: &[u8] = b"CARDS-V1";
141const PEDERSON_H_PRNG_SEED: &[u8] = b"PEDERSON-H-V1";
142const PEDERSON_VECTOR_G_PRNG_SEED: &[u8] = b"PEDERSON-VECTOR-G-V1";
143
144#[derive(Debug, Clone, Copy, PartialEq, Eq)]
145struct PedersonCommitment(CurveAffine);
146
147#[derive(Debug, Clone, Copy, PartialEq, Eq)]
148struct PedersonWitness(Scalar);
149
150#[derive(Debug, Clone, Copy)]
151struct PedersonCommitKey<const N: usize> {
152    h: CurveProj,
153    gs: [CurveProj; N],
154}
155
156impl<const N: usize> Default for PedersonCommitKey<N> {
157    fn default() -> Self {
158        let mut h_drng = StdRng::from_seed(Sha256::digest(PEDERSON_H_PRNG_SEED).into());
159        let h = CurveProj::rand(&mut h_drng);
160
161        let mut gs_drng = StdRng::from_seed(Sha256::digest(PEDERSON_VECTOR_G_PRNG_SEED).into());
162        let gs = array::from_fn(|_| CurveProj::rand(&mut gs_drng));
163
164        Self { h, gs }
165    }
166}
167
168impl<const N: usize> PedersonCommitKey<N> {
169    fn commit_with_r(&self, m: Scalar, r: Scalar) -> CurveProj {
170        (GENERATOR * m) + (self.h * r)
171    }
172
173    fn commit<R: Rng>(&self, rng: &mut R, m: Scalar) -> (PedersonCommitment, PedersonWitness) {
174        let r = Scalar::rand(rng);
175        (
176            PedersonCommitment(self.commit_with_r(m, r).into_affine()),
177            PedersonWitness(r),
178        )
179    }
180
181    fn vector_commit_with_r(&self, ms: &[Scalar; N], r: Scalar) -> CurveProj {
182        (0..N).map(|i| self.gs[i] * ms[i]).sum::<CurveProj>() + (self.h * r)
183    }
184
185    fn vector_commit<R: Rng>(
186        &self,
187        rng: &mut R,
188        ms: &[Scalar; N],
189    ) -> (PedersonCommitment, PedersonWitness) {
190        let r = Scalar::rand(rng);
191        (
192            PedersonCommitment(self.vector_commit_with_r(ms, r).into_affine()),
193            PedersonWitness(r),
194        )
195    }
196}
197
198#[derive(Debug, Clone, PartialEq, Eq)]
199struct Transcript([u8; 32]);
200
201impl Transcript {
202    const SERIALIZE_BUFFER_SIZE: usize = 256;
203
204    fn init(user_ctx: &[u8]) -> Self {
205        Self(Sha256::digest(user_ctx).into())
206    }
207
208    fn update_with_serialized<T: CanonicalSerialize>(h: &mut Sha256, label: &str, t: &T) {
209        let mut serialize_buffer = [0u8; Self::SERIALIZE_BUFFER_SIZE];
210        let serialized_size = t.compressed_size();
211        assert!(
212            serialized_size <= Self::SERIALIZE_BUFFER_SIZE,
213            "serialize buffer too small to serialize {label}: {serialized_size} < {}",
214            Self::SERIALIZE_BUFFER_SIZE
215        );
216        t.serialize_compressed(&mut serialize_buffer[..serialized_size])
217            .expect("infallible serialization");
218        h.update(serialized_size.to_be_bytes());
219        h.update(&serialize_buffer[..serialized_size]);
220    }
221
222    fn append<T: CanonicalSerialize>(self, label: &str, t: &T) -> Self {
223        let mut h = Sha256::new();
224        h.update(self.0);
225        h.update(label.len().to_be_bytes());
226        h.update(label.as_bytes());
227        Self::update_with_serialized(&mut h, label, t);
228        Self(h.finalize().into())
229    }
230
231    fn append_vec<T: CanonicalSerialize>(self, label: &str, v: &[T]) -> Self {
232        let mut h = Sha256::new();
233        h.update(self.0);
234        h.update(label.len().to_be_bytes());
235        h.update(label.as_bytes());
236
237        for (i, t) in v.iter().enumerate() {
238            h.update(i.to_be_bytes());
239            Self::update_with_serialized(&mut h, label, t);
240        }
241
242        Self(h.finalize().into())
243    }
244
245    fn derive_challenge_scalars<const N: usize>(&self, dst: &[u8]) -> [Scalar; N] {
246        <DefaultFieldHasher<Sha256> as HashToField<Scalar>>::new(dst).hash_to_field(&self.0)
247    }
248}
249
250/// Secret key for a player. Memory is automatically wiped on drop for security.
251///
252/// The secret key is used to decrypt cards and create reveal tokens.
253/// It must be kept private and never shared with other players.
254#[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
255#[cfg_attr(test, derive(Debug))]
256pub struct SecretKey(Scalar);
257
258/// Wrapper type indicating that a value has been cryptographically verified.
259///
260/// This type provides compile-time guarantees that proofs have been checked
261/// before sensitive operations. Values can only be constructed through
262/// successful verification.
263///
264/// # Examples
265///
266/// ```
267/// use ziffle::Shuffle;
268/// # let mut rng = ark_std::test_rng();
269/// # let shuffle = Shuffle::<10>::default();
270/// # let ctx = b"game";
271///
272/// let (_, pk, proof) = shuffle.keygen(&mut rng, ctx);
273///
274/// // This creates a Verified<PublicKey>
275/// let verified_pk = proof.verify(pk, ctx).unwrap();
276/// ```
277#[derive(Debug, Clone, Copy, PartialEq, Eq)]
278pub struct Verified<T>(T);
279
280/// Public key for a player, derived from their secret key.
281///
282/// Public keys are shared with all players and used to encrypt cards.
283/// The relationship `pk = sk · G` ensures that only the secret key holder
284/// can decrypt cards encrypted with this public key.
285#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
286pub struct PublicKey(CurveAffine);
287
288/// Zero-knowledge proof of secret key ownership.
289///
290/// Demonstrates knowledge of the discrete logarithm (secret key) corresponding
291/// to a public key without revealing the secret key itself. Uses a Schnorr-style
292/// sigma protocol.
293#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
294pub struct OwnershipProof {
295    a: CurveAffine, // commitment
296    z: Scalar,      // response
297}
298
299impl OwnershipProof {
300    const DLOG_DST: &[u8] = b"ziffle/DLOG/v1";
301
302    fn challenge(pk: &PublicKey, a: &CurveAffine, ctx: &[u8]) -> Scalar {
303        let [e] = Transcript::init(ctx)
304            .append("pk", pk)
305            .append("a", a)
306            .derive_challenge_scalars(Self::DLOG_DST);
307        e
308    }
309
310    fn new<R: Rng>(rng: &mut R, sk: &SecretKey, pk: PublicKey, ctx: &[u8]) -> Self {
311        let w = Scalar::rand(rng);
312        let a = (GENERATOR * w).into_affine();
313        let e = Self::challenge(&pk, &a, ctx);
314        let z = w + e * sk.0;
315        OwnershipProof { a, z }
316    }
317
318    /// Verifies the ownership proof for a given public key.
319    ///
320    /// Returns `Some(Verified<PublicKey>)` if the proof is valid, demonstrating
321    /// that the prover knows the secret key corresponding to the public key.
322    ///
323    /// # Arguments
324    ///
325    /// * `pk` - The public key to verify ownership of
326    /// * `ctx` - Context string that was used when creating the proof
327    ///
328    /// # Returns
329    ///
330    /// `Some(Verified<PublicKey>)` if valid, `None` if the proof is invalid.
331    ///
332    /// # Examples
333    ///
334    /// ```
335    /// use ziffle::Shuffle;
336    /// # let mut rng = ark_std::test_rng();
337    /// # let shuffle = Shuffle::<10>::default();
338    /// # let ctx = b"game";
339    ///
340    /// let (_, pk, proof) = shuffle.keygen(&mut rng, ctx);
341    ///
342    /// // Verify the ownership proof
343    /// match proof.verify(pk, ctx) {
344    ///     Some(verified_pk) => println!("Valid ownership proof"),
345    ///     None => println!("Invalid proof"),
346    /// }
347    /// ```
348    #[must_use]
349    pub fn verify(&self, pk: PublicKey, ctx: &[u8]) -> Option<Verified<PublicKey>> {
350        let e = Self::challenge(&pk, &self.a, ctx);
351        // check: z·G == a + e·pk
352        let lhs = (GENERATOR.into_group() * self.z).into_affine();
353        let rhs = (self.a.into_group() + (pk.0 * e)).into_affine();
354        (lhs == rhs).then_some(Verified(pk))
355    }
356}
357
358/// Aggregate public key combining all players' public keys.
359///
360/// The aggregate key is computed as the sum of all verified individual public keys.
361/// Cards encrypted with this key require cooperation from all players to decrypt.
362///
363/// # Examples
364///
365/// ```
366/// use ziffle::{Shuffle, AggregatePublicKey};
367/// # let mut rng = ark_std::test_rng();
368/// # let shuffle = Shuffle::<10>::default();
369/// # let ctx = b"game";
370///
371/// let (_, pk1, proof1) = shuffle.keygen(&mut rng, ctx);
372/// let vpk1 = proof1.verify(pk1, ctx).unwrap();
373///
374/// let (_, pk2, proof2) = shuffle.keygen(&mut rng, ctx);
375/// let vpk2 = proof2.verify(pk2, ctx).unwrap();
376///
377/// // Create aggregate key from verified keys
378/// let apk = AggregatePublicKey::new(&[vpk1, vpk2]);
379/// ```
380#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
381pub struct AggregatePublicKey(CurveAffine);
382
383impl AggregatePublicKey {
384    /// Creates an aggregate public key from verified individual public keys.
385    ///
386    /// All public keys must be verified before aggregation to ensure they
387    /// come from players who proved ownership of their secret keys.
388    ///
389    /// # Arguments
390    ///
391    /// * `pks` - Slice of verified public keys from all players
392    ///
393    /// # Examples
394    ///
395    /// ```
396    /// use ziffle::{Shuffle, AggregatePublicKey};
397    /// # let mut rng = ark_std::test_rng();
398    /// # let shuffle = Shuffle::<10>::default();
399    /// # let ctx = b"game";
400    /// # let (_, pk1, proof1) = shuffle.keygen(&mut rng, ctx);
401    /// # let vpk1 = proof1.verify(pk1, ctx).unwrap();
402    /// # let (_, pk2, proof2) = shuffle.keygen(&mut rng, ctx);
403    /// # let vpk2 = proof2.verify(pk2, ctx).unwrap();
404    ///
405    /// let apk = AggregatePublicKey::new(&[vpk1, vpk2]);
406    /// ```
407    #[must_use]
408    pub fn new(pks: &[Verified<PublicKey>]) -> Self {
409        let apk: CurveProj = pks.iter().map(|pk| pk.0.0).sum();
410        Self(apk.into_affine())
411    }
412}
413
414/// Zero-knowledge proof for a reveal token using a DLEQ (Discrete Log Equality) proof.
415///
416/// Proves that a reveal token was correctly computed as `sk · c1` without revealing
417/// the secret key. Uses a Chaum-Pedersen style protocol.
418#[derive(Debug, Clone, Copy, PartialEq, Eq)]
419pub struct RevealTokenProof {
420    t_g: CurveAffine,
421    t_c1: CurveAffine,
422    z: Scalar,
423}
424
425impl RevealTokenProof {
426    const DLEQ_DST: &[u8] = b"ziffle/DLEQ/v1";
427
428    fn challenge(
429        pk: PublicKey,
430        share: CurveAffine,
431        c1: CurveAffine,
432        t_g: CurveAffine,
433        t_c1: CurveAffine,
434        ctx: &[u8],
435    ) -> Scalar {
436        let [e]: [_; 1] = Transcript::init(ctx)
437            .append("pk", &pk)
438            .append("share", &share)
439            .append("c1", &c1)
440            .append("t_g", &t_g)
441            .append("t_c1", &t_c1)
442            .derive_challenge_scalars(Self::DLEQ_DST);
443        e
444    }
445
446    fn new<R: Rng>(
447        rng: &mut R,
448        sk: Scalar,
449        pk: PublicKey,
450        share: CurveAffine,
451        c1: CurveProj,
452        ctx: &[u8],
453    ) -> Self {
454        let w = Scalar::rand(rng);
455        let t_g = (GENERATOR * w).into_affine();
456        let t_c1 = (c1 * w).into_affine();
457        let e = Self::challenge(pk, share, c1.into_affine(), t_g, t_c1, ctx);
458        let z = w - (e * sk);
459        Self { t_g, t_c1, z }
460    }
461
462    /// Verifies that a reveal token was correctly computed for a specific card.
463    ///
464    /// # Arguments
465    ///
466    /// * `pk` - Verified public key of the player who created the token
467    /// * `token` - The reveal token to verify
468    /// * `card` - The masked card this token is for
469    /// * `ctx` - Context string used when creating the token
470    ///
471    /// # Returns
472    ///
473    /// `Some(Verified<RevealToken>)` if valid, `None` otherwise.
474    #[must_use]
475    pub fn verify(
476        &self,
477        pk: Verified<PublicKey>,
478        token: RevealToken,
479        card: MaskedCard,
480        ctx: &[u8],
481    ) -> Option<Verified<RevealToken>> {
482        let Verified(pk) = pk;
483        let RevealToken(share) = token;
484        let MaskedCard((c1, _)) = card;
485
486        // Step 1: reproducde challenge scalar
487        let e = Self::challenge(pk, share, c1, self.t_g, self.t_c1, ctx);
488
489        // Step 2: chec t_g == g·z + pk·e
490        if self.t_g != ((GENERATOR * self.z) + (pk.0.into_group() * e)).into_affine() {
491            return None;
492        }
493
494        // Step 3: check t_c1 == c1·z + share·e
495        if self.t_c1 != ((c1.into_group() * self.z) + (share.into_group() * e)).into_affine() {
496            return None;
497        }
498
499        Some(Verified(token))
500    }
501}
502
503/// Decryption share for a specific card from one player.
504///
505/// A reveal token is computed as `sk · c1` where `sk` is the player's secret key
506/// and `c1` is part of the encrypted card. All players must provide valid reveal
507/// tokens to decrypt a card.
508#[derive(Debug, Clone, Copy, PartialEq, Eq)]
509pub struct RevealToken(CurveAffine);
510
511/// Aggregate reveal token combining all players' reveal tokens.
512///
513/// Computed as the sum of all verified individual reveal tokens. Used to
514/// decrypt a card by combining all players' decryption shares.
515///
516/// # Examples
517///
518/// ```
519/// use ziffle::{Shuffle, AggregatePublicKey, AggregateRevealToken};
520/// # let mut rng = ark_std::test_rng();
521/// # let shuffle = Shuffle::<10>::default();
522/// # let ctx = b"game";
523/// # let (sk1, pk1, proof1) = shuffle.keygen(&mut rng, ctx);
524/// # let vpk1 = proof1.verify(pk1, ctx).unwrap();
525/// # let (sk2, pk2, proof2) = shuffle.keygen(&mut rng, ctx);
526/// # let vpk2 = proof2.verify(pk2, ctx).unwrap();
527/// # let apk = AggregatePublicKey::new(&[vpk1, vpk2]);
528/// # let (deck, proof) = shuffle.shuffle_initial_deck(&mut rng, apk, ctx);
529/// # let vdeck = shuffle.verify_initial_shuffle(apk, deck, proof, ctx).unwrap();
530/// # let card = vdeck.get(0).unwrap();
531/// # let (rt1, rt_proof1) = card.reveal_token(&mut rng, &sk1, pk1, ctx);
532/// # let (rt2, rt_proof2) = card.reveal_token(&mut rng, &sk2, pk2, ctx);
533///
534/// // Aggregate verified reveal tokens
535/// let art = AggregateRevealToken::new(&[
536///     rt_proof1.verify(vpk1, rt1, card, ctx).unwrap(),
537///     rt_proof2.verify(vpk2, rt2, card, ctx).unwrap(),
538/// ]);
539/// ```
540#[derive(Debug, Clone, Copy, PartialEq, Eq)]
541pub struct AggregateRevealToken(CurveAffine);
542
543impl AggregateRevealToken {
544    /// Creates an aggregate reveal token from verified individual tokens.
545    ///
546    /// All reveal tokens must be verified before aggregation.
547    ///
548    /// # Arguments
549    ///
550    /// * `pks` - Slice of verified reveal tokens from all players
551    #[must_use]
552    pub fn new(pks: &[Verified<RevealToken>]) -> Self {
553        let art: CurveProj = pks.iter().map(|t| t.0.0.into_group()).sum();
554        Self(art.into_affine())
555    }
556}
557
558/// An encrypted card from the deck.
559///
560/// Cards are encrypted using ElGamal encryption with the aggregate public key.
561/// To reveal a card, all players must provide reveal tokens.
562#[derive(Debug, Clone, Copy, PartialEq, Eq)]
563pub struct MaskedCard(Ciphertext);
564
565impl MaskedCard {
566    /// Creates a reveal token and proof for this card.
567    ///
568    /// Each player uses their secret key to create a partial decryption of the card.
569    /// The proof demonstrates that the token was computed correctly without revealing
570    /// the secret key.
571    ///
572    /// # Arguments
573    ///
574    /// * `rng` - Cryptographically secure random number generator
575    /// * `sk` - Player's secret key
576    /// * `pk` - Player's public key
577    /// * `ctx` - Context string to bind the proof
578    ///
579    /// # Returns
580    ///
581    /// A tuple of `(RevealToken, RevealTokenProof)` that other players can verify.
582    ///
583    /// # Examples
584    ///
585    /// ```
586    /// use ziffle::{Shuffle, AggregatePublicKey};
587    /// # let mut rng = ark_std::test_rng();
588    /// # let shuffle = Shuffle::<10>::default();
589    /// # let ctx = b"game";
590    /// # let (sk, pk, proof) = shuffle.keygen(&mut rng, ctx);
591    /// # let vpk = proof.verify(pk, ctx).unwrap();
592    /// # let apk = AggregatePublicKey::new(&[vpk]);
593    /// # let (deck, shuf_proof) = shuffle.shuffle_initial_deck(&mut rng, apk, ctx);
594    /// # let vdeck = shuffle.verify_initial_shuffle(apk, deck, shuf_proof, ctx).unwrap();
595    ///
596    /// let card = vdeck.get(0).unwrap();
597    /// let (token, proof) = card.reveal_token(&mut rng, &sk, pk, ctx);
598    ///
599    /// // Other players verify the token
600    /// let verified_token = proof.verify(vpk, token, card, ctx).unwrap();
601    /// ```
602    pub fn reveal_token<R: Rng>(
603        &self,
604        rng: &mut R,
605        sk: &SecretKey,
606        pk: PublicKey,
607        ctx: &[u8],
608    ) -> (RevealToken, RevealTokenProof) {
609        let SecretKey(sk) = sk;
610        let c1 = self.0.0.into_group();
611        let share = (c1 * sk).into_affine();
612        let proof = RevealTokenProof::new(rng, *sk, pk, share, c1, ctx);
613        (RevealToken(share), proof)
614    }
615}
616
617/// A deck of `N` encrypted cards.
618///
619/// The deck is represented as an array of ElGamal ciphertexts. Cards can only
620/// be accessed from verified decks (obtained after successful shuffle verification).
621#[derive(Debug, Clone, Copy, PartialEq, Eq)]
622pub struct MaskedDeck<const N: usize>([Ciphertext; N]);
623
624impl<const N: usize> Verified<MaskedDeck<N>> {
625    /// Gets a card from the verified deck by index.
626    ///
627    /// # Arguments
628    ///
629    /// * `idx` - Zero-based index of the card (0 to N-1)
630    ///
631    /// # Returns
632    ///
633    /// `Some(MaskedCard)` if the index is valid, `None` if out of bounds.
634    ///
635    /// # Examples
636    ///
637    /// ```
638    /// use ziffle::{Shuffle, AggregatePublicKey};
639    /// # let mut rng = ark_std::test_rng();
640    /// # let shuffle = Shuffle::<10>::default();
641    /// # let ctx = b"game";
642    /// # let (_, pk, proof) = shuffle.keygen(&mut rng, ctx);
643    /// # let vpk = proof.verify(pk, ctx).unwrap();
644    /// # let apk = AggregatePublicKey::new(&[vpk]);
645    /// # let (deck, shuf_proof) = shuffle.shuffle_initial_deck(&mut rng, apk, ctx);
646    /// # let vdeck = shuffle.verify_initial_shuffle(apk, deck, shuf_proof, ctx).unwrap();
647    ///
648    /// // Access cards from the verified deck
649    /// let first_card = vdeck.get(0).unwrap();
650    /// let last_card = vdeck.get(9).unwrap();
651    ///
652    /// // Out of bounds returns None
653    /// assert!(vdeck.get(10).is_none());
654    /// ```
655    pub fn get(&self, idx: usize) -> Option<MaskedCard> {
656        self.0.0.get(idx).copied().map(MaskedCard)
657    }
658}
659
660macro_rules! usize_to_u64 {
661    ($i:expr) => {
662        u64::try_from($i).expect("usize <= 64 bits")
663    };
664}
665
666#[derive(Debug, Clone, Copy, PartialEq, Eq)]
667struct MultiExpArg<const N: usize> {
668    // commitments
669    c_alpha: PedersonCommitment,
670    c_beta: PedersonCommitment,
671    // pre-committed ciphertexts
672    ct_mxp0: (CurveAffine, CurveAffine),
673    ct_mxp1: (CurveAffine, CurveAffine),
674    // response
675    o_alpha: [Scalar; N],
676    o_r: Scalar,
677    beta: Scalar,
678    o_beta: Scalar,
679    tau: Scalar,
680}
681
682// Multi-scalar “product of powers” over a vector of ElGamal ciphertexts, i.e. 𝒞 = (𝒞_1, 𝒞_2), and matching scalars:
683// ∏[𝒞(i)·𝓈(i)] ==> (Σ 𝒞_1[i]·𝓈[i], Σ 𝒞_2[i]·𝓈[i]) for all i in [0; N-1]
684macro_rules! ct_mspp {
685    ($cts:expr, $ss:expr) => {
686        $cts.iter()
687            .zip($ss)
688            .map(|((c1, c2), s)| (c1.into_group() * s, c2.into_group() * s))
689            .reduce(|(c1_acc, c2_acc), (c1, c2)| (c1_acc + c1, c2_acc + c2))
690            .expect("N > 0")
691    };
692}
693
694struct ProveMultiExpArgInputs<'a, const N: usize> {
695    ck: &'a PedersonCommitKey<N>,
696    apk: AggregatePublicKey,
697    xpi: &'a [Scalar; N],
698    w_xpi: PedersonWitness,
699    next: &'a [Ciphertext; N],
700    rho: &'a [Scalar; N],
701    ts: Transcript,
702}
703
704struct VerifyMultiExpArgInputs<'a, const N: usize> {
705    ck: &'a PedersonCommitKey<N>,
706    apk: AggregatePublicKey,
707    prev: &'a [Ciphertext; N],
708    next: &'a [Ciphertext; N],
709    x_base: Scalar,
710    c_xpi: PedersonCommitment,
711    ts: Transcript,
712}
713
714impl<const N: usize> MultiExpArg<N> {
715    const X_DST: &[u8] = b"ziffle/BG12MultiExpArgX/v1";
716
717    fn challenge_x(
718        ts: Transcript,
719        c_alpha: PedersonCommitment,
720        c_beta: PedersonCommitment,
721        ct_mxp0: Ciphertext,
722        ct_mxp1: Ciphertext,
723    ) -> Scalar {
724        let [x] = ts
725            .append("c_alpha", &c_alpha)
726            .append("c_beta", &c_beta)
727            .append("ct_mxp0", &ct_mxp0)
728            .append("ct_mxp1", &ct_mxp1)
729            .derive_challenge_scalars(Self::X_DST);
730        x
731    }
732
733    fn new<R: Rng>(
734        rng: &mut R,
735        ProveMultiExpArgInputs {
736            ck,
737            apk: AggregatePublicKey(pk),
738            xpi,
739            w_xpi: PedersonWitness(w_xpi),
740            next,
741            rho,
742            ts,
743        }: ProveMultiExpArgInputs<N>,
744    ) -> Self {
745        // Step 1: Then we sample a random scalar vector ɑ and scalar β
746        // (`alpha` and `beta` resp.) and commit to them
747        let alpha: [Scalar; N] = array::from_fn(|_| Scalar::rand(rng));
748        let beta = Scalar::rand(rng);
749        let (c_alpha, PedersonWitness(w_alpha)) = ck.vector_commit(rng, &alpha);
750        let (c_beta, PedersonWitness(w_beta)) = ck.commit(rng, beta);
751
752        // Step 2: we create two ciphertexts, blinding 𝒞mxp0 and anchoring 𝒞mxp1 (`ct_mxp{0,1}`).
753        // 𝒞mxp0 = ℰ(G·β; τ0)·∏[𝒞'(i)·ɑ(i)] for all i in [0; N-1] where τ0 (`tau0`) is a random scalar.
754        let tau0 = Scalar::rand(rng);
755        let ct_mxp0 = {
756            let (c1, c2) = ct_mspp!(next, alpha);
757            (
758                ((GENERATOR * tau0) + c1).into_affine(),
759                ((GENERATOR * beta) + (pk * tau0) + c2).into_affine(),
760            )
761        };
762        // 𝒞mxp1 = ℰ(1; ρ_agg)·∏[𝒞'(i)·x^π(i)] for all i in [0; N-1]
763        // where ρ_agg = -𝛴[ρ(i)·x^π(i)] for all i in [0; N-1]
764        let rho_agg: Scalar = -(0..N).map(|i| rho[i] * xpi[i]).sum::<Scalar>();
765        let ct_mxp1 = {
766            let (c1, c2) = ct_mspp!(next, xpi);
767            (
768                ((GENERATOR * rho_agg) + c1).into_affine(),
769                ((pk * rho_agg) + c2).into_affine(),
770            )
771        };
772
773        // Step 3: add the commitments and ciphertexts to the transcript and derive a challenge scalar x
774        let x = Self::challenge_x(ts, c_alpha, c_beta, ct_mxp0, ct_mxp1);
775
776        // Step 4: compute the openings
777        // use the challenge to compute the following witness openings (𝒪) to reveal:
778        // 𝒪ɑ = [ɑ(i) + x·x^π(i)] for all i in [0; N-1]
779        let o_alpha: [Scalar; N] = array::from_fn(|i| alpha[i] + (x * xpi[i]));
780        // 𝒪r = 𝒲 ɑ + x_mxp·𝒲 x^π where 𝒲 ɑ and 𝒲 x^π are the witnesses to commitments to ɑ and x^π
781        let o_r: Scalar = w_alpha + (x * w_xpi);
782        // τ (`tau`) = τ0 + x·ρ_agg
783        let tau = tau0 + (x * rho_agg);
784
785        Self {
786            c_alpha,
787            c_beta,
788            ct_mxp0,
789            ct_mxp1,
790            o_alpha,
791            o_r,
792            beta,
793            o_beta: w_beta,
794            tau,
795        }
796    }
797
798    #[must_use]
799    fn verify(
800        &self,
801        VerifyMultiExpArgInputs {
802            ck,
803            apk: AggregatePublicKey(pk),
804            prev,
805            next,
806            x_base,
807            c_xpi: PedersonCommitment(c_xpi),
808            ts,
809        }: VerifyMultiExpArgInputs<N>,
810    ) -> bool {
811        // Step 1: derive the challenge scalar x_mxp
812        let x = Self::challenge_x(ts, self.c_alpha, self.c_beta, self.ct_mxp0, self.ct_mxp1);
813
814        // Step 2: check that ∏[𝒞(i)·x^(i + 1)] for all i in [0; N-1] == 𝒞mxp1
815        let check1 = || {
816            let xs: [Scalar; N] = array::from_fn(|i| x_base.pow([usize_to_u64!(i + 1)]));
817            let (prod_c1, prod_c2) = ct_mspp!(prev, xs);
818            let (ct_mxp1_c1, ct_mxp1_c2) = self.ct_mxp1;
819            prod_c1.into_affine() == ct_mxp1_c1 && prod_c2.into_affine() == ct_mxp1_c2
820        };
821
822        // Step 3: check x·comm(x^π) + comm(ɑ) == comm(𝒪ɑ; 𝒪r)
823        let check2 = || {
824            let lhs = (c_xpi.into_group() * x) + self.c_alpha.0.into_group();
825            let rhs = ck.vector_commit_with_r(&self.o_alpha, self.o_r);
826            lhs.into_affine() == rhs.into_affine()
827        };
828
829        // Step 4: check that comm(β) == comm(β; 𝒪β)
830        let check3 = || self.c_beta.0 == ck.commit_with_r(self.beta, self.o_beta);
831
832        // Step 5: check that 𝒞mxp0·𝒞mxp1^x == ℰ(G·β; 𝒪τ)·∏[𝒞'(i)·ɑ(i)] for all i in [0; N-1]
833        let check4 = || {
834            let (ct_mxp0_c1, ct_mxp0_c2) = self.ct_mxp0;
835            let (ct_mxp1_c1, ct_mxp1_c2) = self.ct_mxp1;
836            let lhs_c1 = ct_mxp0_c1.into_group() + (ct_mxp1_c1.into_group() * x);
837            let lhs_c2 = ct_mxp0_c2.into_group() + (ct_mxp1_c2.into_group() * x);
838            let (prod_c1, prod_c2) = ct_mspp!(next, self.o_alpha);
839            let rhs_c1 = (GENERATOR * self.tau) + prod_c1;
840            let rhs_c2 = (GENERATOR * self.beta) + (pk * self.tau) + prod_c2;
841            lhs_c1.into_affine() == rhs_c1.into_affine()
842                && lhs_c2.into_affine() == rhs_c2.into_affine()
843        };
844
845        check1() && check2() && check3() && check4()
846    }
847}
848
849#[derive(Debug, Clone, Copy, PartialEq, Eq)]
850struct SingleValueProductArg<const N: usize> {
851    // commitments
852    c_d: PedersonCommitment,
853    c_sdelta: PedersonCommitment,
854    c_cdelta: PedersonCommitment,
855    // response
856    a_tilde: [Scalar; N],
857    b_tilde: [Scalar; N],
858    r_tilde: Scalar,
859    s_tilde: Scalar,
860}
861
862struct ProveSvpArgInputs<'a, const N: usize> {
863    ck: &'a PedersonCommitKey<N>,
864    y: Scalar,
865    z: Scalar,
866    pi: &'a [Scalar; N],
867    xpi: &'a [Scalar; N],
868    w_pi: PedersonWitness,
869    w_xpi: PedersonWitness,
870    ts: Transcript,
871}
872
873struct VerifySvpArgInputs<'a, const N: usize> {
874    ck: &'a PedersonCommitKey<N>,
875    x_base: Scalar,
876    y: Scalar,
877    z: Scalar,
878    c_pi: PedersonCommitment,
879    c_xpi: PedersonCommitment,
880    ts: Transcript,
881}
882
883impl<const N: usize> SingleValueProductArg<N> {
884    const X_DST: &[u8] = b"ziffle/BG12ProductArgX/v1";
885
886    fn challenge_x(
887        ts: Transcript,
888        c_d: PedersonCommitment,
889        c_sdelta: PedersonCommitment,
890        c_cdelta: PedersonCommitment,
891    ) -> Scalar {
892        let [x] = ts
893            .append("c_d", &c_d)
894            .append("c_sdelta", &c_sdelta)
895            .append("c_cdelta", &c_cdelta)
896            .derive_challenge_scalars(Self::X_DST);
897        x
898    }
899
900    fn new<R: Rng>(
901        rng: &mut R,
902        ProveSvpArgInputs {
903            ck,
904            y,
905            z,
906            pi,
907            xpi,
908            w_pi: PedersonWitness(w_pi),
909            w_xpi: PedersonWitness(w_xpi),
910            ts,
911        }: ProveSvpArgInputs<N>,
912    ) -> Self {
913        // Step 1: sample a random scalar vector d and commit to it
914        let d: [Scalar; N] = array::from_fn(|_| Scalar::rand(rng));
915        let (c_d, PedersonWitness(w_d)) = ck.vector_commit(rng, &d);
916
917        // Step 2: create semi-random scalar vector δ (small delta => `sdelta`)
918        // NOTE: semi-random because δ[0] == d[0] & δ[N-1] == 0
919        let mut sdelta: [Scalar; N] = [Scalar::zero(); N];
920        sdelta[0] = d[0];
921        (1..N - 1).for_each(|i| sdelta[i] = Scalar::rand(rng));
922        // Compute [-δ(i)·d(i + 1)] for all i in [0; N-2] and commit to it
923        let (c_sdelta, PedersonWitness(w_sdelta)) = {
924            // NOTE: we have to use a vec of length N because const generic expressions require nightly
925            // Inititalizing the vector with 0 means the last element has no effect when committing.
926            let mut v = [Scalar::zero(); N];
927            (0..N - 1).for_each(|i| v[i] = -sdelta[i] * d[i + 1]);
928            ck.vector_commit(rng, &v)
929        };
930
931        // Step 3: Compute & commit to 𝛥 (capital delta => `cdelta`) over the vector of:
932        // [δ(i + 1) − a(i + 1)·δ(i) − b(i)·d(i + 1)] for all i in [0; N-2] where:
933        // a = [y·π(i) + x^π(i) - z]
934        // b = [a0, b0·a1, ..., bN-2·aN-1] for all i in [0; N-1], i.e. product progression of a
935        let a: [Scalar; N] = array::from_fn(|i| (y * pi[i]) + xpi[i] - z);
936        let mut b = [Scalar::ONE; N];
937        b[0] = a[0];
938        (1..N).for_each(|i| b[i] = b[i - 1] * a[i]);
939        let (c_cdelta, PedersonWitness(w_cdelta)) = {
940            let mut v = [Scalar::zero(); N];
941            (0..N - 1)
942                .for_each(|i| v[i] = sdelta[i + 1] - (a[i + 1] * sdelta[i]) - (b[i] * d[i + 1]));
943            ck.vector_commit(rng, &v)
944        };
945
946        // Step 4: add the commitments to the transcript and derive a challenge scalar x
947        let x = Self::challenge_x(ts, c_d, c_sdelta, c_cdelta);
948
949        // Step 5: compute the responses
950        // a~ = [x·a(i) + d(i)] for all i in [0; N-1]
951        let a_tilde: [Scalar; N] = array::from_fn(|i| (x * a[i]) + d[i]);
952        // b~ = [x·b(i) + δ(i)] for all i in [0; N-1]
953        let b_tilde: [Scalar; N] = array::from_fn(|i| (x * b[i]) + sdelta[i]);
954        // r~ = x·𝒲 a + 𝒲 d where the witness 𝒲 a can be computed from y·𝒲 π + 𝒲 x^π
955        let w_a = (y * w_pi) + w_xpi;
956        let r_tilde: Scalar = (x * w_a) + w_d;
957        // s~ = x·𝒲 𝛥 + 𝒲 δ
958        let s_tilde: Scalar = (x * w_cdelta) + w_sdelta;
959
960        Self {
961            c_d,
962            c_sdelta,
963            c_cdelta,
964            a_tilde,
965            b_tilde,
966            r_tilde,
967            s_tilde,
968        }
969    }
970
971    #[must_use]
972    fn verify(
973        &self,
974        VerifySvpArgInputs {
975            ck,
976            x_base,
977            y,
978            z,
979            c_pi: PedersonCommitment(c_pi),
980            c_xpi: PedersonCommitment(c_xpi),
981            ts,
982        }: VerifySvpArgInputs<N>,
983    ) -> bool {
984        // Step 1: derive the challenge scalar x
985        let x = Self::challenge_x(ts, self.c_d, self.c_sdelta, self.c_cdelta);
986
987        // Step 2: compute the constant vector commit comm([-z; N], 0)
988        let c_mz = ck.vector_commit_with_r(&[-z; N], Scalar::zero());
989
990        // Step 3: homomorphically compute comm(a) = comm(y·π(i) + x^π(i)) for all i in [1; N]
991        let c_a = (c_pi.into_group() * y) + c_xpi.into_group();
992
993        // Step 4: check comm(d) + (comm(a) + comm(-z))·x == comm(a~, r~)
994        let check1 = || {
995            let c_d_dmz = self.c_d.0.into_group() + ((c_a + c_mz) * x);
996            let c_a_tilde_r_tilde = ck.vector_commit_with_r(&self.a_tilde, self.r_tilde);
997            c_d_dmz.into_affine() == c_a_tilde_r_tilde.into_affine()
998        };
999
1000        // Step 5: check comm(δ) + comm(𝛥)·x == comm([x·b~(i + 1) - b~(i)·a~(i + 2)] for all i in [0; N - 2]; s~)
1001        let check2 = || {
1002            let c_sdelta_cdelta = self.c_sdelta.0.into_group() + (self.c_cdelta.0.into_group() * x);
1003            // we have to use a vec of length N because const generic expressions require nightly
1004            // inititalizing the vector with 0 means the last element has no effect when committing
1005            let mut v = [Scalar::zero(); N];
1006            (0..N - 1).for_each(|i| {
1007                v[i] = (x * self.b_tilde[i + 1]) - (self.b_tilde[i] * self.a_tilde[i + 1]);
1008            });
1009            let c_a_tilde_b_tilde = ck.vector_commit_with_r(&v, self.s_tilde);
1010            c_sdelta_cdelta.into_affine() == c_a_tilde_b_tilde.into_affine()
1011        };
1012
1013        // Step 6: check b~[0] == a~[0]
1014        let check3 = || self.b_tilde[0] == self.a_tilde[0];
1015
1016        // Step 7: check b~[N - 1] == x · ∏[y·i + x^i - z] for i in [1; N]
1017        let check4 = || {
1018            let public_prod: Scalar = (1..=N)
1019                .map(|i| {
1020                    (y * Scalar::new(usize_to_u64!(i).into())) + (x_base.pow([usize_to_u64!(i)]))
1021                        - z
1022                })
1023                .product();
1024            self.b_tilde[N - 1] == (x * public_prod)
1025        };
1026
1027        check1() && check2() && check3() && check4()
1028    }
1029}
1030
1031#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1032/// # BayerGroth 2012 (BG12) Efficient Zero-Knowledge Argument for Correctness of a Shuffle
1033///
1034/// <http://www0.cs.ucl.ac.uk/staff/J.Groth/MinimalShuffle.pdf>
1035///
1036/// The prover must convince the verifier that they know a hidden permutation 𝜋
1037/// and a random witness ρ such that the output ciphertext 𝒞' is the
1038/// corresponding input ciphertext 𝒞 but re-ordered and re-masked by adding ElGamal
1039/// encryption function ℰ over message 1 with randomness ρ(i).
1040///
1041/// i.e. 𝒞'\[i\] = 𝒞\[𝜋(i)\]·ℰ(1; ρ(i)) for all i in \[0; N-1\]
1042///
1043/// # Verification
1044///
1045/// To obtain a `Verified<MaskedDeck<N>>` from a shuffle proof, use:
1046/// - [`Shuffle::verify_initial_shuffle`] for the first shuffle
1047/// - [`Shuffle::verify_shuffle`] for subsequent shuffles
1048pub struct ShuffleProof<const N: usize> {
1049    // commitment to permutations
1050    c_pi: PedersonCommitment,
1051    // commitment to vector[x^p[i]] where p are permutations and x is a fiat-shamir challenge
1052    c_xpi: PedersonCommitment,
1053    // multi-exponentiation argument of knowledge
1054    mexp_arg: MultiExpArg<N>,
1055    // product argument of knowledge
1056    prod_arg: SingleValueProductArg<N>,
1057}
1058
1059struct ShuffleProofInputs<'a, const N: usize> {
1060    ck: &'a PedersonCommitKey<N>,
1061    apk: AggregatePublicKey,
1062    perm: &'a [usize; N],
1063    prev: &'a [Ciphertext; N],
1064    next: &'a [Ciphertext; N],
1065    rho: &'a [Scalar; N],
1066    ctx: &'a [u8],
1067}
1068
1069// NOTE: in the paper, N is split into m rows for proof-size optimization, however for simplicity
1070// we construct the proof over 1 row of N ciphertexts, i.e. we set m = 1.
1071//
1072// Also the variable/field naming attempts to follow the paper as closely as possible to make
1073// cross-referencing the implementation to the paper easier.
1074// 𝜋 = `pi`
1075// ρ = `rho`
1076// 𝒞 = `prev`
1077// 𝒞' = `next`
1078impl<const N: usize> ShuffleProof<N> {
1079    const X_DST: &[u8] = b"ziffle/BG12X/v1";
1080    const YZ_DST: &[u8] = b"ziffle/BG12YZ/v1";
1081
1082    fn challenge_x(
1083        apk: AggregatePublicKey,
1084        prev: &[Ciphertext; N],
1085        next: &[Ciphertext; N],
1086        c_pi: PedersonCommitment,
1087        ctx: &[u8],
1088    ) -> (Transcript, Scalar) {
1089        let ts = Transcript::init(ctx)
1090            .append("apk", &apk)
1091            .append_vec("prev", prev)
1092            .append_vec("next", next)
1093            .append("c_pi", &c_pi);
1094        let [x] = ts.derive_challenge_scalars(Self::X_DST);
1095        (ts, x)
1096    }
1097
1098    fn challenge_yz(ts: Transcript, c_xpi: PedersonCommitment) -> (Transcript, Scalar, Scalar) {
1099        let ts = ts.append("c_xpi", &c_xpi);
1100        let [y, z] = ts.derive_challenge_scalars(Self::YZ_DST);
1101        (ts, y, z)
1102    }
1103
1104    fn new<R: Rng>(
1105        rng: &mut R,
1106        ShuffleProofInputs {
1107            ck,
1108            apk,
1109            perm,
1110            prev,
1111            next,
1112            rho,
1113            ctx,
1114        }: ShuffleProofInputs<N>,
1115    ) -> Self {
1116        // Step 1: convert permutation to 1-based scalars and commit to it
1117        let pi: [_; N] = array::from_fn(|i| Scalar::new(usize_to_u64!(perm[i] + 1).into()));
1118        let (c_pi, w_pi) = ck.vector_commit(rng, &pi);
1119
1120        // Step 2: setup the initial transcript and derive challenge x
1121        let (ts, x) = Self::challenge_x(apk, prev, next, c_pi, ctx);
1122
1123        // Step 3: compute and commit to [x^{π(i) + 1}] for all i in [0; N-1]
1124        // NOTE: the paper uses 1-based indices but we use 0-based, to avoid x⁰ = 1 we add 1 to π(i)
1125        let xpi: [Scalar; N] = array::from_fn(|i| x.pow([usize_to_u64!(perm[i]) + 1]));
1126        let (c_xpi, w_xpi) = ck.vector_commit(rng, &xpi);
1127
1128        // Step 4: update the transcipt with the commitment to c_xpi and derive a challenge scalars y & z
1129        let (ts, y, z) = Self::challenge_yz(ts, c_xpi);
1130
1131        // Step 5: compute the multi-exponentiation argument of knowledge
1132        // This proves that every element of 𝒞' is an element of 𝒞 re-masked with ℰ(1; ρ(i)).
1133        // NOTE: The transcript is forked here.
1134        let mexp_arg = MultiExpArg::new(
1135            rng,
1136            ProveMultiExpArgInputs {
1137                ck,
1138                apk,
1139                xpi: &xpi,
1140                w_xpi,
1141                next,
1142                rho,
1143                ts: ts.clone(),
1144            },
1145        );
1146
1147        // Step 6: compute the product argument of knowledge
1148        // This proves that the permutation is valid, i.e. all indexes 0..N-1 are present
1149        // NOTE: As we set m = 1, only the Single Value Product Argument protocol (Section 5.3) is required.
1150        let prod_arg = SingleValueProductArg::new(
1151            rng,
1152            ProveSvpArgInputs {
1153                ck,
1154                y,
1155                z,
1156                pi: &pi,
1157                xpi: &xpi,
1158                w_pi,
1159                w_xpi,
1160                ts,
1161            },
1162        );
1163
1164        Self {
1165            c_pi,
1166            c_xpi,
1167            mexp_arg,
1168            prod_arg,
1169        }
1170    }
1171
1172    #[must_use]
1173    fn verify(
1174        &self,
1175        ck: &PedersonCommitKey<N>,
1176        apk: AggregatePublicKey,
1177        prev: &[Ciphertext; N],
1178        next: &[Ciphertext; N],
1179        ctx: &[u8],
1180    ) -> Option<Verified<MaskedDeck<N>>> {
1181        let (ts, x) = Self::challenge_x(apk, prev, next, self.c_pi, ctx);
1182        let (ts, y, z) = Self::challenge_yz(ts, self.c_xpi);
1183
1184        if !self.mexp_arg.verify(VerifyMultiExpArgInputs {
1185            ck,
1186            apk,
1187            prev,
1188            next,
1189            x_base: x,
1190            c_xpi: self.c_xpi,
1191            ts: ts.clone(),
1192        }) {
1193            return None;
1194        }
1195
1196        if !self.prod_arg.verify(VerifySvpArgInputs {
1197            ck,
1198            x_base: x,
1199            y,
1200            z,
1201            c_pi: self.c_pi,
1202            c_xpi: self.c_xpi,
1203            ts,
1204        }) {
1205            return None;
1206        }
1207
1208        Some(Verified(MaskedDeck(*next)))
1209    }
1210}
1211
1212/// Build a deterministic "open" deck of `N` plaintext cards using a PRNG seeded with a SHA256 hash.
1213/// Each card is `s_i · G` where `s_i` is derived deterministically.
1214fn open_deck<const N: usize>() -> [CurveAffine; N] {
1215    let mut drng = StdRng::from_seed(Sha256::digest(OPEN_CARD_PRNG_SEED).into());
1216    array::from_fn(|_| (GENERATOR * Scalar::rand(&mut drng)).into_affine())
1217}
1218
1219/// applies the differential update
1220/// (c1, c2) <- (c1 + r·G, c2 + r·pk) with fresh r.
1221fn remask_card<R: Rng>(
1222    rng: &mut R,
1223    AggregatePublicKey(pk): AggregatePublicKey,
1224    (c1, c2): Ciphertext,
1225) -> (Ciphertext, Scalar) {
1226    let r = Scalar::rand(rng);
1227    let c1 = c1 + GENERATOR * r;
1228    let c2 = c2 + (pk * r);
1229    ((c1.into_affine(), c2.into_affine()), r)
1230}
1231
1232fn shuffle_remask_prove<const N: usize, R: Rng>(
1233    rng: &mut R,
1234    ck: &PedersonCommitKey<N>,
1235    apk: AggregatePublicKey,
1236    prev: &[Ciphertext; N],
1237    ctx: &[u8],
1238) -> (MaskedDeck<N>, ShuffleProof<N>) {
1239    let mut perm: [usize; N] = array::from_fn(|idx| idx);
1240    perm.as_mut_slice().shuffle(rng);
1241
1242    let next = &mut [(CurveAffine::identity(), CurveAffine::identity()); N];
1243    // remasked randomness witness vector
1244    let rho = &mut [Scalar::zero(); N];
1245    (0..N).for_each(|i| {
1246        let (c, r) = remask_card(rng, apk, prev[perm[i]]);
1247        next[i] = c;
1248        rho[i] = r;
1249    });
1250
1251    let proof = ShuffleProof::new(
1252        rng,
1253        ShuffleProofInputs {
1254            ck,
1255            apk,
1256            perm: &perm,
1257            prev,
1258            next,
1259            rho,
1260            ctx,
1261        },
1262    );
1263
1264    let Verified(next) = proof
1265        .verify(ck, apk, prev, next, ctx)
1266        .expect("invalid shuffle proof");
1267
1268    (next, proof)
1269}
1270
1271/// Mental poker shuffle protocol for `N` cards using Bayer-Groth 2012 shuffle proofs.
1272///
1273/// This struct provides a complete implementation of a mental poker protocol where
1274/// multiple players can collaboratively shuffle a deck of cards without any player
1275/// knowing the order, then reveal individual cards with cryptographic proofs.
1276///
1277/// # Type Parameters
1278///
1279/// * `N` - The number of cards in the deck (must be > 1)
1280///
1281/// # Examples
1282///
1283/// ```
1284/// use ziffle::Shuffle;
1285/// # use ziffle::AggregatePublicKey;
1286/// # let mut rng = ark_std::test_rng();
1287///
1288/// // Create a 52-card deck
1289/// let shuffle = Shuffle::<52>::default();
1290///
1291/// let ctx = b"my_poker_game";
1292///
1293/// // Player 1 generates keys
1294/// let (sk1, pk1, proof1) = shuffle.keygen(&mut rng, ctx);
1295/// let vpk1 = proof1.verify(pk1, ctx).unwrap();
1296///
1297/// // Player 2 generates keys
1298/// let (sk2, pk2, proof2) = shuffle.keygen(&mut rng, ctx);
1299/// let vpk2 = proof2.verify(pk2, ctx).unwrap();
1300///
1301/// // Create aggregate public key
1302/// let apk = AggregatePublicKey::new(&[vpk1, vpk2]);
1303///
1304/// // Player 1 shuffles the initial deck
1305/// let (deck, proof) = shuffle.shuffle_initial_deck(&mut rng, apk, ctx);
1306/// let vdeck = shuffle.verify_initial_shuffle(apk, deck, proof, ctx).unwrap();
1307/// ```
1308#[derive(Debug, Clone, Copy)]
1309pub struct Shuffle<const N: usize> {
1310    commit_key: PedersonCommitKey<N>,
1311    open_deck: [CurveAffine; N],
1312}
1313
1314impl<const N: usize> Default for Shuffle<N> {
1315    fn default() -> Self {
1316        Self {
1317            commit_key: PedersonCommitKey::default(),
1318            open_deck: open_deck(),
1319        }
1320    }
1321}
1322
1323impl<const N: usize> Shuffle<N> {
1324    const _N_GREATER_THAN_1: () = assert!(N > 1);
1325
1326    fn initial_deck(&self) -> [Ciphertext; N] {
1327        array::from_fn(|i| (CurveAffine::identity(), self.open_deck[i]))
1328    }
1329
1330    /// Generates a new keypair and ownership proof for a player.
1331    ///
1332    /// Each player must generate their own secret key, public key, and proof of ownership.
1333    /// The ownership proof demonstrates knowledge of the secret key without revealing it.
1334    ///
1335    /// # Arguments
1336    ///
1337    /// * `rng` - Cryptographically secure random number generator
1338    /// * `ctx` - Context string to bind the proof (e.g., game session ID)
1339    ///
1340    /// # Returns
1341    ///
1342    /// A tuple of `(SecretKey, PublicKey, OwnershipProof)` where the proof can be
1343    /// verified by other players to obtain a `Verified<PublicKey>`.
1344    ///
1345    /// # Examples
1346    ///
1347    /// ```
1348    /// use ziffle::Shuffle;
1349    /// # let mut rng = ark_std::test_rng();
1350    ///
1351    /// let shuffle = Shuffle::<52>::default();
1352    /// let ctx = b"game_session_123";
1353    ///
1354    /// let (secret_key, public_key, ownership_proof) = shuffle.keygen(&mut rng, ctx);
1355    ///
1356    /// // Verify the ownership proof
1357    /// let verified_pk = ownership_proof.verify(public_key, ctx).unwrap();
1358    /// ```
1359    #[must_use]
1360    pub fn keygen<R: Rng>(
1361        &self,
1362        rng: &mut R,
1363        ctx: &[u8],
1364    ) -> (SecretKey, PublicKey, OwnershipProof) {
1365        let sk = Scalar::rand(rng);
1366        let pk = GENERATOR * sk;
1367        let (sk, pk) = (SecretKey(sk), PublicKey(pk.into_affine()));
1368        let proof = OwnershipProof::new(rng, &sk, pk, ctx);
1369        (sk, pk, proof)
1370    }
1371
1372    /// Performs the first shuffle of the deck with a zero-knowledge proof.
1373    ///
1374    /// The first player shuffles the initial (unencrypted) deck and encrypts it with
1375    /// the aggregate public key. A zero-knowledge proof is generated to demonstrate
1376    /// that the shuffle was performed correctly without revealing the permutation.
1377    ///
1378    /// # Arguments
1379    ///
1380    /// * `rng` - Cryptographically secure random number generator
1381    /// * `apk` - Aggregate public key from all players
1382    /// * `ctx` - Context string to bind the proof
1383    ///
1384    /// # Returns
1385    ///
1386    /// A tuple of `(MaskedDeck, ShuffleProof)` that must be verified by all players
1387    /// using [`verify_initial_shuffle`](Self::verify_initial_shuffle).
1388    ///
1389    /// # Examples
1390    ///
1391    /// ```
1392    /// use ziffle::{Shuffle, AggregatePublicKey};
1393    /// # let mut rng = ark_std::test_rng();
1394    ///
1395    /// let shuffle = Shuffle::<10>::default();
1396    /// let ctx = b"game_session";
1397    ///
1398    /// // Two players generate keys
1399    /// let (_, pk1, proof1) = shuffle.keygen(&mut rng, ctx);
1400    /// let vpk1 = proof1.verify(pk1, ctx).unwrap();
1401    ///
1402    /// let (_, pk2, proof2) = shuffle.keygen(&mut rng, ctx);
1403    /// let vpk2 = proof2.verify(pk2, ctx).unwrap();
1404    ///
1405    /// let apk = AggregatePublicKey::new(&[vpk1, vpk2]);
1406    ///
1407    /// // First player shuffles
1408    /// let (deck, proof) = shuffle.shuffle_initial_deck(&mut rng, apk, ctx);
1409    ///
1410    /// // All players verify
1411    /// let verified_deck = shuffle.verify_initial_shuffle(apk, deck, proof, ctx).unwrap();
1412    /// ```
1413    #[must_use]
1414    pub fn shuffle_initial_deck<R: Rng>(
1415        &self,
1416        rng: &mut R,
1417        apk: AggregatePublicKey,
1418        ctx: &[u8],
1419    ) -> (MaskedDeck<N>, ShuffleProof<N>) {
1420        shuffle_remask_prove(rng, &self.commit_key, apk, &self.initial_deck(), ctx)
1421    }
1422
1423    /// Shuffles an already-shuffled deck with a zero-knowledge proof.
1424    ///
1425    /// Subsequent players can shuffle the deck again to add their own randomness.
1426    /// Each shuffle operation re-encrypts and permutes the deck while generating
1427    /// a proof that the shuffle was performed correctly.
1428    ///
1429    /// # Arguments
1430    ///
1431    /// * `rng` - Cryptographically secure random number generator
1432    /// * `apk` - Aggregate public key from all players
1433    /// * `prev` - Previously verified deck state
1434    /// * `ctx` - Context string to bind the proof
1435    ///
1436    /// # Returns
1437    ///
1438    /// A tuple of `(MaskedDeck, ShuffleProof)` that must be verified by all players
1439    /// using [`verify_shuffle`](Self::verify_shuffle).
1440    ///
1441    /// # Examples
1442    ///
1443    /// ```
1444    /// use ziffle::{Shuffle, AggregatePublicKey};
1445    /// # let mut rng = ark_std::test_rng();
1446    /// # let shuffle = Shuffle::<10>::default();
1447    /// # let ctx = b"game";
1448    /// # let (_, pk1, proof1) = shuffle.keygen(&mut rng, ctx);
1449    /// # let vpk1 = proof1.verify(pk1, ctx).unwrap();
1450    /// # let (_, pk2, proof2) = shuffle.keygen(&mut rng, ctx);
1451    /// # let vpk2 = proof2.verify(pk2, ctx).unwrap();
1452    /// # let apk = AggregatePublicKey::new(&[vpk1, vpk2]);
1453    /// # let (deck, proof) = shuffle.shuffle_initial_deck(&mut rng, apk, ctx);
1454    /// # let vdeck = shuffle.verify_initial_shuffle(apk, deck, proof, ctx).unwrap();
1455    ///
1456    /// // Player 2 shuffles the already-shuffled deck
1457    /// let (deck2, proof2) = shuffle.shuffle_deck(&mut rng, apk, &vdeck, ctx);
1458    /// let vdeck2 = shuffle.verify_shuffle(apk, &vdeck, deck2, proof2, ctx).unwrap();
1459    /// ```
1460    #[must_use]
1461    pub fn shuffle_deck<R: Rng>(
1462        &self,
1463        rng: &mut R,
1464        apk: AggregatePublicKey,
1465        prev: &Verified<MaskedDeck<N>>,
1466        ctx: &[u8],
1467    ) -> (MaskedDeck<N>, ShuffleProof<N>) {
1468        shuffle_remask_prove(rng, &self.commit_key, apk, &prev.0.0, ctx)
1469    }
1470
1471    /// Verifies the initial shuffle proof and returns a verified deck.
1472    ///
1473    /// All players must verify the initial shuffle before proceeding. Verification
1474    /// ensures that the deck was shuffled correctly and all cards are present.
1475    ///
1476    /// # Arguments
1477    ///
1478    /// * `apk` - Aggregate public key from all players
1479    /// * `next` - The shuffled deck to verify
1480    /// * `proof` - Zero-knowledge shuffle proof
1481    /// * `ctx` - Context string used during shuffle
1482    ///
1483    /// # Returns
1484    ///
1485    /// `Some(Verified<MaskedDeck>)` if the proof is valid, `None` otherwise.
1486    ///
1487    /// # Examples
1488    ///
1489    /// ```
1490    /// use ziffle::{Shuffle, AggregatePublicKey};
1491    /// # let mut rng = ark_std::test_rng();
1492    /// # let shuffle = Shuffle::<10>::default();
1493    /// # let ctx = b"game";
1494    /// # let (_, pk1, proof1) = shuffle.keygen(&mut rng, ctx);
1495    /// # let vpk1 = proof1.verify(pk1, ctx).unwrap();
1496    /// # let (_, pk2, proof2) = shuffle.keygen(&mut rng, ctx);
1497    /// # let vpk2 = proof2.verify(pk2, ctx).unwrap();
1498    /// # let apk = AggregatePublicKey::new(&[vpk1, vpk2]);
1499    /// # let (deck, proof) = shuffle.shuffle_initial_deck(&mut rng, apk, ctx);
1500    ///
1501    /// // Verify returns None if proof is invalid
1502    /// match shuffle.verify_initial_shuffle(apk, deck, proof, ctx) {
1503    ///     Some(verified_deck) => println!("Shuffle verified!"),
1504    ///     None => println!("Invalid shuffle proof"),
1505    /// }
1506    /// ```
1507    #[must_use]
1508    pub fn verify_initial_shuffle(
1509        &self,
1510        apk: AggregatePublicKey,
1511        next: MaskedDeck<N>,
1512        proof: ShuffleProof<N>,
1513        ctx: &[u8],
1514    ) -> Option<Verified<MaskedDeck<N>>> {
1515        proof.verify(&self.commit_key, apk, &self.initial_deck(), &next.0, ctx)
1516    }
1517
1518    /// Verifies a subsequent shuffle proof and returns a verified deck.
1519    ///
1520    /// All players must verify each shuffle operation before proceeding. Verification
1521    /// ensures that the re-shuffle was performed correctly.
1522    ///
1523    /// # Arguments
1524    ///
1525    /// * `apk` - Aggregate public key from all players
1526    /// * `prev` - Previously verified deck state
1527    /// * `next` - The newly shuffled deck to verify
1528    /// * `proof` - Zero-knowledge shuffle proof
1529    /// * `ctx` - Context string used during shuffle
1530    ///
1531    /// # Returns
1532    ///
1533    /// `Some(Verified<MaskedDeck>)` if the proof is valid, `None` otherwise.
1534    ///
1535    /// # Examples
1536    ///
1537    /// ```
1538    /// use ziffle::{Shuffle, AggregatePublicKey};
1539    /// # let mut rng = ark_std::test_rng();
1540    /// # let shuffle = Shuffle::<10>::default();
1541    /// # let ctx = b"game";
1542    /// # let (_, pk1, proof1) = shuffle.keygen(&mut rng, ctx);
1543    /// # let vpk1 = proof1.verify(pk1, ctx).unwrap();
1544    /// # let (_, pk2, proof2) = shuffle.keygen(&mut rng, ctx);
1545    /// # let vpk2 = proof2.verify(pk2, ctx).unwrap();
1546    /// # let apk = AggregatePublicKey::new(&[vpk1, vpk2]);
1547    /// # let (deck, proof) = shuffle.shuffle_initial_deck(&mut rng, apk, ctx);
1548    /// # let vdeck = shuffle.verify_initial_shuffle(apk, deck, proof, ctx).unwrap();
1549    /// # let (deck2, proof2) = shuffle.shuffle_deck(&mut rng, apk, &vdeck, ctx);
1550    ///
1551    /// // Verify the second shuffle
1552    /// match shuffle.verify_shuffle(apk, &vdeck, deck2, proof2, ctx) {
1553    ///     Some(verified_deck) => println!("Re-shuffle verified!"),
1554    ///     None => println!("Invalid shuffle proof"),
1555    /// }
1556    /// ```
1557    #[must_use]
1558    pub fn verify_shuffle(
1559        &self,
1560        apk: AggregatePublicKey,
1561        prev: &Verified<MaskedDeck<N>>,
1562        next: MaskedDeck<N>,
1563        proof: ShuffleProof<N>,
1564        ctx: &[u8],
1565    ) -> Option<Verified<MaskedDeck<N>>> {
1566        proof.verify(&self.commit_key, apk, &prev.0.0, &next.0, ctx)
1567    }
1568
1569    /// Reveals a card using an aggregate reveal token from all players.
1570    ///
1571    /// After all players have created and verified reveal tokens for a specific card,
1572    /// the tokens are aggregated and used to decrypt the card. The card index in the
1573    /// original deck is returned.
1574    ///
1575    /// # Arguments
1576    ///
1577    /// * `art` - Aggregate reveal token from all players
1578    /// * `card` - The masked card to reveal
1579    ///
1580    /// # Returns
1581    ///
1582    /// `Some(usize)` with the card's index in the original deck (0-based),
1583    /// or `None` if the card cannot be revealed (wrong tokens or corrupted card).
1584    ///
1585    /// # Examples
1586    ///
1587    /// ```
1588    /// use ziffle::{Shuffle, AggregatePublicKey, AggregateRevealToken};
1589    /// # let mut rng = ark_std::test_rng();
1590    /// # let shuffle = Shuffle::<10>::default();
1591    /// # let ctx = b"game";
1592    /// # let (sk1, pk1, proof1) = shuffle.keygen(&mut rng, ctx);
1593    /// # let vpk1 = proof1.verify(pk1, ctx).unwrap();
1594    /// # let (sk2, pk2, proof2) = shuffle.keygen(&mut rng, ctx);
1595    /// # let vpk2 = proof2.verify(pk2, ctx).unwrap();
1596    /// # let apk = AggregatePublicKey::new(&[vpk1, vpk2]);
1597    /// # let (deck, proof) = shuffle.shuffle_initial_deck(&mut rng, apk, ctx);
1598    /// # let vdeck = shuffle.verify_initial_shuffle(apk, deck, proof, ctx).unwrap();
1599    ///
1600    /// let card = vdeck.get(0).unwrap();
1601    ///
1602    /// // Each player creates a reveal token
1603    /// let (rt1, rt_proof1) = card.reveal_token(&mut rng, &sk1, pk1, ctx);
1604    /// let (rt2, rt_proof2) = card.reveal_token(&mut rng, &sk2, pk2, ctx);
1605    ///
1606    /// // Verify and aggregate tokens
1607    /// let art = AggregateRevealToken::new(&[
1608    ///     rt_proof1.verify(vpk1, rt1, card, ctx).unwrap(),
1609    ///     rt_proof2.verify(vpk2, rt2, card, ctx).unwrap(),
1610    /// ]);
1611    ///
1612    /// // Reveal the card
1613    /// if let Some(card_index) = shuffle.reveal_card(art, card) {
1614    ///     println!("Card is at index {}", card_index);
1615    /// }
1616    /// ```
1617    #[must_use]
1618    pub fn reveal_card(&self, art: AggregateRevealToken, card: MaskedCard) -> Option<usize> {
1619        let AggregateRevealToken(art) = art;
1620        let MaskedCard((_, c2)) = card;
1621        // m = c2 − Σ[sk(i)·c1] for all i in [0; n - 1]
1622        let pt = (c2.into_group() - art.into_group()).into_affine();
1623        self.open_deck.iter().position(|&oc| oc == pt)
1624    }
1625}
1626
1627macro_rules! impl_valid_and_serde_unit {
1628    (@impl_check) => {
1629        fn check(&self) -> Result<(), ark_serialize::SerializationError> {
1630            self.0.check()
1631        }
1632    };
1633
1634    (@impl_ser) => {
1635        fn serialize_with_mode<W: ark_serialize::Write>(
1636            &self,
1637            writer: W,
1638            compress: ark_serialize::Compress,
1639        ) -> Result<(), ark_serialize::SerializationError> {
1640            self.0.serialize_with_mode(writer, compress)
1641        }
1642
1643        fn serialized_size(&self, compress: ark_serialize::Compress) -> usize {
1644            self.0.serialized_size(compress)
1645        }
1646    };
1647
1648    (@impl_deser) => {
1649        fn deserialize_with_mode<R: ark_serialize::Read>(
1650            reader: R,
1651            compress: ark_serialize::Compress,
1652            validate: ark_serialize::Validate,
1653        ) -> Result<Self, ark_serialize::SerializationError> {
1654            ark_serialize::CanonicalDeserialize::deserialize_with_mode(reader, compress, validate).map(Self)
1655        }
1656    };
1657
1658    ($t:tt< $N:ident >) => {
1659        impl<const $N: usize> ark_serialize::Valid for $t<$N> {
1660            impl_valid_and_serde_unit!(@impl_check );
1661        }
1662
1663        impl<const $N: usize> ark_serialize::CanonicalSerialize for $t<$N> {
1664            impl_valid_and_serde_unit!(@impl_ser );
1665        }
1666
1667        impl<const $N: usize> ark_serialize::CanonicalDeserialize for $t<$N> {
1668            impl_valid_and_serde_unit!(@impl_deser);
1669        }
1670    };
1671
1672    ($t:ty) => {
1673        impl ark_serialize::Valid for $t {
1674            impl_valid_and_serde_unit!(@impl_check );
1675        }
1676
1677        impl ark_serialize::CanonicalSerialize for $t {
1678            impl_valid_and_serde_unit!(@impl_ser );
1679        }
1680
1681        impl ark_serialize::CanonicalDeserialize for $t {
1682            impl_valid_and_serde_unit!(@impl_deser);
1683        }
1684    };
1685}
1686
1687impl_valid_and_serde_unit!(PublicKey);
1688impl_valid_and_serde_unit!(SecretKey);
1689impl_valid_and_serde_unit!(PedersonCommitment);
1690impl_valid_and_serde_unit!(RevealToken);
1691impl_valid_and_serde_unit!(MaskedDeck<N>);
1692
1693// only implement serialize so it can only be constructed from verified public keys
1694impl ark_serialize::CanonicalSerialize for AggregatePublicKey {
1695    impl_valid_and_serde_unit!(@impl_ser );
1696}
1697
1698macro_rules! impl_valid_and_deser {
1699    (@impl_check $($field:ident),+) => {
1700        fn check(&self) -> Result<(), ark_serialize::SerializationError> {
1701            $( ark_serialize::Valid::check(&self.$field)?; )*
1702            Ok(())
1703        }
1704    };
1705
1706    (@impl_ser $($field:ident),+) => {
1707        fn serialize_with_mode<W: ark_serialize::Write>(
1708            &self,
1709            mut writer: W,
1710            compress: ark_serialize::Compress,
1711        ) -> Result<(), ark_serialize::SerializationError> {
1712            $( self.$field.serialize_with_mode(&mut writer, compress)?; )*
1713            Ok(())
1714        }
1715
1716        fn serialized_size(&self, compress: ark_serialize::Compress) -> usize {
1717            [
1718                $( self.$field.serialized_size(compress), )*
1719            ].iter().sum()
1720        }
1721    };
1722
1723    (@impl_deser $($field:ident),+) => {
1724        fn deserialize_with_mode<R: ark_serialize::Read>(
1725            mut reader: R,
1726            compress: ark_serialize::Compress,
1727            validate: ark_serialize::Validate,
1728        ) -> Result<Self, ark_serialize::SerializationError> {
1729            Ok(Self {
1730                $( $field: ark_serialize::CanonicalDeserialize::deserialize_with_mode(
1731                    &mut reader, compress, validate
1732                )?, )*
1733            })
1734        }
1735    };
1736
1737    ($t:tt< $N:ident > { $($field:ident),+ }) => {
1738        impl<const $N: usize> ark_serialize::Valid for $t<$N> {
1739            impl_valid_and_deser!(@impl_check $($field),*);
1740        }
1741
1742        impl<const $N: usize> ark_serialize::CanonicalSerialize for $t<$N> {
1743            impl_valid_and_deser!(@impl_ser $($field),*);
1744        }
1745
1746        impl<const $N: usize> ark_serialize::CanonicalDeserialize for $t<$N> {
1747            impl_valid_and_deser!(@impl_deser $($field),*);
1748        }
1749    };
1750
1751    ($t:ty { $($field:ident),+ }) => {
1752        impl ark_serialize::Valid for $t {
1753            impl_valid_and_deser!(@impl_check $($field),*);
1754        }
1755
1756        impl ark_serialize::CanonicalSerialize for $t {
1757            impl_valid_and_deser!(@impl_ser $($field),*);
1758        }
1759
1760        impl ark_serialize::CanonicalDeserialize for $t {
1761            impl_valid_and_deser!(@impl_deser $($field),*);
1762        }
1763    };
1764}
1765
1766impl_valid_and_deser!(MultiExpArg<N> {
1767    c_alpha, c_beta, ct_mxp0, ct_mxp1, o_alpha, o_r, beta, o_beta, tau
1768});
1769impl_valid_and_deser!(SingleValueProductArg<N> {
1770    c_d, c_sdelta, c_cdelta, a_tilde, b_tilde, r_tilde, s_tilde
1771});
1772impl_valid_and_deser!(ShuffleProof<N> {
1773    c_pi, c_xpi, mexp_arg, prod_arg
1774});
1775impl_valid_and_deser!(RevealTokenProof { t_g, t_c1, z });
1776impl_valid_and_deser!(OwnershipProof { a, z });
1777
1778#[cfg(test)]
1779mod test;