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;