Skip to main content

paillier_zk/
paillier_affine_operation_in_range.rs

1//! ZK-proof of paillier operation with group commitment in range. Called Пaff-g
2//! or Raff-g in the CGGMP24 paper.
3//!
4//! ## Description
5//!
6//! A party P performs a paillier affine operation with C, Y, and X
7//! obtaining `D = C*X + Y`. `X` and `Y` are encrypted values of `x` and `y`. P
8//! then wants to prove that `y` and `x` are at most `L` and `L'` bits,
9//! correspondingly, and P doesn't want to disclose none of the plaintexts
10//!
11//! Given:
12//! - `key0`, `pkey0`, `key1`, `pkey1` - pairs of public and private keys in
13//!   paillier cryptosystem
14//! - `nonce_y`, `nonce` - nonces in paillier encryption
15//! - `x`, `y` - some numbers
16//! - `q`, `g` such that `<g> = Zq*` - prime order group
17//! - `C` is some ciphertext encrypted by `key0`
18//! - `Y = key1.encrypt(y, nonce_y)`
19//! - `X = g * x`
20//! - `D = oadd(enc(y, nonce), omul(x, C))` where `enc`, `oadd` and `omul` are
21//!   paillier encryption, homomorphic addition and multiplication with `key0`
22//!
23//! Prove:
24//! - `bitsize(abs(x)) <= l_x`
25//! - `bitsize(abs(y)) <= l_y`
26//!
27//! Disclosing only: `key0`, `key1`, `C`, `D`, `Y`, `X`
28//!
29//! ## Example
30//!
31//! ```rust
32//! use paillier_zk::{paillier_affine_operation_in_range as p, IntegerExt};
33//! use fast_paillier::backend::Integer;
34//! use generic_ec::{Point, curves::Secp256k1 as E};
35//! # mod pregenerated {
36//! #     use super::*;
37//! #     paillier_zk::load_pregenerated_data!(
38//! #         verifier_aux: p::Aux,
39//! #         someone_encryption_key0: fast_paillier::EncryptionKey,
40//! #         someone_encryption_key1: fast_paillier::EncryptionKey,
41//! #     );
42//! # }
43//!
44//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
45//! // Prover and verifier have a shared protocol state
46//! let shared_state = "some shared state";
47//!
48//! let mut rng = rand_core::OsRng;
49//! # let mut rng = rand_dev::DevRng::new();
50//!
51//! // 0. Setup: prover and verifier share common Ring-Pedersen parameters:
52//!
53//! let aux: p::Aux = pregenerated::verifier_aux();
54//! let security = p::SecurityParams {
55//!     l_x: 256,
56//!     l_y: 256 * 5,
57//!     epsilon: 256 * 2,
58//! };
59//!
60//! // 1. Setup: prover prepares the paillier keys
61//!
62//! // C and D are encrypted by this key
63//! let key_j: fast_paillier::EncryptionKey = pregenerated::someone_encryption_key0();
64//! // Y is encrypted using this key
65//! let key_i: fast_paillier::EncryptionKey = pregenerated::someone_encryption_key1();
66//!
67//! // C is some number encrypted using key_j. Neither of parties
68//! // need to know the plaintext
69//! let ciphertext_c = Integer::sample_in_mult_group_of(&mut rng, &key_j.nn());
70//!
71//! // 2. Setup: prover prepares all plaintexts
72//!
73//! // x in paper
74//! let plaintext_x = Integer::from_rng_half_pm(
75//!     &mut rng,
76//!     &(Integer::one() << security.l_x),
77//! );
78//! // y in paper
79//! let plaintext_y = Integer::from_rng_half_pm(
80//!     &mut rng,
81//!     &(Integer::one() << security.l_y),
82//! );
83//!
84//! // 3. Setup: prover encrypts everything on correct keys and remembers some nonces
85//!
86//! // X in paper
87//! let ciphertext_x = Point::<E>::generator() * plaintext_x.to_scalar();
88//! // Y and ρ_y in paper
89//! let (ciphertext_y, nonce_y) = key_i.encrypt_with_random(
90//!     &mut rng,
91//!     &(plaintext_y),
92//! )?;
93//! // nonce is ρ in paper
94//! let (ciphertext_y_by_key_j, nonce) = key_j.encrypt_with_random(
95//!     &mut rng,
96//!     &(plaintext_y)
97//! )?;
98//! // D in paper
99//! let ciphertext_d = key_j
100//!     .oadd(
101//!         &key_j.omul(&plaintext_x, &ciphertext_c)?,
102//!         &ciphertext_y_by_key_j,
103//!     )?;
104//!
105//! // 4. Prover computes a non-interactive proof that plaintext_x and
106//! //    plaintext_y are at most `l_x` and `l_y` bits
107//!
108//! let data = p::Data {
109//!     key_j: &key_j,
110//!     key_i: &key_i,
111//!     c: &ciphertext_c,
112//!     d: &ciphertext_d,
113//!     x: &ciphertext_x,
114//!     y: &ciphertext_y,
115//! };
116//! let pdata = p::PrivateData {
117//!     x: &plaintext_x,
118//!     y: &plaintext_y,
119//!     nonce: &nonce,
120//!     nonce_y: &nonce_y,
121//! };
122//! let proof =
123//!     p::non_interactive::prove::<E, sha2::Sha256>(
124//!         &shared_state,
125//!         &aux,
126//!         data,
127//!         pdata,
128//!         &security,
129//!         &mut rng,
130//!     )?;
131//!
132//! // 5. Prover sends this data to verifier
133//!
134//! # use generic_ec::Curve;
135//! # fn send<E: Curve>(_: &p::Data<E>, _: &p::NiProof<E>) {  }
136//! send(&data, &proof);
137//!
138//! // 6. Verifier receives the data and the proof and verifies it
139//!
140//! # let recv = || (data, proof);
141//! let (data, proof) = recv();
142//! let r = p::non_interactive::verify::<E, sha2::Sha256>(
143//!     &shared_state,
144//!     &aux,
145//!     data,
146//!     &security,
147//!     &proof,
148//! )?;
149//! #
150//! # Ok(()) }
151//! ```
152//!
153//! If the verification succeeded, verifier can continue communication with prover
154
155use fast_paillier::backend::Integer;
156use fast_paillier::{AnyEncryptionKey, Ciphertext, Nonce};
157use generic_ec::{Curve, Point};
158
159#[cfg(feature = "serde")]
160use serde::{Deserialize, Serialize};
161
162pub use crate::common::{Aux, InvalidProof};
163
164/// Security parameters for proof. Choosing the values is a tradeoff between
165/// speed and chance of rejecting a valid proof or accepting an invalid proof
166#[derive(Debug, Clone, udigest::Digestable)]
167#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
168pub struct SecurityParams {
169    /// l in paper, bit size of +-x
170    pub l_x: usize,
171    /// l' in paper, bit size of +-y
172    pub l_y: usize,
173    /// Epsilon in paper, slackness parameter
174    pub epsilon: usize,
175}
176
177/// Public data that both parties know
178#[derive(Debug, Clone, Copy, udigest::Digestable)]
179#[udigest(bound = "")]
180pub struct Data<'a, C: Curve> {
181    /// Nj in the spec, public key that C was encrypted on
182    #[udigest(as = crate::common::encoding::AnyEncryptionKey)]
183    pub key_j: &'a dyn AnyEncryptionKey,
184    /// Ni in the spec, public key that y -> Y was encrypted on
185    #[udigest(as = crate::common::encoding::AnyEncryptionKey)]
186    pub key_i: &'a dyn AnyEncryptionKey,
187    /// C in the spec, some data encrypted on Nj
188    #[udigest(as = &crate::common::encoding::Integer)]
189    pub c: &'a Ciphertext,
190    /// D in the spec, result of affine transformation of C with x and y
191    #[udigest(as = &crate::common::encoding::Integer)]
192    pub d: &'a Integer,
193    /// Y in the spec, y encrypted on Ni
194    #[udigest(as = &crate::common::encoding::Integer)]
195    pub y: &'a Ciphertext,
196    /// X in the spec, obtained as `x G`
197    pub x: &'a Point<C>,
198}
199
200/// Private data of prover
201#[derive(Clone, Copy)]
202pub struct PrivateData<'a> {
203    /// x in the spec, preimage of X
204    pub x: &'a Integer,
205    /// y in the spec, preimage of Y
206    pub y: &'a Integer,
207    /// rho in the spec, nonce in encryption of y for additive action
208    pub nonce: &'a Nonce,
209    /// rho_y in the spec, nonce in encryption of y to obtain Y
210    pub nonce_y: &'a Nonce,
211}
212
213/// Prover's first message, obtained by [`interactive::commit`]
214#[derive(Debug, Clone, udigest::Digestable)]
215#[udigest(bound = "")]
216#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(bound = ""))]
217pub struct Commitment<C: Curve> {
218    #[udigest(as = crate::common::encoding::Integer)]
219    pub a: Integer,
220    pub b_x: Point<C>,
221    #[udigest(as = crate::common::encoding::Integer)]
222    pub b_y: Integer,
223    #[udigest(as = crate::common::encoding::Integer)]
224    pub e: Integer,
225    #[udigest(as = crate::common::encoding::Integer)]
226    pub s: Integer,
227    #[udigest(as = crate::common::encoding::Integer)]
228    pub f: Integer,
229    #[udigest(as = crate::common::encoding::Integer)]
230    pub t: Integer,
231}
232
233/// Prover's data accompanying the commitment. Kept as state between rounds in
234/// the interactive protocol.
235#[derive(Clone)]
236pub struct PrivateCommitment {
237    pub alpha: Integer,
238    pub beta: Integer,
239    pub r: Integer,
240    pub r_y: Integer,
241    pub gamma: Integer,
242    pub delta: Integer,
243    pub m: Integer,
244    pub mu: Integer,
245}
246
247/// Verifier's challenge to prover. Can be obtained deterministically by
248/// [`non_interactive::challenge`] or randomly by [`interactive::challenge`]
249pub type Challenge = Integer;
250
251/// The ZK proof. Computed by [`interactive::prove`].
252#[derive(Debug, Clone)]
253#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
254pub struct Proof {
255    pub z1: Integer,
256    pub z2: Integer,
257    pub z3: Integer,
258    pub z4: Integer,
259    pub w: Integer,
260    pub w_y: Integer,
261}
262
263/// The non-interactive ZK proof. Computed by [`non_interactive::prove`].
264/// Combines commitment and proof.
265#[derive(Debug, Clone)]
266#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
267#[cfg_attr(feature = "serde", serde(bound = ""))]
268pub struct NiProof<C: Curve> {
269    pub commitment: Commitment<C>,
270    pub proof: Proof,
271}
272
273/// The interactive version of the ZK proof. Should be completed in 3 rounds:
274/// prover commits to data, verifier responds with a random challenge, and
275/// prover gives proof with commitment and challenge.
276pub mod interactive {
277    use fast_paillier::backend::Integer;
278    use generic_ec::{Curve, Point};
279    use rand_core::RngCore;
280
281    use crate::common::{fail_if, fail_if_ne, IntegerExt, InvalidProof, InvalidProofReason};
282    use crate::Error;
283
284    use super::*;
285
286    /// Create random commitment
287    pub fn commit<C: Curve, R: RngCore>(
288        aux: &Aux,
289        data: Data<C>,
290        pdata: PrivateData,
291        security: &SecurityParams,
292        mut rng: R,
293    ) -> Result<(Commitment<C>, PrivateCommitment), Error> {
294        let two_to_l = Integer::one() << security.l_x;
295        let two_to_l_e = Integer::one() << (security.l_x + security.epsilon);
296        let two_to_l_prime_e = Integer::one() << (security.l_y + security.epsilon);
297        let hat_n_at_two_to_l_e = &aux.rsa_modulo * &two_to_l_e;
298        let hat_n_at_two_to_l = &aux.rsa_modulo * &two_to_l;
299
300        let alpha = Integer::from_rng_half_pm(&mut rng, &two_to_l_e);
301        let beta = Integer::from_rng_half_pm(&mut rng, &two_to_l_prime_e);
302        let r = Integer::sample_in_mult_group_of(&mut rng, data.key_j.n());
303        let r_y = Integer::sample_in_mult_group_of(&mut rng, data.key_i.n());
304        let gamma = Integer::from_rng_half_pm(&mut rng, &hat_n_at_two_to_l_e);
305        let delta = Integer::from_rng_half_pm(&mut rng, &hat_n_at_two_to_l_e);
306        let m = Integer::from_rng_half_pm(&mut rng, &hat_n_at_two_to_l);
307        let mu = Integer::from_rng_half_pm(&mut rng, &hat_n_at_two_to_l);
308
309        let commitment = Commitment {
310            a: {
311                let beta_enc_key0 = data.key_j.encrypt_with(&beta, &r)?;
312                let alpha_at_c = data.key_j.omul(&alpha, data.c)?;
313                data.key_j.oadd(&alpha_at_c, &beta_enc_key0)?
314            },
315            b_x: Point::<C>::generator() * alpha.to_scalar(),
316            b_y: data.key_i.encrypt_with(&beta, &r_y)?,
317            e: aux.combine(&alpha, &gamma)?,
318            s: aux.combine(pdata.x, &m)?,
319            f: aux.combine(&beta, &delta)?,
320            t: aux.combine(pdata.y, &mu)?,
321        };
322        let private_commitment = PrivateCommitment {
323            alpha,
324            beta,
325            r,
326            r_y,
327            gamma,
328            m,
329            delta,
330            mu,
331        };
332        Ok((commitment, private_commitment))
333    }
334
335    /// Compute proof for given data and prior protocol values
336    pub fn prove<C: Curve>(
337        data: Data<C>,
338        pdata: PrivateData,
339        pcomm: &PrivateCommitment,
340        challenge: &Challenge,
341    ) -> Result<Proof, Error> {
342        Ok(Proof {
343            z1: &pcomm.alpha + challenge * pdata.x,
344            z2: &pcomm.beta + challenge * pdata.y,
345            z3: &pcomm.gamma + challenge * &pcomm.m,
346            z4: &pcomm.delta + challenge * &pcomm.mu,
347            w: data
348                .key_j
349                .n()
350                .combine(&pcomm.r, &Integer::one(), pdata.nonce, challenge)
351                .ok_or_else(crate::BadExponent::undefined)?,
352            // TODO: this can be optimized as prover knows key_i factorization
353            w_y: data
354                .key_i
355                .n()
356                .combine(&pcomm.r_y, &Integer::one(), pdata.nonce_y, challenge)
357                .ok_or_else(crate::BadExponent::undefined)?,
358        })
359    }
360
361    /// Verify the proof
362    pub fn verify<C: Curve>(
363        aux: &Aux,
364        data: Data<C>,
365        commitment: &Commitment<C>,
366        security: &SecurityParams,
367        challenge: &Challenge,
368        proof: &Proof,
369    ) -> Result<(), InvalidProof> {
370        // Verify public data
371        fail_if(
372            InvalidProofReason::RangeCheck(1),
373            data.c.in_mult_group_of(data.key_j.nn()),
374        )?;
375        fail_if(
376            InvalidProofReason::RangeCheck(2),
377            data.d.in_mult_group_of(data.key_j.nn()),
378        )?;
379        fail_if(
380            InvalidProofReason::RangeCheck(3),
381            data.y.in_mult_group_of(data.key_i.nn()),
382        )?;
383        // Verify commitment
384        fail_if(
385            InvalidProofReason::RangeCheck(4),
386            commitment.a.in_mult_group_of(data.key_j.nn()),
387        )?;
388        fail_if(
389            InvalidProofReason::RangeCheck(5),
390            commitment.b_y.in_mult_group_of(data.key_i.nn()),
391        )?;
392        fail_if(
393            InvalidProofReason::RangeCheck(6),
394            aux.is_in_mult_group(&commitment.e),
395        )?;
396        fail_if(
397            InvalidProofReason::RangeCheck(7),
398            aux.is_in_mult_group(&commitment.s),
399        )?;
400        fail_if(
401            InvalidProofReason::RangeCheck(8),
402            aux.is_in_mult_group(&commitment.f),
403        )?;
404        fail_if(
405            InvalidProofReason::RangeCheck(9),
406            aux.is_in_mult_group(&commitment.t),
407        )?;
408
409        // Verify statement
410        {
411            let lhs = {
412                let z1_at_c = data
413                    .key_j
414                    .omul(&proof.z1, data.c)
415                    .map_err(|_| InvalidProofReason::PaillierOp)?;
416                let enc = data
417                    .key_j
418                    .encrypt_with(&proof.z2, &proof.w)
419                    .map_err(|_| InvalidProofReason::PaillierEnc)?;
420                data.key_j
421                    .oadd(&z1_at_c, &enc)
422                    .map_err(|_| InvalidProofReason::PaillierOp)?
423            };
424            let rhs = {
425                let e_at_d = data
426                    .key_j
427                    .omul(challenge, data.d)
428                    .map_err(|_| InvalidProofReason::PaillierOp)?;
429                data.key_j
430                    .oadd(&commitment.a, &e_at_d)
431                    .map_err(|_| InvalidProofReason::PaillierOp)?
432            };
433            fail_if_ne(InvalidProofReason::EqualityCheck(10), lhs, rhs)?;
434        }
435        {
436            let lhs = Point::<C>::generator() * proof.z1.to_scalar();
437            let rhs = commitment.b_x + data.x * challenge.to_scalar();
438            fail_if_ne(InvalidProofReason::EqualityCheck(11), lhs, rhs)?;
439        }
440        {
441            let lhs = data
442                .key_i
443                .encrypt_with(&proof.z2, &proof.w_y)
444                .map_err(|_| InvalidProofReason::PaillierEnc)?;
445            let rhs = {
446                let e_at_y = data
447                    .key_i
448                    .omul(challenge, data.y)
449                    .map_err(|_| InvalidProofReason::PaillierOp)?;
450                data.key_i
451                    .oadd(&commitment.b_y, &e_at_y)
452                    .map_err(|_| InvalidProofReason::PaillierOp)?
453            };
454            fail_if_ne(InvalidProofReason::EqualityCheck(12), lhs, rhs)?;
455        }
456        {
457            let lhs = aux.combine(&proof.z1, &proof.z3)?;
458            let s_to_e = aux.pow_mod(&commitment.s, challenge)?;
459            let rhs = (&commitment.e * s_to_e).modulo(&aux.rsa_modulo);
460            fail_if_ne(InvalidProofReason::EqualityCheck(13), lhs, rhs)?;
461        }
462        {
463            let lhs = aux.combine(&proof.z2, &proof.z4)?;
464            let t_to_e = aux.pow_mod(&commitment.t, challenge)?;
465            let rhs = (&commitment.f * t_to_e).modulo(&aux.rsa_modulo);
466            fail_if_ne(InvalidProofReason::EqualityCheck(14), lhs, rhs)?;
467        }
468        fail_if(
469            InvalidProofReason::RangeCheck(15),
470            proof
471                .z1
472                .is_in_half_pm(&(Integer::one() << (security.l_x + security.epsilon))),
473        )?;
474        fail_if(
475            InvalidProofReason::RangeCheck(16),
476            proof
477                .z2
478                .is_in_half_pm(&(Integer::one() << (security.l_y + security.epsilon))),
479        )?;
480        Ok(())
481    }
482
483    /// Generate random challenge
484    pub fn challenge<C: Curve>(rng: &mut impl rand_core::RngCore) -> Integer {
485        let q = Integer::curve_order::<C>();
486        Integer::from_rng_half_pm(rng, &q)
487    }
488}
489
490/// The non-interactive version of proof. Completed in one round, for example
491/// see the documentation of parent module.
492pub mod non_interactive {
493    use digest::Digest;
494    use generic_ec::Curve;
495
496    use crate::{Error, InvalidProof};
497
498    use super::{Aux, Challenge, Commitment, Data, NiProof, PrivateData, SecurityParams};
499
500    /// Compute proof for the given data, producing random commitment and
501    /// deriving determenistic challenge.
502    ///
503    /// Obtained from the above interactive proof via Fiat-Shamir heuristic.
504    pub fn prove<C: Curve, D: Digest>(
505        shared_state: &impl udigest::Digestable,
506        aux: &Aux,
507        data: Data<C>,
508        pdata: PrivateData,
509        security: &SecurityParams,
510        rng: &mut impl rand_core::RngCore,
511    ) -> Result<NiProof<C>, Error> {
512        let (commitment, pcomm) = super::interactive::commit(aux, data, pdata, security, rng)?;
513        let challenge = challenge::<C, D>(shared_state, aux, data, &commitment, security);
514        let proof = super::interactive::prove(data, pdata, &pcomm, &challenge)?;
515        Ok(NiProof { commitment, proof })
516    }
517
518    /// Verify the proof, deriving challenge independently from same data
519    pub fn verify<C: Curve, D: Digest>(
520        shared_state: &impl udigest::Digestable,
521        aux: &Aux,
522        data: Data<C>,
523        security: &SecurityParams,
524        proof: &NiProof<C>,
525    ) -> Result<(), InvalidProof> {
526        let challenge = challenge::<C, D>(shared_state, aux, data, &proof.commitment, security);
527        super::interactive::verify(
528            aux,
529            data,
530            &proof.commitment,
531            security,
532            &challenge,
533            &proof.proof,
534        )
535    }
536
537    /// Deterministically compute challenge based on prior known values in protocol
538    pub fn challenge<C: Curve, D: Digest>(
539        shared_state: &impl udigest::Digestable,
540        aux: &Aux,
541        data: Data<C>,
542        commitment: &Commitment<C>,
543        security: &SecurityParams,
544    ) -> Challenge {
545        let tag = "paillier_zk.paillier_affine_operation_in_range.ni_challenge";
546        let aux = aux.digest_public_data();
547        let seed = udigest::inline_struct!(tag {
548            shared_state,
549            aux,
550            security,
551            data,
552            commitment,
553        });
554        let mut rng = rand_hash::HashRng::<D, _>::from_seed(seed);
555        super::interactive::challenge::<C>(&mut rng)
556    }
557}
558
559#[cfg(test)]
560mod test {
561    use fast_paillier::backend::Integer;
562    use generic_ec::{Curve, Point};
563    use sha2::Digest;
564
565    use crate::common::test::random_key;
566    use crate::common::{IntegerExt, InvalidProofReason};
567
568    fn run<R: rand_core::RngCore + rand_core::CryptoRng, C: Curve, D: Digest>(
569        rng: &mut R,
570        security: super::SecurityParams,
571        x: Integer,
572        y: Integer,
573    ) -> Result<(), crate::common::InvalidProof> {
574        let dk0 = random_key(rng).unwrap();
575        let dk1 = random_key(rng).unwrap();
576        let ek0 = dk0.encryption_key().clone();
577        let ek1 = dk1.encryption_key().clone();
578
579        let (c, _) = {
580            let plaintext = Integer::from_rng_half_pm(rng, ek0.n());
581            ek0.encrypt_with_random(rng, &plaintext).unwrap()
582        };
583
584        let (y_enc_ek1, rho_y) = ek1.encrypt_with_random(rng, &y).unwrap();
585
586        let (y_enc_ek0, rho) = ek0.encrypt_with_random(rng, &y).unwrap();
587        let x_at_c = ek0.omul(&x, &c).unwrap();
588        let d = ek0.oadd(&x_at_c, &y_enc_ek0).unwrap();
589
590        let data = super::Data {
591            key_j: &ek0,
592            key_i: &ek1,
593            c: &c,
594            d: &d,
595            y: &y_enc_ek1,
596            x: &(x.to_scalar::<C>() * Point::generator()),
597        };
598        let pdata = super::PrivateData {
599            x: &x,
600            y: &y,
601            nonce: &rho,
602            nonce_y: &rho_y,
603        };
604
605        let aux = crate::common::test::aux(rng);
606
607        let shared_state = "shared state";
608
609        let proof =
610            super::non_interactive::prove::<C, D>(&shared_state, &aux, data, pdata, &security, rng)
611                .unwrap();
612        super::non_interactive::verify::<C, D>(&shared_state, &aux, data, &security, &proof)
613    }
614
615    fn passing_test<C: Curve, D: Digest>() {
616        let mut rng = rand_dev::DevRng::new();
617        let security = super::SecurityParams {
618            l_x: 256,
619            l_y: 1280,
620            epsilon: 512,
621        };
622        let x = Integer::from_rng_half_pm(&mut rng, &(Integer::one() << security.l_x));
623        let y = Integer::from_rng_half_pm(&mut rng, &(Integer::one() << security.l_y));
624        run::<_, C, D>(&mut rng, security, x, y).expect("proof failed");
625    }
626
627    fn failing_on_additive<C: Curve, D: Digest>() {
628        let mut rng = rand_dev::DevRng::new();
629        let security = super::SecurityParams {
630            l_x: 256,
631            l_y: 1280,
632            epsilon: 512,
633        };
634        let x = Integer::from_rng_half_pm(&mut rng, &(Integer::one() << security.l_x));
635        let y = (Integer::one() << (security.l_y + security.epsilon - 1)) + 1;
636        let r = run::<_, C, D>(&mut rng, security, x, y).expect_err("proof should not pass");
637        match r.reason() {
638            InvalidProofReason::RangeCheck(16) => (),
639            e => panic!("proof should not fail with: {e:?}"),
640        }
641    }
642
643    fn failing_on_multiplicative<C: Curve, D: Digest>() {
644        let mut rng = rand_dev::DevRng::new();
645        let security = super::SecurityParams {
646            l_x: 256,
647            l_y: 1280,
648            epsilon: 512,
649        };
650        let x = (Integer::one() << (security.l_x + security.epsilon - 1)) + 1;
651        let y = Integer::from_rng_half_pm(&mut rng, &(Integer::one() << security.l_y));
652        let r = run::<_, C, D>(&mut rng, security, x, y).expect_err("proof should not pass");
653        match r.reason() {
654            InvalidProofReason::RangeCheck(15) => (),
655            e => panic!("proof should not fail with: {e:?}"),
656        }
657    }
658
659    #[test]
660    fn passing_p256() {
661        passing_test::<generic_ec::curves::Secp256r1, sha2::Sha256>()
662    }
663    #[test]
664    fn failing_p256_add() {
665        failing_on_additive::<generic_ec::curves::Secp256r1, sha2::Sha256>()
666    }
667    #[test]
668    fn failing_p256_mul() {
669        failing_on_multiplicative::<generic_ec::curves::Secp256r1, sha2::Sha256>()
670    }
671
672    #[test]
673    fn passing_million() {
674        passing_test::<crate::curve::C, sha2::Sha256>()
675    }
676    #[test]
677    fn failing_million_add() {
678        failing_on_additive::<crate::curve::C, sha2::Sha256>()
679    }
680    #[test]
681    fn failing_million_mul() {
682        failing_on_multiplicative::<crate::curve::C, sha2::Sha256>()
683    }
684}