1use generic_ec::{Curve, Point, Scalar};
77
78#[cfg(feature = "serde")]
79use serde::{Deserialize, Serialize};
80
81pub use crate::common::{Aux, InvalidProof};
82
83#[derive(Debug, Clone, Copy, udigest::Digestable)]
85#[udigest(bound = "")]
86pub struct Data<'a, E: Curve> {
87 pub l: &'a Point<E>,
89 pub m: &'a Point<E>,
91 pub x: &'a Point<E>,
93 pub y: &'a Point<E>,
95 pub h: &'a Point<E>,
97}
98
99#[derive(Clone, Copy)]
101pub struct PrivateData<'a, E: Curve> {
102 pub y: &'a Scalar<E>,
104 pub lambda: &'a Scalar<E>,
106}
107
108#[derive(Debug, Clone, udigest::Digestable)]
110#[udigest(bound = "")]
111#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(bound = ""))]
112pub struct Commitment<E: Curve> {
113 pub a: Point<E>,
114 pub n: Point<E>,
115 pub b: Point<E>,
116}
117
118#[derive(Clone)]
121pub struct PrivateCommitment<E: Curve> {
122 pub alpha: Scalar<E>,
123 pub m: Scalar<E>,
124}
125
126pub type Challenge<E> = Scalar<E>;
129
130#[derive(Debug, Clone)]
132#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(bound = ""))]
133pub struct Proof<E: Curve> {
134 pub z: Scalar<E>,
135 pub u: Scalar<E>,
136}
137
138#[derive(Debug, Clone)]
140#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(bound = ""))]
141pub struct NiProof<E: Curve> {
142 pub commitment: Commitment<E>,
143 pub proof: Proof<E>,
144}
145
146pub mod interactive {
150 use generic_ec::{Curve, Point, Scalar};
151 use rand_core::RngCore;
152
153 use crate::common::{fail_if_ne, InvalidProof, InvalidProofReason};
154 use crate::Error;
155
156 use super::*;
157
158 pub fn commit<E: Curve>(
160 data: Data<E>,
161 rng: &mut impl RngCore,
162 ) -> Result<(Commitment<E>, PrivateCommitment<E>), Error> {
163 let alpha = Scalar::random(rng);
164 let m = Scalar::random(rng);
165
166 let a = Point::<E>::generator() * alpha;
167 let n = Point::<E>::generator() * m + data.x * alpha;
168 let b = data.h * m;
169
170 let commitment = Commitment { a, n, b };
171 let private_commitment = PrivateCommitment { alpha, m };
172 Ok((commitment, private_commitment))
173 }
174
175 pub fn prove<E: Curve>(
177 pdata: PrivateData<E>,
178 pcomm: &PrivateCommitment<E>,
179 challenge: &Challenge<E>,
180 ) -> Result<Proof<E>, Error> {
181 let z = pcomm.alpha + challenge * pdata.lambda;
182 let u = pcomm.m + challenge * pdata.y;
183 Ok(Proof { z, u })
184 }
185
186 pub fn verify<E: Curve>(
188 data: Data<E>,
189 commitment: &Commitment<E>,
190 challenge: &Challenge<E>,
191 proof: &Proof<E>,
192 ) -> Result<(), InvalidProof> {
193 {
195 let lhs = Point::<E>::generator() * proof.z;
196 let rhs = commitment.a + data.l * challenge;
197 fail_if_ne(InvalidProofReason::EqualityCheck(1), lhs, rhs)?;
198 }
199 {
200 let lhs = Point::<E>::generator() * proof.u + data.x * proof.z;
201 let rhs = commitment.n + data.m * challenge;
202 fail_if_ne(InvalidProofReason::EqualityCheck(2), lhs, rhs)?;
203 }
204 {
205 let lhs = data.h * proof.u;
206 let rhs = commitment.b + data.y * challenge;
207 fail_if_ne(InvalidProofReason::EqualityCheck(3), lhs, rhs)?;
208 }
209
210 Ok(())
211 }
212
213 pub fn challenge<E: Curve>(rng: &mut impl RngCore) -> Challenge<E> {
215 Scalar::random(rng)
216 }
217}
218
219pub mod non_interactive {
222 use digest::Digest;
223 use generic_ec::Curve;
224
225 use crate::{Error, InvalidProof};
226
227 use super::{Challenge, Commitment, Data, NiProof, PrivateData};
228
229 pub fn prove<E: Curve, D: Digest>(
234 shared_state: &impl udigest::Digestable,
235 data: Data<E>,
236 pdata: PrivateData<E>,
237 rng: &mut impl rand_core::RngCore,
238 ) -> Result<NiProof<E>, Error> {
239 let (commitment, pcomm) = super::interactive::commit(data, rng)?;
240 let challenge = challenge::<E, D>(shared_state, data, &commitment);
241 let proof = super::interactive::prove::<E>(pdata, &pcomm, &challenge)?;
242 Ok(NiProof { commitment, proof })
243 }
244
245 pub fn verify<E: Curve, D: Digest>(
247 shared_state: &impl udigest::Digestable,
248 data: Data<E>,
249 proof: &NiProof<E>,
250 ) -> Result<(), InvalidProof> {
251 let challenge = challenge::<E, D>(shared_state, data, &proof.commitment);
252 super::interactive::verify::<E>(data, &proof.commitment, &challenge, &proof.proof)
253 }
254
255 pub fn challenge<E: Curve, D: Digest>(
257 shared_state: &impl udigest::Digestable,
258 data: Data<E>,
259 commitment: &Commitment<E>,
260 ) -> Challenge<E> {
261 let tag = "paillier_zk.dlog_with_el_gamal.ni_challenge";
262 let seed = udigest::inline_struct!(tag {
263 shared_state,
264 data,
265 commitment,
266 });
267 let mut rng = rand_hash::HashRng::<D, _>::from_seed(seed);
268 super::interactive::challenge(&mut rng)
269 }
270}
271
272#[cfg(test)]
273mod test {
274 use generic_ec::{Curve, Point, Scalar};
275 use sha2::Digest;
276
277 use crate::common::InvalidProofReason;
278
279 fn run<E: Curve, D: Digest>(
280 rng: &mut impl rand_core::CryptoRngCore,
281 data: super::Data<E>,
282 pdata: super::PrivateData<E>,
283 ) -> Result<(), crate::common::InvalidProof> {
284 let shared_state = "shared state";
285
286 let proof = super::non_interactive::prove::<E, D>(&shared_state, data, pdata, rng).unwrap();
287 super::non_interactive::verify::<E, D>(&shared_state, data, &proof)
288 }
289
290 fn passing_test<E: Curve, D: Digest>() {
291 let mut rng = rand_dev::DevRng::new();
292
293 let pdata = super::PrivateData {
294 y: &Scalar::random(&mut rng),
295 lambda: &Scalar::random(&mut rng),
296 };
297
298 let h = Point::<E>::generator() * Scalar::random(&mut rng);
299 let x = Point::<E>::generator() * Scalar::random(&mut rng);
300
301 let data = super::Data {
302 l: &(Point::<E>::generator() * pdata.lambda),
303 m: &(Point::<E>::generator() * pdata.y + x * pdata.lambda),
304 x: &x,
305 y: &(h * pdata.y),
306 h: &h,
307 };
308 run::<E, D>(&mut rng, data, pdata).expect("proof failed");
309 }
310
311 fn failing_check_lambda_<E: Curve, D: Digest>() {
312 let mut rng = rand_dev::DevRng::new();
314
315 let mut pdata = super::PrivateData {
316 y: &Scalar::random(&mut rng),
317 lambda: &Scalar::random(&mut rng),
318 };
319
320 let h = Point::<E>::generator() * Scalar::random(&mut rng);
321 let x = Point::<E>::generator() * Scalar::random(&mut rng);
322
323 let data = super::Data {
324 l: &(Point::<E>::generator() * pdata.lambda),
325 m: &(Point::<E>::generator() * pdata.y + x * pdata.lambda),
326 x: &x,
327 y: &(h * pdata.y),
328 h: &h,
329 };
330
331 let fake_lambda = Scalar::random(&mut rng);
333 pdata.lambda = &fake_lambda;
334
335 let err = run::<E, D>(&mut rng, data, pdata).expect_err("proof should not pass");
336 match err.reason() {
337 InvalidProofReason::EqualityCheck(1) => (),
338 e => panic!("proof should not fail with {e:?}"),
339 }
340 }
341
342 fn failing_check_y_<E: Curve, D: Digest>() {
343 let mut rng = rand_dev::DevRng::new();
345
346 let mut pdata = super::PrivateData {
347 y: &Scalar::random(&mut rng),
348 lambda: &Scalar::random(&mut rng),
349 };
350
351 let h = Point::<E>::generator() * Scalar::random(&mut rng);
352 let x = Point::<E>::generator() * Scalar::random(&mut rng);
353
354 let data = super::Data {
355 l: &(Point::<E>::generator() * pdata.lambda),
356 m: &(Point::<E>::generator() * pdata.y + x * pdata.lambda),
357 x: &x,
358 y: &(h * pdata.y),
359 h: &h,
360 };
361
362 let fake_y = Scalar::random(&mut rng);
364 pdata.y = &fake_y;
365
366 let r = run::<E, D>(&mut rng, data, pdata).expect_err("proof should not pass");
367 match r.reason() {
368 InvalidProofReason::EqualityCheck(2) => (),
369 e => panic!("proof should not fail with {e:?}"),
370 }
371 }
372
373 #[test]
374 fn passing_p256() {
375 passing_test::<generic_ec::curves::Secp256r1, sha2::Sha256>()
376 }
377
378 #[test]
379 fn passing_million() {
380 passing_test::<crate::curve::C, sha2::Sha256>()
381 }
382 #[test]
383 fn failing_check_1_p256() {
384 failing_check_lambda_::<generic_ec::curves::Secp256r1, sha2::Sha256>()
385 }
386
387 #[test]
388 fn failing_check_1_million() {
389 failing_check_lambda_::<crate::curve::C, sha2::Sha256>()
390 }
391 #[test]
392 fn failing_check_2_p256() {
393 failing_check_y_::<generic_ec::curves::Secp256r1, sha2::Sha256>()
394 }
395
396 #[test]
397 fn failing_check_2_million() {
398 failing_check_y_::<crate::curve::C, sha2::Sha256>()
399 }
400}