diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 279025a..76909d8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -41,8 +41,8 @@ jobs: - ristretto255_u64,p256 frontend_feature: - - - --features serde - --features danger + - --features serde toolchain: - stable - 1.51.0 @@ -64,6 +64,12 @@ jobs: command: test args: --no-default-features --features ${{ matrix.backend_feature }} + - name: Run cargo test with alloc + uses: actions-rs/cargo@v1 + with: + command: test + args: --no-default-features ${{ matrix.frontend_feature }},alloc --features ${{ matrix.backend_feature }} + - name: Run cargo test with std uses: actions-rs/cargo@v1 with: @@ -88,8 +94,8 @@ jobs: - --features p256 frontend_feature: - - - --features serde - --features danger + - --features serde steps: - uses: actions/checkout@v2 - uses: hecrj/setup-rust-action@v1 diff --git a/Cargo.toml b/Cargo.toml index d7782b4..4e240ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,16 +13,24 @@ rust-version = "1.51.0" version = "0.3.0" [features] +alloc = [] danger = [] default = ["ristretto255_u64", "serde"] -p256 = ["num-bigint", "num-integer", "num-traits", "once_cell", "p256_"] +p256 = [ + "alloc", + "num-bigint", + "num-integer", + "num-traits", + "once_cell", + "p256_", +] ristretto255 = [] ristretto255_fiat_u32 = ["curve25519-dalek/fiat_u32_backend", "ristretto255"] ristretto255_fiat_u64 = ["curve25519-dalek/fiat_u64_backend", "ristretto255"] ristretto255_simd = ["curve25519-dalek/simd_backend", "ristretto255"] ristretto255_u32 = ["curve25519-dalek/u32_backend", "ristretto255"] ristretto255_u64 = ["curve25519-dalek/u64_backend", "ristretto255"] -std = [] +std = ["alloc"] [dependencies] curve25519-dalek = { version = "3", default-features = false, optional = true } @@ -39,7 +47,9 @@ p256_ = { package = "p256", version = "0.9", default-features = false, features "zeroize", ], optional = true } rand_core = { version = "0.6", default-features = false } -serde = { version = "1", default-features = false, optional = true } +serde = { version = "1", default-features = false, features = [ + "derive", +], optional = true } subtle = { version = "2.3", default-features = false } zeroize = { version = "1", default-features = false } diff --git a/src/lib.rs b/src/lib.rs index a6d6830..1ede007 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -90,9 +90,8 @@ //! use voprf::NonVerifiableClient; //! //! let mut client_rng = OsRng; -//! let client_blind_result = -//! NonVerifiableClient::::blind(b"input".to_vec(), &mut client_rng) -//! .expect("Unable to construct client"); +//! let client_blind_result = NonVerifiableClient::::blind(b"input", &mut client_rng) +//! .expect("Unable to construct client"); //! ``` //! //! ### Server Evaluation @@ -117,7 +116,7 @@ //! # //! # let mut client_rng = OsRng; //! # let client_blind_result = NonVerifiableClient::::blind( -//! # b"input".to_vec(), +//! # b"input", //! # &mut client_rng, //! # ).expect("Unable to construct client"); //! # use voprf::NonVerifiableServer; @@ -149,7 +148,7 @@ //! # //! # let mut client_rng = OsRng; //! # let client_blind_result = NonVerifiableClient::::blind( -//! # b"input".to_vec(), +//! # b"input", //! # &mut client_rng, //! # ).expect("Unable to construct client"); //! # use voprf::NonVerifiableServer; @@ -162,7 +161,7 @@ //! # ).expect("Unable to perform server evaluate"); //! let client_finalize_result = client_blind_result //! .state -//! .finalize(&server_evaluate_result.message, None) +//! .finalize(b"input", &server_evaluate_result.message, None) //! .expect("Unable to perform client finalization"); //! //! println!("VOPRF output: {:?}", client_finalize_result.to_vec()); @@ -233,9 +232,8 @@ //! use voprf::VerifiableClient; //! //! let mut client_rng = OsRng; -//! let client_blind_result = -//! VerifiableClient::::blind(b"input".to_vec(), &mut client_rng) -//! .expect("Unable to construct client"); +//! let client_blind_result = VerifiableClient::::blind(b"input", &mut client_rng) +//! .expect("Unable to construct client"); //! ``` //! //! ### Server Evaluation @@ -260,7 +258,7 @@ //! # //! # let mut client_rng = OsRng; //! # let client_blind_result = VerifiableClient::::blind( -//! # b"input".to_vec(), +//! # b"input", //! # &mut client_rng, //! # ).expect("Unable to construct client"); //! # use voprf::VerifiableServer; @@ -293,7 +291,7 @@ //! # //! # let mut client_rng = OsRng; //! # let client_blind_result = VerifiableClient::::blind( -//! # b"input".to_vec(), +//! # b"input", //! # &mut client_rng, //! # ).expect("Unable to construct client"); //! # use voprf::VerifiableServer; @@ -308,6 +306,7 @@ //! let client_finalize_result = client_blind_result //! .state //! .finalize( +//! b"input", //! &server_evaluate_result.message, //! &server_evaluate_result.proof, //! server.get_public_key(), @@ -332,10 +331,13 @@ //! 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")] @@ -351,12 +353,12 @@ //! let mut client_states = vec![]; //! let mut client_messages = vec![]; //! for _ in 0..10 { -//! let client_blind_result = -//! VerifiableClient::::blind(b"input".to_vec(), &mut client_rng) -//! .expect("Unable to construct client"); +//! let client_blind_result = VerifiableClient::::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); //! } +//! # } //! ``` //! //! Next, the server calls the [VerifiableServer::batch_evaluate] function on a @@ -365,6 +367,7 @@ //! proof: //! //! ``` +//! # #[cfg(feature = "alloc")] { //! # #[cfg(feature = "ristretto255")] //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # #[cfg(feature = "ristretto255")] @@ -381,7 +384,7 @@ //! # let mut client_messages = vec![]; //! # for _ in 0..10 { //! # let client_blind_result = VerifiableClient::::blind( -//! # b"input".to_vec(), +//! # b"input", //! # &mut client_rng, //! # ).expect("Unable to construct client"); //! # client_states.push(client_blind_result.state); @@ -394,6 +397,7 @@ //! let server_batch_evaluate_result = server //! .batch_evaluate(&mut server_rng, &client_messages, None) //! .expect("Unable to perform server batch evaluate"); +//! # } //! ``` //! //! Then, the client calls [VerifiableClient::batch_finalize] on the client @@ -402,6 +406,7 @@ //! outputs if the proof verifies correctly. //! //! ``` +//! # #[cfg(feature = "alloc")] { //! # #[cfg(feature = "ristretto255")] //! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # #[cfg(feature = "ristretto255")] @@ -418,7 +423,7 @@ //! # let mut client_messages = vec![]; //! # for _ in 0..10 { //! # let client_blind_result = VerifiableClient::::blind( -//! # b"input".to_vec(), +//! # b"input", //! # &mut client_rng, //! # ).expect("Unable to construct client"); //! # client_states.push(client_blind_result.state); @@ -434,15 +439,18 @@ //! # 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, //! server.get_public_key(), //! None, //! ) -//! .expect("Unable to perform client batch finalization"); +//! .expect("Unable to perform client batch finalization") +//! .collect::>(); //! //! println!("VOPRF batch outputs: {:?}", client_batch_finalize_result); +//! # } //! ``` //! //! ## Metadata @@ -460,6 +468,9 @@ //! //! # Features //! +//! - The `alloc` feature requires Rusts [`alloc`] crate and enables batching +//! VOPRF evaluations. +//! //! - The `p256` feature enables using p256 as the underlying group for the //! [Group](group::Group) choice. Note that this is currently an experimental //! feature ⚠️, and is not yet ready for production use. @@ -491,6 +502,7 @@ #![warn(clippy::cargo, missing_docs)] #![allow(clippy::multiple_crate_versions)] +#[cfg(any(feature = "alloc", test))] extern crate alloc; #[cfg(feature = "std")] diff --git a/src/serialization.rs b/src/serialization.rs index 1e0be7d..ef3b10d 100644 --- a/src/serialization.rs +++ b/src/serialization.rs @@ -8,14 +8,17 @@ //! Handles the serialization of each of the components used in the VOPRF //! protocol -use alloc::vec::Vec; use core::marker::PhantomData; +use core::ops::Add; use digest::{BlockInput, Digest}; -use generic_array::typenum::Unsigned; +use generic_array::sequence::Concat; +use generic_array::typenum::Sum; +use generic_array::{ArrayLength, GenericArray}; use crate::errors::InternalError; use crate::group::Group; +use crate::util::deserialize; use crate::voprf::{ BlindedElement, EvaluationElement, NonVerifiableClient, NonVerifiableServer, Proof, VerifiableClient, VerifiableServer, @@ -28,23 +31,18 @@ use crate::voprf::{ impl NonVerifiableClient { /// Serialization into bytes - pub fn serialize(&self) -> Vec { - [G::scalar_as_bytes(self.blind).as_slice(), &self.data].concat() + pub fn serialize(&self) -> GenericArray { + G::scalar_as_bytes(self.blind) } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { - let scalar_len = G::ScalarLen::USIZE; - if input.len() < scalar_len { - return Err(InternalError::SizeError); - } + let mut input = input.iter().copied(); - let blind = G::from_scalar_slice(&input[..scalar_len])?; - let data = input[scalar_len..].to_vec(); + let blind = G::from_scalar_slice(&deserialize(&mut input)?)?; Ok(Self { blind, - data, hash: PhantomData, }) } @@ -52,31 +50,24 @@ impl NonVerifiableClient { impl VerifiableClient { /// Serialization into bytes - pub fn serialize(&self) -> Vec { - [ - G::scalar_as_bytes(self.blind).as_slice(), - &self.blinded_element.to_arr(), - &self.data, - ] - .concat() + pub fn serialize(&self) -> GenericArray> + where + G::ScalarLen: Add, + Sum: ArrayLength, + { + G::scalar_as_bytes(self.blind).concat(self.blinded_element.to_arr()) } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { - let scalar_len = G::ScalarLen::USIZE; - let elem_len = G::ElemLen::USIZE; - if input.len() < scalar_len + elem_len { - return Err(InternalError::SizeError); - } + let mut input = input.iter().copied(); - let blind = G::from_scalar_slice(&input[..scalar_len])?; - let blinded_element = G::from_element_slice(&input[scalar_len..scalar_len + elem_len])?; - let data = input[scalar_len + elem_len..].to_vec(); + let blind = G::from_scalar_slice(&deserialize(&mut input)?)?; + let blinded_element = G::from_element_slice(&deserialize(&mut input)?)?; Ok(Self { blind, blinded_element, - data, hash: PhantomData, }) } @@ -84,18 +75,15 @@ impl VerifiableClient { impl NonVerifiableServer { /// Serialization into bytes - pub fn serialize(&self) -> Vec { - G::scalar_as_bytes(self.sk).to_vec() + pub fn serialize(&self) -> GenericArray { + G::scalar_as_bytes(self.sk) } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { - let scalar_len = G::ScalarLen::USIZE; - if input.len() != scalar_len { - return Err(InternalError::SizeError); - } + let mut input = input.iter().copied(); - let sk = G::from_scalar_slice(input)?; + let sk = G::from_scalar_slice(&deserialize(&mut input)?)?; Ok(Self { sk, @@ -106,20 +94,20 @@ impl NonVerifiableServer { impl VerifiableServer { /// Serialization into bytes - pub fn serialize(&self) -> Vec { - [G::scalar_as_bytes(self.sk).as_slice(), &self.pk.to_arr()].concat() + pub fn serialize(&self) -> GenericArray> + where + G::ScalarLen: Add, + Sum: ArrayLength, + { + G::scalar_as_bytes(self.sk).concat(self.pk.to_arr()) } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { - let scalar_len = G::ScalarLen::USIZE; - let elem_len = G::ElemLen::USIZE; - if input.len() != scalar_len + elem_len { - return Err(InternalError::SizeError); - } + let mut input = input.iter().copied(); - let sk = G::from_scalar_slice(&input[..scalar_len])?; - let pk = G::from_element_slice(&input[scalar_len..])?; + let sk = G::from_scalar_slice(&deserialize(&mut input)?)?; + let pk = G::from_element_slice(&deserialize(&mut input)?)?; Ok(Self { sk, @@ -131,23 +119,24 @@ impl VerifiableServer { impl Proof { /// Serialization into bytes - pub fn serialize(&self) -> Vec { - [ - G::scalar_as_bytes(self.c_scalar), - G::scalar_as_bytes(self.s_scalar), - ] - .concat() + pub fn serialize(&self) -> GenericArray> + where + G::ScalarLen: Add, + Sum: ArrayLength, + { + G::scalar_as_bytes(self.c_scalar).concat(G::scalar_as_bytes(self.s_scalar)) } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { - let scalar_len = G::ScalarLen::USIZE; - if input.len() != scalar_len + scalar_len { - return Err(InternalError::SizeError); - } + let mut input = input.iter().copied(); + + let c_scalar = G::from_scalar_slice(&deserialize(&mut input)?)?; + let s_scalar = G::from_scalar_slice(&deserialize(&mut input)?)?; + Ok(Proof { - c_scalar: G::from_scalar_slice(&input[..scalar_len])?, - s_scalar: G::from_scalar_slice(&input[scalar_len..])?, + c_scalar, + s_scalar, hash: PhantomData, }) } @@ -155,18 +144,18 @@ impl Proof { impl BlindedElement { /// Serialization into bytes - pub fn serialize(&self) -> Vec { - self.value.to_arr().to_vec() + pub fn serialize(&self) -> GenericArray { + self.value.to_arr() } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { - let elem_len = G::ElemLen::USIZE; - if input.len() != elem_len { - return Err(InternalError::SizeError); - } + let mut input = input.iter().copied(); + + let value = G::from_element_slice(&deserialize(&mut input)?)?; + Ok(Self { - value: G::from_element_slice(input)?, + value, hash: PhantomData, }) } @@ -174,84 +163,19 @@ impl BlindedElement { impl EvaluationElement { /// Serialization into bytes - pub fn serialize(&self) -> Vec { - self.value.to_arr().to_vec() + pub fn serialize(&self) -> GenericArray { + self.value.to_arr() } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { - let elem_len = G::ElemLen::USIZE; - if input.len() != elem_len { - return Err(InternalError::SizeError); - } + let mut input = input.iter().copied(); + + let value = G::from_element_slice(&deserialize(&mut input)?)?; + Ok(Self { - value: G::from_element_slice(input)?, + value, hash: PhantomData, }) } } - -///////////////////////////////////////////// -// Serde implementation for High-Level API // -// ======================================= // -///////////////////////////////////////////// - -/// Macro used for deriving `serde`'s `Serialize` and `Deserialize` traits. -macro_rules! impl_serialize_and_deserialize_for { - ($item:ident) => { - #[cfg(feature = "serde")] - impl serde::Serialize for $item { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_bytes(&self.serialize()) - } - } - - #[cfg(feature = "serde")] - impl<'de, G: Group, H: BlockInput + Digest> serde::Deserialize<'de> for $item { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - use serde::de::Error; - - struct ByteVisitor(core::marker::PhantomData<(G, H)>); - - impl<'de, G: Group, H: BlockInput + Digest> serde::de::Visitor<'de> for ByteVisitor { - type Value = $item; - - fn expecting( - &self, - formatter: &mut core::fmt::Formatter, - ) -> core::fmt::Result { - formatter.write_str(core::concat!( - "the byte representation of a ", - core::stringify!($item) - )) - } - - fn visit_bytes(self, value: &[u8]) -> Result - where - E: Error, - { - $item::::deserialize(value).map_err(|_| { - Error::invalid_value( - serde::de::Unexpected::Bytes(value), - &core::concat!( - "invalid byte sequence for ", - core::stringify!($item) - ), - ) - }) - } - } - - deserializer - .deserialize_bytes(ByteVisitor::(core::marker::PhantomData)) - .map_err(Error::custom) - } - } - }; -} diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 7665c25..148aedc 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -5,6 +5,7 @@ // 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; diff --git a/src/tests/voprf_test_vectors.rs b/src/tests/voprf_test_vectors.rs index d75fddc..5b0487c 100644 --- a/src/tests/voprf_test_vectors.rs +++ b/src/tests/voprf_test_vectors.rs @@ -12,9 +12,15 @@ use alloc::vec::Vec; use digest::{BlockInput, Digest}; use generic_array::GenericArray; use json::JsonValue; +#[cfg(feature = "alloc")] +use ::{ + core::ops::Add, + generic_array::{typenum::Sum, ArrayLength}, +}; use crate::errors::InternalError; use crate::group::Group; +#[cfg(feature = "alloc")] use crate::tests::mock_rng::CycleRng; use crate::tests::parser::*; use crate::voprf::{ @@ -33,6 +39,7 @@ struct VOPRFTestVectorParameters { blinded_element: Vec>, evaluation_element: Vec>, proof: Vec, + #[cfg(feature = "alloc")] proof_random_scalar: Vec, output: Vec>, } @@ -48,6 +55,7 @@ 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"), } @@ -111,6 +119,7 @@ fn test_vectors() -> Result<(), InternalError> { test_verifiable_seed_to_key::(&ristretto_verifiable_tvs)?; test_verifiable_blind::(&ristretto_verifiable_tvs)?; + #[cfg(feature = "alloc")] test_verifiable_evaluate::(&ristretto_verifiable_tvs)?; test_verifiable_finalize::(&ristretto_verifiable_tvs)?; } @@ -181,7 +190,7 @@ fn test_base_blind( let blind = G::from_scalar_slice(&GenericArray::clone_from_slice(¶meters.blind[i]))?; let client_result = NonVerifiableClient::::deterministic_blind_unchecked( - parameters.input[i].clone(), + ¶meters.input[i], blind, )?; @@ -190,8 +199,8 @@ fn test_base_blind( &G::scalar_as_bytes(client_result.state.blind).to_vec() ); assert_eq!( - ¶meters.blinded_element[i], - &client_result.message.serialize() + parameters.blinded_element[i].as_slice(), + client_result.message.serialize().as_slice(), ); } } @@ -207,7 +216,7 @@ fn test_verifiable_blind( let blind = G::from_scalar_slice(&GenericArray::clone_from_slice(¶meters.blind[i]))?; let client_blind_result = VerifiableClient::::deterministic_blind_unchecked( - parameters.input[i].clone(), + ¶meters.input[i], blind, )?; @@ -216,8 +225,8 @@ fn test_verifiable_blind( &G::scalar_as_bytes(client_blind_result.state.get_blind()).to_vec() ); assert_eq!( - ¶meters.blinded_element[i], - &client_blind_result.message.serialize() + parameters.blinded_element[i].as_slice(), + client_blind_result.message.serialize().as_slice(), ); } } @@ -238,16 +247,21 @@ fn test_base_evaluate( assert_eq!( ¶meters.evaluation_element[i], - &server_result.message.serialize() + &server_result.message.serialize().as_slice() ); } } Ok(()) } +#[cfg(feature = "alloc")] fn test_verifiable_evaluate( tvs: &[VOPRFTestVectorParameters], -) -> Result<(), InternalError> { +) -> Result<(), InternalError> +where + G::ScalarLen: Add, + Sum: ArrayLength, +{ for parameters in tvs { let mut rng = CycleRng::new(parameters.proof_random_scalar.clone()); let server = VerifiableServer::::new_with_key(¶meters.sksm)?; @@ -263,11 +277,14 @@ fn test_verifiable_evaluate( for i in 0..parameters.evaluation_element.len() { assert_eq!( ¶meters.evaluation_element[i], - &batch_evaluate_result.messages[i].serialize(), + &batch_evaluate_result.messages[i].serialize().as_slice(), ); } - assert_eq!(¶meters.proof, &batch_evaluate_result.proof.serialize()); + assert_eq!( + ¶meters.proof, + &batch_evaluate_result.proof.serialize().as_slice() + ); } Ok(()) } @@ -278,12 +295,12 @@ fn test_base_finalize( ) -> Result<(), InternalError> { for parameters in tvs { for i in 0..parameters.input.len() { - let client = NonVerifiableClient::::from_data_and_blind( - ¶meters.input[i], - G::from_scalar_slice(&GenericArray::clone_from_slice(¶meters.blind[i]))?, - ); + let client = NonVerifiableClient::::from_blind(G::from_scalar_slice( + &GenericArray::clone_from_slice(¶meters.blind[i]), + )?); let client_finalize_result = client.finalize( + ¶meters.input[i], &EvaluationElement::deserialize(¶meters.evaluation_element[i])?, Some(¶meters.info), )?; @@ -300,8 +317,7 @@ fn test_verifiable_finalize( for parameters in tvs { let mut clients = vec![]; for i in 0..parameters.input.len() { - let client = VerifiableClient::::from_data_and_blind_and_element( - ¶meters.input[i], + let client = VerifiableClient::::from_blind_and_element( G::from_scalar_slice(&GenericArray::clone_from_slice(¶meters.blind[i]))?, G::from_element_slice(&GenericArray::clone_from_slice( ¶meters.blinded_element[i], @@ -317,6 +333,7 @@ fn test_verifiable_finalize( .collect(); let batch_result = VerifiableClient::batch_finalize( + ¶meters.input, &clients, &messages, &Proof::deserialize(¶meters.proof)?, @@ -327,9 +344,8 @@ fn test_verifiable_finalize( assert_eq!( parameters.output, batch_result - .iter() - .map(|arr| arr.to_vec()) - .collect::>>() + .map(|arr| arr.map(|message| message.to_vec())) + .collect::, _>>()? ); } Ok(()) diff --git a/src/util.rs b/src/util.rs index 94106e9..16e518e 100644 --- a/src/util.rs +++ b/src/util.rs @@ -85,6 +85,13 @@ pub(crate) fn serialize_owned, L2: ArrayLength>( }) } +pub(crate) fn deserialize>( + input: &mut impl Iterator, +) -> Result, InternalError> { + let input = input.by_ref().take(L::USIZE); + GenericArray::from_exact_iter(input).ok_or(InternalError::SizeError) +} + macro_rules! chain_name { ($var:ident, $mod:ident) => { $mod @@ -103,7 +110,7 @@ macro_rules! chain_skip { }; } -/// The purpose of this macro is to simplify +/// The purpose of this macro is to replace /// [`concat`](alloc::slice::Concat::concat)ing slices into an [`Iterator`] to /// avoid allocation macro_rules! chain { diff --git a/src/voprf.rs b/src/voprf.rs index 92dba94..dd0a9b7 100644 --- a/src/voprf.rs +++ b/src/voprf.rs @@ -7,14 +7,16 @@ //! Contains the main VOPRF API +#[cfg(feature = "alloc")] use alloc::vec::Vec; use core::convert::TryInto; +use core::iter::{self, Map, Repeat, Zip}; use core::marker::PhantomData; use derive_where::DeriveWhere; use digest::{BlockInput, Digest}; use generic_array::sequence::Concat; -use generic_array::typenum::{U1, U11, U2}; +use generic_array::typenum::{U1, U11, U2, U20}; use generic_array::GenericArray; use rand_core::{CryptoRng, RngCore}; use subtle::ConstantTimeEq; @@ -54,48 +56,72 @@ enum Mode { #[derive(DeriveWhere)] #[derive_where(Clone, Zeroize(drop))] #[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; G::Scalar)] +#[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde(bound( + deserialize = "G::Scalar: serde::Deserialize<'de>", + serialize = "G::Scalar: serde::Serialize" + )) +)] pub struct NonVerifiableClient { pub(crate) blind: G::Scalar, - pub(crate) data: Vec, #[derive_where(skip(Zeroize))] pub(crate) hash: PhantomData, } -impl_serialize_and_deserialize_for!(NonVerifiableClient); - /// A client which engages with a [VerifiableServer] in verifiable mode, meaning /// that the OPRF outputs can be checked against a server public key. #[derive(DeriveWhere)] #[derive_where(Clone, Zeroize(drop))] #[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; G, G::Scalar)] +#[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde(bound( + deserialize = "G::Scalar: serde::Deserialize<'de>, G: serde::Deserialize<'de>", + serialize = "G::Scalar: serde::Serialize, G: serde::Serialize" + )) +)] pub struct VerifiableClient { pub(crate) blind: G::Scalar, pub(crate) blinded_element: G, - pub(crate) data: Vec, #[derive_where(skip(Zeroize))] pub(crate) hash: PhantomData, } -impl_serialize_and_deserialize_for!(VerifiableClient); - /// A server which engages with a [NonVerifiableClient] in base mode, meaning /// that the OPRF outputs are not verifiable. #[derive(DeriveWhere)] #[derive_where(Clone, Zeroize(drop))] #[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; G::Scalar)] +#[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde(bound( + deserialize = "G::Scalar: serde::Deserialize<'de>", + serialize = "G::Scalar: serde::Serialize" + )) +)] pub struct NonVerifiableServer { pub(crate) sk: G::Scalar, #[derive_where(skip(Zeroize))] pub(crate) hash: PhantomData, } -impl_serialize_and_deserialize_for!(NonVerifiableServer); - /// A server which engages with a [VerifiableClient] in verifiable mode, meaning /// that the OPRF outputs can be checked against a server public key. #[derive(DeriveWhere)] #[derive_where(Clone, Zeroize(drop))] #[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; G, G::Scalar)] +#[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde(bound( + deserialize = "G::Scalar: serde::Deserialize<'de>, G: serde::Deserialize<'de>", + serialize = "G::Scalar: serde::Serialize, G: serde::Serialize" + )) +)] pub struct VerifiableServer { pub(crate) sk: G::Scalar, pub(crate) pk: G, @@ -103,13 +129,19 @@ pub struct VerifiableServer { pub(crate) hash: PhantomData, } -impl_serialize_and_deserialize_for!(VerifiableServer); - /// A proof produced by a [VerifiableServer] that the OPRF output matches /// against a server public key. #[derive(DeriveWhere)] #[derive_where(Clone, Zeroize(drop))] #[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; G::Scalar)] +#[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde(bound( + deserialize = "G::Scalar: serde::Deserialize<'de>", + serialize = "G::Scalar: serde::Serialize" + )) +)] pub struct Proof { pub(crate) c_scalar: G::Scalar, pub(crate) s_scalar: G::Scalar, @@ -117,34 +149,44 @@ pub struct Proof { pub(crate) hash: PhantomData, } -impl_serialize_and_deserialize_for!(Proof); - /// The first client message sent from a client (either verifiable or not) to a /// server (either verifiable or not). #[derive(DeriveWhere)] #[derive_where(Clone, Zeroize(drop))] #[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; G)] +#[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde(bound( + deserialize = "G: serde::Deserialize<'de>", + serialize = "G: serde::Serialize" + )) +)] pub struct BlindedElement { pub(crate) value: G, #[derive_where(skip(Zeroize))] pub(crate) hash: PhantomData, } -impl_serialize_and_deserialize_for!(BlindedElement); - /// The server's response to the [BlindedElement] message from a client (either /// verifiable or not) to a server (either verifiable or not). #[derive(DeriveWhere)] #[derive_where(Clone, Zeroize(drop))] #[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; G)] +#[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde(bound( + deserialize = "G: serde::Deserialize<'de>", + serialize = "G: serde::Serialize" + )) +)] pub struct EvaluationElement { pub(crate) value: G, #[derive_where(skip(Zeroize))] pub(crate) hash: PhantomData, } -impl_serialize_and_deserialize_for!(EvaluationElement); - ///////////////////////// // API Implementations // // =================== // @@ -154,13 +196,12 @@ impl NonVerifiableClient { /// Computes the first step for the multiplicative blinding version of /// DH-OPRF. pub fn blind( - input: Vec, + input: &[u8], blinding_factor_rng: &mut R, ) -> Result, InternalError> { - let (blind, blinded_element) = blind::(&input, blinding_factor_rng, Mode::Base)?; + let (blind, blinded_element) = blind::(input, blinding_factor_rng, Mode::Base)?; Ok(NonVerifiableClientBlindResult { state: Self { - data: input, blind, hash: PhantomData, }, @@ -181,13 +222,12 @@ impl NonVerifiableClient { /// This should be used with caution, since it does not perform any checks /// on the validity of the blinding factor! pub fn deterministic_blind_unchecked( - input: Vec, + input: &[u8], blind: G::Scalar, ) -> Result, InternalError> { - let blinded_element = deterministic_blind_unchecked::(&input, &blind, Mode::Base)?; + let blinded_element = deterministic_blind_unchecked::(input, &blind, Mode::Base)?; Ok(NonVerifiableClientBlindResult { state: Self { - data: input, blind, hash: PhantomData, }, @@ -202,23 +242,23 @@ impl NonVerifiableClient { /// DH-OPRF, in which the client unblinds the server's message. pub fn finalize( &self, + input: &[u8], evaluation_element: &EvaluationElement, metadata: Option<&[u8]>, ) -> Result, InternalError> { let unblinded_element = evaluation_element.value * &G::scalar_invert(&self.blind); - let outputs = finalize_after_unblind::( - Some((self.data.as_slice(), unblinded_element)).into_iter(), + let mut outputs = finalize_after_unblind::( + Some((input, unblinded_element)).into_iter(), metadata.unwrap_or_default(), Mode::Base, )?; - Ok(outputs[0].clone()) + outputs.next().unwrap() } #[cfg(test)] /// Only used for test functions - pub fn from_data_and_blind(data: &[u8], blind: G::Scalar) -> Self { + pub fn from_blind(blind: G::Scalar) -> Self { Self { - data: data.to_vec(), blind, hash: PhantomData, } @@ -235,14 +275,13 @@ impl VerifiableClient { /// Computes the first step for the multiplicative blinding version of /// DH-OPRF. pub fn blind( - input: Vec, + input: &[u8], blinding_factor_rng: &mut R, ) -> Result, InternalError> { let (blind, blinded_element) = - blind::(&input, blinding_factor_rng, Mode::Verifiable)?; + blind::(input, blinding_factor_rng, Mode::Verifiable)?; Ok(VerifiableClientBlindResult { state: Self { - data: input, blind, blinded_element, hash: PhantomData, @@ -264,14 +303,13 @@ impl VerifiableClient { /// This should be used with caution, since it does not perform any checks /// on the validity of the blinding factor! pub fn deterministic_blind_unchecked( - input: Vec, + input: &[u8], blind: G::Scalar, ) -> Result, InternalError> { let blinded_element = - deterministic_blind_unchecked::(&input, &blind, Mode::Verifiable)?; + deterministic_blind_unchecked::(input, &blind, Mode::Verifiable)?; Ok(VerifiableClientBlindResult { state: Self { - data: input, blind, blinded_element, hash: PhantomData, @@ -287,95 +325,62 @@ impl VerifiableClient { /// DH-OPRF, in which the client unblinds the server's message. pub fn finalize( &self, + input: &[u8], evaluation_element: &EvaluationElement, proof: &Proof, pk: G, metadata: Option<&[u8]>, ) -> Result, InternalError> { // `core::array::from_ref` needs a MSRV of 1.53 + let inputs: &[&[u8]; 1] = core::slice::from_ref(&input).try_into().unwrap(); let clients: &[Self; 1] = core::slice::from_ref(self).try_into().unwrap(); let messages: &[EvaluationElement; 1] = core::slice::from_ref(evaluation_element) .try_into() .unwrap(); - let batch_result = Self::batch_finalize(clients, messages, proof, pk, metadata)?; - Ok(batch_result[0].clone()) + let mut batch_result = + Self::batch_finalize(inputs, clients, messages, proof, pk, metadata)?; + batch_result.next().unwrap() } /// Allows for batching of the finalization of multiple [VerifiableClient] /// and [EvaluationElement] pairs - pub fn batch_finalize<'a, IC, IM>( + pub fn batch_finalize<'a, I: 'a, II, IC, IM>( + inputs: &'a II, clients: &'a IC, messages: &'a IM, proof: &Proof, pk: G, - metadata: Option<&[u8]>, - ) -> Result>, InternalError> + metadata: Option<&'a [u8]>, + ) -> Result, InternalError> where G: 'a, H: 'a, + I: AsRef<[u8]>, + &'a II: 'a + IntoIterator, + <&'a II as IntoIterator>::IntoIter: ExactSizeIterator, &'a IC: 'a + IntoIterator>, <&'a IC as IntoIterator>::IntoIter: ExactSizeIterator, &'a IM: 'a + IntoIterator>, <&'a IM as IntoIterator>::IntoIter: ExactSizeIterator, { - struct Items { - clients: IC, - messages: IM, - } - - impl<'a, G: 'a + Group, H: 'a + BlockInput + Digest, IC: Copy, IM: Copy> IntoIterator - for &Items - where - IC: IntoIterator>, - ::IntoIter: ExactSizeIterator, - IM: IntoIterator>, - ::IntoIter: ExactSizeIterator, - { - type Item = BatchItems; - - #[allow(clippy::type_complexity)] - type IntoIter = core::iter::Map< - core::iter::Zip<::IntoIter, ::IntoIter>, - fn((&VerifiableClient, &EvaluationElement)) -> BatchItems, - >; - - fn into_iter(self) -> Self::IntoIter { - self.clients.into_iter().zip(self.messages.into_iter()).map( - |(client, evaluation_element)| BatchItems { - blind: client.blind, - evaluation_element: evaluation_element.copy(), - blinded_element: BlindedElement { - value: client.blinded_element, - hash: PhantomData, - }, - }, - ) - } - } - - let batch_items = Items { clients, messages }; let metadata = metadata.unwrap_or_default(); - let unblinded_elements = verifiable_unblind(&batch_items, pk, proof, metadata)?; + let unblinded_elements = verifiable_unblind(clients, messages, pk, proof, metadata)?; - let inputs_and_unblinded_elements = clients - .into_iter() - .zip(unblinded_elements.iter()) - .map(|(client, &unblinded_element)| (client.data.as_slice(), unblinded_element)); + let inputs_and_unblinded_elements = inputs.into_iter().zip(unblinded_elements); - finalize_after_unblind::(inputs_and_unblinded_elements, metadata, Mode::Verifiable) + finalize_after_unblind::( + inputs_and_unblinded_elements, + metadata, + Mode::Verifiable, + ) } #[cfg(test)] /// Only used for test functions - pub fn from_data_and_blind_and_element( - data: &[u8], - blind: G::Scalar, - blinded_element: G, - ) -> Self { + pub fn from_blind_and_element(blind: G::Scalar, blinded_element: G) -> Self { Self { - data: data.to_vec(), blind, blinded_element, hash: PhantomData, @@ -506,19 +511,28 @@ impl VerifiableServer { blinded_element: &BlindedElement, metadata: Option<&[u8]>, ) -> Result, InternalError> { - // `core::array::from_ref` needs a MSRV of 1.53 - let blinded_elements: &[BlindedElement; 1] = - core::slice::from_ref(blinded_element).try_into().unwrap(); + let (mut evaluation_elements, t) = + self.batch_evaluate_1(Some(blinded_element.copy()).into_iter(), metadata)?; - let batch_result = self.batch_evaluate(rng, blinded_elements, 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(), + t, + )?; + + //let batch_result = self.batch_evaluate(rng, blinded_elements, metadata)?; Ok(VerifiableServerEvaluateResult { - message: batch_result.messages[0].copy(), - proof: batch_result.proof, + message: evaluation_element, + proof, }) } /// Allows for batching of the evaluation of multiple [BlindedElement] /// messages from a [VerifiableClient] + #[cfg(feature = "alloc")] pub fn batch_evaluate<'a, R: RngCore + CryptoRng, I>( &self, rng: &mut R, @@ -530,6 +544,40 @@ impl VerifiableServer { H: 'a, &'a I: IntoIterator>, <&'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), + t, + )?; + + Ok(VerifiableServerBatchEvaluateResult { + messages: evaluation_elements, + proof, + }) + } + + fn batch_evaluate_1( + &self, + blinded_elements: I, + metadata: Option<&[u8]>, + ) -> Result< + ( + impl Iterator> + ExactSizeIterator, + G::Scalar, + ), + InternalError, + > + where + I: Iterator> + ExactSizeIterator, { chain!(context, STR_CONTEXT => |x| Some(x.as_ref()), @@ -540,30 +588,30 @@ impl VerifiableServer { .concat(get_context_string::(Mode::Verifiable)?); let m = G::hash_to_scalar::(context, dst)?; let t = self.sk + &m; - let evaluation_elements: Vec> = blinded_elements - .into_iter() - .map(|x| EvaluationElement { - value: x.value * &G::scalar_invert(&t), - hash: PhantomData, - }) - .collect(); + let evaluation_elements = blinded_elements.map(move |x| EvaluationElement { + value: x.value * &G::scalar_invert(&t), + hash: PhantomData, + }); + Ok((evaluation_elements, t)) + } + + /// Allows for batching of the evaluation of multiple [BlindedElement] + /// messages from a [VerifiableClient] + fn batch_evaluate_2( + rng: &mut R, + blinded_elements: IB, + evaluation_elements: IE, + t: G::Scalar, + ) -> Result, InternalError> + where + IB: Iterator> + ExactSizeIterator, + IE: Iterator> + ExactSizeIterator, + { let g = G::base_point(); let u = g * &t; - let proof = generate_proof( - rng, - t, - g, - u, - evaluation_elements.iter().map(EvaluationElement::copy), - blinded_elements.into_iter().map(BlindedElement::copy), - )?; - - Ok(VerifiableServerBatchEvaluateResult { - messages: evaluation_elements, - proof, - }) + generate_proof(rng, t, g, u, evaluation_elements, blinded_elements) } /// Retrieves the server's public key @@ -599,6 +647,14 @@ pub struct VerifiableClientBlindResult { pub message: BlindedElement, } +pub type VerifiableClientBatchFinalizeResult<'a, G, H, I, II, IC, IM> = FinalizeAfterUnblindResult< + 'a, + G, + H, + I, + Zip<<&'a II as IntoIterator>::IntoIter, VerifiableUnblindResult<'a, G, H, IC, IM>>, +>; + /// Contains the fields that are returned by a verifiable server evaluate pub struct VerifiableServerEvaluateResult { /// The message to send to the client @@ -608,9 +664,10 @@ pub struct VerifiableServerEvaluateResult { } /// Contains the fields that are returned by a verifiable server batch evaluate +#[cfg(feature = "alloc")] pub struct VerifiableServerBatchEvaluateResult { /// The messages to send to the client - pub messages: Vec>, + pub messages: alloc::vec::Vec>, /// The proof for the client to verify pub proof: Proof, } @@ -620,13 +677,6 @@ pub struct VerifiableServerBatchEvaluateResult // ========================================= // /////////////////////////////////////////////// -/// Convenience struct only used in batching APIs -struct BatchItems { - blind: G::Scalar, - evaluation_element: EvaluationElement, - blinded_element: BlindedElement, -} - impl BlindedElement { /// Only used to easier validate allocation fn copy(&self) -> Self { @@ -712,15 +762,27 @@ fn deterministic_blind_unchecked( Ok(hashed_point * blind) } -fn verifiable_unblind<'a, G: 'a + Group, H: 'a + BlockInput + Digest, I>( - batch_items: &'a I, +#[allow(type_alias_bounds)] +type VerifiableUnblindResult<'a, G: Group, H, IC, IM> = Map< + Zip< + Map<<&'a IC as IntoIterator>::IntoIter, fn(&VerifiableClient) -> G::Scalar>, + <&'a IM as IntoIterator>::IntoIter, + >, + fn((G::Scalar, &EvaluationElement)) -> G, +>; + +fn verifiable_unblind<'a, G: 'a + Group, H: 'a + BlockInput + Digest, IC, IM>( + clients: &'a IC, + messages: &'a IM, pk: G, proof: &Proof, info: &[u8], -) -> Result, InternalError> +) -> Result, InternalError> where - &'a I: IntoIterator>, - <&'a I as IntoIterator>::IntoIter: ExactSizeIterator, + &'a IC: 'a + IntoIterator>, + <&'a IC as IntoIterator>::IntoIter: ExactSizeIterator, + &'a IM: 'a + IntoIterator>, + <&'a IM as IntoIterator>::IntoIter: ExactSizeIterator, { chain!(context, STR_CONTEXT => |x| Some(x.as_ref()), @@ -736,17 +798,21 @@ where let t = g * &m; let u = t + &pk; - let blinds = batch_items.into_iter().map(|x| x.blind); - let evaluation_elements = batch_items.into_iter().map(|x| x.evaluation_element); - let blinded_elements = batch_items.into_iter().map(|x| x.blinded_element); + let blinds = clients + .into_iter() + // Convert to `fn` pointer to make a return type possible. + .map() -> _>::from(|x| x.blind)); + let evaluation_elements = messages.into_iter().map(EvaluationElement::copy); + let blinded_elements = clients.into_iter().map(|client| BlindedElement { + value: client.blinded_element, + hash: PhantomData, + }); verify_proof(g, u, evaluation_elements, blinded_elements, proof)?; - let unblinded_elements = blinds - .zip(batch_items.into_iter().map(|x| x.evaluation_element)) - .map(|(blind, x)| x.value * &G::scalar_invert(&blind)) - .collect(); - Ok(unblinded_elements) + Ok(blinds + .zip(messages.into_iter()) + .map(|(blind, x)| x.value * &G::scalar_invert(&blind))) } #[allow(clippy::many_single_char_names)] @@ -823,23 +889,35 @@ fn verify_proof( } } +#[allow(type_alias_bounds)] +type FinalizeAfterUnblindResult<'a, G, H: Digest, I, IE> = Map< + Zip)>>, + fn( + ((I, G), (&'a [u8], GenericArray)), + ) -> Result, InternalError>, +>; + fn finalize_after_unblind< 'a, G: Group, H: BlockInput + Digest, - I: Iterator, + I: AsRef<[u8]>, + IE: 'a + Iterator, >( - inputs_and_unblinded_elements: I, - info: &[u8], + inputs_and_unblinded_elements: IE, + info: &'a [u8], mode: Mode, -) -> Result>, InternalError> { +) -> Result, InternalError> { let finalize_dst = GenericArray::from(STR_FINALIZE).concat(get_context_string::(mode)?); - inputs_and_unblinded_elements - .map(|(input, unblinded_element)| { + Ok(inputs_and_unblinded_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((info, finalize_dst))) + .map(|((input, unblinded_element), (info, finalize_dst))| { chain!( hash_input, - serialize::(input)?, + serialize::(input.as_ref())?, serialize::(info)?, serialize_owned::(unblinded_element.to_arr())?, serialize_owned::(finalize_dst)?, @@ -848,8 +926,7 @@ fn finalize_after_unblind< Ok(hash_input .fold(H::new(), |h, bytes| h.chain(bytes)) .finalize()) - }) - .collect() + })) } fn compute_composites( @@ -919,11 +996,14 @@ fn get_context_string(mode: Mode) -> Result, Int #[cfg(test)] mod tests { - use alloc::vec; + use core::ops::Add; - use generic_array::GenericArray; + 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::Group; @@ -950,23 +1030,25 @@ mod tests { let res = point * &G::scalar_invert(&(key + &m)); - finalize_after_unblind::(Some((input, res)).into_iter(), info, mode).unwrap()[0] - .clone() + finalize_after_unblind::(Some((input, res)).into_iter(), info, mode) + .unwrap() + .next() + .unwrap() + .unwrap() } fn base_retrieval() { let input = b"input"; let info = b"info"; let mut rng = OsRng; - let client_blind_result = - NonVerifiableClient::::blind(input.to_vec(), &mut rng).unwrap(); + let client_blind_result = NonVerifiableClient::::blind(input, &mut rng).unwrap(); let server = NonVerifiableServer::::new(&mut rng).unwrap(); let server_result = server .evaluate(&client_blind_result.message, Some(info)) .unwrap(); let client_finalize_result = client_blind_result .state - .finalize(&server_result.message, Some(info)) + .finalize(input, &server_result.message, Some(info)) .unwrap(); let res2 = prf::(input, server.get_private_key(), info, Mode::Base); assert_eq!(client_finalize_result, res2); @@ -976,8 +1058,7 @@ mod tests { let input = b"input"; let info = b"info"; let mut rng = OsRng; - let client_blind_result = - VerifiableClient::::blind(input.to_vec(), &mut rng).unwrap(); + let client_blind_result = VerifiableClient::::blind(input, &mut rng).unwrap(); let server = VerifiableServer::::new(&mut rng).unwrap(); let server_result = server .evaluate(&mut rng, &client_blind_result.message, Some(info)) @@ -985,6 +1066,7 @@ mod tests { let client_finalize_result = client_blind_result .state .finalize( + input, &server_result.message, &server_result.proof, server.get_public_key(), @@ -995,12 +1077,12 @@ mod tests { assert_eq!(client_finalize_result, res2); } + #[cfg(feature = "alloc")] fn verifiable_bad_public_key() { let input = b"input"; let info = b"info"; let mut rng = OsRng; - let client_blind_result = - VerifiableClient::::blind(input.to_vec(), &mut rng).unwrap(); + let client_blind_result = VerifiableClient::::blind(input, &mut rng).unwrap(); let server = VerifiableServer::::new(&mut rng).unwrap(); let server_result = server .evaluate(&mut rng, &client_blind_result.message, Some(info)) @@ -1010,6 +1092,7 @@ mod tests { G::hash_to_curve::(b"msg", (*b"dst").into()).unwrap() }; let client_finalize_result = client_blind_result.state.finalize( + input, &server_result.message, &server_result.proof, wrong_pk, @@ -1018,6 +1101,7 @@ mod tests { assert!(client_finalize_result.is_err()); } + #[cfg(feature = "alloc")] fn verifiable_batch_retrieval() { let info = b"info"; let mut rng = OsRng; @@ -1026,10 +1110,9 @@ mod tests { let mut client_messages = vec![]; let num_iterations = 10; for _ in 0..num_iterations { - let mut input = vec![0u8; 32]; + let mut input = [0u8; 32]; rng.fill_bytes(&mut input); - let client_blind_result = - VerifiableClient::::blind(input.clone(), &mut rng).unwrap(); + let client_blind_result = VerifiableClient::::blind(&input, &mut rng).unwrap(); inputs.push(input); client_states.push(client_blind_result.state); client_messages.push(client_blind_result.message); @@ -1039,12 +1122,15 @@ mod tests { .batch_evaluate(&mut rng, &client_messages, Some(info)) .unwrap(); let client_finalize_result = VerifiableClient::batch_finalize( + &inputs, &client_states, &server_result.messages, &server_result.proof, server.get_public_key(), Some(info), ) + .unwrap() + .collect::, _>>() .unwrap(); let mut res2 = vec![]; for input in inputs.iter().take(num_iterations) { @@ -1054,6 +1140,7 @@ mod tests { assert_eq!(client_finalize_result, res2); } + #[cfg(feature = "alloc")] fn verifiable_batch_bad_public_key() { let info = b"info"; let mut rng = OsRng; @@ -1062,10 +1149,9 @@ mod tests { let mut client_messages = vec![]; let num_iterations = 10; for _ in 0..num_iterations { - let mut input = vec![0u8; 32]; + let mut input = [0u8; 32]; rng.fill_bytes(&mut input); - let client_blind_result = - VerifiableClient::::blind(input.clone(), &mut rng).unwrap(); + let client_blind_result = VerifiableClient::::blind(&input, &mut rng).unwrap(); inputs.push(input); client_states.push(client_blind_result.state); client_messages.push(client_blind_result.message); @@ -1079,6 +1165,7 @@ mod tests { G::hash_to_curve::(b"msg", (*b"dst").into()).unwrap() }; let client_finalize_result = VerifiableClient::batch_finalize( + &inputs, &client_states, &server_result.messages, &server_result.proof, @@ -1090,14 +1177,14 @@ mod tests { fn base_inversion_unsalted() { let mut rng = OsRng; - let mut input = alloc::vec![0u8; 64]; + let mut input = [0u8; 64]; rng.fill_bytes(&mut input); let info = b"info"; - let client_blind_result = - NonVerifiableClient::::blind(input.clone(), &mut rng).unwrap(); + let client_blind_result = NonVerifiableClient::::blind(&input, &mut rng).unwrap(); let client_finalize_result = client_blind_result .state .finalize( + &input, &EvaluationElement { value: client_blind_result.message.value, hash: PhantomData, @@ -1109,13 +1196,15 @@ mod tests { let dst = GenericArray::from(STR_HASH_TO_GROUP) .concat(get_context_string::(Mode::Base).unwrap()); let point = G::hash_to_curve::(&input, dst).unwrap(); - let res2 = finalize_after_unblind::( - Some((input.as_slice(), point)).into_iter(), + let res2 = finalize_after_unblind::( + Some((input.as_ref(), point)).into_iter(), info, Mode::Base, ) - .unwrap()[0] - .clone(); + .unwrap() + .next() + .unwrap() + .unwrap(); assert_eq!(client_finalize_result, res2); } @@ -1123,8 +1212,7 @@ mod tests { fn zeroize_base_client() { let input = b"input"; let mut rng = OsRng; - let client_blind_result = - NonVerifiableClient::::blind(input.to_vec(), &mut rng).unwrap(); + let client_blind_result = NonVerifiableClient::::blind(input, &mut rng).unwrap(); let mut state = client_blind_result.state; Zeroize::zeroize(&mut state); @@ -1135,11 +1223,14 @@ mod tests { assert!(message.serialize().iter().all(|&x| x == 0)); } - fn zeroize_verifiable_client() { + fn zeroize_verifiable_client() + where + G::ScalarLen: Add, + Sum: ArrayLength, + { let input = b"input"; let mut rng = OsRng; - let client_blind_result = - VerifiableClient::::blind(input.to_vec(), &mut rng).unwrap(); + let client_blind_result = VerifiableClient::::blind(input, &mut rng).unwrap(); let mut state = client_blind_result.state; Zeroize::zeroize(&mut state); @@ -1154,8 +1245,7 @@ mod tests { let input = b"input"; let info = b"info"; let mut rng = OsRng; - let client_blind_result = - NonVerifiableClient::::blind(input.to_vec(), &mut rng).unwrap(); + let client_blind_result = NonVerifiableClient::::blind(input, &mut rng).unwrap(); let server = NonVerifiableServer::::new(&mut rng).unwrap(); let server_result = server .evaluate(&client_blind_result.message, Some(info)) @@ -1170,12 +1260,17 @@ mod tests { assert!(message.serialize().iter().all(|&x| x == 0)); } - fn zeroize_verifiable_server() { + fn zeroize_verifiable_server() + where + G::ScalarLen: Add, + Sum: ArrayLength, + G::ScalarLen: Add, + Sum: ArrayLength, + { let input = b"input"; let info = b"info"; let mut rng = OsRng; - let client_blind_result = - VerifiableClient::::blind(input.to_vec(), &mut rng).unwrap(); + let client_blind_result = VerifiableClient::::blind(input, &mut rng).unwrap(); let server = VerifiableServer::::new(&mut rng).unwrap(); let server_result = server .evaluate(&mut rng, &client_blind_result.message, Some(info)) @@ -1204,8 +1299,11 @@ mod tests { base_retrieval::(); base_inversion_unsalted::(); verifiable_retrieval::(); + #[cfg(feature = "alloc")] verifiable_batch_retrieval::(); + #[cfg(feature = "alloc")] verifiable_bad_public_key::(); + #[cfg(feature = "alloc")] verifiable_batch_bad_public_key::(); zeroize_base_client::();