Browse Source

in middle of modifying ni mp to use different bases for RLWE x RGSW and RGSW x RGSW

par-agg-key-shares
Janmajaya Mall 9 months ago
parent
commit
082a08f3d8
2 changed files with 208 additions and 217 deletions
  1. +190
    -211
      src/bool/evaluator.rs
  2. +18
    -6
      src/bool/keys.rs

+ 190
- 211
src/bool/evaluator.rs

@ -1,19 +1,13 @@
use std::{
borrow::BorrowMut,
cell::{OnceCell, RefCell},
clone,
collections::HashMap,
fmt::{Debug, Display},
iter::Once,
marker::PhantomData,
ops::Shr,
sync::OnceLock,
usize,
};
use itertools::{izip, partition, Itertools};
use num_traits::{
FromPrimitive, Num, One, Pow, PrimInt, ToPrimitive, WrappingAdd, WrappingSub, Zero,
zero, FromPrimitive, Num, One, Pow, PrimInt, ToPrimitive, WrappingAdd, WrappingSub, Zero,
};
use rand::Rng;
use rand_distr::uniform::SampleUniform;
@ -37,8 +31,7 @@ use crate::{
},
rgsw::{
decrypt_rlwe, galois_auto, galois_key_gen, generate_auto_map, public_key_encrypt_rgsw,
rgsw_by_rgsw_inplace, rlwe_by_rgsw, secret_key_encrypt_rgsw, IsTrivial, RgswCiphertext,
RlweCiphertext, RlweSecret,
rgsw_by_rgsw_inplace, secret_key_encrypt_rgsw,
},
utils::{
encode_x_pow_si_with_emebedding_factor, fill_random_ternary_secret_with_hamming_weight,
@ -125,6 +118,8 @@ impl MultiPartyCrs {
/// Initial Seed
/// Puncture 1 -> Key Seed
/// Puncture 1 -> Rgsw ciphertext seed
/// Puncture l+1 -> Seed for zero encs and non-interactive
/// multi-party RGSW ciphertext corresponding to l^th LWE index.
/// Puncture 2 -> auto keys seed
/// Puncture 3 -> Lwe key switching key seed
/// Puncture 2 -> user specific seed for u_j to s ksk
@ -150,11 +145,19 @@ impl NonInteractiveMultiPartyCrs {
puncture_p_rng(&mut p_rng, 1)
}
pub(crate) fn rgsw_cts_seed<R: NewWithSeed<Seed = S> + RandomFill<S>>(&self) -> S {
pub(crate) fn ni_rgsw_cts_main_seed<R: NewWithSeed<Seed = S> + RandomFill<S>>(&self) -> S {
let mut p_rng = R::new_with_seed(self.key_seed::<R>());
puncture_p_rng(&mut p_rng, 1)
}
pub(crate) fn ni_rgsw_ct_seed_for_index<R: NewWithSeed<Seed = S> + RandomFill<S>>(
&self,
lwe_index: usize,
) -> S {
let mut p_rng = R::new_with_seed(self.ni_rgsw_cts_main_seed::<R>());
puncture_p_rng(&mut p_rng, lwe_index + 1)
}
pub(crate) fn auto_keys_cts_seed<R: NewWithSeed<Seed = S> + RandomFill<S>>(&self) -> S {
let mut p_rng = R::new_with_seed(self.key_seed::<R>());
puncture_p_rng(&mut p_rng, 2)
@ -1207,7 +1210,7 @@ where
let rgsw_cts = {
let mut rgsw_prng =
DefaultSecureRng::new_seeded(cr_seed.rgsw_cts_seed::<DefaultSecureRng>());
DefaultSecureRng::new_seeded(cr_seed.ni_rgsw_cts_main_seed::<DefaultSecureRng>());
let rgsw_by_rgsw_decomposer = self
.parameters()
@ -1664,7 +1667,6 @@ where
K: NonInteractiveMultiPartyClientKey<Element = i32>,
>(
&self,
// TODO(Jay): Should get a common reference seed here and derive the rest.
cr_seed: &NonInteractiveMultiPartyCrs<[u8; 32]>,
self_index: usize,
total_users: usize,
@ -1688,7 +1690,7 @@ where
let sk_u_rlwe = client_key.sk_u_rlwe();
let sk_lwe = client_key.sk_lwe();
let (ui_to_s_ksk, zero_encs_for_others) = DefaultSecureRng::with_local_mut(|rng| {
let (ui_to_s_ksk, ksk_zero_encs_for_others) = DefaultSecureRng::with_local_mut(|rng| {
// ui_to_s_ksk
let non_interactive_decomposer = self
.parameters()
@ -1734,51 +1736,182 @@ where
(ui_to_s_ksk, zero_encs_for_others)
});
// Non-interactive RGSW cts = (a_i * u_j + e + \beta X^{s[i]}, a_i * s_j + e')
let ni_rgsw_cts = DefaultSecureRng::with_local_mut(|rng| {
let mut rgsw_cts_prng =
DefaultSecureRng::new_seeded(cr_seed.rgsw_cts_seed::<DefaultSecureRng>());
// generate non-interactive rgsw cts
let rgsw_by_rgsw_decomposer = self
// Non-interactive RGSW cts
let (ni_rgsw_zero_encs, self_leader_ni_rgsw_cts, not_self_leader_rgsw_cts) = {
// Warning: Below we assume that max(d_a, d_b) for RLWE x RGSW are always
// smaller than max(d_a, d_b) for RGSW x RGSW. The assumption is reasonable
// since RGSW x RGSW multiplication always precede RLWE x RGSW multiplication,
// hence require greater room.
let rgsw_x_rgsw_decomposer = self
.parameters()
.rgsw_rgsw_decomposer::<DefaultDecomposer<M::MatElement>>();
let rlwe_x_rgsw_decomposer = self
.parameters()
.rlwe_rgsw_decomposer::<DefaultDecomposer<M::MatElement>>();
let ni_rgrg_gadget_vec = {
if rgsw_by_rgsw_decomposer.a().decomposition_count()
> rgsw_by_rgsw_decomposer.b().decomposition_count()
{
rgsw_by_rgsw_decomposer.a().gadget_vector()
} else {
rgsw_by_rgsw_decomposer.b().gadget_vector()
}
let sj_poly_eval = {
let mut s = M::R::try_convert_from(&sk_rlwe, rlwe_q);
nttop.forward(s.as_mut());
s
};
let ni_rgsw_cts: (Vec<M>, Vec<M>) = sk_lwe
.iter()
.map(|s_i| {
// X^{s[i]}
let mut m = M::R::zeros(ring_size);
let s_i = s_i * (self.pbs_info().embedding_factor() as i32);
if s_i < 0 {
// X^{-s[i]} -> -X^{N+s[i]}
m.as_mut()[ring_size - (s_i.abs() as usize)] = rlwe_q.neg_one();
} else {
m.as_mut()[s_i as usize] = M::MatElement::one();
}
non_interactive_rgsw_ct::<M, _, _, _, _, _>(
&sk_rlwe,
&sk_u_rlwe,
m.as_ref(),
&ni_rgrg_gadget_vec,
&mut rgsw_cts_prng,
rng,
nttop,
rlwe_modop,
)
})
.unzip();
ni_rgsw_cts
});
let d_rgsw_a = rgsw_x_rgsw_decomposer.a().decomposition_count();
// Zero encyptions for each LWE index.
// Zero encryptions are needed to generate RLWE(-sm) from a_i * u_j + e. Hence,
// zero encryptions are only required for first `d_a` a's where d_a
// is decomposition count `a` in RGSW x RGSW.
let zero_encs = {
(0..self.parameters().lwe_n().0)
.map(|lwe_index| {
let mut p_rng = DefaultSecureRng::new_seeded(
cr_seed.ni_rgsw_ct_seed_for_index::<DefaultSecureRng>(lwe_index),
);
let mut scratch = M::R::zeros(self.parameters().rlwe_n().0);
let mut zero_enc = M::zeros(d_rgsw_a, self.parameters().rlwe_n().0);
zero_enc.iter_rows_mut().for_each(|out| {
// sample a_i
RandomFillUniformInModulus::random_fill(
&mut p_rng,
rlwe_q,
out.as_mut(),
);
// a_i * s_j
nttop.forward(out.as_mut());
rlwe_modop.elwise_mul_mut(out.as_mut(), sj_poly_eval.as_ref());
nttop.backward(out.as_mut());
// a_j * s_j + e
DefaultSecureRng::with_local_mut_mut(&mut |rng| {
RandomFillGaussianInModulus::random_fill(
rng,
rlwe_q,
scratch.as_mut(),
);
});
rlwe_modop.elwise_add_mut(out.as_mut(), scratch.as_ref());
});
zero_enc
})
.collect_vec()
};
let uj_poly_eval = {
let mut u = M::R::try_convert_from(&sk_u_rlwe, rlwe_q);
nttop.forward(u.as_mut());
u
};
// Non-interactive RGSW ciphertexts: a_i u_j + e + \beta X^{s_j[l]}
// Non-interactive RGSW ciphertexts are needed for both RLWE'(-sm)
// and RLWE'(m). Hence, we should generate them a_i's with i \in [0,
// max(d_a, d_b)) where d_{a/b} are either decomposition counts for
// RGSW x RGSW (if self is not leader for l^th) or RLWE x RGSW (if
// self is leader for l^th index)
let (self_start_index, self_end_index) = interactive_mult_party_user_id_lwe_segment(
self_index,
total_users,
self.parameters().lwe_n().0,
);
// For LWE indices [self_start_index, self_end_index) user generates
// non-interactive RGSW cts for RLWE x RGSW product. We refer to
// such indices where user is the leader. For the rest of
// the indices user generates non-interactive RGWS cts for RGSW x
// RGSW multiplication. We refer to such indices as where user is
// not the leader.
let self_leader_ni_rgsw_cts = {
let max_rlwe_x_rgsw_d = std::cmp::max(
rlwe_x_rgsw_decomposer.a().decomposition_count(),
rlwe_x_rgsw_decomposer.b().decomposition_count(),
);
(self_start_index..self_end_index)
.map(|lwe_index| {
let mut p_rng = DefaultSecureRng::new_seeded(
cr_seed.ni_rgsw_ct_seed_for_index::<DefaultSecureRng>(lwe_index),
);
let mut ni_rgsw_cts =
M::zeros(max_rlwe_x_rgsw_d, self.parameters().rlwe_n().0);
let mut scratch = M::R::zeros(self.parameters().rlwe_n().0);
ni_rgsw_cts.iter_rows_mut().for_each(|out| {
// sample a_i
RandomFillUniformInModulus::random_fill(
&mut p_rng,
rlwe_q,
out.as_mut(),
);
// u_j * a_i
nttop.forward(out.as_mut());
rlwe_modop.elwise_mul_mut(out.as_mut(), uj_poly_eval.as_ref());
nttop.backward(out.as_mut());
// u_j + a_i + e
DefaultSecureRng::with_local_mut_mut(&mut |rng| {
RandomFillGaussianInModulus::random_fill(
rng,
rlwe_q,
scratch.as_mut(),
);
});
rlwe_modop.elwise_add_mut(out.as_mut(), scratch.as_ref());
});
ni_rgsw_cts
})
.collect_vec()
};
let not_self_leader_rgsw_cts = {
let max_rgsw_x_rgsw_d = std::cmp::max(
rgsw_x_rgsw_decomposer.a().decomposition_count(),
rgsw_x_rgsw_decomposer.b().decomposition_count(),
);
((0..self_start_index).chain(self_end_index..self.parameters().lwe_n().0))
.map(|lwe_index| {
let mut p_rng = DefaultSecureRng::new_seeded(
cr_seed.ni_rgsw_ct_seed_for_index::<DefaultSecureRng>(lwe_index),
);
let mut ni_rgsw_cts =
M::zeros(max_rgsw_x_rgsw_d, self.parameters().rlwe_n().0);
let mut scratch = M::R::zeros(self.parameters().rlwe_n().0);
ni_rgsw_cts.iter_rows_mut().for_each(|out| {
// sample a_i
RandomFillUniformInModulus::random_fill(
&mut p_rng,
rlwe_q,
out.as_mut(),
);
// u_j * a_i
nttop.forward(out.as_mut());
rlwe_modop.elwise_mul_mut(out.as_mut(), uj_poly_eval.as_ref());
nttop.backward(out.as_mut());
// u_j + a_i + e
DefaultSecureRng::with_local_mut_mut(&mut |rng| {
RandomFillGaussianInModulus::random_fill(
rng,
rlwe_q,
scratch.as_mut(),
);
});
rlwe_modop.elwise_add_mut(out.as_mut(), scratch.as_ref());
});
ni_rgsw_cts
})
.collect_vec()
};
(zero_encs, self_leader_ni_rgsw_cts, not_self_leader_rgsw_cts)
};
// Auto key share
let auto_keys_share = {
@ -1793,9 +1926,11 @@ where
};
CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare::new(
ni_rgsw_cts,
self_leader_ni_rgsw_cts,
not_self_leader_rgsw_cts,
ni_rgsw_zero_encs,
ui_to_s_ksk,
zero_encs_for_others,
ksk_zero_encs_for_others,
auto_keys_share,
lwe_ksk_share,
self_index,
@ -2285,159 +2420,3 @@ where
out
}
}
#[cfg(test)]
mod tests {
use rand::{thread_rng, Rng};
use rand_distr::Uniform;
use crate::{
backend::ModulusPowerOf2,
bool::{
keys::{
NonInteractiveServerKeyEvaluationDomain, PublicKey,
ShoupNonInteractiveServerKeyEvaluationDomain,
},
parameters::{MP_BOOL_PARAMS, NI_2P, SMALL_MP_BOOL_PARAMS, SP_TEST_BOOL_PARAMS},
},
evaluator,
ntt::NttBackendU64,
parameters::I_2P,
random::{RandomElementInModulus, DEFAULT_RNG},
rgsw::{
self, measure_noise, public_key_encrypt_rlwe, secret_key_encrypt_rlwe,
tests::{_measure_noise_rgsw, _sk_encrypt_rlwe},
RgswCiphertext, RgswCiphertextEvaluationDomain, SeededRgswCiphertext,
SeededRlweCiphertext,
},
utils::{negacyclic_mul, tests::Stats},
};
use super::*;
#[test]
fn testtest() {
let evaluator = BoolEvaluator::<
Vec<Vec<u64>>,
NttBackendU64,
ModularOpsU64<CiphertextModulus<u64>>,
ModulusPowerOf2<CiphertextModulus<u64>>,
ShoupNonInteractiveServerKeyEvaluationDomain<Vec<Vec<u64>>>,
>::new(NI_2P);
let mp_seed = NonInteractiveMultiPartyCrs { seed: [1u8; 32] };
let ring_size = evaluator.parameters().rlwe_n().0;
let rlwe_q = evaluator.parameters().rlwe_q();
let rlwe_modop = evaluator.pbs_info().modop_rlweq();
let nttop = evaluator.pbs_info().nttop_rlweq();
let parties = 2;
let cks = (0..parties).map(|_| evaluator.client_key()).collect_vec();
let key_shares = (0..parties)
.map(|i| evaluator.non_interactive_multi_party_key_share(&mp_seed, i, parties, &cks[i]))
.collect_vec();
// dbg!(key_shares[1].user_index);
let seeded_server_key = evaluator.aggregate_non_interactive_multi_party_key_share(
&mp_seed,
parties,
&key_shares,
);
let server_key_evaluation_domain =
NonInteractiveServerKeyEvaluationDomain::<_, _, DefaultSecureRng, NttBackendU64>::from(
&seeded_server_key,
);
let mut ideal_rlwe = vec![0; ring_size];
cks.iter().for_each(|k| {
izip!(
ideal_rlwe.iter_mut(),
NonInteractiveMultiPartyClientKey::sk_rlwe(k).iter()
)
.for_each(|(a, b)| {
*a = *a + b;
});
});
let mut ideal_lwe = vec![0; evaluator.parameters().lwe_n().0];
cks.iter().for_each(|k| {
izip!(
ideal_lwe.iter_mut(),
NonInteractiveMultiPartyClientKey::sk_lwe(k).iter()
)
.for_each(|(a, b)| {
*a = *a + b;
});
});
let mut stats = Stats::new();
{
let (rlrg_decomp_a, rlrg_decomp_b) = evaluator
.parameters()
.rlwe_rgsw_decomposer::<DefaultDecomposer<u64>>();
let gadget_vec_a = rlrg_decomp_a.gadget_vector();
let gadget_vec_b = rlrg_decomp_b.gadget_vector();
let d_a = rlrg_decomp_a.decomposition_count();
let d_b = rlrg_decomp_b.decomposition_count();
// s[X]
let s_poly = Vec::<u64>::try_convert_from(ideal_rlwe.as_slice(), rlwe_q);
// -s[X]
let mut neg_s_poly_eval = s_poly.clone();
rlwe_modop.elwise_neg_mut(&mut neg_s_poly_eval);
nttop.forward(neg_s_poly_eval.as_mut());
server_key_evaluation_domain
.rgsw_cts()
.iter()
.enumerate()
.for_each(|(s_index, ct)| {
// X^{lwe_s[i]}
let mut m = vec![0u64; ring_size];
let s_i = ideal_lwe[s_index] * (evaluator.pbs_info().embedding_factor() as i32);
if s_i < 0 {
m[ring_size - (s_i.abs() as usize)] = rlwe_q.neg_one();
} else {
m[(s_i as usize)] = 1;
}
let mut neg_sm = m.clone();
nttop.forward(&mut neg_sm);
rlwe_modop.elwise_mul_mut(&mut neg_sm, &neg_s_poly_eval);
nttop.backward(&mut neg_sm);
// RLWE'(-sm)
gadget_vec_a.iter().enumerate().for_each(|(index, beta)| {
// RLWE(\beta -sm)
// \beta * -sX^[lwe_s[i]]
let mut beta_neg_sm = neg_sm.clone();
rlwe_modop.elwise_scalar_mul_mut(&mut beta_neg_sm, beta);
// extract RLWE(-sm \beta)
let mut rlwe = vec![vec![0u64; ring_size]; 2];
rlwe[0].copy_from_slice(&ct[index]);
rlwe[1].copy_from_slice(&ct[index + d_a]);
// send back to coefficient domain
rlwe.iter_rows_mut()
.for_each(|r| nttop.backward(r.as_mut_slice()));
// decrypt
let mut m_out = vec![0u64; ring_size];
decrypt_rlwe(&rlwe, &ideal_rlwe, &mut m_out, nttop, rlwe_modop); // println!("{:?}", &beta_neg_sm);
let mut diff = m_out;
rlwe_modop.elwise_sub_mut(&mut diff, &beta_neg_sm);
stats.add_more(&Vec::<i64>::try_convert_from(&diff, rlwe_q));
});
});
}
println!("Stats: {}", stats.std_dev().abs().log2());
}
}

+ 18
- 6
src/bool/keys.rs

@ -1186,10 +1186,18 @@ mod shoup_server_key_eval_domain {
}
pub struct CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<M: Matrix, S> {
/// Non-interactive RGSW ciphertexts for secret indices for which user is
/// the leader
self_leader_ni_rgsw_cts: Vec<M>,
/// Non-interactive RGSW ciphertexts for secret indices for which user is
/// not the leader
not_self_leader_ni_rgsw_cts: Vec<M>,
/// Zero encryptions for RGSW ciphertexts for all indicces
ni_rgsw_zero_encs: Vec<M>,
/// (ak*si + e + \beta ui, ak*si + e)
ni_rgsw_cts: (Vec<M>, Vec<M>),
ui_to_s_ksk: M,
others_ksk_zero_encs: Vec<M>,
ksk_zero_encs_for_others: Vec<M>,
auto_keys_share: HashMap<usize, M>,
lwe_ksk_share: M::R,
@ -1203,18 +1211,22 @@ mod impl_common_ref_non_interactive_multi_party_server_share {
impl<M: Matrix, S> CommonReferenceSeededNonInteractiveMultiPartyServerKeyShare<M, S> {
pub(in super::super) fn new(
ni_rgsw_cts: (Vec<M>, Vec<M>),
self_leader_ni_rgsw_cts: Vec<M>,
not_self_leader_ni_rgsw_cts: Vec<M>,
ni_rgsw_zero_encs: Vec<M>,
ui_to_s_ksk: M,
others_ksk_zero_encs: Vec<M>,
ksk_zero_encs_for_others: Vec<M>,
auto_keys_share: HashMap<usize, M>,
lwe_ksk_share: M::R,
user_index: usize,
cr_seed: S,
) -> Self {
Self {
ni_rgsw_cts,
self_leader_ni_rgsw_cts,
not_self_leader_ni_rgsw_cts,
ni_rgsw_zero_encs,
ui_to_s_ksk,
others_ksk_zero_encs,
ksk_zero_encs_for_others,
auto_keys_share,
lwe_ksk_share,
user_index,

Loading…
Cancel
Save