Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Aggregator::is_agg_param_valid method #1139

Merged
merged 3 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/vdaf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,11 @@ pub trait Aggregator<const VERIFY_KEY_SIZE: usize, const NONCE_SIZE: usize>: Vda
agg_param: &Self::AggregationParam,
output_shares: M,
) -> Result<Self::AggregateShare, VdafError>;

/// Validates an aggregation parameter with respect to all previous aggregaiton parameters used
/// for the same input share. `prev` MUST be sorted from least to most recently used.
#[must_use]
fn is_agg_param_valid(cur: &Self::AggregationParam, prev: &[Self::AggregationParam]) -> bool;
}

/// Aggregator that implements differential privacy with Aggregator-side noise addition.
Expand Down
4 changes: 4 additions & 0 deletions src/vdaf/dummy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,10 @@ impl vdaf::Aggregator<0, 16> for Vdaf {
}
Ok(aggregate_share)
}

fn is_agg_param_valid(_cur: &Self::AggregationParam, _prev: &[Self::AggregationParam]) -> bool {
true
}
}

impl vdaf::Client<16> for Vdaf {
Expand Down
85 changes: 85 additions & 0 deletions src/vdaf/poplar1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use crate::{
use bitvec::{prelude::Lsb0, vec::BitVec};
use rand_core::RngCore;
use std::{
collections::BTreeSet,
convert::TryFrom,
fmt::Debug,
io::{Cursor, Read},
Expand Down Expand Up @@ -1245,6 +1246,39 @@ impl<P: Xof<SEED_SIZE>, const SEED_SIZE: usize> Aggregator<SEED_SIZE, 16>
output_shares,
)
}

/// Validates that no aggregation parameter with the same level as `cur` has been used with the
/// same input share before. `prev` contains the aggregation parameters used for the same input.
/// `prev` MUST be sorted from least to most recently used.
fn is_agg_param_valid(cur: &Poplar1AggregationParam, prev: &[Poplar1AggregationParam]) -> bool {
// Exit early if there are no previous aggregation params to compare to, i.e., this is the
// first time the input share has been processed
if prev.is_empty() {
return true;
}

// Unpack this agg param and the last one in the list
let Poplar1AggregationParam {
level: cur_level,
prefixes: cur_prefixes,
} = cur;
let Poplar1AggregationParam {
level: last_level,
prefixes: last_prefixes,
} = prev.last().as_ref().unwrap();
let last_prefixes_set = BTreeSet::from_iter(last_prefixes);

// Check that the level increased.
if cur_level <= last_level {
return false;
}

// Check that current prefixes are extensions of the last level's prefixes.
cur_prefixes.iter().all(|cur_prefix| {
let last_prefix = cur_prefix.prefix(*last_level as usize);
last_prefixes_set.contains(&last_prefix)
})
}
}

impl<P: Xof<SEED_SIZE>, const SEED_SIZE: usize> Collector for Poplar1<P, SEED_SIZE> {
Expand Down Expand Up @@ -1979,6 +2013,57 @@ mod tests {
assert_matches!(err, CodecError::Other(_));
}

// Tests Poplar1::is_valid() functionality. This unit test is translated from
// https://github.com/cfrg/draft-irtf-cfrg-vdaf/blob/a4874547794818573acd8734874c9784043b1140/poc/tests/test_vdaf_poplar1.py#L187
#[test]
fn agg_param_validity() {
// The actual Poplar instance doesn't matter for the parameter validity tests
type V = Poplar1<XofTurboShake128, 16>;

// Helper function for making aggregation params
fn make_agg_param(bitstrings: &[&[u8]]) -> Result<Poplar1AggregationParam, VdafError> {
Poplar1AggregationParam::try_from_prefixes(
bitstrings
.iter()
.map(|v| {
let bools = v.iter().map(|&b| b != 0).collect::<Vec<_>>();
IdpfInput::from_bools(&bools)
})
.collect(),
)
}

// Test `is_valid` returns False on repeated levels, and True otherwise.
let agg_params = [
make_agg_param(&[&[0], &[1]]).unwrap(),
make_agg_param(&[&[0, 0]]).unwrap(),
make_agg_param(&[&[0, 0], &[1, 0]]).unwrap(),
];
assert!(V::is_agg_param_valid(&agg_params[0], &[]));
assert!(V::is_agg_param_valid(&agg_params[1], &agg_params[..1]));
assert!(!V::is_agg_param_valid(&agg_params[2], &agg_params[..2]));

// Test `is_valid` accepts level jumps.
let agg_params = [
make_agg_param(&[&[0], &[1]]).unwrap(),
make_agg_param(&[&[0, 1, 0], &[0, 1, 1], &[1, 0, 1], &[1, 1, 1]]).unwrap(),
];
assert!(V::is_agg_param_valid(&agg_params[1], &agg_params[..1]));

// Test `is_valid` rejects unconnected prefixes.
let agg_params = [
make_agg_param(&[&[0]]).unwrap(),
make_agg_param(&[&[0, 1, 0], &[0, 1, 1], &[1, 0, 1], &[1, 1, 1]]).unwrap(),
];
assert!(!V::is_agg_param_valid(&agg_params[1], &agg_params[..1]));

// Test that the `Poplar1AggregationParam` constructor rejects unsorted and duplicate
// prefixes.
assert!(make_agg_param(&[&[1], &[0]]).is_err());
assert!(make_agg_param(&[&[1, 0, 0], &[0, 1, 1]]).is_err());
assert!(make_agg_param(&[&[0, 0, 0], &[0, 1, 0], &[0, 1, 0]]).is_err());
}

#[derive(Debug, Deserialize)]
struct HexEncoded(#[serde(with = "hex")] Vec<u8>);

Expand Down
5 changes: 5 additions & 0 deletions src/vdaf/prio2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,11 @@ impl Aggregator<32, 16> for Prio2 {

Ok(agg_share)
}

/// Returns `true` iff `prev.is_empty()`
fn is_agg_param_valid(_cur: &Self::AggregationParam, prev: &[Self::AggregationParam]) -> bool {
prev.is_empty()
}
}

impl Collector for Prio2 {
Expand Down
5 changes: 5 additions & 0 deletions src/vdaf/prio3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1441,6 +1441,11 @@ where

Ok(agg_share)
}

/// Returns `true` iff `prev.is_empty()`
fn is_agg_param_valid(_cur: &Self::AggregationParam, prev: &[Self::AggregationParam]) -> bool {
prev.is_empty()
}
}

#[cfg(feature = "experimental")]
Expand Down