1#[cfg(feature = "alloc")]
8use alloc::vec::Vec;
9use core::iter::{self, Map, Repeat, Zip};
10
11use derive_where::derive_where;
12use digest::{Digest, Output, OutputSizeUser};
13use hybrid_array::typenum::Unsigned;
14use hybrid_array::{Array, ArraySize};
15use rand_core::{TryCryptoRng, TryRng};
16
17use crate::common::{
18 BlindedElement, Dst, EvaluationElement, Mode, PreparedEvaluationElement, Proof, STR_FINALIZE,
19 STR_HASH_TO_SCALAR, STR_INFO, derive_keypair, deterministic_blind_unchecked, generate_proof,
20 hash_to_group, i2osp_2, server_evaluate_hash_input, verify_proof,
21};
22#[cfg(feature = "serde")]
23use crate::serialization::serde::{Element, Scalar};
24use crate::{CipherSuite, Error, Group, Result};
25
26#[derive_where(Clone, ZeroizeOnDrop)]
34#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
35#[cfg_attr(
36 feature = "serde",
37 derive(serde::Deserialize, serde::Serialize),
38 serde(bound = "")
39)]
40pub struct PoprfClient<CS: CipherSuite> {
41 #[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
42 pub(crate) blind: <CS::Group as Group>::Scalar,
43 #[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
44 pub(crate) blinded_element: <CS::Group as Group>::Elem,
45}
46
47#[derive_where(Clone, ZeroizeOnDrop)]
50#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
51#[cfg_attr(
52 feature = "serde",
53 derive(serde::Deserialize, serde::Serialize),
54 serde(bound = "")
55)]
56pub struct PoprfServer<CS: CipherSuite> {
57 #[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
58 pub(crate) sk: <CS::Group as Group>::Scalar,
59 #[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
60 pub(crate) pk: <CS::Group as Group>::Elem,
61}
62
63impl<CS: CipherSuite> PoprfClient<CS> {
69 pub fn blind<R: TryRng + TryCryptoRng>(
75 input: &[u8],
76 blinding_factor_rng: &mut R,
77 ) -> Result<PoprfClientBlindResult<CS>> {
78 let blind = CS::Group::random_scalar(blinding_factor_rng)?;
79 Self::deterministic_blind_unchecked_inner(input, blind)
80 }
81
82 #[cfg(any(feature = "danger", test))]
94 pub fn deterministic_blind_unchecked(
95 input: &[u8],
96 blind: <CS::Group as Group>::Scalar,
97 ) -> Result<PoprfClientBlindResult<CS>> {
98 Self::deterministic_blind_unchecked_inner(input, blind)
99 }
100
101 fn deterministic_blind_unchecked_inner(
103 input: &[u8],
104 blind: <CS::Group as Group>::Scalar,
105 ) -> Result<PoprfClientBlindResult<CS>> {
106 let blinded_element = deterministic_blind_unchecked::<CS>(input, &blind, Mode::Poprf)?;
107 Ok(PoprfClientBlindResult {
108 state: Self {
109 blind,
110 blinded_element,
111 },
112 message: BlindedElement(blinded_element),
113 })
114 }
115
116 pub fn finalize(
125 &self,
126 input: &[u8],
127 evaluation_element: &EvaluationElement<CS>,
128 proof: &Proof<CS>,
129 pk: <CS::Group as Group>::Elem,
130 info: Option<&[u8]>,
131 ) -> Result<Output<CS::Hash>>
132 where
133 <<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize: ArraySize,
134 {
135 let clients = core::array::from_ref(self);
136 let messages = core::array::from_ref(evaluation_element);
137
138 let mut batch_result =
139 Self::batch_finalize(iter::once(input), clients, messages, proof, pk, info)?;
140 batch_result.next().unwrap()
141 }
142
143 pub fn batch_finalize<'a, II: 'a + Iterator<Item = &'a [u8]> + ExactSizeIterator, IC, IM>(
156 inputs: II,
157 clients: &'a IC,
158 messages: &'a IM,
159 proof: &Proof<CS>,
160 pk: <CS::Group as Group>::Elem,
161 info: Option<&'a [u8]>,
162 ) -> Result<PoprfClientBatchFinalizeResult<'a, CS, II, IC, IM>>
163 where
164 CS: 'a,
165 &'a IC: 'a + IntoIterator<Item = &'a PoprfClient<CS>>,
166 <&'a IC as IntoIterator>::IntoIter: ExactSizeIterator,
167 &'a IM: 'a + IntoIterator<Item = &'a EvaluationElement<CS>>,
168 <&'a IM as IntoIterator>::IntoIter: ExactSizeIterator,
169 <<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize: ArraySize,
170 {
171 let unblinded_elements = poprf_unblind(clients, messages, pk, proof, info)?;
172
173 finalize_after_unblind::<'a, CS, _, _>(unblinded_elements, inputs, info)
174 }
175
176 #[cfg(test)]
178 pub fn get_blind(&self) -> <CS::Group as Group>::Scalar {
179 self.blind
180 }
181}
182
183impl<CS: CipherSuite> PoprfServer<CS> {
184 pub fn new<R: TryRng + TryCryptoRng>(rng: &mut R) -> Result<Self> {
189 let mut seed = Array::<_, <CS::Group as Group>::ScalarLen>::default();
190 rng.try_fill_bytes(&mut seed).map_err(|_| Error::Protocol)?;
191
192 Self::new_from_seed(&seed, &[])
193 }
194
195 pub fn new_with_key(key: &[u8]) -> Result<Self> {
202 let sk = CS::Group::deserialize_scalar(key)?;
203 let pk = CS::Group::base_elem() * &sk;
204 Ok(Self { sk, pk })
205 }
206
207 pub fn new_from_seed(seed: &[u8], info: &[u8]) -> Result<Self> {
217 let (sk, pk) = derive_keypair::<CS>(seed, info, Mode::Poprf)?;
218 Ok(Self { sk, pk })
219 }
220
221 #[cfg(test)]
223 pub fn get_private_key(&self) -> <CS::Group as Group>::Scalar {
224 self.sk
225 }
226
227 pub fn blind_evaluate<R: TryRng + TryCryptoRng>(
235 &self,
236 rng: &mut R,
237 blinded_element: &BlindedElement<CS>,
238 info: Option<&[u8]>,
239 ) -> Result<PoprfServerEvaluateResult<CS>> {
240 let PoprfServerBatchEvaluatePrepareResult {
241 mut prepared_evaluation_elements,
242 prepared_tweak,
243 } = self.batch_blind_evaluate_prepare(iter::once(blinded_element), info)?;
244
245 let prepared_evaluation_element = prepared_evaluation_elements.next().unwrap();
246 let prepared_evaluation_elements = core::array::from_ref(&prepared_evaluation_element);
247
248 let PoprfServerBatchEvaluateFinishResult {
249 mut messages,
250 proof,
251 } = Self::batch_blind_evaluate_finish(
252 rng,
253 iter::once(blinded_element),
254 prepared_evaluation_elements,
255 &prepared_tweak,
256 )
257 .unwrap();
258
259 Ok(PoprfServerEvaluateResult {
260 message: messages.next().unwrap(),
261 proof,
262 })
263 }
264
265 #[cfg(feature = "alloc")]
272 pub fn batch_blind_evaluate<'a, R: TryRng + TryCryptoRng, IE>(
273 &self,
274 rng: &mut R,
275 blinded_elements: &'a IE,
276 info: Option<&[u8]>,
277 ) -> Result<PoprfServerBatchEvaluateResult<CS>>
278 where
279 CS: 'a,
280 &'a IE: 'a + IntoIterator<Item = &'a BlindedElement<CS>>,
281 <&'a IE as IntoIterator>::IntoIter: ExactSizeIterator,
282 {
283 let PoprfServerBatchEvaluatePrepareResult {
284 prepared_evaluation_elements,
285 prepared_tweak,
286 } = self.batch_blind_evaluate_prepare(blinded_elements.into_iter(), info)?;
287
288 let prepared_evaluation_elements: Vec<_> = prepared_evaluation_elements.collect();
289
290 let PoprfServerBatchEvaluateFinishResult { messages, proof } =
292 Self::batch_blind_evaluate_finish::<_, _, Vec<_>>(
293 rng,
294 blinded_elements.into_iter(),
295 &prepared_evaluation_elements,
296 &prepared_tweak,
297 )
298 .unwrap();
299
300 let messages: Vec<_> = messages.collect();
301
302 Ok(PoprfServerBatchEvaluateResult { messages, proof })
303 }
304
305 pub fn batch_blind_evaluate_prepare<'a, I: Iterator<Item = &'a BlindedElement<CS>>>(
314 &self,
315 blinded_elements: I,
316 info: Option<&[u8]>,
317 ) -> Result<PoprfServerBatchEvaluatePrepareResult<CS, I>>
318 where
319 CS: 'a,
320 {
321 let tweak = compute_tweak::<CS>(self.sk, info)?;
322
323 Ok(PoprfServerBatchEvaluatePrepareResult {
324 prepared_evaluation_elements: blinded_elements.zip(iter::repeat(tweak)).map(
325 |(blinded_element, tweak)| {
326 PreparedEvaluationElement(EvaluationElement(
327 blinded_element.0 * &CS::Group::invert_scalar(tweak),
328 ))
329 },
330 ),
331 prepared_tweak: PoprfPreparedTweak(tweak),
332 })
333 }
334
335 pub fn batch_blind_evaluate_finish<
343 'a,
344 'b,
345 R: TryRng + TryCryptoRng,
346 IB: Iterator<Item = &'a BlindedElement<CS>> + ExactSizeIterator,
347 IE,
348 >(
349 rng: &mut R,
350 blinded_elements: IB,
351 prepared_evaluation_elements: &'b IE,
352 prepared_tweak: &PoprfPreparedTweak<CS>,
353 ) -> Result<PoprfServerBatchEvaluateFinishResult<'b, CS, IE>>
354 where
355 CS: 'a,
356 &'b IE: IntoIterator<Item = &'b PreparedEvaluationElement<CS>>,
357 <&'b IE as IntoIterator>::IntoIter: ExactSizeIterator,
358 {
359 let g = CS::Group::base_elem();
360 let tweak = prepared_tweak.0;
361 let tweaked_key = g * &tweak;
362
363 let proof = generate_proof(
364 rng,
365 tweak,
366 g,
367 tweaked_key,
368 prepared_evaluation_elements
369 .into_iter()
370 .map(|element| element.0.0),
371 blinded_elements.map(|element| element.0),
372 Mode::Poprf,
373 )?;
374
375 let messages = prepared_evaluation_elements.into_iter().map(<fn(
376 &PreparedEvaluationElement<CS>,
377 ) -> _>::from(
378 |element| EvaluationElement(element.0.0),
379 ));
380
381 Ok(PoprfServerBatchEvaluateFinishResult { messages, proof })
382 }
383
384 pub fn evaluate(
389 &self,
390 input: &[u8],
391 info: Option<&[u8]>,
392 ) -> Result<Output<<CS as CipherSuite>::Hash>> {
393 let input_element = hash_to_group::<CS>(input, Mode::Poprf)?;
394 if CS::Group::is_identity_elem(input_element).into() {
395 return Err(Error::Input);
396 };
397
398 let tweak = compute_tweak::<CS>(self.sk, info)?;
399
400 let evaluated_element = input_element * &CS::Group::invert_scalar(tweak);
401
402 let issued_element = CS::Group::serialize_elem(evaluated_element);
403
404 server_evaluate_hash_input::<CS>(input, info, issued_element)
405 }
406
407 pub fn get_public_key(&self) -> <CS::Group as Group>::Elem {
409 self.pk
410 }
411}
412
413impl<CS: CipherSuite> BlindedElement<CS> {
414 #[cfg(feature = "danger")]
421 pub fn from_value_unchecked(value: <CS::Group as Group>::Elem) -> Self {
422 Self(value)
423 }
424
425 #[cfg(feature = "danger")]
427 pub fn value(&self) -> <CS::Group as Group>::Elem {
428 self.0
429 }
430}
431
432impl<CS: CipherSuite> EvaluationElement<CS> {
433 #[cfg(feature = "danger")]
440 pub fn from_value_unchecked(value: <CS::Group as Group>::Elem) -> Self {
441 Self(value)
442 }
443
444 #[cfg(feature = "danger")]
446 pub fn value(&self) -> <CS::Group as Group>::Elem {
447 self.0
448 }
449}
450
451#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
458pub struct PoprfClientBlindResult<CS: CipherSuite> {
459 pub state: PoprfClient<CS>,
461 pub message: BlindedElement<CS>,
463}
464
465pub type PoprfClientBatchFinalizeResult<'a, CS, II, IC, IM> =
467 FinalizeAfterUnblindResult<'a, CS, PoprfUnblindResult<'a, CS, IC, IM>, II>;
468
469#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
471pub struct PoprfServerEvaluateResult<CS: CipherSuite> {
472 pub message: EvaluationElement<CS>,
474 pub proof: Proof<CS>,
476}
477
478#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
480#[cfg(feature = "alloc")]
481pub struct PoprfServerBatchEvaluateResult<CS: CipherSuite> {
482 pub messages: Vec<EvaluationElement<CS>>,
484 pub proof: Proof<CS>,
486}
487
488pub type PoprfServerBatchEvaluatePreparedEvaluationElements<CS, I> = Map<
491 Zip<I, Repeat<<<CS as CipherSuite>::Group as Group>::Scalar>>,
492 fn(
493 (
494 &BlindedElement<CS>,
495 <<CS as CipherSuite>::Group as Group>::Scalar,
496 ),
497 ) -> PreparedEvaluationElement<CS>,
498>;
499
500#[derive_where(Clone, ZeroizeOnDrop)]
502#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::Group as Group>::Scalar)]
503#[cfg_attr(
504 feature = "serde",
505 derive(serde::Deserialize, serde::Serialize),
506 serde(bound = "")
507)]
508pub struct PoprfPreparedTweak<CS: CipherSuite>(
509 #[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
510 <CS::Group as Group>::Scalar,
511);
512
513#[derive_where(Debug; I, <CS::Group as Group>::Scalar)]
516pub struct PoprfServerBatchEvaluatePrepareResult<CS: CipherSuite, I> {
517 pub prepared_evaluation_elements: PoprfServerBatchEvaluatePreparedEvaluationElements<CS, I>,
519 pub prepared_tweak: PoprfPreparedTweak<CS>,
521}
522
523pub type PoprfServerBatchEvaluateFinishedMessages<'a, CS, I> = Map<
526 <&'a I as IntoIterator>::IntoIter,
527 fn(&PreparedEvaluationElement<CS>) -> EvaluationElement<CS>,
528>;
529
530#[derive_where(Debug; <&'a I as IntoIterator>::IntoIter, <CS::Group as Group>::Scalar)]
533pub struct PoprfServerBatchEvaluateFinishResult<'a, CS: 'a + CipherSuite, I>
534where
535 &'a I: IntoIterator<Item = &'a PreparedEvaluationElement<CS>>,
536{
537 pub messages: PoprfServerBatchEvaluateFinishedMessages<'a, CS, I>,
539 pub proof: Proof<CS>,
541}
542
543fn compute_tweaked_key<CS: CipherSuite>(
553 pk: <CS::Group as Group>::Elem,
554 info: Option<&[u8]>,
555) -> Result<<CS::Group as Group>::Elem> {
556 let info = info.unwrap_or_default();
558
559 let info_len = i2osp_2(info.len()).map_err(|_| Error::Info)?;
566 let framed_info = [STR_INFO.as_slice(), &info_len, info];
567
568 let dst = Dst::new::<CS, _>(STR_HASH_TO_SCALAR, Mode::Poprf);
569 let m = CS::Group::hash_to_scalar::<CS::Hash>(&framed_info, &dst.as_dst()).unwrap();
571
572 let t = CS::Group::base_elem() * &m;
573 let tweaked_key = t + &pk;
574
575 match bool::from(CS::Group::is_identity_elem(tweaked_key)) {
577 true => Err(Error::Protocol),
578 false => Ok(tweaked_key),
579 }
580}
581
582fn compute_tweak<CS: CipherSuite>(
587 sk: <CS::Group as Group>::Scalar,
588 info: Option<&[u8]>,
589) -> Result<<CS::Group as Group>::Scalar> {
590 let info = info.unwrap_or_default();
592
593 let info_len = i2osp_2(info.len()).map_err(|_| Error::Info)?;
599 let framed_info = [STR_INFO.as_slice(), &info_len, info];
600
601 let dst = Dst::new::<CS, _>(STR_HASH_TO_SCALAR, Mode::Poprf);
602 let m = CS::Group::hash_to_scalar::<CS::Hash>(&framed_info, &dst.as_dst()).unwrap();
604
605 let t = sk + &m;
606
607 match bool::from(CS::Group::is_zero_scalar(t)) {
609 true => Err(Error::Protocol),
610 false => Ok(t),
611 }
612}
613
614type PoprfUnblindResult<'a, CS, IC, IM> = Map<
615 Zip<
616 Map<
617 <&'a IC as IntoIterator>::IntoIter,
618 fn(&PoprfClient<CS>) -> <<CS as CipherSuite>::Group as Group>::Scalar,
619 >,
620 <&'a IM as IntoIterator>::IntoIter,
621 >,
622 fn(
623 (
624 <<CS as CipherSuite>::Group as Group>::Scalar,
625 &'a EvaluationElement<CS>,
626 ),
627 ) -> <<CS as CipherSuite>::Group as Group>::Elem,
628>;
629
630fn poprf_unblind<'a, CS: 'a + CipherSuite, IC, IM>(
633 clients: &'a IC,
634 messages: &'a IM,
635 pk: <CS::Group as Group>::Elem,
636 proof: &Proof<CS>,
637 info: Option<&[u8]>,
638) -> Result<PoprfUnblindResult<'a, CS, IC, IM>>
639where
640 &'a IC: 'a + IntoIterator<Item = &'a PoprfClient<CS>>,
641 <&'a IC as IntoIterator>::IntoIter: ExactSizeIterator,
642 &'a IM: 'a + IntoIterator<Item = &'a EvaluationElement<CS>>,
643 <&'a IM as IntoIterator>::IntoIter: ExactSizeIterator,
644{
645 let info = info.unwrap_or_default();
646 let tweaked_key = compute_tweaked_key::<CS>(pk, Some(info))?;
647
648 let g = CS::Group::base_elem();
649
650 let blinds = clients
651 .into_iter()
652 .map(<fn(&PoprfClient<CS>) -> _>::from(|x| x.blind));
654 let evaluation_elements = messages.into_iter().map(|element| element.0);
655 let blinded_elements = clients.into_iter().map(|client| client.blinded_element);
656
657 verify_proof(
658 g,
659 tweaked_key,
660 evaluation_elements,
661 blinded_elements,
662 proof,
663 Mode::Poprf,
664 )?;
665
666 Ok(blinds
667 .zip(messages)
668 .map(|(blind, x)| x.0 * &CS::Group::invert_scalar(blind)))
669}
670
671type FinalizeAfterUnblindResult<'a, CS, IE, II> = Map<
672 Zip<Zip<IE, II>, Repeat<&'a [u8]>>,
673 fn(
674 ((<<CS as CipherSuite>::Group as Group>::Elem, &[u8]), &[u8]),
675 ) -> Result<Output<<CS as CipherSuite>::Hash>>,
676>;
677
678fn finalize_after_unblind<
681 'a,
682 CS: CipherSuite,
683 IE: 'a + Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator,
684 II: 'a + Iterator<Item = &'a [u8]> + ExactSizeIterator,
685>(
686 unblinded_elements: IE,
687 inputs: II,
688 info: Option<&'a [u8]>,
689) -> Result<FinalizeAfterUnblindResult<'a, CS, IE, II>>
690where
691 <<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize: ArraySize,
692{
693 if unblinded_elements.len() != inputs.len() {
694 return Err(Error::Batch);
695 }
696
697 let info = info.unwrap_or_default();
698
699 Ok(unblinded_elements.zip(inputs).zip(iter::repeat(info)).map(
700 |((unblinded_element, input), info)| {
701 let elem_len = <CS::Group as Group>::ElemLen::U16.to_be_bytes();
702
703 let output = CS::Hash::new()
709 .chain_update(i2osp_2(input.as_ref().len()).map_err(|_| Error::Input)?)
710 .chain_update(input.as_ref())
711 .chain_update(i2osp_2(info.as_ref().len()).map_err(|_| Error::Info)?)
712 .chain_update(info.as_ref())
713 .chain_update(elem_len)
714 .chain_update(CS::Group::serialize_elem(unblinded_element))
715 .chain_update(STR_FINALIZE)
716 .finalize();
717
718 Ok(output)
719 },
720 ))
721}
722
723#[cfg(test)]
729mod tests {
730 use core::ptr;
731
732 use rand::rngs::SysRng;
733
734 use super::*;
735 use crate::Group;
736 use crate::common::STR_HASH_TO_GROUP;
737
738 fn prf<CS: CipherSuite>(
739 input: &[u8],
740 key: <CS::Group as Group>::Scalar,
741 info: &[u8],
742 mode: Mode,
743 ) -> Output<CS::Hash> {
744 let t = compute_tweak::<CS>(key, Some(info)).unwrap();
745
746 let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, mode);
747 let point = CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst.as_dst()).unwrap();
748
749 let res = point * &CS::Group::invert_scalar(t);
751
752 finalize_after_unblind::<CS, _, _>(iter::once(res), iter::once(input), Some(info))
753 .unwrap()
754 .next()
755 .unwrap()
756 .unwrap()
757 }
758
759 fn verifiable_retrieval<CS: CipherSuite>() {
760 let input = b"input";
761 let info = b"info";
762 let mut rng = SysRng;
763 let server = PoprfServer::<CS>::new(&mut rng).unwrap();
764 let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
765 let server_result = server
766 .blind_evaluate(&mut rng, &client_blind_result.message, Some(info))
767 .unwrap();
768 let client_finalize_result = client_blind_result
769 .state
770 .finalize(
771 input,
772 &server_result.message,
773 &server_result.proof,
774 server.get_public_key(),
775 Some(info),
776 )
777 .unwrap();
778 let res2 = prf::<CS>(input, server.get_private_key(), info, Mode::Poprf);
779 assert_eq!(client_finalize_result, res2);
780 }
781
782 fn verifiable_bad_public_key<CS: CipherSuite>() {
783 let input = b"input";
784 let info = b"info";
785 let mut rng = SysRng;
786 let server = PoprfServer::<CS>::new(&mut rng).unwrap();
787 let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
788 let server_result = server
789 .blind_evaluate(&mut rng, &client_blind_result.message, Some(info))
790 .unwrap();
791 let wrong_pk = {
792 let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, Mode::Oprf);
793 CS::Group::hash_to_curve::<CS::Hash>(&[b"msg"], &dst.as_dst()).unwrap()
795 };
796 let client_finalize_result = client_blind_result.state.finalize(
797 input,
798 &server_result.message,
799 &server_result.proof,
800 wrong_pk,
801 Some(info),
802 );
803 assert!(client_finalize_result.is_err());
804 }
805
806 fn verifiable_server_evaluate<CS: CipherSuite>() {
807 let input = b"input";
808 let info = Some(b"info".as_slice());
809 let mut rng = SysRng;
810 let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
811 let server = PoprfServer::<CS>::new(&mut rng).unwrap();
812 let server_result = server
813 .blind_evaluate(&mut rng, &client_blind_result.message, info)
814 .unwrap();
815
816 let client_finalize = client_blind_result
817 .state
818 .finalize(
819 input,
820 &server_result.message,
821 &server_result.proof,
822 server.get_public_key(),
823 info,
824 )
825 .unwrap();
826
827 let server_evaluate = server.evaluate(input, info).unwrap();
830 assert_eq!(client_finalize, server_evaluate);
831
832 let wrong_input = b"wrong input";
835 let server_evaluate = server.evaluate(wrong_input, info).unwrap();
836 assert!(client_finalize != server_evaluate);
837 }
838
839 fn zeroize_verifiable_client<CS: CipherSuite>() {
840 let input = b"input";
841 let mut rng = SysRng;
842 let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
843
844 let mut state = client_blind_result.state;
845 unsafe { ptr::drop_in_place(&mut state) };
846 assert!(state.serialize().iter().all(|&x| x == 0));
847
848 let mut message = client_blind_result.message;
849 unsafe { ptr::drop_in_place(&mut message) };
850 assert!(message.serialize().iter().all(|&x| x == 0));
851 }
852
853 fn zeroize_verifiable_server<CS: CipherSuite>() {
854 let input = b"input";
855 let info = b"info";
856 let mut rng = SysRng;
857 let server = PoprfServer::<CS>::new(&mut rng).unwrap();
858 let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
859 let server_result = server
860 .blind_evaluate(&mut rng, &client_blind_result.message, Some(info))
861 .unwrap();
862
863 let mut state = server;
864 unsafe { ptr::drop_in_place(&mut state) };
865 assert!(state.serialize().iter().all(|&x| x == 0));
866
867 let mut message = server_result.message;
868 unsafe { ptr::drop_in_place(&mut message) };
869 assert!(message.serialize().iter().all(|&x| x == 0));
870
871 let mut proof = server_result.proof;
872 unsafe { ptr::drop_in_place(&mut proof) };
873 assert!(proof.serialize().iter().all(|&x| x == 0));
874 }
875
876 crate::tests::test_all_curves!(
877 verifiable_retrieval,
878 verifiable_bad_public_key,
879 verifiable_server_evaluate,
880 zeroize_verifiable_client,
881 zeroize_verifiable_server,
882 );
883}