1#[cfg(feature = "experimental")]
9use crate::dp::DifferentialPrivacyStrategy;
10#[cfg(all(feature = "crypto-dependencies", feature = "experimental"))]
11use crate::flp::szk::SzkError;
12#[cfg(all(feature = "crypto-dependencies", feature = "experimental"))]
13use crate::idpf::IdpfError;
14#[cfg(all(feature = "crypto-dependencies", feature = "experimental"))]
15use crate::vidpf::VidpfError;
16use crate::{
17 codec::{CodecError, Decode, Encode, ParameterizedDecode},
18 field::{encode_fieldvec, merge_vector, FieldElement, FieldError},
19 flp::FlpError,
20 prng::PrngError,
21 vdaf::xof::Seed,
22};
23use serde::{Deserialize, Serialize};
24use std::{error::Error, fmt::Debug, io::Cursor};
25use subtle::{Choice, ConstantTimeEq};
26
27pub(crate) const VERSION: u8 = 8;
30
31#[derive(Debug, thiserror::Error)]
33#[non_exhaustive]
34pub enum VdafError {
35 #[error("vdaf error: {0}")]
37 Uncategorized(String),
38
39 #[error("field error: {0}")]
41 Field(#[from] FieldError),
42
43 #[error("io error: {0}")]
45 IoError(#[from] std::io::Error),
46
47 #[error("flp error: {0}")]
49 Flp(#[from] FlpError),
50
51 #[cfg(all(feature = "crypto-dependencies", feature = "experimental"))]
53 #[error("Szk error: {0}")]
54 Szk(#[from] SzkError),
55
56 #[error("prng error: {0}")]
58 Prng(#[from] PrngError),
59
60 #[error("getrandom: {0}")]
62 GetRandom(#[from] getrandom::Error),
63
64 #[cfg(all(feature = "crypto-dependencies", feature = "experimental"))]
66 #[error("idpf error: {0}")]
67 Idpf(#[from] IdpfError),
68
69 #[cfg(all(feature = "crypto-dependencies", feature = "experimental"))]
71 #[error("vidpf error: {0}")]
72 Vidpf(#[from] VidpfError),
73
74 #[error(transparent)]
76 Other(Box<dyn Error + 'static + Send + Sync>),
77}
78
79#[derive(Clone, Debug)]
81pub enum Share<F, const SEED_SIZE: usize> {
82 Leader(Vec<F>),
84
85 Helper(Seed<SEED_SIZE>),
87}
88
89impl<F: ConstantTimeEq, const SEED_SIZE: usize> PartialEq for Share<F, SEED_SIZE> {
90 fn eq(&self, other: &Self) -> bool {
91 self.ct_eq(other).into()
92 }
93}
94
95impl<F: ConstantTimeEq, const SEED_SIZE: usize> Eq for Share<F, SEED_SIZE> {}
96
97impl<F: ConstantTimeEq, const SEED_SIZE: usize> ConstantTimeEq for Share<F, SEED_SIZE> {
98 fn ct_eq(&self, other: &Self) -> subtle::Choice {
99 match (self, other) {
102 (Share::Leader(self_val), Share::Leader(other_val)) => self_val.ct_eq(other_val),
103 (Share::Helper(self_val), Share::Helper(other_val)) => self_val.ct_eq(other_val),
104 _ => Choice::from(0),
105 }
106 }
107}
108
109#[derive(Clone, Debug, PartialEq, Eq)]
111pub(crate) enum ShareDecodingParameter<const SEED_SIZE: usize> {
112 Leader(usize),
113 Helper,
114}
115
116impl<F: FieldElement, const SEED_SIZE: usize> ParameterizedDecode<ShareDecodingParameter<SEED_SIZE>>
117 for Share<F, SEED_SIZE>
118{
119 fn decode_with_param(
120 decoding_parameter: &ShareDecodingParameter<SEED_SIZE>,
121 bytes: &mut Cursor<&[u8]>,
122 ) -> Result<Self, CodecError> {
123 match decoding_parameter {
124 ShareDecodingParameter::Leader(share_length) => {
125 let mut data = Vec::with_capacity(*share_length);
126 for _ in 0..*share_length {
127 data.push(F::decode(bytes)?)
128 }
129 Ok(Self::Leader(data))
130 }
131 ShareDecodingParameter::Helper => {
132 let seed = Seed::decode(bytes)?;
133 Ok(Self::Helper(seed))
134 }
135 }
136 }
137}
138
139impl<F: FieldElement, const SEED_SIZE: usize> Encode for Share<F, SEED_SIZE> {
140 fn encode(&self, bytes: &mut Vec<u8>) -> Result<(), CodecError> {
141 match self {
142 Share::Leader(share_data) => {
143 for x in share_data {
144 x.encode(bytes)?;
145 }
146 Ok(())
147 }
148 Share::Helper(share_seed) => share_seed.encode(bytes),
149 }
150 }
151
152 fn encoded_len(&self) -> Option<usize> {
153 match self {
154 Share::Leader(share_data) => {
155 Some(share_data.len() * F::ENCODED_SIZE)
157 }
158 Share::Helper(share_seed) => share_seed.encoded_len(),
159 }
160 }
161}
162
163pub trait Vdaf: Clone + Debug {
167 type Measurement: Clone + Debug;
169
170 type AggregateResult: Clone + Debug;
172
173 type AggregationParam: Clone + Debug + Decode + Encode;
176
177 type PublicShare: Clone + Debug + ParameterizedDecode<Self> + Encode;
179
180 type InputShare: Clone + Debug + for<'a> ParameterizedDecode<(&'a Self, usize)> + Encode;
182
183 type OutputShare: Clone
185 + Debug
186 + for<'a> ParameterizedDecode<(&'a Self, &'a Self::AggregationParam)>
187 + Encode;
188
189 type AggregateShare: Aggregatable<OutputShare = Self::OutputShare>
191 + for<'a> ParameterizedDecode<(&'a Self, &'a Self::AggregationParam)>
192 + Encode;
193
194 fn algorithm_id(&self) -> u32;
196
197 fn num_aggregators(&self) -> usize;
200
201 fn domain_separation_tag(&self, usage: u16) -> [u8; 8] {
204 let mut dst = [0_u8; 8];
205 dst[0] = VERSION;
206 dst[1] = 0; dst[2..6].copy_from_slice(&(self.algorithm_id()).to_be_bytes());
208 dst[6..8].copy_from_slice(&usage.to_be_bytes());
209 dst
210 }
211}
212
213pub trait Client<const NONCE_SIZE: usize>: Vdaf {
215 fn shard(
222 &self,
223 measurement: &Self::Measurement,
224 nonce: &[u8; NONCE_SIZE],
225 ) -> Result<(Self::PublicShare, Vec<Self::InputShare>), VdafError>;
226}
227
228pub trait Aggregator<const VERIFY_KEY_SIZE: usize, const NONCE_SIZE: usize>: Vdaf {
230 type PrepareState: Clone + Debug + PartialEq + Eq;
232
233 type PrepareShare: Clone + Debug + ParameterizedDecode<Self::PrepareState> + Encode;
238
239 type PrepareMessage: Clone
245 + Debug
246 + PartialEq
247 + Eq
248 + ParameterizedDecode<Self::PrepareState>
249 + Encode;
250
251 fn prepare_init(
258 &self,
259 verify_key: &[u8; VERIFY_KEY_SIZE],
260 agg_id: usize,
261 agg_param: &Self::AggregationParam,
262 nonce: &[u8; NONCE_SIZE],
263 public_share: &Self::PublicShare,
264 input_share: &Self::InputShare,
265 ) -> Result<(Self::PrepareState, Self::PrepareShare), VdafError>;
266
267 fn prepare_shares_to_prepare_message<M: IntoIterator<Item = Self::PrepareShare>>(
273 &self,
274 agg_param: &Self::AggregationParam,
275 inputs: M,
276 ) -> Result<Self::PrepareMessage, VdafError>;
277
278 fn prepare_next(
290 &self,
291 state: Self::PrepareState,
292 input: Self::PrepareMessage,
293 ) -> Result<PrepareTransition<Self, VERIFY_KEY_SIZE, NONCE_SIZE>, VdafError>;
294
295 fn aggregate<M: IntoIterator<Item = Self::OutputShare>>(
297 &self,
298 agg_param: &Self::AggregationParam,
299 output_shares: M,
300 ) -> Result<Self::AggregateShare, VdafError>;
301}
302
303#[cfg(feature = "experimental")]
305#[cfg_attr(docsrs, doc(cfg(feature = "experimental")))]
306pub trait AggregatorWithNoise<
307 const VERIFY_KEY_SIZE: usize,
308 const NONCE_SIZE: usize,
309 DPStrategy: DifferentialPrivacyStrategy,
310>: Aggregator<VERIFY_KEY_SIZE, NONCE_SIZE>
311{
312 fn add_noise_to_agg_share(
315 &self,
316 dp_strategy: &DPStrategy,
317 agg_param: &Self::AggregationParam,
318 agg_share: &mut Self::AggregateShare,
319 num_measurements: usize,
320 ) -> Result<(), VdafError>;
321}
322
323pub trait Collector: Vdaf {
325 fn unshard<M: IntoIterator<Item = Self::AggregateShare>>(
327 &self,
328 agg_param: &Self::AggregationParam,
329 agg_shares: M,
330 num_measurements: usize,
331 ) -> Result<Self::AggregateResult, VdafError>;
332}
333
334#[derive(Clone, Debug)]
336pub enum PrepareTransition<
337 V: Aggregator<VERIFY_KEY_SIZE, NONCE_SIZE>,
338 const VERIFY_KEY_SIZE: usize,
339 const NONCE_SIZE: usize,
340> {
341 Continue(V::PrepareState, V::PrepareShare),
343
344 Finish(V::OutputShare),
346}
347
348pub trait Aggregatable: Clone + Debug + From<Self::OutputShare> {
351 type OutputShare;
353
354 fn merge(&mut self, agg_share: &Self) -> Result<(), VdafError>;
356
357 fn accumulate(&mut self, output_share: &Self::OutputShare) -> Result<(), VdafError>;
359}
360
361#[derive(Clone)]
363pub struct OutputShare<F>(Vec<F>);
364
365impl<F: ConstantTimeEq> PartialEq for OutputShare<F> {
366 fn eq(&self, other: &Self) -> bool {
367 self.ct_eq(other).into()
368 }
369}
370
371impl<F: ConstantTimeEq> Eq for OutputShare<F> {}
372
373impl<F: ConstantTimeEq> ConstantTimeEq for OutputShare<F> {
374 fn ct_eq(&self, other: &Self) -> Choice {
375 self.0.ct_eq(&other.0)
376 }
377}
378
379impl<F> AsRef<[F]> for OutputShare<F> {
380 fn as_ref(&self) -> &[F] {
381 &self.0
382 }
383}
384
385impl<F> From<Vec<F>> for OutputShare<F> {
386 fn from(other: Vec<F>) -> Self {
387 Self(other)
388 }
389}
390
391impl<F: FieldElement> Encode for OutputShare<F> {
392 fn encode(&self, bytes: &mut Vec<u8>) -> Result<(), CodecError> {
393 encode_fieldvec(&self.0, bytes)
394 }
395
396 fn encoded_len(&self) -> Option<usize> {
397 Some(F::ENCODED_SIZE * self.0.len())
398 }
399}
400
401impl<F> Debug for OutputShare<F> {
402 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
403 f.debug_tuple("OutputShare").finish()
404 }
405}
406
407#[derive(Clone, Debug, Serialize, Deserialize)]
412
413pub struct AggregateShare<F>(Vec<F>);
414
415impl<F> From<Vec<F>> for AggregateShare<F> {
416 fn from(other: Vec<F>) -> Self {
417 Self(other)
418 }
419}
420
421impl<F: ConstantTimeEq> PartialEq for AggregateShare<F> {
422 fn eq(&self, other: &Self) -> bool {
423 self.ct_eq(other).into()
424 }
425}
426
427impl<F: ConstantTimeEq> Eq for AggregateShare<F> {}
428
429impl<F: ConstantTimeEq> ConstantTimeEq for AggregateShare<F> {
430 fn ct_eq(&self, other: &Self) -> subtle::Choice {
431 self.0.ct_eq(&other.0)
432 }
433}
434
435impl<F: FieldElement> AsRef<[F]> for AggregateShare<F> {
436 fn as_ref(&self) -> &[F] {
437 &self.0
438 }
439}
440
441impl<F> From<OutputShare<F>> for AggregateShare<F> {
442 fn from(other: OutputShare<F>) -> Self {
443 Self(other.0)
444 }
445}
446
447impl<F: FieldElement> Aggregatable for AggregateShare<F> {
448 type OutputShare = OutputShare<F>;
449
450 fn merge(&mut self, agg_share: &Self) -> Result<(), VdafError> {
451 self.sum(agg_share.as_ref())
452 }
453
454 fn accumulate(&mut self, output_share: &Self::OutputShare) -> Result<(), VdafError> {
455 self.sum(output_share.as_ref())
458 }
459}
460
461impl<F: FieldElement> AggregateShare<F> {
462 fn sum(&mut self, other: &[F]) -> Result<(), VdafError> {
463 merge_vector(&mut self.0, other).map_err(Into::into)
464 }
465}
466
467impl<F: FieldElement> Encode for AggregateShare<F> {
468 fn encode(&self, bytes: &mut Vec<u8>) -> Result<(), CodecError> {
469 encode_fieldvec(&self.0, bytes)
470 }
471
472 fn encoded_len(&self) -> Option<usize> {
473 Some(F::ENCODED_SIZE * self.0.len())
474 }
475}
476
477#[cfg(feature = "test-util")]
479#[cfg_attr(docsrs, doc(cfg(feature = "test-util")))]
480pub mod test_utils {
481 use super::{Aggregatable, Aggregator, Client, Collector, PrepareTransition, VdafError};
482 use crate::codec::{Encode, ParameterizedDecode};
483 use rand::prelude::*;
484
485 pub fn run_vdaf<V, M, const SEED_SIZE: usize>(
487 vdaf: &V,
488 agg_param: &V::AggregationParam,
489 measurements: M,
490 ) -> Result<V::AggregateResult, VdafError>
491 where
492 V: Client<16> + Aggregator<SEED_SIZE, 16> + Collector,
493 M: IntoIterator<Item = V::Measurement>,
494 {
495 let mut sharded_measurements = Vec::new();
496 for measurement in measurements.into_iter() {
497 let nonce = random();
498 let (public_share, input_shares) = vdaf.shard(&measurement, &nonce)?;
499
500 sharded_measurements.push((public_share, nonce, input_shares));
501 }
502
503 run_vdaf_sharded(vdaf, agg_param, sharded_measurements)
504 }
505
506 pub fn run_vdaf_sharded<V, M, I, const SEED_SIZE: usize>(
508 vdaf: &V,
509 agg_param: &V::AggregationParam,
510 sharded_measurements: M,
511 ) -> Result<V::AggregateResult, VdafError>
512 where
513 V: Client<16> + Aggregator<SEED_SIZE, 16> + Collector,
514 M: IntoIterator<Item = (V::PublicShare, [u8; 16], I)>,
515 I: IntoIterator<Item = V::InputShare>,
516 {
517 let mut rng = thread_rng();
518 let mut verify_key = [0; SEED_SIZE];
519 rng.fill(&mut verify_key[..]);
520
521 let mut agg_shares: Vec<Option<V::AggregateShare>> = vec![None; vdaf.num_aggregators()];
522 let mut num_measurements: usize = 0;
523 for (public_share, nonce, input_shares) in sharded_measurements.into_iter() {
524 num_measurements += 1;
525 let out_shares = run_vdaf_prepare(
526 vdaf,
527 &verify_key,
528 agg_param,
529 &nonce,
530 public_share,
531 input_shares,
532 )?;
533 for (out_share, agg_share) in out_shares.into_iter().zip(agg_shares.iter_mut()) {
534 let encoded_out_share = out_share.get_encoded().unwrap();
536 let round_trip_out_share =
537 V::OutputShare::get_decoded_with_param(&(vdaf, agg_param), &encoded_out_share)
538 .unwrap();
539 assert_eq!(
540 round_trip_out_share.get_encoded().unwrap(),
541 encoded_out_share
542 );
543
544 let this_agg_share = V::AggregateShare::from(out_share);
545 if let Some(ref mut inner) = agg_share {
546 inner.merge(&this_agg_share)?;
547 } else {
548 *agg_share = Some(this_agg_share);
549 }
550 }
551 }
552
553 for agg_share in agg_shares.iter() {
554 let encoded_agg_share = agg_share.as_ref().unwrap().get_encoded().unwrap();
556 let round_trip_agg_share =
557 V::AggregateShare::get_decoded_with_param(&(vdaf, agg_param), &encoded_agg_share)
558 .unwrap();
559 assert_eq!(
560 round_trip_agg_share.get_encoded().unwrap(),
561 encoded_agg_share
562 );
563 }
564
565 let res = vdaf.unshard(
566 agg_param,
567 agg_shares.into_iter().map(|option| option.unwrap()),
568 num_measurements,
569 )?;
570 Ok(res)
571 }
572
573 pub fn run_vdaf_prepare<V, M, const SEED_SIZE: usize>(
575 vdaf: &V,
576 verify_key: &[u8; SEED_SIZE],
577 agg_param: &V::AggregationParam,
578 nonce: &[u8; 16],
579 public_share: V::PublicShare,
580 input_shares: M,
581 ) -> Result<Vec<V::OutputShare>, VdafError>
582 where
583 V: Client<16> + Aggregator<SEED_SIZE, 16> + Collector,
584 M: IntoIterator<Item = V::InputShare>,
585 {
586 let public_share =
587 V::PublicShare::get_decoded_with_param(vdaf, &public_share.get_encoded().unwrap())
588 .unwrap();
589 let input_shares = input_shares
590 .into_iter()
591 .map(|input_share| input_share.get_encoded().unwrap());
592
593 let mut states = Vec::new();
594 let mut outbound = Vec::new();
595 for (agg_id, input_share) in input_shares.enumerate() {
596 let (state, msg) = vdaf.prepare_init(
597 verify_key,
598 agg_id,
599 agg_param,
600 nonce,
601 &public_share,
602 &V::InputShare::get_decoded_with_param(&(vdaf, agg_id), &input_share)
603 .expect("failed to decode input share"),
604 )?;
605 states.push(state);
606 outbound.push(msg.get_encoded().unwrap());
607 }
608
609 let mut inbound = vdaf
610 .prepare_shares_to_prepare_message(
611 agg_param,
612 outbound.iter().map(|encoded| {
613 V::PrepareShare::get_decoded_with_param(&states[0], encoded)
614 .expect("failed to decode prep share")
615 }),
616 )?
617 .get_encoded()
618 .unwrap();
619
620 let mut out_shares = Vec::new();
621 loop {
622 let mut outbound = Vec::new();
623 for state in states.iter_mut() {
624 match vdaf.prepare_next(
625 state.clone(),
626 V::PrepareMessage::get_decoded_with_param(state, &inbound)
627 .expect("failed to decode prep message"),
628 )? {
629 PrepareTransition::Continue(new_state, msg) => {
630 outbound.push(msg.get_encoded().unwrap());
631 *state = new_state
632 }
633 PrepareTransition::Finish(out_share) => {
634 out_shares.push(out_share);
635 }
636 }
637 }
638
639 if outbound.len() == vdaf.num_aggregators() {
640 inbound = vdaf
642 .prepare_shares_to_prepare_message(
643 agg_param,
644 outbound.iter().map(|encoded| {
645 V::PrepareShare::get_decoded_with_param(&states[0], encoded)
646 .expect("failed to decode prep share")
647 }),
648 )?
649 .get_encoded()
650 .unwrap();
651 } else if outbound.is_empty() {
652 break;
654 } else {
655 panic!("Aggregators did not finish the prepare phase at the same time");
656 }
657 }
658
659 Ok(out_shares)
660 }
661}
662
663#[cfg(test)]
664fn fieldvec_roundtrip_test<F, V, T>(vdaf: &V, agg_param: &V::AggregationParam, length: usize)
665where
666 F: FieldElement,
667 V: Vdaf,
668 T: Encode,
669 for<'a> T: ParameterizedDecode<(&'a V, &'a V::AggregationParam)>,
670{
671 let vec: Vec<F> = crate::field::random_vector(length).unwrap();
673
674 let mut bytes = Vec::with_capacity(vec.len() * F::ENCODED_SIZE);
676 encode_fieldvec(&vec, &mut bytes).unwrap();
677
678 let value = T::get_decoded_with_param(&(vdaf, agg_param), &bytes).unwrap();
680
681 let encoded = value.get_encoded().unwrap();
683
684 assert_eq!(encoded, bytes);
685}
686
687#[cfg(test)]
688fn equality_comparison_test<T>(values: &[T])
689where
690 T: Debug + PartialEq,
691{
692 use std::ptr;
693
694 for (i, i_val) in values.iter().enumerate() {
698 for (j, j_val) in values.iter().enumerate() {
699 if i == j {
700 assert!(ptr::eq(i_val, j_val)); assert_eq!(
702 i_val, j_val,
703 "Expected element at index {i} to be equal to itself, but it was not"
704 );
705 } else {
706 assert_ne!(
707 i_val, j_val,
708 "Expected elements at indices {i} & {j} to not be equal, but they were"
709 )
710 }
711 }
712 }
713}
714
715#[cfg(test)]
716mod tests {
717 use crate::vdaf::{equality_comparison_test, xof::Seed, AggregateShare, OutputShare, Share};
718
719 #[test]
720 fn share_equality_test() {
721 equality_comparison_test(&[
722 Share::Leader(Vec::from([1, 2, 3])),
723 Share::Leader(Vec::from([3, 2, 1])),
724 Share::Helper(Seed([1, 2, 3])),
725 Share::Helper(Seed([3, 2, 1])),
726 ])
727 }
728
729 #[test]
730 fn output_share_equality_test() {
731 equality_comparison_test(&[
732 OutputShare(Vec::from([1, 2, 3])),
733 OutputShare(Vec::from([3, 2, 1])),
734 ])
735 }
736
737 #[test]
738 fn aggregate_share_equality_test() {
739 equality_comparison_test(&[
740 AggregateShare(Vec::from([1, 2, 3])),
741 AggregateShare(Vec::from([3, 2, 1])),
742 ])
743 }
744}
745
746#[cfg(feature = "test-util")]
747#[cfg_attr(docsrs, doc(cfg(feature = "test-util")))]
748pub mod dummy;
749#[cfg(all(feature = "crypto-dependencies", feature = "experimental"))]
750pub mod mastic;
751#[cfg(all(feature = "crypto-dependencies", feature = "experimental"))]
752#[cfg_attr(
753 docsrs,
754 doc(cfg(all(feature = "crypto-dependencies", feature = "experimental")))
755)]
756pub mod poplar1;
757#[cfg(all(feature = "crypto-dependencies", feature = "experimental"))]
758#[cfg_attr(
759 docsrs,
760 doc(cfg(all(feature = "crypto-dependencies", feature = "experimental")))
761)]
762pub mod prio2;
763pub mod prio3;
764#[cfg(any(test, feature = "test-util"))]
765#[cfg_attr(docsrs, doc(cfg(feature = "test-util")))]
766pub mod prio3_test;
767pub mod xof;