diff --git a/ipa-core/src/ff/galois_field.rs b/ipa-core/src/ff/galois_field.rs index 0808e307a..8925c7aa1 100644 --- a/ipa-core/src/ff/galois_field.rs +++ b/ipa-core/src/ff/galois_field.rs @@ -685,5 +685,11 @@ bit_array_impl!( v } } + + impl From for bool { + fn from(value: Gf2) -> Self { + value != Gf2::ZERO + } + } } ); diff --git a/ipa-core/src/ff/mod.rs b/ipa-core/src/ff/mod.rs index 03b8da9ec..dcbaf6aa2 100644 --- a/ipa-core/src/ff/mod.rs +++ b/ipa-core/src/ff/mod.rs @@ -19,6 +19,8 @@ use generic_array::{ArrayLength, GenericArray}; pub use prime_field::Fp31; pub use prime_field::{Fp32BitPrime, PrimeField}; +use crate::secret_sharing::replicated::semi_honest::BorrowReplicated; + #[derive(Debug, thiserror::Error, PartialEq, Eq)] pub enum Error { #[error("unknown field type {type_str}")] @@ -69,6 +71,29 @@ pub trait ArrayAccess { fn iter(&self) -> Self::Iter<'_>; } +// There are currently a few places that the by-reference access of `ArrayAccessReplicated` is +// required, and many places that the by-value access of `ArrayAccess` is used. +// +// If all uses of `ArrayAccess` were changed to operate by-reference, the replicated vs. not +// distinction may not be a good reason to have separate traits. Uses of the replicated API would +// just specify an additional `BorrowReplicated` bound. Note that there is at least one +// `ArrayAccess` implementation where a straightforward adaptation will not make it work with +// references: Galois fields, which have `ArrayAccess::Output = bool`. It probably would be possible +// to provide by-reference access for Galois fields using the bit-reference functionality in the +// bitvec crate. +// +// No reason this shouldn't have iter too, but deferring that because it's not needed yet, and will +// be messy to support for Galois fields because of the `bool` value type. +pub trait ArrayAccessReplicated { + type Ref<'a>: BorrowReplicated + where + Self: 'a; + + fn get(&self, index: usize) -> Option>; + + fn set(&mut self, index: usize, e: Self::Ref<'_>); +} + pub trait Expand { type Input; diff --git a/ipa-core/src/protocol/basics/share_known_value.rs b/ipa-core/src/protocol/basics/share_known_value.rs index bd811a3df..cd36f33e9 100644 --- a/ipa-core/src/protocol/basics/share_known_value.rs +++ b/ipa-core/src/protocol/basics/share_known_value.rs @@ -12,6 +12,10 @@ use crate::{ }, }; +/// Produce a share of some pre-determined constant. +/// +/// The context is only used to determine the helper role. It is not used for communication or PRSS, +/// and it is not necessary to use a uniquely narrowed context. pub trait ShareKnownValue { fn share_known_value(ctx: &C, value: V) -> Self; } diff --git a/ipa-core/src/protocol/boolean/generate_random_bits.rs b/ipa-core/src/protocol/boolean/generate_random_bits.rs index 01a0029c3..d97cd4cb9 100644 --- a/ipa-core/src/protocol/boolean/generate_random_bits.rs +++ b/ipa-core/src/protocol/boolean/generate_random_bits.rs @@ -4,7 +4,7 @@ use futures::stream::{iter as stream_iter, StreamExt}; use crate::{ error::Error, - ff::PrimeField, + ff::{ArrayAccessReplicated, PrimeField}, helpers::Role, protocol::{ basics::SecureMul, @@ -14,8 +14,8 @@ use crate::{ RecordId, }, secret_sharing::{ - replicated::semi_honest::AdditiveShare as Replicated, BitDecomposed, - Linear as LinearSecretSharing, + replicated::semi_honest::{AdditiveShare as Replicated, BorrowReplicated}, + BitDecomposed, Linear as LinearSecretSharing, }, }; @@ -27,6 +27,45 @@ struct RawRandomBits { right: u64, } +#[derive(Clone, Copy)] +struct RawRandomBitsIndex<'a> { + bits: &'a RawRandomBits, + index: usize, +} + +impl ArrayAccessReplicated for RawRandomBits { + type Ref<'a> = RawRandomBitsIndex<'a>; + + fn get(&self, index: usize) -> Option> { + (index < self.count.try_into().unwrap()).then_some(RawRandomBitsIndex { bits: self, index }) + } + + fn set(&mut self, _index: usize, _e: Self::Ref<'_>) { + unimplemented!("if this is needed, RawRandomBits should use bitvec") + // ... or perhaps be replaced with `AdditiveShare`. + } +} + +impl<'a> BorrowReplicated for RawRandomBitsIndex<'a> { + type Output = bool; + + fn borrow_left(&self) -> &bool { + if ((self.bits.left >> self.index) & 1) == 1 { + &true + } else { + &false + } + } + + fn borrow_right(&self) -> &bool { + if ((self.bits.right >> self.index) & 1) == 1 { + &true + } else { + &false + } + } +} + impl RawRandomBits { fn generate( prss: &InstrumentedIndexedSharedRandomness, @@ -55,27 +94,16 @@ impl ToBitConversionTriples for RawRandomBits { fn triple(&self, role: Role, i: u32) -> BitConversionTriple> { debug_assert!(u128::BITS - F::PRIME.into().leading_zeros() >= self.count); - assert!(i < self.count); BitConversionTriple::new( role, - ((self.left >> i) & 1) == 1, - ((self.right >> i) & 1) == 1, + &self + .get(i.try_into().unwrap()) + .expect("index out of bounds while converting RawRandom"), ) } - fn into_triples( - self, - role: Role, - indices: I, - ) -> ( - BitDecomposed>>, - Self::Residual, - ) - where - F: PrimeField, - I: IntoIterator, - { - (self.triple_range(role, indices), ()) + fn into_residual(self) -> Self::Residual { + Self::Residual::default(); } } diff --git a/ipa-core/src/protocol/ipa_prf/boolean_ops/comparison_and_subtraction_sequential.rs b/ipa-core/src/protocol/ipa_prf/boolean_ops/comparison_and_subtraction_sequential.rs index 54815ff13..d02ab115a 100644 --- a/ipa-core/src/protocol/ipa_prf/boolean_ops/comparison_and_subtraction_sequential.rs +++ b/ipa-core/src/protocol/ipa_prf/boolean_ops/comparison_and_subtraction_sequential.rs @@ -6,7 +6,12 @@ use crate::ff::Expand; use crate::{ error::Error, ff::{ArrayAccess, CustomArray, Field}, - protocol::{basics::SecureMul, context::Context, step::BitOpStep, RecordId}, + protocol::{ + basics::{SecureMul, ShareKnownValue}, + context::Context, + step::BitOpStep, + RecordId, + }, secret_sharing::{replicated::semi_honest::AdditiveShare, SharedValue}, }; @@ -32,11 +37,11 @@ where C: Context, YS: SharedValue + CustomArray, XS: SharedValue + CustomArray + Field, - XS::Element: Field + std::ops::Not, + XS::Element: Field, + AdditiveShare: std::ops::Not>, { // we need to initialize carry to 1 for x>=y, - // since there are three shares 1+1+1 = 1 mod 2, so setting left = 1 and right = 1 works - let mut carry = AdditiveShare(XS::Element::ONE, XS::Element::ONE); + let mut carry = AdditiveShare::::share_known_value(&ctx, XS::Element::ONE); // we don't care about the subtraction, we just want the carry let _ = subtraction_circuit(ctx, record_id, x, y, &mut carry).await; Ok(carry) @@ -56,7 +61,8 @@ where C: Context, YS: SharedValue + CustomArray, XS: SharedValue + CustomArray + Field, - XS::Element: Field + std::ops::Not, + XS::Element: Field, + AdditiveShare: std::ops::Not>, { // we need to initialize carry to 0 for x>y let mut carry = AdditiveShare::::ZERO; @@ -80,10 +86,11 @@ where C: Context, YS: SharedValue + CustomArray, XS: SharedValue + CustomArray + Field, - XS::Element: Field + std::ops::Not, + XS::Element: Field, + AdditiveShare: std::ops::Not>, { // we need to initialize carry to 1 for a subtraction - let mut carry = AdditiveShare(XS::Element::ONE, XS::Element::ONE); + let mut carry = AdditiveShare::::share_known_value(&ctx, XS::Element::ONE); subtraction_circuit(ctx, record_id, x, y, &mut carry).await } @@ -102,9 +109,10 @@ pub async fn integer_sat_sub( where C: Context, S: CustomArray + Field, - S::Element: Field + std::ops::Not, + S::Element: Field, + AdditiveShare: std::ops::Not>, { - let mut carry = AdditiveShare(S::Element::ONE, S::Element::ONE); + let mut carry = AdditiveShare::::share_known_value(&ctx, S::Element::ONE); let result = subtraction_circuit( ctx.narrow(&Step::SaturatedSubtraction), record_id, @@ -139,7 +147,8 @@ where C: Context, XS: SharedValue + CustomArray, YS: SharedValue + CustomArray, - XS::Element: Field + std::ops::Not, + XS::Element: Field, + AdditiveShare: std::ops::Not>, { let mut result = AdditiveShare::::ZERO; for (i, v) in x.iter().enumerate() { @@ -182,7 +191,8 @@ async fn bit_subtractor( ) -> Result, Error> where C: Context, - S: Field + std::ops::Not, + S: Field, + AdditiveShare: std::ops::Not>, { let output = x + !(y.unwrap_or(&AdditiveShare::::ZERO) + &*carry); @@ -216,7 +226,10 @@ mod test { }, }, rand::thread_rng, - secret_sharing::{replicated::semi_honest::AdditiveShare, SharedValue}, + secret_sharing::{ + replicated::{semi_honest::AdditiveShare, ReplicatedSecretSharing}, + SharedValue, + }, test_executor::run, test_fixture::{Reconstruct, Runner, TestWorld}, }; @@ -228,25 +241,25 @@ mod test { assert_eq!(::ONE, !(::ZERO)); assert_eq!(::ZERO, !(::ONE)); assert_eq!( - AdditiveShare(::ZERO, ::ZERO), - !AdditiveShare(::ONE, ::ONE) + AdditiveShare::new(::ZERO, ::ZERO), + !AdditiveShare::new(::ONE, ::ONE) ); assert_eq!( - AdditiveShare( + AdditiveShare::new( ::expand(&::ZERO), ::expand(&::ZERO) ), - !AdditiveShare( + !AdditiveShare::new( ::expand(&::ONE), ::expand(&::ONE) ) ); assert_eq!( - !AdditiveShare( + !AdditiveShare::new( ::expand(&::ZERO), ::expand(&::ZERO) ), - AdditiveShare( + AdditiveShare::new( ::expand(&::ONE), ::expand(&::ONE) ) diff --git a/ipa-core/src/protocol/ipa_prf/boolean_ops/share_conversion_aby.rs b/ipa-core/src/protocol/ipa_prf/boolean_ops/share_conversion_aby.rs index b5f06147d..cc56cd5fb 100644 --- a/ipa-core/src/protocol/ipa_prf/boolean_ops/share_conversion_aby.rs +++ b/ipa-core/src/protocol/ipa_prf/boolean_ops/share_conversion_aby.rs @@ -122,16 +122,16 @@ where // sh_s: H1: (r1,0), H2: (0,0), H3: (0, r1) match ctx.role() { Role::H1 => ( - AdditiveShare(::ZERO, ::ZERO), - AdditiveShare(r.0, ::ZERO), + AdditiveShare::new(::ZERO, ::ZERO), + AdditiveShare::new(r.left(), ::ZERO), ), Role::H2 => ( - AdditiveShare(::ZERO, r.1), - AdditiveShare(::ZERO, ::ZERO), + AdditiveShare::new(::ZERO, r.right()), + AdditiveShare::new(::ZERO, ::ZERO), ), Role::H3 => ( - AdditiveShare(r.0, ::ZERO), - AdditiveShare(::ZERO, r.1), + AdditiveShare::new(r.left(), ::ZERO), + AdditiveShare::new(::ZERO, r.right()), ), } }; @@ -162,22 +162,22 @@ where .await?; // this leaks information, but with negligible probability - let y = AdditiveShare::(sh_y.left(), sh_y.right()) + let y = AdditiveShare::::new(sh_y.left(), sh_y.right()) .partial_reveal(ctx.narrow(&Step::RevealY), record_id, Role::H3) .await?; match ctx.role() { - Role::H1 => Ok(AdditiveShare::( - Fp25519::from(sh_s.0).neg(), + Role::H1 => Ok(AdditiveShare::::new( + Fp25519::from(sh_s.left()).neg(), Fp25519::from(y.unwrap()), )), - Role::H2 => Ok(AdditiveShare::( + Role::H2 => Ok(AdditiveShare::::new( Fp25519::from(y.unwrap()), - Fp25519::from(sh_r.1).neg(), + Fp25519::from(sh_r.right()).neg(), )), - Role::H3 => Ok(AdditiveShare::( - Fp25519::from(sh_r.0).neg(), - Fp25519::from(sh_s.1).neg(), + Role::H3 => Ok(AdditiveShare::::new( + Fp25519::from(sh_r.left()).neg(), + Fp25519::from(sh_s.right()).neg(), )), } } @@ -236,25 +236,6 @@ where y } -/// inserts a smaller array into a larger -/// allows share conversion between secret shared Boolean Array types like 'BA64' and 'BA256' -/// only used for testing purposes -#[cfg(all(test, unit_test))] -pub fn expand_shared_array( - x: &AdditiveShare, - offset: Option, -) -> AdditiveShare -where - XS: CustomArray + SharedValue, - YS: CustomArray + SharedValue, - XS::Element: SharedValue, -{ - AdditiveShare::( - expand_array(&x.left(), offset), - expand_array(&x.right(), offset), - ) -} - #[cfg(all(test, unit_test))] mod tests { use curve25519_dalek::Scalar; @@ -272,12 +253,10 @@ mod tests { protocol, protocol::{ context::Context, - ipa_prf::boolean_ops::share_conversion_aby::{ - convert_to_fp25519, expand_array, expand_shared_array, - }, + ipa_prf::boolean_ops::share_conversion_aby::{convert_to_fp25519, expand_array}, }, rand::thread_rng, - secret_sharing::{replicated::semi_honest::AdditiveShare, SharedValue}, + secret_sharing::SharedValue, test_executor::run, test_fixture::{Reconstruct, Runner, TestWorld}, }; @@ -319,21 +298,13 @@ mod tests { let a = rng.gen::(); - let shared_a = AdditiveShare::(rng.gen::(), rng.gen::()); - let b = expand_array::<_, BA256>(&a, None); - let shared_b = expand_shared_array::<_, BA256>(&shared_a, None); - for i in 0..BA256::BITS as usize { assert_eq!( (i, b.get(i).unwrap_or(Boolean::ZERO)), (i, a.get(i).unwrap_or(Boolean::ZERO)) ); - assert_eq!( - (i, shared_b.get(i).unwrap_or(AdditiveShare::::ZERO)), - (i, shared_a.get(i).unwrap_or(AdditiveShare::::ZERO)) - ); } } } diff --git a/ipa-core/src/protocol/ipa_prf/prf_sharding/mod.rs b/ipa-core/src/protocol/ipa_prf/prf_sharding/mod.rs index 313eefc91..b1fbc0e35 100644 --- a/ipa-core/src/protocol/ipa_prf/prf_sharding/mod.rs +++ b/ipa-core/src/protocol/ipa_prf/prf_sharding/mod.rs @@ -74,8 +74,7 @@ where ); let mut offset = BA7::BITS as usize; - self.sort_key.0.set(offset, self.is_trigger_bit.left()); - self.sort_key.1.set(offset, self.is_trigger_bit.right()); + self.sort_key.set(offset, self.is_trigger_bit.clone()); offset += 1; expand_shared_array_in_place(&mut self.sort_key, &self.timestamp, offset); @@ -244,10 +243,12 @@ pub struct CappedAttributionOutputs { pub capped_attributed_trigger_value: Replicated, } -impl< - BK: SharedValue + CustomArray, - TV: SharedValue + CustomArray, - > ToBitConversionTriples for CappedAttributionOutputs +impl ToBitConversionTriples for CappedAttributionOutputs +where + BK: SharedValue, + TV: SharedValue, + Replicated: CustomArray>, + Replicated: CustomArray>, { type Residual = (); @@ -260,34 +261,15 @@ impl< let i: usize = i.try_into().unwrap(); let bk_bits: usize = BK::BITS.try_into().unwrap(); if i < bk_bits { - BitConversionTriple::new( - role, - self.attributed_breakdown_key_bits.0.get(i).unwrap() == Boolean::ONE, - self.attributed_breakdown_key_bits.1.get(i).unwrap() == Boolean::ONE, - ) + BitConversionTriple::new(role, &self.attributed_breakdown_key_bits.get(i).unwrap()) } else { let i = i - bk_bits; - BitConversionTriple::new( - role, - self.capped_attributed_trigger_value.0.get(i).unwrap() == Boolean::ONE, - self.capped_attributed_trigger_value.1.get(i).unwrap() == Boolean::ONE, - ) + BitConversionTriple::new(role, &self.capped_attributed_trigger_value.get(i).unwrap()) } } - fn into_triples( - self, - role: Role, - indices: I, - ) -> ( - BitDecomposed>>, - Self::Residual, - ) - where - F: PrimeField, - I: IntoIterator, - { - (self.triple_range(role, indices), ()) + fn into_residual(self) -> Self::Residual { + Self::Residual::default(); } } diff --git a/ipa-core/src/protocol/ipa_prf/shuffle/mod.rs b/ipa-core/src/protocol/ipa_prf/shuffle/mod.rs index f9432db6a..ecc9c4f75 100644 --- a/ipa-core/src/protocol/ipa_prf/shuffle/mod.rs +++ b/ipa-core/src/protocol/ipa_prf/shuffle/mod.rs @@ -57,8 +57,7 @@ where let mut offset = BA64::BITS as usize; - y.0.set(offset, input.is_trigger.left()); - y.1.set(offset, input.is_trigger.right()); + y.set(offset, input.is_trigger.clone()); offset += 1; diff --git a/ipa-core/src/protocol/modulus_conversion/convert_shares.rs b/ipa-core/src/protocol/modulus_conversion/convert_shares.rs index ce2a6a369..93bd94242 100644 --- a/ipa-core/src/protocol/modulus_conversion/convert_shares.rs +++ b/ipa-core/src/protocol/modulus_conversion/convert_shares.rs @@ -34,7 +34,7 @@ use pin_project::pin_project; use crate::{ error::Error, exact::ExactSizeStream, - ff::{ArrayAccess, Field, Gf2, PrimeField}, + ff::{ArrayAccessReplicated, Field, Gf2, PrimeField}, helpers::Role, protocol::{ basics::{SecureMul, ZeroPositions}, @@ -43,7 +43,10 @@ use crate::{ RecordId, }, secret_sharing::{ - replicated::{semi_honest::AdditiveShare as Replicated, ReplicatedSecretSharing}, + replicated::{ + semi_honest::{AdditiveShare as Replicated, BorrowReplicated}, + ReplicatedSecretSharing, + }, BitDecomposed, Linear as LinearSecretSharing, SharedValue, }, seq_join::seq_join, @@ -72,9 +75,12 @@ impl BitConversionTriple> { /// If any bits in the bitwise shared input cannot be converted into the given field `F` /// without truncation or if the bit index is out of range for `B`. #[must_use] - pub fn new(helper_role: Role, left: bool, right: bool) -> Self { - let left = F::try_from(u128::from(left)).unwrap(); - let right = F::try_from(u128::from(right)).unwrap(); + pub fn new, T: BorrowReplicated>( + helper_role: Role, + src: &T, + ) -> Self { + let left = F::try_from(u128::from((*src.borrow_left()).into())).unwrap(); + let right = F::try_from(u128::from((*src.borrow_right()).into())).unwrap(); Self(match helper_role { Role::H1 => [ Replicated::new(left, F::ZERO), @@ -95,7 +101,7 @@ impl BitConversionTriple> { } } -pub trait ToBitConversionTriples { +pub trait ToBitConversionTriples: Sized { /// The type of a collection of fields that need to be carried in the stream without conversion. type Residual: Send; // TODO: associated type defaults would be nice here. @@ -119,6 +125,8 @@ pub trait ToBitConversionTriples { BitDecomposed::new(indices.into_iter().map(|i| self.triple(role, i))) } + fn into_residual(self) -> Self::Residual; + fn into_triples( self, role: Role, @@ -129,11 +137,18 @@ pub trait ToBitConversionTriples { ) where F: PrimeField, - I: IntoIterator; + I: IntoIterator, + { + (self.triple_range(role, indices), self.into_residual()) + } } -impl, T: Into> ToBitConversionTriples - for Replicated +impl ToBitConversionTriples for Replicated +where + Self: ArrayAccessReplicated, + for<'a> ::Ref<'a>: BorrowReplicated, + B: SharedValue, + G: Copy + Into, { type Residual = (); @@ -145,24 +160,14 @@ impl, T: Into> ToBitConversionTri let i = usize::try_from(i).unwrap(); BitConversionTriple::new( role, - self.left().get(i).unwrap().into(), - self.right().get(i).unwrap().into(), + &self + .get(i) + .expect("index out of bounds while converting Replicated"), ) } - fn into_triples( - self, - role: Role, - indices: I, - ) -> ( - BitDecomposed>>, - Self::Residual, - ) - where - F: PrimeField, - I: IntoIterator, - { - (self.triple_range(role, indices), ()) + fn into_residual(self) -> Self::Residual { + Self::Residual::default(); } } @@ -174,24 +179,13 @@ impl ToBitConversionTriples for BitDecomposed> { } fn triple(&self, role: Role, i: u32) -> BitConversionTriple> { - const BIT0: u32 = 0; + const BIT0: usize = 0; let i = usize::try_from(i).unwrap(); - BitConversionTriple::new(role, self[i].left()[BIT0], self[i].right()[BIT0]) + BitConversionTriple::new(role, &self[i].get(BIT0).unwrap()) } - fn into_triples( - self, - role: Role, - indices: I, - ) -> ( - BitDecomposed>>, - Self::Residual, - ) - where - F: PrimeField, - I: IntoIterator, - { - (self.triple_range(role, indices), ()) + fn into_residual(self) -> Self::Residual { + Self::Residual::default(); } } @@ -396,7 +390,7 @@ mod tests { use crate::{ error::Error, - ff::{Field, Fp31, Fp32BitPrime, Gf2, PrimeField}, + ff::{ArrayAccessReplicated, Field, Fp31, Fp32BitPrime, Gf2, PrimeField}, helpers::{Direction, Role}, protocol::{ context::{Context, UpgradableContext, UpgradedContext, Validator}, @@ -455,26 +449,18 @@ mod tests { fn triple(&self, role: Role, i: u32) -> BitConversionTriple> { assert_eq!(i, 0, "there is only one convertible bit in TwoBits"); + let i = i.try_into().unwrap(); BitConversionTriple::new( role, - self.convert.left() == Gf2::ONE, - self.convert.right() == Gf2::ONE, + &self + .convert + .get(i) + .expect("index out of bounds while converting TwoBits"), ) } - fn into_triples( - self, - role: Role, - indices: I, - ) -> ( - crate::secret_sharing::BitDecomposed>>, - Self::Residual, - ) - where - F: PrimeField, - I: IntoIterator, - { - (self.triple_range(role, indices), self.keep) + fn into_residual(self) -> Self::Residual { + self.keep } } diff --git a/ipa-core/src/secret_sharing/decomposed.rs b/ipa-core/src/secret_sharing/decomposed.rs index 9abede2e3..50c5b4ee7 100644 --- a/ipa-core/src/secret_sharing/decomposed.rs +++ b/ipa-core/src/secret_sharing/decomposed.rs @@ -2,8 +2,11 @@ use std::{fmt::Debug, ops::Deref}; use crate::{ error::Error, - ff::PrimeField, - secret_sharing::{Linear as LinearSecretSharing, LinearRefOps}, + ff::{ArrayAccessReplicated, PrimeField}, + secret_sharing::{ + replicated::semi_honest::{BorrowReplicated, BorrowReplicatedMut}, + Linear as LinearSecretSharing, LinearRefOps, + }, }; #[derive(Clone, Debug, PartialEq)] @@ -124,3 +127,31 @@ impl IntoIterator for BitDecomposed { self.bits.into_iter() } } + +#[derive(Clone, Copy)] +pub struct BitDecomposedIndex<'a, S>(&'a S); + +impl ArrayAccessReplicated for BitDecomposed { + type Ref<'a> = BitDecomposedIndex<'a, S> where Self: 'a; + + fn get(&self, index: usize) -> Option> { + self.bits.get(index).map(BitDecomposedIndex) + } + + fn set(&mut self, index: usize, e: Self::Ref<'_>) { + *self.bits[index].borrow_left_mut() = e.borrow_left().clone(); + *self.bits[index].borrow_right_mut() = e.borrow_right().clone(); + } +} + +impl<'a, S: BorrowReplicated> BorrowReplicated for BitDecomposedIndex<'a, S> { + type Output = ::Output; + + fn borrow_left(&self) -> &Self::Output { + self.0.borrow_left() + } + + fn borrow_right(&self) -> &Self::Output { + self.0.borrow_left() + } +} diff --git a/ipa-core/src/secret_sharing/replicated/semi_honest/additive_share.rs b/ipa-core/src/secret_sharing/replicated/semi_honest/additive_share.rs index dff9428ab..e6d258578 100644 --- a/ipa-core/src/secret_sharing/replicated/semi_honest/additive_share.rs +++ b/ipa-core/src/secret_sharing/replicated/semi_honest/additive_share.rs @@ -7,7 +7,7 @@ use generic_array::{ArrayLength, GenericArray}; use typenum::Unsigned; use crate::{ - ff::{ArrayAccess, Expand, Field, Serializable}, + ff::{ArrayAccess, ArrayAccessReplicated, Expand, Field, Gf2, Serializable}, secret_sharing::{ replicated::ReplicatedSecretSharing, Linear as LinearSecretSharing, SecretSharing, SharedValue, @@ -15,7 +15,7 @@ use crate::{ }; #[derive(Clone, PartialEq, Eq)] -pub struct AdditiveShare(pub V, pub V); +pub struct AdditiveShare(V, V); #[derive(Clone, PartialEq, Eq)] pub struct ASIterator(pub T, pub T); @@ -60,6 +60,62 @@ impl ReplicatedSecretSharing for AdditiveShare { } } +pub trait BorrowReplicated { + type Output: Clone; + fn borrow_left(&self) -> &Self::Output; + fn borrow_right(&self) -> &Self::Output; +} + +pub trait BorrowReplicatedMut: BorrowReplicated { + fn borrow_left_mut(&mut self) -> &mut Self::Output; + fn borrow_right_mut(&mut self) -> &mut Self::Output; +} + +#[derive(Clone, Copy)] +pub struct AdditiveShareGf2(Gf2, Gf2); + +impl BorrowReplicated for AdditiveShareGf2 { + type Output = bool; + + fn borrow_left(&self) -> &bool { + if self.0.into() { + &true + } else { + &false + } + } + + fn borrow_right(&self) -> &bool { + if self.1.into() { + &true + } else { + &false + } + } +} + +impl BorrowReplicated for AdditiveShare { + type Output = V; + + fn borrow_left(&self) -> &V { + &self.0 + } + + fn borrow_right(&self) -> &V { + &self.1 + } +} + +impl BorrowReplicatedMut for AdditiveShare { + fn borrow_left_mut(&mut self) -> &mut V { + &mut self.0 + } + + fn borrow_right_mut(&mut self) -> &mut V { + &mut self.1 + } +} + impl AdditiveShare where Self: Serializable, @@ -249,10 +305,10 @@ where } /// Implement `ArrayAccess` for `AdditiveShare` over `SharedValue` that implements `ArrayAccess` -impl ArrayAccess for AdditiveShare +impl ArrayAccess for AdditiveShare where - S: ArrayAccess + SharedValue, - ::Output: SharedValue, + S: SharedValue + ArrayAccess, + V: SharedValue, { type Output = AdditiveShare<::Output>; type Iter<'a> = ASIterator>; @@ -274,6 +330,30 @@ where } } +impl ArrayAccessReplicated for AdditiveShare +where + S: SharedValue + ArrayAccess, + T: From, + bool: From, +{ + // This would ideally be an `AdditiveShareRef` type that holds two references, + // but due to Galois fields not being indexable for `Gf2` values currently, + // it is not done that way. + type Ref<'a> = AdditiveShareGf2; + + fn get(&self, index: usize) -> Option> { + self.0 + .get(index) + .zip(self.1.get(index)) + .map(|v| AdditiveShareGf2(bool::from(v.0).into(), bool::from(v.1).into())) + } + + fn set(&mut self, index: usize, e: Self::Ref<'_>) { + self.0.set(index, >::from(e.0).into()); + self.1.set(index, >::from(e.1).into()); + } +} + impl Expand for AdditiveShare where S: Expand + SharedValue, @@ -312,7 +392,7 @@ where { let mut result = AdditiveShare::::ZERO; for (i, v) in iter.into_iter().enumerate() { - result.set(i, v); + ArrayAccess::set(&mut result, i, v); } result } diff --git a/ipa-core/src/secret_sharing/replicated/semi_honest/mod.rs b/ipa-core/src/secret_sharing/replicated/semi_honest/mod.rs index f35ebdd81..578519e7e 100644 --- a/ipa-core/src/secret_sharing/replicated/semi_honest/mod.rs +++ b/ipa-core/src/secret_sharing/replicated/semi_honest/mod.rs @@ -1,3 +1,3 @@ mod additive_share; -pub use additive_share::{ASIterator, AdditiveShare}; +pub use additive_share::{ASIterator, AdditiveShare, BorrowReplicated, BorrowReplicatedMut};