Skip to content

Commit

Permalink
Merge pull request #958 from andyleiserson/field-cleanup
Browse files Browse the repository at this point in the history
Clean up `Field` trait
  • Loading branch information
andyleiserson authored Feb 23, 2024
2 parents 5293492 + fb191e0 commit 57b4d49
Show file tree
Hide file tree
Showing 19 changed files with 342 additions and 235 deletions.
16 changes: 0 additions & 16 deletions ipa-core/src/ff/boolean.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,22 +197,6 @@ impl FromRandomU128 for Boolean {
}
}

impl Vectorizable<64> for Boolean {
type Array = crate::ff::boolean_array::BA64;
}

impl FieldVectorizable<64> for Boolean {
type ArrayAlias = crate::ff::boolean_array::BA64;
}

impl Vectorizable<256> for Boolean {
type Array = crate::ff::boolean_array::BA256;
}

impl FieldVectorizable<256> for Boolean {
type ArrayAlias = crate::ff::boolean_array::BA256;
}

#[cfg(all(test, unit_test))]
mod test {
use generic_array::GenericArray;
Expand Down
64 changes: 2 additions & 62 deletions ipa-core/src/ff/boolean_array.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use bitvec::{
prelude::{bitarr, BitArr, Lsb0},
prelude::{BitArr, Lsb0},
slice::Iter,
};
use generic_array::GenericArray;
Expand All @@ -9,7 +9,7 @@ use crate::{
error::LengthError,
ff::{boolean::Boolean, ArrayAccess, ArrayBuilder, Field, Serializable, U128Conversions},
protocol::prss::{FromRandom, FromRandomU128},
secret_sharing::{Block, FieldVectorizable, SharedValue, StdArray, Vectorizable},
secret_sharing::{Block, SharedValue, StdArray, Vectorizable},
};

/// The implementation below cannot be constrained without breaking Rust's
Expand Down Expand Up @@ -78,64 +78,11 @@ where
}
}

