From e3bb7f236680ce672111239c72248540cb969186 Mon Sep 17 00:00:00 2001 From: Joey Kraut Date: Sat, 13 Apr 2024 14:45:38 -0700 Subject: [PATCH] offline-phase: lowgear: inverse_tuples: Generate inverse tuples --- offline-phase/src/beaver_source.rs | 6 ++ offline-phase/src/lib.rs | 45 +++++++++- offline-phase/src/lowgear/inverse_tuples.rs | 92 ++++++++++++++++++++- offline-phase/src/lowgear/multiplication.rs | 48 +---------- 4 files changed, 141 insertions(+), 50 deletions(-) diff --git a/offline-phase/src/beaver_source.rs b/offline-phase/src/beaver_source.rs index d42c1f6..78cec54 100644 --- a/offline-phase/src/beaver_source.rs +++ b/offline-phase/src/beaver_source.rs @@ -122,6 +122,12 @@ impl ValueMacBatch { 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], macs: &[Scalar]) -> Self { assert_eq!(values.len(), macs.len()); diff --git a/offline-phase/src/lib.rs b/offline-phase/src/lib.rs index 69fd9ff..a6475a8 100644 --- a/offline-phase/src/lib.rs +++ b/offline-phase/src/lib.rs @@ -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}, @@ -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; @@ -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>, Vec>, Vec>) { + let mut rng = thread_rng(); + let a = (0..n).map(|_| Scalar::::random(&mut rng)).collect_vec(); + let b = (0..n).map(|_| Scalar::::random(&mut rng)).collect_vec(); + let c = (0..n).map(|_| Scalar::::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], + mac_key: Scalar, + ) -> (ValueMacBatch, ValueMacBatch) { + 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], + ) -> (Vec>, Vec>) { + 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::::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: F) -> (T, T) where diff --git a/offline-phase/src/lowgear/inverse_tuples.rs b/offline-phase/src/lowgear/inverse_tuples.rs index 6acb126..b279b96 100644 --- a/offline-phase/src/lowgear/inverse_tuples.rs +++ b/offline-phase/src/lowgear/inverse_tuples.rs @@ -3,6 +3,7 @@ use ark_ec::CurveGroup; use ark_mpc::network::MpcNetwork; +use itertools::Itertools; use crate::error::LowGearError; @@ -10,14 +11,97 @@ use super::LowGear; impl + Unpin + Send> LowGear { /// 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>, Vec>) = + 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; + } +} diff --git a/offline-phase/src/lowgear/multiplication.rs b/offline-phase/src/lowgear/multiplication.rs index 73a55a4..cb05825 100644 --- a/offline-phase/src/lowgear/multiplication.rs +++ b/offline-phase/src/lowgear/multiplication.rs @@ -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>, Vec>, Vec>) { - let mut rng = thread_rng(); - let a = (0..n).map(|_| Scalar::::random(&mut rng)).collect_vec(); - let b = (0..n).map(|_| Scalar::::random(&mut rng)).collect_vec(); - let c = (0..n).map(|_| Scalar::::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], - mac_key: Scalar, - ) -> (ValueMacBatch, ValueMacBatch) { - 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], - ) -> (Vec>, Vec>) { - 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::::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;