// Copyright (c) Facebook, Inc. and its affiliates. // // This source code is licensed under both the MIT license found in the // LICENSE-MIT file in the root directory of this source tree and the Apache // License, Version 2.0 found in the LICENSE-APACHE file in the root directory // of this source tree. //! Contains the main VOPRF API use crate::{ ciphersuite::CipherSuite, errors::InternalError, group::Group, serialization::{i2osp, serialize}, }; use alloc::vec; use alloc::vec::Vec; use digest::Digest; use generic_array::{typenum::Unsigned, GenericArray}; use rand::{CryptoRng, RngCore}; /////////////// // Constants // // ========= // /////////////// static STR_HASH_TO_SCALAR: &[u8] = b"HashToScalar-"; static STR_HASH_TO_GROUP: &[u8] = b"HashToGroup-"; static STR_FINALIZE: &[u8] = b"Finalize-"; static STR_SEED: &[u8] = b"Seed-"; static STR_CONTEXT: &[u8] = b"Context-"; static STR_COMPOSITE: &[u8] = b"Composite-"; static STR_CHALLENGE: &[u8] = b"Challenge-"; static STR_VOPRF: &[u8] = b"VOPRF07-"; /// Determines the mode of operation (either base mode or /// verifiable mode) #[derive(Clone, Copy)] enum Mode { Base = 0, Verifiable = 1, } //////////////////////////// // High-level API Structs // // ====================== // //////////////////////////// /// A client which engages with a [NonVerifiableServer] /// in base mode, meaning that the OPRF outputs are not /// verifiable. pub struct NonVerifiableClient { pub(crate) blind: ::Scalar, pub(crate) data: Vec, } impl_traits_for!( struct NonVerifiableClient, [blind, data], [::Scalar], ); /// A client which engages with a [VerifiableServer] /// in verifiable mode, meaning that the OPRF outputs /// can be checked against a server public key. pub struct VerifiableClient { pub(crate) blind: ::Scalar, pub(crate) blinded_element: CS::Group, pub(crate) data: alloc::vec::Vec, } impl_traits_for!( struct VerifiableClient, [blind, blinded_element, data], [::Scalar, CS::Group], ); /// A server which engages with a [NonVerifiableClient] /// in base mode, meaning that the OPRF outputs are not /// verifiable. pub struct NonVerifiableServer { pub(crate) sk: ::Scalar, } impl_traits_for!( struct NonVerifiableServer, [sk], [::Scalar], ); /// A server which engages with a [VerifiableClient] /// in verifiable mode, meaning that the OPRF outputs /// can be checked against a server public key. pub struct VerifiableServer { pub(crate) sk: ::Scalar, pub(crate) pk: CS::Group, } impl_traits_for!( struct VerifiableServer, [sk, pk], [::Scalar, CS::Group], ); /// A proof produced by a [VerifiableServer] that /// the OPRF output matches against a server public key. pub struct Proof { pub(crate) c_scalar: ::Scalar, pub(crate) s_scalar: ::Scalar, } impl_traits_for!( struct Proof, [c_scalar, s_scalar], [::Scalar], ); /// The first client message sent from a client (either verifiable or not) /// to a server (either verifiable or not). pub struct BlindedElement { pub(crate) value: CS::Group, } impl_traits_for!( struct BlindedElement, [value], [CS::Group], ); /// The server's response to the [BlindedElement] message from /// a client (either verifiable or not) /// to a server (either verifiable or not). pub struct EvaluationElement { pub(crate) value: CS::Group, } impl_traits_for!( struct EvaluationElement, [value], [CS::Group], ); ///////////////////////// // API Implementations // // =================== // ///////////////////////// impl NonVerifiableClient { /// Computes the first step for the multiplicative blinding version of DH-OPRF. pub fn blind( input: &[u8], blinding_factor_rng: &mut R, ) -> Result, InternalError> { let (blind, blinded_element) = blind::(input, blinding_factor_rng, Mode::Base)?; Ok(NonVerifiableClientBlindResult { state: Self { data: input.to_vec(), blind, }, message: BlindedElement { value: blinded_element, }, }) } /// Computes the third step for the multiplicative blinding version of DH-OPRF, in which /// the client unblinds the server's message. pub fn finalize( &self, evaluation_element: EvaluationElement, metadata: &Metadata, ) -> Result, InternalError> { let unblinded_element = evaluation_element.value * &::scalar_invert(&self.blind); let outputs = finalize_after_unblind::( &[(self.data.clone(), unblinded_element)], &metadata.0, Mode::Base, )?; Ok(NonVerifiableClientFinalizeResult { output: outputs[0].clone(), }) } #[cfg(test)] /// Only used for test functions pub fn from_data_and_blind(data: &[u8], blind: ::Scalar) -> Self { Self { data: data.to_vec(), blind, } } #[cfg(test)] /// Only used for test functions pub fn get_blind(&self) -> ::Scalar { self.blind } } impl VerifiableClient { /// Computes the first step for the multiplicative blinding version of DH-OPRF. pub fn blind( input: &[u8], blinding_factor_rng: &mut R, ) -> Result, InternalError> { let (blind, blinded_element) = blind::(input, blinding_factor_rng, Mode::Verifiable)?; Ok(VerifiableClientBlindResult { state: Self { data: input.to_vec(), blind, blinded_element, }, message: BlindedElement { value: blinded_element, }, }) } /// Computes the third step for the multiplicative blinding version of DH-OPRF, in which /// the client unblinds the server's message. pub fn finalize( &self, evaluation_element: EvaluationElement, proof: Proof, pk: CS::Group, metadata: &Metadata, ) -> Result, InternalError> { let batch_finalize_input = BatchFinalizeInput::new(vec![self.clone()], vec![evaluation_element]); let batch_result = Self::batch_finalize(batch_finalize_input, proof, pk, metadata)?; Ok(VerifiableClientFinalizeResult { output: batch_result.outputs[0].clone(), }) } /// Allows for batching of the finalization of multiple [VerifiableClient] and [EvaluationElement] pairs #[allow(clippy::type_complexity)] pub fn batch_finalize( batch_finalize_input: BatchFinalizeInput, proof: Proof, pk: CS::Group, metadata: &Metadata, ) -> Result, InternalError> { let batch_items: Vec> = batch_finalize_input .clients .iter() .zip(batch_finalize_input.messages.iter()) .map(|(client, evaluation_element)| BatchItems { blind: client.blind, evaluation_element: evaluation_element.clone(), blinded_element: BlindedElement { value: client.blinded_element, }, }) .collect(); let unblinded_elements = verifiable_unblind(&batch_items, pk, proof, &metadata.0)?; let inputs_and_unblinded_elements: Vec<(Vec, CS::Group)> = batch_finalize_input .clients .iter() .zip(unblinded_elements.iter()) .map(|(client, &unblinded_element)| (client.data.clone(), unblinded_element)) .collect(); Ok(VerifiableClientBatchFinalizeResult { outputs: finalize_after_unblind::( &inputs_and_unblinded_elements, &metadata.0, Mode::Verifiable, )?, }) } #[cfg(test)] /// Only used for test functions pub fn from_data_and_blind( data: &[u8], blind: ::Scalar, blinded_element: CS::Group, ) -> Self { Self { data: data.to_vec(), blind, blinded_element, } } #[cfg(test)] /// Only used for test functions pub fn get_blind(&self) -> ::Scalar { self.blind } } impl NonVerifiableServer { /// Produces a new instance of a [NonVerifiableServer] using a supplied RNG pub fn new(rng: &mut R) -> Result { let mut seed = vec![0u8; ::OutputSize::USIZE]; rng.fill_bytes(&mut seed); Self::new_from_seed(&seed) } /// Produces a new instance of a [NonVerifiableServer] using a supplied set of bytes to /// represent the server's private key pub fn new_with_key(private_key_bytes: &[u8]) -> Result { let sk = CS::Group::from_scalar_slice(&GenericArray::clone_from_slice(private_key_bytes))?; Ok(Self { sk }) } /// Produces a new instance of a [NonVerifiableServer] using a supplied set of bytes which /// are used as a seed to derive the server's private key. /// /// Corresponds to DeriveKeyPair() function from the VOPRF specification. pub fn new_from_seed(seed: &[u8]) -> Result { let dst = [STR_HASH_TO_SCALAR, &get_context_string::(Mode::Base)?].concat(); let sk = CS::Group::hash_to_scalar::(seed, &dst)?; Ok(Self { sk }) } // Only used for tests #[cfg(test)] pub fn get_private_key(&self) -> ::Scalar { self.sk } /// Computes the second step for the multiplicative blinding version of DH-OPRF. This /// message is sent from the server (who holds the OPRF key) to the client. pub fn evaluate( &self, blinded_element: BlindedElement, metadata: &Metadata, ) -> Result, InternalError> { let context = [ STR_CONTEXT, &get_context_string::(Mode::Base)?, &serialize(&metadata.0, 2)?, ] .concat(); let dst = [STR_HASH_TO_SCALAR, &get_context_string::(Mode::Base)?].concat(); let m = CS::Group::hash_to_scalar::(&context, &dst)?; let t = self.sk + &m; let evaluation_element = blinded_element.value * &CS::Group::scalar_invert(&t); Ok(NonVerifiableServerEvaluateResult { message: EvaluationElement { value: evaluation_element, }, }) } } impl VerifiableServer { /// Produces a new instance of a [VerifiableServer] using a supplied RNG pub fn new(rng: &mut R) -> Result { let mut seed = vec![0u8; ::OutputSize::USIZE]; rng.fill_bytes(&mut seed); Self::new_from_seed(&seed) } /// Produces a new instance of a [VerifiableServer] using a supplied set of bytes to /// represent the server's private key pub fn new_with_key(key: &[u8]) -> Result { let sk = CS::Group::from_scalar_slice(&GenericArray::clone_from_slice(key))?; let pk = CS::Group::base_point() * &sk; Ok(Self { sk, pk }) } /// Produces a new instance of a [VerifiableServer] using a supplied set of bytes which /// are used as a seed to derive the server's private key. /// /// Corresponds to DeriveKeyPair() function from the VOPRF specification. pub fn new_from_seed(seed: &[u8]) -> Result { let dst = [ STR_HASH_TO_SCALAR, &get_context_string::(Mode::Verifiable)?, ] .concat(); let sk = CS::Group::hash_to_scalar::(seed, &dst)?; let pk = CS::Group::base_point() * &sk; Ok(Self { sk, pk }) } // Only used for tests #[cfg(test)] pub fn get_private_key(&self) -> ::Scalar { self.sk } /// Computes the second step for the multiplicative blinding version of DH-OPRF. This /// message is sent from the server (who holds the OPRF key) to the client. pub fn evaluate( &self, rng: &mut R, blinded_element: BlindedElement, metadata: &Metadata, ) -> Result, InternalError> { let batch_result = self.batch_evaluate(rng, &[blinded_element], metadata)?; Ok(VerifiableServerEvaluateResult { message: batch_result.messages[0].clone(), proof: batch_result.proof, }) } /// Allows for batching of the evaluation of multiple [BlindedElement] messages from a [VerifiableClient] pub fn batch_evaluate( &self, rng: &mut R, blinded_elements: &[BlindedElement], metadata: &Metadata, ) -> Result, InternalError> { let context = [ STR_CONTEXT, &get_context_string::(Mode::Verifiable)?, &serialize(&metadata.0, 2)?, ] .concat(); let dst = [ STR_HASH_TO_SCALAR, &get_context_string::(Mode::Verifiable)?, ] .concat(); let m = CS::Group::hash_to_scalar::(&context, &dst)?; let t = self.sk + &m; let evaluation_elements: Vec> = blinded_elements .iter() .map(|x| EvaluationElement { value: x.value * &CS::Group::scalar_invert(&t), }) .collect(); let g = CS::Group::base_point(); let u = g * &t; let proof = generate_proof(rng, t, g, u, &evaluation_elements, blinded_elements)?; Ok(VerifiableServerBatchEvaluateResult { messages: evaluation_elements, proof, }) } /// Retrieves the server's public key pub fn get_public_key(&self) -> CS::Group { self.pk } } ///////////////////////// // Optional Parameters // //==================== // ///////////////////////// /// Allows for implementations to specify an optional sequence of /// public bytes that must be agreed-upon by the client and server #[derive(Default)] pub struct Metadata(pub Vec); impl Metadata { /// Specifies no metadata (the default option) pub fn none() -> Self { Self::default() } } ///////////////////////// // Convenience Structs // //==================== // ///////////////////////// /// Contains the fields that are returned by a non-verifiable client blind pub struct NonVerifiableClientBlindResult { /// The state to be persisted on the client pub state: NonVerifiableClient, /// The message to send to the server pub message: BlindedElement, } /// Contains the fields that are returned by a non-verifiable server evaluate pub struct NonVerifiableServerEvaluateResult { /// The message to send to the client pub message: EvaluationElement, } /// Contains the fields that are returned by a non-verifiable client finalize pub struct NonVerifiableClientFinalizeResult { /// The output of the protocol pub output: GenericArray::OutputSize>, } /// Contains the fields that are returned by a verifiable client blind pub struct VerifiableClientBlindResult { /// The state to be persisted on the client pub state: VerifiableClient, /// The message to send to the server pub message: BlindedElement, } /// Contains the fields that are returned by a verifiable server evaluate pub struct VerifiableServerEvaluateResult { /// The message to send to the client pub message: EvaluationElement, /// The proof for the client to verify pub proof: Proof, } /// Contains the fields that are returned by a verifiable server batch evaluate pub struct VerifiableServerBatchEvaluateResult { /// The messages to send to the client pub messages: Vec>, /// The proof for the client to verify pub proof: Proof, } /// Contains the fields that are returned by a verifiable client finalize pub struct VerifiableClientFinalizeResult { /// The output of the protocol pub output: GenericArray::OutputSize>, } /// Contains the fields that are returned by a verifiable client batch finalize pub struct VerifiableClientBatchFinalizeResult { /// The output of the protocol pub outputs: Vec::OutputSize>>, } /// An input to the verifiable client batch finalize function, constructed /// by aggregating clients and server messages pub struct BatchFinalizeInput { clients: Vec>, messages: Vec>, } impl BatchFinalizeInput { /// Create a new instance from a vector of clients and a vector of messages pub fn new(clients: Vec>, messages: Vec>) -> Self { Self { clients, messages } } } /////////////////////////////////////////////// // Inner functions and Trait Implementations // // ========================================= // /////////////////////////////////////////////// /// Convenience struct only used in batching APIs struct BatchItems { blind: ::Scalar, evaluation_element: EvaluationElement, blinded_element: BlindedElement, } // Inner function for blind. Returns the blind scalar and the blinded element fn blind( input: &[u8], blinding_factor_rng: &mut R, mode: Mode, ) -> Result<(::Scalar, CS::Group), InternalError> { // Choose a random scalar that must be non-zero let blind = ::random_nonzero_scalar(blinding_factor_rng); let dst = [STR_HASH_TO_GROUP, &get_context_string::(mode)?].concat(); let hashed_point = ::hash_to_curve::(input, &dst)?; let blinded_element = hashed_point * &blind; Ok((blind, blinded_element)) } fn verifiable_unblind( batch_items: &[BatchItems], pk: CS::Group, proof: Proof, info: &[u8], ) -> Result, InternalError> { let context = [ STR_CONTEXT, &get_context_string::(Mode::Verifiable)?, &serialize(info, 2)?, ] .concat(); let dst = [ STR_HASH_TO_SCALAR, &get_context_string::(Mode::Verifiable)?, ] .concat(); let m = CS::Group::hash_to_scalar::(&context, &dst)?; let g = CS::Group::base_point(); let t = g * &m; let u = t + &pk; let blinds: Vec<::Scalar> = batch_items.iter().map(|x| x.blind).collect(); let evaluation_elements: Vec> = batch_items .iter() .map(|x| x.evaluation_element.clone()) .collect(); let blinded_elements: Vec> = batch_items .iter() .map(|x| x.blinded_element.clone()) .collect(); verify_proof(g, u, &evaluation_elements, &blinded_elements, proof)?; let unblinded_elements = blinds .iter() .zip(evaluation_elements.iter()) .map(|(&blind, x)| x.value * &CS::Group::scalar_invert(&blind)) .collect(); Ok(unblinded_elements) } #[allow(clippy::many_single_char_names)] fn generate_proof( rng: &mut R, k: ::Scalar, a: CS::Group, b: CS::Group, cs: &[EvaluationElement], ds: &[BlindedElement], ) -> Result, InternalError> { let (m, z) = compute_composites::(Some(k), b, cs, ds)?; let r = CS::Group::random_nonzero_scalar(rng); let t2 = a * &r; let t3 = m * &r; let challenge_dst = [STR_CHALLENGE, &get_context_string::(Mode::Verifiable)?].concat(); let h2_input = [ serialize(&b.to_arr().to_vec(), 2)?, serialize(&m.to_arr().to_vec(), 2)?, serialize(&z.to_arr().to_vec(), 2)?, serialize(&t2.to_arr().to_vec(), 2)?, serialize(&t3.to_arr().to_vec(), 2)?, serialize(&challenge_dst, 2)?, ] .concat(); let hash_to_scalar_dst = [ STR_HASH_TO_SCALAR, &get_context_string::(Mode::Verifiable)?, ] .concat(); let c_scalar = CS::Group::hash_to_scalar::(&h2_input, &hash_to_scalar_dst)?; let s_scalar = r - &(c_scalar * &k); Ok(Proof { c_scalar, s_scalar }) } #[allow(clippy::many_single_char_names)] fn verify_proof( a: CS::Group, b: CS::Group, cs: &[EvaluationElement], ds: &[BlindedElement], proof: Proof, ) -> Result<(), InternalError> { let (m, z) = compute_composites::(None, b, cs, ds)?; let t2 = (a * &proof.s_scalar) + &(b * &proof.c_scalar); let t3 = (m * &proof.s_scalar) + &(z * &proof.c_scalar); let challenge_dst = [STR_CHALLENGE, &get_context_string::(Mode::Verifiable)?].concat(); let h2_input = [ serialize(&b.to_arr().to_vec(), 2)?, serialize(&m.to_arr().to_vec(), 2)?, serialize(&z.to_arr().to_vec(), 2)?, serialize(&t2.to_arr().to_vec(), 2)?, serialize(&t3.to_arr().to_vec(), 2)?, serialize(&challenge_dst, 2)?, ] .concat(); let hash_to_scalar_dst = [ STR_HASH_TO_SCALAR, &get_context_string::(Mode::Verifiable)?, ] .concat(); let c = CS::Group::hash_to_scalar::(&h2_input, &hash_to_scalar_dst)?; match CS::Group::ct_equal_scalar(&c, &proof.c_scalar) { true => Ok(()), false => Err(InternalError::ProofVerificationError), } } #[allow(clippy::type_complexity)] fn finalize_after_unblind( inputs_and_unblinded_elements: &[(Vec, CS::Group)], info: &[u8], mode: Mode, ) -> Result::OutputSize>>, InternalError> { let finalize_dst = [STR_FINALIZE, &get_context_string::(mode)?].concat(); let mut outputs = vec![]; for (input, unblinded_element) in inputs_and_unblinded_elements { outputs.push(::digest( &[ serialize(input, 2)?, serialize(info, 2)?, serialize(&unblinded_element.to_arr().to_vec(), 2)?, serialize(&finalize_dst, 2)?, ] .concat(), )); } Ok(outputs) } fn compute_composites( k_option: Option<::Scalar>, b: CS::Group, c_slice: &[EvaluationElement], d_slice: &[BlindedElement], ) -> Result<(CS::Group, CS::Group), InternalError> { if c_slice.len() != d_slice.len() { return Err(InternalError::MismatchedLengthsForCompositeInputs); } let seed_dst = [STR_SEED, &get_context_string::(Mode::Verifiable)?].concat(); let composite_dst = [STR_COMPOSITE, &get_context_string::(Mode::Verifiable)?].concat(); let h1_input = [ serialize(&b.to_arr().to_vec(), 2)?, serialize(&seed_dst, 2)?, ] .concat(); let seed = ::digest(&h1_input); let mut m = CS::Group::identity(); let mut z = CS::Group::identity(); for i in 0..c_slice.len() { let h2_input = [ serialize(&seed, 2)?, i2osp(i, 2)?, serialize(&c_slice[i].value.to_arr().to_vec(), 2)?, serialize(&d_slice[i].value.to_arr().to_vec(), 2)?, serialize(&composite_dst, 2)?, ] .concat(); let dst = [ STR_HASH_TO_SCALAR, &get_context_string::(Mode::Verifiable)?, ] .concat(); let di = CS::Group::hash_to_scalar::(&h2_input, &dst)?; m = c_slice[i].value * &di + &m; z = match k_option { Some(_) => z, None => d_slice[i].value * &di + &z, }; } z = match k_option { Some(k) => m * &k, None => z, }; Ok((m, z)) } /// Generates the contextString parameter as defined in /// fn get_context_string(mode: Mode) -> Result, InternalError> { Ok([ STR_VOPRF, &i2osp(mode as usize, 1)?, &i2osp(CS::Group::SUITE_ID, 2)?, ] .concat()) } /////////// // Tests // // ===== // /////////// #[cfg(test)] mod tests { use super::*; use crate::group::Group; use generic_array::GenericArray; use rand::rngs::OsRng; fn prf( input: &[u8], key: ::Scalar, info: &[u8], mode: Mode, ) -> GenericArray::OutputSize> { let dst = [STR_HASH_TO_GROUP, &get_context_string::(mode).unwrap()].concat(); let point = CS::Group::hash_to_curve::(input, &dst).unwrap(); let context = [ STR_CONTEXT, &get_context_string::(mode).unwrap(), &serialize(info, 2).unwrap(), ] .concat(); let dst = [STR_HASH_TO_SCALAR, &get_context_string::(mode).unwrap()].concat(); let m = ::hash_to_scalar::(&context, &dst).unwrap(); let res = point * &::scalar_invert(&(key + &m)); finalize_after_unblind::(&[(input.to_vec(), res)], info, mode).unwrap()[0].clone() } fn base_retrieval() { let input = b"input"; let info = b"info"; let mut rng = OsRng; 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, &Metadata(info.to_vec())) .unwrap(); let client_finalize_result = client_blind_result .state .finalize(server_result.message, &Metadata(info.to_vec())) .unwrap(); let res2 = prf::(&input[..], server.get_private_key(), info, Mode::Base); assert_eq!(client_finalize_result.output, res2); } fn verifiable_retrieval() { let input = b"input"; let info = b"info"; let mut rng = OsRng; 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, &Metadata(info.to_vec()), ) .unwrap(); let client_finalize_result = client_blind_result .state .finalize( server_result.message, server_result.proof, server.get_public_key(), &Metadata(info.to_vec()), ) .unwrap(); let res2 = prf::(&input[..], server.get_private_key(), info, Mode::Verifiable); assert_eq!(client_finalize_result.output, res2); } fn verifiable_bad_public_key() { let input = b"input"; let info = b"info"; let mut rng = OsRng; 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, &Metadata(info.to_vec()), ) .unwrap(); let wrong_pk = { // Choose a group element that is unlikely to be the right public key CS::Group::hash_to_curve::(b"msg", b"dst").unwrap() }; let client_finalize_result = client_blind_result.state.finalize( server_result.message, server_result.proof, wrong_pk, &Metadata(info.to_vec()), ); assert!(client_finalize_result.is_err()); } fn verifiable_batch_retrieval() { let info = b"info"; let mut rng = OsRng; let mut inputs = vec![]; let mut client_states = vec![]; let mut client_messages = vec![]; let num_iterations = 10; for _ in 0..num_iterations { let mut input = vec![0u8; 32]; rng.fill_bytes(&mut input); 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); } let server = VerifiableServer::::new(&mut rng).unwrap(); let server_result = server .batch_evaluate(&mut rng, &client_messages, &Metadata(info.to_vec())) .unwrap(); let batch_finalize_input = BatchFinalizeInput::new(client_states, server_result.messages); let client_finalize_result = VerifiableClient::batch_finalize( batch_finalize_input, server_result.proof, server.get_public_key(), &Metadata(info.to_vec()), ) .unwrap(); let mut res2 = vec![]; for input in inputs.iter().take(num_iterations) { let output = prf::(&input[..], server.get_private_key(), info, Mode::Verifiable); res2.push(output); } assert_eq!(client_finalize_result.outputs, res2); } fn verifiable_batch_bad_public_key() { let info = b"info"; let mut rng = OsRng; let mut inputs = vec![]; let mut client_states = vec![]; let mut client_messages = vec![]; let num_iterations = 10; for _ in 0..num_iterations { let mut input = vec![0u8; 32]; rng.fill_bytes(&mut input); 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); } let server = VerifiableServer::::new(&mut rng).unwrap(); let server_result = server .batch_evaluate(&mut rng, &client_messages, &Metadata(info.to_vec())) .unwrap(); let batch_finalize_input = BatchFinalizeInput::new(client_states, server_result.messages); let wrong_pk = { // Choose a group element that is unlikely to be the right public key CS::Group::hash_to_curve::(b"msg", b"dst").unwrap() }; let client_finalize_result = VerifiableClient::batch_finalize( batch_finalize_input, server_result.proof, wrong_pk, &Metadata(info.to_vec()), ); assert!(client_finalize_result.is_err()); } fn base_inversion_unsalted() { let mut rng = OsRng; let mut input = alloc::vec![0u8; 64]; rng.fill_bytes(&mut input); let info = b"info"; let client_blind_result = NonVerifiableClient::::blind(&input, &mut rng).unwrap(); let client_finalize_result = client_blind_result .state .finalize( EvaluationElement { value: client_blind_result.message.value, }, &Metadata(info.to_vec()), ) .unwrap(); let dst = [ STR_HASH_TO_GROUP, &get_context_string::(Mode::Base).unwrap(), ] .concat(); let point = CS::Group::hash_to_curve::(&input, &dst).unwrap(); let res2 = finalize_after_unblind::(&[(input.to_vec(), point)], info, Mode::Base) .unwrap()[0] .clone(); assert_eq!(client_finalize_result.output, res2); } #[test] fn test_functionality() -> Result<(), InternalError> { use crate::tests::Ristretto255Sha512; base_retrieval::(); base_inversion_unsalted::(); verifiable_retrieval::(); verifiable_batch_retrieval::(); verifiable_bad_public_key::(); verifiable_batch_bad_public_key::(); #[cfg(feature = "p256")] { use crate::tests::P256Sha256; base_retrieval::(); base_inversion_unsalted::(); verifiable_retrieval::(); verifiable_batch_retrieval::(); verifiable_bad_public_key::(); verifiable_batch_bad_public_key::(); } Ok(()) } }