Skip to content

Commit

Permalink
Vectorized reveal
Browse files Browse the repository at this point in the history
This change also removes the `B: RecordBinding` type parameter on the
`Reveal` trait, which is no longer used after removal of IPAv2.
  • Loading branch information
andyleiserson committed Feb 28, 2024
1 parent 57b4d49 commit 7df7b29
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 53 deletions.
8 changes: 5 additions & 3 deletions ipa-core/src/protocol/basics/check_zero.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,11 @@ pub async fn check_zero<C: Context, F: Field + FromRandom>(
let rv_share = r_sharing
.multiply(v, ctx.narrow(&Step::MultiplyWithR), record_id)
.await?;
let rv = rv_share
.reveal(ctx.narrow(&Step::RevealR), record_id)
.await?;
let rv = F::from_array(
&rv_share
.reveal(ctx.narrow(&Step::RevealR), record_id)
.await?,
);

Ok(rv == F::ZERO)
}
Expand Down
8 changes: 5 additions & 3 deletions ipa-core/src/protocol/basics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ pub use sum_of_product::SumOfProducts;
use crate::{
ff::Field,
protocol::{context::Context, RecordId},
secret_sharing::{replicated::semi_honest::AdditiveShare, SecretSharing, SharedValue},
secret_sharing::{
replicated::semi_honest::AdditiveShare, SecretSharing, SharedValue, Vectorizable,
},
};
#[cfg(feature = "descriptive-gate")]
use crate::{
Expand All @@ -30,10 +32,10 @@ use crate::{
},
};

