Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VRF API refactor #137

Merged
merged 9 commits into from
Nov 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion primitives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
3 changes: 0 additions & 3 deletions primitives/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
126 changes: 98 additions & 28 deletions primitives/src/vrf/blsvrf.rs
Original file line number Diff line number Diff line change
@@ -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<dyn DynDigest>,
}

impl<H> Vrf<H> 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()),
},
alxiong marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

impl Vrf for BLSVRFScheme {
/// Public Parameter.
/// For BLS signatures, we want to use default
/// prime subgroup generators. So here we don't need
Expand All @@ -38,20 +62,22 @@ where
type Proof = BLSSignature;

/// The input of VRF proof.
type Input = [u8; 32];
type Input = Vec<u8>;

/// The output of VRF evaluation.
type Output = [u8; 32];
type Output = Vec<u8>;

/// generate public parameters from RNG.
fn param_gen<R: CryptoRng + RngCore>(
&self,
_prng: Option<&mut R>,
) -> Result<Self::PublicParameter, PrimitivesError> {
Ok(())
}

/// Creates a pair of VRF public and private keys.
fn key_gen<R: CryptoRng + RngCore>(
&self,
pp: &Self::PublicParameter,
prng: &mut R,
) -> Result<(Self::SecretKey, Self::PublicKey), PrimitivesError> {
Expand All @@ -60,6 +86,7 @@ where

/// Creates the VRF proof associated with a VRF secret key.
fn prove<R: CryptoRng + RngCore>(
&self,
pp: &Self::PublicParameter,
secret_key: &Self::SecretKey,
input: &Self::Input,
Expand All @@ -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<Self::Output, PrimitivesError> {
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<bool, PrimitivesError> {
if <BLSSignatureScheme as SignatureScheme>::verify(pp, public_key, input, proof).is_err() {
return Ok(false);
) -> Result<(bool, Option<Self::Output>), PrimitivesError> {
if <BLSSignatureScheme as SignatureScheme>::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<H: Digest>(
vrf: &mut BLSVRFScheme,
message: &<BLSVRFScheme as Vrf>::Input,
bad_message: &<BLSVRFScheme as Vrf>::Input,
) {
let rng = &mut test_rng();

let parameters = vrf.param_gen(Some(rng)).unwrap();
let (sk, pk) = vrf.key_gen(&parameters, rng).unwrap();
let vrf_proof = vrf.prove(&parameters, &sk, message, rng).unwrap();
let vrf_output = vrf.proof_to_hash(&parameters, &vrf_proof).unwrap();
let (is_correct, output) = vrf.verify(&parameters, &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(&parameters, &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(&parameters, &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::<BLSVRFScheme, Sha256>(&message);
failed_verification::<BLSVRFScheme, Sha256>(&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::<Sha256>(&mut blsvrf256, &message, &message_bad);

let mut blsvrf512 = BLSVRFScheme::new(BLSVRFCipherSuite::VRF_BLS_12_381_SHA512);
sign_and_verify::<Sha512>(&mut blsvrf512, &message, &message_bad);
}
}
}
59 changes: 21 additions & 38 deletions primitives/src/vrf/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@ pub mod blsvrf;
pub mod ecvrf;

/// A trait for VRF proof, evaluation and verification.
pub trait Vrf<VrfHasher> {
/// ciphersuite identifier
const CS_ID: &'static str;

pub trait Vrf {
/// Public parameters
type PublicParameter;

Expand All @@ -35,66 +32,52 @@ pub trait Vrf<VrfHasher> {
// `S::param_gen::<StdRng>(None)`
// wheere `StdRng` is redundent.
fn param_gen<R: CryptoRng + RngCore>(
&self,
prng: Option<&mut R>,
) -> Result<Self::PublicParameter, PrimitivesError>;

/// Creates a pair of VRF public and private keys.
fn key_gen<R: CryptoRng + RngCore>(
&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<R: CryptoRng + RngCore>(
&self,
pp: &Self::PublicParameter,
secret_key: &Self::SecretKey,
input: &Self::Input,
prng: &mut R,
) -> Result<Self::Proof, PrimitivesError>;

/// Computes the VRF output associated with a VRF proof.
fn evaluate(
fn proof_to_hash(
&mut self,
pp: &Self::PublicParameter,
proof: &Self::Proof,
) -> Result<Self::Output, PrimitivesError>;

/// Computes the VRF output given a public input and a VRF secret key.
fn evaluate<R: CryptoRng + RngCore>(
&mut self,
pp: &Self::PublicParameter,
secret_key: &Self::SecretKey,
input: &Self::Input,
prng: &mut R,
) -> Result<Self::Output, PrimitivesError> {
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<bool, PrimitivesError>;
}

#[cfg(test)]
mod tests {
use super::*;
use ark_std::{rand::prelude::StdRng, test_rng};

pub(crate) fn sign_and_verify<V: Vrf<H>, H>(message: &V::Input) {
let rng = &mut test_rng();
let parameters = V::param_gen(Some(rng)).unwrap();
let (sk, pk) = V::key_gen(&parameters, rng).unwrap();
let vrf_proof = V::prove(&parameters, &sk, message, rng).unwrap();
let _vrf_output = V::evaluate(&parameters, &vrf_proof).unwrap();
assert!(V::verify(&parameters, &vrf_proof, &pk, message).unwrap());

let parameters = V::param_gen::<StdRng>(None).unwrap();
let (sk, pk) = V::key_gen(&parameters, rng).unwrap();
let vrf_proof = V::prove(&parameters, &sk, message, rng).unwrap();
let _vrf_output = V::evaluate(&parameters, &vrf_proof).unwrap();

assert!(V::verify(&parameters, &vrf_proof, &pk, message).unwrap());
}

pub(crate) fn failed_verification<V: Vrf<H>, 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(&parameters, rng).unwrap();
let vrf_proof = V::prove(&parameters, &sk, message, rng).unwrap();
let _vrf_output = V::evaluate(&parameters, &vrf_proof).unwrap();

assert!(!V::verify(&parameters, &vrf_proof, &pk, bad_message).unwrap());
}
) -> Result<(bool, Option<Self::Output>), PrimitivesError>;
}