// 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 POPRF API #[cfg(feature = "alloc")] use alloc::vec::Vec; use core::iter::{self, Map, Repeat, Zip}; use derive_where::derive_where; use digest::core_api::BlockSizeUser; use digest::{Digest, Output, OutputSizeUser}; use generic_array::sequence::Concat; use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U256}; use generic_array::GenericArray; use rand_core::{CryptoRng, RngCore}; use crate::common::{ create_context_string, derive_keypair, deterministic_blind_unchecked, generate_proof, i2osp_2, verify_proof, BlindedElement, EvaluationElement, Mode, PreparedEvaluationElement, Proof, STR_FINALIZE, STR_HASH_TO_SCALAR, STR_INFO, }; #[cfg(feature = "serde")] use crate::serialization::serde::{Element, Scalar}; use crate::{CipherSuite, Error, Group, Result}; //////////////////////////// // High-level API Structs // // ====================== // //////////////////////////// /// A client which engages with a [PoprfServer] in verifiable mode, meaning /// that the OPRF outputs can be checked against a server public key. #[derive_where(Clone, ZeroizeOnDrop)] #[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; ::Scalar, ::Elem)] #[cfg_attr( feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(crate = "serde", bound = "") )] pub struct PoprfClient where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { #[cfg_attr(feature = "serde", serde(with = "Scalar::"))] pub(crate) blind: ::Scalar, #[cfg_attr(feature = "serde", serde(with = "Element::"))] pub(crate) blinded_element: ::Elem, } /// A server which engages with a [PoprfClient] in verifiable mode, meaning /// that the OPRF outputs can be checked against a server public key. #[derive_where(Clone, ZeroizeOnDrop)] #[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; ::Scalar, ::Elem)] #[cfg_attr( feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(crate = "serde", bound = "") )] pub struct PoprfServer where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { #[cfg_attr(feature = "serde", serde(with = "Scalar::"))] pub(crate) sk: ::Scalar, #[cfg_attr(feature = "serde", serde(with = "Element::"))] pub(crate) pk: ::Elem, } ///////////////////////// // API Implementations // // =================== // ///////////////////////// impl PoprfClient where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { /// Computes the first step for the multiplicative blinding version of /// DH-OPRF. /// /// # Errors /// [`Error::Input`] if the `input` is empty or longer than [`u16::MAX`]. pub fn blind( blinding_factor_rng: &mut R, input: &[u8], ) -> Result> { let blind = CS::Group::random_scalar(blinding_factor_rng); Self::deterministic_blind_unchecked_inner(input, blind) } /// Computes the first step for the multiplicative blinding version of /// DH-OPRF, taking a blinding factor scalar as input instead of sampling /// from an RNG. /// /// # Caution /// /// This should be used with caution, since it does not perform any checks /// on the validity of the blinding factor! /// /// # Errors /// [`Error::Input`] if the `input` is empty or longer than [`u16::MAX`]. #[cfg(any(feature = "danger", test))] pub fn deterministic_blind_unchecked( input: &[u8], blind: ::Scalar, ) -> Result> { Self::deterministic_blind_unchecked_inner(input, blind) } /// Can only fail with [`Error::Input`]. fn deterministic_blind_unchecked_inner( input: &[u8], blind: ::Scalar, ) -> Result> { let blinded_element = deterministic_blind_unchecked::(input, &blind, Mode::Poprf)?; Ok(PoprfClientBlindResult { state: Self { blind, blinded_element, }, message: BlindedElement(blinded_element), }) } /// Computes the third step for the multiplicative blinding version of /// DH-OPRF, in which the client unblinds the server's message. /// /// # Errors /// - [`Error::Info`] if the `info` is longer than `u16::MAX`. /// - [`Error::Input`] if the `input` is empty or longer than [`u16::MAX`]. /// - [`Error::Protocol`] if the protocol fails and can't be completed. /// - [`Error::ProofVerification`] if the `proof` failed to verify. pub fn finalize( &self, input: &[u8], evaluation_element: &EvaluationElement, proof: &Proof, pk: ::Elem, info: Option<&[u8]>, ) -> Result> { let clients = core::array::from_ref(self); let messages = core::array::from_ref(evaluation_element); let mut batch_result = Self::batch_finalize(iter::once(input), clients, messages, proof, pk, info)?; batch_result.next().unwrap() } /// Allows for batching of the finalization of multiple [PoprfClient] /// and [EvaluationElement] pairs /// /// # Errors /// - [`Error::Info`] if the `info` is longer than `u16::MAX`. /// - [`Error::Protocol`] if the protocol fails and can't be completed. /// - [`Error::Batch`] if the number of `inputs`, `clients` and `messages` /// don't match or is longer than [`u16::MAX`]. /// - [`Error::ProofVerification`] if the `proof` failed to verify. /// /// The resulting messages can each fail individually with [`Error::Input`] /// if the `input` is empty or longer than [`u16::MAX`]. pub fn batch_finalize<'a, II: 'a + Iterator + ExactSizeIterator, IC, IM>( inputs: II, clients: &'a IC, messages: &'a IM, proof: &Proof, pk: ::Elem, info: Option<&'a [u8]>, ) -> Result> where CS: 'a, &'a IC: 'a + IntoIterator>, <&'a IC as IntoIterator>::IntoIter: ExactSizeIterator, &'a IM: 'a + IntoIterator>, <&'a IM as IntoIterator>::IntoIter: ExactSizeIterator, { let unblinded_elements = poprf_unblind(clients, messages, pk, proof, info)?; finalize_after_unblind::<'a, CS, _, _>(unblinded_elements, inputs, info) } /// Only used for test functions #[cfg(test)] pub fn get_blind(&self) -> ::Scalar { self.blind } } impl PoprfServer where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { /// Produces a new instance of a [PoprfServer] using a supplied RNG /// /// # Errors /// [`Error::Protocol`] if the protocol fails and can't be completed. pub fn new(rng: &mut R) -> Result { let mut seed = GenericArray::<_, ::ScalarLen>::default(); rng.fill_bytes(&mut seed); Self::new_from_seed(&seed, &[]) } /// Produces a new instance of a [PoprfServer] using a supplied set of /// bytes to represent the server's private key /// /// # Errors /// [`Error::Deserialization`] if the private key is not a valid point on /// the group or zero. pub fn new_with_key(key: &[u8]) -> Result { let sk = CS::Group::deserialize_scalar(key)?; let pk = CS::Group::base_elem() * &sk; Ok(Self { sk, pk }) } /// Produces a new instance of a [PoprfServer] 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. /// /// # Errors /// - [`Error::DeriveKeyPair`] if the `input` and `seed` together are longer /// then `u16::MAX - 3`. /// - [`Error::Protocol`] if the protocol fails and can't be completed. pub fn new_from_seed(seed: &[u8], info: &[u8]) -> Result { let (sk, pk) = derive_keypair::(seed, info, Mode::Poprf)?; 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. /// /// # Errors /// - [`Error::Info`] if the `info` is longer than `u16::MAX`. /// - [`Error::Protocol`] if the protocol fails and can't be completed. pub fn evaluate( &self, rng: &mut R, blinded_element: &BlindedElement, info: Option<&[u8]>, ) -> Result> { let PoprfServerBatchEvaluatePrepareResult { mut prepared_evaluation_elements, prepared_tweak, } = self.batch_evaluate_prepare(iter::once(blinded_element), info)?; let prepared_evaluation_element = prepared_evaluation_elements.next().unwrap(); let prepared_evaluation_elements = core::array::from_ref(&prepared_evaluation_element); let PoprfServerBatchEvaluateFinishResult { mut messages, proof, } = Self::batch_evaluate_finish( rng, iter::once(blinded_element), prepared_evaluation_elements, &prepared_tweak, ) .unwrap(); Ok(PoprfServerEvaluateResult { message: messages.next().unwrap(), proof, }) } /// Allows for batching of the evaluation of multiple [BlindedElement] /// messages from a [PoprfClient] /// /// # Errors /// - [`Error::Info`] if the `info` is longer than `u16::MAX`. /// - [`Error::Protocol`] if the protocol fails and can't be completed. #[cfg(feature = "alloc")] pub fn batch_evaluate<'a, R: RngCore + CryptoRng, IE>( &self, rng: &mut R, blinded_elements: &'a IE, info: Option<&[u8]>, ) -> Result> where CS: 'a, &'a IE: 'a + IntoIterator>, <&'a IE as IntoIterator>::IntoIter: ExactSizeIterator, { let PoprfServerBatchEvaluatePrepareResult { prepared_evaluation_elements, prepared_tweak, } = self.batch_evaluate_prepare(blinded_elements.into_iter(), info)?; let prepared_evaluation_elements: Vec<_> = prepared_evaluation_elements.collect(); // This can't fail because we know the size of the inputs. let PoprfServerBatchEvaluateFinishResult { messages, proof } = Self::batch_evaluate_finish::<_, _, Vec<_>>( rng, blinded_elements.into_iter(), &prepared_evaluation_elements, &prepared_tweak, ) .unwrap(); let messages: Vec<_> = messages.collect(); Ok(PoprfServerBatchEvaluateResult { messages, proof }) } /// 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). /// /// # Errors /// - [`Error::Info`] if the `info` is longer than `u16::MAX`. /// - [`Error::Protocol`] if the protocol fails and can't be completed. pub fn batch_evaluate_prepare<'a, I: Iterator>>( &self, blinded_elements: I, info: Option<&[u8]>, ) -> Result> where CS: 'a, { let tweak = compute_tweak::(self.sk, info)?; Ok(PoprfServerBatchEvaluatePrepareResult { prepared_evaluation_elements: blinded_elements.zip(iter::repeat(tweak)).map( |(blinded_element, tweak)| { PreparedEvaluationElement(EvaluationElement( blinded_element.0 * &CS::Group::invert_scalar(tweak), )) }, ), prepared_tweak: PoprfPreparedTweak(tweak), }) } /// See [`batch_evaluate_prepare`](Self::batch_evaluate_prepare) for more /// details. /// /// # Errors /// [`Error::Batch`] if the number of `blinded_elements` and /// `prepared_evaluation_elements` don't match or is longer then /// [`u16::MAX`] pub fn batch_evaluate_finish< 'a, 'b, R: RngCore + CryptoRng, IB: Iterator> + ExactSizeIterator, IE, >( rng: &mut R, blinded_elements: IB, prepared_evaluation_elements: &'b IE, prepared_tweak: &PoprfPreparedTweak, ) -> Result> where CS: 'a, &'b IE: IntoIterator>, <&'b IE as IntoIterator>::IntoIter: ExactSizeIterator, { let g = CS::Group::base_elem(); let tweak = prepared_tweak.0; let tweaked_key = g * &tweak; let proof = generate_proof( rng, tweak, g, tweaked_key, prepared_evaluation_elements .into_iter() .map(|element| element.0 .0), blinded_elements.map(|element| element.0), Mode::Poprf, )?; let messages = prepared_evaluation_elements.into_iter().map(, ) -> _>::from( |element| EvaluationElement(element.0 .0), )); Ok(PoprfServerBatchEvaluateFinishResult { messages, proof }) } /// Retrieves the server's public key pub fn get_public_key(&self) -> ::Elem { self.pk } } impl BlindedElement where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { /// Creates a [BlindedElement] from a raw group element. /// /// # Caution /// /// This should be used with caution, since it does not perform any checks /// on the validity of the value itself! #[cfg(feature = "danger")] pub fn from_value_unchecked(value: ::Elem) -> Self { Self(value) } /// Exposes the internal value #[cfg(feature = "danger")] pub fn value(&self) -> ::Elem { self.0 } } impl EvaluationElement where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { /// Creates an [EvaluationElement] from a raw group element. /// /// # Caution /// /// This should be used with caution, since it does not perform any checks /// on the validity of the value itself! #[cfg(feature = "danger")] pub fn from_value_unchecked(value: ::Elem) -> Self { Self(value) } /// Exposes the internal value #[cfg(feature = "danger")] pub fn value(&self) -> ::Elem { self.0 } } ///////////////////////// // Convenience Structs // //==================== // ///////////////////////// /// Contains the fields that are returned by a verifiable client blind #[derive_where(Debug; ::Scalar, ::Elem)] pub struct PoprfClientBlindResult where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { /// The state to be persisted on the client pub state: PoprfClient, /// The message to send to the server pub message: BlindedElement, } /// Concrete return type for [`PoprfClient::batch_finalize`]. pub type PoprfClientBatchFinalizeResult<'a, CS, II, IC, IM> = FinalizeAfterUnblindResult<'a, CS, PoprfUnblindResult<'a, CS, IC, IM>, II>; /// Contains the fields that are returned by a verifiable server evaluate #[derive_where(Debug; ::Scalar, ::Elem)] pub struct PoprfServerEvaluateResult where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { /// 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 #[derive_where(Debug; ::Scalar, ::Elem)] #[cfg(feature = "alloc")] pub struct PoprfServerBatchEvaluateResult where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { /// The messages to send to the client pub messages: Vec>, /// The proof for the client to verify pub proof: Proof, } /// Concrete type of [`EvaluationElement`]s in /// [`PoprfServerBatchEvaluatePrepareResult`]. pub type PoprfServerBatchEvaluatePreparedEvaluationElements = Map< Zip::Group as Group>::Scalar>>, fn( ( &BlindedElement, <::Group as Group>::Scalar, ), ) -> PreparedEvaluationElement, >; /// Prepared tweak by a partially verifiable server batch evaluate prepare. #[derive_where(Clone, ZeroizeOnDrop)] #[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; ::Scalar)] #[cfg_attr( feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(crate = "serde", bound = "") )] pub struct PoprfPreparedTweak( #[cfg_attr(feature = "serde", serde(with = "Scalar::"))] ::Scalar, ) where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>; /// Contains the fields that are returned by a partially verifiable server batch /// evaluate prepare #[derive_where(Debug; I, ::Scalar)] pub struct PoprfServerBatchEvaluatePrepareResult where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { /// Prepared [`EvaluationElement`]. pub prepared_evaluation_elements: PoprfServerBatchEvaluatePreparedEvaluationElements, /// Prepared tweak. pub prepared_tweak: PoprfPreparedTweak, } /// Concrete type of [`EvaluationElement`]s in /// [`PoprfServerBatchEvaluateFinishResult`]. pub type PoprfServerBatchEvaluateFinishedMessages<'a, CS, I> = Map< <&'a I as IntoIterator>::IntoIter, fn(&PreparedEvaluationElement) -> EvaluationElement, >; /// Contains the fields that are returned by a verifiable server batch evaluate /// finish. #[derive_where(Debug; <&'a I as IntoIterator>::IntoIter, ::Scalar)] pub struct PoprfServerBatchEvaluateFinishResult<'a, CS: 'a + CipherSuite, I> where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, &'a I: IntoIterator>, { /// The [`EvaluationElement`]s to send to the client pub messages: PoprfServerBatchEvaluateFinishedMessages<'a, CS, I>, /// The proof for the client to verify pub proof: Proof, } ///////////////////// // Inner functions // // =============== // ///////////////////// /// Inner function for POPRF blind. Computes the tweaked key from the server /// public key and info. /// /// Can only fail with [`Error::Info`] or [`Error::Protocol`] fn compute_tweaked_key( pk: ::Elem, info: Option<&[u8]>, ) -> Result<::Elem> where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { // None for info is treated the same as empty bytes let info = info.unwrap_or_default(); // framedInfo = "Info" || I2OSP(len(info), 2) || info // m = G.HashToScalar(framedInfo) // T = G.ScalarBaseMult(m) // tweakedKey = T + pkS // if tweakedKey == G.Identity(): // raise InvalidInputError let info_len = i2osp_2(info.len()).map_err(|_| Error::Info)?; let framed_info = [STR_INFO.as_slice(), &info_len, info]; let dst = GenericArray::from(STR_HASH_TO_SCALAR).concat(create_context_string::(Mode::Poprf)); // This can't fail, the size of the `input` is known. let m = CS::Group::hash_to_scalar::(&framed_info, &dst).unwrap(); let t = CS::Group::base_elem() * &m; let tweaked_key = t + &pk; // Check if resulting element match bool::from(CS::Group::is_identity_elem(tweaked_key)) { true => Err(Error::Protocol), false => Ok(tweaked_key), } } /// Inner function for POPRF evaluate. Computes the tweak from the server /// private key and info. /// /// Can only fail with [`Error::Info`] and [`Error::Protocol`]. fn compute_tweak( sk: ::Scalar, info: Option<&[u8]>, ) -> Result<::Scalar> where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { // None for info is treated the same as empty bytes let info = info.unwrap_or_default(); // framedInfo = "Info" || I2OSP(len(info), 2) || info // m = G.HashToScalar(framedInfo) // t = skS + m // if t == 0: // raise InverseError let info_len = i2osp_2(info.len()).map_err(|_| Error::Info)?; let framed_info = [STR_INFO.as_slice(), &info_len, info]; let dst = GenericArray::from(STR_HASH_TO_SCALAR).concat(create_context_string::(Mode::Poprf)); // This can't fail, the size of the `input` is known. let m = CS::Group::hash_to_scalar::(&framed_info, &dst).unwrap(); let t = sk + &m; // Check if resulting element is equal to zero match bool::from(CS::Group::is_zero_scalar(t)) { true => Err(Error::Protocol), false => Ok(t), } } type PoprfUnblindResult<'a, CS, IC, IM> = Map< Zip< Map< <&'a IC as IntoIterator>::IntoIter, fn(&PoprfClient) -> <::Group as Group>::Scalar, >, <&'a IM as IntoIterator>::IntoIter, >, fn( ( <::Group as Group>::Scalar, &'a EvaluationElement, ), ) -> <::Group as Group>::Elem, >; /// Can only fail with [`Error::Info`], [`Error::Protocol`], [`Error::Batch] or /// [`Error::ProofVerification`]. fn poprf_unblind<'a, CS: 'a + CipherSuite, IC, IM>( clients: &'a IC, messages: &'a IM, pk: ::Elem, proof: &Proof, info: Option<&[u8]>, ) -> Result> where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, &'a IC: 'a + IntoIterator>, <&'a IC as IntoIterator>::IntoIter: ExactSizeIterator, &'a IM: 'a + IntoIterator>, <&'a IM as IntoIterator>::IntoIter: ExactSizeIterator, { let info = info.unwrap_or_default(); let tweaked_key = compute_tweaked_key::(pk, Some(info))?; let g = CS::Group::base_elem(); 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(|element| element.0); let blinded_elements = clients.into_iter().map(|client| client.blinded_element); verify_proof( g, tweaked_key, evaluation_elements, blinded_elements, proof, Mode::Poprf, )?; Ok(blinds .zip(messages.into_iter()) .map(|(blind, x)| x.0 * &CS::Group::invert_scalar(blind))) } type FinalizeAfterUnblindResult<'a, CS, IE, II> = Map< Zip, Repeat<&'a [u8]>>, fn( ((<::Group as Group>::Elem, &[u8]), &[u8]), ) -> Result::Hash as OutputSizeUser>::OutputSize>>, >; /// Can only fail with [`Error::Batch`] and returned values can only fail with /// [`Error::Info`] or [`Error::Input`] individually. fn finalize_after_unblind< 'a, CS: CipherSuite, IE: 'a + Iterator::Elem> + ExactSizeIterator, II: 'a + Iterator + ExactSizeIterator, >( unblinded_elements: IE, inputs: II, info: Option<&'a [u8]>, ) -> Result> where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { if unblinded_elements.len() != inputs.len() { return Err(Error::Batch); } let info = info.unwrap_or_default(); Ok(unblinded_elements.zip(inputs).zip(iter::repeat(info)).map( |((unblinded_element, input), info)| { let elem_len = ::ElemLen::U16.to_be_bytes(); // hashInput = I2OSP(len(input), 2) || input || // I2OSP(len(info), 2) || info || // I2OSP(len(unblindedElement), 2) || unblindedElement || // "Finalize" // return Hash(hashInput) let output = CS::Hash::new() .chain_update(i2osp_2(input.as_ref().len()).map_err(|_| Error::Input)?) .chain_update(input.as_ref()) .chain_update(i2osp_2(info.as_ref().len()).map_err(|_| Error::Info)?) .chain_update(info.as_ref()) .chain_update(elem_len) .chain_update(CS::Group::serialize_elem(unblinded_element)) .chain_update(STR_FINALIZE) .finalize(); Ok(output) }, )) } /////////// // Tests // // ===== // /////////// #[cfg(test)] mod tests { use core::ops::Add; use core::ptr; use generic_array::typenum::Sum; use generic_array::ArrayLength; use rand::rngs::OsRng; use super::*; use crate::common::STR_HASH_TO_GROUP; use crate::Group; fn prf( input: &[u8], key: ::Scalar, info: &[u8], mode: Mode, ) -> Output where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { let t = compute_tweak::(key, Some(info)).unwrap(); let dst = GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::(mode)); let point = CS::Group::hash_to_curve::(&[input], &dst).unwrap(); // evaluatedElement = G.ScalarInverse(t) * blindedElement let res = point * &CS::Group::invert_scalar(t); finalize_after_unblind::(iter::once(res), iter::once(input), Some(info)) .unwrap() .next() .unwrap() .unwrap() } fn verifiable_retrieval() where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { let input = b"input"; let info = b"info"; let mut rng = OsRng; let server = PoprfServer::::new(&mut rng).unwrap(); let client_blind_result = PoprfClient::::blind(&mut rng, input).unwrap(); let server_result = server .evaluate(&mut rng, &client_blind_result.message, Some(info)) .unwrap(); let client_finalize_result = client_blind_result .state .finalize( input, &server_result.message, &server_result.proof, server.get_public_key(), Some(info), ) .unwrap(); let res2 = prf::(input, server.get_private_key(), info, Mode::Poprf); assert_eq!(client_finalize_result, res2); } fn verifiable_bad_public_key() where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { let input = b"input"; let info = b"info"; let mut rng = OsRng; let server = PoprfServer::::new(&mut rng).unwrap(); let client_blind_result = PoprfClient::::blind(&mut rng, input).unwrap(); let server_result = server .evaluate(&mut rng, &client_blind_result.message, Some(info)) .unwrap(); let wrong_pk = { let dst = GenericArray::from(STR_HASH_TO_GROUP) .concat(create_context_string::(Mode::Oprf)); // Choose a group element that is unlikely to be the right public key CS::Group::hash_to_curve::(&[b"msg"], &dst).unwrap() }; let client_finalize_result = client_blind_result.state.finalize( input, &server_result.message, &server_result.proof, wrong_pk, Some(info), ); assert!(client_finalize_result.is_err()); } fn zeroize_verifiable_client() where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, ::ScalarLen: Add<::ElemLen>, Sum<::ScalarLen, ::ElemLen>: ArrayLength, { let input = b"input"; let mut rng = OsRng; let client_blind_result = PoprfClient::::blind(&mut rng, input).unwrap(); let mut state = client_blind_result.state; unsafe { ptr::drop_in_place(&mut state) }; assert!(state.serialize().iter().all(|&x| x == 0)); let mut message = client_blind_result.message; unsafe { ptr::drop_in_place(&mut message) }; assert!(message.serialize().iter().all(|&x| x == 0)); } fn zeroize_verifiable_server() where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, ::ScalarLen: Add<::ElemLen>, Sum<::ScalarLen, ::ElemLen>: ArrayLength, ::ScalarLen: Add<::ScalarLen>, Sum<::ScalarLen, ::ScalarLen>: ArrayLength, { let input = b"input"; let info = b"info"; let mut rng = OsRng; let server = PoprfServer::::new(&mut rng).unwrap(); let client_blind_result = PoprfClient::::blind(&mut rng, input).unwrap(); let server_result = server .evaluate(&mut rng, &client_blind_result.message, Some(info)) .unwrap(); let mut state = server; unsafe { ptr::drop_in_place(&mut state) }; assert!(state.serialize().iter().all(|&x| x == 0)); let mut message = server_result.message; unsafe { ptr::drop_in_place(&mut message) }; assert!(message.serialize().iter().all(|&x| x == 0)); let mut proof = server_result.proof; unsafe { ptr::drop_in_place(&mut proof) }; assert!(proof.serialize().iter().all(|&x| x == 0)); } #[test] fn test_functionality() -> Result<()> { use p256::NistP256; #[cfg(feature = "ristretto255")] { use crate::Ristretto255; verifiable_retrieval::(); verifiable_bad_public_key::(); zeroize_verifiable_client::(); zeroize_verifiable_server::(); } verifiable_retrieval::(); verifiable_bad_public_key::(); zeroize_verifiable_client::(); zeroize_verifiable_server::(); Ok(()) } }