batch_evaluate without alloc (#48)

* `no_alloc` alternative to `VerifiableServer::batch_evaluate()`

* Rename `PreparedT` to `PreparedTscalar`
This commit is contained in:
daxpedda
2021-12-29 03:14:28 -05:00
committed by GitHub
parent 1c5e965446
commit d316ce4c8d
4 changed files with 256 additions and 111 deletions
+60 -23
View File
@@ -331,13 +331,10 @@
//! this case. In the following example, we show how to use the batch API to
//! produce a single proof for 10 parallel VOPRF evaluations.
//!
//! This requires the crate feature `alloc`.
//!
//! First, the client produces 10 blindings, storing their resulting states and
//! messages:
//!
//! ```
//! # #[cfg(feature = "alloc")] {
//! # #[cfg(feature = "ristretto255")]
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
//! # #[cfg(feature = "ristretto255")]
@@ -358,16 +355,14 @@
//! client_states.push(client_blind_result.state);
//! client_messages.push(client_blind_result.message);
//! }
//! # }
//! ```
//!
//! Next, the server calls the [VerifiableServer::batch_evaluate] function on a
//! set of client messages, to produce a corresponding set of messages to be
//! returned to the client (returned in the same order), along with a single
//! proof:
//! Next, the server calls the [VerifiableServer::batch_evaluate_prepare] and
//! [VerifiableServer::batch_evaluate_finish] function on a set of client
//! messages, to produce a corresponding set of messages to be returned to the
//! client (returned in the same order), along with a single proof:
//!
//! ```
//! # #[cfg(feature = "alloc")] {
//! # #[cfg(feature = "ristretto255")]
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
//! # #[cfg(feature = "ristretto255")]
@@ -376,7 +371,7 @@
//! # type Group = p256_::ProjectivePoint;
//! # #[cfg(all(feature = "p256", not(feature = "ristretto255")))]
//! # type Hash = sha2::Sha256;
//! # use voprf::VerifiableClient;
//! # use voprf::{VerifiableServerBatchEvaluatePrepareResult, VerifiableServerBatchEvaluateFinishResult, VerifiableClient};
//! # use rand::{rngs::OsRng, RngCore};
//! #
//! # let mut client_rng = OsRng;
@@ -394,7 +389,50 @@
//! let mut server_rng = OsRng;
//! # let server = VerifiableServer::<Group, Hash>::new(&mut server_rng)
//! # .expect("Unable to construct server");
//! let server_batch_evaluate_result = server
//! let VerifiableServerBatchEvaluatePrepareResult {
//! prepared_evaluation_elements,
//! t,
//! } = server
//! .batch_evaluate_prepare(client_messages.iter(), None)
//! .expect("Unable to perform server batch evaluate");
//! let prepared_elements: Vec<_> = prepared_evaluation_elements.collect();
//! let VerifiableServerBatchEvaluateFinishResult { messages, proof } = VerifiableServer::batch_evaluate_finish(&mut server_rng, client_messages.iter(), &prepared_elements, &t)
//! .expect("Unable to perform server batch evaluate");
//! let messages: Vec<_> = messages.collect();
//! ```
//!
//! If [`alloc`] is available, [VerifiableServer::batch_evaluate] can be called
//! to avoid having to collect output manually:
//!
//! ```
//! # #[cfg(feature = "alloc")] {
//! # #[cfg(feature = "ristretto255")]
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
//! # #[cfg(feature = "ristretto255")]
//! # type Hash = sha2::Sha512;
//! # #[cfg(all(feature = "p256", not(feature = "ristretto255")))]
//! # type Group = p256_::ProjectivePoint;
//! # #[cfg(all(feature = "p256", not(feature = "ristretto255")))]
//! # type Hash = sha2::Sha256;
//! # use voprf::{VerifiableServerBatchEvaluateResult, VerifiableClient};
//! # use rand::{rngs::OsRng, RngCore};
//! #
//! # let mut client_rng = OsRng;
//! # let mut client_states = vec![];
//! # let mut client_messages = vec![];
//! # for _ in 0..10 {
//! # let client_blind_result = VerifiableClient::<Group, Hash>::blind(
//! # b"input",
//! # &mut client_rng,
//! # ).expect("Unable to construct client");
//! # client_states.push(client_blind_result.state);
//! # client_messages.push(client_blind_result.message);
//! # }
//! # use voprf::VerifiableServer;
//! let mut server_rng = OsRng;
//! # let server = VerifiableServer::<Group, Hash>::new(&mut server_rng)
//! # .expect("Unable to construct server");
//! let VerifiableServerBatchEvaluateResult { messages, proof } = server
//! .batch_evaluate(&mut server_rng, &client_messages, None)
//! .expect("Unable to perform server batch evaluate");
//! # }
@@ -415,7 +453,7 @@
//! # type Group = p256_::ProjectivePoint;
//! # #[cfg(all(feature = "p256", not(feature = "ristretto255")))]
//! # type Hash = sha2::Sha256;
//! # use voprf::VerifiableClient;
//! # use voprf::{VerifiableServerBatchEvaluateResult, VerifiableClient};
//! # use rand::{rngs::OsRng, RngCore};
//! #
//! # let mut client_rng = OsRng;
@@ -430,19 +468,17 @@
//! # client_messages.push(client_blind_result.message);
//! # }
//! # use voprf::VerifiableServer;
//! let mut server_rng = OsRng;
//! # let mut server_rng = OsRng;
//! # let server = VerifiableServer::<Group, Hash>::new(&mut server_rng)
//! # .expect("Unable to construct server");
//! # let server_batch_evaluate_result = server.batch_evaluate(
//! # &mut server_rng,
//! # &client_messages,
//! # None,
//! # ).expect("Unable to perform server batch evaluate");
//! # let VerifiableServerBatchEvaluateResult { messages, proof } = server
//! # .batch_evaluate(&mut server_rng, &client_messages, None)
//! # .expect("Unable to perform server batch evaluate");
//! let client_batch_finalize_result = VerifiableClient::batch_finalize(
//! &[b"input"; 10],
//! &client_states,
//! &server_batch_evaluate_result.messages,
//! &server_batch_evaluate_result.proof,
//! &messages,
//! &proof,
//! server.get_public_key(),
//! None,
//! )
@@ -527,7 +563,8 @@ pub use crate::group::Group;
pub use crate::voprf::VerifiableServerBatchEvaluateResult;
pub use crate::voprf::{
BlindedElement, EvaluationElement, NonVerifiableClient, NonVerifiableClientBlindResult,
NonVerifiableServer, NonVerifiableServerEvaluateResult, Proof, VerifiableClient,
VerifiableClientBatchFinalizeResult, VerifiableClientBlindResult, VerifiableServer,
VerifiableServerEvaluateResult,
NonVerifiableServer, NonVerifiableServerEvaluateResult, PreparedEvaluationElement,
PreparedTscalar, Proof, VerifiableClient, VerifiableClientBatchFinalizeResult,
VerifiableClientBlindResult, VerifiableServer, VerifiableServerBatchEvaluateFinishResult,
VerifiableServerBatchEvaluatePrepareResult, VerifiableServerEvaluateResult,
};
-1
View File
@@ -5,7 +5,6 @@
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree.
#[cfg(feature = "alloc")]
mod mock_rng;
mod parser;
mod voprf_test_vectors;
+23 -22
View File
@@ -8,18 +8,14 @@
use alloc::string::{String, ToString};
use alloc::vec;
use alloc::vec::Vec;
use core::ops::Add;
use digest::core_api::BlockSizeUser;
use digest::{Digest, FixedOutputReset};
use generic_array::GenericArray;
use generic_array::typenum::Sum;
use generic_array::{ArrayLength, GenericArray};
use json::JsonValue;
#[cfg(feature = "alloc")]
use ::{
core::ops::Add,
generic_array::{typenum::Sum, ArrayLength},
};
#[cfg(feature = "alloc")]
use crate::tests::mock_rng::CycleRng;
use crate::tests::parser::*;
use crate::{
@@ -38,7 +34,6 @@ struct VOPRFTestVectorParameters {
blinded_element: Vec<Vec<u8>>,
evaluation_element: Vec<Vec<u8>>,
proof: Vec<u8>,
#[cfg(feature = "alloc")]
proof_random_scalar: Vec<u8>,
output: Vec<Vec<u8>>,
}
@@ -54,7 +49,6 @@ fn populate_test_vectors(values: &JsonValue) -> VOPRFTestVectorParameters {
blinded_element: decode_vec(values, "BlindedElement"),
evaluation_element: decode_vec(values, "EvaluationElement"),
proof: decode(values, "Proof"),
#[cfg(feature = "alloc")]
proof_random_scalar: decode(values, "ProofRandomScalar"),
output: decode_vec(values, "Output"),
}
@@ -118,7 +112,6 @@ fn test_vectors() -> Result<()> {
test_verifiable_seed_to_key::<RistrettoPoint, Sha512>(&ristretto_verifiable_tvs)?;
test_verifiable_blind::<RistrettoPoint, Sha512>(&ristretto_verifiable_tvs)?;
#[cfg(feature = "alloc")]
test_verifiable_evaluate::<RistrettoPoint, Sha512>(&ristretto_verifiable_tvs)?;
test_verifiable_finalize::<RistrettoPoint, Sha512>(&ristretto_verifiable_tvs)?;
}
@@ -253,7 +246,6 @@ fn test_base_evaluate<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>(
Ok(())
}
#[cfg(feature = "alloc")]
fn test_verifiable_evaluate<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>(
tvs: &[VOPRFTestVectorParameters],
) -> Result<()>
@@ -261,6 +253,10 @@ where
G::ScalarLen: Add<G::ScalarLen>,
Sum<G::ScalarLen, G::ScalarLen>: ArrayLength<u8>,
{
use crate::{
VerifiableServerBatchEvaluateFinishResult, VerifiableServerBatchEvaluatePrepareResult,
};
for parameters in tvs {
let mut rng = CycleRng::new(parameters.proof_random_scalar.clone());
let server = VerifiableServer::<G, H>::new_with_key(&parameters.sksm)?;
@@ -270,20 +266,25 @@ where
blinded_elements.push(BlindedElement::deserialize(blinded_element_bytes)?);
}
let batch_evaluate_result =
server.batch_evaluate(&mut rng, &blinded_elements, Some(&parameters.info))?;
let VerifiableServerBatchEvaluatePrepareResult {
prepared_evaluation_elements,
t,
} = server.batch_evaluate_prepare(blinded_elements.iter(), Some(&parameters.info))?;
let prepared_elements: Vec<_> = prepared_evaluation_elements.collect();
let VerifiableServerBatchEvaluateFinishResult { messages, proof } =
VerifiableServer::batch_evaluate_finish(
&mut rng,
blinded_elements.iter(),
&prepared_elements,
&t,
)?;
let messages: Vec<_> = messages.collect();
for i in 0..parameters.evaluation_element.len() {
assert_eq!(
&parameters.evaluation_element[i],
&batch_evaluate_result.messages[i].serialize().as_slice(),
);
for (parameter, message) in parameters.evaluation_element.iter().zip(messages) {
assert_eq!(&parameter, &message.serialize().as_slice(),);
}
assert_eq!(
&parameters.proof,
&batch_evaluate_result.proof.serialize().as_slice()
);
assert_eq!(&parameters.proof, &proof.serialize().as_slice());
}
Ok(())
}
+173 -65
View File
@@ -511,23 +511,27 @@ impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> VerifiableServer<G,
blinded_element: &BlindedElement<G, H>,
metadata: Option<&[u8]>,
) -> Result<VerifiableServerEvaluateResult<G, H>> {
let (mut evaluation_elements, t) =
self.batch_evaluate_1(Some(blinded_element.copy()).into_iter(), metadata)?;
let evaluation_element = evaluation_elements.next().unwrap();
let proof = Self::batch_evaluate_2(
rng,
Some(blinded_element.copy()).into_iter(),
Some(evaluation_element.copy()).into_iter(),
let VerifiableServerBatchEvaluatePrepareResult {
prepared_evaluation_elements: mut evaluation_elements,
t,
} = self.batch_evaluate_prepare(Some(blinded_element).into_iter(), metadata)?;
let prepared_element = [evaluation_elements.next().unwrap()];
let VerifiableServerBatchEvaluateFinishResult {
mut messages,
proof,
} = Self::batch_evaluate_finish(
rng,
Some(blinded_element).into_iter(),
&prepared_element,
&t,
)?;
let message = messages.next().unwrap();
//let batch_result = self.batch_evaluate(rng, blinded_elements, metadata)?;
Ok(VerifiableServerEvaluateResult {
message: evaluation_element,
proof,
})
Ok(VerifiableServerEvaluateResult { message, proof })
}
/// Allows for batching of the evaluation of multiple [BlindedElement]
@@ -545,37 +549,36 @@ impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> VerifiableServer<G,
&'a I: IntoIterator<Item = &'a BlindedElement<G, H>>,
<&'a I as IntoIterator>::IntoIter: ExactSizeIterator,
{
let (evaluation_elements, t) = self.batch_evaluate_1(
blinded_elements.into_iter().map(BlindedElement::copy),
metadata,
)?;
let evaluation_elements: Vec<_> = evaluation_elements.collect();
let proof = Self::batch_evaluate_2(
rng,
blinded_elements.into_iter().map(BlindedElement::copy),
evaluation_elements.iter().map(EvaluationElement::copy),
let VerifiableServerBatchEvaluatePrepareResult {
prepared_evaluation_elements: evaluation_elements,
t,
)?;
} = self.batch_evaluate_prepare(blinded_elements.into_iter(), metadata)?;
let prepared_elements = evaluation_elements.collect();
let VerifiableServerBatchEvaluateFinishResult { messages, proof } =
Self::batch_evaluate_finish::<_, _, Vec<_>>(
rng,
blinded_elements.into_iter(),
&prepared_elements,
&t,
)?;
Ok(VerifiableServerBatchEvaluateResult {
messages: evaluation_elements,
messages: messages.collect(),
proof,
})
}
fn batch_evaluate_1<I>(
/// Alternative version of [`batch_evaluate`](Self::batch_evaluate) without
/// memory allocation. Returned [`PreparedEvaluationElement`] have to be
/// [`collect`](Iterator::collect)ed and passed into
/// [`batch_evaluate_finish`](Self::batch_evaluate_finish).
pub fn batch_evaluate_prepare<'a, I: Iterator<Item = &'a BlindedElement<G, H>>>(
&self,
blinded_elements: I,
metadata: Option<&[u8]>,
) -> Result<(
impl Iterator<Item = EvaluationElement<G, H>> + ExactSizeIterator,
G::Scalar,
)>
where
I: Iterator<Item = BlindedElement<G, H>> + ExactSizeIterator,
{
) -> Result<VerifiableServerBatchEvaluatePrepareResult<'a, G, H, I>> {
chain!(context,
STR_CONTEXT => |x| Some(x.as_ref()),
get_context_string::<G>(Mode::Verifiable)? => |x| Some(x.as_slice()),
@@ -585,30 +588,62 @@ impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> VerifiableServer<G,
.concat(get_context_string::<G>(Mode::Verifiable)?);
let m = G::hash_to_scalar::<H, _, _>(context, dst)?;
let t = self.sk + &m;
let evaluation_elements = blinded_elements.map(move |x| EvaluationElement {
value: x.value * &G::scalar_invert(&t),
hash: PhantomData,
});
let evaluation_elements = blinded_elements
// To make a return type possible, we have to convert to a `fn` pointer, which isn't
// possible if we `move` from context.
.zip(iter::repeat(G::scalar_invert(&t)))
.map(<fn((&BlindedElement<G, H>, _)) -> _>::from(|(x, t)| {
PreparedEvaluationElement(EvaluationElement {
value: x.value * &t,
hash: PhantomData,
})
}));
Ok((evaluation_elements, t))
Ok(VerifiableServerBatchEvaluatePrepareResult {
prepared_evaluation_elements: evaluation_elements,
t: PreparedTscalar {
t,
hash: PhantomData,
},
})
}
/// Allows for batching of the evaluation of multiple [BlindedElement]
/// messages from a [VerifiableClient]
fn batch_evaluate_2<R: RngCore + CryptoRng, IE, IB>(
/// See [`batch_evaluate_prepare`](Self::batch_evaluate_prepare) for more
/// details.
pub fn batch_evaluate_finish<'a, 'b, R: RngCore + CryptoRng, IB, IE>(
rng: &mut R,
blinded_elements: IB,
evaluation_elements: IE,
t: G::Scalar,
) -> Result<Proof<G, H>>
evaluation_elements: &'b IE,
PreparedTscalar { t, .. }: &PreparedTscalar<G, H>,
) -> Result<VerifiableServerBatchEvaluateFinishResult<'b, G, H, IE>>
where
IB: Iterator<Item = BlindedElement<G, H>> + ExactSizeIterator,
IE: Iterator<Item = EvaluationElement<G, H>> + ExactSizeIterator,
G: 'a + 'b,
H: 'a + 'b,
IB: Iterator<Item = &'a BlindedElement<G, H>> + ExactSizeIterator,
&'b IE: IntoIterator<Item = &'b PreparedEvaluationElement<G, H>>,
<&'b IE as IntoIterator>::IntoIter: ExactSizeIterator,
{
let g = G::base_point();
let u = g * &t;
let u = g * t;
generate_proof(rng, t, g, u, evaluation_elements, blinded_elements)
let proof = generate_proof(
rng,
*t,
g,
u,
evaluation_elements
.into_iter()
.map(|element| element.0.copy()),
blinded_elements.map(BlindedElement::copy),
)?;
let messages =
evaluation_elements
.into_iter()
.map(<fn(&PreparedEvaluationElement<G, H>) -> _>::from(
|element| element.0.copy(),
));
Ok(VerifiableServerBatchEvaluateFinishResult { messages, proof })
}
/// Retrieves the server's public key
@@ -662,6 +697,59 @@ pub struct VerifiableServerEvaluateResult<G: Group, H: BlockSizeUser + Digest +
pub proof: Proof<G, H>,
}
/// Contains prepared [`EvaluationElement`]s by a verifiable server batch
/// evaluate preparation.
pub struct PreparedEvaluationElement<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>(
EvaluationElement<G, H>,
);
/// Contains the prepared `t` by a verifiable server batch evaluate preparation.
#[derive(DeriveWhere)]
#[derive_where(Zeroize(drop))]
pub struct PreparedTscalar<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> {
t: G::Scalar,
#[derive_where(skip)]
hash: PhantomData<H>,
}
/// Contains the fields that are returned by a verifiable server batch evaluate
/// preparation.
pub struct VerifiableServerBatchEvaluatePrepareResult<
'a,
G: 'a + Group,
H: 'a + BlockSizeUser + Digest + FixedOutputReset,
I: Iterator<Item = &'a BlindedElement<G, H>>,
> {
/// Prepared [`EvaluationElement`]s that will become messages.
#[allow(clippy::type_complexity)]
pub prepared_evaluation_elements: Map<
Zip<I, Repeat<G::Scalar>>,
fn((&BlindedElement<G, H>, G::Scalar)) -> PreparedEvaluationElement<G, H>,
>,
/// Prepared `t` needed to finish the verifiable server batch evaluation.
pub t: PreparedTscalar<G, H>,
}
/// Contains the fields that are returned by a verifiable server batch evaluate
/// finish.
pub struct VerifiableServerBatchEvaluateFinishResult<
'a,
G: 'a + Group,
H: 'a + BlockSizeUser + Digest + FixedOutputReset,
I,
> where
&'a I: IntoIterator<Item = &'a PreparedEvaluationElement<G, H>>,
{
/// The messages to send to the client
#[allow(clippy::type_complexity)]
pub messages: Map<
<&'a I as IntoIterator>::IntoIter,
fn(&PreparedEvaluationElement<G, H>) -> EvaluationElement<G, H>,
>,
/// The proof for the client to verify
pub proof: Proof<G, H>,
}
/// Contains the fields that are returned by a verifiable server batch evaluate
#[cfg(feature = "alloc")]
pub struct VerifiableServerBatchEvaluateResult<
@@ -1008,12 +1096,12 @@ fn get_context_string<G: Group>(mode: Mode) -> Result<GenericArray<u8, U11>> {
mod tests {
use core::ops::Add;
use ::alloc::vec;
use ::alloc::vec::Vec;
use generic_array::typenum::Sum;
use generic_array::{ArrayLength, GenericArray};
use rand::rngs::OsRng;
use zeroize::Zeroize;
#[cfg(feature = "alloc")]
use ::{alloc::vec, alloc::vec::Vec};
use super::*;
use crate::Group;
@@ -1087,7 +1175,6 @@ mod tests {
assert_eq!(client_finalize_result, res2);
}
#[cfg(feature = "alloc")]
fn verifiable_bad_public_key<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>() {
let input = b"input";
let info = b"info";
@@ -1111,7 +1198,6 @@ mod tests {
assert!(client_finalize_result.is_err());
}
#[cfg(feature = "alloc")]
fn verifiable_batch_retrieval<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>() {
let info = b"info";
let mut rng = OsRng;
@@ -1128,14 +1214,27 @@ mod tests {
client_messages.push(client_blind_result.message);
}
let server = VerifiableServer::<G, H>::new(&mut rng).unwrap();
let server_result = server
.batch_evaluate(&mut rng, &client_messages, Some(info))
let VerifiableServerBatchEvaluatePrepareResult {
prepared_evaluation_elements,
t,
} = server
.batch_evaluate_prepare(client_messages.iter(), Some(info))
.unwrap();
let prepared_elements: Vec<_> = prepared_evaluation_elements.collect();
let VerifiableServerBatchEvaluateFinishResult { messages, proof } =
VerifiableServer::batch_evaluate_finish(
&mut rng,
client_messages.iter(),
&prepared_elements,
&t,
)
.unwrap();
let messages: Vec<_> = messages.collect();
let client_finalize_result = VerifiableClient::batch_finalize(
&inputs,
&client_states,
&server_result.messages,
&server_result.proof,
&messages,
&proof,
server.get_public_key(),
Some(info),
)
@@ -1150,7 +1249,6 @@ mod tests {
assert_eq!(client_finalize_result, res2);
}
#[cfg(feature = "alloc")]
fn verifiable_batch_bad_public_key<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>() {
let info = b"info";
let mut rng = OsRng;
@@ -1167,9 +1265,22 @@ mod tests {
client_messages.push(client_blind_result.message);
}
let server = VerifiableServer::<G, H>::new(&mut rng).unwrap();
let server_result = server
.batch_evaluate(&mut rng, &client_messages, Some(info))
let VerifiableServerBatchEvaluatePrepareResult {
prepared_evaluation_elements,
t,
} = server
.batch_evaluate_prepare(client_messages.iter(), Some(info))
.unwrap();
let prepared_elements: Vec<_> = prepared_evaluation_elements.collect();
let VerifiableServerBatchEvaluateFinishResult { messages, proof } =
VerifiableServer::batch_evaluate_finish(
&mut rng,
client_messages.iter(),
&prepared_elements,
&t,
)
.unwrap();
let messages: Vec<_> = messages.collect();
let wrong_pk = {
// Choose a group element that is unlikely to be the right public key
G::hash_to_curve::<H, _>(b"msg", (*b"dst").into()).unwrap()
@@ -1177,8 +1288,8 @@ mod tests {
let client_finalize_result = VerifiableClient::batch_finalize(
&inputs,
&client_states,
&server_result.messages,
&server_result.proof,
&messages,
&proof,
wrong_pk,
Some(info),
);
@@ -1309,11 +1420,8 @@ mod tests {
base_retrieval::<RistrettoPoint, Sha512>();
base_inversion_unsalted::<RistrettoPoint, Sha512>();
verifiable_retrieval::<RistrettoPoint, Sha512>();
#[cfg(feature = "alloc")]
verifiable_batch_retrieval::<RistrettoPoint, Sha512>();
#[cfg(feature = "alloc")]
verifiable_bad_public_key::<RistrettoPoint, Sha512>();
#[cfg(feature = "alloc")]
verifiable_batch_bad_public_key::<RistrettoPoint, Sha512>();
zeroize_base_client::<RistrettoPoint, Sha512>();