use crate::chaum_pedersen::{ChaumPedersen, GroupParams};
use crate::conversion::ByteConvertible;
use crate::rand::RandomGenerator;
use num_bigint::{BigUint, RandBigInt};
use num_traits::One;
use rand::rngs::OsRng;
use std::error::Error;
#[derive(Clone)]
pub struct DiscreteLogChaumPedersen {}
impl ChaumPedersen for DiscreteLogChaumPedersen {
type Secret = BigUint;
type CommitmentRandom = BigUint;
type Response = BigUint;
type Challenge = BigUint;
type GroupParameters = GroupParams<BigUint>;
type CommitParameters = (BigUint, BigUint, BigUint, BigUint);
fn commitment(
params: &Self::GroupParameters, x: &Self::Secret,
) -> (Self::CommitParameters, Self::CommitmentRandom)
where
Self: Sized,
{
let y1 = params.g.modpow(x, ¶ms.p);
let y2 = params.h.modpow(x, ¶ms.p);
let mut rng = OsRng;
let k = rng.gen_biguint_below(¶ms.p);
let r1 = params.g.modpow(&k, ¶ms.p);
let r2 = params.h.modpow(&k, ¶ms.p);
((y1, y2, r1, r2), k)
}
fn challenge(params: &GroupParams<BigUint>) -> BigUint {
let mut rng = OsRng;
rng.gen_biguint_below(¶ms.p)
}
fn challenge_response(
params: &Self::GroupParameters, k: &Self::CommitmentRandom, c: &Self::Challenge,
x: &Self::Secret,
) -> Self::Response
where
Self: Sized,
{
if k >= &(c * x) {
(k - c * x).modpow(&BigUint::one(), ¶ms.q)
} else {
¶ms.q - (c * x - k).modpow(&BigUint::one(), ¶ms.q)
}
}
fn verify(
params: &Self::GroupParameters, s: &Self::Response, c: &Self::Challenge,
cp: &Self::CommitParameters,
) -> bool {
let (y1, y2, r1, r2) = cp;
let lhs1 = params.g.modpow(s, ¶ms.p);
let rhs1 = (r1 * y1.modpow(&(¶ms.p - c - BigUint::one()), ¶ms.p)) % ¶ms.p;
let lhs2 = params.h.modpow(s, ¶ms.p);
let rhs2 = (r2 * y2.modpow(&(¶ms.p - c - BigUint::one()), ¶ms.p)) % ¶ms.p;
lhs1 == rhs1 && lhs2 == rhs2
}
}
impl ByteConvertible<BigUint> for BigUint {
fn convert_to(t: &BigUint) -> Vec<u8> {
t.to_bytes_be()
}
fn convert_from(bytes: &[u8]) -> Result<BigUint, Box<dyn Error>> {
Ok(BigUint::from_bytes_be(bytes))
}
}
impl RandomGenerator<BigUint> for BigUint {
fn generate_random() -> Result<BigUint, Box<dyn std::error::Error>> {
use rand::RngCore;
let mut rng = OsRng;
let mut bytes = [0u8; 32];
rng.fill_bytes(&mut bytes);
Ok(BigUint::from_bytes_be(&bytes))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::chaum_pedersen::constants::{
RFC5114_MODP_1024_160_BIT_PARAMS, RFC5114_MODP_2048_224_BIT_PARAMS,
RFC5114_MODP_2048_256_BIT_PARAMS,
};
use crate::chaum_pedersen::test::test_execute_protocol;
use crate::rand::RandomGenerator;
use num_bigint::ToBigUint;
#[test]
fn biguint_conversion_round_trip() {
let original = 123456789u64.to_biguint().unwrap();
let bytes = BigUint::convert_to(&original);
let recovered = BigUint::convert_from(&bytes).unwrap();
assert_eq!(original, recovered);
}
#[test]
fn test_discrete_log_commitment() {
let g = BigUint::from(4u32);
let h = BigUint::from(9u32);
let p = BigUint::from(23u32);
let q = BigUint::from(11u32);
let x = BigUint::from(3u32);
let params = GroupParams::<BigUint> {
g: g.clone(),
h: h.clone(),
p: p.clone(),
q: q.clone(),
};
let (cp, _k) = DiscreteLogChaumPedersen::commitment(¶ms, &x);
let (y1, y2, r1, r2) = cp;
assert_eq!(y1, params.g.modpow(&x, ¶ms.p));
assert_eq!(y2, params.h.modpow(&x, ¶ms.p));
assert!(r1 < params.p && r2 < params.p);
}
#[test]
fn test_discrete_log_verification() {
let g = BigUint::from(4u32);
let h = BigUint::from(9u32);
let p = BigUint::from(23u32);
let q = BigUint::from(11u32);
let x = BigUint::from(3u32);
let params = GroupParams::<BigUint> {
g: g.clone(),
h: h.clone(),
p: p.clone(),
q: q.clone(),
};
assert!(test_execute_protocol::<DiscreteLogChaumPedersen>(¶ms, &x));
}
#[test]
fn test_rfc_1024_160_bits_params() {
let params = RFC5114_MODP_1024_160_BIT_PARAMS.to_owned();
let mut rng = OsRng;
let x = rng.gen_biguint_below(¶ms.p);
assert!(test_execute_protocol::<DiscreteLogChaumPedersen>(¶ms, &x));
}
#[test]
fn test_rfc_2048_224_bits_params() {
let params = RFC5114_MODP_2048_224_BIT_PARAMS.to_owned();
let mut rng = OsRng;
let x = rng.gen_biguint_below(¶ms.p);
assert!(test_execute_protocol::<DiscreteLogChaumPedersen>(¶ms, &x));
}
#[test]
fn test_rfc_2048_256_bits_params() {
let params = RFC5114_MODP_2048_256_BIT_PARAMS.to_owned();
let mut rng = OsRng;
let x = rng.gen_biguint_below(¶ms.p);
assert!(test_execute_protocol::<DiscreteLogChaumPedersen>(¶ms, &x));
}
#[test]
fn test_fail_rfc_1024_160_bits_params() {
let params = RFC5114_MODP_1024_160_BIT_PARAMS.to_owned();
let mut rng = OsRng;
let x = rng.gen_biguint_below(¶ms.p);
let (cp, _) = DiscreteLogChaumPedersen::commitment(¶ms, &x);
let c = DiscreteLogChaumPedersen::challenge(¶ms);
let fake_response = BigUint::generate_random().unwrap();
let verified = DiscreteLogChaumPedersen::verify(¶ms, &fake_response, &c, &cp);
assert!(!verified);
}
#[test]
fn test_fail_rfc_2048_224_bits_params() {
let params = RFC5114_MODP_2048_224_BIT_PARAMS.to_owned();
let mut rng = OsRng;
let x = rng.gen_biguint_below(¶ms.p);
let (cp, _) = DiscreteLogChaumPedersen::commitment(¶ms, &x);
let c = DiscreteLogChaumPedersen::challenge(¶ms);
let fake_response = BigUint::generate_random().unwrap();
let verified = DiscreteLogChaumPedersen::verify(¶ms, &fake_response, &c, &cp);
assert!(!verified);
}
#[test]
fn test_fail_rfc_2048_256_bits_params() {
let params = RFC5114_MODP_2048_256_BIT_PARAMS.to_owned();
let mut rng = OsRng;
let x = rng.gen_biguint_below(¶ms.p);
let (cp, _) = DiscreteLogChaumPedersen::commitment(¶ms, &x);
let c = DiscreteLogChaumPedersen::challenge(¶ms);
let fake_response = BigUint::generate_random().unwrap();
let verified = DiscreteLogChaumPedersen::verify(¶ms, &fake_response, &c, &cp);
assert!(!verified);
}
#[test]
fn test_verify() {
let g = BigUint::from(4u32);
let h = BigUint::from(9u32);
let p = BigUint::from(23u32);
let q = BigUint::from(11u32);
let params = GroupParams::<BigUint> {
g: g.clone(),
h: h.clone(),
p: p.clone(),
q: q.clone(),
};
let cp = (
BigUint::from(6u32),
BigUint::from(18u32),
BigUint::from(2u32),
BigUint::from(3u32),
);
let x = BigUint::from(10u32);
let k = BigUint::from(17u32);
let c = BigUint::from(0u32);
let s = DiscreteLogChaumPedersen::challenge_response(¶ms, &k, &c, &x);
println!("Response: {:?}", s);
assert!(DiscreteLogChaumPedersen::verify(¶ms, &s, &c, &cp));
}
}