From e534f36ec49a7933789626e576a33df40a027a09 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Wed, 5 Aug 2020 03:26:31 -0700 Subject: [PATCH] bellman: add VerificationError (#254) * bellman: add VerificationError This adds a distinct VerificationError type to the crate and changes `verify_proof` to return `Result<(), VerificationError>` rather than `Result`. This is significantly safer, because it avoids the need to mix pattern-matching logic with boolean logic (the cause of RUSTSEC-2019-0004). * Rename VerificationError variants per review comments. * Add missing Clone impl to VerificationError. --- src/groth16/mod.rs | 4 ++-- src/groth16/tests/mod.rs | 2 +- src/groth16/verifier.rs | 25 +++++++++++++++---------- src/lib.rs | 31 ++++++++++++++++++++++++++----- tests/mimc.rs | 2 +- 5 files changed, 45 insertions(+), 19 deletions(-) diff --git a/src/groth16/mod.rs b/src/groth16/mod.rs index 7c97197..624324f 100644 --- a/src/groth16/mod.rs +++ b/src/groth16/mod.rs @@ -560,8 +560,8 @@ mod test_with_bls12_381 { let de_proof = Proof::read(&v[..]).unwrap(); assert!(proof == de_proof); - assert!(verify_proof(&pvk, &proof, &[c]).unwrap()); - assert!(!verify_proof(&pvk, &proof, &[a]).unwrap()); + assert!(verify_proof(&pvk, &proof, &[c]).is_ok()); + assert!(verify_proof(&pvk, &proof, &[a]).is_err()); } } } diff --git a/src/groth16/tests/mod.rs b/src/groth16/tests/mod.rs index 8038e17..371f7c1 100644 --- a/src/groth16/tests/mod.rs +++ b/src/groth16/tests/mod.rs @@ -377,5 +377,5 @@ fn test_xordemo() { assert_eq!(expected_c, proof.c); } - assert!(verify_proof(&pvk, &proof, &[Fr::one()]).unwrap()); + assert!(verify_proof(&pvk, &proof, &[Fr::one()]).is_ok()); } diff --git a/src/groth16/verifier.rs b/src/groth16/verifier.rs index 0fe8c94..18a376d 100644 --- a/src/groth16/verifier.rs +++ b/src/groth16/verifier.rs @@ -4,7 +4,7 @@ use std::ops::{AddAssign, Neg}; use super::{PreparedVerifyingKey, Proof, VerifyingKey}; -use crate::SynthesisError; +use crate::VerificationError; pub fn prepare_verifying_key(vk: &VerifyingKey) -> PreparedVerifyingKey { let gamma = vk.gamma_g2.neg(); @@ -22,9 +22,9 @@ pub fn verify_proof<'a, E: MultiMillerLoop>( pvk: &'a PreparedVerifyingKey, proof: &Proof, public_inputs: &[E::Fr], -) -> Result { +) -> Result<(), VerificationError> { if (public_inputs.len() + 1) != pvk.ic.len() { - return Err(SynthesisError::MalformedVerifyingKey); + return Err(VerificationError::InvalidVerifyingKey); } let mut acc = pvk.ic[0].to_curve(); @@ -41,11 +41,16 @@ pub fn verify_proof<'a, E: MultiMillerLoop>( // A * B + inputs * (-gamma) + C * (-delta) = alpha * beta // which allows us to do a single final exponentiation. - Ok(E::multi_miller_loop(&[ - (&proof.a, &proof.b.into()), - (&acc.to_affine(), &pvk.neg_gamma_g2), - (&proof.c, &pvk.neg_delta_g2), - ]) - .final_exponentiation() - == pvk.alpha_g1_beta_g2) + if pvk.alpha_g1_beta_g2 + == E::multi_miller_loop(&[ + (&proof.a, &proof.b.into()), + (&acc.to_affine(), &pvk.neg_gamma_g2), + (&proof.c, &pvk.neg_delta_g2), + ]) + .final_exponentiation() + { + Ok(()) + } else { + Err(VerificationError::InvalidProof) + } } diff --git a/src/lib.rs b/src/lib.rs index 7b8df62..affbcb1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -122,7 +122,7 @@ //! let inputs = multipack::compute_multipacking(&hash_bits); //! //! // Check the proof! -//! assert!(groth16::verify_proof(&pvk, &proof, &inputs).unwrap()); +//! assert!(groth16::verify_proof(&pvk, &proof, &inputs).is_ok()); //! ``` //! //! # Roadmap @@ -314,7 +314,7 @@ impl<'a, Scalar: PrimeField> Sub<(Scalar, &'a LinearCombination)> } /// This is an error that could occur during circuit synthesis contexts, -/// such as CRS generation, proving or verification. +/// such as CRS generation or proving. #[derive(Debug)] pub enum SynthesisError { /// During synthesis, we lacked knowledge of a variable assignment. @@ -329,8 +329,6 @@ pub enum SynthesisError { UnexpectedIdentity, /// During proof generation, we encountered an I/O error with the CRS IoError(io::Error), - /// During verification, our verifying key was malformed. - MalformedVerifyingKey, /// During CRS generation, we observed an unconstrained auxiliary variable UnconstrainedVariable, } @@ -352,7 +350,6 @@ impl Error for SynthesisError { SynthesisError::PolynomialDegreeTooLarge => "polynomial degree is too large", SynthesisError::UnexpectedIdentity => "encountered an identity element in the CRS", SynthesisError::IoError(_) => "encountered an I/O error", - SynthesisError::MalformedVerifyingKey => "malformed verifying key", SynthesisError::UnconstrainedVariable => "auxiliary variable was unconstrained", } } @@ -369,6 +366,30 @@ impl fmt::Display for SynthesisError { } } +/// An error during verification. +#[derive(Debug, Clone)] +pub enum VerificationError { + /// Verification was attempted with a malformed verifying key. + InvalidVerifyingKey, + /// Proof verification failed. + InvalidProof, +} + +impl Error for VerificationError { + fn description(&self) -> &str { + match *self { + VerificationError::InvalidVerifyingKey => "malformed verifying key", + VerificationError::InvalidProof => "proof verification failed", + } + } +} + +impl fmt::Display for VerificationError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "{}", self) + } +} + /// Represents a constraint system which can have new variables /// allocated and constrains between them formed. pub trait ConstraintSystem: Sized { diff --git a/tests/mimc.rs b/tests/mimc.rs index b708a18..2696908 100644 --- a/tests/mimc.rs +++ b/tests/mimc.rs @@ -210,7 +210,7 @@ fn test_mimc() { let start = Instant::now(); let proof = Proof::read(&proof_vec[..]).unwrap(); // Check the proof - assert!(verify_proof(&pvk, &proof, &[image]).unwrap()); + assert!(verify_proof(&pvk, &proof, &[image]).is_ok()); total_verifying += start.elapsed(); } let proving_avg = total_proving / SAMPLES;