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

offline-phase: lowgear: inverse_tuples: Generate inverse tuples #75

Merged
merged 1 commit into from
Apr 13, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 6 additions & 0 deletions offline-phase/src/beaver_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,12 @@ impl<C: CurveGroup> ValueMacBatch<C> {
self.inner.iter_mut()
}

/// Split the batch in two at the given index
pub fn split_at(&self, i: usize) -> (Self, Self) {
let (lhs, rhs) = self.inner.split_at(i);
(Self { inner: lhs.to_vec() }, Self { inner: rhs.to_vec() })
}

/// Create a new ValueMacBatch from a batch of values and macs
pub fn from_parts(values: &[Scalar<C>], macs: &[Scalar<C>]) -> Self {
assert_eq!(values.len(), macs.len());
Expand Down
45 changes: 44 additions & 1 deletion offline-phase/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub(crate) mod test_helpers {
PARTY0, PARTY1,
};
use futures::Future;
use itertools::Itertools;
use mp_spdz_rs::fhe::{
ciphertext::Ciphertext,
keys::{BGVKeypair, BGVPublicKey},
Expand All @@ -35,7 +36,7 @@ pub(crate) mod test_helpers {
};
use rand::thread_rng;

use crate::lowgear::LowGear;
use crate::{beaver_source::ValueMacBatch, lowgear::LowGear};

/// The curve used for testing in this crate
pub type TestCurve = ark_bn254::G1Projective;
Expand Down Expand Up @@ -78,6 +79,48 @@ pub(crate) mod test_helpers {
key.encrypt(&pt)
}

/// Generate random mock triples for the Beaver trick
#[allow(clippy::type_complexity)]
pub fn generate_triples(
n: usize,
) -> (Vec<Scalar<TestCurve>>, Vec<Scalar<TestCurve>>, Vec<Scalar<TestCurve>>) {
let mut rng = thread_rng();
let a = (0..n).map(|_| Scalar::<TestCurve>::random(&mut rng)).collect_vec();
let b = (0..n).map(|_| Scalar::<TestCurve>::random(&mut rng)).collect_vec();
let c = (0..n).map(|_| Scalar::<TestCurve>::random(&mut rng)).collect_vec();

(a, b, c)
}

/// Generate authenticated secret shares of a given set of values
pub fn generate_authenticated_secret_shares(
values: &[Scalar<TestCurve>],
mac_key: Scalar<TestCurve>,
) -> (ValueMacBatch<TestCurve>, ValueMacBatch<TestCurve>) {
let (shares1, shares2) = generate_secret_shares(values);
let macs = values.iter().map(|value| *value * mac_key).collect_vec();
let (macs1, macs2) = generate_secret_shares(&macs);

(ValueMacBatch::from_parts(&shares1, &macs1), ValueMacBatch::from_parts(&shares2, &macs2))
}

/// Generate secret shares of a set of values
pub fn generate_secret_shares(
values: &[Scalar<TestCurve>],
) -> (Vec<Scalar<TestCurve>>, Vec<Scalar<TestCurve>>) {
let mut rng = thread_rng();
let mut shares1 = Vec::with_capacity(values.len());
let mut shares2 = Vec::with_capacity(values.len());
for value in values {
let share1 = Scalar::<TestCurve>::random(&mut rng);
let share2 = value - share1;
shares1.push(share1);
shares2.push(share2);
}

(shares1, shares2)
}

/// Run a two-party method with a `LowGear` instance setup and in scope
pub async fn mock_lowgear<F, S, T>(f: F) -> (T, T)
where
Expand Down
92 changes: 88 additions & 4 deletions offline-phase/src/lowgear/inverse_tuples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,105 @@

use ark_ec::CurveGroup;
use ark_mpc::network::MpcNetwork;
use itertools::Itertools;

use crate::error::LowGearError;

use super::LowGear;

impl<C: CurveGroup, N: MpcNetwork<C> + Unpin + Send> LowGear<C, N> {
/// Generate a set of inverse tuples
///
/// 1. Multiply the left and right hand side randomness. We consider one of
/// these random values to be a multiplicative blinder of the other.
/// 2. Open the product and check its MAC
/// 3. Invert the publicly available value and multiply with the shared
/// product to get the inverse of the blinded randomness
pub async fn generate_inverse_tuples(&mut self, n: usize) -> Result<(), LowGearError> {
// We use one triplet per tuple, so we need at least n triples
assert!(self.triples.len() >= n, "not enough triplets for {n} inverse tuples");
// We need `n` triplets to sacrifice for `n` inverse tuples
assert!(self.triples.len() >= n, "Not enough triplets to generate {n} inverse tuples");
let random_values = self.get_authenticated_randomness_vec(2 * n).await?;
let (lhs, rhs) = random_values.split_at(n);

// Split into halves that we will multiply using the Beaver trick
let (random_values1, random_values2) = random_values.into_inner().split_at(n);
// Multiply left and right hand side value
let product = self.beaver_mul(&lhs, &rhs).await?;
let product_open = self.open_and_check_macs(&product).await?;

// Invert the publicly available value and multiply with the shared
// product to get the inverse of the blinded randomness
let inverses = product_open.into_iter().map(|x| x.inverse()).collect_vec();
let shared_inverses = &rhs * inverses.as_slice(); // this leaves `1 / lhs`

// Structure into inverse tuples
let tuples = lhs.into_iter().zip(shared_inverses.into_iter()).collect_vec();
self.inverse_tuples = tuples;

Ok(())
}
}

#[cfg(test)]
mod test {
use ark_mpc::{algebra::Scalar, test_helpers::TestCurve, PARTY0};
use itertools::{izip, Itertools};
use rand::thread_rng;

use crate::{
beaver_source::{ValueMac, ValueMacBatch},
test_helpers::{
encrypt_all, generate_authenticated_secret_shares, generate_triples,
mock_lowgear_with_keys,
},
};

/// Tests generating inverse tuples
#[tokio::test]
async fn test_generate_inverse_tuples() {
let mut rng = thread_rng();
const N: usize = 100; // The number of tuples to generate

// Generate a mac key and shares
let mac_key = Scalar::random(&mut rng);
let mac_key1 = Scalar::random(&mut rng);
let mac_key2 = mac_key - mac_key1;

// Setup a set of mock triples
let (a, b, c) = generate_triples(N);
let (a1, a2) = generate_authenticated_secret_shares(&a, mac_key);
let (b1, b2) = generate_authenticated_secret_shares(&b, mac_key);
let (c1, c2) = generate_authenticated_secret_shares(&c, mac_key);

mock_lowgear_with_keys(|mut lowgear| {
// Setup the mac keys
let is_party0 = lowgear.party_id() == PARTY0;
let other_pk = lowgear.other_pk.as_ref().unwrap();

let my_mac_key = if is_party0 { mac_key1 } else { mac_key2 };
let their_mac_key = if is_party0 { mac_key2 } else { mac_key1 };
lowgear.mac_share = my_mac_key;
lowgear.other_mac_enc = Some(encrypt_all(their_mac_key, other_pk, &lowgear.params));

// Setup the triplets
let (my_a, my_b, my_c) = if is_party0 { (&a1, &b1, &c1) } else { (&a2, &b2, &c2) };
lowgear.triples =
izip!(my_a.clone().into_iter(), my_b.clone().into_iter(), my_c.clone().into_iter())
.collect_vec();

async move {
lowgear.generate_inverse_tuples(N).await.unwrap();

// Check the inverse triples
let (a, a_inv): (Vec<ValueMac<TestCurve>>, Vec<ValueMac<TestCurve>>) =
lowgear.inverse_tuples.clone().into_iter().unzip();
let a_inv_open =
lowgear.open_and_check_macs(&ValueMacBatch::new(a_inv)).await.unwrap();
let a_open = lowgear.open_and_check_macs(&ValueMacBatch::new(a)).await.unwrap();

for (a, a_inv) in izip!(a_open, a_inv_open) {
assert_eq!(a * a_inv, Scalar::one());
}
}
})
.await;
}
}
48 changes: 3 additions & 45 deletions offline-phase/src/lowgear/multiplication.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,53 +78,11 @@ mod tests {
use itertools::{izip, Itertools};
use rand::thread_rng;

use crate::{
beaver_source::ValueMacBatch,
test_helpers::{encrypt_all, mock_lowgear_with_keys, TestCurve},
use crate::test_helpers::{
encrypt_all, generate_authenticated_secret_shares, generate_triples,
mock_lowgear_with_keys, TestCurve,
};

/// Generate random mock triples for the Beaver trick
#[allow(clippy::type_complexity)]
fn generate_triples(
n: usize,
) -> (Vec<Scalar<TestCurve>>, Vec<Scalar<TestCurve>>, Vec<Scalar<TestCurve>>) {
let mut rng = thread_rng();
let a = (0..n).map(|_| Scalar::<TestCurve>::random(&mut rng)).collect_vec();
let b = (0..n).map(|_| Scalar::<TestCurve>::random(&mut rng)).collect_vec();
let c = (0..n).map(|_| Scalar::<TestCurve>::random(&mut rng)).collect_vec();

(a, b, c)
}

/// Generate authenticated secret shares of a given set of values
fn generate_authenticated_secret_shares(
values: &[Scalar<TestCurve>],
mac_key: Scalar<TestCurve>,
) -> (ValueMacBatch<TestCurve>, ValueMacBatch<TestCurve>) {
let (shares1, shares2) = generate_secret_shares(values);
let macs = values.iter().map(|value| *value * mac_key).collect_vec();
let (macs1, macs2) = generate_secret_shares(&macs);

(ValueMacBatch::from_parts(&shares1, &macs1), ValueMacBatch::from_parts(&shares2, &macs2))
}

/// Generate secret shares of a set of values
fn generate_secret_shares(
values: &[Scalar<TestCurve>],
) -> (Vec<Scalar<TestCurve>>, Vec<Scalar<TestCurve>>) {
let mut rng = thread_rng();
let mut shares1 = Vec::with_capacity(values.len());
let mut shares2 = Vec::with_capacity(values.len());
for value in values {
let share1 = Scalar::<TestCurve>::random(&mut rng);
let share2 = value - share1;
shares1.push(share1);
shares2.push(share2);
}

(shares1, shares2)
}

#[tokio::test]
async fn test_beaver_mul() {
const N: usize = 100;
Expand Down
Loading