Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Clean up Field trait #958

Merged
merged 3 commits into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
72 changes: 10 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 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so we don't need this awesome macro anymore? :)


// 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 @@ -237,6 +180,8 @@ macro_rules! impl_serializable_trait {

#[cfg(all(test, unit_test))]
mod fallible_serialization_tests {
use rand::{thread_rng, Rng};

use super::*;

/// [`https://github.com/private-attribution/ipa/issues/911`]
Expand All @@ -252,6 +197,8 @@ macro_rules! impl_serializable_trait {
"Padding only makes sense for lengths that are not multiples of 8."
);

let mut rng = thread_rng();

let mut non_zero_padding = $name::ZERO.0;
non_zero_padding.set($bits, true);
assert_eq!(
Expand All @@ -262,12 +209,13 @@ 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();

let mut rnd_value = $name::ZERO.0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it check something that this test doesn't already?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I don't think it does. I added this one to replace the test case in this function that deserialized the ONE value, but I can drop it in favor of the other test.

rnd_value[..$bits].fill_with(|_| rng.gen());
deserialize(rnd_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 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to use a u8 as a base and increase the SourceLength? I think even Aes uses a GenericArray over u8

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea we have #951 for that

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 = false_value.clone().into();
let true_value = true_value.clone().into();
let condition = B::expand(condition).into();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe use From so the types for these values become obvious?

// 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
Loading