use curve25519_dalek::{
edwards::{CompressedEdwardsY, EdwardsPoint},
scalar::Scalar,
};
use rand_core::OsRng;
use sha2::{Digest, Sha256};
use super::types::DleqProof;
pub fn prove(
secret: &Scalar,
base_g: &EdwardsPoint,
public_key: &EdwardsPoint,
base_h: &EdwardsPoint,
partial: &EdwardsPoint,
) -> DleqProof {
let mut rng = OsRng;
let k = Scalar::random(&mut rng);
let r1 = k * base_g; let r2 = k * base_h;
let challenge = compute_challenge(base_g, public_key, base_h, partial, &r1, &r2);
let response = k - challenge * secret;
DleqProof { challenge, response }
}
pub fn verify(
proof: &DleqProof,
base_g: &EdwardsPoint,
public_key: &EdwardsPoint,
base_h: &EdwardsPoint,
partial: &EdwardsPoint,
) -> bool {
let r1 = proof.response * base_g + proof.challenge * public_key;
let r2 = proof.response * base_h + proof.challenge * partial;
let expected_challenge = compute_challenge(base_g, public_key, base_h, partial, &r1, &r2);
proof.challenge == expected_challenge
}
fn compute_challenge(
base_g: &EdwardsPoint,
public_key: &EdwardsPoint,
base_h: &EdwardsPoint,
partial: &EdwardsPoint,
r1: &EdwardsPoint,
r2: &EdwardsPoint,
) -> Scalar {
let mut hasher = Sha256::new();
hasher.update(b"newton-dleq-v1");
hasher.update(compress(base_g));
hasher.update(compress(public_key));
hasher.update(compress(base_h));
hasher.update(compress(partial));
hasher.update(compress(r1));
hasher.update(compress(r2));
let hash: [u8; 32] = hasher.finalize().into();
Scalar::from_bytes_mod_order(hash)
}
fn compress(point: &EdwardsPoint) -> [u8; 32] {
let compressed: CompressedEdwardsY = point.compress();
compressed.to_bytes()
}
#[cfg(test)]
mod tests {
use super::*;
use curve25519_dalek::constants::ED25519_BASEPOINT_POINT;
#[test]
fn valid_proof_verifies() {
let mut rng = OsRng;
let secret = Scalar::random(&mut rng);
let base_g = ED25519_BASEPOINT_POINT;
let base_h = Scalar::random(&mut rng) * ED25519_BASEPOINT_POINT;
let public_key = secret * base_g;
let partial = secret * base_h;
let proof = prove(&secret, &base_g, &public_key, &base_h, &partial);
assert!(verify(&proof, &base_g, &public_key, &base_h, &partial));
}
#[test]
fn wrong_secret_fails() {
let mut rng = OsRng;
let secret = Scalar::random(&mut rng);
let wrong_secret = Scalar::random(&mut rng);
let base_g = ED25519_BASEPOINT_POINT;
let base_h = Scalar::random(&mut rng) * ED25519_BASEPOINT_POINT;
let public_key = secret * base_g;
let partial = wrong_secret * base_h;
let proof = prove(&secret, &base_g, &public_key, &base_h, &partial);
assert!(!verify(&proof, &base_g, &public_key, &base_h, &partial));
}
#[test]
fn wrong_partial_fails() {
let mut rng = OsRng;
let secret = Scalar::random(&mut rng);
let base_g = ED25519_BASEPOINT_POINT;
let base_h = Scalar::random(&mut rng) * ED25519_BASEPOINT_POINT;
let public_key = secret * base_g;
let partial = secret * base_h;
let wrong_partial = Scalar::random(&mut rng) * base_h;
let proof = prove(&secret, &base_g, &public_key, &base_h, &partial);
assert!(!verify(&proof, &base_g, &public_key, &base_h, &wrong_partial));
}
#[test]
fn wrong_public_key_fails() {
let mut rng = OsRng;
let secret = Scalar::random(&mut rng);
let base_g = ED25519_BASEPOINT_POINT;
let base_h = Scalar::random(&mut rng) * ED25519_BASEPOINT_POINT;
let public_key = secret * base_g;
let wrong_pk = Scalar::random(&mut rng) * base_g;
let partial = secret * base_h;
let proof = prove(&secret, &base_g, &public_key, &base_h, &partial);
assert!(!verify(&proof, &base_g, &wrong_pk, &base_h, &partial));
}
#[test]
fn proof_is_non_interactive_deterministic_verification() {
let mut rng = OsRng;
let secret = Scalar::random(&mut rng);
let base_g = ED25519_BASEPOINT_POINT;
let base_h = Scalar::random(&mut rng) * ED25519_BASEPOINT_POINT;
let public_key = secret * base_g;
let partial = secret * base_h;
let proof = prove(&secret, &base_g, &public_key, &base_h, &partial);
assert!(verify(&proof, &base_g, &public_key, &base_h, &partial));
assert!(verify(&proof, &base_g, &public_key, &base_h, &partial));
}
#[test]
fn different_proofs_for_same_statement() {
let mut rng = OsRng;
let secret = Scalar::random(&mut rng);
let base_g = ED25519_BASEPOINT_POINT;
let base_h = Scalar::random(&mut rng) * ED25519_BASEPOINT_POINT;
let public_key = secret * base_g;
let partial = secret * base_h;
let proof1 = prove(&secret, &base_g, &public_key, &base_h, &partial);
let proof2 = prove(&secret, &base_g, &public_key, &base_h, &partial);
assert_ne!(proof1.challenge, proof2.challenge);
assert!(verify(&proof1, &base_g, &public_key, &base_h, &partial));
assert!(verify(&proof2, &base_g, &public_key, &base_h, &partial));
}
}