pub trait BasicProtocols<C: Context, V: SharedValue>:
pub trait BasicProtocols<C: Context, V: SharedValue + Vectorizable<N>, const N: usize = 1>:
SecretSharing<V>
+ Reshare<C, RecordId>
+ Reveal<C, RecordId, Output = V>
+ Reveal<C, N, Output = <V as Vectorizable<N>>::Array>
+ SecureMul<C>
+ ShareKnownValue<C, V>
+ SumOfProducts<C>
Expand Down
107 changes: 73 additions & 34 deletions ipa-core/src/protocol/basics/reveal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ use embed_doc_image::embed_doc_image;
use crate::{
error::Error,
helpers::{Direction, Role},
protocol::{context::Context, RecordBinding, RecordId},
secret_sharing::{replicated::semi_honest::AdditiveShare as Replicated, SharedValue},
protocol::{context::Context, RecordId},
secret_sharing::{
replicated::semi_honest::AdditiveShare as Replicated, SharedValue, Vectorizable,
},
};
#[cfg(feature = "descriptive-gate")]
use crate::{
Expand All @@ -17,20 +19,20 @@ use crate::{

/// Trait for reveal protocol to open a shared secret to all helpers inside the MPC ring.
#[async_trait]
pub trait Reveal<C: Context, B: RecordBinding>: Sized {
pub trait Reveal<C: Context, const N: usize>: Sized {
type Output;
/// reveal the secret to all helpers in MPC circuit. Note that after method is called,
/// it must be assumed that the secret value has been revealed to at least one of the helpers.
/// Even in case when method never terminates, returns an error, etc.
async fn reveal<'fut>(&self, ctx: C, record_binding: B) -> Result<Self::Output, Error>
async fn reveal<'fut>(&self, ctx: C, record_id: RecordId) -> Result<Self::Output, Error>
where
C: 'fut;

/// partial reveal protocol to open a shared secret to all helpers except helper `left_out` inside the MPC ring.
async fn partial_reveal<'fut>(
&self,
ctx: C,
record_binding: B,
record_id: RecordId,
left_out: Role,
) -> Result<Option<Self::Output>, Error>
where
Expand All @@ -50,26 +52,33 @@ pub trait Reveal<C: Context, B: RecordBinding>: Sized {
/// i.e. their own shares and received share.
#[async_trait]
#[embed_doc_image("reveal", "images/reveal.png")]
impl<C: Context, V: SharedValue> Reveal<C, RecordId> for Replicated<V> {
type Output = V;
impl<C: Context, V: SharedValue + Vectorizable<N>, const N: usize> Reveal<C, N>
for Replicated<V, N>
{
type Output = <V as Vectorizable<N>>::Array;

async fn reveal<'fut>(&self, ctx: C, record_id: RecordId) -> Result<V, Error>
async fn reveal<'fut>(
&self,
ctx: C,
record_id: RecordId,
) -> Result<<V as Vectorizable<N>>::Array, Error>
where
C: 'fut,
{
let (left, right) = self.as_tuple();
let left = self.left_arr();
let right = self.right_arr();

ctx.send_channel(ctx.role().peer(Direction::Right))
ctx.send_channel::<<V as Vectorizable<N>>::Array>(ctx.role().peer(Direction::Right))
.send(record_id, left)
.await?;

// Sleep until `helper's left` sends their share
let share = ctx
let share: <V as Vectorizable<N>>::Array = ctx
.recv_channel(ctx.role().peer(Direction::Left))
.receive(record_id)
.await?;

Ok(left + right + share)
Ok(share + left + right)
}

/// TODO: implement reveal through partial reveal where `left_out` is optional
Expand All @@ -78,28 +87,29 @@ impl<C: Context, V: SharedValue> Reveal<C, RecordId> for Replicated<V> {
ctx: C,
record_id: RecordId,
left_out: Role,
) -> Result<Option<V>, Error>
) -> Result<Option<<V as Vectorizable<N>>::Array>, Error>
where
C: 'fut,
{
let (left, right) = self.as_tuple();
let left = self.left_arr();
let right = self.right_arr();

// send except to left_out
if ctx.role().peer(Direction::Right) != left_out {
ctx.send_channel(ctx.role().peer(Direction::Right))
ctx.send_channel::<<V as Vectorizable<N>>::Array>(ctx.role().peer(Direction::Right))
.send(record_id, left)
.await?;
}

if ctx.role() == left_out {
Ok(None)
} else {
let share = ctx
let share: <V as Vectorizable<N>>::Array = ctx
.recv_channel(ctx.role().peer(Direction::Left))
.receive(record_id)
.await?;

Ok(Some(left + right + share))
Ok(Some(share + left + right))
}
}
}
Expand All @@ -110,16 +120,14 @@ impl<C: Context, V: SharedValue> Reveal<C, RecordId> for Replicated<V> {
/// indeed match.
#[cfg(feature = "descriptive-gate")]
#[async_trait]
impl<'a, F: ExtendableField> Reveal<UpgradedMaliciousContext<'a, F>, RecordId>
for MaliciousReplicated<F>
{
type Output = F;
impl<'a, F: ExtendableField> Reveal<UpgradedMaliciousContext<'a, F>, 1> for MaliciousReplicated<F> {
type Output = <F as Vectorizable<1>>::Array;

async fn reveal<'fut>(
&self,
ctx: UpgradedMaliciousContext<'a, F>,
record_id: RecordId,
) -> Result<F, Error>
) -> Result<<F as Vectorizable<1>>::Array, Error>
where
UpgradedMaliciousContext<'a, F>: 'fut,
{
Expand Down Expand Up @@ -147,7 +155,7 @@ impl<'a, F: ExtendableField> Reveal<UpgradedMaliciousContext<'a, F>, RecordId>
.await?;

if share_from_left == share_from_right {
Ok(left + right + share_from_left)
Ok((left + right + share_from_left).into_array())
} else {
Err(Error::MaliciousRevealFailed)
}
Expand All @@ -158,7 +166,7 @@ impl<'a, F: ExtendableField> Reveal<UpgradedMaliciousContext<'a, F>, RecordId>
ctx: UpgradedMaliciousContext<'a, F>,
record_id: RecordId,
left_out: Role,
) -> Result<Option<F>, Error>
) -> Result<Option<<F as Vectorizable<1>>::Array>, Error>
where
UpgradedMaliciousContext<'a, F>: 'fut,
{
Expand Down Expand Up @@ -190,7 +198,7 @@ impl<'a, F: ExtendableField> Reveal<UpgradedMaliciousContext<'a, F>, RecordId>
.await?;

if share_from_left == share_from_right {
Ok(Some(left + right + share_from_left))
Ok(Some((left + right + share_from_left).into_array()))
} else {
Err(Error::MaliciousRevealFailed)
}
Expand All @@ -202,11 +210,11 @@ impl<'a, F: ExtendableField> Reveal<UpgradedMaliciousContext<'a, F>, RecordId>
mod tests {
use std::iter::zip;

use futures::future::{try_join, try_join3};
use futures::future::{join_all, try_join, try_join3};

use crate::{
error::Error,
ff::{Field, Fp31},
ff::{Field, Fp31, Fp32BitPrime},
helpers::Direction,
protocol::{
basics::Reveal,
Expand All @@ -221,17 +229,45 @@ mod tests {
AdditiveShare as MaliciousReplicated, ExtendableField,
ThisCodeIsAuthorizedToDowngradeFromMalicious,
},
IntoShares,
IntoShares, SharedValue,
},
test_fixture::{join3v, Runner, TestWorld},
};

#[tokio::test]
pub async fn simple() -> Result<(), Error> {
type TestField = Fp31;

let mut rng = thread_rng();
let world = TestWorld::default();

let input = rng.gen::<TestField>();
let results = world
.semi_honest(input, |ctx, share| async move {
TestField::from_array(
&share
.reveal(ctx.set_total_records(1), RecordId::from(0))
.await
.unwrap(),
)
})
.await;

assert_eq!(input, results[0]);
assert_eq!(input, results[1]);
assert_eq!(input, results[2]);

Ok(())
}

#[tokio::test]
pub async fn vectorized() -> Result<(), Error> {
type TestField = [Fp32BitPrime; 32];

let mut rng = thread_rng();
let world = TestWorld::default();

let input = rng.gen::<Fp31>();
let input = rng.gen::<TestField>();
let results = world
.semi_honest(input, |ctx, share| async move {
share
Expand All @@ -250,6 +286,8 @@ mod tests {

#[tokio::test]
pub async fn malicious() -> Result<(), Error> {
type TestField = Fp31;

let mut rng = thread_rng();
let world = TestWorld::default();
let sh_ctx = world.malicious_contexts();
Expand All @@ -262,18 +300,19 @@ mod tests {
.unwrap();

let record_id = RecordId::from(0);
let input: Fp31 = rng.gen();
let input: TestField = rng.gen();

let m_shares = join3v(
zip(m_ctx.iter(), input.share_with(&mut rng))
.map(|(m_ctx, share)| async { m_ctx.upgrade(share).await }),
)
.await;

let results = join3v(
zip(m_ctx.clone().into_iter(), m_shares)
.map(|(m_ctx, m_share)| async move { m_share.reveal(m_ctx, record_id).await }),
)
let results = join_all(zip(m_ctx.clone().into_iter(), m_shares).map(
|(m_ctx, m_share)| async move {
TestField::from_array(&m_share.reveal(m_ctx, record_id).await.unwrap())
},
))
.await;

assert_eq!(input, results[0]);
Expand Down
8 changes: 5 additions & 3 deletions ipa-core/src/protocol/boolean/comparison.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,11 @@ where
let r = rbg.generate(record_id).await?;

// Mask `a` with random `r` and reveal.
let b = (r.b_p + a)
.reveal(ctx.narrow(&Step::Reveal), record_id)
.await?;
let b = F::from_array(
&(r.b_p + a)
.reveal(ctx.narrow(&Step::Reveal), record_id)
.await?,
);

let RBounds { r_lo, r_hi, invert } = compute_r_bounds(b.as_u128(), c, F::PRIME.into());

Expand Down
9 changes: 6 additions & 3 deletions ipa-core/src/protocol/boolean/solved_bits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::{
error::Error,
ff::{Field, PrimeField},
protocol::{
basics::Reveal,
boolean::{
bitwise_less_than_prime::BitwiseLessThanPrime, generate_random_bits::one_random_bit,
},
Expand All @@ -18,7 +19,7 @@ use crate::{
AdditiveShare as MaliciousReplicated, DowngradeMalicious, ExtendableField,
UnauthorizedDowngradeWrapper,
},
BitDecomposed, Linear as LinearSecretSharing, LinearRefOps, SecretSharing,
BitDecomposed, Linear as LinearSecretSharing, LinearRefOps, SecretSharing, Vectorizable,
},
};

Expand Down Expand Up @@ -128,12 +129,14 @@ async fn is_less_than_p<F, C, S>(ctx: C, record_id: RecordId, b_b: &[S]) -> Resu
where
F: PrimeField,
C: Context,
S: LinearSecretSharing<F> + BasicProtocols<C, F>,
S: LinearSecretSharing<F>
+ BasicProtocols<C, F>
+ Reveal<C, 1, Output = <F as Vectorizable<1>>::Array>,
{
let c_b =
BitwiseLessThanPrime::less_than_prime(ctx.narrow(&Step::IsPLessThanB), record_id, b_b)
.await?;
if c_b.reveal(ctx.narrow(&Step::RevealC), record_id).await? == F::ZERO {
if F::from_array(&c_b.reveal(ctx.narrow(&Step::RevealC), record_id).await?) == F::ZERO {
return Ok(false);
}
Ok(true)
Expand Down
5 changes: 4 additions & 1 deletion ipa-core/src/protocol/context/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use crate::{
protocol::basics::Reveal,
protocol::context::Context,
protocol::context::{MaliciousContext, UpgradedMaliciousContext},
secret_sharing::SharedValue,
sync::Arc,
};

Expand Down Expand Up @@ -229,7 +230,9 @@ impl<'a, F: ExtendableField> Validator<MaliciousContext<'a>, F> for Malicious<'a
.validate_ctx
.narrow(&ValidateStep::RevealR)
.set_total_records(1);
let r = self.r_share.reveal(narrow_ctx, RecordId::FIRST).await?;
let r = <F as ExtendableField>::ExtendedField::from_array(
&self.r_share.reveal(narrow_ctx, RecordId::FIRST).await?,
);
let t = u_share - &(w_share * r);

let check_zero_ctx = self
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,10 +169,10 @@ where
match ctx.role() {
Role::H1 => Ok(AdditiveShare::<Fp25519>::new(
Fp25519::from(sh_s.left()).neg(),
Fp25519::from(y.unwrap()),
Fp25519::from(BA256::from_array(&y.unwrap())),
)),
Role::H2 => Ok(AdditiveShare::<Fp25519>::new(
Fp25519::from(y.unwrap()),
Fp25519::from(BA256::from_array(&y.unwrap())),
Fp25519::from(sh_r.right()).neg(),
)),
Role::H3 => Ok(AdditiveShare::<Fp25519>::new(
Expand Down
9 changes: 6 additions & 3 deletions ipa-core/src/protocol/ipa_prf/prf_eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ use crate::{
prss::SharedRandomness,
RecordId,
},
secret_sharing::replicated::{semi_honest::AdditiveShare, ReplicatedSecretSharing},
secret_sharing::{
replicated::{semi_honest::AdditiveShare, ReplicatedSecretSharing},
SharedValue,
},
};

#[derive(Step)]
Expand Down Expand Up @@ -88,8 +91,8 @@ where
.await?;

//reconstruct (z,R)
let gr: RP25519 = sh_gr.reveal(ctx.narrow(&Step::RevealR), record_id).await?;
let z = y.reveal(ctx.narrow(&Step::Revealz), record_id).await?;
let gr = RP25519::from_array(&sh_gr.reveal(ctx.narrow(&Step::RevealR), record_id).await?);
let z = Fp25519::from_array(&y.reveal(ctx.narrow(&Step::Revealz), record_id).await?);

//compute R^(1/z) to u64
Ok(u64::from(gr * (z.invert())))
Expand Down
Loading

0 comments on commit 7df7b29

Please sign in to comment.