-
Notifications
You must be signed in to change notification settings - Fork 25
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
Clean up Field
trait
#958
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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; | ||
|
@@ -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 | ||
|
@@ -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(); | ||
|
@@ -193,10 +140,6 @@ macro_rules! boolean_array_impl_small { | |
Self::truncate_from(src) | ||
} | ||
} | ||
|
||
impl FieldVectorizable<1> for $name { | ||
type ArrayAlias = StdArray<$name, 1>; | ||
} | ||
}; | ||
} | ||
|
||
|
@@ -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`] | ||
|
@@ -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!( | ||
|
@@ -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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does it check something that this test doesn't already? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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(); | ||
} | ||
} | ||
}; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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}, | ||
}; | ||
|
||
|
@@ -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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 { | ||
|
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>( | ||
|
@@ -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 | ||
|
@@ -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(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe use |
||
// 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()) | ||
} |
There was a problem hiding this comment.
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? :)