use crate::{
error,
hash::{hash_points, hash_to_curve},
};
use k256::{
elliptic_curve::{
rand_core::OsRng,
sec1::{FromEncodedPoint, ToEncodedPoint},
subtle::ConditionallyNegatable,
Field, PrimeField,
},
AffinePoint, EncodedPoint, FieldBytes, ProjectivePoint, PublicKey, Scalar, SecretKey,
};
use serde::{Deserialize, Serialize};
use tiny_keccak::{Hasher, Keccak};
#[derive(Clone)]
#[derive(Debug)]
#[derive(Eq, PartialEq)]
#[derive(Serialize, Deserialize)]
pub struct ECVRFProof {
pub gamma: AffinePoint,
pub c: Scalar,
pub s: Scalar,
pub y: Scalar,
pub pk: PublicKey,
}
pub struct ECVRF {
secret_key: SecretKey,
public_key: PublicKey,
}
impl ECVRF {
pub fn new(secret_key: SecretKey) -> Self {
let public_key = PublicKey::from_secret_scalar(&secret_key.to_nonzero_scalar());
ECVRF {
secret_key,
public_key,
}
}
pub fn new_from_bytes(secret_key: &[u8]) -> Result<Self, error::Error> {
let secret_key =
SecretKey::from_slice(secret_key).map_err(|_| error::Error::UnknowError)?;
let public_key = PublicKey::from_secret_scalar(&secret_key.to_nonzero_scalar());
Ok(ECVRF {
secret_key,
public_key,
})
}
pub fn prove(&self, alpha: &[u8]) -> Result<ECVRFProof, error::Error> {
let alpha = Self::generate_alpha(alpha);
let pub_affine: AffinePoint = self.public_key.into();
let secret_key: Scalar = *self.secret_key.to_nonzero_scalar();
let h = hash_to_curve(&alpha, Some(&pub_affine));
let gamma = h * secret_key;
let k = Scalar::random(&mut OsRng);
let kg = ProjectivePoint::GENERATOR * k;
let kh = h * k;
let c = hash_points(
&AffinePoint::GENERATOR,
&h,
&pub_affine,
&gamma.to_affine(),
&kg.to_affine(),
&kh.to_affine(),
);
let mut neg_c = c;
neg_c.conditional_negate(1.into());
let s = k + neg_c * secret_key;
let mut output = [0u8; 32];
let mut hasher = Keccak::v256();
hasher.update(gamma.to_encoded_point(true).as_bytes());
hasher.finalize(&mut output);
let bytes = FieldBytes::from_slice(&output);
let y = Scalar::from_repr(*bytes).unwrap();
Ok(ECVRFProof {
gamma: gamma.to_affine(),
c,
s,
y,
pk: self.public_key,
})
}
pub fn verify(alpha: &[u8], vrf_proof: &ECVRFProof, public_key: &[u8]) -> bool {
let alpha = Self::generate_alpha(alpha);
let pub_encoded = EncodedPoint::from_bytes(public_key).unwrap();
let pub_point = ProjectivePoint::from_encoded_point(&pub_encoded).unwrap();
let u = pub_point * vrf_proof.c + ProjectivePoint::GENERATOR * vrf_proof.s;
let h = hash_to_curve(&alpha, Some(&pub_point.to_affine()));
let witness_gamma = ProjectivePoint::from(vrf_proof.gamma) * vrf_proof.c;
let witness_hash = ProjectivePoint::from(h) * vrf_proof.s;
let v = witness_gamma + witness_hash;
let computed_c = hash_points(
&AffinePoint::GENERATOR,
&h,
&pub_point.to_affine(),
&vrf_proof.gamma,
&u.to_affine(),
&v.to_affine(),
);
let mut output = [0u8; 32];
let mut hasher = Keccak::v256();
hasher.update(vrf_proof.gamma.to_encoded_point(true).as_bytes());
hasher.finalize(&mut output);
let bytes = FieldBytes::from_slice(&output);
let computed_y = Scalar::from_repr(*bytes).unwrap();
computed_c == vrf_proof.c && computed_y == vrf_proof.y
}
fn generate_alpha(alpha: &[u8]) -> Scalar {
let mut output = [0u8; 32];
let mut hasher = Keccak::v256();
hasher.update(alpha);
hasher.finalize(&mut output);
let bytes = FieldBytes::from_slice(&output);
Scalar::from_repr(*bytes).unwrap()
}
}
#[cfg(test)]
mod tests {
use k256::{
elliptic_curve::{rand_core::OsRng, sec1::ToEncodedPoint},
PublicKey, SecretKey,
};
use crate::ECVRF;
const TEST_ALPHA: &[u8] = b"test alphaddddddddddddddddddddddddddddddddddddddddddddddddddddddd";
#[test]
fn success_proof_and_verify() {
let secret_key = SecretKey::random(&mut OsRng);
let public_key = PublicKey::from_secret_scalar(&secret_key.to_nonzero_scalar());
let ecvrf = ECVRF::new(secret_key);
let proof = ecvrf.prove(TEST_ALPHA).unwrap();
assert_eq!(proof.pk, public_key);
let result = ECVRF::verify(
TEST_ALPHA,
&proof,
public_key.to_encoded_point(true).as_bytes(),
);
assert!(result);
}
}