darkfi_sdk/crypto/
ecvrf.rs#![allow(non_snake_case)]
#[cfg(feature = "async")]
use darkfi_serial::async_trait;
use darkfi_serial::{SerialDecodable, SerialEncodable};
use halo2_gadgets::ecc::chip::FixedPoint;
use pasta_curves::{
arithmetic::CurveExt,
group::{
ff::{FromUniformBytes, PrimeField},
GroupEncoding,
},
pallas,
};
use super::{
constants::NullifierK,
util::{fp_mod_fv, hash_to_scalar},
PublicKey, SecretKey,
};
const VRF_DOMAIN: &str = "DarkFi_ECVRF";
#[derive(Copy, Clone, Debug, SerialEncodable, SerialDecodable)]
pub struct VrfProof {
gamma: pallas::Point,
c: blake3::Hash,
s: pallas::Scalar,
}
impl VrfProof {
pub fn prove(x: SecretKey, alpha_string: &[u8]) -> Self {
let Y = PublicKey::from_secret(x);
let mut message = vec![];
message.extend_from_slice(&Y.to_bytes());
message.extend_from_slice(alpha_string);
let H = pallas::Point::hash_to_curve(VRF_DOMAIN)(&message);
let gamma = H * fp_mod_fv(x.inner());
let k = hash_to_scalar(VRF_DOMAIN.as_bytes(), &[&x.inner().to_repr(), &H.to_bytes()]);
let mut hasher = blake3::Hasher::new();
hasher.update(&H.to_bytes());
hasher.update(&gamma.to_bytes());
hasher.update(&(NullifierK.generator() * k).to_bytes());
hasher.update(&(H * k).to_bytes());
let c = hasher.finalize();
let mut c_scalar = [0u8; 64];
c_scalar[..blake3::OUT_LEN].copy_from_slice(c.as_bytes());
let c_scalar = pallas::Scalar::from_uniform_bytes(&c_scalar);
let s = k + c_scalar * fp_mod_fv(x.inner());
Self { gamma, c, s }
}
pub fn verify(&self, Y: PublicKey, alpha_string: &[u8]) -> bool {
let mut message = vec![];
message.extend_from_slice(&Y.to_bytes());
message.extend_from_slice(alpha_string);
let H = pallas::Point::hash_to_curve(VRF_DOMAIN)(&message);
let mut c = [0u8; 64];
c[..blake3::OUT_LEN].copy_from_slice(self.c.as_bytes());
let c_scalar = pallas::Scalar::from_uniform_bytes(&c);
let U = NullifierK.generator() * self.s - Y.inner() * c_scalar;
let V = H * self.s - self.gamma * c_scalar;
let mut hasher = blake3::Hasher::new();
hasher.update(&H.to_bytes());
hasher.update(&self.gamma.to_bytes());
hasher.update(&U.to_bytes());
hasher.update(&V.to_bytes());
hasher.finalize() == self.c
}
pub fn hash_output(&self) -> blake3::Hash {
let mut hasher = blake3::Hasher::new();
hasher.update(VRF_DOMAIN.as_bytes());
hasher.update(&[0x03]);
hasher.update(&self.gamma.to_bytes());
hasher.finalize()
}
}
#[cfg(test)]
mod tests {
use super::*;
use rand::rngs::OsRng;
#[test]
fn ecvrf() {
let secret_key = SecretKey::random(&mut OsRng);
let public_key = PublicKey::from_secret(secret_key);
let input = [0xde, 0xad, 0xbe, 0xef];
let proof = VrfProof::prove(secret_key, &input);
assert!(proof.verify(public_key, &input));
let forged_public_key = PublicKey::from_secret(SecretKey::random(&mut OsRng));
assert!(!proof.verify(forged_public_key, &input));
let forged_input = [0xde, 0xad, 0xba, 0xbe];
assert!(!proof.verify(public_key, &forged_input));
}
}