use sha2::{Digest, Sha256};
use std::sync::Arc;
use crate::group::Group;
struct Prover {}
impl Prover {
fn send<G: Group>(group: &G, g: &G::Element, w: &G::Scalar) -> G::Element {
group.exp(g, w)
}
fn response<G: Group>(
group: &G,
w: &G::Scalar,
alpha: &G::Scalar,
c: &G::Scalar,
) -> G::Scalar {
let alpha_c = group.scalar_mul(alpha, c);
group.scalar_sub(w, &alpha_c)
}
}
struct Verifier {}
impl Verifier {
#[inline]
fn update_framed_hash(hasher: &mut Sha256, bytes: &[u8]) {
hasher.update((bytes.len() as u64).to_be_bytes());
hasher.update(bytes);
}
#[allow(clippy::too_many_arguments)]
fn commitments<G: Group>(
group: &G,
g1: &G::Element,
h1: &G::Element,
g2: &G::Element,
h2: &G::Element,
response: &G::Scalar,
c: &G::Scalar,
) -> (G::Element, G::Element) {
let g1_r = group.exp(g1, response);
let h1_c = group.exp(h1, c);
let a1 = group.mul(&g1_r, &h1_c);
let g2_r = group.exp(g2, response);
let h2_c = group.exp(h2, c);
let a2 = group.mul(&g2_r, &h2_c);
(a1, a2)
}
fn append_transcript<G: Group>(
group: &G,
h1: &G::Element,
h2: &G::Element,
a1: &G::Element,
a2: &G::Element,
hasher: &mut Sha256,
) {
Self::update_framed_hash(hasher, &group.element_to_bytes(h1));
Self::update_framed_hash(hasher, &group.element_to_bytes(h2));
Self::update_framed_hash(hasher, &group.element_to_bytes(a1));
Self::update_framed_hash(hasher, &group.element_to_bytes(a2));
}
#[allow(clippy::too_many_arguments)]
fn update<G: Group>(
group: &G,
g1: &G::Element,
h1: &G::Element,
g2: &G::Element,
h2: &G::Element,
response: &G::Scalar,
c: &G::Scalar,
hasher: &mut Sha256,
) -> (G::Element, G::Element) {
let (a1, a2) = Self::commitments(group, g1, h1, g2, h2, response, c);
Self::append_transcript(group, h1, h2, &a1, &a2, hasher);
(a1, a2)
}
fn check<G: Group>(group: &G, c: &G::Scalar, hasher: &Sha256) -> bool
where
G::Scalar: Clone + Eq,
{
let challenge_hash = hasher.clone().finalize();
let computed = group.hash_to_scalar(&challenge_hash);
computed == *c
}
}
#[derive(Debug, Clone)]
pub struct DLEQ<G: Group> {
pub g1: G::Element,
pub h1: G::Element,
pub g2: G::Element,
pub h2: G::Element,
pub w: G::Scalar,
pub alpha: G::Scalar,
pub c: Option<G::Scalar>,
pub r: Option<G::Scalar>,
pub group: Arc<G>,
}
impl<G: Group> DLEQ<G> {
pub fn new(group: Arc<G>) -> Self
where
G::Scalar: Default,
G::Element: Default,
{
DLEQ {
g1: Default::default(),
h1: Default::default(),
g2: Default::default(),
h2: Default::default(),
w: Default::default(),
alpha: Default::default(),
c: None,
r: None,
group,
}
}
#[allow(clippy::too_many_arguments)]
pub fn init(
&mut self,
g1: G::Element,
h1: G::Element,
g2: G::Element,
h2: G::Element,
alpha: G::Scalar,
w: G::Scalar,
) {
self.g1 = g1;
self.h1 = h1;
self.g2 = g2;
self.h2 = h2;
self.alpha = alpha;
self.w = w;
}
pub fn get_a1(&self) -> G::Element {
Prover::send(self.group.as_ref(), &self.g1, &self.w)
}
pub fn get_a2(&self) -> G::Element {
Prover::send(self.group.as_ref(), &self.g2, &self.w)
}
pub fn get_r(&self) -> Option<G::Scalar>
where
G::Scalar: Clone,
{
self.c.as_ref().map(|c| {
Prover::response(self.group.as_ref(), &self.w, &self.alpha, c)
})
}
#[allow(clippy::too_many_arguments)]
pub fn verifier_commitments(
group: &G,
g1: &G::Element,
h1: &G::Element,
g2: &G::Element,
h2: &G::Element,
response: &G::Scalar,
c: &G::Scalar,
) -> (G::Element, G::Element) {
Verifier::commitments(group, g1, h1, g2, h2, response, c)
}
pub fn append_transcript_hash(
group: &G,
h1: &G::Element,
h2: &G::Element,
a1: &G::Element,
a2: &G::Element,
hasher: &mut Sha256,
) {
Verifier::append_transcript(group, h1, h2, a1, a2, hasher);
}
#[allow(clippy::too_many_arguments)]
pub fn verifier_update_hash(
group: &G,
g1: &G::Element,
h1: &G::Element,
g2: &G::Element,
h2: &G::Element,
response: &G::Scalar,
c: &G::Scalar,
hasher: &mut Sha256,
) -> (G::Element, G::Element) {
Verifier::update(group, g1, h1, g2, h2, response, c, hasher)
}
pub fn verify(&self) -> bool
where
G::Scalar: Clone,
G::Element: Clone,
{
let c = match &self.c {
Some(c) => c,
None => return false,
};
let r = match &self.r {
Some(r) => r,
None => return false,
};
let mut hasher = Sha256::new();
let _ = Self::verifier_update_hash(
self.group.as_ref(),
&self.g1,
&self.h1,
&self.g2,
&self.h2,
r,
c,
&mut hasher,
);
self.check(&hasher)
}
pub fn update_hash(&self, hasher: &mut Sha256)
where
G::Element: Clone,
{
let a1 = self.get_a1();
let a2 = self.get_a2();
Self::append_transcript_hash(
self.group.as_ref(),
&self.h1,
&self.h2,
&a1,
&a2,
hasher,
);
}
pub fn check(&self, hasher: &Sha256) -> bool
where
G::Scalar: Clone + Eq,
{
match &self.c {
Some(c) => Verifier::check(self.group.as_ref(), c, hasher),
None => false,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::groups::ModpGroup;
use num_bigint::{BigInt, RandBigInt, ToBigInt};
use num_integer::Integer;
#[test]
fn test_generic_dleq_new() {
let group = ModpGroup::new();
let dleq = DLEQ::new(group);
assert_eq!(dleq.c, None);
assert_eq!(dleq.r, None);
}
#[test]
fn test_generic_dleq_init() {
let group = ModpGroup::new();
let mut dleq = DLEQ::new(group.clone());
let g1 = BigInt::from(8443);
let h1 = BigInt::from(531216);
let g2 = BigInt::from(1299721);
let h2 = BigInt::from(14767239);
let w = BigInt::from(81647);
let alpha = BigInt::from(163027);
dleq.init(g1.clone(), h1, g2.clone(), h2, alpha, w.clone());
let q = group.modulus();
let a1_expected = g1.modpow(&w, q);
let a2_expected = g2.modpow(&w, q);
assert_eq!(dleq.get_a1(), a1_expected);
assert_eq!(dleq.get_a2(), a2_expected);
}
#[test]
fn test_generic_dleq_get_r() {
let group = ModpGroup::new();
let mut dleq = DLEQ::new(group.clone());
let g1 = BigInt::from(8443);
let h1 = BigInt::from(531216);
let g2 = BigInt::from(1299721);
let h2 = BigInt::from(14767239);
let w = BigInt::from(81647);
let alpha = BigInt::from(163027);
dleq.init(g1, h1, g2, h2, alpha, w);
dleq.c = Some(BigInt::from(127997));
let r = dleq.get_r().unwrap();
let order = group.order();
let expected_r = (BigInt::from(81647)
- BigInt::from(163027) * BigInt::from(127997))
.mod_floor(order);
assert_eq!(r, expected_r);
}
#[test]
fn test_generic_dleq_verify() {
let group = ModpGroup::new();
let mut rng = rand::thread_rng();
let q = group.modulus().clone();
let alpha: BigInt = rng
.gen_biguint_below(&q.to_biguint().unwrap())
.to_bigint()
.unwrap();
let w: BigInt = rng
.gen_biguint_below(&q.to_biguint().unwrap())
.to_bigint()
.unwrap();
let g1 = BigInt::from(8443);
let h1 = g1.modpow(&alpha, &q);
let g2 = BigInt::from(1299721);
let h2 = g2.modpow(&alpha, &q);
let mut dleq = DLEQ::new(group.clone());
dleq.init(g1, h1, g2, h2, alpha, w);
let mut hasher = Sha256::new();
dleq.update_hash(&mut hasher);
let c = group.hash_to_scalar(&hasher.finalize());
dleq.c = Some(c.clone());
let r = dleq.get_r().unwrap();
dleq.r = Some(r);
assert!(dleq.verify());
}
}