-
Notifications
You must be signed in to change notification settings - Fork 23
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
[Sort Multi Bit] Upgrade to malicious + semi honest ipa now calls multi bit sort #396
Closed
Closed
Changes from 5 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
baf120d
Multi bit is replacing bit permutation
richajaindce 408c96f
Merge branch 'main' into multi_bit
richajaindce 7bc13d8
Malicious upgrade of multi bit gen perm
richajaindce d413dda
Remove prss changes
richajaindce 98fabe4
Merge branch 'main' into multi_bit
richajaindce 1edd422
Non working context handling
richajaindce 207e9a3
Generic generate permutation
akoshelev File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,8 +3,10 @@ use crate::{ | |
ff::Field, | ||
protocol::{ | ||
context::Context, | ||
malicious::MaliciousValidator, | ||
sort::generate_permutation::shuffle_and_reveal_permutation, | ||
sort::{ | ||
generate_permutation::malicious_shuffle_and_reveal_permutation, | ||
multi_bit_permutation::multi_bit_permutation, | ||
SortStep::{BitPermutationStep, ComposeStep, MultiApplyInv, ShuffleRevealPermutation}, | ||
}, | ||
|
@@ -16,8 +18,11 @@ use std::iter::repeat; | |
|
||
use super::{compose::compose, secureapplyinv::secureapplyinv}; | ||
use crate::protocol::context::SemiHonestContext; | ||
use crate::{ | ||
protocol::sort::SortStep::MaliciousUpgradeContext, | ||
secret_sharing::replicated::malicious::AdditiveShare as MaliciousReplicated, | ||
}; | ||
use futures::future::try_join_all; | ||
|
||
/// This is an implementation of `OptGenPerm` (Algorithm 12) described in: | ||
/// "An Efficient Secure Three-Party Sorting Protocol with an Honest Majority" | ||
/// by K. Chida, K. Hamada, D. Ikarashi, R. Kikuchi, N. Kiribuchi, and B. Pinkas | ||
|
@@ -109,39 +114,157 @@ where | |
|
||
composed_less_significant_bits_permutation = compose( | ||
ctx_bit.narrow(&ComposeStep), | ||
( | ||
revealed_and_random_permutations | ||
.randoms_for_shuffle | ||
.0 | ||
.as_slice(), | ||
revealed_and_random_permutations | ||
.randoms_for_shuffle | ||
.1 | ||
.as_slice(), | ||
), | ||
&revealed_and_random_permutations.revealed, | ||
(randoms_for_shuffle0, randoms_for_shuffle1), | ||
revealed, | ||
next_few_bits_permutation, | ||
) | ||
.await?; | ||
} | ||
Ok(composed_less_significant_bits_permutation) | ||
} | ||
|
||
#[allow(dead_code)] | ||
/// Returns a sort permutation in a malicious context. | ||
/// This runs sort in a malicious context. The caller is responsible to validate the accumulater contents and downgrade context to Semi-honest before calling this function | ||
/// The function takes care of upgrading and validating while the sort protocol runs. | ||
/// It then returns a semi honest context with output in Replicated format. The caller should then upgrade the output and context before moving forward | ||
/// | ||
/// Steps | ||
/// 1. [Malicious Special] Upgrade the context from semihonest to malicious and get a validator | ||
/// 2. [Malicious Special] Upgrade 0..`num_multi_bits` sort bit keys | ||
/// 3. Compute bit permutation that sorts 0..`num_multi_bits` bit | ||
/// | ||
/// For `num_multi_bits` to N-1th bit of input share | ||
/// 1. i. Shuffle the i-1th composition | ||
/// ii. [Malicious Special] Validate the accumulator contents | ||
/// iii. [Malicious Special] Malicious reveal | ||
/// iv. [Malicious Special] Downgrade context to semihonest | ||
/// 2. i. [Malicious Special] Upgrade ith sort bit keys | ||
/// ii. Sort i..i+`num_multi_bits` bits based on i-1th bits by applying i-1th composition on i..i+`num_multi_bits` bits | ||
/// 3. Compute bit permutation that sorts i..i+`num_multi_bits` bits | ||
/// 4. Compute ith composition by composing i-1th composition on ith permutation | ||
/// In the end, following is returned | ||
/// i. n-1th composition: This is the permutation which sorts the inputs | ||
/// ii. Validator which can be used to validate the leftover items in the accumulator | ||
/// | ||
/// # Panics | ||
/// If sort keys dont have num of bits same as `num_bits` | ||
/// # Errors | ||
pub async fn malicious_generate_permutation_opt<'a, F>( | ||
sh_ctx: SemiHonestContext<'a, F>, | ||
sort_keys: &[Vec<Replicated<F>>], | ||
num_bits: u32, | ||
num_multi_bits: u32, | ||
) -> Result<(MaliciousValidator<'a, F>, Vec<MaliciousReplicated<F>>), Error> | ||
where | ||
F: Field, | ||
{ | ||
let mut malicious_validator = MaliciousValidator::new(sh_ctx.narrow(&MaliciousUpgradeContext)); | ||
let mut m_ctx = malicious_validator.context(); | ||
let m_ctx_0 = m_ctx.narrow(&Sort(0)); | ||
assert_eq!(sort_keys.len(), num_bits as usize); | ||
|
||
let last_bit_num = std::cmp::min(num_multi_bits, num_bits); | ||
|
||
let upgraded_sort_keys = try_join_all( | ||
(0..last_bit_num) | ||
.zip(repeat(m_ctx.clone())) | ||
.map(|(i, m_ctx)| async move { m_ctx.upgrade(sort_keys[i as usize].clone()).await }), | ||
) | ||
.await?; | ||
let lsb_permutation = | ||
multi_bit_permutation(m_ctx_0.narrow(&BitPermutationStep), &upgraded_sort_keys).await?; | ||
let input_len = u32::try_from(sort_keys[0].len()).unwrap(); // safe, we don't sort more that 1B rows | ||
|
||
let mut composed_less_significant_bits_permutation = lsb_permutation; | ||
for bit_num in (num_multi_bits..num_bits).step_by(num_multi_bits.try_into().unwrap()) { | ||
let mut m_ctx_bit = m_ctx.narrow(&Sort(bit_num)); | ||
let revealed_and_random_permutations = malicious_shuffle_and_reveal_permutation( | ||
m_ctx_bit.narrow(&ShuffleRevealPermutation), | ||
input_len, | ||
composed_less_significant_bits_permutation, | ||
malicious_validator, | ||
) | ||
.await?; | ||
|
||
malicious_validator = MaliciousValidator::new(sh_ctx.narrow(&Sort(bit_num))); | ||
m_ctx_bit = malicious_validator.context(); | ||
|
||
let last_bit_num = std::cmp::min(bit_num + num_multi_bits, num_bits); | ||
let upgraded_sort_keys = | ||
&try_join_all( | ||
(bit_num..last_bit_num).zip(repeat(m_ctx_bit.clone())).map( | ||
|(i, m_ctx_bit)| async move { | ||
m_ctx_bit.upgrade(sort_keys[i as usize].clone()).await | ||
}, | ||
), | ||
) | ||
.await?; | ||
|
||
let (randoms_for_shuffle0, randoms_for_shuffle1, revealed) = ( | ||
revealed_and_random_permutations | ||
.randoms_for_shuffle | ||
.0 | ||
.as_slice(), | ||
revealed_and_random_permutations | ||
.randoms_for_shuffle | ||
.1 | ||
.as_slice(), | ||
revealed_and_random_permutations.revealed.as_slice(), | ||
); | ||
Comment on lines
+204
to
+214
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. Is it possible to do this destructuring when |
||
|
||
let futures = (bit_num..last_bit_num).zip(repeat(m_ctx_bit.clone())).map( | ||
|(idx, m_ctx_bit)| async move { | ||
secureapplyinv( | ||
m_ctx_bit.narrow(&MultiApplyInv(idx)), | ||
upgraded_sort_keys[(idx - bit_num) as usize].clone(), | ||
(randoms_for_shuffle0, randoms_for_shuffle1), | ||
revealed, | ||
) | ||
.await | ||
}, | ||
); | ||
let next_few_bits_sorted_by_less_significant_bits = try_join_all(futures).await?; | ||
|
||
let next_few_bits_permutation = multi_bit_permutation( | ||
m_ctx_bit.narrow(&BitPermutationStep), | ||
&next_few_bits_sorted_by_less_significant_bits, | ||
) | ||
.await?; | ||
|
||
composed_less_significant_bits_permutation = compose( | ||
m_ctx_bit.narrow(&ComposeStep), | ||
(randoms_for_shuffle0, randoms_for_shuffle1), | ||
revealed, | ||
next_few_bits_permutation, | ||
) | ||
.await?; | ||
m_ctx = m_ctx_bit; | ||
} | ||
Ok(( | ||
malicious_validator, | ||
composed_less_significant_bits_permutation, | ||
)) | ||
} | ||
|
||
#[cfg(all(test, not(feature = "shuttle")))] | ||
mod tests { | ||
use std::iter::zip; | ||
|
||
use crate::protocol::modulus_conversion::{convert_all_bits, convert_all_bits_local}; | ||
use crate::protocol::sort::generate_permutation_opt::malicious_generate_permutation_opt; | ||
use crate::rand::{thread_rng, Rng}; | ||
|
||
use crate::protocol::context::{Context, SemiHonestContext}; | ||
use crate::test_fixture::{MaskedMatchKey, Runner}; | ||
use crate::test_fixture::{join3, MaskedMatchKey, Runner}; | ||
use crate::{ | ||
ff::{Field, Fp31}, | ||
protocol::sort::generate_permutation_opt::generate_permutation_opt, | ||
test_fixture::{Reconstruct, TestWorld}, | ||
}; | ||
|
||
use crate::secret_sharing::replicated::semi_honest::AdditiveShare as Replicated; | ||
|
||
#[tokio::test] | ||
pub async fn semi_honest() { | ||
const COUNT: usize = 10; | ||
|
@@ -185,4 +308,52 @@ mod tests { | |
|
||
assert_eq!(expected, mpc_sorted_list); | ||
} | ||
|
||
#[tokio::test] | ||
pub async fn malicious_sort() { | ||
const COUNT: usize = 5; | ||
const NUM_MULTI_BITS: u32 = 3; | ||
|
||
let world = TestWorld::new().await; | ||
let mut rng = thread_rng(); | ||
|
||
let mut match_keys = Vec::with_capacity(COUNT); | ||
match_keys.resize_with(COUNT, || MaskedMatchKey::mask(rng.gen())); | ||
|
||
let mut expected = match_keys | ||
.iter() | ||
.map(|mk| u64::from(*mk)) | ||
.collect::<Vec<_>>(); | ||
expected.sort_unstable(); | ||
|
||
let [(v0, result0), (v1, result1), (v2, result2)] = world | ||
.semi_honest(match_keys.clone(), |ctx, mk_shares| async move { | ||
let local_lists = | ||
convert_all_bits_local(ctx.role(), &mk_shares, MaskedMatchKey::BITS); | ||
let converted_shares: Vec<Vec<Replicated<Fp31>>> = | ||
convert_all_bits(&ctx, &local_lists).await.unwrap(); | ||
malicious_generate_permutation_opt( | ||
ctx.narrow("sort"), | ||
&converted_shares, | ||
MaskedMatchKey::BITS, | ||
NUM_MULTI_BITS, | ||
) | ||
.await | ||
.unwrap() | ||
}) | ||
.await; | ||
|
||
let result = join3( | ||
v0.validate(result0), | ||
v1.validate(result1), | ||
v2.validate(result2), | ||
) | ||
.await; | ||
let mut mpc_sorted_list = (0..u64::try_from(COUNT).unwrap()).collect::<Vec<_>>(); | ||
for (match_key, index) in zip(match_keys, result.reconstruct()) { | ||
mpc_sorted_list[index.as_u128() as usize] = u64::from(match_key); | ||
} | ||
|
||
assert_eq!(expected, mpc_sorted_list); | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
@andyleiserson this still will fail since we are using the same context across bits for the same record with the error "Generated randomness for index '0' twice using the same key 'protocol/run-0/sort/malicious_upgrade_context/upgrade_input' ". Any ideas how to resolve this without narrowing context?
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.
My thinking (based on an earlier revision of this PR) was to add an upgrade impl for
Vec<Vec<Replicated>>
and then use that to upgrade everything at once rather than doing multiple upgrades.Does that make sense? I'm not sure if something changed since I looked at it or if there's some other reason I missed that that won't work.
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.
The code was earlier written to just work on one vector at a time and so I am doing some refactoring to let the sort code take in vec<vec<vec>> so that each vec<vec> can be processed in one iteration