From e5bb92c05e5b85fb64de47bd114afd237eee0c23 Mon Sep 17 00:00:00 2001 From: d86leader Date: Mon, 19 Dec 2022 13:18:06 +0100 Subject: [PATCH 01/14] Account for +- ranges --- src/paillier_encryption_in_range.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/paillier_encryption_in_range.rs b/src/paillier_encryption_in_range.rs index da50983..2e135a1 100644 --- a/src/paillier_encryption_in_range.rs +++ b/src/paillier_encryption_in_range.rs @@ -7,7 +7,7 @@ //! cryptosystem. P also has `plaintext`, `nonce`, and //! `ciphertext = key.encrypt(plaintext, nonce)`. //! -//! P wants to prove that `plaintext` is at most `L` bits, without disclosing +//! P wants to prove that `plaintext` is at most `L + 1` bits, without disclosing //! it, the `pkey`, and `nonce` //! ## Example @@ -82,9 +82,10 @@ use crate::unknown_order::BigNumber; #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct SecurityParams { - /// l in paper, bit size of plaintext + /// l in paper, security parameter for bit size of plaintext: it needs to + /// be in range [-2^l; 2^l] or equivalently 2^(l+1) pub l: usize, - /// Epsilon in paper, range extension and security parameter for x + /// Epsilon in paper, slackness parameter pub epsilon: usize, /// q in paper. Security parameter for challenge pub q: BigNumber, @@ -163,8 +164,9 @@ pub mod interactive { security: &SecurityParams, mut rng: R, ) -> (Commitment, PrivateCommitment) { - let two_to_l = BigNumber::from(1) << security.l; - let two_to_l_plus_e = BigNumber::from(1) << (security.l + security.epsilon); + // add 1 to exponents to account for +- + let two_to_l = BigNumber::from(1) << (security.l + 1); + let two_to_l_plus_e = BigNumber::from(1) << (security.l + security.epsilon + 1); let alpha = BigNumber::from_rng(&two_to_l_plus_e, &mut rng); let mu = BigNumber::from_rng(&(two_to_l * &aux.rsa_modulo), &mut rng); let r = gen_inversible(data.key.n(), &mut rng); @@ -241,7 +243,7 @@ pub mod interactive { return Err(InvalidProof::EqualityCheckFailed(2)); } - if proof.z1 > (BigNumber::one() << (security.l + security.epsilon)) { + if proof.z1 > (BigNumber::one() << (security.l + security.epsilon + 1)) { return Err(InvalidProof::RangeCheckFailed(3)); } @@ -249,7 +251,9 @@ pub mod interactive { } pub fn challenge(security: &SecurityParams, rng: &mut R) -> Challenge { - BigNumber::from_rng(&security.q, rng) + // double the range to account for +- + let m = BigNumber::from(2) * &security.q; + BigNumber::from_rng(&m, rng) } } From f0ff59949a9bd9a32f2a1903d72e2d3e903f62b5 Mon Sep 17 00:00:00 2001 From: d86leader Date: Mon, 19 Dec 2022 16:45:27 +0100 Subject: [PATCH 02/14] Refactor tests. Add test for borderline value --- src/paillier_encryption_in_range.rs | 107 +++++++++++++++------------- 1 file changed, 57 insertions(+), 50 deletions(-) diff --git a/src/paillier_encryption_in_range.rs b/src/paillier_encryption_in_range.rs index 2e135a1..55159fc 100644 --- a/src/paillier_encryption_in_range.rs +++ b/src/paillier_encryption_in_range.rs @@ -310,7 +310,8 @@ pub mod non_interactive { .chain_update(&commitment.c.to_bytes()) .finalize(); let mut rng = rand_chacha::ChaCha20Rng::from_seed(seed.into()); - BigNumber::from_rng(&security.q, &mut rng) + let m = BigNumber::from(2) * &security.q; + BigNumber::from_rng(&m, &mut rng) } pub fn verify( @@ -334,16 +335,11 @@ mod test { use crate::common::InvalidProof; use crate::unknown_order::BigNumber; - #[test] - fn passing() { - let security = super::SecurityParams { - l: 1024, - epsilon: 128, - q: BigNumber::prime(256), - }; - let private_key = libpaillier::DecryptionKey::random().unwrap(); + fn run_with(rng: R, security: super::SecurityParams, plaintext: BigNumber) -> Result<(), crate::common::InvalidProof> { + let p = BigNumber::prime(1024); + let q = BigNumber::prime(1024); + let private_key = libpaillier::DecryptionKey::with_primes(&p, &q).unwrap(); let key = libpaillier::EncryptionKey::from(&private_key); - let plaintext: BigNumber = 228.into(); let (ciphertext, nonce) = key.encrypt(plaintext.to_bytes(), None).unwrap(); let data = super::Data { key, ciphertext }; let pdata = super::PrivateData { plaintext, nonce }; @@ -364,16 +360,27 @@ mod test { &data, &pdata, &security, - rand_core::OsRng::default(), + rng, ); - let r = super::non_interactive::verify( + super::non_interactive::verify( shared_state, &aux, &data, &commitment, &security, &proof, - ); + ) + } + + #[test] + fn passing() { + let security = super::SecurityParams { + l: 1024, + epsilon: 256, + q: BigNumber::prime(128), + }; + let plaintext = (BigNumber::one() << (security.l + 1)) - 1; + let r = run_with(rand_core::OsRng::default(), security, plaintext); match r { Ok(()) => (), Err(e) => panic!("{:?}", e), @@ -383,48 +390,48 @@ mod test { fn failing() { let security = super::SecurityParams { l: 1024, - epsilon: 128, - q: BigNumber::prime(256), + epsilon: 256, + q: BigNumber::prime(128), }; - let p = BigNumber::prime(1024); - let q = BigNumber::prime(1024); - let private_key = libpaillier::DecryptionKey::with_primes(&p, &q).unwrap(); - let key = libpaillier::EncryptionKey::from(&private_key); - let plaintext: BigNumber = (BigNumber::one() << (security.l + security.epsilon)) + 1; - let (ciphertext, nonce) = key.encrypt(plaintext.to_bytes(), None).unwrap(); - let data = super::Data { key, ciphertext }; - let pdata = super::PrivateData { plaintext, nonce }; - - let p = BigNumber::prime(1024); - let q = BigNumber::prime(1024); - let rsa_modulo = p * q; - let s: BigNumber = 123.into(); - let t: BigNumber = 321.into(); - assert_eq!(s.gcd(&rsa_modulo), 1.into()); - assert_eq!(t.gcd(&rsa_modulo), 1.into()); - let aux = super::Aux { s, t, rsa_modulo }; - - let shared_state = sha2::Sha256::default(); - let (commitment, proof) = super::non_interactive::prove( - shared_state.clone(), - &aux, - &data, - &pdata, - &security, - rand_core::OsRng::default(), - ); - let r = super::non_interactive::verify( - shared_state, - &aux, - &data, - &commitment, - &security, - &proof, - ); + let plaintext = (BigNumber::one() << (security.l + security.epsilon + 1)) + 1; + let r = run_with(rand_core::OsRng::default(), security, plaintext); match r { Ok(()) => panic!("proof should not pass"), Err(InvalidProof::RangeCheckFailed(_)) => (), Err(e) => panic!("proof should not fail with {:?}", e), } } + + #[test] + fn rejected_with_probability_1_over_2() { + // plaintext in range 2^(l+1) should be rejected with probablility + // q / 2^epsilon. I set parameters like this: + // bitsize(q) = 128 + // epsilon = 129 + // Thus probability should be around 1/2. + // in 32 runs that's not what was observed. Very possible it's an + // artifact of distribution, so I decide to ignore it, and test that + // there are passing and failing values. + + fn maybe_rejected(rng: rand_chacha::ChaCha20Rng) -> bool { + let security = super::SecurityParams { + l: 512, + epsilon: 129, + q: BigNumber::prime(128), + }; + let plaintext: BigNumber = (BigNumber::one() << (security.l + 1)) - 1; + let r = run_with(rng, security, plaintext); + match r { + Ok(()) => true, + Err(InvalidProof::RangeCheckFailed(_)) => false, + Err(e) => panic!("proof should not fail with {:?}", e), + } + } + + use rand_core::SeedableRng; + let rng = rand_chacha::ChaCha20Rng::seed_from_u64(8); + assert!(maybe_rejected(rng), "should pass"); + let rng = rand_chacha::ChaCha20Rng::seed_from_u64(9); + assert!(!maybe_rejected(rng), "should fail"); + } } From c61f79e1f064e10b3b3038a159e7f876f3ecda71 Mon Sep 17 00:00:00 2001 From: d86leader Date: Tue, 20 Dec 2022 14:49:57 +0100 Subject: [PATCH 03/14] +- -> 2x; Refactor tests; Add q as security parameter --- src/paillier_affine_operation_in_range.rs | 179 ++++++++++------------ 1 file changed, 80 insertions(+), 99 deletions(-) diff --git a/src/paillier_affine_operation_in_range.rs b/src/paillier_affine_operation_in_range.rs index b142dc5..e53be21 100644 --- a/src/paillier_affine_operation_in_range.rs +++ b/src/paillier_affine_operation_in_range.rs @@ -5,7 +5,7 @@ //! //! A party P performs a paillier affine operation with C, Y, and X //! obtaining `D = C*X + Y`. `X` and `Y` are encrypted values of `x` and `y`. P -//! then wants to prove that `y` and `x` are at most `L` and `L'` bits, +//! then wants to prove that `y` and `x` are at most `L+1` and `L'+1` bits, //! correspondingly, and P doesn't want to disclose none of the plaintexts //! //! Given: @@ -145,12 +145,14 @@ use crate::unknown_order::BigNumber; #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct SecurityParams { - /// l in paper, bit size of x + /// l in paper, bit size of +-x pub l_x: usize, - /// l' in paper, bit size of y + /// l' in paper, bit size of +-y pub l_y: usize, - /// Epsilon in paper, range extension and security parameter for x and y + /// Epsilon in paper, slackness parameter pub epsilon: usize, + /// q in paper. Security parameter for challenge + pub q: BigNumber, } /// Public data that both parties know @@ -233,7 +235,7 @@ pub use crate::common::Aux; pub mod interactive { use crate::unknown_order::BigNumber; - use generic_ec::{Curve, Point, Scalar}; + use generic_ec::{Curve, Point}; use rand_core::RngCore; use crate::common::{combine, convert_scalar, gen_inversible, InvalidProof, ProtocolError}; @@ -248,13 +250,13 @@ pub mod interactive { security: &SecurityParams, mut rng: R, ) -> Result<(Commitment, PrivateCommitment), ProtocolError> { - let two_to_l = BigNumber::one() << security.l_x; - let two_to_l_e = BigNumber::one() << (security.l_x + security.epsilon); + let two_to_l = BigNumber::one() << (security.l_x + 1); + let two_to_l_e = BigNumber::one() << (security.l_x + security.epsilon + 1); let modulo_l = two_to_l * &aux.rsa_modulo; let modulo_l_e = &two_to_l_e * &aux.rsa_modulo; let alpha = BigNumber::from_rng(&two_to_l_e, &mut rng); - let beta = BigNumber::from_rng(&(BigNumber::one() << security.l_y), &mut rng); + let beta = BigNumber::from_rng(&(BigNumber::one() << (security.l_y + security.epsilon + 1)), &mut rng); let r = gen_inversible(data.key0.n(), &mut rng); let r_y = gen_inversible(data.key1.n(), &mut rng); let gamma = BigNumber::from_rng(&modulo_l_e, &mut rng); @@ -405,23 +407,23 @@ pub mod interactive { )?; fail_if( InvalidProof::RangeCheckFailed(6), - proof.z1 <= &one << (security.l_x + security.epsilon), + proof.z1 <= &one << (security.l_x + security.epsilon + 1), )?; fail_if( InvalidProof::RangeCheckFailed(7), - proof.z2 <= &one << (security.l_y + security.epsilon), + proof.z2 <= &one << (security.l_y + security.epsilon + 1), )?; Ok(()) } /// Generate random challenge - pub fn challenge(rng: &mut R) -> BigNumber + pub fn challenge(rng: &mut R, security: &SecurityParams) -> BigNumber where - C: Curve, R: RngCore, { - let x = Scalar::::random(rng); - BigNumber::from_slice(x.to_be_bytes().as_bytes()) + // double the range to account for +- + let m = BigNumber::from(2) * &security.q; + BigNumber::from_rng(&m, rng) } } @@ -484,7 +486,6 @@ pub mod non_interactive { security: &SecurityParams, ) -> Challenge where - Scalar: FromHash, D: Digest, { use rand_core::SeedableRng; @@ -510,8 +511,8 @@ pub mod non_interactive { .chain_update((security.epsilon as u64).to_le_bytes()) .finalize(); let mut rng = rand_chacha::ChaCha20Rng::from_seed(seed.into()); - let scalar = Scalar::::random(&mut rng); - BigNumber::from_slice(scalar.to_be_bytes().as_bytes()) + let m = BigNumber::from(2) * &security.q; + BigNumber::from_rng(&m, &mut rng) } } @@ -522,25 +523,18 @@ mod test { use crate::common::convert_scalar; use crate::unknown_order::BigNumber; - fn passing_test() + fn run(rng: R, security: super::SecurityParams, plaintext_orig: BigNumber, plaintext_mult: BigNumber, plaintext_add: BigNumber) -> Result<(), crate::common::InvalidProof> where Scalar: FromHash, { - let security = super::SecurityParams { - l_x: 1024, - l_y: 1024, - epsilon: 128, - }; + let affined = &plaintext_mult * &plaintext_orig + &plaintext_add; + let private_key0 = libpaillier::DecryptionKey::random().unwrap(); let key0 = libpaillier::EncryptionKey::from(&private_key0); let private_key1 = libpaillier::DecryptionKey::random().unwrap(); let key1 = libpaillier::EncryptionKey::from(&private_key1); let g = generic_ec::Point::::generator(); - let plaintext: BigNumber = 228.into(); - let plaintext_orig = BigNumber::from(100); - let plaintext_mult = BigNumber::from(2); - let plaintext_add = BigNumber::from(28); - let (ciphertext, _) = key0.encrypt(plaintext.to_bytes(), None).unwrap(); + let (ciphertext, _) = key0.encrypt(affined.to_bytes(), None).unwrap(); let (ciphertext_orig, _) = key0.encrypt(plaintext_orig.to_bytes(), None).unwrap(); let ciphertext_mult = g * convert_scalar(&plaintext_mult); let (ciphertext_add, nonce_y) = key1.encrypt(plaintext_add.to_bytes(), None).unwrap(); @@ -588,97 +582,76 @@ mod test { &data, &pdata, &security, - rand_core::OsRng::default(), + rng, ) .unwrap(); - let r = super::non_interactive::verify( + super::non_interactive::verify( shared_state, &aux, &data, &commitment, &security, &proof, - ); + ) + } + fn passing_test() + where + Scalar: FromHash, + { + let security = super::SecurityParams { + l_x: 1024, + l_y: 1024, + epsilon: 256, + q: BigNumber::prime(128), + }; + let plaintext_orig = BigNumber::from(100); + let plaintext_mult = BigNumber::from(1) << (security.l_x + 1); + let plaintext_add = BigNumber::from(1) << (security.l_y + 1); + let r = run(rand_core::OsRng::default(), security, plaintext_orig, plaintext_mult, plaintext_add); match r { Ok(()) => (), Err(e) => panic!("{:?}", e), } } - fn failing_test() + fn failing_on_additive() where Scalar: FromHash, { let security = super::SecurityParams { l_x: 1024, l_y: 1024, - epsilon: 128, - }; - let private_key0 = libpaillier::DecryptionKey::random().unwrap(); - let key0 = libpaillier::EncryptionKey::from(&private_key0); - let private_key1 = libpaillier::DecryptionKey::random().unwrap(); - let key1 = libpaillier::EncryptionKey::from(&private_key1); - let g = generic_ec::Point::::generator(); - let plaintext_orig = BigNumber::from(1337); - let plaintext_mult = (BigNumber::one() << (1024 + 128)) + 1; - let plaintext_add: BigNumber = (BigNumber::one() << (1024 + 128)) + 2; - let (ciphertext_orig, _) = key0.encrypt(plaintext_orig.to_bytes(), None).unwrap(); - let ciphertext_mult = g * convert_scalar(&plaintext_mult); - let (ciphertext_add, nonce_y) = key1.encrypt(plaintext_add.to_bytes(), None).unwrap(); - let (ciphertext_add_action, nonce) = key0.encrypt(plaintext_add.to_bytes(), None).unwrap(); - // verify that D is obtained from affine transformation of C - let transformed = key0 - .add( - &key0.mul(&ciphertext_orig, &plaintext_mult).unwrap(), - &ciphertext_add_action, - ) - .unwrap(); - let data = super::Data { - key0, - key1, - c: ciphertext_orig, - d: transformed, - y: ciphertext_add, - x: ciphertext_mult, - }; - let pdata = super::PrivateData { - x: plaintext_mult, - y: plaintext_add, - nonce, - nonce_y, + epsilon: 256, + q: BigNumber::prime(128), }; + let plaintext_orig = BigNumber::from(100); + let plaintext_mult = BigNumber::from(1) << (security.l_x + 1); + let plaintext_add = (BigNumber::from(1) << (security.l_y + security.epsilon + 1)) + 1; + let r = run(rand_core::OsRng::default(), security, plaintext_orig, plaintext_mult, plaintext_add); + match r { + Ok(()) => panic!("proof should not pass"), + Err(crate::common::InvalidProof::RangeCheckFailed(7)) => (), + Err(e) => panic!("proof should not fail with: {:?}", e), + } + } - let p = BigNumber::prime(1024 + 128 + 1); - let q = BigNumber::prime(1024 + 128 + 1); - let rsa_modulo = p * q; - let s: BigNumber = 123.into(); - let t: BigNumber = 321.into(); - assert_eq!(s.gcd(&rsa_modulo), 1.into()); - assert_eq!(t.gcd(&rsa_modulo), 1.into()); - let aux = super::Aux { s, t, rsa_modulo }; - - let shared_state = sha2::Sha256::default(); - - let (commitment, proof) = super::non_interactive::prove( - shared_state.clone(), - &aux, - &data, - &pdata, - &security, - rand_core::OsRng::default(), - ) - .unwrap(); - let r = super::non_interactive::verify( - shared_state, - &aux, - &data, - &commitment, - &security, - &proof, - ); + fn failing_on_multiplicative() + where + Scalar: FromHash, + { + let security = super::SecurityParams { + l_x: 1024, + l_y: 1024, + epsilon: 256, + q: BigNumber::prime(128), + }; + let plaintext_orig = BigNumber::from(100); + let plaintext_mult = (BigNumber::from(1) << (security.l_x + security.epsilon + 1)) + 1; + let plaintext_add = BigNumber::from(1) << (security.l_y + 1); + let r = run(rand_core::OsRng::default(), security, plaintext_orig, plaintext_mult, plaintext_add); match r { Ok(()) => panic!("proof should not pass"), - Err(crate::common::InvalidProof::RangeCheckFailed(_)) => (), + Err(crate::common::InvalidProof::RangeCheckFailed(6)) => (), Err(e) => panic!("proof should not fail with: {:?}", e), } } @@ -688,8 +661,12 @@ mod test { passing_test::() } #[test] - fn failing_p256() { - failing_test::() + fn failing_p256_add() { + failing_on_additive::() + } + #[test] + fn failing_p256_mul() { + failing_on_multiplicative::() } #[test] @@ -697,7 +674,11 @@ mod test { passing_test::() } #[test] - fn failing_million() { - failing_test::() + fn failing_million_add() { + failing_on_additive::() + } + #[test] + fn failing_million_mul() { + failing_on_multiplicative::() } } From 8b91f01fed2c7b3c4cb46af04281015f1a498509 Mon Sep 17 00:00:00 2001 From: d86leader Date: Tue, 20 Dec 2022 16:05:49 +0100 Subject: [PATCH 04/14] Fix incorrect scalar generation --- src/curve.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/curve.rs b/src/curve.rs index 513927c..d9bfa33 100644 --- a/src/curve.rs +++ b/src/curve.rs @@ -189,7 +189,7 @@ impl generic_ec_core::One for Scalar { impl generic_ec_core::Samplable for Scalar { fn random(rng: &mut R) -> Self { - Scalar(rng.next_u64()) + rng.next_u64().into() } } From ff8950fb5fbe80f4e0e5edfdfaaf23f3aa27aace Mon Sep 17 00:00:00 2001 From: d86leader Date: Tue, 20 Dec 2022 17:31:29 +0100 Subject: [PATCH 05/14] Fix non-determenism --- src/paillier_affine_operation_in_range.rs | 45 +++++++++++++++-------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/src/paillier_affine_operation_in_range.rs b/src/paillier_affine_operation_in_range.rs index e53be21..c814187 100644 --- a/src/paillier_affine_operation_in_range.rs +++ b/src/paillier_affine_operation_in_range.rs @@ -523,22 +523,34 @@ mod test { use crate::common::convert_scalar; use crate::unknown_order::BigNumber; - fn run(rng: R, security: super::SecurityParams, plaintext_orig: BigNumber, plaintext_mult: BigNumber, plaintext_add: BigNumber) -> Result<(), crate::common::InvalidProof> + fn random_key(rng: &mut R) -> Option { + let p = BigNumber::prime_from_rng(1024, rng); + let q = BigNumber::prime_from_rng(1024, rng); + libpaillier::DecryptionKey::with_primes_unchecked(&p, &q) + } + + fn nonce(rng: &mut R, n: &BigNumber) -> Option { + Some(BigNumber::from_rng(n, rng)) + } + + fn run(mut rng: R, security: super::SecurityParams, plaintext_orig: BigNumber, plaintext_mult: BigNumber, plaintext_add: BigNumber) -> Result<(), crate::common::InvalidProof> where Scalar: FromHash, { let affined = &plaintext_mult * &plaintext_orig + &plaintext_add; - let private_key0 = libpaillier::DecryptionKey::random().unwrap(); + let private_key0 = random_key(&mut rng).unwrap(); let key0 = libpaillier::EncryptionKey::from(&private_key0); - let private_key1 = libpaillier::DecryptionKey::random().unwrap(); + let private_key1 = random_key(&mut rng).unwrap(); let key1 = libpaillier::EncryptionKey::from(&private_key1); let g = generic_ec::Point::::generator(); - let (ciphertext, _) = key0.encrypt(affined.to_bytes(), None).unwrap(); - let (ciphertext_orig, _) = key0.encrypt(plaintext_orig.to_bytes(), None).unwrap(); + let (ciphertext, _) = key0.encrypt(affined.to_bytes(), nonce(&mut rng, key0.n())).unwrap(); + let (ciphertext_orig, _) = key0.encrypt(plaintext_orig.to_bytes(), nonce(&mut rng, key0.n())).unwrap(); let ciphertext_mult = g * convert_scalar(&plaintext_mult); - let (ciphertext_add, nonce_y) = key1.encrypt(plaintext_add.to_bytes(), None).unwrap(); - let (ciphertext_add_action, nonce) = key0.encrypt(plaintext_add.to_bytes(), None).unwrap(); + let nonce_y = nonce(&mut rng, key1.n()); + let (ciphertext_add, nonce_y) = key1.encrypt(plaintext_add.to_bytes(), nonce_y).unwrap(); + let nonce = nonce(&mut rng, key0.n()); + let (ciphertext_add_action, nonce) = key0.encrypt(plaintext_add.to_bytes(), nonce).unwrap(); // verify that D is obtained from affine transformation of C let transformed = key0 .add( @@ -565,8 +577,8 @@ mod test { nonce_y, }; - let p = BigNumber::prime(1024 + 128 + 1); - let q = BigNumber::prime(1024 + 128 + 1); + let p = BigNumber::prime_from_rng(1024, &mut rng); + let q = BigNumber::prime_from_rng(1024, &mut rng); let rsa_modulo = p * q; let s: BigNumber = 123.into(); let t: BigNumber = 321.into(); @@ -598,16 +610,17 @@ mod test { where Scalar: FromHash, { + let mut rng = rand_core::OsRng::default(); let security = super::SecurityParams { l_x: 1024, l_y: 1024, epsilon: 256, - q: BigNumber::prime(128), + q: BigNumber::prime_from_rng(128, &mut rng), }; let plaintext_orig = BigNumber::from(100); let plaintext_mult = BigNumber::from(1) << (security.l_x + 1); let plaintext_add = BigNumber::from(1) << (security.l_y + 1); - let r = run(rand_core::OsRng::default(), security, plaintext_orig, plaintext_mult, plaintext_add); + let r = run(rng, security, plaintext_orig, plaintext_mult, plaintext_add); match r { Ok(()) => (), Err(e) => panic!("{:?}", e), @@ -618,16 +631,17 @@ mod test { where Scalar: FromHash, { + let mut rng = rand_core::OsRng::default(); let security = super::SecurityParams { l_x: 1024, l_y: 1024, epsilon: 256, - q: BigNumber::prime(128), + q: BigNumber::prime_from_rng(128, &mut rng), }; let plaintext_orig = BigNumber::from(100); let plaintext_mult = BigNumber::from(1) << (security.l_x + 1); let plaintext_add = (BigNumber::from(1) << (security.l_y + security.epsilon + 1)) + 1; - let r = run(rand_core::OsRng::default(), security, plaintext_orig, plaintext_mult, plaintext_add); + let r = run(rng, security, plaintext_orig, plaintext_mult, plaintext_add); match r { Ok(()) => panic!("proof should not pass"), Err(crate::common::InvalidProof::RangeCheckFailed(7)) => (), @@ -639,16 +653,17 @@ mod test { where Scalar: FromHash, { + let mut rng = rand_core::OsRng::default(); let security = super::SecurityParams { l_x: 1024, l_y: 1024, epsilon: 256, - q: BigNumber::prime(128), + q: BigNumber::prime_from_rng(128, &mut rng), }; let plaintext_orig = BigNumber::from(100); let plaintext_mult = (BigNumber::from(1) << (security.l_x + security.epsilon + 1)) + 1; let plaintext_add = BigNumber::from(1) << (security.l_y + 1); - let r = run(rand_core::OsRng::default(), security, plaintext_orig, plaintext_mult, plaintext_add); + let r = run(rng, security, plaintext_orig, plaintext_mult, plaintext_add); match r { Ok(()) => panic!("proof should not pass"), Err(crate::common::InvalidProof::RangeCheckFailed(6)) => (), From b072b872a60fc557e245facae33c57cfc10092c7 Mon Sep 17 00:00:00 2001 From: d86leader Date: Tue, 20 Dec 2022 17:35:10 +0100 Subject: [PATCH 06/14] Add borderline test --- src/paillier_affine_operation_in_range.rs | 62 ++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/src/paillier_affine_operation_in_range.rs b/src/paillier_affine_operation_in_range.rs index c814187..76f8bfd 100644 --- a/src/paillier_affine_operation_in_range.rs +++ b/src/paillier_affine_operation_in_range.rs @@ -94,13 +94,15 @@ //! // 5. Prover computes a non-interactive proof that plaintext_add and //! // plaintext_mult are at most L and L' bits //! +//! let mut rng = rand_core::OsRng::default(); +//! //! let security = p::SecurityParams { //! l_x: 1024, //! l_y: 1024, //! epsilon: 128, +//! q: BigNumber::prime_from_rng(128, &mut rng), //! }; //! -//! let rng = rand_core::OsRng::default(); //! let data = p::Data { //! key0, //! key1, @@ -696,4 +698,62 @@ mod test { fn failing_million_mul() { failing_on_multiplicative::() } + + // see notes in + // [crate::paillier_encryption_in_range::test::rejected_with_probability_1_over_2] + // for motivation and design of the following two tests + + #[test] + fn mul_rejected_with_probability_1_over_2() { + use rand_core::SeedableRng; + fn maybe_rejected(mut rng: rand_chacha::ChaCha20Rng) -> bool { + let security = super::SecurityParams { + l_x: 1024, + l_y: 1024, + epsilon: 130, + q: BigNumber::prime_from_rng(128, &mut rng), + }; + let plaintext_orig = BigNumber::from(100); + let plaintext_mult = (BigNumber::from(1) << (security.l_x + 1)) - 1; + let plaintext_add = BigNumber::from(1) << (security.l_y / 2); + let r = run::<_, generic_ec_curves::rust_crypto::Secp256r1>(rng, security, plaintext_orig, plaintext_mult, plaintext_add); + match r { + Ok(()) => true, + Err(crate::common::InvalidProof::RangeCheckFailed(6)) => false, + Err(e) => panic!("proof should not fail with: {:?}", e), + } + } + + let rng = rand_chacha::ChaCha20Rng::seed_from_u64(0); + assert!(!maybe_rejected(rng), "should fail"); + let rng = rand_chacha::ChaCha20Rng::seed_from_u64(1); + assert!(maybe_rejected(rng), "should pass"); + } + + #[test] + fn add_rejected_with_probability_1_over_2() { + use rand_core::SeedableRng; + fn maybe_rejected(mut rng: rand_chacha::ChaCha20Rng) -> bool { + let security = super::SecurityParams { + l_x: 1024, + l_y: 1024, + epsilon: 130, + q: BigNumber::prime_from_rng(128, &mut rng), + }; + let plaintext_orig = BigNumber::from(100); + let plaintext_mult = BigNumber::from(1) << (security.l_x / 2); + let plaintext_add = (BigNumber::from(1) << (security.l_y + 1)) + 1; + let r = run::<_, generic_ec_curves::rust_crypto::Secp256r1>(rng, security, plaintext_orig, plaintext_mult, plaintext_add); + match r { + Ok(()) => true, + Err(crate::common::InvalidProof::RangeCheckFailed(7)) => false, + Err(e) => panic!("proof should not fail with: {:?}", e), + } + } + + let rng = rand_chacha::ChaCha20Rng::seed_from_u64(0); + assert!(maybe_rejected(rng), "should pass"); + let rng = rand_chacha::ChaCha20Rng::seed_from_u64(1); + assert!(!maybe_rejected(rng), "should fail"); + } } From 264f55e6ab0094fcd7557f503e47a26f6c3b5c35 Mon Sep 17 00:00:00 2001 From: d86leader Date: Wed, 25 Jan 2023 13:34:33 +0100 Subject: [PATCH 07/14] =?UTF-8?q?=D0=9Flog*:=20+-=20->=202x;=20Refactor=20?= =?UTF-8?q?tests;=20Add=20q=20as=20security=20parameter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...element_vs_paillier_encryption_in_range.rs | 176 ++++++++++-------- 1 file changed, 97 insertions(+), 79 deletions(-) diff --git a/src/group_element_vs_paillier_encryption_in_range.rs b/src/group_element_vs_paillier_encryption_in_range.rs index a308b1a..905ed6d 100644 --- a/src/group_element_vs_paillier_encryption_in_range.rs +++ b/src/group_element_vs_paillier_encryption_in_range.rs @@ -3,8 +3,9 @@ //! ## Description //! //! A party P has a number `X = g ^ x`, with g being a generator of -//! multiplicative group G. P wants to prove to party V that the logarithm of X, -//! i.e. x, is at most L bits. +//! multiplicative group G. P has encrypted x as C. P shares X and C with V and +//! wants to prove that the logarithm of X is the plaintext of C, and that the +//! plaintext (i.e. x) is at most L+1 bits. //! //! Given: //! - `key0`, `pkey0` - pair of public and private keys in paillier cryptosystem @@ -56,12 +57,14 @@ //! //! // 3. Prover computes a non-interactive proof that plaintext is at most 1024 bits: //! +//! let mut rng = rand_core::OsRng::default(); +//! //! let security = p::SecurityParams { //! l: 1024, //! epsilon: 128, +//! q: BigNumber::prime_from_rng(128, &mut rng), //! }; //! -//! let rng = rand_core::OsRng::default(); //! let data = p::Data { key0, c: ciphertext, x: power, g }; //! let pdata = p::PrivateData { x: plaintext, nonce }; //! let (commitment, proof) = @@ -93,17 +96,22 @@ use crate::unknown_order::BigNumber; #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct SecurityParams { - /// l in paper, bit size of plaintext + /// l in paper, bit size of +-plaintext pub l: usize, - /// Epsilon in paper, range extension and security parameter for x + /// Epsilon in paper, slackness parameter pub epsilon: usize, + /// q in paper. Security parameter for challenge + pub q: BigNumber, } #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(bound = ""))] pub struct Data { + /// N0 in paper, public key that C was encrypted on pub key0: EncryptionKey, + /// C in paper, logarithm of X encrypted on N0 pub c: Ciphertext, + /// X in paper, exponent of plaintext of C pub x: Point, /// A generator in group pub g: Point, @@ -111,7 +119,9 @@ pub struct Data { #[derive(Clone)] pub struct PrivateData { + /// x in paper, logarithm of X and plaintext of C pub x: BigNumber, + /// rho in paper, nonce in encryption x -> C pub nonce: Nonce, } @@ -145,7 +155,7 @@ pub struct Proof { pub use crate::common::Aux; pub mod interactive { - use generic_ec::{Curve, Scalar}; + use generic_ec::Curve; use libpaillier::unknown_order::BigNumber; use rand_core::RngCore; @@ -163,8 +173,8 @@ pub mod interactive { security: &SecurityParams, mut rng: R, ) -> Result<(Commitment, PrivateCommitment), ProtocolError> { - let two_to_l = BigNumber::one() << security.l; - let two_to_l_e = BigNumber::one() << (security.l + security.epsilon); + let two_to_l = BigNumber::one() << (security.l + 1); + let two_to_l_e = BigNumber::one() << (security.l + security.epsilon + 1); let modulo_l = two_to_l * &aux.rsa_modulo; let modulo_l_e = &two_to_l_e * &aux.rsa_modulo; @@ -256,7 +266,7 @@ pub mod interactive { fail_if(lhs == rhs, InvalidProof::EqualityCheckFailed(3))?; } fail_if( - proof.z1 <= one << (security.l + security.epsilon), + proof.z1 <= one << (security.l + security.epsilon + 1), InvalidProof::RangeCheckFailed(4), )?; @@ -264,13 +274,16 @@ pub mod interactive { } /// Generate random challenge - pub fn challenge(rng: &mut R) -> BigNumber + /// + /// `data` parameter is used to generate challenge in correct range + pub fn challenge(rng: &mut R, security: &SecurityParams) -> BigNumber where C: Curve, R: RngCore, { - let x = Scalar::::random(rng); - BigNumber::from_slice(x.to_be_bytes().as_bytes()) + // double the range to account for +- + let m = BigNumber::from(2) * &security.q; + BigNumber::from_rng(&m, rng) } } @@ -353,8 +366,8 @@ pub mod non_interactive { .finalize(); let mut rng = rand_chacha::ChaCha20Rng::from_seed(seed.into()); - let scalar = Scalar::::random(&mut rng); - BigNumber::from_slice(scalar.to_be_bytes().as_bytes()) + let m = BigNumber::from(2) * &security.q; + BigNumber::from_rng(&m, &mut rng) } } @@ -365,19 +378,29 @@ mod test { use crate::common::{convert_scalar, InvalidProof}; - fn passing_test() + fn random_key(rng: &mut R) -> Option { + let p = BigNumber::prime_from_rng(1024, rng); + let q = BigNumber::prime_from_rng(1024, rng); + libpaillier::DecryptionKey::with_primes_unchecked(&p, &q) + } + + fn nonce(rng: &mut R, n: &BigNumber) -> Option { + Some(BigNumber::from_rng(n, rng)) + } + + fn run( + mut rng: R, + security: super::SecurityParams, + plaintext: BigNumber, + ) -> Result<(), crate::common::InvalidProof> where Scalar: FromHash, { - let security = super::SecurityParams { - l: 1024, - epsilon: 128, - }; - let private_key0 = libpaillier::DecryptionKey::random().unwrap(); + let private_key0 = random_key(&mut rng).unwrap(); let key0 = libpaillier::EncryptionKey::from(&private_key0); - let plaintext = BigNumber::from(228); - let (ciphertext, nonce) = key0.encrypt(plaintext.to_bytes(), None).unwrap(); + let nonce = nonce(&mut rng, key0.n()); + let (ciphertext, nonce) = key0.encrypt(plaintext.to_bytes(), nonce).unwrap(); let g = generic_ec::Point::::generator() * generic_ec::Scalar::::from(1337); let x = g * convert_scalar(&plaintext); @@ -392,8 +415,8 @@ mod test { nonce, }; - let p = BigNumber::prime(1024); - let q = BigNumber::prime(1024); + let p = BigNumber::prime_from_rng(1024, &mut rng); + let q = BigNumber::prime_from_rng(1024, &mut rng); let rsa_modulo = p * q; let s: BigNumber = 123.into(); let t: BigNumber = 321.into(); @@ -409,18 +432,25 @@ mod test { &data, &pdata, &security, - rand_core::OsRng::default(), + rng, ) .unwrap(); - let r = super::non_interactive::verify( - shared_state, - &aux, - &data, - &commitment, - &security, - &proof, - ); + super::non_interactive::verify(shared_state, &aux, &data, &commitment, &security, &proof) + } + + fn passing_test() + where + Scalar: FromHash, + { + let mut rng = rand_core::OsRng::default(); + let security = super::SecurityParams { + l: 1024, + epsilon: 256, + q: BigNumber::prime_from_rng(128, &mut rng), + }; + let plaintext = BigNumber::from(228); + let r = run(rng, security, plaintext); match r { Ok(()) => (), Err(e) => panic!("{:?}", e), @@ -431,57 +461,14 @@ mod test { where Scalar: FromHash, { + let mut rng = rand_core::OsRng::default(); let security = super::SecurityParams { l: 1024, epsilon: 128, + q: BigNumber::prime_from_rng(128, &mut rng), }; - let private_key0 = libpaillier::DecryptionKey::random().unwrap(); - let key0 = libpaillier::EncryptionKey::from(&private_key0); - let plaintext = BigNumber::from(1) << (security.l + security.epsilon + 1); - let (ciphertext, nonce) = key0.encrypt(plaintext.to_bytes(), None).unwrap(); - let g = generic_ec::Point::::generator() * generic_ec::Scalar::::from(1337); - let x = g * convert_scalar(&plaintext); - - let data = super::Data { - key0, - c: ciphertext, - x, - g, - }; - let pdata = super::PrivateData { - x: plaintext, - nonce, - }; - - let p = BigNumber::prime(1024); - let q = BigNumber::prime(1024); - let rsa_modulo = p * q; - let s: BigNumber = 123.into(); - let t: BigNumber = 321.into(); - assert_eq!(s.gcd(&rsa_modulo), 1.into()); - assert_eq!(t.gcd(&rsa_modulo), 1.into()); - let aux = super::Aux { s, t, rsa_modulo }; - - let shared_state = sha2::Sha256::default(); - - let (commitment, proof) = super::non_interactive::prove( - shared_state.clone(), - &aux, - &data, - &pdata, - &security, - rand_core::OsRng::default(), - ) - .unwrap(); - let r = super::non_interactive::verify( - shared_state, - &aux, - &data, - &commitment, - &security, - &proof, - ); + let r = run(rng, security, plaintext); match r { Ok(()) => panic!("proof should not pass"), Err(InvalidProof::RangeCheckFailed(_)) => (), @@ -506,4 +493,35 @@ mod test { fn failing_million() { failing_test::() } + + // see notes in + // [crate::paillier_encryption_in_range::test::rejected_with_probability_1_over_2] + // for motivation and design of the following test. + // Altough no security estimate was given in the paper, my own calculations + // show that the parameters here achieve the probability about as good as in + // other tests + + #[test] + fn mul_rejected_with_probability_1_over_2() { + use rand_core::SeedableRng; + fn maybe_rejected(mut rng: rand_chacha::ChaCha20Rng) -> bool { + let security = super::SecurityParams { + l: 1024, + epsilon: 130, + q: BigNumber::prime_from_rng(128, &mut rng), + }; + let plaintext = (BigNumber::from(1) << (security.l + 1)) - 1; + let r = run::<_, generic_ec_curves::rust_crypto::Secp256r1>(rng, security, plaintext); + match r { + Ok(()) => true, + Err(crate::common::InvalidProof::RangeCheckFailed(4)) => false, + Err(e) => panic!("proof should not fail with: {:?}", e), + } + } + + let rng = rand_chacha::ChaCha20Rng::seed_from_u64(2); + assert!(!maybe_rejected(rng), "should fail"); + let rng = rand_chacha::ChaCha20Rng::seed_from_u64(3); + assert!(maybe_rejected(rng), "should pass"); + } } From 38691f7b14e348f394ba06b26c9c8cd1b5ac3777 Mon Sep 17 00:00:00 2001 From: d86leader Date: Wed, 25 Jan 2023 13:44:16 +0100 Subject: [PATCH 08/14] =?UTF-8?q?=D0=9Fdec:=20+-=20->=202x?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/paillier_decryption_modulo_q.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/paillier_decryption_modulo_q.rs b/src/paillier_decryption_modulo_q.rs index c4ac243..bfab923 100644 --- a/src/paillier_decryption_modulo_q.rs +++ b/src/paillier_decryption_modulo_q.rs @@ -163,8 +163,8 @@ pub mod interactive { security: &SecurityParams, mut rng: R, ) -> Result<(Commitment, PrivateCommitment), ProtocolError> { - let two_to_l_e = BigNumber::one() << (security.l + security.epsilon); - let modulo_l = (BigNumber::one() << security.l) * &aux.rsa_modulo; + let two_to_l_e = BigNumber::one() << (security.l + security.epsilon + 1); + let modulo_l = (BigNumber::one() << security.l + 1) * &aux.rsa_modulo; let modulo_l_e = &two_to_l_e * &aux.rsa_modulo; let alpha = BigNumber::from_rng(&two_to_l_e, &mut rng); @@ -188,7 +188,9 @@ pub mod interactive { /// Generate random challenge pub fn challenge(data: &Data, rng: &mut R) -> Challenge { - BigNumber::from_rng(&data.q, rng) + // double the range to account for +- + let m = BigNumber::from(2) * &data.q; + BigNumber::from_rng(&m, rng) } /// Compute proof for given data and prior protocol values @@ -314,7 +316,8 @@ pub mod non_interactive { .chain_update(&commitment.gamma.to_bytes()) .finalize(); let mut rng = rand_chacha::ChaCha20Rng::from_seed(seed.into()); - BigNumber::from_rng(&data.q, &mut rng) + let m = BigNumber::from(2) * &data.q; + BigNumber::from_rng(&m, &mut rng) } pub fn verify( @@ -492,4 +495,10 @@ mod test { panic!("proof should not pass"); } } + + // Following motivation outlined in + // [crate::paillier_encryption_in_range::test::rejected_with_probability_1_over_2], + // I would like to make a similar borderline test, but no security estimate + // was given in the paper and this proof differs significantly from others + // in this library, so I have to omit the test. } From 567eb59fa9f732e28757d146f59e020b23c42d5b Mon Sep 17 00:00:00 2001 From: d86leader Date: Wed, 25 Jan 2023 15:19:11 +0100 Subject: [PATCH 09/14] formatting and clippy --- src/paillier_affine_operation_in_range.rs | 76 ++++++++++++++--------- src/paillier_blum_modulus.rs | 4 +- src/paillier_decryption_modulo_q.rs | 24 +++---- src/paillier_encryption_in_range.rs | 31 +++++---- 4 files changed, 75 insertions(+), 60 deletions(-) diff --git a/src/paillier_affine_operation_in_range.rs b/src/paillier_affine_operation_in_range.rs index 76f8bfd..626840c 100644 --- a/src/paillier_affine_operation_in_range.rs +++ b/src/paillier_affine_operation_in_range.rs @@ -258,7 +258,10 @@ pub mod interactive { let modulo_l_e = &two_to_l_e * &aux.rsa_modulo; let alpha = BigNumber::from_rng(&two_to_l_e, &mut rng); - let beta = BigNumber::from_rng(&(BigNumber::one() << (security.l_y + security.epsilon + 1)), &mut rng); + let beta = BigNumber::from_rng( + &(BigNumber::one() << (security.l_y + security.epsilon + 1)), + &mut rng, + ); let r = gen_inversible(data.key0.n(), &mut rng); let r_y = gen_inversible(data.key1.n(), &mut rng); let gamma = BigNumber::from_rng(&modulo_l_e, &mut rng); @@ -493,21 +496,21 @@ pub mod non_interactive { use rand_core::SeedableRng; let seed = shared_state .chain_update(aux.s.to_bytes()) - .chain_update(&aux.t.to_bytes()) - .chain_update(&aux.rsa_modulo.to_bytes()) - .chain_update(&data.key0.to_bytes()) - .chain_update(&data.key1.to_bytes()) - .chain_update(&data.c.to_bytes()) - .chain_update(&data.d.to_bytes()) - .chain_update(&data.y.to_bytes()) - .chain_update(&data.x.to_bytes(true)) - .chain_update(&commitment.a.to_bytes()) - .chain_update(&commitment.b_x.to_bytes(true)) - .chain_update(&commitment.b_y.to_bytes()) - .chain_update(&commitment.e.to_bytes()) - .chain_update(&commitment.s.to_bytes()) - .chain_update(&commitment.f.to_bytes()) - .chain_update(&commitment.t.to_bytes()) + .chain_update(aux.t.to_bytes()) + .chain_update(aux.rsa_modulo.to_bytes()) + .chain_update(data.key0.to_bytes()) + .chain_update(data.key1.to_bytes()) + .chain_update(data.c.to_bytes()) + .chain_update(data.d.to_bytes()) + .chain_update(data.y.to_bytes()) + .chain_update(data.x.to_bytes(true)) + .chain_update(commitment.a.to_bytes()) + .chain_update(commitment.b_x.to_bytes(true)) + .chain_update(commitment.b_y.to_bytes()) + .chain_update(commitment.e.to_bytes()) + .chain_update(commitment.s.to_bytes()) + .chain_update(commitment.f.to_bytes()) + .chain_update(commitment.t.to_bytes()) .chain_update((security.l_x as u64).to_le_bytes()) .chain_update((security.l_y as u64).to_le_bytes()) .chain_update((security.epsilon as u64).to_le_bytes()) @@ -535,7 +538,13 @@ mod test { Some(BigNumber::from_rng(n, rng)) } - fn run(mut rng: R, security: super::SecurityParams, plaintext_orig: BigNumber, plaintext_mult: BigNumber, plaintext_add: BigNumber) -> Result<(), crate::common::InvalidProof> + fn run( + mut rng: R, + security: super::SecurityParams, + plaintext_orig: BigNumber, + plaintext_mult: BigNumber, + plaintext_add: BigNumber, + ) -> Result<(), crate::common::InvalidProof> where Scalar: FromHash, { @@ -546,8 +555,12 @@ mod test { let private_key1 = random_key(&mut rng).unwrap(); let key1 = libpaillier::EncryptionKey::from(&private_key1); let g = generic_ec::Point::::generator(); - let (ciphertext, _) = key0.encrypt(affined.to_bytes(), nonce(&mut rng, key0.n())).unwrap(); - let (ciphertext_orig, _) = key0.encrypt(plaintext_orig.to_bytes(), nonce(&mut rng, key0.n())).unwrap(); + let (ciphertext, _) = key0 + .encrypt(affined.to_bytes(), nonce(&mut rng, key0.n())) + .unwrap(); + let (ciphertext_orig, _) = key0 + .encrypt(plaintext_orig.to_bytes(), nonce(&mut rng, key0.n())) + .unwrap(); let ciphertext_mult = g * convert_scalar(&plaintext_mult); let nonce_y = nonce(&mut rng, key1.n()); let (ciphertext_add, nonce_y) = key1.encrypt(plaintext_add.to_bytes(), nonce_y).unwrap(); @@ -599,14 +612,7 @@ mod test { rng, ) .unwrap(); - super::non_interactive::verify( - shared_state, - &aux, - &data, - &commitment, - &security, - &proof, - ) + super::non_interactive::verify(shared_state, &aux, &data, &commitment, &security, &proof) } fn passing_test() where @@ -716,7 +722,13 @@ mod test { let plaintext_orig = BigNumber::from(100); let plaintext_mult = (BigNumber::from(1) << (security.l_x + 1)) - 1; let plaintext_add = BigNumber::from(1) << (security.l_y / 2); - let r = run::<_, generic_ec_curves::rust_crypto::Secp256r1>(rng, security, plaintext_orig, plaintext_mult, plaintext_add); + let r = run::<_, generic_ec_curves::rust_crypto::Secp256r1>( + rng, + security, + plaintext_orig, + plaintext_mult, + plaintext_add, + ); match r { Ok(()) => true, Err(crate::common::InvalidProof::RangeCheckFailed(6)) => false, @@ -743,7 +755,13 @@ mod test { let plaintext_orig = BigNumber::from(100); let plaintext_mult = BigNumber::from(1) << (security.l_x / 2); let plaintext_add = (BigNumber::from(1) << (security.l_y + 1)) + 1; - let r = run::<_, generic_ec_curves::rust_crypto::Secp256r1>(rng, security, plaintext_orig, plaintext_mult, plaintext_add); + let r = run::<_, generic_ec_curves::rust_crypto::Secp256r1>( + rng, + security, + plaintext_orig, + plaintext_mult, + plaintext_add, + ); match r { Ok(()) => true, Err(crate::common::InvalidProof::RangeCheckFailed(7)) => false, diff --git a/src/paillier_blum_modulus.rs b/src/paillier_blum_modulus.rs index 20282f9..ff462f8 100644 --- a/src/paillier_blum_modulus.rs +++ b/src/paillier_blum_modulus.rs @@ -251,8 +251,8 @@ pub mod non_interactive { for (i, y_ref) in ys.iter_mut().enumerate() { let seed = shared_state .clone() - .chain_update(&n.to_bytes()) - .chain_update(&commitment.w.to_bytes()) + .chain_update(n.to_bytes()) + .chain_update(commitment.w.to_bytes()) .chain_update((i as u64).to_le_bytes()) .finalize(); let mut rng = rand_chacha::ChaCha20Rng::from_seed(seed.into()); diff --git a/src/paillier_decryption_modulo_q.rs b/src/paillier_decryption_modulo_q.rs index bfab923..f7bb26e 100644 --- a/src/paillier_decryption_modulo_q.rs +++ b/src/paillier_decryption_modulo_q.rs @@ -164,7 +164,7 @@ pub mod interactive { mut rng: R, ) -> Result<(Commitment, PrivateCommitment), ProtocolError> { let two_to_l_e = BigNumber::one() << (security.l + security.epsilon + 1); - let modulo_l = (BigNumber::one() << security.l + 1) * &aux.rsa_modulo; + let modulo_l = (BigNumber::one() << (security.l + 1)) * &aux.rsa_modulo; let modulo_l_e = &two_to_l_e * &aux.rsa_modulo; let alpha = BigNumber::from_rng(&two_to_l_e, &mut rng); @@ -303,17 +303,17 @@ pub mod non_interactive { { use rand_core::SeedableRng; let seed = shared_state - .chain_update(&aux.s.to_bytes()) - .chain_update(&aux.t.to_bytes()) - .chain_update(&aux.rsa_modulo.to_bytes()) - .chain_update(&data.q.to_bytes()) - .chain_update(&data.key.to_bytes()) - .chain_update(&data.c.to_bytes()) - .chain_update(&data.x.to_bytes()) - .chain_update(&commitment.s.to_bytes()) - .chain_update(&commitment.t.to_bytes()) - .chain_update(&commitment.a.to_bytes()) - .chain_update(&commitment.gamma.to_bytes()) + .chain_update(aux.s.to_bytes()) + .chain_update(aux.t.to_bytes()) + .chain_update(aux.rsa_modulo.to_bytes()) + .chain_update(data.q.to_bytes()) + .chain_update(data.key.to_bytes()) + .chain_update(data.c.to_bytes()) + .chain_update(data.x.to_bytes()) + .chain_update(commitment.s.to_bytes()) + .chain_update(commitment.t.to_bytes()) + .chain_update(commitment.a.to_bytes()) + .chain_update(commitment.gamma.to_bytes()) .finalize(); let mut rng = rand_chacha::ChaCha20Rng::from_seed(seed.into()); let m = BigNumber::from(2) * &data.q; diff --git a/src/paillier_encryption_in_range.rs b/src/paillier_encryption_in_range.rs index 55159fc..ef98aab 100644 --- a/src/paillier_encryption_in_range.rs +++ b/src/paillier_encryption_in_range.rs @@ -300,14 +300,14 @@ pub mod non_interactive { { use rand_core::SeedableRng; let seed = shared_state - .chain_update(&aux.s.to_bytes()) - .chain_update(&aux.t.to_bytes()) - .chain_update(&aux.rsa_modulo.to_bytes()) - .chain_update(&data.key.to_bytes()) - .chain_update(&data.ciphertext.to_bytes()) - .chain_update(&commitment.s.to_bytes()) - .chain_update(&commitment.a.to_bytes()) - .chain_update(&commitment.c.to_bytes()) + .chain_update(aux.s.to_bytes()) + .chain_update(aux.t.to_bytes()) + .chain_update(aux.rsa_modulo.to_bytes()) + .chain_update(data.key.to_bytes()) + .chain_update(data.ciphertext.to_bytes()) + .chain_update(commitment.s.to_bytes()) + .chain_update(commitment.a.to_bytes()) + .chain_update(commitment.c.to_bytes()) .finalize(); let mut rng = rand_chacha::ChaCha20Rng::from_seed(seed.into()); let m = BigNumber::from(2) * &security.q; @@ -335,7 +335,11 @@ mod test { use crate::common::InvalidProof; use crate::unknown_order::BigNumber; - fn run_with(rng: R, security: super::SecurityParams, plaintext: BigNumber) -> Result<(), crate::common::InvalidProof> { + fn run_with( + rng: R, + security: super::SecurityParams, + plaintext: BigNumber, + ) -> Result<(), crate::common::InvalidProof> { let p = BigNumber::prime(1024); let q = BigNumber::prime(1024); let private_key = libpaillier::DecryptionKey::with_primes(&p, &q).unwrap(); @@ -362,14 +366,7 @@ mod test { &security, rng, ); - super::non_interactive::verify( - shared_state, - &aux, - &data, - &commitment, - &security, - &proof, - ) + super::non_interactive::verify(shared_state, &aux, &data, &commitment, &security, &proof) } #[test] From 9e72fbc76f423b5a7b51551003876bbb7665415c Mon Sep 17 00:00:00 2001 From: d86leader Date: Thu, 26 Jan 2023 14:56:44 +0100 Subject: [PATCH 10/14] Small review fixes --- Cargo.toml | 3 ++- src/group_element_vs_paillier_encryption_in_range.rs | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4e7d1fb..055b865 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,8 @@ generic-ec-curves = { git = "https://github.com/dfns-labs/generic-ec", branch = [features] default = ["rust"] gmp = ["libpaillier/gmp"] -openssl = ["libpaillier/openssl"] +# openssl random generation doesn't respect the rng supplied from rust, and so shouldn't be used +#openssl = ["libpaillier/openssl"] rust = ["libpaillier/rust"] serde = ["dep:serde", "generic-ec/serde"] diff --git a/src/group_element_vs_paillier_encryption_in_range.rs b/src/group_element_vs_paillier_encryption_in_range.rs index 905ed6d..ade9c92 100644 --- a/src/group_element_vs_paillier_encryption_in_range.rs +++ b/src/group_element_vs_paillier_encryption_in_range.rs @@ -276,9 +276,8 @@ pub mod interactive { /// Generate random challenge /// /// `data` parameter is used to generate challenge in correct range - pub fn challenge(rng: &mut R, security: &SecurityParams) -> BigNumber + pub fn challenge(rng: &mut R, security: &SecurityParams) -> BigNumber where - C: Curve, R: RngCore, { // double the range to account for +- From 7bbf4f2cfd6f807a1ed5e36ed45f1863adf715a4 Mon Sep 17 00:00:00 2001 From: d86leader Date: Thu, 26 Jan 2023 15:00:34 +0100 Subject: [PATCH 11/14] Forbid missing docs --- Cargo.lock | 85 --------------------------------------------------- src/common.rs | 2 ++ src/lib.rs | 1 + 3 files changed, 3 insertions(+), 85 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 776b079..e736411 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,12 +26,6 @@ version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "block-buffer" version = "0.10.3" @@ -41,12 +35,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "cc" -version = "1.0.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" - [[package]] name = "cfg-if" version = "1.0.0" @@ -238,21 +226,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "generic-array" version = "0.14.6" @@ -434,51 +407,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "once_cell" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" - -[[package]] -name = "openssl" -version = "0.10.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29d971fd5722fec23977260f6e81aa67d2f22cadbdc2aa049f1022d9a3be1566" -dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "openssl-sys" -version = "0.9.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5454462c0eced1e97f2ec09036abc8da362e66802f66fd20f86854d9d8cbcbc4" -dependencies = [ - "autocfg", - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "p256" version = "0.11.1" @@ -525,12 +453,6 @@ dependencies = [ "spki", ] -[[package]] -name = "pkg-config" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" - [[package]] name = "ppv-lite86" version = "0.2.17" @@ -785,7 +707,6 @@ dependencies = [ "num-bigint", "num-integer", "num-traits", - "openssl", "rand", "rug", "serde", @@ -793,12 +714,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version_check" version = "0.9.4" diff --git a/src/common.rs b/src/common.rs index 4ff6625..dde948c 100644 --- a/src/common.rs +++ b/src/common.rs @@ -49,6 +49,8 @@ pub enum InvalidProof { EncryptionFailed, } +/// Unexpeted error that can happen in a protocol. You should probably panic if +/// you see this. #[derive(Debug, PartialEq, Eq)] pub enum ProtocolError { /// Encryption of supplied data failed when computing proof diff --git a/src/lib.rs b/src/lib.rs index 5925b17..ce7a448 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +#[forbid(missing_docs)] mod common; pub mod group_element_vs_paillier_encryption_in_range; pub mod paillier_affine_operation_in_range; From 55f0fdbef2858e6af30359ec2989d83a6a7088b0 Mon Sep 17 00:00:00 2001 From: d86leader Date: Thu, 26 Jan 2023 15:13:07 +0100 Subject: [PATCH 12/14] Compile num-bigint with O3 always Otherwise the performance is cripplingly bad --- Cargo.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 055b865..6584f37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,3 +30,8 @@ gmp = ["libpaillier/gmp"] rust = ["libpaillier/rust"] serde = ["dep:serde", "generic-ec/serde"] + +[profile.dev.package.num-bigint] +opt-level = 3 +[profile.dev.package.glass_pumpkin] +opt-level = 3 From 3106df2bc48a2d85e1216102c297b00a767f9bc1 Mon Sep 17 00:00:00 2001 From: d86leader Date: Thu, 26 Jan 2023 16:50:50 +0100 Subject: [PATCH 13/14] Add docs for every struct and function --- src/common.rs | 3 ++- ...element_vs_paillier_encryption_in_range.rs | 16 +++++++++++++ src/lib.rs | 1 - src/paillier_affine_operation_in_range.rs | 14 ++++++++--- src/paillier_blum_modulus.rs | 23 ++++++++++++++++--- src/paillier_decryption_modulo_q.rs | 15 +++++++++--- src/paillier_encryption_in_range.rs | 18 ++++++++++++--- 7 files changed, 76 insertions(+), 14 deletions(-) diff --git a/src/common.rs b/src/common.rs index dde948c..a66d66f 100644 --- a/src/common.rs +++ b/src/common.rs @@ -38,7 +38,8 @@ pub fn convert_scalar(x: &BigNumber) -> generic_ec::Scalar generic_ec::Scalar::::from_be_bytes_mod_order(x.to_bytes()) } -/// Reason for failure. Mainly interesting for debugging purposes +/// Reason for failure. If the proof failes, you should only be interested in a +/// reason for debugging purposes #[derive(Debug, PartialEq, Eq)] pub enum InvalidProof { /// One equality doesn't hold. Parameterized by equality index diff --git a/src/group_element_vs_paillier_encryption_in_range.rs b/src/group_element_vs_paillier_encryption_in_range.rs index ade9c92..576f080 100644 --- a/src/group_element_vs_paillier_encryption_in_range.rs +++ b/src/group_element_vs_paillier_encryption_in_range.rs @@ -93,6 +93,8 @@ use serde::{Deserialize, Serialize}; use crate::unknown_order::BigNumber; +/// Security parameters for proof. Choosing the values is a tradeoff between +/// speed and chance of rejecting a valid proof or accepting an invalid proof #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct SecurityParams { @@ -104,6 +106,7 @@ pub struct SecurityParams { pub q: BigNumber, } +/// Public data that both parties know #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(bound = ""))] pub struct Data { @@ -117,6 +120,7 @@ pub struct Data { pub g: Point, } +/// Private data of prover #[derive(Clone)] pub struct PrivateData { /// x in paper, logarithm of X and plaintext of C @@ -125,6 +129,7 @@ pub struct PrivateData { pub nonce: Nonce, } +/// Prover's first message, obtained by [`interactive::commit`] #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(bound = ""))] pub struct Commitment { @@ -134,6 +139,8 @@ pub struct Commitment { pub d: BigNumber, } +/// Prover's data accompanying the commitment. Kept as state between rounds in +/// the interactive protocol. #[derive(Clone)] pub struct PrivateCommitment { pub alpha: BigNumber, @@ -142,8 +149,12 @@ pub struct PrivateCommitment { pub gamma: BigNumber, } +/// Verifier's challenge to prover. Can be obtained deterministically by +/// [`non_interactive::challenge`] or randomly by [`interactive::challenge`] pub type Challenge = BigNumber; +/// The ZK proof. Computed by [`interactive::prove`] or +/// [`non_interactive::prove`] #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Proof { @@ -154,6 +165,9 @@ pub struct Proof { pub use crate::common::Aux; +/// The interactive version of the ZK proof. Should be completed in 3 rounds: +/// prover commits to data, verifier responds with a random challenge, and +/// prover gives proof with commitment and challenge. pub mod interactive { use generic_ec::Curve; use libpaillier::unknown_order::BigNumber; @@ -286,6 +300,8 @@ pub mod interactive { } } +/// The non-interactive version of proof. Completed in one round, for example +/// see the documentation of parent module. pub mod non_interactive { use generic_ec::{hash_to_curve::FromHash, Curve, Scalar}; use libpaillier::unknown_order::BigNumber; diff --git a/src/lib.rs b/src/lib.rs index ce7a448..5925b17 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,3 @@ -#[forbid(missing_docs)] mod common; pub mod group_element_vs_paillier_encryption_in_range; pub mod paillier_affine_operation_in_range; diff --git a/src/paillier_affine_operation_in_range.rs b/src/paillier_affine_operation_in_range.rs index 626840c..be8b6de 100644 --- a/src/paillier_affine_operation_in_range.rs +++ b/src/paillier_affine_operation_in_range.rs @@ -144,6 +144,8 @@ use serde::{Deserialize, Serialize}; pub use crate::common::InvalidProof; use crate::unknown_order::BigNumber; +/// Security parameters for proof. Choosing the values is a tradeoff between +/// speed and chance of rejecting a valid proof or accepting an invalid proof #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct SecurityParams { @@ -189,7 +191,7 @@ pub struct PrivateData { } // As described in cggmp21 at page 35 -/// Prover's first message, obtained by `commit` +/// Prover's first message, obtained by [`interactive::commit`] #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(bound = ""))] pub struct Commitment { @@ -217,10 +219,11 @@ pub struct PrivateCommitment { } /// Verifier's challenge to prover. Can be obtained deterministically by -/// `challenge` +/// [`non_interactive::challenge`] or randomly by [`interactive::challenge`] pub type Challenge = BigNumber; -/// The ZK proof. Computed by `prove` +/// The ZK proof. Computed by [`interactive::prove`] or +/// [`non_interactive::prove`] #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Proof { @@ -234,6 +237,9 @@ pub struct Proof { pub use crate::common::Aux; +/// The interactive version of the ZK proof. Should be completed in 3 rounds: +/// prover commits to data, verifier responds with a random challenge, and +/// prover gives proof with commitment and challenge. pub mod interactive { use crate::unknown_order::BigNumber; @@ -432,6 +438,8 @@ pub mod interactive { } } +/// The non-interactive version of proof. Completed in one round, for example +/// see the documentation of parent module. pub mod non_interactive { use crate::unknown_order::BigNumber; diff --git a/src/paillier_blum_modulus.rs b/src/paillier_blum_modulus.rs index ff462f8..038e35a 100644 --- a/src/paillier_blum_modulus.rs +++ b/src/paillier_blum_modulus.rs @@ -67,11 +67,17 @@ use serde::{Deserialize, Serialize}; use crate::unknown_order::BigNumber; +/// Reason for failure. If the proof failes, you should only be interested in a +/// reason for debugging purposes #[derive(Debug, PartialEq, Eq)] pub enum InvalidProof { + /// Paillier-Blum modulus is prime ModulusIsPrime, + /// Paillier-Blum modulus ModulusIsEven, + /// Proof's z value in n-th power does not equal commitment value IncorrectNthRoot, + /// Proof's x value in 4-th power does not equal commitment value IncorrectFourthRoot, } @@ -89,7 +95,7 @@ pub struct PrivateData { pub q: BigNumber, } -/// Prover's first message, obtained by `commit` +/// Prover's first message, obtained by [`interactive::commit`] #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Commitment { @@ -97,7 +103,7 @@ pub struct Commitment { } /// Verifier's challenge to prover. Can be obtained deterministically by -/// `challenge` +/// [`non_interactive::challenge`] or randomly by [`interactive::challenge`] /// /// Consists of `M` singular challenges #[derive(Debug, PartialEq, Eq, Clone)] @@ -105,6 +111,7 @@ pub struct Challenge { pub ys: [BigNumber; M], } +/// A part of proof. Having enough of those guarantees security #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct ProofPoint { @@ -114,12 +121,16 @@ pub struct ProofPoint { pub z: BigNumber, } -/// The ZK proof. Computed by `prove`. Consists of M proofs for each challenge +/// The ZK proof. Computed by [`interactive::prove`] or +/// [`non_interactive::prove`]. Consists of M proofs for each challenge #[derive(Debug, Clone)] pub struct Proof { pub points: [ProofPoint; M], } +/// The interactive version of the ZK proof. Should be completed in 3 rounds: +/// prover commits to data, verifier responds with a random challenge, and +/// prover gives proof with commitment and challenge. pub mod interactive { use crate::unknown_order::BigNumber; use rand_core::RngCore; @@ -187,6 +198,9 @@ pub mod interactive { Ok(()) } + /// Generate random challenge + /// + /// `data` parameter is used to generate challenge in correct range pub fn challenge( Data { ref n }: &Data, rng: &mut R, @@ -196,6 +210,8 @@ pub mod interactive { } } +/// The non-interactive version of proof. Completed in one round, for example +/// see the documentation of parent module. pub mod non_interactive { use crate::unknown_order::BigNumber; use rand_core::RngCore; @@ -222,6 +238,7 @@ pub mod non_interactive { (commitment, proof) } + /// Verify the proof, deriving challenge independently from same data pub fn verify( shared_state: D, data: &Data, diff --git a/src/paillier_decryption_modulo_q.rs b/src/paillier_decryption_modulo_q.rs index f7bb26e..6dfbd04 100644 --- a/src/paillier_decryption_modulo_q.rs +++ b/src/paillier_decryption_modulo_q.rs @@ -78,6 +78,8 @@ use serde::{Deserialize, Serialize}; pub use crate::common::InvalidProof; use crate::unknown_order::BigNumber; +/// Security parameters for proof. Choosing the values is a tradeoff between +/// speed and chance of rejecting a valid proof or accepting an invalid proof #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct SecurityParams { @@ -112,7 +114,7 @@ pub struct PrivateData { #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -/// Prover's first message, obtained by `commit` +/// Prover's first message, obtained by [`interactive::commit`] pub struct Commitment { pub s: BigNumber, pub t: BigNumber, @@ -131,10 +133,11 @@ pub struct PrivateCommitment { } /// Verifier's challenge to prover. Can be obtained deterministically by -/// `challenge` +/// [`non_interactive::challenge`] or randomly by [`interactive::challenge`] pub type Challenge = BigNumber; -/// The ZK proof. Computed by `prove` +/// The ZK proof. Computed by [`interactive::prove`] or +/// [`non_interactive::prove`]. Consists of M proofs for each challenge #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Proof { @@ -145,6 +148,9 @@ pub struct Proof { pub use crate::common::Aux; +/// The interactive version of the ZK proof. Should be completed in 3 rounds: +/// prover commits to data, verifier responds with a random challenge, and +/// prover gives proof with commitment and challenge. pub mod interactive { use crate::unknown_order::BigNumber; use rand_core::RngCore; @@ -261,6 +267,8 @@ pub mod interactive { } } +/// The non-interactive version of proof. Completed in one round, for example +/// see the documentation of parent module. pub mod non_interactive { use crate::unknown_order::BigNumber; use rand_core::RngCore; @@ -320,6 +328,7 @@ pub mod non_interactive { BigNumber::from_rng(&m, &mut rng) } + /// Verify the proof, deriving challenge independently from same data pub fn verify( shared_state: D, aux: &Aux, diff --git a/src/paillier_encryption_in_range.rs b/src/paillier_encryption_in_range.rs index ef98aab..9c47398 100644 --- a/src/paillier_encryption_in_range.rs +++ b/src/paillier_encryption_in_range.rs @@ -79,6 +79,8 @@ use serde::{Deserialize, Serialize}; pub use crate::common::InvalidProof; use crate::unknown_order::BigNumber; +/// Security parameters for proof. Choosing the values is a tradeoff between +/// speed and chance of rejecting a valid proof or accepting an invalid proof #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct SecurityParams { @@ -111,7 +113,7 @@ pub struct PrivateData { } // As described in cggmp21 at page 33 -/// Prover's first message, obtained by `commit` +/// Prover's first message, obtained by [`interactive::commit`] #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Commitment { @@ -131,11 +133,12 @@ pub struct PrivateCommitment { } /// Verifier's challenge to prover. Can be obtained deterministically by -/// `challenge` +/// [`non_interactive::challenge`] or randomly by [`interactive::challenge`] pub type Challenge = BigNumber; // As described in cggmp21 at page 33 -/// The ZK proof. Computed by `prove` +/// The ZK proof. Computed by [`interactive::prove`] or +/// [`non_interactive::prove`] #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Proof { @@ -146,6 +149,9 @@ pub struct Proof { pub use crate::common::Aux; +/// The interactive version of the ZK proof. Should be completed in 3 rounds: +/// prover commits to data, verifier responds with a random challenge, and +/// prover gives proof with commitment and challenge. pub mod interactive { use crate::unknown_order::BigNumber; use rand_core::RngCore; @@ -250,6 +256,9 @@ pub mod interactive { Ok(()) } + /// Generate random challenge + /// + /// `security` parameter is used to generate challenge in correct range pub fn challenge(security: &SecurityParams, rng: &mut R) -> Challenge { // double the range to account for +- let m = BigNumber::from(2) * &security.q; @@ -257,6 +266,8 @@ pub mod interactive { } } +/// The non-interactive version of proof. Completed in one round, for example +/// see the documentation of parent module. pub mod non_interactive { use crate::unknown_order::BigNumber; use rand_core::RngCore; @@ -314,6 +325,7 @@ pub mod non_interactive { BigNumber::from_rng(&m, &mut rng) } + /// Verify the proof, deriving challenge independently from same data pub fn verify( shared_state: D, aux: &Aux, From c79e62ffea2b5e7d68df06cc29e0b82b70e4220a Mon Sep 17 00:00:00 2001 From: d86leader Date: Thu, 26 Jan 2023 17:02:23 +0100 Subject: [PATCH 14/14] =?UTF-8?q?Fix=20non-determinism=20in=20=D0=9Fenc=20?= =?UTF-8?q?tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common.rs | 12 ++++++- ...element_vs_paillier_encryption_in_range.rs | 11 +------ src/paillier_affine_operation_in_range.rs | 11 +------ src/paillier_encryption_in_range.rs | 31 ++++++++++--------- 4 files changed, 29 insertions(+), 36 deletions(-) diff --git a/src/common.rs b/src/common.rs index a66d66f..d1bf7fa 100644 --- a/src/common.rs +++ b/src/common.rs @@ -61,7 +61,7 @@ pub enum ProtocolError { } #[cfg(test)] -mod test { +pub mod test { use libpaillier::unknown_order::BigNumber; #[test] @@ -75,4 +75,14 @@ mod test { let scalar2 = super::convert_scalar(&bignumber); assert_eq!(scalar1, scalar2); } + + pub fn random_key(rng: &mut R) -> Option { + let p = BigNumber::prime_from_rng(1024, rng); + let q = BigNumber::prime_from_rng(1024, rng); + libpaillier::DecryptionKey::with_primes_unchecked(&p, &q) + } + + pub fn nonce(rng: &mut R, n: &BigNumber) -> Option { + Some(BigNumber::from_rng(n, rng)) + } } diff --git a/src/group_element_vs_paillier_encryption_in_range.rs b/src/group_element_vs_paillier_encryption_in_range.rs index 576f080..4f0f9a7 100644 --- a/src/group_element_vs_paillier_encryption_in_range.rs +++ b/src/group_element_vs_paillier_encryption_in_range.rs @@ -391,18 +391,9 @@ mod test { use generic_ec::{hash_to_curve::FromHash, Curve, Scalar}; use libpaillier::unknown_order::BigNumber; + use crate::common::test::{nonce, random_key}; use crate::common::{convert_scalar, InvalidProof}; - fn random_key(rng: &mut R) -> Option { - let p = BigNumber::prime_from_rng(1024, rng); - let q = BigNumber::prime_from_rng(1024, rng); - libpaillier::DecryptionKey::with_primes_unchecked(&p, &q) - } - - fn nonce(rng: &mut R, n: &BigNumber) -> Option { - Some(BigNumber::from_rng(n, rng)) - } - fn run( mut rng: R, security: super::SecurityParams, diff --git a/src/paillier_affine_operation_in_range.rs b/src/paillier_affine_operation_in_range.rs index be8b6de..fc29f79 100644 --- a/src/paillier_affine_operation_in_range.rs +++ b/src/paillier_affine_operation_in_range.rs @@ -534,18 +534,9 @@ mod test { use generic_ec::{hash_to_curve::FromHash, Curve, Scalar}; use crate::common::convert_scalar; + use crate::common::test::{nonce, random_key}; use crate::unknown_order::BigNumber; - fn random_key(rng: &mut R) -> Option { - let p = BigNumber::prime_from_rng(1024, rng); - let q = BigNumber::prime_from_rng(1024, rng); - libpaillier::DecryptionKey::with_primes_unchecked(&p, &q) - } - - fn nonce(rng: &mut R, n: &BigNumber) -> Option { - Some(BigNumber::from_rng(n, rng)) - } - fn run( mut rng: R, security: super::SecurityParams, diff --git a/src/paillier_encryption_in_range.rs b/src/paillier_encryption_in_range.rs index 9c47398..568f5ee 100644 --- a/src/paillier_encryption_in_range.rs +++ b/src/paillier_encryption_in_range.rs @@ -348,20 +348,19 @@ mod test { use crate::unknown_order::BigNumber; fn run_with( - rng: R, + mut rng: R, security: super::SecurityParams, plaintext: BigNumber, ) -> Result<(), crate::common::InvalidProof> { - let p = BigNumber::prime(1024); - let q = BigNumber::prime(1024); - let private_key = libpaillier::DecryptionKey::with_primes(&p, &q).unwrap(); + let private_key = crate::common::test::random_key(&mut rng).unwrap(); let key = libpaillier::EncryptionKey::from(&private_key); - let (ciphertext, nonce) = key.encrypt(plaintext.to_bytes(), None).unwrap(); + let nonce = crate::common::test::nonce(&mut rng, key.n()); + let (ciphertext, nonce) = key.encrypt(plaintext.to_bytes(), nonce).unwrap(); let data = super::Data { key, ciphertext }; let pdata = super::PrivateData { plaintext, nonce }; - let p = BigNumber::prime(1024); - let q = BigNumber::prime(1024); + let p = BigNumber::prime_from_rng(1024, &mut rng); + let q = BigNumber::prime_from_rng(1024, &mut rng); let rsa_modulo = p * q; let s: BigNumber = 123.into(); let t: BigNumber = 321.into(); @@ -383,13 +382,14 @@ mod test { #[test] fn passing() { + let mut rng = rand_core::OsRng::default(); let security = super::SecurityParams { l: 1024, epsilon: 256, - q: BigNumber::prime(128), + q: BigNumber::prime_from_rng(128, &mut rng), }; let plaintext = (BigNumber::one() << (security.l + 1)) - 1; - let r = run_with(rand_core::OsRng::default(), security, plaintext); + let r = run_with(rng, security, plaintext); match r { Ok(()) => (), Err(e) => panic!("{:?}", e), @@ -397,13 +397,14 @@ mod test { } #[test] fn failing() { + let mut rng = rand_core::OsRng::default(); let security = super::SecurityParams { l: 1024, epsilon: 256, - q: BigNumber::prime(128), + q: BigNumber::prime_from_rng(128, &mut rng), }; let plaintext = (BigNumber::one() << (security.l + security.epsilon + 1)) + 1; - let r = run_with(rand_core::OsRng::default(), security, plaintext); + let r = run_with(rng, security, plaintext); match r { Ok(()) => panic!("proof should not pass"), Err(InvalidProof::RangeCheckFailed(_)) => (), @@ -422,11 +423,11 @@ mod test { // artifact of distribution, so I decide to ignore it, and test that // there are passing and failing values. - fn maybe_rejected(rng: rand_chacha::ChaCha20Rng) -> bool { + fn maybe_rejected(mut rng: rand_chacha::ChaCha20Rng) -> bool { let security = super::SecurityParams { l: 512, epsilon: 129, - q: BigNumber::prime(128), + q: BigNumber::prime_from_rng(128, &mut rng), }; let plaintext: BigNumber = (BigNumber::one() << (security.l + 1)) - 1; let r = run_with(rng, security, plaintext); @@ -438,9 +439,9 @@ mod test { } use rand_core::SeedableRng; - let rng = rand_chacha::ChaCha20Rng::seed_from_u64(8); + let rng = rand_chacha::ChaCha20Rng::seed_from_u64(1); assert!(maybe_rejected(rng), "should pass"); - let rng = rand_chacha::ChaCha20Rng::seed_from_u64(9); + let rng = rand_chacha::ChaCha20Rng::seed_from_u64(2); assert!(!maybe_rejected(rng), "should fail"); } }