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:
parent
3c4e72d241
commit
7424cababb
23
src/batch.rs
23
src/batch.rs
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
_ => panic!(),
|
||||
(vk.into(), sig, msg).into()
|
||||
}
|
||||
_ => 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue