use serde::{Deserialize, Serialize};
use crate::cryptographic_primitives::hashing::{Digest, DigestExt};
use crate::elliptic::curves::{Curve, Point, Scalar};
use crate::marker::HashChoice;
use super::ProofError;
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
#[serde(bound = "")]
pub struct ECDDHProof<E: Curve, H: Digest + Clone> {
pub a1: Point<E>,
pub a2: Point<E>,
pub z: Scalar<E>,
#[serde(skip)]
pub hash_choice: HashChoice<H>,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct ECDDHStatement<E: Curve> {
pub g1: Point<E>,
pub h1: Point<E>,
pub g2: Point<E>,
pub h2: Point<E>,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct ECDDHWitness<E: Curve> {
pub x: Scalar<E>,
}
impl<E: Curve, H: Digest + Clone> ECDDHProof<E, H> {
pub fn prove(w: &ECDDHWitness<E>, delta: &ECDDHStatement<E>) -> ECDDHProof<E, H> {
let s = Scalar::random();
let a1 = &delta.g1 * &s;
let a2 = &delta.g2 * &s;
let e = H::new()
.chain_point(&delta.g1)
.chain_point(&delta.h1)
.chain_point(&delta.g2)
.chain_point(&delta.h2)
.chain_point(&a1)
.chain_point(&a2)
.result_scalar();
let z = &s + e * &w.x;
ECDDHProof {
a1,
a2,
z,
hash_choice: HashChoice::new(),
}
}
pub fn verify(&self, delta: &ECDDHStatement<E>) -> Result<(), ProofError> {
let e = H::new()
.chain_point(&delta.g1)
.chain_point(&delta.h1)
.chain_point(&delta.g2)
.chain_point(&delta.h2)
.chain_point(&self.a1)
.chain_point(&self.a2)
.result_scalar();
let z_g1 = &delta.g1 * &self.z;
let z_g2 = &delta.g2 * &self.z;
let a1_plus_e_h1 = &self.a1 + &delta.h1 * &e;
let a2_plus_e_h2 = &self.a2 + &delta.h2 * e;
if z_g1 == a1_plus_e_h1 && z_g2 == a2_plus_e_h2 {
Ok(())
} else {
Err(ProofError)
}
}
}
#[cfg(test)]
mod tests {
use crate::test_for_all_curves_and_hashes;
use super::*;
test_for_all_curves_and_hashes!(test_ecddh_proof);
fn test_ecddh_proof<E: Curve, H: Digest + Clone>() {
let x = Scalar::random();
let g1 = Point::generator();
let g2 = Point::base_point2();
let h1 = g1 * &x;
let h2 = g2 * &x;
let delta = ECDDHStatement {
g1: g1.to_point(),
g2: g2.clone(),
h1,
h2,
};
let w = ECDDHWitness { x };
let proof = ECDDHProof::<E, H>::prove(&w, &delta);
assert!(proof.verify(&delta).is_ok());
}
test_for_all_curves_and_hashes!(test_wrong_ecddh_proof);
fn test_wrong_ecddh_proof<E: Curve, H: Digest + Clone>() {
let x = Scalar::random();
let g1 = Point::generator();
let g2 = Point::base_point2();
let x2 = Scalar::random();
let h1 = g1 * &x;
let h2 = g2 * &x2;
let delta = ECDDHStatement {
g1: g1.to_point(),
g2: g2.clone(),
h1,
h2,
};
let w = ECDDHWitness { x };
let proof = ECDDHProof::<E, H>::prove(&w, &delta);
assert!(!proof.verify(&delta).is_ok());
}
}