From 223b610165efe35852511807e1f340de1a0f00df Mon Sep 17 00:00:00 2001 From: d86leader Date: Tue, 2 May 2023 18:20:30 +0200 Subject: [PATCH 1/7] Add rng for use for fiat-shamir challenge deriving --- src/common.rs | 1 + src/common/rng.rs | 119 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 src/common/rng.rs diff --git a/src/common.rs b/src/common.rs index 7c84a72..47373b0 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,4 +1,5 @@ pub mod sqrt; +pub mod rng; use crate::unknown_order::BigNumber; diff --git a/src/common/rng.rs b/src/common/rng.rs new file mode 100644 index 0000000..6750c3a --- /dev/null +++ b/src/common/rng.rs @@ -0,0 +1,119 @@ +use sha2::digest; +use sha2::digest::Digest; + +pub struct HashRng { + hash: F, + counter: u64, + buffer: digest::Output, + offset: usize, +} + +impl HashRng +{ + pub fn new(hash: F) -> Self + where + F: Fn(D) -> digest::Output, + { + let d: D = D::new().chain_update(0u64.to_le_bytes()); + let buffer: digest::Output = hash(d); + HashRng { + hash, + counter: 1, + offset: 0, + buffer, + } + } +} + +impl rand_core::RngCore for HashRng +where + D: Digest, + digest::Output: std::borrow::Borrow<[u8]>, + F: Fn(D) -> digest::Output, +{ + fn next_u32(&mut self) -> u32 { + use std::borrow::Borrow; + + const SIZE: usize = std::mem::size_of::(); + if self.offset + SIZE > self.buffer.borrow().len() { + self.buffer = (self.hash) (D::new().chain_update(self.counter.to_le_bytes())); + self.counter += 1; + self.offset = 0; + } + let bytes = &self.buffer.borrow()[self.offset .. self.offset + SIZE]; + self.offset += SIZE; + let bytes: [u8; SIZE] = bytes.try_into().unwrap(); + u32::from_le_bytes(bytes) + } + + fn next_u64(&mut self) -> u64 { + rand_core::impls::next_u64_via_u32(self) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + rand_core::impls::fill_bytes_via_next(self, dest) + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> { + Ok(self.fill_bytes(dest)) + } +} + +#[cfg(test)] +mod test { + use rand_core::RngCore; + use sha2::Digest; + + #[test] + fn generate_bytes() { + let hash = |d: sha2::Sha256| d + .chain_update("foobar") + .finalize(); + let mut rng = super::HashRng::new(hash); + + let mut zeroes = 0; + let mut total = 0; + + let mut bytes = [0; 32]; + rng.fill_bytes(&mut bytes); + total += bytes.len(); + zeroes += bytes.iter().filter(|b| **b == 0).count(); + assert!(zeroes <= total + 256 / 256); + + let mut bytes = [0; 128]; + rng.fill_bytes(&mut bytes); + total += bytes.len(); + zeroes += bytes.iter().filter(|b| **b == 0).count(); + assert!(zeroes <= total + 256 / 256); + + let mut bytes = [0; 1]; + rng.fill_bytes(&mut bytes); + total += bytes.len(); + zeroes += bytes.iter().filter(|b| **b == 0).count(); + assert!(zeroes <= total + 256 / 256); + + let mut bytes = [0; 1]; + rng.fill_bytes(&mut bytes); + total += bytes.len(); + zeroes += bytes.iter().filter(|b| **b == 0).count(); + assert!(zeroes <= total + 256 / 256); + + let mut bytes = [0; 32]; + rng.fill_bytes(&mut bytes); + total += bytes.len(); + zeroes += bytes.iter().filter(|b| **b == 0).count(); + assert!(zeroes <= total + 256 / 256); + + let mut bytes = [0; 128]; + rng.fill_bytes(&mut bytes); + total += bytes.len(); + zeroes += bytes.iter().filter(|b| **b == 0).count(); + assert!(zeroes <= total + 256 / 256); + + let mut bytes = [0; 137]; + rng.fill_bytes(&mut bytes); + total += bytes.len(); + zeroes += bytes.iter().filter(|b| **b == 0).count(); + assert!(zeroes <= total + 256 / 256); + } +} From 9188d65e304d578544813a47209f8085aea4324b Mon Sep 17 00:00:00 2001 From: d86leader Date: Tue, 2 May 2023 18:59:45 +0200 Subject: [PATCH 2/7] Challenge uses hashing random --- ...oup_element_vs_paillier_encryption_in_range.rs | 11 ++++++----- src/no_small_factor.rs | 9 +++++---- src/paillier_affine_operation_in_range.rs | 15 ++++++++------- src/paillier_blum_modulus.rs | 10 +++++----- src/paillier_decryption_modulo_q.rs | 9 +++++---- src/paillier_encryption_in_range.rs | 9 +++++---- 6 files changed, 34 insertions(+), 29 deletions(-) diff --git a/src/group_element_vs_paillier_encryption_in_range.rs b/src/group_element_vs_paillier_encryption_in_range.rs index 6941f13..4d57194 100644 --- a/src/group_element_vs_paillier_encryption_in_range.rs +++ b/src/group_element_vs_paillier_encryption_in_range.rs @@ -343,10 +343,11 @@ pub mod non_interactive { ) -> Challenge where Scalar: FromHash, - D: Digest, + D: Digest, { - use rand_core::SeedableRng; - let seed = shared_state + let shared_state = shared_state.finalize(); + let hash = |d: D| d + .chain_update(&shared_state) .chain_update(C::CURVE_NAME) .chain_update(aux.s.to_bytes()) .chain_update(aux.t.to_bytes()) @@ -362,7 +363,7 @@ pub mod non_interactive { .chain_update(commitment.d.to_bytes()) .finalize(); - let mut rng = rand_chacha::ChaCha20Rng::from_seed(seed.into()); + let mut rng = crate::common::rng::HashRng::new(hash); super::interactive::challenge::(&mut rng) } } @@ -492,7 +493,7 @@ mod test { let rng = rand_chacha::ChaCha20Rng::seed_from_u64(0); assert!(maybe_rejected(rng), "should pass"); - let rng = rand_chacha::ChaCha20Rng::seed_from_u64(1); + let rng = rand_chacha::ChaCha20Rng::seed_from_u64(2); assert!(!maybe_rejected(rng), "should fail"); } } diff --git a/src/no_small_factor.rs b/src/no_small_factor.rs index 8f47cef..75f91ef 100644 --- a/src/no_small_factor.rs +++ b/src/no_small_factor.rs @@ -346,10 +346,11 @@ pub mod non_interactive { security: &SecurityParams, ) -> Challenge where - D: Digest, + D: Digest, { - use rand_core::SeedableRng; - let seed = shared_state + let shared_state = shared_state.finalize(); + let hash = |d: D| d + .chain_update(&shared_state) .chain_update(aux.s.to_bytes()) .chain_update(aux.t.to_bytes()) .chain_update(aux.rsa_modulo.to_bytes()) @@ -362,7 +363,7 @@ pub mod non_interactive { .chain_update(commitment.t.to_bytes()) .chain_update(commitment.sigma.to_bytes()) .finalize(); - let mut rng = rand_chacha::ChaCha20Rng::from_seed(seed.into()); + let mut rng = crate::common::rng::HashRng::new(hash); super::interactive::challenge(security, &mut rng) } diff --git a/src/paillier_affine_operation_in_range.rs b/src/paillier_affine_operation_in_range.rs index 9d7c1e5..e3400a1 100644 --- a/src/paillier_affine_operation_in_range.rs +++ b/src/paillier_affine_operation_in_range.rs @@ -458,10 +458,11 @@ pub mod non_interactive { security: &SecurityParams, ) -> Challenge where - D: Digest, + D: Digest, { - use rand_core::SeedableRng; - let seed = shared_state + let shared_state = shared_state.finalize(); + let hash = |d: D| d + .chain_update(&shared_state) .chain_update(aux.s.to_bytes()) .chain_update(aux.t.to_bytes()) .chain_update(aux.rsa_modulo.to_bytes()) @@ -482,7 +483,7 @@ pub mod non_interactive { .chain_update(commitment.f.to_bytes()) .chain_update(commitment.t.to_bytes()) .finalize(); - let mut rng = rand_chacha::ChaCha20Rng::from_seed(seed.into()); + let mut rng = crate::common::rng::HashRng::new(hash); super::interactive::challenge::(&mut rng) } } @@ -658,7 +659,7 @@ mod test { let mut rng = rand_chacha::ChaCha20Rng::seed_from_u64(5); assert!(maybe_rejected(&mut rng), "should pass"); - let mut rng = rand_chacha::ChaCha20Rng::seed_from_u64(6); + let mut rng = rand_chacha::ChaCha20Rng::seed_from_u64(0); assert!(!maybe_rejected(&mut rng), "should fail"); } @@ -681,10 +682,10 @@ mod test { } } - let rng = rand_chacha::ChaCha20Rng::seed_from_u64(1); + let rng = rand_chacha::ChaCha20Rng::seed_from_u64(0); assert!(maybe_rejected(rng), "should pass"); - let rng = rand_chacha::ChaCha20Rng::seed_from_u64(2); + let rng = rand_chacha::ChaCha20Rng::seed_from_u64(1); assert!(!maybe_rejected(rng), "should fail"); } } diff --git a/src/paillier_blum_modulus.rs b/src/paillier_blum_modulus.rs index 1ccc96a..f1765d5 100644 --- a/src/paillier_blum_modulus.rs +++ b/src/paillier_blum_modulus.rs @@ -257,20 +257,20 @@ pub mod non_interactive { commitment: &Commitment, ) -> Challenge where - D: Digest + Clone, + D: Digest, { - use rand_core::SeedableRng; + let shared_state = shared_state.finalize(); // since we can't use Default and BigNumber isn't copy, we initialize // like this let mut ys = [(); M].map(|()| BigNumber::zero()); for (i, y_ref) in ys.iter_mut().enumerate() { - let seed = shared_state - .clone() + let hash = |d: D| d + .chain_update(&shared_state) .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()); + let mut rng = crate::common::rng::HashRng::new(hash); *y_ref = BigNumber::from_rng(n, &mut rng); } Challenge { ys } diff --git a/src/paillier_decryption_modulo_q.rs b/src/paillier_decryption_modulo_q.rs index 92ad70e..329292d 100644 --- a/src/paillier_decryption_modulo_q.rs +++ b/src/paillier_decryption_modulo_q.rs @@ -289,10 +289,11 @@ pub mod non_interactive { commitment: &Commitment, ) -> Challenge where - D: Digest, + D: Digest, { - use rand_core::SeedableRng; - let seed = shared_state + let shared_state = shared_state.finalize(); + let hash = |d: D| d + .chain_update(&shared_state) .chain_update(aux.s.to_bytes()) .chain_update(aux.t.to_bytes()) .chain_update(aux.rsa_modulo.to_bytes()) @@ -305,7 +306,7 @@ pub mod non_interactive { .chain_update(commitment.a.to_bytes()) .chain_update(commitment.gamma.to_bytes()) .finalize(); - let mut rng = rand_chacha::ChaCha20Rng::from_seed(seed.into()); + let mut rng = crate::common::rng::HashRng::new(hash); super::interactive::challenge(data, &mut rng) } diff --git a/src/paillier_encryption_in_range.rs b/src/paillier_encryption_in_range.rs index 9b50878..479ba30 100644 --- a/src/paillier_encryption_in_range.rs +++ b/src/paillier_encryption_in_range.rs @@ -313,10 +313,11 @@ pub mod non_interactive { security: &SecurityParams, ) -> Challenge where - D: Digest, + D: Digest, { - use rand_core::SeedableRng; - let seed = shared_state + let shared_state = shared_state.finalize(); + let hash = |d: D| d + .chain_update(&shared_state) .chain_update(aux.s.to_bytes()) .chain_update(aux.t.to_bytes()) .chain_update(aux.rsa_modulo.to_bytes()) @@ -326,7 +327,7 @@ pub mod non_interactive { .chain_update(commitment.a.to_bytes()) .chain_update(commitment.c.to_bytes()) .finalize(); - let mut rng = rand_chacha::ChaCha20Rng::from_seed(seed.into()); + let mut rng = crate::common::rng::HashRng::new(hash); super::interactive::challenge(security, &mut rng) } From 8a42a44777449cef9d8cc4e992a04d12ba32958e Mon Sep 17 00:00:00 2001 From: d86leader Date: Tue, 2 May 2023 19:09:40 +0200 Subject: [PATCH 3/7] simpler generation scheme for Rmod --- src/paillier_blum_modulus.rs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/paillier_blum_modulus.rs b/src/paillier_blum_modulus.rs index f1765d5..1ef9d81 100644 --- a/src/paillier_blum_modulus.rs +++ b/src/paillier_blum_modulus.rs @@ -260,19 +260,15 @@ pub mod non_interactive { D: Digest, { let shared_state = shared_state.finalize(); + let hash = |d: D| d + .chain_update(&shared_state) + .chain_update(n.to_bytes()) + .chain_update(commitment.w.to_bytes()) + .finalize(); + let mut rng = crate::common::rng::HashRng::new(hash); // since we can't use Default and BigNumber isn't copy, we initialize // like this - let mut ys = [(); M].map(|()| BigNumber::zero()); - for (i, y_ref) in ys.iter_mut().enumerate() { - let hash = |d: D| d - .chain_update(&shared_state) - .chain_update(n.to_bytes()) - .chain_update(commitment.w.to_bytes()) - .chain_update((i as u64).to_le_bytes()) - .finalize(); - let mut rng = crate::common::rng::HashRng::new(hash); - *y_ref = BigNumber::from_rng(n, &mut rng); - } + let ys = [(); M].map(|()| BigNumber::from_rng(n, &mut rng)); Challenge { ys } } } From 3f9987c58b121b872f71bbe1da2952f639a3cd22 Mon Sep 17 00:00:00 2001 From: d86leader Date: Tue, 2 May 2023 19:16:30 +0200 Subject: [PATCH 4/7] Fmt, clippy and docs --- src/common.rs | 2 +- src/common/rng.rs | 25 +++++---- ...element_vs_paillier_encryption_in_range.rs | 37 +++++++------- src/no_small_factor.rs | 29 ++++++----- src/paillier_affine_operation_in_range.rs | 51 ++++++++++--------- src/paillier_blum_modulus.rs | 11 ++-- src/paillier_decryption_modulo_q.rs | 29 ++++++----- src/paillier_encryption_in_range.rs | 23 +++++---- 8 files changed, 110 insertions(+), 97 deletions(-) diff --git a/src/common.rs b/src/common.rs index 47373b0..5b64f00 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,5 +1,5 @@ -pub mod sqrt; pub mod rng; +pub mod sqrt; use crate::unknown_order::BigNumber; diff --git a/src/common/rng.rs b/src/common/rng.rs index 6750c3a..305b23d 100644 --- a/src/common/rng.rs +++ b/src/common/rng.rs @@ -1,6 +1,9 @@ use sha2::digest; use sha2::digest::Digest; +/// Pseudo-random generateur that obtains values by hashing the provided values +/// salted with an internal counter. The counter is prepended to conserve +/// entropy. pub struct HashRng { hash: F, counter: u64, @@ -8,8 +11,11 @@ pub struct HashRng { offset: usize, } -impl HashRng -{ +impl HashRng { + /// Create the RNG from the hash finalization function. Use it like this: + /// ```ignore + /// HashRng::new(|d| d.chain_update("my_values").finalize()) + /// ``` pub fn new(hash: F) -> Self where F: Fn(D) -> digest::Output, @@ -35,14 +41,16 @@ where use std::borrow::Borrow; const SIZE: usize = std::mem::size_of::(); + // NOTE: careful with SIZE usage, otherwise it panics if self.offset + SIZE > self.buffer.borrow().len() { - self.buffer = (self.hash) (D::new().chain_update(self.counter.to_le_bytes())); + self.buffer = (self.hash)(D::new().chain_update(self.counter.to_le_bytes())); self.counter += 1; self.offset = 0; } - let bytes = &self.buffer.borrow()[self.offset .. self.offset + SIZE]; + let bytes = &self.buffer.borrow()[self.offset..self.offset + SIZE]; self.offset += SIZE; - let bytes: [u8; SIZE] = bytes.try_into().unwrap(); + #[allow(clippy::expect_used)] + let bytes: [u8; SIZE] = bytes.try_into().expect("Size mismatch"); u32::from_le_bytes(bytes) } @@ -55,7 +63,8 @@ where } fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> { - Ok(self.fill_bytes(dest)) + self.fill_bytes(dest); + Ok(()) } } @@ -66,9 +75,7 @@ mod test { #[test] fn generate_bytes() { - let hash = |d: sha2::Sha256| d - .chain_update("foobar") - .finalize(); + let hash = |d: sha2::Sha256| d.chain_update("foobar").finalize(); let mut rng = super::HashRng::new(hash); let mut zeroes = 0; diff --git a/src/group_element_vs_paillier_encryption_in_range.rs b/src/group_element_vs_paillier_encryption_in_range.rs index 4d57194..66afcfe 100644 --- a/src/group_element_vs_paillier_encryption_in_range.rs +++ b/src/group_element_vs_paillier_encryption_in_range.rs @@ -346,22 +346,23 @@ pub mod non_interactive { D: Digest, { let shared_state = shared_state.finalize(); - let hash = |d: D| d - .chain_update(&shared_state) - .chain_update(C::CURVE_NAME) - .chain_update(aux.s.to_bytes()) - .chain_update(aux.t.to_bytes()) - .chain_update(aux.rsa_modulo.to_bytes()) - .chain_update((security.l as u64).to_le_bytes()) - .chain_update((security.epsilon as u64).to_le_bytes()) - .chain_update(data.key0.to_bytes()) - .chain_update(data.c.to_bytes()) - .chain_update(data.x.to_bytes(true)) - .chain_update(commitment.s.to_bytes()) - .chain_update(commitment.a.to_bytes()) - .chain_update(commitment.y.to_bytes(true)) - .chain_update(commitment.d.to_bytes()) - .finalize(); + let hash = |d: D| { + d.chain_update(&shared_state) + .chain_update(C::CURVE_NAME) + .chain_update(aux.s.to_bytes()) + .chain_update(aux.t.to_bytes()) + .chain_update(aux.rsa_modulo.to_bytes()) + .chain_update((security.l as u64).to_le_bytes()) + .chain_update((security.epsilon as u64).to_le_bytes()) + .chain_update(data.key0.to_bytes()) + .chain_update(data.c.to_bytes()) + .chain_update(data.x.to_bytes(true)) + .chain_update(commitment.s.to_bytes()) + .chain_update(commitment.a.to_bytes()) + .chain_update(commitment.y.to_bytes(true)) + .chain_update(commitment.d.to_bytes()) + .finalize() + }; let mut rng = crate::common::rng::HashRng::new(hash); super::interactive::challenge::(&mut rng) @@ -429,7 +430,7 @@ mod test { epsilon: 300, }; let plaintext = BigNumber::from_rng_pm(&(BigNumber::one() << security.l), &mut rng); - run(rng, security, plaintext).expect("proof failed"); + run::<_, C>(rng, security, plaintext).expect("proof failed"); } fn failing_test() @@ -442,7 +443,7 @@ mod test { epsilon: 300, }; let plaintext = BigNumber::from(1) << (security.l + security.epsilon + 1); - let r = run(rng, security, plaintext).expect_err("proof should not pass"); + let r = run::<_, C>(rng, security, plaintext).expect_err("proof should not pass"); match r.reason() { InvalidProofReason::RangeCheck(_) => (), e => panic!("proof should not fail with: {e:?}"), diff --git a/src/no_small_factor.rs b/src/no_small_factor.rs index 75f91ef..67d42e6 100644 --- a/src/no_small_factor.rs +++ b/src/no_small_factor.rs @@ -349,20 +349,21 @@ pub mod non_interactive { D: Digest, { let shared_state = shared_state.finalize(); - let hash = |d: D| d - .chain_update(&shared_state) - .chain_update(aux.s.to_bytes()) - .chain_update(aux.t.to_bytes()) - .chain_update(aux.rsa_modulo.to_bytes()) - .chain_update(data.n.to_bytes()) - .chain_update(data.n_root.to_bytes()) - .chain_update(commitment.p.to_bytes()) - .chain_update(commitment.q.to_bytes()) - .chain_update(commitment.a.to_bytes()) - .chain_update(commitment.b.to_bytes()) - .chain_update(commitment.t.to_bytes()) - .chain_update(commitment.sigma.to_bytes()) - .finalize(); + let hash = |d: D| { + d.chain_update(&shared_state) + .chain_update(aux.s.to_bytes()) + .chain_update(aux.t.to_bytes()) + .chain_update(aux.rsa_modulo.to_bytes()) + .chain_update(data.n.to_bytes()) + .chain_update(data.n_root.to_bytes()) + .chain_update(commitment.p.to_bytes()) + .chain_update(commitment.q.to_bytes()) + .chain_update(commitment.a.to_bytes()) + .chain_update(commitment.b.to_bytes()) + .chain_update(commitment.t.to_bytes()) + .chain_update(commitment.sigma.to_bytes()) + .finalize() + }; let mut rng = crate::common::rng::HashRng::new(hash); super::interactive::challenge(security, &mut rng) } diff --git a/src/paillier_affine_operation_in_range.rs b/src/paillier_affine_operation_in_range.rs index e3400a1..7514793 100644 --- a/src/paillier_affine_operation_in_range.rs +++ b/src/paillier_affine_operation_in_range.rs @@ -461,28 +461,29 @@ pub mod non_interactive { D: Digest, { let shared_state = shared_state.finalize(); - let hash = |d: D| d - .chain_update(&shared_state) - .chain_update(aux.s.to_bytes()) - .chain_update(aux.t.to_bytes()) - .chain_update(aux.rsa_modulo.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()) - .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()) - .finalize(); + let hash = |d: D| { + d.chain_update(&shared_state) + .chain_update(aux.s.to_bytes()) + .chain_update(aux.t.to_bytes()) + .chain_update(aux.rsa_modulo.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()) + .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()) + .finalize() + }; let mut rng = crate::common::rng::HashRng::new(hash); super::interactive::challenge::(&mut rng) } @@ -566,7 +567,7 @@ mod test { }; let x = BigNumber::from_rng_pm(&(BigNumber::one() << security.l_x), &mut rng); let y = BigNumber::from_rng_pm(&(BigNumber::one() << security.l_y), &mut rng); - run(rng, security, x, y).expect("proof failed"); + run::<_, C>(rng, security, x, y).expect("proof failed"); } fn failing_on_additive() @@ -581,7 +582,7 @@ mod test { }; let x = BigNumber::from_rng_pm(&(BigNumber::one() << security.l_x), &mut rng); let y = (BigNumber::one() << (security.l_y + security.epsilon)) + 1; - let r = run(rng, security, x, y).expect_err("proof should not pass"); + let r = run::<_, C>(rng, security, x, y).expect_err("proof should not pass"); match r.reason() { InvalidProofReason::RangeCheck(7) => (), e => panic!("proof should not fail with: {e:?}"), @@ -600,7 +601,7 @@ mod test { }; let x = (BigNumber::from(1) << (security.l_x + security.epsilon)) + 1; let y = BigNumber::from_rng_pm(&(BigNumber::one() << security.l_y), &mut rng); - let r = run(rng, security, x, y).expect_err("proof should not pass"); + let r = run::<_, C>(rng, security, x, y).expect_err("proof should not pass"); match r.reason() { InvalidProofReason::RangeCheck(6) => (), e => panic!("proof should not fail with: {e:?}"), diff --git a/src/paillier_blum_modulus.rs b/src/paillier_blum_modulus.rs index 1ef9d81..a4497db 100644 --- a/src/paillier_blum_modulus.rs +++ b/src/paillier_blum_modulus.rs @@ -260,11 +260,12 @@ pub mod non_interactive { D: Digest, { let shared_state = shared_state.finalize(); - let hash = |d: D| d - .chain_update(&shared_state) - .chain_update(n.to_bytes()) - .chain_update(commitment.w.to_bytes()) - .finalize(); + let hash = |d: D| { + d.chain_update(&shared_state) + .chain_update(n.to_bytes()) + .chain_update(commitment.w.to_bytes()) + .finalize() + }; let mut rng = crate::common::rng::HashRng::new(hash); // since we can't use Default and BigNumber isn't copy, we initialize // like this diff --git a/src/paillier_decryption_modulo_q.rs b/src/paillier_decryption_modulo_q.rs index 329292d..d3333fb 100644 --- a/src/paillier_decryption_modulo_q.rs +++ b/src/paillier_decryption_modulo_q.rs @@ -292,20 +292,21 @@ pub mod non_interactive { D: Digest, { let shared_state = shared_state.finalize(); - let hash = |d: D| d - .chain_update(&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()) - .finalize(); + let hash = |d: D| { + d.chain_update(&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()) + .finalize() + }; let mut rng = crate::common::rng::HashRng::new(hash); super::interactive::challenge(data, &mut rng) } diff --git a/src/paillier_encryption_in_range.rs b/src/paillier_encryption_in_range.rs index 479ba30..8f3cbe2 100644 --- a/src/paillier_encryption_in_range.rs +++ b/src/paillier_encryption_in_range.rs @@ -316,17 +316,18 @@ pub mod non_interactive { D: Digest, { let shared_state = shared_state.finalize(); - let hash = |d: D| d - .chain_update(&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()) - .finalize(); + let hash = |d: D| { + d.chain_update(&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()) + .finalize() + }; let mut rng = crate::common::rng::HashRng::new(hash); super::interactive::challenge(security, &mut rng) } From 50f1cf3731f8c3ef266cd6db657dcbc5f9d803e8 Mon Sep 17 00:00:00 2001 From: d86leader Date: Wed, 3 May 2023 15:20:37 +0200 Subject: [PATCH 5/7] Fix incorrect rounding up causing bogus tests --- src/common/rng.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/common/rng.rs b/src/common/rng.rs index 305b23d..cba6db4 100644 --- a/src/common/rng.rs +++ b/src/common/rng.rs @@ -85,42 +85,42 @@ mod test { rng.fill_bytes(&mut bytes); total += bytes.len(); zeroes += bytes.iter().filter(|b| **b == 0).count(); - assert!(zeroes <= total + 256 / 256); + assert!(zeroes <= (total + 255) / 256); let mut bytes = [0; 128]; rng.fill_bytes(&mut bytes); total += bytes.len(); zeroes += bytes.iter().filter(|b| **b == 0).count(); - assert!(zeroes <= total + 256 / 256); + assert!(zeroes <= (total + 255) / 256); let mut bytes = [0; 1]; rng.fill_bytes(&mut bytes); total += bytes.len(); zeroes += bytes.iter().filter(|b| **b == 0).count(); - assert!(zeroes <= total + 256 / 256); + assert!(zeroes <= (total + 255) / 256); let mut bytes = [0; 1]; rng.fill_bytes(&mut bytes); total += bytes.len(); zeroes += bytes.iter().filter(|b| **b == 0).count(); - assert!(zeroes <= total + 256 / 256); + assert!(zeroes <= (total + 255) / 256); let mut bytes = [0; 32]; rng.fill_bytes(&mut bytes); total += bytes.len(); zeroes += bytes.iter().filter(|b| **b == 0).count(); - assert!(zeroes <= total + 256 / 256); + assert!(zeroes <= (total + 255) / 256); let mut bytes = [0; 128]; rng.fill_bytes(&mut bytes); total += bytes.len(); zeroes += bytes.iter().filter(|b| **b == 0).count(); - assert!(zeroes <= total + 256 / 256); + assert!(zeroes <= (total + 255) / 256); let mut bytes = [0; 137]; rng.fill_bytes(&mut bytes); total += bytes.len(); zeroes += bytes.iter().filter(|b| **b == 0).count(); - assert!(zeroes <= total + 256 / 256); + assert!(zeroes <= (total + 2*255) / 256); } } From c8620d6673b5fb467761e39682202260abe43b12 Mon Sep 17 00:00:00 2001 From: d86leader Date: Wed, 3 May 2023 15:44:47 +0200 Subject: [PATCH 6/7] Remove redundant traits. Simplify tests --- src/common/rng.rs | 63 ++++++++++------------------------------------- 1 file changed, 13 insertions(+), 50 deletions(-) diff --git a/src/common/rng.rs b/src/common/rng.rs index cba6db4..3f877d1 100644 --- a/src/common/rng.rs +++ b/src/common/rng.rs @@ -4,6 +4,9 @@ use sha2::digest::Digest; /// Pseudo-random generateur that obtains values by hashing the provided values /// salted with an internal counter. The counter is prepended to conserve /// entropy. +/// +/// Having u64 counter means that the period of the sequence is 2^64 times +/// `Digest::OutputSize` bytes pub struct HashRng { hash: F, counter: u64, @@ -34,20 +37,17 @@ impl HashRng { impl rand_core::RngCore for HashRng where D: Digest, - digest::Output: std::borrow::Borrow<[u8]>, F: Fn(D) -> digest::Output, { fn next_u32(&mut self) -> u32 { - use std::borrow::Borrow; - const SIZE: usize = std::mem::size_of::(); // NOTE: careful with SIZE usage, otherwise it panics - if self.offset + SIZE > self.buffer.borrow().len() { + if self.offset + SIZE > self.buffer.len() { self.buffer = (self.hash)(D::new().chain_update(self.counter.to_le_bytes())); - self.counter += 1; + self.counter = self.counter.wrapping_add(1); self.offset = 0; } - let bytes = &self.buffer.borrow()[self.offset..self.offset + SIZE]; + let bytes = &self.buffer[self.offset..self.offset + SIZE]; self.offset += SIZE; #[allow(clippy::expect_used)] let bytes: [u8; SIZE] = bytes.try_into().expect("Size mismatch"); @@ -78,49 +78,12 @@ mod test { let hash = |d: sha2::Sha256| d.chain_update("foobar").finalize(); let mut rng = super::HashRng::new(hash); - let mut zeroes = 0; - let mut total = 0; - - let mut bytes = [0; 32]; - rng.fill_bytes(&mut bytes); - total += bytes.len(); - zeroes += bytes.iter().filter(|b| **b == 0).count(); - assert!(zeroes <= (total + 255) / 256); - - let mut bytes = [0; 128]; - rng.fill_bytes(&mut bytes); - total += bytes.len(); - zeroes += bytes.iter().filter(|b| **b == 0).count(); - assert!(zeroes <= (total + 255) / 256); - - let mut bytes = [0; 1]; - rng.fill_bytes(&mut bytes); - total += bytes.len(); - zeroes += bytes.iter().filter(|b| **b == 0).count(); - assert!(zeroes <= (total + 255) / 256); - - let mut bytes = [0; 1]; - rng.fill_bytes(&mut bytes); - total += bytes.len(); - zeroes += bytes.iter().filter(|b| **b == 0).count(); - assert!(zeroes <= (total + 255) / 256); - - let mut bytes = [0; 32]; - rng.fill_bytes(&mut bytes); - total += bytes.len(); - zeroes += bytes.iter().filter(|b| **b == 0).count(); - assert!(zeroes <= (total + 255) / 256); - - let mut bytes = [0; 128]; - rng.fill_bytes(&mut bytes); - total += bytes.len(); - zeroes += bytes.iter().filter(|b| **b == 0).count(); - assert!(zeroes <= (total + 255) / 256); - - let mut bytes = [0; 137]; - rng.fill_bytes(&mut bytes); - total += bytes.len(); - zeroes += bytes.iter().filter(|b| **b == 0).count(); - assert!(zeroes <= (total + 2*255) / 256); + // Check that it doesn't panic for any window size + for _ in 0..100 { + let size = usize::from(rng.next_u32().to_le_bytes()[0]) + 1; + let mut buffer = Vec::new(); + buffer.resize(size, 0); + rng.fill_bytes(&mut buffer); + } } } From 5952860a878956332bea43236cb648f90e914b37 Mon Sep 17 00:00:00 2001 From: d86leader Date: Thu, 4 May 2023 15:28:04 +0200 Subject: [PATCH 7/7] Simpler size generation Co-authored-by: Denis Varlakov --- src/common/rng.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/rng.rs b/src/common/rng.rs index 3f877d1..bc8e363 100644 --- a/src/common/rng.rs +++ b/src/common/rng.rs @@ -80,7 +80,7 @@ mod test { // Check that it doesn't panic for any window size for _ in 0..100 { - let size = usize::from(rng.next_u32().to_le_bytes()[0]) + 1; + let size = (rng.next_u32() as usize) % 256 + 1; let mut buffer = Vec::new(); buffer.resize(size, 0); rng.fill_bytes(&mut buffer);