1use 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#[derive(Debug, Clone, udigest::Digestable)]
167#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
168pub struct SecurityParams {
169 pub l_x: usize,
171 pub l_y: usize,
173 pub epsilon: usize,
175}
176
177#[derive(Debug, Clone, Copy, udigest::Digestable)]
179#[udigest(bound = "")]
180pub struct Data<'a, C: Curve> {
181 #[udigest(as = crate::common::encoding::AnyEncryptionKey)]
183 pub key_j: &'a dyn AnyEncryptionKey,
184 #[udigest(as = crate::common::encoding::AnyEncryptionKey)]
186 pub key_i: &'a dyn AnyEncryptionKey,
187 #[udigest(as = &crate::common::encoding::Integer)]
189 pub c: &'a Ciphertext,
190 #[udigest(as = &crate::common::encoding::Integer)]
192 pub d: &'a Integer,
193 #[udigest(as = &crate::common::encoding::Integer)]
195 pub y: &'a Ciphertext,
196 pub x: &'a Point<C>,
198}
199
200#[derive(Clone, Copy)]
202pub struct PrivateData<'a> {
203 pub x: &'a Integer,
205 pub y: &'a Integer,
207 pub nonce: &'a Nonce,
209 pub nonce_y: &'a Nonce,
211}
212
213#[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#[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
247pub type Challenge = Integer;
250
251#[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#[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
273pub 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 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 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 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 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 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 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 {
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 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
490pub 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 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 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 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}