diff --git a/src/batch.rs b/src/batch.rs index b1c908b..9979cfa 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -24,6 +24,7 @@ fn gen_128_bits(mut rng: R) -> [u64; 4] { bytes } +#[derive(Clone, Debug)] enum Inner { SpendAuth { vk_bytes: VerificationKeyBytes, @@ -42,6 +43,7 @@ enum Inner { /// This struct exists to allow batch processing to be decoupled from the /// lifetime of the message. This is useful when using the batch verification API /// in an async context. +#[derive(Clone, Debug)] pub struct Item { inner: Inner, } @@ -90,6 +92,27 @@ impl<'msg, M: AsRef<[u8]>> From<(VerificationKeyBytes, Signature Result<(), Error> { + match self.inner { + Inner::Binding { vk_bytes, sig, c } => VerificationKey::::try_from(vk_bytes) + .and_then(|vk| vk.verify_prehashed(&sig, c)), + Inner::SpendAuth { vk_bytes, sig, c } => { + VerificationKey::::try_from(vk_bytes) + .and_then(|vk| vk.verify_prehashed(&sig, c)) + } + } + } +} + #[derive(Default)] /// A batch verification context. pub struct Verifier { diff --git a/src/verification_key.rs b/src/verification_key.rs index 6043c1d..1d898f9 100644 --- a/src/verification_key.rs +++ b/src/verification_key.rs @@ -136,9 +136,22 @@ impl VerificationKey { /// Verify a purported `signature` over `msg` made by this verification key. // This is similar to impl signature::Verifier but without boxed errors pub fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), Error> { - #![allow(non_snake_case)] use crate::HStar; + let c = HStar::default() + .update(&signature.r_bytes[..]) + .update(&self.bytes.bytes[..]) // XXX ugly + .update(msg) + .finalize(); + self.verify_prehashed(signature, c) + } + /// Verify a purported `signature` with a prehashed challenge. + #[allow(non_snake_case)] + pub(crate) fn verify_prehashed( + &self, + signature: &Signature, + c: Scalar, + ) -> Result<(), Error> { let r = { // XXX-jubjub: should not use CtOption here // XXX-jubjub: inconsistent ownership in from_bytes @@ -160,12 +173,6 @@ impl VerificationKey { } }; - let c = HStar::default() - .update(&signature.r_bytes[..]) - .update(&self.bytes.bytes[..]) // XXX ugly - .update(msg) - .finalize(); - // XXX rewrite as normal double scalar mul // Verify check is h * ( - s * B + R + c * A) == 0 // h * ( s * B - c * A - R) == 0 diff --git a/tests/batch.rs b/tests/batch.rs index 39b1a4c..2ead7e0 100644 --- a/tests/batch.rs +++ b/tests/batch.rs @@ -35,23 +35,65 @@ fn alternating_batch_verify() { let rng = thread_rng(); let mut batch = batch::Verifier::new(); for i in 0..32 { - match i % 2 { + let item: batch::Item = match i % 2 { 0 => { let sk = SigningKey::::new(rng); let vk = VerificationKey::from(&sk); let msg = b"BatchVerifyTest"; let sig = sk.sign(rng, &msg[..]); - batch.queue((vk.into(), sig, msg)); + (vk.into(), sig, msg).into() } 1 => { let sk = SigningKey::::new(rng); let vk = VerificationKey::from(&sk); let msg = b"BatchVerifyTest"; let sig = sk.sign(rng, &msg[..]); - batch.queue((vk.into(), sig, msg)); + (vk.into(), sig, msg).into() } - _ => panic!(), - } + _ => unreachable!(), + }; + batch.queue(item); } assert!(batch.verify(rng).is_ok()); } + +#[test] +fn bad_batch_verify() { + let rng = thread_rng(); + let bad_index = 4; // must be even + let mut batch = batch::Verifier::new(); + let mut items = Vec::new(); + for i in 0..32 { + let item: batch::Item = match i % 2 { + 0 => { + let sk = SigningKey::::new(rng); + let vk = VerificationKey::from(&sk); + let msg = b"BatchVerifyTest"; + let sig = if i != bad_index { + sk.sign(rng, &msg[..]) + } else { + sk.sign(rng, b"bad") + }; + (vk.into(), sig, msg).into() + } + 1 => { + let sk = SigningKey::::new(rng); + let vk = VerificationKey::from(&sk); + let msg = b"BatchVerifyTest"; + let sig = sk.sign(rng, &msg[..]); + (vk.into(), sig, msg).into() + } + _ => unreachable!(), + }; + items.push(item.clone()); + batch.queue(item); + } + assert!(batch.verify(rng).is_err()); + for (i, item) in items.drain(..).enumerate() { + if i != bad_index { + assert!(item.verify_single().is_ok()); + } else { + assert!(item.verify_single().is_err()); + } + } +}