/// A value of ONE has a one in the first element of the bit array, followed by `$bits-1` zeros.
/// This macro uses a bit of recursive repetition to produce those zeros.
///
/// The longest call is 8 bits, which involves `2(n+1)` macro expansions in addition to `bitarr!`.
macro_rules! bitarr_one {

// The binary value of `$bits-1` is expanded in MSB order for each of the values we care about.
// e.g., 20 =(-1)=> 19 =(binary)=> 0b10011 =(expand)=> 1 0 0 1 1

(2) => { bitarr_one!(1) };
(3) => { bitarr_one!(1 0) };
(4) => { bitarr_one!(1 1) };
(5) => { bitarr_one!(1 0 0) };
(6) => { bitarr_one!(1 0 1) };
(7) => { bitarr_one!(1 1 0) };
(8) => { bitarr_one!(1 1 1) };
(20) => { bitarr_one!(1 0 0 1 1) };
(32) => { bitarr_one!(1 1 1 1 1) };
(64) => { bitarr_one!(1 1 1 1 1 1) };
(112) => { bitarr_one!(1 1 0 1 1 1 1) };
(256) => { bitarr_one!(1 1 1 1 1 1 1 1) };

// Incrementally convert 1 or 0 into `[0,]` or `[]` as needed for the recursion step.
// This also reverses the bit order so that the MSB comes last, as needed for recursion.

// This passes a value back once the conversion is done.
($([$($x:tt)*])*) => { bitarr_one!(@r $([$($x)*])*) };
// This converts one 1 into `[0,]`.
($([$($x:tt)*])* 1 $($y:tt)*) => { bitarr_one!([0,] $([$($x)*])* $($y)*) };
// This converts one 0 into `[]`.
($([$($x:tt)*])* 0 $($y:tt)*) => { bitarr_one!([] $([$($x)*])* $($y)*) };

// Recursion step.

// This is where recursion ends with a `BitArray`.
(@r [$($x:tt)*]) => { bitarr![const u8, Lsb0; 1, $($x)*] };
// This is the recursion workhorse. It takes a list of lists. The outer lists are bracketed.
// The inner lists contain any form that can be repeated and concatenated, which probably
// means comma-separated values with a trailing comma.
// The first value is repeated once.
// The second value is repeated twice and merged into the first value.
// The third and subsequent values are repeated twice and shifted along one place.
// One-valued bits are represented as `[0,]`, zero-valued bits as `[]`.
(@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, $deser_type:tt) => {
boolean_array_impl!($modname, $name, $bits, $deser_type);

// TODO(812): remove this impl; BAs are not field elements.
impl Field for $name {
const NAME: &'static str = stringify!($name);

const ONE: Self = Self(bitarr_one!($bits));
}

impl U128Conversions for $name {
fn truncate_from<T: Into<u128>>(v: T) -> Self {
let v = v.into();
Expand Down Expand Up @@ -193,10 +140,6 @@ macro_rules! boolean_array_impl_small {
Self::truncate_from(src)
}
}

impl FieldVectorizable<1> for $name {
type ArrayAlias = StdArray<$name, 1>;
}
};
}

Expand Down Expand Up @@ -262,9 +205,6 @@ macro_rules! impl_serializable_trait {
let min_value = $name::ZERO.0;
deserialize(min_value).unwrap();

let one = $name::ONE.0;
deserialize(one).unwrap();

let mut max_value = $name::ZERO.0;
max_value[..$bits].fill(true);
deserialize(max_value).unwrap();
Expand Down
50 changes: 11 additions & 39 deletions ipa-core/src/ff/ec_prime_field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@ use std::convert::Infallible;

use curve25519_dalek::scalar::Scalar;
use generic_array::GenericArray;
use hkdf::Hkdf;
use sha2::Sha256;
use typenum::U32;
use typenum::{U2, U32};

use crate::{
ff::{boolean_array::BA256, Field, Serializable, U128Conversions},
ff::{boolean_array::BA256, Field, Serializable},
impl_shared_value_common,
protocol::prss::FromRandomU128,
protocol::prss::FromRandom,
secret_sharing::{Block, FieldVectorizable, SharedValue, StdArray, Vectorizable},
};

Expand Down Expand Up @@ -193,43 +191,17 @@ impl Field for Fp25519 {
const ONE: Fp25519 = Fp25519::ONE;
}

// TODO(812): remove these impls
impl U128Conversions for Fp25519 {
///both following methods are based on hashing and do not allow to actually convert elements in Fp25519
/// from or into u128. However it is sufficient to generate random elements in Fp25519
fn as_u128(&self) -> u128 {
unimplemented!()
}
impl FromRandom for Fp25519 {
type SourceLength = U2;

///PRSS uses `truncate_from function`, we need to expand the u128 using a PRG (Sha256) to a [u8;32]
fn truncate_from<T: Into<u128>>(_v: T) -> Self {
unimplemented!()
}
}

impl FromRandomU128 for Fp25519 {
fn from_random_u128(v: u128) -> Self {
let hk = Hkdf::<Sha256>::new(None, &v.to_le_bytes());
let mut okm = [0u8; 32];
//error invalid length from expand only happens when okm is very large
hk.expand(&[], &mut okm).unwrap();
Fp25519::deserialize_infallible(&okm.into())
}
}

///implement `TryFrom` since required by Field
impl TryFrom<u128> for Fp25519 {
type Error = crate::error::Error;

fn try_from(v: u128) -> Result<Self, Self::Error> {
let mut bits = [0u8; 32];
bits[..].copy_from_slice(&v.to_le_bytes());
let f: Fp25519 = Fp25519::ONE;
f.serialize((&mut bits).into());
Ok(f)
fn from_random(src: GenericArray<u128, Self::SourceLength>) -> Self {
let mut src_bytes = [0u8; 32];
src_bytes[0..16].copy_from_slice(&src[0].to_le_bytes());
src_bytes[16..32].copy_from_slice(&src[1].to_le_bytes());
// Reduces mod order
Fp25519::deserialize_infallible(<&GenericArray<u8, U32>>::from(&src_bytes))
}
}
// TODO(812): end remove impls

#[cfg(all(test, unit_test))]
mod test {
Expand Down
6 changes: 2 additions & 4 deletions ipa-core/src/ff/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ use std::{
use typenum::{U1, U4};

use crate::{
error,
protocol::prss::FromRandomU128,
protocol::prss::FromRandom,
secret_sharing::{Block, FieldVectorizable, SharedValue, Vectorizable},
};

Expand All @@ -26,8 +25,7 @@ pub trait Field:
SharedValue
+ Mul<Self, Output = Self>
+ MulAssign<Self>
+ FromRandomU128
+ TryFrom<u128, Error = error::Error>
+ FromRandom
+ Into<Self::Storage>
+ Vectorizable<1>
+ FieldVectorizable<1, ArrayAlias = <Self as Vectorizable<1>>::Array>
Expand Down
4 changes: 2 additions & 2 deletions ipa-core/src/protocol/basics/check_zero.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
protocol::{
basics::{reveal::Reveal, SecureMul},
context::Context,
prss::SharedRandomness,
prss::{FromRandom, SharedRandomness},
RecordId,
},
secret_sharing::replicated::semi_honest::AdditiveShare as Replicated,
Expand Down Expand Up @@ -47,7 +47,7 @@ pub(crate) enum Step {
/// ## Errors
/// Lots of things may go wrong here, from timeouts to bad output. They will be signalled
/// back via the error response
pub async fn check_zero<C: Context, F: Field>(
pub async fn check_zero<C: Context, F: Field + FromRandom>(
ctx: C,
record_id: RecordId,
v: &Replicated<F>,
Expand Down
67 changes: 63 additions & 4 deletions ipa-core/src/protocol/basics/if_else.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
use crate::{
error::Error,
ff::Field,
protocol::{basics::SecureMul, context::Context, RecordId},
secret_sharing::{Linear as LinearSecretSharing, LinearRefOps},
ff::{boolean::Boolean, Field},
protocol::{
basics::{
mul::{boolean_array_multiply, BooleanArrayMul},
SecureMul,
},
context::Context,
RecordId,
},
secret_sharing::{replicated::semi_honest::AdditiveShare, LinearRefOps},
};

/// Multiplexer.
///
/// Returns `true_value` if `condition` is a share of 1, else `false_value`.
/// If the arguments are vectors, all must have the same dimension and the
/// operation is performed element-wise.
///
/// Each `condition` must be a share of either 0 or 1.
/// Each `true_value` and `false_value` may be any type supporting multiplication.
///
/// # Errors
/// If the protocol fails to execute.
pub async fn if_else<F, C, S>(
Expand All @@ -18,7 +33,7 @@ pub async fn if_else<F, C, S>(
where
F: Field,
C: Context,
S: LinearSecretSharing<F> + SecureMul<C>,
S: SecureMul<C>,
for<'a> &'a S: LinearRefOps<'a, S, F>,
{
// If `condition` is a share of 1 (true), then
Expand All @@ -34,3 +49,47 @@ where
.multiply(&(true_value - false_value), ctx, record_id)
.await?)
}

/// Wide multiplexer.
///
/// Returns `true_value` if `condition` is a share of 1, else `false_value`.
/// `condition` must be a single shared value. `true_value` and `false_value`
/// may be vectors, in which case one or the other is selected in its entirety,
/// depending on `condition`.
///
/// `condition` must be a share of either 0 or 1.
/// `true_value` and `false_value` may be any type supporting multiplication,
/// vectors of a type supporting multiplication, or a type convertible to
/// one of those.
///
/// # Errors
/// If the protocol fails to execute.
pub async fn select<C, B>(
ctx: C,
record_id: RecordId,
condition: &AdditiveShare<Boolean>,
true_value: &B,
false_value: &B,
) -> Result<B, Error>
where
C: Context,
B: Clone + BooleanArrayMul,
{
let false_value = B::Vectorized::from(false_value.clone());
let true_value = B::Vectorized::from(true_value.clone());
let condition = B::Vectorized::from(B::expand(condition));
// If `condition` is a share of 1 (true), then
// false_value + condition * (true_value - false_value)
// = false_value + true_value - false_value
// = true_value
//
// If `condition` is a share of 0 (false), then
// false_value + condition * (true_value - false_value)
// = false_value + 0
// = false_value
let product =
boolean_array_multiply::<_, B>(ctx, record_id, &condition, &(true_value - &false_value))
.await?;

Ok((false_value + &product).into())
}
4 changes: 2 additions & 2 deletions ipa-core/src/protocol/basics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ pub mod sum_of_product;

#[cfg(feature = "descriptive-gate")]
pub use check_zero::check_zero;
pub use if_else::if_else;
pub use mul::{MultiplyZeroPositions, SecureMul, ZeroPositions};
pub use if_else::{if_else, select};
pub use mul::{BooleanArrayMul, MultiplyZeroPositions, SecureMul, ZeroPositions};
pub use reshare::Reshare;
pub use reveal::Reveal;
pub use share_known_value::ShareKnownValue;
Expand Down
Loading

0 comments on commit 57b4d49

Please sign in to comment.