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<bool, SynthesisError>`.  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.
This commit is contained in:
Henry de Valence 2020-08-05 03:26:31 -07:00 committed by Jack Grigg
parent 701e6cfa1b
commit e534f36ec4
5 changed files with 45 additions and 19 deletions

View File

@ -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());
}
}
}

View File

@ -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());
}

View File

@ -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<E: MultiMillerLoop>(vk: &VerifyingKey<E>) -> PreparedVerifyingKey<E> {
let gamma = vk.gamma_g2.neg();
@ -22,9 +22,9 @@ pub fn verify_proof<'a, E: MultiMillerLoop>(
pvk: &'a PreparedVerifyingKey<E>,
proof: &Proof<E>,
public_inputs: &[E::Fr],
) -> Result<bool, SynthesisError> {
) -> 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)
}
}

View File

@ -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<Scalar>)>
}
/// 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<Scalar: PrimeField>: Sized {

View File

@ -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;