1use super::types::{
4 Commitment, DlogProof, PedersenParams, RangeProofBits, SigmaProtocol, ZkProof, ZkStatement,
5 ZkWitness,
6};
7
8pub fn modpow(base: u64, exp: u64, modulus: u64) -> u64 {
14 if modulus == 1 {
15 return 0;
16 }
17 let mut result: u128 = 1;
18 let mut b = (base as u128) % (modulus as u128);
19 let mut e = exp;
20 let m = modulus as u128;
21 while e > 0 {
22 if e & 1 == 1 {
23 result = result * b % m;
24 }
25 b = b * b % m;
26 e >>= 1;
27 }
28 result as u64
29}
30
31fn mulmod(a: u64, b: u64, m: u64) -> u64 {
33 ((a as u128 * b as u128) % m as u128) as u64
34}
35
36fn reduce_mod(x: i128, m: u64) -> u64 {
38 let m_i = m as i128;
39 let r = x % m_i;
40 if r < 0 {
41 (r + m_i) as u64
42 } else {
43 r as u64
44 }
45}
46
47pub fn toy_params() -> PedersenParams {
53 PedersenParams {
54 g: 2,
55 h: 3,
56 p: 1019,
57 }
58}
59
60pub fn pedersen_commit(x: i64, r: u64, params: &PedersenParams) -> Commitment {
64 let p = params.p;
65 let order = p - 1; let x_mod = reduce_mod(x as i128, order);
67 let gx = modpow(params.g, x_mod, p);
68 let hr = modpow(params.h, r, p);
69 let value = mulmod(gx, hr, p);
70 Commitment {
71 value,
72 randomness: r,
73 }
74}
75
76pub fn pedersen_open(c: &Commitment, x: i64, r: u64, params: &PedersenParams) -> bool {
78 let expected = pedersen_commit(x, r, params);
79 expected.value == c.value && c.randomness == r
80}
81
82pub fn dlog_prove(secret: i64, generator: u64, p: u64, seed: u64) -> DlogProof {
95 let order = p - 1;
96 let k_raw = toy_mix(seed, secret as u64) % order;
98 let k = if k_raw == 0 { 1 } else { k_raw };
99 let commitment = modpow(generator, k, p);
100 let secret_mod = reduce_mod(secret as i128, order);
102 let public = modpow(generator, secret_mod, p);
103 let challenge = toy_mix(commitment, public) % order;
104 let s: i64 = (k as i64) - (challenge as i64) * secret;
106 DlogProof {
107 commitment,
108 challenge,
109 response: s,
110 }
111}
112
113pub fn dlog_verify(public: u64, proof: &DlogProof, generator: u64, p: u64) -> bool {
119 let order = p - 1;
120 let s_mod = reduce_mod(proof.response as i128, order);
121 let c_mod = proof.challenge % order;
122 let gs = modpow(generator, s_mod, p);
123 let yc = modpow(public, c_mod, p);
124 let lhs = mulmod(gs, yc, p);
125 lhs == proof.commitment
126}
127
128pub fn sigma_protocol_properties() -> SigmaProtocol {
132 SigmaProtocol {
133 name: "Schnorr".to_string(),
134 completeness: true,
135 soundness: true,
136 zero_knowledge: true,
137 }
138}
139
140pub fn prove_range(
149 x: u64,
150 lo: u64,
151 hi: u64,
152 params: &PedersenParams,
153 seed: u64,
154) -> Option<RangeProofBits> {
155 if x < lo || x > hi {
156 return None;
157 }
158 let v = x - lo;
159 let range_size = hi - lo;
160 let num_bits = if range_size == 0 {
161 1usize
162 } else {
163 (u64::BITS - range_size.leading_zeros()) as usize
164 };
165
166 let mut bits = Vec::with_capacity(num_bits);
167 for i in 0..num_bits {
168 let bit = (v >> i) & 1; let proof = dlog_prove(bit as i64, params.g, params.p, toy_mix(seed, i as u64));
171 bits.push(proof);
172 }
173 Some(RangeProofBits {
174 bits,
175 range: (lo, hi),
176 })
177}
178
179pub fn verify_range_proof(
185 proof: &RangeProofBits,
186 lo: u64,
187 hi: u64,
188 params: &PedersenParams,
189) -> bool {
190 if proof.range != (lo, hi) {
191 return false;
192 }
193 let p = params.p;
194 let g = params.g;
195 for bit_proof in &proof.bits {
196 let valid_zero = dlog_verify(1, bit_proof, g, p);
198 let valid_one = dlog_verify(g, bit_proof, g, p);
199 if !valid_zero && !valid_one {
200 return false;
201 }
202 }
203 true
204}
205
206pub fn build_zk_proof(
214 statement: &ZkStatement,
215 witness: &ZkWitness,
216 params: &PedersenParams,
217 seed: u64,
218) -> ZkProof {
219 let g = params.g;
220 let p = params.p;
221 let mut commitments = Vec::new();
222 let mut responses = Vec::new();
223 let mut challenge_acc: u64 = 0;
224
225 for (i, &w) in witness.secret_values.iter().enumerate() {
226 let proof = dlog_prove(w, g, p, toy_mix(seed, i as u64));
227 commitments.push(proof.commitment);
228 responses.push(proof.response);
229 challenge_acc = toy_mix(challenge_acc, proof.challenge);
230 }
231 for ch in statement.description.bytes() {
233 challenge_acc = toy_mix(challenge_acc, ch as u64);
234 }
235 ZkProof {
236 challenge: challenge_acc % (p - 1),
237 response: responses,
238 commitment: commitments,
239 }
240}
241
242pub fn verify_zk_proof(proof: &ZkProof, public_keys: &[u64], params: &PedersenParams) -> bool {
246 if proof.commitment.len() != public_keys.len() || proof.response.len() != public_keys.len() {
247 return false;
248 }
249 let g = params.g;
250 let p = params.p;
251 let order = p - 1;
252 for (i, (&pk, (&r, &c))) in public_keys
254 .iter()
255 .zip(proof.commitment.iter().zip(proof.response.iter()))
256 .enumerate()
257 {
258 let _ = i;
259 let s_mod = reduce_mod(c as i128, order);
260 let expected_challenge = toy_mix(r, pk) % order;
261 let gs = modpow(g, s_mod, p);
262 let yc = modpow(pk, expected_challenge, p);
263 let lhs = mulmod(gs, yc, p);
264 if lhs != r {
265 return false;
266 }
267 }
268 true
269}
270
271fn toy_mix(a: u64, b: u64) -> u64 {
277 let mut x = a.wrapping_add(0x9e37_79b9_7f4a_7c15);
278 let mut y = b.wrapping_add(0x6c62_272e_07bb_0142);
279 x = x.wrapping_mul(0x517c_c1b7_2722_0a95);
280 y ^= x.rotate_left(17);
281 y = y.wrapping_mul(0xbf58_476d_1ce4_e5b9);
282 y ^ x
283}
284
285#[cfg(test)]
288mod tests {
289 use super::*;
290
291 #[test]
292 fn test_modpow_simple() {
293 assert_eq!(modpow(2, 10, 1000), 24); assert_eq!(modpow(3, 0, 7), 1);
295 assert_eq!(modpow(0, 5, 13), 0);
296 assert_eq!(modpow(5, 1, 7), 5);
297 }
298
299 #[test]
300 fn test_modpow_mod_one() {
301 assert_eq!(modpow(999, 999, 1), 0);
302 }
303
304 #[test]
305 fn test_modpow_large() {
306 let result = modpow(2, 62, 1019);
308 assert!(result < 1019);
309 }
310
311 #[test]
312 fn test_toy_params_valid() {
313 let p = toy_params();
314 assert_eq!(p.p, 1019);
315 assert_eq!(p.g, 2);
316 assert_eq!(p.h, 3);
317 }
318
319 #[test]
320 fn test_pedersen_commit_deterministic() {
321 let params = toy_params();
322 let c1 = pedersen_commit(5, 7, ¶ms);
323 let c2 = pedersen_commit(5, 7, ¶ms);
324 assert_eq!(c1, c2);
325 }
326
327 #[test]
328 fn test_pedersen_commit_open_valid() {
329 let params = toy_params();
330 let c = pedersen_commit(42, 13, ¶ms);
331 assert!(pedersen_open(&c, 42, 13, ¶ms));
332 }
333
334 #[test]
335 fn test_pedersen_open_wrong_secret() {
336 let params = toy_params();
337 let c = pedersen_commit(42, 13, ¶ms);
338 assert!(!pedersen_open(&c, 43, 13, ¶ms));
339 }
340
341 #[test]
342 fn test_pedersen_open_wrong_randomness() {
343 let params = toy_params();
344 let c = pedersen_commit(42, 13, ¶ms);
345 assert!(!pedersen_open(&c, 42, 14, ¶ms));
346 }
347
348 #[test]
349 fn test_pedersen_commit_zero() {
350 let params = toy_params();
351 let c = pedersen_commit(0, 0, ¶ms);
352 assert_eq!(c.value, 1);
354 }
355
356 #[test]
357 fn test_pedersen_commit_negative_secret() {
358 let params = toy_params();
359 let c = pedersen_commit(-5, 3, ¶ms);
361 assert!(pedersen_open(&c, -5, 3, ¶ms));
362 }
363
364 #[test]
365 fn test_pedersen_hiding() {
366 let params = toy_params();
367 let c1 = pedersen_commit(7, 1, ¶ms);
368 let c2 = pedersen_commit(7, 2, ¶ms);
369 assert_ne!(
370 c1.value, c2.value,
371 "Different randomness should give different commitments"
372 );
373 }
374
375 #[test]
376 fn test_dlog_prove_verify_basic() {
377 let params = toy_params();
378 let p = params.p;
379 let g = params.g;
380 let secret: i64 = 42;
381 let order = (p - 1) as i64;
382 let secret_mod = ((secret % order) + order) as u64 % (p - 1);
383 let public = modpow(g, secret_mod, p);
384 let proof = dlog_prove(secret, g, p, 12345);
385 assert!(dlog_verify(public, &proof, g, p));
386 }
387
388 #[test]
389 fn test_dlog_prove_verify_secret_one() {
390 let params = toy_params();
391 let p = params.p;
392 let g = params.g;
393 let public = modpow(g, 1, p); let proof = dlog_prove(1, g, p, 999);
395 assert!(dlog_verify(public, &proof, g, p));
396 }
397
398 #[test]
399 fn test_dlog_prove_verify_secret_zero() {
400 let params = toy_params();
401 let p = params.p;
402 let g = params.g;
403 let public = modpow(g, 0, p); let proof = dlog_prove(0, g, p, 1);
405 assert!(dlog_verify(public, &proof, g, p));
406 }
407
408 #[test]
409 fn test_dlog_verify_wrong_public() {
410 let params = toy_params();
411 let p = params.p;
412 let g = params.g;
413 let secret: i64 = 10;
414 let proof = dlog_prove(secret, g, p, 42);
415 let wrong_public = modpow(g, 11, p); assert!(!dlog_verify(wrong_public, &proof, g, p));
417 }
418
419 #[test]
420 fn test_sigma_protocol_properties() {
421 let sp = sigma_protocol_properties();
422 assert_eq!(sp.name, "Schnorr");
423 assert!(sp.completeness);
424 assert!(sp.soundness);
425 assert!(sp.zero_knowledge);
426 }
427
428 #[test]
429 fn test_prove_range_valid_in_range() {
430 let params = toy_params();
431 let proof = prove_range(5, 0, 15, ¶ms, 777);
432 assert!(proof.is_some());
433 }
434
435 #[test]
436 fn test_prove_range_out_of_range_low() {
437 let params = toy_params();
438 assert!(prove_range(3, 5, 10, ¶ms, 1).is_none());
439 }
440
441 #[test]
442 fn test_prove_range_out_of_range_high() {
443 let params = toy_params();
444 assert!(prove_range(11, 5, 10, ¶ms, 2).is_none());
445 }
446
447 #[test]
448 fn test_prove_range_boundary_lo() {
449 let params = toy_params();
450 let proof = prove_range(5, 5, 10, ¶ms, 3);
451 assert!(proof.is_some());
452 }
453
454 #[test]
455 fn test_prove_range_boundary_hi() {
456 let params = toy_params();
457 let proof = prove_range(10, 5, 10, ¶ms, 4);
458 assert!(proof.is_some());
459 }
460
461 #[test]
462 fn test_verify_range_proof_valid() {
463 let params = toy_params();
464 let proof = prove_range(7, 0, 15, ¶ms, 100).expect("in range");
465 assert!(verify_range_proof(&proof, 0, 15, ¶ms));
466 }
467
468 #[test]
469 fn test_verify_range_proof_wrong_range() {
470 let params = toy_params();
471 let proof = prove_range(7, 0, 15, ¶ms, 100).expect("in range");
472 assert!(!verify_range_proof(&proof, 1, 15, ¶ms));
474 }
475
476 #[test]
477 fn test_build_zk_proof_structure() {
478 let params = toy_params();
479 let stmt = ZkStatement {
480 description: "know x".to_string(),
481 public_params: vec!["g".to_string(), "p".to_string()],
482 };
483 let witness = ZkWitness {
484 secret_values: vec![3, 7],
485 };
486 let proof = build_zk_proof(&stmt, &witness, ¶ms, 42);
487 assert_eq!(proof.commitment.len(), 2);
488 assert_eq!(proof.response.len(), 2);
489 }
490
491 #[test]
492 fn test_verify_zk_proof_valid() {
493 let params = toy_params();
494 let g = params.g;
495 let p = params.p;
496 let secrets = vec![5i64, 11i64];
497 let public_keys: Vec<u64> = secrets
498 .iter()
499 .map(|&s| {
500 let s_mod = ((s % (p as i64 - 1)) + (p as i64 - 1)) as u64 % (p - 1);
501 modpow(g, s_mod, p)
502 })
503 .collect();
504 let stmt = ZkStatement {
505 description: "test".to_string(),
506 public_params: vec![],
507 };
508 let witness = ZkWitness {
509 secret_values: secrets,
510 };
511 let proof = build_zk_proof(&stmt, &witness, ¶ms, 99);
512 assert!(verify_zk_proof(&proof, &public_keys, ¶ms));
513 }
514
515 #[test]
516 fn test_verify_zk_proof_empty_witness() {
517 let params = toy_params();
518 let stmt = ZkStatement {
519 description: "empty".to_string(),
520 public_params: vec![],
521 };
522 let witness = ZkWitness {
523 secret_values: vec![],
524 };
525 let proof = build_zk_proof(&stmt, &witness, ¶ms, 1);
526 assert!(verify_zk_proof(&proof, &[], ¶ms));
527 }
528
529 #[test]
530 fn test_zk_statement_fields() {
531 let stmt = ZkStatement {
532 description: "knows discrete log".to_string(),
533 public_params: vec!["g".to_string(), "p".to_string(), "y".to_string()],
534 };
535 assert_eq!(stmt.public_params.len(), 3);
536 assert!(stmt.description.contains("discrete log"));
537 }
538
539 #[test]
540 fn test_zk_witness_fields() {
541 let w = ZkWitness {
542 secret_values: vec![1, 2, 3],
543 };
544 assert_eq!(w.secret_values.len(), 3);
545 assert_eq!(w.secret_values[2], 3);
546 }
547}