diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index 8413de98d..0e5f329bb 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -22,7 +22,7 @@ ark-std = { version = "0.3.0", default-features = false } blst = "0.3.10" crypto_box = "0.8.1" derivative = { version = "2", features = ["use_core"] } -digest = { version = "0.10.1", default-features = false } +digest = { version = "0.10.1", default-features = false, features = ["alloc"] } displaydoc = { version = "0.2.3", default-features = false } espresso-systems-common = { git = "https://github.com/espressosystems/espresso-systems-common", branch = "main" } generic-array = { version = "^0.14", default-features = false } diff --git a/primitives/src/constants.rs b/primitives/src/constants.rs index c34e9003e..e017a4be7 100644 --- a/primitives/src/constants.rs +++ b/primitives/src/constants.rs @@ -11,6 +11,3 @@ pub const CS_ID_SCHNORR: &str = "SCHNORR_WITH_RESCUE_HASH_v01"; /// ciphersuite identifier for BLS signature pub const CS_ID_BLS_SIG_NAIVE: &str = "BLS_SIG_WITH_NAIVE_HtG_v01"; - -/// ciphersuite identifier for BLS VRF -pub const CS_ID_BLS_VRF_NAIVE: &str = "BLS_VRF_WITH_NAIVE_HtG_v01"; diff --git a/primitives/src/vrf/blsvrf.rs b/primitives/src/vrf/blsvrf.rs index 28f5a26fa..b22004896 100644 --- a/primitives/src/vrf/blsvrf.rs +++ b/primitives/src/vrf/blsvrf.rs @@ -1,27 +1,51 @@ //! BLS signature based VRF - use super::Vrf; use crate::{ - constants::CS_ID_BLS_VRF_NAIVE, errors::PrimitivesError, signatures::{ bls::{BLSSignKey, BLSSignature, BLSVerKey}, BLSSignatureScheme, SignatureScheme, }, }; -use ark_std::rand::{CryptoRng, RngCore}; -use digest::Digest; +use ark_std::{ + boxed::Box, + rand::{CryptoRng, RngCore}, + vec::Vec, +}; +use digest::{Digest, DynDigest}; +use sha2::{Sha256, Sha512}; + +/// Supported Cipher Suites for BLS VRF. +#[allow(non_camel_case_types)] +#[derive(Debug)] +pub enum BLSVRFCipherSuite { + /// using blst library and VRF output from SHA256 hashing + VRF_BLS_12_381_SHA256, + /// using blst library and VRF output from SHA512 hashing + VRF_BLS_12_381_SHA512, +} /// BLS VRF scheme. /// Optimized for signature size, i.e.: PK in G2 and sig in G1 -pub struct BLSVRFScheme; +pub struct BLSVRFScheme { + hasher: Box, +} -impl Vrf for BLSVRFScheme -where - H: Digest, -{ - const CS_ID: &'static str = CS_ID_BLS_VRF_NAIVE; +impl BLSVRFScheme { + /// Creates a new BLS VRF instance with the given ciphersuite. + pub fn new(cs_id: BLSVRFCipherSuite) -> Self { + match cs_id { + BLSVRFCipherSuite::VRF_BLS_12_381_SHA256 => Self { + hasher: Box::new(Sha256::new()), + }, + BLSVRFCipherSuite::VRF_BLS_12_381_SHA512 => Self { + hasher: Box::new(Sha512::new()), + }, + } + } +} +impl Vrf for BLSVRFScheme { /// Public Parameter. /// For BLS signatures, we want to use default /// prime subgroup generators. So here we don't need @@ -38,13 +62,14 @@ where type Proof = BLSSignature; /// The input of VRF proof. - type Input = [u8; 32]; + type Input = Vec; /// The output of VRF evaluation. - type Output = [u8; 32]; + type Output = Vec; /// generate public parameters from RNG. fn param_gen( + &self, _prng: Option<&mut R>, ) -> Result { Ok(()) @@ -52,6 +77,7 @@ where /// Creates a pair of VRF public and private keys. fn key_gen( + &self, pp: &Self::PublicParameter, prng: &mut R, ) -> Result<(Self::SecretKey, Self::PublicKey), PrimitivesError> { @@ -60,6 +86,7 @@ where /// Creates the VRF proof associated with a VRF secret key. fn prove( + &self, pp: &Self::PublicParameter, secret_key: &Self::SecretKey, input: &Self::Input, @@ -69,43 +96,86 @@ where } /// Computes the VRF output associated with a VRF proof. - fn evaluate( + fn proof_to_hash( + &mut self, _pp: &Self::PublicParameter, proof: &Self::Proof, ) -> Result { let proof_serialized = proof.serialize(); - let mut hasher = H::new(); - hasher.update(proof_serialized); - let mut output = [0u8; 32]; - output.copy_from_slice(hasher.finalize().as_ref()); - Ok(output) + let mut hasher = (*self.hasher).box_clone(); + hasher.update(&proof_serialized); + let output = hasher.finalize(); + Ok(output.to_vec()) } /// Verifies a VRF proof. fn verify( + &mut self, pp: &Self::PublicParameter, proof: &Self::Proof, public_key: &Self::PublicKey, input: &Self::Input, - ) -> Result { - if ::verify(pp, public_key, input, proof).is_err() { - return Ok(false); + ) -> Result<(bool, Option), PrimitivesError> { + if ::verify(pp, public_key, input, proof).is_ok() { + Ok((true, Some(Self::proof_to_hash(self, pp, proof).unwrap()))) + } else { + Ok((false, None)) } - Ok(true) } } #[cfg(test)] mod test { use super::*; - use crate::vrf::tests::{failed_verification, sign_and_verify}; - use sha2::Sha256; + use ark_std::{rand::Rng, test_rng}; + + pub(crate) fn sign_and_verify( + vrf: &mut BLSVRFScheme, + message: &::Input, + bad_message: &::Input, + ) { + let rng = &mut test_rng(); + + let parameters = vrf.param_gen(Some(rng)).unwrap(); + let (sk, pk) = vrf.key_gen(¶meters, rng).unwrap(); + let vrf_proof = vrf.prove(¶meters, &sk, message, rng).unwrap(); + let vrf_output = vrf.proof_to_hash(¶meters, &vrf_proof).unwrap(); + let (is_correct, output) = vrf.verify(¶meters, &vrf_proof, &pk, message).unwrap(); + assert!(is_correct); + // need to use the result + assert!(output.is_some()); + + // check that proof_to_hash(proof) == evaluate(sk, message) + let out = vrf.evaluate(¶meters, &sk, &message, rng).unwrap(); + assert_eq!(out, vrf_output); + + // check the VRF output vs. hashing the proof directly + let mut hasher = H::new(); + hasher.update(vrf_proof.serialize()); + let direct_hash_output = hasher.finalize().to_vec(); + assert_eq!(direct_hash_output, vrf_output); + + // now test for bad message. User can choose to ignore the output if they really + // want to. + let (is_correct, _) = vrf + .verify(¶meters, &vrf_proof, &pk, bad_message) + .unwrap(); + assert!(!is_correct); + } #[test] fn test_bls_vrf() { - let message = [0u8; 32]; - let message_bad = [1u8; 32]; - sign_and_verify::(&message); - failed_verification::(&message, &message_bad); + let rng = &mut test_rng(); + for _ in 0..10 { + let message = rng.gen::<[u8; 32]>().to_vec(); + // bad message is truncated + let message_bad = message.clone()[..31].to_vec(); + let mut blsvrf256 = BLSVRFScheme::new(BLSVRFCipherSuite::VRF_BLS_12_381_SHA256); + + sign_and_verify::(&mut blsvrf256, &message, &message_bad); + + let mut blsvrf512 = BLSVRFScheme::new(BLSVRFCipherSuite::VRF_BLS_12_381_SHA512); + sign_and_verify::(&mut blsvrf512, &message, &message_bad); + } } } diff --git a/primitives/src/vrf/mod.rs b/primitives/src/vrf/mod.rs index ca3ab7071..ea83baacb 100644 --- a/primitives/src/vrf/mod.rs +++ b/primitives/src/vrf/mod.rs @@ -6,10 +6,7 @@ pub mod blsvrf; pub mod ecvrf; /// A trait for VRF proof, evaluation and verification. -pub trait Vrf { - /// ciphersuite identifier - const CS_ID: &'static str; - +pub trait Vrf { /// Public parameters type PublicParameter; @@ -35,17 +32,20 @@ pub trait Vrf { // `S::param_gen::(None)` // wheere `StdRng` is redundent. fn param_gen( + &self, prng: Option<&mut R>, ) -> Result; /// Creates a pair of VRF public and private keys. fn key_gen( + &self, pp: &Self::PublicParameter, prng: &mut R, ) -> Result<(Self::SecretKey, Self::PublicKey), PrimitivesError>; /// Creates the VRF proof associated with a VRF secret key. fn prove( + &self, pp: &Self::PublicParameter, secret_key: &Self::SecretKey, input: &Self::Input, @@ -53,48 +53,31 @@ pub trait Vrf { ) -> Result; /// Computes the VRF output associated with a VRF proof. - fn evaluate( + fn proof_to_hash( + &mut self, pp: &Self::PublicParameter, proof: &Self::Proof, ) -> Result; + /// Computes the VRF output given a public input and a VRF secret key. + fn evaluate( + &mut self, + pp: &Self::PublicParameter, + secret_key: &Self::SecretKey, + input: &Self::Input, + prng: &mut R, + ) -> Result { + let proof = self.prove(pp, secret_key, input, prng)?; + self.proof_to_hash(pp, &proof) + } + /// Verifies a VRF proof. + #[must_use = "Output must be used"] fn verify( + &mut self, pp: &Self::PublicParameter, proof: &Self::Proof, public_key: &Self::PublicKey, input: &Self::Input, - ) -> Result; -} - -#[cfg(test)] -mod tests { - use super::*; - use ark_std::{rand::prelude::StdRng, test_rng}; - - pub(crate) fn sign_and_verify, H>(message: &V::Input) { - let rng = &mut test_rng(); - let parameters = V::param_gen(Some(rng)).unwrap(); - let (sk, pk) = V::key_gen(¶meters, rng).unwrap(); - let vrf_proof = V::prove(¶meters, &sk, message, rng).unwrap(); - let _vrf_output = V::evaluate(¶meters, &vrf_proof).unwrap(); - assert!(V::verify(¶meters, &vrf_proof, &pk, message).unwrap()); - - let parameters = V::param_gen::(None).unwrap(); - let (sk, pk) = V::key_gen(¶meters, rng).unwrap(); - let vrf_proof = V::prove(¶meters, &sk, message, rng).unwrap(); - let _vrf_output = V::evaluate(¶meters, &vrf_proof).unwrap(); - - assert!(V::verify(¶meters, &vrf_proof, &pk, message).unwrap()); - } - - pub(crate) fn failed_verification, H>(message: &V::Input, bad_message: &V::Input) { - let rng = &mut test_rng(); - let parameters = V::param_gen(Some(rng)).unwrap(); - let (sk, pk) = V::key_gen(¶meters, rng).unwrap(); - let vrf_proof = V::prove(¶meters, &sk, message, rng).unwrap(); - let _vrf_output = V::evaluate(¶meters, &vrf_proof).unwrap(); - - assert!(!V::verify(¶meters, &vrf_proof, &pk, bad_message).unwrap()); - } + ) -> Result<(bool, Option), PrimitivesError>; }