Add batch::Item::verify_single and Item: Clone + Debug. (#39)

This closes a gap in the API where it was impossible to retry items in a failed
batch, because the opaque Item type could not be verified individually.
This commit is contained in:
Henry de Valence 2020-07-15 12:38:43 -07:00 committed by GitHub
parent 3c4e72d241
commit 7424cababb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 84 additions and 12 deletions

View File

@ -24,6 +24,7 @@ fn gen_128_bits<R: RngCore + CryptoRng>(mut rng: R) -> [u64; 4] {
bytes
}
#[derive(Clone, Debug)]
enum Inner {
SpendAuth {
vk_bytes: VerificationKeyBytes<SpendAuth>,
@ -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<Binding>, Signature<Bindin
}
}
impl Item {
/// Perform non-batched verification of this `Item`.
///
/// This is useful (in combination with `Item::clone`) for implementing fallback
/// logic when batch verification fails. In contrast to
/// [`VerificationKey::verify`](crate::VerificationKey::verify), which requires
/// borrowing the message data, the `Item` type is unlinked from the lifetime of
/// the message.
#[allow(non_snake_case)]
pub fn verify_single(self) -> Result<(), Error> {
match self.inner {
Inner::Binding { vk_bytes, sig, c } => VerificationKey::<Binding>::try_from(vk_bytes)
.and_then(|vk| vk.verify_prehashed(&sig, c)),
Inner::SpendAuth { vk_bytes, sig, c } => {
VerificationKey::<SpendAuth>::try_from(vk_bytes)
.and_then(|vk| vk.verify_prehashed(&sig, c))
}
}
}
}
#[derive(Default)]
/// A batch verification context.
pub struct Verifier {

View File

@ -136,9 +136,22 @@ impl<T: SigType> VerificationKey<T> {
/// 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<T>) -> 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<T>,
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<T: SigType> VerificationKey<T> {
}
};
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

View File

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