1#[cfg(feature = "alloc")]
6use alloc::vec::Vec;
7use core::iter::{self, Map, Repeat, Zip};
8
9use derive_where::derive_where;
10use digest::{Digest, Output, OutputSizeUser};
11use generic_array::typenum::Unsigned;
12use generic_array::{ArrayLength, GenericArray};
13use rand_core::{TryCryptoRng, TryRng};
14
15use crate::common::{
16 derive_keypair, deterministic_blind_unchecked, generate_proof, hash_to_group, i2osp_2,
17 server_evaluate_hash_input, verify_proof, BlindedElement, Dst, EvaluationElement, Mode,
18 PreparedEvaluationElement, Proof, STR_FINALIZE, STR_HASH_TO_SCALAR, STR_INFO,
19};
20#[cfg(feature = "serde")]
21use crate::serialization::serde::{Element, Scalar};
22use crate::{CipherSuite, Error, Group, Result};
23
24#[derive_where(Clone, ZeroizeOnDrop)]
32#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
33#[cfg_attr(
34 feature = "serde",
35 derive(serde::Deserialize, serde::Serialize),
36 serde(bound = "")
37)]
38pub struct PoprfClient<CS: CipherSuite> {
39 #[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
40 pub(crate) blind: <CS::Group as Group>::Scalar,
41 #[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
42 pub(crate) blinded_element: <CS::Group as Group>::Elem,
43}
44
45#[derive_where(Clone, ZeroizeOnDrop)]
48#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
49#[cfg_attr(
50 feature = "serde",
51 derive(serde::Deserialize, serde::Serialize),
52 serde(bound = "")
53)]
54pub struct PoprfServer<CS: CipherSuite> {
55 #[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
56 pub(crate) sk: <CS::Group as Group>::Scalar,
57 #[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
58 pub(crate) pk: <CS::Group as Group>::Elem,
59}
60
61impl<CS: CipherSuite> PoprfClient<CS> {
67 pub fn blind<R: TryRng + TryCryptoRng>(
74 input: &[u8],
75 blinding_factor_rng: &mut R,
76 ) -> Result<PoprfClientBlindResult<CS>> {
77 let blind = CS::Group::random_scalar(blinding_factor_rng)?;
78 Self::deterministic_blind_unchecked_inner(input, blind)
79 }
80
81 #[cfg(any(feature = "danger", test))]
97 pub fn deterministic_blind_unchecked(
98 input: &[u8],
99 blind: <CS::Group as Group>::Scalar,
100 ) -> Result<PoprfClientBlindResult<CS>> {
101 Self::deterministic_blind_unchecked_inner(input, blind)
102 }
103
104 fn deterministic_blind_unchecked_inner(
106 input: &[u8],
107 blind: <CS::Group as Group>::Scalar,
108 ) -> Result<PoprfClientBlindResult<CS>> {
109 let blinded_element = deterministic_blind_unchecked::<CS>(input, &blind, Mode::Poprf)?;
110 Ok(PoprfClientBlindResult {
111 state: Self {
112 blind,
113 blinded_element,
114 },
115 message: BlindedElement(blinded_element),
116 })
117 }
118
119 pub fn finalize(
128 &self,
129 input: &[u8],
130 evaluation_element: &EvaluationElement<CS>,
131 proof: &Proof<CS>,
132 pk: <CS::Group as Group>::Elem,
133 info: Option<&[u8]>,
134 ) -> Result<Output<CS::Hash>>
135 where
136 <<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize: ArrayLength,
137 {
138 let clients = core::array::from_ref(self);
139 let messages = core::array::from_ref(evaluation_element);
140
141 let mut batch_result =
142 Self::batch_finalize(iter::once(input), clients, messages, proof, pk, info)?;
143 batch_result.next().unwrap()
144 }
145
146 pub fn batch_finalize<'a, II: 'a + Iterator<Item = &'a [u8]> + ExactSizeIterator, IC, IM>(
159 inputs: II,
160 clients: &'a IC,
161 messages: &'a IM,
162 proof: &Proof<CS>,
163 pk: <CS::Group as Group>::Elem,
164 info: Option<&'a [u8]>,
165 ) -> Result<PoprfClientBatchFinalizeResult<'a, CS, II, IC, IM>>
166 where
167 CS: 'a,
168 &'a IC: 'a + IntoIterator<Item = &'a PoprfClient<CS>>,
169 <&'a IC as IntoIterator>::IntoIter: ExactSizeIterator,
170 &'a IM: 'a + IntoIterator<Item = &'a EvaluationElement<CS>>,
171 <&'a IM as IntoIterator>::IntoIter: ExactSizeIterator,
172 <<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize: ArrayLength,
173 {
174 let unblinded_elements = poprf_unblind(clients, messages, pk, proof, info)?;
175
176 finalize_after_unblind::<'a, CS, _, _>(unblinded_elements, inputs, info)
177 }
178
179 #[cfg(test)]
181 pub fn get_blind(&self) -> <CS::Group as Group>::Scalar {
182 self.blind
183 }
184}
185
186impl<CS: CipherSuite> PoprfServer<CS> {
187 pub fn new<R: TryRng + TryCryptoRng>(rng: &mut R) -> Result<Self> {
192 let mut seed = GenericArray::<_, <CS::Group as Group>::ScalarLen>::default();
193 rng.try_fill_bytes(&mut seed).map_err(|_| Error::Rng)?;
194
195 Self::new_from_seed(&seed, &[])
196 }
197
198 pub fn new_with_key(key: &[u8]) -> Result<Self> {
205 let sk = CS::Group::deserialize_scalar(key)?;
206 let pk = CS::Group::base_elem() * &sk;
207 Ok(Self { sk, pk })
208 }
209
210 pub fn new_from_seed(seed: &[u8], info: &[u8]) -> Result<Self> {
220 let (sk, pk) = derive_keypair::<CS>(seed, info, Mode::Poprf)?;
221 Ok(Self { sk, pk })
222 }
223
224 #[cfg(test)]
226 pub fn get_private_key(&self) -> <CS::Group as Group>::Scalar {
227 self.sk
228 }
229
230 pub fn blind_evaluate<R: TryRng + TryCryptoRng>(
239 &self,
240 rng: &mut R,
241 blinded_element: &BlindedElement<CS>,
242 info: Option<&[u8]>,
243 ) -> Result<PoprfServerEvaluateResult<CS>> {
244 let PoprfServerBatchEvaluatePrepareResult {
245 mut prepared_evaluation_elements,
246 prepared_tweak,
247 } = self.batch_blind_evaluate_prepare(iter::once(blinded_element), info)?;
248
249 let prepared_evaluation_element = prepared_evaluation_elements.next().unwrap();
250 let prepared_evaluation_elements = core::array::from_ref(&prepared_evaluation_element);
251
252 let PoprfServerBatchEvaluateFinishResult {
253 mut messages,
254 proof,
255 } = Self::batch_blind_evaluate_finish(
256 rng,
257 iter::once(blinded_element),
258 prepared_evaluation_elements,
259 &prepared_tweak,
260 )?;
261
262 Ok(PoprfServerEvaluateResult {
263 message: messages.next().unwrap(),
264 proof,
265 })
266 }
267
268 #[cfg(feature = "alloc")]
276 pub fn batch_blind_evaluate<'a, R: TryRng + TryCryptoRng, IE>(
277 &self,
278 rng: &mut R,
279 blinded_elements: &'a IE,
280 info: Option<&[u8]>,
281 ) -> Result<PoprfServerBatchEvaluateResult<CS>>
282 where
283 CS: 'a,
284 &'a IE: 'a + IntoIterator<Item = &'a BlindedElement<CS>>,
285 <&'a IE as IntoIterator>::IntoIter: ExactSizeIterator,
286 {
287 let PoprfServerBatchEvaluatePrepareResult {
288 prepared_evaluation_elements,
289 prepared_tweak,
290 } = self.batch_blind_evaluate_prepare(blinded_elements.into_iter(), info)?;
291
292 let prepared_evaluation_elements: Vec<_> = prepared_evaluation_elements.collect();
293
294 let PoprfServerBatchEvaluateFinishResult { messages, proof } =
295 Self::batch_blind_evaluate_finish::<_, _, Vec<_>>(
296 rng,
297 blinded_elements.into_iter(),
298 &prepared_evaluation_elements,
299 &prepared_tweak,
300 )?;
301
302 let messages: Vec<_> = messages.collect();
303
304 Ok(PoprfServerBatchEvaluateResult { messages, proof })
305 }
306
307 pub fn batch_blind_evaluate_prepare<'a, I: Iterator<Item = &'a BlindedElement<CS>>>(
316 &self,
317 blinded_elements: I,
318 info: Option<&[u8]>,
319 ) -> Result<PoprfServerBatchEvaluatePrepareResult<CS, I>>
320 where
321 CS: 'a,
322 {
323 let tweak = compute_tweak::<CS>(self.sk, info)?;
324
325 Ok(PoprfServerBatchEvaluatePrepareResult {
326 prepared_evaluation_elements: blinded_elements.zip(iter::repeat(tweak)).map(
327 |(blinded_element, tweak)| {
328 PreparedEvaluationElement(EvaluationElement(
329 blinded_element.0 * &CS::Group::invert_scalar(tweak),
330 ))
331 },
332 ),
333 prepared_tweak: PoprfPreparedTweak(tweak),
334 })
335 }
336
337 pub fn batch_blind_evaluate_finish<
345 'a,
346 'b,
347 R: TryRng + TryCryptoRng,
348 IB: Iterator<Item = &'a BlindedElement<CS>> + ExactSizeIterator,
349 IE,
350 >(
351 rng: &mut R,
352 blinded_elements: IB,
353 prepared_evaluation_elements: &'b IE,
354 prepared_tweak: &PoprfPreparedTweak<CS>,
355 ) -> Result<PoprfServerBatchEvaluateFinishResult<'b, CS, IE>>
356 where
357 CS: 'a,
358 &'b IE: IntoIterator<Item = &'b PreparedEvaluationElement<CS>>,
359 <&'b IE as IntoIterator>::IntoIter: ExactSizeIterator,
360 {
361 let g = CS::Group::base_elem();
362 let tweak = prepared_tweak.0;
363 let tweaked_key = g * &tweak;
364
365 let proof = generate_proof(
366 rng,
367 tweak,
368 g,
369 tweaked_key,
370 prepared_evaluation_elements
371 .into_iter()
372 .map(|element| element.0 .0),
373 blinded_elements.map(|element| element.0),
374 Mode::Poprf,
375 )?;
376
377 let messages = prepared_evaluation_elements.into_iter().map(<fn(
378 &PreparedEvaluationElement<CS>,
379 ) -> _>::from(
380 |element| EvaluationElement(element.0 .0),
381 ));
382
383 Ok(PoprfServerBatchEvaluateFinishResult { messages, proof })
384 }
385
386 pub fn evaluate(
391 &self,
392 input: &[u8],
393 info: Option<&[u8]>,
394 ) -> Result<Output<<CS as CipherSuite>::Hash>> {
395 let input_element = hash_to_group::<CS>(input, Mode::Poprf)?;
396 if CS::Group::is_identity_elem(input_element).into() {
397 return Err(Error::Input);
398 };
399
400 let tweak = compute_tweak::<CS>(self.sk, info)?;
401
402 let evaluated_element = input_element * &CS::Group::invert_scalar(tweak);
403
404 let issued_element = CS::Group::serialize_elem(evaluated_element);
405
406 server_evaluate_hash_input::<CS>(input, info, issued_element)
407 }
408
409 pub fn get_public_key(&self) -> <CS::Group as Group>::Elem {
411 self.pk
412 }
413}
414
415impl<CS: CipherSuite> BlindedElement<CS> {
416 #[cfg(feature = "danger")]
423 pub fn from_value_unchecked(value: <CS::Group as Group>::Elem) -> Self {
424 Self(value)
425 }
426
427 #[cfg(feature = "danger")]
429 pub fn value(&self) -> <CS::Group as Group>::Elem {
430 self.0
431 }
432}
433
434impl<CS: CipherSuite> EvaluationElement<CS> {
435 #[cfg(feature = "danger")]
442 pub fn from_value_unchecked(value: <CS::Group as Group>::Elem) -> Self {
443 Self(value)
444 }
445
446 #[cfg(feature = "danger")]
448 pub fn value(&self) -> <CS::Group as Group>::Elem {
449 self.0
450 }
451}
452
453#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
460pub struct PoprfClientBlindResult<CS: CipherSuite> {
461 pub state: PoprfClient<CS>,
463 pub message: BlindedElement<CS>,
465}
466
467pub type PoprfClientBatchFinalizeResult<'a, CS, II, IC, IM> =
469 FinalizeAfterUnblindResult<'a, CS, PoprfUnblindResult<'a, CS, IC, IM>, II>;
470
471#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
473pub struct PoprfServerEvaluateResult<CS: CipherSuite> {
474 pub message: EvaluationElement<CS>,
476 pub proof: Proof<CS>,
478}
479
480#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
482#[cfg(feature = "alloc")]
483pub struct PoprfServerBatchEvaluateResult<CS: CipherSuite> {
484 pub messages: Vec<EvaluationElement<CS>>,
486 pub proof: Proof<CS>,
488}
489
490pub type PoprfServerBatchEvaluatePreparedEvaluationElements<CS, I> = Map<
493 Zip<I, Repeat<<<CS as CipherSuite>::Group as Group>::Scalar>>,
494 fn(
495 (
496 &BlindedElement<CS>,
497 <<CS as CipherSuite>::Group as Group>::Scalar,
498 ),
499 ) -> PreparedEvaluationElement<CS>,
500>;
501
502#[derive_where(Clone, ZeroizeOnDrop)]
504#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::Group as Group>::Scalar)]
505#[cfg_attr(
506 feature = "serde",
507 derive(serde::Deserialize, serde::Serialize),
508 serde(bound = "")
509)]
510pub struct PoprfPreparedTweak<CS: CipherSuite>(
511 #[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
512 <CS::Group as Group>::Scalar,
513);
514
515#[derive_where(Debug; I, <CS::Group as Group>::Scalar)]
518pub struct PoprfServerBatchEvaluatePrepareResult<CS: CipherSuite, I> {
519 pub prepared_evaluation_elements: PoprfServerBatchEvaluatePreparedEvaluationElements<CS, I>,
521 pub prepared_tweak: PoprfPreparedTweak<CS>,
523}
524
525pub type PoprfServerBatchEvaluateFinishedMessages<'a, CS, I> = Map<
528 <&'a I as IntoIterator>::IntoIter,
529 fn(&PreparedEvaluationElement<CS>) -> EvaluationElement<CS>,
530>;
531
532#[derive_where(Debug; <&'a I as IntoIterator>::IntoIter, <CS::Group as Group>::Scalar)]
535pub struct PoprfServerBatchEvaluateFinishResult<'a, CS: 'a + CipherSuite, I>
536where
537 &'a I: IntoIterator<Item = &'a PreparedEvaluationElement<CS>>,
538{
539 pub messages: PoprfServerBatchEvaluateFinishedMessages<'a, CS, I>,
541 pub proof: Proof<CS>,
543}
544
545fn compute_tweaked_key<CS: CipherSuite>(
555 pk: <CS::Group as Group>::Elem,
556 info: Option<&[u8]>,
557) -> Result<<CS::Group as Group>::Elem> {
558 let info = info.unwrap_or_default();
560
561 let info_len = i2osp_2(info.len()).map_err(|_| Error::Info)?;
568 let framed_info = [STR_INFO.as_slice(), &info_len, info];
569
570 let dst = Dst::new::<CS, _, _>(STR_HASH_TO_SCALAR, Mode::Poprf);
571 let m = CS::Group::hash_to_scalar::<CS::Hash>(&framed_info, &dst.as_dst()).unwrap();
573
574 let t = CS::Group::base_elem() * &m;
575 let tweaked_key = t + &pk;
576
577 match bool::from(CS::Group::is_identity_elem(tweaked_key)) {
579 true => Err(Error::Protocol),
580 false => Ok(tweaked_key),
581 }
582}
583
584fn compute_tweak<CS: CipherSuite>(
589 sk: <CS::Group as Group>::Scalar,
590 info: Option<&[u8]>,
591) -> Result<<CS::Group as Group>::Scalar> {
592 let info = info.unwrap_or_default();
594
595 let info_len = i2osp_2(info.len()).map_err(|_| Error::Info)?;
601 let framed_info = [STR_INFO.as_slice(), &info_len, info];
602
603 let dst = Dst::new::<CS, _, _>(STR_HASH_TO_SCALAR, Mode::Poprf);
604 let m = CS::Group::hash_to_scalar::<CS::Hash>(&framed_info, &dst.as_dst()).unwrap();
606
607 let t = sk + &m;
608
609 match bool::from(CS::Group::is_zero_scalar(t)) {
611 true => Err(Error::Protocol),
612 false => Ok(t),
613 }
614}
615
616type PoprfUnblindResult<'a, CS, IC, IM> = Map<
617 Zip<
618 Map<
619 <&'a IC as IntoIterator>::IntoIter,
620 fn(&PoprfClient<CS>) -> <<CS as CipherSuite>::Group as Group>::Scalar,
621 >,
622 <&'a IM as IntoIterator>::IntoIter,
623 >,
624 fn(
625 (
626 <<CS as CipherSuite>::Group as Group>::Scalar,
627 &'a EvaluationElement<CS>,
628 ),
629 ) -> <<CS as CipherSuite>::Group as Group>::Elem,
630>;
631
632fn poprf_unblind<'a, CS: 'a + CipherSuite, IC, IM>(
635 clients: &'a IC,
636 messages: &'a IM,
637 pk: <CS::Group as Group>::Elem,
638 proof: &Proof<CS>,
639 info: Option<&[u8]>,
640) -> Result<PoprfUnblindResult<'a, CS, IC, IM>>
641where
642 &'a IC: 'a + IntoIterator<Item = &'a PoprfClient<CS>>,
643 <&'a IC as IntoIterator>::IntoIter: ExactSizeIterator,
644 &'a IM: 'a + IntoIterator<Item = &'a EvaluationElement<CS>>,
645 <&'a IM as IntoIterator>::IntoIter: ExactSizeIterator,
646{
647 let info = info.unwrap_or_default();
648 let tweaked_key = compute_tweaked_key::<CS>(pk, Some(info))?;
649
650 let g = CS::Group::base_elem();
651
652 let blinds = clients
653 .into_iter()
654 .map(<fn(&PoprfClient<CS>) -> _>::from(|x| x.blind));
656 let evaluation_elements = messages.into_iter().map(|element| element.0);
657 let blinded_elements = clients.into_iter().map(|client| client.blinded_element);
658
659 verify_proof(
660 g,
661 tweaked_key,
662 evaluation_elements,
663 blinded_elements,
664 proof,
665 Mode::Poprf,
666 )?;
667
668 Ok(blinds
669 .zip(messages)
670 .map(|(blind, x)| x.0 * &CS::Group::invert_scalar(blind)))
671}
672
673type FinalizeAfterUnblindResult<'a, CS, IE, II> = Map<
674 Zip<Zip<IE, II>, Repeat<&'a [u8]>>,
675 fn(
676 ((<<CS as CipherSuite>::Group as Group>::Elem, &[u8]), &[u8]),
677 ) -> Result<Output<<CS as CipherSuite>::Hash>>,
678>;
679
680fn finalize_after_unblind<
683 'a,
684 CS: CipherSuite,
685 IE: 'a + Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator,
686 II: 'a + Iterator<Item = &'a [u8]> + ExactSizeIterator,
687>(
688 unblinded_elements: IE,
689 inputs: II,
690 info: Option<&'a [u8]>,
691) -> Result<FinalizeAfterUnblindResult<'a, CS, IE, II>>
692where
693 <<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize: ArrayLength,
694{
695 if unblinded_elements.len() != inputs.len() {
696 return Err(Error::Batch);
697 }
698
699 let info = info.unwrap_or_default();
700
701 Ok(unblinded_elements.zip(inputs).zip(iter::repeat(info)).map(
702 |((unblinded_element, input), info)| {
703 let elem_len = <CS::Group as Group>::ElemLen::U16.to_be_bytes();
704
705 let output = CS::Hash::new()
711 .chain_update(i2osp_2(input.as_ref().len()).map_err(|_| Error::Input)?)
712 .chain_update(input.as_ref())
713 .chain_update(i2osp_2(info.as_ref().len()).map_err(|_| Error::Info)?)
714 .chain_update(info.as_ref())
715 .chain_update(elem_len)
716 .chain_update(CS::Group::serialize_elem(unblinded_element))
717 .chain_update(STR_FINALIZE)
718 .finalize();
719
720 Ok(output)
721 },
722 ))
723}
724
725#[cfg(test)]
731mod tests {
732 use core::ptr;
733
734 use rand::rngs::SysRng;
735
736 use super::*;
737 use crate::common::STR_HASH_TO_GROUP;
738 use crate::Group;
739
740 fn prf<CS: CipherSuite>(
741 input: &[u8],
742 key: <CS::Group as Group>::Scalar,
743 info: &[u8],
744 mode: Mode,
745 ) -> Output<CS::Hash> {
746 let t = compute_tweak::<CS>(key, Some(info)).unwrap();
747
748 let dst = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, mode);
749 let point = CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst.as_dst()).unwrap();
750
751 let res = point * &CS::Group::invert_scalar(t);
753
754 finalize_after_unblind::<CS, _, _>(iter::once(res), iter::once(input), Some(info))
755 .unwrap()
756 .next()
757 .unwrap()
758 .unwrap()
759 }
760
761 fn verifiable_retrieval<CS: CipherSuite>() {
762 let input = b"input";
763 let info = b"info";
764 let mut rng = SysRng;
765 let server = PoprfServer::<CS>::new(&mut rng).unwrap();
766 let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
767 let server_result = server
768 .blind_evaluate(&mut rng, &client_blind_result.message, Some(info))
769 .unwrap();
770 let client_finalize_result = client_blind_result
771 .state
772 .finalize(
773 input,
774 &server_result.message,
775 &server_result.proof,
776 server.get_public_key(),
777 Some(info),
778 )
779 .unwrap();
780 let res2 = prf::<CS>(input, server.get_private_key(), info, Mode::Poprf);
781 assert_eq!(client_finalize_result, res2);
782 }
783
784 fn verifiable_bad_public_key<CS: CipherSuite>() {
785 let input = b"input";
786 let info = b"info";
787 let mut rng = SysRng;
788 let server = PoprfServer::<CS>::new(&mut rng).unwrap();
789 let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
790 let server_result = server
791 .blind_evaluate(&mut rng, &client_blind_result.message, Some(info))
792 .unwrap();
793 let wrong_pk = {
794 let dst = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, Mode::Oprf);
795 CS::Group::hash_to_curve::<CS::Hash>(&[b"msg"], &dst.as_dst()).unwrap()
797 };
798 let client_finalize_result = client_blind_result.state.finalize(
799 input,
800 &server_result.message,
801 &server_result.proof,
802 wrong_pk,
803 Some(info),
804 );
805 assert!(client_finalize_result.is_err());
806 }
807
808 fn verifiable_server_evaluate<CS: CipherSuite>() {
809 let input = b"input";
810 let info = Some(b"info".as_slice());
811 let mut rng = SysRng;
812 let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
813 let server = PoprfServer::<CS>::new(&mut rng).unwrap();
814 let server_result = server
815 .blind_evaluate(&mut rng, &client_blind_result.message, info)
816 .unwrap();
817
818 let client_finalize = client_blind_result
819 .state
820 .finalize(
821 input,
822 &server_result.message,
823 &server_result.proof,
824 server.get_public_key(),
825 info,
826 )
827 .unwrap();
828
829 let server_evaluate = server.evaluate(input, info).unwrap();
832 assert_eq!(client_finalize, server_evaluate);
833
834 let wrong_input = b"wrong input";
837 let server_evaluate = server.evaluate(wrong_input, info).unwrap();
838 assert!(client_finalize != server_evaluate);
839 }
840
841 fn zeroize_verifiable_client<CS: CipherSuite>() {
842 let input = b"input";
843 let mut rng = SysRng;
844 let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
845
846 let mut state = client_blind_result.state;
847 unsafe { ptr::drop_in_place(&mut state) };
848 assert!(state.serialize().iter().all(|&x| x == 0));
849
850 let mut message = client_blind_result.message;
851 unsafe { ptr::drop_in_place(&mut message) };
852 assert!(message.serialize().iter().all(|&x| x == 0));
853 }
854
855 fn zeroize_verifiable_server<CS: CipherSuite>() {
856 let input = b"input";
857 let info = b"info";
858 let mut rng = SysRng;
859 let server = PoprfServer::<CS>::new(&mut rng).unwrap();
860 let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
861 let server_result = server
862 .blind_evaluate(&mut rng, &client_blind_result.message, Some(info))
863 .unwrap();
864
865 let mut state = server;
866 unsafe { ptr::drop_in_place(&mut state) };
867 assert!(state.serialize().iter().all(|&x| x == 0));
868
869 let mut message = server_result.message;
870 unsafe { ptr::drop_in_place(&mut message) };
871 assert!(message.serialize().iter().all(|&x| x == 0));
872
873 let mut proof = server_result.proof;
874 unsafe { ptr::drop_in_place(&mut proof) };
875 assert!(proof.serialize().iter().all(|&x| x == 0));
876 }
877
878 #[test]
879 fn test_functionality() -> Result<()> {
880 use p256::NistP256;
881 use p384::NistP384;
882 use p521::NistP521;
883
884 #[cfg(feature = "ristretto255")]
885 {
886 use crate::Ristretto255;
887
888 verifiable_retrieval::<Ristretto255>();
889 verifiable_bad_public_key::<Ristretto255>();
890 verifiable_server_evaluate::<Ristretto255>();
891
892 zeroize_verifiable_client::<Ristretto255>();
893 zeroize_verifiable_server::<Ristretto255>();
894 }
895
896 verifiable_retrieval::<NistP256>();
897 verifiable_bad_public_key::<NistP256>();
898 verifiable_server_evaluate::<NistP256>();
899
900 zeroize_verifiable_client::<NistP256>();
901 zeroize_verifiable_server::<NistP256>();
902
903 verifiable_retrieval::<NistP384>();
904 verifiable_bad_public_key::<NistP384>();
905 verifiable_server_evaluate::<NistP384>();
906
907 zeroize_verifiable_client::<NistP384>();
908 zeroize_verifiable_server::<NistP384>();
909
910 verifiable_retrieval::<NistP521>();
911 verifiable_bad_public_key::<NistP521>();
912 verifiable_server_evaluate::<NistP521>();
913
914 zeroize_verifiable_client::<NistP521>();
915 zeroize_verifiable_server::<NistP521>();
916
917 Ok(())
918 }
919}