From f8c19bb1c62deaa5737a130a4a37bfa42ba3d66e Mon Sep 17 00:00:00 2001 From: Andy Leiserson Date: Tue, 19 Dec 2023 15:27:26 -0800 Subject: [PATCH 1/4] Add `PrssIndex` type --- .../protocol/boolean/generate_random_bits.rs | 2 +- ipa-core/src/protocol/context/prss.rs | 4 +- ipa-core/src/protocol/prss/crypto.rs | 24 ++++-- ipa-core/src/protocol/prss/mod.rs | 84 ++++++++++++++++--- 4 files changed, 94 insertions(+), 20 deletions(-) diff --git a/ipa-core/src/protocol/boolean/generate_random_bits.rs b/ipa-core/src/protocol/boolean/generate_random_bits.rs index 1070208ba..01a0029c3 100644 --- a/ipa-core/src/protocol/boolean/generate_random_bits.rs +++ b/ipa-core/src/protocol/boolean/generate_random_bits.rs @@ -35,7 +35,7 @@ impl RawRandomBits { // This avoids `F::BITS` as that can be larger than we need. let count = u128::BITS - F::PRIME.into().leading_zeros(); assert!(count <= u64::BITS); - let (left, right) = prss.generate_values(record_id); + let (left, right) = prss.generate::<(u128, u128), _>(record_id); #[allow(clippy::cast_possible_truncation)] // See above for the relevant assertion. Self { count, diff --git a/ipa-core/src/protocol/context/prss.rs b/ipa-core/src/protocol/context/prss.rs index fbec6255d..2afa16862 100644 --- a/ipa-core/src/protocol/context/prss.rs +++ b/ipa-core/src/protocol/context/prss.rs @@ -5,7 +5,7 @@ use rand_core::{Error, RngCore}; use crate::{ helpers::Role, protocol::{ - prss::{IndexedSharedRandomness, SequentialSharedRandomness, SharedRandomness}, + prss::{IndexedSharedRandomness, PrssIndex, SequentialSharedRandomness, SharedRandomness}, step::Gate, }, sync::Arc, @@ -34,7 +34,7 @@ impl<'a> InstrumentedIndexedSharedRandomness<'a> { } impl SharedRandomness for InstrumentedIndexedSharedRandomness<'_> { - fn generate_values>(&self, index: I) -> (u128, u128) { + fn generate_values>(&self, index: I) -> (u128, u128) { let step = self.step.as_ref().to_string(); // TODO: what we really want here is a gauge indicating the maximum index used to generate // PRSS. Gauge infrastructure is not supported yet, `Metrics` struct needs to be able to diff --git a/ipa-core/src/protocol/prss/crypto.rs b/ipa-core/src/protocol/prss/crypto.rs index 9293fa306..26096847a 100644 --- a/ipa-core/src/protocol/prss/crypto.rs +++ b/ipa-core/src/protocol/prss/crypto.rs @@ -9,6 +9,7 @@ use x25519_dalek::{EphemeralSecret, PublicKey}; use crate::{ ff::Field, + protocol::prss::PrssIndex, secret_sharing::{ replicated::{semi_honest::AdditiveShare as Replicated, ReplicatedSecretSharing}, SharedValue, @@ -24,6 +25,12 @@ pub trait FromRandomU128 { fn from_random_u128(src: u128) -> Self; } +impl FromRandomU128 for u128 { + fn from_random_u128(src: u128) -> Self { + src + } +} + /// Trait for things that can be generated by PRSS. /// /// The exact semantics of the generation depend on the value being generated, but like @@ -45,13 +52,13 @@ pub trait FromRandomU128 { /// In the first case, `FromPrss` is implemented for a tuple type, while in the second case, /// `FromPrss` is implemented for a secret-shared type. pub trait FromPrss: Sized { - fn from_prss>(prss: &P, index: I) -> Self; + fn from_prss>(prss: &P, index: I) -> Self; } /// Generate two random values, one that is known to the left helper /// and one that is known to the right helper. impl FromPrss for (T, T) { - fn from_prss>(prss: &P, index: I) -> (T, T) { + fn from_prss>(prss: &P, index: I) -> (T, T) { let (l, r) = prss.generate_values(index); (T::from_random_u128(l), T::from_random_u128(r)) } @@ -68,7 +75,10 @@ where T: SharedValue, (T, T): FromPrss, { - fn from_prss>(prss: &P, index: I) -> Replicated { + fn from_prss>( + prss: &P, + index: I, + ) -> Replicated { let (l, r) = FromPrss::from_prss(prss, index); Replicated::new(l, r) } @@ -78,7 +88,7 @@ pub trait SharedRandomness { /// Generate two random values, one that is known to the left helper /// and one that is known to the right helper. #[must_use] - fn generate_values>(&self, index: I) -> (u128, u128); + fn generate_values>(&self, index: I) -> (u128, u128); /// Generate two random field values, one that is known to the left helper /// and one that is known to the right helper. @@ -86,7 +96,7 @@ pub trait SharedRandomness { /// This alias is provided for compatibility with existing code. New code can just use /// `generate`. #[must_use] - fn generate_fields>(&self, index: I) -> (F, F) { + fn generate_fields>(&self, index: I) -> (F, F) { self.generate(index) } @@ -94,7 +104,7 @@ pub trait SharedRandomness { /// /// Generation by `FromPrss` is described in more detail in the `FromPrss` documentation. #[must_use] - fn generate>(&self, index: I) -> T { + fn generate>(&self, index: I) -> T { T::from_prss(self, index) } @@ -105,7 +115,7 @@ pub trait SharedRandomness { // Equivalent functionality could be obtained by defining an `Unreplicated` type that // implements `FromPrss`. #[must_use] - fn zero>(&self, index: I) -> V { + fn zero>(&self, index: I) -> V { let (l, r): (V, V) = self.generate(index); l - r } diff --git a/ipa-core/src/protocol/prss/mod.rs b/ipa-core/src/protocol/prss/mod.rs index 92a3f2b10..0bcd4d96f 100644 --- a/ipa-core/src/protocol/prss/mod.rs +++ b/ipa-core/src/protocol/prss/mod.rs @@ -10,6 +10,7 @@ use x25519_dalek::PublicKey; use super::step::Gate; use crate::{ + protocol::RecordId, rand::{CryptoRng, RngCore}, sync::{Arc, Mutex}, }; @@ -37,7 +38,8 @@ impl UsedSet { /// /// ## Panics /// Panic if this index has been used before. - fn insert(&self, index: u128) { + fn insert(&self, index: PrssIndex128) { + let index = u128::from(index); if index > usize::MAX as u128 { tracing::warn!( "PRSS verification can validate values not exceeding {}, index {index} is greater.", @@ -60,6 +62,65 @@ impl Debug for UsedSet { } } +/// Internal PRSS index. +/// +/// `PrssIndex128` values are directly input to the block cipher used for pseudo-random generation. +/// Each invocation must use a distinct `PrssIndex128` value. Most code should use the `PrssIndex` +/// type instead, which often corresponds to record IDs. `PrssIndex128` values are produced by +/// the `PrssIndex::offset` function and include the primary `PrssIndex` plus a possible offset +/// when more than 128 bits of randomness are required to generate the requested value. +/// +/// This is public so that it can be used by the instrumentation wrappers in +/// `ipa_core::protocol::context`. It should not generally be used outside the PRSS implementation. +#[derive(Clone, Copy, PartialEq, Eq)] +pub(crate) struct PrssIndex128(u128); + +impl From for PrssIndex128 { + fn from(value: u128) -> Self { + Self(value) + } +} + +impl From for u128 { + fn from(value: PrssIndex128) -> Self { + value.0 + } +} + +/// PRSS index. +/// +/// PRSS indexes are used to ensure that distinct pseudo-randomness is generated for every value +/// output by PRSS. It is often sufficient to use record IDs as PRSS indexes, and +/// `impl From for PrssIndex` is provided for that purpose. +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct PrssIndex(u32); + +impl From for PrssIndex { + fn from(value: u32) -> Self { + Self(value) + } +} + +// It would be nice for this to be TryFrom, but there's a lot of places where we use u128s as PRSS indexes. +impl From for PrssIndex { + fn from(value: u128) -> Self { + Self(value.try_into().unwrap()) + } +} + +impl From for PrssIndex { + fn from(value: RecordId) -> Self { + Self(u32::from(value)) + } +} + +impl PrssIndex { + fn offset(self, offset: usize) -> PrssIndex128 { + assert!(offset <= u16::MAX.into()); + PrssIndex128::from(u128::from((u64::from(self.0) << 16) + (offset as u64))) + } +} + /// A participant in a 2-of-N replicated secret sharing. /// Pseudorandom Secret-Sharing has many applications to the 3-party, replicated secret sharing scheme /// You can read about it in the seminal paper: @@ -75,14 +136,17 @@ pub struct IndexedSharedRandomness { } impl SharedRandomness for IndexedSharedRandomness { - fn generate_values>(&self, index: I) -> (u128, u128) { - let index = index.into(); + fn generate_values>(&self, index: I) -> (u128, u128) { + let index = index.into().offset(0); #[cfg(debug_assertions)] { self.used.insert(index); } - (self.left.generate(index), self.right.generate(index)) + ( + self.left.generate(index.into()), + self.right.generate(index.into()), + ) } } @@ -266,7 +330,7 @@ pub mod test { use crate::{ ff::{Field, Fp31}, protocol::{ - prss::{Endpoint, SharedRandomness}, + prss::{Endpoint, PrssIndex, SharedRandomness}, step::{Gate, StepNarrow}, }, rand::{thread_rng, Rng}, @@ -294,14 +358,14 @@ pub mod test { /// and subtract their right value, each share will be added once (as a left share) /// and subtracted once (as a right share), resulting in values that sum to zero. #[must_use] - fn zero_u128>(prss: &P, index: I) -> u128 { + fn zero_u128>(prss: &P, index: I) -> u128 { let (l, r) = prss.generate_values(index); l.wrapping_sub(r) } /// Generate an XOR share of zero. #[must_use] - fn zero_xor>(prss: &P, index: I) -> u128 { + fn zero_xor>(prss: &P, index: I) -> u128 { let (l, r) = prss.generate_values(index); l ^ r } @@ -312,14 +376,14 @@ pub mod test { /// using a wrapping add, the result won't be even because the high bit will /// wrap around and populate the low bit. #[must_use] - fn random_u128>(prss: &P, index: I) -> u128 { + fn random_u128>(prss: &P, index: I) -> u128 { let (l, r) = prss.generate_values(index); l.wrapping_add(r) } /// Generate additive shares of a random field value. #[must_use] - fn random>(prss: &P, index: I) -> F { + fn random>(prss: &P, index: I) -> F { let (l, r): (F, F) = prss.generate_fields(index); l + r } @@ -569,7 +633,7 @@ pub mod test { #[test] #[cfg(debug_assertions)] #[should_panic( - expected = "Generated randomness for index '100' twice using the same key 'protocol/test'" + expected = "Generated randomness for index '6553600' twice using the same key 'protocol/test'" )] fn indexed_rejects_the_same_index() { let [p1, _p2, _p3] = participants(); From 45bf93b39b6a58c011bd647bc5a28afb1be3f085 Mon Sep 17 00:00:00 2001 From: Andy Leiserson Date: Tue, 19 Dec 2023 16:31:46 -0800 Subject: [PATCH 2/4] Variable-size PRSS generation (#884) --- ipa-core/src/protocol/context/prss.rs | 8 +++- ipa-core/src/protocol/prss/crypto.rs | 57 ++++++++++++++++++++------- ipa-core/src/protocol/prss/mod.rs | 20 ++++++---- 3 files changed, 60 insertions(+), 25 deletions(-) diff --git a/ipa-core/src/protocol/context/prss.rs b/ipa-core/src/protocol/context/prss.rs index 2afa16862..5ff5d3cd2 100644 --- a/ipa-core/src/protocol/context/prss.rs +++ b/ipa-core/src/protocol/context/prss.rs @@ -1,5 +1,6 @@ //! Metric-aware PRSS decorators +use generic_array::{ArrayLength, GenericArray}; use rand_core::{Error, RngCore}; use crate::{ @@ -34,13 +35,16 @@ impl<'a> InstrumentedIndexedSharedRandomness<'a> { } impl SharedRandomness for InstrumentedIndexedSharedRandomness<'_> { - fn generate_values>(&self, index: I) -> (u128, u128) { + fn generate_arrays, N: ArrayLength>( + &self, + index: I, + ) -> (GenericArray, GenericArray) { let step = self.step.as_ref().to_string(); // TODO: what we really want here is a gauge indicating the maximum index used to generate // PRSS. Gauge infrastructure is not supported yet, `Metrics` struct needs to be able to // handle gauges metrics::increment_counter!(INDEXED_PRSS_GENERATED, STEP => step, ROLE => self.role.as_static_str()); - self.inner.generate_values(index) + self.inner.generate_arrays(index) } } diff --git a/ipa-core/src/protocol/prss/crypto.rs b/ipa-core/src/protocol/prss/crypto.rs index 26096847a..25e68a1fe 100644 --- a/ipa-core/src/protocol/prss/crypto.rs +++ b/ipa-core/src/protocol/prss/crypto.rs @@ -1,10 +1,12 @@ use aes::{ - cipher::{generic_array::GenericArray, BlockEncrypt, KeyInit}, + cipher::{BlockEncrypt, KeyInit}, Aes256, }; +use generic_array::{ArrayLength, GenericArray}; use hkdf::Hkdf; use rand::{CryptoRng, RngCore}; use sha2::Sha256; +use typenum::U1; use x25519_dalek::{EphemeralSecret, PublicKey}; use crate::{ @@ -31,7 +33,7 @@ impl FromRandomU128 for u128 { } } -/// Trait for things that can be generated by PRSS. +/// Trait for random generation. /// /// The exact semantics of the generation depend on the value being generated, but like /// `rand::distributions::Standard`, a uniform distribution is typical. When implementing @@ -39,8 +41,24 @@ impl FromRandomU128 for u128 { /// an unexpected way. For example, an implementation that draws from a subset of the /// possible values could be dangerous, if used in an unexpected context where /// security relies on sampling from the full space. +pub trait FromRandom: Sized { + type SourceLength: ArrayLength; + + /// Generate a random value of `Self` from `SourceLength` uniformly-distributed u128s. + fn from_random(src: GenericArray) -> Self; +} + +impl FromRandom for T { + type SourceLength = U1; + + fn from_random(src: GenericArray) -> Self { + Self::from_random_u128(src[0]) + } +} + +/// Trait for things that can be generated by PRSS. /// -/// At a high level, there are two kinds of PRSS generation: +/// We support two kinds of PRSS generation: /// 1. Raw values: In this case, two values are generated, one using the randomness that is shared /// with the left helper, and one with the randomness that is shared with the right helper. /// Thus, one of the generated values is known to both us and the left helper, and likewise for @@ -57,10 +75,10 @@ pub trait FromPrss: Sized { /// Generate two random values, one that is known to the left helper /// and one that is known to the right helper. -impl FromPrss for (T, T) { +impl FromPrss for (T, T) { fn from_prss>(prss: &P, index: I) -> (T, T) { - let (l, r) = prss.generate_values(index); - (T::from_random_u128(l), T::from_random_u128(r)) + let (l, r) = prss.generate_arrays(index); + (T::from_random(l), T::from_random(r)) } } @@ -70,16 +88,12 @@ impl FromPrss for (T, T) { /// "Efficient Bit-Decomposition and Modulus Conversion Protocols with an Honest Majority" /// by Ryo Kikuchi, Dai Ikarashi, Takahiro Matsuda, Koki Hamada, and Koji Chida /// -impl FromPrss for Replicated -where - T: SharedValue, - (T, T): FromPrss, -{ +impl FromPrss for Replicated { fn from_prss>( prss: &P, index: I, ) -> Replicated { - let (l, r) = FromPrss::from_prss(prss, index); + let (l, r) = <(T, T) as FromPrss>::from_prss(prss, index); Replicated::new(l, r) } } @@ -88,7 +102,18 @@ pub trait SharedRandomness { /// Generate two random values, one that is known to the left helper /// and one that is known to the right helper. #[must_use] - fn generate_values>(&self, index: I) -> (u128, u128); + fn generate_arrays, N: ArrayLength>( + &self, + index: I, + ) -> (GenericArray, GenericArray); + + /// Generate two random values, one that is known to the left helper + /// and one that is known to the right helper. + #[must_use] + fn generate_values>(&self, index: I) -> (u128, u128) { + let (l, r) = self.generate_arrays::<_, U1>(index); + (l[0], r[0]) + } /// Generate two random field values, one that is known to the left helper /// and one that is known to the right helper. @@ -158,7 +183,7 @@ impl GeneratorFactory { #[allow(clippy::missing_panics_doc)] // Panic should be impossible. #[must_use] pub fn generator(&self, context: &[u8]) -> Generator { - let mut k = GenericArray::default(); + let mut k = aes::cipher::generic_array::GenericArray::default(); self.kdf.expand(context, &mut k).unwrap(); Generator { cipher: Aes256::new(&k), @@ -179,7 +204,9 @@ impl Generator { pub fn generate(&self, index: u128) -> u128 { let mut buf = index.to_le_bytes(); self.cipher - .encrypt_block(GenericArray::from_mut_slice(&mut buf)); + .encrypt_block(aes::cipher::generic_array::GenericArray::from_mut_slice( + &mut buf, + )); u128::from_le_bytes(buf) ^ index } diff --git a/ipa-core/src/protocol/prss/mod.rs b/ipa-core/src/protocol/prss/mod.rs index 0bcd4d96f..e7c711e82 100644 --- a/ipa-core/src/protocol/prss/mod.rs +++ b/ipa-core/src/protocol/prss/mod.rs @@ -6,6 +6,7 @@ use std::{collections::HashSet, fmt::Formatter}; pub use crypto::{ FromPrss, FromRandomU128, Generator, GeneratorFactory, KeyExchange, SharedRandomness, }; +use generic_array::{sequence::GenericSequence, ArrayLength, GenericArray}; use x25519_dalek::PublicKey; use super::step::Gate; @@ -136,17 +137,20 @@ pub struct IndexedSharedRandomness { } impl SharedRandomness for IndexedSharedRandomness { - fn generate_values>(&self, index: I) -> (u128, u128) { - let index = index.into().offset(0); + fn generate_arrays, N: ArrayLength>( + &self, + index: I, + ) -> (GenericArray, GenericArray) { + let index = index.into(); #[cfg(debug_assertions)] { - self.used.insert(index); + for i in 0..N::USIZE { + self.used.insert(index.offset(i)); + } } - - ( - self.left.generate(index.into()), - self.right.generate(index.into()), - ) + let l = GenericArray::generate(|i| self.left.generate(index.offset(i).into())); + let r = GenericArray::generate(|i| self.right.generate(index.offset(i).into())); + (l, r) } } From c9fd7bfac429cf984080ddd728f631b8bdda0c3d Mon Sep 17 00:00:00 2001 From: Andy Leiserson Date: Tue, 19 Dec 2023 17:05:51 -0800 Subject: [PATCH 3/4] Remove small-field impls from BA256 Implement proper random generation of BA256. --- ipa-core/src/ff/boolean_array.rs | 204 +++++++++--------- .../boolean_ops/addition_sequential.rs | 2 +- ipa-core/src/protocol/prss/mod.rs | 3 +- 3 files changed, 105 insertions(+), 104 deletions(-) diff --git a/ipa-core/src/ff/boolean_array.rs b/ipa-core/src/ff/boolean_array.rs index 42b523081..ea93cabb9 100644 --- a/ipa-core/src/ff/boolean_array.rs +++ b/ipa-core/src/ff/boolean_array.rs @@ -3,12 +3,12 @@ use bitvec::{ slice::Iter, }; use generic_array::GenericArray; -use typenum::{U14, U32, U8}; +use typenum::{U14, U2, U32, U8}; use crate::{ - ff::{boolean::Boolean, Serializable}, - protocol::prss::FromRandomU128, - secret_sharing::Block, + ff::{boolean::Boolean, ArrayAccess, Field, Serializable}, + protocol::prss::{FromRandom, FromRandomU128}, + secret_sharing::{Block, SharedValue}, }; /// The implementation below cannot be constrained without breaking Rust's @@ -88,6 +88,73 @@ macro_rules! bitarr_one { (@r [$($x:tt)*] [$($y:tt)*] $([$($z:tt)*])*) => { bitarr_one!(@r [$($x)* $($y)* $($y)*] $([$($z)* $($z)*])*) }; } +// Macro for boolean arrays <= 128 bits. +macro_rules! boolean_array_impl_small { + ($modname:ident, $name:ident, $bits:tt) => { + boolean_array_impl!($modname, $name, $bits); + + impl Field for $name { + const ONE: Self = Self(bitarr_one!($bits)); + + fn as_u128(&self) -> u128 { + (*self).into() + } + + fn truncate_from>(v: T) -> Self { + let v = v.into(); + let mut val = ::ZERO; + for i in 0..std::cmp::min(128, $bits) { + val.set(i, Boolean::from((v >> i & 1) == 1)); + } + + val + } + } + + impl TryFrom for $name { + type Error = crate::error::Error; + + /// Fallible conversion from `u128` to this data type. The input value must + /// be at most `Self::BITS` long. That is, the integer value must be less than + /// or equal to `2^Self::BITS`, or it will return an error. + fn try_from(v: u128) -> Result { + if u128::BITS - v.leading_zeros() <= Self::BITS { + Ok(Self::truncate_from(v)) + } else { + Err(crate::error::Error::FieldValueTruncation(format!( + "Bit array size {} is too small to hold the value {}.", + Self::BITS, + v + ))) + } + } + } + + impl From<$name> for u128 { + /// Infallible conversion from this data type to `u128`. + fn from(v: $name) -> u128 { + debug_assert!(<$name>::BITS <= 128); + v.0.iter() + .by_refs() + .enumerate() + .fold(0_u128, |acc, (i, b)| acc + ((*b as u128) << i)) + } + } + + impl rand::distributions::Distribution<$name> for rand::distributions::Standard { + fn sample(&self, rng: &mut R) -> $name { + <$name>::from_random_u128(rng.gen::()) + } + } + + impl FromRandomU128 for $name { + fn from_random_u128(src: u128) -> Self { + Field::truncate_from(src) + } + } + }; +} + //macro for implementing Boolean array, only works for a byte size for which Block is defined macro_rules! boolean_array_impl { ($modname:ident, $name:ident, $bits:tt) => { @@ -96,7 +163,7 @@ macro_rules! boolean_array_impl { mod $modname { use super::*; use crate::{ - ff::{boolean::Boolean, ArrayAccess, Expand, Field, Serializable}, + ff::{boolean::Boolean, ArrayAccess, Expand, Serializable}, secret_sharing::{ replicated::semi_honest::{ASIterator, AdditiveShare}, SharedValue, @@ -205,38 +272,6 @@ macro_rules! boolean_array_impl { } } - impl Field for $name { - const ONE: Self = Self(bitarr_one!($bits)); - - fn as_u128(&self) -> u128 { - (*self).into() - } - - fn truncate_from>(v: T) -> Self { - let v = v.into(); - let mut val = Self::ZERO; - for i in 0..std::cmp::min(128, $bits) { - val.set(i, Boolean::from((v >> i & 1) == 1)); - } - - val - } - } - - // TODO(812): this should only be implemented like this when bits <= 128 - impl rand::distributions::Distribution<$name> for rand::distributions::Standard { - fn sample(&self, rng: &mut R) -> $name { - <$name>::truncate_from(rng.gen::()) - } - } - - // TODO(812): this should only be implemented when bits <= 128 - impl FromRandomU128 for $name { - fn from_random_u128(src: u128) -> Self { - Field::truncate_from(src) - } - } - impl std::ops::Mul for $name { type Output = Self; fn mul(self, rhs: Self) -> Self::Output { @@ -250,45 +285,12 @@ macro_rules! boolean_array_impl { } } - impl TryFrom for $name { - type Error = crate::error::Error; - - /// Fallible conversion from `u128` to this data type. The input value must - /// be at most `Self::BITS` long. That is, the integer value must be less than - /// or equal to `2^Self::BITS`, or it will return an error. - fn try_from(v: u128) -> Result { - if u128::BITS - v.leading_zeros() <= Self::BITS { - Ok(Self::truncate_from(v)) - } else { - Err(crate::error::Error::FieldValueTruncation(format!( - "Bit array size {} is too small to hold the value {}.", - Self::BITS, - v - ))) - } - } - } - impl From<$name> for Store { fn from(v: $name) -> Self { v.0 } } - impl From<$name> for u128 { - /// Infallible conversion from this data type to `u128`. We assume that the - /// inner value is at most 128-bit long. That is, the integer value must be - /// less than or equal to `2^Self::BITS`. Should be long enough for our use - /// case. - fn from(v: $name) -> u128 { - debug_assert!(<$name>::BITS <= 128); - v.0.iter() - .by_refs() - .enumerate() - .fold(0_u128, |acc, (i, b)| acc + ((*b as u128) << i)) - } - } - impl Expand for $name { type Input = Boolean; @@ -333,6 +335,9 @@ macro_rules! boolean_array_impl { use super::*; + // Only small BAs expose this via `Field`. + const ONE: $name = $name(bitarr_one!($bits)); + #[test] fn set_boolean_array() { let mut rng = thread_rng(); @@ -345,7 +350,7 @@ macro_rules! boolean_array_impl { #[test] fn iterate_boolean_array() { - let bits = $name::ONE; + let bits = ONE; let iter = bits.into_iter(); for (i, j) in iter.enumerate() { if i == 0 { @@ -355,18 +360,18 @@ macro_rules! boolean_array_impl { } } } - } - #[test] - fn iterate_secret_shared_boolean_array() { - use crate::secret_sharing::replicated::ReplicatedSecretSharing; - let bits = AdditiveShare::new($name::ONE, $name::ONE); - let iter = bits.into_iter(); - for (i, j) in iter.enumerate() { - if i == 0 { - assert_eq!(j, AdditiveShare::new(Boolean::ONE, Boolean::ONE)); - } else { - assert_eq!(j, AdditiveShare::::ZERO); + #[test] + fn iterate_secret_shared_boolean_array() { + use crate::secret_sharing::replicated::ReplicatedSecretSharing; + let bits = AdditiveShare::new(ONE, ONE); + let iter = bits.into_iter(); + for (i, j) in iter.enumerate() { + if i == 0 { + assert_eq!(j, AdditiveShare::new(Boolean::ONE, Boolean::ONE)); + } else { + assert_eq!(j, AdditiveShare::::ZERO); + } } } } @@ -386,16 +391,16 @@ store_impl!(U14, 112); store_impl!(U32, 256); //impl BA3 -boolean_array_impl!(boolean_array_3, BA3, 3); -boolean_array_impl!(boolean_array_4, BA4, 4); -boolean_array_impl!(boolean_array_5, BA5, 5); -boolean_array_impl!(boolean_array_6, BA6, 6); -boolean_array_impl!(boolean_array_7, BA7, 7); -boolean_array_impl!(boolean_array_8, BA8, 8); -boolean_array_impl!(boolean_array_20, BA20, 20); -boolean_array_impl!(boolean_array_32, BA32, 32); -boolean_array_impl!(boolean_array_64, BA64, 64); -boolean_array_impl!(boolean_array_112, BA112, 112); +boolean_array_impl_small!(boolean_array_3, BA3, 3); +boolean_array_impl_small!(boolean_array_4, BA4, 4); +boolean_array_impl_small!(boolean_array_5, BA5, 5); +boolean_array_impl_small!(boolean_array_6, BA6, 6); +boolean_array_impl_small!(boolean_array_7, BA7, 7); +boolean_array_impl_small!(boolean_array_8, BA8, 8); +boolean_array_impl_small!(boolean_array_20, BA20, 20); +boolean_array_impl_small!(boolean_array_32, BA32, 32); +boolean_array_impl_small!(boolean_array_64, BA64, 64); +boolean_array_impl_small!(boolean_array_112, BA112, 112); boolean_array_impl!(boolean_array_256, BA256, 256); // used to convert into Fp25519 @@ -411,20 +416,15 @@ impl From<(u128, u128)> for BA256 { } } -// TODO(812): don't generate the default impls; enable these instead. -/* -impl FromPrss for (BA256, BA256) { - fn from_prss>(prss: &P, index: I) -> (BA256, BA256) { - let index = index.into(); - let (l0, r0) = prss.generate_values(2 * index); - let (l1, r1) = prss.generate_values(2 * index + 1); - (BA256::from((l0, l1)), BA256::from((r0, r1))) +impl FromRandom for BA256 { + type SourceLength = U2; + fn from_random(src: GenericArray) -> Self { + (src[0], src[1]).into() } } impl rand::distributions::Distribution for rand::distributions::Standard { fn sample(&self, rng: &mut R) -> BA256 { - BA256::from((rng.gen(), rng.gen())) + (rng.gen(), rng.gen()).into() } } -*/ diff --git a/ipa-core/src/protocol/ipa_prf/boolean_ops/addition_sequential.rs b/ipa-core/src/protocol/ipa_prf/boolean_ops/addition_sequential.rs index 7ca1e517d..8c86e81e0 100644 --- a/ipa-core/src/protocol/ipa_prf/boolean_ops/addition_sequential.rs +++ b/ipa-core/src/protocol/ipa_prf/boolean_ops/addition_sequential.rs @@ -32,7 +32,7 @@ pub async fn integer_add( where C: Context, YS: SharedValue + CustomArray, - XS: SharedValue + CustomArray + Field, + XS: SharedValue + CustomArray + SharedValue, XS::Element: Field, { let mut carry = AdditiveShare::::ZERO; diff --git a/ipa-core/src/protocol/prss/mod.rs b/ipa-core/src/protocol/prss/mod.rs index e7c711e82..ed48c8132 100644 --- a/ipa-core/src/protocol/prss/mod.rs +++ b/ipa-core/src/protocol/prss/mod.rs @@ -4,7 +4,8 @@ use std::{collections::HashMap, fmt::Debug}; use std::{collections::HashSet, fmt::Formatter}; pub use crypto::{ - FromPrss, FromRandomU128, Generator, GeneratorFactory, KeyExchange, SharedRandomness, + FromPrss, FromRandom, FromRandomU128, Generator, GeneratorFactory, KeyExchange, + SharedRandomness, }; use generic_array::{sequence::GenericSequence, ArrayLength, GenericArray}; use x25519_dalek::PublicKey; From 93ab984af6bcd407c2b7af0b5a0fec3389a2b29d Mon Sep 17 00:00:00 2001 From: Andy Leiserson Date: Tue, 26 Dec 2023 12:17:54 -0800 Subject: [PATCH 4/4] PR feedback --- ipa-core/src/ff/boolean_array.rs | 1 + .../src/protocol/ipa_prf/boolean_ops/addition_sequential.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ipa-core/src/ff/boolean_array.rs b/ipa-core/src/ff/boolean_array.rs index ea93cabb9..84da10fd1 100644 --- a/ipa-core/src/ff/boolean_array.rs +++ b/ipa-core/src/ff/boolean_array.rs @@ -93,6 +93,7 @@ macro_rules! boolean_array_impl_small { ($modname:ident, $name:ident, $bits:tt) => { boolean_array_impl!($modname, $name, $bits); + // TODO(812): remove this impl; BAs are not field elements. impl Field for $name { const ONE: Self = Self(bitarr_one!($bits)); diff --git a/ipa-core/src/protocol/ipa_prf/boolean_ops/addition_sequential.rs b/ipa-core/src/protocol/ipa_prf/boolean_ops/addition_sequential.rs index 8c86e81e0..a506f0753 100644 --- a/ipa-core/src/protocol/ipa_prf/boolean_ops/addition_sequential.rs +++ b/ipa-core/src/protocol/ipa_prf/boolean_ops/addition_sequential.rs @@ -32,7 +32,7 @@ pub async fn integer_add( where C: Context, YS: SharedValue + CustomArray, - XS: SharedValue + CustomArray + SharedValue, + XS: SharedValue + CustomArray, XS::Element: Field, { let mut carry = AdditiveShare::::ZERO;