From c14dd95aaec131b821b9a76f72d620b46ccf6c5c Mon Sep 17 00:00:00 2001 From: Christopher Patton Date: Mon, 16 Dec 2024 11:11:45 -0800 Subject: [PATCH] Align XofTurboShake128 with VDAF-13 * Bump seed size for XofTurboShake128 to 32 bytes * Allow variable length seeds for compatibility with IDPF. * Make the dst length prefix 2 bytes --- benches/cycle_counts.rs | 10 +++--- benches/speed_tests.rs | 8 ++--- src/dp/distributions.rs | 20 ++++++------ src/flp/szk.rs | 58 +++++++++++++++++----------------- src/flp/types/dp.rs | 50 ++++++++++++++--------------- src/flp/types/fixedpoint_l2.rs | 16 ++++++---- src/idpf.rs | 4 +-- src/prng.rs | 11 ++++--- src/vdaf/poplar1.rs | 17 +++++++--- src/vdaf/prio3.rs | 34 ++++++++++---------- src/vdaf/prio3_test.rs | 4 +-- src/vdaf/xof.rs | 34 +++++++++++++------- src/vidpf.rs | 5 ++- tests/discrete_gauss.rs | 3 +- 14 files changed, 152 insertions(+), 122 deletions(-) diff --git a/benches/cycle_counts.rs b/benches/cycle_counts.rs index a4598bed1..99bc86ba6 100644 --- a/benches/cycle_counts.rs +++ b/benches/cycle_counts.rs @@ -105,7 +105,7 @@ fn prio2_shard_and_prepare_1000() -> Prio2PrepareShare { prio2_shard_and_prepare(1000) } -fn prio3_client_count() -> Vec> { +fn prio3_client_count() -> Vec> { let prio3 = Prio3::new_count(2).unwrap(); let measurement = true; let nonce = [0; 16]; @@ -115,7 +115,7 @@ fn prio3_client_count() -> Vec> { .1 } -fn prio3_client_histogram_10() -> Vec> { +fn prio3_client_histogram_10() -> Vec> { let prio3 = Prio3::new_histogram(2, 10, 3).unwrap(); let measurement = 9; let nonce = [0; 16]; @@ -125,7 +125,7 @@ fn prio3_client_histogram_10() -> Vec> { .1 } -fn prio3_client_sum_32() -> Vec> { +fn prio3_client_sum_32() -> Vec> { let bits = 16; let prio3 = Prio3::new_sum(2, (1 << bits) - 1).unwrap(); let measurement = 1337; @@ -136,7 +136,7 @@ fn prio3_client_sum_32() -> Vec> { .1 } -fn prio3_client_count_vec_1000() -> Vec> { +fn prio3_client_count_vec_1000() -> Vec> { let len = 1000; let prio3 = Prio3::new_sum_vec(2, 1, len, 31).unwrap(); let measurement = vec![0; len]; @@ -148,7 +148,7 @@ fn prio3_client_count_vec_1000() -> Vec> { } #[cfg(feature = "multithreaded")] -fn prio3_client_count_vec_multithreaded_1000() -> Vec> { +fn prio3_client_count_vec_multithreaded_1000() -> Vec> { let len = 1000; let prio3 = Prio3::new_sum_vec_multithreaded(2, 1, len, 31).unwrap(); let measurement = vec![0; len]; diff --git a/benches/speed_tests.rs b/benches/speed_tests.rs index 94dd5d183..cdce42133 100644 --- a/benches/speed_tests.rs +++ b/benches/speed_tests.rs @@ -179,7 +179,7 @@ fn prio3(c: &mut Criterion) { let vdaf = Prio3::new_count(num_shares).unwrap(); let measurement = black_box(true); let nonce = black_box([0u8; 16]); - let verify_key = black_box([0u8; 16]); + let verify_key = black_box([0u8; 32]); let (public_share, input_shares) = vdaf.shard(b"", &measurement, &nonce).unwrap(); b.iter(|| { vdaf.prepare_init( @@ -215,7 +215,7 @@ fn prio3(c: &mut Criterion) { let vdaf = Prio3::new_sum(num_shares, max_measurement).unwrap(); let measurement = max_measurement; let nonce = black_box([0u8; 16]); - let verify_key = black_box([0u8; 16]); + let verify_key = black_box([0u8; 32]); let (public_share, input_shares) = vdaf.shard(b"", &measurement, &nonce).unwrap(); b.iter(|| { vdaf.prepare_init( @@ -285,7 +285,7 @@ fn prio3(c: &mut Criterion) { .map(|i| i & 1) .collect::>(); let nonce = black_box([0u8; 16]); - let verify_key = black_box([0u8; 16]); + let verify_key = black_box([0u8; 32]); let (public_share, input_shares) = vdaf.shard(b"", &measurement, &nonce).unwrap(); b.iter(|| { vdaf.prepare_init( @@ -414,7 +414,7 @@ fn prio3(c: &mut Criterion) { let vdaf = Prio3::new_histogram(num_shares, *input_length, *chunk_length).unwrap(); let measurement = black_box(0); let nonce = black_box([0u8; 16]); - let verify_key = black_box([0u8; 16]); + let verify_key = black_box([0u8; 32]); let (public_share, input_shares) = vdaf.shard(b"", &measurement, &nonce).unwrap(); b.iter(|| { vdaf.prepare_init( diff --git a/src/dp/distributions.rs b/src/dp/distributions.rs index b963be22c..4f0c9906e 100644 --- a/src/dp/distributions.rs +++ b/src/dp/distributions.rs @@ -391,15 +391,15 @@ mod tests { DiscreteGaussian::new(Ratio::::from_integer(BigUint::from(5u8))).unwrap(); // check samples are consistent - let mut rng = SeedStreamTurboShake128::from_seed([0u8; 16]); + let mut rng = SeedStreamTurboShake128::from_seed([0u8; 32]); let samples: Vec = (0..10) .map(|_| i8::try_from(sampler.sample(&mut rng)).unwrap()) .collect(); let samples1: Vec = (0..10) .map(|_| i8::try_from(sampler.sample(&mut rng)).unwrap()) .collect(); - assert_eq!(samples, vec![0, -3, -2, 3, 2, -1, -5, 4, -7, -5]); - assert_eq!(samples1, vec![2, 7, -8, -3, 1, -3, -3, 6, -3, -1]); + assert_eq!(samples, [10, 7, 2, 1, -1, -2, -1, 3, -3, -1]); + assert_eq!(samples1, [3, 6, 3, -7, -8, -1, 2, -4, -11, -4]); } #[test] @@ -410,7 +410,7 @@ mod tests { // sample from a manually created distribution let sampler1 = DiscreteGaussian::new(Ratio::::from_integer(BigUint::from(4u8))).unwrap(); - let mut rng = SeedStreamTurboShake128::from_seed([0u8; 16]); + let mut rng = SeedStreamTurboShake128::from_seed([0u8; 32]); let samples1: Vec = (0..10) .map(|_| i8::try_from(sampler1.sample(&mut rng)).unwrap()) .collect(); @@ -422,7 +422,7 @@ mod tests { let sampler2 = zcdp .create_distribution(Ratio::::from_integer(1u8.into())) .unwrap(); - let mut rng2 = SeedStreamTurboShake128::from_seed([0u8; 16]); + let mut rng2 = SeedStreamTurboShake128::from_seed([0u8; 32]); let samples2: Vec = (0..10) .map(|_| i8::try_from(sampler2.sample(&mut rng2)).unwrap()) .collect(); @@ -570,7 +570,7 @@ mod tests { .unwrap(); // collect that number of samples - let mut rng = SeedStreamTurboShake128::from_seed([0u8; 16]); + let mut rng = SeedStreamTurboShake128::from_seed([1u8; 32]); let samples: Vec = (1..n_samples) .map(|_| { sample_discrete_gaussian(&Ratio::::from_integer(sigma.clone()), &mut rng) @@ -604,7 +604,7 @@ mod tests { #[test] fn empirical_test_gauss() { [100, 2000, 20000].iter().for_each(|p| { - let mut rng = SeedStreamTurboShake128::from_seed([0u8; 16]); + let mut rng = SeedStreamTurboShake128::from_seed([0u8; 32]); let sampler = || { sample_discrete_gaussian( &Ratio::::from_integer((*p).to_biguint().unwrap()), @@ -626,7 +626,7 @@ mod tests { #[test] fn empirical_test_bernoulli_mean() { [2u8, 5u8, 7u8, 9u8].iter().for_each(|p| { - let mut rng = SeedStreamTurboShake128::from_seed([0u8; 16]); + let mut rng = SeedStreamTurboShake128::from_seed([0u8; 32]); let sampler = || { if sample_bernoulli( &Ratio::::new(BigUint::one(), (*p).into()), @@ -650,7 +650,7 @@ mod tests { #[test] fn empirical_test_geometric_mean() { [2u8, 5u8, 7u8, 9u8].iter().for_each(|p| { - let mut rng = SeedStreamTurboShake128::from_seed([0u8; 16]); + let mut rng = SeedStreamTurboShake128::from_seed([0u8; 32]); let sampler = || { sample_geometric_exp( &Ratio::::new(BigUint::one(), (*p).into()), @@ -673,7 +673,7 @@ mod tests { #[test] fn empirical_test_laplace_mean() { [2u8, 5u8, 7u8, 9u8].iter().for_each(|p| { - let mut rng = SeedStreamTurboShake128::from_seed([0u8; 16]); + let mut rng = SeedStreamTurboShake128::from_seed([0u8; 32]); let sampler = || { sample_discrete_laplace( &Ratio::::new(BigUint::one(), (*p).into()), diff --git a/src/flp/szk.rs b/src/flp/szk.rs index 4531d3bf9..f8659d5e9 100644 --- a/src/flp/szk.rs +++ b/src/flp/szk.rs @@ -313,7 +313,7 @@ where phantom: PhantomData

, } -impl Szk { +impl Szk { /// Create an instance of [`Szk`] using [`XofTurboShake128`]. pub fn new_turboshake128(typ: T, algorithm_id: u32) -> Self { Szk::new(typ, algorithm_id) @@ -666,15 +666,15 @@ mod tests { fn generic_szk_test(typ: T, encoded_measurement: &[T::Field], valid: bool) { let mut nonce = [0u8; 16]; - let mut verify_key = [0u8; 16]; + let mut verify_key = [0u8; 32]; let algorithm_id = 5; let szk_typ = Szk::new_turboshake128(typ.clone(), algorithm_id); thread_rng().fill(&mut verify_key[..]); thread_rng().fill(&mut nonce[..]); - let prove_rand_seed = Seed::<16>::generate().unwrap(); - let helper_seed = Seed::<16>::generate().unwrap(); + let prove_rand_seed = Seed::generate().unwrap(); + let helper_seed = Seed::generate().unwrap(); let leader_seed_opt = if szk_typ.has_joint_rand() { - Some(Seed::<16>::generate().unwrap()) + Some(Seed::generate().unwrap()) } else { None }; @@ -726,7 +726,7 @@ mod tests { //test mutated jr seed if szk_typ.has_joint_rand() { - let joint_rand_seed_opt = Some(Seed::<16>::generate().unwrap()); + let joint_rand_seed_opt = Some(Seed::<32>::generate().unwrap()); if let Ok(leader_decision) = szk_typ.decide(verifier, joint_rand_seed_opt.clone()) { assert!(!leader_decision, "Leader accepted wrong jr seed"); }; @@ -800,9 +800,9 @@ mod tests { let encoded_measurement = sum.encode_measurement(&9).unwrap(); let algorithm_id = 5; let szk_typ = Szk::new_turboshake128(sum, algorithm_id); - let prove_rand_seed = Seed::<16>::generate().unwrap(); - let helper_seed = Seed::<16>::generate().unwrap(); - let leader_seed_opt = Some(Seed::<16>::generate().unwrap()); + let prove_rand_seed = Seed::generate().unwrap(); + let helper_seed = Seed::generate().unwrap(); + let leader_seed_opt = Some(Seed::generate().unwrap()); let helper_input_share = random_vector(szk_typ.typ.input_len()).unwrap(); let mut leader_input_share = encoded_measurement.clone().to_owned(); for (x, y) in leader_input_share.iter_mut().zip(&helper_input_share) { @@ -835,9 +835,9 @@ mod tests { let encoded_measurement = sumvec.encode_measurement(&vec![1, 16, 0]).unwrap(); let algorithm_id = 5; let szk_typ = Szk::new_turboshake128(sumvec, algorithm_id); - let prove_rand_seed = Seed::<16>::generate().unwrap(); - let helper_seed = Seed::<16>::generate().unwrap(); - let leader_seed_opt = Some(Seed::<16>::generate().unwrap()); + let prove_rand_seed = Seed::generate().unwrap(); + let helper_seed = Seed::generate().unwrap(); + let leader_seed_opt = Some(Seed::generate().unwrap()); let helper_input_share = random_vector(szk_typ.typ.input_len()).unwrap(); let mut leader_input_share = encoded_measurement.clone().to_owned(); for (x, y) in leader_input_share.iter_mut().zip(&helper_input_share) { @@ -869,9 +869,9 @@ mod tests { let encoded_measurement = count.encode_measurement(&true).unwrap(); let algorithm_id = 5; let szk_typ = Szk::new_turboshake128(count, algorithm_id); - let prove_rand_seed = Seed::<16>::generate().unwrap(); - let helper_seed = Seed::<16>::generate().unwrap(); - let leader_seed_opt = Some(Seed::<16>::generate().unwrap()); + let prove_rand_seed = Seed::generate().unwrap(); + let helper_seed = Seed::generate().unwrap(); + let leader_seed_opt = Some(Seed::generate().unwrap()); let helper_input_share = random_vector(szk_typ.typ.input_len()).unwrap(); let mut leader_input_share = encoded_measurement.clone().to_owned(); for (x, y) in leader_input_share.iter_mut().zip(&helper_input_share) { @@ -904,8 +904,8 @@ mod tests { let encoded_measurement = sum.encode_measurement(&9).unwrap(); let algorithm_id = 5; let szk_typ = Szk::new_turboshake128(sum, algorithm_id); - let prove_rand_seed = Seed::<16>::generate().unwrap(); - let helper_seed = Seed::<16>::generate().unwrap(); + let prove_rand_seed = Seed::generate().unwrap(); + let helper_seed = Seed::generate().unwrap(); let leader_seed_opt = None; let helper_input_share = random_vector(szk_typ.typ.input_len()).unwrap(); let mut leader_input_share = encoded_measurement.clone().to_owned(); @@ -945,8 +945,8 @@ mod tests { let encoded_measurement = sum.encode_measurement(&9).unwrap(); let algorithm_id = 5; let szk_typ = Szk::new_turboshake128(sum, algorithm_id); - let prove_rand_seed = Seed::<16>::generate().unwrap(); - let helper_seed = Seed::<16>::generate().unwrap(); + let prove_rand_seed = Seed::generate().unwrap(); + let helper_seed = Seed::generate().unwrap(); let leader_seed_opt = None; let helper_input_share = random_vector(szk_typ.typ.input_len()).unwrap(); let mut leader_input_share = encoded_measurement.clone().to_owned(); @@ -985,8 +985,8 @@ mod tests { let encoded_measurement = count.encode_measurement(&true).unwrap(); let algorithm_id = 5; let szk_typ = Szk::new_turboshake128(count, algorithm_id); - let prove_rand_seed = Seed::<16>::generate().unwrap(); - let helper_seed = Seed::<16>::generate().unwrap(); + let prove_rand_seed = Seed::generate().unwrap(); + let helper_seed = Seed::generate().unwrap(); let leader_seed_opt = None; let helper_input_share = random_vector(szk_typ.typ.input_len()).unwrap(); let mut leader_input_share = encoded_measurement.clone().to_owned(); @@ -1025,8 +1025,8 @@ mod tests { let encoded_measurement = count.encode_measurement(&true).unwrap(); let algorithm_id = 5; let szk_typ = Szk::new_turboshake128(count, algorithm_id); - let prove_rand_seed = Seed::<16>::generate().unwrap(); - let helper_seed = Seed::<16>::generate().unwrap(); + let prove_rand_seed = Seed::generate().unwrap(); + let helper_seed = Seed::generate().unwrap(); let leader_seed_opt = None; let helper_input_share = random_vector(szk_typ.typ.input_len()).unwrap(); let mut leader_input_share = encoded_measurement.clone().to_owned(); @@ -1066,9 +1066,9 @@ mod tests { let encoded_measurement = sumvec.encode_measurement(&vec![1, 16, 0]).unwrap(); let algorithm_id = 5; let szk_typ = Szk::new_turboshake128(sumvec, algorithm_id); - let prove_rand_seed = Seed::<16>::generate().unwrap(); - let helper_seed = Seed::<16>::generate().unwrap(); - let leader_seed_opt = Some(Seed::<16>::generate().unwrap()); + let prove_rand_seed = Seed::generate().unwrap(); + let helper_seed = Seed::generate().unwrap(); + let leader_seed_opt = Some(Seed::generate().unwrap()); let helper_input_share = random_vector(szk_typ.typ.input_len()).unwrap(); let mut leader_input_share = encoded_measurement.clone().to_owned(); for (x, y) in leader_input_share.iter_mut().zip(&helper_input_share) { @@ -1107,9 +1107,9 @@ mod tests { let encoded_measurement = sumvec.encode_measurement(&vec![1, 16, 0]).unwrap(); let algorithm_id = 5; let szk_typ = Szk::new_turboshake128(sumvec, algorithm_id); - let prove_rand_seed = Seed::<16>::generate().unwrap(); - let helper_seed = Seed::<16>::generate().unwrap(); - let leader_seed_opt = Some(Seed::<16>::generate().unwrap()); + let prove_rand_seed = Seed::<32>::generate().unwrap(); + let helper_seed = Seed::<32>::generate().unwrap(); + let leader_seed_opt = Some(Seed::<32>::generate().unwrap()); let helper_input_share = random_vector(szk_typ.typ.input_len()).unwrap(); let mut leader_input_share = encoded_measurement.clone().to_owned(); for (x, y) in leader_input_share.iter_mut().zip(&helper_input_share) { diff --git a/src/flp/types/dp.rs b/src/flp/types/dp.rs index 8d5fe00b2..fc092b662 100644 --- a/src/flp/types/dp.rs +++ b/src/flp/types/dp.rs @@ -219,7 +219,7 @@ mod tests { const SIZE: usize = 10; { - let mut rng = XofTurboShake128::init(&[0; 16], &[]).into_seed_stream(); + let mut rng = XofTurboShake128::init(&[0; 32], &[]).into_seed_stream(); let [mut share1, mut share2]: [Vec; 2] = split_vector(&[Field128::zero(); SIZE], 2) .unwrap() @@ -240,22 +240,22 @@ mod tests { assert_eq!( aggregate_result, [ - -Field128::from(7), + Field128::from(9), + Field128::from(5), + Field128::from(15), Field128::from(3), - -Field128::from(9), - -Field128::from(17), - -Field128::from(1), - -Field128::from(7), - -Field128::from(9), + Field128::from(5), Field128::from(0), - -Field128::from(6), - -Field128::from(4), + -Field128::from(3), + -Field128::from(30), + Field128::from(2), + -Field128::from(7), ] ); } { - let mut rng = XofTurboShake128::init(&[1; 16], &[]).into_seed_stream(); + let mut rng = XofTurboShake128::init(&[1; 32], &[]).into_seed_stream(); let [mut share1, mut share2]: [Vec; 2] = split_vector(&[Field128::zero(); SIZE], 2) .unwrap() @@ -276,16 +276,16 @@ mod tests { assert_eq!( aggregate_result, [ - Field128::from(81), - Field128::from(33), - -Field128::from(26), - Field128::from(19), - Field128::from(18), - -Field128::from(1), - -Field128::from(28), - Field128::from(31), - Field128::from(40), - Field128::from(38), + -Field128::from(36), + -Field128::from(8), + Field128::from(24), + Field128::from(32), + Field128::from(9), + -Field128::from(7), + -Field128::from(4), + Field128::from(9), + -Field128::from(8), + -Field128::from(14), ] ); } @@ -298,7 +298,7 @@ mod tests { ); const SIZE: usize = 10; - let mut rng = XofTurboShake128::init(&[2; 16], &[]).into_seed_stream(); + let mut rng = XofTurboShake128::init(&[2; 32], &[]).into_seed_stream(); let [mut share1, mut share2]: [Vec; 2] = split_vector(&[Field128::zero(); SIZE], 2) .unwrap() @@ -321,14 +321,14 @@ mod tests { [ Field128::from(2), Field128::from(1), - Field128::from(0), - -Field128::from(1), -Field128::from(1), + Field128::from(1), Field128::from(3), Field128::from(1), - -Field128::from(1), + Field128::from(0), + Field128::from(4), + Field128::from(3), -Field128::from(2), - Field128::from(1), ] ); } diff --git a/src/flp/types/fixedpoint_l2.rs b/src/flp/types/fixedpoint_l2.rs index f17559875..254db0d22 100644 --- a/src/flp/types/fixedpoint_l2.rs +++ b/src/flp/types/fixedpoint_l2.rs @@ -753,19 +753,23 @@ mod tests { vsum.add_noise( &strategy, &mut v, - &mut SeedStreamTurboShake128::from_seed([0u8; 16]), + &mut SeedStreamTurboShake128::from_seed([0u8; 32]), ) .unwrap(); assert_eq!( vsum.decode_result(&v, 1).unwrap(), match n { // sensitivity depends on encoding so the noise differs - 16 => vec![0.288970947265625, 0.168853759765625, 0.085662841796875], - 32 => vec![0.257810294162482, 0.10634658299386501, 0.10149003705009818], + 16 => vec![0.344757080078125, 0.02886962890625, 0.065277099609375], + 32 => vec![ + 0.3447443675249815, + 0.028876747004687786, + 0.06527946796268225 + ], 64 => vec![ - 0.37697368351762867, - -0.02388947667663828, - 0.19813152630930916 + 0.28790505349445616, + 0.11632535574718131, + 0.10735485151738995 ], _ => panic!("unsupported bitsize"), } diff --git a/src/idpf.rs b/src/idpf.rs index 0c205695d..e17cf93dc 100644 --- a/src/idpf.rs +++ b/src/idpf.rs @@ -251,7 +251,7 @@ fn extend(seed: &[u8; 16], xof_mode: &XofMode<'_>) -> ([[u8; 16]; 2], [Choice; 2 let mut dst = Vec::with_capacity(EXTEND_DOMAIN_SEP.len() + ctx.len()); dst.extend(EXTEND_DOMAIN_SEP); dst.extend(*ctx); - let mut xof = XofTurboShake128::init(seed, &dst); + let mut xof = XofTurboShake128::init_with_variable_length_seed(seed, &dst); xof.update(nonce); let mut seed_stream = xof.into_seed_stream(); seed_stream.fill_bytes(&mut seeds[0]); @@ -287,7 +287,7 @@ where let mut dst = Vec::with_capacity(CONVERT_DOMAIN_SEP.len() + ctx.len()); dst.extend(CONVERT_DOMAIN_SEP); dst.extend(*ctx); - let mut xof = XofTurboShake128::init(seed, &dst); + let mut xof = XofTurboShake128::init_with_variable_length_seed(seed, &dst); xof.update(nonce); let mut seed_stream = xof.into_seed_stream(); seed_stream.fill_bytes(&mut next_seed); diff --git a/src/prng.rs b/src/prng.rs index 2c3f5b664..d6009a00b 100644 --- a/src/prng.rs +++ b/src/prng.rs @@ -227,22 +227,23 @@ mod tests { // These constants were found in a brute-force search, and they test that the XOF performs // rejection sampling correctly when the raw output exceeds the prime modulus. let seed = Seed::get_decoded(&[ - 0xd5, 0x3f, 0xff, 0x5d, 0x88, 0x8c, 0x60, 0x4e, 0x9f, 0x24, 0x16, 0xe1, 0xa2, 0x0a, - 0x62, 0x34, + 0x44, 0x34, 0x1d, 0xc5, 0x2d, 0x71, 0xa2, 0xff, 0x2e, 0x4c, 0x30, 0x5e, 0x93, 0x35, + 0xda, 0x9b, 0x19, 0xaf, 0xc6, 0x8e, 0x10, 0xb8, 0xb5, 0x43, 0x69, 0x0d, 0xad, 0x9d, + 0x3b, 0xbb, 0x46, 0xba, ]) .unwrap(); - let expected = Field64::from(3401316594827516850); + let expected = Field64::from(4857131209231097247); let seed_stream = XofTurboShake128::seed_stream(&seed, b"", b""); let mut prng = Prng::::from_seed_stream(seed_stream); - let actual = prng.nth(662).unwrap(); + let actual = prng.nth(13882).unwrap(); assert_eq!(actual, expected); #[cfg(all(feature = "crypto-dependencies", feature = "experimental"))] { let mut seed_stream = XofTurboShake128::seed_stream(&seed, b"", b""); let mut actual = ::zero(); - for _ in 0..=662 { + for _ in 0..=13882 { actual = ::generate(&mut seed_stream, &()); } assert_eq!(actual, expected); diff --git a/src/vdaf/poplar1.rs b/src/vdaf/poplar1.rs index 98b8c9d29..7f4cefea4 100644 --- a/src/vdaf/poplar1.rs +++ b/src/vdaf/poplar1.rs @@ -46,7 +46,7 @@ impl Poplar1 { } } -impl Poplar1 { +impl Poplar1 { /// Create an instance of [`Poplar1`] using [`XofTurboShake128`]. The caller provides the bit length of /// each measurement (`BITS` as defined in [[draft-irtf-cfrg-vdaf-08]]). /// @@ -2044,7 +2044,7 @@ mod tests { #[test] fn agg_param_validity() { // The actual Poplar instance doesn't matter for the parameter validity tests - type V = Poplar1; + type V = Poplar1; // Helper function for making aggregation params fn make_agg_param(bitstrings: &[&[u8]]) -> Result { @@ -2147,12 +2147,21 @@ mod tests { let nonce = prep.nonce.as_ref().try_into().unwrap(); let mut idpf_random = [[0u8; 16]; 2]; - let mut poplar_random = [[0u8; 16]; 3]; for (input, output) in prep .rand .as_ref() .chunks_exact(16) - .zip(idpf_random.iter_mut().chain(poplar_random.iter_mut())) + .zip(idpf_random.iter_mut()) + { + output.copy_from_slice(input); + } + + let mut poplar_random = [[0u8; 32]; 3]; + for (input, output) in prep + .rand + .as_ref() + .chunks_exact(32) + .zip(poplar_random.iter_mut()) { output.copy_from_slice(input); } diff --git a/src/vdaf/prio3.rs b/src/vdaf/prio3.rs index eab2ec235..9d0d65b89 100644 --- a/src/vdaf/prio3.rs +++ b/src/vdaf/prio3.rs @@ -75,7 +75,7 @@ const DST_JOINT_RAND_SEED: u16 = 6; const DST_JOINT_RAND_PART: u16 = 7; /// The count type. Each measurement is an integer in `[0,2)` and the aggregate result is the sum. -pub type Prio3Count = Prio3, XofTurboShake128, 16>; +pub type Prio3Count = Prio3, XofTurboShake128, 32>; impl Prio3Count { /// Construct an instance of Prio3Count with the given number of aggregators. @@ -87,7 +87,7 @@ impl Prio3Count { /// The count-vector type. Each measurement is a vector of integers in `[0,2^bits)` and the /// aggregate is the element-wise sum. pub type Prio3SumVec = - Prio3>>, XofTurboShake128, 16>; + Prio3>>, XofTurboShake128, 32>; impl Prio3SumVec { /// Construct an instance of Prio3SumVec with the given number of aggregators. `bits` defines @@ -115,7 +115,7 @@ impl Prio3SumVec { pub type Prio3SumVecMultithreaded = Prio3< SumVec>>, XofTurboShake128, - 16, + 32, >; #[cfg(feature = "multithreaded")] @@ -140,7 +140,7 @@ impl Prio3SumVecMultithreaded { /// The sum type. Each measurement is an integer in `[0,2^bits)` for some `0 < bits < 64` and the /// aggregate is the sum. -pub type Prio3Sum = Prio3, XofTurboShake128, 16>; +pub type Prio3Sum = Prio3, XofTurboShake128, 32>; impl Prio3Sum { /// Construct an instance of `Prio3Sum` with the given number of aggregators, where each summand @@ -174,7 +174,7 @@ pub type Prio3FixedPointBoundedL2VecSum = Prio3< ParallelSum>, >, XofTurboShake128, - 16, + 32, >; #[cfg(feature = "experimental")] @@ -210,7 +210,7 @@ pub type Prio3FixedPointBoundedL2VecSumMultithreaded = Prio3< ParallelSumMultithreaded>, >, XofTurboShake128, - 16, + 32, >; #[cfg(all(feature = "experimental", feature = "multithreaded"))] @@ -234,7 +234,7 @@ impl Prio3FixedPointBoundedL2VecSumMultithreaded>>, XofTurboShake128, 16>; + Prio3>>, XofTurboShake128, 32>; impl Prio3Histogram { /// Constructs an instance of Prio3Histogram with the given number of aggregators, @@ -260,7 +260,7 @@ impl Prio3Histogram { pub type Prio3HistogramMultithreaded = Prio3< Histogram>>, XofTurboShake128, - 16, + 32, >; #[cfg(feature = "multithreaded")] @@ -285,7 +285,7 @@ impl Prio3HistogramMultithreaded { /// at most `max_weight` true values, and the aggregate is a histogram counting the number of true /// values at each position across all measurements. pub type Prio3MultihotCountVec = - Prio3>>, XofTurboShake128, 16>; + Prio3>>, XofTurboShake128, 32>; impl Prio3MultihotCountVec { /// Constructs an instance of Prio3MultihotCountVec with the given number of aggregators, number @@ -312,7 +312,7 @@ impl Prio3MultihotCountVec { pub type Prio3MultihotCountVecMultithreaded = Prio3< MultihotCountVec>>, XofTurboShake128, - 16, + 32, >; #[cfg(feature = "multithreaded")] @@ -336,7 +336,7 @@ impl Prio3MultihotCountVecMultithreaded { /// The average type. Each measurement is an integer in `[0,2^bits)` for some `0 < bits < 64` and /// the aggregate is the arithmetic average. -pub type Prio3Average = Prio3, XofTurboShake128, 16>; +pub type Prio3Average = Prio3, XofTurboShake128, 32>; impl Prio3Average { /// Construct an instance of `Prio3Average` with the given number of aggregators, where each @@ -1651,7 +1651,7 @@ mod tests { ); let mut nonce = [0; 16]; - let mut verify_key = [0; 16]; + let mut verify_key = [0; 32]; thread_rng().fill(&mut verify_key[..]); thread_rng().fill(&mut nonce[..]); @@ -1705,7 +1705,7 @@ mod tests { max_measurement + 2, ); - let mut verify_key = [0; 16]; + let mut verify_key = [0; 32]; thread_rng().fill(&mut verify_key[..]); let nonce = [0; 16]; @@ -1766,7 +1766,7 @@ mod tests { let prio3 = Prio3::< SumVec>>, XofTurboShake128, - 16, + 32, >::new(2, 2, 0xFFFF0000, SumVec::new(2, 20, 4).unwrap()) .unwrap(); @@ -1836,7 +1836,7 @@ mod tests { fn test_fixed_vec( fp_0: Fx, - prio3: Prio3, XofTurboShake128, 16>, + prio3: Prio3, XofTurboShake128, 32>, ) where Fx: Fixed + CompatibleFloat + std::ops::Neg, PE: Eq + ParallelSumGadget> + Clone + 'static, @@ -1929,7 +1929,7 @@ mod tests { fp_4_inv: Fx, fp_8_inv: Fx, fp_16_inv: Fx, - prio3: Prio3, XofTurboShake128, 16>, + prio3: Prio3, XofTurboShake128, 32>, ) where Fx: Fixed + CompatibleFloat + std::ops::Neg, PE: Eq + ParallelSumGadget> + Clone + 'static, @@ -1965,7 +1965,7 @@ mod tests { vec!(0.5, 0.0, 0.0), ); - let mut verify_key = [0; 16]; + let mut verify_key = [0; 32]; let mut nonce = [0; 16]; thread_rng().fill(&mut verify_key); thread_rng().fill(&mut nonce); diff --git a/src/vdaf/prio3_test.rs b/src/vdaf/prio3_test.rs index 01c238fe9..3dcfeae99 100644 --- a/src/vdaf/prio3_test.rs +++ b/src/vdaf/prio3_test.rs @@ -276,7 +276,7 @@ mod tests { include_str!("test_vec/13/Prio3Count_1.json"), include_str!("test_vec/13/Prio3Count_2.json"), ] { - check_test_vec_custom_de::( + check_test_vec_custom_de::( test_vector_str, |_json_params, num_shares| Prio3::new_count(num_shares).unwrap(), ); @@ -319,7 +319,7 @@ mod tests { #[test] fn test_vec_prio3_sum_vec_multiproof() { type Prio3SumVecField64Multiproof = - Prio3>>, XofTurboShake128, 16>; + Prio3>>, XofTurboShake128, 32>; for test_vector_str in [ include_str!("test_vec/13/Prio3SumVecWithMultiproof_0.json"), include_str!("test_vec/13/Prio3SumVecWithMultiproof_1.json"), diff --git a/src/vdaf/xof.rs b/src/vdaf/xof.rs index 2fef21083..1c46f3b4f 100644 --- a/src/vdaf/xof.rs +++ b/src/vdaf/xof.rs @@ -206,21 +206,34 @@ impl Debug for SeedStreamAes128 { #[derive(Clone, Debug)] pub struct XofTurboShake128(TurboShake128); -impl Xof<16> for XofTurboShake128 { - type SeedStream = SeedStreamTurboShake128; - - fn init(seed_bytes: &[u8; 16], dst: &[u8]) -> Self { +impl XofTurboShake128 { + pub(crate) fn init_with_variable_length_seed(seed_bytes: &[u8], dst: &[u8]) -> Self { let mut xof = Self(TurboShake128::from_core(TurboShake128Core::new( XOF_TURBO_SHAKE_128_DOMAIN_SEPARATION, ))); - Update::update( - &mut xof.0, - &[dst.len().try_into().expect("dst must be at most 255 bytes")], - ); + + let Ok(dst_len) = u16::try_from(dst.len()) else { + panic!("dst must not exceed 65535 bytes"); + }; + + let Ok(seed_len) = u8::try_from(seed_bytes.len()) else { + panic!("seed must not exceed 255 bytes"); + }; + + Update::update(&mut xof.0, &dst_len.to_le_bytes()); Update::update(&mut xof.0, dst); + Update::update(&mut xof.0, &seed_len.to_le_bytes()); Update::update(&mut xof.0, seed_bytes); xof } +} + +impl Xof<32> for XofTurboShake128 { + type SeedStream = SeedStreamTurboShake128; + + fn init(seed_bytes: &[u8; 32], dst: &[u8]) -> Self { + Self::init_with_variable_length_seed(&seed_bytes[..], dst) + } fn update(&mut self, data: &[u8]) { Update::update(&mut self.0, data); @@ -262,7 +275,7 @@ impl RngCore for SeedStreamTurboShake128 { /// A `rand`-compatible interface to construct XofTurboShake128 seed streams, with the domain /// separation tag and binder string both fixed as the empty string. impl SeedableRng for SeedStreamTurboShake128 { - type Seed = [u8; 16]; + type Seed = [u8; 32]; fn from_seed(seed: Self::Seed) -> Self { XofTurboShake128::init(&seed, b"").into_seed_stream() @@ -553,7 +566,6 @@ mod tests { assert_eq!(got, want); } - #[ignore = "seed size needs to be updated for VDAF draft-13"] #[test] fn xof_turboshake128() { let t: XofTestVector = @@ -574,7 +586,7 @@ mod tests { let got: Vec = xof.clone().into_seed_stream().into_field_vec(t.length); assert_eq!(got, want); - test_xof::(); + test_xof::(); } #[test] diff --git a/src/vidpf.rs b/src/vidpf.rs index 3ec8d1347..0a97c8c4f 100644 --- a/src/vidpf.rs +++ b/src/vidpf.rs @@ -329,7 +329,10 @@ impl Vidpf { level: usize, seed: &VidpfSeed, ) -> Result { - let mut shake = XofTurboShake128::init(seed, VidpfDomainSepTag::NODE_PROOF); + let mut shake = XofTurboShake128::init_with_variable_length_seed( + &seed[..], + VidpfDomainSepTag::NODE_PROOF, + ); for chunk128 in input .index(..=level) .chunks(128) diff --git a/tests/discrete_gauss.rs b/tests/discrete_gauss.rs index d72138b64..b75841f0a 100644 --- a/tests/discrete_gauss.rs +++ b/tests/discrete_gauss.rs @@ -20,12 +20,13 @@ use serde::Deserialize; #[derive(Debug, Eq, PartialEq, Deserialize)] pub struct DiscreteGaussTestVector { #[serde(with = "hex")] - seed: [u8; 16], + seed: [u8; 32], std_num: u128, std_denom: u128, samples: Vec, } +#[ignore = "need to upgrade the test vector code to VDAF-13"] #[test] fn discrete_gauss_reference() { let test_vectors: Vec = vec![