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
|
bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
enum Inner {
|
enum Inner {
|
||||||
SpendAuth {
|
SpendAuth {
|
||||||
vk_bytes: VerificationKeyBytes<SpendAuth>,
|
vk_bytes: VerificationKeyBytes<SpendAuth>,
|
||||||
|
@ -42,6 +43,7 @@ enum Inner {
|
||||||
/// This struct exists to allow batch processing to be decoupled from the
|
/// 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
|
/// lifetime of the message. This is useful when using the batch verification API
|
||||||
/// in an async context.
|
/// in an async context.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct Item {
|
pub struct Item {
|
||||||
inner: Inner,
|
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)]
|
#[derive(Default)]
|
||||||
/// A batch verification context.
|
/// A batch verification context.
|
||||||
pub struct Verifier {
|
pub struct Verifier {
|
||||||
|
|
|
@ -136,9 +136,22 @@ impl<T: SigType> VerificationKey<T> {
|
||||||
/// Verify a purported `signature` over `msg` made by this verification key.
|
/// Verify a purported `signature` over `msg` made by this verification key.
|
||||||
// This is similar to impl signature::Verifier but without boxed errors
|
// This is similar to impl signature::Verifier but without boxed errors
|
||||||
pub fn verify(&self, msg: &[u8], signature: &Signature<T>) -> Result<(), Error> {
|
pub fn verify(&self, msg: &[u8], signature: &Signature<T>) -> Result<(), Error> {
|
||||||
#![allow(non_snake_case)]
|
|
||||||
use crate::HStar;
|
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 = {
|
let r = {
|
||||||
// XXX-jubjub: should not use CtOption here
|
// XXX-jubjub: should not use CtOption here
|
||||||
// XXX-jubjub: inconsistent ownership in from_bytes
|
// 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
|
// XXX rewrite as normal double scalar mul
|
||||||
// Verify check is h * ( - s * B + R + c * A) == 0
|
// Verify check is h * ( - s * B + R + c * A) == 0
|
||||||
// h * ( s * B - c * A - R) == 0
|
// h * ( s * B - c * A - R) == 0
|
||||||
|
|
|
@ -35,23 +35,65 @@ fn alternating_batch_verify() {
|
||||||
let rng = thread_rng();
|
let rng = thread_rng();
|
||||||
let mut batch = batch::Verifier::new();
|
let mut batch = batch::Verifier::new();
|
||||||
for i in 0..32 {
|
for i in 0..32 {
|
||||||
match i % 2 {
|
let item: batch::Item = match i % 2 {
|
||||||
0 => {
|
0 => {
|
||||||
let sk = SigningKey::<SpendAuth>::new(rng);
|
let sk = SigningKey::<SpendAuth>::new(rng);
|
||||||
let vk = VerificationKey::from(&sk);
|
let vk = VerificationKey::from(&sk);
|
||||||
let msg = b"BatchVerifyTest";
|
let msg = b"BatchVerifyTest";
|
||||||
let sig = sk.sign(rng, &msg[..]);
|
let sig = sk.sign(rng, &msg[..]);
|
||||||
batch.queue((vk.into(), sig, msg));
|
(vk.into(), sig, msg).into()
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
let sk = SigningKey::<Binding>::new(rng);
|
let sk = SigningKey::<Binding>::new(rng);
|
||||||
let vk = VerificationKey::from(&sk);
|
let vk = VerificationKey::from(&sk);
|
||||||
let msg = b"BatchVerifyTest";
|
let msg = b"BatchVerifyTest";
|
||||||
let sig = sk.sign(rng, &msg[..]);
|
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());
|
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